WIP: Add imports and modules

This is a big change because it touches a lot of stuff, but here is the
overview:

* Import syntax:
    ```
    import foo
    import bar from foo
    import bar from "foo.npp"
    import bar, baz from foo
    import * from foo
    import "foo.npp"
    ```
    * These are all valid imports. They should be pretty
      straightforward, maybe with exception of the last item. If you are
      importing a path directly, but not importing any members from it,
      it does not insert anything into the current namespace, and just
      executes the file. This is probably going to be unused but I want
      to include it for completeness. We can always remove it later
      before a hypothetical 1.0 release.
    * The "from" keyword is only ever used as a keyword here, and I am
      allowing it to be used as an identifier elsewhere. Don't export
      it, because that's weird and wrong and won't work.
* Modules:
    * Doing an `import foo` will look for "foo.npp" at compile-time,
      relative to the importer's directory, parse it, and compile it.
      The importer will then attempt to execute the module with the new
      `EnterModule` op. This instruction will execute the module kind of
      like a function, assigning the module's global namespace to an
      object that you can pass around.
    * `import bar from foo` and `import bar from "foo.npp"` et al syntax
      is not currently implemented in the compiler.
    * There is a new "Module" object that represents a potentially
      un-initialized module. This can't be referred to directly in code.
* VM:
    * The VM operates around Module objects now. If you want to "call" a
      new module, you should call `enter_module`. This is how the main
      chunk is invoked.
* TODOs:
    * `exit_module` function in the VM
    * Finish up module implementation in compiler
    * Built-in modules
    * Sub-modules - e.g. `import foo.bar` - how does naming work for
      this?
    * Module directories. In Python you have `foo/__init__.py` and in
      Rust you have `foo/mod.rs`.
    * Probably a "Namespace" object that explicitly denotes "this is an
      imported module that you're dealing with"
    * Tests, tests, tests

Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
This commit is contained in:
2024-10-04 10:11:49 -07:00
parent 4a7644b84a
commit f0de5f7850
12 changed files with 753 additions and 268 deletions

123
src/obj/module.rs Normal file
View File

@@ -0,0 +1,123 @@
use std::fmt::{self, Debug, Display};
use std::rc::Rc;
use gc::{Finalize, Trace};
use crate::obj::macros::*;
use crate::obj::prelude::*;
use crate::obj::BaseObj;
use crate::vm::Chunk;
#[derive(Trace, Finalize)]
pub struct Module {
base: BaseObj,
#[unsafe_ignore_trace]
path: Rc<String>,
#[unsafe_ignore_trace]
chunk: Rc<Chunk>,
globals: Vec<String>,
evaluated_value: Option<ObjP>,
}
impl Module {
pub fn new(path: impl ToString, chunk: Rc<Chunk>, globals: Vec<String>) -> Self {
Module {
base: Default::default(),
path: Rc::new(path.to_string()),
chunk,
globals,
evaluated_value: None,
}
}
pub fn path(&self) -> &Rc<String> {
&self.path
}
pub fn chunk(&self) -> &Rc<Chunk> {
&self.chunk
}
pub fn globals(&self) -> &Vec<String> {
&self.globals
}
pub fn evaluated_value(&self) -> &Option<ObjP> {
&self.evaluated_value
}
pub fn set_evaluated_value(&mut self, value: Option<ObjP>) {
self.evaluated_value = value;
}
impl_create!(path: impl ToString, chunk: Rc<Chunk>, globals: Vec<String>);
}
impl Debug for Module {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(fmt, "<Module {}>", self.path())
}
}
impl Display for Module {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(self, fmt)
}
}
impl Object for Module {
fn equals(&self, other: &dyn Object) -> bool {
if let Some(other) = other.as_any().downcast_ref::<Module>() {
// only referential identity
std::ptr::addr_eq(self, other)
} else {
false
}
}
impl_base_obj!(Module);
}
////////////////////////////////////////////////////////////////////////////////
// Module method implementations
////////////////////////////////////////////////////////////////////////////////
/*
impl Module {
pub(crate) fn execute(vm: &mut Vm, state: FunctionState) -> FunctionResult {
match state {
FunctionState::Begin => {
let this = vm.frame_stack()[0].clone();
let value = with_obj_downcast(this.clone(), |module: &Module| {
module.evaluated_value.clone()
});
// don't evaluate this twice
if let Some(value) = value {
return FunctionResult::ReturnPush(value);
}
vm.enter_module(this.clone());
FunctionResult::Yield(0)
}
FunctionState::Resume(0) => {
let this = vm.frame_stack()[0].clone();
let obj = with_obj_downcast(this.clone(), |module: &Module| {
assert_eq!(module.globals().len(), vm.globals().len());
let obj = Obj::create();
module
.globals()
.iter()
.zip(vm.globals())
.for_each(|(name, value)| {
obj.borrow_mut().set_attr(name, value.clone());
});
obj
});
FunctionResult::ReturnPush(obj)
}
_ => unreachable!(),
}
}
}
*/