diff --git a/README.md b/README.md index 124e559..1bef4dd 100644 --- a/README.md +++ b/README.md @@ -86,6 +86,12 @@ Lists are an ordered collection of elements. Lists can be indexed using dotted s let host1 = hosts.0; let host2 = hosts.1; +Lista can be concatenated with the + operator. + + let more_hosts = hosts + ["db3.local.net"]; + +Both the left and the right side of the + operator must be lists or you will get a type fail. + ### Variables UCG can reference a binding using variables. Any named value using diff --git a/src/ast.rs b/src/ast.rs index 44155b7..c31a775 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -96,6 +96,7 @@ pub enum Value { Symbol(LocatedNode), // Complex Values Tuple(LocatedNode), + List(ListDef), Selector(LocatedNode), } @@ -107,6 +108,7 @@ impl Value { &Value::String(_) => "String".to_string(), &Value::Symbol(_) => "Symbol".to_string(), &Value::Tuple(_) => "Tuple".to_string(), + &Value::List(_) => "List".to_string(), &Value::Selector(_) => "Selector".to_string(), } } @@ -123,6 +125,10 @@ impl Value { return buf; } + fn elems_to_string(v: &Vec) -> String { + return format!("{}", v.len()); + } + pub fn to_string(&self) -> String { match self { &Value::Int(ref i) => format!("{}", i.val), @@ -130,6 +136,7 @@ impl Value { &Value::String(ref s) => format!("{}", s.val), &Value::Symbol(ref s) => format!("{}", s.val), &Value::Tuple(ref fs) => format!("{}", Self::fields_to_string(&fs.val)), + &Value::List(ref def) => format!("[{}]", Self::elems_to_string(&def.elems)), &Value::Selector(ref v) => v.val.join("."), } } @@ -141,6 +148,7 @@ impl Value { &Value::String(ref s) => &s.pos, &Value::Symbol(ref s) => &s.pos, &Value::Tuple(ref fs) => &fs.pos, + &Value::List(ref def) => &def.pos, &Value::Selector(ref v) => &v.pos, } } @@ -234,24 +242,26 @@ pub struct MacroDef { } impl MacroDef { + fn symbol_is_in_args(&self, sym: &String) -> bool { + for arg in self.argdefs.iter() { + if &arg.val == sym { + return true; + } + } + return false; + } + fn validate_value_symbols<'a>(&self, stack: &mut Vec<&'a Expression>, val: &'a Value) -> HashSet { let mut bad_symbols = HashSet::new(); if let &Value::Symbol(ref name) = val { - let mut ok = false; - for arg in self.argdefs.iter() { - if arg.val == name.val { - ok = true; - } - } - if !ok { + if !self.symbol_is_in_args(&name.val) { bad_symbols.insert(name.val.clone()); } } else if let &Value::Selector(ref sel_node) = val { let list = &sel_node.val; - let mut ok = false; if list.len() > 0 { // We only look to see if the first selector item exists. // This is because only the first one is a symbol all of the @@ -259,12 +269,7 @@ impl MacroDef { // But we don't know at this time of the value passed into // this macro is a tuple since this isn't a callsite. println!("checking selector head {}", list[0].fragment); - for arg in self.argdefs.iter() { - if arg.val == list[0].fragment { - ok = true; - } - } - if !ok { + if !self.symbol_is_in_args(&list[0].fragment) { bad_symbols.insert(list[0].fragment.to_string()); } } @@ -273,6 +278,10 @@ impl MacroDef { for &(_, ref expr) in fields.iter() { stack.push(expr); } + } else if let &Value::List(ref def) = val { + for elem in def.elems.iter() { + stack.push(elem); + } } return bad_symbols; } @@ -289,9 +298,6 @@ impl MacroDef { bad_symbols.extend(syms_set.drain()); stack.push(&bexpr.right); } - &Expression::List(ref def) => { - stack.extend(def.elems.iter()); - } &Expression::Grouped(ref expr) => { stack.push(expr); } @@ -385,7 +391,6 @@ pub enum Expression { // Complex Expressions Copy(CopyDef), Grouped(Box), - List(ListDef), Format(FormatDef), diff --git a/src/build.rs b/src/build.rs index a8f3a75..d13a64a 100644 --- a/src/build.rs +++ b/src/build.rs @@ -276,6 +276,13 @@ impl Builder { self.lookup_sym(&(s.into())) .ok_or(Box::new(BuildError::NoSuchSymbol(format!("Unable to find {}", s.val)))) } + &Value::List(ref def) => { + let mut vals = Vec::new(); + for expr in def.elems.iter() { + vals.push(try!(self.eval_expr(expr))); + } + return Ok(Rc::new(Val::List(vals))); + } &Value::Tuple(ref tuple_node) => { let fields = tuple_node.val(); let mut new_fields = Vec::<(Positioned, Rc)>::new(); @@ -515,6 +522,23 @@ impl Builder { } } } + Val::List(ref l) => { + match expr_result.as_ref() { + &Val::List(ref r) => { + let mut new_vec = Vec::new(); + new_vec.extend(l.iter().cloned()); + new_vec.extend(r.iter().cloned()); + return Ok(Rc::new(Val::List(new_vec))); + } + val => { + return Err(Box::new(BuildError::TypeFail(format!("Expected \ + List \ + but got \ + {:?}", + val)))) + } + } + } ref expr => { return Err(Box::new( BuildError::Unsupported( @@ -633,13 +657,6 @@ impl Builder { &Expression::Grouped(ref expr) => { return self.eval_expr(expr); } - &Expression::List(ref def) => { - let mut vals = Vec::new(); - for expr in def.elems.iter() { - vals.push(try!(self.eval_expr(expr))); - } - return Ok(Rc::new(Val::List(vals))); - } &Expression::Format(ref def) => { let tmpl = &def.template; let args = &def.args; @@ -867,6 +884,23 @@ mod test { pos: Position{line: 1, column: 0}, }), Val::String("foobar".to_string())), + (Expression::Binary( + BinaryOpDef{ + kind: BinaryExprType::Add, + left: Value::List( + ListDef{ + elems: vec![Expression::Simple(Value::String(make_value_node("foo".to_string(), 1, 1)))], + pos: Position{line: 1, column: 1}, + }), + right: Box::new(Expression::Simple(Value::List( + ListDef{ + elems: vec![Expression::Simple(Value::String(make_value_node("bar".to_string(), 1, 1)))], + pos: Position{line: 1, column: 1}, + }))), + pos: Position{line: 1, column: 0}, + }), + Val::List(vec![Rc::new(Val::String("foo".to_string())), + Rc::new(Val::String("bar".to_string()))])), ], b); } diff --git a/src/parse.rs b/src/parse.rs index f30c8be..32a0d26 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -186,7 +186,33 @@ pub fn selector_or_symbol(input: Span) -> IResult { } } -named!(value( Span ) -> Value, alt!(number | quoted_value | tuple | selector_or_symbol )); +fn tuple_to_list>(t: (Sp, Vec)) -> ParseResult { + return Ok(Value::List(ListDef { + elems: t.1, + pos: t.0.into(), + })); +} + +named!(list_value( Span ) -> Value, + map_res!( + do_parse!( + pos: position!() >> + leftsquarebracket >> + elements: ws!(separated_list!(ws!(commatok), expression)) >> + rightsquarebracket >> + (pos, elements) + ), + tuple_to_list + ) +); + +named!(value( Span ) -> Value, + alt!( + number | + quoted_value | + list_value | + tuple | + selector_or_symbol )); fn value_to_expression(v: Value) -> ParseResult { Ok(Expression::Simple(v)) @@ -442,26 +468,6 @@ named!(call_expression( Span ) -> Expression, ) ); -fn tuple_to_list>(t: (Sp, Vec)) -> ParseResult { - return Ok(Expression::List(ListDef { - elems: t.1, - pos: t.0.into(), - })); -} - -named!(list_expression( Span ) -> Expression, - map_res!( - do_parse!( - pos: position!() >> - leftsquarebracket >> - elements: ws!(separated_list!(ws!(commatok), expression)) >> - rightsquarebracket >> - (pos, elements) - ), - tuple_to_list - ) -); - // NOTE(jwall): HERE THERE BE DRAGONS. The order for these matters // alot. We need to process alternatives in order of decreasing // specificity. Unfortunately this means we are required to go in a @@ -479,7 +485,6 @@ named!(expression( Span ) -> Expression, complete!(mul_expression) | complete!(div_expression) | complete!(grouped_expression) | - complete!(list_expression) | complete!(macro_expression) | complete!(format_expression) | complete!(select_expression) | @@ -577,7 +582,7 @@ pub fn parse(input: Span) -> IResult> { mod test { use super::{Statement, Expression, Value, MacroDef, SelectDef, CallDef}; use super::{number, symbol, parse, field_value, selector_value, selector_or_symbol, tuple, - grouped_expression, list_expression}; + grouped_expression, list_value}; use super::{copy_expression, macro_expression, select_expression}; use super::{format_expression, call_expression, expression}; use super::{expression_statement, let_statement, import_statement, statement}; @@ -1068,7 +1073,7 @@ mod test { ); assert_eq!(expression(LocatedSpan::new("[1, 1]")), IResult::Done(LocatedSpan{fragment: "", offset: 6, line: 1}, - Expression::List( + Expression::Simple(Value::List( ListDef{ elems: vec![ Expression::Simple(Value::Int(value_node!(1, Position{line: 1, column: 2}))), @@ -1078,7 +1083,7 @@ mod test { } ) ) - ); + )); } #[test] @@ -1304,12 +1309,12 @@ mod test { } #[test] - fn test_list_expression_parse() { - assert!(list_expression(LocatedSpan::new("foo")).is_err() ); - assert!(list_expression(LocatedSpan::new("[foo")).is_incomplete() ); - assert_eq!(list_expression(LocatedSpan::new("[foo]")), + fn test_list_value_parse() { + assert!(list_value(LocatedSpan::new("foo")).is_err() ); + assert!(list_value(LocatedSpan::new("[foo")).is_incomplete() ); + assert_eq!(list_value(LocatedSpan::new("[foo]")), IResult::Done(LocatedSpan{fragment: "", offset: 5, line: 1}, - Expression::List( + Value::List( ListDef{ elems: vec![ Expression::Simple(Value::Symbol(value_node!("foo".to_string(), Position{line: 1, column: 2}))) @@ -1320,9 +1325,9 @@ mod test { ) ); - assert_eq!(list_expression(LocatedSpan::new("[1, 1]")), + assert_eq!(list_value(LocatedSpan::new("[1, 1]")), IResult::Done(LocatedSpan{fragment: "", offset: 6, line: 1}, - Expression::List( + Value::List( ListDef{ elems: vec![ Expression::Simple(Value::Int(value_node!(1, Position{line: 1, column: 2}))),