Finish basic parser

This commit is contained in:
elkowar 2021-07-16 14:19:54 +02:00
parent 923d478b33
commit c5643424ca
No known key found for this signature in database
GPG Key ID: E321AD71B1D1F27F
35 changed files with 596 additions and 235 deletions

View File

@ -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]

View File

@ -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
View 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();
}
}
}

View File

@ -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
View 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
View File

@ -0,0 +1,3 @@
pub fn b<T>(x: T) -> Box<T> {
Box::new(x)
}

78
src/lexer.rs Normal file
View 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)))
}
}
}

View File

@ -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]",
);
}
}

View File

@ -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(),
};

View File

@ -0,0 +1,8 @@
---
source: src/lib.rs
expression: "p.parse(Lexer::new(\"\\\"foo\\\" + 12.4\"))"
---
Ok(
("foo" + "12.4"),
)

View File

@ -0,0 +1,8 @@
---
source: src/lib.rs
expression: "p.parse(Lexer::new(\"hi[\\\"ho\\\"]\"))"
---
Ok(
hi["ho"],
)

View File

@ -0,0 +1,8 @@
---
source: src/lib.rs
expression: "p.parse(Lexer::new(\"foo.bar.baz\"))"
---
Ok(
foo["bar"]["baz"],
)

View 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"]]),
)

View 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",
),
),
)

View 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",
]

View File

@ -0,0 +1,18 @@
---
source: src/lib.rs
expression: "p.parse(Lexer::new(\"foo(1, 2)\"))"
---
Ok(
FunctionCall(
"foo",
[
Literal(
"1",
),
Literal(
"2",
),
],
),
)

View 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",
]

View 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",
),
),
),
)

View 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",
]

View File

@ -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"),
)

View 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",
),
),
)

View 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",
]

View File

@ -0,0 +1,15 @@
---
source: src/lib.rs
expression: "p.parse(Lexer::new(\"hi[\\\"ho\\\"]\"))"
---
Ok(
JsonAccess(
VarRef(
"hi",
),
Literal(
"ho",
),
),
)

View 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",
]

View 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",
),
),
)

View 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",
]

View 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",
),
),
),
),
)

View File

@ -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"),
)

View File

@ -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"),
)

View File

@ -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"),
)

View File

@ -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")),
)

View File

@ -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"),
)

View File

@ -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"),
)

View File

@ -0,0 +1,8 @@
---
source: src/lib.rs
expression: "p.parse(Lexer::new(\"! false || ! true\"))"
---
Ok(
(!"false" || !"true"),
)

View File

@ -1,10 +1,8 @@
---
source: src/lib.rs
expression: "p.parse(\"1\")"
expression: "p.parse(Lexer::new(\"1\"))"
---
Ok(
Literal(
"1",
),
"1",
)