Files
lysp/src/compile/block.rs
T
2026-05-09 00:30:35 +03:00

958 lines
34 KiB
Rust

use std::{collections::HashMap, rc::Rc};
use crate::{
compile::{
error::CompileError,
function::FunctionSignature,
instruction::Emitted,
module::CompilationModule,
syntax::{
CallExpression, CondExpression, DefmacroExpression, DefunExpression, Expression,
FunctionBody, IfExpression, LambdaExpression, LetExpression, LoopExpression,
PrognExpression, SetqExpression, VectorExpression, WhileExpression,
},
value::{BuiltinFunction, CompileConstant, CompileValue},
},
vm::{
instruction::{Instruction, LocalId, MathInstruction, U},
value::Value,
},
};
pub struct CompiledFunction {
pub(crate) instructions: Vec<Instruction>,
}
struct LocalScope {
start_index: u32,
locals: Vec<(Rc<str>, bool)>,
}
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,
}
impl LocalScope {
pub fn get_or_insert(
&mut self,
value: Rc<str>,
visible: bool,
) -> Result<LocalId, CompileError> {
let index = if let Some(index) = self.locals.iter().position(|v| v.0 == value) {
self.locals[index].1 = visible;
index
} else {
let index = self.locals.len();
self.locals.push((value, visible));
index
} + self.start_index as usize;
Ok(U::new(index as u32).unwrap())
}
pub fn make_visible(&mut self, index: LocalId) {
self.locals[usize::from(index) - self.start_index as usize].1 = true;
}
pub fn get(&self, value: &str) -> Option<LocalId> {
self.locals
.iter()
.position(|v| v.0.as_ref() == value && v.1)
.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 {
pub fn new(signature: FunctionSignature) -> Self {
Self {
instructions: vec![],
labels: HashMap::new(),
last_label: 0,
signature,
locals_stack: Vec::new(),
max_local_index: 0,
}
}
pub fn new_label(&mut self) -> u32 {
let id = self.last_label;
self.last_label += 1;
self.labels.insert(id, usize::MAX);
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<LocalId> {
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());
}
pub fn resolve_labels(self, trace: bool) -> CompiledFunction {
let mut instructions = vec![];
if trace {
eprintln!("Instruction resolution:");
}
for (function_offset, emitted) in self.instructions.into_iter().enumerate() {
match emitted {
Emitted::Instruction(instruction) => {
if trace {
eprintln!("{function_offset}: {instruction:?}");
}
instructions.push(instruction);
}
Emitted::Branch(label) => {
let address = self.labels.get(&label).copied().unwrap_or(usize::MAX);
if address == usize::MAX {
todo!()
}
let diff = address.checked_signed_diff(function_offset).expect("TODO");
if trace {
eprintln!(
"{function_offset}: Branch: label {label} (@ {address}) -> {diff:+}"
);
}
let offset = U::from_signed(diff as i64).expect("TODO");
instructions.push(Instruction::Branch(offset));
}
Emitted::Jump(label) => {
let address = self.labels.get(&label).copied().unwrap_or(usize::MAX);
if address == usize::MAX {
todo!()
}
let diff = address.checked_signed_diff(function_offset).expect("TODO");
if trace {
eprintln!(
"{function_offset}: jump to label {label} (@ {address}) -> {diff:+}"
);
}
let offset = U::from_signed(diff as i64).expect("TODO");
instructions.push(Instruction::Jump(offset));
}
}
}
CompiledFunction { instructions }
}
pub fn emit<E: Into<Emitted>>(&mut self, instruction: E) {
let emitted = instruction.into();
// eprintln!("emit {emitted:?}");
self.instructions.push(emitted);
}
pub fn compile_body(
&mut self,
module: &mut CompilationModule,
body: &FunctionBody,
) -> Result<(), CompileError> {
let mut local = LocalBlock::root(self, module);
for statement in &body.head {
local.compile_statement(statement, None)?;
}
let value = local.compile_expression(&body.tail, None)?;
local.compile_return(value)?;
Ok(())
}
}
impl<'a> LocalBlock<'a> {
fn root(function: &'a mut FunctionBlock, module: &'a mut CompilationModule) -> Self {
Self { function, module }
}
fn compile_set_global(
&mut self,
identifier: &Rc<str>,
value: CompileValue,
) -> Result<(), CompileError> {
self.compile_push(value)?;
let index = self
.module
.constant(CompileConstant::Identifier(identifier.clone()))?;
self.function.emit(Instruction::PushConstant(index));
self.function.emit(Instruction::SetGlobal);
Ok(())
}
fn compile_export_macro(
&mut self,
identifier: &Rc<str>,
index: u32,
argc: usize,
) -> Result<(), CompileError> {
let macro_index = self
.module
.constant(CompileConstant::LocalFunction(index, argc))?;
let ident_index = self
.module
.constant(CompileConstant::Identifier(identifier.clone()))?;
self.function.emit(Instruction::PushConstant(ident_index));
self.function.emit(Instruction::ExportMacro(macro_index));
Ok(())
}
fn compile_set_local(
&mut self,
identifier: &Rc<str>,
value: CompileValue,
visible: bool,
) -> Result<LocalId, CompileError> {
let index = self
.function
.local_scope_mut()
.unwrap()
.get_or_insert(identifier.clone(), visible)?;
self.compile_push(value)?;
self.function.emit(Instruction::SetLocal(index));
Ok(index)
}
fn compile_push(&mut self, value: CompileValue) -> Result<(), CompileError> {
match value {
CompileValue::Nil => {
self.function.emit(Instruction::PushNil);
}
CompileValue::Global(identifier) => {
let value = self
.module
.constant(CompileConstant::Identifier(identifier))?;
self.function.emit(Instruction::PushConstant(value));
self.function.emit(Instruction::GetGlobal);
}
CompileValue::String(value) => {
let index = self.module.constant(CompileConstant::String(value))?;
self.function.emit(Instruction::PushConstant(index));
}
CompileValue::Quote(value) => {
let index = self.module.constant(CompileConstant::Value(value))?;
self.function.emit(Instruction::PushConstant(index));
}
CompileValue::Local(index) => {
self.function.emit(Instruction::GetLocal(index));
}
CompileValue::Boolean(value) => {
self.function.emit(Instruction::PushBool(value));
}
CompileValue::Integer(value) => {
if let Some(value) = U::from_signed(value) {
self.function.emit(Instruction::PushInteger(value));
} else {
let index = self.module.constant(CompileConstant::Integer(value))?;
self.function.emit(Instruction::PushConstant(index));
}
}
CompileValue::Argument(index) => {
let Some(index) = U::new(index as u32) else {
todo!();
};
self.function.emit(Instruction::PushArgument(index));
}
CompileValue::LocalFunction(index, required_count) => {
let value = self
.module
.constant(CompileConstant::LocalFunction(index, required_count))?;
self.function.emit(Instruction::PushConstant(value));
}
// Already on stack
CompileValue::Stack => (),
}
Ok(())
}
fn compile_drop(&mut self, value: CompileValue) -> Result<(), CompileError> {
if matches!(value, CompileValue::Stack) {
self.function.emit(Instruction::Drop);
}
Ok(())
}
pub fn compile_return(&mut self, value: CompileValue) -> Result<(), CompileError> {
self.compile_push(value)?;
self.function.emit(Instruction::Return);
Ok(())
}
pub fn compile_statement(
&mut self,
expression: &Rc<Expression>,
break_label: Option<u32>,
) -> Result<(), CompileError> {
let value = self.compile_expression(expression, break_label)?;
self.compile_drop(value)?;
Ok(())
}
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));
}
// TODO local bindings
Ok(CompileValue::Global(identifier.clone()))
}
fn compile_lambda(&mut self, lambda: &LambdaExpression) -> Result<CompileValue, CompileError> {
let index = self
.module
.compile_function(lambda.signature.clone(), &lambda.body, false)?;
Ok(CompileValue::LocalFunction(
index,
lambda.signature.required_arguments.len(),
))
}
fn compile_defun(&mut self, defun: &DefunExpression) -> Result<CompileValue, CompileError> {
let index = self
.module
.compile_function(defun.signature.clone(), &defun.body, false)?;
let value = CompileValue::LocalFunction(index, defun.signature.required_arguments.len());
self.compile_set_global(&defun.name, value)?;
Ok(CompileValue::Nil)
}
fn compile_defmacro(
&mut self,
defmacro: &DefmacroExpression,
) -> Result<CompileValue, CompileError> {
let index =
self.module
.compile_function(defmacro.signature.clone(), &defmacro.body, false)?;
self.compile_export_macro(
&defmacro.name,
index,
defmacro.signature.required_arguments.len(),
)?;
Ok(CompileValue::Nil)
}
fn compile_builtin_math_generic(
&mut self,
math: MathInstruction,
args: &[Rc<Expression>],
break_label: Option<u32>,
) -> Result<CompileValue, CompileError> {
// TODO optimize
for arg in args.iter().rev() {
let arg = self.compile_expression(arg, break_label)?;
self.compile_push(arg)?;
}
let Some(count) = U::new(args.len() as u32) else {
todo!()
};
self.function.emit(Instruction::Math(math, count));
Ok(CompileValue::Stack)
}
fn compile_call_builtin(
&mut self,
builtin: BuiltinFunction,
args: &[Rc<Expression>],
break_label: Option<u32>,
) -> Result<CompileValue, CompileError> {
match builtin {
BuiltinFunction::Math(math) => {
self.compile_builtin_math_generic(math, args, break_label)
}
}
}
fn compile_call(
&mut self,
call: &CallExpression,
break_label: Option<u32>,
) -> Result<CompileValue, CompileError> {
match call.callee.as_ref() {
Expression::Identifier(identifier)
if let Some(builtin) = BuiltinFunction::from_identifier(identifier.as_ref()) =>
{
self.compile_call_builtin(builtin, &call.arguments, break_label)
}
_ => {
// Push arguments in reverse order
let Some(count) = U::new(call.arguments.len() as u32) else {
todo!();
};
for arg in call.arguments.iter().rev() {
let arg = self.compile_expression(arg, break_label)?;
self.compile_push(arg)?;
}
let callee = self.compile_expression(&call.callee, break_label)?;
self.compile_push(callee)?;
self.function.emit(Instruction::Call(count));
Ok(CompileValue::Stack)
}
}
}
fn compile_if(
&mut self,
condition: &IfExpression,
break_label: Option<u32>,
) -> Result<CompileValue, CompileError> {
let label_false = self.function.new_label();
let label_end = self.function.new_label();
// Emit condition
let condition_value = self.compile_expression(&condition.condition, break_label)?;
self.compile_push(condition_value)?;
self.function.emit(Emitted::Branch(label_false));
// True branch
let true_value = self.compile_expression(&condition.if_true, break_label)?;
self.compile_push(true_value)?;
self.function.emit(Emitted::Jump(label_end));
// False branch
self.function.adjust_label(label_false);
let false_value = if let Some(if_false) = condition.if_false.as_ref() {
self.compile_expression(if_false, break_label)?
} else {
CompileValue::Nil
};
self.compile_push(false_value)?;
self.function.adjust_label(label_end);
Ok(CompileValue::Stack)
}
fn compile_cond(
&mut self,
condition: &CondExpression,
break_label: Option<u32>,
) -> Result<CompileValue, CompileError> {
let label_end = self.function.new_label();
for arm in condition.arms.iter() {
let label_condition_end = self.function.new_label();
let condition_value = self.compile_expression(&arm.condition, break_label)?;
self.compile_push(condition_value)?;
self.function.emit(Emitted::Branch(label_condition_end));
// Condition true
let then_value = self.compile_expression(&arm.then, break_label)?;
self.compile_push(then_value)?;
self.function.emit(Emitted::Jump(label_end));
// Condition false
self.function.adjust_label(label_condition_end);
}
// Otherwise branch
let otherwise_value = if let Some(othwerise_arm) = condition.otherwise_arm.as_ref() {
self.compile_expression(othwerise_arm, break_label)?
} else {
CompileValue::Nil
};
self.compile_push(otherwise_value)?;
self.function.adjust_label(label_end);
Ok(CompileValue::Stack)
}
fn compile_while(
&mut self,
cloop: &WhileExpression,
break_label: Option<u32>,
) -> Result<CompileValue, CompileError> {
let label_start = self.function.new_label();
let label_end = self.function.new_label();
self.function.adjust_label(label_start);
// Condition
let condition_value = self.compile_expression(&cloop.condition, break_label)?;
self.compile_push(condition_value)?;
self.function.emit(Emitted::Branch(label_end));
// Body
for expression in &cloop.body.head {
self.compile_statement(expression, Some(label_end))?;
}
self.compile_statement(&cloop.body.tail, Some(label_end))?;
// Jump back
self.function.emit(Emitted::Jump(label_start));
self.function.adjust_label(label_end);
Ok(CompileValue::Nil)
}
fn compile_loop(&mut self, cloop: &LoopExpression) -> Result<CompileValue, CompileError> {
let label_start = self.function.new_label();
let label_end = self.function.new_label();
self.function.adjust_label(label_start);
for expression in &cloop.body.head {
self.compile_statement(expression, Some(label_end))?;
}
self.compile_statement(&cloop.body.tail, Some(label_end))?;
self.function.emit(Emitted::Jump(label_start));
self.function.adjust_label(label_end);
Ok(CompileValue::Nil)
}
fn compile_let(
&mut self,
binding: &LetExpression,
break_label: Option<u32>,
) -> Result<CompileValue, CompileError> {
self.function.push_local_scope();
let mut indices = vec![];
for pair in &binding.bindings {
let value = self.compile_expression(&pair.value, break_label)?;
let index = self.compile_set_local(&pair.identifier, value, binding.sequential)?;
indices.push(index);
}
if !binding.sequential {
// Make let bindings visible
let scope = self.function.local_scope_mut().unwrap();
for index in indices {
scope.make_visible(index);
}
}
for expr in &binding.body.head {
self.compile_statement(expr, break_label)?;
}
let value = self.compile_expression(&binding.body.tail, break_label)?;
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,
break_label: Option<u32>,
) -> Result<CompileValue, CompileError> {
for pair in setq.pairs.iter() {
let value = self.compile_expression(&pair.value, break_label)?;
if let Some(index) = self
.function
.local_scope_ref()
.and_then(|scope| scope.get(&pair.identifier))
{
self.compile_push(value)?;
self.function.emit(Instruction::SetLocal(index));
} else {
self.compile_set_global(&pair.identifier, value)?;
}
}
Ok(CompileValue::Nil)
}
fn compile_quote(&mut self, value: &Rc<Value>) -> Result<CompileValue, CompileError> {
Ok(CompileValue::Quote(value.clone()))
}
fn compile_progn(
&mut self,
value: &PrognExpression,
break_label: Option<u32>,
) -> Result<CompileValue, CompileError> {
for expression in &value.body.head {
self.compile_statement(expression, break_label)?;
}
self.compile_statement(&value.body.tail, break_label)?;
Ok(CompileValue::Nil)
}
fn compile_call_return(
&mut self,
break_label: Option<u32>,
) -> Result<CompileValue, CompileError> {
if let Some(break_label) = break_label {
self.function.emit(Emitted::Jump(break_label));
} else {
todo!("return outside a loop")
}
Ok(CompileValue::Nil)
}
fn compile_vector(
&mut self,
vector: &VectorExpression,
break_label: Option<u32>,
) -> Result<CompileValue, CompileError> {
for expression in vector.elements.iter().rev() {
let value = self.compile_expression(expression, break_label)?;
self.compile_push(value)?;
}
self.compile_push(CompileValue::Global("vector".into()))?;
let Some(count) = U::new(vector.elements.len() as u32) else {
todo!()
};
self.function.emit(Instruction::Call(count));
Ok(CompileValue::Stack)
}
pub fn compile_expression(
&mut self,
expression: &Rc<Expression>,
break_label: Option<u32>,
) -> Result<CompileValue, CompileError> {
match expression.as_ref() {
Expression::Nil => Ok(CompileValue::Nil),
Expression::StringLiteral(value) => Ok(CompileValue::String(value.clone())),
Expression::BooleanLiteral(value) => Ok(CompileValue::Boolean(*value)),
Expression::IntegerLiteral(value) => Ok(CompileValue::Integer(*value)),
Expression::Identifier(identifier) => self.compile_identifier(identifier),
Expression::Lambda(lambda) => self.compile_lambda(lambda),
Expression::Defun(defun) => self.compile_defun(defun),
Expression::Vector(vector) => self.compile_vector(vector, break_label),
Expression::Call(call) => self.compile_call(call, break_label),
Expression::If(condition) => self.compile_if(condition, break_label),
Expression::Cond(condition) => self.compile_cond(condition, break_label),
Expression::Let(binding) => self.compile_let(binding, break_label),
Expression::Setq(setq) => self.compile_setq(setq, break_label),
Expression::Defmacro(defmacro) => self.compile_defmacro(defmacro),
Expression::Quote(quote) => self.compile_quote(quote),
Expression::While(cloop) => self.compile_while(cloop, break_label),
Expression::Loop(cloop) => self.compile_loop(cloop),
Expression::Progn(progn) => self.compile_progn(progn, break_label),
Expression::Return => self.compile_call_return(break_label),
Expression::SyntaxError(_) => unreachable!(),
}
}
}
#[cfg(test)]
mod tests {
use std::{collections::HashMap, rc::Rc};
use crate::{
compile::{
block::{CompiledFunction, FunctionBlock, LocalBlock},
function::FunctionSignature,
module::CompilationModule,
syntax::{
CallExpression, CondExpression, CondExpressionArm, DefmacroExpression, Expression,
FunctionBody, IfExpression, LambdaExpression,
},
value::{CompileConstant, CompileValue},
},
vm::instruction::{Instruction, MathInstruction, U},
};
fn test_compile(expression: Expression) -> (CompilationModule, CompiledFunction, CompileValue) {
let mut module = CompilationModule::default();
module.define_macro(DefmacroExpression {
name: "test-macro-1".into(),
signature: FunctionSignature {
required_arguments: vec!["x".into()],
optional_arguments: vec![],
rest_argument: None,
},
body: FunctionBody {
head: vec![],
tail: Rc::new(Expression::Call(CallExpression {
callee: Rc::new(Expression::Identifier("test-macro-2".into())),
arguments: vec![
Rc::new(Expression::Identifier("x".into())),
Rc::new(Expression::IntegerLiteral(1)),
],
})),
},
});
module.define_macro(DefmacroExpression {
name: "test-macro-2".into(),
signature: FunctionSignature {
required_arguments: vec!["x".into(), "y".into()],
optional_arguments: vec![],
rest_argument: None,
},
body: FunctionBody {
head: vec![],
tail: Rc::new(Expression::Call(CallExpression {
callee: Rc::new(Expression::Identifier("+".into())),
arguments: vec![
Rc::new(Expression::Identifier("x".into())),
Rc::new(Expression::Identifier("y".into())),
],
})),
},
});
let mut function = FunctionBlock {
signature: FunctionSignature {
required_arguments: vec!["arg0".into()],
optional_arguments: vec!["arg1".into()],
rest_argument: Some("arg2".into()),
},
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(&Rc::new(expression), None)
.unwrap();
let trace = module.options.trace_compile;
(module, function.resolve_labels(trace), value)
}
#[test]
fn test_identity_compile() {
let (_, f, v) = test_compile(Expression::IntegerLiteral(1));
assert!(f.instructions.is_empty());
assert_eq!(v, CompileValue::Integer(1));
}
#[test]
fn test_comparison_compile() {
let (m, f, v) = test_compile(Expression::Call(CallExpression {
callee: Rc::new(Expression::Identifier(">".into())),
arguments: vec![
Rc::new(Expression::Identifier("a".into())),
Rc::new(Expression::IntegerLiteral(1)),
],
}));
let cs = m.constant_pool.into_map();
assert_eq!(
cs,
[(U::truncate(1), CompileConstant::Identifier("a".into()))].into()
);
assert_eq!(
&f.instructions,
&[
Instruction::PushInteger(U::truncate(1)),
Instruction::PushConstant(U::truncate(1)),
Instruction::GetGlobal,
Instruction::Math(MathInstruction::Gt, U::truncate(2)),
]
);
assert_eq!(v, CompileValue::Stack);
}
#[test]
fn test_if_compile() {
let (_, f, v) = test_compile(Expression::If(IfExpression {
condition: Rc::new(Expression::BooleanLiteral(false)),
if_true: Rc::new(Expression::IntegerLiteral(1)),
if_false: Some(Rc::new(Expression::IntegerLiteral(2))),
}));
assert_eq!(
&f.instructions,
&[
Instruction::PushBool(false), // 0
Instruction::Branch(U::truncate(3)), // 1
Instruction::PushInteger(U::truncate(1)), // 2
Instruction::Jump(U::truncate(2)), // 3
Instruction::PushInteger(U::truncate(2)) // 4
// 5
]
);
assert_eq!(v, CompileValue::Stack);
}
#[test]
fn test_cond_compile() {
let (_, f, v) = test_compile(Expression::Cond(CondExpression {
arms: vec![
CondExpressionArm {
condition: Rc::new(Expression::BooleanLiteral(false)),
then: Rc::new(Expression::IntegerLiteral(1)),
},
CondExpressionArm {
condition: Rc::new(Expression::BooleanLiteral(true)),
then: Rc::new(Expression::IntegerLiteral(2)),
},
],
otherwise_arm: Some(Rc::new(Expression::IntegerLiteral(3))),
}));
assert_eq!(
&f.instructions,
&[
Instruction::PushBool(false), // 0
Instruction::Branch(U::truncate(3)), // 1
Instruction::PushInteger(U::truncate(1)), // 2
Instruction::Jump(U::truncate(6)), // 3
Instruction::PushBool(true), // 4
Instruction::Branch(U::truncate(3)), // 5
Instruction::PushInteger(U::truncate(2)), // 6
Instruction::Jump(U::truncate(2)), // 7
Instruction::PushInteger(U::truncate(3)), // 8
// 9
]
);
assert_eq!(v, CompileValue::Stack);
}
#[test]
fn test_compile_lambda_returning_lambda() {
// ( ((lambda () (lambda (a b) (+ a b)))) 1 2 )
let (m, f, v) = test_compile(Expression::Call(CallExpression {
callee: Rc::new(Expression::Call(CallExpression {
callee: Rc::new(Expression::Lambda(LambdaExpression {
signature: FunctionSignature {
required_arguments: vec![],
optional_arguments: vec![],
rest_argument: None,
},
body: FunctionBody {
head: vec![],
tail: Rc::new(Expression::Lambda(LambdaExpression {
signature: FunctionSignature {
required_arguments: vec!["a".into(), "b".into()],
optional_arguments: vec![],
rest_argument: None,
},
body: FunctionBody {
head: vec![],
tail: Rc::new(Expression::Call(CallExpression {
callee: Rc::new(Expression::Identifier("+".into())),
arguments: vec![
Rc::new(Expression::Identifier("a".into())),
Rc::new(Expression::Identifier("b".into())),
],
})),
},
})),
},
})),
arguments: vec![],
})),
arguments: vec![
Rc::new(Expression::IntegerLiteral(1)),
Rc::new(Expression::IntegerLiteral(2)),
],
}));
assert_eq!(
&f.instructions[..],
&[
Instruction::PushInteger(U::truncate(2)),
Instruction::PushInteger(U::truncate(1)),
Instruction::PushConstant(U::truncate(2)),
Instruction::Call(U::truncate(0)),
Instruction::Call(U::truncate(2)),
]
);
assert_eq!(
&m.local_functions.get(&0).unwrap().instructions[..],
&[
Instruction::PushConstant(U::truncate(1)),
Instruction::Return
]
);
assert_eq!(
&m.local_functions.get(&1).unwrap().instructions[..],
&[
Instruction::PushArgument(U::truncate(1)),
Instruction::PushArgument(U::truncate(0)),
Instruction::Math(MathInstruction::Add, U::truncate(2)),
Instruction::Return
]
);
assert_eq!(v, CompileValue::Stack);
}
#[test]
fn test_compile_lambda() {
// (+ ((lambda (a) (+ a 1))) 2)
let (m, f, v) = test_compile(Expression::Call(CallExpression {
callee: Rc::new(Expression::Identifier("+".into())),
arguments: vec![
Rc::new(Expression::Call(CallExpression {
callee: Expression::Lambda(LambdaExpression {
signature: FunctionSignature {
required_arguments: vec!["a".into()],
optional_arguments: vec![],
rest_argument: None,
},
body: FunctionBody {
head: vec![],
tail: Rc::new(Expression::Call(CallExpression {
callee: Rc::new(Expression::Identifier("+".into())),
arguments: vec![
Rc::new(Expression::Identifier("a".into())),
Rc::new(Expression::IntegerLiteral(1)),
],
})),
},
})
.into(),
arguments: vec![],
})),
Rc::new(Expression::IntegerLiteral(2)),
],
}));
assert_eq!(v, CompileValue::Stack);
assert_eq!(
&m.local_functions.get(&0).unwrap().instructions[..],
&[
Instruction::PushInteger(U::truncate(1)),
Instruction::PushArgument(U::truncate(0)),
Instruction::Math(MathInstruction::Add, U::truncate(2)),
Instruction::Return
]
);
assert_eq!(
&f.instructions[..],
&[
Instruction::PushInteger(U::truncate(2)),
Instruction::PushConstant(U::truncate(1)),
Instruction::Call(U::truncate(0)),
Instruction::Math(MathInstruction::Add, U::truncate(2))
]
);
}
}