talc/talc-lang/src/compiler.rs

486 lines
11 KiB
Rust

use std::rc::Rc;
use crate::ast::{BinaryOp, Expr, LValue};
use crate::chunk::{Instruction as I, Chunk, Arg24};
use crate::symbol::Symbol;
use crate::value::Function;
use crate::value::Value;
use anyhow::Result;
#[derive(Debug, Clone)]
pub struct Local {
name: Rc<str>,
scope: usize,
}
#[derive(Clone, Copy, PartialEq, Eq)]
enum CompilerMode {
Function, Repl, // Module,
}
struct Compiler<'a> {
parent: Option<&'a Compiler<'a>>,
mode: CompilerMode,
func: Function,
scope: usize,
locals: Vec<Local>,
globals: Vec<Local>,
}
pub fn repl(expr: &Expr, globals: &[Local]) -> Result<(Function, Vec<Local>)> {
let locals = vec![Local {
name: "self".into(),
scope: 0,
}];
let mut comp = Compiler {
parent: None,
mode: CompilerMode::Repl,
func: Function { arity: 0, chunk: Chunk::new() },
scope: 0,
locals,
globals: globals.to_vec(),
};
comp.expr(expr)?;
comp.emit(I::Return);
Ok((comp.func, comp.globals))
}
impl<'a> Compiler<'a> {
fn new_function(&'a self, func: Function, args: &[&str]) -> Self {
let mut new = Self {
parent: Some(self),
mode: CompilerMode::Function,
func,
scope: 0,
locals: Vec::new(),
globals: Vec::new(),
};
new.locals.push(Local {
name: "self".into(),
scope: 0,
});
for arg in args {
new.locals.push(Local {
name: (*arg).into(),
scope: 0,
});
}
new
}
pub fn finish(mut self) -> Function {
self.emit(I::Return);
self.func
}
//
// Utility
//
fn add_const(&mut self, val: Value) -> usize {
self.func.chunk.add_const(val)
}
fn emit(&mut self, instr: I) -> usize {
self.func.chunk.add_instr(instr)
}
fn emit_discard(&mut self, mut n: usize) {
while n > 0 {
let instrs = &mut self.func.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.func.chunk.instrs.pop().unwrap();
self.func.chunk.instrs.pop().unwrap();
self.func.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.func.chunk.instrs.len()
}
fn update_instr(&mut self, n: usize, new: I) {
self.func.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: &str) -> 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: &str) -> 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: &str) {
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: &str) -> 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: &str) {
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: &str) {
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) -> Result<()> {
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) => {
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) => {
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::Pipe(a, f) => {
self.expr(a)?;
self.expr(f)?;
self.emit(I::Swap);
self.emit(I::Call(1));
},
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) => {
// 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);
},
Expr::Lambda(args, body) => self.expr_lambda(args, body)?,
}
Ok(())
}
fn expr_lambda(&mut self, args: &[&str], body: &Expr) -> Result<()> {
let func = Function {
arity: args.len(),
chunk: Chunk::new()
};
let mut inner = self.new_function(func, args);
inner.parent = Some(self);
inner.expr(body)?;
let func = inner.finish();
let n = self.add_const(Value::Function(Rc::new(func)));
self.emit(I::Const(Arg24::from_usize(n)));
Ok(())
}
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) -> Result<()> {
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);
},
}
Ok(())
}
}