add veeery simple file reading

This commit is contained in:
Sebastian Hugentobler 2025-05-26 13:58:45 +02:00
parent 7340601939
commit e9cca49fc9
Signed by: shu
SSH key fingerprint: SHA256:ppcx6MlixdNZd5EUM1nkHOKoyQYoJwzuQKXM6J/t66M
2 changed files with 129 additions and 8 deletions

6
lox/read_file.lox Normal file
View file

@ -0,0 +1,6 @@
var filepath = "Cargo.toml";
var c = read(filepath);
while (c != nil) {
out(c);
c = read(filepath);
}

View file

@ -1,10 +1,14 @@
use lazy_static::lazy_static;
use ordered_float::OrderedFloat;
use std::{
collections::HashMap,
fs::File,
io::{BufReader, ErrorKind, Read},
rc::Rc,
sync::RwLock,
time::{SystemTime, UNIX_EPOCH},
};
use ordered_float::OrderedFloat;
use crate::{
callable::{Callable, CallingError},
interpreter::Interpreter,
@ -13,7 +17,6 @@ use crate::{
/// Native clock function that returns the current time in seconds since the Unix epoch
struct Clock;
impl Callable for Clock {
fn name(&self) -> String {
"clock".into()
@ -27,7 +30,7 @@ impl Callable for Clock {
fn call(
&self,
_interpreter: &mut Interpreter,
_args: Vec<crate::value::Value>,
_args: Vec<Value>,
) -> Result<Value, CallingError> {
let start = SystemTime::now();
let since_the_epoch = start
@ -38,10 +41,122 @@ impl Callable for Clock {
}
}
lazy_static! {
static ref FILE_HANDLES: RwLock<HashMap<String, BufReader<File>>> = RwLock::new(HashMap::new());
}
/// Read function that returns each byte of a file.
struct ReadFile;
impl Callable for ReadFile {
fn name(&self) -> String {
"read".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 file_path = args
.first()
.ok_or(CallingError::CallFailed("arg not readable".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 character = match reader.read_exact(&mut buffer) {
Ok(_) => {
let character = buffer[0] as char;
Value::String(character.to_string())
}
Err(e) if e.kind() == ErrorKind::UnexpectedEof => Value::Nil,
Err(e) => return Err(CallingError::CallFailed(e.to_string())),
};
Ok(character)
} else {
Err(CallingError::CallFailed(
"file_path arg must be a string".into(),
))
}
}
}
impl ReadFile {
fn is_fresh(&self, file_path: &str) -> bool {
let file_handles = FILE_HANDLES.read().unwrap();
!file_handles.contains_key(file_path)
}
fn ensure_file_handle(&self, file_path: &str) -> Result<(), CallingError> {
if self.is_fresh(file_path) {
let file =
File::open(file_path).map_err(|e| CallingError::CallFailed(e.to_string()))?;
let reader = BufReader::new(file);
let mut file_handles = FILE_HANDLES.write().unwrap();
file_handles.insert(file_path.to_string(), reader);
}
Ok(())
}
}
struct Out;
impl Callable for Out {
fn name(&self) -> String {
"out".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::String(value) = value {
print!("{value}");
Ok(Value::Nil)
} else {
Err(CallingError::CallFailed(
"file_path arg must be a string".into(),
))
}
}
}
/// Return all native functions available to the Lox interpreter
pub fn all() -> Vec<(String, Value)> {
vec![(
"clock".into(),
Value::Callable((Rc::new(Clock {}), CallableType::Function)),
)]
vec![
(
"clock".into(),
Value::Callable((Rc::new(Clock {}), CallableType::Function)),
),
(
"out".into(),
Value::Callable((Rc::new(Out {}), CallableType::Function)),
),
(
"read".into(),
Value::Callable((Rc::new(ReadFile {}), CallableType::Function)),
),
]
}