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

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

19
interpreter/src/class.rs Normal file
View file

@ -0,0 +1,19 @@
use std::fmt::Display;
#[derive(Debug)]
pub struct LoxClass {
name: String,
}
impl LoxClass {
pub fn new(name: impl Into<String>) -> Self {
let name = name.into();
LoxClass { name }
}
}
impl Display for LoxClass {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "<class {}>", self.name)
}
}

View file

@ -0,0 +1,209 @@
use std::collections::HashMap;
use std::fmt::Display;
use std::io::{Read, Write};
use crate::error::RuntimeError;
use super::value::HeapedValue;
use super::{Runtime, Value};
pub type Scope = HashMap<String, HeapedValue>;
#[derive(Debug)]
pub struct Environment<'a> {
local_scopes: Vec<Scope>,
runtime: &'a mut Runtime,
}
impl<'a> Environment<'a> {
pub fn new(runtime: &'a mut Runtime) -> Self {
Environment {
// globals: HashMap::new(),
// frames: vec![Frame::new_global()],
local_scopes: Vec::new(),
runtime,
}
}
pub fn globals(&self) -> &HashMap<String, Value> {
self.runtime.globals()
}
pub fn enter_scope(&mut self) {
// self.current_frame_mut().enter_scope();
self.local_scopes.push(Scope::new());
}
pub fn push_scope(&mut self, scope: Scope) {
self.local_scopes.push(scope);
}
pub fn exit_scope(&mut self) {
// self.current_frame_mut().exit_scope();
self.local_scopes.pop().expect("Tried to pop global scope");
}
pub fn get_global(&self, name: &str) -> Result<Value, RuntimeError> {
self.runtime.get_global(name)
}
pub fn get_local(&self, name: &str, level: usize) -> Result<Value, RuntimeError> {
if let Some(scope) = self.local_scopes.iter().rev().nth(level) {
if let Some(heap_value) = scope.get(name) {
return Ok(heap_value.get_clone());
}
}
// DEBUG
println!("Name {name} not defined at level {level}");
println!("{self}");
Err(RuntimeError::NameNotDefined { name: name.to_owned() })
// self.runtime.get_global(name)
}
pub fn define(&mut self, name: impl Into<String>, value: Value) {
if let Some(scope) = self.local_scopes.last_mut() {
let name = name.into();
scope.insert(name, HeapedValue::new(value));
} else {
self.runtime.define_global(name, value)
}
}
pub fn assign_global(&mut self, name: &str, value: Value) -> Result<(), RuntimeError> {
self.runtime.assign_global(name, value)
}
pub fn assign(&mut self, name: &str, value: Value, level: usize) -> Result<(), RuntimeError> {
if let Some(scope) = self.local_scopes.iter_mut().rev().nth(level) {
if let Some(heap_value) = scope.get_mut(name) {
heap_value.replace(value);
return Ok(());
}
}
Err(RuntimeError::NameNotDefined { name: name.to_owned() })
// self.runtime.assign_global(name, value)
}
/* pub fn push_frame(&mut self, base_scope: Scope) {
self.frames.push(Frame::new(base_scope));
}
pub fn pop_frame(&mut self) {
self.frames.pop().expect("Tried to pop global frame");
} */
pub fn collect_closure(&self, closure_vars: &[(String, usize)]) -> Scope {
let mut closure_scope = Scope::new();
for (name, level) in closure_vars {
let heap_value = self
.local_scopes
.iter()
.rev()
.nth(*level)
.unwrap()
.get(name)
.unwrap()
.clone();
let name = name.to_owned();
closure_scope.insert(name, heap_value);
}
closure_scope
}
}
impl Display for Environment<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.runtime)?;
for (level, scope) in self.local_scopes.iter().enumerate() {
write!(f, "\nScope {level}:")?;
for (name, value) in scope.iter() {
write!(f, "\n\t{name} = {value}")?;
}
}
Ok(())
}
}
impl Read for Environment<'_> {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
self.runtime.read(buf)
}
}
impl Write for Environment<'_> {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
self.runtime.write(buf)
}
fn flush(&mut self) -> std::io::Result<()> {
self.runtime.flush()
}
}
/*====================================================================================================================*/
/* struct Frame {
scopes: Vec<Scope>,
}
impl Frame {
fn new(base_scope: Scope) -> Self {
Frame {
scopes: vec![base_scope],
}
}
fn new_global() -> Self {
Frame { scopes: Vec::new() }
}
fn enter_scope(&mut self) {
self.scopes.push(Scope::new());
}
fn exit_scope(&mut self) {
self.scopes.pop().expect("Tried to exit scope, but no scope to exit");
}
fn get(&self, name: &str) -> Option<Value> {
for scope in self.scopes.iter().rev() {
if let Some(heap_value) = scope.get(name) {
return Some(heap_value.get_clone());
}
}
None
}
fn try_define(&mut self, name: impl Into<String> + Borrow<str>, value: Value) -> Result<(), Value> {
if let Some(scope) = self.scopes.last_mut() {
scope.insert(name.into(), HeapValue::new(value));
Ok(())
} else {
Err(value)
}
}
fn try_assign(&mut self, name: &str, value: Value) -> Result<(), Value> {
for scope in self.scopes.iter_mut().rev() {
if let Some(heap_value) = scope.get_mut(name) {
heap_value.replace(value);
return Ok(());
}
}
Err(value)
}
} */

85
interpreter/src/error.rs Normal file
View file

@ -0,0 +1,85 @@
use itertools::Itertools;
use rlox2_frontend::lexer::LexerError;
use rlox2_frontend::parser::{BinaryOp, ParserError, UnaryOp};
use thiserror::Error;
use crate::ResolverError;
use crate::Value;
#[derive(Error, Debug)]
pub enum RuntimeError {
#[error("Unary operator {op} had invalid argument {arg}")]
UnaryOpInvalidArgument { op: UnaryOp, arg: Value },
#[error("Binary operator {op} had invalid arguments {left} and {right}")]
BinaryOpInvalidArguments { left: Value, op: BinaryOp, right: Value },
#[error("Division by zero")]
DivisionByZero,
#[error("Name {name} is not defined")]
NameNotDefined { name: String },
#[error("Global {name} is not defined")]
GlobalNotDefined { name: String },
#[error("{callee} is not callable")]
NotCallable { callee: Value },
#[error("{name}() takes {arity} args, but {given} were given.")]
WrongArity { name: String, arity: usize, given: usize },
#[error("Extern function call to {name} failed: {msg}")]
ExtFunCallFailed { name: String, msg: String },
#[error("Uncaught break statement")]
Break,
#[error("Uncaught return statement")]
Return { value: Value },
#[error("Exit with exit code {exit_code}")]
Exit { exit_code: i32 },
}
#[derive(Error, Debug)]
#[allow(clippy::enum_variant_names)]
pub enum LoxError {
#[error("{0}", format_multiple_errors(inner))]
LexerError { inner: Vec<LexerError> },
#[error("{0}", format_multiple_errors(inner))]
ParserError { inner: Vec<ParserError> },
#[error("{inner}")]
ResolverError { inner: ResolverError },
#[error("{inner}")]
RuntimeError { inner: RuntimeError },
#[error("Called exit() with exit code {exit_code}")]
Exit { exit_code: i32 },
}
fn format_multiple_errors(errs: &Vec<impl std::error::Error>) -> String {
let msg = if errs.len() == 1 {
errs[0].to_string()
} else {
errs.iter().map(|err| err.to_string()).join("\n")
};
msg
}
impl From<Vec<LexerError>> for LoxError {
fn from(lexer_errs: Vec<LexerError>) -> Self {
LoxError::LexerError { inner: lexer_errs }
}
}
impl From<Vec<ParserError>> for LoxError {
fn from(parser_errs: Vec<ParserError>) -> Self {
LoxError::ParserError { inner: parser_errs }
}
}
impl From<ResolverError> for LoxError {
fn from(resolver_err: ResolverError) -> Self {
LoxError::ResolverError { inner: resolver_err }
}
}
impl From<RuntimeError> for LoxError {
fn from(runtime_err: RuntimeError) -> Self {
match runtime_err {
RuntimeError::Exit { exit_code } => LoxError::Exit { exit_code },
_ => LoxError::RuntimeError { inner: runtime_err },
}
}
}

104
interpreter/src/function.rs Normal file
View file

@ -0,0 +1,104 @@
use std::fmt::{Debug, Display};
use rlox2_frontend::parser::Stmt;
use super::environment::{Environment, Scope};
use super::interpret::EvalResult;
use super::{Runtime, Value};
#[derive(Debug, Clone)]
pub struct LoxFunction {
name: String,
closure: Scope,
param_names: Vec<String>,
body: Box<Stmt>,
}
impl LoxFunction {
pub fn new(name: impl Into<String>, closure: Scope, param_names: Vec<String>, body: Stmt) -> Self {
let name = name.into();
let body = Box::new(body);
LoxFunction {
name,
closure,
param_names,
body,
}
}
pub fn arity(&self) -> usize {
self.param_names.len()
}
pub fn name(&self) -> &str {
&self.name
}
pub fn closure(&self) -> &Scope {
&self.closure
}
pub fn param_names(&self) -> impl Iterator<Item = &String> {
self.param_names.iter()
}
pub fn body(&self) -> &Stmt {
&self.body
}
}
impl Display for LoxFunction {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "<fun {}>", self.name)
}
}
/*====================================================================================================================*/
pub type ExternFunClosure = fn(Vec<Value>, &mut Environment) -> EvalResult<Value>;
#[derive(Clone)]
pub struct LoxExternFunction {
name: String,
closure: ExternFunClosure,
}
impl LoxExternFunction {
pub fn new(name: impl Into<String>, closure: ExternFunClosure) -> Self {
let name = name.into();
LoxExternFunction { name, closure }
}
pub fn register(self, env: &mut Runtime) {
let name = self.name.clone();
let fun = Value::extern_function(self);
env.define_global(name, fun);
}
pub fn call(&self, args: Vec<Value>, env: &mut Environment) -> EvalResult<Value> {
(self.closure)(args, env)
}
}
impl Display for LoxExternFunction {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "<extern fun {}>", self.name)
}
}
impl Debug for LoxExternFunction {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("LoxExternFunction")
.field("name", &self.name)
.field("closure", &"<closure>")
.finish()
}
}
impl PartialEq for LoxExternFunction {
fn eq(&self, other: &Self) -> bool {
self.name == other.name
}
}

View file

@ -0,0 +1,250 @@
use std::rc::Rc;
use rlox2_frontend::parser::{BinaryOp, Expr, Literal, LogicalOp, Stmt, UnaryOp};
use crate::error::RuntimeError;
use crate::LoxClass;
use super::environment::Environment;
use super::{LoxFunction, Runtime, Value};
pub type EvalResult<T> = Result<T, RuntimeError>;
/*====================================================================================================================*/
pub fn execute(statement: Stmt, runtime: &mut Runtime) -> Result<(), RuntimeError> {
let mut env = Environment::new(runtime);
statement.eval(&mut env)?;
Ok(())
}
/*====================================================================================================================*/
trait Eval {
fn eval(&self, env: &mut Environment) -> EvalResult<Value>;
}
impl Eval for Literal {
fn eval(&self, env: &mut Environment) -> EvalResult<Value> {
let _ = env;
match self {
Literal::String(s) => Ok(Value::String(Rc::clone(s))),
Literal::Number(num) => Ok(Value::Number(*num)),
Literal::Bool(b) => Ok(Value::Bool(*b)),
Literal::Nil => Ok(Value::Nil),
}
}
}
impl Eval for Expr {
fn eval(&self, env: &mut Environment) -> EvalResult<Value> {
match self {
Expr::Literal { literal } => literal.eval(env),
Expr::Unary { op, expr } => {
let arg = expr.eval(env)?;
match (*op, arg) {
(UnaryOp::Negate, Value::Number(num)) => Ok(Value::Number(-num)),
(UnaryOp::Not, Value::Bool(b)) => Ok(Value::Bool(!b)),
(UnaryOp::Not, primitive) => Ok(Value::Bool(!primitive.is_truthy())),
(op, arg) => Err(RuntimeError::UnaryOpInvalidArgument { op, arg }),
}
}
Expr::Binary { left, op, right } => {
use Value::{Bool, Number, String};
let left = left.eval(env)?;
let right = right.eval(env)?;
match (left, *op, right) {
(Number(left), BinaryOp::Add, Number(right)) => Ok(Number(left + right)),
(Number(left), BinaryOp::Subtract, Number(right)) => Ok(Number(left - right)),
(Number(left), BinaryOp::Multiply, Number(right)) => Ok(Number(left * right)),
(Number(left), BinaryOp::Divide, Number(right)) => {
if right == 0.0 {
return Err(RuntimeError::DivisionByZero);
}
Ok(Number(left / right))
}
(String(left), BinaryOp::Add, String(right)) => {
let mut s = std::string::String::with_capacity(left.capacity() + right.capacity());
s += &left;
s += &right;
Ok(String(Rc::new(s)))
}
(left, BinaryOp::Equal, right) => Ok(Bool(left == right)),
(left, BinaryOp::NotEqual, right) => Ok(Bool(left != right)),
(Number(left), BinaryOp::Less, Number(right)) => Ok(Bool(left < right)),
(Number(left), BinaryOp::LessEqual, Number(right)) => Ok(Bool(left <= right)),
(Number(left), BinaryOp::Greater, Number(right)) => Ok(Bool(left > right)),
(Number(left), BinaryOp::GreaterEqual, Number(right)) => Ok(Bool(left >= right)),
(String(left), BinaryOp::Less, String(right)) => Ok(Bool(left < right)),
(String(left), BinaryOp::LessEqual, String(right)) => Ok(Bool(left <= right)),
(String(left), BinaryOp::Greater, String(right)) => Ok(Bool(left > right)),
(String(left), BinaryOp::GreaterEqual, String(right)) => Ok(Bool(left >= right)),
(left, op, right) => Err(RuntimeError::BinaryOpInvalidArguments { left, op, right }),
}
}
Expr::Logical { left, op, right } => {
let left = left.eval(env)?;
match op {
LogicalOp::Or => {
if left.is_truthy() {
return Ok(left);
}
}
LogicalOp::And => {
if !left.is_truthy() {
return Ok(left);
}
}
}
let right = right.eval(env)?;
Ok(right)
}
Expr::Grouping { expr } => expr.eval(env),
Expr::Variable { name } => panic!("Unresolved variable {name}"),
Expr::LocalVariable { name, level } => env.get_local(name, *level),
Expr::GlobalVariable { name } => env.get_global(name),
Expr::Assignment { target, value } => {
let value = value.eval(env)?;
match target.as_ref() {
Expr::LocalVariable { name, level } => env.assign(name, value.clone(), *level)?,
Expr::GlobalVariable { name } => env.assign_global(name, value.clone())?,
_ => panic!("Invalid assigment target {target}"),
}
Ok(value)
}
Expr::Call { callee, args } => {
let callee = callee.eval(env)?;
let args = args
.iter()
.map(|arg| arg.eval(env))
.collect::<EvalResult<Vec<Value>>>()?;
match callee {
Value::Function(fun) => fun.call(args, env),
Value::ExternFunction(ext_fun) => ext_fun.call(args, env),
_ => Err(RuntimeError::NotCallable { callee }),
}
}
Expr::Function {
name,
param_names,
closure_vars,
body,
} => Ok(Value::function(LoxFunction::new(
name,
env.collect_closure(closure_vars),
param_names.clone(),
body.as_ref().clone(),
))),
}
}
}
impl Eval for Stmt {
fn eval(&self, env: &mut Environment) -> EvalResult<Value> {
match self {
Stmt::Print { expr } => {
match expr.eval(env)? {
// special case: when printing a string, drop the surrounding ""
Value::String(s) => println!("{s}"),
val => println!("{val}"),
}
}
Stmt::IfStmt {
condition,
then_branch,
else_branch,
} => {
let condition = condition.eval(env)?;
if condition.is_truthy() {
then_branch.eval(env)?;
} else if let Some(else_branch) = else_branch {
else_branch.eval(env)?;
}
}
Stmt::While { condition, body } => {
while condition.eval(env)?.is_truthy() {
match body.eval(env) {
Ok(_) => {}
Err(RuntimeError::Break) => break,
Err(err) => return Err(err),
}
}
}
Stmt::VarDecl { name, initializer } => {
let initializer = initializer.eval(env)?;
env.define(name, initializer);
}
Stmt::Block { statements } => {
env.enter_scope();
for statement in statements {
if let Err(err) = statement.eval(env) {
env.exit_scope();
return Err(err);
}
}
env.exit_scope();
}
Stmt::Class { name, methods: _ } => {
env.define(name, Value::Nil);
let class = Value::class(LoxClass::new(name));
env.assign(name, class, 0)?;
}
Stmt::ExprStmt { expr } => {
// expr.eval(env)?;
// Ok(Value::Nil)
expr.eval(env)?;
}
Stmt::Break => return Err(RuntimeError::Break),
Stmt::Return { expr } => {
let value = expr.eval(env)?;
return Err(RuntimeError::Return { value });
}
}
Ok(Value::Nil)
}
}
/*====================================================================================================================*/
impl LoxFunction {
pub fn call(&self, args: Vec<Value>, env: &mut Environment) -> EvalResult<Value> {
if args.len() != self.arity() {
return Err(RuntimeError::WrongArity {
name: self.name().to_owned(),
arity: self.arity(),
given: args.len(),
});
}
env.push_scope(self.closure().clone());
for (name, value) in std::iter::zip(self.param_names(), args) {
env.define(name, value);
}
let ret_val = match self.body().eval(env) {
Ok(_) => Ok(Value::Nil),
Err(RuntimeError::Return { value }) => Ok(value),
Err(err) => Err(err),
};
env.exit_scope();
ret_val
}
}

18
interpreter/src/lib.rs Normal file
View file

@ -0,0 +1,18 @@
mod class;
mod environment;
mod error;
mod function;
mod interpret;
mod lox_std;
mod resolver;
mod run;
mod runtime;
mod value;
pub use class::LoxClass;
pub use error::{LoxError, RuntimeError};
pub use function::LoxFunction;
pub use resolver::{resolve, ResolverError};
pub use run::{run_file, run_repl};
pub use runtime::Runtime;
pub use value::Value;

113
interpreter/src/lox_std.rs Normal file
View file

@ -0,0 +1,113 @@
use crate::error::RuntimeError;
use super::function::{ExternFunClosure, LoxExternFunction};
use super::{Runtime, Value};
pub fn init_std(env: &mut Runtime) {
clock().register(env);
exit().register(env);
input().register(env);
print_env().register(env);
print_globals().register(env);
}
fn input() -> LoxExternFunction {
let closure: ExternFunClosure = |args, _env| match *args {
[] => {
let mut buf = String::new();
std::io::stdin()
.read_line(&mut buf)
.map_err(|err| RuntimeError::ExtFunCallFailed {
name: "input".to_owned(),
msg: format!("input() failed to read from stdin: {err}"),
})?;
assert_eq!(buf.pop(), Some('\n'));
Ok(Value::string(buf))
}
_ => Err(RuntimeError::ExtFunCallFailed {
name: "input".to_owned(),
msg: "input() takes no arguments".to_owned(),
}),
};
LoxExternFunction::new("input", closure)
}
fn clock() -> LoxExternFunction {
let closure: ExternFunClosure = |args, _env| match *args {
[] => {
let time = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs_f64();
Ok(Value::Number(time))
}
_ => Err(RuntimeError::ExtFunCallFailed {
name: "clock".to_owned(),
msg: "clock() takes no arguments".to_owned(),
}),
};
LoxExternFunction::new("clock", closure)
}
fn print_globals() -> LoxExternFunction {
let closure: ExternFunClosure = |args, env| match *args {
[] => {
let mut globals: Vec<(&String, &Value)> = env.globals().iter().collect();
globals.sort_by_key(|&(name, _value)| name);
for (name, value) in globals {
println!("{name}: {value}");
}
Ok(Value::Nil)
}
_ => Err(RuntimeError::ExtFunCallFailed {
name: "print_globals".to_owned(),
msg: "print_globals() takes no arguments".to_owned(),
}),
};
LoxExternFunction::new("print_globals", closure)
}
fn print_env() -> LoxExternFunction {
let closure: ExternFunClosure = |args, env| match *args {
[] => {
println!("{env}");
Ok(Value::Nil)
}
_ => Err(RuntimeError::ExtFunCallFailed {
name: "print_env".to_owned(),
msg: "print_env() takes no arguments".to_owned(),
}),
};
LoxExternFunction::new("print_env", closure)
}
fn exit() -> LoxExternFunction {
let closure: ExternFunClosure = |args, _env| {
match &*args {
[] => return Err(RuntimeError::Exit { exit_code: 0 }),
[Value::Number(exit_code)] => {
if exit_code.fract() == 0.0 {
let exit_code = *exit_code as i32;
return Err(RuntimeError::Exit { exit_code });
}
}
_ => {}
}
Err(RuntimeError::ExtFunCallFailed {
name: "exit".to_owned(),
msg: "Arguments to exit() must an integer or nothing for exit code 0".to_owned(),
})
};
LoxExternFunction::new("exit", closure)
}

View file

@ -0,0 +1,11 @@
use thiserror::Error;
#[derive(Error, Debug)]
pub enum ResolverError {
#[error("Can't read variable {name} in its own initializer.")]
VarInOwnInitializer { name: String },
#[error("Could not resolve variable {name}")]
UnresolvableVariable { name: String },
#[error("Return outside of function definition")]
ReturnOutsideFunction,
}

View file

@ -0,0 +1,5 @@
mod error;
mod resolve;
pub use error::ResolverError;
pub use resolve::resolve;

View file

@ -0,0 +1,276 @@
use std::collections::HashMap;
use rlox2_frontend::parser::{Expr, Stmt};
use crate::Runtime;
use crate::{LoxError, ResolverError};
/*====================================================================================================================*/
type ResolverScope = HashMap<String, ResolveStatus>;
type ResolverFrame = Vec<ResolverScope>;
/*====================================================================================================================*/
#[derive(Debug, Clone, Copy)]
enum ResolveStatus {
Declared,
Defined,
}
/*====================================================================================================================*/
pub fn resolve(statement: &mut Stmt, runtime: &mut Runtime) -> Result<(), LoxError> {
let global_names = runtime.globals().keys().cloned();
let mut resolver = Resolver::new(global_names);
resolver.resolve_stmt(statement)?;
Ok(())
}
#[derive(Debug)]
struct Resolver {
global_scope: ResolverScope,
// local_scopes: Vec<ResolverScope>,
frames: Vec<ResolverFrame>,
closure_vars: HashMap<String, usize>,
}
impl Resolver {
fn new(global_names: impl Iterator<Item = String>) -> Self {
let mut global_scope = ResolverScope::new();
for name in global_names {
global_scope.insert(name, ResolveStatus::Defined);
}
Resolver {
global_scope,
frames: vec![ResolverFrame::new()],
closure_vars: HashMap::new(),
}
}
fn local_scopes(&self) -> &ResolverFrame {
self.frames.last().unwrap()
}
fn local_scopes_mut(&mut self) -> &mut ResolverFrame {
self.frames.last_mut().unwrap()
}
fn enter_scope(&mut self) {
self.local_scopes_mut().push(ResolverScope::new());
}
fn exit_scope(&mut self) {
self.local_scopes_mut()
.pop()
.expect("Tried to pop from empty ResolverFrame");
}
fn push_frame(&mut self) {
self.frames.push(vec![ResolverScope::new()]);
}
fn pop_frame(&mut self) {
self.frames.pop().expect("Tried to pop non-existant ResolverFrame");
}
fn declare(&mut self, name: &str) {
let name = name.to_owned();
if let Some(local_scope) = self.local_scopes_mut().last_mut() {
local_scope.insert(name, ResolveStatus::Declared);
} else {
self.global_scope.insert(name, ResolveStatus::Declared);
}
}
fn define(&mut self, name: &str) {
let name = name.to_owned();
if let Some(local_scope) = self.local_scopes_mut().last_mut() {
local_scope.insert(name, ResolveStatus::Defined);
} else {
self.global_scope.insert(name, ResolveStatus::Declared);
}
}
fn check(&self, name: &str) -> Option<ResolveStatus> {
if let Some(local_scope) = self.local_scopes().last() {
local_scope.get(name).cloned()
} else {
self.global_scope.get(name).cloned()
}
}
fn resolve_var(&mut self, name: &str) -> Result<Expr, ResolverError> {
let name = name.to_owned();
let mut level = 0;
for scope in self.local_scopes().iter().rev() {
if scope.contains_key(&name) {
return Ok(Expr::LocalVariable { name, level });
}
// DEBUG
level += 1;
}
for frame in self.frames.iter().rev().skip(1) {
for scope in frame.iter().rev() {
if scope.contains_key(&name) {
if !self.closure_vars.contains_key(&name) {
// the level at which the closed-over variable will be collected from
// from the perspective of the parameter/closure scope i.e. the outmost of the function
self.closure_vars
.insert(name.clone(), level - self.local_scopes().len());
}
return Ok(Expr::LocalVariable {
name,
// distance from actual variable refernce to parameter/closure scope
level: self.local_scopes().len() - 1,
});
}
level += 1;
}
}
if self.global_scope.contains_key(&name) {
return Ok(Expr::GlobalVariable { name });
}
Err(ResolverError::UnresolvableVariable { name })
}
fn resolve_stmt(&mut self, stmt: &mut Stmt) -> Result<(), ResolverError> {
match stmt {
Stmt::Print { expr } => self.resolve_expr(expr),
Stmt::IfStmt {
condition,
then_branch,
else_branch,
} => {
self.resolve_expr(condition)?;
self.resolve_stmt(then_branch)?;
if let Some(else_branch) = else_branch {
self.resolve_stmt(else_branch)?;
}
Ok(())
}
Stmt::While { condition, body } => {
self.resolve_expr(condition)?;
self.resolve_stmt(body)?;
Ok(())
}
Stmt::VarDecl { name, initializer } => {
self.declare(name);
self.resolve_expr(initializer)?;
self.define(name);
Ok(())
}
Stmt::Block { statements } => {
self.enter_scope();
for statement in statements.iter_mut() {
self.resolve_stmt(statement)?;
}
self.exit_scope();
Ok(())
}
Stmt::Class { name, methods: _ } => {
self.declare(name);
self.define(name);
Ok(())
}
Stmt::ExprStmt { expr } => self.resolve_expr(expr),
Stmt::Break => Ok(()),
Stmt::Return { expr } => self.resolve_expr(expr),
}
}
fn resolve_expr(&mut self, expr: &mut Expr) -> Result<(), ResolverError> {
match expr {
Expr::Literal { literal: _ } => Ok(()),
Expr::Unary { op: _, expr } => self.resolve_expr(expr),
Expr::Binary { left, op: _, right } => {
self.resolve_expr(left)?;
self.resolve_expr(right)?;
Ok(())
}
Expr::Logical { left, op: _, right } => {
self.resolve_expr(left)?;
self.resolve_expr(right)?;
Ok(())
}
Expr::Grouping { expr } => self.resolve_expr(expr),
Expr::Variable { name } => {
if let Some(ResolveStatus::Declared) = self.check(name) {
let name = name.clone();
return Err(ResolverError::VarInOwnInitializer { name });
}
*expr = self.resolve_var(name)?;
Ok(())
}
Expr::LocalVariable { name, level: _ } => {
panic!("Tried to resolve variable {name} twice")
}
Expr::GlobalVariable { name } => {
panic!("Tried to resolve variable {name} twice")
}
Expr::Assignment { target, value } => {
self.resolve_expr(value)?;
let target = target.as_mut();
if let Expr::Variable { name } = target {
*target = self.resolve_var(name)?;
} else {
panic!("Invalid assignment target {target}");
}
Ok(())
}
Expr::Call { callee, args } => {
self.resolve_expr(callee)?;
for arg in args.iter_mut() {
self.resolve_expr(arg)?;
}
Ok(())
}
Expr::Function {
name,
param_names,
closure_vars,
body,
} => {
let old_closure_names = std::mem::take(&mut self.closure_vars);
self.push_frame();
self.declare(name);
self.define(name);
for param_name in param_names {
self.declare(param_name);
self.define(param_name);
}
self.resolve_stmt(body)?;
self.pop_frame();
let closure_names = std::mem::replace(&mut self.closure_vars, old_closure_names);
for closure_var in closure_names {
closure_vars.push(closure_var);
}
Ok(())
}
}
}
}

116
interpreter/src/run.rs Normal file
View file

@ -0,0 +1,116 @@
use std::io::Write;
use rlox2_frontend::lexer::{scan_tokens, Token};
use rlox2_frontend::parser::parse_tokens;
use crate::error::LoxError;
use crate::interpret::execute;
use crate::resolver::resolve;
use super::Runtime;
pub fn run_file(runtime: &mut Runtime, script_path: &str) {
let source_code = std::fs::read_to_string(script_path).unwrap_or_else(|err| {
eprintln!("Reading script file {script_path} failed: {err}");
std::process::exit(66);
});
/* if let Err(err) = run(&source_code) {
eprintln!("{}", err);
std::process::exit(65);
} */
match run(&source_code, runtime) {
Ok(()) => {}
Err(err) => {
eprintln!("{err}");
match err {
LoxError::LexerError { .. } | LoxError::ParserError { .. } | LoxError::ResolverError { .. } => {
std::process::exit(65)
}
LoxError::RuntimeError { .. } => std::process::exit(70),
LoxError::Exit { exit_code } => std::process::exit(exit_code),
}
}
}
}
pub fn run_repl(runtime: &mut Runtime) {
let stdin = std::io::stdin();
loop {
let mut input_buf = String::new();
print!("> ");
std::io::stdout().flush().unwrap();
'inner: loop {
stdin.read_line(&mut input_buf).unwrap_or_else(|err| {
eprintln!("Could not read from stdin: {err}");
std::process::exit(66);
});
let num_open_braces = (input_buf.matches('{').count() as i64) - (input_buf.matches('}').count() as i64);
let num_open_parens = (input_buf.matches('(').count() as i64) - (input_buf.matches(')').count() as i64);
let num_open_brackets = (input_buf.matches('[').count() as i64) - (input_buf.matches(']').count() as i64);
// all braces/parens/brackets closed => break
if num_open_braces == 0 && num_open_parens == 0 && num_open_brackets == 0 {
break 'inner;
}
// any braces/parens/brackets more closing than opening => break (will be parse error)
if num_open_braces < 0 || num_open_parens < 0 || num_open_brackets < 0 {
break 'inner;
}
print!("< ");
// let indentation = " ".repeat((num_open_braces + num_open_brackets + num_open_parens) as usize);
// print!("{indentation}");
std::io::stdout().flush().unwrap();
}
let input_buf = input_buf.trim();
if input_buf.is_empty() || input_buf == "exit" || input_buf == "quit" {
std::process::exit(0);
}
match run(input_buf, runtime) {
Ok(()) => {}
Err(LoxError::Exit { exit_code }) => std::process::exit(exit_code),
Err(err) => eprintln!("{err}"),
}
}
}
fn run(code_string: &str, runtime: &mut Runtime) -> Result<(), LoxError> {
let tokens: Vec<Token> = scan_tokens(code_string)?;
/* let token_str = tokens
.iter()
.map(|token| token.to_string())
.join(" ");
println!("{token_str}"); */
let statements = parse_tokens(tokens)?;
/* for statement in statements.iter() {
println!("{statement}");
} */
/* for statement in statements.iter() {
println!("{statement}");
} */
for mut statement in statements {
resolve(&mut statement, runtime)?;
execute(statement, runtime)?;
}
Ok(())
}

101
interpreter/src/runtime.rs Normal file
View file

@ -0,0 +1,101 @@
use std::collections::HashMap;
use std::fmt::Display;
use std::io::{stdin, stdout, Read, Write};
use crate::error::RuntimeError;
use super::lox_std::init_std;
use super::Value;
pub struct Runtime {
globals: HashMap<String, Value>,
in_stream: Box<dyn std::io::BufRead>,
out_stream: Box<dyn std::io::Write>,
}
impl Runtime {
pub fn new(in_stream: Box<dyn std::io::Read>, out_stream: Box<dyn std::io::Write>) -> Self {
let in_stream = Box::new(std::io::BufReader::new(in_stream));
let mut runtime = Runtime {
globals: HashMap::new(),
in_stream,
out_stream,
};
init_std(&mut runtime);
runtime
}
pub fn globals(&self) -> &HashMap<String, Value> {
&self.globals
}
pub fn get_global(&self, name: &str) -> Result<Value, RuntimeError> {
self.globals
.get(name)
.cloned()
.ok_or_else(|| RuntimeError::GlobalNotDefined { name: name.to_owned() })
}
pub fn define_global(&mut self, name: impl Into<String>, value: Value) {
let name = name.into();
self.globals.insert(name, value);
}
pub fn assign_global(&mut self, name: &str, value: Value) -> Result<(), RuntimeError> {
if let Some(old_value) = self.globals.get_mut(name) {
*old_value = value;
Ok(())
} else {
Err(RuntimeError::GlobalNotDefined { name: name.to_owned() })
}
}
}
impl Default for Runtime {
fn default() -> Self {
Runtime::new(Box::new(std::io::BufReader::new(stdin())), Box::new(stdout()))
}
}
impl std::fmt::Debug for Runtime {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Runtime").field("globals", &self.globals).finish()
}
}
impl Display for Runtime {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Globals:")?;
for (name, value) in self.globals.iter() {
write!(f, "\n\t{name} = {value}")?;
}
Ok(())
}
}
impl Drop for Runtime {
fn drop(&mut self) {
self.out_stream.flush().unwrap();
}
}
impl Read for Runtime {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
self.in_stream.read(buf)
}
}
impl Write for Runtime {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
self.out_stream.write(buf)
}
fn flush(&mut self) -> std::io::Result<()> {
self.out_stream.flush()
}
}

115
interpreter/src/value.rs Normal file
View file

@ -0,0 +1,115 @@
use std::cell::UnsafeCell;
use std::fmt::{Debug, Display};
use std::rc::Rc;
use crate::LoxClass;
use super::function::LoxExternFunction;
use super::LoxFunction;
#[derive(Debug, Clone)]
pub enum Value {
Class(Rc<LoxClass>),
Function(Rc<LoxFunction>),
ExternFunction(Rc<LoxExternFunction>),
String(Rc<String>),
Number(f64),
Bool(bool),
Nil,
}
impl Value {
pub fn class(class: LoxClass) -> Self {
let class = Rc::new(class);
Value::Class(class)
}
pub fn function(fun: LoxFunction) -> Self {
let fun = Rc::new(fun);
Value::Function(fun)
}
pub fn extern_function(fun: LoxExternFunction) -> Self {
let fun = Rc::new(fun);
Value::ExternFunction(fun)
}
pub fn string(s: impl Into<String>) -> Self {
let s = Rc::new(s.into());
Value::String(s)
}
pub fn is_truthy(&self) -> bool {
!matches!(self, Value::Bool(false) | Value::Nil)
}
}
impl Display for Value {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Value::Class(class) => write!(f, "{class}"),
Value::Function(fun) => write!(f, "{fun}"),
Value::ExternFunction(ext_fun) => write!(f, "{ext_fun}"),
Value::String(s) => write!(f, "\"{s}\""),
Value::Number(num) => write!(f, "{num}"),
Value::Bool(b) => write!(f, "{b}"),
Value::Nil => write!(f, "nil"),
}
}
}
impl PartialEq for Value {
fn eq(&self, other: &Value) -> bool {
match (self, other) {
(Value::Function(l0), Value::Function(r0)) => Rc::ptr_eq(l0, r0),
(Value::ExternFunction(l0), Value::ExternFunction(r0)) => Rc::ptr_eq(l0, r0),
(Value::String(l0), Value::String(r0)) => l0 == r0,
(Value::Number(l0), Value::Number(r0)) => l0 == r0,
(Value::Bool(l0), Value::Bool(r0)) => l0 == r0,
(Value::Nil, Value::Nil) => true,
_ => false,
}
}
}
/*====================================================================================================================*/
#[derive(Clone)]
pub struct HeapedValue {
inner: Rc<UnsafeCell<Value>>,
}
impl HeapedValue {
pub fn new(value: Value) -> Self {
let inner = Rc::new(UnsafeCell::new(value));
HeapedValue { inner }
}
pub fn get_clone(&self) -> Value {
unsafe {
let ptr = self.inner.get();
let value = &*ptr;
value.clone()
}
}
pub fn replace(&mut self, value: Value) {
let mut value = value;
unsafe {
let ptr_mut = self.inner.get();
std::ptr::swap(&mut value, ptr_mut);
}
}
}
impl Debug for HeapedValue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("HeapValue").field("inner", &self.get_clone()).finish()
}
}
impl Display for HeapedValue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.get_clone())
}
}