use super::document::parse_qualified_name;
use super::prelude::*;
#[cfg_attr(test, parser_test)]
pub fn parse_expression(p: &mut impl Parser) -> bool {
parse_expression_helper(p, OperatorPrecedence::Default)
}
#[derive(Eq, PartialEq, Ord, PartialOrd)]
#[repr(u8)]
enum OperatorPrecedence {
Default,
Logical,
Equality,
Add,
Mul,
Unary,
}
fn parse_expression_helper(p: &mut impl Parser, precedence: OperatorPrecedence) -> bool {
let mut p = p.start_node(SyntaxKind::Expression);
let checkpoint = p.checkpoint();
match p.nth(0).kind() {
SyntaxKind::Identifier => {
parse_qualified_name(&mut *p);
}
SyntaxKind::StringLiteral => {
if p.nth(0).as_str().ends_with('{') {
parse_template_string(&mut *p)
} else {
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 | SyntaxKind::Minus | SyntaxKind::Bang => {
let mut p = p.start_node(SyntaxKind::UnaryOpExpression);
p.consume();
parse_expression_helper(&mut *p, OperatorPrecedence::Unary);
}
SyntaxKind::At => {
parse_at_keyword(&mut *p);
}
_ => {
p.error("invalid expression");
return false;
}
}
loop {
match p.nth(0).kind() {
SyntaxKind::Dot => {
{
let _ = p.start_node_at(checkpoint.clone(), SyntaxKind::Expression);
}
let mut p = p.start_node_at(checkpoint.clone(), SyntaxKind::MemberAccess);
p.consume(); if !p.expect(SyntaxKind::Identifier) {
return false;
}
}
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);
}
SyntaxKind::LBracket => {
{
let _ = p.start_node_at(checkpoint.clone(), SyntaxKind::Expression);
}
let mut p = p.start_node_at(checkpoint.clone(), SyntaxKind::IndexExpression);
p.expect(SyntaxKind::LBracket);
parse_expression(&mut *p);
p.expect(SyntaxKind::RBracket);
}
_ => break,
}
}
if precedence >= OperatorPrecedence::Mul {
return true;
}
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 true;
}
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 true;
}
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 true;
}
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);
}
if p.nth(0).kind() == SyntaxKind::Question {
{
let _ = p.start_node_at(checkpoint.clone(), SyntaxKind::Expression);
}
let mut p = p.start_node_at(checkpoint, SyntaxKind::ConditionalExpression);
p.consume();
parse_expression(&mut *p);
p.expect(SyntaxKind::Colon);
parse_expression(&mut *p);
}
true
}
#[cfg_attr(test, parser_test)]
fn parse_at_keyword(p: &mut impl Parser) {
debug_assert_eq!(p.peek().kind(), SyntaxKind::At);
match p.nth(1).as_str() {
"image-url" | "image_url" => {
let mut p = p.start_node(SyntaxKind::AtImageUrl);
p.consume(); p.consume(); p.expect(SyntaxKind::LParent);
p.expect(SyntaxKind::StringLiteral);
p.expect(SyntaxKind::RParent);
}
"linear-gradient" | "linear_gradient" => {
parse_at_linear_gradient(p);
}
_ => {
p.consume();
p.error("Expected 'image-url' or 'linear-gradient' after '@'");
}
}
}
#[cfg_attr(test, parser_test)]
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)]
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)]
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);
}
#[cfg_attr(test, parser_test)]
fn parse_template_string(p: &mut impl Parser) {
let mut p = p.start_node(SyntaxKind::StringTemplate);
debug_assert!(p.nth(0).as_str().ends_with("\\{"));
{
let mut p = p.start_node(SyntaxKind::Expression);
p.consume();
}
loop {
parse_expression(&mut *p);
let peek = p.peek();
if peek.kind != SyntaxKind::StringLiteral || !peek.as_str().starts_with('}') {
p.error("Error while parsing string template")
}
let mut p = p.start_node(SyntaxKind::Expression);
let cont = peek.as_str().ends_with('{');
p.consume();
if !cont {
break;
}
}
}
#[cfg_attr(test, parser_test)]
fn parse_at_linear_gradient(p: &mut impl Parser) {
let mut p = p.start_node(SyntaxKind::AtLinearGradient);
p.expect(SyntaxKind::At);
debug_assert!(p.peek().as_str() == "linear-gradient" || p.peek().as_str() == "linear_gradient");
p.consume();
p.expect(SyntaxKind::LParent);
while !p.test(SyntaxKind::RParent) {
if !parse_expression(&mut *p) {
return;
}
p.test(SyntaxKind::Comma);
}
}