use std::collections::HashMap; // --- Values --- #[derive(Clone, Debug)] pub enum Value { Number(f64), Bool(bool), Str(String), Array(Vec), Void, Error(String), } impl Value { pub fn display(&self) -> String { match self { Value::Number(n) => format_number(*n), Value::Bool(b) => b.to_string(), Value::Str(s) => s.clone(), Value::Array(items) => { let inner: Vec = items.iter().map(|v| match v { Value::Str(s) => format!("\"{}\"", s), other => other.display(), }).collect(); format!("[{}]", inner.join(", ")) } Value::Void => String::new(), Value::Error(e) => format!("error: {}", e), } } pub fn is_error(&self) -> bool { matches!(self, Value::Error(_)) } fn truthy(&self) -> bool { match self { Value::Bool(b) => *b, Value::Number(n) => *n != 0.0, Value::Str(s) => !s.is_empty(), Value::Array(a) => !a.is_empty(), Value::Void => false, Value::Error(_) => false, } } } fn format_number(n: f64) -> String { if n == n.trunc() && n.abs() < 1e15 { format!("{}", n as i64) } else { let s = format!("{:.10}", n); let s = s.trim_end_matches('0'); let s = s.trim_end_matches('.'); s.to_string() } } // --- Tokens --- #[derive(Debug, Clone, PartialEq)] enum Token { Number(f64), Str(String), Bool(bool), Ident(String), Plus, Minus, Star, Slash, Percent, Caret, LParen, RParen, LBrace, RBrace, LBracket, RBracket, Comma, Eq, EqEq, BangEq, Lt, Gt, LtEq, GtEq, And, Or, Bang, Colon, DotDot, Let, While, Fn, If, Else, For, In, Return, Newline, Eof, } fn tokenize(input: &str) -> Result, String> { let mut tokens = Vec::new(); let chars: Vec = input.chars().collect(); let len = chars.len(); let mut i = 0; while i < len { let c = chars[i]; match c { ' ' | '\t' | '\r' => { i += 1; } '\n' => { tokens.push(Token::Newline); i += 1; } '+' => { tokens.push(Token::Plus); i += 1; } '-' => { // negative number literal: only if preceded by operator/open/start/newline if i + 1 < len && (chars[i + 1].is_ascii_digit() || chars[i + 1] == '.') { let can_be_neg = if tokens.is_empty() { true } else { matches!(tokens.last(), Some( Token::Plus | Token::Minus | Token::Star | Token::Slash | Token::Percent | Token::Caret | Token::LParen | Token::LBracket | Token::Comma | Token::Eq | Token::EqEq | Token::BangEq | Token::Lt | Token::Gt | Token::LtEq | Token::GtEq | Token::And | Token::Or | Token::Bang | Token::Newline | Token::Colon )) }; if can_be_neg { let start = i; i += 1; while i < len && (chars[i].is_ascii_digit() || (chars[i] == '.' && !(i + 1 < len && chars[i + 1] == '.'))) { i += 1; } let s: String = chars[start..i].iter().collect(); let n: f64 = s.parse().map_err(|_| format!("invalid number: {}", s))?; tokens.push(Token::Number(n)); } else { tokens.push(Token::Minus); i += 1; } } else { tokens.push(Token::Minus); i += 1; } } '*' => { tokens.push(Token::Star); i += 1; } '/' => { if i + 1 < len && chars[i + 1] == '/' { while i < len && chars[i] != '\n' { i += 1; } } else { tokens.push(Token::Slash); i += 1; } } '%' => { tokens.push(Token::Percent); i += 1; } '^' => { tokens.push(Token::Caret); i += 1; } '(' => { tokens.push(Token::LParen); i += 1; } ')' => { tokens.push(Token::RParen); i += 1; } '{' => { tokens.push(Token::LBrace); i += 1; } '}' => { tokens.push(Token::RBrace); i += 1; } '[' => { tokens.push(Token::LBracket); i += 1; } ']' => { tokens.push(Token::RBracket); i += 1; } ',' => { tokens.push(Token::Comma); i += 1; } ':' => { tokens.push(Token::Colon); i += 1; } '.' if i + 1 < len && chars[i + 1] == '.' => { tokens.push(Token::DotDot); i += 2; } '!' => { if i + 1 < len && chars[i + 1] == '=' { tokens.push(Token::BangEq); i += 2; } else { tokens.push(Token::Bang); i += 1; } } '=' => { if i + 1 < len && chars[i + 1] == '=' { tokens.push(Token::EqEq); i += 2; } else { tokens.push(Token::Eq); i += 1; } } '<' => { if i + 1 < len && chars[i + 1] == '=' { tokens.push(Token::LtEq); i += 2; } else { tokens.push(Token::Lt); i += 1; } } '>' => { if i + 1 < len && chars[i + 1] == '=' { tokens.push(Token::GtEq); i += 2; } else { tokens.push(Token::Gt); i += 1; } } '&' => { if i + 1 < len && chars[i + 1] == '&' { tokens.push(Token::And); i += 2; } else { return Err("unexpected '&', did you mean '&&'?".into()); } } '|' => { if i + 1 < len && chars[i + 1] == '|' { tokens.push(Token::Or); i += 2; } else { return Err("unexpected '|', did you mean '||'?".into()); } } '"' => { i += 1; let mut s = String::new(); while i < len && chars[i] != '"' { if chars[i] == '\\' && i + 1 < len { i += 1; match chars[i] { 'n' => s.push('\n'), 't' => s.push('\t'), '\\' => s.push('\\'), '"' => s.push('"'), other => { s.push('\\'); s.push(other); } } } else { s.push(chars[i]); } i += 1; } if i >= len { return Err("unterminated string".into()); } i += 1; // closing quote tokens.push(Token::Str(s)); } _ if c.is_ascii_digit() || (c == '.' && i + 1 < len && chars[i + 1].is_ascii_digit()) => { let start = i; while i < len && (chars[i].is_ascii_digit() || (chars[i] == '.' && !(i + 1 < len && chars[i + 1] == '.'))) { i += 1; } let s: String = chars[start..i].iter().collect(); let n: f64 = s.parse().map_err(|_| format!("invalid number: {}", s))?; tokens.push(Token::Number(n)); } _ if c.is_alphabetic() || c == '_' => { let start = i; while i < len && (chars[i].is_alphanumeric() || chars[i] == '_') { i += 1; } let word: String = chars[start..i].iter().collect(); match word.as_str() { "let" => tokens.push(Token::Let), "while" => tokens.push(Token::While), "fn" => tokens.push(Token::Fn), "if" => tokens.push(Token::If), "else" => tokens.push(Token::Else), "for" => tokens.push(Token::For), "in" => tokens.push(Token::In), "return" => tokens.push(Token::Return), "true" => tokens.push(Token::Bool(true)), "false" => tokens.push(Token::Bool(false)), _ => tokens.push(Token::Ident(word)), } } _ => { return Err(format!("unexpected character: '{}'", c)); } } } tokens.push(Token::Eof); Ok(tokens) } // --- AST --- #[derive(Debug, Clone)] enum Op { Add, Sub, Mul, Div, Mod, Pow, Eq, Neq, Lt, Gt, Lte, Gte, And, Or, Not, Neg, } #[derive(Debug, Clone)] enum Stmt { Let(String, Option, Expr), Assign(String, Expr), While(Expr, Vec), IfElse(Expr, Vec, Option>), ForLoop(String, Expr, Vec), FnDef(String, Vec, Vec), Return(Expr), ExprStmt(Expr), } #[derive(Debug, Clone)] enum Expr { Num(f64), Str(String), Bool(bool), Ident(String), BinOp(Op, Box, Box), UnaryOp(Op, Box), Call(String, Vec), Array(Vec), Index(Box, Box), Range(Box, Box), } // --- Parser --- struct Parser { tokens: Vec, pos: usize, } impl Parser { fn new(tokens: Vec) -> Self { Parser { tokens, pos: 0 } } fn peek(&self) -> &Token { self.tokens.get(self.pos).unwrap_or(&Token::Eof) } fn advance(&mut self) -> Token { let tok = self.tokens.get(self.pos).cloned().unwrap_or(Token::Eof); self.pos += 1; tok } fn expect(&mut self, expected: &Token) -> Result<(), String> { let tok = self.advance(); if &tok == expected { Ok(()) } else { Err(format!("expected {:?}, got {:?}", expected, tok)) } } fn skip_newlines(&mut self) { while self.peek() == &Token::Newline { self.advance(); } } fn parse_program(&mut self) -> Result, String> { let mut stmts = Vec::new(); self.skip_newlines(); while self.peek() != &Token::Eof { stmts.push(self.parse_stmt()?); self.skip_newlines(); } Ok(stmts) } fn parse_block(&mut self) -> Result, String> { self.expect(&Token::LBrace)?; self.skip_newlines(); let mut stmts = Vec::new(); while self.peek() != &Token::RBrace && self.peek() != &Token::Eof { stmts.push(self.parse_stmt()?); self.skip_newlines(); } self.expect(&Token::RBrace)?; Ok(stmts) } fn parse_stmt(&mut self) -> Result { self.skip_newlines(); match self.peek().clone() { Token::Let => self.parse_let(), Token::While => self.parse_while(), Token::If => self.parse_if(), Token::For => self.parse_for(), Token::Return => self.parse_return(), Token::Fn => self.parse_fn_def(), Token::Ident(_) => { let saved = self.pos; if let Token::Ident(name) = self.advance() { // name(params) = expr (legacy cord-expr function syntax) if self.peek() == &Token::LParen { let paren_saved = self.pos; self.advance(); let mut params = Vec::new(); let mut valid = true; if self.peek() != &Token::RParen { match self.peek() { Token::Ident(_) => { if let Token::Ident(p) = self.advance() { params.push(p); } while self.peek() == &Token::Comma { self.advance(); if let Token::Ident(p) = self.advance() { params.push(p); } else { valid = false; break; } } } _ => { valid = false; } } } if valid && self.peek() == &Token::RParen { self.advance(); if self.peek() == &Token::Eq { self.advance(); let body_expr = self.parse_expr()?; self.skip_newlines(); return Ok(Stmt::FnDef(name, params, vec![Stmt::ExprStmt(body_expr)])); } } self.pos = paren_saved; // fall through: not a function def, might be assignment } if self.peek() == &Token::Eq { self.advance(); let expr = self.parse_expr()?; self.skip_newlines(); return Ok(Stmt::Assign(name, expr)); } } self.pos = saved; let expr = self.parse_expr()?; self.skip_newlines(); Ok(Stmt::ExprStmt(expr)) } _ => { let expr = self.parse_expr()?; self.skip_newlines(); Ok(Stmt::ExprStmt(expr)) } } } fn parse_let(&mut self) -> Result { self.expect(&Token::Let)?; let name = match self.advance() { Token::Ident(n) => n, t => return Err(format!("expected identifier after 'let', got {:?}", t)), }; let type_ann = if self.peek() == &Token::Colon { self.advance(); match self.advance() { Token::Ident(t) => Some(t), t => return Err(format!("expected type name after ':', got {:?}", t)), } } else { None }; self.expect(&Token::Eq)?; let expr = self.parse_expr()?; self.skip_newlines(); Ok(Stmt::Let(name, type_ann, expr)) } fn parse_while(&mut self) -> Result { self.expect(&Token::While)?; let has_paren = if self.peek() == &Token::LParen { self.advance(); true } else { false }; let cond = self.parse_expr()?; if has_paren { self.expect(&Token::RParen)?; } self.skip_newlines(); let body = self.parse_block()?; Ok(Stmt::While(cond, body)) } fn parse_if(&mut self) -> Result { self.expect(&Token::If)?; let has_paren = if self.peek() == &Token::LParen { self.advance(); true } else { false }; let cond = self.parse_expr()?; if has_paren { self.expect(&Token::RParen)?; } self.skip_newlines(); let then_body = self.parse_block()?; self.skip_newlines(); let else_body = if self.peek() == &Token::Else { self.advance(); self.skip_newlines(); if self.peek() == &Token::If { Some(vec![self.parse_if()?]) } else { Some(self.parse_block()?) } } else { None }; Ok(Stmt::IfElse(cond, then_body, else_body)) } fn parse_for(&mut self) -> Result { self.expect(&Token::For)?; let var = match self.advance() { Token::Ident(n) => n, t => return Err(format!("expected loop variable, got {:?}", t)), }; self.expect(&Token::In)?; let iter = self.parse_expr()?; self.skip_newlines(); let body = self.parse_block()?; Ok(Stmt::ForLoop(var, iter, body)) } fn parse_return(&mut self) -> Result { self.expect(&Token::Return)?; if matches!(self.peek(), Token::Newline | Token::Eof | Token::RBrace) { return Ok(Stmt::Return(Expr::Bool(false))); } let expr = self.parse_expr()?; self.skip_newlines(); Ok(Stmt::Return(expr)) } fn parse_fn_def(&mut self) -> Result { self.expect(&Token::Fn)?; let name = match self.advance() { Token::Ident(n) => n, t => return Err(format!("expected function name, got {:?}", t)), }; self.expect(&Token::LParen)?; let mut params = Vec::new(); if self.peek() != &Token::RParen { match self.advance() { Token::Ident(p) => params.push(p), t => return Err(format!("expected parameter name, got {:?}", t)), } while self.peek() == &Token::Comma { self.advance(); match self.advance() { Token::Ident(p) => params.push(p), t => return Err(format!("expected parameter name, got {:?}", t)), } } } self.expect(&Token::RParen)?; self.skip_newlines(); let body = self.parse_block()?; Ok(Stmt::FnDef(name, params, body)) } fn parse_expr(&mut self) -> Result { let left = self.parse_or()?; if self.peek() == &Token::DotDot { self.advance(); let right = self.parse_or()?; return Ok(Expr::Range(Box::new(left), Box::new(right))); } Ok(left) } fn parse_or(&mut self) -> Result { let mut left = self.parse_and()?; while self.peek() == &Token::Or { self.advance(); let right = self.parse_and()?; left = Expr::BinOp(Op::Or, Box::new(left), Box::new(right)); } Ok(left) } fn parse_and(&mut self) -> Result { let mut left = self.parse_comparison()?; while self.peek() == &Token::And { self.advance(); let right = self.parse_comparison()?; left = Expr::BinOp(Op::And, Box::new(left), Box::new(right)); } Ok(left) } fn parse_comparison(&mut self) -> Result { let mut left = self.parse_additive()?; loop { let op = match self.peek() { Token::EqEq => Op::Eq, Token::BangEq => Op::Neq, Token::Lt => Op::Lt, Token::Gt => Op::Gt, Token::LtEq => Op::Lte, Token::GtEq => Op::Gte, _ => break, }; self.advance(); let right = self.parse_additive()?; left = Expr::BinOp(op, Box::new(left), Box::new(right)); } Ok(left) } fn parse_additive(&mut self) -> Result { let mut left = self.parse_multiplicative()?; loop { let op = match self.peek() { Token::Plus => Op::Add, Token::Minus => Op::Sub, _ => break, }; self.advance(); let right = self.parse_multiplicative()?; left = Expr::BinOp(op, Box::new(left), Box::new(right)); } Ok(left) } fn parse_multiplicative(&mut self) -> Result { let mut left = self.parse_power()?; loop { let op = match self.peek() { Token::Star => Op::Mul, Token::Slash => Op::Div, Token::Percent => Op::Mod, _ => break, }; self.advance(); let right = self.parse_power()?; left = Expr::BinOp(op, Box::new(left), Box::new(right)); } Ok(left) } fn parse_power(&mut self) -> Result { let base = self.parse_unary()?; if self.peek() == &Token::Caret { self.advance(); let exp = self.parse_power()?; // right-associative Ok(Expr::BinOp(Op::Pow, Box::new(base), Box::new(exp))) } else { Ok(base) } } fn parse_unary(&mut self) -> Result { match self.peek() { Token::Bang => { self.advance(); let expr = self.parse_unary()?; Ok(Expr::UnaryOp(Op::Not, Box::new(expr))) } Token::Minus => { // Only treat as unary neg if the minus wasn't already consumed as negative number self.advance(); let expr = self.parse_unary()?; Ok(Expr::UnaryOp(Op::Neg, Box::new(expr))) } _ => self.parse_call(), } } fn parse_call(&mut self) -> Result { let mut expr = self.parse_atom()?; if let Expr::Ident(ref name) = expr { if self.peek() == &Token::LParen { self.advance(); let mut args = Vec::new(); if self.peek() != &Token::RParen { args.push(self.parse_expr()?); while self.peek() == &Token::Comma { self.advance(); args.push(self.parse_expr()?); } } self.expect(&Token::RParen)?; expr = Expr::Call(name.clone(), args); } } while self.peek() == &Token::LBracket { self.advance(); let index = self.parse_expr()?; self.expect(&Token::RBracket)?; expr = Expr::Index(Box::new(expr), Box::new(index)); } Ok(expr) } fn parse_atom(&mut self) -> Result { match self.peek().clone() { Token::Number(n) => { self.advance(); Ok(Expr::Num(n)) } Token::Str(s) => { self.advance(); Ok(Expr::Str(s)) } Token::Bool(b) => { self.advance(); Ok(Expr::Bool(b)) } Token::Ident(name) => { self.advance(); Ok(Expr::Ident(name)) } Token::LParen => { self.advance(); let expr = self.parse_expr()?; self.expect(&Token::RParen)?; Ok(expr) } Token::LBracket => { self.advance(); let mut items = Vec::new(); if self.peek() != &Token::RBracket { items.push(self.parse_expr()?); while self.peek() == &Token::Comma { self.advance(); items.push(self.parse_expr()?); } } self.expect(&Token::RBracket)?; Ok(Expr::Array(items)) } t => Err(format!("unexpected token: {:?}", t)), } } } // --- Interpreter --- #[derive(Clone, Debug)] struct FnDef { params: Vec, body: Vec, } pub struct Interpreter { vars: HashMap, fns: HashMap, } const MAX_ITERATIONS: usize = 10_000; const MAX_CALL_DEPTH: u32 = 256; impl Interpreter { pub fn new() -> Self { Interpreter { vars: HashMap::new(), fns: HashMap::new(), } } pub fn exec_line(&mut self, line: &str) -> Result, String> { let trimmed = line.trim(); if trimmed.is_empty() { return Ok(None); } let tokens = tokenize(trimmed)?; let mut parser = Parser::new(tokens); let stmts = parser.parse_program()?; let mut last = Value::Void; for stmt in stmts { last = self.exec_stmt(&stmt, 0)?; } match last { Value::Void => Ok(None), v => Ok(Some(v)), } } pub fn eval_expr_str(&mut self, expr: &str) -> Result { let trimmed = expr.trim(); if trimmed.is_empty() { return Err("empty expression".into()); } let tokens = tokenize(trimmed)?; let mut parser = Parser::new(tokens); let e = parser.parse_expr()?; self.eval_expr(&e, 0) } fn exec_stmt(&mut self, stmt: &Stmt, depth: u32) -> Result { match stmt { Stmt::Let(name, type_ann, expr) => { let val = self.eval_expr(expr, depth)?; let val = apply_type_annotation(&val, type_ann.as_deref())?; self.vars.insert(name.clone(), val); Ok(Value::Void) } Stmt::Assign(name, expr) => { let val = self.eval_expr(expr, depth)?; self.vars.insert(name.clone(), val); Ok(Value::Void) } Stmt::While(cond, body) => { let mut iterations = 0; loop { let cv = self.eval_expr(cond, depth)?; if !cv.truthy() { break; } iterations += 1; if iterations > MAX_ITERATIONS { return Err(format!("loop exceeded {} iterations", MAX_ITERATIONS)); } let mut last = Value::Void; for s in body { last = self.exec_stmt(s, depth)?; } drop(last); } Ok(Value::Void) } Stmt::IfElse(cond, then_body, else_body) => { let cv = self.eval_expr(cond, depth)?; let body = if cv.truthy() { then_body } else { match else_body { Some(b) => b, None => return Ok(Value::Void) } }; let mut last = Value::Void; for s in body { last = self.exec_stmt(s, depth)?; } Ok(last) } Stmt::ForLoop(var, iter_expr, body) => { let iterable = self.eval_expr(iter_expr, depth)?; let items = match iterable { Value::Array(a) => a, _ => return Err("for loop requires an array or range".into()), }; let mut iterations = 0; let mut last = Value::Void; for item in &items { iterations += 1; if iterations > MAX_ITERATIONS { return Err(format!("loop exceeded {} iterations", MAX_ITERATIONS)); } self.vars.insert(var.clone(), item.clone()); for s in body { last = self.exec_stmt(s, depth)?; } } Ok(last) } Stmt::FnDef(name, params, body) => { self.fns.insert(name.clone(), FnDef { params: params.clone(), body: body.clone(), }); Ok(Value::Void) } Stmt::Return(expr) => { let val = self.eval_expr(expr, depth)?; Err(format!("\x00return:{}", encode_return(&val))) } Stmt::ExprStmt(expr) => { self.eval_expr(expr, depth) } } } fn eval_expr(&mut self, expr: &Expr, depth: u32) -> Result { match expr { Expr::Num(n) => Ok(Value::Number(*n)), Expr::Str(s) => Ok(Value::Str(s.clone())), Expr::Bool(b) => Ok(Value::Bool(*b)), Expr::Ident(name) => { self.vars.get(name).cloned() .ok_or_else(|| format!("undefined variable '{}'", name)) } Expr::Array(items) => { let mut vals = Vec::new(); for item in items { vals.push(self.eval_expr(item, depth)?); } Ok(Value::Array(vals)) } Expr::UnaryOp(Op::Not, inner) => { let v = self.eval_expr(inner, depth)?; Ok(Value::Bool(!v.truthy())) } Expr::UnaryOp(Op::Neg, inner) => { let v = self.eval_expr(inner, depth)?; match v { Value::Number(n) => Ok(Value::Number(-n)), _ => Err("cannot negate non-number".into()), } } Expr::UnaryOp(op, _) => Err(format!("invalid unary op: {:?}", op)), Expr::BinOp(op, lhs, rhs) => self.eval_binop(op, lhs, rhs, depth), Expr::Call(name, args) => self.eval_call(name, args, depth), Expr::Index(target, index) => { let target_val = self.eval_expr(target, depth)?; let index_val = self.eval_expr(index, depth)?; match (&target_val, &index_val) { (Value::Array(arr), Value::Number(n)) => { let i = *n as i64; let idx = if i < 0 { (arr.len() as i64 + i) as usize } else { i as usize }; arr.get(idx).cloned().ok_or_else(|| format!("index {} out of bounds (len {})", i, arr.len())) } (Value::Str(s), Value::Number(n)) => { let i = *n as i64; let chars: Vec = s.chars().collect(); let idx = if i < 0 { (chars.len() as i64 + i) as usize } else { i as usize }; chars.get(idx).map(|c| Value::Str(c.to_string())) .ok_or_else(|| format!("index {} out of bounds (len {})", i, chars.len())) } _ => Err(format!("cannot index {} with {}", type_name(&target_val), type_name(&index_val))), } } Expr::Range(start, end) => { let sv = self.eval_expr(start, depth)?; let ev = self.eval_expr(end, depth)?; match (&sv, &ev) { (Value::Number(a), Value::Number(b)) => { let a = *a as i64; let b = *b as i64; let items: Vec = (a..b).map(|n| Value::Number(n as f64)).collect(); if items.len() > MAX_ITERATIONS { return Err(format!("range too large ({} elements)", items.len())); } Ok(Value::Array(items)) } _ => Err("range requires two numbers".into()), } } } } fn eval_binop(&mut self, op: &Op, lhs: &Expr, rhs: &Expr, depth: u32) -> Result { // short-circuit for logical ops if matches!(op, Op::And) { let l = self.eval_expr(lhs, depth)?; if !l.truthy() { return Ok(Value::Bool(false)); } let r = self.eval_expr(rhs, depth)?; return Ok(Value::Bool(r.truthy())); } if matches!(op, Op::Or) { let l = self.eval_expr(lhs, depth)?; if l.truthy() { return Ok(Value::Bool(true)); } let r = self.eval_expr(rhs, depth)?; return Ok(Value::Bool(r.truthy())); } let l = self.eval_expr(lhs, depth)?; let r = self.eval_expr(rhs, depth)?; match (op, &l, &r) { // number arithmetic (Op::Add, Value::Number(a), Value::Number(b)) => Ok(Value::Number(a + b)), (Op::Sub, Value::Number(a), Value::Number(b)) => Ok(Value::Number(a - b)), (Op::Mul, Value::Number(a), Value::Number(b)) => Ok(Value::Number(a * b)), (Op::Div, Value::Number(_, ), Value::Number(b)) if *b == 0.0 => Err("division by zero".into()), (Op::Div, Value::Number(a), Value::Number(b)) => Ok(Value::Number(a / b)), (Op::Mod, Value::Number(a), Value::Number(b)) => Ok(Value::Number(a % b)), (Op::Pow, Value::Number(a), Value::Number(b)) => Ok(Value::Number(a.powf(*b))), // string concatenation (Op::Add, Value::Str(a), Value::Str(b)) => Ok(Value::Str(format!("{}{}", a, b))), (Op::Add, Value::Str(a), Value::Number(b)) => Ok(Value::Str(format!("{}{}", a, format_number(*b)))), (Op::Add, Value::Number(a), Value::Str(b)) => Ok(Value::Str(format!("{}{}", format_number(*a), b))), (Op::Add, Value::Str(a), Value::Bool(b)) => Ok(Value::Str(format!("{}{}", a, b))), (Op::Add, Value::Bool(a), Value::Str(b)) => Ok(Value::Str(format!("{}{}", a, b))), // number comparisons (Op::Lt, Value::Number(a), Value::Number(b)) => Ok(Value::Bool(a < b)), (Op::Gt, Value::Number(a), Value::Number(b)) => Ok(Value::Bool(a > b)), (Op::Lte, Value::Number(a), Value::Number(b)) => Ok(Value::Bool(a <= b)), (Op::Gte, Value::Number(a), Value::Number(b)) => Ok(Value::Bool(a >= b)), // equality (Op::Eq, Value::Number(a), Value::Number(b)) => Ok(Value::Bool(a == b)), (Op::Eq, Value::Str(a), Value::Str(b)) => Ok(Value::Bool(a == b)), (Op::Eq, Value::Bool(a), Value::Bool(b)) => Ok(Value::Bool(a == b)), (Op::Eq, _, _) => Ok(Value::Bool(false)), (Op::Neq, Value::Number(a), Value::Number(b)) => Ok(Value::Bool(a != b)), (Op::Neq, Value::Str(a), Value::Str(b)) => Ok(Value::Bool(a != b)), (Op::Neq, Value::Bool(a), Value::Bool(b)) => Ok(Value::Bool(a != b)), (Op::Neq, _, _) => Ok(Value::Bool(true)), _ => Err(format!("type error: cannot apply {:?} to {:?} and {:?}", op, type_name(&l), type_name(&r))), } } fn eval_call(&mut self, name: &str, args: &[Expr], depth: u32) -> Result { if depth >= MAX_CALL_DEPTH { return Err("maximum call depth exceeded".into()); } // built-in math functions match name { "sin" | "cos" | "tan" | "asin" | "acos" | "atan" | "sqrt" | "abs" | "floor" | "ceil" | "round" | "ln" | "log" => { if args.len() != 1 { return Err(format!("{}() expects 1 argument", name)); } let v = self.eval_expr(&args[0], depth)?; let n = match v { Value::Number(n) => n, _ => return Err(format!("{}() expects a number", name)), }; let result = match name { "sin" => n.sin(), "cos" => n.cos(), "tan" => n.tan(), "asin" => n.asin(), "acos" => n.acos(), "atan" => n.atan(), "sqrt" => n.sqrt(), "abs" => n.abs(), "floor" => n.floor(), "ceil" => n.ceil(), "round" => n.round(), "ln" => n.ln(), "log" => n.log10(), _ => unreachable!(), }; return Ok(Value::Number(result)); } "len" => { if args.len() != 1 { return Err("len() expects 1 argument".into()); } let v = self.eval_expr(&args[0], depth)?; return match v { Value::Str(s) => Ok(Value::Number(s.len() as f64)), Value::Array(a) => Ok(Value::Number(a.len() as f64)), _ => Err("len() expects a string or array".into()), }; } "range" => { if args.len() != 2 { return Err("range() expects 2 arguments".into()); } let start = match self.eval_expr(&args[0], depth)? { Value::Number(n) => n as i64, _ => return Err("range() expects numbers".into()), }; let end = match self.eval_expr(&args[1], depth)? { Value::Number(n) => n as i64, _ => return Err("range() expects numbers".into()), }; let items: Vec = (start..end).map(|n| Value::Number(n as f64)).collect(); if items.len() > MAX_ITERATIONS { return Err(format!("range too large ({} elements)", items.len())); } return Ok(Value::Array(items)); } "push" => { if args.len() != 2 { return Err("push() expects 2 arguments (array, value)".into()); } let arr = self.eval_expr(&args[0], depth)?; let val = self.eval_expr(&args[1], depth)?; return match arr { Value::Array(mut a) => { a.push(val); Ok(Value::Array(a)) } _ => Err("push() expects an array as first argument".into()), }; } _ => {} } let fdef = self.fns.get(name).cloned() .ok_or_else(|| format!("undefined function '{}'", name))?; if args.len() != fdef.params.len() { return Err(format!("{}() expects {} arguments, got {}", name, fdef.params.len(), args.len())); } let mut arg_vals = Vec::new(); for arg in args { arg_vals.push(self.eval_expr(arg, depth)?); } // save current vars, set up function scope let saved_vars = self.vars.clone(); for (param, val) in fdef.params.iter().zip(arg_vals) { self.vars.insert(param.clone(), val); } let mut result = Value::Void; for stmt in &fdef.body { match self.exec_stmt(stmt, depth + 1) { Ok(v) => result = v, Err(e) if e.starts_with('\x00') => { self.vars = saved_vars; return Ok(decode_return(&e)); } Err(e) => { self.vars = saved_vars; return Err(e); } } } self.vars = saved_vars; Ok(result) } } const RETURN_PREFIX: &str = "\x00return:"; fn encode_return(val: &Value) -> String { match val { Value::Number(n) => format!("n:{}", n), Value::Bool(b) => format!("b:{}", b), Value::Str(s) => format!("s:{}", s), Value::Void => "v:".into(), _ => format!("s:{}", val.display()), } } fn decode_return(encoded: &str) -> Value { let payload = &encoded[RETURN_PREFIX.len()..]; if let Some(rest) = payload.strip_prefix("n:") { rest.parse::().map(Value::Number).unwrap_or(Value::Void) } else if let Some(rest) = payload.strip_prefix("b:") { Value::Bool(rest == "true") } else if let Some(rest) = payload.strip_prefix("s:") { Value::Str(rest.to_string()) } else { Value::Void } } fn type_name(v: &Value) -> &'static str { match v { Value::Number(_) => "number", Value::Bool(_) => "bool", Value::Str(_) => "str", Value::Array(_) => "array", Value::Void => "void", Value::Error(_) => "error", } } fn apply_type_annotation(val: &Value, ann: Option<&str>) -> Result { let ann = match ann { Some(a) => a, None => return Ok(val.clone()), }; match ann { "int" => match val { Value::Number(n) => Ok(Value::Number(n.trunc())), _ => Err(format!("type error: expected int, got {}", type_name(val))), }, "float" => match val { Value::Number(_) => Ok(val.clone()), _ => Err(format!("type error: expected float, got {}", type_name(val))), }, "bool" => match val { Value::Bool(_) => Ok(val.clone()), Value::Number(n) => { if *n == 0.0 { Ok(Value::Bool(false)) } else if *n == 1.0 { Ok(Value::Bool(true)) } else { Err(format!("{} is not a valid bool", format_number(*n))) } } _ => Err(format!("type error: expected bool, got {}", type_name(val))), }, "str" => match val { Value::Str(_) => Ok(val.clone()), _ => Err(format!("type error: expected str, got {}", type_name(val))), }, other => Err(format!("unknown type annotation: {}", other)), } } // --- Public API for eval.rs integration --- pub struct InterpResult { pub line: usize, pub value: Option, pub format: EvalFormat, } #[derive(Debug, Clone, PartialEq)] pub enum EvalFormat { Inline, Table, Tree, } pub fn interpret_document(lines: &[(usize, &str, bool)]) -> Vec { // lines: (line_index, content, is_eval) // Collect all cordial lines and eval lines in order. // For each eval line, evaluate the expression and record the result. let mut interp = Interpreter::new(); let mut results = Vec::new(); // First pass: collect the entire program from cordial lines // and evaluate line by line, recording results for eval lines let mut brace_depth: i32 = 0; let mut block_acc: Vec = Vec::new(); for &(idx, content, is_eval) in lines { if is_eval { if !block_acc.is_empty() { let block_text = block_acc.join("\n"); block_acc.clear(); brace_depth = 0; match interp.exec_line(&block_text) { Ok(_) => {} Err(e) => { results.push(InterpResult { line: idx, value: Some(Value::Error(e)), format: EvalFormat::Inline }); continue; } } } let trimmed = content.trim(); let (format, expr) = if let Some(rest) = trimmed.strip_prefix("/=|") { (EvalFormat::Table, rest.trim()) } else if let Some(rest) = trimmed.strip_prefix("/=\\") { (EvalFormat::Tree, rest.trim()) } else { (EvalFormat::Inline, trimmed.strip_prefix("/=").unwrap_or("").trim()) }; if expr.is_empty() { results.push(InterpResult { line: idx, value: Some(Value::Error("empty expression".into())), format }); continue; } match interp.eval_expr_str(expr) { Ok(val) => results.push(InterpResult { line: idx, value: Some(val), format }), Err(e) => results.push(InterpResult { line: idx, value: Some(Value::Error(e)), format }), } } else { let trimmed = content.trim(); // track brace depth for multi-line blocks let opens = trimmed.matches('{').count() as i32; let closes = trimmed.matches('}').count() as i32; if brace_depth > 0 || !block_acc.is_empty() { block_acc.push(trimmed.to_string()); brace_depth += opens - closes; if brace_depth <= 0 { let block_text = block_acc.join("\n"); block_acc.clear(); brace_depth = 0; if let Err(e) = interp.exec_line(&block_text) { results.push(InterpResult { line: idx, value: Some(Value::Error(e)), format: EvalFormat::Inline }); } } } else if opens > closes { block_acc.push(trimmed.to_string()); brace_depth = opens - closes; } else { if let Err(e) = interp.exec_line(trimmed) { results.push(InterpResult { line: idx, value: Some(Value::Error(e)), format: EvalFormat::Inline }); } } } } results } // --- Display helpers for type-annotated int --- pub fn display_value_with_type(val: &Value, type_ann: Option<&str>) -> String { match (val, type_ann) { (Value::Number(n), Some("int")) => format!("{}", *n as i64), _ => val.display(), } } #[cfg(test)] mod tests { use super::*; fn eval(input: &str) -> String { let lines: Vec<&str> = input.lines().collect(); let mut tagged: Vec<(usize, &str, bool)> = Vec::new(); for (i, line) in lines.iter().enumerate() { let is_eval = line.trim().starts_with("/="); let is_comment = line.trim().starts_with("//"); if !is_eval && !is_comment && !line.trim().is_empty() && !line.trim().starts_with('#') { tagged.push((i, line, false)); } else if is_eval { tagged.push((i, line, true)); } } let results = interpret_document(&tagged); results.iter() .filter_map(|r| r.value.as_ref().map(|v| v.display())) .collect::>() .join(", ") } fn eval_one(input: &str) -> String { let mut interp = Interpreter::new(); match interp.eval_expr_str(input) { Ok(v) => v.display(), Err(e) => format!("error: {}", e), } } #[test] fn basic_arithmetic() { assert_eq!(eval_one("2 + 3"), "5"); assert_eq!(eval_one("10 - 4"), "6"); assert_eq!(eval_one("3 * 7"), "21"); assert_eq!(eval_one("15 / 3"), "5"); assert_eq!(eval_one("2 ^ 10"), "1024"); assert_eq!(eval_one("10 % 3"), "1"); } #[test] fn string_literals() { assert_eq!(eval_one("\"hello\""), "hello"); assert_eq!(eval_one("\"hello\" + \" \" + \"world\""), "hello world"); } #[test] fn string_concatenation_mixed() { assert_eq!(eval_one("\"val: \" + 42"), "val: 42"); assert_eq!(eval_one("100 + \" items\""), "100 items"); } #[test] fn boolean_literals() { assert_eq!(eval_one("true"), "true"); assert_eq!(eval_one("false"), "false"); } #[test] fn comparison_operators() { assert_eq!(eval_one("1 < 2"), "true"); assert_eq!(eval_one("2 > 3"), "false"); assert_eq!(eval_one("5 == 5"), "true"); assert_eq!(eval_one("5 != 3"), "true"); assert_eq!(eval_one("3 <= 3"), "true"); assert_eq!(eval_one("4 >= 5"), "false"); } #[test] fn logical_operators() { assert_eq!(eval_one("true && false"), "false"); assert_eq!(eval_one("true || false"), "true"); assert_eq!(eval_one("!true"), "false"); assert_eq!(eval_one("!false"), "true"); } #[test] fn arrays() { assert_eq!(eval_one("[1, 2, 3]"), "[1, 2, 3]"); assert_eq!(eval_one("[1, \"two\", true]"), "[1, \"two\", true]"); assert_eq!(eval_one("[]"), "[]"); } #[test] fn variable_binding() { let input = "let x = 5\n/= x + 10"; assert_eq!(eval(input), "15"); } #[test] fn variable_reassignment() { let input = "let x = 5\nx = 10\n/= x"; assert_eq!(eval(input), "10"); } #[test] fn while_loop() { let input = "let i = 0\nlet sum = 0\nwhile (i < 10) {\n sum = sum + i\n i = i + 1\n}\n/= sum"; assert_eq!(eval(input), "45"); } #[test] fn while_loop_guard() { let input = "let i = 0\nwhile (true) {\n i = i + 1\n}\n/= i"; let result = eval(input); assert!(result.contains("error"), "should error on infinite loop: {}", result); } #[test] fn function_def_and_call() { let input = "fn add(a, b) {\n a + b\n}\n/= add(3, 4)"; assert_eq!(eval(input), "7"); } #[test] fn function_calling_function() { let input = "fn double(x) {\n x * 2\n}\nfn quad(x) {\n double(double(x))\n}\n/= quad(5)"; assert_eq!(eval(input), "20"); } #[test] fn type_annotation_int() { let input = "let x: int = 3.7\n/= x"; assert_eq!(eval(input), "3"); } #[test] fn type_annotation_bool_valid() { let input = "let x: bool = 1\n/= x"; assert_eq!(eval(input), "true"); } #[test] fn type_annotation_bool_zero() { let input = "let x: bool = 0\n/= x"; assert_eq!(eval(input), "false"); } #[test] fn type_annotation_bool_invalid() { let input = "let x: bool = 2\n/= x"; let result = eval(input); assert!(result.contains("error"), "should error: {}", result); } #[test] fn type_annotation_str() { let input = "let x: str = \"hello\"\n/= x"; assert_eq!(eval(input), "hello"); } #[test] fn type_annotation_str_mismatch() { let input = "let x: str = 42\n/= x"; let result = eval(input); assert!(result.contains("error"), "should error: {}", result); } #[test] fn error_undefined_variable() { let result = eval("/= undefined_var"); assert!(result.contains("error"), "should error: {}", result); assert!(result.contains("undefined variable"), "{}", result); } #[test] fn error_recovery() { let input = "let x = bad_var\nlet y = 5\n/= y"; // x assignment fails, but y should still work let result = eval(input); assert!(result.contains("5"), "should recover and eval y: {}", result); } #[test] fn error_undefined_function() { let result = eval("/= nope(1, 2)"); assert!(result.contains("error"), "should error: {}", result); } #[test] fn multiple_evals() { let input = "let a = 3\n/= a\nlet b = 7\n/= a + b"; assert_eq!(eval(input), "3, 10"); } #[test] fn builtin_math_functions() { assert_eq!(eval_one("abs(-5)"), "5"); assert_eq!(eval_one("floor(3.7)"), "3"); assert_eq!(eval_one("ceil(3.2)"), "4"); assert_eq!(eval_one("sqrt(16)"), "4"); } #[test] fn nested_expressions() { assert_eq!(eval_one("(2 + 3) * (4 - 1)"), "15"); assert_eq!(eval_one("2 * (3 + 4 * 5)"), "46"); } #[test] fn string_variable() { let input = "let x = \"hello\"\nlet y = \"world\"\n/= x + \" \" + y"; assert_eq!(eval(input), "hello world"); } #[test] fn division_by_zero() { let result = eval_one("1 / 0"); assert!(result.contains("error"), "should error on div by zero: {}", result); } #[test] fn len_function() { assert_eq!(eval_one("len(\"hello\")"), "5"); assert_eq!(eval_one("len([1, 2, 3])"), "3"); } #[test] fn negative_numbers() { assert_eq!(eval_one("-5"), "-5"); assert_eq!(eval_one("-3 + 7"), "4"); assert_eq!(eval_one("10 + -3"), "7"); } #[test] fn empty_array() { assert_eq!(eval_one("len([])"), "0"); } #[test] fn complex_while_with_function() { let input = "\ fn fib(n) { let a = 0 let b = 1 let i = 0 while (i < n) { let tmp = b b = a + b a = tmp i = i + 1 } a } /= fib(10)"; assert_eq!(eval(input), "55"); } #[test] fn if_true() { let input = "let x = 10\nif (x > 5) {\n x = 100\n}\n/= x"; assert_eq!(eval(input), "100"); } #[test] fn if_false() { let input = "let x = 3\nif (x > 5) {\n x = 100\n}\n/= x"; assert_eq!(eval(input), "3"); } #[test] fn if_else() { let input = "let x = 3\nif (x > 5) {\n x = 100\n} else {\n x = 0\n}\n/= x"; assert_eq!(eval(input), "0"); } #[test] fn if_else_chain() { let input = "\ let x = 5 let r = 0 if (x > 10) { r = 3 } else if (x > 3) { r = 2 } else { r = 1 } /= r"; assert_eq!(eval(input), "2"); } #[test] fn if_without_parens() { let input = "let x = 10\nif x > 5 {\n x = 100\n}\n/= x"; assert_eq!(eval(input), "100"); } #[test] fn for_loop_array() { let input = "let sum = 0\nfor x in [1, 2, 3, 4, 5] {\n sum = sum + x\n}\n/= sum"; assert_eq!(eval(input), "15"); } #[test] fn for_loop_range() { let input = "let sum = 0\nfor i in 0..5 {\n sum = sum + i\n}\n/= sum"; assert_eq!(eval(input), "10"); } #[test] fn for_loop_range_fn() { let input = "let sum = 0\nfor i in range(1, 6) {\n sum = sum + i\n}\n/= sum"; assert_eq!(eval(input), "15"); } #[test] fn array_index() { assert_eq!(eval_one("[10, 20, 30][1]"), "20"); } #[test] fn array_index_variable() { let input = "let arr = [10, 20, 30]\n/= arr[2]"; assert_eq!(eval(input), "30"); } #[test] fn array_negative_index() { assert_eq!(eval_one("[10, 20, 30][-1]"), "30"); } #[test] fn string_index() { assert_eq!(eval_one("\"hello\"[0]"), "h"); } #[test] fn array_index_out_of_bounds() { let result = eval_one("[1, 2][5]"); assert!(result.contains("error"), "should error: {}", result); } #[test] fn return_from_function() { let input = "\ fn first_positive(a, b) { if (a > 0) { return a } if (b > 0) { return b } return 0 } /= first_positive(-1, 5)"; assert_eq!(eval(input), "5"); } #[test] fn return_early_from_loop() { let input = "\ fn find(arr, target) { for x in arr { if (x == target) { return x } } return -1 } /= find([1, 2, 3, 4], 3)"; assert_eq!(eval(input), "3"); } #[test] fn push_builtin() { let input = "let arr = [1, 2]\nlet arr = push(arr, 3)\n/= arr"; assert_eq!(eval(input), "[1, 2, 3]"); } #[test] fn range_expression() { assert_eq!(eval_one("0..5"), "[0, 1, 2, 3, 4]"); } }