finished resolver (chapter 11) and started classes (chapter 12)

This commit is contained in:
Moritz Gmeiner 2023-01-28 01:11:55 +01:00
commit 10540708d4
34 changed files with 1448 additions and 438 deletions

View file

@ -1,125 +0,0 @@
use thiserror::Error;
use crate::interpreter::Value;
use crate::lexer::Token;
use crate::misc::CodePos;
use crate::parser::{BinaryOp, Expr, UnaryOp};
#[derive(Error, Debug)]
pub enum LexerError {
#[error("Unexpected character '{c}' at {code_pos}.")]
UnexpectedCharacter { c: char, code_pos: CodePos },
#[error("Unterminated string literal starting at {code_pos}.")]
UnterminatedStringLiteral { code_pos: CodePos },
#[error("Unterminated block comment starting at {code_pos}.")]
UnterminatedBlockComment { code_pos: CodePos },
#[error("Invalid number literal {lexeme} at {code_pos}: {msg}")]
InvalidNumberLiteral {
lexeme: String,
msg: String,
code_pos: CodePos,
},
}
#[derive(Error, Debug)]
pub enum ParserError {
#[error("Token stream ended unexpectedly.")]
TokenStreamEnded,
#[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)]
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}")]
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)]
pub enum RuntimeError {
#[error("Unary operator {op} had invalid argument {arg}")]
UnaryOpInvalidArgument { op: UnaryOp, arg: Value },
#[error("Binary operator {op} had invalid arguments {left} and {right}")]
BinaryOpInvalidArguments { left: Value, op: BinaryOp, right: Value },
#[error("Division by zero")]
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_multiple_errors(inner))]
LexerError { inner: Vec<LexerError> },
#[error("{0}", format_multiple_errors(inner))]
ParserError { inner: Vec<ParserError> },
#[error("{inner}")]
RuntimeError { inner: RuntimeError },
#[error("Called exit() with exit code {exit_code}")]
Exit { exit_code: i32 },
}
fn format_multiple_errors(errs: &Vec<impl std::error::Error>) -> String {
let msg = if errs.len() == 1 {
format!("{}", errs[0])
} else {
let msgs: Vec<String> = errs.iter().map(|err| format!("{}", err)).collect();
msgs.join("\n")
};
msg
}
impl From<Vec<LexerError>> for LoxError {
fn from(lexer_errs: Vec<LexerError>) -> Self {
LoxError::LexerError { inner: lexer_errs }
}
}
impl From<Vec<ParserError>> for LoxError {
fn from(parser_errs: Vec<ParserError>) -> Self {
LoxError::ParserError { inner: parser_errs }
}
}
impl From<RuntimeError> for LoxError {
fn from(runtime_err: RuntimeError) -> Self {
match runtime_err {
RuntimeError::Exit { exit_code } => LoxError::Exit { exit_code },
_ => LoxError::RuntimeError { inner: runtime_err },
}
}
}

View file

@ -1,146 +0,0 @@
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;
pub type Scope = HashMap<String, HeapValue>;
pub struct Environment {
globals: Scope,
// scopes: Vec<Scope>,
frames: Vec<Frame>,
}
impl Environment {
pub fn new() -> Self {
let mut env = Environment {
globals: HashMap::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<Value, RuntimeError> {
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 define(&mut self, name: impl Into<String> + Borrow<str>, 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> {
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.replace(value);
Ok(())
} else {
Err(RuntimeError::NameNotDefined { name: name.to_owned() })
}
}
pub fn enter_scope(&mut self) {
self.current_frame_mut().enter_scope();
}
pub fn exit_scope(&mut self) {
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<Scope>,
}
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<Value> {
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<String> + Borrow<str>, 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)
}
}

View file

@ -1,104 +0,0 @@
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<String>,
body: Box<Stmt>,
}
impl LoxFunction {
pub fn new(name: impl Into<String>, param_names: Vec<String>, 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<Item = &String> {
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, "<fun {}>", self.name)
}
}
/*====================================================================================================================*/
pub type ExternFunClosure = fn(Vec<Value>, &mut Environment) -> EvalResult<Value>;
#[derive(Clone)]
pub struct LoxExternFunction {
name: String,
closure: ExternFunClosure,
}
impl LoxExternFunction {
pub fn new(name: impl Into<String>, 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<Value>, env: &mut Environment) -> EvalResult<Value> {
(self.closure)(args, env)
}
}
impl Display for LoxExternFunction {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "<extern fun {}>", 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", &"<closure>")
.finish()
}
}
impl PartialEq for LoxExternFunction {
fn eq(&self, other: &Self) -> bool {
self.name == other.name
}
}

View file

@ -1,232 +0,0 @@
use std::rc::Rc;
use crate::error::RuntimeError;
use crate::parser::{BinaryOp, Expr, Literal, LogicalOp, Stmt, UnaryOp};
use super::environment::Environment;
use super::{LoxFunction, Value};
pub type EvalResult<T> = Result<T, RuntimeError>;
/*====================================================================================================================*/
pub fn execute(statement: Stmt, env: &mut Environment) -> Result<(), RuntimeError> {
statement.execute(env)
}
/*====================================================================================================================*/
/* trait Eval {
fn eval(self, env: &mut Environment) -> EvalResult;
} */
impl Literal {
fn eval(&self, env: &mut Environment) -> EvalResult<Value> {
let _ = env;
match self {
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<Value> {
match self {
Expr::Literal { literal } => literal.eval(env),
Expr::Unary { op, expr } => {
let arg = expr.eval(env)?;
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())),
(op, arg) => Err(RuntimeError::UnaryOpInvalidArgument { op, arg }),
}
}
Expr::Binary { left, op, right } => {
use Value::{Bool, Number, String};
let left = left.eval(env)?;
let right = right.eval(env)?;
match (left, *op, right) {
(Number(left), BinaryOp::Add, Number(right)) => Ok(Number(left + right)),
(Number(left), BinaryOp::Subtract, Number(right)) => Ok(Number(left - right)),
(Number(left), BinaryOp::Multiply, Number(right)) => Ok(Number(left * right)),
(Number(left), BinaryOp::Divide, Number(right)) => {
if right == 0.0 {
return Err(RuntimeError::DivisionByZero);
}
Ok(Number(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)),
(Number(left), BinaryOp::Less, Number(right)) => Ok(Bool(left < right)),
(Number(left), BinaryOp::LessEqual, Number(right)) => Ok(Bool(left <= right)),
(Number(left), BinaryOp::Greater, Number(right)) => Ok(Bool(left > right)),
(Number(left), BinaryOp::GreaterEqual, Number(right)) => Ok(Bool(left >= right)),
(String(left), BinaryOp::Less, String(right)) => Ok(Bool(left < right)),
(String(left), BinaryOp::LessEqual, String(right)) => Ok(Bool(left <= right)),
(String(left), BinaryOp::Greater, String(right)) => Ok(Bool(left > right)),
(String(left), BinaryOp::GreaterEqual, String(right)) => Ok(Bool(left >= right)),
(left, op, right) => Err(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),
Expr::Assignment { name, value } => {
let value = value.eval(env)?;
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::<EvalResult<Vec<Value>>>()?;
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 execute(&self, env: &mut Environment) -> EvalResult<()> {
match self {
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::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.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.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<Value>, env: &mut Environment) -> EvalResult<Value> {
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)
}
}

View file

@ -1,99 +0,0 @@
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)
}

View file

@ -1,10 +0,0 @@
mod environment;
mod function;
mod interpret;
mod lox_std;
mod run;
mod value;
pub use function::LoxFunction;
pub use run::interpreter_main;
pub use value::Value;

View file

@ -1,127 +0,0 @@
use std::io::Write;
use crate::error::LoxError;
use crate::interpreter::interpret::execute;
use crate::lexer::{scan_tokens, Token};
use crate::parser::parse_tokens;
use super::environment::Environment;
pub fn interpreter_main() {
let args: Vec<String> = std::env::args().collect();
match args.len() {
1 => run_repl(),
2 => run_file(&args[1]),
_ => {
eprintln!("Usage: rlox [script]");
std::process::exit(64);
}
}
}
fn run_file(script_path: &str) {
let source_code = std::fs::read_to_string(script_path).unwrap_or_else(|err| {
eprintln!("Reading script file {} failed: {}", script_path, err);
std::process::exit(66);
});
/* if let Err(err) = run(&source_code) {
eprintln!("{}", err);
std::process::exit(65);
} */
let mut env = Environment::new();
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),
}
}
}
}
fn run_repl() {
let stdin = std::io::stdin();
let mut env = Environment::new();
loop {
let mut input_buf = String::new();
print!("> ");
std::io::stdout().flush().unwrap();
'inner: loop {
stdin.read_line(&mut input_buf).unwrap_or_else(|err| {
eprintln!("Could not read from stdin: {}", err);
std::process::exit(66);
});
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);
// all braces/parens/brackets closed => break
if num_open_braces == 0 && num_open_parens == 0 && num_open_brackets == 0 {
break 'inner;
}
// 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;
}
print!("< ");
std::io::stdout().flush().unwrap();
}
let input_buf = input_buf.trim();
if input_buf.is_empty() || input_buf == "exit" || input_buf == "quit" {
std::process::exit(0);
}
match run(input_buf, &mut env) {
Ok(()) => {}
Err(LoxError::Exit { exit_code }) => std::process::exit(exit_code),
Err(err) => eprintln!("{}", err),
}
}
}
fn run(code_string: &str, env: &mut Environment) -> Result<(), LoxError> {
let tokens: Vec<Token> = scan_tokens(code_string)?;
/* let token_str = tokens
.iter()
.map(|token| format!("{token}"))
.collect::<Vec<String>>()
.join(" ");
println!("{token_str}"); */
let statements = parse_tokens(tokens)?;
/* for statement in statements.iter() {
println!("{statement}");
} */
// let mut result = Value::Nil;
for statement in statements {
execute(statement, env)?;
}
/* match result {
Value::Nil => {}
result => println!("{result}"),
} */
Ok(())
}

View file

@ -1,106 +0,0 @@
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<LoxFunction>),
ExternFunction(Rc<LoxExternFunction>),
String(Rc<String>),
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<String>) -> 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<UnsafeCell<Value>>,
}
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())
}
}

View file

@ -1,316 +0,0 @@
use phf::phf_map;
use crate::error::LexerError;
use crate::misc::CodePos;
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::False,
"for" => TokenType::For,
"fun" => TokenType::Fun,
"if" => TokenType::If,
"nil" => TokenType::Nil,
"or" => TokenType::Or,
"print" => TokenType::Print,
"return" => TokenType::Return,
"super" => TokenType::Super,
"this" => TokenType::This,
"true" => TokenType::True,
"var" => TokenType::Var,
"while" => TokenType::While
};
/*====================================================================================================================*/
pub fn scan_tokens(source_code: &str) -> Result<Vec<Token>, Vec<LexerError>> {
let lexer = Lexer::new(source_code);
lexer.scan_tokens()
}
/*====================================================================================================================*/
#[derive(Debug)]
struct Lexer {
source: Vec<char>,
tokens: Vec<Token>,
start: usize,
current: usize,
code_pos: CodePos,
errors: Vec<LexerError>,
}
impl Lexer {
fn new(source_code: &str) -> Self {
let source = source_code.chars().collect();
Lexer {
source,
tokens: Vec::new(),
start: 0,
current: 0,
code_pos: CodePos::default(),
errors: Vec::new(),
}
}
fn scan_tokens(self) -> Result<Vec<Token>, Vec<LexerError>> {
let mut me = self;
while !me.source_is_empty() {
me.scan_token();
}
me.tokens.push(Token::new(TokenType::EOF, me.code_pos));
if me.errors.is_empty() {
Ok(me.tokens)
} else {
Err(me.errors)
}
}
fn scan_token(&mut self) {
use TokenType::*;
self.start = self.current;
let c = self.advance();
let token_type = match c {
'(' => Some(LeftParen),
')' => Some(RightParen),
'{' => Some(LeftBrace),
'}' => Some(RightBrace),
',' => Some(Comma),
'.' => Some(Dot),
'+' => Some(Plus),
'-' => Some(Minus),
';' => Some(Semicolon),
'*' => Some(Star),
'!' => {
if self.consume('=') {
Some(BangEqual)
} else {
Some(Bang)
}
}
'=' => {
if self.consume('=') {
Some(EqualEqual)
} else {
Some(Equal)
}
}
'<' => {
if self.consume('=') {
Some(LessEqual)
} else {
Some(Less)
}
}
'>' => {
if self.consume('=') {
Some(GreaterEqual)
} else {
Some(Greater)
}
}
'/' => {
if self.consume('/') {
// line comment
// advance until either source is empty or newline if found
while !self.source_is_empty() && self.advance() != '\n' {}
None
} else if self.consume('*') {
// block comment
let mut depth = 1;
loop {
if depth == 0 {
break;
}
if self.source_is_empty() {
self.errors.push(LexerError::UnterminatedBlockComment {
code_pos: self.code_pos,
});
break;
}
if self.peek() == Some('/') && self.peek_two() == Some('*') {
// nested block comment
// consume '/' and '*'
self.advance();
self.advance();
depth += 1;
continue;
}
if self.peek() == Some('*') && self.peek_two() == Some('/') {
// consume '*' and '/'
self.advance();
self.advance();
depth -= 1;
continue;
}
self.advance();
}
None
} else {
Some(Slash)
}
}
'"' => self.try_parse_string(),
'0'..='9' => self.try_parse_number(),
' ' | '\r' | '\n' | '\t' => None, // handled automatically in advance()
c @ '_' | c if c.is_ascii_alphabetic() => self.try_parse_identifier(),
_ => {
self.errors.push(LexerError::UnexpectedCharacter {
c,
code_pos: self.code_pos,
});
None
}
};
if let Some(token_type) = token_type {
self.push_token(token_type);
}
}
fn source_is_empty(&self) -> bool {
self.current >= self.source.len()
}
fn advance(&mut self) -> char {
assert!(!self.source_is_empty());
let c = self.source[self.current];
self.current += 1;
self.code_pos.col += 1;
if c == '\t' {
self.code_pos.col += 3;
} else if c == '\n' {
self.code_pos.col = 0;
self.code_pos.line += 1;
}
c
}
fn peek(&self) -> Option<char> {
self.source.get(self.current).copied()
}
fn peek_two(&self) -> Option<char> {
self.source.get(self.current + 1).copied()
}
fn consume(&mut self, c: char) -> bool {
if self.peek() == Some(c) {
self.advance();
true
} else {
false
}
}
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, self.code_pos));
}
fn try_parse_string(&mut self) -> Option<TokenType> {
// advance until second "
while self.advance() != '"' {
if self.source_is_empty() {
self.errors.push(LexerError::UnterminatedStringLiteral {
code_pos: self.code_pos,
});
return None;
}
}
let string_literal = self.source[self.start + 1..self.current - 1].iter().collect();
Some(TokenType::String(string_literal))
}
fn try_parse_number(&mut self) -> Option<TokenType> {
let is_some_digit = |c: Option<char>| c.map_or(false, |c| c.is_ascii_digit());
// eat all digits
while is_some_digit(self.peek()) {
self.advance();
}
// consume separator dot and continue eating digits
if self.peek() == Some('.') && is_some_digit(self.peek_two()) {
// consume the '.'
self.advance();
while is_some_digit(self.peek()) {
self.advance();
}
}
// consume exponential e and continue eating digits
if self.peek() == Some('e') && is_some_digit(self.peek_two()) {
// consume the 'e'
self.advance();
while is_some_digit(self.peek()) {
self.advance();
}
}
let lexeme: String = self.source[self.start..self.current].iter().collect();
let num: f64 = match lexeme.parse() {
Ok(num) => num,
Err(err) => {
self.errors.push(LexerError::InvalidNumberLiteral {
lexeme,
msg: format!("{err}"),
code_pos: self.code_pos,
});
return None;
}
};
Some(TokenType::Number(num))
}
fn try_parse_identifier(&mut self) -> Option<TokenType> {
let is_alpha_num_underscore =
|c: Option<char>| c.map_or(false, |c| matches!(c, '0'..='9' | 'A'..='Z' | '_' | 'a'..='z'));
while is_alpha_num_underscore(self.peek()) {
self.advance();
}
let lexeme: String = self.source[self.start..self.current].iter().collect();
let token_type = KEYWORDS.get(&lexeme).cloned().unwrap_or(TokenType::Identifier(lexeme));
Some(token_type)
}
}

View file

@ -1,5 +0,0 @@
mod _lexer;
mod token;
pub use _lexer::scan_tokens;
pub use token::{Token, TokenType};

View file

@ -1,57 +0,0 @@
use crate::misc::CodePos;
#[allow(dead_code, clippy::upper_case_acronyms)]
#[derive(Debug, Clone, PartialEq)]
#[rustfmt::skip]
pub enum TokenType {
// Single-character tokens
LeftParen, RightParen, LeftBrace, RightBrace,
Comma, Dot, Minus, Plus, Semicolon, Slash, Star,
// One or two character tokens
Bang, BangEqual,
Equal, EqualEqual,
Greater, GreaterEqual,
Less, LessEqual,
// Literals
Identifier(String),
String(String),
Number(f64),
// Keywords
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,
pub code_pos: CodePos,
}
impl Token {
pub fn new(token_type: TokenType, pos: CodePos) -> Self {
Token {
token_type,
// lexeme,
code_pos: pos,
}
}
}
impl std::fmt::Debug for Token {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "<{:?}>", self.token_type)
// write!(f, "<{:?}> (\"{}\")", self.token_type, self.lexeme)
}
}
impl std::fmt::Display for Token {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "<{:?}>", self.token_type)
}
}

View file

@ -1,9 +1,31 @@
mod error;
mod interpreter;
mod lexer;
mod misc;
mod parser;
use clap::{ArgAction, Parser};
use rlox2_interpreter::{run_file, run_repl, Runtime};
#[derive(Parser, Debug)]
struct CliArgs {
#[arg()]
file_name: Option<String>,
#[arg(short, action = ArgAction::SetTrue)]
interactive: bool,
}
fn main() {
interpreter::interpreter_main();
interpreter_main();
}
pub fn interpreter_main() {
let cli_args = CliArgs::parse();
let mut runtime = Runtime::default();
if let Some(file_name) = cli_args.file_name {
run_file(&mut runtime, &file_name);
if cli_args.interactive {
run_repl(&mut runtime);
}
} else {
run_repl(&mut runtime);
}
}

View file

@ -1,34 +0,0 @@
use std::fmt::{Debug, Display};
#[derive(Copy, Clone)]
pub struct CodePos {
pub line: u32,
pub col: u32,
}
impl Default for CodePos {
fn default() -> Self {
Self { line: 1, col: 0 }
}
}
impl Display for CodePos {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "line {}, col {}", self.line, self.col)
}
}
impl Debug for CodePos {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}:{}", self.line, self.col)
}
}
/*====================================================================================================================*/
pub fn indent(s: String) -> String {
s.split('\n')
.map(|line| format!("\t{line}"))
.collect::<Vec<String>>()
.join("\n")
}

View file

@ -1,44 +0,0 @@
program -> statement* EOF ;
statement -> if_stmt
| print_stmt
| while_stmt
| for_stmt
| declaration
| block
| expr_stmt
| break
| return_stmt ;
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 | 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 | 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 )* )? ;

View file

@ -1,699 +0,0 @@
use std::vec::IntoIter;
use crate::error::ParserError;
use crate::lexer::{Token, TokenType};
use crate::parser::expr::BinaryOp;
use super::expr::{Expr, UnaryOp};
use super::{LogicalOp, Stmt};
/*====================================================================================================================*/
type ParserResult<T> = Result<T, ParserError>;
pub fn parse_tokens(tokens: Vec<Token>) -> Result<Vec<Stmt>, Vec<ParserError>> {
Parser::new(tokens).parse()
}
/*====================================================================================================================*/
// takes care of token iteration
struct TokenIter {
token_iter: IntoIter<Token>,
peek_token: Option<Token>,
}
impl TokenIter {
pub fn new(tokens: Vec<Token>) -> Self {
TokenIter {
token_iter: tokens.into_iter(),
peek_token: None,
}
}
fn peek(&mut self) -> Option<&Token> {
// if peek_token is empty: fill with next token from token_iter
if self.peek_token.is_none() && self.token_iter.len() != 0 {
self.peek_token = self.token_iter.next();
}
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 {
type Item = Token;
fn next(&mut self) -> Option<Self::Item> {
// return the peeked token if any, or else next token from token_iter
self.peek_token.take().or_else(|| self.token_iter.next())
}
}
/*====================================================================================================================*/
struct Parser {
token_iter: TokenIter,
parse_errors: Vec<ParserError>,
}
impl Parser {
pub fn new(tokens: Vec<Token>) -> Self {
Parser {
token_iter: TokenIter::new(tokens),
parse_errors: Vec::new(),
}
}
pub fn parse(self) -> Result<Vec<Stmt>, Vec<ParserError>> {
let mut me = self;
let mut statements = 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()?);
match me.statement() {
Ok(stmt) => {
statements.push(stmt);
}
Err(err) => {
me.parse_errors.push(err);
// println!("Synchronising...");
me.synchronise();
// println!("Synchronised")
}
}
}
// me.consume_token(TokenType::EOF).unwrap();
if !me.parse_errors.is_empty() {
Err(me.parse_errors)
} else {
Ok(statements)
}
}
fn synchronise(&mut self) {
loop {
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 {
TokenType::Class
| TokenType::Fun
| TokenType::Var
| TokenType::For
| TokenType::If
| TokenType::While
| TokenType::Print
| TokenType::Return
| TokenType::EOF => return,
TokenType::Semicolon => {
// discard semicolon first, then return
let _ = self.next_token();
return;
}
_ => {}
}
// no sync point: discard token
let _ = self.next_token();
// println!("Discarding {} token", self.next_token());
}
}
fn statement(&mut self) -> ParserResult<Stmt> {
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<Stmt> {
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<Stmt> {
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<Stmt> {
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<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 fun_declaration(&mut self) -> ParserResult<Stmt> {
assert_eq!(self.next_token().token_type, TokenType::Fun);
let name = self.identifier("Missing function name")?;
let fun = self.fun_params_and_body(name.clone())?;
Ok(Stmt::var_decl(name, fun))
}
fn fun_params_and_body(&mut self, name: impl Into<String>) -> ParserResult<Expr> {
// <Fun> token has already been eaten by primary or fun_declaration
// 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()?;
let name = name.into();
Ok(Expr::function(name, param_names, body))
}
fn collect_params(&mut self) -> ParserResult<Vec<String>> {
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<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.assignment()
}
fn assignment(&mut self) -> ParserResult<Expr> {
let code_pos = self.peek_token().code_pos;
let expr = self.logical_or()?;
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 logical_or(&mut self) -> ParserResult<Expr> {
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<Expr> {
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<Expr> {
let mut expr = self.comparison()?;
loop {
// get comparison operator as BinaryOp; otherwise break out of loop
let operator = match self.peek_token().token_type {
TokenType::EqualEqual => BinaryOp::Equal,
TokenType::BangEqual => BinaryOp::NotEqual,
_ => break,
};
// consume operator token
let _ = self.next_token();
let right = self.comparison()?;
expr = Expr::binary(expr, operator, right);
}
Ok(expr)
}
fn comparison(&mut self) -> ParserResult<Expr> {
let mut expr = self.term()?;
loop {
let operator = match self.peek_token().token_type {
TokenType::Less => BinaryOp::Less,
TokenType::LessEqual => BinaryOp::LessEqual,
TokenType::Greater => BinaryOp::Greater,
TokenType::GreaterEqual => BinaryOp::GreaterEqual,
_ => break,
};
// consume operator token
let _ = self.next_token();
let right = self.term()?;
expr = Expr::binary(expr, operator, right);
}
Ok(expr)
}
fn term(&mut self) -> ParserResult<Expr> {
let mut expr = self.factor()?;
loop {
let operator = match self.peek_token().token_type {
TokenType::Plus => BinaryOp::Add,
TokenType::Minus => BinaryOp::Subtract,
_ => break,
};
// consume operator token
let _ = self.next_token();
let right = self.factor()?;
expr = Expr::binary(expr, operator, right);
}
Ok(expr)
}
fn factor(&mut self) -> ParserResult<Expr> {
let mut expr = self.unary()?;
loop {
let operator = match self.peek_token().token_type {
TokenType::Star => BinaryOp::Multiply,
TokenType::Slash => BinaryOp::Divide,
_ => break,
};
// consume operator token
let _ = self.next_token();
let right = self.unary()?;
expr = Expr::binary(expr, operator, right);
}
Ok(expr)
}
fn unary(&mut self) -> ParserResult<Expr> {
match self.peek_token().token_type {
TokenType::Bang => {
self.next_token();
Ok(Expr::unary(UnaryOp::Not, self.unary()?))
}
TokenType::Minus => {
let _ = self.next_token();
Ok(Expr::unary(UnaryOp::Negate, self.unary()?))
}
_ => self.call(),
}
}
fn call(&mut self) -> ParserResult<Expr> {
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<Vec<Expr>> {
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<Expr> {
if self.peek_token().token_type == TokenType::EOF {
return Err(ParserError::TokenStreamEnded);
}
let token = self.next_token();
match token.token_type {
TokenType::Fun => Ok(self.fun_params_and_body("<lambda>")?),
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, |token| ParserError::MissingRightParen {
code_pos: token.code_pos,
})?;
Ok(Expr::grouping(expr))
}
TokenType::Identifier(name) => Ok(Expr::Variable { name }),
_ => Err(ParserError::ExpectedPrimary { token }),
}
}
fn semicolon(&mut self) -> ParserResult<()> {
self.consume_token(TokenType::Semicolon, |token| ParserError::MissingSemicolon {
code_pos: token.code_pos,
})
}
fn identifier(&mut self, msg: &str) -> ParserResult<String> {
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,
}),
}
}
fn next_token(&mut self) -> Token {
/* let token = self.token_iter.next().unwrap();
// println!("Next token: {next:?}");
if token.token_type == TokenType::EOF {
panic!("Someone ate a EOF token");
}
// if token.token_type == TokenType::Print {
// panic!("Found the print");
// }
token */
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<F>(&mut self, token_type: TokenType, err_fn: F) -> ParserResult<()>
where
F: Fn(Token) -> ParserError,
{
/* let token = self.next_token();
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())),
}
}
}

View file

@ -1,228 +0,0 @@
use std::fmt::Display;
use std::rc::Rc;
use super::Stmt;
#[derive(Debug, Clone)]
pub enum Expr {
Literal {
literal: Literal,
},
Unary {
op: UnaryOp,
expr: Box<Expr>,
},
Binary {
left: Box<Expr>,
op: BinaryOp,
right: Box<Expr>,
},
Logical {
left: Box<Expr>,
op: LogicalOp,
right: Box<Expr>,
},
Grouping {
expr: Box<Expr>,
},
Variable {
name: String,
},
Assignment {
name: String,
value: Box<Expr>,
},
Call {
callee: Box<Expr>,
args: Vec<Expr>,
},
Function {
name: String,
param_names: Vec<String>,
body: Box<Stmt>,
},
}
impl Expr {
pub fn string(s: impl Into<String>) -> Self {
let s = s.into();
Expr::Literal {
literal: Literal::String(Rc::new(s)),
}
}
pub fn number(num: f64) -> Self {
Expr::Literal {
literal: Literal::Number(num),
}
}
pub fn bool(b: bool) -> Self {
Expr::Literal {
literal: Literal::Bool(b),
}
}
pub fn nil() -> Self {
Expr::Literal { literal: Literal::Nil }
}
pub fn unary(op: UnaryOp, expr: Expr) -> Self {
let expr = Box::new(expr);
Expr::Unary { op, expr }
}
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 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: impl Into<String>, value: Expr) -> Self {
let name = name.into();
let value = Box::new(value);
Expr::Assignment { name, value }
}
pub fn call(callee: Expr, args: Vec<Expr>) -> Self {
let callee = Box::new(callee);
Expr::Call { callee, args }
}
pub fn function(name: String, param_names: Vec<String>, body: Stmt) -> Self {
let body = Box::new(body);
Self::Function {
name,
param_names,
body,
}
}
}
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::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::<Vec<String>>()
.join(" ")
),
Expr::Function {
name,
param_names,
body,
} => {
write!(f, "fun {name}({}) {body}", param_names.join(", "))
}
}
}
}
/*====================================================================================================================*/
#[derive(Debug, Clone)]
pub enum Literal {
String(Rc<String>),
Number(f64),
Bool(bool),
Nil,
}
impl Display for Literal {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Literal::String(s) => write!(f, "\"{s}\""),
Literal::Number(num) => write!(f, "{num}"),
Literal::Bool(b) => write!(f, "{b}"),
Literal::Nil => write!(f, "nil"),
}
}
}
/*====================================================================================================================*/
#[derive(Debug, Clone, Copy)]
pub enum UnaryOp {
Negate,
Not,
}
impl Display for UnaryOp {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
UnaryOp::Negate => write!(f, "-"),
UnaryOp::Not => write!(f, "!"),
}
}
}
/*====================================================================================================================*/
#[derive(Debug, Clone, Copy)]
#[rustfmt::skip]
pub enum BinaryOp {
// arithmetic
Add, Subtract, Multiply, Divide,
// equality
Equal, NotEqual,
// comparison
Less, LessEqual,
Greater, GreaterEqual,
}
impl Display for BinaryOp {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
BinaryOp::Add => write!(f, "+"),
BinaryOp::Subtract => write!(f, "-"),
BinaryOp::Multiply => write!(f, "*"),
BinaryOp::Divide => write!(f, "/"),
BinaryOp::Equal => write!(f, "=="),
BinaryOp::NotEqual => write!(f, "!="),
BinaryOp::Less => write!(f, "<"),
BinaryOp::LessEqual => write!(f, "<="),
BinaryOp::Greater => write!(f, ">"),
BinaryOp::GreaterEqual => write!(f, ">="),
}
}
}
#[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"),
}
}
}

View file

@ -1,7 +0,0 @@
mod _parser;
mod expr;
mod stmt;
pub use _parser::parse_tokens;
pub use expr::{BinaryOp, Expr, Literal, LogicalOp, UnaryOp};
pub use stmt::Stmt;

View file

@ -1,129 +0,0 @@
use std::fmt::Display;
use crate::misc::indent;
use super::Expr;
#[derive(Debug, Clone)]
pub enum Stmt {
Print {
expr: Box<Expr>,
},
IfStmt {
condition: Box<Expr>,
then_branch: Box<Stmt>,
else_branch: Option<Box<Stmt>>,
},
While {
condition: Box<Expr>,
body: Box<Stmt>,
},
VarDecl {
name: String,
initializer: Box<Expr>,
},
Block {
statements: Vec<Stmt>,
},
ExprStmt {
expr: Box<Expr>,
},
Break,
Return {
expr: Box<Expr>,
},
}
impl Stmt {
pub fn print_stmt(expr: Expr) -> Self {
let expr = Box::new(expr);
Stmt::Print { expr }
}
pub fn if_stmt(
condition: impl Into<Box<Expr>>,
then_branch: impl Into<Box<Stmt>>,
else_branch: Option<impl Into<Box<Stmt>>>,
) -> 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<Box<Expr>>, body: impl Into<Box<Stmt>>) -> Self {
let condition = condition.into();
let body = body.into();
Stmt::While { condition, body }
}
pub fn var_decl(name: impl Into<String>, initializer: impl Into<Box<Expr>>) -> Self {
let name = name.into();
let initializer = initializer.into();
Stmt::VarDecl { name, initializer }
}
pub fn expr_stmt(expr: impl Into<Box<Expr>>) -> Self {
let expr = expr.into();
Stmt::ExprStmt { expr }
}
pub fn return_stmt(expr: impl Into<Box<Expr>>) -> 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::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 {
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};")
}
}
}
}