diff --git a/src/compiler.c b/src/compiler.c index 2cf9ae9..fe73a24 100644 --- a/src/compiler.c +++ b/src/compiler.c @@ -91,6 +91,7 @@ typedef enum { //TK_XOREQ, // ^= // Keywords. + TK_FROM, // from TK_IMPORT, // import TK_AS, // as TK_DEF, // def @@ -152,6 +153,7 @@ typedef struct { // List of keywords mapped into their identifiers. static _Keyword _keywords[] = { + { "from", 4, TK_FROM }, { "import", 6, TK_IMPORT }, { "as", 2, TK_AS }, { "def", 3, TK_DEF }, @@ -928,6 +930,7 @@ GrammarRule rules[] = { // Prefix Infix Infix Precedence /* TK_DIVEQ */ NO_RULE, // exprAssignment, PREC_ASSIGNMENT /* TK_SRIGHT */ { NULL, exprBinaryOp, PREC_BITWISE_SHIFT }, /* TK_SLEFT */ { NULL, exprBinaryOp, PREC_BITWISE_SHIFT }, + /* TK_FROM */ NO_RULE, /* TK_IMPORT */ NO_RULE, /* TK_AS */ NO_RULE, /* TK_DEF */ NO_RULE, @@ -1214,7 +1217,7 @@ static void exprAttrib(Compiler* compiler, bool can_assign) { TokenType assignment = parser->previous.type; if (assignment != TK_EQ) { - emitOpcode(compiler, OP_GET_ATTRIB_AOP); + emitOpcode(compiler, OP_GET_ATTRIB_KEEP); emitShort(compiler, index); compileExpression(compiler); @@ -1560,61 +1563,94 @@ static void compileBlockBody(Compiler* compiler, BlockType type) { compilerExitBlock(compiler); } -static void _compilerImportEntry(Compiler* compiler, bool is_from) { +typedef enum { + IMPORT_REGULAR, //< Regular import + IMPORT_FROM_LIB, //< Import the lib for `from` import. + IMPORT_FROM_SYMBOL, //< Entry of a `from` import. + // TODO: from os import * +} ImportEntryType; + +// Single entry of a comma seperated import statement. The instructions will +// import the and assign to the corresponding variables. +static void _compilerImportEntry(Compiler* compiler, ImportEntryType ie_type) { const char* name = NULL; uint32_t length = 0; - int lib = -1; //< Imported library variable index. + int entry = -1; //< Imported library variable index. if (match(&compiler->parser, TK_NAME)) { name = compiler->parser.previous.start; length = compiler->parser.previous.length; uint32_t name_line = compiler->parser.previous.line; - lib = compilerAddVariable(compiler, name, length, name_line); + + // >> from os import clock + // Here `os` is temporary don't add to variable. + if (ie_type != IMPORT_FROM_LIB) { + entry = compilerAddVariable(compiler, name, length, name_line); + } int index = (int)scriptAddName(compiler->script, compiler->vm, name, length); - emitOpcode(compiler, OP_IMPORT); - emitShort(compiler, index); + + // >> from os import clock + // Here clock is not imported but get attribute of os. + if (ie_type != IMPORT_FROM_SYMBOL) { + emitOpcode(compiler, OP_IMPORT); + emitShort(compiler, index); //< Name of the lib. + } else { + // Don't pop the lib since it'll be used for the next entry. + emitOpcode(compiler, OP_GET_ATTRIB_KEEP); + emitShort(compiler, index); //< Name of the attrib. + } } else { - TODO; //? - //consume(&compiler->parser, TK_STRING, "Expected a name or a string literal" - // " after import."); - //String* path = (String*)AS_OBJ(compiler->parser.previous.value); - //name = path->data; - //length = path->length; - //uint32_t index = compilerAddConstant(compiler, VAR_OBJ(path)); + TODO; // import and push the lib to the stack. } + // >> from os import clock + // Here the os is imported and not bound to any variable, just temp. + if (ie_type == IMPORT_FROM_LIB) return; + + // Store to the variable. if (match(&compiler->parser, TK_AS)) { consume(&compiler->parser, TK_NAME, "Expected a name after as."); - compiler->variables[lib].name = compiler->parser.previous.start; - compiler->variables[lib].length = compiler->parser.previous.length; + // TODO: validate the name (maybe can't be predefined?). + compiler->variables[entry].name = compiler->parser.previous.start; + compiler->variables[entry].length = compiler->parser.previous.length; // TODO: add the name to global_names ?? } - emitStoreVariable(compiler, lib, true); + emitStoreVariable(compiler, entry, true); emitOpcode(compiler, OP_POP); } -/* - TODO: write doc below. - from os import clock as c, path as p - import os, json as j, math as m -*/ +// The 'import' statement compilation. It's inspired by the python's import +// statement. It could be multiple import libs comma seperated and they can +// be aliased with 'as' keyword. Using from keyword it's possible to import +// some of the members of the lib. +// +// example. import lib +// import lib1, lib2 as l2 +// from lib import func1, func2 as f2 +// static void compileImportStatement(Compiler* compiler, bool is_from) { if (is_from) { - TODO; // ? + _compilerImportEntry(compiler, IMPORT_FROM_LIB); + consume(&compiler->parser, TK_IMPORT, "Expected keyword 'import'."); + + do { + _compilerImportEntry(compiler, IMPORT_FROM_SYMBOL); + } while (match(&compiler->parser, TK_COMMA)); + + // Done getting all the attributes, now pop the lib from the stack. + emitOpcode(compiler, OP_POP); } else { - bool skip_lines = false; do { - if (skip_lines) skipNewLines(&compiler->parser); - _compilerImportEntry(compiler, is_from); - skip_lines = true; + //skipNewLines(&compiler->parser); + _compilerImportEntry(compiler, IMPORT_REGULAR); } while (match(&compiler->parser, TK_COMMA)); } @@ -1853,9 +1889,8 @@ bool compile(PKVM* vm, Script* script, const char* source) { } else if (match(parser, TK_DEF)) { compileFunction(&compiler, FN_SCRIPT); - // TODO: implement from keyword. - //} else if (match(parser, TK_FROM)) { - // compileImportStatement(&compiler, true); + } else if (match(parser, TK_FROM)) { + compileImportStatement(&compiler, true); } else if (match(parser, TK_IMPORT)) { compileImportStatement(&compiler, false); diff --git a/src/core.c b/src/core.c index 3042712..1ec50e9 100644 --- a/src/core.c +++ b/src/core.c @@ -184,27 +184,11 @@ void corePrint(PKVM* vm) { vm->config.write_fn(vm, "\n"); } -//void coreImport(PKVM* vm) { -// Var arg1 = vm->fiber->ret[1]; -// if (!IS_OBJ(arg1) || AS_OBJ(arg1)->type != OBJ_STRING) { -// pkSetRuntimeError(vm, "Expected a String argument."); -// } -// -// String* path = (String*)AS_OBJ(arg1); -// if (path->length > 4 && strncmp(path->data, "std:", 4) == 0) { -// Script* scr = vmGetStdScript(vm, path->data + 4); -// ASSERT(scr != NULL, OOPS); -// RET(VAR_OBJ(scr)); -// } -// -// TODO; -//} - /*****************************************************************************/ /* STD METHODS */ /*****************************************************************************/ -// std:list Methods. +// 'list' library methods. void stdListSort(PKVM* vm) { Var list = ARG(1); if (!IS_OBJ(list) || AS_OBJ(list)->type != OBJ_LIST) { @@ -216,7 +200,7 @@ void stdListSort(PKVM* vm) { RET(list); } -// std:os Methods. +// 'os' library methods. void stdOsClock(PKVM* vm) { RET(VAR_NUM((double)clock() / CLOCKS_PER_SEC)); } @@ -249,10 +233,6 @@ void initializeCore(PKVM* vm) { INITALIZE_BUILTIN_FN("to_string", coreToString, 1); INITALIZE_BUILTIN_FN("print", corePrint, -1); - //INITALIZE_BUILTIN_FN("import", coreImport, 1); - - // Sentinal to mark the end of the array. - //initializeBuiltinFN(vm, &builtins[i], NULL, 0, 0, NULL); // Make STD scripts. Script* std; // A temporary pointer to the current std script. @@ -334,7 +314,6 @@ Var varAdd(PKVM* vm, Var v1, Var v2) { } } - vm->fiber->error = stringFormat(vm, "Unsupported operand types for operator '+' " "$ and $", varTypeName(v1), varTypeName(v2)); diff --git a/src/debug.c b/src/debug.c index 1d6c409..f5d66d8 100644 --- a/src/debug.c +++ b/src/debug.c @@ -208,7 +208,7 @@ void dumpInstructions(PKVM* vm, Function* func) { case OP_RETURN: NO_ARGS(); break; case OP_GET_ATTRIB: - case OP_GET_ATTRIB_AOP: + case OP_GET_ATTRIB_KEEP: case OP_SET_ATTRIB: SHORT_ARG(); break; diff --git a/src/opcodes.h b/src/opcodes.h index b9275c8..b0df13f 100644 --- a/src/opcodes.h +++ b/src/opcodes.h @@ -146,10 +146,9 @@ OPCODE(RETURN, 0, -1) // param: 2 byte attrib name index. OPCODE(GET_ATTRIB, 2, 0) -// Get attribute to perform assignment operation before store it, so don't -// pop the var. +// It'll keep the instance on the stack and push the attribute on the stack. // param: 2 byte attrib name index. -OPCODE(GET_ATTRIB_AOP, 2, 1) +OPCODE(GET_ATTRIB_KEEP, 2, 1) // Pop var and value update the attribute push result. // param: 2 byte attrib name index. diff --git a/src/vm.c b/src/vm.c index 103fb88..0d15848 100644 --- a/src/vm.c +++ b/src/vm.c @@ -720,9 +720,9 @@ PKInterpretResult vmRunScript(PKVM* vm, Script* _script) { DISPATCH(); } - OPCODE(GET_ATTRIB_AOP): + OPCODE(GET_ATTRIB_KEEP): { - Var on = *(vm->fiber->sp - 1); + Var on = PEEK(); String* name = script->names.data[READ_SHORT()]; PUSH(varGetAttrib(vm, on, name)); DISPATCH(); diff --git a/test/benchmark/fib/fib.pk b/test/benchmark/fib/fib.pk index aeadf6f..70be646 100644 --- a/test/benchmark/fib/fib.pk +++ b/test/benchmark/fib/fib.pk @@ -1,15 +1,15 @@ -import os +from os import clock def fib(n) if n < 2 do return 1 end return fib(n - 1) + fib(n - 2) end -start = os.clock() +start = clock() for i in 0..10 print(fib(28)) end -print('elapsed:', os.clock() - start, 's') +print('elapsed:', clock() - start, 's')