From 1b216c0e4e8acdb98a2e70d452bd25fa00a88de3 Mon Sep 17 00:00:00 2001 From: Moritz Gmeiner Date: Wed, 25 Jan 2023 19:01:13 +0100 Subject: [PATCH] Chapter 10 done --- src/error.rs | 52 ++- src/interpreter/environment.rs | 145 ++++++-- src/interpreter/function.rs | 104 ++++++ src/interpreter/{eval.rs => interpret.rs} | 179 +++++++--- src/interpreter/lox_std.rs | 99 ++++++ src/interpreter/mod.rs | 8 +- src/interpreter/run.rs | 36 +- src/interpreter/value.rs | 106 ++++++ src/lexer/lexer.rs | 5 +- src/lexer/token.rs | 5 +- src/misc.rs | 9 + src/parser/GRAMMAR | 40 ++- src/parser/expr.rs | 88 ++++- src/parser/mod.rs | 2 +- src/parser/parser.rs | 399 +++++++++++++++++++--- src/parser/stmt.rs | 120 +++++-- 16 files changed, 1196 insertions(+), 201 deletions(-) create mode 100644 src/interpreter/function.rs rename src/interpreter/{eval.rs => interpret.rs} (51%) create mode 100644 src/interpreter/lox_std.rs create mode 100644 src/interpreter/value.rs diff --git a/src/error.rs b/src/error.rs index 22196db..854b47c 100644 --- a/src/error.rs +++ b/src/error.rs @@ -25,8 +25,8 @@ pub enum LexerError { pub enum ParserError { #[error("Token stream ended unexpectedly.")] TokenStreamEnded, - #[error("Unexpected token {token} at {0}.", token.code_pos)] - UnexpectedToken { token: Token }, + #[error("Expected a primary expression, but found a {token} token instead at {0}.", token.code_pos)] + ExpectedPrimary { token: Token }, #[error("Missing semicolon at {code_pos}")] MissingSemicolon { code_pos: CodePos }, #[error("Expected variable name at {0}, got {token} instead", token.code_pos)] @@ -36,7 +36,23 @@ pub enum ParserError { #[error("Missing closing curly brace at {code_pos}")] MissingRightBrace { code_pos: CodePos }, #[error("Missing closing parenthesis at {code_pos}")] - MissingRightParenthesis { code_pos: CodePos }, + MissingRightParen { code_pos: CodePos }, + #[error("Missing parenthesis after if at {code_pos}")] + MissingParenAfterIf { code_pos: CodePos }, + #[error("Missing parenthesis after while at {code_pos}")] + MissingParenAfterWhile { code_pos: CodePos }, + #[error("Missing parenthesis after for at {code_pos}")] + MissingParenAfterFor { code_pos: CodePos }, + #[error("Call at {code_pos} has too many arguments")] + TooManyArguments { code_pos: CodePos }, + #[error("{msg} at {code_pos}")] + MissingIdentifier { msg: String, code_pos: CodePos }, + #[error("Missing arguments to function declaration at {code_pos}")] + MissingFunctionArgs { code_pos: CodePos }, + #[error("Missing body to function declaration at {code_pos}")] + MissingFunctionBody { code_pos: CodePos }, + #[error("Function declaration at {code_pos} has too many parameters")] + TooManyParams { code_pos: CodePos }, } #[derive(Error, Debug)] @@ -49,19 +65,34 @@ pub enum RuntimeError { DivisionByZero, #[error("Name {name} is not defined")] NameNotDefined { name: String }, + #[error("{callee} is not callable")] + NotCallable { callee: Value }, + #[error("{name}() takes {arity} args, but {given} were given.")] + WrongArity { name: String, arity: usize, given: usize }, + #[error("Extern function call to {name} failed: {msg}")] + ExtFunCallFailed { name: String, msg: String }, + #[error("Uncaught break statement")] + Break, + #[error("Uncaught return statement")] + Return { value: Value }, + #[error("Exit with exit code {exit_code}")] + Exit { exit_code: i32 }, } #[derive(Error, Debug)] +#[allow(clippy::enum_variant_names)] pub enum LoxError { - #[error("{0}", format_errors(inner))] + #[error("{0}", format_multiple_errors(inner))] LexerError { inner: Vec }, - #[error("{0}", format_errors(inner))] + #[error("{0}", format_multiple_errors(inner))] ParserError { inner: Vec }, #[error("{inner}")] - EvalError { inner: RuntimeError }, + RuntimeError { inner: RuntimeError }, + #[error("Called exit() with exit code {exit_code}")] + Exit { exit_code: i32 }, } -fn format_errors(errs: &Vec) -> String { +fn format_multiple_errors(errs: &Vec) -> String { let msg = if errs.len() == 1 { format!("{}", errs[0]) } else { @@ -85,7 +116,10 @@ impl From> for LoxError { } impl From for LoxError { - fn from(eval_err: RuntimeError) -> Self { - LoxError::EvalError { inner: eval_err } + fn from(runtime_err: RuntimeError) -> Self { + match runtime_err { + RuntimeError::Exit { exit_code } => LoxError::Exit { exit_code }, + _ => LoxError::RuntimeError { inner: runtime_err }, + } } } diff --git a/src/interpreter/environment.rs b/src/interpreter/environment.rs index eab5cbb..0a4e8f4 100644 --- a/src/interpreter/environment.rs +++ b/src/interpreter/environment.rs @@ -1,67 +1,146 @@ +use std::borrow::Borrow; use std::collections::HashMap; use crate::error::RuntimeError; +use super::lox_std::init_std; +use super::value::HeapValue; use super::Value; -type Scope = HashMap; +pub type Scope = HashMap; pub struct Environment { - globals: HashMap, + globals: Scope, - scopes: Vec, + // scopes: Vec, + frames: Vec, } impl Environment { pub fn new() -> Self { - Environment { + let mut env = Environment { globals: HashMap::new(), - scopes: Vec::new(), + frames: vec![Frame::new_global()], + }; + + init_std(&mut env); + + env + } + + fn current_frame(&self) -> &Frame { + self.frames.last().unwrap() + } + + fn current_frame_mut(&mut self) -> &mut Frame { + self.frames.last_mut().unwrap() + } + + pub fn globals(&self) -> &Scope { + &self.globals + } + + pub fn get(&self, name: &str) -> Result { + if let Some(value) = self.current_frame().get(name) { + return Ok(value); + } + + match self.globals.get(name) { + Some(heap_value) => Ok(heap_value.get_clone()), + None => Err(RuntimeError::NameNotDefined { name: name.to_owned() }), } } - 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 define(&mut self, name: impl Into + Borrow, value: Value) { + if let Err(value) = self.current_frame_mut().try_define(name.borrow(), value) { + self.globals.insert(name.into(), HeapValue::new(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(()); - } - } + let value = match self.current_frame_mut().try_assign(name, value) { + Ok(()) => return Ok(()), + Err(value) => value, + }; if let Some(var) = self.globals.get_mut(name) { - *var = value; + // *var = value; + var.replace(value); Ok(()) } else { - let name = name.to_owned(); - Err(RuntimeError::NameNotDefined { name }) + Err(RuntimeError::NameNotDefined { name: name.to_owned() }) } } pub fn enter_scope(&mut self) { - self.scopes.push(Scope::new()); + self.current_frame_mut().enter_scope(); } pub fn exit_scope(&mut self) { - self.scopes.pop().expect("Tried to exit scope, but no scope to exit"); + self.current_frame_mut().exit_scope(); + } + + pub fn push_frame(&mut self, base_scope: Scope) { + self.frames.push(Frame::new(base_scope)); + } + + pub fn pop_frame(&mut self) { + self.frames.pop().expect("Tried to pop global frame"); + } +} + +/*====================================================================================================================*/ + +struct Frame { + scopes: Vec, +} + +impl Frame { + fn new(base_scope: Scope) -> Self { + Frame { + scopes: vec![base_scope], + } + } + + fn new_global() -> Self { + Frame { scopes: Vec::new() } + } + + fn enter_scope(&mut self) { + self.scopes.push(Scope::new()); + } + + fn exit_scope(&mut self) { + self.scopes.pop().expect("Tried to exit scope, but no scope to exit"); + } + + fn get(&self, name: &str) -> Option { + for scope in self.scopes.iter().rev() { + if let Some(heap_value) = scope.get(name) { + return Some(heap_value.get_clone()); + } + } + + None + } + + fn try_define(&mut self, name: impl Into + Borrow, value: Value) -> Result<(), Value> { + if let Some(scope) = self.scopes.last_mut() { + scope.insert(name.into(), HeapValue::new(value)); + Ok(()) + } else { + Err(value) + } + } + + fn try_assign(&mut self, name: &str, value: Value) -> Result<(), Value> { + for scope in self.scopes.iter_mut().rev() { + if let Some(heap_value) = scope.get_mut(name) { + heap_value.replace(value); + return Ok(()); + } + } + + Err(value) } } diff --git a/src/interpreter/function.rs b/src/interpreter/function.rs new file mode 100644 index 0000000..0b83116 --- /dev/null +++ b/src/interpreter/function.rs @@ -0,0 +1,104 @@ +use std::fmt::{Debug, Display}; + +use crate::parser::Stmt; + +use super::environment::{Environment, Scope}; +use super::interpret::EvalResult; +use super::Value; + +#[derive(Debug, Clone)] +pub struct LoxFunction { + name: String, + closure: Scope, + param_names: Vec, + body: Box, +} + +impl LoxFunction { + pub fn new(name: impl Into, param_names: Vec, body: Stmt) -> Self { + let name = name.into(); + let body = Box::new(body); + LoxFunction { + name, + closure: Scope::new(), + param_names, + body, + } + } + + pub fn arity(&self) -> usize { + self.param_names.len() + } + + pub fn name(&self) -> &str { + &self.name + } + + pub fn closure(&self) -> &Scope { + &self.closure + } + + pub fn param_names(&self) -> impl Iterator { + self.param_names.iter() + } + + pub fn body(&self) -> &Stmt { + &self.body + } +} + +impl Display for LoxFunction { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "", self.name) + } +} + +/*====================================================================================================================*/ + +pub type ExternFunClosure = fn(Vec, &mut Environment) -> EvalResult; + +#[derive(Clone)] +pub struct LoxExternFunction { + name: String, + + closure: ExternFunClosure, +} + +impl LoxExternFunction { + pub fn new(name: impl Into, closure: ExternFunClosure) -> Self { + let name = name.into(); + + LoxExternFunction { name, closure } + } + + pub fn register(self, env: &mut Environment) { + let name = self.name.clone(); + let fun = Value::extern_function(self); + env.define(name, fun); + } + + pub fn call(&self, args: Vec, env: &mut Environment) -> EvalResult { + (self.closure)(args, env) + } +} + +impl Display for LoxExternFunction { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "", self.name) + } +} + +impl Debug for LoxExternFunction { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("LoxExternFunction") + .field("name", &self.name) + .field("closure", &"") + .finish() + } +} + +impl PartialEq for LoxExternFunction { + fn eq(&self, other: &Self) -> bool { + self.name == other.name + } +} diff --git a/src/interpreter/eval.rs b/src/interpreter/interpret.rs similarity index 51% rename from src/interpreter/eval.rs rename to src/interpreter/interpret.rs index 2990b3a..ac274c8 100644 --- a/src/interpreter/eval.rs +++ b/src/interpreter/interpret.rs @@ -1,46 +1,17 @@ -use std::fmt::Display; +use std::rc::Rc; use crate::error::RuntimeError; -use crate::parser::{BinaryOp, Expr, Literal, Stmt, UnaryOp}; +use crate::parser::{BinaryOp, Expr, Literal, LogicalOp, Stmt, UnaryOp}; use super::environment::Environment; +use super::{LoxFunction, Value}; pub type EvalResult = Result; /*====================================================================================================================*/ pub fn execute(statement: Stmt, env: &mut Environment) -> Result<(), RuntimeError> { - statement.eval(env) -} - -/*====================================================================================================================*/ - -#[derive(Debug, Clone, PartialEq)] -pub enum Value { - String(String), - Number(f64), - Bool(bool), - Nil, -} - -impl Value { - fn is_truthy(&self) -> bool { - match self { - Value::Bool(false) | Value::Nil => false, - _ => true, - } - } -} - -impl Display for Value { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Value::String(s) => write!(f, "\"{s}\""), - Value::Number(num) => write!(f, "{num}"), - Value::Bool(b) => write!(f, "{b}"), - Value::Nil => write!(f, "nil"), - } - } + statement.execute(env) } /*====================================================================================================================*/ @@ -50,24 +21,25 @@ impl Display for Value { } */ impl Literal { - fn eval(self, _env: &mut Environment) -> EvalResult { + fn eval(&self, env: &mut Environment) -> EvalResult { + let _ = env; match self { - Literal::String(s) => Ok(Value::String(s)), - Literal::Number(num) => Ok(Value::Number(num)), - Literal::Bool(b) => Ok(Value::Bool(b)), + Literal::String(s) => Ok(Value::String(Rc::clone(s))), + Literal::Number(num) => Ok(Value::Number(*num)), + Literal::Bool(b) => Ok(Value::Bool(*b)), Literal::Nil => Ok(Value::Nil), } } } impl Expr { - fn eval(self, env: &mut Environment) -> EvalResult { + fn eval(&self, env: &mut Environment) -> EvalResult { match self { Expr::Literal { literal } => literal.eval(env), Expr::Unary { op, expr } => { let arg = expr.eval(env)?; - match (op, arg) { + match (*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())), @@ -80,7 +52,7 @@ impl Expr { let left = left.eval(env)?; let right = right.eval(env)?; - match (left, op, right) { + 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)), @@ -91,7 +63,12 @@ impl Expr { Ok(Number(left / right)) } - (String(left), BinaryOp::Add, String(right)) => Ok(String(left + &right)), + (String(left), BinaryOp::Add, String(right)) => { + let mut s = std::string::String::with_capacity(left.capacity() + right.capacity()); + s += &left; + s += &right; + Ok(String(Rc::new(s))) + } (left, BinaryOp::Equal, right) => Ok(Bool(left == right)), (left, BinaryOp::NotEqual, right) => Ok(Bool(left != right)), @@ -109,26 +86,59 @@ impl Expr { (left, op, right) => Err(RuntimeError::BinaryOpInvalidArguments { left, op, right }), } } + Expr::Logical { left, op, right } => { + let left = left.eval(env)?; + match op { + LogicalOp::Or => { + if left.is_truthy() { + return Ok(left); + } + } + LogicalOp::And => { + if !left.is_truthy() { + return Ok(left); + } + } + } + let right = right.eval(env)?; + Ok(right) + } Expr::Grouping { expr } => expr.eval(env), - Expr::Variable { name } => env.get(&name).cloned(), + Expr::Variable { name } => env.get(name), Expr::Assignment { name, value } => { let value = value.eval(env)?; - env.assign(&name, value.clone())?; + env.assign(name, value.clone())?; Ok(value) } + Expr::Call { callee, args } => { + let callee = callee.eval(env)?; + let args = args + .iter() + .map(|arg| arg.eval(env)) + .collect::>>()?; + + match callee { + Value::Function(fun) => fun.call(args, env), + Value::ExternFunction(ext_fun) => ext_fun.call(args, env), + _ => Err(RuntimeError::NotCallable { callee }), + } + } + Expr::Function { + name, + param_names, + body, + } => Ok(Value::function(LoxFunction::new( + name, + param_names.clone(), + body.as_ref().clone(), + ))), } } } impl Stmt { - fn eval(self, env: &mut Environment) -> EvalResult<()> { + fn execute(&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 "" @@ -138,20 +148,85 @@ impl Stmt { Ok(()) } + Stmt::IfStmt { + condition, + then_branch, + else_branch, + } => { + let condition = condition.eval(env)?; + if condition.is_truthy() { + then_branch.execute(env) + } else if let Some(else_branch) = else_branch { + else_branch.execute(env) + } else { + Ok(()) + } + } + Stmt::While { condition, body } => { + while condition.eval(env)?.is_truthy() { + match body.execute(env) { + Ok(_) => {} + Err(RuntimeError::Break) => break, + Err(err) => return Err(err), + } + } + Ok(()) + } Stmt::VarDecl { name, initializer } => { let initializer = initializer.eval(env)?; - env.define(name, initializer); + env.define(name.as_ref(), 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)?; + statement.execute(env)?; } env.exit_scope(); Ok(()) } + Stmt::ExprStmt { expr } => { + // expr.eval(env)?; + // Ok(Value::Nil) + expr.eval(env)?; + Ok(()) + } + Stmt::Break => Err(RuntimeError::Break), + Stmt::Return { expr } => { + let value = expr.eval(env)?; + Err(RuntimeError::Return { value }) + } } } } + +/*====================================================================================================================*/ + +impl LoxFunction { + pub fn call(&self, args: Vec, env: &mut Environment) -> EvalResult { + if args.len() != self.arity() { + return Err(RuntimeError::WrongArity { + name: self.name().to_owned(), + arity: self.arity(), + given: args.len(), + }); + } + + env.push_frame(self.closure().clone()); + + for (name, value) in std::iter::zip(self.param_names(), args) { + env.define(name.as_ref(), value); + } + + let ret_val = match self.body().execute(env) { + Ok(()) => Value::Nil, + Err(RuntimeError::Return { value }) => value, + Err(err) => return Err(err), + }; + + env.pop_frame(); + + Ok(ret_val) + } +} diff --git a/src/interpreter/lox_std.rs b/src/interpreter/lox_std.rs new file mode 100644 index 0000000..73a3f9b --- /dev/null +++ b/src/interpreter/lox_std.rs @@ -0,0 +1,99 @@ +use crate::error::RuntimeError; + +use super::environment::Environment; +use super::function::{ExternFunClosure, LoxExternFunction}; +use super::value::HeapValue; +use super::Value; + +pub fn init_std(env: &mut Environment) { + input().register(env); + clock().register(env); + print_globals().register(env); + exit().register(env); +} + +fn input() -> LoxExternFunction { + let closure: ExternFunClosure = |args, _env| match *args { + [] => { + let mut buf = String::new(); + std::io::stdin() + .read_line(&mut buf) + .map_err(|err| RuntimeError::ExtFunCallFailed { + name: "input".to_owned(), + msg: format!("input() failed to read from stdin: {err}"), + })?; + + assert_eq!(buf.pop(), Some('\n')); + + Ok(Value::string(buf)) + } + _ => Err(RuntimeError::ExtFunCallFailed { + name: "input".to_owned(), + msg: "input() takes no arguments".to_owned(), + }), + }; + + LoxExternFunction::new("input", closure) +} + +fn clock() -> LoxExternFunction { + let closure: ExternFunClosure = |args, _env| match *args { + [] => { + let time = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_secs_f64(); + + Ok(Value::Number(time)) + } + _ => Err(RuntimeError::ExtFunCallFailed { + name: "clock".to_owned(), + msg: "clock() takes no arguments".to_owned(), + }), + }; + + LoxExternFunction::new("clock", closure) +} + +fn print_globals() -> LoxExternFunction { + let closure: ExternFunClosure = |args, env| match *args { + [] => { + let mut globals: Vec<(&String, &HeapValue)> = env.globals().iter().collect(); + + globals.sort_by_key(|&(name, _value)| name); + + for (name, value) in globals { + println!("{name}: {value}"); + } + + Ok(Value::Nil) + } + _ => Err(RuntimeError::ExtFunCallFailed { + name: "print_globals".to_owned(), + msg: "print_globals() takes no arguments".to_owned(), + }), + }; + + LoxExternFunction::new("print_globals", closure) +} + +fn exit() -> LoxExternFunction { + let closure: ExternFunClosure = |args, _env| { + match &*args { + [] => return Err(RuntimeError::Exit { exit_code: 0 }), + [Value::Number(exit_code)] => { + if exit_code.fract() == 0.0 { + let exit_code = *exit_code as i32; + return Err(RuntimeError::Exit { exit_code }); + } + } + _ => {} + } + Err(RuntimeError::ExtFunCallFailed { + name: "exit".to_owned(), + msg: "Arguments to exit() must an integer or nothing for exit code 0".to_owned(), + }) + }; + + LoxExternFunction::new("exit", closure) +} diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 726f65e..8d949d1 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -1,6 +1,10 @@ mod environment; -mod eval; +mod function; +mod interpret; +mod lox_std; mod run; +mod value; -pub use eval::Value; +pub use function::LoxFunction; pub use run::interpreter_main; +pub use value::Value; diff --git a/src/interpreter/run.rs b/src/interpreter/run.rs index 2b284de..4c9fd4c 100644 --- a/src/interpreter/run.rs +++ b/src/interpreter/run.rs @@ -1,8 +1,7 @@ use std::io::Write; use crate::error::LoxError; -use crate::interpreter::eval::execute; -use crate::interpreter::Value; +use crate::interpreter::interpret::execute; use crate::lexer::{scan_tokens, Token}; use crate::parser::parse_tokens; @@ -34,11 +33,15 @@ fn run_file(script_path: &str) { 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), - LoxError::EvalError { .. } => std::process::exit(70), + match run(&source_code, &mut env) { + Ok(()) => std::process::exit(0), + Err(err) => { + eprintln!("{err}"); + match err { + LoxError::LexerError { .. } | LoxError::ParserError { .. } => std::process::exit(65), + LoxError::RuntimeError { .. } => std::process::exit(70), + LoxError::Exit { exit_code } => std::process::exit(exit_code), + } } } } @@ -48,7 +51,7 @@ fn run_repl() { let mut env = Environment::new(); - 'outer: loop { + loop { let mut input_buf = String::new(); print!("> "); @@ -60,7 +63,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); @@ -72,22 +75,21 @@ 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(); } - input_buf = input_buf.trim().to_owned(); + let input_buf = input_buf.trim(); if input_buf.is_empty() || input_buf == "exit" || input_buf == "quit" { - break 'outer; + std::process::exit(0); } - match run(&input_buf, &mut env) { + match run(input_buf, &mut env) { Ok(()) => {} + Err(LoxError::Exit { exit_code }) => std::process::exit(exit_code), Err(err) => eprintln!("{}", err), } } @@ -106,7 +108,9 @@ fn run(code_string: &str, env: &mut Environment) -> Result<(), LoxError> { let statements = parse_tokens(tokens)?; - // println!("{expr}"); + /* for statement in statements.iter() { + println!("{statement}"); + } */ // let mut result = Value::Nil; diff --git a/src/interpreter/value.rs b/src/interpreter/value.rs new file mode 100644 index 0000000..42dcd1e --- /dev/null +++ b/src/interpreter/value.rs @@ -0,0 +1,106 @@ +use std::cell::UnsafeCell; +use std::fmt::{Debug, Display}; +use std::rc::Rc; + +use super::function::LoxExternFunction; +use super::LoxFunction; + +#[derive(Debug, Clone)] +pub enum Value { + Function(Rc), + ExternFunction(Rc), + String(Rc), + Number(f64), + Bool(bool), + Nil, +} + +impl Value { + pub fn function(fun: LoxFunction) -> Self { + let fun = Rc::new(fun); + Value::Function(fun) + } + + pub fn extern_function(fun: LoxExternFunction) -> Self { + let fun = Rc::new(fun); + Value::ExternFunction(fun) + } + + pub fn string(s: impl Into) -> Self { + let s = Rc::new(s.into()); + Value::String(s) + } + + pub fn is_truthy(&self) -> bool { + !matches!(self, Value::Bool(false) | Value::Nil) + } +} + +impl Display for Value { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Value::Function(fun) => write!(f, "{fun}"), + Value::ExternFunction(ext_fun) => write!(f, "{ext_fun}"), + Value::String(s) => write!(f, "\"{s}\""), + Value::Number(num) => write!(f, "{num}"), + Value::Bool(b) => write!(f, "{b}"), + Value::Nil => write!(f, "nil"), + } + } +} + +impl PartialEq for Value { + fn eq(&self, other: &Value) -> bool { + match (self, other) { + (Value::Function(l0), Value::Function(r0)) => Rc::ptr_eq(l0, r0), + (Value::ExternFunction(l0), Value::ExternFunction(r0)) => Rc::ptr_eq(l0, r0), + (Value::String(l0), Value::String(r0)) => l0 == r0, + (Value::Number(l0), Value::Number(r0)) => l0 == r0, + (Value::Bool(l0), Value::Bool(r0)) => l0 == r0, + (Value::Nil, Value::Nil) => true, + _ => false, + } + } +} + +/*====================================================================================================================*/ + +#[derive(Clone)] +pub struct HeapValue { + inner: Rc>, +} + +impl HeapValue { + pub fn new(value: Value) -> Self { + let inner = Rc::new(UnsafeCell::new(value)); + HeapValue { inner } + } + + pub fn get_clone(&self) -> Value { + unsafe { + let ptr = self.inner.get(); + let value = &*ptr; + value.clone() + } + } + + pub fn replace(&mut self, value: Value) { + let mut value = value; + unsafe { + let ptr_mut = self.inner.get(); + std::ptr::swap(&mut value, ptr_mut); + } + } +} + +impl Debug for HeapValue { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("HeapValue").field("inner", &self.get_clone()).finish() + } +} + +impl Display for HeapValue { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.get_clone()) + } +} diff --git a/src/lexer/lexer.rs b/src/lexer/lexer.rs index 3ce0691..c525b7f 100644 --- a/src/lexer/lexer.rs +++ b/src/lexer/lexer.rs @@ -9,9 +9,10 @@ use super::{Token, TokenType}; static KEYWORDS: phf::Map<&'static str, TokenType> = phf_map! { "and" => TokenType::And, + "break" => TokenType::Break, "class" => TokenType::Class, "else" => TokenType::Else, - "false" => TokenType::Else, + "false" => TokenType::False, "for" => TokenType::For, "fun" => TokenType::Fun, "if" => TokenType::If, @@ -232,7 +233,7 @@ impl Lexer { } fn push_token(&mut self, token_type: TokenType) { - let lexeme: String = self.source[self.start..self.current].iter().collect(); + // let lexeme: String = self.source[self.start..self.current].iter().collect(); self.tokens.push(Token::new(token_type, self.code_pos)); } diff --git a/src/lexer/token.rs b/src/lexer/token.rs index 33fff42..1da1471 100644 --- a/src/lexer/token.rs +++ b/src/lexer/token.rs @@ -1,6 +1,6 @@ use crate::misc::CodePos; -#[allow(dead_code)] +#[allow(dead_code, clippy::upper_case_acronyms)] #[derive(Debug, Clone, PartialEq)] #[rustfmt::skip] pub enum TokenType { @@ -20,12 +20,13 @@ pub enum TokenType { Number(f64), // Keywords - And, Class, Else, False, Fun, For, If, Nil, Or, + And, Break, Class, Else, False, Fun, For, If, Nil, Or, Print, Return, Super, This, True, Var, While, EOF } +#[derive(Clone)] pub struct Token { pub token_type: TokenType, // pub lexeme: String, diff --git a/src/misc.rs b/src/misc.rs index 955df68..2b55450 100644 --- a/src/misc.rs +++ b/src/misc.rs @@ -23,3 +23,12 @@ impl Debug for CodePos { write!(f, "{}:{}", self.line, self.col) } } + +/*====================================================================================================================*/ + +pub fn indent(s: String) -> String { + s.split('\n') + .map(|line| format!("\t{line}")) + .collect::>() + .join("\n") +} diff --git a/src/parser/GRAMMAR b/src/parser/GRAMMAR index 74d6a85..118ae5c 100644 --- a/src/parser/GRAMMAR +++ b/src/parser/GRAMMAR @@ -1,18 +1,44 @@ program -> statement* EOF ; -statement -> exprStmt | printStmt | declaration | block ; +statement -> if_stmt + | print_stmt + | while_stmt + | for_stmt + | declaration + | block + | expr_stmt + | break + | return_stmt ; -exprStmt -> expression ";" ; -printStmt -> "print" expression ";" ; -declaration -> "var" IDENTIFIER ( "=" expression )? ";" ; +if_stmt -> "if" "(" expression ")" statement ( "else" statement )? ; +print_stmt -> "print" expression ";" ; +while_stmt -> "while" "(" expression ")" statement ; +for_stmt -> "for" "(" (declaration | expr_stmt | ";") ";" expression? ";" expression ";" ")" statement ; +declaration -> var_decl | fun_decl ; block -> "{" statement* "}" ; +expr_Stmt -> expression ";" ; +break -> "break" ";" ; +return -> "return" expression? ";" ; + +var_decl -> "var" IDENTIFIER ( "=" expression )? ";" +fun_decl -> "fun" IDENTIFIER "(" parameters ")" block ; expression -> assignment -assignment -> IDENTIFIER "=" assignment | equality ; +assignment -> IDENTIFIER "=" assignment | logic_or ; +logic_or -> logic_and ( "or" logic_and )* ; +logic_and -> equality ( "and" equality )* ; equality -> comparison ( ( "==" | "!=" ) comparison )* ; comparison -> term ( ">" | ">=" | "<" | "<=" term )* ; term -> factor ( ( "+" | "-" ) factor )* factor -> unary ( ( "*" | "/" ) unary )* ; -unary -> ( "!" | "-" ) unary | primary ; -primary -> "(" expression ")" | IDENTIFIER | NUMBER | STRING | "true" | "false" | "nil" ; \ No newline at end of file +unary -> ( "!" | "-" ) unary | call ; + +call -> primary ( "(" arguments? ")" )* ; +arguments -> expression ( "," expression )* ; + +primary -> "(" expression ")" | IDENTIFIER | fun_expr | NUMBER | STRING | "true" | "false" | "nil" ; + +fun_expr -> "fun" "(" parameters ")" block ; + +parameters -> ( IDENTIFIER ( "," IDENTIFIER )* )? ; \ No newline at end of file diff --git a/src/parser/expr.rs b/src/parser/expr.rs index b743ca5..7bb8491 100644 --- a/src/parser/expr.rs +++ b/src/parser/expr.rs @@ -1,6 +1,9 @@ use std::fmt::Display; +use std::rc::Rc; -#[derive(Debug)] +use super::Stmt; + +#[derive(Debug, Clone)] pub enum Expr { Literal { literal: Literal, @@ -14,6 +17,11 @@ pub enum Expr { op: BinaryOp, right: Box, }, + Logical { + left: Box, + op: LogicalOp, + right: Box, + }, Grouping { expr: Box, }, @@ -24,12 +32,22 @@ pub enum Expr { name: String, value: Box, }, + Call { + callee: Box, + args: Vec, + }, + Function { + name: String, + param_names: Vec, + body: Box, + }, } impl Expr { - pub fn string(s: String) -> Self { + pub fn string(s: impl Into) -> Self { + let s = s.into(); Expr::Literal { - literal: Literal::String(s), + literal: Literal::String(Rc::new(s)), } } @@ -60,15 +78,36 @@ impl Expr { Expr::Binary { left, op, right } } + pub fn logical(left: Expr, op: LogicalOp, right: Expr) -> Self { + let left = Box::new(left); + let right = Box::new(right); + Expr::Logical { left, op, right } + } + pub fn grouping(expr: Expr) -> Self { let expr = Box::new(expr); Expr::Grouping { expr } } - pub fn assignment(name: String, value: Expr) -> Self { + pub fn assignment(name: impl Into, value: Expr) -> Self { + let name = name.into(); let value = Box::new(value); Expr::Assignment { name, value } } + + pub fn call(callee: Expr, args: Vec) -> Self { + let callee = Box::new(callee); + Expr::Call { callee, args } + } + + pub fn function(name: String, param_names: Vec, body: Stmt) -> Self { + let body = Box::new(body); + Self::Function { + name, + param_names, + body, + } + } } impl Display for Expr { @@ -77,18 +116,36 @@ impl Display for 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::Logical { 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}"), + Expr::Call { callee, args } => write!( + f, + "({callee} {})", + args.iter() + .map(|arg| format!("{arg}")) + .collect::>() + .join(" ") + ), + Expr::Function { + name, + param_names, + body, + } => { + write!(f, "fun {name}({}) {body}", param_names.join(", ")) + } } } } /*====================================================================================================================*/ -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum Literal { - String(String), + String(Rc), Number(f64), Bool(bool), Nil, @@ -107,7 +164,7 @@ impl Display for Literal { /*====================================================================================================================*/ -#[derive(Debug)] +#[derive(Debug, Clone, Copy)] pub enum UnaryOp { Negate, Not, @@ -124,7 +181,7 @@ impl Display for UnaryOp { /*====================================================================================================================*/ -#[derive(Debug)] +#[derive(Debug, Clone, Copy)] #[rustfmt::skip] pub enum BinaryOp { // arithmetic @@ -154,3 +211,18 @@ impl Display for BinaryOp { } } } + +#[derive(Debug, Clone, Copy)] +pub enum LogicalOp { + Or, + And, +} + +impl Display for LogicalOp { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + LogicalOp::Or => write!(f, "or"), + LogicalOp::And => write!(f, "and"), + } + } +} diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 5d2a9ff..2b4daea 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -2,6 +2,6 @@ mod expr; mod parser; mod stmt; -pub use expr::{BinaryOp, Expr, Literal, UnaryOp}; +pub use expr::{BinaryOp, Expr, Literal, LogicalOp, UnaryOp}; pub use parser::parse_tokens; pub use stmt::Stmt; diff --git a/src/parser/parser.rs b/src/parser/parser.rs index 732c798..b0ad993 100644 --- a/src/parser/parser.rs +++ b/src/parser/parser.rs @@ -5,7 +5,7 @@ use crate::lexer::{Token, TokenType}; use crate::parser::expr::BinaryOp; use super::expr::{Expr, UnaryOp}; -use super::Stmt; +use super::{LogicalOp, Stmt}; /*====================================================================================================================*/ @@ -60,12 +60,15 @@ impl Iterator for TokenIter { struct Parser { token_iter: TokenIter, + + parse_errors: Vec, } impl Parser { pub fn new(tokens: Vec) -> Self { Parser { token_iter: TokenIter::new(tokens), + parse_errors: Vec::new(), } } @@ -73,7 +76,7 @@ impl Parser { let mut me = self; let mut statements = Vec::new(); - let mut parse_errors = Vec::new(); + // let mut parse_errors = Vec::new(); while !me.token_iter.is_empty() && me.peek_token().token_type != TokenType::EOF { // statements.push(me.statement()?); @@ -82,16 +85,18 @@ impl Parser { statements.push(stmt); } Err(err) => { - parse_errors.push(err); + me.parse_errors.push(err); + // println!("Synchronising..."); me.synchronise(); + // println!("Synchronised") } } } // me.consume_token(TokenType::EOF).unwrap(); - if !parse_errors.is_empty() { - Err(parse_errors) + if !me.parse_errors.is_empty() { + Err(me.parse_errors) } else { Ok(statements) } @@ -126,18 +131,133 @@ impl Parser { // no sync point: discard token let _ = self.next_token(); + // println!("Discarding {} token", self.next_token()); } } fn statement(&mut self) -> ParserResult { match self.peek_token().token_type { TokenType::Print => self.print_statement(), + TokenType::If => self.if_statement(), + TokenType::While => self.while_statement(), + TokenType::For => self.for_statement(), TokenType::Var => self.var_declaration(), + TokenType::Fun => self.fun_declaration(), TokenType::LeftBrace => self.block(), + TokenType::Break => { + assert_eq!(self.next_token().token_type, TokenType::Break); + self.semicolon()?; + Ok(Stmt::Break) + } + TokenType::Return => { + assert_eq!(self.next_token().token_type, TokenType::Return); + let expr = match self.peek_token().token_type { + TokenType::Semicolon => Expr::nil(), + _ => self.expression()?, + }; + self.semicolon()?; + Ok(Stmt::return_stmt(expr)) + } _ => self.expression_statement(), } } + fn if_statement(&mut self) -> ParserResult { + assert_eq!(self.next_token().token_type, TokenType::If); + + self.consume_token(TokenType::LeftParen, |token| ParserError::MissingParenAfterIf { + code_pos: token.code_pos, + })?; + + let condition = self.expression()?; + + self.consume_token(TokenType::RightParen, |token| ParserError::MissingRightParen { + code_pos: token.code_pos, + })?; + + let then_branch = self.statement()?; + + let else_branch = if self.peek_token().token_type == TokenType::Else { + // consume else token + let _ = self.next_token(); + + Some(self.statement()?) + } else { + None + }; + + Ok(Stmt::if_stmt(condition, then_branch, else_branch)) + } + + fn while_statement(&mut self) -> ParserResult { + assert_eq!(self.next_token().token_type, TokenType::While); + + self.consume_token(TokenType::LeftParen, |token| ParserError::MissingParenAfterWhile { + code_pos: token.code_pos, + })?; + + let condition = self.expression()?; + + self.consume_token(TokenType::RightParen, |token| ParserError::MissingRightParen { + code_pos: token.code_pos, + })?; + + let body = self.statement()?; + + Ok(Stmt::while_stmt(condition, body)) + } + + fn for_statement(&mut self) -> ParserResult { + assert_eq!(self.next_token().token_type, TokenType::For); + + self.consume_token(TokenType::LeftParen, |token| ParserError::MissingParenAfterFor { + code_pos: token.code_pos, + })?; + + let initializer = match self.peek_token().token_type { + TokenType::Semicolon => { + assert_eq!(self.next_token().token_type, TokenType::Semicolon); + None + } + TokenType::Var => Some(self.var_declaration()?), + _ => Some(self.expression_statement()?), + }; + + let condition = match self.peek_token().token_type { + TokenType::Semicolon => Expr::bool(true), + _ => self.expression()?, + }; + + self.semicolon()?; + + let increment = match self.peek_token().token_type { + TokenType::RightParen => None, + _ => Some(self.expression()?), + }; + + self.consume_token(TokenType::RightParen, |token| ParserError::MissingRightParen { + code_pos: token.code_pos, + })?; + + let mut body = self.statement()?; + + if let Some(increment) = increment { + body = Stmt::Block { + statements: vec![body, Stmt::expr_stmt(increment)], + } + } + + let mut for_stmt = Stmt::while_stmt(condition, body); + + if let Some(initializer) = initializer { + for_stmt = Stmt::Block { + statements: vec![initializer, for_stmt], + }; + } + + Ok(for_stmt) + } + fn print_statement(&mut self) -> ParserResult { // self.consume_token(TokenType::Print)?; assert_eq!(self.next_token().token_type, TokenType::Print); @@ -175,6 +295,97 @@ impl Parser { Ok(Stmt::var_decl(name, initializer)) } + fn fun_declaration(&mut self) -> ParserResult { + assert_eq!(self.next_token().token_type, TokenType::Fun); + + let name = self.identifier("Missing function name")?; + + if self.peek_token().token_type != TokenType::LeftParen { + return Err(ParserError::MissingFunctionArgs { + code_pos: self.peek_token().code_pos, + }); + } + + let params_code_pos = self.peek_token().code_pos; + + let param_names = self.collect_params()?; + + if param_names.len() > 255 { + self.parse_errors.push(ParserError::TooManyParams { + code_pos: params_code_pos, + }); + } + + if self.peek_token().token_type != TokenType::LeftBrace { + return Err(ParserError::MissingFunctionBody { + code_pos: self.peek_token().code_pos, + }); + } + + let body = self.block()?; + + let fun = Expr::function(name.clone(), param_names, body); + + Ok(Stmt::var_decl(name, fun)) + } + + fn lambda(&mut self) -> ParserResult { + // has already been eaten by primary()! + // assert_eq!(self.next_token().token_type, TokenType::Fun); + + if self.peek_token().token_type != TokenType::LeftParen { + return Err(ParserError::MissingFunctionArgs { + code_pos: self.peek_token().code_pos, + }); + } + + let params_code_pos = self.peek_token().code_pos; + + let param_names = self.collect_params()?; + + if param_names.len() > 255 { + self.parse_errors.push(ParserError::TooManyParams { + code_pos: params_code_pos, + }); + } + + if self.peek_token().token_type != TokenType::LeftBrace { + return Err(ParserError::MissingFunctionBody { + code_pos: self.peek_token().code_pos, + }); + } + + let body = self.block()?; + + Ok(Expr::function("".to_owned(), param_names, body)) + } + + fn collect_params(&mut self) -> ParserResult> { + assert_eq!(self.next_token().token_type, TokenType::LeftParen); + + if self.peek_token().token_type == TokenType::RightParen { + assert_eq!(self.next_token().token_type, TokenType::RightParen); + + return Ok(Vec::new()); + } + + let mut param_names = Vec::new(); + + param_names.push(self.identifier("Expected parameter name")?); + + while self.peek_token().token_type == TokenType::Comma { + assert_eq!(self.next_token().token_type, TokenType::Comma); + + param_names.push(self.identifier("Expected parameter name")?); + } + + self.consume_token(TokenType::RightParen, |token| ParserError::MissingRightParen { + code_pos: token.code_pos, + })?; + + Ok(param_names) + } + fn block(&mut self) -> ParserResult { // self.consume_token(TokenType::LeftBrace)?; assert_eq!(self.next_token().token_type, TokenType::LeftBrace); @@ -215,7 +426,7 @@ impl Parser { fn assignment(&mut self) -> ParserResult { let code_pos = self.peek_token().code_pos; - let expr = self.equality()?; + let expr = self.logical_or()?; if self.peek_token().token_type != TokenType::Equal { return Ok(expr); @@ -232,6 +443,36 @@ impl Parser { } } + fn logical_or(&mut self) -> ParserResult { + let mut expr = self.logical_and()?; + + if self.peek_token().token_type == TokenType::Or { + // consume or + let _ = self.next_token(); + + let right = self.logical_or()?; + + expr = Expr::logical(expr, LogicalOp::Or, right); + } + + Ok(expr) + } + + fn logical_and(&mut self) -> ParserResult { + let mut expr = self.equality()?; + + if self.peek_token().token_type == TokenType::And { + // consume and + let _ = self.next_token(); + + let right = self.logical_and()?; + + expr = Expr::logical(expr, LogicalOp::And, right); + } + + Ok(expr) + } + fn equality(&mut self) -> ParserResult { let mut expr = self.comparison()?; @@ -329,10 +570,62 @@ impl Parser { let _ = self.next_token(); Ok(Expr::unary(UnaryOp::Negate, self.unary()?)) } - _ => self.primary(), + _ => self.call(), } } + fn call(&mut self) -> ParserResult { + let mut expr = self.primary()?; + + loop { + match self.peek_token().token_type { + TokenType::LeftParen => { + let args_code_pos = self.peek_token().code_pos; + + let args = self.collect_args()?; + + if args.len() > 255 { + self.parse_errors.push(ParserError::TooManyArguments { + code_pos: args_code_pos, + }); + } + + expr = Expr::call(expr, args); + } + TokenType::Dot => todo!(), + _ => break, + } + } + + Ok(expr) + } + + fn collect_args(&mut self) -> ParserResult> { + assert_eq!(self.next_token().token_type, TokenType::LeftParen); + + if self.peek_token().token_type == TokenType::RightParen { + assert_eq!(self.next_token().token_type, TokenType::RightParen); + + return Ok(Vec::new()); + } + + let mut args = Vec::new(); + + args.push(self.expression()?); + + while self.peek_token().token_type == TokenType::Comma { + assert_eq!(self.next_token().token_type, TokenType::Comma); + + args.push(self.expression()?); + } + + self.consume_token(TokenType::RightParen, |token| ParserError::MissingRightParen { + code_pos: token.code_pos, + })?; + + Ok(args) + } + fn primary(&mut self) -> ParserResult { if self.peek_token().token_type == TokenType::EOF { return Err(ParserError::TokenStreamEnded); @@ -341,6 +634,7 @@ impl Parser { let token = self.next_token(); match token.token_type { + TokenType::Fun => Ok(self.lambda()?), TokenType::Number(num) => Ok(Expr::number(num)), TokenType::String(s) => Ok(Expr::string(s)), TokenType::False => Ok(Expr::bool(false)), @@ -348,56 +642,54 @@ impl Parser { TokenType::Nil => Ok(Expr::nil()), TokenType::LeftParen => { let expr = self.expression()?; - // 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(); + self.consume_token(TokenType::RightParen, |token| ParserError::MissingRightParen { + code_pos: token.code_pos, + })?; Ok(Expr::grouping(expr)) } TokenType::Identifier(name) => Ok(Expr::Variable { name }), - _ => Err(ParserError::UnexpectedToken { token }), + _ => Err(ParserError::ExpectedPrimary { token }), } } 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, + self.consume_token(TokenType::Semicolon, |token| ParserError::MissingSemicolon { + code_pos: token.code_pos, + }) + } + + fn identifier(&mut self, msg: &str) -> ParserResult { + match self.peek_token().token_type { + TokenType::Identifier(_) => { + if let TokenType::Identifier(name) = self.next_token().token_type { + Ok(name) + } else { + unreachable!() + } + } + _ => Err(ParserError::MissingIdentifier { + msg: msg.to_owned(), + code_pos: self.peek_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(); + /* let token = self.token_iter.next().unwrap(); // println!("Next token: {next:?}"); - if let Some(ref token) = next { - if token.token_type == TokenType::EOF { - panic!("Someone ate a EOF token"); - } + if token.token_type == TokenType::EOF { + panic!("Someone ate a EOF token"); } - next.ok_or(ParserError::TokenStreamEnded) */ + // if token.token_type == TokenType::Print { + // panic!("Found the print"); + // } + + token */ self.token_iter.next().unwrap() // .ok_or(ParserError::TokenStreamEnded) } @@ -406,16 +698,25 @@ impl Parser { 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 - } - }) */ + fn consume_token(&mut self, token_type: TokenType, err_fn: F) -> ParserResult<()> + where + F: Fn(Token) -> ParserError, + { + /* let token = self.next_token(); - Ok(self.next_token().token_type == token_type) - } */ + if token.token_type == token_type { + Ok(()) + } else { + Err(err_fn(token)) + } */ + + match &self.peek_token().token_type { + tt if tt == &token_type => { + let _ = self.next_token(); + Ok(()) + } + TokenType::EOF => Err(err_fn(self.peek_token().clone())), + _ => Err(err_fn(self.next_token())), + } + } } diff --git a/src/parser/stmt.rs b/src/parser/stmt.rs index 2dbc164..8781f68 100644 --- a/src/parser/stmt.rs +++ b/src/parser/stmt.rs @@ -1,49 +1,129 @@ use std::fmt::Display; +use crate::misc::indent; + use super::Expr; +#[derive(Debug, Clone)] pub enum Stmt { - ExprStmt { expr: Box }, - Print { expr: Box }, - VarDecl { name: String, initializer: Box }, - Block { statements: Vec }, + Print { + expr: Box, + }, + IfStmt { + condition: Box, + then_branch: Box, + else_branch: Option>, + }, + While { + condition: Box, + body: Box, + }, + VarDecl { + name: String, + initializer: Box, + }, + Block { + statements: Vec, + }, + ExprStmt { + expr: Box, + }, + Break, + Return { + expr: Box, + }, } 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); + pub fn if_stmt( + condition: impl Into>, + then_branch: impl Into>, + else_branch: Option>>, + ) -> Self { + let condition = condition.into(); + let then_branch = then_branch.into(); + let else_branch = else_branch.map(|stmt| stmt.into()); + + Stmt::IfStmt { + condition, + then_branch, + else_branch, + } + } + + pub fn while_stmt(condition: impl Into>, body: impl Into>) -> Self { + let condition = condition.into(); + let body = body.into(); + + Stmt::While { condition, body } + } + + pub fn var_decl(name: impl Into, initializer: impl Into>) -> Self { + let name = name.into(); + let initializer = initializer.into(); Stmt::VarDecl { name, initializer } } + + pub fn expr_stmt(expr: impl Into>) -> Self { + let expr = expr.into(); + Stmt::ExprStmt { expr } + } + + pub fn return_stmt(expr: impl Into>) -> Self { + let expr = expr.into(); + Stmt::Return { expr } + } } 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::Print { expr } => write!(f, "print {expr};"), + Stmt::IfStmt { + condition, + then_branch, + else_branch, + } => { + writeln!(f, "if {condition}")?; + match then_branch.as_ref() { + Stmt::Block { .. } => write!(f, "{then_branch}")?, + _ => write!(f, "{}", indent(format!("{then_branch}")))?, + } + if let Some(else_branch) = else_branch { + writeln!(f, "\nelse")?; + match else_branch.as_ref() { + Stmt::Block { .. } => write!(f, "{else_branch}")?, + _ => write!(f, "{}", indent(format!("{else_branch}")))?, + } + } + Ok(()) + } + Stmt::While { condition, body } => { + writeln!(f, "{condition}")?; + match body.as_ref() { + Stmt::Block { .. } => write!(f, "{body}")?, + _ => write!(f, "{}", indent(format!("{body}")))?, + } + Ok(()) + } + 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, "{}", indent(format!("{statement}")))?; } write!(f, "}}") } + Stmt::ExprStmt { expr } => write!(f, "{expr};"), + Stmt::Break => write!(f, "break;"), + Stmt::Return { expr } => { + write!(f, "return {expr};") + } } } }