git.delta.rocks / jrsonnet / refs/commits / 45767d31ad1f

difftreelog

refactor(parser) make parser parse stdlib successfully

Лач2020-05-29parent: #aee104d.patch.diff
in: master

4 files changed

modifiedcrates/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" }
addedcrates/jsonnet-parser/README.mddiffbeforeafterboth
--- /dev/null
+++ b/crates/jsonnet-parser/README.md
@@ -0,0 +1,3 @@
+# jsonnet-parser
+
+Parser for jsonnet language
modifiedcrates/jsonnet-parser/src/expr.rsdiffbeforeafterboth
--- a/crates/jsonnet-parser/src/expr.rs
+++ b/crates/jsonnet-parser/src/expr.rs
@@ -1,3 +1,5 @@
+use std::fmt::Display;
+
 #[derive(Debug, Clone, PartialEq)]
 pub enum FieldName {
 	/// {fixed: 2}
@@ -20,25 +22,18 @@
 pub struct AssertStmt(pub Box<Expr>, pub Option<Box<Expr>>);
 
 #[derive(Debug, Clone, PartialEq)]
-pub enum FieldMember {
-	Value {
-		name: FieldName,
-		plus: bool,
-		visibility: Visibility,
-		value: Expr,
-	},
-	Function {
-		name: FieldName,
-		params: ParamsDesc,
-		visibility: Visibility,
-		value: Expr,
-	},
+pub struct FieldMember {
+	pub name: FieldName,
+	pub plus: bool,
+	pub params: Option<ParamsDesc>,
+	pub visibility: Visibility,
+	pub value: Expr,
 }
 
 #[derive(Debug, Clone, PartialEq)]
 pub enum Member {
 	Field(FieldMember),
-	BindStmt(Bind),
+	BindStmt(BindSpec),
 	AssertStmt(AssertStmt),
 }
 
@@ -50,7 +45,7 @@
 	Not,
 }
 
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, Copy, PartialEq)]
 pub enum BinaryOpType {
 	Mul,
 	Div,
@@ -80,73 +75,90 @@
 	Or,
 }
 
+/// name, default value
 #[derive(Debug, Clone, PartialEq)]
-pub enum Param {
-	Positional(String),
-	Named(String, Box<Expr>),
-}
-
+pub struct Param(pub String, pub Option<Box<Expr>>);
+/// Defined function parameters
 #[derive(Debug, Clone, PartialEq)]
 pub struct ParamsDesc(pub Vec<Param>);
-
-#[derive(Debug, Clone, PartialEq)]
-pub enum Arg {
-	Positional(Box<Expr>),
-	Named(String, Box<Expr>),
+impl ParamsDesc {
+	pub fn with_defaults(&self) -> Vec<Param> {
+		self.0
+			.iter()
+			.filter(|e| e.1.is_some())
+			.map(|e| e.clone())
+			.collect()
+	}
 }
 
 #[derive(Debug, Clone, PartialEq)]
+pub struct Arg(pub Option<String>, pub Box<Expr>);
+#[derive(Debug, Clone, PartialEq)]
 pub struct ArgsDesc(pub Vec<Arg>);
 
 #[derive(Debug, Clone, PartialEq)]
-pub enum Bind {
-	Value(String, Box<Expr>),
-	Function(String, ParamsDesc, Box<Expr>),
+pub struct BindSpec {
+	pub name: String,
+	pub params: Option<ParamsDesc>,
+	pub value: Box<Expr>,
 }
 
 #[derive(Debug, Clone, PartialEq)]
-pub struct IfSpec(pub Box<Expr>);
+pub struct IfSpecData(pub Box<Expr>);
 #[derive(Debug, Clone, PartialEq)]
-pub struct ForSpec(pub String, pub Vec<IfSpec>);
+pub struct ForSpecData(pub String, pub Box<Expr>);
 
 #[derive(Debug, Clone, PartialEq)]
 pub enum CompSpec {
-	IfSpec(IfSpec),
-	ForSpec(ForSpec),
+	IfSpec(IfSpecData),
+	ForSpec(ForSpecData),
 }
 
 #[derive(Debug, Clone, PartialEq)]
 pub enum ObjBody {
 	MemberList(Vec<Member>),
 	ObjComp {
-		pre_locals: Vec<Bind>,
+		pre_locals: Vec<BindSpec>,
 		key: Box<Expr>,
 		value: Box<Expr>,
-		post_locals: Vec<Bind>,
-		first: ForSpec,
+		post_locals: Vec<BindSpec>,
+		first: ForSpecData,
 		rest: Vec<CompSpec>,
 	},
 }
 
 #[derive(Debug, Clone, PartialEq)]
-pub enum ValueType {
+pub enum LiteralType {
+	This,
+	Super,
+	Dollar,
 	Null,
 	True,
 	False,
 }
+impl Display for LiteralType {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		use LiteralType::*;
+		match self {
+			This => write!(f, "this"),
+			Null => write!(f, "null"),
+			True => write!(f, "true"),
+			False => write!(f, "false"),
+			_ => panic!("non printable item"),
+		}
+	}
+}
 
 #[derive(Debug, Clone, PartialEq)]
-pub enum LiteralType {
-	This,
-	Super,
-	Dollar,
+pub struct SliceDesc {
+	pub start: Option<Box<Expr>>,
+	pub end: Option<Box<Expr>>,
+	pub step: Option<Box<Expr>>,
 }
 
 /// Syntax base
 #[derive(Debug, Clone, PartialEq)]
 pub enum Expr {
-	Value(ValueType),
-	/// Plain value: null/true/false
 	Literal(LiteralType),
 
 	/// String value: "hello"
@@ -169,7 +181,7 @@
 	///    ]
 	///  ],
 	/// ```
-	ArrComp(Box<Expr>, Vec<ForSpec>),
+	ArrComp(Box<Expr>, ForSpecData, Vec<CompSpec>),
 
 	/// Object: {a: 2}
 	Obj(ObjBody),
@@ -179,33 +191,48 @@
 	/// (obj)
 	Parened(Box<Expr>),
 
+	/// Params in function definition
+	/// hello, world, test = 2
 	Params(ParamsDesc),
+	/// Args in function call
+	/// 2 + 2, 3, named = 6
 	Args(ArgsDesc),
 
+	/// -2
 	UnaryOp(UnaryOpType, Box<Expr>),
+	/// 2 - 2
 	BinaryOp(Box<Expr>, BinaryOpType, Box<Expr>),
+	/// assert 2 == 2 : "Math is broken"
 	AssertExpr(AssertStmt, Box<Expr>),
-	LocalExpr(Vec<Bind>, Box<Expr>),
+	/// local a = 2; { b: a }
+	LocalExpr(Vec<BindSpec>, Box<Expr>),
 
-	Bind(Bind),
+	/// a = 3
+	Bind(BindSpec),
+	/// import "hello"
 	Import(String),
+	/// importStr "file.txt"
 	ImportStr(String),
+	/// error "I'm broken"
 	Error(Box<Expr>),
+	/// a(b, c)
 	Apply(Box<Expr>, ArgsDesc),
+	///
 	Select(Box<Expr>, String),
+	/// a[b]
 	Index(Box<Expr>, Box<Expr>),
-	Slice {
-		value: Box<Expr>,
-		start: Option<Box<Expr>>,
-		end: Option<Box<Expr>>,
-		step: Option<Box<Expr>>,
-	},
+	/// a[1::2]
+	Slice(Box<Expr>, SliceDesc),
+	/// function(x) x
 	Function(ParamsDesc, Box<Expr>),
+	/// if true == false then 1 else 2
 	IfElse {
-		cond: IfSpec,
+		cond: IfSpecData,
 		cond_then: Box<Expr>,
 		cond_else: Option<Box<Expr>>,
 	},
-	IfSpec(IfSpec),
-	ForSpec(ForSpec),
+	/// if 2 = 3
+	IfSpec(IfSpecData),
+	/// for elem in array
+	ForSpec(ForSpecData),
 }
modifiedcrates/jsonnet-parser/src/lib.rsdiffbeforeafterboth
before · crates/jsonnet-parser/src/lib.rs
1#![feature(box_syntax)]23use peg::parser;45mod expr;6pub use expr::*;78enum Suffix {9	String(String),10	Expression(Expr),11	Apply(expr::ArgsDesc),12}1314parser! {15	grammar jsonnet_parser() for str {16		rule delimiter() = quiet!{__() "," __()} / expected!("<elements delimiter>")17		rule _() = quiet!{[' ' | '\n' | '\t']+} / expected!("<whitespace>")18		rule __() = quiet!{[' ' | '\n' | '\t']*}19		rule alpha() -> char = c:$(['_' | 'a'..='z' | 'A'..='Z']) {c.chars().nth(0).unwrap()}20		rule digit() -> char = d:$(['0'..='9']) {d.chars().nth(0).unwrap()}21		rule int() -> u32 = a:$(digit()+) { a.parse().unwrap() }22		rule number() -> f64 = quiet!{a:$((['-'|'+'])? int() ("." int())? (['e'|'E'] (s:['+'|'-'])? int())?) { a.parse().unwrap() }} / expected!("<number>")23		rule id() -> String = quiet!{ !("local" / "super" / "self" / "true" / "false" / "null" / "$" / "if" / "then" / "else" / "function") s:$(alpha() (alpha() / digit())*) {s.to_owned()}} / expected!("<identifier>")2425		pub rule positional_param() -> expr::Param = name:id() {expr::Param::Positional(name)}26		pub rule named_param() -> expr::Param = name:id() __() "=" __() expr:boxed_expr() {expr::Param::Named(name, expr)}27		pub rule params() -> expr::ParamsDesc28			= positionals:(positional_param() ** delimiter()) named: (delimiter() named:(named_param() ** delimiter()) {named})? {29				if named.is_some() {30					expr::ParamsDesc([&positionals[..], &named.unwrap()[..]].concat())31				} else {32					expr::ParamsDesc(positionals)33				}34			}35			/ named:(named_param() ** delimiter()) {expr::ParamsDesc(named)}36			/ {expr::ParamsDesc(Vec::new())}3738		pub rule positional_arg() -> expr::Arg = quiet!{name:boxed_expr() {expr::Arg::Positional(name)}}/expected!("<positional arg>")39		pub rule named_arg() -> expr::Arg = quiet!{name:id() __() "=" __() expr:boxed_expr() {expr::Arg::Named(name, expr)}}/expected!("<named arg>")40		pub rule args() -> expr::ArgsDesc41			= positionals:(positional_arg() ** delimiter()) named: (delimiter() named:(named_arg() ** delimiter()) {named})? {42				if named.is_some() {43					expr::ArgsDesc([&positionals[..], &named.unwrap()[..]].concat())44				} else {45					expr::ArgsDesc(positionals)46				}47			}48			/ named:(named_arg() ** delimiter()) {expr::ArgsDesc(named)}49			/ {expr::ArgsDesc(Vec::new())}5051		pub rule bind() -> expr::Bind52			= name:id() __() "=" __() expr:boxed_expr() {expr::Bind::Value(name, expr)}53			/ name:id() __() "(" __() params:params() __() ")" __() "=" __() expr:boxed_expr() {expr::Bind::Function(name, params, expr)}54		pub rule assertion() -> expr::AssertStmt = "assert" _() cond:boxed_expr() msg:(__() ":" __() e:boxed_expr() {e})? { expr::AssertStmt(cond, msg) }55		pub rule string() -> String56			= "\"" str:$((!['"'][_])*) "\"" {str.to_owned()}57			/ "'" str:$((!['\''][_])*) "'" {str.to_owned()}58		pub rule field_name() -> expr::FieldName59			= name:id() {expr::FieldName::Fixed(name)}60			/ name:string() {expr::FieldName::Fixed(name)}61			/ "[" __() expr:boxed_expr() __() "]" {expr::FieldName::Dyn(expr)}62		pub rule visibility() -> expr::Visibility63			= ":::" {expr::Visibility::Unhide}64			/ "::" {expr::Visibility::Hidden}65			/ ":" {expr::Visibility::Normal}66		pub rule field() -> expr::FieldMember67			= name:field_name() __() plus:"+"? __() visibility:visibility() __() value:expr() {expr::FieldMember::Value{68				name,69				plus: plus.is_some(),70				visibility,71				value,72			}}73			/ name:field_name() __() "(" __() params:params() __() ")" __() visibility:visibility() __() value:expr() {expr::FieldMember::Function{74				name,75				params,76				visibility,77				value,78			}}79		pub rule member() -> expr::Member80			= "local" _() bind:bind() {expr::Member::BindStmt(bind)}81			/ assertion:assertion() {expr::Member::AssertStmt(assertion)}82			/ field:field() {expr::Member::Field(field)}83		pub rule obj_body() -> expr::ObjBody = members:(member() ** delimiter()) delimiter()? {expr::ObjBody::MemberList(members)}84		pub rule ifspec() -> expr::IfSpec = "if" _() expr:boxed_expr() {expr::IfSpec(expr)}85		pub rule forspec() -> expr::ForSpec = "for" _() id:id() _() "in" _() ifs:ifspec()* {expr::ForSpec(id, ifs)}86		pub rule bind_expr() -> Expr = bind:bind() {Expr::Bind(bind)}87		pub rule local_expr() -> Expr = "local" _() binds:(bind() ** delimiter()) __() ";" __() expr:boxed_expr() { Expr::LocalExpr(binds, expr) }88		pub rule string_expr() -> Expr = s:string() {Expr::Str(s)}89		pub rule parened_expr() -> Expr = "(" e:boxed_expr() ")" {Expr::Parened(e)}90		pub rule obj_expr() -> Expr = "{" __() body:obj_body() __() "}" {Expr::Obj(body)}91		pub rule array_expr() -> Expr = "[" __() elems:(expr() ** delimiter()) __() delimiter()? "]" {Expr::Arr(elems)}92		pub rule array_comp_expr() -> Expr = "[" __() expr:boxed_expr() delimiter()? fors:forspec()+ __() "]" {Expr::ArrComp(expr, fors)}93		pub rule index_expr() -> Expr94			= val:boxed_expr() "." idx:id() {Expr::Index(val, Box::new(Expr::Str(idx)))}95			/ val:boxed_expr() "[" key:boxed_expr() "]" {Expr::Index(val, key)}96		pub rule slice_expr() -> Expr97			= value:boxed_expr() "[" start:boxed_expr()? ":" pair:(end:boxed_expr()? step:(":" e:boxed_expr() {e})? {(end, step)})? "]" {98			if let Some((end, step)) = pair {99				Expr::Slice { value, start, end, step }100			}else{101				Expr::Slice{ value, start, end: None, step: None }102			}103		}104		pub rule number_expr() -> Expr = n:number() { expr::Expr::Num(n) }105		pub rule var_expr() -> Expr = n:id() { expr::Expr::Var(n) }106		pub rule if_then_else_expr() -> Expr = cond:ifspec() _() "then" _() cond_then:boxed_expr() cond_else:(_() "else" _() e:boxed_expr() {e})? {Expr::IfElse{107			cond,108			cond_then,109			cond_else,110		}}111		pub rule expr_basic() -> Expr112			= "null" {Expr::Value(ValueType::Null)}113			/ "true" {Expr::Value(ValueType::True)} / "false" {Expr::Value(ValueType::False)}114115			/ "self" {Expr::Literal(LiteralType::This)} / "$" {Expr::Literal(LiteralType::Dollar)}116			/ "super" {Expr::Literal(LiteralType::Super)}117118			/ string_expr() / number_expr()119			/ array_expr()120			/ array_comp_expr()121			/ obj_expr()122			/ array_expr()123			/ array_comp_expr()124125			/ var_expr()126			/ if_then_else_expr()127			/ local_expr()128129			/ "function" __() "(" __() params:params() __() ")" __() expr:boxed_expr() {Expr::Function(params, expr)}130131		rule expr_basic_with_suffix() -> Expr132			= a:expr_basic() suffixes:(__() suffix:expr_suffix() {suffix})* {133				let mut cur = a;134				for suffix in suffixes {135					match suffix {136						Suffix::String(index) => {137							cur = Expr::Index(Box::new(cur), Box::new(Expr::Str(index)))138						},139						Suffix::Expression(index) => {140							cur = Expr::Index(Box::new(cur), Box::new(index))141						},142						Suffix::Apply(args) => {143							cur = Expr::Apply(Box::new(cur), args)144						}145					}146				}147				cur148			}149150		rule expr_suffix() -> Suffix151			= "." __() s:id() { Suffix::String(s) }152			/ "[" __() s:expr() __() "]" { Suffix::Expression(s) }153			/ "(" __() args:args() __() ")" { Suffix::Apply(args) }154155		rule expr() -> Expr156			= a:precedence! {157				a:(@) __() "||" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::Or, Box::new(b))}158				--159				a:(@) __() "&&" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::And, Box::new(b))}160				--161				a:(@) __() "|" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::BitOr, Box::new(b))}162				--163				a:@ __() "^" __() b:(@) {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::BitXor, Box::new(b))}164				--165				a:(@) __() "&" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::BitAnd, Box::new(b))}166				--167				a:(@) __() "==" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::Eq, Box::new(b))}168				a:(@) __() "!=" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::Ne, Box::new(b))}169				--170				a:(@) __() "<" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::Lt, Box::new(b))}171				a:(@) __() ">" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::Gt, Box::new(b))}172				a:(@) __() "<=" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::Lte, Box::new(b))}173				a:(@) __() ">=" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::Gte, Box::new(b))}174				--175				a:(@) __() "<<" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::Lhs, Box::new(b))}176				a:(@) __() ">>" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::Rhs, Box::new(b))}177				--178				a:(@) __() "+" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::Add, Box::new(b))}179				a:(@) __() "-" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::Sub, Box::new(b))}180				--181				a:(@) __() "*" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::Mul, Box::new(b))}182				a:(@) __() "/" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::Div, Box::new(b))}183				a:(@) __() "%" __() b:@ {Expr::BinaryOp(Box::new(a), expr::BinaryOpType::Mod, Box::new(b))}184				--185				e:expr_basic_with_suffix() {e}186				"(" __() e:boxed_expr() __() ")" {Expr::Parened(e)}187			}188			/ e:expr_basic_with_suffix() {e}189190		pub rule boxed_expr() -> Box<Expr> = e:expr() {Box::new(e)}191		pub rule jsonnet() -> Expr = __() e:expr() __() {e}192	}193}194195// TODO: impl FromStr from Expr196pub fn parse(str: &str) -> Result<Expr, peg::error::ParseError<peg::str::LineCol>> {197	jsonnet_parser::jsonnet(str)198}199200#[cfg(test)]201pub mod tests {202	use super::{expr::*, parse};203	#[test]204	fn empty_object() {205		assert_eq!(parse("{}").unwrap(), Expr::Obj(ObjBody::MemberList(vec![])),);206	}207	#[test]208	fn basic_math() {209		assert_eq!(210			parse("2+2*2").unwrap(),211			Expr::BinaryOp(212				Box::new(Expr::Num(2.0)),213				BinaryOpType::Add,214				Box::new(Expr::BinaryOp(215					Box::new(Expr::Num(2.0)),216					BinaryOpType::Mul,217					Box::new(Expr::Num(2.0))218				))219			)220		);221	}222223	#[test]224	fn suffix_comparsion() {225		use Expr::*;226		assert_eq!(227			parse("std.type(a) == \"string\"").unwrap(),228			BinaryOp(229				box Apply(230					box Index(box Var("std".to_owned()), box Str("type".to_owned())),231					ArgsDesc(vec![Arg::Positional(box Var("a".to_owned()))])232				),233				BinaryOpType::Eq,234				box Str("string".to_owned())235			)236		);237	}238}
after · crates/jsonnet-parser/src/lib.rs
1#![feature(box_syntax)]23use peg::parser;45mod expr;6pub use expr::*;78enum Suffix {9	String(String),10	Slice(SliceDesc),11	Expression(Expr),12	Apply(expr::ArgsDesc),13	Extend(expr::ObjBody),14}1516parser! {17	grammar jsonnet_parser() for str {18		use peg::ParseLiteral;1920		/// Standard C-like comments21		rule comment() = "//" (!['\n'][_])* "\n" / "/*" ((!("*/")[_][_])/("\\" "*/"))* "*/"22		rule _() = ([' ' | '\n' | '\t'] / comment())*2324		/// For comma-delimited elements25		rule comma() = quiet!{_ "," _} / expected!("<comma>")26		rule alpha() -> char = c:$(['_' | 'a'..='z' | 'A'..='Z']) {c.chars().nth(0).unwrap()}27		rule digit() -> char = d:$(['0'..='9']) {d.chars().nth(0).unwrap()}28		rule end_of_ident() = !['0'..='9' | '_' | 'a'..='z' | 'A'..='Z']29		/// Sequence of digits30		rule uint() -> u32 = a:$(digit()+) { a.parse().unwrap() }31		/// Number in scientific notation format32		rule number() -> f64 = quiet!{a:$(uint() ("." uint())? (['e'|'E'] (s:['+'|'-'])? uint())?) { a.parse().unwrap() }} / expected!("<number>")3334		/// Reserved word followed by any non-alphanumberic35		rule reserved() = ("assert" / "else" / "error" / "false" / "for" / "function" / "if" / "import" / "importstr" / "in" / "local" / "null" / "tailstrict" / "then" / "self" / "super" / "true") end_of_ident()36		rule id() -> String = quiet!{ !reserved() s:$(alpha() (alpha() / digit())*) {s.to_owned()}} / expected!("<identifier>")37		rule keyword(id: &'static str) = ##parse_string_literal(id) end_of_ident()3839		pub rule param() -> expr::Param = name:id() expr:(_ "=" _ expr:boxed_expr(){expr})? { expr::Param(name, expr) }40		pub rule params() -> expr::ParamsDesc41			= params:(param() ** comma()) {42				let mut defaults_started = false;43				for param in &params {44					defaults_started = defaults_started || param.1.is_some();45					assert_eq!(defaults_started, param.1.is_some(), "defauld parameters should be used after all positionals");46				}47				expr::ParamsDesc(params)48			}49			/ { expr::ParamsDesc(Vec::new()) }5051		pub rule arg() -> expr::Arg52			= name:id() _ "=" _ expr:boxed_expr() {expr::Arg(Some(name), expr)}53			/ expr:boxed_expr() {expr::Arg(None, expr)}54		pub rule args() -> expr::ArgsDesc55			= args:arg() ** comma() comma()? {56				let mut named_started = false;57				for arg in &args {58					named_started = named_started || arg.0.is_some();59					assert_eq!(named_started, arg.0.is_some(), "named args should be used after all positionals");60				}61				expr::ArgsDesc(args)62			}63			/ { expr::ArgsDesc(Vec::new()) }6465		pub rule bind() -> expr::BindSpec66			= name:id() _ "=" _ expr:boxed_expr() {expr::BindSpec{name, params: None, value: expr}}67			/ name:id() _ "(" _ params:params() _ ")" _ "=" _ expr:boxed_expr() {expr::BindSpec{name, params: Some(params), value: expr}}68		pub rule assertion() -> expr::AssertStmt = keyword("assert") _ cond:boxed_expr() msg:(_ ":" _ e:boxed_expr() {e})? { expr::AssertStmt(cond, msg) }69		pub rule string() -> String70			= "\"" str:$(("\\\"" / !['"'][_])*) "\"" {str.to_owned()}71			/ "'" str:$((!['\''][_])*) "'" {str.to_owned()}72		pub rule field_name() -> expr::FieldName73			= name:id() {expr::FieldName::Fixed(name)}74			/ name:string() {expr::FieldName::Fixed(name)}75			/ "[" _ expr:boxed_expr() _ "]" {expr::FieldName::Dyn(expr)}76		pub rule visibility() -> expr::Visibility77			= ":::" {expr::Visibility::Unhide}78			/ "::" {expr::Visibility::Hidden}79			/ ":" {expr::Visibility::Normal}80		pub rule field() -> expr::FieldMember81			= name:field_name() _ plus:"+"? _ visibility:visibility() _ value:expr() {expr::FieldMember{82				name,83				plus: plus.is_some(),84				params: None,85				visibility,86				value,87			}}88			/ name:field_name() _ "(" _ params:params() _ ")" _ visibility:visibility() _ value:expr() {expr::FieldMember{89				name,90				plus: false,91				params: Some(params),92				visibility,93				value,94			}}95		pub rule obj_local() -> BindSpec96			= keyword("local") _ bind:bind() {bind}97		pub rule member() -> expr::Member98			= bind:obj_local() {expr::Member::BindStmt(bind)}99			/ assertion:assertion() {expr::Member::AssertStmt(assertion)}100			/ field:field() {expr::Member::Field(field)}101		pub rule objinside() -> expr::ObjBody102			= 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})? {103				expr::ObjBody::ObjComp {104					pre_locals,105					key,106					value,107					post_locals,108					first,109					rest: rest.unwrap_or(Vec::new()),110				}111			}112			/ members:(member() ** comma()) comma()? {expr::ObjBody::MemberList(members)}113		pub rule ifspec() -> IfSpecData = keyword("if") _ expr:boxed_expr() {IfSpecData(expr)}114		pub rule forspec() -> ForSpecData = keyword("for") _ id:id() _ keyword("in") _ cond:boxed_expr() {ForSpecData(id, cond)}115		pub rule compspec() -> Vec<expr::CompSpec> = s:(i:ifspec() { expr::CompSpec::IfSpec(i) } / f:forspec() {expr::CompSpec::ForSpec(f)} )+ {s}116		pub rule bind_expr() -> Expr = bind:bind() {Expr::Bind(bind)}117		pub rule local_expr() -> Expr = keyword("local") _ binds:bind() ** comma() _ ";" _ expr:boxed_expr() { Expr::LocalExpr(binds, expr) }118		pub rule string_expr() -> Expr = s:string() {Expr::Str(s)}119		pub rule parened_expr() -> Expr = "(" e:boxed_expr() ")" {Expr::Parened(e)}120		pub rule obj_expr() -> Expr = "{" _ body:objinside() _ "}" {Expr::Obj(body)}121		pub rule array_expr() -> Expr = "[" _ elems:(expr() ** comma()) _ comma()? "]" {Expr::Arr(elems)}122		pub rule array_comp_expr() -> Expr = "[" _ expr:boxed_expr() _ comma()? _ forspec:forspec() _ others:(others: compspec() _ {others})? "]" {Expr::ArrComp(expr, forspec, others.unwrap_or(vec![]))}123		pub rule index_expr() -> Expr124			= val:boxed_expr() "." idx:id() {Expr::Index(val, Box::new(Expr::Str(idx)))}125			/ val:boxed_expr() "[" key:boxed_expr() "]" {Expr::Index(val, key)}126		pub rule number_expr() -> Expr = n:number() { expr::Expr::Num(n) }127		pub rule var_expr() -> Expr = n:id() { expr::Expr::Var(n) }128		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{129			cond,130			cond_then,131			cond_else,132		}}133134		pub rule literal() -> Expr135			= v:(136				keyword("null") {LiteralType::Null}137				/ keyword("true") {LiteralType::True}138				/ keyword("false") {LiteralType::False}139				/ keyword("self") {LiteralType::This}140				/ keyword("$") {LiteralType::Dollar}141				/ keyword("super") {LiteralType::Super}142			) {Expr::Literal(v)}143144		pub rule expr_basic() -> Expr145			= literal()146147			/ string_expr() / number_expr()148			/ array_expr()149			/ obj_expr()150			/ array_expr()151			/ array_comp_expr()152153			/ var_expr()154			/ local_expr()155			/ if_then_else_expr()156			/ "-" _ expr:boxed_expr() { Expr::UnaryOp(UnaryOpType::Minus, expr) }157			/ "!" _ expr:boxed_expr() { Expr::UnaryOp(UnaryOpType::Not, expr) }158159			/ keyword("function") _ "(" _ params:params() _ ")" _ expr:boxed_expr() {Expr::Function(params, expr)}160			/ assertion:assertion() _ ";" _ expr:boxed_expr() { Expr::AssertExpr(assertion, expr) }161162			/ keyword("error") _ expr:boxed_expr() { Expr::Error(expr) }163164		rule expr_basic_with_suffix() -> Expr165			= a:expr_basic() suffixes:(_ suffix:expr_suffix() {suffix})* {166				let mut cur = a;167				for suffix in suffixes {168					cur = match suffix {169						Suffix::String(index) => Expr::Index(Box::new(cur), Box::new(Expr::Str(index))),170						Suffix::Slice(desc) => Expr::Slice(Box::new(cur), desc),171						Suffix::Expression(index) => Expr::Index(Box::new(cur), Box::new(index)),172						Suffix::Apply(args) => Expr::Apply(Box::new(cur), args),173						Suffix::Extend(body) => Expr::ObjExtend(box cur, body),174					}175				}176				cur177			}178179		pub rule slice_desc() -> SliceDesc180			= start:boxed_expr()? _ ":" _ pair:(end:boxed_expr()? _ step:(":" _ e:boxed_expr() {e})? {(end, step)})? {181				if let Some((end, step)) = pair {182					SliceDesc { start, end, step }183				}else{184					SliceDesc { start, end: None, step: None }185				}186			}187188		rule expr_suffix() -> Suffix189			= "." _ s:id() { Suffix::String(s) }190			/ "[" _ s:slice_desc() _ "]" { Suffix::Slice(s) }191			/ "[" _ s:expr() _ "]" { Suffix::Expression(s) }192			/ "(" _ args:args() _ ")" (_ keyword("tailstrict"))? { Suffix::Apply(args) }193			/ "{" _ body:objinside() _ "}" { Suffix::Extend(body) }194195		rule expr() -> Expr196			= a:precedence! {197				a:(@) _ "||" _ b:@ {Expr::BinaryOp(Box::new(a), BinaryOpType::Or, Box::new(b))}198				--199				a:(@) _ "&&" _ b:@ {Expr::BinaryOp(Box::new(a), BinaryOpType::And, Box::new(b))}200				--201				a:(@) _ "|" _ b:@ {Expr::BinaryOp(Box::new(a), BinaryOpType::BitOr, Box::new(b))}202				--203				a:@ _ "^" _ b:(@) {Expr::BinaryOp(Box::new(a), BinaryOpType::BitXor, Box::new(b))}204				--205				a:(@) _ "&" _ b:@ {Expr::BinaryOp(Box::new(a), BinaryOpType::BitAnd, Box::new(b))}206				--207				a:(@) _ "==" _ b:@ {Expr::BinaryOp(Box::new(a), BinaryOpType::Eq, Box::new(b))}208				a:(@) _ "!=" _ b:@ {Expr::BinaryOp(Box::new(a), BinaryOpType::Ne, Box::new(b))}209				--210				a:(@) _ "<" _ b:@ {Expr::BinaryOp(Box::new(a), BinaryOpType::Lt, Box::new(b))}211				a:(@) _ ">" _ b:@ {Expr::BinaryOp(Box::new(a), BinaryOpType::Gt, Box::new(b))}212				a:(@) _ "<=" _ b:@ {Expr::BinaryOp(Box::new(a), BinaryOpType::Lte, Box::new(b))}213				a:(@) _ ">=" _ b:@ {Expr::BinaryOp(Box::new(a), BinaryOpType::Gte, Box::new(b))}214				--215				a:(@) _ "<<" _ b:@ {Expr::BinaryOp(Box::new(a), BinaryOpType::Lhs, Box::new(b))}216				a:(@) _ ">>" _ b:@ {Expr::BinaryOp(Box::new(a), BinaryOpType::Rhs, Box::new(b))}217				--218				a:(@) _ "+" _ b:@ {Expr::BinaryOp(Box::new(a), BinaryOpType::Add, Box::new(b))}219				a:(@) _ "-" _ b:@ {Expr::BinaryOp(Box::new(a), BinaryOpType::Sub, Box::new(b))}220				--221				a:(@) _ "*" _ b:@ {Expr::BinaryOp(Box::new(a), BinaryOpType::Mul, Box::new(b))}222				a:(@) _ "/" _ b:@ {Expr::BinaryOp(Box::new(a), BinaryOpType::Div, Box::new(b))}223				a:(@) _ "%" _ b:@ {Expr::BinaryOp(Box::new(a), BinaryOpType::Mod, Box::new(b))}224				--225				e:expr_basic_with_suffix() {e}226				"(" _ e:boxed_expr() _ ")" {Expr::Parened(e)}227			}228			/ e:expr_basic_with_suffix() {e}229230		pub rule boxed_expr() -> Box<Expr> = e:expr() {Box::new(e)}231		pub rule jsonnet() -> Expr = _ e:expr() _ {e}232	}233}234235// TODO: impl FromStr from Expr236pub fn parse(str: &str) -> Result<Expr, peg::error::ParseError<peg::str::LineCol>> {237	jsonnet_parser::jsonnet(str)238}239240#[cfg(test)]241pub mod tests {242	use super::{expr::*, parse};243	#[test]244	fn empty_object() {245		assert_eq!(parse("{}").unwrap(), Expr::Obj(ObjBody::MemberList(vec![])),);246	}247	#[test]248	fn basic_math() {249		assert_eq!(250			parse("2+2*2").unwrap(),251			Expr::BinaryOp(252				Box::new(Expr::Num(2.0)),253				BinaryOpType::Add,254				Box::new(Expr::BinaryOp(255					Box::new(Expr::Num(2.0)),256					BinaryOpType::Mul,257					Box::new(Expr::Num(2.0))258				))259			)260		);261	}262263	/// Comments should not affect parsing264	#[test]265	fn comments() {266		assert_eq!(267			parse("2//comment\n+//comment\n3/*test*/*/*test*/4").unwrap(),268			Expr::BinaryOp(269				box Expr::Num(2.0),270				BinaryOpType::Add,271				box Expr::BinaryOp(box Expr::Num(3.0), BinaryOpType::Mul, box Expr::Num(4.0))272			)273		);274	}275276	/// Comments should be able to be escaped277	#[test]278	fn comment_escaping() {279		assert_eq!(280			parse("2/*\\*/+*/ - 22").unwrap(),281			Expr::BinaryOp(box Expr::Num(2.0), BinaryOpType::Sub, box Expr::Num(22.0))282		);283	}284285	#[test]286	fn suffix_comparsion() {287		use Expr::*;288		assert_eq!(289			parse("std.type(a) == \"string\"").unwrap(),290			BinaryOp(291				box Apply(292					box Index(box Var("std".to_owned()), box Str("type".to_owned())),293					ArgsDesc(vec![Arg(None, box Var("a".to_owned()))])294				),295				BinaryOpType::Eq,296				box Str("string".to_owned())297			)298		);299	}300301	#[test]302	fn array_comp() {303		use Expr::*;304		assert_eq!(305			parse("[std.deepJoin(x) for x in arr]").unwrap(),306			ArrComp(307				box Apply(308					box Index(box Var("std".to_owned()), box Str("deepJoin".to_owned())),309					ArgsDesc(vec![Arg(None, box Var("x".to_owned()))])310				),311				ForSpecData("x".to_owned(), box Var("arr".to_owned())),312				vec![]313			),314		)315	}316317	#[test]318	fn array_comp_with_ifs() {319		use Expr::*;320		assert_eq!(321			parse("[k for k in std.objectFields(patch) if patch[k] == null]").unwrap(),322			ArrComp(323				box Var("k".to_owned()),324				ForSpecData(325					"k".to_owned(),326					box Apply(327						box Index(328							box Var("std".to_owned()),329							box Str("objectFields".to_owned())330						),331						ArgsDesc(vec![Arg(None, box Var("patch".to_owned()))])332					)333				),334				vec![CompSpec::IfSpec(IfSpecData(box BinaryOp(335					box Index(box Var("patch".to_owned()), box Var("k".to_owned())),336					BinaryOpType::Eq,337					box Literal(LiteralType::Null)338				)))]339			),340		);341	}342343	#[test]344	fn reserved() {345		use Expr::*;346		assert_eq!(parse("null").unwrap(), Literal(LiteralType::Null));347		assert_eq!(parse("nulla").unwrap(), Var("nulla".to_owned()));348	}349350	#[test]351	fn multiple_args_buf() {352		parse("a(b, null_fields)").unwrap();353	}354355	#[test]356	fn can_parse_stdlib() {357		parse(jsonnet_stdlib::STDLIB_STR).unwrap();358	}359}