Value conversions + more useful native functions
This commit is contained in:
@@ -204,7 +204,7 @@ impl<'a> LocalBlock<'a> {
|
||||
Ok(CompileValue::Stack)
|
||||
}
|
||||
|
||||
fn compile_builtin_sub(&mut self, args: &[Expression]) -> Result<CompileValue, CompileError> {
|
||||
fn compile_builtin_sub(&mut self, _args: &[Expression]) -> Result<CompileValue, CompileError> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
@@ -239,7 +239,6 @@ impl<'a> LocalBlock<'a> {
|
||||
}
|
||||
|
||||
fn compile_call(&mut self, call: &CallExpression) -> Result<CompileValue, CompileError> {
|
||||
eprintln!("compile_call({:?}, {:?})", &call.callee, &call.arguments);
|
||||
match call.callee.as_ref() {
|
||||
Expression::Identifier(identifier)
|
||||
if let Some(builtin) = BuiltinFunction::from_identifier(identifier.as_ref()) =>
|
||||
@@ -396,6 +395,7 @@ mod tests {
|
||||
&[
|
||||
Instruction::PushInteger(U::truncate(1)),
|
||||
Instruction::PushConstant(U::truncate(1)),
|
||||
Instruction::GetGlobal,
|
||||
Instruction::Compare(false, Comparison::Gt, U::truncate(2)),
|
||||
]
|
||||
);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use std::{error::Error as StdError, fmt, rc::Rc};
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::vm::value::{ConsCell, Keyword, Value};
|
||||
|
||||
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
use crate::vm::{
|
||||
machine::{Machine, MachineError},
|
||||
native::NativeFunction,
|
||||
value::{BytecodeFunction, ConsCell, Keyword, Value},
|
||||
};
|
||||
|
||||
pub enum AnyFunction {
|
||||
Bytecode(BytecodeFunction),
|
||||
Native(NativeFunction),
|
||||
}
|
||||
|
||||
pub trait TryFromValue<'a>: Sized {
|
||||
fn try_from_value(value: &'a Value) -> Result<Self, MachineError>;
|
||||
|
||||
fn try_from_value_list(mut list_value: &'a Value) -> Result<Vec<Self>, MachineError> {
|
||||
let mut output = vec![];
|
||||
while !list_value.is_nil() {
|
||||
let Value::Cons(cons) = list_value else {
|
||||
return Err(MachineError::InvalidArgument);
|
||||
};
|
||||
let ConsCell(car, cdr) = cons.as_ref();
|
||||
output.push(Self::try_from_value(car)?);
|
||||
list_value = cdr;
|
||||
}
|
||||
Ok(output)
|
||||
}
|
||||
}
|
||||
|
||||
// From values
|
||||
macro_rules! impl_primitive_value {
|
||||
($($t:ty),+ $(,)?) => {
|
||||
$(
|
||||
impl From<$t> for Value {
|
||||
fn from(value: $t) -> Self {
|
||||
Self::Integer(value as i64)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFromValue<'_> for $t {
|
||||
fn try_from_value(value: &Value) -> Result<Self, MachineError> {
|
||||
match value {
|
||||
#[allow(irrefutable_let_patterns)]
|
||||
&Value::Integer(value) if let Ok(value) = value.try_into() => Ok(value),
|
||||
_ => Err(MachineError::InvalidArgument)
|
||||
}
|
||||
}
|
||||
}
|
||||
)+
|
||||
};
|
||||
}
|
||||
|
||||
impl AnyFunction {
|
||||
pub fn invoke(&self, vm: &mut Machine, args: &[Value]) -> Result<Value, MachineError> {
|
||||
match self {
|
||||
Self::Bytecode(bytecode) => Ok(vm.eval_bytecode_call(bytecode.clone(), args).unwrap()),
|
||||
Self::Native(native) => native.invoke(vm, args),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFromValue<'_> for Keyword {
|
||||
fn try_from_value(value: &Value) -> Result<Self, MachineError> {
|
||||
match value {
|
||||
Value::Keyword(value) => Ok(*value),
|
||||
_ => Err(MachineError::InvalidArgument),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFromValue<'_> for bool {
|
||||
fn try_from_value(value: &Value) -> Result<Self, MachineError> {
|
||||
match value {
|
||||
Value::Nil => Ok(false),
|
||||
Value::Cons(_) => Ok(true),
|
||||
Value::Boolean(value) => Ok(*value),
|
||||
Value::Integer(value) => Ok(*value != 0),
|
||||
Value::Keyword(_)
|
||||
| Value::Identifier(_)
|
||||
| Value::OpaqueValue(_)
|
||||
| Value::NativeFunction(_)
|
||||
| Value::BytecodeFunction(_) => Ok(true),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFromValue<'_> for Value {
|
||||
fn try_from_value(value: &Value) -> Result<Self, MachineError> {
|
||||
Ok(value.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFromValue<'a> for &'a Value {
|
||||
fn try_from_value(value: &'a Value) -> Result<Self, MachineError> {
|
||||
Ok(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFromValue<'_> for AnyFunction {
|
||||
fn try_from_value(value: &Value) -> Result<Self, MachineError> {
|
||||
match value {
|
||||
Value::BytecodeFunction(bytecode) => Ok(Self::Bytecode(bytecode.clone())),
|
||||
Value::NativeFunction(native) => Ok(Self::Native(native.clone())),
|
||||
_ => Err(MachineError::InvalidArgument),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl_primitive_value!(i8, i16, i32, i64);
|
||||
impl_primitive_value!(u8, u16, u32, u64);
|
||||
|
||||
impl From<Value> for Result<Value, MachineError> {
|
||||
fn from(value: Value) -> Self {
|
||||
Ok(value)
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -1,6 +1,7 @@
|
||||
#![feature(coverage_attribute, debug_closure_helpers)]
|
||||
#![feature(coverage_attribute, debug_closure_helpers, unboxed_closures)]
|
||||
|
||||
pub mod compile;
|
||||
pub mod convert;
|
||||
pub mod error;
|
||||
pub mod parse;
|
||||
pub mod vm;
|
||||
|
||||
+1
-1
@@ -159,7 +159,7 @@ fn parse_identifier(input: &str) -> IResult<&str, &str> {
|
||||
fn parse_identifier_or_keyword_or_nil(input: &str) -> IResult<&str, Value> {
|
||||
map(parse_identifier, |ident| match ident {
|
||||
"NIL" | "nil" => Value::Nil,
|
||||
_ if let Some(keyword) = Keyword::from_str(ident) => Value::Keyword(keyword),
|
||||
_ if let Some(keyword) = Keyword::parse(ident) => Value::Keyword(keyword),
|
||||
_ => Value::Identifier(ident.into()),
|
||||
})
|
||||
.parse(input)
|
||||
|
||||
+80
-39
@@ -5,8 +5,9 @@ use crate::{
|
||||
vm::{
|
||||
instruction::{Comparison, ConstantId, Instruction, InstructionError},
|
||||
module::{Module, ModuleConstant, ModuleRef},
|
||||
native::NativeFunction,
|
||||
stack::Stack,
|
||||
value::{BytecodeFunction, NativeFunction, Value},
|
||||
value::{BytecodeFunction, Value},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -28,6 +29,8 @@ pub enum MachineError {
|
||||
CallStackOverflow,
|
||||
#[error("Unbound identifier: {0:?}")]
|
||||
UnboundIdentifier(Rc<str>),
|
||||
#[error("Invalid argument provided in a call")]
|
||||
InvalidArgument,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -59,7 +62,8 @@ pub struct Machine {
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum ExecutionEvent {
|
||||
ModuleEntry(ModuleRef),
|
||||
ModuleExit(ModuleRef),
|
||||
BytecodeFunctionExit(BytecodeFunction),
|
||||
None,
|
||||
}
|
||||
|
||||
@@ -75,6 +79,20 @@ impl Default for Machine {
|
||||
}
|
||||
|
||||
impl Machine {
|
||||
pub fn set_global<S: Into<Rc<str>>>(&mut self, identifier: S, value: Value) {
|
||||
self.globals.insert(identifier.into(), value);
|
||||
}
|
||||
|
||||
pub fn defun_native<S, F>(&mut self, identifier: S, function: F)
|
||||
where
|
||||
S: Into<Rc<str>>,
|
||||
F: Fn(&mut Machine, &[Value]) -> Result<Value, MachineError> + 'static,
|
||||
{
|
||||
let identifier = identifier.into();
|
||||
let native = NativeFunction::new(identifier.clone(), function);
|
||||
self.set_global(identifier, Value::NativeFunction(native));
|
||||
}
|
||||
|
||||
fn pop(&mut self) -> Result<Value, MachineError> {
|
||||
self.value_stack
|
||||
.pop()
|
||||
@@ -87,14 +105,34 @@ impl Machine {
|
||||
.map_err(|_| MachineError::ValueStackOverflow)
|
||||
}
|
||||
|
||||
fn enter_bytecode_function(
|
||||
&mut self,
|
||||
bytecode: BytecodeFunction,
|
||||
arguments: Vec<Value>,
|
||||
) -> Result<(), MachineError> {
|
||||
let source_ip = self.ip.clone().unwrap();
|
||||
let frame = CallFrame {
|
||||
arguments,
|
||||
event: ExecutionEvent::BytecodeFunctionExit(bytecode.clone()),
|
||||
return_address: InstructionPointer {
|
||||
module: source_ip.module,
|
||||
address: source_ip.address + 1,
|
||||
},
|
||||
};
|
||||
let BytecodeFunction { module, address } = bytecode;
|
||||
if self.call_stack.push(frame).is_err() {
|
||||
return Err(MachineError::CallStackOverflow);
|
||||
}
|
||||
self.ip = Some(InstructionPointer { module, address });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn execute_call(&mut self, count: usize) -> Result<(), MachineError> {
|
||||
enum Callee {
|
||||
Bytecode(BytecodeFunction),
|
||||
Native(NativeFunction),
|
||||
}
|
||||
|
||||
let source_ip = self.ip.clone().unwrap();
|
||||
|
||||
let callee = self.pop()?;
|
||||
let callee = match callee {
|
||||
Value::BytecodeFunction(bytecode) => Callee::Bytecode(bytecode),
|
||||
@@ -107,22 +145,11 @@ impl Machine {
|
||||
}
|
||||
match callee {
|
||||
Callee::Bytecode(bytecode) => {
|
||||
let BytecodeFunction { module, address } = bytecode;
|
||||
let frame = CallFrame {
|
||||
arguments,
|
||||
event: ExecutionEvent::None,
|
||||
return_address: InstructionPointer {
|
||||
module: source_ip.module,
|
||||
address: source_ip.address + 1,
|
||||
},
|
||||
};
|
||||
if self.call_stack.push(frame).is_err() {
|
||||
return Err(MachineError::CallStackOverflow);
|
||||
}
|
||||
self.ip = Some(InstructionPointer { module, address });
|
||||
self.enter_bytecode_function(bytecode, arguments)?;
|
||||
}
|
||||
Callee::Native(native) => {
|
||||
let result = native.apply(self, &arguments).unwrap();
|
||||
let source_ip = self.ip.clone().unwrap();
|
||||
let result = native.invoke(self, &arguments)?;
|
||||
self.push(result)?;
|
||||
self.ip = Some(InstructionPointer {
|
||||
module: source_ip.module,
|
||||
@@ -140,7 +167,7 @@ impl Machine {
|
||||
Ok(frame.event)
|
||||
} else {
|
||||
self.ip = None;
|
||||
Ok(ExecutionEvent::ModuleEntry(ip.module))
|
||||
Ok(ExecutionEvent::ModuleExit(ip.module))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,7 +186,7 @@ impl Machine {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn execute_sub(&mut self, count: usize) -> Result<(), MachineError> {
|
||||
fn execute_sub(&mut self, _count: usize) -> Result<(), MachineError> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
@@ -272,10 +299,6 @@ impl Machine {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_global<S: Into<Rc<str>>>(&mut self, identifier: S, value: Value) {
|
||||
self.globals.insert(identifier.into(), value);
|
||||
}
|
||||
|
||||
pub fn execute_next(&mut self) -> Result<ExecutionEvent, MachineError> {
|
||||
let ip = self.ip.clone().unwrap();
|
||||
let instruction = ip
|
||||
@@ -344,6 +367,27 @@ impl Machine {
|
||||
Ok(event)
|
||||
}
|
||||
|
||||
pub fn eval_bytecode_call(
|
||||
&mut self,
|
||||
function: BytecodeFunction,
|
||||
args: &[Value],
|
||||
) -> Result<Value, MachineError> {
|
||||
let expect = ExecutionEvent::BytecodeFunctionExit(function.clone());
|
||||
self.enter_bytecode_function(function, args.into())?;
|
||||
loop {
|
||||
let event = match self.execute_next() {
|
||||
Ok(event) => event,
|
||||
// TODO rework error handling
|
||||
Err(_error) => todo!(),
|
||||
};
|
||||
if event == expect {
|
||||
break;
|
||||
}
|
||||
}
|
||||
let value = self.pop()?;
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
pub fn load_module(&mut self, module: ModuleRef) -> Result<ModuleRef, MachineError> {
|
||||
let entry = module.entry();
|
||||
let entry_ip = InstructionPointer {
|
||||
@@ -356,7 +400,7 @@ impl Machine {
|
||||
.push(CallFrame {
|
||||
arguments: vec![],
|
||||
return_address: ip,
|
||||
event: ExecutionEvent::ModuleEntry(module.clone()),
|
||||
event: ExecutionEvent::ModuleExit(module.clone()),
|
||||
})
|
||||
.is_err()
|
||||
{
|
||||
@@ -371,7 +415,7 @@ impl Machine {
|
||||
Ok(module) => module,
|
||||
Err(error) => return EvalResult::LoadErr(error),
|
||||
};
|
||||
let expect = ExecutionEvent::ModuleEntry(module.clone());
|
||||
let expect = ExecutionEvent::ModuleExit(module.clone());
|
||||
loop {
|
||||
let ip = self.ip.clone();
|
||||
let event = match self.execute_next() {
|
||||
@@ -434,7 +478,8 @@ mod tests {
|
||||
instruction::{Instruction, U},
|
||||
machine::{InstructionPointer, Machine},
|
||||
module::{Module, ModuleBuilder, ModuleConstant, ModuleRef},
|
||||
value::{NativeFunction, Value},
|
||||
native::NativeFunction,
|
||||
value::Value,
|
||||
};
|
||||
|
||||
fn execute_all<F: Fn(u32, &mut ModuleBuilder), G: FnOnce(&mut Machine)>(
|
||||
@@ -560,18 +605,14 @@ mod tests {
|
||||
_ => unreachable!(),
|
||||
},
|
||||
|m| {
|
||||
m.set_global(
|
||||
"native",
|
||||
Value::NativeFunction(NativeFunction::new("native", |_, args| {
|
||||
assert_eq!(args.len(), 3);
|
||||
assert_eq!(
|
||||
&args,
|
||||
&[Value::Integer(3), Value::Integer(2), Value::Integer(1)]
|
||||
);
|
||||
NATIVE_STATE.store(4321, Ordering::Release);
|
||||
Ok(Value::Integer(1234))
|
||||
})),
|
||||
);
|
||||
m.defun_native("native", |_vm, args| {
|
||||
assert_eq!(
|
||||
&args,
|
||||
&[Value::Integer(3), Value::Integer(2), Value::Integer(1)]
|
||||
);
|
||||
NATIVE_STATE.store(4321, Ordering::Release);
|
||||
Ok(Value::Integer(1234))
|
||||
});
|
||||
},
|
||||
);
|
||||
assert!(m.value_stack.is_empty());
|
||||
|
||||
@@ -2,6 +2,7 @@ pub mod instruction;
|
||||
pub mod loader;
|
||||
pub mod machine;
|
||||
pub mod module;
|
||||
pub mod native;
|
||||
pub mod pool;
|
||||
pub mod prelude;
|
||||
pub mod stack;
|
||||
|
||||
+22
-7
@@ -130,10 +130,17 @@ impl Module {
|
||||
|
||||
print!("{i:4}: {instruction:08x}");
|
||||
if let Ok(instruction) = Instruction::try_from(*instruction) {
|
||||
println!(" {instruction:?}");
|
||||
} else {
|
||||
println!();
|
||||
print!(" {instruction:?}");
|
||||
|
||||
if let Instruction::PushConstant(id) = instruction {
|
||||
if let Some(value) = self.constant(id) {
|
||||
print!(" [-> {value}]");
|
||||
} else {
|
||||
print!(" UNDEFINED");
|
||||
}
|
||||
}
|
||||
}
|
||||
println!();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -171,14 +178,22 @@ impl ModuleBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ModuleConstant {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Integer(value) => fmt::Display::fmt(value, f),
|
||||
Self::Identifier(ident) => write!(f, "ident {ident:?}"),
|
||||
Self::LocalFunction(address) => write!(f, "label {address}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::vm::{
|
||||
instruction::{Instruction, U},
|
||||
module::{Module, ModuleConstant},
|
||||
value::{Keyword, Value},
|
||||
module::Module,
|
||||
value::Value,
|
||||
};
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
use std::{fmt, rc::Rc};
|
||||
|
||||
use crate::vm::{
|
||||
machine::{Machine, MachineError},
|
||||
value::Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct NativeFunction {
|
||||
name: Rc<str>,
|
||||
inner: Rc<dyn Fn(&mut Machine, &[Value]) -> Result<Value, MachineError> + 'static>,
|
||||
}
|
||||
|
||||
impl NativeFunction {
|
||||
pub fn new<S, F>(name: S, function: F) -> Self
|
||||
where
|
||||
S: Into<Rc<str>>,
|
||||
F: Fn(&mut Machine, &[Value]) -> Result<Value, MachineError> + 'static,
|
||||
{
|
||||
Self {
|
||||
name: name.into(),
|
||||
inner: Rc::new(function),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn invoke(
|
||||
&self,
|
||||
machine: &mut Machine,
|
||||
arguments: &[Value],
|
||||
) -> Result<Value, MachineError> {
|
||||
(self.inner)(machine, arguments)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for NativeFunction {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.name == other.name
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for NativeFunction {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("NativeFunction")
|
||||
.field("name", &self.name)
|
||||
.field_with("inner", |f| write!(f, "{:p}", self.inner))
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for NativeFunction {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "<native {:?} {:p}>", self.name, self.inner)
|
||||
}
|
||||
}
|
||||
+28
-2
@@ -1,3 +1,29 @@
|
||||
use crate::vm::machine::Machine;
|
||||
use crate::{
|
||||
convert::{AnyFunction, TryFromValue},
|
||||
vm::{
|
||||
machine::{Machine, MachineError},
|
||||
value::Value,
|
||||
},
|
||||
};
|
||||
|
||||
pub fn load(_machine: &mut Machine) {}
|
||||
pub fn load(vm: &mut Machine) {
|
||||
vm.defun_native("map", |vm, args| {
|
||||
let [f, xs] = args else {
|
||||
return Err(MachineError::InvalidArgument);
|
||||
};
|
||||
let f = AnyFunction::try_from_value(f)?;
|
||||
let xs = xs.proper_iter();
|
||||
let out = Value::try_list_or_nil(xs.map(|v| f.invoke(vm, &[v?.clone()])))?;
|
||||
Ok(out)
|
||||
});
|
||||
vm.defun_native("identity", |_, args| {
|
||||
let [arg] = args else {
|
||||
return Err(MachineError::InvalidArgument);
|
||||
};
|
||||
Ok(arg.clone())
|
||||
});
|
||||
vm.defun_native("list", |_, args| {
|
||||
let out = Value::list_or_nil(args.iter().cloned());
|
||||
Ok(out)
|
||||
});
|
||||
}
|
||||
|
||||
+48
-45
@@ -1,17 +1,6 @@
|
||||
use std::{any::Any, fmt, rc::Rc};
|
||||
|
||||
use crate::vm::{machine::Machine, module::ModuleRef};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
InvalidArgument,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct NativeFunction {
|
||||
name: Rc<str>,
|
||||
inner: fn(&mut Machine, &[Value]) -> Result<Value, Error>,
|
||||
}
|
||||
use crate::vm::{machine::MachineError, module::ModuleRef, native::NativeFunction};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct BytecodeFunction {
|
||||
@@ -24,6 +13,10 @@ pub struct OpaqueValue {
|
||||
inner: Rc<dyn Any>,
|
||||
}
|
||||
|
||||
pub struct ProperListIter<'a> {
|
||||
head: Option<&'a Value>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Value {
|
||||
// "Expression" values
|
||||
@@ -39,9 +32,28 @@ pub enum Value {
|
||||
OpaqueValue(OpaqueValue),
|
||||
}
|
||||
|
||||
impl<'a> Iterator for ProperListIter<'a> {
|
||||
type Item = Result<&'a Value, MachineError>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let head = self.head.take()?;
|
||||
match head {
|
||||
Value::Cons(cons) => {
|
||||
let ConsCell(car, cdr) = cons.as_ref();
|
||||
self.head = Some(cdr);
|
||||
Some(Ok(car))
|
||||
}
|
||||
Value::Nil => None,
|
||||
_ => Some(Err(MachineError::InvalidArgument)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl OpaqueValue {
|
||||
pub fn cast<T: 'static>(&self) -> Result<&T, Error> {
|
||||
self.inner.downcast_ref().ok_or(Error::InvalidArgument)
|
||||
pub fn cast<T: 'static>(&self) -> Result<&T, MachineError> {
|
||||
self.inner
|
||||
.downcast_ref()
|
||||
.ok_or(MachineError::InvalidArgument)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,7 +95,7 @@ macro_rules! impl_keyword {
|
||||
}
|
||||
|
||||
impl $name {
|
||||
pub fn from_str(s: &str) -> Option<Self> {
|
||||
pub fn parse(s: &str) -> Option<Self> {
|
||||
match s {
|
||||
$(
|
||||
$s => Some(Self::$variant),
|
||||
@@ -122,14 +134,18 @@ impl_keyword! {
|
||||
pub struct ConsCell(pub Value, pub Value);
|
||||
|
||||
impl Value {
|
||||
pub fn proper_iter(&self) -> ProperListIter<'_> {
|
||||
ProperListIter { head: Some(self) }
|
||||
}
|
||||
|
||||
pub fn is_nil(&self) -> bool {
|
||||
matches!(self, Self::Nil)
|
||||
}
|
||||
|
||||
pub fn as_opaque<T: 'static>(&self) -> Result<&T, Error> {
|
||||
pub fn as_opaque<T: 'static>(&self) -> Result<&T, MachineError> {
|
||||
match self {
|
||||
Self::OpaqueValue(opaque) => opaque.cast(),
|
||||
_ => Err(Error::InvalidArgument),
|
||||
_ => Err(MachineError::InvalidArgument),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,10 +153,25 @@ impl Value {
|
||||
Self::Cons(Rc::new(ConsCell(self, cdr)))
|
||||
}
|
||||
|
||||
pub fn try_list_or_nil<I: IntoIterator<Item = Result<Self, MachineError>>>(
|
||||
items: I,
|
||||
) -> Result<Self, MachineError> {
|
||||
Self::try_list_or_nil_inner(&mut items.into_iter())
|
||||
}
|
||||
|
||||
pub fn list_or_nil<I: IntoIterator<Item = Self>>(items: I) -> Self {
|
||||
Self::list_or_nil_inner(&mut items.into_iter())
|
||||
}
|
||||
|
||||
fn try_list_or_nil_inner<I: Iterator<Item = Result<Self, MachineError>>>(
|
||||
items: &mut I,
|
||||
) -> Result<Self, MachineError> {
|
||||
match items.next() {
|
||||
Some(value) => Ok(value?.cons(Self::try_list_or_nil_inner(items)?)),
|
||||
None => Ok(Self::Nil),
|
||||
}
|
||||
}
|
||||
|
||||
fn list_or_nil_inner<I: Iterator<Item = Self>>(items: &mut I) -> Self {
|
||||
match items.next() {
|
||||
Some(value) => value.cons(Self::list_or_nil_inner(items)),
|
||||
@@ -170,40 +201,12 @@ impl ConsCell {
|
||||
}
|
||||
}
|
||||
|
||||
impl NativeFunction {
|
||||
pub fn new<S: Into<Rc<str>>>(
|
||||
name: S,
|
||||
inner: fn(&mut Machine, &[Value]) -> Result<Value, Error>,
|
||||
) -> Self {
|
||||
Self {
|
||||
name: name.into(),
|
||||
inner,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn apply(&self, machine: &mut Machine, arguments: &[Value]) -> Result<Value, Error> {
|
||||
(self.inner)(machine, arguments)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for NativeFunction {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.name == other.name
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ConsCell {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.fmt_inner(f, true)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for NativeFunction {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "<native {:?} {:p}>", self.name, self.inner)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for BytecodeFunction {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "<bytecode {:p}:{}>", self.module, self.address)
|
||||
|
||||
Reference in New Issue
Block a user