difftreelog
feat exp-bigint
in: master
16 files changed
Cargo.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"
Cargo.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"]
cmds/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"]
crates/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]
crates/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 }
crates/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(),
crates/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() {
crates/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() {
crates/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,
crates/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
crates/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"))
}
crates/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
crates/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("[]");
crates/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("[]");
crates/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::*;
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 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}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}