FEATURE: Add the not operator.

This commit is contained in:
Jeremy Wall 2019-01-24 16:53:02 -06:00
parent 2409f0c64e
commit 016e4a22db
7 changed files with 65 additions and 2 deletions

View File

@ -219,4 +219,19 @@ let name = "foo";
assert { assert {
ok = (name) in {foo="bar"}, ok = (name) in {foo="bar"},
desc = "(name) in {foo=\"bar\"} works", desc = "(name) in {foo=\"bar\"} works",
};
assert {
ok = not false,
desc = "not false is true",
};
assert {
ok = not true == false,
desc = "not true is false",
};
assert {
ok = not not true,
desc = "double negatives are positive",
}; };

View File

@ -601,11 +601,18 @@ pub struct FailDef {
pub message: Box<Expression>, pub message: Box<Expression>,
} }
#[derive(Debug, PartialEq, Clone)]
pub struct NotDef {
pub pos: Position,
pub expr: Box<Expression>,
}
/// Encodes a ucg expression. Expressions compute a value from. /// Encodes a ucg expression. Expressions compute a value from.
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
pub enum Expression { pub enum Expression {
// Base Expression // Base Expression
Simple(Value), Simple(Value),
Not(NotDef),
// Binary expressions // Binary expressions
Binary(BinaryOpDef), Binary(BinaryOpDef),
@ -646,6 +653,7 @@ impl Expression {
&Expression::Include(ref def) => &def.pos, &Expression::Include(ref def) => &def.pos,
&Expression::Import(ref def) => &def.pos, &Expression::Import(ref def) => &def.pos,
&Expression::Fail(ref def) => &def.pos, &Expression::Fail(ref def) => &def.pos,
&Expression::Not(ref def) => &def.pos,
} }
} }
} }
@ -695,6 +703,9 @@ impl fmt::Display for Expression {
&Expression::Fail(_) => { &Expression::Fail(_) => {
write!(w, "<Fail>")?; write!(w, "<Fail>")?;
} }
&Expression::Not(ref def) => {
write!(w, "!{}", def.expr)?;
}
} }
Ok(()) Ok(())
} }

View File

@ -114,6 +114,9 @@ impl<'a> AstWalker<'a> {
Expression::Import(_) | Expression::Include(_) | Expression::Fail(_) => { Expression::Import(_) | Expression::Include(_) | Expression::Fail(_) => {
//noop //noop
} }
Expression::Not(ref mut def) => {
self.walk_expression(def.expr.as_mut());
}
} }
} }

View File

@ -1786,7 +1786,7 @@ impl<'a> FileBuilder<'a> {
} else { } else {
Err(Box::new(error::BuildError::new( Err(Box::new(error::BuildError::new(
format!( format!(
"Expected string form message but got {}", "Expected string for message but got {}",
def.message.as_ref() def.message.as_ref()
), ),
error::ErrorType::TypeFail, error::ErrorType::TypeFail,
@ -1794,6 +1794,21 @@ impl<'a> FileBuilder<'a> {
))) )))
}; };
} }
&Expression::Not(ref def) => {
let val = self.eval_expr(&def.expr, scope)?;
return if let Val::Boolean(b) = val.as_ref() {
Ok(Rc::new(Val::Boolean(!b)))
} else {
Err(Box::new(error::BuildError::new(
format!(
"Expected boolean for expression but got {}",
def.expr.as_ref()
),
error::ErrorType::TypeFail,
def.expr.pos().clone(),
)))
};
}
} }
} }
} }

View File

@ -667,6 +667,19 @@ make_fn!(
) )
); );
make_fn!(
not_expression<SliceIter<Token>, Expression>,
do_each!(
pos => pos,
_ => word!("not"),
expr => must!(wrap_err!(expression, "Expected failure message")),
(Expression::Not(NotDef{
pos: pos,
expr: Box::new(expr),
}))
)
);
fn unprefixed_expression(input: SliceIter<Token>) -> ParseResult<Expression> { fn unprefixed_expression(input: SliceIter<Token>) -> ParseResult<Expression> {
let _input = input.clone(); let _input = input.clone();
either!( either!(
@ -685,6 +698,7 @@ make_fn!(
trace_parse!(func_op_expression), trace_parse!(func_op_expression),
trace_parse!(macro_expression), trace_parse!(macro_expression),
trace_parse!(import_expression), trace_parse!(import_expression),
trace_parse!(not_expression),
trace_parse!(fail_expression), trace_parse!(fail_expression),
trace_parse!(module_expression), trace_parse!(module_expression),
trace_parse!(select_expression), trace_parse!(select_expression),

View File

@ -278,6 +278,10 @@ make_fn!(istok<OffsetStrIter, Token>,
do_text_token_tok!(TokenType::BAREWORD, "is", WS) do_text_token_tok!(TokenType::BAREWORD, "is", WS)
); );
make_fn!(nottok<OffsetStrIter, Token>,
do_text_token_tok!(TokenType::BAREWORD, "not", WS)
);
make_fn!(failtok<OffsetStrIter, Token>, make_fn!(failtok<OffsetStrIter, Token>,
do_text_token_tok!(TokenType::BAREWORD, "fail", WS) do_text_token_tok!(TokenType::BAREWORD, "fail", WS)
); );
@ -417,6 +421,7 @@ fn token<'a>(input: OffsetStrIter<'a>) -> Result<OffsetStrIter<'a>, Token> {
booleantok, booleantok,
intok, intok,
istok, istok,
nottok,
lettok, lettok,
outtok, outtok,
selecttok, selecttok,

View File

@ -30,7 +30,7 @@ let asserts = module{
// descriptive message to use in output. // descriptive message to use in output.
desc=mod.default_description, desc=mod.default_description,
} => { } => {
let ok = mod.test != true; let ok = not mod.test;
let desc = "@" % (mod.desc); let desc = "@" % (mod.desc);