From 956c4d0f280e7aab36ed60ea9645811d6f9e0650 Mon Sep 17 00:00:00 2001 From: Moritz Gmeiner Date: Sun, 22 Jan 2023 23:33:57 +0100 Subject: [PATCH] chapter 8 done --- .gitignore | 1 + src/error.rs | 54 ++++--- src/interpreter/environment.rs | 67 ++++++++ src/interpreter/eval.rs | 122 +++++++++----- src/interpreter/mod.rs | 3 +- src/interpreter/run.rs | 38 +++-- src/lexer/lexer.rs | 4 +- src/lexer/token.rs | 22 +-- src/parser/GRAMMAR | 14 +- src/parser/expr.rs | 79 ++++++--- src/parser/mod.rs | 9 +- src/parser/parser.rs | 285 ++++++++++++++++++++++++++------- src/parser/stmt.rs | 49 ++++++ 13 files changed, 570 insertions(+), 177 deletions(-) create mode 100644 src/interpreter/environment.rs create mode 100644 src/parser/stmt.rs diff --git a/.gitignore b/.gitignore index b83d222..98311bb 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /target/ +test.lox \ No newline at end of file diff --git a/src/error.rs b/src/error.rs index a45a93f..22196db 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,9 +1,9 @@ use thiserror::Error; -use crate::interpreter::Primitive; +use crate::interpreter::Value; use crate::lexer::Token; use crate::misc::CodePos; -use crate::parser::expr::{BinaryOp, UnaryOp}; +use crate::parser::{BinaryOp, Expr, UnaryOp}; #[derive(Error, Debug)] pub enum LexerError { @@ -25,39 +25,47 @@ pub enum LexerError { pub enum ParserError { #[error("Token stream ended unexpectedly.")] TokenStreamEnded, - #[error("Unexpected token {token} at {0}.", token.code_pos())] + #[error("Unexpected token {token} at {0}.", token.code_pos)] UnexpectedToken { token: Token }, + #[error("Missing semicolon at {code_pos}")] + MissingSemicolon { code_pos: CodePos }, + #[error("Expected variable name at {0}, got {token} instead", token.code_pos)] + ExpectedVarName { token: Token }, + #[error("Can't assign to {expr} at {code_pos}")] + InvalidAssignment { expr: Expr, code_pos: CodePos }, + #[error("Missing closing curly brace at {code_pos}")] + MissingRightBrace { code_pos: CodePos }, + #[error("Missing closing parenthesis at {code_pos}")] + MissingRightParenthesis { code_pos: CodePos }, } #[derive(Error, Debug)] -pub enum EvalError { +pub enum RuntimeError { #[error("Unary operator {op} had invalid argument {arg}")] - UnaryOpInvalidArgument { op: UnaryOp, arg: Primitive }, + UnaryOpInvalidArgument { op: UnaryOp, arg: Value }, #[error("Binary operator {op} had invalid arguments {left} and {right}")] - BinaryOpInvalidArguments { - left: Primitive, - op: BinaryOp, - right: Primitive, - }, + BinaryOpInvalidArguments { left: Value, op: BinaryOp, right: Value }, #[error("Division by zero")] DivisionByZero, + #[error("Name {name} is not defined")] + NameNotDefined { name: String }, } #[derive(Error, Debug)] pub enum LoxError { - #[error("{0}", format_lexer_errors(inner))] + #[error("{0}", format_errors(inner))] LexerError { inner: Vec }, + #[error("{0}", format_errors(inner))] + ParserError { inner: Vec }, #[error("{inner}")] - ParserError { inner: ParserError }, - #[error("{inner}")] - EvalError { inner: EvalError }, + EvalError { inner: RuntimeError }, } -fn format_lexer_errors(lexer_errs: &Vec) -> String { - let msg = if lexer_errs.len() == 1 { - format!("{}", lexer_errs[0]) +fn format_errors(errs: &Vec) -> String { + let msg = if errs.len() == 1 { + format!("{}", errs[0]) } else { - let msgs: Vec = lexer_errs.iter().map(|err| format!("{}", err)).collect(); + let msgs: Vec = errs.iter().map(|err| format!("{}", err)).collect(); msgs.join("\n") }; @@ -70,14 +78,14 @@ impl From> for LoxError { } } -impl From for LoxError { - fn from(parser_err: ParserError) -> Self { - LoxError::ParserError { inner: parser_err } +impl From> for LoxError { + fn from(parser_errs: Vec) -> Self { + LoxError::ParserError { inner: parser_errs } } } -impl From for LoxError { - fn from(eval_err: EvalError) -> Self { +impl From for LoxError { + fn from(eval_err: RuntimeError) -> Self { LoxError::EvalError { inner: eval_err } } } diff --git a/src/interpreter/environment.rs b/src/interpreter/environment.rs new file mode 100644 index 0000000..eab5cbb --- /dev/null +++ b/src/interpreter/environment.rs @@ -0,0 +1,67 @@ +use std::collections::HashMap; + +use crate::error::RuntimeError; + +use super::Value; + +type Scope = HashMap; + +pub struct Environment { + globals: HashMap, + + scopes: Vec, +} + +impl Environment { + pub fn new() -> Self { + Environment { + globals: HashMap::new(), + scopes: Vec::new(), + } + } + + pub fn get(&self, name: &str) -> Result<&Value, RuntimeError> { + for scope in self.scopes.iter().rev() { + if let Some(value) = scope.get(name) { + return Ok(value); + } + } + + self.globals + .get(name) + .ok_or(RuntimeError::NameNotDefined { name: name.to_owned() }) + } + + pub fn define(&mut self, name: String, value: Value) { + if let Some(scope) = self.scopes.last_mut() { + scope.insert(name, value); + } else { + self.globals.insert(name, value); + } + } + + pub fn assign(&mut self, name: &str, value: Value) -> Result<(), RuntimeError> { + for scope in self.scopes.iter_mut().rev() { + if let Some(var) = scope.get_mut(name) { + *var = value; + return Ok(()); + } + } + + if let Some(var) = self.globals.get_mut(name) { + *var = value; + Ok(()) + } else { + let name = name.to_owned(); + Err(RuntimeError::NameNotDefined { name }) + } + } + + pub fn enter_scope(&mut self) { + self.scopes.push(Scope::new()); + } + + pub fn exit_scope(&mut self) { + self.scopes.pop().expect("Tried to exit scope, but no scope to exit"); + } +} diff --git a/src/interpreter/eval.rs b/src/interpreter/eval.rs index c634272..2990b3a 100644 --- a/src/interpreter/eval.rs +++ b/src/interpreter/eval.rs @@ -1,82 +1,84 @@ use std::fmt::Display; -use crate::error::EvalError; -use crate::parser::expr::{BinaryOp, Expr, Literal, UnaryOp}; +use crate::error::RuntimeError; +use crate::parser::{BinaryOp, Expr, Literal, Stmt, UnaryOp}; -pub type EvalResult = Result; +use super::environment::Environment; + +pub type EvalResult = Result; /*====================================================================================================================*/ -pub fn evaluate(expr: Expr) -> EvalResult { - expr.eval() +pub fn execute(statement: Stmt, env: &mut Environment) -> Result<(), RuntimeError> { + statement.eval(env) } /*====================================================================================================================*/ -#[derive(Debug, PartialEq)] -pub enum Primitive { +#[derive(Debug, Clone, PartialEq)] +pub enum Value { String(String), Number(f64), Bool(bool), Nil, } -impl Primitive { +impl Value { fn is_truthy(&self) -> bool { match self { - Primitive::Bool(false) | Primitive::Nil => false, + Value::Bool(false) | Value::Nil => false, _ => true, } } } -impl Display for Primitive { +impl Display for Value { 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"), + Value::String(s) => write!(f, "\"{s}\""), + Value::Number(num) => write!(f, "{num}"), + Value::Bool(b) => write!(f, "{b}"), + Value::Nil => write!(f, "nil"), } } } /*====================================================================================================================*/ -trait Eval { - fn eval(self) -> EvalResult; -} +/* trait Eval { + fn eval(self, env: &mut Environment) -> EvalResult; +} */ -impl Eval for Literal { - fn eval(self) -> EvalResult { +impl Literal { + fn eval(self, _env: &mut Environment) -> 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), + Literal::String(s) => Ok(Value::String(s)), + Literal::Number(num) => Ok(Value::Number(num)), + Literal::Bool(b) => Ok(Value::Bool(b)), + Literal::Nil => Ok(Value::Nil), } } } -impl Eval for Expr { - fn eval(self) -> EvalResult { +impl Expr { + fn eval(self, env: &mut Environment) -> EvalResult { match self { - Expr::Literal(literal) => literal.eval(), - Expr::Unary(op, expr) => { - let arg = expr.eval()?; + Expr::Literal { literal } => literal.eval(env), + Expr::Unary { op, expr } => { + let arg = expr.eval(env)?; 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 }), + (UnaryOp::Negate, Value::Number(num)) => Ok(Value::Number(-num)), + (UnaryOp::Not, Value::Bool(b)) => Ok(Value::Bool(!b)), + (UnaryOp::Not, primitive) => Ok(Value::Bool(!primitive.is_truthy())), + (op, arg) => Err(RuntimeError::UnaryOpInvalidArgument { op, arg }), } } - Expr::Binary(left_expr, op, right_expr) => { - use Primitive::{Bool, Number, String}; + Expr::Binary { left, op, right } => { + use Value::{Bool, Number, String}; - let left = left_expr.eval()?; - let right = right_expr.eval()?; + let left = left.eval(env)?; + let right = right.eval(env)?; match (left, op, right) { (Number(left), BinaryOp::Add, Number(right)) => Ok(Number(left + right)), @@ -84,7 +86,7 @@ impl Eval for Expr { (Number(left), BinaryOp::Multiply, Number(right)) => Ok(Number(left * right)), (Number(left), BinaryOp::Divide, Number(right)) => { if right == 0.0 { - return Err(EvalError::DivisionByZero); + return Err(RuntimeError::DivisionByZero); } Ok(Number(left / right)) } @@ -104,10 +106,52 @@ impl Eval for Expr { (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 }), + (left, op, right) => Err(RuntimeError::BinaryOpInvalidArguments { left, op, right }), } } - Expr::Grouping(expr) => expr.eval(), + Expr::Grouping { expr } => expr.eval(env), + Expr::Variable { name } => env.get(&name).cloned(), + Expr::Assignment { name, value } => { + let value = value.eval(env)?; + env.assign(&name, value.clone())?; + Ok(value) + } + } + } +} + +impl Stmt { + fn eval(self, env: &mut Environment) -> EvalResult<()> { + match self { + Stmt::ExprStmt { expr } => { + // expr.eval(env)?; + // Ok(Value::Nil) + expr.eval(env)?; + Ok(()) + } + Stmt::Print { expr } => { + match expr.eval(env)? { + // special case: when printing a string, drop the surrounding "" + Value::String(s) => println!("{s}"), + val => println!("{val}"), + } + + Ok(()) + } + Stmt::VarDecl { name, initializer } => { + let initializer = initializer.eval(env)?; + env.define(name, initializer); + Ok(()) + } + Stmt::Block { statements } => { + env.enter_scope(); + for statement in statements { + // on error the current frame gets destroyed anyways, so no need to exit scope + statement.eval(env)?; + } + env.exit_scope(); + Ok(()) + } } } } diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index aec2d38..726f65e 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -1,5 +1,6 @@ +mod environment; mod eval; mod run; -pub use eval::Primitive; +pub use eval::Value; pub use run::interpreter_main; diff --git a/src/interpreter/run.rs b/src/interpreter/run.rs index f720c31..2b284de 100644 --- a/src/interpreter/run.rs +++ b/src/interpreter/run.rs @@ -1,9 +1,12 @@ use std::io::Write; use crate::error::LoxError; -use crate::interpreter::eval::evaluate; +use crate::interpreter::eval::execute; +use crate::interpreter::Value; use crate::lexer::{scan_tokens, Token}; -use crate::parser::parser::parse_tokens; +use crate::parser::parse_tokens; + +use super::environment::Environment; pub fn interpreter_main() { let args: Vec = std::env::args().collect(); @@ -29,7 +32,9 @@ fn run_file(script_path: &str) { std::process::exit(65); } */ - if let Err(err) = run(&source_code) { + let mut env = Environment::new(); + + if let Err(err) = run(&source_code, &mut env) { eprintln!("{err}"); match err { LoxError::LexerError { .. } | LoxError::ParserError { .. } => std::process::exit(65), @@ -41,6 +46,8 @@ fn run_file(script_path: &str) { fn run_repl() { let stdin = std::io::stdin(); + let mut env = Environment::new(); + 'outer: loop { let mut input_buf = String::new(); @@ -53,7 +60,7 @@ fn run_repl() { std::process::exit(66); }); - let num_open_braces = (input_buf.matches('{').count() as i64) - (input_buf.matches('}').count() as i64); + /* let num_open_braces = (input_buf.matches('{').count() as i64) - (input_buf.matches('}').count() as i64); let num_open_parens = (input_buf.matches('(').count() as i64) - (input_buf.matches(')').count() as i64); let num_open_brackets = (input_buf.matches('[').count() as i64) - (input_buf.matches(']').count() as i64); @@ -65,7 +72,9 @@ fn run_repl() { // any braces/parens/brackets more closing than opening => break (will be parse error) if num_open_braces < 0 || num_open_parens < 0 || num_open_brackets < 0 { break 'inner; - } + } */ + + break 'inner; print!("< "); std::io::stdout().flush().unwrap(); @@ -77,14 +86,14 @@ fn run_repl() { break 'outer; } - match run(&input_buf) { + match run(&input_buf, &mut env) { Ok(()) => {} Err(err) => eprintln!("{}", err), } } } -fn run(code_string: &str) -> Result<(), LoxError> { +fn run(code_string: &str, env: &mut Environment) -> Result<(), LoxError> { let tokens: Vec = scan_tokens(code_string)?; /* let token_str = tokens @@ -95,13 +104,20 @@ fn run(code_string: &str) -> Result<(), LoxError> { println!("{token_str}"); */ - let expr = parse_tokens(tokens)?; + let statements = parse_tokens(tokens)?; - println!("{expr}"); + // println!("{expr}"); - let result = evaluate(expr)?; + // let mut result = Value::Nil; - println!("{result}"); + for statement in statements { + execute(statement, env)?; + } + + /* match result { + Value::Nil => {} + result => println!("{result}"), + } */ Ok(()) } diff --git a/src/lexer/lexer.rs b/src/lexer/lexer.rs index c25af25..3ce0691 100644 --- a/src/lexer/lexer.rs +++ b/src/lexer/lexer.rs @@ -71,7 +71,7 @@ impl Lexer { me.scan_token(); } - me.tokens.push(Token::new(TokenType::EOF, "".to_owned(), me.code_pos)); + me.tokens.push(Token::new(TokenType::EOF, me.code_pos)); if me.errors.is_empty() { Ok(me.tokens) @@ -234,7 +234,7 @@ impl Lexer { fn push_token(&mut self, token_type: TokenType) { let lexeme: String = self.source[self.start..self.current].iter().collect(); - self.tokens.push(Token::new(token_type, lexeme, self.code_pos)); + self.tokens.push(Token::new(token_type, self.code_pos)); } fn try_parse_string(&mut self) -> Option { diff --git a/src/lexer/token.rs b/src/lexer/token.rs index aa8e941..33fff42 100644 --- a/src/lexer/token.rs +++ b/src/lexer/token.rs @@ -27,33 +27,25 @@ pub enum TokenType { } pub struct Token { - token_type: TokenType, - lexeme: String, - - code_pos: CodePos, + pub token_type: TokenType, + // pub lexeme: String, + pub code_pos: CodePos, } impl Token { - pub fn new(token_type: TokenType, lexeme: String, pos: CodePos) -> Self { + pub fn new(token_type: TokenType, pos: CodePos) -> Self { Token { token_type, - lexeme: lexeme, + // lexeme, code_pos: pos, } } - - pub fn token_type(&self) -> &TokenType { - &self.token_type - } - - pub fn code_pos(&self) -> CodePos { - self.code_pos - } } impl std::fmt::Debug for Token { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "<{:?}> (\"{}\")", self.token_type, self.lexeme) + write!(f, "<{:?}>", self.token_type) + // write!(f, "<{:?}> (\"{}\")", self.token_type, self.lexeme) } } diff --git a/src/parser/GRAMMAR b/src/parser/GRAMMAR index 68b3cf3..74d6a85 100644 --- a/src/parser/GRAMMAR +++ b/src/parser/GRAMMAR @@ -1,8 +1,18 @@ -expression -> equality +program -> statement* EOF ; +statement -> exprStmt | printStmt | declaration | block ; + +exprStmt -> expression ";" ; +printStmt -> "print" expression ";" ; +declaration -> "var" IDENTIFIER ( "=" expression )? ";" ; +block -> "{" statement* "}" ; + +expression -> assignment + +assignment -> IDENTIFIER "=" assignment | equality ; equality -> comparison ( ( "==" | "!=" ) comparison )* ; comparison -> term ( ">" | ">=" | "<" | "<=" term )* ; term -> factor ( ( "+" | "-" ) factor )* factor -> unary ( ( "*" | "/" ) unary )* ; unary -> ( "!" | "-" ) unary | primary ; -primary -> "(" expression ")" | NUMBER | STRING | "true" | "false" | "nil" ; \ No newline at end of file +primary -> "(" expression ")" | IDENTIFIER | NUMBER | STRING | "true" | "false" | "nil" ; \ No newline at end of file diff --git a/src/parser/expr.rs b/src/parser/expr.rs index 32a4e7a..b743ca5 100644 --- a/src/parser/expr.rs +++ b/src/parser/expr.rs @@ -2,49 +2,84 @@ use std::fmt::Display; #[derive(Debug)] pub enum Expr { - Literal(Literal), - Unary(UnaryOp, Box), - Binary(Box, BinaryOp, Box), - Grouping(Box), + Literal { + literal: Literal, + }, + Unary { + op: UnaryOp, + expr: Box, + }, + Binary { + left: Box, + op: BinaryOp, + right: Box, + }, + Grouping { + expr: Box, + }, + Variable { + name: String, + }, + Assignment { + name: String, + value: Box, + }, } impl Expr { - pub fn new_string(s: String) -> Self { - Expr::Literal(Literal::String(s)) + pub fn string(s: String) -> Self { + Expr::Literal { + literal: Literal::String(s), + } } - pub fn new_number(num: f64) -> Self { - Expr::Literal(Literal::Number(num)) + pub fn number(num: f64) -> Self { + Expr::Literal { + literal: Literal::Number(num), + } } - pub fn new_bool(b: bool) -> Self { - Expr::Literal(Literal::Bool(b)) + pub fn bool(b: bool) -> Self { + Expr::Literal { + literal: Literal::Bool(b), + } } - pub fn new_nil() -> Self { - Expr::Literal(Literal::Nil) + pub fn nil() -> Self { + Expr::Literal { literal: Literal::Nil } } - pub fn new_unary(operator: UnaryOp, expr: Expr) -> Self { - Expr::Unary(operator, Box::new(expr)) + pub fn unary(op: UnaryOp, expr: Expr) -> Self { + let expr = Box::new(expr); + Expr::Unary { op, expr } } - pub fn new_binary(left: Expr, operator: BinaryOp, right: Expr) -> Self { - Expr::Binary(Box::new(left), operator, Box::new(right)) + pub fn binary(left: Expr, op: BinaryOp, right: Expr) -> Self { + let left = Box::new(left); + let right = Box::new(right); + Expr::Binary { left, op, right } } - pub fn new_grouping(expr: Expr) -> Self { - Expr::Grouping(Box::new(expr)) + pub fn grouping(expr: Expr) -> Self { + let expr = Box::new(expr); + Expr::Grouping { expr } + } + + pub fn assignment(name: String, value: Expr) -> Self { + let value = Box::new(value); + Expr::Assignment { name, value } } } impl Display for Expr { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - Expr::Literal(literal) => write!(f, "{literal}"), - Expr::Unary(op, expr) => write!(f, "({op} {expr})"), - Expr::Binary(left, op, right) => write!(f, "({op} {left} {right})"), - Expr::Grouping(expr) => write!(f, "(group {expr})"), + Expr::Literal { literal } => write!(f, "{literal}"), + Expr::Unary { op, expr } => write!(f, "({op} {expr})"), + Expr::Binary { left, op, right } => write!(f, "({op} {left} {right})"), + Expr::Grouping { expr } => write!(f, "(group {expr})"), + Expr::Variable { name } => write!(f, "{name}"), + Expr::Assignment { name, value } => write!(f, "{name} = {value}"), } } } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 79b536a..5d2a9ff 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -1,2 +1,7 @@ -pub mod expr; -pub mod parser; +mod expr; +mod parser; +mod stmt; + +pub use expr::{BinaryOp, Expr, Literal, UnaryOp}; +pub use parser::parse_tokens; +pub use stmt::Stmt; diff --git a/src/parser/parser.rs b/src/parser/parser.rs index 3965735..732c798 100644 --- a/src/parser/parser.rs +++ b/src/parser/parser.rs @@ -5,12 +5,13 @@ use crate::lexer::{Token, TokenType}; use crate::parser::expr::BinaryOp; use super::expr::{Expr, UnaryOp}; +use super::Stmt; /*====================================================================================================================*/ type ParserResult = Result; -pub fn parse_tokens(tokens: Vec) -> ParserResult { +pub fn parse_tokens(tokens: Vec) -> Result, Vec> { Parser::new(tokens).parse() } @@ -39,6 +40,11 @@ impl TokenIter { self.peek_token.as_ref() } + + fn is_empty(&self) -> bool { + // peek_token is None and there are no more tokens to take from token_iter + self.peek_token.is_none() && self.token_iter.len() == 0 + } } impl Iterator for TokenIter { @@ -63,24 +69,44 @@ impl Parser { } } - pub fn parse(self) -> ParserResult { + pub fn parse(self) -> Result, Vec> { let mut me = self; - let expr = me.expression()?; + let mut statements = Vec::new(); + let mut parse_errors = Vec::new(); - me.consume_token(TokenType::EOF)?; + while !me.token_iter.is_empty() && me.peek_token().token_type != TokenType::EOF { + // statements.push(me.statement()?); + match me.statement() { + Ok(stmt) => { + statements.push(stmt); + } + Err(err) => { + parse_errors.push(err); + me.synchronise(); + } + } + } - Ok(expr) + // me.consume_token(TokenType::EOF).unwrap(); + + if !parse_errors.is_empty() { + Err(parse_errors) + } else { + Ok(statements) + } } fn synchronise(&mut self) { loop { - let peek_token = self - .peek_token() - .unwrap_or_else(|err| panic!("peek_token returned error in synchronise: {err}")); + if self.token_iter.is_empty() { + return; + } + + let peek_token = self.peek_token(); // if we match a synchronisation point: return - match peek_token.token_type() { + match peek_token.token_type { TokenType::Class | TokenType::Fun | TokenType::Var @@ -92,23 +118,118 @@ impl Parser { | TokenType::EOF => return, TokenType::Semicolon => { // discard semicolon first, then return - let _ = self - .next_token() - .unwrap_or_else(|err| panic!("next_token returned error in synchronise: {err}")); + let _ = self.next_token(); return; } _ => {} } // no sync point: discard token - let _ = self - .next_token() - .unwrap_or_else(|err| panic!("next_token returned error in synchronise: {err}")); + let _ = self.next_token(); } } + fn statement(&mut self) -> ParserResult { + match self.peek_token().token_type { + TokenType::Print => self.print_statement(), + TokenType::Var => self.var_declaration(), + TokenType::LeftBrace => self.block(), + _ => self.expression_statement(), + } + } + + fn print_statement(&mut self) -> ParserResult { + // self.consume_token(TokenType::Print)?; + assert_eq!(self.next_token().token_type, TokenType::Print); + + let expr = self.expression()?; + + self.semicolon()?; + + Ok(Stmt::print_stmt(expr)) + } + + fn var_declaration(&mut self) -> ParserResult { + // self.consume_token(TokenType::Var)?; + assert_eq!(self.next_token().token_type, TokenType::Var); + + let name = match self.next_token() { + Token { + token_type: TokenType::Identifier(name), + .. + } => name, + token => return Err(ParserError::ExpectedVarName { token }), + }; + + let initializer = if self.peek_token().token_type == TokenType::Equal { + // self.consume_token(TokenType::Equal).unwrap(); + assert_eq!(self.next_token().token_type, TokenType::Equal); + + self.expression()? + } else { + Expr::nil() + }; + + self.semicolon()?; + + Ok(Stmt::var_decl(name, initializer)) + } + + fn block(&mut self) -> ParserResult { + // self.consume_token(TokenType::LeftBrace)?; + assert_eq!(self.next_token().token_type, TokenType::LeftBrace); + + let mut statements = Vec::new(); + + while self.peek_token().token_type != TokenType::RightBrace { + let statement = self.statement().map_err(|err| { + if self.peek_token().token_type == TokenType::EOF { + ParserError::MissingRightBrace { + code_pos: self.peek_token().code_pos, + } + } else { + err + } + })?; + statements.push(statement); + } + + // self.consume_token(TokenType::RightBrace)?; + assert_eq!(self.next_token().token_type, TokenType::RightBrace); + + Ok(Stmt::Block { statements }) + } + + fn expression_statement(&mut self) -> ParserResult { + let expr = self.expression()?; + + self.semicolon()?; + + Ok(Stmt::expr_stmt(expr)) + } + fn expression(&mut self) -> ParserResult { - self.equality() + self.assignment() + } + + fn assignment(&mut self) -> ParserResult { + let code_pos = self.peek_token().code_pos; + + let expr = self.equality()?; + + if self.peek_token().token_type != TokenType::Equal { + return Ok(expr); + } + + // self.consume_token(TokenType::Equal).unwrap(); + assert_eq!(self.next_token().token_type, TokenType::Equal); + + let value = self.assignment()?; + + match expr { + Expr::Variable { name } => Ok(Expr::assignment(name, value)), + _ => Err(ParserError::InvalidAssignment { expr, code_pos }), + } } fn equality(&mut self) -> ParserResult { @@ -116,18 +237,18 @@ impl Parser { loop { // get comparison operator as BinaryOp; otherwise break out of loop - let operator = match self.peek_token()?.token_type() { + let operator = match self.peek_token().token_type { TokenType::EqualEqual => BinaryOp::Equal, TokenType::BangEqual => BinaryOp::NotEqual, _ => break, }; // consume operator token - let _ = self.next_token().unwrap(); + let _ = self.next_token(); let right = self.comparison()?; - expr = Expr::new_binary(expr, operator, right); + expr = Expr::binary(expr, operator, right); } Ok(expr) @@ -137,7 +258,7 @@ impl Parser { let mut expr = self.term()?; loop { - let operator = match self.peek_token()?.token_type() { + let operator = match self.peek_token().token_type { TokenType::Less => BinaryOp::Less, TokenType::LessEqual => BinaryOp::LessEqual, TokenType::Greater => BinaryOp::Greater, @@ -146,11 +267,11 @@ impl Parser { }; // consume operator token - let _ = self.next_token().unwrap(); + let _ = self.next_token(); let right = self.term()?; - expr = Expr::new_binary(expr, operator, right); + expr = Expr::binary(expr, operator, right); } Ok(expr) @@ -160,18 +281,18 @@ impl Parser { let mut expr = self.factor()?; loop { - let operator = match self.peek_token()?.token_type() { + let operator = match self.peek_token().token_type { TokenType::Plus => BinaryOp::Add, TokenType::Minus => BinaryOp::Subtract, _ => break, }; // consume operator token - let _ = self.next_token().unwrap(); + let _ = self.next_token(); let right = self.factor()?; - expr = Expr::new_binary(expr, operator, right); + expr = Expr::binary(expr, operator, right); } Ok(expr) @@ -181,76 +302,120 @@ impl Parser { let mut expr = self.unary()?; loop { - let operator = match self.peek_token()?.token_type() { + let operator = match self.peek_token().token_type { TokenType::Star => BinaryOp::Multiply, TokenType::Slash => BinaryOp::Divide, _ => break, }; // consume operator token - let _ = self.next_token().unwrap(); + let _ = self.next_token(); let right = self.unary()?; - expr = Expr::new_binary(expr, operator, right); + expr = Expr::binary(expr, operator, right); } Ok(expr) } fn unary(&mut self) -> ParserResult { - match self.peek_token()?.token_type() { + match self.peek_token().token_type { TokenType::Bang => { - let _ = self.next_token().unwrap(); - Ok(Expr::new_unary(UnaryOp::Not, self.unary()?)) + self.next_token(); + Ok(Expr::unary(UnaryOp::Not, self.unary()?)) } TokenType::Minus => { - let _ = self.next_token().unwrap(); - Ok(Expr::new_unary(UnaryOp::Negate, self.unary()?)) + let _ = self.next_token(); + Ok(Expr::unary(UnaryOp::Negate, self.unary()?)) } _ => self.primary(), } } fn primary(&mut self) -> ParserResult { - let token = self.next_token()?; + if self.peek_token().token_type == TokenType::EOF { + return Err(ParserError::TokenStreamEnded); + } - match token.token_type() { - TokenType::Number(num) => Ok(Expr::new_number(*num)), - TokenType::String(s) => Ok(Expr::new_string(s.clone())), - TokenType::False => Ok(Expr::new_bool(false)), - TokenType::True => Ok(Expr::new_bool(true)), - TokenType::Nil => Ok(Expr::new_nil()), + let token = self.next_token(); + + match token.token_type { + TokenType::Number(num) => Ok(Expr::number(num)), + TokenType::String(s) => Ok(Expr::string(s)), + TokenType::False => Ok(Expr::bool(false)), + TokenType::True => Ok(Expr::bool(true)), + TokenType::Nil => Ok(Expr::nil()), TokenType::LeftParen => { let expr = self.expression()?; - self.consume_token(TokenType::RightParen)?; - Ok(Expr::new_grouping(expr)) + // self.consume_token(TokenType::RightParen)?; + let peek_token = self.peek_token(); + if peek_token.token_type != TokenType::RightParen { + return Err(ParserError::MissingRightParenthesis { + code_pos: token.code_pos, + }); + } + + let _ = self.next_token(); + + Ok(Expr::grouping(expr)) } + TokenType::Identifier(name) => Ok(Expr::Variable { name }), _ => Err(ParserError::UnexpectedToken { token }), } } - fn next_token(&mut self) -> ParserResult { - // let next = self.token_iter.next(); + fn semicolon(&mut self) -> ParserResult<()> { + /* match self.next_token() { + token if token.token_type == TokenType::Semicolon => Ok(()), + token => Err(ParserError::MissingSemicolon { + code_pos: token.code_pos, + }), + // None => Err(ParserError::TokenStreamEnded), + } */ + + let peek_token = self.peek_token(); + + if peek_token.token_type == TokenType::Semicolon { + self.next_token(); + Ok(()) + } else { + Err(ParserError::MissingSemicolon { + code_pos: peek_token.code_pos, + }) + } + } + + fn next_token(&mut self) -> Token { + /* let next = self.token_iter.next(); // println!("Next token: {next:?}"); - // next.ok_or(ParserError::TokenStreamEnded) - - self.token_iter.next().ok_or(ParserError::TokenStreamEnded) - } - - fn peek_token(&mut self) -> ParserResult<&Token> { - self.token_iter.peek().ok_or(ParserError::TokenStreamEnded) - } - - fn consume_token(&mut self, token_type: TokenType) -> ParserResult { - self.next_token().and_then(|token| { - if token.token_type() == &token_type { - Ok(token) - } else { - Err(ParserError::UnexpectedToken { token }) + if let Some(ref token) = next { + if token.token_type == TokenType::EOF { + panic!("Someone ate a EOF token"); } - }) + } + + next.ok_or(ParserError::TokenStreamEnded) */ + + self.token_iter.next().unwrap() // .ok_or(ParserError::TokenStreamEnded) } + + fn peek_token(&mut self) -> &Token { + self.token_iter.peek().unwrap() // .ok_or(ParserError::TokenStreamEnded) + } + + /* fn consume_token(&mut self, token_type: TokenType) -> ParserResult { + /* self.next_token().and_then(|token| { + if token.token_type == token_type { + Some(token) + } else { + // Err(ParserError::UnexpectedToken { token }) + None + } + }) */ + + Ok(self.next_token().token_type == token_type) + } */ } diff --git a/src/parser/stmt.rs b/src/parser/stmt.rs new file mode 100644 index 0000000..2dbc164 --- /dev/null +++ b/src/parser/stmt.rs @@ -0,0 +1,49 @@ +use std::fmt::Display; + +use super::Expr; + +pub enum Stmt { + ExprStmt { expr: Box }, + Print { expr: Box }, + VarDecl { name: String, initializer: Box }, + Block { statements: Vec }, +} + +impl Stmt { + pub fn expr_stmt(expr: Expr) -> Self { + let expr = Box::new(expr); + Stmt::ExprStmt { expr } + } + + pub fn print_stmt(expr: Expr) -> Self { + let expr = Box::new(expr); + Stmt::Print { expr } + } + + pub fn var_decl(name: String, initializer: Expr) -> Self { + let initializer = Box::new(initializer); + Stmt::VarDecl { name, initializer } + } +} + +impl Display for Stmt { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Stmt::ExprStmt { expr } => write!(f, "{expr}"), + Stmt::Print { expr } => write!(f, "print {expr}"), + Stmt::VarDecl { name, initializer } => write!(f, "var {name} = {initializer}"), + Stmt::Block { statements } => { + writeln!(f, "{{")?; + for statement in statements { + // for each statement: statement to string, split by lines, preprend each line with tab, print + // return first error or, on success, "collect" the resulting () into a Vec (ZST, so no allocation) + format!("{statement}") + .split("\n") + .map(|line| writeln!(f, "\t{line}")) + .collect::, std::fmt::Error>>()?; + } + write!(f, "}}") + } + } + } +}