mirror of
https://github.com/zekexiao/pocketlang.git
synced 2025-02-11 15:16:41 +08:00
opcodes implemented
This commit is contained in:
parent
6317274d89
commit
09d397bbfc
12
src/common.h
12
src/common.h
@ -37,6 +37,16 @@
|
|||||||
#define MS_PUBLIC
|
#define MS_PUBLIC
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define STRINGIFY(x) TOSTRING(x)
|
||||||
|
#define TOSTRING(x) #x
|
||||||
|
|
||||||
|
// The factor by which a buffer will grow when it's capacity reached.
|
||||||
|
#define GROW_FACTOR 2
|
||||||
|
|
||||||
|
// The initial capacity of a buffer.
|
||||||
|
#define MIN_CAPACITY 8
|
||||||
|
|
||||||
|
|
||||||
// Unique number to identify for various cases.
|
// Unique number to identify for various cases.
|
||||||
typedef uint32_t ID;
|
typedef uint32_t ID;
|
||||||
|
|
||||||
@ -77,7 +87,7 @@ typedef struct Function Function;
|
|||||||
#define UNREACHABLE() \
|
#define UNREACHABLE() \
|
||||||
do { \
|
do { \
|
||||||
fprintf(stderr, "Execution reached an unreachable path\n" \
|
fprintf(stderr, "Execution reached an unreachable path\n" \
|
||||||
"\tat %s() (%s:%i)\n", __FILE__, __LINE__, __func__); \
|
"\tat %s() (%s:%i)\n", __func__, __FILE__, __LINE__); \
|
||||||
abort(); \
|
abort(); \
|
||||||
} while (false)
|
} while (false)
|
||||||
|
|
||||||
|
419
src/compiler.c
419
src/compiler.c
@ -15,6 +15,16 @@
|
|||||||
// which is using a single byte value to identify the local.
|
// which is using a single byte value to identify the local.
|
||||||
#define MAX_VARIABLES 256
|
#define MAX_VARIABLES 256
|
||||||
|
|
||||||
|
// The maximum number of constant literal a script can contain. Also it's
|
||||||
|
// limited by it's opcode which is using a short value to identify.
|
||||||
|
#define MAX_CONSTANTS (1 << 16)
|
||||||
|
|
||||||
|
// The maximum address possible to jump. Similar limitation as above.
|
||||||
|
#define MAX_JUMP (1 << 16)
|
||||||
|
|
||||||
|
// Max number of break statement in a loop statement to patch.
|
||||||
|
#define MAX_BREAK_PATCH 256
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
|
||||||
TK_ERROR = 0,
|
TK_ERROR = 0,
|
||||||
@ -246,16 +256,16 @@ typedef struct {
|
|||||||
typedef struct sLoop {
|
typedef struct sLoop {
|
||||||
|
|
||||||
// Index of the loop's start instruction where the execution will jump
|
// Index of the loop's start instruction where the execution will jump
|
||||||
// back to once it reach the loop end.
|
// back to once it reach the loop end or continue used.
|
||||||
int start;
|
int start;
|
||||||
|
|
||||||
// Index of the jump out address instruction to patch it's value once done
|
// Index of the jump out address instruction to patch it's value once done
|
||||||
// compiling the loop.
|
// compiling the loop.
|
||||||
int exit_jump;
|
int exit_jump;
|
||||||
|
|
||||||
// Index of the first body instruction. Needed to start patching jump
|
// Array of address indexes to patch break address.
|
||||||
// address from which till the loop end.
|
int patches[MAX_BREAK_PATCH];
|
||||||
int body;
|
int patch_count;
|
||||||
|
|
||||||
// The outer loop of the current loop used to set and reset the compiler's
|
// The outer loop of the current loop used to set and reset the compiler's
|
||||||
// current loop context.
|
// current loop context.
|
||||||
@ -275,11 +285,24 @@ struct Compiler {
|
|||||||
Variable variables[MAX_VARIABLES]; //< Variables in the current context.
|
Variable variables[MAX_VARIABLES]; //< Variables in the current context.
|
||||||
int var_count; //< Number of locals in [variables].
|
int var_count; //< Number of locals in [variables].
|
||||||
|
|
||||||
|
int stack_size; //< Current size including locals ind temps.
|
||||||
|
|
||||||
// TODO: compiler should mark Script* below not to be garbage collected.
|
// TODO: compiler should mark Script* below not to be garbage collected.
|
||||||
|
|
||||||
Script* script; //< Current script.
|
Script* script; //< Current script.
|
||||||
Loop* loop; //< Current loop.
|
Loop* loop; //< Current loop.
|
||||||
Function* fn; //< Current function.
|
Function* function; //< Current function.
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int params;
|
||||||
|
int stack;
|
||||||
|
} OpInfo;
|
||||||
|
|
||||||
|
static OpInfo opcode_info[] = {
|
||||||
|
#define OPCODE(name, params, stack) { params, stack },
|
||||||
|
#include "opcodes.h"
|
||||||
|
#undef OPCODE
|
||||||
};
|
};
|
||||||
|
|
||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
@ -289,6 +312,7 @@ struct Compiler {
|
|||||||
static void reportError(Parser* parser, const char* file, int line,
|
static void reportError(Parser* parser, const char* file, int line,
|
||||||
const char* fmt, va_list args) {
|
const char* fmt, va_list args) {
|
||||||
parser->has_errors = true;
|
parser->has_errors = true;
|
||||||
|
ASSERT(false, "TODO:");
|
||||||
// TODO: parser->vm->config.error_fn(...)
|
// TODO: parser->vm->config.error_fn(...)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -337,7 +361,7 @@ static void eatString(Parser* parser) {
|
|||||||
if (c == '"') break;
|
if (c == '"') break;
|
||||||
|
|
||||||
if (c == '\0') {
|
if (c == '\0') {
|
||||||
// TODO: syntaxError()
|
lexError(parser, "Non terminated string.");
|
||||||
|
|
||||||
// Null byte is required by TK_EOF.
|
// Null byte is required by TK_EOF.
|
||||||
parser->current_char--;
|
parser->current_char--;
|
||||||
@ -353,7 +377,7 @@ static void eatString(Parser* parser) {
|
|||||||
case 't': byteBufferWrite(&buff, parser->vm, '\t'); break;
|
case 't': byteBufferWrite(&buff, parser->vm, '\t'); break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// TODO: syntaxError("Error: invalid escape character")
|
lexError(parser, "Error: invalid escape character");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -429,7 +453,9 @@ static void eatNumber(Parser* parser) {
|
|||||||
errno = 0;
|
errno = 0;
|
||||||
Var value = VAR_NUM(strtod(parser->token_start, NULL));
|
Var value = VAR_NUM(strtod(parser->token_start, NULL));
|
||||||
if (errno == ERANGE) {
|
if (errno == ERANGE) {
|
||||||
// TODO: error() literal number is too large.
|
const char* start = parser->token_start;
|
||||||
|
int len = (parser->current_char - start);
|
||||||
|
lexError(parser, "Literal is too large (%.*s)", len, start);
|
||||||
value = VAR_NUM(0);
|
value = VAR_NUM(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -577,9 +603,9 @@ static void lexToken(Parser* parser) {
|
|||||||
eatName(parser);
|
eatName(parser);
|
||||||
} else {
|
} else {
|
||||||
if (c >= 32 && c <= 126) {
|
if (c >= 32 && c <= 126) {
|
||||||
// TODO: syntaxError("Invalid character %c", c);
|
lexError(parser, "Invalid character %c", c);
|
||||||
} else {
|
} else {
|
||||||
// TODO: syntaxError("Invalid byte 0x%x", (uint8_t)c);
|
lexError(parser, "Invalid byte 0x%x", (uint8_t)c);
|
||||||
}
|
}
|
||||||
setNextToken(parser, TK_ERROR);
|
setNextToken(parser, TK_ERROR);
|
||||||
}
|
}
|
||||||
@ -645,23 +671,33 @@ static bool matchLine(Parser* parser) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Match semi collon or multiple new lines.
|
// Match semi collon or multiple new lines.
|
||||||
static void matchEndStatement(Parser* parser) {
|
static void consumeEndStatement(Parser* parser) {
|
||||||
|
bool consumed = false;
|
||||||
|
|
||||||
// Semi collon must be on the same line.
|
// Semi collon must be on the same line.
|
||||||
if (peek(parser) == TK_SEMICOLLON)
|
if (peek(parser) == TK_SEMICOLLON) {
|
||||||
match(parser, TK_SEMICOLLON);
|
match(parser, TK_SEMICOLLON);
|
||||||
|
consumed = true;
|
||||||
skipNewLines(parser);
|
}
|
||||||
|
if (matchLine(parser)) consumed = true;
|
||||||
|
if (!consumed && peek(parser) != TK_EOF) {
|
||||||
|
parseError(parser, "Expected statement end with newline or ';'.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Match optional "do" keyword and new lines.
|
// Match optional "do" keyword and new lines.
|
||||||
static void matchStartBlock(Parser* parser) {
|
static void consumeStartBlock(Parser* parser) {
|
||||||
|
bool consumed = false;
|
||||||
|
|
||||||
// "do" must be on the same line.
|
// "do" must be on the same line.
|
||||||
if (peek(parser) == TK_DO)
|
if (peek(parser) == TK_DO) {
|
||||||
match(parser, TK_DO);
|
match(parser, TK_DO);
|
||||||
|
consumed = true;
|
||||||
skipNewLines(parser);
|
}
|
||||||
|
if (matchLine(parser)) consumed = true;
|
||||||
|
if (!consumed) {
|
||||||
|
parseError(parser, "Expected enter block with newline or 'do'.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Consume the the current token and if it's not [expected] emits error log
|
// Consume the the current token and if it's not [expected] emits error log
|
||||||
@ -673,7 +709,7 @@ static void consume(Parser* self, TokenType expected, const char* err_msg) {
|
|||||||
|
|
||||||
lexToken(self);
|
lexToken(self);
|
||||||
if (self->previous.type != expected) {
|
if (self->previous.type != expected) {
|
||||||
// TODO: syntaxError(err_msg);
|
parseError(self, "%s", err_msg);
|
||||||
|
|
||||||
// If the next token is expected discard the current to minimize
|
// If the next token is expected discard the current to minimize
|
||||||
// cascaded errors and continue parsing.
|
// cascaded errors and continue parsing.
|
||||||
@ -687,15 +723,22 @@ static void consume(Parser* self, TokenType expected, const char* err_msg) {
|
|||||||
* PARSING GRAMMAR *
|
* PARSING GRAMMAR *
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
// Forward declaration of grammar functions.
|
// Forward declaration of codegen functions.
|
||||||
|
static void emitOpcode(Compiler* compiler, Opcode opcode);
|
||||||
|
static int emitByte(Compiler* compiler, int byte);
|
||||||
|
static int emitShort(Compiler* compiler, int arg);
|
||||||
|
static int compilerAddConstant(Compiler* compiler, Var value);
|
||||||
|
|
||||||
|
// Forward declaration of grammar functions.
|
||||||
|
static void parsePrecedence(Compiler* compiler, Precedence precedence);
|
||||||
|
|
||||||
|
static void compileExpression(Compiler* compiler);
|
||||||
static void exprAssignment(Compiler* compiler, bool can_assign);
|
static void exprAssignment(Compiler* compiler, bool can_assign);
|
||||||
|
|
||||||
// Bool, Num, String, Null, -and- bool_t, Array_t, String_t, ...
|
// Bool, Num, String, Null, -and- bool_t, Array_t, String_t, ...
|
||||||
static void exprLiteral(Compiler* compiler, bool can_assign);
|
static void exprLiteral(Compiler* compiler, bool can_assign);
|
||||||
static void exprName(Compiler* compiler, bool can_assign);
|
static void exprName(Compiler* compiler, bool can_assign);
|
||||||
|
|
||||||
|
|
||||||
static void exprBinaryOp(Compiler* compiler, bool can_assign);
|
static void exprBinaryOp(Compiler* compiler, bool can_assign);
|
||||||
static void exprUnaryOp(Compiler* compiler, bool can_assign);
|
static void exprUnaryOp(Compiler* compiler, bool can_assign);
|
||||||
|
|
||||||
@ -732,7 +775,7 @@ GrammarRule rules[] = { // Prefix Infix Infix Precedence
|
|||||||
/* TK_PIPE */ { NULL, exprBinaryOp, PREC_BITWISE_OR },
|
/* TK_PIPE */ { NULL, exprBinaryOp, PREC_BITWISE_OR },
|
||||||
/* TK_CARET */ { NULL, exprBinaryOp, PREC_BITWISE_XOR },
|
/* TK_CARET */ { NULL, exprBinaryOp, PREC_BITWISE_XOR },
|
||||||
/* TK_PLUS */ { NULL, exprBinaryOp, PREC_TERM },
|
/* TK_PLUS */ { NULL, exprBinaryOp, PREC_TERM },
|
||||||
/* TK_MINUS */ { NULL, exprBinaryOp, PREC_TERM },
|
/* TK_MINUS */ { exprUnaryOp, exprBinaryOp, PREC_TERM },
|
||||||
/* TK_STAR */ { NULL, exprBinaryOp, PREC_FACTOR },
|
/* TK_STAR */ { NULL, exprBinaryOp, PREC_FACTOR },
|
||||||
/* TK_FSLASH */ { NULL, exprBinaryOp, PREC_FACTOR },
|
/* TK_FSLASH */ { NULL, exprBinaryOp, PREC_FACTOR },
|
||||||
/* TK_BSLASH */ NO_RULE,
|
/* TK_BSLASH */ NO_RULE,
|
||||||
@ -760,7 +803,7 @@ GrammarRule rules[] = { // Prefix Infix Infix Precedence
|
|||||||
/* TK_IN */ { NULL, exprBinaryOp, PREC_IN },
|
/* TK_IN */ { NULL, exprBinaryOp, PREC_IN },
|
||||||
/* TK_AND */ { NULL, exprBinaryOp, PREC_LOGICAL_AND },
|
/* TK_AND */ { NULL, exprBinaryOp, PREC_LOGICAL_AND },
|
||||||
/* TK_OR */ { NULL, exprBinaryOp, PREC_LOGICAL_OR },
|
/* TK_OR */ { NULL, exprBinaryOp, PREC_LOGICAL_OR },
|
||||||
/* TK_NOT */ { NULL, exprUnaryOp, PREC_LOGICAL_NOT },
|
/* TK_NOT */ { exprUnaryOp, NULL, PREC_LOGICAL_NOT },
|
||||||
/* TK_TRUE */ { exprLiteral, NULL, NO_INFIX },
|
/* TK_TRUE */ { exprLiteral, NULL, NO_INFIX },
|
||||||
/* TK_FALSE */ { exprLiteral, NULL, NO_INFIX },
|
/* TK_FALSE */ { exprLiteral, NULL, NO_INFIX },
|
||||||
/* TK_BOOL_T */ { exprLiteral, NULL, NO_INFIX },
|
/* TK_BOOL_T */ { exprLiteral, NULL, NO_INFIX },
|
||||||
@ -789,23 +832,93 @@ static GrammarRule* getRule(TokenType type) {
|
|||||||
return &(rules[(int)type]);
|
return &(rules[(int)type]);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void exprAssignment(Compiler* compiler, bool can_assign) { /*TODO*/ }
|
static void exprAssignment(Compiler* compiler, bool can_assign) { ASSERT(false, "TODO:"); }
|
||||||
|
|
||||||
static void exprLiteral(Compiler* compiler, bool can_assign) { /*TODO*/ }
|
static void exprLiteral(Compiler* compiler, bool can_assign) {
|
||||||
static void exprName(Compiler* compiler, bool can_assign) { /*TODO*/ }
|
Token* value = &compiler->parser.previous;
|
||||||
|
int index = compilerAddConstant(compiler, value->value);
|
||||||
|
emitOpcode(compiler, OP_CONSTANT);
|
||||||
|
emitShort(compiler, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void exprName(Compiler* compiler, bool can_assign) { ASSERT(false, "TODO:"); }
|
||||||
|
|
||||||
static void exprBinaryOp(Compiler* compiler, bool can_assign) { /*TODO*/ }
|
static void exprBinaryOp(Compiler* compiler, bool can_assign) {
|
||||||
static void exprUnaryOp(Compiler* compiler, bool can_assign) { /*TODO*/ }
|
TokenType op = compiler->parser.previous.type;
|
||||||
|
skipNewLines(&compiler->parser);
|
||||||
|
parsePrecedence(compiler, (Precedence)(getRule(op)->precedence + 1));
|
||||||
|
|
||||||
static void exprGrouping(Compiler* compiler, bool can_assign) { /*TODO*/ }
|
switch (op) {
|
||||||
static void exprArray(Compiler* compiler, bool can_assign) { /*TODO*/ }
|
case TK_DOTDOT: emitOpcode(compiler, OP_RANGE); break;
|
||||||
static void exprMap(Compiler* compiler, bool can_assign) { /*TODO*/ }
|
case TK_PERCENT: emitOpcode(compiler, OP_MOD); break;
|
||||||
|
case TK_AMP: emitOpcode(compiler, OP_BIT_AND); break;
|
||||||
|
case TK_PIPE: emitOpcode(compiler, OP_BIT_OR); break;
|
||||||
|
case TK_CARET: emitOpcode(compiler, OP_BIT_XOR); break;
|
||||||
|
case TK_PLUS: emitOpcode(compiler, OP_ADD); break;
|
||||||
|
case TK_MINUS: emitOpcode(compiler, OP_SUBTRACT); break;
|
||||||
|
case TK_STAR: emitOpcode(compiler, OP_MULTIPLY); break;
|
||||||
|
case TK_FSLASH: emitOpcode(compiler, OP_DIVIDE); break;
|
||||||
|
case TK_GT: emitOpcode(compiler, OP_GT); break;
|
||||||
|
case TK_LT: emitOpcode(compiler, OP_LT); break;
|
||||||
|
case TK_EQEQ: emitOpcode(compiler, OP_EQEQ); break;
|
||||||
|
case TK_NOTEQ: emitOpcode(compiler, OP_NOTEQ); break;
|
||||||
|
case TK_GTEQ: emitOpcode(compiler, OP_GTEQ); break;
|
||||||
|
case TK_LTEQ: emitOpcode(compiler, OP_LTEQ); break;
|
||||||
|
case TK_SRIGHT: emitOpcode(compiler, OP_BIT_RSHIFT); break;
|
||||||
|
case TK_SLEFT: emitOpcode(compiler, OP_BIT_LSHIFT); break;
|
||||||
|
case TK_IS: emitOpcode(compiler, OP_IS); break;
|
||||||
|
case TK_IN: emitOpcode(compiler, OP_IN); break;
|
||||||
|
case TK_AND: emitOpcode(compiler, OP_AND); break;
|
||||||
|
case TK_OR: emitOpcode(compiler, OP_OR); break;
|
||||||
|
default:
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void exprCall(Compiler* compiler, bool can_assign) { /*TODO*/ }
|
static void exprUnaryOp(Compiler* compiler, bool can_assign) {
|
||||||
static void exprAttrib(Compiler* compiler, bool can_assign) { /*TODO*/ }
|
TokenType op = compiler->parser.previous.type;
|
||||||
static void exprSubscript(Compiler* compiler, bool can_assign) { /*TODO*/ }
|
skipNewLines(&compiler->parser);
|
||||||
|
parsePrecedence(compiler, (Precedence)(PREC_UNARY + 1));
|
||||||
|
|
||||||
|
switch (op) {
|
||||||
|
case TK_TILD: emitOpcode(compiler, OP_BIT_NOT); break;
|
||||||
|
case TK_MINUS: emitOpcode(compiler, OP_NEGATIVE); break;
|
||||||
|
case TK_NOT: emitOpcode(compiler, OP_NOT); break;
|
||||||
|
default:
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void exprGrouping(Compiler* compiler, bool can_assign) {
|
||||||
|
compileExpression(compiler);
|
||||||
|
consume(&compiler->parser, TK_RPARAN, "Expected ')' after expression ");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void exprArray(Compiler* compiler, bool can_assign) { ASSERT(false, "TODO:"); }
|
||||||
|
static void exprMap(Compiler* compiler, bool can_assign) { ASSERT(false, "TODO:"); }
|
||||||
|
|
||||||
|
static void exprCall(Compiler* compiler, bool can_assign) { ASSERT(false, "TODO:"); }
|
||||||
|
static void exprAttrib(Compiler* compiler, bool can_assign) { ASSERT(false, "TODO:"); }
|
||||||
|
static void exprSubscript(Compiler* compiler, bool can_assign) { ASSERT(false, "TODO:"); }
|
||||||
|
|
||||||
|
static void parsePrecedence(Compiler* compiler, Precedence precedence) {
|
||||||
|
lexToken(&compiler->parser);
|
||||||
|
GrammarFn prefix = getRule(compiler->parser.previous.type)->prefix;
|
||||||
|
|
||||||
|
if (prefix == NULL) {
|
||||||
|
parseError(&compiler->parser, "Expected an expression.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool can_assign = precedence <= PREC_ASSIGNMENT;
|
||||||
|
prefix(compiler, can_assign);
|
||||||
|
|
||||||
|
while (getRule(compiler->parser.current.type)->precedence >= precedence) {
|
||||||
|
lexToken(&compiler->parser);
|
||||||
|
GrammarFn infix = getRule(compiler->parser.previous.type)->infix;
|
||||||
|
infix(compiler, can_assign);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
* COMPILING *
|
* COMPILING *
|
||||||
@ -855,6 +968,7 @@ static void compilerInit(Compiler* compiler, MSVM* vm, const char* source,
|
|||||||
vm->compiler = compiler;
|
vm->compiler = compiler;
|
||||||
compiler->scope_depth = -1;
|
compiler->scope_depth = -1;
|
||||||
compiler->var_count = 0;
|
compiler->var_count = 0;
|
||||||
|
compiler->stack_size = 0;
|
||||||
Loop* loop = NULL;
|
Loop* loop = NULL;
|
||||||
Function* fn = NULL;
|
Function* fn = NULL;
|
||||||
}
|
}
|
||||||
@ -901,6 +1015,97 @@ static int compilerAddVariable(Compiler* compiler, const char* name,
|
|||||||
return compiler->var_count++;
|
return compiler->var_count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add a literal constant to scripts literals and return it's index.
|
||||||
|
static int compilerAddConstant(Compiler* compiler, Var value) {
|
||||||
|
VarBuffer* literals = &compiler->script->literals;
|
||||||
|
|
||||||
|
for (int i = 0; i < literals->count; i++) {
|
||||||
|
if (isVauesSame(literals->data[i], value)) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add new constant to script.
|
||||||
|
if (literals->count < MAX_CONSTANTS) {
|
||||||
|
varBufferWrite(literals, compiler->vm, value);
|
||||||
|
} else {
|
||||||
|
parseError(&compiler->parser, "A script should contain at most %d "
|
||||||
|
"unique constants.", MAX_CONSTANTS);
|
||||||
|
}
|
||||||
|
return (int)literals->count - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enters inside a block.
|
||||||
|
static void compilerEnterBlock(Compiler* compiler) {
|
||||||
|
compiler->scope_depth++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exits a block.
|
||||||
|
static void compilerExitBlock(Compiler* compiler) {
|
||||||
|
ASSERT(compiler->scope_depth > -1, "Cannot exit toplevel.");
|
||||||
|
|
||||||
|
while (compiler->variables[compiler->var_count - 1].depth >=
|
||||||
|
compiler->scope_depth) {
|
||||||
|
compiler->var_count--;
|
||||||
|
compiler->stack_size--;
|
||||||
|
}
|
||||||
|
compiler->scope_depth--;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
* COMPILING (EMIT BYTECODE) *
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
// Emit a single byte and return it's index.
|
||||||
|
static int emitByte(Compiler* compiler, int byte) {
|
||||||
|
|
||||||
|
byteBufferWrite(&compiler->function->fn->opcodes, compiler->vm,
|
||||||
|
(uint8_t)byte);
|
||||||
|
intBufferWrite(&compiler->function->fn->oplines, compiler->vm,
|
||||||
|
compiler->parser.previous.line);
|
||||||
|
return (int)compiler->function->fn->opcodes.count - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emit 2 bytes argument as big indian. return it's starting index.
|
||||||
|
static int emitShort(Compiler* compiler, int arg) {
|
||||||
|
emitByte(compiler, (arg >> 8) & 0xff);
|
||||||
|
return emitByte(compiler, arg & 0xff) - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emits an instruction and update stack size (variable stack size opcodes
|
||||||
|
// should be handled).
|
||||||
|
static void emitOpcode(Compiler* compiler, Opcode opcode) {
|
||||||
|
emitByte(compiler, (int)opcode);
|
||||||
|
|
||||||
|
compiler->stack_size += opcode_info[opcode].stack;
|
||||||
|
if (compiler->stack_size > compiler->function->fn->stack_size) {
|
||||||
|
compiler->function->fn->stack_size = compiler->stack_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emits a constant value if it doesn't exists on the current script it'll make
|
||||||
|
// one.
|
||||||
|
static void emitConstant(Compiler* compiler, Var value) {
|
||||||
|
int index = compilerAddConstant(compiler, value);
|
||||||
|
emitOpcode(compiler, OP_CONSTANT);
|
||||||
|
emitShort(compiler, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void patchJump(Compiler* compiler, int addr_index) {
|
||||||
|
int jump_to = (int)compiler->function->fn->opcodes.count;
|
||||||
|
ASSERT(jump_to < MAX_JUMP, "Too large address to jump.");
|
||||||
|
|
||||||
|
compiler->function->fn->opcodes.data[addr_index] = (jump_to >> 8) & 0xff;
|
||||||
|
compiler->function->fn->opcodes.data[addr_index + 1] = jump_to & 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
* COMPILING (PARSE TOPLEVEL) *
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
static void compileStatement(Compiler* compiler);
|
||||||
|
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;
|
||||||
@ -926,7 +1131,7 @@ static void compileFunction(Compiler* compiler, bool is_native) {
|
|||||||
functionBufferWrite(&compiler->script->functions, compiler->vm, func);
|
functionBufferWrite(&compiler->script->functions, compiler->vm, func);
|
||||||
vmPopTempRef(compiler->vm);
|
vmPopTempRef(compiler->vm);
|
||||||
|
|
||||||
compiler->fn = func;
|
compiler->function = func;
|
||||||
|
|
||||||
consume(parser, TK_LPARAN, "Expected '(' after function name.");
|
consume(parser, TK_LPARAN, "Expected '(' after function name.");
|
||||||
|
|
||||||
@ -937,34 +1142,153 @@ static void compileFunction(Compiler* compiler, bool is_native) {
|
|||||||
int predef = compilerSearchVariables(compiler, parser->previous.start,
|
int predef = compilerSearchVariables(compiler, parser->previous.start,
|
||||||
parser->previous.length, SCOPE_CURRENT);
|
parser->previous.length, SCOPE_CURRENT);
|
||||||
if (predef != -1) {
|
if (predef != -1) {
|
||||||
// TODO: error("Multiple definition of a parameter");
|
parseError(parser, "Multiple definition of a parameter");
|
||||||
}
|
}
|
||||||
match(parser, TK_COMMA);
|
match(parser, TK_COMMA);
|
||||||
}
|
}
|
||||||
|
|
||||||
consume(parser, TK_RPARAN, "Expected ')' after parameters end.");
|
consume(parser, TK_RPARAN, "Expected ')' after parameters end.");
|
||||||
matchEndStatement(parser);
|
consumeEndStatement(parser);
|
||||||
|
|
||||||
if (is_native) { // Done here.
|
if (is_native) { // Done here.
|
||||||
compiler->scope_depth--; // Parameter scope.
|
compiler->scope_depth--; // Parameter scope.
|
||||||
compiler->fn = NULL;
|
compiler->function = NULL;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Compile body.
|
compileBlockBody(compiler, false);
|
||||||
|
|
||||||
compiler->scope_depth--; // Parameter scope.
|
compiler->scope_depth--; // Parameter scope.
|
||||||
compiler->fn = NULL;
|
compiler->function = compiler->script->body;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*****************************************************************************
|
// Finish a block body.
|
||||||
* COMPILING (STATEMENTS) *
|
static void compileBlockBody(Compiler* compiler, bool if_body) {
|
||||||
*****************************************************************************/
|
compilerEnterBlock(compiler);
|
||||||
|
|
||||||
|
TokenType next = peek(&compiler->parser);
|
||||||
|
while (!(next == TK_END || next == TK_EOF || (
|
||||||
|
if_body && (next == TK_ELSE || next == TK_ELIF)))) {
|
||||||
|
|
||||||
|
compileStatement(compiler);
|
||||||
|
next = peek(&compiler->parser);
|
||||||
|
}
|
||||||
|
|
||||||
|
compilerExitBlock(compiler);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compiles an expression. An expression will result a value on top of the
|
||||||
|
// stack.
|
||||||
|
static void compileExpression(Compiler* compiler) {
|
||||||
|
parsePrecedence(compiler, PREC_LOWEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void compileIfStatement(Compiler* compiler) {
|
||||||
|
|
||||||
|
compileExpression(compiler); //< Condition.
|
||||||
|
emitOpcode(compiler, OP_JUMP_IF_NOT);
|
||||||
|
int ifpatch = emitByte(compiler, 0xffff); //< Will be patched.
|
||||||
|
|
||||||
|
consumeStartBlock(&compiler->parser);
|
||||||
|
|
||||||
|
compileBlockBody(compiler, true);
|
||||||
|
|
||||||
|
if (match(&compiler->parser, TK_ELIF)) {
|
||||||
|
patchJump(compiler, ifpatch);
|
||||||
|
compileBlockBody(compiler, true);
|
||||||
|
|
||||||
|
} else if (match(&compiler->parser, TK_ELSE)) {
|
||||||
|
patchJump(compiler, ifpatch);
|
||||||
|
compileBlockBody(compiler, false);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
patchJump(compiler, ifpatch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void compileWhileStatement(Compiler* compiler) {
|
||||||
|
Loop loop;
|
||||||
|
loop.start = (int)compiler->function->fn->opcodes.count;
|
||||||
|
loop.patch_count = 0;
|
||||||
|
loop.outer_loop = compiler->loop;
|
||||||
|
compiler->loop = &loop;
|
||||||
|
|
||||||
|
compileExpression(compiler); //< Condition.
|
||||||
|
emitOpcode(compiler, OP_JUMP_IF_NOT);
|
||||||
|
int whilepatch = emitByte(compiler, 0xffff); //< Will be patched.
|
||||||
|
|
||||||
|
compileBlockBody(compiler, false);
|
||||||
|
|
||||||
|
emitOpcode(compiler, OP_JUMP);
|
||||||
|
emitShort(compiler, loop.start);
|
||||||
|
|
||||||
|
patchJump(compiler, whilepatch);
|
||||||
|
|
||||||
|
// Patch break statement.
|
||||||
|
for (int i = 0; i < compiler->loop->patch_count; i++) {
|
||||||
|
patchJump(compiler, compiler->loop->patches[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
compiler->loop = loop.outer_loop;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void compileForStatement(Compiler* compiler) {
|
||||||
|
ASSERT(false, "TODO:");
|
||||||
|
}
|
||||||
|
|
||||||
// Compiles a statement. Assignment could be an assignment statement or a new
|
// Compiles a statement. Assignment could be an assignment statement or a new
|
||||||
// variable declaration, which will be handled.
|
// variable declaration, which will be handled.
|
||||||
static void compileStatement(Compiler* compiler) {
|
static void compileStatement(Compiler* compiler) {
|
||||||
// TODO:
|
Parser* parser = &compiler->parser;
|
||||||
|
if (match(parser, TK_BREAK)) {
|
||||||
|
if (compiler->loop == NULL) {
|
||||||
|
parseError(parser, "Cannot use 'break' outside a loop.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT(compiler->loop->patch_count < MAX_BREAK_PATCH,
|
||||||
|
"Too many break statements (" STRINGIFY(MAX_BREAK_PATCH) ")." );
|
||||||
|
|
||||||
|
emitOpcode(compiler, OP_JUMP);
|
||||||
|
int patch = emitByte(compiler, 0xffff); //< Will be patched.
|
||||||
|
compiler->loop->patches[compiler->loop->patch_count++] = patch;
|
||||||
|
|
||||||
|
} else if (match(parser, TK_CONTINUE)) {
|
||||||
|
if (compiler->loop == NULL) {
|
||||||
|
parseError(parser, "Cannot use 'continue' outside a loop.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
emitOpcode(compiler, OP_JUMP);
|
||||||
|
emitShort(compiler, compiler->loop->start);
|
||||||
|
|
||||||
|
} else if (match(parser, TK_RETURN)) {
|
||||||
|
|
||||||
|
if (compiler->scope_depth == -1) {
|
||||||
|
parseError(parser, "Invalid 'return' outside a function.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (peek(parser) == TK_SEMICOLLON || peek(parser) == TK_LINE) {
|
||||||
|
emitOpcode(compiler, OP_PUSH_NULL);
|
||||||
|
emitOpcode(compiler, OP_RETURN);
|
||||||
|
} else {
|
||||||
|
compileExpression(compiler); //< Return value is at stack top.
|
||||||
|
emitOpcode(compiler, OP_RETURN);
|
||||||
|
}
|
||||||
|
} else if (match(parser, TK_IF)) {
|
||||||
|
compileIfStatement(compiler);
|
||||||
|
|
||||||
|
} else if (match(parser, TK_WHILE)) {
|
||||||
|
compileWhileStatement(compiler);
|
||||||
|
|
||||||
|
} else if (match(parser, TK_FOR)) {
|
||||||
|
compileForStatement(compiler);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
compileExpression(compiler);
|
||||||
|
emitOpcode(compiler, OP_POP);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Script* compileSource(MSVM* vm, const char* path) {
|
Script* compileSource(MSVM* vm, const char* path) {
|
||||||
@ -983,6 +1307,7 @@ Script* compileSource(MSVM* vm, const char* path) {
|
|||||||
|
|
||||||
Script* script = newScript(vm);
|
Script* script = newScript(vm);
|
||||||
compiler.script = script;
|
compiler.script = script;
|
||||||
|
compiler.function = script->body;
|
||||||
|
|
||||||
// Parser pointer for quick access.
|
// Parser pointer for quick access.
|
||||||
Parser* parser = &compiler.parser;
|
Parser* parser = &compiler.parser;
|
||||||
@ -1002,7 +1327,7 @@ Script* compileSource(MSVM* vm, const char* path) {
|
|||||||
|
|
||||||
} else if (match(parser, TK_IMPORT)) {
|
} else if (match(parser, TK_IMPORT)) {
|
||||||
// TODO: import statement must be first of all other.
|
// TODO: import statement must be first of all other.
|
||||||
|
ASSERT(false, "TODO:");
|
||||||
} else {
|
} else {
|
||||||
compileStatement(&compiler);
|
compileStatement(&compiler);
|
||||||
}
|
}
|
||||||
|
179
src/opcodes.h
Normal file
179
src/opcodes.h
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2021 Thakee Nathees
|
||||||
|
* Licensed under: MIT License
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Opcodes X macro (http://en.wikipedia.org/wiki/X_Macro) should be included
|
||||||
|
// in the source where it'll be used. Required to define the following...
|
||||||
|
//
|
||||||
|
// #define OPCODE(name, params, stack)
|
||||||
|
// #include "opcodes.h"
|
||||||
|
//
|
||||||
|
// first parameter is the opcode name, 2nd will be the size of the parameter
|
||||||
|
// in bytes 3 one is how many stack slots it'll take after executing the
|
||||||
|
// instruction.
|
||||||
|
|
||||||
|
// Load the constant at index [arg] from the script's literals.
|
||||||
|
// params: 2 byte (uint16_t) index value.
|
||||||
|
OPCODE(CONSTANT, 2, 1)
|
||||||
|
|
||||||
|
// Push null on the stack.
|
||||||
|
OPCODE(PUSH_NULL, 0, 0)
|
||||||
|
|
||||||
|
// Push true on the stack.
|
||||||
|
OPCODE(PUSH_TRUE, 0, 0)
|
||||||
|
|
||||||
|
// Push false on the stack.
|
||||||
|
OPCODE(PUSH_FALSE, 0, 0)
|
||||||
|
|
||||||
|
// Push stack local on top of the stack. Locals at 0 to 8 marked explicitly
|
||||||
|
// since it's performance criticle.
|
||||||
|
// params: PUSH_LOCAL_N -> 2 bytes (uint16_t) count value.
|
||||||
|
OPCODE(PUSH_LOCAL_0, 0, 1)
|
||||||
|
OPCODE(PUSH_LOCAL_1, 0, 1)
|
||||||
|
OPCODE(PUSH_LOCAL_2, 0, 1)
|
||||||
|
OPCODE(PUSH_LOCAL_3, 0, 1)
|
||||||
|
OPCODE(PUSH_LOCAL_4, 0, 1)
|
||||||
|
OPCODE(PUSH_LOCAL_5, 0, 1)
|
||||||
|
OPCODE(PUSH_LOCAL_6, 0, 1)
|
||||||
|
OPCODE(PUSH_LOCAL_7, 0, 1)
|
||||||
|
OPCODE(PUSH_LOCAL_8, 0, 1)
|
||||||
|
OPCODE(PUSH_LOCAL_N, 2, 1)
|
||||||
|
|
||||||
|
// Pop the stack top value and store to another stack local index.
|
||||||
|
// params: STORE_LOCAL_N -> 2 bytes (uint16_t) count value.
|
||||||
|
OPCODE(STORE_LOCAL_0, 0, -1)
|
||||||
|
OPCODE(STORE_LOCAL_1, 0, -1)
|
||||||
|
OPCODE(STORE_LOCAL_2, 0, -1)
|
||||||
|
OPCODE(STORE_LOCAL_3, 0, -1)
|
||||||
|
OPCODE(STORE_LOCAL_4, 0, -1)
|
||||||
|
OPCODE(STORE_LOCAL_5, 0, -1)
|
||||||
|
OPCODE(STORE_LOCAL_6, 0, -1)
|
||||||
|
OPCODE(STORE_LOCAL_7, 0, -1)
|
||||||
|
OPCODE(STORE_LOCAL_8, 0, -1)
|
||||||
|
OPCODE(STORE_LOCAL_N, 2, -1)
|
||||||
|
|
||||||
|
// Push the script global value on the stack.
|
||||||
|
// params: 2 bytes (uint16_t) index.
|
||||||
|
OPCODE(PUSH_GLOBAL, 2, 1)
|
||||||
|
|
||||||
|
// Pop and store the value to script's global.
|
||||||
|
// params: 2 bytes (uint16_t) index.
|
||||||
|
OPCODE(STORE_GLOBAL, 2, -1)
|
||||||
|
|
||||||
|
// Push imported script's global value on the stack.
|
||||||
|
// params: 4 bytes script ID and 2 bytes index.
|
||||||
|
OPCODE(PUSH_GLOBAL_EXT, 6, 1)
|
||||||
|
|
||||||
|
// Pop and store the value to imported script's global.
|
||||||
|
// params: 4 bytes script ID and 2 bytes index.
|
||||||
|
OPCODE(STORE_GLOBAL_EXT, 6, -1)
|
||||||
|
|
||||||
|
// Push the script's function on the stack. It could later be called. But a
|
||||||
|
// function can't be stored i.e. can't assign a function with something else.
|
||||||
|
// params: 2 bytes index.
|
||||||
|
OPCODE(PUSH_FN, 2, 1)
|
||||||
|
|
||||||
|
// Push an imported script's function.
|
||||||
|
// params: 4 bytes script ID and 2 bytes index.
|
||||||
|
OPCODE(PUSH_FN_EXT, 6, 1)
|
||||||
|
|
||||||
|
// Pop the stack top.
|
||||||
|
OPCODE(POP, 0, -1)
|
||||||
|
|
||||||
|
// Calls a function using stack's top N values as the arguments and once it
|
||||||
|
// done the stack top should be stored otherwise it'll be disregarded. The
|
||||||
|
// function should set the 0 th argment to return value. Locals at 0 to 8
|
||||||
|
// marked explicitly since it's performance criticle.
|
||||||
|
// params: CALL_0..8 -> 2 bytes index. _N -> 2 bytes index and 2 bytes count.
|
||||||
|
OPCODE(CALL_0, 2, 1) //< Return value will be pushed.
|
||||||
|
OPCODE(CALL_1, 2, 0) //< 1st argument poped and return value pushed.
|
||||||
|
OPCODE(CALL_2, 2, -1) //< 2 args will be popped and return value pushed.
|
||||||
|
OPCODE(CALL_3, 2, -2)
|
||||||
|
OPCODE(CALL_4, 2, -3)
|
||||||
|
OPCODE(CALL_5, 2, -4)
|
||||||
|
OPCODE(CALL_6, 2, -5)
|
||||||
|
OPCODE(CALL_7, 2, -6)
|
||||||
|
OPCODE(CALL_8, 2, -7)
|
||||||
|
OPCODE(CALL_N, 4, -0) //< Will calculated at compile time.
|
||||||
|
|
||||||
|
// Call a function from an imported script.
|
||||||
|
// params: 4 bytes script ID and 2 bytes index. _N -> +2 bytes for argc.
|
||||||
|
OPCODE(CALL_EXT_0, 6, 1)
|
||||||
|
OPCODE(CALL_EXT_1, 6, 0)
|
||||||
|
OPCODE(CALL_EXT_2, 6, -1)
|
||||||
|
OPCODE(CALL_EXT_3, 6, -2)
|
||||||
|
OPCODE(CALL_EXT_4, 6, -3)
|
||||||
|
OPCODE(CALL_EXT_5, 6, -4)
|
||||||
|
OPCODE(CALL_EXT_6, 6, -5)
|
||||||
|
OPCODE(CALL_EXT_7, 6, -6)
|
||||||
|
OPCODE(CALL_EXT_8, 6, -7)
|
||||||
|
OPCODE(CALL_EXT_N, 8, -0) //< Will calculated at compile time.
|
||||||
|
|
||||||
|
// The address to jump to. It'll set the ip to the address it should jump to
|
||||||
|
// and the address is absolute not an offset from ip's current value.
|
||||||
|
// param: 2 bytes jump address.
|
||||||
|
OPCODE(JUMP, 2, 0)
|
||||||
|
|
||||||
|
// Pop the stack top value and if it's true jump.
|
||||||
|
// param: 2 bytes jump address.
|
||||||
|
OPCODE(JUMP_NOT, 2, -1)
|
||||||
|
|
||||||
|
// Pop the stack top value and if it's false jump.
|
||||||
|
// param: 2 bytes jump address.
|
||||||
|
OPCODE(JUMP_IF_NOT, 2, -1)
|
||||||
|
|
||||||
|
// Pop the stack top value and store it to the current stack frame's 0 index.
|
||||||
|
// Then it'll pop the current stack frame.
|
||||||
|
OPCODE(RETURN, 0, -1)
|
||||||
|
|
||||||
|
// Pop var get attribute push the value.
|
||||||
|
// param: 2 byte attrib name index.
|
||||||
|
OPCODE(GET_ATTRIB, 2, 0)
|
||||||
|
|
||||||
|
// Pop var and value update the attribute push result.
|
||||||
|
// param: 2 byte attrib name index.
|
||||||
|
OPCODE(SET_ATTRIB, 2, -1)
|
||||||
|
|
||||||
|
// Pop var, key, get value and push the result.
|
||||||
|
OPCODE(GET_SUBSCRIPT, 0, -1)
|
||||||
|
|
||||||
|
// Pop var, key, value set and push value back.
|
||||||
|
OPCODE(SET_SUBSCRIPT, 0, -2)
|
||||||
|
|
||||||
|
// Pop unary operand and push value.
|
||||||
|
OPCODE(NEGATIVE, 0, 0) //< Negative number value.
|
||||||
|
OPCODE(NOT, 0, 0) //< boolean not.
|
||||||
|
OPCODE(BIT_NOT, 0, 0) //< bitwise not.
|
||||||
|
|
||||||
|
// Pop binary operands and push value.
|
||||||
|
OPCODE(ADD, 0, -1)
|
||||||
|
OPCODE(SUBTRACT, 0, -1)
|
||||||
|
OPCODE(MULTIPLY, 0, -1)
|
||||||
|
OPCODE(DIVIDE, 0, -1)
|
||||||
|
OPCODE(MOD, 0, -1)
|
||||||
|
|
||||||
|
OPCODE(BIT_AND, 0, -1)
|
||||||
|
OPCODE(BIT_OR, 0, -1)
|
||||||
|
OPCODE(BIT_XOR, 0, -1)
|
||||||
|
OPCODE(BIT_LSHIFT, 0, -1)
|
||||||
|
OPCODE(BIT_RSHIFT, 0, -1)
|
||||||
|
|
||||||
|
OPCODE(AND, 0, -1)
|
||||||
|
OPCODE(OR, 0, -1)
|
||||||
|
OPCODE(EQEQ, 0, -1)
|
||||||
|
OPCODE(NOTEQ, 0, -1)
|
||||||
|
OPCODE(LT, 0, -1)
|
||||||
|
OPCODE(LTEQ, 0, -1)
|
||||||
|
OPCODE(GT, 0, -1)
|
||||||
|
OPCODE(GTEQ, 0, -1)
|
||||||
|
|
||||||
|
OPCODE(RANGE, 0, -1) //< Pop 2 integer make range push.
|
||||||
|
OPCODE(IS, 0, -1)
|
||||||
|
OPCODE(IN, 0, -1)
|
||||||
|
|
||||||
|
// TODO: literal list, map
|
||||||
|
|
||||||
|
// A sudo instruction which will never be called. A function's last opcode
|
||||||
|
// used for debugging.
|
||||||
|
OPCODE(END, 0, 0)
|
@ -29,6 +29,7 @@ void $name_l$BufferFill($name$Buffer* self, MSVM* vm, $type$ data, int count) {
|
|||||||
|
|
||||||
if (self->capacity < self->count + count) {
|
if (self->capacity < self->count + count) {
|
||||||
int capacity = utilPowerOf2Ceil((int)self->count + count);
|
int capacity = utilPowerOf2Ceil((int)self->count + count);
|
||||||
|
if (capacity < MIN_CAPACITY) capacity = MIN_CAPACITY;
|
||||||
self->data = ($type$*)vmRealloc(vm, self->data,
|
self->data = ($type$*)vmRealloc(vm, self->data,
|
||||||
self->capacity * sizeof($type$), capacity * sizeof($type$));
|
self->capacity * sizeof($type$), capacity * sizeof($type$));
|
||||||
self->capacity = capacity;
|
self->capacity = capacity;
|
||||||
|
@ -13,12 +13,6 @@
|
|||||||
#include "../common.h"
|
#include "../common.h"
|
||||||
#include "miniscript.h"
|
#include "miniscript.h"
|
||||||
|
|
||||||
// The factor by which the buffer will grow when it's capacity reached.
|
|
||||||
#define GROW_FACTOR 2
|
|
||||||
|
|
||||||
// The initial capacity of the buffer.
|
|
||||||
#define MIN_CAPACITY 16
|
|
||||||
|
|
||||||
// A place holder typedef to prevent IDE syntax errors. Remove this line
|
// A place holder typedef to prevent IDE syntax errors. Remove this line
|
||||||
// when generating the source.
|
// when generating the source.
|
||||||
typedef uint8_t $type$;
|
typedef uint8_t $type$;
|
||||||
|
20
src/var.c
20
src/var.c
@ -62,6 +62,12 @@ Script* newScript(MSVM* vm) {
|
|||||||
varBufferInit(&script->globals);
|
varBufferInit(&script->globals);
|
||||||
nameTableInit(&script->global_names);
|
nameTableInit(&script->global_names);
|
||||||
|
|
||||||
|
varBufferInit(&script->literals);
|
||||||
|
|
||||||
|
vmPushTempRef(vm, &script->_super);
|
||||||
|
script->body = newFunction(vm, "@(ScriptLevel)", script, false);
|
||||||
|
vmPopTempRef(vm);
|
||||||
|
|
||||||
functionBufferInit(&script->functions);
|
functionBufferInit(&script->functions);
|
||||||
nameTableInit(&script->function_names);
|
nameTableInit(&script->function_names);
|
||||||
|
|
||||||
@ -83,9 +89,9 @@ Function* newFunction(MSVM* vm, const char* name, Script* owner,
|
|||||||
if (is_native) {
|
if (is_native) {
|
||||||
func->native = NULL;
|
func->native = NULL;
|
||||||
} else {
|
} else {
|
||||||
vmPushTempRef(vm, &func->_super);
|
//vmPushTempRef(vm, &func->_super);
|
||||||
Fn* fn = ALLOCATE(vm, Fn);
|
Fn* fn = ALLOCATE(vm, Fn);
|
||||||
vmPopTempRef(vm);
|
//vmPopTempRef(vm);
|
||||||
|
|
||||||
byteBufferInit(&fn->opcodes);
|
byteBufferInit(&fn->opcodes);
|
||||||
intBufferInit(&fn->oplines);
|
intBufferInit(&fn->oplines);
|
||||||
@ -94,3 +100,13 @@ Function* newFunction(MSVM* vm, const char* name, Script* owner,
|
|||||||
}
|
}
|
||||||
return func;
|
return func;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool isVauesSame(Var v1, Var v2) {
|
||||||
|
#if VAR_NAN_TAGGING
|
||||||
|
// Bit representation of each values are unique so just compare the bits.
|
||||||
|
return v1 == v2;
|
||||||
|
#else
|
||||||
|
#error TODO:
|
||||||
|
#endif
|
||||||
|
}
|
10
src/var.h
10
src/var.h
@ -242,6 +242,10 @@ struct Script {
|
|||||||
VarBuffer globals; //< Script level global variables.
|
VarBuffer globals; //< Script level global variables.
|
||||||
NameTable global_names; //< Name map to index in globals.
|
NameTable global_names; //< Name map to index in globals.
|
||||||
|
|
||||||
|
VarBuffer literals; //< Script literal constant values.
|
||||||
|
|
||||||
|
Function* body; //< Script body is an anonymous function.
|
||||||
|
|
||||||
FunctionBuffer functions; //< Script level functions.
|
FunctionBuffer functions; //< Script level functions.
|
||||||
NameTable function_names; //< Name map to index in functions.
|
NameTable function_names; //< Name map to index in functions.
|
||||||
|
|
||||||
@ -293,7 +297,11 @@ Script* newScript(MSVM* vm);
|
|||||||
|
|
||||||
// Allocate new Function object and return Function*. Parameter [name] should
|
// Allocate new Function object and return Function*. Parameter [name] should
|
||||||
// be the name in the Script's nametable.
|
// be the name in the Script's nametable.
|
||||||
Function* newFunction(MSVM* vm, const char* name, Script* owner, bool is_native);
|
Function* newFunction(MSVM* vm, const char* name, Script* owner,
|
||||||
|
bool is_native);
|
||||||
|
|
||||||
|
// Utility functions //////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
bool isVauesSame(Var v1, Var v2);
|
||||||
|
|
||||||
#endif // VAR_H
|
#endif // VAR_H
|
||||||
|
6
src/vm.h
6
src/vm.h
@ -16,6 +16,12 @@
|
|||||||
// garbage collected.
|
// garbage collected.
|
||||||
#define MAX_TEMP_REFERENCE 8
|
#define MAX_TEMP_REFERENCE 8
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
#define OPCODE(name, _, __) OP_##name,
|
||||||
|
#include "opcodes.h"
|
||||||
|
#undef OPCODE
|
||||||
|
} Opcode;
|
||||||
|
|
||||||
struct MSVM {
|
struct MSVM {
|
||||||
|
|
||||||
// The first object in the link list of all heap allocated objects.
|
// The first object in the link list of all heap allocated objects.
|
||||||
|
@ -35,7 +35,10 @@ MSLoadScriptResult loadScript(MSVM* vm, const char* path) {
|
|||||||
MSLoadScriptResult result;
|
MSLoadScriptResult result;
|
||||||
result.is_failed = false;
|
result.is_failed = false;
|
||||||
|
|
||||||
result.source = "def someFunction(a, b, c);";
|
result.source = ""
|
||||||
|
"if -1+2 * 3\n"
|
||||||
|
"end\n"
|
||||||
|
;
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
// FIXME:
|
// FIXME:
|
||||||
@ -60,7 +63,6 @@ int main() {
|
|||||||
//clogger_logfError("[DummyError] dummy error\n");
|
//clogger_logfError("[DummyError] dummy error\n");
|
||||||
//clogger_logfWarning("[DummyWarning] dummy warning\n");
|
//clogger_logfWarning("[DummyWarning] dummy warning\n");
|
||||||
|
|
||||||
printf("Here are the first 8 chars: %.8s\n", "A string that is more than 8 chars");
|
|
||||||
//parseError(parser, "A function named %.*s already exists at %s:%s", length, start, file, line);
|
//parseError(parser, "A function named %.*s already exists at %s:%s", length, start, file, line);
|
||||||
|
|
||||||
FILE* fp = fopen("test.ms", "r");
|
FILE* fp = fopen("test.ms", "r");
|
||||||
|
Loading…
Reference in New Issue
Block a user