git.delta.rocks / jrsonnet / refs/commits / 0b0d703c6d05

difftreelog

refactor do not desugar mod/slice

Yaroslav Bolyukin2021-07-04parent: #53ec857.patch.diff
in: master

7 files changed

modifiedcrates/jrsonnet-evaluator/src/builtin/mod.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/builtin/mod.rs
+++ b/crates/jrsonnet-evaluator/src/builtin/mod.rs
@@ -1,13 +1,14 @@
 use crate::{
 	equals,
 	error::{Error::*, Result},
+	operator::evaluate_mod_op,
 	parse_args, primitive_equals, push, throw, with_state, ArrValue, Context, EvaluationState,
-	FuncVal, LazyVal, Val,
+	FuncVal, IndexableVal, LazyVal, Val,
 };
 use format::{format_arr, format_obj};
 use jrsonnet_gc::Gc;
 use jrsonnet_interner::IStr;
-use jrsonnet_parser::{ArgsDesc, BinaryOpType, ExprLocation};
+use jrsonnet_parser::{ArgsDesc, ExprLocation};
 use jrsonnet_types::ty;
 use std::{collections::HashMap, path::PathBuf, rc::Rc};
 
@@ -20,7 +21,7 @@
 pub mod manifest;
 pub mod sort;
 
-fn std_format(str: IStr, vals: Val) -> Result<Val> {
+pub fn std_format(str: IStr, vals: Val) -> Result<Val> {
 	push(
 		Some(&ExprLocation(Rc::from(PathBuf::from("std.jsonnet")), 0, 0)),
 		|| format!("std.format of {}", str),
@@ -34,6 +35,38 @@
 	)
 }
 
+pub fn std_slice(
+	indexable: IndexableVal,
+	index: Option<usize>,
+	end: Option<usize>,
+	step: Option<usize>,
+) -> Result<Val> {
+	let index = index.unwrap_or(0);
+	let end = end.unwrap_or_else(|| match &indexable {
+		IndexableVal::Str(_) => usize::MAX,
+		IndexableVal::Arr(v) => v.len(),
+	});
+	let step = step.unwrap_or(1);
+	match &indexable {
+		IndexableVal::Str(s) => Ok(Val::Str(
+			(s.chars()
+				.skip(index)
+				.take(end - index)
+				.step_by(step)
+				.collect::<String>())
+			.into(),
+		)),
+		IndexableVal::Arr(arr) => Ok(Val::Arr(
+			(arr.iter()
+				.skip(index)
+				.take(end - index)
+				.step_by(step)
+				.collect::<Result<Vec<Val>>>()?)
+			.into(),
+		)),
+	}
+}
+
 type Builtin = fn(context: Context, loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val>;
 
 type BuiltinsType = HashMap<Box<str>, Builtin>;
@@ -188,34 +221,12 @@
 		2, end: ty!((number | null));
 		3, step: ty!((number | null));
 	], {
-		let index = match index {
-			Val::Num(v) => v as usize,
-			Val::Null => 0,
-			_ => unreachable!(),
-		};
-		let end = match end {
-			Val::Num(v) => v as usize,
-			Val::Null => match &indexable {
-				Val::Str(s) => s.chars().count(),
-				Val::Arr(v) => v.len(),
-				_ => unreachable!()
-			},
-			_ => unreachable!()
-		};
-		let step = match step {
-			Val::Num(v) => v as usize,
-			Val::Null => 1,
-			_ => unreachable!()
-		};
-		match &indexable {
-			Val::Str(s) => {
-				Ok(Val::Str((s.chars().skip(index).take(end-index).step_by(step).collect::<String>()).into()))
-			}
-			Val::Arr(arr) => {
-				Ok(Val::Arr((arr.iter().skip(index).take(end-index).step_by(step).collect::<Result<Vec<Val>>>()?).into()))
-			}
-			_ => unreachable!()
-		}
+		std_slice(
+			indexable.to_indexable()?,
+			index.try_cast_nullable_num("index")?.map(|v| v as usize),
+			end.try_cast_nullable_num("end")?.map(|v| v as usize),
+			step.try_cast_nullable_num("step")?.map(|v| v as usize),
+		)
 	})
 }
 
@@ -257,11 +268,7 @@
 		0, a: ty!((number | string));
 		1, b: ty!(any);
 	], {
-		match (a, b) {
-			(Val::Num(a), Val::Num(b)) => Ok(Val::Num(a % b)),
-			(Val::Str(str), vals) => std_format(str, vals),
-			(a, b) => throw!(BinaryOperatorDoesNotOperateOnValues(BinaryOpType::Mod, a.value_type(), b.value_type()))
-		}
+		evaluate_mod_op(&a, &b)
 	})
 }
 
modifiedcrates/jrsonnet-evaluator/src/error.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/error.rs
+++ b/crates/jrsonnet-evaluator/src/error.rs
@@ -73,6 +73,8 @@
 	ValueIndexMustBeTypeGot(ValType, ValType, ValType),
 	#[error("cant index into {0}")]
 	CantIndexInto(ValType),
+	#[error("{0} is not indexable")]
+	ValueIsNotIndexable(ValType),
 
 	#[error("super can't be used standalone")]
 	StandaloneSuper,
modifiedcrates/jrsonnet-evaluator/src/evaluate/mod.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/evaluate/mod.rs
+++ b/crates/jrsonnet-evaluator/src/evaluate/mod.rs
@@ -1,4 +1,5 @@
 use crate::{
+	builtin::std_slice,
 	error::Error::*,
 	evaluate::operator::{evaluate_add_op, evaluate_binary_op_special, evaluate_unary_op},
 	push, throw, with_state, ArrValue, Bindable, Context, ContextCreator, FuncDesc, FuncVal,
@@ -679,6 +680,28 @@
 				}
 			}
 		}
+		Slice(value, desc) => {
+			let indexable = evaluate(context.clone(), value)?;
+
+			fn parse_num(
+				context: &Context,
+				expr: Option<&LocExpr>,
+				desc: &'static str,
+			) -> Result<Option<usize>> {
+				Ok(match expr {
+					Some(s) => evaluate(context.clone(), &s)?
+						.try_cast_nullable_num(desc)?
+						.map(|v| v as usize),
+					None => None,
+				})
+			}
+
+			let start = parse_num(&context, desc.start.as_ref(), "start")?;
+			let end = parse_num(&context, desc.end.as_ref(), "end")?;
+			let step = parse_num(&context, desc.step.as_ref(), "step")?;
+
+			std_slice(indexable.to_indexable()?, start, end, step)?
+		}
 		Import(path) => {
 			let tmp = loc
 				.clone()
modifiedcrates/jrsonnet-evaluator/src/evaluate/operator.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/evaluate/operator.rs
+++ b/crates/jrsonnet-evaluator/src/evaluate/operator.rs
@@ -1,3 +1,4 @@
+use crate::builtin::std_format;
 use crate::{equals, evaluate, Context, Val};
 use crate::{error::Error::*, throw, Result};
 use jrsonnet_parser::{BinaryOpType, LocExpr, UnaryOpType};
@@ -41,6 +42,19 @@
 	})
 }
 
+pub fn evaluate_mod_op(a: &Val, b: &Val) -> Result<Val> {
+	use Val::*;
+	match (a, b) {
+		(Num(a), Num(b)) => Ok(Num(a % b)),
+		(Str(str), vals) => std_format(str.clone(), vals.clone()),
+		(a, b) => throw!(BinaryOperatorDoesNotOperateOnValues(
+			BinaryOpType::Mod,
+			a.value_type(),
+			b.value_type()
+		)),
+	}
+}
+
 pub fn evaluate_binary_op_special(
 	context: Context,
 	a: &LocExpr,
@@ -60,13 +74,14 @@
 	use BinaryOpType::*;
 	use Val::*;
 	Ok(match (a, op, b) {
-		(Str(a), In, Obj(obj)) => Bool(obj.has_field_ex(a.clone(), true)),
-
 		(a, Add, b) => evaluate_add_op(a, b)?,
 
 		(a, Eq, b) => Bool(equals(a, b)?),
 		(a, Neq, b) => Bool(!equals(a, b)?),
 
+		(Str(a), In, Obj(obj)) => Bool(obj.has_field_ex(a.clone(), true)),
+		(a, Mod, b) => evaluate_mod_op(a, b)?,
+
 		(Str(v1), Mul, Num(v2)) => Str(v1.repeat(*v2 as usize).into()),
 
 		// Bool X Bool
modifiedcrates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/val.rs
+++ b/crates/jrsonnet-evaluator/src/val.rs
@@ -345,6 +345,11 @@
 	}
 }
 
+pub enum IndexableVal {
+	Str(IStr),
+	Arr(ArrValue),
+}
+
 #[derive(Debug, Clone, Trace)]
 #[trivially_drop]
 pub enum Val {
@@ -402,6 +407,17 @@
 		self.assert_type(context, ValType::Num)?;
 		self.unwrap_num()
 	}
+	pub fn try_cast_nullable_num(self, context: &'static str) -> Result<Option<f64>> {
+		Ok(match self {
+			Val::Null => None,
+			Val::Num(num) => Some(num),
+			_ => throw!(TypeMismatch(
+				context,
+				vec![ValType::Null, ValType::Num],
+				self.value_type()
+			)),
+		})
+	}
 	pub const fn value_type(&self) -> ValType {
 		match self {
 			Self::Str(..) => ValType::Str,
@@ -580,6 +596,13 @@
 			.try_cast_str("to json")
 		})
 	}
+	pub fn to_indexable(self) -> Result<IndexableVal> {
+		Ok(match self {
+			Val::Str(s) => IndexableVal::Str(s),
+			Val::Arr(arr) => IndexableVal::Arr(arr),
+			_ => throw!(ValueIsNotIndexable(self.value_type())),
+		})
+	}
 }
 
 const fn is_function_like(val: &Val) -> bool {
modifiedcrates/jrsonnet-parser/src/expr.rsdiffbeforeafterboth
before · crates/jrsonnet-parser/src/expr.rs
1use jrsonnet_gc::{unsafe_empty_trace, Finalize, Trace};2use jrsonnet_interner::IStr;3#[cfg(feature = "deserialize")]4use serde::Deserialize;5#[cfg(feature = "serialize")]6use serde::Serialize;7use std::{8	fmt::{Debug, Display},9	ops::Deref,10	path::{Path, PathBuf},11	rc::Rc,12};1314#[cfg_attr(feature = "serialize", derive(Serialize))]15#[cfg_attr(feature = "deserialize", derive(Deserialize))]16#[derive(Debug, PartialEq, Trace)]17#[trivially_drop]18pub enum FieldName {19	/// {fixed: 2}20	Fixed(IStr),21	/// {["dyn"+"amic"]: 3}22	Dyn(LocExpr),23}2425#[cfg_attr(feature = "serialize", derive(Serialize))]26#[cfg_attr(feature = "deserialize", derive(Deserialize))]27#[derive(Debug, Clone, Copy, PartialEq, Trace)]28#[trivially_drop]29pub enum Visibility {30	/// :31	Normal,32	/// ::33	Hidden,34	/// :::35	Unhide,36}3738impl Visibility {39	pub fn is_visible(&self) -> bool {40		matches!(self, Self::Normal | Self::Unhide)41	}42}4344#[cfg_attr(feature = "serialize", derive(Serialize))]45#[cfg_attr(feature = "deserialize", derive(Deserialize))]46#[derive(Clone, Debug, PartialEq, Trace)]47#[trivially_drop]48pub struct AssertStmt(pub LocExpr, pub Option<LocExpr>);4950#[cfg_attr(feature = "serialize", derive(Serialize))]51#[cfg_attr(feature = "deserialize", derive(Deserialize))]52#[derive(Debug, PartialEq, Trace)]53#[trivially_drop]54pub struct FieldMember {55	pub name: FieldName,56	pub plus: bool,57	pub params: Option<ParamsDesc>,58	pub visibility: Visibility,59	pub value: LocExpr,60}6162#[cfg_attr(feature = "serialize", derive(Serialize))]63#[cfg_attr(feature = "deserialize", derive(Deserialize))]64#[derive(Debug, PartialEq, Trace)]65#[trivially_drop]66pub enum Member {67	Field(FieldMember),68	BindStmt(BindSpec),69	AssertStmt(AssertStmt),70}7172#[cfg_attr(feature = "serialize", derive(Serialize))]73#[cfg_attr(feature = "deserialize", derive(Deserialize))]74#[derive(Debug, Clone, Copy, PartialEq, Trace)]75#[trivially_drop]76pub enum UnaryOpType {77	Plus,78	Minus,79	BitNot,80	Not,81}8283impl Display for UnaryOpType {84	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {85		use UnaryOpType::*;86		write!(87			f,88			"{}",89			match self {90				Plus => "+",91				Minus => "-",92				BitNot => "~",93				Not => "!",94			}95		)96	}97}9899#[cfg_attr(feature = "serialize", derive(Serialize))]100#[cfg_attr(feature = "deserialize", derive(Deserialize))]101#[derive(Debug, Clone, Copy, PartialEq, Trace)]102#[trivially_drop]103pub enum BinaryOpType {104	Mul,105	Div,106107	/// Implemented as intrinsic, put here for completeness108	Mod,109110	Add,111	Sub,112113	Lhs,114	Rhs,115116	Lt,117	Gt,118	Lte,119	Gte,120121	BitAnd,122	BitOr,123	BitXor,124125	Eq,126	Neq,127128	And,129	Or,130131	// Equialent to std.objectHasEx(a, b, true)132	In,133}134135impl Display for BinaryOpType {136	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {137		use BinaryOpType::*;138		write!(139			f,140			"{}",141			match self {142				Mul => "*",143				Div => "/",144				Mod => "%",145				Add => "+",146				Sub => "-",147				Lhs => "<<",148				Rhs => ">>",149				Lt => "<",150				Gt => ">",151				Lte => "<=",152				Gte => ">=",153				BitAnd => "&",154				BitOr => "|",155				BitXor => "^",156				Eq => "==",157				Neq => "!=",158				And => "&&",159				Or => "||",160				In => "in",161			}162		)163	}164}165166/// name, default value167#[cfg_attr(feature = "serialize", derive(Serialize))]168#[cfg_attr(feature = "deserialize", derive(Deserialize))]169#[derive(Debug, PartialEq, Trace)]170#[trivially_drop]171pub struct Param(pub IStr, pub Option<LocExpr>);172173/// Defined function parameters174#[cfg_attr(feature = "serialize", derive(Serialize))]175#[cfg_attr(feature = "deserialize", derive(Deserialize))]176#[derive(Debug, Clone, PartialEq)]177pub struct ParamsDesc(pub Rc<Vec<Param>>);178179/// Safety:180/// AST is acyclic, and there should be no gc pointers181unsafe impl Trace for ParamsDesc {182	unsafe_empty_trace!();183}184impl Finalize for ParamsDesc {}185186impl Deref for ParamsDesc {187	type Target = Vec<Param>;188	fn deref(&self) -> &Self::Target {189		&self.0190	}191}192193#[cfg_attr(feature = "serialize", derive(Serialize))]194#[cfg_attr(feature = "deserialize", derive(Deserialize))]195#[derive(Debug, PartialEq, Trace)]196#[trivially_drop]197pub struct Arg(pub Option<String>, pub LocExpr);198199#[cfg_attr(feature = "serialize", derive(Serialize))]200#[cfg_attr(feature = "deserialize", derive(Deserialize))]201#[derive(Debug, PartialEq, Trace)]202#[trivially_drop]203pub struct ArgsDesc(pub Vec<Arg>);204205impl Deref for ArgsDesc {206	type Target = Vec<Arg>;207	fn deref(&self) -> &Self::Target {208		&self.0209	}210}211212#[cfg_attr(feature = "serialize", derive(Serialize))]213#[cfg_attr(feature = "deserialize", derive(Deserialize))]214#[derive(Debug, Clone, PartialEq, Trace)]215#[trivially_drop]216pub struct BindSpec {217	pub name: IStr,218	pub params: Option<ParamsDesc>,219	pub value: LocExpr,220}221222#[cfg_attr(feature = "serialize", derive(Serialize))]223#[cfg_attr(feature = "deserialize", derive(Deserialize))]224#[derive(Debug, PartialEq, Trace)]225#[trivially_drop]226pub struct IfSpecData(pub LocExpr);227228#[cfg_attr(feature = "serialize", derive(Serialize))]229#[cfg_attr(feature = "deserialize", derive(Deserialize))]230#[derive(Debug, PartialEq, Trace)]231#[trivially_drop]232pub struct ForSpecData(pub IStr, pub LocExpr);233234#[cfg_attr(feature = "serialize", derive(Serialize))]235#[cfg_attr(feature = "deserialize", derive(Deserialize))]236#[derive(Debug, PartialEq, Trace)]237#[trivially_drop]238pub enum CompSpec {239	IfSpec(IfSpecData),240	ForSpec(ForSpecData),241}242243#[cfg_attr(feature = "serialize", derive(Serialize))]244#[cfg_attr(feature = "deserialize", derive(Deserialize))]245#[derive(Debug, PartialEq, Trace)]246#[trivially_drop]247pub struct ObjComp {248	pub pre_locals: Vec<BindSpec>,249	pub key: LocExpr,250	pub value: LocExpr,251	pub post_locals: Vec<BindSpec>,252	pub compspecs: Vec<CompSpec>,253}254255#[cfg_attr(feature = "serialize", derive(Serialize))]256#[cfg_attr(feature = "deserialize", derive(Deserialize))]257#[derive(Debug, PartialEq, Trace)]258#[trivially_drop]259pub enum ObjBody {260	MemberList(Vec<Member>),261	ObjComp(ObjComp),262}263264#[cfg_attr(feature = "serialize", derive(Serialize))]265#[cfg_attr(feature = "deserialize", derive(Deserialize))]266#[derive(Debug, PartialEq, Clone, Copy, Trace)]267#[trivially_drop]268pub enum LiteralType {269	This,270	Super,271	Dollar,272	Null,273	True,274	False,275}276277#[derive(Debug, PartialEq, Trace)]278#[trivially_drop]279pub struct SliceDesc {280	pub start: Option<LocExpr>,281	pub end: Option<LocExpr>,282	pub step: Option<LocExpr>,283}284285/// Syntax base286#[cfg_attr(feature = "serialize", derive(Serialize))]287#[cfg_attr(feature = "deserialize", derive(Deserialize))]288#[derive(Debug, PartialEq, Trace)]289#[trivially_drop]290pub enum Expr {291	Literal(LiteralType),292293	/// String value: "hello"294	Str(IStr),295	/// Number: 1, 2.0, 2e+20296	Num(f64),297	/// Variable name: test298	Var(IStr),299300	/// Array of expressions: [1, 2, "Hello"]301	Arr(Vec<LocExpr>),302	/// Array comprehension:303	/// ```jsonnet304	///  ingredients: [305	///    { kind: kind, qty: 4 / 3 }306	///    for kind in [307	///      'Honey Syrup',308	///      'Lemon Juice',309	///      'Farmers Gin',310	///    ]311	///  ],312	/// ```313	ArrComp(LocExpr, Vec<CompSpec>),314315	/// Object: {a: 2}316	Obj(ObjBody),317	/// Object extension: var1 {b: 2}318	ObjExtend(LocExpr, ObjBody),319320	/// (obj)321	Parened(LocExpr),322323	/// -2324	UnaryOp(UnaryOpType, LocExpr),325	/// 2 - 2326	BinaryOp(LocExpr, BinaryOpType, LocExpr),327	/// assert 2 == 2 : "Math is broken"328	AssertExpr(AssertStmt, LocExpr),329	/// local a = 2; { b: a }330	LocalExpr(Vec<BindSpec>, LocExpr),331332	/// import "hello"333	Import(PathBuf),334	/// importStr "file.txt"335	ImportStr(PathBuf),336	/// error "I'm broken"337	ErrorStmt(LocExpr),338	/// a(b, c)339	Apply(LocExpr, ArgsDesc, bool),340	/// a[b]341	Index(LocExpr, LocExpr),342	/// function(x) x343	Function(ParamsDesc, LocExpr),344	/// std.primitiveEquals345	Intrinsic(IStr),346	/// if true == false then 1 else 2347	IfElse {348		cond: IfSpecData,349		cond_then: LocExpr,350		cond_else: Option<LocExpr>,351	},352}353354/// file, begin offset, end offset355#[cfg_attr(feature = "serialize", derive(Serialize))]356#[cfg_attr(feature = "deserialize", derive(Deserialize))]357#[derive(Clone, PartialEq, Trace)]358#[trivially_drop]359pub struct ExprLocation(pub Rc<Path>, pub usize, pub usize);360361impl Debug for ExprLocation {362	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {363		write!(f, "{:?}:{:?}-{:?}", self.0, self.1, self.2)364	}365}366367/// Holds AST expression and its location in source file368#[cfg_attr(feature = "serialize", derive(Serialize))]369#[cfg_attr(feature = "deserialize", derive(Deserialize))]370#[derive(Clone, PartialEq)]371pub struct LocExpr(pub Rc<Expr>, pub Option<ExprLocation>);372/// Safety:373/// AST is acyclic, and there should be no gc pointers374unsafe impl Trace for LocExpr {375	unsafe_empty_trace!();376}377impl Finalize for LocExpr {}378379impl Debug for LocExpr {380	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {381		if f.alternate() {382			write!(f, "{:#?}", self.0)?;383		} else {384			write!(f, "{:?}", self.0)?;385		}386		if let Some(loc) = &self.1 {387			write!(f, " from {:?}", loc)?;388		}389		Ok(())390	}391}392393/// Creates LocExpr from Expr and ExprLocation components394#[macro_export]395macro_rules! loc_expr {396	($expr:expr, $need_loc:expr,($name:expr, $start:expr, $end:expr)) => {397		LocExpr(398			std::rc::Rc::new($expr),399			if $need_loc {400				Some(ExprLocation($name, $start, $end))401			} else {402				None403			},404		)405	};406}407408/// Creates LocExpr without location info409#[macro_export]410macro_rules! loc_expr_todo {411	($expr:expr) => {412		LocExpr(Rc::new($expr), None)413	};414}
after · crates/jrsonnet-parser/src/expr.rs
1use jrsonnet_gc::{unsafe_empty_trace, Finalize, Trace};2use jrsonnet_interner::IStr;3#[cfg(feature = "deserialize")]4use serde::Deserialize;5#[cfg(feature = "serialize")]6use serde::Serialize;7use std::{8	fmt::{Debug, Display},9	ops::Deref,10	path::{Path, PathBuf},11	rc::Rc,12};1314#[cfg_attr(feature = "serialize", derive(Serialize))]15#[cfg_attr(feature = "deserialize", derive(Deserialize))]16#[derive(Debug, PartialEq, Trace)]17#[trivially_drop]18pub enum FieldName {19	/// {fixed: 2}20	Fixed(IStr),21	/// {["dyn"+"amic"]: 3}22	Dyn(LocExpr),23}2425#[cfg_attr(feature = "serialize", derive(Serialize))]26#[cfg_attr(feature = "deserialize", derive(Deserialize))]27#[derive(Debug, Clone, Copy, PartialEq, Trace)]28#[trivially_drop]29pub enum Visibility {30	/// :31	Normal,32	/// ::33	Hidden,34	/// :::35	Unhide,36}3738impl Visibility {39	pub fn is_visible(&self) -> bool {40		matches!(self, Self::Normal | Self::Unhide)41	}42}4344#[cfg_attr(feature = "serialize", derive(Serialize))]45#[cfg_attr(feature = "deserialize", derive(Deserialize))]46#[derive(Clone, Debug, PartialEq, Trace)]47#[trivially_drop]48pub struct AssertStmt(pub LocExpr, pub Option<LocExpr>);4950#[cfg_attr(feature = "serialize", derive(Serialize))]51#[cfg_attr(feature = "deserialize", derive(Deserialize))]52#[derive(Debug, PartialEq, Trace)]53#[trivially_drop]54pub struct FieldMember {55	pub name: FieldName,56	pub plus: bool,57	pub params: Option<ParamsDesc>,58	pub visibility: Visibility,59	pub value: LocExpr,60}6162#[cfg_attr(feature = "serialize", derive(Serialize))]63#[cfg_attr(feature = "deserialize", derive(Deserialize))]64#[derive(Debug, PartialEq, Trace)]65#[trivially_drop]66pub enum Member {67	Field(FieldMember),68	BindStmt(BindSpec),69	AssertStmt(AssertStmt),70}7172#[cfg_attr(feature = "serialize", derive(Serialize))]73#[cfg_attr(feature = "deserialize", derive(Deserialize))]74#[derive(Debug, Clone, Copy, PartialEq, Trace)]75#[trivially_drop]76pub enum UnaryOpType {77	Plus,78	Minus,79	BitNot,80	Not,81}8283impl Display for UnaryOpType {84	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {85		use UnaryOpType::*;86		write!(87			f,88			"{}",89			match self {90				Plus => "+",91				Minus => "-",92				BitNot => "~",93				Not => "!",94			}95		)96	}97}9899#[cfg_attr(feature = "serialize", derive(Serialize))]100#[cfg_attr(feature = "deserialize", derive(Deserialize))]101#[derive(Debug, Clone, Copy, PartialEq, Trace)]102#[trivially_drop]103pub enum BinaryOpType {104	Mul,105	Div,106107	/// Implemented as intrinsic, put here for completeness108	Mod,109110	Add,111	Sub,112113	Lhs,114	Rhs,115116	Lt,117	Gt,118	Lte,119	Gte,120121	BitAnd,122	BitOr,123	BitXor,124125	Eq,126	Neq,127128	And,129	Or,130131	// Equialent to std.objectHasEx(a, b, true)132	In,133}134135impl Display for BinaryOpType {136	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {137		use BinaryOpType::*;138		write!(139			f,140			"{}",141			match self {142				Mul => "*",143				Div => "/",144				Mod => "%",145				Add => "+",146				Sub => "-",147				Lhs => "<<",148				Rhs => ">>",149				Lt => "<",150				Gt => ">",151				Lte => "<=",152				Gte => ">=",153				BitAnd => "&",154				BitOr => "|",155				BitXor => "^",156				Eq => "==",157				Neq => "!=",158				And => "&&",159				Or => "||",160				In => "in",161			}162		)163	}164}165166/// name, default value167#[cfg_attr(feature = "serialize", derive(Serialize))]168#[cfg_attr(feature = "deserialize", derive(Deserialize))]169#[derive(Debug, PartialEq, Trace)]170#[trivially_drop]171pub struct Param(pub IStr, pub Option<LocExpr>);172173/// Defined function parameters174#[cfg_attr(feature = "serialize", derive(Serialize))]175#[cfg_attr(feature = "deserialize", derive(Deserialize))]176#[derive(Debug, Clone, PartialEq)]177pub struct ParamsDesc(pub Rc<Vec<Param>>);178179/// Safety:180/// AST is acyclic, and there should be no gc pointers181unsafe impl Trace for ParamsDesc {182	unsafe_empty_trace!();183}184impl Finalize for ParamsDesc {}185186impl Deref for ParamsDesc {187	type Target = Vec<Param>;188	fn deref(&self) -> &Self::Target {189		&self.0190	}191}192193#[cfg_attr(feature = "serialize", derive(Serialize))]194#[cfg_attr(feature = "deserialize", derive(Deserialize))]195#[derive(Debug, PartialEq, Trace)]196#[trivially_drop]197pub struct Arg(pub Option<String>, pub LocExpr);198199#[cfg_attr(feature = "serialize", derive(Serialize))]200#[cfg_attr(feature = "deserialize", derive(Deserialize))]201#[derive(Debug, PartialEq, Trace)]202#[trivially_drop]203pub struct ArgsDesc(pub Vec<Arg>);204205impl Deref for ArgsDesc {206	type Target = Vec<Arg>;207	fn deref(&self) -> &Self::Target {208		&self.0209	}210}211212#[cfg_attr(feature = "serialize", derive(Serialize))]213#[cfg_attr(feature = "deserialize", derive(Deserialize))]214#[derive(Debug, Clone, PartialEq, Trace)]215#[trivially_drop]216pub struct BindSpec {217	pub name: IStr,218	pub params: Option<ParamsDesc>,219	pub value: LocExpr,220}221222#[cfg_attr(feature = "serialize", derive(Serialize))]223#[cfg_attr(feature = "deserialize", derive(Deserialize))]224#[derive(Debug, PartialEq, Trace)]225#[trivially_drop]226pub struct IfSpecData(pub LocExpr);227228#[cfg_attr(feature = "serialize", derive(Serialize))]229#[cfg_attr(feature = "deserialize", derive(Deserialize))]230#[derive(Debug, PartialEq, Trace)]231#[trivially_drop]232pub struct ForSpecData(pub IStr, pub LocExpr);233234#[cfg_attr(feature = "serialize", derive(Serialize))]235#[cfg_attr(feature = "deserialize", derive(Deserialize))]236#[derive(Debug, PartialEq, Trace)]237#[trivially_drop]238pub enum CompSpec {239	IfSpec(IfSpecData),240	ForSpec(ForSpecData),241}242243#[cfg_attr(feature = "serialize", derive(Serialize))]244#[cfg_attr(feature = "deserialize", derive(Deserialize))]245#[derive(Debug, PartialEq, Trace)]246#[trivially_drop]247pub struct ObjComp {248	pub pre_locals: Vec<BindSpec>,249	pub key: LocExpr,250	pub value: LocExpr,251	pub post_locals: Vec<BindSpec>,252	pub compspecs: Vec<CompSpec>,253}254255#[cfg_attr(feature = "serialize", derive(Serialize))]256#[cfg_attr(feature = "deserialize", derive(Deserialize))]257#[derive(Debug, PartialEq, Trace)]258#[trivially_drop]259pub enum ObjBody {260	MemberList(Vec<Member>),261	ObjComp(ObjComp),262}263264#[cfg_attr(feature = "serialize", derive(Serialize))]265#[cfg_attr(feature = "deserialize", derive(Deserialize))]266#[derive(Debug, PartialEq, Clone, Copy, Trace)]267#[trivially_drop]268pub enum LiteralType {269	This,270	Super,271	Dollar,272	Null,273	True,274	False,275}276277#[cfg_attr(feature = "serialize", derive(Serialize))]278#[cfg_attr(feature = "deserialize", derive(Deserialize))]279#[derive(Debug, PartialEq, Trace)]280#[trivially_drop]281pub struct SliceDesc {282	pub start: Option<LocExpr>,283	pub end: Option<LocExpr>,284	pub step: Option<LocExpr>,285}286287/// Syntax base288#[cfg_attr(feature = "serialize", derive(Serialize))]289#[cfg_attr(feature = "deserialize", derive(Deserialize))]290#[derive(Debug, PartialEq, Trace)]291#[trivially_drop]292pub enum Expr {293	Literal(LiteralType),294295	/// String value: "hello"296	Str(IStr),297	/// Number: 1, 2.0, 2e+20298	Num(f64),299	/// Variable name: test300	Var(IStr),301302	/// Array of expressions: [1, 2, "Hello"]303	Arr(Vec<LocExpr>),304	/// Array comprehension:305	/// ```jsonnet306	///  ingredients: [307	///    { kind: kind, qty: 4 / 3 }308	///    for kind in [309	///      'Honey Syrup',310	///      'Lemon Juice',311	///      'Farmers Gin',312	///    ]313	///  ],314	/// ```315	ArrComp(LocExpr, Vec<CompSpec>),316317	/// Object: {a: 2}318	Obj(ObjBody),319	/// Object extension: var1 {b: 2}320	ObjExtend(LocExpr, ObjBody),321322	/// (obj)323	Parened(LocExpr),324325	/// -2326	UnaryOp(UnaryOpType, LocExpr),327	/// 2 - 2328	BinaryOp(LocExpr, BinaryOpType, LocExpr),329	/// assert 2 == 2 : "Math is broken"330	AssertExpr(AssertStmt, LocExpr),331	/// local a = 2; { b: a }332	LocalExpr(Vec<BindSpec>, LocExpr),333334	/// import "hello"335	Import(PathBuf),336	/// importStr "file.txt"337	ImportStr(PathBuf),338	/// error "I'm broken"339	ErrorStmt(LocExpr),340	/// a(b, c)341	Apply(LocExpr, ArgsDesc, bool),342	/// a[b]343	Index(LocExpr, LocExpr),344	/// function(x) x345	Function(ParamsDesc, LocExpr),346	/// std.primitiveEquals347	Intrinsic(IStr),348	/// if true == false then 1 else 2349	IfElse {350		cond: IfSpecData,351		cond_then: LocExpr,352		cond_else: Option<LocExpr>,353	},354	Slice(LocExpr, SliceDesc),355}356357/// file, begin offset, end offset358#[cfg_attr(feature = "serialize", derive(Serialize))]359#[cfg_attr(feature = "deserialize", derive(Deserialize))]360#[derive(Clone, PartialEq, Trace)]361#[trivially_drop]362pub struct ExprLocation(pub Rc<Path>, pub usize, pub usize);363364impl Debug for ExprLocation {365	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {366		write!(f, "{:?}:{:?}-{:?}", self.0, self.1, self.2)367	}368}369370/// Holds AST expression and its location in source file371#[cfg_attr(feature = "serialize", derive(Serialize))]372#[cfg_attr(feature = "deserialize", derive(Deserialize))]373#[derive(Clone, PartialEq)]374pub struct LocExpr(pub Rc<Expr>, pub Option<ExprLocation>);375/// Safety:376/// AST is acyclic, and there should be no gc pointers377unsafe impl Trace for LocExpr {378	unsafe_empty_trace!();379}380impl Finalize for LocExpr {}381382impl Debug for LocExpr {383	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {384		if f.alternate() {385			write!(f, "{:#?}", self.0)?;386		} else {387			write!(f, "{:?}", self.0)?;388		}389		if let Some(loc) = &self.1 {390			write!(f, " from {:?}", loc)?;391		}392		Ok(())393	}394}395396/// Creates LocExpr from Expr and ExprLocation components397#[macro_export]398macro_rules! loc_expr {399	($expr:expr, $need_loc:expr,($name:expr, $start:expr, $end:expr)) => {400		LocExpr(401			std::rc::Rc::new($expr),402			if $need_loc {403				Some(ExprLocation($name, $start, $end))404			} else {405				None406			},407		)408	};409}410411/// Creates LocExpr without location info412#[macro_export]413macro_rules! loc_expr_todo {414	($expr:expr) => {415		LocExpr(Rc::new($expr), None)416	};417}
modifiedcrates/jrsonnet-parser/src/lib.rsdiffbeforeafterboth
--- a/crates/jrsonnet-parser/src/lib.rs
+++ b/crates/jrsonnet-parser/src/lib.rs
@@ -14,6 +14,17 @@
 	pub file_name: Rc<Path>,
 }
 
+macro_rules! expr_bin {
+	($a:ident $op:ident $b:ident) => {
+		loc_expr_todo!(Expr::BinaryOp($a, $op, $b))
+	};
+}
+macro_rules! expr_un {
+	($op:ident $a:ident) => {
+		loc_expr_todo!(Expr::UnaryOp($op, $a))
+	};
+}
+
 parser! {
 	grammar jsonnet_parser() for str {
 		use peg::ParseLiteral;
@@ -219,54 +230,43 @@
 
 
 		use BinaryOpType::*;
+		use UnaryOpType::*;
 		rule expr(s: &ParserSettings) -> LocExpr
 			= start:position!() a:precedence! {
-				a:(@) _ binop(<"||">) _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, Or, b))}
+				a:(@) _ binop(<"||">) _ b:@ {expr_bin!(a Or b)}
 				--
-				a:(@) _ binop(<"&&">) _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, And, b))}
+				a:(@) _ binop(<"&&">) _ b:@ {expr_bin!(a And b)}
 				--
-				a:(@) _ binop(<"|">) _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, BitOr, b))}
+				a:(@) _ binop(<"|">) _ b:@ {expr_bin!(a BitOr b)}
 				--
-				a:@ _ binop(<"^">) _ b:(@) {loc_expr_todo!(Expr::BinaryOp(a, BitXor, b))}
+				a:@ _ binop(<"^">) _ b:(@) {expr_bin!(a BitXor b)}
 				--
-				a:(@) _ binop(<"&">) _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, BitAnd, b))}
+				a:(@) _ binop(<"&">) _ b:@ {expr_bin!(a BitAnd b)}
 				--
-				a:(@) _ binop(<"==">) _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, Eq, b))}
-				a:(@) _ binop(<"!=">) _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, Neq, b))}
+				a:(@) _ binop(<"==">) _ b:@ {expr_bin!(a Eq b)}
+				a:(@) _ binop(<"!=">) _ b:@ {expr_bin!(a Neq b)}
 				--
-				a:(@) _ binop(<"<">) _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, Lt, b))}
-				a:(@) _ binop(<">">) _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, Gt, b))}
-				a:(@) _ binop(<"<=">) _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, Lte, b))}
-				a:(@) _ binop(<">=">) _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, Gte, b))}
-				a:(@) _ binop(<keyword("in")>) _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, In, b))}
+				a:(@) _ binop(<"<">) _ b:@ {expr_bin!(a Lt b)}
+				a:(@) _ binop(<">">) _ b:@ {expr_bin!(a Gt b)}
+				a:(@) _ binop(<"<=">) _ b:@ {expr_bin!(a Lte b)}
+				a:(@) _ binop(<">=">) _ b:@ {expr_bin!(a Gte b)}
+				a:(@) _ binop(<keyword("in")>) _ b:@ {expr_bin!(a In b)}
 				--
-				a:(@) _ binop(<"<<">) _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, Lhs, b))}
-				a:(@) _ binop(<">>">) _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, Rhs, b))}
+				a:(@) _ binop(<"<<">) _ b:@ {expr_bin!(a Lhs b)}
+				a:(@) _ binop(<">>">) _ b:@ {expr_bin!(a Rhs b)}
 				--
-				a:(@) _ binop(<"+">) _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, Add, b))}
-				a:(@) _ binop(<"-">) _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, Sub, b))}
+				a:(@) _ binop(<"+">) _ b:@ {expr_bin!(a Add b)}
+				a:(@) _ binop(<"-">) _ b:@ {expr_bin!(a Sub b)}
 				--
-				a:(@) _ binop(<"*">) _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, Mul, b))}
-				a:(@) _ binop(<"/">) _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, Div, b))}
-				a:(@) _ binop(<"%">) _ b:@ {loc_expr_todo!(Expr::Apply(
-					el!(Expr::Intrinsic("mod".into())), ArgsDesc(vec![Arg(None, a), Arg(None, b)]),
-					false
-				))}
+				a:(@) _ binop(<"*">) _ b:@ {expr_bin!(a Mul b)}
+				a:(@) _ binop(<"/">) _ b:@ {expr_bin!(a Div b)}
+				a:(@) _ binop(<"%">) _ b:@ {expr_bin!(a Mod b)}
 				--
-						unaryop(<"-">) _ b:@ {loc_expr_todo!(Expr::UnaryOp(UnaryOpType::Minus, b))}
-						unaryop(<"!">) _ b:@ {loc_expr_todo!(Expr::UnaryOp(UnaryOpType::Not, b))}
-						unaryop(<"~">) _ b:@ { loc_expr_todo!(Expr::UnaryOp(UnaryOpType::BitNot, b)) }
+						unaryop(<"-">) _ b:@ {expr_un!(Minus b)}
+						unaryop(<"!">) _ b:@ {expr_un!(Not b)}
+						unaryop(<"~">) _ b:@ {expr_un!(BitNot b)}
 				--
-				a:(@) _ "[" _ s:slice_desc(s) _ "]" {loc_expr_todo!(Expr::Apply(
-					el!(Expr::Intrinsic("slice".into())),
-					ArgsDesc(vec![
-						Arg(None, a),
-						Arg(None, s.start.unwrap_or_else(||el!(Expr::Literal(LiteralType::Null)))),
-						Arg(None, s.end.unwrap_or_else(||el!(Expr::Literal(LiteralType::Null)))),
-						Arg(None, s.step.unwrap_or_else(||el!(Expr::Literal(LiteralType::Null)))),
-					]),
-					true,
-				))}
+				a:(@) _ "[" _ s:slice_desc(s) _ "]" {loc_expr_todo!(Expr::Slice(a, s))}
 				a:(@) _ "." _ s:$(id()) {loc_expr_todo!(Expr::Index(a, el!(Expr::Str(s.into()))))}
 				a:(@) _ "[" _ s:expr(s) _ "]" {loc_expr_todo!(Expr::Index(a, s))}
 				a:(@) _ "(" _ args:args(s) _ ")" ts:(_ keyword("tailstrict"))? {loc_expr_todo!(Expr::Apply(a, args, ts.is_some()))}