mirror of
https://github.com/MorizzG/rlox.git
synced 2025-12-06 04:12:42 +00:00
chapter 8 done
This commit is contained in:
parent
f56fcc4a8b
commit
956c4d0f28
13 changed files with 570 additions and 177 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1 +1,2 @@
|
|||
/target/
|
||||
test.lox
|
||||
54
src/error.rs
54
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<LexerError> },
|
||||
#[error("{0}", format_errors(inner))]
|
||||
ParserError { inner: Vec<ParserError> },
|
||||
#[error("{inner}")]
|
||||
ParserError { inner: ParserError },
|
||||
#[error("{inner}")]
|
||||
EvalError { inner: EvalError },
|
||||
EvalError { inner: RuntimeError },
|
||||
}
|
||||
|
||||
fn format_lexer_errors(lexer_errs: &Vec<LexerError>) -> String {
|
||||
let msg = if lexer_errs.len() == 1 {
|
||||
format!("{}", lexer_errs[0])
|
||||
fn format_errors(errs: &Vec<impl std::error::Error>) -> String {
|
||||
let msg = if errs.len() == 1 {
|
||||
format!("{}", errs[0])
|
||||
} else {
|
||||
let msgs: Vec<String> = lexer_errs.iter().map(|err| format!("{}", err)).collect();
|
||||
let msgs: Vec<String> = errs.iter().map(|err| format!("{}", err)).collect();
|
||||
msgs.join("\n")
|
||||
};
|
||||
|
||||
|
|
@ -70,14 +78,14 @@ impl From<Vec<LexerError>> for LoxError {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<ParserError> for LoxError {
|
||||
fn from(parser_err: ParserError) -> Self {
|
||||
LoxError::ParserError { inner: parser_err }
|
||||
impl From<Vec<ParserError>> for LoxError {
|
||||
fn from(parser_errs: Vec<ParserError>) -> Self {
|
||||
LoxError::ParserError { inner: parser_errs }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<EvalError> for LoxError {
|
||||
fn from(eval_err: EvalError) -> Self {
|
||||
impl From<RuntimeError> for LoxError {
|
||||
fn from(eval_err: RuntimeError) -> Self {
|
||||
LoxError::EvalError { inner: eval_err }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
67
src/interpreter/environment.rs
Normal file
67
src/interpreter/environment.rs
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use crate::error::RuntimeError;
|
||||
|
||||
use super::Value;
|
||||
|
||||
type Scope = HashMap<String, Value>;
|
||||
|
||||
pub struct Environment {
|
||||
globals: HashMap<String, Value>,
|
||||
|
||||
scopes: Vec<Scope>,
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
|
@ -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<Primitive, EvalError>;
|
||||
use super::environment::Environment;
|
||||
|
||||
pub type EvalResult<T> = Result<T, RuntimeError>;
|
||||
|
||||
/*====================================================================================================================*/
|
||||
|
||||
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<Value> {
|
||||
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<Value> {
|
||||
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(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
mod environment;
|
||||
mod eval;
|
||||
mod run;
|
||||
|
||||
pub use eval::Primitive;
|
||||
pub use eval::Value;
|
||||
pub use run::interpreter_main;
|
||||
|
|
|
|||
|
|
@ -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<String> = 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<Token> = 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(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<TokenType> {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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" ;
|
||||
primary -> "(" expression ")" | IDENTIFIER | NUMBER | STRING | "true" | "false" | "nil" ;
|
||||
|
|
@ -2,49 +2,84 @@ use std::fmt::Display;
|
|||
|
||||
#[derive(Debug)]
|
||||
pub enum Expr {
|
||||
Literal(Literal),
|
||||
Unary(UnaryOp, Box<Expr>),
|
||||
Binary(Box<Expr>, BinaryOp, Box<Expr>),
|
||||
Grouping(Box<Expr>),
|
||||
Literal {
|
||||
literal: Literal,
|
||||
},
|
||||
Unary {
|
||||
op: UnaryOp,
|
||||
expr: Box<Expr>,
|
||||
},
|
||||
Binary {
|
||||
left: Box<Expr>,
|
||||
op: BinaryOp,
|
||||
right: Box<Expr>,
|
||||
},
|
||||
Grouping {
|
||||
expr: Box<Expr>,
|
||||
},
|
||||
Variable {
|
||||
name: String,
|
||||
},
|
||||
Assignment {
|
||||
name: String,
|
||||
value: Box<Expr>,
|
||||
},
|
||||
}
|
||||
|
||||
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}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -5,12 +5,13 @@ use crate::lexer::{Token, TokenType};
|
|||
use crate::parser::expr::BinaryOp;
|
||||
|
||||
use super::expr::{Expr, UnaryOp};
|
||||
use super::Stmt;
|
||||
|
||||
/*====================================================================================================================*/
|
||||
|
||||
type ParserResult<T> = Result<T, ParserError>;
|
||||
|
||||
pub fn parse_tokens(tokens: Vec<Token>) -> ParserResult<Expr> {
|
||||
pub fn parse_tokens(tokens: Vec<Token>) -> Result<Vec<Stmt>, Vec<ParserError>> {
|
||||
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<Expr> {
|
||||
pub fn parse(self) -> Result<Vec<Stmt>, Vec<ParserError>> {
|
||||
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<Stmt> {
|
||||
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<Stmt> {
|
||||
// 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<Stmt> {
|
||||
// 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<Stmt> {
|
||||
// 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<Stmt> {
|
||||
let expr = self.expression()?;
|
||||
|
||||
self.semicolon()?;
|
||||
|
||||
Ok(Stmt::expr_stmt(expr))
|
||||
}
|
||||
|
||||
fn expression(&mut self) -> ParserResult<Expr> {
|
||||
self.equality()
|
||||
self.assignment()
|
||||
}
|
||||
|
||||
fn assignment(&mut self) -> ParserResult<Expr> {
|
||||
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<Expr> {
|
||||
|
|
@ -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<Expr> {
|
||||
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<Expr> {
|
||||
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<Token> {
|
||||
// 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),
|
||||
} */
|
||||
|
||||
// println!("Next token: {next:?}");
|
||||
let peek_token = self.peek_token();
|
||||
|
||||
// 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<Token> {
|
||||
self.next_token().and_then(|token| {
|
||||
if token.token_type() == &token_type {
|
||||
Ok(token)
|
||||
if peek_token.token_type == TokenType::Semicolon {
|
||||
self.next_token();
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ParserError::UnexpectedToken { token })
|
||||
}
|
||||
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:?}");
|
||||
|
||||
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<bool> {
|
||||
/* 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)
|
||||
} */
|
||||
}
|
||||
|
|
|
|||
49
src/parser/stmt.rs
Normal file
49
src/parser/stmt.rs
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
use std::fmt::Display;
|
||||
|
||||
use super::Expr;
|
||||
|
||||
pub enum Stmt {
|
||||
ExprStmt { expr: Box<Expr> },
|
||||
Print { expr: Box<Expr> },
|
||||
VarDecl { name: String, initializer: Box<Expr> },
|
||||
Block { statements: Vec<Stmt> },
|
||||
}
|
||||
|
||||
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::<Result<Vec<()>, std::fmt::Error>>()?;
|
||||
}
|
||||
write!(f, "}}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue