python like import statement implemented

This commit is contained in:
Thakee Nathees 2021-05-10 00:30:59 +05:30
parent dd42fc61ad
commit 57931c5dca
6 changed files with 74 additions and 61 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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