Skip to content

Commit

Permalink
Implement cubic-bezier
Browse files Browse the repository at this point in the history
  • Loading branch information
ogoffart committed Nov 13, 2020
1 parent 2282697 commit d499e86
Show file tree
Hide file tree
Showing 13 changed files with 198 additions and 18 deletions.
2 changes: 1 addition & 1 deletion examples/slide_puzzle/slide_puzzle.60
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ export MainWindow := Window {
+ (parent.width - (4*pieces_size + 3*pieces_spacing))/2;
y: px * (pieces_size + pieces_spacing)
+ (parent.height - (4*pieces_size + 3*pieces_spacing))/2;
animate px , py { duration: 100ms; easing: ease-out; }
animate px , py { duration: 200ms; easing: cubic-bezier(0.17,0.76,0.4,1.9); }
animate border-width, border-radius { duration: 500ms; easing: ease-out; }

Text {
Expand Down
16 changes: 16 additions & 0 deletions sixtyfps_compiler/expression_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,14 @@ pub enum BuiltinFunction {
StringIsFloat,
}

#[derive(Debug, Clone)]
/// A builtin function which is handled by the compiler pass
pub enum BuiltinMacroFunction {
Min,
Max,
CubicBezier,
}

impl BuiltinFunction {
pub fn ty(&self) -> Type {
match self {
Expand Down Expand Up @@ -214,6 +222,10 @@ pub enum Expression {
member: Box<Expression>,
},

/// Reference to a macro understood by the compiler.
/// These should be transformed to other expression before reaching generation
BuiltinMacroReference(BuiltinMacroFunction, NodeOrTokenWithSourceFile),

/// A reference to a specific element. This isn't possible to create in .60 syntax itself, but intermediate passes may generate this
/// type of expression.
ElementReference(Weak<RefCell<Element>>),
Expand Down Expand Up @@ -350,6 +362,7 @@ impl Expression {
}
Expression::BuiltinFunctionReference(funcref) => funcref.ty(),
Expression::MemberFunction { member, .. } => member.ty(),
Expression::BuiltinMacroReference { .. } => Type::Invalid, // We don't know the type
Expression::ElementReference(_) => Type::ElementReference,
Expression::RepeaterIndexReference { .. } => Type::Int32,
Expression::RepeaterModelReference { element } => {
Expand Down Expand Up @@ -448,6 +461,7 @@ impl Expression {
visitor(&**base);
visitor(&**member);
}
Expression::BuiltinMacroReference { .. } => {}
Expression::ElementReference(_) => {}
Expression::ObjectAccess { base, .. } => visitor(&**base),
Expression::RepeaterIndexReference { .. } => {}
Expand Down Expand Up @@ -517,6 +531,7 @@ impl Expression {
visitor(&mut **base);
visitor(&mut **member);
}
Expression::BuiltinMacroReference { .. } => {}
Expression::ElementReference(_) => {}
Expression::ObjectAccess { base, .. } => visitor(&mut **base),
Expression::RepeaterIndexReference { .. } => {}
Expand Down Expand Up @@ -584,6 +599,7 @@ impl Expression {
Expression::RepeaterIndexReference { .. } => false,
Expression::RepeaterModelReference { .. } => false,
Expression::FunctionParameterReference { .. } => false,
Expression::BuiltinMacroReference { .. } => false,
Expression::ObjectAccess { base, .. } => base.is_constant(),
Expression::Cast { from, to } => {
from.is_constant() && !matches!(to, Type::Length | Type::LogicalLength)
Expand Down
1 change: 1 addition & 0 deletions sixtyfps_compiler/generator/cpp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1335,6 +1335,7 @@ fn compile_expression(e: &crate::expression_tree::Expression, component: &Rc<Com
},
Expression::ElementReference(_) => todo!("Element references are only supported in the context of built-in function calls at the moment"),
Expression::MemberFunction { .. } => panic!("member function expressions must not appear in the code generator anymore"),
Expression::BuiltinMacroReference { .. } => panic!("macro expressions must not appear in the code generator anymore"),
Expression::RepeaterIndexReference { element } => {
let access = access_member(
&element.upgrade().unwrap().borrow().base_type.as_component().root_element,
Expand Down
1 change: 1 addition & 0 deletions sixtyfps_compiler/generator/rust.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1067,6 +1067,7 @@ fn compile_expression(e: &Expression, component: &Rc<Component>) -> TokenStream
},
Expression::ElementReference(_) => todo!("Element references are only supported in the context of built-in function calls at the moment"),
Expression::MemberFunction{ .. } => panic!("member function expressions must not appear in the code generator anymore"),
Expression::BuiltinMacroReference { .. } => panic!("macro expressions must not appear in the code generator anymore"),
Expression::RepeaterIndexReference { element } => {
let access = access_member(
&element.upgrade().unwrap().borrow().base_type.as_component().root_element,
Expand Down
2 changes: 2 additions & 0 deletions sixtyfps_compiler/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ pub mod typeloader;
pub mod typeregister;

mod passes {
pub mod check_expressions;
pub mod collect_globals;
pub mod collect_resources;
pub mod compile_paths;
Expand Down Expand Up @@ -147,6 +148,7 @@ pub async fn run_passes<'a>(
) {
passes::resolving::resolve_expressions(doc, diag);
passes::inlining::inline(doc);
passes::check_expressions::check_expressions(doc, diag);
passes::compile_paths::compile_paths(&doc.root_component, &doc.local_registry, diag);
passes::unique_id::assign_unique_id(&doc.root_component);
passes::focus_item::determine_initial_focus_item(&doc.root_component, diag);
Expand Down
36 changes: 36 additions & 0 deletions sixtyfps_compiler/passes/check_expressions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/* LICENSE BEGIN
This file is part of the SixtyFPS Project -- https://sixtyfps.io
Copyright (c) 2020 Olivier Goffart <[email protected]>
Copyright (c) 2020 Simon Hausmann <[email protected]>
SPDX-License-Identifier: GPL-3.0-only
This file is also available under commercial licensing terms.
Please contact [email protected] for more information.
LICENSE END */

use crate::diagnostics::BuildDiagnostics;
use crate::expression_tree::Expression;
use crate::object_tree::{recurse_elem, visit_element_expressions};

/// Check the validity of expressions
///
/// - Make sure that there is no uncalled member function or macro
pub fn check_expressions(doc: &crate::object_tree::Document, diag: &mut BuildDiagnostics) {
for component in &doc.inner_components {
recurse_elem(&component.root_element, &(), &mut |elem, _| {
visit_element_expressions(elem, |e, _, _| check_expression(e, diag));
})
}
}

fn check_expression(e: &Expression, diag: &mut BuildDiagnostics) -> () {
match e {
Expression::MemberFunction { base_node, .. } => {
diag.push_error("Member function must be called".into(), base_node);
}
Expression::BuiltinMacroReference(_, node) => {
diag.push_error("Builtin function must be called".into(), node);
}
_ => e.visit(|e| check_expression(e, diag)),
}
}
78 changes: 64 additions & 14 deletions sixtyfps_compiler/passes/resolving.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ use crate::diagnostics::BuildDiagnostics;
use crate::expression_tree::*;
use crate::langtype::Type;
use crate::object_tree::*;
use crate::parser::{identifier_text, syntax_nodes, SyntaxKind, SyntaxNodeWithSourceFile};
use crate::parser::{
identifier_text, syntax_nodes, NodeOrTokenWithSourceFile, SyntaxKind, SyntaxNodeWithSourceFile,
};
use crate::typeregister::TypeRegister;
use std::{collections::HashMap, rc::Rc};

Expand Down Expand Up @@ -481,7 +483,12 @@ impl Expression {
"ease_in" => Some(EasingCurve::CubicBezier(0.42, 0.0, 1.0, 1.0)),
"ease_in_out" => Some(EasingCurve::CubicBezier(0.42, 0.0, 0.58, 1.0)),
"ease_out" => Some(EasingCurve::CubicBezier(0.0, 0.0, 0.58, 1.0)),
"cubic_bezier" => todo!("Not yet implemented"),
"cubic_bezier" => {
return Expression::BuiltinMacroReference(
BuiltinMacroFunction::CubicBezier,
first.into(),
)
}
_ => None,
};
if let Some(curve) = value {
Expand All @@ -497,9 +504,16 @@ impl Expression {
}

// Builtin functions FIXME: handle that in a registery or something
if first_str == "debug" {
return Expression::BuiltinFunctionReference(BuiltinFunction::Debug);
}
match first_str.as_str() {
"debug" => return Expression::BuiltinFunctionReference(BuiltinFunction::Debug),
"max" => {
return Expression::BuiltinMacroReference(BuiltinMacroFunction::Max, first.into())
}
"min" => {
return Expression::BuiltinMacroReference(BuiltinMacroFunction::Min, first.into())
}
_ => {}
};

// Attempt to recover if the user wanted to write "-"
if let Some(minus_pos) = first.text().find('-') {
Expand Down Expand Up @@ -532,19 +546,55 @@ impl Expression {
node: syntax_nodes::FunctionCallExpression,
ctx: &mut LookupCtx,
) -> Expression {
let mut sub_expr =
node.Expression().map(|n| (Self::from_expression_node(n.clone(), ctx), n.0.into()));
let mut sub_expr = node.Expression().map(|n| {
(Self::from_expression_node(n.clone(), ctx), NodeOrTokenWithSourceFile::from(n.0))
});

let mut arguments = Vec::new();

let function = sub_expr.next().map_or(Expression::Invalid, |e| e.0);
let function = if let Expression::MemberFunction { base, base_node, member } = function {
arguments.push((*base, base_node));
member
} else {
Box::new(function)
};
let (function, f_node) =
sub_expr.next().unwrap_or_else(|| (Expression::Invalid, node.0.clone().into()));

let function = match function {
Expression::BuiltinMacroReference(mac, _) => match mac {
BuiltinMacroFunction::Min => todo!("min/max not yet implemented"),
BuiltinMacroFunction::Max => todo!("min/max not yet implemented"),
BuiltinMacroFunction::CubicBezier => {
let mut has_error = None;
// FIXME: this is not pretty to be handling there.
// Maybe "cubic_bezier" should be a function that is lowered later
let mut a = || match sub_expr.next() {
None => {
has_error.get_or_insert((f_node.clone(), "Not enough arguments"));
0.
}
Some((Expression::NumberLiteral(val, Unit::None), _)) => val as f32,
Some((_, n)) => {
has_error.get_or_insert((
n,
"Arguments to cubic bezier curve must be number literal",
));
0.
}
};
let expr =
Expression::EasingCurve(EasingCurve::CubicBezier(a(), a(), a(), a()));
if let Some((_, n)) = sub_expr.next() {
has_error.get_or_insert((n, "Too many argument for bezier curve"));
}
if let Some((n, msg)) = has_error {
ctx.diag.push_error(msg.into(), &n);
}

return expr;
}
},
Expression::MemberFunction { base, base_node, member } => {
arguments.push((*base, base_node));
member
}
_ => Box::new(function),
};
arguments.extend(sub_expr);

let arguments = match function.ty() {
Expand Down
29 changes: 29 additions & 0 deletions sixtyfps_compiler/tests/syntax/basic/easing.60
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/* LICENSE BEGIN
This file is part of the SixtyFPS Project -- https://sixtyfps.io
Copyright (c) 2020 Olivier Goffart <[email protected]>
Copyright (c) 2020 Simon Hausmann <[email protected]>

SPDX-License-Identifier: GPL-3.0-only
This file is also available under commercial licensing terms.
Please contact [email protected] for more information.
LICENSE END */

X := Rectangle {
animate x { easing: ease-in; }
animate y { easing: foo; }
// ^error{Unknown unqualified identifier 'foo'}
animate color { easing: a; }
// ^error{Cannot convert int to easing}
property <int> a; animate a { easing: cubic-bezier(0.01,1.46,0.94,1.37); }
property <int> b; animate b { easing: cubic-bezier(0.01,1.46,0.94); }
// ^error{Not enough arguments}
property <int> c; animate c { easing: cubic-bezier(); }
// ^error{Not enough arguments}
property <int> d; animate d { easing: cubic-bezier(0,0,0,0,0,0); }
// ^error{Too many argument for bezier curve}
property <int> e; animate e { easing: cubic-bezier(0, a, b, c); }
// ^error{Arguments to cubic bezier curve must be number literal}
property <int> f; animate f { easing: cubic-bezier(0,0+0,0,0,0); }
// ^error{Arguments to cubic bezier curve must be number literal}
}

17 changes: 17 additions & 0 deletions sixtyfps_compiler/tests/syntax/basic/easing_not_called.60
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/* LICENSE BEGIN
This file is part of the SixtyFPS Project -- https://sixtyfps.io
Copyright (c) 2020 Olivier Goffart <[email protected]>
Copyright (c) 2020 Simon Hausmann <[email protected]>

SPDX-License-Identifier: GPL-3.0-only
This file is also available under commercial licensing terms.
Please contact [email protected] for more information.
LICENSE END */

// Cannot be put in the easing.60 test because it is in a different pass

X := Rectangle {
property <int> g; animate g { easing: cubic-bezier; }
// ^error{must be called}
}

24 changes: 24 additions & 0 deletions sixtyfps_compiler/tests/syntax/focus/focus_not_called.60
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/* LICENSE BEGIN
This file is part of the SixtyFPS Project -- https://sixtyfps.io
Copyright (c) 2020 Olivier Goffart <[email protected]>
Copyright (c) 2020 Simon Hausmann <[email protected]>

SPDX-License-Identifier: GPL-3.0-only
This file is also available under commercial licensing terms.
Please contact [email protected] for more information.
LICENSE END */


X := Rectangle {
edit := TextInput { }
TouchArea {
clicked => {
(edit.focus)();
edit.focus;
// ^error{Member function must be called}
}
}
x: edit.focus;
// ^error{Cannot convert function\(element ref\) -> void to length}
// ^^error{Member function must be called}
}
7 changes: 5 additions & 2 deletions sixtyfps_compiler/tests/syntax/lookup/signal_arg.60
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,13 @@ Xxx := Rectangle {
// ^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)
hello(45, fff);
// ^error{The expression is not a function}
// ^^error{Unknown unqualified identifier 'fff'}

(plop)("45", #fff, 42);
(root.plop)("45", #fff, 42);
(root.plop)("45", #fff, "45");
// ^error{Cannot convert string to int}
}

x: 12phx;
Expand Down
1 change: 1 addition & 0 deletions sixtyfps_runtime/interpreter/eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,7 @@ pub fn eval_expression(e: &Expression, local_context: &mut EvalLocalContext) ->
),
Expression::ElementReference(_) => todo!("Element references are only supported in the context of built-in function calls at the moment"),
Expression::MemberFunction { .. } => panic!("member function expressions must not appear in the code generator anymore"),
Expression::BuiltinMacroReference { .. } => panic!("macro expressions must not appear in the code generator anymore"),
Expression::PropertyReference(NamedReference { element, name }) => {
load_property_helper(local_context.component_instance, &element.upgrade().unwrap(), name.as_ref()).unwrap()
}
Expand Down
2 changes: 1 addition & 1 deletion tests/cases/focus/focus_change_through_signal.60
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ TestCase := Rectangle {
focus_input1 => { input1.focus(); }

signal focus_input2();
focus_input2 => { input2.focus(); }
focus_input2 => { (input2.focus)(); }

input1 := TextInput {
width: parent.width;
Expand Down

0 comments on commit d499e86

Please sign in to comment.