Merge pull request #2 from ThakeeNathees/increment-operators

Increment operators
This commit is contained in:
Thakee Nathees 2021-02-16 11:49:18 +05:30 committed by GitHub
commit 1243bbad5b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 677 additions and 152 deletions

View File

@ -78,7 +78,7 @@ typedef struct {
} MSConfiguration;
typedef enum {
RESULT_SUCCESS,
RESULT_SUCCESS = 0,
RESULT_COMPILE_ERROR,
RESULT_RUNTIME_ERROR,
} MSInterpretResult;

View File

@ -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 {

View File

@ -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;
}

View File

@ -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.

View File

@ -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)

View File

@ -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) {

View File

@ -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);

View File

@ -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;
}

View File

@ -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
View File

@ -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;

View File

@ -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);

View File

@ -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;
}