diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..2fe12b9 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,32 @@ +use std::fmt; + +#[derive(Debug)] +pub enum Error { + SyntaxError(String), + IndexOutOfBounds(String), + VariableNotDeclared(String), + VariableAlreadyDeclared(String), + TypeError(String), + UnsupportedOperation(String), + BreakOutsideLoop, + ContinueOutsideLoop, + //UnknownError(String), +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Error::SyntaxError(msg) => write!(f, "SyntaxError: {}", msg), + Error::IndexOutOfBounds(msg) => write!(f, "IndexOutOfBounds: {}", msg), + Error::VariableNotDeclared(msg) => write!(f, "VariableNotDeclared: {}", msg), + Error::VariableAlreadyDeclared(msg) => write!(f, "VariableAlreadyDeclared: {}", msg), + Error::TypeError(msg) => write!(f, "TypeError: {}", msg), + Error::UnsupportedOperation(msg) => write!(f, "UnsupportedOperation: {}", msg), + Error::BreakOutsideLoop => write!(f, "Break statement outside of loop"), + Error::ContinueOutsideLoop => write!(f, "Continue statement outside of loop"), + // Error::UnknownError(msg) => write!(f, "UnknownError: {}", msg), + } + } +} + +impl std::error::Error for Error {} diff --git a/src/interpreter.rs b/src/interpreter.rs index 368427b..0fafb2b 100755 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -1,5 +1,6 @@ use crate::parser::{ASTNode, Value}; use crate::lexer::Token; +use crate::error::Error; use std::collections::HashMap; use std::fmt; @@ -28,172 +29,172 @@ impl fmt::Display for Value { } } -pub fn interpret(ast: Vec, is_verbose: bool) -> Option { +pub fn interpret(ast: Vec, is_verbose: bool) -> Result, Error> { let mut symbol_table: HashMap = HashMap::new(); // (Value, is_mutable) let mut result = None; for node in ast { - result = Some(interpret_node(&node, &mut symbol_table, is_verbose, false)); + result = Some(interpret_node(&node, &mut symbol_table, is_verbose, false)?); if let Value::Break = result.as_ref().unwrap() { - panic!("Break statement outside of loop"); + return Err(Error::BreakOutsideLoop); } if let Value::Continue = result.as_ref().unwrap() { - panic!("Continue statement outside of loop"); + return Err(Error::ContinueOutsideLoop); } } - result + Ok(result) } -fn interpret_node(node: &ASTNode, symbol_table: &mut HashMap, is_verbose: bool, in_loop: bool) -> Value { +fn interpret_node(node: &ASTNode, symbol_table: &mut HashMap, is_verbose: bool, in_loop: bool) -> Result { match node { - ASTNode::Number(val) => Value::Number(*val), - ASTNode::String(val) => Value::String(val.clone()), - ASTNode::Float(val) => Value::Float(*val), - ASTNode::Boolean(val) => Value::Boolean(*val), - ASTNode::Null => Value::Null, + ASTNode::Number(val) => Ok(Value::Number(*val)), + ASTNode::String(val) => Ok(Value::String(val.clone())), + ASTNode::Float(val) => Ok(Value::Float(*val)), + ASTNode::Boolean(val) => Ok(Value::Boolean(*val)), + ASTNode::Null => Ok(Value::Null), ASTNode::BinaryOp(left, op, right) => { - let left_val = interpret_node(left, symbol_table, is_verbose, in_loop); + let left_val = interpret_node(left, symbol_table, is_verbose, in_loop)?; match op { Token::And => { if let Value::Boolean(false) = left_val { - return Value::Boolean(false); + return Ok(Value::Boolean(false)); } - let right_val = interpret_node(right, symbol_table, is_verbose, in_loop); + let right_val = interpret_node(right, symbol_table, is_verbose, in_loop)?; match (left_val, right_val) { - (Value::Boolean(l), Value::Boolean(r)) => Value::Boolean(l && r), - _ => panic!("AND operator can only be applied to boolean values"), + (Value::Boolean(l), Value::Boolean(r)) => Ok(Value::Boolean(l && r)), + _ => Err(Error::TypeError(format!("AND operator can only be applied to boolean values"))), } }, Token::Or => { if let Value::Boolean(true) = left_val { - return Value::Boolean(true); + return Ok(Value::Boolean(true)); } - let right_val = interpret_node(right, symbol_table, is_verbose, in_loop); + let right_val = interpret_node(right, symbol_table, is_verbose, in_loop)?; match (left_val, right_val) { - (Value::Boolean(l), Value::Boolean(r)) => Value::Boolean(l || r), - _ => panic!("OR operator can only be applied to boolean values"), + (Value::Boolean(l), Value::Boolean(r)) => Ok(Value::Boolean(l || r)), + _ => Err(Error::TypeError(format!("OR operator can only be applied to boolean values"))), } }, _ => { - let right_val = interpret_node(right, symbol_table, is_verbose, in_loop); + let right_val = interpret_node(right, symbol_table, is_verbose, in_loop)?; match (left_val, right_val) { (Value::Number(l), Value::Number(r)) => { match op { - Token::Plus => Value::Number(l + r), - Token::Minus => Value::Number(l - r), - Token::Multiply => Value::Number(l * r), - Token::Divide => Value::Float(l as f64 / r as f64), - Token::Equal => Value::Boolean(l == r), - Token::NotEqual => Value::Boolean(l != r), - Token::Greater => Value::Boolean(l > r), - Token::Less => Value::Boolean(l < r), - Token::GreaterEqual => Value::Boolean(l >= r), - Token::FloorDivide => Value::Number(l / r), - Token::LessEqual => Value::Boolean(l <= r), - Token::Modulus => Value::Number(l % r), - Token::Power => Value::Number(l.pow(r as u32)), - _ => panic!("Unsupported operator for numbers"), + Token::Plus => Ok(Value::Number(l + r)), + Token::Minus => Ok(Value::Number(l - r)), + Token::Multiply => Ok(Value::Number(l * r)), + Token::Divide => Ok(Value::Float(l as f64 / r as f64)), + Token::Equal => Ok(Value::Boolean(l == r)), + Token::NotEqual => Ok(Value::Boolean(l != r)), + Token::Greater => Ok(Value::Boolean(l > r)), + Token::Less => Ok(Value::Boolean(l < r)), + Token::GreaterEqual => Ok(Value::Boolean(l >= r)), + Token::FloorDivide => Ok(Value::Number(l / r)), + Token::LessEqual => Ok(Value::Boolean(l <= r)), + Token::Modulus => Ok(Value::Number(l % r)), + Token::Power => Ok(Value::Number(l.pow(r as u32))), + _ => Err(Error::UnsupportedOperation(format!("Unsupported operator for numbers"))), } } - (Value::Float(l), Value::Float(r)) => { + (Value::Float(l), Value::Float(r)) => { match op { - Token::Plus => Value::Float(l + r), - Token::Minus => Value::Float(l - r), - Token::Multiply => Value::Float(l * r), - Token::Divide => Value::Float(l / r), - Token::Equal => Value::Boolean(l == r), - Token::NotEqual => Value::Boolean(l != r), - Token::Greater => Value::Boolean(l > r), - Token::Modulus => Value::Float(l % r), - Token::FloorDivide => Value::Number((l / r).floor() as i32), - Token::Less => Value::Boolean(l < r), - Token::GreaterEqual => Value::Boolean(l >= r), - Token::LessEqual => Value::Boolean(l <= r), - Token::Power => Value::Float(l.powf(r)), - _ => panic!("Unsupported operator for floats"), + Token::Plus => Ok(Value::Float(l + r)), + Token::Minus => Ok(Value::Float(l - r)), + Token::Multiply => Ok(Value::Float(l * r)), + Token::Divide => Ok(Value::Float(l / r)), + Token::Equal => Ok(Value::Boolean(l == r)), + Token::NotEqual => Ok(Value::Boolean(l != r)), + Token::Greater => Ok(Value::Boolean(l > r)), + Token::Modulus => Ok(Value::Float(l % r)), + Token::FloorDivide => Ok(Value::Number((l / r).floor() as i32)), + Token::Less => Ok(Value::Boolean(l < r)), + Token::GreaterEqual => Ok(Value::Boolean(l >= r)), + Token::LessEqual => Ok(Value::Boolean(l <= r)), + Token::Power => Ok(Value::Float(l.powf(r))), + _ => Err(Error::UnsupportedOperation(format!("Unsupported operator for floats"))), } } - (Value::Number(l), Value::Float(r)) => { + (Value::Number(l), Value::Float(r)) => { let l = l as f64; match op { - Token::Plus => Value::Float(l + r), - Token::Minus => Value::Float(l - r), - Token::Multiply => Value::Float(l * r), - Token::Divide => Value::Float(l / r), - Token::Equal => Value::Boolean(l == r), - Token::Modulus => Value::Float(l % r), - Token::NotEqual => Value::Boolean(l != r), - Token::FloorDivide => Value::Number((l / r).floor() as i32), - Token::Greater => Value::Boolean(l > r), - Token::Less => Value::Boolean(l < r), - Token::GreaterEqual => Value::Boolean(l >= r), - Token::Power => Value::Float(l.powf(r)), - Token::LessEqual => Value::Boolean(l <= r), - _ => panic!("Unsupported operator for mixed number and float"), + Token::Plus => Ok(Value::Float(l + r)), + Token::Minus => Ok(Value::Float(l - r)), + Token::Multiply => Ok(Value::Float(l * r)), + Token::Divide => Ok(Value::Float(l / r)), + Token::Equal => Ok(Value::Boolean(l == r)), + Token::Modulus => Ok(Value::Float(l % r)), + Token::NotEqual => Ok(Value::Boolean(l != r)), + Token::FloorDivide => Ok(Value::Number((l / r).floor() as i32)), + Token::Greater => Ok(Value::Boolean(l > r)), + Token::Less => Ok(Value::Boolean(l < r)), + Token::GreaterEqual => Ok(Value::Boolean(l >= r)), + Token::Power => Ok(Value::Float(l.powf(r))), + Token::LessEqual => Ok(Value::Boolean(l <= r)), + _ => Err(Error::UnsupportedOperation(format!("Unsupported operator for mixed number and float"))), } } - (Value::Float(l), Value::Number(r)) => { + (Value::Float(l), Value::Number(r)) => { let r = r as f64; match op { - Token::Plus => Value::Float(l + r), - Token::Minus => Value::Float(l - r), - Token::Multiply => Value::Float(l * r), - Token::Divide => Value::Float(l / r), - Token::Equal => Value::Boolean(l == r), - Token::Modulus => Value::Float(l % r), - Token::NotEqual => Value::Boolean(l != r), - Token::Greater => Value::Boolean(l > r), - Token::Less => Value::Boolean(l < r), - Token::GreaterEqual => Value::Boolean(l >= r), - Token::FloorDivide => Value::Number((l / r).floor() as i32), - Token::Power => Value::Float(l.powf(r)), - Token::LessEqual => Value::Boolean(l <= r), - _ => panic!("Unsupported operator for mixed float and number"), + Token::Plus => Ok(Value::Float(l + r)), + Token::Minus => Ok(Value::Float(l - r)), + Token::Multiply => Ok(Value::Float(l * r)), + Token::Divide => Ok(Value::Float(l / r)), + Token::Equal => Ok(Value::Boolean(l == r)), + Token::Modulus => Ok(Value::Float(l % r)), + Token::NotEqual => Ok(Value::Boolean(l != r)), + Token::Greater => Ok(Value::Boolean(l > r)), + Token::Less => Ok(Value::Boolean(l < r)), + Token::GreaterEqual => Ok(Value::Boolean(l >= r)), + Token::FloorDivide => Ok(Value::Number((l / r).floor() as i32)), + Token::Power => Ok(Value::Float(l.powf(r))), + Token::LessEqual => Ok(Value::Boolean(l <= r)), + _ => Err(Error::UnsupportedOperation(format!("Unsupported operator for mixed float and number"))), } } (Value::String(s), Value::String(t)) => { match op { - Token::Plus => Value::String(s + &t), + Token::Plus => Ok(Value::String(s + &t)), Token::Multiply => { - if let Value::Number(n) = interpret_node(right, symbol_table, is_verbose, in_loop) { - Value::String(s.repeat(n as usize)) + if let Value::Number(n) = interpret_node(right, symbol_table, is_verbose, in_loop)? { + Ok(Value::String(s.repeat(n as usize))) } else { - panic!("String can only be multiplied by an integer") + Err(Error::TypeError(format!("String can only be multiplied by an integer"))) } } - Token::Equal => Value::Boolean(s == t), - Token::NotEqual => Value::Boolean(s != t), - _ => panic!("Unsupported operator for strings"), + Token::Equal => Ok(Value::Boolean(s == t)), + Token::NotEqual => Ok(Value::Boolean(s != t)), + _ => Err(Error::UnsupportedOperation(format!("Unsupported operator for strings"))), } } (Value::String(s), Value::Number(n)) => { match op { - Token::Multiply => Value::String(s.repeat(n as usize)), - _ => panic!("Unsupported operation between string and number"), + Token::Multiply => Ok(Value::String(s.repeat(n as usize))), + _ => Err(Error::UnsupportedOperation(format!("Unsupported operation between string and number"))), } } (Value::Number(n), Value::String(s)) => { match op { - Token::Multiply => Value::String(s.repeat(n as usize)), - _ => panic!("Unsupported operation between number and string"), + Token::Multiply => Ok(Value::String(s.repeat(n as usize))), + _ => Err(Error::UnsupportedOperation(format!("Unsupported operation between number and string"))), } } (Value::Boolean(b1), Value::Boolean(b2)) => { match op { - Token::Equal => Value::Boolean(b1 == b2), - Token::NotEqual => Value::Boolean(b1 != b2), - _ => panic!("Unsupported operator for booleans"), + Token::Equal => Ok(Value::Boolean(b1 == b2)), + Token::NotEqual => Ok(Value::Boolean(b1 != b2)), + _ => Err(Error::UnsupportedOperation(format!("Unsupported operator for booleans"))), } } (Value::Type(t1), Value::Type(t2)) => { match op { - Token::Equal => Value::Boolean(t1 == t2), - Token::NotEqual => Value::Boolean(t1 != t2), - _ => panic!("Unsupported operator for types"), + Token::Equal => Ok(Value::Boolean(t1 == t2)), + Token::NotEqual => Ok(Value::Boolean(t1 != t2)), + _ => Err(Error::UnsupportedOperation(format!("Unsupported operator for types"))), } } - _ => panic!("Unsupported operation for given types"), + _ => Err(Error::UnsupportedOperation(format!("Unsupported operation for given types"))), } } } @@ -202,67 +203,67 @@ fn interpret_node(node: &ASTNode, symbol_table: &mut HashMap = elements .iter() .map(|elem| interpret_node(elem, symbol_table, is_verbose, in_loop)) - .collect(); - Value::Array(values) + .collect::>()?; + Ok(Value::Array(values)) }, ASTNode::Index(expr, index) => { - let array = interpret_node(expr, symbol_table, is_verbose, in_loop); - let index = interpret_node(index, symbol_table, is_verbose, in_loop); - + let array = interpret_node(expr, symbol_table, is_verbose, in_loop)?; + let index = interpret_node(index, symbol_table, is_verbose, in_loop)?; + match (array, index) { (Value::Array(arr), Value::Number(i)) => { if i < 0 || i >= arr.len() as i32 { - panic!("Index out of bounds"); + return Err(Error::IndexOutOfBounds(format!("Index out of bounds"))); } - arr[i as usize].clone() + Ok(arr[i as usize].clone()) }, (Value::String(s), Value::Number(i)) => { if i < 0 || i >= s.len() as i32 { - panic!("Index out of bounds"); + return Err(Error::IndexOutOfBounds(format!("Index out of bounds"))); } - Value::String(s.chars().nth(i as usize).unwrap().to_string()) + Ok(Value::String(s.chars().nth(i as usize).unwrap().to_string())) }, - _ => panic!("Invalid indexing operation"), + _ => Err(Error::TypeError(format!("Invalid indexing operation"))), } }, ASTNode::Print(expr) => { - let value = interpret_node(expr, symbol_table, is_verbose, in_loop); + let value = interpret_node(expr, symbol_table, is_verbose, in_loop)?; if is_verbose { println!("call print({})", value); } else { println!("{}", value); } - Value::Null + Ok(Value::Null) }, ASTNode::UnaryOp(op, expr) => { - let value = interpret_node(expr, symbol_table, is_verbose, in_loop); + let value = interpret_node(expr, symbol_table, is_verbose, in_loop)?; match (op, value) { - (Token::Not, Value::Boolean(b)) => Value::Boolean(!b), - _ => panic!("Unsupported unary operation"), + (Token::Not, Value::Boolean(b)) => Ok(Value::Boolean(!b)), + _ => Err(Error::UnsupportedOperation(format!("Unsupported unary operation"))), } }, ASTNode::While(condition, body) => { loop { - let cond_value = interpret_node(condition, symbol_table, is_verbose, true); + let cond_value = interpret_node(condition, symbol_table, is_verbose, true)?; if let Value::Boolean(false) = cond_value { break; } - + for stmt in body { - let result = interpret_node(stmt, symbol_table, is_verbose, true); + let result = interpret_node(stmt, symbol_table, is_verbose, true)?; match result { - Value::Break => return Value::Null, + Value::Break => return Ok(Value::Null), Value::Continue => break, _ => {} } } } - Value::Null + Ok(Value::Null) }, ASTNode::Var(name, expr, is_mutable) => { let value = if let Some(expr) = expr { - interpret_node(expr, symbol_table, is_verbose, in_loop) + interpret_node(expr, symbol_table, is_verbose, in_loop)? } else { Value::Null }; @@ -270,68 +271,68 @@ fn interpret_node(node: &ASTNode, symbol_table: &mut HashMap { - let value = interpret_node(expr, symbol_table, is_verbose, in_loop); + let value = interpret_node(expr, symbol_table, is_verbose, in_loop)?; if let Some((current_value, is_mutable)) = symbol_table.get_mut(name) { if !*is_mutable { - panic!("Cannot assign to immutable variable: {}", name); + return Err(Error::TypeError(format!("Cannot assign to immutable variable: {}", name))); } *current_value = value.clone(); if is_verbose { println!("assign {} = {:?}", name, value); } } else { - panic!("Variable not declared: {}", name); + return Err(Error::VariableNotDeclared(format!("Variable not declared: {}", name))); } - Value::Null + Ok(Value::Null) }, ASTNode::IndexAssign(array, index, value) => { let array_name = if let ASTNode::Identifier(name) = &**array { name } else { - panic!("Expected array identifier in index assignment"); + return Err(Error::TypeError(format!("Expected array identifier in index assignment"))); }; - let index_value = interpret_node(index, symbol_table, is_verbose, in_loop); - let value = interpret_node(value, symbol_table, is_verbose, in_loop); + let index_value = interpret_node(index, symbol_table, is_verbose, in_loop)?; + let value = interpret_node(value, symbol_table, is_verbose, in_loop)?; if let Value::Number(index) = index_value { if let Some((Value::Array(ref mut arr), is_mutable)) = symbol_table.get_mut(array_name) { if !*is_mutable { - panic!("Cannot assign to immutable array '{}'", array_name); + return Err(Error::TypeError(format!("Cannot assign to immutable array '{}'", array_name))); } if index as usize >= arr.len() { - panic!("Index out of bounds for array '{}'", array_name); + return Err(Error::IndexOutOfBounds(format!("Index out of bounds for array '{}'", array_name))); } arr[index as usize] = value; } else { - panic!("Array '{}' not found or is not mutable", array_name); + return Err(Error::TypeError(format!("Array '{}' not found or is not mutable", array_name))); } } else { - panic!("Expected integer index in array assignment"); + return Err(Error::TypeError(format!("Expected integer index in array assignment"))); } - Value::Null + Ok(Value::Null) }, ASTNode::Identifier(name) => { if let Some((value, _)) = symbol_table.get(name) { - value.clone() + Ok(value.clone()) } else { - panic!("Variable not found: {}", name); + Err(Error::VariableNotDeclared(format!("Variable not found: {}", name))) } }, ASTNode::TypeLiteral(type_name) => { - Value::Type(type_name.clone()) + Ok(Value::Type(type_name.clone())) }, ASTNode::Type(expr) => { - let value = interpret_node(expr, symbol_table, is_verbose, in_loop); + let value = interpret_node(expr, symbol_table, is_verbose, in_loop)?; let type_str = match value { Value::Number(_) => "int", Value::String(_) => "str", Value::Boolean(_) => "bool", - Value::Float(_) => "float", + Value::Float(_) => "float", Value::Null => "null", Value::Type(_) => "type", Value::Break => "break", @@ -341,73 +342,73 @@ fn interpret_node(node: &ASTNode, symbol_table: &mut HashMap { - let value = interpret_node(expr, symbol_table, is_verbose, in_loop); + let value = interpret_node(expr, symbol_table, is_verbose, in_loop)?; match type_name.as_str() { "int" => match value { - Value::Number(n) => Value::Number(n), - Value::Float(f) => Value::Number(f as i32), + Value::Number(n) => Ok(Value::Number(n)), + Value::Float(f) => Ok(Value::Number(f as i32)), Value::String(s) => { if s.chars().all(|c| c.is_digit(10)) { - Value::Number(s.parse::().unwrap()) + Ok(Value::Number(s.parse::().unwrap())) } else { - panic!("Cannot convert string '{}' to int", s) + Err(Error::TypeError(format!("Cannot convert string '{}' to int", s))) } }, - Value::Boolean(b) => Value::Number(if b { 1 } else { 0 }), - _ => panic!("Cannot convert to int"), + Value::Boolean(b) => Ok(Value::Number(if b { 1 } else { 0 })), + _ => Err(Error::TypeError(format!("Cannot convert to int"))), }, "str" => match value { - Value::Number(n) => Value::String(n.to_string()), - Value::Float(f) => Value::String(f.to_string()), - Value::String(s) => Value::String(s), - Value::Boolean(b) => Value::String(b.to_string()), - Value::Null => Value::String("null".to_string()), - _ => panic!("Cannot convert to string"), + Value::Number(n) => Ok(Value::String(n.to_string())), + Value::Float(f) => Ok(Value::String(f.to_string())), + Value::String(s) => Ok(Value::String(s)), + Value::Boolean(b) => Ok(Value::String(b.to_string())), + Value::Null => Ok(Value::String("null".to_string())), + _ => Err(Error::TypeError(format!("Cannot convert to string"))), }, "float" => match value { - Value::Number(n) => Value::Float(n as f64), - Value::Float(f) => Value::Float(f), + Value::Number(n) => Ok(Value::Float(n as f64)), + Value::Float(f) => Ok(Value::Float(f)), Value::String(s) => { match s.parse::() { - Ok(f) => Value::Float(f), - Err(_) => panic!("Cannot convert string '{}' to float", s), + Ok(f) => Ok(Value::Float(f)), + Err(_) => Err(Error::TypeError(format!("Cannot convert string '{}' to float", s))), } }, - Value::Boolean(b) => Value::Float(if b { 1.0 } else { 0.0 }), - _ => panic!("Cannot convert to float"), + Value::Boolean(b) => Ok(Value::Float(if b { 1.0 } else { 0.0 })), + _ => Err(Error::TypeError(format!("Cannot convert to float"))), }, "bool" => match value { - Value::Number(n) => Value::Boolean(n != 0), - Value::Float(f) => Value::Boolean(f != 0.0), - Value::String(s) => Value::Boolean(!s.is_empty()), - Value::Boolean(b) => Value::Boolean(b), - Value::Null => Value::Boolean(false), - _ => panic!("Cannot convert to bool"), + Value::Number(n) => Ok(Value::Boolean(n != 0)), + Value::Float(f) => Ok(Value::Boolean(f != 0.0)), + Value::String(s) => Ok(Value::Boolean(!s.is_empty())), + Value::Boolean(b) => Ok(Value::Boolean(b)), + Value::Null => Ok(Value::Boolean(false)), + _ => Err(Error::TypeError(format!("Cannot convert to bool"))), }, - _ => panic!("Unknown type cast: {}", type_name), + _ => Err(Error::TypeError(format!("Unknown type cast: {}", type_name))), } }, ASTNode::If(condition, if_block, elif_blocks, else_block) => { - let condition_value = interpret_node(condition, symbol_table, is_verbose, in_loop); + let condition_value = interpret_node(condition, symbol_table, is_verbose, in_loop)?; if let Value::Boolean(true) = condition_value { for stmt in if_block { - let result = interpret_node(stmt, symbol_table, is_verbose, in_loop); + let result = interpret_node(stmt, symbol_table, is_verbose, in_loop)?; if matches!(result, Value::Break | Value::Continue) { - return result; + return Ok(result); } } } else { let mut executed = false; for (elif_condition, elif_statements) in elif_blocks { - let elif_condition_value = interpret_node(elif_condition, symbol_table, is_verbose, in_loop); + let elif_condition_value = interpret_node(elif_condition, symbol_table, is_verbose, in_loop)?; if let Value::Boolean(true) = elif_condition_value { for stmt in elif_statements { - let result = interpret_node(stmt, symbol_table, is_verbose, in_loop); + let result = interpret_node(stmt, symbol_table, is_verbose, in_loop)?; if matches!(result, Value::Break | Value::Continue) { - return result; + return Ok(result); } } executed = true; @@ -417,48 +418,48 @@ fn interpret_node(node: &ASTNode, symbol_table: &mut HashMap { - interpret_node(init, symbol_table, is_verbose, true); + interpret_node(init, symbol_table, is_verbose, true)?; loop { - let cond_value = interpret_node(condition, symbol_table, is_verbose, true); + let cond_value = interpret_node(condition, symbol_table, is_verbose, true)?; if let Value::Boolean(false) = cond_value { break; } - + for stmt in body { - let result = interpret_node(stmt, symbol_table, is_verbose, true); + let result = interpret_node(stmt, symbol_table, is_verbose, true)?; match result { - Value::Break => return Value::Null, + Value::Break => return Ok(Value::Null), Value::Continue => break, _ => {} } } - - interpret_node(update, symbol_table, is_verbose, true); + + interpret_node(update, symbol_table, is_verbose, true)?; } - Value::Null + Ok(Value::Null) }, ASTNode::Break => { if !in_loop { - panic!("Break statement outside of loop"); + return Err(Error::BreakOutsideLoop); } - Value::Break + Ok(Value::Break) }, ASTNode::Continue => { if !in_loop { - panic!("Continue statement outside of loop"); + return Err(Error::ContinueOutsideLoop); } - Value::Continue + Ok(Value::Continue) }, } -} \ No newline at end of file +} diff --git a/src/lexer.rs b/src/lexer.rs index 669062a..3fe6374 100755 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -1,5 +1,6 @@ use std::str::Chars; use std::iter::Peekable; +use crate::error::Error; #[derive(Debug, PartialEq, Clone)] pub enum Token { @@ -52,103 +53,107 @@ pub enum Token { pub struct Lexer<'a> { input: Peekable>, + pub line: usize, + column: usize, } impl<'a> Lexer<'a> { pub fn new(input: &'a str) -> Self { Lexer { input: input.chars().peekable(), + line: 1, + column: 1, } } - pub fn next_token(&mut self) -> Token { + pub fn next_token(&mut self) -> Result { self.skip_whitespace(); if let Some(token) = self.handle_comment() { - return token; + return Ok(token); } match self.input.next() { - Some(',') => Token::Comma, + Some(',') => Ok(Token::Comma), Some('/') => { if self.input.peek() == Some(&'/') { - self.input.next(); - Token::FloorDivide + self.input.next(); + Ok(Token::FloorDivide) } else { - Token::Divide + Ok(Token::Divide) } }, Some('*') => { if self.input.peek() == Some(&'*') { - self.input.next(); - Token::Power + self.input.next(); + Ok(Token::Power) } else { - Token::Multiply + Ok(Token::Multiply) } }, Some('&') => { if self.input.next_if_eq(&'&').is_some() { - Token::And + Ok(Token::And) } else { - panic!("Unexpected character: &") + Err(Error::SyntaxError(format!("Unexpected character: & at line {}, column {}", self.line, self.column))) } }, Some('|') => { if self.input.next_if_eq(&'|').is_some() { - Token::Or + Ok(Token::Or) } else { - panic!("Unexpected character: |") + Err(Error::SyntaxError(format!("Unexpected character: | at line {}, column {}", self.line, self.column))) } }, Some('!') => { if self.input.next_if_eq(&'=').is_some() { - Token::NotEqual + Ok(Token::NotEqual) } else { - Token::Not + Ok(Token::Not) } }, Some(ch) => match ch { '0'..='9' => self.read_number(ch), - '+' => Token::Plus, - '-' => Token::Minus, + '+' => Ok(Token::Plus), + '-' => Ok(Token::Minus), '=' => { if self.input.next_if_eq(&'=').is_some() { - Token::Equal + Ok(Token::Equal) } else { - Token::Assign + Ok(Token::Assign) } }, '>' => { if self.input.next_if_eq(&'=').is_some() { - Token::GreaterEqual + Ok(Token::GreaterEqual) } else { - Token::Greater + Ok(Token::Greater) } }, '<' => { if self.input.next_if_eq(&'=').is_some() { - Token::LessEqual + Ok(Token::LessEqual) } else { - Token::Less + Ok(Token::Less) } }, - ';' => Token::Semicolon, - '(' => Token::LParen, - ')' => Token::RParen, - '{' => Token::LBrace, - '}' => Token::RBrace, - '[' => Token::LBracket, - ']' => Token::RBracket, - '%' => Token::Modulus, + ';' => Ok(Token::Semicolon), + '(' => Ok(Token::LParen), + ')' => Ok(Token::RParen), + '{' => Ok(Token::LBrace), + '}' => Ok(Token::RBrace), + '[' => Ok(Token::LBracket), + ']' => Ok(Token::RBracket), + '%' => Ok(Token::Modulus), '"' => self.read_string(), 'a'..='z' | 'A'..='Z' | '_' => self.read_identifier_or_keyword(ch), - _ => panic!("Unexpected character: {}", ch), + _ => Err(Error::SyntaxError(format!("Unexpected character: {} at line {}, column {}", ch, self.line, self.column))), }, - None => Token::EOF, + None => Ok(Token::EOF), } } - fn read_number(&mut self, first_digit: char) -> Token { + fn read_number(&mut self, first_digit: char) -> Result { let mut number = first_digit.to_string(); let mut is_float = false; while let Some(&ch) = self.input.peek() { @@ -164,13 +169,13 @@ impl<'a> Lexer<'a> { } } if is_float { - Token::Float(number.parse().unwrap()) + Ok(Token::Float(number.parse().unwrap())) } else { - Token::Number(number.parse().unwrap()) + Ok(Token::Number(number.parse().unwrap())) } } - fn read_identifier_or_keyword(&mut self, first_char: char) -> Token { + fn read_identifier_or_keyword(&mut self, first_char: char) -> Result { let mut identifier = first_char.to_string(); while let Some(&ch) = self.input.peek() { if ch.is_alphanumeric() || ch == '_' { @@ -181,28 +186,28 @@ impl<'a> Lexer<'a> { } } match identifier.as_str() { - "var" => Token::Var, - "novar" => Token::NoVar, - "print" => Token::Print, - "type" => Token::Type, - "if" => Token::If, - "elif" => Token::Elif, - "else" => Token::Else, - "null" => Token::Null, - "true" => Token::Boolean(true), - "false" => Token::Boolean(false), - "for" => Token::For, - "while" => Token::While, - "break" => Token::Break, - "continue" => Token::Continue, + "var" => Ok(Token::Var), + "novar" => Ok(Token::NoVar), + "print" => Ok(Token::Print), + "type" => Ok(Token::Type), + "if" => Ok(Token::If), + "elif" => Ok(Token::Elif), + "else" => Ok(Token::Else), + "null" => Ok(Token::Null), + "true" => Ok(Token::Boolean(true)), + "false" => Ok(Token::Boolean(false)), + "for" => Ok(Token::For), + "while" => Ok(Token::While), + "break" => Ok(Token::Break), + "continue" => Ok(Token::Continue), "int" | "str" | "float" | "bool" => { if self.input.peek() == Some(&'(') { - Token::TypeCast(identifier) + Ok(Token::TypeCast(identifier)) } else { - Token::TypeLiteral(identifier) + Ok(Token::TypeLiteral(identifier)) } }, - _ => Token::Identifier(identifier), + _ => Ok(Token::Identifier(identifier)), } } @@ -210,7 +215,7 @@ impl<'a> Lexer<'a> { if self.input.next_if(|&ch| ch == '/').is_some() { if self.input.next_if(|&ch| ch == '*').is_some() { self.skip_multiline_comment(); - return Some(self.next_token()); + return Some(self.next_token().unwrap()); } else { return Some(Token::Divide); } @@ -236,27 +241,32 @@ impl<'a> Lexer<'a> { } } - - fn read_string(&mut self) -> Token { + fn read_string(&mut self) -> Result { let mut string = String::new(); while let Some(&ch) = self.input.peek() { if ch == '"' { - self.input.next(); + self.input.next(); break; } string.push(ch); self.input.next(); } - Token::String(string) + Ok(Token::String(string)) } fn skip_whitespace(&mut self) { while let Some(&ch) = self.input.peek() { if ch.is_whitespace() { + if ch == '\n' { + self.line += 1; + self.column = 1; + } else { + self.column += 1; + } self.input.next(); } else { break; } } } -} \ No newline at end of file +} diff --git a/src/main.rs b/src/main.rs index b2f8790..14904cc 100755 --- a/src/main.rs +++ b/src/main.rs @@ -2,10 +2,13 @@ use std::env; use std::fs; use std::path::Path; use std::collections::HashMap; +use std::process; +use std::io::{self, Write}; mod interpreter; mod lexer; mod parser; +mod error; fn main() { // collect args @@ -17,7 +20,7 @@ fn main() { // error display lul if args.len() < 2 || args.contains(&String::from("help")) || args.contains(&String::from("--help")) || args.contains(&String::from("-h")) { help(); - std::process::exit(1); + process::exit(1); } let filename = &args[1]; @@ -25,13 +28,13 @@ fn main() { if !filename.ends_with(".td") && !is_brain_rot { eprintln!("Error: Input file must have a .td or .br extension"); - std::process::exit(1); + process::exit(1); } // file check disk if !Path::new(filename).exists() { eprintln!("Error: File '{}' not found", filename); - std::process::exit(1); + process::exit(1); } // read file @@ -49,10 +52,22 @@ fn main() { let mut parser = parser::Parser::new(&processed_contents); // Parser to AST - let ast = parser.parse(); + let ast = match parser.parse() { + Ok(ast) => ast, + Err(e) => { + print_error(&e); + process::exit(1); + } + }; // Interpreter - interpreter::interpret(ast, is_verbose); + match interpreter::interpret(ast, is_verbose) { + Ok(_) => {}, + Err(e) => { + print_error(&e); + process::exit(1); + } + } } fn help() { @@ -136,4 +151,11 @@ fn preprocess_skibidi(input: &str) -> String { } result -} \ No newline at end of file +} + +fn print_error(error: &error::Error) { + let stderr = io::stderr(); + let mut handle = stderr.lock(); + + writeln!(handle, "\x1b[31m{}\x1b[0m", error).unwrap(); +} diff --git a/src/parser.rs b/src/parser.rs index b0ae013..6382fba 100755 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,4 +1,5 @@ use crate::lexer::{Lexer, Token}; +use crate::error::Error; use std::collections::HashMap; #[derive(Debug, Clone)] @@ -40,8 +41,6 @@ pub enum ASTNode { Continue, } - - pub struct Parser<'a> { lexer: Lexer<'a>, current_token: Token, @@ -51,7 +50,7 @@ pub struct Parser<'a> { impl<'a> Parser<'a> { pub fn new(input: &'a str) -> Self { let mut lexer = Lexer::new(input); - let current_token = lexer.next_token(); + let current_token = lexer.next_token().unwrap(); Parser { lexer, current_token, @@ -59,23 +58,24 @@ impl<'a> Parser<'a> { } } - fn eat(&mut self, token: Token) { + fn eat(&mut self, token: Token) -> Result<(), Error> { if self.current_token == token { - self.current_token = self.lexer.next_token(); + self.current_token = self.lexer.next_token()?; + Ok(()) } else { - panic!("Unexpected token: {:?}, expected: {:?}", self.current_token, token); + Err(Error::SyntaxError(format!("Unexpected token: {:?}, expected: {:?} at line {}", self.current_token, token, self.lexer.line))) } } - pub fn parse(&mut self) -> Vec { + pub fn parse(&mut self) -> Result, Error> { let mut ast_nodes = Vec::new(); while self.current_token != Token::EOF { - ast_nodes.push(self.parse_statement()); + ast_nodes.push(self.parse_statement()?); } - ast_nodes + Ok(ast_nodes) } - fn parse_statement(&mut self) -> ASTNode { + fn parse_statement(&mut self) -> Result { match &self.current_token { Token::Var | Token::NoVar => self.parse_var_decl(), Token::Identifier(_) => self.parse_assign_stmt(), @@ -86,420 +86,417 @@ impl<'a> Parser<'a> { Token::Continue => self.parse_continue(), Token::While => self.parse_while_loop(), Token::Type => self.parse_type(), - _ => panic!("Unexpected token in statement: {:?}", self.current_token), + _ => Err(Error::SyntaxError(format!("Unexpected token in statement: {:?} at line {}", self.current_token, self.lexer.line))), } } - fn parse_type(&mut self) -> ASTNode { - self.eat(Token::Type); - self.eat(Token::LParen); - let expr = self.parse_expr(); - self.eat(Token::RParen); - self.eat(Token::Semicolon); - ASTNode::Type(Box::new(expr)) + fn parse_type(&mut self) -> Result { + self.eat(Token::Type)?; + self.eat(Token::LParen)?; + let expr = self.parse_expr()?; + self.eat(Token::RParen)?; + self.eat(Token::Semicolon)?; + Ok(ASTNode::Type(Box::new(expr))) } - fn parse_while_loop(&mut self) -> ASTNode { - self.eat(Token::While); - self.eat(Token::LParen); - let condition = self.parse_expr(); - self.eat(Token::RParen); - self.eat(Token::LBrace); - let body = self.parse_block(); - self.eat(Token::RBrace); + fn parse_while_loop(&mut self) -> Result { + self.eat(Token::While)?; + self.eat(Token::LParen)?; + let condition = self.parse_expr()?; + self.eat(Token::RParen)?; + self.eat(Token::LBrace)?; + let body = self.parse_block()?; + self.eat(Token::RBrace)?; - ASTNode::While(Box::new(condition), body) + Ok(ASTNode::While(Box::new(condition), body)) } - fn parse_if_statement(&mut self) -> ASTNode { - self.eat(Token::If); - self.eat(Token::LParen); - let condition = self.parse_expr(); - self.eat(Token::RParen); - self.eat(Token::LBrace); - let if_block = self.parse_block(); - self.eat(Token::RBrace); + fn parse_if_statement(&mut self) -> Result { + self.eat(Token::If)?; + self.eat(Token::LParen)?; + let condition = self.parse_expr()?; + self.eat(Token::RParen)?; + self.eat(Token::LBrace)?; + let if_block = self.parse_block()?; + self.eat(Token::RBrace)?; let mut elif_blocks = Vec::new(); let mut else_block = None; while self.current_token == Token::Elif { - self.eat(Token::Elif); - self.eat(Token::LParen); - let elif_condition = self.parse_expr(); - self.eat(Token::RParen); - self.eat(Token::LBrace); - let elif_statements = self.parse_block(); - self.eat(Token::RBrace); + self.eat(Token::Elif)?; + self.eat(Token::LParen)?; + let elif_condition = self.parse_expr()?; + self.eat(Token::RParen)?; + self.eat(Token::LBrace)?; + let elif_statements = self.parse_block()?; + self.eat(Token::RBrace)?; elif_blocks.push((elif_condition, elif_statements)); } if self.current_token == Token::Else { - self.eat(Token::Else); - self.eat(Token::LBrace); - else_block = Some(self.parse_block()); - self.eat(Token::RBrace); + self.eat(Token::Else)?; + self.eat(Token::LBrace)?; + else_block = Some(self.parse_block()?); + self.eat(Token::RBrace)?; } - ASTNode::If(Box::new(condition), if_block, elif_blocks, else_block) + Ok(ASTNode::If(Box::new(condition), if_block, elif_blocks, else_block)) } - fn parse_for_loop(&mut self) -> ASTNode { - self.eat(Token::For); - self.eat(Token::LParen); - + fn parse_for_loop(&mut self) -> Result { + self.eat(Token::For)?; + self.eat(Token::LParen)?; + let init = if let Token::Var | Token::NoVar = self.current_token { - self.parse_var_decl() + self.parse_var_decl()? } else { - self.parse_assign_stmt() + self.parse_assign_stmt()? }; - - let condition = self.parse_expr(); - self.eat(Token::Semicolon); - - let update = self.parse_assign_stmt(); - self.eat(Token::RParen); - - self.eat(Token::LBrace); - let body = self.parse_block(); - self.eat(Token::RBrace); - - ASTNode::For(Box::new(init), Box::new(condition), Box::new(update), body) + + let condition = self.parse_expr()?; + self.eat(Token::Semicolon)?; + + let update = self.parse_assign_stmt()?; + self.eat(Token::RParen)?; + + self.eat(Token::LBrace)?; + let body = self.parse_block()?; + self.eat(Token::RBrace)?; + + Ok(ASTNode::For(Box::new(init), Box::new(condition), Box::new(update), body)) } - - fn parse_break(&mut self) -> ASTNode { - self.eat(Token::Break); - self.eat(Token::Semicolon); - ASTNode::Break + + fn parse_break(&mut self) -> Result { + self.eat(Token::Break)?; + self.eat(Token::Semicolon)?; + Ok(ASTNode::Break) } - fn parse_continue(&mut self) -> ASTNode { - self.eat(Token::Continue); - self.eat(Token::Semicolon); - ASTNode::Continue + fn parse_continue(&mut self) -> Result { + self.eat(Token::Continue)?; + self.eat(Token::Semicolon)?; + Ok(ASTNode::Continue) } - fn parse_block(&mut self) -> Vec { + fn parse_block(&mut self) -> Result, Error> { let mut statements = Vec::new(); while self.current_token != Token::RBrace { - statements.push(self.parse_statement()); + statements.push(self.parse_statement()?); } - statements + Ok(statements) } - fn parse_expr(&mut self) -> ASTNode { + fn parse_expr(&mut self) -> Result { self.parse_logical_or() } - fn parse_logical_or(&mut self) -> ASTNode { - let mut node = self.parse_logical_and(); + fn parse_logical_or(&mut self) -> Result { + let mut node = self.parse_logical_and()?; while self.current_token == Token::Or { let op = self.current_token.clone(); - self.eat(Token::Or); - let right = self.parse_logical_and(); + self.eat(Token::Or)?; + let right = self.parse_logical_and()?; node = ASTNode::BinaryOp(Box::new(node), op, Box::new(right)); } - node + Ok(node) } - fn parse_logical_and(&mut self) -> ASTNode { - let mut node = self.parse_comparison(); + fn parse_logical_and(&mut self) -> Result { + let mut node = self.parse_comparison()?; while self.current_token == Token::And { let op = self.current_token.clone(); - self.eat(Token::And); - let right = self.parse_comparison(); + self.eat(Token::And)?; + let right = self.parse_comparison()?; node = ASTNode::BinaryOp(Box::new(node), op, Box::new(right)); } - node + Ok(node) } - fn parse_comparison(&mut self) -> ASTNode { - let mut node = self.parse_arithmetic(); - + fn parse_comparison(&mut self) -> Result { + let mut node = self.parse_arithmetic()?; + loop { match &self.current_token { Token::Equal | Token::NotEqual | Token::Greater | Token::Less | Token::GreaterEqual | Token::LessEqual => { let op = self.current_token.clone(); - self.eat(op.clone()); - let right = self.parse_arithmetic(); + self.eat(op.clone())?; + let right = self.parse_arithmetic()?; node = ASTNode::BinaryOp(Box::new(node), op, Box::new(right)); } _ => break, } } - - node + + Ok(node) } - fn parse_arithmetic(&mut self) -> ASTNode { - let mut node = self.parse_term(); + fn parse_arithmetic(&mut self) -> Result { + let mut node = self.parse_term()?; loop { match &self.current_token { Token::Plus | Token::Minus => { let op = self.current_token.clone(); - self.eat(op.clone()); - let right = self.parse_term(); + self.eat(op.clone())?; + let right = self.parse_term()?; node = ASTNode::BinaryOp(Box::new(node), op, Box::new(right)); } _ => break, } } - node + Ok(node) } - fn parse_term(&mut self) -> ASTNode { - let mut node = self.parse_power(); - - loop { - match &self.current_token { - Token::Multiply => { - self.eat(Token::Multiply); - let right = self.parse_power(); - node = ASTNode::BinaryOp(Box::new(node), Token::Multiply, Box::new(right)); - } - Token::Divide => { - self.eat(Token::Divide); - if self.current_token == Token::Divide { - self.eat(Token::Divide); - let right = self.parse_power(); - node = ASTNode::BinaryOp(Box::new(node), Token::FloorDivide, Box::new(right)); - } else { - let right = self.parse_power(); - node = ASTNode::BinaryOp(Box::new(node), Token::Divide, Box::new(right)); - } - } - Token::Modulus => { - self.eat(Token::Modulus); - let right = self.parse_power(); - node = ASTNode::BinaryOp(Box::new(node), Token::Modulus, Box::new(right)); + fn parse_term(&mut self) -> Result { + let mut node = self.parse_power()?; + + loop { + match &self.current_token { + Token::Multiply => { + self.eat(Token::Multiply)?; + let right = self.parse_power()?; + node = ASTNode::BinaryOp(Box::new(node), Token::Multiply, Box::new(right)); + } + Token::Divide => { + self.eat(Token::Divide)?; + if self.current_token == Token::Divide { + self.eat(Token::Divide)?; + let right = self.parse_power()?; + node = ASTNode::BinaryOp(Box::new(node), Token::FloorDivide, Box::new(right)); + } else { + let right = self.parse_power()?; + node = ASTNode::BinaryOp(Box::new(node), Token::Divide, Box::new(right)); } - _ => break, } + Token::Modulus => { + self.eat(Token::Modulus)?; + let right = self.parse_power()?; + node = ASTNode::BinaryOp(Box::new(node), Token::Modulus, Box::new(right)); + } + _ => break, } - - node } - fn parse_power(&mut self) -> ASTNode { - let mut node = self.parse_factor(); + Ok(node) + } + + fn parse_power(&mut self) -> Result { + let mut node = self.parse_factor()?; while self.current_token == Token::Power { let op = self.current_token.clone(); - self.eat(Token::Power); - let right = self.parse_factor(); + self.eat(Token::Power)?; + let right = self.parse_factor()?; node = ASTNode::BinaryOp(Box::new(node), op, Box::new(right)); } - node + Ok(node) } - fn parse_factor(&mut self) -> ASTNode { + fn parse_factor(&mut self) -> Result { match &self.current_token { Token::Minus => { - self.eat(Token::Minus); - let factor = self.parse_factor(); - ASTNode::BinaryOp(Box::new(ASTNode::Number(0)), Token::Minus, Box::new(factor)) + self.eat(Token::Minus)?; + let factor = self.parse_factor()?; + Ok(ASTNode::BinaryOp(Box::new(ASTNode::Number(0)), Token::Minus, Box::new(factor))) } Token::Number(val) => { let num = *val; - self.eat(Token::Number(num)); - ASTNode::Number(num) + self.eat(Token::Number(num))?; + Ok(ASTNode::Number(num)) } Token::Not => { - self.eat(Token::Not); - let factor = self.parse_factor(); - ASTNode::UnaryOp(Token::Not, Box::new(factor)) + self.eat(Token::Not)?; + let factor = self.parse_factor()?; + Ok(ASTNode::UnaryOp(Token::Not, Box::new(factor))) }, Token::Float(val) => { let num = *val; - self.eat(Token::Float(num)); - ASTNode::Float(num) + self.eat(Token::Float(num))?; + Ok(ASTNode::Float(num)) } Token::LParen => { - self.eat(Token::LParen); - let expr = self.parse_expr(); - self.eat(Token::RParen); - expr + self.eat(Token::LParen)?; + let expr = self.parse_expr()?; + self.eat(Token::RParen)?; + Ok(expr) } Token::LBracket => self.parse_array_literal(), Token::Identifier(_) | Token::String(_) | Token::Boolean(_) | Token::Null | Token::TypeLiteral(_) | Token::TypeCast(_) | Token::Type => { self.parse_primary() } - _ => panic!("Unexpected token in factor: {:?}", self.current_token), + _ => Err(Error::SyntaxError(format!("Unexpected token in factor: {:?} at line {}", self.current_token, self.lexer.line))), } } - fn parse_primary(&mut self) -> ASTNode { + fn parse_primary(&mut self) -> Result { let mut node = match &self.current_token { Token::Number(val) => { let num = *val; - self.eat(Token::Number(num)); + self.eat(Token::Number(num))?; ASTNode::Number(num) } - Token::Float(val) => { + Token::Float(val) => { let num = *val; - self.eat(Token::Float(num)); + self.eat(Token::Float(num))?; ASTNode::Float(num) } Token::String(val) => { let s = val.clone(); - self.eat(Token::String(s.clone())); + self.eat(Token::String(s.clone()))?; ASTNode::String(s) } Token::Boolean(val) => { let b = *val; - self.eat(Token::Boolean(b)); + self.eat(Token::Boolean(b))?; ASTNode::Boolean(b) } Token::Identifier(var_name) => { let name = var_name.clone(); - self.eat(Token::Identifier(name.clone())); + self.eat(Token::Identifier(name.clone()))?; ASTNode::Identifier(name) } Token::TypeLiteral(type_name) => { let name = type_name.clone(); - self.eat(Token::TypeLiteral(name.clone())); + self.eat(Token::TypeLiteral(name.clone()))?; ASTNode::TypeLiteral(name) } Token::TypeCast(type_name) => { - self.parse_type_cast(type_name.clone()) + self.parse_type_cast(type_name.clone())? } Token::Null => { - self.eat(Token::Null); + self.eat(Token::Null)?; ASTNode::Null } Token::LParen => { - self.eat(Token::LParen); - let expr = self.parse_expr(); - self.eat(Token::RParen); + self.eat(Token::LParen)?; + let expr = self.parse_expr()?; + self.eat(Token::RParen)?; expr } Token::Type => { - self.eat(Token::Type); - self.eat(Token::LParen); - let expr = self.parse_expr(); - self.eat(Token::RParen); + self.eat(Token::Type)?; + self.eat(Token::LParen)?; + let expr = self.parse_expr()?; + self.eat(Token::RParen)?; ASTNode::Type(Box::new(expr)) } - _ => panic!("Unexpected token in primary: {:?}", self.current_token), + _ => return Err(Error::SyntaxError(format!("Unexpected token in primary: {:?} at line {}", self.current_token, self.lexer.line))), }; while self.current_token == Token::LBracket { - node = self.parse_index(node); + node = self.parse_index(node)?; } - - node + + Ok(node) } - fn parse_array_literal(&mut self) -> ASTNode { - self.eat(Token::LBracket); + fn parse_array_literal(&mut self) -> Result { + self.eat(Token::LBracket)?; let mut elements = Vec::new(); - + if self.current_token != Token::RBracket { loop { - elements.push(self.parse_expr()); + elements.push(self.parse_expr()?); if self.current_token == Token::Comma { - self.eat(Token::Comma); + self.eat(Token::Comma)?; } else { break; } } } - - self.eat(Token::RBracket); - ASTNode::Array(elements) - } - fn parse_index(&mut self, expr: ASTNode) -> ASTNode { - self.eat(Token::LBracket); - let index = self.parse_expr(); - self.eat(Token::RBracket); - ASTNode::Index(Box::new(expr), Box::new(index)) + self.eat(Token::RBracket)?; + Ok(ASTNode::Array(elements)) } - fn parse_type_cast(&mut self, type_name: String) -> ASTNode { - self.eat(Token::TypeCast(type_name.clone())); - self.eat(Token::LParen); - let expr = self.parse_expr(); - self.eat(Token::RParen); - ASTNode::TypeCast(type_name, Box::new(expr)) + fn parse_index(&mut self, expr: ASTNode) -> Result { + self.eat(Token::LBracket)?; + let index = self.parse_expr()?; + self.eat(Token::RBracket)?; + Ok(ASTNode::Index(Box::new(expr), Box::new(index))) } + fn parse_type_cast(&mut self, type_name: String) -> Result { + self.eat(Token::TypeCast(type_name.clone()))?; + self.eat(Token::LParen)?; + let expr = self.parse_expr()?; + self.eat(Token::RParen)?; + Ok(ASTNode::TypeCast(type_name, Box::new(expr))) + } - fn parse_var_decl(&mut self) -> ASTNode { + fn parse_var_decl(&mut self) -> Result { let is_mutable = match self.current_token { Token::Var => true, Token::NoVar => false, - _ => panic!("Expected var or novar"), + _ => return Err(Error::SyntaxError(format!("Expected var or novar at line {}", self.lexer.line))), }; - self.eat(self.current_token.clone()); - + self.eat(self.current_token.clone())?; + let name = if let Token::Identifier(ident) = self.current_token.clone() { - self.eat(Token::Identifier(ident.clone())); + self.eat(Token::Identifier(ident.clone()))?; ident } else { - panic!("Expected identifier in variable declaration"); + return Err(Error::SyntaxError(format!("Expected identifier in variable declaration at line {}", self.lexer.line))); }; - + if self.symbol_table.contains_key(&name) { - panic!("Variable '{}' has already been declared", name); + return Err(Error::VariableAlreadyDeclared(format!("Variable '{}' has already been declared at line {}", name, self.lexer.line))); } - + self.symbol_table.insert(name.clone(), is_mutable); - + let initializer = if self.current_token == Token::Assign { - self.eat(Token::Assign); - Some(Box::new(self.parse_expr())) + self.eat(Token::Assign)?; + Some(Box::new(self.parse_expr()?)) } else { None }; - - self.eat(Token::Semicolon); - ASTNode::Var(name, initializer, is_mutable) + + self.eat(Token::Semicolon)?; + Ok(ASTNode::Var(name, initializer, is_mutable)) } + fn parse_assign_stmt(&mut self) -> Result { + let name = if let Token::Identifier(ident) = self.current_token.clone() { + self.eat(Token::Identifier(ident.clone()))?; + ident + } else { + return Err(Error::SyntaxError(format!("Expected identifier in assignment at line {}", self.lexer.line))); + }; + + let mut expr = ASTNode::Identifier(name.clone()); + if self.current_token == Token::LBracket { + self.eat(Token::LBracket)?; + let index = self.parse_expr()?; + self.eat(Token::RBracket)?; + expr = ASTNode::Index(Box::new(expr), Box::new(index)); + } - fn parse_assign_stmt(&mut self) -> ASTNode { - let name = if let Token::Identifier(ident) = self.current_token.clone() { - self.eat(Token::Identifier(ident.clone())); - ident - } else { - panic!("Expected identifier in assignment, got: {:?}", self.current_token); - }; - - let mut expr = ASTNode::Identifier(name.clone()); - if self.current_token == Token::LBracket { - self.eat(Token::LBracket); - let index = self.parse_expr(); - self.eat(Token::RBracket); - expr = ASTNode::Index(Box::new(expr), Box::new(index)); - } - - self.eat(Token::Assign); - let value = self.parse_expr(); - - if self.current_token == Token::Semicolon { - self.eat(Token::Semicolon); - } - - match expr { - ASTNode::Index(array, index) => ASTNode::IndexAssign(array, index, Box::new(value)), - _ => ASTNode::Assign(name, Box::new(value)), - } + self.eat(Token::Assign)?; + let value = self.parse_expr()?; + + if self.current_token == Token::Semicolon { + self.eat(Token::Semicolon)?; + } + + match expr { + ASTNode::Index(array, index) => Ok(ASTNode::IndexAssign(array, index, Box::new(value))), + _ => Ok(ASTNode::Assign(name, Box::new(value))), + } } - - - fn parse_print(&mut self) -> ASTNode { - self.eat(Token::Print); - self.eat(Token::LParen); - let expr = self.parse_expr(); - self.eat(Token::RParen); - self.eat(Token::Semicolon); - ASTNode::Print(Box::new(expr)) + + fn parse_print(&mut self) -> Result { + self.eat(Token::Print)?; + self.eat(Token::LParen)?; + let expr = self.parse_expr()?; + self.eat(Token::RParen)?; + self.eat(Token::Semicolon)?; + Ok(ASTNode::Print(Box::new(expr))) } -} \ No newline at end of file +}