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:
Thakee Nathees 2022-04-03 21:03:12 +05:30 committed by GitHub
parent 5e67403a9d
commit cda9cc75ca
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 25 additions and 77 deletions

View File

@ -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);

View File

@ -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.

View File

@ -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)

View File

@ -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')

View File

@ -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)

View File

@ -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')