mirror of
https://github.com/zekexiao/pocketlang.git
synced 2025-02-11 07:00:58 +08:00
iterator implemented
This commit is contained in:
parent
2b132409e2
commit
38a22fea9f
24
README.md
24
README.md
@ -6,29 +6,29 @@ MiniScript is a simple embeddable, functional, dynamic-typed, bytecode-interpret
|
|||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
|
|
||||||
## Find and return the maximum value in the array.
|
## Find and return the maximum value in the [list].
|
||||||
def get_max(arr)
|
def get_max(list)
|
||||||
ret = arr[0]
|
ret = list[0]
|
||||||
for i in 1..arr.length
|
for i in 1..list.length
|
||||||
ret = max(ret, arr[i])
|
ret = max(ret, list[i])
|
||||||
end
|
end
|
||||||
return ret
|
return ret
|
||||||
end
|
end
|
||||||
|
|
||||||
## Return an array where each element returns true with function [fn] and
|
## Return a list where each element returns true with function [fn] and
|
||||||
## belongs to [arr].
|
## belongs to [list].
|
||||||
def filter(arr, fn)
|
def filter(list, fn)
|
||||||
ret = []
|
ret = []
|
||||||
for elem in arr
|
for elem in list
|
||||||
if fn(elem)
|
if fn(elem)
|
||||||
array_append(ret, elem)
|
list_append(ret, elem)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return ret
|
return ret
|
||||||
end
|
end
|
||||||
|
|
||||||
array = [42, null, 3.14, "String", 0..10, [100]]
|
list = [42, null, 3.14, "String", 0..10, ['hello']]
|
||||||
nums = filter(array, is_num)
|
nums = filter(list, is_num)
|
||||||
print(get_max(nums))
|
print(get_max(nums))
|
||||||
|
|
||||||
```
|
```
|
||||||
|
@ -63,11 +63,10 @@ typedef struct Var Var;
|
|||||||
|
|
||||||
typedef struct Object Object;
|
typedef struct Object Object;
|
||||||
typedef struct String String;
|
typedef struct String String;
|
||||||
typedef struct Array Array;
|
typedef struct List List;
|
||||||
typedef struct Range Range;
|
typedef struct Range Range;
|
||||||
|
|
||||||
typedef struct Script Script;
|
typedef struct Script Script;
|
||||||
//typedef struct Class Class;
|
|
||||||
typedef struct Function Function;
|
typedef struct Function Function;
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
@ -115,6 +114,7 @@ typedef struct Function Function;
|
|||||||
#endif // DEBUG
|
#endif // DEBUG
|
||||||
|
|
||||||
#define TODO ASSERT(false, "TODO")
|
#define TODO ASSERT(false, "TODO")
|
||||||
|
#define OOPS "Oops a bug!! report plese."
|
||||||
|
|
||||||
// Allocate object of [type] using the vmRealloc function.
|
// Allocate object of [type] using the vmRealloc function.
|
||||||
#define ALLOCATE(vm, type) \
|
#define ALLOCATE(vm, type) \
|
||||||
|
140
src/compiler.c
140
src/compiler.c
@ -332,14 +332,14 @@ static void setNextToken(Parser* parser, TokenType type);
|
|||||||
static bool matchChar(Parser* parser, char c);
|
static bool matchChar(Parser* parser, char c);
|
||||||
static bool matchLine(Parser* parser);
|
static bool matchLine(Parser* parser);
|
||||||
|
|
||||||
static void eatString(Parser* parser) {
|
static void eatString(Parser* parser, char quote) {
|
||||||
ByteBuffer buff;
|
ByteBuffer buff;
|
||||||
byteBufferInit(&buff);
|
byteBufferInit(&buff);
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
char c = eatChar(parser);
|
char c = eatChar(parser);
|
||||||
|
|
||||||
if (c == '"') break;
|
if (c == quote) break;
|
||||||
|
|
||||||
if (c == '\0') {
|
if (c == '\0') {
|
||||||
lexError(parser, "Non terminated string.");
|
lexError(parser, "Non terminated string.");
|
||||||
@ -352,6 +352,7 @@ static void eatString(Parser* parser) {
|
|||||||
if (c == '\\') {
|
if (c == '\\') {
|
||||||
switch (eatChar(parser)) {
|
switch (eatChar(parser)) {
|
||||||
case '"': byteBufferWrite(&buff, parser->vm, '"'); break;
|
case '"': byteBufferWrite(&buff, parser->vm, '"'); break;
|
||||||
|
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 'n': byteBufferWrite(&buff, parser->vm, '\n'); break;
|
||||||
case 'r': byteBufferWrite(&buff, parser->vm, '\r'); break;
|
case 'r': byteBufferWrite(&buff, parser->vm, '\r'); break;
|
||||||
@ -427,7 +428,8 @@ static void eatNumber(Parser* parser) {
|
|||||||
while (utilIsDigit(peekChar(parser)))
|
while (utilIsDigit(peekChar(parser)))
|
||||||
eatChar(parser);
|
eatChar(parser);
|
||||||
|
|
||||||
if (matchChar(parser, '.')) {
|
if (peekChar(parser) == '.' && utilIsDigit(peekNextChar(parser))) {
|
||||||
|
matchChar(parser, '.');
|
||||||
while (utilIsDigit(peekChar(parser)))
|
while (utilIsDigit(peekChar(parser)))
|
||||||
eatChar(parser);
|
eatChar(parser);
|
||||||
}
|
}
|
||||||
@ -574,7 +576,9 @@ static void lexToken(Parser* parser) {
|
|||||||
setNextTwoCharToken(parser, '=', TK_FSLASH, TK_DIVEQ);
|
setNextTwoCharToken(parser, '=', TK_FSLASH, TK_DIVEQ);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case '"': eatString(parser); return;
|
case '"': eatString(parser, '"'); return;
|
||||||
|
|
||||||
|
case '\'': eatString(parser, '\''); return;
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
|
|
||||||
@ -731,14 +735,7 @@ static NameSearchResult compilerSearchName(Compiler* compiler,
|
|||||||
|
|
||||||
NameSearchResult result;
|
NameSearchResult result;
|
||||||
result.type = NAME_NOT_DEFINED;
|
result.type = NAME_NOT_DEFINED;
|
||||||
|
int index;
|
||||||
// Search through builtin functions.
|
|
||||||
int index = findBuiltinFunction(name, length);
|
|
||||||
if (index != -1) {
|
|
||||||
result.type = NAME_BUILTIN;
|
|
||||||
result.index = index;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Search through local and global valriables.
|
// Search through local and global valriables.
|
||||||
NameDefnType type = NAME_LOCAL_VAR; //< Will change to local.
|
NameDefnType type = NAME_LOCAL_VAR; //< Will change to local.
|
||||||
@ -771,6 +768,14 @@ static NameSearchResult compilerSearchName(Compiler* compiler,
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Search through builtin functions.
|
||||||
|
index = findBuiltinFunction(name, length);
|
||||||
|
if (index != -1) {
|
||||||
|
result.type = NAME_BUILTIN;
|
||||||
|
result.index = index;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
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 exprUnaryOp(Compiler* compiler, bool can_assign);
|
||||||
|
|
||||||
static void exprGrouping(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 exprMap(Compiler* compiler, bool can_assign);
|
||||||
|
|
||||||
static void exprCall(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_HASH */ NO_RULE,
|
||||||
/* TK_LPARAN */ { exprGrouping, exprCall, PREC_CALL },
|
/* TK_LPARAN */ { exprGrouping, exprCall, PREC_CALL },
|
||||||
/* TK_RPARAN */ NO_RULE,
|
/* TK_RPARAN */ NO_RULE,
|
||||||
/* TK_LBRACKET */ { exprArray, exprSubscript, PREC_SUBSCRIPT },
|
/* TK_LBRACKET */ { exprList, exprSubscript, PREC_SUBSCRIPT },
|
||||||
/* TK_RBRACKET */ NO_RULE,
|
/* TK_RBRACKET */ NO_RULE,
|
||||||
/* TK_LBRACE */ { exprMap, NULL, NO_INFIX },
|
/* TK_LBRACE */ { exprMap, NULL, NO_INFIX },
|
||||||
/* TK_RBRACE */ NO_RULE,
|
/* TK_RBRACE */ NO_RULE,
|
||||||
@ -1026,7 +1031,28 @@ static void exprGrouping(Compiler* compiler, bool can_assign) {
|
|||||||
consume(&compiler->parser, TK_RPARAN, "Expected ')' after expression ");
|
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 exprMap(Compiler* compiler, bool can_assign) { TODO; }
|
||||||
|
|
||||||
static void exprCall(Compiler* compiler, bool can_assign) {
|
static void exprCall(Compiler* compiler, bool can_assign) {
|
||||||
@ -1211,14 +1237,23 @@ static void emitConstant(Compiler* compiler, Var value) {
|
|||||||
emitShort(compiler, index);
|
emitShort(compiler, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update the jump offset.
|
||||||
static void patchJump(Compiler* compiler, int addr_index) {
|
static void patchJump(Compiler* compiler, int addr_index) {
|
||||||
int jump_to = (int)compiler->function->fn->opcodes.count;
|
int jump_to = (int)compiler->function->fn->opcodes.count - addr_index - 2;
|
||||||
ASSERT(jump_to < MAX_JUMP, "Too large address to jump.");
|
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] = (jump_to >> 8) & 0xff;
|
||||||
compiler->function->fn->opcodes.data[addr_index + 1] = jump_to & 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) *
|
* COMPILING (PARSE TOPLEVEL) *
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
@ -1229,14 +1264,12 @@ static void compileBlockBody(Compiler* compiler, bool if_body);
|
|||||||
static void compileFunction(Compiler* compiler, bool is_native) {
|
static void compileFunction(Compiler* compiler, bool is_native) {
|
||||||
|
|
||||||
Parser* parser = &compiler->parser;
|
Parser* parser = &compiler->parser;
|
||||||
|
consume(parser, TK_NAME, "Expected a function name.");
|
||||||
consume(&compiler->parser, TK_NAME, "Expected a function name.");
|
|
||||||
|
|
||||||
const char* name_start = parser->previous.start;
|
const char* name_start = parser->previous.start;
|
||||||
int name_length = parser->previous.length;
|
int name_length = parser->previous.length;
|
||||||
NameSearchResult result = compilerSearchName(compiler, name_start,
|
NameSearchResult result = compilerSearchName(compiler, name_start,
|
||||||
name_length);
|
name_length);
|
||||||
|
|
||||||
if (result.type != NAME_NOT_DEFINED) {
|
if (result.type != NAME_NOT_DEFINED) {
|
||||||
parseError(&compiler->parser, "Name %.*s already exists.", name_length,
|
parseError(&compiler->parser, "Name %.*s already exists.", name_length,
|
||||||
name_start);
|
name_start);
|
||||||
@ -1309,6 +1342,8 @@ static void compileFunction(Compiler* compiler, bool is_native) {
|
|||||||
|
|
||||||
// Finish a block body.
|
// Finish a block body.
|
||||||
static void compileBlockBody(Compiler* compiler, bool if_body) {
|
static void compileBlockBody(Compiler* compiler, bool if_body) {
|
||||||
|
|
||||||
|
consumeStartBlock(&compiler->parser);
|
||||||
compilerEnterBlock(compiler);
|
compilerEnterBlock(compiler);
|
||||||
skipNewLines(&compiler->parser);
|
skipNewLines(&compiler->parser);
|
||||||
|
|
||||||
@ -1338,8 +1373,6 @@ static void compileIfStatement(Compiler* compiler) {
|
|||||||
emitOpcode(compiler, OP_JUMP_IF_NOT);
|
emitOpcode(compiler, OP_JUMP_IF_NOT);
|
||||||
int ifpatch = emitShort(compiler, 0xffff); //< Will be patched.
|
int ifpatch = emitShort(compiler, 0xffff); //< Will be patched.
|
||||||
|
|
||||||
consumeStartBlock(&compiler->parser);
|
|
||||||
|
|
||||||
compileBlockBody(compiler, true);
|
compileBlockBody(compiler, true);
|
||||||
|
|
||||||
if (match(&compiler->parser, TK_ELIF)) {
|
if (match(&compiler->parser, TK_ELIF)) {
|
||||||
@ -1365,30 +1398,78 @@ static void compileWhileStatement(Compiler* compiler) {
|
|||||||
loop.outer_loop = compiler->loop;
|
loop.outer_loop = compiler->loop;
|
||||||
compiler->loop = &loop;
|
compiler->loop = &loop;
|
||||||
|
|
||||||
skipNewLines(&compiler->parser);
|
|
||||||
compileExpression(compiler); //< Condition.
|
compileExpression(compiler); //< Condition.
|
||||||
emitOpcode(compiler, OP_JUMP_IF_NOT);
|
emitOpcode(compiler, OP_JUMP_IF_NOT);
|
||||||
int whilepatch = emitByte(compiler, 0xffff); //< Will be patched.
|
int whilepatch = emitByte(compiler, 0xffff); //< Will be patched.
|
||||||
|
|
||||||
compileBlockBody(compiler, false);
|
compileBlockBody(compiler, false);
|
||||||
|
|
||||||
emitOpcode(compiler, OP_JUMP);
|
emitLoopJump(compiler);
|
||||||
emitShort(compiler, loop.start);
|
|
||||||
|
|
||||||
patchJump(compiler, whilepatch);
|
patchJump(compiler, whilepatch);
|
||||||
|
|
||||||
// Patch break statement.
|
// Patch break statement.
|
||||||
for (int i = 0; i < compiler->loop->patch_count; i++) {
|
for (int i = 0; i < compiler->loop->patch_count; i++) {
|
||||||
patchJump(compiler, compiler->loop->patches[i]);
|
patchJump(compiler, compiler->loop->patches[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
compiler->loop = loop.outer_loop;
|
compiler->loop = loop.outer_loop;
|
||||||
|
|
||||||
skipNewLines(&compiler->parser);
|
skipNewLines(&compiler->parser);
|
||||||
consume(&compiler->parser, TK_END, "Expected 'end' after statement end.");
|
consume(&compiler->parser, TK_END, "Expected 'end' after statement end.");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void compileForStatement(Compiler* compiler) {
|
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
|
// Compiles a statement. Assignment could be an assignment statement or a new
|
||||||
@ -1417,8 +1498,7 @@ static void compileStatement(Compiler* compiler) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
consumeEndStatement(parser);
|
consumeEndStatement(parser);
|
||||||
emitOpcode(compiler, OP_JUMP);
|
emitLoopJump(compiler);
|
||||||
emitShort(compiler, compiler->loop->start);
|
|
||||||
|
|
||||||
} else if (match(parser, TK_RETURN)) {
|
} else if (match(parser, TK_RETURN)) {
|
||||||
|
|
||||||
|
193
src/core.c
193
src/core.c
@ -4,6 +4,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "core.h"
|
#include "core.h"
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
#include "vm.h"
|
#include "vm.h"
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@ -15,6 +17,9 @@ typedef struct {
|
|||||||
// Count of builtin function +1 for termination.
|
// Count of builtin function +1 for termination.
|
||||||
#define BUILTIN_COUNT 50
|
#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.
|
// Array of all builtin functions.
|
||||||
_BuiltinFn builtins[BUILTIN_COUNT];
|
_BuiltinFn builtins[BUILTIN_COUNT];
|
||||||
|
|
||||||
@ -42,6 +47,65 @@ int findBuiltinFunction(const char* name, int length) {
|
|||||||
return -1;
|
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) {
|
Function* getBuiltinFunction(int index) {
|
||||||
ASSERT(index < BUILTIN_COUNT, "Index out of bound.");
|
ASSERT(index < BUILTIN_COUNT, "Index out of bound.");
|
||||||
return &builtins[index].fn;
|
return &builtins[index].fn;
|
||||||
@ -49,16 +113,16 @@ Function* getBuiltinFunction(int index) {
|
|||||||
|
|
||||||
#define FN_IS_PRIMITE_TYPE(name, check) \
|
#define FN_IS_PRIMITE_TYPE(name, check) \
|
||||||
void coreIs##name(MSVM* vm) { \
|
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) \
|
#define FN_IS_OBJ_TYPE(name, _enum) \
|
||||||
void coreIs##name(MSVM* vm) { \
|
void coreIs##name(MSVM* vm) { \
|
||||||
Var arg1 = vm->rbp[1]; \
|
Var arg1 = ARG(1); \
|
||||||
if (IS_OBJ(arg1) && AS_OBJ(arg1)->type == _enum) { \
|
if (IS_OBJ(arg1) && AS_OBJ(arg1)->type == _enum) { \
|
||||||
vm->rbp[0] = VAR_TRUE; \
|
RET(VAR_TRUE); \
|
||||||
} else { \
|
} 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_PRIMITE_TYPE(Num, IS_NUM)
|
||||||
|
|
||||||
FN_IS_OBJ_TYPE(String, OBJ_STRING)
|
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(Map, OBJ_MAP)
|
||||||
FN_IS_OBJ_TYPE(Range, OBJ_RANGE)
|
FN_IS_OBJ_TYPE(Range, OBJ_RANGE)
|
||||||
FN_IS_OBJ_TYPE(Function, OBJ_FUNC)
|
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)
|
FN_IS_OBJ_TYPE(UserObj, OBJ_USER)
|
||||||
|
|
||||||
void coreToString(MSVM* vm) {
|
void coreToString(MSVM* vm) {
|
||||||
Var arg1 = vm->rbp[1];
|
RET(VAR_OBJ(&toString(vm, ARG(1), false)->_super));
|
||||||
vm->rbp[0] = VAR_OBJ(&toString(vm, arg1)->_super);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void corePrint(MSVM* vm) {
|
void corePrint(MSVM* vm) {
|
||||||
Var arg1 = vm->rbp[1];
|
|
||||||
String* str; //< Will be cleaned by garbage collector;
|
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 it's already a string don't allocate a new string instead use it.
|
||||||
if (IS_OBJ(arg1) && AS_OBJ(arg1)->type == OBJ_STRING) {
|
if (IS_OBJ(arg) && AS_OBJ(arg)->type == OBJ_STRING) {
|
||||||
str = (String*)AS_OBJ(arg1);
|
str = (String*)AS_OBJ(arg);
|
||||||
} else {
|
} 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, str->data);
|
||||||
|
}
|
||||||
|
|
||||||
vm->config.write_fn(vm, "\n");
|
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_num", 1, coreIsNum);
|
||||||
|
|
||||||
initializeBuiltinFN(vm, &builtins[i++], "is_string", 1, coreIsString);
|
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_map", 1, coreIsMap);
|
||||||
initializeBuiltinFN(vm, &builtins[i++], "is_range", 1, coreIsRange);
|
initializeBuiltinFN(vm, &builtins[i++], "is_range", 1, coreIsRange);
|
||||||
initializeBuiltinFN(vm, &builtins[i++], "is_function", 1, coreIsFunction);
|
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++], "is_userobj", 1, coreIsUserObj);
|
||||||
|
|
||||||
initializeBuiltinFN(vm, &builtins[i++], "to_string", 1, coreToString);
|
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);
|
initializeBuiltinFN(vm, &builtins[i++], "import", 1, coreImport);
|
||||||
|
|
||||||
// Sentinal to mark the end of the array.
|
// 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 //////////////////////////////////////////////////////////////////
|
// Operators //////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
Var varAdd(MSVM* vm, Var v1, Var v2) {
|
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) {
|
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;
|
TODO;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
@ -18,16 +18,24 @@
|
|||||||
OPCODE(CONSTANT, 2, 1)
|
OPCODE(CONSTANT, 2, 1)
|
||||||
|
|
||||||
// Push null on the stack.
|
// 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.
|
// 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.
|
// Push true on the stack.
|
||||||
OPCODE(PUSH_TRUE, 0, 0)
|
OPCODE(PUSH_TRUE, 0, 1)
|
||||||
|
|
||||||
// Push false on the stack.
|
// 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
|
// Push stack local on top of the stack. Locals at 0 to 8 marked explicitly
|
||||||
// since it's performance criticle.
|
// since it's performance criticle.
|
||||||
@ -94,14 +102,23 @@ OPCODE(POP, 0, -1)
|
|||||||
//OPCODE(CALL_8, 2, -8)
|
//OPCODE(CALL_8, 2, -8)
|
||||||
OPCODE(CALL, 4, -0) //< Will calculated at compile time.
|
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
|
// The stack top will be iteration value, next one is iterator (integer) and
|
||||||
// and the address is absolute not an offset from ip's current value.
|
// next would be the container. It'll update those values but not push or pop
|
||||||
// param: 2 bytes jump address.
|
// 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)
|
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.
|
// Pop the stack top value and if it's true jump.
|
||||||
// param: 2 bytes jump address.
|
// 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.
|
// Pop the stack top value and if it's false jump.
|
||||||
// param: 2 bytes jump address.
|
// param: 2 bytes jump address.
|
||||||
|
21
src/var.c
21
src/var.c
@ -58,6 +58,16 @@ String* newString(MSVM* vm, const char* text, uint32_t length) {
|
|||||||
return string;
|
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* newRange(MSVM* vm, double from, double to) {
|
||||||
Range* range = ALLOCATE(vm, Range);
|
Range* range = ALLOCATE(vm, Range);
|
||||||
varInitObject(&range->_super, vm, OBJ_RANGE);
|
varInitObject(&range->_super, vm, OBJ_RANGE);
|
||||||
@ -95,7 +105,7 @@ Function* newFunction(MSVM* vm, const char* name, Script* owner,
|
|||||||
|
|
||||||
func->name = name;
|
func->name = name;
|
||||||
func->owner = owner;
|
func->owner = owner;
|
||||||
func->arity = -1;
|
func->arity = -2; // -1 means variadic args.
|
||||||
|
|
||||||
func->is_native = is_native;
|
func->is_native = is_native;
|
||||||
|
|
||||||
@ -125,7 +135,7 @@ bool isVauesSame(Var v1, Var v2) {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
String* toString(MSVM* vm, Var v) {
|
String* toString(MSVM* vm, Var v, bool recursive) {
|
||||||
|
|
||||||
if (IS_NULL(v)) {
|
if (IS_NULL(v)) {
|
||||||
return newString(vm, "null", 4);
|
return newString(vm, "null", 4);
|
||||||
@ -147,10 +157,15 @@ String* toString(MSVM* vm, Var v) {
|
|||||||
Object* obj = AS_OBJ(v);
|
Object* obj = AS_OBJ(v);
|
||||||
switch (obj->type) {
|
switch (obj->type) {
|
||||||
case OBJ_STRING:
|
case OBJ_STRING:
|
||||||
|
{
|
||||||
|
// If recursive return with quotes (ex: [42, "hello", 0..10])
|
||||||
|
if (!recursive)
|
||||||
return newString(vm, ((String*)obj)->data, ((String*)obj)->length);
|
return newString(vm, ((String*)obj)->data, ((String*)obj)->length);
|
||||||
|
TODO; //< Add quotes around the string.
|
||||||
break;
|
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_MAP: return newString(vm, "[Map]", 5); // TODO;
|
||||||
case OBJ_RANGE: return newString(vm, "[Range]", 7); // TODO;
|
case OBJ_RANGE: return newString(vm, "[Range]", 7); // TODO;
|
||||||
case OBJ_SCRIPT: return newString(vm, "[Script]", 8); // TODO;
|
case OBJ_SCRIPT: return newString(vm, "[Script]", 8); // TODO;
|
||||||
|
14
src/var.h
14
src/var.h
@ -153,7 +153,7 @@
|
|||||||
|
|
||||||
#define AS_STRING(value) ((String*)AS_OBJ(value))
|
#define AS_STRING(value) ((String*)AS_OBJ(value))
|
||||||
#define AS_CSTRING(value) (AS_STRING(value)->data)
|
#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_MAP(value) ((Map*)AS_OBJ(value))
|
||||||
#define AS_RANGE(value) ((Range*)AS_OBJ(value))
|
#define AS_RANGE(value) ((Range*)AS_OBJ(value))
|
||||||
|
|
||||||
@ -189,7 +189,7 @@ typedef struct {
|
|||||||
|
|
||||||
typedef enum /* ObjectType */ {
|
typedef enum /* ObjectType */ {
|
||||||
OBJ_STRING,
|
OBJ_STRING,
|
||||||
OBJ_ARRAY,
|
OBJ_LIST,
|
||||||
OBJ_MAP,
|
OBJ_MAP,
|
||||||
OBJ_RANGE,
|
OBJ_RANGE,
|
||||||
|
|
||||||
@ -215,7 +215,7 @@ struct String {
|
|||||||
char data[DYNAMIC_TAIL_ARRAY];
|
char data[DYNAMIC_TAIL_ARRAY];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Array {
|
struct List {
|
||||||
Object _super;
|
Object _super;
|
||||||
|
|
||||||
VarBuffer elements; //< Elements of the array.
|
VarBuffer elements; //< Elements of the array.
|
||||||
@ -280,6 +280,9 @@ double varToDouble(Var value);
|
|||||||
// Allocate new String object and return String*.
|
// Allocate new String object and return String*.
|
||||||
String* newString(MSVM* vm, const char* text, uint32_t length);
|
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*.
|
// Allocate new Range object and return Range*.
|
||||||
Range* newRange(MSVM* vm, double from, double to);
|
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.
|
// Returns true if both variables are the same.
|
||||||
bool isVauesSame(Var v1, Var v2);
|
bool isVauesSame(Var v1, Var v2);
|
||||||
|
|
||||||
// Returns the string version of the value.
|
// Returns the string version of the value. Note: pass false as [_recursive]
|
||||||
String* toString(MSVM* vm, Var v);
|
// 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
|
#endif // VAR_H
|
||||||
|
76
src/vm.c
76
src/vm.c
@ -61,7 +61,7 @@ void vmPopTempRef(MSVM* self) {
|
|||||||
void _printStackTop(MSVM* vm) {
|
void _printStackTop(MSVM* vm) {
|
||||||
if (vm->sp != vm->stack) {
|
if (vm->sp != vm->stack) {
|
||||||
Var v = *(vm->sp - 1);
|
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 uint8_t* ip; //< Current instruction pointer.
|
||||||
register Var* rbp; //< Stack base pointer register.
|
register Var* rbp; //< Stack base pointer register.
|
||||||
register CallFrame* frame; //< Current call frame.
|
register CallFrame* frame; //< Current call frame.
|
||||||
Script* script; //< Currently executing script.
|
register Script* script; //< Currently executing script.
|
||||||
|
|
||||||
#define PUSH(value) (*vm->sp++ = (value))
|
#define PUSH(value) (*vm->sp++ = (value))
|
||||||
#define POP() (*(--vm->sp))
|
#define POP() (*(--vm->sp))
|
||||||
@ -215,6 +215,22 @@ MSInterpretResult vmRunScript(MSVM* vm, Script* _script) {
|
|||||||
PUSH(VAR_FALSE);
|
PUSH(VAR_FALSE);
|
||||||
DISPATCH();
|
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_0):
|
||||||
OPCODE(PUSH_LOCAL_1):
|
OPCODE(PUSH_LOCAL_1):
|
||||||
OPCODE(PUSH_LOCAL_2):
|
OPCODE(PUSH_LOCAL_2):
|
||||||
@ -260,7 +276,7 @@ MSInterpretResult vmRunScript(MSVM* vm, Script* _script) {
|
|||||||
OPCODE(PUSH_GLOBAL):
|
OPCODE(PUSH_GLOBAL):
|
||||||
{
|
{
|
||||||
int index = READ_SHORT();
|
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]);
|
PUSH(script->globals.data[index]);
|
||||||
DISPATCH();
|
DISPATCH();
|
||||||
}
|
}
|
||||||
@ -268,7 +284,7 @@ MSInterpretResult vmRunScript(MSVM* vm, Script* _script) {
|
|||||||
OPCODE(STORE_GLOBAL):
|
OPCODE(STORE_GLOBAL):
|
||||||
{
|
{
|
||||||
int index = READ_SHORT();
|
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();
|
script->globals.data[index] = POP();
|
||||||
DISPATCH();
|
DISPATCH();
|
||||||
}
|
}
|
||||||
@ -276,7 +292,7 @@ MSInterpretResult vmRunScript(MSVM* vm, Script* _script) {
|
|||||||
OPCODE(PUSH_FN):
|
OPCODE(PUSH_FN):
|
||||||
{
|
{
|
||||||
int index = READ_SHORT();
|
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];
|
Function* fn = script->functions.data[index];
|
||||||
PUSH(VAR_OBJ(&fn->_super));
|
PUSH(VAR_OBJ(&fn->_super));
|
||||||
DISPATCH();
|
DISPATCH();
|
||||||
@ -301,7 +317,9 @@ MSInterpretResult vmRunScript(MSVM* vm, Script* _script) {
|
|||||||
if (IS_OBJ(*callable) && AS_OBJ(*callable)->type == OBJ_FUNC) {
|
if (IS_OBJ(*callable) && AS_OBJ(*callable)->type == OBJ_FUNC) {
|
||||||
|
|
||||||
Function* fn = (Function*)AS_OBJ(*callable);
|
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);
|
RUNTIME_ERROR("Expected excatly %d argument(s).", fn->arity);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -330,8 +348,35 @@ MSInterpretResult vmRunScript(MSVM* vm, Script* _script) {
|
|||||||
DISPATCH();
|
DISPATCH();
|
||||||
}
|
}
|
||||||
|
|
||||||
OPCODE(JUMP):
|
OPCODE(ITER) :
|
||||||
OPCODE(JUMP_NOT):
|
{
|
||||||
|
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):
|
OPCODE(JUMP_IF_NOT):
|
||||||
TODO;
|
TODO;
|
||||||
|
|
||||||
@ -361,25 +406,40 @@ MSInterpretResult vmRunScript(MSVM* vm, Script* _script) {
|
|||||||
OPCODE(SET_ATTRIB):
|
OPCODE(SET_ATTRIB):
|
||||||
OPCODE(GET_SUBSCRIPT):
|
OPCODE(GET_SUBSCRIPT):
|
||||||
OPCODE(SET_SUBSCRIPT):
|
OPCODE(SET_SUBSCRIPT):
|
||||||
|
TODO;
|
||||||
|
|
||||||
OPCODE(NEGATIVE):
|
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(NOT):
|
||||||
OPCODE(BIT_NOT):
|
OPCODE(BIT_NOT):
|
||||||
TODO;
|
TODO;
|
||||||
|
|
||||||
OPCODE(ADD):
|
OPCODE(ADD):
|
||||||
PUSH(varAdd(vm, POP(), POP()));
|
PUSH(varAdd(vm, POP(), POP()));
|
||||||
|
CHECK_ERROR();
|
||||||
DISPATCH();
|
DISPATCH();
|
||||||
|
|
||||||
OPCODE(SUBTRACT):
|
OPCODE(SUBTRACT):
|
||||||
PUSH(varSubtract(vm, POP(), POP()));
|
PUSH(varSubtract(vm, POP(), POP()));
|
||||||
|
CHECK_ERROR();
|
||||||
DISPATCH();
|
DISPATCH();
|
||||||
|
|
||||||
OPCODE(MULTIPLY):
|
OPCODE(MULTIPLY):
|
||||||
PUSH(varMultiply(vm, POP(), POP()));
|
PUSH(varMultiply(vm, POP(), POP()));
|
||||||
|
CHECK_ERROR();
|
||||||
DISPATCH();
|
DISPATCH();
|
||||||
|
|
||||||
OPCODE(DIVIDE):
|
OPCODE(DIVIDE):
|
||||||
PUSH(varDivide(vm, POP(), POP()));
|
PUSH(varDivide(vm, POP(), POP()));
|
||||||
|
CHECK_ERROR();
|
||||||
DISPATCH();
|
DISPATCH();
|
||||||
|
|
||||||
OPCODE(MOD):
|
OPCODE(MOD):
|
||||||
|
Loading…
Reference in New Issue
Block a user