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

@ -220,3 +220,18 @@ assert {
ok = (name) in {foo="bar"},
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>,
}
#[derive(Debug, PartialEq, Clone)]
pub struct NotDef {
pub pos: Position,
pub expr: Box<Expression>,
}
/// Encodes a ucg expression. Expressions compute a value from.
#[derive(Debug, PartialEq, Clone)]
pub enum Expression {
// Base Expression
Simple(Value),
Not(NotDef),
// Binary expressions
Binary(BinaryOpDef),
@ -646,6 +653,7 @@ impl Expression {
&Expression::Include(ref def) => &def.pos,
&Expression::Import(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(_) => {
write!(w, "<Fail>")?;
}
&Expression::Not(ref def) => {
write!(w, "!{}", def.expr)?;
}
}
Ok(())
}

View File

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

View File

@ -1786,7 +1786,7 @@ impl<'a> FileBuilder<'a> {
} else {
Err(Box::new(error::BuildError::new(
format!(
"Expected string form message but got {}",
"Expected string for message but got {}",
def.message.as_ref()
),
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> {
let _input = input.clone();
either!(
@ -685,6 +698,7 @@ make_fn!(
trace_parse!(func_op_expression),
trace_parse!(macro_expression),
trace_parse!(import_expression),
trace_parse!(not_expression),
trace_parse!(fail_expression),
trace_parse!(module_expression),
trace_parse!(select_expression),

View File

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

View File

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