feat(tvix/eval): implement assert operator

This implements `assert`, which evaluates an expression and aborts
evaluation if the value is not `true`.

At this point we should introduce eval-failed-* tests; probably
asserting against some representation of the error enum?

Change-Id: If54c8f616d89b829c1860a4835dde60a2cd70d7a
Reviewed-on: https://cl.tvl.fyi/c/depot/+/6230
Reviewed-by: grfn <grfn@gws.fyi>
Reviewed-by: sterni <sternenseemann@systemli.org>
Tested-by: BuildkiteCI
This commit is contained in:
Vincent Ambo 2022-08-16 15:53:35 +03:00 committed by tazjin
parent 8fa3bc7137
commit a00e4730a5
4 changed files with 27 additions and 0 deletions

View file

@ -170,6 +170,11 @@ impl Compiler {
self.compile_with(node)
}
rnix::SyntaxKind::NODE_ASSERT => {
let node = rnix::types::Assert::cast(node).unwrap();
self.compile_assert(node)
}
kind => panic!("visiting unsupported node: {:?}", kind),
}
}
@ -795,6 +800,17 @@ impl Compiler {
self.compile(node.body().unwrap())
}
fn compile_assert(&mut self, node: rnix::types::Assert) -> EvalResult<()> {
// Compile the assertion condition to leave its value on the stack.
self.compile(node.condition().unwrap())?;
self.chunk.push_op(OpCode::OpAssert);
// The runtime will abort evaluation at this point if the
// assertion failed, if not the body simply continues on like
// normal.
self.compile(node.body().unwrap())
}
// Emit the literal string value of an identifier. Required for
// several operations related to attribute sets, where identifiers
// are used as string keys.

View file

@ -33,6 +33,8 @@ pub enum Error {
UnknownDynamicVariable(String),
ParseErrors(Vec<rnix::parser::ParseError>),
AssertionFailed,
}
impl Display for Error {

View file

@ -72,4 +72,7 @@ pub enum OpCode {
// Close scopes while leaving their expression value around.
OpCloseScope(usize), // number of locals to pop
// Asserts stack top is a boolean, and true.
OpAssert,
}

View file

@ -304,6 +304,12 @@ impl VM {
return Err(Error::UnknownDynamicVariable(ident.to_string()));
}
OpCode::OpAssert => {
if !self.pop().as_bool()? {
return Err(Error::AssertionFailed);
}
}
}
#[cfg(feature = "disassembler")]