mirror of
https://github.com/zaphar/ucg.git
synced 2025-07-22 18:19:54 -04:00
FIXES: Unit test and fixing performance regressions.
This commit is contained in:
parent
3524a5df73
commit
bb198d9d02
8
TODO.md
8
TODO.md
@ -38,3 +38,11 @@ Some options here could be:
|
||||
* Flags should allow different seperators for prefixed flags.
|
||||
* YAML export
|
||||
* HCL export
|
||||
|
||||
# Release Checklist
|
||||
|
||||
* Cargo test
|
||||
* Cargo fmt
|
||||
* Update Cargo.toml version.
|
||||
* Tag git commit with version tag.
|
||||
* Cargo publish
|
@ -41,6 +41,18 @@ fn test_comparisons() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_empty_value() {
|
||||
assert_build(
|
||||
"let empty = NULL;
|
||||
let tpl = {
|
||||
foo = NULL,
|
||||
};
|
||||
assert \"tpl.foo == empty\";
|
||||
",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_deep_comparison() {
|
||||
assert_build(
|
||||
@ -48,7 +60,7 @@ fn test_deep_comparison() {
|
||||
foo = \"bar\",
|
||||
lst = [1, 2, 3],
|
||||
inner = {
|
||||
fld = \"value\"
|
||||
fld = \"value\",
|
||||
}
|
||||
};
|
||||
let copy = tpl1;
|
||||
|
@ -134,31 +134,32 @@ impl Val {
|
||||
(&Val::Float(ref f), &Val::Float(ref ff)) => Ok(f == ff),
|
||||
(&Val::Boolean(ref b), &Val::Boolean(ref bb)) => Ok(b == bb),
|
||||
(&Val::String(ref s), &Val::String(ref ss)) => Ok(s == ss),
|
||||
(&Val::List(ref ldef), &Val::List(ref lldef)) => {
|
||||
if ldef.len() != lldef.len() {
|
||||
(&Val::List(ref ldef), &Val::List(ref rdef)) => {
|
||||
if ldef.len() != rdef.len() {
|
||||
Ok(false)
|
||||
} else {
|
||||
for (i, v) in ldef.iter().enumerate() {
|
||||
try!(v.equal(lldef[i].as_ref(), file_name, pos.clone()));
|
||||
for (i, lv) in ldef.iter().enumerate() {
|
||||
try!(lv.equal(rdef[i].as_ref(), file_name, pos.clone()));
|
||||
}
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
(&Val::Tuple(ref ldef), &Val::Tuple(ref lldef)) => {
|
||||
if ldef.len() != lldef.len() {
|
||||
(&Val::Tuple(ref ldef), &Val::Tuple(ref rdef)) => {
|
||||
if ldef.len() != rdef.len() {
|
||||
Ok(false)
|
||||
} else {
|
||||
for (i, v) in ldef.iter().enumerate() {
|
||||
let field_target = &lldef[i];
|
||||
if v.0.val != field_target.0.val {
|
||||
for (i, lv) in ldef.iter().enumerate() {
|
||||
let field_target = &rdef[i];
|
||||
if lv.0.val != field_target.0.val {
|
||||
// field name equality
|
||||
return Ok(false);
|
||||
} else {
|
||||
// field value equality.
|
||||
if !try!(
|
||||
v.1
|
||||
.equal(field_target.1.as_ref(), file_name, v.0.pos.clone())
|
||||
) {
|
||||
if !try!(lv.1.equal(
|
||||
field_target.1.as_ref(),
|
||||
file_name,
|
||||
lv.0.pos.clone()
|
||||
)) {
|
||||
return Ok(false);
|
||||
}
|
||||
}
|
||||
@ -328,6 +329,7 @@ macro_rules! eval_binary_expr {
|
||||
}
|
||||
|
||||
impl Builder {
|
||||
// FIXME(jwall): This needs some unit tests.
|
||||
fn tuple_to_val(&self, fields: &Vec<(Token, Expression)>) -> Result<Rc<Val>, Box<Error>> {
|
||||
let mut new_fields = Vec::<(Positioned<String>, Rc<Val>)>::new();
|
||||
for &(ref name, ref expr) in fields.iter() {
|
||||
@ -433,6 +435,7 @@ impl Builder {
|
||||
pub fn eval_string(&mut self, input: &str) -> Result<Rc<Val>, Box<Error>> {
|
||||
match parse(Span::new(input)) {
|
||||
Ok(stmts) => {
|
||||
//panic!("Successfully parsed {}", input);
|
||||
let mut out: Option<Rc<Val>> = None;
|
||||
for stmt in stmts.iter() {
|
||||
out = Some(try!(self.build_stmt(stmt)));
|
||||
|
@ -237,6 +237,87 @@ fn test_eval_add_expr_fail() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_eval_nested_tuple() {
|
||||
test_expr_to_val(
|
||||
vec![
|
||||
(
|
||||
Expression::Simple(Value::Tuple(value_node!(
|
||||
vec![(
|
||||
Token::new("foo", TokenType::BAREWORD, 1, 1),
|
||||
Expression::Simple(Value::Tuple(value_node!(Vec::new(), 1, 1))),
|
||||
)],
|
||||
1,
|
||||
1
|
||||
))),
|
||||
Val::Tuple(vec![(
|
||||
Positioned::new("foo".to_string(), 1, 1),
|
||||
Rc::new(Val::Tuple(Vec::new())),
|
||||
)]),
|
||||
),
|
||||
(
|
||||
Expression::Simple(Value::Tuple(value_node!(
|
||||
vec![(
|
||||
Token::new("foo", TokenType::BAREWORD, 1, 1),
|
||||
Expression::Simple(Value::Tuple(value_node!(
|
||||
vec![(
|
||||
Token::new("bar".to_string(), TokenType::BAREWORD, 1, 5),
|
||||
Expression::Simple(Value::Tuple(value_node!(vec![], 1, 10))),
|
||||
)],
|
||||
1,
|
||||
1
|
||||
))),
|
||||
)],
|
||||
1,
|
||||
1
|
||||
))),
|
||||
Val::Tuple(vec![(
|
||||
Positioned::new("foo".to_string(), 1, 1),
|
||||
Rc::new(Val::Tuple(vec![(
|
||||
Positioned::new("bar".to_string(), 1, 10),
|
||||
Rc::new(Val::Tuple(vec![])),
|
||||
)])),
|
||||
)]),
|
||||
),
|
||||
(
|
||||
Expression::Simple(Value::Tuple(value_node!(
|
||||
vec![(
|
||||
Token::new("foo", TokenType::BAREWORD, 1, 1),
|
||||
Expression::Simple(Value::Tuple(value_node!(
|
||||
vec![(
|
||||
Token::new("bar".to_string(), TokenType::BAREWORD, 1, 5),
|
||||
Expression::Simple(Value::Tuple(value_node!(
|
||||
vec![(
|
||||
Token::new("quux".to_string(), TokenType::BAREWORD, 1, 1),
|
||||
Expression::Simple(Value::Int(value_node!(3, 1, 1))),
|
||||
)],
|
||||
1,
|
||||
10
|
||||
))),
|
||||
)],
|
||||
1,
|
||||
1
|
||||
))),
|
||||
)],
|
||||
1,
|
||||
1
|
||||
))),
|
||||
Val::Tuple(vec![(
|
||||
Positioned::new("foo".to_string(), 1, 1),
|
||||
Rc::new(Val::Tuple(vec![(
|
||||
Positioned::new("bar".to_string(), 1, 10),
|
||||
Rc::new(Val::Tuple(vec![(
|
||||
Positioned::new("quux".to_string(), 1, 1),
|
||||
Rc::new(Val::Int(3)),
|
||||
)])),
|
||||
)])),
|
||||
)]),
|
||||
),
|
||||
],
|
||||
Builder::new(std::env::current_dir().unwrap()),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_eval_simple_expr() {
|
||||
test_expr_to_val(
|
||||
@ -385,6 +466,8 @@ fn test_eval_selector_list_expr() {
|
||||
);
|
||||
}
|
||||
|
||||
// TODO(jwall): Eval for tuple and list.
|
||||
// Include nested for each.
|
||||
#[test]
|
||||
#[should_panic(expected = "Unable to find tpl1")]
|
||||
fn test_expr_copy_no_such_tuple() {
|
||||
|
146
src/parse/mod.rs
146
src/parse/mod.rs
@ -129,6 +129,31 @@ macro_rules! alt_peek {
|
||||
}
|
||||
);
|
||||
|
||||
// These are our no fallback termination cases.
|
||||
(__inner $i:expr, $peekrule:ident!( $($peekargs:tt)* ) => $parserule:ident, __end ) => (
|
||||
alt_peek!(__inner $i, $peekrule!($($peekargs)*) => call!($parserule), __end )
|
||||
);
|
||||
|
||||
(__inner $i:expr, $peekrule:ident!( $($peekargs:tt)* ) => $parserule:ident!( $($parseargs:tt)* ), __end ) => (
|
||||
{
|
||||
let _i = $i.clone();
|
||||
let pre_res = peek!(_i, $peekrule!($($peekargs)*));
|
||||
match pre_res {
|
||||
// if the peek was incomplete then it might still match so return incomplete.
|
||||
nom::IResult::Incomplete(i) => nom::IResult::Incomplete(i),
|
||||
// If the peek was in error then try the next peek => parse pair.
|
||||
nom::IResult::Error(_) => {
|
||||
alt_peek!(__inner $i, __end)
|
||||
},
|
||||
// If the peek was successful then return the result of the parserule
|
||||
// regardless of it's result.
|
||||
nom::IResult::Done(_i, _) => {
|
||||
$parserule!(_i, $($parseargs)*)
|
||||
},
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// These are our fallback termination cases.
|
||||
(__inner $i:expr, $fallback:ident, __end) => (
|
||||
{
|
||||
@ -149,7 +174,7 @@ macro_rules! alt_peek {
|
||||
// If there is no fallback then we return an Error.
|
||||
(__inner $i:expr, __end) => {
|
||||
// FIXME(jwall): We should do a better custom error here.
|
||||
nom::IResult::Error(error_position!($crate::ErrorKind::Alt,$i))
|
||||
nom::IResult::Error(error_position!(nom::ErrorKind::Alt,$i))
|
||||
};
|
||||
|
||||
// alt_peek entry_point.
|
||||
@ -322,52 +347,53 @@ fn tuple_to_binary_expression(
|
||||
/// are passed in as lowerrule parsers. We default to grouped_expression and simple_expression as
|
||||
/// the most tightly bound expressions.
|
||||
macro_rules! do_binary_expr {
|
||||
($i:expr, $oprule:ident!( $($args:tt)* ), $typ:expr) => {
|
||||
do_binary_expr!($i, $oprule!($($args)*), $typ, non_op_expression)
|
||||
($i:expr, $oprule:ident!( $($args:tt)* )) => {
|
||||
do_binary_expr!($i, $oprule!($($args)*), non_op_expression)
|
||||
};
|
||||
|
||||
($i:expr, $oprule:ident!( $($args:tt)* ), $typ:expr, $lowerrule:ident) => {
|
||||
do_binary_expr!($i, $oprule!($($args)*), $typ, call!($lowerrule))
|
||||
($i:expr, $oprule:ident!( $($args:tt)* ), $lowerrule:ident) => {
|
||||
do_binary_expr!($i, $oprule!($($args)*), call!($lowerrule))
|
||||
};
|
||||
|
||||
($i:expr, $oprule:ident!( $($args:tt)* ), $typ:expr, $lowerrule:ident!( $($lowerargs:tt)* )) => {
|
||||
($i:expr, $oprule:ident!( $($args:tt)* ), $lowerrule:ident!( $($lowerargs:tt)* )) => {
|
||||
map_res!($i,
|
||||
do_parse!(
|
||||
pos: pos >>
|
||||
left: $lowerrule!($($lowerargs)*) >>
|
||||
$oprule!($($args)*) >>
|
||||
typ: $oprule!($($args)*) >>
|
||||
right: $lowerrule!($($lowerargs)*) >>
|
||||
(pos, $typ, left, right)
|
||||
(pos, typ, left, right)
|
||||
),
|
||||
tuple_to_binary_expression
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
// Matches an operator token to a BinaryExprType
|
||||
named!(math_op_type<TokenIter, BinaryExprType, error::Error>,
|
||||
alt!(
|
||||
do_parse!(punct!("+") >> (BinaryExprType::Add)) |
|
||||
do_parse!(punct!("-") >> (BinaryExprType::Sub)) |
|
||||
do_parse!(punct!("*") >> (BinaryExprType::Mul)) |
|
||||
do_parse!(punct!("/") >> (BinaryExprType::Div))
|
||||
)
|
||||
);
|
||||
|
||||
// trace_macros!(true);
|
||||
named!(add_expression<TokenIter, Expression, error::Error>,
|
||||
do_binary_expr!(punct!("+"), BinaryExprType::Add, alt!(product_expression | simple_expression | grouped_expression))
|
||||
);
|
||||
// trace_macros!(false);
|
||||
|
||||
named!(sub_expression<TokenIter, Expression, error::Error>,
|
||||
do_binary_expr!(punct!("-"), BinaryExprType::Sub, alt!(product_expression | simple_expression | grouped_expression))
|
||||
);
|
||||
|
||||
named!(sum_expression<TokenIter, Expression, error::Error>,
|
||||
alt!(add_expression | sub_expression)
|
||||
);
|
||||
|
||||
named!(mul_expression<TokenIter, Expression, error::Error>,
|
||||
do_binary_expr!(punct!("*"), BinaryExprType::Mul)
|
||||
);
|
||||
|
||||
named!(div_expression<TokenIter, Expression, error::Error>,
|
||||
do_binary_expr!(punct!("/"), BinaryExprType::Div)
|
||||
do_binary_expr!(
|
||||
alt_peek!(
|
||||
punct!("+") => math_op_type |
|
||||
punct!("-") => math_op_type),
|
||||
alt!(product_expression | simple_expression | grouped_expression))
|
||||
);
|
||||
|
||||
named!(product_expression<TokenIter, Expression, error::Error>,
|
||||
alt!(mul_expression | div_expression)
|
||||
do_binary_expr!(
|
||||
alt_peek!(
|
||||
punct!("*") => math_op_type |
|
||||
punct!("/") => math_op_type)
|
||||
)
|
||||
);
|
||||
|
||||
named!(math_expression<TokenIter, Expression, error::Error>,
|
||||
@ -386,57 +412,31 @@ fn tuple_to_compare_expression(
|
||||
}))
|
||||
}
|
||||
|
||||
// This macro is much simpler than the math binary expressions since they are the
|
||||
// bottom of the precendence tree and we can hard code the precedence in here.
|
||||
macro_rules! do_compare_expr {
|
||||
($i:expr, $subrule:ident!( $($args:tt)* ), $typ:expr) => {
|
||||
map_res!($i,
|
||||
do_parse!(
|
||||
pos: pos >>
|
||||
left: alt!(math_expression | non_op_expression) >>
|
||||
$subrule!($($args)*) >>
|
||||
right: alt!(math_expression | non_op_expression) >>
|
||||
(pos, $typ, left, right)
|
||||
),
|
||||
tuple_to_compare_expression
|
||||
named!(compare_op_type<TokenIter, CompareType, error::Error>,
|
||||
alt!(
|
||||
do_parse!(punct!("==") >> (CompareType::Equal)) |
|
||||
do_parse!(punct!("!=") >> (CompareType::NotEqual)) |
|
||||
do_parse!(punct!("<=") >> (CompareType::LTEqual)) |
|
||||
do_parse!(punct!(">=") >> (CompareType::GTEqual)) |
|
||||
do_parse!(punct!("<") >> (CompareType::LT)) |
|
||||
do_parse!(punct!(">") >> (CompareType::GT))
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
named!(eqeq_expression<TokenIter, Expression, error::Error>,
|
||||
do_compare_expr!(punct!("=="), CompareType::Equal)
|
||||
);
|
||||
|
||||
named!(not_eqeq_expression<TokenIter, Expression, error::Error>,
|
||||
do_compare_expr!(punct!("!="), CompareType::NotEqual)
|
||||
);
|
||||
|
||||
named!(lt_eqeq_expression<TokenIter, Expression, error::Error>,
|
||||
do_compare_expr!(punct!("<="), CompareType::LTEqual)
|
||||
);
|
||||
|
||||
named!(gt_eqeq_expression<TokenIter, Expression, error::Error>,
|
||||
do_compare_expr!(punct!(">="), CompareType::GTEqual)
|
||||
);
|
||||
|
||||
named!(gt_expression<TokenIter, Expression, error::Error>,
|
||||
do_compare_expr!(punct!(">"), CompareType::GT)
|
||||
);
|
||||
|
||||
named!(lt_expression<TokenIter, Expression, error::Error>,
|
||||
do_compare_expr!(punct!("<"), CompareType::LT)
|
||||
);
|
||||
|
||||
named!(compare_expression<TokenIter, Expression, error::Error>,
|
||||
alt!(
|
||||
eqeq_expression |
|
||||
not_eqeq_expression |
|
||||
lt_eqeq_expression |
|
||||
gt_eqeq_expression |
|
||||
lt_expression |
|
||||
gt_expression)
|
||||
map_res!(
|
||||
do_parse!(
|
||||
pos: pos >>
|
||||
left: alt!(math_expression | non_op_expression) >>
|
||||
typ: compare_op_type >>
|
||||
right: alt!(math_expression | non_op_expression) >>
|
||||
(pos, typ, left, right)
|
||||
),
|
||||
tuple_to_compare_expression
|
||||
)
|
||||
);
|
||||
|
||||
// FIXME(jwall): This is really *really* slow.
|
||||
named!(op_expression<TokenIter, Expression, error::Error>,
|
||||
alt!(math_expression | compare_expression)
|
||||
);
|
||||
@ -817,7 +817,7 @@ named!(non_op_expression<TokenIter, Expression, error::Error>,
|
||||
);
|
||||
|
||||
named!(expression<TokenIter, Expression, error::Error>,
|
||||
alt!(complete!(op_expression) | complete!(non_op_expression))
|
||||
alt_complete!(op_expression | non_op_expression)
|
||||
);
|
||||
|
||||
fn expression_to_statement(v: Expression) -> ParseResult<Statement> {
|
||||
|
@ -43,6 +43,15 @@ macro_rules! assert_error {
|
||||
#[test]
|
||||
fn test_null_parsing() {
|
||||
assert_parse!(empty_value("NULL "), Value::Empty(Position::new(1, 1)));
|
||||
assert_parse!(value("NULL "), Value::Empty(Position::new(1, 1)));
|
||||
assert_parse!(
|
||||
simple_expression("NULL "),
|
||||
Expression::Simple(Value::Empty(Position::new(1, 1)))
|
||||
);
|
||||
assert_parse!(
|
||||
expression("NULL,"),
|
||||
Expression::Simple(Value::Empty(Position::new(1, 1)))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -412,7 +421,7 @@ fn test_expression_parse() {
|
||||
})
|
||||
);
|
||||
assert_parse!(
|
||||
mul_expression("1 * 1"),
|
||||
product_expression("1 * 1"),
|
||||
Expression::Binary(BinaryOpDef {
|
||||
kind: BinaryExprType::Mul,
|
||||
left: Box::new(Expression::Simple(Value::Int(value_node!(1, 1, 1)))),
|
||||
@ -1013,6 +1022,34 @@ fn test_tuple_parse() {
|
||||
1
|
||||
))
|
||||
);
|
||||
|
||||
assert_parse!(
|
||||
expression("{ foo = 1, lst = [1, 2, 3], }"),
|
||||
Expression::Simple(Value::Tuple(value_node!(
|
||||
vec![
|
||||
(
|
||||
make_tok!("foo", 1, 3),
|
||||
Expression::Simple(Value::Int(value_node!(1, Position::new(1, 9)))),
|
||||
),
|
||||
(
|
||||
make_tok!("lst", 1, 12),
|
||||
Expression::Simple(Value::List(ListDef {
|
||||
elems: vec![
|
||||
Expression::Simple(Value::Int(value_node!(1, Position::new(1, 19)))),
|
||||
Expression::Simple(Value::Int(value_node!(2, Position::new(1, 22)))),
|
||||
Expression::Simple(Value::Int(value_node!(3, Position::new(1, 25)))),
|
||||
],
|
||||
pos: Position {
|
||||
line: 1,
|
||||
column: 18,
|
||||
},
|
||||
})),
|
||||
),
|
||||
],
|
||||
1,
|
||||
1
|
||||
)))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -1043,6 +1080,23 @@ fn test_field_list_parse() {
|
||||
(make_tok!("quux", 3, 1), make_expr!(2 => int, 3, 8)),
|
||||
]
|
||||
);
|
||||
f_list = "foo = 1,\nquux = [1, 2],";
|
||||
assert_parse!(
|
||||
field_list(f_list),
|
||||
vec![
|
||||
(make_tok!("foo", 1, 1), make_expr!(1 => int, 1, 7)),
|
||||
(
|
||||
make_tok!("quux", 2, 1),
|
||||
Expression::Simple(Value::List(ListDef {
|
||||
elems: vec![
|
||||
Expression::Simple(Value::Int(value_node!(1, Position::new(2, 9)))),
|
||||
Expression::Simple(Value::Int(value_node!(2, Position::new(2, 12)))),
|
||||
],
|
||||
pos: Position::new(2, 8),
|
||||
})),
|
||||
),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -1106,6 +1160,19 @@ fn test_field_value_parse() {
|
||||
))
|
||||
)
|
||||
);
|
||||
assert_parse!(
|
||||
field_value("foo = [1,2], "),
|
||||
(
|
||||
make_tok!("foo", 1, 1),
|
||||
Expression::Simple(Value::List(ListDef {
|
||||
elems: vec![
|
||||
Expression::Simple(Value::Int(value_node!(1, Position::new(1, 8)))),
|
||||
Expression::Simple(Value::Int(value_node!(2, Position::new(1, 10)))),
|
||||
],
|
||||
pos: Position::new(1, 7),
|
||||
}))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -142,7 +142,7 @@ macro_rules! do_tag_tok {
|
||||
}
|
||||
|
||||
named!(emptytok( Span ) -> Token,
|
||||
do_tag_tok!(TokenType::EMPTY, "NULL", WS)
|
||||
do_tag_tok!(TokenType::EMPTY, "NULL")
|
||||
);
|
||||
|
||||
named!(commatok( Span ) -> Token,
|
||||
|
Loading…
x
Reference in New Issue
Block a user