Source code
Revision control
Copy as Markdown
Other Tools
use crate::ast::{Expression, OpCode};
use crate::lexer::Token;
grammar<'input>();
extern {
type Location = usize;
type Error = crate::lexer::LexError;
enum Token<'input> {
// Literals
Number => Token::Number(<f64>),
DoubleQuotedString => Token::DoubleQuotedString(<&'input str>),
SingleQuotedString => Token::SingleQuotedString(<&'input str>),
Boolean => Token::Boolean(<bool>),
Null => Token::Null,
Identifier => Token::Identifier(<&'input str>),
// Operators
"+" => Token::Plus,
"-" => Token::Minus,
"*" => Token::Multiply,
"/" => Token::Divide,
"//" => Token::FloorDivide,
"%" => Token::Modulus,
"^" => Token::Exponent,
// Comparison
"==" => Token::Equal,
"!=" => Token::NotEqual,
">" => Token::Greater,
">=" => Token::GreaterEqual,
"<" => Token::Less,
"<=" => Token::LessEqual,
"in" => Token::In,
// Logical
"&&" => Token::And,
"||" => Token::Or,
// Punctuation
"(" => Token::LeftParen,
")" => Token::RightParen,
"[" => Token::LeftBracket,
"]" => Token::RightBracket,
"{" => Token::LeftBrace,
"}" => Token::RightBrace,
"," => Token::Comma,
"." => Token::Dot,
":" => Token::Colon,
"?" => Token::Question,
"|" => Token::Pipe,
}
}
pub Expression: Box<Expression> = Expr00;
Expr00: Box<Expression> = {
<left: Expression> <operation: Op10> <right: Expr10> => Box::new(Expression::BinaryOperation { left, right, operation }),
Expr10,
};
Expr10: Box<Expression> = {
<left: Expr10> <operation: Op20> <right: Expr20> => Box::new(Expression::BinaryOperation { left, right, operation }),
Expr20,
};
Expr20: Box<Expression> = {
<left: Expr20> <operation: Op30> <right: Expr30> => Box::new(Expression::BinaryOperation { left, right, operation }),
Expr30,
};
Expr30: Box<Expression> = {
<left: Expr30> <operation: Op40> <right: Expr40> => Box::new(Expression::BinaryOperation { left, right, operation }),
Expr40,
};
Expr40: Box<Expression> = {
<left: Expr40> <operation: Op50> <right: Expr50> => Box::new(Expression::BinaryOperation { left, right, operation }),
Expr50,
};
Expr50: Box<Expression> = {
<left: Expr50> "?" <truthy: Expr60> ":" <falsy: Expr60> => Box::new(Expression::Conditional {left, truthy, falsy}),
Expr60,
}
Expr60: Box<Expression> = {
<subject: Expr60> "|" <name: Identifier> <args: Args?> => Box::new(Expression::Transform{name: name.to_string(), subject, args}),
Expr70
};
/// Expression for dereferencing.
/// Used for dereferencing object literals, array literals, and the context
/// There are two types of operations here:
/// - Either a `dot` operation, taking an expression on the left hand side, and an identifier on the right hand side (a string without the quotations)
/// - Or an `index` operation, taking an expression on the left hand side, and another expression inside square ("[]") brackets.
///
/// # Examples:
///
/// Assume our context is the following
/// ```
///{
/// "foo":
/// {
/// "bar": [{"baz": 1}, {"bobo": [13, 12]}]
// }
// }
/// ```
///
/// `foo.bar == [{"baz": 1}, {"bobo": [13, 12]]`
/// `foo.bar[0] == {"baz": 1}`
/// `foo.bar[1].bobo[0] == 13`
/// `[1, 2, 3][1] == 2`
Expr70: Box<Expression> = {
<subject: Expr70> <index: Index> => Box::new(Expression::IndexOperation{subject, index}),
<subject: Expr70> "." <ident: Identifier> => Box::new(Expression::DotOperation{subject, ident: ident.to_string()}),
Expr80
};
Expr80: Box<Expression> = {
Number => Box::new(Expression::Number(<>)),
Boolean => Box::new(Expression::Boolean(<>)),
String => Box::new(Expression::String(<>)),
Array => Box::new(Expression::Array(<>)),
Object => Box::new(Expression::Object(<>)),
Null => Box::new(Expression::Null),
Identifier => Box::new(Expression::Identifier(<>.to_string())),
"(" <Expression> ")",
};
Args: Vec<Box<Expression>> = {
"(" <Comma<Expression>> ")"
};
Op10: OpCode = {
"&&" => OpCode::And,
"||" => OpCode::Or,
};
Op20: OpCode = {
"==" => OpCode::Equal,
"!=" => OpCode::NotEqual,
">=" => OpCode::GreaterEqual,
"<=" => OpCode::LessEqual,
">" => OpCode::Greater,
"<" => OpCode::Less,
"in" => OpCode::In,
};
Op30: OpCode = {
"+" => OpCode::Add,
"-" => OpCode::Subtract,
};
Op40: OpCode = {
"*" => OpCode::Multiply,
"//" => OpCode::FloorDivide,
"/" => OpCode::Divide,
};
Op50: OpCode = {
"%" => OpCode::Modulus,
"^" => OpCode::Exponent,
};
// The weird string literal handling here to preserve the weird semantics of the previous lexer implementation:
// String: String = {
// <s: r#""([^"\\]*(\\")?)*""#> => s[1..s.len() - 1].to_string().replace("\\\"", "\""),
// <s: r#"'([^'\\]*(\\')?)*'"#> => s[1..s.len() - 1].to_string().replace("\\'", "'"),
// };
String: String = {
<s: DoubleQuotedString> => s.replace("\\\"", "\""),
<s: SingleQuotedString> => s.replace("\\'", "'"),
};
Index: Box<Expression> = {
"[" "." <ident: Identifier> <op: Op20> <right: Expr80> "]" => Box::new(Expression::Filter {ident: ident.to_string(), op, right}),
"[" <Expression> "]",
}
// Boolean tokens are now handled by the external lexer
Comma<T>: Vec<T> = {
<v: (<T> ",")*> <e:T?> => match e {
None => v,
Some(e) => {
let mut v = v;
v.push(e);
v
}
}
};
Array: Vec<Box<Expression>> = {
"[" <Comma<Expression>> "]"
}
Object: Vec<(String, Box<Expression>)> = {
"{" <Comma<(<ObjectIdentifier> ":" <Expression>)>> "}",
}
ObjectIdentifier: String = {
String,
Identifier => <>.to_string()
}