261 lines
7.1 KiB
Rust
261 lines
7.1 KiB
Rust
use std::{
|
|
borrow::Cow,
|
|
io::{BufRead, Write, stdin, stdout},
|
|
rc::Rc,
|
|
};
|
|
|
|
use crate::{
|
|
compile::{
|
|
Compile, CompileContext, CompileOptions,
|
|
syntax::{Expression, FunctionBody, ParseError},
|
|
},
|
|
error::{MachineError, MachineErrorAt, ReadError},
|
|
parse::{self, parse_value},
|
|
util::Either,
|
|
vm::{
|
|
env::Environment,
|
|
instruction::Instruction,
|
|
machine::Machine,
|
|
macros::MacroExpand,
|
|
value::{BytecodeFunction, IdentifierValue, Value},
|
|
},
|
|
};
|
|
|
|
pub trait Reader {
|
|
type Error: Into<MachineErrorAt>;
|
|
|
|
fn read(&mut self) -> Result<Option<Value>, Self::Error>;
|
|
}
|
|
|
|
pub struct InteractiveReader {
|
|
buffer: String,
|
|
prompt_empty: Cow<'static, str>,
|
|
prompt_continuation: Cow<'static, str>,
|
|
}
|
|
|
|
pub struct FileReader<R: BufRead> {
|
|
reader: R,
|
|
buffer: String,
|
|
}
|
|
|
|
pub struct ModuleReader<R: BufRead> {
|
|
reader: FileReader<R>,
|
|
macro_machine: Machine,
|
|
}
|
|
|
|
impl InteractiveReader {
|
|
pub fn new<T: Into<Cow<'static, str>>, U: Into<Cow<'static, str>>>(
|
|
prompt_empty: T,
|
|
prompt_continuation: U,
|
|
) -> Self {
|
|
Self {
|
|
buffer: String::new(),
|
|
prompt_empty: prompt_empty.into(),
|
|
prompt_continuation: prompt_continuation.into(),
|
|
}
|
|
}
|
|
|
|
pub fn reset(&mut self) {
|
|
self.buffer.clear();
|
|
}
|
|
}
|
|
|
|
impl Reader for InteractiveReader {
|
|
type Error = MachineErrorAt;
|
|
|
|
fn read(&mut self) -> Result<Option<Value>, Self::Error> {
|
|
let stdin = stdin();
|
|
read_inner(
|
|
&mut stdin.lock(),
|
|
&mut self.buffer,
|
|
Some((
|
|
self.prompt_empty.as_ref(),
|
|
self.prompt_continuation.as_ref(),
|
|
)),
|
|
)
|
|
}
|
|
}
|
|
|
|
impl<R: BufRead> FileReader<R> {
|
|
pub fn new(reader: R) -> Self {
|
|
Self {
|
|
buffer: String::new(),
|
|
reader,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<R: BufRead> Reader for FileReader<R> {
|
|
type Error = MachineErrorAt;
|
|
|
|
fn read(&mut self) -> Result<Option<Value>, Self::Error> {
|
|
read_inner(&mut self.reader, &mut self.buffer, None)
|
|
}
|
|
}
|
|
|
|
impl<R: BufRead> ModuleReader<R> {
|
|
pub fn new(reader: R) -> Self {
|
|
Self {
|
|
reader: FileReader::new(reader),
|
|
macro_machine: Machine::default(),
|
|
}
|
|
}
|
|
|
|
pub fn read_expression(
|
|
&mut self,
|
|
options: &CompileOptions,
|
|
env: &mut Environment,
|
|
) -> Result<Option<Rc<Expression>>, Either<MachineErrorAt, Vec<ParseError>>> {
|
|
loop {
|
|
let value =
|
|
read(&mut self.reader, &mut self.macro_machine, env).map_err(Either::Left)?;
|
|
let Some(value) = value else {
|
|
return Ok(None);
|
|
};
|
|
let expression = Expression::parse(&value).map_err(Either::Right)?;
|
|
if let Expression::Defmacro(_) = expression.as_ref() {
|
|
self.macro_machine
|
|
.evaluate_value(options.clone(), Some("defmacro".into()), env, value)
|
|
.map_err(Either::Left)?;
|
|
continue;
|
|
}
|
|
return Ok(Some(expression));
|
|
}
|
|
}
|
|
|
|
pub fn compile(
|
|
mut self,
|
|
module_name: Option<IdentifierValue>,
|
|
options: &CompileOptions,
|
|
env: &mut Environment,
|
|
) -> Result<Rc<BytecodeFunction>, Either<MachineErrorAt, Vec<ParseError>>> {
|
|
let mut cx = CompileContext::new(options.clone(), module_name);
|
|
let mut body = FunctionBody {
|
|
head: vec![],
|
|
tail: Rc::new(Expression::Nil),
|
|
};
|
|
|
|
let mut syntax_errors = vec![];
|
|
|
|
loop {
|
|
let expression = match self.read_expression(options, env) {
|
|
Ok(Some(expression)) => expression,
|
|
Ok(None) => break,
|
|
Err(Either::Left(error)) => return Err(Either::Left(error)),
|
|
Err(Either::Right(errors)) => {
|
|
syntax_errors.extend(errors);
|
|
continue;
|
|
}
|
|
};
|
|
|
|
body.head.push(expression);
|
|
}
|
|
|
|
if !syntax_errors.is_empty() {
|
|
return Err(Either::Right(syntax_errors));
|
|
}
|
|
|
|
let value = body
|
|
.compile(&mut cx)
|
|
.map_err(MachineError::Compile)
|
|
.map_err(MachineErrorAt::at_unknown)
|
|
.map_err(Either::Left)?;
|
|
cx.push(value)
|
|
.map_err(MachineError::Compile)
|
|
.map_err(MachineErrorAt::at_unknown)
|
|
.map_err(Either::Left)?;
|
|
cx.emit(Instruction::Return);
|
|
|
|
let function = cx
|
|
.to_bytecode()
|
|
.map_err(MachineError::Compile)
|
|
.map_err(MachineErrorAt::at_unknown)
|
|
.map_err(Either::Left)?;
|
|
|
|
Ok(function)
|
|
}
|
|
}
|
|
|
|
fn read_inner<R: BufRead>(
|
|
reader: &mut R,
|
|
buffer: &mut String,
|
|
prompt: Option<(&str, &str)>,
|
|
) -> Result<Option<Value>, MachineErrorAt> {
|
|
loop {
|
|
let mut incomplete = None;
|
|
let mut i = buffer.trim_start();
|
|
|
|
while !i.is_empty() {
|
|
i = match parse::skip_comment_and_whitespace(i) {
|
|
Ok((i, _)) => i,
|
|
Err(_error) => {
|
|
buffer.clear();
|
|
i = buffer.trim_start();
|
|
continue;
|
|
}
|
|
};
|
|
if i.is_empty() {
|
|
buffer.clear();
|
|
break;
|
|
}
|
|
let result = parse_value(i);
|
|
let (tail, value) = match result {
|
|
Ok(r) => r,
|
|
Err(nom::Err::Incomplete(error)) => {
|
|
incomplete = Some(error);
|
|
break;
|
|
}
|
|
Err(error) => {
|
|
let error = ReadError::Lexical(error.map_input(|i| i.into()));
|
|
let error = MachineError::Read(error);
|
|
buffer.clear();
|
|
return Err(error.at_unknown());
|
|
}
|
|
};
|
|
|
|
*buffer = tail.trim_start().into();
|
|
return Ok(Some(value));
|
|
}
|
|
|
|
*buffer = buffer.trim_start().into();
|
|
|
|
if let Some((prompt_empty, prompt_continuation)) = prompt {
|
|
if buffer.is_empty() {
|
|
print!("{prompt_empty}");
|
|
} else {
|
|
print!("{prompt_continuation}");
|
|
}
|
|
stdout().flush().ok();
|
|
}
|
|
|
|
let len = reader
|
|
.read_line(buffer)
|
|
.map_err(ReadError::Io)
|
|
.map_err(MachineError::Read)
|
|
.map_err(MachineErrorAt::at_unknown)?;
|
|
if len == 0 {
|
|
return if let Some(incomplete) = incomplete {
|
|
let error = ReadError::Lexical(nom::Err::Incomplete(incomplete));
|
|
let error = MachineError::Read(error);
|
|
Err(error.at_unknown())
|
|
} else {
|
|
assert!(buffer.is_empty());
|
|
Ok(None)
|
|
};
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn read<R: Reader>(
|
|
reader: &mut R,
|
|
vm: &mut Machine,
|
|
env: &mut Environment,
|
|
) -> Result<Option<Value>, MachineErrorAt> {
|
|
let raw_value = reader.read().map_err(Into::into)?;
|
|
let Some(raw_value) = raw_value else {
|
|
return Ok(None);
|
|
};
|
|
let exp_value = raw_value.macro_expand(vm, env, false)?;
|
|
Ok(Some(exp_value))
|
|
}
|