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:
@@ -1,5 +1,6 @@
|
||||
use crate::obj::prelude::*;
|
||||
use shredder::{GcSafe, Scan, Scanner};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
/// A stack call frame.
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -25,20 +26,20 @@ unsafe impl GcSafe for FrameKind {}
|
||||
|
||||
#[derive(Scan, Debug, Clone)]
|
||||
pub struct Frame {
|
||||
locals: Names,
|
||||
locals: FrameLocals,
|
||||
kind: FrameKind,
|
||||
}
|
||||
|
||||
impl Frame {
|
||||
pub fn new(locals: Names, kind: FrameKind) -> Self {
|
||||
pub fn new(locals: FrameLocals, kind: FrameKind) -> Self {
|
||||
Self { locals, kind, }
|
||||
}
|
||||
|
||||
pub fn locals(&self) -> &Names {
|
||||
pub fn locals(&self) -> &FrameLocals {
|
||||
&self.locals
|
||||
}
|
||||
|
||||
pub fn locals_mut(&mut self) -> &mut Names {
|
||||
pub fn locals_mut(&mut self) -> &mut FrameLocals {
|
||||
&mut self.locals
|
||||
}
|
||||
|
||||
@@ -46,3 +47,5 @@ impl Frame {
|
||||
&self.kind
|
||||
}
|
||||
}
|
||||
|
||||
pub type FrameLocals = BTreeMap<usize, ObjRef>;
|
||||
|
||||
@@ -10,10 +10,16 @@ pub enum Inst {
|
||||
PushConst(ConstHandle),
|
||||
|
||||
/// Looks up and pushes a local value.
|
||||
LoadName(Name),
|
||||
LoadLocal(Name),
|
||||
|
||||
/// Looks up and pushes a global value.
|
||||
LoadGlobal(Name),
|
||||
|
||||
/// Pop a value from the stack, possibly into a local symbol.
|
||||
Pop(Option<Name>),
|
||||
PopLocal(Option<Name>),
|
||||
|
||||
/// Pop a value from the stack, possibly into a global symbol.
|
||||
PopGlobal(Option<Name>),
|
||||
|
||||
/// Pops a symbol value and an object reference.
|
||||
///
|
||||
@@ -119,8 +125,10 @@ impl Inst {
|
||||
match self {
|
||||
Inst::PushSym(_) => "PUSH_SYM",
|
||||
Inst::PushConst(_) => "PUSH_CONST",
|
||||
Inst::LoadName(_) => "LOAD_NAME",
|
||||
Inst::Pop(_) => "POP",
|
||||
Inst::LoadLocal(_) => "LOAD_LOCAL",
|
||||
Inst::LoadGlobal(_) => "LOAD_GLOBAL",
|
||||
Inst::PopLocal(_) => "POP_LOCAL",
|
||||
Inst::PopGlobal(_) => "POP_GLOBAL",
|
||||
Inst::GetAttr(_) => "GET_ATTR",
|
||||
Inst::SetAttr(_) => "SET_ATTR",
|
||||
Inst::Jump(_) => "JUMP",
|
||||
|
||||
@@ -143,14 +143,16 @@ impl<'p> Vm<'p> {
|
||||
fn begin_call(&mut self, caller: ObjRef, args: Vec<ObjRef>) {
|
||||
// create stack frame
|
||||
let stack_frame = if let Some(user_fun) = std::any::Any::downcast_ref::<UserFunRef>(&caller) {
|
||||
let names: Names = {
|
||||
let names: FrameLocals = {
|
||||
read_obj!(let fun_ref = user_fun);
|
||||
fun_ref.locals()
|
||||
.iter()
|
||||
.enumerate()
|
||||
.zip(args.into_iter())
|
||||
.map(|((_, v), arg)| (*v, arg.clone()))
|
||||
.map(|((index, _), arg)| (index, arg.clone()))
|
||||
.collect()
|
||||
};
|
||||
// TODO : check function arity vs argument count
|
||||
|
||||
Frame::new(names, FrameKind::User {
|
||||
last_pc: self.pc(),
|
||||
@@ -202,20 +204,31 @@ impl<'p> Vm<'p> {
|
||||
.clone();
|
||||
self.push(obj_ref);
|
||||
}
|
||||
Inst::LoadName(name) => {
|
||||
Inst::LoadLocal(local) => {
|
||||
let obj_ref = self.frames()
|
||||
.iter()
|
||||
.rev()
|
||||
.filter_map(|frame| frame.locals().get(&name))
|
||||
.filter_map(|frame| frame.locals().get(&local.index()))
|
||||
.cloned()
|
||||
.next();
|
||||
if let Some(obj_ref) = obj_ref {
|
||||
self.push(obj_ref);
|
||||
} else {
|
||||
todo!("TODO: implement \"name value not found\" lookup")
|
||||
todo!("TODO: implement \"local value not found\" lookup")
|
||||
}
|
||||
}
|
||||
Inst::Pop(name) => {
|
||||
Inst::LoadGlobal(global) => {
|
||||
let obj_ref = self.frames()
|
||||
.first()
|
||||
.and_then(|frame| frame.locals().get(&global.index()))
|
||||
.cloned();
|
||||
if let Some(obj_ref) = obj_ref {
|
||||
self.push(obj_ref);
|
||||
} else {
|
||||
todo!("TODO: implement \"local value not found\" lookup")
|
||||
}
|
||||
}
|
||||
Inst::PopLocal(name) => {
|
||||
let tos = self.pop()
|
||||
.expect("stack underflow");
|
||||
// pop into name
|
||||
@@ -224,6 +237,15 @@ impl<'p> Vm<'p> {
|
||||
}
|
||||
// else discard
|
||||
}
|
||||
Inst::PopGlobal(name) => {
|
||||
let tos = self.pop()
|
||||
.expect("stack underflow");
|
||||
// pop into name
|
||||
if let Some(name) = name {
|
||||
self.set_global(name, tos);
|
||||
}
|
||||
// else discard
|
||||
}
|
||||
Inst::GetAttr(sym) => {
|
||||
let obj_ref = self.pop().expect("getattr object");
|
||||
read_obj!(let obj = obj_ref);
|
||||
@@ -281,21 +303,20 @@ impl<'p> Vm<'p> {
|
||||
}
|
||||
|
||||
fn set_local(&mut self, name: Name, value: ObjRef) {
|
||||
for frame in self.frames.iter_mut().rev() {
|
||||
let locals = frame.locals_mut();
|
||||
if locals.contains_key(&name) {
|
||||
locals.insert(name, value);
|
||||
return;
|
||||
}
|
||||
}
|
||||
unreachable!("unregistered name {:?}", name);
|
||||
let frame = self.frame_mut().unwrap();
|
||||
let locals = frame.locals_mut();
|
||||
locals.insert(name.index(), value);
|
||||
}
|
||||
|
||||
fn set_global(&mut self, name: Name, value: ObjRef) {
|
||||
let frame = self.frames.first_mut().unwrap();
|
||||
let locals = frame.locals_mut();
|
||||
locals.insert(name.index(), value);
|
||||
}
|
||||
|
||||
fn get_local(&mut self, name: Name) -> Option<ObjRef> {
|
||||
self.frames.iter()
|
||||
.rev()
|
||||
.filter_map(|frame| frame.locals().get(&name))
|
||||
.next()
|
||||
self.frame()
|
||||
.and_then(|frame| frame.locals().get(&name.index()))
|
||||
.cloned()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use crate::{obj::prelude::*, vm::{consts::ConstPool, inst::Inst}};
|
||||
use shredder::Scan;
|
||||
use std::io::{self, Write};
|
||||
|
||||
/// A compiled package that can be executed by a VM.
|
||||
#[derive(Scan, Debug)]
|
||||
@@ -31,74 +30,4 @@ impl Package {
|
||||
pub fn code(&self) -> &Vec<Inst> {
|
||||
&self.code
|
||||
}
|
||||
|
||||
/// Dumps a debug output of this package to the given writer.
|
||||
pub fn dump(&self, writer: &mut dyn Write) -> io::Result<()> {
|
||||
// column widths
|
||||
let addr_w = num_digits(self.code().len(), 16).max(4);
|
||||
let inst_col = 16 - addr_w;
|
||||
let inst_w = 16;
|
||||
|
||||
for (addr, inst) in self.code().iter().enumerate() {
|
||||
let (param_val, mut param_name) = match inst {
|
||||
Inst::PushSym(sym) | Inst::GetAttr(sym) | Inst::SetAttr(sym) => (
|
||||
sym.index().to_string(),
|
||||
global_sym_lookup(*sym).unwrap().to_string(),
|
||||
),
|
||||
Inst::PushConst(hdl) => (
|
||||
hdl.index().to_string(),
|
||||
{
|
||||
let obj_ref = self.const_pool().get(*hdl);
|
||||
read_obj!(let obj = obj_ref);
|
||||
format!("{:?}", obj)
|
||||
},
|
||||
),
|
||||
Inst::LoadName(local) => (
|
||||
local.index().to_string(),
|
||||
global_sym_lookup(self.names()[local.index()]).unwrap().to_string(),
|
||||
),
|
||||
Inst::Pop(local) => {
|
||||
if let Some(local) = local {
|
||||
let index = local.index();
|
||||
let sym = self.names()[index];
|
||||
let name = global_sym_lookup(sym).unwrap().to_string();
|
||||
(index.to_string(), name)
|
||||
} else {
|
||||
("(discarded)".to_string(), String::new())
|
||||
}
|
||||
}
|
||||
Inst::Jump(addr) | Inst::JumpTrue(addr) => (
|
||||
addr.to_string(),
|
||||
String::new(),
|
||||
),
|
||||
Inst::Call(argc) => (
|
||||
argc.to_string(),
|
||||
String::new(),
|
||||
),
|
||||
_ => (String::new(), String::new()),
|
||||
};
|
||||
|
||||
if !param_name.is_empty() {
|
||||
param_name = format!("({})", param_name);
|
||||
}
|
||||
|
||||
writeln!(
|
||||
writer,
|
||||
"{:0addr_w$x}{space: >inst_col$}{: <inst_w$}{: <4} {}",
|
||||
addr,
|
||||
inst.name(),
|
||||
param_val,
|
||||
param_name,
|
||||
space = "",
|
||||
addr_w = addr_w,
|
||||
inst_w = inst_w,
|
||||
inst_col = inst_col,
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn num_digits(n: usize, radix: usize) -> usize {
|
||||
((n as f64) + 1.0).log(radix as f64).ceil() as usize
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user