mirror of
https://github.com/zaphar/ucg.git
synced 2025-07-22 18:19:54 -04:00
parent
0bbb417f69
commit
bdf56f6d3c
@ -37,4 +37,32 @@ assert |
|
|||||||
|;
|
|;
|
||||||
assert |
|
assert |
|
||||||
nestedtpl.list.3 == 4;
|
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;
|
||||||
|;
|
|;
|
@ -67,7 +67,7 @@ impl MacroDef {
|
|||||||
for (i, arg) in args.drain(0..).enumerate() {
|
for (i, arg) in args.drain(0..).enumerate() {
|
||||||
scope.entry(self.argdefs[i].clone()).or_insert(arg.clone());
|
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<String>, Rc<Val>)> = Vec::new();
|
let mut result: Vec<(PositionedItem<String>, Rc<Val>)> = Vec::new();
|
||||||
for &(ref key, ref expr) in self.fields.iter() {
|
for &(ref key, ref expr) in self.fields.iter() {
|
||||||
// We clone the expressions here because this macro may be consumed
|
// 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 is our built output.
|
||||||
build_output: ValueMap,
|
build_output: ValueMap,
|
||||||
/// last is the result of the last statement.
|
/// last is the result of the last statement.
|
||||||
|
pub stack: Option<Vec<Rc<Val>>>,
|
||||||
pub last: Option<Rc<Val>>,
|
pub last: Option<Rc<Val>>,
|
||||||
pub out_lock: Option<(String, Rc<Val>)>,
|
pub out_lock: Option<(String, Rc<Val>)>,
|
||||||
}
|
}
|
||||||
@ -137,7 +138,7 @@ macro_rules! eval_binary_expr {
|
|||||||
|
|
||||||
impl<'a> Builder<'a> {
|
impl<'a> Builder<'a> {
|
||||||
// TOOD(jwall): This needs some unit tests.
|
// TOOD(jwall): This needs some unit tests.
|
||||||
fn tuple_to_val(&self, fields: &Vec<(Token, Expression)>) -> Result<Rc<Val>, Box<Error>> {
|
fn tuple_to_val(&mut self, fields: &Vec<(Token, Expression)>) -> Result<Rc<Val>, Box<Error>> {
|
||||||
let mut new_fields = Vec::<(PositionedItem<String>, Rc<Val>)>::new();
|
let mut new_fields = Vec::<(PositionedItem<String>, Rc<Val>)>::new();
|
||||||
for &(ref name, ref expr) in fields.iter() {
|
for &(ref name, ref expr) in fields.iter() {
|
||||||
let val = try!(self.eval_expr(expr));
|
let val = try!(self.eval_expr(expr));
|
||||||
@ -146,7 +147,7 @@ impl<'a> Builder<'a> {
|
|||||||
Ok(Rc::new(Val::Tuple(new_fields)))
|
Ok(Rc::new(Val::Tuple(new_fields)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn list_to_val(&self, def: &ListDef) -> Result<Rc<Val>, Box<Error>> {
|
fn list_to_val(&mut self, def: &ListDef) -> Result<Rc<Val>, Box<Error>> {
|
||||||
let mut vals = Vec::new();
|
let mut vals = Vec::new();
|
||||||
for expr in def.elems.iter() {
|
for expr in def.elems.iter() {
|
||||||
vals.push(try!(self.eval_expr(expr)));
|
vals.push(try!(self.eval_expr(expr)));
|
||||||
@ -154,7 +155,7 @@ impl<'a> Builder<'a> {
|
|||||||
Ok(Rc::new(Val::List(vals)))
|
Ok(Rc::new(Val::List(vals)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn value_to_val(&self, v: &Value) -> Result<Rc<Val>, Box<Error>> {
|
fn value_to_val(&mut self, v: &Value) -> Result<Rc<Val>, Box<Error>> {
|
||||||
match v {
|
match v {
|
||||||
&Value::Empty(_) => Ok(Rc::new(Val::Empty)),
|
&Value::Empty(_) => Ok(Rc::new(Val::Empty)),
|
||||||
&Value::Boolean(ref b) => Ok(Rc::new(Val::Boolean(b.val))),
|
&Value::Boolean(ref b) => Ok(Rc::new(Val::Boolean(b.val))),
|
||||||
@ -221,6 +222,7 @@ impl<'a> Builder<'a> {
|
|||||||
assets: cache,
|
assets: cache,
|
||||||
build_output: scope,
|
build_output: scope,
|
||||||
out_lock: None,
|
out_lock: None,
|
||||||
|
stack: None,
|
||||||
last: None,
|
last: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -380,6 +382,10 @@ impl<'a> Builder<'a> {
|
|||||||
if &sym.val == "env" {
|
if &sym.val == "env" {
|
||||||
return Some(self.env.clone());
|
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) {
|
if self.build_output.contains_key(sym) {
|
||||||
return Some(self.build_output[sym].clone());
|
return Some(self.build_output[sym].clone());
|
||||||
}
|
}
|
||||||
@ -451,7 +457,7 @@ impl<'a> Builder<'a> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lookup_selector(&self, sl: &SelectorList) -> Result<Rc<Val>, Box<Error>> {
|
fn lookup_selector(&mut self, sl: &SelectorList) -> Result<Rc<Val>, Box<Error>> {
|
||||||
let first = try!(self.eval_expr(&sl.head));
|
let first = try!(self.eval_expr(&sl.head));
|
||||||
// First we ensure that the result is a tuple or a list.
|
// First we ensure that the result is a tuple or a list.
|
||||||
let mut stack = VecDeque::new();
|
let mut stack = VecDeque::new();
|
||||||
@ -765,7 +771,7 @@ impl<'a> Builder<'a> {
|
|||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eval_binary(&self, def: &BinaryOpDef) -> Result<Rc<Val>, Box<Error>> {
|
fn eval_binary(&mut self, def: &BinaryOpDef) -> Result<Rc<Val>, Box<Error>> {
|
||||||
let kind = &def.kind;
|
let kind = &def.kind;
|
||||||
let left = try!(self.eval_expr(&def.left));
|
let left = try!(self.eval_expr(&def.left));
|
||||||
let right = try!(self.eval_expr(&def.right));
|
let right = try!(self.eval_expr(&def.right));
|
||||||
@ -777,7 +783,7 @@ impl<'a> Builder<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eval_compare(&self, def: &ComparisonDef) -> Result<Rc<Val>, Box<Error>> {
|
fn eval_compare(&mut self, def: &ComparisonDef) -> Result<Rc<Val>, Box<Error>> {
|
||||||
let kind = &def.kind;
|
let kind = &def.kind;
|
||||||
let left = try!(self.eval_expr(&def.left));
|
let left = try!(self.eval_expr(&def.left));
|
||||||
let right = try!(self.eval_expr(&def.right));
|
let right = try!(self.eval_expr(&def.right));
|
||||||
@ -791,9 +797,36 @@ impl<'a> Builder<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eval_copy(&self, def: &CopyDef) -> Result<Rc<Val>, Box<Error>> {
|
fn push_val(&mut self, tuple: Rc<Val>) {
|
||||||
|
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<Rc<Val>> {
|
||||||
|
if let Some(ref mut v) = self.stack {
|
||||||
|
v.pop()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn peek_val(&self) -> Option<Rc<Val>> {
|
||||||
|
if let Some(ref v) = self.stack {
|
||||||
|
v.first().map(|v| v.clone())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eval_copy(&mut self, def: &CopyDef) -> Result<Rc<Val>, Box<Error>> {
|
||||||
let v = try!(self.lookup_selector(&def.selector.sel));
|
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::<PositionedItem<String>, (i32, Rc<Val>)>::new();
|
let mut m = HashMap::<PositionedItem<String>, (i32, Rc<Val>)>::new();
|
||||||
// loop through fields and build up a hashmap
|
// loop through fields and build up a hashmap
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
@ -802,6 +835,7 @@ impl<'a> Builder<'a> {
|
|||||||
v.insert((count, val.clone()));
|
v.insert((count, val.clone()));
|
||||||
count += 1;
|
count += 1;
|
||||||
} else {
|
} else {
|
||||||
|
self.pop_val();
|
||||||
return Err(Box::new(error::BuildError::new(
|
return Err(Box::new(error::BuildError::new(
|
||||||
format!(
|
format!(
|
||||||
"Duplicate \
|
"Duplicate \
|
||||||
@ -815,6 +849,7 @@ impl<'a> Builder<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for &(ref key, ref val) in def.fields.iter() {
|
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));
|
let expr_result = try!(self.eval_expr(val));
|
||||||
match m.entry(key.into()) {
|
match m.entry(key.into()) {
|
||||||
// brand new field here.
|
// brand new field here.
|
||||||
@ -829,6 +864,7 @@ impl<'a> Builder<'a> {
|
|||||||
if src_val.1.type_equal(&expr_result) {
|
if src_val.1.type_equal(&expr_result) {
|
||||||
v.insert((src_val.0, expr_result));
|
v.insert((src_val.0, expr_result));
|
||||||
} else {
|
} else {
|
||||||
|
self.pop_val();
|
||||||
return Err(Box::new(error::BuildError::new(
|
return Err(Box::new(error::BuildError::new(
|
||||||
format!(
|
format!(
|
||||||
"Expected type {} for field {} but got {}",
|
"Expected type {} for field {} but got {}",
|
||||||
@ -843,6 +879,7 @@ impl<'a> Builder<'a> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
self.pop_val();
|
||||||
let mut new_fields: Vec<(PositionedItem<String>, (i32, Rc<Val>))> = m.drain().collect();
|
let mut new_fields: Vec<(PositionedItem<String>, (i32, Rc<Val>))> = m.drain().collect();
|
||||||
// We want to maintain our order for the fields to make comparing tuples
|
// 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.
|
// 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<Rc<Val>, Box<Error>> {
|
fn eval_format(&mut self, def: &FormatDef) -> Result<Rc<Val>, Box<Error>> {
|
||||||
let tmpl = &def.template;
|
let tmpl = &def.template;
|
||||||
let args = &def.args;
|
let args = &def.args;
|
||||||
let mut vals = Vec::new();
|
let mut vals = Vec::new();
|
||||||
@ -880,7 +917,7 @@ impl<'a> Builder<'a> {
|
|||||||
Ok(Rc::new(Val::Str(try!(formatter.render(&def.pos)))))
|
Ok(Rc::new(Val::Str(try!(formatter.render(&def.pos)))))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eval_call(&self, def: &CallDef) -> Result<Rc<Val>, Box<Error>> {
|
fn eval_call(&mut self, def: &CallDef) -> Result<Rc<Val>, Box<Error>> {
|
||||||
let sel = &def.macroref;
|
let sel = &def.macroref;
|
||||||
let args = &def.arglist;
|
let args = &def.arglist;
|
||||||
let v = try!(self.lookup_selector(&sel.sel));
|
let v = try!(self.lookup_selector(&sel.sel));
|
||||||
@ -921,7 +958,7 @@ impl<'a> Builder<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eval_select(&self, def: &SelectDef) -> Result<Rc<Val>, Box<Error>> {
|
fn eval_select(&mut self, def: &SelectDef) -> Result<Rc<Val>, Box<Error>> {
|
||||||
let target = &def.val;
|
let target = &def.val;
|
||||||
let def_expr = &def.default;
|
let def_expr = &def.default;
|
||||||
let fields = &def.tuple;
|
let fields = &def.tuple;
|
||||||
@ -951,7 +988,7 @@ impl<'a> Builder<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eval_list_op(&self, def: &ListOpDef) -> Result<Rc<Val>, Box<Error>> {
|
fn eval_list_op(&mut self, def: &ListOpDef) -> Result<Rc<Val>, Box<Error>> {
|
||||||
let maybe_list = try!(self.eval_expr(&def.target));
|
let maybe_list = try!(self.eval_expr(&def.target));
|
||||||
let l = match maybe_list.as_ref() {
|
let l = match maybe_list.as_ref() {
|
||||||
&Val::List(ref elems) => elems,
|
&Val::List(ref elems) => elems,
|
||||||
@ -1058,7 +1095,7 @@ impl<'a> Builder<'a> {
|
|||||||
|
|
||||||
// Evals a single Expression in the context of a running Builder.
|
// Evals a single Expression in the context of a running Builder.
|
||||||
// It does not mutate the builders collected state at all.
|
// It does not mutate the builders collected state at all.
|
||||||
pub fn eval_expr(&self, expr: &Expression) -> Result<Rc<Val>, Box<Error>> {
|
pub fn eval_expr(&mut self, expr: &Expression) -> Result<Rc<Val>, Box<Error>> {
|
||||||
match expr {
|
match expr {
|
||||||
&Expression::Simple(ref val) => self.value_to_val(val),
|
&Expression::Simple(ref val) => self.value_to_val(val),
|
||||||
&Expression::Binary(ref def) => self.eval_binary(def),
|
&Expression::Binary(ref def) => self.eval_binary(def),
|
||||||
|
@ -19,7 +19,7 @@ use std;
|
|||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::rc::Rc;
|
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..) {
|
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).unwrap(), Rc::new(tpl.1));
|
||||||
}
|
}
|
||||||
|
24
src/lib.rs
24
src/lib.rs
@ -47,6 +47,7 @@
|
|||||||
//!
|
//!
|
||||||
//! The following words are reserved in ucg and can't be used as named bindings.
|
//! The following words are reserved in ucg and can't be used as named bindings.
|
||||||
//!
|
//!
|
||||||
|
//! * self
|
||||||
//! * assert
|
//! * assert
|
||||||
//! * true
|
//! * true
|
||||||
//! * false
|
//! * 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
|
//! #### Conditional data
|
||||||
//!
|
//!
|
||||||
|
Loading…
x
Reference in New Issue
Block a user