FEATURE: Add a function to the mod binding that imports the containing package.

This commit is contained in:
Jeremy Wall 2019-04-16 20:09:27 -05:00
parent 4afac26497
commit 0e41a40ab3
7 changed files with 59 additions and 16 deletions

View File

@ -576,9 +576,9 @@ let embedded_with_params = top_mod{deep_value = "Some"};
embedded_with_params.embedded.value == "Some";
```
### Out Expressions
### Return Expressions
If there is an out expression then the module will only export the result of
If there is a return expression then the module will only export the result of
that expression. The out expression is computed after the last statement in the
module has been evaluated.
@ -611,6 +611,29 @@ 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.
```
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},
false = [mod.start],
};
};
recursive{} == [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
```
Fail Expression
---------------

View File

@ -1071,7 +1071,7 @@ impl<'a> FileBuilder<'a> {
Rc::new(Val::Tuple(fields))
}
fn copy_from_base(
fn copy_fields_from_base(
&self,
src_fields: &Vec<(String, Rc<Val>)>,
overrides: &Vec<(Token, Expression)>,
@ -1166,9 +1166,29 @@ impl<'a> FileBuilder<'a> {
// Push our base tuple on the stack so the copy can use
// self to reference it.
let child_scope = scope.spawn_child().set_curr_val(maybe_tpl.clone());
let mod_args = self.copy_from_base(src_fields, &def.fields, &child_scope)?;
let mut overrides = Vec::new();
if let Some(ref path) = mod_def.pos.file {
overrides.push((
Token::new("pkg", TokenType::BAREWORD, def.pos.clone()),
Expression::Func(FuncDef {
scope: None,
argdefs: Vec::new(),
fields: Box::new(Expression::Import(ImportDef {
pos: def.pos.clone(),
path: Token::new(
path.to_string_lossy().to_string(),
TokenType::QUOTED,
def.pos.clone(),
),
})),
pos: def.pos.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.
let mod_key = PositionedItem::new_with_pos(String::from("mod"), Position::new(0, 0, 0));
let mod_key = PositionedItem::new_with_pos(String::from("mod"), def.pos.clone());
match b.scope.build_output.entry(mod_key) {
Entry::Occupied(e) => {
return Err(error::BuildError::with_pos(
@ -1215,7 +1235,7 @@ impl<'a> FileBuilder<'a> {
let v = self.eval_value(&def.selector, scope)?;
if let &Val::Tuple(ref src_fields) = v.as_ref() {
let child_scope = scope.spawn_child().set_curr_val(v.clone());
return self.copy_from_base(&src_fields, &def.fields, &child_scope);
return self.copy_fields_from_base(&src_fields, &def.fields, &child_scope);
}
if let &Val::Module(ref mod_def) = v.as_ref() {
return self.eval_module_copy(def, mod_def, scope);

View File

@ -16,7 +16,7 @@
let maybe = module{
val = NULL,
} => ({do=do, is_null=is_null, or=or, unwrap=unwrap, expect=expect}) {
let maybe = import "std/functional.ucg".maybe;
let maybe = mod.pkg().maybe;
let do = func (op) => select (mod.val != NULL), maybe{val=NULL}, {
true = maybe{val=op(mod.val)},

View File

@ -77,7 +77,7 @@ let slice = module {
end = NULL,
list = NULL,
} => (result) {
let list = import "std/lists.ucg";
let list = mod.pkg();
let list_len = list.len(mod.list);
@ -111,7 +111,7 @@ let zip = module{
list1 = NULL,
list2 = NULL,
} => (result) {
let len = import "std/lists.ucg".len;
let len = mod.pkg().len;
// Compute the length of each list.
let len1 = len(mod.list1);
@ -169,7 +169,7 @@ let ops = module{
reverse=reverse,
list=list,
}) {
let super = import "std/lists.ucg";
let super = mod.pkg();
let list = mod.list;
let len = super.len(mod.list);
let str_join = func(sep) => super.str_join{list=mod.list, sep=sep};

View File

@ -32,7 +32,7 @@ let any = module {
// The set of allowed type shapes it can be.
types=[],
} => (result) {
let schema = import "std/schema.ucg";
let schema = mod.pkg();
let reducer = func (acc, t) => acc{
ok = acc.ok || (schema.shaped{val=acc.val, shape=t}),
@ -66,7 +66,7 @@ let shaped = module {
// shape it is compared with.
partial = true,
} => (result) {
let schema = import "std/schema.ucg";
let schema = mod.pkg();
let simple_handler = func (val, shape) => val is schema.base_type_of(shape);

View File

@ -65,7 +65,7 @@ let ops = module {
start = 0,
end = len,
} => (result) {
let filepkg = import "std/strings.ucg";
let filepkg = mod.pkg();
let reducer = func(acc, char) => acc{
counter = acc.counter + 1,
str = select ((acc.counter >= mod.start) && (acc.counter <= mod.end)), acc.str, {

View File

@ -48,7 +48,7 @@ let ops = module{
} => ({fields=fields, values=values, iter=iter}) {
(mod.tpl != NULL) || fail "tpl must not be null";
let super = import "std/tuples.ucg";
let super = mod.pkg();
let fields = func() => super.fields{tpl=mod.tpl};
let values = func() => super.values{tpl=mod.tpl};
@ -71,7 +71,7 @@ let has_fields = module{
tpl = NULL,
fields = [],
} => (result) {
let lib = import "std/tuples.ucg";
let lib = mod.pkg();
// First we check that mod.tpl is a tuple.
lib.assert_tuple(mod.tpl);
@ -86,7 +86,7 @@ let field_type = module{
field = NULL,
type = NULL,
} => (result) {
let lib = import "std/tuples.ucg";
let lib = mod.pkg();
// First we check that mod.tpl is a tuple.
lib.assert_tuple(mod.tpl);