mirror of
https://github.com/zekexiao/pocketlang.git
synced 2025-02-11 07:00:58 +08:00
increment operators implemented
This commit is contained in:
parent
c69a5686bb
commit
f090089e4b
@ -78,7 +78,7 @@ typedef struct {
|
||||
} MSConfiguration;
|
||||
|
||||
typedef enum {
|
||||
RESULT_SUCCESS,
|
||||
RESULT_SUCCESS = 0,
|
||||
RESULT_COMPILE_ERROR,
|
||||
RESULT_RUNTIME_ERROR,
|
||||
} MSInterpretResult;
|
||||
|
145
src/compiler.c
145
src/compiler.c
@ -5,6 +5,9 @@
|
||||
|
||||
#include "compiler.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "core.h"
|
||||
#include "types/name_table.h"
|
||||
#include "types/gen/byte_buffer.h"
|
||||
@ -481,10 +484,10 @@ static void eatNumber(Parser* parser) {
|
||||
|
||||
// Read and ignore chars till it reach new line or EOF.
|
||||
static void skipLineComment(Parser* parser) {
|
||||
char c = eatChar(parser);
|
||||
|
||||
while (c != '\n' && c != '\0') {
|
||||
c = eatChar(parser);
|
||||
char c;
|
||||
while ((c = peekChar(parser)) != '\0') {
|
||||
eatChar(parser);
|
||||
if (c == '\n') return;
|
||||
}
|
||||
}
|
||||
|
||||
@ -680,6 +683,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 {
|
||||
|
45
src/core.c
45
src/core.c
@ -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
|
||||
|
@ -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.
|
||||
|
28
src/var.c
28
src/var.c
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
43
src/vm.c
43
src/vm.c
@ -14,7 +14,7 @@
|
||||
#define INITIAL_CALL_FRAMES 4
|
||||
|
||||
// Minimum size of the stack.
|
||||
#define MIN_STACK_SIZE 16
|
||||
#define MIN_STACK_SIZE 128
|
||||
|
||||
void* vmRealloc(MSVM* self, void* memory, size_t old_size, size_t new_size) {
|
||||
|
||||
@ -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;
|
||||
|
||||
|
29
test/main.c
29
test/main.c
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user