mirror of
https://github.com/zekexiao/pocketlang.git
synced 2025-02-11 15:16:41 +08:00
python like import statement implemented
This commit is contained in:
parent
dd42fc61ad
commit
57931c5dca
@ -91,6 +91,7 @@ typedef enum {
|
|||||||
//TK_XOREQ, // ^=
|
//TK_XOREQ, // ^=
|
||||||
|
|
||||||
// Keywords.
|
// Keywords.
|
||||||
|
TK_FROM, // from
|
||||||
TK_IMPORT, // import
|
TK_IMPORT, // import
|
||||||
TK_AS, // as
|
TK_AS, // as
|
||||||
TK_DEF, // def
|
TK_DEF, // def
|
||||||
@ -152,6 +153,7 @@ typedef struct {
|
|||||||
|
|
||||||
// List of keywords mapped into their identifiers.
|
// List of keywords mapped into their identifiers.
|
||||||
static _Keyword _keywords[] = {
|
static _Keyword _keywords[] = {
|
||||||
|
{ "from", 4, TK_FROM },
|
||||||
{ "import", 6, TK_IMPORT },
|
{ "import", 6, TK_IMPORT },
|
||||||
{ "as", 2, TK_AS },
|
{ "as", 2, TK_AS },
|
||||||
{ "def", 3, TK_DEF },
|
{ "def", 3, TK_DEF },
|
||||||
@ -928,6 +930,7 @@ GrammarRule rules[] = { // Prefix Infix Infix Precedence
|
|||||||
/* TK_DIVEQ */ NO_RULE, // exprAssignment, PREC_ASSIGNMENT
|
/* TK_DIVEQ */ NO_RULE, // exprAssignment, PREC_ASSIGNMENT
|
||||||
/* TK_SRIGHT */ { NULL, exprBinaryOp, PREC_BITWISE_SHIFT },
|
/* TK_SRIGHT */ { NULL, exprBinaryOp, PREC_BITWISE_SHIFT },
|
||||||
/* TK_SLEFT */ { NULL, exprBinaryOp, PREC_BITWISE_SHIFT },
|
/* TK_SLEFT */ { NULL, exprBinaryOp, PREC_BITWISE_SHIFT },
|
||||||
|
/* TK_FROM */ NO_RULE,
|
||||||
/* TK_IMPORT */ NO_RULE,
|
/* TK_IMPORT */ NO_RULE,
|
||||||
/* TK_AS */ NO_RULE,
|
/* TK_AS */ NO_RULE,
|
||||||
/* TK_DEF */ NO_RULE,
|
/* TK_DEF */ NO_RULE,
|
||||||
@ -1214,7 +1217,7 @@ static void exprAttrib(Compiler* compiler, bool can_assign) {
|
|||||||
|
|
||||||
TokenType assignment = parser->previous.type;
|
TokenType assignment = parser->previous.type;
|
||||||
if (assignment != TK_EQ) {
|
if (assignment != TK_EQ) {
|
||||||
emitOpcode(compiler, OP_GET_ATTRIB_AOP);
|
emitOpcode(compiler, OP_GET_ATTRIB_KEEP);
|
||||||
emitShort(compiler, index);
|
emitShort(compiler, index);
|
||||||
compileExpression(compiler);
|
compileExpression(compiler);
|
||||||
|
|
||||||
@ -1560,61 +1563,94 @@ static void compileBlockBody(Compiler* compiler, BlockType type) {
|
|||||||
compilerExitBlock(compiler);
|
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;
|
const char* name = NULL;
|
||||||
uint32_t length = 0;
|
uint32_t length = 0;
|
||||||
int lib = -1; //< Imported library variable index.
|
int entry = -1; //< Imported library variable index.
|
||||||
|
|
||||||
if (match(&compiler->parser, TK_NAME)) {
|
if (match(&compiler->parser, TK_NAME)) {
|
||||||
name = compiler->parser.previous.start;
|
name = compiler->parser.previous.start;
|
||||||
length = compiler->parser.previous.length;
|
length = compiler->parser.previous.length;
|
||||||
|
|
||||||
uint32_t name_line = compiler->parser.previous.line;
|
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,
|
int index = (int)scriptAddName(compiler->script, compiler->vm,
|
||||||
name, length);
|
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 {
|
} else {
|
||||||
TODO; //?
|
// Don't pop the lib since it'll be used for the next entry.
|
||||||
//consume(&compiler->parser, TK_STRING, "Expected a name or a string literal"
|
emitOpcode(compiler, OP_GET_ATTRIB_KEEP);
|
||||||
// " after import.");
|
emitShort(compiler, index); //< Name of the attrib.
|
||||||
//String* path = (String*)AS_OBJ(compiler->parser.previous.value);
|
|
||||||
//name = path->data;
|
|
||||||
//length = path->length;
|
|
||||||
//uint32_t index = compilerAddConstant(compiler, VAR_OBJ(path));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
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)) {
|
if (match(&compiler->parser, TK_AS)) {
|
||||||
consume(&compiler->parser, TK_NAME, "Expected a name after as.");
|
consume(&compiler->parser, TK_NAME, "Expected a name after as.");
|
||||||
compiler->variables[lib].name = compiler->parser.previous.start;
|
// TODO: validate the name (maybe can't be predefined?).
|
||||||
compiler->variables[lib].length = compiler->parser.previous.length;
|
compiler->variables[entry].name = compiler->parser.previous.start;
|
||||||
|
compiler->variables[entry].length = compiler->parser.previous.length;
|
||||||
// TODO: add the name to global_names ??
|
// TODO: add the name to global_names ??
|
||||||
}
|
}
|
||||||
|
|
||||||
emitStoreVariable(compiler, lib, true);
|
emitStoreVariable(compiler, entry, true);
|
||||||
emitOpcode(compiler, OP_POP);
|
emitOpcode(compiler, OP_POP);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// The 'import' statement compilation. It's inspired by the python's import
|
||||||
TODO: write doc below.
|
// statement. It could be multiple import libs comma seperated and they can
|
||||||
from os import clock as c, path as p
|
// be aliased with 'as' keyword. Using from keyword it's possible to import
|
||||||
import os, json as j, math as m
|
// 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) {
|
static void compileImportStatement(Compiler* compiler, bool is_from) {
|
||||||
|
|
||||||
if (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 {
|
} else {
|
||||||
bool skip_lines = false;
|
|
||||||
do {
|
do {
|
||||||
if (skip_lines) skipNewLines(&compiler->parser);
|
//skipNewLines(&compiler->parser);
|
||||||
_compilerImportEntry(compiler, is_from);
|
_compilerImportEntry(compiler, IMPORT_REGULAR);
|
||||||
skip_lines = true;
|
|
||||||
} while (match(&compiler->parser, TK_COMMA));
|
} 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)) {
|
} else if (match(parser, TK_DEF)) {
|
||||||
compileFunction(&compiler, FN_SCRIPT);
|
compileFunction(&compiler, FN_SCRIPT);
|
||||||
|
|
||||||
// TODO: implement from keyword.
|
} else if (match(parser, TK_FROM)) {
|
||||||
//} else if (match(parser, TK_FROM)) {
|
compileImportStatement(&compiler, true);
|
||||||
// compileImportStatement(&compiler, true);
|
|
||||||
|
|
||||||
} else if (match(parser, TK_IMPORT)) {
|
} else if (match(parser, TK_IMPORT)) {
|
||||||
compileImportStatement(&compiler, false);
|
compileImportStatement(&compiler, false);
|
||||||
|
25
src/core.c
25
src/core.c
@ -184,27 +184,11 @@ void corePrint(PKVM* vm) {
|
|||||||
vm->config.write_fn(vm, "\n");
|
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 METHODS */
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
// std:list Methods.
|
// 'list' library methods.
|
||||||
void stdListSort(PKVM* vm) {
|
void stdListSort(PKVM* vm) {
|
||||||
Var list = ARG(1);
|
Var list = ARG(1);
|
||||||
if (!IS_OBJ(list) || AS_OBJ(list)->type != OBJ_LIST) {
|
if (!IS_OBJ(list) || AS_OBJ(list)->type != OBJ_LIST) {
|
||||||
@ -216,7 +200,7 @@ void stdListSort(PKVM* vm) {
|
|||||||
RET(list);
|
RET(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
// std:os Methods.
|
// 'os' library methods.
|
||||||
void stdOsClock(PKVM* vm) {
|
void stdOsClock(PKVM* vm) {
|
||||||
RET(VAR_NUM((double)clock() / CLOCKS_PER_SEC));
|
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("to_string", coreToString, 1);
|
||||||
INITALIZE_BUILTIN_FN("print", corePrint, -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.
|
// Make STD scripts.
|
||||||
Script* std; // A temporary pointer to the current std script.
|
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 '+' "
|
vm->fiber->error = stringFormat(vm, "Unsupported operand types for operator '+' "
|
||||||
"$ and $", varTypeName(v1), varTypeName(v2));
|
"$ and $", varTypeName(v1), varTypeName(v2));
|
||||||
|
|
||||||
|
@ -208,7 +208,7 @@ void dumpInstructions(PKVM* vm, Function* func) {
|
|||||||
case OP_RETURN: NO_ARGS(); break;
|
case OP_RETURN: NO_ARGS(); break;
|
||||||
|
|
||||||
case OP_GET_ATTRIB:
|
case OP_GET_ATTRIB:
|
||||||
case OP_GET_ATTRIB_AOP:
|
case OP_GET_ATTRIB_KEEP:
|
||||||
case OP_SET_ATTRIB:
|
case OP_SET_ATTRIB:
|
||||||
SHORT_ARG();
|
SHORT_ARG();
|
||||||
break;
|
break;
|
||||||
|
@ -146,10 +146,9 @@ OPCODE(RETURN, 0, -1)
|
|||||||
// param: 2 byte attrib name index.
|
// param: 2 byte attrib name index.
|
||||||
OPCODE(GET_ATTRIB, 2, 0)
|
OPCODE(GET_ATTRIB, 2, 0)
|
||||||
|
|
||||||
// Get attribute to perform assignment operation before store it, so don't
|
// It'll keep the instance on the stack and push the attribute on the stack.
|
||||||
// pop the var.
|
|
||||||
// param: 2 byte attrib name index.
|
// 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.
|
// Pop var and value update the attribute push result.
|
||||||
// param: 2 byte attrib name index.
|
// param: 2 byte attrib name index.
|
||||||
|
4
src/vm.c
4
src/vm.c
@ -720,9 +720,9 @@ PKInterpretResult vmRunScript(PKVM* vm, Script* _script) {
|
|||||||
DISPATCH();
|
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()];
|
String* name = script->names.data[READ_SHORT()];
|
||||||
PUSH(varGetAttrib(vm, on, name));
|
PUSH(varGetAttrib(vm, on, name));
|
||||||
DISPATCH();
|
DISPATCH();
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
|
|
||||||
import os
|
from os import clock
|
||||||
|
|
||||||
def fib(n)
|
def fib(n)
|
||||||
if n < 2 do return 1 end
|
if n < 2 do return 1 end
|
||||||
return fib(n - 1) + fib(n - 2)
|
return fib(n - 1) + fib(n - 2)
|
||||||
end
|
end
|
||||||
|
|
||||||
start = os.clock()
|
start = clock()
|
||||||
for i in 0..10
|
for i in 0..10
|
||||||
print(fib(28))
|
print(fib(28))
|
||||||
end
|
end
|
||||||
|
|
||||||
print('elapsed:', os.clock() - start, 's')
|
print('elapsed:', clock() - start, 's')
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user