iterator implemented

This commit is contained in:
Thakee Nathees 2021-02-12 23:10:19 +05:30
parent 2b132409e2
commit 38a22fea9f
8 changed files with 411 additions and 112 deletions

View File

@ -6,29 +6,29 @@ MiniScript is a simple embeddable, functional, dynamic-typed, bytecode-interpret
```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])
## Find and return the maximum value in the [list].
def get_max(list)
ret = list[0]
for i in 1..list.length
ret = max(ret, list[i])
end
return ret
end
## Return an array where each element returns true with function [fn] and
## belongs to [arr].
def filter(arr, fn)
## Return a list where each element returns true with function [fn] and
## belongs to [list].
def filter(list, fn)
ret = []
for elem in arr
for elem in list
if fn(elem)
array_append(ret, elem)
list_append(ret, elem)
end
end
return ret
end
array = [42, null, 3.14, "String", 0..10, [100]]
nums = filter(array, is_num)
list = [42, null, 3.14, "String", 0..10, ['hello']]
nums = filter(list, is_num)
print(get_max(nums))
```

View File

@ -63,11 +63,10 @@ typedef struct Var Var;
typedef struct Object Object;
typedef struct String String;
typedef struct Array Array;
typedef struct List List;
typedef struct Range Range;
typedef struct Script Script;
//typedef struct Class Class;
typedef struct Function Function;
#ifdef DEBUG
@ -115,6 +114,7 @@ typedef struct Function Function;
#endif // DEBUG
#define TODO ASSERT(false, "TODO")
#define OOPS "Oops a bug!! report plese."
// Allocate object of [type] using the vmRealloc function.
#define ALLOCATE(vm, type) \

View File

@ -332,14 +332,14 @@ static void setNextToken(Parser* parser, TokenType type);
static bool matchChar(Parser* parser, char c);
static bool matchLine(Parser* parser);
static void eatString(Parser* parser) {
static void eatString(Parser* parser, char quote) {
ByteBuffer buff;
byteBufferInit(&buff);
while (true) {
char c = eatChar(parser);
if (c == '"') break;
if (c == quote) break;
if (c == '\0') {
lexError(parser, "Non terminated string.");
@ -352,6 +352,7 @@ static void eatString(Parser* parser) {
if (c == '\\') {
switch (eatChar(parser)) {
case '"': byteBufferWrite(&buff, parser->vm, '"'); break;
case '\'': byteBufferWrite(&buff, parser->vm, '\''); break;
case '\\': byteBufferWrite(&buff, parser->vm, '\\'); break;
case 'n': byteBufferWrite(&buff, parser->vm, '\n'); break;
case 'r': byteBufferWrite(&buff, parser->vm, '\r'); break;
@ -427,7 +428,8 @@ static void eatNumber(Parser* parser) {
while (utilIsDigit(peekChar(parser)))
eatChar(parser);
if (matchChar(parser, '.')) {
if (peekChar(parser) == '.' && utilIsDigit(peekNextChar(parser))) {
matchChar(parser, '.');
while (utilIsDigit(peekChar(parser)))
eatChar(parser);
}
@ -574,7 +576,9 @@ static void lexToken(Parser* parser) {
setNextTwoCharToken(parser, '=', TK_FSLASH, TK_DIVEQ);
return;
case '"': eatString(parser); return;
case '"': eatString(parser, '"'); return;
case '\'': eatString(parser, '\''); return;
default: {
@ -731,14 +735,7 @@ static NameSearchResult compilerSearchName(Compiler* compiler,
NameSearchResult result;
result.type = NAME_NOT_DEFINED;
// Search through builtin functions.
int index = findBuiltinFunction(name, length);
if (index != -1) {
result.type = NAME_BUILTIN;
result.index = index;
return result;
}
int index;
// Search through local and global valriables.
NameDefnType type = NAME_LOCAL_VAR; //< Will change to local.
@ -771,6 +768,14 @@ static NameSearchResult compilerSearchName(Compiler* compiler,
return result;
}
// Search through builtin functions.
index = findBuiltinFunction(name, length);
if (index != -1) {
result.type = NAME_BUILTIN;
result.index = index;
return result;
}
return result;
}
@ -799,7 +804,7 @@ static void exprBinaryOp(Compiler* compiler, bool can_assign);
static void exprUnaryOp(Compiler* compiler, bool can_assign);
static void exprGrouping(Compiler* compiler, bool can_assign);
static void exprArray(Compiler* compiler, bool can_assign);
static void exprList(Compiler* compiler, bool can_assign);
static void exprMap(Compiler* compiler, bool can_assign);
static void exprCall(Compiler* compiler, bool can_assign);
@ -824,7 +829,7 @@ GrammarRule rules[] = { // Prefix Infix Infix Precedence
/* TK_HASH */ NO_RULE,
/* TK_LPARAN */ { exprGrouping, exprCall, PREC_CALL },
/* TK_RPARAN */ NO_RULE,
/* TK_LBRACKET */ { exprArray, exprSubscript, PREC_SUBSCRIPT },
/* TK_LBRACKET */ { exprList, exprSubscript, PREC_SUBSCRIPT },
/* TK_RBRACKET */ NO_RULE,
/* TK_LBRACE */ { exprMap, NULL, NO_INFIX },
/* TK_RBRACE */ NO_RULE,
@ -1026,7 +1031,28 @@ static void exprGrouping(Compiler* compiler, bool can_assign) {
consume(&compiler->parser, TK_RPARAN, "Expected ')' after expression ");
}
static void exprArray(Compiler* compiler, bool can_assign) { TODO; }
static void exprList(Compiler* compiler, bool can_assign) {
emitOpcode(compiler, OP_PUSH_LIST);
int size_index = emitShort(compiler, 0);
int size = 0;
do {
skipNewLines(&compiler->parser);
if (peek(&compiler->parser) == TK_COMMA) break;
compileExpression(compiler);
emitOpcode(compiler, OP_LIST_APPEND);
size++;
} while (match(&compiler->parser, TK_COMMA));
consume(&compiler->parser, TK_RBRACKET, "Expected ']' after list elements.");
compiler->function->fn->opcodes.data[size_index] = (size >> 8) & 0xff;
compiler->function->fn->opcodes.data[size_index + 1] = size & 0xff;
}
static void exprMap(Compiler* compiler, bool can_assign) { TODO; }
static void exprCall(Compiler* compiler, bool can_assign) {
@ -1211,14 +1237,23 @@ static void emitConstant(Compiler* compiler, Var value) {
emitShort(compiler, index);
}
// Update the jump offset.
static void patchJump(Compiler* compiler, int addr_index) {
int jump_to = (int)compiler->function->fn->opcodes.count;
ASSERT(jump_to < MAX_JUMP, "Too large address to jump.");
int jump_to = (int)compiler->function->fn->opcodes.count - addr_index - 2;
ASSERT(jump_to < MAX_JUMP, "Too large address offset to jump to.");
compiler->function->fn->opcodes.data[addr_index] = (jump_to >> 8) & 0xff;
compiler->function->fn->opcodes.data[addr_index + 1] = jump_to & 0xff;
}
// Jump back to the start of the loop.
static void emitLoopJump(Compiler* compiler) {
emitOpcode(compiler, OP_LOOP);
int offset = (int)compiler->function->fn->opcodes.count -
compiler->loop->start + 2;
emitShort(compiler, offset);
}
/****************************************************************************
* COMPILING (PARSE TOPLEVEL) *
****************************************************************************/
@ -1229,14 +1264,12 @@ static void compileBlockBody(Compiler* compiler, bool if_body);
static void compileFunction(Compiler* compiler, bool is_native) {
Parser* parser = &compiler->parser;
consume(&compiler->parser, TK_NAME, "Expected a function name.");
consume(parser, TK_NAME, "Expected a function name.");
const char* name_start = parser->previous.start;
int name_length = parser->previous.length;
NameSearchResult result = compilerSearchName(compiler, name_start,
name_length);
if (result.type != NAME_NOT_DEFINED) {
parseError(&compiler->parser, "Name %.*s already exists.", name_length,
name_start);
@ -1309,6 +1342,8 @@ static void compileFunction(Compiler* compiler, bool is_native) {
// Finish a block body.
static void compileBlockBody(Compiler* compiler, bool if_body) {
consumeStartBlock(&compiler->parser);
compilerEnterBlock(compiler);
skipNewLines(&compiler->parser);
@ -1338,8 +1373,6 @@ static void compileIfStatement(Compiler* compiler) {
emitOpcode(compiler, OP_JUMP_IF_NOT);
int ifpatch = emitShort(compiler, 0xffff); //< Will be patched.
consumeStartBlock(&compiler->parser);
compileBlockBody(compiler, true);
if (match(&compiler->parser, TK_ELIF)) {
@ -1365,30 +1398,78 @@ static void compileWhileStatement(Compiler* compiler) {
loop.outer_loop = compiler->loop;
compiler->loop = &loop;
skipNewLines(&compiler->parser);
compileExpression(compiler); //< Condition.
emitOpcode(compiler, OP_JUMP_IF_NOT);
int whilepatch = emitByte(compiler, 0xffff); //< Will be patched.
compileBlockBody(compiler, false);
emitOpcode(compiler, OP_JUMP);
emitShort(compiler, loop.start);
emitLoopJump(compiler);
patchJump(compiler, whilepatch);
// Patch break statement.
for (int i = 0; i < compiler->loop->patch_count; i++) {
patchJump(compiler, compiler->loop->patches[i]);
}
compiler->loop = loop.outer_loop;
skipNewLines(&compiler->parser);
consume(&compiler->parser, TK_END, "Expected 'end' after statement end.");
}
static void compileForStatement(Compiler* compiler) {
TODO;
compilerEnterBlock(compiler);
Parser* parser = &compiler->parser;
consume(parser, TK_NAME, "Expected an iterator name.");
// Unlike functions local variable could shadow a name.
const char* iter_name = parser->previous.start;
int iter_len = parser->previous.length;
int iter_line = parser->previous.line;
consume(parser, TK_IN, "Expected 'in' after iterator name.");
// Compile and store container.
int container = compilerAddVariable(compiler, "@container", 10, iter_line);
compileExpression(compiler);
// Add iterator to locals. It would initially be null and once the loop
// started it'll be an increasing integer indicating that the current
// loop is nth.
int iterator = compilerAddVariable(compiler, "@iterator", 9, iter_line);
emitOpcode(compiler, OP_PUSH_NULL);
// Add the iteration value. It'll be updated to each element in an array of
// each character in a string etc.
int iter_value = compilerAddVariable(compiler, iter_name, iter_len,
iter_line);
emitOpcode(compiler, OP_PUSH_NULL);
Loop loop;
loop.start = (int)compiler->function->fn->opcodes.count;
loop.patch_count = 0;
loop.outer_loop = compiler->loop;
compiler->loop = &loop;
// Compile next iteration.
emitOpcode(compiler, OP_ITER);
int forpatch = emitShort(compiler, 0xffff);
compileBlockBody(compiler, false);
emitLoopJump(compiler);
patchJump(compiler, forpatch);
// Patch break statement.
for (int i = 0; i < compiler->loop->patch_count; i++) {
patchJump(compiler, compiler->loop->patches[i]);
}
compiler->loop = loop.outer_loop;
skipNewLines(&compiler->parser);
consume(&compiler->parser, TK_END, "Expected 'end' after statement end.");
compilerExitBlock(compiler); //< Iterator scope.
}
// Compiles a statement. Assignment could be an assignment statement or a new
@ -1417,8 +1498,7 @@ static void compileStatement(Compiler* compiler) {
}
consumeEndStatement(parser);
emitOpcode(compiler, OP_JUMP);
emitShort(compiler, compiler->loop->start);
emitLoopJump(compiler);
} else if (match(parser, TK_RETURN)) {

View File

@ -4,6 +4,8 @@
*/
#include "core.h"
#include <math.h>
#include "vm.h"
typedef struct {
@ -15,6 +17,9 @@ typedef struct {
// Count of builtin function +1 for termination.
#define BUILTIN_COUNT 50
// Convert number var as int32_t. Check if it's number before using it.
#define _AS_INTEGER(var) (int32_t)trunc(AS_NUM(var))
// Array of all builtin functions.
_BuiltinFn builtins[BUILTIN_COUNT];
@ -42,6 +47,65 @@ int findBuiltinFunction(const char* name, int length) {
return -1;
}
// Validators /////////////////////////////////////////////////////////////////
// Check if a numeric value bool/number and set [value].
static bool isNumeric(Var var, double* value) {
if (IS_BOOL(var)) {
*value = AS_BOOL(var);
return true;
}
if (IS_NUM(var)) {
*value = AS_NUM(var);
return true;
}
return false;
}
// Check if [var] is bool/number. If not set error and return false.
static bool validateNumeric(MSVM* vm, Var var, double* value,
const char* name) {
if (isNumeric(var, value)) return true;
msSetRuntimeError(vm, "%s must be a numeric value.", name);
return false;
}
// Check if [var] is integer. If not set error and return false.
static bool validateIngeger(MSVM* vm, Var var, int32_t* value,
const char* name) {
double number;
if (isNumeric(var, &number)) {
double truncated = trunc(number);
if (truncated == number) {
*value = (int32_t)(truncated);
return true;
}
}
msSetRuntimeError(vm, "%s must be an integer.", name);
return false;
}
static bool validateIndex(MSVM* vm, int32_t index, int32_t size,
const char* container) {
if (index < 0 || size <= index) {
msSetRuntimeError(vm, "%s index out of range.", container);
return false;
}
return true;
}
// Builtin Functions //////////////////////////////////////////////////////////
// Argument getter (1 based).
#define ARG(n) vm->rbp[n]
// Argument count used in variadic functions.
#define ARGC ((int)(vm->sp - vm->rbp) - 1)
// Set return value.
#define RET(value) vm->rbp[0] = value
Function* getBuiltinFunction(int index) {
ASSERT(index < BUILTIN_COUNT, "Index out of bound.");
return &builtins[index].fn;
@ -49,16 +113,16 @@ Function* getBuiltinFunction(int index) {
#define FN_IS_PRIMITE_TYPE(name, check) \
void coreIs##name(MSVM* vm) { \
vm->rbp[0] = VAR_BOOL(check(vm->rbp[1])); \
RET(VAR_BOOL(check(ARG(1)))); \
}
#define FN_IS_OBJ_TYPE(name, _enum) \
void coreIs##name(MSVM* vm) { \
Var arg1 = vm->rbp[1]; \
Var arg1 = ARG(1); \
if (IS_OBJ(arg1) && AS_OBJ(arg1)->type == _enum) { \
vm->rbp[0] = VAR_TRUE; \
RET(VAR_TRUE); \
} else { \
vm->rbp[0] = VAR_FALSE; \
RET(VAR_FALSE); \
} \
}
@ -67,7 +131,7 @@ 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(List, OBJ_LIST)
FN_IS_OBJ_TYPE(Map, OBJ_MAP)
FN_IS_OBJ_TYPE(Range, OBJ_RANGE)
FN_IS_OBJ_TYPE(Function, OBJ_FUNC)
@ -75,21 +139,25 @@ 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);
RET(VAR_OBJ(&toString(vm, ARG(1), false)->_super));
}
void corePrint(MSVM* vm) {
Var arg1 = vm->rbp[1];
String* str; //< Will be cleaned by garbage collector;
for (int i = 1; i <= ARGC; i++) {
Var arg = ARG(i);
// If it's already a string don't allocate a new string instead use it.
if (IS_OBJ(arg1) && AS_OBJ(arg1)->type == OBJ_STRING) {
str = (String*)AS_OBJ(arg1);
if (IS_OBJ(arg) && AS_OBJ(arg)->type == OBJ_STRING) {
str = (String*)AS_OBJ(arg);
} else {
str = toString(vm, arg1);
str = toString(vm, arg, false);
}
if (i != 1) vm->config.write_fn(vm, " ");
vm->config.write_fn(vm, str->data);
}
vm->config.write_fn(vm, "\n");
}
@ -112,7 +180,7 @@ void initializeCore(MSVM* vm) {
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_list", 1, coreIsList);
initializeBuiltinFN(vm, &builtins[i++], "is_map", 1, coreIsMap);
initializeBuiltinFN(vm, &builtins[i++], "is_range", 1, coreIsRange);
initializeBuiltinFN(vm, &builtins[i++], "is_function", 1, coreIsFunction);
@ -120,7 +188,7 @@ void initializeCore(MSVM* vm) {
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++], "print", -1, corePrint);
initializeBuiltinFN(vm, &builtins[i++], "import", 1, coreImport);
// Sentinal to mark the end of the array.
@ -128,28 +196,6 @@ void initializeCore(MSVM* vm) {
}
// Validators /////////////////////////////////////////////////////////////////
// Check if a numeric value bool/number and set [value].
bool isNumeric(Var var, double* value) {
if (IS_BOOL(var)) {
*value = AS_BOOL(var);
return true;
}
if (IS_NUM(var)) {
*value = AS_NUM(var);
return true;
}
return false;
}
// Check if [var] is bool/number. if not set error and return false.
bool validateNumeric(MSVM* vm, Var var, double* value, const char* arg) {
if (isNumeric(var, value)) return true;
msSetRuntimeError(vm, "%s must be a numeric value.", arg);
return false;
}
// Operators //////////////////////////////////////////////////////////////////
Var varAdd(MSVM* vm, Var v1, Var v2) {
@ -191,6 +237,83 @@ Var varDivide(MSVM* vm, Var v1, Var v2) {
}
bool varIterate(MSVM* vm, Var seq, Var* iterator, Var* value) {
#ifdef DEBUG
int32_t _temp;
ASSERT(IS_NUM(*iterator) || IS_NULL(*iterator), OOPS);
if (IS_NUM(*iterator)) {
ASSERT(validateIngeger(vm, *iterator, &_temp, "Assetion."), OOPS);
}
#endif
// Primitive types are not iterable.
if (!IS_OBJ(seq)) {
if (IS_NULL(seq)) {
msSetRuntimeError(vm, "Null is not iterable.");
} else if (IS_BOOL(seq)) {
msSetRuntimeError(vm, "Boolenan is not iterable.");
} else if (IS_NUM(seq)) {
msSetRuntimeError(vm, "Number is not iterable.");
} else {
UNREACHABLE();
}
*value = VAR_NULL;
return false;
}
Object* obj = AS_OBJ(seq);
int32_t iter = 0; //< Nth iteration.
if (IS_NUM(*iterator)) {
iter = _AS_INTEGER(*iterator);
}
switch (obj->type) {
case OBJ_STRING: {
TODO; // Need to consider utf8.
TODO; // Return string[index].
}
case OBJ_LIST: {
VarBuffer* elems = &((List*)obj)->elements;
if (iter < 0 || iter >= elems->count) {
return false; //< Stop iteration.
}
*value = elems->data[iter];
*iterator = VAR_NUM((double)iter + 1);
return true;
}
case OBJ_MAP:
TODO;
case OBJ_RANGE: {
double from = ((Range*)obj)->from;
double to = ((Range*)obj)->to;
if (from == to) return false;
double current;
if (from <= to) { //< Straight range.
current = from + (double)iter;
} else { //< Reversed range.
current = from - (double)iter;
}
if (current == to) return false;
*value = VAR_NUM(current);
*iterator = VAR_NUM((double)iter + 1);
return true;
}
case OBJ_SCRIPT:
case OBJ_FUNC:
case OBJ_USER:
TODO;
break;
default:
UNREACHABLE();
}
TODO;
return false;
}

View File

@ -18,16 +18,24 @@
OPCODE(CONSTANT, 2, 1)
// Push null on the stack.
OPCODE(PUSH_NULL, 0, 0)
OPCODE(PUSH_NULL, 0, 1)
// Push self on the stack. If the runtime don't have self it'll push null.
OPCODE(PUSH_SELF, 0, 0)
OPCODE(PUSH_SELF, 0, 1)
// Push true on the stack.
OPCODE(PUSH_TRUE, 0, 0)
OPCODE(PUSH_TRUE, 0, 1)
// Push false on the stack.
OPCODE(PUSH_FALSE, 0, 0)
OPCODE(PUSH_FALSE, 0, 1)
// Push a new list to construct from literal.
// param: 2 bytes list size (defalt is 0).
OPCODE(PUSH_LIST, 2, 1)
// Pop the value on the stack the next stack top would be a list. Append the
// value to the list. Used in literal array construction.
OPCODE(LIST_APPEND, 0, -1)
// Push stack local on top of the stack. Locals at 0 to 8 marked explicitly
// since it's performance criticle.
@ -94,14 +102,23 @@ OPCODE(POP, 0, -1)
//OPCODE(CALL_8, 2, -8)
OPCODE(CALL, 4, -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.
// The stack top will be iteration value, next one is iterator (integer) and
// next would be the container. It'll update those values but not push or pop
// any values. We need to ensure that stack state at the point.
// param: 2 bytes jump offset if the iteration should stop.
OPCODE(ITER, 2, 0)
// The address offset to jump to. It'll add the offset to ip.
// param: 2 bytes jump address offset.
OPCODE(JUMP, 2, 0)
// The address offset to jump to. It'll SUBTRACT the offset to ip.
// param: 2 bytes jump address offset.
OPCODE(LOOP, 2, 0)
// Pop the stack top value and if it's true jump.
// param: 2 bytes jump address.
OPCODE(JUMP_NOT, 2, -1)
OPCODE(JUMP_IF, 2, -1)
// Pop the stack top value and if it's false jump.
// param: 2 bytes jump address.

View File

@ -58,6 +58,16 @@ String* newString(MSVM* vm, const char* text, uint32_t length) {
return string;
}
List* newList(MSVM* vm, uint32_t size) {
List* list = ALLOCATE(vm, List);
varInitObject(&list->_super, vm, OBJ_LIST);
varBufferInit(&list->elements);
if (size > 0) {
varBufferFill(&list->elements, vm, VAR_NULL, size);
list->elements.count = 0;
}
}
Range* newRange(MSVM* vm, double from, double to) {
Range* range = ALLOCATE(vm, Range);
varInitObject(&range->_super, vm, OBJ_RANGE);
@ -95,7 +105,7 @@ Function* newFunction(MSVM* vm, const char* name, Script* owner,
func->name = name;
func->owner = owner;
func->arity = -1;
func->arity = -2; // -1 means variadic args.
func->is_native = is_native;
@ -125,7 +135,7 @@ bool isVauesSame(Var v1, Var v2) {
#endif
}
String* toString(MSVM* vm, Var v) {
String* toString(MSVM* vm, Var v, bool recursive) {
if (IS_NULL(v)) {
return newString(vm, "null", 4);
@ -147,10 +157,15 @@ String* toString(MSVM* vm, Var v) {
Object* obj = AS_OBJ(v);
switch (obj->type) {
case OBJ_STRING:
{
// If recursive return with quotes (ex: [42, "hello", 0..10])
if (!recursive)
return newString(vm, ((String*)obj)->data, ((String*)obj)->length);
TODO; //< Add quotes around the string.
break;
}
case OBJ_ARRAY: return newString(vm, "[Array]", 7); // TODO;
case OBJ_LIST: 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;

View File

@ -153,7 +153,7 @@
#define AS_STRING(value) ((String*)AS_OBJ(value))
#define AS_CSTRING(value) (AS_STRING(value)->data)
#define AS_ARRAY(value) ((Array*)AS_OBJ(value))
#define AS_ARRAY(value) ((List*)AS_OBJ(value))
#define AS_MAP(value) ((Map*)AS_OBJ(value))
#define AS_RANGE(value) ((Range*)AS_OBJ(value))
@ -189,7 +189,7 @@ typedef struct {
typedef enum /* ObjectType */ {
OBJ_STRING,
OBJ_ARRAY,
OBJ_LIST,
OBJ_MAP,
OBJ_RANGE,
@ -215,7 +215,7 @@ struct String {
char data[DYNAMIC_TAIL_ARRAY];
};
struct Array {
struct List {
Object _super;
VarBuffer elements; //< Elements of the array.
@ -280,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 List and return List*.
List* newList(MSVM* vm, uint32_t size);
// Allocate new Range object and return Range*.
Range* newRange(MSVM* vm, double from, double to);
@ -296,7 +299,8 @@ Function* newFunction(MSVM* vm, const char* name, Script* owner,
// Returns true if both variables are the same.
bool isVauesSame(Var v1, Var v2);
// Returns the string version of the value.
String* toString(MSVM* vm, Var v);
// Returns the string version of the value. Note: pass false as [_recursive]
// It's an internal use (or may be I could make a wrapper around).
String* toString(MSVM* vm, Var v, bool _recursive);
#endif // VAR_H

View File

@ -61,7 +61,7 @@ void vmPopTempRef(MSVM* self) {
void _printStackTop(MSVM* vm) {
if (vm->sp != vm->stack) {
Var v = *(vm->sp - 1);
printf("%s\n", toString(vm, v)->data);
printf("%s\n", toString(vm, v, false)->data);
}
}
@ -123,7 +123,7 @@ MSInterpretResult vmRunScript(MSVM* vm, Script* _script) {
register uint8_t* ip; //< Current instruction pointer.
register Var* rbp; //< Stack base pointer register.
register CallFrame* frame; //< Current call frame.
Script* script; //< Currently executing script.
register Script* script; //< Currently executing script.
#define PUSH(value) (*vm->sp++ = (value))
#define POP() (*(--vm->sp))
@ -215,6 +215,22 @@ MSInterpretResult vmRunScript(MSVM* vm, Script* _script) {
PUSH(VAR_FALSE);
DISPATCH();
OPCODE(PUSH_LIST):
{
List* list = newList(vm, (uint32_t)READ_SHORT());
PUSH(VAR_OBJ(&list->_super));
DISPATCH();
}
OPCODE(LIST_APPEND):
{
Var elem = POP();
Var list = *(vm->sp - 1);
ASSERT(IS_OBJ(list) && AS_OBJ(list)->type == OBJ_LIST, OOPS);
varBufferWrite(&((List*)AS_OBJ(list))->elements, vm, elem);
DISPATCH();
}
OPCODE(PUSH_LOCAL_0):
OPCODE(PUSH_LOCAL_1):
OPCODE(PUSH_LOCAL_2):
@ -260,7 +276,7 @@ MSInterpretResult vmRunScript(MSVM* vm, Script* _script) {
OPCODE(PUSH_GLOBAL):
{
int index = READ_SHORT();
ASSERT(index < script->globals.count, "Oops a Bug report plese!");
ASSERT(index < script->globals.count, OOPS);
PUSH(script->globals.data[index]);
DISPATCH();
}
@ -268,7 +284,7 @@ MSInterpretResult vmRunScript(MSVM* vm, Script* _script) {
OPCODE(STORE_GLOBAL):
{
int index = READ_SHORT();
ASSERT(index < script->globals.count, "Oops a Bug report plese!");
ASSERT(index < script->globals.count, OOPS);
script->globals.data[index] = POP();
DISPATCH();
}
@ -276,7 +292,7 @@ MSInterpretResult vmRunScript(MSVM* vm, Script* _script) {
OPCODE(PUSH_FN):
{
int index = READ_SHORT();
ASSERT(index < script->functions.count, "Oops a Bug report plese!");
ASSERT(index < script->functions.count, OOPS);
Function* fn = script->functions.data[index];
PUSH(VAR_OBJ(&fn->_super));
DISPATCH();
@ -301,7 +317,9 @@ MSInterpretResult vmRunScript(MSVM* vm, Script* _script) {
if (IS_OBJ(*callable) && AS_OBJ(*callable)->type == OBJ_FUNC) {
Function* fn = (Function*)AS_OBJ(*callable);
if (fn->arity != argc) {
// -1 argument means multiple number of args.
if (fn->arity != -1 && fn->arity != argc) {
RUNTIME_ERROR("Expected excatly %d argument(s).", fn->arity);
}
@ -330,8 +348,35 @@ MSInterpretResult vmRunScript(MSVM* vm, Script* _script) {
DISPATCH();
}
OPCODE(JUMP):
OPCODE(JUMP_NOT):
OPCODE(ITER) :
{
Var* iter_value = (vm->sp - 1);
Var* iterator = (vm->sp - 2);
Var* container = (vm->sp - 3);
int jump_offset = READ_SHORT();
if (!varIterate(vm, *container, iterator, iter_value)) {
DROP(); //< Iter value.
DROP(); //< Iterator.
DROP(); //< Container.
ip += jump_offset;
}
DISPATCH();
}
OPCODE(JUMP): {
int offset = READ_SHORT();
ip += offset;
DISPATCH();
}
OPCODE(LOOP): {
int offset = READ_SHORT();
ip -= offset;
DISPATCH();
}
OPCODE(JUMP_IF):
OPCODE(JUMP_IF_NOT):
TODO;
@ -361,25 +406,40 @@ MSInterpretResult vmRunScript(MSVM* vm, Script* _script) {
OPCODE(SET_ATTRIB):
OPCODE(GET_SUBSCRIPT):
OPCODE(SET_SUBSCRIPT):
TODO;
OPCODE(NEGATIVE):
{
Var num = POP();
if (!IS_NUM(num)) {
RUNTIME_ERROR("Cannot negate a non numeric value.");
}
PUSH(VAR_NUM(-AS_NUM(num)));
DISPATCH();
}
OPCODE(NOT):
OPCODE(BIT_NOT):
TODO;
OPCODE(ADD):
PUSH(varAdd(vm, POP(), POP()));
CHECK_ERROR();
DISPATCH();
OPCODE(SUBTRACT):
PUSH(varSubtract(vm, POP(), POP()));
CHECK_ERROR();
DISPATCH();
OPCODE(MULTIPLY):
PUSH(varMultiply(vm, POP(), POP()));
CHECK_ERROR();
DISPATCH();
OPCODE(DIVIDE):
PUSH(varDivide(vm, POP(), POP()));
CHECK_ERROR();
DISPATCH();
OPCODE(MOD):