mirror of
https://github.com/slint-ui/slint.git
synced 2025-07-08 21:55:28 +00:00

This does some refactoring to allow builtin item functions to return a value: - builtin member functions are no longer BuiltinFunction, but they are just normal NamedReference - Move special case for them in the LLR/eval
290 lines
12 KiB
Rust
290 lines
12 KiB
Rust
// Copyright © SixtyFPS GmbH <info@slint.dev>
|
|
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
|
|
|
|
/*!
|
|
Parse the contents of builtins.slint and fill the builtin type registry
|
|
*/
|
|
|
|
use smol_str::{SmolStr, ToSmolStr};
|
|
use std::cell::RefCell;
|
|
use std::collections::HashMap;
|
|
use std::rc::Rc;
|
|
|
|
use crate::expression_tree::Expression;
|
|
use crate::langtype::{
|
|
BuiltinElement, BuiltinPropertyDefault, BuiltinPropertyInfo, DefaultSizeBinding, ElementType,
|
|
Function, NativeClass, Type,
|
|
};
|
|
use crate::object_tree::{self, *};
|
|
use crate::parser::{identifier_text, syntax_nodes, SyntaxKind, SyntaxNode};
|
|
use crate::typeregister::TypeRegister;
|
|
|
|
/// Parse the contents of builtins.slint and fill the builtin type registry
|
|
/// `register` is the register to fill with the builtin types.
|
|
/// At this point, it really should already contain the basic Types (string, int, ...)
|
|
pub(crate) fn load_builtins(register: &mut TypeRegister) {
|
|
let mut diag = crate::diagnostics::BuildDiagnostics::default();
|
|
let node = crate::parser::parse(include_str!("builtins.slint").into(), None, &mut diag);
|
|
if !diag.is_empty() {
|
|
let vec = diag.to_string_vec();
|
|
#[cfg(feature = "display-diagnostics")]
|
|
diag.print();
|
|
panic!("Error parsing the builtin elements: {vec:?}");
|
|
}
|
|
|
|
assert_eq!(node.kind(), crate::parser::SyntaxKind::Document);
|
|
let doc: syntax_nodes::Document = node.into();
|
|
|
|
let mut natives = HashMap::<SmolStr, Rc<BuiltinElement>>::new();
|
|
|
|
let exports = doc
|
|
.ExportsList()
|
|
.flat_map(|e| {
|
|
e.Component()
|
|
.map(|x| {
|
|
let x = identifier_text(&x.DeclaredIdentifier()).unwrap();
|
|
(x.clone(), x)
|
|
})
|
|
.into_iter()
|
|
.chain(e.ExportSpecifier().map(|e| {
|
|
(
|
|
identifier_text(&e.ExportIdentifier()).unwrap(),
|
|
identifier_text(&e.ExportName().unwrap()).unwrap(),
|
|
)
|
|
}))
|
|
})
|
|
.collect::<HashMap<_, _>>();
|
|
|
|
for c in doc.Component().chain(doc.ExportsList().filter_map(|e| e.Component())) {
|
|
let id = identifier_text(&c.DeclaredIdentifier()).unwrap();
|
|
let e = c.Element();
|
|
let diag = RefCell::new(&mut diag);
|
|
let mut n = NativeClass::new_with_properties(
|
|
&id,
|
|
e.PropertyDeclaration()
|
|
.filter(|p| p.TwoWayBinding().is_none()) // aliases are handled further down
|
|
.map(|p| {
|
|
let prop_name = identifier_text(&p.DeclaredIdentifier()).unwrap();
|
|
|
|
let mut info = BuiltinPropertyInfo::new(object_tree::type_from_node(
|
|
p.Type().unwrap(),
|
|
*diag.borrow_mut(),
|
|
register,
|
|
));
|
|
|
|
info.property_visibility = PropertyVisibility::Private;
|
|
|
|
for token in p.children_with_tokens() {
|
|
if token.kind() != SyntaxKind::Identifier {
|
|
continue;
|
|
}
|
|
match (token.as_token().unwrap().text(), info.property_visibility) {
|
|
("in", PropertyVisibility::Private) => {
|
|
info.property_visibility = PropertyVisibility::Input
|
|
}
|
|
("out", PropertyVisibility::Private) => {
|
|
info.property_visibility = PropertyVisibility::Output
|
|
}
|
|
("in-out", PropertyVisibility::Private) => {
|
|
info.property_visibility = PropertyVisibility::InOut
|
|
}
|
|
("property", _) => (),
|
|
_ => unreachable!("invalid property keyword when parsing builtin file for property {id}::{prop_name}"),
|
|
}
|
|
}
|
|
|
|
if let Some(e) = p.BindingExpression() {
|
|
let ty = info.ty.clone();
|
|
info.default_value = BuiltinPropertyDefault::Expr(compiled(e, register, ty));
|
|
}
|
|
|
|
(prop_name, info)
|
|
})
|
|
.chain(e.CallbackDeclaration().map(|s| {
|
|
(
|
|
identifier_text(&s.DeclaredIdentifier()).unwrap(),
|
|
BuiltinPropertyInfo::new(Type::Callback(Rc::new(Function{
|
|
args: s
|
|
.CallbackDeclarationParameter()
|
|
.map(|a| {
|
|
object_tree::type_from_node(a.Type(), *diag.borrow_mut(), register)
|
|
})
|
|
.collect(),
|
|
return_type: s.ReturnType().map(|a| {
|
|
object_tree::type_from_node(
|
|
a.Type(),
|
|
*diag.borrow_mut(),
|
|
register,
|
|
)
|
|
}).unwrap_or(Type::Void),
|
|
arg_names: s
|
|
.CallbackDeclarationParameter()
|
|
.map(|a| a.DeclaredIdentifier().and_then(|x| identifier_text(&x)).unwrap_or_default())
|
|
.collect()
|
|
}))),
|
|
)
|
|
}))
|
|
);
|
|
n.deprecated_aliases = e
|
|
.PropertyDeclaration()
|
|
.flat_map(|p| {
|
|
if let Some(twb) = p.TwoWayBinding() {
|
|
let alias_name = identifier_text(&p.DeclaredIdentifier()).unwrap();
|
|
let alias_target = identifier_text(&twb.Expression().QualifiedName().expect(
|
|
"internal error: built-in aliases can only be declared within the type",
|
|
))
|
|
.unwrap();
|
|
Some((alias_name, alias_target))
|
|
} else {
|
|
None
|
|
}
|
|
})
|
|
.collect();
|
|
n.cpp_type = parse_annotation("cpp_type", &e).map(|x| x.unwrap());
|
|
n.rust_type_constructor = parse_annotation("rust_type_constructor", &e).map(|x| x.unwrap());
|
|
enum Base {
|
|
None,
|
|
Global,
|
|
NativeParent(Rc<BuiltinElement>),
|
|
}
|
|
let base = if c.child_text(SyntaxKind::Identifier).is_some_and(|t| t == "global") {
|
|
Base::Global
|
|
} else if let Some(base) = e.QualifiedName() {
|
|
let base = QualifiedTypeName::from_node(base).to_smolstr();
|
|
let base = natives.get(&base).unwrap().clone();
|
|
// because they are not taken from if we inherit from it
|
|
assert!(
|
|
base.additional_accepted_child_types.is_empty() && !base.additional_accept_self
|
|
);
|
|
n.parent = Some(base.native_class.clone());
|
|
Base::NativeParent(base)
|
|
} else {
|
|
Base::None
|
|
};
|
|
|
|
n.properties.extend(e.Function().map(|f| {
|
|
let name = identifier_text(&f.DeclaredIdentifier()).unwrap();
|
|
let return_type = f.ReturnType().map_or(Type::Void, |p| {
|
|
object_tree::type_from_node(p.Type(), *diag.borrow_mut(), register)
|
|
});
|
|
(
|
|
name,
|
|
BuiltinPropertyInfo::new(Type::Function(
|
|
Function { return_type, args: vec![], arg_names: vec![] }.into(),
|
|
)),
|
|
)
|
|
}));
|
|
|
|
let mut builtin = BuiltinElement::new(Rc::new(n));
|
|
builtin.is_global = matches!(base, Base::Global);
|
|
let properties = &mut builtin.properties;
|
|
if let Base::NativeParent(parent) = &base {
|
|
properties.extend(parent.properties.iter().map(|(k, v)| (k.clone(), v.clone())));
|
|
}
|
|
properties
|
|
.extend(builtin.native_class.properties.iter().map(|(k, v)| (k.clone(), v.clone())));
|
|
|
|
builtin.disallow_global_types_as_child_elements =
|
|
parse_annotation("disallow_global_types_as_child_elements", &e).is_some();
|
|
builtin.is_non_item_type = parse_annotation("is_non_item_type", &e).is_some();
|
|
builtin.is_internal = parse_annotation("is_internal", &e).is_some();
|
|
builtin.accepts_focus = parse_annotation("accepts_focus", &e).is_some();
|
|
builtin.default_size_binding = parse_annotation("default_size_binding", &e)
|
|
.map(|size_type| match size_type.as_deref() {
|
|
Some("expands_to_parent_geometry") => DefaultSizeBinding::ExpandsToParentGeometry,
|
|
Some("implicit_size") => DefaultSizeBinding::ImplicitSize,
|
|
other => panic!("invalid default size binding {other:?}"),
|
|
})
|
|
.unwrap_or(DefaultSizeBinding::None);
|
|
builtin.additional_accepted_child_types = e
|
|
.SubElement()
|
|
.filter_map(|s| {
|
|
let a = identifier_text(&s.Element().QualifiedName().unwrap()).unwrap();
|
|
if a == builtin.native_class.class_name {
|
|
builtin.additional_accept_self = true;
|
|
None
|
|
} else {
|
|
let t = natives[&a].clone();
|
|
Some((a, t))
|
|
}
|
|
})
|
|
.collect();
|
|
if let Some(builtin_name) = exports.get(&id) {
|
|
if !matches!(&base, Base::Global) {
|
|
builtin.name.clone_from(builtin_name);
|
|
register.add_builtin(Rc::new(builtin));
|
|
} else {
|
|
let glob = Rc::new(Component {
|
|
id: builtin_name.clone(),
|
|
root_element: Rc::new(RefCell::new(Element {
|
|
base_type: ElementType::Builtin(Rc::new(builtin)),
|
|
..Default::default()
|
|
})),
|
|
..Default::default()
|
|
});
|
|
glob.root_element.borrow_mut().enclosing_component = Rc::downgrade(&glob);
|
|
register.add(glob);
|
|
}
|
|
} else {
|
|
natives.insert(id, Rc::new(builtin));
|
|
}
|
|
}
|
|
|
|
register.property_animation_type =
|
|
ElementType::Builtin(natives.remove("PropertyAnimation").unwrap());
|
|
|
|
register.empty_type = ElementType::Builtin(natives.remove("Empty").unwrap());
|
|
|
|
if !diag.is_empty() {
|
|
let vec = diag.to_string_vec();
|
|
#[cfg(feature = "display-diagnostics")]
|
|
diag.print();
|
|
panic!("Error loading the builtin elements: {vec:?}");
|
|
}
|
|
}
|
|
|
|
/// Compile an expression, knowing that the expression is basic (does not have lookup to other things)
|
|
fn compiled(
|
|
node: syntax_nodes::BindingExpression,
|
|
type_register: &TypeRegister,
|
|
ty: Type,
|
|
) -> Expression {
|
|
let mut diag = crate::diagnostics::BuildDiagnostics::default();
|
|
let e = Expression::from_binding_expression_node(
|
|
node.clone().into(),
|
|
&mut crate::lookup::LookupCtx::empty_context(type_register, &mut diag),
|
|
)
|
|
.maybe_convert_to(ty, &node, &mut diag);
|
|
if diag.has_errors() {
|
|
let vec = diag.to_string_vec();
|
|
#[cfg(feature = "display-diagnostics")]
|
|
diag.print();
|
|
panic!("Error parsing the builtin elements: {vec:?}");
|
|
}
|
|
e
|
|
}
|
|
|
|
/// Find out if there are comments that starts with `//-key` and returns `None`
|
|
/// if no annotation with this key is found, or `Some(None)` if it is found without a value
|
|
/// or `Some(Some(value))` if there is a `//-key:value` match
|
|
fn parse_annotation(key: &str, node: &SyntaxNode) -> Option<Option<SmolStr>> {
|
|
for x in node.children_with_tokens() {
|
|
if x.kind() == SyntaxKind::Comment {
|
|
if let Some(comment) = x
|
|
.as_token()
|
|
.unwrap()
|
|
.text()
|
|
.strip_prefix("//-")
|
|
.and_then(|x| x.trim_end().strip_prefix(key))
|
|
{
|
|
if comment.is_empty() {
|
|
return Some(None);
|
|
}
|
|
if let Some(comment) = comment.strip_prefix(':') {
|
|
return Some(Some(comment.into()));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
None
|
|
}
|