mirror of
https://github.com/zekexiao/pocketlang.git
synced 2025-02-11 15:16:41 +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.
|
// To implement.
|
||||||
|
|
||||||
[ ] and/or operator implementation.
|
|
||||||
[ ] Relative file import.
|
[ ] Relative file import.
|
||||||
[ ] Remove resolve path for the root module.
|
[ ] Remove resolve path for the root module.
|
||||||
|
|
||||||
|
@ -11,11 +11,11 @@
|
|||||||
void errorPrint(PKVM* vm, PKErrorType type, const char* file, int line,
|
void errorPrint(PKVM* vm, PKErrorType type, const char* file, int line,
|
||||||
const char* message) {
|
const char* message) {
|
||||||
if (type == PK_ERROR_COMPILE) {
|
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) {
|
} else if (type == PK_ERROR_RUNTIME) {
|
||||||
fprintf(stderr, "Error: %s\n", message);
|
fprintf(stderr, "Error: %s\n", message);
|
||||||
} else if (type == PK_ERROR_STACKTRACE) {
|
} 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;
|
consumed = true;
|
||||||
|
|
||||||
if (!consumed) {
|
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 void emitOpcode(Compiler* compiler, Opcode opcode);
|
||||||
static int emitByte(Compiler* compiler, int byte);
|
static int emitByte(Compiler* compiler, int byte);
|
||||||
static int emitShort(Compiler* compiler, int arg);
|
static int emitShort(Compiler* compiler, int arg);
|
||||||
|
static void patchJump(Compiler* compiler, int addr_index);
|
||||||
static int compilerAddConstant(Compiler* compiler, Var value);
|
static int compilerAddConstant(Compiler* compiler, Var value);
|
||||||
|
|
||||||
static int compilerAddVariable(Compiler* compiler, const char* name,
|
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 exprFunc(Compiler* compiler, bool can_assign);
|
||||||
static void exprName(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 exprBinaryOp(Compiler* compiler, bool can_assign);
|
||||||
static void exprUnaryOp(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_NULL */ { exprValue, NULL, NO_INFIX },
|
||||||
/* TK_SELF */ { exprValue, NULL, NO_INFIX },
|
/* TK_SELF */ { exprValue, NULL, NO_INFIX },
|
||||||
/* TK_IN */ { NULL, exprBinaryOp, PREC_IN },
|
/* TK_IN */ { NULL, exprBinaryOp, PREC_IN },
|
||||||
/* TK_AND */ { NULL, exprBinaryOp, PREC_LOGICAL_AND },
|
/* TK_AND */ { NULL, exprAnd, PREC_LOGICAL_AND },
|
||||||
/* TK_OR */ { NULL, exprBinaryOp, PREC_LOGICAL_OR },
|
/* TK_OR */ { NULL, exprOr, PREC_LOGICAL_OR },
|
||||||
/* TK_NOT */ { exprUnaryOp, NULL, PREC_LOGICAL_NOT },
|
/* TK_NOT */ { exprUnaryOp, NULL, PREC_LOGICAL_NOT },
|
||||||
/* TK_TRUE */ { exprValue, NULL, NO_INFIX },
|
/* TK_TRUE */ { exprValue, NULL, NO_INFIX },
|
||||||
/* TK_FALSE */ { exprValue, NULL, NO_INFIX },
|
/* TK_FALSE */ { exprValue, NULL, NO_INFIX },
|
||||||
@ -1051,14 +1058,14 @@ static void exprName(Compiler* compiler, bool can_assign) {
|
|||||||
} else {
|
} else {
|
||||||
// TODO: The name could be a function which hasn't been defined at this point.
|
// TODO: The name could be a function which hasn't been defined at this point.
|
||||||
// Implement opcode to push a named variable.
|
// 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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (result.type) {
|
switch (result.type) {
|
||||||
case NAME_LOCAL_VAR:
|
case NAME_LOCAL_VAR:
|
||||||
case NAME_GLOBAL_VAR:
|
case NAME_GLOBAL_VAR: {
|
||||||
|
|
||||||
if (can_assign && matchAssignment(parser)) {
|
if (can_assign && matchAssignment(parser)) {
|
||||||
TokenType assignment = parser->previous.type;
|
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);
|
emitPushVariable(compiler, result.index, result.type == NAME_GLOBAL_VAR);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
case NAME_FUNCTION:
|
case NAME_FUNCTION:
|
||||||
emitOpcode(compiler, OP_PUSH_FN);
|
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) {
|
static void exprBinaryOp(Compiler* compiler, bool can_assign) {
|
||||||
TokenType op = compiler->parser.previous.type;
|
TokenType op = compiler->parser.previous.type;
|
||||||
skipNewLines(&compiler->parser);
|
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_SRIGHT: emitOpcode(compiler, OP_BIT_RSHIFT); break;
|
||||||
case TK_SLEFT: emitOpcode(compiler, OP_BIT_LSHIFT); break;
|
case TK_SLEFT: emitOpcode(compiler, OP_BIT_LSHIFT); break;
|
||||||
case TK_IN: emitOpcode(compiler, OP_IN); 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:
|
default:
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
@ -1442,11 +1496,11 @@ static void emitConstant(Compiler* compiler, Var value) {
|
|||||||
|
|
||||||
// Update the jump offset.
|
// Update the jump offset.
|
||||||
static void patchJump(Compiler* compiler, int addr_index) {
|
static void patchJump(Compiler* compiler, int addr_index) {
|
||||||
int jump_to = (int)_FN->opcodes.count - addr_index - 2;
|
int offset = (int)_FN->opcodes.count - addr_index - 2;
|
||||||
ASSERT(jump_to < MAX_JUMP, "Too large address offset to jump to.");
|
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] = (offset >> 8) & 0xff;
|
||||||
_FN->opcodes.data[addr_index + 1] = jump_to & 0xff;
|
_FN->opcodes.data[addr_index + 1] = offset & 0xff;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Jump back to the start of the loop.
|
// 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,
|
NameSearchResult result = compilerSearchName(compiler, name,
|
||||||
name_length);
|
name_length);
|
||||||
if (result.type != NAME_NOT_DEFINED) {
|
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);
|
name);
|
||||||
// Not returning here as to complete for skip cascaded errors.
|
// Not returning here as to complete for skip cascaded errors.
|
||||||
}
|
}
|
||||||
|
@ -171,7 +171,7 @@ void coreAssert(PKVM* vm) {
|
|||||||
msg = (String*)AS_OBJ(ARG2);
|
msg = (String*)AS_OBJ(ARG2);
|
||||||
}
|
}
|
||||||
vmPushTempRef(vm, &msg->_super);
|
vmPushTempRef(vm, &msg->_super);
|
||||||
vm->fiber->error = stringFormat(vm, "Assertion failed: @.", msg);
|
vm->fiber->error = stringFormat(vm, "Assertion failed: '@'.", msg);
|
||||||
vmPopTempRef(vm);
|
vmPopTempRef(vm);
|
||||||
} else {
|
} else {
|
||||||
vm->fiber->error = newString(vm, "Assertion failed.");
|
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)
|
@ -4,6 +4,7 @@ set files=( ^
|
|||||||
lang\basics.pk ^
|
lang\basics.pk ^
|
||||||
lang\import.pk ^
|
lang\import.pk ^
|
||||||
lang\if.pk ^
|
lang\if.pk ^
|
||||||
|
lang\logical.pk ^
|
||||||
^
|
^
|
||||||
examples\fib.pk ^
|
examples\fib.pk ^
|
||||||
examples\prime.pk ^
|
examples\prime.pk ^
|
||||||
|
Loading…
Reference in New Issue
Block a user