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

difftreelog

feat exp-bigint

Yaroslav Bolyukin2023-04-17parent: #205090d.patch.diff
in: master

16 files changed

modifiedCargo.lockdiffbeforeafterboth
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -298,6 +298,7 @@
  "jrsonnet-macros",
  "jrsonnet-parser",
  "jrsonnet-types",
+ "num-bigint",
  "pathdiff",
  "rustc-hash",
  "serde",
@@ -370,6 +371,7 @@
  "jrsonnet-macros",
  "jrsonnet-parser",
  "md5",
+ "num-bigint",
  "serde",
  "serde_json",
  "serde_yaml_with_quirks",
@@ -449,6 +451,37 @@
 ]
 
 [[package]]
+name = "num-bigint"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f"
+dependencies = [
+ "autocfg",
+ "num-integer",
+ "num-traits",
+ "serde",
+]
+
+[[package]]
+name = "num-integer"
+version = "0.1.45"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
+dependencies = [
+ "autocfg",
+ "num-traits",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
 name = "once_cell"
 version = "1.17.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
modifiedCargo.tomldiffbeforeafterboth
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,5 +1,5 @@
 [workspace]
-package.version = "0.5.0"
+package.version = "0.5.0-pre7"
 members = ["crates/*", "bindings/jsonnet", "cmds/jrsonnet", "tests"]
 default-members = ["cmds/jrsonnet"]
 
modifiedcmds/jrsonnet/Cargo.tomldiffbeforeafterboth
--- a/cmds/jrsonnet/Cargo.toml
+++ b/cmds/jrsonnet/Cargo.toml
@@ -19,6 +19,8 @@
 exp-destruct = ["jrsonnet-evaluator/exp-destruct"]
 # Iteration over objects yields [key, value] elements
 exp-object-iteration = ["jrsonnet-evaluator/exp-object-iteration"]
+# Bigint type
+exp-bigint = ["jrsonnet-evaluator/exp-bigint", "jrsonnet-cli/exp-bigint"]
 
 # std.thisFile support
 legacy-this-file = ["jrsonnet-cli/legacy-this-file"]
modifiedcrates/jrsonnet-cli/Cargo.tomldiffbeforeafterboth
--- a/crates/jrsonnet-cli/Cargo.toml
+++ b/crates/jrsonnet-cli/Cargo.toml
@@ -11,6 +11,10 @@
     "jrsonnet-evaluator/exp-preserve-order",
     "jrsonnet-stdlib/exp-preserve-order",
 ]
+exp-bigint = [
+    "jrsonnet-evaluator/exp-bigint",
+    "jrsonnet-stdlib/exp-bigint",
+]
 legacy-this-file = ["jrsonnet-stdlib/legacy-this-file"]
 
 [dependencies]
modifiedcrates/jrsonnet-evaluator/Cargo.tomldiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/Cargo.toml
+++ b/crates/jrsonnet-evaluator/Cargo.toml
@@ -24,6 +24,8 @@
 exp-destruct = ["jrsonnet-parser/exp-destruct"]
 # Iteration over objects yields [key, value] elements
 exp-object-iteration = []
+# Bigint type
+exp-bigint = ["num-bigint"]
 
 # Improves performance, and implements some useful things using nightly-only features
 nightly = ["hashbrown/nightly"]
@@ -54,3 +56,5 @@
 annotate-snippets = { version = "0.9.1", features = ["color"], optional = true }
 # Async imports
 async-trait = { version = "0.1.60", optional = true }
+# Bigint
+num-bigint = { version = "0.4.3", features = ["serde"], optional = true }
modifiedcrates/jrsonnet-evaluator/src/evaluate/operator.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/evaluate/operator.rs
+++ b/crates/jrsonnet-evaluator/src/evaluate/operator.rs
@@ -47,6 +47,8 @@
 		(Arr(a), Arr(b)) => Val::Arr(ArrValue::extended(a.clone(), b.clone())),
 
 		(Num(v1), Num(v2)) => Val::new_checked_num(v1 + v2)?,
+		#[cfg(feature = "exp-bigint")]
+		(BigInt(a), BigInt(b)) => BigInt(Box::new((&**a).clone() + (&**b).clone())),
 		_ => throw!(BinaryOperatorDoesNotOperateOnValues(
 			BinaryOpType::Add,
 			a.value_type(),
@@ -95,6 +97,8 @@
 	Ok(match (a, b) {
 		(Str(a), Str(b)) => a.cmp(b),
 		(Num(a), Num(b)) => a.partial_cmp(b).expect("jsonnet numbers are non NaN"),
+		#[cfg(feature = "exp-bigint")]
+		(BigInt(a), BigInt(b)) => a.cmp(b),
 		(Arr(a), Arr(b)) => {
 			if let (Some(ai), Some(bi)) = (a.iter_cheap(), b.iter_cheap()) {
 				for (a, b) in ai.zip(bi) {
@@ -174,6 +178,12 @@
 			Num(f64::from((*v1 as i32) >> (*v2 as i32)))
 		}
 
+		// Bigint X Bigint
+		#[cfg(feature = "exp-bigint")]
+		(BigInt(a), Mul, BigInt(b)) => BigInt(Box::new((&**a).clone() * (&**b).clone())),
+		#[cfg(feature = "exp-bigint")]
+		(BigInt(a), Sub, BigInt(b)) => BigInt(Box::new((&**a).clone() - (&**b).clone())),
+
 		_ => throw!(BinaryOperatorDoesNotOperateOnValues(
 			op,
 			a.value_type(),
modifiedcrates/jrsonnet-evaluator/src/integrations/serde.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/integrations/serde.rs
+++ b/crates/jrsonnet-evaluator/src/integrations/serde.rs
@@ -162,6 +162,8 @@
 			Val::Null => serializer.serialize_none(),
 			Val::Str(s) => serializer.serialize_str(&s.clone().into_flat()),
 			Val::Num(n) => serializer.serialize_f64(*n),
+			#[cfg(feature = "exp-bigint")]
+			Val::BigInt(b) => b.serialize(serializer),
 			Val::Arr(arr) => {
 				let mut seq = serializer.serialize_seq(Some(arr.len()))?;
 				for (i, element) in arr.iter().enumerate() {
modifiedcrates/jrsonnet-evaluator/src/manifest.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/manifest.rs
+++ b/crates/jrsonnet-evaluator/src/manifest.rs
@@ -149,6 +149,8 @@
 		Val::Null => buf.push_str("null"),
 		Val::Str(s) => escape_string_json_buf(&s.clone().into_flat(), buf),
 		Val::Num(n) => write!(buf, "{n}").unwrap(),
+		#[cfg(feature = "exp-bigint")]
+		Val::BigInt(n) => write!(buf, "{n}").unwrap(),
 		Val::Arr(items) => {
 			buf.push('[');
 			if !items.is_empty() {
modifiedcrates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/val.rs
+++ b/crates/jrsonnet-evaluator/src/val.rs
@@ -311,6 +311,9 @@
 	/// Should be finite, and not NaN
 	/// This restriction isn't enforced by enum, as enum field can't be marked as private
 	Num(f64),
+	/// Experimental bigint
+	#[cfg(feature = "exp-bigint")]
+	BigInt(#[trace(skip)] Box<num_bigint::BigInt>),
 	/// Represents a Jsonnet array.
 	Arr(ArrValue),
 	/// Represents a Jsonnet object.
@@ -389,6 +392,8 @@
 		match self {
 			Self::Str(..) => ValType::Str,
 			Self::Num(..) => ValType::Num,
+			#[cfg(feature = "exp-bigint")]
+			Self::BigInt(..) => ValType::BigInt,
 			Self::Arr(..) => ValType::Arr,
 			Self::Obj(..) => ValType::Obj,
 			Self::Bool(_) => ValType::Bool,
modifiedcrates/jrsonnet-stdlib/Cargo.tomldiffbeforeafterboth
--- a/crates/jrsonnet-stdlib/Cargo.toml
+++ b/crates/jrsonnet-stdlib/Cargo.toml
@@ -17,6 +17,8 @@
 exp-preserve-order = ["jrsonnet-evaluator/exp-preserve-order"]
 # Add nonstandard `std.sha256` function
 exp-more-hashes = ["dep:sha2"]
+# Bigint type
+exp-bigint = ["num-bigint", "jrsonnet-evaluator/exp-bigint"]
 
 [dependencies]
 jrsonnet-evaluator.workspace = true
@@ -39,6 +41,7 @@
 serde_yaml_with_quirks = "0.8.24"
 
 sha2 = { version = "0.10.6", optional = true }
+num-bigint = { version = "0.4.3", optional = true }
 
 [build-dependencies]
 jrsonnet-parser.workspace = true
modifiedcrates/jrsonnet-stdlib/src/expr.rsdiffbeforeafterboth
--- a/crates/jrsonnet-stdlib/src/expr.rs
+++ b/crates/jrsonnet-stdlib/src/expr.rs
@@ -83,7 +83,7 @@
 			pub(super) use std::{option::Option, rc::Rc, vec};
 
 			pub(super) use jrsonnet_parser::*;
-		};
+		}
 
 		include!(concat!(env!("OUT_DIR"), "/stdlib.rs"))
 	}
modifiedcrates/jrsonnet-stdlib/src/lib.rsdiffbeforeafterboth
--- a/crates/jrsonnet-stdlib/src/lib.rs
+++ b/crates/jrsonnet-stdlib/src/lib.rs
@@ -143,6 +143,8 @@
 		("asciiLower", builtin_ascii_lower::INST),
 		("findSubstr", builtin_find_substr::INST),
 		("parseInt", builtin_parse_int::INST),
+		#[cfg(feature = "exp-bigint")]
+		("bigint", builtin_bigint::INST),
 		("parseOctal", builtin_parse_octal::INST),
 		("parseHex", builtin_parse_hex::INST),
 		// Misc
modifiedcrates/jrsonnet-stdlib/src/manifest/toml.rsdiffbeforeafterboth
--- a/crates/jrsonnet-stdlib/src/manifest/toml.rs
+++ b/crates/jrsonnet-stdlib/src/manifest/toml.rs
@@ -103,6 +103,8 @@
 			escape_string_json_buf(&s.clone().into_flat(), buf);
 		}
 		Val::Num(n) => write!(buf, "{n}").unwrap(),
+		#[cfg(feature = "exp-bigint")]
+		Val::BigInt(n) => write!(buf, "{n}").unwrap(),
 		Val::Arr(a) => {
 			if a.is_empty() {
 				buf.push_str("[]");
modifiedcrates/jrsonnet-stdlib/src/manifest/yaml.rsdiffbeforeafterboth
--- a/crates/jrsonnet-stdlib/src/manifest/yaml.rs
+++ b/crates/jrsonnet-stdlib/src/manifest/yaml.rs
@@ -140,6 +140,8 @@
 			}
 		}
 		Val::Num(n) => write!(buf, "{}", *n).unwrap(),
+		#[cfg(feature = "exp-bigint")]
+		Val::BigInt(n) => write!(buf, "{}", *n).unwrap(),
 		Val::Arr(a) => {
 			if a.is_empty() {
 				buf.push_str("[]");
modifiedcrates/jrsonnet-stdlib/src/strings.rsdiffbeforeafterboth
--- a/crates/jrsonnet-stdlib/src/strings.rs
+++ b/crates/jrsonnet-stdlib/src/strings.rs
@@ -151,6 +151,20 @@
 	})
 }
 
+#[cfg(feature = "exp-bigint")]
+#[builtin]
+pub fn builtin_bigint(v: Either![f64, IStr]) -> Result<Val> {
+	use Either2::*;
+	Ok(match v {
+		A(a) => Val::BigInt(Box::new((a as i64).into())),
+		B(b) => Val::BigInt(Box::new(
+			b.as_str()
+				.parse()
+				.map_err(|e| RuntimeError(format!("bad bigint: {e}").into()))?,
+		)),
+	})
+}
+
 #[cfg(test)]
 mod tests {
 	use super::*;
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	Arr,92	Obj,93	Func,94}9596impl ValType {97	pub const fn name(&self) -> &'static str {98		use ValType::*;99		match self {100			Bool => "boolean",101			Null => "null",102			Str => "string",103			Num => "number",104			Arr => "array",105			Obj => "object",106			Func => "function",107		}108	}109}110111impl Display for ValType {112	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {113		write!(f, "{}", self.name())114	}115}116117#[derive(Debug, Clone, PartialEq, Trace)]118#[trace(skip)]119pub enum ComplexValType {120	Any,121	Char,122	Simple(ValType),123	BoundedNumber(Option<f64>, Option<f64>),124	Array(Box<ComplexValType>),125	ArrayRef(&'static ComplexValType),126	ObjectRef(&'static [(&'static str, &'static ComplexValType)]),127	Union(Vec<ComplexValType>),128	UnionRef(&'static [&'static ComplexValType]),129	Sum(Vec<ComplexValType>),130	SumRef(&'static [&'static ComplexValType]),131}132133impl From<ValType> for ComplexValType {134	fn from(s: ValType) -> Self {135		Self::Simple(s)136	}137}138139fn write_union<'i>(140	f: &mut std::fmt::Formatter<'_>,141	is_union: bool,142	union: impl Iterator<Item = &'i ComplexValType>,143) -> std::fmt::Result {144	for (i, v) in union.enumerate() {145		let should_add_braces =146			matches!(v, ComplexValType::UnionRef(_) | ComplexValType::Union(_) if !is_union);147		if i != 0 {148			write!(f, " {} ", if is_union { '|' } else { '&' })?;149		}150		if should_add_braces {151			write!(f, "(")?;152		}153		write!(f, "{v}")?;154		if should_add_braces {155			write!(f, ")")?;156		}157	}158	Ok(())159}160161fn print_array(a: &ComplexValType, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {162	if *a == ComplexValType::Any {163		write!(f, "array")?164	} else {165		write!(f, "Array<{a}>")?166	}167	Ok(())168}169170impl Display for ComplexValType {171	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {172		match self {173			ComplexValType::Any => write!(f, "any")?,174			ComplexValType::Simple(s) => write!(f, "{s}")?,175			ComplexValType::Char => write!(f, "char")?,176			ComplexValType::BoundedNumber(a, b) => write!(177				f,178				"BoundedNumber<{}, {}>",179				a.map(|e| e.to_string()).unwrap_or_else(|| "".into()),180				b.map(|e| e.to_string()).unwrap_or_else(|| "".into())181			)?,182			ComplexValType::ArrayRef(a) => print_array(a, f)?,183			ComplexValType::Array(a) => print_array(a, f)?,184			ComplexValType::ObjectRef(fields) => {185				write!(f, "{{")?;186				for (i, (k, v)) in fields.iter().enumerate() {187					if i != 0 {188						write!(f, ", ")?;189					}190					write!(f, "{k}: {v}")?;191				}192				write!(f, "}}")?;193			}194			ComplexValType::Union(v) => write_union(f, true, v.iter())?,195			ComplexValType::UnionRef(v) => write_union(f, true, v.iter().copied())?,196			ComplexValType::Sum(v) => write_union(f, false, v.iter())?,197			ComplexValType::SumRef(v) => write_union(f, false, v.iter().copied())?,198		};199		Ok(())200	}201}202203peg::parser! {204pub grammar parser() for str {205	rule number() -> f64206		= n:$(['0'..='9']+) { n.parse().unwrap() }207208	rule any_ty() -> ComplexValType = "any" { ComplexValType::Any }209	rule char_ty() -> ComplexValType = "character" { ComplexValType::Char }210	rule bool_ty() -> ComplexValType = "boolean" { ComplexValType::Simple(ValType::Bool) }211	rule null_ty() -> ComplexValType = "null" { ComplexValType::Simple(ValType::Null) }212	rule str_ty() -> ComplexValType = "string" { ComplexValType::Simple(ValType::Str) }213	rule num_ty() -> ComplexValType = "number" { ComplexValType::Simple(ValType::Num) }214	rule simple_array_ty() -> ComplexValType = "array" { ComplexValType::Simple(ValType::Arr) }215	rule simple_object_ty() -> ComplexValType = "object" { ComplexValType::Simple(ValType::Obj) }216	rule simple_function_ty() -> ComplexValType = "function" { ComplexValType::Simple(ValType::Func) }217218	rule array_ty() -> ComplexValType219		= "Array<" t:ty() ">" { ComplexValType::Array(Box::new(t)) }220221	rule bounded_number_ty() -> ComplexValType222		= "BoundedNumber<" a:number() ", " b:number() ">" { ComplexValType::BoundedNumber(Some(a), Some(b)) }223224	rule ty_basic() -> ComplexValType225		= any_ty()226		/ char_ty()227		/ bool_ty()228		/ null_ty()229		/ str_ty()230		/ num_ty()231		/ simple_array_ty()232		/ simple_object_ty()233		/ simple_function_ty()234		/ array_ty()235		/ bounded_number_ty()236237	pub rule ty() -> ComplexValType238		= precedence! {239			a:(@) " | " b:@ {240				match a {241					ComplexValType::Union(mut a) => {242						a.push(b);243						ComplexValType::Union(a)244					}245					_ => ComplexValType::Union(vec![a, b]),246				}247			}248			--249			a:(@) " & " b:@ {250				match a {251					ComplexValType::Sum(mut a) => {252						a.push(b);253						ComplexValType::Sum(a)254					}255					_ => ComplexValType::Sum(vec![a, b]),256				}257			}258			--259			"(" t:ty() ")" { t }260			t:ty_basic() { t }261		}262}263}264265#[cfg(test)]266pub mod tests {267	use super::parser;268269	#[test]270	fn precedence() {271		assert_eq!(272			parser::ty("(any & any) | (any | any) & any")273				.unwrap()274				.to_string(),275			"any & any | (any | any) & any"276		);277	}278279	#[test]280	fn array() {281		assert_eq!(parser::ty("Array<any>").unwrap().to_string(), "array");282		assert_eq!(283			parser::ty("Array<number>").unwrap().to_string(),284			"Array<number>"285		);286	}287	#[test]288	fn bounded_number() {289		assert_eq!(290			parser::ty("BoundedNumber<1, 2>").unwrap().to_string(),291			"BoundedNumber<1, 2>"292		);293	}294}
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	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}