1use crate::BoxedLazyVal;2use crate::{3 bool_val, ArgsBinding, BoxedBinding, BoxedContextCreator, ConstantContextCreator, Context,4 FuncDesc, FunctionDefault, FunctionRhs, NoArgsBinding, Val,5};6use crate::{7 future_wrapper, BoxedFunctionDefault, BoxedFunctionRhs, ContextCreator, ObjMember, ObjValue,8 PlainLazyVal,9};10use jsonnet_parser::{11 ArgsDesc, BinaryOpType, BindSpec, Expr, FieldMember, LiteralType, Member, ObjBody, ParamsDesc,12 Visibility,13};14use std::{15 cell::RefCell,16 collections::{BTreeMap, HashMap},17 rc::Rc,18};1920pub fn evaluate_binding<'t>(21 b: &BindSpec,22 context_creator: BoxedContextCreator,23) -> (String, BoxedBinding) {24 if let Some(args) = &b.params {25 (26 b.name.clone(),27 Rc::new(ArgsBinding {28 expr: *b.value.clone(),29 args: args.clone(),30 context_creator: context_creator.clone(),31 }),32 )33 } else {34 (35 b.name.clone(),36 Rc::new(NoArgsBinding {37 expr: *b.value.clone(),38 context_creator: context_creator.clone(),39 }) as BoxedBinding,40 )41 }42}4344#[derive(Debug)]45struct MethodRhs {46 rhs: Expr,47}48impl FunctionRhs for MethodRhs {49 fn evaluate(&self, ctx: Context) -> Val {50 evaluate(ctx, &self.rhs)51 }52}5354#[derive(Debug)]55struct MethodDefault {}56impl FunctionDefault for MethodDefault {57 fn default(&self, ctx: Context, expr: Expr) -> Val {58 evaluate(ctx, &expr)59 }60}6162pub fn evaluate_method(ctx: Context, expr: &Expr, arg_spec: ParamsDesc) -> Val {63 Val::Func(FuncDesc {64 ctx,65 params: arg_spec,66 eval_rhs: BoxedFunctionRhs(Rc::new(MethodRhs { rhs: expr.clone() })),67 eval_default: BoxedFunctionDefault(Rc::new(MethodDefault {})),68 })69}7071pub fn evaluate_field_name(context: Context, field_name: &jsonnet_parser::FieldName) -> String {72 match field_name {73 jsonnet_parser::FieldName::Fixed(n) => n.clone(),74 jsonnet_parser::FieldName::Dyn(expr) => {75 let name = evaluate(context, expr).unwrap_if_lazy();76 match name {77 Val::Str(n) => n.clone(),78 _ => panic!(79 "dynamic field name can be only evaluated to 'string', got: {:?}",80 name81 ),82 }83 }84 }85}8687pub fn evaluate_binary_op(a: &Val, op: BinaryOpType, b: &Val) -> Val {88 match (a, op, b) {89 (Val::Lazy(l), o, r) => evaluate_binary_op(&l.evaluate(), o, r),90 (l, o, Val::Lazy(r)) => evaluate_binary_op(l, o, &r.evaluate()),9192 (Val::Str(v1), BinaryOpType::Add, Val::Str(v2)) => Val::Str(v1.to_owned() + &v2),93 (Val::Str(v1), BinaryOpType::Eq, Val::Str(v2)) => bool_val(v1 == v2),94 (Val::Str(v1), BinaryOpType::Ne, Val::Str(v2)) => bool_val(v1 != v2),9596 (Val::Str(v1), BinaryOpType::Add, Val::Num(v2)) => Val::Str(format!("{}{}", v1, v2)),97 (Val::Str(v1), BinaryOpType::Mul, Val::Num(v2)) => Val::Str(v1.repeat(*v2 as usize)),9899 (Val::Obj(v1), BinaryOpType::Add, Val::Obj(v2)) => Val::Obj(v2.with_super(v1.clone())),100101 (Val::Arr(a), BinaryOpType::Add, Val::Arr(b)) => Val::Arr([&a[..], &b[..]].concat()),102103 (Val::Num(v1), BinaryOpType::Mul, Val::Num(v2)) => Val::Num(v1 * v2),104 (Val::Num(v1), BinaryOpType::Div, Val::Num(v2)) => Val::Num(v1 / v2),105 (Val::Num(v1), BinaryOpType::Mod, Val::Num(v2)) => Val::Num(v1 % v2),106107 (Val::Num(v1), BinaryOpType::Add, Val::Num(v2)) => Val::Num(v1 + v2),108 (Val::Num(v1), BinaryOpType::Sub, Val::Num(v2)) => Val::Num(v1 - v2),109110 (Val::Num(v1), BinaryOpType::Lhs, Val::Num(v2)) => {111 Val::Num(((*v1 as i32) << (*v2 as i32)) as f64)112 }113 (Val::Num(v1), BinaryOpType::Rhs, Val::Num(v2)) => {114 Val::Num(((*v1 as i32) >> (*v2 as i32)) as f64)115 }116117 (Val::Num(v1), BinaryOpType::Lt, Val::Num(v2)) => bool_val(v1 < v2),118 (Val::Num(v1), BinaryOpType::Gt, Val::Num(v2)) => bool_val(v1 > v2),119 (Val::Num(v1), BinaryOpType::Lte, Val::Num(v2)) => bool_val(v1 <= v2),120 (Val::Num(v1), BinaryOpType::Gte, Val::Num(v2)) => bool_val(v1 >= v2),121122 (Val::Num(v1), BinaryOpType::Eq, Val::Num(v2)) => bool_val(v1 == v2),123 (Val::Num(v1), BinaryOpType::Ne, Val::Num(v2)) => bool_val(v1 != v2),124125 (Val::Num(v1), BinaryOpType::BitAnd, Val::Num(v2)) => {126 Val::Num(((*v1 as i32) & (*v2 as i32)) as f64)127 }128 (Val::Num(v1), BinaryOpType::BitOr, Val::Num(v2)) => {129 Val::Num(((*v1 as i32) | (*v2 as i32)) as f64)130 }131 (Val::Num(v1), BinaryOpType::BitXor, Val::Num(v2)) => {132 Val::Num(((*v1 as i32) ^ (*v2 as i32)) as f64)133 }134 _ => panic!("no rules for binary operation: {:?} {:?} {:?}", a, op, b),135 }136}137138future_wrapper!(HashMap<String, BoxedBinding>, FutureNewBindings);139140#[derive(Debug)]141pub struct ObjectContextCreator {142 original: Context,143 future_bindings: FutureNewBindings,144}145146impl ContextCreator for ObjectContextCreator {147 fn create_context(&self, this: &Option<ObjValue>, super_obj: &Option<ObjValue>) -> Context {148 self.original.extend(149 self.future_bindings.clone().unwrap(),150 self.original.dollar().clone().or_else(|| this.clone()),151 this.clone(),152 super_obj.clone(),153 )154 }155}156157158pub fn evaluate_object(context: Context, object: ObjBody) -> ObjValue {159 match object {160 ObjBody::MemberList(members) => {161 let future_bindings = FutureNewBindings::new();162 let binding_context_creator = Rc::new(ObjectContextCreator {163 future_bindings: future_bindings.clone(),164 original: context.clone(),165 });166 let mut bindings: HashMap<String, BoxedBinding> = HashMap::new();167 for (n, b) in members168 .iter()169 .filter_map(|m| match m {170 Member::BindStmt(b) => Some(b.clone()),171 _ => None,172 })173 .map(|b| evaluate_binding(&b, binding_context_creator.clone()))174 {175 bindings.insert(n, b);176 }177 let bindings = future_bindings.fill(bindings);178 let mut new_members = BTreeMap::new();179 for member in members.iter() {180 match member {181 Member::Field(FieldMember {182 name,183 plus,184 params: None,185 visibility,186 value,187 }) => {188 let name = evaluate_field_name(context.clone(), name);189 new_members.insert(190 name,191 ObjMember {192 add: *plus,193 visibility: visibility.clone(),194 invoke: Rc::new(NoArgsBinding {195 context_creator: binding_context_creator.clone(),196 expr: value.clone(),197 }),198 },199 );200 }201 Member::Field(FieldMember {202 name,203 params: Some(params),204 value,205 ..206 }) => {207 let name = evaluate_field_name(context.clone(), name);208 new_members.insert(209 name,210 ObjMember {211 add: false,212 visibility: Visibility::Hidden,213 invoke: Rc::new(ArgsBinding {214 expr: value.clone(),215 args: params.clone(),216 context_creator: binding_context_creator.clone(),217 }),218 },219 );220 }221 Member::BindStmt(_) => {}222 Member::AssertStmt(_) => {}223 _ => todo!(),224 }225 }226 ObjValue::new(None, Rc::new(new_members))227 }228 _ => todo!(),229 }230}231232pub fn evaluate(context: Context, expr: &Expr) -> Val {233 use Expr::*;234 match &*expr {235 Literal(t) => Val::Literal(t.clone()),236 Parened(e) => evaluate(context, e),237 Str(v) => Val::Str(v.clone()),238 Num(v) => Val::Num(*v),239 BinaryOp(v1, o, v2) => {240 evaluate_binary_op(&evaluate(context.clone(), v1), *o, &evaluate(context, v2))241 }242 Var(name) => {243 let variable = context.binding(&name);244 let val = variable.evaluate(None, None);245 val246 }247 Index(box value, box index) => {248 match (249 evaluate(context.clone(), value).unwrap_if_lazy(),250 evaluate(context.clone(), index),251 ) {252 (Val::Literal(LiteralType::Super), _idx) => todo!(),253 (Val::Literal(LiteralType::This), idx) => match &idx.unwrap_if_lazy() {254 Val::Str(str) => context255 .this()256 .clone()257 .unwrap_or_else(|| panic!("'this' is not defined in current context"))258 .get_raw(str, None)259 .unwrap_or_else(|| {260 panic!(261 "key {} not found in current context 'this' ({:?})",262 str,263 context.this()264 )265 }),266 _ => panic!("bad index"),267 },268 (Val::Obj(v), Val::Str(s)) => v269 .get_raw(&s, None)270 .unwrap_or_else(|| panic!("{} not found in {:?}", s, v)),271 (v, i) => todo!("not implemented: {:?}[{:?}]", v, i.unwrap_if_lazy()),272 }273 }274 LocalExpr(bindings, returned) => {275 let mut new_bindings: HashMap<String, BoxedBinding> = HashMap::new();276 let future_context = Context::new_future();277278 let context_creator = Rc::new(ConstantContextCreator {279 context: future_context.clone(),280 });281 for (k, v) in bindings282 .iter()283 .map(move |b| evaluate_binding(b, context_creator.clone()))284 {285 new_bindings.insert(k, v);286 }287288 let context = context289 .extend(new_bindings, None, None, None)290 .into_future(future_context);291 evaluate(context, &*returned.clone())292 }293 Obj(body) => Val::Obj(evaluate_object(context, body.clone())),294 Apply(box value, ArgsDesc(args)) => {295 let value = evaluate(context.clone(), value).unwrap_if_lazy();296 match value {297 Val::Func(f) => f.evaluate(298 args.clone()299 .into_iter()300 .map(|a| {301 (302 a.0,303 Val::Lazy(BoxedLazyVal(Rc::new(PlainLazyVal {304 context: context.clone(),305 expr: *a.1,306 }))),307 )308 })309 .collect(),310 ),311 _ => panic!("{:?} is not a function", value),312 }313 }314 Function(params, body) => evaluate_method(context, body, params.clone()),315 Error(e) => panic!("error: {}", evaluate(context, e)),316 IfElse {317 cond,318 cond_then,319 cond_else,320 } => match evaluate(context.clone(), &cond.0).unwrap_if_lazy() {321 Val::Literal(LiteralType::True) => evaluate(context.clone(), cond_then),322 Val::Literal(LiteralType::False) => match cond_else {323 Some(v) => evaluate(context.clone(), v),324 None => Val::Literal(LiteralType::False),325 },326 v => panic!("if condition evaluated to {:?} (boolean needed instead)", v),327 },328 _ => panic!("evaluation not implemented: {:?}", expr),329 }330}