mirror of
https://github.com/CPunch/Cosmo.git
synced 2024-12-22 14:30:05 +00:00
added clang-format
This commit is contained in:
parent
517b0b9532
commit
7279623e24
26
.clang-format
Normal file
26
.clang-format
Normal 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
|
||||
...
|
||||
|
@ -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
|
339
src/cbaselib.c
339
src/cbaselib.c
@ -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");
|
||||
|
||||
|
@ -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
|
||||
|
33
src/cchunk.c
33
src/cchunk.c
@ -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++) {
|
||||
|
34
src/cchunk.h
34
src/cchunk.h
@ -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
|
||||
|
289
src/cdebug.c
289
src/cdebug.c
@ -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;
|
||||
}
|
||||
|
480
src/clex.c
480
src/clex.c
@ -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!");
|
||||
|
19
src/clex.h
19
src/clex.h
@ -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;
|
||||
|
214
src/cmem.c
214
src/cmem.c
@ -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;
|
||||
|
||||
|
75
src/cmem.h
75
src/cmem.h
@ -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);
|
||||
}
|
||||
|
||||
|
739
src/cobj.c
739
src/cobj.c
File diff suppressed because it is too large
Load Diff
129
src/cobj.h
129
src/cobj.h
@ -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);
|
||||
|
@ -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]]
|
||||
|
||||
|
19
src/cosmo.h
19
src/cosmo.h
@ -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
|
||||
|
684
src/cparse.c
684
src/cparse.c
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
|
35
src/cstate.c
35
src/cstate.c
@ -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));
|
||||
|
62
src/cstate.h
62
src/cstate.h
@ -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);
|
||||
|
||||
|
137
src/ctable.c
137
src/ctable.c
@ -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++) {
|
||||
|
@ -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;
|
||||
|
160
src/cvalue.c
160
src/cvalue.c
@ -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>");
|
||||
}
|
||||
}
|
||||
|
93
src/cvalue.h
93
src/cvalue.h
@ -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
|
||||
|
82
src/cvm.h
82
src/cvm.h
@ -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());
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user