difftreelog
feat unify Arg and Typed handling for Thunk
in: master
8 files changed
crates/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()
}
crates/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)
+ }
+}
crates/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()),
}
}
}
crates/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"))]
crates/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 {
crates/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(()),
}
}
}
crates/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 {
crates/jrsonnet-types/src/lib.rsdiffbeforeafterboth1#![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}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}