FEATURE: More ergonomic syntax for asserts.

This commit is contained in:
Jeremy Wall 2018-08-20 22:51:55 -05:00
parent b87d75c5c7
commit a7a32d56b2
11 changed files with 95 additions and 55 deletions

View File

@ -16,15 +16,15 @@ let list = [1, 2, 3];
let list2 = list; let list2 = list;
let list3 = [1, 2]; let list3 = [1, 2];
assert "one == one"; assert |one == one|;
assert "one == one"; assert |one == one|;
assert "one >= one"; assert |one >= one|;
assert "two > one"; assert |two > one|;
assert "two >= two"; assert |two >= two|;
assert "tpl1 == tpl2"; assert |tpl1 == tpl2|;
assert "tpl1 != tpl3"; assert |tpl1 != tpl3|;
assert "list == list2"; assert |list == list2|;
assert "list != list3"; assert |list != list3|;
// Deep Comparisons // Deep Comparisons
let tpl4 = { let tpl4 = {
@ -40,17 +40,17 @@ let less = {
foo = "bar" foo = "bar"
}; };
assert "tpl4.inner == copy.inner"; assert |tpl4.inner == copy.inner|;
assert "tpl4.inner.fld == copy.inner.fld"; assert |tpl4.inner.fld == copy.inner.fld|;
assert "tpl4.lst == copy.lst"; assert |tpl4.lst == copy.lst|;
assert "tpl4.foo == copy.foo"; assert |tpl4.foo == copy.foo|;
assert "tpl4 == copy"; assert |tpl4 == copy|;
assert "tpl4 != extra"; assert |tpl4 != extra|;
assert "tpl4 != less"; assert |tpl4 != less|;
// Expression comparisons // Expression comparisons
assert "2 == 1+1"; assert |2 == 1+1|;
assert "(1+1) == 2"; assert |(1+1) == 2|;
assert "(1+1) == (1+1)"; assert |(1+1) == (1+1)|;
let want = "foo"; let want = "foo";
assert "select want, 1, { foo=2, } == 2"; assert |select want, 1, { foo=2, } == 2|;

View File

@ -2,4 +2,4 @@ let empty = NULL;
let tpl = { let tpl = {
foo = NULL, foo = NULL,
}; };
assert "tpl.foo == empty"; assert |tpl.foo == empty|;

View File

@ -13,10 +13,10 @@ let cplxmacro = macro(argint, argstr, argfloat) => {
let simpleresult = simplemacro(1, 2, 3); let simpleresult = simplemacro(1, 2, 3);
let cplxresult = cplxmacro(1, "We", 3.0); let cplxresult = cplxmacro(1, "We", 3.0);
assert "simpleresult.field1 == 1"; assert |simpleresult.field1 == 1|;
assert "simpleresult.field2 == 2"; assert |simpleresult.field2 == 2|;
assert "simpleresult.field3 == 3"; assert |simpleresult.field3 == 3|;
assert "cplxresult.field1 == 2"; assert |cplxresult.field1 == 2|;
assert "cplxresult.field2 == \"We are here\""; assert |cplxresult.field2 == "We are here"|;
assert "cplxresult.field3 == 2.0"; assert |cplxresult.field3 == 2.0|;

View File

@ -1,7 +1,7 @@
assert "2 * 2 + 1 == 5"; assert |2 * 2 + 1 == 5|;
assert "2 + 2 * 3 == 8"; assert |2 + 2 * 3 == 8|;
assert "2 * (2 + 1) == 6"; assert |2 * (2 + 1) == 6|;
assert "2 * 2 + 1 > 4"; assert |2 * 2 + 1 > 4|;
assert "2 * 2 + 1 > 6"; assert |2 * 2 + 1 > 6|;
assert "2 * 2 + 1 >= 5"; assert |2 * 2 + 1 >= 5|;
assert "2 * 2 + 1 <= 5"; assert |2 * 2 + 1 <= 5|;

View File

@ -10,10 +10,10 @@ let testmacro = macro(arg) => {
output = arg, output = arg,
}; };
assert "list.0 == 1"; assert |list.0 == 1|;
assert "list.1 == 2"; assert |list.1 == 2|;
assert "list.3 == 4"; assert |list.3 == 4|;
assert "tuple.field1 == 1"; assert |tuple.field1 == 1|;
assert "tuple.field2 == 3"; assert |tuple.field2 == 3|;
assert "tuple.deeplist.0 == \"foo\""; assert |tuple.deeplist.0 == "foo"|;
assert "tuple.deeplist.1 == \"bar\""; assert |tuple.deeplist.1 == "bar"|;

View File

@ -71,6 +71,7 @@ pub enum TokenType {
WS, WS,
COMMENT, COMMENT,
QUOTED, QUOTED,
PIPEQUOTE,
DIGIT, DIGIT,
BAREWORD, BAREWORD,
PUNCT, PUNCT,

View File

@ -409,23 +409,15 @@
//! //!
//! The assert statement defines an expression that must evaluate to either true or false. Assert statements are noops except //! 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 //! 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 //! of unit testing for your configurations. It starts with the assert keyword followed by a valid boolean ucg expression
//! itself a valid boolean ucg expression. //! delimited by `|` characters.
//! //!
//! ```ucg //! ```ucg
//! assert "host == \"www.example.com\""; //! assert host == "www.example.com";
//! assert "select qa, 443, { //! assert |select qa, 443, {
//! qa = 80, //! qa = 80,
//! prod = 443, //! prod = 443,
//! } == 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";
//! ``` //! ```
//! //!
//! When _test.ucg files are run in a validation run then ucg will output a log of all the assertions //! When _test.ucg files are run in a validation run then ucg will output a log of all the assertions

View File

@ -838,7 +838,7 @@ named!(assert_statement<TokenIter, Statement, error::Error>,
error::Error::new( error::Error::new(
"Invalid syntax for assert", "Invalid syntax for assert",
error::ErrorType::ParseError, pos)), error::ErrorType::ParseError, pos)),
match_type!(STR)) >> match_type!(PIPEQUOTE)) >>
punct!(";") >> punct!(";") >>
(Statement::Assert(tok.clone())) (Statement::Assert(tok.clone()))
) )

View File

@ -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] #[test]
fn test_expression_statement_parse() { fn test_expression_statement_parse() {
assert_error!(expression_statement("foo")); assert_error!(expression_statement("foo"));

View File

@ -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, named!(barewordtok( Span ) -> Token,
do_parse!( do_parse!(
span: position!() >> span: position!() >>
@ -336,6 +350,7 @@ named!(whitespace( Span ) -> Token,
named!(token( Span ) -> Token, named!(token( Span ) -> Token,
alt!( alt!(
strtok | strtok |
pipequotetok |
emptytok | // This must come before the barewordtok emptytok | // This must come before the barewordtok
digittok | digittok |
commatok | commatok |
@ -475,6 +490,14 @@ macro_rules! match_type {
match_type!($i, STR => token_clone) 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) => { ($i:expr,DIGIT => $h:expr) => {
match_type!($i, TokenType::DIGIT, "Not a DIGIT", $h) match_type!($i, TokenType::DIGIT, "Not a DIGIT", $h)
}; };

View File

@ -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] #[test]
fn test_string_with_escaping() { fn test_string_with_escaping() {
let result = strtok(LocatedSpan::new("\"foo \\\\ \\\"bar\"")); let result = strtok(LocatedSpan::new("\"foo \\\\ \\\"bar\""));