mirror of
https://github.com/zaphar/ucg.git
synced 2025-07-21 18:10:42 -04:00
FEATURE: Give modules a reference to self.
This commit is contained in:
parent
f12d264778
commit
439ebf74f3
@ -604,29 +604,23 @@ let embedded_default_params = top_mod_out_expr{};
|
||||
embedded_default_params.value == "None";
|
||||
```
|
||||
|
||||
### Recursive Modules
|
||||
### Module builtin bindings
|
||||
|
||||
One consequence of a module being able to import the same file they are located in
|
||||
is that modules can be called recursively. They are the only expression that is
|
||||
capable of recursion in UCG. Recursion can be done by importing the module's file
|
||||
inside the module's definition and using it as normal.
|
||||
|
||||
There is a convenience function `mod.pkg` in the mod binding for a module. That imports the
|
||||
package/file that the module was declared in. This binding is only present if the module
|
||||
was declared in a file. Modules created as part of an eval will not have it.
|
||||
Modules have ia recursive reference to the current module `mod.this` that can
|
||||
be used for recursive modules.
|
||||
|
||||
```
|
||||
let recursive = module {
|
||||
counter=1,
|
||||
stop=10,
|
||||
} => (result) {
|
||||
// import our enclosing file again. Careful since calling this function
|
||||
// means that if you instantiate this module in the same file it is
|
||||
// declared in you will trigger an import cycle error.
|
||||
let pkg = mod.pkg();
|
||||
|
||||
let result = select mod.counter != mod.stop, {
|
||||
true = [mod.start] + pkg.recursive{counter=mod.counter+1},
|
||||
true = [mod.start] + mod.this{counter=mod.counter+1},
|
||||
false = [mod.start],
|
||||
};
|
||||
};
|
||||
@ -634,6 +628,24 @@ let recursive = module {
|
||||
recursive{} == [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
||||
```
|
||||
|
||||
There is also a convenience function `mod.pkg` in the mod binding for a module.
|
||||
That imports the package/file the module was declared in. This binding is only
|
||||
present if the module was declared in a file. Modules created as part of an
|
||||
eval will not have it.
|
||||
|
||||
```
|
||||
let self_importer = module {
|
||||
item=NULL,
|
||||
} => () {
|
||||
let pkg = mod.pkg();
|
||||
|
||||
let result = pkg.recursive{};
|
||||
};
|
||||
|
||||
self_importer{} == [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
||||
```
|
||||
|
||||
|
||||
Fail Expression
|
||||
---------------
|
||||
|
||||
|
@ -109,4 +109,16 @@ assert t.ok{
|
||||
assert t.equal{
|
||||
left = export_module_tuple{foo="bar"},
|
||||
right = {quux = "bar"},
|
||||
};
|
||||
|
||||
let recursive_module = module {start=1, end=10} => (result) {
|
||||
let this = mod.this;
|
||||
let result = select mod.start != mod.end, mod.start, {
|
||||
true = this{start=mod.start + 1, end=mod.end},
|
||||
};
|
||||
};
|
||||
|
||||
assert t.equal{
|
||||
left = recursive_module{},
|
||||
right = 10,
|
||||
};
|
@ -1159,7 +1159,7 @@ impl<'a> FileBuilder<'a> {
|
||||
mod_def: &ModuleDef,
|
||||
scope: &Scope,
|
||||
) -> Result<Rc<Val>, Box<dyn Error>> {
|
||||
let maybe_tpl = mod_def.clone().arg_tuple.unwrap().clone();
|
||||
let maybe_tpl = mod_def.arg_tuple.as_ref().unwrap().clone();
|
||||
if let &Val::Tuple(ref src_fields) = maybe_tpl.as_ref() {
|
||||
// 1. First we create a builder.
|
||||
let mut b = self.clone_builder();
|
||||
@ -1188,6 +1188,10 @@ impl<'a> FileBuilder<'a> {
|
||||
}),
|
||||
));
|
||||
}
|
||||
overrides.push((
|
||||
Token::new("this", TokenType::BAREWORD, def.pos.clone()),
|
||||
Expression::Module(mod_def.clone()),
|
||||
));
|
||||
overrides.extend(def.fields.iter().cloned());
|
||||
let mod_args = self.copy_fields_from_base(src_fields, &overrides, &child_scope)?;
|
||||
// put our copied parameters tuple in our builder under the mod key.
|
||||
@ -1327,7 +1331,12 @@ impl<'a> FileBuilder<'a> {
|
||||
// First we rewrite the imports to be absolute paths.
|
||||
def.imports_to_absolute(root);
|
||||
// Then we create our tuple default.
|
||||
def.arg_tuple = Some(self.eval_tuple(&def.arg_set, scope)?);
|
||||
if def.arg_tuple.is_none() {
|
||||
// NOTE: This is an ugly hack to enable recursive references to work
|
||||
// in eval_module_copy. We should really fix out DataModel to properly
|
||||
// handle this but for now this should work.
|
||||
def.arg_tuple = Some(self.eval_tuple(&def.arg_set, scope)?);
|
||||
}
|
||||
// Then we construct a new Val::Module
|
||||
Ok(Rc::new(Val::Module(def)))
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user