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}