mini_builder_rs/evaluator/
mod.rs

1//! Transformation of the AST into [Block]s that can be evaluated into strings.
2//!
3//! ASTs can't be directly evaluated because the trees they build describe the
4//! order of operations of expressions, not control flow. They have no notion
5//! of scoping and branching.
6//! The Control flow is described by a tree of [Block]s
7//!
8//! # [Block]
9//! A [Block] can either hold a sequence of [super::parser::Node] are evaluated
10//! in order, or a sequence of [Block]s which are used for branching.
11//!
12//! # [EvaluationContext]
13//! Anything data that expressions reference that is not literals is stored in a
14//! context. This includes variables, functions, and builders.
15//!
16//! # [evaluator::Evaluator] And [evaluator::EvaluatorBuilder]
17//! An [evaluator::Evaluator] holds a sequence of [Block]s.
18//! The [evaluator::EvaluatorBuilder] takes a sequence of [crate::parser::Node]s
19//! and constructs an [evaluator::Evaluator].
20//!
21//! # [contained_evaluator::ContainedEvaluator]
22//! [evaluator::Evaluator] has two lifetime bounds which is inconvenient in some
23//! cases. [contained_evaluator::ContainedEvaluator] is a self referential
24//! struct that eliminates these lifetime bounds and can be much more easily
25//! used.
26//!
27//! # [Dependencies]
28//! This struct holds all the items the evaluator references such as variables,
29//! functions, and builders. This information is used by some of the components
30//! of [crate::builder]
31//!
32//! # Example
33//! ```rust
34//! let source = "is a greater than b? {{ a > b ? 'yes' : 'no' }}";
35//! // tokenize, parse, and build the evaluator
36//! let tokens = Tokenizer::new(source, TokenizerOptions::default())
37//!     .tokenize()
38//!     .unwrap();
39//! let nodes = Parser::new(tokens.as_slice()).parse().unwrap();
40//! let mut evaluator_builder = EvaluatorBuilder::new(nodes.as_slice());
41//! let evaluator = evaluator_builder.build_evaluator().unwrap();
42//!
43//! // prepare the variables
44//! let global_variables = HashMap::new();
45//! let local_variables = vec![HashMap::from_iter(
46//!     [
47//!         ("a".to_string(), Value::Number(2.0)),
48//!         ("b".to_string(), Value::Number(1.0)),
49//!     ]
50//!     .into_iter(),
51//! )];
52//! let functions = HashMap::new();
53//! let builders = HashMap::<String, Evaluator>::new();
54//!
55//! // create the context and evaluate
56//! let mut context = EvaluationContext::new(
57//!     &global_variables,
58//!     local_variables,
59//!     &builders,
60//!     &functions,
61//!     Default::default(),
62//! );
63//! println!("{}", evaluator.evaluate(&mut context));
64//! // the output will be: `is a greater than b? yes`
65//! ```
66
67pub mod block;
68pub mod contained_evaluator;
69pub mod dependencies;
70pub mod evaluation_context;
71pub mod evaluator;
72pub mod evaluator_builder_error;
73
74use std::{borrow::Borrow, collections::HashMap};
75
76use hmap::hmap;
77
78use crate::{parser::expression::Expression, tokenizer::token::TokenType, value::Value};
79
80use self::{
81	block::Block,
82	dependencies::Dependencies,
83	evaluation_context::{ContextLocation, EvaluationContext},
84};
85
86pub type Variables = HashMap<String, Value>;
87pub type ValueFunction = Box<dyn Fn(&[Value]) -> Value + 'static>;
88
89/// Every evaluator should have function to evaluate itself with a context,
90/// a dependencies getter.
91pub trait Evaluate {
92	fn evaluate<'ea>(&self, context: &mut EvaluationContext<'ea, impl Evaluate>) -> String;
93
94	fn get_dependencies(&self) -> &Dependencies;
95}
96
97/// Evaluates an expression in a context and returns its value.
98pub fn evaluate_expression<'c>(
99	context: &mut EvaluationContext<'c, impl Evaluate>,
100	expression: &Expression,
101) -> Value {
102	match expression {
103		// arithmetic
104		Expression::Additive(add, subtract) => {
105			// convert to iterators
106			let mut add = add.iter();
107			let mut subtract = subtract.iter();
108
109			// take the first value from add if exists, otherwise from subtract
110			let mut value = match add.next() {
111				Some(expression) => evaluate_expression(context, expression.borrow()),
112				None => evaluate_expression(context, subtract.next().unwrap().borrow()),
113			};
114
115			// add remaining
116			for expression in add {
117				value = value.add(&evaluate_expression(context, expression.borrow()));
118			}
119
120			// subtract remaining
121			for expression in subtract {
122				value = value.sub(&evaluate_expression(context, expression.borrow()));
123			}
124
125			value
126		}
127		Expression::Multiplicative(multiply, divide) => {
128			// convert to iterators
129			let mut multiply = multiply.iter();
130			let mut divide = divide.iter();
131
132			// take the first value from multiply if exists, otherwise from divide
133			let mut value = match multiply.next() {
134				Some(expression) => evaluate_expression(context, expression.borrow()),
135				None => evaluate_expression(context, divide.next().unwrap().borrow()),
136			};
137
138			// multiply remaining
139			for expression in multiply {
140				value = value.mul(&evaluate_expression(context, expression.borrow()));
141			}
142
143			// divide remaining
144			for expression in divide {
145				value = value.div(&evaluate_expression(context, expression.borrow()));
146			}
147
148			value
149		}
150		Expression::Negate(expression) => evaluate_expression(context, expression.borrow()).neg(),
151		// logic
152		Expression::Or(expressions) => expressions
153			.iter()
154			.map(|expression| evaluate_expression(context, expression.borrow()))
155			.reduce(|acc, x| acc.or(&x))
156			.unwrap(),
157		Expression::And(expressions) => expressions
158			.iter()
159			.map(|expression| evaluate_expression(context, expression.borrow()))
160			.reduce(|acc, x| acc.and(&x))
161			.unwrap(),
162		Expression::Not(expression) => evaluate_expression(context, expression.borrow()).not(),
163		// compare
164		Expression::Comparison(lhs, op, rhs) => {
165			let lhs = evaluate_expression(context, lhs.borrow());
166			let rhs = evaluate_expression(context, rhs.borrow());
167			match op {
168				TokenType::SmallerThan => lhs.less_than(&rhs),
169				TokenType::SmallerEquals => lhs.less_equals(&rhs),
170				TokenType::GreaterThan => lhs.greater_than(&rhs),
171				TokenType::GreaterEquals => lhs.greater_than(&rhs),
172				TokenType::Equals => lhs.equals(&rhs),
173				TokenType::NotEquals => lhs.not_equals(&rhs),
174				_ => Value::None,
175			}
176		}
177		// value
178		Expression::Value(value) => value.clone(),
179		Expression::Variable(name) => context.get_variable_value(*name).unwrap_or(Value::None),
180		Expression::ListValue(expressions) => Value::List(
181			expressions
182				.iter()
183				.map(|expression| evaluate_expression(context, expression))
184				.collect(),
185		),
186		Expression::Builder(name, args) => {
187			if let Some(builder) = context.get_evaluator(name) {
188				// evaluate the arguments
189				let mut local_variables = HashMap::new();
190				for b in args {
191					let (name, expression) = b.borrow();
192					local_variables
193						.insert(name.to_string(), evaluate_expression(context, expression));
194				}
195
196				// create a new context with the evaluated arguments
197				if let Ok(mut context) =
198					context.spawn_new(vec![local_variables], ContextLocation::template(name))
199				{
200					// evaluate the builder and return the output text
201					Value::Text(builder.evaluate(&mut context))
202				} else {
203					// if the new context could not be created, return an empty
204					// string
205					Value::text("")
206				}
207			} else {
208				Value::Text(String::new())
209			}
210		}
211		Expression::FunctionCall(name, args) => {
212			if let Some(function) = context.get_function(name) {
213				let args: Vec<Value> = args
214					.iter()
215					.map(|arg| evaluate_expression(context, arg))
216					.collect::<Vec<_>>();
217				function(&args)
218			} else {
219				Value::None
220			}
221		}
222		Expression::IndexOf(expression, index_expression) => {
223			let expression_value = evaluate_expression(context, expression);
224			let index_value = evaluate_expression(context, index_expression);
225			if let Value::List(l) = expression_value {
226				if let Value::Number(i) = index_value {
227					// TODO: round check
228					let i = i.round() as usize;
229					// TODO: is there a better way to move out a value
230					return l.into_iter().skip(i).next().unwrap();
231				}
232			}
233
234			Value::None
235		}
236		// control flow
237		Expression::Ternary(condition, a, b) => {
238			if evaluate_expression(context, condition.borrow()).bool_or_false() {
239				evaluate_expression(context, a.borrow())
240			} else {
241				evaluate_expression(context, b.borrow())
242			}
243		}
244	}
245}
246
247/// Calls [evaluate_block] in order and concatenates the output
248pub fn evaluate_blocks<'a, 'b, 'c>(
249	context: &mut EvaluationContext<'c, impl Evaluate>,
250	blocks: &[Box<Block<'a, 'b>>],
251) -> String {
252	blocks
253		.iter()
254		.map(|block| evaluate_block(context, block.borrow()))
255		.collect::<Vec<_>>()
256		.join("")
257}
258
259/// Evaluates a block in a context and returns the evaluated text.
260pub fn evaluate_block<'a, 'b, 'c>(
261	context: &mut EvaluationContext<'c, impl Evaluate>,
262	block: &Block,
263) -> String {
264	match block {
265		Block::Source(s) => s.to_string(),
266		Block::Expression(expression) => evaluate_expression(context, *expression).to_string(),
267		Block::Assignment(name, _tt, expression) => {
268			let value = evaluate_expression(context, *expression);
269			context.assign_local_variable(name, value);
270			String::new()
271		}
272		Block::If(if_block) => {
273			let mut s = None;
274
275			// iterate over all the if arms
276			for (expression, blocks) in &if_block.if_blocks {
277				if evaluate_expression(context, expression).bool_or_false() {
278					s = Some(evaluate_blocks(context, &blocks));
279					break;
280				}
281			}
282
283			// if none of the if arms were triggered, evaluate the else block, otherwise return the already evaluated value
284			if let Some(s) = s {
285				s
286			} else {
287				if let Some(else_blocks) = &if_block.else_blocks {
288					// evaluate all the blocks into strings and join
289					evaluate_blocks(context, &else_blocks)
290				} else {
291					String::new()
292				}
293			}
294		}
295		Block::For(for_block) => {
296			let value = evaluate_expression(context, for_block.expression);
297			let var_name = for_block.var_name;
298			if let Value::List(l) = value {
299				// vector to hold all the evaluated blocks' outputs
300				let mut outs = Vec::new();
301
302				// iterate over each value of list
303				for value in l {
304					// add the for loop's variable
305					context.push_variables(hmap!(
306						var_name.to_string() => value
307					));
308
309					// evaluate and push each of the for loop's inner blocks to the output
310					for block in &for_block.blocks {
311						outs.push(evaluate_block(context, block));
312					}
313
314					// remove the for loop's variable
315					context.pop_variables();
316				}
317
318				outs.join("")
319			} else {
320				String::new()
321			}
322		}
323	}
324}
325
326/// Helper function to list all the dependencies an expression references and
327/// add them to the dependencies.
328pub fn list_dependencies_expression(expression: &Expression, dependencies: &mut Dependencies) {
329	match expression {
330		Expression::Additive(a, b) | Expression::Multiplicative(a, b) => {
331			for expression in a.iter().chain(b.iter()) {
332				list_dependencies_expression(expression.borrow(), dependencies);
333			}
334		}
335		Expression::Negate(a) | Expression::Not(a) => {
336			list_dependencies_expression(a.borrow(), dependencies);
337		}
338		Expression::Or(a) | Expression::And(a) => {
339			for expression in a {
340				list_dependencies_expression(expression.borrow(), dependencies);
341			}
342		}
343		Expression::Comparison(a, _, b) => {
344			list_dependencies_expression(a.borrow(), dependencies);
345			list_dependencies_expression(b.borrow(), dependencies);
346		}
347		Expression::Variable(name) => {
348			dependencies.add_variable(name);
349		}
350		Expression::Builder(name, args) => {
351			dependencies.add_builder(name);
352			for arg in args {
353				let (_, expression) = arg.borrow();
354				list_dependencies_expression(expression.borrow(), dependencies);
355			}
356		}
357		Expression::FunctionCall(name, args) => {
358			dependencies.add_function(name);
359			for arg in args {
360				list_dependencies_expression(arg, dependencies);
361			}
362		}
363		Expression::Ternary(a, b, c) => {
364			list_dependencies_expression(a.borrow(), dependencies);
365			list_dependencies_expression(b.borrow(), dependencies);
366			list_dependencies_expression(c.borrow(), dependencies);
367		}
368		_ => {}
369	}
370}
371
372/// Helper function to list all the dependencies a block references and
373/// add them to the dependencies.
374pub fn list_dependencies_block(block: &Block, dependencies: &mut Dependencies) {
375	match block {
376		Block::Expression(expression) | Block::Assignment(.., expression) => {
377			list_dependencies_expression(expression, dependencies)
378		}
379		Block::If(if_block) => {
380			// if arms
381			for (expression, blocks) in if_block.if_blocks.iter() {
382				list_dependencies_expression(expression, dependencies);
383				for block in blocks.iter() {
384					list_dependencies_block(block, dependencies);
385				}
386			}
387
388			// optional else arm
389			if let Some(else_block) = &if_block.else_blocks {
390				for block in else_block.iter() {
391					list_dependencies_block(block.borrow(), dependencies);
392				}
393			}
394		}
395		Block::For(for_block) => {
396			for block in &for_block.blocks {
397				list_dependencies_block(&block, dependencies);
398			}
399		}
400		Block::Source(_) => {}
401	}
402}
403
404#[cfg(test)]
405mod tests_blocks {
406	use crate::{
407		evaluator::evaluator::EvaluatorBuilder,
408		parser::Parser,
409		tokenizer::{Tokenizer, TokenizerOptions},
410	};
411
412	fn test(source: &str) {
413		let tokens = Tokenizer::new(source, TokenizerOptions::default())
414			.tokenize()
415			.unwrap();
416		let nodes = Parser::new(tokens.as_slice()).parse().unwrap();
417		let mut evaluator_builder = EvaluatorBuilder::new(nodes.as_slice());
418		let blocks = evaluator_builder.build();
419		println!("{:?}", blocks);
420	}
421
422	#[test]
423	fn test_01() {
424		test("abc {{a + 1}} def");
425	}
426
427	#[test]
428	fn test_02() {
429		test("{{# if a > b}} a > b {{#}}");
430	}
431
432	#[test]
433	fn test_03() {
434		test("{{# if a > b}} a > b {{# else }} a <= b {{#}}");
435	}
436
437	#[test]
438	fn test_04() {
439		test("{{# if a > b}} a > b {{# elif a < b }} a < b {{#}}");
440	}
441
442	#[test]
443	fn test_05() {
444		test("{{# if a > b}} part 1 {{# if c > d}} c > d {{# else }} c <= d {{#}} {{# elif a < b }} a < b {{#}}");
445	}
446
447	#[test]
448	fn test_06() {
449		test("{{# if a}} {{b ? @ file1(a = a, b = b) : @ file1(a = a, b = 0)}} {{#}}");
450	}
451}
452
453#[cfg(test)]
454mod tests_evaluator {
455	use std::collections::HashMap;
456
457	use hmap::hmap;
458
459	use crate::{
460		evaluator::evaluation_context::{ContextLocation, EvaluationContextWarnings},
461		parser::Parser,
462		tokenizer::{Tokenizer, TokenizerOptions},
463		value::Value,
464	};
465
466	use super::{
467		evaluation_context::EvaluationContext,
468		evaluator::{Evaluator, EvaluatorBuilder},
469		Evaluate,
470	};
471
472	fn test(
473		source: &str,
474		global_variables: Vec<(&str, Value)>,
475		builders: HashMap<String, Evaluator>,
476	) -> String {
477		// tokenize, parse, and build the evaluation blocks
478		let tokens = Tokenizer::new(source, TokenizerOptions::default())
479			.tokenize()
480			.unwrap();
481		let nodes = Parser::new(tokens.as_slice()).parse().unwrap();
482		let mut evaluator_builder = EvaluatorBuilder::new(nodes.as_slice());
483		let evaluator = evaluator_builder.build_evaluator().unwrap();
484
485		// prepare the variables
486		let global_variables = global_variables
487			.into_iter()
488			.map(|(k, v)| (k.to_string(), v));
489		let global_variables = HashMap::from_iter(global_variables);
490
491		let functions = HashMap::new();
492
493		// create the context and evaluate
494		let mut context = EvaluationContext::new(
495			&global_variables,
496			vec![],
497			&builders,
498			&functions,
499			Default::default(),
500			EvaluationContextWarnings::default(),
501			ContextLocation::source("main"),
502		);
503		let out = evaluator.evaluate(&mut context);
504		println!("{}", out);
505		out
506	}
507
508	#[test]
509	fn test_01() {
510		test("abc {{ 1 + 2 }} def", vec![], HashMap::new());
511	}
512
513	#[test]
514	fn test_02() {
515		test(
516			"result: {{ 1 > 2 ? 'item 1' : 'item 2' }}",
517			vec![],
518			HashMap::new(),
519		);
520	}
521
522	#[test]
523	fn test_03() {
524		test("result: {{ unknown_variable }}", vec![], HashMap::new());
525	}
526
527	#[test]
528	fn test_04() {
529		test(
530			"a > b = {{a}} > {{b}} = {{ a > b }}",
531			vec![("a", Value::Number(1.0)), ("b", Value::Number(2.0))],
532			HashMap::new(),
533		);
534	}
535
536	#[test]
537	fn test_05() {
538		test(
539			"a is {{ a > b ? 'bigger than' : a < b ? 'smaller than' : 'equal to'}} b",
540			vec![("a", Value::Number(1.0)), ("b", Value::Number(2.0))],
541			HashMap::new(),
542		);
543	}
544
545	#[test]
546	fn test_06() {
547		test(
548			"a is {{#if a > b}}bigger than{{# elif a < b}}smaller than{{#else}}equal to{{#}} b",
549			vec![("a", Value::Number(1.0)), ("b", Value::Number(1.0))],
550			HashMap::new(),
551		);
552	}
553
554	#[test]
555	fn test_07() {
556		// create a template
557		let source = "saying hi to: {{name}}";
558		// tokenize, parse, and build the evaluation blocks
559		let tokens = Tokenizer::new(source, TokenizerOptions::default())
560			.tokenize()
561			.unwrap();
562		let nodes = Parser::new(tokens.as_slice()).parse().unwrap();
563		let mut evaluator_builder = EvaluatorBuilder::new(nodes.as_slice());
564		let evaluator = evaluator_builder.build_evaluator().unwrap();
565
566		test(
567			"{{a > b ? @ say_hi(name = 'name ' + a) : @ say_hi(name = 'name ' + b)}}",
568			vec![("a", Value::Number(1.0)), ("b", Value::Number(2.0))],
569			hmap!(
570				format!("say_hi") => evaluator
571			),
572		);
573	}
574
575	#[test]
576	fn test_08() {
577		let source = "{{a != None ? a : 'default'}}";
578		assert_eq!(
579			&test(source, vec![("a", Value::text("var-a"))], HashMap::new()),
580			"var-a"
581		);
582		assert_eq!(&test(source, vec![], HashMap::new()), "default");
583	}
584}
585
586#[cfg(test)]
587mod tests_contained_evaluator {
588	use std::collections::HashMap;
589
590	use crate::evaluator::{
591		contained_evaluator::ContainedEvaluator,
592		evaluation_context::{ContextLocation, EvaluationContext, EvaluationContextWarnings},
593		Evaluate,
594	};
595
596	#[test]
597	fn test_01() {
598		// prepare the variables
599		let global_variables = HashMap::new();
600		let local_variables = vec![];
601		let builders = HashMap::<_, ContainedEvaluator>::new();
602		let functions = HashMap::new();
603
604		// create the context and evaluate
605		let mut context = EvaluationContext::new(
606			&global_variables,
607			local_variables,
608			&builders,
609			&functions,
610			Default::default(),
611			EvaluationContextWarnings::default(),
612			ContextLocation::source("main"),
613		);
614
615		// create the evaluator and evaluate
616		let evaluator = ContainedEvaluator::from_string("1 + 2 = {{ 1 + 2 }}".to_string()).unwrap();
617		println!("{:?}", std::ptr::addr_of!(evaluator));
618		println!("{}", evaluator.evaluator.evaluate(&mut context));
619		let _a = 0;
620		{
621			let moved = evaluator;
622			println!("{:?}", std::ptr::addr_of!(moved));
623			println!("{}", moved.evaluator.evaluate(&mut context));
624		}
625	}
626}