Finally have things printing to the screen
Nothing fancy yet. This commit adds a bunch of stuff (oops): * compiling code * VM with instructions * remove eval.rs and just eval in the Machine struct itself * squash most warnings now that we're using stuff here And probably more. But that's all for now folks Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
This commit is contained in:
110
src/compile.rs
110
src/compile.rs
@@ -0,0 +1,110 @@
|
||||
use crate::object::{QuoteTable, Value};
|
||||
use crate::syn::{ast::*, words::*};
|
||||
use crate::vm::inst::*;
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn compile(&mut self, expr_list: &Vec<SpExpr>) -> Vec<Inst> {
|
||||
// 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 {
|
||||
self.discover_locals(expr);
|
||||
let thunk = self.compile_expr(expr);
|
||||
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
|
||||
Expr::Quote(exprs) => {
|
||||
self.scope_stack.push_scope();
|
||||
let compiled = self.compile(exprs);
|
||||
let locals = self.scope_stack.pop_scope().unwrap();
|
||||
let quote = self
|
||||
.quote_table
|
||||
.insert(expr.span(), locals, exprs.clone(), compiled);
|
||||
Inst::PushValue(Value::Quote(quote)).into()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn compile_atom(&mut self, atom: &SpAtom) -> Thunk {
|
||||
match atom.inner() {
|
||||
Atom::Float(f) => Inst::PushValue(Value::Float(*f)).into(),
|
||||
Atom::Int(i) => Inst::PushValue(Value::Int(*i)).into(),
|
||||
Atom::Str(s) => Inst::PushValue(Value::Str(s.clone())).into(),
|
||||
Atom::Assign(text) => {
|
||||
let word = self.scope_stack.insert_local(text);
|
||||
Inst::Store(word).into()
|
||||
}
|
||||
Atom::Word(text) => {
|
||||
// XXX : probably something better than this
|
||||
// Check builtins
|
||||
if text == "." {
|
||||
return Inst::Print.into();
|
||||
}
|
||||
|
||||
// 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)
|
||||
};
|
||||
Inst::Load(word).into()
|
||||
}
|
||||
Atom::Apply => Inst::Call.into(),
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
Block(Vec<Inst>),
|
||||
List(Vec<Thunk>),
|
||||
}
|
||||
|
||||
impl From<Inst> for Thunk {
|
||||
fn from(inst: Inst) -> Thunk {
|
||||
Thunk::Block(vec![inst])
|
||||
}
|
||||
}
|
||||
|
||||
impl Thunk {
|
||||
fn flatten(self) -> Vec<Inst> {
|
||||
use Thunk::*;
|
||||
match self {
|
||||
Block(block) => block,
|
||||
List(list) => list.into_iter().flat_map(Thunk::flatten).collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user