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:
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user