From 0e41a40ab38f43b7574846dcefd0fa69e98f9087 Mon Sep 17 00:00:00 2001 From: Jeremy Wall Date: Tue, 16 Apr 2019 20:09:27 -0500 Subject: [PATCH] FEATURE: Add a function to the mod binding that imports the containing package. --- docsite/site/content/reference/expressions.md | 27 ++++++++++++++++-- src/build/mod.rs | 28 ++++++++++++++++--- std/functional.ucg | 2 +- std/lists.ucg | 6 ++-- std/schema.ucg | 4 +-- std/strings.ucg | 2 +- std/tuples.ucg | 6 ++-- 7 files changed, 59 insertions(+), 16 deletions(-) diff --git a/docsite/site/content/reference/expressions.md b/docsite/site/content/reference/expressions.md index 95f853d..23af70e 100644 --- a/docsite/site/content/reference/expressions.md +++ b/docsite/site/content/reference/expressions.md @@ -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 --------------- diff --git a/src/build/mod.rs b/src/build/mod.rs index 309542f..f1368d0 100644 --- a/src/build/mod.rs +++ b/src/build/mod.rs @@ -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)>, 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); diff --git a/std/functional.ucg b/std/functional.ucg index 94503c0..c7a64f1 100644 --- a/std/functional.ucg +++ b/std/functional.ucg @@ -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)}, diff --git a/std/lists.ucg b/std/lists.ucg index 9f0bba9..74e61fb 100644 --- a/std/lists.ucg +++ b/std/lists.ucg @@ -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}; diff --git a/std/schema.ucg b/std/schema.ucg index 5af7fa8..886ecc4 100644 --- a/std/schema.ucg +++ b/std/schema.ucg @@ -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); diff --git a/std/strings.ucg b/std/strings.ucg index e0f2a90..cdbbcec 100644 --- a/std/strings.ucg +++ b/std/strings.ucg @@ -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, { diff --git a/std/tuples.ucg b/std/tuples.ucg index 1ce43b4..bc1e17e 100644 --- a/std/tuples.ucg +++ b/std/tuples.ucg @@ -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);