This commit is contained in:
Sebastian Hugentobler 2025-05-28 09:05:18 +02:00
parent 6e02a1a644
commit 463e4abd30
Signed by: shu
SSH key fingerprint: SHA256:ppcx6MlixdNZd5EUM1nkHOKoyQYoJwzuQKXM6J/t66M
13 changed files with 172 additions and 26 deletions

View file

@ -99,16 +99,14 @@ impl Environment {
/// Find an environment at a specific lexical distance from the current one.
fn ancestor(&self, distance: usize) -> Result<Environment, EnvironmentError> {
let mut environment = self.clone();
for _ in 0..distance {
let enclosing = match &environment.enclosing {
Some(enc) => enc.borrow().clone(),
None => return Err(EnvironmentError::InvalidDistance),
};
environment = enclosing;
// println!("{distance}: {self:?}");
if distance == 0 {
return Ok(self.clone());
}
Ok(environment)
match &self.enclosing {
Some(enclosing) => enclosing.borrow().ancestor(distance - 1),
None => Err(EnvironmentError::InvalidDistance),
}
}
}

View file

@ -52,9 +52,9 @@ pub struct Interpreter {
/// Global environment containing built-in functions and top-level variables
pub globals: Rc<RefCell<Environment>>,
/// Current environment for variable lookups and assignments
environment: Rc<RefCell<Environment>>,
pub environment: Rc<RefCell<Environment>>,
/// Map of expressions to their lexical distance for variable resolution
locals: HashMap<Expression, usize>,
pub locals: HashMap<Expression, usize>,
}
/// Default configuration for the interpreter, with builtin native functions.
@ -452,11 +452,6 @@ impl Interpreter {
fn assign(&mut self, name: &Token, expression: &Expression) -> Result<Value, InterpreterError> {
let value = self.evaluate(expression)?;
self.environment
.borrow_mut()
.assign(name, value.clone())
.map_err(InterpreterError::UndefinedVariable)?;
if let Some(distance) = self.locals.get(expression) {
self.environment
.borrow_mut()

View file

@ -94,6 +94,9 @@ fn run_rox(input: &str, interpreter: &mut Interpreter) -> Result<(), RoxError> {
debug!("AST:\n{}", crate::ast_printer::print(&ast));
resolver.resolve(&ast)?;
println!("globals: {:?}", interpreter.globals);
println!("environment: {:?}", interpreter.environment);
println!("locals: {:?}", interpreter.locals);
interpreter.run(ast)?;
Ok(())

View file

@ -3,7 +3,7 @@ use ordered_float::OrderedFloat;
use std::{
collections::HashMap,
fs::File,
io::{BufReader, ErrorKind, Read},
io::{stdin, stdout, BufReader, ErrorKind, Read, Seek, SeekFrom, Write},
rc::Rc,
sync::RwLock,
time::{SystemTime, UNIX_EPOCH},
@ -53,7 +53,7 @@ impl Callable for ReadFile {
}
fn arity(&self) -> usize {
1
2
}
fn call(
@ -61,18 +61,24 @@ impl Callable for ReadFile {
_interpreter: &mut Interpreter,
args: Vec<Value>,
) -> Result<Value, CallingError> {
if args.len() != self.arity() {
return Err(CallingError::ArgumentMismatch(self.arity(), args.len()));
}
let file_path = args
.first()
.ok_or(CallingError::CallFailed("arg not readable".into()))?;
let file_path = args.first().ok_or(CallingError::CallFailed(
"first arg must be file path".into(),
))?;
if let Value::String(file_path) = file_path {
self.ensure_file_handle(file_path)?;
let mut file_handles = FILE_HANDLES.write().unwrap();
let reader = file_handles.get_mut(file_path).unwrap();
let mut buffer = [0; 1];
let backwards = args.get(1).unwrap_or(&Value::Boolean(false));
if let Value::Boolean(backwards) = backwards {
if *backwards {
reader
.seek(SeekFrom::Current(-2))
.map_err(|e| CallingError::CallFailed(e.to_string()))?;
}
}
let character = match reader.read_exact(&mut buffer) {
Ok(_) => {
let character = buffer[0] as char;
@ -143,9 +149,81 @@ impl Callable for Out {
}
}
struct AsciiOut;
impl Callable for AsciiOut {
fn name(&self) -> String {
"asciiOut".into()
}
fn arity(&self) -> usize {
1
}
fn call(&self, interpreter: &mut Interpreter, args: Vec<Value>) -> Result<Value, CallingError> {
if args.len() != self.arity() {
return Err(CallingError::ArgumentMismatch(self.arity(), args.len()));
}
let value = args
.first()
.ok_or(CallingError::CallFailed("arg not readable".into()))?;
if let Value::Number(value) = value {
let ascii = value.0 as u8 as char;
print!("{ascii}");
Ok(Value::Nil)
} else {
Err(CallingError::CallFailed(
"file_path arg must be a number".into(),
))
}
}
}
struct PromptAscii;
impl Callable for PromptAscii {
fn name(&self) -> String {
"prompt_ascii".into()
}
fn arity(&self) -> usize {
1
}
fn call(
&self,
_interpreter: &mut Interpreter,
args: Vec<Value>,
) -> Result<Value, CallingError> {
if args.len() != self.arity() {
return Err(CallingError::ArgumentMismatch(self.arity(), args.len()));
}
let prompt = args
.first()
.ok_or(CallingError::CallFailed("arg not readable".into()))?;
if let Value::String(prompt) = prompt {
print!("{prompt} ");
stdout().flush();
let mut buffer = [0; 1];
stdin().read_exact(&mut buffer);
todo!()
// Ok(Value::Number(buffer[0] as char))
} else {
Err(CallingError::CallFailed("prompt must be a string".into()))
}
}
}
/// Return all native functions available to the Lox interpreter
pub fn all() -> Vec<(String, Value)> {
vec![
(
"asciiOut".into(),
Value::Callable((Rc::new(AsciiOut {}), CallableType::Function)),
),
(
"clock".into(),
Value::Callable((Rc::new(Clock {}), CallableType::Function)),
@ -158,5 +236,9 @@ pub fn all() -> Vec<(String, Value)> {
"read".into(),
Value::Callable((Rc::new(ReadFile {}), CallableType::Function)),
),
(
"prompt".into(),
Value::Callable((Rc::new(PromptAscii {}), CallableType::Function)),
),
]
}

View file

@ -4,7 +4,7 @@ use ordered_float::OrderedFloat;
use crate::{callable::Callable, instance, token::Literal};
/// Represents the type of a callable value in Lox.
/// Represent the type of a callable value in Lox.
#[derive(Clone, Debug, PartialEq)]
pub enum CallableType {
/// A regular function or method