use std::mem;
use crate::common::{
number::split_number,
span::{Span, Spanned},
lambda::{Captured, Lambda},
opcode::Opcode,
data::Data,
};
use crate::compiler::{
cst::{CST, CSTPattern},
syntax::Syntax,
};
pub fn gen(cst: Spanned<CST>) -> Result<Lambda, Syntax> {
let mut compiler = Compiler::base();
compiler.walk(&cst)?;
return Ok(compiler.lambda);
}
#[derive(Debug, Clone, PartialEq)]
pub struct Local {
pub name: String,
pub depth: usize
}
impl Local {
pub fn new(name: String, depth: usize) -> Local {
Local { name, depth }
}
}
pub struct Compiler {
enclosing: Option<Box<Compiler>>,
lambda: Lambda,
locals: Vec<Local>,
captures: Vec<usize>,
depth: usize,
}
impl Compiler {
pub fn base() -> Compiler {
Compiler {
enclosing: None,
lambda: Lambda::empty(),
locals: vec![],
captures: vec![],
depth: 0,
}
}
pub fn declare(&mut self, name: String) {
self.locals.push(
Local { name, 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, cst: &Spanned<CST>) -> Result<(), Syntax> {
self.lambda.emit_span(&cst.span);
return match cst.item.clone() {
CST::Data(data) => Ok(self.data(data)),
CST::Symbol(name) => self.symbol(&name, cst.span.clone()),
CST::Block(block) => self.block(block),
CST::Print(expression) => self.print(*expression),
CST::Label(name, expression) => self.label(name, *expression),
CST::Assign { pattern, expression } => self.assign(*pattern, *expression),
CST::Lambda { pattern, expression } => self.lambda(*pattern, *expression),
CST::Call { fun, arg } => self.call(*fun, *arg),
};
}
pub fn data(&mut self, data: Data) {
self.lambda.emit(Opcode::Con);
let mut split = split_number(self.lambda.index_data(data));
self.lambda.emit_bytes(&mut split);
}
pub fn local(&self, name: &str) -> Option<usize> {
for (index, l) in self.locals.iter().enumerate() {
if name == l.name {
return Some(index)
}
}
return None;
}
pub fn captured(&mut self, name: &str) -> Option<Captured> {
if let Some(index) = self.local(name) {
let already = self.captures.contains(&index);
if !already {
self.captures.push(index);
self.lambda.emit(Opcode::Capture);
self.lambda.emit_bytes(&mut split_number(index));
}
return Some(Captured::Local(index));
}
if let Some(enclosing) = self.enclosing.as_mut() {
if let Some(captured) = enclosing.captured(name) {
let included = self.lambda.captures.contains(&captured);
let upvalue = if !included {
self.lambda.captures.push(captured);
self.lambda.captures.len() - 1
} else {
self.lambda.captures.iter().position(|c| c == &captured).unwrap()
};
return Some(Captured::Nonlocal(upvalue));
}
}
return None
}
pub fn captured_upvalue(&mut self, name: &str) -> Option<usize> {
match self.captured(name) {
Some(Captured::Nonlocal(upvalue)) => Some(upvalue),
_ => None,
}
}
pub fn symbol(&mut self, name: &str, span: Span) -> Result<(), Syntax> {
if let Some(index) = self.local(name) {
self.lambda.emit(Opcode::Load);
self.lambda.emit_bytes(&mut split_number(index))
} else if let Some(upvalue) = self.captured_upvalue(name) {
self.lambda.emit(Opcode::LoadCap);
self.lambda.emit_bytes(&mut split_number(upvalue))
} else {
return Err(Syntax::error(
"Variable referenced before assignment; it is undefined", &span
));
}
Ok(())
}
pub fn block(&mut self, children: Vec<Spanned<CST>>) -> Result<(), Syntax> {
if children.is_empty() {
self.data(Data::Unit);
return Ok(());
}
for child in children {
self.walk(&child)?;
self.lambda.emit(Opcode::Del);
}
self.lambda.demit();
Ok(())
}
pub fn print(&mut self, expression: Spanned<CST>) -> Result<(), Syntax> {
self.walk(&expression)?;
self.lambda.emit(Opcode::Print);
Ok(())
}
pub fn label(&mut self, name: String, expression: Spanned<CST>) -> Result<(), Syntax> {
self.walk(&expression)?;
self.data(Data::Kind(name));
self.lambda.emit(Opcode::Label);
Ok(())
}
pub fn resolve_assign(&mut self, name: &str) {
let index = if let Some(i) = self.local(name) {
self.lambda.emit(Opcode::Save); i
} else if let Some(i) = self.captured_upvalue(name) {
self.lambda.emit(Opcode::SaveCap); i
} else {
self.declare(name.to_string());
self.lambda.emit(Opcode::Save);
self.locals.len() - 1
};
self.lambda.emit_bytes(&mut split_number(index));
}
pub fn destructure(&mut self, pattern: Spanned<CSTPattern>, redeclare: bool) {
self.lambda.emit_span(&pattern.span);
match pattern.item {
CSTPattern::Symbol(name) => {
if redeclare { self.declare(name.to_string()) }
self.resolve_assign(&name);
}
CSTPattern::Data(expected) => {
self.data(expected);
self.lambda.emit(Opcode::UnData);
}
CSTPattern::Label(name, pattern) => {
self.data(Data::Kind(name));
self.lambda.emit(Opcode::UnLabel);
self.destructure(*pattern, redeclare);
}
}
}
pub fn assign(
&mut self,
pattern: Spanned<CSTPattern>,
expression: Spanned<CST>
) -> Result<(), Syntax> {
self.walk(&expression)?;
self.destructure(pattern, false);
self.data(Data::Unit);
Ok(())
}
pub fn lambda(
&mut self,
pattern: Spanned<CSTPattern>,
expression: Spanned<CST>
) -> Result<(), Syntax> {
self.enter_scope();
{
self.destructure(pattern, true);
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(Box::new(lambda)));
self.lambda.emit(Opcode::Closure);
self.lambda.emit_bytes(&mut split_number(lambda_index));
Ok(())
}
pub fn call(&mut self, fun: Spanned<CST>, arg: Spanned<CST>) -> 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,
parse::parse,
desugar::desugar,
};
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(desugar(parse(lex(source).unwrap()).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(desugar(parse(lex(source).unwrap()).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);
}
}