1use std::collections::HashMap;
7use std::sync::Arc;
8
9use super::proto_registry::ProtoRegistry;
10use super::{Activation, EmptyActivation, Evaluator, FunctionRegistry, Value};
11use crate::Ast;
12
13#[derive(Clone)]
19pub struct Program {
20 ast: Arc<Ast>,
21 functions: Arc<FunctionRegistry>,
22 proto_registry: Option<Arc<dyn ProtoRegistry>>,
23 abbreviations: Option<Arc<HashMap<String, String>>>,
24 strong_enums: bool,
25}
26
27impl Program {
28 pub fn new(ast: Arc<Ast>, functions: Arc<FunctionRegistry>) -> Self {
30 Self {
31 ast,
32 functions,
33 proto_registry: None,
34 abbreviations: None,
35 strong_enums: true,
36 }
37 }
38
39 pub fn with_proto_registry(
41 ast: Arc<Ast>,
42 functions: Arc<FunctionRegistry>,
43 proto_registry: Arc<dyn ProtoRegistry>,
44 ) -> Self {
45 Self {
46 ast,
47 functions,
48 proto_registry: Some(proto_registry),
49 abbreviations: None,
50 strong_enums: true,
51 }
52 }
53
54 pub fn with_abbreviations(
56 ast: Arc<Ast>,
57 functions: Arc<FunctionRegistry>,
58 abbreviations: HashMap<String, String>,
59 ) -> Self {
60 Self {
61 ast,
62 functions,
63 proto_registry: None,
64 abbreviations: Some(Arc::new(abbreviations)),
65 strong_enums: true,
66 }
67 }
68
69 pub fn with_proto_registry_and_abbreviations(
71 ast: Arc<Ast>,
72 functions: Arc<FunctionRegistry>,
73 proto_registry: Arc<dyn ProtoRegistry>,
74 abbreviations: HashMap<String, String>,
75 ) -> Self {
76 Self {
77 ast,
78 functions,
79 proto_registry: Some(proto_registry),
80 abbreviations: Some(Arc::new(abbreviations)),
81 strong_enums: true,
82 }
83 }
84
85 pub fn with_legacy_enums(mut self) -> Self {
87 self.strong_enums = false;
88 self
89 }
90
91 pub fn ast(&self) -> &Ast {
93 &self.ast
94 }
95
96 pub fn functions(&self) -> &FunctionRegistry {
98 &self.functions
99 }
100
101 pub fn eval(&self, activation: &dyn Activation) -> Value {
103 self.eval_with_container(activation, "")
104 }
105
106 pub fn eval_with_container(&self, activation: &dyn Activation, container: &str) -> Value {
117 let mut evaluator = Evaluator::new(activation, &self.functions);
118
119 if let Some(type_info) = self.ast.type_info() {
121 evaluator = evaluator.with_reference_map(&type_info.reference_map);
122 }
123
124 if let Some(ref proto_registry) = self.proto_registry {
126 evaluator = evaluator.with_proto_registry(proto_registry.as_ref());
127 }
128
129 if !container.is_empty() {
131 evaluator = evaluator.with_container(container);
132 }
133
134 if let Some(ref abbreviations) = self.abbreviations {
136 evaluator = evaluator.with_abbreviations(abbreviations);
137 }
138
139 if !self.strong_enums {
141 evaluator = evaluator.with_legacy_enums();
142 }
143
144 evaluator.eval(self.ast.expr())
145 }
146
147 pub fn eval_empty(&self) -> Value {
149 self.eval(&EmptyActivation)
150 }
151}
152
153impl std::fmt::Debug for Program {
154 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
155 f.debug_struct("Program")
156 .field("ast", &self.ast)
157 .field("functions", &format!("{} functions", self.functions.len()))
158 .finish()
159 }
160}
161
162#[cfg(test)]
163mod tests {
164 use super::*;
165 use crate::eval::MapActivation;
166 use crate::parse;
167
168 fn create_program(source: &str) -> Program {
169 let result = parse(source);
170 assert!(result.errors.is_empty());
171 let ast = Ast::new_unchecked(result.ast.unwrap(), source);
172 Program::new(Arc::new(ast), Arc::new(FunctionRegistry::new()))
173 }
174
175 #[test]
176 fn test_eval_literal() {
177 let program = create_program("42");
178 assert_eq!(program.eval_empty(), Value::Int(42));
179 }
180
181 #[test]
182 fn test_eval_with_variables() {
183 let program = create_program("x + y");
184 let mut activation = MapActivation::new();
185 activation.insert("x", Value::Int(1));
186 activation.insert("y", Value::Int(2));
187 assert_eq!(program.eval(&activation), Value::Int(3));
188 }
189
190 #[test]
191 fn test_reuse_program() {
192 let program = create_program("x * 2");
193
194 let mut act1 = MapActivation::new();
195 act1.insert("x", Value::Int(5));
196 assert_eq!(program.eval(&act1), Value::Int(10));
197
198 let mut act2 = MapActivation::new();
199 act2.insert("x", Value::Int(21));
200 assert_eq!(program.eval(&act2), Value::Int(42));
201 }
202}