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:
2024-10-04 20:11:06 -07:00
parent c176efb13a
commit 17408a8695
5 changed files with 62 additions and 14 deletions

View File

@@ -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(())

View File

@@ -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();
} }

View File

@@ -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| {

View File

@@ -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)

View File

@@ -3,3 +3,6 @@ importing test_import_local
1 1
2 2
3 3
1
2
3