use crate::object_tree::*; use crate::parser::{Spanned, SyntaxNode}; use crate::{diagnostics::Diagnostics, typeregister::Type}; use core::cell::RefCell; use std::{collections::HashMap, rc::Weak}; /// Reference to a property or signal of a given name within an element. #[derive(Debug, Clone)] pub struct NamedReference { pub element: Weak>, pub name: String, } /// The Expression is hold by properties, so it should not hold any strong references to node from the object_tree #[derive(Debug, Clone)] pub enum Expression { /// Something went wrong (and an error will be reported) Invalid, /// We haven't done the lookup yet Uncompiled(SyntaxNode), /// A string literal. The .0 is the content of the string, without the quotes StringLiteral(String), /// Number NumberLiteral(f64), /// Reference to the signal in the /// /// Note: if we are to separate expression and statement, we probably do not need to have signal reference within expressions SignalReference(NamedReference), /// Reference to the signal in the PropertyReference(NamedReference), /// Reference to the index variable of a repeater /// /// Example: `idx` in `for xxx[idx] in ...`. The element is the reference to the /// element that is repeated RepeaterIndexReference { element: Weak>, }, /// Reference to the model variable of a repeater /// /// Example: `xxx` in `for xxx[idx] in ...`. The element is the reference to the /// element that is repeated RepeaterModelReference { element: Weak>, }, /// Access to a field of the given name within a object. ObjectAccess { /// This expression should have Type::Object type base: Box, name: String, }, /// Cast an expression to the given type Cast { from: Box, to: Type, }, /// a code block with different expression CodeBlock(Vec), /// A function call FunctionCall { function: Box, }, SelfAssignment { lhs: Box, rhs: Box, /// '+', '-', '/', or '*' op: char, }, BinaryExpression { lhs: Box, rhs: Box, /// '+', '-', '/', or '*' op: char, }, ResourceReference { absolute_source_path: String, }, Condition { condition: Box, true_expr: Box, false_expr: Box, }, Array { element_ty: Type, values: Vec, }, Object { ty: Type, values: HashMap, }, } impl Expression { /// Return the type of this property pub fn ty(&self) -> Type { match self { Expression::Invalid => Type::Invalid, Expression::Uncompiled(_) => Type::Invalid, Expression::StringLiteral(_) => Type::String, Expression::NumberLiteral(_) => Type::Float32, Expression::SignalReference { .. } => Type::Signal, Expression::PropertyReference(NamedReference { element, name }) => { element.upgrade().unwrap().borrow().lookup_property(name) } Expression::RepeaterIndexReference { .. } => Type::Int32, Expression::RepeaterModelReference { element } => { if let Expression::Cast { from, .. } = element .upgrade() .unwrap() .borrow() .repeated .as_ref() .map_or(&Expression::Invalid, |e| &e.model) { match from.ty() { Type::Float32 | Type::Int32 => Type::Int32, Type::Array(elem) => *elem, _ => Type::Invalid, } } else { Type::Invalid } } Expression::ObjectAccess { base, name } => { if let Type::Object(o) = base.ty() { o.get(name.as_str()).unwrap_or(&Type::Invalid).clone() } else { Type::Invalid } } Expression::Cast { to, .. } => to.clone(), Expression::CodeBlock(sub) => sub.last().map_or(Type::Invalid, |e| e.ty()), Expression::FunctionCall { function } => function.ty(), Expression::SelfAssignment { .. } => Type::Invalid, Expression::ResourceReference { .. } => Type::Resource, Expression::Condition { condition: _, true_expr, false_expr } => { let true_type = true_expr.ty(); let false_type = false_expr.ty(); if true_type == false_type { true_type } else { Type::Invalid } } Expression::BinaryExpression { lhs, .. } => lhs.ty(), Expression::Array { element_ty, .. } => Type::Array(Box::new(element_ty.clone())), Expression::Object { ty, .. } => ty.clone(), } } /// Call the visitor for each sub-expression. (note: this function does not recurse) pub fn visit(&self, mut visitor: impl FnMut(&Self)) { match self { Expression::Invalid => {} Expression::Uncompiled(_) => {} Expression::StringLiteral(_) => {} Expression::NumberLiteral(_) => {} Expression::SignalReference { .. } => {} Expression::PropertyReference { .. } => {} Expression::ObjectAccess { base, .. } => visitor(&**base), Expression::RepeaterIndexReference { .. } => {} Expression::RepeaterModelReference { .. } => {} Expression::Cast { from, .. } => visitor(&**from), Expression::CodeBlock(sub) => { for e in sub { visitor(e) } } Expression::FunctionCall { function } => visitor(&**function), Expression::SelfAssignment { lhs, rhs, .. } => { visitor(&**lhs); visitor(&**rhs); } Expression::ResourceReference { .. } => {} Expression::Condition { condition, true_expr, false_expr } => { visitor(&**condition); visitor(&**true_expr); visitor(&**false_expr); } Expression::BinaryExpression { lhs, rhs, .. } => { visitor(&**lhs); visitor(&**rhs); } Expression::Array { values, .. } => { for x in values { visitor(x); } } Expression::Object { values, .. } => { for (_, x) in values { visitor(x); } } } } pub fn visit_mut(&mut self, mut visitor: impl FnMut(&mut Self)) { match self { Expression::Invalid => {} Expression::Uncompiled(_) => {} Expression::StringLiteral(_) => {} Expression::NumberLiteral(_) => {} Expression::SignalReference { .. } => {} Expression::PropertyReference { .. } => {} Expression::ObjectAccess { base, .. } => visitor(&mut **base), Expression::RepeaterIndexReference { .. } => {} Expression::RepeaterModelReference { .. } => {} Expression::Cast { from, .. } => visitor(&mut **from), Expression::CodeBlock(sub) => { for e in sub { visitor(e) } } Expression::FunctionCall { function } => visitor(&mut **function), Expression::SelfAssignment { lhs, rhs, .. } => { visitor(&mut **lhs); visitor(&mut **rhs); } Expression::ResourceReference { .. } => {} Expression::Condition { condition, true_expr, false_expr } => { visitor(&mut **condition); visitor(&mut **true_expr); visitor(&mut **false_expr); } Expression::BinaryExpression { lhs, rhs, .. } => { visitor(&mut **lhs); visitor(&mut **rhs); } Expression::Array { values, .. } => { for x in values { visitor(x); } } Expression::Object { values, .. } => { for (_, x) in values { visitor(x); } } } } pub fn is_constant(&self) -> bool { match self { Expression::Invalid => true, Expression::Uncompiled(_) => false, Expression::StringLiteral(_) => true, Expression::NumberLiteral(_) => true, Expression::SignalReference { .. } => false, Expression::PropertyReference { .. } => false, Expression::RepeaterIndexReference { .. } => false, Expression::RepeaterModelReference { .. } => false, Expression::ObjectAccess { base, .. } => base.is_constant(), Expression::Cast { from, .. } => from.is_constant(), Expression::CodeBlock(sub) => sub.len() == 1 && sub.first().unwrap().is_constant(), Expression::FunctionCall { .. } => false, Expression::SelfAssignment { .. } => false, Expression::ResourceReference { .. } => true, Expression::Condition { .. } => false, Expression::BinaryExpression { lhs, rhs, .. } => lhs.is_constant() && rhs.is_constant(), Expression::Array { values, .. } => values.iter().all(Expression::is_constant), Expression::Object { values, .. } => values.iter().all(|(_, v)| v.is_constant()), } } /// Create a conversion node if needed, or throw an error if the type is not matching pub fn maybe_convert_to( self, target_type: Type, node: &SyntaxNode, diag: &mut Diagnostics, ) -> Expression { let ty = self.ty(); if ty == target_type { self } else if ty.can_convert(&target_type) { Expression::Cast { from: Box::new(self), to: target_type } } else if ty == Type::Invalid { self } else { diag.push_error(format!("Cannot convert {} to {}", ty, target_type), node.span()); self } } }