jrsonnet_parser/
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};
13
14#[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}
24
25#[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}
37
38impl Visibility {
39	pub fn is_visible(&self) -> bool {
40		matches!(self, Self::Normal | Self::Unhide)
41	}
42}
43
44#[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>);
49
50#[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}
61
62#[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}
71
72#[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}
82
83impl 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}
98
99#[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,
106
107	/// Implemented as intrinsic, put here for completeness
108	Mod,
109
110	Add,
111	Sub,
112
113	Lhs,
114	Rhs,
115
116	Lt,
117	Gt,
118	Lte,
119	Gte,
120
121	BitAnd,
122	BitOr,
123	BitXor,
124
125	Eq,
126	Neq,
127
128	And,
129	Or,
130
131	// Equialent to std.objectHasEx(a, b, true)
132	In,
133}
134
135impl 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}
165
166/// name, default value
167#[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>);
172
173/// Defined function parameters
174#[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>>);
178
179/// Safety:
180/// AST is acyclic, and there should be no gc pointers
181unsafe impl Trace for ParamsDesc {
182	unsafe_empty_trace!();
183}
184impl Finalize for ParamsDesc {}
185
186impl Deref for ParamsDesc {
187	type Target = Vec<Param>;
188	fn deref(&self) -> &Self::Target {
189		&self.0
190	}
191}
192
193#[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);
198
199#[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>);
204
205impl Deref for ArgsDesc {
206	type Target = Vec<Arg>;
207	fn deref(&self) -> &Self::Target {
208		&self.0
209	}
210}
211
212#[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}
221
222#[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);
227
228#[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);
233
234#[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}
242
243#[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}
254
255#[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}
263
264#[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}
276
277#[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}
286
287/// Syntax base
288#[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),
294
295	/// String value: "hello"
296	Str(IStr),
297	/// Number: 1, 2.0, 2e+20
298	Num(f64),
299	/// Variable name: test
300	Var(IStr),
301
302	/// Array of expressions: [1, 2, "Hello"]
303	Arr(Vec<LocExpr>),
304	/// Array comprehension:
305	/// ```jsonnet
306	///  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>),
316
317	/// Object: {a: 2}
318	Obj(ObjBody),
319	/// Object extension: var1 {b: 2}
320	ObjExtend(LocExpr, ObjBody),
321
322	/// (obj)
323	Parened(LocExpr),
324
325	/// -2
326	UnaryOp(UnaryOpType, LocExpr),
327	/// 2 - 2
328	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),
333
334	/// 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) x
345	Function(ParamsDesc, LocExpr),
346	/// std.primitiveEquals
347	Intrinsic(IStr),
348	/// if true == false then 1 else 2
349	IfElse {
350		cond: IfSpecData,
351		cond_then: LocExpr,
352		cond_else: Option<LocExpr>,
353	},
354	Slice(LocExpr, SliceDesc),
355}
356
357/// file, begin offset, end offset
358#[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);
363
364impl 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}
369
370/// Holds AST expression and its location in source file
371#[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 pointers
377unsafe impl Trace for LocExpr {
378	unsafe_empty_trace!();
379}
380impl Finalize for LocExpr {}
381
382impl 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}
395
396/// Creates LocExpr from Expr and ExprLocation components
397#[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				None
406			},
407		)
408	};
409}
410
411/// Creates LocExpr without location info
412#[macro_export]
413macro_rules! loc_expr_todo {
414	($expr:expr) => {
415		LocExpr(Rc::new($expr), None)
416	};
417}