BUGFIX: Regression tests for our list operators and bugfixes.

This commit is contained in:
Jeremy Wall 2018-08-21 23:05:06 -05:00
parent 523e2db483
commit f9cdd4f3f6
5 changed files with 68 additions and 69 deletions

View File

@ -0,0 +1,17 @@
let list1 = [1, 2, 3, 4];
let list2 = ["foo", "bar", "foo", "bar"];
let mapper = macro(item) => { result = item + 1 };
let filtrator = macro(item) => {
result = select item, NULL, {
foo = item,
},
};
assert |map mapper.result list1 == [2, 3, 4, 5]|;
assert |(map mapper.result [1, 2, 3, 4]) == [2, 3, 4, 5]|;
assert |map mapper.result [1, 2, 3, 4] == [2, 3, 4, 5]|;
assert |filter filtrator.result list2 == ["foo", "foo"]|;
assert |(filter filtrator.result ["foo", "bar", "foo", "bar"]) == ["foo", "foo"]|;
assert |filter filtrator.result ["foo", "bar", "foo", "bar"] == ["foo", "foo"]|;

View File

@ -691,7 +691,7 @@ pub struct ListOpDef {
pub typ: ListOpType, pub typ: ListOpType,
pub mac: SelectorDef, pub mac: SelectorDef,
pub field: String, pub field: String,
pub target: ListDef, pub target: Box<Expression>,
pub pos: Position, pub pos: Position,
} }

View File

@ -61,3 +61,8 @@ fn test_binary_operator_precedence() {
"../../integration_tests/operator_precedence_test.ucg" "../../integration_tests/operator_precedence_test.ucg"
)); ));
} }
#[test]
fn test_list_operations() {
assert_build(include_str!("../../integration_tests/list_ops_test.ucg"));
}

View File

@ -1137,12 +1137,22 @@ impl<'a> Builder<'a> {
} }
fn eval_list_op(&self, def: &ListOpDef) -> Result<Rc<Val>, Box<Error>> { fn eval_list_op(&self, def: &ListOpDef) -> Result<Rc<Val>, Box<Error>> {
let l = &def.target.elems; let maybe_list = try!(self.eval_expr(&def.target));
let l = match maybe_list.as_ref() {
&Val::List(ref elems) => elems,
other => {
return Err(Box::new(error::Error::new(
format!("Expected List as target but got {:?}", other.type_name()),
error::ErrorType::TypeFail,
def.target.pos().clone(),
)));
}
};
let mac = &def.mac; let mac = &def.mac;
if let &Val::Macro(ref macdef) = try!(self.lookup_selector(&mac.sel)).as_ref() { if let &Val::Macro(ref macdef) = try!(self.lookup_selector(&mac.sel)).as_ref() {
let mut out = Vec::new(); let mut out = Vec::new();
for expr in l.iter() { for expr in l.iter() {
let argvals = vec![try!(self.eval_expr(expr))]; let argvals = vec![expr.clone()];
let fields = try!(macdef.eval( let fields = try!(macdef.eval(
self.root.clone(), self.root.clone(),
self.assets.clone(), self.assets.clone(),

View File

@ -43,7 +43,7 @@ macro_rules! trace_nom {
} }
let result = $rule($i, $($args)* ); let result = $rule($i, $($args)* );
if ENABLE_TRACE { if ENABLE_TRACE {
eprintln!("Exiting Rule: {:?}", stringify!($rule)); eprintln!("Exiting Rule: {:?} with {:?}", stringify!($rule), result);
} }
result result
} }
@ -57,7 +57,7 @@ macro_rules! trace_nom {
} }
let result = call!($i, $rule); let result = call!($i, $rule);
if ENABLE_TRACE { if ENABLE_TRACE {
eprintln!("Exiting Rule: {:?}", stringify!($rule)); eprintln!("Exiting Rule: {:?} with {:?}", stringify!($rule), result);
} }
result result
} }
@ -636,31 +636,7 @@ named!(call_expression<TokenIter, Expression, error::Error>,
) )
); );
fn symbol_or_list(input: TokenIter) -> NomResult<Value> { fn tuple_to_list_op(tpl: (Position, Token, Value, Expression)) -> ParseResult<Expression> {
let sym = do_parse!(input, sym: symbol >> (sym));
match sym {
IResult::Incomplete(i) => {
return IResult::Incomplete(i);
}
IResult::Error(_) => match list_value(input) {
IResult::Incomplete(i) => {
return IResult::Incomplete(i);
}
IResult::Error(e) => {
return IResult::Error(e);
}
IResult::Done(i, val) => {
return IResult::Done(i, val);
}
},
IResult::Done(rest, val) => {
return IResult::Done(rest, val);
}
}
}
fn tuple_to_list_op(tpl: (Position, Token, Value, Value)) -> ParseResult<Expression> {
let pos = tpl.0; let pos = tpl.0;
let t = if &tpl.1.fragment == "map" { let t = if &tpl.1.fragment == "map" {
ListOpType::Map ListOpType::Map
@ -683,6 +659,12 @@ fn tuple_to_list_op(tpl: (Position, Token, Value, Value)) -> ParseResult<Express
// two sections. // two sections.
let fieldname: String = match &mut def.sel.tail { let fieldname: String = match &mut def.sel.tail {
&mut None => { &mut None => {
if ENABLE_TRACE {
eprintln!(
"tuple_to_list_op had error {}",
"Missing a result field for the macro"
);
}
return Err(error::Error::new( return Err(error::Error::new(
format!("Missing a result field for the macro"), format!("Missing a result field for the macro"),
error::ErrorType::IncompleteParsing, error::ErrorType::IncompleteParsing,
@ -691,6 +673,12 @@ fn tuple_to_list_op(tpl: (Position, Token, Value, Value)) -> ParseResult<Express
} }
&mut Some(ref mut tl) => { &mut Some(ref mut tl) => {
if tl.len() < 1 { if tl.len() < 1 {
if ENABLE_TRACE {
eprintln!(
"tuple_to_list_op had error {}",
"Missing a result field for the macro"
);
}
return Err(error::Error::new( return Err(error::Error::new(
format!("Missing a result field for the macro"), format!("Missing a result field for the macro"),
error::ErrorType::IncompleteParsing, error::ErrorType::IncompleteParsing,
@ -701,20 +689,19 @@ fn tuple_to_list_op(tpl: (Position, Token, Value, Value)) -> ParseResult<Express
fname.unwrap().fragment fname.unwrap().fragment
} }
}; };
if let Value::List(ldef) = list { return Ok(Expression::ListOp(ListOpDef {
return Ok(Expression::ListOp(ListOpDef { typ: t,
typ: t, mac: def,
mac: def, field: fieldname,
field: fieldname, target: Box::new(list),
target: ldef, pos: pos,
pos: pos, }));
})); }
} if ENABLE_TRACE {
return Err(error::Error::new( eprintln!(
format!("Expected a list but got {}", list.type_name()), "tuple_to_list_op had error {}",
error::ErrorType::UnexpectedToken, format!("Expected a selector but got {}", macroname.type_name())
pos, );
));
} }
return Err(error::Error::new( return Err(error::Error::new(
format!("Expected a selector but got {}", macroname.type_name()), format!("Expected a selector but got {}", macroname.type_name()),
@ -729,7 +716,7 @@ named!(list_op_expression<TokenIter, Expression, error::Error>,
pos: pos >> pos: pos >>
optype: alt!(word!("map") | word!("filter")) >> optype: alt!(word!("map") | word!("filter")) >>
macroname: trace_nom!(selector_value) >> macroname: trace_nom!(selector_value) >>
list: trace_nom!(symbol_or_list) >> list: trace_nom!(non_op_expression) >>
(pos, optype, macroname, list) (pos, optype, macroname, list)
), ),
tuple_to_list_op tuple_to_list_op
@ -785,12 +772,7 @@ named!(let_statement<TokenIter, Statement, error::Error>,
do_parse!( do_parse!(
word!("let") >> word!("let") >>
pos: pos >> pos: pos >>
stmt: add_return_error!( stmt: trace_nom!(let_stmt_body) >>
nom::ErrorKind::Custom(
error::Error::new(
"Invalid syntax for let binding",
error::ErrorType::ParseError, pos)),
trace_nom!(let_stmt_body)) >>
(stmt) (stmt)
) )
); );
@ -819,12 +801,7 @@ named!(import_statement<TokenIter, Statement, error::Error>,
word!("import") >> word!("import") >>
// past this point we know this is supposed to be an import statement. // past this point we know this is supposed to be an import statement.
pos: pos >> pos: pos >>
stmt: add_return_error!( stmt: trace_nom!(import_stmt_body) >>
nom::ErrorKind::Custom(
error::Error::new(
"Invalid syntax for import",
error::ErrorType::ParseError, pos)),
trace_nom!(import_stmt_body)) >>
(stmt) (stmt)
) )
); );
@ -833,12 +810,7 @@ named!(assert_statement<TokenIter, Statement, error::Error>,
do_parse!( do_parse!(
word!("assert") >> word!("assert") >>
pos: pos >> pos: pos >>
tok: add_return_error!( tok: match_type!(PIPEQUOTE) >>
nom::ErrorKind::Custom(
error::Error::new(
"Invalid syntax for assert",
error::ErrorType::ParseError, pos)),
match_type!(PIPEQUOTE)) >>
punct!(";") >> punct!(";") >>
(Statement::Assert(tok.clone())) (Statement::Assert(tok.clone()))
) )
@ -849,12 +821,7 @@ named!(out_statement<TokenIter, Statement, error::Error>,
word!("out") >> word!("out") >>
pos: pos >> pos: pos >>
typ: match_type!(BAREWORD) >> typ: match_type!(BAREWORD) >>
expr: add_return_error!( expr: expression >>
nom::ErrorKind::Custom(
error::Error::new(
"Invalid syntax for assert",
error::ErrorType::ParseError, pos)),
expression) >>
punct!(";") >> punct!(";") >>
(Statement::Output(typ.clone(), expr.clone())) (Statement::Output(typ.clone(), expr.clone()))
) )