difftreelog
feat string interning
in: master
20 files changed
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 {
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,6 +22,7 @@
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" }
@@ -58,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,11 @@
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"
)
})
.collect(),
@@ -52,15 +51,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
@@ -2,6 +2,7 @@
#![allow(clippy::too_many_arguments)]
use crate::{error::Error::*, throw, LocError, ObjValue, Result, Val};
+use jrsonnet_interner::IStr;
use jrsonnet_types::ValType;
use thiserror::Error;
@@ -20,7 +21,7 @@
#[error("mapping keys required")]
MappingKeysRequired,
#[error("no such format field: {0}")]
- NoSuchFormatField(Rc<str>),
+ NoSuchFormatField(IStr),
}
impl From<FormatError> for LocError {
@@ -29,7 +30,6 @@
}
}
-use std::rc::Rc;
use FormatError::*;
type ParseResult<'t, T> = std::result::Result<(T, &'t str), FormatError>;
@@ -680,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/mod.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/builtin/mod.rs
+++ b/crates/jrsonnet-evaluator/src/builtin/mod.rs
@@ -6,6 +6,7 @@
with_state, ArrValue, Context, FuncVal, LazyVal, Val,
};
use format::{format_arr, format_obj};
+use jrsonnet_interner::IStr;
use jrsonnet_parser::{ArgsDesc, BinaryOpType, ExprLocation};
use jrsonnet_types::ty;
use std::{collections::HashMap, path::PathBuf, rc::Rc};
@@ -19,7 +20,7 @@
pub mod manifest;
pub mod sort;
-fn std_format(str: Rc<str>, vals: Val) -> Result<Val> {
+fn std_format(str: IStr, vals: Val) -> Result<Val> {
push(
&Some(ExprLocation(Rc::from(PathBuf::from("std.jsonnet")), 0, 0)),
|| format!("std.format of {}", str),
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<IStr, 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
@@ -2,6 +2,7 @@
builtin::{format::FormatError, sort::SortError},
typed::TypeLocError,
};
+use jrsonnet_interner::IStr;
use jrsonnet_parser::{BinaryOpType, ExprLocation, UnaryOpType};
use jrsonnet_types::ValType;
use std::{path::PathBuf, rc::Rc};
@@ -10,7 +11,7 @@
#[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,
@@ -33,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}")]
@@ -86,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")]
crates/jrsonnet-evaluator/src/evaluate.rsdiffbeforeafterboth1use crate::{2 context_creator, error::Error::*, future_wrapper, lazy_val, push, throw, with_state, Context,3 ContextCreator, FuncDesc, FuncVal, LazyBinding, LazyVal, ObjMember, ObjValue, Result, Val,4};5use closure::closure;6use jrsonnet_parser::{7 ArgsDesc, AssertStmt, BinaryOpType, BindSpec, CompSpec, Expr, ExprLocation, FieldMember,8 ForSpecData, IfSpecData, LiteralType, LocExpr, Member, ObjBody, ParamsDesc, UnaryOpType,9 Visibility,10};11use jrsonnet_types::ValType;12use rustc_hash::FxHashMap;13use std::{collections::HashMap, rc::Rc};1415pub fn evaluate_binding(b: &BindSpec, context_creator: ContextCreator) -> (Rc<str>, LazyBinding) {16 let b = b.clone();17 if let Some(params) = &b.params {18 let params = params.clone();19 (20 b.name.clone(),21 LazyBinding::Bindable(Rc::new(move |this, super_obj| {22 Ok(lazy_val!(23 closure!(clone b, clone params, clone context_creator, || Ok(evaluate_method(24 context_creator.0(this.clone(), super_obj.clone())?,25 b.name.clone(),26 params.clone(),27 b.value.clone(),28 )))29 ))30 })),31 )32 } else {33 (34 b.name.clone(),35 LazyBinding::Bindable(Rc::new(move |this, super_obj| {36 Ok(lazy_val!(closure!(clone context_creator, clone b, ||37 evaluate_named(38 context_creator.0(this.clone(), super_obj.clone())?,39 &b.value,40 b.name.clone()41 )42 )))43 })),44 )45 }46}4748pub fn evaluate_method(ctx: Context, name: Rc<str>, params: ParamsDesc, body: LocExpr) -> Val {49 Val::Func(Rc::new(FuncVal::Normal(FuncDesc {50 name,51 ctx,52 params,53 body,54 })))55}5657pub fn evaluate_field_name(58 context: Context,59 field_name: &jrsonnet_parser::FieldName,60) -> Result<Option<Rc<str>>> {61 Ok(match field_name {62 jrsonnet_parser::FieldName::Fixed(n) => Some(n.clone()),63 jrsonnet_parser::FieldName::Dyn(expr) => {64 let value = evaluate(context, expr)?;65 if matches!(value, Val::Null) {66 None67 } else {68 Some(value.try_cast_str("dynamic field name")?)69 }70 }71 })72}7374pub fn evaluate_unary_op(op: UnaryOpType, b: &Val) -> Result<Val> {75 Ok(match (op, b) {76 (UnaryOpType::Not, Val::Bool(v)) => Val::Bool(!v),77 (UnaryOpType::Minus, Val::Num(n)) => Val::Num(-*n),78 (UnaryOpType::BitNot, Val::Num(n)) => Val::Num(!(*n as i32) as f64),79 (op, o) => throw!(UnaryOperatorDoesNotOperateOnType(op, o.value_type())),80 })81}8283pub fn evaluate_add_op(a: &Val, b: &Val) -> Result<Val> {84 Ok(match (a, b) {85 (Val::Str(v1), Val::Str(v2)) => Val::Str(((**v1).to_owned() + v2).into()),8687 // Can't use generic json serialization way, because it depends on number to string concatenation (std.jsonnet:890)88 (Val::Num(n), Val::Str(o)) => Val::Str(format!("{}{}", n, o).into()),89 (Val::Str(o), Val::Num(n)) => Val::Str(format!("{}{}", o, n).into()),9091 (Val::Str(s), o) => Val::Str(format!("{}{}", s, o.clone().to_string()?).into()),92 (o, Val::Str(s)) => Val::Str(format!("{}{}", o.clone().to_string()?, s).into()),9394 (Val::Obj(v1), Val::Obj(v2)) => Val::Obj(v2.with_super(v1.clone())),95 (Val::Arr(a), Val::Arr(b)) => {96 let mut out = Vec::with_capacity(a.len() + b.len());97 out.extend(a.iter_lazy());98 out.extend(b.iter_lazy());99 Val::Arr(out.into())100 }101 (Val::Num(v1), Val::Num(v2)) => Val::new_checked_num(v1 + v2)?,102 _ => throw!(BinaryOperatorDoesNotOperateOnValues(103 BinaryOpType::Add,104 a.value_type(),105 b.value_type(),106 )),107 })108}109110pub fn evaluate_binary_op_special(111 context: Context,112 a: &LocExpr,113 op: BinaryOpType,114 b: &LocExpr,115) -> Result<Val> {116 Ok(match (evaluate(context.clone(), a)?, op, b) {117 (Val::Bool(true), BinaryOpType::Or, _o) => Val::Bool(true),118 (Val::Bool(false), BinaryOpType::And, _o) => Val::Bool(false),119 (a, op, eb) => evaluate_binary_op_normal(&a, op, &evaluate(context, eb)?)?,120 })121}122123pub fn evaluate_binary_op_normal(a: &Val, op: BinaryOpType, b: &Val) -> Result<Val> {124 Ok(match (a, op, b) {125 (a, BinaryOpType::Add, b) => evaluate_add_op(a, b)?,126127 (Val::Str(v1), BinaryOpType::Mul, Val::Num(v2)) => Val::Str(v1.repeat(*v2 as usize).into()),128129 // Bool X Bool130 (Val::Bool(a), BinaryOpType::And, Val::Bool(b)) => Val::Bool(*a && *b),131 (Val::Bool(a), BinaryOpType::Or, Val::Bool(b)) => Val::Bool(*a || *b),132133 // Str X Str134 (Val::Str(v1), BinaryOpType::Lt, Val::Str(v2)) => Val::Bool(v1 < v2),135 (Val::Str(v1), BinaryOpType::Gt, Val::Str(v2)) => Val::Bool(v1 > v2),136 (Val::Str(v1), BinaryOpType::Lte, Val::Str(v2)) => Val::Bool(v1 <= v2),137 (Val::Str(v1), BinaryOpType::Gte, Val::Str(v2)) => Val::Bool(v1 >= v2),138139 // Num X Num140 (Val::Num(v1), BinaryOpType::Mul, Val::Num(v2)) => Val::new_checked_num(v1 * v2)?,141 (Val::Num(v1), BinaryOpType::Div, Val::Num(v2)) => {142 if *v2 <= f64::EPSILON {143 throw!(DivisionByZero)144 }145 Val::new_checked_num(v1 / v2)?146 }147148 (Val::Num(v1), BinaryOpType::Sub, Val::Num(v2)) => Val::new_checked_num(v1 - v2)?,149150 (Val::Num(v1), BinaryOpType::Lt, Val::Num(v2)) => Val::Bool(v1 < v2),151 (Val::Num(v1), BinaryOpType::Gt, Val::Num(v2)) => Val::Bool(v1 > v2),152 (Val::Num(v1), BinaryOpType::Lte, Val::Num(v2)) => Val::Bool(v1 <= v2),153 (Val::Num(v1), BinaryOpType::Gte, Val::Num(v2)) => Val::Bool(v1 >= v2),154155 (Val::Num(v1), BinaryOpType::BitAnd, Val::Num(v2)) => {156 Val::Num(((*v1 as i32) & (*v2 as i32)) as f64)157 }158 (Val::Num(v1), BinaryOpType::BitOr, Val::Num(v2)) => {159 Val::Num(((*v1 as i32) | (*v2 as i32)) as f64)160 }161 (Val::Num(v1), BinaryOpType::BitXor, Val::Num(v2)) => {162 Val::Num(((*v1 as i32) ^ (*v2 as i32)) as f64)163 }164 (Val::Num(v1), BinaryOpType::Lhs, Val::Num(v2)) => {165 if *v2 < 0.0 {166 throw!(RuntimeError("shift by negative exponent".into()))167 }168 Val::Num(((*v1 as i32) << (*v2 as i32)) as f64)169 }170 (Val::Num(v1), BinaryOpType::Rhs, Val::Num(v2)) => {171 if *v2 < 0.0 {172 throw!(RuntimeError("shift by negative exponent".into()))173 }174 Val::Num(((*v1 as i32) >> (*v2 as i32)) as f64)175 }176177 _ => throw!(BinaryOperatorDoesNotOperateOnValues(178 op,179 a.value_type(),180 b.value_type(),181 )),182 })183}184185future_wrapper!(HashMap<Rc<str>, LazyBinding>, FutureNewBindings);186future_wrapper!(ObjValue, FutureObjValue);187188pub fn evaluate_comp<T>(189 context: Context,190 value: &impl Fn(Context) -> Result<T>,191 specs: &[CompSpec],192) -> Result<Option<Vec<T>>> {193 Ok(match specs.get(0) {194 None => Some(vec![value(context)?]),195 Some(CompSpec::IfSpec(IfSpecData(cond))) => {196 if evaluate(context.clone(), cond)?.try_cast_bool("if spec")? {197 evaluate_comp(context, value, &specs[1..])?198 } else {199 None200 }201 }202 Some(CompSpec::ForSpec(ForSpecData(var, expr))) => match evaluate(context.clone(), expr)? {203 Val::Arr(list) => {204 let mut out = Vec::new();205 for item in list.iter() {206 out.push(evaluate_comp(207 context.clone().with_var(var.clone(), item?.clone()),208 value,209 &specs[1..],210 )?);211 }212 Some(out.into_iter().flatten().flatten().collect())213 }214 _ => throw!(InComprehensionCanOnlyIterateOverArray),215 },216 })217}218219pub fn evaluate_member_list_object(context: Context, members: &[Member]) -> Result<ObjValue> {220 let new_bindings = FutureNewBindings::new();221 let future_this = FutureObjValue::new();222 let context_creator = context_creator!(223 closure!(clone context, clone new_bindings, |this: Option<ObjValue>, super_obj: Option<ObjValue>| {224 Ok(context.clone().extend_unbound(225 new_bindings.clone().unwrap(),226 context.dollar().clone().or_else(||this.clone()),227 Some(this.unwrap()),228 super_obj229 )?)230 })231 );232 {233 let mut bindings: HashMap<Rc<str>, LazyBinding> = HashMap::new();234 for (n, b) in members235 .iter()236 .filter_map(|m| match m {237 Member::BindStmt(b) => Some(b.clone()),238 _ => None,239 })240 .map(|b| evaluate_binding(&b, context_creator.clone()))241 {242 bindings.insert(n, b);243 }244 new_bindings.fill(bindings);245 }246247 let mut new_members = HashMap::new();248 for member in members.iter() {249 match member {250 Member::Field(FieldMember {251 name,252 plus,253 params: None,254 visibility,255 value,256 }) => {257 let name = evaluate_field_name(context.clone(), name)?;258 if name.is_none() {259 continue;260 }261 let name = name.unwrap();262 new_members.insert(263 name.clone(),264 ObjMember {265 add: *plus,266 visibility: *visibility,267 invoke: LazyBinding::Bindable(Rc::new(268 closure!(clone name, clone value, clone context_creator, |this, super_obj| {269 Ok(LazyVal::new_resolved(evaluate(270 context_creator.0(this, super_obj)?,271 &value,272 )?))273 }),274 )),275 location: value.1.clone(),276 },277 );278 }279 Member::Field(FieldMember {280 name,281 params: Some(params),282 value,283 ..284 }) => {285 let name = evaluate_field_name(context.clone(), name)?;286 if name.is_none() {287 continue;288 }289 let name = name.unwrap();290 new_members.insert(291 name.clone(),292 ObjMember {293 add: false,294 visibility: Visibility::Hidden,295 invoke: LazyBinding::Bindable(Rc::new(296 closure!(clone value, clone context_creator, clone params, clone name, |this, super_obj| {297 // TODO: Assert298 Ok(LazyVal::new_resolved(evaluate_method(299 context_creator.0(this, super_obj)?,300 name.clone(),301 params.clone(),302 value.clone(),303 )))304 }),305 )),306 location: value.1.clone(),307 },308 );309 }310 Member::BindStmt(_) => {}311 Member::AssertStmt(_) => {}312 }313 }314 Ok(future_this.fill(ObjValue::new(None, Rc::new(new_members))))315}316317pub fn evaluate_object(context: Context, object: &ObjBody) -> Result<ObjValue> {318 Ok(match object {319 ObjBody::MemberList(members) => evaluate_member_list_object(context, members)?,320 ObjBody::ObjComp(obj) => {321 let future_this = FutureObjValue::new();322 let mut new_members = HashMap::new();323 for (k, v) in evaluate_comp(324 context.clone(),325 &|ctx| {326 let new_bindings = FutureNewBindings::new();327 let context_creator = context_creator!(328 closure!(clone context, clone new_bindings, |this: Option<ObjValue>, super_obj: Option<ObjValue>| {329 Ok(context.clone().extend_unbound(330 new_bindings.clone().unwrap(),331 context.dollar().clone().or_else(||this.clone()),332 None,333 super_obj334 )?)335 })336 );337 let mut bindings: HashMap<Rc<str>, LazyBinding> = HashMap::new();338 for (n, b) in obj339 .pre_locals340 .iter()341 .chain(obj.post_locals.iter())342 .map(|b| evaluate_binding(b, context_creator.clone()))343 {344 bindings.insert(n, b);345 }346 let bindings = new_bindings.fill(bindings);347 let ctx = ctx.extend_unbound(bindings, None, None, None)?;348 let key = evaluate(ctx.clone(), &obj.key)?;349 let value = LazyBinding::Bindable(Rc::new(350 closure!(clone ctx, clone obj.value, |this, _super_obj| {351 Ok(LazyVal::new_resolved(evaluate(ctx.clone().extend(FxHashMap::default(), None, this, None), &value)?))352 }),353 ));354355 Ok((key, value))356 },357 &obj.compspecs,358 )?359 .unwrap()360 {361 match k {362 Val::Null => {}363 Val::Str(n) => {364 new_members.insert(365 n,366 ObjMember {367 add: false,368 visibility: Visibility::Normal,369 invoke: v,370 location: obj.value.1.clone(),371 },372 );373 }374 v => throw!(FieldMustBeStringGot(v.value_type())),375 }376 }377378 future_this.fill(ObjValue::new(None, Rc::new(new_members)))379 }380 })381}382383pub fn evaluate_apply(384 context: Context,385 value: &LocExpr,386 args: &ArgsDesc,387 loc: &Option<ExprLocation>,388 tailstrict: bool,389) -> Result<Val> {390 let value = evaluate(context.clone(), value)?;391 Ok(match value {392 Val::Func(f) => {393 let body = || f.evaluate(context, loc, args, tailstrict);394 if tailstrict {395 body()?396 } else {397 push(loc, || format!("function <{}> call", f.name()), body)?398 }399 }400 v => throw!(OnlyFunctionsCanBeCalledGot(v.value_type())),401 })402}403404pub fn evaluate_named(context: Context, lexpr: &LocExpr, name: Rc<str>) -> Result<Val> {405 use Expr::*;406 let LocExpr(expr, _loc) = lexpr;407 Ok(match &**expr {408 Function(params, body) => evaluate_method(context, name, params.clone(), body.clone()),409 _ => evaluate(context, lexpr)?,410 })411}412413pub fn evaluate(context: Context, expr: &LocExpr) -> Result<Val> {414 use Expr::*;415 let LocExpr(expr, loc) = expr;416 Ok(match &**expr {417 Literal(LiteralType::This) => {418 Val::Obj(context.this().clone().ok_or(CantUseSelfOutsideOfObject)?)419 }420 Literal(LiteralType::Dollar) => {421 Val::Obj(context.dollar().clone().ok_or(NoTopLevelObjectFound)?)422 }423 Literal(LiteralType::True) => Val::Bool(true),424 Literal(LiteralType::False) => Val::Bool(false),425 Literal(LiteralType::Null) => Val::Null,426 Parened(e) => evaluate(context, e)?,427 Str(v) => Val::Str(v.clone()),428 Num(v) => Val::new_checked_num(*v)?,429 BinaryOp(v1, o, v2) => evaluate_binary_op_special(context, v1, *o, v2)?,430 UnaryOp(o, v) => evaluate_unary_op(*o, &evaluate(context, v)?)?,431 Var(name) => push(432 loc,433 || format!("variable <{}>", name),434 || Ok(context.binding(name.clone())?.evaluate()?),435 )?,436 Index(LocExpr(v, _), index) if matches!(&**v, Expr::Literal(LiteralType::Super)) => {437 let name = evaluate(context.clone(), index)?.try_cast_str("object index")?;438 context439 .super_obj()440 .clone()441 .expect("no super found")442 .get_raw(name, Some(&context.this().clone().expect("no this found")))?443 .expect("value not found")444 }445 Index(value, index) => {446 match (evaluate(context.clone(), value)?, evaluate(context, index)?) {447 (Val::Obj(v), Val::Str(s)) => {448 let sn = s.clone();449 push(450 loc,451 || format!("field <{}> access", sn),452 || {453 if let Some(v) = v.get(s.clone())? {454 Ok(v)455 } else if v.get("__intrinsic_namespace__".into())?.is_some() {456 Ok(Val::Func(Rc::new(FuncVal::Intrinsic(s))))457 } else {458 throw!(NoSuchField(s))459 }460 },461 )?462 }463 (Val::Obj(_), n) => throw!(ValueIndexMustBeTypeGot(464 ValType::Obj,465 ValType::Str,466 n.value_type(),467 )),468469 (Val::Arr(v), Val::Num(n)) => {470 if n.fract() > f64::EPSILON {471 throw!(FractionalIndex)472 }473 v.get(n as usize)?474 .ok_or_else(|| ArrayBoundsError(n as usize, v.len()))?475 .clone()476 }477 (Val::Arr(_), Val::Str(n)) => throw!(AttemptedIndexAnArrayWithString(n)),478 (Val::Arr(_), n) => throw!(ValueIndexMustBeTypeGot(479 ValType::Arr,480 ValType::Num,481 n.value_type(),482 )),483484 (Val::Str(s), Val::Num(n)) => Val::Str(485 s.chars()486 .skip(n as usize)487 .take(1)488 .collect::<String>()489 .into(),490 ),491 (Val::Str(_), n) => throw!(ValueIndexMustBeTypeGot(492 ValType::Str,493 ValType::Num,494 n.value_type(),495 )),496497 (v, _) => throw!(CantIndexInto(v.value_type())),498 }499 }500 LocalExpr(bindings, returned) => {501 let mut new_bindings: HashMap<Rc<str>, LazyBinding> = HashMap::new();502 let future_context = Context::new_future();503504 let context_creator = context_creator!(505 closure!(clone future_context, |_, _| Ok(future_context.clone().unwrap()))506 );507508 for (k, v) in bindings509 .iter()510 .map(|b| evaluate_binding(b, context_creator.clone()))511 {512 new_bindings.insert(k, v);513 }514515 let context = context516 .extend_unbound(new_bindings, None, None, None)?517 .into_future(future_context);518 evaluate(context, &returned.clone())?519 }520 Arr(items) => {521 let mut out = Vec::with_capacity(items.len());522 for item in items {523 out.push(LazyVal::new(Box::new(524 closure!(clone context, clone item, || {525 evaluate(context.clone(), &item)526 }),527 )));528 }529 Val::Arr(out.into())530 }531 ArrComp(expr, comp_specs) => Val::Arr(532 // First comp_spec should be for_spec, so no "None" possible here533 evaluate_comp(context, &|ctx| evaluate(ctx, expr), comp_specs)?534 .unwrap()535 .into(),536 ),537 Obj(body) => Val::Obj(evaluate_object(context, body)?),538 ObjExtend(s, t) => evaluate_add_op(539 &evaluate(context.clone(), s)?,540 &Val::Obj(evaluate_object(context, t)?),541 )?,542 Apply(value, args, tailstrict) => evaluate_apply(context, value, args, loc, *tailstrict)?,543 Function(params, body) => {544 evaluate_method(context, "anonymous".into(), params.clone(), body.clone())545 }546 Intrinsic(name) => Val::Func(Rc::new(FuncVal::Intrinsic(name.clone()))),547 AssertExpr(AssertStmt(value, msg), returned) => {548 let assertion_result = push(549 &value.1,550 || "assertion condition".to_owned(),551 || {552 evaluate(context.clone(), value)?553 .try_cast_bool("assertion condition should be of type `boolean`")554 },555 )?;556 if assertion_result {557 evaluate(context, returned)?558 } else {559 push(560 &value.1,561 || "assertion failure".to_owned(),562 || {563 if let Some(msg) = msg {564 throw!(AssertionFailed(evaluate(context, msg)?.to_string()?));565 } else {566 throw!(AssertionFailed(Val::Null.to_string()?));567 }568 },569 )?570 }571 }572 ErrorStmt(e) => push(573 loc,574 || "error statement".to_owned(),575 || {576 throw!(RuntimeError(577 evaluate(context, e)?.try_cast_str("error text should be of type `string`")?,578 ))579 },580 )?,581 IfElse {582 cond,583 cond_then,584 cond_else,585 } => {586 if push(587 loc,588 || "if condition".to_owned(),589 || evaluate(context.clone(), &cond.0)?.try_cast_bool("in if condition"),590 )? {591 evaluate(context, cond_then)?592 } else {593 match cond_else {594 Some(v) => evaluate(context, v)?,595 None => Val::Null,596 }597 }598 }599 Import(path) => {600 let mut tmp = loc601 .clone()602 .expect("imports cannot be used without loc_data")603 .0;604 let import_location = Rc::make_mut(&mut tmp);605 import_location.pop();606 push(607 loc,608 || format!("import {:?}", path),609 || with_state(|s| s.import_file(import_location, path)),610 )?611 }612 ImportStr(path) => {613 let mut tmp = loc614 .clone()615 .expect("imports cannot be used without loc_data")616 .0;617 let import_location = Rc::make_mut(&mut tmp);618 import_location.pop();619 Val::Str(with_state(|s| s.import_file_str(import_location, path))?)620 }621 Literal(LiteralType::Super) => throw!(StandaloneSuper),622 })623}1use crate::{2 context_creator, error::Error::*, future_wrapper, lazy_val, push, throw, with_state, Context,3 ContextCreator, FuncDesc, FuncVal, LazyBinding, LazyVal, ObjMember, ObjValue, Result, Val,4};5use closure::closure;6use jrsonnet_interner::IStr;7use jrsonnet_parser::{8 ArgsDesc, AssertStmt, BinaryOpType, BindSpec, CompSpec, Expr, ExprLocation, FieldMember,9 ForSpecData, IfSpecData, LiteralType, LocExpr, Member, ObjBody, ParamsDesc, UnaryOpType,10 Visibility,11};12use jrsonnet_types::ValType;13use rustc_hash::FxHashMap;14use std::{collections::HashMap, rc::Rc};1516pub fn evaluate_binding(b: &BindSpec, context_creator: ContextCreator) -> (IStr, LazyBinding) {17 let b = b.clone();18 if let Some(params) = &b.params {19 let params = params.clone();20 (21 b.name.clone(),22 LazyBinding::Bindable(Rc::new(move |this, super_obj| {23 Ok(lazy_val!(24 closure!(clone b, clone params, clone context_creator, || Ok(evaluate_method(25 context_creator.0(this.clone(), super_obj.clone())?,26 b.name.clone(),27 params.clone(),28 b.value.clone(),29 )))30 ))31 })),32 )33 } else {34 (35 b.name.clone(),36 LazyBinding::Bindable(Rc::new(move |this, super_obj| {37 Ok(lazy_val!(closure!(clone context_creator, clone b, ||38 evaluate_named(39 context_creator.0(this.clone(), super_obj.clone())?,40 &b.value,41 b.name.clone()42 )43 )))44 })),45 )46 }47}4849pub fn evaluate_method(ctx: Context, name: IStr, params: ParamsDesc, body: LocExpr) -> Val {50 Val::Func(Rc::new(FuncVal::Normal(FuncDesc {51 name,52 ctx,53 params,54 body,55 })))56}5758pub fn evaluate_field_name(59 context: Context,60 field_name: &jrsonnet_parser::FieldName,61) -> Result<Option<IStr>> {62 Ok(match field_name {63 jrsonnet_parser::FieldName::Fixed(n) => Some(n.clone()),64 jrsonnet_parser::FieldName::Dyn(expr) => {65 let value = evaluate(context, expr)?;66 if matches!(value, Val::Null) {67 None68 } else {69 Some(value.try_cast_str("dynamic field name")?)70 }71 }72 })73}7475pub fn evaluate_unary_op(op: UnaryOpType, b: &Val) -> Result<Val> {76 Ok(match (op, b) {77 (UnaryOpType::Not, Val::Bool(v)) => Val::Bool(!v),78 (UnaryOpType::Minus, Val::Num(n)) => Val::Num(-*n),79 (UnaryOpType::BitNot, Val::Num(n)) => Val::Num(!(*n as i32) as f64),80 (op, o) => throw!(UnaryOperatorDoesNotOperateOnType(op, o.value_type())),81 })82}8384pub fn evaluate_add_op(a: &Val, b: &Val) -> Result<Val> {85 Ok(match (a, b) {86 (Val::Str(v1), Val::Str(v2)) => Val::Str(((**v1).to_owned() + v2).into()),8788 // Can't use generic json serialization way, because it depends on number to string concatenation (std.jsonnet:890)89 (Val::Num(n), Val::Str(o)) => Val::Str(format!("{}{}", n, o).into()),90 (Val::Str(o), Val::Num(n)) => Val::Str(format!("{}{}", o, n).into()),9192 (Val::Str(s), o) => Val::Str(format!("{}{}", s, o.clone().to_string()?).into()),93 (o, Val::Str(s)) => Val::Str(format!("{}{}", o.clone().to_string()?, s).into()),9495 (Val::Obj(v1), Val::Obj(v2)) => Val::Obj(v2.with_super(v1.clone())),96 (Val::Arr(a), Val::Arr(b)) => {97 let mut out = Vec::with_capacity(a.len() + b.len());98 out.extend(a.iter_lazy());99 out.extend(b.iter_lazy());100 Val::Arr(out.into())101 }102 (Val::Num(v1), Val::Num(v2)) => Val::new_checked_num(v1 + v2)?,103 _ => throw!(BinaryOperatorDoesNotOperateOnValues(104 BinaryOpType::Add,105 a.value_type(),106 b.value_type(),107 )),108 })109}110111pub fn evaluate_binary_op_special(112 context: Context,113 a: &LocExpr,114 op: BinaryOpType,115 b: &LocExpr,116) -> Result<Val> {117 Ok(match (evaluate(context.clone(), a)?, op, b) {118 (Val::Bool(true), BinaryOpType::Or, _o) => Val::Bool(true),119 (Val::Bool(false), BinaryOpType::And, _o) => Val::Bool(false),120 (a, op, eb) => evaluate_binary_op_normal(&a, op, &evaluate(context, eb)?)?,121 })122}123124pub fn evaluate_binary_op_normal(a: &Val, op: BinaryOpType, b: &Val) -> Result<Val> {125 Ok(match (a, op, b) {126 (a, BinaryOpType::Add, b) => evaluate_add_op(a, b)?,127128 (Val::Str(v1), BinaryOpType::Mul, Val::Num(v2)) => Val::Str(v1.repeat(*v2 as usize).into()),129130 // Bool X Bool131 (Val::Bool(a), BinaryOpType::And, Val::Bool(b)) => Val::Bool(*a && *b),132 (Val::Bool(a), BinaryOpType::Or, Val::Bool(b)) => Val::Bool(*a || *b),133134 // Str X Str135 (Val::Str(v1), BinaryOpType::Lt, Val::Str(v2)) => Val::Bool(v1 < v2),136 (Val::Str(v1), BinaryOpType::Gt, Val::Str(v2)) => Val::Bool(v1 > v2),137 (Val::Str(v1), BinaryOpType::Lte, Val::Str(v2)) => Val::Bool(v1 <= v2),138 (Val::Str(v1), BinaryOpType::Gte, Val::Str(v2)) => Val::Bool(v1 >= v2),139140 // Num X Num141 (Val::Num(v1), BinaryOpType::Mul, Val::Num(v2)) => Val::new_checked_num(v1 * v2)?,142 (Val::Num(v1), BinaryOpType::Div, Val::Num(v2)) => {143 if *v2 <= f64::EPSILON {144 throw!(DivisionByZero)145 }146 Val::new_checked_num(v1 / v2)?147 }148149 (Val::Num(v1), BinaryOpType::Sub, Val::Num(v2)) => Val::new_checked_num(v1 - v2)?,150151 (Val::Num(v1), BinaryOpType::Lt, Val::Num(v2)) => Val::Bool(v1 < v2),152 (Val::Num(v1), BinaryOpType::Gt, Val::Num(v2)) => Val::Bool(v1 > v2),153 (Val::Num(v1), BinaryOpType::Lte, Val::Num(v2)) => Val::Bool(v1 <= v2),154 (Val::Num(v1), BinaryOpType::Gte, Val::Num(v2)) => Val::Bool(v1 >= v2),155156 (Val::Num(v1), BinaryOpType::BitAnd, Val::Num(v2)) => {157 Val::Num(((*v1 as i32) & (*v2 as i32)) as f64)158 }159 (Val::Num(v1), BinaryOpType::BitOr, Val::Num(v2)) => {160 Val::Num(((*v1 as i32) | (*v2 as i32)) as f64)161 }162 (Val::Num(v1), BinaryOpType::BitXor, Val::Num(v2)) => {163 Val::Num(((*v1 as i32) ^ (*v2 as i32)) as f64)164 }165 (Val::Num(v1), BinaryOpType::Lhs, Val::Num(v2)) => {166 if *v2 < 0.0 {167 throw!(RuntimeError("shift by negative exponent".into()))168 }169 Val::Num(((*v1 as i32) << (*v2 as i32)) as f64)170 }171 (Val::Num(v1), BinaryOpType::Rhs, Val::Num(v2)) => {172 if *v2 < 0.0 {173 throw!(RuntimeError("shift by negative exponent".into()))174 }175 Val::Num(((*v1 as i32) >> (*v2 as i32)) as f64)176 }177178 _ => throw!(BinaryOperatorDoesNotOperateOnValues(179 op,180 a.value_type(),181 b.value_type(),182 )),183 })184}185186future_wrapper!(HashMap<IStr, LazyBinding>, FutureNewBindings);187future_wrapper!(ObjValue, FutureObjValue);188189pub fn evaluate_comp<T>(190 context: Context,191 value: &impl Fn(Context) -> Result<T>,192 specs: &[CompSpec],193) -> Result<Option<Vec<T>>> {194 Ok(match specs.get(0) {195 None => Some(vec![value(context)?]),196 Some(CompSpec::IfSpec(IfSpecData(cond))) => {197 if evaluate(context.clone(), cond)?.try_cast_bool("if spec")? {198 evaluate_comp(context, value, &specs[1..])?199 } else {200 None201 }202 }203 Some(CompSpec::ForSpec(ForSpecData(var, expr))) => match evaluate(context.clone(), expr)? {204 Val::Arr(list) => {205 let mut out = Vec::new();206 for item in list.iter() {207 out.push(evaluate_comp(208 context.clone().with_var(var.clone(), item?.clone()),209 value,210 &specs[1..],211 )?);212 }213 Some(out.into_iter().flatten().flatten().collect())214 }215 _ => throw!(InComprehensionCanOnlyIterateOverArray),216 },217 })218}219220pub fn evaluate_member_list_object(context: Context, members: &[Member]) -> Result<ObjValue> {221 let new_bindings = FutureNewBindings::new();222 let future_this = FutureObjValue::new();223 let context_creator = context_creator!(224 closure!(clone context, clone new_bindings, |this: Option<ObjValue>, super_obj: Option<ObjValue>| {225 Ok(context.clone().extend_unbound(226 new_bindings.clone().unwrap(),227 context.dollar().clone().or_else(||this.clone()),228 Some(this.unwrap()),229 super_obj230 )?)231 })232 );233 {234 let mut bindings: HashMap<IStr, LazyBinding> = HashMap::new();235 for (n, b) in members236 .iter()237 .filter_map(|m| match m {238 Member::BindStmt(b) => Some(b.clone()),239 _ => None,240 })241 .map(|b| evaluate_binding(&b, context_creator.clone()))242 {243 bindings.insert(n, b);244 }245 new_bindings.fill(bindings);246 }247248 let mut new_members = HashMap::new();249 for member in members.iter() {250 match member {251 Member::Field(FieldMember {252 name,253 plus,254 params: None,255 visibility,256 value,257 }) => {258 let name = evaluate_field_name(context.clone(), name)?;259 if name.is_none() {260 continue;261 }262 let name = name.unwrap();263 new_members.insert(264 name.clone(),265 ObjMember {266 add: *plus,267 visibility: *visibility,268 invoke: LazyBinding::Bindable(Rc::new(269 closure!(clone name, clone value, clone context_creator, |this, super_obj| {270 Ok(LazyVal::new_resolved(evaluate(271 context_creator.0(this, super_obj)?,272 &value,273 )?))274 }),275 )),276 location: value.1.clone(),277 },278 );279 }280 Member::Field(FieldMember {281 name,282 params: Some(params),283 value,284 ..285 }) => {286 let name = evaluate_field_name(context.clone(), name)?;287 if name.is_none() {288 continue;289 }290 let name = name.unwrap();291 new_members.insert(292 name.clone(),293 ObjMember {294 add: false,295 visibility: Visibility::Hidden,296 invoke: LazyBinding::Bindable(Rc::new(297 closure!(clone value, clone context_creator, clone params, clone name, |this, super_obj| {298 // TODO: Assert299 Ok(LazyVal::new_resolved(evaluate_method(300 context_creator.0(this, super_obj)?,301 name.clone(),302 params.clone(),303 value.clone(),304 )))305 }),306 )),307 location: value.1.clone(),308 },309 );310 }311 Member::BindStmt(_) => {}312 Member::AssertStmt(_) => {}313 }314 }315 Ok(future_this.fill(ObjValue::new(None, Rc::new(new_members))))316}317318pub fn evaluate_object(context: Context, object: &ObjBody) -> Result<ObjValue> {319 Ok(match object {320 ObjBody::MemberList(members) => evaluate_member_list_object(context, members)?,321 ObjBody::ObjComp(obj) => {322 let future_this = FutureObjValue::new();323 let mut new_members = HashMap::new();324 for (k, v) in evaluate_comp(325 context.clone(),326 &|ctx| {327 let new_bindings = FutureNewBindings::new();328 let context_creator = context_creator!(329 closure!(clone context, clone new_bindings, |this: Option<ObjValue>, super_obj: Option<ObjValue>| {330 Ok(context.clone().extend_unbound(331 new_bindings.clone().unwrap(),332 context.dollar().clone().or_else(||this.clone()),333 None,334 super_obj335 )?)336 })337 );338 let mut bindings: HashMap<IStr, LazyBinding> = HashMap::new();339 for (n, b) in obj340 .pre_locals341 .iter()342 .chain(obj.post_locals.iter())343 .map(|b| evaluate_binding(b, context_creator.clone()))344 {345 bindings.insert(n, b);346 }347 let bindings = new_bindings.fill(bindings);348 let ctx = ctx.extend_unbound(bindings, None, None, None)?;349 let key = evaluate(ctx.clone(), &obj.key)?;350 let value = LazyBinding::Bindable(Rc::new(351 closure!(clone ctx, clone obj.value, |this, _super_obj| {352 Ok(LazyVal::new_resolved(evaluate(ctx.clone().extend(FxHashMap::default(), None, this, None), &value)?))353 }),354 ));355356 Ok((key, value))357 },358 &obj.compspecs,359 )?360 .unwrap()361 {362 match k {363 Val::Null => {}364 Val::Str(n) => {365 new_members.insert(366 n,367 ObjMember {368 add: false,369 visibility: Visibility::Normal,370 invoke: v,371 location: obj.value.1.clone(),372 },373 );374 }375 v => throw!(FieldMustBeStringGot(v.value_type())),376 }377 }378379 future_this.fill(ObjValue::new(None, Rc::new(new_members)))380 }381 })382}383384pub fn evaluate_apply(385 context: Context,386 value: &LocExpr,387 args: &ArgsDesc,388 loc: &Option<ExprLocation>,389 tailstrict: bool,390) -> Result<Val> {391 let value = evaluate(context.clone(), value)?;392 Ok(match value {393 Val::Func(f) => {394 let body = || f.evaluate(context, loc, args, tailstrict);395 if tailstrict {396 body()?397 } else {398 push(loc, || format!("function <{}> call", f.name()), body)?399 }400 }401 v => throw!(OnlyFunctionsCanBeCalledGot(v.value_type())),402 })403}404405pub fn evaluate_named(context: Context, lexpr: &LocExpr, name: IStr) -> Result<Val> {406 use Expr::*;407 let LocExpr(expr, _loc) = lexpr;408 Ok(match &**expr {409 Function(params, body) => evaluate_method(context, name, params.clone(), body.clone()),410 _ => evaluate(context, lexpr)?,411 })412}413414pub fn evaluate(context: Context, expr: &LocExpr) -> Result<Val> {415 use Expr::*;416 let LocExpr(expr, loc) = expr;417 Ok(match &**expr {418 Literal(LiteralType::This) => {419 Val::Obj(context.this().clone().ok_or(CantUseSelfOutsideOfObject)?)420 }421 Literal(LiteralType::Dollar) => {422 Val::Obj(context.dollar().clone().ok_or(NoTopLevelObjectFound)?)423 }424 Literal(LiteralType::True) => Val::Bool(true),425 Literal(LiteralType::False) => Val::Bool(false),426 Literal(LiteralType::Null) => Val::Null,427 Parened(e) => evaluate(context, e)?,428 Str(v) => Val::Str(v.clone()),429 Num(v) => Val::new_checked_num(*v)?,430 BinaryOp(v1, o, v2) => evaluate_binary_op_special(context, v1, *o, v2)?,431 UnaryOp(o, v) => evaluate_unary_op(*o, &evaluate(context, v)?)?,432 Var(name) => push(433 loc,434 || format!("variable <{}>", name),435 || Ok(context.binding(name.clone())?.evaluate()?),436 )?,437 Index(LocExpr(v, _), index) if matches!(&**v, Expr::Literal(LiteralType::Super)) => {438 let name = evaluate(context.clone(), index)?.try_cast_str("object index")?;439 context440 .super_obj()441 .clone()442 .expect("no super found")443 .get_raw(name, Some(&context.this().clone().expect("no this found")))?444 .expect("value not found")445 }446 Index(value, index) => {447 match (evaluate(context.clone(), value)?, evaluate(context, index)?) {448 (Val::Obj(v), Val::Str(s)) => {449 let sn = s.clone();450 push(451 loc,452 || format!("field <{}> access", sn),453 || {454 if let Some(v) = v.get(s.clone())? {455 Ok(v)456 } else if v.get("__intrinsic_namespace__".into())?.is_some() {457 Ok(Val::Func(Rc::new(FuncVal::Intrinsic(s))))458 } else {459 throw!(NoSuchField(s))460 }461 },462 )?463 }464 (Val::Obj(_), n) => throw!(ValueIndexMustBeTypeGot(465 ValType::Obj,466 ValType::Str,467 n.value_type(),468 )),469470 (Val::Arr(v), Val::Num(n)) => {471 if n.fract() > f64::EPSILON {472 throw!(FractionalIndex)473 }474 v.get(n as usize)?475 .ok_or_else(|| ArrayBoundsError(n as usize, v.len()))?476 .clone()477 }478 (Val::Arr(_), Val::Str(n)) => throw!(AttemptedIndexAnArrayWithString(n)),479 (Val::Arr(_), n) => throw!(ValueIndexMustBeTypeGot(480 ValType::Arr,481 ValType::Num,482 n.value_type(),483 )),484485 (Val::Str(s), Val::Num(n)) => Val::Str(486 s.chars()487 .skip(n as usize)488 .take(1)489 .collect::<String>()490 .into(),491 ),492 (Val::Str(_), n) => throw!(ValueIndexMustBeTypeGot(493 ValType::Str,494 ValType::Num,495 n.value_type(),496 )),497498 (v, _) => throw!(CantIndexInto(v.value_type())),499 }500 }501 LocalExpr(bindings, returned) => {502 let mut new_bindings: HashMap<IStr, LazyBinding> = HashMap::new();503 let future_context = Context::new_future();504505 let context_creator = context_creator!(506 closure!(clone future_context, |_, _| Ok(future_context.clone().unwrap()))507 );508509 for (k, v) in bindings510 .iter()511 .map(|b| evaluate_binding(b, context_creator.clone()))512 {513 new_bindings.insert(k, v);514 }515516 let context = context517 .extend_unbound(new_bindings, None, None, None)?518 .into_future(future_context);519 evaluate(context, &returned.clone())?520 }521 Arr(items) => {522 let mut out = Vec::with_capacity(items.len());523 for item in items {524 out.push(LazyVal::new(Box::new(525 closure!(clone context, clone item, || {526 evaluate(context.clone(), &item)527 }),528 )));529 }530 Val::Arr(out.into())531 }532 ArrComp(expr, comp_specs) => Val::Arr(533 // First comp_spec should be for_spec, so no "None" possible here534 evaluate_comp(context, &|ctx| evaluate(ctx, expr), comp_specs)?535 .unwrap()536 .into(),537 ),538 Obj(body) => Val::Obj(evaluate_object(context, body)?),539 ObjExtend(s, t) => evaluate_add_op(540 &evaluate(context.clone(), s)?,541 &Val::Obj(evaluate_object(context, t)?),542 )?,543 Apply(value, args, tailstrict) => evaluate_apply(context, value, args, loc, *tailstrict)?,544 Function(params, body) => {545 evaluate_method(context, "anonymous".into(), params.clone(), body.clone())546 }547 Intrinsic(name) => Val::Func(Rc::new(FuncVal::Intrinsic(name.clone()))),548 AssertExpr(AssertStmt(value, msg), returned) => {549 let assertion_result = push(550 &value.1,551 || "assertion condition".to_owned(),552 || {553 evaluate(context.clone(), value)?554 .try_cast_bool("assertion condition should be of type `boolean`")555 },556 )?;557 if assertion_result {558 evaluate(context, returned)?559 } else {560 push(561 &value.1,562 || "assertion failure".to_owned(),563 || {564 if let Some(msg) = msg {565 throw!(AssertionFailed(evaluate(context, msg)?.to_string()?));566 } else {567 throw!(AssertionFailed(Val::Null.to_string()?));568 }569 },570 )?571 }572 }573 ErrorStmt(e) => push(574 loc,575 || "error statement".to_owned(),576 || {577 throw!(RuntimeError(578 evaluate(context, e)?.try_cast_str("error text should be of type `string`")?,579 ))580 },581 )?,582 IfElse {583 cond,584 cond_then,585 cond_else,586 } => {587 if push(588 loc,589 || "if condition".to_owned(),590 || evaluate(context.clone(), &cond.0)?.try_cast_bool("in if condition"),591 )? {592 evaluate(context, cond_then)?593 } else {594 match cond_else {595 Some(v) => evaluate(context, v)?,596 None => Val::Null,597 }598 }599 }600 Import(path) => {601 let mut tmp = loc602 .clone()603 .expect("imports cannot be used without loc_data")604 .0;605 let import_location = Rc::make_mut(&mut tmp);606 import_location.pop();607 push(608 loc,609 || format!("import {:?}", path),610 || with_state(|s| s.import_file(import_location, path)),611 )?612 }613 ImportStr(path) => {614 let mut tmp = loc615 .clone()616 .expect("imports cannot be used without loc_data")617 .0;618 let import_location = Rc::make_mut(&mut tmp);619 import_location.pop();620 Val::Str(with_state(|s| s.import_file_str(import_location, path))?)621 }622 Literal(LiteralType::Super) => throw!(StandaloneSuper),623 })624}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());
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/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/lib.rs
+++ b/crates/jrsonnet-evaluator/src/lib.rs
@@ -23,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::*;
@@ -63,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
@@ -102,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>,
}
@@ -144,7 +145,7 @@
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(),
@@ -169,7 +170,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(
@@ -183,7 +184,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())
}
@@ -205,7 +206,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)?;
@@ -257,7 +258,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(),
@@ -321,13 +322,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()))
}
@@ -371,7 +372,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 {
@@ -391,26 +392,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);
@@ -420,7 +421,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)?)
}
@@ -431,7 +432,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);
}
@@ -468,6 +469,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};
@@ -905,13 +907,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/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)]
@@ -33,7 +34,7 @@
}
let mut debug = f.debug_struct("ObjValue");
for (name, member) in self.0.this_entries.iter() {
- debug.field(name, member);
+ debug.field(&name, member);
}
#[cfg(feature = "unstable")]
{
@@ -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,10 @@
visible_fields.sort();
visible_fields
}
- pub fn get(&self, key: Rc<str>) -> Result<Option<Val>> {
+ 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: Option<&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);
crates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/val.rs
+++ b/crates/jrsonnet-evaluator/src/val.rs
@@ -9,6 +9,7 @@
native::NativeCallback,
throw, with_state, Context, ObjValue, Result,
};
+use jrsonnet_interner::IStr;
use jrsonnet_parser::{el, Arg, ArgsDesc, Expr, ExprLocation, LiteralType, LocExpr, ParamsDesc};
use jrsonnet_types::ValType;
use std::{cell::RefCell, collections::HashMap, fmt::Debug, rc::Rc};
@@ -61,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,
@@ -72,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 {
@@ -91,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(),
@@ -131,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 {
@@ -270,7 +271,7 @@
pub enum Val {
Bool(bool),
Null,
- Str(Rc<str>),
+ Str(IStr),
Num(f64),
Arr(ArrValue),
Obj(ObjValue),
@@ -314,7 +315,7 @@
self.assert_type(context, ValType::Bool)?;
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, Self::Str(v), v))
}
@@ -334,7 +335,7 @@
}
}
- pub fn to_string(&self) -> Result<Rc<str>> {
+ pub fn to_string(&self) -> Result<IStr> {
Ok(match self {
Self::Bool(true) => "true".into(),
Self::Bool(false) => "false".into(),
@@ -352,7 +353,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),
@@ -370,7 +371,7 @@
}
/// 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),
@@ -382,7 +383,7 @@
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 {
@@ -419,7 +420,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 {
@@ -471,7 +472,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()?
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) {
+ return 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)]
@@ -147,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)]
@@ -166,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)]
@@ -184,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)]
@@ -215,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)]
@@ -227,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)]
@@ -236,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)]
@@ -257,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)]
@@ -265,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>),
@@ -316,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,
@@ -326,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)]
@@ -338,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)]