Skip to content

Commit

Permalink
⚠️ refactored errors
Browse files Browse the repository at this point in the history
  • Loading branch information
aym-n committed Jan 7, 2024
1 parent e07cd7a commit b6f2d04
Show file tree
Hide file tree
Showing 7 changed files with 115 additions and 47 deletions.
1 change: 1 addition & 0 deletions generate_ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ pub fn generate_ast(output_dir: &String) -> io::Result<()> {
"Print : Expr expression".to_string(),
"Var : Token name, Option<Expr> initializer".to_string(),
"While : Expr condition, Box<Stmt> body".to_string(),
"Return : Token keyword, Option<Expr> value".to_string(),
],
&vec![
"crate::expr::Expr".to_string(),
Expand Down
12 changes: 6 additions & 6 deletions src/enviroment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ impl Environment {
None => {
match &self.enclosing {
Some(env) => env.borrow().get(token),
None => Err(Error::new(
token.line,
format!("Undefined variable '{}'.", token.lexeme),
None => Err(Error::parse_error(
token,
&format!("Undefined variable '{}'.", token.lexeme),
)),
}
}
Expand All @@ -54,9 +54,9 @@ impl Environment {
None => {
match &self.enclosing {
Some(env) => env.borrow_mut().assign(name, value),
None => Err(Error::new(
name.line,
format!("Undefined variable '{}'.", name.lexeme),
None => Err(Error::parse_error(
name,
&format!("Undefined variable '{}'.", name.lexeme),
)),
}
}
Expand Down
62 changes: 49 additions & 13 deletions src/errors.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,57 @@
use std::process::exit;
use crate::tokens::*;

// TODO: implement error handling with Result<T, E>
pub struct Error {
pub line: usize,
pub message: String,
pub enum Error {
ParseError { token: Token, message: String },
RuntimeError { token: Token, message: String },
SystemError { message: String },
ReturnValue { value: Object },
}

impl Error {
pub fn new(line: usize, message: String) -> Self {
Error {
line,
message,
}
pub fn return_value(value: Object) -> Error {
Error::ReturnValue { value }
}

pub fn report(&self) {
eprintln!("[line {}] Error: {}", self.line, self.message);
exit(65);
pub fn parse_error(token: &Token, message: &str) -> Error {
let err = Error::ParseError {
token: token.clone(),
message: message.to_string(),
};
err.report("");
err
}

pub fn runtime_error(token: &Token, message: &str) -> Error {
let err = Error::RuntimeError {
token: token.clone(),
message: message.to_string(),
};
err.report("");
err
}

pub fn system_error(message: &str) -> Error {
let err = Error::SystemError {
message: message.to_string(),
};
err.report("");
err
}

fn report(&self, loc: &str) {
match self {
Error::ParseError { token, message }
| Error::RuntimeError { token, message } => {
if token.kind == TokenKind::EOF {
eprintln!("{} at end {}", token.line, message);
} else {
eprintln!("line {} at '{}' {}", token.line, token.lexeme, message);
}
}
Error::SystemError { message } => {
eprintln!("System Error: {message}");
}
Error::ReturnValue { .. } => {}
};
}
}
28 changes: 16 additions & 12 deletions src/interpreter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ pub struct Interpreter {
}

impl StmtVisitor<()> for Interpreter {

fn visit_return_stmt(&self, stmt: &ReturnStmt) -> Result<(), Error> {
Ok(())
}
fn visit_function_stmt(&self, stmt: &FunctionStmt) -> Result<(), Error>{
let function = Function::new(&stmt);
self.environment
Expand Down Expand Up @@ -119,9 +121,9 @@ impl ExprVisitor<Object> for Interpreter {
TokenKind::Bang => {
return Ok(Object::Bool(!self.is_truthy(right)));
}
_ => Err(Error::new(
expr.operator.line,
format!("Expect unary operator. Got {}", expr.operator.lexeme),
_ => Err(Error::runtime_error(
&expr.operator,
"Invalid unary operator",
)),
}
}
Expand Down Expand Up @@ -191,7 +193,10 @@ impl ExprVisitor<Object> for Interpreter {
};

if result == Object::ArithmeticError {
Err(Error::new(expr.operator.line, format!("Arithmetic error")))
Err(Error::runtime_error(
&expr.operator,
"Invalid binary operator",
))
} else {
Ok(result)
}
Expand All @@ -208,9 +213,9 @@ impl ExprVisitor<Object> for Interpreter {

if let Object::Function(function) = callee {
if arguments.len() != function.arity() {
return Err(Error::new(
expr.paren.line,
format!(
return Err(Error::runtime_error(
&expr.paren,
&format!(
"Expected {} arguments but got {}",
function.arity(),
arguments.len()
Expand All @@ -219,9 +224,9 @@ impl ExprVisitor<Object> for Interpreter {
}
function.func.call(self, &arguments)
} else {
return Err(Error::new(
expr.paren.line,
format!("Can only call functions and classes"),
return Err(Error::runtime_error(
&expr.paren,
"Can only call functions and classes",
));
}

Expand Down Expand Up @@ -269,7 +274,6 @@ impl Interpreter {
let mut success = true;
for statment in statements {
if let Err(e) = self.execute(statment) {
e.report();
success = false;
break;
}
Expand Down
2 changes: 1 addition & 1 deletion src/native_functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ impl CallableTrait for NativeClock {
fn call(&self, _terp: &Interpreter, _args: &Vec<Object>) -> Result<Object, Error> {
match SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) {
Ok(n) => Ok(Object::Num(n.as_millis() as f64)),
Err(_) => Err(Error::new(0, "Could not get time".to_string())),
Err(_) => Err(Error::system_error("Failed to get time.")),
}
}

Expand Down
43 changes: 28 additions & 15 deletions src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ impl Parser {
return self.print_statement();
}

if self.match_token(vec![TokenKind::Return]) {
return self.return_statement();
}

if self.match_token(vec![TokenKind::While]) {
return self.while_statement();
}
Expand All @@ -66,6 +70,18 @@ impl Parser {
self.expression_statement()
}

fn return_statement(&mut self) -> Result<Stmt, Error> {
let keyword = self.previous();
let value = if self.check(TokenKind::Semicolon) {
None
} else {
Some(self.expression()?)
};

self.consume(TokenKind::Semicolon, "Expect ';' after return value.")?;
Ok(Stmt::Return(ReturnStmt { keyword, value }))
}

fn for_statement(&mut self) -> Result<Stmt, Error> {
self.consume(TokenKind::LeftParen, "Expect '(' after 'for'.")?;

Expand Down Expand Up @@ -188,10 +204,10 @@ impl Parser {
params.push(self.consume(TokenKind::Identifier, "Expect Parameter Name")?);
while self.match_token(vec![TokenKind::Comma]) {
if params.len() >= 255 {
return Err(Error::new(
self.peek().line,
format!("can't have more than 255 parameters"),
));
return Err(Error::parse_error(
&self.peek(),
"Can't have more than 255 parameters.",
))
}
params.push(self.consume(TokenKind::Identifier, "Expect Parameter Name")?);
}
Expand Down Expand Up @@ -231,9 +247,9 @@ impl Parser {
}));
}
_ => {
return Err(Error::new(
equals.line,
"Invalid assignment target.".to_string(),
return Err(Error::parse_error(
&equals,
"Invalid assignment target.",
));
}
}
Expand Down Expand Up @@ -362,9 +378,9 @@ impl Parser {
if !self.check(TokenKind::RightParen) {
loop {
if arguments.len() >= 255 {
return Err(Error::new(
self.peek().line,
"Can't have more than 255 arguments.".to_string(),
return Err(Error::parse_error(
&self.peek(),
"Can't have more than 255 arguments.",
));
}
arguments.push(self.expression()?);
Expand Down Expand Up @@ -435,10 +451,7 @@ impl Parser {
}));
}

Err(Error::new(
self.peek().line,
format!("Expect expression. Got {}", self.peek().lexeme),
))
Err(Error::parse_error(&self.peek(), "Expect expression."))
}

fn match_token(&mut self, kinds: Vec<TokenKind>) -> bool {
Expand Down Expand Up @@ -482,7 +495,7 @@ impl Parser {
return Ok(self.advance());
}

Err(Error::new(self.peek().line, message.to_string()))
Err(Error::parse_error(&self.peek(), message))
}

fn synchronize(&mut self) {
Expand Down
14 changes: 14 additions & 0 deletions src/stmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ pub enum Stmt {
Print(PrintStmt),
Var(VarStmt),
While(WhileStmt),
Return(ReturnStmt),
}

impl Stmt {
Expand All @@ -23,6 +24,7 @@ impl Stmt {
Stmt::Print(v) => v.accept(stmt_visitor),
Stmt::Var(v) => v.accept(stmt_visitor),
Stmt::While(v) => v.accept(stmt_visitor),
Stmt::Return(v) => v.accept(stmt_visitor),
}
}
}
Expand Down Expand Up @@ -61,6 +63,11 @@ pub struct WhileStmt {
pub body: Box<Stmt>,
}

pub struct ReturnStmt {
pub keyword: Token,
pub value: Option<Expr>,
}

pub trait StmtVisitor<T> {
fn visit_block_stmt(&self, expr: &BlockStmt) -> Result<T , Error>;
fn visit_expression_stmt(&self, expr: &ExpressionStmt) -> Result<T , Error>;
Expand All @@ -69,6 +76,7 @@ pub trait StmtVisitor<T> {
fn visit_print_stmt(&self, expr: &PrintStmt) -> Result<T , Error>;
fn visit_var_stmt(&self, expr: &VarStmt) -> Result<T , Error>;
fn visit_while_stmt(&self, expr: &WhileStmt) -> Result<T , Error>;
fn visit_return_stmt(&self, expr: &ReturnStmt) -> Result<T , Error>;
}

impl BlockStmt {
Expand Down Expand Up @@ -113,3 +121,9 @@ impl WhileStmt {
}
}

impl ReturnStmt {
pub fn accept<T>(&self, visitor: &dyn StmtVisitor<T>) -> Result<T , Error> {
visitor.visit_return_stmt(self)
}
}

0 comments on commit b6f2d04

Please sign in to comment.