Major refactoring, added classes, many bug fixes

This commit is contained in:
CPunch 2020-11-09 19:44:12 -06:00
parent aa975b7330
commit c42a72dfad
21 changed files with 353 additions and 194 deletions

View File

@ -7,6 +7,7 @@
"tuple": "cpp", "tuple": "cpp",
"type_traits": "cpp", "type_traits": "cpp",
"utility": "cpp", "utility": "cpp",
"vector": "cpp" "vector": "cpp",
"typeinfo": "c"
} }
} }

View File

@ -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
} }

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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);
} }

View File

@ -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

View File

@ -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>");
} }
} }

View File

@ -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);

View File

@ -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,

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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();

View File

@ -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
View File

@ -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;
} }
// args = # of pass parameters, nresults = # of expected results // load function into callframe
COSMOVMRESULT cosmoV_call(CState *state, int args, int nresults) { 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 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

View File

@ -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

View File

@ -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");
} }
} }

View File

@ -1,7 +1,26 @@
var test = {} class test
function __init(self, str)
if !test.hello then self.hello = str
test.hello = "hello world!"
end 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()