mirror of
https://github.com/elkowar/eww.git
synced 2024-09-19 23:57:57 +03:00
Finish basic parser
This commit is contained in:
parent
923d478b33
commit
c5643424ca
@ -19,6 +19,8 @@ logos = "0.12"
|
||||
serde = {version = "1.0", features = ["derive"]}
|
||||
serde_json = "1.0"
|
||||
|
||||
strum = { version = "0.21", features = ["derive"] }
|
||||
|
||||
|
||||
|
||||
[build-dependencies]
|
||||
|
2
build.rs
2
build.rs
@ -1,4 +1,4 @@
|
||||
extern crate lalrpop;
|
||||
fn main() {
|
||||
lalrpop::process_root().unwrap();
|
||||
lalrpop::Configuration::new().log_verbose().process_current_dir().unwrap();
|
||||
}
|
||||
|
19
examples/errors.rs
Normal file
19
examples/errors.rs
Normal file
@ -0,0 +1,19 @@
|
||||
fn main() {
|
||||
let mut files = codespan_reporting::files::SimpleFiles::new();
|
||||
|
||||
let input = "12 + \"hi\" * foo ) ? bar == baz : false";
|
||||
|
||||
let _ = files.add("foo.eww", input);
|
||||
let ast = simplexpr::parse_string(input);
|
||||
match ast {
|
||||
Ok(ast) => {
|
||||
println!("{:?}", ast);
|
||||
}
|
||||
Err(err) => {
|
||||
let diag = err.pretty_diagnostic();
|
||||
use codespan_reporting::term;
|
||||
let mut writer = term::termcolor::StandardStream::stderr(term::termcolor::ColorChoice::Always);
|
||||
term::emit(&mut writer, &term::Config::default(), &files, &diag).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
100
src/ast.rs
100
src/ast.rs
@ -1,8 +1,8 @@
|
||||
use itertools::Itertools;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Eq, PartialEq, Clone, Copy)]
|
||||
pub struct Span(pub usize, pub usize, pub usize);
|
||||
#[derive(Eq, PartialEq, Clone, Copy, Serialize, Deserialize)]
|
||||
pub struct Span(pub usize, pub usize);
|
||||
|
||||
impl std::fmt::Display for Span {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
@ -16,79 +16,59 @@ impl std::fmt::Debug for Span {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Serialize, Deserialize, Debug)]
|
||||
#[rustfmt::skip]
|
||||
#[derive(Clone, PartialEq, Serialize, Deserialize, Debug, strum::EnumString, strum::Display)]
|
||||
pub enum BinOp {
|
||||
Plus,
|
||||
Minus,
|
||||
Times,
|
||||
Div,
|
||||
Mod,
|
||||
Equals,
|
||||
NotEquals,
|
||||
And,
|
||||
Or,
|
||||
GT,
|
||||
LT,
|
||||
Elvis,
|
||||
RegexMatch,
|
||||
#[strum(serialize = "+") ] Plus,
|
||||
#[strum(serialize = "-") ] Minus,
|
||||
#[strum(serialize = "*") ] Times,
|
||||
#[strum(serialize = "/") ] Div,
|
||||
#[strum(serialize = "%") ] Mod,
|
||||
#[strum(serialize = "==")] Equals,
|
||||
#[strum(serialize = "!=")] NotEquals,
|
||||
#[strum(serialize = "&&")] And,
|
||||
#[strum(serialize = "||")] Or,
|
||||
#[strum(serialize = ">") ] GT,
|
||||
#[strum(serialize = "<") ] LT,
|
||||
#[strum(serialize = "?:")] Elvis,
|
||||
#[strum(serialize = "=~")] RegexMatch,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for BinOp {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
BinOp::Plus => write!(f, "+"),
|
||||
BinOp::Minus => write!(f, "-"),
|
||||
BinOp::Times => write!(f, "*"),
|
||||
BinOp::Div => write!(f, "/"),
|
||||
BinOp::Mod => write!(f, "%"),
|
||||
BinOp::Equals => write!(f, "=="),
|
||||
BinOp::NotEquals => write!(f, "!="),
|
||||
BinOp::And => write!(f, "&&"),
|
||||
BinOp::Or => write!(f, "||"),
|
||||
BinOp::GT => write!(f, ">"),
|
||||
BinOp::LT => write!(f, "<"),
|
||||
BinOp::Elvis => write!(f, "?:"),
|
||||
BinOp::RegexMatch => write!(f, "=~"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Serialize, Deserialize, Debug)]
|
||||
#[derive(Clone, PartialEq, Serialize, Deserialize, Debug, strum::EnumString, strum::Display)]
|
||||
pub enum UnaryOp {
|
||||
#[strum(serialize = "!")]
|
||||
Not,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for UnaryOp {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
UnaryOp::Not => write!(f, "!"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Serialize, Deserialize, Debug)]
|
||||
#[derive(Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub enum SimplExpr {
|
||||
Literal(String),
|
||||
VarRef(String),
|
||||
BinOp(Box<SimplExpr>, BinOp, Box<SimplExpr>),
|
||||
UnaryOp(UnaryOp, Box<SimplExpr>),
|
||||
IfElse(Box<SimplExpr>, Box<SimplExpr>, Box<SimplExpr>),
|
||||
JsonAccess(Box<SimplExpr>, Box<SimplExpr>),
|
||||
FunctionCall(String, Vec<SimplExpr>),
|
||||
Literal(Span, String),
|
||||
VarRef(Span, String),
|
||||
BinOp(Span, Box<SimplExpr>, BinOp, Box<SimplExpr>),
|
||||
UnaryOp(Span, UnaryOp, Box<SimplExpr>),
|
||||
IfElse(Span, Box<SimplExpr>, Box<SimplExpr>, Box<SimplExpr>),
|
||||
JsonAccess(Span, Box<SimplExpr>, Box<SimplExpr>),
|
||||
FunctionCall(Span, String, Vec<SimplExpr>),
|
||||
}
|
||||
|
||||
impl std::fmt::Display for SimplExpr {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
SimplExpr::VarRef(x) => write!(f, "{}", x),
|
||||
SimplExpr::Literal(x) => write!(f, "\"{}\"", x),
|
||||
SimplExpr::BinOp(l, op, r) => write!(f, "({} {} {})", l, op, r),
|
||||
SimplExpr::UnaryOp(op, x) => write!(f, "{}{}", op, x),
|
||||
SimplExpr::IfElse(a, b, c) => write!(f, "(if {} then {} else {})", a, b, c),
|
||||
SimplExpr::JsonAccess(value, index) => write!(f, "{}[{}]", value, index),
|
||||
SimplExpr::FunctionCall(function_name, args) => {
|
||||
SimplExpr::VarRef(_, x) => write!(f, "{}", x),
|
||||
SimplExpr::Literal(_, x) => write!(f, "\"{}\"", x),
|
||||
SimplExpr::BinOp(_, l, op, r) => write!(f, "({} {} {})", l, op, r),
|
||||
SimplExpr::UnaryOp(_, op, x) => write!(f, "{}{}", op, x),
|
||||
SimplExpr::IfElse(_, a, b, c) => write!(f, "(if {} then {} else {})", a, b, c),
|
||||
SimplExpr::JsonAccess(_, value, index) => write!(f, "{}[{}]", value, index),
|
||||
SimplExpr::FunctionCall(_, function_name, args) => {
|
||||
write!(f, "{}({})", function_name, args.iter().join(", "))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for SimplExpr {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self)
|
||||
}
|
||||
}
|
||||
|
46
src/error.rs
Normal file
46
src/error.rs
Normal file
@ -0,0 +1,46 @@
|
||||
use crate::{ast::Span, lexer};
|
||||
use codespan_reporting::diagnostic;
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
pub enum Error {
|
||||
ParseError { source: lalrpop_util::ParseError<usize, lexer::Token, lexer::LexicalError> },
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::ParseError { source } => write!(f, "Parse error: {}", source),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Error {
|
||||
pub fn from_parse_error(err: lalrpop_util::ParseError<usize, lexer::Token, lexer::LexicalError>) -> Self {
|
||||
Error::ParseError { source: err }
|
||||
}
|
||||
|
||||
pub fn get_span(&self) -> Option<Span> {
|
||||
match self {
|
||||
Self::ParseError { source } => get_parse_error_span(source),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pretty_diagnostic(&self) -> diagnostic::Diagnostic<usize> {
|
||||
let diag = diagnostic::Diagnostic::error().with_message(format!("{}", self));
|
||||
if let Some(span) = self.get_span() {
|
||||
diag.with_labels(vec![diagnostic::Label::primary(0, span.0..span.1)])
|
||||
} else {
|
||||
diag
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_parse_error_span(err: &lalrpop_util::ParseError<usize, lexer::Token, lexer::LexicalError>) -> Option<Span> {
|
||||
match err {
|
||||
lalrpop_util::ParseError::InvalidToken { location } => Some(Span(*location, *location)),
|
||||
lalrpop_util::ParseError::UnrecognizedEOF { location, expected: _ } => Some(Span(*location, *location)),
|
||||
lalrpop_util::ParseError::UnrecognizedToken { token, expected: _ } => Some(Span(token.0, token.2)),
|
||||
lalrpop_util::ParseError::ExtraToken { token } => Some(Span(token.0, token.2)),
|
||||
lalrpop_util::ParseError::User { error: _ } => None,
|
||||
}
|
||||
}
|
3
src/lalrpop_helpers.rs
Normal file
3
src/lalrpop_helpers.rs
Normal file
@ -0,0 +1,3 @@
|
||||
pub fn b<T>(x: T) -> Box<T> {
|
||||
Box::new(x)
|
||||
}
|
78
src/lexer.rs
Normal file
78
src/lexer.rs
Normal file
@ -0,0 +1,78 @@
|
||||
use logos::Logos;
|
||||
|
||||
#[rustfmt::skip]
|
||||
#[derive(Logos, Debug, PartialEq, Eq, Clone, strum::Display)]
|
||||
pub enum Token {
|
||||
#[strum(serialize = "+") ] #[token("+") ] Plus,
|
||||
#[strum(serialize = "-") ] #[token("-") ] Minus,
|
||||
#[strum(serialize = "*") ] #[token("*") ] Times,
|
||||
#[strum(serialize = "/") ] #[token("/") ] Div,
|
||||
#[strum(serialize = "%") ] #[token("%") ] Mod,
|
||||
#[strum(serialize = "==")] #[token("==")] Equals,
|
||||
#[strum(serialize = "!=")] #[token("!=")] NotEquals,
|
||||
#[strum(serialize = "&&")] #[token("&&")] And,
|
||||
#[strum(serialize = "||")] #[token("||")] Or,
|
||||
#[strum(serialize = ">") ] #[token(">") ] GT,
|
||||
#[strum(serialize = "<") ] #[token("<") ] LT,
|
||||
#[strum(serialize = "?:")] #[token("?:")] Elvis,
|
||||
#[strum(serialize = "=~")] #[token("=~")] RegexMatch,
|
||||
|
||||
#[strum(serialize = "!") ] #[token("!") ] Not,
|
||||
|
||||
#[strum(serialize = ",") ] #[token(",") ] Comma,
|
||||
#[strum(serialize = "?") ] #[token("?") ] Question,
|
||||
#[strum(serialize = ":") ] #[token(":") ] Colon,
|
||||
#[strum(serialize = "(") ] #[token("(") ] LPren,
|
||||
#[strum(serialize = ")") ] #[token(")") ] RPren,
|
||||
#[strum(serialize = "[") ] #[token("[") ] LBrack,
|
||||
#[strum(serialize = "]") ] #[token("]") ] RBrack,
|
||||
#[strum(serialize = ".") ] #[token(".") ] Dot,
|
||||
#[strum(serialize = "true") ] #[token("true") ] True,
|
||||
#[strum(serialize = "false")] #[token("false")] False,
|
||||
|
||||
#[regex(r"[a-zA-Z_-]+", |x| x.slice().to_string())]
|
||||
Ident(String),
|
||||
#[regex(r"[+-]?(?:[0-9]+[.])?[0-9]+", |x| x.slice().to_string())]
|
||||
NumLit(String),
|
||||
#[regex(r#""(?:[^"\\]|\\.)*""#, |x| x.slice().to_string())]
|
||||
StrLit(String),
|
||||
|
||||
|
||||
#[error]
|
||||
#[regex(r"[ \t\n\f]+", logos::skip)]
|
||||
Error,
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
|
||||
pub struct LexicalError(usize, usize);
|
||||
|
||||
impl std::fmt::Display for LexicalError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "Lexical error at {}..{}", self.0, self.1)
|
||||
}
|
||||
}
|
||||
|
||||
pub type SpannedResult<Tok, Loc, Error> = Result<(Loc, Tok, Loc), Error>;
|
||||
|
||||
pub struct Lexer<'input> {
|
||||
lexer: logos::SpannedIter<'input, Token>,
|
||||
}
|
||||
|
||||
impl<'input> Lexer<'input> {
|
||||
pub fn new(text: &'input str) -> Self {
|
||||
Lexer { lexer: logos::Lexer::new(text).spanned() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'input> Iterator for Lexer<'input> {
|
||||
type Item = SpannedResult<Token, usize, LexicalError>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let (token, range) = self.lexer.next()?;
|
||||
if token == Token::Error {
|
||||
Some(Err(LexicalError(range.start, range.end)))
|
||||
} else {
|
||||
Some(Ok((range.start, token, range.end)))
|
||||
}
|
||||
}
|
||||
}
|
38
src/lib.rs
38
src/lib.rs
@ -1,23 +1,33 @@
|
||||
pub mod ast;
|
||||
pub mod error;
|
||||
mod lalrpop_helpers;
|
||||
mod lexer;
|
||||
use ast::SimplExpr;
|
||||
use error::{Error, Result};
|
||||
use lalrpop_util::lalrpop_mod;
|
||||
|
||||
lalrpop_mod!(pub parser);
|
||||
|
||||
macro_rules! test_parser {
|
||||
($($text:literal),*) => {{
|
||||
let p = crate::parser::ExprParser::new();
|
||||
//use crate::lexer::Lexer;
|
||||
|
||||
::insta::with_settings!({sort_maps => true}, {
|
||||
$(
|
||||
::insta::assert_debug_snapshot!(p.parse($text));
|
||||
)*
|
||||
});
|
||||
}}
|
||||
pub fn parse_string(s: &str) -> Result<SimplExpr> {
|
||||
let lexer = lexer::Lexer::new(s);
|
||||
let parser = parser::ExprParser::new();
|
||||
Ok(parser.parse(lexer).map_err(|e| Error::from_parse_error(e))?)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
macro_rules! test_parser {
|
||||
($($text:literal),* $(,)?) => {{
|
||||
let p = crate::parser::ExprParser::new();
|
||||
use crate::lexer::Lexer;
|
||||
::insta::with_settings!({sort_maps => true}, {
|
||||
$(
|
||||
::insta::assert_debug_snapshot!(p.parse(Lexer::new($text)));
|
||||
)*
|
||||
});
|
||||
}}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
test_parser!(
|
||||
@ -29,7 +39,11 @@ mod tests {
|
||||
"1 + true ? 2 : 5 + 2",
|
||||
"1 + (true ? 2 : 5) + 2",
|
||||
"foo(1, 2)",
|
||||
"! false || ! true"
|
||||
"! false || ! true",
|
||||
"\"foo\" + 12.4",
|
||||
"hi[\"ho\"]",
|
||||
"foo.bar.baz",
|
||||
"foo.bar[2 + 2] * asdf[foo.bar]",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,50 @@
|
||||
|
||||
use crate::ast::{SimplExpr, Span, BinOp, UnaryOp};
|
||||
use crate::ast::{SimplExpr::{self, *}, Span, BinOp::*, UnaryOp::*};
|
||||
use crate::lexer::{Token, LexicalError};
|
||||
use crate::lalrpop_helpers::*;
|
||||
|
||||
|
||||
grammar;
|
||||
|
||||
extern {
|
||||
type Location = usize;
|
||||
type Error = LexicalError;
|
||||
|
||||
enum Token {
|
||||
"+" => Token::Plus,
|
||||
"-" => Token::Minus,
|
||||
"*" => Token::Times,
|
||||
"/" => Token::Div,
|
||||
"%" => Token::Mod,
|
||||
"==" => Token::Equals,
|
||||
"!=" => Token::NotEquals,
|
||||
"&&" => Token::And,
|
||||
"||" => Token::Or,
|
||||
">" => Token::GT,
|
||||
"<" => Token::LT,
|
||||
"?:" => Token::Elvis,
|
||||
"=~" => Token::RegexMatch,
|
||||
|
||||
"!" => Token::Not,
|
||||
|
||||
"," => Token::Comma,
|
||||
"?" => Token::Question,
|
||||
":" => Token::Colon,
|
||||
"(" => Token::LPren,
|
||||
")" => Token::RPren,
|
||||
"[" => Token::LBrack,
|
||||
"]" => Token::RBrack,
|
||||
"." => Token::Dot,
|
||||
"true" => Token::True,
|
||||
"false" => Token::False,
|
||||
|
||||
"identifier" => Token::Ident(<String>),
|
||||
"number" => Token::NumLit(<String>),
|
||||
"string" => Token::StrLit(<String>),
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Comma<T>: Vec<T> = {
|
||||
<mut v:(<T> ",")*> <e:T?> => match e {
|
||||
None => v,
|
||||
@ -15,43 +57,57 @@ Comma<T>: Vec<T> = {
|
||||
|
||||
pub Expr: SimplExpr = {
|
||||
#[precedence(level="0")]
|
||||
"true" => SimplExpr::Literal("true".to_string()),
|
||||
"false" => SimplExpr::Literal("false".to_string()),
|
||||
<Number>,
|
||||
<Literal>,
|
||||
<l:@L> <ident:"identifier"> <r:@R> => VarRef(Span(l, r), ident.to_string()),
|
||||
"(" <ExprReset> ")",
|
||||
<ident:Ident> "(" <args: Comma<ExprReset>> ")" => SimplExpr::FunctionCall(ident, args),
|
||||
|
||||
#[precedence(level="1")] #[assoc(side="right")]
|
||||
<l:@L> <ident:"identifier"> "(" <args: Comma<ExprReset>> ")" <r:@R> => FunctionCall(Span(l, r), ident, args),
|
||||
<l:@L> <value:Expr> "[" <index: ExprReset> "]" <r:@R> => JsonAccess(Span(l, r), b(value), b(index)),
|
||||
|
||||
#[precedence(level="1")] #[assoc(side="left")]
|
||||
"!" <Expr> => SimplExpr::UnaryOp(UnaryOp::Not, Box::new(<>))
|
||||
<l:@L> <value:Expr> "." <lit_l:@L> <index:"identifier"> <r:@R> => {
|
||||
JsonAccess(Span(l, r), b(value), b(Literal(Span(lit_l, r), index)))
|
||||
},
|
||||
|
||||
|
||||
#[precedence(level="2")] #[assoc(side="left")]
|
||||
<l:Expr> "*" <r:Expr> => SimplExpr::BinOp(Box::new(l), BinOp::Times, Box::new(r)),
|
||||
<l:Expr> "/" <r:Expr> => SimplExpr::BinOp(Box::new(l), BinOp::Div, Box::new(r)),
|
||||
<l:Expr> "%" <r:Expr> => SimplExpr::BinOp(Box::new(l), BinOp::Mod, Box::new(r)),
|
||||
#[precedence(level="2")] #[assoc(side="right")]
|
||||
<l:@L> "!" <e:Expr> <r:@R> => UnaryOp(Span(l, r), Not, b(e)),
|
||||
|
||||
#[precedence(level="3")] #[assoc(side="left")]
|
||||
<l:Expr> "+" <r:Expr> => SimplExpr::BinOp(Box::new(l), BinOp::Plus, Box::new(r)),
|
||||
<l:Expr> "-" <r:Expr> => SimplExpr::BinOp(Box::new(l), BinOp::Minus, Box::new(r)),
|
||||
<l:@L> <le:Expr> "*" <re:Expr> <r:@R> => BinOp(Span(l, r), b(le), Times, b(re)),
|
||||
<l:@L> <le:Expr> "/" <re:Expr> <r:@R> => BinOp(Span(l, r), b(le), Div, b(re)),
|
||||
<l:@L> <le:Expr> "%" <re:Expr> <r:@R> => BinOp(Span(l, r), b(le), Mod, b(re)),
|
||||
|
||||
#[precedence(level="4")] #[assoc(side="left")]
|
||||
<l:Expr> "==" <r:Expr> => SimplExpr::BinOp(Box::new(l), BinOp::Equals, Box::new(r)),
|
||||
<l:Expr> "!=" <r:Expr> => SimplExpr::BinOp(Box::new(l), BinOp::NotEquals, Box::new(r)),
|
||||
<l:Expr> "<" <r:Expr> => SimplExpr::BinOp(Box::new(l), BinOp::GT, Box::new(r)),
|
||||
<l:Expr> ">" <r:Expr> => SimplExpr::BinOp(Box::new(l), BinOp::LT, Box::new(r)),
|
||||
<l:Expr> "=~" <r:Expr> => SimplExpr::BinOp(Box::new(l), BinOp::RegexMatch, Box::new(r)),
|
||||
<l:@L> <le:Expr> "+" <re:Expr> <r:@R> => BinOp(Span(l, r), b(le), Plus, b(re)),
|
||||
<l:@L> <le:Expr> "-" <re:Expr> <r:@R> => BinOp(Span(l, r), b(le), Minus, b(re)),
|
||||
|
||||
#[precedence(level="5")] #[assoc(side="left")]
|
||||
<l:Expr> "&&" <r:Expr> => SimplExpr::BinOp(Box::new(l), BinOp::And, Box::new(r)),
|
||||
<l:Expr> "||" <r:Expr> => SimplExpr::BinOp(Box::new(l), BinOp::Or, Box::new(r)),
|
||||
<l:Expr> "?:" <r:Expr> => SimplExpr::BinOp(Box::new(l), BinOp::Elvis, Box::new(r)),
|
||||
<l:@L> <le:Expr> "==" <re:Expr> <r:@R> => BinOp(Span(l, r), b(le), Equals, b(re)),
|
||||
<l:@L> <le:Expr> "!=" <re:Expr> <r:@R> => BinOp(Span(l, r), b(le), NotEquals, b(re)),
|
||||
<l:@L> <le:Expr> "<" <re:Expr> <r:@R> => BinOp(Span(l, r), b(le), GT, b(re)),
|
||||
<l:@L> <le:Expr> ">" <re:Expr> <r:@R> => BinOp(Span(l, r), b(le), LT, b(re)),
|
||||
<l:@L> <le:Expr> "=~" <re:Expr> <r:@R> => BinOp(Span(l, r), b(le), RegexMatch, b(re)),
|
||||
|
||||
#[precedence(level="6")] #[assoc(side="right")]
|
||||
<cond:Expr> "?" <then:ExprReset> ":" <els:Expr> => SimplExpr::IfElse(Box::new(cond), Box::new(then), Box::new(els)),
|
||||
#[precedence(level="6")] #[assoc(side="left")]
|
||||
<l:@L> <le:Expr> "&&" <re:Expr> <r:@R> => BinOp(Span(l, r), b(le), And, b(re)),
|
||||
<l:@L> <le:Expr> "||" <re:Expr> <r:@R> => BinOp(Span(l, r), b(le), Or, b(re)),
|
||||
<l:@L> <le:Expr> "?:" <re:Expr> <r:@R> => BinOp(Span(l, r), b(le), Elvis, b(re)),
|
||||
|
||||
#[precedence(level="7")] #[assoc(side="right")]
|
||||
<l:@L> <cond:Expr> "?" <then:ExprReset> ":" <els:Expr> <r:@R> => {
|
||||
IfElse(Span(l, r), b(cond), b(then), b(els))
|
||||
},
|
||||
};
|
||||
|
||||
ExprReset = <Expr>;
|
||||
|
||||
Number: SimplExpr = r"[+-]?(?:[0-9]+[.])?[0-9]+" => SimplExpr::Literal(<>.to_string());
|
||||
Ident: String = r"[a-zA-Z_][^\s{}\(\)]*" => <>.to_string();
|
||||
Literal: SimplExpr = {
|
||||
<l:@L> <x:StrLit> <r:@R> => Literal(Span(l, r), x),
|
||||
<l:@L> <x:"number"> <r:@R> => Literal(Span(l, r), x.to_string()),
|
||||
<l:@L> "true" <r:@R> => Literal(Span(l, r), "true".to_string()),
|
||||
<l:@L> "false" <r:@R> => Literal(Span(l, r), "false".to_string()),
|
||||
}
|
||||
|
||||
StrLit: String = {
|
||||
<x:"string"> => x[1..x.len() - 1].to_owned(),
|
||||
};
|
||||
|
8
src/snapshots/simplexpr__tests__test-10.snap
Normal file
8
src/snapshots/simplexpr__tests__test-10.snap
Normal file
@ -0,0 +1,8 @@
|
||||
---
|
||||
source: src/lib.rs
|
||||
expression: "p.parse(Lexer::new(\"\\\"foo\\\" + 12.4\"))"
|
||||
|
||||
---
|
||||
Ok(
|
||||
("foo" + "12.4"),
|
||||
)
|
8
src/snapshots/simplexpr__tests__test-11.snap
Normal file
8
src/snapshots/simplexpr__tests__test-11.snap
Normal file
@ -0,0 +1,8 @@
|
||||
---
|
||||
source: src/lib.rs
|
||||
expression: "p.parse(Lexer::new(\"hi[\\\"ho\\\"]\"))"
|
||||
|
||||
---
|
||||
Ok(
|
||||
hi["ho"],
|
||||
)
|
8
src/snapshots/simplexpr__tests__test-12.snap
Normal file
8
src/snapshots/simplexpr__tests__test-12.snap
Normal file
@ -0,0 +1,8 @@
|
||||
---
|
||||
source: src/lib.rs
|
||||
expression: "p.parse(Lexer::new(\"foo.bar.baz\"))"
|
||||
|
||||
---
|
||||
Ok(
|
||||
foo["bar"]["baz"],
|
||||
)
|
8
src/snapshots/simplexpr__tests__test-13.snap
Normal file
8
src/snapshots/simplexpr__tests__test-13.snap
Normal file
@ -0,0 +1,8 @@
|
||||
---
|
||||
source: src/lib.rs
|
||||
expression: "p.parse(Lexer::new(\"foo.bar[2 + 2] * asdf[foo.bar]\"))"
|
||||
|
||||
---
|
||||
Ok(
|
||||
(foo["bar"][("2" + "2")] * asdf[foo["bar"]]),
|
||||
)
|
30
src/snapshots/simplexpr__tests__test-14.snap
Normal file
30
src/snapshots/simplexpr__tests__test-14.snap
Normal file
@ -0,0 +1,30 @@
|
||||
---
|
||||
source: src/lib.rs
|
||||
expression: "p.parse(Lexer::new(\"1 + (true ? 2 : 5) + 2\"))"
|
||||
|
||||
---
|
||||
Ok(
|
||||
BinOp(
|
||||
BinOp(
|
||||
Literal(
|
||||
"1",
|
||||
),
|
||||
Plus,
|
||||
IfElse(
|
||||
Literal(
|
||||
"true",
|
||||
),
|
||||
Literal(
|
||||
"2",
|
||||
),
|
||||
Literal(
|
||||
"5",
|
||||
),
|
||||
),
|
||||
),
|
||||
Plus,
|
||||
Literal(
|
||||
"2",
|
||||
),
|
||||
),
|
||||
)
|
13
src/snapshots/simplexpr__tests__test-15.snap
Normal file
13
src/snapshots/simplexpr__tests__test-15.snap
Normal file
@ -0,0 +1,13 @@
|
||||
---
|
||||
source: src/lib.rs
|
||||
expression: "Lexer::new(\"foo(1, 2)\").filter_map(|x|\n x.ok()).map(|(_, x, _)|\n match x {\n Token::Ident(x) |\n Token::NumLit(x) |\n Token::StrLit(x) =>\n format!(\"{}\", x),\n x =>\n format!(\"{}\", x),\n }).collect::<Vec<_>>()"
|
||||
|
||||
---
|
||||
[
|
||||
"foo",
|
||||
"LPren",
|
||||
"1",
|
||||
"Comma",
|
||||
"2",
|
||||
"RPren",
|
||||
]
|
18
src/snapshots/simplexpr__tests__test-16.snap
Normal file
18
src/snapshots/simplexpr__tests__test-16.snap
Normal file
@ -0,0 +1,18 @@
|
||||
---
|
||||
source: src/lib.rs
|
||||
expression: "p.parse(Lexer::new(\"foo(1, 2)\"))"
|
||||
|
||||
---
|
||||
Ok(
|
||||
FunctionCall(
|
||||
"foo",
|
||||
[
|
||||
Literal(
|
||||
"1",
|
||||
),
|
||||
Literal(
|
||||
"2",
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
12
src/snapshots/simplexpr__tests__test-17.snap
Normal file
12
src/snapshots/simplexpr__tests__test-17.snap
Normal file
@ -0,0 +1,12 @@
|
||||
---
|
||||
source: src/lib.rs
|
||||
expression: "Lexer::new(\"! false || ! true\").filter_map(|x|\n x.ok()).map(|(_, x, _)|\n match x {\n Token::Ident(x)\n |\n Token::NumLit(x)\n |\n Token::StrLit(x)\n =>\n format!(\"{}\",\n x),\n x =>\n format!(\"{}\",\n x),\n }).collect::<Vec<_>>()"
|
||||
|
||||
---
|
||||
[
|
||||
"!",
|
||||
"False",
|
||||
"||",
|
||||
"!",
|
||||
"True",
|
||||
]
|
22
src/snapshots/simplexpr__tests__test-18.snap
Normal file
22
src/snapshots/simplexpr__tests__test-18.snap
Normal file
@ -0,0 +1,22 @@
|
||||
---
|
||||
source: src/lib.rs
|
||||
expression: "p.parse(Lexer::new(\"! false || ! true\"))"
|
||||
|
||||
---
|
||||
Ok(
|
||||
BinOp(
|
||||
UnaryOp(
|
||||
Not,
|
||||
Literal(
|
||||
"false",
|
||||
),
|
||||
),
|
||||
Or,
|
||||
UnaryOp(
|
||||
Not,
|
||||
Literal(
|
||||
"true",
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
10
src/snapshots/simplexpr__tests__test-19.snap
Normal file
10
src/snapshots/simplexpr__tests__test-19.snap
Normal file
@ -0,0 +1,10 @@
|
||||
---
|
||||
source: src/lib.rs
|
||||
expression: "Lexer::new(\"\\\"foo\\\" + 12.4\").filter_map(|x|\n x.ok()).map(|(_, x, _)|\n match x {\n Token::Ident(x)\n |\n Token::NumLit(x)\n |\n Token::StrLit(x)\n =>\n format!(\"{}\",\n x),\n x =>\n format!(\"{}\",\n x),\n }).collect::<Vec<_>>()"
|
||||
|
||||
---
|
||||
[
|
||||
"\"foo\"",
|
||||
"+",
|
||||
"12.4",
|
||||
]
|
@ -1,16 +1,8 @@
|
||||
---
|
||||
source: src/lib.rs
|
||||
expression: "p.parse(\"2 + 5\")"
|
||||
expression: "p.parse(Lexer::new(\"2 + 5\"))"
|
||||
|
||||
---
|
||||
Ok(
|
||||
BinOp(
|
||||
Literal(
|
||||
"2",
|
||||
),
|
||||
Plus,
|
||||
Literal(
|
||||
"5",
|
||||
),
|
||||
),
|
||||
("2" + "5"),
|
||||
)
|
||||
|
16
src/snapshots/simplexpr__tests__test-20.snap
Normal file
16
src/snapshots/simplexpr__tests__test-20.snap
Normal file
@ -0,0 +1,16 @@
|
||||
---
|
||||
source: src/lib.rs
|
||||
expression: "p.parse(Lexer::new(\"\\\"foo\\\" + 12.4\"))"
|
||||
|
||||
---
|
||||
Ok(
|
||||
BinOp(
|
||||
Literal(
|
||||
"foo",
|
||||
),
|
||||
Plus,
|
||||
Literal(
|
||||
"12.4",
|
||||
),
|
||||
),
|
||||
)
|
11
src/snapshots/simplexpr__tests__test-21.snap
Normal file
11
src/snapshots/simplexpr__tests__test-21.snap
Normal file
@ -0,0 +1,11 @@
|
||||
---
|
||||
source: src/lib.rs
|
||||
expression: "Lexer::new(\"hi[\\\"ho\\\"]\").filter_map(|x|\n x.ok()).map(|(_, x, _)|\n match x {\n Token::Ident(x) |\n Token::NumLit(x) |\n Token::StrLit(x)\n =>\n format!(\"{}\", x),\n x =>\n format!(\"{}\", x),\n }).collect::<Vec<_>>()"
|
||||
|
||||
---
|
||||
[
|
||||
"hi",
|
||||
"LBrack",
|
||||
"\"ho\"",
|
||||
"RBrack",
|
||||
]
|
15
src/snapshots/simplexpr__tests__test-22.snap
Normal file
15
src/snapshots/simplexpr__tests__test-22.snap
Normal file
@ -0,0 +1,15 @@
|
||||
---
|
||||
source: src/lib.rs
|
||||
expression: "p.parse(Lexer::new(\"hi[\\\"ho\\\"]\"))"
|
||||
|
||||
---
|
||||
Ok(
|
||||
JsonAccess(
|
||||
VarRef(
|
||||
"hi",
|
||||
),
|
||||
Literal(
|
||||
"ho",
|
||||
),
|
||||
),
|
||||
)
|
12
src/snapshots/simplexpr__tests__test-23.snap
Normal file
12
src/snapshots/simplexpr__tests__test-23.snap
Normal file
@ -0,0 +1,12 @@
|
||||
---
|
||||
source: src/lib.rs
|
||||
expression: "Lexer::new(\"foo.bar.baz\").filter_map(|x|\n x.ok()).map(|(_, x, _)|\n match x {\n Token::Ident(x) |\n Token::NumLit(x)\n |\n Token::StrLit(x)\n =>\n format!(\"{}\", x),\n x =>\n format!(\"{}\", x),\n }).collect::<Vec<_>>()"
|
||||
|
||||
---
|
||||
[
|
||||
"foo",
|
||||
"Dot",
|
||||
"bar",
|
||||
"Dot",
|
||||
"baz",
|
||||
]
|
20
src/snapshots/simplexpr__tests__test-24.snap
Normal file
20
src/snapshots/simplexpr__tests__test-24.snap
Normal file
@ -0,0 +1,20 @@
|
||||
---
|
||||
source: src/lib.rs
|
||||
expression: "p.parse(Lexer::new(\"foo.bar.baz\"))"
|
||||
|
||||
---
|
||||
Ok(
|
||||
JsonAccess(
|
||||
JsonAccess(
|
||||
VarRef(
|
||||
"foo",
|
||||
),
|
||||
Literal(
|
||||
"bar",
|
||||
),
|
||||
),
|
||||
Literal(
|
||||
"baz",
|
||||
),
|
||||
),
|
||||
)
|
22
src/snapshots/simplexpr__tests__test-25.snap
Normal file
22
src/snapshots/simplexpr__tests__test-25.snap
Normal file
@ -0,0 +1,22 @@
|
||||
---
|
||||
source: src/lib.rs
|
||||
expression: "Lexer::new(\"foo.bar[2 + 2] * asdf[foo.bar]\").filter_map(|x|\n x.ok()).map(|(_,\n x,\n _)|\n match x\n {\n Token::Ident(x)\n |\n Token::NumLit(x)\n |\n Token::StrLit(x)\n =>\n format!(\"{}\",\n x),\n x\n =>\n format!(\"{}\",\n x),\n }).collect::<Vec<_>>()"
|
||||
|
||||
---
|
||||
[
|
||||
"foo",
|
||||
"Dot",
|
||||
"bar",
|
||||
"LBrack",
|
||||
"2",
|
||||
"+",
|
||||
"2",
|
||||
"RBrack",
|
||||
"*",
|
||||
"asdf",
|
||||
"LBrack",
|
||||
"foo",
|
||||
"Dot",
|
||||
"bar",
|
||||
"RBrack",
|
||||
]
|
42
src/snapshots/simplexpr__tests__test-26.snap
Normal file
42
src/snapshots/simplexpr__tests__test-26.snap
Normal file
@ -0,0 +1,42 @@
|
||||
---
|
||||
source: src/lib.rs
|
||||
expression: "p.parse(Lexer::new(\"foo.bar[2 + 2] * asdf[foo.bar]\"))"
|
||||
|
||||
---
|
||||
Ok(
|
||||
BinOp(
|
||||
JsonAccess(
|
||||
JsonAccess(
|
||||
VarRef(
|
||||
"foo",
|
||||
),
|
||||
Literal(
|
||||
"bar",
|
||||
),
|
||||
),
|
||||
BinOp(
|
||||
Literal(
|
||||
"2",
|
||||
),
|
||||
Plus,
|
||||
Literal(
|
||||
"2",
|
||||
),
|
||||
),
|
||||
),
|
||||
Times,
|
||||
JsonAccess(
|
||||
VarRef(
|
||||
"asdf",
|
||||
),
|
||||
JsonAccess(
|
||||
VarRef(
|
||||
"foo",
|
||||
),
|
||||
Literal(
|
||||
"bar",
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
@ -1,34 +1,8 @@
|
||||
---
|
||||
source: src/lib.rs
|
||||
expression: "p.parse(\"2 * 5 + 1 * 1 + 3\")"
|
||||
expression: "p.parse(Lexer::new(\"2 * 5 + 1 * 1 + 3\"))"
|
||||
|
||||
---
|
||||
Ok(
|
||||
BinOp(
|
||||
BinOp(
|
||||
BinOp(
|
||||
Literal(
|
||||
"2",
|
||||
),
|
||||
Times,
|
||||
Literal(
|
||||
"5",
|
||||
),
|
||||
),
|
||||
Plus,
|
||||
BinOp(
|
||||
Literal(
|
||||
"1",
|
||||
),
|
||||
Times,
|
||||
Literal(
|
||||
"1",
|
||||
),
|
||||
),
|
||||
),
|
||||
Plus,
|
||||
Literal(
|
||||
"3",
|
||||
),
|
||||
),
|
||||
((("2" * "5") + ("1" * "1")) + "3"),
|
||||
)
|
||||
|
@ -1,22 +1,8 @@
|
||||
---
|
||||
source: src/lib.rs
|
||||
expression: "p.parse(\"(1 + 2) * 2\")"
|
||||
expression: "p.parse(Lexer::new(\"(1 + 2) * 2\"))"
|
||||
|
||||
---
|
||||
Ok(
|
||||
BinOp(
|
||||
BinOp(
|
||||
Literal(
|
||||
"1",
|
||||
),
|
||||
Plus,
|
||||
Literal(
|
||||
"2",
|
||||
),
|
||||
),
|
||||
Times,
|
||||
Literal(
|
||||
"2",
|
||||
),
|
||||
),
|
||||
(("1" + "2") * "2"),
|
||||
)
|
||||
|
@ -1,24 +1,8 @@
|
||||
---
|
||||
source: src/lib.rs
|
||||
expression: "p.parse(\"1 + true ? 2 : 5\")"
|
||||
expression: "p.parse(Lexer::new(\"1 + true ? 2 : 5\"))"
|
||||
|
||||
---
|
||||
Ok(
|
||||
IfElse(
|
||||
BinOp(
|
||||
Literal(
|
||||
"1",
|
||||
),
|
||||
Plus,
|
||||
Literal(
|
||||
"true",
|
||||
),
|
||||
),
|
||||
Literal(
|
||||
"2",
|
||||
),
|
||||
Literal(
|
||||
"5",
|
||||
),
|
||||
),
|
||||
(if ("1" + "true") then "2" else "5"),
|
||||
)
|
||||
|
@ -1,30 +1,8 @@
|
||||
---
|
||||
source: src/lib.rs
|
||||
expression: "p.parse(\"1 + true ? 2 : 5 + 2\")"
|
||||
expression: "p.parse(Lexer::new(\"1 + true ? 2 : 5 + 2\"))"
|
||||
|
||||
---
|
||||
Ok(
|
||||
IfElse(
|
||||
BinOp(
|
||||
Literal(
|
||||
"1",
|
||||
),
|
||||
Plus,
|
||||
Literal(
|
||||
"true",
|
||||
),
|
||||
),
|
||||
Literal(
|
||||
"2",
|
||||
),
|
||||
BinOp(
|
||||
Literal(
|
||||
"5",
|
||||
),
|
||||
Plus,
|
||||
Literal(
|
||||
"2",
|
||||
),
|
||||
),
|
||||
),
|
||||
(if ("1" + "true") then "2" else ("5" + "2")),
|
||||
)
|
||||
|
@ -1,30 +1,8 @@
|
||||
---
|
||||
source: src/lib.rs
|
||||
expression: "p.parse(\"1 + (if true then 2 else 5) + 2\")"
|
||||
expression: "p.parse(Lexer::new(\"1 + (true ? 2 : 5) + 2\"))"
|
||||
|
||||
---
|
||||
Ok(
|
||||
BinOp(
|
||||
BinOp(
|
||||
Literal(
|
||||
"1",
|
||||
),
|
||||
Plus,
|
||||
IfElse(
|
||||
Literal(
|
||||
"true",
|
||||
),
|
||||
Literal(
|
||||
"2",
|
||||
),
|
||||
Literal(
|
||||
"5",
|
||||
),
|
||||
),
|
||||
),
|
||||
Plus,
|
||||
Literal(
|
||||
"2",
|
||||
),
|
||||
),
|
||||
(("1" + (if "true" then "2" else "5")) + "2"),
|
||||
)
|
||||
|
@ -1,18 +1,8 @@
|
||||
---
|
||||
source: src/lib.rs
|
||||
expression: "p.parse(\"foo(1, 2)\")"
|
||||
expression: "p.parse(Lexer::new(\"foo(1, 2)\"))"
|
||||
|
||||
---
|
||||
Ok(
|
||||
FunctionCall(
|
||||
"foo",
|
||||
[
|
||||
Literal(
|
||||
"1",
|
||||
),
|
||||
Literal(
|
||||
"2",
|
||||
),
|
||||
],
|
||||
),
|
||||
foo("1", "2"),
|
||||
)
|
||||
|
8
src/snapshots/simplexpr__tests__test-9.snap
Normal file
8
src/snapshots/simplexpr__tests__test-9.snap
Normal file
@ -0,0 +1,8 @@
|
||||
---
|
||||
source: src/lib.rs
|
||||
expression: "p.parse(Lexer::new(\"! false || ! true\"))"
|
||||
|
||||
---
|
||||
Ok(
|
||||
(!"false" || !"true"),
|
||||
)
|
@ -1,10 +1,8 @@
|
||||
---
|
||||
source: src/lib.rs
|
||||
expression: "p.parse(\"1\")"
|
||||
expression: "p.parse(Lexer::new(\"1\"))"
|
||||
|
||||
---
|
||||
Ok(
|
||||
Literal(
|
||||
"1",
|
||||
),
|
||||
"1",
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user