From a65f2dcf81caea8b694180566d51798f8a212635 Mon Sep 17 00:00:00 2001 From: "K. Lange" Date: Mon, 1 Mar 2021 11:55:51 +0900 Subject: [PATCH] Add 'assert' statement --- src/compiler.c | 23 +++++++++++++++++++++++ src/exceptions.c | 1 + src/rline.c | 2 +- src/scanner.c | 6 +++++- src/scanner.h | 1 + src/vm.h | 1 + test/testAssert.krk | 14 ++++++++++++++ test/testAssert.krk.expect | 2 ++ 8 files changed, 48 insertions(+), 2 deletions(-) create mode 100644 test/testAssert.krk create mode 100644 test/testAssert.krk.expect diff --git a/src/compiler.c b/src/compiler.c index f02d2d6..83f12d3 100644 --- a/src/compiler.c +++ b/src/compiler.c @@ -1734,6 +1734,27 @@ static void delStatement() { inDel = 0; } +static void assertStatement() { + expression(); + int elseJump = emitJump(OP_JUMP_IF_TRUE); + + KrkToken assertionError = syntheticToken("AssertionError"); + size_t ind = identifierConstant(&assertionError); + EMIT_CONSTANT_OP(OP_GET_GLOBAL, ind); + int args = 0; + + if (match(TOKEN_COMMA)) { + expression(); + args = 1; + } + + EMIT_CONSTANT_OP(OP_CALL, args); + emitByte(OP_RAISE); + + patchJump(elseJump); + emitByte(OP_POP); +} + static void statement() { if (match(TOKEN_EOL) || match(TOKEN_EOF)) { return; /* Meaningless blank line */ @@ -1766,6 +1787,8 @@ _anotherSimpleStatement: continueStatement(); } else if (match(TOKEN_DEL)) { delStatement(); + } else if (match(TOKEN_ASSERT)) { + assertStatement(); } else if (match(TOKEN_PASS)) { /* Do nothing. */ } else { diff --git a/src/exceptions.c b/src/exceptions.c index d19ed67..3e5a4dc 100644 --- a/src/exceptions.c +++ b/src/exceptions.c @@ -158,6 +158,7 @@ void _createAndBind_exceptions(void) { ADD_EXCEPTION_CLASS(vm.exceptions->keyboardInterrupt, "KeyboardInterrupt", vm.exceptions->baseException); ADD_EXCEPTION_CLASS(vm.exceptions->zeroDivisionError, "ZeroDivisionError", vm.exceptions->baseException); ADD_EXCEPTION_CLASS(vm.exceptions->notImplementedError, "NotImplementedError", vm.exceptions->baseException); + ADD_EXCEPTION_CLASS(vm.exceptions->assertionError, "AssertionError", vm.exceptions->baseException); ADD_EXCEPTION_CLASS(vm.exceptions->syntaxError, "SyntaxError", vm.exceptions->baseException); krk_defineNative(&vm.exceptions->syntaxError->methods, ".__str__", _syntaxerror_str); krk_finalizeClass(vm.exceptions->syntaxError); diff --git a/src/rline.c b/src/rline.c index 3d35bb2..9ac53dc 100644 --- a/src/rline.c +++ b/src/rline.c @@ -544,7 +544,7 @@ char * syn_krk_keywords[] = { "and","class","def","else","for","if","in","import","del", "let","not","or","return","while","try","except","raise", "continue","break","as","from","elif","lambda","with","is", - "pass", + "pass","assert", NULL }; diff --git a/src/scanner.c b/src/scanner.c index 6f0cdc1..f1ce95c 100644 --- a/src/scanner.c +++ b/src/scanner.c @@ -197,7 +197,11 @@ static KrkTokenType identifierType() { switch (*scanner.start) { case 'a': if (MORE(1)) switch(scanner.start[1]) { case 'n': return checkKeyword(2, "d", TOKEN_AND); - case 's': return checkKeyword(2, "", TOKEN_AS); + case 's': if (MORE(2)) { + return checkKeyword(2, "sert", TOKEN_ASSERT); + } else { + return checkKeyword(2, "", TOKEN_AS); + } } break; case 'b': if (MORE(1)) return checkKeyword(1, "reak", TOKEN_BREAK); else if (scanner.start[1] == '\'' || scanner.start[1] == '"') return TOKEN_PREFIX_B; diff --git a/src/scanner.h b/src/scanner.h index aba1b9a..19b17b0 100644 --- a/src/scanner.h +++ b/src/scanner.h @@ -90,6 +90,7 @@ typedef enum { TOKEN_AS, TOKEN_FROM, TOKEN_LAMBDA, + TOKEN_ASSERT, TOKEN_WITH, TOKEN_PREFIX_B, diff --git a/src/vm.h b/src/vm.h index dda57fe..678fd7b 100644 --- a/src/vm.h +++ b/src/vm.h @@ -120,6 +120,7 @@ struct Exceptions { KrkClass * zeroDivisionError; /**< @exception ZeroDivisionError A mathematical function attempted to divide by zero. */ KrkClass * notImplementedError; /**< @exception NotImplementedError The method is not implemented, either for the given arguments or in general. */ KrkClass * syntaxError; /**< @exception SyntaxError The compiler encountered an unrecognized or invalid source code input. */ + KrkClass * assertionError; /**< @exception AssertionError An @c assert statement failed. */ }; /** diff --git a/test/testAssert.krk b/test/testAssert.krk new file mode 100644 index 0000000..870fa83 --- /dev/null +++ b/test/testAssert.krk @@ -0,0 +1,14 @@ + + +assert 1 + 1 +assert True, "Test" + +try: + assert False +except AssertionError: + print("Pass") + +try: + assert False, "with a message" +except AssertionError as e: + print("msg =", str(e)) diff --git a/test/testAssert.krk.expect b/test/testAssert.krk.expect new file mode 100644 index 0000000..8a262b7 --- /dev/null +++ b/test/testAssert.krk.expect @@ -0,0 +1,2 @@ +Pass +msg = with a message