mirror of
https://github.com/zekexiao/pocketlang.git
synced 2025-02-11 07:00:58 +08:00
un used functions were removed (#174)
In addition tail call is not enabled for functions that doesn't return the last call (caused a bug).
This commit is contained in:
parent
5e67403a9d
commit
cda9cc75ca
@ -224,7 +224,6 @@ typedef enum {
|
||||
PREC_TERM, // + -
|
||||
PREC_FACTOR, // * / %
|
||||
PREC_UNARY, // - ! ~ not
|
||||
PREC_CHAIN_CALL, // ->
|
||||
PREC_CALL, // ()
|
||||
PREC_SUBSCRIPT, // []
|
||||
PREC_ATTRIB, // .index
|
||||
@ -1126,7 +1125,6 @@ static void exprName(Compiler* compiler);
|
||||
static void exprOr(Compiler* compiler);
|
||||
static void exprAnd(Compiler* compiler);
|
||||
|
||||
static void exprChainCall(Compiler* compiler);
|
||||
static void exprBinaryOp(Compiler* compiler);
|
||||
static void exprUnaryOp(Compiler* compiler);
|
||||
|
||||
@ -1165,7 +1163,7 @@ GrammarRule rules[] = { // Prefix Infix Infix Precedence
|
||||
/* TK_AMP */ { NULL, exprBinaryOp, PREC_BITWISE_AND },
|
||||
/* TK_PIPE */ { NULL, exprBinaryOp, PREC_BITWISE_OR },
|
||||
/* TK_CARET */ { NULL, exprBinaryOp, PREC_BITWISE_XOR },
|
||||
/* TK_ARROW */ { NULL, exprChainCall, PREC_CHAIN_CALL },
|
||||
/* TK_ARROW */ NO_RULE,
|
||||
/* TK_PLUS */ { NULL, exprBinaryOp, PREC_TERM },
|
||||
/* TK_MINUS */ { exprUnaryOp, exprBinaryOp, PREC_TERM },
|
||||
/* TK_STAR */ { NULL, exprBinaryOp, PREC_FACTOR },
|
||||
@ -1300,7 +1298,9 @@ static void exprName(Compiler* compiler) {
|
||||
compiler->new_local = true;
|
||||
|
||||
// Ensure the local variable's index is equals to the stack top index.
|
||||
ASSERT((compiler->stack_size - 1) == index, OOPS);
|
||||
// If the compiler has errors, we cannot and don't have to assert.
|
||||
ASSERT(compiler->has_errors || (compiler->stack_size - 1) == index,
|
||||
OOPS);
|
||||
|
||||
// We don't need to call emitStoreVariable (which emit STORE_LOCAL)
|
||||
// because the local is already at it's location in the stack, we just
|
||||
@ -1408,34 +1408,6 @@ void exprAnd(Compiler* compiler) {
|
||||
compiler->is_last_call = false;
|
||||
}
|
||||
|
||||
static void exprChainCall(Compiler* compiler) {
|
||||
skipNewLines(compiler);
|
||||
parsePrecedence(compiler, (Precedence)(PREC_CHAIN_CALL + 1));
|
||||
emitOpcode(compiler, OP_SWAP); // Swap the data with the function.
|
||||
|
||||
int argc = 1; // The initial data.
|
||||
|
||||
if (match(compiler, TK_LBRACE)) {
|
||||
if (!match(compiler, TK_RBRACE)) {
|
||||
do {
|
||||
skipNewLines(compiler);
|
||||
compileExpression(compiler);
|
||||
skipNewLines(compiler);
|
||||
argc++;
|
||||
} while (match(compiler, TK_COMMA));
|
||||
consume(compiler, TK_RBRACE, "Expected '}' after chain call "
|
||||
"parameter list.");
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: ensure argc < 256 (MAX_ARGC) 1byte.
|
||||
|
||||
emitOpcode(compiler, OP_CALL);
|
||||
emitByte(compiler, argc);
|
||||
|
||||
compiler->is_last_call = false;
|
||||
}
|
||||
|
||||
static void exprBinaryOp(Compiler* compiler) {
|
||||
TokenType op = compiler->previous.type;
|
||||
skipNewLines(compiler);
|
||||
@ -2102,16 +2074,6 @@ static int compileFunction(Compiler* compiler, FuncType fn_type) {
|
||||
if (fn_type != FN_NATIVE) {
|
||||
compileBlockBody(compiler, BLOCK_FUNC);
|
||||
|
||||
// Tail call optimization disabled at debug mode.
|
||||
if (compiler->options && !compiler->options->debug) {
|
||||
if (compiler->is_last_call) {
|
||||
ASSERT(_FN->opcodes.count >= 3, OOPS); // OP_CALL, argc, OP_POP
|
||||
ASSERT(_FN->opcodes.data[_FN->opcodes.count - 1] == OP_POP, OOPS);
|
||||
ASSERT(_FN->opcodes.data[_FN->opcodes.count - 3] == OP_CALL, OOPS);
|
||||
_FN->opcodes.data[_FN->opcodes.count - 3] = OP_TAIL_CALL;
|
||||
}
|
||||
}
|
||||
|
||||
consume(compiler, TK_END, "Expected 'end' after function definition end.");
|
||||
compilerExitBlock(compiler); // Parameter depth.
|
||||
emitFunctionEnd(compiler);
|
||||
|
26
src/pk_vm.c
26
src/pk_vm.c
@ -439,32 +439,6 @@ static void* defaultRealloc(void* memory, size_t new_size, void* user_data) {
|
||||
return realloc(memory, new_size);
|
||||
}
|
||||
|
||||
// If failed to resolve it'll return false. Parameter [result] should be points
|
||||
// to the string which is the path that has to be resolved and once it resolved
|
||||
// the provided result's string's on_done() will be called and, it's string
|
||||
// will be updated with the new resolved path string.
|
||||
static inline bool resolveScriptPath(PKVM* vm, PkStringPtr* path_string) {
|
||||
if (vm->config.resolve_path_fn == NULL) return true;
|
||||
|
||||
const char* path = path_string->string;
|
||||
PkStringPtr resolved;
|
||||
|
||||
Fiber* fiber = vm->fiber;
|
||||
if (fiber == NULL || fiber->frame_count <= 0) {
|
||||
// fiber == NULL => vm haven't started yet and it's a root script.
|
||||
resolved = vm->config.resolve_path_fn(vm, NULL, path);
|
||||
} else {
|
||||
const Function* fn = fiber->frames[fiber->frame_count - 1].fn;
|
||||
resolved = vm->config.resolve_path_fn(vm, fn->owner->path->data, path);
|
||||
}
|
||||
|
||||
// Done with the last string and update it with the new string.
|
||||
if (path_string->on_done != NULL) path_string->on_done(vm, *path_string);
|
||||
*path_string = resolved;
|
||||
|
||||
return path_string->string != NULL;
|
||||
}
|
||||
|
||||
// Import and return Script object as Var. If the script is imported and
|
||||
// compiled here it'll set [is_new_script] to true otherwise (using the cached
|
||||
// script) set to false.
|
||||
|
@ -50,6 +50,15 @@ if a and b then assert(false) end
|
||||
if a or b then val = 42 else assert(false) end assert(val == 42)
|
||||
if get_true() or false then val = 12 end assert(val == 12)
|
||||
|
||||
## Newer logical or implementation.
|
||||
## a or b === if (a) return a else return b
|
||||
## a and b === if (!a) return a else return b
|
||||
a = null; b = [1, 2, 3]
|
||||
assert((a or b) == [1, 2, 3]) ## Note == has higher precedence than and/or.
|
||||
assert((a and b) == null)
|
||||
assert((b or a) == [1, 2, 3])
|
||||
assert((a or b) == b or a)
|
||||
|
||||
## Recursive to_string list/map
|
||||
l = [1]
|
||||
list_append(l, l)
|
||||
|
@ -14,14 +14,12 @@ end
|
||||
assert(val == 'defined after the call')
|
||||
|
||||
## Chain call tests. (concatenative programming)
|
||||
|
||||
def fn1(data) return '[fn1:' + data + ']' end
|
||||
def fn2(data, suffix) return '[fn2:' + data + '|' + suffix + ']' end
|
||||
def fn3(data) return '[fn3:' + data + ']' end
|
||||
|
||||
result = 'data' -> fn1 -> fn2{'suff'} -> fn3
|
||||
assert(result == '[fn3:[fn2:[fn1:data]|suff]]')
|
||||
|
||||
## Chain call no more supported (unnecessary feature).
|
||||
#def fn1(data) return '[fn1:' + data + ']' end
|
||||
#def fn2(data, suffix) return '[fn2:' + data + '|' + suffix + ']' end
|
||||
#def fn3(data) return '[fn3:' + data + ']' end
|
||||
#result = 'data' -> fn1 -> fn2{'suff'} -> fn3
|
||||
#assert(result == '[fn3:[fn2:[fn1:data]|suff]]')
|
||||
# str_lower(s) function refactored to s.lower attribute.
|
||||
#result = ' tEST+InG ' -> str_strip -> str_lower
|
||||
#assert(result == 'test+ing')
|
||||
|
@ -11,7 +11,7 @@ from path import *
|
||||
|
||||
import "basics.pk" ## will import all
|
||||
import "controlflow.pk" as if_test
|
||||
from "functions.pk" import fn1, fn2 as f2, fn3
|
||||
from "functions.pk" import f1, f2 as fn2, f3
|
||||
|
||||
import "class.pk" as class_
|
||||
assert(class_.Vec(42, 3.14).y == 3.14)
|
||||
|
@ -71,6 +71,11 @@ for i in 0..100
|
||||
assert(i%2 == f(i))
|
||||
end
|
||||
|
||||
## The below function caused a bug after compile time stack size calculation
|
||||
## bug fixed. Added here to check for regression.
|
||||
def h()
|
||||
f() ## <-- Not tail called here (anymore) because it's not returned.
|
||||
end
|
||||
|
||||
# If we got here, that means all test were passed.
|
||||
print('All TESTS PASSED')
|
||||
|
Loading…
Reference in New Issue
Block a user