Parse declaration of signal with arguments

This commit is contained in:
Olivier Goffart 2020-09-07 17:41:24 +02:00
parent 5682b7c989
commit 9f026c820d
12 changed files with 68 additions and 33 deletions

View file

@ -97,7 +97,7 @@ fn create<'cx>(
cx.throw_error(format!("Property {} not found in the component", prop_name))
})?
.clone();
if ty == Type::Signal {
if let Type::Signal { .. } = ty {
let _fun = value.downcast_or_throw::<JsFunction, _>(cx)?;
let fun_idx = persistent_context.allocate(cx, value);
component_type
@ -152,7 +152,7 @@ fn to_eval_value<'cx>(
| Type::Native(_)
| Type::Function { .. }
| Type::Model
| Type::Signal
| Type::Signal { .. }
| Type::Easing
| Type::PathElements => cx.throw_error("Cannot convert to a Sixtyfps property value"),
Type::Float32 | Type::Int32 | Type::Duration | Type::Length | Type::LogicalLength => {
@ -245,7 +245,7 @@ declare_types! {
let properties = ct.properties();
let array = JsArray::new(&mut cx, properties.len() as u32);
let mut len: u32 = 0;
for (p, _) in properties.iter().filter(|(_, prop_type)| **prop_type == Type::Signal) {
for (p, _) in properties.iter().filter(|(_, prop_type)| matches!(**prop_type, Type::Signal{..})) {
let prop_name = JsString::new(&mut cx, p);
array.set(&mut cx, len, prop_name)?;
len = len + 1;

View file

@ -293,7 +293,9 @@ impl Expression {
Expression::StringLiteral(_) => Type::String,
Expression::NumberLiteral(_, unit) => unit.ty(),
Expression::BoolLiteral(_) => Type::Bool,
Expression::SignalReference { .. } => Type::Signal,
Expression::SignalReference(NamedReference { element, name }) => {
element.upgrade().unwrap().borrow().lookup_property(name)
}
Expression::PropertyReference(NamedReference { element, name }) => {
element.upgrade().unwrap().borrow().lookup_property(name)
}
@ -619,7 +621,7 @@ impl Expression {
| Type::Component(_)
| Type::Builtin(_)
| Type::Native(_)
| Type::Signal
| Type::Signal { .. }
| Type::Function { .. }
| Type::Void => Expression::Invalid,
Type::Float32 => Expression::NumberLiteral(0., Unit::None),

View file

@ -302,7 +302,7 @@ fn handle_item(item: &Element, main_struct: &mut Struct, init: &mut Vec<String>)
let id = &item.id;
init.extend(item.bindings.iter().map(|(s, i)| {
if matches!(item.lookup_property(s.as_str()), Type::Signal) {
if matches!(item.lookup_property(s.as_str()), Type::Signal{..}) {
let signal_accessor_prefix = if item.property_declarations.contains_key(s) {
String::new()
} else {
@ -483,7 +483,7 @@ fn generate_component(
let mut init = vec!["[[maybe_unused]] auto self = this;".into()];
for (cpp_name, property_decl) in component.root_element.borrow().property_declarations.iter() {
let ty = if property_decl.property_type == Type::Signal {
let ty = if matches!(property_decl.property_type, Type::Signal{..}) {
if property_decl.expose_in_public_api && is_root {
let signal_emitter = vec![format!("{}.emit();", cpp_name)];
component_struct.members.push((
@ -1006,7 +1006,7 @@ fn compile_expression(e: &crate::expression_tree::Expression, component: &Rc<Com
format!("[&]{{ {} }}()", x.join(";"))
}
Expression::FunctionCall { function } => {
if matches!(function.ty(), Type::Signal | Type::Function{..}) {
if matches!(function.ty(), Type::Signal{..} | Type::Function{..}) {
compile_expression(&*function, component)
} else {
format!("\n#error the function `{:?}` is not a signal\n", function)

View file

@ -94,7 +94,7 @@ fn generate_component(
let mut property_and_signal_accessors: Vec<TokenStream> = vec![];
for (prop_name, property_decl) in component.root_element.borrow().property_declarations.iter() {
let prop_ident = quote::format_ident!("{}", prop_name);
if property_decl.property_type == Type::Signal {
if matches!(property_decl.property_type, Type::Signal{..}) {
declared_signals.push(prop_ident.clone());
if property_decl.expose_in_public_api {
let emitter_ident = quote::format_ident!("emit_{}", prop_name);
@ -299,7 +299,7 @@ fn generate_component(
let rust_property = quote!(#rust_property_accessor_prefix#rust_property_ident);
let tokens_for_expression = compile_expression(binding_expression, &component);
if matches!(item.lookup_property(k.as_str()), Type::Signal) {
if matches!(item.lookup_property(k.as_str()), Type::Signal{..}) {
init.push(quote!(
self_pinned.#rust_property.set_handler({
let self_weak = sixtyfps::re_exports::PinWeak::downgrade(self_pinned.clone());
@ -757,7 +757,7 @@ fn compile_expression(e: &Expression, component: &Rc<Component>) -> TokenStream
quote!(#access.emit(()))
}
Expression::FunctionCall { function } => {
if matches!(function.ty(), Type::Signal | Type::Function{..}) {
if matches!(function.ty(), Type::Signal{..} | Type::Function{..}) {
compile_expression(function, &component)
} else {
let error = format!("the function {:?} is not a signal", e);

View file

@ -259,10 +259,11 @@ impl Element {
let name_token =
sig_decl.DeclaredIdentifier().child_token(SyntaxKind::Identifier).unwrap();
let name = name_token.text().to_string();
let args = sig_decl.Type().map(|node_ty| type_from_node(node_ty, diag, tr)).collect();
r.property_declarations.insert(
name,
PropertyDeclaration {
property_type: Type::Signal,
property_type: Type::Signal { args },
type_node: Some(sig_decl.into()),
..Default::default()
},
@ -276,7 +277,7 @@ impl Element {
};
let name = name_token.text().to_string();
let prop_type = r.lookup_property(&name);
if !matches!(prop_type, Type::Signal) {
if !matches!(prop_type, Type::Signal{..}) {
diag.push_error(format!("'{}' is not a signal in {}", name, base), &name_token);
}
if r.bindings
@ -522,7 +523,9 @@ impl Element {
diag.push_error(
match prop_type {
Type::Invalid => format!("Unknown property {} in {}", name, base),
Type::Signal => format!("'{}' is a signal. Use `=>` to connect", name),
Type::Signal { .. } => {
format!("'{}' is a signal. Use `=>` to connect", name)
}
_ => format!("Cannot assing to {} in {}", name, base),
},
&name_token,

View file

@ -283,7 +283,7 @@ declare_syntax! {
RepeatedElement -> [ ?DeclaredIdentifier, ?RepeatedIndex, Expression , Element],
RepeatedIndex -> [],
ConditionalElement -> [ Expression , Element],
SignalDeclaration -> [ DeclaredIdentifier ],
SignalDeclaration -> [ DeclaredIdentifier, *Type ],
SignalConnection -> [ CodeBlock ],
/// Declaration of a propery.
PropertyDeclaration-> [ Type , DeclaredIdentifier, ?BindingExpression ],
@ -428,7 +428,11 @@ mod parser_trait {
checkpoint: Option<Self::Checkpoint>,
token: NodeToken,
);
fn peek(&mut self) -> Token;
/// Same as nth(0)
fn peek(&mut self) -> Token {
self.nth(0)
}
/// Peek the n'th token, not including whitespaces and comments
fn nth(&mut self, n: usize) -> Token;
fn consume(&mut self);
@ -540,11 +544,6 @@ impl Parser for DefaultParser {
self.builder.finish_node();
}
fn peek(&mut self) -> Token {
self.consume_ws();
self.current_token()
}
/// Peek the n'th token, not including whitespaces and comments
fn nth(&mut self, mut n: usize) -> Token {
self.consume_ws();

View file

@ -310,6 +310,10 @@ fn parse_signal_connection(p: &mut impl Parser) {
#[cfg_attr(test, parser_test)]
/// ```test,SignalDeclaration
/// signal foobar;
/// signal my_signal();
/// signal foo(int, string);
/// signal one_arg({ a: string, b: string});
/// signal end_coma(a, b, c,);
/// ```
/// Must consume at least one token
fn parse_signal_declaration(p: &mut impl Parser) {
@ -320,6 +324,15 @@ fn parse_signal_declaration(p: &mut impl Parser) {
let mut p = p.start_node(SyntaxKind::DeclaredIdentifier);
p.expect(SyntaxKind::Identifier);
}
if p.test(SyntaxKind::LParent) {
while p.peek().kind() != SyntaxKind::RParent {
parse_type(&mut *p);
if !p.test(SyntaxKind::Comma) {
break;
}
}
p.expect(SyntaxKind::RParent);
}
p.expect(SyntaxKind::Semicolon);
}

View file

@ -73,7 +73,10 @@ fn generate_test(fn_name: &str, doc: &str) -> String {
{{
let mut p = DefaultParser::new("{source}".to_owned());
{fn}(&mut p);
assert!(!p.diags.has_error());
let has_error = p.diags.has_error();
//#[cfg(feature = "display-diagnostics")]
//p.diags.print();
assert!(!has_error);
assert_eq!(p.cursor, p.tokens.len());
{verify}
}}

View file

@ -17,7 +17,7 @@ use std::{cell::RefCell, collections::HashMap};
pub fn deduplicate_property_read(component: &Component) {
recurse_elem(&component.root_element, &(), &mut |elem, _| {
visit_element_expressions(elem, |expr, ty| {
if ty() == Type::Signal {
if matches!(ty(), Type::Signal{..}) {
// Signal handler can't be optimizes because they can have side effect.
// But that's fine as they also do not register dependencies
return;

View file

@ -351,7 +351,7 @@ impl Expression {
name: prop_name.text().to_string(),
});
return maybe_lookup_object(prop, it, ctx);
} else if matches!(p, Type::Signal) {
} else if matches!(p, Type::Signal{..}) {
if let Some(x) = it.next() {
ctx.diag.push_error("Cannot access fields of signal".into(), &x)
}
@ -382,7 +382,7 @@ impl Expression {
name: first_str.to_string(),
});
return maybe_lookup_object(prop, it, ctx);
} else if matches!(property, Type::Signal) {
} else if matches!(property, Type::Signal{..}) {
if let Some(x) = it.next() {
ctx.diag.push_error("Cannot access fields of signal".into(), &x)
}

View file

@ -20,7 +20,9 @@ pub enum Type {
Builtin(Rc<BuiltinElement>),
Native(Rc<NativeClass>),
Signal,
Signal {
args: Vec<Type>,
},
Function {
return_type: Box<Type>,
args: Vec<Type>,
@ -55,7 +57,7 @@ impl core::cmp::PartialEq for Type {
(Type::Component(a), Type::Component(b)) => Rc::ptr_eq(a, b),
(Type::Builtin(a), Type::Builtin(b)) => Rc::ptr_eq(a, b),
(Type::Native(a), Type::Native(b)) => Rc::ptr_eq(a, b),
(Type::Signal, Type::Signal) => true,
(Type::Signal { args: a }, Type::Signal { args: b }) => a == b,
(
Type::Function { return_type: lhs_rt, args: lhs_args },
Type::Function { return_type: rhs_rt, args: rhs_args },
@ -88,7 +90,20 @@ impl Display for Type {
Type::Component(c) => c.id.fmt(f),
Type::Builtin(b) => b.native_class.class_name.fmt(f),
Type::Native(b) => b.class_name.fmt(f),
Type::Signal => write!(f, "signal"),
Type::Signal { args } => {
write!(f, "signal")?;
if !args.is_empty() {
write!(f, "(")?;
for (i, arg) in args.iter().enumerate() {
if i > 0 {
write!(f, ",")?;
}
write!(f, "{}", arg)?;
}
write!(f, ")")?
}
Ok(())
}
Type::Function { return_type, args } => {
write!(f, "function(")?;
for (i, arg) in args.iter().enumerate() {
@ -555,7 +570,7 @@ impl TypeRegister {
("mouse_y", Type::Length),
("pressed_x", Type::Length),
("pressed_y", Type::Length),
("clicked", Type::Signal),
("clicked", Type::Signal { args: vec![] }),
],
);
@ -683,7 +698,7 @@ impl TypeRegister {
("height", Type::Length),
("text", Type::String),
("pressed", Type::Bool),
("clicked", Type::Signal),
("clicked", Type::Signal { args: vec![] }),
],
);
native_class(
@ -696,7 +711,7 @@ impl TypeRegister {
("height", Type::Length),
("text", Type::String),
("checked", Type::Bool),
("toggled", Type::Signal),
("toggled", Type::Signal { args: vec![] }),
],
);
native_class(

View file

@ -470,7 +470,7 @@ fn generate_component<'id>(
Type::LogicalLength => animated_property_info::<f32>(),
Type::Resource => property_info::<Resource>(),
Type::Bool => property_info::<bool>(),
Type::Signal => {
Type::Signal { .. } => {
custom_signals.insert(name.clone(), builder.add_field_type::<Signal<()>>());
continue;
}
@ -610,7 +610,7 @@ pub fn instantiate<'id>(
let elem = item_within_component.elem.borrow();
for (prop, expr) in &elem.bindings {
let ty = elem.lookup_property(prop.as_str());
if ty == Type::Signal {
if let Type::Signal { .. } = ty {
let signal = item_within_component
.rtti
.signals