Implement setq/let/let* bindings

This commit is contained in:
2026-05-06 09:46:34 +03:00
parent 2c84f1fbb2
commit bad52a57c3
17 changed files with 584 additions and 44 deletions
+12
View File
@@ -12,3 +12,15 @@
(assert (= 0 (^ 1 3 5 7) (apply ^ (list 1 3 5 7))))
(assert (= #t (&& #t #t) (apply && (list #t #t))))
(assert (= #f (|| #f #f) (apply || (list #f #f))))
(setq a 1)
(assert (= a 1))
(assert (= 6
(let (a 1)
(let (b 2)
(+ a b 3)
)
)
))
(assert (/= (let (a 2) a) a))
+131 -12
View File
@@ -8,7 +8,7 @@ use crate::{
module::CompilationModule,
syntax::{
CallExpression, CondExpression, DefunExpression, Expression, FunctionBody,
IfExpression, LambdaExpression,
IfExpression, LambdaExpression, LetExpression, SetqExpression,
},
value::{BuiltinFunction, CompileConstant, CompileValue},
},
@@ -19,18 +19,49 @@ pub struct CompiledFunction {
pub(crate) instructions: Vec<Instruction>,
}
struct LocalScope {
start_index: u32,
locals: Vec<Rc<str>>,
}
pub struct FunctionBlock {
pub(crate) instructions: Vec<Emitted>,
labels: HashMap<u32, usize>,
last_label: u32,
signature: FunctionSignature,
locals_stack: Vec<LocalScope>,
max_local_index: u32,
}
pub struct LocalBlock<'a> {
// TODO local bindings
function: &'a mut FunctionBlock,
module: &'a mut CompilationModule,
parent: Option<&'a mut LocalBlock<'a>>,
}
impl LocalScope {
pub fn get_or_insert(&mut self, value: Rc<str>) -> Result<U<16>, CompileError> {
let index = if let Some(index) = self.locals.iter().position(|v| *v == value) {
index
} else {
let index = self.locals.len();
self.locals.push(value);
index
} + self.start_index as usize;
Ok(U::new(index as u32).unwrap())
}
pub fn get(&self, value: &str) -> Option<U<16>> {
self.locals
.iter()
.position(|v| v.as_ref() == value)
.map(|v| v + self.start_index as usize)
.and_then(|v| U::new(v as u32))
}
fn end_index(&self) -> u32 {
self.start_index + self.locals.len() as u32
}
}
impl FunctionBlock {
@@ -40,6 +71,8 @@ impl FunctionBlock {
labels: HashMap::new(),
last_label: 0,
signature,
locals_stack: Vec::new(),
max_local_index: 0,
}
}
@@ -50,6 +83,43 @@ impl FunctionBlock {
id
}
fn push_local_scope(&mut self) -> &mut LocalScope {
let start_index = self
.local_scope_ref()
.map(LocalScope::end_index)
.unwrap_or(0);
let scope = LocalScope {
locals: vec![],
start_index,
};
self.locals_stack.push_mut(scope)
}
fn pop_local_scope(&mut self) {
let scope = self
.locals_stack
.pop()
.expect("Local scope stack underflow");
self.max_local_index = self.max_local_index.max(scope.end_index());
}
fn local_scope_mut(&mut self) -> Option<&mut LocalScope> {
self.locals_stack.last_mut()
}
fn local_scope_ref(&self) -> Option<&LocalScope> {
self.locals_stack.last()
}
fn lookup_local(&self, identifier: &str) -> Option<U<16>> {
for scope in self.locals_stack.iter().rev() {
if let Some(index) = scope.get(identifier) {
return Some(index);
}
}
None
}
pub fn adjust_label(&mut self, label: u32) {
self.labels.insert(label, self.instructions.len());
}
@@ -105,11 +175,7 @@ impl FunctionBlock {
impl<'a> LocalBlock<'a> {
fn root(function: &'a mut FunctionBlock, module: &'a mut CompilationModule) -> Self {
Self {
function,
module,
parent: None,
}
Self { function, module }
}
fn compile_set_global(
@@ -126,6 +192,21 @@ impl<'a> LocalBlock<'a> {
Ok(())
}
fn compile_set_local(
&mut self,
identifier: &Rc<str>,
value: CompileValue,
) -> Result<(), CompileError> {
let index = self
.function
.local_scope_mut()
.unwrap()
.get_or_insert(identifier.clone())?;
self.compile_push(value)?;
self.function.emit(Instruction::SetLocal(index));
Ok(())
}
fn compile_push(&mut self, value: CompileValue) -> Result<(), CompileError> {
match value {
CompileValue::Nil => {
@@ -138,6 +219,9 @@ impl<'a> LocalBlock<'a> {
self.function.emit(Instruction::PushConstant(value));
self.function.emit(Instruction::GetGlobal);
}
CompileValue::Local(index) => {
self.function.emit(Instruction::GetLocal(index));
}
CompileValue::Boolean(value) => {
self.function.emit(Instruction::PushBool(value));
}
@@ -188,6 +272,9 @@ impl<'a> LocalBlock<'a> {
}
fn compile_identifier(&mut self, identifier: &Rc<str>) -> Result<CompileValue, CompileError> {
if let Some(local) = self.function.lookup_local(identifier) {
return Ok(CompileValue::Local(local));
}
if let Some(argument) = self.function.signature.argument(identifier) {
return Ok(CompileValue::Argument(argument));
}
@@ -316,6 +403,34 @@ impl<'a> LocalBlock<'a> {
Ok(CompileValue::Stack)
}
fn compile_let(&mut self, binding: &LetExpression) -> Result<CompileValue, CompileError> {
self.function.push_local_scope();
for pair in &binding.bindings {
let value = self.compile_expression(&pair.value)?;
self.compile_set_local(&pair.identifier, value)?;
}
for expr in &binding.body.head {
self.compile_statement(expr)?;
}
let value = self.compile_expression(&binding.body.tail)?;
if let CompileValue::Local(_) = value {
self.compile_push(value)?;
self.function.pop_local_scope();
Ok(CompileValue::Stack)
} else {
self.function.pop_local_scope();
Ok(value)
}
}
fn compile_setq(&mut self, setq: &SetqExpression) -> Result<CompileValue, CompileError> {
for pair in setq.pairs.iter() {
let value = self.compile_expression(&pair.value)?;
self.compile_set_global(&pair.identifier, value)?;
}
Ok(CompileValue::Nil)
}
pub fn compile_expression(
&mut self,
expression: &Expression,
@@ -330,6 +445,8 @@ impl<'a> LocalBlock<'a> {
Expression::Call(call) => self.compile_call(call),
Expression::If(condition) => self.compile_if(condition),
Expression::Cond(condition) => self.compile_cond(condition),
Expression::Let(binding) => self.compile_let(binding),
Expression::Setq(setq) => self.compile_setq(setq),
Expression::SyntaxError(_) => unreachable!(),
}
@@ -351,7 +468,7 @@ mod tests {
},
value::{CompileConstant, CompileValue},
},
vm::instruction::{Instruction, U},
vm::instruction::{Instruction, MathInstruction, U},
};
fn test_compile(
@@ -367,6 +484,8 @@ mod tests {
labels: HashMap::new(),
last_label: 0,
instructions: vec![],
locals_stack: vec![],
max_local_index: 0,
};
let mut local = LocalBlock::root(&mut function, &mut module);
let value = local.compile_expression(expression).unwrap();
@@ -400,7 +519,7 @@ mod tests {
Instruction::PushInteger(U::truncate(1)),
Instruction::PushConstant(U::truncate(1)),
Instruction::GetGlobal,
Instruction::Compare(false, Comparison::Gt, U::truncate(2)),
Instruction::Math(MathInstruction::Gt, U::truncate(2)),
]
);
assert_eq!(v, CompileValue::Stack);
@@ -518,7 +637,7 @@ mod tests {
&[
Instruction::PushArgument(U::truncate(1)),
Instruction::PushArgument(U::truncate(0)),
Instruction::Add(U::truncate(2)),
Instruction::Math(MathInstruction::Add, U::truncate(2)),
Instruction::Return
]
);
@@ -561,7 +680,7 @@ mod tests {
&[
Instruction::PushInteger(U::truncate(1)),
Instruction::PushArgument(U::truncate(0)),
Instruction::Add(U::truncate(2)),
Instruction::Math(MathInstruction::Add, U::truncate(2)),
Instruction::Return
]
);
@@ -571,7 +690,7 @@ mod tests {
Instruction::PushInteger(U::truncate(2)),
Instruction::PushConstant(U::truncate(1)),
Instruction::Call(U::truncate(0)),
Instruction::Add(U::truncate(2))
Instruction::Math(MathInstruction::Add, U::truncate(2))
]
);
}
+2
View File
@@ -1,3 +1,5 @@
#![coverage(off)]
use crate::compile::syntax::ParseError;
#[derive(Debug, thiserror::Error)]
+4 -1
View File
@@ -9,4 +9,7 @@ mod value;
pub use error::CompileError;
pub use function::FunctionSignature;
pub use module::CompilationModule;
pub use syntax::{CallExpression, Expression, FunctionBody, LambdaExpression, ParseError};
pub use syntax::{
CallExpression, ExpectedWhat, ExpectedWhere, Expression, FunctionBody, LambdaExpression,
ParseError, ParseErrorKind,
};
+293
View File
@@ -0,0 +1,293 @@
use std::rc::Rc;
use crate::{
compile::{
ExpectedWhat, ExpectedWhere, Expression, FunctionBody, ParseError, ParseErrorKind,
syntax::CollectErrors,
},
vm::value::{ConsCell, Keyword, Value},
};
#[derive(Debug, PartialEq)]
pub struct Assignment {
pub identifier: Rc<str>,
pub value: Expression,
}
#[derive(Debug, PartialEq)]
pub struct SetqExpression {
pub pairs: Vec<Assignment>,
}
#[derive(Debug, PartialEq)]
pub struct LetExpression {
pub sequential: bool,
pub bindings: Vec<Assignment>,
pub body: FunctionBody,
}
impl Assignment {
fn parse_list(value: &Value, input: &Value, after: Keyword) -> Result<Vec<Self>, ParseError> {
let mut iter = value.syntax_iter(ExpectedWhere::InAssignment);
let mut pairs = vec![];
loop {
let [identifier, value] = match iter.next_chunk::<2>() {
Ok(pair) => pair,
Err(rest) => {
if !rest.is_empty() {
return Err(ParseError {
input: input.clone(),
error: ParseErrorKind::try_collect_extraneous(
rest.map(|x| x.cloned()),
)?,
});
}
break;
}
};
let Value::Identifier(identifier) = identifier? else {
return Err(ParseError {
input: input.clone(),
error: ParseErrorKind::Expected(
ExpectedWhat::Identifier,
ExpectedWhere::InAssignment,
),
});
};
let value = Expression::parse_inner(value?);
pairs.push(Self {
identifier: identifier.clone(),
value,
});
}
if pairs.is_empty() {
return Err(ParseError {
input: input.clone(),
error: ParseErrorKind::Expected(
ExpectedWhat::AtLeastOneBinding,
ExpectedWhere::AfterKeyword(after),
),
});
}
Ok(pairs)
}
}
impl LetExpression {
pub(super) fn parse(
value: &Value,
input: &Value,
keyword: Keyword,
) -> Result<Self, ParseError> {
let Value::Cons(value) = value else {
return Err(ParseError {
input: input.clone(),
error: ParseErrorKind::Expected(
ExpectedWhat::ProperList,
ExpectedWhere::AfterKeyword(keyword),
),
});
};
let ConsCell(bindings, cdr) = value.as_ref();
let bindings = Assignment::parse_list(bindings, input, keyword)?;
let body = FunctionBody::parse(cdr, input, keyword)?;
let sequential = keyword == Keyword::LetStar;
Ok(Self {
sequential,
bindings,
body,
})
}
}
impl SetqExpression {
pub(super) fn parse(value: &Value, input: &Value) -> Result<Self, ParseError> {
let pairs = Assignment::parse_list(value, input, Keyword::Setq)?;
Ok(Self { pairs })
}
}
impl CollectErrors<ParseError> for SetqExpression {
fn collect_errors(&self, errors: &mut Vec<ParseError>) -> bool {
self.pairs.iter().fold(false, |acc, v| {
let r = v.collect_errors(errors);
acc | r
})
}
}
impl CollectErrors<ParseError> for LetExpression {
fn collect_errors(&self, errors: &mut Vec<ParseError>) -> bool {
let r0 = self.bindings.iter().fold(false, |acc, v| {
let r = v.collect_errors(errors);
acc | r
});
let r1 = self.body.collect_errors(errors);
r0 | r1
}
}
impl CollectErrors<ParseError> for Assignment {
fn collect_errors(&self, errors: &mut Vec<ParseError>) -> bool {
self.value.collect_errors(errors)
}
}
#[cfg(test)]
mod tests {
use std::rc::Rc;
use crate::{
compile::{
ExpectedWhat, ExpectedWhere, Expression, FunctionBody, ParseError, ParseErrorKind,
syntax::{Assignment, LetExpression, SetqExpression},
},
vm::value::{ConsCell, Keyword, Value},
};
#[test]
fn test_parse_let() {
let v = Value::list_or_nil([
Value::Keyword(Keyword::Let),
Value::list_or_nil([Value::Identifier("a".into()), Value::Integer(1)]),
Value::Nil,
]);
let e = Expression::parse(&v).unwrap();
assert_eq!(
e,
Expression::Let(LetExpression {
sequential: false,
bindings: vec![Assignment {
identifier: "a".into(),
value: Expression::IntegerLiteral(1)
},],
body: FunctionBody {
head: vec![],
tail: Rc::new(Expression::Nil)
},
})
);
let v = Value::list_or_nil([
Value::Keyword(Keyword::LetStar),
Value::list_or_nil([
Value::Identifier("a".into()),
Value::Integer(1),
Value::Identifier("b".into()),
Value::Integer(2),
]),
Value::Nil,
]);
let e = Expression::parse(&v).unwrap();
assert_eq!(
e,
Expression::Let(LetExpression {
sequential: true,
bindings: vec![
Assignment {
identifier: "a".into(),
value: Expression::IntegerLiteral(1)
},
Assignment {
identifier: "b".into(),
value: Expression::IntegerLiteral(2)
}
],
body: FunctionBody {
head: vec![],
tail: Rc::new(Expression::Nil)
},
})
);
// syntax errors
let v = Value::Cons(Rc::new(ConsCell(
Value::Keyword(Keyword::Let),
Value::Identifier("a".into()),
)));
let e = Expression::parse(&v).unwrap_err();
assert_eq!(
&e[..],
&[ParseError {
input: v,
error: ParseErrorKind::Expected(
ExpectedWhat::ProperList,
ExpectedWhere::AfterKeyword(Keyword::Let)
)
}]
);
}
#[test]
fn test_parse_setq() {
let v = Value::list_or_nil([
Value::Keyword(Keyword::Setq),
Value::Identifier("a".into()),
Value::Identifier("b".into()),
Value::Identifier("c".into()),
Value::Identifier("d".into()),
]);
let e = Expression::parse(&v).unwrap();
assert_eq!(
e,
Expression::Setq(SetqExpression {
pairs: vec![
Assignment {
identifier: "a".into(),
value: Expression::Identifier("b".into()),
},
Assignment {
identifier: "c".into(),
value: Expression::Identifier("d".into()),
}
]
})
);
// syntax errors
let v = Value::list_or_nil([Value::Keyword(Keyword::Setq)]);
let e = Expression::parse(&v).unwrap_err();
assert_eq!(
&e[..],
&[ParseError {
input: v,
error: ParseErrorKind::Expected(
ExpectedWhat::AtLeastOneBinding,
ExpectedWhere::AfterKeyword(Keyword::Setq)
)
}]
);
let v = Value::list_or_nil([
Value::Keyword(Keyword::Setq),
Value::Identifier("a".into()),
Value::Integer(1),
Value::Identifier("b".into()),
]);
let e = Expression::parse(&v).unwrap_err();
assert_eq!(
&e[..],
&[ParseError {
input: v,
error: ParseErrorKind::ExtraneousExpressions(Rc::new(ConsCell(
Value::Identifier("b".into()),
Value::Nil
)))
}]
);
let v = Value::list_or_nil([
Value::Keyword(Keyword::Setq),
Value::Integer(1),
Value::Identifier("a".into()),
]);
let e = Expression::parse(&v).unwrap_err();
assert_eq!(
&e[..],
&[ParseError {
input: v,
error: ParseErrorKind::Expected(
ExpectedWhat::Identifier,
ExpectedWhere::InAssignment
)
}]
);
}
}
+1 -1
View File
@@ -481,7 +481,7 @@ mod tests {
e,
vec![ParseError {
input: v,
error: ParseErrorKind::ExtraneousExpressionList(Rc::new(ConsCell::end(
error: ParseErrorKind::ExtraneousExpressions(Rc::new(ConsCell::end(
Value::Integer(3)
)))
}]
+16 -2
View File
@@ -19,7 +19,7 @@ pub enum ParseErrorKind {
#[error("Extraneous expression: {0}")]
ExtraneousExpression(Value),
#[error("Extraneous expression(s): {0}")]
ExtraneousExpressionList(Rc<ConsCell>),
ExtraneousExpressions(Rc<ConsCell>),
}
#[derive(Debug, Clone, PartialEq, thiserror::Error)]
@@ -42,6 +42,8 @@ pub enum ExpectedWhat {
ProperList,
#[error("an identifier")]
Identifier,
#[error("at least one binding pair")]
AtLeastOneBinding,
}
#[derive(Debug, Clone, PartialEq, thiserror::Error)]
@@ -62,6 +64,8 @@ pub enum ExpectedWhere {
InConditionArm,
#[error("in the function definition")]
InFunctionDefinition,
#[error("in assignment")]
InAssignment,
}
pub(super) trait CollectErrors<E> {
@@ -84,8 +88,18 @@ impl ParseErrorKind {
pub fn extraneous(value: &Value) -> Self {
match value {
Value::Nil => unreachable!(),
Value::Cons(cons) => Self::ExtraneousExpressionList(cons.clone()),
Value::Cons(cons) => Self::ExtraneousExpressions(cons.clone()),
_ => Self::ExtraneousExpression(value.clone()),
}
}
pub fn collect_extraneous<I: IntoIterator<Item = Value>>(iter: I) -> Self {
Self::extraneous(&Value::list_or_nil(iter))
}
pub fn try_collect_extraneous<E, I: IntoIterator<Item = Result<Value, E>>>(
iter: I,
) -> Result<Self, E> {
Ok(Self::extraneous(&Value::try_list_or_nil(iter)?))
}
}
+12
View File
@@ -2,12 +2,14 @@ use std::rc::Rc;
use crate::vm::value::{ConsCell, Keyword, Value};
mod binding;
mod call;
mod condition;
mod error;
mod function;
mod lambda;
pub use binding::*;
pub use call::*;
pub use condition::*;
pub use error::*;
@@ -25,6 +27,8 @@ pub enum Expression {
Call(CallExpression),
If(IfExpression),
Cond(CondExpression),
Setq(SetqExpression),
Let(LetExpression),
SyntaxError(ParseError),
}
@@ -67,6 +71,12 @@ impl Expression {
Value::Keyword(Keyword::Defun) => {
Self::map_or(DefunExpression::parse(cdr, value), Expression::Defun)
}
Value::Keyword(keyword @ (Keyword::Let | Keyword::LetStar)) => {
Self::map_or(LetExpression::parse(cdr, value, *keyword), Expression::Let)
}
Value::Keyword(Keyword::Setq) => {
Self::map_or(SetqExpression::parse(cdr, value), Expression::Setq)
}
_ => Self::map_or(CallExpression::parse(cons, value), Expression::Call),
}
}
@@ -90,6 +100,8 @@ impl CollectErrors<ParseError> for Expression {
Self::Lambda(lambda) => lambda.collect_errors(errors),
Self::Defun(defun) => defun.collect_errors(errors),
Self::Call(call) => call.collect_errors(errors),
Self::Let(let_) => let_.collect_errors(errors),
Self::Setq(setq) => setq.collect_errors(errors),
Self::Nil | Self::IntegerLiteral(_) | Self::Identifier(_) | Self::BooleanLiteral(_) => {
false
}
+2 -1
View File
@@ -1,12 +1,13 @@
use std::rc::Rc;
use crate::vm::instruction::MathInstruction;
use crate::vm::instruction::{MathInstruction, U};
#[derive(Debug, PartialEq)]
pub enum CompileValue {
Integer(i64),
Boolean(bool),
LocalFunction(u32, usize),
Local(U<16>),
Argument(usize),
Global(Rc<str>),
Stack,
+2
View File
@@ -1,3 +1,5 @@
#![coverage(off)]
use crate::{compile::CompileError, vm::machine::MachineError};
#[derive(Debug, thiserror::Error)]
+7 -1
View File
@@ -1,4 +1,10 @@
#![feature(coverage_attribute, debug_closure_helpers, unboxed_closures)]
#![feature(
coverage_attribute,
debug_closure_helpers,
unboxed_closures,
iter_next_chunk,
exact_size_is_empty
)]
pub mod compile;
pub mod convert;
+6 -1
View File
@@ -80,6 +80,8 @@ pub enum Instruction {
Drop,
SetGlobal,
GetGlobal,
SetLocal(U<16>),
GetLocal(U<16>),
Call(U<6>),
Return,
Math(MathInstruction, U<6>),
@@ -145,6 +147,8 @@ impl From<Instruction> for u32 {
Instruction::Return => 0b0000_0000_0000_0100,
Instruction::SetGlobal => 0b0000_0000_0000_0101,
Instruction::GetGlobal => 0b0000_0000_0000_0110,
Instruction::GetLocal(value) => 0b0000_0010_0000_0000 | value.0,
Instruction::SetLocal(value) => 0b0000_0011_0000_0000 | value.0,
Instruction::Call(count) => 0b0000_0000_0100_0000 | count.0,
Instruction::PushInteger(value) => 0b0001_0000_0000_0000 | value.0,
Instruction::PushConstant(index) => 0b0010_0000_0000_0000 | index.0,
@@ -181,7 +185,8 @@ impl TryFrom<u32> for Instruction {
"0000_0000_11??_????" => todo!(),
"0000_0001_????_????" => todo!(),
"0000_001?_????_????" => todo!(),
"0000_0010_xxxx_xxxx" => Ok(Instruction::GetLocal(U(x))),
"0000_0011_xxxx_xxxx" => Ok(Instruction::SetLocal(U(x))),
"0000_01xx_xxyy_yyyy" => Ok(Instruction::Math(x.try_into()?, U(y))),
"0000_10xx_xxxx_xxxx" => Ok(Instruction::Branch(U(x))),
"0000_11xx_xxxx_xxxx" => Ok(Instruction::Jump(U(x))),
+52 -8
View File
@@ -54,6 +54,7 @@ pub struct CallFrame {
arguments: Vec<Value>,
return_address: InstructionPointer,
event: ExecutionEvent,
locals: HashMap<u32, Value>,
}
pub struct Machine {
@@ -61,6 +62,8 @@ pub struct Machine {
ip: Option<InstructionPointer>,
value_stack: Stack<Value>,
call_stack: Stack<CallFrame>,
// Top-level locals
locals: HashMap<u32, Value>,
}
#[derive(Debug, Clone, PartialEq)]
@@ -77,6 +80,7 @@ impl Default for Machine {
ip: None,
value_stack: Stack::new(1024),
call_stack: Stack::new(32),
locals: HashMap::new(),
}
}
}
@@ -90,6 +94,22 @@ impl Machine {
self.globals.insert(identifier.into(), value);
}
pub fn set_local(&mut self, index: u32, value: Value) {
let locals = match self.call_stack.current_mut() {
Some(frame) => &mut frame.locals,
None => &mut self.locals,
};
locals.insert(index, value);
}
fn get_local(&self, index: u32) -> Option<&Value> {
let locals = match self.call_stack.current() {
Some(frame) => &frame.locals,
None => &self.locals,
};
locals.get(&index)
}
pub fn defun_native<S, F>(&mut self, identifier: S, function: F)
where
S: Into<Rc<str>>,
@@ -136,6 +156,7 @@ impl Machine {
module: source_ip.module,
address: source_ip.address + 1,
},
locals: HashMap::new(),
};
if self.call_stack.push(frame).is_err() {
return Err(MachineError::CallStackOverflow);
@@ -322,6 +343,23 @@ impl Machine {
Ok(())
}
fn execute_get_local(&mut self, index: u32) -> Result<(), MachineError> {
let value = self.get_local(index).cloned();
if let Some(value) = value {
self.push(value.clone())?;
} else {
eprintln!(":: Warning: local #{index} referenced before assignment");
self.push(Value::Nil)?;
}
Ok(())
}
fn execute_set_local(&mut self, index: u32) -> Result<(), MachineError> {
let value = self.pop()?;
self.set_local(index, value);
Ok(())
}
pub fn execute_next(&mut self) -> Result<ExecutionEvent, MachineError> {
let ip = self.ip.clone().unwrap();
let instruction = ip
@@ -356,6 +394,12 @@ impl Machine {
Instruction::SetGlobal => {
self.execute_set_global()?;
}
Instruction::SetLocal(index) => {
self.execute_set_local(index.into())?;
}
Instruction::GetLocal(index) => {
self.execute_get_local(index.into())?;
}
Instruction::Return => {
advance = false;
event = self.execute_return()?;
@@ -418,6 +462,7 @@ impl Machine {
arguments: vec![],
return_address: ip,
event: ExecutionEvent::ModuleExit(module.clone()),
locals: HashMap::new(),
})
.is_err()
{
@@ -492,10 +537,9 @@ mod tests {
use std::sync::atomic::{AtomicI64, Ordering};
use crate::vm::{
instruction::{Instruction, U},
instruction::{Instruction, MathInstruction, U},
machine::{InstructionPointer, Machine},
module::{Module, ModuleBuilder, ModuleConstant, ModuleRef},
native::NativeFunction,
value::Value,
};
@@ -534,9 +578,9 @@ mod tests {
builder.add_all([
Instruction::PushInteger(U::truncate(1)),
Instruction::PushInteger(U::truncate(2)),
Instruction::Add(U::truncate(2)),
Instruction::Math(MathInstruction::Add, U::truncate(2)),
Instruction::PushConstant(c0),
Instruction::Add(U::truncate(2)),
Instruction::Math(MathInstruction::Add, U::truncate(2)),
]);
},
|_| {},
@@ -551,7 +595,7 @@ mod tests {
let (m, vs) = execute_all(
1,
|_, builder| {
let c0 = builder.constant(ModuleConstant::LocalFunction(4));
let c0 = builder.constant(ModuleConstant::LocalFunction(4, 1));
builder.add_all([
// main
Instruction::PushInteger(U::truncate(34)),
@@ -561,7 +605,7 @@ mod tests {
// c0
Instruction::PushArgument(U::truncate(0)),
Instruction::PushInteger(U::truncate(1200)),
Instruction::Add(U::truncate(2)),
Instruction::Math(MathInstruction::Add, U::truncate(2)),
Instruction::Return,
]);
},
@@ -579,7 +623,7 @@ mod tests {
2,
|id, builder| match id {
1 => {
let c0 = builder.constant(ModuleConstant::LocalFunction(4));
let c0 = builder.constant(ModuleConstant::LocalFunction(4, 1));
let c1 = builder.constant(ModuleConstant::Identifier("extern-function".into()));
builder.add_all([
// main: (local 1)
@@ -600,7 +644,7 @@ mod tests {
0 => {
let c0 = builder.constant(ModuleConstant::Integer(3));
let c1 = builder.constant(ModuleConstant::Identifier("native".into()));
let c2 = builder.constant(ModuleConstant::LocalFunction(4));
let c2 = builder.constant(ModuleConstant::LocalFunction(4, 2));
let c3 = builder.constant(ModuleConstant::Identifier("extern-function".into()));
builder.add_all([
// main
+2 -2
View File
@@ -198,7 +198,7 @@ impl fmt::Display for ModuleConstant {
#[cfg(test)]
mod tests {
use crate::vm::{
instruction::{Instruction, U},
instruction::{Instruction, MathInstruction, U},
module::Module,
value::Value,
};
@@ -215,7 +215,7 @@ mod tests {
let is = [
Instruction::PushInteger(U::truncate(2)),
Instruction::PushInteger(U::truncate(1)),
Instruction::Add(U::truncate(2)),
Instruction::Math(MathInstruction::Add, U::truncate(2)),
Instruction::Return,
]
.into_iter()
+5 -3
View File
@@ -216,7 +216,7 @@ pub fn load(vm: &mut Machine) {
return Err(MachineError::InvalidArgument);
};
let f = AnyFunction::try_from_value(f)?;
let xs = xs.proper_iter();
let xs = xs.proper_iter(MachineError::InvalidArgument);
let out = Value::try_list_or_nil(xs.map(|v| f.invoke(vm, slice::from_ref(v?))))?;
Ok(out)
});
@@ -225,7 +225,9 @@ pub fn load(vm: &mut Machine) {
return Err(MachineError::InvalidArgument);
};
let f = AnyFunction::try_from_value(f)?;
let xs = xs.proper_iter().map(|x| x.cloned());
let xs = xs
.proper_iter(MachineError::InvalidArgument)
.map(|x| x.cloned());
let out = Value::try_list_or_nil(xs.try_filter(|v| {
let result = f.invoke(vm, slice::from_ref(v))?;
bool::try_from_value(&result)
@@ -252,7 +254,7 @@ pub fn load(vm: &mut Machine) {
};
let f = AnyFunction::try_from_value(f)?;
let args = xs
.proper_iter()
.proper_iter(MachineError::InvalidArgument)
.map(|x| x.cloned())
.collect::<Result<Vec<_>, _>>()?;
f.invoke(vm, &args[..])
+8
View File
@@ -41,6 +41,14 @@ impl<T> Stack<T> {
}
}
pub fn current_mut(&mut self) -> Option<&mut T> {
if self.pointer >= self.data.len() {
None
} else {
Some(unsafe { self.data[self.pointer].assume_init_mut() })
}
}
pub fn push(&mut self, value: T) -> Result<(), T> {
if self.pointer > 0 {
self.pointer -= 1;
+29 -12
View File
@@ -1,6 +1,9 @@
use std::{any::Any, fmt, rc::Rc};
use crate::vm::{machine::MachineError, module::ModuleRef, native::NativeFunction};
use crate::{
compile::{ExpectedWhat, ExpectedWhere, ParseError, ParseErrorKind},
vm::{machine::MachineError, module::ModuleRef, native::NativeFunction},
};
#[derive(Debug, Clone, PartialEq)]
pub struct BytecodeFunction {
@@ -14,8 +17,9 @@ pub struct OpaqueValue {
inner: Rc<dyn Any>,
}
pub struct ProperListIter<'a> {
pub struct ProperListIter<'a, E> {
head: Option<&'a Value>,
error: Option<E>,
}
#[derive(Debug, Clone, PartialEq)]
@@ -33,8 +37,8 @@ pub enum Value {
OpaqueValue(OpaqueValue),
}
impl<'a> Iterator for ProperListIter<'a> {
type Item = Result<&'a Value, MachineError>;
impl<'a, E> Iterator for ProperListIter<'a, E> {
type Item = Result<&'a Value, E>;
fn next(&mut self) -> Option<Self::Item> {
let head = self.head.take()?;
@@ -45,7 +49,7 @@ impl<'a> Iterator for ProperListIter<'a> {
Some(Ok(car))
}
Value::Nil => None,
_ => Some(Err(MachineError::InvalidArgument)),
_ => self.error.take().map(Err),
}
}
}
@@ -127,7 +131,10 @@ impl_keyword! {
While => "while",
Otherwise => "&otherwise",
Optional => "&optional",
Rest => "&rest"
Rest => "&rest",
Setq => "setq",
Let => "let",
LetStar => "let*",
}
}
@@ -135,8 +142,18 @@ impl_keyword! {
pub struct ConsCell(pub Value, pub Value);
impl Value {
pub fn proper_iter(&self) -> ProperListIter<'_> {
ProperListIter { head: Some(self) }
pub fn proper_iter<E>(&self, error: E) -> ProperListIter<'_, E> {
ProperListIter {
head: Some(self),
error: Some(error),
}
}
pub fn syntax_iter(&self, location: ExpectedWhere) -> ProperListIter<'_, ParseError> {
self.proper_iter(ParseError {
input: self.clone(),
error: ParseErrorKind::Expected(ExpectedWhat::ProperList, location),
})
}
pub fn is_nil(&self) -> bool {
@@ -154,9 +171,9 @@ impl Value {
Self::Cons(Rc::new(ConsCell(self, cdr)))
}
pub fn try_list_or_nil<I: IntoIterator<Item = Result<Self, MachineError>>>(
pub fn try_list_or_nil<E, I: IntoIterator<Item = Result<Self, E>>>(
items: I,
) -> Result<Self, MachineError> {
) -> Result<Self, E> {
Self::try_list_or_nil_inner(&mut items.into_iter())
}
@@ -164,9 +181,9 @@ impl Value {
Self::list_or_nil_inner(&mut items.into_iter())
}
fn try_list_or_nil_inner<I: Iterator<Item = Result<Self, MachineError>>>(
fn try_list_or_nil_inner<E, I: Iterator<Item = Result<Self, E>>>(
items: &mut I,
) -> Result<Self, MachineError> {
) -> Result<Self, E> {
match items.next() {
Some(value) => Ok(value?.cons(Self::try_list_or_nil_inner(items)?)),
None => Ok(Self::Nil),