added clang-format

This commit is contained in:
CPunch 2023-02-09 12:32:48 -06:00
parent 517b0b9532
commit 7279623e24
25 changed files with 2894 additions and 2301 deletions

26
.clang-format Normal file
View File

@ -0,0 +1,26 @@
---
Language: Cpp
# BasedOnStyle: Mozilla
AccessModifierOffset: -2
AlignAfterOpenBracket: Align
AlignArrayOfStructures: Right
AlignConsecutiveMacros: AcrossEmptyLinesAndComments
AlignConsecutiveAssignments: None
AlignConsecutiveBitFields: None
AlignConsecutiveDeclarations: None
AlignEscapedNewlines: Right
AlignOperands: Align
AlignTrailingComments: true
AllowAllArgumentsOnNextLine: true
AllowShortEnumsOnASingleLine: false
AllowShortFunctionsOnASingleLine: None
AllowShortBlocksOnASingleLine: Never
AllowShortIfStatementsOnASingleLine: Never
AlwaysBreakAfterReturnType: None
BreakBeforeBraces: Mozilla
IndentWidth: 4
ColumnLimit: 100
IncludeBlocks: Regroup
IndentPPDirectives: AfterHash
...

View File

@ -9,7 +9,7 @@ proto Test
end end
// stressing the GC // stressing the GC
for (var i = 0; ; i++) do for (var i = 0; i < 100000; i++) do
var x = Test("Hello world " .. i) var x = Test("Hello world " .. i)
x:print() x:print()
end end

View File

@ -1,15 +1,18 @@
#include "cbaselib.h" #include "cbaselib.h"
#include "cvm.h"
#include "cvalue.h"
#include "cobj.h"
#include "cmem.h" #include "cmem.h"
#include "cobj.h"
#include "cvalue.h"
#include "cvm.h"
#include <math.h> #include <math.h>
#include <sys/time.h> #include <sys/time.h>
// ================================================================ [BASELIB] ================================================================ // ================================================================ [BASELIB]
// ================================================================
int cosmoB_print(CState *state, int nargs, CValue *args) { int cosmoB_print(CState *state, int nargs, CValue *args)
{
for (int i = 0; i < nargs; i++) { for (int i = 0; i < nargs; i++) {
if (IS_REF(args[i])) { // if its a CObj*, generate the CObjString if (IS_REF(args[i])) { // if its a CObj*, generate the CObjString
CObjString *str = cosmoV_toString(state, args[i]); CObjString *str = cosmoV_toString(state, args[i]);
@ -23,7 +26,8 @@ int cosmoB_print(CState *state, int nargs, CValue *args) {
return 0; // print doesn't return any args return 0; // print doesn't return any args
} }
int cosmoB_assert(CState *state, int nargs, CValue *args) { int cosmoB_assert(CState *state, int nargs, CValue *args)
{
if (nargs < 1 || nargs > 2) { if (nargs < 1 || nargs > 2) {
cosmoV_error(state, "assert() expected 1 or 2 arguments, got %d!", nargs); cosmoV_error(state, "assert() expected 1 or 2 arguments, got %d!", nargs);
return 0; // nothing pushed onto the stack to return return 0; // nothing pushed onto the stack to return
@ -31,7 +35,8 @@ int cosmoB_assert(CState *state, int nargs, CValue *args) {
if (!IS_BOOLEAN(args[0]) || (nargs == 2 && !IS_STRING(args[1]))) { if (!IS_BOOLEAN(args[0]) || (nargs == 2 && !IS_STRING(args[1]))) {
if (nargs == 2) { if (nargs == 2) {
cosmoV_typeError(state, "assert()", "<boolean>, <string>", "%s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1])); cosmoV_typeError(state, "assert()", "<boolean>, <string>", "%s, %s",
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
} else { } else {
cosmoV_typeError(state, "assert()", "<boolean>", "%s", cosmoV_typeStr(args[0])); cosmoV_typeError(state, "assert()", "<boolean>", "%s", cosmoV_typeStr(args[0]));
} }
@ -44,7 +49,8 @@ int cosmoB_assert(CState *state, int nargs, CValue *args) {
return 0; return 0;
} }
int cosmoB_type(CState *state, int nargs, CValue *args) { int cosmoB_type(CState *state, int nargs, CValue *args)
{
if (nargs != 1) { if (nargs != 1) {
cosmoV_error(state, "type() expected 1 argument, got %d!", nargs); cosmoV_error(state, "type() expected 1 argument, got %d!", nargs);
return 0; return 0;
@ -55,7 +61,8 @@ int cosmoB_type(CState *state, int nargs, CValue *args) {
return 1; // 1 return value, the type string :D return 1; // 1 return value, the type string :D
} }
int cosmoB_pcall(CState *state, int nargs, CValue *args) { int cosmoB_pcall(CState *state, int nargs, CValue *args)
{
if (nargs < 1) { if (nargs < 1) {
cosmoV_error(state, "pcall() expected at least 1 argument!"); cosmoV_error(state, "pcall() expected at least 1 argument!");
return 0; return 0;
@ -65,7 +72,7 @@ int cosmoB_pcall(CState *state, int nargs, CValue *args) {
cosmoM_unfreezeGC(state); cosmoM_unfreezeGC(state);
// call the passed callable // call the passed callable
COSMOVMRESULT res = cosmoV_pcall(state, nargs-1, 1); COSMOVMRESULT res = cosmoV_pcall(state, nargs - 1, 1);
// insert false before the result // insert false before the result
cosmo_insert(state, 0, cosmoV_newBoolean(res == COSMOVM_OK)); cosmo_insert(state, 0, cosmoV_newBoolean(res == COSMOVM_OK));
@ -75,7 +82,8 @@ int cosmoB_pcall(CState *state, int nargs, CValue *args) {
return 2; return 2;
} }
int cosmoB_tonumber(CState *state, int nargs, CValue *args) { int cosmoB_tonumber(CState *state, int nargs, CValue *args)
{
if (nargs != 1) { if (nargs != 1) {
cosmoV_error(state, "tonumber() expected 1 argument, got %d!", nargs); cosmoV_error(state, "tonumber() expected 1 argument, got %d!", nargs);
return 0; return 0;
@ -85,17 +93,19 @@ int cosmoB_tonumber(CState *state, int nargs, CValue *args) {
return 1; return 1;
} }
int cosmoB_tostring(CState *state, int nargs, CValue *args) { int cosmoB_tostring(CState *state, int nargs, CValue *args)
{
if (nargs != 1) { if (nargs != 1) {
cosmoV_error(state, "tostring() expected 1 argument, got %d!", nargs); cosmoV_error(state, "tostring() expected 1 argument, got %d!", nargs);
return 0; return 0;
} }
cosmoV_pushRef(state, (CObj*)cosmoV_toString(state, args[0])); cosmoV_pushRef(state, (CObj *)cosmoV_toString(state, args[0]));
return 1; return 1;
} }
int cosmoB_loadstring(CState *state, int nargs, CValue *args) { int cosmoB_loadstring(CState *state, int nargs, CValue *args)
{
if (nargs != 1) { if (nargs != 1) {
cosmoV_error(state, "loadstring() expected 1 argument, got %d!", nargs); cosmoV_error(state, "loadstring() expected 1 argument, got %d!", nargs);
return 0; return 0;
@ -113,7 +123,8 @@ int cosmoB_loadstring(CState *state, int nargs, CValue *args) {
return 2; // <boolean>, <closure> or <error> return 2; // <boolean>, <closure> or <error>
} }
int cosmoB_error(CState *state, int nargs, CValue *args) { int cosmoB_error(CState *state, int nargs, CValue *args)
{
if (nargs != 1) { if (nargs != 1) {
cosmoV_error(state, "error() expected 1 argument, got %d!", nargs); cosmoV_error(state, "error() expected 1 argument, got %d!", nargs);
return 0; return 0;
@ -129,31 +140,16 @@ int cosmoB_error(CState *state, int nargs, CValue *args) {
return 0; return 0;
} }
void cosmoB_loadLibrary(CState *state) { void cosmoB_loadLibrary(CState *state)
const char *identifiers[] = { {
"print", const char *identifiers[] = {"print", "assert", "type", "pcall",
"assert", "tonumber", "tostring", "loadstring", "error"};
"type",
"pcall",
"tonumber",
"tostring",
"loadstring",
"error"
};
CosmoCFunction baseLib[] = { CosmoCFunction baseLib[] = {cosmoB_print, cosmoB_assert, cosmoB_type, cosmoB_pcall,
cosmoB_print, cosmoB_tonumber, cosmoB_tostring, cosmoB_loadstring, cosmoB_error};
cosmoB_assert,
cosmoB_type,
cosmoB_pcall,
cosmoB_tonumber,
cosmoB_tostring,
cosmoB_loadstring,
cosmoB_error
};
int i; int i;
for (i = 0; i < sizeof(identifiers)/sizeof(identifiers[0]); i++) { for (i = 0; i < sizeof(identifiers) / sizeof(identifiers[0]); i++) {
cosmoV_pushString(state, identifiers[i]); cosmoV_pushString(state, identifiers[i]);
cosmoV_pushCFunction(state, baseLib[i]); cosmoV_pushCFunction(state, baseLib[i]);
} }
@ -167,9 +163,11 @@ void cosmoB_loadLibrary(CState *state) {
cosmoB_loadMathLib(state); cosmoB_loadMathLib(state);
} }
// ================================================================ [OBJECT.*] ================================================================ // ================================================================ [OBJECT.*]
// ================================================================
int cosmoB_osetProto(CState *state, int nargs, CValue *args) { int cosmoB_osetProto(CState *state, int nargs, CValue *args)
{
if (nargs == 2) { if (nargs == 2) {
CObj *obj = cosmoV_readRef(args[0]); // object to set proto too CObj *obj = cosmoV_readRef(args[0]); // object to set proto too
CObjObject *proto = cosmoV_readObject(args[1]); CObjObject *proto = cosmoV_readObject(args[1]);
@ -182,25 +180,28 @@ int cosmoB_osetProto(CState *state, int nargs, CValue *args) {
return 0; // nothing return 0; // nothing
} }
int cosmoB_ogetProto(CState *state, int nargs, CValue *args) { int cosmoB_ogetProto(CState *state, int nargs, CValue *args)
{
if (nargs != 1) { if (nargs != 1) {
cosmoV_error(state, "Expected 1 argument, got %d!", nargs); cosmoV_error(state, "Expected 1 argument, got %d!", nargs);
return 0; return 0;
} }
cosmoV_pushRef(state, (CObj*)cosmoV_readObject(args[0])->_obj.proto); // just return the proto cosmoV_pushRef(state, (CObj *)cosmoV_readObject(args[0])->_obj.proto); // just return the proto
return 1; // 1 result return 1; // 1 result
} }
int cosmoB_oisChild(CState *state, int nargs, CValue *args) { int cosmoB_oisChild(CState *state, int nargs, CValue *args)
{
if (nargs != 2) { if (nargs != 2) {
cosmoV_error(state, "object.ischild() expected 2 arguments, got %d!", nargs); cosmoV_error(state, "object.ischild() expected 2 arguments, got %d!", nargs);
return 0; return 0;
} }
if (!IS_REF(args[0]) || !IS_OBJECT(args[1])) { if (!IS_REF(args[0]) || !IS_OBJECT(args[1])) {
cosmoV_typeError(state, "object.ischild()", "<reference obj>, <object>", "%s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1])); cosmoV_typeError(state, "object.ischild()", "<reference obj>, <object>", "%s, %s",
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
return 0; return 0;
} }
@ -212,14 +213,11 @@ int cosmoB_oisChild(CState *state, int nargs, CValue *args) {
return 1; return 1;
} }
COSMO_API void cosmoB_loadObjLib(CState *state) { COSMO_API void cosmoB_loadObjLib(CState *state)
const char *identifiers[] = { {
"ischild" const char *identifiers[] = {"ischild"};
};
CosmoCFunction objLib[] = { CosmoCFunction objLib[] = {cosmoB_oisChild};
cosmoB_oisChild
};
// make object library object // make object library object
cosmoV_pushString(state, "object"); cosmoV_pushString(state, "object");
@ -242,7 +240,7 @@ COSMO_API void cosmoB_loadObjLib(CState *state) {
cosmoV_makeTable(state, 1); cosmoV_makeTable(state, 1);
int i; int i;
for (i = 0; i < sizeof(identifiers)/sizeof(identifiers[0]); i++) { for (i = 0; i < sizeof(identifiers) / sizeof(identifiers[0]); i++) {
cosmoV_pushString(state, identifiers[i]); cosmoV_pushString(state, identifiers[i]);
cosmoV_pushCFunction(state, objLib[i]); cosmoV_pushCFunction(state, objLib[i]);
} }
@ -256,10 +254,12 @@ COSMO_API void cosmoB_loadObjLib(CState *state) {
cosmoV_register(state, 1); cosmoV_register(state, 1);
} }
// ================================================================ [OS.*] ================================================================ // ================================================================ [OS.*]
// ================================================================
// os.read() // os.read()
int cosmoB_osRead(CState *state, int nargs, CValue *args) { int cosmoB_osRead(CState *state, int nargs, CValue *args)
{
if (nargs != 1) { if (nargs != 1) {
cosmoV_error(state, "os.read() expected 1 argument, got %d!", nargs); cosmoV_error(state, "os.read() expected 1 argument, got %d!", nargs);
return 0; return 0;
@ -303,7 +303,8 @@ int cosmoB_osRead(CState *state, int nargs, CValue *args) {
} }
// os.time() // os.time()
int cosmoB_osTime(CState *state, int nargs, CValue *args) { int cosmoB_osTime(CState *state, int nargs, CValue *args)
{
struct timeval time; struct timeval time;
if (nargs > 0) { if (nargs > 0) {
cosmoV_error(state, "os.time() expected no arguments, got %d!", nargs); cosmoV_error(state, "os.time() expected no arguments, got %d!", nargs);
@ -316,7 +317,8 @@ int cosmoB_osTime(CState *state, int nargs, CValue *args) {
} }
// os.system() // os.system()
int cosmoB_osSystem(CState *state, int nargs, CValue *args) { int cosmoB_osSystem(CState *state, int nargs, CValue *args)
{
if (nargs != 1) { if (nargs != 1) {
cosmoV_error(state, "os.system() expects 1 argument, got %d!", nargs); cosmoV_error(state, "os.system() expects 1 argument, got %d!", nargs);
return 0; return 0;
@ -332,23 +334,16 @@ int cosmoB_osSystem(CState *state, int nargs, CValue *args) {
return 1; return 1;
} }
COSMO_API void cosmoB_loadOSLib(CState *state) { COSMO_API void cosmoB_loadOSLib(CState *state)
const char *identifiers[] = { {
"read", const char *identifiers[] = {"read", "time", "system"};
"time",
"system"
};
CosmoCFunction osLib[] = { CosmoCFunction osLib[] = {cosmoB_osRead, cosmoB_osTime, cosmoB_osSystem};
cosmoB_osRead,
cosmoB_osTime,
cosmoB_osSystem
};
cosmoV_pushString(state, "os"); cosmoV_pushString(state, "os");
int i; int i;
for (i = 0; i < sizeof(identifiers)/sizeof(identifiers[0]); i++) { for (i = 0; i < sizeof(identifiers) / sizeof(identifiers[0]); i++) {
cosmoV_pushString(state, identifiers[i]); cosmoV_pushString(state, identifiers[i]);
cosmoV_pushCFunction(state, osLib[i]); cosmoV_pushCFunction(state, osLib[i]);
} }
@ -357,13 +352,16 @@ COSMO_API void cosmoB_loadOSLib(CState *state) {
cosmoV_register(state, 1); // register the os.* object to the global table cosmoV_register(state, 1); // register the os.* object to the global table
} }
// ================================================================ [STRING.*] ================================================================ // ================================================================ [STRING.*]
// ================================================================
// string.sub // string.sub
int cosmoB_sSub(CState *state, int nargs, CValue *args) { int cosmoB_sSub(CState *state, int nargs, CValue *args)
{
if (nargs == 2) { if (nargs == 2) {
if (!IS_STRING(args[0]) || !IS_NUMBER(args[1])) { if (!IS_STRING(args[0]) || !IS_NUMBER(args[1])) {
cosmoV_typeError(state, "string.sub()", "<string>, <number>", "%s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1])); cosmoV_typeError(state, "string.sub()", "<string>, <number>", "%s, %s",
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
return 0; return 0;
} }
@ -372,14 +370,17 @@ int cosmoB_sSub(CState *state, int nargs, CValue *args) {
// make sure we stay within memory // make sure we stay within memory
if (indx < 0 || indx >= str->length) { if (indx < 0 || indx >= str->length) {
cosmoV_error(state, "string.sub() expected index to be 0-%d, got %d!", str->length - 1, indx); cosmoV_error(state, "string.sub() expected index to be 0-%d, got %d!", str->length - 1,
indx);
return 0; return 0;
} }
cosmoV_pushLString(state, str->str + ((int)indx), str->length - ((int)indx)); cosmoV_pushLString(state, str->str + ((int)indx), str->length - ((int)indx));
} else if (nargs == 3) { } else if (nargs == 3) {
if (!IS_STRING(args[0]) || !IS_NUMBER(args[1]) || !IS_NUMBER(args[2])) { if (!IS_STRING(args[0]) || !IS_NUMBER(args[1]) || !IS_NUMBER(args[2])) {
cosmoV_typeError(state, "string.sub()", "<string>, <number>, <number>", "%s, %s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]), cosmoV_typeStr(args[2])); cosmoV_typeError(state, "string.sub()", "<string>, <number>, <number>", "%s, %s, %s",
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]),
cosmoV_typeStr(args[2]));
return 0; return 0;
} }
@ -389,7 +390,9 @@ int cosmoB_sSub(CState *state, int nargs, CValue *args) {
// make sure we stay within memory // make sure we stay within memory
if (indx + length < 0 || indx + length >= str->length || indx < 0 || indx >= str->length) { if (indx + length < 0 || indx + length >= str->length || indx < 0 || indx >= str->length) {
cosmoV_error(state, "string.sub() expected subbed string goes out of bounds, max length is %d!", str->length); cosmoV_error(
state, "string.sub() expected subbed string goes out of bounds, max length is %d!",
str->length);
return 0; return 0;
} }
@ -403,10 +406,12 @@ int cosmoB_sSub(CState *state, int nargs, CValue *args) {
} }
// string.find // string.find
int cosmoB_sFind(CState *state, int nargs, CValue *args) { int cosmoB_sFind(CState *state, int nargs, CValue *args)
{
if (nargs == 2) { if (nargs == 2) {
if (!IS_STRING(args[0]) || !IS_STRING(args[1])) { if (!IS_STRING(args[0]) || !IS_STRING(args[1])) {
cosmoV_typeError(state, "string.find()", "<string>, <string>", "%s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1])); cosmoV_typeError(state, "string.find()", "<string>, <string>", "%s, %s",
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
return 0; return 0;
} }
@ -425,7 +430,9 @@ int cosmoB_sFind(CState *state, int nargs, CValue *args) {
cosmoV_pushNumber(state, indx - str->str); cosmoV_pushNumber(state, indx - str->str);
} else if (nargs == 3) { } else if (nargs == 3) {
if (!IS_STRING(args[0]) || !IS_STRING(args[1]) || !IS_NUMBER(args[2])) { if (!IS_STRING(args[0]) || !IS_STRING(args[1]) || !IS_NUMBER(args[2])) {
cosmoV_typeError(state, "string.find()", "<string>, <string>, <number>", "%s, %s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]), cosmoV_typeStr(args[2])); cosmoV_typeError(state, "string.find()", "<string>, <string>, <number>", "%s, %s, %s",
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]),
cosmoV_typeStr(args[2]));
return 0; return 0;
} }
@ -452,14 +459,16 @@ int cosmoB_sFind(CState *state, int nargs, CValue *args) {
} }
// string.split // string.split
int cosmoB_sSplit(CState *state, int nargs, CValue *args) { int cosmoB_sSplit(CState *state, int nargs, CValue *args)
{
if (nargs != 2) { if (nargs != 2) {
cosmoV_error(state, "string.split() expected 2 arguments, got %d!", nargs); cosmoV_error(state, "string.split() expected 2 arguments, got %d!", nargs);
return 0; return 0;
} }
if (!IS_STRING(args[0]) || !IS_STRING(args[1])) { if (!IS_STRING(args[0]) || !IS_STRING(args[1])) {
cosmoV_typeError(state, "string.split()", "<string>, <string>", "%s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1])); cosmoV_typeError(state, "string.split()", "<string>, <string>", "%s, %s",
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
return 0; return 0;
} }
@ -475,7 +484,8 @@ int cosmoB_sSplit(CState *state, int nargs, CValue *args) {
nIndx = strstr(indx, ptrn->str); nIndx = strstr(indx, ptrn->str);
cosmoV_pushNumber(state, nEntries++); cosmoV_pushNumber(state, nEntries++);
cosmoV_pushLString(state, indx, nIndx == NULL ? str->length - (indx - str->str) : nIndx - indx); cosmoV_pushLString(state, indx,
nIndx == NULL ? str->length - (indx - str->str) : nIndx - indx);
indx = nIndx + ptrn->length; indx = nIndx + ptrn->length;
} while (nIndx != NULL); } while (nIndx != NULL);
@ -486,7 +496,8 @@ int cosmoB_sSplit(CState *state, int nargs, CValue *args) {
} }
// string.byte // string.byte
int cosmoB_sByte(CState *state, int nargs, CValue *args) { int cosmoB_sByte(CState *state, int nargs, CValue *args)
{
if (nargs != 1) { if (nargs != 1) {
cosmoV_error(state, "string.byte() expected 1 argument, got %d!", nargs); cosmoV_error(state, "string.byte() expected 1 argument, got %d!", nargs);
return 0; return 0;
@ -500,8 +511,8 @@ int cosmoB_sByte(CState *state, int nargs, CValue *args) {
CObjString *str = cosmoV_readString(args[0]); CObjString *str = cosmoV_readString(args[0]);
if (str->length < 1) { if (str->length < 1) {
// the length of the string is less than 1, in the future I might throw an error for this, but // the length of the string is less than 1, in the future I might throw an error for this,
// for now im going to copy lua and just return a nil // but for now im going to copy lua and just return a nil
return 0; return 0;
} }
@ -511,7 +522,8 @@ int cosmoB_sByte(CState *state, int nargs, CValue *args) {
} }
// string.char // string.char
int cosmoB_sChar(CState *state, int nargs, CValue *args) { int cosmoB_sChar(CState *state, int nargs, CValue *args)
{
if (nargs != 1) { if (nargs != 1) {
cosmoV_error(state, "string.char() expected 1 argument, got %d!", nargs); cosmoV_error(state, "string.char() expected 1 argument, got %d!", nargs);
return 0; return 0;
@ -522,7 +534,8 @@ int cosmoB_sChar(CState *state, int nargs, CValue *args) {
return 0; return 0;
} }
// small side effect of truncating the number, but ignoring the decimal instead of throwing an error is the better option imo // small side effect of truncating the number, but ignoring the decimal instead of throwing an
// error is the better option imo
int num = (int)cosmoV_readNumber(args[0]); int num = (int)cosmoV_readNumber(args[0]);
char c = num; char c = num;
@ -536,7 +549,8 @@ int cosmoB_sChar(CState *state, int nargs, CValue *args) {
return 1; return 1;
} }
int cosmoB_sLen(CState *state, int nargs, CValue *args) { int cosmoB_sLen(CState *state, int nargs, CValue *args)
{
if (nargs < 1) { if (nargs < 1) {
cosmoV_error(state, "string.len() expected 1 argument, got %d!", nargs); cosmoV_error(state, "string.len() expected 1 argument, got %d!", nargs);
return 0; return 0;
@ -552,7 +566,8 @@ int cosmoB_sLen(CState *state, int nargs, CValue *args) {
return 1; return 1;
} }
int cosmoB_sRep(CState *state, int nargs, CValue *args) { int cosmoB_sRep(CState *state, int nargs, CValue *args)
{
if (nargs != 2) { if (nargs != 2) {
cosmoV_error(state, "string.rep() expected 2 arguments, got %d!", nargs); cosmoV_error(state, "string.rep() expected 2 arguments, got %d!", nargs);
return 0; return 0;
@ -560,7 +575,8 @@ int cosmoB_sRep(CState *state, int nargs, CValue *args) {
// expects <string>, <number> // expects <string>, <number>
if (!IS_STRING(args[0]) || !IS_NUMBER(args[1])) { if (!IS_STRING(args[0]) || !IS_NUMBER(args[1])) {
cosmoV_typeError(state, "string.rep", "<string>, <number>", "%s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1])); cosmoV_typeError(state, "string.rep", "<string>, <number>", "%s, %s",
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
return 0; return 0;
} }
@ -584,35 +600,21 @@ int cosmoB_sRep(CState *state, int nargs, CValue *args) {
newStr[length] = '\0'; newStr[length] = '\0';
// finally, push the resulting string onto the stack // finally, push the resulting string onto the stack
cosmoV_pushRef(state, (CObj*)cosmoO_takeString(state, newStr, length)); cosmoV_pushRef(state, (CObj *)cosmoO_takeString(state, newStr, length));
return 1; return 1;
} }
void cosmoB_loadStrLib(CState *state) { void cosmoB_loadStrLib(CState *state)
const char *identifiers[] = { {
"sub", const char *identifiers[] = {"sub", "find", "split", "byte", "char", "len", "rep"};
"find",
"split",
"byte",
"char",
"len",
"rep"
};
CosmoCFunction strLib[] = { CosmoCFunction strLib[] = {cosmoB_sSub, cosmoB_sFind, cosmoB_sSplit, cosmoB_sByte,
cosmoB_sSub, cosmoB_sChar, cosmoB_sLen, cosmoB_sRep};
cosmoB_sFind,
cosmoB_sSplit,
cosmoB_sByte,
cosmoB_sChar,
cosmoB_sLen,
cosmoB_sRep
};
// make string library object // make string library object
cosmoV_pushString(state, "string"); cosmoV_pushString(state, "string");
int i; int i;
for (i = 0; i < sizeof(identifiers)/sizeof(identifiers[0]); i++) { for (i = 0; i < sizeof(identifiers) / sizeof(identifiers[0]); i++) {
cosmoV_pushString(state, identifiers[i]); cosmoV_pushString(state, identifiers[i]);
cosmoV_pushCFunction(state, strLib[i]); cosmoV_pushCFunction(state, strLib[i]);
} }
@ -626,10 +628,12 @@ void cosmoB_loadStrLib(CState *state) {
cosmoV_register(state, 1); cosmoV_register(state, 1);
} }
// ================================================================ [MATH] ================================================================ // ================================================================ [MATH]
// ================================================================
// math.abs // math.abs
int cosmoB_mAbs(CState *state, int nargs, CValue *args) { int cosmoB_mAbs(CState *state, int nargs, CValue *args)
{
if (nargs != 1) { if (nargs != 1) {
cosmoV_error(state, "math.abs() expected 1 argument, got %d!", nargs); cosmoV_error(state, "math.abs() expected 1 argument, got %d!", nargs);
return 0; return 0;
@ -645,7 +649,8 @@ int cosmoB_mAbs(CState *state, int nargs, CValue *args) {
} }
// math.floor // math.floor
int cosmoB_mFloor(CState *state, int nargs, CValue *args) { int cosmoB_mFloor(CState *state, int nargs, CValue *args)
{
if (nargs != 1) { if (nargs != 1) {
cosmoV_error(state, "math.floor() expected 1 argument, got %d!", nargs); cosmoV_error(state, "math.floor() expected 1 argument, got %d!", nargs);
return 0; return 0;
@ -661,7 +666,8 @@ int cosmoB_mFloor(CState *state, int nargs, CValue *args) {
} }
// math.ceil // math.ceil
int cosmoB_mCeil(CState *state, int nargs, CValue *args) { int cosmoB_mCeil(CState *state, int nargs, CValue *args)
{
if (nargs != 1) { if (nargs != 1) {
cosmoV_error(state, "math.ceil() expected 1 argument, got %d!", nargs); cosmoV_error(state, "math.ceil() expected 1 argument, got %d!", nargs);
return 0; return 0;
@ -684,7 +690,8 @@ int cosmoB_mCeil(CState *state, int nargs, CValue *args) {
return 1; return 1;
} }
int cosmoB_mSin(CState *state, int nargs, CValue *args) { int cosmoB_mSin(CState *state, int nargs, CValue *args)
{
if (nargs != 1) { if (nargs != 1) {
cosmoV_error(state, "math.sin() expected 1 argument, got %d!", nargs); cosmoV_error(state, "math.sin() expected 1 argument, got %d!", nargs);
return 0; return 0;
@ -699,7 +706,8 @@ int cosmoB_mSin(CState *state, int nargs, CValue *args) {
return 1; return 1;
} }
int cosmoB_mCos(CState *state, int nargs, CValue *args) { int cosmoB_mCos(CState *state, int nargs, CValue *args)
{
if (nargs != 1) { if (nargs != 1) {
cosmoV_error(state, "math.cos() expected 1 argument, got %d!", nargs); cosmoV_error(state, "math.cos() expected 1 argument, got %d!", nargs);
return 0; return 0;
@ -714,7 +722,8 @@ int cosmoB_mCos(CState *state, int nargs, CValue *args) {
return 1; return 1;
} }
int cosmoB_mTan(CState *state, int nargs, CValue *args) { int cosmoB_mTan(CState *state, int nargs, CValue *args)
{
if (nargs != 1) { if (nargs != 1) {
cosmoV_error(state, "math.tan() expected 1 argument, got %d!", nargs); cosmoV_error(state, "math.tan() expected 1 argument, got %d!", nargs);
return 0; return 0;
@ -729,7 +738,8 @@ int cosmoB_mTan(CState *state, int nargs, CValue *args) {
return 1; return 1;
} }
int cosmoB_mASin(CState *state, int nargs, CValue *args) { int cosmoB_mASin(CState *state, int nargs, CValue *args)
{
if (nargs != 1) { if (nargs != 1) {
cosmoV_error(state, "math.asin() expected 1 argument, got %d!", nargs); cosmoV_error(state, "math.asin() expected 1 argument, got %d!", nargs);
return 0; return 0;
@ -744,7 +754,8 @@ int cosmoB_mASin(CState *state, int nargs, CValue *args) {
return 1; return 1;
} }
int cosmoB_mACos(CState *state, int nargs, CValue *args) { int cosmoB_mACos(CState *state, int nargs, CValue *args)
{
if (nargs != 1) { if (nargs != 1) {
cosmoV_error(state, "math.acos() expected 1 argument, got %d!", nargs); cosmoV_error(state, "math.acos() expected 1 argument, got %d!", nargs);
return 0; return 0;
@ -759,7 +770,8 @@ int cosmoB_mACos(CState *state, int nargs, CValue *args) {
return 1; return 1;
} }
int cosmoB_mATan(CState *state, int nargs, CValue *args) { int cosmoB_mATan(CState *state, int nargs, CValue *args)
{
if (nargs != 1) { if (nargs != 1) {
cosmoV_error(state, "math.atan() expected 1 argument, got %d!", nargs); cosmoV_error(state, "math.atan() expected 1 argument, got %d!", nargs);
return 0; return 0;
@ -774,7 +786,8 @@ int cosmoB_mATan(CState *state, int nargs, CValue *args) {
return 1; return 1;
} }
int cosmoB_mRad(CState *state, int nargs, CValue *args) { int cosmoB_mRad(CState *state, int nargs, CValue *args)
{
if (nargs != 1) { if (nargs != 1) {
cosmoV_error(state, "math.rad() expected 1 argument, got %d!", nargs); cosmoV_error(state, "math.rad() expected 1 argument, got %d!", nargs);
return 0; return 0;
@ -790,7 +803,8 @@ int cosmoB_mRad(CState *state, int nargs, CValue *args) {
return 1; return 1;
} }
int cosmoB_mDeg(CState *state, int nargs, CValue *args) { int cosmoB_mDeg(CState *state, int nargs, CValue *args)
{
if (nargs != 1) { if (nargs != 1) {
cosmoV_error(state, "math.deg() expected 1 argument, got %d!", nargs); cosmoV_error(state, "math.deg() expected 1 argument, got %d!", nargs);
return 0; return 0;
@ -806,39 +820,19 @@ int cosmoB_mDeg(CState *state, int nargs, CValue *args) {
return 1; return 1;
} }
void cosmoB_loadMathLib(CState *state) { void cosmoB_loadMathLib(CState *state)
const char *identifiers[] = { {
"abs", const char *identifiers[] = {"abs", "floor", "ceil", "sin", "cos", "tan",
"floor", "asin", "acos", "atan", "rad", "deg"};
"ceil",
"sin",
"cos",
"tan",
"asin",
"acos",
"atan",
"rad",
"deg"
};
CosmoCFunction mathLib[] = { CosmoCFunction mathLib[] = {cosmoB_mAbs, cosmoB_mFloor, cosmoB_mCeil, cosmoB_mSin,
cosmoB_mAbs, cosmoB_mCos, cosmoB_mTan, cosmoB_mASin, cosmoB_mACos,
cosmoB_mFloor, cosmoB_mATan, cosmoB_mRad, cosmoB_mDeg};
cosmoB_mCeil,
cosmoB_mSin,
cosmoB_mCos,
cosmoB_mTan,
cosmoB_mASin,
cosmoB_mACos,
cosmoB_mATan,
cosmoB_mRad,
cosmoB_mDeg
};
// make math library object // make math library object
cosmoV_pushString(state, "math"); cosmoV_pushString(state, "math");
int i; int i;
for (i = 0; i < sizeof(identifiers)/sizeof(identifiers[0]); i++) { for (i = 0; i < sizeof(identifiers) / sizeof(identifiers[0]); i++) {
cosmoV_pushString(state, identifiers[i]); cosmoV_pushString(state, identifiers[i]);
cosmoV_pushCFunction(state, mathLib[i]); cosmoV_pushCFunction(state, mathLib[i]);
} }
@ -852,41 +846,47 @@ void cosmoB_loadMathLib(CState *state) {
cosmoV_register(state, 1); cosmoV_register(state, 1);
} }
// ================================================================ [VM.*] ================================================================ // ================================================================ [VM.*]
// ================================================================
// vm.__getter["globals"] // vm.__getter["globals"]
int cosmoB_vgetGlobal(CState *state, int nargs, CValue *args) { int cosmoB_vgetGlobal(CState *state, int nargs, CValue *args)
{
// this function doesn't need to check anything, just return the global table // this function doesn't need to check anything, just return the global table
cosmoV_pushRef(state, (CObj*)state->globals); cosmoV_pushRef(state, (CObj *)state->globals);
return 1; return 1;
} }
// vm.__setter["globals"] // vm.__setter["globals"]
int cosmoB_vsetGlobal(CState *state, int nargs, CValue *args) { int cosmoB_vsetGlobal(CState *state, int nargs, CValue *args)
{
if (nargs != 2) { if (nargs != 2) {
cosmoV_error(state, "Expected 2 argumenst, got %d!", nargs); cosmoV_error(state, "Expected 2 argumenst, got %d!", nargs);
return 0; return 0;
} }
if (!IS_TABLE(args[1])) { if (!IS_TABLE(args[1])) {
cosmoV_typeError(state, "vm.__setter[\"globals\"]", "<object>, <table>", "%s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1])); cosmoV_typeError(state, "vm.__setter[\"globals\"]", "<object>, <table>", "%s, %s",
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
return 0; return 0;
} }
// this makes me very nervous ngl // this makes me very nervous ngl
CObjTable *tbl = (CObjTable*)cosmoV_readRef(args[1]); CObjTable *tbl = (CObjTable *)cosmoV_readRef(args[1]);
state->globals = tbl; state->globals = tbl;
return 0; return 0;
} }
int cosmoB_vindexBProto(CState *state, int nargs, CValue *args) { int cosmoB_vindexBProto(CState *state, int nargs, CValue *args)
{
if (nargs != 2) { if (nargs != 2) {
cosmoV_error(state, "Expected 2 arguments, got %d!", nargs); cosmoV_error(state, "Expected 2 arguments, got %d!", nargs);
return 0; return 0;
} }
if (!IS_NUMBER(args[1])) { if (!IS_NUMBER(args[1])) {
cosmoV_typeError(state, "baseProtos.__index", "<object>, <number>", "%s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1])); cosmoV_typeError(state, "baseProtos.__index", "<object>, <number>", "%s, %s",
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
return 0; return 0;
} }
@ -898,21 +898,24 @@ int cosmoB_vindexBProto(CState *state, int nargs, CValue *args) {
} }
if (state->protoObjects[indx] != NULL) if (state->protoObjects[indx] != NULL)
cosmoV_pushRef(state, (CObj*)state->protoObjects[indx]); cosmoV_pushRef(state, (CObj *)state->protoObjects[indx]);
else else
cosmoV_pushNil(state); cosmoV_pushNil(state);
return 1; // 1 value pushed, 1 value returned return 1; // 1 value pushed, 1 value returned
} }
int cosmoB_vnewindexBProto(CState *state, int nargs, CValue *args) { int cosmoB_vnewindexBProto(CState *state, int nargs, CValue *args)
{
if (nargs != 3) { if (nargs != 3) {
cosmoV_error(state, "Expected 3 arguments, got %d!", nargs); cosmoV_error(state, "Expected 3 arguments, got %d!", nargs);
return 0; return 0;
} }
if (!IS_NUMBER(args[1]) || !IS_OBJECT(args[2])) { if (!IS_NUMBER(args[1]) || !IS_OBJECT(args[2])) {
cosmoV_typeError(state, "baseProtos.__newindex", "<object>, <number>, <object>", "%s, %s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]), cosmoV_typeStr(args[2])); cosmoV_typeError(state, "baseProtos.__newindex", "<object>, <number>, <object>",
"%s, %s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]),
cosmoV_typeStr(args[2]));
return 0; return 0;
} }
@ -929,7 +932,8 @@ int cosmoB_vnewindexBProto(CState *state, int nargs, CValue *args) {
} }
// vm.collect() // vm.collect()
int cosmoB_vcollect(CState *state, int nargs, CValue *args) { int cosmoB_vcollect(CState *state, int nargs, CValue *args)
{
// first, unfreeze the state (we start frozen on entry to any C Function) // first, unfreeze the state (we start frozen on entry to any C Function)
cosmoM_unfreezeGC(state); cosmoM_unfreezeGC(state);
@ -943,7 +947,8 @@ int cosmoB_vcollect(CState *state, int nargs, CValue *args) {
return 0; return 0;
} }
void cosmoB_loadVM(CState *state) { void cosmoB_loadVM(CState *state)
{
// make vm.* object // make vm.* object
cosmoV_pushString(state, "vm"); cosmoV_pushString(state, "vm");

View File

@ -32,7 +32,8 @@ COSMO_API void cosmoB_loadOSLib(CState *state);
- string.char & <string>:char() - string.char & <string>:char()
- string.rep & <string>:rep() - string.rep & <string>:rep()
The base proto object for strings is also set, allowing you to invoke the string.* api through string objects, eg. The base proto object for strings is also set, allowing you to invoke the string.* api through
string objects, eg.
`"hello world":split(" ")` is equivalent to `string.split("hello world", " ")` `"hello world":split(" ")` is equivalent to `string.split("hello world", " ")`
*/ */
COSMO_API void cosmoB_loadStrLib(CState *state); COSMO_API void cosmoB_loadStrLib(CState *state);

View File

@ -1,16 +1,19 @@
#include "cmem.h"
#include "cchunk.h" #include "cchunk.h"
#include "cmem.h"
#include "cobj.h"
#include "cvalue.h" #include "cvalue.h"
#include "cvm.h" #include "cvm.h"
#include "cobj.h"
CChunk *newChunk(CState* state, size_t startCapacity) { CChunk *newChunk(CState *state, size_t startCapacity)
{
CChunk *chunk = cosmoM_xmalloc(state, sizeof(CChunk)); CChunk *chunk = cosmoM_xmalloc(state, sizeof(CChunk));
initChunk(state, chunk, startCapacity); initChunk(state, chunk, startCapacity);
return chunk; return chunk;
} }
void initChunk(CState* state, CChunk *chunk, size_t startCapacity) { void initChunk(CState *state, CChunk *chunk, size_t startCapacity)
{
chunk->capacity = startCapacity; chunk->capacity = startCapacity;
chunk->lineCapacity = startCapacity; chunk->lineCapacity = startCapacity;
chunk->count = 0; chunk->count = 0;
@ -21,7 +24,8 @@ void initChunk(CState* state, CChunk *chunk, size_t startCapacity) {
initValArray(state, &chunk->constants, ARRAY_START); initValArray(state, &chunk->constants, ARRAY_START);
} }
void cleanChunk(CState* state, CChunk *chunk) { void cleanChunk(CState *state, CChunk *chunk)
{
// first, free the chunk buffer // first, free the chunk buffer
cosmoM_freearray(state, INSTRUCTION, chunk->buf, chunk->capacity); cosmoM_freearray(state, INSTRUCTION, chunk->buf, chunk->capacity);
// then the line info // then the line info
@ -30,13 +34,15 @@ void cleanChunk(CState* state, CChunk *chunk) {
cleanValArray(state, &chunk->constants); cleanValArray(state, &chunk->constants);
} }
void freeChunk(CState* state, CChunk *chunk) { void freeChunk(CState *state, CChunk *chunk)
{
cleanChunk(state, chunk); cleanChunk(state, chunk);
// now, free the wrapper struct // now, free the wrapper struct
cosmoM_free(state, CChunk, chunk); cosmoM_free(state, CChunk, chunk);
} }
int addConstant(CState* state, CChunk *chunk, CValue value) { int addConstant(CState *state, CChunk *chunk, CValue value)
{
// before adding the constant, check if we already have it // before adding the constant, check if we already have it
for (size_t i = 0; i < chunk->constants.count; i++) { for (size_t i = 0; i < chunk->constants.count; i++) {
if (cosmoV_equal(state, value, chunk->constants.values[i])) if (cosmoV_equal(state, value, chunk->constants.values[i]))
@ -49,9 +55,11 @@ int addConstant(CState* state, CChunk *chunk, CValue value) {
return chunk->constants.count - 1; // return the index of the new constants return chunk->constants.count - 1; // return the index of the new constants
} }
// ================================================================ [WRITE TO CHUNK] ================================================================ // ================================================================ [WRITE TO CHUNK]
// ================================================================
void writeu8Chunk(CState* state, CChunk *chunk, INSTRUCTION i, int line) { void writeu8Chunk(CState *state, CChunk *chunk, INSTRUCTION i, int line)
{
// does the buffer need to be reallocated? // does the buffer need to be reallocated?
cosmoM_growarray(state, INSTRUCTION, chunk->buf, chunk->count, chunk->capacity); cosmoM_growarray(state, INSTRUCTION, chunk->buf, chunk->count, chunk->capacity);
cosmoM_growarray(state, int, chunk->lineInfo, chunk->count, chunk->lineCapacity); cosmoM_growarray(state, int, chunk->lineInfo, chunk->count, chunk->lineCapacity);
@ -61,8 +69,9 @@ void writeu8Chunk(CState* state, CChunk *chunk, INSTRUCTION i, int line) {
chunk->buf[chunk->count++] = i; chunk->buf[chunk->count++] = i;
} }
void writeu16Chunk(CState* state, CChunk *chunk, uint16_t i, int line) { void writeu16Chunk(CState *state, CChunk *chunk, uint16_t i, int line)
INSTRUCTION *buffer = (INSTRUCTION*)(&i); {
INSTRUCTION *buffer = (INSTRUCTION *)(&i);
int sz = sizeof(uint16_t) / sizeof(INSTRUCTION); int sz = sizeof(uint16_t) / sizeof(INSTRUCTION);
for (int i = 0; i < sz; i++) { for (int i = 0; i < sz; i++) {

View File

@ -1,12 +1,12 @@
#ifndef CCHUNK_H #ifndef CCHUNK_H
#define CCHUNK_H #define CCHUNK_H
#include "cosmo.h"
#include "coperators.h" #include "coperators.h"
#include "cosmo.h"
#include "cvalue.h" #include "cvalue.h"
struct CChunk { struct CChunk
{
size_t capacity; // the amount of space we've allocated for size_t capacity; // the amount of space we've allocated for
size_t count; // the space we're currently using size_t count; // the space we're currently using
INSTRUCTION *buf; // whole chunk INSTRUCTION *buf; // whole chunk
@ -15,23 +15,25 @@ struct CChunk {
int *lineInfo; int *lineInfo;
}; };
CChunk *newChunk(CState* state, size_t startCapacity); CChunk *newChunk(CState *state, size_t startCapacity);
void initChunk(CState* state, CChunk *chunk, size_t startCapacity); void initChunk(CState *state, CChunk *chunk, size_t startCapacity);
void cleanChunk(CState* state, CChunk *chunk); // frees everything but the struct void cleanChunk(CState *state, CChunk *chunk); // frees everything but the struct
void freeChunk(CState* state, CChunk *chunk); // frees everything including the struct void freeChunk(CState *state, CChunk *chunk); // frees everything including the struct
int addConstant(CState* state, CChunk *chunk, CValue value); int addConstant(CState *state, CChunk *chunk, CValue value);
// write to chunk // write to chunk
void writeu8Chunk(CState* state, CChunk *chunk, INSTRUCTION i, int line); void writeu8Chunk(CState *state, CChunk *chunk, INSTRUCTION i, int line);
void writeu16Chunk(CState* state, CChunk *chunk, uint16_t i, int line); void writeu16Chunk(CState *state, CChunk *chunk, uint16_t i, int line);
// read from chunk // read from chunk
static inline INSTRUCTION readu8Chunk(CChunk *chunk, int offset) { static inline INSTRUCTION readu8Chunk(CChunk *chunk, int offset)
{
return chunk->buf[offset]; return chunk->buf[offset];
} }
static inline uint16_t readu16Chunk(CChunk *chunk, int offset) { static inline uint16_t readu16Chunk(CChunk *chunk, int offset)
return *((uint16_t*)(&chunk->buf[offset])); {
return *((uint16_t *)(&chunk->buf[offset]));
} }
#endif #endif

View File

@ -1,49 +1,62 @@
#include "cdebug.h" #include "cdebug.h"
#include "cvalue.h"
#include "cobj.h"
void printIndent(int indent) { #include "cobj.h"
#include "cvalue.h"
void printIndent(int indent)
{
for (int i = 0; i < indent; i++) for (int i = 0; i < indent; i++)
printf("\t"); printf("\t");
} }
int simpleInstruction(const char *name, int offset) { int simpleInstruction(const char *name, int offset)
{
printf("%s", name); printf("%s", name);
return offset + 1; // consume opcode return offset + 1; // consume opcode
} }
int u8OperandInstruction(const char *name, CChunk *chunk, int offset) { int u8OperandInstruction(const char *name, CChunk *chunk, int offset)
{
printf("%-16s [%03d]", name, readu8Chunk(chunk, offset + 1)); printf("%-16s [%03d]", name, readu8Chunk(chunk, offset + 1));
return offset + 2; return offset + 2;
} }
int u16OperandInstruction(const char *name, CChunk *chunk, int offset) { int u16OperandInstruction(const char *name, CChunk *chunk, int offset)
{
printf("%-16s [%05d]", name, readu16Chunk(chunk, offset + 1)); printf("%-16s [%05d]", name, readu16Chunk(chunk, offset + 1));
return offset + 1 + (sizeof(uint16_t) / sizeof(INSTRUCTION)); return offset + 1 + (sizeof(uint16_t) / sizeof(INSTRUCTION));
} }
int JumpInstruction(const char *name, CChunk *chunk, int offset, int dir) { int JumpInstruction(const char *name, CChunk *chunk, int offset, int dir)
{
int jmp = ((int)readu16Chunk(chunk, offset + 1)) * dir; int jmp = ((int)readu16Chunk(chunk, offset + 1)) * dir;
printf("%-16s [%05d] - jumps to %04d", name, jmp, offset + 3 + jmp); printf("%-16s [%05d] - jumps to %04d", name, jmp, offset + 3 + jmp);
return offset + 1 + (sizeof(uint16_t) / sizeof(INSTRUCTION)); return offset + 1 + (sizeof(uint16_t) / sizeof(INSTRUCTION));
} }
int u8u8OperandInstruction(const char *name, CChunk *chunk, int offset) { int u8u8OperandInstruction(const char *name, CChunk *chunk, int offset)
printf("%-16s [%03d] [%03d]", name, readu8Chunk(chunk, offset + 1), readu8Chunk(chunk, offset + 2)); {
printf("%-16s [%03d] [%03d]", name, readu8Chunk(chunk, offset + 1),
readu8Chunk(chunk, offset + 2));
return offset + 3; // op + u8 + u8 return offset + 3; // op + u8 + u8
} }
int u8u16OperandInstruction(const char *name, CChunk *chunk, int offset) { int u8u16OperandInstruction(const char *name, CChunk *chunk, int offset)
printf("%-16s [%03d] [%05d]", name, readu8Chunk(chunk, offset + 1), readu16Chunk(chunk, offset + 2)); {
printf("%-16s [%03d] [%05d]", name, readu8Chunk(chunk, offset + 1),
readu16Chunk(chunk, offset + 2));
return offset + 4; // op + u8 + u16 return offset + 4; // op + u8 + u16
} }
int u8u8u16OperandInstruction(const char *name, CChunk *chunk, int offset) { int u8u8u16OperandInstruction(const char *name, CChunk *chunk, int offset)
printf("%-16s [%03d] [%03d] [%05d]", name, readu8Chunk(chunk, offset + 1), readu8Chunk(chunk, offset + 2), readu16Chunk(chunk, offset + 3)); {
printf("%-16s [%03d] [%03d] [%05d]", name, readu8Chunk(chunk, offset + 1),
readu8Chunk(chunk, offset + 2), readu16Chunk(chunk, offset + 3));
return offset + 5; // op + u8 + u8 + u16 return offset + 5; // op + u8 + u8 + u16
} }
int constInstruction(const char *name, CChunk *chunk, int offset) { int constInstruction(const char *name, CChunk *chunk, int offset)
{
int index = readu16Chunk(chunk, offset + 1); int index = readu16Chunk(chunk, offset + 1);
printf("%-16s [%05d] - ", name, index); printf("%-16s [%05d] - ", name, index);
CValue val = chunk->constants.values[index]; CValue val = chunk->constants.values[index];
@ -55,7 +68,8 @@ int constInstruction(const char *name, CChunk *chunk, int offset) {
// 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)
{
printIndent(indent); printIndent(indent);
printf("===[[ disasm for %s ]]===\n", name); printf("===[[ disasm for %s ]]===\n", name);
@ -65,7 +79,8 @@ void disasmChunk(CChunk *chunk, const char *name, int indent) {
} }
} }
int disasmInstr(CChunk *chunk, int offset, int indent) { int disasmInstr(CChunk *chunk, int offset, int indent)
{
printIndent(indent); printIndent(indent);
printf("%04d ", offset); printf("%04d ", offset);
@ -109,7 +124,7 @@ int disasmInstr(CChunk *chunk, int offset, int indent) {
int index = readu16Chunk(chunk, offset + 1); int index = readu16Chunk(chunk, offset + 1);
printf("%-16s [%05d] - ", "OP_CLOSURE", index); printf("%-16s [%05d] - ", "OP_CLOSURE", index);
CValue val = chunk->constants.values[index]; CValue val = chunk->constants.values[index];
CObjFunction *cobjFunc = (CObjFunction*)cosmoV_readRef(val); CObjFunction *cobjFunc = (CObjFunction *)cosmoV_readRef(val);
offset += 3; // we consumed the opcode + u16 offset += 3; // we consumed the opcode + u16
printValue(val); printValue(val);
@ -124,7 +139,8 @@ int disasmInstr(CChunk *chunk, int offset, int indent) {
} }
// print the chunk // print the chunk
disasmChunk(&cobjFunc->chunk, cobjFunc->name == NULL ? UNNAMEDCHUNK : cobjFunc->name->str, indent+1); disasmChunk(&cobjFunc->chunk, cobjFunc->name == NULL ? UNNAMEDCHUNK : cobjFunc->name->str,
indent + 1);
return offset; return offset;
} }
case OP_CLOSE: case OP_CLOSE:
@ -204,6 +220,5 @@ int disasmInstr(CChunk *chunk, int offset, int indent) {
return 1; return 1;
} }
return 1; return 1;
} }

View File

@ -1,72 +1,81 @@
#include "clex.h" #include "clex.h"
#include "cmem.h" #include "cmem.h"
#include <string.h> #include <string.h>
CReservedWord reservedWords[] = { CReservedWord reservedWords[] = {
{TOKEN_AND, "and", 3}, { TOKEN_AND, "and", 3},
{TOKEN_BREAK, "break", 5}, { TOKEN_BREAK, "break", 5},
{TOKEN_CONTINUE, "continue", 8}, {TOKEN_CONTINUE, "continue", 8},
{TOKEN_DO, "do", 2}, { TOKEN_DO, "do", 2},
{TOKEN_ELSE, "else", 4}, { TOKEN_ELSE, "else", 4},
{TOKEN_ELSEIF, "elseif", 6}, { TOKEN_ELSEIF, "elseif", 6},
{TOKEN_END, "end", 3}, { TOKEN_END, "end", 3},
{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_IF, "if", 2}, { TOKEN_IF, "if", 2},
{TOKEN_IN, "in", 2}, { TOKEN_IN, "in", 2},
{TOKEN_LOCAL, "local", 5}, { TOKEN_LOCAL, "local", 5},
{TOKEN_NIL, "nil", 3}, { TOKEN_NIL, "nil", 3},
{TOKEN_NOT, "not", 3}, { TOKEN_NOT, "not", 3},
{TOKEN_OR, "or", 2}, { TOKEN_OR, "or", 2},
{TOKEN_PROTO, "proto", 5}, { TOKEN_PROTO, "proto", 5},
{TOKEN_RETURN, "return", 6}, { TOKEN_RETURN, "return", 6},
{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_WHILE, "while", 5} { TOKEN_WHILE, "while", 5}
}; };
// returns true if current token is a heap allocated buffer // returns true if current token is a heap allocated buffer
static bool isBuffer(CLexState *state) { static bool isBuffer(CLexState *state)
{
return state->buffer != NULL; return state->buffer != NULL;
} }
// marks the current token as heap allocated & allocates the buffer // marks the current token as heap allocated & allocates the buffer
static void makeBuffer(CLexState *state) { static void makeBuffer(CLexState *state)
state->buffer = cosmoM_xmalloc(state->cstate, sizeof(char) * 32); // start with a 32 character long buffer {
state->buffer =
cosmoM_xmalloc(state->cstate, sizeof(char) * 32); // start with a 32 character long buffer
state->bufCount = 0; state->bufCount = 0;
state->bufCap = 32; state->bufCap = 32;
} }
static void resetBuffer(CLexState *state) { static void resetBuffer(CLexState *state)
{
state->buffer = NULL; state->buffer = NULL;
state->bufCount = 0; state->bufCount = 0;
state->bufCap = 0; state->bufCap = 0;
} }
// cancels the token heap buffer and frees it // cancels the token heap buffer and frees it
static void freeBuffer(CLexState *state) { static void freeBuffer(CLexState *state)
{
cosmoM_freearray(state->cstate, char, state->buffer, state->bufCap); cosmoM_freearray(state->cstate, char, state->buffer, state->bufCap);
resetBuffer(state); resetBuffer(state);
} }
// adds character to buffer // adds character to buffer
static void appendBuffer(CLexState *state, char c) { static void appendBuffer(CLexState *state, char c)
{
cosmoM_growarray(state->cstate, char, state->buffer, state->bufCount, state->bufCap); cosmoM_growarray(state->cstate, char, state->buffer, state->bufCount, state->bufCap);
state->buffer[state->bufCount++] = c; state->buffer[state->bufCount++] = c;
} }
// saves the current character to the buffer, grows the buffer as needed // saves the current character to the buffer, grows the buffer as needed
static void saveBuffer(CLexState *state) { static void saveBuffer(CLexState *state)
{
appendBuffer(state, *state->currentChar); appendBuffer(state, *state->currentChar);
} }
// resets the lex state buffer & returns the allocated buffer as a null terminated string // resets the lex state buffer & returns the allocated buffer as a null terminated string
static char *cutBuffer(CLexState *state, int *length) { static char *cutBuffer(CLexState *state, int *length)
{
// append the null terminator // append the null terminator
appendBuffer(state, '\0'); appendBuffer(state, '\0');
@ -84,7 +93,8 @@ static char *cutBuffer(CLexState *state, int *length) {
return cosmoM_reallocate(state->cstate, buf, cap, count); return cosmoM_reallocate(state->cstate, buf, cap, count);
} }
static CToken makeToken(CLexState *state, CTokenType type) { static CToken makeToken(CLexState *state, CTokenType type)
{
CToken token; CToken token;
token.type = type; token.type = type;
token.line = state->line; token.line = state->line;
@ -101,10 +111,11 @@ static CToken makeToken(CLexState *state, CTokenType type) {
return token; return token;
} }
static CToken makeError(CLexState *state, const char *msg) { static CToken makeError(CLexState *state, const char *msg)
{
CToken token; CToken token;
token.type = TOKEN_ERROR; token.type = TOKEN_ERROR;
token.start = (char*)msg; token.start = (char *)msg;
token.length = strlen(msg); token.length = strlen(msg);
token.line = state->line; token.line = state->line;
@ -114,19 +125,23 @@ static CToken makeError(CLexState *state, const char *msg) {
return token; return token;
} }
static inline bool isEnd(CLexState *state) { static inline bool isEnd(CLexState *state)
{
return *state->currentChar == '\0'; return *state->currentChar == '\0';
} }
static inline bool isNumerical(char c) { static inline bool isNumerical(char c)
{
return c >= '0' && c <= '9'; return c >= '0' && c <= '9';
} }
static bool isAlpha(char c) { static bool isAlpha(char c)
{
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'; // identifiers can have '_' return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'; // identifiers can have '_'
} }
static bool match(CLexState *state, char expected) { static bool match(CLexState *state, char expected)
{
if (isEnd(state) || *state->currentChar != expected) if (isEnd(state) || *state->currentChar != expected)
return false; return false;
@ -135,35 +150,41 @@ static bool match(CLexState *state, char expected) {
return true; return true;
} }
char peek(CLexState *state) { char peek(CLexState *state)
{
return *state->currentChar; return *state->currentChar;
} }
static char peekNext(CLexState *state) { static char peekNext(CLexState *state)
{
if (isEnd(state)) if (isEnd(state))
return '\0'; return '\0';
return state->currentChar[1]; return state->currentChar[1];
} }
char next(CLexState *state) { char next(CLexState *state)
{
if (isEnd(state)) if (isEnd(state))
return '\0'; // return a null terminator return '\0'; // return a null terminator
state->currentChar++; state->currentChar++;
return state->currentChar[-1]; return state->currentChar[-1];
} }
bool isHex(char c) { bool isHex(char c)
{
return isNumerical(c) || ('A' <= c && 'F' >= c) || ('a' <= c && 'f' >= c); return isNumerical(c) || ('A' <= c && 'F' >= c) || ('a' <= c && 'f' >= c);
} }
CTokenType identifierType(CLexState *state) { CTokenType identifierType(CLexState *state)
{
int length = state->currentChar - state->startChar; int length = state->currentChar - state->startChar;
// check against reserved word list // check against reserved word list
for (size_t i = 0; i < sizeof(reservedWords) / sizeof(CReservedWord); i++) { for (size_t i = 0; i < sizeof(reservedWords) / sizeof(CReservedWord); i++) {
// it matches the reserved word // it matches the reserved word
if (reservedWords[i].len == length && memcmp(state->startChar, reservedWords[i].word, length) == 0) if (reservedWords[i].len == length &&
memcmp(state->startChar, reservedWords[i].word, length) == 0)
return reservedWords[i].type; return reservedWords[i].type;
} }
@ -171,7 +192,8 @@ CTokenType identifierType(CLexState *state) {
return TOKEN_IDENTIFIER; return TOKEN_IDENTIFIER;
} }
void skipWhitespace(CLexState *state) { void skipWhitespace(CLexState *state)
{
while (true) { while (true) {
char c = peek(state); char c = peek(state);
switch (c) { switch (c) {
@ -184,14 +206,19 @@ void skipWhitespace(CLexState *state) {
break; break;
case '/': // consume comments case '/': // consume comments
if (peekNext(state) == '/') { if (peekNext(state) == '/') {
// skip to next line (also let \n be consumed on the next iteration to properly handle that) // skip to next line (also let \n be consumed on the next iteration to properly
while (!isEnd(state) && peek(state) != '\n') // if it's not a newline or the end of the source // handle that)
while (!isEnd(state) &&
peek(state) != '\n') // if it's not a newline or the end of the source
next(state); next(state);
// keep consuming whitespace // keep consuming whitespace
break; break;
} else if (peekNext(state) == '*') { // multiline comments } else if (peekNext(state) == '*') { // multiline comments
while (!isEnd(state) && !(peek(state) == '*' && peekNext(state) == '/')) // if it's the end of the comment or the end of the source while (!isEnd(state) &&
!(peek(state) == '*' &&
peekNext(state) ==
'/')) // if it's the end of the comment or the end of the source
next(state); next(state);
// consume the '*/' // consume the '*/'
@ -208,7 +235,8 @@ void skipWhitespace(CLexState *state) {
} }
} }
CToken parseString(CLexState *state) { CToken parseString(CLexState *state)
{
makeBuffer(state); // buffer mode makeBuffer(state); // buffer mode
while (peek(state) != '"' && !isEnd(state)) { while (peek(state) != '"' && !isEnd(state)) {
switch (peek(state)) { switch (peek(state)) {
@ -218,10 +246,19 @@ CToken parseString(CLexState *state) {
next(state); // consume the '\' character next(state); // consume the '\' character
switch (peek(state)) { switch (peek(state)) {
case 'r': case 'n': appendBuffer(state, '\n'); break; case 'r':
case 't': appendBuffer(state, '\t'); break; case 'n':
case '\\': appendBuffer(state, '\\'); break; appendBuffer(state, '\n');
case '"': appendBuffer(state, '"'); break; break;
case 't':
appendBuffer(state, '\t');
break;
case '\\':
appendBuffer(state, '\\');
break;
case '"':
appendBuffer(state, '"');
break;
case 'x': // hexadecimal character encoding case 'x': // hexadecimal character encoding
next(state); // skip 'x' next(state); // skip 'x'
@ -282,7 +319,8 @@ CToken parseString(CLexState *state) {
break; break;
} }
return makeError(state, "Unknown special character!"); // TODO: maybe a more descriptive error? return makeError(
state, "Unknown special character!"); // TODO: maybe a more descriptive error?
} }
} }
@ -303,7 +341,8 @@ CToken parseString(CLexState *state) {
return makeToken(state, TOKEN_STRING); return makeToken(state, TOKEN_STRING);
} }
CToken parseNumber(CLexState *state) { CToken parseNumber(CLexState *state)
{
switch (peek(state)) { switch (peek(state)) {
case 'x': // hexadecimal number case 'x': // hexadecimal number
next(state); next(state);
@ -325,7 +364,6 @@ CToken parseNumber(CLexState *state) {
// if it is a number, fall through and parse normally // if it is a number, fall through and parse normally
} }
// consume number // consume number
while (isNumerical(peek(state))) { while (isNumerical(peek(state))) {
next(state); next(state);
@ -342,7 +380,8 @@ CToken parseNumber(CLexState *state) {
return makeToken(state, TOKEN_NUMBER); return makeToken(state, TOKEN_NUMBER);
} }
CToken parseIdentifier(CLexState *state) { CToken parseIdentifier(CLexState *state)
{
// read literal // read literal
while ((isAlpha(peek(state)) || isNumerical(peek(state))) && !isEnd(state)) while ((isAlpha(peek(state)) || isNumerical(peek(state))) && !isEnd(state))
next(state); next(state);
@ -350,10 +389,11 @@ CToken parseIdentifier(CLexState *state) {
return makeToken(state, identifierType(state)); // is it a reserved word? return makeToken(state, identifierType(state)); // is it a reserved word?
} }
CLexState *cosmoL_newLexState(CState *cstate, const char *source) { CLexState *cosmoL_newLexState(CState *cstate, const char *source)
{
CLexState *state = cosmoM_xmalloc(cstate, sizeof(CLexState)); CLexState *state = cosmoM_xmalloc(cstate, sizeof(CLexState));
state->startChar = (char*)source; state->startChar = (char *)source;
state->currentChar = (char*)source; state->currentChar = (char *)source;
state->line = 1; state->line = 1;
state->lastLine = 0; state->lastLine = 0;
state->lastType = TOKEN_ERROR; state->lastType = TOKEN_ERROR;
@ -364,11 +404,13 @@ CLexState *cosmoL_newLexState(CState *cstate, const char *source) {
return state; return state;
} }
void cosmoL_freeLexState(CState *state, CLexState *lstate) { void cosmoL_freeLexState(CState *state, CLexState *lstate)
{
cosmoM_free(state, CLexState, lstate); cosmoM_free(state, CLexState, lstate);
} }
CToken cosmoL_scanToken(CLexState *state) { CToken cosmoL_scanToken(CLexState *state)
{
skipWhitespace(state); skipWhitespace(state);
state->startChar = state->currentChar; state->startChar = state->currentChar;
@ -380,37 +422,59 @@ CToken cosmoL_scanToken(CLexState *state) {
switch (c) { switch (c) {
// single character tokens // single character tokens
case '(': return makeToken(state, TOKEN_LEFT_PAREN); case '(':
case ')': return makeToken(state, TOKEN_RIGHT_PAREN); return makeToken(state, TOKEN_LEFT_PAREN);
case '{': return makeToken(state, TOKEN_LEFT_BRACE); case ')':
case '}': return makeToken(state, TOKEN_RIGHT_BRACE); return makeToken(state, TOKEN_RIGHT_PAREN);
case '[': return makeToken(state, TOKEN_LEFT_BRACKET); case '{':
case ']': return makeToken(state, TOKEN_RIGHT_BRACKET); return makeToken(state, TOKEN_LEFT_BRACE);
case ';': return makeToken(state, TOKEN_EOS); case '}':
case ',': return makeToken(state, TOKEN_COMMA); return makeToken(state, TOKEN_RIGHT_BRACE);
case ':': return makeToken(state, TOKEN_COLON); case '[':
case '*': return makeToken(state, TOKEN_STAR); return makeToken(state, TOKEN_LEFT_BRACKET);
case '%': return makeToken(state, TOKEN_PERCENT); case ']':
case '^': return makeToken(state, TOKEN_CARROT); return makeToken(state, TOKEN_RIGHT_BRACKET);
case '#': return makeToken(state, TOKEN_POUND); case ';':
case '/': return makeToken(state, TOKEN_SLASH); return makeToken(state, TOKEN_EOS);
case ',':
return makeToken(state, TOKEN_COMMA);
case ':':
return makeToken(state, TOKEN_COLON);
case '*':
return makeToken(state, TOKEN_STAR);
case '%':
return makeToken(state, TOKEN_PERCENT);
case '^':
return makeToken(state, TOKEN_CARROT);
case '#':
return makeToken(state, TOKEN_POUND);
case '/':
return makeToken(state, TOKEN_SLASH);
// two character tokens // two character tokens
case '+': case '+':
return match(state, '+') ? makeToken(state, TOKEN_PLUS_PLUS) : makeToken(state, TOKEN_PLUS); return match(state, '+') ? makeToken(state, TOKEN_PLUS_PLUS) : makeToken(state, TOKEN_PLUS);
case '-': case '-':
return match(state, '-') ? makeToken(state, TOKEN_MINUS_MINUS) : makeToken(state, TOKEN_MINUS); return match(state, '-') ? makeToken(state, TOKEN_MINUS_MINUS)
: makeToken(state, TOKEN_MINUS);
case '.': case '.':
return match(state, '.') ? (match(state, '.') ? makeToken(state, TOKEN_DOT_DOT_DOT) : makeToken(state, TOKEN_DOT_DOT)) : makeToken(state, TOKEN_DOT); return match(state, '.') ? (match(state, '.') ? makeToken(state, TOKEN_DOT_DOT_DOT)
: makeToken(state, TOKEN_DOT_DOT))
: makeToken(state, TOKEN_DOT);
case '!': case '!':
return match(state, '=') ? makeToken(state, TOKEN_BANG_EQUAL) : makeToken(state, TOKEN_BANG); return match(state, '=') ? makeToken(state, TOKEN_BANG_EQUAL)
: makeToken(state, TOKEN_BANG);
case '=': case '=':
return match(state, '=') ? makeToken(state, TOKEN_EQUAL_EQUAL) : makeToken(state, TOKEN_EQUAL); return match(state, '=') ? makeToken(state, TOKEN_EQUAL_EQUAL)
: makeToken(state, TOKEN_EQUAL);
case '>': case '>':
return match(state, '=') ? makeToken(state, TOKEN_GREATER_EQUAL) : makeToken(state, TOKEN_GREATER); return match(state, '=') ? makeToken(state, TOKEN_GREATER_EQUAL)
: makeToken(state, TOKEN_GREATER);
case '<': case '<':
return match(state, '=') ? makeToken(state, TOKEN_LESS_EQUAL) : makeToken(state, TOKEN_LESS); return match(state, '=') ? makeToken(state, TOKEN_LESS_EQUAL)
: makeToken(state, TOKEN_LESS);
// literals // literals
case '"': return parseString(state); case '"':
return parseString(state);
default: default:
if (isNumerical(c)) if (isNumerical(c))
return parseNumber(state); return parseNumber(state);

View File

@ -3,7 +3,8 @@
#include "cosmo.h" #include "cosmo.h"
typedef enum { typedef enum
{
// single character tokens // single character tokens
TOKEN_LEFT_PAREN, TOKEN_LEFT_PAREN,
TOKEN_RIGHT_PAREN, TOKEN_RIGHT_PAREN,
@ -72,23 +73,27 @@ typedef enum {
TOKEN_EOF TOKEN_EOF
} CTokenType; } CTokenType;
typedef struct { typedef struct
{
CTokenType type; CTokenType type;
const char *word; const char *word;
int len; int len;
} CReservedWord; } CReservedWord;
typedef struct { typedef struct
{
CTokenType type; CTokenType type;
char *start; char *start;
int length; int length;
int line; int line;
} CToken; } CToken;
typedef struct { typedef struct
{
char *currentChar; char *currentChar;
char *startChar; char *startChar;
char *buffer; // if non-NULL & bufCount > 0, token->start & token->length will be set to buffer & bufCount respectively char *buffer; // if non-NULL & bufCount > 0, token->start & token->length will be set to buffer
// & bufCount respectively
size_t bufCount; size_t bufCount;
size_t bufCap; size_t bufCap;
int line; // current line int line; // current line

View File

@ -1,13 +1,15 @@
#include "cmem.h" #include "cmem.h"
#include "cstate.h"
#include "cvalue.h"
#include "ctable.h"
#include "cparse.h"
#include "cobj.h"
#include "cbaselib.h" #include "cbaselib.h"
#include "cobj.h"
#include "cparse.h"
#include "cstate.h"
#include "ctable.h"
#include "cvalue.h"
// realloc wrapper // realloc wrapper
void *cosmoM_reallocate(CState* state, void *buf, size_t oldSize, size_t newSize) { void *cosmoM_reallocate(CState *state, void *buf, size_t oldSize, size_t newSize)
{
state->allocatedBytes += newSize - oldSize; state->allocatedBytes += newSize - oldSize;
if (newSize == 0) { // it needs to be freed if (newSize == 0) { // it needs to be freed
@ -19,11 +21,11 @@ void *cosmoM_reallocate(CState* state, void *buf, size_t oldSize, size_t newSize
if (!(cosmoM_isFrozen(state)) && newSize > oldSize) { if (!(cosmoM_isFrozen(state)) && newSize > oldSize) {
cosmoM_collectGarbage(state); cosmoM_collectGarbage(state);
} }
#ifdef GC_DEBUG # ifdef GC_DEBUG
else { else {
printf("GC event ignored! state frozen! [%d]\n", state->freezeGC); printf("GC event ignored! state frozen! [%d]\n", state->freezeGC);
} }
#endif # endif
#else #else
cosmoM_checkGarbage(state, 0); cosmoM_checkGarbage(state, 0);
#endif #endif
@ -39,7 +41,8 @@ void *cosmoM_reallocate(CState* state, void *buf, size_t oldSize, size_t newSize
return newBuf; return newBuf;
} }
COSMO_API bool cosmoM_checkGarbage(CState *state, size_t needed) { COSMO_API bool cosmoM_checkGarbage(CState *state, size_t needed)
{
if (!(cosmoM_isFrozen(state)) && state->allocatedBytes + needed > state->nextGC) { if (!(cosmoM_isFrozen(state)) && state->allocatedBytes + needed > state->nextGC) {
cosmoM_collectGarbage(state); // cya lol cosmoM_collectGarbage(state); // cya lol
return true; return true;
@ -51,7 +54,8 @@ COSMO_API bool cosmoM_checkGarbage(CState *state, size_t needed) {
void markObject(CState *state, CObj *obj); void markObject(CState *state, CObj *obj);
void markValue(CState *state, CValue val); void markValue(CState *state, CValue val);
void markTable(CState *state, CTable *tbl) { void markTable(CState *state, CTable *tbl)
{
if (tbl->table == NULL) // table is still being initialized if (tbl->table == NULL) // table is still being initialized
return; return;
@ -64,14 +68,17 @@ void markTable(CState *state, CTable *tbl) {
} }
// frees white members from the table // frees white members from the table
void tableRemoveWhite(CState *state, CTable *tbl) { void tableRemoveWhite(CState *state, CTable *tbl)
{
if (tbl->table == NULL) // table is still being initialized if (tbl->table == NULL) // table is still being initialized
return; return;
int cap = tbl->capacityMask + 1; int cap = tbl->capacityMask + 1;
for (int i = 0; i < cap; i++) { for (int i = 0; i < cap; i++) {
CTableEntry *entry = &tbl->table[i]; CTableEntry *entry = &tbl->table[i];
if (IS_REF(entry->key) && !(cosmoV_readRef(entry->key))->isMarked) { // if the key is a object and it's white (unmarked), remove it from the table if (IS_REF(entry->key) &&
!(cosmoV_readRef(entry->key))->isMarked) { // if the key is a object and it's white
// (unmarked), remove it from the table
cosmoT_remove(state, tbl, entry->key); cosmoT_remove(state, tbl, entry->key);
} }
} }
@ -79,7 +86,8 @@ void tableRemoveWhite(CState *state, CTable *tbl) {
cosmoT_checkShrink(state, tbl); // recovers the memory we're no longer using cosmoT_checkShrink(state, tbl); // recovers the memory we're no longer using
} }
void markArray(CState *state, CValueArray *array) { void markArray(CState *state, CValueArray *array)
{
for (size_t i = 0; i < array->count; i++) { for (size_t i = 0; i < array->count; i++) {
markValue(state, array->values[i]); markValue(state, array->values[i]);
} }
@ -87,8 +95,9 @@ void markArray(CState *state, CValueArray *array) {
// mark all references associated with the object // mark all references associated with the object
// black = keep, white = discard // black = keep, white = discard
void blackenObject(CState *state, CObj *obj) { void blackenObject(CState *state, CObj *obj)
markObject(state, (CObj*)obj->proto); {
markObject(state, (CObj *)obj->proto);
switch (obj->type) { switch (obj->type) {
case COBJ_STRING: case COBJ_STRING:
case COBJ_CFUNCTION: case COBJ_CFUNCTION:
@ -96,63 +105,64 @@ 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
CObjObject *cobj = (CObjObject*)obj; CObjObject *cobj = (CObjObject *)obj;
markTable(state, &cobj->tbl); markTable(state, &cobj->tbl);
break; break;
} }
case COBJ_TABLE: { // tables are just wrappers for CTable case COBJ_TABLE: { // tables are just wrappers for CTable
CObjTable *tbl = (CObjTable*)obj; CObjTable *tbl = (CObjTable *)obj;
markTable(state, &tbl->tbl); markTable(state, &tbl->tbl);
break; break;
} }
case COBJ_UPVALUE: { case COBJ_UPVALUE: {
markValue(state, ((CObjUpval*)obj)->closed); markValue(state, ((CObjUpval *)obj)->closed);
break; break;
} }
case COBJ_FUNCTION: { case COBJ_FUNCTION: {
CObjFunction *func = (CObjFunction*)obj; CObjFunction *func = (CObjFunction *)obj;
markObject(state, (CObj*)func->name); markObject(state, (CObj *)func->name);
markObject(state, (CObj*)func->module); markObject(state, (CObj *)func->module);
markArray(state, &func->chunk.constants); markArray(state, &func->chunk.constants);
break; break;
} }
case COBJ_METHOD: { case COBJ_METHOD: {
CObjMethod *method = (CObjMethod*)obj; CObjMethod *method = (CObjMethod *)obj;
markValue(state, method->func); markValue(state, method->func);
markObject(state, (CObj*)method->obj); markObject(state, (CObj *)method->obj);
break; break;
} }
case COBJ_ERROR: { case COBJ_ERROR: {
CObjError *err = (CObjError*)obj; CObjError *err = (CObjError *)obj;
markValue(state, err->err); markValue(state, err->err);
// mark callframes // mark callframes
for (int i = 0; i < err->frameCount; i++) for (int i = 0; i < err->frameCount; i++)
markObject(state, (CObj*)err->frames[i].closure); markObject(state, (CObj *)err->frames[i].closure);
break; 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);
// mark all upvalues // mark all upvalues
for (int i = 0; i < closure->upvalueCount; i++) { for (int i = 0; i < closure->upvalueCount; i++) {
markObject(state, (CObj*)closure->upvalues[i]); markObject(state, (CObj *)closure->upvalues[i]);
} }
break; break;
} }
default: default:
#ifdef GC_DEBUG #ifdef GC_DEBUG
printf("Unknown type in blackenObject with %p, type %d\n", (void*)obj, obj->type); printf("Unknown type in blackenObject with %p, type %d\n", (void *)obj, obj->type);
#endif #endif
break; break;
} }
} }
void markObject(CState *state, CObj *obj) { void markObject(CState *state, CObj *obj)
{
if (obj == NULL || obj->isMarked) // skip if NULL or already marked if (obj == NULL || obj->isMarked) // skip if NULL or already marked
return; return;
@ -169,25 +179,29 @@ void markObject(CState *state, CObj *obj) {
return; return;
// we can use cosmoM_growarray because we lock the GC when we entered in cosmoM_collectGarbage // we can use cosmoM_growarray because we lock the GC when we entered in cosmoM_collectGarbage
cosmoM_growarray(state, CObj*, state->grayStack.array, state->grayStack.count, state->grayStack.capacity); cosmoM_growarray(state, CObj *, state->grayStack.array, state->grayStack.count,
state->grayStack.capacity);
state->grayStack.array[state->grayStack.count++] = obj; state->grayStack.array[state->grayStack.count++] = obj;
} }
void markValue(CState *state, CValue val) { void markValue(CState *state, CValue val)
{
if (IS_REF(val)) if (IS_REF(val))
markObject(state, cosmoV_readRef(val)); markObject(state, cosmoV_readRef(val));
} }
// trace our gray references // trace our gray references
void traceGrays(CState *state) { void traceGrays(CState *state)
{
while (state->grayStack.count > 0) { while (state->grayStack.count > 0) {
CObj* obj = state->grayStack.array[--state->grayStack.count]; CObj *obj = state->grayStack.array[--state->grayStack.count];
blackenObject(state, obj); blackenObject(state, obj);
} }
} }
void sweep(CState *state) { void sweep(CState *state)
{
CObj *prev = NULL; CObj *prev = NULL;
CObj *object = state->objects; CObj *object = state->objects;
while (object != NULL) { while (object != NULL) {
@ -210,7 +224,8 @@ void sweep(CState *state) {
} }
} }
void markUserRoots(CState *state) { void markUserRoots(CState *state)
{
CObj *root = state->userRoots; CObj *root = state->userRoots;
// traverse userRoots and mark all the object // traverse userRoots and mark all the object
@ -220,7 +235,8 @@ void markUserRoots(CState *state) {
} }
} }
void markRoots(CState *state) { void markRoots(CState *state)
{
// mark all values on the stack // mark all values on the stack
for (StkPtr value = state->stack; value < state->top; value++) { for (StkPtr value = state->stack; value < state->top; value++) {
markValue(state, *value); markValue(state, *value);
@ -228,33 +244,34 @@ void markRoots(CState *state) {
// mark all active callframe closures // mark all active callframe closures
for (int i = 0; i < state->frameCount; i++) { for (int i = 0; i < state->frameCount; i++) {
markObject(state, (CObj*)state->callFrame[i].closure); markObject(state, (CObj *)state->callFrame[i].closure);
} }
// mark all open upvalues // mark all open upvalues
for (CObjUpval *upvalue = state->openUpvalues; upvalue != NULL; upvalue = upvalue->next) { for (CObjUpval *upvalue = state->openUpvalues; upvalue != NULL; upvalue = upvalue->next) {
markObject(state, (CObj*)upvalue); markObject(state, (CObj *)upvalue);
} }
markObject(state, (CObj*)state->globals); markObject(state, (CObj *)state->globals);
// mark all internal strings // mark all internal strings
for (int i = 0; i < ISTRING_MAX; i++) for (int i = 0; i < ISTRING_MAX; i++)
markObject(state, (CObj*)state->iStrings[i]); markObject(state, (CObj *)state->iStrings[i]);
// mark the user defined roots // mark the user defined roots
markUserRoots(state); markUserRoots(state);
// mark other misc. internally reserved objects // mark other misc. internally reserved objects
markObject(state, (CObj*)state->error); markObject(state, (CObj *)state->error);
for (int i = 0; i < COBJ_MAX; i++) for (int i = 0; i < COBJ_MAX; i++)
markObject(state, (CObj*)state->protoObjects[i]); markObject(state, (CObj *)state->protoObjects[i]);
traceGrays(state); traceGrays(state);
} }
COSMO_API void cosmoM_collectGarbage(CState *state) { COSMO_API void cosmoM_collectGarbage(CState *state)
{
#ifdef GC_DEBUG #ifdef GC_DEBUG
printf("-- GC start\n"); printf("-- GC start\n");
size_t start = state->allocatedBytes; size_t start = state->allocatedBytes;
@ -263,26 +280,32 @@ COSMO_API void cosmoM_collectGarbage(CState *state) {
markRoots(state); markRoots(state);
tableRemoveWhite(state, &state->strings); // make sure we aren't referencing any strings that are about to be freed tableRemoveWhite(
state,
&state->strings); // make sure we aren't referencing any strings that are about to be freed
// now finally, free all the unmarked objects // now finally, free all the unmarked objects
sweep(state); sweep(state);
// set our next GC event // set our next GC event
cosmoM_updateThreshhold(state); cosmoM_updateThreshhold(state);
state->freezeGC--; // we don't want to use cosmoM_unfreezeGC because that might trigger a GC event (if GC_STRESS is defined) state->freezeGC--; // we don't want to use cosmoM_unfreezeGC because that might trigger a GC
// event (if GC_STRESS is defined)
#ifdef GC_DEBUG #ifdef GC_DEBUG
printf("-- GC end, reclaimed %ld bytes (started at %ld, ended at %ld), next garbage collection scheduled at %ld bytes\n", printf("-- GC end, reclaimed %ld bytes (started at %ld, ended at %ld), next garbage collection "
"scheduled at %ld bytes\n",
start - state->allocatedBytes, start, state->allocatedBytes, state->nextGC); start - state->allocatedBytes, start, state->allocatedBytes, state->nextGC);
getchar(); // pauses execution getchar(); // pauses execution
#endif #endif
} }
COSMO_API void cosmoM_updateThreshhold(CState *state) { COSMO_API void cosmoM_updateThreshhold(CState *state)
{
state->nextGC = state->allocatedBytes * HEAP_GROW_FACTOR; state->nextGC = state->allocatedBytes * HEAP_GROW_FACTOR;
} }
COSMO_API void cosmoM_addRoot(CState *state, CObj *newRoot) { COSMO_API void cosmoM_addRoot(CState *state, CObj *newRoot)
{
// first, check and make sure this root doesn't already exist in the list // first, check and make sure this root doesn't already exist in the list
CObj *root = state->userRoots; CObj *root = state->userRoots;
while (root != NULL) { while (root != NULL) {
@ -297,7 +320,8 @@ COSMO_API void cosmoM_addRoot(CState *state, CObj *newRoot) {
state->userRoots = newRoot; state->userRoots = newRoot;
} }
COSMO_API void cosmoM_removeRoot(CState *state, CObj *oldRoot) { COSMO_API void cosmoM_removeRoot(CState *state, CObj *oldRoot)
{
CObj *prev = NULL; CObj *prev = NULL;
CObj *root = state->userRoots; CObj *root = state->userRoots;

View File

@ -2,22 +2,22 @@
#define CMEME_C // meme lol #define CMEME_C // meme lol
#include "cosmo.h" #include "cosmo.h"
#include "cstate.h" #include "cstate.h"
//#define GC_STRESS // #define GC_STRESS
//#define GC_DEBUG // #define GC_DEBUG
// arrays *must* grow by a factor of 2 // arrays *must* grow by a factor of 2
#define GROW_FACTOR 2 #define GROW_FACTOR 2
#define HEAP_GROW_FACTOR 2 #define HEAP_GROW_FACTOR 2
#define ARRAY_START 8 #define ARRAY_START 8
#ifdef GC_DEBUG #ifdef GC_DEBUG
#define cosmoM_freearray(state, type, buf, capacity) \ # define cosmoM_freearray(state, type, buf, capacity) \
printf("freeing array %p [size %lu] at %s:%d\n", buf, sizeof(type) * capacity, __FILE__, __LINE__); \ printf("freeing array %p [size %lu] at %s:%d\n", buf, sizeof(type) * capacity, __FILE__, \
__LINE__); \
cosmoM_reallocate(state, buf, sizeof(type) * capacity, 0) cosmoM_reallocate(state, buf, sizeof(type) * capacity, 0)
#else #else
#define cosmoM_freearray(state, type, buf, capacity) \ # define cosmoM_freearray(state, type, buf, capacity) \
cosmoM_reallocate(state, buf, sizeof(type) * capacity, 0) cosmoM_reallocate(state, buf, sizeof(type) * capacity, 0)
#endif #endif
@ -25,46 +25,44 @@
if (count >= capacity || buf == NULL) { \ if (count >= capacity || buf == NULL) { \
int old = capacity; \ int old = capacity; \
capacity = old * GROW_FACTOR; \ capacity = old * GROW_FACTOR; \
buf = (type*)cosmoM_reallocate(state, buf, sizeof(type) *old, sizeof(type) *capacity); \ buf = (type *)cosmoM_reallocate(state, buf, sizeof(type) * old, sizeof(type) * capacity); \
} }
#ifdef GC_DEBUG #ifdef GC_DEBUG
#define cosmoM_free(state, type, x) \ # define cosmoM_free(state, type, x) \
printf("freeing %p [size %lu] at %s:%d\n", x, sizeof(type), __FILE__, __LINE__); \ printf("freeing %p [size %lu] at %s:%d\n", x, sizeof(type), __FILE__, __LINE__); \
cosmoM_reallocate(state, x, sizeof(type), 0) cosmoM_reallocate(state, x, sizeof(type), 0)
#else #else
#define cosmoM_free(state, type, x) \ # define cosmoM_free(state, type, x) cosmoM_reallocate(state, x, sizeof(type), 0)
cosmoM_reallocate(state, x, sizeof(type), 0)
#endif #endif
#define cosmoM_isFrozen(state) \ #define cosmoM_isFrozen(state) (state->freezeGC > 0)
(state->freezeGC > 0)
// if debugging, print the locations of when the state is frozen/unfrozen // if debugging, print the locations of when the state is frozen/unfrozen
#ifdef GC_DEBUG #ifdef GC_DEBUG
#define cosmoM_freezeGC(state) \ # define cosmoM_freezeGC(state) \
state->freezeGC++; \ state->freezeGC++; \
printf("freezing state at %s:%d [%d]\n", __FILE__, __LINE__, state->freezeGC) printf("freezing state at %s:%d [%d]\n", __FILE__, __LINE__, state->freezeGC)
#define cosmoM_unfreezeGC(state) \ # define cosmoM_unfreezeGC(state) \
state->freezeGC--; \ state->freezeGC--; \
printf("unfreezing state at %s:%d [%d]\n", __FILE__, __LINE__, state->freezeGC); \ printf("unfreezing state at %s:%d [%d]\n", __FILE__, __LINE__, state->freezeGC); \
cosmoM_checkGarbage(state, 0) cosmoM_checkGarbage(state, 0)
#else #else
// freeze's the garbage collector until cosmoM_unfreezeGC is called // freeze's the garbage collector until cosmoM_unfreezeGC is called
#define cosmoM_freezeGC(state) \ # define cosmoM_freezeGC(state) state->freezeGC++
state->freezeGC++
// unfreeze's the garbage collector and tries to run a garbage collection cycle // unfreeze's the garbage collector and tries to run a garbage collection cycle
#define cosmoM_unfreezeGC(state) \ # define cosmoM_unfreezeGC(state) \
state->freezeGC--; \ state->freezeGC--; \
cosmoM_checkGarbage(state, 0) cosmoM_checkGarbage(state, 0)
#endif #endif
COSMO_API void *cosmoM_reallocate(CState *state, void *buf, size_t oldSize, size_t newSize); COSMO_API void *cosmoM_reallocate(CState *state, void *buf, size_t oldSize, size_t newSize);
COSMO_API bool cosmoM_checkGarbage(CState *state, size_t needed); // returns true if GC event was triggered COSMO_API bool cosmoM_checkGarbage(CState *state,
size_t needed); // returns true if GC event was triggered
COSMO_API void cosmoM_collectGarbage(CState *state); COSMO_API void cosmoM_collectGarbage(CState *state);
COSMO_API void cosmoM_updateThreshhold(CState *state); COSMO_API void cosmoM_updateThreshhold(CState *state);
@ -75,7 +73,8 @@ COSMO_API void cosmoM_addRoot(CState *state, CObj *newRoot);
COSMO_API void cosmoM_removeRoot(CState *state, CObj *oldRoot); COSMO_API void cosmoM_removeRoot(CState *state, CObj *oldRoot);
// wrapper for cosmoM_reallocate so we can track our memory usage (it's also safer :P) // wrapper for cosmoM_reallocate so we can track our memory usage (it's also safer :P)
static inline void *cosmoM_xmalloc(CState *state, size_t sz) { static inline void *cosmoM_xmalloc(CState *state, size_t sz)
{
return cosmoM_reallocate(state, NULL, 0, sz); return cosmoM_reallocate(state, NULL, 0, sz);
} }

View File

@ -1,27 +1,30 @@
#include "cobj.h"
#include "clex.h"
#include "cmem.h"
#include "cstate.h" #include "cstate.h"
#include "ctable.h" #include "ctable.h"
#include "cobj.h"
#include "cmem.h"
#include "cvm.h" #include "cvm.h"
#include "clex.h"
#include <string.h>
#include <stdarg.h> #include <stdarg.h>
#include <string.h>
#include <unistd.h> #include <unistd.h>
// we don't actually hash the whole string :eyes: // we don't actually hash the whole string :eyes:
uint32_t hashString(const char *str, size_t sz) { uint32_t hashString(const char *str, size_t sz)
{
uint32_t hash = sz; uint32_t hash = sz;
size_t step = (sz>>5)+1; size_t step = (sz >> 5) + 1;
for (size_t i = sz; i >= step; i-=step) for (size_t i = sz; i >= step; i -= step)
hash = ((hash << 5) + (hash>>2)) + str[i-1]; hash = ((hash << 5) + (hash >> 2)) + str[i - 1];
return hash; return hash;
} }
CObj *cosmoO_allocateBase(CState *state, size_t sz, CObjType type) { CObj *cosmoO_allocateBase(CState *state, size_t sz, CObjType type)
CObj* obj = (CObj*)cosmoM_xmalloc(state, sz); {
CObj *obj = (CObj *)cosmoM_xmalloc(state, sz);
obj->type = type; obj->type = type;
obj->isMarked = false; obj->isMarked = false;
obj->proto = state->protoObjects[type]; obj->proto = state->protoObjects[type];
@ -36,33 +39,34 @@ CObj *cosmoO_allocateBase(CState *state, size_t sz, CObjType type) {
return obj; return obj;
} }
void cosmoO_free(CState *state, CObj *obj) { void cosmoO_free(CState *state, CObj *obj)
{
#ifdef GC_DEBUG #ifdef GC_DEBUG
printf("freeing %p [", obj); printf("freeing %p [", obj);
printObject(obj); printObject(obj);
printf("]\n"); printf("]\n");
#endif #endif
switch(obj->type) { switch (obj->type) {
case COBJ_STRING: { case COBJ_STRING: {
CObjString *objStr = (CObjString*)obj; CObjString *objStr = (CObjString *)obj;
cosmoM_freearray(state, char, objStr->str, objStr->length + 1); cosmoM_freearray(state, char, objStr->str, objStr->length + 1);
cosmoM_free(state, CObjString, objStr); cosmoM_free(state, CObjString, objStr);
break; break;
} }
case COBJ_OBJECT: { case COBJ_OBJECT: {
CObjObject *objTbl = (CObjObject*)obj; CObjObject *objTbl = (CObjObject *)obj;
cosmoT_clearTable(state, &objTbl->tbl); cosmoT_clearTable(state, &objTbl->tbl);
cosmoM_free(state, CObjObject, objTbl); cosmoM_free(state, CObjObject, objTbl);
break; break;
} }
case COBJ_STREAM: { case COBJ_STREAM: {
CObjStream *objStrm = (CObjStream*)obj; CObjStream *objStrm = (CObjStream *)obj;
close(objStrm->fd); close(objStrm->fd);
cosmoM_free(state, CObjStream, objStrm); cosmoM_free(state, CObjStream, objStrm);
break; break;
} }
case COBJ_TABLE: { case COBJ_TABLE: {
CObjTable *tbl = (CObjTable*)obj; CObjTable *tbl = (CObjTable *)obj;
cosmoT_clearTable(state, &tbl->tbl); cosmoT_clearTable(state, &tbl->tbl);
cosmoM_free(state, CObjTable, tbl); cosmoM_free(state, CObjTable, tbl);
break; break;
@ -72,7 +76,7 @@ void cosmoO_free(CState *state, CObj *obj) {
break; break;
} }
case COBJ_FUNCTION: { case COBJ_FUNCTION: {
CObjFunction *objFunc = (CObjFunction*)obj; CObjFunction *objFunc = (CObjFunction *)obj;
cleanChunk(state, &objFunc->chunk); cleanChunk(state, &objFunc->chunk);
cosmoM_free(state, CObjFunction, objFunc); cosmoM_free(state, CObjFunction, objFunc);
break; break;
@ -86,27 +90,30 @@ void cosmoO_free(CState *state, CObj *obj) {
break; break;
} }
case COBJ_ERROR: { case COBJ_ERROR: {
CObjError *err = (CObjError*)obj; CObjError *err = (CObjError *)obj;
cosmoM_freearray(state, CCallFrame, err->frames, err->frameCount); cosmoM_freearray(state, CCallFrame, err->frames, err->frameCount);
cosmoM_free(state, CObjError, obj); cosmoM_free(state, CObjError, obj);
break; 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);
cosmoM_free(state, CObjClosure, closure); cosmoM_free(state, CObjClosure, closure);
break; break;
} }
case COBJ_MAX: case COBJ_MAX:
default: { /* stubbed, should never happen */ } default: { /* stubbed, should never happen */
}
} }
} }
bool cosmoO_equal(CState *state, CObj *obj1, CObj *obj2) { bool cosmoO_equal(CState *state, CObj *obj1, CObj *obj2)
{
CObjObject *proto1, *proto2; CObjObject *proto1, *proto2;
CValue eq1, eq2; CValue eq1, eq2;
if (obj1 == obj2) // its the same reference, this compares strings for us since they're interned anyways :) if (obj1 == obj2) // its the same reference, this compares strings for us since they're interned
// anyways :)
return true; return true;
// its not the same type, maybe both <ref>'s have the same '__equal' metamethod in their protos? // its not the same type, maybe both <ref>'s have the same '__equal' metamethod in their protos?
@ -116,28 +123,29 @@ bool cosmoO_equal(CState *state, CObj *obj1, CObj *obj2) {
switch (obj1->type) { switch (obj1->type) {
case COBJ_STRING: { case COBJ_STRING: {
/* /*
we already compared the pointers at the top of the function, this prevents the `__equal` metamethod we already compared the pointers at the top of the function, this prevents the `__equal`
from being checked. If you plan on using `__equal` with strings just remove this case! metamethod from being checked. If you plan on using `__equal` with strings just remove
this case!
*/ */
return false; return false;
} }
case COBJ_CFUNCTION: { case COBJ_CFUNCTION: {
CObjCFunction *cfunc1 = (CObjCFunction*)obj1; CObjCFunction *cfunc1 = (CObjCFunction *)obj1;
CObjCFunction *cfunc2 = (CObjCFunction*)obj2; CObjCFunction *cfunc2 = (CObjCFunction *)obj2;
if (cfunc1->cfunc == cfunc2->cfunc) if (cfunc1->cfunc == cfunc2->cfunc)
return true; return true;
goto _eqFail; goto _eqFail;
} }
case COBJ_METHOD: { case COBJ_METHOD: {
CObjMethod *method1 = (CObjMethod*)obj1; CObjMethod *method1 = (CObjMethod *)obj1;
CObjMethod *method2 = (CObjMethod*)obj2; CObjMethod *method2 = (CObjMethod *)obj2;
if (cosmoV_equal(state, method1->func, method2->func)) if (cosmoV_equal(state, method1->func, method2->func))
return true; return true;
goto _eqFail; goto _eqFail;
} }
case COBJ_CLOSURE: { case COBJ_CLOSURE: {
CObjClosure *closure1 = (CObjClosure*)obj1; CObjClosure *closure1 = (CObjClosure *)obj1;
CObjClosure *closure2 = (CObjClosure*)obj2; CObjClosure *closure2 = (CObjClosure *)obj2;
// we just compare the function pointer // we just compare the function pointer
if (closure1->function == closure2->function) if (closure1->function == closure2->function)
return true; return true;
@ -148,11 +156,17 @@ bool cosmoO_equal(CState *state, CObj *obj1, CObj *obj2) {
} }
_eqFail: _eqFail:
// this is pretty expensive (bad lookup caching helps a lot), but it only all gets run if both objects have protos & both have the `__equal` metamethod defined so... // this is pretty expensive (bad lookup caching helps a lot), but it only all gets run if both
// it should stay light for the majority of cases // objects have protos & both have the `__equal` metamethod defined so... it should stay light
if ((proto1 = cosmoO_grabProto(obj1)) != NULL && (proto2 = cosmoO_grabProto(obj2)) != NULL && // make sure both protos exist // for the majority of cases
cosmoO_getIString(state, proto1, ISTRING_EQUAL, &eq1) && // grab the `__equal` metamethod from the first proto, if fail abort if ((proto1 = cosmoO_grabProto(obj1)) != NULL &&
cosmoO_getIString(state, proto2, ISTRING_EQUAL, &eq2) && // grab the `__equal` metamethod from the second proto, if fail abort (proto2 = cosmoO_grabProto(obj2)) != NULL && // make sure both protos exist
cosmoO_getIString(
state, proto1, ISTRING_EQUAL,
&eq1) && // grab the `__equal` metamethod from the first proto, if fail abort
cosmoO_getIString(
state, proto2, ISTRING_EQUAL,
&eq2) && // grab the `__equal` metamethod from the second proto, if fail abort
cosmoV_equal(state, eq1, eq2)) { // compare the two `__equal` metamethods cosmoV_equal(state, eq1, eq2)) { // compare the two `__equal` metamethods
// now finally, call the `__equal` metamethod (<object>, <object>) // now finally, call the `__equal` metamethod (<object>, <object>)
@ -164,7 +178,8 @@ _eqFail:
// check return value and make sure it's a boolean // check return value and make sure it's a boolean
if (!IS_BOOLEAN(*cosmoV_getTop(state, 0))) { if (!IS_BOOLEAN(*cosmoV_getTop(state, 0))) {
cosmoV_error(state, "__equal expected to return <boolean>, got %s!", cosmoV_typeStr(*cosmoV_pop(state))); cosmoV_error(state, "__equal expected to return <boolean>, got %s!",
cosmoV_typeStr(*cosmoV_pop(state)));
return false; return false;
} }
@ -175,39 +190,44 @@ _eqFail:
return false; return false;
} }
CObjObject *cosmoO_newObject(CState *state) { CObjObject *cosmoO_newObject(CState *state)
CObjObject *obj = (CObjObject*)cosmoO_allocateBase(state, sizeof(CObjObject), COBJ_OBJECT); {
CObjObject *obj = (CObjObject *)cosmoO_allocateBase(state, sizeof(CObjObject), COBJ_OBJECT);
obj->istringFlags = 0; obj->istringFlags = 0;
obj->userP = NULL; // reserved for C API obj->userP = NULL; // reserved for C API
obj->userT = 0; obj->userT = 0;
obj->isLocked = false; obj->isLocked = false;
cosmoV_pushRef(state, (CObj*)obj); // so our GC can keep track of it cosmoV_pushRef(state, (CObj *)obj); // so our GC can keep track of it
cosmoT_initTable(state, &obj->tbl, ARRAY_START); cosmoT_initTable(state, &obj->tbl, ARRAY_START);
cosmoV_pop(state); cosmoV_pop(state);
return obj; return obj;
} }
CObjStream *cosmoO_newStream(CState *state, int fd) { CObjStream *cosmoO_newStream(CState *state, int fd)
CObjStream *strm = (CObjStream*)cosmoO_allocateBase(state, sizeof(CObjStream), COBJ_STREAM); {
CObjStream *strm = (CObjStream *)cosmoO_allocateBase(state, sizeof(CObjStream), COBJ_STREAM);
strm->fd = fd; strm->fd = fd;
return strm; return strm;
} }
CObjTable *cosmoO_newTable(CState *state) { CObjTable *cosmoO_newTable(CState *state)
CObjTable *obj = (CObjTable*)cosmoO_allocateBase(state, sizeof(CObjTable), COBJ_TABLE); {
CObjTable *obj = (CObjTable *)cosmoO_allocateBase(state, sizeof(CObjTable), COBJ_TABLE);
// init the table (might cause a GC event) // init the table (might cause a GC event)
cosmoV_pushRef(state, (CObj*)obj); // so our GC can keep track of obj cosmoV_pushRef(state, (CObj *)obj); // so our GC can keep track of obj
cosmoT_initTable(state, &obj->tbl, ARRAY_START); cosmoT_initTable(state, &obj->tbl, ARRAY_START);
cosmoV_pop(state); cosmoV_pop(state);
return obj; return obj;
} }
CObjFunction *cosmoO_newFunction(CState *state) { CObjFunction *cosmoO_newFunction(CState *state)
CObjFunction *func = (CObjFunction*)cosmoO_allocateBase(state, sizeof(CObjFunction), COBJ_FUNCTION); {
CObjFunction *func =
(CObjFunction *)cosmoO_allocateBase(state, sizeof(CObjFunction), COBJ_FUNCTION);
func->args = 0; func->args = 0;
func->upvals = 0; func->upvals = 0;
func->variadic = false; func->variadic = false;
@ -218,14 +238,17 @@ CObjFunction *cosmoO_newFunction(CState *state) {
return func; return func;
} }
CObjCFunction *cosmoO_newCFunction(CState *state, CosmoCFunction func) { CObjCFunction *cosmoO_newCFunction(CState *state, CosmoCFunction func)
CObjCFunction *cfunc = (CObjCFunction*)cosmoO_allocateBase(state, sizeof(CObjCFunction), COBJ_CFUNCTION); {
CObjCFunction *cfunc =
(CObjCFunction *)cosmoO_allocateBase(state, sizeof(CObjCFunction), COBJ_CFUNCTION);
cfunc->cfunc = func; cfunc->cfunc = func;
return cfunc; return cfunc;
} }
CObjError *cosmoO_newError(CState *state, CValue err) { CObjError *cosmoO_newError(CState *state, CValue err)
CObjError *cerror = (CObjError*)cosmoO_allocateBase(state, sizeof(CObjError), COBJ_ERROR); {
CObjError *cerror = (CObjError *)cosmoO_allocateBase(state, sizeof(CObjError), COBJ_ERROR);
cerror->err = err; cerror->err = err;
cerror->frameCount = state->frameCount; cerror->frameCount = state->frameCount;
cerror->parserError = false; cerror->parserError = false;
@ -240,22 +263,25 @@ CObjError *cosmoO_newError(CState *state, CValue err) {
return cerror; return cerror;
} }
CObjMethod *cosmoO_newMethod(CState *state, CValue func, CObj *obj) { CObjMethod *cosmoO_newMethod(CState *state, CValue func, CObj *obj)
CObjMethod *method = (CObjMethod*)cosmoO_allocateBase(state, sizeof(CObjMethod), COBJ_METHOD); {
CObjMethod *method = (CObjMethod *)cosmoO_allocateBase(state, sizeof(CObjMethod), COBJ_METHOD);
method->func = func; method->func = func;
method->obj = obj; method->obj = obj;
return method; return method;
} }
CObjClosure *cosmoO_newClosure(CState *state, CObjFunction *func) { CObjClosure *cosmoO_newClosure(CState *state, CObjFunction *func)
{
// initialize array of pointers // initialize array of pointers
CObjUpval **upvalues = cosmoM_xmalloc(state, sizeof(CObjUpval*) * func->upvals); CObjUpval **upvalues = cosmoM_xmalloc(state, sizeof(CObjUpval *) * func->upvals);
for (int i = 0; i < func->upvals; i++) { for (int i = 0; i < func->upvals; i++) {
upvalues[i] = NULL; upvalues[i] = NULL;
} }
CObjClosure *closure = (CObjClosure*)cosmoO_allocateBase(state, sizeof(CObjClosure), COBJ_CLOSURE); CObjClosure *closure =
(CObjClosure *)cosmoO_allocateBase(state, sizeof(CObjClosure), COBJ_CLOSURE);
closure->function = func; closure->function = func;
closure->upvalues = upvalues; closure->upvalues = upvalues;
closure->upvalueCount = func->upvals; closure->upvalueCount = func->upvals;
@ -263,8 +289,9 @@ CObjClosure *cosmoO_newClosure(CState *state, CObjFunction *func) {
return closure; return closure;
} }
CObjUpval *cosmoO_newUpvalue(CState *state, CValue *val) { CObjUpval *cosmoO_newUpvalue(CState *state, CValue *val)
CObjUpval *upval = (CObjUpval*)cosmoO_allocateBase(state, sizeof(CObjUpval), COBJ_UPVALUE); {
CObjUpval *upval = (CObjUpval *)cosmoO_allocateBase(state, sizeof(CObjUpval), COBJ_UPVALUE);
upval->val = val; upval->val = val;
upval->closed = cosmoV_newNil(); upval->closed = cosmoV_newNil();
upval->next = NULL; upval->next = NULL;
@ -272,7 +299,8 @@ CObjUpval *cosmoO_newUpvalue(CState *state, CValue *val) {
return upval; return upval;
} }
CObjString *cosmoO_copyString(CState *state, const char *str, size_t length) { CObjString *cosmoO_copyString(CState *state, const char *str, size_t length)
{
uint32_t hash = hashString(str, length); uint32_t hash = hashString(str, length);
CObjString *lookup = cosmoT_lookupString(&state->strings, str, length, hash); CObjString *lookup = cosmoT_lookupString(&state->strings, str, length, hash);
@ -287,37 +315,43 @@ CObjString *cosmoO_copyString(CState *state, const char *str, size_t length) {
return cosmoO_allocateString(state, buf, length, hash); return cosmoO_allocateString(state, buf, length, hash);
} }
// length shouldn't include the null terminator! str should be a null terminated string! (char array should also have been allocated using cosmoM_xmalloc!) // length shouldn't include the null terminator! str should be a null terminated string! (char array
CObjString *cosmoO_takeString(CState *state, char *str, size_t length) { // should also have been allocated using cosmoM_xmalloc!)
CObjString *cosmoO_takeString(CState *state, char *str, size_t length)
{
uint32_t hash = hashString(str, length); uint32_t hash = hashString(str, length);
CObjString *lookup = cosmoT_lookupString(&state->strings, str, length, hash); CObjString *lookup = cosmoT_lookupString(&state->strings, str, length, hash);
// have we already interned this string? // have we already interned this string?
if (lookup != NULL) { if (lookup != NULL) {
cosmoM_freearray(state, char, str, length + 1); // free our passed character array, it's unneeded! cosmoM_freearray(state, char, str,
length + 1); // free our passed character array, it's unneeded!
return lookup; return lookup;
} }
return cosmoO_allocateString(state, str, length, hash); return cosmoO_allocateString(state, str, length, hash);
} }
CObjString *cosmoO_allocateString(CState *state, const char *str, size_t sz, uint32_t hash) { CObjString *cosmoO_allocateString(CState *state, const char *str, size_t sz, uint32_t hash)
CObjString *strObj = (CObjString*)cosmoO_allocateBase(state, sizeof(CObjString), COBJ_STRING); {
CObjString *strObj = (CObjString *)cosmoO_allocateBase(state, sizeof(CObjString), COBJ_STRING);
strObj->isIString = false; strObj->isIString = false;
strObj->str = (char*)str; strObj->str = (char *)str;
strObj->length = sz; strObj->length = sz;
strObj->hash = hash; strObj->hash = hash;
// we push & pop the string so our GC can find it (we don't use freezeGC/unfreezeGC because we *want* a GC event to happen) // we push & pop the string so our GC can find it (we don't use freezeGC/unfreezeGC because we
cosmoV_pushRef(state, (CObj*)strObj); // *want* a GC event to happen)
cosmoT_insert(state, &state->strings, cosmoV_newRef((CObj*)strObj)); cosmoV_pushRef(state, (CObj *)strObj);
cosmoT_insert(state, &state->strings, cosmoV_newRef((CObj *)strObj));
cosmoV_pop(state); cosmoV_pop(state);
return strObj; return strObj;
} }
CObjString *cosmoO_pushVFString(CState *state, const char *format, va_list args) { CObjString *cosmoO_pushVFString(CState *state, const char *format, va_list args)
{
StkPtr start = state->top; StkPtr start = state->top;
const char *end; const char *end;
char c; char c;
@ -362,19 +396,21 @@ CObjString *cosmoO_pushVFString(CState *state, const char *format, va_list args)
} }
cosmoV_pushString(state, format); // push the rest of the string cosmoV_pushString(state, format); // push the rest of the string
cosmoV_concat(state, state->top - start); // use cosmoV_concat to concat all the strings on the stack cosmoV_concat(state,
state->top - start); // use cosmoV_concat to concat all the strings on the stack
return cosmoV_readString(*start); // start should be state->top - 1 return cosmoV_readString(*start); // start should be state->top - 1
} }
// walks the protos of obj and checks for proto // walks the protos of obj and checks for proto
bool cosmoO_isDescendant(CObj *obj, CObjObject *proto) { bool cosmoO_isDescendant(CObj *obj, CObjObject *proto)
{
CObjObject *curr = obj->proto; CObjObject *curr = obj->proto;
while (curr != NULL) { while (curr != NULL) {
if (curr == proto) if (curr == proto)
return true; // found proto! return true return true; // found proto! return true
curr = ((CObj*)curr)->proto; curr = ((CObj *)curr)->proto;
} }
// we didn't find the proto // we didn't find the proto
@ -382,18 +418,22 @@ bool cosmoO_isDescendant(CObj *obj, CObjObject *proto) {
} }
// returns false if error thrown // returns false if error thrown
bool cosmoO_getRawObject(CState *state, CObjObject *proto, CValue key, CValue *val, CObj *obj) { bool cosmoO_getRawObject(CState *state, CObjObject *proto, CValue key, CValue *val, CObj *obj)
if (!cosmoT_get(state, &proto->tbl, key, val)) { // if the field doesn't exist in the object, check the proto {
if (cosmoO_getIString(state, proto, ISTRING_GETTER, val) && IS_TABLE(*val) && cosmoT_get(state, &cosmoV_readTable(*val)->tbl, key, val)) { if (!cosmoT_get(state, &proto->tbl, key,
val)) { // if the field doesn't exist in the object, check the proto
if (cosmoO_getIString(state, proto, ISTRING_GETTER, val) && IS_TABLE(*val) &&
cosmoT_get(state, &cosmoV_readTable(*val)->tbl, key, val)) {
cosmoV_pushValue(state, *val); // push function cosmoV_pushValue(state, *val); // push function
cosmoV_pushRef(state, (CObj*)obj); // push object cosmoV_pushRef(state, (CObj *)obj); // push object
if (cosmoV_call(state, 1, 1) != COSMOVM_OK) // call the function with the 1 argument if (cosmoV_call(state, 1, 1) != COSMOVM_OK) // call the function with the 1 argument
return false; return false;
*val = *cosmoV_pop(state); // set value to the return value of __index *val = *cosmoV_pop(state); // set value to the return value of __index
return true; return true;
} }
if (proto->_obj.proto != NULL && cosmoO_getRawObject(state, proto->_obj.proto, key, val, obj)) if (proto->_obj.proto != NULL &&
cosmoO_getRawObject(state, proto->_obj.proto, key, val, obj))
return true; return true;
*val = cosmoV_newNil(); *val = cosmoV_newNil();
@ -403,7 +443,8 @@ bool cosmoO_getRawObject(CState *state, CObjObject *proto, CValue key, CValue *v
return true; return true;
} }
void cosmoO_setRawObject(CState *state, CObjObject *proto, CValue key, CValue val, CObj *obj) { void cosmoO_setRawObject(CState *state, CObjObject *proto, CValue key, CValue val, CObj *obj)
{
CValue ret; CValue ret;
// if the object is locked, throw an error // if the object is locked, throw an error
@ -413,9 +454,10 @@ void cosmoO_setRawObject(CState *state, CObjObject *proto, CValue key, CValue va
} }
// check for __setters // check for __setters
if (cosmoO_getIString(state, proto, ISTRING_SETTER, &ret) && IS_TABLE(ret) && cosmoT_get(state, &cosmoV_readTable(ret)->tbl, key, &ret)) { if (cosmoO_getIString(state, proto, ISTRING_SETTER, &ret) && IS_TABLE(ret) &&
cosmoT_get(state, &cosmoV_readTable(ret)->tbl, key, &ret)) {
cosmoV_pushValue(state, ret); // push function cosmoV_pushValue(state, ret); // push function
cosmoV_pushRef(state, (CObj*)obj); // push object cosmoV_pushRef(state, (CObj *)obj); // push object
cosmoV_pushValue(state, val); // push new value cosmoV_pushValue(state, val); // push new value
cosmoV_call(state, 2, 0); cosmoV_call(state, 2, 0);
return; return;
@ -433,39 +475,48 @@ void cosmoO_setRawObject(CState *state, CObjObject *proto, CValue key, CValue va
} }
} }
void cosmoO_setUserP(CObjObject *object, void *p) { void cosmoO_setUserP(CObjObject *object, void *p)
{
object->userP = p; object->userP = p;
} }
void *cosmoO_getUserP(CObjObject *object) { void *cosmoO_getUserP(CObjObject *object)
{
return object->userP; return object->userP;
} }
void cosmoO_setUserI(CObjObject *object, int i) { void cosmoO_setUserI(CObjObject *object, int i)
{
object->userI = i; object->userI = i;
} }
int cosmoO_getUserI(CObjObject *object) { int cosmoO_getUserI(CObjObject *object)
{
return object->userI; return object->userI;
} }
void cosmoO_setUserT(CObjObject *object, int t) { void cosmoO_setUserT(CObjObject *object, int t)
{
object->userT = t; object->userT = t;
} }
int cosmoO_getUserT(CObjObject *object) { int cosmoO_getUserT(CObjObject *object)
{
return object->userT; return object->userT;
} }
void cosmoO_lock(CObjObject *object) { void cosmoO_lock(CObjObject *object)
{
object->isLocked = true; object->isLocked = true;
} }
void cosmoO_unlock(CObjObject *object) { void cosmoO_unlock(CObjObject *object)
{
object->isLocked = false; object->isLocked = false;
} }
bool rawgetIString(CState *state, CObjObject *object, int flag, CValue *val) { bool rawgetIString(CState *state, CObjObject *object, int flag, CValue *val)
{
if (readFlag(object->istringFlags, flag)) if (readFlag(object->istringFlags, flag))
return false; // it's been cached as bad return false; // it's been cached as bad
@ -478,7 +529,8 @@ bool rawgetIString(CState *state, CObjObject *object, int flag, CValue *val) {
return true; // :) return true; // :)
} }
bool cosmoO_getIString(CState *state, CObjObject *object, int flag, CValue *val) { bool cosmoO_getIString(CState *state, CObjObject *object, int flag, CValue *val)
{
CObjObject *obj = object; CObjObject *obj = object;
do { do {
@ -489,10 +541,11 @@ bool cosmoO_getIString(CState *state, CObjObject *object, int flag, CValue *val)
return false; // obj->proto was false, the istring doesn't exist in this object chain return false; // obj->proto was false, the istring doesn't exist in this object chain
} }
bool cosmoO_indexObject(CState *state, CObjObject *object, CValue key, CValue *val) { bool cosmoO_indexObject(CState *state, CObjObject *object, CValue key, CValue *val)
{
if (cosmoO_getIString(state, object, ISTRING_INDEX, val)) { if (cosmoO_getIString(state, object, ISTRING_INDEX, val)) {
cosmoV_pushValue(state, *val); // push function cosmoV_pushValue(state, *val); // push function
cosmoV_pushRef(state, (CObj*)object); // push object cosmoV_pushRef(state, (CObj *)object); // push object
cosmoV_pushValue(state, key); // push key cosmoV_pushValue(state, key); // push key
if (cosmoV_call(state, 2, 1) != COSMOVM_OK) // call the function with the 2 arguments if (cosmoV_call(state, 2, 1) != COSMOVM_OK) // call the function with the 2 arguments
return false; return false;
@ -505,12 +558,13 @@ bool cosmoO_indexObject(CState *state, CObjObject *object, CValue key, CValue *v
return false; return false;
} }
bool cosmoO_newIndexObject(CState *state, CObjObject *object, CValue key, CValue val) { bool cosmoO_newIndexObject(CState *state, CObjObject *object, CValue key, CValue val)
{
CValue ret; // return value for cosmoO_getIString CValue ret; // return value for cosmoO_getIString
if (cosmoO_getIString(state, object, ISTRING_NEWINDEX, &ret)) { if (cosmoO_getIString(state, object, ISTRING_NEWINDEX, &ret)) {
cosmoV_pushValue(state, ret); // push function cosmoV_pushValue(state, ret); // push function
cosmoV_pushRef(state, (CObj*)object); // push object cosmoV_pushRef(state, (CObj *)object); // push object
cosmoV_pushValue(state, key); // push key & value pair cosmoV_pushValue(state, key); // push key & value pair
cosmoV_pushValue(state, val); cosmoV_pushValue(state, val);
return cosmoV_call(state, 3, 0) == COSMOVM_OK; return cosmoV_call(state, 3, 0) == COSMOVM_OK;
@ -521,82 +575,88 @@ bool cosmoO_newIndexObject(CState *state, CObjObject *object, CValue key, CValue
return false; return false;
} }
CObjString *cosmoO_toString(CState *state, CObj *obj) { CObjString *cosmoO_toString(CState *state, CObj *obj)
{
CObjObject *protoObject = cosmoO_grabProto(obj); CObjObject *protoObject = cosmoO_grabProto(obj);
CValue res; CValue res;
// use user-defined __tostring // use user-defined __tostring
if (protoObject != NULL && cosmoO_getIString(state, protoObject, ISTRING_TOSTRING, &res)) { if (protoObject != NULL && cosmoO_getIString(state, protoObject, ISTRING_TOSTRING, &res)) {
cosmoV_pushValue(state, res); cosmoV_pushValue(state, res);
cosmoV_pushRef(state, (CObj*)obj); cosmoV_pushRef(state, (CObj *)obj);
if (cosmoV_call(state, 1, 1) != COSMOVM_OK) if (cosmoV_call(state, 1, 1) != COSMOVM_OK)
return cosmoO_copyString(state, "<err>", 5); return cosmoO_copyString(state, "<err>", 5);
// make sure the __tostring function returned a string // make sure the __tostring function returned a string
StkPtr ret = cosmoV_getTop(state, 0); StkPtr ret = cosmoV_getTop(state, 0);
if (!IS_STRING(*ret)) { if (!IS_STRING(*ret)) {
cosmoV_error(state, "__tostring expected to return <string>, got %s!", cosmoV_typeStr(*ret)); cosmoV_error(state, "__tostring expected to return <string>, got %s!",
cosmoV_typeStr(*ret));
return cosmoO_copyString(state, "<err>", 5); return cosmoO_copyString(state, "<err>", 5);
} }
// return string // return string
cosmoV_pop(state); cosmoV_pop(state);
return (CObjString*)cosmoV_readRef(*ret); return (CObjString *)cosmoV_readRef(*ret);
} }
switch (obj->type) { switch (obj->type) {
case COBJ_STRING: { case COBJ_STRING: {
return (CObjString*)obj; return (CObjString *)obj;
} }
case COBJ_CLOSURE: { // should be transparent to the user imo case COBJ_CLOSURE: { // should be transparent to the user imo
CObjClosure *closure = (CObjClosure*)obj; CObjClosure *closure = (CObjClosure *)obj;
return cosmoO_toString(state, (CObj*)closure->function); return cosmoO_toString(state, (CObj *)closure->function);
} }
case COBJ_FUNCTION: { case COBJ_FUNCTION: {
CObjFunction *func = (CObjFunction*)obj; 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_CFUNCTION: { case COBJ_CFUNCTION: {
CObjCFunction *cfunc = (CObjCFunction*)obj; CObjCFunction *cfunc = (CObjCFunction *)obj;
char buf[64]; char buf[64];
int sz = sprintf(buf, "<c function> %p", (void*)cfunc->cfunc) + 1; // +1 for the null character int sz =
sprintf(buf, "<c function> %p", (void *)cfunc->cfunc) + 1; // +1 for the null character
return cosmoO_copyString(state, buf, sz); return cosmoO_copyString(state, buf, sz);
} }
case COBJ_OBJECT: { case COBJ_OBJECT: {
char buf[64]; char buf[64];
int sz = sprintf(buf, "<obj> %p", (void*)obj) + 1; // +1 for the null character int sz = sprintf(buf, "<obj> %p", (void *)obj) + 1; // +1 for the null character
return cosmoO_copyString(state, buf, sz); return cosmoO_copyString(state, buf, sz);
} }
case COBJ_ERROR: { case COBJ_ERROR: {
CObjError *err = (CObjError*)obj; CObjError *err = (CObjError *)obj;
return cosmoV_toString(state, err->err); return cosmoV_toString(state, err->err);
} }
case COBJ_TABLE: { case COBJ_TABLE: {
char buf[64]; char buf[64];
int sz = sprintf(buf, "<tbl> %p", (void*)obj) + 1; // +1 for the null character int sz = sprintf(buf, "<tbl> %p", (void *)obj) + 1; // +1 for the null character
return cosmoO_copyString(state, buf, sz); return cosmoO_copyString(state, buf, sz);
} }
default: { default: {
char buf[64]; char buf[64];
int sz = sprintf(buf, "<unkn obj> %p", (void*)obj) + 1; // +1 for the null character int sz = sprintf(buf, "<unkn obj> %p", (void *)obj) + 1; // +1 for the null character
return cosmoO_copyString(state, buf, sz); return cosmoO_copyString(state, buf, sz);
} }
} }
} }
cosmo_Number cosmoO_toNumber(CState *state, CObj *obj) { cosmo_Number cosmoO_toNumber(CState *state, CObj *obj)
{
CObjObject *proto = cosmoO_grabProto(obj); CObjObject *proto = cosmoO_grabProto(obj);
CValue res; CValue res;
if (proto != NULL && cosmoO_getIString(state, proto, ISTRING_TONUMBER, &res)) { if (proto != NULL && cosmoO_getIString(state, proto, ISTRING_TONUMBER, &res)) {
cosmoV_pushValue(state, res); cosmoV_pushValue(state, res);
cosmoV_pushRef(state, (CObj*)obj); cosmoV_pushRef(state, (CObj *)obj);
if (cosmoV_call(state, 1, 1) != COSMOVM_OK) // call res, expect 1 return val of <number> if (cosmoV_call(state, 1, 1) != COSMOVM_OK) // call res, expect 1 return val of <number>
return 0; return 0;
StkPtr temp = cosmoV_getTop(state, 0); StkPtr temp = cosmoV_getTop(state, 0);
if (!IS_NUMBER(*temp)) { if (!IS_NUMBER(*temp)) {
cosmoV_error(state, "__tonumber expected to return <number>, got %s!", cosmoV_typeStr(*temp)); cosmoV_error(state, "__tonumber expected to return <number>, got %s!",
cosmoV_typeStr(*temp));
return 0; return 0;
} }
@ -607,7 +667,7 @@ cosmo_Number cosmoO_toNumber(CState *state, CObj *obj) {
switch (obj->type) { switch (obj->type) {
case COBJ_STRING: { case COBJ_STRING: {
CObjString *str = (CObjString*)obj; CObjString *str = (CObjString *)obj;
return strtod(str->str, NULL); return strtod(str->str, NULL);
} }
default: // maybe in the future throw an error? default: // maybe in the future throw an error?
@ -615,19 +675,22 @@ cosmo_Number cosmoO_toNumber(CState *state, CObj *obj) {
} }
} }
int cosmoO_count(CState *state, CObj *obj) { int cosmoO_count(CState *state, CObj *obj)
{
CObjObject *proto = cosmoO_grabProto(obj); CObjObject *proto = cosmoO_grabProto(obj);
CValue res; CValue res;
if (proto != NULL && cosmoO_getIString(state, proto, ISTRING_COUNT, &res)) { if (proto != NULL && cosmoO_getIString(state, proto, ISTRING_COUNT, &res)) {
cosmoV_pushValue(state, res); cosmoV_pushValue(state, res);
cosmoV_pushRef(state, (CObj*)obj); cosmoV_pushRef(state, (CObj *)obj);
if (cosmoV_call(state, 1, 1) != COSMOVM_OK) // call res, we expect 1 return value of type <number> if (cosmoV_call(state, 1, 1) !=
COSMOVM_OK) // call res, we expect 1 return value of type <number>
return 0; return 0;
StkPtr ret = cosmoV_getTop(state, 0); StkPtr ret = cosmoV_getTop(state, 0);
if (!IS_NUMBER(*ret)) { if (!IS_NUMBER(*ret)) {
cosmoV_error(state, "__count expected to return <number>, got %s!", cosmoV_typeStr(*ret)); cosmoV_error(state, "__count expected to return <number>, got %s!",
cosmoV_typeStr(*ret));
return 0; return 0;
} }
@ -638,11 +701,11 @@ int cosmoO_count(CState *state, CObj *obj) {
switch (obj->type) { switch (obj->type) {
case COBJ_TABLE: { // returns the # of entries in the hash table case COBJ_TABLE: { // returns the # of entries in the hash table
CObjTable *tbl = (CObjTable*)obj; CObjTable *tbl = (CObjTable *)obj;
return cosmoT_count(&tbl->tbl); return cosmoT_count(&tbl->tbl);
} }
case COBJ_STRING: { // returns the length of the string case COBJ_STRING: { // returns the length of the string
CObjString *str = (CObjString*)obj; CObjString *str = (CObjString *)obj;
return str->length; return str->length;
} }
default: default:
@ -651,24 +714,25 @@ int cosmoO_count(CState *state, CObj *obj) {
} }
} }
void printObject(CObj *o) { void printObject(CObj *o)
{
switch (o->type) { switch (o->type) {
case COBJ_STRING: { case COBJ_STRING: {
CObjString *objStr = (CObjString*)o; CObjString *objStr = (CObjString *)o;
printf("%.*s", objStr->length, objStr->str); printf("%.*s", objStr->length, objStr->str);
break; break;
} }
case COBJ_OBJECT: { case COBJ_OBJECT: {
printf("<obj> %p", (void*)o); printf("<obj> %p", (void *)o);
break; break;
} }
case COBJ_TABLE: { case COBJ_TABLE: {
CObjTable *tbl = (CObjTable*)o; CObjTable *tbl = (CObjTable *)o;
printf("<tbl> %p", (void*)tbl); printf("<tbl> %p", (void *)tbl);
break; break;
} }
case COBJ_FUNCTION: { case COBJ_FUNCTION: {
CObjFunction *objFunc = (CObjFunction*)o; CObjFunction *objFunc = (CObjFunction *)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
@ -676,49 +740,58 @@ void printObject(CObj *o) {
break; break;
} }
case COBJ_CFUNCTION: { case COBJ_CFUNCTION: {
CObjCFunction *objCFunc = (CObjCFunction*)o; CObjCFunction *objCFunc = (CObjCFunction *)o;
printf("<c function> %p", (void*)objCFunc->cfunc); printf("<c function> %p", (void *)objCFunc->cfunc);
break; break;
} }
case COBJ_ERROR: { case COBJ_ERROR: {
CObjError *err = (CObjError*)o; CObjError *err = (CObjError *)o;
printf("<error> %p -> ", (void*)o); printf("<error> %p -> ", (void *)o);
printValue(err->err); printValue(err->err);
break; break;
} }
case COBJ_METHOD: { case COBJ_METHOD: {
CObjMethod *method = (CObjMethod*)o; CObjMethod *method = (CObjMethod *)o;
printf("<method> %p -> ", (void*)method); printf("<method> %p -> ", (void *)method);
printValue(method->func); printValue(method->func);
break; break;
} }
case COBJ_CLOSURE: { case COBJ_CLOSURE: {
CObjClosure *closure = (CObjClosure*)o; CObjClosure *closure = (CObjClosure *)o;
printf("<closure> %p -> ", (void*)closure); printf("<closure> %p -> ", (void *)closure);
printObject((CObj*)closure->function); // just print the function printObject((CObj *)closure->function); // just print the function
break; break;
} }
case COBJ_UPVALUE: { case COBJ_UPVALUE: {
CObjUpval *upval = (CObjUpval*)o; CObjUpval *upval = (CObjUpval *)o;
printf("<upvalue> %p -> ", (void*)upval->val); printf("<upvalue> %p -> ", (void *)upval->val);
printValue(*upval->val); printValue(*upval->val);
break; break;
} }
default: default:
printf("<unkn obj %p>", (void*)o); printf("<unkn obj %p>", (void *)o);
} }
} }
const char *cosmoO_typeStr(CObj* obj) { const char *cosmoO_typeStr(CObj *obj)
{
switch (obj->type) { switch (obj->type) {
case COBJ_STRING: return "<string>"; case COBJ_STRING:
case COBJ_OBJECT: return "<object>"; return "<string>";
case COBJ_TABLE: return "<table>"; case COBJ_OBJECT:
case COBJ_FUNCTION: return "<function>"; return "<object>";
case COBJ_CFUNCTION: return "<c function>"; case COBJ_TABLE:
case COBJ_METHOD: return "<method>"; return "<table>";
case COBJ_CLOSURE: return "<closure>"; case COBJ_FUNCTION:
case COBJ_UPVALUE: return "<upvalue>"; return "<function>";
case COBJ_CFUNCTION:
return "<c function>";
case COBJ_METHOD:
return "<method>";
case COBJ_CLOSURE:
return "<closure>";
case COBJ_UPVALUE:
return "<upvalue>";
default: default:
return "<unkn obj>"; // TODO: maybe panic? could be a malformed object :eyes: return "<unkn obj>"; // TODO: maybe panic? could be a malformed object :eyes:

View File

@ -3,7 +3,8 @@
#include "cosmo.h" #include "cosmo.h"
typedef enum CObjType { typedef enum CObjType
{
COBJ_STRING, COBJ_STRING,
COBJ_OBJECT, COBJ_OBJECT,
COBJ_TABLE, COBJ_TABLE,
@ -18,10 +19,10 @@ typedef enum CObjType {
COBJ_MAX COBJ_MAX
} CObjType; } CObjType;
#include "cstate.h"
#include "cchunk.h" #include "cchunk.h"
#include "cvalue.h" #include "cstate.h"
#include "ctable.h" #include "ctable.h"
#include "cvalue.h"
#define CommonHeader CObj _obj #define CommonHeader CObj _obj
#define readFlag(x, flag) (x & (1u << flag)) #define readFlag(x, flag) (x & (1u << flag))
@ -29,7 +30,8 @@ typedef enum CObjType {
typedef int (*CosmoCFunction)(CState *state, int argCount, CValue *args); typedef int (*CosmoCFunction)(CState *state, int argCount, CValue *args);
struct CObj { struct CObj
{
struct CObj *next; struct CObj *next;
struct CObj *nextRoot; // for the root linked list struct CObj *nextRoot; // for the root linked list
struct CObjObject *proto; // protoobject, describes the behavior of the object struct CObjObject *proto; // protoobject, describes the behavior of the object
@ -37,7 +39,8 @@ struct CObj {
bool isMarked; // for the GC bool isMarked; // for the GC
}; };
struct CObjString { struct CObjString
{
CommonHeader; // "is a" CObj CommonHeader; // "is a" CObj
char *str; // NULL termincated string char *str; // NULL termincated string
uint32_t hash; // for hashtable lookup uint32_t hash; // for hashtable lookup
@ -45,12 +48,14 @@ struct CObjString {
bool isIString; bool isIString;
}; };
struct CObjStream { struct CObjStream
{
CommonHeader; // "is a" CObj CommonHeader; // "is a" CObj
int fd; // handle to file descriptor, on POSIX compliant OSes this can also be a socket :pog: int fd; // handle to file descriptor, on POSIX compliant OSes this can also be a socket :pog:
}; };
struct CObjError { struct CObjError
{
CommonHeader; // "is a" CObj CommonHeader; // "is a" CObj
CValue err; // error string CValue err; // error string
CCallFrame *frames; CCallFrame *frames;
@ -59,11 +64,14 @@ struct CObjError {
bool parserError; // if true, cosmoV_printError will format the error to the lexer bool parserError; // if true, cosmoV_printError will format the error to the lexer
}; };
struct CObjObject { struct CObjObject
{
CommonHeader; // "is a" CObj CommonHeader; // "is a" CObj
CTable tbl; CTable tbl;
cosmo_Flag istringFlags; // enables us to have a much faster lookup for reserved IStrings (like __init, __index, etc.) cosmo_Flag istringFlags; // enables us to have a much faster lookup for reserved IStrings (like
union { // userdata (NULL by default) // __init, __index, etc.)
union
{ // userdata (NULL by default)
void *userP; void *userP;
int userI; int userI;
}; };
@ -71,12 +79,14 @@ struct CObjObject {
bool isLocked; bool isLocked;
}; };
struct CObjTable { // table, a wrapper for CTable struct CObjTable
{ // table, a wrapper for CTable
CommonHeader; // "is a" CObj CommonHeader; // "is a" CObj
CTable tbl; CTable tbl;
}; };
struct CObjFunction { struct CObjFunction
{
CommonHeader; // "is a" CObj CommonHeader; // "is a" CObj
CChunk chunk; CChunk chunk;
CObjString *name; CObjString *name;
@ -86,25 +96,29 @@ struct CObjFunction {
bool variadic; bool variadic;
}; };
struct CObjCFunction { struct CObjCFunction
{
CommonHeader; // "is a" CObj CommonHeader; // "is a" CObj
CosmoCFunction cfunc; CosmoCFunction cfunc;
}; };
struct CObjClosure { struct CObjClosure
{
CommonHeader; // "is a" CObj CommonHeader; // "is a" CObj
CObjFunction *function; CObjFunction *function;
CObjUpval **upvalues; CObjUpval **upvalues;
int upvalueCount; int upvalueCount;
}; };
struct CObjMethod { struct CObjMethod
{
CommonHeader; // "is a " CObj CommonHeader; // "is a " CObj
CValue func; CValue func;
CObj *obj; // obj this method is bound too CObj *obj; // obj this method is bound too
}; };
struct CObjUpval { struct CObjUpval
{
CommonHeader; // "is a" CObj CommonHeader; // "is a" CObj
CValue closed; CValue closed;
CValue *val; CValue *val;
@ -122,29 +136,31 @@ struct CObjUpval {
#define IS_METHOD(x) isObjType(x, COBJ_METHOD) #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_readRef(x)) #define cosmoV_readString(x) ((CObjString *)cosmoV_readRef(x))
#define cosmoV_readCString(x) (((CObjString*)cosmoV_readRef(x))->str) #define cosmoV_readCString(x) (((CObjString *)cosmoV_readRef(x))->str)
#define cosmoV_readFD(x) (((CObjStream*)cosmoV_readRef(x))->fd) #define cosmoV_readFD(x) (((CObjStream *)cosmoV_readRef(x))->fd)
#define cosmoV_readObject(x) ((CObjObject*)cosmoV_readRef(x)) #define cosmoV_readObject(x) ((CObjObject *)cosmoV_readRef(x))
#define cosmoV_readTable(x) ((CObjTable*)cosmoV_readRef(x)) #define cosmoV_readTable(x) ((CObjTable *)cosmoV_readRef(x))
#define cosmoV_readFunction(x) ((CObjFunction*)cosmoV_readRef(x)) #define cosmoV_readFunction(x) ((CObjFunction *)cosmoV_readRef(x))
#define cosmoV_readCFunction(x) (((CObjCFunction*)cosmoV_readRef(x))->cfunc) #define cosmoV_readCFunction(x) (((CObjCFunction *)cosmoV_readRef(x))->cfunc)
#define cosmoV_readMethod(x) ((CObjMethod*)cosmoV_readRef(x)) #define cosmoV_readMethod(x) ((CObjMethod *)cosmoV_readRef(x))
#define cosmoV_readClosure(x) ((CObjClosure*)cosmoV_readRef(x)) #define cosmoV_readClosure(x) ((CObjClosure *)cosmoV_readRef(x))
#define cosmoO_readCString(x) ((CObjString*)x)->str #define cosmoO_readCString(x) ((CObjString *)x)->str
static inline bool isObjType(CValue val, CObjType type) { static inline bool isObjType(CValue val, CObjType type)
{
return IS_REF(val) && cosmoV_readRef(val)->type == type; return IS_REF(val) && cosmoV_readRef(val)->type == type;
} }
// just protects against macro expansion // just protects against macro expansion
static inline bool IS_CALLABLE(CValue val) { static inline bool IS_CALLABLE(CValue val)
{
return IS_CLOSURE(val) || IS_CFUNCTION(val) || IS_METHOD(val); return IS_CLOSURE(val) || IS_CFUNCTION(val) || IS_METHOD(val);
} }
void cosmoO_free(CState *state, CObj* obj); void cosmoO_free(CState *state, CObj *obj);
bool cosmoO_equal(CState *state, CObj* obj1, CObj* obj2); bool cosmoO_equal(CState *state, CObj *obj1, CObj *obj2);
// walks the protos of obj and checks for proto // walks the protos of obj and checks for proto
bool cosmoO_isDescendant(CObj *obj, CObjObject *proto); bool cosmoO_isDescendant(CObj *obj, CObjObject *proto);
@ -160,8 +176,9 @@ CObjClosure *cosmoO_newClosure(CState *state, CObjFunction *func);
CObjUpval *cosmoO_newUpvalue(CState *state, CValue *val); CObjUpval *cosmoO_newUpvalue(CState *state, CValue *val);
// grabs the base proto of the CObj* (if CObj is a CObjObject, that is returned) // grabs the base proto of the CObj* (if CObj is a CObjObject, that is returned)
static inline CObjObject *cosmoO_grabProto(CObj *obj) { static inline CObjObject *cosmoO_grabProto(CObj *obj)
return obj->type == COBJ_OBJECT ? (CObjObject*)obj : obj->proto; {
return obj->type == COBJ_OBJECT ? (CObjObject *)obj : obj->proto;
} }
bool cosmoO_getRawObject(CState *state, CObjObject *proto, CValue key, CValue *val, CObj *obj); bool cosmoO_getRawObject(CState *state, CObjObject *proto, CValue key, CValue *val, CObj *obj);
@ -169,11 +186,13 @@ void cosmoO_setRawObject(CState *state, CObjObject *proto, CValue key, CValue va
bool cosmoO_indexObject(CState *state, CObjObject *object, CValue key, CValue *val); bool cosmoO_indexObject(CState *state, CObjObject *object, CValue key, CValue *val);
bool cosmoO_newIndexObject(CState *state, CObjObject *object, CValue key, CValue val); bool cosmoO_newIndexObject(CState *state, CObjObject *object, CValue key, CValue val);
// sets the user-defined pointer, if a user-define integer is already defined it will be over written // sets the user-defined pointer, if a user-define integer is already defined it will be over
// written
void cosmoO_setUserP(CObjObject *object, void *p); void cosmoO_setUserP(CObjObject *object, void *p);
// gets the user-defined pointer // gets the user-defined pointer
void *cosmoO_getUserP(CObjObject *object); void *cosmoO_getUserP(CObjObject *object);
// sets the user-defined integer, if a user-define pointer is already defined it will be over written // sets the user-defined integer, if a user-define pointer is already defined it will be over
// written
void cosmoO_setUserI(CObjObject *object, int i); void cosmoO_setUserI(CObjObject *object, int i);
// gets the user-defined integer // gets the user-defined integer
int cosmoO_getUserI(CObjObject *object); int cosmoO_getUserI(CObjObject *object);
@ -189,10 +208,12 @@ void cosmoO_unlock(CObjObject *object);
// internal string // internal string
bool cosmoO_getIString(CState *state, CObjObject *object, int flag, CValue *val); bool cosmoO_getIString(CState *state, CObjObject *object, int flag, CValue *val);
// copies the *str buffer to the heap and returns a CObjString struct which is also on the heap (length should not include the null terminator) // copies the *str buffer to the heap and returns a CObjString struct which is also on the heap
// (length should not include the null terminator)
CObjString *cosmoO_copyString(CState *state, const char *str, size_t length); CObjString *cosmoO_copyString(CState *state, const char *str, size_t length);
// length shouldn't include the null terminator! str should be a null terminated string! (char array should also have been allocated using cosmoM_xmalloc!) // length shouldn't include the null terminator! str should be a null terminated string! (char array
// should also have been allocated using cosmoM_xmalloc!)
CObjString *cosmoO_takeString(CState *state, char *str, size_t length); CObjString *cosmoO_takeString(CState *state, char *str, size_t length);
// allocates a CObjStruct pointing directly to *str // allocates a CObjStruct pointing directly to *str
@ -209,7 +230,7 @@ CObjString *cosmoO_allocateString(CState *state, const char *str, size_t length,
CObjString *cosmoO_pushVFString(CState *state, const char *format, va_list args); CObjString *cosmoO_pushVFString(CState *state, const char *format, va_list args);
COSMO_API void printObject(CObj *o); COSMO_API void printObject(CObj *o);
const char *cosmoO_typeStr(CObj* obj); const char *cosmoO_typeStr(CObj *obj);
CObjString *cosmoO_toString(CState *state, CObj *obj); CObjString *cosmoO_toString(CState *state, CObj *obj);
cosmo_Number cosmoO_toNumber(CState *state, CObj *obj); cosmo_Number cosmoO_toNumber(CState *state, CObj *obj);

View File

@ -5,7 +5,8 @@
// instructions // instructions
typedef enum { typedef enum
{
// STACK/STATE MANIPULATION // STACK/STATE MANIPULATION
OP_LOADCONST, // pushes const[uint8_t] to the stack OP_LOADCONST, // pushes const[uint8_t] to the stack
OP_SETGLOBAL, // pops and sets global[const[uint16_t]] OP_SETGLOBAL, // pops and sets global[const[uint16_t]]

View File

@ -1,21 +1,21 @@
#ifndef COSMOMAIN_H #ifndef COSMOMAIN_H
#define COSMOMAIN_H #define COSMOMAIN_H
#include <stdlib.h> #include <assert.h>
#include <stdbool.h>
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include <stdbool.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
#include <assert.h>
/* /*
SAFE_STACK: SAFE_STACK:
if undefined, the stack will not be checked for stack overflows. This may improve performance, however if undefined, the stack will not be checked for stack overflows. This may improve
this will produce undefined behavior as you reach the stack limit (and may cause a seg fault!). It is recommended to keep this enabled. performance, however this will produce undefined behavior as you reach the stack limit (and may
cause a seg fault!). It is recommended to keep this enabled.
*/ */
#define SAFE_STACK #define SAFE_STACK
//#define NAN_BOXXED // #define NAN_BOXXED
// forward declare *most* stuff so our headers are cleaner // forward declare *most* stuff so our headers are cleaner
typedef struct CState CState; typedef struct CState CState;
@ -54,7 +54,6 @@ typedef uint8_t INSTRUCTION;
#define UNNAMEDCHUNK "_main" #define UNNAMEDCHUNK "_main"
#define COSMOASSERT(x) assert(x) #define COSMOASSERT(x) assert(x)
#define CERROR(err) \ #define CERROR(err) printf("%s : %s\n", "[ERROR]", err)
printf("%s : %s\n", "[ERROR]", err)
#endif #endif

File diff suppressed because it is too large Load Diff

View File

@ -1,10 +1,11 @@
#ifndef CPARSE_H #ifndef CPARSE_H
#define CPARSE_H #define CPARSE_H
#include "cosmo.h"
#include "clex.h" #include "clex.h"
#include "cosmo.h"
// compiles source into CChunk, if NULL is returned, a syntaxical error has occurred and pushed onto the stack // compiles source into CChunk, if NULL is returned, a syntaxical error has occurred and pushed onto
CObjFunction* cosmoP_compileString(CState *state, const char *source, const char *module); // the stack
CObjFunction *cosmoP_compileString(CState *state, const char *source, const char *module);
#endif #endif

View File

@ -1,12 +1,14 @@
#include "cstate.h" #include "cstate.h"
#include "cchunk.h" #include "cchunk.h"
#include "cmem.h"
#include "cobj.h" #include "cobj.h"
#include "cvm.h" #include "cvm.h"
#include "cmem.h"
#include <string.h> #include <string.h>
CState *cosmoV_newState() { CState *cosmoV_newState()
{
// we use C's malloc because we don't want to trigger a GC with an invalid state // we use C's malloc because we don't want to trigger a GC with an invalid state
CState *state = malloc(sizeof(CState)); CState *state = malloc(sizeof(CState));
@ -73,7 +75,8 @@ CState *cosmoV_newState() {
return state; return state;
} }
void cosmoV_freeState(CState *state) { void cosmoV_freeState(CState *state)
{
#ifdef GC_DEBUG #ifdef GC_DEBUG
printf("state %p is being free'd!\n", state); printf("state %p is being free'd!\n", state);
#endif #endif
@ -95,20 +98,21 @@ void cosmoV_freeState(CState *state) {
cosmoT_clearTable(state, &state->strings); cosmoT_clearTable(state, &state->strings);
// free our gray stack & finally free the state structure // free our gray stack & finally free the state structure
cosmoM_freearray(state, CObj*, state->grayStack.array, state->grayStack.capacity); cosmoM_freearray(state, CObj *, state->grayStack.array, state->grayStack.capacity);
// TODO: yeah idk, it looks like im missing 520 bytes somewhere? i'll look into it later // TODO: yeah idk, it looks like im missing 520 bytes somewhere? i'll look into it later
/*#ifdef GC_DEBUG /*#ifdef GC_DEBUG
if (state->allocatedBytes != sizeof(CState)) { if (state->allocatedBytes != sizeof(CState)) {
printf("state->allocatedBytes doesn't match expected value (%lu), got %lu!", sizeof(CState), state->allocatedBytes); printf("state->allocatedBytes doesn't match expected value (%lu), got %lu!",
exit(0); sizeof(CState), state->allocatedBytes); exit(0);
} }
#endif*/ #endif*/
free(state); free(state);
} }
// expects 2*pairs values on the stack, each pair should consist of 1 key and 1 value // expects 2*pairs values on the stack, each pair should consist of 1 key and 1 value
void cosmoV_register(CState *state, int pairs) { void cosmoV_register(CState *state, int pairs)
{
for (int i = 0; i < pairs; i++) { for (int i = 0; i < pairs; i++) {
StkPtr key = cosmoV_getTop(state, 1); StkPtr key = cosmoV_getTop(state, 1);
StkPtr val = cosmoV_getTop(state, 0); StkPtr val = cosmoV_getTop(state, 0);
@ -120,7 +124,8 @@ void cosmoV_register(CState *state, int pairs) {
} }
} }
void cosmoV_printStack(CState *state) { void cosmoV_printStack(CState *state)
{
printf("==== [[ stack dump ]] ====\n"); printf("==== [[ stack dump ]] ====\n");
for (CValue *top = state->top - 1; top >= state->stack; top--) { for (CValue *top = state->top - 1; top >= state->stack; top--) {
printf("%d: ", (int)(top - state->stack)); printf("%d: ", (int)(top - state->stack));

View File

@ -1,18 +1,20 @@
#ifndef CSTATE_H #ifndef CSTATE_H
#define CSTATE_H #define CSTATE_H
#include "cosmo.h"
#include "cobj.h" #include "cobj.h"
#include "cvalue.h" #include "cosmo.h"
#include "ctable.h" #include "ctable.h"
#include "cvalue.h"
struct CCallFrame { struct CCallFrame
{
CObjClosure *closure; CObjClosure *closure;
INSTRUCTION *pc; INSTRUCTION *pc;
CValue* base; CValue *base;
}; };
typedef enum IStringEnum { typedef enum IStringEnum
{
ISTRING_INIT, // __init ISTRING_INIT, // __init
ISTRING_TOSTRING, // __tostring ISTRING_TOSTRING, // __tostring
ISTRING_TONUMBER, // __tonumber ISTRING_TONUMBER, // __tonumber
@ -25,24 +27,29 @@ typedef enum IStringEnum {
ISTRING_ITER, // __iter ISTRING_ITER, // __iter
ISTRING_NEXT, // __next ISTRING_NEXT, // __next
ISTRING_RESERVED, // __reserved ISTRING_RESERVED, // __reserved
ISTRING_MAX // if this becomes greater than 33, we are out of space in cosmo_Flag. you'll have to change that to uint64_t ISTRING_MAX // if this becomes greater than 33, we are out of space in cosmo_Flag. you'll have
// to change that to uint64_t
} IStringEnum; } IStringEnum;
typedef struct ArrayCObj { typedef struct ArrayCObj
{
CObj **array; CObj **array;
int count; int count;
int capacity; int capacity;
} ArrayCObj; } ArrayCObj;
struct CState { struct CState
{
bool panic; bool panic;
int freezeGC; // when > 0, GC events will be ignored (for internal use) int freezeGC; // when > 0, GC events will be ignored (for internal use)
int frameCount; int frameCount;
CObjError *error; // NULL, unless panic is true CObjError *error; // NULL, unless panic is true
CObj *objects; // tracks all of our allocated objects CObj *objects; // tracks all of our allocated objects
CObj *userRoots; // user definable roots, this holds CObjs that should be considered "roots", lets the VM know you are holding a reference to a CObj in your code CObj *userRoots; // user definable roots, this holds CObjs that should be considered "roots",
ArrayCObj grayStack; // keeps track of which objects *haven't yet* been traversed in our GC, but *have been* found // lets the VM know you are holding a reference to a CObj in your code
ArrayCObj grayStack; // keeps track of which objects *haven't yet* been traversed in our GC, but
// *have been* found
size_t allocatedBytes; size_t allocatedBytes;
size_t nextGC; // when allocatedBytes reaches this threshhold, trigger a GC event size_t nextGC; // when allocatedBytes reaches this threshhold, trigger a GC event
@ -52,7 +59,8 @@ struct CState {
CValue *top; // top of the stack CValue *top; // top of the stack
CObjObject *protoObjects[COBJ_MAX]; // proto object for each COBJ type [NULL = no default proto] CObjObject *protoObjects[COBJ_MAX]; // proto object for each COBJ type [NULL = no default proto]
CObjString *iStrings[ISTRING_MAX]; // strings used internally by the VM, eg. __init, __index & friends CObjString
*iStrings[ISTRING_MAX]; // strings used internally by the VM, eg. __init, __index & friends
CCallFrame callFrame[FRAME_MAX]; // call frames CCallFrame callFrame[FRAME_MAX]; // call frames
CValue stack[STACK_MAX]; // stack CValue stack[STACK_MAX]; // stack
}; };

View File

@ -1,7 +1,8 @@
#include "ctable.h" #include "ctable.h"
#include "cmem.h" #include "cmem.h"
#include "cvalue.h"
#include "cobj.h" #include "cobj.h"
#include "cvalue.h"
#include <string.h> #include <string.h>
@ -10,12 +11,15 @@
#define MIN_TABLE_CAPACITY ARRAY_START #define MIN_TABLE_CAPACITY ARRAY_START
// bit-twiddling hacks, gets the next power of 2 // bit-twiddling hacks, gets the next power of 2
unsigned int nextPow2(unsigned int x) { unsigned int nextPow2(unsigned int x)
if (x <= ARRAY_START - 1) return ARRAY_START; // sanity check {
if (x <= ARRAY_START - 1)
return ARRAY_START; // sanity check
x--; x--;
int power = 2; int power = 2;
while (x >>= 1) power <<= 1; while (x >>= 1)
power <<= 1;
if (power < ARRAY_START) if (power < ARRAY_START)
return ARRAY_START; return ARRAY_START;
@ -23,7 +27,8 @@ unsigned int nextPow2(unsigned int x) {
return power; return power;
} }
void cosmoT_initTable(CState *state, CTable *tbl, int startCap) { void cosmoT_initTable(CState *state, CTable *tbl, int startCap)
{
startCap = startCap != 0 ? startCap : ARRAY_START; // sanity check :P startCap = startCap != 0 ? startCap : ARRAY_START; // sanity check :P
tbl->capacityMask = startCap - 1; tbl->capacityMask = startCap - 1;
@ -39,7 +44,8 @@ void cosmoT_initTable(CState *state, CTable *tbl, int startCap) {
} }
} }
void cosmoT_addTable(CState *state, CTable *from, CTable *to) { void cosmoT_addTable(CState *state, CTable *from, CTable *to)
{
int cap = from->capacityMask + 1; int cap = from->capacityMask + 1;
for (int i = 0; i < cap; i++) { for (int i = 0; i < cap; i++) {
CTableEntry *entry = &from->table[i]; CTableEntry *entry = &from->table[i];
@ -51,32 +57,36 @@ void cosmoT_addTable(CState *state, CTable *from, CTable *to) {
} }
} }
void cosmoT_clearTable(CState *state, CTable *tbl) { void cosmoT_clearTable(CState *state, CTable *tbl)
{
cosmoM_freearray(state, CTableEntry, tbl->table, (tbl->capacityMask + 1)); cosmoM_freearray(state, CTableEntry, tbl->table, (tbl->capacityMask + 1));
} }
uint32_t getObjectHash(CObj *obj) { uint32_t getObjectHash(CObj *obj)
switch(obj->type) { {
switch (obj->type) {
case COBJ_STRING: case COBJ_STRING:
return ((CObjString*)obj)->hash; return ((CObjString *)obj)->hash;
default: default:
return (uint32_t)obj; // just "hash" the pointer return (uint32_t)obj; // just "hash" the pointer
} }
} }
uint32_t getValueHash(CValue *val) { uint32_t getValueHash(CValue *val)
{
switch (GET_TYPE(*val)) { switch (GET_TYPE(*val)) {
case COSMO_TREF: case COSMO_TREF:
return getObjectHash(cosmoV_readRef(*val)); return getObjectHash(cosmoV_readRef(*val));
case COSMO_TNUMBER: { case COSMO_TNUMBER: {
uint32_t buf[sizeof(cosmo_Number)/sizeof(uint32_t)]; uint32_t buf[sizeof(cosmo_Number) / sizeof(uint32_t)];
cosmo_Number num = cosmoV_readNumber(*val); cosmo_Number num = cosmoV_readNumber(*val);
if (num == 0) if (num == 0)
return 0; return 0;
memcpy(buf, &num, sizeof(buf)); memcpy(buf, &num, sizeof(buf));
for (size_t i = 0; i < sizeof(cosmo_Number)/sizeof(uint32_t); i++) buf[0] += buf[i]; for (size_t i = 0; i < sizeof(cosmo_Number) / sizeof(uint32_t); i++)
buf[0] += buf[i];
return buf[0]; return buf[0];
} }
// TODO: add support for other types // TODO: add support for other types
@ -86,9 +96,11 @@ uint32_t getValueHash(CValue *val) {
} }
// mask should always be (capacity - 1) // mask should always be (capacity - 1)
static CTableEntry *findEntry(CState *state, CTableEntry *entries, int mask, CValue key) { static CTableEntry *findEntry(CState *state, CTableEntry *entries, int mask, CValue key)
{
uint32_t hash = getValueHash(&key); uint32_t hash = getValueHash(&key);
uint32_t indx = hash & mask; // since we know the capacity will *always* be a power of 2, we can use bitwise & to perform a MUCH faster mod operation uint32_t indx = hash & mask; // since we know the capacity will *always* be a power of 2, we can
// use bitwise & to perform a MUCH faster mod operation
CTableEntry *tomb = NULL; CTableEntry *tomb = NULL;
// keep looking for an open slot in the entries array // keep looking for an open slot in the entries array
@ -112,7 +124,8 @@ static CTableEntry *findEntry(CState *state, CTableEntry *entries, int mask, CVa
} }
} }
static void resizeTbl(CState *state, CTable *tbl, int newCapacity, bool canShrink) { static void resizeTbl(CState *state, CTable *tbl, int newCapacity, bool canShrink)
{
if (canShrink && cosmoT_checkShrink(state, tbl)) if (canShrink && cosmoT_checkShrink(state, tbl))
return; return;
@ -122,7 +135,8 @@ static void resizeTbl(CState *state, CTable *tbl, int newCapacity, bool canShrin
cosmoM_checkGarbage(state, size); // if this allocation would cause a GC, run the GC cosmoM_checkGarbage(state, size); // if this allocation would cause a GC, run the GC
if (tbl->count < cachedCount) // the GC removed some objects from this table and resized it, ignore our resize event! if (tbl->count < cachedCount) // the GC removed some objects from this table and resized it,
// ignore our resize event!
return; return;
CTableEntry *entries = cosmoM_xmalloc(state, size); CTableEntry *entries = cosmoM_xmalloc(state, size);
@ -157,10 +171,14 @@ static void resizeTbl(CState *state, CTable *tbl, int newCapacity, bool canShrin
tbl->tombstones = 0; tbl->tombstones = 0;
} }
bool cosmoT_checkShrink(CState *state, CTable *tbl) { bool cosmoT_checkShrink(CState *state, CTable *tbl)
{
// if count > 8 and active entries < tombstones // if count > 8 and active entries < tombstones
if (tbl->count > MIN_TABLE_CAPACITY && (tbl->count - tbl->tombstones < tbl->tombstones || tbl->tombstones > 50)) { // TODO: 50 should be a threshhold if (tbl->count > MIN_TABLE_CAPACITY &&
resizeTbl(state, tbl, nextPow2(tbl->count - tbl->tombstones) * GROW_FACTOR, false); // shrink based on active entries to the next pow of 2 (tbl->count - tbl->tombstones < tbl->tombstones ||
tbl->tombstones > 50)) { // TODO: 50 should be a threshhold
resizeTbl(state, tbl, nextPow2(tbl->count - tbl->tombstones) * GROW_FACTOR,
false); // shrink based on active entries to the next pow of 2
return true; return true;
} }
@ -168,7 +186,8 @@ bool cosmoT_checkShrink(CState *state, CTable *tbl) {
} }
// returns a pointer to the allocated value // returns a pointer to the allocated value
COSMO_API CValue* cosmoT_insert(CState *state, CTable *tbl, CValue key) { COSMO_API CValue *cosmoT_insert(CState *state, CTable *tbl, CValue key)
{
// make sure we have enough space allocated // make sure we have enough space allocated
int cap = tbl->capacityMask + 1; int cap = tbl->capacityMask + 1;
if (tbl->count + 1 > (int)(cap * MAX_TABLE_FILL)) { if (tbl->count + 1 > (int)(cap * MAX_TABLE_FILL)) {
@ -178,7 +197,8 @@ COSMO_API CValue* cosmoT_insert(CState *state, CTable *tbl, CValue key) {
} }
// insert into the table // insert into the table
CTableEntry *entry = findEntry(state, tbl->table, tbl->capacityMask, key); // -1 for our capacity mask CTableEntry *entry =
findEntry(state, tbl->table, tbl->capacityMask, key); // -1 for our capacity mask
if (IS_NIL(entry->key)) { if (IS_NIL(entry->key)) {
if (IS_NIL(entry->val)) // is it empty? if (IS_NIL(entry->val)) // is it empty?
@ -191,7 +211,8 @@ COSMO_API CValue* cosmoT_insert(CState *state, CTable *tbl, CValue key) {
return &entry->val; return &entry->val;
} }
bool cosmoT_get(CState *state, CTable *tbl, CValue key, CValue *val) { bool cosmoT_get(CState *state, CTable *tbl, CValue key, CValue *val)
{
// sanity check // sanity check
if (tbl->count == 0) { if (tbl->count == 0) {
*val = cosmoV_newNil(); *val = cosmoV_newNil();
@ -205,8 +226,10 @@ bool cosmoT_get(CState *state, CTable *tbl, CValue key, CValue *val) {
return !(IS_NIL(entry->key)); return !(IS_NIL(entry->key));
} }
bool cosmoT_remove(CState* state, CTable *tbl, CValue key) { bool cosmoT_remove(CState *state, CTable *tbl, CValue key)
if (tbl->count == 0) return 0; // sanity check {
if (tbl->count == 0)
return 0; // sanity check
CTableEntry *entry = findEntry(state, tbl->table, tbl->capacityMask, key); CTableEntry *entry = findEntry(state, tbl->table, tbl->capacityMask, key);
if (IS_NIL(entry->key)) // sanity check if (IS_NIL(entry->key)) // sanity check
@ -214,20 +237,26 @@ bool cosmoT_remove(CState* state, CTable *tbl, CValue key) {
// crafts tombstone // crafts tombstone
entry->key = cosmoV_newNil(); // this has to be nil entry->key = cosmoV_newNil(); // this has to be nil
entry->val = cosmoV_newBoolean(false); // doesn't really matter what this is, as long as it isn't nil entry->val =
cosmoV_newBoolean(false); // doesn't really matter what this is, as long as it isn't nil
tbl->tombstones++; tbl->tombstones++;
return true; return true;
} }
// returns the active entry count // returns the active entry count
COSMO_API int cosmoT_count(CTable *tbl) { COSMO_API int cosmoT_count(CTable *tbl)
{
return tbl->count - tbl->tombstones; return tbl->count - tbl->tombstones;
} }
CObjString *cosmoT_lookupString(CTable *tbl, const char *str, int length, uint32_t hash) { CObjString *cosmoT_lookupString(CTable *tbl, const char *str, int length, uint32_t hash)
if (tbl->count == 0) return 0; // sanity check {
uint32_t indx = hash & tbl->capacityMask; // since we know the capacity will *always* be a power of 2, we can use bitwise & to perform a MUCH faster mod operation if (tbl->count == 0)
return 0; // sanity check
uint32_t indx =
hash & tbl->capacityMask; // since we know the capacity will *always* be a power of 2, we
// can use bitwise & to perform a MUCH faster mod operation
// keep looking for an open slot in the entries array // keep looking for an open slot in the entries array
while (true) { while (true) {
@ -236,9 +265,10 @@ CObjString *cosmoT_lookupString(CTable *tbl, const char *str, int length, uint32
// check if it's an empty slot (meaning we dont have it in the table) // check if it's an empty slot (meaning we dont have it in the table)
if (IS_NIL(entry->key) && IS_NIL(entry->val)) { if (IS_NIL(entry->key) && IS_NIL(entry->val)) {
return NULL; return NULL;
} else if (IS_STRING(entry->key) && cosmoV_readString(entry->key)->length == length && memcmp(cosmoV_readString(entry->key)->str, str, length) == 0) { } else if (IS_STRING(entry->key) && cosmoV_readString(entry->key)->length == length &&
memcmp(cosmoV_readString(entry->key)->str, str, length) == 0) {
// it's a match! // it's a match!
return (CObjString*)cosmoV_readRef(entry->key); return (CObjString *)cosmoV_readRef(entry->key);
} }
indx = (indx + 1) & tbl->capacityMask; // fast mod here too indx = (indx + 1) & tbl->capacityMask; // fast mod here too
@ -246,7 +276,8 @@ CObjString *cosmoT_lookupString(CTable *tbl, const char *str, int length, uint32
} }
// for debugging purposes // for debugging purposes
void cosmoT_printTable(CTable *tbl, const char *name) { void cosmoT_printTable(CTable *tbl, const char *name)
{
printf("==== [[%s]] ====\n", name); printf("==== [[%s]] ====\n", name);
int cap = tbl->capacityMask + 1; int cap = tbl->capacityMask + 1;
for (int i = 0; i < cap; i++) { for (int i = 0; i < cap; i++) {

View File

@ -1,17 +1,20 @@
#ifndef CTABLE_H #ifndef CTABLE_H
#define CTABLE_H #define CTABLE_H
/* TODO: rewrite this table implementation. compared to other languages (including python!) this table is verrryyyy slow */ /* TODO: rewrite this table implementation. compared to other languages (including python!) this
* table is verrryyyy slow */
#include "cosmo.h" #include "cosmo.h"
#include "cvalue.h" #include "cvalue.h"
typedef struct CTableEntry { typedef struct CTableEntry
{
CValue key; CValue key;
CValue val; CValue val;
} CTableEntry; } CTableEntry;
typedef struct CTable { typedef struct CTable
{
int count; int count;
int capacityMask; // +1 to get the capacity int capacityMask; // +1 to get the capacity
int tombstones; int tombstones;

View File

@ -1,48 +1,59 @@
#include "cosmo.h"
#include "cmem.h"
#include "cvalue.h" #include "cvalue.h"
#include "cobj.h"
void initValArray(CState *state, CValueArray *val, size_t startCapacity) { #include "cmem.h"
#include "cobj.h"
#include "cosmo.h"
void initValArray(CState *state, CValueArray *val, size_t startCapacity)
{
val->count = 0; val->count = 0;
val->capacity = startCapacity; val->capacity = startCapacity;
val->values = NULL; val->values = NULL;
} }
void cleanValArray(CState *state, CValueArray *array) { void cleanValArray(CState *state, CValueArray *array)
{
cosmoM_freearray(state, CValue, array->values, array->capacity); cosmoM_freearray(state, CValue, array->values, array->capacity);
} }
void appendValArray(CState *state, CValueArray *array, CValue val) { void appendValArray(CState *state, CValueArray *array, CValue val)
{
cosmoM_growarray(state, CValue, array->values, array->count, array->capacity); cosmoM_growarray(state, CValue, array->values, array->count, array->capacity);
array->values[array->count++] = val; array->values[array->count++] = val;
} }
bool cosmoV_equal(CState *state, CValue valA, CValue valB) { bool cosmoV_equal(CState *state, CValue valA, CValue valB)
{
if (GET_TYPE(valA) != GET_TYPE(valB)) // are they the same type? if (GET_TYPE(valA) != GET_TYPE(valB)) // are they the same type?
return false; return false;
// compare // compare
switch (GET_TYPE(valA)) { switch (GET_TYPE(valA)) {
case COSMO_TBOOLEAN: return cosmoV_readBoolean(valA) == cosmoV_readBoolean(valB); case COSMO_TBOOLEAN:
case COSMO_TNUMBER: return cosmoV_readNumber(valA) == cosmoV_readNumber(valB); return cosmoV_readBoolean(valA) == cosmoV_readBoolean(valB);
case COSMO_TREF: return cosmoO_equal(state, cosmoV_readRef(valA), cosmoV_readRef(valB)); case COSMO_TNUMBER:
case COSMO_TNIL: return true; return cosmoV_readNumber(valA) == cosmoV_readNumber(valB);
case COSMO_TREF:
return cosmoO_equal(state, cosmoV_readRef(valA), cosmoV_readRef(valB));
case COSMO_TNIL:
return true;
default: default:
return false; return false;
} }
} }
CObjString *cosmoV_toString(CState *state, CValue val) { CObjString *cosmoV_toString(CState *state, CValue val)
{
switch (GET_TYPE(val)) { switch (GET_TYPE(val)) {
case COSMO_TNUMBER: { case COSMO_TNUMBER: {
char buf[32]; char buf[32];
int size = snprintf((char*)&buf, 32, "%.14g", cosmoV_readNumber(val)); int size = snprintf((char *)&buf, 32, "%.14g", cosmoV_readNumber(val));
return cosmoO_copyString(state, (char*)&buf, size); return cosmoO_copyString(state, (char *)&buf, size);
} }
case COSMO_TBOOLEAN: { case COSMO_TBOOLEAN: {
return cosmoV_readBoolean(val) ? cosmoO_copyString(state, "true", 4) : cosmoO_copyString(state, "false", 5); return cosmoV_readBoolean(val) ? cosmoO_copyString(state, "true", 4)
: cosmoO_copyString(state, "false", 5);
} }
case COSMO_TREF: { case COSMO_TREF: {
return cosmoO_toString(state, cosmoV_readRef(val)); return cosmoO_toString(state, cosmoV_readRef(val));
@ -55,8 +66,9 @@ CObjString *cosmoV_toString(CState *state, CValue val) {
} }
} }
cosmo_Number cosmoV_toNumber(CState *state, CValue val) { cosmo_Number cosmoV_toNumber(CState *state, CValue val)
switch(GET_TYPE(val)) { {
switch (GET_TYPE(val)) {
case COSMO_TNUMBER: { case COSMO_TNUMBER: {
return cosmoV_readNumber(val); return cosmoV_readNumber(val);
} }
@ -72,19 +84,25 @@ cosmo_Number cosmoV_toNumber(CState *state, CValue val) {
} }
} }
const char *cosmoV_typeStr(CValue val) { const char *cosmoV_typeStr(CValue val)
{
switch (GET_TYPE(val)) { switch (GET_TYPE(val)) {
case COSMO_TNIL: return "<nil>"; case COSMO_TNIL:
case COSMO_TBOOLEAN: return "<bool>"; return "<nil>";
case COSMO_TNUMBER: return "<number>"; case COSMO_TBOOLEAN:
case COSMO_TREF: return cosmoO_typeStr(cosmoV_readRef(val)); return "<bool>";
case COSMO_TNUMBER:
return "<number>";
case COSMO_TREF:
return cosmoO_typeStr(cosmoV_readRef(val));
default: default:
return "<unkn val>"; return "<unkn val>";
} }
} }
void printValue(CValue val) { void printValue(CValue val)
{
switch (GET_TYPE(val)) { switch (GET_TYPE(val)) {
case COSMO_TNUMBER: case COSMO_TNUMBER:
printf("%g", cosmoV_readNumber(val)); printf("%g", cosmoV_readNumber(val));

View File

@ -3,7 +3,8 @@
#include "cosmo.h" #include "cosmo.h"
typedef enum { typedef enum
{
COSMO_TNUMBER, // number has to be 0 because NaN box COSMO_TNUMBER, // number has to be 0 because NaN box
COSMO_TBOOLEAN, COSMO_TBOOLEAN,
COSMO_TREF, COSMO_TREF,
@ -18,8 +19,8 @@ typedef double cosmo_Number;
#ifdef NAN_BOXXED #ifdef NAN_BOXXED
/* /*
NaN box, this is great for fitting more in the cpu cache on x86_64 or ARM64 architectures. If you don't know how this works please reference these NaN box, this is great for fitting more in the cpu cache on x86_64 or ARM64 architectures.
two articles: If you don't know how this works please reference these two articles:
https://leonardschuetz.ch/blog/nan-boxing/ and https://piotrduperas.com/posts/nan-boxing/ https://leonardschuetz.ch/blog/nan-boxing/ and https://piotrduperas.com/posts/nan-boxing/
@ -27,83 +28,88 @@ typedef double cosmo_Number;
TL;DR: we can store payloads in the NaN value in the IEEE 754 standard. TL;DR: we can store payloads in the NaN value in the IEEE 754 standard.
*/ */
union CValue { union CValue
{
uint64_t data; uint64_t data;
cosmo_Number num; cosmo_Number num;
}; };
#define MASK_TYPE ((uint64_t)0x0007000000000000) # define MASK_TYPE ((uint64_t)0x0007000000000000)
#define MASK_PAYLOAD ((uint64_t)0x0000ffffffffffff) # define MASK_PAYLOAD ((uint64_t)0x0000ffffffffffff)
// 3 bits (low bits) are reserved for the type // 3 bits (low bits) are reserved for the type
#define MAKE_PAYLOAD(x) ((uint64_t)(x) & MASK_PAYLOAD) # define MAKE_PAYLOAD(x) ((uint64_t)(x)&MASK_PAYLOAD)
#define READ_PAYLOAD(x) ((x).data & MASK_PAYLOAD) # define READ_PAYLOAD(x) ((x).data & MASK_PAYLOAD)
// The bits that must be set to indicate a quiet NaN. // The bits that must be set to indicate a quiet NaN.
#define MASK_QUIETNAN ((uint64_t)0x7ff8000000000000) # define MASK_QUIETNAN ((uint64_t)0x7ff8000000000000)
#define GET_TYPE(x) \ # define GET_TYPE(x) \
((((x).data & MASK_QUIETNAN) == MASK_QUIETNAN) ? (((x).data & MASK_TYPE) >> 48) : COSMO_TNUMBER) ((((x).data & MASK_QUIETNAN) == MASK_QUIETNAN) ? (((x).data & MASK_TYPE) >> 48) \
: COSMO_TNUMBER)
#define SIG_MASK (MASK_QUIETNAN | MASK_TYPE) # define SIG_MASK (MASK_QUIETNAN | MASK_TYPE)
#define BOOL_SIG (MASK_QUIETNAN | ((uint64_t)(COSMO_TBOOLEAN) << 48)) # define BOOL_SIG (MASK_QUIETNAN | ((uint64_t)(COSMO_TBOOLEAN) << 48))
#define OBJ_SIG (MASK_QUIETNAN | ((uint64_t)(COSMO_TREF) << 48)) # define OBJ_SIG (MASK_QUIETNAN | ((uint64_t)(COSMO_TREF) << 48))
#define NIL_SIG (MASK_QUIETNAN | ((uint64_t)(COSMO_TNIL) << 48)) # define NIL_SIG (MASK_QUIETNAN | ((uint64_t)(COSMO_TNIL) << 48))
#define cosmoV_newNumber(x) ((CValue){.num = x}) # define cosmoV_newNumber(x) ((CValue){.num = x})
#define cosmoV_newBoolean(x) ((CValue){.data = MAKE_PAYLOAD(x) | BOOL_SIG}) # define cosmoV_newBoolean(x) ((CValue){.data = MAKE_PAYLOAD(x) | BOOL_SIG})
#define cosmoV_newRef(x) ((CValue){.data = MAKE_PAYLOAD((uintptr_t)x) | OBJ_SIG}) # define cosmoV_newRef(x) ((CValue){.data = MAKE_PAYLOAD((uintptr_t)x) | OBJ_SIG})
#define cosmoV_newNil() ((CValue){.data = NIL_SIG}) # define cosmoV_newNil() ((CValue){.data = NIL_SIG})
#define cosmoV_readNumber(x) ((x).num) # define cosmoV_readNumber(x) ((x).num)
#define cosmoV_readBoolean(x) ((bool)READ_PAYLOAD(x)) # define cosmoV_readBoolean(x) ((bool)READ_PAYLOAD(x))
#define cosmoV_readRef(x) ((CObj*)READ_PAYLOAD(x)) # define cosmoV_readRef(x) ((CObj *)READ_PAYLOAD(x))
#define IS_NUMBER(x) (((x).data & MASK_QUIETNAN) != MASK_QUIETNAN) # define IS_NUMBER(x) (((x).data & MASK_QUIETNAN) != MASK_QUIETNAN)
#define IS_BOOLEAN(x) (((x).data & SIG_MASK) == BOOL_SIG) # define IS_BOOLEAN(x) (((x).data & SIG_MASK) == BOOL_SIG)
#define IS_NIL(x) (((x).data & SIG_MASK) == NIL_SIG) # define IS_NIL(x) (((x).data & SIG_MASK) == NIL_SIG)
#define IS_REF(x) (((x).data & SIG_MASK) == OBJ_SIG) # define IS_REF(x) (((x).data & SIG_MASK) == OBJ_SIG)
#else #else
/* /*
Tagged union, this is the best platform independent solution Tagged union, this is the best platform independent solution
*/ */
struct CValue { struct CValue
{
CosmoType type; CosmoType type;
union { union
{
cosmo_Number num; cosmo_Number num;
bool b; // boolean bool b; // boolean
CObj *obj; CObj *obj;
} val; } val;
}; };
#define GET_TYPE(x) ((x).type) # define GET_TYPE(x) ((x).type)
// create CValues // create CValues
#define cosmoV_newNumber(x) ((CValue){COSMO_TNUMBER, {.num = (x)}}) # define cosmoV_newNumber(x) ((CValue){COSMO_TNUMBER, {.num = (x)}})
#define cosmoV_newBoolean(x) ((CValue){COSMO_TBOOLEAN, {.b = (x)}}) # define cosmoV_newBoolean(x) ((CValue){COSMO_TBOOLEAN, {.b = (x)}})
#define cosmoV_newRef(x) ((CValue){COSMO_TREF, {.obj = (CObj*)(x)}}) # define cosmoV_newRef(x) ((CValue){COSMO_TREF, {.obj = (CObj *)(x)}})
#define cosmoV_newNil() ((CValue){COSMO_TNIL, {.num = 0}}) # define cosmoV_newNil() ((CValue){COSMO_TNIL, {.num = 0}})
// read CValues // read CValues
#define cosmoV_readNumber(x) ((cosmo_Number)(x).val.num) # define cosmoV_readNumber(x) ((cosmo_Number)(x).val.num)
#define cosmoV_readBoolean(x) ((bool)(x).val.b) # define cosmoV_readBoolean(x) ((bool)(x).val.b)
// grabs the CObj* pointer from the CValue // grabs the CObj* pointer from the CValue
#define cosmoV_readRef(x) ((CObj*)(x).val.obj) # define cosmoV_readRef(x) ((CObj *)(x).val.obj)
#define IS_NUMBER(x) (GET_TYPE(x) == COSMO_TNUMBER) # define IS_NUMBER(x) (GET_TYPE(x) == COSMO_TNUMBER)
#define IS_BOOLEAN(x) (GET_TYPE(x) == COSMO_TBOOLEAN) # define IS_BOOLEAN(x) (GET_TYPE(x) == COSMO_TBOOLEAN)
#define IS_NIL(x) (GET_TYPE(x) == COSMO_TNIL) # define IS_NIL(x) (GET_TYPE(x) == COSMO_TNIL)
#define IS_REF(x) (GET_TYPE(x) == COSMO_TREF) # define IS_REF(x) (GET_TYPE(x) == COSMO_TREF)
#endif #endif
typedef CValue* StkPtr; typedef CValue *StkPtr;
struct CValueArray { struct CValueArray
{
size_t capacity; size_t capacity;
size_t count; size_t count;
CValue *values; CValue *values;
@ -117,6 +123,7 @@ void printValue(CValue val);
COSMO_API bool cosmoV_equal(CState *state, CValue valA, CValue valB); COSMO_API bool cosmoV_equal(CState *state, CValue valA, CValue valB);
COSMO_API CObjString *cosmoV_toString(CState *state, CValue val); COSMO_API CObjString *cosmoV_toString(CState *state, CValue val);
COSMO_API cosmo_Number cosmoV_toNumber(CState *state, CValue val); COSMO_API cosmo_Number cosmoV_toNumber(CState *state, CValue val);
COSMO_API const char *cosmoV_typeStr(CValue val); // return constant char array for corresponding type COSMO_API const char *
cosmoV_typeStr(CValue val); // return constant char array for corresponding type
#endif #endif

342
src/cvm.c
View File

@ -1,15 +1,16 @@
#include "cvm.h" #include "cvm.h"
#include "cstate.h"
#include "cdebug.h" #include "cdebug.h"
#include "cmem.h" #include "cmem.h"
#include "cparse.h" #include "cparse.h"
#include "cstate.h"
#include <math.h>
#include <stdarg.h> #include <stdarg.h>
#include <string.h> #include <string.h>
#include <math.h> COSMO_API void cosmoV_pushFString(CState *state, const char *format, ...)
{
COSMO_API void cosmoV_pushFString(CState *state, const char *format, ...) {
va_list args; va_list args;
va_start(args, format); va_start(args, format);
cosmoO_pushVFString(state, format, args); cosmoO_pushVFString(state, format, args);
@ -17,18 +18,20 @@ COSMO_API void cosmoV_pushFString(CState *state, const char *format, ...) {
} }
// inserts val at state->top - indx - 1, moving everything else up // inserts val at state->top - indx - 1, moving everything else up
COSMO_API void cosmo_insert(CState *state, int indx, CValue val) { COSMO_API void cosmo_insert(CState *state, int indx, CValue val)
{
StkPtr tmp = cosmoV_getTop(state, indx); StkPtr tmp = cosmoV_getTop(state, indx);
// moves everything up // moves everything up
for (StkPtr i = state->top; i > tmp; i--) for (StkPtr i = state->top; i > tmp; i--)
*i = *(i-1); *i = *(i - 1);
*tmp = val; *tmp = val;
state->top++; state->top++;
} }
COSMO_API bool cosmoV_compileString(CState *state, const char *src, const char *name) { COSMO_API bool cosmoV_compileString(CState *state, const char *src, const char *name)
{
CObjFunction *func; CObjFunction *func;
if ((func = cosmoP_compileString(state, src, name)) != NULL) { if ((func = cosmoP_compileString(state, src, name)) != NULL) {
@ -36,19 +39,21 @@ COSMO_API bool cosmoV_compileString(CState *state, const char *src, const char *
#ifdef VM_DEBUG #ifdef VM_DEBUG
disasmChunk(&func->chunk, func->module->str, 0); disasmChunk(&func->chunk, func->module->str, 0);
#endif #endif
// push function onto the stack so it doesn't it cleaned up by the GC, at the same stack location put our closure // push function onto the stack so it doesn't it cleaned up by the GC, at the same stack
cosmoV_pushRef(state, (CObj*)func); // location put our closure
cosmoV_pushRef(state, (CObj *)func);
*(cosmoV_getTop(state, 0)) = cosmoV_newRef(cosmoO_newClosure(state, func)); *(cosmoV_getTop(state, 0)) = cosmoV_newRef(cosmoO_newClosure(state, func));
return true; return true;
} }
// fail // fail
state->panic = false; state->panic = false;
cosmoV_pushRef(state, (CObj*)state->error); cosmoV_pushRef(state, (CObj *)state->error);
return false; return false;
} }
COSMO_API void cosmoV_printError(CState *state, CObjError *err) { COSMO_API void cosmoV_printError(CState *state, CObjError *err)
{
// print stack trace // print stack trace
for (int i = 0; i < err->frameCount; i++) { for (int i = 0; i < err->frameCount; i++) {
CCallFrame *frame = &err->frames[i]; CCallFrame *frame = &err->frames[i];
@ -57,8 +62,11 @@ COSMO_API void cosmoV_printError(CState *state, CObjError *err) {
int line = chunk->lineInfo[frame->pc - chunk->buf - 1]; int line = chunk->lineInfo[frame->pc - chunk->buf - 1];
if (i == err->frameCount - 1 && !err->parserError) // it's the last call frame (and not a parser error), prepare for the objection to be printed if (i == err->frameCount - 1 &&
fprintf(stderr, "Objection in %.*s on [line %d] in ", function->module->length, function->module->str, line); !err->parserError) // it's the last call frame (and not a parser error), prepare for the
// objection to be printed
fprintf(stderr, "Objection in %.*s on [line %d] in ", function->module->length,
function->module->str, line);
else else
fprintf(stderr, "[line %d] in ", line); fprintf(stderr, "[line %d] in ", line);
@ -78,11 +86,12 @@ COSMO_API void cosmoV_printError(CState *state, CObjError *err) {
} }
/* /*
takes value on top of the stack and wraps an CObjError around it, state->error is set to that value takes value on top of the stack and wraps an CObjError around it, state->error is set to that
the value on the stack is *expected* to be a string, but not required, so value the value on the stack is *expected* to be a string, but not required, so yes, this means
yes, this means you could throw a nil value if you really wanted too.. you could throw a nil value if you really wanted too..
*/ */
CObjError* cosmoV_throw(CState *state) { CObjError *cosmoV_throw(CState *state)
{
StkPtr temp = cosmoV_getTop(state, 0); StkPtr temp = cosmoV_getTop(state, 0);
CObjError *error = cosmoO_newError(state, *temp); CObjError *error = cosmoO_newError(state, *temp);
@ -93,7 +102,8 @@ CObjError* cosmoV_throw(CState *state) {
return error; return error;
} }
void cosmoV_error(CState *state, const char *format, ...) { void cosmoV_error(CState *state, const char *format, ...)
{
if (state->panic) if (state->panic)
return; return;
@ -110,11 +120,13 @@ void cosmoV_error(CState *state, const char *format, ...) {
cosmoV_throw(state); cosmoV_throw(state);
} }
CObjUpval *captureUpvalue(CState *state, CValue *local) { CObjUpval *captureUpvalue(CState *state, CValue *local)
{
CObjUpval *prev = NULL; CObjUpval *prev = NULL;
CObjUpval *upvalue = state->openUpvalues; CObjUpval *upvalue = state->openUpvalues;
while (upvalue != NULL && upvalue->val > local) { // while upvalue exists and is higher on the stack than local while (upvalue != NULL &&
upvalue->val > local) { // while upvalue exists and is higher on the stack than local
prev = upvalue; prev = upvalue;
upvalue = upvalue->next; upvalue = upvalue->next;
} }
@ -136,8 +148,11 @@ CObjUpval *captureUpvalue(CState *state, CValue *local) {
return newUpval; return newUpval;
} }
void closeUpvalues(CState *state, CValue *local) { void closeUpvalues(CState *state, CValue *local)
while (state->openUpvalues != NULL && state->openUpvalues->val >= local) { // for every upvalue that points to the local or anything above it {
while (state->openUpvalues != NULL &&
state->openUpvalues->val >=
local) { // for every upvalue that points to the local or anything above it
CObjUpval *upvalue = state->openUpvalues; CObjUpval *upvalue = state->openUpvalues;
upvalue->closed = *upvalue->val; upvalue->closed = *upvalue->val;
upvalue->val = &upvalue->closed; // upvalue now points to itself :P upvalue->val = &upvalue->closed; // upvalue now points to itself :P
@ -145,7 +160,8 @@ void closeUpvalues(CState *state, CValue *local) {
} }
} }
void pushCallFrame(CState *state, CObjClosure *closure, int args) { void pushCallFrame(CState *state, CObjClosure *closure, int args)
{
#ifdef SAFE_STACK #ifdef SAFE_STACK
if (state->frameCount >= FRAME_MAX) { if (state->frameCount >= FRAME_MAX) {
cosmoV_error(state, "Callframe overflow!"); cosmoV_error(state, "Callframe overflow!");
@ -159,23 +175,27 @@ void pushCallFrame(CState *state, CObjClosure *closure, int args) {
frame->closure = closure; frame->closure = closure;
} }
// offset is the offset of the callframe base we set the state->top back too (useful for passing values in the stack as arguments, like methods) // offset is the offset of the callframe base we set the state->top back too (useful for passing
void popCallFrame(CState *state, int offset) { // values in the stack as arguments, like methods)
closeUpvalues(state, state->callFrame[state->frameCount - 1].base); // close any upvalue still open void popCallFrame(CState *state, int offset)
{
closeUpvalues(state,
state->callFrame[state->frameCount - 1].base); // close any upvalue still open
state->top = state->callFrame[state->frameCount - 1].base + offset; // resets the stack state->top = state->callFrame[state->frameCount - 1].base + offset; // resets the stack
state->frameCount--; state->frameCount--;
} }
void cosmoV_concat(CState *state, int vals) { void cosmoV_concat(CState *state, int vals)
{
StkPtr start = state->top - vals; StkPtr start = state->top - vals;
StkPtr end = cosmoV_getTop(state, 0); StkPtr end = cosmoV_getTop(state, 0);
CObjString *result = cosmoV_toString(state, *start); CObjString *result = cosmoV_toString(state, *start);
for (StkPtr current = start + 1; current <= end; current++) { for (StkPtr current = start + 1; current <= end; current++) {
cosmoV_pushRef(state, (CObj*)result); // so our GC can find our current result string cosmoV_pushRef(state, (CObj *)result); // so our GC can find our current result string
CObjString *otherStr = cosmoV_toString(state, *current); CObjString *otherStr = cosmoV_toString(state, *current);
cosmoV_pushRef(state, (CObj*)otherStr); // also so our GC won't free otherStr cosmoV_pushRef(state, (CObj *)otherStr); // also so our GC won't free otherStr
// concat the two strings together // concat the two strings together
size_t sz = result->length + otherStr->length; size_t sz = result->length + otherStr->length;
@ -190,34 +210,37 @@ void cosmoV_concat(CState *state, int vals) {
} }
state->top = start; state->top = start;
cosmoV_pushRef(state, (CObj*)result); cosmoV_pushRef(state, (CObj *)result);
} }
int cosmoV_execute(CState *state); int cosmoV_execute(CState *state);
bool invokeMethod(CState* state, CObj *obj, CValue func, int args, int nresults, int offset); bool invokeMethod(CState *state, CObj *obj, CValue func, int args, int nresults, int offset);
/* /*
calls a native C Function with # args on the stack, nresults are pushed onto the stack upon return. calls a native C Function with # args on the stack, nresults are pushed onto the stack upon
return.
returns: returns:
false: state paniced during C Function, error is at state->error false: state paniced during C Function, error is at state->error
true: state->top is moved to base + offset + nresults, with nresults pushed onto the stack from base + offset true: state->top is moved to base + offset + nresults, with nresults pushed onto the stack
from base + offset
*/ */
static bool callCFunction(CState *state, CosmoCFunction cfunc, int args, int nresults, int offset) { static bool callCFunction(CState *state, CosmoCFunction cfunc, int args, int nresults, int offset)
{
StkPtr savedBase = cosmoV_getTop(state, args); StkPtr savedBase = cosmoV_getTop(state, args);
// 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 // 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); cosmoM_freezeGC(state);
int nres = cfunc(state, args, savedBase + 1); int nres = cfunc(state, args, savedBase + 1);
cosmoM_unfreezeGC(state); cosmoM_unfreezeGC(state);
// caller function wasn't expecting this many return values, cap it // caller function wasn't expecting this many return values, cap it
if (nres > nresults) if (nres > nresults)
nres = nresults; nres = nresults;
// remember where the return values are // remember where the return values are
StkPtr results = cosmoV_getTop(state, nres-1); StkPtr results = cosmoV_getTop(state, nres - 1);
state->top = savedBase + offset; // set stack state->top = savedBase + offset; // set stack
@ -226,7 +249,8 @@ static bool callCFunction(CState *state, CosmoCFunction cfunc, int args, int nre
return false; return false;
// push the return value back onto the stack // push the return value back onto the stack
memmove(state->top, results, sizeof(CValue) * nres); // copies the return values to the top of the stack memmove(state->top, results,
sizeof(CValue) * nres); // copies the return values to the top of the stack
state->top += nres; // and make sure to move state->top to match state->top += nres; // and make sure to move state->top to match
// now, if the caller function expected more return values, push nils onto the stack // now, if the caller function expected more return values, push nils onto the stack
@ -237,19 +261,22 @@ static bool callCFunction(CState *state, CosmoCFunction cfunc, int args, int nre
} }
/* /*
calls a raw closure object with # args on the stack, nresults are pushed onto the stack upon return. calls a raw closure object with # args on the stack, nresults are pushed onto the stack upon
return.
returns: returns:
false: state paniced, error is at state->error false: state paniced, error is at state->error
true: stack->top is moved to base + offset + nresults, with nresults pushed onto the stack from base + offset true: stack->top is moved to base + offset + nresults, with nresults pushed onto the stack
from base + offset
*/ */
static bool rawCall(CState *state, CObjClosure *closure, int args, int nresults, int offset) { static bool rawCall(CState *state, CObjClosure *closure, int args, int nresults, int offset)
{
CObjFunction *func = closure->function; CObjFunction *func = closure->function;
// if the function is variadic and theres more args than parameters, push the args into a table // if the function is variadic and theres more args than parameters, push the args into a table
if (func->variadic && args >= func->args) { if (func->variadic && args >= func->args) {
int extraArgs = args - func->args; int extraArgs = args - func->args;
StkPtr variStart = cosmoV_getTop(state, extraArgs-1); StkPtr variStart = cosmoV_getTop(state, extraArgs - 1);
// push key & value pairs // push key & value pairs
for (int i = 0; i < extraArgs; i++) { for (int i = 0; i < extraArgs; i++) {
@ -263,7 +290,9 @@ static bool rawCall(CState *state, CObjClosure *closure, int args, int nresults,
pushCallFrame(state, closure, func->args + 1); pushCallFrame(state, closure, func->args + 1);
} else if (args != func->args) { // mismatched args } else if (args != func->args) { // mismatched args
cosmoV_error(state, "Expected %d arguments for %s, got %d!", closure->function->args, closure->function->name == NULL ? UNNAMEDCHUNK : closure->function->name->str, args); cosmoV_error(state, "Expected %d arguments for %s, got %d!", closure->function->args,
closure->function->name == NULL ? UNNAMEDCHUNK : closure->function->name->str,
args);
return false; return false;
} else { } else {
// load function into callframe // load function into callframe
@ -277,7 +306,7 @@ static bool rawCall(CState *state, CObjClosure *closure, int args, int nresults,
nres = nresults; nres = nresults;
// remember where the return values are // remember where the return values are
StkPtr results = cosmoV_getTop(state, nres-1); StkPtr results = cosmoV_getTop(state, nres - 1);
// pop the callframe and return results :) // pop the callframe and return results :)
popCallFrame(state, offset); popCallFrame(state, offset);
@ -298,7 +327,8 @@ static bool rawCall(CState *state, CObjClosure *closure, int args, int nresults,
return true; return true;
} }
bool callCValue(CState *state, CValue func, int args, int nresults, int offset) { bool callCValue(CState *state, CValue func, int args, int nresults, int offset)
{
#ifdef VM_DEBUG #ifdef VM_DEBUG
printf("\n"); printf("\n");
printIndent(state->frameCount - 1); printIndent(state->frameCount - 1);
@ -317,21 +347,21 @@ bool callCValue(CState *state, CValue func, int args, int nresults, int offset)
case COBJ_CFUNCTION: case COBJ_CFUNCTION:
return callCFunction(state, cosmoV_readCFunction(func), args, nresults, offset); return callCFunction(state, cosmoV_readCFunction(func), args, nresults, offset);
case COBJ_METHOD: { case COBJ_METHOD: {
CObjMethod *method = (CObjMethod*)cosmoV_readRef(func); CObjMethod *method = (CObjMethod *)cosmoV_readRef(func);
return invokeMethod(state, method->obj, method->func, args, nresults, offset + 1); return invokeMethod(state, method->obj, method->func, args, nresults, offset + 1);
} }
case COBJ_OBJECT: { // object is being instantiated, making another object case COBJ_OBJECT: { // object is being instantiated, making another object
CObjObject *protoObj = (CObjObject*)cosmoV_readRef(func); CObjObject *protoObj = (CObjObject *)cosmoV_readRef(func);
CValue ret; CValue ret;
cosmoV_pushRef(state, (CObj*)protoObj); // push proto to stack for GC to find cosmoV_pushRef(state, (CObj *)protoObj); // push proto to stack for GC to find
CObjObject *newObj = cosmoO_newObject(state); CObjObject *newObj = cosmoO_newObject(state);
newObj->_obj.proto = protoObj; newObj->_obj.proto = protoObj;
cosmoV_pop(state); // pop proto cosmoV_pop(state); // pop proto
// check if they defined an initializer (we accept 0 return values) // check if they defined an initializer (we accept 0 return values)
if (cosmoO_getIString(state, protoObj, ISTRING_INIT, &ret)) { if (cosmoO_getIString(state, protoObj, ISTRING_INIT, &ret)) {
if (!invokeMethod(state, (CObj*)newObj, ret, args, 0, offset + 1)) if (!invokeMethod(state, (CObj *)newObj, ret, args, 0, offset + 1))
return false; return false;
} else { } else {
// no default initializer // no default initializer
@ -340,10 +370,11 @@ bool callCValue(CState *state, CValue func, int args, int nresults, int offset)
} }
if (nresults > 0) { if (nresults > 0) {
cosmoV_pushRef(state, (CObj*)newObj); cosmoV_pushRef(state, (CObj *)newObj);
// push the nils to fill up the expected return values // push the nils to fill up the expected return values
for (int i = 0; i < nresults - 1; i++) { // -1 since the we already pushed the important value for (int i = 0; i < nresults - 1;
i++) { // -1 since the we already pushed the important value
cosmoV_pushValue(state, cosmoV_newNil()); cosmoV_pushValue(state, cosmoV_newNil());
} }
} }
@ -357,16 +388,19 @@ bool callCValue(CState *state, CValue func, int args, int nresults, int offset)
return true; return true;
} }
bool invokeMethod(CState* state, CObj *obj, CValue func, int args, int nresults, int offset) { bool invokeMethod(CState *state, CObj *obj, CValue func, int args, int nresults, int offset)
{
// first, set the first argument to the object // first, set the first argument to the object
StkPtr temp = cosmoV_getTop(state, args); StkPtr temp = cosmoV_getTop(state, args);
*temp = cosmoV_newRef(obj); *temp = cosmoV_newRef(obj);
return callCValue(state, func, args+1, nresults, offset); return callCValue(state, func, args + 1, nresults, offset);
} }
// wraps cosmoV_call in a protected state, CObjError will be pushed onto the stack if function call failed, else return values are passed // wraps cosmoV_call in a protected state, CObjError will be pushed onto the stack if function call
COSMOVMRESULT cosmoV_pcall(CState *state, int args, int nresults) { // failed, else return values are passed
COSMOVMRESULT cosmoV_pcall(CState *state, int args, int nresults)
{
StkPtr base = cosmoV_getTop(state, args); StkPtr base = cosmoV_getTop(state, args);
if (!callCValue(state, *base, args, nresults, 0)) { if (!callCValue(state, *base, args, nresults, 0)) {
@ -374,10 +408,10 @@ COSMOVMRESULT cosmoV_pcall(CState *state, int args, int nresults) {
state->panic = false; state->panic = false;
if (nresults > 0) { if (nresults > 0) {
cosmoV_pushRef(state, (CObj*)state->error); cosmoV_pushRef(state, (CObj *)state->error);
// push other expected results onto the stack // push other expected results onto the stack
for (int i = 0; i < nresults-1; i++) for (int i = 0; i < nresults - 1; i++)
cosmoV_pushValue(state, cosmoV_newNil()); cosmoV_pushValue(state, cosmoV_newNil());
} }
@ -388,30 +422,34 @@ COSMOVMRESULT cosmoV_pcall(CState *state, int args, int nresults) {
} }
/* /*
calls a callable object at stack->top - args - 1, passing the # of args to the callable, and ensuring nresults are returned calls a callable object at stack->top - args - 1, passing the # of args to the callable, and
ensuring nresults are returned
returns: returns:
COSMOVM_OK: callable object exited normally COSMOVM_OK: callable object exited normally
COSMOVM_RUNTIME_ERR: an error occurred, grab the error from state->error COSMOVM_RUNTIME_ERR: an error occurred, grab the error from state->error
*/ */
COSMOVMRESULT cosmoV_call(CState *state, int args, int nresults) { COSMOVMRESULT cosmoV_call(CState *state, int args, int nresults)
{
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
return callCValue(state, *val, args, nresults, 0) ? COSMOVM_OK : COSMOVM_RUNTIME_ERR; return callCValue(state, *val, args, nresults, 0) ? COSMOVM_OK : COSMOVM_RUNTIME_ERR;
} }
static inline bool isFalsey(StkPtr val) { static inline bool isFalsey(StkPtr val)
{
return IS_NIL(*val) || (IS_BOOLEAN(*val) && !cosmoV_readBoolean(*val)); return IS_NIL(*val) || (IS_BOOLEAN(*val) && !cosmoV_readBoolean(*val));
} }
COSMO_API CObjObject* cosmoV_makeObject(CState *state, int pairs) { COSMO_API CObjObject *cosmoV_makeObject(CState *state, int pairs)
{
StkPtr key, val; StkPtr key, val;
CObjObject *newObj = cosmoO_newObject(state); CObjObject *newObj = cosmoO_newObject(state);
cosmoV_pushRef(state, (CObj*)newObj); // so our GC doesn't free our new object cosmoV_pushRef(state, (CObj *)newObj); // so our GC doesn't free our new object
for (int i = 0; i < pairs; i++) { for (int i = 0; i < pairs; i++) {
val = cosmoV_getTop(state, (i*2) + 1); val = cosmoV_getTop(state, (i * 2) + 1);
key = cosmoV_getTop(state, (i*2) + 2); 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);
@ -420,11 +458,12 @@ COSMO_API CObjObject* cosmoV_makeObject(CState *state, int pairs) {
// once done, pop everything off the stack + push new object // once done, pop everything off the stack + push new object
cosmoV_setTop(state, (pairs * 2) + 1); // + 1 for our object cosmoV_setTop(state, (pairs * 2) + 1); // + 1 for our object
cosmoV_pushRef(state, (CObj*)newObj); cosmoV_pushRef(state, (CObj *)newObj);
return newObj; return newObj;
} }
COSMO_API bool cosmoV_registerProtoObject(CState *state, CObjType objType, CObjObject *obj) { COSMO_API bool cosmoV_registerProtoObject(CState *state, CObjType objType, CObjObject *obj)
{
bool replaced = state->protoObjects[objType] != NULL; bool replaced = state->protoObjects[objType] != NULL;
state->protoObjects[objType] = obj; state->protoObjects[objType] = obj;
@ -441,14 +480,15 @@ COSMO_API bool cosmoV_registerProtoObject(CState *state, CObjType objType, CObjO
return replaced; return replaced;
} }
COSMO_API void cosmoV_makeTable(CState *state, int pairs) { COSMO_API void cosmoV_makeTable(CState *state, int pairs)
{
StkPtr key, val; StkPtr key, val;
CObjTable *newObj = cosmoO_newTable(state); CObjTable *newObj = cosmoO_newTable(state);
cosmoV_pushRef(state, (CObj*)newObj); // so our GC doesn't free our new table cosmoV_pushRef(state, (CObj *)newObj); // so our GC doesn't free our new table
for (int i = 0; i < pairs; i++) { for (int i = 0; i < pairs; i++) {
val = cosmoV_getTop(state, (i*2) + 1); val = cosmoV_getTop(state, (i * 2) + 1);
key = cosmoV_getTop(state, (i*2) + 2); 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);
@ -457,22 +497,24 @@ COSMO_API void cosmoV_makeTable(CState *state, int pairs) {
// once done, pop everything off the stack + push new table // once done, pop everything off the stack + push new table
cosmoV_setTop(state, (pairs * 2) + 1); // + 1 for our table cosmoV_setTop(state, (pairs * 2) + 1); // + 1 for our table
cosmoV_pushRef(state, (CObj*)newObj); cosmoV_pushRef(state, (CObj *)newObj);
} }
bool cosmoV_rawget(CState *state, CObj *_obj, CValue key, CValue *val) { bool cosmoV_rawget(CState *state, CObj *_obj, CValue key, CValue *val)
{
CObjObject *object = cosmoO_grabProto(_obj); CObjObject *object = cosmoO_grabProto(_obj);
// no proto to get from // no proto to get from
if (object == NULL) { if (object == NULL) {
CObjString *field = cosmoV_toString(state, key); CObjString *field = cosmoV_toString(state, key);
cosmoV_error(state, "No proto defined! Couldn't get field '%s' from type %s", field->str, cosmoO_typeStr(_obj)); cosmoV_error(state, "No proto defined! Couldn't get field '%s' from type %s", field->str,
cosmoO_typeStr(_obj));
*val = cosmoV_newNil(); *val = cosmoV_newNil();
return false; return false;
} }
// push the object onto the stack so the GC can find it // push the object onto the stack so the GC can find it
cosmoV_pushRef(state, (CObj*)object); cosmoV_pushRef(state, (CObj *)object);
if (cosmoO_getRawObject(state, object, key, val, _obj)) { if (cosmoO_getRawObject(state, object, key, val, _obj)) {
// *val now equals the response, pop the object // *val now equals the response, pop the object
cosmoV_pop(state); cosmoV_pop(state);
@ -483,13 +525,15 @@ bool cosmoV_rawget(CState *state, CObj *_obj, CValue key, CValue *val) {
return false; return false;
} }
bool cosmoV_rawset(CState *state, CObj *_obj, CValue key, CValue val) { bool cosmoV_rawset(CState *state, CObj *_obj, CValue key, CValue val)
{
CObjObject *object = cosmoO_grabProto(_obj); CObjObject *object = cosmoO_grabProto(_obj);
// no proto to set to // no proto to set to
if (object == NULL) { if (object == NULL) {
CObjString *field = cosmoV_toString(state, key); CObjString *field = cosmoV_toString(state, key);
cosmoV_error(state, "No proto defined! Couldn't set field '%s' to type %s", field->str, cosmoO_typeStr(_obj)); cosmoV_error(state, "No proto defined! Couldn't set field '%s' to type %s", field->str,
cosmoO_typeStr(_obj));
return false; return false;
} }
@ -497,7 +541,8 @@ bool cosmoV_rawset(CState *state, CObj *_obj, CValue key, CValue val) {
return true; return true;
} }
COSMO_API bool cosmoV_get(CState *state) { COSMO_API bool cosmoV_get(CState *state)
{
CValue val; CValue val;
StkPtr obj = cosmoV_getTop(state, 1); // object was pushed first StkPtr obj = cosmoV_getTop(state, 1); // object was pushed first
StkPtr key = cosmoV_getTop(state, 0); // then the key StkPtr key = cosmoV_getTop(state, 0); // then the key
@ -517,7 +562,8 @@ COSMO_API bool cosmoV_get(CState *state) {
} }
// yes, this would technically make it possible to set fields of types other than <string>. go crazy // yes, this would technically make it possible to set fields of types other than <string>. go crazy
COSMO_API bool cosmoV_set(CState *state) { COSMO_API bool cosmoV_set(CState *state)
{
StkPtr obj = cosmoV_getTop(state, 2); // object was pushed first StkPtr obj = cosmoV_getTop(state, 2); // object was pushed first
StkPtr key = cosmoV_getTop(state, 1); // then the key StkPtr key = cosmoV_getTop(state, 1); // then the key
StkPtr val = cosmoV_getTop(state, 0); // and finally the value StkPtr val = cosmoV_getTop(state, 0); // and finally the value
@ -535,14 +581,15 @@ COSMO_API bool cosmoV_set(CState *state) {
return true; return true;
} }
COSMO_API bool cosmoV_getMethod(CState *state, CObj *obj, CValue key, CValue *val) { COSMO_API bool cosmoV_getMethod(CState *state, CObj *obj, CValue key, CValue *val)
{
if (!cosmoV_rawget(state, obj, key, val)) if (!cosmoV_rawget(state, obj, key, val))
return false; return false;
// if the result is callable, wrap it in an method // if the result is callable, wrap it in an method
if (IS_CALLABLE(*val)) { if (IS_CALLABLE(*val)) {
// push object to stack so the GC can find it // push object to stack so the GC can find it
cosmoV_pushRef(state, (CObj*)obj); cosmoV_pushRef(state, (CObj *)obj);
CObjMethod *method = cosmoO_newMethod(state, *val, obj); CObjMethod *method = cosmoO_newMethod(state, *val, obj);
cosmoV_pop(state); // pop the object cosmoV_pop(state); // pop the object
*val = cosmoV_newRef(method); *val = cosmoV_newRef(method);
@ -551,7 +598,8 @@ COSMO_API bool cosmoV_getMethod(CState *state, CObj *obj, CValue key, CValue *va
return true; return true;
} }
int _tbl__next(CState *state, int nargs, CValue *args) { int _tbl__next(CState *state, int nargs, CValue *args)
{
if (nargs != 1) { if (nargs != 1) {
cosmoV_error(state, "Expected 1 parameter, %d received!", nargs); cosmoV_error(state, "Expected 1 parameter, %d received!", nargs);
return 0; return 0;
@ -569,10 +617,11 @@ int _tbl__next(CState *state, int nargs, CValue *args) {
cosmoO_getIString(state, obj, ISTRING_RESERVED, &val); cosmoO_getIString(state, obj, ISTRING_RESERVED, &val);
if (!IS_TABLE(val)) { if (!IS_TABLE(val)) {
return 0; // someone set the __reserved member to something else. this will exit the iterator loop return 0; // someone set the __reserved member to something else. this will exit the
// iterator loop
} }
CObjTable *table = (CObjTable*)cosmoV_readRef(val); CObjTable *table = (CObjTable *)cosmoV_readRef(val);
// while the entry is invalid, go to the next entry // while the entry is invalid, go to the next entry
int cap = table->tbl.capacityMask + 1; int cap = table->tbl.capacityMask + 1;
@ -582,7 +631,8 @@ int _tbl__next(CState *state, int nargs, CValue *args) {
} while (IS_NIL(entry->key) && index < cap); } while (IS_NIL(entry->key) && index < cap);
cosmoO_setUserI(obj, index); // update the userdata cosmoO_setUserI(obj, index); // update the userdata
if (index < cap && !IS_NIL(entry->key)) { // if the entry is valid, return it's key and value pair if (index < cap &&
!IS_NIL(entry->key)) { // if the entry is valid, return it's key and value pair
cosmoV_pushValue(state, entry->key); cosmoV_pushValue(state, entry->key);
cosmoV_pushValue(state, entry->val); cosmoV_pushValue(state, entry->val);
return 2; // we pushed 2 values onto the stack for the return values return 2; // we pushed 2 values onto the stack for the return values
@ -598,22 +648,25 @@ int _tbl__next(CState *state, int nargs, CValue *args) {
cosmoV_setTop(state, 2); /* pop the 2 values */ \ cosmoV_setTop(state, 2); /* pop the 2 values */ \
cosmoV_pushValue(state, typeConst(cosmoV_readNumber(*valA) op cosmoV_readNumber(*valB))); \ cosmoV_pushValue(state, typeConst(cosmoV_readNumber(*valA) op cosmoV_readNumber(*valB))); \
} else { \ } else { \
cosmoV_error(state, "Expected numbers, got %s and %s!", cosmoV_typeStr(*valA), cosmoV_typeStr(*valB)); \ cosmoV_error(state, "Expected numbers, got %s and %s!", cosmoV_typeStr(*valA), \
cosmoV_typeStr(*valB)); \
return -1; \ return -1; \
} \ }
// returns -1 if panic // returns -1 if panic
int 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 :)
#define READBYTE() *frame->pc++ #define READBYTE() *frame->pc++
#define READUINT() (frame->pc += 2, *(uint16_t*)(&frame->pc[-2])) #define READUINT() (frame->pc += 2, *(uint16_t *)(&frame->pc[-2]))
while (!state->panic) { while (!state->panic) {
#ifdef VM_DEBUG #ifdef VM_DEBUG
cosmoV_printStack(state); cosmoV_printStack(state);
disasmInstr(&frame->closure->function->chunk, frame->pc - frame->closure->function->chunk.buf, state->frameCount - 1); disasmInstr(&frame->closure->function->chunk,
frame->pc - frame->closure->function->chunk.buf, state->frameCount - 1);
printf("\n"); printf("\n");
#endif #endif
switch (READBYTE()) { switch (READBYTE()) {
@ -700,7 +753,7 @@ int cosmoV_execute(CState *state) {
uint16_t index = READUINT(); uint16_t index = READUINT();
CObjFunction *func = cosmoV_readFunction(constants[index]); CObjFunction *func = cosmoV_readFunction(constants[index]);
CObjClosure *closure = cosmoO_newClosure(state, func); CObjClosure *closure = cosmoO_newClosure(state, func);
cosmoV_pushRef(state, (CObj*)closure); cosmoV_pushRef(state, (CObj *)closure);
for (int i = 0; i < closure->upvalueCount; i++) { for (int i = 0; i < closure->upvalueCount; i++) {
uint8_t encoding = READBYTE(); uint8_t encoding = READBYTE();
@ -730,19 +783,20 @@ int cosmoV_execute(CState *state) {
uint16_t pairs = READUINT(); uint16_t pairs = READUINT();
StkPtr val; StkPtr val;
CObjTable *newObj = cosmoO_newTable(state); CObjTable *newObj = cosmoO_newTable(state);
cosmoV_pushRef(state, (CObj*)newObj); // so our GC doesn't free our new table cosmoV_pushRef(state, (CObj *)newObj); // so our GC doesn't free our new table
for (int i = 0; i < pairs; i++) { for (int i = 0; i < pairs; i++) {
val = cosmoV_getTop(state, i + 1); val = cosmoV_getTop(state, i + 1);
// set key/value pair // set key/value pair
CValue *newVal = cosmoT_insert(state, &newObj->tbl, cosmoV_newNumber(pairs - i - 1)); CValue *newVal =
cosmoT_insert(state, &newObj->tbl, cosmoV_newNumber(pairs - i - 1));
*newVal = *val; *newVal = *val;
} }
// once done, pop everything off the stack + push new table // once done, pop everything off the stack + push new table
cosmoV_setTop(state, pairs + 1); // + 1 for our table cosmoV_setTop(state, pairs + 1); // + 1 for our table
cosmoV_pushRef(state, (CObj*)newObj); cosmoV_pushRef(state, (CObj *)newObj);
continue; continue;
} }
case OP_INDEX: { case OP_INDEX: {
@ -761,14 +815,16 @@ int cosmoV_execute(CState *state) {
if (proto != NULL) { if (proto != NULL) {
// check for __index metamethod // check for __index metamethod
if (!cosmoO_indexObject(state, proto, *key, &val)) // if returns false, cosmoV_error was called if (!cosmoO_indexObject(state, proto, *key,
&val)) // if returns false, cosmoV_error was called
return -1; return -1;
} else if (obj->type == COBJ_TABLE) { } else if (obj->type == COBJ_TABLE) {
CObjTable *tbl = (CObjTable*)obj; CObjTable *tbl = (CObjTable *)obj;
cosmoT_get(state, &tbl->tbl, *key, &val); cosmoT_get(state, &tbl->tbl, *key, &val);
} else { } else {
cosmoV_error(state, "No proto defined! Couldn't __index from type %s", cosmoV_typeStr(*temp)); cosmoV_error(state, "No proto defined! Couldn't __index from type %s",
cosmoV_typeStr(*temp));
return -1; return -1;
} }
@ -791,15 +847,17 @@ int cosmoV_execute(CState *state) {
CObjObject *proto = cosmoO_grabProto(obj); CObjObject *proto = cosmoO_grabProto(obj);
if (proto != NULL) { if (proto != NULL) {
if (!cosmoO_newIndexObject(state, proto, *key, *value)) // if it returns false, cosmoV_error was called if (!cosmoO_newIndexObject(state, proto, *key,
*value)) // if it returns false, cosmoV_error was called
return -1; return -1;
} else if (obj->type == COBJ_TABLE) { } else if (obj->type == COBJ_TABLE) {
CObjTable *tbl = (CObjTable*)obj; CObjTable *tbl = (CObjTable *)obj;
CValue *newVal = cosmoT_insert(state, &tbl->tbl, *key); CValue *newVal = cosmoT_insert(state, &tbl->tbl, *key);
*newVal = *value; // set the index *newVal = *value; // set the index
} else { } else {
cosmoV_error(state, "No proto defined! Couldn't __newindex from type %s", cosmoV_typeStr(*temp)); cosmoV_error(state, "No proto defined! Couldn't __newindex from type %s",
cosmoV_typeStr(*temp));
return -1; return -1;
} }
@ -823,7 +881,8 @@ int cosmoV_execute(CState *state) {
return -1; return -1;
} else { } else {
CObjString *field = cosmoV_toString(state, constants[ident]); CObjString *field = cosmoV_toString(state, constants[ident]);
cosmoV_error(state, "Couldn't set field '%s' on type %s!", field->str, cosmoV_typeStr(*temp)); cosmoV_error(state, "Couldn't set field '%s' on type %s!", field->str,
cosmoV_typeStr(*temp));
return -1; return -1;
} }
@ -842,7 +901,8 @@ int cosmoV_execute(CState *state) {
return -1; return -1;
} else { } else {
CObjString *field = cosmoV_toString(state, constants[ident]); CObjString *field = cosmoV_toString(state, constants[ident]);
cosmoV_error(state, "Couldn't get field '%s' from type %s!", field->str, cosmoV_typeStr(*temp)); cosmoV_error(state, "Couldn't get field '%s' from type %s!", field->str,
cosmoV_typeStr(*temp));
return -1; return -1;
} }
@ -855,13 +915,15 @@ int cosmoV_execute(CState *state) {
StkPtr temp = cosmoV_getTop(state, 0); // that should be the object StkPtr temp = cosmoV_getTop(state, 0); // that should be the object
uint16_t ident = READUINT(); // use for the key uint16_t ident = READUINT(); // use for the key
// this is almost identical to GETOBJECT, however cosmoV_getMethod is used instead of just cosmoV_get // this is almost identical to GETOBJECT, however cosmoV_getMethod is used instead of
// just cosmoV_get
if (IS_REF(*temp)) { if (IS_REF(*temp)) {
if (!cosmoV_getMethod(state, cosmoV_readRef(*temp), constants[ident], &val)) if (!cosmoV_getMethod(state, cosmoV_readRef(*temp), constants[ident], &val))
return -1; return -1;
} else { } else {
CObjString *field = cosmoV_toString(state, constants[ident]); CObjString *field = cosmoV_toString(state, constants[ident]);
cosmoV_error(state, "Couldn't get field '%s' from type %s!", field->str, cosmoV_typeStr(*temp)); cosmoV_error(state, "Couldn't get field '%s' from type %s!", field->str,
cosmoV_typeStr(*temp));
return -1; return -1;
} }
@ -895,7 +957,8 @@ int cosmoV_execute(CState *state) {
StkPtr temp = cosmoV_getTop(state, 0); // should be the object/table StkPtr temp = cosmoV_getTop(state, 0); // should be the object/table
if (!IS_REF(*temp)) { if (!IS_REF(*temp)) {
cosmoV_error(state, "Couldn't iterate over non-iterator type %s!", cosmoV_typeStr(*temp)); cosmoV_error(state, "Couldn't iterate over non-iterator type %s!",
cosmoV_typeStr(*temp));
return -1; return -1;
} }
@ -908,43 +971,49 @@ int cosmoV_execute(CState *state) {
if (cosmoO_getIString(state, proto, ISTRING_ITER, &val)) { if (cosmoO_getIString(state, proto, ISTRING_ITER, &val)) {
cosmoV_pop(state); // pop the object from the stack cosmoV_pop(state); // pop the object from the stack
cosmoV_pushValue(state, val); cosmoV_pushValue(state, val);
cosmoV_pushRef(state, (CObj*)obj); cosmoV_pushRef(state, (CObj *)obj);
if (cosmoV_call(state, 1, 1) != COSMOVM_OK) // we expect 1 return value on the stack, the iterable object if (cosmoV_call(state, 1, 1) !=
COSMOVM_OK) // we expect 1 return value on the stack, the iterable object
return -1; return -1;
StkPtr iObj = cosmoV_getTop(state, 0); StkPtr iObj = cosmoV_getTop(state, 0);
if (!IS_OBJECT(*iObj)) { if (!IS_OBJECT(*iObj)) {
cosmoV_error(state, "Expected iterable object! '__iter' returned %s, expected <object>!", cosmoV_typeStr(*iObj)); cosmoV_error(
state,
"Expected iterable object! '__iter' returned %s, expected <object>!",
cosmoV_typeStr(*iObj));
return -1; return -1;
} }
// get __next method and place it at the top of the stack // get __next method and place it at the top of the stack
cosmoV_getMethod(state, cosmoV_readRef(*iObj), cosmoV_newRef(state->iStrings[ISTRING_NEXT]), iObj); cosmoV_getMethod(state, cosmoV_readRef(*iObj),
cosmoV_newRef(state->iStrings[ISTRING_NEXT]), iObj);
} else { } else {
cosmoV_error(state, "Expected iterable object! '__iter' not defined!"); cosmoV_error(state, "Expected iterable object! '__iter' not defined!");
return -1; return -1;
} }
} else if (obj->type == COBJ_TABLE) { } else if (obj->type == COBJ_TABLE) {
CObjTable *tbl = (CObjTable*)obj; CObjTable *tbl = (CObjTable *)obj;
cosmoV_pushRef(state, (CObj*)state->iStrings[ISTRING_RESERVED]); // key cosmoV_pushRef(state, (CObj *)state->iStrings[ISTRING_RESERVED]); // key
cosmoV_pushRef(state, (CObj*)tbl); // value cosmoV_pushRef(state, (CObj *)tbl); // value
cosmoV_pushString(state, "__next"); // key cosmoV_pushString(state, "__next"); // key
CObjCFunction *tbl_next = cosmoO_newCFunction(state, _tbl__next); CObjCFunction *tbl_next = cosmoO_newCFunction(state, _tbl__next);
cosmoV_pushRef(state, (CObj*)tbl_next); // value cosmoV_pushRef(state, (CObj *)tbl_next); // value
CObjObject *obj = cosmoV_makeObject(state, 2); // pushes the new object to the stack CObjObject *obj = cosmoV_makeObject(state, 2); // pushes the new object to the stack
cosmoO_setUserI(obj, 0); // increment for iterator cosmoO_setUserI(obj, 0); // increment for iterator
// make our CObjMethod for OP_NEXT to call // make our CObjMethod for OP_NEXT to call
CObjMethod *method = cosmoO_newMethod(state, cosmoV_newRef(tbl_next), (CObj*)obj); CObjMethod *method = cosmoO_newMethod(state, cosmoV_newRef(tbl_next), (CObj *)obj);
cosmoV_setTop(state, 2); // pops the object & the tbl cosmoV_setTop(state, 2); // pops the object & the tbl
cosmoV_pushRef(state, (CObj*)method); // pushes the method for OP_NEXT cosmoV_pushRef(state, (CObj *)method); // pushes the method for OP_NEXT
} else { } else {
cosmoV_error(state, "No proto defined! Couldn't get from type %s", cosmoO_typeStr(obj)); cosmoV_error(state, "No proto defined! Couldn't get from type %s",
cosmoO_typeStr(obj));
return -1; return -1;
} }
@ -956,7 +1025,8 @@ int cosmoV_execute(CState *state) {
StkPtr temp = cosmoV_getTop(state, 0); // we don't actually pop this off the stack StkPtr temp = cosmoV_getTop(state, 0); // we don't actually pop this off the stack
if (!IS_METHOD(*temp)) { if (!IS_METHOD(*temp)) {
cosmoV_error(state, "Expected '__next' to be a method, got type %s!", cosmoV_typeStr(*temp)); cosmoV_error(state, "Expected '__next' to be a method, got type %s!",
cosmoV_typeStr(*temp));
return -1; return -1;
} }
@ -964,7 +1034,8 @@ int cosmoV_execute(CState *state) {
if (cosmoV_call(state, 0, nresults) != COSMOVM_OK) if (cosmoV_call(state, 0, nresults) != COSMOVM_OK)
return -1; return -1;
if (IS_NIL(*(cosmoV_getTop(state, 0)))) { // __next returned a nil, which means to exit the loop if (IS_NIL(*(cosmoV_getTop(
state, 0)))) { // __next returned a nil, which means to exit the loop
cosmoV_setTop(state, nresults); // pop the return values cosmoV_setTop(state, nresults); // pop the return values
frame->pc += jump; frame->pc += jump;
} }
@ -991,9 +1062,11 @@ int cosmoV_execute(CState *state) {
StkPtr valB = cosmoV_getTop(state, 0); StkPtr valB = cosmoV_getTop(state, 0);
if (IS_NUMBER(*valA) && IS_NUMBER(*valB)) { if (IS_NUMBER(*valA) && IS_NUMBER(*valB)) {
cosmoV_setTop(state, 2); /* pop the 2 values */ cosmoV_setTop(state, 2); /* pop the 2 values */
cosmoV_pushValue(state, cosmoV_newNumber(fmod(cosmoV_readNumber(*valA), cosmoV_readNumber(*valB)))); cosmoV_pushValue(state, cosmoV_newNumber(fmod(cosmoV_readNumber(*valA),
cosmoV_readNumber(*valB))));
} else { } else {
cosmoV_error(state, "Expected numbers, got %s and %s!", cosmoV_typeStr(*valA), cosmoV_typeStr(*valB)); cosmoV_error(state, "Expected numbers, got %s and %s!", cosmoV_typeStr(*valA),
cosmoV_typeStr(*valB));
return -1; return -1;
} }
continue; continue;
@ -1003,9 +1076,11 @@ int cosmoV_execute(CState *state) {
StkPtr valB = cosmoV_getTop(state, 0); StkPtr valB = cosmoV_getTop(state, 0);
if (IS_NUMBER(*valA) && IS_NUMBER(*valB)) { if (IS_NUMBER(*valA) && IS_NUMBER(*valB)) {
cosmoV_setTop(state, 2); /* pop the 2 values */ cosmoV_setTop(state, 2); /* pop the 2 values */
cosmoV_pushValue(state, cosmoV_newNumber(pow(cosmoV_readNumber(*valA), cosmoV_readNumber(*valB)))); cosmoV_pushValue(state, cosmoV_newNumber(pow(cosmoV_readNumber(*valA),
cosmoV_readNumber(*valB))));
} else { } else {
cosmoV_error(state, "Expected numbers, got %s and %s!", cosmoV_typeStr(*valA), cosmoV_typeStr(*valB)); cosmoV_error(state, "Expected numbers, got %s and %s!", cosmoV_typeStr(*valA),
cosmoV_typeStr(*valB));
return -1; return -1;
} }
continue; continue;
@ -1119,12 +1194,13 @@ int cosmoV_execute(CState *state) {
cosmoV_pushValue(state, val); // pushes old value onto the stack :) cosmoV_pushValue(state, val); // pushes old value onto the stack :)
// call __newindex // call __newindex
if (!cosmoO_newIndexObject(state, proto, *key, cosmoV_newNumber(cosmoV_readNumber(val) + inc))) if (!cosmoO_newIndexObject(state, proto, *key,
cosmoV_newNumber(cosmoV_readNumber(val) + inc)))
return -1; return -1;
} else } else
return -1; // cosmoO_indexObject failed and threw an error return -1; // cosmoO_indexObject failed and threw an error
} else if (obj->type == COBJ_TABLE) { } else if (obj->type == COBJ_TABLE) {
CObjTable *tbl = (CObjTable*)obj; CObjTable *tbl = (CObjTable *)obj;
CValue *val = cosmoT_insert(state, &tbl->tbl, *key); CValue *val = cosmoT_insert(state, &tbl->tbl, *key);
if (!IS_NUMBER(*val)) { if (!IS_NUMBER(*val)) {
@ -1137,7 +1213,8 @@ int cosmoV_execute(CState *state) {
cosmoV_pushValue(state, *val); // pushes old value onto the stack :) cosmoV_pushValue(state, *val); // pushes old value onto the stack :)
*val = cosmoV_newNumber(cosmoV_readNumber(*val) + inc); // sets table index *val = cosmoV_newNumber(cosmoV_readNumber(*val) + inc); // sets table index
} else { } else {
cosmoV_error(state, "No proto defined! Couldn't __index from type %s", cosmoV_typeStr(*temp)); cosmoV_error(state, "No proto defined! Couldn't __index from type %s",
cosmoV_typeStr(*temp));
return -1; return -1;
} }
@ -1163,7 +1240,8 @@ int cosmoV_execute(CState *state) {
// check that it's a number value // check that it's a number value
if (IS_NUMBER(val)) { if (IS_NUMBER(val)) {
cosmoV_pushValue(state, val); // pushes old value onto the stack :) cosmoV_pushValue(state, val); // pushes old value onto the stack :)
if (!cosmoV_rawset(state, obj, ident, cosmoV_newNumber(cosmoV_readNumber(val) + inc))) if (!cosmoV_rawset(state, obj, ident,
cosmoV_newNumber(cosmoV_readNumber(val) + inc)))
return -1; return -1;
} else { } else {
cosmoV_error(state, "Expected number, got %s!", cosmoV_typeStr(val)); cosmoV_error(state, "Expected number, got %s!", cosmoV_typeStr(val));
@ -1201,9 +1279,15 @@ int cosmoV_execute(CState *state) {
NUMBEROP(cosmoV_newBoolean, <=) NUMBEROP(cosmoV_newBoolean, <=)
continue; continue;
} }
case OP_TRUE: cosmoV_pushBoolean(state, true); continue; case OP_TRUE:
case OP_FALSE: cosmoV_pushBoolean(state, false); continue; cosmoV_pushBoolean(state, true);
case OP_NIL: cosmoV_pushValue(state, cosmoV_newNil()); continue; continue;
case OP_FALSE:
cosmoV_pushBoolean(state, false);
continue;
case OP_NIL:
cosmoV_pushValue(state, cosmoV_newNil());
continue;
case OP_RETURN: { case OP_RETURN: {
uint8_t res = READBYTE(); uint8_t res = READBYTE();
return res; return res;

View File

@ -1,14 +1,15 @@
#ifndef COSMOVM_H #ifndef COSMOVM_H
#define COSMOVM_H #define COSMOVM_H
#include <string.h>
#include "cosmo.h" #include "cosmo.h"
#include "cstate.h" #include "cstate.h"
//#define VM_DEBUG #include <string.h>
typedef enum { // #define VM_DEBUG
typedef enum
{
COSMOVM_OK, COSMOVM_OK,
COSMOVM_RUNTIME_ERR, COSMOVM_RUNTIME_ERR,
COSMOVM_BUILDTIME_ERR COSMOVM_BUILDTIME_ERR
@ -19,25 +20,26 @@ COSMO_API COSMOVMRESULT cosmoV_call(CState *state, int args, int nresults);
COSMO_API COSMOVMRESULT cosmoV_pcall(CState *state, int args, int nresults); COSMO_API COSMOVMRESULT cosmoV_pcall(CState *state, int args, int nresults);
// pushes new object onto the stack & returns a pointer to the new object // pushes new object onto the stack & returns a pointer to the new object
COSMO_API CObjObject* cosmoV_makeObject(CState *state, int pairs); COSMO_API CObjObject *cosmoV_makeObject(CState *state, int pairs);
COSMO_API void cosmoV_makeTable(CState *state, int pairs); COSMO_API void cosmoV_makeTable(CState *state, int pairs);
COSMO_API void cosmoV_concat(CState *state, int vals); COSMO_API void cosmoV_concat(CState *state, int vals);
COSMO_API void cosmoV_pushFString(CState *state, const char *format, ...); COSMO_API void cosmoV_pushFString(CState *state, const char *format, ...);
COSMO_API void cosmoV_printError(CState *state, CObjError *err); COSMO_API void cosmoV_printError(CState *state, CObjError *err);
COSMO_API CObjError* cosmoV_throw(CState *state); COSMO_API CObjError *cosmoV_throw(CState *state);
COSMO_API void cosmoV_error(CState *state, const char *format, ...); COSMO_API void cosmoV_error(CState *state, const char *format, ...);
COSMO_API void cosmo_insert(CState *state, int indx, CValue val); COSMO_API void cosmo_insert(CState *state, int indx, CValue val);
/* /*
Sets the default proto objects for the passed objType. Also walks through the object heap and updates protos Sets the default proto objects for the passed objType. Also walks through the object heap and
for the passed objType if that CObj* has no proto. updates protos for the passed objType if that CObj* has no proto.
returns true if replacing a previously registered proto object for this type returns true if replacing a previously registered proto object for this type
*/ */
COSMO_API bool cosmoV_registerProtoObject(CState *state, CObjType objType, CObjObject *obj); COSMO_API bool cosmoV_registerProtoObject(CState *state, CObjType objType, CObjObject *obj);
/* /*
compiles string into a <closure>, if successful, <closure> will be pushed onto the stack otherwise the <error> will be pushed. compiles string into a <closure>, if successful, <closure> will be pushed onto the stack
otherwise the <error> will be pushed.
returns: returns:
false : <error> is at the top of the stack false : <error> is at the top of the stack
@ -48,28 +50,32 @@ COSMO_API bool cosmoV_compileString(CState *state, const char *src, const char *
/* /*
expects object to be pushed, then the key. expects object to be pushed, then the key.
returns false if an error was thrown, returns true if the value was pushed onto the stack and the object and key were popped returns false if an error was thrown, returns true if the value was pushed onto the stack and
the object and key were popped
*/ */
COSMO_API bool cosmoV_get(CState *state); COSMO_API bool cosmoV_get(CState *state);
/* /*
expects object to be pushed, then the key, and finally the new value. expects object to be pushed, then the key, and finally the new value.
returns false if an error was thrown, returns true if the value was set and the object key, and value were popped returns false if an error was thrown, returns true if the value was set and the object key, and
value were popped
*/ */
COSMO_API bool cosmoV_set(CState *state); COSMO_API bool cosmoV_set(CState *state);
// wraps the closure into a CObjMethod, so the function is called as an invoked method // wraps the closure into a CObjMethod, so the function is called as an invoked method
COSMO_API bool cosmoV_getMethod(CState *state, CObj *obj, CValue key, CValue *val); COSMO_API bool cosmoV_getMethod(CState *state, CObj *obj, CValue key, CValue *val);
// clears the stack, callstack and restores the state into a usable state after a calloverflow or another hard to recover error // clears the stack, callstack and restores the state into a usable state after a calloverflow or
// (keeps the global table intact) // another hard to recover error (keeps the global table intact)
COSMO_API bool cosmoV_restore(CState *state); COSMO_API bool cosmoV_restore(CState *state);
// nice to have wrappers // nice to have wrappers
// pushes a raw CValue to the stack, might throw an error if the stack is overflowed (with the SAFE_STACK macro on) // pushes a raw CValue to the stack, might throw an error if the stack is overflowed (with the
static inline void cosmoV_pushValue(CState *state, CValue val) { // SAFE_STACK macro on)
static inline void cosmoV_pushValue(CState *state, CValue val)
{
#ifdef SAFE_STACK #ifdef SAFE_STACK
ptrdiff_t stackSize = state->top - state->stack; ptrdiff_t stackSize = state->top - state->stack;
@ -91,51 +97,61 @@ static inline void cosmoV_pushValue(CState *state, CValue val) {
} }
// sets stack->top to stack->top - indx // sets stack->top to stack->top - indx
static inline StkPtr cosmoV_setTop(CState *state, int indx) { static inline StkPtr cosmoV_setTop(CState *state, int indx)
{
state->top -= indx; state->top -= indx;
return state->top; return state->top;
} }
// returns stack->top - indx - 1 // returns stack->top - indx - 1
static inline StkPtr cosmoV_getTop(CState *state, int indx) { static inline StkPtr cosmoV_getTop(CState *state, int indx)
{
return &state->top[-(indx + 1)]; return &state->top[-(indx + 1)];
} }
// pops 1 value off the stack, returns the popped value // pops 1 value off the stack, returns the popped value
static inline StkPtr cosmoV_pop(CState *state) { static inline StkPtr cosmoV_pop(CState *state)
{
return cosmoV_setTop(state, 1); return cosmoV_setTop(state, 1);
} }
// pushes a cosmo_Number to the stack // pushes a cosmo_Number to the stack
static inline void cosmoV_pushNumber(CState *state, cosmo_Number num) { static inline void cosmoV_pushNumber(CState *state, cosmo_Number num)
{
cosmoV_pushValue(state, cosmoV_newNumber(num)); cosmoV_pushValue(state, cosmoV_newNumber(num));
} }
// pushes a boolean to the stack // pushes a boolean to the stack
static inline void cosmoV_pushBoolean(CState *state, bool b) { static inline void cosmoV_pushBoolean(CState *state, bool b)
{
cosmoV_pushValue(state, cosmoV_newBoolean(b)); cosmoV_pushValue(state, cosmoV_newBoolean(b));
} }
static inline void cosmoV_pushRef(CState *state, CObj *obj) { static inline void cosmoV_pushRef(CState *state, CObj *obj)
{
cosmoV_pushValue(state, cosmoV_newRef(obj)); cosmoV_pushValue(state, cosmoV_newRef(obj));
} }
// pushes a C Function to the stack // pushes a C Function to the stack
static inline void cosmoV_pushCFunction(CState *state, CosmoCFunction func) { static inline void cosmoV_pushCFunction(CState *state, CosmoCFunction func)
cosmoV_pushRef(state, (CObj*)cosmoO_newCFunction(state, func)); {
cosmoV_pushRef(state, (CObj *)cosmoO_newCFunction(state, func));
} }
// len is the length of the string without the NULL terminator // len is the length of the string without the NULL terminator
static inline void cosmoV_pushLString(CState *state, const char *str, size_t len) { static inline void cosmoV_pushLString(CState *state, const char *str, size_t len)
cosmoV_pushRef(state, (CObj*)cosmoO_copyString(state, str, len)); {
cosmoV_pushRef(state, (CObj *)cosmoO_copyString(state, str, len));
} }
// accepts a null terminated string and copys the buffer to the VM heap // accepts a null terminated string and copys the buffer to the VM heap
static inline void cosmoV_pushString(CState *state, const char *str) { static inline void cosmoV_pushString(CState *state, const char *str)
{
cosmoV_pushLString(state, str, strlen(str)); cosmoV_pushLString(state, str, strlen(str));
} }
static inline void cosmoV_pushNil(CState *state) { static inline void cosmoV_pushNil(CState *state)
{
cosmoV_pushValue(state, cosmoV_newNil()); cosmoV_pushValue(state, cosmoV_newNil());
} }