mirror of
https://github.com/CPunch/Cosmo.git
synced 2024-11-21 23: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",
|
||||
"type_traits": "cpp",
|
||||
"utility": "cpp",
|
||||
"vector": "cpp"
|
||||
"vector": "cpp",
|
||||
"typeinfo": "c"
|
||||
}
|
||||
}
|
@ -9,13 +9,12 @@ void cosmoB_loadlibrary(CState *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++) {
|
||||
CObjString *str = cosmoV_toString(state, args[i]);
|
||||
printf("%s", cosmoO_readCString(str));
|
||||
}
|
||||
|
||||
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"
|
||||
|
||||
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
|
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
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
void disasmChunk(CChunk *chunk, const char *name, int indent) {
|
||||
@ -91,7 +83,7 @@ int disasmInstr(CChunk *chunk, int offset, int indent) {
|
||||
case OP_POP:
|
||||
return shortOperandInstruction("OP_POP", chunk, offset);
|
||||
case OP_CALL:
|
||||
return ABOperandInstruction("OP_CALL", chunk, offset);
|
||||
return shortOperandInstruction("OP_CALL", chunk, offset);
|
||||
case OP_CLOSURE: {
|
||||
int index = readu16Chunk(chunk, offset + 1);
|
||||
printf("%-16s [%05d] - ", "OP_CLOSURE", index);
|
||||
@ -153,7 +145,7 @@ int disasmInstr(CChunk *chunk, int offset, int indent) {
|
||||
case OP_CONCAT:
|
||||
return shortOperandInstruction("OP_CONCAT", chunk, offset);
|
||||
case OP_RETURN:
|
||||
return shortOperandInstruction("OP_RETURN", chunk, offset);
|
||||
return simpleInstruction("OP_RETURN", offset);
|
||||
default:
|
||||
printf("Unknown opcode! [%d]\n", i);
|
||||
exit(0);
|
||||
|
34
src/clex.c
34
src/clex.c
@ -12,6 +12,7 @@ CReservedWord reservedWords[] = {
|
||||
{TOKEN_FALSE, "false", 5},
|
||||
{TOKEN_FOR, "for", 3},
|
||||
{TOKEN_FUNCTION, "function", 8},
|
||||
{TOKEN_CLASS, "class", 5},
|
||||
{TOKEN_IF, "if", 2},
|
||||
{TOKEN_LOCAL, "local", 5},
|
||||
{TOKEN_NIL, "nil", 3},
|
||||
@ -21,11 +22,9 @@ CReservedWord reservedWords[] = {
|
||||
{TOKEN_THEN, "then", 4},
|
||||
{TOKEN_TRUE, "true", 4},
|
||||
{TOKEN_VAR, "var", 3},
|
||||
{TOKEN_THIS, "this", 4},
|
||||
{TOKEN_WHILE, "while", 5}
|
||||
};
|
||||
|
||||
|
||||
static CToken makeToken(CLexState *state, CTokenType type) {
|
||||
CToken token;
|
||||
token.type = type;
|
||||
@ -49,7 +48,7 @@ static CToken makeError(CLexState *state, const char *msg) {
|
||||
}
|
||||
|
||||
static inline bool isEnd(CLexState *state) {
|
||||
return state->isEnd;
|
||||
return *state->currentChar == '\0';
|
||||
}
|
||||
|
||||
static inline bool isNumerical(char c) {
|
||||
@ -103,14 +102,13 @@ void skipWhitespace(CLexState *state) {
|
||||
while (true) {
|
||||
char c = peek(state);
|
||||
switch (c) {
|
||||
case '\n': // mark new line
|
||||
state->line++;
|
||||
case ' ':
|
||||
case '\r':
|
||||
case '\t':
|
||||
next(state); // consume the whitespace
|
||||
break;
|
||||
case '\n': // mark new line, make the main loop consume it
|
||||
state->line++;
|
||||
return;
|
||||
case '-': // consume comments
|
||||
if (peekNext(state) == '-') {
|
||||
|
||||
@ -171,8 +169,6 @@ CLexState *cosmoL_newLexState(CState *cstate, const char *source) {
|
||||
state->currentChar = (char*)source;
|
||||
state->line = 1;
|
||||
state->lastLine = 0;
|
||||
state->openedBraces = 0;
|
||||
state->isEnd = false;
|
||||
state->lastType = TOKEN_ERROR;
|
||||
|
||||
return state;
|
||||
@ -195,16 +191,12 @@ _scanTokenEnter:
|
||||
|
||||
switch (c) {
|
||||
// single character tokens
|
||||
case '(': state->openedBraces++; return makeToken(state, TOKEN_LEFT_PAREN);
|
||||
case ')': state->openedBraces--; return makeToken(state, TOKEN_RIGHT_PAREN);
|
||||
case '{': state->openedBraces++; return makeToken(state, TOKEN_LEFT_BRACE);
|
||||
case '}': state->openedBraces--; return makeToken(state, TOKEN_RIGHT_BRACE);
|
||||
case '[': state->openedBraces++; return makeToken(state, TOKEN_LEFT_BRACKET);
|
||||
case ']': state->openedBraces--; return makeToken(state, TOKEN_RIGHT_BRACKET);
|
||||
case '\0':
|
||||
state->isEnd = true;
|
||||
if (state->lastType == TOKEN_EOS)
|
||||
return makeToken(state, TOKEN_EOF);
|
||||
case '(': return makeToken(state, TOKEN_LEFT_PAREN);
|
||||
case ')': return makeToken(state, TOKEN_RIGHT_PAREN);
|
||||
case '{': return makeToken(state, TOKEN_LEFT_BRACE);
|
||||
case '}': return makeToken(state, TOKEN_RIGHT_BRACE);
|
||||
case '[': return makeToken(state, TOKEN_LEFT_BRACKET);
|
||||
case ']': return makeToken(state, TOKEN_RIGHT_BRACKET);
|
||||
// fall through
|
||||
case ';': return makeToken(state, TOKEN_EOS);
|
||||
case ',': return makeToken(state, TOKEN_COMMA);
|
||||
@ -212,12 +204,6 @@ _scanTokenEnter:
|
||||
case '-': return makeToken(state, TOKEN_MINUS);
|
||||
case '*': return makeToken(state, TOKEN_STAR);
|
||||
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
|
||||
case '.':
|
||||
return match(state, '.') ? makeToken(state, TOKEN_DOT_DOT) : makeToken(state, TOKEN_DOT);
|
||||
|
@ -46,6 +46,7 @@ typedef enum {
|
||||
TOKEN_END,
|
||||
TOKEN_FOR,
|
||||
TOKEN_FUNCTION,
|
||||
TOKEN_CLASS,
|
||||
TOKEN_IF,
|
||||
TOKEN_LOCAL,
|
||||
TOKEN_NOT,
|
||||
@ -53,7 +54,6 @@ typedef enum {
|
||||
TOKEN_RETURN,
|
||||
TOKEN_THEN,
|
||||
TOKEN_VAR,
|
||||
TOKEN_THIS,
|
||||
TOKEN_WHILE,
|
||||
|
||||
TOKEN_ERROR,
|
||||
@ -78,7 +78,6 @@ typedef struct {
|
||||
char *startChar;
|
||||
int line; // current line
|
||||
int lastLine; // line of the previous consumed token
|
||||
int openedBraces; // tracks open [], {}, or ()
|
||||
bool isEnd;
|
||||
CTokenType lastType;
|
||||
} CLexState;
|
||||
|
11
src/cmem.c
11
src/cmem.c
@ -85,7 +85,9 @@ void blackenObject(CState *state, CObj *obj) {
|
||||
break;
|
||||
case COBJ_OBJECT: {
|
||||
// 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;
|
||||
}
|
||||
case COBJ_UPVALUE: {
|
||||
@ -99,6 +101,12 @@ void blackenObject(CState *state, CObj *obj) {
|
||||
|
||||
break;
|
||||
}
|
||||
case COBJ_METHOD: {
|
||||
CObjMethod *method = (CObjMethod*)obj;
|
||||
markObject(state, (CObj*)method->closure);
|
||||
markObject(state, (CObj*)method->obj);
|
||||
break;
|
||||
}
|
||||
case COBJ_CLOSURE: {
|
||||
CObjClosure *closure = (CObjClosure*)obj;
|
||||
markObject(state, (CObj*)closure->function);
|
||||
@ -200,6 +208,7 @@ void markRoots(CState *state) {
|
||||
}
|
||||
|
||||
markTable(state, &state->globals);
|
||||
markObject(state, (CObj*)state->initString);
|
||||
|
||||
traceGrays(state);
|
||||
}
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
#include "cstate.h"
|
||||
|
||||
//#define GC_STRESS
|
||||
#define GC_STRESS
|
||||
//#define GC_DEBUG
|
||||
// arrays will grow by a factor of 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);
|
||||
break;
|
||||
}
|
||||
case COBJ_METHOD: {
|
||||
cosmoM_free(state, CObjMethod, obj); // we don't own the closure or the object so /shrug
|
||||
break;
|
||||
}
|
||||
case COBJ_CLOSURE: {
|
||||
CObjClosure* closure = (CObjClosure*)obj;
|
||||
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 *tbl = (CObjObject*)cosmoO_allocateBase(state, sizeof(CObjObject), COBJ_OBJECT);
|
||||
CObjObject *cosmoO_newObject(CState *state) {
|
||||
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 tbl;
|
||||
return obj;
|
||||
}
|
||||
|
||||
CObjFunction *cosmoO_newFunction(CState *state) {
|
||||
@ -111,6 +118,13 @@ CObjCFunction *cosmoO_newCFunction(CState *state, CosmoCFunction func) {
|
||||
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) {
|
||||
// intialize array of pointers
|
||||
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) {
|
||||
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) {
|
||||
@ -188,22 +206,22 @@ void cosmoO_setObject(CState *state, CObjObject *object, CValue key, CValue val)
|
||||
*newVal = val;
|
||||
}
|
||||
|
||||
CObjString *cosmoO_toString(CState *state, CObj *val) {
|
||||
switch (val->type) {
|
||||
CObjString *cosmoO_toString(CState *state, CObj *obj) {
|
||||
switch (obj->type) {
|
||||
case COBJ_STRING: {
|
||||
return (CObjString*)val;
|
||||
return (CObjString*)obj;
|
||||
}
|
||||
case COBJ_FUNCTION: {
|
||||
CObjFunction *func = (CObjFunction*)val;
|
||||
CObjFunction *func = (CObjFunction*)obj;
|
||||
return func->name != NULL ? func->name : cosmoO_copyString(state, UNNAMEDCHUNK, strlen(UNNAMEDCHUNK));
|
||||
}
|
||||
case COBJ_OBJECT: { // TODO: maybe not safe??
|
||||
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);
|
||||
}
|
||||
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)
|
||||
printf("<function> %.*s", objFunc->name->length, objFunc->name->str);
|
||||
else
|
||||
printf("<function> _main");
|
||||
printf("<function> %s", UNNAMEDCHUNK);
|
||||
break;
|
||||
}
|
||||
case COBJ_CFUNCTION: {
|
||||
@ -237,12 +255,21 @@ void printObject(CObj *o) {
|
||||
printf("<c function> %p", objCFunc->cfunc);
|
||||
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: {
|
||||
CObjClosure *closure = (CObjClosure*)o;
|
||||
printObject((CObj*)closure->function); // just print the function
|
||||
break;
|
||||
}
|
||||
default:
|
||||
printf("<unkn>");
|
||||
printf("<unkn obj>");
|
||||
}
|
||||
}
|
18
src/cobj.h
18
src/cobj.h
@ -13,14 +13,15 @@ typedef enum {
|
||||
COBJ_OBJECT,
|
||||
COBJ_FUNCTION,
|
||||
COBJ_CFUNCTION,
|
||||
COBJ_METHOD,
|
||||
// internal use
|
||||
COBJ_CLOSURE,
|
||||
COBJ_UPVALUE,
|
||||
} 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 {
|
||||
CObjType type;
|
||||
@ -38,7 +39,7 @@ typedef struct CObjString {
|
||||
typedef struct CObjObject {
|
||||
CommonHeader; // "is a" CObj
|
||||
CTable tbl;
|
||||
//struct CObjObject *meta; // metaobject, used to describe object behavior
|
||||
struct CObjObject *meta; // metaobject, describes the behavior of the object
|
||||
} CObjObject;
|
||||
|
||||
typedef struct CObjFunction {
|
||||
@ -61,6 +62,12 @@ typedef struct CObjClosure {
|
||||
int upvalueCount;
|
||||
} 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 {
|
||||
CommonHeader; // "is a" CObj
|
||||
CValue *val;
|
||||
@ -72,12 +79,14 @@ typedef struct CObjUpval {
|
||||
#define IS_TABLE(x) isObjType(x, COBJ_OBJECT)
|
||||
#define IS_FUNCTION(x) isObjType(x, COBJ_FUNCTION)
|
||||
#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 cosmoV_readString(x) ((CObjString*)cosmoV_readObj(x))
|
||||
#define cosmoV_readObject(x) ((CObjObject*)cosmoV_readObj(x))
|
||||
#define cosmoV_readFunction(x) ((CObjFunction*)cosmoV_readObj(x))
|
||||
#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))
|
||||
|
||||
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);
|
||||
|
||||
CObjObject *cosmoO_newObject(CState *state, int startCap);
|
||||
CObjObject *cosmoO_newObject(CState *state);
|
||||
CObjFunction *cosmoO_newFunction(CState *state);
|
||||
CObjCFunction *cosmoO_newCFunction(CState *state, CosmoCFunction func);
|
||||
CObjMethod *cosmoO_newMethod(CState *state, CObjClosure *func, CObjObject *obj);
|
||||
CObjClosure *cosmoO_newClosure(CState *state, CObjFunction *func);
|
||||
CObjString *cosmoO_toString(CState *state, CObj *val);
|
||||
CObjUpval *cosmoO_newUpvalue(CState *state, CValue *val);
|
||||
|
@ -32,6 +32,7 @@ typedef enum {
|
||||
OP_NEWOBJECT,
|
||||
OP_GETOBJECT,
|
||||
OP_SETOBJECT,
|
||||
OP_NEWCLASS,
|
||||
|
||||
// ARITHMETIC
|
||||
OP_ADD,
|
||||
|
@ -18,6 +18,8 @@ typedef struct CObjString CObjString;
|
||||
typedef struct CObjUpval CObjUpval;
|
||||
typedef struct CObjFunction CObjFunction;
|
||||
typedef struct CObjCFunction CObjCFunction;
|
||||
typedef struct CObjMethod CObjMethod;
|
||||
typedef struct CObjClass CObjClass;
|
||||
typedef struct CObjClosure CObjClosure;
|
||||
|
||||
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
|
||||
|
||||
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 {
|
||||
CLexState *lex;
|
||||
CCompilerState* compiler;
|
||||
@ -50,7 +80,7 @@ static void declaration(CParseState *pstate);
|
||||
static void function(CParseState *pstate, FunctionType type);
|
||||
static void expressionStatement(CParseState *pstate);
|
||||
static ParseRule* getRule(CTokenType type);
|
||||
static CObjFunction *endCompiler(CParseState *pstate, int results);
|
||||
static CObjFunction *endCompiler(CParseState *pstate);
|
||||
|
||||
// ================================================================ [FRONT END/TALK TO LEXER] ================================================================
|
||||
|
||||
@ -66,16 +96,15 @@ static void initCompilerState(CParseState* pstate, CCompilerState *ccstate, Func
|
||||
ccstate->type = type;
|
||||
ccstate->function = cosmoO_newFunction(pstate->state);
|
||||
|
||||
|
||||
if (type != FTYPE_SCRIPT)
|
||||
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->depth = 0;
|
||||
local->isCaptured = false;
|
||||
local->name.length = 0;
|
||||
local->name.start = "";
|
||||
local->name.length = 0;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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] ================================================================
|
||||
|
||||
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)
|
||||
writeu8(pstate, OP_CALL);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
static void object(CParseState *pstate, bool canAssign) {
|
||||
// already consumed the beginning '{'
|
||||
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, entries);
|
||||
@ -515,7 +578,7 @@ static void object(CParseState *pstate, bool canAssign) {
|
||||
|
||||
static void dot(CParseState *pstate, bool canAssign) {
|
||||
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);
|
||||
writeu16(pstate, name);
|
||||
|
||||
@ -579,6 +642,7 @@ ParseRule ruleTable[] = {
|
||||
[TOKEN_END] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_FOR] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_FUNCTION] = {anonFunction, NULL, PREC_NONE},
|
||||
[TOKEN_CLASS] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_IF] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_LOCAL] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_NOT] = {NULL, NULL, PREC_NONE},
|
||||
@ -588,7 +652,6 @@ ParseRule ruleTable[] = {
|
||||
[TOKEN_WHILE] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_ERROR] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_VAR] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_THIS] = {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;
|
||||
|
||||
if (prefix == NULL) {
|
||||
return error(pstate, "Expected expression!");
|
||||
}
|
||||
if (prefix == NULL)
|
||||
return;
|
||||
|
||||
bool canAssign = prec <= PREC_ASSIGNMENT;
|
||||
prefix(pstate, canAssign);
|
||||
@ -666,6 +728,33 @@ static void defineVariable(CParseState *pstate, uint16_t global, bool forceLocal
|
||||
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) {
|
||||
if (pstate->hadError)
|
||||
return;
|
||||
@ -708,7 +797,7 @@ static void endScope(CParseState *pstate) {
|
||||
|
||||
// parses expressionStatements until a TOKEN_END is consumed
|
||||
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);
|
||||
}
|
||||
|
||||
@ -760,7 +849,7 @@ static void ifStatement(CParseState *pstate) {
|
||||
// parse until 'end' or 'else'
|
||||
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);
|
||||
}
|
||||
|
||||
@ -828,7 +917,7 @@ static void function(CParseState *pstate, FunctionType type) {
|
||||
}
|
||||
|
||||
// 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);
|
||||
valuePushed(pstate, 1); // they *will* be populated during runtime
|
||||
} while (match(pstate, TOKEN_COMMA));
|
||||
@ -840,7 +929,7 @@ static void function(CParseState *pstate, FunctionType type) {
|
||||
alignStack(pstate, savedPushed);
|
||||
endScope(pstate);
|
||||
|
||||
CObjFunction *objFunc = endCompiler(pstate, 0);
|
||||
CObjFunction *objFunc = endCompiler(pstate);
|
||||
|
||||
// push closure
|
||||
writeu8(pstate, OP_CLOSURE);
|
||||
@ -855,7 +944,7 @@ static void function(CParseState *pstate, FunctionType type) {
|
||||
}
|
||||
|
||||
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)
|
||||
markInitialized(pstate, var);
|
||||
@ -871,27 +960,19 @@ static void returnStatement(CParseState *pstate) {
|
||||
return;
|
||||
}
|
||||
|
||||
// can return multiple results
|
||||
int results = 0;
|
||||
if (!check(pstate, TOKEN_EOS)) { // make sure its not an end of a statement
|
||||
do {
|
||||
expression(pstate);
|
||||
results++;
|
||||
} while (match(pstate, TOKEN_COMMA));
|
||||
|
||||
if (results > UINT8_MAX) {
|
||||
error(pstate, "Too many results returned!");
|
||||
if (blockFollow(pstate->current)) { // does this return have a value
|
||||
writeu8(pstate, OP_NIL);
|
||||
writeu8(pstate, OP_RETURN);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
expression(pstate);
|
||||
writeu8(pstate, OP_RETURN);
|
||||
writeu8(pstate, results);
|
||||
valuePopped(pstate, results);
|
||||
valuePopped(pstate, 1);
|
||||
}
|
||||
|
||||
static void localFunction(CParseState *pstate) {
|
||||
uint8_t var = parseVariable(pstate, "Expected identifer!", true);
|
||||
uint16_t var = parseVariable(pstate, "Expected identifer!", true);
|
||||
markInitialized(pstate, var);
|
||||
|
||||
function(pstate, FTYPE_FUNCTION);
|
||||
@ -907,6 +988,7 @@ static void forLoop(CParseState *pstate) {
|
||||
// parse initalizer
|
||||
if (!match(pstate, TOKEN_EOS)) {
|
||||
expressionStatement(pstate);
|
||||
consume(pstate, TOKEN_EOS, "Expected ';' after initalizer!");
|
||||
}
|
||||
|
||||
int loopStart = getChunk(pstate)->count;
|
||||
@ -985,14 +1067,13 @@ static void expressionStatement(CParseState *pstate) {
|
||||
forLoop(pstate);
|
||||
} else if (match(pstate, TOKEN_FUNCTION)) {
|
||||
functionDeclaration(pstate);
|
||||
} else if (match(pstate, TOKEN_CLASS)) {
|
||||
_class(pstate);
|
||||
} else if (match(pstate, TOKEN_RETURN)) {
|
||||
returnStatement(pstate);
|
||||
} else if (check(pstate, TOKEN_EOS)) {
|
||||
// do nothing, just consume it
|
||||
} else {
|
||||
expression(pstate);
|
||||
}
|
||||
consume(pstate, TOKEN_EOS, "Expected end of statement after expression.");
|
||||
|
||||
// realign the stack
|
||||
alignStack(pstate, pstate->compiler->savedPushed);
|
||||
@ -1010,10 +1091,10 @@ static void declaration(CParseState *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
|
||||
writeu8(pstate, OP_NIL);
|
||||
writeu8(pstate, OP_RETURN);
|
||||
writeu8(pstate, results);
|
||||
|
||||
// update pstate to next compiler state
|
||||
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
|
||||
|
||||
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);
|
||||
|
||||
// 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)
|
||||
cosmoV_pushValue(state, cosmoV_newObj((CObj*)cosmoO_newClosure(state, resFunc)));
|
||||
|
||||
endCompiler(&parser, 0);
|
||||
endCompiler(&parser);
|
||||
freeParseState(&parser);
|
||||
cosmoM_unfreezeGC(state);
|
||||
return resFunc;
|
||||
|
29
src/cparse.h
29
src/cparse.h
@ -4,35 +4,6 @@
|
||||
#include "cosmo.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
|
||||
CObjFunction* cosmoP_compileString(CState *state, const char *source);
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
#include "cchunk.h"
|
||||
#include "cobj.h"
|
||||
#include "cvm.h"
|
||||
#include "cmem.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
@ -32,10 +33,16 @@ CState *cosmoV_newState() {
|
||||
|
||||
cosmoT_initTable(state, &state->strings, 8); // init string table
|
||||
cosmoT_initTable(state, &state->globals, 8); // init global table
|
||||
|
||||
state->initString = NULL;
|
||||
state->initString = cosmoO_copyString(state, "__init", 6);
|
||||
return state;
|
||||
}
|
||||
|
||||
void cosmoV_freeState(CState *state) {
|
||||
#ifdef GC_DEBUG
|
||||
printf("state %p is being free'd!\n", state);
|
||||
#endif
|
||||
// frees all the objects
|
||||
CObj *objs = state->objects;
|
||||
while (objs != NULL) {
|
||||
@ -45,6 +52,7 @@ void cosmoV_freeState(CState *state) {
|
||||
}
|
||||
|
||||
// free our string & global table
|
||||
state->initString = NULL;
|
||||
cosmoT_clearTable(state, &state->strings);
|
||||
cosmoT_clearTable(state, &state->globals);
|
||||
|
||||
|
@ -6,8 +6,6 @@
|
||||
#include "cobj.h"
|
||||
#include "ctable.h"
|
||||
|
||||
typedef struct CCompilerState CCompilerState;
|
||||
|
||||
typedef struct CCallFrame {
|
||||
CObjClosure *closure;
|
||||
INSTRUCTION *pc;
|
||||
@ -32,6 +30,8 @@ typedef struct CState {
|
||||
CValue stack[STACK_MAX]; // stack
|
||||
CCallFrame callFrame[FRAME_MAX]; // call frames
|
||||
int frameCount;
|
||||
|
||||
CObjString *initString;
|
||||
} CState;
|
||||
|
||||
COSMO_API CState *cosmoV_newState();
|
||||
|
@ -46,8 +46,11 @@ COSMO_API CObjString *cosmoV_toString(CState *state, CValue val) {
|
||||
case COSMO_TOBJ: {
|
||||
return cosmoO_toString(state, val.val.obj);
|
||||
}
|
||||
case COSMO_TNIL: {
|
||||
return cosmoO_copyString(state, "nil", 3);
|
||||
}
|
||||
default:
|
||||
return cosmoO_copyString(state, "<unkn>", 6);
|
||||
return cosmoO_copyString(state, "<unkn val>", 10);
|
||||
}
|
||||
}
|
||||
|
||||
@ -67,6 +70,6 @@ void printValue(CValue val) {
|
||||
printf("nil");
|
||||
break;
|
||||
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);
|
||||
}
|
||||
|
||||
int cosmoV_execute(CState *state);
|
||||
bool cosmoV_execute(CState *state);
|
||||
|
||||
typedef enum {
|
||||
CALL_CLOSURE,
|
||||
CALL_CFUNCTION
|
||||
} preCallResult;
|
||||
|
||||
int cosmoV_preCall(CState *state, int args, int nresults) {
|
||||
return -1;
|
||||
bool call(CState *state, CObjClosure *closure, int args) {
|
||||
// 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;
|
||||
}
|
||||
|
||||
// args = # of pass parameters, nresults = # of expected results
|
||||
COSMOVMRESULT cosmoV_call(CState *state, int args, int nresults) {
|
||||
// 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
|
||||
COSMOVMRESULT cosmoV_call(CState *state, int args) {
|
||||
StkPtr val = cosmoV_getTop(state, args); // function will always be right above the args
|
||||
|
||||
if (!(val->type == COSMO_TOBJ)) {
|
||||
@ -128,9 +149,18 @@ COSMOVMRESULT cosmoV_call(CState *state, int args, int nresults) {
|
||||
switch (val->val.obj->type) {
|
||||
case COBJ_CLOSURE: {
|
||||
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.
|
||||
if (args != closure->function->args) {
|
||||
CObjClosure *closure = method->closure;
|
||||
|
||||
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);
|
||||
return COSMOVM_RUNTIME_ERR;
|
||||
}
|
||||
@ -139,22 +169,53 @@ COSMOVMRESULT cosmoV_call(CState *state, int args, int nresults) {
|
||||
pushCallFrame(state, closure, closure->function->args);
|
||||
|
||||
// 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)
|
||||
CValue* results = state->top;
|
||||
|
||||
// pop the callframe and return result :)
|
||||
// pop the callframe and return result
|
||||
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
|
||||
for (int i = 1; i <= nresults; i++) {
|
||||
if (i <= res)
|
||||
cosmoV_pushValue(state, results[-i]);
|
||||
else
|
||||
cosmoV_pushValue(state, cosmoV_newNil());
|
||||
// check if they defined an initalizer
|
||||
if (cosmoO_getObject(state, metaObj, cosmoV_newObj(state->initString), &ret) && IS_CLOSURE(ret)) {
|
||||
CObjClosure *closure = cosmoV_readClosure(ret);
|
||||
|
||||
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);
|
||||
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;
|
||||
}
|
||||
case COBJ_CFUNCTION: {
|
||||
@ -163,21 +224,11 @@ COSMOVMRESULT cosmoV_call(CState *state, int args, int nresults) {
|
||||
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
|
||||
int res = cfunc(state, args, state->top - args);
|
||||
CValue res = cfunc(state, args, state->top - args);
|
||||
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;
|
||||
|
||||
// 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());
|
||||
}
|
||||
|
||||
cosmoV_pushValue(state, res);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@ -199,12 +250,11 @@ static inline bool isFalsey(StkPtr val) {
|
||||
cosmoV_setTop(state, 2); /* pop the 2 values */ \
|
||||
cosmoV_pushValue(state, typeConst((valA->val.num) op (valB->val.num))); \
|
||||
} else { \
|
||||
runtimeError(state, "Expected number! got %d and %d", valA->type, valB->type); \
|
||||
runtimeError(state, "Expected number!"); \
|
||||
} \
|
||||
|
||||
|
||||
// returns -1 if error, otherwise returns ammount of results
|
||||
int cosmoV_execute(CState *state) {
|
||||
// returns false if panic
|
||||
bool cosmoV_execute(CState *state) {
|
||||
CCallFrame* frame = &state->callFrame[state->frameCount - 1]; // grabs the current frame
|
||||
CValue *constants = frame->closure->function->chunk.constants.values; // cache the pointer :)
|
||||
|
||||
@ -285,8 +335,7 @@ int cosmoV_execute(CState *state) {
|
||||
}
|
||||
case OP_CALL: {
|
||||
uint8_t args = READBYTE();
|
||||
uint8_t results = READBYTE();
|
||||
COSMOVMRESULT result = cosmoV_call(state, args, results);
|
||||
COSMOVMRESULT result = cosmoV_call(state, args);
|
||||
if (result != COSMOVM_OK) {
|
||||
return result;
|
||||
}
|
||||
@ -320,12 +369,12 @@ int cosmoV_execute(CState *state) {
|
||||
case OP_NEWOBJECT: {
|
||||
uint8_t entries = READBYTE();
|
||||
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
|
||||
|
||||
for (int i = 0; i < entries; i++) {
|
||||
val = cosmoV_getTop(state, (i*2) + 2);
|
||||
key = cosmoV_getTop(state, (i*2) + 1);
|
||||
val = cosmoV_getTop(state, (i*2) + 1);
|
||||
key = cosmoV_getTop(state, (i*2) + 2);
|
||||
|
||||
// set key/value pair
|
||||
CValue *newVal = cosmoT_insert(state, &newObj->tbl, *key);
|
||||
@ -351,6 +400,10 @@ int cosmoV_execute(CState *state) {
|
||||
CValue val; // to hold our value
|
||||
|
||||
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_pushValue(state, val); // pushes the field result
|
||||
break;
|
||||
@ -453,8 +506,7 @@ int cosmoV_execute(CState *state) {
|
||||
case OP_FALSE: cosmoV_pushValue(state, cosmoV_newBoolean(false)); break;
|
||||
case OP_NIL: cosmoV_pushValue(state, cosmoV_newNil()); break;
|
||||
case OP_RETURN: {
|
||||
uint8_t results = READBYTE();
|
||||
return results;
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
CERROR("unknown opcode!");
|
||||
@ -467,7 +519,7 @@ int cosmoV_execute(CState *state) {
|
||||
#undef READUINT
|
||||
|
||||
// we'll only reach this is state->panic is true
|
||||
return COSMOVM_RUNTIME_ERR;
|
||||
return false;
|
||||
}
|
||||
|
||||
#undef BINARYOP
|
@ -11,6 +11,6 @@ typedef enum {
|
||||
} COSMOVMRESULT;
|
||||
|
||||
// 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
|
13
src/main.c
13
src/main.c
@ -7,12 +7,12 @@
|
||||
|
||||
#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;
|
||||
|
||||
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) {
|
||||
@ -22,11 +22,10 @@ static void interpret(CState *state, const char* script) {
|
||||
if (func != NULL) {
|
||||
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);
|
||||
//cosmoT_printTable(&state->globals, "globals");
|
||||
//cosmoT_printTable(&state->strings, "strings");
|
||||
if (res == COSMOVM_RUNTIME_ERR)
|
||||
state->panic = false; // so our repl isn't broken
|
||||
}
|
||||
}
|
||||
|
||||
|
29
test.cosmo
29
test.cosmo
@ -1,7 +1,26 @@
|
||||
var test = {}
|
||||
|
||||
if !test.hello then
|
||||
test.hello = "hello world!"
|
||||
class test
|
||||
function __init(self, str)
|
||||
self.hello = str
|
||||
end
|
||||
|
||||
print(test.hello .. "!!!!")
|
||||
function print(self, i)
|
||||
local str = self.hello
|
||||
|
||||
for (var x = i; x > 0; x=x-1) do
|
||||
str = str .. "!"
|
||||
end
|
||||
|
||||
print(str)
|
||||
end
|
||||
end
|
||||
|
||||
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