git.delta.rocks / jrsonnet / refs/commits / 1d941f87f4d5

difftreelog

feat Spanned::{map,as_ref}

xoxzrqynYaroslav Bolyukin2026-04-25parent: #d6e3ee6.patch.diff
in: master

1 file changed

modifiedcrates/jrsonnet-ir/src/expr.rsdiffbeforeafterboth
after · crates/jrsonnet-ir/src/expr.rs
1use std::{2	fmt::{self, Debug, Display},3	ops::{Deref, RangeInclusive},4	rc::Rc,5};67use jrsonnet_gcmodule::Acyclic;8use jrsonnet_interner::IStr;910use crate::{11	function::{FunctionSignature, ParamDefault, ParamName, ParamParse},12	source::Source,13};1415#[derive(Debug, PartialEq, Acyclic)]16pub enum FieldName {17	/// {fixed: 2}18	Fixed(IStr),19	/// {["dyn"+"amic"]: 3}20	Dyn(Expr),21}2223#[derive(Debug, Clone, Copy, PartialEq, Eq, Acyclic)]24#[repr(u8)]25pub enum Visibility {26	/// :27	Normal,28	/// ::29	Hidden,30	/// :::31	Unhide,32}3334impl Visibility {35	pub fn is_visible(&self) -> bool {36		matches!(self, Self::Normal | Self::Unhide)37	}38}3940#[derive(Debug, PartialEq, Acyclic)]41pub struct AssertStmt {42	pub assertion: Spanned<Expr>,43	pub message: Option<Expr>,44}4546#[derive(Debug, PartialEq, Acyclic)]47pub struct FieldMember {48	pub name: Spanned<FieldName>,49	pub plus: bool,50	pub params: Option<ExprParams>,51	pub visibility: Visibility,52	pub value: Rc<Expr>,53}5455#[derive(Debug, PartialEq, Acyclic)]56pub enum Member {57	Field(FieldMember),58	BindStmt(BindSpec),59	AssertStmt(AssertStmt),60}6162#[derive(Debug, Clone, Copy, PartialEq, Eq, Acyclic)]63pub enum UnaryOpType {64	Plus,65	Minus,66	BitNot,67	Not,68}6970impl Display for UnaryOpType {71	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {72		use UnaryOpType::*;73		write!(74			f,75			"{}",76			match self {77				Plus => "+",78				Minus => "-",79				BitNot => "~",80				Not => "!",81			}82		)83	}84}8586#[derive(Debug, Clone, Copy, PartialEq, Eq, Acyclic)]87pub enum BinaryOpType {88	Mul,89	Div,9091	/// Implemented as intrinsic, put here for completeness92	Mod,9394	Add,95	Sub,9697	Lhs,98	Rhs,99100	Lt,101	Gt,102	Lte,103	Gte,104105	BitAnd,106	BitOr,107	BitXor,108109	Eq,110	Neq,111112	And,113	Or,114	#[cfg(feature = "exp-null-coaelse")]115	NullCoaelse,116117	// Equialent to std.objectHasEx(a, b, true)118	In,119}120121impl Display for BinaryOpType {122	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {123		use BinaryOpType::*;124		write!(125			f,126			"{}",127			match self {128				Mul => "*",129				Div => "/",130				Mod => "%",131				Add => "+",132				Sub => "-",133				Lhs => "<<",134				Rhs => ">>",135				Lt => "<",136				Gt => ">",137				Lte => "<=",138				Gte => ">=",139				BitAnd => "&",140				BitOr => "|",141				BitXor => "^",142				Eq => "==",143				Neq => "!=",144				And => "&&",145				Or => "||",146				In => "in",147				#[cfg(feature = "exp-null-coaelse")]148				NullCoaelse => "??",149			}150		)151	}152}153154/// name, default value155#[derive(Debug, PartialEq, Acyclic)]156pub struct ExprParam {157	pub destruct: Destruct,158	pub default: Option<Rc<Expr>>,159}160161/// Defined function parameters162#[derive(Debug, Clone, PartialEq, Acyclic)]163pub struct ExprParams {164	pub exprs: Rc<Vec<ExprParam>>,165	pub signature: FunctionSignature,166	pub(crate) binds_len: usize,167}168impl ExprParams {169	pub fn len(&self) -> usize {170		self.exprs.len()171	}172	pub fn is_empty(&self) -> bool {173		self.exprs.is_empty()174	}175176	pub fn binds_len(&self) -> usize {177		self.binds_len178	}179	pub fn new(exprs: Vec<ExprParam>) -> Self {180		Self {181			signature: FunctionSignature::new(182				exprs183					.iter()184					.map(|p| {185						ParamParse::new(186							p.destruct.name(),187							ParamDefault::exists(p.default.is_some()),188						)189					})190					.collect(),191			),192			binds_len: exprs.iter().map(|v| v.destruct.binds_len()).sum(),193			exprs: Rc::new(exprs),194		}195	}196}197198#[derive(Debug, PartialEq, Acyclic)]199pub struct ArgsDesc {200	pub unnamed: Vec<Rc<Expr>>,201	pub names: Vec<IStr>,202	pub values: Vec<Rc<Expr>>,203}204impl ArgsDesc {205	pub fn new(unnamed: Vec<Rc<Expr>>, names: Vec<IStr>, values: Vec<Rc<Expr>>) -> Self {206		Self {207			unnamed,208			names,209			values,210		}211	}212}213214#[derive(Debug, Clone, PartialEq, Eq, Acyclic)]215pub enum DestructRest {216	/// ...rest217	Keep(IStr),218	/// ...219	Drop,220}221222#[derive(Debug, Clone, PartialEq, Acyclic)]223pub enum Destruct {224	Full(IStr),225	#[cfg(feature = "exp-destruct")]226	Skip,227	#[cfg(feature = "exp-destruct")]228	Array {229		start: Vec<Destruct>,230		rest: Option<DestructRest>,231		end: Vec<Destruct>,232	},233	#[cfg(feature = "exp-destruct")]234	Object {235		#[allow(clippy::type_complexity)]236		fields: Vec<(IStr, Option<Destruct>, Option<Rc<Spanned<Expr>>>)>,237		rest: Option<DestructRest>,238	},239}240impl Destruct {241	/// Name of destructure, used for function parameter names242	pub fn name(&self) -> ParamName {243		match self {244			Self::Full(name) => ParamName::Named(name.clone()),245			#[cfg(feature = "exp-destruct")]246			_ => ParamName::Unnamed,247		}248	}249	pub fn binds_len(&self) -> usize {250		#[cfg(feature = "exp-destruct")]251		fn cap_rest(rest: &Option<DestructRest>) -> usize {252			match rest {253				Some(DestructRest::Keep(_)) => 1,254				Some(DestructRest::Drop) => 0,255				None => 0,256			}257		}258		match self {259			Self::Full(_) => 1,260			#[cfg(feature = "exp-destruct")]261			Self::Skip => 0,262			#[cfg(feature = "exp-destruct")]263			Self::Array { start, rest, end } => {264				start.iter().map(Destruct::binds_len).sum::<usize>()265					+ end.iter().map(Destruct::binds_len).sum::<usize>()266					+ cap_rest(rest)267			}268			#[cfg(feature = "exp-destruct")]269			Self::Object { fields, rest } => {270				let mut out = 0;271				for (_, into, _) in fields {272					match into {273						Some(v) => out += v.binds_len(),274						// Field is destructured to default name275						None => out += 1,276					}277				}278				out + cap_rest(rest)279			}280		}281	}282}283284#[derive(Debug, PartialEq, Acyclic)]285pub enum BindSpec {286	Field {287		into: Destruct,288		value: Rc<Expr>,289	},290	Function {291		name: IStr,292		params: ExprParams,293		value: Rc<Expr>,294	},295}296impl BindSpec {297	pub fn binds_len(&self) -> usize {298		match self {299			BindSpec::Field { into, .. } => into.binds_len(),300			BindSpec::Function { .. } => 1,301		}302	}303}304305#[derive(Debug, PartialEq, Acyclic)]306pub struct IfSpecData {307	pub span: Span,308	pub cond: Expr,309}310311#[derive(Debug, PartialEq, Acyclic)]312pub struct ForSpecData {313	pub destruct: Destruct,314	pub over: Expr,315}316317#[derive(Debug, PartialEq, Acyclic)]318pub enum CompSpec {319	IfSpec(IfSpecData),320	ForSpec(ForSpecData),321}322323#[derive(Debug, PartialEq, Acyclic)]324pub struct ObjComp {325	pub locals: Rc<Vec<BindSpec>>,326	pub field: Rc<FieldMember>,327	pub compspecs: Vec<CompSpec>,328}329330#[derive(Debug, PartialEq, Acyclic)]331pub struct ObjMembers {332	pub locals: Rc<Vec<BindSpec>>,333	pub asserts: Rc<Vec<AssertStmt>>,334	pub fields: Vec<FieldMember>,335}336337#[derive(Debug, PartialEq, Acyclic)]338pub enum ObjBody {339	MemberList(ObjMembers),340	ObjComp(ObjComp),341}342343#[derive(Debug, PartialEq, Eq, Clone, Copy, Acyclic)]344pub enum LiteralType {345	This,346	Super,347	Dollar,348	Null,349	True,350	False,351}352353#[derive(Debug, PartialEq, Acyclic)]354pub struct SliceDesc {355	pub start: Option<Spanned<Expr>>,356	pub end: Option<Spanned<Expr>>,357	pub step: Option<Spanned<Expr>>,358}359360#[derive(Debug, PartialEq, Acyclic)]361pub struct AssertExpr {362	pub assert: AssertStmt,363	pub rest: Expr,364}365366#[derive(Debug, PartialEq, Acyclic)]367pub struct BinaryOp {368	pub lhs: Expr,369	pub op: BinaryOpType,370	pub rhs: Expr,371}372373#[derive(Debug, PartialEq, Acyclic)]374pub enum ImportKind {375	Normal,376	Str,377	Bin,378}379380#[derive(Debug, PartialEq, Acyclic)]381pub struct IfElse {382	pub cond: IfSpecData,383	pub cond_then: Expr,384	pub cond_else: Option<Expr>,385}386387#[derive(Debug, PartialEq, Acyclic)]388pub struct Slice {389	pub value: Expr,390	pub slice: SliceDesc,391}392393/// Syntax base394#[derive(Debug, PartialEq, Acyclic)]395pub enum Expr {396	Literal(LiteralType),397398	/// String value: "hello"399	Str(IStr),400	/// Number: 1, 2.0, 2e+20401	Num(f64),402	/// Variable name: test403	Var(Spanned<IStr>),404405	/// Array of expressions: [1, 2, "Hello"]406	Arr(Rc<Vec<Expr>>),407	/// Array comprehension:408	/// ```jsonnet409	///  ingredients: [410	///    { kind: kind, qty: 4 / 3 }411	///    for kind in [412	///      'Honey Syrup',413	///      'Lemon Juice',414	///      'Farmers Gin',415	///    ]416	///  ],417	/// ```418	ArrComp(Rc<Expr>, Vec<CompSpec>),419420	/// Object: {a: 2}421	Obj(ObjBody),422	/// Object extension: var1 {b: 2}423	ObjExtend(Rc<Expr>, ObjBody),424425	/// -2426	UnaryOp(UnaryOpType, Box<Expr>),427	/// 2 - 2428	BinaryOp(Box<BinaryOp>),429	/// assert 2 == 2 : "Math is broken"430	AssertExpr(Rc<AssertExpr>),431	/// local a = 2; { b: a }432	LocalExpr(Vec<BindSpec>, Box<Expr>),433434	/// import* "hello"435	Import(Spanned<ImportKind>, Box<Expr>),436	/// error "I'm broken"437	ErrorStmt(Span, Box<Expr>),438	/// a(b, c)439	Apply(Box<Expr>, Spanned<ArgsDesc>, bool),440	/// a[b], a.b, a?.b441	Index {442		indexable: Box<Expr>,443		parts: Vec<IndexPart>,444	},445	/// function(x) x446	Function(ExprParams, Rc<Expr>),447	/// if true == false then 1 else 2448	IfElse(Box<IfElse>),449	Slice(Box<Slice>),450}451452#[derive(Debug, PartialEq, Acyclic)]453pub struct IndexPart {454	pub span: Span,455	pub value: Expr,456	#[cfg(feature = "exp-null-coaelse")]457	pub null_coaelse: bool,458}459460/// file, begin offset, end offset461#[derive(Clone, PartialEq, Eq, Acyclic)]462#[repr(C)]463pub struct Span(pub Source, pub u32, pub u32);464impl Span {465	pub fn belongs_to(&self, other: &Span) -> bool {466		other.0 == self.0 && other.1 <= self.1 && other.2 >= self.2467	}468	pub fn range(&self) -> RangeInclusive<usize> {469		let start = self.1;470		let mut end = self.2;471		if end > start {472			// Because it is originally exclusive473			end -= 1;474		}475		start as usize..=end as usize476	}477}478479#[cfg(target_pointer_width = "64")]480static_assertions::assert_eq_size!(Span, (usize, usize));481482impl Debug for Span {483	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {484		write!(f, "{:?}:{:?}-{:?}", self.0, self.1, self.2)485	}486}487488#[derive(Clone, PartialEq, Acyclic)]489pub struct Spanned<T: Acyclic> {490	pub value: T,491	pub span: Span,492}493impl<T: Acyclic> Deref for Spanned<T> {494	type Target = T;495	fn deref(&self) -> &Self::Target {496		&self.value497	}498}499impl<T: Acyclic> Spanned<T> {500	#[inline]501	pub fn new(value: T, span: Span) -> Self {502		Self { value, span }503	}504	pub fn map<U: Acyclic>(self, v: impl FnOnce(T) -> U) -> Spanned<U> {505		Spanned {506			span: self.span,507			value: v(self.value),508		}509	}510	pub fn as_ref<'a>(&'a self) -> Spanned<&'a T>511	where512		&'a T: Acyclic,513	{514		Spanned {515			span: self.span.clone(),516			value: &self.value,517		}518	}519}520521impl<T: Debug + Acyclic> Debug for Spanned<T> {522	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {523		let expr = &**self;524		if f.alternate() {525			write!(f, "{:#?}", expr)?;526		} else {527			write!(f, "{:?}", expr)?;528		}529		write!(f, " from {:?}", self.span)?;530		Ok(())531	}532}