FEATURE: out expression for modules.

issue #35
This commit is contained in:
Jeremy Wall 2019-02-21 19:39:30 -06:00
parent 0e5751c2b0
commit d9b2c0cb00
4 changed files with 125 additions and 52 deletions

View File

@ -1,3 +1,5 @@
let t = import "std/testing.ucg".asserts{};
let test_empty_mod = module { let test_empty_mod = module {
} => { } => {
}; };
@ -67,4 +69,44 @@ assert {
assert { assert {
ok = embedded_mod{dep_value="Some"}.embedded_def{}.value == "None", ok = embedded_mod{dep_value="Some"}.embedded_def{}.value == "None",
desc = "embedded_mod{dep_value=\"Some\"}.embedded_def{}.value == \"None\"", desc = "embedded_mod{dep_value=\"Some\"}.embedded_def{}.value == \"None\"",
};
let export_module = module {
foo = "",
} => (result) {
let foo = mod.foo;
let result = {
out_foo = foo,
};
};
assert t.ok{
test = out_foo in export_module{foo="bar"},
desc = "out_foo is in our output tuple",
};
assert t.equal{
left = export_module{foo="bar"},
right = {out_foo = "bar"},
};
let export_module_tuple = module {
foo = "",
} => ({quux = result.out_foo}) {
let foo = mod.foo;
let result = {
out_foo = foo,
};
};
assert t.ok{
test = quux in export_module_tuple{foo="bar"},
desc = "quux is in our output tuple",
};
assert t.equal{
left = export_module_tuple{foo="bar"},
right = {quux = "bar"},
}; };

View File

@ -535,6 +535,7 @@ pub struct ModuleDef {
pub scope: Option<Scope>, pub scope: Option<Scope>,
pub pos: Position, pub pos: Position,
pub arg_set: FieldList, pub arg_set: FieldList,
pub out_expr: Option<Box<Expression>>,
pub arg_tuple: Option<Rc<Val>>, pub arg_tuple: Option<Rc<Val>>,
pub statements: Vec<Statement>, pub statements: Vec<Statement>,
} }
@ -545,11 +546,16 @@ impl ModuleDef {
scope: None, scope: None,
pos: pos.into(), pos: pos.into(),
arg_set: arg_set, arg_set: arg_set,
out_expr: None,
arg_tuple: None, arg_tuple: None,
statements: stmts, statements: stmts,
} }
} }
pub fn set_out_expr(&mut self, expr: Expression) {
self.out_expr = Some(Box::new(expr));
}
pub fn imports_to_absolute(&mut self, base: PathBuf) { pub fn imports_to_absolute(&mut self, base: PathBuf) {
let rewrite_import = |e: &mut Expression| { let rewrite_import = |e: &mut Expression| {
if let Expression::Include(ref mut def) = e { if let Expression::Include(ref mut def) = e {

View File

@ -1134,6 +1134,68 @@ impl<'a> FileBuilder<'a> {
))); )));
} }
fn eval_module_copy(
&self,
def: &CopyDef,
mod_def: &ModuleDef,
scope: &Scope,
) -> Result<Rc<Val>, Box<dyn Error>> {
let maybe_tpl = mod_def.clone().arg_tuple.unwrap().clone();
if let &Val::Tuple(ref src_fields) = maybe_tpl.as_ref() {
// 1. First we create a builder.
// TODO(jwall): This file should optionally come from the module def itself.
let mut b = self.clone_builder();
b.is_module = true;
// 2. We construct an argument tuple by copying from the defs
// argset.
// 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)?;
// 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));
match b.scope.build_output.entry(mod_key) {
Entry::Occupied(e) => {
return Err(error::BuildError::with_pos(
format!(
"Binding \
for {:?} already \
exists in module",
e.key(),
),
error::ErrorType::DuplicateBinding,
mod_def.pos.clone(),
)
.to_boxed());
}
Entry::Vacant(e) => {
e.insert(mod_args.clone());
}
}
// 4. Evaluate all the statements using the builder.
b.eval_stmts(&mod_def.statements)?;
if let Some(ref expr) = mod_def.out_expr {
// 5. Produce the out expression in the context of the statements
// we evaluated previously.
return b.eval_expr(expr, &b.scope);
} else {
// 5. Take all of the bindings in the module and construct a new
// tuple using them.
return Ok(b.get_outputs_as_val());
}
} else {
return Err(error::BuildError::with_pos(
format!(
"Weird value stored in our module parameters slot {:?}",
mod_def.arg_tuple
),
error::ErrorType::TypeFail,
def.selector.pos().clone(),
)
.to_boxed());
}
}
fn eval_copy(&self, def: &CopyDef, scope: &Scope) -> Result<Rc<Val>, Box<dyn Error>> { fn eval_copy(&self, def: &CopyDef, scope: &Scope) -> Result<Rc<Val>, Box<dyn Error>> {
let v = self.eval_value(&def.selector, scope)?; let v = self.eval_value(&def.selector, scope)?;
if let &Val::Tuple(ref src_fields) = v.as_ref() { if let &Val::Tuple(ref src_fields) = v.as_ref() {
@ -1141,55 +1203,7 @@ impl<'a> FileBuilder<'a> {
return self.copy_from_base(&src_fields, &def.fields, &child_scope); return self.copy_from_base(&src_fields, &def.fields, &child_scope);
} }
if let &Val::Module(ref mod_def) = v.as_ref() { if let &Val::Module(ref mod_def) = v.as_ref() {
let maybe_tpl = mod_def.clone().arg_tuple.unwrap().clone(); return self.eval_module_copy(def, mod_def, scope);
if let &Val::Tuple(ref src_fields) = maybe_tpl.as_ref() {
// 1. First we create a builder.
// TODO(jwall): This file should optionally come from the module def itself.
let mut b = self.clone_builder();
b.is_module = true;
// 2. We construct an argument tuple by copying from the defs
// argset.
// 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)?;
// 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));
match b.scope.build_output.entry(mod_key) {
Entry::Occupied(e) => {
return Err(error::BuildError::with_pos(
format!(
"Binding \
for {:?} already \
exists in module",
e.key(),
),
error::ErrorType::DuplicateBinding,
mod_def.pos.clone(),
)
.to_boxed());
}
Entry::Vacant(e) => {
e.insert(mod_args.clone());
}
}
// 4. Evaluate all the statements using the builder.
b.eval_stmts(&mod_def.statements)?;
// 5. Take all of the bindings in the module and construct a new
// tuple using them.
return Ok(b.get_outputs_as_val());
} else {
return Err(error::BuildError::with_pos(
format!(
"Weird value stored in our module parameters slot {:?}",
mod_def.arg_tuple
),
error::ErrorType::TypeFail,
def.selector.pos().clone(),
)
.to_boxed());
}
} }
Err(error::BuildError::with_pos( Err(error::BuildError::with_pos(
format!("Expected Tuple or Module but got ({})", v), format!("Expected Tuple or Module but got ({})", v),

View File

@ -379,17 +379,28 @@ fn module_expression(input: SliceIter<Token>) -> Result<SliceIter<Token>, Expres
_ => optional!(punct!(",")), _ => optional!(punct!(",")),
_ => must!(punct!("}")), _ => must!(punct!("}")),
_ => must!(punct!("=>")), _ => must!(punct!("=>")),
out_expr => optional!(
do_each!(
_ => punct!("("),
expr => must!(expression),
_ => must!(punct!(")")),
(expr)
)
),
_ => must!(punct!("{")), _ => must!(punct!("{")),
stmt_list => trace_parse!(repeat!(statement)), stmt_list => trace_parse!(repeat!(statement)),
_ => must!(punct!("}")), _ => must!(punct!("}")),
(pos, arglist, stmt_list) (pos, arglist, out_expr, stmt_list)
); );
match parsed { match parsed {
Result::Abort(e) => Result::Abort(e), Result::Abort(e) => Result::Abort(e),
Result::Fail(e) => Result::Fail(e), Result::Fail(e) => Result::Fail(e),
Result::Incomplete(offset) => Result::Incomplete(offset), Result::Incomplete(offset) => Result::Incomplete(offset),
Result::Complete(rest, (pos, arglist, stmt_list)) => { Result::Complete(rest, (pos, arglist, out_expr, stmt_list)) => {
let def = ModuleDef::new(arglist.unwrap_or_else(|| Vec::new()), stmt_list, pos); let mut def = ModuleDef::new(arglist.unwrap_or_else(|| Vec::new()), stmt_list, pos);
if let Some(expr) = out_expr {
def.set_out_expr(expr);
}
//eprintln!( //eprintln!(
// "module def at: {:?} arg_typle len {} stmts len {}", // "module def at: {:?} arg_typle len {} stmts len {}",
// def.pos, // def.pos,