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,
};
use crate::core::{
ffi_core,
ffi::FFI,
};
pub fn gen(cst: Spanned<CST>) -> Result<Lambda, Syntax> {
return gen_with_ffi(cst, ffi_core());
}
pub fn gen_with_ffi(cst: Spanned<CST>, ffi: FFI) -> Result<Lambda, Syntax> {
let mut compiler = Compiler::base(ffi);
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,
ffi: FFI,
ffi_names: Vec<String>,
}
impl Compiler {
pub fn base(ffi: FFI) -> Compiler {
Compiler {
enclosing: None,
lambda: Lambda::empty(),
locals: vec![],
captures: vec![],
depth: 0,
ffi,
ffi_names: vec![]
}
}
pub fn declare(&mut self, name: String) {
self.locals.push(Local { name, depth: self.depth });
self.lambda.decls = self.locals.len();
}
pub fn enter_scope(&mut self) {
let ffi = mem::replace(&mut self.ffi, FFI::new());
let mut nested = Compiler::base(ffi);
nested.depth = self.depth + 1;
let enclosing = mem::replace(self, nested);
self.enclosing = Some(Box::new(enclosing));
}
pub fn exit_scope(&mut self) -> Compiler {
let ffi = mem::replace(&mut self.ffi, FFI::new());
let enclosing = mem::replace(&mut self.enclosing, None);
let nested = match enclosing {
Some(compiler) => mem::replace(self, *compiler),
None => unreachable!("Can not go back past root copiler"),
};
self.ffi = ffi;
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::Tuple(tuple) => self.tuple(tuple),
CST::FFI { name, expression } => self.ffi(name, *expression, cst.span.clone()),
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 tuple(&mut self, tuple: Vec<Spanned<CST>>) -> Result<(), Syntax> {
let length = tuple.len();
for item in tuple.into_iter() {
self.walk(&item)?;
}
self.lambda.emit(Opcode::Tuple);
self.lambda.emit_bytes(&mut split_number(length));
Ok(())
}
pub fn ffi(&mut self, name: String, expression: Spanned<CST>, span: Span) -> Result<(), Syntax> {
self.walk(&expression)?;
let function = self.ffi.get(&name)
.map_err(|s| Syntax::error(&s, &span))?;
let index = match self.ffi_names.iter().position(|n| n == &name) {
Some(p) => p,
None => {
self.ffi_names.push(name);
self.lambda.add_ffi(function)
},
};
self.lambda.emit_span(&span);
self.lambda.emit(Opcode::FFICall);
self.lambda.emit_bytes(&mut split_number(index));
Ok(())
}
pub fn resolve_assign(&mut self, name: &str) -> bool {
let mut declared = false;
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());
declared = true;
self.lambda.emit(Opcode::Save);
self.locals.len() - 1
};
self.lambda.emit_bytes(&mut split_number(index));
return declared;
}
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);
}
CSTPattern::Tuple(tuple) => {
for (index, sub_pattern) in tuple.into_iter().enumerate() {
self.lambda.emit(Opcode::UnTuple);
self.lambda.emit_bytes(&mut split_number(index));
self.destructure(sub_pattern, redeclare);
}
self.lambda.emit(Opcode::Del);
},
}
}
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);
}
}