Add basic io, port integration tests
This commit is contained in:
@@ -0,0 +1,11 @@
|
|||||||
|
(print (+ (car *args*) ":"))
|
||||||
|
(setq handle (fopen (car *args*)))
|
||||||
|
|
||||||
|
(loop
|
||||||
|
(let (data (fread handle))
|
||||||
|
(if data
|
||||||
|
(fwrite *stdout* data)
|
||||||
|
(break)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
@@ -589,7 +589,7 @@ impl FunctionBlock {
|
|||||||
};
|
};
|
||||||
let local = &self.locals[index];
|
let local = &self.locals[index];
|
||||||
if local.depth == -1 {
|
if local.depth == -1 {
|
||||||
todo!("Own init");
|
return Err(CompileError::UndefinedLocalReference(name.into()));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Some(index.try_into().unwrap()))
|
Ok(Some(index.try_into().unwrap()))
|
||||||
|
|||||||
@@ -133,6 +133,7 @@ mod tests {
|
|||||||
head: vec![],
|
head: vec![],
|
||||||
tail: Rc::new(Expression::Call(CallExpression {
|
tail: Rc::new(Expression::Call(CallExpression {
|
||||||
callee: Rc::new(Expression::Lambda(LambdaExpression {
|
callee: Rc::new(Expression::Lambda(LambdaExpression {
|
||||||
|
docstring: None,
|
||||||
signature: FunctionSignature {
|
signature: FunctionSignature {
|
||||||
required_arguments: vec!["y".into()],
|
required_arguments: vec!["y".into()],
|
||||||
optional_arguments: vec![],
|
optional_arguments: vec![],
|
||||||
@@ -156,23 +157,24 @@ mod tests {
|
|||||||
let (cx, _v) = test_compile(&e).unwrap();
|
let (cx, _v) = test_compile(&e).unwrap();
|
||||||
assert_eq!(cx.current, 0);
|
assert_eq!(cx.current, 0);
|
||||||
assert_eq!(cx.function_blocks.len(), 2);
|
assert_eq!(cx.function_blocks.len(), 2);
|
||||||
let root_function = cx.function_blocks[0].to_bytecode();
|
let root_function = cx.function_blocks[0].to_bytecode().unwrap();
|
||||||
let lambda_function = cx.function_blocks[1].to_bytecode();
|
let lambda_function = cx.function_blocks[1].to_bytecode().unwrap();
|
||||||
|
|
||||||
// lambda
|
// lambda
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
lambda_function.instructions.as_ref(),
|
lambda_function.instructions.as_ref(),
|
||||||
&[
|
&[
|
||||||
Instruction::GetLocal.into(),
|
|
||||||
0,
|
|
||||||
Instruction::GetUpvalue.into(),
|
Instruction::GetUpvalue.into(),
|
||||||
0,
|
0,
|
||||||
|
Instruction::GetLocal.into(),
|
||||||
|
0,
|
||||||
Instruction::Add.into(),
|
Instruction::Add.into(),
|
||||||
2,
|
2,
|
||||||
Instruction::Return.into(),
|
Instruction::Return.into(),
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
assert_eq!(lambda_function.arity, 1);
|
assert_eq!(lambda_function.min_arity, 1);
|
||||||
|
assert_eq!(lambda_function.max_arity, 1);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
lambda_function.upvalues.as_ref(),
|
lambda_function.upvalues.as_ref(),
|
||||||
&[UpvalueDef {
|
&[UpvalueDef {
|
||||||
@@ -214,6 +216,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_lambda_compile() {
|
fn test_lambda_compile() {
|
||||||
let e = Expression::Lambda(LambdaExpression {
|
let e = Expression::Lambda(LambdaExpression {
|
||||||
|
docstring: None,
|
||||||
signature: FunctionSignature {
|
signature: FunctionSignature {
|
||||||
required_arguments: vec!["a".into()],
|
required_arguments: vec!["a".into()],
|
||||||
optional_arguments: vec![],
|
optional_arguments: vec![],
|
||||||
@@ -230,7 +233,7 @@ mod tests {
|
|||||||
let CompileValue::LocalFunction(index) = v else {
|
let CompileValue::LocalFunction(index) = v else {
|
||||||
panic!("lambda did not compile to a function value")
|
panic!("lambda did not compile to a function value")
|
||||||
};
|
};
|
||||||
let lambda_function = cx.function_blocks[index].to_bytecode();
|
let lambda_function = cx.function_blocks[index].to_bytecode().unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
lambda_function.instructions.as_ref(),
|
lambda_function.instructions.as_ref(),
|
||||||
&[
|
&[
|
||||||
@@ -246,6 +249,7 @@ mod tests {
|
|||||||
fn test_compile_defun() {
|
fn test_compile_defun() {
|
||||||
let e = Expression::Defun(DefunExpression {
|
let e = Expression::Defun(DefunExpression {
|
||||||
name: "my-function".into(),
|
name: "my-function".into(),
|
||||||
|
docstring: None,
|
||||||
signature: FunctionSignature {
|
signature: FunctionSignature {
|
||||||
required_arguments: vec!["a".into()],
|
required_arguments: vec!["a".into()],
|
||||||
optional_arguments: vec![],
|
optional_arguments: vec![],
|
||||||
@@ -259,8 +263,8 @@ mod tests {
|
|||||||
let (cx, _v) = test_compile(&e).unwrap();
|
let (cx, _v) = test_compile(&e).unwrap();
|
||||||
assert_eq!(cx.current, 0);
|
assert_eq!(cx.current, 0);
|
||||||
assert_eq!(cx.function_blocks.len(), 2);
|
assert_eq!(cx.function_blocks.len(), 2);
|
||||||
let root_function = cx.function_blocks[0].to_bytecode();
|
let root_function = cx.function_blocks[0].to_bytecode().unwrap();
|
||||||
let defun_function = cx.function_blocks[1].to_bytecode();
|
let defun_function = cx.function_blocks[1].to_bytecode().unwrap();
|
||||||
// identifier + function
|
// identifier + function
|
||||||
assert_eq!(root_function.constants.len(), 2);
|
assert_eq!(root_function.constants.len(), 2);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -279,7 +283,8 @@ mod tests {
|
|||||||
);
|
);
|
||||||
// inner function
|
// inner function
|
||||||
assert!(defun_function.constants.is_empty());
|
assert!(defun_function.constants.is_empty());
|
||||||
assert_eq!(defun_function.arity, 1);
|
assert_eq!(defun_function.min_arity, 1);
|
||||||
|
assert_eq!(defun_function.max_arity, 1);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
defun_function.instructions.as_ref(),
|
defun_function.instructions.as_ref(),
|
||||||
&[
|
&[
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#![coverage(off)]
|
#![coverage(off)]
|
||||||
|
|
||||||
use crate::compile::syntax::ParseError;
|
use crate::{compile::syntax::ParseError, vm::value::IdentifierValue};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, thiserror::Error)]
|
#[derive(Debug, PartialEq, thiserror::Error)]
|
||||||
pub enum CompileError {
|
pub enum CompileError {
|
||||||
@@ -16,8 +16,10 @@ pub enum CompileError {
|
|||||||
BreakOutsideOfLoop,
|
BreakOutsideOfLoop,
|
||||||
#[error("(continue) used outside of a loop")]
|
#[error("(continue) used outside of a loop")]
|
||||||
ContinueOutsideOfLoop,
|
ContinueOutsideOfLoop,
|
||||||
#[error("Cannot emit branch instruction from {0} to {1}")]
|
#[error("cannot emit branch instruction from {0} to {1}")]
|
||||||
CannotEmitBranch(usize, usize),
|
CannotEmitBranch(usize, usize),
|
||||||
|
#[error("undefined local value reference: {0}")]
|
||||||
|
UndefinedLocalReference(IdentifierValue),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Vec<ParseError>> for CompileError {
|
impl From<Vec<ParseError>> for CompileError {
|
||||||
|
|||||||
@@ -326,6 +326,7 @@ mod tests {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
expr.as_ref(),
|
expr.as_ref(),
|
||||||
&Expression::Lambda(LambdaExpression {
|
&Expression::Lambda(LambdaExpression {
|
||||||
|
docstring: None,
|
||||||
signature: FunctionSignature {
|
signature: FunctionSignature {
|
||||||
required_arguments: vec!["a".into()],
|
required_arguments: vec!["a".into()],
|
||||||
optional_arguments: vec!["b".into()],
|
optional_arguments: vec!["b".into()],
|
||||||
|
|||||||
@@ -124,6 +124,7 @@ impl Expression {
|
|||||||
Value::Closure(_) => todo!("Error here"),
|
Value::Closure(_) => todo!("Error here"),
|
||||||
Value::Function(_) => todo!("Error here"),
|
Value::Function(_) => todo!("Error here"),
|
||||||
Value::NativeFunction(_) => todo!("Error here"),
|
Value::NativeFunction(_) => todo!("Error here"),
|
||||||
|
Value::NativeValue(_) => todo!("Error here"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-5
@@ -678,10 +678,12 @@ mod tests {
|
|||||||
upvalues: vec![],
|
upvalues: vec![],
|
||||||
function: Rc::new(BytecodeFunction {
|
function: Rc::new(BytecodeFunction {
|
||||||
identifier: Some("test-script".into()),
|
identifier: Some("test-script".into()),
|
||||||
|
docstring: None,
|
||||||
instructions: instructions.into(),
|
instructions: instructions.into(),
|
||||||
constants: constants.into(),
|
constants: constants.into(),
|
||||||
upvalues: [].into(),
|
upvalues: [].into(),
|
||||||
arity: 0,
|
min_arity: 0,
|
||||||
|
max_arity: 0,
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
let mut machine = Machine::default();
|
let mut machine = Machine::default();
|
||||||
@@ -734,15 +736,16 @@ mod tests {
|
|||||||
// (lambda (y) (let (x 123) (+ x y)))
|
// (lambda (y) (let (x 123) (+ x y)))
|
||||||
let lambda_function = Rc::new(BytecodeFunction {
|
let lambda_function = Rc::new(BytecodeFunction {
|
||||||
identifier: None,
|
identifier: None,
|
||||||
|
docstring: None,
|
||||||
instructions: [
|
instructions: [
|
||||||
// x 123
|
// x 123
|
||||||
Instruction::PushInteger.into(),
|
Instruction::PushInteger.into(),
|
||||||
123,
|
123,
|
||||||
0,
|
0,
|
||||||
Instruction::GetLocal.into(),
|
Instruction::GetLocal.into(),
|
||||||
1,
|
|
||||||
Instruction::GetLocal.into(),
|
|
||||||
0,
|
0,
|
||||||
|
Instruction::GetLocal.into(),
|
||||||
|
1,
|
||||||
Instruction::Add.into(),
|
Instruction::Add.into(),
|
||||||
2,
|
2,
|
||||||
Instruction::SetTemp.into(),
|
Instruction::SetTemp.into(),
|
||||||
@@ -753,7 +756,8 @@ mod tests {
|
|||||||
.into(),
|
.into(),
|
||||||
constants: [].into(),
|
constants: [].into(),
|
||||||
upvalues: [].into(),
|
upvalues: [].into(),
|
||||||
arity: 2,
|
min_arity: 1,
|
||||||
|
max_arity: 1,
|
||||||
});
|
});
|
||||||
let (m, r) = eval0(
|
let (m, r) = eval0(
|
||||||
&mut env,
|
&mut env,
|
||||||
@@ -780,6 +784,7 @@ mod tests {
|
|||||||
// (lambda (x y) (+ x y))
|
// (lambda (x y) (+ x y))
|
||||||
let lambda_function = Rc::new(BytecodeFunction {
|
let lambda_function = Rc::new(BytecodeFunction {
|
||||||
identifier: None,
|
identifier: None,
|
||||||
|
docstring: None,
|
||||||
instructions: [
|
instructions: [
|
||||||
Instruction::GetLocal.into(),
|
Instruction::GetLocal.into(),
|
||||||
0,
|
0,
|
||||||
@@ -792,7 +797,8 @@ mod tests {
|
|||||||
.into(),
|
.into(),
|
||||||
constants: [].into(),
|
constants: [].into(),
|
||||||
upvalues: [].into(),
|
upvalues: [].into(),
|
||||||
arity: 2,
|
min_arity: 2,
|
||||||
|
max_arity: 2,
|
||||||
});
|
});
|
||||||
let (m, r) = eval0(
|
let (m, r) = eval0(
|
||||||
&mut env,
|
&mut env,
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ impl MacroExpand for Value {
|
|||||||
| Self::Closure(_)
|
| Self::Closure(_)
|
||||||
| Self::Function(_)
|
| Self::Function(_)
|
||||||
| Self::NativeFunction(_)
|
| Self::NativeFunction(_)
|
||||||
|
| Self::NativeValue(_)
|
||||||
| Self::Vector(_)
|
| Self::Vector(_)
|
||||||
| Self::Unquote(_) => Ok(self.clone()),
|
| Self::Unquote(_) => Ok(self.clone()),
|
||||||
// | Self::NativeFunction(_) => Ok(self.clone()),
|
// | Self::NativeFunction(_) => Ok(self.clone()),
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ pub fn load(env: &mut Environment) {
|
|||||||
function.docstring()
|
function.docstring()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
Value::NativeValue(_) => "a native value".into(),
|
||||||
};
|
};
|
||||||
Ok(Value::String(explanation.into()))
|
Ok(Value::String(explanation.into()))
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -21,17 +21,6 @@ pub fn load(env: &mut Environment) {
|
|||||||
};
|
};
|
||||||
Ok(outcome)
|
Ok(outcome)
|
||||||
});
|
});
|
||||||
// env.defun_native("apply", |vm, env, args| {
|
|
||||||
// let [f, xs] = args else {
|
|
||||||
// return Err(vm.error_at_ip(MachineErrorKind::InvalidArgument));
|
|
||||||
// };
|
|
||||||
// let f = AnyFunction::try_from_value(f).map_err(|e| vm.error_at_ip(e))?;
|
|
||||||
// let args = xs
|
|
||||||
// .proper_iter(vm.error_at_ip(MachineErrorKind::InvalidArgument))
|
|
||||||
// .map(|x| x.cloned())
|
|
||||||
// .collect::<Result<Vec<_>, _>>()?;
|
|
||||||
// f.invoke(vm, env, &args[..])
|
|
||||||
// });
|
|
||||||
env.defun_native(
|
env.defun_native(
|
||||||
"read",
|
"read",
|
||||||
"Reads a form from standard input",
|
"Reads a form from standard input",
|
||||||
|
|||||||
@@ -1,4 +1,10 @@
|
|||||||
use crate::{error::MachineError, vm::env::Environment};
|
use crate::{
|
||||||
|
error::{MachineError, ValueConversionError},
|
||||||
|
vm::{
|
||||||
|
env::Environment,
|
||||||
|
value::convert::{AnyFunction, TryFromValue},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
pub fn load(env: &mut Environment) {
|
pub fn load(env: &mut Environment) {
|
||||||
// env.defun_native("map", |vm, env, args| {
|
// env.defun_native("map", |vm, env, args| {
|
||||||
@@ -24,6 +30,24 @@ pub fn load(env: &mut Environment) {
|
|||||||
// }))?;
|
// }))?;
|
||||||
// Ok(out)
|
// Ok(out)
|
||||||
// });
|
// });
|
||||||
|
env.defun_native(
|
||||||
|
"apply",
|
||||||
|
"Applies the function to a given argument list",
|
||||||
|
|vm, env, args| {
|
||||||
|
let [f, xs] = args else {
|
||||||
|
return Err(MachineError::InvalidArgumentCount);
|
||||||
|
};
|
||||||
|
let f = AnyFunction::try_from_value(f)?;
|
||||||
|
let args = xs
|
||||||
|
.proper_iter(MachineError::ValueConversion(ValueConversionError {
|
||||||
|
expected: "proper list".into(),
|
||||||
|
got: xs.clone(),
|
||||||
|
}))
|
||||||
|
.map(|x| x.cloned())
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
f.invoke(vm, env, &args[..])
|
||||||
|
},
|
||||||
|
);
|
||||||
env.defun_native("identity", "Returns the argument as is", |_, _, args| {
|
env.defun_native("identity", "Returns the argument as is", |_, _, args| {
|
||||||
let [arg] = args else {
|
let [arg] = args else {
|
||||||
return Err(MachineError::InvalidArgumentCount);
|
return Err(MachineError::InvalidArgumentCount);
|
||||||
|
|||||||
@@ -0,0 +1,152 @@
|
|||||||
|
use std::{
|
||||||
|
any::Any,
|
||||||
|
cell::RefCell,
|
||||||
|
fmt,
|
||||||
|
fs::File,
|
||||||
|
io::{Read, Write, stdin, stdout},
|
||||||
|
rc::Rc,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
error::{MachineError, ValueConversionError},
|
||||||
|
vm::{
|
||||||
|
Value,
|
||||||
|
env::Environment,
|
||||||
|
value::{NativeObject, NativeValue, StringValue, convert::TryFromValue},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum Stream {
|
||||||
|
Stdin,
|
||||||
|
Stdout,
|
||||||
|
File(RefCell<Option<File>>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NativeObject for Stream {
|
||||||
|
fn as_any(&self) -> &dyn Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Stream {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.write_str("stream")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Stream {
|
||||||
|
pub fn read(&self, amount: usize) -> Option<Value> {
|
||||||
|
let mut output = vec![];
|
||||||
|
let mut remaining = amount;
|
||||||
|
let mut buffer = [0; 4096];
|
||||||
|
|
||||||
|
while remaining != 0 {
|
||||||
|
let want = remaining.min(buffer.len());
|
||||||
|
let len = match self {
|
||||||
|
Self::Stdin => stdin().read(&mut buffer[..want]).ok()?,
|
||||||
|
Self::File(cell) => match &mut *cell.borrow_mut() {
|
||||||
|
Some(file) => file.read(&mut buffer[..want]).ok()?,
|
||||||
|
None => return None,
|
||||||
|
},
|
||||||
|
Self::Stdout => return None,
|
||||||
|
};
|
||||||
|
|
||||||
|
if len == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
output.extend_from_slice(&buffer[..len]);
|
||||||
|
remaining -= len;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(output.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write(&self, bytes: &[u8]) -> Option<Value> {
|
||||||
|
let len = match self {
|
||||||
|
Self::Stdin => return None,
|
||||||
|
Self::Stdout => stdout().write(bytes).ok()?,
|
||||||
|
Self::File(cell) => match &mut *cell.borrow_mut() {
|
||||||
|
Some(file) => file.write(bytes).ok()?,
|
||||||
|
None => return None,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
Some(Value::Number(len.into()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load(env: &mut Environment) {
|
||||||
|
let stdin: Rc<dyn NativeObject> = Rc::new(Stream::Stdin);
|
||||||
|
let stdout: Rc<dyn NativeObject> = Rc::new(Stream::Stdout);
|
||||||
|
|
||||||
|
env.set_global_value("*stdin*", NativeValue::from(stdin));
|
||||||
|
env.set_global_value("*stdout*", NativeValue::from(stdout));
|
||||||
|
|
||||||
|
env.defun_native("fopen", "Opens a file", |_, _, args| {
|
||||||
|
let [path] = args else {
|
||||||
|
return Err(MachineError::InvalidArgumentCount);
|
||||||
|
};
|
||||||
|
let path = StringValue::try_from_value(path)?;
|
||||||
|
match File::open(&*path) {
|
||||||
|
Ok(file) => {
|
||||||
|
let object: Rc<dyn NativeObject> = Rc::new(Stream::File(RefCell::new(Some(file))));
|
||||||
|
Ok(Value::NativeValue(object.into()))
|
||||||
|
}
|
||||||
|
Err(error) => {
|
||||||
|
eprintln!("TODO: not sure whether to use result/exceptions");
|
||||||
|
eprintln!("{path}: {error}");
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
env.defun_native(
|
||||||
|
"fread",
|
||||||
|
"Reads data as a byte vector from the stream",
|
||||||
|
|_, _, args| {
|
||||||
|
let (stream, amount) = match args {
|
||||||
|
// TODO read to end instead
|
||||||
|
[stream] => (stream.as_native::<Stream>()?, 4096),
|
||||||
|
[stream, amount] => (
|
||||||
|
stream.as_native::<Stream>()?,
|
||||||
|
usize::try_from_value(amount)?,
|
||||||
|
),
|
||||||
|
_ => return Err(MachineError::InvalidArgumentCount),
|
||||||
|
};
|
||||||
|
match stream.read(amount) {
|
||||||
|
Some(value) => Ok(value),
|
||||||
|
None => todo!(),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
env.defun_native("fwrite", "Writes data to the stream", |_, _, args| {
|
||||||
|
let [stream, data] = args else {
|
||||||
|
return Err(MachineError::InvalidArgumentCount);
|
||||||
|
};
|
||||||
|
let stream = stream.as_native::<Stream>()?;
|
||||||
|
let result = match data {
|
||||||
|
Value::Vector(value) if let Some(bytes) = value.as_bytes() => stream.write(&bytes[..]),
|
||||||
|
Value::String(value) => stream.write(value.as_bytes()),
|
||||||
|
_ => {
|
||||||
|
return Err(MachineError::ValueConversion(ValueConversionError {
|
||||||
|
expected: "byte vector or string".into(),
|
||||||
|
got: data.clone(),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
match result {
|
||||||
|
Some(value) => Ok(value),
|
||||||
|
None => todo!(),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
env.defun_native("fclose", "Closes the stream", |_, _, args| {
|
||||||
|
let [stream] = args else {
|
||||||
|
return Err(MachineError::InvalidArgumentCount);
|
||||||
|
};
|
||||||
|
let stream = stream.as_native::<Stream>()?;
|
||||||
|
if let Stream::File(cell) = stream.as_ref() {
|
||||||
|
cell.replace(None);
|
||||||
|
}
|
||||||
|
Ok(Value::Nil)
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ mod convert;
|
|||||||
mod debug;
|
mod debug;
|
||||||
mod eval;
|
mod eval;
|
||||||
mod functional;
|
mod functional;
|
||||||
|
mod io;
|
||||||
mod math;
|
mod math;
|
||||||
|
|
||||||
pub(crate) use math::*;
|
pub(crate) use math::*;
|
||||||
@@ -16,4 +17,5 @@ pub fn load(env: &mut Environment) {
|
|||||||
collections::load(env);
|
collections::load(env);
|
||||||
convert::load(env);
|
convert::load(env);
|
||||||
debug::load(env);
|
debug::load(env);
|
||||||
|
io::load(env);
|
||||||
}
|
}
|
||||||
|
|||||||
+65
-3
@@ -1,10 +1,15 @@
|
|||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
error::ValueConversionError,
|
error::{MachineError, ValueConversionError},
|
||||||
vm::{
|
vm::{
|
||||||
Value,
|
Value,
|
||||||
value::{BooleanValue, ConsCell, NumberValue, StringValue, Vector},
|
env::Environment,
|
||||||
|
machine::Machine,
|
||||||
|
value::{
|
||||||
|
BooleanValue, BytecodeFunction, ClosureValue, ConsCell, NativeFunction, NativeValue,
|
||||||
|
NumberValue, StringValue, Vector,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -12,6 +17,12 @@ pub trait TryFromValue<'a>: Sized {
|
|||||||
fn try_from_value(value: &'a Value) -> Result<Self, ValueConversionError>;
|
fn try_from_value(value: &'a Value) -> Result<Self, ValueConversionError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum AnyFunction {
|
||||||
|
Native(NativeFunction),
|
||||||
|
Function(Rc<BytecodeFunction>),
|
||||||
|
Closure(ClosureValue),
|
||||||
|
}
|
||||||
|
|
||||||
macro_rules! impl_integer {
|
macro_rules! impl_integer {
|
||||||
($($ty:ty : $bits:literal),+ $(,)?) => {
|
($($ty:ty : $bits:literal),+ $(,)?) => {
|
||||||
$(
|
$(
|
||||||
@@ -103,9 +114,60 @@ impl TryFromValue<'_> for StringValue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<NativeValue> for Value {
|
||||||
|
fn from(value: NativeValue) -> Self {
|
||||||
|
Self::NativeValue(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Vec<u8>> for Value {
|
||||||
|
fn from(value: Vec<u8>) -> Self {
|
||||||
|
Value::Vector(Rc::new(Vector::from_iter(value)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFromValue<'_> for AnyFunction {
|
||||||
|
fn try_from_value(value: &'_ Value) -> Result<Self, ValueConversionError> {
|
||||||
|
match value {
|
||||||
|
Value::NativeFunction(function) => Ok(Self::Native(function.clone())),
|
||||||
|
Value::Closure(closure) => Ok(Self::Closure(closure.clone())),
|
||||||
|
Value::Function(function) => Ok(Self::Function(function.clone())),
|
||||||
|
_ => Err(ValueConversionError {
|
||||||
|
expected: "function".into(),
|
||||||
|
got: value.clone(),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AnyFunction {
|
||||||
|
pub fn invoke(
|
||||||
|
&self,
|
||||||
|
vm: &mut Machine,
|
||||||
|
env: &mut Environment,
|
||||||
|
args: &[Value],
|
||||||
|
) -> Result<Value, MachineError> {
|
||||||
|
match self {
|
||||||
|
Self::Native(function) => function.invoke(vm, env, args),
|
||||||
|
Self::Closure(closure) => vm
|
||||||
|
.evaluate_closure_args(env, closure.clone(), args)
|
||||||
|
.map_err(|error| error.error),
|
||||||
|
Self::Function(function) => {
|
||||||
|
let closure = ClosureValue {
|
||||||
|
function: function.clone(),
|
||||||
|
upvalues: vec![],
|
||||||
|
};
|
||||||
|
vm.evaluate_closure_args(env, closure, args)
|
||||||
|
.map_err(|error| error.error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl_integer!(
|
impl_integer!(
|
||||||
i8: 8,
|
i8: 8,
|
||||||
i16: 16,
|
i16: 16,
|
||||||
i32: 32,
|
i32: 32,
|
||||||
i64: 64
|
i64: 64,
|
||||||
|
usize: 64,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -52,6 +52,6 @@ impl_keyword! {
|
|||||||
Break => "break",
|
Break => "break",
|
||||||
Continue => "continue",
|
Continue => "continue",
|
||||||
Declare => "declare",
|
Declare => "declare",
|
||||||
Cons => "cons",
|
// Cons => "cons",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-1
@@ -23,7 +23,7 @@ pub use cons::ConsCell;
|
|||||||
pub use function::BytecodeFunction;
|
pub use function::BytecodeFunction;
|
||||||
pub use identifier::IdentifierValue;
|
pub use identifier::IdentifierValue;
|
||||||
pub use keyword::Keyword;
|
pub use keyword::Keyword;
|
||||||
pub use native::{NativeFunction, OpaqueValue};
|
pub use native::{NativeFunction, NativeObject, NativeValue};
|
||||||
pub use number::NumberValue;
|
pub use number::NumberValue;
|
||||||
pub use string::StringValue;
|
pub use string::StringValue;
|
||||||
pub use vector::Vector;
|
pub use vector::Vector;
|
||||||
@@ -54,6 +54,7 @@ pub enum Value {
|
|||||||
Function(Rc<BytecodeFunction>),
|
Function(Rc<BytecodeFunction>),
|
||||||
// Native
|
// Native
|
||||||
NativeFunction(NativeFunction),
|
NativeFunction(NativeFunction),
|
||||||
|
NativeValue(NativeValue),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Value {
|
impl Value {
|
||||||
@@ -100,6 +101,7 @@ impl Value {
|
|||||||
Self::Vector(_) => Self::Identifier("vector".into()),
|
Self::Vector(_) => Self::Identifier("vector".into()),
|
||||||
Self::Keyword(_) => Self::Identifier("keyword".into()),
|
Self::Keyword(_) => Self::Identifier("keyword".into()),
|
||||||
Self::String(_) => Self::Identifier("string".into()),
|
Self::String(_) => Self::Identifier("string".into()),
|
||||||
|
Self::NativeValue(_) => Self::Identifier("native".into()),
|
||||||
Self::NativeFunction(_) | Self::Function(_) | Self::Closure(_) => {
|
Self::NativeFunction(_) | Self::Function(_) | Self::Closure(_) => {
|
||||||
Self::Identifier("function".into())
|
Self::Identifier("function".into())
|
||||||
}
|
}
|
||||||
@@ -121,6 +123,7 @@ impl Value {
|
|||||||
| Self::Unquote(_)
|
| Self::Unquote(_)
|
||||||
| Self::Closure(_)
|
| Self::Closure(_)
|
||||||
| Self::NativeFunction(_)
|
| Self::NativeFunction(_)
|
||||||
|
| Self::NativeValue(_)
|
||||||
| Self::Function(_) => true,
|
| Self::Function(_) => true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -175,6 +178,16 @@ impl Value {
|
|||||||
.collect::<Result<_, _>>()
|
.collect::<Result<_, _>>()
|
||||||
.ok()
|
.ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn as_native<T: NativeObject>(&self) -> Result<Rc<T>, ValueConversionError> {
|
||||||
|
match self {
|
||||||
|
Self::NativeValue(value) if let Some(value) = value.cast_rc() => Ok(value),
|
||||||
|
_ => Err(ValueConversionError {
|
||||||
|
expected: "native value".into(),
|
||||||
|
got: self.clone(),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Value {
|
impl fmt::Display for Value {
|
||||||
@@ -210,6 +223,7 @@ impl fmt::Display for Value {
|
|||||||
Self::Boolean(value) => fmt::Display::fmt(value, f),
|
Self::Boolean(value) => fmt::Display::fmt(value, f),
|
||||||
Self::Closure(value) => fmt::Display::fmt(value, f),
|
Self::Closure(value) => fmt::Display::fmt(value, f),
|
||||||
Self::Function(value) => fmt::Display::fmt(value, f),
|
Self::Function(value) => fmt::Display::fmt(value, f),
|
||||||
|
Self::NativeValue(value) => fmt::Display::fmt(value, f),
|
||||||
Self::NativeFunction(value) => fmt::Display::fmt(value, f),
|
Self::NativeFunction(value) => fmt::Display::fmt(value, f),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+29
-44
@@ -14,11 +14,13 @@ use crate::{
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
pub trait NativeObject: Any + fmt::Debug + fmt::Display {
|
||||||
pub struct OpaqueValue {
|
fn as_any(&self) -> &dyn Any;
|
||||||
inner: Rc<dyn Any>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct NativeValue(Rc<dyn NativeObject>);
|
||||||
|
|
||||||
pub type NativeFunctionImpl =
|
pub type NativeFunctionImpl =
|
||||||
Rc<dyn Fn(&mut Machine, &mut Environment, &[Value]) -> Result<Value, MachineError> + 'static>;
|
Rc<dyn Fn(&mut Machine, &mut Environment, &[Value]) -> Result<Value, MachineError> + 'static>;
|
||||||
|
|
||||||
@@ -29,47 +31,6 @@ pub struct NativeFunction {
|
|||||||
docstring: StringValue,
|
docstring: StringValue,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OpaqueValue {
|
|
||||||
pub fn cast<T: 'static>(&self) -> Result<&T, MachineError> {
|
|
||||||
todo!()
|
|
||||||
// self.inner
|
|
||||||
// .downcast_ref()
|
|
||||||
// .ok_or(MachineError::InvalidArgument)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Hash for OpaqueValue {
|
|
||||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
|
||||||
(&raw const *self.inner.as_ref()).addr().hash(state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Eq for OpaqueValue {}
|
|
||||||
|
|
||||||
impl fmt::Debug for OpaqueValue {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
f.debug_tuple("OpaqueValue")
|
|
||||||
.field_with(|f| write!(f, "{:p}", self.inner))
|
|
||||||
.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl PartialEq for OpaqueValue {
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
Rc::ptr_eq(&self.inner, &other.inner)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Rc<dyn Any>> for OpaqueValue {
|
|
||||||
fn from(value: Rc<dyn Any>) -> Self {
|
|
||||||
Self { inner: value }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl fmt::Display for OpaqueValue {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
write!(f, "<opaque {:p}>", self.inner)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl NativeFunction {
|
impl NativeFunction {
|
||||||
pub fn new<S, D, F>(name: S, docstring: D, function: F) -> Self
|
pub fn new<S, D, F>(name: S, docstring: D, function: F) -> Self
|
||||||
where
|
where
|
||||||
@@ -136,3 +97,27 @@ impl fmt::Display for NativeFunction {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl NativeValue {
|
||||||
|
pub fn cast_rc<T: NativeObject>(&self) -> Option<Rc<T>> {
|
||||||
|
Rc::downcast(self.0.clone()).ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Rc<dyn NativeObject + 'static>> for NativeValue {
|
||||||
|
fn from(value: Rc<dyn NativeObject>) -> Self {
|
||||||
|
Self(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for NativeValue {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
Rc::ptr_eq(&self.0, &other.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for NativeValue {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "<native {}>", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -189,6 +189,15 @@ impl From<i64> for NumberValue {
|
|||||||
Self::Int(value)
|
Self::Int(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl From<usize> for NumberValue {
|
||||||
|
fn from(value: usize) -> Self {
|
||||||
|
if let Ok(value) = i64::try_from(value) {
|
||||||
|
Self::Int(value)
|
||||||
|
} else {
|
||||||
|
todo!("TODO: promote to bigint")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl TryFrom<NumberValue> for i8 {
|
impl TryFrom<NumberValue> for i8 {
|
||||||
type Error = NumberConvertError;
|
type Error = NumberConvertError;
|
||||||
@@ -231,6 +240,17 @@ impl TryFrom<NumberValue> for i64 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TryFrom<NumberValue> for usize {
|
||||||
|
type Error = NumberConvertError;
|
||||||
|
|
||||||
|
fn try_from(value: NumberValue) -> Result<Self, Self::Error> {
|
||||||
|
match value {
|
||||||
|
NumberValue::Int(value) if let Ok(value) = value.try_into() => Ok(value),
|
||||||
|
_ => Err(NumberConvertError),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl fmt::Display for NumberValue {
|
impl fmt::Display for NumberValue {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
|
|||||||
+63
-55
@@ -1,8 +1,6 @@
|
|||||||
#![allow(irrefutable_let_patterns)]
|
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
cell::{Ref, RefCell},
|
cell::{Ref, RefCell},
|
||||||
fmt,
|
fmt, mem,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::vm::value::{NumberValue, Value};
|
use crate::vm::value::{NumberValue, Value};
|
||||||
@@ -38,6 +36,16 @@ impl Vector {
|
|||||||
pub fn set_value_at(&self, index: usize, value: Value) {
|
pub fn set_value_at(&self, index: usize, value: Value) {
|
||||||
self.0.borrow_mut().set_value_at(index, value);
|
self.0.borrow_mut().set_value_at(index, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn as_bytes(&self) -> Option<Ref<'_, [u8]>> {
|
||||||
|
Ref::filter_map(self.borrow(), |r| match r {
|
||||||
|
VectorStorage::I8(values) => {
|
||||||
|
Some(unsafe { mem::transmute::<&[i8], &[u8]>(&values[..]) })
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.ok()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Vector {
|
impl fmt::Display for Vector {
|
||||||
@@ -300,55 +308,55 @@ impl_primitive_from_iter!(i16, u16 => I16);
|
|||||||
impl_primitive_from_iter!(i32, u32 => I32);
|
impl_primitive_from_iter!(i32, u32 => I32);
|
||||||
impl_primitive_from_iter!(i64, u64 => I64);
|
impl_primitive_from_iter!(i64, u64 => I64);
|
||||||
|
|
||||||
// #[cfg(test)]
|
#[cfg(test)]
|
||||||
// mod tests {
|
mod tests {
|
||||||
// use crate::vm::value::{Value, vector::VectorStorage};
|
use crate::vm::value::{Value, vector::VectorStorage};
|
||||||
//
|
|
||||||
// #[test]
|
#[test]
|
||||||
// fn test_vector_storage_from_iter() {
|
fn test_vector_storage_from_iter() {
|
||||||
// let v = VectorStorage::from_iter([1i8, 2, 3, 4, 5, 6, 7]);
|
let v = VectorStorage::from_iter([1i8, 2, 3, 4, 5, 6, 7]);
|
||||||
// assert_eq!(VectorStorage::I8(vec![1, 2, 3, 4, 5, 6, 7]), v);
|
assert_eq!(VectorStorage::I8(vec![1, 2, 3, 4, 5, 6, 7]), v);
|
||||||
// let v = VectorStorage::from_iter([1u8, 2, 3, 4, 5, 6, 7]);
|
let v = VectorStorage::from_iter([1u8, 2, 3, 4, 5, 6, 7]);
|
||||||
// assert_eq!(VectorStorage::I8(vec![1, 2, 3, 4, 5, 6, 7]), v);
|
assert_eq!(VectorStorage::I8(vec![1, 2, 3, 4, 5, 6, 7]), v);
|
||||||
// let v = VectorStorage::from_iter([1i16, 2, 3, 4, 5, 6, 7]);
|
let v = VectorStorage::from_iter([1i16, 2, 3, 4, 5, 6, 7]);
|
||||||
// assert_eq!(VectorStorage::I16(vec![1, 2, 3, 4, 5, 6, 7]), v);
|
assert_eq!(VectorStorage::I16(vec![1, 2, 3, 4, 5, 6, 7]), v);
|
||||||
// let v = VectorStorage::from_iter([1u16, 2, 3, 4, 5, 6, 7]);
|
let v = VectorStorage::from_iter([1u16, 2, 3, 4, 5, 6, 7]);
|
||||||
// assert_eq!(VectorStorage::I16(vec![1, 2, 3, 4, 5, 6, 7]), v);
|
assert_eq!(VectorStorage::I16(vec![1, 2, 3, 4, 5, 6, 7]), v);
|
||||||
// let v = VectorStorage::from_iter([1i32, 2, 3, 4, 5, 6, 7]);
|
let v = VectorStorage::from_iter([1i32, 2, 3, 4, 5, 6, 7]);
|
||||||
// assert_eq!(VectorStorage::I32(vec![1, 2, 3, 4, 5, 6, 7]), v);
|
assert_eq!(VectorStorage::I32(vec![1, 2, 3, 4, 5, 6, 7]), v);
|
||||||
// let v = VectorStorage::from_iter([1u32, 2, 3, 4, 5, 6, 7]);
|
let v = VectorStorage::from_iter([1u32, 2, 3, 4, 5, 6, 7]);
|
||||||
// assert_eq!(VectorStorage::I32(vec![1, 2, 3, 4, 5, 6, 7]), v);
|
assert_eq!(VectorStorage::I32(vec![1, 2, 3, 4, 5, 6, 7]), v);
|
||||||
// let v = VectorStorage::from_iter([1i64, 2, 3, 4, 5, 6, 7]);
|
let v = VectorStorage::from_iter([1i64, 2, 3, 4, 5, 6, 7]);
|
||||||
// assert_eq!(VectorStorage::I64(vec![1, 2, 3, 4, 5, 6, 7]), v);
|
assert_eq!(VectorStorage::I64(vec![1, 2, 3, 4, 5, 6, 7]), v);
|
||||||
// let v = VectorStorage::from_iter([1u64, 2, 3, 4, 5, 6, 7]);
|
let v = VectorStorage::from_iter([1u64, 2, 3, 4, 5, 6, 7]);
|
||||||
// assert_eq!(VectorStorage::I64(vec![1, 2, 3, 4, 5, 6, 7]), v);
|
assert_eq!(VectorStorage::I64(vec![1, 2, 3, 4, 5, 6, 7]), v);
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// #[test]
|
#[test]
|
||||||
// fn test_vector_unspecialize() {
|
fn test_vector_unspecialize() {
|
||||||
// let mut v = VectorStorage::from_iter([1i8, 2, 3]);
|
let mut v = VectorStorage::from_iter([1i8, 2, 3]);
|
||||||
// assert_eq!(VectorStorage::I8(vec![1, 2, 3]), v);
|
assert_eq!(VectorStorage::I8(vec![1, 2, 3]), v);
|
||||||
// v.push(Value::Integer(1234));
|
v.push(Value::Number(1234.into()));
|
||||||
// assert_eq!(VectorStorage::I16(vec![1, 2, 3, 1234]), v);
|
assert_eq!(VectorStorage::I16(vec![1, 2, 3, 1234]), v);
|
||||||
// v.push(Value::Integer(12341234));
|
v.push(Value::Number(12341234.into()));
|
||||||
// assert_eq!(VectorStorage::I32(vec![1, 2, 3, 1234, 12341234]), v);
|
assert_eq!(VectorStorage::I32(vec![1, 2, 3, 1234, 12341234]), v);
|
||||||
// v.push(Value::Integer(1234123412341234));
|
v.push(Value::Number(1234123412341234i64.into()));
|
||||||
// assert_eq!(
|
assert_eq!(
|
||||||
// VectorStorage::I64(vec![1, 2, 3, 1234, 12341234, 1234123412341234]),
|
VectorStorage::I64(vec![1, 2, 3, 1234, 12341234, 1234123412341234]),
|
||||||
// v
|
v
|
||||||
// );
|
);
|
||||||
// v.push(Value::String("a".into()));
|
v.push(Value::String("a".into()));
|
||||||
// assert_eq!(
|
assert_eq!(
|
||||||
// VectorStorage::Any(vec![
|
VectorStorage::Any(vec![
|
||||||
// Value::Integer(1),
|
Value::Number(1.into()),
|
||||||
// Value::Integer(2),
|
Value::Number(2.into()),
|
||||||
// Value::Integer(3),
|
Value::Number(3.into()),
|
||||||
// Value::Integer(1234),
|
Value::Number(1234.into()),
|
||||||
// Value::Integer(12341234),
|
Value::Number(12341234.into()),
|
||||||
// Value::Integer(1234123412341234),
|
Value::Number(1234123412341234i64.into()),
|
||||||
// Value::String("a".into())
|
Value::String("a".into())
|
||||||
// ]),
|
]),
|
||||||
// v
|
v
|
||||||
// );
|
);
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
|
|||||||
+185
-174
@@ -1,174 +1,185 @@
|
|||||||
// use std::io::{self, BufReader, Read};
|
use std::io::{self, BufReader, Read};
|
||||||
//
|
|
||||||
// use lysp::{
|
use lysp::{
|
||||||
// error::{EvalError, MachineError, MachineErrorKind},
|
error::MachineErrorAt,
|
||||||
// read::{FileReader, read},
|
read::{FileReader, read},
|
||||||
// vm::{env::Environment, machine::Machine, prelude, value::Value},
|
vm::{env::Environment, machine::Machine, prelude, value::Value},
|
||||||
// };
|
};
|
||||||
//
|
|
||||||
// struct SliceReader<'a>(&'a [u8]);
|
struct SliceReader<'a>(&'a [u8]);
|
||||||
//
|
|
||||||
// impl Read for SliceReader<'_> {
|
impl Read for SliceReader<'_> {
|
||||||
// fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||||
// let count = self.0.len().min(buf.len());
|
let count = self.0.len().min(buf.len());
|
||||||
// buf[..count].copy_from_slice(&self.0[..count]);
|
buf[..count].copy_from_slice(&self.0[..count]);
|
||||||
// self.0 = &self.0[count..];
|
self.0 = &self.0[count..];
|
||||||
// Ok(count)
|
Ok(count)
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// fn eval_str_in(code: &str, env: &mut Environment) -> Result<Value, EvalError> {
|
#[track_caller]
|
||||||
// let mut machine = Machine::default();
|
fn eval_str_in(code: &str, env: &mut Environment) -> Result<Value, MachineErrorAt> {
|
||||||
// let reader = BufReader::new(SliceReader(code.as_bytes()));
|
let mut machine = Machine::default();
|
||||||
// let mut reader = FileReader::new(reader);
|
let reader = BufReader::new(SliceReader(code.as_bytes()));
|
||||||
//
|
let mut reader = FileReader::new(reader);
|
||||||
// let mut last_value = None;
|
|
||||||
// loop {
|
let mut last_value = None;
|
||||||
// let value = match read(&mut reader, &mut machine, env) {
|
loop {
|
||||||
// Ok(Some(value)) => value,
|
let value = match read(&mut reader, &mut machine, env) {
|
||||||
// Ok(None) => break,
|
Ok(Some(value)) => value,
|
||||||
// Err(error) => panic!("{error}"),
|
Ok(None) => break,
|
||||||
// };
|
Err(error) => panic!("{error}"),
|
||||||
//
|
};
|
||||||
// last_value = Some(machine.eval_value(Default::default(), env, value, false));
|
|
||||||
// }
|
last_value = Some(machine.evaluate_value(Default::default(), None, env, value));
|
||||||
//
|
}
|
||||||
// last_value.expect("no expressions evaluated")
|
|
||||||
// }
|
last_value.expect("no expressions evaluated")
|
||||||
//
|
}
|
||||||
// fn eval_str(code: &str) -> Value {
|
|
||||||
// let mut env = Environment::default();
|
#[track_caller]
|
||||||
// prelude::load(&mut env);
|
fn eval_str(code: &str) -> Value {
|
||||||
// eval_str_in(code, &mut env).expect("expression evaluation failed")
|
let mut env = Environment::default();
|
||||||
// }
|
prelude::load(&mut env);
|
||||||
//
|
eval_str_in(code, &mut env).expect("expression evaluation failed")
|
||||||
// fn eval_str_err(code: &str) -> EvalError {
|
}
|
||||||
// let mut env = Environment::default();
|
|
||||||
// prelude::load(&mut env);
|
#[track_caller]
|
||||||
// eval_str_in(code, &mut env).expect_err("expression was expected to fail")
|
fn eval_str_err(code: &str) -> MachineErrorAt {
|
||||||
// }
|
let mut env = Environment::default();
|
||||||
//
|
prelude::load(&mut env);
|
||||||
// #[test]
|
eval_str_in(code, &mut env).expect_err("expression was expected to fail")
|
||||||
// fn test_math() {
|
}
|
||||||
// // math
|
|
||||||
// assert_eq!(eval_str("(+ 1 2 3)"), Value::Integer(6));
|
#[test]
|
||||||
// assert_eq!(eval_str("(- 3 2 1)"), Value::Integer(0));
|
fn test_math() {
|
||||||
// assert_eq!(eval_str("(* 2 3 4)"), Value::Integer(24));
|
// math
|
||||||
// assert_eq!(eval_str("(/ 16 4 2)"), Value::Integer(2));
|
assert_eq!(eval_str("(+ 1 2 3)"), Value::Number(6.into()));
|
||||||
// assert_eq!(eval_str("(% 35 16)"), Value::Integer(3));
|
assert_eq!(eval_str("(- 3 2 1)"), Value::Number(0.into()));
|
||||||
// assert_eq!(eval_str("(| 1 2 4)"), Value::Integer(7));
|
assert_eq!(eval_str("(* 2 3 4)"), Value::Number(24.into()));
|
||||||
// assert_eq!(eval_str("(& 1 2 4)"), Value::Integer(0));
|
assert_eq!(eval_str("(/ 16 4 2)"), Value::Number(2.into()));
|
||||||
// assert_eq!(eval_str("(& 1 3 7)"), Value::Integer(1));
|
assert_eq!(eval_str("(% 35 16)"), Value::Number(3.into()));
|
||||||
// assert_eq!(eval_str("(^ 1 3 8)"), Value::Integer(10));
|
// TODO
|
||||||
// // comparison
|
// assert_eq!(eval_str("(| 1 2 4)"), Value::Number(7.into()));
|
||||||
// assert_eq!(eval_str("(< 1 2 3 4 5)"), Value::Boolean(true));
|
// assert_eq!(eval_str("(& 1 2 4)"), Value::Number(0.into()));
|
||||||
// assert_eq!(eval_str("(< 1 2 3 3 4 5)"), Value::Boolean(false));
|
// assert_eq!(eval_str("(& 1 3 7)"), Value::Number(1.into()));
|
||||||
// assert_eq!(eval_str("(> 1 2 3 4 5)"), Value::Boolean(false));
|
// assert_eq!(eval_str("(^ 1 3 8)"), Value::Number(10.into()));
|
||||||
// assert_eq!(eval_str("(>= 5 5 4 3 2 1)"), Value::Boolean(true));
|
// comparison
|
||||||
// assert_eq!(eval_str("(> 5 5 4 3 2 1)"), Value::Boolean(false));
|
assert_eq!(eval_str("(< 1 2 3 4 5)"), Value::Boolean(true.into()));
|
||||||
// assert_eq!(eval_str("(<= 1 2 3 3 3 4)"), Value::Boolean(true));
|
assert_eq!(eval_str("(< 1 2 3 3 4 5)"), Value::Boolean(false.into()));
|
||||||
// assert_eq!(eval_str("(/= 1 2 3 4)"), Value::Boolean(true));
|
assert_eq!(eval_str("(> 1 2 3 4 5)"), Value::Boolean(false.into()));
|
||||||
// assert_eq!(eval_str("(/= 1 2 2 4)"), Value::Boolean(false));
|
assert_eq!(eval_str("(>= 5 5 4 3 2 1)"), Value::Boolean(true.into()));
|
||||||
// assert_eq!(eval_str("(= 1 2 3 4)"), Value::Boolean(false));
|
assert_eq!(eval_str("(> 5 5 4 3 2 1)"), Value::Boolean(false.into()));
|
||||||
// assert_eq!(eval_str("(= 1 1 1 1)"), Value::Boolean(true));
|
assert_eq!(eval_str("(<= 1 2 3 3 3 4)"), Value::Boolean(true.into()));
|
||||||
// // logic
|
assert_eq!(eval_str("(/= 1 2 3 4)"), Value::Boolean(true.into()));
|
||||||
// assert_eq!(eval_str("(&& 1 0)"), Value::Boolean(false));
|
assert_eq!(eval_str("(/= 1 2 2 4)"), Value::Boolean(false.into()));
|
||||||
// assert_eq!(eval_str("(&& #t 1 \"yes\")"), Value::Boolean(true));
|
assert_eq!(eval_str("(= 1 2 3 4)"), Value::Boolean(false.into()));
|
||||||
// assert_eq!(eval_str("(|| #f NIL \"\" ())"), Value::Boolean(false));
|
assert_eq!(eval_str("(= 1 1 1 1)"), Value::Boolean(true.into()));
|
||||||
// assert_eq!(eval_str("(|| #f '(1 2 3) \"\" ())"), Value::Boolean(true));
|
// logic
|
||||||
// }
|
// TODO
|
||||||
//
|
// assert_eq!(eval_str("(&& 1 0)"), Value::Boolean(false.into()));
|
||||||
// #[test]
|
// assert_eq!(eval_str("(&& #t 1 \"yes\")"), Value::Boolean(true.into()));
|
||||||
// fn test_abort() {
|
// assert_eq!(
|
||||||
// let err = eval_str_err("(assert (= 1 (+ 1 1)))");
|
// eval_str("(|| #f NIL \"\" ())"),
|
||||||
// let EvalError::Machine(MachineError {
|
// Value::Boolean(false.into())
|
||||||
// error: MachineErrorKind::Aborted(message),
|
// );
|
||||||
// ..
|
// assert_eq!(
|
||||||
// }) = err
|
// eval_str("(|| #f '(1 2 3) \"\" ())"),
|
||||||
// else {
|
// Value::Boolean(true.into())
|
||||||
// panic!("Invalid error returned")
|
// );
|
||||||
// };
|
}
|
||||||
// assert_eq!(&message, "<undefined>: assertion failed: (= 1 (+ 1 1))");
|
|
||||||
//
|
#[test]
|
||||||
// let err = eval_str_err("(abort 1234)");
|
fn test_abort() {
|
||||||
// let EvalError::Machine(MachineError {
|
// let err = eval_str_err("(assert (= 1 (+ 1 1)))");
|
||||||
// error: MachineErrorKind::Aborted(message),
|
// let EvalError::Machine(MachineError {
|
||||||
// ..
|
// error: MachineErrorKind::Aborted(message),
|
||||||
// }) = err
|
// ..
|
||||||
// else {
|
// }) = err
|
||||||
// panic!("Invalid error returned")
|
// else {
|
||||||
// };
|
// panic!("Invalid error returned")
|
||||||
// assert_eq!(&message, "1234");
|
// };
|
||||||
// }
|
// assert_eq!(&message, "<undefined>: assertion failed: (= 1 (+ 1 1))");
|
||||||
//
|
|
||||||
// #[test]
|
// let err = eval_str_err("(abort 1234)");
|
||||||
// fn test_lambda() {
|
// let EvalError::Machine(MachineError {
|
||||||
// assert_eq!(eval_str("((lambda (x) (+ x 1)) 1)"), Value::Integer(2));
|
// error: MachineErrorKind::Aborted(message),
|
||||||
// assert_eq!(
|
// ..
|
||||||
// eval_str("((lambda (x) (+ ((lambda () 2)) 1)) 1)"),
|
// }) = err
|
||||||
// Value::Integer(3)
|
// else {
|
||||||
// );
|
// panic!("Invalid error returned")
|
||||||
// }
|
// };
|
||||||
//
|
// assert_eq!(&message, "1234");
|
||||||
// #[test]
|
}
|
||||||
// fn test_math_behaves_the_same_inside_apply() {
|
|
||||||
// assert_eq!(eval_str("(apply + '(1 2 3 4))"), eval_str("(+ 1 2 3 4)"));
|
#[test]
|
||||||
// assert_eq!(eval_str("(apply * '(1 2 3 4))"), eval_str("(* 1 2 3 4)"));
|
fn test_lambda() {
|
||||||
// assert_eq!(eval_str("(apply - '(1 2 3 4))"), eval_str("(- 1 2 3 4)"));
|
assert_eq!(
|
||||||
// assert_eq!(eval_str("(apply / '(100 25 2))"), eval_str("(/ 100 25 2)"));
|
eval_str("((lambda (x) (+ x 1)) 1)"),
|
||||||
// assert_eq!(eval_str("(apply % '(90 37))"), eval_str("(% 90 37)"));
|
Value::Number(2.into())
|
||||||
// assert_eq!(eval_str("(apply | '(1 2 4))"), eval_str("(| 1 2 4)"));
|
);
|
||||||
// assert_eq!(eval_str("(apply & '(1 3 7))"), eval_str("(& 1 3 7)"));
|
assert_eq!(
|
||||||
// assert_eq!(eval_str("(apply ^ '(1 3 7))"), eval_str("(^ 1 3 7)"));
|
eval_str("((lambda (x) (+ ((lambda () 2)) 1)) 1)"),
|
||||||
// assert_eq!(eval_str("(apply < '(1 2 3 4))"), eval_str("(< 1 2 3 4)"));
|
Value::Number(3.into())
|
||||||
// assert_eq!(eval_str("(apply > '(1 2 3 4))"), eval_str("(> 1 2 3 4)"));
|
);
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// #[test]
|
#[test]
|
||||||
// fn test_setq() {
|
fn test_math_behaves_the_same_inside_apply() {
|
||||||
// assert_eq!(eval_str("(setq a 1234) a\n"), Value::Integer(1234));
|
assert_eq!(eval_str("(apply + '(1 2 3 4))"), eval_str("(+ 1 2 3 4)"));
|
||||||
// }
|
assert_eq!(eval_str("(apply * '(1 2 3 4))"), eval_str("(* 1 2 3 4)"));
|
||||||
//
|
assert_eq!(eval_str("(apply - '(1 2 3 4))"), eval_str("(- 1 2 3 4)"));
|
||||||
// #[test]
|
assert_eq!(eval_str("(apply / '(100 25 2))"), eval_str("(/ 100 25 2)"));
|
||||||
// fn test_let() {
|
assert_eq!(eval_str("(apply % '(90 37))"), eval_str("(% 90 37)"));
|
||||||
// // Should fail
|
// TODO
|
||||||
// eval_str_err("(let (a 1234 b (+ a 1)) NIL)");
|
// assert_eq!(eval_str("(apply | '(1 2 4))"), eval_str("(| 1 2 4)"));
|
||||||
//
|
// assert_eq!(eval_str("(apply & '(1 3 7))"), eval_str("(& 1 3 7)"));
|
||||||
// assert_eq!(
|
// assert_eq!(eval_str("(apply ^ '(1 3 7))"), eval_str("(^ 1 3 7)"));
|
||||||
// eval_str("(let (a 1234 b 4321) (+ a b))"),
|
// assert_eq!(eval_str("(apply < '(1 2 3 4))"), eval_str("(< 1 2 3 4)"));
|
||||||
// Value::Integer(5555)
|
// assert_eq!(eval_str("(apply > '(1 2 3 4))"), eval_str("(> 1 2 3 4)"));
|
||||||
// );
|
}
|
||||||
// assert_eq!(
|
|
||||||
// eval_str("(let* (a 1234 b (+ a 4321)) (+ b 4444))"),
|
#[test]
|
||||||
// Value::Integer(9999)
|
fn test_setq() {
|
||||||
// );
|
assert_eq!(eval_str("(setq a 1234) a\n"), Value::Number(1234.into()));
|
||||||
//
|
}
|
||||||
// // Nested let
|
|
||||||
// assert_eq!(
|
#[test]
|
||||||
// eval_str("(let (a 1234) (let (b 4321) (+ a b)))"),
|
fn test_let() {
|
||||||
// Value::Integer(5555)
|
// Should fail
|
||||||
// );
|
eval_str_err("(let (a 1234 b (+ a 1)) NIL)");
|
||||||
// // Doesn't shadow
|
eval_str_err("(let (a 1234) (let (a 9999 b (+ a 4321)) b))");
|
||||||
// assert_eq!(
|
|
||||||
// eval_str("(let (a 1234) (let (a 9999 b (+ a 4321)) b))"),
|
assert_eq!(
|
||||||
// Value::Integer(5555)
|
eval_str("(let (a 1234 b 4321) (+ a b))"),
|
||||||
// );
|
Value::Number(5555.into())
|
||||||
// // Does shadow
|
);
|
||||||
// assert_eq!(
|
assert_eq!(
|
||||||
// eval_str("(let (a 1234) (let* (a 9999 b (+ a 4321)) b))"),
|
eval_str("(let* (a 1234 b (+ a 4321)) (+ b 4444))"),
|
||||||
// Value::Integer(14320)
|
Value::Number(9999.into())
|
||||||
// );
|
);
|
||||||
// }
|
|
||||||
//
|
// Nested let
|
||||||
// #[test]
|
assert_eq!(
|
||||||
// fn test_macro() {
|
eval_str("(let (a 1234) (let (b 4321) (+ a b)))"),
|
||||||
// assert_eq!(
|
Value::Number(5555.into())
|
||||||
// eval_str("(defmacro stringify (x) (cons 'list `,x)) (stringify (1 2 3 4))"),
|
);
|
||||||
// Value::list_or_nil([
|
// Does shadow
|
||||||
// Value::Integer(1),
|
assert_eq!(
|
||||||
// Value::Integer(2),
|
eval_str("(let (a 1234) (let* (a 9999 b (+ a 4321)) b))"),
|
||||||
// Value::Integer(3),
|
Value::Number(14320.into())
|
||||||
// Value::Integer(4)
|
);
|
||||||
// ])
|
}
|
||||||
// );
|
|
||||||
// }
|
#[test]
|
||||||
|
fn test_macro() {
|
||||||
|
assert_eq!(
|
||||||
|
eval_str("(defmacro stringify (x) (cons 'list `,x)) (stringify (1 2 3 4))"),
|
||||||
|
Value::list_or_nil([
|
||||||
|
Value::Number(1.into()),
|
||||||
|
Value::Number(2.into()),
|
||||||
|
Value::Number(3.into()),
|
||||||
|
Value::Number(4.into())
|
||||||
|
])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user