difftreelog
refactor(parser) make parser parse stdlib successfully
in: master
4 files changed
crates/jsonnet-parser/Cargo.tomldiffbeforeafterboth--- a/crates/jsonnet-parser/Cargo.toml
+++ b/crates/jsonnet-parser/Cargo.toml
@@ -11,3 +11,6 @@
[dependencies]
peg = "0.6.2"
+
+[dev-dependencies]
+jsonnet-stdlib = { version = "0.1.0", path = "../jsonnet-stdlib" }
crates/jsonnet-parser/README.mddiffbeforeafterboth--- /dev/null
+++ b/crates/jsonnet-parser/README.md
@@ -0,0 +1,3 @@
+# jsonnet-parser
+
+Parser for jsonnet language
crates/jsonnet-parser/src/expr.rsdiffbeforeafterboth1#[derive(Debug, Clone, PartialEq)]2pub enum FieldName {3 /// {fixed: 2}4 Fixed(String),5 /// {["dyn"+"amic"]: 3}6 Dyn(Box<Expr>),7}89#[derive(Debug, Clone, PartialEq)]10pub enum Visibility {11 /// :12 Normal,13 /// ::14 Hidden,15 /// :::16 Unhide,17}1819#[derive(Debug, Clone, PartialEq)]20pub struct AssertStmt(pub Box<Expr>, pub Option<Box<Expr>>);2122#[derive(Debug, Clone, PartialEq)]23pub enum FieldMember {24 Value {25 name: FieldName,26 plus: bool,27 visibility: Visibility,28 value: Expr,29 },30 Function {31 name: FieldName,32 params: ParamsDesc,33 visibility: Visibility,34 value: Expr,35 },36}3738#[derive(Debug, Clone, PartialEq)]39pub enum Member {40 Field(FieldMember),41 BindStmt(Bind),42 AssertStmt(AssertStmt),43}4445#[derive(Debug, Clone, PartialEq)]46pub enum UnaryOpType {47 Plus,48 Minus,49 BitNot,50 Not,51}5253#[derive(Debug, Clone, PartialEq)]54pub enum BinaryOpType {55 Mul,56 Div,57 Mod,5859 Add,60 Sub,6162 Lhs,63 Rhs,6465 Lt,66 Gt,67 Lte,68 Gte,6970 In,7172 Eq,73 Ne,7475 BitAnd,76 BitOr,77 BitXor,7879 And,80 Or,81}8283#[derive(Debug, Clone, PartialEq)]84pub enum Param {85 Positional(String),86 Named(String, Box<Expr>),87}8889#[derive(Debug, Clone, PartialEq)]90pub struct ParamsDesc(pub Vec<Param>);9192#[derive(Debug, Clone, PartialEq)]93pub enum Arg {94 Positional(Box<Expr>),95 Named(String, Box<Expr>),96}9798#[derive(Debug, Clone, PartialEq)]99pub struct ArgsDesc(pub Vec<Arg>);100101#[derive(Debug, Clone, PartialEq)]102pub enum Bind {103 Value(String, Box<Expr>),104 Function(String, ParamsDesc, Box<Expr>),105}106107#[derive(Debug, Clone, PartialEq)]108pub struct IfSpec(pub Box<Expr>);109#[derive(Debug, Clone, PartialEq)]110pub struct ForSpec(pub String, pub Vec<IfSpec>);111112#[derive(Debug, Clone, PartialEq)]113pub enum CompSpec {114 IfSpec(IfSpec),115 ForSpec(ForSpec),116}117118#[derive(Debug, Clone, PartialEq)]119pub enum ObjBody {120 MemberList(Vec<Member>),121 ObjComp {122 pre_locals: Vec<Bind>,123 key: Box<Expr>,124 value: Box<Expr>,125 post_locals: Vec<Bind>,126 first: ForSpec,127 rest: Vec<CompSpec>,128 },129}130131#[derive(Debug, Clone, PartialEq)]132pub enum ValueType {133 Null,134 True,135 False,136}137138#[derive(Debug, Clone, PartialEq)]139pub enum LiteralType {140 This,141 Super,142 Dollar,143}144145/// Syntax base146#[derive(Debug, Clone, PartialEq)]147pub enum Expr {148 Value(ValueType),149 /// Plain value: null/true/false150 Literal(LiteralType),151152 /// String value: "hello"153 Str(String),154 /// Number: 1, 2.0, 2e+20155 Num(f64),156 /// Variable name: test157 Var(String),158159 /// Array of expressions: [1, 2, "Hello"]160 Arr(Vec<Expr>),161 /// Array comprehension:162 /// ```jsonnet163 /// ingredients: [164 /// { kind: kind, qty: 4 / 3 }165 /// for kind in [166 /// 'Honey Syrup',167 /// 'Lemon Juice',168 /// 'Farmers Gin',169 /// ]170 /// ],171 /// ```172 ArrComp(Box<Expr>, Vec<ForSpec>),173174 /// Object: {a: 2}175 Obj(ObjBody),176 /// Object extension: var1 {b: 2}177 ObjExtend(Box<Expr>, ObjBody),178179 /// (obj)180 Parened(Box<Expr>),181182 Params(ParamsDesc),183 Args(ArgsDesc),184185 UnaryOp(UnaryOpType, Box<Expr>),186 BinaryOp(Box<Expr>, BinaryOpType, Box<Expr>),187 AssertExpr(AssertStmt, Box<Expr>),188 LocalExpr(Vec<Bind>, Box<Expr>),189190 Bind(Bind),191 Import(String),192 ImportStr(String),193 Error(Box<Expr>),194 Apply(Box<Expr>, ArgsDesc),195 Select(Box<Expr>, String),196 Index(Box<Expr>, Box<Expr>),197 Slice {198 value: Box<Expr>,199 start: Option<Box<Expr>>,200 end: Option<Box<Expr>>,201 step: Option<Box<Expr>>,202 },203 Function(ParamsDesc, Box<Expr>),204 IfElse {205 cond: IfSpec,206 cond_then: Box<Expr>,207 cond_else: Option<Box<Expr>>,208 },209 IfSpec(IfSpec),210 ForSpec(ForSpec),211}crates/jsonnet-parser/src/lib.rsdiffbeforeafterboth--- a/crates/jsonnet-parser/src/lib.rs
+++ b/crates/jsonnet-parser/src/lib.rs
@@ -7,188 +7,228 @@
enum Suffix {
String(String),
+ Slice(SliceDesc),
Expression(Expr),
Apply(expr::ArgsDesc),
+ Extend(expr::ObjBody),
}
parser! {
grammar jsonnet_parser() for str {
- rule delimiter() = quiet!{__() "," __()} / expected!("<elements delimiter>")
- rule _() = quiet!{[' ' | '\n' | '\t']+} / expected!("<whitespace>")
- rule __() = quiet!{[' ' | '\n' | '\t']*}
+ use peg::ParseLiteral;
+
+ /// Standard C-like comments
+ rule comment() = "//" (!['\n'][_])* "\n" / "/*" ((!("*/")[_][_])/("\\" "*/"))* "*/"
+ rule _() = ([' ' | '\n' | '\t'] / comment())*
+
+ /// For comma-delimited elements
+ rule comma() = quiet!{_ "," _} / expected!("<comma>")
rule alpha() -> char = c:$(['_' | 'a'..='z' | 'A'..='Z']) {c.chars().nth(0).unwrap()}
rule digit() -> char = d:$(['0'..='9']) {d.chars().nth(0).unwrap()}
- rule int() -> u32 = a:$(digit()+) { a.parse().unwrap() }
- rule number() -> f64 = quiet!{a:$((['-'|'+'])? int() ("." int())? (['e'|'E'] (s:['+'|'-'])? int())?) { a.parse().unwrap() }} / expected!("<number>")
- rule id() -> String = quiet!{ !("local" / "super" / "self" / "true" / "false" / "null" / "$" / "if" / "then" / "else" / "function") s:$(alpha() (alpha() / digit())*) {s.to_owned()}} / expected!("<identifier>")
+ rule end_of_ident() = !['0'..='9' | '_' | 'a'..='z' | 'A'..='Z']
+ /// Sequence of digits
+ rule uint() -> u32 = a:$(digit()+) { a.parse().unwrap() }
+ /// Number in scientific notation format
+ rule number() -> f64 = quiet!{a:$(uint() ("." uint())? (['e'|'E'] (s:['+'|'-'])? uint())?) { a.parse().unwrap() }} / expected!("<number>")
+
+ /// Reserved word followed by any non-alphanumberic
+ rule reserved() = ("assert" / "else" / "error" / "false" / "for" / "function" / "if" / "import" / "importstr" / "in" / "local" / "null" / "tailstrict" / "then" / "self" / "super" / "true") end_of_ident()
+ rule id() -> String = quiet!{ !reserved() s:$(alpha() (alpha() / digit())*) {s.to_owned()}} / expected!("<identifier>")
+ rule keyword(id: &'static str) = ##parse_string_literal(id) end_of_ident()
- pub rule positional_param() -> expr::Param = name:id() {expr::Param::Positional(name)}
- pub rule named_param() -> expr::Param = name:id() __() "=" __() expr:boxed_expr() {expr::Param::Named(name, expr)}
+ pub rule param() -> expr::Param = name:id() expr:(_ "=" _ expr:boxed_expr(){expr})? { expr::Param(name, expr) }
pub rule params() -> expr::ParamsDesc
- = positionals:(positional_param() ** delimiter()) named: (delimiter() named:(named_param() ** delimiter()) {named})? {
- if named.is_some() {
- expr::ParamsDesc([&positionals[..], &named.unwrap()[..]].concat())
- } else {
- expr::ParamsDesc(positionals)
+ = params:(param() ** comma()) {
+ let mut defaults_started = false;
+ for param in ¶ms {
+ defaults_started = defaults_started || param.1.is_some();
+ assert_eq!(defaults_started, param.1.is_some(), "defauld parameters should be used after all positionals");
}
+ expr::ParamsDesc(params)
}
- / named:(named_param() ** delimiter()) {expr::ParamsDesc(named)}
- / {expr::ParamsDesc(Vec::new())}
+ / { expr::ParamsDesc(Vec::new()) }
- pub rule positional_arg() -> expr::Arg = quiet!{name:boxed_expr() {expr::Arg::Positional(name)}}/expected!("<positional arg>")
- pub rule named_arg() -> expr::Arg = quiet!{name:id() __() "=" __() expr:boxed_expr() {expr::Arg::Named(name, expr)}}/expected!("<named arg>")
+ pub rule arg() -> expr::Arg
+ = name:id() _ "=" _ expr:boxed_expr() {expr::Arg(Some(name), expr)}
+ / expr:boxed_expr() {expr::Arg(None, expr)}
pub rule args() -> expr::ArgsDesc
- = positionals:(positional_arg() ** delimiter()) named: (delimiter() named:(named_arg() ** delimiter()) {named})? {
- if named.is_some() {
- expr::ArgsDesc([&positionals[..], &named.unwrap()[..]].concat())
- } else {
- expr::ArgsDesc(positionals)
+ = args:arg() ** comma() comma()? {
+ let mut named_started = false;
+ for arg in &args {
+ named_started = named_started || arg.0.is_some();
+ assert_eq!(named_started, arg.0.is_some(), "named args should be used after all positionals");
}
+ expr::ArgsDesc(args)
}
- / named:(named_arg() ** delimiter()) {expr::ArgsDesc(named)}
- / {expr::ArgsDesc(Vec::new())}
+ / { expr::ArgsDesc(Vec::new()) }
- pub rule bind() -> expr::Bind
- = name:id() __() "=" __() expr:boxed_expr() {expr::Bind::Value(name, expr)}
- / name:id() __() "(" __() params:params() __() ")" __() "=" __() expr:boxed_expr() {expr::Bind::Function(name, params, expr)}
- pub rule assertion() -> expr::AssertStmt = "assert" _() cond:boxed_expr() msg:(__() ":" __() e:boxed_expr() {e})? { expr::AssertStmt(cond, msg) }
+ pub rule bind() -> expr::BindSpec
+ = name:id() _ "=" _ expr:boxed_expr() {expr::BindSpec{name, params: None, value: expr}}
+ / name:id() _ "(" _ params:params() _ ")" _ "=" _ expr:boxed_expr() {expr::BindSpec{name, params: Some(params), value: expr}}
+ pub rule assertion() -> expr::AssertStmt = keyword("assert") _ cond:boxed_expr() msg:(_ ":" _ e:boxed_expr() {e})? { expr::AssertStmt(cond, msg) }
pub rule string() -> String
- = "\"" str:$((!['"'][_])*) "\"" {str.to_owned()}
+ = "\"" str:$(("\\\"" / !['"'][_])*) "\"" {str.to_owned()}
/ "'" str:$((!['\''][_])*) "'" {str.to_owned()}
pub rule field_name() -> expr::FieldName
= name:id() {expr::FieldName::Fixed(name)}
/ name:string() {expr::FieldName::Fixed(name)}
- / "[" __() expr:boxed_expr() __() "]" {expr::FieldName::Dyn(expr)}
+ / "[" _ expr:boxed_expr() _ "]" {expr::FieldName::Dyn(expr)}
pub rule visibility() -> expr::Visibility
= ":::" {expr::Visibility::Unhide}
/ "::" {expr::Visibility::Hidden}
/ ":" {expr::Visibility::Normal}
pub rule field() -> expr::FieldMember
- = name:field_name() __() plus:"+"? __() visibility:visibility() __() value:expr() {expr::FieldMember::Value{
+ = name:field_name() _ plus:"+"? _ visibility:visibility() _ value:expr() {expr::FieldMember{
name,
plus: plus.is_some(),
+ params: None,
visibility,
value,
}}
- / name:field_name() __() "(" __() params:params() __() ")" __() visibility:visibility() __() value:expr() {expr::FieldMember::Function{
+ / name:field_name() _ "(" _ params:params() _ ")" _ visibility:visibility() _ value:expr() {expr::FieldMember{
name,
- params,
+ plus: false,
+ params: Some(params),
visibility,
value,
}}
+ pub rule obj_local() -> BindSpec
+ = keyword("local") _ bind:bind() {bind}
pub rule member() -> expr::Member
- = "local" _() bind:bind() {expr::Member::BindStmt(bind)}
+ = bind:obj_local() {expr::Member::BindStmt(bind)}
/ assertion:assertion() {expr::Member::AssertStmt(assertion)}
/ field:field() {expr::Member::Field(field)}
- pub rule obj_body() -> expr::ObjBody = members:(member() ** delimiter()) delimiter()? {expr::ObjBody::MemberList(members)}
- pub rule ifspec() -> expr::IfSpec = "if" _() expr:boxed_expr() {expr::IfSpec(expr)}
- pub rule forspec() -> expr::ForSpec = "for" _() id:id() _() "in" _() ifs:ifspec()* {expr::ForSpec(id, ifs)}
+ pub rule objinside() -> expr::ObjBody
+ = pre_locals:(b: obj_local() comma() {b})* "[" _ key:boxed_expr() _ "]" _ ":" _ value:boxed_expr() post_locals:(comma() b:obj_local() {b})* _ first:forspec() rest:(_ rest:compspec() {rest})? {
+ expr::ObjBody::ObjComp {
+ pre_locals,
+ key,
+ value,
+ post_locals,
+ first,
+ rest: rest.unwrap_or(Vec::new()),
+ }
+ }
+ / members:(member() ** comma()) comma()? {expr::ObjBody::MemberList(members)}
+ pub rule ifspec() -> IfSpecData = keyword("if") _ expr:boxed_expr() {IfSpecData(expr)}
+ pub rule forspec() -> ForSpecData = keyword("for") _ id:id() _ keyword("in") _ cond:boxed_expr() {ForSpecData(id, cond)}
+ pub rule compspec() -> Vec<expr::CompSpec> = s:(i:ifspec() { expr::CompSpec::IfSpec(i) } / f:forspec() {expr::CompSpec::ForSpec(f)} )+ {s}
pub rule bind_expr() -> Expr = bind:bind() {Expr::Bind(bind)}
- pub rule local_expr() -> Expr = "local" _() binds:(bind() ** delimiter()) __() ";" __() expr:boxed_expr() { Expr::LocalExpr(binds, expr) }
+ pub rule local_expr() -> Expr = keyword("local") _ binds:bind() ** comma() _ ";" _ expr:boxed_expr() { Expr::LocalExpr(binds, expr) }
pub rule string_expr() -> Expr = s:string() {Expr::Str(s)}
pub rule parened_expr() -> Expr = "(" e:boxed_expr() ")" {Expr::Parened(e)}
- pub rule obj_expr() -> Expr = "{" __() body:obj_body() __() "}" {Expr::Obj(body)}
- pub rule array_expr() -> Expr = "[" __() elems:(expr() ** delimiter()) __() delimiter()? "]" {Expr::Arr(elems)}
- pub rule array_comp_expr() -> Expr = "[" __() expr:boxed_expr() delimiter()? fors:forspec()+ __() "]" {Expr::ArrComp(expr, fors)}
+ pub rule obj_expr() -> Expr = "{" _ body:objinside() _ "}" {Expr::Obj(body)}
+ pub rule array_expr() -> Expr = "[" _ elems:(expr() ** comma()) _ comma()? "]" {Expr::Arr(elems)}
+ pub rule array_comp_expr() -> Expr = "[" _ expr:boxed_expr() _ comma()? _ forspec:forspec() _ others:(others: compspec() _ {others})? "]" {Expr::ArrComp(expr, forspec, others.unwrap_or(vec![]))}
pub rule index_expr() -> Expr
= val:boxed_expr() "." idx:id() {Expr::Index(val, Box::new(Expr::Str(idx)))}
/ val:boxed_expr() "[" key:boxed_expr() "]" {Expr::Index(val, key)}
- pub rule slice_expr() -> Expr
- = value:boxed_expr() "[" start:boxed_expr()? ":" pair:(end:boxed_expr()? step:(":" e:boxed_expr() {e})? {(end, step)})? "]" {
- if let Some((end, step)) = pair {
- Expr::Slice { value, start, end, step }
- }else{
- Expr::Slice{ value, start, end: None, step: None }
- }
- }
pub rule number_expr() -> Expr = n:number() { expr::Expr::Num(n) }
pub rule var_expr() -> Expr = n:id() { expr::Expr::Var(n) }
- pub rule if_then_else_expr() -> Expr = cond:ifspec() _() "then" _() cond_then:boxed_expr() cond_else:(_() "else" _() e:boxed_expr() {e})? {Expr::IfElse{
+ pub rule if_then_else_expr() -> Expr = cond:ifspec() _ keyword("then") _ cond_then:boxed_expr() cond_else:(_ keyword("else") _ e:boxed_expr() {e})? {Expr::IfElse{
cond,
cond_then,
cond_else,
}}
+
+ pub rule literal() -> Expr
+ = v:(
+ keyword("null") {LiteralType::Null}
+ / keyword("true") {LiteralType::True}
+ / keyword("false") {LiteralType::False}
+ / keyword("self") {LiteralType::This}
+ / keyword("$") {LiteralType::Dollar}
+ / keyword("super") {LiteralType::Super}
+ ) {Expr::Literal(v)}
+
pub rule expr_basic() -> Expr
- = "null" {Expr::Value(ValueType::Null)}
- / "true" {Expr::Value(ValueType::True)} / "false" {Expr::Value(ValueType::False)}
+ = literal()
- / "self" {Expr::Literal(LiteralType::This)} / "$" {Expr::Literal(LiteralType::Dollar)}
- / "super" {Expr::Literal(LiteralType::Super)}
-
/ string_expr() / number_expr()
/ array_expr()
- / array_comp_expr()
/ obj_expr()
/ array_expr()
/ array_comp_expr()
/ var_expr()
+ / local_expr()
/ if_then_else_expr()
- / local_expr()
+ / "-" _ expr:boxed_expr() { Expr::UnaryOp(UnaryOpType::Minus, expr) }
+ / "!" _ expr:boxed_expr() { Expr::UnaryOp(UnaryOpType::Not, expr) }
- / "function" __() "(" __() params:params() __() ")" __() expr:boxed_expr() {Expr::Function(params, expr)}
+ / keyword("function") _ "(" _ params:params() _ ")" _ expr:boxed_expr() {Expr::Function(params, expr)}
+ / assertion:assertion() _ ";" _ expr:boxed_expr() { Expr::AssertExpr(assertion, expr) }
+ / keyword("error") _ expr:boxed_expr() { Expr::Error(expr) }
+
rule expr_basic_with_suffix() -> Expr
- = a:expr_basic() suffixes:(__() suffix:expr_suffix() {suffix})* {
+ = a:expr_basic() suffixes:(_ suffix:expr_suffix() {suffix})* {
let mut cur = a;
for suffix in suffixes {
- match suffix {
- Suffix::String(index) => {
- cur = Expr::Index(Box::new(cur), Box::new(Expr::Str(index)))
- },
- Suffix::Expression(index) => {
- cur = Expr::Index(Box::new(cur), Box::new(index))
- },
- Suffix::Apply(args) => {
- cur = Expr::Apply(Box::new(cur), args)
- }
+ cur = match suffix {
+ Suffix::String(index) => Expr::Index(Box::new(cur), Box::new(Expr::Str(index))),
+ Suffix::Slice(desc) => Expr::Slice(Box::new(cur), desc),
+ Suffix::Expression(index) => Expr::Index(Box::new(cur), Box::new(index)),
+ Suffix::Apply(args) => Expr::Apply(Box::new(cur), args),
+ Suffix::Extend(body) => Expr::ObjExtend(box cur, body),
}
}
cur
}
+ pub rule slice_desc() -> SliceDesc
+ = start:boxed_expr()? _ ":" _ pair:(end:boxed_expr()? _ step:(":" _ e:boxed_expr() {e})? {(end, step)})? {
+ if let Some((end, step)) = pair {
+ SliceDesc { start, end, step }
+ }else{
+ SliceDesc { start, end: None, step: None }
+ }
+ }
+
rule expr_suffix() -> Suffix
- = "." __() s:id() { Suffix::String(s) }
- / "[" __() s:expr() __() "]" { Suffix::Expression(s) }
- / "(" __() args:args() __() ")" { Suffix::Apply(args) }
+ = "." _ s:id() { Suffix::String(s) }
+ / "[" _ s:slice_desc() _ "]" { Suffix::Slice(s) }
+ / "[" _ s:expr() _ "]" { Suffix::Expression(s) }
+ / "(" _ args:args() _ ")" (_ keyword("tailstrict"))? { Suffix::Apply(args) }
+ / "{" _ body:objinside() _ "}" { Suffix::Extend(body) }
rule expr() -> Expr
= a:precedence! {
- a:(@) __() "||" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::Or, Box::new(b))}
+ a:(@) _ "||" _ b:@ {Expr::BinaryOp(Box::new(a), BinaryOpType::Or, Box::new(b))}
--
- a:(@) __() "&&" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::And, Box::new(b))}
+ a:(@) _ "&&" _ b:@ {Expr::BinaryOp(Box::new(a), BinaryOpType::And, Box::new(b))}
--
- a:(@) __() "|" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::BitOr, Box::new(b))}
+ a:(@) _ "|" _ b:@ {Expr::BinaryOp(Box::new(a), BinaryOpType::BitOr, Box::new(b))}
--
- a:@ __() "^" __() b:(@) {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::BitXor, Box::new(b))}
+ a:@ _ "^" _ b:(@) {Expr::BinaryOp(Box::new(a), BinaryOpType::BitXor, Box::new(b))}
--
- a:(@) __() "&" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::BitAnd, Box::new(b))}
+ a:(@) _ "&" _ b:@ {Expr::BinaryOp(Box::new(a), BinaryOpType::BitAnd, Box::new(b))}
--
- a:(@) __() "==" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::Eq, Box::new(b))}
- a:(@) __() "!=" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::Ne, Box::new(b))}
+ a:(@) _ "==" _ b:@ {Expr::BinaryOp(Box::new(a), BinaryOpType::Eq, Box::new(b))}
+ a:(@) _ "!=" _ b:@ {Expr::BinaryOp(Box::new(a), BinaryOpType::Ne, Box::new(b))}
--
- a:(@) __() "<" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::Lt, Box::new(b))}
- a:(@) __() ">" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::Gt, Box::new(b))}
- a:(@) __() "<=" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::Lte, Box::new(b))}
- a:(@) __() ">=" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::Gte, Box::new(b))}
+ a:(@) _ "<" _ b:@ {Expr::BinaryOp(Box::new(a), BinaryOpType::Lt, Box::new(b))}
+ a:(@) _ ">" _ b:@ {Expr::BinaryOp(Box::new(a), BinaryOpType::Gt, Box::new(b))}
+ a:(@) _ "<=" _ b:@ {Expr::BinaryOp(Box::new(a), BinaryOpType::Lte, Box::new(b))}
+ a:(@) _ ">=" _ b:@ {Expr::BinaryOp(Box::new(a), BinaryOpType::Gte, Box::new(b))}
--
- a:(@) __() "<<" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::Lhs, Box::new(b))}
- a:(@) __() ">>" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::Rhs, Box::new(b))}
+ a:(@) _ "<<" _ b:@ {Expr::BinaryOp(Box::new(a), BinaryOpType::Lhs, Box::new(b))}
+ a:(@) _ ">>" _ b:@ {Expr::BinaryOp(Box::new(a), BinaryOpType::Rhs, Box::new(b))}
--
- a:(@) __() "+" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::Add, Box::new(b))}
- a:(@) __() "-" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::Sub, Box::new(b))}
+ a:(@) _ "+" _ b:@ {Expr::BinaryOp(Box::new(a), BinaryOpType::Add, Box::new(b))}
+ a:(@) _ "-" _ b:@ {Expr::BinaryOp(Box::new(a), BinaryOpType::Sub, Box::new(b))}
--
- a:(@) __() "*" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::Mul, Box::new(b))}
- a:(@) __() "/" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::Div, Box::new(b))}
- a:(@) __() "%" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::Mod, Box::new(b))}
+ a:(@) _ "*" _ b:@ {Expr::BinaryOp(Box::new(a), BinaryOpType::Mul, Box::new(b))}
+ a:(@) _ "/" _ b:@ {Expr::BinaryOp(Box::new(a), BinaryOpType::Div, Box::new(b))}
+ a:(@) _ "%" _ b:@ {Expr::BinaryOp(Box::new(a), BinaryOpType::Mod, Box::new(b))}
--
e:expr_basic_with_suffix() {e}
- "(" __() e:boxed_expr() __() ")" {Expr::Parened(e)}
+ "(" _ e:boxed_expr() _ ")" {Expr::Parened(e)}
}
/ e:expr_basic_with_suffix() {e}
pub rule boxed_expr() -> Box<Expr> = e:expr() {Box::new(e)}
- pub rule jsonnet() -> Expr = __() e:expr() __() {e}
+ pub rule jsonnet() -> Expr = _ e:expr() _ {e}
}
}
@@ -220,7 +260,29 @@
);
}
+ /// Comments should not affect parsing
#[test]
+ fn comments() {
+ assert_eq!(
+ parse("2//comment\n+//comment\n3/*test*/*/*test*/4").unwrap(),
+ Expr::BinaryOp(
+ box Expr::Num(2.0),
+ BinaryOpType::Add,
+ box Expr::BinaryOp(box Expr::Num(3.0), BinaryOpType::Mul, box Expr::Num(4.0))
+ )
+ );
+ }
+
+ /// Comments should be able to be escaped
+ #[test]
+ fn comment_escaping() {
+ assert_eq!(
+ parse("2/*\\*/+*/ - 22").unwrap(),
+ Expr::BinaryOp(box Expr::Num(2.0), BinaryOpType::Sub, box Expr::Num(22.0))
+ );
+ }
+
+ #[test]
fn suffix_comparsion() {
use Expr::*;
assert_eq!(
@@ -228,11 +290,70 @@
BinaryOp(
box Apply(
box Index(box Var("std".to_owned()), box Str("type".to_owned())),
- ArgsDesc(vec![Arg::Positional(box Var("a".to_owned()))])
+ ArgsDesc(vec![Arg(None, box Var("a".to_owned()))])
),
BinaryOpType::Eq,
box Str("string".to_owned())
)
);
}
+
+ #[test]
+ fn array_comp() {
+ use Expr::*;
+ assert_eq!(
+ parse("[std.deepJoin(x) for x in arr]").unwrap(),
+ ArrComp(
+ box Apply(
+ box Index(box Var("std".to_owned()), box Str("deepJoin".to_owned())),
+ ArgsDesc(vec![Arg(None, box Var("x".to_owned()))])
+ ),
+ ForSpecData("x".to_owned(), box Var("arr".to_owned())),
+ vec![]
+ ),
+ )
+ }
+
+ #[test]
+ fn array_comp_with_ifs() {
+ use Expr::*;
+ assert_eq!(
+ parse("[k for k in std.objectFields(patch) if patch[k] == null]").unwrap(),
+ ArrComp(
+ box Var("k".to_owned()),
+ ForSpecData(
+ "k".to_owned(),
+ box Apply(
+ box Index(
+ box Var("std".to_owned()),
+ box Str("objectFields".to_owned())
+ ),
+ ArgsDesc(vec![Arg(None, box Var("patch".to_owned()))])
+ )
+ ),
+ vec![CompSpec::IfSpec(IfSpecData(box BinaryOp(
+ box Index(box Var("patch".to_owned()), box Var("k".to_owned())),
+ BinaryOpType::Eq,
+ box Literal(LiteralType::Null)
+ )))]
+ ),
+ );
+ }
+
+ #[test]
+ fn reserved() {
+ use Expr::*;
+ assert_eq!(parse("null").unwrap(), Literal(LiteralType::Null));
+ assert_eq!(parse("nulla").unwrap(), Var("nulla".to_owned()));
+ }
+
+ #[test]
+ fn multiple_args_buf() {
+ parse("a(b, null_fields)").unwrap();
+ }
+
+ #[test]
+ fn can_parse_stdlib() {
+ parse(jsonnet_stdlib::STDLIB_STR).unwrap();
+ }
}