chain call implemented

This commit is contained in:
Thakee Nathees 2021-05-16 12:35:54 +05:30
parent 57d0d7757e
commit c2f67d0b48
11 changed files with 229 additions and 72 deletions

View File

@ -1,6 +1,7 @@
// To implement.
[ ] Resolve function name (called before defined).
[ ] Relative file import.
[ ] Remove resolve path for the root module.

View File

@ -62,6 +62,7 @@ typedef enum {
TK_AMP, // &
TK_PIPE, // |
TK_CARET, // ^
TK_ARROW, // ->
TK_PLUS, // +
TK_MINUS, // -
@ -221,6 +222,7 @@ typedef enum {
PREC_TERM, // + -
PREC_FACTOR, // * / %
PREC_UNARY, // - ! ~
PREC_CHAIN_CALL, // ->
PREC_CALL, // ()
PREC_SUBSCRIPT, // []
PREC_ATTRIB, // .index
@ -623,7 +625,13 @@ static void lexToken(Parser* parser) {
return;
case '-':
setNextTwoCharToken(parser, '=', TK_MINUS, TK_MINUSEQ);
if (matchChar(parser, '=')) {
setNextToken(parser, TK_MINUSEQ); // '-='
} else if (matchChar(parser, '>')) {
setNextToken(parser, TK_ARROW); // '->'
} else {
setNextToken(parser, TK_MINUS); // '-'
}
return;
case '*':
@ -892,6 +900,7 @@ 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 exprChainCall(Compiler* compiler, bool can_assign);
static void exprBinaryOp(Compiler* compiler, bool can_assign);
static void exprUnaryOp(Compiler* compiler, bool can_assign);
@ -930,6 +939,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_PLUS */ { NULL, exprBinaryOp, PREC_TERM },
/* TK_MINUS */ { exprUnaryOp, exprBinaryOp, PREC_TERM },
/* TK_STAR */ { NULL, exprBinaryOp, PREC_FACTOR },
@ -1016,7 +1026,7 @@ static void emitPushVariable(Compiler* compiler, int index, bool global) {
static void exprLiteral(Compiler* compiler, bool can_assign) {
Token* value = &compiler->parser.previous;
int index = compilerAddConstant(compiler, value->value);
emitOpcode(compiler, OP_CONSTANT);
emitOpcode(compiler, OP_PUSH_CONSTANT);
emitShort(compiler, index);
}
@ -1161,6 +1171,30 @@ void exprAnd(Compiler* compiler, bool can_assign) {
patchJump(compiler, end_offset);
}
static void exprChainCall(Compiler* compiler, bool can_assign) {
skipNewLines(&compiler->parser);
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->parser, TK_LBRACE)) {
if (!match(&compiler->parser, TK_RBRACE)) {
do {
skipNewLines(&compiler->parser);
compileExpression(compiler);
skipNewLines(&compiler->parser);
argc++;
} while (match(&compiler->parser, TK_COMMA));
consume(&compiler->parser, TK_RBRACE, "Expected '}' after chain call"
"parameter list.");
}
}
emitOpcode(compiler, OP_CALL);
emitShort(compiler, argc);
}
static void exprBinaryOp(Compiler* compiler, bool can_assign) {
TokenType op = compiler->parser.previous.type;
skipNewLines(&compiler->parser);
@ -1490,7 +1524,7 @@ static void emitOpcode(Compiler* compiler, Opcode opcode) {
// make one.
static void emitConstant(Compiler* compiler, Var value) {
int index = compilerAddConstant(compiler, value);
emitOpcode(compiler, OP_CONSTANT);
emitOpcode(compiler, OP_PUSH_CONSTANT);
emitShort(compiler, index);
}
@ -1610,7 +1644,7 @@ static int compileFunction(Compiler* compiler, FuncType fn_type) {
compilerExitBlock(compiler); // Parameter depth.
#if DEBUG_DUMP_COMPILED_CODE
dumpInstructions(compiler->vm, compiler->func->ptr);
dumpFunctionCode(compiler->vm, compiler->func->ptr);
#endif
compiler->func = compiler->func->outer_func;

View File

@ -5,9 +5,11 @@
#include "core.h"
#include <ctype.h>
#include <math.h>
#include <time.h>
#include "utils.h"
#include "var.h"
#include "vm.h"
@ -84,6 +86,20 @@ static inline bool validateIndex(PKVM* vm, int32_t index, int32_t size,
return true;
}
// Check if [var] is string for argument at [arg_ind]. If not set error and
// return false.
static bool validateArgString(PKVM* vm, Var var, String** value, int arg_ind) {
if (!IS_OBJ(var) || AS_OBJ(var)->type != OBJ_STRING) {
String* str_arg = toString(vm, VAR_NUM((double)arg_ind), false);
vmPushTempRef(vm, &str_arg->_super);
vm->fiber->error = stringFormat(vm, "Expected a string at argument @.",
str_arg, false);
vmPopTempRef(vm);
}
*value = (String*)AS_OBJ(var);
return true;
}
/*****************************************************************************/
/* BUILTIN FUNCTIONS API */
/*****************************************************************************/
@ -91,7 +107,7 @@ static inline bool validateIndex(PKVM* vm, int32_t index, int32_t size,
// Argument getter (1 based).
#define ARG(n) vm->fiber->ret[n]
// Convinent macro
// Convinent macros.
#define ARG1 ARG(1)
#define ARG2 ARG(2)
#define ARG3 ARG(3)
@ -216,6 +232,49 @@ void corePrint(PKVM* vm) {
vm->config.write_fn(vm, "\n");
}
// string functions
// ----------------
void coreStrLower(PKVM* vm) {
String* str;
if (!validateArgString(vm, ARG1, &str, 1)) return;
String* result = newStringLength(vm, str->data, str->length);
char* data = result->data;
for (; *data; ++data) *data = tolower(*data);
// Since the string is modified re-hash it.
result->hash = utilHashString(result->data);
RET(VAR_OBJ(&result->_super));
}
void coreStrUpper(PKVM* vm) {
String* str;
if (!validateArgString(vm, ARG1, &str, 1)) return;
String* result = newStringLength(vm, str->data, str->length);
char* data = result->data;
for (; *data; ++data) *data = toupper(*data);
// Since the string is modified re-hash it.
result->hash = utilHashString(result->data);
RET(VAR_OBJ(&result->_super));
}
void coreStrStrip(PKVM* vm) {
String* str;
if (!validateArgString(vm, ARG1, &str, 1)) return;
const char* start = str->data;
while (*start && isspace(*start)) start++;
if (*start == '\0') RET(VAR_OBJ(&newStringLength(vm, NULL, 0)->_super));
const char* end = str->data + str->length - 1;
while (isspace(*end)) end--;
RET(VAR_OBJ(&newStringLength(vm, start, end - start + 1)->_super));
}
/*****************************************************************************/
/* CORE LIBRARY METHODS */
/*****************************************************************************/
@ -303,6 +362,11 @@ void initializeCore(PKVM* vm) {
INITALIZE_BUILTIN_FN("to_string", coreToString, 1);
INITALIZE_BUILTIN_FN("print", corePrint, -1);
// string functions.
INITALIZE_BUILTIN_FN("str_lower", coreStrLower, 1);
INITALIZE_BUILTIN_FN("str_upper", coreStrUpper, 1);
INITALIZE_BUILTIN_FN("str_strip", coreStrStrip, 1);
// Make STD scripts.
Script* std; // A temporary pointer to the current std script.
Function* fn; // A temporary pointer to the allocated function function.
@ -671,12 +735,15 @@ Var varGetSubscript(PKVM* vm, Var on, Var key) {
{
Var value = mapGet((Map*)obj, key);
if (IS_UNDEF(value)) {
String* key_str = toString(vm, key, true);
vmPushTempRef(vm, &key_str->_super);
vm->fiber->error = stringFormat(vm, "Key (@) not exists", key_str);
if (IS_OBJ(key) && !isObjectHashable(AS_OBJ(key)->type)) {
vm->fiber->error = stringFormat(vm, "Invalid key '@'.", key_str);
} else {
vm->fiber->error = stringFormat(vm, "Key '@' not exists", key_str);
}
vmPopTempRef(vm);
return VAR_NULL;
}
return value;

View File

@ -16,7 +16,6 @@ static const char* op_name[] = {
NULL,
};
static void _dumpValue(PKVM* vm, Var value, bool recursive) {
if (IS_NULL(value)) {
printf("null");
@ -53,8 +52,22 @@ static void _dumpValue(PKVM* vm, Var value, bool recursive) {
}
case OBJ_MAP:
TODO;
{
Map* map = (Map*)obj;
if (recursive) {
printf("{...}");
} else {
printf("{");
for (uint32_t i = 0; i < map->count; i++) {
if (i != 0) printf(", ");
_dumpValue(vm, map->entries[i].key, true);
printf(":");
_dumpValue(vm, map->entries[i].value, true);
}
printf("}");
}
return;
}
case OBJ_RANGE:
{
@ -64,10 +77,11 @@ static void _dumpValue(PKVM* vm, Var value, bool recursive) {
}
case OBJ_SCRIPT:
printf("[Script:%p]", obj);
printf("[Script:%s]", ((Script*)obj)->name->data);
return;
case OBJ_FUNC:
printf("[Fn:%p]", obj);
printf("[Fn:%s]", ((Function*)obj)->name);
return;
case OBJ_FIBER:
@ -84,8 +98,7 @@ void dumpValue(PKVM* vm, Var value) {
_dumpValue(vm, value, false);
}
void dumpInstructions(PKVM* vm, Function* func) {
void dumpFunctionCode(PKVM* vm, Function* func) {
uint32_t i = 0;
uint8_t* opcodes = func->fn->opcodes.data;
@ -116,7 +129,7 @@ void dumpInstructions(PKVM* vm, Function* func) {
Opcode op = (Opcode)func->fn->opcodes.data[i++];
switch (op) {
case OP_CONSTANT:
case OP_PUSH_CONSTANT:
{
int index = READ_SHORT();
printf("%5d ", index);
@ -131,6 +144,7 @@ void dumpInstructions(PKVM* vm, Function* func) {
case OP_PUSH_SELF:
case OP_PUSH_TRUE:
case OP_PUSH_FALSE:
case OP_SWAP:
NO_ARGS();
break;
@ -174,9 +188,20 @@ void dumpInstructions(PKVM* vm, Function* func) {
case OP_PUSH_GLOBAL:
case OP_STORE_GLOBAL:
case OP_PUSH_FN:
SHORT_ARG();
{
int index = READ_SHORT();
int name_index = func->owner->global_names.data[index];
String* name = func->owner->names.data[name_index];
printf("%5d '%s'\n", index, name->data);
break;
}
case OP_PUSH_FN:
{
int index = READ_SHORT();
printf("%5d [Fn:%s]\n", index, func->name);
break;
}
case OP_PUSH_BUILTIN_FN:
{
@ -184,7 +209,6 @@ void dumpInstructions(PKVM* vm, Function* func) {
printf("%5d [Fn:%s]\n", index, getBuiltinFunctionName(vm, index));
break;
}
case OP_POP: NO_ARGS(); break;
case OP_IMPORT: NO_ARGS(); break;
@ -237,8 +261,6 @@ void dumpInstructions(PKVM* vm, Function* func) {
case OP_BIT_XOR:
case OP_BIT_LSHIFT:
case OP_BIT_RSHIFT:
case OP_AND:
case OP_OR:
case OP_EQEQ:
case OP_NOTEQ:
case OP_LT:
@ -258,19 +280,18 @@ void dumpInstructions(PKVM* vm, Function* func) {
}
}
void reportStackTrace(PKVM* vm) {
if (vm->config.error_fn == NULL) return;
void dumpStackFrame(PKVM* vm) {
Fiber* fiber = vm->fiber;
int frame_ind = fiber->frame_count - 1;
ASSERT(frame_ind >= 0, OOPS);
CallFrame* frame = &fiber->frames[frame_ind];
Var* sp = fiber->sp - 1;
vm->config.error_fn(vm, PK_ERROR_RUNTIME, NULL, -1, fiber->error->data);
for (int i = fiber->frame_count - 1; i >= 0; i--) {
CallFrame* frame = &fiber->frames[i];
Function* fn = frame->fn;
ASSERT(!fn->is_native, OOPS);
int line = fn->fn->oplines.data[frame->ip - fn->fn->opcodes.data - 1];
vm->config.error_fn(vm, PK_ERROR_STACKTRACE, fn->owner->name->data, line, fn->name);
printf("Frame: %d.\n", frame_ind);
for (; sp >= frame->rbp; sp--) {
printf(" ");
dumpValue(vm, *sp);
printf("\n");
}
}

View File

@ -12,9 +12,9 @@
void dumpValue(PKVM* vm, Var value);
// Dump opcodes of the given function.
void dumpInstructions(PKVM* vm, Function* func);
void dumpFunctionCode(PKVM* vm, Function* func);
// Print stack track.
void reportStackTrace(PKVM* vm);
// Dump the current (top most) stack call frame.
void dumpStackFrame(PKVM* vm);
#endif // DEBUG_H

View File

@ -15,7 +15,7 @@
// Load the constant at index [arg] from the script's literals.
// params: 2 byte (uint16_t) index value.
OPCODE(CONSTANT, 2, 1)
OPCODE(PUSH_CONSTANT, 2, 1)
// Push null on the stack.
OPCODE(PUSH_NULL, 0, 1)
@ -29,6 +29,9 @@ OPCODE(PUSH_TRUE, 0, 1)
// Push false on the stack.
OPCODE(PUSH_FALSE, 0, 1)
// Swap the top 2 stack values.
OPCODE(SWAP, 0, 0)
// Push a new list to construct from literal.
// param: 2 bytes list size (defalt is 0).
OPCODE(PUSH_LIST, 2, 1)
@ -182,8 +185,6 @@ OPCODE(BIT_XOR, 0, -1)
OPCODE(BIT_LSHIFT, 0, -1)
OPCODE(BIT_RSHIFT, 0, -1)
OPCODE(AND, 0, -1)
OPCODE(OR, 0, -1)
OPCODE(EQEQ, 0, -1)
OPCODE(NOTEQ, 0, -1)
OPCODE(LT, 0, -1)

View File

@ -6,7 +6,7 @@
#include "vm.h"
#include "core.h"
#include "debug.h"
//#include "debug.h" //< Wrap around debug macro.
#include "utils.h"
#define HAS_ERROR() (vm->fiber->error != NULL)
@ -375,10 +375,19 @@ void pkSetRuntimeError(PKVM* vm, const char* message) {
void vmReportError(PKVM* vm) {
ASSERT(HAS_ERROR(), "runtimeError() should be called after an error.");
// TODO: pass the error to the caller of the fiber.
reportStackTrace(vm);
// Print the Error message and stack trace.
if (vm->config.error_fn == NULL) return;
Fiber* fiber = vm->fiber;
vm->config.error_fn(vm, PK_ERROR_RUNTIME, NULL, -1, fiber->error->data);
for (int i = fiber->frame_count - 1; i >= 0; i--) {
CallFrame* frame = &fiber->frames[i];
Function* fn = frame->fn;
ASSERT(!fn->is_native, OOPS);
int line = fn->fn->oplines.data[frame->ip - fn->fn->opcodes.data - 1];
vm->config.error_fn(vm, PK_ERROR_STACKTRACE, fn->owner->name->data, line, fn->name);
}
}
// FIXME: temp.
@ -530,7 +539,7 @@ PKInterpretResult vmRunScript(PKVM* vm, Script* _script) {
Opcode instruction;
SWITCH(instruction) {
OPCODE(CONSTANT):
OPCODE(PUSH_CONSTANT):
{
uint16_t index = READ_SHORT();
ASSERT_INDEX(index, script->literals.count);
@ -550,6 +559,16 @@ PKInterpretResult vmRunScript(PKVM* vm, Script* _script) {
PUSH(VAR_FALSE);
DISPATCH();
OPCODE(SWAP):
{
Var top1 = *(vm->fiber->sp - 1);
Var top2 = *(vm->fiber->sp - 2);
*(vm->fiber->sp - 1) = top2;
*(vm->fiber->sp - 2) = top1;
DISPATCH();
}
OPCODE(PUSH_LIST):
{
List* list = newList(vm, (uint32_t)READ_SHORT());
@ -754,7 +773,6 @@ PKInterpretResult vmRunScript(PKVM* vm, Script* _script) {
DISPATCH();
}
OPCODE(JUMP_IF):
{
Var cond = POP();
@ -929,21 +947,6 @@ PKInterpretResult vmRunScript(PKVM* vm, Script* _script) {
OPCODE(BIT_XOR):
OPCODE(BIT_LSHIFT):
OPCODE(BIT_RSHIFT):
OPCODE(AND):
TODO;
OPCODE(OR):
{
TODO;
// Python like or operator.
//Var v1 = POP(), v2 = POP();
//if (toBool(v1)) {
// PUSH(v1);
//} else {
// PUSH(v2);
//}
DISPATCH();
}
OPCODE(EQEQ) :
{

27
test/lang/chain_call.pk Normal file
View File

@ -0,0 +1,27 @@
## 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
## `result -> print` same as `print(result)`
assert(result == '[fn3:[fn2:[fn1:data]|suff]]')
result = ' tEST+InG ' -> str_strip -> str_lower
assert(result == 'test+ing')

View File

@ -1,9 +1,9 @@
## Testing import statement
import os
import os, path
import os as o, path as p
from os import clock
from os import clock as c
import lang
import lang, path
import lang as o, path as p
from lang import clock
from lang import clock as c
from path import abspath, curdir
from path import abspath as ap, curdir as cd

View File

@ -5,8 +5,10 @@ val = 0
a = false; b = true;
if a and b then assert(false) end
if a or b then va
else assert(false, '') end
if a or b then val = 42
else assert(false) end
assert(val == 42)
get_true = func return true end

View File

@ -1,13 +1,14 @@
@echo off
set files=( ^
lang\basics.pk ^
lang\import.pk ^
lang\if.pk ^
lang\logical.pk ^
^
examples\fib.pk ^
examples\prime.pk ^
set files=( ^
lang\basics.pk ^
lang\import.pk ^
lang\if.pk ^
lang\logical.pk ^
lang\chain_call.pk ^
^
examples\fib.pk ^
examples\prime.pk ^
)
set errorlevel=0