jrsonnet_types/
lib.rs

1#![allow(clippy::redundant_closure_call)]
2
3use jrsonnet_gc::Trace;
4use std::fmt::Display;
5
6#[macro_export]
7macro_rules! ty {
8	((Array<number>)) => {{
9		$crate::ComplexValType::ArrayRef(&$crate::ComplexValType::Simple($crate::ValType::Num))
10	}};
11	((Array<ubyte>)) => {{
12		$crate::ComplexValType::ArrayRef(&$crate::ComplexValType::BoundedNumber(Some(0.0), Some(255.0)))
13	}};
14	(array) => {
15		$crate::ComplexValType::Simple($crate::ValType::Arr)
16	};
17	(boolean) => {
18		$crate::ComplexValType::Simple($crate::ValType::Bool)
19	};
20	(null) => {
21		$crate::ComplexValType::Simple($crate::ValType::Null)
22	};
23	(string) => {
24		$crate::ComplexValType::Simple($crate::ValType::Str)
25	};
26	(char) => {
27		$crate::ComplexValType::Char
28	};
29	(number) => {
30		$crate::ComplexValType::Simple($crate::ValType::Num)
31	};
32	(BoundedNumber<($min:expr), ($max:expr)>) => {{
33		$crate::ComplexValType::BoundedNumber($min, $max)
34	}};
35	(object) => {
36		$crate::ComplexValType::Simple($crate::ValType::Obj)
37	};
38	(any) => {
39		$crate::ComplexValType::Any
40	};
41	(function) => {
42		$crate::ComplexValType::Simple($crate::ValType::Func)
43	};
44	(($($a:tt) |+)) => {{
45		static CONTENTS: &'static [$crate::ComplexValType] = &[
46			$(ty!($a)),+
47		];
48		$crate::ComplexValType::UnionRef(CONTENTS)
49	}};
50	(($($a:tt) &+)) => {{
51		static CONTENTS: &'static [$crate::ComplexValType] = &[
52			$(ty!($a)),+
53		];
54		$crate::ComplexValType::SumRef(CONTENTS)
55	}};
56}
57
58#[test]
59fn test() {
60	assert_eq!(
61		ty!((Array<number>)),
62		ComplexValType::ArrayRef(&ComplexValType::Simple(ValType::Num))
63	);
64	assert_eq!(ty!(array), ComplexValType::Simple(ValType::Arr));
65	assert_eq!(ty!(any), ComplexValType::Any);
66	assert_eq!(
67		ty!((string | number)),
68		ComplexValType::UnionRef(&[
69			ComplexValType::Simple(ValType::Str),
70			ComplexValType::Simple(ValType::Num)
71		])
72	);
73	assert_eq!(
74		format!("{}", ty!(((string & number) | (object & null)))),
75		"string & number | object & null"
76	);
77	assert_eq!(format!("{}", ty!((string | array))), "string | array");
78	assert_eq!(
79		format!("{}", ty!(((string & number) | array))),
80		"string & number | array"
81	);
82}
83
84#[derive(Debug, Clone, Copy, PartialEq, Eq, Trace)]
85#[trivially_drop]
86pub enum ValType {
87	Bool,
88	Null,
89	Str,
90	Num,
91	Arr,
92	Obj,
93	Func,
94}
95
96impl 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}
110
111impl Display for ValType {
112	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
113		write!(f, "{}", self.name())
114	}
115}
116
117#[derive(Debug, Clone, PartialEq, Trace)]
118#[trivially_drop]
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, ComplexValType)]),
127	Union(Vec<ComplexValType>),
128	UnionRef(&'static [ComplexValType]),
129	Sum(Vec<ComplexValType>),
130	SumRef(&'static [ComplexValType]),
131}
132
133impl From<ValType> for ComplexValType {
134	fn from(s: ValType) -> Self {
135		Self::Simple(s)
136	}
137}
138
139fn write_union(
140	f: &mut std::fmt::Formatter<'_>,
141	is_union: bool,
142	union: &[ComplexValType],
143) -> std::fmt::Result {
144	for (i, v) in union.iter().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}
160
161fn 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}
169
170impl 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)?,
195			ComplexValType::UnionRef(v) => write_union(f, true, v)?,
196			ComplexValType::Sum(v) => write_union(f, false, v)?,
197			ComplexValType::SumRef(v) => write_union(f, false, v)?,
198		};
199		Ok(())
200	}
201}
202
203peg::parser! {
204pub grammar parser() for str {
205	rule number() -> f64
206		= n:$(['0'..='9']+) { n.parse().unwrap() }
207
208	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) }
217
218	rule array_ty() -> ComplexValType
219		= "Array<" t:ty() ">" { ComplexValType::Array(Box::new(t)) }
220
221	rule bounded_number_ty() -> ComplexValType
222		= "BoundedNumber<" a:number() ", " b:number() ">" { ComplexValType::BoundedNumber(Some(a), Some(b)) }
223
224	rule ty_basic() -> ComplexValType
225		= 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()
236
237	pub rule ty() -> ComplexValType
238		= 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}
264
265#[cfg(test)]
266pub mod tests {
267	use super::parser;
268
269	#[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	}
278
279	#[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}