mirror of
https://github.com/CPunch/Cosmo.git
synced 2024-11-05 08:10:05 +00:00
Major refactoring, added classes, many bug fixes
This commit is contained in:
parent
aa975b7330
commit
c42a72dfad
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@ -7,6 +7,7 @@
|
|||||||
"tuple": "cpp",
|
"tuple": "cpp",
|
||||||
"type_traits": "cpp",
|
"type_traits": "cpp",
|
||||||
"utility": "cpp",
|
"utility": "cpp",
|
||||||
"vector": "cpp"
|
"vector": "cpp",
|
||||||
|
"typeinfo": "c"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -9,13 +9,12 @@ void cosmoB_loadlibrary(CState *state) {
|
|||||||
cosmoM_unfreezeGC(state);
|
cosmoM_unfreezeGC(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
int cosmoB_print(CState *state, int nargs, CValue *args) {
|
CValue cosmoB_print(CState *state, int nargs, CValue *args) {
|
||||||
for (int i = 0; i < nargs; i++) {
|
for (int i = 0; i < nargs; i++) {
|
||||||
CObjString *str = cosmoV_toString(state, args[i]);
|
CObjString *str = cosmoV_toString(state, args[i]);
|
||||||
printf("%s", cosmoO_readCString(str));
|
printf("%s", cosmoO_readCString(str));
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("\n");
|
printf("\n");
|
||||||
|
|
||||||
return 0; // print doesn't return any args
|
return cosmoV_newNil(); // print doesn't return any args
|
||||||
}
|
}
|
@ -4,6 +4,6 @@
|
|||||||
#include "cstate.h"
|
#include "cstate.h"
|
||||||
|
|
||||||
COSMO_API void cosmoB_loadlibrary(CState *state);
|
COSMO_API void cosmoB_loadlibrary(CState *state);
|
||||||
COSMO_API int cosmoB_print(CState *state, int nargs, CValue *args);
|
COSMO_API CValue cosmoB_print(CState *state, int nargs, CValue *args);
|
||||||
|
|
||||||
#endif
|
#endif
|
12
src/cdebug.c
12
src/cdebug.c
@ -32,14 +32,6 @@ int constInstruction(const char *name, CChunk *chunk, int offset, int indent) {
|
|||||||
return offset + 1 + (sizeof(uint16_t) / sizeof(INSTRUCTION)); // consume opcode + uint
|
return offset + 1 + (sizeof(uint16_t) / sizeof(INSTRUCTION)); // consume opcode + uint
|
||||||
}
|
}
|
||||||
|
|
||||||
int ABOperandInstruction(const char *name, CChunk *chunk, int offset) {
|
|
||||||
int args = readu8Chunk(chunk, offset + 1);
|
|
||||||
int nresults = readu8Chunk(chunk, offset + 2);
|
|
||||||
|
|
||||||
printf("%-16s [%03d] [%03d]", name, args, nresults);
|
|
||||||
return offset + 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
// public methods in the cdebug.h header
|
// public methods in the cdebug.h header
|
||||||
|
|
||||||
void disasmChunk(CChunk *chunk, const char *name, int indent) {
|
void disasmChunk(CChunk *chunk, const char *name, int indent) {
|
||||||
@ -91,7 +83,7 @@ int disasmInstr(CChunk *chunk, int offset, int indent) {
|
|||||||
case OP_POP:
|
case OP_POP:
|
||||||
return shortOperandInstruction("OP_POP", chunk, offset);
|
return shortOperandInstruction("OP_POP", chunk, offset);
|
||||||
case OP_CALL:
|
case OP_CALL:
|
||||||
return ABOperandInstruction("OP_CALL", chunk, offset);
|
return shortOperandInstruction("OP_CALL", chunk, offset);
|
||||||
case OP_CLOSURE: {
|
case OP_CLOSURE: {
|
||||||
int index = readu16Chunk(chunk, offset + 1);
|
int index = readu16Chunk(chunk, offset + 1);
|
||||||
printf("%-16s [%05d] - ", "OP_CLOSURE", index);
|
printf("%-16s [%05d] - ", "OP_CLOSURE", index);
|
||||||
@ -153,7 +145,7 @@ int disasmInstr(CChunk *chunk, int offset, int indent) {
|
|||||||
case OP_CONCAT:
|
case OP_CONCAT:
|
||||||
return shortOperandInstruction("OP_CONCAT", chunk, offset);
|
return shortOperandInstruction("OP_CONCAT", chunk, offset);
|
||||||
case OP_RETURN:
|
case OP_RETURN:
|
||||||
return shortOperandInstruction("OP_RETURN", chunk, offset);
|
return simpleInstruction("OP_RETURN", offset);
|
||||||
default:
|
default:
|
||||||
printf("Unknown opcode! [%d]\n", i);
|
printf("Unknown opcode! [%d]\n", i);
|
||||||
exit(0);
|
exit(0);
|
||||||
|
34
src/clex.c
34
src/clex.c
@ -12,6 +12,7 @@ CReservedWord reservedWords[] = {
|
|||||||
{TOKEN_FALSE, "false", 5},
|
{TOKEN_FALSE, "false", 5},
|
||||||
{TOKEN_FOR, "for", 3},
|
{TOKEN_FOR, "for", 3},
|
||||||
{TOKEN_FUNCTION, "function", 8},
|
{TOKEN_FUNCTION, "function", 8},
|
||||||
|
{TOKEN_CLASS, "class", 5},
|
||||||
{TOKEN_IF, "if", 2},
|
{TOKEN_IF, "if", 2},
|
||||||
{TOKEN_LOCAL, "local", 5},
|
{TOKEN_LOCAL, "local", 5},
|
||||||
{TOKEN_NIL, "nil", 3},
|
{TOKEN_NIL, "nil", 3},
|
||||||
@ -21,11 +22,9 @@ CReservedWord reservedWords[] = {
|
|||||||
{TOKEN_THEN, "then", 4},
|
{TOKEN_THEN, "then", 4},
|
||||||
{TOKEN_TRUE, "true", 4},
|
{TOKEN_TRUE, "true", 4},
|
||||||
{TOKEN_VAR, "var", 3},
|
{TOKEN_VAR, "var", 3},
|
||||||
{TOKEN_THIS, "this", 4},
|
|
||||||
{TOKEN_WHILE, "while", 5}
|
{TOKEN_WHILE, "while", 5}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
static CToken makeToken(CLexState *state, CTokenType type) {
|
static CToken makeToken(CLexState *state, CTokenType type) {
|
||||||
CToken token;
|
CToken token;
|
||||||
token.type = type;
|
token.type = type;
|
||||||
@ -49,7 +48,7 @@ static CToken makeError(CLexState *state, const char *msg) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static inline bool isEnd(CLexState *state) {
|
static inline bool isEnd(CLexState *state) {
|
||||||
return state->isEnd;
|
return *state->currentChar == '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool isNumerical(char c) {
|
static inline bool isNumerical(char c) {
|
||||||
@ -103,14 +102,13 @@ void skipWhitespace(CLexState *state) {
|
|||||||
while (true) {
|
while (true) {
|
||||||
char c = peek(state);
|
char c = peek(state);
|
||||||
switch (c) {
|
switch (c) {
|
||||||
|
case '\n': // mark new line
|
||||||
|
state->line++;
|
||||||
case ' ':
|
case ' ':
|
||||||
case '\r':
|
case '\r':
|
||||||
case '\t':
|
case '\t':
|
||||||
next(state); // consume the whitespace
|
next(state); // consume the whitespace
|
||||||
break;
|
break;
|
||||||
case '\n': // mark new line, make the main loop consume it
|
|
||||||
state->line++;
|
|
||||||
return;
|
|
||||||
case '-': // consume comments
|
case '-': // consume comments
|
||||||
if (peekNext(state) == '-') {
|
if (peekNext(state) == '-') {
|
||||||
|
|
||||||
@ -171,8 +169,6 @@ CLexState *cosmoL_newLexState(CState *cstate, const char *source) {
|
|||||||
state->currentChar = (char*)source;
|
state->currentChar = (char*)source;
|
||||||
state->line = 1;
|
state->line = 1;
|
||||||
state->lastLine = 0;
|
state->lastLine = 0;
|
||||||
state->openedBraces = 0;
|
|
||||||
state->isEnd = false;
|
|
||||||
state->lastType = TOKEN_ERROR;
|
state->lastType = TOKEN_ERROR;
|
||||||
|
|
||||||
return state;
|
return state;
|
||||||
@ -195,16 +191,12 @@ _scanTokenEnter:
|
|||||||
|
|
||||||
switch (c) {
|
switch (c) {
|
||||||
// single character tokens
|
// single character tokens
|
||||||
case '(': state->openedBraces++; return makeToken(state, TOKEN_LEFT_PAREN);
|
case '(': return makeToken(state, TOKEN_LEFT_PAREN);
|
||||||
case ')': state->openedBraces--; return makeToken(state, TOKEN_RIGHT_PAREN);
|
case ')': return makeToken(state, TOKEN_RIGHT_PAREN);
|
||||||
case '{': state->openedBraces++; return makeToken(state, TOKEN_LEFT_BRACE);
|
case '{': return makeToken(state, TOKEN_LEFT_BRACE);
|
||||||
case '}': state->openedBraces--; return makeToken(state, TOKEN_RIGHT_BRACE);
|
case '}': return makeToken(state, TOKEN_RIGHT_BRACE);
|
||||||
case '[': state->openedBraces++; return makeToken(state, TOKEN_LEFT_BRACKET);
|
case '[': return makeToken(state, TOKEN_LEFT_BRACKET);
|
||||||
case ']': state->openedBraces--; return makeToken(state, TOKEN_RIGHT_BRACKET);
|
case ']': return makeToken(state, TOKEN_RIGHT_BRACKET);
|
||||||
case '\0':
|
|
||||||
state->isEnd = true;
|
|
||||||
if (state->lastType == TOKEN_EOS)
|
|
||||||
return makeToken(state, TOKEN_EOF);
|
|
||||||
// fall through
|
// fall through
|
||||||
case ';': return makeToken(state, TOKEN_EOS);
|
case ';': return makeToken(state, TOKEN_EOS);
|
||||||
case ',': return makeToken(state, TOKEN_COMMA);
|
case ',': return makeToken(state, TOKEN_COMMA);
|
||||||
@ -212,12 +204,6 @@ _scanTokenEnter:
|
|||||||
case '-': return makeToken(state, TOKEN_MINUS);
|
case '-': return makeToken(state, TOKEN_MINUS);
|
||||||
case '*': return makeToken(state, TOKEN_STAR);
|
case '*': return makeToken(state, TOKEN_STAR);
|
||||||
case '/': return makeToken(state, TOKEN_SLASH);
|
case '/': return makeToken(state, TOKEN_SLASH);
|
||||||
case '\n': { // might be treated like a TOKEN_EOS
|
|
||||||
if (state->openedBraces == 0 && state->lastType != TOKEN_EOS)
|
|
||||||
return makeToken(state, TOKEN_EOS);
|
|
||||||
else // go back to the start
|
|
||||||
goto _scanTokenEnter;
|
|
||||||
}
|
|
||||||
// two character tokens
|
// two character tokens
|
||||||
case '.':
|
case '.':
|
||||||
return match(state, '.') ? makeToken(state, TOKEN_DOT_DOT) : makeToken(state, TOKEN_DOT);
|
return match(state, '.') ? makeToken(state, TOKEN_DOT_DOT) : makeToken(state, TOKEN_DOT);
|
||||||
|
@ -46,6 +46,7 @@ typedef enum {
|
|||||||
TOKEN_END,
|
TOKEN_END,
|
||||||
TOKEN_FOR,
|
TOKEN_FOR,
|
||||||
TOKEN_FUNCTION,
|
TOKEN_FUNCTION,
|
||||||
|
TOKEN_CLASS,
|
||||||
TOKEN_IF,
|
TOKEN_IF,
|
||||||
TOKEN_LOCAL,
|
TOKEN_LOCAL,
|
||||||
TOKEN_NOT,
|
TOKEN_NOT,
|
||||||
@ -53,7 +54,6 @@ typedef enum {
|
|||||||
TOKEN_RETURN,
|
TOKEN_RETURN,
|
||||||
TOKEN_THEN,
|
TOKEN_THEN,
|
||||||
TOKEN_VAR,
|
TOKEN_VAR,
|
||||||
TOKEN_THIS,
|
|
||||||
TOKEN_WHILE,
|
TOKEN_WHILE,
|
||||||
|
|
||||||
TOKEN_ERROR,
|
TOKEN_ERROR,
|
||||||
@ -78,7 +78,6 @@ typedef struct {
|
|||||||
char *startChar;
|
char *startChar;
|
||||||
int line; // current line
|
int line; // current line
|
||||||
int lastLine; // line of the previous consumed token
|
int lastLine; // line of the previous consumed token
|
||||||
int openedBraces; // tracks open [], {}, or ()
|
|
||||||
bool isEnd;
|
bool isEnd;
|
||||||
CTokenType lastType;
|
CTokenType lastType;
|
||||||
} CLexState;
|
} CLexState;
|
||||||
|
11
src/cmem.c
11
src/cmem.c
@ -85,7 +85,9 @@ void blackenObject(CState *state, CObj *obj) {
|
|||||||
break;
|
break;
|
||||||
case COBJ_OBJECT: {
|
case COBJ_OBJECT: {
|
||||||
// mark everything this object is keeping track of
|
// mark everything this object is keeping track of
|
||||||
markTable(state, &((CObjObject*)obj)->tbl);
|
CObjObject *cobj = (CObjObject*)obj;
|
||||||
|
markTable(state, &cobj->tbl);
|
||||||
|
markObject(state, (CObj*)cobj->meta);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case COBJ_UPVALUE: {
|
case COBJ_UPVALUE: {
|
||||||
@ -99,6 +101,12 @@ void blackenObject(CState *state, CObj *obj) {
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case COBJ_METHOD: {
|
||||||
|
CObjMethod *method = (CObjMethod*)obj;
|
||||||
|
markObject(state, (CObj*)method->closure);
|
||||||
|
markObject(state, (CObj*)method->obj);
|
||||||
|
break;
|
||||||
|
}
|
||||||
case COBJ_CLOSURE: {
|
case COBJ_CLOSURE: {
|
||||||
CObjClosure *closure = (CObjClosure*)obj;
|
CObjClosure *closure = (CObjClosure*)obj;
|
||||||
markObject(state, (CObj*)closure->function);
|
markObject(state, (CObj*)closure->function);
|
||||||
@ -200,6 +208,7 @@ void markRoots(CState *state) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
markTable(state, &state->globals);
|
markTable(state, &state->globals);
|
||||||
|
markObject(state, (CObj*)state->initString);
|
||||||
|
|
||||||
traceGrays(state);
|
traceGrays(state);
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
#include "cstate.h"
|
#include "cstate.h"
|
||||||
|
|
||||||
//#define GC_STRESS
|
#define GC_STRESS
|
||||||
//#define GC_DEBUG
|
//#define GC_DEBUG
|
||||||
// arrays will grow by a factor of 2
|
// arrays will grow by a factor of 2
|
||||||
#define GROW_FACTOR 2
|
#define GROW_FACTOR 2
|
||||||
|
53
src/cobj.c
53
src/cobj.c
@ -62,6 +62,10 @@ void cosmoO_free(CState *state, CObj* obj) {
|
|||||||
cosmoM_free(state, CObjCFunction, obj);
|
cosmoM_free(state, CObjCFunction, obj);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case COBJ_METHOD: {
|
||||||
|
cosmoM_free(state, CObjMethod, obj); // we don't own the closure or the object so /shrug
|
||||||
|
break;
|
||||||
|
}
|
||||||
case COBJ_CLOSURE: {
|
case COBJ_CLOSURE: {
|
||||||
CObjClosure* closure = (CObjClosure*)obj;
|
CObjClosure* closure = (CObjClosure*)obj;
|
||||||
cosmoM_freearray(state, CObjUpval*, closure->upvalues, closure->upvalueCount);
|
cosmoM_freearray(state, CObjUpval*, closure->upvalues, closure->upvalueCount);
|
||||||
@ -88,11 +92,14 @@ bool cosmoO_equal(CObj* obj1, CObj* obj2) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CObjObject *cosmoO_newObject(CState *state, int startCap) {
|
CObjObject *cosmoO_newObject(CState *state) {
|
||||||
CObjObject *tbl = (CObjObject*)cosmoO_allocateBase(state, sizeof(CObjObject), COBJ_OBJECT);
|
CObjObject *obj = (CObjObject*)cosmoO_allocateBase(state, sizeof(CObjObject), COBJ_OBJECT);
|
||||||
|
obj->meta = NULL;
|
||||||
|
cosmoV_pushValue(state, cosmoV_newObj(obj)); // so out GC can keep track of it
|
||||||
|
cosmoT_initTable(state, &obj->tbl, ARRAY_START);
|
||||||
|
cosmoV_pop(state);
|
||||||
|
|
||||||
cosmoT_initTable(state, &tbl->tbl, startCap);
|
return obj;
|
||||||
return tbl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CObjFunction *cosmoO_newFunction(CState *state) {
|
CObjFunction *cosmoO_newFunction(CState *state) {
|
||||||
@ -111,6 +118,13 @@ CObjCFunction *cosmoO_newCFunction(CState *state, CosmoCFunction func) {
|
|||||||
return cfunc;
|
return cfunc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CObjMethod *cosmoO_newMethod(CState *state, CObjClosure *func, CObjObject *obj) {
|
||||||
|
CObjMethod *method = (CObjMethod*)cosmoO_allocateBase(state, sizeof(CObjMethod), COBJ_METHOD);
|
||||||
|
method->closure = func;
|
||||||
|
method->obj = obj;
|
||||||
|
return method;
|
||||||
|
}
|
||||||
|
|
||||||
CObjClosure *cosmoO_newClosure(CState *state, CObjFunction *func) {
|
CObjClosure *cosmoO_newClosure(CState *state, CObjFunction *func) {
|
||||||
// intialize array of pointers
|
// intialize array of pointers
|
||||||
CObjUpval **upvalues = cosmoM_xmalloc(state, sizeof(CObjUpval*) * func->upvals);
|
CObjUpval **upvalues = cosmoM_xmalloc(state, sizeof(CObjUpval*) * func->upvals);
|
||||||
@ -180,7 +194,11 @@ CObjString *cosmoO_allocateString(CState *state, const char *str, size_t sz, uin
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool cosmoO_getObject(CState *state, CObjObject *object, CValue key, CValue *val) {
|
bool cosmoO_getObject(CState *state, CObjObject *object, CValue key, CValue *val) {
|
||||||
return cosmoT_get(&object->tbl, key, val);
|
if (!cosmoT_get(&object->tbl, key, val) && object->meta != NULL) { // if the field doesn't exist in the object, check the meta
|
||||||
|
return cosmoO_getObject(state, object->meta, key, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void cosmoO_setObject(CState *state, CObjObject *object, CValue key, CValue val) {
|
void cosmoO_setObject(CState *state, CObjObject *object, CValue key, CValue val) {
|
||||||
@ -188,22 +206,22 @@ void cosmoO_setObject(CState *state, CObjObject *object, CValue key, CValue val)
|
|||||||
*newVal = val;
|
*newVal = val;
|
||||||
}
|
}
|
||||||
|
|
||||||
CObjString *cosmoO_toString(CState *state, CObj *val) {
|
CObjString *cosmoO_toString(CState *state, CObj *obj) {
|
||||||
switch (val->type) {
|
switch (obj->type) {
|
||||||
case COBJ_STRING: {
|
case COBJ_STRING: {
|
||||||
return (CObjString*)val;
|
return (CObjString*)obj;
|
||||||
}
|
}
|
||||||
case COBJ_FUNCTION: {
|
case COBJ_FUNCTION: {
|
||||||
CObjFunction *func = (CObjFunction*)val;
|
CObjFunction *func = (CObjFunction*)obj;
|
||||||
return func->name != NULL ? func->name : cosmoO_copyString(state, UNNAMEDCHUNK, strlen(UNNAMEDCHUNK));
|
return func->name != NULL ? func->name : cosmoO_copyString(state, UNNAMEDCHUNK, strlen(UNNAMEDCHUNK));
|
||||||
}
|
}
|
||||||
case COBJ_OBJECT: { // TODO: maybe not safe??
|
case COBJ_OBJECT: { // TODO: maybe not safe??
|
||||||
char buf[64];
|
char buf[64];
|
||||||
int sz = sprintf(buf, "<obj> %p", val) + 1; // +1 for the null character
|
int sz = sprintf(buf, "<obj> %p", obj) + 1; // +1 for the null character
|
||||||
return cosmoO_copyString(state, buf, sz);
|
return cosmoO_copyString(state, buf, sz);
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return cosmoO_copyString(state, "<unkn>", 6);
|
return cosmoO_copyString(state, "<unkn obj>", 10);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -229,7 +247,7 @@ void printObject(CObj *o) {
|
|||||||
if (objFunc->name != NULL)
|
if (objFunc->name != NULL)
|
||||||
printf("<function> %.*s", objFunc->name->length, objFunc->name->str);
|
printf("<function> %.*s", objFunc->name->length, objFunc->name->str);
|
||||||
else
|
else
|
||||||
printf("<function> _main");
|
printf("<function> %s", UNNAMEDCHUNK);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case COBJ_CFUNCTION: {
|
case COBJ_CFUNCTION: {
|
||||||
@ -237,12 +255,21 @@ void printObject(CObj *o) {
|
|||||||
printf("<c function> %p", objCFunc->cfunc);
|
printf("<c function> %p", objCFunc->cfunc);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case COBJ_METHOD: {
|
||||||
|
CObjMethod *method = (CObjMethod*)o;
|
||||||
|
if (method->closure->function->name != NULL) {
|
||||||
|
printf("<method> %p : %.*s", method->obj, method->closure->function->name->length, method->closure->function->name->str);
|
||||||
|
} else {
|
||||||
|
printf("<method> %p : %s", method->obj, UNNAMEDCHUNK);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
case COBJ_CLOSURE: {
|
case COBJ_CLOSURE: {
|
||||||
CObjClosure *closure = (CObjClosure*)o;
|
CObjClosure *closure = (CObjClosure*)o;
|
||||||
printObject((CObj*)closure->function); // just print the function
|
printObject((CObj*)closure->function); // just print the function
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
printf("<unkn>");
|
printf("<unkn obj>");
|
||||||
}
|
}
|
||||||
}
|
}
|
18
src/cobj.h
18
src/cobj.h
@ -13,14 +13,15 @@ typedef enum {
|
|||||||
COBJ_OBJECT,
|
COBJ_OBJECT,
|
||||||
COBJ_FUNCTION,
|
COBJ_FUNCTION,
|
||||||
COBJ_CFUNCTION,
|
COBJ_CFUNCTION,
|
||||||
|
COBJ_METHOD,
|
||||||
// internal use
|
// internal use
|
||||||
COBJ_CLOSURE,
|
COBJ_CLOSURE,
|
||||||
COBJ_UPVALUE,
|
COBJ_UPVALUE,
|
||||||
} CObjType;
|
} CObjType;
|
||||||
|
|
||||||
#define CommonHeader CObj obj;
|
#define CommonHeader CObj _obj;
|
||||||
|
|
||||||
typedef int (*CosmoCFunction)(CState *state, int argCount, CValue *args);
|
typedef CValue (*CosmoCFunction)(CState *state, int argCount, CValue *args);
|
||||||
|
|
||||||
typedef struct CObj {
|
typedef struct CObj {
|
||||||
CObjType type;
|
CObjType type;
|
||||||
@ -38,7 +39,7 @@ typedef struct CObjString {
|
|||||||
typedef struct CObjObject {
|
typedef struct CObjObject {
|
||||||
CommonHeader; // "is a" CObj
|
CommonHeader; // "is a" CObj
|
||||||
CTable tbl;
|
CTable tbl;
|
||||||
//struct CObjObject *meta; // metaobject, used to describe object behavior
|
struct CObjObject *meta; // metaobject, describes the behavior of the object
|
||||||
} CObjObject;
|
} CObjObject;
|
||||||
|
|
||||||
typedef struct CObjFunction {
|
typedef struct CObjFunction {
|
||||||
@ -61,6 +62,12 @@ typedef struct CObjClosure {
|
|||||||
int upvalueCount;
|
int upvalueCount;
|
||||||
} CObjClosure;
|
} CObjClosure;
|
||||||
|
|
||||||
|
typedef struct CObjMethod {
|
||||||
|
CommonHeader; // "is a " CObj
|
||||||
|
CObjObject *obj; // obj this method is bound too
|
||||||
|
CObjClosure *closure; // TODO: change this to a union to a pointer to a closure object or a c function object
|
||||||
|
} CObjMethod;
|
||||||
|
|
||||||
typedef struct CObjUpval {
|
typedef struct CObjUpval {
|
||||||
CommonHeader; // "is a" CObj
|
CommonHeader; // "is a" CObj
|
||||||
CValue *val;
|
CValue *val;
|
||||||
@ -72,12 +79,14 @@ typedef struct CObjUpval {
|
|||||||
#define IS_TABLE(x) isObjType(x, COBJ_OBJECT)
|
#define IS_TABLE(x) isObjType(x, COBJ_OBJECT)
|
||||||
#define IS_FUNCTION(x) isObjType(x, COBJ_FUNCTION)
|
#define IS_FUNCTION(x) isObjType(x, COBJ_FUNCTION)
|
||||||
#define IS_CFUNCTION(x) isObjType(x, COBJ_CFUNCTION)
|
#define IS_CFUNCTION(x) isObjType(x, COBJ_CFUNCTION)
|
||||||
|
#define IS_METHOD(x) isObjType(x, COBJ_METHOD)
|
||||||
#define IS_CLOSURE(x) isObjType(x, COBJ_CLOSURE)
|
#define IS_CLOSURE(x) isObjType(x, COBJ_CLOSURE)
|
||||||
|
|
||||||
#define cosmoV_readString(x) ((CObjString*)cosmoV_readObj(x))
|
#define cosmoV_readString(x) ((CObjString*)cosmoV_readObj(x))
|
||||||
#define cosmoV_readObject(x) ((CObjObject*)cosmoV_readObj(x))
|
#define cosmoV_readObject(x) ((CObjObject*)cosmoV_readObj(x))
|
||||||
#define cosmoV_readFunction(x) ((CObjFunction*)cosmoV_readObj(x))
|
#define cosmoV_readFunction(x) ((CObjFunction*)cosmoV_readObj(x))
|
||||||
#define cosmoV_readCFunction(x) (((CObjCFunction*)cosmoV_readObj(x))->cfunc)
|
#define cosmoV_readCFunction(x) (((CObjCFunction*)cosmoV_readObj(x))->cfunc)
|
||||||
|
#define cosmoV_readMethod(x) ((CObjMethod*)cosmoV_readObj(x))
|
||||||
#define cosmoV_readClosure(x) ((CObjClosure*)cosmoV_readObj(x))
|
#define cosmoV_readClosure(x) ((CObjClosure*)cosmoV_readObj(x))
|
||||||
|
|
||||||
static inline bool isObjType(CValue val, CObjType type) {
|
static inline bool isObjType(CValue val, CObjType type) {
|
||||||
@ -89,9 +98,10 @@ void cosmoO_free(CState *state, CObj* obj);
|
|||||||
|
|
||||||
bool cosmoO_equal(CObj* obj1, CObj* obj2);
|
bool cosmoO_equal(CObj* obj1, CObj* obj2);
|
||||||
|
|
||||||
CObjObject *cosmoO_newObject(CState *state, int startCap);
|
CObjObject *cosmoO_newObject(CState *state);
|
||||||
CObjFunction *cosmoO_newFunction(CState *state);
|
CObjFunction *cosmoO_newFunction(CState *state);
|
||||||
CObjCFunction *cosmoO_newCFunction(CState *state, CosmoCFunction func);
|
CObjCFunction *cosmoO_newCFunction(CState *state, CosmoCFunction func);
|
||||||
|
CObjMethod *cosmoO_newMethod(CState *state, CObjClosure *func, CObjObject *obj);
|
||||||
CObjClosure *cosmoO_newClosure(CState *state, CObjFunction *func);
|
CObjClosure *cosmoO_newClosure(CState *state, CObjFunction *func);
|
||||||
CObjString *cosmoO_toString(CState *state, CObj *val);
|
CObjString *cosmoO_toString(CState *state, CObj *val);
|
||||||
CObjUpval *cosmoO_newUpvalue(CState *state, CValue *val);
|
CObjUpval *cosmoO_newUpvalue(CState *state, CValue *val);
|
||||||
|
@ -32,6 +32,7 @@ typedef enum {
|
|||||||
OP_NEWOBJECT,
|
OP_NEWOBJECT,
|
||||||
OP_GETOBJECT,
|
OP_GETOBJECT,
|
||||||
OP_SETOBJECT,
|
OP_SETOBJECT,
|
||||||
|
OP_NEWCLASS,
|
||||||
|
|
||||||
// ARITHMETIC
|
// ARITHMETIC
|
||||||
OP_ADD,
|
OP_ADD,
|
||||||
|
@ -18,6 +18,8 @@ typedef struct CObjString CObjString;
|
|||||||
typedef struct CObjUpval CObjUpval;
|
typedef struct CObjUpval CObjUpval;
|
||||||
typedef struct CObjFunction CObjFunction;
|
typedef struct CObjFunction CObjFunction;
|
||||||
typedef struct CObjCFunction CObjCFunction;
|
typedef struct CObjCFunction CObjCFunction;
|
||||||
|
typedef struct CObjMethod CObjMethod;
|
||||||
|
typedef struct CObjClass CObjClass;
|
||||||
typedef struct CObjClosure CObjClosure;
|
typedef struct CObjClosure CObjClosure;
|
||||||
|
|
||||||
typedef uint8_t INSTRUCTION;
|
typedef uint8_t INSTRUCTION;
|
||||||
|
169
src/cparse.c
169
src/cparse.c
@ -9,6 +9,36 @@
|
|||||||
|
|
||||||
// we define all of this here because we only need it in this file, no need for it to be in the header /shrug
|
// we define all of this here because we only need it in this file, no need for it to be in the header /shrug
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
CToken name;
|
||||||
|
int depth;
|
||||||
|
bool isCaptured; // is the Local referenced in an upvalue?
|
||||||
|
} Local;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t index;
|
||||||
|
bool isLocal;
|
||||||
|
} Upvalue;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
FTYPE_FUNCTION,
|
||||||
|
FTYPE_METHOD, // a function bounded to an object (can use "this" identifer to access the current object :pog:)
|
||||||
|
FTYPE_SCRIPT
|
||||||
|
} FunctionType;
|
||||||
|
|
||||||
|
typedef struct CCompilerState {
|
||||||
|
CObjFunction *function;
|
||||||
|
FunctionType type;
|
||||||
|
|
||||||
|
Local locals[256];
|
||||||
|
Upvalue upvalues[256];
|
||||||
|
int localCount;
|
||||||
|
int scopeDepth;
|
||||||
|
int pushedValues;
|
||||||
|
int savedPushed;
|
||||||
|
struct CCompilerState* enclosing;
|
||||||
|
} CCompilerState;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
CLexState *lex;
|
CLexState *lex;
|
||||||
CCompilerState* compiler;
|
CCompilerState* compiler;
|
||||||
@ -50,7 +80,7 @@ static void declaration(CParseState *pstate);
|
|||||||
static void function(CParseState *pstate, FunctionType type);
|
static void function(CParseState *pstate, FunctionType type);
|
||||||
static void expressionStatement(CParseState *pstate);
|
static void expressionStatement(CParseState *pstate);
|
||||||
static ParseRule* getRule(CTokenType type);
|
static ParseRule* getRule(CTokenType type);
|
||||||
static CObjFunction *endCompiler(CParseState *pstate, int results);
|
static CObjFunction *endCompiler(CParseState *pstate);
|
||||||
|
|
||||||
// ================================================================ [FRONT END/TALK TO LEXER] ================================================================
|
// ================================================================ [FRONT END/TALK TO LEXER] ================================================================
|
||||||
|
|
||||||
@ -66,16 +96,15 @@ static void initCompilerState(CParseState* pstate, CCompilerState *ccstate, Func
|
|||||||
ccstate->type = type;
|
ccstate->type = type;
|
||||||
ccstate->function = cosmoO_newFunction(pstate->state);
|
ccstate->function = cosmoO_newFunction(pstate->state);
|
||||||
|
|
||||||
|
|
||||||
if (type != FTYPE_SCRIPT)
|
if (type != FTYPE_SCRIPT)
|
||||||
ccstate->function->name = cosmoO_copyString(pstate->state, pstate->previous.start, pstate->previous.length);
|
ccstate->function->name = cosmoO_copyString(pstate->state, pstate->previous.start, pstate->previous.length);
|
||||||
|
|
||||||
// mark first local slot as used (this'll hold the CObjFunction of the current function)
|
// mark first local slot as used (this'll hold the CObjFunction of the current function, or if it's a method it'll hold the currently bounded object)
|
||||||
Local *local = &ccstate->locals[ccstate->localCount++];
|
Local *local = &ccstate->locals[ccstate->localCount++];
|
||||||
local->depth = 0;
|
local->depth = 0;
|
||||||
local->isCaptured = false;
|
local->isCaptured = false;
|
||||||
local->name.length = 0;
|
|
||||||
local->name.start = "";
|
local->name.start = "";
|
||||||
|
local->name.length = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void initParseState(CParseState *pstate, CCompilerState *ccstate, CState *s, const char *source) {
|
static void initParseState(CParseState *pstate, CCompilerState *ccstate, CState *s, const char *source) {
|
||||||
@ -170,6 +199,16 @@ static void inline valuePopped(CParseState *pstate, int values) {
|
|||||||
pstate->compiler->pushedValues -= values;
|
pstate->compiler->pushedValues -= values;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool blockFollow(CToken token) {
|
||||||
|
switch (token.type) {
|
||||||
|
case TOKEN_END: case TOKEN_ELSE:
|
||||||
|
case TOKEN_ELSEIF: case TOKEN_EOS:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ================================================================ [WRITE TO CHUNK] ================================================================
|
// ================================================================ [WRITE TO CHUNK] ================================================================
|
||||||
|
|
||||||
CChunk* getChunk(CParseState *pstate) {
|
CChunk* getChunk(CParseState *pstate) {
|
||||||
@ -491,22 +530,46 @@ static void call_(CParseState *pstate, bool canAssign) {
|
|||||||
valuePopped(pstate, argCount + 1); // all of these values will be popped off the stack when returned (+1 for the function)
|
valuePopped(pstate, argCount + 1); // all of these values will be popped off the stack when returned (+1 for the function)
|
||||||
writeu8(pstate, OP_CALL);
|
writeu8(pstate, OP_CALL);
|
||||||
writeu8(pstate, argCount);
|
writeu8(pstate, argCount);
|
||||||
|
|
||||||
// hacky hacky hacky hACKY GACJ HACK!!!!!!!!
|
|
||||||
if (pstate->compiler->pushedValues < pstate->compiler->savedPushed) { // there's empty spots on the stack waiting to be filled, lets make OP_CALL fill those spots for us
|
|
||||||
writeu8(pstate, pstate->compiler->savedPushed - pstate->compiler->pushedValues); // number of expected results
|
|
||||||
pstate->compiler->pushedValues = pstate->compiler->savedPushed; // either way the stack will be balanaced after this call
|
|
||||||
} else {
|
|
||||||
writeu8(pstate, 1); // we expect 1 result by default
|
|
||||||
valuePushed(pstate, 1);
|
valuePushed(pstate, 1);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void object(CParseState *pstate, bool canAssign) {
|
static void object(CParseState *pstate, bool canAssign) {
|
||||||
// already consumed the beginning '{'
|
// already consumed the beginning '{'
|
||||||
int entries = 0;
|
int entries = 0;
|
||||||
|
|
||||||
consume(pstate, TOKEN_RIGHT_BRACE, "Expected '}' to end object definition!");
|
if (!match(pstate, TOKEN_RIGHT_BRACE)) {
|
||||||
|
do {
|
||||||
|
if (match(pstate, TOKEN_IDENTIFIER)) {
|
||||||
|
uint16_t fieldIdent = identifierConstant(pstate, &pstate->previous);
|
||||||
|
|
||||||
|
// OP_NEWOBJECT expects the key on the stack before the value
|
||||||
|
writeu8(pstate, OP_LOADCONST);
|
||||||
|
writeu16(pstate, fieldIdent);
|
||||||
|
|
||||||
|
consume(pstate, TOKEN_EQUAL, "Invalid syntax!");
|
||||||
|
|
||||||
|
// parse field
|
||||||
|
expression(pstate);
|
||||||
|
valuePopped(pstate, 1);
|
||||||
|
} else if (match(pstate, TOKEN_LEFT_BRACKET)) {
|
||||||
|
// parse the key first
|
||||||
|
expression(pstate); // should parse until end bracket
|
||||||
|
|
||||||
|
consume(pstate, TOKEN_RIGHT_BRACKET, "Expected ']' to end index definition.");
|
||||||
|
consume(pstate, TOKEN_EQUAL, "Expected '='.");
|
||||||
|
|
||||||
|
// now, parse the value (until comma)
|
||||||
|
expression(pstate);
|
||||||
|
valuePopped(pstate, 2);
|
||||||
|
} else {
|
||||||
|
error(pstate, "Invalid syntax!");
|
||||||
|
}
|
||||||
|
|
||||||
|
entries++;
|
||||||
|
} while (match(pstate, TOKEN_COMMA) && !pstate->hadError);
|
||||||
|
|
||||||
|
consume(pstate, TOKEN_RIGHT_BRACE, "Expected '}' to end object definition.");
|
||||||
|
}
|
||||||
|
|
||||||
writeu8(pstate, OP_NEWOBJECT);
|
writeu8(pstate, OP_NEWOBJECT);
|
||||||
writeu8(pstate, entries);
|
writeu8(pstate, entries);
|
||||||
@ -515,7 +578,7 @@ static void object(CParseState *pstate, bool canAssign) {
|
|||||||
|
|
||||||
static void dot(CParseState *pstate, bool canAssign) {
|
static void dot(CParseState *pstate, bool canAssign) {
|
||||||
consume(pstate, TOKEN_IDENTIFIER, "Expected property name after '.'.");
|
consume(pstate, TOKEN_IDENTIFIER, "Expected property name after '.'.");
|
||||||
uint8_t name = identifierConstant(pstate, &pstate->previous);
|
uint16_t name = identifierConstant(pstate, &pstate->previous);
|
||||||
writeu8(pstate, OP_LOADCONST);
|
writeu8(pstate, OP_LOADCONST);
|
||||||
writeu16(pstate, name);
|
writeu16(pstate, name);
|
||||||
|
|
||||||
@ -579,6 +642,7 @@ ParseRule ruleTable[] = {
|
|||||||
[TOKEN_END] = {NULL, NULL, PREC_NONE},
|
[TOKEN_END] = {NULL, NULL, PREC_NONE},
|
||||||
[TOKEN_FOR] = {NULL, NULL, PREC_NONE},
|
[TOKEN_FOR] = {NULL, NULL, PREC_NONE},
|
||||||
[TOKEN_FUNCTION] = {anonFunction, NULL, PREC_NONE},
|
[TOKEN_FUNCTION] = {anonFunction, NULL, PREC_NONE},
|
||||||
|
[TOKEN_CLASS] = {NULL, NULL, PREC_NONE},
|
||||||
[TOKEN_IF] = {NULL, NULL, PREC_NONE},
|
[TOKEN_IF] = {NULL, NULL, PREC_NONE},
|
||||||
[TOKEN_LOCAL] = {NULL, NULL, PREC_NONE},
|
[TOKEN_LOCAL] = {NULL, NULL, PREC_NONE},
|
||||||
[TOKEN_NOT] = {NULL, NULL, PREC_NONE},
|
[TOKEN_NOT] = {NULL, NULL, PREC_NONE},
|
||||||
@ -588,7 +652,6 @@ ParseRule ruleTable[] = {
|
|||||||
[TOKEN_WHILE] = {NULL, NULL, PREC_NONE},
|
[TOKEN_WHILE] = {NULL, NULL, PREC_NONE},
|
||||||
[TOKEN_ERROR] = {NULL, NULL, PREC_NONE},
|
[TOKEN_ERROR] = {NULL, NULL, PREC_NONE},
|
||||||
[TOKEN_VAR] = {NULL, NULL, PREC_NONE},
|
[TOKEN_VAR] = {NULL, NULL, PREC_NONE},
|
||||||
[TOKEN_THIS] = {NULL, NULL, PREC_NONE},
|
|
||||||
[TOKEN_EOF] = {NULL, NULL, PREC_NONE}
|
[TOKEN_EOF] = {NULL, NULL, PREC_NONE}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -601,9 +664,8 @@ static void parsePrecedence(CParseState *pstate, Precedence prec) {
|
|||||||
|
|
||||||
ParseFunc prefix = getRule(pstate->previous.type)->prefix;
|
ParseFunc prefix = getRule(pstate->previous.type)->prefix;
|
||||||
|
|
||||||
if (prefix == NULL) {
|
if (prefix == NULL)
|
||||||
return error(pstate, "Expected expression!");
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
bool canAssign = prec <= PREC_ASSIGNMENT;
|
bool canAssign = prec <= PREC_ASSIGNMENT;
|
||||||
prefix(pstate, canAssign);
|
prefix(pstate, canAssign);
|
||||||
@ -666,6 +728,33 @@ static void defineVariable(CParseState *pstate, uint16_t global, bool forceLocal
|
|||||||
valuePopped(pstate, 1);
|
valuePopped(pstate, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void _class(CParseState *pstate) {
|
||||||
|
uint16_t var = parseVariable(pstate, "Expected identifer!", false);
|
||||||
|
int entries = 0;
|
||||||
|
|
||||||
|
while (!match(pstate, TOKEN_END) && !match(pstate, TOKEN_EOF) && !pstate->hadError) {
|
||||||
|
if (match(pstate, TOKEN_FUNCTION)) {
|
||||||
|
// define method
|
||||||
|
consume(pstate, TOKEN_IDENTIFIER, "Expected identifier!");
|
||||||
|
uint16_t fieldIdent = identifierConstant(pstate, &pstate->previous);
|
||||||
|
|
||||||
|
// OP_NEWOBJECT expects the key on the stack before the value
|
||||||
|
writeu8(pstate, OP_LOADCONST);
|
||||||
|
writeu16(pstate, fieldIdent);
|
||||||
|
|
||||||
|
function(pstate, FTYPE_METHOD);
|
||||||
|
valuePopped(pstate, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
entries++;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeu8(pstate, OP_NEWOBJECT);
|
||||||
|
writeu8(pstate, entries);
|
||||||
|
valuePushed(pstate, 1);
|
||||||
|
defineVariable(pstate, var, false);
|
||||||
|
}
|
||||||
|
|
||||||
static void popLocals(CParseState *pstate, int toScope) {
|
static void popLocals(CParseState *pstate, int toScope) {
|
||||||
if (pstate->hadError)
|
if (pstate->hadError)
|
||||||
return;
|
return;
|
||||||
@ -708,7 +797,7 @@ static void endScope(CParseState *pstate) {
|
|||||||
|
|
||||||
// parses expressionStatements until a TOKEN_END is consumed
|
// parses expressionStatements until a TOKEN_END is consumed
|
||||||
static void block(CParseState *pstate) {
|
static void block(CParseState *pstate) {
|
||||||
while(!check(pstate, TOKEN_END) && !check(pstate, TOKEN_EOF)) {
|
while(!check(pstate, TOKEN_END) && !check(pstate, TOKEN_EOF) && !check(pstate, TOKEN_ERROR)) {
|
||||||
declaration(pstate);
|
declaration(pstate);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -760,7 +849,7 @@ static void ifStatement(CParseState *pstate) {
|
|||||||
// parse until 'end' or 'else'
|
// parse until 'end' or 'else'
|
||||||
beginScope(pstate);
|
beginScope(pstate);
|
||||||
|
|
||||||
while(!check(pstate, TOKEN_END) && !check(pstate, TOKEN_ELSE) && !check(pstate, TOKEN_ELSEIF) && !check(pstate, TOKEN_EOF)) {
|
while(!check(pstate, TOKEN_END) && !check(pstate, TOKEN_ELSE) && !check(pstate, TOKEN_ELSEIF) && !check(pstate, TOKEN_EOF) && !check(pstate, TOKEN_ERROR)) {
|
||||||
declaration(pstate);
|
declaration(pstate);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -828,7 +917,7 @@ static void function(CParseState *pstate, FunctionType type) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// parse identifier for param (force them to be a local)
|
// parse identifier for param (force them to be a local)
|
||||||
uint8_t funcIdent = parseVariable(pstate, "Expected identifier for function!", true);
|
uint16_t funcIdent = parseVariable(pstate, "Expected identifier for function!", true);
|
||||||
defineVariable(pstate, funcIdent, true);
|
defineVariable(pstate, funcIdent, true);
|
||||||
valuePushed(pstate, 1); // they *will* be populated during runtime
|
valuePushed(pstate, 1); // they *will* be populated during runtime
|
||||||
} while (match(pstate, TOKEN_COMMA));
|
} while (match(pstate, TOKEN_COMMA));
|
||||||
@ -840,7 +929,7 @@ static void function(CParseState *pstate, FunctionType type) {
|
|||||||
alignStack(pstate, savedPushed);
|
alignStack(pstate, savedPushed);
|
||||||
endScope(pstate);
|
endScope(pstate);
|
||||||
|
|
||||||
CObjFunction *objFunc = endCompiler(pstate, 0);
|
CObjFunction *objFunc = endCompiler(pstate);
|
||||||
|
|
||||||
// push closure
|
// push closure
|
||||||
writeu8(pstate, OP_CLOSURE);
|
writeu8(pstate, OP_CLOSURE);
|
||||||
@ -855,7 +944,7 @@ static void function(CParseState *pstate, FunctionType type) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void functionDeclaration(CParseState *pstate) {
|
static void functionDeclaration(CParseState *pstate) {
|
||||||
uint8_t var = parseVariable(pstate, "Expected identifer!", false);
|
uint16_t var = parseVariable(pstate, "Expected identifer!", false);
|
||||||
|
|
||||||
if (pstate->compiler->scopeDepth > 0)
|
if (pstate->compiler->scopeDepth > 0)
|
||||||
markInitialized(pstate, var);
|
markInitialized(pstate, var);
|
||||||
@ -871,27 +960,19 @@ static void returnStatement(CParseState *pstate) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// can return multiple results
|
if (blockFollow(pstate->current)) { // does this return have a value
|
||||||
int results = 0;
|
writeu8(pstate, OP_NIL);
|
||||||
if (!check(pstate, TOKEN_EOS)) { // make sure its not an end of a statement
|
writeu8(pstate, OP_RETURN);
|
||||||
do {
|
|
||||||
expression(pstate);
|
|
||||||
results++;
|
|
||||||
} while (match(pstate, TOKEN_COMMA));
|
|
||||||
|
|
||||||
if (results > UINT8_MAX) {
|
|
||||||
error(pstate, "Too many results returned!");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
expression(pstate);
|
||||||
writeu8(pstate, OP_RETURN);
|
writeu8(pstate, OP_RETURN);
|
||||||
writeu8(pstate, results);
|
valuePopped(pstate, 1);
|
||||||
valuePopped(pstate, results);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void localFunction(CParseState *pstate) {
|
static void localFunction(CParseState *pstate) {
|
||||||
uint8_t var = parseVariable(pstate, "Expected identifer!", true);
|
uint16_t var = parseVariable(pstate, "Expected identifer!", true);
|
||||||
markInitialized(pstate, var);
|
markInitialized(pstate, var);
|
||||||
|
|
||||||
function(pstate, FTYPE_FUNCTION);
|
function(pstate, FTYPE_FUNCTION);
|
||||||
@ -907,6 +988,7 @@ static void forLoop(CParseState *pstate) {
|
|||||||
// parse initalizer
|
// parse initalizer
|
||||||
if (!match(pstate, TOKEN_EOS)) {
|
if (!match(pstate, TOKEN_EOS)) {
|
||||||
expressionStatement(pstate);
|
expressionStatement(pstate);
|
||||||
|
consume(pstate, TOKEN_EOS, "Expected ';' after initalizer!");
|
||||||
}
|
}
|
||||||
|
|
||||||
int loopStart = getChunk(pstate)->count;
|
int loopStart = getChunk(pstate)->count;
|
||||||
@ -985,14 +1067,13 @@ static void expressionStatement(CParseState *pstate) {
|
|||||||
forLoop(pstate);
|
forLoop(pstate);
|
||||||
} else if (match(pstate, TOKEN_FUNCTION)) {
|
} else if (match(pstate, TOKEN_FUNCTION)) {
|
||||||
functionDeclaration(pstate);
|
functionDeclaration(pstate);
|
||||||
|
} else if (match(pstate, TOKEN_CLASS)) {
|
||||||
|
_class(pstate);
|
||||||
} else if (match(pstate, TOKEN_RETURN)) {
|
} else if (match(pstate, TOKEN_RETURN)) {
|
||||||
returnStatement(pstate);
|
returnStatement(pstate);
|
||||||
} else if (check(pstate, TOKEN_EOS)) {
|
|
||||||
// do nothing, just consume it
|
|
||||||
} else {
|
} else {
|
||||||
expression(pstate);
|
expression(pstate);
|
||||||
}
|
}
|
||||||
consume(pstate, TOKEN_EOS, "Expected end of statement after expression.");
|
|
||||||
|
|
||||||
// realign the stack
|
// realign the stack
|
||||||
alignStack(pstate, pstate->compiler->savedPushed);
|
alignStack(pstate, pstate->compiler->savedPushed);
|
||||||
@ -1010,10 +1091,10 @@ static void declaration(CParseState *pstate) {
|
|||||||
synchronize(pstate);
|
synchronize(pstate);
|
||||||
}
|
}
|
||||||
|
|
||||||
static CObjFunction *endCompiler(CParseState *pstate, int results) {
|
static CObjFunction *endCompiler(CParseState *pstate) {
|
||||||
popLocals(pstate, pstate->compiler->scopeDepth); // remove the locals from other scopes
|
popLocals(pstate, pstate->compiler->scopeDepth); // remove the locals from other scopes
|
||||||
|
writeu8(pstate, OP_NIL);
|
||||||
writeu8(pstate, OP_RETURN);
|
writeu8(pstate, OP_RETURN);
|
||||||
writeu8(pstate, results);
|
|
||||||
|
|
||||||
// update pstate to next compiler state
|
// update pstate to next compiler state
|
||||||
CCompilerState *cachedCCState = pstate->compiler;
|
CCompilerState *cachedCCState = pstate->compiler;
|
||||||
@ -1041,7 +1122,7 @@ CObjFunction* cosmoP_compileString(CState *state, const char *source) {
|
|||||||
popLocals(&parser, -1); // needed to close over the values
|
popLocals(&parser, -1); // needed to close over the values
|
||||||
|
|
||||||
if (parser.hadError) { // we don't free the function, the state already has a reference to it in it's linked list of objects!
|
if (parser.hadError) { // we don't free the function, the state already has a reference to it in it's linked list of objects!
|
||||||
endCompiler(&parser, 0);
|
endCompiler(&parser);
|
||||||
freeParseState(&parser);
|
freeParseState(&parser);
|
||||||
|
|
||||||
// the VM still expects a result on the stack TODO: push the error string to the stack
|
// the VM still expects a result on the stack TODO: push the error string to the stack
|
||||||
@ -1054,7 +1135,7 @@ CObjFunction* cosmoP_compileString(CState *state, const char *source) {
|
|||||||
// VM expects the closure on the stack :P (we do this before ending the compiler so our GC doesn't free it)
|
// VM expects the closure on the stack :P (we do this before ending the compiler so our GC doesn't free it)
|
||||||
cosmoV_pushValue(state, cosmoV_newObj((CObj*)cosmoO_newClosure(state, resFunc)));
|
cosmoV_pushValue(state, cosmoV_newObj((CObj*)cosmoO_newClosure(state, resFunc)));
|
||||||
|
|
||||||
endCompiler(&parser, 0);
|
endCompiler(&parser);
|
||||||
freeParseState(&parser);
|
freeParseState(&parser);
|
||||||
cosmoM_unfreezeGC(state);
|
cosmoM_unfreezeGC(state);
|
||||||
return resFunc;
|
return resFunc;
|
||||||
|
29
src/cparse.h
29
src/cparse.h
@ -4,35 +4,6 @@
|
|||||||
#include "cosmo.h"
|
#include "cosmo.h"
|
||||||
#include "clex.h"
|
#include "clex.h"
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
CToken name;
|
|
||||||
int depth;
|
|
||||||
bool isCaptured; // is the Local referenced in an upvalue?
|
|
||||||
} Local;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
uint8_t index;
|
|
||||||
bool isLocal;
|
|
||||||
} Upvalue;
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
FTYPE_FUNCTION,
|
|
||||||
FTYPE_SCRIPT
|
|
||||||
} FunctionType;
|
|
||||||
|
|
||||||
typedef struct CCompilerState {
|
|
||||||
CObjFunction *function;
|
|
||||||
FunctionType type;
|
|
||||||
|
|
||||||
Local locals[256];
|
|
||||||
Upvalue upvalues[256];
|
|
||||||
int localCount;
|
|
||||||
int scopeDepth;
|
|
||||||
int pushedValues;
|
|
||||||
int savedPushed;
|
|
||||||
struct CCompilerState* enclosing;
|
|
||||||
} CCompilerState;
|
|
||||||
|
|
||||||
// compiles source into CChunk, if NULL is returned, a syntaxical error has occured and pushed onto the stack
|
// compiles source into CChunk, if NULL is returned, a syntaxical error has occured and pushed onto the stack
|
||||||
CObjFunction* cosmoP_compileString(CState *state, const char *source);
|
CObjFunction* cosmoP_compileString(CState *state, const char *source);
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
#include "cchunk.h"
|
#include "cchunk.h"
|
||||||
#include "cobj.h"
|
#include "cobj.h"
|
||||||
#include "cvm.h"
|
#include "cvm.h"
|
||||||
|
#include "cmem.h"
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
@ -32,10 +33,16 @@ CState *cosmoV_newState() {
|
|||||||
|
|
||||||
cosmoT_initTable(state, &state->strings, 8); // init string table
|
cosmoT_initTable(state, &state->strings, 8); // init string table
|
||||||
cosmoT_initTable(state, &state->globals, 8); // init global table
|
cosmoT_initTable(state, &state->globals, 8); // init global table
|
||||||
|
|
||||||
|
state->initString = NULL;
|
||||||
|
state->initString = cosmoO_copyString(state, "__init", 6);
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
void cosmoV_freeState(CState *state) {
|
void cosmoV_freeState(CState *state) {
|
||||||
|
#ifdef GC_DEBUG
|
||||||
|
printf("state %p is being free'd!\n", state);
|
||||||
|
#endif
|
||||||
// frees all the objects
|
// frees all the objects
|
||||||
CObj *objs = state->objects;
|
CObj *objs = state->objects;
|
||||||
while (objs != NULL) {
|
while (objs != NULL) {
|
||||||
@ -45,6 +52,7 @@ void cosmoV_freeState(CState *state) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// free our string & global table
|
// free our string & global table
|
||||||
|
state->initString = NULL;
|
||||||
cosmoT_clearTable(state, &state->strings);
|
cosmoT_clearTable(state, &state->strings);
|
||||||
cosmoT_clearTable(state, &state->globals);
|
cosmoT_clearTable(state, &state->globals);
|
||||||
|
|
||||||
|
@ -6,8 +6,6 @@
|
|||||||
#include "cobj.h"
|
#include "cobj.h"
|
||||||
#include "ctable.h"
|
#include "ctable.h"
|
||||||
|
|
||||||
typedef struct CCompilerState CCompilerState;
|
|
||||||
|
|
||||||
typedef struct CCallFrame {
|
typedef struct CCallFrame {
|
||||||
CObjClosure *closure;
|
CObjClosure *closure;
|
||||||
INSTRUCTION *pc;
|
INSTRUCTION *pc;
|
||||||
@ -32,6 +30,8 @@ typedef struct CState {
|
|||||||
CValue stack[STACK_MAX]; // stack
|
CValue stack[STACK_MAX]; // stack
|
||||||
CCallFrame callFrame[FRAME_MAX]; // call frames
|
CCallFrame callFrame[FRAME_MAX]; // call frames
|
||||||
int frameCount;
|
int frameCount;
|
||||||
|
|
||||||
|
CObjString *initString;
|
||||||
} CState;
|
} CState;
|
||||||
|
|
||||||
COSMO_API CState *cosmoV_newState();
|
COSMO_API CState *cosmoV_newState();
|
||||||
|
@ -46,8 +46,11 @@ COSMO_API CObjString *cosmoV_toString(CState *state, CValue val) {
|
|||||||
case COSMO_TOBJ: {
|
case COSMO_TOBJ: {
|
||||||
return cosmoO_toString(state, val.val.obj);
|
return cosmoO_toString(state, val.val.obj);
|
||||||
}
|
}
|
||||||
|
case COSMO_TNIL: {
|
||||||
|
return cosmoO_copyString(state, "nil", 3);
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return cosmoO_copyString(state, "<unkn>", 6);
|
return cosmoO_copyString(state, "<unkn val>", 10);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,6 +70,6 @@ void printValue(CValue val) {
|
|||||||
printf("nil");
|
printf("nil");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
printf("<unkn>");
|
printf("<unkn val>");
|
||||||
}
|
}
|
||||||
}
|
}
|
136
src/cvm.c
136
src/cvm.c
@ -105,19 +105,40 @@ CObjString *cosmoV_concat(CState *state, CObjString *strA, CObjString *strB) {
|
|||||||
return cosmoO_takeString(state, buf, sz);
|
return cosmoO_takeString(state, buf, sz);
|
||||||
}
|
}
|
||||||
|
|
||||||
int cosmoV_execute(CState *state);
|
bool cosmoV_execute(CState *state);
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
CALL_CLOSURE,
|
CALL_CLOSURE,
|
||||||
CALL_CFUNCTION
|
CALL_CFUNCTION
|
||||||
} preCallResult;
|
} preCallResult;
|
||||||
|
|
||||||
int cosmoV_preCall(CState *state, int args, int nresults) {
|
bool call(CState *state, CObjClosure *closure, int args) {
|
||||||
return -1;
|
// missmatched args, thats an obvious user error, so error.
|
||||||
|
if (args != closure->function->args) {
|
||||||
|
runtimeError(state, "Expected %d parameters for %s, got %d!", closure->function->args, closure->function->name == NULL ? UNNAMEDCHUNK : closure->function->name->str, args);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// load function into callframe
|
||||||
|
pushCallFrame(state, closure, closure->function->args);
|
||||||
|
|
||||||
|
// execute
|
||||||
|
if (!cosmoV_execute(state))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// remember where the return value is
|
||||||
|
CValue* result = cosmoV_getTop(state, 0);
|
||||||
|
|
||||||
|
// pop the callframe and return result :)
|
||||||
|
popCallFrame(state);
|
||||||
|
|
||||||
|
// push the return value back onto the stack
|
||||||
|
cosmoV_pushValue(state, *result);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// args = # of pass parameters, nresults = # of expected results
|
// args = # of pass parameters
|
||||||
COSMOVMRESULT cosmoV_call(CState *state, int args, int nresults) {
|
COSMOVMRESULT cosmoV_call(CState *state, int args) {
|
||||||
StkPtr val = cosmoV_getTop(state, args); // function will always be right above the args
|
StkPtr val = cosmoV_getTop(state, args); // function will always be right above the args
|
||||||
|
|
||||||
if (!(val->type == COSMO_TOBJ)) {
|
if (!(val->type == COSMO_TOBJ)) {
|
||||||
@ -128,9 +149,18 @@ COSMOVMRESULT cosmoV_call(CState *state, int args, int nresults) {
|
|||||||
switch (val->val.obj->type) {
|
switch (val->val.obj->type) {
|
||||||
case COBJ_CLOSURE: {
|
case COBJ_CLOSURE: {
|
||||||
CObjClosure *closure = (CObjClosure*)(val->val.obj);
|
CObjClosure *closure = (CObjClosure*)(val->val.obj);
|
||||||
|
if (!call(state, closure, args)) {
|
||||||
|
return COSMOVM_RUNTIME_ERR;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case COBJ_METHOD: {
|
||||||
|
CObjMethod *method = (CObjMethod*)val->val.obj;
|
||||||
|
*val = cosmoV_newObj(method->obj); // sets the object on the stack so the function can reference it in it's first argument
|
||||||
|
|
||||||
// missmatched args, thats an obvious user error, so error.
|
CObjClosure *closure = method->closure;
|
||||||
if (args != closure->function->args) {
|
|
||||||
|
if (args+1 != closure->function->args) {
|
||||||
runtimeError(state, "Expected %d parameters for %s, got %d!", closure->function->args, closure->function->name == NULL ? UNNAMEDCHUNK : closure->function->name->str, args);
|
runtimeError(state, "Expected %d parameters for %s, got %d!", closure->function->args, closure->function->name == NULL ? UNNAMEDCHUNK : closure->function->name->str, args);
|
||||||
return COSMOVM_RUNTIME_ERR;
|
return COSMOVM_RUNTIME_ERR;
|
||||||
}
|
}
|
||||||
@ -139,22 +169,53 @@ COSMOVMRESULT cosmoV_call(CState *state, int args, int nresults) {
|
|||||||
pushCallFrame(state, closure, closure->function->args);
|
pushCallFrame(state, closure, closure->function->args);
|
||||||
|
|
||||||
// execute
|
// execute
|
||||||
int res = cosmoV_execute(state);
|
if (!cosmoV_execute(state))
|
||||||
|
return COSMOVM_RUNTIME_ERR;
|
||||||
|
CValue* result = state->top;
|
||||||
|
|
||||||
// so, since we can have any # of results, we need to move the expected results to the original call frame (that means popping/adding however many results)
|
// pop the callframe and return result
|
||||||
CValue* results = state->top;
|
|
||||||
|
|
||||||
// pop the callframe and return result :)
|
|
||||||
popCallFrame(state);
|
popCallFrame(state);
|
||||||
|
state->top++; // adjust stack back into place
|
||||||
|
cosmoV_pushValue(state, *result); // and push return value
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case COBJ_OBJECT: {
|
||||||
|
CObjObject *metaObj = (CObjObject*)val->val.obj;
|
||||||
|
CObjObject *newObj = cosmoO_newObject(state);
|
||||||
|
newObj->meta = metaObj;
|
||||||
|
*val = cosmoV_newObj(newObj);
|
||||||
|
CValue ret;
|
||||||
|
|
||||||
// return the results to the stack
|
// check if they defined an initalizer
|
||||||
for (int i = 1; i <= nresults; i++) {
|
if (cosmoO_getObject(state, metaObj, cosmoV_newObj(state->initString), &ret) && IS_CLOSURE(ret)) {
|
||||||
if (i <= res)
|
CObjClosure *closure = cosmoV_readClosure(ret);
|
||||||
cosmoV_pushValue(state, results[-i]);
|
|
||||||
else
|
if (args+1 != closure->function->args) {
|
||||||
cosmoV_pushValue(state, cosmoV_newNil());
|
runtimeError(state, "Expected %d parameters for %s, got %d!", closure->function->args, closure->function->name == NULL ? UNNAMEDCHUNK : closure->function->name->str, args);
|
||||||
|
return COSMOVM_RUNTIME_ERR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// load function into callframe
|
||||||
|
pushCallFrame(state, closure, closure->function->args);
|
||||||
|
|
||||||
|
// execute
|
||||||
|
if (!cosmoV_execute(state))
|
||||||
|
return COSMOVM_RUNTIME_ERR;
|
||||||
|
|
||||||
|
// we throw away the return result, it's unused
|
||||||
|
// pop the callframe and return result :)
|
||||||
|
popCallFrame(state);
|
||||||
|
state->top++; // adjust stack back into place
|
||||||
|
} else {
|
||||||
|
// no default initalizer
|
||||||
|
if (args != 0) {
|
||||||
|
runtimeError(state, "Expected 0 parameters, got %d!", args);
|
||||||
|
return COSMOVM_RUNTIME_ERR;
|
||||||
|
}
|
||||||
|
state->top--;
|
||||||
|
}
|
||||||
|
|
||||||
|
cosmoV_pushValue(state, cosmoV_newObj(newObj));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case COBJ_CFUNCTION: {
|
case COBJ_CFUNCTION: {
|
||||||
@ -163,21 +224,11 @@ COSMOVMRESULT cosmoV_call(CState *state, int args, int nresults) {
|
|||||||
CValue *savedBase = state->top - args - 1;
|
CValue *savedBase = state->top - args - 1;
|
||||||
|
|
||||||
cosmoM_freezeGC(state); // we don't want a GC event during c api because we don't actually trust the user to know how to evade the GC
|
cosmoM_freezeGC(state); // we don't want a GC event during c api because we don't actually trust the user to know how to evade the GC
|
||||||
int res = cfunc(state, args, state->top - args);
|
CValue res = cfunc(state, args, state->top - args);
|
||||||
cosmoM_unfreezeGC(state);
|
cosmoM_unfreezeGC(state);
|
||||||
|
|
||||||
// so, since we can have any # of results, we need to move the expected results to the original call frame
|
|
||||||
CValue* results = state->top;
|
|
||||||
state->top = savedBase;
|
state->top = savedBase;
|
||||||
|
cosmoV_pushValue(state, res);
|
||||||
// return the results to the stack
|
|
||||||
for (int i = 1; i <= nresults; i++) {
|
|
||||||
if (i <= res)
|
|
||||||
cosmoV_pushValue(state, results[-i]);
|
|
||||||
else
|
|
||||||
cosmoV_pushValue(state, cosmoV_newNil());
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@ -199,12 +250,11 @@ static inline bool isFalsey(StkPtr val) {
|
|||||||
cosmoV_setTop(state, 2); /* pop the 2 values */ \
|
cosmoV_setTop(state, 2); /* pop the 2 values */ \
|
||||||
cosmoV_pushValue(state, typeConst((valA->val.num) op (valB->val.num))); \
|
cosmoV_pushValue(state, typeConst((valA->val.num) op (valB->val.num))); \
|
||||||
} else { \
|
} else { \
|
||||||
runtimeError(state, "Expected number! got %d and %d", valA->type, valB->type); \
|
runtimeError(state, "Expected number!"); \
|
||||||
} \
|
} \
|
||||||
|
|
||||||
|
// returns false if panic
|
||||||
// returns -1 if error, otherwise returns ammount of results
|
bool cosmoV_execute(CState *state) {
|
||||||
int cosmoV_execute(CState *state) {
|
|
||||||
CCallFrame* frame = &state->callFrame[state->frameCount - 1]; // grabs the current frame
|
CCallFrame* frame = &state->callFrame[state->frameCount - 1]; // grabs the current frame
|
||||||
CValue *constants = frame->closure->function->chunk.constants.values; // cache the pointer :)
|
CValue *constants = frame->closure->function->chunk.constants.values; // cache the pointer :)
|
||||||
|
|
||||||
@ -285,8 +335,7 @@ int cosmoV_execute(CState *state) {
|
|||||||
}
|
}
|
||||||
case OP_CALL: {
|
case OP_CALL: {
|
||||||
uint8_t args = READBYTE();
|
uint8_t args = READBYTE();
|
||||||
uint8_t results = READBYTE();
|
COSMOVMRESULT result = cosmoV_call(state, args);
|
||||||
COSMOVMRESULT result = cosmoV_call(state, args, results);
|
|
||||||
if (result != COSMOVM_OK) {
|
if (result != COSMOVM_OK) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -320,12 +369,12 @@ int cosmoV_execute(CState *state) {
|
|||||||
case OP_NEWOBJECT: {
|
case OP_NEWOBJECT: {
|
||||||
uint8_t entries = READBYTE();
|
uint8_t entries = READBYTE();
|
||||||
StkPtr key, val;
|
StkPtr key, val;
|
||||||
CObjObject *newObj = cosmoO_newObject(state, entries * 3); // start the table with enough space to hopefully prevent reallocation since that's costly
|
CObjObject *newObj = cosmoO_newObject(state); // start the table with enough space to hopefully prevent reallocation since that's costly
|
||||||
cosmoV_pushValue(state, cosmoV_newObj(newObj)); // so our GC doesn't free our new object
|
cosmoV_pushValue(state, cosmoV_newObj(newObj)); // so our GC doesn't free our new object
|
||||||
|
|
||||||
for (int i = 0; i < entries; i++) {
|
for (int i = 0; i < entries; i++) {
|
||||||
val = cosmoV_getTop(state, (i*2) + 2);
|
val = cosmoV_getTop(state, (i*2) + 1);
|
||||||
key = cosmoV_getTop(state, (i*2) + 1);
|
key = cosmoV_getTop(state, (i*2) + 2);
|
||||||
|
|
||||||
// set key/value pair
|
// set key/value pair
|
||||||
CValue *newVal = cosmoT_insert(state, &newObj->tbl, *key);
|
CValue *newVal = cosmoT_insert(state, &newObj->tbl, *key);
|
||||||
@ -351,6 +400,10 @@ int cosmoV_execute(CState *state) {
|
|||||||
CValue val; // to hold our value
|
CValue val; // to hold our value
|
||||||
|
|
||||||
cosmoO_getObject(state, object, *key, &val);
|
cosmoO_getObject(state, object, *key, &val);
|
||||||
|
if (IS_CLOSURE(val)) { // is it a function? if so, make it a method to the current object
|
||||||
|
CObjMethod *method = cosmoO_newMethod(state, cosmoV_readClosure(val), object);
|
||||||
|
val = cosmoV_newObj(method);
|
||||||
|
}
|
||||||
cosmoV_setTop(state, 2); // pops the object & the key
|
cosmoV_setTop(state, 2); // pops the object & the key
|
||||||
cosmoV_pushValue(state, val); // pushes the field result
|
cosmoV_pushValue(state, val); // pushes the field result
|
||||||
break;
|
break;
|
||||||
@ -453,8 +506,7 @@ int cosmoV_execute(CState *state) {
|
|||||||
case OP_FALSE: cosmoV_pushValue(state, cosmoV_newBoolean(false)); break;
|
case OP_FALSE: cosmoV_pushValue(state, cosmoV_newBoolean(false)); break;
|
||||||
case OP_NIL: cosmoV_pushValue(state, cosmoV_newNil()); break;
|
case OP_NIL: cosmoV_pushValue(state, cosmoV_newNil()); break;
|
||||||
case OP_RETURN: {
|
case OP_RETURN: {
|
||||||
uint8_t results = READBYTE();
|
return true;
|
||||||
return results;
|
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
CERROR("unknown opcode!");
|
CERROR("unknown opcode!");
|
||||||
@ -467,7 +519,7 @@ int cosmoV_execute(CState *state) {
|
|||||||
#undef READUINT
|
#undef READUINT
|
||||||
|
|
||||||
// we'll only reach this is state->panic is true
|
// we'll only reach this is state->panic is true
|
||||||
return COSMOVM_RUNTIME_ERR;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef BINARYOP
|
#undef BINARYOP
|
@ -11,6 +11,6 @@ typedef enum {
|
|||||||
} COSMOVMRESULT;
|
} COSMOVMRESULT;
|
||||||
|
|
||||||
// args = # of pass parameters, nresults = # of expected results
|
// args = # of pass parameters, nresults = # of expected results
|
||||||
COSMO_API COSMOVMRESULT cosmoV_call(CState *state, int args, int nresults);
|
COSMO_API COSMOVMRESULT cosmoV_call(CState *state, int args);
|
||||||
|
|
||||||
#endif
|
#endif
|
13
src/main.c
13
src/main.c
@ -7,12 +7,12 @@
|
|||||||
|
|
||||||
#include "cmem.h"
|
#include "cmem.h"
|
||||||
|
|
||||||
static bool _ACTIVE;
|
static bool _ACTIVE = false;
|
||||||
|
|
||||||
int cosmoB_quitRepl(CState *state, int nargs, CValue *args) {
|
CValue cosmoB_quitRepl(CState *state, int nargs, CValue *args) {
|
||||||
_ACTIVE = false;
|
_ACTIVE = false;
|
||||||
|
|
||||||
return 0; // we don't do anything to the stack
|
return cosmoV_newNil(); // we don't return anything
|
||||||
}
|
}
|
||||||
|
|
||||||
static void interpret(CState *state, const char* script) {
|
static void interpret(CState *state, const char* script) {
|
||||||
@ -22,11 +22,10 @@ static void interpret(CState *state, const char* script) {
|
|||||||
if (func != NULL) {
|
if (func != NULL) {
|
||||||
disasmChunk(&func->chunk, "_main", 0);
|
disasmChunk(&func->chunk, "_main", 0);
|
||||||
|
|
||||||
cosmoV_call(state, 0, 0); // 0 args being passed, 0 results expected
|
COSMOVMRESULT res = cosmoV_call(state, 0); // 0 args being passed
|
||||||
|
|
||||||
//cosmoV_printStack(state);
|
if (res == COSMOVM_RUNTIME_ERR)
|
||||||
//cosmoT_printTable(&state->globals, "globals");
|
state->panic = false; // so our repl isn't broken
|
||||||
//cosmoT_printTable(&state->strings, "strings");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
27
test.cosmo
27
test.cosmo
@ -1,7 +1,26 @@
|
|||||||
var test = {}
|
class test
|
||||||
|
function __init(self, str)
|
||||||
|
self.hello = str
|
||||||
|
end
|
||||||
|
|
||||||
if !test.hello then
|
function print(self, i)
|
||||||
test.hello = "hello world!"
|
local str = self.hello
|
||||||
|
|
||||||
|
for (var x = i; x > 0; x=x-1) do
|
||||||
|
str = str .. "!"
|
||||||
|
end
|
||||||
|
|
||||||
|
print(str)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
print(test.hello .. "!!!!")
|
var obj = test("Hello world")
|
||||||
|
for (var i = 1; i <= 10; i=i+1) do
|
||||||
|
obj.print(i)
|
||||||
|
end
|
||||||
|
|
||||||
|
test.debug = function(self)
|
||||||
|
print("hi from " .. self)
|
||||||
|
end
|
||||||
|
|
||||||
|
obj.debug()
|
Loading…
Reference in New Issue
Block a user