2022-01-12 17:35:48 -08:00
|
|
|
use crate::object::{QuoteTable, Value};
|
2022-01-12 18:00:11 -08:00
|
|
|
use crate::scope::*;
|
|
|
|
|
use crate::syn::ast::*;
|
2022-01-12 17:35:48 -08:00
|
|
|
use crate::vm::inst::*;
|
2022-01-12 21:49:41 -08:00
|
|
|
use std::rc::Rc;
|
2022-01-12 17:35:48 -08:00
|
|
|
|
|
|
|
|
pub struct Compile<'s> {
|
|
|
|
|
scope_stack: &'s mut ScopeStack,
|
|
|
|
|
quote_table: &'s mut QuoteTable,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<'s> Compile<'s> {
|
|
|
|
|
pub fn new(scope_stack: &'s mut ScopeStack, quote_table: &'s mut QuoteTable) -> Self {
|
|
|
|
|
Compile {
|
|
|
|
|
scope_stack,
|
|
|
|
|
quote_table,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-16 16:49:54 -08:00
|
|
|
pub fn compile(&mut self, stmt_list: Vec<SpStmt>) -> Vec<SpInst> {
|
2022-01-16 14:14:50 -08:00
|
|
|
// Compile meta-statements
|
|
|
|
|
let expr_list = self.compile_meta(stmt_list);
|
|
|
|
|
self.compile_expr_list(&expr_list)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Compile meta-expressions, like includes.
|
|
|
|
|
fn compile_meta(&mut self, stmt_list: Vec<SpStmt>) -> Vec<SpExpr> {
|
|
|
|
|
let mut expr_list = Vec::new();
|
|
|
|
|
for stmt in stmt_list {
|
|
|
|
|
match stmt.into_inner() {
|
|
|
|
|
Stmt::Include(path) => {
|
|
|
|
|
let stmt_list = self.parse_include(&path);
|
|
|
|
|
expr_list.extend(self.compile_meta(stmt_list));
|
|
|
|
|
}
|
|
|
|
|
Stmt::Expr(expr) => expr_list.push(expr),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
expr_list
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn parse_include(&mut self, _path: &str) -> Vec<SpStmt> {
|
|
|
|
|
todo!("includes")
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-16 16:49:54 -08:00
|
|
|
fn compile_expr_list(&mut self, expr_list: &Vec<SpExpr>) -> Vec<SpInst> {
|
2022-01-12 17:35:48 -08:00
|
|
|
// Scoping is done here.
|
|
|
|
|
// Local scopes are implicit. If a variable is assigned to at
|
|
|
|
|
// all in the current scope, it's considered to be a local.
|
|
|
|
|
let mut code = Vec::new();
|
|
|
|
|
for expr in expr_list {
|
2022-01-16 14:14:50 -08:00
|
|
|
self.discover_locals(&expr);
|
|
|
|
|
let thunk = self.compile_expr(&expr);
|
2022-01-12 17:35:48 -08:00
|
|
|
code.extend(thunk.flatten());
|
|
|
|
|
}
|
|
|
|
|
code
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn compile_expr(&mut self, expr: &SpExpr) -> Thunk {
|
|
|
|
|
match expr.inner() {
|
|
|
|
|
Expr::Atom(atom) => self.compile_atom(atom),
|
|
|
|
|
// this gets compiled whenever it gets evaluated
|
2022-01-16 14:14:50 -08:00
|
|
|
Expr::Quote(stmts) => {
|
2022-01-12 17:35:48 -08:00
|
|
|
self.scope_stack.push_scope();
|
2022-01-16 14:14:50 -08:00
|
|
|
// TODO - is self.compile the right thing to do here?
|
|
|
|
|
let compiled = Rc::new(self.compile(stmts.clone()));
|
2022-01-12 17:35:48 -08:00
|
|
|
let locals = self.scope_stack.pop_scope().unwrap();
|
2022-01-16 14:40:09 -08:00
|
|
|
let quote =
|
|
|
|
|
self.quote_table
|
|
|
|
|
.insert(expr.span().clone(), locals, stmts.clone(), compiled);
|
2022-01-16 16:49:54 -08:00
|
|
|
let inst = Inst::PushValue(Value::Quote(quote));
|
|
|
|
|
SpInst::new(expr.span().clone(), inst).into()
|
2022-01-12 17:35:48 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn compile_atom(&mut self, atom: &SpAtom) -> Thunk {
|
2022-01-16 16:49:54 -08:00
|
|
|
let inst = match atom.inner() {
|
|
|
|
|
Atom::Float(f) => Inst::PushValue(Value::Float(*f)),
|
|
|
|
|
Atom::Int(i) => Inst::PushValue(Value::Int(*i)),
|
|
|
|
|
Atom::Str(s) => Inst::PushValue(Value::Str(s.clone())),
|
2022-01-12 17:35:48 -08:00
|
|
|
Atom::Assign(text) => {
|
|
|
|
|
let word = self.scope_stack.insert_local(text);
|
2022-01-16 16:49:54 -08:00
|
|
|
Inst::Store(word)
|
2022-01-12 17:35:48 -08:00
|
|
|
}
|
|
|
|
|
Atom::Word(text) => {
|
|
|
|
|
// Look for locally defined symbols first. One can't be found,
|
|
|
|
|
// then create a local variable. The local variable *should* be
|
|
|
|
|
// defined already, but sometimes things happen.
|
|
|
|
|
let word = if let Some(word) = self.scope_stack.lookup_scoped(text) {
|
|
|
|
|
word
|
|
|
|
|
} else {
|
|
|
|
|
self.scope_stack.insert_local(text)
|
|
|
|
|
};
|
2022-01-16 16:49:54 -08:00
|
|
|
Inst::Load(word)
|
2022-01-12 17:35:48 -08:00
|
|
|
}
|
2022-01-16 16:49:54 -08:00
|
|
|
Atom::Apply => Inst::Call,
|
|
|
|
|
};
|
|
|
|
|
SpInst::new(atom.span().clone(), inst).into()
|
2022-01-12 17:35:48 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Discovers and inserts local variables into the scope.
|
|
|
|
|
///
|
|
|
|
|
/// If any assignment for a local variable appears in the scope, it will be
|
|
|
|
|
/// inserted.
|
|
|
|
|
fn discover_locals(&mut self, expr: &SpExpr) {
|
|
|
|
|
if let Expr::Atom(atom) = expr.inner() {
|
|
|
|
|
if let Atom::Assign(text) = atom.inner() {
|
|
|
|
|
self.scope_stack.insert_local(text);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
|
enum Thunk {
|
2022-01-16 16:49:54 -08:00
|
|
|
Block(Vec<SpInst>),
|
2022-01-12 17:35:48 -08:00
|
|
|
List(Vec<Thunk>),
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-16 16:49:54 -08:00
|
|
|
impl From<SpInst> for Thunk {
|
|
|
|
|
fn from(inst: SpInst) -> Thunk {
|
2022-01-12 17:35:48 -08:00
|
|
|
Thunk::Block(vec![inst])
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Thunk {
|
2022-01-16 16:49:54 -08:00
|
|
|
fn flatten(self) -> Vec<SpInst> {
|
2022-01-12 17:35:48 -08:00
|
|
|
use Thunk::*;
|
|
|
|
|
match self {
|
|
|
|
|
Block(block) => block,
|
|
|
|
|
List(list) => list.into_iter().flat_map(Thunk::flatten).collect(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|