From a7a32d56b2558470f8729e1502a65913688fa8ae Mon Sep 17 00:00:00 2001 From: Jeremy Wall Date: Mon, 20 Aug 2018 22:51:55 -0500 Subject: [PATCH] FEATURE: More ergonomic syntax for asserts. --- integration_tests/comparisons_test.ucg | 40 +++++++++---------- integration_tests/empty_test.ucg | 2 +- integration_tests/macros_test.ucg | 12 +++--- .../operator_precedence_test.ucg | 14 +++---- integration_tests/selectors_test.ucg | 14 +++---- src/ast/mod.rs | 1 + src/lib.rs | 18 +++------ src/parse/mod.rs | 2 +- src/parse/test.rs | 14 +++++++ src/tokenizer/mod.rs | 23 +++++++++++ src/tokenizer/test.rs | 10 +++++ 11 files changed, 95 insertions(+), 55 deletions(-) diff --git a/integration_tests/comparisons_test.ucg b/integration_tests/comparisons_test.ucg index 70a8e99..e63f522 100644 --- a/integration_tests/comparisons_test.ucg +++ b/integration_tests/comparisons_test.ucg @@ -16,15 +16,15 @@ let list = [1, 2, 3]; let list2 = list; let list3 = [1, 2]; -assert "one == one"; -assert "one == one"; -assert "one >= one"; -assert "two > one"; -assert "two >= two"; -assert "tpl1 == tpl2"; -assert "tpl1 != tpl3"; -assert "list == list2"; -assert "list != list3"; +assert |one == one|; +assert |one == one|; +assert |one >= one|; +assert |two > one|; +assert |two >= two|; +assert |tpl1 == tpl2|; +assert |tpl1 != tpl3|; +assert |list == list2|; +assert |list != list3|; // Deep Comparisons let tpl4 = { @@ -40,17 +40,17 @@ let less = { foo = "bar" }; -assert "tpl4.inner == copy.inner"; -assert "tpl4.inner.fld == copy.inner.fld"; -assert "tpl4.lst == copy.lst"; -assert "tpl4.foo == copy.foo"; -assert "tpl4 == copy"; -assert "tpl4 != extra"; -assert "tpl4 != less"; +assert |tpl4.inner == copy.inner|; +assert |tpl4.inner.fld == copy.inner.fld|; +assert |tpl4.lst == copy.lst|; +assert |tpl4.foo == copy.foo|; +assert |tpl4 == copy|; +assert |tpl4 != extra|; +assert |tpl4 != less|; // Expression comparisons -assert "2 == 1+1"; -assert "(1+1) == 2"; -assert "(1+1) == (1+1)"; +assert |2 == 1+1|; +assert |(1+1) == 2|; +assert |(1+1) == (1+1)|; let want = "foo"; -assert "select want, 1, { foo=2, } == 2"; \ No newline at end of file +assert |select want, 1, { foo=2, } == 2|; \ No newline at end of file diff --git a/integration_tests/empty_test.ucg b/integration_tests/empty_test.ucg index 331b4af..d6772ee 100644 --- a/integration_tests/empty_test.ucg +++ b/integration_tests/empty_test.ucg @@ -2,4 +2,4 @@ let empty = NULL; let tpl = { foo = NULL, }; -assert "tpl.foo == empty"; \ No newline at end of file +assert |tpl.foo == empty|; \ No newline at end of file diff --git a/integration_tests/macros_test.ucg b/integration_tests/macros_test.ucg index abd48f3..81e94b0 100644 --- a/integration_tests/macros_test.ucg +++ b/integration_tests/macros_test.ucg @@ -13,10 +13,10 @@ let cplxmacro = macro(argint, argstr, argfloat) => { let simpleresult = simplemacro(1, 2, 3); let cplxresult = cplxmacro(1, "We", 3.0); -assert "simpleresult.field1 == 1"; -assert "simpleresult.field2 == 2"; -assert "simpleresult.field3 == 3"; +assert |simpleresult.field1 == 1|; +assert |simpleresult.field2 == 2|; +assert |simpleresult.field3 == 3|; -assert "cplxresult.field1 == 2"; -assert "cplxresult.field2 == \"We are here\""; -assert "cplxresult.field3 == 2.0"; \ No newline at end of file +assert |cplxresult.field1 == 2|; +assert |cplxresult.field2 == "We are here"|; +assert |cplxresult.field3 == 2.0|; \ No newline at end of file diff --git a/integration_tests/operator_precedence_test.ucg b/integration_tests/operator_precedence_test.ucg index 384bedd..1e99e6f 100644 --- a/integration_tests/operator_precedence_test.ucg +++ b/integration_tests/operator_precedence_test.ucg @@ -1,7 +1,7 @@ -assert "2 * 2 + 1 == 5"; -assert "2 + 2 * 3 == 8"; -assert "2 * (2 + 1) == 6"; -assert "2 * 2 + 1 > 4"; -assert "2 * 2 + 1 > 6"; -assert "2 * 2 + 1 >= 5"; -assert "2 * 2 + 1 <= 5"; \ No newline at end of file +assert |2 * 2 + 1 == 5|; +assert |2 + 2 * 3 == 8|; +assert |2 * (2 + 1) == 6|; +assert |2 * 2 + 1 > 4|; +assert |2 * 2 + 1 > 6|; +assert |2 * 2 + 1 >= 5|; +assert |2 * 2 + 1 <= 5|; \ No newline at end of file diff --git a/integration_tests/selectors_test.ucg b/integration_tests/selectors_test.ucg index ec7f995..207094d 100644 --- a/integration_tests/selectors_test.ucg +++ b/integration_tests/selectors_test.ucg @@ -10,10 +10,10 @@ let testmacro = macro(arg) => { output = arg, }; -assert "list.0 == 1"; -assert "list.1 == 2"; -assert "list.3 == 4"; -assert "tuple.field1 == 1"; -assert "tuple.field2 == 3"; -assert "tuple.deeplist.0 == \"foo\""; -assert "tuple.deeplist.1 == \"bar\""; \ No newline at end of file +assert |list.0 == 1|; +assert |list.1 == 2|; +assert |list.3 == 4|; +assert |tuple.field1 == 1|; +assert |tuple.field2 == 3|; +assert |tuple.deeplist.0 == "foo"|; +assert |tuple.deeplist.1 == "bar"|; \ No newline at end of file diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 31fd331..f3c3148 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -71,6 +71,7 @@ pub enum TokenType { WS, COMMENT, QUOTED, + PIPEQUOTE, DIGIT, BAREWORD, PUNCT, diff --git a/src/lib.rs b/src/lib.rs index ca0afef..ce4331c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -409,23 +409,15 @@ //! //! The assert statement defines an expression that must evaluate to either true or false. Assert statements are noops except //! during a validation compile. They give you a way to assert certains properties about your data and can be used as a form -//! of unit testting for your configurations. It starts with the assert keyword followed by a quoted string that is -//! itself a valid boolean ucg expression. +//! of unit testing for your configurations. It starts with the assert keyword followed by a valid boolean ucg expression +//! delimited by `|` characters. //! //! ```ucg -//! assert "host == \"www.example.com\""; -//! assert "select qa, 443, { +//! assert host == "www.example.com"; +//! assert |select qa, 443, { //! qa = 80, //! prod = 443, -//! } == 443"; -//! ``` -//! -//! It is a little bit awkward for strings since you have to escape their quotes. But you can work around it by -//! by storing the expectations in variables first and referencing them in the assert statement. -//! -//! ```ucg -//! let expected_host = "www.example.com"; -//! assert "host == expected_host"; +//! } == 443|; //! ``` //! //! When _test.ucg files are run in a validation run then ucg will output a log of all the assertions diff --git a/src/parse/mod.rs b/src/parse/mod.rs index 9d39c80..f9c21ab 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -838,7 +838,7 @@ named!(assert_statement, error::Error::new( "Invalid syntax for assert", error::ErrorType::ParseError, pos)), - match_type!(STR)) >> + match_type!(PIPEQUOTE)) >> punct!(";") >> (Statement::Assert(tok.clone())) ) diff --git a/src/parse/test.rs b/src/parse/test.rs index 358fa90..b1520eb 100644 --- a/src/parse/test.rs +++ b/src/parse/test.rs @@ -309,6 +309,20 @@ fn test_out_statement_parse() { ); } +#[test] +fn test_assert_statement_parse() { + assert_error!(out_statement("assert")); + assert_error!(out_statement("assert |")); + assert_error!(out_statement("assert |foo")); + assert_parse!( + assert_statement("assert |foo|;"), + Statement::Assert(Token { + pos: Position { line: 1, column: 8 }, + fragment: "foo".to_string(), + typ: TokenType::PIPEQUOTE + }) + ); +} #[test] fn test_expression_statement_parse() { assert_error!(expression_statement("foo")); diff --git a/src/tokenizer/mod.rs b/src/tokenizer/mod.rs index ad4b4bb..4e7a1bd 100644 --- a/src/tokenizer/mod.rs +++ b/src/tokenizer/mod.rs @@ -73,6 +73,20 @@ named!(strtok( Span ) -> Token, ) ); +named!(pipequotetok( Span ) -> Token, + do_parse!( + span: position!() >> + tag!("|") >> + frag: take_until!("|") >> + tag!("|") >> + (Token{ + typ: TokenType::PIPEQUOTE, + pos: Position::from(span), + fragment: frag.fragment.to_string(), + }) + ) +); + named!(barewordtok( Span ) -> Token, do_parse!( span: position!() >> @@ -336,6 +350,7 @@ named!(whitespace( Span ) -> Token, named!(token( Span ) -> Token, alt!( strtok | + pipequotetok | emptytok | // This must come before the barewordtok digittok | commatok | @@ -475,6 +490,14 @@ macro_rules! match_type { match_type!($i, STR => token_clone) }; + ($i:expr,PIPEQUOTE => $h:expr) => { + match_type!($i, TokenType::PIPEQUOTE, "Not a Pipe Quoted String", $h) + }; + + ($i:expr,PIPEQUOTE) => { + match_type!($i, PIPEQUOTE => token_clone) + }; + ($i:expr,DIGIT => $h:expr) => { match_type!($i, TokenType::DIGIT, "Not a DIGIT", $h) }; diff --git a/src/tokenizer/test.rs b/src/tokenizer/test.rs index be85938..5f1dec2 100644 --- a/src/tokenizer/test.rs +++ b/src/tokenizer/test.rs @@ -42,6 +42,16 @@ fn test_escape_quoted() { } } +#[test] +fn test_pipe_quoted() { + let result = pipequotetok(LocatedSpan::new("|foo|")); + assert!(result.is_done(), format!("result {:?} is not ok", result)); + if let nom::IResult::Done(_, tok) = result { + assert_eq!(tok.fragment, "foo".to_string()); + assert_eq!(tok.typ, TokenType::PIPEQUOTE); + } +} + #[test] fn test_string_with_escaping() { let result = strtok(LocatedSpan::new("\"foo \\\\ \\\"bar\""));