implement chapter 11 in rust
This commit is contained in:
parent
8860a1c639
commit
a25b6d1e92
16 changed files with 470 additions and 32 deletions
9
lox/fibonacci.lox
Normal file
9
lox/fibonacci.lox
Normal file
|
@ -0,0 +1,9 @@
|
|||
var a = 0;
|
||||
var temp;
|
||||
|
||||
var b = 1;
|
||||
for(var b = 1; a < 1000; b = temp + b) {
|
||||
print a;
|
||||
temp = a;
|
||||
a = b;
|
||||
}
|
4
lox/name_redefinition.lox
Normal file
4
lox/name_redefinition.lox
Normal file
|
@ -0,0 +1,4 @@
|
|||
fun bad() {
|
||||
var a = "first";
|
||||
var a = "second";
|
||||
}
|
10
lox/scoping.lox
Normal file
10
lox/scoping.lox
Normal file
|
@ -0,0 +1,10 @@
|
|||
var a = "global";
|
||||
{
|
||||
fun showA() {
|
||||
print a;
|
||||
}
|
||||
|
||||
showA();
|
||||
var a = "block";
|
||||
showA();
|
||||
}
|
1
lox/top_level_return.lox
Normal file
1
lox/top_level_return.lox
Normal file
|
@ -0,0 +1 @@
|
|||
return "at top level";
|
25
rust/rox/Cargo.lock
generated
25
rust/rox/Cargo.lock
generated
|
@ -61,6 +61,12 @@ dependencies = [
|
|||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
|
@ -162,12 +168,30 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
|
||||
|
||||
[[package]]
|
||||
name = "ordered-float"
|
||||
version = "5.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2c1f9f56e534ac6a9b8a4600bdf0f530fb393b5f393e7b4d03489c3cf0c3f01"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "overload"
|
||||
version = "0.1.1"
|
||||
|
@ -248,6 +272,7 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"clap",
|
||||
"lazy_static",
|
||||
"ordered-float",
|
||||
"thiserror",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
|
|
|
@ -8,6 +8,7 @@ authors = ["Sebastian Hugentobler <shu@vanwa.ch>"]
|
|||
[dependencies]
|
||||
clap = { version = "4.5.28", features = ["derive"] }
|
||||
lazy_static = "1.5.0"
|
||||
ordered-float = "5.0.0"
|
||||
thiserror = "2.0.11"
|
||||
tracing = "0.1.41"
|
||||
tracing-subscriber = { version = "0.3.19", features = ["env-filter"] }
|
||||
|
|
|
@ -8,6 +8,8 @@ use crate::{token::Token, value::Value};
|
|||
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
|
||||
|
@ -50,6 +52,18 @@ impl Environment {
|
|||
}
|
||||
}
|
||||
|
||||
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> {
|
||||
|
@ -64,4 +78,29 @@ impl Environment {
|
|||
))
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::token::{self, Token};
|
||||
|
||||
/// Enumeration of all types of expressions.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum Expression {
|
||||
Assign {
|
||||
name: Token,
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use std::{cell::RefCell, rc::Rc};
|
||||
use std::{cell::RefCell, collections::HashMap, rc::Rc};
|
||||
|
||||
use ordered_float::OrderedFloat;
|
||||
use thiserror::Error;
|
||||
use tracing::error;
|
||||
|
||||
|
@ -39,6 +40,7 @@ pub enum InterpreterError {
|
|||
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.
|
||||
|
@ -57,6 +59,7 @@ impl Default for Interpreter {
|
|||
Self {
|
||||
globals: env.clone(),
|
||||
environment: env.clone(),
|
||||
locals: HashMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -74,6 +77,10 @@ impl Interpreter {
|
|||
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 {
|
||||
|
@ -145,7 +152,7 @@ impl Interpreter {
|
|||
operator,
|
||||
right,
|
||||
} => self.binary(left, operator, right),
|
||||
Expression::Variable { name } => self.var_expression(name),
|
||||
Expression::Variable { name } => self.var_expression(name, expression),
|
||||
Expression::Assign { name, value } => self.assign(name, value),
|
||||
Expression::Logical {
|
||||
left,
|
||||
|
@ -276,13 +283,26 @@ impl Interpreter {
|
|||
}
|
||||
|
||||
/// Assign the value of an expression to a variable.
|
||||
fn assign(&mut self, name: &Token, value: &Expression) -> Result<Value, InterpreterError> {
|
||||
let value = self.evaluate(value)?;
|
||||
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)
|
||||
}
|
||||
|
||||
|
@ -340,12 +360,31 @@ impl Interpreter {
|
|||
}
|
||||
|
||||
/// Get the value of a variable.
|
||||
fn var_expression(&mut self, name: &Token) -> Result<Value, InterpreterError> {
|
||||
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 {
|
||||
|
@ -383,7 +422,11 @@ impl Interpreter {
|
|||
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(self.number_op(left, op.token_type, right)))
|
||||
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 {
|
||||
|
@ -397,7 +440,11 @@ impl Interpreter {
|
|||
| 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)))
|
||||
Ok(Value::Boolean(self.boolean_op(
|
||||
*left,
|
||||
op.token_type,
|
||||
*right,
|
||||
)))
|
||||
} else {
|
||||
Err(InterpreterError::BinaryExpressionNeedsNumber(op.line))
|
||||
}
|
||||
|
|
|
@ -7,7 +7,10 @@ use std::{
|
|||
path::Path,
|
||||
};
|
||||
|
||||
use interpreter::Interpreter;
|
||||
use interpreter::{Interpreter, InterpreterError};
|
||||
use parser::ParserError;
|
||||
use resolver::{Resolver, ResolverError};
|
||||
use thiserror::Error;
|
||||
use tracing::error;
|
||||
|
||||
pub mod callable;
|
||||
|
@ -19,6 +22,7 @@ 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;
|
||||
|
@ -35,6 +39,16 @@ pub mod tokenizer {
|
|||
}
|
||||
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)?;
|
||||
|
@ -64,12 +78,18 @@ pub fn repl() {
|
|||
|
||||
/// Evaluate a Lox input string and print errors or output.
|
||||
fn run(input: &str, interpreter: &mut Interpreter) {
|
||||
let tokens = scanner::tokenize(input);
|
||||
match parser::ast(tokens) {
|
||||
Ok(ast) => match interpreter.run(ast) {
|
||||
Ok(_) => {}
|
||||
Err(e) => error!("{e}"),
|
||||
},
|
||||
Err(e) => error!("{e}"),
|
||||
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(())
|
||||
}
|
||||
|
|
|
@ -3,6 +3,8 @@ use std::{
|
|||
time::{SystemTime, UNIX_EPOCH},
|
||||
};
|
||||
|
||||
use ordered_float::OrderedFloat;
|
||||
|
||||
use crate::{
|
||||
callable::{Callable, CallingError},
|
||||
interpreter::Interpreter,
|
||||
|
@ -30,7 +32,7 @@ impl Callable for Clock {
|
|||
.duration_since(UNIX_EPOCH)
|
||||
.map_err(|e| CallingError::CallFailed(e.to_string()))?;
|
||||
|
||||
Ok(Value::Number(since_the_epoch.as_secs_f64()))
|
||||
Ok(Value::Number(OrderedFloat(since_the_epoch.as_secs_f64())))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
273
rust/rox/src/resolver.rs
Normal file
273
rust/rox/src/resolver.rs
Normal file
|
@ -0,0 +1,273 @@
|
|||
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),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
enum FunctionType {
|
||||
None,
|
||||
Function,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Resolver<'a> {
|
||||
scopes: Vec<HashMap<String, bool>>,
|
||||
interpreter: &'a mut Interpreter,
|
||||
current_fun: FunctionType,
|
||||
}
|
||||
|
||||
impl<'a> Resolver<'a> {
|
||||
pub fn new(interpreter: &'a mut Interpreter) -> Self {
|
||||
Self {
|
||||
scopes: Vec::default(),
|
||||
interpreter,
|
||||
current_fun: FunctionType::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 } => {
|
||||
if self.current_fun == FunctionType::None {
|
||||
return Err(ResolverError::TopLevelReturn(keyword.line));
|
||||
}
|
||||
if let Some(value) = value {
|
||||
self.resolve_expression(value)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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),
|
||||
}
|
||||
}
|
||||
|
||||
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_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_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(())
|
||||
}
|
||||
}
|
|
@ -64,6 +64,8 @@ pub fn tokenize(source: &str) -> Vec<Token> {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use ordered_float::OrderedFloat;
|
||||
|
||||
use crate::token::Literal;
|
||||
use crate::token::Token;
|
||||
use crate::token::TokenType::*;
|
||||
|
@ -88,13 +90,13 @@ mod tests {
|
|||
vec![Token {
|
||||
token_type: Number,
|
||||
lexeme: "0".into(),
|
||||
literal: Some(Literal::Number(0.0)),
|
||||
literal: Some(Literal::Number(OrderedFloat(0.0))),
|
||||
line: 1
|
||||
},],
|
||||
vec![Token {
|
||||
token_type: Number,
|
||||
lexeme: "0".into(),
|
||||
literal: Some(Literal::Number(0.0)),
|
||||
literal: Some(Literal::Number(OrderedFloat(0.0))),
|
||||
line: 1
|
||||
},]
|
||||
);
|
||||
|
@ -153,7 +155,7 @@ mod tests {
|
|||
Token {
|
||||
token_type: Number,
|
||||
lexeme: "0".into(),
|
||||
literal: Some(Literal::Number(0.0)),
|
||||
literal: Some(Literal::Number(OrderedFloat(0.0))),
|
||||
line: 3
|
||||
},
|
||||
Token {
|
||||
|
@ -213,7 +215,7 @@ mod tests {
|
|||
Token {
|
||||
token_type: Number,
|
||||
lexeme: "1".into(),
|
||||
literal: Some(Literal::Number(1.0)),
|
||||
literal: Some(Literal::Number(OrderedFloat(1.0))),
|
||||
line: 6
|
||||
},
|
||||
Token {
|
||||
|
@ -237,7 +239,7 @@ mod tests {
|
|||
Token {
|
||||
token_type: Number,
|
||||
lexeme: "10000".into(),
|
||||
literal: Some(Literal::Number(10000.0)),
|
||||
literal: Some(Literal::Number(OrderedFloat(10000.0))),
|
||||
line: 6
|
||||
},
|
||||
Token {
|
||||
|
|
|
@ -2,8 +2,10 @@
|
|||
|
||||
use std::fmt::Display;
|
||||
|
||||
use ordered_float::OrderedFloat;
|
||||
|
||||
/// Exhaustive enumeration of all types of different tokens.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum TokenType {
|
||||
// Single-character tokens.
|
||||
LeftParen,
|
||||
|
@ -55,12 +57,12 @@ pub enum TokenType {
|
|||
}
|
||||
|
||||
/// Literal value.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum Literal {
|
||||
/// String literal.
|
||||
String(String),
|
||||
// Number literal, represented as f64 (thus it can be decimal).
|
||||
Number(f64),
|
||||
Number(OrderedFloat<f64>),
|
||||
/// Boolean literal.
|
||||
Boolean(bool),
|
||||
/// Null literal.
|
||||
|
@ -68,7 +70,7 @@ pub enum Literal {
|
|||
}
|
||||
|
||||
/// Consumed token.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct Token {
|
||||
/// Type of the token.
|
||||
pub token_type: TokenType,
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use crate::token::{Literal, Token, TokenType};
|
||||
use ordered_float::OrderedFloat;
|
||||
use std::{iter::Peekable, str::CharIndices};
|
||||
use tracing::error;
|
||||
|
||||
|
@ -29,7 +30,7 @@ impl Tokenizer for Number {
|
|||
Ok(literal) => Some(Token {
|
||||
token_type: TokenType::Number,
|
||||
lexeme,
|
||||
literal: Some(Literal::Number(literal)),
|
||||
literal: Some(Literal::Number(OrderedFloat(literal))),
|
||||
line,
|
||||
}),
|
||||
Err(e) => {
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
use std::{fmt::Display, rc::Rc};
|
||||
|
||||
use ordered_float::OrderedFloat;
|
||||
|
||||
use crate::{callable::Callable, token::Literal};
|
||||
|
||||
/// Concrete value in the interpreter.
|
||||
|
@ -8,7 +10,7 @@ pub enum Value {
|
|||
Return(Box<Value>),
|
||||
Callable(Rc<dyn Callable>),
|
||||
String(String),
|
||||
Number(f64),
|
||||
Number(OrderedFloat<f64>),
|
||||
Boolean(bool),
|
||||
Nil,
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue