Implement setq/let/let* bindings
This commit is contained in:
@@ -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
@@ -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))
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#![coverage(off)]
|
||||
|
||||
use crate::compile::syntax::ParseError;
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
|
||||
+4
-1
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}]
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
)))
|
||||
}]
|
||||
|
||||
@@ -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)?))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#![coverage(off)]
|
||||
|
||||
use crate::{compile::CompileError, vm::machine::MachineError};
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
|
||||
+7
-1
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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[..])
|
||||
|
||||
@@ -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
@@ -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),
|
||||
|
||||
Reference in New Issue
Block a user