1use crate::{2 binding, bool_val, context_creator, function_default, function_rhs, future_wrapper,3 lazy_binding, lazy_val, Context, ContextCreator, FuncDesc, LazyBinding, ObjMember, ObjValue,4 Val,5};6use closure::closure;7use jsonnet_parser::{8 ArgsDesc, BinaryOpType, BindSpec, Expr, FieldMember, LiteralType, LocExpr, Member, ObjBody,9 ParamsDesc, UnaryOpType, Visibility,10};11use std::{12 collections::{BTreeMap, HashMap},13 rc::Rc,14};1516pub fn evaluate_binding(b: &BindSpec, context_creator: ContextCreator) -> (String, LazyBinding) {17 let b = b.clone();18 if let Some(args) = &b.params {19 let args = args.clone();20 (21 b.name.clone(),22 lazy_binding!(move |this, super_obj| lazy_val!(23 closure!(clone b, clone args, clone context_creator, || evaluate_method(24 context_creator.0(this.clone(), super_obj.clone()),25 &b.value,26 args.clone()27 ))28 )),29 )30 } else {31 (32 b.name.clone(),33 lazy_binding!(move |this, super_obj| {34 lazy_val!(closure!(clone context_creator, clone b, || evaluate(35 context_creator.0(this.clone(), super_obj.clone()),36 &b.value37 )))38 }),39 )40 }41}4243pub fn evaluate_method(ctx: Context, expr: &LocExpr, arg_spec: ParamsDesc) -> Val {44 Val::Func(FuncDesc {45 ctx,46 params: arg_spec,47 eval_rhs: function_rhs!(closure!(clone expr, |ctx| evaluate(ctx, &expr))),48 eval_default: function_default!(|ctx, default| evaluate(ctx, &default)),49 })50}5152pub fn evaluate_field_name(context: Context, field_name: &jsonnet_parser::FieldName) -> String {53 match field_name {54 jsonnet_parser::FieldName::Fixed(n) => n.clone(),55 jsonnet_parser::FieldName::Dyn(expr) => {56 let name = evaluate(context, expr).unwrap_if_lazy();57 match name {58 Val::Str(n) => n,59 _ => panic!(60 "dynamic field name can be only evaluated to 'string', got: {:?}",61 name62 ),63 }64 }65 }66}6768pub fn evaluate_unary_op(op: UnaryOpType, b: &Val) -> Val {69 match (op, b) {70 (o, Val::Lazy(l)) => evaluate_unary_op(o, &l.0()),71 (UnaryOpType::Not, Val::Literal(LiteralType::True)) => Val::Literal(LiteralType::False),72 (UnaryOpType::Not, Val::Literal(LiteralType::False)) => Val::Literal(LiteralType::True),73 (op, o) => panic!("unary op not implemented: {:?} {:?}", op, o),74 }75}7677pub fn evaluate_binary_op(a: &Val, op: BinaryOpType, b: &Val) -> Val {78 match (a, op, b) {79 (Val::Lazy(a), o, b) => evaluate_binary_op(&a.0(), o, b),80 (a, o, Val::Lazy(b)) => evaluate_binary_op(a, o, &b.0()),8182 (Val::Str(v1), BinaryOpType::Add, Val::Str(v2)) => Val::Str(v1.to_owned() + &v2),83 (Val::Str(v1), BinaryOpType::Eq, Val::Str(v2)) => bool_val(v1 == v2),84 (Val::Str(v1), BinaryOpType::Ne, Val::Str(v2)) => bool_val(v1 != v2),8586 (Val::Str(v1), BinaryOpType::Add, Val::Num(v2)) => Val::Str(format!("{}{}", v1, v2)),87 (Val::Str(v1), BinaryOpType::Mul, Val::Num(v2)) => Val::Str(v1.repeat(*v2 as usize)),8889 (Val::Literal(LiteralType::False), BinaryOpType::And, Val::Literal(LiteralType::False)) => {90 bool_val(false)91 }92 (Val::Literal(LiteralType::False), BinaryOpType::And, Val::Literal(LiteralType::True)) => {93 bool_val(false)94 }95 (Val::Literal(LiteralType::True), BinaryOpType::And, Val::Literal(LiteralType::False)) => {96 bool_val(false)97 }98 (Val::Literal(LiteralType::True), BinaryOpType::And, Val::Literal(LiteralType::True)) => {99 bool_val(true)100 }101102 (Val::Obj(v1), BinaryOpType::Add, Val::Obj(v2)) => Val::Obj(v2.with_super(v1.clone())),103104 (Val::Arr(a), BinaryOpType::Add, Val::Arr(b)) => Val::Arr([&a[..], &b[..]].concat()),105106 (Val::Num(v1), BinaryOpType::Mul, Val::Num(v2)) => Val::Num(v1 * v2),107 (Val::Num(v1), BinaryOpType::Div, Val::Num(v2)) => Val::Num(v1 / v2),108 (Val::Num(v1), BinaryOpType::Mod, Val::Num(v2)) => Val::Num(v1 % v2),109110 (Val::Num(v1), BinaryOpType::Add, Val::Num(v2)) => Val::Num(v1 + v2),111 (Val::Num(v1), BinaryOpType::Sub, Val::Num(v2)) => Val::Num(v1 - v2),112113 (Val::Num(v1), BinaryOpType::Lhs, Val::Num(v2)) => {114 Val::Num(((*v1 as i32) << (*v2 as i32)) as f64)115 }116 (Val::Num(v1), BinaryOpType::Rhs, Val::Num(v2)) => {117 Val::Num(((*v1 as i32) >> (*v2 as i32)) as f64)118 }119120 (Val::Num(v1), BinaryOpType::Lt, Val::Num(v2)) => bool_val(v1 < v2),121 (Val::Num(v1), BinaryOpType::Gt, Val::Num(v2)) => bool_val(v1 > v2),122 (Val::Num(v1), BinaryOpType::Lte, Val::Num(v2)) => bool_val(v1 <= v2),123 (Val::Num(v1), BinaryOpType::Gte, Val::Num(v2)) => bool_val(v1 >= v2),124125 (Val::Num(v1), BinaryOpType::Eq, Val::Num(v2)) => bool_val((v1 - v2).abs() < f64::EPSILON),126 (Val::Num(v1), BinaryOpType::Ne, Val::Num(v2)) => bool_val((v1 - v2).abs() > f64::EPSILON),127128 (Val::Num(v1), BinaryOpType::BitAnd, Val::Num(v2)) => {129 Val::Num(((*v1 as i32) & (*v2 as i32)) as f64)130 }131 (Val::Num(v1), BinaryOpType::BitOr, Val::Num(v2)) => {132 Val::Num(((*v1 as i32) | (*v2 as i32)) as f64)133 }134 (Val::Num(v1), BinaryOpType::BitXor, Val::Num(v2)) => {135 Val::Num(((*v1 as i32) ^ (*v2 as i32)) as f64)136 }137 _ => panic!("no rules for binary operation: {:?} {:?} {:?}", a, op, b),138 }139}140141future_wrapper!(HashMap<String, LazyBinding>, FutureNewBindings);142future_wrapper!(ObjValue, FutureObjValue);143144145pub fn evaluate_object(context: Context, object: ObjBody) -> ObjValue {146 match object {147 ObjBody::MemberList(members) => {148 let new_bindings = FutureNewBindings::new();149 let future_this = FutureObjValue::new();150 let context_creator = context_creator!(151 closure!(clone context, clone new_bindings, clone future_this, |this: Option<ObjValue>, super_obj: Option<ObjValue>| {152 context.clone().extend(153 new_bindings.clone().unwrap(),154 context.clone().dollar().clone().or_else(||this.clone()),155 Some(this.unwrap()),156 super_obj157 )158 })159 );160 {161 let mut bindings: HashMap<String, LazyBinding> = HashMap::new();162 for (n, b) in members163 .iter()164 .filter_map(|m| match m {165 Member::BindStmt(b) => Some(b.clone()),166 _ => None,167 })168 .map(|b| evaluate_binding(&b, context_creator.clone()))169 {170 bindings.insert(n, b);171 }172 new_bindings.fill(bindings);173 }174175 let mut new_members = BTreeMap::new();176 for member in members.into_iter() {177 match member {178 Member::Field(FieldMember {179 name,180 plus,181 params: None,182 visibility,183 value,184 }) => {185 let name = evaluate_field_name(context.clone(), &name);186 new_members.insert(187 name,188 ObjMember {189 add: plus,190 visibility: visibility.clone(),191 invoke: binding!(192 closure!(clone value, clone context_creator, |this, super_obj| {193 let context = context_creator.0(this, super_obj);194 195 evaluate(196 context,197 &value,198 ).unwrap_if_lazy()199 })200 ),201 },202 );203 }204 Member::Field(FieldMember {205 name,206 params: Some(params),207 value,208 ..209 }) => {210 let name = evaluate_field_name(context.clone(), &name);211 new_members.insert(212 name,213 ObjMember {214 add: false,215 visibility: Visibility::Hidden,216 invoke: binding!(217 closure!(clone value, clone context_creator, |this, super_obj| {218 219 evaluate_method(220 context_creator.0(this, super_obj),221 &value.clone(),222 params.clone(),223 )224 })225 ),226 },227 );228 }229 Member::BindStmt(_) => {}230 Member::AssertStmt(_) => {}231 }232 }233 future_this.fill(ObjValue::new(None, Rc::new(new_members)))234 }235 _ => todo!(),236 }237}238239pub fn evaluate(context: Context, expr: &LocExpr) -> Val {240 use Expr::*;241 let LocExpr(expr, _location) = expr;242 match &**expr {243 Literal(LiteralType::This) => Val::Obj(244 context245 .this()246 .clone()247 .unwrap_or_else(|| panic!("this not found")),248 ),249 Literal(LiteralType::Super) => Val::Obj(250 context251 .super_obj()252 .clone()253 .unwrap_or_else(|| panic!("super not found")),254 ),255 Literal(t) => Val::Literal(t.clone()),256 Parened(e) => evaluate(context, e),257 Str(v) => Val::Str(v.clone()),258 Num(v) => Val::Num(*v),259 BinaryOp(v1, o, v2) => {260 evaluate_binary_op(&evaluate(context.clone(), v1), *o, &evaluate(context, v2))261 }262 UnaryOp(o, v) => evaluate_unary_op(*o, &evaluate(context, v)),263 Var(name) => Val::Lazy(context.binding(&name)).unwrap_if_lazy(),264 Index(value, index) => {265 match (266 evaluate(context.clone(), value).unwrap_if_lazy(),267 evaluate(context.clone(), index),268 ) {269 (Val::Obj(v), Val::Str(s)) => v270 .get(&s)271 .unwrap_or_else(closure!(clone context, || {272 if let Some(n) = v.get("__intristic_namespace__") {273 if let Val::Str(n) = n.unwrap_if_lazy() {274 Val::Intristic(n, s)275 } else {276 panic!("__intristic_namespace__ should be string");277 }278 } else {279 panic!("{} not found in {:?}", s, v)280 }281 }))282 .unwrap_if_lazy(),283 (Val::Arr(v), Val::Num(n)) => v284 .get(n as usize)285 .unwrap_or_else(|| panic!("out of bounds"))286 .clone(),287 (Val::Str(s), Val::Num(n)) => {288 Val::Str(s.chars().skip(n as usize).take(1).collect())289 }290 (v, i) => todo!("not implemented: {:?}[{:?}]", v, i.unwrap_if_lazy()),291 }292 }293 LocalExpr(bindings, returned) => {294 let mut new_bindings: HashMap<String, LazyBinding> = HashMap::new();295 let future_context = Context::new_future();296297 let context_creator = context_creator!(298 closure!(clone future_context, |_, _| future_context.clone().unwrap())299 );300301 for (k, v) in bindings302 .iter()303 .map(move |b| evaluate_binding(b, context_creator.clone()))304 {305 new_bindings.insert(k, v);306 }307308 let context = context309 .extend(new_bindings, None, None, None)310 .into_future(future_context);311 evaluate(context, &returned.clone())312 }313 Obj(body) => Val::Obj(evaluate_object(context, body.clone())),314 Apply(value, ArgsDesc(args)) => {315 let value = evaluate(context.clone(), value).unwrap_if_lazy();316 match value {317 318 Val::Intristic(ns, name) => match (&ns as &str, &name as &str) {319 ("std", "length") => {320 assert_eq!(args.len(), 1);321 let expr = &args.get(0).unwrap().1;322 match evaluate(context, expr) {323 Val::Str(n) => Val::Num(n.chars().count() as f64),324 Val::Arr(i) => Val::Num(i.len() as f64),325 v => panic!("can't get length of {:?}", v),326 }327 }328 ("std", "type") => {329 assert_eq!(args.len(), 1);330 let expr = &args.get(0).unwrap().1;331 Val::Str(evaluate(context, expr).type_of().to_owned())332 }333 ("std", "makeArray") => {334 assert_eq!(args.len(), 2);335 if let (Val::Num(v), Val::Func(d)) = (336 evaluate(context.clone(), &args[0].1),337 evaluate(context, &args[1].1),338 ) {339 assert!(v > 0.0);340 let mut out = Vec::with_capacity(v as usize);341 for i in 0..v as usize {342 out.push(d.evaluate(vec![(None, Val::Num(i as f64))]))343 }344 Val::Arr(out)345 } else {346 panic!("bad makeArray call");347 }348 }349 ("std", "codepoint") => {350 assert_eq!(args.len(), 1);351 if let Val::Str(s) = evaluate(context, &args[0].1) {352 assert!(353 s.chars().count() == 1,354 "std.codepoint should receive single char string"355 );356 Val::Num(s.chars().take(1).next().unwrap() as u32 as f64)357 } else {358 panic!("bad codepoint call");359 }360 }361 (ns, name) => panic!("Intristic not found: {}.{}", ns, name),362 },363 Val::Func(f) => f.evaluate(364 args.clone()365 .into_iter()366 .map(|a| {367 (368 a.clone().0,369 Val::Lazy(lazy_val!(370 closure!(clone context, clone a, || evaluate(context.clone(), &a.clone().1))371 )),372 )373 })374 .collect(),375 ),376 _ => panic!("{:?} is not a function", value),377 }378 }379 Function(params, body) => evaluate_method(context, body, params.clone()),380 Error(e) => panic!("error: {}", evaluate(context, e)),381 IfElse {382 cond,383 cond_then,384 cond_else,385 } => match evaluate(context.clone(), &cond.0).unwrap_if_lazy() {386 Val::Literal(LiteralType::True) => evaluate(context, cond_then),387 Val::Literal(LiteralType::False) => match cond_else {388 Some(v) => evaluate(context, v),389 None => Val::Literal(LiteralType::False),390 },391 v => panic!("if condition evaluated to {:?} (boolean needed instead)", v),392 },393 _ => panic!("evaluation not implemented: {:?}", expr),394 }395}