From 2b7c8e65f87ff0f7835b1f2690aea3236234685b Mon Sep 17 00:00:00 2001 From: Jeremy Wall Date: Mon, 31 Dec 2018 17:13:58 -0600 Subject: [PATCH] REFACTOR: Better scope handling. --- src/build/mod.rs | 244 +++++++++++++++++---------------------------- src/build/scope.rs | 100 +++++++++++++++++++ src/build/test.rs | 25 +++-- 3 files changed, 209 insertions(+), 160 deletions(-) create mode 100644 src/build/scope.rs diff --git a/src/build/mod.rs b/src/build/mod.rs index 602f702..c0cc049 100644 --- a/src/build/mod.rs +++ b/src/build/mod.rs @@ -28,6 +28,7 @@ use std::string::ToString; use simple_error; use crate::ast::*; +use crate::build::scope::{Scope, ValueMap}; use crate::error; use crate::format; use crate::iter::OffsetStrIter; @@ -35,6 +36,7 @@ use crate::parse::parse; pub mod assets; pub mod ir; +pub mod scope; pub use self::ir::Val; @@ -67,12 +69,13 @@ impl MacroDef { scope.entry(self.argdefs[i].clone()).or_insert(arg.clone()); } let mut b = parent_builder.clone_builder(root); - b.set_scope(scope); + b.set_build_output(scope); let mut result: Vec<(PositionedItem, Rc)> = Vec::new(); for &(ref key, ref expr) in self.fields.iter() { // We clone the expressions here because this macro may be consumed // multiple times in the future. - let val = b.eval_expr(expr)?; + let scope = b.scope.spawn_child(); + let val = b.eval_expr(expr, &scope)?; result.push((key.into(), val.clone())); } Ok(result) @@ -82,9 +85,6 @@ impl MacroDef { /// The result of a build. type BuildResult = Result<(), Box>; -/// Defines a set of values in a parsed file. -type ValueMap = HashMap, Rc>; - /// AssertCollector collects the results of assertions in the UCG AST. pub struct AssertCollector { pub success: bool, @@ -99,7 +99,7 @@ pub struct FileBuilder<'a> { validate_mode: bool, pub assert_collector: AssertCollector, strict: bool, - env: Rc, + scope: Scope, // NOTE(jwall): We use interior mutability here because we need // our asset cache to be shared by multiple different sub-builders. // We use Rc to handle the reference counting for us and we use @@ -112,11 +112,6 @@ pub struct FileBuilder<'a> { // so multiple imports of the same file don't have to be parsed // multiple times. assets: Rc>, - /// build_output is our built output. - build_output: ValueMap, - /// last is the result of the last statement. - import_stack: Vec, - pub stack: Option>>, pub is_module: bool, pub last: Option>, pub out_lock: Option<(String, Rc)>, @@ -146,37 +141,21 @@ impl<'a> FileBuilder<'a> { import_paths: &'a Vec, cache: Rc>, ) -> Self { - Self::new_with_scope(file, import_paths, cache, HashMap::new()) + let env_vars: Vec<(String, String)> = env::vars().collect(); + let scope = scope::Scope::new(Rc::new(Val::Env(env_vars))); + Self::new_with_scope(file, import_paths, cache, scope) } /// Constructs a new Builder with a provided scope. pub fn new_with_scope>( - root: P, - import_paths: &'a Vec, - cache: Rc>, - scope: ValueMap, - ) -> Self { - let env_vars: Vec<(String, String)> = env::vars().collect(); - Self::new_with_env_and_scope( - root, - import_paths, - cache, - scope, - Rc::new(Val::Env(env_vars)), - ) - } - - pub fn new_with_env_and_scope>( file: P, import_paths: &'a Vec, cache: Rc>, - scope: ValueMap, - env: Rc, + scope: Scope, ) -> Self { let file = file.into(); FileBuilder { // Our import stack is initialized with ourself. - import_stack: vec![file.to_string_lossy().to_string()], file: file, import_path: import_paths, validate_mode: false, @@ -185,12 +164,10 @@ impl<'a> FileBuilder<'a> { summary: String::new(), failures: String::new(), }, - env: env, + scope: scope, strict: true, assets: cache, - build_output: scope, out_lock: None, - stack: None, is_module: false, last: None, } @@ -198,8 +175,6 @@ impl<'a> FileBuilder<'a> { pub fn clone_builder>(&self, file: P) -> Self { FileBuilder { - // Our import stack is initialized with ourself. - import_stack: self.import_stack.clone(), file: file.into(), import_path: self.import_path, validate_mode: false, @@ -208,49 +183,45 @@ impl<'a> FileBuilder<'a> { summary: String::new(), failures: String::new(), }, - env: self.env.clone(), strict: true, assets: self.assets.clone(), - build_output: HashMap::new(), + scope: self.scope.spawn_clean(), out_lock: None, - stack: None, is_module: false, last: None, } } - pub fn set_scope(&mut self, scope: ValueMap) { - self.build_output = scope; + pub fn set_build_output(&mut self, scope: ValueMap) { + self.scope.build_output = scope; } pub fn set_strict(&mut self, to: bool) { self.strict = to; } - pub fn prepend_import_stack(&mut self, imports: &Vec) { - let mut new_stack = self.import_stack.clone(); - new_stack.append(imports.clone().as_mut()); - self.import_stack = new_stack; - } - - fn eval_tuple(&mut self, fields: &Vec<(Token, Expression)>) -> Result, Box> { + fn eval_tuple( + &mut self, + fields: &Vec<(Token, Expression)>, + scope: &Scope, + ) -> Result, Box> { let mut new_fields = Vec::<(PositionedItem, Rc)>::new(); for &(ref name, ref expr) in fields.iter() { - let val = self.eval_expr(expr)?; + let val = self.eval_expr(expr, scope)?; new_fields.push((name.into(), val)); } Ok(Rc::new(Val::Tuple(new_fields))) } - fn eval_list(&mut self, def: &ListDef) -> Result, Box> { + fn eval_list(&mut self, def: &ListDef, scope: &Scope) -> Result, Box> { let mut vals = Vec::new(); for expr in def.elems.iter() { - vals.push(self.eval_expr(expr)?); + vals.push(self.eval_expr(expr, scope)?); } Ok(Rc::new(Val::List(vals))) } - fn eval_value(&mut self, v: &Value) -> Result, Box> { + fn eval_value(&mut self, v: &Value, scope: &Scope) -> Result, Box> { match v { &Value::Empty(_) => Ok(Rc::new(Val::Empty)), &Value::Boolean(ref b) => Ok(Rc::new(Val::Boolean(b.val))), @@ -258,17 +229,18 @@ impl<'a> FileBuilder<'a> { &Value::Float(ref f) => Ok(Rc::new(Val::Float(f.val))), &Value::Str(ref s) => Ok(Rc::new(Val::Str(s.val.to_string()))), &Value::Symbol(ref s) => { - self.lookup_sym(&(s.into())) + scope + .lookup_sym(&(s.into())) .ok_or(Box::new(error::BuildError::new( format!("Unable to find binding {}", s.val,), error::ErrorType::NoSuchSymbol, v.pos().clone(), ))) } - &Value::List(ref def) => self.eval_list(def), - &Value::Tuple(ref tuple) => self.eval_tuple(&tuple.val), + &Value::List(ref def) => self.eval_list(def, scope), + &Value::Tuple(ref tuple) => self.eval_tuple(&tuple.val, scope), &Value::Selector(ref selector_list_node) => { - self.lookup_selector(&selector_list_node.sel) + self.lookup_selector(&selector_list_node.sel, scope) } } } @@ -279,7 +251,7 @@ impl<'a> FileBuilder<'a> { pos: Position::new(0, 0, 0), val: name.to_string(), }; - self.lookup_sym(&key) + self.scope.lookup_sym(&key) } /// Puts the builder in validation mode. @@ -358,7 +330,11 @@ impl<'a> FileBuilder<'a> { } fn detect_import_cycle(&self, path: &str) -> bool { - self.import_stack.iter().find(|p| *p == path).is_some() + self.scope + .import_stack + .iter() + .find(|p| *p == path) + .is_some() } fn eval_import(&mut self, def: &ImportDef) -> Result, Box> { @@ -398,7 +374,7 @@ impl<'a> FileBuilder<'a> { format!( "Import Cycle Detected!!!! {} is already in import stack: {:?}", normalized.to_string_lossy(), - self.import_stack, + self.scope.import_stack, ), error::ErrorType::Unsupported, sym.pos.clone(), @@ -417,21 +393,22 @@ impl<'a> FileBuilder<'a> { } }; let key = sym.into(); - if self.build_output.contains_key(&key) { + if self.scope.build_output.contains_key(&key) { return Err(Box::new(error::BuildError::new( format!("Binding for import name {} already exists", sym.fragment), error::ErrorType::DuplicateBinding, def.path.pos.clone(), ))); } - self.build_output.insert(key, result.clone()); + self.scope.build_output.insert(key, result.clone()); let mut mut_assets_cache = self.assets.borrow_mut(); mut_assets_cache.stash(normalized.clone(), result.clone())?; return Ok(result); } fn eval_let(&mut self, def: &LetDef) -> Result, Box> { - let val = self.eval_expr(&def.value)?; + let child_scope = self.scope.clone(); + let val = self.eval_expr(&def.value, &child_scope)?; let name = &def.name; if Self::check_reserved_word(&name.fragment) { return Err(Box::new(error::BuildError::new( @@ -440,7 +417,7 @@ impl<'a> FileBuilder<'a> { name.pos.clone(), ))); } - match self.build_output.entry(name.into()) { + match self.scope.build_output.entry(name.into()) { Entry::Occupied(e) => { return Err(Box::new(error::BuildError::new( format!( @@ -461,16 +438,17 @@ impl<'a> FileBuilder<'a> { } fn eval_stmt(&mut self, stmt: &Statement) -> Result, Box> { + let child_scope = self.scope.clone(); match stmt { &Statement::Assert(ref expr) => self.build_assert(&expr), &Statement::Let(ref def) => self.eval_let(def), &Statement::Import(ref def) => self.eval_import(def), - &Statement::Expression(ref expr) => self.eval_expr(expr), + &Statement::Expression(ref expr) => self.eval_expr(expr, &child_scope), // Only one output can be used per file. Right now we enforce this by // having a single builder per file. &Statement::Output(ref typ, ref expr) => { if let None = self.out_lock { - let val = self.eval_expr(expr)?; + let val = self.eval_expr(expr, &child_scope)?; self.out_lock = Some((typ.fragment.to_string(), val.clone())); Ok(val) } else { @@ -484,19 +462,6 @@ impl<'a> FileBuilder<'a> { } } - fn lookup_sym(&self, sym: &PositionedItem) -> Option> { - if &sym.val == "env" { - return Some(self.env.clone()); - } - if &sym.val == "self" { - return self.peek_val(); - } - if self.build_output.contains_key(sym) { - return Some(self.build_output[sym].clone()); - } - None - } - fn find_in_fieldlist( target: &str, fs: &Vec<(PositionedItem, Rc)>, @@ -580,8 +545,8 @@ impl<'a> FileBuilder<'a> { Ok(()) } - fn lookup_selector(&mut self, sl: &SelectorList) -> Result, Box> { - let first = self.eval_expr(&sl.head)?; + fn lookup_selector(&mut self, sl: &SelectorList, scope: &Scope) -> Result, Box> { + let first = self.eval_expr(&sl.head, scope)?; // First we ensure that the result is a tuple or a list. let mut stack = VecDeque::new(); match first.as_ref() { @@ -892,10 +857,10 @@ impl<'a> FileBuilder<'a> { ))) } - fn eval_binary(&mut self, def: &BinaryOpDef) -> Result, Box> { + fn eval_binary(&mut self, def: &BinaryOpDef, scope: &Scope) -> Result, Box> { let kind = &def.kind; - let left = self.eval_expr(&def.left)?; - let right = self.eval_expr(&def.right)?; + let left = self.eval_expr(&def.left, scope)?; + let right = self.eval_expr(&def.right, scope)?; match kind { &BinaryExprType::Add => self.add_vals(&def.pos, left, right), &BinaryExprType::Sub => self.subtract_vals(&def.pos, left, right), @@ -904,10 +869,10 @@ impl<'a> FileBuilder<'a> { } } - fn eval_compare(&mut self, def: &ComparisonDef) -> Result, Box> { + fn eval_compare(&mut self, def: &ComparisonDef, scope: &Scope) -> Result, Box> { let kind = &def.kind; - let left = self.eval_expr(&def.left)?; - let right = self.eval_expr(&def.right)?; + let left = self.eval_expr(&def.left, scope)?; + let right = self.eval_expr(&def.right, scope)?; match kind { &CompareType::Equal => self.do_deep_equal(&def.pos, left, right), &CompareType::GT => self.do_gt(&def.pos, left, right), @@ -918,34 +883,9 @@ impl<'a> FileBuilder<'a> { } } - fn push_val(&mut self, tuple: Rc) { - if let Some(ref mut v) = self.stack { - v.push(tuple); - } else { - let mut v = Vec::new(); - v.push(tuple); - self.stack = Some(v); - } - } - - fn pop_val(&mut self) -> Option> { - if let Some(ref mut v) = self.stack { - v.pop() - } else { - None - } - } - - fn peek_val(&self) -> Option> { - if let Some(ref v) = self.stack { - v.first().map(|v| v.clone()) - } else { - None - } - } - fn get_outputs_as_val(&mut self) -> Rc { - let fields: Vec<(PositionedItem, Rc)> = self.build_output.drain().collect(); + let fields: Vec<(PositionedItem, Rc)> = + self.scope.build_output.drain().collect(); Rc::new(Val::Tuple(fields)) } @@ -953,6 +893,7 @@ impl<'a> FileBuilder<'a> { &mut self, src_fields: &Vec<(PositionedItem, Rc)>, overrides: &Vec<(Token, Expression)>, + scope: &Scope, ) -> Result, Box> { let mut m = HashMap::, (i32, Rc)>::new(); // loop through fields and build up a hashmap @@ -962,7 +903,6 @@ impl<'a> FileBuilder<'a> { v.insert((count, val.clone())); count += 1; } else { - self.pop_val(); return Err(Box::new(error::BuildError::new( format!( "Duplicate \ @@ -976,7 +916,7 @@ impl<'a> FileBuilder<'a> { } } for &(ref key, ref val) in overrides.iter() { - let expr_result = self.eval_expr(val)?; + let expr_result = self.eval_expr(val, scope)?; match m.entry(key.into()) { // brand new field here. Entry::Vacant(v) => { @@ -993,7 +933,6 @@ impl<'a> FileBuilder<'a> { { v.insert((src_val.0, expr_result)); } else { - self.pop_val(); return Err(Box::new(error::BuildError::new( format!( "Expected type {} for field {} but got {}", @@ -1008,7 +947,6 @@ impl<'a> FileBuilder<'a> { } }; } - self.pop_val(); let mut new_fields: Vec<(PositionedItem, (i32, Rc))> = m.drain().collect(); // We want to maintain our order for the fields to make comparing tuples // easier in later code. So we sort by the field order before constructing a new tuple. @@ -1029,11 +967,12 @@ impl<'a> FileBuilder<'a> { ))); } - fn eval_copy(&mut self, def: &CopyDef) -> Result, Box> { - let v = self.lookup_selector(&def.selector.sel)?; + fn eval_copy(&mut self, def: &CopyDef, scope: &Scope) -> Result, Box> { + let v = self.lookup_selector(&def.selector.sel, scope)?; if let &Val::Tuple(ref src_fields) = v.as_ref() { - self.push_val(v.clone()); - return self.copy_from_base(&src_fields, &def.fields); + let mut child_scope = scope.spawn_child(); + child_scope.set_curr_val(v.clone()); + return self.copy_from_base(&src_fields, &def.fields, &child_scope); } if let &Val::Module(ref mod_def) = v.as_ref() { let maybe_tpl = mod_def.clone().arg_tuple.unwrap().clone(); @@ -1045,12 +984,13 @@ impl<'a> FileBuilder<'a> { // argset. // Push our base tuple on the stack so the copy can use // self to reference it. - b.push_val(maybe_tpl.clone()); - let mod_args = self.copy_from_base(src_fields, &def.fields)?; + let mut child_scope = scope.spawn_child(); + child_scope.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.build_output.entry(mod_key) { + match b.scope.build_output.entry(mod_key) { Entry::Occupied(e) => { return Err(Box::new(error::BuildError::new( format!( @@ -1090,27 +1030,27 @@ impl<'a> FileBuilder<'a> { ))) } - fn eval_format(&mut self, def: &FormatDef) -> Result, Box> { + fn eval_format(&mut self, def: &FormatDef, scope: &Scope) -> Result, Box> { let tmpl = &def.template; let args = &def.args; let mut vals = Vec::new(); for v in args.iter() { - let rcv = self.eval_expr(v)?; + let rcv = self.eval_expr(v, scope)?; vals.push(rcv.deref().clone()); } let formatter = format::Formatter::new(tmpl.clone(), vals); Ok(Rc::new(Val::Str(formatter.render(&def.pos)?))) } - fn eval_call(&mut self, def: &CallDef) -> Result, Box> { + fn eval_call(&mut self, def: &CallDef, scope: &Scope) -> Result, Box> { let sel = &def.macroref; let args = &def.arglist; - let v = self.lookup_selector(&sel.sel)?; + let v = self.lookup_selector(&sel.sel, scope)?; if let &Val::Macro(ref m) = v.deref() { // Congratulations this is actually a macro. let mut argvals: Vec> = Vec::new(); for arg in args.iter() { - argvals.push(self.eval_expr(arg)?); + argvals.push(self.eval_expr(arg, scope)?); } let fields = m.eval(self.file.clone(), self, argvals)?; return Ok(Rc::new(Val::Tuple(fields))); @@ -1148,46 +1088,46 @@ impl<'a> FileBuilder<'a> { }; } - fn eval_module_def(&mut self, def: &ModuleDef) -> Result, Box> { + fn eval_module_def(&mut self, def: &ModuleDef, scope: &Scope) -> Result, Box> { let root = self.file_dir(); // Always work on a copy. The original should not be modified. let mut def = def.clone(); // 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)?); + def.arg_tuple = Some(self.eval_tuple(&def.arg_set, scope)?); // Then we construct a new Val::Module Ok(Rc::new(Val::Module(def))) } - fn eval_select(&mut self, def: &SelectDef) -> Result, Box> { + fn eval_select(&mut self, def: &SelectDef, scope: &Scope) -> Result, Box> { let target = &def.val; let def_expr = &def.default; let fields = &def.tuple; // First resolve the target expression. - let v = self.eval_expr(target)?; + let v = self.eval_expr(target, scope)?; // Second ensure that the expression resolves to a string. if let &Val::Str(ref name) = v.deref() { // Third find the field with that name in the tuple. for &(ref fname, ref val_expr) in fields.iter() { if &fname.fragment == name { // Fourth return the result of evaluating that field. - return self.eval_expr(val_expr); + return self.eval_expr(val_expr, scope); } } // Otherwise return the default. - return self.eval_expr(def_expr); + return self.eval_expr(def_expr, scope); } else if let &Val::Boolean(b) = v.deref() { for &(ref fname, ref val_expr) in fields.iter() { if &fname.fragment == "true" && b { // Fourth return the result of evaluating that field. - return self.eval_expr(val_expr); + return self.eval_expr(val_expr, scope); } else if &fname.fragment == "false" && !b { - return self.eval_expr(val_expr); + return self.eval_expr(val_expr, scope); } } // Otherwise return the default. - return self.eval_expr(def_expr); + return self.eval_expr(def_expr, scope); } else { return Err(Box::new(error::BuildError::new( format!( @@ -1201,8 +1141,8 @@ impl<'a> FileBuilder<'a> { } } - fn eval_list_op(&mut self, def: &ListOpDef) -> Result, Box> { - let maybe_list = self.eval_expr(&def.target)?; + fn eval_list_op(&mut self, def: &ListOpDef, scope: &Scope) -> Result, Box> { + let maybe_list = self.eval_expr(&def.target, scope)?; let l = match maybe_list.as_ref() { &Val::List(ref elems) => elems, other => { @@ -1214,7 +1154,7 @@ impl<'a> FileBuilder<'a> { } }; let mac = &def.mac; - if let &Val::Macro(ref macdef) = self.lookup_selector(&mac.sel)?.as_ref() { + if let &Val::Macro(ref macdef) = self.lookup_selector(&mac.sel, scope)?.as_ref() { let mut out = Vec::new(); for item in l.iter() { let argvals = vec![item.clone()]; @@ -1303,19 +1243,19 @@ impl<'a> FileBuilder<'a> { // Evals a single Expression in the context of a running Builder. // It does not mutate the builders collected state at all. - pub fn eval_expr(&mut self, expr: &Expression) -> Result, Box> { + pub fn eval_expr(&mut self, expr: &Expression, scope: &Scope) -> Result, Box> { match expr { - &Expression::Simple(ref val) => self.eval_value(val), - &Expression::Binary(ref def) => self.eval_binary(def), - &Expression::Compare(ref def) => self.eval_compare(def), - &Expression::Copy(ref def) => self.eval_copy(def), - &Expression::Grouped(ref expr) => self.eval_expr(expr), - &Expression::Format(ref def) => self.eval_format(def), - &Expression::Call(ref def) => self.eval_call(def), + &Expression::Simple(ref val) => self.eval_value(val, scope), + &Expression::Binary(ref def) => self.eval_binary(def, scope), + &Expression::Compare(ref def) => self.eval_compare(def, scope), + &Expression::Copy(ref def) => self.eval_copy(def, scope), + &Expression::Grouped(ref expr) => self.eval_expr(expr, scope), + &Expression::Format(ref def) => self.eval_format(def, scope), + &Expression::Call(ref def) => self.eval_call(def, scope), &Expression::Macro(ref def) => self.eval_macro_def(def), - &Expression::Module(ref def) => self.eval_module_def(def), - &Expression::Select(ref def) => self.eval_select(def), - &Expression::ListOp(ref def) => self.eval_list_op(def), + &Expression::Module(ref def) => self.eval_module_def(def, scope), + &Expression::Select(ref def) => self.eval_select(def, scope), + &Expression::ListOp(ref def) => self.eval_list_op(def, scope), } } } diff --git a/src/build/scope.rs b/src/build/scope.rs new file mode 100644 index 0000000..5c531ed --- /dev/null +++ b/src/build/scope.rs @@ -0,0 +1,100 @@ +use std::clone::Clone; +use std::collections::HashMap; +use std::convert::Into; +use std::rc::Rc; + +use crate::ast::PositionedItem; +use crate::build::ir::Val; + +/// Defines a set of values in a parsed file. +pub type ValueMap = HashMap, Rc>; + +/// Defines a scope for execution in ucg. +/// +/// Scopes in ucg are defined by the currently executing file and +/// the complex data types in that file. (Tuple, List, Modules, and the +/// left operands for dot selectors). +/// +/// UCG Scopes do not descend up into their parent scopes so we do not maintain a stack +/// for those. +#[derive(Clone)] +pub struct Scope { + pub import_stack: Vec, + pub env: Rc, + pub curr_val: Option>, + pub build_output: ValueMap, +} + +impl Scope { + // Construct a new scope with environment variables. + pub fn new(env: Rc) -> Self { + Self { + import_stack: Vec::new(), + env: env, + // CurrVal represents the currently processing value. + // (eg: Tuple, List. left side of a dot selection.) + curr_val: None, + build_output: HashMap::new(), + } + } + + /// Spawn a child scope based on the current scope but without the current + /// val set. + pub fn spawn_child(&self) -> Self { + Self { + import_stack: self.import_stack.clone(), + env: self.env.clone(), + // Children start with no current val + curr_val: None, + build_output: self.build_output.clone(), + } + } + + pub fn spawn_clean(&self) -> Self { + Self { + import_stack: self.import_stack.clone(), + env: self.env.clone(), + // Children start with no current val + curr_val: None, + build_output: HashMap::new(), + } + } + + /// Push an import onto the import stack. + pub fn push_import>(&mut self, path: S) { + self.import_stack.push(path.into()); + } + + pub fn prepend_import_stack(&mut self, imports: &Vec) { + let mut new_stack = self.import_stack.clone(); + new_stack.append(imports.clone().as_mut()); + self.import_stack = new_stack; + } + + /// Set the current value for our execution context. + pub fn set_curr_val(&mut self, val: Rc) { + self.curr_val = Some(val); + } + + /// Lookup a symbol in the current execution context. + /// + /// The lookup rules are simple. + /// + /// * `env` is always an environment variable lookup. + /// * `self` is always the current value. This symbol is only + /// valid when the current value is a tuple. + /// * everything else is looked up in the currently accumulated build output + /// for this execution context. + pub fn lookup_sym(&self, sym: &PositionedItem) -> Option> { + if &sym.val == "env" { + return Some(self.env.clone()); + } + if &sym.val == "self" { + return self.curr_val.clone(); + } + if self.build_output.contains_key(sym) { + return Some(self.build_output[sym].clone()); + } + None + } +} diff --git a/src/build/test.rs b/src/build/test.rs index 9aabfd4..c3915ca 100644 --- a/src/build/test.rs +++ b/src/build/test.rs @@ -21,7 +21,10 @@ use std::rc::Rc; fn test_expr_to_val(mut cases: Vec<(Expression, Val)>, mut b: FileBuilder) { for tpl in cases.drain(0..) { - assert_eq!(b.eval_expr(&tpl.0).unwrap(), Rc::new(tpl.1)); + assert_eq!( + b.eval_expr(&tpl.0, &b.scope.spawn_child()).unwrap(), + Rc::new(tpl.1) + ); } } @@ -133,14 +136,15 @@ fn test_eval_simple_lookup_error() { let i_paths = Vec::new(); let cache = Rc::new(RefCell::new(MemoryCache::new())); let mut b = FileBuilder::new(std::env::current_dir().unwrap(), &i_paths, cache); - b.build_output + b.scope + .build_output .entry(value_node!("var1".to_string(), Position::new(1, 0, 0))) .or_insert(Rc::new(Val::Int(1))); let expr = Expression::Simple(Value::Symbol(value_node!( "var".to_string(), Position::new(1, 1, 1) ))); - assert!(b.eval_expr(&expr).is_err()); + assert!(b.eval_expr(&expr, &b.scope.spawn_child()).is_err()); } // Include nested for each. @@ -172,7 +176,8 @@ fn test_expr_copy_not_a_tuple() { let i_paths = Vec::new(); let cache = Rc::new(RefCell::new(MemoryCache::new())); let mut b = FileBuilder::new(std::env::current_dir().unwrap(), &i_paths, cache); - b.build_output + b.scope + .build_output .entry(value_node!("tpl1".to_string(), Position::new(1, 0, 0))) .or_insert(Rc::new(Val::Int(1))); test_expr_to_val( @@ -197,7 +202,8 @@ fn test_expr_copy_field_type_error() { let i_paths = Vec::new(); let cache = Rc::new(RefCell::new(MemoryCache::new())); let mut b = FileBuilder::new(std::env::current_dir().unwrap(), &i_paths, cache); - b.build_output + b.scope + .build_output .entry(value_node!("tpl1".to_string(), Position::new(1, 0, 0))) .or_insert(Rc::new(Val::Tuple(vec![( value_node!("fld1".to_string(), Position::new(1, 0, 0)), @@ -234,10 +240,12 @@ fn test_macro_hermetic() { let i_paths = Vec::new(); let cache = Rc::new(RefCell::new(MemoryCache::new())); let mut b = FileBuilder::new(std::env::current_dir().unwrap(), &i_paths, cache); - b.build_output + b.scope + .build_output .entry(value_node!("arg1".to_string(), Position::new(1, 0, 0))) .or_insert(Rc::new(Val::Str("bar".to_string()))); - b.build_output + b.scope + .build_output .entry(value_node!("tstmac".to_string(), Position::new(1, 0, 0))) .or_insert(Rc::new(Val::Macro(MacroDef { argdefs: vec![value_node!("arg2".to_string(), Position::new(1, 0, 0))], @@ -278,7 +286,8 @@ fn test_select_expr_not_a_string() { let i_paths = Vec::new(); let cache = Rc::new(RefCell::new(MemoryCache::new())); let mut b = FileBuilder::new(std::env::current_dir().unwrap(), &i_paths, cache); - b.build_output + b.scope + .build_output .entry(value_node!("foo".to_string(), Position::new(1, 0, 0))) .or_insert(Rc::new(Val::Int(4))); test_expr_to_val(