1use crate::ast::{Expr, Stmt, Context, LoopControl, Op};
2
3pub fn eval_expr(expr: &Expr, ctx: &Context) -> String {
16 match expr {
17 Expr::Int(i) => i.to_string(),
18 Expr::Str(s) => s.clone(),
19 Expr::Var(name) => ctx
20 .variables
21 .get(name)
22 .cloned()
23 .unwrap_or_else(|| panic!("Undefined variable: {}", name)),
24 Expr::Binary(left, op, right) => {
25 let l = eval_expr(left, ctx).parse::<i64>().unwrap();
26 let r = eval_expr(right, ctx).parse::<i64>().unwrap();
27 let result = match op {
28 Op::Add => l + r,
29 Op::Sub => l - r,
30 Op::Mul => l * r,
31 Op::Div => l / r,
32 Op::Greater => (l > r) as i64,
33 Op::Less => (l < r) as i64,
34 Op::GreaterEq => (l >= r) as i64,
35 Op::LessEq => (l <= r) as i64,
36 Op::Equal => (l == r) as i64,
37 Op::NotEqual => (l != r) as i64,
38 };
39 result.to_string()
40 }
41 Expr::Call(name, args) => {
42 let (params, body) = ctx
43 .functions
44 .get(name)
45 .unwrap_or_else(|| panic!("Undefined function: {}", name))
46 .clone();
47
48 if params.len() != args.len() {
49 panic!(
50 "Function '{}' expected {} args, got {}",
51 name,
52 params.len(),
53 args.len()
54 );
55 }
56
57 let mut local_ctx = Context::default();
58 for (param, arg) in params.iter().zip(args.iter()) {
59 let value = eval_expr(arg, ctx);
60 local_ctx.variables.insert(param.clone(), value);
61 }
62
63 for stmt in body {
64 match exec_stmt(&stmt, &mut local_ctx) {
65 LoopControl::Return(val) => return val,
66 LoopControl::None => continue,
67 _ => panic!("Unexpected control flow in function"),
68 }
69 }
70 "".to_string()
71 }
72 }
73}
74
75pub fn exec_stmt(stmt: &Stmt, ctx: &mut Context) -> LoopControl {
87 match stmt {
88 Stmt::Print(expr) => {
89 println!("{}", eval_expr(expr, ctx));
90 LoopControl::None
91 }
92 Stmt::Let(name, expr) => {
93 let value = eval_expr(expr, ctx);
94 ctx.variables.insert(name.clone(), value);
95 LoopControl::None
96 }
97 Stmt::Break => LoopControl::Break,
98 Stmt::Continue => LoopControl::Continue,
99 Stmt::If {
100 condition,
101 then_branch,
102 else_branch,
103 } => {
104 let cond_value = eval_expr(condition, ctx);
105 let is_true = cond_value != "0" && cond_value != "" && cond_value != "false";
106 let fallback = Vec::new();
107 let branch = if is_true {
108 then_branch
109 } else {
110 else_branch.as_ref().unwrap_or(&fallback)
111 };
112 for stmt in branch {
113 match exec_stmt(stmt, ctx) {
114 LoopControl::None => continue,
115 control => return control,
116 }
117 }
118 LoopControl::None
119 }
120 Stmt::While { condition, body } => {
121 while eval_expr(condition, ctx) != "0" {
122 for stmt in body {
123 match exec_stmt(stmt, ctx) {
124 LoopControl::None => continue,
125 LoopControl::Break => return LoopControl::None,
126 LoopControl::Continue => break,
127 LoopControl::Return(val) => return LoopControl::Return(val),
128 }
129 }
130 }
131 LoopControl::None
132 }
133 Stmt::Fn { name, params, body } => {
134 ctx.functions
135 .insert(name.clone(), (params.clone(), body.to_vec()));
136 LoopControl::None
137 }
138 Stmt::Call(name, args) => {
139 let (params, body) = ctx.functions.get(name).unwrap().clone();
140 let mut local_ctx = Context::default();
141 for (param, arg) in params.iter().zip(args.iter()) {
142 let value = eval_expr(arg, ctx);
143 local_ctx.variables.insert(param.clone(), value);
144 }
145 for stmt in body {
146 exec_stmt(&stmt, &mut local_ctx);
147 }
148 LoopControl::None
149 }
150 Stmt::Return(expr) => {
151 let value = eval_expr(expr, ctx);
152 LoopControl::Return(value)
153 }
154 }
155}
156
157#[cfg(test)]
158mod tests {
159 use super::*;
160 use crate::ast::{Expr, Op, Context};
161
162 #[test]
163 fn test_addition_expr() {
164 let expr = Expr::Binary(Box::new(Expr::Int(2)), Op::Add, Box::new(Expr::Int(3)));
165 let ctx = Context::default();
166 let result = eval_expr(&expr, &ctx);
167 assert_eq!(result, "5");
168 }
169
170 #[test]
171 fn test_variable_lookup() {
172 let mut ctx = Context::default();
173 ctx.variables.insert("x".to_string(), "42".to_string());
174 let expr = Expr::Var("x".to_string());
175 let result = eval_expr(&expr, &ctx);
176 assert_eq!(result, "42");
177 }
178}