difftreelog
Merge pull request #29 from CertainLach/type-safety
in: master
Jsonnet type system
36 files changed
Cargo.lockdiffbeforeafterboth--- a/Cargo.lock
+++ b/Cargo.lock
@@ -167,26 +167,34 @@
"bincode",
"closure",
"indexmap",
+ "jrsonnet-interner",
"jrsonnet-parser",
"jrsonnet-stdlib",
+ "jrsonnet-types",
"md5",
"pathdiff",
"rustc-hash",
"serde",
"serde_json",
- "structdump",
"thiserror",
]
[[package]]
+name = "jrsonnet-interner"
+version = "0.3.3"
+dependencies = [
+ "rustc-hash",
+ "serde",
+]
+
+[[package]]
name = "jrsonnet-parser"
version = "0.3.3"
dependencies = [
+ "jrsonnet-interner",
"jrsonnet-stdlib",
"peg",
"serde",
- "structdump",
- "structdump-derive",
"unescape",
]
@@ -195,10 +203,18 @@
version = "0.3.3"
[[package]]
+name = "jrsonnet-types"
+version = "0.3.3"
+dependencies = [
+ "peg",
+]
+
+[[package]]
name = "jsonnet"
version = "0.3.3"
dependencies = [
"jrsonnet-evaluator",
+ "jrsonnet-interner",
"jrsonnet-parser",
]
@@ -370,23 +386,6 @@
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
-
-[[package]]
-name = "structdump"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2e16ec33a0342fdb67d13913b4ffae6527ebccfa04b5d7da174bdc7a31db29b8"
-
-[[package]]
-name = "structdump-derive"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "06c337fdc077e02ccbfcc62af0090564a4af342975c3b7be09705efab90c1888"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
[[package]]
name = "syn"
Cargo.tomldiffbeforeafterboth--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,11 +1,13 @@
[workspace]
members = [
+ "crates/jrsonnet-interner",
"crates/jrsonnet-parser",
"crates/jrsonnet-evaluator",
"crates/jrsonnet-stdlib",
"crates/jrsonnet-cli",
+ "crates/jrsonnet-types",
"bindings/jsonnet",
- "cmds/jrsonnet"
+ "cmds/jrsonnet",
]
[profile.test]
bindings/jsonnet/Cargo.tomldiffbeforeafterboth--- a/bindings/jsonnet/Cargo.toml
+++ b/bindings/jsonnet/Cargo.toml
@@ -5,6 +5,7 @@
edition = "2018"
[dependencies]
+jrsonnet-interner = { path = "../../crates/jrsonnet-interner", version = "0.3.3" }
jrsonnet-evaluator = { path = "../../crates/jrsonnet-evaluator", version = "0.3.3" }
jrsonnet-parser = { path = "../../crates/jrsonnet-parser", version = "0.3.3" }
bindings/jsonnet/src/import.rsdiffbeforeafterboth--- a/bindings/jsonnet/src/import.rs
+++ b/bindings/jsonnet/src/import.rs
@@ -4,6 +4,7 @@
error::{Error::*, Result},
throw, EvaluationState, ImportResolver,
};
+use jrsonnet_interner::IStr;
use std::{
any::Any,
cell::RefCell,
@@ -30,7 +31,7 @@
cb: JsonnetImportCallback,
ctx: *mut c_void,
- out: RefCell<HashMap<PathBuf, Rc<str>>>,
+ out: RefCell<HashMap<PathBuf, IStr>>,
}
impl ImportResolver for CallbackImportResolver {
fn resolve_file(&self, from: &PathBuf, path: &PathBuf) -> Result<Rc<PathBuf>> {
@@ -75,7 +76,7 @@
Ok(Rc::new(found_here_buf))
}
- fn load_file_contents(&self, resolved: &PathBuf) -> Result<Rc<str>> {
+ fn load_file_contents(&self, resolved: &PathBuf) -> Result<IStr> {
Ok(self.out.borrow().get(resolved).unwrap().clone())
}
unsafe fn as_any(&self) -> &dyn Any {
@@ -124,7 +125,7 @@
throw!(ImportFileNotFound(from.clone(), path.clone()))
}
}
- fn load_file_contents(&self, id: &PathBuf) -> Result<Rc<str>> {
+ fn load_file_contents(&self, id: &PathBuf) -> Result<IStr> {
let mut file = File::open(id).map_err(|_e| ResolvedFileNotFound(id.clone()))?;
let mut out = String::new();
file.read_to_string(&mut out)
bindings/jsonnet/src/lib.rsdiffbeforeafterboth--- a/bindings/jsonnet/src/lib.rs
+++ b/bindings/jsonnet/src/lib.rs
@@ -8,6 +8,7 @@
use import::NativeImportResolver;
use jrsonnet_evaluator::{EvaluationState, ManifestFormat, Val};
+use jrsonnet_interner::IStr;
use std::{
alloc::Layout,
ffi::{CStr, CString},
@@ -161,7 +162,7 @@
})
}
-fn multi_to_raw(multi: Vec<(Rc<str>, Rc<str>)>) -> *const c_char {
+fn multi_to_raw(multi: Vec<(IStr, IStr)>) -> *const c_char {
let mut out = Vec::new();
for (i, (k, v)) in multi.iter().enumerate() {
if i != 0 {
@@ -237,7 +238,7 @@
})
}
-fn stream_to_raw(multi: Vec<Rc<str>>) -> *const c_char {
+fn stream_to_raw(multi: Vec<IStr>) -> *const c_char {
let mut out = Vec::new();
for (i, v) in multi.iter().enumerate() {
if i != 0 {
bindings/jsonnet/src/native.rsdiffbeforeafterboth--- a/bindings/jsonnet/src/native.rs
+++ b/bindings/jsonnet/src/native.rs
@@ -35,7 +35,7 @@
vm.add_native(
name,
- Rc::new(NativeCallback::new(params, move |args| {
+ Rc::new(NativeCallback::new(params, move |_caller, args| {
let mut n_args = Vec::new();
for a in args {
n_args.push(Some(Box::new(a.clone())));
bindings/jsonnet/src/val_extract.rsdiffbeforeafterboth--- a/bindings/jsonnet/src/val_extract.rs
+++ b/bindings/jsonnet/src/val_extract.rs
@@ -9,7 +9,7 @@
#[no_mangle]
pub extern "C" fn jsonnet_json_extract_string(_vm: &EvaluationState, v: &Val) -> *mut c_char {
- match v.unwrap_if_lazy().unwrap() {
+ match v {
Val::Str(s) => CString::new(&*s as &str).unwrap().into_raw(),
_ => std::ptr::null_mut(),
}
@@ -20,9 +20,9 @@
v: &Val,
out: &mut c_double,
) -> c_int {
- match v.unwrap_if_lazy().unwrap() {
+ match v {
Val::Num(n) => {
- *out = n;
+ *out = *n;
1
}
_ => 0,
@@ -30,7 +30,7 @@
}
#[no_mangle]
pub extern "C" fn jsonnet_json_extract_bool(_vm: &EvaluationState, v: &Val) -> c_int {
- match v.unwrap_if_lazy().unwrap() {
+ match v {
Val::Bool(false) => 0,
Val::Bool(true) => 1,
_ => 2,
@@ -38,7 +38,7 @@
}
#[no_mangle]
pub extern "C" fn jsonnet_json_extract_null(_vm: &EvaluationState, v: &Val) -> c_int {
- match v.unwrap_if_lazy().unwrap() {
+ match v {
Val::Null => 1,
_ => 0,
}
bindings/jsonnet/src/val_make.rsdiffbeforeafterboth--- a/bindings/jsonnet/src/val_make.rs
+++ b/bindings/jsonnet/src/val_make.rs
@@ -1,6 +1,6 @@
//! Create values in VM
-use jrsonnet_evaluator::{EvaluationState, ObjValue, Val};
+use jrsonnet_evaluator::{ArrValue, EvaluationState, ObjValue, Val};
use std::{
ffi::CStr,
os::raw::{c_char, c_double, c_int},
@@ -38,7 +38,7 @@
#[no_mangle]
pub extern "C" fn jsonnet_json_make_array(_vm: &EvaluationState) -> *mut Val {
- Box::into_raw(Box::new(Val::Arr(Rc::new(Vec::new()))))
+ Box::into_raw(Box::new(Val::Arr(ArrValue::Eager(Rc::new(Vec::new())))))
}
#[no_mangle]
bindings/jsonnet/src/val_modify.rsdiffbeforeafterboth--- a/bindings/jsonnet/src/val_modify.rs
+++ b/bindings/jsonnet/src/val_modify.rs
@@ -2,7 +2,9 @@
//! Only tested with variables, which haven't altered by code before appearing here
//! In jrsonnet every value is immutable, and this code is probally broken
-use jrsonnet_evaluator::{EvaluationState, LazyBinding, LazyVal, ObjMember, ObjValue, Val};
+use jrsonnet_evaluator::{
+ ArrValue, EvaluationState, LazyBinding, LazyVal, ObjMember, ObjValue, Val,
+};
use jrsonnet_parser::Visibility;
use std::{collections::HashMap, ffi::CStr, os::raw::c_char, rc::Rc};
@@ -12,14 +14,17 @@
#[no_mangle]
pub unsafe extern "C" fn jsonnet_json_array_append(
_vm: &EvaluationState,
- arr: *mut Val,
+ arr: &mut Val,
val: &Val,
) {
- match *Box::from_raw(arr) {
+ match arr {
Val::Arr(old) => {
- let mut new = Rc::try_unwrap(old).expect("arr with no refs");
- new.push(val.clone());
- *arr = Val::Arr(Rc::new(new));
+ let mut new = Vec::new();
+ for item in old.iter_lazy() {
+ new.push(item);
+ }
+ new.push(LazyVal::new_resolved(val.clone()));
+ *arr = Val::Arr(ArrValue::Lazy(Rc::new(new)));
}
_ => panic!("should receive array"),
}
crates/jrsonnet-evaluator/Cargo.tomldiffbeforeafterboth--- a/crates/jrsonnet-evaluator/Cargo.toml
+++ b/crates/jrsonnet-evaluator/Cargo.toml
@@ -12,8 +12,6 @@
serialized-stdlib = ["serde", "bincode", "jrsonnet-parser/deserialize"]
# Allow to convert Val into serde_json::Value and backwards
serde-json = ["serde", "serde_json"]
-# Same as above, but with generated code instead of serde. Reduces memory usage, but increases binary size and compilation time
-codegenerated-stdlib = []
# Replace some standard library functions with faster implementations (I.e manifestJsonEx)
# Library works fine without this feature, but requires more memory and time for std function calls
faster = []
@@ -24,8 +22,10 @@
unstable = []
[dependencies]
+jrsonnet-interner = { path = "../jrsonnet-interner" }
jrsonnet-parser = { path = "../jrsonnet-parser", version = "0.3.3" }
jrsonnet-stdlib = { path = "../jrsonnet-stdlib", version = "0.3.3" }
+jrsonnet-types = { path = "../jrsonnet-types", version = "0.3.3" }
pathdiff = "0.2.0"
closure = "0.3.0"
@@ -57,8 +57,7 @@
optional = true
[build-dependencies]
-jrsonnet-parser = { path = "../jrsonnet-parser", features = ["dump", "serialize", "deserialize"], version = "0.3.3" }
+jrsonnet-parser = { path = "../jrsonnet-parser", features = ["serialize", "deserialize"], version = "0.3.3" }
jrsonnet-stdlib = { path = "../jrsonnet-stdlib", version = "0.3.3" }
-structdump = "0.1.2"
serde = "1.0"
bincode = "1.3.1"
crates/jrsonnet-evaluator/build.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/build.rs
+++ b/crates/jrsonnet-evaluator/build.rs
@@ -10,7 +10,6 @@
path::{Path, PathBuf},
rc::Rc,
};
-use structdump::CodegenResult;
fn main() {
let parsed = parse(
@@ -36,11 +35,12 @@
name: FieldName::Fixed(name),
..
})
- if **name == *"join" || **name == *"manifestJsonEx" ||
- **name == *"escapeStringJson" || **name == *"equals" ||
- **name == *"base64" || **name == *"foldl" || **name == *"foldr" ||
- **name == *"sortImpl" || **name == *"format" || **name == *"range" ||
- **name == *"reverse" || **name == *"slice" || **name == *"mod"
+ if name == "join" || name == "manifestJsonEx" ||
+ name == "escapeStringJson" || name == "equals" ||
+ name == "base64" || name == "foldl" || name == "foldr" ||
+ name == "sortImpl" || name == "format" || name == "range" ||
+ name == "reverse" || name == "slice" || name == "mod" ||
+ name == "strReplace"
)
})
.collect(),
@@ -52,15 +52,6 @@
} else {
parsed
};
- {
- let mut codegen = CodegenResult::default();
- let code = codegen.codegen(&parsed);
-
- let out_dir = env::var("OUT_DIR").unwrap();
- let dest_path = Path::new(&out_dir).join("stdlib.rs");
- let mut f = File::create(&dest_path).unwrap();
- f.write_all(&code.as_bytes()).unwrap();
- }
{
let out_dir = env::var("OUT_DIR").unwrap();
let dest_path = Path::new(&out_dir).join("stdlib.bincode");
crates/jrsonnet-evaluator/src/builtin/format.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/builtin/format.rs
+++ b/crates/jrsonnet-evaluator/src/builtin/format.rs
@@ -1,7 +1,9 @@
//! faster std.format impl
#![allow(clippy::too_many_arguments)]
-use crate::{error::Error::*, throw, LocError, ObjValue, Result, Val, ValType};
+use crate::{error::Error::*, throw, LocError, ObjValue, Result, Val};
+use jrsonnet_interner::IStr;
+use jrsonnet_types::ValType;
use thiserror::Error;
#[derive(Debug, Clone, Error)]
@@ -19,7 +21,7 @@
#[error("mapping keys required")]
MappingKeysRequired,
#[error("no such format field: {0}")]
- NoSuchFormatField(Rc<str>),
+ NoSuchFormatField(IStr),
}
impl From<FormatError> for LocError {
@@ -28,7 +30,6 @@
}
}
-use std::rc::Rc;
use FormatError::*;
type ParseResult<'t, T> = std::result::Result<(T, &'t str), FormatError>;
@@ -573,7 +574,7 @@
);
}
}
- ConvTypeV::Char => match value.clone().unwrap_if_lazy()? {
+ ConvTypeV::Char => match value.clone() {
Val::Num(n) => tmp_out.push(
std::char::from_u32(n as u32)
.ok_or_else(|| InvalidUnicodeCodepointGot(n as u32))?,
@@ -590,7 +591,7 @@
throw!(TypeMismatch(
"%c requires number/string",
vec![ValType::Num, ValType::Str],
- value.value_type()?,
+ value.value_type(),
));
}
},
@@ -679,7 +680,7 @@
}
Element::Code(c) => {
// TODO: Operate on ref
- let f: Rc<str> = c.mkey.into();
+ let f: IStr = c.mkey.into();
let width = match c.width {
Width::Star => {
throw!(CannotUseStarWidthWithObject);
crates/jrsonnet-evaluator/src/builtin/manifest.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/builtin/manifest.rs
+++ b/crates/jrsonnet-evaluator/src/builtin/manifest.rs
@@ -33,16 +33,16 @@
) -> Result<()> {
use std::fmt::Write;
let mtype = options.mtype;
- match val.unwrap_if_lazy()? {
+ match val {
Val::Bool(v) => {
- if v {
+ if *v {
buf.push_str("true");
} else {
buf.push_str("false");
}
}
Val::Null => buf.push_str("null"),
- Val::Str(s) => buf.push_str(&escape_string_json(&s)),
+ Val::Str(s) => buf.push_str(&escape_string_json(s)),
Val::Num(n) => write!(buf, "{}", n).unwrap(),
Val::Arr(items) => {
buf.push('[');
@@ -63,7 +63,7 @@
}
}
buf.push_str(cur_padding);
- manifest_json_ex_buf(item, buf, cur_padding, options)?;
+ manifest_json_ex_buf(&item?, buf, cur_padding, options)?;
}
cur_padding.truncate(old_len);
@@ -118,7 +118,6 @@
buf.push('}');
}
Val::Func(_) => throw!(RuntimeError("tried to manifest function".into())),
- Val::Lazy(_) => unreachable!(),
};
Ok(())
}
crates/jrsonnet-evaluator/src/builtin/mod.rsdiffbeforeafterboth1use crate::{1use crate::{2 equals,2 equals,3 error::{Error::*, Result},3 error::{Error::*, Result},4 evaluate, parse_args, primitive_equals, push, throw, with_state, Context, FuncVal, Val,4 parse_args, primitive_equals, push, throw, with_state, ArrValue, Context, FuncVal, LazyVal,5 ValType,5 Val,6};6};7use format::{format_arr, format_obj};7use format::{format_arr, format_obj};8use jrsonnet_parser::{ArgsDesc, ExprLocation};8use jrsonnet_interner::IStr;9use jrsonnet_parser::{ArgsDesc, BinaryOpType, ExprLocation};9use manifest::{escape_string_json, manifest_json_ex, ManifestJsonOptions, ManifestType};10use jrsonnet_types::ty;10use std::{path::PathBuf, rc::Rc};11use std::{collections::HashMap, path::PathBuf, rc::Rc};111212pub mod stdlib;13pub mod stdlib;13pub use stdlib::*;14pub use stdlib::*;141516use self::manifest::{escape_string_json, manifest_json_ex, ManifestJsonOptions, ManifestType};1715pub mod format;18pub mod format;16pub mod manifest;19pub mod manifest;17pub mod sort;20pub mod sort;182119fn std_format(str: Rc<str>, vals: Val) -> Result<Val> {22fn std_format(str: IStr, vals: Val) -> Result<Val> {20 push(23 push(21 &Some(ExprLocation(Rc::from(PathBuf::from("std.jsonnet")), 0, 0)),24 Some(&ExprLocation(Rc::from(PathBuf::from("std.jsonnet")), 0, 0)),22 || format!("std.format of {}", str),25 || format!("std.format of {}", str),23 || {26 || {24 Ok(match vals {27 Ok(match vals {25 Val::Arr(vals) => Val::Str(format_arr(&str, &vals)?.into()),28 Val::Arr(vals) => Val::Str(format_arr(&str, &vals.evaluated()?)?.into()),26 Val::Obj(obj) => Val::Str(format_obj(&str, &obj)?.into()),29 Val::Obj(obj) => Val::Str(format_obj(&str, &obj)?.into()),27 o => Val::Str(format_arr(&str, &[o])?.into()),30 o => Val::Str(format_arr(&str, &[o])?.into()),28 })31 })29 },32 },30 )33 )31}34}323533#[allow(clippy::cognitive_complexity)]36type Builtin = fn(context: Context, loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val>;34pub fn call_builtin(3738type BuiltinsType = HashMap<Box<str>, Builtin>;3940thread_local! {41 static BUILTINS: BuiltinsType = {42 [43 ("length".into(), builtin_length as Builtin),44 ("type".into(), builtin_type),45 ("makeArray".into(), builtin_make_array),46 ("codepoint".into(), builtin_codepoint),47 ("objectFieldsEx".into(), builtin_object_fields_ex),48 ("objectHasEx".into(), builtin_object_has_ex),49 ("slice".into(), builtin_slice),50 ("primitiveEquals".into(), builtin_primitive_equals),51 ("equals".into(), builtin_equals),52 ("modulo".into(), builtin_modulo),53 ("mod".into(), builtin_mod),54 ("floor".into(), builtin_floor),55 ("log".into(), builtin_log),56 ("pow".into(), builtin_pow),57 ("extVar".into(), builtin_ext_var),58 ("native".into(), builtin_native),59 ("filter".into(), builtin_filter),60 ("foldl".into(), builtin_foldl),61 ("foldr".into(), builtin_foldr),62 ("sortImpl".into(), builtin_sort_impl),63 ("format".into(), builtin_format),64 ("range".into(), builtin_range),65 ("char".into(), builtin_char),66 ("encodeUTF8".into(), builtin_encode_utf8),67 ("md5".into(), builtin_md5),68 ("base64".into(), builtin_base64),69 ("trace".into(), builtin_trace),70 ("join".into(), builtin_join),71 ("escapeStringJson".into(), builtin_escape_string_json),72 ("manifestJsonEx".into(), builtin_manifest_json_ex),73 ("reverse".into(), builtin_reverse),74 ("id".into(), builtin_id),75 ("strReplace".into(), builtin_str_replace),76 ].iter().cloned().collect()77 };78}7980fn builtin_length(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {81 parse_args!(context, "length", args, 1, [82 0, x: ty!((string | object | array));83 ], {84 Ok(match x {85 Val::Str(n) => Val::Num(n.chars().count() as f64),86 Val::Arr(a) => Val::Num(a.len() as f64),87 Val::Obj(o) => Val::Num(88 o.fields_visibility()89 .into_iter()90 .filter(|(_k, v)| *v)91 .count() as f64,92 ),93 _ => unreachable!(),94 })95 })96}9798fn builtin_type(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {99 parse_args!(context, "type", args, 1, [100 0, x: ty!(any);101 ], {102 Ok(Val::Str(x.value_type().name().into()))103 })104}105106fn builtin_make_array(35 context: Context,107 context: Context,36 loc: &Option<ExprLocation>,108 _loc: Option<&ExprLocation>,37 name: &str,38 args: &ArgsDesc,109 args: &ArgsDesc,39) -> Result<Val> {110) -> Result<Val> {40 Ok(match name as &str {111 parse_args!(context, "makeArray", args, 2, [41 // arr/string/function112 0, sz: ty!(BoundedNumber<(Some(0.0)), (None)>) => Val::Num;42 "length" => parse_args!(context, "std.length", args, 1, [113 1, func: ty!(function) => Val::Func;43 0, x: [Val::Str|Val::Arr|Val::Obj], vec![ValType::Str, ValType::Arr, ValType::Obj];114 ], {44 ], {115 let mut out = Vec::with_capacity(sz as usize);45 Ok(match x {116 for i in 0..sz as usize {46 Val::Str(n) => Val::Num(n.chars().count() as f64),117 out.push(LazyVal::new_resolved(func.evaluate_values(47 Val::Arr(i) => Val::Num(i.len() as f64),118 Context::new(),48 Val::Obj(o) => Val::Num(119 &[Val::Num(i as f64)]49 o.fields_visibility()120 )?))50 .into_iter()121 }51 .filter(|(_k, v)| *v)122 Ok(Val::Arr(out.into()))52 .count() as f64,123 })53 ),124}54 _ => unreachable!(),12555 })126fn builtin_codepoint(56 })?,127 context: Context,57 // any128 _loc: Option<&ExprLocation>,58 "type" => parse_args!(context, "std.type", args, 1, [129 args: &ArgsDesc,59 0, x, vec![];130) -> Result<Val> {60 ], {131 parse_args!(context, "codepoint", args, 1, [61 Ok(Val::Str(x.value_type()?.name().into()))132 0, str: ty!(char) => Val::Str;62 })?,133 ], {63 // length, idx=>any134 Ok(Val::Num(str.chars().next().unwrap() as u32 as f64))64 "makeArray" => parse_args!(context, "std.makeArray", args, 2, [135 })65 0, sz: [Val::Num]!!Val::Num, vec![ValType::Num];136}66 1, func: [Val::Func]!!Val::Func, vec![ValType::Func];13767 ], {138fn builtin_object_fields_ex(68 if sz < 0.0 {139 context: Context,69 throw!(RuntimeError(format!("makeArray requires size >= 0, got {}", sz).into()));140 _loc: Option<&ExprLocation>,70 }141 args: &ArgsDesc,71 let mut out = Vec::with_capacity(sz as usize);142) -> Result<Val> {72 for i in 0..sz as usize {143 parse_args!(context, "objectFieldsEx", args, 2, [73 out.push(func.evaluate_values(144 0, obj: ty!(object) => Val::Obj;74 Context::new(),145 1, inc_hidden: ty!(boolean) => Val::Bool;75 &[Val::Num(i as f64)]146 ], {76 )?)147 let mut out = obj.fields_visibility()77 }148 .into_iter()78 Ok(Val::Arr(Rc::new(out)))149 .filter(|(_k, v)| *v || inc_hidden)79 })?,150 .map(|(k, _v)|k)80 // string151 .collect::<Vec<_>>();81 "codepoint" => parse_args!(context, "std.codepoint", args, 1, [152 out.sort();82 0, str: [Val::Str]!!Val::Str, vec![ValType::Str];153 Ok(Val::Arr(out.into_iter().map(Val::Str).collect::<Vec<_>>().into()))83 ], {154 })84 assert!(155}85 str.chars().count() == 1,15686 "std.codepoint should receive single char string"157fn builtin_object_has_ex(87 );158 context: Context,88 Ok(Val::Num(str.chars().take(1).next().unwrap() as u32 as f64))159 _loc: Option<&ExprLocation>,89 })?,160 args: &ArgsDesc,90 // object, includeHidden161) -> Result<Val> {91 "objectFieldsEx" => parse_args!(context, "std.objectFieldsEx",args, 2, [162 parse_args!(context, "objectHasEx", args, 3, [92 0, obj: [Val::Obj]!!Val::Obj, vec![ValType::Obj];163 0, obj: ty!(object) => Val::Obj;93 1, inc_hidden: [Val::Bool]!!Val::Bool, vec![ValType::Bool];164 1, f: ty!(string) => Val::Str;94 ], {165 2, inc_hidden: ty!(boolean) => Val::Bool;95 let mut out = obj.fields_visibility()166 ], {167 Ok(Val::Bool(168 obj.fields_visibility()96 .into_iter()169 .into_iter()97 .filter(|(_k, v)| *v || inc_hidden)170 .filter(|(_k, v)| *v || inc_hidden)98 .map(|(k, _v)|k)171 .any(|(k, _v)| *k == *f),99 .collect::<Vec<_>>();100 out.sort();101 Ok(Val::Arr(Rc::new(out.into_iter().map(Val::Str).collect())))102 })?,103 // object, field, includeHidden104 "objectHasEx" => parse_args!(context, "std.objectHasEx", args, 3, [105 0, obj: [Val::Obj]!!Val::Obj, vec![ValType::Obj];106 1, f: [Val::Str]!!Val::Str, vec![ValType::Str];107 2, inc_hidden: [Val::Bool]!!Val::Bool, vec![ValType::Bool];108 ], {109 Ok(Val::Bool(110 obj.fields_visibility()111 .into_iter()112 .filter(|(_k, v)| *v || inc_hidden)113 .any(|(k, _v)| *k == *f),114 ))172 ))115 })?,173 })174}116175117 // faster176// faster118 "slice" => parse_args!(context, "slice", args, 4, [177fn builtin_slice(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {178 parse_args!(context, "slice", args, 4, [119 0, indexable: [Val::Str | Val::Arr], vec![ValType::Str, ValType::Arr];179 0, indexable: ty!((string | array));120 1, index, vec![ValType::Num, ValType::Null];180 1, index: ty!((number | null));121 2, end, vec![ValType::Num, ValType::Null];181 2, end: ty!((number | null));122 3, step, vec![ValType::Num, ValType::Null];182 3, step: ty!((number | null));123 ], {183 ], {124 let index = match index {184 let index = match index {125 Val::Num(v) => v as usize,185 Val::Num(v) => v as usize,126 Val::Null => 0,186 Val::Null => 0,127 _ => unreachable!(),187 _ => unreachable!(),128 };188 };129 let end = match end {189 let end = match end {130 Val::Num(v) => v as usize,190 Val::Num(v) => v as usize,131 Val::Null => match &indexable {191 Val::Null => match &indexable {132 Val::Str(s) => s.chars().count(),192 Val::Str(s) => s.chars().count(),133 Val::Arr(v) => v.len(),193 Val::Arr(v) => v.len(),134 _ => unreachable!()135 },136 _ => unreachable!()194 _ => unreachable!()137 };195 },138 let step = match step {196 _ => unreachable!()197 };198 let step = match step {139 Val::Num(v) => v as usize,199 Val::Num(v) => v as usize,140 Val::Null => 1,200 Val::Null => 1,141 _ => unreachable!()201 _ => unreachable!()142 };202 };143 match &indexable {203 match &indexable {144 Val::Str(s) => {204 Val::Str(s) => {145 Ok(Val::Str((s.chars().skip(index).take(end-index).step_by(step).collect::<String>()).into()))205 Ok(Val::Str((s.chars().skip(index).take(end-index).step_by(step).collect::<String>()).into()))146 }147 Val::Arr(arr) => {148 Ok(Val::Arr((arr.iter().skip(index).take(end-index).step_by(step).cloned().collect::<Vec<Val>>()).into()))149 }150 _ => unreachable!()151 }206 }152 })?,207 Val::Arr(arr) => {153 "primitiveEquals" => parse_args!(context, "std.primitiveEquals", args, 2, [154 0, a, vec![];155 1, b, vec![];156 ], {157 Ok(Val::Bool(primitive_equals(&a, &b)?))158 })?,159 // faster160 "equals" => parse_args!(context, "std.equals", args, 2, [161 0, a, vec![];162 1, b, vec![];163 ], {164 Ok(Val::Bool(equals(&a, &b)?))208 Ok(Val::Arr((arr.iter().skip(index).take(end-index).step_by(step).collect::<Result<Vec<Val>>>()?).into()))165 })?,166 "mod" => parse_args!(context, "std.mod", args, 2, [167 0, a: [Val::Num | Val::Str], vec![ValType::Num, ValType::Str];168 1, b, vec![];169 ], {170 match (a, b) {171 (Val::Num(a), Val::Num(b)) => Ok(Val::Num(a % b)),172 (Val::Str(str), vals) => std_format(str, vals),173 (a, b) => throw!(BinaryOperatorDoesNotOperateOnValues(jrsonnet_parser::BinaryOpType::Mod, a.value_type()?, b.value_type()?))174 }209 }175 })?,210 _ => unreachable!()176 "modulo" => parse_args!(context, "std.modulo", args, 2, [211 }177 0, a: [Val::Num]!!Val::Num, vec![ValType::Num];212 })178 1, b: [Val::Num]!!Val::Num, vec![ValType::Num];213}179 ], {180 Ok(Val::Num(a % b))181 })?,182 "floor" => parse_args!(context, "std.floor", args, 1, [183 0, x: [Val::Num]!!Val::Num, vec![ValType::Num];184 ], {185 Ok(Val::Num(x.floor()))186 })?,187 "log" => parse_args!(context, "std.log", args, 2, [188 0, n: [Val::Num]!!Val::Num, vec![ValType::Num];189 ], {190 Ok(Val::Num(n.ln()))191 })?,192 "trace" => parse_args!(context, "std.trace", args, 2, [193 0, str: [Val::Str]!!Val::Str, vec![ValType::Str];194 1, rest, vec![];195 ], {196 eprint!("TRACE:");197 if let Some(loc) = loc {198 with_state(|s|{199 let locs = s.map_source_locations(&loc.0, &[loc.1]);200 eprint!(" {}:{}", loc.0.file_name().unwrap().to_str().unwrap(), locs[0].line);201 });202 }203 eprintln!(" {}", str);204 Ok(rest)205 })?,206 "pow" => parse_args!(context, "std.modulo", args, 2, [207 0, x: [Val::Num]!!Val::Num, vec![ValType::Num];208 1, n: [Val::Num]!!Val::Num, vec![ValType::Num];209 ], {210 Ok(Val::Num(x.powf(n)))211 })?,212 "extVar" => parse_args!(context, "std.extVar", args, 1, [213 0, x: [Val::Str]!!Val::Str, vec![ValType::Str];214 ], {215 Ok(with_state(|s| s.settings().ext_vars.get(&x).cloned()).ok_or(UndefinedExternalVariable(x))?)216 })?,217 "native" => parse_args!(context, "std.native", args, 1, [218 0, x: [Val::Str]!!Val::Str, vec![ValType::Str];219 ], {220 Ok(with_state(|s| s.settings().ext_natives.get(&x).cloned()).map(|v| Val::Func(Rc::new(FuncVal::NativeExt(x.clone(), v)))).ok_or(UndefinedExternalFunction(x))?)221 })?,222 "filter" => parse_args!(context, "std.filter", args, 2, [223 0, func: [Val::Func]!!Val::Func, vec![ValType::Func];224 1, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];225 ], {226 Ok(Val::Arr(Rc::new(227 arr.iter()228 .cloned()229 .filter(|e| {230 func231 .evaluate_values(context.clone(), &[e.clone()])232 .unwrap()233 .try_cast_bool("filter predicate")234 .unwrap()235 })236 .collect(),237 )))238 })?,239 // faster240 "foldl" => parse_args!(context, "std.foldl", args, 3, [241 0, func: [Val::Func]!!Val::Func, vec![ValType::Func];242 1, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];243 2, init, vec![];244 ], {245 let mut acc = init;246 for i in arr.iter().cloned() {247 acc = func.evaluate_values(context.clone(), &[acc, i])?;248 }249 Ok(acc)250 })?,251 // faster252 "foldr" => parse_args!(context, "std.foldr", args, 3, [253 0, func: [Val::Func]!!Val::Func, vec![ValType::Func];254 1, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];255 2, init, vec![];256 ], {257 let mut acc = init;258 for i in arr.iter().rev().cloned() {259 acc = func.evaluate_values(context.clone(), &[acc, i])?;260 }261 Ok(acc)262 })?,263 // faster264 #[allow(non_snake_case)]265 "sortImpl" => parse_args!(context, "std.sort", args, 2, [266 0, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];267 1, keyF: [Val::Func]!!Val::Func, vec![ValType::Func];268 ], {269 if arr.len() <= 1 {270 return Ok(Val::Arr(arr))271 }272 Ok(Val::Arr(sort::sort(context, arr, &keyF)?))273 })?,274 // faster275 "format" => parse_args!(context, "std.format", args, 2, [276 0, str: [Val::Str]!!Val::Str, vec![ValType::Str];277 1, vals, vec![]278 ], {279 std_format(str, vals)280 })?,281 // faster282 "range" => parse_args!(context, "std.range", args, 2, [283 0, from: [Val::Num]!!Val::Num, vec![ValType::Num];284 1, to: [Val::Num]!!Val::Num, vec![ValType::Num];285 ], {286 if to < from {287 return Ok(Val::Arr(Rc::new(Vec::new())))288 }289 let mut out = Vec::with_capacity((1+to as usize-from as usize).max(0));290 for i in from as usize..=to as usize {291 out.push(Val::Num(i as f64));292 }293 Ok(Val::Arr(Rc::new(out)))294 })?,295 "char" => parse_args!(context, "std.char", args, 1, [296 0, n: [Val::Num]!!Val::Num, vec![ValType::Num];297 ], {298 let mut out = String::new();299 out.push(std::char::from_u32(n as u32).ok_or_else(||300 InvalidUnicodeCodepointGot(n as u32)301 )?);302 Ok(Val::Str(out.into()))303 })?,304 "encodeUTF8" => parse_args!(context, "std.encodeUtf8", args, 1, [305 0, str: [Val::Str]!!Val::Str, vec![ValType::Str];306 ], {307 Ok(Val::Arr(Rc::new(str.bytes().map(|b| Val::Num(b as f64)).collect())))308 })?,309 "md5" => parse_args!(context, "std.md5", args, 1, [310 0, str: [Val::Str]!!Val::Str, vec![ValType::Str];311 ], {312 Ok(Val::Str(format!("{:x}", md5::compute(&str.as_bytes())).into()))313 })?,314 // faster315 "base64" => parse_args!(context, "std.base64", args, 1, [316 0, input: [Val::Str | Val::Arr], vec![ValType::Arr, ValType::Str];317 ], {318 Ok(Val::Str(match input {319 Val::Str(s) => {320 base64::encode(s.bytes().collect::<Vec<_>>()).into()321 },322 Val::Arr(a) => {323 base64::encode(a.iter().map(|v| {324 Ok(v.clone().try_cast_num("base64 array")? as u8)325 }).collect::<Result<Vec<_>>>()?).into()326 },327 _ => unreachable!()328 }))329 })?,330 // faster331 "join" => parse_args!(context, "std.join", args, 2, [332 0, sep: [Val::Str|Val::Arr], vec![ValType::Str, ValType::Arr];333 1, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];334 ], {335 Ok(match sep {336 Val::Arr(joiner_items) => {337 let mut out = Vec::new();338214339 let mut first = true;215// faster340 for item in arr.iter().cloned() {216fn builtin_primitive_equals(341 if let Val::Arr(items) = item.unwrap_if_lazy()? {217 context: Context,342 if !first {218 _loc: Option<&ExprLocation>,343 out.reserve(joiner_items.len());219 args: &ArgsDesc,344 out.extend(joiner_items.iter().cloned());220) -> Result<Val> {221 parse_args!(context, "primitiveEquals", args, 2, [222 0, a: ty!(any);223 1, b: ty!(any);224 ], {225 Ok(Val::Bool(primitive_equals(&a, &b)?))226 })227}228229// faster230fn builtin_equals(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {231 parse_args!(context, "equals", args, 2, [232 0, a: ty!(any);233 1, b: ty!(any);234 ], {235 Ok(Val::Bool(equals(&a, &b)?))236 })237}238239fn builtin_modulo(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {240 parse_args!(context, "modulo", args, 2, [241 0, a: ty!(number) => Val::Num;242 1, b: ty!(number) => Val::Num;243 ], {244 Ok(Val::Num(a % b))245 })246}247248fn builtin_mod(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {249 parse_args!(context, "mod", args, 2, [250 0, a: ty!((number | string));251 1, b: ty!(any);252 ], {253 match (a, b) {254 (Val::Num(a), Val::Num(b)) => Ok(Val::Num(a % b)),255 (Val::Str(str), vals) => std_format(str, vals),256 (a, b) => throw!(BinaryOperatorDoesNotOperateOnValues(BinaryOpType::Mod, a.value_type(), b.value_type()))257 }258 })259}260261fn builtin_floor(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {262 parse_args!(context, "floor", args, 1, [263 0, x: ty!(number) => Val::Num;264 ], {265 Ok(Val::Num(x.floor()))266 })267}268269fn builtin_log(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {270 parse_args!(context, "log", args, 1, [271 0, n: ty!(number) => Val::Num;272 ], {273 Ok(Val::Num(n.ln()))274 })275}276277fn builtin_pow(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {278 parse_args!(context, "pow", args, 2, [279 0, x: ty!(number) => Val::Num;280 1, n: ty!(number) => Val::Num;281 ], {282 Ok(Val::Num(x.powf(n)))283 })284}285286fn builtin_ext_var(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {287 parse_args!(context, "extVar", args, 1, [288 0, x: ty!(string) => Val::Str;289 ], {290 Ok(with_state(|s| s.settings().ext_vars.get(&x).cloned()).ok_or(UndefinedExternalVariable(x))?)291 })292}293294fn builtin_native(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {295 parse_args!(context, "native", args, 1, [296 0, x: ty!(string) => Val::Str;297 ], {298 Ok(with_state(|s| s.settings().ext_natives.get(&x).cloned()).map(|v| Val::Func(Rc::new(FuncVal::NativeExt(x.clone(), v)))).ok_or(UndefinedExternalFunction(x))?)299 })300}301302fn builtin_filter(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {303 parse_args!(context, "filter", args, 2, [304 0, func: ty!(function) => Val::Func;305 1, arr: ty!(array) => Val::Arr;306 ], {307 let mut out = Vec::new();308 for item in arr.iter() {309 let item = item?;310 if func311 .evaluate_values(context.clone(), &[item.clone()])?312 .try_cast_bool("filter predicate")? {313 out.push(item);314 }315 }316 Ok(Val::Arr(out.into()))317 })318}319320fn builtin_foldl(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {321 parse_args!(context, "foldl", args, 3, [322 0, func: ty!(function) => Val::Func;323 1, arr: ty!(array) => Val::Arr;324 2, init: ty!(any);325 ], {326 let mut acc = init;327 for i in arr.iter() {328 acc = func.evaluate_values(context.clone(), &[acc, i?])?;329 }330 Ok(acc)331 })332}333334fn builtin_foldr(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {335 parse_args!(context, "foldr", args, 3, [336 0, func: ty!(function) => Val::Func;337 1, arr: ty!(array) => Val::Arr;338 2, init: ty!(any);339 ], {340 let mut acc = init;341 for i in arr.iter().rev() {342 acc = func.evaluate_values(context.clone(), &[acc, i?])?;343 }344 Ok(acc)345 })346}347348#[allow(non_snake_case)]349fn builtin_sort_impl(350 context: Context,351 _loc: Option<&ExprLocation>,352 args: &ArgsDesc,353) -> Result<Val> {354 parse_args!(context, "sort", args, 2, [355 0, arr: ty!(array) => Val::Arr;356 1, keyF: ty!(function) => Val::Func;357 ], {358 if arr.len() <= 1 {359 return Ok(Val::Arr(arr))360 }361 Ok(Val::Arr(ArrValue::Eager(sort::sort(context, arr.evaluated()?, &keyF)?)))362 })363}364365// faster366fn builtin_format(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {367 parse_args!(context, "format", args, 2, [368 0, str: ty!(string) => Val::Str;369 1, vals: ty!(any)370 ], {371 std_format(str, vals)372 })373}374375fn builtin_range(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {376 parse_args!(context, "range", args, 2, [377 0, from: ty!(number) => Val::Num;378 1, to: ty!(number) => Val::Num;379 ], {380 if to < from {381 return Ok(Val::Arr(ArrValue::new_eager()))382 }383 let mut out = Vec::with_capacity((1+to as usize-from as usize).max(0));384 for i in from as usize..=to as usize {385 out.push(Val::Num(i as f64));386 }387 Ok(Val::Arr(out.into()))388 })389}390391fn builtin_char(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {392 parse_args!(context, "char", args, 1, [393 0, n: ty!(number) => Val::Num;394 ], {395 let mut out = String::new();396 out.push(std::char::from_u32(n as u32).ok_or_else(||397 InvalidUnicodeCodepointGot(n as u32)398 )?);399 Ok(Val::Str(out.into()))400 })401}402403fn builtin_encode_utf8(404 context: Context,405 _loc: Option<&ExprLocation>,406 args: &ArgsDesc,407) -> Result<Val> {408 parse_args!(context, "encodeUTF8", args, 1, [409 0, str: ty!(string) => Val::Str;410 ], {411 Ok(Val::Arr((str.bytes().map(|b| Val::Num(b as f64)).collect::<Vec<Val>>()).into()))412 })413}414415fn builtin_md5(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {416 parse_args!(context, "md5", args, 1, [417 0, str: ty!(string) => Val::Str;418 ], {419 Ok(Val::Str(format!("{:x}", md5::compute(&str.as_bytes())).into()))420 })421}422423fn builtin_trace(context: Context, loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {424 parse_args!(context, "trace", args, 2, [425 0, str: ty!(string) => Val::Str;426 1, rest: ty!(any);427 ], {428 eprint!("TRACE:");429 if let Some(loc) = loc {430 with_state(|s|{431 let locs = s.map_source_locations(&loc.0, &[loc.1]);432 eprint!(" {}:{}", loc.0.file_name().unwrap().to_str().unwrap(), locs[0].line);433 });434 }435 eprintln!(" {}", str);436 Ok(rest)437 })438}439440fn builtin_base64(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {441 parse_args!(context, "base64", args, 1, [442 0, input: ty!((string | (Array<number>)));443 ], {444 Ok(Val::Str(match input {445 Val::Str(s) => {446 base64::encode(s.bytes().collect::<Vec<_>>()).into()447 },448 Val::Arr(a) => {449 base64::encode(a.iter().map(|v| {450 Ok(v?.unwrap_num()? as u8)451 }).collect::<Result<Vec<_>>>()?).into()452 },453 _ => unreachable!()454 }))455 })456}457458fn builtin_join(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {459 parse_args!(context, "join", args, 2, [460 0, sep: ty!((string | array));461 1, arr: ty!(array) => Val::Arr;462 ], {463 Ok(match sep {464 Val::Arr(joiner_items) => {465 let mut out = Vec::new();466467 let mut first = true;468 for item in arr.iter() {469 let item = item?.clone();470 if let Val::Arr(items) = item {471 if !first {472 out.reserve(joiner_items.len());473 // TODO: extend474 for item in joiner_items.iter() {475 out.push(item?);345 }476 }346 first = false;347 out.reserve(items.len());348 out.extend(items.iter().cloned());349 } else {350 throw!(RuntimeError("in std.join all items should be arrays".into()));351 }477 }478 first = false;479 out.reserve(items.len());480 // TODO: extend481 for item in items.iter() {482 out.push(item?);483 }484 } else {485 throw!(RuntimeError("in std.join all items should be arrays".into()));352 }486 }487 }353488354 Val::Arr(Rc::new(out))489 Val::Arr(out.into())355 },490 },356 Val::Str(sep) => {491 Val::Str(sep) => {357 let mut out = String::new();492 let mut out = String::new();358493359 let mut first = true;494 let mut first = true;360 for item in arr.iter().cloned() {495 for item in arr.iter() {361 if let Val::Str(item) = item.unwrap_if_lazy()? {496 let item = item?.clone();497 if let Val::Str(item) = item {362 if !first {498 if !first {363 out += &sep;499 out += &sep;364 }365 first = false;366 out += &item;367 } else {368 throw!(RuntimeError("in std.join all items should be strings".into()));369 }500 }501 first = false;502 out += &item;503 } else {504 throw!(RuntimeError("in std.join all items should be strings".into()));370 }505 }506 }371507372 Val::Str(out.into())508 Val::Str(out.into())373 },509 },374 _ => unreachable!()510 _ => unreachable!()375 })511 })376 })?,377 // Faster378 "escapeStringJson" => parse_args!(context, "std.escapeStringJson", args, 1, [379 0, str_: [Val::Str]!!Val::Str, vec![ValType::Str];380 ], {381 Ok(Val::Str(escape_string_json(&str_).into()))382 })?,383 // Faster384 "manifestJsonEx" => parse_args!(context, "std.manifestJsonEx", args, 2, [385 0, value, vec![];386 1, indent: [Val::Str]!!Val::Str, vec![ValType::Str];387 ], {388 Ok(Val::Str(manifest_json_ex(&value, &ManifestJsonOptions {389 padding: &indent,390 mtype: ManifestType::Std,391 })?.into()))392 })?,393 // Faster394 "reverse" => parse_args!(context, "std.reverse", args, 1, [395 0, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];396 ], {397 let mut marr = arr;398 Rc::make_mut(&mut marr).reverse();399 Ok(Val::Arr(marr))400 })?,401 "id" => parse_args!(context, "std.id", args, 1, [402 0, v, vec![];403 ], {404 Ok(v)405 })?,406 name => throw!(IntrinsicNotFound(name.into())),407 })512 })513}514515// faster516fn builtin_escape_string_json(517 context: Context,518 _loc: Option<&ExprLocation>,519 args: &ArgsDesc,520) -> Result<Val> {521 parse_args!(context, "escapeStringJson", args, 1, [522 0, str_: ty!(string) => Val::Str;523 ], {524 Ok(Val::Str(escape_string_json(&str_).into()))525 })526}527528// faster529fn builtin_manifest_json_ex(530 context: Context,531 _loc: Option<&ExprLocation>,532 args: &ArgsDesc,533) -> Result<Val> {534 parse_args!(context, "manifestJsonEx", args, 2, [535 0, value: ty!(any);536 1, indent: ty!(string) => Val::Str;537 ], {538 Ok(Val::Str(manifest_json_ex(&value, &ManifestJsonOptions {539 padding: &indent,540 mtype: ManifestType::Std,541 })?.into()))542 })543}544545// faster546fn builtin_reverse(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {547 parse_args!(context, "reverse", args, 1, [548 0, value: ty!(array) => Val::Arr;549 ], {550 Ok(Val::Arr(value.reversed()))551 })552}553554fn builtin_id(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {555 parse_args!(context, "id", args, 1, [556 0, v: ty!(any);557 ], {558 Ok(v)559 })560}561562// faster563fn builtin_str_replace(564 context: Context,565 _loc: Option<&ExprLocation>,566 args: &ArgsDesc,567) -> Result<Val> {568 parse_args!(context, "strReplace", args, 3, [569 0, str: ty!(string) => Val::Str;570 1, from: ty!(string) => Val::Str;571 2, to: ty!(string) => Val::Str;572 ], {573 let mut out = String::new();574 let mut last_idx = 0;575 while let Some(idx) = (&str[last_idx..]).find(&from as &str) {576 out.push_str(&str[last_idx..last_idx+idx]);577 out.push_str(&to);578 last_idx += idx + from.len();579 }580 if last_idx == 0 {581 return Ok(Val::Str(str))582 }583 out.push_str(&str[last_idx..]);584 Ok(Val::Str(out.into()))585 })586}587588pub fn call_builtin(589 context: Context,590 loc: Option<&ExprLocation>,591 name: &str,592 args: &ArgsDesc,593) -> Result<Val> {594 if let Some(f) = BUILTINS.with(|builtins| builtins.get(name).copied()) {595 return Ok(f(context, loc, args)?);596 }597 throw!(IntrinsicNotFound(name.into()))408}598}409599crates/jrsonnet-evaluator/src/builtin/sort.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/builtin/sort.rs
+++ b/crates/jrsonnet-evaluator/src/builtin/sort.rs
@@ -46,7 +46,6 @@
let mut sort_type = SortKeyType::Unknown;
for i in values.iter_mut() {
let i = key_getter(i);
- i.inplace_unwrap()?;
match (i, sort_type) {
(Val::Str(_), SortKeyType::Unknown) => sort_type = SortKeyType::String,
(Val::Num(_), SortKeyType::Unknown) => sort_type = SortKeyType::Number,
crates/jrsonnet-evaluator/src/ctx.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/ctx.rs
+++ b/crates/jrsonnet-evaluator/src/ctx.rs
@@ -2,6 +2,7 @@
error::Error::*, future_wrapper, map::LayeredHashMap, rc_fn_helper, resolved_lazy_val,
LazyBinding, LazyVal, ObjValue, Result, Val,
};
+use jrsonnet_interner::IStr;
use rustc_hash::FxHashMap;
use std::hash::BuildHasherDefault;
use std::{cell::RefCell, collections::HashMap, fmt::Debug, rc::Rc};
@@ -18,7 +19,7 @@
dollar: Option<ObjValue>,
this: Option<ObjValue>,
super_obj: Option<ObjValue>,
- bindings: LayeredHashMap<Rc<str>, LazyVal>,
+ bindings: LayeredHashMap<LazyVal>,
}
impl Debug for ContextInternals {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
@@ -57,7 +58,7 @@
}))
}
- pub fn binding(&self, name: Rc<str>) -> Result<LazyVal> {
+ pub fn binding(&self, name: IStr) -> Result<LazyVal> {
Ok(self
.0
.bindings
@@ -72,7 +73,7 @@
ctx.unwrap()
}
- pub fn with_var(self, name: Rc<str>, value: Val) -> Self {
+ pub fn with_var(self, name: IStr, value: Val) -> Self {
let mut new_bindings =
FxHashMap::with_capacity_and_hasher(1, BuildHasherDefault::default());
new_bindings.insert(name, resolved_lazy_val!(value));
@@ -81,7 +82,7 @@
pub fn extend(
self,
- new_bindings: FxHashMap<Rc<str>, LazyVal>,
+ new_bindings: FxHashMap<IStr, LazyVal>,
new_dollar: Option<ObjValue>,
new_this: Option<ObjValue>,
new_super_obj: Option<ObjValue>,
@@ -123,7 +124,7 @@
}
pub fn extend_unbound(
self,
- new_bindings: HashMap<Rc<str>, LazyBinding>,
+ new_bindings: HashMap<IStr, LazyBinding>,
new_dollar: Option<ObjValue>,
new_this: Option<ObjValue>,
new_super_obj: Option<ObjValue>,
crates/jrsonnet-evaluator/src/error.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/error.rs
+++ b/crates/jrsonnet-evaluator/src/error.rs
@@ -1,15 +1,17 @@
use crate::{
builtin::{format::FormatError, sort::SortError},
- ValType,
+ typed::TypeLocError,
};
+use jrsonnet_interner::IStr;
use jrsonnet_parser::{BinaryOpType, ExprLocation, UnaryOpType};
+use jrsonnet_types::ValType;
use std::{path::PathBuf, rc::Rc};
use thiserror::Error;
#[derive(Error, Debug, Clone)]
pub enum Error {
#[error("intrinsic not found: {0}")]
- IntrinsicNotFound(Rc<str>),
+ IntrinsicNotFound(IStr),
#[error("argument reordering in intrisics not supported yet")]
IntrinsicArgumentReorderingIsNotSupportedYet,
@@ -32,36 +34,36 @@
ArrayBoundsError(usize, usize),
#[error("assert failed: {0}")]
- AssertionFailed(Rc<str>),
+ AssertionFailed(IStr),
#[error("variable is not defined: {0}")]
- VariableIsNotDefined(Rc<str>),
+ VariableIsNotDefined(IStr),
#[error("type mismatch: expected {}, got {2} {0}", .1.iter().map(|e| format!("{}", e)).collect::<Vec<_>>().join(", "))]
TypeMismatch(&'static str, Vec<ValType>, ValType),
#[error("no such field: {0}")]
- NoSuchField(Rc<str>),
+ NoSuchField(IStr),
#[error("only functions can be called, got {0}")]
OnlyFunctionsCanBeCalledGot(ValType),
#[error("parameter {0} is not defined")]
UnknownFunctionParameter(String),
#[error("argument {0} is already bound")]
- BindingParameterASecondTime(Rc<str>),
+ BindingParameterASecondTime(IStr),
#[error("too many args, function has {0}")]
TooManyArgsFunctionHas(usize),
#[error("founction argument is not passed: {0}")]
- FunctionParameterNotBoundInCall(Rc<str>),
+ FunctionParameterNotBoundInCall(IStr),
#[error("external variable is not defined: {0}")]
- UndefinedExternalVariable(Rc<str>),
+ UndefinedExternalVariable(IStr),
#[error("native is not defined: {0}")]
- UndefinedExternalFunction(Rc<str>),
+ UndefinedExternalFunction(IStr),
#[error("field name should be string, got {0}")]
FieldMustBeStringGot(ValType),
#[error("attempted to index array with string {0}")]
- AttemptedIndexAnArrayWithString(Rc<str>),
+ AttemptedIndexAnArrayWithString(IStr),
#[error("{0} index type should be {1}, got {2}")]
ValueIndexMustBeTypeGot(ValType, ValType, ValType),
#[error("cant index into {0}")]
@@ -85,12 +87,12 @@
)]
ImportSyntaxError {
path: Rc<PathBuf>,
- source_code: Rc<str>,
+ source_code: IStr,
error: Box<jrsonnet_parser::ParseError>,
},
#[error("runtime error: {0}")]
- RuntimeError(Rc<str>),
+ RuntimeError(IStr),
#[error("stack overflow, try to reduce recursion, or set --max-stack to bigger value")]
StackOverflow,
#[error("tried to index by fractional value")]
@@ -117,6 +119,8 @@
#[error("format error: {0}")]
Format(#[from] FormatError),
+ #[error("type error: {0}")]
+ TypeError(TypeLocError),
#[error("sort error: {0}")]
Sort(#[from] SortError),
}
@@ -128,7 +132,7 @@
#[derive(Clone, Debug)]
pub struct StackTraceElement {
- pub location: ExprLocation,
+ pub location: Option<ExprLocation>,
pub desc: String,
}
#[derive(Debug, Clone)]
@@ -144,6 +148,9 @@
pub const fn error(&self) -> &Error {
&(self.0).0
}
+ pub fn error_mut(&mut self) -> &mut Error {
+ &mut (self.0).0
+ }
pub const fn trace(&self) -> &StackTrace {
&(self.0).1
}
crates/jrsonnet-evaluator/src/evaluate.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/evaluate.rs
+++ b/crates/jrsonnet-evaluator/src/evaluate.rs
@@ -1,18 +1,19 @@
use crate::{
context_creator, error::Error::*, future_wrapper, lazy_val, push, throw, with_state, Context,
ContextCreator, FuncDesc, FuncVal, LazyBinding, LazyVal, ObjMember, ObjValue, Result, Val,
- ValType,
};
use closure::closure;
+use jrsonnet_interner::IStr;
use jrsonnet_parser::{
ArgsDesc, AssertStmt, BinaryOpType, BindSpec, CompSpec, Expr, ExprLocation, FieldMember,
ForSpecData, IfSpecData, LiteralType, LocExpr, Member, ObjBody, ParamsDesc, UnaryOpType,
Visibility,
};
+use jrsonnet_types::ValType;
use rustc_hash::FxHashMap;
use std::{collections::HashMap, rc::Rc};
-pub fn evaluate_binding(b: &BindSpec, context_creator: ContextCreator) -> (Rc<str>, LazyBinding) {
+pub fn evaluate_binding(b: &BindSpec, context_creator: ContextCreator) -> (IStr, LazyBinding) {
let b = b.clone();
if let Some(params) = &b.params {
let params = params.clone();
@@ -45,7 +46,7 @@
}
}
-pub fn evaluate_method(ctx: Context, name: Rc<str>, params: ParamsDesc, body: LocExpr) -> Val {
+pub fn evaluate_method(ctx: Context, name: IStr, params: ParamsDesc, body: LocExpr) -> Val {
Val::Func(Rc::new(FuncVal::Normal(FuncDesc {
name,
ctx,
@@ -57,12 +58,11 @@
pub fn evaluate_field_name(
context: Context,
field_name: &jrsonnet_parser::FieldName,
-) -> Result<Option<Rc<str>>> {
+) -> Result<Option<IStr>> {
Ok(match field_name {
jrsonnet_parser::FieldName::Fixed(n) => Some(n.clone()),
jrsonnet_parser::FieldName::Dyn(expr) => {
- let lazy = evaluate(context, expr)?;
- let value = lazy.unwrap_if_lazy()?;
+ let value = evaluate(context, expr)?;
if matches!(value, Val::Null) {
None
} else {
@@ -74,11 +74,10 @@
pub fn evaluate_unary_op(op: UnaryOpType, b: &Val) -> Result<Val> {
Ok(match (op, b) {
- (o, Val::Lazy(l)) => evaluate_unary_op(o, &l.evaluate()?)?,
(UnaryOpType::Not, Val::Bool(v)) => Val::Bool(!v),
(UnaryOpType::Minus, Val::Num(n)) => Val::Num(-*n),
(UnaryOpType::BitNot, Val::Num(n)) => Val::Num(!(*n as i32) as f64),
- (op, o) => throw!(UnaryOperatorDoesNotOperateOnType(op, o.value_type()?)),
+ (op, o) => throw!(UnaryOperatorDoesNotOperateOnType(op, o.value_type())),
})
}
@@ -94,12 +93,17 @@
(o, Val::Str(s)) => Val::Str(format!("{}{}", o.clone().to_string()?, s).into()),
(Val::Obj(v1), Val::Obj(v2)) => Val::Obj(v2.with_super(v1.clone())),
- (Val::Arr(a), Val::Arr(b)) => Val::Arr(Rc::new([&a[..], &b[..]].concat())),
+ (Val::Arr(a), Val::Arr(b)) => {
+ let mut out = Vec::with_capacity(a.len() + b.len());
+ out.extend(a.iter_lazy());
+ out.extend(b.iter_lazy());
+ Val::Arr(out.into())
+ }
(Val::Num(v1), Val::Num(v2)) => Val::new_checked_num(v1 + v2)?,
_ => throw!(BinaryOperatorDoesNotOperateOnValues(
BinaryOpType::Add,
- a.value_type()?,
- b.value_type()?,
+ a.value_type(),
+ b.value_type(),
)),
})
}
@@ -110,15 +114,11 @@
op: BinaryOpType,
b: &LocExpr,
) -> Result<Val> {
- Ok(
- match (evaluate(context.clone(), a)?.unwrap_if_lazy()?, op, b) {
- (Val::Bool(true), BinaryOpType::Or, _o) => Val::Bool(true),
- (Val::Bool(false), BinaryOpType::And, _o) => Val::Bool(false),
- (a, op, eb) => {
- evaluate_binary_op_normal(&a, op, &evaluate(context, eb)?.unwrap_if_lazy()?)?
- }
- },
- )
+ Ok(match (evaluate(context.clone(), a)?, op, b) {
+ (Val::Bool(true), BinaryOpType::Or, _o) => Val::Bool(true),
+ (Val::Bool(false), BinaryOpType::And, _o) => Val::Bool(false),
+ (a, op, eb) => evaluate_binary_op_normal(&a, op, &evaluate(context, eb)?)?,
+ })
}
pub fn evaluate_binary_op_normal(a: &Val, op: BinaryOpType, b: &Val) -> Result<Val> {
@@ -177,13 +177,13 @@
_ => throw!(BinaryOperatorDoesNotOperateOnValues(
op,
- a.value_type()?,
- b.value_type()?,
+ a.value_type(),
+ b.value_type(),
)),
})
}
-future_wrapper!(HashMap<Rc<str>, LazyBinding>, FutureNewBindings);
+future_wrapper!(HashMap<IStr, LazyBinding>, FutureNewBindings);
future_wrapper!(ObjValue, FutureObjValue);
pub fn evaluate_comp<T>(
@@ -200,23 +200,20 @@
None
}
}
- Some(CompSpec::ForSpec(ForSpecData(var, expr))) => {
- match evaluate(context.clone(), expr)?.unwrap_if_lazy()? {
- Val::Arr(list) => {
- let mut out = Vec::new();
- for item in list.iter() {
- let item = item.unwrap_if_lazy()?;
- out.push(evaluate_comp(
- context.clone().with_var(var.clone(), item.clone()),
- value,
- &specs[1..],
- )?);
- }
- Some(out.into_iter().flatten().flatten().collect())
+ Some(CompSpec::ForSpec(ForSpecData(var, expr))) => match evaluate(context.clone(), expr)? {
+ Val::Arr(list) => {
+ let mut out = Vec::new();
+ for item in list.iter() {
+ out.push(evaluate_comp(
+ context.clone().with_var(var.clone(), item?.clone()),
+ value,
+ &specs[1..],
+ )?);
}
- _ => throw!(InComprehensionCanOnlyIterateOverArray),
+ Some(out.into_iter().flatten().flatten().collect())
}
- }
+ _ => throw!(InComprehensionCanOnlyIterateOverArray),
+ },
})
}
@@ -234,7 +231,7 @@
})
);
{
- let mut bindings: HashMap<Rc<str>, LazyBinding> = HashMap::new();
+ let mut bindings: HashMap<IStr, LazyBinding> = HashMap::new();
for (n, b) in members
.iter()
.filter_map(|m| match m {
@@ -338,7 +335,7 @@
)?)
})
);
- let mut bindings: HashMap<Rc<str>, LazyBinding> = HashMap::new();
+ let mut bindings: HashMap<IStr, LazyBinding> = HashMap::new();
for (n, b) in obj
.pre_locals
.iter()
@@ -375,7 +372,7 @@
},
);
}
- v => throw!(FieldMustBeStringGot(v.value_type()?)),
+ v => throw!(FieldMustBeStringGot(v.value_type())),
}
}
@@ -388,11 +385,10 @@
context: Context,
value: &LocExpr,
args: &ArgsDesc,
- loc: &Option<ExprLocation>,
+ loc: Option<&ExprLocation>,
tailstrict: bool,
) -> Result<Val> {
- let lazy = evaluate(context.clone(), value)?;
- let value = lazy.unwrap_if_lazy()?;
+ let value = evaluate(context.clone(), value)?;
Ok(match value {
Val::Func(f) => {
let body = || f.evaluate(context, loc, args, tailstrict);
@@ -402,11 +398,11 @@
push(loc, || format!("function <{}> call", f.name()), body)?
}
}
- v => throw!(OnlyFunctionsCanBeCalledGot(v.value_type()?)),
+ v => throw!(OnlyFunctionsCanBeCalledGot(v.value_type())),
})
}
-pub fn evaluate_named(context: Context, lexpr: &LocExpr, name: Rc<str>) -> Result<Val> {
+pub fn evaluate_named(context: Context, lexpr: &LocExpr, name: IStr) -> Result<Val> {
use Expr::*;
let LocExpr(expr, _loc) = lexpr;
Ok(match &**expr {
@@ -434,9 +430,9 @@
BinaryOp(v1, o, v2) => evaluate_binary_op_special(context, v1, *o, v2)?,
UnaryOp(o, v) => evaluate_unary_op(*o, &evaluate(context, v)?)?,
Var(name) => push(
- loc,
+ loc.as_ref(),
|| format!("variable <{}>", name),
- || Ok(Val::Lazy(context.binding(name.clone())?).unwrap_if_lazy()?),
+ || Ok(context.binding(name.clone())?.evaluate()?),
)?,
Index(LocExpr(v, _), index) if matches!(&**v, Expr::Literal(LiteralType::Super)) => {
let name = evaluate(context.clone(), index)?.try_cast_str("object index")?;
@@ -444,22 +440,19 @@
.super_obj()
.clone()
.expect("no super found")
- .get_raw(name, &context.this().clone().expect("no this found"))?
+ .get_raw(name, Some(&context.this().clone().expect("no this found")))?
.expect("value not found")
}
Index(value, index) => {
- match (
- evaluate(context.clone(), value)?.unwrap_if_lazy()?,
- evaluate(context, index)?,
- ) {
+ match (evaluate(context.clone(), value)?, evaluate(context, index)?) {
(Val::Obj(v), Val::Str(s)) => {
let sn = s.clone();
push(
- loc,
+ loc.as_ref(),
|| format!("field <{}> access", sn),
|| {
if let Some(v) = v.get(s.clone())? {
- Ok(v.unwrap_if_lazy()?)
+ Ok(v)
} else if v.get("__intrinsic_namespace__".into())?.is_some() {
Ok(Val::Func(Rc::new(FuncVal::Intrinsic(s))))
} else {
@@ -471,23 +464,21 @@
(Val::Obj(_), n) => throw!(ValueIndexMustBeTypeGot(
ValType::Obj,
ValType::Str,
- n.value_type()?,
+ n.value_type(),
)),
(Val::Arr(v), Val::Num(n)) => {
if n.fract() > f64::EPSILON {
throw!(FractionalIndex)
}
- v.get(n as usize)
+ v.get(n as usize)?
.ok_or_else(|| ArrayBoundsError(n as usize, v.len()))?
- .clone()
- .unwrap_if_lazy()?
}
(Val::Arr(_), Val::Str(n)) => throw!(AttemptedIndexAnArrayWithString(n)),
(Val::Arr(_), n) => throw!(ValueIndexMustBeTypeGot(
ValType::Arr,
ValType::Num,
- n.value_type()?,
+ n.value_type(),
)),
(Val::Str(s), Val::Num(n)) => Val::Str(
@@ -500,14 +491,14 @@
(Val::Str(_), n) => throw!(ValueIndexMustBeTypeGot(
ValType::Str,
ValType::Num,
- n.value_type()?,
+ n.value_type(),
)),
- (v, _) => throw!(CantIndexInto(v.value_type()?)),
+ (v, _) => throw!(CantIndexInto(v.value_type())),
}
}
LocalExpr(bindings, returned) => {
- let mut new_bindings: HashMap<Rc<str>, LazyBinding> = HashMap::new();
+ let mut new_bindings: HashMap<IStr, LazyBinding> = HashMap::new();
let future_context = Context::new_future();
let context_creator = context_creator!(
@@ -529,31 +520,35 @@
Arr(items) => {
let mut out = Vec::with_capacity(items.len());
for item in items {
- out.push(Val::Lazy(lazy_val!(
+ out.push(LazyVal::new(Box::new(
closure!(clone context, clone item, || {
evaluate(context.clone(), &item)
- })
+ }),
)));
}
- Val::Arr(Rc::new(out))
+ Val::Arr(out.into())
}
ArrComp(expr, comp_specs) => Val::Arr(
// First comp_spec should be for_spec, so no "None" possible here
- Rc::new(evaluate_comp(context, &|ctx| evaluate(ctx, expr), comp_specs)?.unwrap()),
+ evaluate_comp(context, &|ctx| evaluate(ctx, expr), comp_specs)?
+ .unwrap()
+ .into(),
),
Obj(body) => Val::Obj(evaluate_object(context, body)?),
ObjExtend(s, t) => evaluate_add_op(
&evaluate(context.clone(), s)?,
&Val::Obj(evaluate_object(context, t)?),
)?,
- Apply(value, args, tailstrict) => evaluate_apply(context, value, args, loc, *tailstrict)?,
+ Apply(value, args, tailstrict) => {
+ evaluate_apply(context, value, args, loc.as_ref(), *tailstrict)?
+ }
Function(params, body) => {
evaluate_method(context, "anonymous".into(), params.clone(), body.clone())
}
Intrinsic(name) => Val::Func(Rc::new(FuncVal::Intrinsic(name.clone()))),
AssertExpr(AssertStmt(value, msg), returned) => {
let assertion_result = push(
- &value.1,
+ value.1.as_ref(),
|| "assertion condition".to_owned(),
|| {
evaluate(context.clone(), value)?
@@ -562,14 +557,22 @@
)?;
if assertion_result {
evaluate(context, returned)?
- } else if let Some(msg) = msg {
- throw!(AssertionFailed(evaluate(context, msg)?.to_string()?));
} else {
- throw!(AssertionFailed(Val::Null.to_string()?));
+ push(
+ value.1.as_ref(),
+ || "assertion failure".to_owned(),
+ || {
+ if let Some(msg) = msg {
+ throw!(AssertionFailed(evaluate(context, msg)?.to_string()?));
+ } else {
+ throw!(AssertionFailed(Val::Null.to_string()?));
+ }
+ },
+ )?
}
}
ErrorStmt(e) => push(
- loc,
+ loc.as_ref(),
|| "error statement".to_owned(),
|| {
throw!(RuntimeError(
@@ -583,7 +586,7 @@
cond_else,
} => {
if push(
- loc,
+ loc.as_ref(),
|| "if condition".to_owned(),
|| evaluate(context.clone(), &cond.0)?.try_cast_bool("in if condition"),
)? {
@@ -603,7 +606,7 @@
let import_location = Rc::make_mut(&mut tmp);
import_location.pop();
push(
- loc,
+ loc.as_ref(),
|| format!("import {:?}", path),
|| with_state(|s| s.import_file(import_location, path)),
)?
crates/jrsonnet-evaluator/src/function.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/function.rs
+++ b/crates/jrsonnet-evaluator/src/function.rs
@@ -1,8 +1,9 @@
use crate::{error::Error::*, evaluate, lazy_val, resolved_lazy_val, throw, Context, Result, Val};
use closure::closure;
+use jrsonnet_interner::IStr;
use jrsonnet_parser::{ArgsDesc, ParamsDesc};
use rustc_hash::FxHashMap;
-use std::{collections::HashMap, hash::BuildHasherDefault, rc::Rc};
+use std::{collections::HashMap, hash::BuildHasherDefault};
const NO_DEFAULT_CONTEXT: &str =
"no default context set for call with defined default parameter value";
@@ -66,7 +67,7 @@
ctx: Context,
body_ctx: Option<Context>,
params: &ParamsDesc,
- args: &HashMap<Rc<str>, Val>,
+ args: &HashMap<IStr, Val>,
tailstrict: bool,
) -> Result<Context> {
let mut out = FxHashMap::with_capacity_and_hasher(params.len(), BuildHasherDefault::default());
@@ -143,9 +144,10 @@
#[macro_export]
macro_rules! parse_args {
($ctx: expr, $fn_name: expr, $args: expr, $total_args: expr, [
- $($id: expr, $name: ident $(: [$($p: path)|+] $(!! $a: path)?)?, $nt: expr);+ $(;)?
+ $($id: expr, $name: ident: $ty: expr $(=>$match: path)?);+ $(;)?
], $handler:block) => {{
- use crate::{throw, error::Error::*};
+ use $crate::{error::Error::*, throw, evaluate, push_stack_frame, typed::CheckType};
+
let args = $args;
if args.len() > $total_args {
throw!(TooManyArgsFunctionHas($total_args));
@@ -160,47 +162,19 @@
throw!(IntrinsicArgumentReorderingIsNotSupportedYet);
}
}
- let $name = evaluate($ctx.clone(), &$name.1)?;
+ let $name = push_stack_frame(None, || format!("evaluating argument"), || {
+ let value = evaluate($ctx.clone(), &$name.1)?;
+ $ty.check(&value)?;
+ Ok(value)
+ })?;
$(
- match $name {
- $($p(_))|+ => {},
- _ => throw!(TypeMismatch(
- concat!($fn_name, " ", stringify!($id), "nd (", stringify!($name), ") argument"),
- $nt, $name.value_type()?
- )),
+ let $name = if let $match(v) = $name {
+ v
+ } else {
+ unreachable!();
};
- $(
- let $name = match $name {
- $a(v) => v,
- _ =>throw!(TypeMismatch(concat!($fn_name, " ", stringify!($id), "nd (", stringify!($name), ") argument"), $nt, $name.value_type()?)),
- };
- )*
- )*
+ )?
)+
($handler as crate::Result<_>)
}};
-}
-
-#[test]
-fn test() -> Result<()> {
- use crate::val::ValType;
- use jrsonnet_parser::*;
- let state = crate::EvaluationState::default();
- let evaluator = state.with_stdlib();
- let ctx = evaluator.create_default_context()?;
- evaluator.run_in_state(|| {
- parse_args!(ctx, "test", ArgsDesc(vec![
- Arg(None, el!(Expr::Num(2.0))),
- Arg(Some("b".into()), el!(Expr::Num(1.0))),
- ]), 2, [
- 0, a: [Val::Num]!!Val::Num, vec![ValType::Num];
- 1, b: [Val::Num]!!Val::Num, vec![ValType::Num];
- ], {
- assert!((a - 2.0).abs() <= f64::EPSILON);
- assert!((b - 1.0).abs() <= f64::EPSILON);
- Ok(())
- })
- .unwrap();
- Ok(())
- })
}
crates/jrsonnet-evaluator/src/import.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/import.rs
+++ b/crates/jrsonnet-evaluator/src/import.rs
@@ -3,6 +3,7 @@
throw,
};
use fs::File;
+use jrsonnet_interner::IStr;
use std::fs;
use std::io::Read;
use std::{any::Any, cell::RefCell, collections::HashMap, path::PathBuf, rc::Rc};
@@ -15,7 +16,7 @@
fn resolve_file(&self, from: &PathBuf, path: &PathBuf) -> Result<Rc<PathBuf>>;
/// Reads file from filesystem, should be used only with path received from `resolve_file`
- fn load_file_contents(&self, resolved: &PathBuf) -> Result<Rc<str>>;
+ fn load_file_contents(&self, resolved: &PathBuf) -> Result<IStr>;
/// # Safety
///
@@ -32,7 +33,7 @@
throw!(ImportNotSupported(from.clone(), path.clone()))
}
- fn load_file_contents(&self, _resolved: &PathBuf) -> Result<Rc<str>> {
+ fn load_file_contents(&self, _resolved: &PathBuf) -> Result<IStr> {
// Can be only caused by library direct consumer, not by supplied jsonnet
panic!("dummy resolver can't load any file")
}
@@ -72,7 +73,7 @@
throw!(ImportFileNotFound(from.clone(), path.clone()))
}
}
- fn load_file_contents(&self, id: &PathBuf) -> Result<Rc<str>> {
+ fn load_file_contents(&self, id: &PathBuf) -> Result<IStr> {
let mut file = File::open(id).map_err(|_e| ResolvedFileNotFound(id.clone()))?;
let mut out = String::new();
file.read_to_string(&mut out)
@@ -89,7 +90,7 @@
/// Caches results of the underlying resolver
pub struct CachingImportResolver {
resolution_cache: RefCell<HashMap<ResolutionData, Result<Rc<PathBuf>>>>,
- loading_cache: RefCell<HashMap<PathBuf, Result<Rc<str>>>>,
+ loading_cache: RefCell<HashMap<PathBuf, Result<IStr>>>,
inner: Box<dyn ImportResolver>,
}
impl ImportResolver for CachingImportResolver {
@@ -101,7 +102,7 @@
.clone()
}
- fn load_file_contents(&self, resolved: &PathBuf) -> Result<Rc<str>> {
+ fn load_file_contents(&self, resolved: &PathBuf) -> Result<IStr> {
self.loading_cache
.borrow_mut()
.entry(resolved.clone())
crates/jrsonnet-evaluator/src/integrations/serde.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/integrations/serde.rs
+++ b/crates/jrsonnet-evaluator/src/integrations/serde.rs
@@ -22,11 +22,10 @@
} else {
Number::from_f64(*n).expect("to json number")
}),
- Val::Lazy(v) => (&v.evaluate()?).try_into()?,
Val::Arr(a) => {
let mut out = Vec::with_capacity(a.len());
for item in a.iter() {
- out.push(item.try_into()?);
+ out.push((&item?).try_into()?);
}
Self::Array(out)
}
@@ -55,9 +54,9 @@
Value::Array(a) => {
let mut out = Vec::with_capacity(a.len());
for v in a {
- out.push(v.into());
+ out.push(LazyVal::new_resolved(v.into()));
}
- Self::Arr(Rc::new(out))
+ Self::Arr(out.into())
}
Value::Object(o) => {
let mut entries = HashMap::with_capacity(o.len());
crates/jrsonnet-evaluator/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/lib.rs
+++ b/crates/jrsonnet-evaluator/src/lib.rs
@@ -14,6 +14,7 @@
pub mod native;
mod obj;
pub mod trace;
+pub mod typed;
mod val;
pub use ctx::*;
@@ -22,6 +23,7 @@
pub use evaluate::*;
pub use function::parse_function_call;
pub use import::*;
+use jrsonnet_interner::IStr;
use jrsonnet_parser::*;
use native::NativeCallback;
pub use obj::*;
@@ -62,13 +64,13 @@
/// Limits amount of stack trace items preserved
pub max_trace: usize,
/// Used for s`td.extVar`
- pub ext_vars: HashMap<Rc<str>, Val>,
+ pub ext_vars: HashMap<IStr, Val>,
/// Used for ext.native
- pub ext_natives: HashMap<Rc<str>, Rc<NativeCallback>>,
+ pub ext_natives: HashMap<IStr, Rc<NativeCallback>>,
/// TLA vars
- pub tla_vars: HashMap<Rc<str>, Val>,
+ pub tla_vars: HashMap<IStr, Val>,
/// Global variables are inserted in default context
- pub globals: HashMap<Rc<str>, Val>,
+ pub globals: HashMap<IStr, Val>,
/// Used to resolve file locations/contents
pub import_resolver: Box<dyn ImportResolver>,
/// Used in manifestification functions
@@ -101,11 +103,11 @@
stack_depth: usize,
/// Contains file source codes and evaluation results for imports and pretty-printed stacktraces
files: HashMap<Rc<PathBuf>, FileData>,
- str_files: HashMap<Rc<PathBuf>, Rc<str>>,
+ str_files: HashMap<Rc<PathBuf>, IStr>,
}
pub struct FileData {
- source_code: Rc<str>,
+ source_code: IStr,
parsed: LocExpr,
evaluated: Option<Val>,
}
@@ -126,24 +128,28 @@
EVAL_STATE.with(|s| f(s.borrow().as_ref().unwrap()))
}
pub(crate) fn push<T>(
- e: &Option<ExprLocation>,
+ e: Option<&ExprLocation>,
frame_desc: impl FnOnce() -> String,
f: impl FnOnce() -> Result<T>,
) -> Result<T> {
- if let Some(v) = e {
- with_state(|s| s.push(v, frame_desc, f))
- } else {
- f()
- }
+ with_state(|s| s.push(e, frame_desc, f))
}
+pub fn push_stack_frame<T>(
+ e: Option<&ExprLocation>,
+ frame_desc: impl FnOnce() -> String,
+ f: impl FnOnce() -> Result<T>,
+) -> Result<T> {
+ push(e, frame_desc, f)
+}
+
/// Maintains stack trace and import resolution
#[derive(Default, Clone)]
pub struct EvaluationState(Rc<EvaluationStateInternals>);
impl EvaluationState {
/// Parses and adds file as loaded
- pub fn add_file(&self, path: Rc<PathBuf>, source_code: Rc<str>) -> Result<()> {
+ pub fn add_file(&self, path: Rc<PathBuf>, source_code: IStr) -> Result<()> {
self.add_parsed_file(
path.clone(),
source_code.clone(),
@@ -168,7 +174,7 @@
pub fn add_parsed_file(
&self,
name: Rc<PathBuf>,
- source_code: Rc<str>,
+ source_code: IStr,
parsed: LocExpr,
) -> Result<()> {
self.data_mut().files.insert(
@@ -182,7 +188,7 @@
Ok(())
}
- pub fn get_source(&self, name: &PathBuf) -> Option<Rc<str>> {
+ pub fn get_source(&self, name: &PathBuf) -> Option<IStr> {
let ro_map = &self.data().files;
ro_map.get(name).map(|value| value.source_code.clone())
}
@@ -204,7 +210,7 @@
self.add_file(file_path.clone(), contents)?;
self.evaluate_loaded_file_raw(&file_path)
}
- pub(crate) fn import_file_str(&self, from: &PathBuf, path: &PathBuf) -> Result<Rc<str>> {
+ pub(crate) fn import_file_str(&self, from: &PathBuf, path: &PathBuf) -> Result<IStr> {
let path = self.resolve_file(from, path)?;
if !self.data().str_files.contains_key(&path) {
let file_str = self.load_file_contents(&path)?;
@@ -256,7 +262,7 @@
/// Creates context with all passed global variables
pub fn create_default_context(&self) -> Result<Context> {
let globals = &self.settings().globals;
- let mut new_bindings: HashMap<Rc<str>, LazyBinding> = HashMap::new();
+ let mut new_bindings: HashMap<IStr, LazyBinding> = HashMap::new();
for (name, value) in globals.iter() {
new_bindings.insert(
name.clone(),
@@ -269,7 +275,7 @@
/// Executes code creating a new stack frame
pub fn push<T>(
&self,
- e: &ExprLocation,
+ e: Option<&ExprLocation>,
frame_desc: impl FnOnce() -> String,
f: impl FnOnce() -> Result<T>,
) -> Result<T> {
@@ -288,7 +294,7 @@
self.data_mut().stack_depth -= 1;
if let Err(mut err) = result {
err.trace_mut().0.push(StackTraceElement {
- location: e.clone(),
+ location: e.cloned(),
desc: frame_desc(),
});
return Err(err);
@@ -320,13 +326,13 @@
out
}
- pub fn manifest(&self, val: Val) -> Result<Rc<str>> {
+ pub fn manifest(&self, val: Val) -> Result<IStr> {
self.run_in_state(|| val.manifest(&self.manifest_format()))
}
- pub fn manifest_multi(&self, val: Val) -> Result<Vec<(Rc<str>, Rc<str>)>> {
+ pub fn manifest_multi(&self, val: Val) -> Result<Vec<(IStr, IStr)>> {
self.run_in_state(|| val.manifest_multi(&self.manifest_format()))
}
- pub fn manifest_stream(&self, val: Val) -> Result<Vec<Rc<str>>> {
+ pub fn manifest_stream(&self, val: Val) -> Result<Vec<IStr>> {
self.run_in_state(|| val.manifest_stream(&self.manifest_format()))
}
@@ -334,10 +340,16 @@
pub fn with_tla(&self, val: Val) -> Result<Val> {
self.run_in_state(|| {
Ok(match val {
- Val::Func(func) => func.evaluate_map(
- self.create_default_context()?,
- &self.settings().tla_vars,
- true,
+ Val::Func(func) => push(
+ None,
+ || "during TLA call".to_owned(),
+ || {
+ Ok(func.evaluate_map(
+ self.create_default_context()?,
+ &self.settings().tla_vars,
+ true,
+ )?)
+ },
)?,
v => v,
})
@@ -370,7 +382,7 @@
self.run_in_state(|| self.import_file(&PathBuf::from("."), name))
}
/// Parses and evaluates the given snippet
- pub fn evaluate_snippet_raw(&self, source: Rc<PathBuf>, code: Rc<str>) -> Result<Val> {
+ pub fn evaluate_snippet_raw(&self, source: Rc<PathBuf>, code: IStr) -> Result<Val> {
let parsed = parse(
&code,
&ParserSettings {
@@ -390,26 +402,26 @@
/// Settings utilities
impl EvaluationState {
- pub fn add_ext_var(&self, name: Rc<str>, value: Val) {
+ pub fn add_ext_var(&self, name: IStr, value: Val) {
self.settings_mut().ext_vars.insert(name, value);
}
- pub fn add_ext_str(&self, name: Rc<str>, value: Rc<str>) {
+ pub fn add_ext_str(&self, name: IStr, value: IStr) {
self.add_ext_var(name, Val::Str(value));
}
- pub fn add_ext_code(&self, name: Rc<str>, code: Rc<str>) -> Result<()> {
+ pub fn add_ext_code(&self, name: IStr, code: IStr) -> Result<()> {
let value =
self.evaluate_snippet_raw(Rc::new(PathBuf::from(format!("ext_code {}", name))), code)?;
self.add_ext_var(name, value);
Ok(())
}
- pub fn add_tla(&self, name: Rc<str>, value: Val) {
+ pub fn add_tla(&self, name: IStr, value: Val) {
self.settings_mut().tla_vars.insert(name, value);
}
- pub fn add_tla_str(&self, name: Rc<str>, value: Rc<str>) {
+ pub fn add_tla_str(&self, name: IStr, value: IStr) {
self.add_tla(name, Val::Str(value));
}
- pub fn add_tla_code(&self, name: Rc<str>, code: Rc<str>) -> Result<()> {
+ pub fn add_tla_code(&self, name: IStr, code: IStr) -> Result<()> {
let value =
self.evaluate_snippet_raw(Rc::new(PathBuf::from(format!("tla_code {}", name))), code)?;
self.add_tla(name, value);
@@ -419,7 +431,7 @@
pub fn resolve_file(&self, from: &PathBuf, path: &PathBuf) -> Result<Rc<PathBuf>> {
Ok(self.settings().import_resolver.resolve_file(from, path)?)
}
- pub fn load_file_contents(&self, path: &PathBuf) -> Result<Rc<str>> {
+ pub fn load_file_contents(&self, path: &PathBuf) -> Result<IStr> {
Ok(self.settings().import_resolver.load_file_contents(path)?)
}
@@ -430,7 +442,7 @@
self.settings_mut().import_resolver = resolver;
}
- pub fn add_native(&self, name: Rc<str>, cb: Rc<NativeCallback>) {
+ pub fn add_native(&self, name: IStr, cb: Rc<NativeCallback>) {
self.settings_mut().ext_natives.insert(name, cb);
}
@@ -467,6 +479,7 @@
pub mod tests {
use super::Val;
use crate::{error::Error::*, primitive_equals, EvaluationState};
+ use jrsonnet_interner::IStr;
use jrsonnet_parser::*;
use std::{path::PathBuf, rc::Rc};
@@ -477,11 +490,19 @@
state.run_in_state(|| {
state
.push(
- &ExprLocation(Rc::new(PathBuf::from("test1.jsonnet")), 10, 20),
+ Some(&ExprLocation(
+ Rc::new(PathBuf::from("test1.jsonnet")),
+ 10,
+ 20,
+ )),
|| "outer".to_owned(),
|| {
state.push(
- &ExprLocation(Rc::new(PathBuf::from("test2.jsonnet")), 30, 40),
+ Some(&ExprLocation(
+ Rc::new(PathBuf::from("test2.jsonnet")),
+ 30,
+ 40,
+ )),
|| "inner".to_owned(),
|| Err(RuntimeError("".into()).into()),
)?;
@@ -883,14 +904,20 @@
Param("a".into(), None),
Param("b".into(), None),
])),
- |args| match (&args[0], &args[1]) {
- (Val::Num(a), Val::Num(b)) => Ok(Val::Num(a + b)),
- (_, _) => todo!(),
+ |caller, args| {
+ assert_eq!(
+ caller.unwrap(),
+ Rc::new(PathBuf::from("native_caller.jsonnet"))
+ );
+ match (&args[0], &args[1]) {
+ (Val::Num(a), Val::Num(b)) => Ok(Val::Num(a + b)),
+ (_, _) => unreachable!(),
+ }
},
)),
);
evaluator.evaluate_snippet_raw(
- Rc::new(PathBuf::from("test.jsonnet")),
+ Rc::new(PathBuf::from("native_caller.jsonnet")),
"std.assertEqual(std.native(\"native_add\")(1, 2), 3)".into(),
)?;
Ok(())
@@ -904,13 +931,13 @@
Ok(())
}
- struct TestImportResolver(Rc<str>);
+ struct TestImportResolver(IStr);
impl crate::import::ImportResolver for TestImportResolver {
fn resolve_file(&self, _: &PathBuf, _: &PathBuf) -> crate::error::Result<Rc<PathBuf>> {
Ok(Rc::new(PathBuf::from("/test")))
}
- fn load_file_contents(&self, _: &PathBuf) -> crate::error::Result<Rc<str>> {
+ fn load_file_contents(&self, _: &PathBuf) -> crate::error::Result<IStr> {
Ok(self.0.clone())
}
crates/jrsonnet-evaluator/src/map.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/map.rs
+++ b/crates/jrsonnet-evaluator/src/map.rs
@@ -1,17 +1,18 @@
+use jrsonnet_interner::IStr;
use rustc_hash::FxHashMap;
-use std::{borrow::Borrow, hash::Hash, rc::Rc};
+use std::rc::Rc;
#[derive(Default, Debug)]
-struct LayeredHashMapInternals<K: Hash, V> {
- parent: Option<LayeredHashMap<K, V>>,
- current: FxHashMap<K, V>,
+struct LayeredHashMapInternals<V> {
+ parent: Option<LayeredHashMap<V>>,
+ current: FxHashMap<IStr, V>,
}
#[derive(Debug)]
-pub struct LayeredHashMap<K: Hash, V>(Rc<LayeredHashMapInternals<K, V>>);
+pub struct LayeredHashMap<V>(Rc<LayeredHashMapInternals<V>>);
-impl<K: Hash + Eq, V> LayeredHashMap<K, V> {
- pub fn extend(self, new_layer: FxHashMap<K, V>) -> Self {
+impl<V> LayeredHashMap<V> {
+ pub fn extend(self, new_layer: FxHashMap<IStr, V>) -> Self {
match Rc::try_unwrap(self.0) {
Ok(mut map) => {
map.current.extend(new_layer);
@@ -24,11 +25,7 @@
}
}
- pub fn get<Q: ?Sized>(&self, key: &Q) -> Option<&V>
- where
- K: Borrow<Q>,
- Q: Hash + Eq,
- {
+ pub fn get(&self, key: &IStr) -> Option<&V> {
(self.0)
.current
.get(key)
@@ -36,13 +33,13 @@
}
}
-impl<K: Hash, V> Clone for LayeredHashMap<K, V> {
+impl<V> Clone for LayeredHashMap<V> {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
-impl<K: Hash + Eq, V> Default for LayeredHashMap<K, V> {
+impl<V> Default for LayeredHashMap<V> {
fn default() -> Self {
Self(Rc::new(LayeredHashMapInternals {
parent: None,
crates/jrsonnet-evaluator/src/native.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/native.rs
+++ b/crates/jrsonnet-evaluator/src/native.rs
@@ -1,20 +1,27 @@
+#![allow(clippy::type_complexity)]
+
use crate::{error::Result, Val};
use jrsonnet_parser::ParamsDesc;
use std::fmt::Debug;
+use std::path::PathBuf;
+use std::rc::Rc;
pub struct NativeCallback {
pub params: ParamsDesc,
- handler: Box<dyn Fn(&[Val]) -> Result<Val>>,
+ handler: Box<dyn Fn(Option<Rc<PathBuf>>, &[Val]) -> Result<Val>>,
}
impl NativeCallback {
- pub fn new(params: ParamsDesc, handler: impl Fn(&[Val]) -> Result<Val> + 'static) -> Self {
+ pub fn new(
+ params: ParamsDesc,
+ handler: impl Fn(Option<Rc<PathBuf>>, &[Val]) -> Result<Val> + 'static,
+ ) -> Self {
Self {
params,
handler: Box::new(handler),
}
}
- pub fn call(&self, args: &[Val]) -> Result<Val> {
- (self.handler)(args)
+ pub fn call(&self, caller: Option<Rc<PathBuf>>, args: &[Val]) -> Result<Val> {
+ (self.handler)(caller, args)
}
}
impl Debug for NativeCallback {
crates/jrsonnet-evaluator/src/obj.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/obj.rs
+++ b/crates/jrsonnet-evaluator/src/obj.rs
@@ -1,5 +1,6 @@
use crate::{evaluate_add_op, LazyBinding, Result, Val};
use indexmap::IndexMap;
+use jrsonnet_interner::IStr;
use jrsonnet_parser::{ExprLocation, Visibility};
use std::{cell::RefCell, collections::HashMap, fmt::Debug, rc::Rc};
@@ -12,11 +13,11 @@
}
// Field => This
-type CacheKey = (Rc<str>, usize);
+type CacheKey = (IStr, usize);
#[derive(Debug)]
pub struct ObjValueInternals {
super_obj: Option<ObjValue>,
- this_entries: Rc<HashMap<Rc<str>, ObjMember>>,
+ this_entries: Rc<HashMap<IStr, ObjMember>>,
value_cache: RefCell<HashMap<CacheKey, Option<Val>>>,
}
#[derive(Clone)]
@@ -47,7 +48,7 @@
}
impl ObjValue {
- pub fn new(super_obj: Option<Self>, this_entries: Rc<HashMap<Rc<str>, ObjMember>>) -> Self {
+ pub fn new(super_obj: Option<Self>, this_entries: Rc<HashMap<IStr, ObjMember>>) -> Self {
Self(Rc::new(ObjValueInternals {
super_obj,
this_entries,
@@ -63,7 +64,7 @@
Some(v) => Self::new(Some(v.with_super(super_obj)), self.0.this_entries.clone()),
}
}
- pub fn enum_fields(&self, handler: &impl Fn(&Rc<str>, &Visibility)) {
+ pub fn enum_fields(&self, handler: &impl Fn(&IStr, &Visibility)) {
if let Some(s) = &self.0.super_obj {
s.enum_fields(handler);
}
@@ -71,7 +72,7 @@
handler(name, &member.visibility);
}
}
- pub fn fields_visibility(&self) -> IndexMap<Rc<str>, bool> {
+ pub fn fields_visibility(&self) -> IndexMap<IStr, bool> {
let out = Rc::new(RefCell::new(IndexMap::new()));
self.enum_fields(&|name, visibility| {
let mut out = out.borrow_mut();
@@ -91,7 +92,7 @@
});
Rc::try_unwrap(out).unwrap().into_inner()
}
- pub fn visible_fields(&self) -> Vec<Rc<str>> {
+ pub fn visible_fields(&self) -> Vec<IStr> {
let mut visible_fields: Vec<_> = self
.fields_visibility()
.into_iter()
@@ -101,10 +102,11 @@
visible_fields.sort();
visible_fields
}
- pub fn get(&self, key: Rc<str>) -> Result<Option<Val>> {
- Ok(self.get_raw(key, self)?)
+ pub fn get(&self, key: IStr) -> Result<Option<Val>> {
+ Ok(self.get_raw(key, None)?)
}
- pub(crate) fn get_raw(&self, key: Rc<str>, real_this: &Self) -> Result<Option<Val>> {
+ pub(crate) fn get_raw(&self, key: IStr, real_this: Option<&Self>) -> Result<Option<Val>> {
+ let real_this = real_this.unwrap_or(self);
let cache_key = (key.clone(), Rc::as_ptr(&real_this.0) as usize);
if let Some(v) = self.0.value_cache.borrow().get(&cache_key) {
@@ -115,7 +117,7 @@
(Some(k), Some(s)) => {
let our = self.evaluate_this(k, real_this)?;
if k.add {
- s.get_raw(key, real_this)?
+ s.get_raw(key, Some(real_this))?
.map_or(Ok(Some(our.clone())), |v| {
Ok(Some(evaluate_add_op(&v, &our)?))
})
@@ -123,7 +125,7 @@
Ok(Some(our))
}
}
- (None, Some(s)) => s.get_raw(key, real_this),
+ (None, Some(s)) => s.get_raw(key, Some(real_this)),
(None, None) => Ok(None),
}?;
self.0
@@ -137,6 +139,10 @@
.evaluate(Some(real_this.clone()), self.0.super_obj.clone())?
.evaluate()?)
}
+
+ pub fn ptr_eq(a: &Self, b: &Self) -> bool {
+ Rc::ptr_eq(&a.0, &b.0)
+ }
}
impl PartialEq for ObjValue {
fn eq(&self, other: &Self) -> bool {
crates/jrsonnet-evaluator/src/trace/location.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/trace/location.rs
+++ b/crates/jrsonnet-evaluator/src/trace/location.rs
@@ -37,7 +37,11 @@
];
let mut with_no_known_line_ending = vec![];
let mut this_line_offset = 0;
- for (pos, ch) in file.chars().enumerate() {
+ for (pos, ch) in file
+ .chars()
+ .enumerate()
+ .chain(std::iter::once((file.len(), ' ')))
+ {
column += 1;
match offset_map.last() {
Some(x) if x.0 == pos => {
crates/jrsonnet-evaluator/src/trace/mod.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/trace/mod.rs
+++ b/crates/jrsonnet-evaluator/src/trace/mod.rs
@@ -65,7 +65,7 @@
out,
"{}:{}-{}:{}",
start.line,
- end.column - 1,
+ end.column.saturating_sub(1),
start.line,
end.column
)?;
@@ -119,20 +119,23 @@
.0
.iter()
.map(|el| {
- let resolved_path = self.resolver.resolve(&el.location.0);
- // TODO: Process all trace elements first
- let location = evaluation_state
- .map_source_locations(&el.location.0, &[el.location.1, el.location.2]);
- (resolved_path, location)
- })
- .map(|(mut n, location)| {
- use std::fmt::Write;
- write!(n, ":").unwrap();
- print_code_location(&mut n, &location[0], &location[1]).unwrap();
- n
+ el.location.as_ref().map(|l| {
+ use std::fmt::Write;
+ let mut resolved_path = self.resolver.resolve(&l.0);
+ // TODO: Process all trace elements first
+ let location = evaluation_state.map_source_locations(&l.0, &[l.1, l.2]);
+ write!(resolved_path, ":").unwrap();
+ print_code_location(&mut resolved_path, &location[0], &location[1]).unwrap();
+ resolved_path
+ })
})
.collect::<Vec<_>>();
- let align = file_names.iter().map(|e| e.len()).max().unwrap_or(0);
+ let align = file_names
+ .iter()
+ .flatten()
+ .map(|e| e.len())
+ .max()
+ .unwrap_or(0);
for (i, (el, file)) in error.trace().0.iter().zip(file_names).enumerate() {
if i != 0 {
writeln!(out)?;
@@ -141,7 +144,7 @@
out,
"{:<p$}{:<w$}: {}",
"",
- file,
+ file.unwrap_or_else(|| "".to_owned()),
el.desc,
p = self.padding,
w = align
@@ -165,17 +168,21 @@
writeln!(out)?;
}
let desc = &item.desc;
- let source = item.location.clone();
- let start_end = evaluation_state.map_source_locations(&source.0, &[source.1, source.2]);
+ if let Some(source) = &item.location {
+ let start_end =
+ evaluation_state.map_source_locations(&source.0, &[source.1, source.2]);
- write!(
- out,
- " at {} ({}:{}:{})",
- desc,
- source.0.to_str().unwrap(),
- start_end[0].line,
- start_end[0].column,
- )?;
+ write!(
+ out,
+ " at {} ({}:{}:{})",
+ desc,
+ source.0.to_str().unwrap(),
+ start_end[0].line,
+ start_end[0].column,
+ )?;
+ } else {
+ write!(out, " at {}", desc,)?;
+ }
}
Ok(())
}
@@ -225,17 +232,20 @@
let trace = &error.trace();
for item in trace.0.iter() {
let desc = &item.desc;
- let source = item.location.clone();
- let start_end = evaluation_state.map_source_locations(&source.0, &[source.1, source.2]);
-
- self.print_snippet(
- out,
- &evaluation_state.get_source(&source.0).unwrap(),
- &source.0,
- &start_end[0],
- &start_end[1],
- desc,
- )?;
+ if let Some(source) = &item.location {
+ let start_end =
+ evaluation_state.map_source_locations(&source.0, &[source.1, source.2]);
+ self.print_snippet(
+ out,
+ &evaluation_state.get_source(&source.0).unwrap(),
+ &source.0,
+ &start_end[0],
+ &start_end[1],
+ desc,
+ )?;
+ } else {
+ write!(out, "{}", desc)?;
+ }
}
Ok(())
}
crates/jrsonnet-evaluator/src/typed.rsdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-evaluator/src/typed.rs
@@ -0,0 +1,265 @@
+use std::{fmt::Display, rc::Rc};
+
+use crate::{
+ error::{Error, LocError, Result},
+ push, Val,
+};
+use jrsonnet_parser::ExprLocation;
+use jrsonnet_types::{ComplexValType, ValType};
+use thiserror::Error;
+
+#[macro_export]
+macro_rules! unwrap_type {
+ ($desc: expr, $value: expr, $typ: expr => $match: path) => {{
+ use $crate::{push_stack_frame, typed::CheckType};
+ push_stack_frame(None, $desc, || Ok($typ.check(&$value)?))?;
+ match $value {
+ $match(v) => v,
+ _ => unreachable!(),
+ }
+ }};
+}
+
+#[derive(Debug, Error, Clone)]
+pub enum TypeError {
+ #[error("expected {0}, got {1}")]
+ ExpectedGot(ComplexValType, ValType),
+ #[error("missing property {0} from {1:?}")]
+ MissingProperty(Rc<str>, ComplexValType),
+ #[error("every failed from {0}:\n{1}")]
+ UnionFailed(ComplexValType, TypeLocErrorList),
+ #[error("number out of bounds: {0} not in {1:?}..{2:?}")]
+ BoundsFailed(f64, Option<f64>, Option<f64>),
+}
+impl From<TypeError> for LocError {
+ fn from(e: TypeError) -> Self {
+ Error::TypeError(e.into()).into()
+ }
+}
+
+#[derive(Debug, Clone)]
+pub struct TypeLocError(Box<TypeError>, ValuePathStack);
+impl From<TypeError> for TypeLocError {
+ fn from(e: TypeError) -> Self {
+ Self(Box::new(e), ValuePathStack(Vec::new()))
+ }
+}
+impl From<TypeLocError> for LocError {
+ fn from(e: TypeLocError) -> Self {
+ Error::TypeError(e).into()
+ }
+}
+impl Display for TypeLocError {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "{}", self.0)?;
+ if !(self.1).0.is_empty() {
+ write!(f, "at {}", self.1)?;
+ }
+ Ok(())
+ }
+}
+
+#[derive(Debug, Clone)]
+pub struct TypeLocErrorList(Vec<TypeLocError>);
+impl Display for TypeLocErrorList {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ use std::fmt::Write;
+ let mut out = String::new();
+ for (i, err) in self.0.iter().enumerate() {
+ if i != 0 {
+ writeln!(f)?;
+ }
+ out.clear();
+ write!(out, "{}", err)?;
+
+ for (i, line) in out.lines().enumerate() {
+ if line.trim().is_empty() {
+ continue;
+ }
+ if i != 0 {
+ writeln!(f)?;
+ write!(f, " ")?;
+ } else {
+ write!(f, " - ")?;
+ }
+ write!(f, "{}", line)?;
+ }
+ }
+ Ok(())
+ }
+}
+
+fn push_type(
+ location: Option<&ExprLocation>,
+ error_reason: impl Fn() -> String,
+ path: impl Fn() -> ValuePathItem,
+ item: impl Fn() -> Result<()>,
+) -> Result<()> {
+ push(location, error_reason, || match item() {
+ Ok(_) => Ok(()),
+ Err(mut e) => {
+ if let Error::TypeError(e) = &mut e.error_mut() {
+ (e.1).0.push(path())
+ }
+ Err(e)
+ }
+ })
+}
+
+// TODO: check_fast for fast path of union type checking
+pub trait CheckType {
+ fn check(&self, value: &Val) -> Result<()>;
+}
+
+impl CheckType for ValType {
+ fn check(&self, value: &Val) -> Result<()> {
+ let got = value.value_type();
+ if got != *self {
+ let loc_error: TypeLocError = TypeError::ExpectedGot((*self).into(), got).into();
+ return Err(loc_error.into());
+ }
+ Ok(())
+ }
+}
+
+#[derive(Clone, Debug)]
+enum ValuePathItem {
+ Field(Rc<str>),
+ Index(u64),
+}
+impl Display for ValuePathItem {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ match self {
+ Self::Field(name) => write!(f, ".{}", name)?,
+ Self::Index(idx) => write!(f, "[{}]", idx)?,
+ }
+ Ok(())
+ }
+}
+
+#[derive(Clone, Debug)]
+struct ValuePathStack(Vec<ValuePathItem>);
+impl Display for ValuePathStack {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "self")?;
+ for elem in self.0.iter().rev() {
+ write!(f, "{}", elem)?;
+ }
+ Ok(())
+ }
+}
+
+impl CheckType for ComplexValType {
+ fn check(&self, value: &Val) -> Result<()> {
+ match self {
+ Self::Any => Ok(()),
+ Self::Simple(s) => s.check(value),
+ Self::Char => match value {
+ Val::Str(s) if s.len() == 1 || s.chars().count() == 1 => Ok(()),
+ v => Err(TypeError::ExpectedGot(self.clone(), v.value_type()).into()),
+ },
+ Self::BoundedNumber(from, to) => {
+ if let Val::Num(n) = value {
+ if from.map(|from| from > *n).unwrap_or(false)
+ || to.map(|to| to <= *n).unwrap_or(false)
+ {
+ return Err(TypeError::BoundsFailed(*n, *from, *to).into());
+ }
+ Ok(())
+ } else {
+ Err(TypeError::ExpectedGot(self.clone(), value.value_type()).into())
+ }
+ }
+ Self::Array(elem_type) => match value {
+ Val::Arr(a) => {
+ for (i, item) in a.iter().enumerate() {
+ push_type(
+ None,
+ || format!("array index {}", i),
+ || ValuePathItem::Index(i as u64),
+ || Ok(elem_type.check(&item.clone()?)?),
+ )?;
+ }
+ Ok(())
+ }
+ v => Err(TypeError::ExpectedGot(self.clone(), v.value_type()).into()),
+ },
+ Self::ArrayRef(elem_type) => match value {
+ Val::Arr(a) => {
+ for (i, item) in a.iter().enumerate() {
+ push_type(
+ None,
+ || format!("array index {}", i),
+ || ValuePathItem::Index(i as u64),
+ || Ok(elem_type.check(&item.clone()?)?),
+ )?;
+ }
+ Ok(())
+ }
+ v => Err(TypeError::ExpectedGot(self.clone(), v.value_type()).into()),
+ },
+ Self::ObjectRef(elems) => match value {
+ Val::Obj(obj) => {
+ for (k, v) in elems.iter() {
+ if let Some(got_v) = obj.get((*k).into())? {
+ push_type(
+ None,
+ || format!("property {}", k),
+ || ValuePathItem::Field((*k).into()),
+ || v.check(&got_v),
+ )?
+ } else {
+ return Err(
+ TypeError::MissingProperty((*k).into(), self.clone()).into()
+ );
+ }
+ }
+ Ok(())
+ }
+ v => Err(TypeError::ExpectedGot(self.clone(), v.value_type()).into()),
+ },
+ Self::Union(types) => {
+ let mut errors = Vec::new();
+ for ty in types.iter() {
+ match ty.check(value) {
+ Ok(()) => {
+ return Ok(());
+ }
+ Err(e) => match e.error() {
+ Error::TypeError(e) => errors.push(e.clone()),
+ _ => return Err(e),
+ },
+ }
+ }
+ Err(TypeError::UnionFailed(self.clone(), TypeLocErrorList(errors)).into())
+ }
+ Self::UnionRef(types) => {
+ let mut errors = Vec::new();
+ for ty in types.iter() {
+ match ty.check(value) {
+ Ok(()) => {
+ return Ok(());
+ }
+ Err(e) => match e.error() {
+ Error::TypeError(e) => errors.push(e.clone()),
+ _ => return Err(e),
+ },
+ }
+ }
+ Err(TypeError::UnionFailed(self.clone(), TypeLocErrorList(errors)).into())
+ }
+ Self::Sum(types) => {
+ for ty in types.iter() {
+ ty.check(value)?
+ }
+ Ok(())
+ }
+ Self::SumRef(types) => {
+ for ty in types.iter() {
+ ty.check(value)?
+ }
+ Ok(())
+ }
+ }
+ }
+}
crates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/val.rs
+++ b/crates/jrsonnet-evaluator/src/val.rs
@@ -9,13 +9,10 @@
native::NativeCallback,
throw, with_state, Context, ObjValue, Result,
};
+use jrsonnet_interner::IStr;
use jrsonnet_parser::{el, Arg, ArgsDesc, Expr, ExprLocation, LiteralType, LocExpr, ParamsDesc};
-use std::{
- cell::RefCell,
- collections::HashMap,
- fmt::{Debug, Display},
- rc::Rc,
-};
+use jrsonnet_types::ValType;
+use std::{cell::RefCell, collections::HashMap, fmt::Debug, rc::Rc};
enum LazyValInternals {
Computed(Val),
@@ -65,7 +62,7 @@
#[derive(Debug, PartialEq)]
pub struct FuncDesc {
- pub name: Rc<str>,
+ pub name: IStr,
pub ctx: Context,
pub params: ParamsDesc,
pub body: LocExpr,
@@ -76,9 +73,9 @@
/// Plain function implemented in jsonnet
Normal(FuncDesc),
/// Standard library function
- Intrinsic(Rc<str>),
+ Intrinsic(IStr),
/// Library functions implemented in native
- NativeExt(Rc<str>, Rc<NativeCallback>),
+ NativeExt(IStr, Rc<NativeCallback>),
}
impl PartialEq for FuncVal {
@@ -95,7 +92,7 @@
pub fn is_ident(&self) -> bool {
matches!(&self, Self::Intrinsic(n) if n as &str == "id")
}
- pub fn name(&self) -> Rc<str> {
+ pub fn name(&self) -> IStr {
match self {
Self::Normal(normal) => normal.name.clone(),
Self::Intrinsic(name) => format!("std.{}", name).into(),
@@ -105,7 +102,7 @@
pub fn evaluate(
&self,
call_ctx: Context,
- loc: &Option<ExprLocation>,
+ loc: Option<&ExprLocation>,
args: &ArgsDesc,
tailstrict: bool,
) -> Result<Val> {
@@ -127,7 +124,7 @@
for p in handler.params.0.iter() {
out_args.push(args.binding(p.0.clone())?.evaluate()?);
}
- Ok(handler.call(&out_args)?)
+ Ok(handler.call(loc.clone().map(|l| l.0.clone()), &out_args)?)
}
}
}
@@ -135,7 +132,7 @@
pub fn evaluate_map(
&self,
call_ctx: Context,
- args: &HashMap<Rc<str>, Val>,
+ args: &HashMap<IStr, Val>,
tailstrict: bool,
) -> Result<Val> {
match self {
@@ -166,53 +163,154 @@
}
}
-#[derive(Debug, Clone, Copy, PartialEq)]
-pub enum ValType {
- Bool,
- Null,
- Str,
- Num,
- Arr,
- Obj,
- Func,
+#[derive(Clone)]
+pub enum ManifestFormat {
+ YamlStream(Box<ManifestFormat>),
+ Yaml(usize),
+ Json(usize),
+ ToString,
+ String,
}
-impl ValType {
- pub const fn name(&self) -> &'static str {
- use ValType::*;
+
+#[derive(Debug, Clone)]
+pub enum ArrValue {
+ Lazy(Rc<Vec<LazyVal>>),
+ Eager(Rc<Vec<Val>>),
+ Extended(Box<(Self, Self)>),
+}
+impl ArrValue {
+ pub fn new_eager() -> Self {
+ Self::Eager(Rc::new(Vec::new()))
+ }
+
+ pub fn len(&self) -> usize {
+ match self {
+ Self::Lazy(l) => l.len(),
+ Self::Eager(e) => e.len(),
+ Self::Extended(v) => v.0.len() + v.1.len(),
+ }
+ }
+
+ pub fn is_empty(&self) -> bool {
+ self.len() == 0
+ }
+
+ pub fn get(&self, index: usize) -> Result<Option<Val>> {
+ match self {
+ Self::Lazy(vec) => {
+ if let Some(v) = vec.get(index) {
+ Ok(Some(v.evaluate()?))
+ } else {
+ Ok(None)
+ }
+ }
+ Self::Eager(vec) => Ok(vec.get(index).cloned()),
+ Self::Extended(v) => {
+ let a_len = v.0.len();
+ if a_len > index {
+ v.0.get(index)
+ } else {
+ v.1.get(index - a_len)
+ }
+ }
+ }
+ }
+
+ pub fn get_lazy(&self, index: usize) -> Option<LazyVal> {
+ match self {
+ Self::Lazy(vec) => vec.get(index).cloned(),
+ Self::Eager(vec) => vec.get(index).cloned().map(LazyVal::new_resolved),
+ Self::Extended(v) => {
+ let a_len = v.0.len();
+ if a_len > index {
+ v.0.get_lazy(index)
+ } else {
+ v.1.get_lazy(index - a_len)
+ }
+ }
+ }
+ }
+
+ pub fn evaluated(&self) -> Result<Rc<Vec<Val>>> {
+ Ok(match self {
+ Self::Lazy(vec) => {
+ let mut out = Vec::with_capacity(vec.len());
+ for item in vec.iter() {
+ out.push(item.evaluate()?);
+ }
+ Rc::new(out)
+ }
+ Self::Eager(vec) => vec.clone(),
+ Self::Extended(_v) => {
+ let mut out = Vec::with_capacity(self.len());
+ for item in self.iter() {
+ out.push(item?);
+ }
+ Rc::new(out)
+ }
+ })
+ }
+
+ pub fn iter(&self) -> impl DoubleEndedIterator<Item = Result<Val>> + '_ {
+ (0..self.len()).map(move |idx| match self {
+ Self::Lazy(l) => l[idx].evaluate(),
+ Self::Eager(e) => Ok(e[idx].clone()),
+ Self::Extended(_) => self.get(idx).map(|e| e.unwrap()),
+ })
+ }
+
+ pub fn iter_lazy(&self) -> impl DoubleEndedIterator<Item = LazyVal> + '_ {
+ (0..self.len()).map(move |idx| match self {
+ Self::Lazy(l) => l[idx].clone(),
+ Self::Eager(e) => LazyVal::new_resolved(e[idx].clone()),
+ Self::Extended(_) => self.get_lazy(idx).unwrap(),
+ })
+ }
+
+ pub fn reversed(self) -> Self {
match self {
- Bool => "boolean",
- Null => "null",
- Str => "string",
- Num => "number",
- Arr => "array",
- Obj => "object",
- Func => "function",
+ Self::Lazy(vec) => {
+ let mut out = (&vec as &Vec<_>).clone();
+ out.reverse();
+ Self::Lazy(Rc::new(out))
+ }
+ Self::Eager(vec) => {
+ let mut out = (&vec as &Vec<_>).clone();
+ out.reverse();
+ Self::Eager(Rc::new(out))
+ }
+ Self::Extended(b) => Self::Extended(Box::new((b.1.reversed(), b.0.reversed()))),
}
}
+
+ pub fn ptr_eq(a: &Self, b: &Self) -> bool {
+ match (a, b) {
+ (Self::Lazy(a), Self::Lazy(b)) => Rc::ptr_eq(a, b),
+ (Self::Eager(a), Self::Eager(b)) => Rc::ptr_eq(a, b),
+ _ => false,
+ }
+ }
}
-impl Display for ValType {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- write!(f, "{}", self.name())
+
+impl From<Vec<LazyVal>> for ArrValue {
+ fn from(v: Vec<LazyVal>) -> Self {
+ Self::Lazy(Rc::new(v))
}
}
-#[derive(Clone)]
-pub enum ManifestFormat {
- YamlStream(Box<ManifestFormat>),
- Yaml(usize),
- Json(usize),
- ToString,
- String,
+impl From<Vec<Val>> for ArrValue {
+ fn from(v: Vec<Val>) -> Self {
+ Self::Eager(Rc::new(v))
+ }
}
#[derive(Debug, Clone)]
pub enum Val {
Bool(bool),
Null,
- Str(Rc<str>),
+ Str(IStr),
Num(f64),
- Lazy(LazyVal),
- Arr(Rc<Vec<Val>>),
+ Arr(ArrValue),
Obj(ObjValue),
Func(Rc<FuncVal>),
}
@@ -237,40 +335,33 @@
}
pub fn assert_type(&self, context: &'static str, val_type: ValType) -> Result<()> {
- let this_type = self.value_type()?;
+ let this_type = self.value_type();
if this_type != val_type {
throw!(TypeMismatch(context, vec![val_type], this_type))
} else {
Ok(())
}
}
+ pub fn unwrap_num(self) -> Result<f64> {
+ Ok(matches_unwrap!(self, Self::Num(v), v))
+ }
+ pub fn unwrap_func(self) -> Result<Rc<FuncVal>> {
+ Ok(matches_unwrap!(self, Self::Func(v), v))
+ }
pub fn try_cast_bool(self, context: &'static str) -> Result<bool> {
self.assert_type(context, ValType::Bool)?;
- Ok(matches_unwrap!(self.unwrap_if_lazy()?, Self::Bool(v), v))
+ Ok(matches_unwrap!(self, Self::Bool(v), v))
}
- pub fn try_cast_str(self, context: &'static str) -> Result<Rc<str>> {
+ pub fn try_cast_str(self, context: &'static str) -> Result<IStr> {
self.assert_type(context, ValType::Str)?;
- Ok(matches_unwrap!(self.unwrap_if_lazy()?, Self::Str(v), v))
+ Ok(matches_unwrap!(self, Self::Str(v), v))
}
pub fn try_cast_num(self, context: &'static str) -> Result<f64> {
self.assert_type(context, ValType::Num)?;
- Ok(matches_unwrap!(self.unwrap_if_lazy()?, Self::Num(v), v))
- }
- pub fn inplace_unwrap(&mut self) -> Result<()> {
- while let Self::Lazy(lazy) = self {
- *self = lazy.evaluate()?;
- }
- Ok(())
- }
- pub fn unwrap_if_lazy(&self) -> Result<Self> {
- Ok(if let Self::Lazy(v) = self {
- v.evaluate()?.unwrap_if_lazy()?
- } else {
- self.clone()
- })
+ self.unwrap_num()
}
- pub fn value_type(&self) -> Result<ValType> {
- Ok(match self {
+ pub const fn value_type(&self) -> ValType {
+ match self {
Self::Str(..) => ValType::Str,
Self::Num(..) => ValType::Num,
Self::Arr(..) => ValType::Arr,
@@ -278,18 +369,17 @@
Self::Bool(_) => ValType::Bool,
Self::Null => ValType::Null,
Self::Func(..) => ValType::Func,
- Self::Lazy(_) => self.clone().unwrap_if_lazy()?.value_type()?,
- })
+ }
}
- pub fn to_string(&self) -> Result<Rc<str>> {
- Ok(match self.unwrap_if_lazy()? {
+ pub fn to_string(&self) -> Result<IStr> {
+ Ok(match self {
Self::Bool(true) => "true".into(),
Self::Bool(false) => "false".into(),
Self::Null => "null".into(),
- Self::Str(s) => s,
+ Self::Str(s) => s.clone(),
v => manifest_json_ex(
- &v,
+ v,
&ManifestJsonOptions {
padding: "",
mtype: ManifestType::ToString,
@@ -300,7 +390,7 @@
}
/// Expects value to be object, outputs (key, manifested value) pairs
- pub fn manifest_multi(&self, ty: &ManifestFormat) -> Result<Vec<(Rc<str>, Rc<str>)>> {
+ pub fn manifest_multi(&self, ty: &ManifestFormat) -> Result<Vec<(IStr, IStr)>> {
let obj = match self {
Self::Obj(obj) => obj,
_ => throw!(MultiManifestOutputIsNotAObject),
@@ -318,19 +408,19 @@
}
/// Expects value to be array, outputs manifested values
- pub fn manifest_stream(&self, ty: &ManifestFormat) -> Result<Vec<Rc<str>>> {
+ pub fn manifest_stream(&self, ty: &ManifestFormat) -> Result<Vec<IStr>> {
let arr = match self {
Self::Arr(a) => a,
_ => throw!(StreamManifestOutputIsNotAArray),
};
let mut out = Vec::with_capacity(arr.len());
for i in arr.iter() {
- out.push(i.manifest(ty)?);
+ out.push(i?.manifest(ty)?);
}
Ok(out)
}
- pub fn manifest(&self, ty: &ManifestFormat) -> Result<Rc<str>> {
+ pub fn manifest(&self, ty: &ManifestFormat) -> Result<IStr> {
Ok(match ty {
ManifestFormat::YamlStream(format) => {
let arr = match self {
@@ -348,7 +438,7 @@
if !arr.is_empty() {
for v in arr.iter() {
out.push_str("---\n");
- out.push_str(&v.manifest(format)?);
+ out.push_str(&v?.manifest(format)?);
out.push('\n');
}
out.push_str("...");
@@ -367,7 +457,7 @@
}
/// For manifestification
- pub fn to_json(&self, padding: usize) -> Result<Rc<str>> {
+ pub fn to_json(&self, padding: usize) -> Result<IStr> {
manifest_json_ex(
self,
&ManifestJsonOptions {
@@ -419,7 +509,7 @@
.try_cast_str("to json")?)
})
}
- pub fn to_yaml(&self, padding: usize) -> Result<Rc<str>> {
+ pub fn to_yaml(&self, padding: usize) -> Result<IStr> {
with_state(|s| {
let ctx = s
.create_default_context()?
@@ -456,7 +546,7 @@
/// Native implementation of `std.primitiveEquals`
pub fn primitive_equals(val_a: &Val, val_b: &Val) -> Result<bool> {
- Ok(match (val_a.unwrap_if_lazy()?, val_b.unwrap_if_lazy()?) {
+ Ok(match (val_a, val_b) {
(Val::Bool(a), Val::Bool(b)) => a == b,
(Val::Null, Val::Null) => true,
(Val::Str(a), Val::Str(b)) => a == b,
@@ -467,7 +557,7 @@
(Val::Obj(_), Val::Obj(_)) => throw!(RuntimeError(
"primitiveEquals operates on primitive types, got object".into(),
)),
- (a, b) if is_function_like(&a) && is_function_like(&b) => {
+ (a, b) if is_function_like(a) && is_function_like(b) => {
throw!(RuntimeError("cannot test equality of functions".into()))
}
(_, _) => false,
@@ -476,26 +566,28 @@
/// Native implementation of `std.equals`
pub fn equals(val_a: &Val, val_b: &Val) -> Result<bool> {
- let val_a = val_a.unwrap_if_lazy()?;
- let val_b = val_b.unwrap_if_lazy()?;
-
- if val_a.value_type()? != val_b.value_type()? {
+ if val_a.value_type() != val_b.value_type() {
return Ok(false);
}
match (val_a, val_b) {
- // Cant test for ptr equality, because all fields needs to be evaluated
(Val::Arr(a), Val::Arr(b)) => {
+ if ArrValue::ptr_eq(a, b) {
+ return Ok(true);
+ }
if a.len() != b.len() {
return Ok(false);
}
for (a, b) in a.iter().zip(b.iter()) {
- if !equals(&a.unwrap_if_lazy()?, &b.unwrap_if_lazy()?)? {
+ if !equals(&a?, &b?)? {
return Ok(false);
}
}
Ok(true)
}
(Val::Obj(a), Val::Obj(b)) => {
+ if ObjValue::ptr_eq(a, b) {
+ return Ok(true);
+ }
let fields = a.visible_fields();
if fields != b.visible_fields() {
return Ok(false);
@@ -507,6 +599,6 @@
}
Ok(true)
}
- (a, b) => Ok(primitive_equals(&a, &b)?),
+ (a, b) => Ok(primitive_equals(a, b)?),
}
}
crates/jrsonnet-interner/.gitignorediffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-interner/.gitignore
@@ -0,0 +1,2 @@
+/target
+Cargo.lock
crates/jrsonnet-interner/Cargo.tomldiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-interner/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "jrsonnet-interner"
+version = "0.3.3"
+authors = ["Yaroslav Bolyukin <iam@lach.pw>"]
+edition = "2018"
+
+[dependencies]
+serde = { version = "1.0" }
+rustc-hash = "1.1.0"
crates/jrsonnet-interner/src/lib.rsdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-interner/src/lib.rs
@@ -0,0 +1,104 @@
+use rustc_hash::FxHashMap;
+use serde::{Deserialize, Serialize};
+use std::{
+ cell::RefCell,
+ fmt::{self, Display},
+ hash::{BuildHasherDefault, Hash, Hasher},
+ ops::Deref,
+ rc::Rc,
+};
+
+#[derive(Clone, PartialOrd, Ord, Eq)]
+pub struct IStr(Rc<str>);
+
+impl Deref for IStr {
+ type Target = str;
+
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+
+impl PartialEq for IStr {
+ fn eq(&self, other: &Self) -> bool {
+ // It is ok, since all IStr should be inlined into same pool
+ Rc::ptr_eq(&self.0, &other.0)
+ }
+}
+
+impl PartialEq<str> for IStr {
+ fn eq(&self, other: &str) -> bool {
+ &self.0 as &str == other
+ }
+}
+
+impl Hash for IStr {
+ fn hash<H: Hasher>(&self, state: &mut H) {
+ state.write_usize(Rc::as_ptr(&self.0) as *const () as usize)
+ }
+}
+
+impl Drop for IStr {
+ fn drop(&mut self) {
+ // First reference - current object, second - POOL
+ if Rc::strong_count(&self.0) <= 2 {
+ STR_POOL.with(|pool| pool.borrow_mut().remove(&self.0));
+ }
+ }
+}
+
+impl fmt::Debug for IStr {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "{:?}", &self.0)
+ }
+}
+
+impl Display for IStr {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.write_str(&self.0)
+ }
+}
+
+thread_local! {
+ static STR_POOL: RefCell<FxHashMap<Rc<str>, ()>> = RefCell::new(FxHashMap::with_capacity_and_hasher(200, BuildHasherDefault::default()));
+}
+
+impl From<&str> for IStr {
+ fn from(str: &str) -> Self {
+ IStr(STR_POOL.with(|pool| {
+ let mut pool = pool.borrow_mut();
+ if let Some((k, _)) = pool.get_key_value(str) {
+ k.clone()
+ } else {
+ let rc: Rc<str> = str.into();
+ pool.insert(rc.clone(), ());
+ rc
+ }
+ }))
+ }
+}
+
+impl From<String> for IStr {
+ fn from(str: String) -> Self {
+ (&str as &str).into()
+ }
+}
+
+impl Serialize for IStr {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: serde::Serializer,
+ {
+ (&self.0 as &str).serialize(serializer)
+ }
+}
+
+impl<'de> Deserialize<'de> for IStr {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: serde::Deserializer<'de>,
+ {
+ let s = <&str>::deserialize(deserializer)?;
+ Ok(s.into())
+ }
+}
crates/jrsonnet-parser/Cargo.tomldiffbeforeafterboth--- a/crates/jrsonnet-parser/Cargo.toml
+++ b/crates/jrsonnet-parser/Cargo.toml
@@ -10,16 +10,14 @@
default = []
serialize = ["serde"]
deserialize = ["serde"]
-# Adds ability to dump AST as source code for easy embedding
-dump = ["structdump", "structdump-derive"]
[dependencies]
+jrsonnet-interner = { path = "../jrsonnet-interner" }
+
peg = "0.6.3"
unescape = "0.1.0"
serde = { version = "1.0", features = ["derive", "rc"], optional = true }
-structdump = { version = "0.1.2", optional = true }
-structdump-derive = { version = "0.1.2", optional = true }
[dev-dependencies]
jrsonnet-stdlib = { path = "../jrsonnet-stdlib", version = "0.3.3" }
crates/jrsonnet-parser/src/expr.rsdiffbeforeafterboth--- a/crates/jrsonnet-parser/src/expr.rs
+++ b/crates/jrsonnet-parser/src/expr.rs
@@ -1,3 +1,4 @@
+use jrsonnet_interner::IStr;
#[cfg(feature = "deserialize")]
use serde::Deserialize;
#[cfg(feature = "serialize")]
@@ -8,21 +9,17 @@
path::PathBuf,
rc::Rc,
};
-#[cfg(feature = "dump")]
-use structdump_derive::Codegen;
-#[cfg_attr(feature = "dump", derive(Codegen))]
#[cfg_attr(feature = "serialize", derive(Serialize))]
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
#[derive(Debug, PartialEq)]
pub enum FieldName {
/// {fixed: 2}
- Fixed(Rc<str>),
+ Fixed(IStr),
/// {["dyn"+"amic"]: 3}
Dyn(LocExpr),
}
-#[cfg_attr(feature = "dump", derive(Codegen))]
#[cfg_attr(feature = "serialize", derive(Serialize))]
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
#[derive(Debug, Clone, Copy, PartialEq)]
@@ -35,13 +32,11 @@
Unhide,
}
-#[cfg_attr(feature = "dump", derive(Codegen))]
#[cfg_attr(feature = "serialize", derive(Serialize))]
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
#[derive(Debug, PartialEq)]
pub struct AssertStmt(pub LocExpr, pub Option<LocExpr>);
-#[cfg_attr(feature = "dump", derive(Codegen))]
#[cfg_attr(feature = "serialize", derive(Serialize))]
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
#[derive(Debug, PartialEq)]
@@ -53,7 +48,6 @@
pub value: LocExpr,
}
-#[cfg_attr(feature = "dump", derive(Codegen))]
#[cfg_attr(feature = "serialize", derive(Serialize))]
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
#[derive(Debug, PartialEq)]
@@ -63,7 +57,6 @@
AssertStmt(AssertStmt),
}
-#[cfg_attr(feature = "dump", derive(Codegen))]
#[cfg_attr(feature = "serialize", derive(Serialize))]
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
#[derive(Debug, Clone, Copy, PartialEq)]
@@ -89,7 +82,6 @@
}
}
-#[cfg_attr(feature = "dump", derive(Codegen))]
#[cfg_attr(feature = "serialize", derive(Serialize))]
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
#[derive(Debug, Clone, Copy, PartialEq)]
@@ -97,6 +89,7 @@
Mul,
Div,
+ /// Implemented as intrinsic, put here for completeness
Mod,
Add,
@@ -146,14 +139,12 @@
}
/// name, default value
-#[cfg_attr(feature = "dump", derive(Codegen))]
#[cfg_attr(feature = "serialize", derive(Serialize))]
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
#[derive(Debug, PartialEq)]
-pub struct Param(pub Rc<str>, pub Option<LocExpr>);
+pub struct Param(pub IStr, pub Option<LocExpr>);
/// Defined function parameters
-#[cfg_attr(feature = "dump", derive(Codegen))]
#[cfg_attr(feature = "serialize", derive(Serialize))]
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
#[derive(Debug, Clone, PartialEq)]
@@ -165,13 +156,11 @@
}
}
-#[cfg_attr(feature = "dump", derive(Codegen))]
#[cfg_attr(feature = "serialize", derive(Serialize))]
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
#[derive(Debug, PartialEq)]
pub struct Arg(pub Option<String>, pub LocExpr);
-#[cfg_attr(feature = "dump", derive(Codegen))]
#[cfg_attr(feature = "serialize", derive(Serialize))]
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
#[derive(Debug, PartialEq)]
@@ -183,29 +172,25 @@
}
}
-#[cfg_attr(feature = "dump", derive(Codegen))]
#[cfg_attr(feature = "serialize", derive(Serialize))]
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
#[derive(Debug, Clone, PartialEq)]
pub struct BindSpec {
- pub name: Rc<str>,
+ pub name: IStr,
pub params: Option<ParamsDesc>,
pub value: LocExpr,
}
-#[cfg_attr(feature = "dump", derive(Codegen))]
#[cfg_attr(feature = "serialize", derive(Serialize))]
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
#[derive(Debug, PartialEq)]
pub struct IfSpecData(pub LocExpr);
-#[cfg_attr(feature = "dump", derive(Codegen))]
#[cfg_attr(feature = "serialize", derive(Serialize))]
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
#[derive(Debug, PartialEq)]
-pub struct ForSpecData(pub Rc<str>, pub LocExpr);
+pub struct ForSpecData(pub IStr, pub LocExpr);
-#[cfg_attr(feature = "dump", derive(Codegen))]
#[cfg_attr(feature = "serialize", derive(Serialize))]
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
#[derive(Debug, PartialEq)]
@@ -214,7 +199,6 @@
ForSpec(ForSpecData),
}
-#[cfg_attr(feature = "dump", derive(Codegen))]
#[cfg_attr(feature = "serialize", derive(Serialize))]
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
#[derive(Debug, PartialEq)]
@@ -226,7 +210,6 @@
pub compspecs: Vec<CompSpec>,
}
-#[cfg_attr(feature = "dump", derive(Codegen))]
#[cfg_attr(feature = "serialize", derive(Serialize))]
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
#[derive(Debug, PartialEq)]
@@ -235,7 +218,6 @@
ObjComp(ObjComp),
}
-#[cfg_attr(feature = "dump", derive(Codegen))]
#[cfg_attr(feature = "serialize", derive(Serialize))]
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
#[derive(Debug, PartialEq, Clone, Copy)]
@@ -256,7 +238,6 @@
}
/// Syntax base
-#[cfg_attr(feature = "dump", derive(Codegen))]
#[cfg_attr(feature = "serialize", derive(Serialize))]
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
#[derive(Debug, PartialEq)]
@@ -264,11 +245,11 @@
Literal(LiteralType),
/// String value: "hello"
- Str(Rc<str>),
+ Str(IStr),
/// Number: 1, 2.0, 2e+20
Num(f64),
/// Variable name: test
- Var(Rc<str>),
+ Var(IStr),
/// Array of expressions: [1, 2, "Hello"]
Arr(Vec<LocExpr>),
@@ -315,7 +296,7 @@
/// function(x) x
Function(ParamsDesc, LocExpr),
/// std.primitiveEquals
- Intrinsic(Rc<str>),
+ Intrinsic(IStr),
/// if true == false then 1 else 2
IfElse {
cond: IfSpecData,
@@ -325,7 +306,6 @@
}
/// file, begin offset, end offset
-#[cfg_attr(feature = "dump", derive(Codegen))]
#[cfg_attr(feature = "serialize", derive(Serialize))]
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
#[derive(Clone, PartialEq)]
@@ -337,7 +317,6 @@
}
/// Holds AST expression and its location in source file
-#[cfg_attr(feature = "dump", derive(Codegen))]
#[cfg_attr(feature = "serialize", derive(Serialize))]
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
#[derive(Clone, PartialEq)]
crates/jrsonnet-types/Cargo.tomldiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-types/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "jrsonnet-types"
+version = "0.3.3"
+authors = ["Yaroslav Bolyukin <iam@lach.pw>"]
+edition = "2018"
+
+[dependencies]
+peg = "0.6.3"
\ No newline at end of file
crates/jrsonnet-types/src/lib.rsdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-types/src/lib.rs
@@ -0,0 +1,285 @@
+use std::fmt::Display;
+
+#[macro_export]
+macro_rules! ty {
+ ((Array<number>)) => {{
+ $crate::ComplexValType::ArrayRef(&$crate::ComplexValType::Simple($crate::ValType::Num))
+ }};
+ (array) => {
+ $crate::ComplexValType::Simple($crate::ValType::Arr)
+ };
+ (boolean) => {
+ $crate::ComplexValType::Simple($crate::ValType::Bool)
+ };
+ (null) => {
+ $crate::ComplexValType::Simple($crate::ValType::Null)
+ };
+ (string) => {
+ $crate::ComplexValType::Simple($crate::ValType::Str)
+ };
+ (char) => {
+ $crate::ComplexValType::Char
+ };
+ (number) => {
+ $crate::ComplexValType::Simple($crate::ValType::Num)
+ };
+ (BoundedNumber<($min:expr), ($max:expr)>) => {{
+ $crate::ComplexValType::BoundedNumber($min, $max)
+ }};
+ (object) => {
+ $crate::ComplexValType::Simple($crate::ValType::Obj)
+ };
+ (any) => {
+ $crate::ComplexValType::Any
+ };
+ (function) => {
+ $crate::ComplexValType::Simple($crate::ValType::Func)
+ };
+ (($($a:tt) |+)) => {{
+ static CONTENTS: &'static [$crate::ComplexValType] = &[
+ $(ty!($a)),+
+ ];
+ $crate::ComplexValType::UnionRef(CONTENTS)
+ }};
+ (($($a:tt) &+)) => {{
+ static CONTENTS: &'static [$crate::ComplexValType] = &[
+ $(ty!($a)),+
+ ];
+ $crate::ComplexValType::SumRef(CONTENTS)
+ }};
+}
+
+#[test]
+fn test() {
+ assert_eq!(
+ ty!((Array<number>)),
+ ComplexValType::ArrayRef(&ComplexValType::Simple(ValType::Num))
+ );
+ assert_eq!(ty!(array), ComplexValType::Simple(ValType::Arr));
+ assert_eq!(ty!(any), ComplexValType::Any);
+ assert_eq!(
+ ty!((string | number)),
+ ComplexValType::UnionRef(&[
+ ComplexValType::Simple(ValType::Str),
+ ComplexValType::Simple(ValType::Num)
+ ])
+ );
+ assert_eq!(
+ format!("{}", ty!(((string & number) | (object & null)))),
+ "string & number | object & null"
+ );
+ assert_eq!(format!("{}", ty!((string | array))), "string | array");
+ assert_eq!(
+ format!("{}", ty!(((string & number) | array))),
+ "string & number | array"
+ );
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum ValType {
+ Bool,
+ Null,
+ Str,
+ Num,
+ Arr,
+ Obj,
+ Func,
+}
+
+impl ValType {
+ pub const fn name(&self) -> &'static str {
+ use ValType::*;
+ match self {
+ Bool => "boolean",
+ Null => "null",
+ Str => "string",
+ Num => "number",
+ Arr => "array",
+ Obj => "object",
+ Func => "function",
+ }
+ }
+}
+
+impl Display for ValType {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "{}", self.name())
+ }
+}
+
+#[derive(Debug, Clone, PartialEq)]
+pub enum ComplexValType {
+ Any,
+ Char,
+ Simple(ValType),
+ BoundedNumber(Option<f64>, Option<f64>),
+ Array(Box<ComplexValType>),
+ ArrayRef(&'static ComplexValType),
+ ObjectRef(&'static [(&'static str, ComplexValType)]),
+ Union(Vec<ComplexValType>),
+ UnionRef(&'static [ComplexValType]),
+ Sum(Vec<ComplexValType>),
+ SumRef(&'static [ComplexValType]),
+}
+impl From<ValType> for ComplexValType {
+ fn from(s: ValType) -> Self {
+ Self::Simple(s)
+ }
+}
+
+fn write_union(
+ f: &mut std::fmt::Formatter<'_>,
+ is_union: bool,
+ union: &[ComplexValType],
+) -> std::fmt::Result {
+ for (i, v) in union.iter().enumerate() {
+ let should_add_braces =
+ matches!(v, ComplexValType::UnionRef(_) | ComplexValType::Union(_) if !is_union);
+ if i != 0 {
+ write!(f, " {} ", if is_union { '|' } else { '&' })?;
+ }
+ if should_add_braces {
+ write!(f, "(")?;
+ }
+ write!(f, "{}", v)?;
+ if should_add_braces {
+ write!(f, ")")?;
+ }
+ }
+ Ok(())
+}
+
+fn print_array(a: &ComplexValType, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ if *a == ComplexValType::Any {
+ write!(f, "array")?
+ } else {
+ write!(f, "Array<{}>", a)?
+ }
+ Ok(())
+}
+
+impl Display for ComplexValType {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ match self {
+ ComplexValType::Any => write!(f, "any")?,
+ ComplexValType::Simple(s) => write!(f, "{}", s)?,
+ ComplexValType::Char => write!(f, "char")?,
+ ComplexValType::BoundedNumber(a, b) => write!(
+ f,
+ "BoundedNumber<{}, {}>",
+ a.map(|e| e.to_string()).unwrap_or_else(|| "".into()),
+ b.map(|e| e.to_string()).unwrap_or_else(|| "".into())
+ )?,
+ ComplexValType::ArrayRef(a) => print_array(a, f)?,
+ ComplexValType::Array(a) => print_array(a, f)?,
+ ComplexValType::ObjectRef(fields) => {
+ write!(f, "{{")?;
+ for (i, (k, v)) in fields.iter().enumerate() {
+ if i != 0 {
+ write!(f, ", ")?;
+ }
+ write!(f, "{}: {}", k, v)?;
+ }
+ write!(f, "}}")?;
+ }
+ ComplexValType::Union(v) => write_union(f, true, v)?,
+ ComplexValType::UnionRef(v) => write_union(f, true, v)?,
+ ComplexValType::Sum(v) => write_union(f, false, v)?,
+ ComplexValType::SumRef(v) => write_union(f, false, v)?,
+ };
+ Ok(())
+ }
+}
+
+peg::parser! {
+pub grammar parser() for str {
+ rule number() -> f64
+ = n:$(['0'..='9']+) { n.parse().unwrap() }
+
+ rule any_ty() -> ComplexValType = "any" { ComplexValType::Any }
+ rule char_ty() -> ComplexValType = "character" { ComplexValType::Char }
+ rule bool_ty() -> ComplexValType = "boolean" { ComplexValType::Simple(ValType::Bool) }
+ rule null_ty() -> ComplexValType = "null" { ComplexValType::Simple(ValType::Null) }
+ rule str_ty() -> ComplexValType = "string" { ComplexValType::Simple(ValType::Str) }
+ rule num_ty() -> ComplexValType = "number" { ComplexValType::Simple(ValType::Num) }
+ rule simple_array_ty() -> ComplexValType = "array" { ComplexValType::Simple(ValType::Arr) }
+ rule simple_object_ty() -> ComplexValType = "object" { ComplexValType::Simple(ValType::Obj) }
+ rule simple_function_ty() -> ComplexValType = "function" { ComplexValType::Simple(ValType::Func) }
+
+ rule array_ty() -> ComplexValType
+ = "Array<" t:ty() ">" { ComplexValType::Array(Box::new(t)) }
+
+ rule bounded_number_ty() -> ComplexValType
+ = "BoundedNumber<" a:number() ", " b:number() ">" { ComplexValType::BoundedNumber(Some(a), Some(b)) }
+
+ rule ty_basic() -> ComplexValType
+ = any_ty()
+ / char_ty()
+ / bool_ty()
+ / null_ty()
+ / str_ty()
+ / num_ty()
+ / simple_array_ty()
+ / simple_object_ty()
+ / simple_function_ty()
+ / array_ty()
+ / bounded_number_ty()
+
+ pub rule ty() -> ComplexValType
+ = precedence! {
+ a:(@) " | " b:@ {
+ match a {
+ ComplexValType::Union(mut a) => {
+ a.push(b);
+ ComplexValType::Union(a)
+ }
+ _ => ComplexValType::Union(vec![a, b]),
+ }
+ }
+ --
+ a:(@) " & " b:@ {
+ match a {
+ ComplexValType::Sum(mut a) => {
+ a.push(b);
+ ComplexValType::Sum(a)
+ }
+ _ => ComplexValType::Sum(vec![a, b]),
+ }
+ }
+ --
+ "(" t:ty() ")" { t }
+ t:ty_basic() { t }
+ }
+}
+}
+
+#[cfg(test)]
+pub mod tests {
+ use super::parser;
+
+ #[test]
+ fn precedence() {
+ assert_eq!(
+ parser::ty("(any & any) | (any | any) & any")
+ .unwrap()
+ .to_string(),
+ "any & any | (any | any) & any"
+ );
+ }
+
+ #[test]
+ fn array() {
+ assert_eq!(parser::ty("Array<any>").unwrap().to_string(), "array");
+ assert_eq!(
+ parser::ty("Array<number>").unwrap().to_string(),
+ "Array<number>"
+ );
+ }
+ #[test]
+ fn bounded_number() {
+ assert_eq!(
+ parser::ty("BoundedNumber<1, 2>").unwrap().to_string(),
+ "BoundedNumber<1, 2>"
+ );
+ }
+}