use std::mem;
use crate::common::{
number::split_number,
span::{Span, Spanned},
lambda::Lambda,
opcode::Opcode,
data::Data,
};
use crate::compiler::{
ast::AST,
syntax::Syntax,
};
pub fn gen(ast: Spanned<AST>) -> Result<Lambda, Syntax> {
let mut compiler = Compiler::base();
compiler.walk(&ast)?;
return Ok(compiler.lambda);
}
#[derive(Debug, Clone, PartialEq)]
pub struct Local {
pub span: Span,
pub depth: usize
}
impl Local {
pub fn new(span: Span, depth: usize) -> Local {
Local { span, depth }
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Captured {
Local(usize),
Nonlocal(usize),
}
pub struct Compiler {
enclosing: Option<Box<Compiler>>,
lambda: Lambda,
locals: Vec<Local>,
captureds: Vec<Captured>,
depth: usize,
}
impl Compiler {
pub fn base() -> Compiler {
Compiler {
enclosing: None,
lambda: Lambda::empty(),
locals: vec![],
captureds: vec![],
depth: 0,
}
}
pub fn declare(&mut self, span: Span) {
self.locals.push(
Local { span, depth: self.depth }
)
}
pub fn enter_scope(&mut self) {
let depth = self.depth + 1;
let mut nested = Compiler::base();
nested.depth = depth;
let enclosing = mem::replace(self, nested);
self.enclosing = Some(Box::new(enclosing));
}
pub fn exit_scope(&mut self) -> Compiler {
let enclosing = mem::replace(&mut self.enclosing, None);
let nested = match enclosing {
Some(compiler) => mem::replace(self, *compiler),
None => panic!("Can not go back past base compiler"),
};
return nested;
}
pub fn walk(&mut self, ast: &Spanned<AST>) -> Result<(), Syntax> {
self.lambda.emit_span(&ast.span);
let result = match ast.item.clone() {
AST::Data(data) => self.data(data),
AST::Symbol => self.symbol(ast.span.clone()),
AST::Block(block) => self.block(block),
AST::Print(expression) => self.print(*expression),
AST::Assign { pattern, expression } => self.assign(*pattern, *expression),
AST::Lambda { pattern, expression } => self.lambda(*pattern, *expression),
AST::Call { fun, arg } => self.call(*fun, *arg),
};
return result;
}
pub fn data(&mut self, data: Data) -> Result<(), Syntax> {
self.lambda.emit(Opcode::Con);
let mut split = split_number(self.lambda.index_data(data));
self.lambda.emit_bytes(&mut split);
Ok(())
}
pub fn local(&self, span: Span) -> Option<usize> {
for (index, l) in self.locals.iter().enumerate() {
if span.contents() == l.span.contents() {
return Some(index)
}
}
return None;
}
pub fn capture(&mut self, captured: Captured) -> usize {
for (i, c) in self.captureds.iter().enumerate() {
if &captured == c {
return i;
}
}
match captured {
Captured::Local(index) => {
self.lambda.emit(Opcode::Capture);
self.lambda.emit_bytes(&mut split_number(index));
},
Captured::Nonlocal(upvalue) => {
self.lambda.captureds.push(upvalue);
},
}
self.captureds.push(captured);
return self.captureds.len() - 1;
}
pub fn captured(&mut self, span: Span) -> Option<usize> {
if let Some(index) = self.local(span.clone()) {
return Some(self.capture(Captured::Local(index)));
}
if let Some(enclosing) = self.enclosing.as_mut() {
if let Some(upvalue) = enclosing.captured(span) {
return Some(self.capture(Captured::Nonlocal(upvalue)))
}
}
return None;
}
pub fn symbol(&mut self, span: Span) -> Result<(), Syntax> {
if let Some(index) = self.local(span.clone()) {
self.lambda.emit(Opcode::Load);
self.lambda.emit_bytes(&mut split_number(index))
} else if let Some(index) = self.captured(span.clone()) {
self.lambda.emit(Opcode::LoadCap);
self.lambda.emit_bytes(&mut split_number(index))
} else {
return Err(Syntax::error(
"Variable referenced before assignment; it is undefined", span
));
}
Ok(())
}
pub fn block(&mut self, children: Vec<Spanned<AST>>) -> Result<(), Syntax> {
for child in children {
self.walk(&child)?;
self.lambda.emit(Opcode::Del);
}
self.lambda.demit();
Ok(())
}
pub fn print(&mut self, expression: Spanned<AST>) -> Result<(), Syntax> {
self.walk(&expression)?;
self.lambda.emit(Opcode::Print);
self.data(Data::Unit)?;
Ok(())
}
pub fn assign(&mut self, symbol: Spanned<AST>, expression: Spanned<AST>) -> Result<(), Syntax> {
self.walk(&expression)?;
let span = match symbol.item {
AST::Symbol => symbol.span,
_ => unreachable!(),
};
self.lambda.emit_span(&span);
let index = if let Some(i) = self.local(span.clone()) {
self.lambda.emit(Opcode::Save); i
} else if let Some(i) = self.captured(span.clone()) {
self.lambda.emit(Opcode::SaveCap); i
} else {
self.lambda.emit(Opcode::Save);
self.locals.push(Local::new(span, self.depth));
self.locals.len() - 1
};
self.lambda.emit_bytes(&mut split_number(index));
self.data(Data::Unit)?;
Ok(())
}
pub fn lambda(&mut self, symbol: Spanned<AST>, expression: Spanned<AST>) -> Result<(), Syntax> {
self.enter_scope();
{
if let AST::Symbol = symbol.item {} else { unreachable!() }
self.lambda.emit(Opcode::Save);
self.locals.push(Local::new(symbol.span, self.depth));
self.lambda.emit_bytes(&mut split_number(0));
self.walk(&expression)?;
self.lambda.emit(Opcode::Return);
self.lambda.emit_bytes(&mut split_number(self.locals.len()));
}
let lambda = self.exit_scope().lambda;
let lambda_index = self.lambda.index_data(Data::Lambda(lambda));
self.lambda.emit(Opcode::Closure);
self.lambda.emit_bytes(&mut split_number(lambda_index));
Ok(())
}
pub fn call(&mut self, fun: Spanned<AST>, arg: Spanned<AST>) -> Result<(), Syntax> {
self.walk(&arg)?;
self.walk(&fun)?;
self.lambda.emit_span(&Span::combine(&fun.span, &arg.span));
self.lambda.emit(Opcode::Call);
Ok(())
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::compiler::lex::lex;
use crate::compiler::parse::parse;
use crate::common::source::Source;
#[test]
fn constants() {
let source = Source::source("heck = true; lol = 0.0; lmao = false; eyy = \"GOod MoRNiNg, SiR\"");
let lambda = gen(parse(lex(source).unwrap()).unwrap()).unwrap();
let result = vec![
Data::Boolean(true),
Data::Unit,
Data::Real(0.0),
Data::Boolean(false),
Data::String("GOod MoRNiNg, SiR".to_string()),
];
assert_eq!(lambda.constants, result);
}
#[test]
fn bytecode() {
let source = Source::source("heck = true; lol = heck; lmao = false");
let lambda = gen(parse(lex(source).unwrap()).unwrap()).unwrap();
let result = vec![
(Opcode::Con as u8), 128, (Opcode::Save as u8), 128,
(Opcode::Con as u8), 129, (Opcode::Del as u8),
(Opcode::Load as u8), 128, (Opcode::Save as u8), 129,
(Opcode::Con as u8), 129, (Opcode::Del as u8),
(Opcode::Con as u8), 130, (Opcode::Save as u8), 130,
(Opcode::Con as u8), 129,
];
assert_eq!(result, lambda.code);
}
}