cala-cel-parser 0.15.2

A parser for the Common Expression Language (CEL)
Documentation
use crate::{LeftRightOp, LogicOp, RelationOp, ArithmeticOp, Expression, UnaryOp, Member, Literal};
use std::sync::Arc;

grammar;

match {
    // Skip whitespace and comments
   r"\s*" => { },
   r"//[^\n\r]*[\n\r]*" => { },
} else {
   _
}

pub Expression: Expression = {
    <condition:ConditionalOr> "?" <left:ConditionalOr> ":" <right:Expression> => Expression::Ternary(Box::new(condition), Box::new(left), Box::new(right)),
    ConditionalOr
};

Tier<Op, NextTier>: Expression = {
    <left:Tier<Op, NextTier>> <op:Op> <right:NextTier> => Expression::from_op(op, left.into(), right.into()),
    NextTier
};

ConditionalOr: Expression = Tier<LogicOr, ConditionalAnd>;
ConditionalAnd: Expression = Tier<LogicAnd, Relation>;
Relation: Expression = Tier<RelationOp, Addition>;
Addition: Expression = Tier<AdditionOp, Multiplication>;
Multiplication: Expression = Tier<MultiplicationOp, Unary>;

Unary: Expression = {
    <op:UnaryOp> <expr:Member> => Expression::Unary(op, expr.into()),
    Member
};

Member: Expression = {
    <left:Member> "." <identifier:Ident> => Expression::Member(left.into(), Box::new(Member::Attribute(identifier))),
    <left:Member> "." <identifier:Ident> "(" <arguments:CommaSeparated<Expression>> ")" => {
            let inner = Expression::Member(Box::new(left), Box::new(Member::Attribute(identifier)));
            Expression::Member(Box::new(inner), Member::FunctionCall(arguments).into())
    },
    <left:Member> "[" <expression:Expression> "]" => Expression::Member(Box::new(left), Box::new(Member::Index(expression.into()))),
    Primary,
}

Primary: Expression = {
    "."? <Ident> => Expression::Ident(<>.into()),
    "."? <identifier:Ident> "(" <arguments:CommaSeparated<Expression>> ")" => {
            let inner = Expression::Ident(identifier);
            Expression::Member(Box::new(inner), Box::new(Member::FunctionCall(arguments)))
    },
    "has" "(" <expr:Expression> ")" => Expression::Has(Box::new(expr)),
    "(" <Expression> ")",
    "[" <members:CommaSeparated<Expression>> "]" => Expression::List(<>),
    "{" <fields:CommaSeparated<MapInits>> "}" => Expression::Map(<>),
    "."? <ident:Ident+> "{" <fields:CommaSeparated<FieldInits>> "}" => Expression::Struct(ident,fields),
    Literal => Expression::Literal(<>)
}

FieldInits: (Arc<String>, Expression) = {
    <Ident> ":" <Expression>
}

MapInits: (Expression, Expression) = {
    <Expression> ":" <Expression>
}

CommaSeparated<T>: Vec<T> = {
    <v:(<T> ",")*> <e:T?> => match e {
        None => v,
        Some(e) => {
            let mut v = v;
            v.push(e);
            v
        }
    }
};

LogicOr: LeftRightOp = {
  "||" => LeftRightOp::Logic(LogicOp::Or)
};
LogicAnd: LeftRightOp = {
  "&&" => LeftRightOp::Logic(LogicOp::And)
};
RelationOp: LeftRightOp = {
    "<" => LeftRightOp::Relation(RelationOp::LessThan),
    "<=" => LeftRightOp::Relation(RelationOp::LessThanEq),
    ">" => LeftRightOp::Relation(RelationOp::GreaterThan),
    ">=" => LeftRightOp::Relation(RelationOp::GreaterThanEq),
    "==" => LeftRightOp::Relation(RelationOp::Equals),
    "!=" => LeftRightOp::Relation(RelationOp::NotEquals),
    "in" => LeftRightOp::Relation(RelationOp::In)
}
AdditionOp: LeftRightOp = {
    "+" => LeftRightOp::Arithmetic(ArithmeticOp::Add),
    "-" => LeftRightOp::Arithmetic(ArithmeticOp::Subtract),
};
MultiplicationOp: LeftRightOp = {
    "*" => LeftRightOp::Arithmetic(ArithmeticOp::Multiply),
    "/" => LeftRightOp::Arithmetic(ArithmeticOp::Divide),
    "%" => LeftRightOp::Arithmetic(ArithmeticOp::Modulus),
};
UnaryOp: UnaryOp = {
    <v:"!"+> => if v.len() % 2 == 0 { UnaryOp::DoubleNot } else { UnaryOp::Not },
    <v:"-"+> => if v.len() % 2 == 0 { UnaryOp::DoubleMinus } else { UnaryOp::Minus },
};

Literal: Literal = {
    // Integer literals. Annoying to parse :/
    r"-?[0-9]+" => Literal::Int(<>.parse().unwrap()),
    r"-?0[xX]([0-9a-fA-F]+)" => Literal::Int(i64::from_str_radix(<>, 16).unwrap()),
    r"-?[0-9]+ [uU]" => Literal::UInt(<>.parse().unwrap()),
    r"-?0[xX]([0-9a-fA-F]+) [uU]" => Literal::UInt(u64::from_str_radix(<>, 16).unwrap()),

    // Float with decimals and optional exponent
    r"([-+]?[0-9]*\.[0-9]+([eE][-+]?[0-9]+)?)" => Literal::Double(<>.to_string().into()),
    // Float with no decimals and required exponent
    r"[-+]?[0-9]+[eE][-+]?[0-9]+" => Literal::Double(<>.to_string().into()),

    // Double quoted string
    <s:r#""(\\.|[^"\n])*""#> => Literal::String(s[1..(s.len()-1)].to_string().into()),
    <s:r#""""(\\.|[^"{3}])*""""#> => Literal::String(s[3..(s.len()-3)].to_string().into()),

    // Single quoted string
    <s:r#"'(\\.|[^'\n])*'"#> => Literal::String(s[1..(s.len()-1)].to_string().into()),
    <s:r#"'''(\\.|[^'{3}])*'''"#> => Literal::String(s[3..(s.len()-3)].to_string().into()),

    // Double quoted bytes
    r#"[bB]"(\\.|[^"\n])*""# => Literal::Bytes(Vec::from(<>.as_bytes()).into()),
    r#"[bB]"""(\\.|[^"{3}])*""""# => Literal::Bytes(Vec::from(<>.as_bytes()).into()),

    // Single quoted bytes
    r#"[bB]'(\\.|[^'\n])*'"# => Literal::Bytes(Vec::from(<>.as_bytes()).into()),
    r#"[bB]'''(\\.|[^'{3}])*'''"# => Literal::Bytes(Vec::from(<>.as_bytes()).into()),

    "true" => Literal::Bool(true),
    "false" => Literal::Bool(false),
    "null" => Literal::Null,
};

Ident: Arc<String> = {
    r"[_a-zA-Z][_a-zA-Z0-9]*" => <>.to_string().into()
}