difftreelog
perf lesser and cheaper clones
in: master
11 files changed
cmds/jrsonnet/src/main.rsdiffbeforeafterboth--- a/cmds/jrsonnet/src/main.rs
+++ b/cmds/jrsonnet/src/main.rs
@@ -5,7 +5,7 @@
use jsonnet_parser::{el, Arg, ArgsDesc, Expr, LocExpr, ParserSettings};
use location::{offset_to_location, CodeLocation};
use std::env::current_dir;
-use std::{collections::HashMap, path::PathBuf, str::FromStr};
+use std::{collections::HashMap, path::PathBuf, str::FromStr, rc::Rc};
enum Format {
None,
@@ -128,10 +128,10 @@
evaluator.with_stdlib();
}
for ExtStr { name, value } in opts.ext_str.iter().cloned() {
- evaluator.add_ext_var(name, Val::Str(value));
+ evaluator.add_ext_var(name.into(), Val::Str(value.into()));
}
for ExtStr { name, value } in opts.ext_code.iter().cloned() {
- evaluator.add_ext_var(name, evaluator.parse_evaluate_raw(&value).unwrap());
+ evaluator.add_ext_var(name.into(), evaluator.parse_evaluate_raw(&value).unwrap());
}
let mut input = current_dir().unwrap();
input.push(opts.input.clone());
@@ -147,7 +147,7 @@
Val::Func(f) => {
let mut desc_map = HashMap::new();
for ExtStr { name, value } in opts.tla_str.iter().cloned() {
- desc_map.insert(name, el!(Expr::Str(value)));
+ desc_map.insert(name, el!(Expr::Str(value.into())));
}
for ExtStr { name, value } in opts.tla_code.iter().cloned() {
desc_map.insert(
@@ -155,17 +155,17 @@
jsonnet_parser::parse(
&value,
&ParserSettings {
- file_name: PathBuf::new(),
+ file_name: Rc::new(PathBuf::new()),
loc_data: false,
},
)
.unwrap(),
);
}
- evaluator.add_global("__tmp__tlf__".to_owned(), Val::Func(f));
+ evaluator.add_global("__tmp__tlf__".into(), Val::Func(f));
evaluator
.evaluate_raw(el!(Expr::Apply(
- el!(Expr::Var("__tmp__tlf__".to_owned())),
+ el!(Expr::Var("__tmp__tlf__".into())),
ArgsDesc(desc_map.into_iter().map(|(k, v)| Arg(Some(k), v)).collect()),
false,
)))
@@ -178,7 +178,7 @@
if opts.no_stdlib {
evaluator.with_stdlib();
}
- evaluator.add_global("__tmp__to_json__".to_owned(), v);
+ evaluator.add_global("__tmp__to_json__".into(), v);
let v = evaluator.parse_evaluate_raw(&format!(
"std.manifestJsonEx(__tmp__to_json__, \"{}\")",
" ".repeat(opts.line_padding),
@@ -195,7 +195,7 @@
if opts.no_stdlib {
evaluator.with_stdlib();
}
- evaluator.add_global("__tmp__to_yaml__".to_owned(), v);
+ evaluator.add_global("__tmp__to_yaml__".into(), v);
let v = evaluator
.parse_evaluate_raw("std.manifestYamlDoc(__tmp__to_yaml__, \" \")");
match v {
crates/jsonnet-evaluator/build.rsdiffbeforeafterboth--- a/crates/jsonnet-evaluator/build.rs
+++ b/crates/jsonnet-evaluator/build.rs
@@ -34,7 +34,7 @@
Member::Field(FieldMember {
name: FieldName::Fixed(name),
..
- }) if name == "join"
+ }) if **name == *"join"
)
})
.collect(),
crates/jsonnet-evaluator/src/ctx.rsdiffbeforeafterboth--- a/crates/jsonnet-evaluator/src/ctx.rs
+++ b/crates/jsonnet-evaluator/src/ctx.rs
@@ -16,7 +16,7 @@
dollar: Option<ObjValue>,
this: Option<ObjValue>,
super_obj: Option<ObjValue>,
- bindings: LayeredHashMap<String, LazyVal>,
+ bindings: LayeredHashMap<Rc<str>, LazyVal>,
}
pub struct Context(Rc<ContextInternals>);
impl Debug for Context {
@@ -53,9 +53,9 @@
}))
}
- pub fn binding(&self, name: &str) -> Result<LazyVal> {
- self.0.bindings.get(name).cloned().ok_or_else(|| {
- create_error::<()>(Error::UnknownVariable(name.to_owned()))
+ pub fn binding(&self, name: Rc<str>) -> Result<LazyVal> {
+ self.0.bindings.get(&name).cloned().ok_or_else(|| {
+ create_error::<()>(Error::UnknownVariable(name))
.err()
.unwrap()
})
@@ -67,7 +67,7 @@
ctx.unwrap()
}
- pub fn with_var(&self, name: String, value: Val) -> Result<Context> {
+ pub fn with_var(&self, name: Rc<str>, value: Val) -> Result<Context> {
let mut new_bindings = HashMap::with_capacity(1);
new_bindings.insert(name, resolved_lazy_val!(value));
self.extend(new_bindings, None, None, None)
@@ -75,7 +75,7 @@
pub fn extend(
&self,
- new_bindings: HashMap<String, LazyVal>,
+ new_bindings: HashMap<Rc<str>, LazyVal>,
new_dollar: Option<ObjValue>,
new_this: Option<ObjValue>,
new_super_obj: Option<ObjValue>,
@@ -97,7 +97,7 @@
}
pub fn extend_unbound(
&self,
- new_bindings: HashMap<String, LazyBinding>,
+ new_bindings: HashMap<Rc<str>, LazyBinding>,
new_dollar: Option<ObjValue>,
new_this: Option<ObjValue>,
new_super_obj: Option<ObjValue>,
crates/jsonnet-evaluator/src/error.rsdiffbeforeafterboth--- a/crates/jsonnet-evaluator/src/error.rs
+++ b/crates/jsonnet-evaluator/src/error.rs
@@ -6,20 +6,20 @@
pub enum Error {
VariableIsNotDefined(String),
TypeMismatch(&'static str, Vec<ValType>, ValType),
- NoSuchField(String),
+ NoSuchField(Rc<str>),
- UnknownVariable(String),
+ UnknownVariable(Rc<str>),
UnknownFunctionParameter(String),
- BindingParameterASecondTime(String),
+ BindingParameterASecondTime(Rc<str>),
TooManyArgsFunctionHas(usize),
- FunctionParameterNotBoundInCall(String),
+ FunctionParameterNotBoundInCall(Rc<str>),
- UndefinedExternalVariable(String),
+ UndefinedExternalVariable(Rc<str>),
FieldMustBeStringGot(ValType),
- AttemptedIndexAnArrayWithString(String),
+ AttemptedIndexAnArrayWithString(Rc<str>),
ValueIndexMustBeTypeGot(ValType, ValType, ValType),
CantIndexInto(ValType),
@@ -31,7 +31,7 @@
ImportNotSupported(PathBuf, PathBuf),
ImportSyntaxError(jsonnet_parser::ParseError),
- RuntimeError(String),
+ RuntimeError(Rc<str>),
StackOverflow,
FractionalIndex,
DivisionByZero,
crates/jsonnet-evaluator/src/evaluate.rsdiffbeforeafterboth1use crate::{2 context_creator, create_error, future_wrapper, lazy_val, push, with_state, Context,3 ContextCreator, Error, FuncDesc, LazyBinding, LazyVal, ObjMember, ObjValue, Result, Val,4 ValType,5};6use closure::closure;7use jsonnet_parser::{8 el, Arg, ArgsDesc, AssertStmt, BinaryOpType, BindSpec, CompSpec, Expr, FieldMember,9 ForSpecData, IfSpecData, LiteralType, LocExpr, Member, ObjBody, ParamsDesc, UnaryOpType,10 Visibility,11};12use std::{13 collections::{BTreeMap, HashMap},14 rc::Rc,15};1617pub fn evaluate_binding(b: &BindSpec, context_creator: ContextCreator) -> (String, LazyBinding) {18 let b = b.clone();19 if let Some(params) = &b.params {20 let params = params.clone();21 (22 b.name.clone(),23 LazyBinding::Bindable(Rc::new(move |this, super_obj| {24 Ok(lazy_val!(25 closure!(clone b, clone params, clone context_creator, || Ok(evaluate_method(26 context_creator.0(this.clone(), super_obj.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 push(&b.value.1, "thunk", ||{39 evaluate(40 context_creator.0(this.clone(), super_obj.clone())?,41 &b.value42 )43 })44 )))45 })),46 )47 }48}4950pub fn evaluate_method(ctx: Context, params: ParamsDesc, body: LocExpr) -> Val {51 Val::Func(FuncDesc { ctx, params, body })52}5354pub fn evaluate_field_name(55 context: Context,56 field_name: &jsonnet_parser::FieldName,57) -> Result<Option<String>> {58 Ok(match field_name {59 jsonnet_parser::FieldName::Fixed(n) => Some(n.clone()),60 jsonnet_parser::FieldName::Dyn(expr) => {61 let value = evaluate(context, expr)?.unwrap_if_lazy()?;62 if matches!(value, Val::Null) {63 None64 } else {65 Some(value.try_cast_str("dynamic field name")?)66 }67 }68 })69}7071pub fn evaluate_unary_op(op: UnaryOpType, b: &Val) -> Result<Val> {72 Ok(match (op, b) {73 (o, Val::Lazy(l)) => evaluate_unary_op(o, &l.evaluate()?)?,74 (UnaryOpType::Not, Val::Bool(v)) => Val::Bool(!v),75 (UnaryOpType::Minus, Val::Num(n)) => Val::Num(-*n),76 (UnaryOpType::BitNot, Val::Num(n)) => Val::Num(!(*n as i32) as f64),77 (op, o) => panic!("unary op not implemented: {:?} {:?}", op, o),78 })79}8081pub(crate) fn evaluate_add_op(a: &Val, b: &Val) -> Result<Val> {82 Ok(match (a, b) {83 (Val::Str(v1), Val::Str(v2)) => Val::Str(v1.to_owned() + &v2),8485 // Can't use generic json serialization way, because it depends on number to string concatenation (std.jsonnet:890)86 (Val::Num(n), Val::Str(o)) => Val::Str(format!("{}{}", n, o)),87 (Val::Str(o), Val::Num(n)) => Val::Str(format!("{}{}", o, n)),8889 (Val::Str(s), o) => Val::Str(format!("{}{}", s, o.clone().into_json(0)?)),90 (o, Val::Str(s)) => Val::Str(format!("{}{}", o.clone().into_json(0)?, s)),9192 (Val::Obj(v1), Val::Obj(v2)) => Val::Obj(v2.with_super(v1.clone())),93 (Val::Arr(a), Val::Arr(b)) => Val::Arr([&a[..], &b[..]].concat()),94 (Val::Num(v1), Val::Num(v2)) => Val::Num(v1 + v2),95 _ => panic!("can't add: {:?} and {:?}", a, b),96 })97}9899pub fn evaluate_binary_op_special(100 context: Context,101 a: &LocExpr,102 op: BinaryOpType,103 b: &LocExpr,104) -> Result<Val> {105 Ok(106 match (evaluate(context.clone(), &a)?.unwrap_if_lazy()?, op, b) {107 (Val::Bool(true), BinaryOpType::Or, _o) => Val::Bool(true),108 (Val::Bool(false), BinaryOpType::And, _o) => Val::Bool(false),109 (a, op, eb) => {110 evaluate_binary_op_normal(&a, op, &evaluate(context, eb)?.unwrap_if_lazy()?)?111 }112 },113 )114}115116pub fn evaluate_binary_op_normal(a: &Val, op: BinaryOpType, b: &Val) -> Result<Val> {117 Ok(match (a, op, b) {118 (a, BinaryOpType::Add, b) => evaluate_add_op(a, b)?,119120 (Val::Str(v1), BinaryOpType::Mul, Val::Num(v2)) => Val::Str(v1.repeat(*v2 as usize)),121122 // Bool X Bool123 (Val::Bool(a), BinaryOpType::And, Val::Bool(b)) => Val::Bool(*a && *b),124 (Val::Bool(a), BinaryOpType::Or, Val::Bool(b)) => Val::Bool(*a || *b),125126 // Str X Str127 (Val::Str(v1), BinaryOpType::Lt, Val::Str(v2)) => Val::Bool(v1 < v2),128 (Val::Str(v1), BinaryOpType::Gt, Val::Str(v2)) => Val::Bool(v1 > v2),129 (Val::Str(v1), BinaryOpType::Lte, Val::Str(v2)) => Val::Bool(v1 <= v2),130 (Val::Str(v1), BinaryOpType::Gte, Val::Str(v2)) => Val::Bool(v1 >= v2),131132 // Num X Num133 (Val::Num(v1), BinaryOpType::Mul, Val::Num(v2)) => Val::Num(v1 * v2),134 (Val::Num(v1), BinaryOpType::Div, Val::Num(v2)) => {135 if *v2 <= f64::EPSILON {136 create_error(crate::Error::DivisionByZero)?137 }138 Val::Num(v1 / v2)139 }140141 (Val::Num(v1), BinaryOpType::Sub, Val::Num(v2)) => Val::Num(v1 - v2),142143 (Val::Num(v1), BinaryOpType::Lt, Val::Num(v2)) => Val::Bool(v1 < v2),144 (Val::Num(v1), BinaryOpType::Gt, Val::Num(v2)) => Val::Bool(v1 > v2),145 (Val::Num(v1), BinaryOpType::Lte, Val::Num(v2)) => Val::Bool(v1 <= v2),146 (Val::Num(v1), BinaryOpType::Gte, Val::Num(v2)) => Val::Bool(v1 >= v2),147148 (Val::Num(v1), BinaryOpType::BitAnd, Val::Num(v2)) => {149 Val::Num(((*v1 as i32) & (*v2 as i32)) as f64)150 }151 (Val::Num(v1), BinaryOpType::BitOr, Val::Num(v2)) => {152 Val::Num(((*v1 as i32) | (*v2 as i32)) as f64)153 }154 (Val::Num(v1), BinaryOpType::BitXor, Val::Num(v2)) => {155 Val::Num(((*v1 as i32) ^ (*v2 as i32)) as f64)156 }157 (Val::Num(v1), BinaryOpType::Lhs, Val::Num(v2)) => {158 Val::Num(((*v1 as i32) << (*v2 as i32)) as f64)159 }160 (Val::Num(v1), BinaryOpType::Rhs, Val::Num(v2)) => {161 Val::Num(((*v1 as i32) >> (*v2 as i32)) as f64)162 }163164 _ => panic!("no rules for binary operation: {:?} {:?} {:?}", a, op, b),165 })166}167168future_wrapper!(HashMap<String, LazyBinding>, FutureNewBindings);169future_wrapper!(ObjValue, FutureObjValue);170171pub fn evaluate_comp<T>(172 context: Context,173 value: &impl Fn(Context) -> Result<T>,174 specs: &[CompSpec],175) -> Result<Option<Vec<T>>> {176 Ok(match specs.get(0) {177 None => Some(vec![value(context)?]),178 Some(CompSpec::IfSpec(IfSpecData(cond))) => {179 if evaluate(context.clone(), &cond)?.try_cast_bool("if spec")? {180 evaluate_comp(context, value, &specs[1..])?181 } else {182 None183 }184 }185 Some(CompSpec::ForSpec(ForSpecData(var, expr))) => {186 match evaluate(context.clone(), &expr)?.unwrap_if_lazy()? {187 Val::Arr(list) => {188 let mut out = Vec::new();189 for item in list {190 let item = item.clone().unwrap_if_lazy()?;191 out.push(evaluate_comp(192 context.with_var(var.clone(), item)?,193 value,194 &specs[1..],195 )?);196 }197 Some(out.into_iter().flatten().flatten().collect())198 }199 _ => panic!("for expression evaluated to non-iterable value"),200 }201 }202 })203}204205// TODO: Asserts206pub fn evaluate_object(context: Context, object: ObjBody) -> Result<ObjValue> {207 Ok(match object {208 ObjBody::MemberList(members) => {209 let new_bindings = FutureNewBindings::new();210 let future_this = FutureObjValue::new();211 let context_creator = context_creator!(212 closure!(clone context, clone new_bindings, |this: Option<ObjValue>, super_obj: Option<ObjValue>| {213 Ok(context.extend_unbound(214 new_bindings.clone().unwrap(),215 context.dollar().clone().or_else(||this.clone()),216 Some(this.unwrap()),217 super_obj218 )?)219 })220 );221 {222 let mut bindings: HashMap<String, LazyBinding> = HashMap::new();223 for (n, b) in members224 .iter()225 .filter_map(|m| match m {226 Member::BindStmt(b) => Some(b.clone()),227 _ => None,228 })229 .map(|b| evaluate_binding(&b, context_creator.clone()))230 {231 bindings.insert(n, b);232 }233 new_bindings.fill(bindings);234 }235236 let mut new_members = BTreeMap::new();237 for member in members.into_iter() {238 match member {239 Member::Field(FieldMember {240 name,241 plus,242 params: None,243 visibility,244 value,245 }) => {246 let name = evaluate_field_name(context.clone(), &name)?;247 if name.is_none() {248 continue;249 }250 let name = name.unwrap();251 new_members.insert(252 name.clone(),253 ObjMember {254 add: plus,255 visibility: visibility.clone(),256 invoke: LazyBinding::Bindable(Rc::new(257 closure!(clone name, clone value, clone context_creator, |this, super_obj| {258 Ok(LazyVal::new_resolved(push(&value.1, "object field", ||{259 let context = context_creator.0(this, super_obj)?;260 evaluate(261 context,262 &value,263 )?.unwrap_if_lazy()264 })?))265 }),266 )),267 },268 );269 }270 Member::Field(FieldMember {271 name,272 params: Some(params),273 value,274 ..275 }) => {276 let name = evaluate_field_name(context.clone(), &name)?;277 if name.is_none() {278 continue;279 }280 let name = name.unwrap();281 new_members.insert(282 name,283 ObjMember {284 add: false,285 visibility: Visibility::Hidden,286 invoke: LazyBinding::Bindable(Rc::new(287 closure!(clone value, clone context_creator, |this, super_obj| {288 // TODO: Assert289 Ok(LazyVal::new_resolved(evaluate_method(290 context_creator.0(this, super_obj)?,291 params.clone(),292 value.clone(),293 )))294 }),295 )),296 },297 );298 }299 Member::BindStmt(_) => {}300 Member::AssertStmt(_) => {}301 }302 }303 future_this.fill(ObjValue::new(None, Rc::new(new_members)))304 }305 ObjBody::ObjComp {306 pre_locals,307 key,308 value,309 post_locals,310 compspecs,311 } => {312 let future_this = FutureObjValue::new();313 let mut new_members = BTreeMap::new();314 for (k, v) in evaluate_comp(315 context.clone(),316 &|ctx| {317 let new_bindings = FutureNewBindings::new();318 let context_creator = context_creator!(319 closure!(clone context, clone new_bindings, |this: Option<ObjValue>, super_obj: Option<ObjValue>| {320 Ok(context.extend_unbound(321 new_bindings.clone().unwrap(),322 context.dollar().clone().or_else(||this.clone()),323 None,324 super_obj325 )?)326 })327 );328 let mut bindings: HashMap<String, LazyBinding> = HashMap::new();329 for (n, b) in pre_locals330 .iter()331 .chain(post_locals.iter())332 .map(|b| evaluate_binding(b, context_creator.clone()))333 {334 bindings.insert(n, b);335 }336 let bindings = new_bindings.fill(bindings);337 let ctx = ctx.extend_unbound(bindings, None, None, None)?;338 let key = evaluate(ctx.clone(), &key)?;339 let value = LazyBinding::Bindable(Rc::new(340 closure!(clone ctx, clone value, |this, _super_obj| {341 Ok(LazyVal::new_resolved(evaluate(ctx.extend(HashMap::new(), None, this, None)?, &value)?))342 }),343 ));344345 Ok((key, value))346 },347 &compspecs,348 )?349 .unwrap()350 {351 match k {352 Val::Null => {}353 Val::Str(n) => {354 new_members.insert(355 n,356 ObjMember {357 add: false,358 visibility: Visibility::Normal,359 invoke: v,360 },361 );362 }363 v => create_error(Error::FieldMustBeStringGot(v.value_type()?))?,364 }365 }366367 future_this.fill(ObjValue::new(None, Rc::new(new_members)))368 }369 })370}371372pub fn evaluate(context: Context, expr: &LocExpr) -> Result<Val> {373 use Expr::*;374 let LocExpr(expr, loc) = expr;375 Ok(match &**expr {376 Literal(LiteralType::This) => Val::Obj(377 context378 .this()379 .clone()380 .unwrap_or_else(|| panic!("this not found")),381 ),382 Literal(LiteralType::Dollar) => Val::Obj(383 context384 .dollar()385 .clone()386 .unwrap_or_else(|| panic!("dollar not found")),387 ),388 Literal(LiteralType::True) => Val::Bool(true),389 Literal(LiteralType::False) => Val::Bool(false),390 Literal(LiteralType::Null) => Val::Null,391 Parened(e) => evaluate(context, e)?,392 Str(v) => Val::Str(v.clone()),393 Num(v) => Val::Num(*v),394 BinaryOp(v1, o, v2) => evaluate_binary_op_special(context, &v1, *o, &v2)?,395 UnaryOp(o, v) => evaluate_unary_op(*o, &evaluate(context, v)?)?,396 Var(name) => push(loc, "var", || {397 Val::Lazy(context.binding(&name)?).unwrap_if_lazy()398 })?,399 Index(LocExpr(v, _), index) if matches!(&**v, Expr::Literal(LiteralType::Super)) => {400 let name = evaluate(context.clone(), index)?.try_cast_str("object index")?;401 context402 .super_obj()403 .clone()404 .expect("no super found")405 .get_raw(&name, &context.this().clone().expect("no this found"))?406 .expect("value not found")407 }408 Index(value, index) => {409 match (410 evaluate(context.clone(), value)?.unwrap_if_lazy()?,411 evaluate(context, index)?,412 ) {413 (Val::Obj(v), Val::Str(s)) => {414 if let Some(v) = v.get(&s)? {415 v.unwrap_if_lazy()?416 } else if let Some(Val::Str(n)) = v.get("__intristic_namespace__")? {417 Val::Intristic(n, s)418 } else {419 create_error(crate::Error::NoSuchField(s))?420 }421 }422 (Val::Obj(_), n) => create_error(crate::Error::ValueIndexMustBeTypeGot(423 ValType::Obj,424 ValType::Str,425 n.value_type()?,426 ))?,427428 (Val::Arr(v), Val::Num(n)) => {429 if n.fract() > f64::EPSILON {430 create_error(crate::Error::FractionalIndex)?431 }432 v.get(n as usize)433 .unwrap_or_else(|| panic!("out of bounds"))434 .clone()435 .unwrap_if_lazy()?436 }437 (Val::Arr(_), Val::Str(n)) => {438 create_error(crate::Error::AttemptedIndexAnArrayWithString(n))?439 }440 (Val::Arr(_), n) => create_error(crate::Error::ValueIndexMustBeTypeGot(441 ValType::Arr,442 ValType::Num,443 n.value_type()?,444 ))?,445446 (Val::Str(s), Val::Num(n)) => {447 Val::Str(s.chars().skip(n as usize).take(1).collect())448 }449 (Val::Str(_), n) => create_error(crate::Error::ValueIndexMustBeTypeGot(450 ValType::Str,451 ValType::Num,452 n.value_type()?,453 ))?,454455 (v, _) => create_error(crate::Error::CantIndexInto(v.value_type()?))?,456 }457 }458 LocalExpr(bindings, returned) => {459 let mut new_bindings: HashMap<String, LazyBinding> = HashMap::new();460 let future_context = Context::new_future();461462 let context_creator = context_creator!(463 closure!(clone future_context, |_, _| Ok(future_context.clone().unwrap()))464 );465466 for (k, v) in bindings467 .iter()468 .map(|b| evaluate_binding(b, context_creator.clone()))469 {470 new_bindings.insert(k, v);471 }472473 let context = context474 .extend_unbound(new_bindings, None, None, None)?475 .into_future(future_context);476 evaluate(context, &returned.clone())?477 }478 Arr(items) => {479 let mut out = Vec::with_capacity(items.len());480 for item in items {481 out.push(Val::Lazy(lazy_val!(482 closure!(clone context, clone item, || {483 evaluate(context.clone(), &item)484 })485 )));486 }487 Val::Arr(out)488 }489 ArrComp(expr, compspecs) => Val::Arr(490 // First compspec should be forspec, so no "None" possible here491 evaluate_comp(context, &|ctx| evaluate(ctx, expr), compspecs)?.unwrap(),492 ),493 Obj(body) => Val::Obj(evaluate_object(context, body.clone())?),494 ObjExtend(s, t) => evaluate_add_op(495 &evaluate(context.clone(), s)?,496 &Val::Obj(evaluate_object(context, t.clone())?),497 )?,498 Apply(value, args, tailstrict) => {499 let value = evaluate(context.clone(), value)?.unwrap_if_lazy()?;500 match value {501 Val::Intristic(ns, name) => match (&ns as &str, &name as &str) {502 // arr/string/function503 ("std", "length") => {504 assert_eq!(args.len(), 1);505 let expr = &args.get(0).unwrap().1;506 match evaluate(context, expr)? {507 Val::Str(n) => Val::Num(n.chars().count() as f64),508 Val::Arr(i) => Val::Num(i.len() as f64),509 Val::Obj(o) => Val::Num(510 o.fields_visibility()511 .into_iter()512 .filter(|(_k, v)| *v)513 .count() as f64,514 ),515 v => panic!("can't get length of {:?}", v),516 }517 }518 // any519 ("std", "type") => {520 assert_eq!(args.len(), 1);521 let expr = &args.get(0).unwrap().1;522 Val::Str(evaluate(context, expr)?.value_type()?.name().to_owned())523 }524 // length, idx=>any525 ("std", "makeArray") => {526 assert_eq!(args.len(), 2);527 if let (Val::Num(v), Val::Func(d)) = (528 evaluate(context.clone(), &args[0].1)?,529 evaluate(context, &args[1].1)?,530 ) {531 assert!(v >= 0.0);532 let mut out = Vec::with_capacity(v as usize);533 for i in 0..v as usize {534 let call_ctx =535 Context::new().with_var("v".to_owned(), Val::Num(i as f64))?;536 out.push(d.evaluate(537 call_ctx,538 &ArgsDesc(vec![Arg(None, el!(Expr::Var("v".to_owned())))]),539 true,540 )?)541 }542 Val::Arr(out)543 } else {544 panic!("bad makeArray call");545 }546 }547 // string548 ("std", "codepoint") => {549 assert_eq!(args.len(), 1);550 if let Val::Str(s) = evaluate(context, &args[0].1)? {551 assert!(552 s.chars().count() == 1,553 "std.codepoint should receive single char string"554 );555 Val::Num(s.chars().take(1).next().unwrap() as u32 as f64)556 } else {557 panic!("bad codepoint call");558 }559 }560 // object, includeHidden561 ("std", "objectFieldsEx") => {562 assert_eq!(args.len(), 2);563 if let (Val::Obj(body), Val::Bool(include_hidden)) = (564 evaluate(context.clone(), &args[0].1)?,565 evaluate(context, &args[1].1)?,566 ) {567 Val::Arr(568 body.fields_visibility()569 .into_iter()570 .filter(|(_k, v)| *v || include_hidden)571 .map(|(k, _v)| Val::Str(k))572 .collect(),573 )574 } else {575 panic!("bad objectFieldsEx call");576 }577 }578 // object, field, includeHidden579 ("std", "objectHasEx") => {580 assert_eq!(args.len(), 3);581 if let (Val::Obj(body), Val::Str(name), Val::Bool(include_hidden)) = (582 evaluate(context.clone(), &args[0].1)?,583 evaluate(context.clone(), &args[1].1)?,584 evaluate(context, &args[2].1)?,585 ) {586 Val::Bool(587 body.fields_visibility()588 .into_iter()589 .filter(|(_k, v)| *v || include_hidden)590 .any(|(k, _v)| k == name),591 )592 } else {593 panic!("bad objectHasEx call");594 }595 }596 ("std", "primitiveEquals") => {597 assert_eq!(args.len(), 2);598 let (a, b) = (599 evaluate(context.clone(), &args[0].1)?,600 evaluate(context, &args[1].1)?,601 );602 Val::Bool(a == b)603 }604 ("std", "modulo") => {605 assert_eq!(args.len(), 2);606 if let (Val::Num(a), Val::Num(b)) = (607 evaluate(context.clone(), &args[0].1)?,608 evaluate(context, &args[1].1)?,609 ) {610 Val::Num(a % b)611 } else {612 panic!("bad modulo call");613 }614 }615 ("std", "floor") => {616 assert_eq!(args.len(), 1);617 if let Val::Num(a) = evaluate(context, &args[0].1)? {618 Val::Num(a.floor())619 } else {620 panic!("bad floor call");621 }622 }623 ("std", "trace") => {624 assert_eq!(args.len(), 2);625 if let (Val::Str(a), b) = (626 evaluate(context.clone(), &args[0].1)?,627 evaluate(context, &args[1].1)?,628 ) {629 // TODO: Line numbers as in original jsonnet630 println!("TRACE: {}", a);631 b632 } else {633 panic!("bad trace call");634 }635 }636 ("std", "pow") => {637 assert_eq!(args.len(), 2);638 if let (Val::Num(a), Val::Num(b)) = (639 evaluate(context.clone(), &args[0].1)?,640 evaluate(context, &args[1].1)?,641 ) {642 Val::Num(a.powf(b))643 } else {644 panic!("bad pow call");645 }646 }647 ("std", "extVar") => {648 assert_eq!(args.len(), 1);649 if let Val::Str(a) = evaluate(context, &args[0].1)? {650 with_state(|s| s.0.ext_vars.borrow().get(&a).cloned()).ok_or_else(651 || {652 create_error::<()>(crate::Error::UndefinedExternalVariable(a))653 .err()654 .unwrap()655 },656 )?657 } else {658 panic!("bad extVar call");659 }660 }661 ("std", "filter") => {662 assert_eq!(args.len(), 2);663 if let (Val::Func(predicate), Val::Arr(arr)) = (664 evaluate(context.clone(), &args[0].1)?,665 evaluate(context.clone(), &args[1].1)?,666 ) {667 Val::Arr(668 arr.into_iter()669 .filter(|e| {670 predicate671 .evaluate_values(context.clone(), &[e.clone()])672 .unwrap()673 .try_cast_bool("filter predicate")674 .unwrap()675 })676 .collect(),677 )678 } else {679 panic!("bad filter call");680 }681 }682 // faster683 ("std", "join") => {684 assert_eq!(args.len(), 2);685 let joiner = evaluate(context.clone(), &args[0].1)?.unwrap_if_lazy()?;686 let items = evaluate(context, &args[1].1)?.unwrap_if_lazy()?;687 match (joiner, items) {688 (Val::Arr(joiner_items), Val::Arr(items)) => {689 // TODO: Minimal size should be known690 let mut out = Vec::new();691692 let mut first = true;693 for item in items {694 if let Val::Arr(items) = item.unwrap_if_lazy()? {695 if !first {696 out.extend(joiner_items.iter().cloned());697 }698 first = false;699 out.extend(items);700 } else {701 panic!("all array items should be arrays")702 }703 }704705 Val::Arr(out)706 }707 (Val::Str(joiner), Val::Arr(items)) => {708 let mut out = String::new();709710 let mut first = true;711 for item in items {712 if let Val::Str(item) = item.unwrap_if_lazy()? {713 if !first {714 out += &joiner;715 }716 first = false;717 out += &item;718 } else {719 panic!("all array items should be strings")720 }721 }722723 Val::Str(out)724 }725 (joiner, items) => panic!("bad join call: {:?} {:?}", joiner, items),726 }727 }728 (ns, name) => panic!("Intristic not found: {}.{}", ns, name),729 },730 Val::Func(f) => {731 let body = || f.evaluate(context, args, *tailstrict);732 if *tailstrict {733 body()?734 } else {735 push(loc, "function call", body)?736 }737 }738 _ => panic!("{:?} is not a function", value),739 }740 }741 Function(params, body) => evaluate_method(context, params.clone(), body.clone()),742 AssertExpr(AssertStmt(value, msg), returned) => {743 let assertion_result = push(&value.1, "assertion condition", || {744 evaluate(context.clone(), &value)?745 .try_cast_bool("assertion condition should be boolean")746 })?;747 if assertion_result {748 evaluate(context, returned)?749 } else if let Some(msg) = msg {750 panic!(751 "assertion failed ({:?}): {}",752 value,753 evaluate(context, msg)?.try_cast_str("assertion message should be string")?754 );755 } else {756 panic!("assertion failed ({:?}): no message", value);757 }758 }759 Error(e) => create_error(crate::Error::RuntimeError(760 evaluate(context, e)?.try_cast_str("error text should be string")?,761 ))?,762 IfElse {763 cond,764 cond_then,765 cond_else,766 } => {767 if evaluate(context.clone(), &cond.0)?768 .try_cast_bool("if condition should be boolean")?769 {770 evaluate(context, cond_then)?771 } else {772 match cond_else {773 Some(v) => evaluate(context, v)?,774 None => Val::Null,775 }776 }777 }778 Import(path) => {779 let mut import_location = loc780 .clone()781 .expect("imports can't be used without loc_data")782 .0783 .clone();784 import_location.pop();785 with_state(|s| s.import_file(&import_location, path))?786 }787 ImportStr(path) => {788 let mut import_location = loc789 .clone()790 .expect("imports can't be used without loc_data")791 .0792 .clone();793 import_location.pop();794 Val::Str(with_state(|s| s.import_file_str(&import_location, path))?)795 }796 Literal(LiteralType::Super) => return create_error(crate::error::Error::StandaloneSuper),797 })798}1use crate::{2 context_creator, create_error, future_wrapper, lazy_val, push, with_state, Context,3 ContextCreator, Error, FuncDesc, LazyBinding, LazyVal, ObjMember, ObjValue, Result, Val,4 ValType,5};6use closure::closure;7use jsonnet_parser::{8 el, Arg, ArgsDesc, AssertStmt, BinaryOpType, BindSpec, CompSpec, Expr, FieldMember,9 ForSpecData, IfSpecData, LiteralType, LocExpr, Member, ObjBody, ParamsDesc, UnaryOpType,10 Visibility,11};12use std::{13 collections::{BTreeMap, HashMap},14 rc::Rc,15};1617pub fn evaluate_binding(b: &BindSpec, context_creator: ContextCreator) -> (Rc<str>, LazyBinding) {18 let b = b.clone();19 if let Some(params) = &b.params {20 let params = params.clone();21 (22 b.name.clone(),23 LazyBinding::Bindable(Rc::new(move |this, super_obj| {24 Ok(lazy_val!(25 closure!(clone b, clone params, clone context_creator, || Ok(evaluate_method(26 context_creator.0(this.clone(), super_obj.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 push(&b.value.1, "thunk", ||{39 evaluate(40 context_creator.0(this.clone(), super_obj.clone())?,41 &b.value42 )43 })44 )))45 })),46 )47 }48}4950pub fn evaluate_method(ctx: Context, params: ParamsDesc, body: LocExpr) -> Val {51 Val::Func(FuncDesc { ctx, params, body })52}5354pub fn evaluate_field_name(55 context: Context,56 field_name: &jsonnet_parser::FieldName,57) -> Result<Option<Rc<str>>> {58 Ok(match field_name {59 jsonnet_parser::FieldName::Fixed(n) => Some(n.clone()),60 jsonnet_parser::FieldName::Dyn(expr) => {61 let lazy = evaluate(context, expr)?;62 let value = lazy.unwrap_if_lazy()?;63 if matches!(value, Val::Null) {64 None65 } else {66 Some(value.try_cast_str("dynamic field name")?)67 }68 }69 })70}7172pub fn evaluate_unary_op(op: UnaryOpType, b: &Val) -> Result<Val> {73 Ok(match (op, b) {74 (o, Val::Lazy(l)) => evaluate_unary_op(o, &l.evaluate()?)?,75 (UnaryOpType::Not, Val::Bool(v)) => Val::Bool(!v),76 (UnaryOpType::Minus, Val::Num(n)) => Val::Num(-*n),77 (UnaryOpType::BitNot, Val::Num(n)) => Val::Num(!(*n as i32) as f64),78 (op, o) => panic!("unary op not implemented: {:?} {:?}", op, o),79 })80}8182pub(crate) fn evaluate_add_op(a: &Val, b: &Val) -> Result<Val> {83 Ok(match (a, b) {84 (Val::Str(v1), Val::Str(v2)) => Val::Str(((**v1).to_owned() + &v2).into()),8586 // Can't use generic json serialization way, because it depends on number to string concatenation (std.jsonnet:890)87 (Val::Num(n), Val::Str(o)) => Val::Str(format!("{}{}", n, o).into()),88 (Val::Str(o), Val::Num(n)) => Val::Str(format!("{}{}", o, n).into()),8990 (Val::Str(s), o) => Val::Str(format!("{}{}", s, o.clone().into_json(0)?).into()),91 (o, Val::Str(s)) => Val::Str(format!("{}{}", o.clone().into_json(0)?, s).into()),9293 (Val::Obj(v1), Val::Obj(v2)) => Val::Obj(v2.with_super(v1.clone())),94 (Val::Arr(a), Val::Arr(b)) => Val::Arr(Rc::new([&a[..], &b[..]].concat())),95 (Val::Num(v1), Val::Num(v2)) => Val::Num(v1 + v2),96 _ => panic!("can't add: {:?} and {:?}", a, b),97 })98}99100pub fn evaluate_binary_op_special(101 context: Context,102 a: &LocExpr,103 op: BinaryOpType,104 b: &LocExpr,105) -> Result<Val> {106 Ok(107 match (evaluate(context.clone(), &a)?.unwrap_if_lazy()?, op, b) {108 (Val::Bool(true), BinaryOpType::Or, _o) => Val::Bool(true),109 (Val::Bool(false), BinaryOpType::And, _o) => Val::Bool(false),110 (a, op, eb) => {111 evaluate_binary_op_normal(&a, op, &evaluate(context, eb)?.unwrap_if_lazy()?)?112 }113 },114 )115}116117pub fn evaluate_binary_op_normal(a: &Val, op: BinaryOpType, b: &Val) -> Result<Val> {118 Ok(match (a, op, b) {119 (a, BinaryOpType::Add, b) => evaluate_add_op(a, b)?,120121 (Val::Str(v1), BinaryOpType::Mul, Val::Num(v2)) => Val::Str(v1.repeat(*v2 as usize).into()),122123 // Bool X Bool124 (Val::Bool(a), BinaryOpType::And, Val::Bool(b)) => Val::Bool(*a && *b),125 (Val::Bool(a), BinaryOpType::Or, Val::Bool(b)) => Val::Bool(*a || *b),126127 // Str X Str128 (Val::Str(v1), BinaryOpType::Lt, Val::Str(v2)) => Val::Bool(v1 < v2),129 (Val::Str(v1), BinaryOpType::Gt, Val::Str(v2)) => Val::Bool(v1 > v2),130 (Val::Str(v1), BinaryOpType::Lte, Val::Str(v2)) => Val::Bool(v1 <= v2),131 (Val::Str(v1), BinaryOpType::Gte, Val::Str(v2)) => Val::Bool(v1 >= v2),132133 // Num X Num134 (Val::Num(v1), BinaryOpType::Mul, Val::Num(v2)) => Val::Num(v1 * v2),135 (Val::Num(v1), BinaryOpType::Div, Val::Num(v2)) => {136 if *v2 <= f64::EPSILON {137 create_error(crate::Error::DivisionByZero)?138 }139 Val::Num(v1 / v2)140 }141142 (Val::Num(v1), BinaryOpType::Sub, Val::Num(v2)) => Val::Num(v1 - v2),143144 (Val::Num(v1), BinaryOpType::Lt, Val::Num(v2)) => Val::Bool(v1 < v2),145 (Val::Num(v1), BinaryOpType::Gt, Val::Num(v2)) => Val::Bool(v1 > v2),146 (Val::Num(v1), BinaryOpType::Lte, Val::Num(v2)) => Val::Bool(v1 <= v2),147 (Val::Num(v1), BinaryOpType::Gte, Val::Num(v2)) => Val::Bool(v1 >= v2),148149 (Val::Num(v1), BinaryOpType::BitAnd, Val::Num(v2)) => {150 Val::Num(((*v1 as i32) & (*v2 as i32)) as f64)151 }152 (Val::Num(v1), BinaryOpType::BitOr, Val::Num(v2)) => {153 Val::Num(((*v1 as i32) | (*v2 as i32)) as f64)154 }155 (Val::Num(v1), BinaryOpType::BitXor, Val::Num(v2)) => {156 Val::Num(((*v1 as i32) ^ (*v2 as i32)) as f64)157 }158 (Val::Num(v1), BinaryOpType::Lhs, Val::Num(v2)) => {159 Val::Num(((*v1 as i32) << (*v2 as i32)) as f64)160 }161 (Val::Num(v1), BinaryOpType::Rhs, Val::Num(v2)) => {162 Val::Num(((*v1 as i32) >> (*v2 as i32)) as f64)163 }164165 _ => panic!("no rules for binary operation: {:?} {:?} {:?}", a, op, b),166 })167}168169future_wrapper!(HashMap<Rc<str>, LazyBinding>, FutureNewBindings);170future_wrapper!(ObjValue, FutureObjValue);171172pub fn evaluate_comp<T>(173 context: Context,174 value: &impl Fn(Context) -> Result<T>,175 specs: &[CompSpec],176) -> Result<Option<Vec<T>>> {177 Ok(match specs.get(0) {178 None => Some(vec![value(context)?]),179 Some(CompSpec::IfSpec(IfSpecData(cond))) => {180 if evaluate(context.clone(), &cond)?.try_cast_bool("if spec")? {181 evaluate_comp(context, value, &specs[1..])?182 } else {183 None184 }185 }186 Some(CompSpec::ForSpec(ForSpecData(var, expr))) => {187 match evaluate(context.clone(), &expr)?.unwrap_if_lazy()? {188 Val::Arr(list) => {189 let mut out = Vec::new();190 for item in list.iter() {191 let item = item.unwrap_if_lazy()?;192 out.push(evaluate_comp(193 context.with_var(var.clone(), item.clone())?,194 value,195 &specs[1..],196 )?);197 }198 Some(out.into_iter().flatten().flatten().collect())199 }200 _ => panic!("for expression evaluated to non-iterable value"),201 }202 }203 })204}205206pub fn evaluate_member_list_object(context: Context, members: &Vec<Member>) -> Result<ObjValue> {207 let new_bindings = FutureNewBindings::new();208 let future_this = FutureObjValue::new();209 let context_creator = context_creator!(210 closure!(clone context, clone new_bindings, |this: Option<ObjValue>, super_obj: Option<ObjValue>| {211 Ok(context.extend_unbound(212 new_bindings.clone().unwrap(),213 context.dollar().clone().or_else(||this.clone()),214 Some(this.unwrap()),215 super_obj216 )?)217 })218 );219 {220 let mut bindings: HashMap<Rc<str>, LazyBinding> = HashMap::new();221 for (n, b) in members222 .iter()223 .filter_map(|m| match m {224 Member::BindStmt(b) => Some(b.clone()),225 _ => None,226 })227 .map(|b| evaluate_binding(&b, context_creator.clone()))228 {229 bindings.insert(n, b);230 }231 new_bindings.fill(bindings);232 }233234 let mut new_members = BTreeMap::new();235 for member in members.iter() {236 match member {237 Member::Field(FieldMember {238 name,239 plus,240 params: None,241 visibility,242 value,243 }) => {244 let name = evaluate_field_name(context.clone(), &name)?;245 if name.is_none() {246 continue;247 }248 let name = name.unwrap();249 new_members.insert(250 name.clone(),251 ObjMember {252 add: *plus,253 visibility: *visibility,254 invoke: LazyBinding::Bindable(Rc::new(255 closure!(clone name, clone value, clone context_creator, |this, super_obj| {256 Ok(LazyVal::new_resolved(push(&value.1, "object field", ||{257 let context = context_creator.0(this, super_obj)?;258 evaluate(259 context,260 &value,261 )262 })?))263 }),264 )),265 },266 );267 }268 Member::Field(FieldMember {269 name,270 params: Some(params),271 value,272 ..273 }) => {274 let name = evaluate_field_name(context.clone(), &name)?;275 if name.is_none() {276 continue;277 }278 let name = name.unwrap();279 new_members.insert(280 name,281 ObjMember {282 add: false,283 visibility: Visibility::Hidden,284 invoke: LazyBinding::Bindable(Rc::new(285 closure!(clone value, clone context_creator, clone params, |this, super_obj| {286 // TODO: Assert287 Ok(LazyVal::new_resolved(evaluate_method(288 context_creator.0(this, super_obj)?,289 params.clone(),290 value.clone(),291 )))292 }),293 )),294 },295 );296 }297 Member::BindStmt(_) => {}298 Member::AssertStmt(_) => {}299 }300 }301 Ok(future_this.fill(ObjValue::new(None, Rc::new(new_members))))302}303304pub fn evaluate_object(context: Context, object: &ObjBody) -> Result<ObjValue> {305 Ok(match object {306 ObjBody::MemberList(members) => evaluate_member_list_object(context, &members)?,307 ObjBody::ObjComp(obj) => {308 let future_this = FutureObjValue::new();309 let mut new_members = BTreeMap::new();310 for (k, v) in evaluate_comp(311 context.clone(),312 &|ctx| {313 let new_bindings = FutureNewBindings::new();314 let context_creator = context_creator!(315 closure!(clone context, clone new_bindings, |this: Option<ObjValue>, super_obj: Option<ObjValue>| {316 Ok(context.extend_unbound(317 new_bindings.clone().unwrap(),318 context.dollar().clone().or_else(||this.clone()),319 None,320 super_obj321 )?)322 })323 );324 let mut bindings: HashMap<Rc<str>, LazyBinding> = HashMap::new();325 for (n, b) in obj326 .pre_locals327 .iter()328 .chain(obj.post_locals.iter())329 .map(|b| evaluate_binding(b, context_creator.clone()))330 {331 bindings.insert(n, b);332 }333 let bindings = new_bindings.fill(bindings);334 let ctx = ctx.extend_unbound(bindings, None, None, None)?;335 let key = evaluate(ctx.clone(), &obj.key)?;336 let value = LazyBinding::Bindable(Rc::new(337 closure!(clone ctx, clone obj.value, |this, _super_obj| {338 Ok(LazyVal::new_resolved(evaluate(ctx.extend(HashMap::new(), None, this, None)?, &value)?))339 }),340 ));341342 Ok((key, value))343 },344 &obj.compspecs,345 )?346 .unwrap()347 {348 match k {349 Val::Null => {}350 Val::Str(n) => {351 new_members.insert(352 n,353 ObjMember {354 add: false,355 visibility: Visibility::Normal,356 invoke: v,357 },358 );359 }360 v => create_error(Error::FieldMustBeStringGot(v.value_type()?))?,361 }362 }363364 future_this.fill(ObjValue::new(None, Rc::new(new_members)))365 }366 })367}368369pub fn evaluate(context: Context, expr: &LocExpr) -> Result<Val> {370 use Expr::*;371 let LocExpr(expr, loc) = expr;372 Ok(match &**expr {373 Literal(LiteralType::This) => Val::Obj(374 context375 .this()376 .clone()377 .unwrap_or_else(|| panic!("this not found")),378 ),379 Literal(LiteralType::Dollar) => Val::Obj(380 context381 .dollar()382 .clone()383 .unwrap_or_else(|| panic!("dollar not found")),384 ),385 Literal(LiteralType::True) => Val::Bool(true),386 Literal(LiteralType::False) => Val::Bool(false),387 Literal(LiteralType::Null) => Val::Null,388 Parened(e) => evaluate(context, e)?,389 Str(v) => Val::Str(v.clone()),390 Num(v) => Val::Num(*v),391 BinaryOp(v1, o, v2) => evaluate_binary_op_special(context, &v1, *o, &v2)?,392 UnaryOp(o, v) => evaluate_unary_op(*o, &evaluate(context, v)?)?,393 Var(name) => push(loc, "var", || {394 Ok(Val::Lazy(context.binding(name.clone())?).unwrap_if_lazy()?)395 })?,396 Index(LocExpr(v, _), index) if matches!(&**v, Expr::Literal(LiteralType::Super)) => {397 let name = evaluate(context.clone(), index)?.try_cast_str("object index")?;398 context399 .super_obj()400 .clone()401 .expect("no super found")402 .get_raw(&name, &context.this().clone().expect("no this found"))?403 .expect("value not found")404 }405 Index(value, index) => {406 match (407 evaluate(context.clone(), value)?.unwrap_if_lazy()?,408 evaluate(context, index)?,409 ) {410 (Val::Obj(v), Val::Str(s)) => {411 if let Some(v) = v.get(s.clone())? {412 v.unwrap_if_lazy()?413 } else if let Some(Val::Str(n)) = v.get("__intristic_namespace__".into())? {414 Val::Intristic(n, s)415 } else {416 create_error(crate::Error::NoSuchField(s))?417 }418 }419 (Val::Obj(_), n) => create_error(crate::Error::ValueIndexMustBeTypeGot(420 ValType::Obj,421 ValType::Str,422 n.value_type()?,423 ))?,424425 (Val::Arr(v), Val::Num(n)) => {426 if n.fract() > f64::EPSILON {427 create_error(crate::Error::FractionalIndex)?428 }429 v.get(n as usize)430 .unwrap_or_else(|| panic!("out of bounds"))431 .clone()432 .unwrap_if_lazy()?433 }434 (Val::Arr(_), Val::Str(n)) => {435 create_error(crate::Error::AttemptedIndexAnArrayWithString(n))?436 }437 (Val::Arr(_), n) => create_error(crate::Error::ValueIndexMustBeTypeGot(438 ValType::Arr,439 ValType::Num,440 n.value_type()?,441 ))?,442443 (Val::Str(s), Val::Num(n)) => Val::Str(444 s.chars()445 .skip(n as usize)446 .take(1)447 .collect::<String>()448 .into(),449 ),450 (Val::Str(_), n) => create_error(crate::Error::ValueIndexMustBeTypeGot(451 ValType::Str,452 ValType::Num,453 n.value_type()?,454 ))?,455456 (v, _) => create_error(crate::Error::CantIndexInto(v.value_type()?))?,457 }458 }459 LocalExpr(bindings, returned) => {460 let mut new_bindings: HashMap<Rc<str>, LazyBinding> = HashMap::new();461 let future_context = Context::new_future();462463 let context_creator = context_creator!(464 closure!(clone future_context, |_, _| Ok(future_context.clone().unwrap()))465 );466467 for (k, v) in bindings468 .iter()469 .map(|b| evaluate_binding(b, context_creator.clone()))470 {471 new_bindings.insert(k, v);472 }473474 let context = context475 .extend_unbound(new_bindings, None, None, None)?476 .into_future(future_context);477 evaluate(context, &returned.clone())?478 }479 Arr(items) => {480 let mut out = Vec::with_capacity(items.len());481 for item in items {482 out.push(Val::Lazy(lazy_val!(483 closure!(clone context, clone item, || {484 evaluate(context.clone(), &item)485 })486 )));487 }488 Val::Arr(Rc::new(out))489 }490 ArrComp(expr, compspecs) => Val::Arr(491 // First compspec should be forspec, so no "None" possible here492 Rc::new(evaluate_comp(context, &|ctx| evaluate(ctx, expr), compspecs)?.unwrap()),493 ),494 Obj(body) => Val::Obj(evaluate_object(context, body)?),495 ObjExtend(s, t) => evaluate_add_op(496 &evaluate(context.clone(), s)?,497 &Val::Obj(evaluate_object(context, t)?),498 )?,499 Apply(value, args, tailstrict) => {500 let lazy = evaluate(context.clone(), value)?;501 let value = lazy.unwrap_if_lazy()?;502 match value {503 Val::Intristic(ns, name) => match (&ns as &str, &name as &str) {504 // arr/string/function505 ("std", "length") => {506 assert_eq!(args.len(), 1);507 let expr = &args.get(0).unwrap().1;508 match evaluate(context, expr)? {509 Val::Str(n) => Val::Num(n.chars().count() as f64),510 Val::Arr(i) => Val::Num(i.len() as f64),511 Val::Obj(o) => Val::Num(512 o.fields_visibility()513 .into_iter()514 .filter(|(_k, v)| *v)515 .count() as f64,516 ),517 v => panic!("can't get length of {:?}", v),518 }519 }520 // any521 ("std", "type") => {522 assert_eq!(args.len(), 1);523 let expr = &args.get(0).unwrap().1;524 Val::Str(evaluate(context, expr)?.value_type()?.name().into())525 }526 // length, idx=>any527 ("std", "makeArray") => {528 assert_eq!(args.len(), 2);529 if let (Val::Num(v), Val::Func(d)) = (530 evaluate(context.clone(), &args[0].1)?,531 evaluate(context, &args[1].1)?,532 ) {533 assert!(v >= 0.0);534 let mut out = Vec::with_capacity(v as usize);535 for i in 0..v as usize {536 let call_ctx =537 Context::new().with_var("v".into(), Val::Num(i as f64))?;538 out.push(d.evaluate(539 call_ctx,540 &ArgsDesc(vec![Arg(None, el!(Expr::Var("v".into())))]),541 true,542 )?)543 }544 Val::Arr(Rc::new(out))545 } else {546 panic!("bad makeArray call");547 }548 }549 // string550 ("std", "codepoint") => {551 assert_eq!(args.len(), 1);552 if let Val::Str(s) = evaluate(context, &args[0].1)? {553 assert!(554 s.chars().count() == 1,555 "std.codepoint should receive single char string"556 );557 Val::Num(s.chars().take(1).next().unwrap() as u32 as f64)558 } else {559 panic!("bad codepoint call");560 }561 }562 // object, includeHidden563 ("std", "objectFieldsEx") => {564 assert_eq!(args.len(), 2);565 if let (Val::Obj(body), Val::Bool(include_hidden)) = (566 evaluate(context.clone(), &args[0].1)?,567 evaluate(context, &args[1].1)?,568 ) {569 Val::Arr(Rc::new(570 body.fields_visibility()571 .into_iter()572 .filter(|(_k, v)| *v || include_hidden)573 .map(|(k, _v)| Val::Str(k))574 .collect(),575 ))576 } else {577 panic!("bad objectFieldsEx call");578 }579 }580 // object, field, includeHidden581 ("std", "objectHasEx") => {582 assert_eq!(args.len(), 3);583 if let (Val::Obj(body), Val::Str(name), Val::Bool(include_hidden)) = (584 evaluate(context.clone(), &args[0].1)?,585 evaluate(context.clone(), &args[1].1)?,586 evaluate(context, &args[2].1)?,587 ) {588 Val::Bool(589 body.fields_visibility()590 .into_iter()591 .filter(|(_k, v)| *v || include_hidden)592 .any(|(k, _v)| *k == *name),593 )594 } else {595 panic!("bad objectHasEx call");596 }597 }598 ("std", "primitiveEquals") => {599 assert_eq!(args.len(), 2);600 let (a, b) = (601 evaluate(context.clone(), &args[0].1)?,602 evaluate(context, &args[1].1)?,603 );604 Val::Bool(a == b)605 }606 ("std", "modulo") => {607 assert_eq!(args.len(), 2);608 if let (Val::Num(a), Val::Num(b)) = (609 evaluate(context.clone(), &args[0].1)?,610 evaluate(context, &args[1].1)?,611 ) {612 Val::Num(a % b)613 } else {614 panic!("bad modulo call");615 }616 }617 ("std", "floor") => {618 assert_eq!(args.len(), 1);619 if let Val::Num(a) = evaluate(context, &args[0].1)? {620 Val::Num(a.floor())621 } else {622 panic!("bad floor call");623 }624 }625 ("std", "trace") => {626 assert_eq!(args.len(), 2);627 if let (Val::Str(a), b) = (628 evaluate(context.clone(), &args[0].1)?,629 evaluate(context, &args[1].1)?,630 ) {631 // TODO: Line numbers as in original jsonnet632 println!("TRACE: {}", a);633 b634 } else {635 panic!("bad trace call");636 }637 }638 ("std", "pow") => {639 assert_eq!(args.len(), 2);640 if let (Val::Num(a), Val::Num(b)) = (641 evaluate(context.clone(), &args[0].1)?,642 evaluate(context, &args[1].1)?,643 ) {644 Val::Num(a.powf(b))645 } else {646 panic!("bad pow call");647 }648 }649 ("std", "extVar") => {650 assert_eq!(args.len(), 1);651 if let Val::Str(a) = evaluate(context, &args[0].1)? {652 with_state(|s| s.0.ext_vars.borrow().get(&a).cloned()).ok_or_else(653 || {654 create_error::<()>(crate::Error::UndefinedExternalVariable(a))655 .err()656 .unwrap()657 },658 )?659 } else {660 panic!("bad extVar call");661 }662 }663 ("std", "filter") => {664 assert_eq!(args.len(), 2);665 if let (Val::Func(predicate), Val::Arr(arr)) = (666 evaluate(context.clone(), &args[0].1)?,667 evaluate(context.clone(), &args[1].1)?,668 ) {669 Val::Arr(Rc::new(670 arr.iter()671 .filter(|e| {672 predicate673 .evaluate_values(context.clone(), &[e.clone()])674 .unwrap()675 .try_cast_bool("filter predicate")676 .unwrap()677 })678 .cloned()679 .collect(),680 ))681 } else {682 panic!("bad filter call");683 }684 }685 // faster686 ("std", "join") => {687 assert_eq!(args.len(), 2);688 let joiner = evaluate(context.clone(), &args[0].1)?;689 let items = evaluate(context, &args[1].1)?;690 match (joiner.unwrap_if_lazy()?, items.unwrap_if_lazy()?) {691 (Val::Arr(joiner_items), Val::Arr(items)) => {692 // TODO: Minimal size should be known693 let mut out = Vec::new();694695 let mut first = true;696 for item in items.iter().cloned() {697 if let Val::Arr(items) = item.unwrap_if_lazy()? {698 if !first {699 out.reserve(joiner_items.len());700 out.extend(joiner_items.iter().cloned());701 }702 first = false;703 out.reserve(items.len());704 out.extend(items.iter().cloned());705 } else {706 panic!("all array items should be arrays")707 }708 }709710 Val::Arr(Rc::new(out))711 }712 (Val::Str(joiner), Val::Arr(items)) => {713 let mut out = String::new();714715 let mut first = true;716 for item in items.iter().cloned() {717 if let Val::Str(item) = item.unwrap_if_lazy()? {718 if !first {719 out += &joiner;720 }721 first = false;722 out += &item;723 } else {724 panic!("all array items should be strings")725 }726 }727728 Val::Str(out.into())729 }730 (joiner, items) => panic!("bad join call: {:?} {:?}", joiner, items),731 }732 }733 (ns, name) => panic!("Intristic not found: {}.{}", ns, name),734 },735 Val::Func(f) => {736 let body = || f.evaluate(context, args, *tailstrict);737 if *tailstrict {738 body()?739 } else {740 push(loc, "function call", body)?741 }742 }743 _ => panic!("{:?} is not a function", value),744 }745 }746 Function(params, body) => evaluate_method(context, params.clone(), body.clone()),747 AssertExpr(AssertStmt(value, msg), returned) => {748 let assertion_result = push(&value.1, "assertion condition", || {749 evaluate(context.clone(), &value)?750 .try_cast_bool("assertion condition should be boolean")751 })?;752 if assertion_result {753 evaluate(context, returned)?754 } else if let Some(msg) = msg {755 panic!(756 "assertion failed ({:?}): {}",757 value,758 evaluate(context, msg)?.try_cast_str("assertion message should be string")?759 );760 } else {761 panic!("assertion failed ({:?}): no message", value);762 }763 }764 Error(e) => create_error(crate::Error::RuntimeError(765 evaluate(context, e)?.try_cast_str("error text should be string")?,766 ))?,767 IfElse {768 cond,769 cond_then,770 cond_else,771 } => {772 if evaluate(context.clone(), &cond.0)?773 .try_cast_bool("if condition should be boolean")?774 {775 evaluate(context, cond_then)?776 } else {777 match cond_else {778 Some(v) => evaluate(context, v)?,779 None => Val::Null,780 }781 }782 }783 Import(path) => {784 let mut tmp = loc785 .clone()786 .expect("imports can't be used without loc_data")787 .0;788 let import_location = Rc::make_mut(&mut tmp);789 import_location.pop();790 with_state(|s| s.import_file(&import_location, path))?791 }792 ImportStr(path) => {793 let mut tmp = loc794 .clone()795 .expect("imports can't be used without loc_data")796 .0;797 let import_location = Rc::make_mut(&mut tmp);798 import_location.pop();799 Val::Str(with_state(|s| s.import_file_str(&import_location, path))?)800 }801 Literal(LiteralType::Super) => return create_error(crate::error::Error::StandaloneSuper),802 })803}crates/jsonnet-evaluator/src/function.rsdiffbeforeafterboth--- a/crates/jsonnet-evaluator/src/function.rs
+++ b/crates/jsonnet-evaluator/src/function.rs
@@ -35,7 +35,7 @@
let mut positioned_args = vec![None; params.0.len()];
for (id, arg) in args.iter().enumerate() {
let idx = if let Some(name) = &arg.0 {
- params.iter().position(|p| &p.0 == name).ok_or_else(|| {
+ params.iter().position(|p| *p.0 == *name).ok_or_else(|| {
create_error::<()>(Error::UnknownFunctionParameter(name.clone()))
.err()
.unwrap()
crates/jsonnet-evaluator/src/lib.rsdiffbeforeafterboth--- a/crates/jsonnet-evaluator/src/lib.rs
+++ b/crates/jsonnet-evaluator/src/lib.rs
@@ -65,11 +65,11 @@
/// Contains file source codes and evaluated results for imports and pretty
/// printing stacktraces
files: RefCell<HashMap<PathBuf, FileData>>,
- str_files: RefCell<HashMap<PathBuf, String>>,
- globals: RefCell<HashMap<String, Val>>,
+ str_files: RefCell<HashMap<PathBuf, Rc<str>>>,
+ globals: RefCell<HashMap<Rc<str>, Val>>,
/// Values to use with std.extVar
- ext_vars: RefCell<HashMap<String, Val>>,
+ ext_vars: RefCell<HashMap<Rc<str>, Val>>,
settings: EvaluationSettings,
import_resolver: Box<dyn ImportResolver>,
@@ -87,7 +87,7 @@
with_state(|s| s.error(err))
}
pub(crate) fn push<T>(
- e: &Option<Rc<ExprLocation>>,
+ e: &Option<ExprLocation>,
comment: &str,
f: impl FnOnce() -> Result<T>,
) -> Result<T> {
@@ -185,11 +185,14 @@
})?;
self.evaluate_file(&file_path)
}
- pub(crate) fn import_file_str(&self, from: &PathBuf, path: &PathBuf) -> Result<String> {
+ pub(crate) fn import_file_str(&self, from: &PathBuf, path: &PathBuf) -> Result<Rc<str>> {
let path = self.0.import_resolver.resolve_file(from, path)?;
if !self.0.str_files.borrow().contains_key(&path) {
let file_str = self.0.import_resolver.load_file_contents(&path)?;
- self.0.str_files.borrow_mut().insert(path.clone(), file_str);
+ self.0
+ .str_files
+ .borrow_mut()
+ .insert(path.clone(), file_str.into());
}
Ok(self.0.str_files.borrow().get(&path).cloned().unwrap())
}
@@ -210,10 +213,10 @@
self.run_in_state(|| evaluate(self.create_default_context()?, &code))
}
- pub fn add_global(&self, name: String, value: Val) {
+ pub fn add_global(&self, name: Rc<str>, value: Val) {
self.0.globals.borrow_mut().insert(name, value);
}
- pub fn add_ext_var(&self, name: String, value: Val) {
+ pub fn add_ext_var(&self, name: Rc<str>, value: Val) {
self.0.ext_vars.borrow_mut().insert(name, value);
}
@@ -236,14 +239,14 @@
.unwrap();
}
let val = self.evaluate_file(&PathBuf::from("std.jsonnet")).unwrap();
- self.add_global("std".to_owned(), val);
+ self.add_global("std".into(), val);
});
self
}
pub fn create_default_context(&self) -> Result<Context> {
let globals = self.0.globals.borrow();
- let mut new_bindings: HashMap<String, LazyBinding> = HashMap::new();
+ let mut new_bindings: HashMap<Rc<str>, LazyBinding> = HashMap::new();
for (name, value) in globals.iter() {
new_bindings.insert(
name.clone(),
@@ -255,7 +258,7 @@
pub fn push<T>(
&self,
- e: Rc<ExprLocation>,
+ e: ExprLocation,
comment: String,
f: impl FnOnce() -> Result<T>,
) -> Result<T> {
@@ -362,7 +365,7 @@
let evaluator = EvaluationState::default();
evaluator.with_stdlib();
let val = evaluator.parse_evaluate_raw($str).unwrap();
- evaluator.add_global("__tmp__to_yaml__".to_owned(), val);
+ evaluator.add_global("__tmp__to_yaml__".into(), val);
evaluator
.parse_evaluate_raw("std.manifestJsonEx(__tmp__to_yaml__, \"\")")
.unwrap()
crates/jsonnet-evaluator/src/obj.rsdiffbeforeafterboth--- a/crates/jsonnet-evaluator/src/obj.rs
+++ b/crates/jsonnet-evaluator/src/obj.rs
@@ -18,9 +18,10 @@
#[derive(Debug)]
pub struct ObjValueInternals {
super_obj: Option<ObjValue>,
- this_entries: Rc<BTreeMap<String, ObjMember>>,
- value_cache: RefCell<HashMap<String, Val>>,
+ this_entries: Rc<BTreeMap<Rc<str>, ObjMember>>,
+ value_cache: RefCell<HashMap<Rc<str>, Val>>,
}
+#[derive(Clone)]
pub struct ObjValue(pub(crate) Rc<ObjValueInternals>);
impl Debug for ObjValue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
@@ -43,7 +44,7 @@
impl ObjValue {
pub fn new(
super_obj: Option<ObjValue>,
- this_entries: Rc<BTreeMap<String, ObjMember>>,
+ this_entries: Rc<BTreeMap<Rc<str>, ObjMember>>,
) -> ObjValue {
ObjValue(Rc::new(ObjValueInternals {
super_obj,
@@ -57,7 +58,7 @@
Some(v) => ObjValue::new(Some(v.with_super(super_obj)), self.0.this_entries.clone()),
}
}
- pub fn enum_fields(&self, handler: &impl Fn(&str, &Visibility)) {
+ pub fn enum_fields(&self, handler: &impl Fn(&Rc<str>, &Visibility)) {
if let Some(s) = &self.0.super_obj {
s.enum_fields(handler);
}
@@ -65,7 +66,7 @@
handler(&name, &member.visibility);
}
}
- pub fn fields_visibility(&self) -> IndexMap<String, bool> {
+ pub fn fields_visibility(&self) -> IndexMap<Rc<str>, bool> {
let out = Rc::new(RefCell::new(IndexMap::new()));
self.enum_fields(&|name, visibility| {
let mut out = out.borrow_mut();
@@ -85,16 +86,20 @@
});
Rc::try_unwrap(out).unwrap().into_inner()
}
- pub fn get(&self, key: &str) -> Result<Option<Val>> {
- if let Some(v) = self.0.value_cache.borrow().get(key) {
+ pub fn visible_fields(&self) -> Vec<Rc<str>> {
+ self.fields_visibility()
+ .into_iter()
+ .filter(|(_k, v)| *v)
+ .map(|(k, _)| k)
+ .collect()
+ }
+ pub fn get(&self, key: Rc<str>) -> Result<Option<Val>> {
+ if let Some(v) = self.0.value_cache.borrow().get(&key) {
return Ok(Some(v.clone()));
}
- if let Some(v) = self.get_raw(key, self)? {
+ if let Some(v) = self.get_raw(&key, self)? {
let v = v.unwrap_if_lazy()?;
- self.0
- .value_cache
- .borrow_mut()
- .insert(key.to_owned(), v.clone());
+ self.0.value_cache.borrow_mut().insert(key, v.clone());
Ok(Some(v))
} else {
Ok(None)
@@ -108,10 +113,10 @@
.evaluate()?,
)),
(Some(k), Some(s)) => {
- let our = k
+ let lazy = k
.invoke
- .evaluate(Some(real_this.clone()), self.0.super_obj.clone())?
- .evaluate()?;
+ .evaluate(Some(real_this.clone()), self.0.super_obj.clone())?;
+ let our = lazy.evaluate()?;
if k.add {
s.get_raw(key, real_this)?
.map_or(Ok(Some(our.clone())), |v| {
@@ -124,11 +129,6 @@
(None, Some(s)) => s.get_raw(key, real_this),
(None, None) => Ok(None),
}
- }
-}
-impl Clone for ObjValue {
- fn clone(&self) -> Self {
- ObjValue(self.0.clone())
}
}
impl PartialEq for ObjValue {
crates/jsonnet-evaluator/src/val.rsdiffbeforeafterboth--- a/crates/jsonnet-evaluator/src/val.rs
+++ b/crates/jsonnet-evaluator/src/val.rs
@@ -81,7 +81,7 @@
}
}
-#[derive(Debug, Clone)]
+#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ValType {
Bool,
Null,
@@ -115,42 +115,46 @@
pub enum Val {
Bool(bool),
Null,
- Str(String),
+ Str(Rc<str>),
Num(f64),
Lazy(LazyVal),
- Arr(Vec<Val>),
+ Arr(Rc<Vec<Val>>),
Obj(ObjValue),
Func(FuncDesc),
// Library functions implemented in native
- Intristic(String, String),
+ Intristic(Rc<str>, Rc<str>),
+}
+macro_rules! matches_unwrap {
+ ($e: expr, $p: pat, $r: expr) => {
+ match $e {
+ $p => $r,
+ _ => panic!("no match"),
+ }
+ };
}
impl Val {
+ pub fn assert_type(&self, context: &'static str, val_type: ValType) -> Result<()> {
+ let this_type = self.value_type()?;
+ if this_type != val_type {
+ create_error(Error::TypeMismatch(context, vec![val_type], this_type))
+ } else {
+ Ok(())
+ }
+ }
pub fn try_cast_bool(self, context: &'static str) -> Result<bool> {
- match self.unwrap_if_lazy()? {
- Val::Bool(v) => Ok(v),
- v => create_error(Error::TypeMismatch(
- context,
- vec![ValType::Bool],
- v.value_type()?,
- )),
- }
+ self.assert_type(context, ValType::Bool)?;
+ Ok(matches_unwrap!(self.unwrap_if_lazy()?, Val::Bool(v), v))
}
- pub fn try_cast_str(self, context: &'static str) -> Result<String> {
- match self.unwrap_if_lazy()? {
- Val::Str(v) => Ok(v),
- v => create_error(Error::TypeMismatch(
- context,
- vec![ValType::Str],
- v.value_type()?,
- )),
- }
+ pub fn try_cast_str(self, context: &'static str) -> Result<Rc<str>> {
+ self.assert_type(context, ValType::Str)?;
+ Ok(matches_unwrap!(self.unwrap_if_lazy()?, Val::Str(v), v))
}
- pub fn unwrap_if_lazy(self) -> Result<Self> {
+ pub fn unwrap_if_lazy(&self) -> Result<Self> {
Ok(if let Val::Lazy(v) = self {
v.evaluate()?.unwrap_if_lazy()?
} else {
- self
+ self.clone()
})
}
pub fn value_type(&self) -> Result<ValType> {
@@ -166,26 +170,30 @@
Val::Lazy(_) => self.clone().unwrap_if_lazy()?.value_type()?,
})
}
- pub fn into_json(self, padding: usize) -> Result<String> {
+ pub fn into_json(self, padding: usize) -> Result<Rc<str>> {
with_state(|s| {
let ctx = s
.create_default_context()?
- .with_var("__tmp__to_json__".to_owned(), self)?;
- if let Val::Str(result) = evaluate(
+ .with_var("__tmp__to_json__".into(), self)?;
+ Ok(evaluate(
ctx,
&el!(Expr::Apply(
el!(Expr::Index(
- el!(Expr::Var("std".to_owned())),
- el!(Expr::Str("manifestJsonEx".to_owned()))
+ el!(Expr::Var("std".into())),
+ el!(Expr::Str("manifestJsonEx".into()))
)),
ArgsDesc(vec![
- Arg(None, el!(Expr::Var("__tmp__to_json__".to_owned()))),
- Arg(None, el!(Expr::Str(" ".repeat(padding))))
+ Arg(None, el!(Expr::Var("__tmp__to_json__".into()))),
+ Arg(None, el!(Expr::Str(" ".repeat(padding).into())))
]),
false
)),
- )? {
- Ok(result)
+ )?
+ .try_cast_str("to json")?)
+ })
+ }
+}
+
} else {
unreachable!()
}
crates/jsonnet-parser/src/expr.rsdiffbeforeafterboth--- a/crates/jsonnet-parser/src/expr.rs
+++ b/crates/jsonnet-parser/src/expr.rs
@@ -1,15 +1,15 @@
use serde::{Deserialize, Serialize};
use std::{fmt::Debug, ops::Deref, path::PathBuf, rc::Rc};
-#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum FieldName {
/// {fixed: 2}
- Fixed(String),
+ Fixed(Rc<str>),
/// {["dyn"+"amic"]: 3}
Dyn(LocExpr),
}
-#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
+#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub enum Visibility {
/// :
Normal,
@@ -19,10 +19,10 @@
Unhide,
}
-#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct AssertStmt(pub LocExpr, pub Option<LocExpr>);
-#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct FieldMember {
pub name: FieldName,
pub plus: bool,
@@ -31,7 +31,7 @@
pub value: LocExpr,
}
-#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum Member {
Field(FieldMember),
BindStmt(BindSpec),
@@ -71,11 +71,11 @@
}
/// name, default value
-#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
-pub struct Param(pub String, pub Option<LocExpr>);
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
+pub struct Param(pub Rc<str>, pub Option<LocExpr>);
/// Defined function parameters
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
-pub struct ParamsDesc(pub Vec<Param>);
+pub struct ParamsDesc(pub Rc<Vec<Param>>);
impl Deref for ParamsDesc {
type Target = Vec<Param>;
fn deref(&self) -> &Self::Target {
@@ -83,9 +83,9 @@
}
}
-#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct Arg(pub Option<String>, pub LocExpr);
-#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct ArgsDesc(pub Vec<Arg>);
impl Deref for ArgsDesc {
type Target = Vec<Arg>;
@@ -96,35 +96,38 @@
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct BindSpec {
- pub name: String,
+ pub name: Rc<str>,
pub params: Option<ParamsDesc>,
pub value: LocExpr,
}
-#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct IfSpecData(pub LocExpr);
-#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
-pub struct ForSpecData(pub String, pub LocExpr);
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
+pub struct ForSpecData(pub Rc<str>, pub LocExpr);
-#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum CompSpec {
IfSpec(IfSpecData),
ForSpec(ForSpecData),
}
-#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
+pub struct ObjComp {
+ pub pre_locals: Vec<BindSpec>,
+ pub key: LocExpr,
+ pub value: LocExpr,
+ pub post_locals: Vec<BindSpec>,
+ pub compspecs: Vec<CompSpec>,
+}
+
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum ObjBody {
MemberList(Vec<Member>),
- ObjComp {
- pre_locals: Vec<BindSpec>,
- key: LocExpr,
- value: LocExpr,
- post_locals: Vec<BindSpec>,
- compspecs: Vec<CompSpec>,
- },
+ ObjComp(ObjComp),
}
-#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
+#[derive(Debug, PartialEq, Serialize, Deserialize, Clone, Copy)]
pub enum LiteralType {
This,
Super,
@@ -134,7 +137,7 @@
False,
}
-#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct SliceDesc {
pub start: Option<LocExpr>,
pub end: Option<LocExpr>,
@@ -142,16 +145,16 @@
}
/// Syntax base
-#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum Expr {
Literal(LiteralType),
/// String value: "hello"
- Str(String),
+ Str(Rc<str>),
/// Number: 1, 2.0, 2e+20
Num(f64),
/// Variable name: test
- Var(String),
+ Var(Rc<str>),
/// Array of expressions: [1, 2, "Hello"]
Arr(Vec<LocExpr>),
crates/jsonnet-parser/src/lib.rsdiffbeforeafterboth--- a/crates/jsonnet-parser/src/lib.rs
+++ b/crates/jsonnet-parser/src/lib.rs
@@ -39,7 +39,7 @@
/// Reserved word followed by any non-alphanumberic
rule reserved() = ("assert" / "else" / "error" / "false" / "for" / "function" / "if" / "import" / "importstr" / "in" / "local" / "null" / "tailstrict" / "then" / "self" / "super" / "true") end_of_ident()
- rule id() -> String = quiet!{ !reserved() s:$(alpha() (alpha() / digit())*) {s.to_owned()}} / expected!("<identifier>")
+ rule id() = quiet!{ !reserved() alpha() (alpha() / digit())*} / expected!("<identifier>")
rule keyword(id: &'static str)
= ##parse_string_literal(id) end_of_ident()
@@ -47,7 +47,7 @@
rule l(s: &ParserSettings, x: rule<Expr>) -> LocExpr
= start:position!() v:x() end:position!() {loc_expr!(v, s.loc_data, (s.file_name.clone(), start, end))}
- pub rule param(s: &ParserSettings) -> expr::Param = name:id() expr:(_ "=" _ expr:expr(s){expr})? { expr::Param(name, expr) }
+ pub rule param(s: &ParserSettings) -> expr::Param = name:$(id()) expr:(_ "=" _ expr:expr(s){expr})? { expr::Param(name.into(), expr) }
pub rule params(s: &ParserSettings) -> expr::ParamsDesc
= params:(param(s) ** comma()) {
let mut defaults_started = false;
@@ -55,12 +55,12 @@
defaults_started = defaults_started || param.1.is_some();
assert_eq!(defaults_started, param.1.is_some(), "defauld parameters should be used after all positionals");
}
- expr::ParamsDesc(params)
+ expr::ParamsDesc(Rc::new(params))
}
- / { expr::ParamsDesc(Vec::new()) }
+ / { expr::ParamsDesc(Rc::new(Vec::new())) }
pub rule arg(s: &ParserSettings) -> expr::Arg
- = name:id() _ "=" _ expr:expr(s) {expr::Arg(Some(name), expr)}
+ = name:$(id()) _ "=" _ expr:expr(s) {expr::Arg(Some(name.into()), expr)}
/ expr:expr(s) {expr::Arg(None, expr)}
pub rule args(s: &ParserSettings) -> expr::ArgsDesc
= args:arg(s) ** comma() comma()? {
@@ -74,8 +74,8 @@
/ { expr::ArgsDesc(Vec::new()) }
pub rule bind(s: &ParserSettings) -> expr::BindSpec
- = name:id() _ "=" _ expr:expr(s) {expr::BindSpec{name, params: None, value: expr}}
- / name:id() _ "(" _ params:params(s) _ ")" _ "=" _ expr:expr(s) {expr::BindSpec{name, params: Some(params), value: expr}}
+ = name:$(id()) _ "=" _ expr:expr(s) {expr::BindSpec{name:name.into(), params: None, value: expr}}
+ / name:$(id()) _ "(" _ params:params(s) _ ")" _ "=" _ expr:expr(s) {expr::BindSpec{name:name.into(), params: Some(params), value: expr}}
pub rule assertion(s: &ParserSettings) -> expr::AssertStmt
= keyword("assert") _ cond:expr(s) msg:(_ ":" _ e:expr(s) {e})? { expr::AssertStmt(cond, msg) }
@@ -95,8 +95,8 @@
/ string_block()
pub rule field_name(s: &ParserSettings) -> expr::FieldName
- = name:id() {expr::FieldName::Fixed(name)}
- / name:string() {expr::FieldName::Fixed(name)}
+ = name:$(id()) {expr::FieldName::Fixed(name.into())}
+ / name:string() {expr::FieldName::Fixed(name.into())}
/ "[" _ expr:expr(s) _ "]" {expr::FieldName::Dyn(expr)}
pub rule visibility() -> expr::Visibility
= ":::" {expr::Visibility::Unhide}
@@ -125,35 +125,41 @@
/ field:field(s) {expr::Member::Field(field)}
pub rule objinside(s: &ParserSettings) -> expr::ObjBody
= pre_locals:(b: obj_local(s) comma() {b})* "[" _ key:expr(s) _ "]" _ ":" _ value:expr(s) post_locals:(comma() b:obj_local(s) {b})* _ forspec:forspec(s) others:(_ rest:compspec(s) {rest})? {
- expr::ObjBody::ObjComp {
+ let mut compspecs = vec![CompSpec::ForSpec(forspec)];
+ compspecs.extend(others.unwrap_or_default());
+ expr::ObjBody::ObjComp(expr::ObjComp{
pre_locals,
key,
value,
post_locals,
- compspecs: [vec![CompSpec::ForSpec(forspec)], others.unwrap_or_default()].concat(),
- }
+ compspecs,
+ })
}
/ members:(member(s) ** comma()) comma()? {expr::ObjBody::MemberList(members)}
pub rule ifspec(s: &ParserSettings) -> IfSpecData
= keyword("if") _ expr:expr(s) {IfSpecData(expr)}
pub rule forspec(s: &ParserSettings) -> ForSpecData
- = keyword("for") _ id:id() _ keyword("in") _ cond:expr(s) {ForSpecData(id, cond)}
+ = keyword("for") _ id:$(id()) _ keyword("in") _ cond:expr(s) {ForSpecData(id.into(), cond)}
pub rule compspec(s: &ParserSettings) -> Vec<expr::CompSpec>
= s:(i:ifspec(s) { expr::CompSpec::IfSpec(i) } / f:forspec(s) {expr::CompSpec::ForSpec(f)} ) ** _ {s}
pub rule local_expr(s: &ParserSettings) -> LocExpr
= l(s,<keyword("local") _ binds:bind(s) ** comma() _ ";" _ expr:expr(s) { Expr::LocalExpr(binds, expr) }>)
pub rule string_expr(s: &ParserSettings) -> LocExpr
- = l(s, <s:string() {Expr::Str(s)}>)
+ = l(s, <s:string() {Expr::Str(s.into())}>)
pub rule obj_expr(s: &ParserSettings) -> LocExpr
= l(s,<"{" _ body:objinside(s) _ "}" {Expr::Obj(body)}>)
pub rule array_expr(s: &ParserSettings) -> LocExpr
= l(s,<"[" _ elems:(expr(s) ** comma()) _ comma()? "]" {Expr::Arr(elems)}>)
pub rule array_comp_expr(s: &ParserSettings) -> LocExpr
- = l(s,<"[" _ expr:expr(s) _ comma()? _ forspec:forspec(s) _ others:(others: compspec(s) _ {others})? "]" {Expr::ArrComp(expr, [vec![CompSpec::ForSpec(forspec)], others.unwrap_or_default()].concat())}>)
+ = l(s,<"[" _ expr:expr(s) _ comma()? _ forspec:forspec(s) _ others:(others: compspec(s) _ {others})? "]" {
+ let mut specs = vec![CompSpec::ForSpec(forspec)];
+ specs.extend(others.unwrap_or_default());
+ Expr::ArrComp(expr, specs)
+ }>)
pub rule number_expr(s: &ParserSettings) -> LocExpr
= l(s,<n:number() { expr::Expr::Num(n) }>)
pub rule var_expr(s: &ParserSettings) -> LocExpr
- = l(s,<n:id() { expr::Expr::Var(n) }>)
+ = l(s,<n:$(id()) { expr::Expr::Var(n.into()) }>)
pub rule if_then_else_expr(s: &ParserSettings) -> LocExpr
= l(s,<cond:ifspec(s) _ keyword("then") _ cond_then:expr(s) cond_else:(_ keyword("else") _ e:expr(s) {e})? {Expr::IfElse{
cond,
@@ -219,16 +225,16 @@
--
a:(@) _ "==" _ b:@ {loc_expr_todo!(Expr::Apply(
el!(Expr::Index(
- el!(Expr::Var("std".to_owned())),
- el!(Expr::Str("equals".to_owned()))
+ el!(Expr::Var("std".into())),
+ el!(Expr::Str("equals".into()))
)),
ArgsDesc(vec![Arg(None, a), Arg(None, b)]),
true
))}
a:(@) _ "!=" _ b:@ {loc_expr_todo!(Expr::UnaryOp(UnaryOpType::Not, el!(Expr::Apply(
el!(Expr::Index(
- el!(Expr::Var("std".to_owned())),
- el!(Expr::Str("equals".to_owned()))
+ el!(Expr::Var("std".into())),
+ el!(Expr::Str("equals".into()))
)),
ArgsDesc(vec![Arg(None, a), Arg(None, b)]),
true
@@ -240,8 +246,8 @@
a:(@) _ ">=" _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, BinaryOpType::Gte, b))}
a:(@) _ keyword("in") _ b:@ {loc_expr_todo!(Expr::Apply(
el!(Expr::Index(
- el!(Expr::Var("std".to_owned())),
- el!(Expr::Str("objectHasEx".to_owned()))
+ el!(Expr::Var("std".into())),
+ el!(Expr::Str("objectHasEx".into()))
)), ArgsDesc(vec![Arg(None, b), Arg(None, a), Arg(None, el!(Expr::Literal(LiteralType::True)))]),
true
))}
@@ -256,8 +262,8 @@
a:(@) _ "/" _ b:@ {loc_expr_todo!(Expr::BinaryOp(a, BinaryOpType::Div, b))}
a:(@) _ "%" _ b:@ {loc_expr_todo!(Expr::Apply(
el!(Expr::Index(
- el!(Expr::Var("std".to_owned())),
- el!(Expr::Str("mod".to_owned()))
+ el!(Expr::Var("std".into())),
+ el!(Expr::Str("mod".into()))
)), ArgsDesc(vec![Arg(None, a), Arg(None, b)]),
true
))}
@@ -268,8 +274,8 @@
--
a:(@) _ "[" _ s:slice_desc(s) _ "]" {loc_expr_todo!(Expr::Apply(
el!(Expr::Index(
- el!(Expr::Var("std".to_owned())),
- el!(Expr::Str("slice".to_owned())),
+ el!(Expr::Var("std".into())),
+ el!(Expr::Str("slice".into())),
)),
ArgsDesc(vec![
Arg(None, a),
@@ -279,7 +285,7 @@
]),
true,
))}
- a:(@) _ "." _ s:id() {loc_expr_todo!(Expr::Index(a, el!(Expr::Str(s))))}
+ a:(@) _ "." _ s:$(id()) {loc_expr_todo!(Expr::Index(a, el!(Expr::Str(s.into()))))}
a:(@) _ "[" _ s:expr(s) _ "]" {loc_expr_todo!(Expr::Index(a, s))}
a:(@) _ "(" _ args:args(s) _ ")" ts:(_ keyword("tailstrict"))? {loc_expr_todo!(Expr::Apply(a, args, ts.is_some()))}
a:(@) _ "{" _ body:objinside(s) _ "}" {loc_expr_todo!(Expr::ObjExtend(a, body))}
@@ -352,7 +358,7 @@
fn multiline_string() {
assert_eq!(
parse!("|||\n Hello world!\n a\n|||"),
- el!(Expr::Str("Hello world!\n a\n".to_owned())),
+ el!(Expr::Str("Hello world!\n a\n".into())),
)
}
@@ -369,20 +375,20 @@
fn string_escaping() {
assert_eq!(
parse!(r#""Hello, \"world\"!""#),
- el!(Expr::Str(r#"Hello, "world"!"#.to_owned())),
+ el!(Expr::Str(r#"Hello, "world"!"#.into())),
);
assert_eq!(
parse!(r#"'Hello \'world\'!'"#),
- el!(Expr::Str("Hello 'world'!".to_owned())),
+ el!(Expr::Str("Hello 'world'!".into())),
);
- assert_eq!(parse!(r#"'\\\\'"#), el!(Expr::Str("\\\\".to_owned())),);
+ assert_eq!(parse!(r#"'\\\\'"#), el!(Expr::Str("\\\\".into())),);
}
#[test]
fn string_unescaping() {
assert_eq!(
parse!(r#""Hello\nWorld""#),
- el!(Expr::Str("Hello\nWorld".to_owned())),
+ el!(Expr::Str("Hello\nWorld".into())),
);
}
@@ -390,7 +396,7 @@
fn string_verbantim() {
assert_eq!(
parse!(r#"@"Hello\n""World""""#),
- el!(Expr::Str("Hello\\n\"World\"".to_owned())),
+ el!(Expr::Str("Hello\\n\"World\"".into())),
);
}
@@ -489,16 +495,13 @@
parse!("[std.deepJoin(x) for x in arr]"),
el!(ArrComp(
el!(Apply(
- el!(Index(
- el!(Var("std".to_owned())),
- el!(Str("deepJoin".to_owned()))
- )),
- ArgsDesc(vec![Arg(None, el!(Var("x".to_owned())))]),
+ el!(Index(el!(Var("std".into())), el!(Str("deepJoin".into())))),
+ ArgsDesc(vec![Arg(None, el!(Var("x".into())))]),
false,
)),
vec![CompSpec::ForSpec(ForSpecData(
- "x".to_owned(),
- el!(Var("arr".to_owned()))
+ "x".into(),
+ el!(Var("arr".into()))
))]
)),
)
@@ -508,7 +511,7 @@
fn reserved() {
use Expr::*;
assert_eq!(parse!("null"), el!(Literal(LiteralType::Null)));
- assert_eq!(parse!("nulla"), el!(Var("nulla".to_owned())));
+ assert_eq!(parse!("nulla"), el!(Var("nulla".into())));
}
#[test]
@@ -522,9 +525,9 @@
assert_eq!(
parse!("!a && !b"),
el!(BinaryOp(
- el!(UnaryOp(UnaryOpType::Not, el!(Var("a".to_owned())))),
+ el!(UnaryOp(UnaryOpType::Not, el!(Var("a".into())))),
BinaryOpType::And,
- el!(UnaryOp(UnaryOpType::Not, el!(Var("b".to_owned()))))
+ el!(UnaryOp(UnaryOpType::Not, el!(Var("b".into()))))
))
);
}
@@ -535,9 +538,9 @@
assert_eq!(
parse!("!a / !b"),
el!(BinaryOp(
- el!(UnaryOp(UnaryOpType::Not, el!(Var("a".to_owned())))),
+ el!(UnaryOp(UnaryOpType::Not, el!(Var("a".into())))),
BinaryOpType::Div,
- el!(UnaryOp(UnaryOpType::Not, el!(Var("b".to_owned()))))
+ el!(UnaryOp(UnaryOpType::Not, el!(Var("b".into()))))
))
);
}
@@ -549,7 +552,7 @@
parse!("!!a"),
el!(UnaryOp(
UnaryOpType::Not,
- el!(UnaryOp(UnaryOpType::Not, el!(Var("a".to_owned()))))
+ el!(UnaryOp(UnaryOpType::Not, el!(Var("a".into()))))
))
)
}