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
before · crates/jsonnet-parser/src/expr.rs
1#[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}
after · crates/jsonnet-parser/src/expr.rs
1use std::fmt::Display;23#[derive(Debug, Clone, PartialEq)]4pub enum FieldName {5	/// {fixed: 2}6	Fixed(String),7	/// {["dyn"+"amic"]: 3}8	Dyn(Box<Expr>),9}1011#[derive(Debug, Clone, PartialEq)]12pub enum Visibility {13	/// :14	Normal,15	/// ::16	Hidden,17	/// :::18	Unhide,19}2021#[derive(Debug, Clone, PartialEq)]22pub struct AssertStmt(pub Box<Expr>, pub Option<Box<Expr>>);2324#[derive(Debug, Clone, PartialEq)]25pub struct FieldMember {26	pub name: FieldName,27	pub plus: bool,28	pub params: Option<ParamsDesc>,29	pub visibility: Visibility,30	pub value: Expr,31}3233#[derive(Debug, Clone, PartialEq)]34pub enum Member {35	Field(FieldMember),36	BindStmt(BindSpec),37	AssertStmt(AssertStmt),38}3940#[derive(Debug, Clone, PartialEq)]41pub enum UnaryOpType {42	Plus,43	Minus,44	BitNot,45	Not,46}4748#[derive(Debug, Clone, Copy, PartialEq)]49pub enum BinaryOpType {50	Mul,51	Div,52	Mod,5354	Add,55	Sub,5657	Lhs,58	Rhs,5960	Lt,61	Gt,62	Lte,63	Gte,6465	In,6667	Eq,68	Ne,6970	BitAnd,71	BitOr,72	BitXor,7374	And,75	Or,76}7778/// name, default value79#[derive(Debug, Clone, PartialEq)]80pub struct Param(pub String, pub Option<Box<Expr>>);81/// Defined function parameters82#[derive(Debug, Clone, PartialEq)]83pub struct ParamsDesc(pub Vec<Param>);84impl ParamsDesc {85	pub fn with_defaults(&self) -> Vec<Param> {86		self.087			.iter()88			.filter(|e| e.1.is_some())89			.map(|e| e.clone())90			.collect()91	}92}9394#[derive(Debug, Clone, PartialEq)]95pub struct Arg(pub Option<String>, pub Box<Expr>);96#[derive(Debug, Clone, PartialEq)]97pub struct ArgsDesc(pub Vec<Arg>);9899#[derive(Debug, Clone, PartialEq)]100pub struct BindSpec {101	pub name: String,102	pub params: Option<ParamsDesc>,103	pub value: Box<Expr>,104}105106#[derive(Debug, Clone, PartialEq)]107pub struct IfSpecData(pub Box<Expr>);108#[derive(Debug, Clone, PartialEq)]109pub struct ForSpecData(pub String, pub Box<Expr>);110111#[derive(Debug, Clone, PartialEq)]112pub enum CompSpec {113	IfSpec(IfSpecData),114	ForSpec(ForSpecData),115}116117#[derive(Debug, Clone, PartialEq)]118pub enum ObjBody {119	MemberList(Vec<Member>),120	ObjComp {121		pre_locals: Vec<BindSpec>,122		key: Box<Expr>,123		value: Box<Expr>,124		post_locals: Vec<BindSpec>,125		first: ForSpecData,126		rest: Vec<CompSpec>,127	},128}129130#[derive(Debug, Clone, PartialEq)]131pub enum LiteralType {132	This,133	Super,134	Dollar,135	Null,136	True,137	False,138}139impl Display for LiteralType {140	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {141		use LiteralType::*;142		match self {143			This => write!(f, "this"),144			Null => write!(f, "null"),145			True => write!(f, "true"),146			False => write!(f, "false"),147			_ => panic!("non printable item"),148		}149	}150}151152#[derive(Debug, Clone, PartialEq)]153pub struct SliceDesc {154	pub start: Option<Box<Expr>>,155	pub end: Option<Box<Expr>>,156	pub step: Option<Box<Expr>>,157}158159/// Syntax base160#[derive(Debug, Clone, PartialEq)]161pub enum Expr {162	Literal(LiteralType),163164	/// String value: "hello"165	Str(String),166	/// Number: 1, 2.0, 2e+20167	Num(f64),168	/// Variable name: test169	Var(String),170171	/// Array of expressions: [1, 2, "Hello"]172	Arr(Vec<Expr>),173	/// Array comprehension:174	/// ```jsonnet175	///  ingredients: [176	///    { kind: kind, qty: 4 / 3 }177	///    for kind in [178	///      'Honey Syrup',179	///      'Lemon Juice',180	///      'Farmers Gin',181	///    ]182	///  ],183	/// ```184	ArrComp(Box<Expr>, ForSpecData, Vec<CompSpec>),185186	/// Object: {a: 2}187	Obj(ObjBody),188	/// Object extension: var1 {b: 2}189	ObjExtend(Box<Expr>, ObjBody),190191	/// (obj)192	Parened(Box<Expr>),193194	/// Params in function definition195	/// hello, world, test = 2196	Params(ParamsDesc),197	/// Args in function call198	/// 2 + 2, 3, named = 6199	Args(ArgsDesc),200201	/// -2202	UnaryOp(UnaryOpType, Box<Expr>),203	/// 2 - 2204	BinaryOp(Box<Expr>, BinaryOpType, Box<Expr>),205	/// assert 2 == 2 : "Math is broken"206	AssertExpr(AssertStmt, Box<Expr>),207	/// local a = 2; { b: a }208	LocalExpr(Vec<BindSpec>, Box<Expr>),209210	/// a = 3211	Bind(BindSpec),212	/// import "hello"213	Import(String),214	/// importStr "file.txt"215	ImportStr(String),216	/// error "I'm broken"217	Error(Box<Expr>),218	/// a(b, c)219	Apply(Box<Expr>, ArgsDesc),220	///221	Select(Box<Expr>, String),222	/// a[b]223	Index(Box<Expr>, Box<Expr>),224	/// a[1::2]225	Slice(Box<Expr>, SliceDesc),226	/// function(x) x227	Function(ParamsDesc, Box<Expr>),228	/// if true == false then 1 else 2229	IfElse {230		cond: IfSpecData,231		cond_then: Box<Expr>,232		cond_else: Option<Box<Expr>>,233	},234	/// if 2 = 3235	IfSpec(IfSpecData),236	/// for elem in array237	ForSpec(ForSpecData),238}
modifiedcrates/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 &params {
+					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();
+	}
 }