added clang-format

This commit is contained in:
CPunch 2023-02-09 12:32:48 -06:00 committed by cpunch
parent 88284a0b6e
commit 6056f8eb5b
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
// stressing the GC
for (var i = 0; ; i++) do
for (var i = 0; i < 100000; i++) do
var x = Test("Hello world " .. i)
x:print()
end

View File

@ -1,15 +1,18 @@
#include "cbaselib.h"
#include "cvm.h"
#include "cvalue.h"
#include "cobj.h"
#include "cmem.h"
#include "cobj.h"
#include "cvalue.h"
#include "cvm.h"
#include <math.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++) {
if (IS_REF(args[i])) { // if its a CObj*, generate the CObjString
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
}
int cosmoB_assert(CState *state, int nargs, CValue *args) {
int cosmoB_assert(CState *state, int nargs, CValue *args)
{
if (nargs < 1 || nargs > 2) {
cosmoV_error(state, "assert() expected 1 or 2 arguments, got %d!", nargs);
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 (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 {
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;
}
int cosmoB_type(CState *state, int nargs, CValue *args) {
int cosmoB_type(CState *state, int nargs, CValue *args)
{
if (nargs != 1) {
cosmoV_error(state, "type() expected 1 argument, got %d!", nargs);
return 0;
@ -55,7 +61,8 @@ int cosmoB_type(CState *state, int nargs, CValue *args) {
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) {
cosmoV_error(state, "pcall() expected at least 1 argument!");
return 0;
@ -65,7 +72,7 @@ int cosmoB_pcall(CState *state, int nargs, CValue *args) {
cosmoM_unfreezeGC(state);
// 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
cosmo_insert(state, 0, cosmoV_newBoolean(res == COSMOVM_OK));
@ -75,7 +82,8 @@ int cosmoB_pcall(CState *state, int nargs, CValue *args) {
return 2;
}
int cosmoB_tonumber(CState *state, int nargs, CValue *args) {
int cosmoB_tonumber(CState *state, int nargs, CValue *args)
{
if (nargs != 1) {
cosmoV_error(state, "tonumber() expected 1 argument, got %d!", nargs);
return 0;
@ -85,17 +93,19 @@ int cosmoB_tonumber(CState *state, int nargs, CValue *args) {
return 1;
}
int cosmoB_tostring(CState *state, int nargs, CValue *args) {
int cosmoB_tostring(CState *state, int nargs, CValue *args)
{
if (nargs != 1) {
cosmoV_error(state, "tostring() expected 1 argument, got %d!", nargs);
return 0;
}
cosmoV_pushRef(state, (CObj*)cosmoV_toString(state, args[0]));
cosmoV_pushRef(state, (CObj *)cosmoV_toString(state, args[0]));
return 1;
}
int cosmoB_loadstring(CState *state, int nargs, CValue *args) {
int cosmoB_loadstring(CState *state, int nargs, CValue *args)
{
if (nargs != 1) {
cosmoV_error(state, "loadstring() expected 1 argument, got %d!", nargs);
return 0;
@ -113,7 +123,8 @@ int cosmoB_loadstring(CState *state, int nargs, CValue *args) {
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) {
cosmoV_error(state, "error() expected 1 argument, got %d!", nargs);
return 0;
@ -129,31 +140,16 @@ int cosmoB_error(CState *state, int nargs, CValue *args) {
return 0;
}
void cosmoB_loadLibrary(CState *state) {
const char *identifiers[] = {
"print",
"assert",
"type",
"pcall",
"tonumber",
"tostring",
"loadstring",
"error"
};
void cosmoB_loadLibrary(CState *state)
{
const char *identifiers[] = {"print", "assert", "type", "pcall",
"tonumber", "tostring", "loadstring", "error"};
CosmoCFunction baseLib[] = {
cosmoB_print,
cosmoB_assert,
cosmoB_type,
cosmoB_pcall,
cosmoB_tonumber,
cosmoB_tostring,
cosmoB_loadstring,
cosmoB_error
};
CosmoCFunction baseLib[] = {cosmoB_print, cosmoB_assert, cosmoB_type, cosmoB_pcall,
cosmoB_tonumber, cosmoB_tostring, cosmoB_loadstring, cosmoB_error};
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_pushCFunction(state, baseLib[i]);
}
@ -167,9 +163,11 @@ void cosmoB_loadLibrary(CState *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) {
CObj *obj = cosmoV_readRef(args[0]); // object to set proto too
CObjObject *proto = cosmoV_readObject(args[1]);
@ -182,25 +180,28 @@ int cosmoB_osetProto(CState *state, int nargs, CValue *args) {
return 0; // nothing
}
int cosmoB_ogetProto(CState *state, int nargs, CValue *args) {
int cosmoB_ogetProto(CState *state, int nargs, CValue *args)
{
if (nargs != 1) {
cosmoV_error(state, "Expected 1 argument, got %d!", nargs);
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
}
int cosmoB_oisChild(CState *state, int nargs, CValue *args) {
int cosmoB_oisChild(CState *state, int nargs, CValue *args)
{
if (nargs != 2) {
cosmoV_error(state, "object.ischild() expected 2 arguments, got %d!", nargs);
return 0;
}
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;
}
@ -212,14 +213,11 @@ int cosmoB_oisChild(CState *state, int nargs, CValue *args) {
return 1;
}
COSMO_API void cosmoB_loadObjLib(CState *state) {
const char *identifiers[] = {
"ischild"
};
COSMO_API void cosmoB_loadObjLib(CState *state)
{
const char *identifiers[] = {"ischild"};
CosmoCFunction objLib[] = {
cosmoB_oisChild
};
CosmoCFunction objLib[] = {cosmoB_oisChild};
// make object library object
cosmoV_pushString(state, "object");
@ -228,7 +226,7 @@ COSMO_API void cosmoB_loadObjLib(CState *state) {
cosmoV_pushString(state, "__getter");
// key & value pair
cosmoV_pushString(state, "__proto"); // key
cosmoV_pushString(state, "__proto"); // key
cosmoV_pushCFunction(state, cosmoB_ogetProto); // value
cosmoV_makeTable(state, 1);
@ -242,7 +240,7 @@ COSMO_API void cosmoB_loadObjLib(CState *state) {
cosmoV_makeTable(state, 1);
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_pushCFunction(state, objLib[i]);
}
@ -256,10 +254,12 @@ COSMO_API void cosmoB_loadObjLib(CState *state) {
cosmoV_register(state, 1);
}
// ================================================================ [OS.*] ================================================================
// ================================================================ [OS.*]
// ================================================================
// os.read()
int cosmoB_osRead(CState *state, int nargs, CValue *args) {
int cosmoB_osRead(CState *state, int nargs, CValue *args)
{
if (nargs != 1) {
cosmoV_error(state, "os.read() expected 1 argument, got %d!", nargs);
return 0;
@ -287,7 +287,7 @@ int cosmoB_osRead(CState *state, int nargs, CValue *args) {
size = ftell(file);
rewind(file);
buf = cosmoM_xmalloc(state, size + 1); // +1 for the NULL terminator
buf = cosmoM_xmalloc(state, size + 1); // +1 for the NULL terminator
bRead = fread(buf, sizeof(char), size, file); // read the file into the buffer
if (bRead < size) {
@ -303,7 +303,8 @@ int cosmoB_osRead(CState *state, int nargs, CValue *args) {
}
// os.time()
int cosmoB_osTime(CState *state, int nargs, CValue *args) {
int cosmoB_osTime(CState *state, int nargs, CValue *args)
{
struct timeval time;
if (nargs > 0) {
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()
int cosmoB_osSystem(CState *state, int nargs, CValue *args) {
int cosmoB_osSystem(CState *state, int nargs, CValue *args)
{
if (nargs != 1) {
cosmoV_error(state, "os.system() expects 1 argument, got %d!", nargs);
return 0;
@ -332,23 +334,16 @@ int cosmoB_osSystem(CState *state, int nargs, CValue *args) {
return 1;
}
COSMO_API void cosmoB_loadOSLib(CState *state) {
const char *identifiers[] = {
"read",
"time",
"system"
};
COSMO_API void cosmoB_loadOSLib(CState *state)
{
const char *identifiers[] = {"read", "time", "system"};
CosmoCFunction osLib[] = {
cosmoB_osRead,
cosmoB_osTime,
cosmoB_osSystem
};
CosmoCFunction osLib[] = {cosmoB_osRead, cosmoB_osTime, cosmoB_osSystem};
cosmoV_pushString(state, "os");
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_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
}
// ================================================================ [STRING.*] ================================================================
// ================================================================ [STRING.*]
// ================================================================
// string.sub
int cosmoB_sSub(CState *state, int nargs, CValue *args) {
int cosmoB_sSub(CState *state, int nargs, CValue *args)
{
if (nargs == 2) {
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;
}
@ -372,14 +370,17 @@ int cosmoB_sSub(CState *state, int nargs, CValue *args) {
// make sure we stay within memory
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;
}
cosmoV_pushLString(state, str->str + ((int)indx), str->length - ((int)indx));
} else if (nargs == 3) {
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;
}
@ -389,7 +390,9 @@ int cosmoB_sSub(CState *state, int nargs, CValue *args) {
// make sure we stay within memory
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;
}
@ -403,10 +406,12 @@ int cosmoB_sSub(CState *state, int nargs, CValue *args) {
}
// string.find
int cosmoB_sFind(CState *state, int nargs, CValue *args) {
int cosmoB_sFind(CState *state, int nargs, CValue *args)
{
if (nargs == 2) {
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;
}
@ -425,7 +430,9 @@ int cosmoB_sFind(CState *state, int nargs, CValue *args) {
cosmoV_pushNumber(state, indx - str->str);
} else if (nargs == 3) {
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;
}
@ -452,14 +459,16 @@ int cosmoB_sFind(CState *state, int nargs, CValue *args) {
}
// string.split
int cosmoB_sSplit(CState *state, int nargs, CValue *args) {
int cosmoB_sSplit(CState *state, int nargs, CValue *args)
{
if (nargs != 2) {
cosmoV_error(state, "string.split() expected 2 arguments, got %d!", nargs);
return 0;
}
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;
}
@ -475,7 +484,8 @@ int cosmoB_sSplit(CState *state, int nargs, CValue *args) {
nIndx = strstr(indx, ptrn->str);
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;
} while (nIndx != NULL);
@ -486,7 +496,8 @@ int cosmoB_sSplit(CState *state, int nargs, CValue *args) {
}
// string.byte
int cosmoB_sByte(CState *state, int nargs, CValue *args) {
int cosmoB_sByte(CState *state, int nargs, CValue *args)
{
if (nargs != 1) {
cosmoV_error(state, "string.byte() expected 1 argument, got %d!", nargs);
return 0;
@ -496,12 +507,12 @@ int cosmoB_sByte(CState *state, int nargs, CValue *args) {
cosmoV_typeError(state, "string.byte", "<string>", "%s", cosmoV_typeStr(args[0]));
return 0;
}
CObjString *str = cosmoV_readString(args[0]);
if (str->length < 1) {
// the length of the string is less than 1, in the future I might throw an error for this, but
// for now im going to copy lua and just return a nil
// the length of the string is less than 1, in the future I might throw an error for this,
// but for now im going to copy lua and just return a nil
return 0;
}
@ -511,7 +522,8 @@ int cosmoB_sByte(CState *state, int nargs, CValue *args) {
}
// string.char
int cosmoB_sChar(CState *state, int nargs, CValue *args) {
int cosmoB_sChar(CState *state, int nargs, CValue *args)
{
if (nargs != 1) {
cosmoV_error(state, "string.char() expected 1 argument, got %d!", nargs);
return 0;
@ -522,7 +534,8 @@ int cosmoB_sChar(CState *state, int nargs, CValue *args) {
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]);
char c = num;
@ -536,7 +549,8 @@ int cosmoB_sChar(CState *state, int nargs, CValue *args) {
return 1;
}
int cosmoB_sLen(CState *state, int nargs, CValue *args) {
int cosmoB_sLen(CState *state, int nargs, CValue *args)
{
if (nargs < 1) {
cosmoV_error(state, "string.len() expected 1 argument, got %d!", nargs);
return 0;
@ -552,7 +566,8 @@ int cosmoB_sLen(CState *state, int nargs, CValue *args) {
return 1;
}
int cosmoB_sRep(CState *state, int nargs, CValue *args) {
int cosmoB_sRep(CState *state, int nargs, CValue *args)
{
if (nargs != 2) {
cosmoV_error(state, "string.rep() expected 2 arguments, got %d!", nargs);
return 0;
@ -560,7 +575,8 @@ int cosmoB_sRep(CState *state, int nargs, CValue *args) {
// expects <string>, <number>
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;
}
@ -582,37 +598,23 @@ int cosmoB_sRep(CState *state, int nargs, CValue *args) {
// write the NULL terminator
newStr[length] = '\0';
// 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;
}
void cosmoB_loadStrLib(CState *state) {
const char *identifiers[] = {
"sub",
"find",
"split",
"byte",
"char",
"len",
"rep"
};
void cosmoB_loadStrLib(CState *state)
{
const char *identifiers[] = {"sub", "find", "split", "byte", "char", "len", "rep"};
CosmoCFunction strLib[] = {
cosmoB_sSub,
cosmoB_sFind,
cosmoB_sSplit,
cosmoB_sByte,
cosmoB_sChar,
cosmoB_sLen,
cosmoB_sRep
};
CosmoCFunction strLib[] = {cosmoB_sSub, cosmoB_sFind, cosmoB_sSplit, cosmoB_sByte,
cosmoB_sChar, cosmoB_sLen, cosmoB_sRep};
// make string library object
cosmoV_pushString(state, "string");
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_pushCFunction(state, strLib[i]);
}
@ -626,10 +628,12 @@ void cosmoB_loadStrLib(CState *state) {
cosmoV_register(state, 1);
}
// ================================================================ [MATH] ================================================================
// ================================================================ [MATH]
// ================================================================
// math.abs
int cosmoB_mAbs(CState *state, int nargs, CValue *args) {
int cosmoB_mAbs(CState *state, int nargs, CValue *args)
{
if (nargs != 1) {
cosmoV_error(state, "math.abs() expected 1 argument, got %d!", nargs);
return 0;
@ -645,7 +649,8 @@ int cosmoB_mAbs(CState *state, int nargs, CValue *args) {
}
// math.floor
int cosmoB_mFloor(CState *state, int nargs, CValue *args) {
int cosmoB_mFloor(CState *state, int nargs, CValue *args)
{
if (nargs != 1) {
cosmoV_error(state, "math.floor() expected 1 argument, got %d!", nargs);
return 0;
@ -661,7 +666,8 @@ int cosmoB_mFloor(CState *state, int nargs, CValue *args) {
}
// math.ceil
int cosmoB_mCeil(CState *state, int nargs, CValue *args) {
int cosmoB_mCeil(CState *state, int nargs, CValue *args)
{
if (nargs != 1) {
cosmoV_error(state, "math.ceil() expected 1 argument, got %d!", nargs);
return 0;
@ -684,7 +690,8 @@ int cosmoB_mCeil(CState *state, int nargs, CValue *args) {
return 1;
}
int cosmoB_mSin(CState *state, int nargs, CValue *args) {
int cosmoB_mSin(CState *state, int nargs, CValue *args)
{
if (nargs != 1) {
cosmoV_error(state, "math.sin() expected 1 argument, got %d!", nargs);
return 0;
@ -699,7 +706,8 @@ int cosmoB_mSin(CState *state, int nargs, CValue *args) {
return 1;
}
int cosmoB_mCos(CState *state, int nargs, CValue *args) {
int cosmoB_mCos(CState *state, int nargs, CValue *args)
{
if (nargs != 1) {
cosmoV_error(state, "math.cos() expected 1 argument, got %d!", nargs);
return 0;
@ -714,7 +722,8 @@ int cosmoB_mCos(CState *state, int nargs, CValue *args) {
return 1;
}
int cosmoB_mTan(CState *state, int nargs, CValue *args) {
int cosmoB_mTan(CState *state, int nargs, CValue *args)
{
if (nargs != 1) {
cosmoV_error(state, "math.tan() expected 1 argument, got %d!", nargs);
return 0;
@ -729,7 +738,8 @@ int cosmoB_mTan(CState *state, int nargs, CValue *args) {
return 1;
}
int cosmoB_mASin(CState *state, int nargs, CValue *args) {
int cosmoB_mASin(CState *state, int nargs, CValue *args)
{
if (nargs != 1) {
cosmoV_error(state, "math.asin() expected 1 argument, got %d!", nargs);
return 0;
@ -744,7 +754,8 @@ int cosmoB_mASin(CState *state, int nargs, CValue *args) {
return 1;
}
int cosmoB_mACos(CState *state, int nargs, CValue *args) {
int cosmoB_mACos(CState *state, int nargs, CValue *args)
{
if (nargs != 1) {
cosmoV_error(state, "math.acos() expected 1 argument, got %d!", nargs);
return 0;
@ -759,7 +770,8 @@ int cosmoB_mACos(CState *state, int nargs, CValue *args) {
return 1;
}
int cosmoB_mATan(CState *state, int nargs, CValue *args) {
int cosmoB_mATan(CState *state, int nargs, CValue *args)
{
if (nargs != 1) {
cosmoV_error(state, "math.atan() expected 1 argument, got %d!", nargs);
return 0;
@ -774,7 +786,8 @@ int cosmoB_mATan(CState *state, int nargs, CValue *args) {
return 1;
}
int cosmoB_mRad(CState *state, int nargs, CValue *args) {
int cosmoB_mRad(CState *state, int nargs, CValue *args)
{
if (nargs != 1) {
cosmoV_error(state, "math.rad() expected 1 argument, got %d!", nargs);
return 0;
@ -790,7 +803,8 @@ int cosmoB_mRad(CState *state, int nargs, CValue *args) {
return 1;
}
int cosmoB_mDeg(CState *state, int nargs, CValue *args) {
int cosmoB_mDeg(CState *state, int nargs, CValue *args)
{
if (nargs != 1) {
cosmoV_error(state, "math.deg() expected 1 argument, got %d!", nargs);
return 0;
@ -806,39 +820,19 @@ int cosmoB_mDeg(CState *state, int nargs, CValue *args) {
return 1;
}
void cosmoB_loadMathLib(CState *state) {
const char *identifiers[] = {
"abs",
"floor",
"ceil",
"sin",
"cos",
"tan",
"asin",
"acos",
"atan",
"rad",
"deg"
};
void cosmoB_loadMathLib(CState *state)
{
const char *identifiers[] = {"abs", "floor", "ceil", "sin", "cos", "tan",
"asin", "acos", "atan", "rad", "deg"};
CosmoCFunction mathLib[] = {
cosmoB_mAbs,
cosmoB_mFloor,
cosmoB_mCeil,
cosmoB_mSin,
cosmoB_mCos,
cosmoB_mTan,
cosmoB_mASin,
cosmoB_mACos,
cosmoB_mATan,
cosmoB_mRad,
cosmoB_mDeg
};
CosmoCFunction mathLib[] = {cosmoB_mAbs, cosmoB_mFloor, cosmoB_mCeil, cosmoB_mSin,
cosmoB_mCos, cosmoB_mTan, cosmoB_mASin, cosmoB_mACos,
cosmoB_mATan, cosmoB_mRad, cosmoB_mDeg};
// make math library object
cosmoV_pushString(state, "math");
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_pushCFunction(state, mathLib[i]);
}
@ -852,41 +846,47 @@ void cosmoB_loadMathLib(CState *state) {
cosmoV_register(state, 1);
}
// ================================================================ [VM.*] ================================================================
// ================================================================ [VM.*]
// ================================================================
// 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
cosmoV_pushRef(state, (CObj*)state->globals);
cosmoV_pushRef(state, (CObj *)state->globals);
return 1;
}
// vm.__setter["globals"]
int cosmoB_vsetGlobal(CState *state, int nargs, CValue *args) {
int cosmoB_vsetGlobal(CState *state, int nargs, CValue *args)
{
if (nargs != 2) {
cosmoV_error(state, "Expected 2 argumenst, got %d!", nargs);
return 0;
}
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;
}
// this makes me very nervous ngl
CObjTable *tbl = (CObjTable*)cosmoV_readRef(args[1]);
CObjTable *tbl = (CObjTable *)cosmoV_readRef(args[1]);
state->globals = tbl;
return 0;
}
int cosmoB_vindexBProto(CState *state, int nargs, CValue *args) {
int cosmoB_vindexBProto(CState *state, int nargs, CValue *args)
{
if (nargs != 2) {
cosmoV_error(state, "Expected 2 arguments, got %d!", nargs);
return 0;
}
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;
}
@ -898,21 +898,24 @@ int cosmoB_vindexBProto(CState *state, int nargs, CValue *args) {
}
if (state->protoObjects[indx] != NULL)
cosmoV_pushRef(state, (CObj*)state->protoObjects[indx]);
cosmoV_pushRef(state, (CObj *)state->protoObjects[indx]);
else
cosmoV_pushNil(state);
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) {
cosmoV_error(state, "Expected 3 arguments, got %d!", nargs);
return 0;
}
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;
}
@ -929,10 +932,11 @@ int cosmoB_vnewindexBProto(CState *state, int nargs, CValue *args) {
}
// 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)
cosmoM_unfreezeGC(state);
// now force a garbage collection
cosmoM_collectGarbage(state);
@ -943,7 +947,8 @@ int cosmoB_vcollect(CState *state, int nargs, CValue *args) {
return 0;
}
void cosmoB_loadVM(CState *state) {
void cosmoB_loadVM(CState *state)
{
// make vm.* object
cosmoV_pushString(state, "vm");

View File

@ -32,7 +32,8 @@ COSMO_API void cosmoB_loadOSLib(CState *state);
- string.char & <string>:char()
- 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", " ")`
*/
COSMO_API void cosmoB_loadStrLib(CState *state);
@ -54,7 +55,7 @@ COSMO_API void cosmoB_loadMathLib(CState *state);
*/
COSMO_API void cosmoB_loadVM(CState *state);
#define cosmoV_typeError(state, name, expectedTypes, formatStr, ...) \
cosmoV_error(state, name " expected (" expectedTypes "), got (" formatStr ")!", __VA_ARGS__);
#define cosmoV_typeError(state, name, expectedTypes, formatStr, ...) \
cosmoV_error(state, name " expected (" expectedTypes "), got (" formatStr ")!", __VA_ARGS__);
#endif

View File

@ -1,27 +1,31 @@
#include "cmem.h"
#include "cchunk.h"
#include "cmem.h"
#include "cobj.h"
#include "cvalue.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));
initChunk(state, chunk, startCapacity);
return chunk;
}
void initChunk(CState* state, CChunk *chunk, size_t startCapacity) {
void initChunk(CState *state, CChunk *chunk, size_t startCapacity)
{
chunk->capacity = startCapacity;
chunk->lineCapacity = startCapacity;
chunk->count = 0;
chunk->buf = NULL; // when writeByteChunk is called, it'll allocate the array for us
chunk->lineInfo = NULL;
// constants
initValArray(state, &chunk->constants, ARRAY_START);
}
void cleanChunk(CState* state, CChunk *chunk) {
void cleanChunk(CState *state, CChunk *chunk)
{
// first, free the chunk buffer
cosmoM_freearray(state, INSTRUCTION, chunk->buf, chunk->capacity);
// then the line info
@ -30,13 +34,15 @@ void cleanChunk(CState* state, CChunk *chunk) {
cleanValArray(state, &chunk->constants);
}
void freeChunk(CState* state, CChunk *chunk) {
void freeChunk(CState *state, CChunk *chunk)
{
cleanChunk(state, chunk);
// now, free the wrapper struct
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
for (size_t i = 0; i < chunk->constants.count; 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
}
// ================================================================ [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?
cosmoM_growarray(state, INSTRUCTION, chunk->buf, chunk->count, chunk->capacity);
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;
}
void writeu16Chunk(CState* state, CChunk *chunk, uint16_t i, int line) {
INSTRUCTION *buffer = (INSTRUCTION*)(&i);
void writeu16Chunk(CState *state, CChunk *chunk, uint16_t i, int line)
{
INSTRUCTION *buffer = (INSTRUCTION *)(&i);
int sz = sizeof(uint16_t) / sizeof(INSTRUCTION);
for (int i = 0; i < sz; i++) {

View File

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

View File

@ -1,49 +1,62 @@
#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++)
printf("\t");
}
int simpleInstruction(const char *name, int offset) {
int simpleInstruction(const char *name, int offset)
{
printf("%s", name);
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));
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));
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;
printf("%-16s [%05d] - jumps to %04d", name, jmp, offset + 3 + jmp);
return offset + 1 + (sizeof(uint16_t) / sizeof(INSTRUCTION));
}
int u8u8OperandInstruction(const char *name, CChunk *chunk, int offset) {
printf("%-16s [%03d] [%03d]", name, readu8Chunk(chunk, offset + 1), readu8Chunk(chunk, offset + 2));
int u8u8OperandInstruction(const char *name, CChunk *chunk, int offset)
{
printf("%-16s [%03d] [%03d]", name, readu8Chunk(chunk, offset + 1),
readu8Chunk(chunk, offset + 2));
return offset + 3; // op + u8 + u8
}
int u8u16OperandInstruction(const char *name, CChunk *chunk, int offset) {
printf("%-16s [%03d] [%05d]", name, readu8Chunk(chunk, offset + 1), readu16Chunk(chunk, offset + 2));
int u8u16OperandInstruction(const char *name, CChunk *chunk, int offset)
{
printf("%-16s [%03d] [%05d]", name, readu8Chunk(chunk, offset + 1),
readu16Chunk(chunk, offset + 2));
return offset + 4; // op + u8 + u16
}
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));
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));
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);
printf("%-16s [%05d] - ", name, 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
void disasmChunk(CChunk *chunk, const char *name, int indent) {
void disasmChunk(CChunk *chunk, const char *name, int indent)
{
printIndent(indent);
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);
printf("%04d ", offset);
@ -79,131 +94,131 @@ int disasmInstr(CChunk *chunk, int offset, int indent) {
}
switch (i) {
case OP_LOADCONST:
return constInstruction("OP_LOADCONST", chunk, offset);
case OP_SETGLOBAL:
return constInstruction("OP_SETGLOBAL", chunk, offset);
case OP_GETGLOBAL:
return constInstruction("OP_GETGLOBAL", chunk, offset);
case OP_SETLOCAL:
return u8OperandInstruction("OP_SETLOCAL", chunk, offset);
case OP_GETLOCAL:
return u8OperandInstruction("OP_GETLOCAL", chunk, offset);
case OP_SETUPVAL:
return u8OperandInstruction("OP_SETUPVAL", chunk, offset);
case OP_GETUPVAL:
return u8OperandInstruction("OP_GETUPVAL", chunk, offset);
case OP_PEJMP:
return JumpInstruction("OP_PEJMP", chunk, offset, 1);
case OP_EJMP:
return JumpInstruction("OP_EJMP", chunk, offset, 1);
case OP_JMP:
return JumpInstruction("OP_JMP", chunk, offset, 1);
case OP_JMPBACK:
return JumpInstruction("OP_JMPBACK", chunk, offset, -1);
case OP_POP:
return u8OperandInstruction("OP_POP", chunk, offset);
case OP_CALL:
return u8u8OperandInstruction("OP_CALL", chunk, offset);
case OP_CLOSURE: {
int index = readu16Chunk(chunk, offset + 1);
printf("%-16s [%05d] - ", "OP_CLOSURE", index);
CValue val = chunk->constants.values[index];
CObjFunction *cobjFunc = (CObjFunction*)cosmoV_readRef(val);
offset += 3; // we consumed the opcode + u16
case OP_LOADCONST:
return constInstruction("OP_LOADCONST", chunk, offset);
case OP_SETGLOBAL:
return constInstruction("OP_SETGLOBAL", chunk, offset);
case OP_GETGLOBAL:
return constInstruction("OP_GETGLOBAL", chunk, offset);
case OP_SETLOCAL:
return u8OperandInstruction("OP_SETLOCAL", chunk, offset);
case OP_GETLOCAL:
return u8OperandInstruction("OP_GETLOCAL", chunk, offset);
case OP_SETUPVAL:
return u8OperandInstruction("OP_SETUPVAL", chunk, offset);
case OP_GETUPVAL:
return u8OperandInstruction("OP_GETUPVAL", chunk, offset);
case OP_PEJMP:
return JumpInstruction("OP_PEJMP", chunk, offset, 1);
case OP_EJMP:
return JumpInstruction("OP_EJMP", chunk, offset, 1);
case OP_JMP:
return JumpInstruction("OP_JMP", chunk, offset, 1);
case OP_JMPBACK:
return JumpInstruction("OP_JMPBACK", chunk, offset, -1);
case OP_POP:
return u8OperandInstruction("OP_POP", chunk, offset);
case OP_CALL:
return u8u8OperandInstruction("OP_CALL", chunk, offset);
case OP_CLOSURE: {
int index = readu16Chunk(chunk, offset + 1);
printf("%-16s [%05d] - ", "OP_CLOSURE", index);
CValue val = chunk->constants.values[index];
CObjFunction *cobjFunc = (CObjFunction *)cosmoV_readRef(val);
offset += 3; // we consumed the opcode + u16
printValue(val);
printf("\n");
// list the upvalues/locals that are captured
for (int i = 0; i < cobjFunc->upvals; i++) {
uint8_t encoding = readu8Chunk(chunk, offset++);
uint8_t index = readu8Chunk(chunk, offset++);
printIndent(indent + 1);
printf("references %s [%d]\n", encoding == OP_GETLOCAL ? "local" : "upvalue", index);
}
printValue(val);
printf("\n");
// print the chunk
disasmChunk(&cobjFunc->chunk, cobjFunc->name == NULL ? UNNAMEDCHUNK : cobjFunc->name->str, indent+1);
return offset;
// list the upvalues/locals that are captured
for (int i = 0; i < cobjFunc->upvals; i++) {
uint8_t encoding = readu8Chunk(chunk, offset++);
uint8_t index = readu8Chunk(chunk, offset++);
printIndent(indent + 1);
printf("references %s [%d]\n", encoding == OP_GETLOCAL ? "local" : "upvalue", index);
}
case OP_CLOSE:
return simpleInstruction("OP_CLOSE", offset);
case OP_NEWTABLE:
return u16OperandInstruction("OP_NEWTABLE", chunk, offset);
case OP_NEWARRAY:
return u16OperandInstruction("OP_NEWARRAY", chunk, offset);
case OP_INDEX:
return simpleInstruction("OP_INDEX", offset);
case OP_NEWINDEX:
return simpleInstruction("OP_NEWINDEX", offset);
case OP_NEWOBJECT:
return u16OperandInstruction("OP_NEWOBJECT", chunk, offset);
case OP_SETOBJECT:
return constInstruction("OP_SETOBJECT", chunk, offset);
case OP_GETOBJECT:
return constInstruction("OP_GETOBJECT", chunk, offset);
case OP_GETMETHOD:
return constInstruction("OP_GETMETHOD", chunk, offset);
case OP_INVOKE:
return u8u8u16OperandInstruction("OP_INVOKE", chunk, offset);
case OP_ITER:
return simpleInstruction("OP_ITER", offset);
case OP_NEXT:
return u8u16OperandInstruction("OP_NEXT", chunk, offset);
case OP_ADD:
return simpleInstruction("OP_ADD", offset);
case OP_SUB:
return simpleInstruction("OP_SUB", offset);
case OP_MULT:
return simpleInstruction("OP_MULT", offset);
case OP_DIV:
return simpleInstruction("OP_DIV", offset);
case OP_MOD:
return simpleInstruction("OP_MOD", offset);
case OP_POW:
return simpleInstruction("OP_POW", offset);
case OP_TRUE:
return simpleInstruction("OP_TRUE", offset);
case OP_FALSE:
return simpleInstruction("OP_FALSE", offset);
case OP_NIL:
return simpleInstruction("OP_NIL", offset);
case OP_NOT:
return simpleInstruction("OP_NOT", offset);
case OP_EQUAL:
return simpleInstruction("OP_EQUAL", offset);
case OP_GREATER:
return simpleInstruction("OP_GREATER", offset);
case OP_GREATER_EQUAL:
return simpleInstruction("OP_GREATER_EQUAL", offset);
case OP_LESS:
return simpleInstruction("OP_LESS", offset);
case OP_LESS_EQUAL:
return simpleInstruction("OP_LESS_EQUAL", offset);
case OP_NEGATE:
return simpleInstruction("OP_NEGATE", offset);
case OP_COUNT:
return simpleInstruction("OP_COUNT", offset);
case OP_CONCAT:
return u8OperandInstruction("OP_CONCAT", chunk, offset);
case OP_INCLOCAL:
return u8u8OperandInstruction("OP_INCLOCAL", chunk, offset);
case OP_INCGLOBAL:
return u8u16OperandInstruction("OP_INCGLOBAL", chunk, offset);
case OP_INCUPVAL:
return u8u8OperandInstruction("OP_INCUPVAL", chunk, offset);
case OP_INCINDEX:
return u8OperandInstruction("OP_INCINDEX", chunk, offset);
case OP_INCOBJECT:
return u8u16OperandInstruction("OP_INCOBJECT", chunk, offset);
case OP_RETURN:
return u8OperandInstruction("OP_RETURN", chunk, offset);
default:
printf("Unknown opcode! [%d]\n", i);
return 1;
}
// print the chunk
disasmChunk(&cobjFunc->chunk, cobjFunc->name == NULL ? UNNAMEDCHUNK : cobjFunc->name->str,
indent + 1);
return offset;
}
case OP_CLOSE:
return simpleInstruction("OP_CLOSE", offset);
case OP_NEWTABLE:
return u16OperandInstruction("OP_NEWTABLE", chunk, offset);
case OP_NEWARRAY:
return u16OperandInstruction("OP_NEWARRAY", chunk, offset);
case OP_INDEX:
return simpleInstruction("OP_INDEX", offset);
case OP_NEWINDEX:
return simpleInstruction("OP_NEWINDEX", offset);
case OP_NEWOBJECT:
return u16OperandInstruction("OP_NEWOBJECT", chunk, offset);
case OP_SETOBJECT:
return constInstruction("OP_SETOBJECT", chunk, offset);
case OP_GETOBJECT:
return constInstruction("OP_GETOBJECT", chunk, offset);
case OP_GETMETHOD:
return constInstruction("OP_GETMETHOD", chunk, offset);
case OP_INVOKE:
return u8u8u16OperandInstruction("OP_INVOKE", chunk, offset);
case OP_ITER:
return simpleInstruction("OP_ITER", offset);
case OP_NEXT:
return u8u16OperandInstruction("OP_NEXT", chunk, offset);
case OP_ADD:
return simpleInstruction("OP_ADD", offset);
case OP_SUB:
return simpleInstruction("OP_SUB", offset);
case OP_MULT:
return simpleInstruction("OP_MULT", offset);
case OP_DIV:
return simpleInstruction("OP_DIV", offset);
case OP_MOD:
return simpleInstruction("OP_MOD", offset);
case OP_POW:
return simpleInstruction("OP_POW", offset);
case OP_TRUE:
return simpleInstruction("OP_TRUE", offset);
case OP_FALSE:
return simpleInstruction("OP_FALSE", offset);
case OP_NIL:
return simpleInstruction("OP_NIL", offset);
case OP_NOT:
return simpleInstruction("OP_NOT", offset);
case OP_EQUAL:
return simpleInstruction("OP_EQUAL", offset);
case OP_GREATER:
return simpleInstruction("OP_GREATER", offset);
case OP_GREATER_EQUAL:
return simpleInstruction("OP_GREATER_EQUAL", offset);
case OP_LESS:
return simpleInstruction("OP_LESS", offset);
case OP_LESS_EQUAL:
return simpleInstruction("OP_LESS_EQUAL", offset);
case OP_NEGATE:
return simpleInstruction("OP_NEGATE", offset);
case OP_COUNT:
return simpleInstruction("OP_COUNT", offset);
case OP_CONCAT:
return u8OperandInstruction("OP_CONCAT", chunk, offset);
case OP_INCLOCAL:
return u8u8OperandInstruction("OP_INCLOCAL", chunk, offset);
case OP_INCGLOBAL:
return u8u16OperandInstruction("OP_INCGLOBAL", chunk, offset);
case OP_INCUPVAL:
return u8u8OperandInstruction("OP_INCUPVAL", chunk, offset);
case OP_INCINDEX:
return u8OperandInstruction("OP_INCINDEX", chunk, offset);
case OP_INCOBJECT:
return u8u16OperandInstruction("OP_INCOBJECT", chunk, offset);
case OP_RETURN:
return u8OperandInstruction("OP_RETURN", chunk, offset);
default:
printf("Unknown opcode! [%d]\n", i);
return 1;
}
return 1;
}

View File

@ -1,72 +1,81 @@
#include "clex.h"
#include "cmem.h"
#include <string.h>
CReservedWord reservedWords[] = {
{TOKEN_AND, "and", 3},
{TOKEN_BREAK, "break", 5},
{ TOKEN_AND, "and", 3},
{ TOKEN_BREAK, "break", 5},
{TOKEN_CONTINUE, "continue", 8},
{TOKEN_DO, "do", 2},
{TOKEN_ELSE, "else", 4},
{TOKEN_ELSEIF, "elseif", 6},
{TOKEN_END, "end", 3},
{TOKEN_FALSE, "false", 5},
{TOKEN_FOR, "for", 3},
{ TOKEN_DO, "do", 2},
{ TOKEN_ELSE, "else", 4},
{ TOKEN_ELSEIF, "elseif", 6},
{ TOKEN_END, "end", 3},
{ TOKEN_FALSE, "false", 5},
{ TOKEN_FOR, "for", 3},
{TOKEN_FUNCTION, "function", 8},
{TOKEN_IF, "if", 2},
{TOKEN_IN, "in", 2},
{TOKEN_LOCAL, "local", 5},
{TOKEN_NIL, "nil", 3},
{TOKEN_NOT, "not", 3},
{TOKEN_OR, "or", 2},
{TOKEN_PROTO, "proto", 5},
{TOKEN_RETURN, "return", 6},
{TOKEN_THEN, "then", 4},
{TOKEN_TRUE, "true", 4},
{TOKEN_VAR, "var", 3},
{TOKEN_WHILE, "while", 5}
{ TOKEN_IF, "if", 2},
{ TOKEN_IN, "in", 2},
{ TOKEN_LOCAL, "local", 5},
{ TOKEN_NIL, "nil", 3},
{ TOKEN_NOT, "not", 3},
{ TOKEN_OR, "or", 2},
{ TOKEN_PROTO, "proto", 5},
{ TOKEN_RETURN, "return", 6},
{ TOKEN_THEN, "then", 4},
{ TOKEN_TRUE, "true", 4},
{ TOKEN_VAR, "var", 3},
{ TOKEN_WHILE, "while", 5}
};
// returns true if current token is a heap allocated buffer
static bool isBuffer(CLexState *state) {
return state->buffer != NULL;
static bool isBuffer(CLexState *state)
{
return state->buffer != NULL;
}
// marks the current token as heap allocated & allocates the buffer
static void makeBuffer(CLexState *state) {
state->buffer = cosmoM_xmalloc(state->cstate, sizeof(char) * 32); // start with a 32 character long buffer
static void makeBuffer(CLexState *state)
{
state->buffer =
cosmoM_xmalloc(state->cstate, sizeof(char) * 32); // start with a 32 character long buffer
state->bufCount = 0;
state->bufCap = 32;
}
static void resetBuffer(CLexState *state) {
static void resetBuffer(CLexState *state)
{
state->buffer = NULL;
state->bufCount = 0;
state->bufCap = 0;
}
// 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);
resetBuffer(state);
}
// 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);
state->buffer[state->bufCount++] = c;
}
// 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);
}
// 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
appendBuffer(state, '\0');
@ -84,7 +93,8 @@ static char *cutBuffer(CLexState *state, int *length) {
return cosmoM_reallocate(state->cstate, buf, cap, count);
}
static CToken makeToken(CLexState *state, CTokenType type) {
static CToken makeToken(CLexState *state, CTokenType type)
{
CToken token;
token.type = type;
token.line = state->line;
@ -95,38 +105,43 @@ static CToken makeToken(CLexState *state, CTokenType type) {
token.start = state->startChar;
token.length = state->currentChar - state->startChar; // delta between start & current
}
state->lastType = type;
return token;
}
static CToken makeError(CLexState *state, const char *msg) {
static CToken makeError(CLexState *state, const char *msg)
{
CToken token;
token.type = TOKEN_ERROR;
token.start = (char*)msg;
token.start = (char *)msg;
token.length = strlen(msg);
token.line = state->line;
if (isBuffer(state))
freeBuffer(state);
return token;
}
static inline bool isEnd(CLexState *state) {
static inline bool isEnd(CLexState *state)
{
return *state->currentChar == '\0';
}
static inline bool isNumerical(char c) {
static inline bool isNumerical(char c)
{
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 '_'
}
static bool match(CLexState *state, char expected) {
static bool match(CLexState *state, char expected)
{
if (isEnd(state) || *state->currentChar != expected)
return false;
@ -135,35 +150,41 @@ static bool match(CLexState *state, char expected) {
return true;
}
char peek(CLexState *state) {
char peek(CLexState *state)
{
return *state->currentChar;
}
static char peekNext(CLexState *state) {
if (isEnd(state))
static char peekNext(CLexState *state)
{
if (isEnd(state))
return '\0';
return state->currentChar[1];
}
char next(CLexState *state) {
char next(CLexState *state)
{
if (isEnd(state))
return '\0'; // return a null terminator
return '\0'; // return a null terminator
state->currentChar++;
return state->currentChar[-1];
}
bool isHex(char c) {
bool isHex(char 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;
// check against reserved word list
for (size_t i = 0; i < sizeof(reservedWords) / sizeof(CReservedWord); i++) {
// 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;
}
@ -171,161 +192,178 @@ CTokenType identifierType(CLexState *state) {
return TOKEN_IDENTIFIER;
}
void skipWhitespace(CLexState *state) {
void skipWhitespace(CLexState *state)
{
while (true) {
char c = peek(state);
switch (c) {
case '\n': // mark new line
state->line++;
case ' ':
case '\r':
case '\t':
next(state); // consume the whitespace
case '\n': // mark new line
state->line++;
case ' ':
case '\r':
case '\t':
next(state); // consume the whitespace
break;
case '/': // consume comments
if (peekNext(state) == '/') {
// skip to next line (also let \n be consumed on the next iteration to properly
// handle that)
while (!isEnd(state) &&
peek(state) != '\n') // if it's not a newline or the end of the source
next(state);
// keep consuming whitespace
break;
case '/': // consume comments
if (peekNext(state) == '/') {
// skip to next line (also let \n be consumed on the next iteration to properly handle that)
while (!isEnd(state) && peek(state) != '\n') // if it's not a newline or the end of the source
next(state);
// keep consuming whitespace
break;
} 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
next(state);
// consume the '*/'
next(state);
} 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
next(state);
// keep consuming whitespace
break;
}
return; // it's a TOKEN_SLASH, let the main body handle that
default: // it's no longer whitespace, return!
return;
// consume the '*/'
next(state);
next(state);
// keep consuming whitespace
break;
}
return; // it's a TOKEN_SLASH, let the main body handle that
default: // it's no longer whitespace, return!
return;
}
}
}
CToken parseString(CLexState *state) {
CToken parseString(CLexState *state)
{
makeBuffer(state); // buffer mode
while (peek(state) != '"' && !isEnd(state)) {
switch (peek(state)) {
case '\n': // strings can't stretch across lines
return makeError(state, "Unterminated string!");
case '\\': { // special character
next(state); // consume the '\' character
case '\n': // strings can't stretch across lines
return makeError(state, "Unterminated string!");
case '\\': { // special character
next(state); // consume the '\' character
switch (peek(state)) {
case 'r': case 'n': appendBuffer(state, '\n'); break;
case 't': appendBuffer(state, '\t'); break;
case '\\': appendBuffer(state, '\\'); break;
case '"': appendBuffer(state, '"'); break;
case 'x': // hexadecimal character encoding
next(state); // skip 'x'
switch (peek(state)) {
case 'r':
case 'n':
appendBuffer(state, '\n');
break;
case 't':
appendBuffer(state, '\t');
break;
case '\\':
appendBuffer(state, '\\');
break;
case '"':
appendBuffer(state, '"');
break;
case 'x': // hexadecimal character encoding
next(state); // skip 'x'
if (isHex(peek(state))) {
char *numStart = state->currentChar;
if (isHex(peek(state))) {
char *numStart = state->currentChar;
// consume the hexnum
while (isHex(peek(state)))
next(state);
state->currentChar--; // since next() is called after
unsigned int num = (unsigned int)strtoul(numStart, NULL, 16);
// consume the hexnum
while (isHex(peek(state)))
next(state);
state->currentChar--; // since next() is called after
if (num > 255) // sanity check
return makeError(state, "Character out of range! > 255!");
unsigned int num = (unsigned int)strtoul(numStart, NULL, 16);
appendBuffer(state, num);
break;
}
return makeError(state, "Unknown hexadecimal character encoding!");
case 'b': // binary character encoding
next(state); // skip 'b'
if (num > 255) // sanity check
return makeError(state, "Character out of range! > 255!");
if (peek(state) == '0' || peek(state) == '1') {
char *numStart = state->currentChar;
// consume the bin
while (peek(state) == '0' || peek(state) == '1')
next(state);
state->currentChar--; // since next() is called after
unsigned int num = (unsigned int)strtoul(numStart, NULL, 2);
if (num > 255) // sanity check
return makeError(state, "Character out of range! > 255!");
appendBuffer(state, num);
break;
}
return makeError(state, "Unknown binary character encoding!");
default: {
if (isNumerical(peek(state))) {
char *numStart = state->currentChar;
// consume the number
while (isNumerical(peek(state)))
next(state);
state->currentChar--; // since next() is called after
unsigned int num = (unsigned int)strtoul(numStart, NULL, 10);
if (num > 255) // sanity check
return makeError(state, "Character out of range! > 255!");
appendBuffer(state, num);
break;
}
return makeError(state, "Unknown special character!"); // TODO: maybe a more descriptive error?
}
appendBuffer(state, num);
break;
}
next(state); // consume special character
break;
}
return makeError(state, "Unknown hexadecimal character encoding!");
case 'b': // binary character encoding
next(state); // skip 'b'
if (peek(state) == '0' || peek(state) == '1') {
char *numStart = state->currentChar;
// consume the bin
while (peek(state) == '0' || peek(state) == '1')
next(state);
state->currentChar--; // since next() is called after
unsigned int num = (unsigned int)strtoul(numStart, NULL, 2);
if (num > 255) // sanity check
return makeError(state, "Character out of range! > 255!");
appendBuffer(state, num);
break;
}
return makeError(state, "Unknown binary character encoding!");
default: {
saveBuffer(state); // save the character!
next(state); // consume
if (isNumerical(peek(state))) {
char *numStart = state->currentChar;
// consume the number
while (isNumerical(peek(state)))
next(state);
state->currentChar--; // since next() is called after
unsigned int num = (unsigned int)strtoul(numStart, NULL, 10);
if (num > 255) // sanity check
return makeError(state, "Character out of range! > 255!");
appendBuffer(state, num);
break;
}
return makeError(
state, "Unknown special character!"); // TODO: maybe a more descriptive error?
}
}
next(state); // consume special character
break;
}
default: {
saveBuffer(state); // save the character!
next(state); // consume
}
}
}
if (isEnd(state))
return makeError(state, "Unterminated string!");
next(state); // consume closing quote
return makeToken(state, TOKEN_STRING);
}
CToken parseNumber(CLexState *state) {
CToken parseNumber(CLexState *state)
{
switch (peek(state)) {
case 'x': // hexadecimal number
case 'x': // hexadecimal number
next(state);
while (isHex(peek(state)))
next(state);
while (isHex(peek(state)))
next(state);
return makeToken(state, TOKEN_HEXNUMBER);
case 'b': // binary number
next(state);
return makeToken(state, TOKEN_HEXNUMBER);
case 'b': // binary number
while (peek(state) == '0' || peek(state) == '1')
next(state);
while (peek(state) == '0' || peek(state) == '1')
next(state);
return makeToken(state, TOKEN_BINNUMBER);
default: // it's a one digit number!!!!!
if (!isNumerical(peek(state)) && !(peek(state) == '.'))
return makeToken(state, TOKEN_NUMBER);
// if it is a number, fall through and parse normally
return makeToken(state, TOKEN_BINNUMBER);
default: // it's a one digit number!!!!!
if (!isNumerical(peek(state)) && !(peek(state) == '.'))
return makeToken(state, TOKEN_NUMBER);
// if it is a number, fall through and parse normally
}
// consume number
while (isNumerical(peek(state))) {
next(state);
@ -342,18 +380,20 @@ CToken parseNumber(CLexState *state) {
return makeToken(state, TOKEN_NUMBER);
}
CToken parseIdentifier(CLexState *state) {
CToken parseIdentifier(CLexState *state)
{
// read literal
while ((isAlpha(peek(state)) || isNumerical(peek(state))) && !isEnd(state))
while ((isAlpha(peek(state)) || isNumerical(peek(state))) && !isEnd(state))
next(state);
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));
state->startChar = (char*)source;
state->currentChar = (char*)source;
state->startChar = (char *)source;
state->currentChar = (char *)source;
state->line = 1;
state->lastLine = 0;
state->lastType = TOKEN_ERROR;
@ -364,58 +404,82 @@ CLexState *cosmoL_newLexState(CState *cstate, const char *source) {
return state;
}
void cosmoL_freeLexState(CState *state, CLexState *lstate) {
void cosmoL_freeLexState(CState *state, CLexState *lstate)
{
cosmoM_free(state, CLexState, lstate);
}
CToken cosmoL_scanToken(CLexState *state) {
CToken cosmoL_scanToken(CLexState *state)
{
skipWhitespace(state);
state->startChar = state->currentChar;
if (isEnd(state))
return makeToken(state, TOKEN_EOF);
char c = next(state);
switch (c) {
// single character tokens
case '(': return makeToken(state, TOKEN_LEFT_PAREN);
case ')': return makeToken(state, TOKEN_RIGHT_PAREN);
case '{': return makeToken(state, TOKEN_LEFT_BRACE);
case '}': return makeToken(state, TOKEN_RIGHT_BRACE);
case '[': return makeToken(state, TOKEN_LEFT_BRACKET);
case ']': return makeToken(state, TOKEN_RIGHT_BRACKET);
case ';': 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
case '+':
return match(state, '+') ? makeToken(state, TOKEN_PLUS_PLUS) : makeToken(state, TOKEN_PLUS);
case '-':
return match(state, '-') ? makeToken(state, TOKEN_MINUS_MINUS) : makeToken(state, TOKEN_MINUS);
case '.':
return match(state, '.') ? (match(state, '.') ? makeToken(state, TOKEN_DOT_DOT_DOT) : makeToken(state, TOKEN_DOT_DOT)) : makeToken(state, TOKEN_DOT);
case '!':
return match(state, '=') ? makeToken(state, TOKEN_BANG_EQUAL) : makeToken(state, TOKEN_BANG);
case '=':
return match(state, '=') ? makeToken(state, TOKEN_EQUAL_EQUAL) : makeToken(state, TOKEN_EQUAL);
case '>':
return match(state, '=') ? makeToken(state, TOKEN_GREATER_EQUAL) : makeToken(state, TOKEN_GREATER);
case '<':
return match(state, '=') ? makeToken(state, TOKEN_LESS_EQUAL) : makeToken(state, TOKEN_LESS);
// literals
case '"': return parseString(state);
default:
if (isNumerical(c))
return parseNumber(state);
if (isAlpha(c))
return parseIdentifier(state);
// single character tokens
case '(':
return makeToken(state, TOKEN_LEFT_PAREN);
case ')':
return makeToken(state, TOKEN_RIGHT_PAREN);
case '{':
return makeToken(state, TOKEN_LEFT_BRACE);
case '}':
return makeToken(state, TOKEN_RIGHT_BRACE);
case '[':
return makeToken(state, TOKEN_LEFT_BRACKET);
case ']':
return makeToken(state, TOKEN_RIGHT_BRACKET);
case ';':
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
case '+':
return match(state, '+') ? makeToken(state, TOKEN_PLUS_PLUS) : makeToken(state, TOKEN_PLUS);
case '-':
return match(state, '-') ? makeToken(state, TOKEN_MINUS_MINUS)
: makeToken(state, TOKEN_MINUS);
case '.':
return match(state, '.') ? (match(state, '.') ? makeToken(state, TOKEN_DOT_DOT_DOT)
: makeToken(state, TOKEN_DOT_DOT))
: makeToken(state, TOKEN_DOT);
case '!':
return match(state, '=') ? makeToken(state, TOKEN_BANG_EQUAL)
: makeToken(state, TOKEN_BANG);
case '=':
return match(state, '=') ? makeToken(state, TOKEN_EQUAL_EQUAL)
: makeToken(state, TOKEN_EQUAL);
case '>':
return match(state, '=') ? makeToken(state, TOKEN_GREATER_EQUAL)
: makeToken(state, TOKEN_GREATER);
case '<':
return match(state, '=') ? makeToken(state, TOKEN_LESS_EQUAL)
: makeToken(state, TOKEN_LESS);
// literals
case '"':
return parseString(state);
default:
if (isNumerical(c))
return parseNumber(state);
if (isAlpha(c))
return parseIdentifier(state);
}
return makeError(state, "Unknown symbol!");

View File

@ -3,7 +3,8 @@
#include "cosmo.h"
typedef enum {
typedef enum
{
// single character tokens
TOKEN_LEFT_PAREN,
TOKEN_RIGHT_PAREN,
@ -72,26 +73,30 @@ typedef enum {
TOKEN_EOF
} CTokenType;
typedef struct {
typedef struct
{
CTokenType type;
const char *word;
int len;
} CReservedWord;
typedef struct {
typedef struct
{
CTokenType type;
char *start;
int length;
int line;
} CToken;
typedef struct {
typedef struct
{
char *currentChar;
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 bufCap;
int line; // current line
size_t bufCap;
int line; // current line
int lastLine; // line of the previous consumed token
bool isEnd;
CTokenType lastType;

View File

@ -1,13 +1,15 @@
#include "cmem.h"
#include "cstate.h"
#include "cvalue.h"
#include "ctable.h"
#include "cparse.h"
#include "cobj.h"
#include "cbaselib.h"
#include "cobj.h"
#include "cparse.h"
#include "cstate.h"
#include "ctable.h"
#include "cvalue.h"
// 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;
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) {
cosmoM_collectGarbage(state);
}
#ifdef GC_DEBUG
# ifdef GC_DEBUG
else {
printf("GC event ignored! state frozen! [%d]\n", state->freezeGC);
}
#endif
# endif
#else
cosmoM_checkGarbage(state, 0);
#endif
@ -39,7 +41,8 @@ void *cosmoM_reallocate(CState* state, void *buf, size_t oldSize, size_t newSize
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) {
cosmoM_collectGarbage(state); // cya lol
return true;
@ -51,10 +54,11 @@ COSMO_API bool cosmoM_checkGarbage(CState *state, size_t needed) {
void markObject(CState *state, CObj *obj);
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
return;
int cap = tbl->capacityMask + 1;
for (int i = 0; i < cap; i++) {
CTableEntry *entry = &tbl->table[i];
@ -64,14 +68,17 @@ void markTable(CState *state, CTable *tbl) {
}
// 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
return;
int cap = tbl->capacityMask + 1;
for (int i = 0; i < cap; 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);
}
}
@ -79,7 +86,8 @@ void tableRemoveWhite(CState *state, CTable *tbl) {
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++) {
markValue(state, array->values[i]);
}
@ -87,72 +95,74 @@ void markArray(CState *state, CValueArray *array) {
// mark all references associated with the object
// black = keep, white = discard
void blackenObject(CState *state, CObj *obj) {
markObject(state, (CObj*)obj->proto);
void blackenObject(CState *state, CObj *obj)
{
markObject(state, (CObj *)obj->proto);
switch (obj->type) {
case COBJ_STRING:
case COBJ_CFUNCTION:
// stubbed
break;
case COBJ_OBJECT: {
// mark everything this object is keeping track of
CObjObject *cobj = (CObjObject*)obj;
markTable(state, &cobj->tbl);
break;
}
case COBJ_TABLE: { // tables are just wrappers for CTable
CObjTable *tbl = (CObjTable*)obj;
markTable(state, &tbl->tbl);
break;
}
case COBJ_UPVALUE: {
markValue(state, ((CObjUpval*)obj)->closed);
break;
}
case COBJ_FUNCTION: {
CObjFunction *func = (CObjFunction*)obj;
markObject(state, (CObj*)func->name);
markObject(state, (CObj*)func->module);
markArray(state, &func->chunk.constants);
case COBJ_STRING:
case COBJ_CFUNCTION:
// stubbed
break;
case COBJ_OBJECT: {
// mark everything this object is keeping track of
CObjObject *cobj = (CObjObject *)obj;
markTable(state, &cobj->tbl);
break;
}
case COBJ_TABLE: { // tables are just wrappers for CTable
CObjTable *tbl = (CObjTable *)obj;
markTable(state, &tbl->tbl);
break;
}
case COBJ_UPVALUE: {
markValue(state, ((CObjUpval *)obj)->closed);
break;
}
case COBJ_FUNCTION: {
CObjFunction *func = (CObjFunction *)obj;
markObject(state, (CObj *)func->name);
markObject(state, (CObj *)func->module);
markArray(state, &func->chunk.constants);
break;
}
case COBJ_METHOD: {
CObjMethod *method = (CObjMethod*)obj;
markValue(state, method->func);
markObject(state, (CObj*)method->obj);
break;
}
case COBJ_ERROR: {
CObjError *err = (CObjError*)obj;
markValue(state, err->err);
break;
}
case COBJ_METHOD: {
CObjMethod *method = (CObjMethod *)obj;
markValue(state, method->func);
markObject(state, (CObj *)method->obj);
break;
}
case COBJ_ERROR: {
CObjError *err = (CObjError *)obj;
markValue(state, err->err);
// mark callframes
for (int i = 0; i < err->frameCount; i++)
markObject(state, (CObj*)err->frames[i].closure);
// mark callframes
for (int i = 0; i < err->frameCount; i++)
markObject(state, (CObj *)err->frames[i].closure);
break;
break;
}
case COBJ_CLOSURE: {
CObjClosure *closure = (CObjClosure *)obj;
markObject(state, (CObj *)closure->function);
// mark all upvalues
for (int i = 0; i < closure->upvalueCount; i++) {
markObject(state, (CObj *)closure->upvalues[i]);
}
case COBJ_CLOSURE: {
CObjClosure *closure = (CObjClosure*)obj;
markObject(state, (CObj*)closure->function);
// mark all upvalues
for (int i = 0; i < closure->upvalueCount; i++) {
markObject(state, (CObj*)closure->upvalues[i]);
}
break;
}
default:
break;
}
default:
#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
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
return;
@ -165,33 +175,37 @@ void markObject(CState *state, CObj *obj) {
#endif
// they don't need to be added to the gray stack, they don't reference any other CObjs
if (obj->type == COBJ_CFUNCTION || obj->type == COBJ_STRING)
if (obj->type == COBJ_CFUNCTION || obj->type == COBJ_STRING)
return;
// 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;
}
void markValue(CState *state, CValue val) {
void markValue(CState *state, CValue val)
{
if (IS_REF(val))
markObject(state, cosmoV_readRef(val));
}
// trace our gray references
void traceGrays(CState *state) {
void traceGrays(CState *state)
{
while (state->grayStack.count > 0) {
CObj* obj = state->grayStack.array[--state->grayStack.count];
CObj *obj = state->grayStack.array[--state->grayStack.count];
blackenObject(state, obj);
}
}
void sweep(CState *state) {
void sweep(CState *state)
{
CObj *prev = NULL;
CObj *object = state->objects;
while (object != NULL) {
if (object->isMarked) { // skip over it
if (object->isMarked) { // skip over it
object->isMarked = false; // rest to white
prev = object;
object = object->next;
@ -210,7 +224,8 @@ void sweep(CState *state) {
}
}
void markUserRoots(CState *state) {
void markUserRoots(CState *state)
{
CObj *root = state->userRoots;
// 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
for (StkPtr value = state->stack; value < state->top; value++) {
markValue(state, *value);
@ -228,33 +244,34 @@ void markRoots(CState *state) {
// mark all active callframe closures
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
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
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
markUserRoots(state);
// mark other misc. internally reserved objects
markObject(state, (CObj*)state->error);
markObject(state, (CObj *)state->error);
for (int i = 0; i < COBJ_MAX; i++)
markObject(state, (CObj*)state->protoObjects[i]);
markObject(state, (CObj *)state->protoObjects[i]);
traceGrays(state);
}
COSMO_API void cosmoM_collectGarbage(CState *state) {
COSMO_API void cosmoM_collectGarbage(CState *state)
{
#ifdef GC_DEBUG
printf("-- GC start\n");
size_t start = state->allocatedBytes;
@ -263,41 +280,48 @@ COSMO_API void cosmoM_collectGarbage(CState *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
sweep(state);
// set our next GC event
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
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);
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);
getchar(); // pauses execution
#endif
}
COSMO_API void cosmoM_updateThreshhold(CState *state) {
COSMO_API void cosmoM_updateThreshhold(CState *state)
{
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
CObj *root = state->userRoots;
while (root != NULL) {
if (root == newRoot) // found in the list, abort
return;
root = root->nextRoot;
}
// adds root to userRoot linked list
newRoot->nextRoot = state->userRoots;
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 *root = state->userRoots;

View File

@ -2,69 +2,67 @@
#define CMEME_C // meme lol
#include "cosmo.h"
#include "cstate.h"
//#define GC_STRESS
//#define GC_DEBUG
// arrays *must* grow by a factor of 2
#define GROW_FACTOR 2
// #define GC_STRESS
// #define GC_DEBUG
// arrays *must* grow by a factor of 2
#define GROW_FACTOR 2
#define HEAP_GROW_FACTOR 2
#define ARRAY_START 8
#define ARRAY_START 8
#ifdef GC_DEBUG
#define cosmoM_freearray(state, type, buf, capacity) \
printf("freeing array %p [size %lu] at %s:%d\n", buf, sizeof(type) * capacity, __FILE__, __LINE__); \
cosmoM_reallocate(state, buf, sizeof(type) * capacity, 0)
# define cosmoM_freearray(state, type, buf, capacity) \
printf("freeing array %p [size %lu] at %s:%d\n", buf, sizeof(type) * capacity, __FILE__, \
__LINE__); \
cosmoM_reallocate(state, buf, sizeof(type) * capacity, 0)
#else
#define cosmoM_freearray(state, type, buf, capacity) \
cosmoM_reallocate(state, buf, sizeof(type) * capacity, 0)
# define cosmoM_freearray(state, type, buf, capacity) \
cosmoM_reallocate(state, buf, sizeof(type) * capacity, 0)
#endif
#define cosmoM_growarray(state, type, buf, count, capacity) \
if (count >= capacity || buf == NULL) { \
int old = capacity; \
capacity = old * GROW_FACTOR; \
buf = (type*)cosmoM_reallocate(state, buf, sizeof(type) *old, sizeof(type) *capacity); \
#define cosmoM_growarray(state, type, buf, count, capacity) \
if (count >= capacity || buf == NULL) { \
int old = capacity; \
capacity = old * GROW_FACTOR; \
buf = (type *)cosmoM_reallocate(state, buf, sizeof(type) * old, sizeof(type) * capacity); \
}
#ifdef GC_DEBUG
#define cosmoM_free(state, type, x) \
printf("freeing %p [size %lu] at %s:%d\n", x, sizeof(type), __FILE__, __LINE__); \
cosmoM_reallocate(state, x, sizeof(type), 0)
# define cosmoM_free(state, type, x) \
printf("freeing %p [size %lu] at %s:%d\n", x, sizeof(type), __FILE__, __LINE__); \
cosmoM_reallocate(state, x, sizeof(type), 0)
#else
#define cosmoM_free(state, type, x) \
cosmoM_reallocate(state, x, sizeof(type), 0)
# define cosmoM_free(state, type, x) cosmoM_reallocate(state, x, sizeof(type), 0)
#endif
#define cosmoM_isFrozen(state) \
(state->freezeGC > 0)
#define cosmoM_isFrozen(state) (state->freezeGC > 0)
// if debugging, print the locations of when the state is frozen/unfrozen
#ifdef GC_DEBUG
#define cosmoM_freezeGC(state) \
state->freezeGC++; \
printf("freezing state at %s:%d [%d]\n", __FILE__, __LINE__, state->freezeGC)
# define cosmoM_freezeGC(state) \
state->freezeGC++; \
printf("freezing state at %s:%d [%d]\n", __FILE__, __LINE__, state->freezeGC)
#define cosmoM_unfreezeGC(state) \
state->freezeGC--; \
printf("unfreezing state at %s:%d [%d]\n", __FILE__, __LINE__, state->freezeGC); \
cosmoM_checkGarbage(state, 0)
# define cosmoM_unfreezeGC(state) \
state->freezeGC--; \
printf("unfreezing state at %s:%d [%d]\n", __FILE__, __LINE__, state->freezeGC); \
cosmoM_checkGarbage(state, 0)
#else
// freeze's the garbage collector until cosmoM_unfreezeGC is called
#define cosmoM_freezeGC(state) \
state->freezeGC++
# define cosmoM_freezeGC(state) state->freezeGC++
// unfreeze's the garbage collector and tries to run a garbage collection cycle
#define cosmoM_unfreezeGC(state) \
state->freezeGC--; \
cosmoM_checkGarbage(state, 0)
# define cosmoM_unfreezeGC(state) \
state->freezeGC--; \
cosmoM_checkGarbage(state, 0)
#endif
#endif
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_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);
// 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);
}

File diff suppressed because it is too large Load Diff

View File

@ -3,7 +3,8 @@
#include "cosmo.h"
typedef enum CObjType {
typedef enum CObjType
{
COBJ_STRING,
COBJ_OBJECT,
COBJ_TABLE,
@ -18,52 +19,59 @@ typedef enum CObjType {
COBJ_MAX
} CObjType;
#include "cstate.h"
#include "cchunk.h"
#include "cvalue.h"
#include "cstate.h"
#include "ctable.h"
#include "cvalue.h"
#define CommonHeader CObj _obj
#define readFlag(x, flag) (x & (1u << flag))
#define setFlagOn(x, flag) (x |= (1u << flag))
#define CommonHeader CObj _obj
#define readFlag(x, flag) (x & (1u << flag))
#define setFlagOn(x, flag) (x |= (1u << flag))
typedef int (*CosmoCFunction)(CState *state, int argCount, CValue *args);
struct CObj {
struct CObj
{
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
CObjType type;
bool isMarked; // for the GC
};
struct CObjString {
CommonHeader; // "is a" CObj
char *str; // NULL termincated string
struct CObjString
{
CommonHeader; // "is a" CObj
char *str; // NULL termincated string
uint32_t hash; // for hashtable lookup
int length;
bool isIString;
};
struct CObjStream {
struct CObjStream
{
CommonHeader; // "is a" CObj
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
CValue err; // error string
CValue err; // error string
CCallFrame *frames;
int frameCount;
int line; // reserved for parser errors
int line; // reserved for parser errors
bool parserError; // if true, cosmoV_printError will format the error to the lexer
};
struct CObjObject {
struct CObjObject
{
CommonHeader; // "is a" CObj
CTable tbl;
cosmo_Flag istringFlags; // enables us to have a much faster lookup for reserved IStrings (like __init, __index, etc.)
union { // userdata (NULL by default)
cosmo_Flag istringFlags; // enables us to have a much faster lookup for reserved IStrings (like
// __init, __index, etc.)
union
{ // userdata (NULL by default)
void *userP;
int userI;
};
@ -71,12 +79,14 @@ struct CObjObject {
bool isLocked;
};
struct CObjTable { // table, a wrapper for CTable
struct CObjTable
{ // table, a wrapper for CTable
CommonHeader; // "is a" CObj
CTable tbl;
};
struct CObjFunction {
struct CObjFunction
{
CommonHeader; // "is a" CObj
CChunk chunk;
CObjString *name;
@ -86,25 +96,29 @@ struct CObjFunction {
bool variadic;
};
struct CObjCFunction {
struct CObjCFunction
{
CommonHeader; // "is a" CObj
CosmoCFunction cfunc;
};
struct CObjClosure {
struct CObjClosure
{
CommonHeader; // "is a" CObj
CObjFunction *function;
CObjUpval **upvalues;
int upvalueCount;
};
struct CObjMethod {
struct CObjMethod
{
CommonHeader; // "is a " CObj
CValue func;
CObj *obj; // obj this method is bound too
};
struct CObjUpval {
struct CObjUpval
{
CommonHeader; // "is a" CObj
CValue closed;
CValue *val;
@ -113,38 +127,40 @@ struct CObjUpval {
#undef CommonHeader
#define IS_STRING(x) isObjType(x, COBJ_STRING)
#define IS_OBJECT(x) isObjType(x, COBJ_OBJECT)
#define IS_STREAM(x) isObjType(x, COBJ_STREAM)
#define IS_TABLE(x) isObjType(x, COBJ_TABLE)
#define IS_FUNCTION(x) isObjType(x, COBJ_FUNCTION)
#define IS_CFUNCTION(x) isObjType(x, COBJ_CFUNCTION)
#define IS_METHOD(x) isObjType(x, COBJ_METHOD)
#define IS_CLOSURE(x) isObjType(x, COBJ_CLOSURE)
#define IS_STRING(x) isObjType(x, COBJ_STRING)
#define IS_OBJECT(x) isObjType(x, COBJ_OBJECT)
#define IS_STREAM(x) isObjType(x, COBJ_STREAM)
#define IS_TABLE(x) isObjType(x, COBJ_TABLE)
#define IS_FUNCTION(x) isObjType(x, COBJ_FUNCTION)
#define IS_CFUNCTION(x) isObjType(x, COBJ_CFUNCTION)
#define IS_METHOD(x) isObjType(x, COBJ_METHOD)
#define IS_CLOSURE(x) isObjType(x, COBJ_CLOSURE)
#define cosmoV_readString(x) ((CObjString*)cosmoV_readRef(x))
#define cosmoV_readCString(x) (((CObjString*)cosmoV_readRef(x))->str)
#define cosmoV_readFD(x) (((CObjStream*)cosmoV_readRef(x))->fd)
#define cosmoV_readObject(x) ((CObjObject*)cosmoV_readRef(x))
#define cosmoV_readTable(x) ((CObjTable*)cosmoV_readRef(x))
#define cosmoV_readFunction(x) ((CObjFunction*)cosmoV_readRef(x))
#define cosmoV_readCFunction(x) (((CObjCFunction*)cosmoV_readRef(x))->cfunc)
#define cosmoV_readMethod(x) ((CObjMethod*)cosmoV_readRef(x))
#define cosmoV_readClosure(x) ((CObjClosure*)cosmoV_readRef(x))
#define cosmoV_readString(x) ((CObjString *)cosmoV_readRef(x))
#define cosmoV_readCString(x) (((CObjString *)cosmoV_readRef(x))->str)
#define cosmoV_readFD(x) (((CObjStream *)cosmoV_readRef(x))->fd)
#define cosmoV_readObject(x) ((CObjObject *)cosmoV_readRef(x))
#define cosmoV_readTable(x) ((CObjTable *)cosmoV_readRef(x))
#define cosmoV_readFunction(x) ((CObjFunction *)cosmoV_readRef(x))
#define cosmoV_readCFunction(x) (((CObjCFunction *)cosmoV_readRef(x))->cfunc)
#define cosmoV_readMethod(x) ((CObjMethod *)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;
}
// 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);
}
}
void cosmoO_free(CState *state, CObj* obj);
bool cosmoO_equal(CState *state, CObj* obj1, CObj* obj2);
void cosmoO_free(CState *state, CObj *obj);
bool cosmoO_equal(CState *state, CObj *obj1, CObj *obj2);
// walks the protos of obj and checks for 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);
// grabs the base proto of the CObj* (if CObj is a CObjObject, that is returned)
static inline CObjObject *cosmoO_grabProto(CObj *obj) {
return obj->type == COBJ_OBJECT ? (CObjObject*)obj : obj->proto;
static inline CObjObject *cosmoO_grabProto(CObj *obj)
{
return obj->type == COBJ_OBJECT ? (CObjObject *)obj : obj->proto;
}
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_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);
// gets the user-defined pointer
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);
// gets the user-defined integer
int cosmoO_getUserI(CObjObject *object);
@ -189,10 +208,12 @@ void cosmoO_unlock(CObjObject *object);
// internal string
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);
// 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);
// 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);
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);
cosmo_Number cosmoO_toNumber(CState *state, CObj *obj);

View File

@ -3,24 +3,25 @@
#include "cosmo.h"
// instructions
// instructions
typedef enum {
typedef enum
{
// STACK/STATE MANIPULATION
OP_LOADCONST, // pushes const[uint8_t] to the stack
OP_SETGLOBAL, // pops and sets global[const[uint16_t]]
OP_GETGLOBAL, // pushes global[const[uint16_t]]
OP_SETLOCAL, // pops and sets base[uint8_t]
OP_GETLOCAL, // pushes base[uint8_t]
OP_GETUPVAL, // pushes closure->upvals[uint8_t]
OP_SETUPVAL, // pops and sets closure->upvals[uint8_t]
OP_PEJMP, // pops, if false jumps uint16_t
OP_EJMP, // if peek(0) is falsey jumps uint16_t
OP_JMP, // always jumps uint16_t
OP_JMPBACK, // jumps -uint16_t
OP_POP, // - pops[uint8_t] from stack
OP_CALL, // calls top[-uint8_t] expecting uint8_t results
OP_CLOSURE,
OP_SETLOCAL, // pops and sets base[uint8_t]
OP_GETLOCAL, // pushes base[uint8_t]
OP_GETUPVAL, // pushes closure->upvals[uint8_t]
OP_SETUPVAL, // pops and sets closure->upvals[uint8_t]
OP_PEJMP, // pops, if false jumps uint16_t
OP_EJMP, // if peek(0) is falsey jumps uint16_t
OP_JMP, // always jumps uint16_t
OP_JMPBACK, // jumps -uint16_t
OP_POP, // - pops[uint8_t] from stack
OP_CALL, // calls top[-uint8_t] expecting uint8_t results
OP_CLOSURE,
OP_CLOSE,
OP_NEWTABLE,
OP_NEWARRAY, // really just a table
@ -44,10 +45,10 @@ typedef enum {
OP_NOT,
OP_NEGATE,
OP_COUNT,
OP_CONCAT, // concats uint8_t vars on the stack
OP_INCLOCAL, // pushes old value to stack, adds (uint8_t-128) to local[uint8_t]
OP_CONCAT, // concats uint8_t vars on the stack
OP_INCLOCAL, // pushes old value to stack, adds (uint8_t-128) to local[uint8_t]
OP_INCGLOBAL, // pushes old value to stack, adds (uint8_t-128) to globals[const[uint16_t]]
OP_INCUPVAL, // pushes old value to stack, adds (uint8_t-128) to closure->upval[uint8_t]
OP_INCUPVAL, // pushes old value to stack, adds (uint8_t-128) to closure->upval[uint8_t]
OP_INCINDEX,
OP_INCOBJECT, // pushes old value to stack, adds (uint8_t-128) to obj[const[uint16_t]]

View File

@ -1,21 +1,21 @@
#ifndef COSMOMAIN_H
#define COSMOMAIN_H
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
/*
/*
SAFE_STACK:
if undefined, the stack will not be checked for stack overflows. This may improve 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.
if undefined, the stack will not be checked for stack overflows. This may improve
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 NAN_BOXXED
// #define NAN_BOXXED
// forward declare *most* stuff so our headers are cleaner
typedef struct CState CState;
@ -54,7 +54,6 @@ typedef uint8_t INSTRUCTION;
#define UNNAMEDCHUNK "_main"
#define COSMOASSERT(x) assert(x)
#define CERROR(err) \
printf("%s : %s\n", "[ERROR]", err)
#define CERROR(err) printf("%s : %s\n", "[ERROR]", err)
#endif

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -1,12 +1,14 @@
#include "cstate.h"
#include "cchunk.h"
#include "cmem.h"
#include "cobj.h"
#include "cvm.h"
#include "cmem.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
CState *state = malloc(sizeof(CState));
@ -33,7 +35,7 @@ CState *cosmoV_newState() {
state->openUpvalues = NULL;
state->error = NULL;
// set default proto objects
for (int i = 0; i < COBJ_MAX; i++)
state->protoObjects[i] = NULL;
@ -73,7 +75,8 @@ CState *cosmoV_newState() {
return state;
}
void cosmoV_freeState(CState *state) {
void cosmoV_freeState(CState *state)
{
#ifdef GC_DEBUG
printf("state %p is being free'd!\n", state);
#endif
@ -93,34 +96,36 @@ void cosmoV_freeState(CState *state) {
// free our string table (the string table includes the internal VM strings)
cosmoT_clearTable(state, &state->strings);
// 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
/*#ifdef GC_DEBUG
if (state->allocatedBytes != sizeof(CState)) {
printf("state->allocatedBytes doesn't match expected value (%lu), got %lu!", sizeof(CState), state->allocatedBytes);
exit(0);
}
#endif*/
/*#ifdef GC_DEBUG
if (state->allocatedBytes != sizeof(CState)) {
printf("state->allocatedBytes doesn't match expected value (%lu), got %lu!",
sizeof(CState), state->allocatedBytes); exit(0);
}
#endif*/
free(state);
}
// 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++) {
StkPtr key = cosmoV_getTop(state, 1);
StkPtr val = cosmoV_getTop(state, 0);
CValue *oldVal = cosmoT_insert(state, &state->globals->tbl, *key);
*oldVal = *val;
cosmoV_setTop(state, 2); // pops the 2 values off the stack
}
}
void cosmoV_printStack(CState *state) {
void cosmoV_printStack(CState *state)
{
printf("==== [[ stack dump ]] ====\n");
for (CValue *top = state->top - 1; top >= state->stack; top--) {
printf("%d: ", (int)(top - state->stack));

View File

@ -1,48 +1,55 @@
#ifndef CSTATE_H
#define CSTATE_H
#include "cosmo.h"
#include "cobj.h"
#include "cvalue.h"
#include "cosmo.h"
#include "ctable.h"
#include "cvalue.h"
struct CCallFrame {
struct CCallFrame
{
CObjClosure *closure;
INSTRUCTION *pc;
CValue* base;
CValue *base;
};
typedef enum IStringEnum {
ISTRING_INIT, // __init
ISTRING_TOSTRING, // __tostring
ISTRING_TONUMBER, // __tonumber
ISTRING_EQUAL, // __equals
ISTRING_INDEX, // __index
ISTRING_NEWINDEX, // __newindex
ISTRING_COUNT, // __count
ISTRING_GETTER, // __getter
ISTRING_SETTER, // __setter
ISTRING_ITER, // __iter
ISTRING_NEXT, // __next
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
typedef enum IStringEnum
{
ISTRING_INIT, // __init
ISTRING_TOSTRING, // __tostring
ISTRING_TONUMBER, // __tonumber
ISTRING_EQUAL, // __equals
ISTRING_INDEX, // __index
ISTRING_NEWINDEX, // __newindex
ISTRING_COUNT, // __count
ISTRING_GETTER, // __getter
ISTRING_SETTER, // __setter
ISTRING_ITER, // __iter
ISTRING_NEXT, // __next
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
} IStringEnum;
typedef struct ArrayCObj {
typedef struct ArrayCObj
{
CObj **array;
int count;
int capacity;
} ArrayCObj;
struct CState {
struct CState
{
bool panic;
int freezeGC; // when > 0, GC events will be ignored (for internal use)
int frameCount;
CObjError *error; // NULL, unless panic is true
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
ArrayCObj grayStack; // keeps track of which objects *haven't yet* been traversed in our GC, but *have been* found
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
ArrayCObj grayStack; // keeps track of which objects *haven't yet* been traversed in our GC, but
// *have been* found
size_t allocatedBytes;
size_t nextGC; // when allocatedBytes reaches this threshhold, trigger a GC event
@ -50,16 +57,17 @@ struct CState {
CTable strings;
CObjTable *globals;
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]
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
CValue stack[STACK_MAX]; // stack
CValue stack[STACK_MAX]; // stack
};
COSMO_API CState *cosmoV_newState();
// expects 2*pairs values on the stack, each pair should consist of 1 key and 1 value
COSMO_API void cosmoV_register(CState *state, int pairs);
COSMO_API void cosmoV_register(CState *state, int pairs);
COSMO_API void cosmoV_freeState(CState *state);
COSMO_API void cosmoV_printStack(CState *state);

View File

@ -1,21 +1,25 @@
#include "ctable.h"
#include "cmem.h"
#include "cvalue.h"
#include "cobj.h"
#include "cvalue.h"
#include <string.h>
#define MAX_TABLE_FILL 0.75
#define MAX_TABLE_FILL 0.75
// at 30% capacity with capacity > ARRAY_START, shrink the array
#define MIN_TABLE_CAPACITY ARRAY_START
// bit-twiddling hacks, gets the next power of 2
unsigned int nextPow2(unsigned int x) {
if (x <= ARRAY_START - 1) return ARRAY_START; // sanity check
unsigned int nextPow2(unsigned int x)
{
if (x <= ARRAY_START - 1)
return ARRAY_START; // sanity check
x--;
int power = 2;
while (x >>= 1) power <<= 1;
while (x >>= 1)
power <<= 1;
if (power < ARRAY_START)
return ARRAY_START;
@ -23,7 +27,8 @@ unsigned int nextPow2(unsigned int x) {
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
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;
for (int i = 0; i < cap; i++) {
CTableEntry *entry = &from->table[i];
@ -51,44 +57,50 @@ 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));
}
uint32_t getObjectHash(CObj *obj) {
switch(obj->type) {
case COBJ_STRING:
return ((CObjString*)obj)->hash;
default:
return (uint32_t)obj; // just "hash" the pointer
uint32_t getObjectHash(CObj *obj)
{
switch (obj->type) {
case COBJ_STRING:
return ((CObjString *)obj)->hash;
default:
return (uint32_t)obj; // just "hash" the pointer
}
}
uint32_t getValueHash(CValue *val) {
uint32_t getValueHash(CValue *val)
{
switch (GET_TYPE(*val)) {
case COSMO_TREF:
return getObjectHash(cosmoV_readRef(*val));
case COSMO_TNUMBER: {
uint32_t buf[sizeof(cosmo_Number)/sizeof(uint32_t)];
cosmo_Number num = cosmoV_readNumber(*val);
case COSMO_TREF:
return getObjectHash(cosmoV_readRef(*val));
case COSMO_TNUMBER: {
uint32_t buf[sizeof(cosmo_Number) / sizeof(uint32_t)];
cosmo_Number num = cosmoV_readNumber(*val);
if (num == 0)
return 0;
memcpy(buf, &num, sizeof(buf));
for (size_t i = 0; i < sizeof(cosmo_Number)/sizeof(uint32_t); i++) buf[0] += buf[i];
return buf[0];
}
// TODO: add support for other types
default:
if (num == 0)
return 0;
memcpy(buf, &num, sizeof(buf));
for (size_t i = 0; i < sizeof(cosmo_Number) / sizeof(uint32_t); i++)
buf[0] += buf[i];
return buf[0];
}
// TODO: add support for other types
default:
return 0;
}
}
// 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 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;
// keep looking for an open slot in the entries array
@ -112,17 +124,19 @@ 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))
return;
size_t size = sizeof(CTableEntry) * newCapacity;
int cachedCount = tbl->count;
int newCount, oldCap;
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;
CTableEntry *entries = cosmoM_xmalloc(state, size);
@ -157,10 +171,14 @@ static void resizeTbl(CState *state, CTable *tbl, int newCapacity, bool canShrin
tbl->tombstones = 0;
}
bool cosmoT_checkShrink(CState *state, CTable *tbl) {
// 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
resizeTbl(state, tbl, nextPow2(tbl->count - tbl->tombstones) * GROW_FACTOR, false); // shrink based on active entries to the next pow of 2
bool cosmoT_checkShrink(CState *state, CTable *tbl)
{
// 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
resizeTbl(state, tbl, nextPow2(tbl->count - tbl->tombstones) * GROW_FACTOR,
false); // shrink based on active entries to the next pow of 2
return true;
}
@ -168,7 +186,8 @@ bool cosmoT_checkShrink(CState *state, CTable *tbl) {
}
// 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
int cap = tbl->capacityMask + 1;
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
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->val)) // is it empty?
@ -191,22 +211,25 @@ COSMO_API CValue* cosmoT_insert(CState *state, CTable *tbl, CValue key) {
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
if (tbl->count == 0) {
*val = cosmoV_newNil();
return false;
}
CTableEntry *entry = findEntry(state, tbl->table, tbl->capacityMask, key);
*val = entry->val;
// return if get was successful
return !(IS_NIL(entry->key));
}
bool cosmoT_remove(CState* state, CTable *tbl, CValue key) {
if (tbl->count == 0) return 0; // sanity check
bool cosmoT_remove(CState *state, CTable *tbl, CValue key)
{
if (tbl->count == 0)
return 0; // sanity check
CTableEntry *entry = findEntry(state, tbl->table, tbl->capacityMask, key);
if (IS_NIL(entry->key)) // sanity check
@ -214,20 +237,26 @@ bool cosmoT_remove(CState* state, CTable *tbl, CValue key) {
// crafts tombstone
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++;
return true;
}
// returns the active entry count
COSMO_API int cosmoT_count(CTable *tbl) {
COSMO_API int cosmoT_count(CTable *tbl)
{
return tbl->count - tbl->tombstones;
}
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
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
// keep looking for an open slot in the entries array
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)
if (IS_NIL(entry->key) && IS_NIL(entry->val)) {
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!
return (CObjString*)cosmoV_readRef(entry->key);
return (CObjString *)cosmoV_readRef(entry->key);
}
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
void cosmoT_printTable(CTable *tbl, const char *name) {
void cosmoT_printTable(CTable *tbl, const char *name)
{
printf("==== [[%s]] ====\n", name);
int cap = tbl->capacityMask + 1;
for (int i = 0; i < cap; i++) {

View File

@ -1,17 +1,20 @@
#ifndef 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 "cvalue.h"
typedef struct CTableEntry {
typedef struct CTableEntry
{
CValue key;
CValue val;
} CTableEntry;
typedef struct CTable {
typedef struct CTable
{
int count;
int capacityMask; // +1 to get the capacity
int tombstones;

View File

@ -1,105 +1,123 @@
#include "cosmo.h"
#include "cmem.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->capacity = startCapacity;
val->values = NULL;
}
void cleanValArray(CState *state, CValueArray *array) {
void cleanValArray(CState *state, CValueArray *array)
{
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);
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?
return false;
// compare
switch (GET_TYPE(valA)) {
case COSMO_TBOOLEAN: return cosmoV_readBoolean(valA) == cosmoV_readBoolean(valB);
case COSMO_TNUMBER: 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:
return false;
case COSMO_TBOOLEAN:
return cosmoV_readBoolean(valA) == cosmoV_readBoolean(valB);
case COSMO_TNUMBER:
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:
return false;
}
}
CObjString *cosmoV_toString(CState *state, CValue val) {
CObjString *cosmoV_toString(CState *state, CValue val)
{
switch (GET_TYPE(val)) {
case COSMO_TNUMBER: {
char buf[32];
int size = snprintf((char*)&buf, 32, "%.14g", cosmoV_readNumber(val));
return cosmoO_copyString(state, (char*)&buf, size);
}
case COSMO_TBOOLEAN: {
return cosmoV_readBoolean(val) ? cosmoO_copyString(state, "true", 4) : cosmoO_copyString(state, "false", 5);
}
case COSMO_TREF: {
return cosmoO_toString(state, cosmoV_readRef(val));
}
case COSMO_TNIL: {
return cosmoO_copyString(state, "nil", 3);
}
default:
return cosmoO_copyString(state, "<unkn val>", 10);
case COSMO_TNUMBER: {
char buf[32];
int size = snprintf((char *)&buf, 32, "%.14g", cosmoV_readNumber(val));
return cosmoO_copyString(state, (char *)&buf, size);
}
case COSMO_TBOOLEAN: {
return cosmoV_readBoolean(val) ? cosmoO_copyString(state, "true", 4)
: cosmoO_copyString(state, "false", 5);
}
case COSMO_TREF: {
return cosmoO_toString(state, cosmoV_readRef(val));
}
case COSMO_TNIL: {
return cosmoO_copyString(state, "nil", 3);
}
default:
return cosmoO_copyString(state, "<unkn val>", 10);
}
}
cosmo_Number cosmoV_toNumber(CState *state, CValue val) {
switch(GET_TYPE(val)) {
case COSMO_TNUMBER: {
return cosmoV_readNumber(val);
}
case COSMO_TBOOLEAN: {
return cosmoV_readBoolean(val) ? 1 : 0;
}
case COSMO_TREF: {
return cosmoO_toNumber(state, cosmoV_readRef(val));
}
case COSMO_TNIL: // fall through
default:
return 0;
}
}
const char *cosmoV_typeStr(CValue val) {
cosmo_Number cosmoV_toNumber(CState *state, CValue val)
{
switch (GET_TYPE(val)) {
case COSMO_TNIL: return "<nil>";
case COSMO_TBOOLEAN: return "<bool>";
case COSMO_TNUMBER: return "<number>";
case COSMO_TREF: return cosmoO_typeStr(cosmoV_readRef(val));
default:
return "<unkn val>";
case COSMO_TNUMBER: {
return cosmoV_readNumber(val);
}
case COSMO_TBOOLEAN: {
return cosmoV_readBoolean(val) ? 1 : 0;
}
case COSMO_TREF: {
return cosmoO_toNumber(state, cosmoV_readRef(val));
}
case COSMO_TNIL: // fall through
default:
return 0;
}
}
void printValue(CValue val) {
const char *cosmoV_typeStr(CValue val)
{
switch (GET_TYPE(val)) {
case COSMO_TNUMBER:
printf("%g", cosmoV_readNumber(val));
break;
case COSMO_TBOOLEAN:
printf(cosmoV_readBoolean(val) ? "true" : "false");
break;
case COSMO_TREF: {
printObject(cosmoV_readRef(val));
break;
}
case COSMO_TNIL:
printf("nil");
break;
default:
printf("<unkn val>");
case COSMO_TNIL:
return "<nil>";
case COSMO_TBOOLEAN:
return "<bool>";
case COSMO_TNUMBER:
return "<number>";
case COSMO_TREF:
return cosmoO_typeStr(cosmoV_readRef(val));
default:
return "<unkn val>";
}
}
void printValue(CValue val)
{
switch (GET_TYPE(val)) {
case COSMO_TNUMBER:
printf("%g", cosmoV_readNumber(val));
break;
case COSMO_TBOOLEAN:
printf(cosmoV_readBoolean(val) ? "true" : "false");
break;
case COSMO_TREF: {
printObject(cosmoV_readRef(val));
break;
}
case COSMO_TNIL:
printf("nil");
break;
default:
printf("<unkn val>");
}
}

View File

@ -3,7 +3,8 @@
#include "cosmo.h"
typedef enum {
typedef enum
{
COSMO_TNUMBER, // number has to be 0 because NaN box
COSMO_TBOOLEAN,
COSMO_TREF,
@ -18,8 +19,8 @@ typedef double cosmo_Number;
#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
two articles:
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 two articles:
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.
*/
union CValue {
union CValue
{
uint64_t data;
cosmo_Number num;
};
#define MASK_TYPE ((uint64_t)0x0007000000000000)
#define MASK_PAYLOAD ((uint64_t)0x0000ffffffffffff)
# define MASK_TYPE ((uint64_t)0x0007000000000000)
# define MASK_PAYLOAD ((uint64_t)0x0000ffffffffffff)
// 3 bits (low bits) are reserved for the type
#define MAKE_PAYLOAD(x) ((uint64_t)(x) & MASK_PAYLOAD)
#define READ_PAYLOAD(x) ((x).data & MASK_PAYLOAD)
# define MAKE_PAYLOAD(x) ((uint64_t)(x)&MASK_PAYLOAD)
# define READ_PAYLOAD(x) ((x).data & MASK_PAYLOAD)
// 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) \
((((x).data & MASK_QUIETNAN) == MASK_QUIETNAN) ? (((x).data & MASK_TYPE) >> 48) : COSMO_TNUMBER)
# define GET_TYPE(x) \
((((x).data & MASK_QUIETNAN) == MASK_QUIETNAN) ? (((x).data & MASK_TYPE) >> 48) \
: COSMO_TNUMBER)
#define SIG_MASK (MASK_QUIETNAN | MASK_TYPE)
#define BOOL_SIG (MASK_QUIETNAN | ((uint64_t)(COSMO_TBOOLEAN) << 48))
#define OBJ_SIG (MASK_QUIETNAN | ((uint64_t)(COSMO_TREF) << 48))
#define NIL_SIG (MASK_QUIETNAN | ((uint64_t)(COSMO_TNIL) << 48))
# define SIG_MASK (MASK_QUIETNAN | MASK_TYPE)
# define BOOL_SIG (MASK_QUIETNAN | ((uint64_t)(COSMO_TBOOLEAN) << 48))
# define OBJ_SIG (MASK_QUIETNAN | ((uint64_t)(COSMO_TREF) << 48))
# define NIL_SIG (MASK_QUIETNAN | ((uint64_t)(COSMO_TNIL) << 48))
#define cosmoV_newNumber(x) ((CValue){.num = x})
#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_newNil() ((CValue){.data = NIL_SIG})
# define cosmoV_newNumber(x) ((CValue){.num = x})
# 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_newNil() ((CValue){.data = NIL_SIG})
#define cosmoV_readNumber(x) ((x).num)
#define cosmoV_readBoolean(x) ((bool)READ_PAYLOAD(x))
#define cosmoV_readRef(x) ((CObj*)READ_PAYLOAD(x))
# define cosmoV_readNumber(x) ((x).num)
# define cosmoV_readBoolean(x) ((bool)READ_PAYLOAD(x))
# define cosmoV_readRef(x) ((CObj *)READ_PAYLOAD(x))
#define IS_NUMBER(x) (((x).data & MASK_QUIETNAN) != MASK_QUIETNAN)
#define IS_BOOLEAN(x) (((x).data & SIG_MASK) == BOOL_SIG)
#define IS_NIL(x) (((x).data & SIG_MASK) == NIL_SIG)
#define IS_REF(x) (((x).data & SIG_MASK) == OBJ_SIG)
# define IS_NUMBER(x) (((x).data & MASK_QUIETNAN) != MASK_QUIETNAN)
# define IS_BOOLEAN(x) (((x).data & SIG_MASK) == BOOL_SIG)
# define IS_NIL(x) (((x).data & SIG_MASK) == NIL_SIG)
# define IS_REF(x) (((x).data & SIG_MASK) == OBJ_SIG)
#else
/*
Tagged union, this is the best platform independent solution
*/
struct CValue {
struct CValue
{
CosmoType type;
union {
union
{
cosmo_Number num;
bool b; // boolean
CObj *obj;
} val;
};
#define GET_TYPE(x) ((x).type)
# define GET_TYPE(x) ((x).type)
// create CValues
#define cosmoV_newNumber(x) ((CValue){COSMO_TNUMBER, {.num = (x)}})
#define cosmoV_newBoolean(x) ((CValue){COSMO_TBOOLEAN, {.b = (x)}})
#define cosmoV_newRef(x) ((CValue){COSMO_TREF, {.obj = (CObj*)(x)}})
#define cosmoV_newNil() ((CValue){COSMO_TNIL, {.num = 0}})
# define cosmoV_newNumber(x) ((CValue){COSMO_TNUMBER, {.num = (x)}})
# define cosmoV_newBoolean(x) ((CValue){COSMO_TBOOLEAN, {.b = (x)}})
# define cosmoV_newRef(x) ((CValue){COSMO_TREF, {.obj = (CObj *)(x)}})
# define cosmoV_newNil() ((CValue){COSMO_TNIL, {.num = 0}})
// read CValues
#define cosmoV_readNumber(x) ((cosmo_Number)(x).val.num)
#define cosmoV_readBoolean(x) ((bool)(x).val.b)
# define cosmoV_readNumber(x) ((cosmo_Number)(x).val.num)
# define cosmoV_readBoolean(x) ((bool)(x).val.b)
// 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_BOOLEAN(x) (GET_TYPE(x) == COSMO_TBOOLEAN)
#define IS_NIL(x) (GET_TYPE(x) == COSMO_TNIL)
#define IS_REF(x) (GET_TYPE(x) == COSMO_TREF)
# define IS_NUMBER(x) (GET_TYPE(x) == COSMO_TNUMBER)
# define IS_BOOLEAN(x) (GET_TYPE(x) == COSMO_TBOOLEAN)
# define IS_NIL(x) (GET_TYPE(x) == COSMO_TNIL)
# define IS_REF(x) (GET_TYPE(x) == COSMO_TREF)
#endif
typedef CValue* StkPtr;
typedef CValue *StkPtr;
struct CValueArray {
struct CValueArray
{
size_t capacity;
size_t count;
CValue *values;
@ -117,6 +123,7 @@ void printValue(CValue val);
COSMO_API bool cosmoV_equal(CState *state, CValue valA, CValue valB);
COSMO_API CObjString *cosmoV_toString(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

1488
src/cvm.c

File diff suppressed because it is too large Load Diff

View File

@ -1,14 +1,15 @@
#ifndef COSMOVM_H
#define COSMOVM_H
#include <string.h>
#include "cosmo.h"
#include "cstate.h"
//#define VM_DEBUG
#include <string.h>
typedef enum {
// #define VM_DEBUG
typedef enum
{
COSMOVM_OK,
COSMOVM_RUNTIME_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);
// 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_concat(CState *state, int vals);
COSMO_API void cosmoV_pushFString(CState *state, const char *format, ...);
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 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
for the passed objType if that CObj* has no proto.
Sets the default proto objects for the passed objType. Also walks through the object heap and
updates protos for the passed objType if that CObj* has no proto.
returns true if replacing a previously registered proto object for this type
*/
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:
false : <error> is at the top of the stack
@ -46,30 +48,34 @@ COSMO_API bool cosmoV_registerProtoObject(CState *state, CObjType objType, CObjO
COSMO_API bool cosmoV_compileString(CState *state, const char *src, const char *name);
/*
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
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
*/
COSMO_API bool cosmoV_get(CState *state);
/*
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
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
*/
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);
// clears the stack, callstack and restores the state into a usable state after a calloverflow or another hard to recover error
// (keeps the global table intact)
// clears the stack, callstack and restores the state into a usable state after a calloverflow or
// another hard to recover error (keeps the global table intact)
COSMO_API bool cosmoV_restore(CState *state);
// 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)
static inline void cosmoV_pushValue(CState *state, CValue val) {
// pushes a raw CValue to the stack, might throw an error if the stack is overflowed (with the
// SAFE_STACK macro on)
static inline void cosmoV_pushValue(CState *state, CValue val)
{
#ifdef SAFE_STACK
ptrdiff_t stackSize = state->top - state->stack;
@ -78,7 +84,7 @@ static inline void cosmoV_pushValue(CState *state, CValue val) {
if (state->panic) { // we're in a panic state, let the 8 reserved slots be filled
if (stackSize < STACK_MAX)
*(state->top++) = val;
return;
}
@ -91,51 +97,61 @@ static inline void cosmoV_pushValue(CState *state, CValue val) {
}
// 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;
return state->top;
}
// 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)];
}
// 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);
}
// 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));
}
// 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));
}
static inline void cosmoV_pushRef(CState *state, CObj *obj) {
static inline void cosmoV_pushRef(CState *state, CObj *obj)
{
cosmoV_pushValue(state, cosmoV_newRef(obj));
}
// pushes a C Function to the stack
static inline void cosmoV_pushCFunction(CState *state, CosmoCFunction func) {
cosmoV_pushRef(state, (CObj*)cosmoO_newCFunction(state, func));
static inline void cosmoV_pushCFunction(CState *state, CosmoCFunction func)
{
cosmoV_pushRef(state, (CObj *)cosmoO_newCFunction(state, func));
}
// len is the length of the string without the NULL terminator
static inline void cosmoV_pushLString(CState *state, const char *str, size_t len) {
cosmoV_pushRef(state, (CObj*)cosmoO_copyString(state, str, len));
static inline void cosmoV_pushLString(CState *state, const char *str, size_t len)
{
cosmoV_pushRef(state, (CObj *)cosmoO_copyString(state, str, len));
}
// 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));
}
static inline void cosmoV_pushNil(CState *state) {
static inline void cosmoV_pushNil(CState *state)
{
cosmoV_pushValue(state, cosmoV_newNil());
}