git.delta.rocks / jrsonnet / refs/commits / f9925f61b616

difftreelog

fix(parser) desugar == to std.equals

Лач2020-06-04parent: #8c59188.patch.diff
in: master

4 files changed

modifiedcrates/jsonnet-evaluator/src/evaluate.rsdiffbeforeafterboth
--- a/crates/jsonnet-evaluator/src/evaluate.rs
+++ b/crates/jsonnet-evaluator/src/evaluate.rs
@@ -5,9 +5,8 @@
 };
 use closure::closure;
 use jsonnet_parser::{
-	el, Arg, ArgsDesc, AssertStmt, BinaryOpType, BindSpec, CompSpec, Expr, FieldMember,
-	ForSpecData, IfSpecData, LiteralType, LocExpr, Member, ObjBody, ParamsDesc, UnaryOpType,
-	Visibility,
+	ArgsDesc, AssertStmt, BinaryOpType, BindSpec, CompSpec, Expr, FieldMember, ForSpecData,
+	IfSpecData, LiteralType, LocExpr, Member, ObjBody, ParamsDesc, UnaryOpType, Visibility,
 };
 use std::{
 	collections::{BTreeMap, HashMap},
@@ -102,55 +101,20 @@
 		match (evaluate(context.clone(), &a)?.unwrap_if_lazy()?, op, b) {
 			(Val::Bool(true), BinaryOpType::Or, _o) => Val::Bool(true),
 			(Val::Bool(false), BinaryOpType::And, _o) => Val::Bool(false),
-			(a, op, eb) => evaluate_binary_op_normal(
-				context.clone(),
-				&a,
-				op,
-				&evaluate(context, eb)?.unwrap_if_lazy()?,
-			)?,
+			(a, op, eb) => {
+				evaluate_binary_op_normal(&a, op, &evaluate(context, eb)?.unwrap_if_lazy()?)?
+			}
 		},
 	)
 }
 
-pub fn evaluate_binary_op_normal(
-	context: Context,
-	a: &Val,
-	op: BinaryOpType,
-	b: &Val,
-) -> Result<Val> {
+pub fn evaluate_binary_op_normal(a: &Val, op: BinaryOpType, b: &Val) -> Result<Val> {
 	Ok(match (a, op, b) {
 		(a, BinaryOpType::Add, b) => evaluate_add_op(a, b)?,
-
-		(Val::Str(v1), BinaryOpType::Eq, Val::Str(v2)) => Val::Bool(v1 == v2),
-		(Val::Str(v1), BinaryOpType::Ne, Val::Str(v2)) => Val::Bool(v1 != v2),
 
 		(Val::Str(v1), BinaryOpType::Mul, Val::Num(v2)) => Val::Str(v1.repeat(*v2 as usize)),
-		(Val::Str(format), BinaryOpType::Mod, args) => evaluate(
-			context
-				.with_var("__tmp__format__".to_owned(), Val::Str(format.to_owned()))?
-				.with_var(
-					"__tmp__args__".to_owned(),
-					match args {
-						Val::Arr(v) => Val::Arr(v.clone()),
-						v => Val::Arr(vec![v.clone()]),
-					},
-				)?,
-			&el!(Expr::Apply(
-				el!(Expr::Index(
-					el!(Expr::Var("std".to_owned())),
-					el!(Expr::Str("format".to_owned()))
-				)),
-				ArgsDesc(vec![
-					Arg(None, el!(Expr::Var("__tmp__format__".to_owned()))),
-					Arg(None, el!(Expr::Var("__tmp__args__".to_owned())))
-				])
-			)),
-		)?,
 
 		// Bool X Bool
-		(Val::Bool(a), BinaryOpType::Eq, Val::Bool(b)) => Val::Bool(a == b),
-		(Val::Bool(a), BinaryOpType::Ne, Val::Bool(b)) => Val::Bool(a != b),
-
 		(Val::Bool(a), BinaryOpType::And, Val::Bool(b)) => Val::Bool(*a && *b),
 		(Val::Bool(a), BinaryOpType::Or, Val::Bool(b)) => Val::Bool(*a || *b),
 
@@ -163,7 +127,6 @@
 		// Num X Num
 		(Val::Num(v1), BinaryOpType::Mul, Val::Num(v2)) => Val::Num(v1 * v2),
 		(Val::Num(v1), BinaryOpType::Div, Val::Num(v2)) => Val::Num(v1 / v2),
-		(Val::Num(v1), BinaryOpType::Mod, Val::Num(v2)) => Val::Num(v1 % v2),
 
 		(Val::Num(v1), BinaryOpType::Sub, Val::Num(v2)) => Val::Num(v1 - v2),
 
@@ -171,9 +134,6 @@
 		(Val::Num(v1), BinaryOpType::Gt, Val::Num(v2)) => Val::Bool(v1 > v2),
 		(Val::Num(v1), BinaryOpType::Lte, Val::Num(v2)) => Val::Bool(v1 <= v2),
 		(Val::Num(v1), BinaryOpType::Gte, Val::Num(v2)) => Val::Bool(v1 >= v2),
-
-		(Val::Num(v1), BinaryOpType::Eq, Val::Num(v2)) => Val::Bool((v1 - v2).abs() < f64::EPSILON),
-		(Val::Num(v1), BinaryOpType::Ne, Val::Num(v2)) => Val::Bool((v1 - v2).abs() > f64::EPSILON),
 
 		(Val::Num(v1), BinaryOpType::BitAnd, Val::Num(v2)) => {
 			Val::Num(((*v1 as i32) & (*v2 as i32)) as f64)
@@ -191,28 +151,6 @@
 			Val::Num(((*v1 as i32) >> (*v2 as i32)) as f64)
 		}
 
-		// Arr X Arr
-		(Val::Arr(a), BinaryOpType::Eq, Val::Arr(b)) => {
-			if a.len() != b.len() {
-				Val::Bool(false)
-			} else {
-				for i in 0..a.len() {
-					if let Val::Bool(v) = evaluate_binary_op_normal(
-						context.clone(),
-						&a[i].clone().unwrap_if_lazy()?,
-						op,
-						&b[i].clone().unwrap_if_lazy()?,
-					)? {
-						if !v {
-							return Ok(Val::Bool(false));
-						}
-					} else {
-						unreachable!()
-					}
-				}
-				return Ok(Val::Bool(true));
-			}
-		}
 		_ => panic!("no rules for binary operation: {:?} {:?} {:?}", a, op, b),
 	})
 }
@@ -385,7 +323,7 @@
 		Index(value, index) => {
 			match (
 				evaluate(context.clone(), value)?.unwrap_if_lazy()?,
-				evaluate(context.clone(), index)?,
+				evaluate(context, index)?,
 			) {
 				(Val::Obj(v), Val::Str(s)) => {
 					if let Some(v) = v.get(&s)? {
@@ -506,9 +444,17 @@
 							panic!("bad objectFieldsEx call");
 						}
 					}
+					("std", "primitiveEquals") => {
+						assert_eq!(args.len(), 2);
+						let (a, b) = (
+							evaluate(context.clone(), &args[0].1)?,
+							evaluate(context, &args[1].1)?,
+						);
+						Val::Bool(a == b)
+					}
 					(ns, name) => panic!("Intristic not found: {}.{}", ns, name),
 				},
-				Val::Func(f) => push(locexpr.clone(), "function call".to_owned(), || {
+				Val::Func(f) => push(locexpr, "function call".to_owned(), || {
 					f.evaluate(
 						args.clone()
 							.into_iter()
@@ -528,10 +474,11 @@
 		}
 		Function(params, body) => evaluate_method(context, body, params.clone()),
 		AssertExpr(AssertStmt(value, msg), returned) => {
-			if push(value.clone(), "assertion condition".to_owned(), || {
+			let assertion_result = push(value.clone(), "assertion condition".to_owned(), || {
 				evaluate(context.clone(), &value)?
 					.try_cast_bool("assertion condition should be boolean")
-			})? {
+			})?;
+			if assertion_result {
 				push(
 					returned.clone(),
 					"assert 'return' branch".to_owned(),
@@ -555,9 +502,10 @@
 			cond_then,
 			cond_else,
 		} => {
-			if push(cond.0.clone(), "if condition".to_owned(), || {
+			let condition_result = push(cond.0.clone(), "if condition".to_owned(), || {
 				evaluate(context.clone(), &cond.0)?.try_cast_bool("if condition should be boolean")
-			})? {
+			})?;
+			if condition_result {
 				push(
 					cond_then.clone(),
 					"if condition 'then' branch".to_owned(),
modifiedcrates/jsonnet-parser/Cargo.tomldiffbeforeafterboth
--- a/crates/jsonnet-parser/Cargo.toml
+++ b/crates/jsonnet-parser/Cargo.toml
@@ -7,7 +7,7 @@
 [features]
 default = []
 # Trace peg token parsing
-trace = ["peg/trace"]
+# trace = ["peg/trace"]
 # TODO:
 # serialize = ["serde"]
 
modifiedcrates/jsonnet-parser/src/expr.rsdiffbeforeafterboth
--- a/crates/jsonnet-parser/src/expr.rs
+++ b/crates/jsonnet-parser/src/expr.rs
@@ -50,7 +50,7 @@
 pub enum BinaryOpType {
 	Mul,
 	Div,
-	// Mod is desugared to stdlib
+	// Mod is desugared to std.mod
 	// Mod,
 	Add,
 	Sub,
@@ -65,8 +65,9 @@
 
 	In,
 
-	Eq,
-	Ne,
+	// Eq/Ne is desugared to std.equals
+	// Eq,
+	// Ne,
 
 	BitAnd,
 	BitOr,
modifiedcrates/jsonnet-parser/src/lib.rsdiffbeforeafterboth
before · crates/jsonnet-parser/src/lib.rs
1#![feature(box_syntax)]2#![feature(test)]34extern crate test;56use peg::parser;7use std::rc::Rc;8mod expr;9pub use expr::*;1011enum Suffix {12	String(String),13	Slice(SliceDesc),14	Expression(LocExpr),15	Apply(expr::ArgsDesc),16	Extend(expr::ObjBody),17}18struct LocSuffix(Suffix, ExprLocation);1920pub struct ParserSettings {21	pub loc_data: bool,22	pub file_name: String,23}2425parser! {26	grammar jsonnet_parser() for str {27		use peg::ParseLiteral;2829		/// Standard C-like comments30		rule comment()31			= "//" (!['\n'][_])* "\n"32			/ "/*" ((!("*/")[_][_])/("\\" "*/"))* "*/"33			/ "#" (!['\n'][_])* "\n"3435		rule _() = ([' ' | '\n' | '\t'] / comment())*3637		/// For comma-delimited elements38		rule comma() = quiet!{_ "," _} / expected!("<comma>")39		rule alpha() -> char = c:$(['_' | 'a'..='z' | 'A'..='Z']) {c.chars().next().unwrap()}40		rule digit() -> char = d:$(['0'..='9']) {d.chars().next().unwrap()}41		rule end_of_ident() = !['0'..='9' | '_' | 'a'..='z' | 'A'..='Z']42		/// Sequence of digits43		rule uint() -> u32 = a:$(digit()+) { a.parse().unwrap() }44		/// Number in scientific notation format45		rule number() -> f64 = quiet!{a:$(uint() ("." uint())? (['e'|'E'] (s:['+'|'-'])? uint())?) { a.parse().unwrap() }} / expected!("<number>")4647		/// Reserved word followed by any non-alphanumberic48		rule reserved() = ("assert" / "else" / "error" / "false" / "for" / "function" / "if" / "import" / "importstr" / "in" / "local" / "null" / "tailstrict" / "then" / "self" / "super" / "true") end_of_ident()49		rule id() -> String = quiet!{ !reserved() s:$(alpha() (alpha() / digit())*) {s.to_owned()}} / expected!("<identifier>")5051		rule keyword(id: &'static str)52			= ##parse_string_literal(id) end_of_ident()53		// Adds location data information to existing expression54		rule l(s: &ParserSettings, x: rule<Expr>) -> LocExpr55			= start:position!() v:x() end:position!() {loc_expr!(v, s.loc_data, (s.file_name.clone(), start, end))}5657		pub rule param(s: &ParserSettings) -> expr::Param = name:id() expr:(_ "=" _ expr:expr(s){expr})? { expr::Param(name, expr) }58		pub rule params(s: &ParserSettings) -> expr::ParamsDesc59			= params:(param(s) ** comma()) {60				let mut defaults_started = false;61				for param in &params {62					defaults_started = defaults_started || param.1.is_some();63					assert_eq!(defaults_started, param.1.is_some(), "defauld parameters should be used after all positionals");64				}65				expr::ParamsDesc(params)66			}67			/ { expr::ParamsDesc(Vec::new()) }6869		pub rule arg(s: &ParserSettings) -> expr::Arg70			= name:id() _ "=" _ expr:expr(s) {expr::Arg(Some(name), expr)}71			/ expr:expr(s) {expr::Arg(None, expr)}72		pub rule args(s: &ParserSettings) -> expr::ArgsDesc73			= args:arg(s) ** comma() comma()? {74				let mut named_started = false;75				for arg in &args {76					named_started = named_started || arg.0.is_some();77					assert_eq!(named_started, arg.0.is_some(), "named args should be used after all positionals");78				}79				expr::ArgsDesc(args)80			}81			/ { expr::ArgsDesc(Vec::new()) }8283		pub rule bind(s: &ParserSettings) -> expr::BindSpec84			= name:id() _ "=" _ expr:expr(s) {expr::BindSpec{name, params: None, value: expr}}85			/ name:id() _ "(" _ params:params(s) _ ")" _ "=" _ expr:expr(s) {expr::BindSpec{name, params: Some(params), value: expr}}86		pub rule assertion(s: &ParserSettings) -> expr::AssertStmt87			= keyword("assert") _ cond:expr(s) msg:(_ ":" _ e:expr(s) {e})? { expr::AssertStmt(cond, msg) }88		pub rule string() -> String89			= v:("\"" str:$(("\\\"" / !['"'][_])*) "\"" {str.to_owned()}90			/ "'" str:$((!['\''][_])*) "'" {str.to_owned()}) {v.replace("\\n", "\n")}91		pub rule field_name(s: &ParserSettings) -> expr::FieldName92			= name:id() {expr::FieldName::Fixed(name)}93			/ name:string() {expr::FieldName::Fixed(name)}94			/ "[" _ expr:expr(s) _ "]" {expr::FieldName::Dyn(expr)}95		pub rule visibility() -> expr::Visibility96			= ":::" {expr::Visibility::Unhide}97			/ "::" {expr::Visibility::Hidden}98			/ ":" {expr::Visibility::Normal}99		pub rule field(s: &ParserSettings) -> expr::FieldMember100			= name:field_name(s) _ plus:"+"? _ visibility:visibility() _ value:expr(s) {expr::FieldMember{101				name,102				plus: plus.is_some(),103				params: None,104				visibility,105				value,106			}}107			/ name:field_name(s) _ "(" _ params:params(s) _ ")" _ visibility:visibility() _ value:expr(s) {expr::FieldMember{108				name,109				plus: false,110				params: Some(params),111				visibility,112				value,113			}}114		pub rule obj_local(s: &ParserSettings) -> BindSpec115			= keyword("local") _ bind:bind(s) {bind}116		pub rule member(s: &ParserSettings) -> expr::Member117			= bind:obj_local(s) {expr::Member::BindStmt(bind)}118			/ assertion:assertion(s) {expr::Member::AssertStmt(assertion)}119			/ field:field(s) {expr::Member::Field(field)}120		pub rule objinside(s: &ParserSettings) -> expr::ObjBody121			= pre_locals:(b: obj_local(s) comma() {b})* "[" _ key:expr(s) _ "]" _ ":" _ value:expr(s) post_locals:(comma() b:obj_local(s) {b})* _ first:forspec(s) rest:(_ rest:compspec(s) {rest})? {122				expr::ObjBody::ObjComp {123					pre_locals,124					key,125					value,126					post_locals,127					first,128					rest: rest.unwrap_or_default(),129				}130			}131			/ members:(member(s) ** comma()) comma()? {expr::ObjBody::MemberList(members)}132		pub rule ifspec(s: &ParserSettings) -> IfSpecData133			= keyword("if") _ expr:expr(s) {IfSpecData(expr)}134		pub rule forspec(s: &ParserSettings) -> ForSpecData135			= keyword("for") _ id:id() _ keyword("in") _ cond:expr(s) {ForSpecData(id, cond)}136		pub rule compspec(s: &ParserSettings) -> Vec<expr::CompSpec>137			= s:(i:ifspec(s) { expr::CompSpec::IfSpec(i) } / f:forspec(s) {expr::CompSpec::ForSpec(f)} ) ** _ {s}138		pub rule local_expr(s: &ParserSettings) -> LocExpr139			= l(s,<keyword("local") _ binds:bind(s) ** comma() _ ";" _ expr:expr(s) { Expr::LocalExpr(binds, expr) }>)140		pub rule string_expr(s: &ParserSettings) -> LocExpr141			= l(s, <s:string() {Expr::Str(s)}>)142		pub rule obj_expr(s: &ParserSettings) -> LocExpr143			= l(s,<"{" _ body:objinside(s) _ "}" {Expr::Obj(body)}>)144		pub rule array_expr(s: &ParserSettings) -> LocExpr145			= l(s,<"[" _ elems:(expr(s) ** comma()) _ comma()? "]" {Expr::Arr(elems)}>)146		pub rule array_comp_expr(s: &ParserSettings) -> LocExpr147			= l(s,<"[" _ expr:expr(s) _ comma()? _ forspec:forspec(s) _ others:(others: compspec(s) _ {others})? "]" {Expr::ArrComp(expr, [vec![CompSpec::ForSpec(forspec)], others.unwrap_or_default()].concat())}>)148		pub rule number_expr(s: &ParserSettings) -> LocExpr149			= l(s,<n:number() { expr::Expr::Num(n) }>)150		pub rule var_expr(s: &ParserSettings) -> LocExpr151			= l(s,<n:id() { expr::Expr::Var(n) }>)152		pub rule if_then_else_expr(s: &ParserSettings) -> LocExpr153			= l(s,<cond:ifspec(s) _ keyword("then") _ cond_then:expr(s) cond_else:(_ keyword("else") _ e:expr(s) {e})? {Expr::IfElse{154				cond,155				cond_then,156				cond_else,157			}}>)158159		pub rule literal(s: &ParserSettings) -> LocExpr160			= l(s,<v:(161				keyword("null") {LiteralType::Null}162				/ keyword("true") {LiteralType::True}163				/ keyword("false") {LiteralType::False}164				/ keyword("self") {LiteralType::This}165				/ keyword("$") {LiteralType::Dollar}166				/ keyword("super") {LiteralType::Super}167			) {Expr::Literal(v)}>)168169		pub rule expr_basic(s: &ParserSettings) -> LocExpr170			= literal(s)171172			/ string_expr(s) / number_expr(s)173			/ array_expr(s)174			/ obj_expr(s)175			/ array_expr(s)176			/ array_comp_expr(s)177178			/ var_expr(s)179			/ local_expr(s)180			/ if_then_else_expr(s)181182			/ l(s,<keyword("function") _ "(" _ params:params(s) _ ")" _ expr:expr(s) {Expr::Function(params, expr)}>)183			/ l(s,<assertion:assertion(s) _ ";" _ expr:expr(s) { Expr::AssertExpr(assertion, expr) }>)184185			/ l(s,<keyword("error") _ expr:expr(s) { Expr::Error(expr) }>)186187		rule expr_basic_with_suffix(s: &ParserSettings) -> LocExpr188			= a:expr_basic(s) suffixes:(_ suffix:l_expr_suffix(s) {suffix})* {189				let mut cur = a;190				for suffix in suffixes {191					let LocSuffix(suffix, location) = suffix;192					cur = LocExpr(Rc::new(match suffix {193						Suffix::String(index) => Expr::Index(cur, loc_expr!(Expr::Str(index), s.loc_data, (s.file_name.clone(), location.1, location.2))),194						Suffix::Slice(desc) => Expr::Slice(cur, desc),195						Suffix::Expression(index) => Expr::Index(cur, index),196						Suffix::Apply(args) => Expr::Apply(cur, args),197						Suffix::Extend(body) => Expr::ObjExtend(cur, body),198					}), if s.loc_data { Some(Rc::new(location)) } else { None })199				}200				cur201			}202203		pub rule slice_desc(s: &ParserSettings) -> SliceDesc204			= start:expr(s)? _ ":" _ pair:(end:expr(s)? _ step:(":" _ e:expr(s) {e})? {(end, step)})? {205				if let Some((end, step)) = pair {206					SliceDesc { start, end, step }207				}else{208					SliceDesc { start, end: None, step: None }209				}210			}211212		rule expr_suffix(s: &ParserSettings) -> Suffix213			= "." _ s:id() { Suffix::String(s) }214			/ "[" _ s:slice_desc(s) _ "]" { Suffix::Slice(s) }215			/ "[" _ s:expr(s) _ "]" { Suffix::Expression(s) }216			/ "(" _ args:args(s) _ ")" (_ keyword("tailstrict"))? { Suffix::Apply(args) }217			/ "{" _ body:objinside(s) _ "}" { Suffix::Extend(body) }218		rule l_expr_suffix(s: &ParserSettings) -> LocSuffix219			= start:position!() suffix:expr_suffix(s) end:position!() {LocSuffix(suffix, ExprLocation(s.file_name.clone(), start, end))}220221		rule expr(s: &ParserSettings) -> LocExpr222			= start:position!() a:precedence! {223				a:(@) _ "||" _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, BinaryOpType::Or, b))}224				--225				a:(@) _ "&&" _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, BinaryOpType::And, b))}226				--227				a:(@) _ "|" _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, BinaryOpType::BitOr, b))}228				--229				a:@ _ "^" _ b:(@) {loc_expr_todo!(Expr::BinaryOp(a, BinaryOpType::BitXor, b))}230				--231				a:(@) _ "&" _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, BinaryOpType::BitAnd, b))}232				--233				a:(@) _ "==" _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, BinaryOpType::Eq, b))}234				a:(@) _ "!=" _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, BinaryOpType::Ne, b))}235				--236				a:(@) _ "<" _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, BinaryOpType::Lt, b))}237				a:(@) _ ">" _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, BinaryOpType::Gt, b))}238				a:(@) _ "<=" _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, BinaryOpType::Lte, b))}239				a:(@) _ ">=" _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, BinaryOpType::Gte, b))}240				--241				a:(@) _ "<<" _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, BinaryOpType::Lhs, b))}242				a:(@) _ ">>" _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, BinaryOpType::Rhs, b))}243				--244				a:(@) _ "+" _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, BinaryOpType::Add, b))}245				a:(@) _ "-" _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, BinaryOpType::Sub, b))}246				--247				a:(@) _ "*" _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, BinaryOpType::Mul, b))}248				a:(@) _ "/" _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, BinaryOpType::Div, b))}249				a:(@) _ "%" _ b:@ {loc_expr_todo!(Expr::Apply(250					el!(Expr::Index(251						el!(Expr::Var("std".to_owned())),252						el!(Expr::Str("mod".to_owned()))253					)), ArgsDesc(vec![Arg(None, a), Arg(None, b)])254				))}255				--256						"-" _ b:@ {loc_expr_todo!(Expr::UnaryOp(UnaryOpType::Minus, b))}257						"!" _ b:@ {loc_expr_todo!(Expr::UnaryOp(UnaryOpType::Not, b))}258						"~" _ b:@ { loc_expr_todo!(Expr::UnaryOp(UnaryOpType::BitNot, b)) }259				--260				e:expr_basic_with_suffix(s) {e}261				"(" _ e:expr(s) _ ")" {loc_expr_todo!(Expr::Parened(e))}262			} end:position!() {263				let LocExpr(e, _) = a;264				LocExpr(e, if s.loc_data {265					Some(Rc::new(ExprLocation(s.file_name.to_owned(), start, end)))266				} else {267					None268				})269			}270			/ e:expr_basic_with_suffix(s) {e}271272		pub rule jsonnet(s: &ParserSettings) -> LocExpr = _ e:expr(s) _ {e}273	}274}275276pub fn parse(277	str: &str,278	settings: &ParserSettings,279) -> Result<LocExpr, peg::error::ParseError<peg::str::LineCol>> {280	jsonnet_parser::jsonnet(str, settings)281}282283#[macro_export]284macro_rules! el {285	($expr:expr) => {286		LocExpr(std::rc::Rc::new($expr), None)287	};288}289290#[cfg(test)]291pub mod tests {292	use super::{expr::*, parse};293	use crate::ParserSettings;294295	macro_rules! parse {296		($s:expr) => {297			parse(298				$s,299				&ParserSettings {300					loc_data: false,301					file_name: "test.jsonnet".to_owned(),302					},303				)304			.unwrap()305		};306	}307308	mod expressions {309		use super::*;310311		pub fn basic_math() -> LocExpr {312			el!(Expr::BinaryOp(313				el!(Expr::Num(2.0)),314				BinaryOpType::Add,315				el!(Expr::BinaryOp(316					el!(Expr::Num(2.0)),317					BinaryOpType::Mul,318					el!(Expr::Num(2.0)),319				)),320			))321		}322	}323324	#[test]325	fn empty_object() {326		assert_eq!(parse!("{}"), el!(Expr::Obj(ObjBody::MemberList(vec![]))));327	}328329	#[test]330	fn basic_math() {331		assert_eq!(332			parse!("2+2*2"),333			el!(Expr::BinaryOp(334				el!(Expr::Num(2.0)),335				BinaryOpType::Add,336				el!(Expr::BinaryOp(337					el!(Expr::Num(2.0)),338					BinaryOpType::Mul,339					el!(Expr::Num(2.0))340				))341			))342		);343	}344345	#[test]346	fn basic_math_with_indents() {347		assert_eq!(parse!("2	+ 	  2	  *	2   	"), expressions::basic_math());348	}349350	#[test]351	fn basic_math_parened() {352		assert_eq!(353			parse!("2+(2+2*2)"),354			el!(Expr::BinaryOp(355				el!(Expr::Num(2.0)),356				BinaryOpType::Add,357				el!(Expr::Parened(expressions::basic_math())),358			))359		);360	}361362	/// Comments should not affect parsing363	#[test]364	fn comments() {365		assert_eq!(366			parse!("2//comment\n+//comment\n3/*test*/*/*test*/4"),367			el!(Expr::BinaryOp(368				el!(Expr::Num(2.0)),369				BinaryOpType::Add,370				el!(Expr::BinaryOp(371					el!(Expr::Num(3.0)),372					BinaryOpType::Mul,373					el!(Expr::Num(4.0))374				))375			))376		);377	}378379	/// Comments should be able to be escaped380	#[test]381	fn comment_escaping() {382		assert_eq!(383			parse!("2/*\\*/+*/ - 22"),384			el!(Expr::BinaryOp(385				el!(Expr::Num(2.0)),386				BinaryOpType::Sub,387				el!(Expr::Num(22.0))388			))389		);390	}391392	#[test]393	fn suffix_comparsion() {394		use Expr::*;395		assert_eq!(396			parse!("std.type(a) == \"string\""),397			el!(BinaryOp(398				el!(Apply(399					el!(Index(400						el!(Var("std".to_owned())),401						el!(Str("type".to_owned()))402					)),403					ArgsDesc(vec![Arg(None, el!(Var("a".to_owned())))])404				)),405				BinaryOpType::Eq,406				el!(Str("string".to_owned()))407			))408		);409	}410411	#[test]412	fn array_comp() {413		use Expr::*;414		assert_eq!(415			parse!("[std.deepJoin(x) for x in arr]"),416			el!(ArrComp(417				el!(Apply(418					el!(Index(419						el!(Var("std".to_owned())),420						el!(Str("deepJoin".to_owned()))421					)),422					ArgsDesc(vec![Arg(None, el!(Var("x".to_owned())))])423				)),424				vec![CompSpec::ForSpec(ForSpecData(425					"x".to_owned(),426					el!(Var("arr".to_owned()))427				))]428			)),429		)430	}431432	#[test]433	fn array_comp_with_ifs() {434		use Expr::*;435		assert_eq!(436			parse!("[k for k in std.objectFields(patch) if patch[k] == null]"),437			el!(ArrComp(438				el!(Var("k".to_owned())),439				vec![440					CompSpec::ForSpec(ForSpecData(441						"k".to_owned(),442						el!(Apply(443							el!(Index(444								el!(Var("std".to_owned())),445								el!(Str("objectFields".to_owned()))446							)),447							ArgsDesc(vec![Arg(None, el!(Var("patch".to_owned())))])448						))449					)),450					CompSpec::IfSpec(IfSpecData(el!(BinaryOp(451						el!(Index(452							el!(Var("patch".to_owned())),453							el!(Var("k".to_owned()))454						)),455						BinaryOpType::Eq,456						el!(Literal(LiteralType::Null))457					))))458				]459			))460		);461	}462463	#[test]464	fn reserved() {465		use Expr::*;466		assert_eq!(parse!("null"), el!(Literal(LiteralType::Null)));467		assert_eq!(parse!("nulla"), el!(Var("nulla".to_owned())));468	}469470	#[test]471	fn multiple_args_buf() {472		parse!("a(b, null_fields)");473	}474475	#[test]476	fn infix_precedence() {477		use Expr::*;478		assert_eq!(479			parse!("!a && !b"),480			el!(BinaryOp(481				el!(UnaryOp(UnaryOpType::Not, el!(Var("a".to_owned())))),482				BinaryOpType::And,483				el!(UnaryOp(UnaryOpType::Not, el!(Var("b".to_owned()))))484			))485		);486	}487488	#[test]489	fn infix_precedence_division() {490		use Expr::*;491		assert_eq!(492			parse!("!a / !b"),493			el!(BinaryOp(494				el!(UnaryOp(UnaryOpType::Not, el!(Var("a".to_owned())))),495				BinaryOpType::Div,496				el!(UnaryOp(UnaryOpType::Not, el!(Var("b".to_owned()))))497			))498		);499	}500501	#[test]502	fn double_negation() {503		use Expr::*;504		assert_eq!(505			parse!("!!a"),506			el!(UnaryOp(507				UnaryOpType::Not,508				el!(UnaryOp(UnaryOpType::Not, el!(Var("a".to_owned()))))509			))510		)511	}512513	#[test]514	fn array_test_error() {515		parse!("[a for a in b if c for e in f]");516		//                    ^^^^ failed code517	}518519	#[test]520	fn can_parse_stdlib() {521		parse!(jsonnet_stdlib::STDLIB_STR);522	}523524	use test::Bencher;525526	// From source code527	#[bench]528	fn bench_parse_peg(b: &mut Bencher) {529		b.iter(|| parse!(jsonnet_stdlib::STDLIB_STR))530	}531532	// From serialized blob533	#[bench]534	fn bench_parse_serde_bincode(b: &mut Bencher) {535		let serialized = bincode::serialize(&parse!(jsonnet_stdlib::STDLIB_STR)).unwrap();536		b.iter(|| bincode::deserialize::<LocExpr>(&serialized))537	}538}