mirror of
https://github.com/zekexiao/pocketlang.git
synced 2025-02-10 22:50:58 +08:00
Merge pull request #2 from ThakeeNathees/increment-operators
Increment operators
This commit is contained in:
commit
1243bbad5b
@ -78,7 +78,7 @@ typedef struct {
|
||||
} MSConfiguration;
|
||||
|
||||
typedef enum {
|
||||
RESULT_SUCCESS,
|
||||
RESULT_SUCCESS = 0,
|
||||
RESULT_COMPILE_ERROR,
|
||||
RESULT_RUNTIME_ERROR,
|
||||
} MSInterpretResult;
|
||||
|
287
src/compiler.c
287
src/compiler.c
@ -5,6 +5,9 @@
|
||||
|
||||
#include "compiler.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "core.h"
|
||||
#include "types/name_table.h"
|
||||
#include "types/gen/byte_buffer.h"
|
||||
@ -481,10 +484,10 @@ static void eatNumber(Parser* parser) {
|
||||
|
||||
// Read and ignore chars till it reach new line or EOF.
|
||||
static void skipLineComment(Parser* parser) {
|
||||
char c = eatChar(parser);
|
||||
|
||||
while (c != '\n' && c != '\0') {
|
||||
c = eatChar(parser);
|
||||
char c;
|
||||
while ((c = peekChar(parser)) != '\0') {
|
||||
eatChar(parser);
|
||||
if (c == '\n') return;
|
||||
}
|
||||
}
|
||||
|
||||
@ -680,6 +683,24 @@ static bool match(Parser* self, TokenType expected) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Consume the the current token and if it's not [expected] emits error log
|
||||
// and continue parsing for more error logs.
|
||||
static void consume(Parser* self, TokenType expected, const char* err_msg) {
|
||||
//ASSERT(expected != TK_LINE, "Can't match TK_LINE.");
|
||||
//matchLine(self);
|
||||
|
||||
lexToken(self);
|
||||
if (self->previous.type != expected) {
|
||||
parseError(self, "%s", err_msg);
|
||||
|
||||
// If the next token is expected discard the current to minimize
|
||||
// cascaded errors and continue parsing.
|
||||
if (peek(self) == expected) {
|
||||
lexToken(self);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Match one or more lines and return true if there any.
|
||||
static bool matchLine(Parser* parser) {
|
||||
if (peek(parser) != TK_LINE) return false;
|
||||
@ -688,17 +709,21 @@ static bool matchLine(Parser* parser) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Match semi collon or multiple new lines.
|
||||
static void consumeEndStatement(Parser* parser) {
|
||||
bool consumed = false;
|
||||
|
||||
// Semi collon must be on the same line.
|
||||
if (peek(parser) == TK_SEMICOLLON) {
|
||||
match(parser, TK_SEMICOLLON);
|
||||
consumed = true;
|
||||
// Match semi collon, multiple new lines or peek 'end' keyword.
|
||||
static bool matchEndStatement(Parser* parser) {
|
||||
if (match(parser, TK_SEMICOLLON)) {
|
||||
skipNewLines(parser);
|
||||
return true;
|
||||
}
|
||||
if (matchLine(parser)) consumed = true;
|
||||
if (!consumed && peek(parser) != TK_EOF) {
|
||||
|
||||
if (matchLine(parser) || peek(parser) == TK_END || peek(parser) == TK_EOF)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Consume semi collon, multiple new lines or peek 'end' keyword.
|
||||
static void consumeEndStatement(Parser* parser) {
|
||||
if (!matchEndStatement(parser)) {
|
||||
parseError(parser, "Expected statement end with newline or ';'.");
|
||||
}
|
||||
}
|
||||
@ -719,22 +744,14 @@ static void consumeStartBlock(Parser* parser) {
|
||||
}
|
||||
}
|
||||
|
||||
// Consume the the current token and if it's not [expected] emits error log
|
||||
// and continue parsing for more error logs.
|
||||
static void consume(Parser* self, TokenType expected, const char* err_msg) {
|
||||
//ASSERT(expected != TK_LINE, "Can't match TK_LINE.");
|
||||
//matchLine(self);
|
||||
|
||||
lexToken(self);
|
||||
if (self->previous.type != expected) {
|
||||
parseError(self, "%s", err_msg);
|
||||
|
||||
// If the next token is expected discard the current to minimize
|
||||
// cascaded errors and continue parsing.
|
||||
if (peek(self) == expected) {
|
||||
lexToken(self);
|
||||
}
|
||||
}
|
||||
// Returns a optional compound assignment.
|
||||
static bool matchAssignment(Parser* parser) {
|
||||
if (match(parser, TK_EQ)) return true;
|
||||
if (match(parser, TK_PLUSEQ)) return true;
|
||||
if (match(parser, TK_MINUSEQ)) return true;
|
||||
if (match(parser, TK_STAREQ)) return true;
|
||||
if (match(parser, TK_DIVEQ)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
@ -866,7 +883,7 @@ GrammarRule rules[] = { // Prefix Infix Infix Precedence
|
||||
/* TK_ERROR */ NO_RULE,
|
||||
/* TK_EOF */ NO_RULE,
|
||||
/* TK_LINE */ NO_RULE,
|
||||
/* TK_DOT */ { exprAttrib, NULL, PREC_ATTRIB },
|
||||
/* TK_DOT */ { NULL, exprAttrib, PREC_ATTRIB },
|
||||
/* TK_DOTDOT */ { NULL, exprBinaryOp, PREC_RANGE },
|
||||
/* TK_COMMA */ NO_RULE,
|
||||
/* TK_COLLON */ NO_RULE,
|
||||
@ -932,7 +949,7 @@ static GrammarRule* getRule(TokenType type) {
|
||||
}
|
||||
|
||||
// Emit variable store.
|
||||
static void _emitStoreVariable(Compiler* compiler, int index, bool global) {
|
||||
static void emitStoreVariable(Compiler* compiler, int index, bool global) {
|
||||
if (global) {
|
||||
emitOpcode(compiler, OP_STORE_GLOBAL);
|
||||
emitShort(compiler, index);
|
||||
@ -947,7 +964,7 @@ static void _emitStoreVariable(Compiler* compiler, int index, bool global) {
|
||||
}
|
||||
}
|
||||
|
||||
static void _emitPushVariable(Compiler* compiler, int index, bool global) {
|
||||
static void emitPushVariable(Compiler* compiler, int index, bool global) {
|
||||
if (global) {
|
||||
emitOpcode(compiler, OP_PUSH_GLOBAL);
|
||||
emitShort(compiler, index);
|
||||
@ -977,45 +994,56 @@ static void exprFunc(Compiler* compiler, bool can_assign) {
|
||||
|
||||
// Local/global variables, script/native/builtin functions name.
|
||||
static void exprName(Compiler* compiler, bool can_assign) {
|
||||
const char* name_start = compiler->parser.previous.start;
|
||||
int name_len = compiler->parser.previous.length;
|
||||
int name_line = compiler->parser.previous.line;
|
||||
|
||||
Parser* parser = &compiler->parser;
|
||||
|
||||
const char* name_start = parser->previous.start;
|
||||
int name_len = parser->previous.length;
|
||||
int name_line = parser->previous.line;
|
||||
NameSearchResult result = compilerSearchName(compiler, name_start, name_len);
|
||||
|
||||
if (result.type == NAME_NOT_DEFINED) {
|
||||
|
||||
if (can_assign && match(&compiler->parser, TK_EQ)) {
|
||||
if (can_assign && match(parser, TK_EQ)) {
|
||||
int index = compilerAddVariable(compiler, name_start, name_len,
|
||||
name_line);
|
||||
compileExpression(compiler);
|
||||
_emitStoreVariable(compiler, index, compiler->scope_depth == DEPTH_GLOBAL);
|
||||
emitStoreVariable(compiler, index, compiler->scope_depth == DEPTH_GLOBAL);
|
||||
return;
|
||||
} else {
|
||||
parseError(&compiler->parser, "Name \"%.*s\" is not defined.", name_len,
|
||||
name_start);
|
||||
parseError(parser, "Name \"%.*s\" is not defined.", name_len, name_start);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: can_assign and += -= etc.
|
||||
|
||||
switch (result.type) {
|
||||
case NAME_LOCAL_VAR:
|
||||
if (can_assign && match(&compiler->parser, TK_EQ)) {
|
||||
compileExpression(compiler);
|
||||
_emitStoreVariable(compiler, result.index, false);
|
||||
} else {
|
||||
_emitPushVariable(compiler, result.index, false);
|
||||
}
|
||||
return;
|
||||
|
||||
case NAME_GLOBAL_VAR:
|
||||
if (can_assign && match(&compiler->parser, TK_EQ)) {
|
||||
compileExpression(compiler);
|
||||
_emitStoreVariable(compiler, result.index, true);
|
||||
|
||||
|
||||
if (can_assign && matchAssignment(parser)) {
|
||||
TokenType assignment = parser->previous.type;
|
||||
if (assignment != TK_EQ) {
|
||||
emitPushVariable(compiler, result.index, result.type == NAME_GLOBAL_VAR);
|
||||
compileExpression(compiler);
|
||||
|
||||
switch (assignment) {
|
||||
case TK_PLUSEQ: emitOpcode(compiler, OP_ADD); break;
|
||||
case TK_MINUSEQ: emitOpcode(compiler, OP_SUBTRACT); break;
|
||||
case TK_STAREQ: emitOpcode(compiler, OP_MULTIPLY); break;
|
||||
case TK_DIVEQ: emitOpcode(compiler, OP_DIVIDE); break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
|
||||
}
|
||||
} else {
|
||||
compileExpression(compiler);
|
||||
}
|
||||
|
||||
emitStoreVariable(compiler, result.index, result.type == NAME_GLOBAL_VAR);
|
||||
|
||||
} else {
|
||||
emitOpcode(compiler, OP_PUSH_GLOBAL);
|
||||
emitShort(compiler, result.index);
|
||||
emitPushVariable(compiler, result.index, result.type == NAME_GLOBAL_VAR);
|
||||
}
|
||||
return;
|
||||
|
||||
@ -1126,9 +1154,10 @@ static void exprCall(Compiler* compiler, bool can_assign) {
|
||||
}
|
||||
|
||||
static void exprAttrib(Compiler* compiler, bool can_assign) {
|
||||
consume(&compiler->parser, TK_NAME, "Expected an attribute name after '.'.");
|
||||
const char* name = compiler->parser.previous.start;
|
||||
int length = compiler->parser.previous.length;
|
||||
Parser* parser = &compiler->parser;
|
||||
consume(parser, TK_NAME, "Expected an attribute name after '.'.");
|
||||
const char* name = parser->previous.start;
|
||||
int length = parser->previous.length;
|
||||
|
||||
// Store the name in script's names.
|
||||
String* string = newString(compiler->vm, name, length);
|
||||
@ -1138,9 +1167,27 @@ static void exprAttrib(Compiler* compiler, bool can_assign) {
|
||||
|
||||
int index = (int)compiler->script->names.count - 1;
|
||||
|
||||
// TODO: +=, -=, ...
|
||||
if (can_assign && match(&compiler->parser, TK_EQ)) {
|
||||
compileExpression(compiler);
|
||||
if (can_assign && matchAssignment(parser)) {
|
||||
|
||||
TokenType assignment = parser->previous.type;
|
||||
if (assignment != TK_EQ) {
|
||||
emitOpcode(compiler, OP_GET_ATTRIB_AOP);
|
||||
emitShort(compiler, index);
|
||||
compileExpression(compiler);
|
||||
|
||||
switch (assignment) {
|
||||
case TK_PLUSEQ: emitOpcode(compiler, OP_ADD); break;
|
||||
case TK_MINUSEQ: emitOpcode(compiler, OP_SUBTRACT); break;
|
||||
case TK_STAREQ: emitOpcode(compiler, OP_MULTIPLY); break;
|
||||
case TK_DIVEQ: emitOpcode(compiler, OP_DIVIDE); break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
compileExpression(compiler);
|
||||
}
|
||||
|
||||
emitOpcode(compiler, OP_SET_ATTRIB);
|
||||
emitShort(compiler, index);
|
||||
|
||||
@ -1150,7 +1197,37 @@ static void exprAttrib(Compiler* compiler, bool can_assign) {
|
||||
}
|
||||
}
|
||||
|
||||
static void exprSubscript(Compiler* compiler, bool can_assign) { TODO; }
|
||||
static void exprSubscript(Compiler* compiler, bool can_assign) {
|
||||
Parser* parser = &compiler->parser;
|
||||
compileExpression(compiler);
|
||||
consume(parser, TK_RBRACKET, "Expected ']' after subscription ends.");
|
||||
|
||||
if (can_assign && matchAssignment(parser)) {
|
||||
|
||||
TokenType assignment = parser->previous.type;
|
||||
if (assignment != TK_EQ) {
|
||||
emitOpcode(compiler, OP_GET_SUBSCRIPT_AOP);
|
||||
compileExpression(compiler);
|
||||
|
||||
switch (assignment) {
|
||||
case TK_PLUSEQ: emitOpcode(compiler, OP_ADD); break;
|
||||
case TK_MINUSEQ: emitOpcode(compiler, OP_SUBTRACT); break;
|
||||
case TK_STAREQ: emitOpcode(compiler, OP_MULTIPLY); break;
|
||||
case TK_DIVEQ: emitOpcode(compiler, OP_DIVIDE); break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
compileExpression(compiler);
|
||||
}
|
||||
|
||||
emitOpcode(compiler, OP_SET_SUBSCRIPT);
|
||||
|
||||
} else {
|
||||
emitOpcode(compiler, OP_GET_SUBSCRIPT);
|
||||
}
|
||||
}
|
||||
|
||||
static void exprValue(Compiler* compiler, bool can_assign) {
|
||||
TokenType op = compiler->parser.previous.type;
|
||||
@ -1309,8 +1386,16 @@ static void emitLoopJump(Compiler* compiler) {
|
||||
* COMPILING (PARSE TOPLEVEL) *
|
||||
****************************************************************************/
|
||||
|
||||
typedef enum {
|
||||
BLOCK_FUNC,
|
||||
BLOCK_LOOP,
|
||||
BLOCK_IF,
|
||||
BLOCK_ELIF,
|
||||
BLOCK_ELSE,
|
||||
} BlockType;
|
||||
|
||||
static void compileStatement(Compiler* compiler);
|
||||
static void compileBlockBody(Compiler* compiler, bool if_body);
|
||||
static void compileBlockBody(Compiler* compiler, BlockType type);
|
||||
|
||||
// Compile a function and return it's index in the script's function buffer.
|
||||
static int compileFunction(Compiler* compiler, FuncType fn_type) {
|
||||
@ -1318,34 +1403,28 @@ static int compileFunction(Compiler* compiler, FuncType fn_type) {
|
||||
Parser* parser = &compiler->parser;
|
||||
|
||||
const char* name;
|
||||
int name_length;
|
||||
|
||||
if (fn_type != FN_LITERAL) {
|
||||
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 = parser->previous.start;
|
||||
name_length = parser->previous.length;
|
||||
NameSearchResult result = compilerSearchName(compiler, name,
|
||||
name_length);
|
||||
if (result.type != NAME_NOT_DEFINED) {
|
||||
parseError(&compiler->parser, "Name %.*s already exists.", name_length,
|
||||
name_start);
|
||||
name);
|
||||
// Not returning here as to complete for skip cascaded errors.
|
||||
}
|
||||
|
||||
int index = nameTableAdd(&compiler->script->function_names, compiler->vm,
|
||||
name_start, name_length);
|
||||
name = nameTableGet(&compiler->script->function_names, index);
|
||||
|
||||
} else {
|
||||
name = "[FunctionLiteral]";
|
||||
name_length = (int)strlen(name);
|
||||
}
|
||||
|
||||
Function* func = newFunction(compiler->vm, name,
|
||||
Function* func = newFunction(compiler->vm, name, name_length,
|
||||
compiler->script, fn_type == FN_NATIVE);
|
||||
|
||||
vmPushTempRef(compiler->vm, &func->_super);
|
||||
functionBufferWrite(&compiler->script->functions, compiler->vm, func);
|
||||
vmPopTempRef(compiler->vm);
|
||||
int fn_index = compiler->script->functions.count - 1;
|
||||
int fn_index = (int)compiler->script->functions.count - 1;
|
||||
|
||||
Func curr_func;
|
||||
curr_func.outer_func = compiler->func;
|
||||
@ -1392,7 +1471,7 @@ static int compileFunction(Compiler* compiler, FuncType fn_type) {
|
||||
func->arity = argc;
|
||||
|
||||
if (fn_type != FN_NATIVE) {
|
||||
compileBlockBody(compiler, false);
|
||||
compileBlockBody(compiler, BLOCK_FUNC);
|
||||
}
|
||||
|
||||
consume(parser, TK_END, "Expected 'end' after function definition end.");
|
||||
@ -1408,11 +1487,16 @@ static int compileFunction(Compiler* compiler, FuncType fn_type) {
|
||||
}
|
||||
|
||||
// Finish a block body.
|
||||
static void compileBlockBody(Compiler* compiler, bool if_body) {
|
||||
static void compileBlockBody(Compiler* compiler, BlockType type) {
|
||||
|
||||
consumeStartBlock(&compiler->parser);
|
||||
compilerEnterBlock(compiler);
|
||||
skipNewLines(&compiler->parser);
|
||||
|
||||
if (type != BLOCK_ELIF) {
|
||||
consumeStartBlock(&compiler->parser);
|
||||
skipNewLines(&compiler->parser);
|
||||
}
|
||||
|
||||
bool if_body = (type == BLOCK_IF) || (type == BLOCK_ELIF);
|
||||
|
||||
TokenType next = peek(&compiler->parser);
|
||||
while (!(next == TK_END || next == TK_EOF || (
|
||||
@ -1440,22 +1524,45 @@ static void compileIfStatement(Compiler* compiler) {
|
||||
emitOpcode(compiler, OP_JUMP_IF_NOT);
|
||||
int ifpatch = emitShort(compiler, 0xffff); //< Will be patched.
|
||||
|
||||
compileBlockBody(compiler, true);
|
||||
compileBlockBody(compiler, BLOCK_IF);
|
||||
|
||||
// Elif statement's don't consume 'end' after they end since it's treated as
|
||||
// else and if they require 2 'end' statements. But we're omitting the 'end'
|
||||
// for the 'else' since it'll consumed by the 'if'.
|
||||
bool elif = false;
|
||||
|
||||
if (peek(&compiler->parser) == TK_ELIF) {
|
||||
elif = true;
|
||||
// Override the elif to if so that it'll be parsed as a new if statement
|
||||
// and that's why we're not consuming it here.
|
||||
compiler->parser.current.type = TK_IF;
|
||||
|
||||
// Jump pass else.
|
||||
emitOpcode(compiler, OP_JUMP);
|
||||
int exit_jump = emitShort(compiler, 0xffff); //< Will be patched.
|
||||
|
||||
if (match(&compiler->parser, TK_ELIF)) {
|
||||
patchJump(compiler, ifpatch);
|
||||
compileBlockBody(compiler, true);
|
||||
compileBlockBody(compiler, BLOCK_ELIF);
|
||||
patchJump(compiler, exit_jump);
|
||||
|
||||
} else if (match(&compiler->parser, TK_ELSE)) {
|
||||
|
||||
// Jump pass else.
|
||||
emitOpcode(compiler, OP_JUMP);
|
||||
int exit_jump = emitShort(compiler, 0xffff); //< Will be patched.
|
||||
|
||||
patchJump(compiler, ifpatch);
|
||||
compileBlockBody(compiler, false);
|
||||
compileBlockBody(compiler, BLOCK_ELSE);
|
||||
patchJump(compiler, exit_jump);
|
||||
|
||||
} else {
|
||||
patchJump(compiler, ifpatch);
|
||||
}
|
||||
|
||||
skipNewLines(&compiler->parser);
|
||||
consume(&compiler->parser, TK_END, "Expected 'end' after statement end.");
|
||||
if (!elif) {
|
||||
skipNewLines(&compiler->parser);
|
||||
consume(&compiler->parser, TK_END, "Expected 'end' after statement end.");
|
||||
}
|
||||
}
|
||||
|
||||
static void compileWhileStatement(Compiler* compiler) {
|
||||
@ -1469,7 +1576,7 @@ static void compileWhileStatement(Compiler* compiler) {
|
||||
emitOpcode(compiler, OP_JUMP_IF_NOT);
|
||||
int whilepatch = emitByte(compiler, 0xffff); //< Will be patched.
|
||||
|
||||
compileBlockBody(compiler, false);
|
||||
compileBlockBody(compiler, BLOCK_LOOP);
|
||||
|
||||
emitLoopJump(compiler);
|
||||
patchJump(compiler, whilepatch);
|
||||
@ -1523,7 +1630,7 @@ static void compileForStatement(Compiler* compiler) {
|
||||
emitOpcode(compiler, OP_ITER);
|
||||
int forpatch = emitShort(compiler, 0xffff);
|
||||
|
||||
compileBlockBody(compiler, false);
|
||||
compileBlockBody(compiler, BLOCK_LOOP);
|
||||
|
||||
emitLoopJump(compiler);
|
||||
patchJump(compiler, forpatch);
|
||||
@ -1574,7 +1681,7 @@ static void compileStatement(Compiler* compiler) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (match(parser, TK_SEMICOLLON)|| match(parser, TK_LINE)) {
|
||||
if (matchEndStatement(parser)) {
|
||||
emitOpcode(compiler, OP_PUSH_NULL);
|
||||
emitOpcode(compiler, OP_RETURN);
|
||||
} else {
|
||||
|
256
src/core.c
256
src/core.c
@ -6,6 +6,8 @@
|
||||
#include "core.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "vm.h"
|
||||
|
||||
typedef struct {
|
||||
@ -47,10 +49,12 @@ int findBuiltinFunction(const char* name, int length) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Validators /////////////////////////////////////////////////////////////////
|
||||
/*****************************************************************************/
|
||||
/* VALIDATORS */
|
||||
/*****************************************************************************/
|
||||
|
||||
// Check if a numeric value bool/number and set [value].
|
||||
static bool isNumeric(Var var, double* value) {
|
||||
static inline bool isNumeric(Var var, double* value) {
|
||||
if (IS_BOOL(var)) {
|
||||
*value = AS_BOOL(var);
|
||||
return true;
|
||||
@ -63,7 +67,7 @@ static bool isNumeric(Var var, double* value) {
|
||||
}
|
||||
|
||||
// Check if [var] is bool/number. If not set error and return false.
|
||||
static bool validateNumeric(MSVM* vm, Var var, double* value,
|
||||
static inline 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);
|
||||
@ -71,7 +75,7 @@ static bool validateNumeric(MSVM* vm, Var var, double* value,
|
||||
}
|
||||
|
||||
// Check if [var] is integer. If not set error and return false.
|
||||
static bool validateIngeger(MSVM* vm, Var var, int32_t* value,
|
||||
static inline bool validateIngeger(MSVM* vm, Var var, int32_t* value,
|
||||
const char* name) {
|
||||
double number;
|
||||
if (isNumeric(var, &number)) {
|
||||
@ -86,7 +90,7 @@ static bool validateIngeger(MSVM* vm, Var var, int32_t* value,
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool validateIndex(MSVM* vm, int32_t index, int32_t size,
|
||||
static inline 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);
|
||||
@ -95,7 +99,9 @@ static bool validateIndex(MSVM* vm, int32_t index, int32_t size,
|
||||
return true;
|
||||
}
|
||||
|
||||
// Builtin Functions //////////////////////////////////////////////////////////
|
||||
/*****************************************************************************/
|
||||
/* BUILTIN FUNCTIONS */
|
||||
/*****************************************************************************/
|
||||
|
||||
// Argument getter (1 based).
|
||||
#define ARG(n) vm->rbp[n]
|
||||
@ -104,7 +110,11 @@ static bool validateIndex(MSVM* vm, int32_t index, int32_t size,
|
||||
#define ARGC ((int)(vm->sp - vm->rbp) - 1)
|
||||
|
||||
// Set return value.
|
||||
#define RET(value) vm->rbp[0] = value
|
||||
#define RET(value) \
|
||||
do { \
|
||||
vm->rbp[0] = value; \
|
||||
return; \
|
||||
} while (false)
|
||||
|
||||
Function* getBuiltinFunction(int index) {
|
||||
ASSERT(index < BUILTIN_COUNT, "Index out of bound.");
|
||||
@ -163,13 +173,44 @@ void corePrint(MSVM* vm) {
|
||||
|
||||
void coreImport(MSVM* vm) {
|
||||
Var arg1 = vm->rbp[1];
|
||||
if (IS_OBJ(arg1) && AS_OBJ(arg1)->type == OBJ_STRING) {
|
||||
TODO;
|
||||
} else {
|
||||
if (!IS_OBJ(arg1) || !AS_OBJ(arg1)->type == OBJ_STRING) {
|
||||
msSetRuntimeError(vm, "Expected a String argument.");
|
||||
}
|
||||
|
||||
String* path = (String*)AS_OBJ(arg1);
|
||||
if (path->length > 4 && strncmp(path->data, "std:", 4) == 0) {
|
||||
Script* scr = vmGetStdScript(vm, path->data + 4);
|
||||
ASSERT(scr != NULL, OOPS);
|
||||
RET(VAR_OBJ(scr));
|
||||
}
|
||||
|
||||
TODO;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* STD METHODS */
|
||||
/*****************************************************************************/
|
||||
|
||||
// std:list Methods.
|
||||
void stdListSort(MSVM* vm) {
|
||||
Var list = ARG(1);
|
||||
if (!IS_OBJ(list) || AS_OBJ(list)->type != OBJ_LIST) {
|
||||
msSetRuntimeError(vm, "Expected a list at argument 1.");
|
||||
}
|
||||
|
||||
// TODO: sort.
|
||||
|
||||
RET(list);
|
||||
}
|
||||
|
||||
// std:os Methods.
|
||||
void stdOsClock(MSVM* vm) {
|
||||
RET(VAR_NUM((double)clock() / CLOCKS_PER_SEC));
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* CORE INITIALIZATION */
|
||||
/*****************************************************************************/
|
||||
void initializeCore(MSVM* vm) {
|
||||
|
||||
int i = 0; //< Iterate through builtins.
|
||||
@ -194,9 +235,40 @@ void initializeCore(MSVM* vm) {
|
||||
// Sentinal to mark the end of the array.
|
||||
initializeBuiltinFN(vm, &builtins[i], NULL, 0, NULL);
|
||||
|
||||
// Make STD scripts.
|
||||
Script* std; // A temporary pointer to the current std script.
|
||||
Function* fn; // A temporary pointer to the allocated function function.
|
||||
|
||||
#define STD_NEW_SCRIPT(_name) \
|
||||
do { \
|
||||
std = newScript(vm); \
|
||||
std->name = _name; \
|
||||
std->name_length = (int)strlen(_name); \
|
||||
vmPushTempRef(vm, &std->_super); \
|
||||
vmAddStdScript(vm, std); \
|
||||
vmPopTempRef(vm); \
|
||||
} while (false)
|
||||
|
||||
#define STD_ADD_FUNCTION(_name, fptr, _arity) \
|
||||
do { \
|
||||
fn = newFunction(vm, _name, (int)strlen(_name), std, true); \
|
||||
fn->native = fptr; \
|
||||
fn->arity = _arity; \
|
||||
} while (false)
|
||||
|
||||
// std:list script.
|
||||
STD_NEW_SCRIPT("std:list");
|
||||
STD_ADD_FUNCTION("sort", stdListSort, 1);
|
||||
|
||||
|
||||
// std:os script.
|
||||
STD_NEW_SCRIPT("std:os");
|
||||
STD_ADD_FUNCTION("clock", stdOsClock, 0); // TODO: rename coreClock.
|
||||
}
|
||||
|
||||
// Operators //////////////////////////////////////////////////////////////////
|
||||
/*****************************************************************************/
|
||||
/* OPERATORS */
|
||||
/*****************************************************************************/
|
||||
|
||||
Var varAdd(MSVM* vm, Var v1, Var v2) {
|
||||
|
||||
@ -213,7 +285,17 @@ Var varAdd(MSVM* vm, Var v1, Var v2) {
|
||||
}
|
||||
|
||||
Var varSubtract(MSVM* vm, Var v1, Var v2) {
|
||||
TODO;
|
||||
double d1, d2;
|
||||
if (isNumeric(v1, &d1)) {
|
||||
if (validateNumeric(vm, v2, &d2, "Right operand")) {
|
||||
return VAR_NUM(d1 - d2);
|
||||
}
|
||||
return VAR_NULL;
|
||||
}
|
||||
|
||||
msSetRuntimeError(vm, "Unsupported operand types for operator '-' "
|
||||
"%s and %s", varTypeName(v1), varTypeName(v2));
|
||||
|
||||
return VAR_NULL;
|
||||
}
|
||||
|
||||
@ -227,15 +309,159 @@ Var varMultiply(MSVM* vm, Var v1, Var v2) {
|
||||
return VAR_NULL;
|
||||
}
|
||||
|
||||
TODO;
|
||||
msSetRuntimeError(vm, "Unsupported operand types for operator '*' "
|
||||
"%s and %s", varTypeName(v1), varTypeName(v2));
|
||||
return VAR_NULL;
|
||||
}
|
||||
|
||||
Var varDivide(MSVM* vm, Var v1, Var v2) {
|
||||
TODO;
|
||||
double d1, d2;
|
||||
if (isNumeric(v1, &d1)) {
|
||||
if (validateNumeric(vm, v2, &d2, "Right operand")) {
|
||||
return VAR_NUM(d1 / d2);
|
||||
}
|
||||
return VAR_NULL;
|
||||
}
|
||||
|
||||
msSetRuntimeError(vm, "Unsupported operand types for operator '/' "
|
||||
"%s and %s", varTypeName(v1), varTypeName(v2));
|
||||
return VAR_NULL;
|
||||
}
|
||||
|
||||
bool varGreater(MSVM* vm, Var v1, Var v2) {
|
||||
double d1, d2;
|
||||
if (isNumeric(v1, &d1) && isNumeric(v2, &d2)) {
|
||||
return d1 > d2;
|
||||
}
|
||||
|
||||
TODO;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool varLesser(MSVM* vm, Var v1, Var v2) {
|
||||
double d1, d2;
|
||||
if (isNumeric(v1, &d1) && isNumeric(v2, &d2)) {
|
||||
return d1 < d2;
|
||||
}
|
||||
|
||||
TODO;
|
||||
return false;
|
||||
}
|
||||
|
||||
Var varGetAttrib(MSVM* vm, Var on, String* attrib) {
|
||||
if (!IS_OBJ(on)) {
|
||||
msSetRuntimeError(vm, "%s type is not subscriptable.", varTypeName(on));
|
||||
return VAR_NULL;
|
||||
}
|
||||
|
||||
Object* obj = (Object*)AS_OBJ(on);
|
||||
switch (obj->type) {
|
||||
case OBJ_STRING:
|
||||
case OBJ_LIST:
|
||||
case OBJ_MAP:
|
||||
case OBJ_RANGE:
|
||||
TODO;
|
||||
|
||||
case OBJ_SCRIPT: {
|
||||
Script* scr = (Script*)obj;
|
||||
|
||||
// Search in functions.
|
||||
int index = nameTableFind(&scr->function_names, attrib->data,
|
||||
attrib->length);
|
||||
if (index != -1) {
|
||||
// TODO: Assert index (not a runtime error).
|
||||
return VAR_OBJ(scr->functions.data[index]);
|
||||
}
|
||||
|
||||
TODO; // Search in global variables.
|
||||
}
|
||||
|
||||
case OBJ_FUNC:
|
||||
case OBJ_USER:
|
||||
TODO;
|
||||
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
UNREACHABLE();
|
||||
return VAR_NULL;
|
||||
}
|
||||
|
||||
void varSetAttrib(MSVM* vm, Var on, String* name, Var value) {
|
||||
TODO;
|
||||
}
|
||||
|
||||
Var varGetSubscript(MSVM* vm, Var on, Var key) {
|
||||
if (!IS_OBJ(on)) {
|
||||
msSetRuntimeError(vm, "%s type is not subscriptable.", varTypeName(on));
|
||||
return VAR_NULL;
|
||||
}
|
||||
|
||||
Object* obj = AS_OBJ(on);
|
||||
switch (obj->type) {
|
||||
case OBJ_STRING: TODO;
|
||||
|
||||
case OBJ_LIST:
|
||||
{
|
||||
int32_t index;
|
||||
VarBuffer* elems = &((List*)obj)->elements;
|
||||
if (!validateIngeger(vm, key, &index, "List index")) {
|
||||
return VAR_NULL;
|
||||
}
|
||||
if (!validateIndex(vm, index, (int)elems->count, "List")) {
|
||||
return VAR_NULL;
|
||||
}
|
||||
return elems->data[index];
|
||||
}
|
||||
|
||||
case OBJ_MAP:
|
||||
case OBJ_RANGE:
|
||||
case OBJ_SCRIPT:
|
||||
case OBJ_FUNC:
|
||||
case OBJ_USER:
|
||||
TODO;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
UNREACHABLE();
|
||||
return VAR_NULL;
|
||||
}
|
||||
|
||||
void varsetSubscript(MSVM* vm, Var on, Var key, Var value) {
|
||||
if (!IS_OBJ(on)) {
|
||||
msSetRuntimeError(vm, "%s type is not subscriptable.", varTypeName(on));
|
||||
return;
|
||||
}
|
||||
|
||||
Object* obj = AS_OBJ(on);
|
||||
switch (obj->type) {
|
||||
case OBJ_STRING: TODO;
|
||||
|
||||
case OBJ_LIST:
|
||||
{
|
||||
int32_t index;
|
||||
VarBuffer* elems = &((List*)obj)->elements;
|
||||
if (!validateIngeger(vm, key, &index, "List index")) return;
|
||||
if (!validateIndex(vm, index, (int)elems->count, "List")) return;
|
||||
elems->data[index] = value;
|
||||
return;
|
||||
}
|
||||
|
||||
case OBJ_MAP:
|
||||
case OBJ_RANGE:
|
||||
case OBJ_SCRIPT:
|
||||
case OBJ_FUNC:
|
||||
case OBJ_USER:
|
||||
TODO;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
bool varIterate(MSVM* vm, Var seq, Var* iterator, Var* value) {
|
||||
|
||||
#ifdef DEBUG
|
||||
@ -314,6 +540,6 @@ bool varIterate(MSVM* vm, Var seq, Var* iterator, Var* value) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
TODO;
|
||||
UNREACHABLE();
|
||||
return false;
|
||||
}
|
@ -24,6 +24,15 @@ Var varSubtract(MSVM* vm, Var v1, Var v2);
|
||||
Var varMultiply(MSVM* vm, Var v1, Var v2);
|
||||
Var varDivide(MSVM* vm, Var v1, Var v2);
|
||||
|
||||
bool varGreater(MSVM* vm, Var v1, Var v2);
|
||||
bool varLesser(MSVM* vm, Var v1, Var v2);
|
||||
|
||||
Var varGetAttrib(MSVM* vm, Var on, String* attrib);
|
||||
void varSetAttrib(MSVM* vm, Var on, String* name, Var value);
|
||||
|
||||
Var varGetSubscript(MSVM* vm, Var on, Var key);
|
||||
void varsetSubscript(MSVM* vm, Var on, Var key, Var value);
|
||||
|
||||
// Functions //////////////////////////////////////////////////////////////////
|
||||
|
||||
// Parameter [iterator] should be VAR_NULL before starting the iteration.
|
||||
|
@ -134,6 +134,11 @@ OPCODE(RETURN, 0, -1)
|
||||
// param: 2 byte attrib name index.
|
||||
OPCODE(GET_ATTRIB, 2, 0)
|
||||
|
||||
// Get attribute to perform assignment operation before store it, so don't
|
||||
// pop the var.
|
||||
// param: 2 byte attrib name index.
|
||||
OPCODE(GET_ATTRIB_AOP, 2, 1)
|
||||
|
||||
// Pop var and value update the attribute push result.
|
||||
// param: 2 byte attrib name index.
|
||||
OPCODE(SET_ATTRIB, 2, -1)
|
||||
@ -141,6 +146,10 @@ OPCODE(SET_ATTRIB, 2, -1)
|
||||
// Pop var, key, get value and push the result.
|
||||
OPCODE(GET_SUBSCRIPT, 0, -1)
|
||||
|
||||
// Get subscript to perform assignment operation before store it, so it won't
|
||||
// pop the var and the key.
|
||||
OPCODE(GET_SUBSCRIPT_AOP, 0, 1)
|
||||
|
||||
// Pop var, key, value set and push value back.
|
||||
OPCODE(SET_SUBSCRIPT, 0, -2)
|
||||
|
||||
|
@ -15,14 +15,17 @@ void nameTableClear(NameTable* self, MSVM* vm) {
|
||||
stringBufferClear(self, vm);
|
||||
}
|
||||
|
||||
int nameTableAdd(NameTable* self, MSVM* vm, const char* name, size_t length) {
|
||||
int nameTableAdd(NameTable* self, MSVM* vm, const char* name, size_t length,
|
||||
String** ptr) {
|
||||
String* string = newString(vm, name, (uint32_t)length);
|
||||
|
||||
vmPushTempRef(vm, &string->_super);
|
||||
stringBufferWrite(self, vm, string);
|
||||
vmPopTempRef(vm);
|
||||
|
||||
return (int)(self->count - 1);
|
||||
int index = (int)(self->count - 1);
|
||||
if (ptr != NULL) *ptr = self->data[index];
|
||||
return index;
|
||||
}
|
||||
|
||||
const char* nameTableGet(NameTable* self, int index) {
|
||||
|
@ -19,7 +19,9 @@ void nameTableInit(NameTable* self);
|
||||
void nameTableClear(NameTable* self, MSVM* vm);
|
||||
|
||||
// Add a name to the name table and return the index of the name in the table.
|
||||
int nameTableAdd(NameTable* self, MSVM* vm, const char* name, size_t length);
|
||||
// Parameter [ptr] will updated with the string entry if it's not NULL.
|
||||
int nameTableAdd(NameTable* self, MSVM* vm, const char* name, size_t length,
|
||||
String** ptr);
|
||||
|
||||
// Return name at index.
|
||||
const char* nameTableGet(NameTable* self, int index);
|
||||
|
81
src/var.c
81
src/var.c
@ -3,6 +3,8 @@
|
||||
* Licensed under: MIT License
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "var.h"
|
||||
#include "vm.h"
|
||||
|
||||
@ -81,41 +83,47 @@ Script* newScript(MSVM* vm) {
|
||||
Script* script = ALLOCATE(vm, Script);
|
||||
varInitObject(&script->_super, vm, OBJ_SCRIPT);
|
||||
|
||||
script->name = NULL;
|
||||
script->name_length = 0;
|
||||
script->path = NULL;
|
||||
|
||||
varBufferInit(&script->globals);
|
||||
nameTableInit(&script->global_names);
|
||||
|
||||
varBufferInit(&script->literals);
|
||||
|
||||
vmPushTempRef(vm, &script->_super);
|
||||
script->body = newFunction(vm, "@(ScriptLevel)", script, false);
|
||||
vmPopTempRef(vm);
|
||||
|
||||
functionBufferInit(&script->functions);
|
||||
nameTableInit(&script->function_names);
|
||||
|
||||
stringBufferInit(&script->names);
|
||||
|
||||
vmPushTempRef(vm, &script->_super);
|
||||
const char* fn_name = "@(ScriptLevel)";
|
||||
script->body = newFunction(vm, fn_name, (int)strlen(fn_name), script, false);
|
||||
vmPopTempRef(vm);
|
||||
|
||||
return script;
|
||||
}
|
||||
|
||||
Function* newFunction(MSVM* vm, const char* name, Script* owner,
|
||||
Function* newFunction(MSVM* vm, const char* name, int length, Script* owner,
|
||||
bool is_native) {
|
||||
|
||||
Function* func = ALLOCATE(vm, Function);
|
||||
varInitObject(&func->_super, vm, OBJ_FUNC);
|
||||
|
||||
func->name = name;
|
||||
// Add the name in the script's function buffer.
|
||||
String* name_ptr;
|
||||
vmPushTempRef(vm, &func->_super);
|
||||
functionBufferWrite(&owner->functions, vm, func);
|
||||
nameTableAdd(&owner->function_names, vm, name, length, &name_ptr);
|
||||
vmPopTempRef(vm);
|
||||
|
||||
func->name = name_ptr->data;
|
||||
func->owner = owner;
|
||||
func->arity = -2; // -1 means variadic args.
|
||||
|
||||
func->is_native = is_native;
|
||||
|
||||
if (is_native) {
|
||||
func->native = NULL;
|
||||
} else {
|
||||
vmPushTempRef(vm, &func->_super);
|
||||
Fn* fn = ALLOCATE(vm, Fn);
|
||||
vmPopTempRef(vm);
|
||||
|
||||
byteBufferInit(&fn->opcodes);
|
||||
intBufferInit(&fn->oplines);
|
||||
@ -127,6 +135,27 @@ Function* newFunction(MSVM* vm, const char* name, Script* owner,
|
||||
|
||||
// Utility functions //////////////////////////////////////////////////////////
|
||||
|
||||
const char* varTypeName(Var v) {
|
||||
if (IS_NULL(v)) return "null";
|
||||
if (IS_BOOL(v)) return "bool";
|
||||
if (IS_NUM(v)) return "number";
|
||||
|
||||
ASSERT(IS_OBJ(v), OOPS);
|
||||
Object* obj = AS_OBJ(v);
|
||||
switch (obj->type) {
|
||||
case OBJ_STRING: return "String";
|
||||
case OBJ_LIST: return "List";
|
||||
case OBJ_MAP: return "Map";
|
||||
case OBJ_RANGE: return "Range";
|
||||
case OBJ_SCRIPT: return "Script";
|
||||
case OBJ_FUNC: return "Func";
|
||||
case OBJ_USER: return "UserObj";
|
||||
TODO;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
bool isVauesSame(Var v1, Var v2) {
|
||||
#if VAR_NAN_TAGGING
|
||||
// Bit representation of each values are unique so just compare the bits.
|
||||
@ -174,7 +203,7 @@ String* toString(MSVM* vm, Var v, bool recursive) {
|
||||
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, "[Func:", 6);
|
||||
memcpy(buff + 6, name, length);
|
||||
buff[6 + length] = ']';
|
||||
return newString(vm, buff, 6 + length + 1);
|
||||
@ -187,3 +216,29 @@ String* toString(MSVM* vm, Var v, bool recursive) {
|
||||
UNREACHABLE();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool toBool(Var v) {
|
||||
if (IS_BOOL(v)) return AS_BOOL(v);
|
||||
if (IS_NULL(v)) return false;
|
||||
if (IS_NUM(v)) {
|
||||
return AS_NUM(v) != 0;
|
||||
}
|
||||
|
||||
ASSERT(IS_OBJ(v), OOPS);
|
||||
Object* o = AS_OBJ(v);
|
||||
switch (o->type) {
|
||||
case OBJ_STRING: return ((String*)o)->length != 0;
|
||||
|
||||
case OBJ_LIST: return ((List*)o)->elements.count != 0;
|
||||
case OBJ_MAP: TODO;
|
||||
case OBJ_RANGE: TODO; // Nout sure.
|
||||
case OBJ_SCRIPT: // [[FALLTHROUGH]]
|
||||
case OBJ_FUNC:
|
||||
case OBJ_USER:
|
||||
return true;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
12
src/var.h
12
src/var.h
@ -233,7 +233,9 @@ struct Range {
|
||||
struct Script {
|
||||
Object _super;
|
||||
|
||||
String* path; //< Absolute path of the script.
|
||||
const char* name; //< Std script's name. Null for user script.
|
||||
int name_length; //< Length of the name.
|
||||
String* path; //< Absolute path of the script. Null for std scripts.
|
||||
|
||||
ID imports[MAX_IMPORT_SCRIPTS]; //< Imported script IDs.
|
||||
int import_count; //< Number of import in imports.
|
||||
@ -291,11 +293,14 @@ Script* newScript(MSVM* vm);
|
||||
|
||||
// Allocate new Function object and return Function*. Parameter [name] should
|
||||
// be the name in the Script's nametable.
|
||||
Function* newFunction(MSVM* vm, const char* name, Script* owner,
|
||||
Function* newFunction(MSVM* vm, const char* name, int length, Script* owner,
|
||||
bool is_native);
|
||||
|
||||
// Utility functions //////////////////////////////////////////////////////////
|
||||
|
||||
// Returns the type name of the var [v].
|
||||
const char* varTypeName(Var v);
|
||||
|
||||
// Returns true if both variables are the same.
|
||||
bool isVauesSame(Var v1, Var v2);
|
||||
|
||||
@ -303,4 +308,7 @@ bool isVauesSame(Var v1, Var v2);
|
||||
// It's an internal use (or may be I could make a wrapper around).
|
||||
String* toString(MSVM* vm, Var v, bool _recursive);
|
||||
|
||||
// Returns the truthy value of the var.
|
||||
bool toBool(Var v);
|
||||
|
||||
#endif // VAR_H
|
||||
|
109
src/vm.c
109
src/vm.c
@ -14,7 +14,7 @@
|
||||
#define INITIAL_CALL_FRAMES 4
|
||||
|
||||
// Minimum size of the stack.
|
||||
#define MIN_STACK_SIZE 16
|
||||
#define MIN_STACK_SIZE 128
|
||||
|
||||
void* vmRealloc(MSVM* self, void* memory, size_t old_size, size_t new_size) {
|
||||
|
||||
@ -53,10 +53,28 @@ void vmPopTempRef(MSVM* self) {
|
||||
self->temp_reference_count--;
|
||||
}
|
||||
|
||||
void vmAddStdScript(MSVM* self, Script* script) {
|
||||
ASSERT(self->std_count < MAX_SCRIPT_CACHE, OOPS);
|
||||
self->std_scripts[self->std_count++] = script;
|
||||
}
|
||||
|
||||
Script* vmGetStdScript(MSVM* self, const char* name) {
|
||||
for (int i = 0; i < self->std_count; i++) {
|
||||
Script* scr = self->std_scripts[i];
|
||||
// +4 to skip "std:".
|
||||
if (strcmp(name, scr->name + 4) == 0) {
|
||||
return scr;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* RUNTIME *
|
||||
*****************************************************************************/
|
||||
|
||||
#ifdef DEBUG
|
||||
#include <stdio.h>
|
||||
// TODO: A function for quick debug. REMOVE.
|
||||
void _printStackTop(MSVM* vm) {
|
||||
if (vm->sp != vm->stack) {
|
||||
@ -64,6 +82,7 @@ void _printStackTop(MSVM* vm) {
|
||||
printf("%s\n", toString(vm, v, false)->data);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void ensureStackSize(MSVM* vm, int size) {
|
||||
if (vm->stack_size > size) return;
|
||||
@ -377,8 +396,24 @@ MSInterpretResult vmRunScript(MSVM* vm, Script* _script) {
|
||||
|
||||
|
||||
OPCODE(JUMP_IF):
|
||||
{
|
||||
Var cond = POP();
|
||||
int offset = READ_SHORT();
|
||||
if (toBool(cond)) {
|
||||
ip += offset;
|
||||
}
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
OPCODE(JUMP_IF_NOT):
|
||||
TODO;
|
||||
{
|
||||
Var cond = POP();
|
||||
int offset = READ_SHORT();
|
||||
if (!toBool(cond)) {
|
||||
ip += offset;
|
||||
}
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
OPCODE(RETURN):
|
||||
{
|
||||
@ -403,11 +438,55 @@ MSInterpretResult vmRunScript(MSVM* vm, Script* _script) {
|
||||
}
|
||||
|
||||
OPCODE(GET_ATTRIB):
|
||||
{
|
||||
Var on = POP();
|
||||
String* name = script->names.data[READ_SHORT()];
|
||||
PUSH(varGetAttrib(vm, on, name));
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
OPCODE(GET_ATTRIB_AOP):
|
||||
{
|
||||
Var on = *(vm->sp - 1);
|
||||
String* name = script->names.data[READ_SHORT()];
|
||||
PUSH(varGetAttrib(vm, on, name));
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
OPCODE(SET_ATTRIB):
|
||||
OPCODE(GET_SUBSCRIPT):
|
||||
OPCODE(SET_SUBSCRIPT):
|
||||
TODO;
|
||||
|
||||
OPCODE(GET_SUBSCRIPT):
|
||||
{
|
||||
Var key = POP();
|
||||
Var on = POP();
|
||||
PUSH(varGetSubscript(vm, on, key));
|
||||
CHECK_ERROR();
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
OPCODE(GET_SUBSCRIPT_AOP):
|
||||
{
|
||||
Var key = *(vm->sp - 1);
|
||||
Var on = *(vm->sp - 2);
|
||||
PUSH(varGetSubscript(vm, on, key));
|
||||
CHECK_ERROR();
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
OPCODE(SET_SUBSCRIPT):
|
||||
{
|
||||
Var value = POP();
|
||||
Var key = POP();
|
||||
Var on = POP();
|
||||
|
||||
varsetSubscript(vm, on, key, value);
|
||||
CHECK_ERROR();
|
||||
PUSH(value);
|
||||
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
OPCODE(NEGATIVE):
|
||||
{
|
||||
Var num = POP();
|
||||
@ -419,6 +498,12 @@ MSInterpretResult vmRunScript(MSVM* vm, Script* _script) {
|
||||
}
|
||||
|
||||
OPCODE(NOT):
|
||||
{
|
||||
Var val = POP();
|
||||
PUSH(VAR_BOOL(!toBool(val)));
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
OPCODE(BIT_NOT):
|
||||
TODO;
|
||||
|
||||
@ -452,9 +537,25 @@ MSInterpretResult vmRunScript(MSVM* vm, Script* _script) {
|
||||
OPCODE(OR):
|
||||
OPCODE(EQEQ):
|
||||
OPCODE(NOTEQ):
|
||||
TODO;
|
||||
|
||||
OPCODE(LT):
|
||||
{
|
||||
PUSH(VAR_BOOL(varLesser(vm, POP(), POP())));
|
||||
CHECK_ERROR();
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
OPCODE(LTEQ):
|
||||
TODO;
|
||||
|
||||
OPCODE(GT):
|
||||
{
|
||||
PUSH(VAR_BOOL(varGreater(vm, POP(), POP())));
|
||||
CHECK_ERROR();
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
OPCODE(GTEQ):
|
||||
TODO;
|
||||
|
||||
|
14
src/vm.h
14
src/vm.h
@ -49,6 +49,12 @@ struct MSVM {
|
||||
// Current compiler reference to mark it's heap allocated objects.
|
||||
Compiler* compiler;
|
||||
|
||||
// Std scripts array.
|
||||
Script* std_scripts[MAX_SCRIPT_CACHE];
|
||||
|
||||
// Std scripts count.
|
||||
int std_count;
|
||||
|
||||
// Execution variables ////////////////////////////////////////////////////
|
||||
|
||||
// Compiled script cache.
|
||||
@ -105,6 +111,14 @@ void vmPushTempRef(MSVM* self, Object* obj);
|
||||
// Pop the top most object from temporary reference stack.
|
||||
void vmPopTempRef(MSVM* self);
|
||||
|
||||
// Add a std script to vm when initializing core.
|
||||
void vmAddStdScript(MSVM* self, Script* script);
|
||||
|
||||
// Returns the std script with the name [name]. Note that the name shouldn't
|
||||
// be start with "std:" but the actual name of the script. If not found
|
||||
// returns NULL.
|
||||
Script* vmGetStdScript(MSVM* self, const char* name);
|
||||
|
||||
// Runs the script and return result.
|
||||
MSInterpretResult vmRunScript(MSVM* vm, Script* script);
|
||||
|
||||
|
39
test/main.c
39
test/main.c
@ -5,20 +5,10 @@
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
//#define CLOGGER_IMPLEMENT
|
||||
//#include "clogger.h"
|
||||
|
||||
#include "miniscript.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
static const char* opnames[] = {
|
||||
#define OPCODE(name, params, stack) #name,
|
||||
#include "../src/opcodes.h"
|
||||
#undef OPCODE
|
||||
NULL,
|
||||
};
|
||||
|
||||
void errorPrint(MSVM* vm, MSErrorType type, const char* file, int line,
|
||||
const char* message) {
|
||||
fprintf(stderr, "Error: %s\n\tat %s:%i\n", message, file, line);
|
||||
@ -63,6 +53,19 @@ MSLoadScriptResult loadScript(MSVM* vm, const char* path) {
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
const char* notice =
|
||||
"MiniScript " MS_VERSION_STRING " (https://github.com/ThakeeNathees/miniscript/)\n"
|
||||
"Copyright(c) 2020 - 2021 ThakeeNathees.\n"
|
||||
"Free and open source software under the terms of the MIT license.\n";
|
||||
const char* help = "Usage: miniscript <source_path>\n";
|
||||
|
||||
if (argc < 2) {
|
||||
printf("%s\n%s", notice, help);
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char* source_path = argv[1];
|
||||
|
||||
MSConfiguration config;
|
||||
config.error_fn = errorPrint;
|
||||
config.write_fn = writeFunction;
|
||||
@ -70,19 +73,7 @@ int main(int argc, char** argv) {
|
||||
config.load_script_done_fn = loadScriptDone;
|
||||
|
||||
MSVM* vm = msNewVM(&config);
|
||||
MSInterpretResult result = msInterpret(vm, "build/test.ms");
|
||||
MSInterpretResult result = msInterpret(vm, source_path);
|
||||
|
||||
//Script* script = compileSource(vm, "../some/path/file.ms");
|
||||
//vmRunScript(vm, script);
|
||||
//
|
||||
//ByteBuffer* bytes = &script->body->fn->opcodes;
|
||||
//for (int i = 0; i < bytes->count; i++) {
|
||||
// const char* op = "(???)";
|
||||
// if (bytes->data[i] <= (int)OP_END) {
|
||||
// op = opnames[bytes->data[i]];
|
||||
// }
|
||||
// printf("%s %i\n", op, bytes->data[i]);
|
||||
//}
|
||||
|
||||
return 0;
|
||||
return result;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user