mirror of
https://github.com/MorizzG/rlox.git
synced 2025-12-06 04:12:42 +00:00
finished resolver (chapter 11) and started classes (chapter 12)
This commit is contained in:
parent
42c9f17399
commit
10540708d4
34 changed files with 1449 additions and 439 deletions
13
interpreter/Cargo.toml
Normal file
13
interpreter/Cargo.toml
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
[package]
|
||||
name = "rlox2-interpreter"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies.rlox2-frontend]
|
||||
path = "../frontend"
|
||||
|
||||
[dependencies]
|
||||
thiserror = "1.0.38"
|
||||
itertools = "0.10.5"
|
||||
19
interpreter/src/class.rs
Normal file
19
interpreter/src/class.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
209
interpreter/src/environment.rs
Normal file
209
interpreter/src/environment.rs
Normal 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
85
interpreter/src/error.rs
Normal 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
104
interpreter/src/function.rs
Normal 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
|
||||
}
|
||||
}
|
||||
250
interpreter/src/interpret.rs
Normal file
250
interpreter/src/interpret.rs
Normal 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
18
interpreter/src/lib.rs
Normal 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
113
interpreter/src/lox_std.rs
Normal 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)
|
||||
}
|
||||
11
interpreter/src/resolver/error.rs
Normal file
11
interpreter/src/resolver/error.rs
Normal 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,
|
||||
}
|
||||
5
interpreter/src/resolver/mod.rs
Normal file
5
interpreter/src/resolver/mod.rs
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
mod error;
|
||||
mod resolve;
|
||||
|
||||
pub use error::ResolverError;
|
||||
pub use resolve::resolve;
|
||||
276
interpreter/src/resolver/resolve.rs
Normal file
276
interpreter/src/resolver/resolve.rs
Normal 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
116
interpreter/src/run.rs
Normal 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
101
interpreter/src/runtime.rs
Normal 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
115
interpreter/src/value.rs
Normal 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())
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue