add veeery simple file reading
This commit is contained in:
parent
7340601939
commit
e9cca49fc9
2 changed files with 129 additions and 8 deletions
6
lox/read_file.lox
Normal file
6
lox/read_file.lox
Normal file
|
@ -0,0 +1,6 @@
|
|||
var filepath = "Cargo.toml";
|
||||
var c = read(filepath);
|
||||
while (c != nil) {
|
||||
out(c);
|
||||
c = read(filepath);
|
||||
}
|
|
@ -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)),
|
||||
),
|
||||
]
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue