1use crate::ast::{self, BinaryOp, Expr, Program, Stmt, UnaryOp};
4use crate::error::{JsonnetError, Result};
5use crate::value::{JsonnetBuiltin, JsonnetFunction, JsonnetValue};
6use std::collections::HashMap;
7
8pub struct Evaluator {
10 globals: HashMap<String, JsonnetValue>,
12}
13
14impl Evaluator {
15 pub fn new() -> Self {
17 let mut evaluator = Evaluator {
18 globals: HashMap::new(),
19 };
20 evaluator.init_stdlib();
21 evaluator
22 }
23
24 fn init_stdlib(&mut self) {
26
27 let mut std_object = HashMap::new();
29
30 std_object.insert("length".to_string(), JsonnetValue::Builtin(JsonnetBuiltin::Length));
32
33 let std_functions = vec![
36 "type", "makeArray", "filter", "map", "foldl", "foldr", "range", "join", "split",
37 "contains", "startsWith", "endsWith", "substr", "char", "codepoint", "toString",
38 "parseInt", "parseJson", "encodeUTF8", "decodeUTF8", "md5", "base64", "base64Decode",
39 "manifestJson", "manifestJsonEx", "manifestYaml", "escapeStringJson", "escapeStringYaml",
40 "escapeStringPython", "escapeStringBash", "escapeStringDollars", "stringChars", "stringBytes",
41 "format", "isArray", "isBoolean", "isFunction", "isNumber", "isObject", "isString",
42 "count", "find", "member", "modulo", "pow", "exp", "log", "sqrt", "sin", "cos", "tan",
43 "asin", "acos", "atan", "floor", "ceil", "round", "abs", "max", "min", "clamp",
44 "assertEqual", "sort", "uniq", "reverse", "mergePatch", "get", "objectFields",
45 "objectFieldsAll", "objectHas", "objectHasAll", "objectValues", "objectValuesAll",
46 "prune", "mapWithKey", "toLower", "toUpper", "trim", "trace", "all", "any",
47 "id", "equals", "lines", "strReplace", "sha1", "sha256", "sha3", "sha512",
48 "asciiLower", "asciiUpper", "set", "setMember", "setUnion", "setInter", "setDiff",
49 "flatMap", "mapWithIndex", "lstripChars", "rstripChars", "stripChars", "findSubstr", "repeat",
50 "manifestIni", "manifestPython", "manifestCpp", "manifestXmlJsonml",
51 "log2", "log10", "log1p", "expm1"
52 ];
53
54 for func_name in std_functions {
55 std_object.insert(func_name.to_string(), JsonnetValue::Builtin(JsonnetBuiltin::StdLibFunction(func_name.to_string())));
56 }
57
58 self.globals.insert("std".to_string(), JsonnetValue::Object(std_object));
59 }
60
61 pub fn evaluate_file(&mut self, source: &str, _filename: &str) -> Result<JsonnetValue> {
63 use crate::parser::Parser;
64
65 let mut parser = Parser::new();
67 match parser.parse_expression(source) {
68 Ok(expr) => self.evaluate_expression(&expr),
69 Err(_) => {
70 let mut program_parser = Parser::new();
72 let program = program_parser.parse(source)?;
73 self.evaluate_program(program)
74 }
75 }
76 }
77
78
79
80 fn evaluate_program(&mut self, program: Program) -> Result<JsonnetValue> {
82 let mut result = JsonnetValue::Null;
83
84 for stmt in program.statements {
85 result = self.evaluate_statement(&stmt)?;
86 }
87
88 Ok(result)
89 }
90
91 fn evaluate_statement(&mut self, stmt: &Stmt) -> Result<JsonnetValue> {
93 match stmt {
94 Stmt::Expr(expr) => self.evaluate_expression(expr),
95 Stmt::Local(bindings) => {
96 let mut scope = HashMap::new();
97
98 for (name, expr) in bindings {
100 let value = self.evaluate_expression(expr)?;
101 scope.insert(name.clone(), value);
102 }
103
104 if let Some((_, expr)) = bindings.last() {
107 self.evaluate_expression(expr)
108 } else {
109 Ok(JsonnetValue::Null)
110 }
111 }
112 Stmt::Assert { cond, message } => {
113 let cond_value = self.evaluate_expression(cond)?;
114 if !cond_value.is_truthy() {
115 let msg = if let Some(msg_expr) = message {
116 match self.evaluate_expression(msg_expr)? {
117 JsonnetValue::String(s) => s,
118 _ => "Assertion failed".to_string(),
119 }
120 } else {
121 "Assertion failed".to_string()
122 };
123 return Err(JsonnetError::runtime_error(&msg));
124 }
125 Ok(JsonnetValue::Null) }
127 }
128 }
129
130 fn evaluate_expression(&mut self, expr: &Expr) -> Result<JsonnetValue> {
132 match expr {
133 Expr::Literal(value) => Ok(value.clone()),
134 Expr::StringInterpolation(parts) => {
135 let mut result = String::new();
136 for part in parts {
137 match part {
138 ast::StringInterpolationPart::Literal(s) => result.push_str(s),
139 ast::StringInterpolationPart::Interpolation(expr) => {
140 let value = self.evaluate_expression(expr)?;
141 match value {
142 JsonnetValue::String(s) => result.push_str(&s),
143 JsonnetValue::Number(n) => result.push_str(&n.to_string()),
144 JsonnetValue::Boolean(b) => result.push_str(&b.to_string()),
145 JsonnetValue::Null => result.push_str("null"),
146 _ => result.push_str(&value.to_string()),
147 }
148 }
149 }
150 }
151 Ok(JsonnetValue::string(result))
152 }
153 Expr::Var(name) => {
154 if let Some(value) = self.globals.get(name) {
155 Ok(value.clone())
156 } else {
157 Err(JsonnetError::runtime_error(format!("Undefined variable: {}", name)))
158 }
159 }
160 Expr::BinaryOp { left, op, right } => {
161 let left_val = self.evaluate_expression(left)?;
162 let right_val = self.evaluate_expression(right)?;
163 self.evaluate_binary_op(*op, left_val, right_val)
164 }
165 Expr::UnaryOp { op, expr } => {
166 let val = self.evaluate_expression(expr)?;
167 self.evaluate_unary_op(*op, val)
168 }
169 Expr::Array(elements) => {
170 let mut values = Vec::new();
171 for elem in elements {
172 values.push(self.evaluate_expression(elem)?);
173 }
174 Ok(JsonnetValue::Array(values))
175 }
176 Expr::ArrayComp { expr, var, array, cond } => {
177 let array_val = self.evaluate_expression(array)?;
178 let mut result = Vec::new();
179
180 if let JsonnetValue::Array(elements) = array_val {
181 for element in elements {
182 let old_globals = self.globals.clone();
184 self.globals.insert(var.clone(), element.clone());
185
186 let condition_met = if let Some(cond_expr) = cond {
188 let cond_val = self.evaluate_expression(cond_expr)?;
189 cond_val.is_truthy()
190 } else {
191 true
192 };
193
194 if condition_met {
195 let value = self.evaluate_expression(expr)?;
196 result.push(value);
197 }
198
199 self.globals = old_globals;
201 }
202 } else {
203 return Err(JsonnetError::RuntimeError {
204 message: "Array comprehension requires an array".to_string(),
205 });
206 }
207
208 Ok(JsonnetValue::Array(result))
209 }
210 Expr::Object(fields) => {
211 let mut object = HashMap::new();
212 for field in fields {
213 let key = self.evaluate_object_field_key(&field.name)?;
214 let value = self.evaluate_expression(&field.expr)?;
215 object.insert(key, value);
216 }
217 Ok(JsonnetValue::Object(object))
218 }
219 Expr::Call { func, args } => {
220 let func_val = self.evaluate_expression(func)?;
221 let mut arg_vals = Vec::new();
222 for arg in args {
223 arg_vals.push(self.evaluate_expression(arg)?);
224 }
225 self.call_function(func_val, arg_vals)
226 }
227 Expr::Index { target, index } => {
228 let target_val = self.evaluate_expression(target)?;
229 let index_val = self.evaluate_expression(index)?;
230 self.index_access(target_val, index_val)
231 }
232 Expr::Local { bindings, body } => {
233 let mut local_scope = HashMap::new();
235
236 for (name, expr) in bindings {
238 let value = self.evaluate_expression(expr)?;
239 local_scope.insert(name.clone(), value);
240 }
241
242 let old_globals = self.globals.clone();
244 for (name, value) in &local_scope {
245 self.globals.insert(name.clone(), value.clone());
246 }
247
248 let result = self.evaluate_expression(body);
249
250 self.globals = old_globals;
252
253 result
254 }
255 Expr::Function { parameters, body } => {
256 Ok(JsonnetValue::Function(JsonnetFunction::new(parameters.clone(), Box::new((**body).clone()), HashMap::new())))
257 }
258 Expr::If { cond, then_branch, else_branch } => {
259 let cond_val = self.evaluate_expression(cond)?;
260 if cond_val.is_truthy() {
261 self.evaluate_expression(then_branch)
262 } else if let Some(else_expr) = else_branch {
263 self.evaluate_expression(else_expr)
264 } else {
265 Ok(JsonnetValue::Null)
266 }
267 }
268 _ => Err(JsonnetError::runtime_error("Expression type not implemented yet")),
269 }
270 }
271
272 fn evaluate_binary_op(&self, op: BinaryOp, left: JsonnetValue, right: JsonnetValue) -> Result<JsonnetValue> {
274 match op {
275 BinaryOp::Add => left.add(&right),
276 BinaryOp::Sub => left.sub(&right),
277 BinaryOp::Mul => left.mul(&right),
278 BinaryOp::Div => left.div(&right),
279 BinaryOp::Mod => left.modulo(&right),
280 BinaryOp::Lt => left.lt(&right),
281 BinaryOp::Le => left.le(&right),
282 BinaryOp::Gt => left.gt(&right),
283 BinaryOp::Ge => left.ge(&right),
284 BinaryOp::Eq => left.eq(&right),
285 BinaryOp::Ne => left.ne(&right),
286 BinaryOp::And => left.and(&right),
287 BinaryOp::Or => left.or(&right),
288 _ => Err(JsonnetError::runtime_error("Binary operator not implemented")),
289 }
290 }
291
292 fn evaluate_unary_op(&self, op: UnaryOp, value: JsonnetValue) -> Result<JsonnetValue> {
294 match op {
295 UnaryOp::Not => value.not(),
296 UnaryOp::Neg => value.neg(),
297 UnaryOp::Pos => Ok(value),
298 UnaryOp::BitNot => Err(JsonnetError::runtime_error("Bitwise NOT not implemented")),
299 }
300 }
301
302 fn evaluate_object_field_key(&mut self, field_name: &ast::FieldName) -> Result<String> {
304 match field_name {
305 ast::FieldName::Fixed(name) => Ok(name.clone()),
306 ast::FieldName::Computed(expr) => {
307 match self.evaluate_expression(expr)? {
308 JsonnetValue::String(s) => Ok(s),
309 _ => Err(JsonnetError::runtime_error("Computed field name must evaluate to a string")),
310 }
311 }
312 }
313 }
314
315 fn call_function(&mut self, func: JsonnetValue, args: Vec<JsonnetValue>) -> Result<JsonnetValue> {
317 match func {
318 JsonnetValue::Function(f) => {
319 if args.len() != f.parameters.len() {
320 return Err(JsonnetError::runtime_error(
321 format!("Expected {} arguments, got {}", f.parameters.len(), args.len())
322 ));
323 }
324
325 let old_globals = self.globals.clone();
327 for (param, arg) in f.parameters.iter().zip(args) {
328 self.globals.insert(param.clone(), arg);
329 }
330
331 let result = self.evaluate_expression(&f.body);
332
333 self.globals = old_globals;
335
336 result
337 }
338 JsonnetValue::Builtin(builtin) => {
339 builtin.call(args)
340 }
341 _ => Err(JsonnetError::runtime_error("Cannot call non-function value")),
342 }
343 }
344
345 fn index_access(&self, target: JsonnetValue, index: JsonnetValue) -> Result<JsonnetValue> {
347 match (&target, &index) {
348 (JsonnetValue::Array(arr), JsonnetValue::Number(idx)) => {
349 let idx = *idx as usize;
350 if idx >= arr.len() {
351 return Err(JsonnetError::runtime_error("Array index out of bounds"));
352 }
353 Ok(arr[idx].clone())
354 }
355 (JsonnetValue::Object(obj), JsonnetValue::String(key)) => {
356 if let Some(value) = obj.get(key) {
357 Ok(value.clone())
358 } else {
359 Ok(JsonnetValue::Null) }
361 }
362 _ => Err(JsonnetError::runtime_error("Invalid index operation")),
363 }
364 }
365}
366
367#[cfg(test)]
368mod tests {
369 use super::*;
370
371 #[test]
372 fn test_evaluate_null() {
373 let mut evaluator = Evaluator::new();
374 let result = evaluator.evaluate_file("null", "");
375 assert!(result.is_ok());
376 assert_eq!(result.unwrap(), JsonnetValue::Null);
377 }
378
379 #[test]
380 fn test_evaluate_boolean() {
381 let mut evaluator = Evaluator::new();
382 let result = evaluator.evaluate_file("true", "");
383 assert!(result.is_ok());
384 assert_eq!(result.unwrap(), JsonnetValue::boolean(true));
385 }
386
387 #[test]
388 fn test_evaluate_number() {
389 let mut evaluator = Evaluator::new();
390 let result = evaluator.evaluate_file("42", "");
391 assert!(result.is_ok());
392 assert_eq!(result.unwrap(), JsonnetValue::number(42.0));
393 }
394
395 #[test]
396 fn test_evaluate_string() {
397 let mut evaluator = Evaluator::new();
398 let result = evaluator.evaluate_file(r#""hello""#, "");
399 assert!(result.is_ok());
400 assert_eq!(result.unwrap(), JsonnetValue::string("hello"));
401 }
402
403 #[test]
404 fn test_evaluate_arithmetic() {
405 let mut evaluator = Evaluator::new();
406
407 let result = evaluator.evaluate_file("2 + 3", "");
408 assert!(result.is_ok());
409 assert_eq!(result.unwrap(), JsonnetValue::number(5.0));
410
411 let result = evaluator.evaluate_file("10 - 4", "");
412 assert!(result.is_ok());
413 assert_eq!(result.unwrap(), JsonnetValue::number(6.0));
414
415 let result = evaluator.evaluate_file("3 * 4", "");
416 assert!(result.is_ok());
417 assert_eq!(result.unwrap(), JsonnetValue::number(12.0));
418
419 let result = evaluator.evaluate_file("8 / 2", "");
420 assert!(result.is_ok());
421 assert_eq!(result.unwrap(), JsonnetValue::number(4.0));
422 }
423
424 #[test]
425 fn test_evaluate_string_concatenation() {
426 let mut evaluator = Evaluator::new();
427 let result = evaluator.evaluate_file(r#""hello" + " " + "world""#, "");
428 assert!(result.is_ok());
429 assert_eq!(result.unwrap(), JsonnetValue::string("hello world"));
430 }
431
432 #[test]
433 fn test_evaluate_std_length() {
434 let mut evaluator = Evaluator::new();
435
436 let result = evaluator.evaluate_file("std.length([1, 2, 3])", "");
437 assert!(result.is_ok());
438 assert_eq!(result.unwrap(), JsonnetValue::number(3.0));
439
440 let result = evaluator.evaluate_file(r#"std.length("hello")"#, "");
441 assert!(result.is_ok());
442 assert_eq!(result.unwrap(), JsonnetValue::number(5.0));
443 }
444
445 #[test]
446 fn test_evaluate_division_by_zero() {
447 let mut evaluator = Evaluator::new();
448 let result = evaluator.evaluate_file("1 / 0", "");
449 assert!(result.is_err());
450 match result.err().unwrap() {
451 JsonnetError::RuntimeError { message: _ } => {}, _ => panic!("Expected runtime error"),
453 }
454 }
455
456 #[test]
457 fn test_evaluate_undefined_variable() {
458 let mut evaluator = Evaluator::new();
459 let result = evaluator.evaluate_file("undefined_var", "");
460 assert!(result.is_err());
461 match result.err().unwrap() {
462 JsonnetError::RuntimeError { message: _ } => {}, _ => panic!("Expected runtime error"),
464 }
465 }
466}
467
468impl Default for Evaluator {
469 fn default() -> Self {
470 Self::new()
471 }
472}