import statement refactored to a builtin function

This commit is contained in:
Thakee Nathees 2021-02-12 12:23:52 +05:30
parent 21c57a6d39
commit 2b132409e2
9 changed files with 191 additions and 101 deletions

View File

@ -1 +1,35 @@
# MiniScript
## MiniScript Language
MiniScript is a simple embeddable, functional, dynamic-typed, bytecode-interpreted, scripting language written in C. It uses the [mark-and-sweep](https://en.wikipedia.org/wiki/Tracing_garbage_collection) method for garbage collection. MiniScript is is syntactically similar to Ruby. The frontend and expression parsing techniques were written using [Wren Language](https://wren.io/) and their wonderful book [craftinginterpreters](http://www.craftinginterpreters.com/) as a reference.
### What MiniScript looks like
```ruby
## Find and return the maximum value in the array.
def get_max(arr)
ret = arr[0]
for i in 1..arr.length
ret = max(ret, arr[i])
end
return ret
end
## Return an array where each element returns true with function [fn] and
## belongs to [arr].
def filter(arr, fn)
ret = []
for elem in arr
if fn(elem)
array_append(ret, elem)
end
end
return ret
end
array = [42, null, 3.14, "String", 0..10, [100]]
nums = filter(array, is_num)
print(get_max(nums))
```

View File

@ -87,7 +87,6 @@ typedef enum {
//TK_XOREQ, // ^=
// Keywords.
TK_IMPORT, // import
TK_DEF, // def
TK_NATIVE, // native (C function declaration)
TK_END, // end
@ -146,7 +145,6 @@ typedef struct {
// List of keywords mapped into their identifiers.
static _Keyword _keywords[] = {
{ "import", 6, TK_IMPORT },
{ "def", 3, TK_DEF },
{ "native", 6, TK_NATIVE },
{ "end", 3, TK_END },
@ -719,13 +717,6 @@ typedef struct {
NameDefnType type;
// Could be found in one of the imported script or in it's imported script
// recursively. If true [_extern] will be the script ID.
bool is_extern;
// Extern script's index.
int _extern;
// Index in the variable/function buffer/array.
int index;
@ -740,7 +731,6 @@ static NameSearchResult compilerSearchName(Compiler* compiler,
NameSearchResult result;
result.type = NAME_NOT_DEFINED;
result.is_extern = false;
// Search through builtin functions.
int index = findBuiltinFunction(name, length);
@ -781,8 +771,6 @@ static NameSearchResult compilerSearchName(Compiler* compiler,
return result;
}
// TODO: search in imported scripts.
return result;
}
@ -863,7 +851,6 @@ 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_IMPORT */ NO_RULE,
/* TK_DEF */ NO_RULE,
/* TK_EXTERN */ NO_RULE,
/* TK_END */ NO_RULE,
@ -967,34 +954,17 @@ static void exprName(Compiler* compiler, bool can_assign) {
case NAME_GLOBAL_VAR:
if (can_assign && match(&compiler->parser, TK_EQ)) {
compileExpression(compiler);
if (result.is_extern) {
emitOpcode(compiler, OP_STORE_GLOBAL_EXT);
emitShort(compiler, result._extern);
emitShort(compiler, result.index);
} else {
_emitStoreVariable(compiler, result.index, true);
}
_emitStoreVariable(compiler, result.index, true);
} else {
if (result.is_extern) {
emitOpcode(compiler, OP_PUSH_GLOBAL_EXT);
emitShort(compiler, result._extern);
emitShort(compiler, result.index);
} else {
emitOpcode(compiler, OP_PUSH_GLOBAL);
emitShort(compiler, result.index);
}
emitOpcode(compiler, OP_PUSH_GLOBAL);
emitShort(compiler, result.index);
}
return;
case NAME_FUNCTION:
if (result.is_extern) {
emitOpcode(compiler, OP_PUSH_FN_EXT);
emitShort(compiler, result._extern);
emitShort(compiler, result.index);
} else {
emitOpcode(compiler, OP_PUSH_FN);
emitShort(compiler, result.index);
}
emitOpcode(compiler, OP_PUSH_FN);
emitShort(compiler, result.index);
return;
case NAME_BUILTIN:
@ -1329,6 +1299,10 @@ static void compileFunction(Compiler* compiler, bool is_native) {
consume(parser, TK_END, "Expected 'end' after function definition end.");
emitOpcode(compiler, OP_PUSH_NULL);
emitOpcode(compiler, OP_RETURN);
emitOpcode(compiler, OP_END);
compilerExitBlock(compiler); // Parameter depth.
compiler->function = compiler->script->body;
}
@ -1511,9 +1485,6 @@ Script* compileSource(MSVM* vm, const char* path) {
} else if (match(parser, TK_DEF)) {
compileFunction(&compiler, false);
} else if (match(parser, TK_IMPORT)) {
// TODO: import statement must be first of all other.
TODO;
} else {
compileStatement(&compiler);
}

View File

@ -13,7 +13,7 @@ typedef struct {
} _BuiltinFn;
// Count of builtin function +1 for termination.
#define BUILTIN_COUNT 10
#define BUILTIN_COUNT 50
// Array of all builtin functions.
_BuiltinFn builtins[BUILTIN_COUNT];
@ -47,6 +47,33 @@ Function* getBuiltinFunction(int index) {
return &builtins[index].fn;
}
#define FN_IS_PRIMITE_TYPE(name, check) \
void coreIs##name(MSVM* vm) { \
vm->rbp[0] = VAR_BOOL(check(vm->rbp[1])); \
}
#define FN_IS_OBJ_TYPE(name, _enum) \
void coreIs##name(MSVM* vm) { \
Var arg1 = vm->rbp[1]; \
if (IS_OBJ(arg1) && AS_OBJ(arg1)->type == _enum) { \
vm->rbp[0] = VAR_TRUE; \
} else { \
vm->rbp[0] = VAR_FALSE; \
} \
}
FN_IS_PRIMITE_TYPE(Null, IS_NULL)
FN_IS_PRIMITE_TYPE(Bool, IS_BOOL)
FN_IS_PRIMITE_TYPE(Num, IS_NUM)
FN_IS_OBJ_TYPE(String, OBJ_STRING)
FN_IS_OBJ_TYPE(Array, OBJ_ARRAY)
FN_IS_OBJ_TYPE(Map, OBJ_MAP)
FN_IS_OBJ_TYPE(Range, OBJ_RANGE)
FN_IS_OBJ_TYPE(Function, OBJ_FUNC)
FN_IS_OBJ_TYPE(Script, OBJ_SCRIPT)
FN_IS_OBJ_TYPE(UserObj, OBJ_USER)
void coreToString(MSVM* vm) {
Var arg1 = vm->rbp[1];
vm->rbp[0] = VAR_OBJ(&toString(vm, arg1)->_super);
@ -66,12 +93,35 @@ void corePrint(MSVM* vm) {
vm->config.write_fn(vm, "\n");
}
void coreImport(MSVM* vm) {
Var arg1 = vm->rbp[1];
if (IS_OBJ(arg1) && AS_OBJ(arg1)->type == OBJ_STRING) {
TODO;
} else {
msSetRuntimeError(vm, "Expected a String argument.");
}
}
void initializeCore(MSVM* vm) {
int i = 0; //< Iterate through builtins.
// Initialize builtin functions.
int i = 0;
initializeBuiltinFN(vm, &builtins[i++], "print", 1, corePrint);
initializeBuiltinFN(vm, &builtins[i++], "is_null", 1, coreIsNull);
initializeBuiltinFN(vm, &builtins[i++], "is_bool", 1, coreIsBool);
initializeBuiltinFN(vm, &builtins[i++], "is_num", 1, coreIsNum);
initializeBuiltinFN(vm, &builtins[i++], "is_string", 1, coreIsString);
initializeBuiltinFN(vm, &builtins[i++], "is_array", 1, coreIsArray);
initializeBuiltinFN(vm, &builtins[i++], "is_map", 1, coreIsMap);
initializeBuiltinFN(vm, &builtins[i++], "is_range", 1, coreIsRange);
initializeBuiltinFN(vm, &builtins[i++], "is_function", 1, coreIsFunction);
initializeBuiltinFN(vm, &builtins[i++], "is_script", 1, coreIsScript);
initializeBuiltinFN(vm, &builtins[i++], "is_userobj", 1, coreIsUserObj);
initializeBuiltinFN(vm, &builtins[i++], "to_string", 1, coreToString);
initializeBuiltinFN(vm, &builtins[i++], "print", 1, corePrint);
initializeBuiltinFN(vm, &builtins[i++], "import", 1, coreImport);
// Sentinal to mark the end of the array.
initializeBuiltinFN(vm, &builtins[i], NULL, 0, NULL);
@ -112,11 +162,12 @@ Var varAdd(MSVM* vm, Var v1, Var v2) {
return VAR_NULL;
}
// TODO: string addition/ array addition etc.
TODO; //string addition/ array addition etc.
return VAR_NULL;
}
Var varSubtract(MSVM* vm, Var v1, Var v2) {
TODO;
return VAR_NULL;
}
@ -130,9 +181,16 @@ Var varMultiply(MSVM* vm, Var v1, Var v2) {
return VAR_NULL;
}
TODO;
return VAR_NULL;
}
Var varDivide(MSVM* vm, Var v1, Var v2) {
TODO;
return VAR_NULL;
}
bool varIterate(MSVM* vm, Var seq, Var* iterator, Var* value) {
TODO;
return false;
}

View File

@ -24,5 +24,11 @@ Var varSubtract(MSVM* vm, Var v1, Var v2);
Var varMultiply(MSVM* vm, Var v1, Var v2);
Var varDivide(MSVM* vm, Var v1, Var v2);
// Functions //////////////////////////////////////////////////////////////////
// Parameter [iterator] should be VAR_NULL before starting the iteration.
// If an element is obtained by iteration it'll return true otherwise returns
// false indicating that the iteration is over.
bool varIterate(MSVM* vm, Var seq, Var* iterator, Var* value);
#endif // CORE_H

View File

@ -64,23 +64,11 @@ OPCODE(PUSH_GLOBAL, 2, 1)
// params: 2 bytes (uint16_t) index.
OPCODE(STORE_GLOBAL, 2, -1)
// Push imported script's global value on the stack.
// params: 2 bytes script index and 2 bytes index.
OPCODE(PUSH_GLOBAL_EXT, 4, 1)
// Pop and store the value to imported script's global.
// params: 4 bytes script ID and 2 bytes index.
OPCODE(STORE_GLOBAL_EXT, 6, -1)
// Push the script's function on the stack. It could later be called. But a
// function can't be stored i.e. can't assign a function with something else.
// params: 2 bytes index.
OPCODE(PUSH_FN, 2, 1)
// Push an imported script's function.
// params: 2 bytes script index and 2 bytes index.
OPCODE(PUSH_FN_EXT, 4, 1)
// Push a built in function.
// params: 2 bytes index of the script.
OPCODE(PUSH_BUILTIN_FN, 2, 1)
@ -106,22 +94,6 @@ OPCODE(POP, 0, -1)
//OPCODE(CALL_8, 2, -8)
OPCODE(CALL, 4, -0) //< Will calculated at compile time.
// A function pointer will be on top of the stack and it'll be called by OP_CALL...
// and doesn't need to specify opcode for extern or not.
//
// Call a function from an imported script.
// params: 2 bytes script ID and 2 bytes index. _N -> +2 bytes for argc.
//OPCODE(CALL_EXT_0, 4, 0)
//OPCODE(CALL_EXT_1, 4, -1)
//OPCODE(CALL_EXT_2, 4, -2)
//OPCODE(CALL_EXT_3, 4, -3)
//OPCODE(CALL_EXT_4, 4, -4)
//OPCODE(CALL_EXT_5, 4, -5)
//OPCODE(CALL_EXT_6, 4, -6)
//OPCODE(CALL_EXT_7, 4, -7)
//OPCODE(CALL_EXT_8, 4, -8)
//OPCODE(CALL_EXT_N, 6, -0) //< Will calculated at compile time.
// The address to jump to. It'll set the ip to the address it should jump to
// and the address is absolute not an offset from ip's current value.
// param: 2 bytes jump address.

View File

@ -58,6 +58,14 @@ String* newString(MSVM* vm, const char* text, uint32_t length) {
return string;
}
Range* newRange(MSVM* vm, double from, double to) {
Range* range = ALLOCATE(vm, Range);
varInitObject(&range->_super, vm, OBJ_RANGE);
range->from = from;
range->to = to;
return range;
}
Script* newScript(MSVM* vm) {
Script* script = ALLOCATE(vm, Script);
varInitObject(&script->_super, vm, OBJ_SCRIPT);
@ -142,13 +150,20 @@ String* toString(MSVM* vm, Var v) {
return newString(vm, ((String*)obj)->data, ((String*)obj)->length);
break;
case OBJ_ARRAY: TODO;
case OBJ_MAP: TODO;
case OBJ_RANGE: TODO;
case OBJ_SCRIPT: TODO;
case OBJ_FUNC: TODO;
case OBJ_INSTANCE: TODO;
case OBJ_USER: TODO;
case OBJ_ARRAY: return newString(vm, "[Array]", 7); // TODO;
case OBJ_MAP: return newString(vm, "[Map]", 5); // TODO;
case OBJ_RANGE: return newString(vm, "[Range]", 7); // TODO;
case OBJ_SCRIPT: return newString(vm, "[Script]", 8); // TODO;
case OBJ_FUNC: {
const char* name = ((Function*)obj)->name;
int length = (int)strlen(name); // TODO: Assert length.
char buff[TO_STRING_BUFF_SIZE];
memcpy(buff, "[func:", 6);
memcpy(buff + 6, name, length);
buff[6 + length] = ']';
return newString(vm, buff, 6 + length + 1);
}
case OBJ_USER: return newString(vm, "[UserObj]", 9); // TODO;
break;
}

View File

@ -195,7 +195,6 @@ typedef enum /* ObjectType */ {
OBJ_SCRIPT,
OBJ_FUNC,
OBJ_INSTANCE,
OBJ_USER,
} ObjectType;
@ -281,6 +280,9 @@ double varToDouble(Var value);
// Allocate new String object and return String*.
String* newString(MSVM* vm, const char* text, uint32_t length);
// Allocate new Range object and return Range*.
Range* newRange(MSVM* vm, double from, double to);
// Allocate new Script object and return Script*.
Script* newScript(MSVM* vm);

View File

@ -61,8 +61,7 @@ void vmPopTempRef(MSVM* self) {
void _printStackTop(MSVM* vm) {
if (vm->sp != vm->stack) {
Var v = *(vm->sp - 1);
double n = AS_NUM(v);
printf("%f\n", n);
printf("%s\n", toString(vm, v)->data);
}
}
@ -94,7 +93,6 @@ static inline void pushCallFrame(MSVM* vm, Function* fn) {
frame->ip = fn->fn->opcodes.data;
}
void msSetRuntimeError(MSVM* vm, const char* format, ...) {
vm->error = newString(vm, "TODO:", 5);
TODO;
@ -168,7 +166,7 @@ MSInterpretResult vmRunScript(MSVM* vm, Script* _script) {
#error "OPCODE" should not be deifined here.
#endif
#define DEBUG_INSTRUCTION()
#define DEBUG_INSTRUCTION() //_printStackTop(vm)
#define SWITCH(code) \
L_vm_main_loop: \
@ -231,7 +229,6 @@ MSInterpretResult vmRunScript(MSVM* vm, Script* _script) {
PUSH(rbp[index]);
DISPATCH();
}
OPCODE(PUSH_LOCAL_N):
{
int index = READ_SHORT();
@ -248,8 +245,17 @@ MSInterpretResult vmRunScript(MSVM* vm, Script* _script) {
OPCODE(STORE_LOCAL_6):
OPCODE(STORE_LOCAL_7):
OPCODE(STORE_LOCAL_8):
{
int index = (int)(instruction - OP_STORE_LOCAL_0);
rbp[index] = POP();
DISPATCH();
}
OPCODE(STORE_LOCAL_N):
TODO;
{
int index = READ_SHORT();
rbp[index] = POP();
DISPATCH();
}
OPCODE(PUSH_GLOBAL):
{
@ -267,10 +273,6 @@ MSInterpretResult vmRunScript(MSVM* vm, Script* _script) {
DISPATCH();
}
OPCODE(PUSH_GLOBAL_EXT):
OPCODE(STORE_GLOBAL_EXT):
TODO;
OPCODE(PUSH_FN):
{
int index = READ_SHORT();
@ -280,9 +282,6 @@ MSInterpretResult vmRunScript(MSVM* vm, Script* _script) {
DISPATCH();
}
OPCODE(PUSH_FN_EXT) :
TODO;
OPCODE(PUSH_BUILTIN_FN):
{
Function* fn = getBuiltinFunction(READ_SHORT());
@ -298,7 +297,6 @@ MSInterpretResult vmRunScript(MSVM* vm, Script* _script) {
{
int argc = READ_SHORT();
Var* callable = vm->sp - argc - 1;
vm->rbp = callable; //< Next call frame starts here.
if (IS_OBJ(*callable) && AS_OBJ(*callable)->type == OBJ_FUNC) {
@ -311,8 +309,13 @@ MSInterpretResult vmRunScript(MSVM* vm, Script* _script) {
// initialized with VAR_NULL as return value.
*callable = VAR_NULL;
// Next call frame starts here. (including return value).
vm->rbp = callable;
if (fn->is_native) {
fn->native(vm);
// Pop function arguments except for the return value.
vm->sp = vm->rbp + 1;
CHECK_ERROR();
} else {
@ -330,7 +333,30 @@ MSInterpretResult vmRunScript(MSVM* vm, Script* _script) {
OPCODE(JUMP):
OPCODE(JUMP_NOT):
OPCODE(JUMP_IF_NOT):
TODO;
OPCODE(RETURN):
{
Var ret = POP();
vm->frame_count--;
// If no more call frames. We're done.
if (vm->frame_count == 0) {
vm->sp = vm->stack;
PUSH(ret);
return RESULT_SUCCESS;
}
// Set the return value.
*(frame->rbp - 1) = ret;
// Pop the locals and update stack pointer.
vm->sp = frame->rbp;
LOAD_FRAME();
DISPATCH();
}
OPCODE(GET_ATTRIB):
OPCODE(SET_ATTRIB):
OPCODE(GET_SUBSCRIPT):
@ -370,9 +396,22 @@ MSInterpretResult vmRunScript(MSVM* vm, Script* _script) {
OPCODE(LTEQ):
OPCODE(GT):
OPCODE(GTEQ):
TODO;
OPCODE(RANGE):
{
Var to = POP();
Var from = POP();
if (!IS_NUM(from) || !IS_NUM(to)) {
RUNTIME_ERROR("Range arguments must be number.");
}
PUSH(VAR_OBJ(newRange(vm, AS_NUM(from), AS_NUM(to))));
DISPATCH();
}
OPCODE(IN):
OPCODE(END):
TODO;
break;
default:

View File

@ -12,13 +12,6 @@
#include <stdlib.h>
// FIXME:
//#include "../src/common.h"
//#include "../src/var.h"
//#include "../src/vm.h"
//#include "../src/types/gen/string_buffer.h"
//#include "../src/types/gen/byte_buffer.h"
static const char* opnames[] = {
#define OPCODE(name, params, stack) #name,
#include "../src/opcodes.h"