increment operators implemented

This commit is contained in:
Thakee Nathees 2021-02-15 18:19:19 +05:30
parent c69a5686bb
commit f090089e4b
8 changed files with 223 additions and 75 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,30 @@ 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);
} else {
// Skip the current line, to prevent multiple caseaded errors on the
// same line.
skipLineComment(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 +715,20 @@ 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;
}
// 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 +749,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;
}
/*****************************************************************************
@ -932,7 +954,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 +969,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 +999,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;
@ -1345,7 +1378,7 @@ static int compileFunction(Compiler* compiler, FuncType fn_type) {
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;
@ -1574,7 +1607,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 {
@ -50,7 +52,7 @@ int findBuiltinFunction(const char* name, int length) {
// 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;
@ -170,6 +172,13 @@ void coreImport(MSVM* vm) {
}
}
// TODO: Temp function to benchmark.
void coreClock(MSVM* vm) {
RET(VAR_NUM((double)clock() / CLOCKS_PER_SEC));
}
void initializeCore(MSVM* vm) {
int i = 0; //< Iterate through builtins.
@ -191,6 +200,9 @@ void initializeCore(MSVM* vm) {
initializeBuiltinFN(vm, &builtins[i++], "print", -1, corePrint);
initializeBuiltinFN(vm, &builtins[i++], "import", 1, coreImport);
// FIXME: move this. temp for benchmarking.
initializeBuiltinFN(vm, &builtins[i++], "clock", 0, coreClock);
// Sentinal to mark the end of the array.
initializeBuiltinFN(vm, &builtins[i], NULL, 0, NULL);
@ -213,7 +225,15 @@ 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;
}
TODO; //string addition/ array addition etc.
return VAR_NULL;
}
@ -236,6 +256,27 @@ Var varDivide(MSVM* vm, Var v1, Var v2) {
return VAR_NULL;
}
bool varGreater(Var v1, Var v2) {
double d1, d2;
if (isNumeric(v1, &d1) && isNumeric(v2, &d2)) {
return d1 > d2;
}
TODO;
return false;
}
bool varLesser(Var v1, Var v2) {
double d1, d2;
if (isNumeric(v1, &d1) && isNumeric(v2, &d2)) {
return d1 < d2;
}
TODO;
return false;
}
bool varIterate(MSVM* vm, Var seq, Var* iterator, Var* value) {
#ifdef DEBUG

View File

@ -24,6 +24,9 @@ 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(Var v1, Var v2);
bool varLesser(Var v1, Var v2);
// Functions //////////////////////////////////////////////////////////////////
// Parameter [iterator] should be VAR_NULL before starting the iteration.

View File

@ -3,6 +3,8 @@
* Licensed under: MIT License
*/
#include <stdio.h>
#include "var.h"
#include "vm.h"
@ -187,3 +189,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

@ -303,4 +303,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

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) {
@ -57,6 +57,8 @@ void vmPopTempRef(MSVM* self) {
* RUNTIME *
*****************************************************************************/
#ifdef DEBUG
#include <stdio.h>
// TODO: A function for quick debug. REMOVE.
void _printStackTop(MSVM* vm) {
if (vm->sp != vm->stack) {
@ -64,6 +66,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 +380,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):
{
@ -419,6 +438,12 @@ MSInterpretResult vmRunScript(MSVM* vm, Script* _script) {
}
OPCODE(NOT):
{
Var val = POP();
PUSH(VAR_BOOL(!toBool(val)));
DISPATCH();
}
OPCODE(BIT_NOT):
TODO;
@ -452,9 +477,23 @@ MSInterpretResult vmRunScript(MSVM* vm, Script* _script) {
OPCODE(OR):
OPCODE(EQEQ):
OPCODE(NOTEQ):
TODO;
OPCODE(LT):
{
PUSH(VAR_BOOL(varLesser(POP(), POP())));
DISPATCH();
}
OPCODE(LTEQ):
TODO;
OPCODE(GT):
{
PUSH(VAR_BOOL(varGreater(POP(), POP())));
DISPATCH();
}
OPCODE(GTEQ):
TODO;

View File

@ -63,6 +63,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 +83,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;
}