Implement import a, b, c from foo syntax
This brings stuff into the local scope, but it is a little funky with local scopes that are above the current level (in the same function or module). Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
This commit is contained in:
@@ -795,6 +795,28 @@ impl StmtVisitor for Compiler<'_> {
|
|||||||
|
|
||||||
let line = stmt_line_number(stmt);
|
let line = stmt_line_number(stmt);
|
||||||
|
|
||||||
|
// allocate names - local or global
|
||||||
|
let nil_constant = self.insert_constant(Nil::create())?;
|
||||||
|
for what in &stmt.what {
|
||||||
|
if self.is_global_scope() {
|
||||||
|
self.insert_global(&what.text)?;
|
||||||
|
} else {
|
||||||
|
self.emit((what.line, what.line), Op::PushConstant(nil_constant));
|
||||||
|
self.insert_local(what.text.to_string())?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if stmt.what.is_empty() && stmt.module.kind == TokenKind::Name {
|
||||||
|
if self.is_global_scope() {
|
||||||
|
self.insert_global(&stmt.module.text)?;
|
||||||
|
} else {
|
||||||
|
self.emit(
|
||||||
|
(stmt.module.line, stmt.module.line),
|
||||||
|
Op::PushConstant(nil_constant),
|
||||||
|
);
|
||||||
|
self.insert_local(stmt.module.text.to_string())?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// resolve filename and get full filepath
|
// resolve filename and get full filepath
|
||||||
let path = match stmt.module.kind {
|
let path = match stmt.module.kind {
|
||||||
TokenKind::Name => self
|
TokenKind::Name => self
|
||||||
@@ -831,12 +853,12 @@ impl StmtVisitor for Compiler<'_> {
|
|||||||
};
|
};
|
||||||
let module_constant = self.insert_constant(upcast_obj(module.clone()))?;
|
let module_constant = self.insert_constant(upcast_obj(module.clone()))?;
|
||||||
|
|
||||||
self.emit(stmt_line_number(stmt), Op::PushConstant(module_constant));
|
// evaluate module
|
||||||
|
self.emit(line, Op::PushConstant(module_constant));
|
||||||
|
self.emit(line, Op::EvalModule);
|
||||||
|
|
||||||
if stmt.what.is_empty() {
|
if stmt.what.is_empty() {
|
||||||
// evaluate the module, and then assign the resulting object to the module name as
|
// assign the resulting object to the module name as appropriate
|
||||||
// appropriate
|
|
||||||
self.emit(line, Op::EnterModule);
|
|
||||||
// only assign if it's a name, if it's a string we don't assign anything
|
// only assign if it's a name, if it's a string we don't assign anything
|
||||||
if stmt.module.kind == TokenKind::Name {
|
if stmt.module.kind == TokenKind::Name {
|
||||||
self.emit_assign(line, &stmt.module.text)?;
|
self.emit_assign(line, &stmt.module.text)?;
|
||||||
@@ -845,8 +867,16 @@ impl StmtVisitor for Compiler<'_> {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// evaluate the module, and then assign all names that were imported as appropriate
|
// evaluate the module, and then assign all names that were imported as appropriate
|
||||||
// TODO Compiler::visit_import_stmt - visit names from module
|
for what in stmt.what.iter() {
|
||||||
todo!("import names from module")
|
self.emit(line, Op::Dup);
|
||||||
|
let constant_id = self.insert_constant(Str::create(&what.text))?;
|
||||||
|
self.emit(line, Op::GetAttr(constant_id));
|
||||||
|
self.emit_assign(line, &what.text)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// not importing the module name itself, so clean up the stack after we're done setting
|
||||||
|
// names
|
||||||
|
self.emit(line, Op::Pop);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -37,6 +37,11 @@ fn disassemble_chunk(chunk: &Chunk, globals: &Vec<String>, constants: &Vec<ObjP>
|
|||||||
arg = format!("{}", &constants[*constant_id as usize].borrow());
|
arg = format!("{}", &constants[*constant_id as usize].borrow());
|
||||||
info = format!("(constant ID {constant_id})");
|
info = format!("(constant ID {constant_id})");
|
||||||
}
|
}
|
||||||
|
Op::Dup => {
|
||||||
|
op_str = "DUP";
|
||||||
|
arg = String::new();
|
||||||
|
info = String::new();
|
||||||
|
}
|
||||||
Op::GetLocal(local_id) => {
|
Op::GetLocal(local_id) => {
|
||||||
op_str = "GET_LOCAL";
|
op_str = "GET_LOCAL";
|
||||||
let local = &chunk.locals[*local_id as usize];
|
let local = &chunk.locals[*local_id as usize];
|
||||||
@@ -109,8 +114,8 @@ fn disassemble_chunk(chunk: &Chunk, globals: &Vec<String>, constants: &Vec<ObjP>
|
|||||||
arg = String::new();
|
arg = String::new();
|
||||||
info = String::new();
|
info = String::new();
|
||||||
}
|
}
|
||||||
Op::EnterModule => {
|
Op::EvalModule => {
|
||||||
op_str = "ENTER_MODULE";
|
op_str = "EVAL_MODULE";
|
||||||
arg = String::new();
|
arg = String::new();
|
||||||
info = String::new();
|
info = String::new();
|
||||||
}
|
}
|
||||||
|
|||||||
16
src/vm.rs
16
src/vm.rs
@@ -8,6 +8,7 @@ pub enum Op {
|
|||||||
// Stack functions
|
// Stack functions
|
||||||
Pop,
|
Pop,
|
||||||
PushConstant(LongOpArg),
|
PushConstant(LongOpArg),
|
||||||
|
Dup,
|
||||||
|
|
||||||
// Variables
|
// Variables
|
||||||
GetLocal(LocalIndex),
|
GetLocal(LocalIndex),
|
||||||
@@ -34,7 +35,7 @@ pub enum Op {
|
|||||||
|
|
||||||
// VM control
|
// VM control
|
||||||
Nop,
|
Nop,
|
||||||
EnterModule,
|
EvalModule,
|
||||||
ExitModule,
|
ExitModule,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,10 +156,9 @@ impl<'c> Vm<'c> {
|
|||||||
|
|
||||||
/// Gets the chunk of the currently executing frame.
|
/// Gets the chunk of the currently executing frame.
|
||||||
pub fn chunk(&self) -> Option<&Chunk> {
|
pub fn chunk(&self) -> Option<&Chunk> {
|
||||||
if let Function::Chunk(chunk) = &self.frame().function {
|
match &self.frame().function {
|
||||||
Some(chunk)
|
Function::Chunk(chunk) | Function::Module(chunk) => Some(chunk),
|
||||||
} else {
|
Function::Builtin(_, _) => None,
|
||||||
None
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -339,6 +339,10 @@ impl<'c> Vm<'c> {
|
|||||||
let constant = self.constants[constant_id as usize].clone();
|
let constant = self.constants[constant_id as usize].clone();
|
||||||
self.push(constant);
|
self.push(constant);
|
||||||
}
|
}
|
||||||
|
Op::Dup => {
|
||||||
|
let top = self.peek();
|
||||||
|
self.push(top);
|
||||||
|
}
|
||||||
Op::GetLocal(local_index) => {
|
Op::GetLocal(local_index) => {
|
||||||
let local = &self.chunk().expect("no chunk").locals[local_index as usize];
|
let local = &self.chunk().expect("no chunk").locals[local_index as usize];
|
||||||
let value = self.stack[self.frame().stack_base + local.slot as usize].clone();
|
let value = self.stack[self.frame().stack_base + local.slot as usize].clone();
|
||||||
@@ -470,7 +474,7 @@ impl<'c> Vm<'c> {
|
|||||||
Op::Nop => {
|
Op::Nop => {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Op::EnterModule => {
|
Op::EvalModule => {
|
||||||
// check if the module has been evaluated yet
|
// check if the module has been evaluated yet
|
||||||
let module = self.peek();
|
let module = self.peek();
|
||||||
let value = with_obj_downcast(module.clone(), |module: &Module| {
|
let value = with_obj_downcast(module.clone(), |module: &Module| {
|
||||||
|
|||||||
@@ -4,3 +4,9 @@ import test_import_local
|
|||||||
println(test_import_local.foo)
|
println(test_import_local.foo)
|
||||||
println(test_import_local.bar)
|
println(test_import_local.bar)
|
||||||
println(test_import_local.baz)
|
println(test_import_local.baz)
|
||||||
|
|
||||||
|
import foo, bar, baz from test_import_local
|
||||||
|
|
||||||
|
println(foo)
|
||||||
|
println(bar)
|
||||||
|
println(baz)
|
||||||
|
|||||||
@@ -3,3 +3,6 @@ importing test_import_local
|
|||||||
1
|
1
|
||||||
2
|
2
|
||||||
3
|
3
|
||||||
|
1
|
||||||
|
2
|
||||||
|
3
|
||||||
|
|||||||
Reference in New Issue
Block a user