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()
}