WIP: Implement the return statement

This commit is contained in:
Simon Hausmann 2020-12-04 13:46:35 +01:00 committed by Olivier Goffart
parent 16a75a2ff8
commit b22bbb1c0f
6 changed files with 128 additions and 6 deletions

View file

@ -363,6 +363,8 @@ pub enum Expression {
EasingCurve(EasingCurve), EasingCurve(EasingCurve),
EnumerationValue(EnumerationValue), EnumerationValue(EnumerationValue),
ReturnStatement(Option<Box<Expression>>),
} }
impl Default for Expression { impl Default for Expression {
@ -469,6 +471,7 @@ impl Expression {
Expression::ReadLocalVariable { ty, .. } => ty.clone(), Expression::ReadLocalVariable { ty, .. } => ty.clone(),
Expression::EasingCurve(_) => Type::Easing, Expression::EasingCurve(_) => Type::Easing,
Expression::EnumerationValue(value) => Type::Enumeration(value.enumeration.clone()), Expression::EnumerationValue(value) => Type::Enumeration(value.enumeration.clone()),
Expression::ReturnStatement(expr) => expr.as_ref().map_or(Type::Void, |expr| expr.ty()),
} }
} }
@ -542,6 +545,9 @@ impl Expression {
Expression::ReadLocalVariable { .. } => {} Expression::ReadLocalVariable { .. } => {}
Expression::EasingCurve(_) => {} Expression::EasingCurve(_) => {}
Expression::EnumerationValue(_) => {} Expression::EnumerationValue(_) => {}
Expression::ReturnStatement(expr) => {
expr.as_deref().map(|expr| visitor(expr));
}
} }
} }
@ -614,6 +620,9 @@ impl Expression {
Expression::ReadLocalVariable { .. } => {} Expression::ReadLocalVariable { .. } => {}
Expression::EasingCurve(_) => {} Expression::EasingCurve(_) => {}
Expression::EnumerationValue(_) => {} Expression::EnumerationValue(_) => {}
Expression::ReturnStatement(expr) => {
expr.as_deref_mut().map(|expr| visitor(expr));
}
} }
} }
@ -666,6 +675,9 @@ impl Expression {
Expression::ReadLocalVariable { .. } => false, Expression::ReadLocalVariable { .. } => false,
Expression::EasingCurve(_) => true, Expression::EasingCurve(_) => true,
Expression::EnumerationValue(_) => true, Expression::EnumerationValue(_) => true,
Expression::ReturnStatement(expr) => {
expr.as_ref().map_or(true, |expr| expr.is_constant())
}
} }
} }
@ -676,6 +688,13 @@ impl Expression {
node: &impl SpannedWithSourceFile, node: &impl SpannedWithSourceFile,
diag: &mut BuildDiagnostics, diag: &mut BuildDiagnostics,
) -> Expression { ) -> Expression {
if let Expression::ReturnStatement(expr) = self {
return Expression::ReturnStatement(expr.map(|mut expr| {
let expr = std::mem::replace(&mut *expr, Expression::Invalid);
Box::new(expr.maybe_convert_to(target_type, node, diag))
}));
}
let ty = self.ty(); let ty = self.ty();
if ty == target_type || target_type == Type::Void || target_type == Type::Invalid { if ty == target_type || target_type == Type::Void || target_type == Type::Invalid {
self self

View file

@ -1452,8 +1452,18 @@ fn compile_expression(
} }
} }
Expression::CodeBlock(sub) => { Expression::CodeBlock(sub) => {
let mut x = sub.iter().map(|e| compile_expression(e, component)).collect::<Vec<_>>(); let len = sub.len();
let mut x = sub.iter().enumerate().map(|(i, e)| {
match e {
Expression::ReturnStatement(return_expr) if i == len - 1 => {
return_expr.as_ref().map_or_else(String::new, |return_expr| compile_expression(return_expr, component))
},
e => compile_expression(e, component)
}
}).collect::<Vec<_>>();
if let Some(s) = x.last_mut() { *s = format!("return {};", s) }; if let Some(s) = x.last_mut() { *s = format!("return {};", s) };
format!("[&]{{ {} }}()", x.join(";")) format!("[&]{{ {} }}()", x.join(";"))
} }
Expression::FunctionCall { function, arguments, source_location: _ } => match &**function { Expression::FunctionCall { function, arguments, source_location: _ } => match &**function {
@ -1608,6 +1618,7 @@ fn compile_expression(
} }
Expression::Uncompiled(_) | Expression::TwoWayBinding(..) => panic!(), Expression::Uncompiled(_) | Expression::TwoWayBinding(..) => panic!(),
Expression::Invalid => "\n#error invalid expression\n".to_string(), Expression::Invalid => "\n#error invalid expression\n".to_string(),
Expression::ReturnStatement(expr) => format!("return {};", expr.as_ref().map_or_else(String::new, |expr| compile_expression(expr, component))),
} }
} }

View file

@ -1296,6 +1296,10 @@ fn compile_expression(expr: &Expression, component: &Rc<Component>) -> TokenStre
let value_ident = format_ident!("{}", value.to_string()); let value_ident = format_ident!("{}", value.to_string());
quote!(sixtyfps::re_exports::#base_ident::#value_ident) quote!(sixtyfps::re_exports::#base_ident::#value_ident)
} }
Expression::ReturnStatement(expr) => {
let return_expr = expr.as_ref().map(|expr| compile_expression(expr, component));
quote!(return (#return_expr) as _;)
},
} }
} }

View file

@ -269,11 +269,49 @@ impl Expression {
fn from_codeblock_node(node: syntax_nodes::CodeBlock, ctx: &mut LookupCtx) -> Expression { fn from_codeblock_node(node: syntax_nodes::CodeBlock, ctx: &mut LookupCtx) -> Expression {
debug_assert_eq!(node.kind(), SyntaxKind::CodeBlock); debug_assert_eq!(node.kind(), SyntaxKind::CodeBlock);
Expression::CodeBlock(
node.children() let mut statements_or_exprs = node
.filter(|n| n.kind() == SyntaxKind::Expression) .children()
.map(|n| Self::from_expression_node(n.into(), ctx)) .filter_map(|n| match n.kind() {
.collect(), SyntaxKind::Expression => Some(Self::from_expression_node(n.into(), ctx)),
SyntaxKind::ReturnStatement => Some(Self::from_return_statement(n.into(), ctx)),
_ => None,
})
.collect::<Vec<_>>();
let exit_points_and_return_types = statements_or_exprs
.iter()
.enumerate()
.filter_map(|(index, statement_or_expr)| {
if index == statements_or_exprs.len()
|| matches!(statement_or_expr, Expression::ReturnStatement(..))
{
Some((index, statement_or_expr.ty()))
} else {
None
}
})
.collect::<Vec<_>>();
let common_return_type = Self::common_target_type_for_type_list(
exit_points_and_return_types.iter().map(|(_, ty)| ty.clone()),
);
exit_points_and_return_types.into_iter().for_each(|(index, _)| {
let mut expr = std::mem::replace(&mut statements_or_exprs[index], Expression::Invalid);
expr = expr.maybe_convert_to(common_return_type.clone(), &node, &mut ctx.diag);
statements_or_exprs[index] = expr;
});
Expression::CodeBlock(statements_or_exprs)
}
fn from_return_statement(
node: syntax_nodes::ReturnStatement,
ctx: &mut LookupCtx,
) -> Expression {
Expression::ReturnStatement(
node.Expression().map(|n| Box::new(Self::from_expression_node(n.into(), ctx))),
) )
} }

View file

@ -360,6 +360,9 @@ pub fn eval_expression(e: &Expression, local_context: &mut EvalLocalContext) ->
Expression::CodeBlock(sub) => { Expression::CodeBlock(sub) => {
let mut v = Value::Void; let mut v = Value::Void;
for e in sub { for e in sub {
if let Expression::ReturnStatement(expr) = e {
return expr.as_ref().map_or(Value::Void, |expr| eval_expression(expr, local_context))
}
v = eval_expression(e, local_context); v = eval_expression(e, local_context);
} }
v v
@ -606,6 +609,7 @@ pub fn eval_expression(e: &Expression, local_context: &mut EvalLocalContext) ->
Expression::EnumerationValue(value) => { Expression::EnumerationValue(value) => {
Value::EnumerationValue(value.enumeration.name.clone(), value.to_string()) Value::EnumerationValue(value.enumeration.name.clone(), value.to_string())
} }
Expression::ReturnStatement(_) => panic!("internal error: return statement must only appear inside code block and handled there")
} }
} }

View file

@ -0,0 +1,46 @@
/* LICENSE BEGIN
This file is part of the SixtyFPS Project -- https://sixtyfps.io
Copyright (c) 2020 Olivier Goffart <olivier.goffart@sixtyfps.io>
Copyright (c) 2020 Simon Hausmann <simon.hausmann@sixtyfps.io>
SPDX-License-Identifier: GPL-3.0-only
This file is also available under commercial licensing terms.
Please contact info@sixtyfps.io for more information.
LICENSE END */
TestCase := Rectangle {
property <bool> toggle;
property <int> value: {
if (toggle) {
return 42;
}
return 100;
}
signal test_signal;
property <bool> block_signal;
property <bool> signal_handled;
test_signal => {
if (block_signal) {
return;
}
signal_handled = true;
}
}
/*
```cpp
auto handle = TestCase::create();
const TestCase &instance = *handle;
assert_eq(instance.get_value(), 100);
instance.set_toggle(true);
assert_eq(instance.get_value(), 42);
instance.emit_test_signal();
assert(instance.get_signal_handled());
instance.set_signal_handled(false);
instance.set_block_signal(true);
instance.emit_test_signal();
assert(!instance.get_signal_handled());
```
*/