talc/talc-lang/src/compiler.rs

573 lines
13 KiB
Rust

use std::rc::Rc;
use crate::ast::{BinaryOp, Expr, LValue, CatchBlock};
use crate::chunk::{Instruction as I, Chunk, Arg24, Catch};
use crate::lstr;
use crate::lstring::LStr;
use crate::symbol::Symbol;
use crate::value::function::{FuncAttrs, Function};
use crate::value::Value;
#[derive(Debug, Clone)]
pub struct Local {
name: Rc<LStr>,
scope: usize,
}
#[derive(Clone, Copy, PartialEq, Eq)]
enum CompilerMode {
Function, Repl, Module,
}
struct Compiler<'a> {
mode: CompilerMode,
parent: Option<&'a Compiler<'a>>,
chunk: Chunk,
attrs: FuncAttrs,
scope: usize,
locals: Vec<Local>,
globals: Vec<Local>,
}
pub fn compile(expr: &Expr) -> Function {
let mut comp = Compiler::new_module(None);
comp.expr(expr);
comp.finish()
}
pub fn compile_repl(expr: &Expr, globals: &[Local]) -> (Function, Vec<Local>) {
let mut comp = Compiler::new_repl(globals);
comp.expr(expr);
comp.finish_repl()
}
impl<'a> Default for Compiler<'a> {
fn default() -> Self {
let locals = vec![Local {
name: lstr!("self").into(),
scope: 0,
}];
Self {
mode: CompilerMode::Function,
parent: None,
chunk: Chunk::new(),
attrs: FuncAttrs { arity: 0, variadic: false },
scope: 0,
locals,
globals: Vec::new(),
}
}
}
impl<'a> Compiler<'a> {
fn new_repl(globals: &[Local]) -> Self {
Self {
mode: CompilerMode::Repl,
globals: globals.to_vec(),
..Self::default()
}
}
fn new_module(parent: Option<&'a Self>) -> Self {
Self {
mode: CompilerMode::Module,
parent,
..Self::default()
}
}
fn new_function(&'a self, args: &[&LStr]) -> Self {
let mut new = Self {
mode: CompilerMode::Function,
parent: Some(self),
..Self::default()
};
new.attrs.arity = args.len();
for arg in args {
new.locals.push(Local {
name: (*arg).into(),
scope: 0,
});
}
new
}
pub fn finish(mut self) -> Function {
self.emit(I::Return);
Function { chunk: Rc::new(self.chunk), attrs: self.attrs }
}
pub fn finish_repl(mut self) -> (Function, Vec<Local>) {
self.emit(I::Return);
(
Function { chunk: Rc::new(self.chunk), attrs: self.attrs },
self.globals
)
}
//
// Utility
//
fn add_const(&mut self, val: Value) -> usize {
self.chunk.add_const(val)
}
fn emit(&mut self, instr: I) -> usize {
self.chunk.add_instr(instr)
}
fn emit_discard(&mut self, mut n: usize) {
while n > 0 {
let instrs = &mut self.chunk.instrs;
// dup followed by store: remove the dup
if instrs.len() >= 2
&& matches!(instrs.get(instrs.len() - 2), Some(I::Dup))
&& matches!(instrs.last(), Some(
I::NewLocal | I::StoreLocal(_) | I::StoreGlobal(_)
))
{
// can't panic: checked that instrs.len() >= 2
let i = self.chunk.instrs.pop().unwrap();
self.chunk.instrs.pop().unwrap();
self.chunk.instrs.push(i);
n -= 1;
continue;
}
// final side-effectless instruction
let poppable = matches!(
instrs.last(),
Some(
I::Dup | I::Const(_) | I::Int(_)
| I::Nil | I::Bool(_) | I::Symbol(_)
| I::LoadLocal(_)
)
);
if poppable {
// can't panic: checked that instrs.last() was Some
instrs.pop().unwrap();
n -= 1;
continue;
}
// no more optimizations possible
break;
}
if n > 0 {
self.emit(I::Drop(Arg24::from_usize(n)));
}
}
fn ip(&self) -> usize {
self.chunk.instrs.len()
}
fn update_instr(&mut self, n: usize, new: I) {
self.chunk.instrs[n] = new;
}
fn begin_scope(&mut self) {
self.scope += 1;
}
fn end_scope(&mut self) {
self.scope -= 1;
// no need to clean up at bottom scope
if self.scope == 0 { return; }
for i in (0..self.globals.len()).rev() {
if self.globals[i].scope <= self.scope {
break;
}
self.globals.pop();
}
let mut count = 0;
for i in (0..self.locals.len()).rev() {
if self.locals[i].scope <= self.scope {
break;
}
self.locals.pop();
count += 1;
}
if count > 0 && self.scope > 0 {
self.emit(I::DropLocal(Arg24::from_usize(count)));
}
}
//
// variables
//
fn resolve_local(&mut self, name: &LStr) -> Option<usize> {
self.locals.iter().rev()
.position(|v| v.name.as_ref() == name)
.map(|x| self.locals.len() - x - 1)
}
fn resolve_global(&mut self, name: &LStr) -> Option<usize> {
self.globals.iter().rev()
.position(|v| v.name.as_ref() == name)
.map(|x| self.globals.len() - x - 1)
}
fn load_var(&mut self, name: &LStr) {
match (self.resolve_local(name), self.resolve_global(name)) {
(Some(n), None) => {
self.emit(I::LoadLocal(Arg24::from_usize(n)));
},
(Some(n), Some(m)) if n >= m => {
self.emit(I::LoadLocal(Arg24::from_usize(n)));
},
_ => {
let sym = Symbol::get(name);
self.emit(I::LoadGlobal(Arg24::from_symbol(sym)));
}
}
}
fn declare_local(&mut self, name: &LStr) -> usize {
if let Some(i) = self.resolve_local(name) {
if self.locals[i].scope == self.scope {
self.emit(I::StoreLocal(Arg24::from_usize(i)));
return i;
}
}
self.locals.push(Local {
name: name.into(),
scope: self.scope,
});
let i = self.locals.len() - 1;
self.emit(I::NewLocal);
i
}
fn store_local(&mut self, i: usize) {
self.emit(I::StoreLocal(Arg24::from_usize(i)));
}
fn store_global(&mut self, name: &LStr) {
let sym = Symbol::get(name);
self.emit(I::StoreGlobal(Arg24::from_symbol(sym)));
if let Some(i) = self.resolve_global(name) {
if self.globals[i].scope == self.scope {
return
}
}
self.globals.push(Local {
name: name.into(),
scope: self.scope,
});
}
fn store_default(&mut self, name: &LStr) {
match (self.resolve_local(name), self.resolve_global(name)) {
(Some(n), None) => self.store_local(n),
(Some(n), Some(m)) if n >= m => self.store_local(n),
(_, Some(_)) => self.store_global(name),
(None, None) => {
if self.mode == CompilerMode::Repl && self.scope == 1 {
self.store_global(name);
} else {
self.declare_local(name);
}
}
}
}
//
// Expressions
//
fn expr(&mut self, e: &Expr) {
match e {
Expr::Block(xs) if xs.is_empty() => { self.emit(I::Nil); },
Expr::Block(xs) => {
self.begin_scope();
for x in &xs[0..xs.len()-1] {
self.expr(x);
self.emit_discard(1);
}
self.expr(&xs[xs.len()-1]);
self.end_scope();
},
Expr::Literal(v) => self.expr_literal(v),
Expr::Ident(ident) => self.load_var(ident),
Expr::UnaryOp(o, a) => {
self.expr(a);
self.emit(I::UnaryOp(*o));
},
Expr::BinaryOp(o, a, b) => {
self.expr(a);
self.expr(b);
self.emit(I::BinaryOp(*o));
},
Expr::Assign(o, lv, a) => self.expr_assign(*o, lv, a),
Expr::AssignVar(name, a) => {
self.expr(a);
self.emit(I::Dup);
self.declare_local(name);
},
Expr::AssignGlobal(name, a) => {
self.expr(a);
self.emit(I::Dup);
self.store_global(name);
},
Expr::List(xs) if xs.is_empty() => {
self.emit(I::NewList(0));
},
Expr::List(xs) => {
let mut first = true;
for chunk in xs.chunks(16) {
for e in chunk {
self.expr(e);
}
if first {
self.emit(I::NewList(chunk.len() as u8));
first = false;
} else {
self.emit(I::GrowList(chunk.len() as u8));
}
}
},
Expr::Table(xs) if xs.is_empty() => {
self.emit(I::NewTable(0));
},
Expr::Table(xs) => {
let mut first = true;
for chunk in xs.chunks(8) {
for (k, v) in chunk {
self.expr(k);
self.expr(v);
}
if first {
self.emit(I::NewTable(chunk.len() as u8));
first = false;
} else {
self.emit(I::GrowTable(chunk.len() as u8));
}
}
},
Expr::Index(ct, idx) => {
self.expr(ct);
self.expr(idx);
self.emit(I::Index);
},
Expr::FnCall(f, args) => {
self.expr(f);
for a in args {
self.expr(a);
}
self.emit(I::Call(args.len() as u8));
},
Expr::AssocFnCall(o, f, args) => {
self.expr(o);
self.emit(I::Dup);
self.emit(I::Symbol(Arg24::from_symbol(*f)));
self.emit(I::Index);
self.emit(I::Swap);
for a in args {
self.expr(a);
}
self.emit(I::Call((args.len() + 1) as u8));
},
Expr::Return(e) => {
self.expr(e);
self.emit(I::Return);
},
Expr::Pipe(a, f) => {
self.expr(a);
self.expr(f);
self.emit(I::Swap);
self.emit(I::Call(1));
},
Expr::Lambda(args, body) => self.expr_lambda(args, body),
Expr::And(a, b) => {
self.expr(a);
self.emit(I::Dup);
let j1 = self.emit(I::JumpFalse(Arg24::from_usize(0)));
self.emit_discard(1);
self.expr(b);
self.update_instr(j1, I::JumpFalse(Arg24::from_usize(self.ip())));
},
Expr::Or(a, b) => {
self.expr(a);
self.emit(I::Dup);
let j1 = self.emit(I::JumpTrue(Arg24::from_usize(0)));
self.emit_discard(1);
self.expr(b);
self.update_instr(j1, I::JumpTrue(Arg24::from_usize(self.ip())));
},
Expr::If(cond, b1, b2) => {
self.expr(cond);
let j1 = self.emit(I::JumpFalse(Arg24::from_usize(0)));
self.expr(b1);
let j2 = self.emit(I::Jump(Arg24::from_usize(0)));
self.update_instr(j1, I::JumpFalse(Arg24::from_usize(self.ip())));
if let Some(b2) = b2 {
self.expr(b2);
} else {
self.emit(I::Nil);
}
self.update_instr(j2,
I::Jump(Arg24::from_usize(self.ip()))
);
},
Expr::While(cond, body) => {
let start = self.ip();
self.expr(cond);
let j1 = self.emit(I::JumpFalse(Arg24::from_usize(0)));
self.expr(body);
self.emit_discard(1);
self.emit(I::Jump(Arg24::from_usize(start)));
self.update_instr(j1, I::JumpFalse(Arg24::from_usize(self.ip())));
self.emit(I::Nil);
},
Expr::For(name, iter, body) => self.expr_for(name, iter, body),
Expr::Try(body, catches) => self.expr_try(body, catches),
}
}
fn expr_try(&mut self, body: &Expr, catch_blocks: &[CatchBlock]) {
let (idx, mut table) = self.chunk.begin_try_table(self.locals.len());
self.emit(I::BeginTry(Arg24::from_usize(idx)));
self.expr(body);
self.emit(I::EndTry);
let body_end_addr = self.emit(I::Jump(Arg24::from_usize(0)));
let mut catch_end_addrs = Vec::new();
for catch_block in catch_blocks {
table.catches.push(Catch {
addr: self.ip(),
types: catch_block.types.clone(),
});
self.begin_scope();
if let Some(name) = catch_block.name {
self.declare_local(name);
} else {
self.emit_discard(1);
}
self.expr(&catch_block.body);
self.end_scope();
let end_addr = self.emit(I::Jump(Arg24::from_usize(0)));
catch_end_addrs.push(end_addr);
}
let ip = Arg24::from_usize(self.ip());
self.update_instr(body_end_addr, I::Jump(ip));
for addr in catch_end_addrs {
self.update_instr(addr, I::Jump(ip));
}
self.chunk.finish_catch_table(idx, table);
}
fn expr_for(&mut self, name: &LStr, iter: &Expr, body: &Expr) {
// load iterable and convert to iterator
self.expr(iter);
self.emit(I::IterBegin);
// declare loop variable
self.begin_scope();
self.emit(I::Nil);
let local = self.declare_local(name);
// begin loop
let start = self.ip();
// call iterator and jump if nil, otherwise store
self.emit(I::Dup);
self.emit(I::Call(0));
let j1 = self.emit(I::IterTest(Arg24::from_usize(0)));
self.store_local(local);
// body
self.expr(body);
self.emit_discard(1);
// end loop
self.emit(I::Jump(Arg24::from_usize(start)));
self.update_instr(j1, I::IterTest(Arg24::from_usize(self.ip())));
self.end_scope();
self.emit(I::Nil);
}
fn expr_lambda(&mut self, args: &[&LStr], body: &Expr) {
let mut inner = self.new_function(args);
inner.parent = Some(self);
inner.expr(body);
let func = inner.finish();
let n = self.add_const(func.into());
self.emit(I::Const(Arg24::from_usize(n)));
}
fn expr_literal(&mut self, val: &Value) {
match val {
Value::Nil
=> { self.emit(I::Nil); },
Value::Bool(b)
=> { self.emit(I::Bool(*b)); },
Value::Int(i) if (-0x80_0000..=0x7f_ffff).contains(i)
=> { self.emit(I::Int(Arg24::from_i64(*i))); },
Value::Symbol(s)
=> { self.emit(I::Symbol(Arg24::from_symbol(*s))); },
_ => {
let n = self.add_const(val.clone());
self.emit(I::Const(Arg24::from_usize(n)));
},
}
}
fn expr_assign(&mut self, o: Option<BinaryOp>, lv: &LValue, a: &Expr) {
match (lv, o) {
(LValue::Ident(i), None) => {
self.expr(a);
self.emit(I::Dup);
self.store_default(i);
},
(LValue::Ident(i), Some(o)) => {
self.load_var(i);
self.expr(a);
self.emit(I::BinaryOp(o));
self.emit(I::Dup);
self.store_default(i);
},
(LValue::Index(ct, i), None) => {
self.expr(ct);
self.expr(i);
self.expr(a);
self.emit(I::StoreIndex);
},
(LValue::Index(ct, i), Some(o)) => {
self.expr(ct);
self.expr(i);
self.emit(I::DupTwo);
self.emit(I::Index);
self.expr(a);
self.emit(I::BinaryOp(o));
self.emit(I::StoreIndex);
},
}
}
}