difftreelog
feat(evaluator) ArrComp support
in: master
7 files changed
crates/jsonnet-evaluator/src/ctx.rsdiffbeforeafterboth--- a/crates/jsonnet-evaluator/src/ctx.rs
+++ b/crates/jsonnet-evaluator/src/ctx.rs
@@ -1,4 +1,7 @@
-use crate::{future_wrapper, rc_fn_helper, LazyBinding, LazyVal, ObjValue};
+use crate::{
+ future_wrapper, lazy_binding, lazy_val, rc_fn_helper, LazyBinding, LazyVal, ObjValue, Val,
+};
+use closure::closure;
use std::{cell::RefCell, collections::HashMap, fmt::Debug, rc::Rc};
rc_fn_helper!(
@@ -62,6 +65,17 @@
ctx.unwrap()
}
+ pub fn with_var(&self, name: String, value: Val) -> Context {
+ let mut new_bindings: HashMap<_, LazyBinding> = HashMap::new();
+ new_bindings.insert(
+ name,
+ lazy_binding!(
+ closure!(clone value, |_t, _s|lazy_val!(closure!(clone value, ||value.clone())))
+ ),
+ );
+ self.extend(new_bindings, None, None, None)
+ }
+
pub fn extend(
&self,
new_bindings: HashMap<String, LazyBinding>,
crates/jsonnet-evaluator/src/evaluate.rsdiffbeforeafterboth--- a/crates/jsonnet-evaluator/src/evaluate.rs
+++ b/crates/jsonnet-evaluator/src/evaluate.rs
@@ -5,8 +5,9 @@
};
use closure::closure;
use jsonnet_parser::{
- ArgsDesc, BinaryOpType, BindSpec, Expr, FieldMember, LiteralType, LocExpr, Member, ObjBody,
- ParamsDesc, UnaryOpType, Visibility,
+ el, Arg, ArgsDesc, AssertStmt, BinaryOpType, BindSpec, CompSpec, Expr, FieldMember,
+ ForSpecData, IfSpecData, LiteralType, LocExpr, Member, ObjBody, ParamsDesc, UnaryOpType,
+ Visibility,
};
use std::{
collections::{BTreeMap, HashMap},
@@ -94,29 +95,64 @@
}
}
-pub fn evaluate_binary_op(a: &Val, op: BinaryOpType, b: &Val) -> Val {
+pub fn evaluate_add_op(a: &Val, b: &Val) -> Val {
+ match (a, b) {
+ (Val::Str(v1), Val::Str(v2)) => Val::Str(v1.to_owned() + &v2),
+ (Val::Str(v1), Val::Num(v2)) => Val::Str(format!("{}{}", v1, v2)),
+ (Val::Num(v1), Val::Str(v2)) => Val::Str(format!("{}{}", v1, v2)),
+ (Val::Obj(v1), Val::Obj(v2)) => Val::Obj(v2.with_super(v1.clone())),
+ (Val::Arr(a), Val::Arr(b)) => Val::Arr([&a[..], &b[..]].concat()),
+ (Val::Num(v1), Val::Num(v2)) => Val::Num(v1 + v2),
+ _ => panic!("can't add: {:?} and {:?}", a, b),
+ }
+}
+
+pub fn evaluate_binary_op(
+ context: Context,
+ eval_state: EvaluationState,
+ a: &Val,
+ op: BinaryOpType,
+ b: &Val,
+) -> Val {
match (a, op, b) {
- (Val::Lazy(a), o, b) => evaluate_binary_op(&a.evaluate(), o, b),
- (a, o, Val::Lazy(b)) => evaluate_binary_op(a, o, &b.evaluate()),
+ (Val::Lazy(a), o, b) => evaluate_binary_op(context, eval_state, &a.evaluate(), o, b),
+ (a, o, Val::Lazy(b)) => evaluate_binary_op(context, eval_state, a, o, &b.evaluate()),
+
+ (a, BinaryOpType::Add, b) => evaluate_add_op(a, b),
- (Val::Str(v1), BinaryOpType::Add, Val::Str(v2)) => Val::Str(v1.to_owned() + &v2),
(Val::Str(v1), BinaryOpType::Ne, Val::Str(v2)) => bool_val(v1 != v2),
- (Val::Str(v1), BinaryOpType::Add, Val::Num(v2)) => Val::Str(format!("{}{}", v1, v2)),
(Val::Str(v1), BinaryOpType::Mul, Val::Num(v2)) => Val::Str(v1.repeat(*v2 as usize)),
+ (Val::Str(format), BinaryOpType::Mod, args) => evaluate(
+ context
+ .with_var("__tmp__format__".to_owned(), Val::Str(format.to_owned()))
+ .with_var(
+ "__tmp__args__".to_owned(),
+ match args {
+ Val::Arr(v) => Val::Arr(v.clone()),
+ v => Val::Arr(vec![v.clone()]),
+ },
+ ),
+ eval_state,
+ &el!(Expr::Apply(
+ el!(Expr::Index(
+ el!(Expr::Var("std".to_owned())),
+ el!(Expr::Str("format".to_owned()))
+ )),
+ ArgsDesc(vec![
+ Arg(None, el!(Expr::Var("__tmp__format__".to_owned()))),
+ Arg(None, el!(Expr::Var("__tmp__args__".to_owned())))
+ ])
+ )),
+ ),
(Val::Bool(a), BinaryOpType::And, Val::Bool(b)) => Val::Bool(*a && *b),
(Val::Bool(a), BinaryOpType::Or, Val::Bool(b)) => Val::Bool(*a || *b),
- (Val::Obj(v1), BinaryOpType::Add, Val::Obj(v2)) => Val::Obj(v2.with_super(v1.clone())),
-
- (Val::Arr(a), BinaryOpType::Add, Val::Arr(b)) => Val::Arr([&a[..], &b[..]].concat()),
-
(Val::Num(v1), BinaryOpType::Mul, Val::Num(v2)) => Val::Num(v1 * v2),
(Val::Num(v1), BinaryOpType::Div, Val::Num(v2)) => Val::Num(v1 / v2),
(Val::Num(v1), BinaryOpType::Mod, Val::Num(v2)) => Val::Num(v1 % v2),
- (Val::Num(v1), BinaryOpType::Add, Val::Num(v2)) => Val::Num(v1 + v2),
(Val::Num(v1), BinaryOpType::Sub, Val::Num(v2)) => Val::Num(v1 - v2),
(Val::Num(v1), BinaryOpType::Lhs, Val::Num(v2)) => {
@@ -152,6 +188,42 @@
future_wrapper!(HashMap<String, LazyBinding>, FutureNewBindings);
future_wrapper!(ObjValue, FutureObjValue);
+pub fn evaluate_comp(
+ context: Context,
+ eval_state: EvaluationState,
+ value: &LocExpr,
+ specs: &[CompSpec],
+) -> Option<Vec<Val>> {
+ match specs.get(0) {
+ None => Some(vec![evaluate(context, eval_state, &value)]),
+ Some(CompSpec::IfSpec(IfSpecData(cond))) => {
+ match evaluate(context.clone(), eval_state.clone(), &cond).unwrap_if_lazy() {
+ Val::Bool(false) => None,
+ Val::Bool(true) => evaluate_comp(context, eval_state, value, &specs[1..]),
+ _ => panic!("if expression evaluated to non-boolean value"),
+ }
+ }
+ Some(CompSpec::ForSpec(ForSpecData(var, expr))) => {
+ match evaluate(context.clone(), eval_state.clone(), &expr).unwrap_if_lazy() {
+ Val::Arr(list) => {
+ let mut out = Vec::new();
+ for item in list {
+ let item = item.clone();
+ out.push(evaluate_comp(
+ context.with_var(var.clone(), item),
+ eval_state.clone(),
+ value,
+ &specs[1..],
+ ));
+ }
+ Some(out.iter().flatten().flatten().cloned().collect())
+ }
+ _ => panic!("for expression evaluated to non-iterable value"),
+ }
+ }
+ }
+}
+
// TODO: Asserts
pub fn evaluate_object(context: Context, eval_state: EvaluationState, object: ObjBody) -> ObjValue {
match object {
@@ -272,11 +344,18 @@
Parened(e) => evaluate(context, eval_state.clone(), e),
Str(v) => Val::Str(v.clone()),
Num(v) => Val::Num(*v),
- BinaryOp(v1, o, v2) => evaluate_binary_op(
- &evaluate(context.clone(), eval_state.clone(), v1),
- *o,
- &evaluate(context, eval_state.clone(), v2),
- ),
+ BinaryOp(v1, o, v2) => {
+ let a = evaluate(context.clone(), eval_state.clone(), v1).unwrap_if_lazy();
+ let op = *o;
+ let b = evaluate(context.clone(), eval_state.clone(), v2).unwrap_if_lazy();
+ evaluate_binary_op(
+ context,
+ eval_state,
+ &a,
+ op,
+ &b,
+ )
+ },
UnaryOp(o, v) => evaluate_unary_op(*o, &evaluate(context, eval_state, v)),
Var(name) => Val::Lazy(context.binding(&name)).unwrap_if_lazy(),
Index(value, index) => {
@@ -286,7 +365,7 @@
) {
(Val::Obj(v), Val::Str(s)) => v
.get(&s)
- .unwrap_or_else(closure!(clone context, || {
+ .unwrap_or_else(closure!(clone context, clone eval_state, || {
if let Some(n) = v.get("__intristic_namespace__") {
if let Val::Str(n) = n.unwrap_if_lazy() {
Val::Intristic(n, s)
@@ -335,12 +414,16 @@
}
Val::Arr(out)
}
+ ArrComp(expr, compspecs) => {
+ Val::Arr(evaluate_comp(context, eval_state, expr, compspecs).unwrap())
+ }
Obj(body) => Val::Obj(evaluate_object(context, eval_state, body.clone())),
Apply(value, ArgsDesc(args)) => {
let value = evaluate(context.clone(), eval_state.clone(), value).unwrap_if_lazy();
match value {
// TODO: Capture context of application
Val::Intristic(ns, name) => match (&ns as &str, &name as &str) {
+ // arr/string/function
("std", "length") => {
assert_eq!(args.len(), 1);
let expr = &args.get(0).unwrap().1;
@@ -350,11 +433,13 @@
v => panic!("can't get length of {:?}", v),
}
}
+ // any
("std", "type") => {
assert_eq!(args.len(), 1);
let expr = &args.get(0).unwrap().1;
Val::Str(evaluate(context, eval_state, expr).type_of().to_owned())
}
+ // length, idx=>any
("std", "makeArray") => {
assert_eq!(args.len(), 2);
if let (Val::Num(v), Val::Func(d)) = (
@@ -371,6 +456,7 @@
panic!("bad makeArray call");
}
}
+ // string
("std", "codepoint") => {
assert_eq!(args.len(), 1);
if let Val::Str(s) = evaluate(context, eval_state, &args[0].1) {
@@ -383,6 +469,19 @@
panic!("bad codepoint call");
}
}
+ // object, includeHidden
+ ("std", "objectFieldsEx") => {
+ assert_eq!(args.len(), 2);
+ if let (Val::Obj(body), Val::Bool(_include_hidden)) = (
+ evaluate(context.clone(), eval_state.clone(), &args[0].1),
+ evaluate(context, eval_state, &args[1].1),
+ ) {
+ // TODO: handle visibility (_include_hidden)
+ Val::Arr(body.fields().into_iter().map(Val::Str).collect())
+ } else {
+ panic!("bad objectFieldsEx call");
+ }
+ }
(ns, name) => panic!("Intristic not found: {}.{}", ns, name),
},
Val::Func(f) => f.evaluate(
@@ -402,6 +501,17 @@
}
}
Function(params, body) => evaluate_method(context, eval_state, body, params.clone()),
+ AssertExpr(AssertStmt(value, msg), returned) => {
+ if evaluate(context.clone(), eval_state.clone(), &value).try_cast_bool() {
+ evaluate(context, eval_state, returned)
+ }else {
+ if let Some(msg) = msg {
+ panic!("assertion failed ({:?}): {}", value, evaluate(context, eval_state, msg).try_cast_str());
+ } else {
+ panic!("assertion failed ({:?}): no message", value);
+ }
+ }
+ },
Error(e) => panic!("error: {}", evaluate(context, eval_state, e)),
IfElse {
cond,
crates/jsonnet-evaluator/src/lib.rsdiffbeforeafterboth--- a/crates/jsonnet-evaluator/src/lib.rs
+++ b/crates/jsonnet-evaluator/src/lib.rs
@@ -359,7 +359,12 @@
#[test]
fn json() {
- println!("{:?}", eval_stdlib!(r#"std.manifestJson({a:3, b:4, c:6})"#));
+ println!("{:?}", eval_stdlib!(r#"std.manifestJsonEx({a:3, b:4, c:6},"")"#));
+ }
+
+ #[test]
+ fn test() {
+ assert_json_stdlib!(r#"[[a, b] for a in [1,2,3] for b in [4,5,6]]"#, "");
}
#[test]
crates/jsonnet-evaluator/src/obj.rsdiffbeforeafterboth--- a/crates/jsonnet-evaluator/src/obj.rs
+++ b/crates/jsonnet-evaluator/src/obj.rs
@@ -1,5 +1,5 @@
-use crate::{evaluate_binary_op, Binding, Val};
-use jsonnet_parser::{BinaryOpType, Visibility};
+use crate::{evaluate_add_op, Binding, Val};
+use jsonnet_parser::Visibility;
use std::{
cell::RefCell,
collections::{BTreeMap, BTreeSet, HashMap},
@@ -90,9 +90,8 @@
(Some(k), Some(s)) => {
let our = k.invoke.0(Some(real_this.clone()), self.0.super_obj.clone());
if k.add {
- s.get_raw(key, real_this).map_or(Some(our.clone()), |v| {
- Some(evaluate_binary_op(&v, BinaryOpType::Add, &our))
- })
+ s.get_raw(key, real_this)
+ .map_or(Some(our.clone()), |v| Some(evaluate_add_op(&v, &our)))
} else {
Some(our)
}
crates/jsonnet-evaluator/src/val.rsdiffbeforeafterboth--- a/crates/jsonnet-evaluator/src/val.rs
+++ b/crates/jsonnet-evaluator/src/val.rs
@@ -117,6 +117,18 @@
Intristic(String, String),
}
impl Val {
+ pub fn try_cast_bool(self) -> bool {
+ match self.unwrap_if_lazy() {
+ Val::Bool(v) => v,
+ v => panic!("expected bool, got {:?}", v),
+ }
+ }
+ pub fn try_cast_str(self) -> String {
+ match self.unwrap_if_lazy() {
+ Val::Str(v) => v,
+ v => panic!("expected bool, got {:?}", v),
+ }
+ }
pub fn unwrap_if_lazy(self) -> Self {
if let Val::Lazy(v) = self {
v.evaluate().unwrap_if_lazy()
crates/jsonnet-parser/src/expr.rsdiffbeforeafterboth1use std::{fmt::Debug, rc::Rc};23#[derive(Debug, Clone, PartialEq)]4pub enum FieldName {5 /// {fixed: 2}6 Fixed(String),7 /// {["dyn"+"amic"]: 3}8 Dyn(LocExpr),9}1011#[derive(Debug, Clone, PartialEq)]12pub enum Visibility {13 /// :14 Normal,15 /// ::16 Hidden,17 /// :::18 Unhide,19}2021#[derive(Debug, Clone, PartialEq)]22pub struct AssertStmt(pub LocExpr, pub Option<LocExpr>);2324#[derive(Debug, Clone, PartialEq)]25pub struct FieldMember {26 pub name: FieldName,27 pub plus: bool,28 pub params: Option<ParamsDesc>,29 pub visibility: Visibility,30 pub value: LocExpr,31}3233#[derive(Debug, Clone, PartialEq)]34pub enum Member {35 Field(FieldMember),36 BindStmt(BindSpec),37 AssertStmt(AssertStmt),38}3940#[derive(Debug, Clone, Copy, PartialEq)]41pub enum UnaryOpType {42 Plus,43 Minus,44 BitNot,45 Not,46}4748#[derive(Debug, Clone, Copy, PartialEq)]49pub enum BinaryOpType {50 Mul,51 Div,52 Mod,5354 Add,55 Sub,5657 Lhs,58 Rhs,5960 Lt,61 Gt,62 Lte,63 Gte,6465 In,6667 Eq,68 Ne,6970 BitAnd,71 BitOr,72 BitXor,7374 And,75 Or,76}7778/// name, default value79#[derive(Debug, Clone, PartialEq)]80pub struct Param(pub String, pub Option<LocExpr>);81/// Defined function parameters82#[derive(Debug, Clone, PartialEq)]83pub struct ParamsDesc(pub Vec<Param>);84impl ParamsDesc {85 pub fn with_defaults(&self) -> Vec<Param> {86 self.0.iter().filter(|e| e.1.is_some()).cloned().collect()87 }88}8990#[derive(Debug, Clone, PartialEq)]91pub struct Arg(pub Option<String>, pub LocExpr);92#[derive(Debug, Clone, PartialEq)]93pub struct ArgsDesc(pub Vec<Arg>);9495#[derive(Debug, Clone, PartialEq)]96pub struct BindSpec {97 pub name: String,98 pub params: Option<ParamsDesc>,99 pub value: LocExpr,100}101102#[derive(Debug, Clone, PartialEq)]103pub struct IfSpecData(pub LocExpr);104#[derive(Debug, Clone, PartialEq)]105pub struct ForSpecData(pub String, pub LocExpr);106107#[derive(Debug, Clone, PartialEq)]108pub enum CompSpec {109 IfSpec(IfSpecData),110 ForSpec(ForSpecData),111}112113#[derive(Debug, Clone, PartialEq)]114pub enum ObjBody {115 MemberList(Vec<Member>),116 ObjComp {117 pre_locals: Vec<BindSpec>,118 key: LocExpr,119 value: LocExpr,120 post_locals: Vec<BindSpec>,121 first: ForSpecData,122 rest: Vec<CompSpec>,123 },124}125126#[derive(Debug, Clone, PartialEq)]127pub enum LiteralType {128 This,129 Super,130 Dollar,131 Null,132 True,133 False,134}135136#[derive(Debug, Clone, PartialEq)]137pub struct SliceDesc {138 pub start: Option<LocExpr>,139 pub end: Option<LocExpr>,140 pub step: Option<LocExpr>,141}142143/// Syntax base144#[derive(Debug, Clone, PartialEq)]145pub enum Expr {146 Literal(LiteralType),147148 /// String value: "hello"149 Str(String),150 /// Number: 1, 2.0, 2e+20151 Num(f64),152 /// Variable name: test153 Var(String),154155 /// Array of expressions: [1, 2, "Hello"]156 Arr(Vec<LocExpr>),157 /// Array comprehension:158 /// ```jsonnet159 /// ingredients: [160 /// { kind: kind, qty: 4 / 3 }161 /// for kind in [162 /// 'Honey Syrup',163 /// 'Lemon Juice',164 /// 'Farmers Gin',165 /// ]166 /// ],167 /// ```168 ArrComp(LocExpr, ForSpecData, Vec<CompSpec>),169170 /// Object: {a: 2}171 Obj(ObjBody),172 /// Object extension: var1 {b: 2}173 ObjExtend(LocExpr, ObjBody),174175 /// (obj)176 Parened(LocExpr),177178 /// Params in function definition179 /// hello, world, test = 2180 Params(ParamsDesc),181 /// Args in function call182 /// 2 + 2, 3, named = 6183 Args(ArgsDesc),184185 /// -2186 UnaryOp(UnaryOpType, LocExpr),187 /// 2 - 2188 BinaryOp(LocExpr, BinaryOpType, LocExpr),189 /// assert 2 == 2 : "Math is broken"190 AssertExpr(AssertStmt, LocExpr),191 /// local a = 2; { b: a }192 LocalExpr(Vec<BindSpec>, LocExpr),193194 /// a = 3195 Bind(BindSpec),196 /// import "hello"197 Import(String),198 /// importStr "file.txt"199 ImportStr(String),200 /// error "I'm broken"201 Error(LocExpr),202 /// a(b, c)203 Apply(LocExpr, ArgsDesc),204 ///205 Select(LocExpr, String),206 /// a[b]207 Index(LocExpr, LocExpr),208 /// a[1::2]209 Slice(LocExpr, SliceDesc),210 /// function(x) x211 Function(ParamsDesc, LocExpr),212 /// if true == false then 1 else 2213 IfElse {214 cond: IfSpecData,215 cond_then: LocExpr,216 cond_else: Option<LocExpr>,217 },218 /// if 2 = 3219 IfSpec(IfSpecData),220 /// for elem in array221 ForSpec(ForSpecData),222}223224/// file, begin offset, end offset225#[derive(Clone, PartialEq)]226pub struct ExprLocation(pub String, pub usize, pub usize);227impl Debug for ExprLocation {228 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {229 write!(f, "{}:{:?}-{:?}", self.0, self.1, self.2)230 }231}232233/// Holds AST expression and its location in source file+234#[derive(Clone, PartialEq)]235pub struct LocExpr(pub Rc<Expr>, pub Option<Rc<ExprLocation>>);236impl Debug for LocExpr {237 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {238 write!(f, "{:?} from {:?}", self.0, self.1)239 }240}241242/// Creates LocExpr from Expr and ExprLocation components243#[macro_export]244macro_rules! loc_expr {245 ($expr:expr, $need_loc:expr, ($name:expr, $start:expr, $end:expr)) => {246 LocExpr(247 std::rc::Rc::new($expr),248 if $need_loc {249 Some(std::rc::Rc::new(ExprLocation(250 $name.to_owned(),251 $start,252 $end,253 )))254 } else {255 None256 },257 )258 };259}260261/// Creates LocExpr without location info262#[macro_export]263macro_rules! loc_expr_todo {264 ($expr:expr) => {265 LocExpr(Rc::new($expr), None)266 };267}1use std::{fmt::Debug, rc::Rc};23#[derive(Debug, Clone, PartialEq)]4pub enum FieldName {5 /// {fixed: 2}6 Fixed(String),7 /// {["dyn"+"amic"]: 3}8 Dyn(LocExpr),9}1011#[derive(Debug, Clone, PartialEq)]12pub enum Visibility {13 /// :14 Normal,15 /// ::16 Hidden,17 /// :::18 Unhide,19}2021#[derive(Debug, Clone, PartialEq)]22pub struct AssertStmt(pub LocExpr, pub Option<LocExpr>);2324#[derive(Debug, Clone, PartialEq)]25pub struct FieldMember {26 pub name: FieldName,27 pub plus: bool,28 pub params: Option<ParamsDesc>,29 pub visibility: Visibility,30 pub value: LocExpr,31}3233#[derive(Debug, Clone, PartialEq)]34pub enum Member {35 Field(FieldMember),36 BindStmt(BindSpec),37 AssertStmt(AssertStmt),38}3940#[derive(Debug, Clone, Copy, PartialEq)]41pub enum UnaryOpType {42 Plus,43 Minus,44 BitNot,45 Not,46}4748#[derive(Debug, Clone, Copy, PartialEq)]49pub enum BinaryOpType {50 Mul,51 Div,52 Mod,5354 Add,55 Sub,5657 Lhs,58 Rhs,5960 Lt,61 Gt,62 Lte,63 Gte,6465 In,6667 Eq,68 Ne,6970 BitAnd,71 BitOr,72 BitXor,7374 And,75 Or,76}7778/// name, default value79#[derive(Debug, Clone, PartialEq)]80pub struct Param(pub String, pub Option<LocExpr>);81/// Defined function parameters82#[derive(Debug, Clone, PartialEq)]83pub struct ParamsDesc(pub Vec<Param>);84impl ParamsDesc {85 pub fn with_defaults(&self) -> Vec<Param> {86 self.0.iter().filter(|e| e.1.is_some()).cloned().collect()87 }88}8990#[derive(Debug, Clone, PartialEq)]91pub struct Arg(pub Option<String>, pub LocExpr);92#[derive(Debug, Clone, PartialEq)]93pub struct ArgsDesc(pub Vec<Arg>);9495#[derive(Debug, Clone, PartialEq)]96pub struct BindSpec {97 pub name: String,98 pub params: Option<ParamsDesc>,99 pub value: LocExpr,100}101102#[derive(Debug, Clone, PartialEq)]103pub struct IfSpecData(pub LocExpr);104#[derive(Debug, Clone, PartialEq)]105pub struct ForSpecData(pub String, pub LocExpr);106107#[derive(Debug, Clone, PartialEq)]108pub enum CompSpec {109 IfSpec(IfSpecData),110 ForSpec(ForSpecData),111}112113#[derive(Debug, Clone, PartialEq)]114pub enum ObjBody {115 MemberList(Vec<Member>),116 ObjComp {117 pre_locals: Vec<BindSpec>,118 key: LocExpr,119 value: LocExpr,120 post_locals: Vec<BindSpec>,121 first: ForSpecData,122 rest: Vec<CompSpec>,123 },124}125126#[derive(Debug, Clone, PartialEq)]127pub enum LiteralType {128 This,129 Super,130 Dollar,131 Null,132 True,133 False,134}135136#[derive(Debug, Clone, PartialEq)]137pub struct SliceDesc {138 pub start: Option<LocExpr>,139 pub end: Option<LocExpr>,140 pub step: Option<LocExpr>,141}142143/// Syntax base144#[derive(Debug, Clone, PartialEq)]145pub enum Expr {146 Literal(LiteralType),147148 /// String value: "hello"149 Str(String),150 /// Number: 1, 2.0, 2e+20151 Num(f64),152 /// Variable name: test153 Var(String),154155 /// Array of expressions: [1, 2, "Hello"]156 Arr(Vec<LocExpr>),157 /// Array comprehension:158 /// ```jsonnet159 /// ingredients: [160 /// { kind: kind, qty: 4 / 3 }161 /// for kind in [162 /// 'Honey Syrup',163 /// 'Lemon Juice',164 /// 'Farmers Gin',165 /// ]166 /// ],167 /// ```168 ArrComp(LocExpr, Vec<CompSpec>),169170 /// Object: {a: 2}171 Obj(ObjBody),172 /// Object extension: var1 {b: 2}173 ObjExtend(LocExpr, ObjBody),174175 /// (obj)176 Parened(LocExpr),177178 /// Params in function definition179 /// hello, world, test = 2180 Params(ParamsDesc),181 /// Args in function call182 /// 2 + 2, 3, named = 6183 Args(ArgsDesc),184185 /// -2186 UnaryOp(UnaryOpType, LocExpr),187 /// 2 - 2188 BinaryOp(LocExpr, BinaryOpType, LocExpr),189 /// assert 2 == 2 : "Math is broken"190 AssertExpr(AssertStmt, LocExpr),191 /// local a = 2; { b: a }192 LocalExpr(Vec<BindSpec>, LocExpr),193194 /// a = 3195 Bind(BindSpec),196 /// import "hello"197 Import(String),198 /// importStr "file.txt"199 ImportStr(String),200 /// error "I'm broken"201 Error(LocExpr),202 /// a(b, c)203 Apply(LocExpr, ArgsDesc),204 ///205 Select(LocExpr, String),206 /// a[b]207 Index(LocExpr, LocExpr),208 /// a[1::2]209 Slice(LocExpr, SliceDesc),210 /// function(x) x211 Function(ParamsDesc, LocExpr),212 /// if true == false then 1 else 2213 IfElse {214 cond: IfSpecData,215 cond_then: LocExpr,216 cond_else: Option<LocExpr>,217 },218 /// if 2 = 3219 IfSpec(IfSpecData),220 /// for elem in array221 ForSpec(ForSpecData),222}223224/// file, begin offset, end offset225#[derive(Clone, PartialEq)]226pub struct ExprLocation(pub String, pub usize, pub usize);227impl Debug for ExprLocation {228 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {229 write!(f, "{}:{:?}-{:?}", self.0, self.1, self.2)230 }231}232233/// Holds AST expression and its location in source file+234#[derive(Clone, PartialEq)]235pub struct LocExpr(pub Rc<Expr>, pub Option<Rc<ExprLocation>>);236impl Debug for LocExpr {237 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {238 write!(f, "{:?} from {:?}", self.0, self.1)239 }240}241242/// Creates LocExpr from Expr and ExprLocation components243#[macro_export]244macro_rules! loc_expr {245 ($expr:expr, $need_loc:expr, ($name:expr, $start:expr, $end:expr)) => {246 LocExpr(247 std::rc::Rc::new($expr),248 if $need_loc {249 Some(std::rc::Rc::new(ExprLocation(250 $name.to_owned(),251 $start,252 $end,253 )))254 } else {255 None256 },257 )258 };259}260261/// Creates LocExpr without location info262#[macro_export]263macro_rules! loc_expr_todo {264 ($expr:expr) => {265 LocExpr(Rc::new($expr), None)266 };267}crates/jsonnet-parser/src/lib.rsdiffbeforeafterboth--- a/crates/jsonnet-parser/src/lib.rs
+++ b/crates/jsonnet-parser/src/lib.rs
@@ -125,7 +125,7 @@
pub rule string_expr(s: &ParserSettings) -> LocExpr = l(s, <s:string() {Expr::Str(s)}>)
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, forspec, others.unwrap_or_default())}>)
+ 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())}>)
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) }>)
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{
@@ -252,15 +252,18 @@
jsonnet_parser::jsonnet(str, settings)
}
+#[macro_export]
+macro_rules! el {
+ ($expr:expr) => {
+ LocExpr(std::rc::Rc::new($expr), None)
+ };
+}
+
#[cfg(test)]
pub mod tests {
use super::{expr::*, parse};
use crate::ParserSettings;
- macro_rules! el {
- ($expr:expr) => {
- LocExpr(std::rc::Rc::new($expr), None)
- };
- }
+
macro_rules! parse {
($s:expr) => {
parse(
@@ -390,8 +393,10 @@
)),
ArgsDesc(vec![Arg(None, el!(Var("x".to_owned())))])
)),
- ForSpecData("x".to_owned(), el!(Var("arr".to_owned()))),
- vec![]
+ vec![CompSpec::ForSpec(ForSpecData(
+ "x".to_owned(),
+ el!(Var("arr".to_owned()))
+ ))]
)),
)
}
@@ -403,24 +408,26 @@
parse!("[k for k in std.objectFields(patch) if patch[k] == null]"),
el!(ArrComp(
el!(Var("k".to_owned())),
- ForSpecData(
- "k".to_owned(),
- el!(Apply(
+ vec![
+ CompSpec::ForSpec(ForSpecData(
+ "k".to_owned(),
+ el!(Apply(
+ el!(Index(
+ el!(Var("std".to_owned())),
+ el!(Str("objectFields".to_owned()))
+ )),
+ ArgsDesc(vec![Arg(None, el!(Var("patch".to_owned())))])
+ ))
+ )),
+ CompSpec::IfSpec(IfSpecData(el!(BinaryOp(
el!(Index(
- el!(Var("std".to_owned())),
- el!(Str("objectFields".to_owned()))
+ el!(Var("patch".to_owned())),
+ el!(Var("k".to_owned()))
)),
- ArgsDesc(vec![Arg(None, el!(Var("patch".to_owned())))])
- ))
- ),
- vec![CompSpec::IfSpec(IfSpecData(el!(BinaryOp(
- el!(Index(
- el!(Var("patch".to_owned())),
- el!(Var("k".to_owned()))
- )),
- BinaryOpType::Eq,
- el!(Literal(LiteralType::Null))
- ))))]
+ BinaryOpType::Eq,
+ el!(Literal(LiteralType::Null))
+ ))))
+ ]
))
);
}