Feature: add list concatenation.

This commit is contained in:
Jeremy Wall 2017-11-30 22:39:41 -06:00
parent fffdad589c
commit 091cde9ffb
4 changed files with 107 additions and 57 deletions

View File

@ -86,6 +86,12 @@ Lists are an ordered collection of elements. Lists can be indexed using dotted s
let host1 = hosts.0; let host1 = hosts.0;
let host2 = hosts.1; 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 ### Variables
UCG can reference a binding using variables. Any named value using UCG can reference a binding using variables. Any named value using

View File

@ -96,6 +96,7 @@ pub enum Value {
Symbol(LocatedNode<String>), Symbol(LocatedNode<String>),
// Complex Values // Complex Values
Tuple(LocatedNode<FieldList>), Tuple(LocatedNode<FieldList>),
List(ListDef),
Selector(LocatedNode<SelectorList>), Selector(LocatedNode<SelectorList>),
} }
@ -107,6 +108,7 @@ impl Value {
&Value::String(_) => "String".to_string(), &Value::String(_) => "String".to_string(),
&Value::Symbol(_) => "Symbol".to_string(), &Value::Symbol(_) => "Symbol".to_string(),
&Value::Tuple(_) => "Tuple".to_string(), &Value::Tuple(_) => "Tuple".to_string(),
&Value::List(_) => "List".to_string(),
&Value::Selector(_) => "Selector".to_string(), &Value::Selector(_) => "Selector".to_string(),
} }
} }
@ -123,6 +125,10 @@ impl Value {
return buf; return buf;
} }
fn elems_to_string(v: &Vec<Expression>) -> String {
return format!("{}", v.len());
}
pub fn to_string(&self) -> String { pub fn to_string(&self) -> String {
match self { match self {
&Value::Int(ref i) => format!("{}", i.val), &Value::Int(ref i) => format!("{}", i.val),
@ -130,6 +136,7 @@ impl Value {
&Value::String(ref s) => format!("{}", s.val), &Value::String(ref s) => format!("{}", s.val),
&Value::Symbol(ref s) => format!("{}", s.val), &Value::Symbol(ref s) => format!("{}", s.val),
&Value::Tuple(ref fs) => format!("{}", Self::fields_to_string(&fs.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("."), &Value::Selector(ref v) => v.val.join("."),
} }
} }
@ -141,6 +148,7 @@ impl Value {
&Value::String(ref s) => &s.pos, &Value::String(ref s) => &s.pos,
&Value::Symbol(ref s) => &s.pos, &Value::Symbol(ref s) => &s.pos,
&Value::Tuple(ref fs) => &fs.pos, &Value::Tuple(ref fs) => &fs.pos,
&Value::List(ref def) => &def.pos,
&Value::Selector(ref v) => &v.pos, &Value::Selector(ref v) => &v.pos,
} }
} }
@ -234,24 +242,26 @@ pub struct MacroDef {
} }
impl 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, fn validate_value_symbols<'a>(&self,
stack: &mut Vec<&'a Expression>, stack: &mut Vec<&'a Expression>,
val: &'a Value) val: &'a Value)
-> HashSet<String> { -> HashSet<String> {
let mut bad_symbols = HashSet::new(); let mut bad_symbols = HashSet::new();
if let &Value::Symbol(ref name) = val { if let &Value::Symbol(ref name) = val {
let mut ok = false; if !self.symbol_is_in_args(&name.val) {
for arg in self.argdefs.iter() {
if arg.val == name.val {
ok = true;
}
}
if !ok {
bad_symbols.insert(name.val.clone()); bad_symbols.insert(name.val.clone());
} }
} else if let &Value::Selector(ref sel_node) = val { } else if let &Value::Selector(ref sel_node) = val {
let list = &sel_node.val; let list = &sel_node.val;
let mut ok = false;
if list.len() > 0 { if list.len() > 0 {
// We only look to see if the first selector item exists. // We only look to see if the first selector item exists.
// This is because only the first one is a symbol all of the // 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 // But we don't know at this time of the value passed into
// this macro is a tuple since this isn't a callsite. // this macro is a tuple since this isn't a callsite.
println!("checking selector head {}", list[0].fragment); println!("checking selector head {}", list[0].fragment);
for arg in self.argdefs.iter() { if !self.symbol_is_in_args(&list[0].fragment) {
if arg.val == list[0].fragment {
ok = true;
}
}
if !ok {
bad_symbols.insert(list[0].fragment.to_string()); bad_symbols.insert(list[0].fragment.to_string());
} }
} }
@ -273,6 +278,10 @@ impl MacroDef {
for &(_, ref expr) in fields.iter() { for &(_, ref expr) in fields.iter() {
stack.push(expr); stack.push(expr);
} }
} else if let &Value::List(ref def) = val {
for elem in def.elems.iter() {
stack.push(elem);
}
} }
return bad_symbols; return bad_symbols;
} }
@ -289,9 +298,6 @@ impl MacroDef {
bad_symbols.extend(syms_set.drain()); bad_symbols.extend(syms_set.drain());
stack.push(&bexpr.right); stack.push(&bexpr.right);
} }
&Expression::List(ref def) => {
stack.extend(def.elems.iter());
}
&Expression::Grouped(ref expr) => { &Expression::Grouped(ref expr) => {
stack.push(expr); stack.push(expr);
} }
@ -385,7 +391,6 @@ pub enum Expression {
// Complex Expressions // Complex Expressions
Copy(CopyDef), Copy(CopyDef),
Grouped(Box<Expression>), Grouped(Box<Expression>),
List(ListDef),
Format(FormatDef), Format(FormatDef),

View File

@ -276,6 +276,13 @@ impl Builder {
self.lookup_sym(&(s.into())) self.lookup_sym(&(s.into()))
.ok_or(Box::new(BuildError::NoSuchSymbol(format!("Unable to find {}", s.val)))) .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) => { &Value::Tuple(ref tuple_node) => {
let fields = tuple_node.val(); let fields = tuple_node.val();
let mut new_fields = Vec::<(Positioned<String>, Rc<Val>)>::new(); let mut new_fields = Vec::<(Positioned<String>, Rc<Val>)>::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 => { ref expr => {
return Err(Box::new( return Err(Box::new(
BuildError::Unsupported( BuildError::Unsupported(
@ -633,13 +657,6 @@ impl Builder {
&Expression::Grouped(ref expr) => { &Expression::Grouped(ref expr) => {
return self.eval_expr(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) => { &Expression::Format(ref def) => {
let tmpl = &def.template; let tmpl = &def.template;
let args = &def.args; let args = &def.args;
@ -867,6 +884,23 @@ mod test {
pos: Position{line: 1, column: 0}, pos: Position{line: 1, column: 0},
}), }),
Val::String("foobar".to_string())), 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); ], b);
} }

View File

@ -186,7 +186,33 @@ pub fn selector_or_symbol(input: Span) -> IResult<Span, Value> {
} }
} }
named!(value( Span ) -> Value, alt!(number | quoted_value | tuple | selector_or_symbol )); fn tuple_to_list<Sp: Into<Position>>(t: (Sp, Vec<Expression>)) -> ParseResult<Value> {
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<Expression> { fn value_to_expression(v: Value) -> ParseResult<Expression> {
Ok(Expression::Simple(v)) Ok(Expression::Simple(v))
@ -442,26 +468,6 @@ named!(call_expression( Span ) -> Expression,
) )
); );
fn tuple_to_list<Sp: Into<Position>>(t: (Sp, Vec<Expression>)) -> ParseResult<Expression> {
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 // NOTE(jwall): HERE THERE BE DRAGONS. The order for these matters
// alot. We need to process alternatives in order of decreasing // alot. We need to process alternatives in order of decreasing
// specificity. Unfortunately this means we are required to go in a // specificity. Unfortunately this means we are required to go in a
@ -479,7 +485,6 @@ named!(expression( Span ) -> Expression,
complete!(mul_expression) | complete!(mul_expression) |
complete!(div_expression) | complete!(div_expression) |
complete!(grouped_expression) | complete!(grouped_expression) |
complete!(list_expression) |
complete!(macro_expression) | complete!(macro_expression) |
complete!(format_expression) | complete!(format_expression) |
complete!(select_expression) | complete!(select_expression) |
@ -577,7 +582,7 @@ pub fn parse(input: Span) -> IResult<Span, Vec<Statement>> {
mod test { mod test {
use super::{Statement, Expression, Value, MacroDef, SelectDef, CallDef}; use super::{Statement, Expression, Value, MacroDef, SelectDef, CallDef};
use super::{number, symbol, parse, field_value, selector_value, selector_or_symbol, tuple, 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::{copy_expression, macro_expression, select_expression};
use super::{format_expression, call_expression, expression}; use super::{format_expression, call_expression, expression};
use super::{expression_statement, let_statement, import_statement, statement}; use super::{expression_statement, let_statement, import_statement, statement};
@ -1068,7 +1073,7 @@ mod test {
); );
assert_eq!(expression(LocatedSpan::new("[1, 1]")), assert_eq!(expression(LocatedSpan::new("[1, 1]")),
IResult::Done(LocatedSpan{fragment: "", offset: 6, line: 1}, IResult::Done(LocatedSpan{fragment: "", offset: 6, line: 1},
Expression::List( Expression::Simple(Value::List(
ListDef{ ListDef{
elems: vec![ elems: vec![
Expression::Simple(Value::Int(value_node!(1, Position{line: 1, column: 2}))), Expression::Simple(Value::Int(value_node!(1, Position{line: 1, column: 2}))),
@ -1078,7 +1083,7 @@ mod test {
} }
) )
) )
); ));
} }
#[test] #[test]
@ -1304,12 +1309,12 @@ mod test {
} }
#[test] #[test]
fn test_list_expression_parse() { fn test_list_value_parse() {
assert!(list_expression(LocatedSpan::new("foo")).is_err() ); assert!(list_value(LocatedSpan::new("foo")).is_err() );
assert!(list_expression(LocatedSpan::new("[foo")).is_incomplete() ); assert!(list_value(LocatedSpan::new("[foo")).is_incomplete() );
assert_eq!(list_expression(LocatedSpan::new("[foo]")), assert_eq!(list_value(LocatedSpan::new("[foo]")),
IResult::Done(LocatedSpan{fragment: "", offset: 5, line: 1}, IResult::Done(LocatedSpan{fragment: "", offset: 5, line: 1},
Expression::List( Value::List(
ListDef{ ListDef{
elems: vec![ elems: vec![
Expression::Simple(Value::Symbol(value_node!("foo".to_string(), Position{line: 1, column: 2}))) 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}, IResult::Done(LocatedSpan{fragment: "", offset: 6, line: 1},
Expression::List( Value::List(
ListDef{ ListDef{
elems: vec![ elems: vec![
Expression::Simple(Value::Int(value_node!(1, Position{line: 1, column: 2}))), Expression::Simple(Value::Int(value_node!(1, Position{line: 1, column: 2}))),