remove java implementation, was merely a book copy

This commit is contained in:
Sebastian Hugentobler 2025-05-26 10:29:54 +02:00
parent 175f672dd6
commit c869360aaa
Signed by: shu
SSH key fingerprint: SHA256:ppcx6MlixdNZd5EUM1nkHOKoyQYoJwzuQKXM6J/t66M
63 changed files with 4 additions and 2501 deletions

39
src/callable.rs Normal file
View file

@ -0,0 +1,39 @@
use crate::{interpreter::Interpreter, value::Value};
use core::fmt::Debug;
use std::fmt::Display;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum CallingError {
#[error("Failed calling: {0}.")]
CallFailed(String),
#[error("Expected {0} arguments but got {1}.")]
ArgumentMismatch(usize, usize),
#[error("Failed calling: {0}.")]
CallInterpretationFailed(String),
}
/// Implementations can be called.
pub trait Callable {
fn name(&self) -> String;
fn arity(&self) -> usize;
fn call(&self, interpreter: &mut Interpreter, args: Vec<Value>) -> Result<Value, CallingError>;
}
impl Debug for dyn Callable {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self)
}
}
impl Display for dyn Callable {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.name())
}
}
impl PartialEq for dyn Callable {
fn eq(&self, other: &Self) -> bool {
self.name() == other.name() && self.arity() == other.arity()
}
}

75
src/class.rs Normal file
View file

@ -0,0 +1,75 @@
use std::{collections::HashMap, fmt::Display};
use crate::{
callable::{Callable, CallingError},
function::Function,
instance::Instance,
value::Value,
};
#[derive(Debug, Clone, PartialEq)]
pub struct Class {
pub name: String,
pub superclass: Option<Box<Class>>,
pub methods: HashMap<String, Function>,
}
impl Display for Class {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.name)
}
}
impl Class {
pub fn new(
name: String,
superclass: Option<Class>,
methods: HashMap<String, Function>,
) -> Self {
let superclass = superclass.map(|x| Box::new(x));
Self {
name,
superclass,
methods,
}
}
pub fn find_method(&self, name: &str) -> Option<&Function> {
self.methods.get(name).or_else(|| {
if let Some(superclass) = &self.superclass {
superclass.find_method(name)
} else {
None
}
})
}
}
impl Callable for Class {
fn name(&self) -> String {
self.name.clone()
}
fn arity(&self) -> usize {
if let Some(initializer) = self.find_method("init") {
initializer.arity()
} else {
0
}
}
fn call(
&self,
interpreter: &mut crate::interpreter::Interpreter,
args: Vec<crate::value::Value>,
) -> Result<Value, CallingError> {
let class = self.clone();
let instance = Instance::new(class);
if let Some(initializer) = self.find_method("init") {
initializer.bind(instance.clone()).call(interpreter, args)?;
};
Ok(Value::Instance(instance))
}
}

29
src/cli.rs Normal file
View file

@ -0,0 +1,29 @@
//! Cli interface.
use std::path::PathBuf;
use clap::{Args, Parser, Subcommand};
/// Interpreter for the Lox language.
#[derive(Parser)]
#[command(version, about, long_about = None)]
#[command(propagate_version = true)]
pub struct Cli {
#[command(subcommand)]
pub command: Commands,
}
#[derive(Args, Clone)]
pub struct RunConfig {
/// Path to Lox source file.
#[arg(short, long)]
pub source: PathBuf,
}
#[derive(Subcommand)]
pub enum Commands {
/// Run a Lox source file.
Run(RunConfig),
/// Run a Lox REPL.
Repl,
}

106
src/environment.rs Normal file
View file

@ -0,0 +1,106 @@
use std::{cell::RefCell, collections::HashMap, rc::Rc};
use thiserror::Error;
use crate::{token::Token, value::Value};
#[derive(Error, Debug)]
pub enum EnvironmentError {
#[error("line {0}: undefined variable: {1}")]
UndefinedVariable(usize, String),
#[error("invalid environment distance")]
InvalidDistance,
}
/// Environment mapping variable names to their respective values. Can have an optional enclosing
/// environment. The outermost (global) environment has no enclosing one.
#[derive(Default, Debug, Clone)]
pub struct Environment {
values: HashMap<String, Value>,
enclosing: Option<Rc<RefCell<Environment>>>,
}
impl Environment {
/// Initialize a new environment with an enclosing one.
pub fn with_enclosing(enclosing: Rc<RefCell<Environment>>) -> Self {
Self {
values: HashMap::default(),
enclosing: Some(enclosing),
}
}
/// Define a new variable binding with a value.
///
/// Overwrite any binding of the same name.
pub fn define(&mut self, name: String, value: Value) {
self.values.insert(name, value);
}
/// Assign a new value to an existing (defined) variable. Error if there is no such variable in
/// this environment or any of the enclosing ones.
pub fn assign(&mut self, token: &Token, value: Value) -> Result<(), EnvironmentError> {
if self.values.contains_key(token.lexeme.as_str()) {
self.values.insert(token.lexeme.clone(), value);
Ok(())
} else if let Some(enclosing) = &mut self.enclosing {
enclosing.borrow_mut().assign(token, value)
} else {
Err(EnvironmentError::UndefinedVariable(
token.line,
token.lexeme.clone(),
))
}
}
pub fn assign_at(
&mut self,
distance: usize,
token: &Token,
value: Value,
) -> Result<(), EnvironmentError> {
self.ancestor(distance)?
.values
.insert(token.lexeme.clone(), value);
Ok(())
}
/// Get the value of an existing (defined) variable. Error if there is no such variable in
/// this environment or any of the enclosing ones.
pub fn get(&self, token: &Token) -> Result<Value, EnvironmentError> {
if let Some(v) = self.values.get(token.lexeme.as_str()) {
Ok(v.clone())
} else if let Some(enclosing) = &self.enclosing {
enclosing.borrow().get(token)
} else {
Err(EnvironmentError::UndefinedVariable(
token.line,
token.lexeme.clone(),
))
}
}
pub fn get_at(&self, distance: usize, token: &Token) -> Result<Value, EnvironmentError> {
if let Some(v) = self.ancestor(distance)?.values.get(token.lexeme.as_str()) {
Ok(v.clone())
} else {
Err(EnvironmentError::UndefinedVariable(
token.line,
token.lexeme.clone(),
))
}
}
fn ancestor(&self, distance: usize) -> Result<Environment, EnvironmentError> {
let mut environment = self.clone();
for _ in 0..distance {
let enclosing = match &environment.enclosing {
Some(enc) => enc.borrow().clone(),
None => return Err(EnvironmentError::InvalidDistance),
};
environment = enclosing;
}
Ok(environment)
}
}

54
src/expression.rs Normal file
View file

@ -0,0 +1,54 @@
use crate::token::{self, Token};
/// Enumeration of all types of expressions.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Expression {
Assign {
name: Token,
value: Box<Expression>,
},
Binary {
left: Box<Expression>,
operator: Token,
right: Box<Expression>,
},
Call {
callee: Box<Expression>,
paren: Token,
args: Vec<Expression>,
},
Get {
object: Box<Expression>,
name: Token,
},
Set {
object: Box<Expression>,
name: Token,
value: Box<Expression>,
},
This {
keyword: Token,
},
Grouping {
expression: Box<Expression>,
},
Literal {
value: token::Literal,
},
Logical {
left: Box<Expression>,
operator: Token,
right: Box<Expression>,
},
Unary {
operator: Token,
right: Box<Expression>,
},
Variable {
name: Token,
},
Super {
keyword: Token,
method: Token,
},
}

77
src/function.rs Normal file
View file

@ -0,0 +1,77 @@
use crate::{
callable::{Callable, CallingError},
environment::Environment,
instance::Instance,
interpreter::Interpreter,
statement::Statement,
token::Token,
value::Value,
};
use core::fmt::Debug;
use std::{cell::RefCell, fmt::Display, rc::Rc};
/// A lox function.
#[derive(Debug, Clone)]
pub struct Function {
pub name: Token,
pub is_initializer: bool,
pub params: Vec<Token>,
pub body: Vec<Statement>,
pub closure: Rc<RefCell<Environment>>,
}
impl Display for Function {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.name.lexeme)
}
}
impl PartialEq for Function {
fn eq(&self, other: &Self) -> bool {
self.name == other.name && self.params == other.params && self.body == other.body
}
}
impl Callable for Function {
fn name(&self) -> String {
self.name.lexeme.clone()
}
fn arity(&self) -> usize {
self.params.len()
}
fn call(&self, interpreter: &mut Interpreter, args: Vec<Value>) -> Result<Value, CallingError> {
let mut env = Environment::with_enclosing(self.closure.clone());
for (i, param) in self.params.iter().enumerate() {
let arg = args
.get(i)
.ok_or(CallingError::ArgumentMismatch(
self.params.len(),
args.len(),
))?
.clone();
env.define(param.lexeme.clone(), arg);
}
Ok(interpreter
.block(&self.body, env)
.map_err(|e| CallingError::CallInterpretationFailed(e.to_string()))?
.unwrap_or(Value::Nil))
}
}
impl Function {
pub fn bind(&self, instance: Rc<RefCell<Instance>>) -> Self {
let mut env = Environment::with_enclosing(self.closure.clone());
env.define("this".to_string(), Value::Instance(instance));
Self {
name: self.name.clone(),
is_initializer: self.is_initializer,
params: self.params.clone(),
body: self.body.clone(),
closure: Rc::new(RefCell::new(env)),
}
}
}

56
src/instance.rs Normal file
View file

@ -0,0 +1,56 @@
use std::{cell::RefCell, collections::HashMap, fmt::Display, rc::Rc};
use thiserror::Error;
use crate::{
class::Class,
token::Token,
value::{CallableType, Value},
};
#[derive(Error, Debug)]
pub enum InstanceError {
#[error("line {0}: no property named {1}.")]
NoSuchProperty(usize, String),
}
#[derive(Debug, Clone, PartialEq)]
pub struct Instance {
class: Class,
fields: HashMap<String, Value>,
}
impl Display for Instance {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{} instance", self.class.name)
}
}
impl Instance {
pub fn new(class: Class) -> Rc<RefCell<Self>> {
Rc::new(RefCell::new(Self {
class,
fields: HashMap::default(),
}))
}
pub fn get(&self, name: &Token, instance: Rc<RefCell<Self>>) -> Result<Value, InstanceError> {
if let Some(field) = self.fields.get(name.lexeme.as_str()) {
Ok(field.clone())
} else if let Some(method) = self.class.find_method(name.lexeme.as_str()) {
Ok(Value::Callable((
Rc::new(method.bind(instance)),
CallableType::Function,
)))
} else {
Err(InstanceError::NoSuchProperty(
name.line,
name.lexeme.clone(),
))
}
}
pub fn set(&mut self, name: &Token, value: Value) {
self.fields.insert(name.lexeme.clone(), value);
}
}

614
src/interpreter.rs Normal file
View file

@ -0,0 +1,614 @@
use std::{cell::RefCell, collections::HashMap, rc::Rc};
use ordered_float::OrderedFloat;
use thiserror::Error;
use tracing::error;
use crate::{
callable::CallingError,
class::Class,
environment::{Environment, EnvironmentError},
expression::Expression,
function::{self, Function},
native_functions,
statement::Statement,
token::{Literal, Token, TokenType},
value::{CallableType, Value},
};
#[derive(Error, Debug)]
pub enum InterpreterError {
#[error("line {0}: MINUS unary expression expects a number on the right")]
UnaryExpressionNotANumber(usize),
#[error("line {0}: unknown unary operator: {1}")]
UnaryOperatorUnknown(usize, String),
#[error("line {0}: unknown binary operator: {1}")]
BinaryOperatorUnknown(usize, String),
#[error("line {0}: left or right is not a number.")]
BinaryExpressionNeedsNumber(usize),
#[error("line {0}: left or right is neither a number nor string.")]
BinaryExpressionNeedsNumberOrString(usize),
#[error("{0}")]
UndefinedVariable(EnvironmentError),
#[error("line {0}: {1} is not callable.")]
NotACallable(usize, Value),
#[error("line {0}: {1}.")]
FailedToCall(usize, CallingError),
#[error("line {0}: only instances have fields.")]
OnlyInstancesHaveFields(usize),
#[error("line {0}: superclass must be a class.")]
SuperclassNotClass(usize),
#[error("line {0}: Undefined property '{1}'.")]
UndefinedProperty(usize, String),
#[error("{0}")]
InstanceError(#[from] crate::instance::InstanceError),
#[error("{0}")]
EnvironmentError(#[from] crate::environment::EnvironmentError),
}
/// Interpreter for the Lox language.
#[derive(Debug)]
pub struct Interpreter {
pub globals: Rc<RefCell<Environment>>,
environment: Rc<RefCell<Environment>>,
locals: HashMap<Expression, usize>,
}
/// Default configuration for the interpreter, with builtin native functions.
impl Default for Interpreter {
fn default() -> Self {
let env: Rc<RefCell<Environment>> = Default::default();
{
// add all native functions to global environment
let mut env = env.borrow_mut();
for (name, fun) in native_functions::all() {
env.define(name, fun);
}
}
Self {
globals: env.clone(),
environment: env.clone(),
locals: HashMap::new(),
}
}
}
impl Interpreter {
/// Try to evaluate an expression and return its result.
pub fn run(&mut self, statements: Vec<Statement>) -> Result<(), InterpreterError> {
for stmt in statements {
match self.execute(&stmt) {
Ok(_) => {}
Err(e) => error!("{e}"),
};
}
Ok(())
}
pub fn resolve(&mut self, expression: Expression, depth: usize) {
self.locals.insert(expression, depth);
}
///Execute a statement.
fn execute(&mut self, statement: &Statement) -> Result<Option<Value>, InterpreterError> {
match statement {
Statement::Block(statements) => {
let sub_env = Environment::with_enclosing(self.environment.clone());
self.block(statements, sub_env)
}
Statement::Print(expression) => self.print_statement(expression),
Statement::Expression(expression) => Ok(Some(self.evaluate(expression)?)),
Statement::Var { name, initializer } => {
self.var_statement(name, initializer.as_ref().as_ref())
}
Statement::If {
condition,
then_branch,
else_branch,
} => {
let else_branch = else_branch.as_ref().map(|x| *x.clone());
self.if_statement(condition, then_branch, else_branch.as_ref())
}
Statement::While { condition, body } => self.while_statement(condition, body),
Statement::Function { name, params, body } => {
self.function_statement(name, params, body)
}
Statement::Return { keyword: _, value } => self.return_statement(value),
Statement::Class {
name,
superclass,
methods,
} => self.class(name, superclass, methods),
}
}
/// Execute all statements within a block, using a new environment (with the old one as the
/// enclosing one). Immediately return the value if a Return Value is encountered after
/// execution of a statement.
pub fn block(
&mut self,
statements: &[Statement],
environment: Environment,
) -> Result<Option<Value>, InterpreterError> {
let prev_env = self.environment.clone();
self.environment = Rc::new(RefCell::new(environment));
for stmt in statements {
let execution = self.execute(stmt);
match execution {
Ok(x) => {
if let Some(Value::Return(x)) = x {
self.environment = prev_env.clone();
return Ok(Some(*x));
}
}
Err(e) => error!("{e}"),
}
}
self.environment = prev_env.clone();
Ok(None)
}
fn class(
&mut self,
name: &Token,
superclass: &Option<Expression>,
methods: &[Statement],
) -> Result<Option<Value>, InterpreterError> {
let superclass = if let Some(superclass) = superclass {
let superclass = self.evaluate(superclass)?;
if let Value::Callable((_, CallableType::Class(class))) = superclass {
Some(class)
} else {
return Err(InterpreterError::SuperclassNotClass(name.line));
}
} else {
None
};
self.environment
.borrow_mut()
.define(name.lexeme.clone(), Value::Nil);
let old_env = self.environment.clone();
if let Some(ref superclass) = superclass {
self.environment = Rc::new(RefCell::new(Environment::with_enclosing(
self.environment.clone(),
)));
self.environment.borrow_mut().define(
"super".to_string(),
Value::Callable((
Rc::new(superclass.clone()),
CallableType::Class(superclass.clone()),
)),
);
}
let mut class_methods: HashMap<String, Function> = HashMap::new();
for method in methods {
if let Statement::Function { name, params, body } = method {
let function = function::Function {
name: name.clone(),
is_initializer: name.lexeme == "init",
params: params.clone(),
body: body.clone(),
closure: self.environment.clone(),
};
class_methods.insert(name.lexeme.clone(), function);
}
}
let class = Class::new(name.lexeme.clone(), superclass, class_methods);
if class.superclass.is_some() {
self.environment = old_env;
}
self.environment
.borrow_mut()
.assign(
name,
Value::Callable((Rc::new(class.clone()), CallableType::Class(class))),
)
.map_err(InterpreterError::UndefinedVariable)?;
Ok(None)
}
/// Evaluate an expression and return its value.
fn evaluate(&mut self, expression: &Expression) -> Result<Value, InterpreterError> {
match expression {
Expression::Literal { value } => self.literal(value.clone()),
Expression::Grouping { expression } => self.grouping(expression),
Expression::Unary {
operator: op,
right,
} => self.unary(op, right),
Expression::Binary {
left,
operator,
right,
} => self.binary(left, operator, right),
Expression::Variable { name } => self.var_expression(name, expression),
Expression::Assign { name, value } => self.assign(name, value),
Expression::Logical {
left,
operator,
right,
} => self.logical_expression(left, operator, right),
Expression::Call {
callee,
paren,
args,
} => self.call(callee, paren, args),
Expression::Get { object, name } => self.get(object, name),
Expression::Set {
object,
name,
value,
} => self.set(object, name, value),
Expression::This { keyword } => self.lookup_var(keyword, expression),
Expression::Super { keyword, method } => self.super_expr(expression, keyword, method),
}
}
/// Call a callable if it is one (meaning it starts with a LeftParen after an identifier),
/// otherwise evaluate the expression.
fn call(
&mut self,
callee: &Expression,
paren: &Token,
arg_expressions: &[Expression],
) -> Result<Value, InterpreterError> {
let callee = self.evaluate(callee)?;
let mut args = Vec::new();
for arg in arg_expressions {
args.push(self.evaluate(arg)?);
}
if let Value::Callable((f, _)) = callee {
f.call(self, args)
.map_err(|e| InterpreterError::FailedToCall(paren.line, e))
} else {
Err(InterpreterError::NotACallable(paren.line, callee))
}
}
fn get(&mut self, object: &Expression, name: &Token) -> Result<Value, InterpreterError> {
match self.evaluate(object)? {
Value::Instance(instance) => Ok(instance.borrow().get(name, instance.clone())?),
_ => Err(InterpreterError::OnlyInstancesHaveFields(name.line)),
}
}
fn set(
&mut self,
object: &Expression,
name: &Token,
value: &Expression,
) -> Result<Value, InterpreterError> {
match self.evaluate(object)? {
Value::Instance(instance) => {
let value = self.evaluate(value)?;
instance.borrow_mut().set(name, value.clone());
Ok(value)
}
_ => Err(InterpreterError::OnlyInstancesHaveFields(name.line)),
}
}
fn super_expr(
&mut self,
expression: &Expression,
keyword: &Token,
method: &Token,
) -> Result<Value, InterpreterError> {
if let Some(distance) = self.locals.get(expression) {
let superclass = self.environment.borrow().get_at(*distance, keyword)?;
let object = self.environment.borrow().get_at(
*distance - 1,
&Token {
token_type: TokenType::This,
lexeme: "this".to_string(),
literal: Some(Literal::String("this".to_string())),
line: 0,
},
)?;
let Value::Instance(instance) = object else {
return Err(InterpreterError::SuperclassNotClass(keyword.line));
};
let Value::Callable((_, CallableType::Class(class))) = superclass else {
return Err(InterpreterError::SuperclassNotClass(keyword.line));
};
if let Some(method) = class.find_method(&method.lexeme) {
let method = method.bind(instance);
Ok(Value::Callable((Rc::new(method), CallableType::Function)))
} else {
Err(InterpreterError::UndefinedProperty(
keyword.line,
method.lexeme.clone(),
))
}
} else {
Err(InterpreterError::SuperclassNotClass(keyword.line))
}
}
/// Define a new function.
fn function_statement(
&mut self,
name: &Token,
params: &[Token],
body: &[Statement],
) -> Result<Option<Value>, InterpreterError> {
let fn_name = name.lexeme.clone();
let fun = Function {
name: name.clone(),
is_initializer: false,
params: params.to_vec(),
body: body.to_vec(),
closure: self.environment.clone(),
};
self.environment.borrow_mut().define(
fn_name,
Value::Callable((Rc::new(fun), CallableType::Function)),
);
Ok(None)
}
/// If the condition evaluates to truthy, execute the then branch, otherwise the else branch.
fn if_statement(
&mut self,
condition: &Expression,
then_branch: &Statement,
else_branch: Option<&Statement>,
) -> Result<Option<Value>, InterpreterError> {
if self.evaluate(condition)?.is_truthy() {
self.execute(then_branch)
} else if let Some(else_branch) = else_branch {
self.execute(else_branch)
} else {
Ok(None)
}
}
/// Evaluate an expression and print its value to stdout.
fn print_statement(
&mut self,
expression: &Expression,
) -> Result<Option<Value>, InterpreterError> {
let value = self.evaluate(expression)?;
println!("{value}");
Ok(None)
}
/// Return a value from a statement.
fn return_statement(
&mut self,
value: &Option<Expression>,
) -> Result<Option<Value>, InterpreterError> {
Ok(match value {
Some(x) => Some(Value::Return(Box::new(self.evaluate(x)?))),
None => Some(Value::Return(Box::new(Value::Nil))),
})
}
/// Initialize a variable with an initializer expression or nil.
fn var_statement(
&mut self,
name: &Token,
initializer: Option<&Expression>,
) -> Result<Option<Value>, InterpreterError> {
let value = if let Some(initializer) = initializer {
self.evaluate(initializer)
} else {
Ok(Value::Nil)
}?;
self.environment
.borrow_mut()
.define(name.lexeme.clone(), value);
Ok(None)
}
/// Execute the body as long as the condition evaluates to true.
fn while_statement(
&mut self,
condition: &Expression,
body: &Statement,
) -> Result<Option<Value>, InterpreterError> {
while self.evaluate(condition)?.is_truthy() {
self.execute(body)?;
}
Ok(None)
}
/// Assign the value of an expression to a variable.
fn assign(&mut self, name: &Token, expression: &Expression) -> Result<Value, InterpreterError> {
let value = self.evaluate(expression)?;
self.environment
.borrow_mut()
.assign(name, value.clone())
.map_err(InterpreterError::UndefinedVariable)?;
if let Some(distance) = self.locals.get(expression) {
self.environment
.borrow_mut()
.assign_at(*distance, name, value.clone())
.map_err(InterpreterError::UndefinedVariable)?;
} else {
self.globals
.borrow_mut()
.assign(name, value.clone())
.map_err(InterpreterError::UndefinedVariable)?;
}
Ok(value)
}
/// Convert the literal value into a Value.
fn literal(&self, literal: Literal) -> Result<Value, InterpreterError> {
Ok(literal.into())
}
/// Evaluate left and if the operator is Or and left is truthy or the operator is not Or and
/// not leftis truthy short circuit and return left. Otherwise evaluate and return right.
fn logical_expression(
&mut self,
left: &Expression,
operator: &Token,
right: &Expression,
) -> Result<Value, InterpreterError> {
let left = self.evaluate(left)?;
let truthy = if operator.token_type == TokenType::Or {
left.is_truthy()
} else {
!left.is_truthy()
};
if truthy {
Ok(left)
} else {
self.evaluate(right)
}
}
/// Evaluate the inner expression.
fn grouping(&mut self, inner: &Expression) -> Result<Value, InterpreterError> {
self.evaluate(inner)
}
/// Evaluate the expression on the right and use its result when evaluating the unary operator.
fn unary(&mut self, op: &Token, right: &Expression) -> Result<Value, InterpreterError> {
let right = self.evaluate(right)?;
match op.token_type {
TokenType::Minus => {
if let Value::Number(val) = right {
Ok(Value::Number(-val))
} else {
Err(InterpreterError::UnaryExpressionNotANumber(op.line))
}
}
TokenType::Bang => Ok(Value::Boolean(!right.is_truthy())),
_ => Err(InterpreterError::UnaryOperatorUnknown(
op.line,
op.lexeme.clone(),
)),
}
}
/// Get the value of a variable.
fn var_expression(
&mut self,
name: &Token,
expression: &Expression,
) -> Result<Value, InterpreterError> {
self.lookup_var(name, expression)
}
fn lookup_var(
&mut self,
name: &Token,
expression: &Expression,
) -> Result<Value, InterpreterError> {
if let Some(distance) = self.locals.get(expression) {
self.environment
.borrow()
.get_at(*distance, name)
.map_err(InterpreterError::UndefinedVariable)
} else {
self.globals
.borrow()
.get(name)
.map_err(InterpreterError::UndefinedVariable)
}
}
/// Calculate number operations.
fn number_op(&self, left: f64, op: TokenType, right: f64) -> f64 {
match op {
TokenType::Minus => left - right,
TokenType::Plus => left + right,
TokenType::Slash => left / right,
TokenType::Star => left * right,
_ => unreachable!(),
}
}
/// Calculate boolean operations.
fn boolean_op(&self, left: f64, op: TokenType, right: f64) -> bool {
match op {
TokenType::Greater => left > right,
TokenType::GreaterEqual => left >= right,
TokenType::Less => left < right,
TokenType::LessEqual => left <= right,
_ => unreachable!(),
}
}
/// Evaluate the left and right expressions (in that order) and then combine them with the
/// specified operator.
fn binary(
&mut self,
left: &Expression,
op: &Token,
right: &Expression,
) -> Result<Value, InterpreterError> {
let left = self.evaluate(left)?;
let right = self.evaluate(right)?;
match op.token_type {
TokenType::Minus | TokenType::Slash | TokenType::Star | TokenType::Plus => {
if let (Value::Number(left), Value::Number(right)) = (left.clone(), right.clone()) {
Ok(Value::Number(OrderedFloat(self.number_op(
*left,
op.token_type,
*right,
))))
} else if let (Value::String(left), Value::String(right)) = (left, right) {
Ok(Value::String(format!("{}{}", left.clone(), right.clone())))
} else {
Err(InterpreterError::BinaryExpressionNeedsNumberOrString(
op.line,
))
}
}
TokenType::Greater
| TokenType::GreaterEqual
| TokenType::Less
| TokenType::LessEqual => {
if let (Value::Number(left), Value::Number(right)) = (left, right) {
Ok(Value::Boolean(self.boolean_op(
*left,
op.token_type,
*right,
)))
} else {
Err(InterpreterError::BinaryExpressionNeedsNumber(op.line))
}
}
TokenType::BangEqual => Ok(Value::Boolean(left != right)),
TokenType::EqualEqual => Ok(Value::Boolean(left == right)),
_ => Err(InterpreterError::BinaryOperatorUnknown(
op.line,
op.lexeme.clone(),
)),
}
}
}

26
src/keywords.rs Normal file
View file

@ -0,0 +1,26 @@
use crate::token::TokenType::{self, *};
use lazy_static::lazy_static;
use std::collections::HashMap;
lazy_static! {
/// Mapping of reserved keywords to their respective TokenType.
pub static ref KEYWORDS: HashMap<std::string::String, TokenType> = {
let mut m = HashMap::new();
m.insert("and".into(), And);
m.insert("class".into(), Class);
m.insert("else".into(), Else);
m.insert("false".into(), False);
m.insert("for".into(), For);
m.insert("fun".into(), Fun);
m.insert("if".into(), If);
m.insert("nil".into(), Nil);
m.insert("or".into(), Or);
m.insert("print".into(), Print);
m.insert("return".into(), Return);
m.insert("super".into(), Super);
m.insert("true".into(), True);
m.insert("var".into(), Var);
m.insert("while".into(), While);
m
};
}

97
src/lib.rs Normal file
View file

@ -0,0 +1,97 @@
//! Interpret the Lox language. Either compile (interpret for now though) some source code or run a
//! REPL.
use std::{
fs::{self},
io::{self, Write},
path::Path,
};
use interpreter::{Interpreter, InterpreterError};
use parser::ParserError;
use resolver::{Resolver, ResolverError};
use thiserror::Error;
use tracing::error;
pub mod callable;
pub mod class;
pub mod cli;
pub mod environment;
pub mod expression;
pub mod function;
pub mod instance;
pub mod interpreter;
pub mod keywords;
pub mod native_functions;
pub mod parser;
pub mod resolver;
pub mod scanner;
pub mod statement;
pub mod token;
pub mod tokenizer {
pub mod comment;
pub mod identifier;
pub mod interface;
pub mod lookahead;
pub mod newline;
pub mod number;
pub mod single_char;
pub mod string;
pub mod whitespace;
}
pub mod value;
#[derive(Error, Debug)]
pub enum RoxError {
#[error("parser failed: {0}")]
ParseError(#[from] ParserError),
#[error("resolver failed: {0}")]
ResolverError(#[from] ResolverError),
#[error("interpreter failed: {0}")]
InterpreterError(#[from] InterpreterError),
}
/// Read the source code in a file and scan it to tokens.
pub fn compile(source: &Path) -> Result<(), io::Error> {
let input = fs::read_to_string(source)?;
let mut interpreter = Interpreter::default();
run(&input, &mut interpreter);
Ok(())
}
/// Run a Lox REPL until SIGINT.
pub fn repl() {
let mut interpreter = Interpreter::default();
loop {
print!("> ");
let _ = io::stdout().flush();
let mut input = String::new();
match io::stdin().read_line(&mut input) {
Ok(_) => {}
Err(e) => error!("{}", e),
}
let input = input.trim().to_string();
run(&input, &mut interpreter);
}
}
/// Evaluate a Lox input string and print errors or output.
fn run(input: &str, interpreter: &mut Interpreter) {
if let Err(e) = run_rox(input, interpreter) {
error!("{e}");
}
}
fn run_rox(input: &str, interpreter: &mut Interpreter) -> Result<(), RoxError> {
let tokens = scanner::tokenize(input);
let mut resolver = Resolver::new(interpreter);
let ast = parser::ast(tokens)?;
resolver.resolve(&ast)?;
interpreter.run(ast)?;
Ok(())
}

38
src/main.rs Normal file
View file

@ -0,0 +1,38 @@
use std::process::exit;
use clap::Parser;
use rox::cli::{Cli, Commands};
use tracing::error;
/// Cli entrypoint.
fn main() {
if std::env::var_os("RUST_LOG").is_none() {
std::env::set_var("RUST_LOG", "info");
}
tracing_subscriber::fmt::init();
let cli = Cli::parse();
match &cli.command {
Commands::Run(compile_config) => {
if !&compile_config.source.exists() {
error!(
"{} does not exist",
&compile_config.source.to_string_lossy()
);
exit(1);
}
if let Err(e) = rox::compile(&compile_config.source) {
error!(
"failed to compile {}: {}",
&compile_config.source.to_string_lossy(),
e
);
}
}
Commands::Repl => {
rox::repl();
}
}
}

44
src/native_functions.rs Normal file
View file

@ -0,0 +1,44 @@
use std::{
rc::Rc,
time::{SystemTime, UNIX_EPOCH},
};
use ordered_float::OrderedFloat;
use crate::{
callable::{Callable, CallingError},
interpreter::Interpreter,
value::{CallableType, Value},
};
struct Clock;
impl Callable for Clock {
fn name(&self) -> String {
"clock".into()
}
fn arity(&self) -> usize {
0
}
fn call(
&self,
_interpreter: &mut Interpreter,
_args: Vec<crate::value::Value>,
) -> Result<Value, CallingError> {
let start = SystemTime::now();
let since_the_epoch = start
.duration_since(UNIX_EPOCH)
.map_err(|e| CallingError::CallFailed(e.to_string()))?;
Ok(Value::Number(OrderedFloat(since_the_epoch.as_secs_f64())))
}
}
pub fn all() -> Vec<(String, Value)> {
vec![(
"clock".into(),
Value::Callable((Rc::new(Clock {}), CallableType::Function)),
)]
}

662
src/parser.rs Normal file
View file

@ -0,0 +1,662 @@
use crate::{
expression::Expression,
statement::Statement,
token::{
self, Literal, Token,
TokenType::{self, *},
},
};
use thiserror::Error;
use tracing::error;
#[derive(Error, Debug)]
pub enum ParserError {
#[error("empty token stream")]
NoTokens,
#[error("line {0}: expected expression")]
ExpressionExpected(usize),
#[error("line {0}: expected ')' after expression.")]
ParenAfterExpression(usize),
#[error("Out of bounds access at index {0}.")]
OutOfBoundsAccess(usize),
#[error("line {0}: literal expected.")]
LiteralExpected(usize),
#[error("line {0}: expected ';' after value.")]
SemicolonAfterValueExpected(usize),
#[error("line {0}: expected ';' after expression.")]
SemicolonAfterExpressionExpected(usize),
#[error("line {0}: expected class name.")]
ClassNameExpected(usize),
#[error("line {0}: expected variable name.")]
VariableNameExpected(usize),
#[error("line {0}: invalid assignment target.")]
InvalidAssignmentTarget(usize),
#[error("line {0}: expected '}}' after block.")]
RightBraceAfterBlockExpected(usize),
#[error("line {0}: expected '(' after if.")]
LeftParenAfterIfExpected(usize),
#[error("line {0}: expected ')' after condition.")]
RightParenAfterConditionExpected(usize),
#[error("line {0}: expected '(' after while.")]
LeftParenAfterWhileExpected(usize),
#[error("line {0}: expected '(' after for.")]
LeftParenAfterForExpected(usize),
#[error("line {0}: expected ';' after loop condition.")]
SemicolonAfterLoopConditionExpected(usize),
#[error("line {0}: expected ')' after for clauses.")]
RightParenAfterForClausesExpected(usize),
#[error("line {0}: expected ')' after arguments.")]
RightParenAfterArgumentsExpected(usize),
#[error("line {0}: expected function name.")]
FunctionNameExpected(usize),
#[error("line {0}: expected '(' after function name.")]
LeftParenAfterFunctionNameExpected(usize),
#[error("line {0}: expected ')' after parameters.")]
RightParenAfterParamsExpected(usize),
#[error("line {0}: expected parameter name.")]
ParamNameExpected(usize),
#[error("line {0}: expected '{{' before function body.")]
LeftBraceBeforeFunctionBodyExpected(usize),
#[error("line {0}: expected ';' after return value.")]
SemicolonAfterReturnExpected(usize),
#[error("line {0}: expected '{{' before class body.")]
LeftBraceBeforeClassExpected(usize),
#[error("line {0}: expected '}}' after class body.")]
RightBraceAfterClassExpected(usize),
#[error("line {0}: expected property name after '.'.")]
PropertyNameAfterDotExpected(usize),
#[error("line {0}: expected '.' after 'super'.")]
DotAfterSuper(usize),
#[error("line {0}: expected superclass method name.")]
SuperclassMethodNameExpected(usize),
}
/// Parse the Lox language.
#[derive(Debug, Clone)]
struct Parser {
current: usize,
current_token: Token,
tokens: Vec<Token>,
}
impl Parser {
/// Create a new parser instance, fail if the tokens vector is empty.
fn new(tokens: Vec<Token>) -> Result<Self, ParserError> {
let current_token = tokens.first().ok_or(ParserError::NoTokens)?.clone();
Ok(Self {
current: 0,
current_token,
tokens,
})
}
/// Parse all tokens to a list of statements for execution.
fn run(&mut self) -> Result<Vec<Statement>, ParserError> {
let mut statements = Vec::new();
while !self.is_at_end() {
match self.declaration() {
Ok(x) => statements.push(x),
Err(e) => {
error!("{e}");
self.synchronize();
}
}
}
Ok(statements)
}
/// Check if any of the provided types match the type of the current token.
///
/// If so, advance the current token.
fn matches(&mut self, types: &[TokenType]) -> bool {
let matches = types.iter().any(|x| self.check(x));
matches.then(|| self.advance());
matches
}
/// Return true if the current token type matches the match_type, false otherwise.
fn check(&self, match_type: &TokenType) -> bool {
self.current_token.token_type == *match_type
}
/// Advance the current token if we have not hit Eof yet.
///
/// Return the token before the advancement.
fn advance(&mut self) -> Result<&Token, ParserError> {
if !self.is_at_end() {
self.current += 1;
self.current_token = self
.tokens
.get(self.current)
.ok_or(ParserError::OutOfBoundsAccess(self.current))?
.clone();
}
self.previous()
}
/// Return true if the current token is Eof, false otherwise.
fn is_at_end(&self) -> bool {
self.current_token.token_type == Eof
}
/// Return the token before the current one or an error if there is none.
fn previous(&self) -> Result<&Token, ParserError> {
self.tokens
.get(self.current - 1)
.ok_or_else(|| ParserError::OutOfBoundsAccess(self.current - 1))
}
/// Consume the current token if its token type matches the provided token_type and advance the
/// current token. Otherwise return None..
fn consume(&mut self, token_type: &TokenType) -> Option<&Token> {
if self.check(token_type) {
self.advance().ok()
} else {
None
}
}
/// Parse a binary expression using the next_precedence function and operators to match.
fn binary_expr(
&mut self,
next_precedence: impl Fn(&mut Self) -> Result<Expression, ParserError>,
operators: &[TokenType],
) -> Result<Expression, ParserError> {
let mut expr = next_precedence(self)?;
while self.matches(operators) {
let operator = self.previous()?.clone();
let right = next_precedence(self)?;
expr = Expression::Binary {
left: Box::new(expr.clone()),
operator,
right: Box::new(right),
};
}
Ok(expr)
}
/// expression -> equality ;
fn expression(&mut self) -> Result<Expression, ParserError> {
self.assignment()
}
fn declaration(&mut self) -> Result<Statement, ParserError> {
if self.matches(&[Class]) {
self.class_declaration()
} else if self.matches(&[Fun]) {
self.function()
} else if self.matches(&[Var]) {
self.var_declaration()
} else {
self.statement()
}
}
fn statement(&mut self) -> Result<Statement, ParserError> {
if self.matches(&[For]) {
self.for_statement()
} else if self.matches(&[If]) {
self.if_statement()
} else if self.matches(&[Print]) {
self.print_statement()
} else if self.matches(&[Return]) {
self.return_statement()
} else if self.matches(&[While]) {
self.while_statement()
} else if self.matches(&[LeftBrace]) {
Ok(Statement::Block(self.block()?))
} else {
self.expression_statement()
}
}
/// Build up a while statement from a for statement.
fn for_statement(&mut self) -> Result<Statement, ParserError> {
let line = self.current_token.line;
self.consume(&LeftParen)
.ok_or(ParserError::LeftParenAfterForExpected(line))?;
let initializer = if self.matches(&[Semicolon]) {
None
} else if self.matches(&[Var]) {
Some(self.var_declaration()?)
} else {
Some(self.expression_statement()?)
};
let condition = if !self.matches(&[Semicolon]) {
self.expression()?
} else {
Expression::Literal {
value: Literal::Boolean(true),
}
};
self.consume(&Semicolon)
.ok_or(ParserError::SemicolonAfterLoopConditionExpected(line))?;
let increment = if !self.check(&RightParen) {
Some(self.expression()?)
} else {
None
};
self.consume(&RightParen)
.ok_or(ParserError::RightParenAfterForClausesExpected(line))?;
let body = self.statement()?;
let body = match increment {
Some(inc) => Statement::Block(vec![body, Statement::Expression(inc)]),
None => body,
};
let body = Statement::While {
condition,
body: Box::new(body),
};
let body = match initializer {
Some(initializer) => Statement::Block(vec![initializer, body]),
None => body,
};
Ok(body)
}
fn if_statement(&mut self) -> Result<Statement, ParserError> {
let line = self.current_token.line;
self.consume(&LeftParen)
.ok_or(ParserError::LeftParenAfterIfExpected(line))?;
let condition = self.expression()?;
self.consume(&RightParen)
.ok_or(ParserError::RightParenAfterConditionExpected(line))?;
let then_branch = self.statement()?;
let else_branch = if self.matches(&[Else]) {
Some(Box::new(self.statement()?))
} else {
None
};
Ok(Statement::If {
condition,
then_branch: Box::new(then_branch),
else_branch,
})
}
fn print_statement(&mut self) -> Result<Statement, ParserError> {
let value = self.expression()?;
let line = self.current_token.line;
self.consume(&Semicolon)
.ok_or(ParserError::SemicolonAfterValueExpected(line))?;
Ok(Statement::Print(value))
}
fn return_statement(&mut self) -> Result<Statement, ParserError> {
let keyword = self.previous()?.clone();
let value = if self.check(&Semicolon) {
None
} else {
Some(self.expression()?)
};
self.consume(&Semicolon)
.ok_or(ParserError::SemicolonAfterReturnExpected(keyword.line))?;
Ok(Statement::Return { keyword, value })
}
fn class_declaration(&mut self) -> Result<Statement, ParserError> {
let line = self.current_token.line;
let name = self
.consume(&Identifier)
.ok_or(ParserError::ClassNameExpected(line))?
.clone();
let superclass = if self.matches(&[Less]) {
self.consume(&Identifier)
.ok_or(ParserError::ClassNameExpected(line))?;
let name = self.previous()?.clone();
Some(Expression::Variable { name })
} else {
None
};
self.consume(&LeftBrace)
.ok_or(ParserError::LeftBraceBeforeClassExpected(line))?;
let mut methods = Vec::new();
while !self.check(&RightBrace) && !self.is_at_end() {
let method = self.function()?;
methods.push(method);
}
self.consume(&RightBrace)
.ok_or(ParserError::RightBraceAfterClassExpected(line))?;
Ok(Statement::Class {
name,
superclass,
methods,
})
}
fn var_declaration(&mut self) -> Result<Statement, ParserError> {
let line = self.current_token.line;
let name = self
.consume(&Identifier)
.ok_or(ParserError::VariableNameExpected(line))?
.clone();
let initializer = if self.matches(&[Equal]) {
Some(self.expression()?)
} else {
None
};
self.consume(&Semicolon)
.ok_or(ParserError::SemicolonAfterExpressionExpected(line))?;
Ok(Statement::Var {
name,
initializer: Box::new(initializer),
})
}
fn while_statement(&mut self) -> Result<Statement, ParserError> {
let line = self.current_token.line;
self.consume(&LeftParen)
.ok_or(ParserError::LeftParenAfterWhileExpected(line))?;
let condition = self.expression()?;
self.consume(&RightParen)
.ok_or(ParserError::RightParenAfterConditionExpected(line))?;
let body = self.statement()?;
Ok(Statement::While {
condition,
body: Box::new(body),
})
}
fn expression_statement(&mut self) -> Result<Statement, ParserError> {
let expr = self.expression()?;
let line = self.current_token.line;
self.consume(&Semicolon)
.ok_or(ParserError::SemicolonAfterExpressionExpected(line))?;
Ok(Statement::Expression(expr))
}
fn function(&mut self) -> Result<Statement, ParserError> {
let line = self.current_token.line;
let name = self
.consume(&Identifier)
.ok_or(ParserError::FunctionNameExpected(line))?
.clone();
self.consume(&LeftParen)
.ok_or(ParserError::LeftParenAfterFunctionNameExpected(line))?;
let mut params = Vec::new();
if !self.check(&RightParen) {
let param = self
.consume(&Identifier)
.ok_or(ParserError::ParamNameExpected(line))?
.clone();
params.push(param);
while self.matches(&[Comma]) {
let param = self
.consume(&Identifier)
.ok_or(ParserError::ParamNameExpected(line))?
.clone();
params.push(param);
}
}
self.consume(&RightParen)
.ok_or(ParserError::RightParenAfterParamsExpected(line))?;
self.consume(&LeftBrace)
.ok_or(ParserError::LeftBraceBeforeFunctionBodyExpected(line))?;
let body = self.block()?;
Ok(Statement::Function { name, params, body })
}
fn block(&mut self) -> Result<Vec<Statement>, ParserError> {
let mut statements = Vec::new();
while !self.check(&RightBrace) && !self.is_at_end() {
statements.push(self.declaration()?);
}
let line = self.previous()?.line;
self.consume(&RightBrace)
.ok_or(ParserError::RightBraceAfterBlockExpected(line))?;
Ok(statements)
}
fn assignment(&mut self) -> Result<Expression, ParserError> {
let expr = self.or()?;
if self.matches(&[Equal]) {
let equals = self.previous()?.clone();
let value = self.assignment()?;
if let Expression::Variable { name } = expr {
Ok(Expression::Assign {
name,
value: Box::new(value),
})
} else if let Expression::Get { object, name } = expr {
Ok(Expression::Set {
object,
name,
value: Box::new(value),
})
} else {
Err(ParserError::InvalidAssignmentTarget(equals.line))
}
} else {
Ok(expr)
}
}
fn logical_operator<F>(
&mut self,
operator: TokenType,
parse_fn: F,
) -> Result<Expression, ParserError>
where
F: Fn(&mut Self) -> Result<Expression, ParserError>,
{
let mut expr = parse_fn(self)?;
while self.matches(&[operator]) {
let operator = self.previous()?.clone();
let right = parse_fn(self)?;
expr = Expression::Logical {
left: Box::new(expr),
operator,
right: Box::new(right),
};
}
Ok(expr)
}
fn or(&mut self) -> Result<Expression, ParserError> {
self.logical_operator(Or, Self::and)
}
fn and(&mut self) -> Result<Expression, ParserError> {
self.logical_operator(And, Self::equality)
}
/// equality -> comparison ( ( "!=" | "==" ) comparison )* ;
fn equality(&mut self) -> Result<Expression, ParserError> {
self.binary_expr(Self::comparison, &[BangEqual, EqualEqual])
}
/// comparison -> term ( ( ">" | ">=" | "<" | "<=" ) term )* ;
fn comparison(&mut self) -> Result<Expression, ParserError> {
self.binary_expr(Self::term, &[Greater, GreaterEqual, Less, LessEqual])
}
/// term -> factor ( ( "-" | "+" ) factor )* ;
fn term(&mut self) -> Result<Expression, ParserError> {
self.binary_expr(Self::factor, &[Minus, Plus])
}
/// factor -> unary ( ( "/" | "*" ) unary )* ;
fn factor(&mut self) -> Result<Expression, ParserError> {
self.binary_expr(Self::unary, &[Slash, Star])
}
/// unary -> ( "!" | "-" ) unary | primary ;
fn unary(&mut self) -> Result<Expression, ParserError> {
if self.matches(&[Bang, Minus]) {
let operator = self.previous()?.clone();
let right = self.unary()?;
Ok(Expression::Unary {
operator,
right: Box::new(right),
})
} else {
self.call()
}
}
fn call(&mut self) -> Result<Expression, ParserError> {
let mut expr = self.primary()?;
loop {
if self.matches(&[LeftParen]) {
expr = self.finish_call(expr)?;
} else if self.matches(&[Dot]) {
let line = self.current_token.line;
let name = self
.consume(&Identifier)
.ok_or(ParserError::PropertyNameAfterDotExpected(line))?
.clone();
expr = Expression::Get {
object: Box::new(expr),
name,
}
} else {
break;
}
}
Ok(expr)
}
fn finish_call(&mut self, callee: Expression) -> Result<Expression, ParserError> {
let mut args = Vec::new();
if !self.check(&RightParen) {
args.push(self.expression()?);
while self.matches(&[Comma]) {
args.push(self.expression()?);
}
}
let line = self.current_token.line;
let paren = self
.consume(&RightParen)
.ok_or(ParserError::RightParenAfterArgumentsExpected(line))?
.clone();
Ok(Expression::Call {
callee: Box::new(callee),
paren,
args,
})
}
/// primary -> NUMBER | STRING | "true" | "false" | "nil" | "(" expression ")" ;
fn primary(&mut self) -> Result<Expression, ParserError> {
if self.matches(&[False]) {
Ok(Expression::Literal {
value: token::Literal::Boolean(false),
})
} else if self.matches(&[True]) {
Ok(Expression::Literal {
value: token::Literal::Boolean(true),
})
} else if self.matches(&[Nil]) {
Ok(Expression::Literal {
value: token::Literal::Nil,
})
} else if self.matches(&[Super]) {
let line = self.current_token.line;
let keyword = self.previous()?.clone();
self.consume(&Dot).ok_or(ParserError::DotAfterSuper(line))?;
let method = self
.consume(&Identifier)
.ok_or(ParserError::SuperclassMethodNameExpected(line))?
.clone();
Ok(Expression::Super { keyword, method })
} else if self.matches(&[This]) {
Ok(Expression::This {
keyword: self.previous()?.clone(),
})
} else if self.matches(&[Identifier]) {
let prev = self.previous()?.clone();
Ok(Expression::Variable { name: prev })
} else if self.matches(&[Number, String]) {
let prev = self.previous()?;
let value = prev
.literal
.clone()
.ok_or(ParserError::LiteralExpected(prev.line))?;
Ok(Expression::Literal { value })
} else if self.matches(&[LeftParen]) {
let expr = self.expression()?;
let line = self.current_token.line;
self.consume(&RightParen)
.ok_or(ParserError::ParenAfterExpression(line))?;
Ok(Expression::Grouping {
expression: Box::new(expr),
})
} else {
let prev = self.previous()?;
Err(ParserError::ExpressionExpected(prev.line))
}
}
fn synchronize(&mut self) {
let _ = self.advance();
while !self.is_at_end()
&& self.previous().unwrap().token_type != Semicolon
&& !&[Class, Fun, Var, For, If, While, Print, Return]
.contains(&self.current_token.token_type)
{
let _ = self.advance();
}
}
}
/// Try to parse the provided tokens into an AST.
pub fn ast(tokens: Vec<Token>) -> Result<Vec<Statement>, ParserError> {
let mut parser = Parser::new(tokens)?;
parser.run()
}

408
src/resolver.rs Normal file
View file

@ -0,0 +1,408 @@
use crate::{expression::Expression, interpreter::Interpreter, statement::Statement, token::Token};
use std::collections::HashMap;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum ResolverError {
#[error("line {0}: can not read local variable in its own initializer.")]
ReadVarInOwnInitializer(usize),
#[error("line {0}: already a variable with this name in this scope.")]
NameRedefinition(usize),
#[error("line {0}: can not return from top-level code.")]
TopLevelReturn(usize),
#[error("line {0}: can not use 'this' outside of a class.")]
ThisOutsideClass(usize),
#[error("line {0}: can not return a value from an initializer.")]
ReturnFromInitializer(usize),
#[error("line {0}: a class can not inherit from itself.")]
SelfInheritance(usize),
#[error("line {0}: can not use 'super' outside of a class.")]
SuperOutsideClass(usize),
#[error("line {0}: can not use 'super' in a class with no superclass.")]
SuperWithoutSuperclass(usize),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum FunctionType {
None,
Function,
Initializer,
Method,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum ClassType {
None,
Class,
Subclass,
}
#[derive(Debug)]
pub struct Resolver<'a> {
scopes: Vec<HashMap<String, bool>>,
interpreter: &'a mut Interpreter,
current_fun: FunctionType,
current_class: ClassType,
}
impl<'a> Resolver<'a> {
pub fn new(interpreter: &'a mut Interpreter) -> Self {
Self {
scopes: Vec::default(),
interpreter,
current_fun: FunctionType::None,
current_class: ClassType::None,
}
}
pub fn resolve(&mut self, statements: &[Statement]) -> Result<(), ResolverError> {
for stm in statements {
self.resolve_statement(stm)?;
}
Ok(())
}
fn resolve_statement(&mut self, statement: &Statement) -> Result<(), ResolverError> {
match statement {
Statement::Block(statements) => self.resolve_block(statements),
Statement::Print(expression) => self.resolve_expression(expression),
Statement::Expression(expression) => self.resolve_expression(expression),
Statement::Var { name, initializer } => self.resolve_var(name, *initializer.clone()),
Statement::If {
condition,
then_branch,
else_branch,
} => self.resolve_if_stmt(
condition,
then_branch,
else_branch.as_ref().map(|boxed| *boxed.clone()),
),
Statement::While { condition, body } => self.resolve_while_stmt(condition, body),
Statement::Function { name, params, body } => {
self.resolve_function_stmt(name, params, body)
}
Statement::Return { keyword, value } => self.resolve_return_stmt(keyword, value),
Statement::Class {
name,
superclass,
methods,
} => self.resolve_class_stmt(name, superclass, methods),
}
}
fn resolve_expression(&mut self, expr: &Expression) -> Result<(), ResolverError> {
match expr {
Expression::Assign { name, value } => self.resolve_assign_expr(expr, name, value),
Expression::Binary {
left,
operator: _,
right,
} => self.resolve_binary_expr(left, right),
Expression::Call {
callee,
paren: _,
args,
} => self.resolve_call_expr(callee, args),
Expression::Grouping { expression } => self.resolve_expression(expression),
Expression::Literal { value: _ } => Ok(()),
Expression::Logical {
left,
operator: _,
right,
} => self.resolve_binary_expr(left, right),
Expression::Unary { operator: _, right } => self.resolve_expression(right),
Expression::Variable { name } => self.resolve_var_expr(expr, name),
Expression::Get { object, name: _ } => self.resolve_expression(object),
Expression::Set {
object,
name: _,
value,
} => self.resolve_set_expression(object, value),
Expression::This { keyword } => {
if self.current_class == ClassType::None {
return Err(ResolverError::ThisOutsideClass(keyword.line));
}
self.resolve_local(expr, keyword)
}
Expression::Super { keyword, method: _ } => {
self.resolve_super_expression(keyword, expr)
}
}
}
fn begin_scope(&mut self) {
self.scopes.push(HashMap::new());
}
fn end_scope(&mut self) {
self.scopes.pop();
}
fn declare(&mut self, name: &Token) -> Result<(), ResolverError> {
if let Some(scope) = self.scopes.last_mut() {
if scope.contains_key(&name.lexeme) {
return Err(ResolverError::NameRedefinition(name.line));
}
scope.insert(name.lexeme.clone(), false);
}
Ok(())
}
fn define(&mut self, name: &Token) {
if let Some(scope) = self.scopes.last_mut() {
scope.insert(name.lexeme.clone(), true);
}
}
fn resolve_block(&mut self, statements: &[Statement]) -> Result<(), ResolverError> {
self.begin_scope();
self.resolve(statements)?;
self.end_scope();
Ok(())
}
fn resolve_class_stmt(
&mut self,
name: &Token,
superclass: &Option<Expression>,
methods: &[Statement],
) -> Result<(), ResolverError> {
let enclosing_class = self.current_class;
self.current_class = ClassType::Class;
self.declare(name)?;
self.define(name);
if let Some(
superclass @ Expression::Variable {
name: superclass_name,
},
) = superclass
{
if superclass_name.lexeme == name.lexeme {
return Err(ResolverError::SelfInheritance(superclass_name.line));
}
self.current_class = ClassType::Subclass;
self.resolve_expression(superclass)?;
self.begin_scope();
if let Some(scope) = self.scopes.last_mut() {
scope.insert("super".to_string(), true);
}
}
self.begin_scope();
if let Some(scope) = self.scopes.last_mut() {
scope.insert("this".to_string(), true);
}
for method in methods {
if let Statement::Function { name, params, body } = method {
let declaration = if name.lexeme == "init" {
FunctionType::Initializer
} else {
FunctionType::Method
};
self.resolve_function(params, body, declaration)?;
}
}
self.end_scope();
if superclass.is_some() {
self.end_scope();
}
self.current_class = enclosing_class;
Ok(())
}
fn resolve_return_stmt(
&mut self,
keyword: &Token,
value: &Option<Expression>,
) -> Result<(), ResolverError> {
if self.current_fun == FunctionType::None {
return Err(ResolverError::TopLevelReturn(keyword.line));
}
if let Some(value) = value {
if self.current_fun == FunctionType::Initializer {
return Err(ResolverError::ReturnFromInitializer(keyword.line));
}
self.resolve_expression(value)
} else {
Ok(())
}
}
fn resolve_var(
&mut self,
name: &Token,
initializer: Option<Expression>,
) -> Result<(), ResolverError> {
self.declare(name)?;
if let Some(initializer) = initializer {
self.resolve_expression(&initializer)?;
}
self.define(name);
Ok(())
}
fn resolve_var_expr(&mut self, expr: &Expression, name: &Token) -> Result<(), ResolverError> {
if let Some(scope) = self.scopes.last() {
if !scope.get(&name.lexeme).unwrap_or(&true) {
return Err(ResolverError::ReadVarInOwnInitializer(name.line));
}
}
self.resolve_local(expr, name)?;
Ok(())
}
fn resolve_assign_expr(
&mut self,
expr: &Expression,
name: &Token,
val: &Expression,
) -> Result<(), ResolverError> {
self.resolve_expression(val)?;
self.resolve_local(expr, name)?;
Ok(())
}
fn resolve_binary_expr(
&mut self,
left: &Expression,
right: &Expression,
) -> Result<(), ResolverError> {
self.resolve_expression(left)?;
self.resolve_expression(right)?;
Ok(())
}
fn resolve_function_stmt(
&mut self,
name: &Token,
params: &[Token],
body: &[Statement],
) -> Result<(), ResolverError> {
self.declare(name)?;
self.define(name);
self.resolve_function(params, body, FunctionType::Function)?;
Ok(())
}
fn resolve_call_expr(
&mut self,
callee: &Expression,
args: &[Expression],
) -> Result<(), ResolverError> {
self.resolve_expression(callee)?;
for arg in args {
self.resolve_expression(arg)?;
}
Ok(())
}
fn resolve_set_expression(
&mut self,
object: &Expression,
value: &Expression,
) -> Result<(), ResolverError> {
self.resolve_expression(value)?;
self.resolve_expression(object)?;
Ok(())
}
fn resolve_super_expression(
&mut self,
keyword: &Token,
expression: &Expression,
) -> Result<(), ResolverError> {
if self.current_class == ClassType::None {
return Err(ResolverError::SuperOutsideClass(keyword.line));
} else if self.current_class != ClassType::Subclass {
return Err(ResolverError::SuperWithoutSuperclass(keyword.line));
}
self.resolve_local(expression, keyword)
}
fn resolve_if_stmt(
&mut self,
condition: &Expression,
then_branch: &Statement,
else_branch: Option<Statement>,
) -> Result<(), ResolverError> {
self.resolve_expression(condition)?;
self.resolve_statement(then_branch)?;
if let Some(stmt) = else_branch {
self.resolve_statement(&stmt)?;
}
Ok(())
}
fn resolve_while_stmt(
&mut self,
condition: &Expression,
body: &Statement,
) -> Result<(), ResolverError> {
self.resolve_expression(condition)?;
self.resolve_statement(body)?;
Ok(())
}
fn resolve_function(
&mut self,
params: &[Token],
body: &[Statement],
fun_type: FunctionType,
) -> Result<(), ResolverError> {
let enclosing_fun = self.current_fun;
self.current_fun = fun_type;
self.begin_scope();
for param in params {
self.declare(param)?;
self.define(param);
}
self.resolve(body)?;
self.end_scope();
self.current_fun = enclosing_fun;
Ok(())
}
fn resolve_local(
&mut self,
expression: &Expression,
name: &Token,
) -> Result<(), ResolverError> {
for (idx, scope) in self.scopes.iter().rev().enumerate() {
if scope.contains_key(&name.lexeme) {
self.interpreter.resolve(expression.clone(), idx);
return Ok(());
}
}
Ok(())
}
}

374
src/scanner.rs Normal file
View file

@ -0,0 +1,374 @@
//! Scan source code to create tokens out of it.
use crate::{
token::{Token, TokenType::Eof},
tokenizer::{
comment::Comment, identifier::Identifier, interface::Tokenizer, lookahead::Lookahead,
newline::Newline, number::Number, single_char::SingleChar, string::String,
whitespace::Whitespace,
},
};
use lazy_static::lazy_static;
use tracing::error;
lazy_static! {
/// Tokenizers to use in scanning. They are tried in the exact order in which they appear in
/// the list.
static ref TOKENIZERS: Vec<Box<dyn Tokenizer>> = vec![
Box::new(SingleChar),
Box::new(Whitespace),
Box::new(Newline),
Box::new(Lookahead),
Box::new(Comment),
Box::new(String),
Box::new(Number),
Box::new(Identifier),
];
}
/// Take source code as input and return a list of tokens representing it.
pub fn tokenize(source: &str) -> Vec<Token> {
let mut tokens: Vec<Token> = Vec::new();
let mut source_chars = source.char_indices().peekable();
let mut line = 1;
while let Some(c) = source_chars.next() {
// careful, a tokenizer run can move the iterator but I have not found a more ergonomic variant yet
match TOKENIZERS
.iter()
.find_map(|x| x.run(c, &mut source_chars, source, line))
{
Some((line_advance, token)) => {
// I do not like handling it this way, but it suffices for now
line += line_advance;
if let Some(token) = token {
tokens.push(token);
}
}
None => {
error!("unexpected character: {}", c.1)
}
}
}
// Eof is always the last token
tokens.push(Token {
token_type: Eof,
lexeme: "".to_string(),
literal: None,
line,
});
tokens
}
#[cfg(test)]
mod tests {
use ordered_float::OrderedFloat;
use crate::token::Literal;
use crate::token::Token;
use crate::token::TokenType::*;
use super::tokenize;
const FIBONACCI: &str = r#"
// first 21 elements in the Fibonacci sequence
var a = 0;
var temp;
for (var b = 1; a < 10000; b = temp + b) {
print a;
temp = a;
a = b;
}
"#;
#[test]
fn floating_points() {
assert_eq!(
vec![Token {
token_type: Number,
lexeme: "0".into(),
literal: Some(Literal::Number(OrderedFloat(0.0))),
line: 1
},],
vec![Token {
token_type: Number,
lexeme: "0".into(),
literal: Some(Literal::Number(OrderedFloat(0.0))),
line: 1
},]
);
}
#[test]
fn multiline_string() {
let input = r#""Hello,
world
!""#;
let tokens = tokenize(input);
assert_eq!(
tokens,
vec![
Token {
token_type: String,
lexeme: "\"Hello,\n world\n!\"".into(),
literal: Some(Literal::String("Hello,\n world\n!".into())),
line: 1
},
Token {
token_type: Eof,
lexeme: "".into(),
literal: None,
line: 3
},
]
);
}
#[test]
fn fibonacci_tokens() {
let tokens = tokenize(FIBONACCI);
assert_eq!(
tokens,
vec![
Token {
token_type: Var,
lexeme: "var".into(),
literal: None,
line: 3
},
Token {
token_type: Identifier,
lexeme: "a".into(),
literal: None,
line: 3
},
Token {
token_type: Equal,
lexeme: "=".into(),
literal: None,
line: 3
},
Token {
token_type: Number,
lexeme: "0".into(),
literal: Some(Literal::Number(OrderedFloat(0.0))),
line: 3
},
Token {
token_type: Semicolon,
lexeme: ";".into(),
literal: None,
line: 3
},
Token {
token_type: Var,
lexeme: "var".into(),
literal: None,
line: 4
},
Token {
token_type: Identifier,
lexeme: "temp".into(),
literal: None,
line: 4
},
Token {
token_type: Semicolon,
lexeme: ";".into(),
literal: None,
line: 4
},
Token {
token_type: For,
lexeme: "for".into(),
literal: None,
line: 6
},
Token {
token_type: LeftParen,
lexeme: "(".into(),
literal: None,
line: 6
},
Token {
token_type: Var,
lexeme: "var".into(),
literal: None,
line: 6
},
Token {
token_type: Identifier,
lexeme: "b".into(),
literal: None,
line: 6
},
Token {
token_type: Equal,
lexeme: "=".into(),
literal: None,
line: 6
},
Token {
token_type: Number,
lexeme: "1".into(),
literal: Some(Literal::Number(OrderedFloat(1.0))),
line: 6
},
Token {
token_type: Semicolon,
lexeme: ";".into(),
literal: None,
line: 6
},
Token {
token_type: Identifier,
lexeme: "a".into(),
literal: None,
line: 6
},
Token {
token_type: Less,
lexeme: "<".into(),
literal: None,
line: 6
},
Token {
token_type: Number,
lexeme: "10000".into(),
literal: Some(Literal::Number(OrderedFloat(10000.0))),
line: 6
},
Token {
token_type: Semicolon,
lexeme: ";".into(),
literal: None,
line: 6
},
Token {
token_type: Identifier,
lexeme: "b".into(),
literal: None,
line: 6
},
Token {
token_type: Equal,
lexeme: "=".into(),
literal: None,
line: 6
},
Token {
token_type: Identifier,
lexeme: "temp".into(),
literal: None,
line: 6
},
Token {
token_type: Plus,
lexeme: "+".into(),
literal: None,
line: 6
},
Token {
token_type: Identifier,
lexeme: "b".into(),
literal: None,
line: 6
},
Token {
token_type: RightParen,
lexeme: ")".into(),
literal: None,
line: 6
},
Token {
token_type: LeftBrace,
lexeme: "{".into(),
literal: None,
line: 6
},
Token {
token_type: Print,
lexeme: "print".into(),
literal: None,
line: 7
},
Token {
token_type: Identifier,
lexeme: "a".into(),
literal: None,
line: 7
},
Token {
token_type: Semicolon,
lexeme: ";".into(),
literal: None,
line: 7
},
Token {
token_type: Identifier,
lexeme: "temp".into(),
literal: None,
line: 8
},
Token {
token_type: Equal,
lexeme: "=".into(),
literal: None,
line: 8
},
Token {
token_type: Identifier,
lexeme: "a".into(),
literal: None,
line: 8
},
Token {
token_type: Semicolon,
lexeme: ";".into(),
literal: None,
line: 8
},
Token {
token_type: Identifier,
lexeme: "a".into(),
literal: None,
line: 9
},
Token {
token_type: Equal,
lexeme: "=".into(),
literal: None,
line: 9
},
Token {
token_type: Identifier,
lexeme: "b".into(),
literal: None,
line: 9
},
Token {
token_type: Semicolon,
lexeme: ";".into(),
literal: None,
line: 9
},
Token {
token_type: RightBrace,
lexeme: "}".into(),
literal: None,
line: 10
},
Token {
token_type: Eof,
lexeme: "".into(),
literal: None,
line: 11
},
]
);
}
}

36
src/statement.rs Normal file
View file

@ -0,0 +1,36 @@
use crate::{expression::Expression, token::Token};
/// Enumeration of all types of statements.
#[derive(Debug, Clone, PartialEq)]
pub enum Statement {
Block(Vec<Statement>),
Print(Expression),
Expression(Expression),
Var {
name: Token,
initializer: Box<Option<Expression>>,
},
If {
condition: Expression,
then_branch: Box<Statement>,
else_branch: Option<Box<Statement>>,
},
While {
condition: Expression,
body: Box<Statement>,
},
Function {
name: Token,
params: Vec<Token>,
body: Vec<Statement>,
},
Return {
keyword: Token,
value: Option<Expression>,
},
Class {
name: Token,
superclass: Option<Expression>,
methods: Vec<Statement>,
},
}

93
src/token.rs Normal file
View file

@ -0,0 +1,93 @@
//! Token values and data structs.
use std::fmt::Display;
use ordered_float::OrderedFloat;
/// Exhaustive enumeration of all types of different tokens.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
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,
Number,
// Keywords.
And,
Class,
Else,
False,
Fun,
For,
If,
Nil,
Or,
Print,
Return,
Super,
This,
True,
Var,
While,
Eof,
}
/// Literal value.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum Literal {
/// String literal.
String(String),
// Number literal, represented as f64 (thus it can be decimal).
Number(OrderedFloat<f64>),
/// Boolean literal.
Boolean(bool),
/// Null literal.
Nil,
}
/// Consumed token.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Token {
/// Type of the token.
pub token_type: TokenType,
/// Lexeme that was consumed to create this token.
pub lexeme: String,
/// Literal value of the token, if any.
pub literal: Option<Literal>,
/// Starting line on which the token was oonsumed from the source.
pub line: usize,
}
impl Display for Token {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}: {:?} {} {:?}",
self.line, self.token_type, self.lexeme, self.literal
)
}
}

43
src/tokenizer/comment.rs Normal file
View file

@ -0,0 +1,43 @@
use crate::token::TokenType::Slash;
use std::{iter::Peekable, str::CharIndices};
use crate::token::Token;
use super::interface::Tokenizer;
/// Consume comments.
///
/// A comment starts with '//' and runs until the end of the line.
/// If only one '/' is seen, it is consumed as a Slash token.
pub struct Comment;
impl Tokenizer for Comment {
fn run(
&self,
c: (usize, char),
chars: &mut Peekable<CharIndices<'_>>,
source: &str,
line: usize,
) -> Option<(usize, Option<Token>)> {
match c.1 {
'/' => {
let (line_advance, token) = if chars.next_if(|(_, peek)| *peek == '/').is_some() {
while chars.next_if(|(_, peek)| *peek != '\n').is_some() {}
chars.next();
(1, None)
} else {
(
0,
Some(Token {
token_type: Slash,
lexeme: source[c.0..=c.0].to_string(),
literal: None,
line,
}),
)
};
Some((line_advance, token))
}
_ => None,
}
}
}

View file

@ -0,0 +1,46 @@
use crate::{
keywords::KEYWORDS,
token::{Token, TokenType},
};
use std::{iter::Peekable, str::CharIndices};
use super::interface::Tokenizer;
/// Consume an identifier which also might be a keyword.
///
/// An identifier starts with an alphabetic character and goes on consuming alphanumeric and
/// underscore characters until the first different one.
pub struct Identifier;
impl Tokenizer for Identifier {
fn run(
&self,
c: (usize, char),
chars: &mut Peekable<CharIndices<'_>>,
source: &str,
line: usize,
) -> Option<(usize, Option<Token>)> {
if c.1.is_alphabetic() || c.1 == '_' {
let mut end_idx = c.0;
while let Some((idx, _)) = chars.next_if(|(_, x)| x.is_alphanumeric() || c.1 == '_') {
end_idx = idx;
}
let lexeme = source[c.0..=end_idx].to_string();
let token_type = match KEYWORDS.get(&lexeme) {
Some(token_type) => *token_type,
None => TokenType::Identifier,
};
Some((
0,
Some(Token {
token_type,
lexeme,
literal: None,
line,
}),
))
} else {
None
}
}
}

View file

@ -0,0 +1,18 @@
use std::{iter::Peekable, str::CharIndices};
use crate::token::Token;
/// Interface to implement by a tokenizer.
pub trait Tokenizer: Send + Sync {
/// Take a tuple consisting of the index of a char and the char itself, the whole source code
/// iterator, the source itself and the current line. Return None if you can not handle the
/// current lexeme or an Option consisting of a tuple where the first element is how much the
/// current line moved and the second element is an Option that can have the consumed token.
fn run(
&self,
c: (usize, char),
chars: &mut Peekable<CharIndices<'_>>,
source: &str,
line: usize,
) -> Option<(usize, Option<Token>)>;
}

View file

@ -0,0 +1,97 @@
use super::interface::Tokenizer;
use crate::token::TokenType::*;
use crate::token::{Token, TokenType};
use lazy_static::lazy_static;
use std::{collections::HashMap, iter::Peekable, str::CharIndices};
/// Data for one and two character lexemes.
struct LookaheadEntry {
/// TokenType if a lexeme is a one character one.
default_token: TokenType,
/// Mapping of second level character to a TokenType.
lookahead_map: HashMap<char, TokenType>,
}
lazy_static! {
/// Mapping of one and two character lexemes, specifiyng the one character variant and as many
/// two character ones as needed.
static ref LOOKAHEAD_TOKENS: HashMap<char, LookaheadEntry> = {
let mut m = HashMap::new();
let mut bang_map = HashMap::new();
bang_map.insert('=', BangEqual);
m.insert(
'!',
LookaheadEntry {
default_token: Bang,
lookahead_map: bang_map,
},
);
let mut equal_map = HashMap::new();
equal_map.insert('=', EqualEqual);
m.insert(
'=',
LookaheadEntry {
default_token: Equal,
lookahead_map: equal_map,
},
);
let mut less_map = HashMap::new();
less_map.insert('=', LessEqual);
m.insert(
'<',
LookaheadEntry {
default_token: Less,
lookahead_map: less_map,
},
);
let mut greater_map = HashMap::new();
greater_map.insert('=', GreaterEqual);
m.insert(
'>',
LookaheadEntry {
default_token: Greater,
lookahead_map: greater_map,
},
);
m
};
}
/// Consume lexemes that consist of exactly one or two characters.
pub struct Lookahead;
impl Tokenizer for Lookahead {
fn run(
&self,
c: (usize, char),
chars: &mut Peekable<CharIndices<'_>>,
source: &str,
line: usize,
) -> Option<(usize, Option<Token>)> {
LOOKAHEAD_TOKENS.get(&c.1).map(|entry| {
let (lexeme, token_type) = if let Some(&(_, peeked)) = chars.peek() {
if let Some(&token_type) = entry.lookahead_map.get(&peeked) {
chars.next();
(source[c.0..=c.0 + 1].to_string(), token_type)
} else {
(source[c.0..=c.0].to_string(), entry.default_token)
}
} else {
(source[c.0..=c.0].to_string(), entry.default_token)
};
(
0,
Some(Token {
token_type,
lexeme,
literal: None,
line,
}),
)
})
}
}

20
src/tokenizer/newline.rs Normal file
View file

@ -0,0 +1,20 @@
use super::interface::Tokenizer;
use crate::token::Token;
use std::{iter::Peekable, str::CharIndices};
/// Consume newlines. Do not yield a token but increase the current line.
pub struct Newline;
impl Tokenizer for Newline {
fn run(
&self,
c: (usize, char),
_chars: &mut Peekable<CharIndices<'_>>,
_source: &str,
_line: usize,
) -> Option<(usize, Option<Token>)> {
match c.1 {
'\n' => Some((1, None)),
_ => None,
}
}
}

47
src/tokenizer/number.rs Normal file
View file

@ -0,0 +1,47 @@
use crate::token::{Literal, Token, TokenType};
use ordered_float::OrderedFloat;
use std::{iter::Peekable, str::CharIndices};
use tracing::error;
use super::interface::Tokenizer;
/// Consume a number literal. Numbers can have one decimal point.
pub struct Number;
impl Tokenizer for Number {
fn run(
&self,
c: (usize, char),
chars: &mut Peekable<CharIndices<'_>>,
source: &str,
line: usize,
) -> Option<(usize, Option<Token>)> {
if c.1.is_ascii_digit() {
let mut end_idx = c.0;
while let Some((idx, _)) = chars.next_if(|(_, x)| x.is_ascii_digit()) {
end_idx = idx;
}
chars.next_if(|(_, x)| *x == '.');
while let Some((idx, _)) = chars.next_if(|(_, x)| x.is_ascii_digit()) {
end_idx = idx;
}
let lexeme = source[c.0..=end_idx].to_string();
let token = match lexeme.parse::<f64>() {
Ok(literal) => Some(Token {
token_type: TokenType::Number,
lexeme,
literal: Some(Literal::Number(OrderedFloat(literal))),
line,
}),
Err(e) => {
error!("failed to parse number: {e}");
None
}
};
Some((0, token))
} else {
None
}
}
}

View file

@ -0,0 +1,51 @@
use super::interface::Tokenizer;
use crate::token::{
Token,
TokenType::{self, *},
};
use lazy_static::lazy_static;
use std::{collections::HashMap, iter::Peekable, str::CharIndices};
lazy_static! {
/// Mapping of single characters to their respective TokenType.
static ref SINGLE_CHAR_TOKENS: HashMap<char, TokenType> = {
let mut m = HashMap::new();
m.insert('(', LeftParen);
m.insert(')', RightParen);
m.insert('{', LeftBrace);
m.insert('}', RightBrace);
m.insert(',', Comma);
m.insert('.', Dot);
m.insert('-', Minus);
m.insert('+', Plus);
m.insert(';', Semicolon);
m.insert('*', Star);
m
};
}
/// Consume a single character and produce its corresponding token.
pub struct SingleChar;
impl Tokenizer for SingleChar {
fn run(
&self,
c: (usize, char),
_chars: &mut Peekable<CharIndices<'_>>,
source: &str,
line: usize,
) -> Option<(usize, Option<Token>)> {
let lexeme = source[c.0..=c.0].to_string();
SINGLE_CHAR_TOKENS.get(&c.1).map(|token_type| {
(
0,
Some(Token {
token_type: *token_type,
lexeme,
literal: None,
line,
}),
)
})
}
}

51
src/tokenizer/string.rs Normal file
View file

@ -0,0 +1,51 @@
use super::interface::Tokenizer;
use crate::token::{Literal, Token, TokenType};
use std::{iter::Peekable, str::CharIndices};
use tracing::error;
/// Consume a string literal.
///
/// A string literal consists of everything between two '"' and can stretch across multiple lines.
pub struct String;
impl Tokenizer for String {
fn run(
&self,
c: (usize, char),
chars: &mut Peekable<CharIndices<'_>>,
source: &str,
line: usize,
) -> Option<(usize, Option<Token>)> {
match c.1 {
'"' => {
let mut lines = 0;
while let Some((_, c)) = chars.next_if(|(_, peek)| *peek != '"') {
if c == '\n' {
lines += 1;
}
}
match chars.next_if(|(_, x)| *x == '"') {
Some((end_idx, _)) => {
let lexeme = source[c.0..=end_idx].to_string();
let literal = source[c.0 + 1..end_idx].to_string();
Some((
lines,
Some(Token {
token_type: TokenType::String,
lexeme,
literal: Some(Literal::String(literal)),
line,
}),
))
}
None => {
error!("unterminated string");
None
}
}
}
_ => None,
}
}
}

View file

@ -0,0 +1,20 @@
use super::interface::Tokenizer;
use crate::token::Token;
use std::{iter::Peekable, str::CharIndices};
/// Consume and ignore whitespace characters.
pub struct Whitespace;
impl Tokenizer for Whitespace {
fn run(
&self,
c: (usize, char),
_chars: &mut Peekable<CharIndices<'_>>,
_source: &str,
_line: usize,
) -> Option<(usize, Option<Token>)> {
match c.1 {
' ' | '\r' | '\t' => Some((0, None)),
_ => None,
}
}
}

59
src/value.rs Normal file
View file

@ -0,0 +1,59 @@
use std::{cell::RefCell, fmt::Display, rc::Rc};
use ordered_float::OrderedFloat;
use crate::{callable::Callable, instance, token::Literal};
#[derive(Clone, Debug, PartialEq)]
pub enum CallableType {
Function,
Class(crate::class::Class),
}
/// Concrete value in the interpreter.
#[derive(Clone, Debug, PartialEq)]
pub enum Value {
Return(Box<Value>),
Callable((Rc<dyn Callable>, CallableType)),
String(String),
Number(OrderedFloat<f64>),
Boolean(bool),
Instance(Rc<RefCell<instance::Instance>>),
Nil,
}
impl Display for Value {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Value::Return(x) => write!(f, "{x}"),
Value::Callable(x) => write!(f, "{}", x.0),
Value::String(x) => write!(f, "{x}"),
Value::Number(x) => write!(f, "{x}"),
Value::Boolean(x) => write!(f, "{x}"),
Value::Instance(x) => write!(f, "{}", x.borrow()),
Value::Nil => write!(f, "nil"),
}
}
}
impl Value {
/// Return false for nil or a false boolean, true otherwise.
pub fn is_truthy(&self) -> bool {
match self {
Value::Nil => false,
Value::Boolean(b) => *b,
_ => true,
}
}
}
impl From<Literal> for Value {
fn from(value: Literal) -> Self {
match value {
Literal::String(x) => Value::String(x),
Literal::Number(x) => Value::Number(x),
Literal::Boolean(x) => Value::Boolean(x),
Literal::Nil => Value::Nil,
}
}
}