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/
|
/target/
|
||||||
|
test.lox
|
||||||
54
src/error.rs
54
src/error.rs
|
|
@ -1,9 +1,9 @@
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::interpreter::Primitive;
|
use crate::interpreter::Value;
|
||||||
use crate::lexer::Token;
|
use crate::lexer::Token;
|
||||||
use crate::misc::CodePos;
|
use crate::misc::CodePos;
|
||||||
use crate::parser::expr::{BinaryOp, UnaryOp};
|
use crate::parser::{BinaryOp, Expr, UnaryOp};
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum LexerError {
|
pub enum LexerError {
|
||||||
|
|
@ -25,39 +25,47 @@ pub enum LexerError {
|
||||||
pub enum ParserError {
|
pub enum ParserError {
|
||||||
#[error("Token stream ended unexpectedly.")]
|
#[error("Token stream ended unexpectedly.")]
|
||||||
TokenStreamEnded,
|
TokenStreamEnded,
|
||||||
#[error("Unexpected token {token} at {0}.", token.code_pos())]
|
#[error("Unexpected token {token} at {0}.", token.code_pos)]
|
||||||
UnexpectedToken { token: Token },
|
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)]
|
#[derive(Error, Debug)]
|
||||||
pub enum EvalError {
|
pub enum RuntimeError {
|
||||||
#[error("Unary operator {op} had invalid argument {arg}")]
|
#[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}")]
|
#[error("Binary operator {op} had invalid arguments {left} and {right}")]
|
||||||
BinaryOpInvalidArguments {
|
BinaryOpInvalidArguments { left: Value, op: BinaryOp, right: Value },
|
||||||
left: Primitive,
|
|
||||||
op: BinaryOp,
|
|
||||||
right: Primitive,
|
|
||||||
},
|
|
||||||
#[error("Division by zero")]
|
#[error("Division by zero")]
|
||||||
DivisionByZero,
|
DivisionByZero,
|
||||||
|
#[error("Name {name} is not defined")]
|
||||||
|
NameNotDefined { name: String },
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum LoxError {
|
pub enum LoxError {
|
||||||
#[error("{0}", format_lexer_errors(inner))]
|
#[error("{0}", format_errors(inner))]
|
||||||
LexerError { inner: Vec<LexerError> },
|
LexerError { inner: Vec<LexerError> },
|
||||||
|
#[error("{0}", format_errors(inner))]
|
||||||
|
ParserError { inner: Vec<ParserError> },
|
||||||
#[error("{inner}")]
|
#[error("{inner}")]
|
||||||
ParserError { inner: ParserError },
|
EvalError { inner: RuntimeError },
|
||||||
#[error("{inner}")]
|
|
||||||
EvalError { inner: EvalError },
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format_lexer_errors(lexer_errs: &Vec<LexerError>) -> String {
|
fn format_errors(errs: &Vec<impl std::error::Error>) -> String {
|
||||||
let msg = if lexer_errs.len() == 1 {
|
let msg = if errs.len() == 1 {
|
||||||
format!("{}", lexer_errs[0])
|
format!("{}", errs[0])
|
||||||
} else {
|
} 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")
|
msgs.join("\n")
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -70,14 +78,14 @@ impl From<Vec<LexerError>> for LoxError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ParserError> for LoxError {
|
impl From<Vec<ParserError>> for LoxError {
|
||||||
fn from(parser_err: ParserError) -> Self {
|
fn from(parser_errs: Vec<ParserError>) -> Self {
|
||||||
LoxError::ParserError { inner: parser_err }
|
LoxError::ParserError { inner: parser_errs }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<EvalError> for LoxError {
|
impl From<RuntimeError> for LoxError {
|
||||||
fn from(eval_err: EvalError) -> Self {
|
fn from(eval_err: RuntimeError) -> Self {
|
||||||
LoxError::EvalError { inner: eval_err }
|
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 std::fmt::Display;
|
||||||
|
|
||||||
use crate::error::EvalError;
|
use crate::error::RuntimeError;
|
||||||
use crate::parser::expr::{BinaryOp, Expr, Literal, UnaryOp};
|
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 {
|
pub fn execute(statement: Stmt, env: &mut Environment) -> Result<(), RuntimeError> {
|
||||||
expr.eval()
|
statement.eval(env)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*====================================================================================================================*/
|
/*====================================================================================================================*/
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum Primitive {
|
pub enum Value {
|
||||||
String(String),
|
String(String),
|
||||||
Number(f64),
|
Number(f64),
|
||||||
Bool(bool),
|
Bool(bool),
|
||||||
Nil,
|
Nil,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Primitive {
|
impl Value {
|
||||||
fn is_truthy(&self) -> bool {
|
fn is_truthy(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Primitive::Bool(false) | Primitive::Nil => false,
|
Value::Bool(false) | Value::Nil => false,
|
||||||
_ => true,
|
_ => true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Primitive {
|
impl Display for Value {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Primitive::String(s) => write!(f, "\"{s}\""),
|
Value::String(s) => write!(f, "\"{s}\""),
|
||||||
Primitive::Number(num) => write!(f, "{num}"),
|
Value::Number(num) => write!(f, "{num}"),
|
||||||
Primitive::Bool(b) => write!(f, "{b}"),
|
Value::Bool(b) => write!(f, "{b}"),
|
||||||
Primitive::Nil => write!(f, "nil"),
|
Value::Nil => write!(f, "nil"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*====================================================================================================================*/
|
/*====================================================================================================================*/
|
||||||
|
|
||||||
trait Eval {
|
/* trait Eval {
|
||||||
fn eval(self) -> EvalResult;
|
fn eval(self, env: &mut Environment) -> EvalResult;
|
||||||
}
|
} */
|
||||||
|
|
||||||
impl Eval for Literal {
|
impl Literal {
|
||||||
fn eval(self) -> EvalResult {
|
fn eval(self, _env: &mut Environment) -> EvalResult<Value> {
|
||||||
match self {
|
match self {
|
||||||
Literal::String(s) => Ok(Primitive::String(s)),
|
Literal::String(s) => Ok(Value::String(s)),
|
||||||
Literal::Number(num) => Ok(Primitive::Number(num)),
|
Literal::Number(num) => Ok(Value::Number(num)),
|
||||||
Literal::Bool(b) => Ok(Primitive::Bool(b)),
|
Literal::Bool(b) => Ok(Value::Bool(b)),
|
||||||
Literal::Nil => Ok(Primitive::Nil),
|
Literal::Nil => Ok(Value::Nil),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eval for Expr {
|
impl Expr {
|
||||||
fn eval(self) -> EvalResult {
|
fn eval(self, env: &mut Environment) -> EvalResult<Value> {
|
||||||
match self {
|
match self {
|
||||||
Expr::Literal(literal) => literal.eval(),
|
Expr::Literal { literal } => literal.eval(env),
|
||||||
Expr::Unary(op, expr) => {
|
Expr::Unary { op, expr } => {
|
||||||
let arg = expr.eval()?;
|
let arg = expr.eval(env)?;
|
||||||
|
|
||||||
match (op, arg) {
|
match (op, arg) {
|
||||||
(UnaryOp::Negate, Primitive::Number(num)) => Ok(Primitive::Number(-num)),
|
(UnaryOp::Negate, Value::Number(num)) => Ok(Value::Number(-num)),
|
||||||
(UnaryOp::Not, Primitive::Bool(b)) => Ok(Primitive::Bool(!b)),
|
(UnaryOp::Not, Value::Bool(b)) => Ok(Value::Bool(!b)),
|
||||||
(UnaryOp::Not, primitive) => Ok(Primitive::Bool(!primitive.is_truthy())),
|
(UnaryOp::Not, primitive) => Ok(Value::Bool(!primitive.is_truthy())),
|
||||||
(op, arg) => Err(EvalError::UnaryOpInvalidArgument { op, arg }),
|
(op, arg) => Err(RuntimeError::UnaryOpInvalidArgument { op, arg }),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expr::Binary(left_expr, op, right_expr) => {
|
Expr::Binary { left, op, right } => {
|
||||||
use Primitive::{Bool, Number, String};
|
use Value::{Bool, Number, String};
|
||||||
|
|
||||||
let left = left_expr.eval()?;
|
let left = left.eval(env)?;
|
||||||
let right = right_expr.eval()?;
|
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::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::Multiply, Number(right)) => Ok(Number(left * right)),
|
||||||
(Number(left), BinaryOp::Divide, Number(right)) => {
|
(Number(left), BinaryOp::Divide, Number(right)) => {
|
||||||
if right == 0.0 {
|
if right == 0.0 {
|
||||||
return Err(EvalError::DivisionByZero);
|
return Err(RuntimeError::DivisionByZero);
|
||||||
}
|
}
|
||||||
Ok(Number(left / right))
|
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::Greater, String(right)) => Ok(Bool(left > right)),
|
||||||
(String(left), BinaryOp::GreaterEqual, 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 eval;
|
||||||
mod run;
|
mod run;
|
||||||
|
|
||||||
pub use eval::Primitive;
|
pub use eval::Value;
|
||||||
pub use run::interpreter_main;
|
pub use run::interpreter_main;
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,12 @@
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
||||||
use crate::error::LoxError;
|
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::lexer::{scan_tokens, Token};
|
||||||
use crate::parser::parser::parse_tokens;
|
use crate::parser::parse_tokens;
|
||||||
|
|
||||||
|
use super::environment::Environment;
|
||||||
|
|
||||||
pub fn interpreter_main() {
|
pub fn interpreter_main() {
|
||||||
let args: Vec<String> = std::env::args().collect();
|
let args: Vec<String> = std::env::args().collect();
|
||||||
|
|
@ -29,7 +32,9 @@ fn run_file(script_path: &str) {
|
||||||
std::process::exit(65);
|
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}");
|
eprintln!("{err}");
|
||||||
match err {
|
match err {
|
||||||
LoxError::LexerError { .. } | LoxError::ParserError { .. } => std::process::exit(65),
|
LoxError::LexerError { .. } | LoxError::ParserError { .. } => std::process::exit(65),
|
||||||
|
|
@ -41,6 +46,8 @@ fn run_file(script_path: &str) {
|
||||||
fn run_repl() {
|
fn run_repl() {
|
||||||
let stdin = std::io::stdin();
|
let stdin = std::io::stdin();
|
||||||
|
|
||||||
|
let mut env = Environment::new();
|
||||||
|
|
||||||
'outer: loop {
|
'outer: loop {
|
||||||
let mut input_buf = String::new();
|
let mut input_buf = String::new();
|
||||||
|
|
||||||
|
|
@ -53,7 +60,7 @@ fn run_repl() {
|
||||||
std::process::exit(66);
|
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_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);
|
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)
|
// 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 {
|
if num_open_braces < 0 || num_open_parens < 0 || num_open_brackets < 0 {
|
||||||
break 'inner;
|
break 'inner;
|
||||||
}
|
} */
|
||||||
|
|
||||||
|
break 'inner;
|
||||||
|
|
||||||
print!("< ");
|
print!("< ");
|
||||||
std::io::stdout().flush().unwrap();
|
std::io::stdout().flush().unwrap();
|
||||||
|
|
@ -77,14 +86,14 @@ fn run_repl() {
|
||||||
break 'outer;
|
break 'outer;
|
||||||
}
|
}
|
||||||
|
|
||||||
match run(&input_buf) {
|
match run(&input_buf, &mut env) {
|
||||||
Ok(()) => {}
|
Ok(()) => {}
|
||||||
Err(err) => eprintln!("{}", err),
|
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 tokens: Vec<Token> = scan_tokens(code_string)?;
|
||||||
|
|
||||||
/* let token_str = tokens
|
/* let token_str = tokens
|
||||||
|
|
@ -95,13 +104,20 @@ fn run(code_string: &str) -> Result<(), LoxError> {
|
||||||
|
|
||||||
println!("{token_str}"); */
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,7 @@ impl Lexer {
|
||||||
me.scan_token();
|
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() {
|
if me.errors.is_empty() {
|
||||||
Ok(me.tokens)
|
Ok(me.tokens)
|
||||||
|
|
@ -234,7 +234,7 @@ impl Lexer {
|
||||||
fn push_token(&mut self, token_type: TokenType) {
|
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, lexeme, self.code_pos));
|
self.tokens.push(Token::new(token_type, self.code_pos));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_parse_string(&mut self) -> Option<TokenType> {
|
fn try_parse_string(&mut self) -> Option<TokenType> {
|
||||||
|
|
|
||||||
|
|
@ -27,33 +27,25 @@ pub enum TokenType {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Token {
|
pub struct Token {
|
||||||
token_type: TokenType,
|
pub token_type: TokenType,
|
||||||
lexeme: String,
|
// pub lexeme: String,
|
||||||
|
pub code_pos: CodePos,
|
||||||
code_pos: CodePos,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Token {
|
impl Token {
|
||||||
pub fn new(token_type: TokenType, lexeme: String, pos: CodePos) -> Self {
|
pub fn new(token_type: TokenType, pos: CodePos) -> Self {
|
||||||
Token {
|
Token {
|
||||||
token_type,
|
token_type,
|
||||||
lexeme: lexeme,
|
// lexeme,
|
||||||
code_pos: pos,
|
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 {
|
impl std::fmt::Debug for Token {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
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 )* ;
|
equality -> comparison ( ( "==" | "!=" ) comparison )* ;
|
||||||
comparison -> term ( ">" | ">=" | "<" | "<=" term )* ;
|
comparison -> term ( ">" | ">=" | "<" | "<=" term )* ;
|
||||||
term -> factor ( ( "+" | "-" ) factor )*
|
term -> factor ( ( "+" | "-" ) factor )*
|
||||||
factor -> unary ( ( "*" | "/" ) unary )* ;
|
factor -> unary ( ( "*" | "/" ) unary )* ;
|
||||||
unary -> ( "!" | "-" ) unary | primary ;
|
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)]
|
#[derive(Debug)]
|
||||||
pub enum Expr {
|
pub enum Expr {
|
||||||
Literal(Literal),
|
Literal {
|
||||||
Unary(UnaryOp, Box<Expr>),
|
literal: Literal,
|
||||||
Binary(Box<Expr>, BinaryOp, Box<Expr>),
|
},
|
||||||
Grouping(Box<Expr>),
|
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 {
|
impl Expr {
|
||||||
pub fn new_string(s: String) -> Self {
|
pub fn string(s: String) -> Self {
|
||||||
Expr::Literal(Literal::String(s))
|
Expr::Literal {
|
||||||
|
literal: Literal::String(s),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_number(num: f64) -> Self {
|
pub fn number(num: f64) -> Self {
|
||||||
Expr::Literal(Literal::Number(num))
|
Expr::Literal {
|
||||||
|
literal: Literal::Number(num),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_bool(b: bool) -> Self {
|
pub fn bool(b: bool) -> Self {
|
||||||
Expr::Literal(Literal::Bool(b))
|
Expr::Literal {
|
||||||
|
literal: Literal::Bool(b),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_nil() -> Self {
|
pub fn nil() -> Self {
|
||||||
Expr::Literal(Literal::Nil)
|
Expr::Literal { literal: Literal::Nil }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_unary(operator: UnaryOp, expr: Expr) -> Self {
|
pub fn unary(op: UnaryOp, expr: Expr) -> Self {
|
||||||
Expr::Unary(operator, Box::new(expr))
|
let expr = Box::new(expr);
|
||||||
|
Expr::Unary { op, expr }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_binary(left: Expr, operator: BinaryOp, right: Expr) -> Self {
|
pub fn binary(left: Expr, op: BinaryOp, right: Expr) -> Self {
|
||||||
Expr::Binary(Box::new(left), operator, Box::new(right))
|
let left = Box::new(left);
|
||||||
|
let right = Box::new(right);
|
||||||
|
Expr::Binary { left, op, right }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_grouping(expr: Expr) -> Self {
|
pub fn grouping(expr: Expr) -> Self {
|
||||||
Expr::Grouping(Box::new(expr))
|
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 {
|
impl Display for Expr {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Expr::Literal(literal) => write!(f, "{literal}"),
|
Expr::Literal { literal } => write!(f, "{literal}"),
|
||||||
Expr::Unary(op, expr) => write!(f, "({op} {expr})"),
|
Expr::Unary { op, expr } => write!(f, "({op} {expr})"),
|
||||||
Expr::Binary(left, op, right) => write!(f, "({op} {left} {right})"),
|
Expr::Binary { left, op, right } => write!(f, "({op} {left} {right})"),
|
||||||
Expr::Grouping(expr) => write!(f, "(group {expr})"),
|
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;
|
mod expr;
|
||||||
pub mod parser;
|
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 crate::parser::expr::BinaryOp;
|
||||||
|
|
||||||
use super::expr::{Expr, UnaryOp};
|
use super::expr::{Expr, UnaryOp};
|
||||||
|
use super::Stmt;
|
||||||
|
|
||||||
/*====================================================================================================================*/
|
/*====================================================================================================================*/
|
||||||
|
|
||||||
type ParserResult<T> = Result<T, ParserError>;
|
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()
|
Parser::new(tokens).parse()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -39,6 +40,11 @@ impl TokenIter {
|
||||||
|
|
||||||
self.peek_token.as_ref()
|
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 {
|
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 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) {
|
fn synchronise(&mut self) {
|
||||||
loop {
|
loop {
|
||||||
let peek_token = self
|
if self.token_iter.is_empty() {
|
||||||
.peek_token()
|
return;
|
||||||
.unwrap_or_else(|err| panic!("peek_token returned error in synchronise: {err}"));
|
}
|
||||||
|
|
||||||
|
let peek_token = self.peek_token();
|
||||||
|
|
||||||
// if we match a synchronisation point: return
|
// if we match a synchronisation point: return
|
||||||
match peek_token.token_type() {
|
match peek_token.token_type {
|
||||||
TokenType::Class
|
TokenType::Class
|
||||||
| TokenType::Fun
|
| TokenType::Fun
|
||||||
| TokenType::Var
|
| TokenType::Var
|
||||||
|
|
@ -92,23 +118,118 @@ impl Parser {
|
||||||
| TokenType::EOF => return,
|
| TokenType::EOF => return,
|
||||||
TokenType::Semicolon => {
|
TokenType::Semicolon => {
|
||||||
// discard semicolon first, then return
|
// discard semicolon first, then return
|
||||||
let _ = self
|
let _ = self.next_token();
|
||||||
.next_token()
|
|
||||||
.unwrap_or_else(|err| panic!("next_token returned error in synchronise: {err}"));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// no sync point: discard token
|
// no sync point: discard token
|
||||||
let _ = self
|
let _ = self.next_token();
|
||||||
.next_token()
|
|
||||||
.unwrap_or_else(|err| panic!("next_token returned error in synchronise: {err}"));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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> {
|
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> {
|
fn equality(&mut self) -> ParserResult<Expr> {
|
||||||
|
|
@ -116,18 +237,18 @@ impl Parser {
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
// get comparison operator as BinaryOp; otherwise break out of 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::EqualEqual => BinaryOp::Equal,
|
||||||
TokenType::BangEqual => BinaryOp::NotEqual,
|
TokenType::BangEqual => BinaryOp::NotEqual,
|
||||||
_ => break,
|
_ => break,
|
||||||
};
|
};
|
||||||
|
|
||||||
// consume operator token
|
// consume operator token
|
||||||
let _ = self.next_token().unwrap();
|
let _ = self.next_token();
|
||||||
|
|
||||||
let right = self.comparison()?;
|
let right = self.comparison()?;
|
||||||
|
|
||||||
expr = Expr::new_binary(expr, operator, right);
|
expr = Expr::binary(expr, operator, right);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(expr)
|
Ok(expr)
|
||||||
|
|
@ -137,7 +258,7 @@ impl Parser {
|
||||||
let mut expr = self.term()?;
|
let mut expr = self.term()?;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let operator = match self.peek_token()?.token_type() {
|
let operator = match self.peek_token().token_type {
|
||||||
TokenType::Less => BinaryOp::Less,
|
TokenType::Less => BinaryOp::Less,
|
||||||
TokenType::LessEqual => BinaryOp::LessEqual,
|
TokenType::LessEqual => BinaryOp::LessEqual,
|
||||||
TokenType::Greater => BinaryOp::Greater,
|
TokenType::Greater => BinaryOp::Greater,
|
||||||
|
|
@ -146,11 +267,11 @@ impl Parser {
|
||||||
};
|
};
|
||||||
|
|
||||||
// consume operator token
|
// consume operator token
|
||||||
let _ = self.next_token().unwrap();
|
let _ = self.next_token();
|
||||||
|
|
||||||
let right = self.term()?;
|
let right = self.term()?;
|
||||||
|
|
||||||
expr = Expr::new_binary(expr, operator, right);
|
expr = Expr::binary(expr, operator, right);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(expr)
|
Ok(expr)
|
||||||
|
|
@ -160,18 +281,18 @@ impl Parser {
|
||||||
let mut expr = self.factor()?;
|
let mut expr = self.factor()?;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let operator = match self.peek_token()?.token_type() {
|
let operator = match self.peek_token().token_type {
|
||||||
TokenType::Plus => BinaryOp::Add,
|
TokenType::Plus => BinaryOp::Add,
|
||||||
TokenType::Minus => BinaryOp::Subtract,
|
TokenType::Minus => BinaryOp::Subtract,
|
||||||
_ => break,
|
_ => break,
|
||||||
};
|
};
|
||||||
|
|
||||||
// consume operator token
|
// consume operator token
|
||||||
let _ = self.next_token().unwrap();
|
let _ = self.next_token();
|
||||||
|
|
||||||
let right = self.factor()?;
|
let right = self.factor()?;
|
||||||
|
|
||||||
expr = Expr::new_binary(expr, operator, right);
|
expr = Expr::binary(expr, operator, right);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(expr)
|
Ok(expr)
|
||||||
|
|
@ -181,76 +302,120 @@ impl Parser {
|
||||||
let mut expr = self.unary()?;
|
let mut expr = self.unary()?;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let operator = match self.peek_token()?.token_type() {
|
let operator = match self.peek_token().token_type {
|
||||||
TokenType::Star => BinaryOp::Multiply,
|
TokenType::Star => BinaryOp::Multiply,
|
||||||
TokenType::Slash => BinaryOp::Divide,
|
TokenType::Slash => BinaryOp::Divide,
|
||||||
_ => break,
|
_ => break,
|
||||||
};
|
};
|
||||||
|
|
||||||
// consume operator token
|
// consume operator token
|
||||||
let _ = self.next_token().unwrap();
|
let _ = self.next_token();
|
||||||
|
|
||||||
let right = self.unary()?;
|
let right = self.unary()?;
|
||||||
|
|
||||||
expr = Expr::new_binary(expr, operator, right);
|
expr = Expr::binary(expr, operator, right);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(expr)
|
Ok(expr)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unary(&mut self) -> ParserResult<Expr> {
|
fn unary(&mut self) -> ParserResult<Expr> {
|
||||||
match self.peek_token()?.token_type() {
|
match self.peek_token().token_type {
|
||||||
TokenType::Bang => {
|
TokenType::Bang => {
|
||||||
let _ = self.next_token().unwrap();
|
self.next_token();
|
||||||
Ok(Expr::new_unary(UnaryOp::Not, self.unary()?))
|
Ok(Expr::unary(UnaryOp::Not, self.unary()?))
|
||||||
}
|
}
|
||||||
TokenType::Minus => {
|
TokenType::Minus => {
|
||||||
let _ = self.next_token().unwrap();
|
let _ = self.next_token();
|
||||||
Ok(Expr::new_unary(UnaryOp::Negate, self.unary()?))
|
Ok(Expr::unary(UnaryOp::Negate, self.unary()?))
|
||||||
}
|
}
|
||||||
_ => self.primary(),
|
_ => self.primary(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn primary(&mut self) -> ParserResult<Expr> {
|
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() {
|
let token = self.next_token();
|
||||||
TokenType::Number(num) => Ok(Expr::new_number(*num)),
|
|
||||||
TokenType::String(s) => Ok(Expr::new_string(s.clone())),
|
match token.token_type {
|
||||||
TokenType::False => Ok(Expr::new_bool(false)),
|
TokenType::Number(num) => Ok(Expr::number(num)),
|
||||||
TokenType::True => Ok(Expr::new_bool(true)),
|
TokenType::String(s) => Ok(Expr::string(s)),
|
||||||
TokenType::Nil => Ok(Expr::new_nil()),
|
TokenType::False => Ok(Expr::bool(false)),
|
||||||
|
TokenType::True => Ok(Expr::bool(true)),
|
||||||
|
TokenType::Nil => Ok(Expr::nil()),
|
||||||
TokenType::LeftParen => {
|
TokenType::LeftParen => {
|
||||||
let expr = self.expression()?;
|
let expr = self.expression()?;
|
||||||
self.consume_token(TokenType::RightParen)?;
|
// self.consume_token(TokenType::RightParen)?;
|
||||||
Ok(Expr::new_grouping(expr))
|
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 }),
|
_ => Err(ParserError::UnexpectedToken { token }),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn next_token(&mut self) -> ParserResult<Token> {
|
fn semicolon(&mut self) -> ParserResult<()> {
|
||||||
// let next = self.token_iter.next();
|
/* 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:?}");
|
// println!("Next token: {next:?}");
|
||||||
|
|
||||||
// next.ok_or(ParserError::TokenStreamEnded)
|
if let Some(ref token) = next {
|
||||||
|
if token.token_type == TokenType::EOF {
|
||||||
self.token_iter.next().ok_or(ParserError::TokenStreamEnded)
|
panic!("Someone ate a EOF token");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn peek_token(&mut self) -> ParserResult<&Token> {
|
next.ok_or(ParserError::TokenStreamEnded) */
|
||||||
self.token_iter.peek().ok_or(ParserError::TokenStreamEnded)
|
|
||||||
|
self.token_iter.next().unwrap() // .ok_or(ParserError::TokenStreamEnded)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn consume_token(&mut self, token_type: TokenType) -> ParserResult<Token> {
|
fn peek_token(&mut self) -> &Token {
|
||||||
self.next_token().and_then(|token| {
|
self.token_iter.peek().unwrap() // .ok_or(ParserError::TokenStreamEnded)
|
||||||
if token.token_type() == &token_type {
|
}
|
||||||
Ok(token)
|
|
||||||
|
/* 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 {
|
} else {
|
||||||
Err(ParserError::UnexpectedToken { token })
|
// 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