Merge pull request #21 from ThakeeNathees/and-or-expr

and or expression parsing implemented
This commit is contained in:
Thakee Nathees 2021-05-16 00:32:50 +05:30 committed by GitHub
commit 57d0d7757e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 98 additions and 28 deletions

View File

@ -1,7 +1,6 @@
// To implement.
[ ] and/or operator implementation.
[ ] Relative file import.
[ ] Remove resolve path for the root module.

View File

@ -11,11 +11,11 @@
void errorPrint(PKVM* vm, PKErrorType type, const char* file, int line,
const char* message) {
if (type == PK_ERROR_COMPILE) {
fprintf(stderr, "Error: %s\n\tat %s:%i\n", message, file, line);
fprintf(stderr, "Error: %s\n at %s:%i\n", message, file, line);
} else if (type == PK_ERROR_RUNTIME) {
fprintf(stderr, "Error: %s\n", message);
} else if (type == PK_ERROR_STACKTRACE) {
fprintf(stderr, " [%s:%i] %s()\n", file, line, message);
fprintf(stderr, " %s() [%s:%i]\n", message, file, line);
}
}

View File

@ -763,7 +763,10 @@ static void consumeStartBlock(Parser* parser, TokenType delimiter) {
consumed = true;
if (!consumed) {
parseError(parser, "Expected enter block with newline or 'do'.");
const char* msg;
if (delimiter == TK_DO) msg = "Expected enter block with newline or 'do'.";
else msg = "Expected enter block with newline or 'then'.";
parseError(parser, msg);
}
}
@ -870,6 +873,7 @@ static NameSearchResult compilerSearchName(Compiler* compiler,
static void emitOpcode(Compiler* compiler, Opcode opcode);
static int emitByte(Compiler* compiler, int byte);
static int emitShort(Compiler* compiler, int arg);
static void patchJump(Compiler* compiler, int addr_index);
static int compilerAddConstant(Compiler* compiler, Var value);
static int compilerAddVariable(Compiler* compiler, const char* name,
@ -885,6 +889,9 @@ static void exprLiteral(Compiler* compiler, bool can_assign);
static void exprFunc(Compiler* compiler, bool can_assign);
static void exprName(Compiler* compiler, bool can_assign);
static void exprOr(Compiler* compiler, bool can_assign);
static void exprAnd(Compiler* compiler, bool can_assign);
static void exprBinaryOp(Compiler* compiler, bool can_assign);
static void exprUnaryOp(Compiler* compiler, bool can_assign);
@ -951,8 +958,8 @@ GrammarRule rules[] = { // Prefix Infix Infix Precedence
/* TK_NULL */ { exprValue, NULL, NO_INFIX },
/* TK_SELF */ { exprValue, NULL, NO_INFIX },
/* TK_IN */ { NULL, exprBinaryOp, PREC_IN },
/* TK_AND */ { NULL, exprBinaryOp, PREC_LOGICAL_AND },
/* TK_OR */ { NULL, exprBinaryOp, PREC_LOGICAL_OR },
/* TK_AND */ { NULL, exprAnd, PREC_LOGICAL_AND },
/* TK_OR */ { NULL, exprOr, PREC_LOGICAL_OR },
/* TK_NOT */ { exprUnaryOp, NULL, PREC_LOGICAL_NOT },
/* TK_TRUE */ { exprValue, NULL, NO_INFIX },
/* TK_FALSE */ { exprValue, NULL, NO_INFIX },
@ -1051,14 +1058,14 @@ static void exprName(Compiler* compiler, bool can_assign) {
} else {
// TODO: The name could be a function which hasn't been defined at this point.
// Implement opcode to push a named variable.
parseError(parser, "Name \"%.*s\" is not defined.", name_len, name_start);
parseError(parser, "Name '%.*s' is not defined.", name_len, name_start);
}
return;
}
switch (result.type) {
case NAME_LOCAL_VAR:
case NAME_GLOBAL_VAR:
case NAME_GLOBAL_VAR: {
if (can_assign && matchAssignment(parser)) {
TokenType assignment = parser->previous.type;
@ -1087,6 +1094,7 @@ static void exprName(Compiler* compiler, bool can_assign) {
emitPushVariable(compiler, result.index, result.type == NAME_GLOBAL_VAR);
}
return;
}
case NAME_FUNCTION:
emitOpcode(compiler, OP_PUSH_FN);
@ -1103,6 +1111,56 @@ static void exprName(Compiler* compiler, bool can_assign) {
}
}
/* a or b: | a and b:
|
(...) | (...)
.-- jump_if [offset] | .-- jump_if_not [offset]
| (...) | | (...)
|-- jump_if [offset] | |-- jump_if_not [offset]
| push false | | push true
.--+-- jump [offset] | .--+-- jump [offset]
| '-> push true | | '-> push false
'----> (...) | '----> (...)
*/
void exprOr(Compiler* compiler, bool can_assign) {
emitOpcode(compiler, OP_JUMP_IF);
int true_offset_a = emitShort(compiler, 0xffff); //< Will be patched.
parsePrecedence(compiler, PREC_LOGICAL_OR);
emitOpcode(compiler, OP_JUMP_IF);
int true_offset_b = emitShort(compiler, 0xffff); //< Will be patched.
emitOpcode(compiler, OP_PUSH_FALSE);
emitOpcode(compiler, OP_JUMP);
int end_offset = emitShort(compiler, 0xffff); //< Will be patched.
patchJump(compiler, true_offset_a);
patchJump(compiler, true_offset_b);
emitOpcode(compiler, OP_PUSH_TRUE);
patchJump(compiler, end_offset);
}
void exprAnd(Compiler* compiler, bool can_assign) {
emitOpcode(compiler, OP_JUMP_IF_NOT);
int false_offset_a = emitShort(compiler, 0xffff); //< Will be patched.
parsePrecedence(compiler, PREC_LOGICAL_AND);
emitOpcode(compiler, OP_JUMP_IF_NOT);
int false_offset_b = emitShort(compiler, 0xffff); //< Will be patched.
emitOpcode(compiler, OP_PUSH_TRUE);
emitOpcode(compiler, OP_JUMP);
int end_offset = emitShort(compiler, 0xffff); //< Will be patched.
patchJump(compiler, false_offset_a);
patchJump(compiler, false_offset_b);
emitOpcode(compiler, OP_PUSH_FALSE);
patchJump(compiler, end_offset);
}
static void exprBinaryOp(Compiler* compiler, bool can_assign) {
TokenType op = compiler->parser.previous.type;
skipNewLines(&compiler->parser);
@ -1127,10 +1185,6 @@ static void exprBinaryOp(Compiler* compiler, bool can_assign) {
case TK_SRIGHT: emitOpcode(compiler, OP_BIT_RSHIFT); break;
case TK_SLEFT: emitOpcode(compiler, OP_BIT_LSHIFT); break;
case TK_IN: emitOpcode(compiler, OP_IN); break;
// TODO: it doesn't work that way.
case TK_AND: emitOpcode(compiler, OP_AND); break;
case TK_OR: emitOpcode(compiler, OP_OR); break;
default:
UNREACHABLE();
}
@ -1442,11 +1496,11 @@ static void emitConstant(Compiler* compiler, Var value) {
// Update the jump offset.
static void patchJump(Compiler* compiler, int addr_index) {
int jump_to = (int)_FN->opcodes.count - addr_index - 2;
ASSERT(jump_to < MAX_JUMP, "Too large address offset to jump to.");
int offset = (int)_FN->opcodes.count - addr_index - 2;
ASSERT(offset < MAX_JUMP, "Too large address offset to jump to.");
_FN->opcodes.data[addr_index] = (jump_to >> 8) & 0xff;
_FN->opcodes.data[addr_index + 1] = jump_to & 0xff;
_FN->opcodes.data[addr_index] = (offset >> 8) & 0xff;
_FN->opcodes.data[addr_index + 1] = offset & 0xff;
}
// Jump back to the start of the loop.
@ -1486,7 +1540,7 @@ static int compileFunction(Compiler* compiler, FuncType fn_type) {
NameSearchResult result = compilerSearchName(compiler, name,
name_length);
if (result.type != NAME_NOT_DEFINED) {
parseError(&compiler->parser, "Name %.*s already exists.", name_length,
parseError(&compiler->parser, "Name '%.*s' already exists.", name_length,
name);
// Not returning here as to complete for skip cascaded errors.
}

View File

@ -171,7 +171,7 @@ void coreAssert(PKVM* vm) {
msg = (String*)AS_OBJ(ARG2);
}
vmPushTempRef(vm, &msg->_super);
vm->fiber->error = stringFormat(vm, "Assertion failed: @.", msg);
vm->fiber->error = stringFormat(vm, "Assertion failed: '@'.", msg);
vmPopTempRef(vm);
} else {
vm->fiber->error = newString(vm, "Assertion failed.");

16
test/lang/logical.pk Normal file
View File

@ -0,0 +1,16 @@
## Logical statement test
val = 0
a = false; b = true;
if a and b then assert(false) end
if a or b then va
else assert(false, '') end
get_true = func return true end
if get_true() or false
val = 12
end
assert(val == 12)

View File

@ -1,20 +1,21 @@
@echo off
set files=( ^
lang\basics.pk ^
lang\import.pk ^
lang\if.pk ^
^
examples\fib.pk ^
examples\prime.pk ^
set files=( ^
lang\basics.pk ^
lang\import.pk ^
lang\if.pk ^
lang\logical.pk ^
^
examples\fib.pk ^
examples\prime.pk ^
)
set errorlevel=0
for %%f in %files% do (
echo Testing %%f
pocket %%f
if %errorlevel% neq 0 goto :FAILED
echo Testing %%f
pocket %%f
if %errorlevel% neq 0 goto :FAILED
)
goto :SUCCESS