git.delta.rocks / jrsonnet / refs/commits / b89fdd32bd3a

difftreelog

source

crates/jrsonnet-ir/src/expr.rs10.3 KiBsourcehistory
1use std::{2	fmt::{self, Debug, Display},3	ops::{Deref, RangeInclusive},4};56use jrsonnet_gcmodule::Acyclic;7use jrsonnet_interner::IStr;89use crate::{10	NumValue,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}3334#[derive(Debug, Clone, PartialEq, Acyclic)]35pub enum TrivialVal {36	Null,37	Bool(bool),38	Num(NumValue),39	Str(IStr),40}4142impl Visibility {43	pub fn is_visible(&self) -> bool {44		matches!(self, Self::Normal | Self::Unhide)45	}46}4748#[derive(Debug, PartialEq, Acyclic)]49pub struct AssertStmt {50	pub assertion: Spanned<Expr>,51	pub message: Option<Expr>,52}5354#[derive(Debug, PartialEq, Acyclic)]55pub struct FieldMember {56	pub name: Spanned<FieldName>,57	pub plus: bool,58	pub params: Option<ExprParams>,59	pub visibility: Visibility,60	pub value: Expr,61}6263#[derive(Debug, PartialEq, Acyclic)]64pub enum Member {65	Field(FieldMember),66	BindStmt(BindSpec),67	AssertStmt(AssertStmt),68}6970#[derive(Debug, Clone, Copy, PartialEq, Eq, Acyclic)]71pub enum UnaryOpType {72	Plus,73	Minus,74	BitNot,75	Not,76}7778impl Display for UnaryOpType {79	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {80		use UnaryOpType::*;81		write!(82			f,83			"{}",84			match self {85				Plus => "+",86				Minus => "-",87				BitNot => "~",88				Not => "!",89			}90		)91	}92}9394#[derive(Debug, Clone, Copy, PartialEq, Eq, Acyclic)]95pub enum BinaryOpType {96	Mul,97	Div,9899	/// Implemented as intrinsic, put here for completeness100	Mod,101102	Add,103	Sub,104105	Lhs,106	Rhs,107108	Lt,109	Gt,110	Lte,111	Gte,112113	BitAnd,114	BitOr,115	BitXor,116117	Eq,118	Neq,119120	And,121	Or,122	#[cfg(feature = "exp-null-coaelse")]123	NullCoaelse,124125	// Equialent to std.objectHasEx(a, b, true)126	In,127}128129impl Display for BinaryOpType {130	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {131		use BinaryOpType::*;132		write!(133			f,134			"{}",135			match self {136				Mul => "*",137				Div => "/",138				Mod => "%",139				Add => "+",140				Sub => "-",141				Lhs => "<<",142				Rhs => ">>",143				Lt => "<",144				Gt => ">",145				Lte => "<=",146				Gte => ">=",147				BitAnd => "&",148				BitOr => "|",149				BitXor => "^",150				Eq => "==",151				Neq => "!=",152				And => "&&",153				Or => "||",154				In => "in",155				#[cfg(feature = "exp-null-coaelse")]156				NullCoaelse => "??",157			}158		)159	}160}161162/// name, default value163#[derive(Debug, PartialEq, Acyclic)]164pub struct ExprParam {165	pub destruct: Destruct,166	pub default: Option<Expr>,167}168169/// Defined function parameters170#[derive(Debug, PartialEq, Acyclic)]171pub struct ExprParams {172	pub exprs: Vec<ExprParam>,173	pub signature: FunctionSignature,174	pub(crate) binds_len: usize,175}176impl ExprParams {177	pub fn len(&self) -> usize {178		self.exprs.len()179	}180	pub fn is_empty(&self) -> bool {181		self.exprs.is_empty()182	}183184	pub fn binds_len(&self) -> usize {185		self.binds_len186	}187	pub fn new(exprs: Vec<ExprParam>) -> Self {188		Self {189			signature: FunctionSignature::new(190				exprs191					.iter()192					.map(|p| {193						ParamParse::new(194							p.destruct.name(),195							ParamDefault::exists(p.default.is_some()),196						)197					})198					.collect(),199			),200			binds_len: exprs.iter().map(|v| v.destruct.binds_len()).sum(),201			exprs,202		}203	}204}205206#[derive(Debug, PartialEq, Acyclic)]207pub struct ArgsDesc {208	pub unnamed: Vec<Expr>,209	pub names: Vec<IStr>,210	pub values: Vec<Expr>,211}212impl ArgsDesc {213	pub fn new(unnamed: Vec<Expr>, names: Vec<IStr>, values: Vec<Expr>) -> Self {214		Self {215			unnamed,216			names,217			values,218		}219	}220}221222#[derive(Debug, PartialEq, Eq, Acyclic)]223pub enum DestructRest {224	/// ...rest225	Keep(IStr),226	/// ...227	Drop,228}229230#[derive(Debug, PartialEq, Acyclic)]231pub enum Destruct {232	Full(Spanned<IStr>),233	#[cfg(feature = "exp-destruct")]234	Skip,235	#[cfg(feature = "exp-destruct")]236	Array {237		start: Vec<Destruct>,238		rest: Option<DestructRest>,239		end: Vec<Destruct>,240	},241	#[cfg(feature = "exp-destruct")]242	Object {243		#[allow(clippy::type_complexity)]244		fields: Vec<(IStr, Option<Destruct>, Option<Spanned<Expr>>)>,245		rest: Option<DestructRest>,246	},247}248impl Destruct {249	/// Name of destructure, used for function parameter names250	pub fn name(&self) -> ParamName {251		match self {252			Self::Full(name) => ParamName::Named(name.value.clone()),253			#[cfg(feature = "exp-destruct")]254			_ => ParamName::Unnamed,255		}256	}257	pub fn binds_len(&self) -> usize {258		#[cfg(feature = "exp-destruct")]259		fn cap_rest(rest: &Option<DestructRest>) -> usize {260			match rest {261				Some(DestructRest::Keep(_)) => 1,262				Some(DestructRest::Drop) => 0,263				None => 0,264			}265		}266		match self {267			Self::Full(_) => 1,268			#[cfg(feature = "exp-destruct")]269			Self::Skip => 0,270			#[cfg(feature = "exp-destruct")]271			Self::Array { start, rest, end } => {272				start.iter().map(Destruct::binds_len).sum::<usize>()273					+ end.iter().map(Destruct::binds_len).sum::<usize>()274					+ cap_rest(rest)275			}276			#[cfg(feature = "exp-destruct")]277			Self::Object { fields, rest } => {278				let mut out = 0;279				for (_, into, _) in fields {280					match into {281						Some(v) => out += v.binds_len(),282						// Field is destructured to default name283						None => out += 1,284					}285				}286				out + cap_rest(rest)287			}288		}289	}290}291292#[derive(Debug, PartialEq, Acyclic)]293pub enum BindSpec {294	Field {295		into: Destruct,296		value: Expr,297	},298	Function {299		name: Spanned<IStr>,300		params: ExprParams,301		value: Expr,302	},303}304impl BindSpec {305	pub fn binds_len(&self) -> usize {306		match self {307			BindSpec::Field { into, .. } => into.binds_len(),308			BindSpec::Function { .. } => 1,309		}310	}311}312313#[derive(Debug, PartialEq, Acyclic)]314pub struct IfSpecData {315	pub span: Span,316	pub cond: Expr,317}318319#[derive(Debug, PartialEq, Acyclic)]320pub struct ForSpecData {321	pub destruct: Destruct,322	pub over: Expr,323}324325#[cfg(feature = "exp-object-iteration")]326#[derive(Debug, PartialEq, Acyclic)]327pub struct ForObjSpecData {328	pub key: IStr,329	pub visibility: Visibility,330	pub value: Destruct,331	pub over: Expr,332}333334#[derive(Debug, PartialEq, Acyclic)]335pub enum CompSpec {336	IfSpec(IfSpecData),337	ForSpec(ForSpecData),338	#[cfg(feature = "exp-object-iteration")]339	ForObjSpec(ForObjSpecData),340}341342#[derive(Debug, PartialEq, Acyclic)]343pub struct ObjComp {344	pub locals: Vec<BindSpec>,345	pub field: Box<FieldMember>,346	pub compspecs: Vec<CompSpec>,347}348349#[derive(Debug, PartialEq, Acyclic)]350pub struct ObjMembers {351	pub locals: Vec<BindSpec>,352	pub asserts: Vec<AssertStmt>,353	pub fields: Vec<FieldMember>,354}355356#[derive(Debug, PartialEq, Acyclic)]357pub enum ObjBody {358	MemberList(ObjMembers),359	ObjComp(ObjComp),360}361362/// Object identity reference: `self`, `super`, or `$`.#[derive(Debug, PartialEq, Eq, Clone, Copy, Acyclic)]363#[derive(Debug, PartialEq, Acyclic)]364pub enum IdentityKind {365	This,366	Super,367	Dollar,368}369370#[derive(Debug, PartialEq, Acyclic)]371pub struct SliceDesc {372	pub start: Option<Spanned<Expr>>,373	pub end: Option<Spanned<Expr>>,374	pub step: Option<Spanned<Expr>>,375}376377#[derive(Debug, PartialEq, Acyclic)]378pub struct AssertExpr {379	pub assert: AssertStmt,380	pub rest: Expr,381}382383#[derive(Debug, PartialEq, Acyclic)]384pub struct BinaryOp {385	pub lhs: Expr,386	pub op: BinaryOpType,387	pub rhs: Expr,388}389390#[derive(Debug, PartialEq, Acyclic, Clone, Copy)]391pub enum ImportKind {392	Normal,393	Str,394	Bin,395}396397#[derive(Debug, PartialEq, Acyclic)]398pub struct IfElse {399	pub cond: IfSpecData,400	pub cond_then: Expr,401	pub cond_else: Option<Expr>,402}403404#[derive(Debug, PartialEq, Acyclic)]405pub struct Slice {406	pub value: Expr,407	pub slice: SliceDesc,408}409410/// Syntax base411#[derive(Debug, PartialEq, Acyclic)]412pub enum Expr {413	/// Object-identity reference: `self`, `super`, `$`.414	Identity(Span, IdentityKind),415416	/// Trivial value literal417	Trivial(TrivialVal),418419	/// Variable name: test420	Var(Spanned<IStr>),421422	/// Array of expressions: [1, 2, "Hello"]423	Arr(Vec<Expr>),424	/// Array comprehension:425	/// ```jsonnet426	///  ingredients: [427	///    { kind: kind, qty: 4 / 3 }428	///    for kind in [429	///      'Honey Syrup',430	///      'Lemon Juice',431	///      'Farmers Gin',432	///    ]433	///  ],434	/// ```435	ArrComp(Box<Expr>, Vec<CompSpec>),436437	/// Object: {a: 2}438	Obj(ObjBody),439	/// Object extension: var1 {b: 2}440	ObjExtend(Box<Expr>, ObjBody),441442	/// -2443	UnaryOp(UnaryOpType, Box<Expr>),444	/// 2 - 2445	BinaryOp(Box<BinaryOp>),446	/// assert 2 == 2 : "Math is broken"447	AssertExpr(Box<AssertExpr>),448	/// local a = 2; { b: a }449	LocalExpr(Vec<BindSpec>, Box<Expr>),450451	/// import* "hello"452	Import(Spanned<ImportKind>, Box<Expr>),453	/// error "I'm broken"454	ErrorStmt(Span, Box<Expr>),455	/// a(b, c)456	Apply(Box<Expr>, Spanned<ArgsDesc>, bool),457	/// a[b], a.b, a?.b458	Index {459		indexable: Box<Expr>,460		parts: Vec<IndexPart>,461	},462	/// function(x) x463	Function(Span, ExprParams, Box<Expr>),464	/// if true == false then 1 else 2465	IfElse(Box<IfElse>),466	Slice(Box<Slice>),467}468469#[derive(Debug, PartialEq, Acyclic)]470pub struct IndexPart {471	pub span: Span,472	pub value: Expr,473	#[cfg(feature = "exp-null-coaelse")]474	pub null_coaelse: bool,475}476477/// file, begin offset, end offset478#[derive(Clone, PartialEq, Eq, Acyclic)]479#[repr(C)]480pub struct Span(pub Source, pub u32, pub u32);481impl Span {482	pub fn belongs_to(&self, other: &Span) -> bool {483		other.0 == self.0 && other.1 <= self.1 && other.2 >= self.2484	}485	pub fn range(&self) -> RangeInclusive<usize> {486		let start = self.1;487		let mut end = self.2;488		if end > start {489			// Because it is originally exclusive490			end -= 1;491		}492		start as usize..=end as usize493	}494}495496#[cfg(target_pointer_width = "64")]497static_assertions::assert_eq_size!(Span, (usize, usize));498499impl Debug for Span {500	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {501		write!(f, "{:?}:{:?}-{:?}", self.0, self.1, self.2)502	}503}504505#[derive(Clone, PartialEq, Acyclic)]506pub struct Spanned<T: Acyclic> {507	pub value: T,508	pub span: Span,509}510impl<T: Acyclic> Deref for Spanned<T> {511	type Target = T;512	fn deref(&self) -> &Self::Target {513		&self.value514	}515}516impl<T: Acyclic> Spanned<T> {517	#[inline]518	pub fn new(value: T, span: Span) -> Self {519		Self { value, span }520	}521	pub fn map<U: Acyclic>(self, v: impl FnOnce(T) -> U) -> Spanned<U> {522		Spanned {523			span: self.span,524			value: v(self.value),525		}526	}527	pub fn as_ref<'a>(&'a self) -> Spanned<&'a T>528	where529		&'a T: Acyclic,530	{531		Spanned {532			span: self.span.clone(),533			value: &self.value,534		}535	}536}537538impl<T: Debug + Acyclic> Debug for Spanned<T> {539	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {540		let expr = &**self;541		if f.alternate() {542			write!(f, "{:#?}", expr)?;543		} else {544			write!(f, "{:?}", expr)?;545		}546		write!(f, " from {:?}", self.span)?;547		Ok(())548	}549}