Add console.assert

This commit is contained in:
Pranav Gaddamadugu 2022-08-17 15:27:11 -07:00
parent 1d55322276
commit 615cc61e9e
17 changed files with 31 additions and 106 deletions

View File

@ -216,6 +216,7 @@ pub trait StatementReconstructor: ExpressionReconstructor {
fn reconstruct_console(&mut self, input: ConsoleStatement) -> Statement {
Statement::Console(ConsoleStatement {
function: match input.function {
ConsoleFunction::Assert(expr) => ConsoleFunction::Assert(self.reconstruct_expression(expr).0),
ConsoleFunction::AssertEq(left, right) => ConsoleFunction::AssertEq(
self.reconstruct_expression(left).0,
self.reconstruct_expression(right).0,

View File

@ -154,6 +154,9 @@ pub trait StatementVisitor<'a>: ExpressionVisitor<'a> {
fn visit_console(&mut self, input: &'a ConsoleStatement) {
match &input.function {
ConsoleFunction::Assert(expr) => {
self.visit_expression(expr, &Default::default());
}
ConsoleFunction::AssertEq(left, right) => {
self.visit_expression(left, &Default::default());
self.visit_expression(right, &Default::default());

View File

@ -22,11 +22,11 @@ use std::fmt;
/// A console logging function to invoke.
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
pub enum ConsoleFunction {
/// A `console.assert_eq(expr1, expr2)` call to invoke,
/// asserting that the operands are equal.
/// A `console.assert(expr)` call to invoke, asserting that the operands are equal
Assert(Expression),
/// A `console.assert_eq(expr1, expr2)` call to invoke, asserting that the operands are equal.
AssertEq(Expression, Expression),
/// A `console.assert_neq(expr1, expr2)` call to invoke,
/// asserting that the operands are not equal.
/// A `console.assert_neq(expr1, expr2)` call to invoke, asserting that the operands are not equal.
AssertNeq(Expression, Expression),
/// Dummy statement for the parser.
Dummy,
@ -35,6 +35,7 @@ pub enum ConsoleFunction {
impl fmt::Display for ConsoleFunction {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
ConsoleFunction::Assert(expr) => write!(f, "assert({})", expr),
ConsoleFunction::AssertEq(expr1, expr2) => write!(f, "assert_eq({}, {})", expr1, expr2),
ConsoleFunction::AssertNeq(expr1, expr2) => write!(f, "assert_neq({}, {})", expr1, expr2),
ConsoleFunction::Dummy => write!(f, "dummy"),

View File

@ -78,7 +78,7 @@ impl fmt::Display for Type {
Type::Boolean => write!(f, "boolean"),
Type::Field => write!(f, "field"),
Type::Group => write!(f, "group"),
Type::Identifier(ref variable) => write!(f, "circuit {}", variable),
Type::Identifier(ref variable) => write!(f, "{}", variable),
Type::Integer(ref integer_type) => write!(f, "{}", integer_type),
Type::Scalar => write!(f, "scalar"),
Type::String => write!(f, "string"),

View File

@ -178,6 +178,12 @@ impl ParserContext<'_> {
self.expect(&Token::Dot)?;
let identifier = self.expect_identifier()?;
let (span, function) = match identifier.name {
sym::assert => {
self.expect(&Token::LeftParen)?;
let expr = self.parse_expression()?;
self.expect(&Token::RightParen)?;
(keyword + expr.span(), ConsoleFunction::Assert(expr))
}
sym::assert_eq => {
self.expect(&Token::LeftParen)?;
let left = self.parse_expression()?;

View File

@ -89,7 +89,7 @@ impl<'a> CodeGenerator<'a> {
let mut generate_assert_instruction = |name: &str, left: &'a Expression, right: &'a Expression| {
let (left_operand, left_instructions) = self.visit_expression(left);
let (right_operand, right_instructions) = self.visit_expression(right);
let assert_instruction = format!(" {} {}, {});", name, left_operand, right_operand);
let assert_instruction = format!(" {} {} {};", name, left_operand, right_operand);
// Concatenate the instructions.
let mut instructions = left_instructions;
@ -99,6 +99,13 @@ impl<'a> CodeGenerator<'a> {
instructions
};
match &input.function {
ConsoleFunction::Assert(expr) => {
let (operand, mut instructions) = self.visit_expression(expr);
let assert_instruction = format!(" assert.eq {} true;", operand);
instructions.push_str(&assert_instruction);
instructions
}
ConsoleFunction::AssertEq(left, right) => generate_assert_instruction("assert.eq", left, right),
ConsoleFunction::AssertNeq(left, right) => generate_assert_instruction("assert.neq", left, right),
ConsoleFunction::Dummy => String::new(),

View File

@ -192,6 +192,10 @@ impl<'a> StatementVisitor<'a> for TypeChecker<'a> {
fn visit_console(&mut self, input: &'a ConsoleStatement) {
match &input.function {
ConsoleFunction::Assert(expr) => {
let type_ = self.visit_expression(expr, &Some(Type::Boolean));
self.assert_bool_type(&type_, expr.span());
}
ConsoleFunction::AssertEq(left, right) | ConsoleFunction::AssertNeq(left, right) => {
let t1 = self.visit_expression(left, &None);
let t2 = self.visit_expression(right, &None);

View File

@ -106,7 +106,9 @@ impl<'a> TypeChecker<'a> {
/// Emits an error if the two given types are not equal.
pub(crate) fn check_eq_types(&self, t1: &Option<Type>, t2: &Option<Type>, span: Span) {
match (t1, t2) {
(Some(t1), Some(t2)) if t1 != t2 => self.emit_err(TypeCheckerError::type_should_be(t1, t2, span)),
(Some(t1), Some(t2)) if !Type::eq_flat(t1, t2) => {
self.emit_err(TypeCheckerError::type_should_be(t1, t2, span))
}
(Some(type_), None) | (None, Some(type_)) => {
self.emit_err(TypeCheckerError::type_should_be("no type", type_, span))
}

View File

@ -1,11 +0,0 @@
/*
namespace: Compile
expectation: Pass
input_file: inputs/dummy.in
*/
@program
function main(y: bool) -> bool {
console.error("hello error");
return y == true;
}

View File

@ -1,11 +0,0 @@
/*
namespace: Compile
expectation: Pass
input_file: inputs/dummy.in
*/
@program
function main(y: bool) -> bool {
console.log("hello world");
return y == true;
}

View File

@ -1,17 +0,0 @@
/*
namespace: Compile
expectation: Pass
input_file:
- inputs/input_unequal.in
- inputs/input_equal.in
*/
// Conditionally add two u32 integers and log the result to the console.
@program
function main(a: u32, b: u32) -> bool {
if a == b {
console.log("{}=={}", a, b); // This line should not fail.
}
return a == b;
}

View File

@ -1,8 +0,0 @@
/*
namespace: Compile
expectation: Fail
*/
function main() {
console.log( hello );
}

View File

@ -1,11 +0,0 @@
/*
namespace: Compile
expectation: Pass
input_file: inputs/dummy.in
*/
@program
function main(y: bool) -> bool {
console.log("a = {}", y);
return y == true;
}

View File

@ -1,11 +0,0 @@
/*
namespace: Compile
expectation: Pass
input_file: inputs/dummy.in
*/
@program
function main(y: bool) -> bool {
console.log("{}", 1u32);
return y == true;
}

View File

@ -1,11 +0,0 @@
/*
namespace: Compile
expectation: Pass
input_file: inputs/dummy.in
*/
@program
function main(y: bool) -> bool {
console.log("{} {}", 1u32, true);
return y == true;
}

View File

@ -1,8 +0,0 @@
/*
namespace: Compile
expectation: Fail
*/
function main() {
console.log("{}", a);
}

View File

@ -1,11 +0,0 @@
/*
namespace: Compile
expectation: Fail
input_file: inputs/dummy.in
*/
function main(y: bool) -> bool {
let hello: string = "hello world";
console.log(hello);
return y == true;
}