mirror of
https://github.com/zaphar/ucg.git
synced 2025-07-22 18:19:54 -04:00
Feature: add list concatenation.
This commit is contained in:
parent
fffdad589c
commit
091cde9ffb
@ -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
|
||||||
|
41
src/ast.rs
41
src/ast.rs
@ -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),
|
||||||
|
|
||||||
|
48
src/build.rs
48
src/build.rs
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
69
src/parse.rs
69
src/parse.rs
@ -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}))),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user