Update how scope rules work, and update implementation

* Currently, scopes are only allowed to look at their locals and the
  globals. Inner functions cannot refer to values in their parent
  functions. This will change eventually.
* Scope lookup is split between globals and locals. Locals are defined
  in a scope if they are explicitly assigned to.
    * i.e. `a = foo` will treat `a` as a local in the current scope if
      it appears anywhere in that scope. This does not extend to
      setattrs; `a.b = foo` will not trigger `a` into being a local var.
* `Package` objects are no longer returned from the compiler - instead,
  a user function is returned.
* Other various changes and renames

Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
This commit is contained in:
2020-09-27 19:33:18 -07:00
parent 4848a342f0
commit 958a6caabb
12 changed files with 304 additions and 171 deletions

View File

@@ -1,18 +1,17 @@
pub mod basic_block;
pub mod error;
mod locals;
mod scope;
pub mod thunk;
use crate::{syn::ast::Body, obj::prelude::*, vm::{consts::*, package::Package}};
use crate::{syn::ast::Body, obj::prelude::*, vm::consts::*};
use scope::*;
use std::collections::HashMap;
#[derive(Default)]
pub struct Compile {
const_data: ConstData,
globals: Locals,
scope: Vec<Locals>,
names: Vec<Sym>,
next_local: Name,
scope: Scope,
}
impl Compile {
@@ -22,12 +21,24 @@ impl Compile {
}
/// Compiles the given AST body.
pub fn compile(mut self, body: &Body) -> error::Result<Package> {
pub fn compile(mut self, body: &Body) -> error::Result<(ConstPool, UserFunRef)> {
self.push_scope_layer();
let main = thunk::CompileBody::new(&mut self)
.compile(body)?
.flatten()
.to_vec();
Ok(Package::new(self.names, self.const_data.const_pool, main))
let globals_syms: std::collections::BTreeMap<_, _> = self.pop_scope_layer().unwrap()
.into_iter()
.map(|(sym, name)| (name, sym))
.collect();
let globals = globals_syms.into_iter()
.enumerate()
.map(|(index, (name, sym))| {
assert_eq!(index, name.index());
sym
})
.collect();
Ok((self.const_data.const_pool, UserFun::new_obj(main, globals)))
}
/// Gets the constant data that is interned in this compile session.
@@ -55,64 +66,34 @@ impl Compile {
self.const_data_mut().push(obj)
}
/// Looks up a variable name.
pub fn lookup_scope(&mut self, sym: Sym) -> Option<Name> {
self.scope
.iter()
.rev()
.filter_map(|locals| locals.get(&sym))
.next()
.or_else(|| self.globals.get(&sym))
.copied()
/// Looks up a local variable name.
pub fn lookup_local(&mut self, sym: Sym) -> Option<Name> {
self.scope.lookup_local(sym)
}
/// Looks up a variable name, or creates a global name if it doesn't exist.
pub fn lookup_scope_or_create_global(&mut self, sym: Sym) -> Name {
if let Some(local) = self.lookup_scope(sym) {
local
} else {
self.create_global(sym)
}
/// Looks up a global variable name.
pub fn lookup_global(&mut self, sym: Sym) -> Option<Name> {
self.scope.lookup_global(sym)
}
/// Creates a new local variable if it does not exist in the current local scope.
pub(crate) fn create_local(&mut self, sym: Sym) -> Name {
let locals = self.scope.last_mut().expect("scope");
if let Some(local) = locals.get(&sym) {
*local
} else {
// wish I could use mem::replace here, oh well
let local = self.next_local;
self.next_local = self.next_local.next();
locals.insert(sym, local);
self.names.push(sym);
assert_eq!(self.names.len(), self.next_local.index());
local
}
self.scope.create_local(sym)
}
/// Creates a new global variable if it does not exist in the current global scope.
pub fn create_global(&mut self, sym: Sym) -> Name {
if let Some(global) = self.globals.get(&sym) {
*global
} else {
let global = self.next_local;
self.next_local = self.next_local.next();
self.globals.insert(sym, global);
self.names.push(sym);
assert_eq!(self.names.len(), self.next_local.index());
global
}
self.scope.create_global(sym)
}
/// Pushes an empty scope layer of local variables.
pub(crate) fn push_scope_layer(&mut self) {
self.scope.push(Default::default());
pub fn push_scope_layer(&mut self) {
self.scope.push_layer()
}
/// Pops a scope layer of local variables, if any are available.
pub(crate) fn pop_scope_layer(&mut self) -> Option<Locals> {
self.scope.pop()
pub fn pop_scope_layer(&mut self) -> Option<ScopeLocalSyms> {
self.scope.pop_layer()
}
/// Collects local variables for the given AST body non-recursively.