From f56fcc4a8b9fe47fcde476a21b74b40742f1ea4d Mon Sep 17 00:00:00 2001 From: Moritz Gmeiner Date: Fri, 20 Jan 2023 21:44:27 +0100 Subject: [PATCH] Chapter 7 done --- .gitignore | 2 +- src/error.rs | 58 ++++++++--- src/interpreter/eval.rs | 113 +++++++++++++++++++++ src/interpreter/mod.rs | 5 + src/{interpreter.rs => interpreter/run.rs} | 15 ++- src/parser/parser.rs | 12 ++- 6 files changed, 186 insertions(+), 19 deletions(-) create mode 100644 src/interpreter/eval.rs create mode 100644 src/interpreter/mod.rs rename src/{interpreter.rs => interpreter/run.rs} (87%) diff --git a/.gitignore b/.gitignore index ea8c4bf..b83d222 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1 @@ -/target +/target/ diff --git a/src/error.rs b/src/error.rs index ae5f689..a45a93f 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,7 +1,9 @@ use thiserror::Error; +use crate::interpreter::Primitive; use crate::lexer::Token; use crate::misc::CodePos; +use crate::parser::expr::{BinaryOp, UnaryOp}; #[derive(Error, Debug)] pub enum LexerError { @@ -27,31 +29,55 @@ pub enum ParserError { UnexpectedToken { token: Token }, } +#[derive(Error, Debug)] +pub enum EvalError { + #[error("Unary operator {op} had invalid argument {arg}")] + UnaryOpInvalidArgument { op: UnaryOp, arg: Primitive }, + #[error("Binary operator {op} had invalid arguments {left} and {right}")] + BinaryOpInvalidArguments { + left: Primitive, + op: BinaryOp, + right: Primitive, + }, + #[error("Division by zero")] + DivisionByZero, +} + #[derive(Error, Debug)] pub enum LoxError { - #[error("{msg}")] - LexerError { msg: String }, - #[error("{msg}")] - ParserError { msg: String }, + #[error("{0}", format_lexer_errors(inner))] + LexerError { inner: Vec }, + #[error("{inner}")] + ParserError { inner: ParserError }, + #[error("{inner}")] + EvalError { inner: EvalError }, +} + +fn format_lexer_errors(lexer_errs: &Vec) -> String { + let msg = if lexer_errs.len() == 1 { + format!("{}", lexer_errs[0]) + } else { + let msgs: Vec = lexer_errs.iter().map(|err| format!("{}", err)).collect(); + msgs.join("\n") + }; + + msg } impl From> for LoxError { fn from(lexer_errs: Vec) -> Self { - let msg = if lexer_errs.len() == 1 { - format!("{}", lexer_errs[0]) - } else { - let msgs: Vec = lexer_errs.iter().map(|err| format!("{}", err)).collect(); - msgs.join("\n") - }; - - LoxError::LexerError { msg } + LoxError::LexerError { inner: lexer_errs } } } impl From for LoxError { - fn from(parser_error: ParserError) -> Self { - LoxError::ParserError { - msg: format!("{parser_error}"), - } + fn from(parser_err: ParserError) -> Self { + LoxError::ParserError { inner: parser_err } + } +} + +impl From for LoxError { + fn from(eval_err: EvalError) -> Self { + LoxError::EvalError { inner: eval_err } } } diff --git a/src/interpreter/eval.rs b/src/interpreter/eval.rs new file mode 100644 index 0000000..c634272 --- /dev/null +++ b/src/interpreter/eval.rs @@ -0,0 +1,113 @@ +use std::fmt::Display; + +use crate::error::EvalError; +use crate::parser::expr::{BinaryOp, Expr, Literal, UnaryOp}; + +pub type EvalResult = Result; + +/*====================================================================================================================*/ + +pub fn evaluate(expr: Expr) -> EvalResult { + expr.eval() +} + +/*====================================================================================================================*/ + +#[derive(Debug, PartialEq)] +pub enum Primitive { + String(String), + Number(f64), + Bool(bool), + Nil, +} + +impl Primitive { + fn is_truthy(&self) -> bool { + match self { + Primitive::Bool(false) | Primitive::Nil => false, + _ => true, + } + } +} + +impl Display for Primitive { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Primitive::String(s) => write!(f, "\"{s}\""), + Primitive::Number(num) => write!(f, "{num}"), + Primitive::Bool(b) => write!(f, "{b}"), + Primitive::Nil => write!(f, "nil"), + } + } +} + +/*====================================================================================================================*/ + +trait Eval { + fn eval(self) -> EvalResult; +} + +impl Eval for Literal { + fn eval(self) -> EvalResult { + match self { + Literal::String(s) => Ok(Primitive::String(s)), + Literal::Number(num) => Ok(Primitive::Number(num)), + Literal::Bool(b) => Ok(Primitive::Bool(b)), + Literal::Nil => Ok(Primitive::Nil), + } + } +} + +impl Eval for Expr { + fn eval(self) -> EvalResult { + match self { + Expr::Literal(literal) => literal.eval(), + Expr::Unary(op, expr) => { + let arg = expr.eval()?; + + match (op, arg) { + (UnaryOp::Negate, Primitive::Number(num)) => Ok(Primitive::Number(-num)), + (UnaryOp::Not, Primitive::Bool(b)) => Ok(Primitive::Bool(!b)), + (UnaryOp::Not, primitive) => Ok(Primitive::Bool(!primitive.is_truthy())), + (op, arg) => Err(EvalError::UnaryOpInvalidArgument { op, arg }), + } + } + Expr::Binary(left_expr, op, right_expr) => { + use Primitive::{Bool, Number, String}; + + let left = left_expr.eval()?; + let right = right_expr.eval()?; + + match (left, op, right) { + (Number(left), BinaryOp::Add, Number(right)) => Ok(Number(left + right)), + (Number(left), BinaryOp::Subtract, Number(right)) => Ok(Number(left - right)), + (Number(left), BinaryOp::Multiply, Number(right)) => Ok(Number(left * right)), + (Number(left), BinaryOp::Divide, Number(right)) => { + if right == 0.0 { + return Err(EvalError::DivisionByZero); + } + Ok(Number(left / right)) + } + + (String(left), BinaryOp::Add, String(right)) => Ok(String(left + &right)), + + (left, BinaryOp::Equal, right) => Ok(Bool(left == right)), + (left, BinaryOp::NotEqual, right) => Ok(Bool(left != right)), + + (Number(left), BinaryOp::Less, Number(right)) => Ok(Bool(left < right)), + (Number(left), BinaryOp::LessEqual, Number(right)) => Ok(Bool(left <= right)), + (Number(left), BinaryOp::Greater, Number(right)) => Ok(Bool(left > right)), + (Number(left), BinaryOp::GreaterEqual, Number(right)) => Ok(Bool(left >= right)), + + (String(left), BinaryOp::Less, String(right)) => Ok(Bool(left < right)), + (String(left), BinaryOp::LessEqual, String(right)) => Ok(Bool(left <= right)), + (String(left), BinaryOp::Greater, String(right)) => Ok(Bool(left > right)), + (String(left), BinaryOp::GreaterEqual, String(right)) => Ok(Bool(left >= right)), + + (left, op, right) => Err(EvalError::BinaryOpInvalidArguments { left, op, right }), + } + } + Expr::Grouping(expr) => expr.eval(), + } + } +} diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs new file mode 100644 index 0000000..aec2d38 --- /dev/null +++ b/src/interpreter/mod.rs @@ -0,0 +1,5 @@ +mod eval; +mod run; + +pub use eval::Primitive; +pub use run::interpreter_main; diff --git a/src/interpreter.rs b/src/interpreter/run.rs similarity index 87% rename from src/interpreter.rs rename to src/interpreter/run.rs index 0b8437f..f720c31 100644 --- a/src/interpreter.rs +++ b/src/interpreter/run.rs @@ -1,6 +1,7 @@ use std::io::Write; use crate::error::LoxError; +use crate::interpreter::eval::evaluate; use crate::lexer::{scan_tokens, Token}; use crate::parser::parser::parse_tokens; @@ -23,9 +24,17 @@ fn run_file(script_path: &str) { std::process::exit(66); }); - if let Err(err) = run(&source_code) { + /* if let Err(err) = run(&source_code) { eprintln!("{}", err); std::process::exit(65); + } */ + + if let Err(err) = run(&source_code) { + eprintln!("{err}"); + match err { + LoxError::LexerError { .. } | LoxError::ParserError { .. } => std::process::exit(65), + LoxError::EvalError { .. } => std::process::exit(70), + } } } @@ -90,5 +99,9 @@ fn run(code_string: &str) -> Result<(), LoxError> { println!("{expr}"); + let result = evaluate(expr)?; + + println!("{result}"); + Ok(()) } diff --git a/src/parser/parser.rs b/src/parser/parser.rs index 1204e52..3965735 100644 --- a/src/parser/parser.rs +++ b/src/parser/parser.rs @@ -66,7 +66,11 @@ impl Parser { pub fn parse(self) -> ParserResult { let mut me = self; - me.expression() + let expr = me.expression()?; + + me.consume_token(TokenType::EOF)?; + + Ok(expr) } fn synchronise(&mut self) { @@ -227,6 +231,12 @@ impl Parser { } fn next_token(&mut self) -> ParserResult { + // let next = self.token_iter.next(); + + // println!("Next token: {next:?}"); + + // next.ok_or(ParserError::TokenStreamEnded) + self.token_iter.next().ok_or(ParserError::TokenStreamEnded) }