Passerine's module system is pretty simple. At it's core, a module is defined as:
my_module = mod {
x = 0
y = n -> x + 1
double = a -> 2 * a
-- ...
}
my_module::double 4
-- is 8
Basically a module turns a scope into a struct, where all top-level variables become fields on that struct. As seen in the above example, we use mod to do this.
All files are modules, and are imported using the use keyword.
-- my_module.pn
x = 0
y = n -> x + 1
double = a -> 2 * a
-- ...
-- main.pn
use my_module
my_module::double 3
-- is 2
A folder with a file named mod.pn in it is also a module. This is similar to how Rust behaves:
-- my_module/mod.pn
-- ...
-- main.pn
use nest::my_module
-- ...
Modules are resolved at compile time. Upon using use, Passerine:
Looks for the file X.pn or X/mod.pn in the current scope of the file being compiled
If the file has already been compiled, use the cached version
Insert the compiled file into the current file, wrapping it all in a mod operation.
All modules must form a tree hierarchy. i.e, there should be only one path from the root ,nest, to any other module. Modules can be mutually recursive, but in this case the full path must be specified:
-- a.pn
use nest::b
print b::x
y = "Hello, "
-- b.pn
use nest::a
print a::y
x = "World!"
-- main.pn
use nest::a
use nest::b
Modules are only executed once, upon first load. Hello, World would be printed to the terminal in the above example. Here's the big ol' todo list. If you'd like to help, I'll mentor anyone through any one of these steps:
[ ] Implement a Record variant on Data in src/common/data.rs, including methods for displaying it.
[ ] TODO: This will require some discussion, but we basically need to add support for paths.
[ ] Implement lexing/parsing for the index (::), mod <block>, use <path>, and nest keywords.
[x] Implement lexing/parsing for record types, e.g. { foo: "Bar", baz: 27.5 }
[ ] Implement destructuring on record types, e.g. { thing: banana } = { thing: "Hello" }
[ ] Add support for compiling mod blocks to bytecode, converting them to records
[ ] Add support for indexing on records, and the nest keyword.
[ ] Add support for loading modules from other files. TODO: We might have to loop in Aspen to resolve these files.
[ ] Add support for loading modules from other folders. TODO: Aspen, see above step.
[ ] Polish and make sure everything works together.
The dev branch is a bit of a mess right now as I'm doing some big refactoring, so please make a feature branch off of master and implement any changes there. This will be a 0.9.X feature.
Passerine's module system is pretty simple. At it's core, a module is defined as:
Basically a module turns a scope into a struct, where all top-level variables become fields on that struct. As seen in the above example, we use
mod
to do this.All files are modules, and are imported using the
use
keyword.A folder with a file named
mod.pn
in it is also a module. This is similar to how Rust behaves:Modules are resolved at compile time. Upon using
use
, Passerine:X.pn
orX/mod.pn
in the current scope of the file being compiledmod
operation.All modules must form a tree hierarchy. i.e, there should be only one path from the root ,
nest
, to any other module. Modules can be mutually recursive, but in this case the full path must be specified:Modules are only executed once, upon first load.
Hello, World
would be printed to the terminal in the above example. Here's the big ol' todo list. If you'd like to help, I'll mentor anyone through any one of these steps:Record
variant onData
insrc/common/data.rs
, including methods for displaying it.::
),mod <block>
,use <path>
, andnest
keywords.{ foo: "Bar", baz: 27.5 }
{ thing: banana } = { thing: "Hello" }
mod
blocks to bytecode, converting them to recordsnest
keyword.The
dev
branch is a bit of a mess right now as I'm doing some big refactoring, so please make a feature branch off of master and implement any changes there. This will be a0.9.X
feature.