diff --git a/integration_tests/tuple_test.ucg b/integration_tests/tuple_test.ucg index 82b00c5..847fd96 100644 --- a/integration_tests/tuple_test.ucg +++ b/integration_tests/tuple_test.ucg @@ -37,4 +37,32 @@ assert | |; assert | nestedtpl.list.3 == 4; +|; + +let nestedcopy = nestedtpl{ + inner = self.inner{ + inner = { + field3 = "three", + }, + field2 = 2, + }, +}; + +let deepnestedcopy = nestedcopy{ + ignoreme = "ignored", + inner = self.inner{ + inner = self.inner{ + field4 = 4, + }, + }, +}; + +assert | + nestedcopy.inner.field2 == 2; +|; +assert | + nestedcopy.inner.inner.field3 == "three"; +|; +assert | + deepnestedcopy.inner.inner.field4 == 4; |; \ No newline at end of file diff --git a/src/build/mod.rs b/src/build/mod.rs index f0a647c..79c67e9 100644 --- a/src/build/mod.rs +++ b/src/build/mod.rs @@ -67,7 +67,7 @@ impl MacroDef { for (i, arg) in args.drain(0..).enumerate() { scope.entry(self.argdefs[i].clone()).or_insert(arg.clone()); } - let b = Builder::new_with_env_and_scope(root, cache, scope, env); + let mut b = Builder::new_with_env_and_scope(root, cache, scope, env); 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 @@ -114,6 +114,7 @@ pub struct Builder<'a> { /// build_output is our built output. build_output: ValueMap, /// last is the result of the last statement. + pub stack: Option>>, pub last: Option>, pub out_lock: Option<(String, Rc)>, } @@ -137,7 +138,7 @@ macro_rules! eval_binary_expr { impl<'a> Builder<'a> { // TOOD(jwall): This needs some unit tests. - fn tuple_to_val(&self, fields: &Vec<(Token, Expression)>) -> Result, Box> { + fn tuple_to_val(&mut self, fields: &Vec<(Token, Expression)>) -> Result, Box> { let mut new_fields = Vec::<(PositionedItem, Rc)>::new(); for &(ref name, ref expr) in fields.iter() { let val = try!(self.eval_expr(expr)); @@ -146,7 +147,7 @@ impl<'a> Builder<'a> { Ok(Rc::new(Val::Tuple(new_fields))) } - fn list_to_val(&self, def: &ListDef) -> Result, Box> { + fn list_to_val(&mut self, def: &ListDef) -> Result, Box> { let mut vals = Vec::new(); for expr in def.elems.iter() { vals.push(try!(self.eval_expr(expr))); @@ -154,7 +155,7 @@ impl<'a> Builder<'a> { Ok(Rc::new(Val::List(vals))) } - fn value_to_val(&self, v: &Value) -> Result, Box> { + fn value_to_val(&mut self, v: &Value) -> Result, Box> { match v { &Value::Empty(_) => Ok(Rc::new(Val::Empty)), &Value::Boolean(ref b) => Ok(Rc::new(Val::Boolean(b.val))), @@ -221,6 +222,7 @@ impl<'a> Builder<'a> { assets: cache, build_output: scope, out_lock: None, + stack: None, last: None, } } @@ -380,6 +382,10 @@ impl<'a> Builder<'a> { if &sym.val == "env" { return Some(self.env.clone()); } + if &sym.val == "self" { + // TODO(jwall): we need to look at the current tuple in the stack. + return self.peek_val(); + } if self.build_output.contains_key(sym) { return Some(self.build_output[sym].clone()); } @@ -451,7 +457,7 @@ impl<'a> Builder<'a> { Ok(()) } - fn lookup_selector(&self, sl: &SelectorList) -> Result, Box> { + fn lookup_selector(&mut self, sl: &SelectorList) -> Result, Box> { let first = try!(self.eval_expr(&sl.head)); // First we ensure that the result is a tuple or a list. let mut stack = VecDeque::new(); @@ -765,7 +771,7 @@ impl<'a> Builder<'a> { ))) } - fn eval_binary(&self, def: &BinaryOpDef) -> Result, Box> { + fn eval_binary(&mut self, def: &BinaryOpDef) -> Result, Box> { let kind = &def.kind; let left = try!(self.eval_expr(&def.left)); let right = try!(self.eval_expr(&def.right)); @@ -777,7 +783,7 @@ impl<'a> Builder<'a> { } } - fn eval_compare(&self, def: &ComparisonDef) -> Result, Box> { + fn eval_compare(&mut self, def: &ComparisonDef) -> Result, Box> { let kind = &def.kind; let left = try!(self.eval_expr(&def.left)); let right = try!(self.eval_expr(&def.right)); @@ -791,9 +797,36 @@ impl<'a> Builder<'a> { } } - fn eval_copy(&self, def: &CopyDef) -> Result, Box> { + 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 eval_copy(&mut self, def: &CopyDef) -> Result, Box> { let v = try!(self.lookup_selector(&def.selector.sel)); - if let Val::Tuple(ref src_fields) = *v { + if let &Val::Tuple(ref src_fields) = v.as_ref() { + self.push_val(v.clone()); let mut m = HashMap::, (i32, Rc)>::new(); // loop through fields and build up a hashmap let mut count = 0; @@ -802,6 +835,7 @@ impl<'a> Builder<'a> { v.insert((count, val.clone())); count += 1; } else { + self.pop_val(); return Err(Box::new(error::BuildError::new( format!( "Duplicate \ @@ -815,6 +849,7 @@ impl<'a> Builder<'a> { } } for &(ref key, ref val) in def.fields.iter() { + // TODO(jwall): Allow the special value self to refer to the base tuple. let expr_result = try!(self.eval_expr(val)); match m.entry(key.into()) { // brand new field here. @@ -829,6 +864,7 @@ impl<'a> Builder<'a> { if src_val.1.type_equal(&expr_result) { 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 {}", @@ -843,6 +879,7 @@ impl<'a> Builder<'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. @@ -868,7 +905,7 @@ impl<'a> Builder<'a> { ))) } - fn eval_format(&self, def: &FormatDef) -> Result, Box> { + fn eval_format(&mut self, def: &FormatDef) -> Result, Box> { let tmpl = &def.template; let args = &def.args; let mut vals = Vec::new(); @@ -880,7 +917,7 @@ impl<'a> Builder<'a> { Ok(Rc::new(Val::Str(try!(formatter.render(&def.pos))))) } - fn eval_call(&self, def: &CallDef) -> Result, Box> { + fn eval_call(&mut self, def: &CallDef) -> Result, Box> { let sel = &def.macroref; let args = &def.arglist; let v = try!(self.lookup_selector(&sel.sel)); @@ -921,7 +958,7 @@ impl<'a> Builder<'a> { } } - fn eval_select(&self, def: &SelectDef) -> Result, Box> { + fn eval_select(&mut self, def: &SelectDef) -> Result, Box> { let target = &def.val; let def_expr = &def.default; let fields = &def.tuple; @@ -951,7 +988,7 @@ impl<'a> Builder<'a> { } } - fn eval_list_op(&self, def: &ListOpDef) -> Result, Box> { + fn eval_list_op(&mut self, def: &ListOpDef) -> Result, Box> { let maybe_list = try!(self.eval_expr(&def.target)); let l = match maybe_list.as_ref() { &Val::List(ref elems) => elems, @@ -1058,7 +1095,7 @@ impl<'a> Builder<'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(&self, expr: &Expression) -> Result, Box> { + pub fn eval_expr(&mut self, expr: &Expression) -> Result, Box> { match expr { &Expression::Simple(ref val) => self.value_to_val(val), &Expression::Binary(ref def) => self.eval_binary(def), diff --git a/src/build/test.rs b/src/build/test.rs index 53f4e1d..b24bedb 100644 --- a/src/build/test.rs +++ b/src/build/test.rs @@ -19,7 +19,7 @@ use std; use std::cell::RefCell; use std::rc::Rc; -fn test_expr_to_val(mut cases: Vec<(Expression, Val)>, b: Builder) { +fn test_expr_to_val(mut cases: Vec<(Expression, Val)>, mut b: Builder) { for tpl in cases.drain(0..) { assert_eq!(b.eval_expr(&tpl.0).unwrap(), Rc::new(tpl.1)); } diff --git a/src/lib.rs b/src/lib.rs index a43f562..33b9d4f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -47,6 +47,7 @@ //! //! The following words are reserved in ucg and can't be used as named bindings. //! +//! * self //! * assert //! * true //! * false @@ -273,6 +274,29 @@ //! }; //! //! ``` +//! When you are copying a tuple you can use the special symbol `self` to refer to the tuple you are copying. +//! This allows you to override fields in nested tuples with an concise syntax. Self always refers +//! to the innermost tuple you are copying. +//! +//! ```ucg +//! let nestedtpl = { +//! field1 = "value1", +//! inner = { +//! field2 = 2 +//! inner = { +//! field3 = "three", +//! }, +//! }, +//! }; +//! +//! let copiedtpl = nestedtpl{ +//! inner = self.inner{ +//! inner = self.inner{ +//! field4 = 4, +//! }, +//! }, +//! }; +//! ``` //! //! #### Conditional data //!