958 lines
34 KiB
Rust
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))
|
|
]
|
|
);
|
|
}
|
|
}
|