diff --git a/Cargo.toml b/Cargo.toml index 87e69fd..2d6c0c2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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] diff --git a/build.rs b/build.rs index 57684be..cbc2c25 100644 --- a/build.rs +++ b/build.rs @@ -1,4 +1,4 @@ extern crate lalrpop; fn main() { - lalrpop::process_root().unwrap(); + lalrpop::Configuration::new().log_verbose().process_current_dir().unwrap(); } diff --git a/examples/errors.rs b/examples/errors.rs new file mode 100644 index 00000000..d089959 --- /dev/null +++ b/examples/errors.rs @@ -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(); + } + } +} diff --git a/src/ast.rs b/src/ast.rs index 0bb2684..9ef345a 100644 --- a/src/ast.rs +++ b/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, BinOp, Box), - UnaryOp(UnaryOp, Box), - IfElse(Box, Box, Box), - JsonAccess(Box, Box), - FunctionCall(String, Vec), + Literal(Span, String), + VarRef(Span, String), + BinOp(Span, Box, BinOp, Box), + UnaryOp(Span, UnaryOp, Box), + IfElse(Span, Box, Box, Box), + JsonAccess(Span, Box, Box), + FunctionCall(Span, String, Vec), } 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) + } +} diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 00000000..2f04c9b --- /dev/null +++ b/src/error.rs @@ -0,0 +1,46 @@ +use crate::{ast::Span, lexer}; +use codespan_reporting::diagnostic; + +pub type Result = std::result::Result; +pub enum Error { + ParseError { source: lalrpop_util::ParseError }, +} + +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) -> Self { + Error::ParseError { source: err } + } + + pub fn get_span(&self) -> Option { + match self { + Self::ParseError { source } => get_parse_error_span(source), + } + } + + pub fn pretty_diagnostic(&self) -> diagnostic::Diagnostic { + 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) -> Option { + 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, + } +} diff --git a/src/lalrpop_helpers.rs b/src/lalrpop_helpers.rs new file mode 100644 index 00000000..f642a30 --- /dev/null +++ b/src/lalrpop_helpers.rs @@ -0,0 +1,3 @@ +pub fn b(x: T) -> Box { + Box::new(x) +} diff --git a/src/lexer.rs b/src/lexer.rs new file mode 100644 index 00000000..ab73f36 --- /dev/null +++ b/src/lexer.rs @@ -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 = 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; + + fn next(&mut self) -> Option { + 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))) + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 1636430..96fca9c 100644 --- a/src/lib.rs +++ b/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 { + 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]", ); } } diff --git a/src/parser.lalrpop b/src/parser.lalrpop index 2db6bf0..d574c7c 100644 --- a/src/parser.lalrpop +++ b/src/parser.lalrpop @@ -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(), + "number" => Token::NumLit(), + "string" => Token::StrLit(), + + } +} + Comma: Vec = { ",")*> => match e { None => v, @@ -15,43 +57,57 @@ Comma: Vec = { pub Expr: SimplExpr = { #[precedence(level="0")] - "true" => SimplExpr::Literal("true".to_string()), - "false" => SimplExpr::Literal("false".to_string()), - , + , + => VarRef(Span(l, r), ident.to_string()), "(" ")", - "(" > ")" => SimplExpr::FunctionCall(ident, args), + #[precedence(level="1")] #[assoc(side="right")] + "(" > ")" => FunctionCall(Span(l, r), ident, args), + "[" "]" => JsonAccess(Span(l, r), b(value), b(index)), - #[precedence(level="1")] #[assoc(side="left")] - "!" => SimplExpr::UnaryOp(UnaryOp::Not, Box::new(<>)) + "." => { + JsonAccess(Span(l, r), b(value), b(Literal(Span(lit_l, r), index))) + }, - - #[precedence(level="2")] #[assoc(side="left")] - "*" => SimplExpr::BinOp(Box::new(l), BinOp::Times, Box::new(r)), - "/" => SimplExpr::BinOp(Box::new(l), BinOp::Div, Box::new(r)), - "%" => SimplExpr::BinOp(Box::new(l), BinOp::Mod, Box::new(r)), + #[precedence(level="2")] #[assoc(side="right")] + "!" => UnaryOp(Span(l, r), Not, b(e)), #[precedence(level="3")] #[assoc(side="left")] - "+" => SimplExpr::BinOp(Box::new(l), BinOp::Plus, Box::new(r)), - "-" => SimplExpr::BinOp(Box::new(l), BinOp::Minus, Box::new(r)), + "*" => BinOp(Span(l, r), b(le), Times, b(re)), + "/" => BinOp(Span(l, r), b(le), Div, b(re)), + "%" => BinOp(Span(l, r), b(le), Mod, b(re)), #[precedence(level="4")] #[assoc(side="left")] - "==" => SimplExpr::BinOp(Box::new(l), BinOp::Equals, Box::new(r)), - "!=" => SimplExpr::BinOp(Box::new(l), BinOp::NotEquals, Box::new(r)), - "<" => SimplExpr::BinOp(Box::new(l), BinOp::GT, Box::new(r)), - ">" => SimplExpr::BinOp(Box::new(l), BinOp::LT, Box::new(r)), - "=~" => SimplExpr::BinOp(Box::new(l), BinOp::RegexMatch, Box::new(r)), + "+" => BinOp(Span(l, r), b(le), Plus, b(re)), + "-" => BinOp(Span(l, r), b(le), Minus, b(re)), #[precedence(level="5")] #[assoc(side="left")] - "&&" => SimplExpr::BinOp(Box::new(l), BinOp::And, Box::new(r)), - "||" => SimplExpr::BinOp(Box::new(l), BinOp::Or, Box::new(r)), - "?:" => SimplExpr::BinOp(Box::new(l), BinOp::Elvis, Box::new(r)), + "==" => BinOp(Span(l, r), b(le), Equals, b(re)), + "!=" => BinOp(Span(l, r), b(le), NotEquals, b(re)), + "<" => BinOp(Span(l, r), b(le), GT, b(re)), + ">" => BinOp(Span(l, r), b(le), LT, b(re)), + "=~" => BinOp(Span(l, r), b(le), RegexMatch, b(re)), - #[precedence(level="6")] #[assoc(side="right")] - "?" ":" => SimplExpr::IfElse(Box::new(cond), Box::new(then), Box::new(els)), + #[precedence(level="6")] #[assoc(side="left")] + "&&" => BinOp(Span(l, r), b(le), And, b(re)), + "||" => BinOp(Span(l, r), b(le), Or, b(re)), + "?:" => BinOp(Span(l, r), b(le), Elvis, b(re)), + + #[precedence(level="7")] #[assoc(side="right")] + "?" ":" => { + IfElse(Span(l, r), b(cond), b(then), b(els)) + }, }; ExprReset = ; -Number: SimplExpr = r"[+-]?(?:[0-9]+[.])?[0-9]+" => SimplExpr::Literal(<>.to_string()); -Ident: String = r"[a-zA-Z_][^\s{}\(\)]*" => <>.to_string(); +Literal: SimplExpr = { + => Literal(Span(l, r), x), + => Literal(Span(l, r), x.to_string()), + "true" => Literal(Span(l, r), "true".to_string()), + "false" => Literal(Span(l, r), "false".to_string()), +} + +StrLit: String = { + => x[1..x.len() - 1].to_owned(), +}; diff --git a/src/snapshots/simplexpr__tests__test-10.snap b/src/snapshots/simplexpr__tests__test-10.snap new file mode 100644 index 00000000..1fa28f0 --- /dev/null +++ b/src/snapshots/simplexpr__tests__test-10.snap @@ -0,0 +1,8 @@ +--- +source: src/lib.rs +expression: "p.parse(Lexer::new(\"\\\"foo\\\" + 12.4\"))" + +--- +Ok( + ("foo" + "12.4"), +) diff --git a/src/snapshots/simplexpr__tests__test-11.snap b/src/snapshots/simplexpr__tests__test-11.snap new file mode 100644 index 00000000..44b65b7 --- /dev/null +++ b/src/snapshots/simplexpr__tests__test-11.snap @@ -0,0 +1,8 @@ +--- +source: src/lib.rs +expression: "p.parse(Lexer::new(\"hi[\\\"ho\\\"]\"))" + +--- +Ok( + hi["ho"], +) diff --git a/src/snapshots/simplexpr__tests__test-12.snap b/src/snapshots/simplexpr__tests__test-12.snap new file mode 100644 index 00000000..e19354b --- /dev/null +++ b/src/snapshots/simplexpr__tests__test-12.snap @@ -0,0 +1,8 @@ +--- +source: src/lib.rs +expression: "p.parse(Lexer::new(\"foo.bar.baz\"))" + +--- +Ok( + foo["bar"]["baz"], +) diff --git a/src/snapshots/simplexpr__tests__test-13.snap b/src/snapshots/simplexpr__tests__test-13.snap new file mode 100644 index 00000000..7494deb --- /dev/null +++ b/src/snapshots/simplexpr__tests__test-13.snap @@ -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"]]), +) diff --git a/src/snapshots/simplexpr__tests__test-14.snap b/src/snapshots/simplexpr__tests__test-14.snap new file mode 100644 index 00000000..692ac35 --- /dev/null +++ b/src/snapshots/simplexpr__tests__test-14.snap @@ -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", + ), + ), +) diff --git a/src/snapshots/simplexpr__tests__test-15.snap b/src/snapshots/simplexpr__tests__test-15.snap new file mode 100644 index 00000000..16b8e8a --- /dev/null +++ b/src/snapshots/simplexpr__tests__test-15.snap @@ -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::>()" + +--- +[ + "foo", + "LPren", + "1", + "Comma", + "2", + "RPren", +] diff --git a/src/snapshots/simplexpr__tests__test-16.snap b/src/snapshots/simplexpr__tests__test-16.snap new file mode 100644 index 00000000..e128f15 --- /dev/null +++ b/src/snapshots/simplexpr__tests__test-16.snap @@ -0,0 +1,18 @@ +--- +source: src/lib.rs +expression: "p.parse(Lexer::new(\"foo(1, 2)\"))" + +--- +Ok( + FunctionCall( + "foo", + [ + Literal( + "1", + ), + Literal( + "2", + ), + ], + ), +) diff --git a/src/snapshots/simplexpr__tests__test-17.snap b/src/snapshots/simplexpr__tests__test-17.snap new file mode 100644 index 00000000..ae5f678 --- /dev/null +++ b/src/snapshots/simplexpr__tests__test-17.snap @@ -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::>()" + +--- +[ + "!", + "False", + "||", + "!", + "True", +] diff --git a/src/snapshots/simplexpr__tests__test-18.snap b/src/snapshots/simplexpr__tests__test-18.snap new file mode 100644 index 00000000..9ffd512 --- /dev/null +++ b/src/snapshots/simplexpr__tests__test-18.snap @@ -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", + ), + ), + ), +) diff --git a/src/snapshots/simplexpr__tests__test-19.snap b/src/snapshots/simplexpr__tests__test-19.snap new file mode 100644 index 00000000..8d6b02b --- /dev/null +++ b/src/snapshots/simplexpr__tests__test-19.snap @@ -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::>()" + +--- +[ + "\"foo\"", + "+", + "12.4", +] diff --git a/src/snapshots/simplexpr__tests__test-2.snap b/src/snapshots/simplexpr__tests__test-2.snap index 9d2e4fb..3b4e5e4 100644 --- a/src/snapshots/simplexpr__tests__test-2.snap +++ b/src/snapshots/simplexpr__tests__test-2.snap @@ -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"), ) diff --git a/src/snapshots/simplexpr__tests__test-20.snap b/src/snapshots/simplexpr__tests__test-20.snap new file mode 100644 index 00000000..9d42e5a --- /dev/null +++ b/src/snapshots/simplexpr__tests__test-20.snap @@ -0,0 +1,16 @@ +--- +source: src/lib.rs +expression: "p.parse(Lexer::new(\"\\\"foo\\\" + 12.4\"))" + +--- +Ok( + BinOp( + Literal( + "foo", + ), + Plus, + Literal( + "12.4", + ), + ), +) diff --git a/src/snapshots/simplexpr__tests__test-21.snap b/src/snapshots/simplexpr__tests__test-21.snap new file mode 100644 index 00000000..84a5eb7 --- /dev/null +++ b/src/snapshots/simplexpr__tests__test-21.snap @@ -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::>()" + +--- +[ + "hi", + "LBrack", + "\"ho\"", + "RBrack", +] diff --git a/src/snapshots/simplexpr__tests__test-22.snap b/src/snapshots/simplexpr__tests__test-22.snap new file mode 100644 index 00000000..958cec3 --- /dev/null +++ b/src/snapshots/simplexpr__tests__test-22.snap @@ -0,0 +1,15 @@ +--- +source: src/lib.rs +expression: "p.parse(Lexer::new(\"hi[\\\"ho\\\"]\"))" + +--- +Ok( + JsonAccess( + VarRef( + "hi", + ), + Literal( + "ho", + ), + ), +) diff --git a/src/snapshots/simplexpr__tests__test-23.snap b/src/snapshots/simplexpr__tests__test-23.snap new file mode 100644 index 00000000..6e91f79 --- /dev/null +++ b/src/snapshots/simplexpr__tests__test-23.snap @@ -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::>()" + +--- +[ + "foo", + "Dot", + "bar", + "Dot", + "baz", +] diff --git a/src/snapshots/simplexpr__tests__test-24.snap b/src/snapshots/simplexpr__tests__test-24.snap new file mode 100644 index 00000000..be97009 --- /dev/null +++ b/src/snapshots/simplexpr__tests__test-24.snap @@ -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", + ), + ), +) diff --git a/src/snapshots/simplexpr__tests__test-25.snap b/src/snapshots/simplexpr__tests__test-25.snap new file mode 100644 index 00000000..3a21b7b --- /dev/null +++ b/src/snapshots/simplexpr__tests__test-25.snap @@ -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::>()" + +--- +[ + "foo", + "Dot", + "bar", + "LBrack", + "2", + "+", + "2", + "RBrack", + "*", + "asdf", + "LBrack", + "foo", + "Dot", + "bar", + "RBrack", +] diff --git a/src/snapshots/simplexpr__tests__test-26.snap b/src/snapshots/simplexpr__tests__test-26.snap new file mode 100644 index 00000000..bdabe4e --- /dev/null +++ b/src/snapshots/simplexpr__tests__test-26.snap @@ -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", + ), + ), + ), + ), +) diff --git a/src/snapshots/simplexpr__tests__test-3.snap b/src/snapshots/simplexpr__tests__test-3.snap index ed8b2c9..7a11e2b 100644 --- a/src/snapshots/simplexpr__tests__test-3.snap +++ b/src/snapshots/simplexpr__tests__test-3.snap @@ -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"), ) diff --git a/src/snapshots/simplexpr__tests__test-4.snap b/src/snapshots/simplexpr__tests__test-4.snap index 1c5c5f5..9025ed3 100644 --- a/src/snapshots/simplexpr__tests__test-4.snap +++ b/src/snapshots/simplexpr__tests__test-4.snap @@ -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"), ) diff --git a/src/snapshots/simplexpr__tests__test-5.snap b/src/snapshots/simplexpr__tests__test-5.snap index f71ba69..683c97b 100644 --- a/src/snapshots/simplexpr__tests__test-5.snap +++ b/src/snapshots/simplexpr__tests__test-5.snap @@ -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"), ) diff --git a/src/snapshots/simplexpr__tests__test-6.snap b/src/snapshots/simplexpr__tests__test-6.snap index 4efff48..ca3c9ba 100644 --- a/src/snapshots/simplexpr__tests__test-6.snap +++ b/src/snapshots/simplexpr__tests__test-6.snap @@ -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")), ) diff --git a/src/snapshots/simplexpr__tests__test-7.snap b/src/snapshots/simplexpr__tests__test-7.snap index ff662bd..bd2587c 100644 --- a/src/snapshots/simplexpr__tests__test-7.snap +++ b/src/snapshots/simplexpr__tests__test-7.snap @@ -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"), ) diff --git a/src/snapshots/simplexpr__tests__test-8.snap b/src/snapshots/simplexpr__tests__test-8.snap index 976aa45..cbd02d1 100644 --- a/src/snapshots/simplexpr__tests__test-8.snap +++ b/src/snapshots/simplexpr__tests__test-8.snap @@ -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"), ) diff --git a/src/snapshots/simplexpr__tests__test-9.snap b/src/snapshots/simplexpr__tests__test-9.snap new file mode 100644 index 00000000..eba646a --- /dev/null +++ b/src/snapshots/simplexpr__tests__test-9.snap @@ -0,0 +1,8 @@ +--- +source: src/lib.rs +expression: "p.parse(Lexer::new(\"! false || ! true\"))" + +--- +Ok( + (!"false" || !"true"), +) diff --git a/src/snapshots/simplexpr__tests__test.snap b/src/snapshots/simplexpr__tests__test.snap index 929695f..6c1e712 100644 --- a/src/snapshots/simplexpr__tests__test.snap +++ b/src/snapshots/simplexpr__tests__test.snap @@ -1,10 +1,8 @@ --- source: src/lib.rs -expression: "p.parse(\"1\")" +expression: "p.parse(Lexer::new(\"1\"))" --- Ok( - Literal( - "1", - ), + "1", )