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

difftreelog

feat unify Arg and Typed handling for Thunk

Yaroslav Bolyukin2023-07-27parent: #a85a211.patch.diff
in: master

8 files changed

modifiedcrates/jrsonnet-evaluator/src/ctx.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/ctx.rs
+++ b/crates/jrsonnet-evaluator/src/ctx.rs
@@ -81,7 +81,7 @@
 	#[must_use]
 	pub fn into_future(self, ctx: Pending<Self>) -> Self {
 		{
-			ctx.0.borrow_mut().replace(self);
+			ctx.clone().fill(self);
 		}
 		ctx.unwrap()
 	}
modifiedcrates/jrsonnet-evaluator/src/dynamic.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/dynamic.rs
+++ b/crates/jrsonnet-evaluator/src/dynamic.rs
@@ -1,29 +1,49 @@
-use std::cell::RefCell;
+use std::cell::OnceCell;
 
 use jrsonnet_gcmodule::{Cc, Trace};
 
+use crate::{error::ErrorKind::InfiniteRecursionDetected, throw, val::ThunkValue, Result, Thunk};
+
 // TODO: Replace with OnceCell once in std
 #[derive(Clone, Trace)]
-pub struct Pending<V: Trace + 'static>(pub Cc<RefCell<Option<V>>>);
+pub struct Pending<V: Trace + 'static>(pub Cc<OnceCell<V>>);
 impl<T: Trace + 'static> Pending<T> {
 	pub fn new() -> Self {
-		Self(Cc::new(RefCell::new(None)))
+		Self(Cc::new(OnceCell::new()))
 	}
 	pub fn new_filled(v: T) -> Self {
-		Self(Cc::new(RefCell::new(Some(v))))
+		let cell = OnceCell::new();
+		let _ = cell.set(v);
+		Self(Cc::new(cell))
 	}
 	/// # Panics
 	/// If wrapper is filled already
 	pub fn fill(self, value: T) {
-		assert!(self.0.borrow().is_none(), "wrapper is filled already");
-		self.0.borrow_mut().replace(value);
+		self.0
+			.set(value)
+			.map_err(|_| ())
+			.expect("wrapper is filled already")
 	}
 }
 impl<T: Clone + Trace + 'static> Pending<T> {
 	/// # Panics
 	/// If wrapper is not yet filled
 	pub fn unwrap(&self) -> T {
-		self.0.borrow().as_ref().cloned().unwrap()
+		self.0.get().cloned().expect("pending was not filled")
+	}
+	pub fn try_get(&self) -> Option<T> {
+		self.0.get().cloned()
+	}
+}
+
+impl<T: Trace + Clone> ThunkValue for Pending<T> {
+	type Output = T;
+
+	fn get(self: Box<Self>) -> Result<Self::Output> {
+		let Some(value) = self.0.get() else {
+			throw!(InfiniteRecursionDetected);
+		};
+		Ok(value.clone())
 	}
 }
 
@@ -32,3 +52,9 @@
 		Self::new()
 	}
 }
+
+impl<T: Trace + Clone> Into<Thunk<T>> for Pending<T> {
+	fn into(self) -> Thunk<T> {
+		Thunk::new(self)
+	}
+}
modifiedcrates/jrsonnet-evaluator/src/function/arglike.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/function/arglike.rs
+++ b/crates/jrsonnet-evaluator/src/function/arglike.rs
@@ -48,28 +48,22 @@
 where
 	T: Typed + Clone,
 {
-	fn evaluate_arg(&self, _ctx: Context, _tailstrict: bool) -> Result<Thunk<Val>> {
+	fn evaluate_arg(&self, _ctx: Context, tailstrict: bool) -> Result<Thunk<Val>> {
+		if T::provides_lazy() && !tailstrict {
+			return Ok(T::into_lazy_untyped(self.clone()));
+		}
 		let val = T::into_untyped(self.clone())?;
 		Ok(Thunk::evaluated(val))
 	}
 }
 impl<T> OptionalContext for T where T: Typed + Clone {}
 
-impl ArgLike for Thunk<Val> {
-	fn evaluate_arg(&self, _ctx: Context, tailstrict: bool) -> Result<Thunk<Val>> {
-		if tailstrict {
-			self.force()?;
-		}
-		Ok(self.clone())
-	}
-}
-impl OptionalContext for Thunk<Val> {}
-
 #[derive(Clone, Trace)]
 pub enum TlaArg {
 	String(IStr),
 	Code(LocExpr),
 	Val(Val),
+	Lazy(Thunk<Val>),
 }
 impl ArgLike for TlaArg {
 	fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<Thunk<Val>> {
@@ -84,6 +78,7 @@
 				})
 			}),
 			TlaArg::Val(val) => Ok(Thunk::evaluated(val.clone())),
+			TlaArg::Lazy(lazy) => Ok(lazy.clone()),
 		}
 	}
 }
modifiedcrates/jrsonnet-evaluator/src/obj.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/obj.rs
+++ b/crates/jrsonnet-evaluator/src/obj.rs
@@ -15,7 +15,9 @@
 	function::CallLocation,
 	gc::{GcHashMap, GcHashSet, TraceBox},
 	operator::evaluate_add_op,
-	tb, throw, MaybeUnbound, Result, State, Thunk, Unbound, Val,
+	tb, throw,
+	val::ThunkValue,
+	MaybeUnbound, Result, State, Thunk, Unbound, Val,
 };
 
 #[cfg(not(feature = "exp-preserve-order"))]
modifiedcrates/jrsonnet-evaluator/src/typed/conversions.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/typed/conversions.rs
+++ b/crates/jrsonnet-evaluator/src/typed/conversions.rs
@@ -1,6 +1,6 @@
-use std::ops::Deref;
+use std::{collections::BTreeMap, marker::PhantomData, ops::Deref};
 
-use jrsonnet_gcmodule::Cc;
+use jrsonnet_gcmodule::{Cc, Trace};
 use jrsonnet_interner::{IBytes, IStr};
 pub use jrsonnet_macros::Typed;
 use jrsonnet_types::{ComplexValType, ValType};
@@ -11,10 +11,28 @@
 	function::{native::NativeDesc, FuncDesc, FuncVal},
 	throw,
 	typed::CheckType,
-	val::{IndexableVal, StrValue},
-	ObjValue, ObjValueBuilder, Val,
+	val::{IndexableVal, StrValue, ThunkMapper},
+	ObjValue, ObjValueBuilder, Thunk, Val,
 };
 
+#[derive(Trace)]
+struct FromUntyped<K: Trace>(PhantomData<fn() -> K>);
+impl<K> ThunkMapper<Val> for FromUntyped<K>
+where
+	K: Typed + Trace,
+{
+	type Output = K;
+
+	fn map(self, from: Val) -> Result<Self::Output> {
+		K::from_untyped(from)
+	}
+}
+impl<K: Trace> Default for FromUntyped<K> {
+	fn default() -> Self {
+		Self(PhantomData)
+	}
+}
+
 pub trait TypedObj: Typed {
 	fn serialize(self, out: &mut ObjValueBuilder) -> Result<()>;
 	fn parse(obj: &ObjValue) -> Result<Self>;
@@ -28,8 +46,24 @@
 pub trait Typed: Sized {
 	const TYPE: &'static ComplexValType;
 	fn into_untyped(typed: Self) -> Result<Val>;
+	fn into_lazy_untyped(typed: Self) -> Thunk<Val> {
+		Thunk::from(Self::into_untyped(typed))
+	}
 	fn from_untyped(untyped: Val) -> Result<Self>;
+	fn from_lazy_untyped(lazy: Thunk<Val>) -> Result<Self> {
+		Self::from_untyped(lazy.evaluate()?)
+	}
+
+	// Whatever caller should use `into_lazy_untyped` instead of `into_untyped`
+	fn provides_lazy() -> bool {
+		false
+	}
 
+	// Whatever caller should use `from_lazy_untyped` instead of `from_untyped` when possible
+	fn wants_lazy() -> bool {
+		false
+	}
+
 	/// Hack to make builtins be able to return non-result values, and make macros able to convert those values to result
 	/// This method returns identity in impl Typed for Result, and should not be overriden
 	#[doc(hidden)]
@@ -39,6 +73,54 @@
 	}
 }
 
+impl<T> Typed for Thunk<T>
+where
+	T: Typed + Trace + Clone,
+{
+	const TYPE: &'static ComplexValType = &ComplexValType::Lazy(T::TYPE);
+
+	fn into_untyped(typed: Self) -> Result<Val> {
+		T::into_untyped(typed.evaluate()?)
+	}
+
+	fn from_untyped(untyped: Val) -> Result<Self> {
+		Self::from_lazy_untyped(Thunk::evaluated(untyped))
+	}
+
+	fn provides_lazy() -> bool {
+		true
+	}
+
+	fn into_lazy_untyped(inner: Self) -> Thunk<Val> {
+		#[derive(Trace)]
+		struct IntoUntyped<K: Trace>(PhantomData<fn() -> K>);
+		impl<K> ThunkMapper<K> for IntoUntyped<K>
+		where
+			K: Typed + Trace,
+		{
+			type Output = Val;
+
+			fn map(self, from: K) -> Result<Self::Output> {
+				K::into_untyped(from)
+			}
+		}
+		impl<K: Trace> Default for IntoUntyped<K> {
+			fn default() -> Self {
+				Self(PhantomData)
+			}
+		}
+		inner.map(<IntoUntyped<T>>::default())
+	}
+
+	fn wants_lazy() -> bool {
+		true
+	}
+
+	fn from_lazy_untyped(inner: Thunk<Val>) -> Result<Self> {
+		Ok(inner.map(<FromUntyped<T>>::default()))
+	}
+}
+
 const MAX_SAFE_INTEGER: f64 = ((1u64 << (f64::MANTISSA_DIGITS + 1)) - 1) as f64;
 
 macro_rules! impl_int {
modifiedcrates/jrsonnet-evaluator/src/typed/mod.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/typed/mod.rs
+++ b/crates/jrsonnet-evaluator/src/typed/mod.rs
@@ -252,6 +252,7 @@
 				}
 				Ok(())
 			}
+			Self::Lazy(_lazy) => Ok(()),
 		}
 	}
 }
modifiedcrates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/val.rs
+++ b/crates/jrsonnet-evaluator/src/val.rs
@@ -88,6 +88,54 @@
 	}
 }
 
+pub trait ThunkMapper<Input>: Trace {
+	type Output;
+	fn map(self, from: Input) -> Result<Self::Output>;
+}
+impl<Input> Thunk<Input>
+where
+	Input: Trace + Clone,
+{
+	pub fn map<M>(self, mapper: M) -> Thunk<M::Output>
+	where
+		M: ThunkMapper<Input>,
+		M::Output: Trace,
+	{
+		#[derive(Trace)]
+		struct Mapped<Input: Trace, Mapper: Trace> {
+			inner: Thunk<Input>,
+			mapper: Mapper,
+		}
+		impl<Input, Mapper> ThunkValue for Mapped<Input, Mapper>
+		where
+			Input: Trace + Clone,
+			Mapper: ThunkMapper<Input>,
+		{
+			type Output = Mapper::Output;
+
+			fn get(self: Box<Self>) -> Result<Self::Output> {
+				let value = self.inner.evaluate()?;
+				let mapped = self.mapper.map(value)?;
+				Ok(mapped)
+			}
+		}
+
+		Thunk::new(Mapped::<Input, M> {
+			inner: self,
+			mapper,
+		})
+	}
+}
+
+impl<T: Trace> From<Result<T>> for Thunk<T> {
+	fn from(value: Result<T>) -> Self {
+		match value {
+			Ok(o) => Self::evaluated(o),
+			Err(e) => Self::errored(e),
+		}
+	}
+}
+
 type CacheKey = (Option<WeakObjValue>, Option<WeakObjValue>);
 
 #[derive(Trace, Clone)]
@@ -272,6 +320,11 @@
 		Self::Flat(value.into())
 	}
 }
+impl From<IStr> for StrValue {
+	fn from(value: IStr) -> Self {
+		Self::Flat(value)
+	}
+}
 impl Display for StrValue {
 	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 		match self {
modifiedcrates/jrsonnet-types/src/lib.rsdiffbeforeafterboth
before · crates/jrsonnet-types/src/lib.rs
1#![allow(clippy::redundant_closure_call)]23use std::fmt::Display;45use jrsonnet_gcmodule::Trace;67#[macro_export]8macro_rules! ty {9	((Array<number>)) => {{10		$crate::ComplexValType::ArrayRef(&$crate::ComplexValType::Simple($crate::ValType::Num))11	}};12	((Array<ubyte>)) => {{13		$crate::ComplexValType::ArrayRef(&$crate::ComplexValType::BoundedNumber(Some(0.0), Some(255.0)))14	}};15	(array) => {16		$crate::ComplexValType::Simple($crate::ValType::Arr)17	};18	(boolean) => {19		$crate::ComplexValType::Simple($crate::ValType::Bool)20	};21	(null) => {22		$crate::ComplexValType::Simple($crate::ValType::Null)23	};24	(string) => {25		$crate::ComplexValType::Simple($crate::ValType::Str)26	};27	(char) => {28		$crate::ComplexValType::Char29	};30	(number) => {31		$crate::ComplexValType::Simple($crate::ValType::Num)32	};33	(BoundedNumber<($min:expr), ($max:expr)>) => {{34		$crate::ComplexValType::BoundedNumber($min, $max)35	}};36	(object) => {37		$crate::ComplexValType::Simple($crate::ValType::Obj)38	};39	(any) => {40		$crate::ComplexValType::Any41	};42	(function) => {43		$crate::ComplexValType::Simple($crate::ValType::Func)44	};45	(($($a:tt) |+)) => {{46		static CONTENTS: &'static [&'static $crate::ComplexValType] = &[47			$(&ty!($a)),+48		];49		$crate::ComplexValType::UnionRef(CONTENTS)50	}};51	(($($a:tt) &+)) => {{52		static CONTENTS: &'static [&'static $crate::ComplexValType] = &[53			$(&ty!($a)),+54		];55		$crate::ComplexValType::SumRef(CONTENTS)56	}};57}5859#[test]60fn test() {61	assert_eq!(62		ty!((Array<number>)),63		ComplexValType::ArrayRef(&ComplexValType::Simple(ValType::Num))64	);65	assert_eq!(ty!(array), ComplexValType::Simple(ValType::Arr));66	assert_eq!(ty!(any), ComplexValType::Any);67	assert_eq!(68		ty!((string | number)),69		ComplexValType::UnionRef(&[70			&ComplexValType::Simple(ValType::Str),71			&ComplexValType::Simple(ValType::Num)72		])73	);74	assert_eq!(75		format!("{}", ty!(((string & number) | (object & null)))),76		"string & number | object & null"77	);78	assert_eq!(format!("{}", ty!((string | array))), "string | array");79	assert_eq!(80		format!("{}", ty!(((string & number) | array))),81		"string & number | array"82	);83}8485#[derive(Debug, Clone, Copy, PartialEq, Eq, Trace)]86pub enum ValType {87	Bool,88	Null,89	Str,90	Num,91	#[cfg(feature = "exp-bigint")]92	BigInt,93	Arr,94	Obj,95	Func,96}9798impl ValType {99	pub const fn name(&self) -> &'static str {100		use ValType::*;101		match self {102			Bool => "boolean",103			Null => "null",104			Str => "string",105			Num => "number",106			#[cfg(feature = "exp-bigint")]107			BigInt => "bigint",108			Arr => "array",109			Obj => "object",110			Func => "function",111		}112	}113}114115impl Display for ValType {116	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {117		write!(f, "{}", self.name())118	}119}120121#[derive(Debug, Clone, PartialEq, Trace)]122#[trace(skip)]123pub enum ComplexValType {124	Any,125	Char,126	Simple(ValType),127	BoundedNumber(Option<f64>, Option<f64>),128	Array(Box<ComplexValType>),129	ArrayRef(&'static ComplexValType),130	ObjectRef(&'static [(&'static str, &'static ComplexValType)]),131	Union(Vec<ComplexValType>),132	UnionRef(&'static [&'static ComplexValType]),133	Sum(Vec<ComplexValType>),134	SumRef(&'static [&'static ComplexValType]),135}136137impl From<ValType> for ComplexValType {138	fn from(s: ValType) -> Self {139		Self::Simple(s)140	}141}142143fn write_union<'i>(144	f: &mut std::fmt::Formatter<'_>,145	is_union: bool,146	union: impl Iterator<Item = &'i ComplexValType>,147) -> std::fmt::Result {148	for (i, v) in union.enumerate() {149		let should_add_braces =150			matches!(v, ComplexValType::UnionRef(_) | ComplexValType::Union(_) if !is_union);151		if i != 0 {152			write!(f, " {} ", if is_union { '|' } else { '&' })?;153		}154		if should_add_braces {155			write!(f, "(")?;156		}157		write!(f, "{v}")?;158		if should_add_braces {159			write!(f, ")")?;160		}161	}162	Ok(())163}164165fn print_array(a: &ComplexValType, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {166	if *a == ComplexValType::Any {167		write!(f, "array")?168	} else {169		write!(f, "Array<{a}>")?170	}171	Ok(())172}173174impl Display for ComplexValType {175	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {176		match self {177			ComplexValType::Any => write!(f, "any")?,178			ComplexValType::Simple(s) => write!(f, "{s}")?,179			ComplexValType::Char => write!(f, "char")?,180			ComplexValType::BoundedNumber(a, b) => write!(181				f,182				"BoundedNumber<{}, {}>",183				a.map(|e| e.to_string()).unwrap_or_else(|| "".into()),184				b.map(|e| e.to_string()).unwrap_or_else(|| "".into())185			)?,186			ComplexValType::ArrayRef(a) => print_array(a, f)?,187			ComplexValType::Array(a) => print_array(a, f)?,188			ComplexValType::ObjectRef(fields) => {189				write!(f, "{{")?;190				for (i, (k, v)) in fields.iter().enumerate() {191					if i != 0 {192						write!(f, ", ")?;193					}194					write!(f, "{k}: {v}")?;195				}196				write!(f, "}}")?;197			}198			ComplexValType::Union(v) => write_union(f, true, v.iter())?,199			ComplexValType::UnionRef(v) => write_union(f, true, v.iter().copied())?,200			ComplexValType::Sum(v) => write_union(f, false, v.iter())?,201			ComplexValType::SumRef(v) => write_union(f, false, v.iter().copied())?,202		};203		Ok(())204	}205}206207peg::parser! {208pub grammar parser() for str {209	rule number() -> f64210		= n:$(['0'..='9']+) { n.parse().unwrap() }211212	rule any_ty() -> ComplexValType = "any" { ComplexValType::Any }213	rule char_ty() -> ComplexValType = "character" { ComplexValType::Char }214	rule bool_ty() -> ComplexValType = "boolean" { ComplexValType::Simple(ValType::Bool) }215	rule null_ty() -> ComplexValType = "null" { ComplexValType::Simple(ValType::Null) }216	rule str_ty() -> ComplexValType = "string" { ComplexValType::Simple(ValType::Str) }217	rule num_ty() -> ComplexValType = "number" { ComplexValType::Simple(ValType::Num) }218	rule simple_array_ty() -> ComplexValType = "array" { ComplexValType::Simple(ValType::Arr) }219	rule simple_object_ty() -> ComplexValType = "object" { ComplexValType::Simple(ValType::Obj) }220	rule simple_function_ty() -> ComplexValType = "function" { ComplexValType::Simple(ValType::Func) }221222	rule array_ty() -> ComplexValType223		= "Array<" t:ty() ">" { ComplexValType::Array(Box::new(t)) }224225	rule bounded_number_ty() -> ComplexValType226		= "BoundedNumber<" a:number() ", " b:number() ">" { ComplexValType::BoundedNumber(Some(a), Some(b)) }227228	rule ty_basic() -> ComplexValType229		= any_ty()230		/ char_ty()231		/ bool_ty()232		/ null_ty()233		/ str_ty()234		/ num_ty()235		/ simple_array_ty()236		/ simple_object_ty()237		/ simple_function_ty()238		/ array_ty()239		/ bounded_number_ty()240241	pub rule ty() -> ComplexValType242		= precedence! {243			a:(@) " | " b:@ {244				match a {245					ComplexValType::Union(mut a) => {246						a.push(b);247						ComplexValType::Union(a)248					}249					_ => ComplexValType::Union(vec![a, b]),250				}251			}252			--253			a:(@) " & " b:@ {254				match a {255					ComplexValType::Sum(mut a) => {256						a.push(b);257						ComplexValType::Sum(a)258					}259					_ => ComplexValType::Sum(vec![a, b]),260				}261			}262			--263			"(" t:ty() ")" { t }264			t:ty_basic() { t }265		}266}267}268269#[cfg(test)]270pub mod tests {271	use super::parser;272273	#[test]274	fn precedence() {275		assert_eq!(276			parser::ty("(any & any) | (any | any) & any")277				.unwrap()278				.to_string(),279			"any & any | (any | any) & any"280		);281	}282283	#[test]284	fn array() {285		assert_eq!(parser::ty("Array<any>").unwrap().to_string(), "array");286		assert_eq!(287			parser::ty("Array<number>").unwrap().to_string(),288			"Array<number>"289		);290	}291	#[test]292	fn bounded_number() {293		assert_eq!(294			parser::ty("BoundedNumber<1, 2>").unwrap().to_string(),295			"BoundedNumber<1, 2>"296		);297	}298}
after · crates/jrsonnet-types/src/lib.rs
1#![allow(clippy::redundant_closure_call)]23use std::fmt::Display;45use jrsonnet_gcmodule::Trace;67#[macro_export]8macro_rules! ty {9	((Array<number>)) => {{10		$crate::ComplexValType::ArrayRef(&$crate::ComplexValType::Simple($crate::ValType::Num))11	}};12	((Array<ubyte>)) => {{13		$crate::ComplexValType::ArrayRef(&$crate::ComplexValType::BoundedNumber(Some(0.0), Some(255.0)))14	}};15	(array) => {16		$crate::ComplexValType::Simple($crate::ValType::Arr)17	};18	(boolean) => {19		$crate::ComplexValType::Simple($crate::ValType::Bool)20	};21	(null) => {22		$crate::ComplexValType::Simple($crate::ValType::Null)23	};24	(string) => {25		$crate::ComplexValType::Simple($crate::ValType::Str)26	};27	(char) => {28		$crate::ComplexValType::Char29	};30	(number) => {31		$crate::ComplexValType::Simple($crate::ValType::Num)32	};33	(BoundedNumber<($min:expr), ($max:expr)>) => {{34		$crate::ComplexValType::BoundedNumber($min, $max)35	}};36	(object) => {37		$crate::ComplexValType::Simple($crate::ValType::Obj)38	};39	(any) => {40		$crate::ComplexValType::Any41	};42	(function) => {43		$crate::ComplexValType::Simple($crate::ValType::Func)44	};45	(($($a:tt) |+)) => {{46		static CONTENTS: &'static [&'static $crate::ComplexValType] = &[47			$(&ty!($a)),+48		];49		$crate::ComplexValType::UnionRef(CONTENTS)50	}};51	(($($a:tt) &+)) => {{52		static CONTENTS: &'static [&'static $crate::ComplexValType] = &[53			$(&ty!($a)),+54		];55		$crate::ComplexValType::SumRef(CONTENTS)56	}};57}5859#[test]60fn test() {61	assert_eq!(62		ty!((Array<number>)),63		ComplexValType::ArrayRef(&ComplexValType::Simple(ValType::Num))64	);65	assert_eq!(ty!(array), ComplexValType::Simple(ValType::Arr));66	assert_eq!(ty!(any), ComplexValType::Any);67	assert_eq!(68		ty!((string | number)),69		ComplexValType::UnionRef(&[70			&ComplexValType::Simple(ValType::Str),71			&ComplexValType::Simple(ValType::Num)72		])73	);74	assert_eq!(75		format!("{}", ty!(((string & number) | (object & null)))),76		"string & number | object & null"77	);78	assert_eq!(format!("{}", ty!((string | array))), "string | array");79	assert_eq!(80		format!("{}", ty!(((string & number) | array))),81		"string & number | array"82	);83}8485#[derive(Debug, Clone, Copy, PartialEq, Eq, Trace)]86pub enum ValType {87	Bool,88	Null,89	Str,90	Num,91	#[cfg(feature = "exp-bigint")]92	BigInt,93	Arr,94	Obj,95	Func,96}9798impl ValType {99	pub const fn name(&self) -> &'static str {100		use ValType::*;101		match self {102			Bool => "boolean",103			Null => "null",104			Str => "string",105			Num => "number",106			#[cfg(feature = "exp-bigint")]107			BigInt => "bigint",108			Arr => "array",109			Obj => "object",110			Func => "function",111		}112	}113}114115impl Display for ValType {116	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {117		write!(f, "{}", self.name())118	}119}120121#[derive(Debug, Clone, PartialEq, Trace)]122#[trace(skip)]123pub enum ComplexValType {124	Any,125	Char,126	Simple(ValType),127	BoundedNumber(Option<f64>, Option<f64>),128	Array(Box<ComplexValType>),129	ArrayRef(&'static ComplexValType),130	ObjectRef(&'static [(&'static str, &'static ComplexValType)]),131	AttrsOf(&'static ComplexValType),132	Union(Vec<ComplexValType>),133	UnionRef(&'static [&'static ComplexValType]),134	Sum(Vec<ComplexValType>),135	SumRef(&'static [&'static ComplexValType]),136	Lazy(&'static ComplexValType),137}138139impl From<ValType> for ComplexValType {140	fn from(s: ValType) -> Self {141		Self::Simple(s)142	}143}144145fn write_union<'i>(146	f: &mut std::fmt::Formatter<'_>,147	is_union: bool,148	union: impl Iterator<Item = &'i ComplexValType>,149) -> std::fmt::Result {150	for (i, v) in union.enumerate() {151		let should_add_braces =152			matches!(v, ComplexValType::UnionRef(_) | ComplexValType::Union(_) if !is_union);153		if i != 0 {154			write!(f, " {} ", if is_union { '|' } else { '&' })?;155		}156		if should_add_braces {157			write!(f, "(")?;158		}159		write!(f, "{v}")?;160		if should_add_braces {161			write!(f, ")")?;162		}163	}164	Ok(())165}166167fn print_array(a: &ComplexValType, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {168	if *a == ComplexValType::Any {169		write!(f, "array")?170	} else {171		write!(f, "Array<{a}>")?172	}173	Ok(())174}175176impl Display for ComplexValType {177	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {178		match self {179			ComplexValType::Any => write!(f, "any")?,180			ComplexValType::Simple(s) => write!(f, "{s}")?,181			ComplexValType::Char => write!(f, "char")?,182			ComplexValType::BoundedNumber(a, b) => write!(183				f,184				"BoundedNumber<{}, {}>",185				a.map(|e| e.to_string()).unwrap_or_else(|| "".into()),186				b.map(|e| e.to_string()).unwrap_or_else(|| "".into())187			)?,188			ComplexValType::ArrayRef(a) => print_array(a, f)?,189			ComplexValType::Array(a) => print_array(a, f)?,190			ComplexValType::ObjectRef(fields) => {191				write!(f, "{{")?;192				for (i, (k, v)) in fields.iter().enumerate() {193					if i != 0 {194						write!(f, ", ")?;195					}196					write!(f, "{k}: {v}")?;197				}198				write!(f, "}}")?;199			}200			ComplexValType::AttrsOf(a) => {201				if matches!(a, ComplexValType::Any) {202					write!(f, "object")?;203				} else {204					write!(f, "AttrsOf<{a}>")?;205				}206			}207			ComplexValType::Union(v) => write_union(f, true, v.iter())?,208			ComplexValType::UnionRef(v) => write_union(f, true, v.iter().copied())?,209			ComplexValType::Sum(v) => write_union(f, false, v.iter())?,210			ComplexValType::SumRef(v) => write_union(f, false, v.iter().copied())?,211			ComplexValType::Lazy(lazy) => write!(f, "Lazy<{lazy}>")?,212		};213		Ok(())214	}215}216217peg::parser! {218pub grammar parser() for str {219	rule number() -> f64220		= n:$(['0'..='9']+) { n.parse().unwrap() }221222	rule any_ty() -> ComplexValType = "any" { ComplexValType::Any }223	rule char_ty() -> ComplexValType = "character" { ComplexValType::Char }224	rule bool_ty() -> ComplexValType = "boolean" { ComplexValType::Simple(ValType::Bool) }225	rule null_ty() -> ComplexValType = "null" { ComplexValType::Simple(ValType::Null) }226	rule str_ty() -> ComplexValType = "string" { ComplexValType::Simple(ValType::Str) }227	rule num_ty() -> ComplexValType = "number" { ComplexValType::Simple(ValType::Num) }228	rule simple_array_ty() -> ComplexValType = "array" { ComplexValType::Simple(ValType::Arr) }229	rule simple_object_ty() -> ComplexValType = "object" { ComplexValType::Simple(ValType::Obj) }230	rule simple_function_ty() -> ComplexValType = "function" { ComplexValType::Simple(ValType::Func) }231232	rule array_ty() -> ComplexValType233		= "Array<" t:ty() ">" { ComplexValType::Array(Box::new(t)) }234235	rule bounded_number_ty() -> ComplexValType236		= "BoundedNumber<" a:number() ", " b:number() ">" { ComplexValType::BoundedNumber(Some(a), Some(b)) }237238	rule ty_basic() -> ComplexValType239		= any_ty()240		/ char_ty()241		/ bool_ty()242		/ null_ty()243		/ str_ty()244		/ num_ty()245		/ simple_array_ty()246		/ simple_object_ty()247		/ simple_function_ty()248		/ array_ty()249		/ bounded_number_ty()250251	pub rule ty() -> ComplexValType252		= precedence! {253			a:(@) " | " b:@ {254				match a {255					ComplexValType::Union(mut a) => {256						a.push(b);257						ComplexValType::Union(a)258					}259					_ => ComplexValType::Union(vec![a, b]),260				}261			}262			--263			a:(@) " & " b:@ {264				match a {265					ComplexValType::Sum(mut a) => {266						a.push(b);267						ComplexValType::Sum(a)268					}269					_ => ComplexValType::Sum(vec![a, b]),270				}271			}272			--273			"(" t:ty() ")" { t }274			t:ty_basic() { t }275		}276}277}278279#[cfg(test)]280pub mod tests {281	use super::parser;282283	#[test]284	fn precedence() {285		assert_eq!(286			parser::ty("(any & any) | (any | any) & any")287				.unwrap()288				.to_string(),289			"any & any | (any | any) & any"290		);291	}292293	#[test]294	fn array() {295		assert_eq!(parser::ty("Array<any>").unwrap().to_string(), "array");296		assert_eq!(297			parser::ty("Array<number>").unwrap().to_string(),298			"Array<number>"299		);300	}301	#[test]302	fn bounded_number() {303		assert_eq!(304			parser::ty("BoundedNumber<1, 2>").unwrap().to_string(),305			"BoundedNumber<1, 2>"306		);307	}308}