diff --git a/api/sixtyfps-cpp/include/sixtyfps_string.h b/api/sixtyfps-cpp/include/sixtyfps_string.h index 2940dc632..02349727d 100644 --- a/api/sixtyfps-cpp/include/sixtyfps_string.h +++ b/api/sixtyfps-cpp/include/sixtyfps_string.h @@ -58,6 +58,9 @@ struct SharedString return std::string_view(a) != std::string_view(b); } + friend std::ostream& operator<< (std::ostream& stream, const SharedString& shared_string) { + return stream << std::string_view(shared_string); + } private: /// Use SharedString::from_number explicit SharedString(double n) { cbindgen_private::sixtyfps_shared_string_from_number(this, n); } diff --git a/sixtyfps_compiler/expression_tree.rs b/sixtyfps_compiler/expression_tree.rs index 8c08d22bf..7fe0779b6 100644 --- a/sixtyfps_compiler/expression_tree.rs +++ b/sixtyfps_compiler/expression_tree.rs @@ -233,6 +233,7 @@ pub enum Expression { /// A function call FunctionCall { function: Box, + arguments: Vec, }, /// A SelfAssignment or an Assignment. When op is '=' this is a signel assignment. @@ -335,7 +336,7 @@ impl Expression { } Expression::Cast { to, .. } => to.clone(), Expression::CodeBlock(sub) => sub.last().map_or(Type::Void, |e| e.ty()), - Expression::FunctionCall { function } => function.ty(), + Expression::FunctionCall { function, .. } => function.ty(), Expression::SelfAssignment { .. } => Type::Void, Expression::ResourceReference { .. } => Type::Resource, Expression::Condition { condition: _, true_expr, false_expr } => { @@ -397,11 +398,12 @@ impl Expression { Expression::RepeaterModelReference { .. } => {} Expression::Cast { from, .. } => visitor(&**from), Expression::CodeBlock(sub) => { - for e in sub { - visitor(e) - } + sub.iter().for_each(visitor); + } + Expression::FunctionCall { function, arguments } => { + visitor(&**function); + arguments.iter().for_each(visitor); } - Expression::FunctionCall { function } => visitor(&**function), Expression::SelfAssignment { lhs, rhs, .. } => { visitor(&**lhs); visitor(&**rhs); @@ -457,11 +459,12 @@ impl Expression { Expression::RepeaterModelReference { .. } => {} Expression::Cast { from, .. } => visitor(&mut **from), Expression::CodeBlock(sub) => { - for e in sub { - visitor(e) - } + sub.iter_mut().for_each(visitor); + } + Expression::FunctionCall { function, arguments } => { + visitor(&mut **function); + arguments.iter_mut().for_each(visitor); } - Expression::FunctionCall { function } => visitor(&mut **function), Expression::SelfAssignment { lhs, rhs, .. } => { visitor(&mut **lhs); visitor(&mut **rhs); @@ -561,6 +564,7 @@ impl Expression { function: Box::new(Expression::BuiltinFunctionReference( BuiltinFunction::GetWindowScaleFactor, )), + arguments: vec![], }), op: '/', }, @@ -570,6 +574,7 @@ impl Expression { function: Box::new(Expression::BuiltinFunctionReference( BuiltinFunction::GetWindowScaleFactor, )), + arguments: vec![], }), op: '*', }, diff --git a/sixtyfps_compiler/generator/cpp.rs b/sixtyfps_compiler/generator/cpp.rs index 577f1d5cc..bff81523e 100644 --- a/sixtyfps_compiler/generator/cpp.rs +++ b/sixtyfps_compiler/generator/cpp.rs @@ -186,7 +186,7 @@ use crate::diagnostics::{BuildDiagnostics, CompilerDiagnostic, Level, Spanned}; use crate::expression_tree::{BuiltinFunction, EasingCurve, Expression, ExpressionSpanned}; use crate::layout::{gen::LayoutItemCodeGen, Layout, LayoutElement}; use crate::object_tree::{Component, Element, ElementRc, RepeatedElementInfo}; -use crate::{parser::SyntaxNodeWithSourceFile, typeregister::Type}; +use crate::typeregister::Type; use cpp_ast::*; use std::collections::HashMap; use std::rc::Rc; @@ -944,18 +944,18 @@ fn compile_expression(e: &crate::expression_tree::Expression, component: &Rc { - let access = - access_member(&element.upgrade().unwrap(), name.as_str(), component, "self"); - format!(r#"{}.emit()"#, access) - } + Expression::SignalReference(NamedReference { element, name }) => format!( + "{}.emit", + access_member(&element.upgrade().unwrap(), name.as_str(), component, "self") + ), Expression::BuiltinFunctionReference(funcref) => match funcref { BuiltinFunction::GetWindowScaleFactor => { - format!("{}.scale_factor()", window_ref_expression(component)) + format!("{}.scale_factor", window_ref_expression(component)) + } + BuiltinFunction::Debug => { + "[](auto... args){ (std::cout << ... << args) << std::endl; return nullptr; }" + .into() } - BuiltinFunction::Debug => - "[]{ std::cout << \"FIXME: the debug statement in C++ should print the argument\" << std::endl; return nullptr; }()".into() - , }, Expression::RepeaterIndexReference { element } => { let access = access_member( @@ -975,7 +975,9 @@ fn compile_expression(e: &crate::expression_tree::Expression, component: &Rc format!("std::get<{}>(args)", index), + Expression::FunctionParameterReference { index, .. } => { + format!("std::get<{}>(args)", index) + } Expression::StoreLocalVariable { name, value } => { format!("auto {} = {};", name, compile_expression(value, component)) } @@ -1010,12 +1012,10 @@ fn compile_expression(e: &crate::expression_tree::Expression, component: &Rc { - if matches!(function.ty(), Type::Signal{..} | Type::Function{..}) { - compile_expression(&*function, component) - } else { - format!("\n#error the function `{:?}` is not a signal\n", function) - } + Expression::FunctionCall { function, arguments } => { + let args = + arguments.iter().map(|e| compile_expression(e, component)).collect::>(); + format!("{}({})", compile_expression(&function, component), args.join(", ")) } Expression::SelfAssignment { lhs, rhs, op } => match &**lhs { Expression::PropertyReference(NamedReference { element, name }) => { diff --git a/sixtyfps_compiler/generator/rust.rs b/sixtyfps_compiler/generator/rust.rs index 7c8a0eebb..589375ea1 100644 --- a/sixtyfps_compiler/generator/rust.rs +++ b/sixtyfps_compiler/generator/rust.rs @@ -112,7 +112,7 @@ fn generate_component( quote!( #[allow(dead_code)] pub fn #on_ident(self: ::core::pin::Pin<&Self>, f: impl Fn() + 'static) { - Self::FIELD_OFFSETS.#prop_ident.apply_pin(self).set_handler(move |()|f()) + Self::FIELD_OFFSETS.#prop_ident.apply_pin(self).set_handler(move |_: &()|f()) } ) .into(), @@ -704,11 +704,9 @@ fn compile_expression(e: &Expression, component: &Rc) -> TokenStream Expression::BuiltinFunctionReference(funcref) => match funcref { BuiltinFunction::GetWindowScaleFactor => { let window_ref = window_ref_expression(component); - quote!(#window_ref.scale_factor()) - } - BuiltinFunction::Debug => { - quote!(println!("FIXME: the debug statement in rust should print the argument");) + quote!(#window_ref.scale_factor) } + BuiltinFunction::Debug => quote!((|x| println!("{:?}", x))), }, Expression::RepeaterIndexReference { element } => { let access = access_member( @@ -750,22 +748,20 @@ fn compile_expression(e: &Expression, component: &Rc) -> TokenStream let map = sub.iter().map(|e| compile_expression(e, &component)); quote!({ #(#map);* }) } - Expression::SignalReference(NamedReference { element, name, .. }) => { - let access = access_member( - &element.upgrade().unwrap(), - name.as_str(), - component, - quote!(_self), - false, - ); - quote!(#access.emit(&())) - } - Expression::FunctionCall { function } => { - if matches!(function.ty(), Type::Signal{..} | Type::Function{..}) { - compile_expression(function, &component) + Expression::SignalReference(NamedReference { element, name, .. }) => access_member( + &element.upgrade().unwrap(), + name.as_str(), + component, + quote!(_self), + false, + ), + Expression::FunctionCall { function, arguments } => { + let f = compile_expression(function, &component); + let a = arguments.iter().map(|a| compile_expression(a, &component)); + if matches!(function.ty(), Type::Signal{..}) { + quote! { #f.emit(&(#((#a).clone(),)*).into())} } else { - let error = format!("the function {:?} is not a signal", e); - quote!(compile_error! {#error}) + quote! { #f(#(#a.clone()),*)} } } Expression::SelfAssignment { lhs, rhs, op } => match &**lhs { diff --git a/sixtyfps_compiler/passes/resolving.rs b/sixtyfps_compiler/passes/resolving.rs index 1050f1339..b991a8631 100644 --- a/sixtyfps_compiler/passes/resolving.rs +++ b/sixtyfps_compiler/passes/resolving.rs @@ -253,13 +253,7 @@ impl Expression { }) }) .or_else(|| { - node.FunctionCallExpression().map(|n| Expression::FunctionCall { - function: Box::new( - n.child_node(SyntaxKind::Expression) - .map(|n| Self::from_expression_node(n.into(), ctx)) - .unwrap_or(Expression::Invalid), - ), - }) + node.FunctionCallExpression().map(|n| Self::from_function_call_node(n, ctx)) }) .or_else(|| node.SelfAssignment().map(|n| Self::from_self_assignement_node(n, ctx))) .or_else(|| node.BinaryExpression().map(|n| Self::from_binary_expression_node(n, ctx))) @@ -470,6 +464,44 @@ impl Expression { Self::Invalid } + fn from_function_call_node( + node: syntax_nodes::FunctionCallExpression, + ctx: &mut LookupCtx, + ) -> Expression { + let mut sub_expr = + node.Expression().map(|n| (Self::from_expression_node(n.clone(), ctx), n)); + let function = Box::new(sub_expr.next().map_or(Expression::Invalid, |e| e.0)); + let arguments = sub_expr.collect::>(); + + let arguments = match function.ty() { + Type::Function { args, .. } | Type::Signal { args } => { + if arguments.len() != args.len() { + ctx.diag.push_error( + format!( + "The signal or function expects {} arguments, but {} are provided", + args.len(), + arguments.len() + ), + &node, + ); + arguments.into_iter().map(|x| x.0).collect() + } else { + arguments + .into_iter() + .zip(args.iter()) + .map(|((e, node), ty)| e.maybe_convert_to(ty.clone(), &node, &mut ctx.diag)) + .collect() + } + } + _ => { + ctx.diag.push_error("The expression is not a function".into(), &node); + arguments.into_iter().map(|x| x.0).collect() + } + }; + + Expression::FunctionCall { function, arguments } + } + fn from_self_assignement_node( node: syntax_nodes::SelfAssignment, ctx: &mut LookupCtx, diff --git a/sixtyfps_compiler/tests/syntax/lookup/signal_arg.60 b/sixtyfps_compiler/tests/syntax/lookup/signal_arg.60 index aaa65a136..10eecc197 100644 --- a/sixtyfps_compiler/tests/syntax/lookup/signal_arg.60 +++ b/sixtyfps_compiler/tests/syntax/lookup/signal_arg.60 @@ -18,6 +18,17 @@ Xxx := Rectangle { // ^error{Assignement need to be done on a property} width = x; // ^error{Cannot convert string to length} + plop("hallo", #fff, 42); + plop("hallo", #fff,); +// ^error{The signal or function expects 3 arguments, but 2 are provided} + plop("hallo", #fff, 42, true); +// ^error{The signal or function expects 3 arguments, but 4 are provided} + plop(42, 42, 42); +// ^error{Cannot convert float to color} + hello(45, fff) +// ^error{The expression is not a function} +// ^^error{Unknown unqualified identifier 'fff'} + } x: 12px;