mirror of
https://github.com/zaphar/ucg.git
synced 2025-07-22 18:19:54 -04:00
FEATURE: Parsing support for assert statements.
This commit is contained in:
parent
5c0df5b538
commit
9674d6006f
@ -792,6 +792,9 @@ pub enum Statement {
|
|||||||
|
|
||||||
// Import a file.
|
// Import a file.
|
||||||
Import(ImportDef),
|
Import(ImportDef),
|
||||||
|
|
||||||
|
// Assert statement
|
||||||
|
Assert(Expression),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -505,6 +505,7 @@ impl Builder {
|
|||||||
|
|
||||||
fn build_stmt(&mut self, stmt: &Statement) -> Result<Rc<Val>, Box<Error>> {
|
fn build_stmt(&mut self, stmt: &Statement) -> Result<Rc<Val>, Box<Error>> {
|
||||||
match stmt {
|
match stmt {
|
||||||
|
&Statement::Assert(ref expr) => self.build_assert(expr),
|
||||||
&Statement::Let(ref def) => self.build_let(def),
|
&Statement::Let(ref def) => self.build_let(def),
|
||||||
&Statement::Import(ref def) => self.build_import(def),
|
&Statement::Import(ref def) => self.build_import(def),
|
||||||
&Statement::Expression(ref expr) => self.eval_expr(expr),
|
&Statement::Expression(ref expr) => self.eval_expr(expr),
|
||||||
@ -1110,6 +1111,21 @@ impl Builder {
|
|||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn build_assert(&self, expr: &Expression) -> Result<Rc<Val>, Box<Error>> {
|
||||||
|
let ok = try!(self.eval_expr(expr));
|
||||||
|
if let &Val::Boolean(b) = ok.as_ref() {
|
||||||
|
// record the assertion result.
|
||||||
|
if b {
|
||||||
|
// success!
|
||||||
|
} else {
|
||||||
|
// failure!
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// record an assertion type-failure result.
|
||||||
|
}
|
||||||
|
Ok(ok)
|
||||||
|
}
|
||||||
|
|
||||||
// Evals a single Expression in the context of a running Builder.
|
// Evals a single Expression in the context of a running Builder.
|
||||||
// It does not mutate the builders collected state at all.
|
// It does not mutate the builders collected state at all.
|
||||||
pub fn eval_expr(&self, expr: &Expression) -> Result<Rc<Val>, Box<Error>> {
|
pub fn eval_expr(&self, expr: &Expression) -> Result<Rc<Val>, Box<Error>> {
|
||||||
|
17
src/lib.rs
17
src/lib.rs
@ -48,6 +48,7 @@
|
|||||||
//!
|
//!
|
||||||
//! The following words are reserved in ucg and can't be used as named bindings.
|
//! The following words are reserved in ucg and can't be used as named bindings.
|
||||||
//!
|
//!
|
||||||
|
//! * assert
|
||||||
//! * true
|
//! * true
|
||||||
//! * false
|
//! * false
|
||||||
//! * let
|
//! * let
|
||||||
@ -371,7 +372,7 @@
|
|||||||
//!
|
//!
|
||||||
//! * Let statements
|
//! * Let statements
|
||||||
//!
|
//!
|
||||||
//! The let expression binds the result of any valid expression to a name. It starts with the `let` keyword and is followed by
|
//! The let statement binds the result of any valid expression to a name. It starts with the `let` keyword and is followed by
|
||||||
//! the name of the binding, an `=`, and a valid ucg expression.
|
//! the name of the binding, an `=`, and a valid ucg expression.
|
||||||
//!
|
//!
|
||||||
//! ```ucg
|
//! ```ucg
|
||||||
@ -389,6 +390,20 @@
|
|||||||
//!
|
//!
|
||||||
//! let mysqlconf = dbconfigs.mysql;
|
//! let mysqlconf = dbconfigs.mysql;
|
||||||
//! ```
|
//! ```
|
||||||
|
//!
|
||||||
|
//! * Assert statement
|
||||||
|
//!
|
||||||
|
//! 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 valid boolean ucg expression.
|
||||||
|
//!
|
||||||
|
//! ```ucg
|
||||||
|
//! assert host == "www.example.com";
|
||||||
|
//! assert select qa, 443, {
|
||||||
|
//! qa = 80,
|
||||||
|
//! prod = 443,
|
||||||
|
//! } == 443;
|
||||||
|
//! ```
|
||||||
|
|
||||||
// The following is necessary to allow the macros in tokenizer and parse modules
|
// The following is necessary to allow the macros in tokenizer and parse modules
|
||||||
// to succeed.
|
// to succeed.
|
||||||
|
@ -896,9 +896,24 @@ named!(import_statement<TokenIter, Statement, error::Error>,
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
named!(assert_statement<TokenIter, Statement, error::Error>,
|
||||||
|
do_parse!(
|
||||||
|
word!("assert") >>
|
||||||
|
pos: pos >>
|
||||||
|
expr: add_return_error!(
|
||||||
|
nom::ErrorKind::Custom(
|
||||||
|
error::Error::new(
|
||||||
|
"Invalid syntax for assert",
|
||||||
|
error::ErrorType::ParseError, pos)),
|
||||||
|
expression) >>
|
||||||
|
(Statement::Assert(expr))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
//trace_macros!(true);
|
//trace_macros!(true);
|
||||||
fn statement(i: TokenIter) -> nom::IResult<TokenIter, Statement, error::Error> {
|
fn statement(i: TokenIter) -> nom::IResult<TokenIter, Statement, error::Error> {
|
||||||
return alt_peek!(i,
|
return alt_peek!(i,
|
||||||
|
word!("assert") => assert_statement |
|
||||||
word!("import") => import_statement |
|
word!("import") => import_statement |
|
||||||
word!("let") => let_statement |
|
word!("let") => let_statement |
|
||||||
expression_statement);
|
expression_statement);
|
||||||
|
@ -142,7 +142,7 @@ macro_rules! do_tag_tok {
|
|||||||
}
|
}
|
||||||
|
|
||||||
named!(emptytok( Span ) -> Token,
|
named!(emptytok( Span ) -> Token,
|
||||||
do_tag_tok!(TokenType::EMPTY, "NULL")
|
do_tag_tok!(TokenType::EMPTY, "NULL", WS)
|
||||||
);
|
);
|
||||||
|
|
||||||
named!(commatok( Span ) -> Token,
|
named!(commatok( Span ) -> Token,
|
||||||
@ -233,10 +233,6 @@ named!(fatcommatok( Span ) -> Token,
|
|||||||
do_tag_tok!(TokenType::PUNCT, "=>")
|
do_tag_tok!(TokenType::PUNCT, "=>")
|
||||||
);
|
);
|
||||||
|
|
||||||
named!(lettok( Span ) -> Token,
|
|
||||||
do_tag_tok!(TokenType::BAREWORD, "let", WS)
|
|
||||||
);
|
|
||||||
|
|
||||||
named!(selecttok( Span ) -> Token,
|
named!(selecttok( Span ) -> Token,
|
||||||
do_tag_tok!(TokenType::BAREWORD, "select", WS)
|
do_tag_tok!(TokenType::BAREWORD, "select", WS)
|
||||||
);
|
);
|
||||||
@ -245,10 +241,18 @@ named!(macrotok( Span ) -> Token,
|
|||||||
do_tag_tok!(TokenType::BAREWORD, "macro", WS)
|
do_tag_tok!(TokenType::BAREWORD, "macro", WS)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
named!(lettok( Span ) -> Token,
|
||||||
|
do_tag_tok!(TokenType::BAREWORD, "let", WS)
|
||||||
|
);
|
||||||
|
|
||||||
named!(importtok( Span ) -> Token,
|
named!(importtok( Span ) -> Token,
|
||||||
do_tag_tok!(TokenType::BAREWORD, "import", WS)
|
do_tag_tok!(TokenType::BAREWORD, "import", WS)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
named!(asserttok( Span ) -> Token,
|
||||||
|
do_tag_tok!(TokenType::BAREWORD, "assert", WS)
|
||||||
|
);
|
||||||
|
|
||||||
named!(astok( Span ) -> Token,
|
named!(astok( Span ) -> Token,
|
||||||
do_tag_tok!(TokenType::BAREWORD, "as", WS)
|
do_tag_tok!(TokenType::BAREWORD, "as", WS)
|
||||||
);
|
);
|
||||||
@ -356,6 +360,7 @@ named!(token( Span ) -> Token,
|
|||||||
booleantok |
|
booleantok |
|
||||||
lettok |
|
lettok |
|
||||||
selecttok |
|
selecttok |
|
||||||
|
asserttok |
|
||||||
macrotok |
|
macrotok |
|
||||||
importtok |
|
importtok |
|
||||||
astok |
|
astok |
|
||||||
|
@ -12,6 +12,16 @@ fn test_empty_token() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_assert_token() {
|
||||||
|
let result = asserttok(LocatedSpan::new("assert "));
|
||||||
|
assert!(result.is_done(), format!("result {:?} is not done", result));
|
||||||
|
if let nom::IResult::Done(_, tok) = result {
|
||||||
|
assert_eq!(tok.fragment, "assert");
|
||||||
|
assert_eq!(tok.typ, TokenType::BAREWORD);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_escape_quoted() {
|
fn test_escape_quoted() {
|
||||||
let result = escapequoted(LocatedSpan::new("foo \\\"bar\""));
|
let result = escapequoted(LocatedSpan::new("foo \\\"bar\""));
|
||||||
@ -93,7 +103,7 @@ fn test_lteqtok() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_tokenize_one_of_each() {
|
fn test_tokenize_one_of_each() {
|
||||||
let result = tokenize(LocatedSpan::new(
|
let result = tokenize(LocatedSpan::new(
|
||||||
"let import macro select as => [ ] { } ; = % / * \
|
"map filter assert let import macro select as => [ ] { } ; = % / * \
|
||||||
+ - . ( ) , 1 . foo \"bar\" // comment\n ; true false == < > <= >= !=",
|
+ - . ( ) , 1 . foo \"bar\" // comment\n ; true false == < > <= >= !=",
|
||||||
));
|
));
|
||||||
assert!(result.is_ok(), format!("result {:?} is not ok", result));
|
assert!(result.is_ok(), format!("result {:?} is not ok", result));
|
||||||
@ -101,8 +111,8 @@ fn test_tokenize_one_of_each() {
|
|||||||
for (i, t) in v.iter().enumerate() {
|
for (i, t) in v.iter().enumerate() {
|
||||||
println!("{}: {:?}", i, t);
|
println!("{}: {:?}", i, t);
|
||||||
}
|
}
|
||||||
assert_eq!(v.len(), 35);
|
assert_eq!(v.len(), 38);
|
||||||
assert_eq!(v[34].typ, TokenType::END);
|
assert_eq!(v[37].typ, TokenType::END);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user