use serde::{Deserialize, Serialize}; use vegafusion_core::proto::gen::expression as proto_expression; /// ESTree-style AST Node for identifiers /// /// https://github.com/estree/estree/blob/0fa6c005fa452f1f970b3923d5faa38178906d08/es5.md#identifier #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Hash)] pub struct Identifier { pub name: String, } impl Identifier { pub fn to_proto(&self) -> proto_expression::expression::Expr { proto_expression::expression::Expr::Identifier(proto_expression::Identifier { name: self.name.clone(), }) } } /// AST Node for liter scalar values #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(untagged)] pub enum LiteralValue { String(String), Boolean(bool), Number(f64), Null, } impl LiteralValue { pub fn to_proto(&self) -> proto_expression::literal::Value { match self { LiteralValue::String(v) => proto_expression::literal::Value::String(v.clone()), LiteralValue::Boolean(v) => proto_expression::literal::Value::Boolean(*v), LiteralValue::Number(v) => proto_expression::literal::Value::Number(*v), LiteralValue::Null => proto_expression::literal::Value::Null(false), } } } /// ESTree-style AST Node for literal value /// /// https://github.com/estree/estree/blob/0fa6c005fa452f1f970b3923d5faa38178906d08/es5.md#literal #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Literal { pub value: LiteralValue, pub raw: String, } impl Literal { pub fn to_proto(&self) -> proto_expression::expression::Expr { proto_expression::expression::Expr::Literal(proto_expression::Literal { raw: self.raw.clone(), value: Some(self.value.to_proto()), }) } } /// ESTree-style AST Node for binary infix operators /// /// https://github.com/estree/estree/blob/0fa6c005fa452f1f970b3923d5faa38178906d08/es5.md#binaryoperator #[derive(Debug, Copy, Clone, Serialize, Deserialize)] pub enum BinaryOperator { #[serde(rename = "==")] Equals, #[serde(rename = "!=")] NotEquals, #[serde(rename = "===")] StrictEquals, #[serde(rename = "!==")] NotStrictEquals, #[serde(rename = "<")] LessThan, #[serde(rename = "<=")] LessThanEqual, #[serde(rename = ">")] GreaterThan, #[serde(rename = ">=")] GreaterThanEqual, #[serde(rename = "+")] Plus, #[serde(rename = "-")] Minus, #[serde(rename = "*")] Mult, #[serde(rename = "/")] Div, #[serde(rename = "%")] Mod, #[serde(rename = "&")] BitwiseAnd, #[serde(rename = "|")] BitwiseOr, #[serde(rename = "^")] BitwiseXor, #[serde(rename = "<<")] BitwiseShiftLeft, #[serde(rename = ">>")] BitwiseShiftRight, } impl BinaryOperator { pub fn to_proto(&self) -> proto_expression::BinaryOperator { match self { BinaryOperator::Equals => proto_expression::BinaryOperator::Equals, BinaryOperator::NotEquals => proto_expression::BinaryOperator::NotEquals, BinaryOperator::StrictEquals => proto_expression::BinaryOperator::StrictEquals, BinaryOperator::NotStrictEquals => proto_expression::BinaryOperator::NotStrictEquals, BinaryOperator::LessThan => proto_expression::BinaryOperator::LessThan, BinaryOperator::LessThanEqual => proto_expression::BinaryOperator::LessThanEqual, BinaryOperator::GreaterThan => proto_expression::BinaryOperator::GreaterThan, BinaryOperator::GreaterThanEqual => proto_expression::BinaryOperator::GreaterThanEqual, BinaryOperator::Plus => proto_expression::BinaryOperator::Plus, BinaryOperator::Minus => proto_expression::BinaryOperator::Minus, BinaryOperator::Mult => proto_expression::BinaryOperator::Mult, BinaryOperator::Div => proto_expression::BinaryOperator::Div, BinaryOperator::Mod => proto_expression::BinaryOperator::Mod, BinaryOperator::BitwiseAnd => proto_expression::BinaryOperator::BitwiseAnd, BinaryOperator::BitwiseOr => proto_expression::BinaryOperator::BitwiseOr, BinaryOperator::BitwiseXor => proto_expression::BinaryOperator::BitwiseXor, BinaryOperator::BitwiseShiftLeft => proto_expression::BinaryOperator::BitwiseShiftLeft, BinaryOperator::BitwiseShiftRight => { proto_expression::BinaryOperator::BitwiseShiftRight } } } } /// ESTree-style AST Node for binary infix expression /// /// https://github.com/estree/estree/blob/0fa6c005fa452f1f970b3923d5faa38178906d08/es5.md#binaryexpression #[derive(Debug, Clone, Serialize, Deserialize)] pub struct BinaryExpression { pub left: Box, pub operator: BinaryOperator, pub right: Box, } impl BinaryExpression { pub fn to_proto(&self) -> proto_expression::expression::Expr { proto_expression::expression::Expr::Binary(Box::new(proto_expression::BinaryExpression { left: Some(Box::new(self.left.to_proto())), operator: self.operator.to_proto() as i32, right: Some(Box::new(self.right.to_proto())), })) } } /// ESTree-style AST Node for logical infix operators /// /// https://github.com/estree/estree/blob/0fa6c005fa452f1f970b3923d5faa38178906d08/es5.md#logicaloperator #[derive(Debug, Copy, Clone, Serialize, Deserialize)] pub enum LogicalOperator { #[serde(rename = "||")] Or, #[serde(rename = "&&")] And, } impl LogicalOperator { pub fn to_proto(&self) -> proto_expression::LogicalOperator { match self { LogicalOperator::Or => proto_expression::LogicalOperator::Or, LogicalOperator::And => proto_expression::LogicalOperator::And, } } } /// ESTree-style AST Node for logical infix expression /// /// https://github.com/estree/estree/blob/0fa6c005fa452f1f970b3923d5faa38178906d08/es5.md#logicalexpression #[derive(Debug, Clone, Serialize, Deserialize)] pub struct LogicalExpression { pub left: Box, pub operator: LogicalOperator, pub right: Box, } impl LogicalExpression { pub fn to_proto(&self) -> proto_expression::expression::Expr { proto_expression::expression::Expr::Logical(Box::new(proto_expression::LogicalExpression { left: Some(Box::new(self.left.to_proto())), operator: self.operator.to_proto() as i32, right: Some(Box::new(self.right.to_proto())), })) } } /// ESTree-style AST Node for unary operators /// /// https://github.com/estree/estree/blob/0fa6c005fa452f1f970b3923d5faa38178906d08/es5.md#unaryoperator #[derive(Debug, Clone, Serialize, Deserialize)] pub enum UnaryOperator { #[serde(rename = "+")] Pos, #[serde(rename = "-")] Neg, #[serde(rename = "!")] Not, } impl UnaryOperator { pub fn to_proto(&self) -> proto_expression::UnaryOperator { match self { UnaryOperator::Pos => proto_expression::UnaryOperator::Pos, UnaryOperator::Neg => proto_expression::UnaryOperator::Neg, UnaryOperator::Not => proto_expression::UnaryOperator::Not, } } } /// ESTree-style AST Node for unary expressions /// /// https://github.com/estree/estree/blob/0fa6c005fa452f1f970b3923d5faa38178906d08/es5.md#updateexpression #[derive(Debug, Clone, Serialize, Deserialize)] pub struct UnaryExpression { pub operator: UnaryOperator, pub prefix: bool, pub argument: Box, } impl UnaryExpression { pub fn to_proto(&self) -> proto_expression::expression::Expr { proto_expression::expression::Expr::Unary(Box::new(proto_expression::UnaryExpression { argument: Some(Box::new(self.argument.to_proto())), operator: self.operator.to_proto() as i32, prefix: self.prefix, })) } } /// ESTree-style AST Node for conditional/ternary expression /// /// https://github.com/estree/estree/blob/0fa6c005fa452f1f970b3923d5faa38178906d08/es5.md#conditionalexpression #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ConditionalExpression { pub test: Box, pub consequent: Box, pub alternate: Box, } impl ConditionalExpression { pub fn to_proto(&self) -> proto_expression::expression::Expr { proto_expression::expression::Expr::Conditional(Box::new( proto_expression::ConditionalExpression { test: Some(Box::new(self.test.to_proto())), consequent: Some(Box::new(self.consequent.to_proto())), alternate: Some(Box::new(self.alternate.to_proto())), }, )) } } /// Enum that serializes like Expression, but only includes the Identifier variant #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(tag = "type")] pub enum Callee { Identifier(Identifier), } /// ESTree-style AST Node for call expression /// /// https://github.com/estree/estree/blob/0fa6c005fa452f1f970b3923d5faa38178906d08/es5.md#callexpression #[derive(Debug, Clone, Serialize, Deserialize)] pub struct CallExpression { pub callee: Callee, pub arguments: Vec, } impl CallExpression { pub fn to_proto(&self) -> proto_expression::expression::Expr { let callee_string = match &self.callee { Callee::Identifier(id) => id.name.clone(), }; proto_expression::expression::Expr::Call(proto_expression::CallExpression { callee: callee_string, arguments: self.arguments.iter().map(|arg| arg.to_proto()).collect(), }) } } /// ESTree-style AST Node for array literal (e.g. [23, "Hello"]) /// /// https://github.com/estree/estree/blob/0fa6c005fa452f1f970b3923d5faa38178906d08/es5.md#arrayexpression #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ArrayExpression { pub elements: Vec, } impl ArrayExpression { pub fn to_proto(&self) -> proto_expression::expression::Expr { proto_expression::expression::Expr::Array(proto_expression::ArrayExpression { elements: self.elements.iter().map(|e| e.to_proto()).collect(), }) } } #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(tag = "type")] pub enum PropertyKey { Literal(Literal), Identifier(Identifier), } impl PropertyKey { pub fn to_proto(&self) -> proto_expression::property::Key { match self { PropertyKey::Literal(lit) => { proto_expression::property::Key::Literal(proto_expression::Literal { raw: lit.raw.clone(), value: Some(lit.value.to_proto()), }) } PropertyKey::Identifier(ident) => { proto_expression::property::Key::Identifier(proto_expression::Identifier { name: ident.name.clone(), }) } } } } /// ESTree-style AST Node for object property (e.g. `a: 23`) /// /// https://github.com/estree/estree/blob/0fa6c005fa452f1f970b3923d5faa38178906d08/es5.md#property #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Property { pub key: PropertyKey, pub value: ESTreeExpression, pub kind: String, } impl Property { pub fn to_proto(&self) -> proto_expression::Property { proto_expression::Property { value: Some(self.value.to_proto()), kind: self.kind.clone(), key: Some(self.key.to_proto()), } } } /// ESTree-style AST Node for object literal (e.g. `{a: 23, "Hello": false}`) /// /// https://github.com/estree/estree/blob/0fa6c005fa452f1f970b3923d5faa38178906d08/es5.md#objectexpression #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ObjectExpression { pub properties: Vec, } impl ObjectExpression { pub fn to_proto(&self) -> proto_expression::expression::Expr { proto_expression::expression::Expr::Object(proto_expression::ObjectExpression { properties: self.properties.iter().map(|p| p.to_proto()).collect(), }) } } /// ESTree-style AST Node for member/element lookup in object or array (e.g. `foo[bar]`)) /// /// https://github.com/estree/estree/blob/0fa6c005fa452f1f970b3923d5faa38178906d08/es5.md#property #[derive(Debug, Clone, Serialize, Deserialize)] pub struct MemberExpression { pub object: Box, pub property: Box, pub computed: bool, } impl MemberExpression { pub fn to_proto(&self) -> proto_expression::expression::Expr { proto_expression::expression::Expr::Member(Box::new(proto_expression::MemberExpression { object: Some(Box::new(self.object.to_proto())), property: Some(Box::new(self.property.to_proto())), computed: self.computed, })) } } /// Expression enum that serializes to ESTree compatible JSON object #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(tag = "type")] pub enum ESTreeExpression { Identifier(Identifier), Literal(Literal), BinaryExpression(BinaryExpression), LogicalExpression(LogicalExpression), UnaryExpression(UnaryExpression), ConditionalExpression(ConditionalExpression), CallExpression(CallExpression), ArrayExpression(ArrayExpression), ObjectExpression(ObjectExpression), MemberExpression(MemberExpression), } impl ESTreeExpression { pub fn to_proto(&self) -> proto_expression::Expression { let expr = match self { ESTreeExpression::Identifier(expr) => expr.to_proto(), ESTreeExpression::Literal(expr) => expr.to_proto(), ESTreeExpression::BinaryExpression(expr) => expr.to_proto(), ESTreeExpression::LogicalExpression(expr) => expr.to_proto(), ESTreeExpression::UnaryExpression(expr) => expr.to_proto(), ESTreeExpression::ConditionalExpression(expr) => expr.to_proto(), ESTreeExpression::CallExpression(expr) => expr.to_proto(), ESTreeExpression::ArrayExpression(expr) => expr.to_proto(), ESTreeExpression::ObjectExpression(expr) => expr.to_proto(), ESTreeExpression::MemberExpression(expr) => expr.to_proto(), }; proto_expression::Expression { span: None, expr: Some(expr), } } }