1use crate::ast::{ComponentDecl, Expr, Program, ProgramItem};
4use crate::error::NewtError;
5use std::collections::HashMap;
6
7const MAX_RANGE_SIZE: i64 = 10_000;
9
10#[derive(Clone, Debug)]
11pub enum Value {
12 Number(f64),
13 String(String),
14 Bool(bool),
15 Color { r: u8, g: u8, b: u8, a: u8 },
16 Array(Vec<Value>),
17}
18
19impl Value {
20 pub fn as_number(&self) -> Result<f64, NewtError> {
21 match self {
22 Value::Number(n) => Ok(*n),
23 _ => Err(NewtError::Other("expected number".into())),
24 }
25 }
26
27 pub fn as_string(&self) -> Result<&str, NewtError> {
28 match self {
29 Value::String(s) => Ok(s),
30 _ => Err(NewtError::Other("expected string".into())),
31 }
32 }
33
34 pub fn as_bool(&self) -> Result<bool, NewtError> {
35 match self {
36 Value::Bool(b) => Ok(*b),
37 _ => Err(NewtError::Other("expected bool".into())),
38 }
39 }
40
41 pub fn as_color(&self) -> Result<(u8, u8, u8, u8), NewtError> {
42 match self {
43 Value::Color { r, g, b, a } => Ok((*r, *g, *b, *a)),
44 _ => Err(NewtError::Other("expected color".into())),
45 }
46 }
47
48 pub fn as_array(&self) -> Result<&[Value], NewtError> {
49 match self {
50 Value::Array(a) => Ok(a.as_slice()),
51 _ => Err(NewtError::Other("expected array".into())),
52 }
53 }
54}
55
56pub struct EvalContext {
57 pub variables: HashMap<String, Value>,
58 pub components: HashMap<String, ComponentDecl>,
59}
60
61impl EvalContext {
62 pub fn from_program(program: &Program) -> Self {
63 let mut components = HashMap::new();
64 let mut themes: HashMap<String, HashMap<String, Value>> = HashMap::new();
65 for item in &program.items {
66 if let ProgramItem::Component(c) = item {
67 components.insert(c.name.clone(), c.clone());
68 }
69 }
70 let mut ctx = Self {
71 variables: HashMap::new(),
72 components,
73 };
74 for item in &program.items {
75 match item {
76 ProgramItem::Variable(v) => {
77 if let Ok(val) = eval_expr(&ctx, &v.value) {
78 ctx.variables.insert(v.name.clone(), val);
79 }
80 }
81 ProgramItem::Theme(t) => {
82 let mut theme_vars: HashMap<String, Value> = HashMap::new();
83 for v in &t.vars {
84 let mut theme_ctx = EvalContext {
85 variables: ctx.variables.clone(),
86 components: ctx.components.clone(),
87 };
88 for (k, val) in &theme_vars {
89 theme_ctx.variables.insert(k.clone(), val.clone());
90 }
91 if let Ok(val) = eval_expr(&theme_ctx, &v.value) {
92 theme_vars.insert(v.name.clone(), val);
93 }
94 }
95 themes.insert(t.name.clone(), theme_vars);
96 }
97 ProgramItem::UseTheme(name) => {
98 if let Some(theme_vars) = themes.get(name) {
99 for (k, v) in theme_vars {
100 ctx.variables.insert(k.clone(), v.clone());
101 }
102 }
103 }
104 ProgramItem::StateDecl(sd) => {
105 if let Ok(val) = eval_expr(&ctx, &sd.initial_value) {
106 ctx.variables.insert(sd.name.clone(), val);
107 }
108 }
109 _ => {}
110 }
111 }
112 ctx
113 }
114}
115
116pub fn value_to_string(v: &Value) -> String {
118 match v {
119 Value::String(s) => s.clone(),
120 Value::Number(n) => {
121 if *n == (*n as i64) as f64 {
122 format!("{}", *n as i64)
123 } else {
124 format!("{}", n)
125 }
126 }
127 Value::Bool(b) => format!("{}", b),
128 Value::Color { r, g, b, a } => {
129 if *a == 255 {
130 format!("#{:02x}{:02x}{:02x}", r, g, b)
131 } else {
132 format!("#{:02x}{:02x}{:02x}{:02x}", r, g, b, a)
133 }
134 }
135 Value::Array(arr) => {
136 let items: Vec<String> = arr.iter().map(value_to_string).collect();
137 format!("[{}]", items.join(", "))
138 }
139 }
140}
141
142pub fn value_to_json(v: &Value) -> serde_json::Value {
143 match v {
144 Value::Number(n) => {
145 if *n == (*n as i64) as f64 {
146 serde_json::Value::Number(serde_json::Number::from(*n as i64))
147 } else {
148 serde_json::json!(*n)
149 }
150 }
151 Value::String(s) => serde_json::Value::String(s.clone()),
152 Value::Bool(b) => serde_json::Value::Bool(*b),
153 Value::Color { r, g, b, a } => {
154 if *a == 255 {
155 serde_json::json!(format!("#{:02x}{:02x}{:02x}", r, g, b))
156 } else {
157 serde_json::json!(format!("#{:02x}{:02x}{:02x}{:02x}", r, g, b, a))
158 }
159 }
160 Value::Array(arr) => serde_json::Value::Array(arr.iter().map(value_to_json).collect()),
161 }
162}
163
164pub fn json_to_value(v: &serde_json::Value) -> Option<Value> {
165 match v {
166 serde_json::Value::Number(n) => Some(Value::Number(n.as_f64()?)),
167 serde_json::Value::String(s) => Some(Value::String(s.clone())),
168 serde_json::Value::Bool(b) => Some(Value::Bool(*b)),
169 _ => None,
170 }
171}
172
173pub fn eval_expr(ctx: &EvalContext, expr: &Expr) -> Result<Value, NewtError> {
174 use crate::ast::{BinaryOp, Expr, Literal, UnaryOp};
175 match expr {
176 Expr::Literal(lit) => match lit {
177 Literal::Number(n) => Ok(Value::Number(*n)),
178 Literal::String(s) => Ok(Value::String(s.clone())),
179 Literal::Bool(b) => Ok(Value::Bool(*b)),
180 Literal::Color { r, g, b, a } => Ok(Value::Color {
181 r: *r,
182 g: *g,
183 b: *b,
184 a: *a,
185 }),
186 Literal::Array(elems) => {
187 let mut arr = Vec::with_capacity(elems.len());
188 for e in elems {
189 arr.push(eval_expr(ctx, e)?);
190 }
191 Ok(Value::Array(arr))
192 }
193 },
194 Expr::Ident(name, span) => {
195 ctx.variables
196 .get(name)
197 .cloned()
198 .ok_or_else(|| NewtError::semantic(*span, format!("undefined variable '{}'", name)))
199 }
200 Expr::Binary { left, op, right, .. } => {
201 let l = eval_expr(ctx, left)?;
202 let r = eval_expr(ctx, right)?;
203 match op {
204 BinaryOp::Add => Ok(Value::Number(l.as_number()? + r.as_number()?)),
205 BinaryOp::Sub => Ok(Value::Number(l.as_number()? - r.as_number()?)),
206 BinaryOp::Mul => Ok(Value::Number(l.as_number()? * r.as_number()?)),
207 BinaryOp::Div => Ok(Value::Number(l.as_number()? / r.as_number()?)),
208 BinaryOp::Mod => Ok(Value::Number(l.as_number()? % r.as_number()?)),
209 BinaryOp::Eq => Ok(Value::Bool(match (&l, &r) {
210 (Value::Number(a), Value::Number(b)) => a == b,
211 (Value::Bool(a), Value::Bool(b)) => a == b,
212 (Value::String(a), Value::String(b)) => a == b,
213 _ => false,
214 })),
215 BinaryOp::Ne => Ok(Value::Bool(match (&l, &r) {
216 (Value::Number(a), Value::Number(b)) => a != b,
217 (Value::Bool(a), Value::Bool(b)) => a != b,
218 (Value::String(a), Value::String(b)) => a != b,
219 _ => true,
220 })),
221 BinaryOp::Lt => Ok(Value::Bool(l.as_number()? < r.as_number()?)),
222 BinaryOp::Le => Ok(Value::Bool(l.as_number()? <= r.as_number()?)),
223 BinaryOp::Gt => Ok(Value::Bool(l.as_number()? > r.as_number()?)),
224 BinaryOp::Ge => Ok(Value::Bool(l.as_number()? >= r.as_number()?)),
225 BinaryOp::And => Ok(Value::Bool(l.as_bool()? && r.as_bool()?)),
226 BinaryOp::Or => Ok(Value::Bool(l.as_bool()? || r.as_bool()?)),
227 }
228 }
229 Expr::Unary { op, inner, .. } => {
230 let v = eval_expr(ctx, inner)?;
231 match op {
232 UnaryOp::Not => Ok(Value::Bool(!v.as_bool()?)),
233 UnaryOp::Neg => Ok(Value::Number(-v.as_number()?)),
234 }
235 }
236 Expr::Block { stmts, .. } => {
237 let mut block_ctx = EvalContext {
238 variables: ctx.variables.clone(),
239 components: ctx.components.clone(),
240 };
241 let mut last = Value::Bool(false);
242 for s in stmts {
243 match s {
244 crate::ast::Stmt::Expr(e) => last = eval_expr(&block_ctx, e)?,
245 crate::ast::Stmt::Let { name, value, .. } => {
246 let val = eval_expr(&block_ctx, value)?;
247 block_ctx.variables.insert(name.clone(), val.clone());
248 last = val;
249 }
250 crate::ast::Stmt::StateDecl(sd) => {
251 let val = eval_expr(&block_ctx, &sd.initial_value)?;
252 block_ctx.variables.insert(sd.name.clone(), val.clone());
253 last = val;
254 }
255 }
256 }
257 Ok(last)
258 }
259 Expr::If { cond, then_branch, else_branch, .. } => {
260 if eval_expr(ctx, cond)?.as_bool()? {
261 eval_expr(ctx, then_branch)
262 } else if let Some(eb) = else_branch {
263 eval_expr(ctx, eb)
264 } else {
265 Ok(Value::Bool(false))
266 }
267 }
268 Expr::Call {
269 callee,
270 args,
271 slot_args,
272 span,
273 ..
274 } => {
275 if callee == "range" && args.len() == 1 && slot_args.is_none() {
276 let n = eval_expr(ctx, &args[0])?.as_number()? as i64;
277 let n = n.max(0).min(MAX_RANGE_SIZE) as usize;
278 return Ok(Value::Array(
279 (0..n).map(|i| Value::Number(i as f64)).collect(),
280 ));
281 }
282 let comp = ctx
283 .components
284 .get(callee)
285 .ok_or_else(|| NewtError::semantic(*span, format!("unknown component '{}'", callee)))?;
286 let body = if let Some(ref slots) = slot_args {
287 crate::ast::substitute_slots(&comp.body, slots)
288 } else {
289 comp.body.clone()
290 };
291 let mut new_ctx = EvalContext {
292 variables: ctx.variables.clone(),
293 components: ctx.components.clone(),
294 };
295 if slot_args.is_none() {
296 for (i, param) in comp.params.iter().enumerate() {
297 if let Some(arg) = args.get(i) {
298 if let Ok(v) = eval_expr(ctx, arg) {
299 new_ctx.variables.insert(param.clone(), v);
300 }
301 }
302 }
303 }
304 eval_expr(&new_ctx, &body)
305 }
306 Expr::For { var, iter, body, span, .. } => {
307 let iter_val = eval_expr(ctx, iter)?;
308 let arr = iter_val.as_array().map_err(|_| {
309 NewtError::semantic(*span, "for loop expects an array or range(n)")
310 })?;
311 let results: Result<Vec<Value>, NewtError> = arr.iter().map(|val| {
312 let mut loop_ctx = EvalContext {
313 variables: ctx.variables.clone(),
314 components: ctx.components.clone(),
315 };
316 loop_ctx.variables.insert(var.clone(), val.clone());
317 eval_expr(&loop_ctx, body)
318 }).collect();
319 Ok(Value::Array(results?))
320 }
321 Expr::InterpolatedString { parts, .. } => {
322 let mut result = String::new();
323 for seg in parts {
324 match seg {
325 crate::ast::InterpSegment::Literal(s) => result.push_str(s),
326 crate::ast::InterpSegment::Expr(e) => {
327 let val = eval_expr(ctx, e)?;
328 result.push_str(&value_to_string(&val));
329 }
330 }
331 }
332 Ok(Value::String(result))
333 }
334 Expr::Assignment { value, .. } => {
335 eval_expr(ctx, value)
337 }
338 _ => Err(NewtError::Other("expression not evaluable to value".into())),
339 }
340}