mirror of
https://github.com/MorizzG/rlox.git
synced 2025-12-06 04:12:42 +00:00
updated a bunch of stuff
This commit is contained in:
parent
660464638f
commit
67bb5fe8fd
24 changed files with 683 additions and 702 deletions
|
|
@ -10,7 +10,7 @@ use crate::{LoxFunction, LoxReference, Value};
|
|||
pub struct LoxClass {
|
||||
superclass: Option<Rc<LoxClass>>,
|
||||
|
||||
name: String,
|
||||
name: Box<str>,
|
||||
|
||||
methods: FxHashMap<String, Value>,
|
||||
}
|
||||
|
|
@ -18,7 +18,7 @@ pub struct LoxClass {
|
|||
/// Representation of a class in Lox. Always behind an Rc to ensure uniqueness. Should never be handled raw.
|
||||
impl LoxClass {
|
||||
pub fn new(
|
||||
name: impl Into<String>,
|
||||
name: impl Into<Box<str>>,
|
||||
methods: FxHashMap<String, Value>,
|
||||
superclass: Option<Rc<LoxClass>>,
|
||||
) -> Rc<Self> {
|
||||
|
|
@ -33,10 +33,7 @@ impl LoxClass {
|
|||
let mut body = init.body().clone();
|
||||
|
||||
if let Stmt::Block { ref mut statements } = body {
|
||||
statements.push(Stmt::return_stmt(Expr::LocalVariable {
|
||||
name: "this".to_owned(),
|
||||
level: 1,
|
||||
}));
|
||||
statements.push(Stmt::return_stmt(Expr::local_variable("this", 1)));
|
||||
} else {
|
||||
panic!("Body of init method of class {name} wasn't a block");
|
||||
}
|
||||
|
|
@ -130,7 +127,12 @@ impl LoxClass {
|
|||
|
||||
impl Display for LoxClass {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "<class {} at {:#X}>", self.name, (self as *const LoxClass) as usize)
|
||||
write!(
|
||||
f,
|
||||
"<class {} at {:#X}>",
|
||||
self.name,
|
||||
(self as *const LoxClass) as usize
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,12 +8,11 @@ use crate::error::RuntimeError;
|
|||
use super::value::HeapedValue;
|
||||
use super::{Runtime, Value};
|
||||
|
||||
pub type Scope = FxHashMap<String, HeapedValue>;
|
||||
pub type Scope = FxHashMap<Box<str>, HeapedValue>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Environment<'a> {
|
||||
scope_stack: Vec<Scope>,
|
||||
scope_top: usize,
|
||||
|
||||
runtime: &'a mut Runtime,
|
||||
|
||||
|
|
@ -24,7 +23,6 @@ impl<'a> Environment<'a> {
|
|||
pub fn new(runtime: &'a mut Runtime) -> Self {
|
||||
Environment {
|
||||
scope_stack: Vec::new(),
|
||||
scope_top: 0,
|
||||
|
||||
runtime,
|
||||
|
||||
|
|
@ -32,6 +30,10 @@ impl<'a> Environment<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn runtime(&mut self) -> &mut Runtime {
|
||||
self.runtime
|
||||
}
|
||||
|
||||
pub fn push_arg(&mut self, arg: Value) {
|
||||
self.arg_stack.push(arg);
|
||||
}
|
||||
|
|
@ -44,16 +46,16 @@ impl<'a> Environment<'a> {
|
|||
std::mem::take(&mut self.arg_stack)
|
||||
}
|
||||
|
||||
pub fn globals(&self) -> &FxHashMap<String, Value> {
|
||||
pub fn globals(&self) -> &FxHashMap<Box<str>, Value> {
|
||||
self.runtime.globals()
|
||||
}
|
||||
|
||||
pub fn scopes(&self) -> &[Scope] {
|
||||
&self.scope_stack[..self.scope_top]
|
||||
&self.scope_stack
|
||||
}
|
||||
|
||||
pub fn scopes_mut(&mut self) -> &mut [Scope] {
|
||||
&mut self.scope_stack[..self.scope_top]
|
||||
&mut self.scope_stack
|
||||
}
|
||||
|
||||
pub fn has_scope(&self) -> bool {
|
||||
|
|
@ -61,30 +63,24 @@ impl<'a> Environment<'a> {
|
|||
}
|
||||
|
||||
pub fn current_scope(&self) -> &Scope {
|
||||
&self.scope_stack[self.scope_top - 1]
|
||||
self.scope_stack.last().unwrap()
|
||||
}
|
||||
|
||||
pub fn current_scope_mut(&mut self) -> &mut Scope {
|
||||
&mut self.scope_stack[self.scope_top - 1]
|
||||
self.scope_stack.last_mut().unwrap()
|
||||
}
|
||||
|
||||
pub fn enter_scope(&mut self) {
|
||||
// self.current_frame_mut().enter_scope();
|
||||
// self.local_scopes.push(Scope::new());
|
||||
|
||||
if self.scope_top == self.scope_stack.len() {
|
||||
self.scope_stack.push(Scope::default());
|
||||
} else {
|
||||
self.scope_stack[self.scope_top].clear();
|
||||
}
|
||||
|
||||
self.scope_top += 1;
|
||||
self.scope_stack.push(Scope::default());
|
||||
}
|
||||
|
||||
pub fn exit_scope(&mut self) {
|
||||
// self.current_frame_mut().exit_scope();
|
||||
// self.local_scopes.pop().expect("Tried to pop global scope");
|
||||
self.scope_top -= 1;
|
||||
self.scope_stack.pop();
|
||||
}
|
||||
|
||||
pub fn insert_closure(&mut self, closure: Scope) {
|
||||
|
|
@ -108,14 +104,17 @@ impl<'a> Environment<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
Err(RuntimeError::NameNotDefined { name: name.to_owned() })
|
||||
Err(RuntimeError::NameNotDefined {
|
||||
name: name.to_owned(),
|
||||
})
|
||||
// self.runtime.get_global(name)
|
||||
}
|
||||
|
||||
pub fn define(&mut self, name: impl Into<String>, value: Value) {
|
||||
pub fn define(&mut self, name: impl Into<Box<str>>, value: Value) {
|
||||
if self.has_scope() {
|
||||
let name = name.into();
|
||||
self.current_scope_mut().insert(name, HeapedValue::new(value));
|
||||
self.current_scope_mut()
|
||||
.insert(name, HeapedValue::new(value));
|
||||
} else {
|
||||
self.runtime.define_global(name, value)
|
||||
}
|
||||
|
|
@ -133,7 +132,9 @@ impl<'a> Environment<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
Err(RuntimeError::NameNotDefined { name: name.to_owned() })
|
||||
Err(RuntimeError::NameNotDefined {
|
||||
name: name.to_owned(),
|
||||
})
|
||||
// self.runtime.assign_global(name, value)
|
||||
}
|
||||
|
||||
|
|
@ -145,12 +146,12 @@ impl<'a> Environment<'a> {
|
|||
self.frames.pop().expect("Tried to pop global frame");
|
||||
} */
|
||||
|
||||
pub fn collect_closure(&self, closure_vars: &[(String, usize)]) -> Scope {
|
||||
pub fn collect_closure(&self, closure_vars: &[(Box<str>, usize)]) -> Scope {
|
||||
let mut closure_scope = Scope::default();
|
||||
|
||||
for (name, level) in closure_vars {
|
||||
// special injected variables
|
||||
if name == "this" || name == "super" {
|
||||
if &**name == "this" || &**name == "super" {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -181,7 +182,7 @@ impl Display for Environment<'_> {
|
|||
write!(f, "\nScope {level}:")?;
|
||||
|
||||
for (name, value) in scope.iter() {
|
||||
write!(f, "\n\t{name} = {value}")?;
|
||||
write!(f, "\n\t{name} = {}", value.as_ref())?;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,35 +10,43 @@ use crate::{LoxClass, ResolverError};
|
|||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum RuntimeError {
|
||||
#[error("Unary operator {op} had invalid argument {arg}")]
|
||||
#[error("RuntimeError: 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")]
|
||||
#[error("RuntimeError: Binary operator {op} had invalid arguments {left} and {right}")]
|
||||
BinaryOpInvalidArguments {
|
||||
left: Value,
|
||||
op: BinaryOp,
|
||||
right: Value,
|
||||
},
|
||||
#[error("RuntimeError: Division by zero")]
|
||||
DivisionByZero,
|
||||
#[error("Local {name} is not defined")]
|
||||
#[error("RuntimeError: Local {name} is not defined")]
|
||||
NameNotDefined { name: String },
|
||||
#[error("Global {name} is not defined")]
|
||||
#[error("RuntimeError: Global {name} is not defined")]
|
||||
GlobalNotDefined { name: String },
|
||||
#[error("{callee} is not callable")]
|
||||
#[error("RuntimeError: {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}")]
|
||||
#[error("RuntimeError: {name}() takes {arity} args, but {given} were given.")]
|
||||
WrongArity {
|
||||
name: String,
|
||||
arity: usize,
|
||||
given: usize,
|
||||
},
|
||||
#[error("RuntimeError: Extern function call to {name} failed: {msg}")]
|
||||
ExtFunCallFailed { name: String, msg: String },
|
||||
#[error("Uncaught break statement")]
|
||||
#[error("RuntimeError: Uncaught break statement")]
|
||||
Break,
|
||||
#[error("Uncaught return statement")]
|
||||
#[error("RuntimeError: Uncaught return statement")]
|
||||
Return { value: Value },
|
||||
#[error("Exit with exit code {exit_code}")]
|
||||
#[error("RuntimeError: Exit with exit code {exit_code}")]
|
||||
Exit { exit_code: i32 },
|
||||
#[error("Only objects have attributes")]
|
||||
#[error("RuntimeError: Only objects have attributes")]
|
||||
InvalidGetTarget,
|
||||
#[error("Only objects have attributes")]
|
||||
#[error("RuntimeError: Only objects have attributes")]
|
||||
InvalidSetTarget,
|
||||
#[error("Class {0} has no property {name}", class.name())]
|
||||
UndefinedAttribute { class: Rc<LoxClass>, name: String },
|
||||
#[error("{value} is not a valid superclass")]
|
||||
#[error("RuntimeError: Class {0} has no property {name}", class.name())]
|
||||
UndefinedAttribute { class: Rc<LoxClass>, name: Box<str> },
|
||||
#[error("RuntimeError: {value} is not a valid superclass")]
|
||||
InvalidSuperclass { value: Value },
|
||||
}
|
||||
|
||||
|
|
@ -57,7 +65,7 @@ pub enum LoxError {
|
|||
Exit { exit_code: i32 },
|
||||
}
|
||||
|
||||
fn format_multiple_errors(errs: &Vec<impl std::error::Error>) -> String {
|
||||
fn format_multiple_errors(errs: &[impl std::error::Error]) -> String {
|
||||
let msg = if errs.len() == 1 {
|
||||
errs[0].to_string()
|
||||
} else {
|
||||
|
|
@ -81,7 +89,9 @@ impl From<Vec<ParserError>> for LoxError {
|
|||
|
||||
impl From<ResolverError> for LoxError {
|
||||
fn from(resolver_err: ResolverError) -> Self {
|
||||
LoxError::ResolverError { inner: resolver_err }
|
||||
LoxError::ResolverError {
|
||||
inner: resolver_err,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,14 +11,19 @@ use super::{Runtime, Value};
|
|||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct LoxFunction {
|
||||
name: String,
|
||||
name: Box<str>,
|
||||
closure: Scope,
|
||||
param_names: Vec<String>,
|
||||
param_names: Vec<Box<str>>,
|
||||
body: Box<Stmt>,
|
||||
}
|
||||
|
||||
impl LoxFunction {
|
||||
pub fn new(name: impl Into<String>, closure: Scope, param_names: Vec<String>, body: Stmt) -> Rc<Self> {
|
||||
pub fn new(
|
||||
name: impl Into<Box<str>>,
|
||||
closure: Scope,
|
||||
param_names: Vec<Box<str>>,
|
||||
body: Stmt,
|
||||
) -> Rc<Self> {
|
||||
let name = name.into();
|
||||
let body = Box::new(body);
|
||||
let fun = LoxFunction {
|
||||
|
|
@ -38,7 +43,7 @@ impl LoxFunction {
|
|||
&self.closure
|
||||
}
|
||||
|
||||
pub fn param_names(&self) -> &[String] {
|
||||
pub fn param_names(&self) -> &[Box<str>] {
|
||||
&self.param_names
|
||||
}
|
||||
|
||||
|
|
@ -50,7 +55,7 @@ impl LoxFunction {
|
|||
self.param_names().len()
|
||||
}
|
||||
|
||||
pub fn inject_closed_var(&mut self, name: impl Into<String>, value: Value) {
|
||||
pub fn inject_closed_var(&mut self, name: impl Into<Box<str>>, value: Value) {
|
||||
let name = name.into();
|
||||
let heaped_value = HeapedValue::new(value);
|
||||
self.closure.insert(name, heaped_value);
|
||||
|
|
@ -88,7 +93,11 @@ impl LoxExternFunction {
|
|||
pub fn new(name: impl Into<String>, arity: usize, closure: ExternFunClosure) -> Self {
|
||||
let name = name.into();
|
||||
|
||||
LoxExternFunction { name, arity, closure }
|
||||
LoxExternFunction {
|
||||
name,
|
||||
arity,
|
||||
closure,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &str {
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ trait Eval {
|
|||
impl Eval for Literal {
|
||||
fn eval(&self, _env: &mut Environment) -> EvalResult<Value> {
|
||||
match self {
|
||||
Literal::String(s) => Ok(Value::String(Rc::clone(s))),
|
||||
Literal::String(s) => Ok(Value::String(s.clone())),
|
||||
Literal::Number(num) => Ok(Value::Number(*num)),
|
||||
Literal::Bool(b) => Ok(Value::Bool(*b)),
|
||||
Literal::Nil => Ok(Value::Nil),
|
||||
|
|
@ -83,10 +83,10 @@ impl Eval for Expr {
|
|||
},
|
||||
(String(left), String(right)) => match op {
|
||||
BinaryOp::Add => {
|
||||
let mut s = std::string::String::with_capacity(left.capacity() + right.capacity());
|
||||
s += &left;
|
||||
s += &right;
|
||||
Ok(String(Rc::new(s)))
|
||||
let s: Box<str> =
|
||||
itertools::chain(left.as_ref().chars(), right.chars()).collect();
|
||||
|
||||
Ok(Value::string(s))
|
||||
}
|
||||
BinaryOp::Less => Ok(Bool(left < right)),
|
||||
BinaryOp::LessEqual => Ok(Bool(left <= right)),
|
||||
|
|
@ -99,14 +99,18 @@ impl Eval for Expr {
|
|||
right: String(right),
|
||||
}),
|
||||
},
|
||||
(left, right) => Err(RuntimeError::BinaryOpInvalidArguments { left, op, right }),
|
||||
(left, right) => {
|
||||
Err(RuntimeError::BinaryOpInvalidArguments { left, op, right })
|
||||
}
|
||||
}
|
||||
}
|
||||
Expr::Logical { left, op, right } => {
|
||||
let left = left.eval(env)?;
|
||||
|
||||
// shortcircuit
|
||||
if *op == LogicalOp::Or && left.is_truthy() || *op == LogicalOp::And && !left.is_truthy() {
|
||||
if *op == LogicalOp::Or && left.is_truthy()
|
||||
|| *op == LogicalOp::And && !left.is_truthy()
|
||||
{
|
||||
return Ok(left);
|
||||
}
|
||||
|
||||
|
|
@ -121,7 +125,9 @@ impl Eval for Expr {
|
|||
let value = value.eval(env)?;
|
||||
|
||||
match target.as_ref() {
|
||||
Expr::LocalVariable { name, level } => env.assign(name, value.clone(), *level)?,
|
||||
Expr::LocalVariable { name, level } => {
|
||||
env.assign(name, value.clone(), *level)?
|
||||
}
|
||||
Expr::GlobalVariable { name } => env.assign_global(name, value.clone())?,
|
||||
_ => panic!("Invalid assigment target {target}"),
|
||||
}
|
||||
|
|
@ -159,12 +165,16 @@ impl Eval for Expr {
|
|||
Err(RuntimeError::InvalidGetTarget)
|
||||
}
|
||||
}
|
||||
Expr::Set { target, name, value } => {
|
||||
Expr::Set {
|
||||
target,
|
||||
name,
|
||||
value,
|
||||
} => {
|
||||
let target = target.eval(env)?;
|
||||
|
||||
if let Value::Object(mut object) = target {
|
||||
let value = value.eval(env)?;
|
||||
object.set(name, value);
|
||||
object.set(name.clone(), value);
|
||||
Ok(Value::Nil)
|
||||
} else {
|
||||
Err(RuntimeError::InvalidSetTarget)
|
||||
|
|
@ -176,7 +186,7 @@ impl Eval for Expr {
|
|||
closure_vars,
|
||||
body,
|
||||
} => Ok(Value::function(LoxFunction::new(
|
||||
name,
|
||||
name.clone(),
|
||||
env.collect_closure(closure_vars),
|
||||
param_names.clone(),
|
||||
body.as_ref().clone(),
|
||||
|
|
@ -208,7 +218,11 @@ impl Eval for Expr {
|
|||
|
||||
env.exit_scope();
|
||||
|
||||
Ok(Value::class(LoxClass::new(name, methods, superclass)))
|
||||
Ok(Value::class(LoxClass::new(
|
||||
name.clone(),
|
||||
methods,
|
||||
superclass,
|
||||
)))
|
||||
}
|
||||
Expr::This => panic!("Unresolved this"),
|
||||
Expr::Super {
|
||||
|
|
@ -223,7 +237,9 @@ impl Eval for Expr {
|
|||
Err(RuntimeError::InvalidGetTarget)
|
||||
}
|
||||
}
|
||||
(super_val, this_val) => panic!("super evaluated to {super_val} and this evaluated to {this_val}"),
|
||||
(super_val, this_val) => {
|
||||
panic!("super evaluated to {super_val} and this evaluated to {this_val}")
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
@ -233,11 +249,10 @@ 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}"),
|
||||
}
|
||||
use std::io::Write;
|
||||
|
||||
let val = expr.eval(env)?;
|
||||
writeln!(env.runtime(), "{val}").unwrap();
|
||||
}
|
||||
Stmt::IfStmt {
|
||||
condition,
|
||||
|
|
@ -262,7 +277,7 @@ impl Eval for Stmt {
|
|||
}
|
||||
Stmt::VarDecl { name, initializer } => {
|
||||
let initializer = initializer.eval(env)?;
|
||||
env.define(name, initializer);
|
||||
env.define(name.clone(), initializer);
|
||||
}
|
||||
Stmt::Block { statements } => {
|
||||
env.enter_scope();
|
||||
|
|
@ -310,7 +325,7 @@ pub fn call_fun(fun: Rc<LoxFunction>, env: &mut Environment) -> EvalResult<Value
|
|||
env.insert_closure(fun.closure().clone());
|
||||
|
||||
for (name, value) in std::iter::zip(fun.param_names(), args) {
|
||||
env.define(name, value);
|
||||
env.define(name.clone(), value);
|
||||
}
|
||||
|
||||
let ret_val = match fun.body().eval(env) {
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ fn clock() -> LoxExternFunction {
|
|||
fn print_globals() -> LoxExternFunction {
|
||||
let closure: ExternFunClosure = |args, env| {
|
||||
assert_eq!(args.len(), 0);
|
||||
let mut globals: Vec<(&String, &Value)> = env.globals().iter().collect();
|
||||
let mut globals: Vec<(&Box<str>, &Value)> = env.globals().iter().collect();
|
||||
|
||||
globals.sort_by_key(|&(name, _value)| name);
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ use crate::{LoxClass, Value};
|
|||
struct LoxObject {
|
||||
class: Rc<LoxClass>,
|
||||
|
||||
attrs: FxHashMap<String, Value>,
|
||||
attrs: FxHashMap<Box<str>, Value>,
|
||||
}
|
||||
|
||||
impl LoxObject {
|
||||
|
|
@ -35,7 +35,7 @@ impl LoxObject {
|
|||
self.class.get_method(name, this)
|
||||
}
|
||||
|
||||
fn set(&mut self, name: impl Into<String>, value: Value) {
|
||||
fn set(&mut self, name: impl Into<Box<str>>, value: Value) {
|
||||
let name = name.into();
|
||||
self.attrs.insert(name, value);
|
||||
}
|
||||
|
|
@ -75,7 +75,7 @@ impl LoxReference {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn set(&mut self, name: impl Into<String>, value: Value) {
|
||||
pub fn set(&mut self, name: impl Into<Box<str>>, value: Value) {
|
||||
unsafe {
|
||||
let ptr = self.inner.get();
|
||||
let object = &mut *ptr;
|
||||
|
|
@ -99,7 +99,12 @@ impl Display for LoxReference {
|
|||
unsafe {
|
||||
let ptr = self.inner.get();
|
||||
let object = &*ptr;
|
||||
write!(f, "<{} object at {:#X}>", object.class().name(), ptr as usize)
|
||||
write!(
|
||||
f,
|
||||
"<{} object at {:#X}>",
|
||||
object.class().name(),
|
||||
ptr as usize
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use crate::{LoxError, ResolverError};
|
|||
|
||||
/*====================================================================================================================*/
|
||||
|
||||
type ResolverScope = FxHashMap<String, ResolveStatus>;
|
||||
type ResolverScope = FxHashMap<Box<str>, ResolveStatus>;
|
||||
type ResolverFrame = Vec<ResolverScope>;
|
||||
|
||||
/*====================================================================================================================*/
|
||||
|
|
@ -35,11 +35,11 @@ struct Resolver {
|
|||
// local_scopes: Vec<ResolverScope>,
|
||||
frames: Vec<ResolverFrame>,
|
||||
|
||||
closure_vars: FxHashMap<String, usize>,
|
||||
closure_vars: FxHashMap<Box<str>, usize>,
|
||||
}
|
||||
|
||||
impl Resolver {
|
||||
fn new(global_names: impl Iterator<Item = String>) -> Self {
|
||||
fn new(global_names: impl Iterator<Item = Box<str>>) -> Self {
|
||||
let mut global_scope = ResolverScope::default();
|
||||
|
||||
for name in global_names {
|
||||
|
|
@ -76,11 +76,14 @@ impl Resolver {
|
|||
}
|
||||
|
||||
fn pop_frame(&mut self) {
|
||||
self.frames.pop().expect("Tried to pop non-existant ResolverFrame");
|
||||
self.frames
|
||||
.pop()
|
||||
.expect("Tried to pop non-existant ResolverFrame");
|
||||
}
|
||||
|
||||
fn declare(&mut self, name: &str) {
|
||||
let name = name.to_owned();
|
||||
fn declare(&mut self, name: impl Into<Box<str>>) {
|
||||
let name = name.into();
|
||||
|
||||
if let Some(local_scope) = self.local_scopes_mut().last_mut() {
|
||||
local_scope.insert(name, ResolveStatus::Declared);
|
||||
} else {
|
||||
|
|
@ -88,8 +91,9 @@ impl Resolver {
|
|||
}
|
||||
}
|
||||
|
||||
fn define(&mut self, name: &str) {
|
||||
let name = name.to_owned();
|
||||
fn define(&mut self, name: impl Into<Box<str>>) {
|
||||
let name = name.into();
|
||||
|
||||
if let Some(local_scope) = self.local_scopes_mut().last_mut() {
|
||||
local_scope.insert(name, ResolveStatus::Defined);
|
||||
} else {
|
||||
|
|
@ -106,38 +110,36 @@ impl Resolver {
|
|||
}
|
||||
|
||||
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 });
|
||||
if scope.contains_key(name) {
|
||||
return Ok(Expr::local_variable(name, level));
|
||||
}
|
||||
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) {
|
||||
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());
|
||||
.insert(name.into(), 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,
|
||||
});
|
||||
|
||||
// distance from actual variable refernce to parameter/closure scope
|
||||
let level = self.local_scopes().len() - 1;
|
||||
|
||||
return Ok(Expr::local_variable(name, level));
|
||||
}
|
||||
level += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if self.global_scope.contains_key(&name) {
|
||||
return Ok(Expr::GlobalVariable { name });
|
||||
if self.global_scope.contains_key(name) {
|
||||
return Ok(Expr::global_variable(name));
|
||||
}
|
||||
|
||||
if name == "this" {
|
||||
|
|
@ -145,6 +147,7 @@ impl Resolver {
|
|||
} else if name == "super" {
|
||||
Err(ResolverError::SuperOutsideMethod)
|
||||
} else {
|
||||
let name = name.into();
|
||||
Err(ResolverError::UnresolvableVariable { name })
|
||||
}
|
||||
}
|
||||
|
|
@ -170,9 +173,9 @@ impl Resolver {
|
|||
Ok(())
|
||||
}
|
||||
Stmt::VarDecl { name, initializer } => {
|
||||
self.declare(name);
|
||||
self.declare(name.clone());
|
||||
self.resolve_expr(initializer)?;
|
||||
self.define(name);
|
||||
self.define(name.clone());
|
||||
Ok(())
|
||||
}
|
||||
Stmt::Block { statements } => {
|
||||
|
|
@ -244,7 +247,11 @@ impl Resolver {
|
|||
self.resolve_expr(target)?;
|
||||
Ok(())
|
||||
}
|
||||
Expr::Set { target, name: _, value } => {
|
||||
Expr::Set {
|
||||
target,
|
||||
name: _,
|
||||
value,
|
||||
} => {
|
||||
self.resolve_expr(target)?;
|
||||
self.resolve_expr(value)?;
|
||||
Ok(())
|
||||
|
|
@ -259,12 +266,12 @@ impl Resolver {
|
|||
|
||||
self.push_frame();
|
||||
|
||||
self.declare(name);
|
||||
self.define(name);
|
||||
self.declare(name.clone());
|
||||
self.define(name.clone());
|
||||
|
||||
for param_name in param_names.iter() {
|
||||
self.declare(param_name);
|
||||
self.define(param_name);
|
||||
self.declare(param_name.clone());
|
||||
self.define(param_name.clone());
|
||||
}
|
||||
|
||||
self.resolve_stmt(body)?;
|
||||
|
|
@ -284,13 +291,13 @@ impl Resolver {
|
|||
name,
|
||||
methods,
|
||||
} => {
|
||||
self.declare(name);
|
||||
self.declare(name.clone());
|
||||
|
||||
if let Some(superclass) = superclass {
|
||||
self.resolve_expr(superclass)?;
|
||||
}
|
||||
|
||||
self.define(name);
|
||||
self.define(name.clone());
|
||||
|
||||
// this is the scope "this" is defined in
|
||||
self.enter_scope();
|
||||
|
|
|
|||
|
|
@ -3,9 +3,9 @@ use thiserror::Error;
|
|||
#[derive(Error, Debug)]
|
||||
pub enum ResolverError {
|
||||
#[error("Can't read variable {name} in its own initializer.")]
|
||||
VarInOwnInitializer { name: String },
|
||||
VarInOwnInitializer { name: Box<str> },
|
||||
#[error("Variable {name} not defined")]
|
||||
UnresolvableVariable { name: String },
|
||||
UnresolvableVariable { name: Box<str> },
|
||||
#[error("Return outside of function definition")]
|
||||
ReturnOutsideFunction,
|
||||
#[error("this outside of method")]
|
||||
|
|
|
|||
|
|
@ -24,9 +24,12 @@ pub fn run_repl(runtime: &mut Runtime) {
|
|||
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);
|
||||
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 {
|
||||
|
|
@ -47,12 +50,12 @@ pub fn run_repl(runtime: &mut Runtime) {
|
|||
std::io::stdout().flush().unwrap();
|
||||
}
|
||||
|
||||
let input_buf = input_buf.trim();
|
||||
|
||||
if input_buf.is_empty() || input_buf == "exit" || input_buf == "quit" {
|
||||
if input_buf.is_empty() || input_buf == "exit\n" || input_buf == "quit\n" {
|
||||
std::process::exit(0);
|
||||
}
|
||||
|
||||
let input_buf = input_buf.trim();
|
||||
|
||||
match run(input_buf, runtime) {
|
||||
Ok(()) => {}
|
||||
Err(LoxError::Exit { exit_code }) => std::process::exit(exit_code),
|
||||
|
|
@ -64,20 +67,25 @@ pub fn run_repl(runtime: &mut Runtime) {
|
|||
pub fn run(source: &str, runtime: &mut Runtime) -> Result<(), LoxError> {
|
||||
let tokens: Vec<Token> = scan_tokens(source)?;
|
||||
|
||||
let token_str = itertools::Itertools::join(&mut tokens.iter().map(|token| token.to_string()), " ");
|
||||
if runtime.debug() {
|
||||
let token_str =
|
||||
itertools::Itertools::join(&mut tokens.iter().map(|token| token.to_string()), " ");
|
||||
|
||||
println!("{token_str}");
|
||||
println!("{token_str}");
|
||||
}
|
||||
|
||||
let statements = parse_tokens(tokens)?;
|
||||
|
||||
/* for statement in statements.iter() {
|
||||
println!("{statement}");
|
||||
} */
|
||||
|
||||
for mut statement in statements {
|
||||
if runtime.debug() {
|
||||
println!("{statement}");
|
||||
}
|
||||
|
||||
resolve(&mut statement, runtime)?;
|
||||
|
||||
// println!("{statement}");
|
||||
if runtime.debug() {
|
||||
println!("{statement}");
|
||||
}
|
||||
|
||||
execute(statement, runtime)?;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
use std::cell::RefCell;
|
||||
use std::fmt::Display;
|
||||
use std::io::{stdin, stdout, Read, Write};
|
||||
use std::rc::Rc;
|
||||
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
|
|
@ -9,19 +11,24 @@ use super::lox_std::init_std;
|
|||
use super::Value;
|
||||
|
||||
pub struct Runtime {
|
||||
globals: FxHashMap<String, Value>,
|
||||
globals: FxHashMap<Box<str>, Value>,
|
||||
|
||||
in_stream: Box<dyn std::io::BufRead>,
|
||||
out_stream: Box<dyn std::io::Write>,
|
||||
in_stream: Rc<RefCell<dyn std::io::Read>>,
|
||||
out_stream: Rc<RefCell<dyn std::io::Write>>,
|
||||
|
||||
debug: bool,
|
||||
}
|
||||
|
||||
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));
|
||||
pub fn new(
|
||||
in_stream: Rc<RefCell<dyn std::io::Read>>,
|
||||
out_stream: Rc<RefCell<dyn std::io::Write>>,
|
||||
) -> Self {
|
||||
let mut runtime = Runtime {
|
||||
globals: FxHashMap::default(),
|
||||
in_stream,
|
||||
out_stream,
|
||||
debug: false,
|
||||
};
|
||||
|
||||
init_std(&mut runtime);
|
||||
|
|
@ -29,7 +36,24 @@ impl Runtime {
|
|||
runtime
|
||||
}
|
||||
|
||||
pub fn globals(&self) -> &FxHashMap<String, Value> {
|
||||
pub fn set_debug(&mut self, debug: bool) {
|
||||
self.debug = debug;
|
||||
}
|
||||
|
||||
pub fn debug(&self) -> bool {
|
||||
self.debug
|
||||
}
|
||||
|
||||
pub fn in_stream(&mut self) -> std::cell::RefMut<dyn std::io::Read> {
|
||||
self.in_stream.as_ref().borrow_mut()
|
||||
}
|
||||
|
||||
pub fn out_stream(&mut self) -> std::cell::RefMut<dyn std::io::Write> {
|
||||
// &mut self.out_stream
|
||||
self.out_stream.as_ref().borrow_mut()
|
||||
}
|
||||
|
||||
pub fn globals(&self) -> &FxHashMap<Box<str>, Value> {
|
||||
&self.globals
|
||||
}
|
||||
|
||||
|
|
@ -37,10 +61,12 @@ impl Runtime {
|
|||
self.globals
|
||||
.get(name)
|
||||
.cloned()
|
||||
.ok_or_else(|| RuntimeError::GlobalNotDefined { name: name.to_owned() })
|
||||
.ok_or_else(|| RuntimeError::GlobalNotDefined {
|
||||
name: name.to_owned(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn define_global(&mut self, name: impl Into<String>, value: Value) {
|
||||
pub fn define_global(&mut self, name: impl Into<Box<str>>, value: Value) {
|
||||
let name = name.into();
|
||||
self.globals.insert(name, value);
|
||||
}
|
||||
|
|
@ -50,20 +76,30 @@ impl Runtime {
|
|||
*old_value = value;
|
||||
Ok(())
|
||||
} else {
|
||||
Err(RuntimeError::GlobalNotDefined { name: name.to_owned() })
|
||||
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()))
|
||||
// let buf_reader = std::io::BufReader::new(stdin());
|
||||
// let buf_reader_boxed = Box::new(&mut buf_reader);
|
||||
|
||||
let in_stream = Rc::new(RefCell::new(stdin()));
|
||||
let out_stream = Rc::new(RefCell::new(stdout()));
|
||||
|
||||
Runtime::new(in_stream, out_stream)
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
f.debug_struct("Runtime")
|
||||
.field("globals", &self.globals)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -81,22 +117,22 @@ impl Display for Runtime {
|
|||
|
||||
impl Drop for Runtime {
|
||||
fn drop(&mut self) {
|
||||
self.out_stream.flush().unwrap();
|
||||
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)
|
||||
self.in_stream().read(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for Runtime {
|
||||
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
||||
self.out_stream.write(buf)
|
||||
self.out_stream().write(buf)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> std::io::Result<()> {
|
||||
self.out_stream.flush()
|
||||
self.out_stream().flush()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,11 +13,12 @@ pub enum Value {
|
|||
Class(Rc<LoxClass>),
|
||||
Function(Rc<LoxFunction>),
|
||||
ExternFunction(Rc<LoxExternFunction>),
|
||||
String(Rc<String>),
|
||||
String(Box<str>),
|
||||
Number(f64),
|
||||
Bool(bool),
|
||||
Nil,
|
||||
}
|
||||
|
||||
impl Value {
|
||||
pub fn class(class: Rc<LoxClass>) -> Self {
|
||||
Value::Class(class)
|
||||
|
|
@ -33,8 +34,8 @@ impl Value {
|
|||
Value::ExternFunction(fun)
|
||||
}
|
||||
|
||||
pub fn string(s: impl Into<String>) -> Self {
|
||||
let s = Rc::new(s.into());
|
||||
pub fn string(s: impl Into<Box<str>>) -> Self {
|
||||
let s = s.into();
|
||||
Value::String(s)
|
||||
}
|
||||
|
||||
|
|
@ -50,7 +51,7 @@ impl Display for Value {
|
|||
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::String(s) => write!(f, "{s}"),
|
||||
Value::Number(num) => write!(f, "{num}"),
|
||||
Value::Bool(b) => write!(f, "{b}"),
|
||||
Value::Nil => write!(f, "nil"),
|
||||
|
|
@ -60,7 +61,7 @@ impl Display for Value {
|
|||
|
||||
/*====================================================================================================================*/
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct HeapedValue {
|
||||
inner: Rc<UnsafeCell<Value>>,
|
||||
}
|
||||
|
|
@ -88,14 +89,11 @@ impl HeapedValue {
|
|||
}
|
||||
}
|
||||
|
||||
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())
|
||||
impl AsRef<Value> for HeapedValue {
|
||||
fn as_ref(&self) -> &Value {
|
||||
unsafe {
|
||||
let ptr = self.inner.get();
|
||||
&*ptr
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue