/* LICENSE BEGIN This file is part of the SixtyFPS Project -- https://sixtyfps.io Copyright (c) 2020 Olivier Goffart Copyright (c) 2020 Simon Hausmann 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 */ use super::document::parse_qualified_name; use super::prelude::*; #[cfg_attr(test, parser_test)] /// ```test,Expression /// something /// "something" /// 0.3 /// 42 /// 42px /// #aabbcc /// (something) /// img!"something" /// some_id.some_property /// function_call() /// function_call(hello, world) /// cond ? first : second /// call_cond() ? first : second /// (nested()) ? (ok) : (other.ko) /// 4 + 4 /// 4 + 8 * 7 / 5 + 3 - 7 - 7 * 8 /// -0.3px + 0.3px - 3.pt+3pt /// aa == cc && bb && (xxx || fff) && 3 + aaa == bbb /// [array] /// {object:42} /// ``` pub fn parse_expression(p: &mut impl Parser) { parse_expression_helper(p, OperatorPrecedence::Default) } #[derive(Eq, PartialEq, Ord, PartialOrd)] #[repr(u8)] enum OperatorPrecedence { /// ` ?: ` Default, /// `||`, `&&` Logical, /// `==` `!=` `>=` `<=` `<` `>` Equality, /// `+ -` Add, /// `* /` Mul, Unary, Bang, } fn parse_expression_helper(p: &mut impl Parser, precedence: OperatorPrecedence) { let mut p = p.start_node(SyntaxKind::Expression); let checkpoint = p.checkpoint(); match p.nth(0).kind() { SyntaxKind::Identifier => { if p.nth(1).kind() == SyntaxKind::Bang { parse_bang_expression(&mut *p) } else { parse_qualified_name(&mut *p); } } SyntaxKind::StringLiteral => p.consume(), SyntaxKind::NumberLiteral => p.consume(), SyntaxKind::ColorLiteral => p.consume(), SyntaxKind::LParent => { p.consume(); parse_expression(&mut *p); p.expect(SyntaxKind::RParent); } SyntaxKind::LBracket => parse_array(&mut *p), SyntaxKind::LBrace => parse_object_notation(&mut *p), SyntaxKind::Plus => { let mut p = p.start_node(SyntaxKind::UnaryOpExpression); p.consume(); parse_expression_helper(&mut *p, OperatorPrecedence::Unary); } SyntaxKind::Minus => { let mut p = p.start_node(SyntaxKind::UnaryOpExpression); p.consume(); parse_expression_helper(&mut *p, OperatorPrecedence::Unary); } SyntaxKind::Bang => { let mut p = p.start_node(SyntaxKind::UnaryOpExpression); p.consume(); parse_expression_helper(&mut *p, OperatorPrecedence::Unary); } _ => { p.error("invalid expression"); return; } } match p.nth(0).kind() { SyntaxKind::LParent => { { let _ = p.start_node_at(checkpoint.clone(), SyntaxKind::Expression); } let mut p = p.start_node_at(checkpoint.clone(), SyntaxKind::FunctionCallExpression); parse_function_arguments(&mut *p); } _ => {} } if precedence >= OperatorPrecedence::Mul { return; } while matches!(p.nth(0).kind(), SyntaxKind::Star | SyntaxKind::Div) { { let _ = p.start_node_at(checkpoint.clone(), SyntaxKind::Expression); } let mut p = p.start_node_at(checkpoint.clone(), SyntaxKind::BinaryExpression); p.consume(); parse_expression_helper(&mut *p, OperatorPrecedence::Mul); } if precedence >= OperatorPrecedence::Add { return; } while matches!(p.nth(0).kind(), SyntaxKind::Plus | SyntaxKind::Minus) { { let _ = p.start_node_at(checkpoint.clone(), SyntaxKind::Expression); } let mut p = p.start_node_at(checkpoint.clone(), SyntaxKind::BinaryExpression); p.consume(); parse_expression_helper(&mut *p, OperatorPrecedence::Add); } if precedence > OperatorPrecedence::Equality { return; } if matches!( p.nth(0).kind(), SyntaxKind::LessEqual | SyntaxKind::GreaterEqual | SyntaxKind::EqualEqual | SyntaxKind::NotEqual | SyntaxKind::LAngle | SyntaxKind::RAngle ) { if precedence == OperatorPrecedence::Equality { p.error("Use parentheses to disambiguate equality expression on the same level"); } { let _ = p.start_node_at(checkpoint.clone(), SyntaxKind::Expression); } let mut p = p.start_node_at(checkpoint.clone(), SyntaxKind::BinaryExpression); p.consume(); parse_expression_helper(&mut *p, OperatorPrecedence::Equality); } if precedence >= OperatorPrecedence::Logical { return; } let mut prev_logical_op = None; while matches!(p.nth(0).kind(), SyntaxKind::AndAnd | SyntaxKind::OrOr) { if let Some(prev) = prev_logical_op { if prev != p.nth(0).kind() { p.error("Use parentheses to disambiguate between && and ||"); prev_logical_op = None; } } else { prev_logical_op = Some(p.nth(0).kind()); } { let _ = p.start_node_at(checkpoint.clone(), SyntaxKind::Expression); } let mut p = p.start_node_at(checkpoint.clone(), SyntaxKind::BinaryExpression); p.consume(); parse_expression_helper(&mut *p, OperatorPrecedence::Logical); } match p.nth(0).kind() { SyntaxKind::Question => { { let _ = p.start_node_at(checkpoint.clone(), SyntaxKind::Expression); } let mut p = p.start_node_at(checkpoint.clone(), SyntaxKind::ConditionalExpression); p.consume(); parse_expression(&mut *p); p.expect(SyntaxKind::Colon); parse_expression(&mut *p); } _ => (), } } #[cfg_attr(test, parser_test)] /// ```test,BangExpression /// foo!bar /// foo!(bar) /// foo!("bar") /// foo ! "bar" /// foo ! plop ! bar /// foo ! (plop ! bar) /// ``` fn parse_bang_expression(p: &mut impl Parser) { let mut p = p.start_node(SyntaxKind::BangExpression); p.expect(SyntaxKind::Identifier); // Or assert? p.expect(SyntaxKind::Bang); // Or assert? parse_expression_helper(&mut *p, OperatorPrecedence::Bang); } #[cfg_attr(test, parser_test)] /// ```test,Array /// [ a, b, c , d] /// [] /// [a,] /// [ [], [] ] /// ``` fn parse_array(p: &mut impl Parser) { let mut p = p.start_node(SyntaxKind::Array); p.expect(SyntaxKind::LBracket); while p.nth(0).kind() != SyntaxKind::RBracket { parse_expression(&mut *p); if !p.test(SyntaxKind::Comma) { break; } } p.expect(SyntaxKind::RBracket); } #[cfg_attr(test, parser_test)] /// ```test,ObjectLiteral /// {} /// {a:b} /// { a: "foo" , } /// {a:b, c: 4 + 4, d: [a,] } /// ``` fn parse_object_notation(p: &mut impl Parser) { let mut p = p.start_node(SyntaxKind::ObjectLiteral); p.expect(SyntaxKind::LBrace); while p.nth(0).kind() != SyntaxKind::RBrace { let mut p = p.start_node(SyntaxKind::ObjectMember); p.expect(SyntaxKind::Identifier); p.expect(SyntaxKind::Colon); parse_expression(&mut *p); if !p.test(SyntaxKind::Comma) { break; } } p.expect(SyntaxKind::RBrace); } #[cfg_attr(test, parser_test)] /// ```test /// () /// (foo) /// (foo, bar, foo) /// (foo, bar(), xx+xx,) /// ``` fn parse_function_arguments(p: &mut impl Parser) { p.expect(SyntaxKind::LParent); while p.nth(0).kind() != SyntaxKind::RParent { parse_expression(&mut *p); if !p.test(SyntaxKind::Comma) { break; } } p.expect(SyntaxKind::RParent); }