git.delta.rocks / jrsonnet / refs/commits / 078724755e3c

difftreelog

source

crates/jsonnet-evaluator/src/evaluate.rs9.4 KiBsourcehistory
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}156157// TODO: Asserts158pub 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}