mirror of
https://github.com/zekexiao/pocketlang.git
synced 2025-02-11 07:00:58 +08:00
Merge pull request #21 from ThakeeNathees/and-or-expr
and or expression parsing implemented
This commit is contained in:
commit
57d0d7757e
@ -1,7 +1,6 @@
|
||||
|
||||
// To implement.
|
||||
|
||||
[ ] and/or operator implementation.
|
||||
[ ] Relative file import.
|
||||
[ ] Remove resolve path for the root module.
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
}
|
||||
|
@ -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
16
test/lang/logical.pk
Normal 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)
|
@ -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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user