mirror of
https://github.com/CPunch/Cosmo.git
synced 2024-11-05 08:10: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
|
end
|
||||||
|
|
||||||
// stressing the GC
|
// stressing the GC
|
||||||
for (var i = 0; ; i++) do
|
for (var i = 0; i < 100000; i++) do
|
||||||
var x = Test("Hello world " .. i)
|
var x = Test("Hello world " .. i)
|
||||||
x:print()
|
x:print()
|
||||||
end
|
end
|
339
src/cbaselib.c
339
src/cbaselib.c
@ -1,15 +1,18 @@
|
|||||||
#include "cbaselib.h"
|
#include "cbaselib.h"
|
||||||
#include "cvm.h"
|
|
||||||
#include "cvalue.h"
|
|
||||||
#include "cobj.h"
|
|
||||||
#include "cmem.h"
|
#include "cmem.h"
|
||||||
|
#include "cobj.h"
|
||||||
|
#include "cvalue.h"
|
||||||
|
#include "cvm.h"
|
||||||
|
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
|
|
||||||
// ================================================================ [BASELIB] ================================================================
|
// ================================================================ [BASELIB]
|
||||||
|
// ================================================================
|
||||||
|
|
||||||
int cosmoB_print(CState *state, int nargs, CValue *args) {
|
int cosmoB_print(CState *state, int nargs, CValue *args)
|
||||||
|
{
|
||||||
for (int i = 0; i < nargs; i++) {
|
for (int i = 0; i < nargs; i++) {
|
||||||
if (IS_REF(args[i])) { // if its a CObj*, generate the CObjString
|
if (IS_REF(args[i])) { // if its a CObj*, generate the CObjString
|
||||||
CObjString *str = cosmoV_toString(state, args[i]);
|
CObjString *str = cosmoV_toString(state, args[i]);
|
||||||
@ -23,7 +26,8 @@ int cosmoB_print(CState *state, int nargs, CValue *args) {
|
|||||||
return 0; // print doesn't return any args
|
return 0; // print doesn't return any args
|
||||||
}
|
}
|
||||||
|
|
||||||
int cosmoB_assert(CState *state, int nargs, CValue *args) {
|
int cosmoB_assert(CState *state, int nargs, CValue *args)
|
||||||
|
{
|
||||||
if (nargs < 1 || nargs > 2) {
|
if (nargs < 1 || nargs > 2) {
|
||||||
cosmoV_error(state, "assert() expected 1 or 2 arguments, got %d!", nargs);
|
cosmoV_error(state, "assert() expected 1 or 2 arguments, got %d!", nargs);
|
||||||
return 0; // nothing pushed onto the stack to return
|
return 0; // nothing pushed onto the stack to return
|
||||||
@ -31,7 +35,8 @@ int cosmoB_assert(CState *state, int nargs, CValue *args) {
|
|||||||
|
|
||||||
if (!IS_BOOLEAN(args[0]) || (nargs == 2 && !IS_STRING(args[1]))) {
|
if (!IS_BOOLEAN(args[0]) || (nargs == 2 && !IS_STRING(args[1]))) {
|
||||||
if (nargs == 2) {
|
if (nargs == 2) {
|
||||||
cosmoV_typeError(state, "assert()", "<boolean>, <string>", "%s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
|
cosmoV_typeError(state, "assert()", "<boolean>, <string>", "%s, %s",
|
||||||
|
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
|
||||||
} else {
|
} else {
|
||||||
cosmoV_typeError(state, "assert()", "<boolean>", "%s", cosmoV_typeStr(args[0]));
|
cosmoV_typeError(state, "assert()", "<boolean>", "%s", cosmoV_typeStr(args[0]));
|
||||||
}
|
}
|
||||||
@ -44,7 +49,8 @@ int cosmoB_assert(CState *state, int nargs, CValue *args) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int cosmoB_type(CState *state, int nargs, CValue *args) {
|
int cosmoB_type(CState *state, int nargs, CValue *args)
|
||||||
|
{
|
||||||
if (nargs != 1) {
|
if (nargs != 1) {
|
||||||
cosmoV_error(state, "type() expected 1 argument, got %d!", nargs);
|
cosmoV_error(state, "type() expected 1 argument, got %d!", nargs);
|
||||||
return 0;
|
return 0;
|
||||||
@ -55,7 +61,8 @@ int cosmoB_type(CState *state, int nargs, CValue *args) {
|
|||||||
return 1; // 1 return value, the type string :D
|
return 1; // 1 return value, the type string :D
|
||||||
}
|
}
|
||||||
|
|
||||||
int cosmoB_pcall(CState *state, int nargs, CValue *args) {
|
int cosmoB_pcall(CState *state, int nargs, CValue *args)
|
||||||
|
{
|
||||||
if (nargs < 1) {
|
if (nargs < 1) {
|
||||||
cosmoV_error(state, "pcall() expected at least 1 argument!");
|
cosmoV_error(state, "pcall() expected at least 1 argument!");
|
||||||
return 0;
|
return 0;
|
||||||
@ -65,7 +72,7 @@ int cosmoB_pcall(CState *state, int nargs, CValue *args) {
|
|||||||
cosmoM_unfreezeGC(state);
|
cosmoM_unfreezeGC(state);
|
||||||
|
|
||||||
// call the passed callable
|
// call the passed callable
|
||||||
COSMOVMRESULT res = cosmoV_pcall(state, nargs-1, 1);
|
COSMOVMRESULT res = cosmoV_pcall(state, nargs - 1, 1);
|
||||||
|
|
||||||
// insert false before the result
|
// insert false before the result
|
||||||
cosmo_insert(state, 0, cosmoV_newBoolean(res == COSMOVM_OK));
|
cosmo_insert(state, 0, cosmoV_newBoolean(res == COSMOVM_OK));
|
||||||
@ -75,7 +82,8 @@ int cosmoB_pcall(CState *state, int nargs, CValue *args) {
|
|||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
int cosmoB_tonumber(CState *state, int nargs, CValue *args) {
|
int cosmoB_tonumber(CState *state, int nargs, CValue *args)
|
||||||
|
{
|
||||||
if (nargs != 1) {
|
if (nargs != 1) {
|
||||||
cosmoV_error(state, "tonumber() expected 1 argument, got %d!", nargs);
|
cosmoV_error(state, "tonumber() expected 1 argument, got %d!", nargs);
|
||||||
return 0;
|
return 0;
|
||||||
@ -85,17 +93,19 @@ int cosmoB_tonumber(CState *state, int nargs, CValue *args) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int cosmoB_tostring(CState *state, int nargs, CValue *args) {
|
int cosmoB_tostring(CState *state, int nargs, CValue *args)
|
||||||
|
{
|
||||||
if (nargs != 1) {
|
if (nargs != 1) {
|
||||||
cosmoV_error(state, "tostring() expected 1 argument, got %d!", nargs);
|
cosmoV_error(state, "tostring() expected 1 argument, got %d!", nargs);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
cosmoV_pushRef(state, (CObj*)cosmoV_toString(state, args[0]));
|
cosmoV_pushRef(state, (CObj *)cosmoV_toString(state, args[0]));
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int cosmoB_loadstring(CState *state, int nargs, CValue *args) {
|
int cosmoB_loadstring(CState *state, int nargs, CValue *args)
|
||||||
|
{
|
||||||
if (nargs != 1) {
|
if (nargs != 1) {
|
||||||
cosmoV_error(state, "loadstring() expected 1 argument, got %d!", nargs);
|
cosmoV_error(state, "loadstring() expected 1 argument, got %d!", nargs);
|
||||||
return 0;
|
return 0;
|
||||||
@ -113,7 +123,8 @@ int cosmoB_loadstring(CState *state, int nargs, CValue *args) {
|
|||||||
return 2; // <boolean>, <closure> or <error>
|
return 2; // <boolean>, <closure> or <error>
|
||||||
}
|
}
|
||||||
|
|
||||||
int cosmoB_error(CState *state, int nargs, CValue *args) {
|
int cosmoB_error(CState *state, int nargs, CValue *args)
|
||||||
|
{
|
||||||
if (nargs != 1) {
|
if (nargs != 1) {
|
||||||
cosmoV_error(state, "error() expected 1 argument, got %d!", nargs);
|
cosmoV_error(state, "error() expected 1 argument, got %d!", nargs);
|
||||||
return 0;
|
return 0;
|
||||||
@ -129,31 +140,16 @@ int cosmoB_error(CState *state, int nargs, CValue *args) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void cosmoB_loadLibrary(CState *state) {
|
void cosmoB_loadLibrary(CState *state)
|
||||||
const char *identifiers[] = {
|
{
|
||||||
"print",
|
const char *identifiers[] = {"print", "assert", "type", "pcall",
|
||||||
"assert",
|
"tonumber", "tostring", "loadstring", "error"};
|
||||||
"type",
|
|
||||||
"pcall",
|
|
||||||
"tonumber",
|
|
||||||
"tostring",
|
|
||||||
"loadstring",
|
|
||||||
"error"
|
|
||||||
};
|
|
||||||
|
|
||||||
CosmoCFunction baseLib[] = {
|
CosmoCFunction baseLib[] = {cosmoB_print, cosmoB_assert, cosmoB_type, cosmoB_pcall,
|
||||||
cosmoB_print,
|
cosmoB_tonumber, cosmoB_tostring, cosmoB_loadstring, cosmoB_error};
|
||||||
cosmoB_assert,
|
|
||||||
cosmoB_type,
|
|
||||||
cosmoB_pcall,
|
|
||||||
cosmoB_tonumber,
|
|
||||||
cosmoB_tostring,
|
|
||||||
cosmoB_loadstring,
|
|
||||||
cosmoB_error
|
|
||||||
};
|
|
||||||
|
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i < sizeof(identifiers)/sizeof(identifiers[0]); i++) {
|
for (i = 0; i < sizeof(identifiers) / sizeof(identifiers[0]); i++) {
|
||||||
cosmoV_pushString(state, identifiers[i]);
|
cosmoV_pushString(state, identifiers[i]);
|
||||||
cosmoV_pushCFunction(state, baseLib[i]);
|
cosmoV_pushCFunction(state, baseLib[i]);
|
||||||
}
|
}
|
||||||
@ -167,9 +163,11 @@ void cosmoB_loadLibrary(CState *state) {
|
|||||||
cosmoB_loadMathLib(state);
|
cosmoB_loadMathLib(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ================================================================ [OBJECT.*] ================================================================
|
// ================================================================ [OBJECT.*]
|
||||||
|
// ================================================================
|
||||||
|
|
||||||
int cosmoB_osetProto(CState *state, int nargs, CValue *args) {
|
int cosmoB_osetProto(CState *state, int nargs, CValue *args)
|
||||||
|
{
|
||||||
if (nargs == 2) {
|
if (nargs == 2) {
|
||||||
CObj *obj = cosmoV_readRef(args[0]); // object to set proto too
|
CObj *obj = cosmoV_readRef(args[0]); // object to set proto too
|
||||||
CObjObject *proto = cosmoV_readObject(args[1]);
|
CObjObject *proto = cosmoV_readObject(args[1]);
|
||||||
@ -182,25 +180,28 @@ int cosmoB_osetProto(CState *state, int nargs, CValue *args) {
|
|||||||
return 0; // nothing
|
return 0; // nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
int cosmoB_ogetProto(CState *state, int nargs, CValue *args) {
|
int cosmoB_ogetProto(CState *state, int nargs, CValue *args)
|
||||||
|
{
|
||||||
if (nargs != 1) {
|
if (nargs != 1) {
|
||||||
cosmoV_error(state, "Expected 1 argument, got %d!", nargs);
|
cosmoV_error(state, "Expected 1 argument, got %d!", nargs);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
cosmoV_pushRef(state, (CObj*)cosmoV_readObject(args[0])->_obj.proto); // just return the proto
|
cosmoV_pushRef(state, (CObj *)cosmoV_readObject(args[0])->_obj.proto); // just return the proto
|
||||||
|
|
||||||
return 1; // 1 result
|
return 1; // 1 result
|
||||||
}
|
}
|
||||||
|
|
||||||
int cosmoB_oisChild(CState *state, int nargs, CValue *args) {
|
int cosmoB_oisChild(CState *state, int nargs, CValue *args)
|
||||||
|
{
|
||||||
if (nargs != 2) {
|
if (nargs != 2) {
|
||||||
cosmoV_error(state, "object.ischild() expected 2 arguments, got %d!", nargs);
|
cosmoV_error(state, "object.ischild() expected 2 arguments, got %d!", nargs);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!IS_REF(args[0]) || !IS_OBJECT(args[1])) {
|
if (!IS_REF(args[0]) || !IS_OBJECT(args[1])) {
|
||||||
cosmoV_typeError(state, "object.ischild()", "<reference obj>, <object>", "%s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
|
cosmoV_typeError(state, "object.ischild()", "<reference obj>, <object>", "%s, %s",
|
||||||
|
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -212,14 +213,11 @@ int cosmoB_oisChild(CState *state, int nargs, CValue *args) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
COSMO_API void cosmoB_loadObjLib(CState *state) {
|
COSMO_API void cosmoB_loadObjLib(CState *state)
|
||||||
const char *identifiers[] = {
|
{
|
||||||
"ischild"
|
const char *identifiers[] = {"ischild"};
|
||||||
};
|
|
||||||
|
|
||||||
CosmoCFunction objLib[] = {
|
CosmoCFunction objLib[] = {cosmoB_oisChild};
|
||||||
cosmoB_oisChild
|
|
||||||
};
|
|
||||||
|
|
||||||
// make object library object
|
// make object library object
|
||||||
cosmoV_pushString(state, "object");
|
cosmoV_pushString(state, "object");
|
||||||
@ -228,7 +226,7 @@ COSMO_API void cosmoB_loadObjLib(CState *state) {
|
|||||||
cosmoV_pushString(state, "__getter");
|
cosmoV_pushString(state, "__getter");
|
||||||
|
|
||||||
// key & value pair
|
// key & value pair
|
||||||
cosmoV_pushString(state, "__proto"); // key
|
cosmoV_pushString(state, "__proto"); // key
|
||||||
cosmoV_pushCFunction(state, cosmoB_ogetProto); // value
|
cosmoV_pushCFunction(state, cosmoB_ogetProto); // value
|
||||||
|
|
||||||
cosmoV_makeTable(state, 1);
|
cosmoV_makeTable(state, 1);
|
||||||
@ -242,7 +240,7 @@ COSMO_API void cosmoB_loadObjLib(CState *state) {
|
|||||||
cosmoV_makeTable(state, 1);
|
cosmoV_makeTable(state, 1);
|
||||||
|
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i < sizeof(identifiers)/sizeof(identifiers[0]); i++) {
|
for (i = 0; i < sizeof(identifiers) / sizeof(identifiers[0]); i++) {
|
||||||
cosmoV_pushString(state, identifiers[i]);
|
cosmoV_pushString(state, identifiers[i]);
|
||||||
cosmoV_pushCFunction(state, objLib[i]);
|
cosmoV_pushCFunction(state, objLib[i]);
|
||||||
}
|
}
|
||||||
@ -256,10 +254,12 @@ COSMO_API void cosmoB_loadObjLib(CState *state) {
|
|||||||
cosmoV_register(state, 1);
|
cosmoV_register(state, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ================================================================ [OS.*] ================================================================
|
// ================================================================ [OS.*]
|
||||||
|
// ================================================================
|
||||||
|
|
||||||
// os.read()
|
// os.read()
|
||||||
int cosmoB_osRead(CState *state, int nargs, CValue *args) {
|
int cosmoB_osRead(CState *state, int nargs, CValue *args)
|
||||||
|
{
|
||||||
if (nargs != 1) {
|
if (nargs != 1) {
|
||||||
cosmoV_error(state, "os.read() expected 1 argument, got %d!", nargs);
|
cosmoV_error(state, "os.read() expected 1 argument, got %d!", nargs);
|
||||||
return 0;
|
return 0;
|
||||||
@ -287,7 +287,7 @@ int cosmoB_osRead(CState *state, int nargs, CValue *args) {
|
|||||||
size = ftell(file);
|
size = ftell(file);
|
||||||
rewind(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
|
bRead = fread(buf, sizeof(char), size, file); // read the file into the buffer
|
||||||
|
|
||||||
if (bRead < size) {
|
if (bRead < size) {
|
||||||
@ -303,7 +303,8 @@ int cosmoB_osRead(CState *state, int nargs, CValue *args) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// os.time()
|
// os.time()
|
||||||
int cosmoB_osTime(CState *state, int nargs, CValue *args) {
|
int cosmoB_osTime(CState *state, int nargs, CValue *args)
|
||||||
|
{
|
||||||
struct timeval time;
|
struct timeval time;
|
||||||
if (nargs > 0) {
|
if (nargs > 0) {
|
||||||
cosmoV_error(state, "os.time() expected no arguments, got %d!", nargs);
|
cosmoV_error(state, "os.time() expected no arguments, got %d!", nargs);
|
||||||
@ -316,7 +317,8 @@ int cosmoB_osTime(CState *state, int nargs, CValue *args) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// os.system()
|
// os.system()
|
||||||
int cosmoB_osSystem(CState *state, int nargs, CValue *args) {
|
int cosmoB_osSystem(CState *state, int nargs, CValue *args)
|
||||||
|
{
|
||||||
if (nargs != 1) {
|
if (nargs != 1) {
|
||||||
cosmoV_error(state, "os.system() expects 1 argument, got %d!", nargs);
|
cosmoV_error(state, "os.system() expects 1 argument, got %d!", nargs);
|
||||||
return 0;
|
return 0;
|
||||||
@ -332,23 +334,16 @@ int cosmoB_osSystem(CState *state, int nargs, CValue *args) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
COSMO_API void cosmoB_loadOSLib(CState *state) {
|
COSMO_API void cosmoB_loadOSLib(CState *state)
|
||||||
const char *identifiers[] = {
|
{
|
||||||
"read",
|
const char *identifiers[] = {"read", "time", "system"};
|
||||||
"time",
|
|
||||||
"system"
|
|
||||||
};
|
|
||||||
|
|
||||||
CosmoCFunction osLib[] = {
|
CosmoCFunction osLib[] = {cosmoB_osRead, cosmoB_osTime, cosmoB_osSystem};
|
||||||
cosmoB_osRead,
|
|
||||||
cosmoB_osTime,
|
|
||||||
cosmoB_osSystem
|
|
||||||
};
|
|
||||||
|
|
||||||
cosmoV_pushString(state, "os");
|
cosmoV_pushString(state, "os");
|
||||||
|
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i < sizeof(identifiers)/sizeof(identifiers[0]); i++) {
|
for (i = 0; i < sizeof(identifiers) / sizeof(identifiers[0]); i++) {
|
||||||
cosmoV_pushString(state, identifiers[i]);
|
cosmoV_pushString(state, identifiers[i]);
|
||||||
cosmoV_pushCFunction(state, osLib[i]);
|
cosmoV_pushCFunction(state, osLib[i]);
|
||||||
}
|
}
|
||||||
@ -357,13 +352,16 @@ COSMO_API void cosmoB_loadOSLib(CState *state) {
|
|||||||
cosmoV_register(state, 1); // register the os.* object to the global table
|
cosmoV_register(state, 1); // register the os.* object to the global table
|
||||||
}
|
}
|
||||||
|
|
||||||
// ================================================================ [STRING.*] ================================================================
|
// ================================================================ [STRING.*]
|
||||||
|
// ================================================================
|
||||||
|
|
||||||
// string.sub
|
// string.sub
|
||||||
int cosmoB_sSub(CState *state, int nargs, CValue *args) {
|
int cosmoB_sSub(CState *state, int nargs, CValue *args)
|
||||||
|
{
|
||||||
if (nargs == 2) {
|
if (nargs == 2) {
|
||||||
if (!IS_STRING(args[0]) || !IS_NUMBER(args[1])) {
|
if (!IS_STRING(args[0]) || !IS_NUMBER(args[1])) {
|
||||||
cosmoV_typeError(state, "string.sub()", "<string>, <number>", "%s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
|
cosmoV_typeError(state, "string.sub()", "<string>, <number>", "%s, %s",
|
||||||
|
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -372,14 +370,17 @@ int cosmoB_sSub(CState *state, int nargs, CValue *args) {
|
|||||||
|
|
||||||
// make sure we stay within memory
|
// make sure we stay within memory
|
||||||
if (indx < 0 || indx >= str->length) {
|
if (indx < 0 || indx >= str->length) {
|
||||||
cosmoV_error(state, "string.sub() expected index to be 0-%d, got %d!", str->length - 1, indx);
|
cosmoV_error(state, "string.sub() expected index to be 0-%d, got %d!", str->length - 1,
|
||||||
|
indx);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
cosmoV_pushLString(state, str->str + ((int)indx), str->length - ((int)indx));
|
cosmoV_pushLString(state, str->str + ((int)indx), str->length - ((int)indx));
|
||||||
} else if (nargs == 3) {
|
} else if (nargs == 3) {
|
||||||
if (!IS_STRING(args[0]) || !IS_NUMBER(args[1]) || !IS_NUMBER(args[2])) {
|
if (!IS_STRING(args[0]) || !IS_NUMBER(args[1]) || !IS_NUMBER(args[2])) {
|
||||||
cosmoV_typeError(state, "string.sub()", "<string>, <number>, <number>", "%s, %s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]), cosmoV_typeStr(args[2]));
|
cosmoV_typeError(state, "string.sub()", "<string>, <number>, <number>", "%s, %s, %s",
|
||||||
|
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]),
|
||||||
|
cosmoV_typeStr(args[2]));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -389,7 +390,9 @@ int cosmoB_sSub(CState *state, int nargs, CValue *args) {
|
|||||||
|
|
||||||
// make sure we stay within memory
|
// make sure we stay within memory
|
||||||
if (indx + length < 0 || indx + length >= str->length || indx < 0 || indx >= str->length) {
|
if (indx + length < 0 || indx + length >= str->length || indx < 0 || indx >= str->length) {
|
||||||
cosmoV_error(state, "string.sub() expected subbed string goes out of bounds, max length is %d!", str->length);
|
cosmoV_error(
|
||||||
|
state, "string.sub() expected subbed string goes out of bounds, max length is %d!",
|
||||||
|
str->length);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -403,10 +406,12 @@ int cosmoB_sSub(CState *state, int nargs, CValue *args) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// string.find
|
// string.find
|
||||||
int cosmoB_sFind(CState *state, int nargs, CValue *args) {
|
int cosmoB_sFind(CState *state, int nargs, CValue *args)
|
||||||
|
{
|
||||||
if (nargs == 2) {
|
if (nargs == 2) {
|
||||||
if (!IS_STRING(args[0]) || !IS_STRING(args[1])) {
|
if (!IS_STRING(args[0]) || !IS_STRING(args[1])) {
|
||||||
cosmoV_typeError(state, "string.find()", "<string>, <string>", "%s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
|
cosmoV_typeError(state, "string.find()", "<string>, <string>", "%s, %s",
|
||||||
|
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -425,7 +430,9 @@ int cosmoB_sFind(CState *state, int nargs, CValue *args) {
|
|||||||
cosmoV_pushNumber(state, indx - str->str);
|
cosmoV_pushNumber(state, indx - str->str);
|
||||||
} else if (nargs == 3) {
|
} else if (nargs == 3) {
|
||||||
if (!IS_STRING(args[0]) || !IS_STRING(args[1]) || !IS_NUMBER(args[2])) {
|
if (!IS_STRING(args[0]) || !IS_STRING(args[1]) || !IS_NUMBER(args[2])) {
|
||||||
cosmoV_typeError(state, "string.find()", "<string>, <string>, <number>", "%s, %s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]), cosmoV_typeStr(args[2]));
|
cosmoV_typeError(state, "string.find()", "<string>, <string>, <number>", "%s, %s, %s",
|
||||||
|
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]),
|
||||||
|
cosmoV_typeStr(args[2]));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -452,14 +459,16 @@ int cosmoB_sFind(CState *state, int nargs, CValue *args) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// string.split
|
// string.split
|
||||||
int cosmoB_sSplit(CState *state, int nargs, CValue *args) {
|
int cosmoB_sSplit(CState *state, int nargs, CValue *args)
|
||||||
|
{
|
||||||
if (nargs != 2) {
|
if (nargs != 2) {
|
||||||
cosmoV_error(state, "string.split() expected 2 arguments, got %d!", nargs);
|
cosmoV_error(state, "string.split() expected 2 arguments, got %d!", nargs);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!IS_STRING(args[0]) || !IS_STRING(args[1])) {
|
if (!IS_STRING(args[0]) || !IS_STRING(args[1])) {
|
||||||
cosmoV_typeError(state, "string.split()", "<string>, <string>", "%s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
|
cosmoV_typeError(state, "string.split()", "<string>, <string>", "%s, %s",
|
||||||
|
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -475,7 +484,8 @@ int cosmoB_sSplit(CState *state, int nargs, CValue *args) {
|
|||||||
nIndx = strstr(indx, ptrn->str);
|
nIndx = strstr(indx, ptrn->str);
|
||||||
|
|
||||||
cosmoV_pushNumber(state, nEntries++);
|
cosmoV_pushNumber(state, nEntries++);
|
||||||
cosmoV_pushLString(state, indx, nIndx == NULL ? str->length - (indx - str->str) : nIndx - indx);
|
cosmoV_pushLString(state, indx,
|
||||||
|
nIndx == NULL ? str->length - (indx - str->str) : nIndx - indx);
|
||||||
|
|
||||||
indx = nIndx + ptrn->length;
|
indx = nIndx + ptrn->length;
|
||||||
} while (nIndx != NULL);
|
} while (nIndx != NULL);
|
||||||
@ -486,7 +496,8 @@ int cosmoB_sSplit(CState *state, int nargs, CValue *args) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// string.byte
|
// string.byte
|
||||||
int cosmoB_sByte(CState *state, int nargs, CValue *args) {
|
int cosmoB_sByte(CState *state, int nargs, CValue *args)
|
||||||
|
{
|
||||||
if (nargs != 1) {
|
if (nargs != 1) {
|
||||||
cosmoV_error(state, "string.byte() expected 1 argument, got %d!", nargs);
|
cosmoV_error(state, "string.byte() expected 1 argument, got %d!", nargs);
|
||||||
return 0;
|
return 0;
|
||||||
@ -496,12 +507,12 @@ int cosmoB_sByte(CState *state, int nargs, CValue *args) {
|
|||||||
cosmoV_typeError(state, "string.byte", "<string>", "%s", cosmoV_typeStr(args[0]));
|
cosmoV_typeError(state, "string.byte", "<string>", "%s", cosmoV_typeStr(args[0]));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
CObjString *str = cosmoV_readString(args[0]);
|
CObjString *str = cosmoV_readString(args[0]);
|
||||||
|
|
||||||
if (str->length < 1) {
|
if (str->length < 1) {
|
||||||
// the length of the string is less than 1, in the future I might throw an error for this, but
|
// the length of the string is less than 1, in the future I might throw an error for this,
|
||||||
// for now im going to copy lua and just return a nil
|
// but for now im going to copy lua and just return a nil
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -511,7 +522,8 @@ int cosmoB_sByte(CState *state, int nargs, CValue *args) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// string.char
|
// string.char
|
||||||
int cosmoB_sChar(CState *state, int nargs, CValue *args) {
|
int cosmoB_sChar(CState *state, int nargs, CValue *args)
|
||||||
|
{
|
||||||
if (nargs != 1) {
|
if (nargs != 1) {
|
||||||
cosmoV_error(state, "string.char() expected 1 argument, got %d!", nargs);
|
cosmoV_error(state, "string.char() expected 1 argument, got %d!", nargs);
|
||||||
return 0;
|
return 0;
|
||||||
@ -522,7 +534,8 @@ int cosmoB_sChar(CState *state, int nargs, CValue *args) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// small side effect of truncating the number, but ignoring the decimal instead of throwing an error is the better option imo
|
// small side effect of truncating the number, but ignoring the decimal instead of throwing an
|
||||||
|
// error is the better option imo
|
||||||
int num = (int)cosmoV_readNumber(args[0]);
|
int num = (int)cosmoV_readNumber(args[0]);
|
||||||
char c = num;
|
char c = num;
|
||||||
|
|
||||||
@ -536,7 +549,8 @@ int cosmoB_sChar(CState *state, int nargs, CValue *args) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int cosmoB_sLen(CState *state, int nargs, CValue *args) {
|
int cosmoB_sLen(CState *state, int nargs, CValue *args)
|
||||||
|
{
|
||||||
if (nargs < 1) {
|
if (nargs < 1) {
|
||||||
cosmoV_error(state, "string.len() expected 1 argument, got %d!", nargs);
|
cosmoV_error(state, "string.len() expected 1 argument, got %d!", nargs);
|
||||||
return 0;
|
return 0;
|
||||||
@ -552,7 +566,8 @@ int cosmoB_sLen(CState *state, int nargs, CValue *args) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int cosmoB_sRep(CState *state, int nargs, CValue *args) {
|
int cosmoB_sRep(CState *state, int nargs, CValue *args)
|
||||||
|
{
|
||||||
if (nargs != 2) {
|
if (nargs != 2) {
|
||||||
cosmoV_error(state, "string.rep() expected 2 arguments, got %d!", nargs);
|
cosmoV_error(state, "string.rep() expected 2 arguments, got %d!", nargs);
|
||||||
return 0;
|
return 0;
|
||||||
@ -560,7 +575,8 @@ int cosmoB_sRep(CState *state, int nargs, CValue *args) {
|
|||||||
|
|
||||||
// expects <string>, <number>
|
// expects <string>, <number>
|
||||||
if (!IS_STRING(args[0]) || !IS_NUMBER(args[1])) {
|
if (!IS_STRING(args[0]) || !IS_NUMBER(args[1])) {
|
||||||
cosmoV_typeError(state, "string.rep", "<string>, <number>", "%s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
|
cosmoV_typeError(state, "string.rep", "<string>, <number>", "%s, %s",
|
||||||
|
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -582,37 +598,23 @@ int cosmoB_sRep(CState *state, int nargs, CValue *args) {
|
|||||||
|
|
||||||
// write the NULL terminator
|
// write the NULL terminator
|
||||||
newStr[length] = '\0';
|
newStr[length] = '\0';
|
||||||
|
|
||||||
// finally, push the resulting string onto the stack
|
// finally, push the resulting string onto the stack
|
||||||
cosmoV_pushRef(state, (CObj*)cosmoO_takeString(state, newStr, length));
|
cosmoV_pushRef(state, (CObj *)cosmoO_takeString(state, newStr, length));
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void cosmoB_loadStrLib(CState *state) {
|
void cosmoB_loadStrLib(CState *state)
|
||||||
const char *identifiers[] = {
|
{
|
||||||
"sub",
|
const char *identifiers[] = {"sub", "find", "split", "byte", "char", "len", "rep"};
|
||||||
"find",
|
|
||||||
"split",
|
|
||||||
"byte",
|
|
||||||
"char",
|
|
||||||
"len",
|
|
||||||
"rep"
|
|
||||||
};
|
|
||||||
|
|
||||||
CosmoCFunction strLib[] = {
|
CosmoCFunction strLib[] = {cosmoB_sSub, cosmoB_sFind, cosmoB_sSplit, cosmoB_sByte,
|
||||||
cosmoB_sSub,
|
cosmoB_sChar, cosmoB_sLen, cosmoB_sRep};
|
||||||
cosmoB_sFind,
|
|
||||||
cosmoB_sSplit,
|
|
||||||
cosmoB_sByte,
|
|
||||||
cosmoB_sChar,
|
|
||||||
cosmoB_sLen,
|
|
||||||
cosmoB_sRep
|
|
||||||
};
|
|
||||||
|
|
||||||
// make string library object
|
// make string library object
|
||||||
cosmoV_pushString(state, "string");
|
cosmoV_pushString(state, "string");
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i < sizeof(identifiers)/sizeof(identifiers[0]); i++) {
|
for (i = 0; i < sizeof(identifiers) / sizeof(identifiers[0]); i++) {
|
||||||
cosmoV_pushString(state, identifiers[i]);
|
cosmoV_pushString(state, identifiers[i]);
|
||||||
cosmoV_pushCFunction(state, strLib[i]);
|
cosmoV_pushCFunction(state, strLib[i]);
|
||||||
}
|
}
|
||||||
@ -626,10 +628,12 @@ void cosmoB_loadStrLib(CState *state) {
|
|||||||
cosmoV_register(state, 1);
|
cosmoV_register(state, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ================================================================ [MATH] ================================================================
|
// ================================================================ [MATH]
|
||||||
|
// ================================================================
|
||||||
|
|
||||||
// math.abs
|
// math.abs
|
||||||
int cosmoB_mAbs(CState *state, int nargs, CValue *args) {
|
int cosmoB_mAbs(CState *state, int nargs, CValue *args)
|
||||||
|
{
|
||||||
if (nargs != 1) {
|
if (nargs != 1) {
|
||||||
cosmoV_error(state, "math.abs() expected 1 argument, got %d!", nargs);
|
cosmoV_error(state, "math.abs() expected 1 argument, got %d!", nargs);
|
||||||
return 0;
|
return 0;
|
||||||
@ -645,7 +649,8 @@ int cosmoB_mAbs(CState *state, int nargs, CValue *args) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// math.floor
|
// math.floor
|
||||||
int cosmoB_mFloor(CState *state, int nargs, CValue *args) {
|
int cosmoB_mFloor(CState *state, int nargs, CValue *args)
|
||||||
|
{
|
||||||
if (nargs != 1) {
|
if (nargs != 1) {
|
||||||
cosmoV_error(state, "math.floor() expected 1 argument, got %d!", nargs);
|
cosmoV_error(state, "math.floor() expected 1 argument, got %d!", nargs);
|
||||||
return 0;
|
return 0;
|
||||||
@ -661,7 +666,8 @@ int cosmoB_mFloor(CState *state, int nargs, CValue *args) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// math.ceil
|
// math.ceil
|
||||||
int cosmoB_mCeil(CState *state, int nargs, CValue *args) {
|
int cosmoB_mCeil(CState *state, int nargs, CValue *args)
|
||||||
|
{
|
||||||
if (nargs != 1) {
|
if (nargs != 1) {
|
||||||
cosmoV_error(state, "math.ceil() expected 1 argument, got %d!", nargs);
|
cosmoV_error(state, "math.ceil() expected 1 argument, got %d!", nargs);
|
||||||
return 0;
|
return 0;
|
||||||
@ -684,7 +690,8 @@ int cosmoB_mCeil(CState *state, int nargs, CValue *args) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int cosmoB_mSin(CState *state, int nargs, CValue *args) {
|
int cosmoB_mSin(CState *state, int nargs, CValue *args)
|
||||||
|
{
|
||||||
if (nargs != 1) {
|
if (nargs != 1) {
|
||||||
cosmoV_error(state, "math.sin() expected 1 argument, got %d!", nargs);
|
cosmoV_error(state, "math.sin() expected 1 argument, got %d!", nargs);
|
||||||
return 0;
|
return 0;
|
||||||
@ -699,7 +706,8 @@ int cosmoB_mSin(CState *state, int nargs, CValue *args) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int cosmoB_mCos(CState *state, int nargs, CValue *args) {
|
int cosmoB_mCos(CState *state, int nargs, CValue *args)
|
||||||
|
{
|
||||||
if (nargs != 1) {
|
if (nargs != 1) {
|
||||||
cosmoV_error(state, "math.cos() expected 1 argument, got %d!", nargs);
|
cosmoV_error(state, "math.cos() expected 1 argument, got %d!", nargs);
|
||||||
return 0;
|
return 0;
|
||||||
@ -714,7 +722,8 @@ int cosmoB_mCos(CState *state, int nargs, CValue *args) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int cosmoB_mTan(CState *state, int nargs, CValue *args) {
|
int cosmoB_mTan(CState *state, int nargs, CValue *args)
|
||||||
|
{
|
||||||
if (nargs != 1) {
|
if (nargs != 1) {
|
||||||
cosmoV_error(state, "math.tan() expected 1 argument, got %d!", nargs);
|
cosmoV_error(state, "math.tan() expected 1 argument, got %d!", nargs);
|
||||||
return 0;
|
return 0;
|
||||||
@ -729,7 +738,8 @@ int cosmoB_mTan(CState *state, int nargs, CValue *args) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int cosmoB_mASin(CState *state, int nargs, CValue *args) {
|
int cosmoB_mASin(CState *state, int nargs, CValue *args)
|
||||||
|
{
|
||||||
if (nargs != 1) {
|
if (nargs != 1) {
|
||||||
cosmoV_error(state, "math.asin() expected 1 argument, got %d!", nargs);
|
cosmoV_error(state, "math.asin() expected 1 argument, got %d!", nargs);
|
||||||
return 0;
|
return 0;
|
||||||
@ -744,7 +754,8 @@ int cosmoB_mASin(CState *state, int nargs, CValue *args) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int cosmoB_mACos(CState *state, int nargs, CValue *args) {
|
int cosmoB_mACos(CState *state, int nargs, CValue *args)
|
||||||
|
{
|
||||||
if (nargs != 1) {
|
if (nargs != 1) {
|
||||||
cosmoV_error(state, "math.acos() expected 1 argument, got %d!", nargs);
|
cosmoV_error(state, "math.acos() expected 1 argument, got %d!", nargs);
|
||||||
return 0;
|
return 0;
|
||||||
@ -759,7 +770,8 @@ int cosmoB_mACos(CState *state, int nargs, CValue *args) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int cosmoB_mATan(CState *state, int nargs, CValue *args) {
|
int cosmoB_mATan(CState *state, int nargs, CValue *args)
|
||||||
|
{
|
||||||
if (nargs != 1) {
|
if (nargs != 1) {
|
||||||
cosmoV_error(state, "math.atan() expected 1 argument, got %d!", nargs);
|
cosmoV_error(state, "math.atan() expected 1 argument, got %d!", nargs);
|
||||||
return 0;
|
return 0;
|
||||||
@ -774,7 +786,8 @@ int cosmoB_mATan(CState *state, int nargs, CValue *args) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int cosmoB_mRad(CState *state, int nargs, CValue *args) {
|
int cosmoB_mRad(CState *state, int nargs, CValue *args)
|
||||||
|
{
|
||||||
if (nargs != 1) {
|
if (nargs != 1) {
|
||||||
cosmoV_error(state, "math.rad() expected 1 argument, got %d!", nargs);
|
cosmoV_error(state, "math.rad() expected 1 argument, got %d!", nargs);
|
||||||
return 0;
|
return 0;
|
||||||
@ -790,7 +803,8 @@ int cosmoB_mRad(CState *state, int nargs, CValue *args) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int cosmoB_mDeg(CState *state, int nargs, CValue *args) {
|
int cosmoB_mDeg(CState *state, int nargs, CValue *args)
|
||||||
|
{
|
||||||
if (nargs != 1) {
|
if (nargs != 1) {
|
||||||
cosmoV_error(state, "math.deg() expected 1 argument, got %d!", nargs);
|
cosmoV_error(state, "math.deg() expected 1 argument, got %d!", nargs);
|
||||||
return 0;
|
return 0;
|
||||||
@ -806,39 +820,19 @@ int cosmoB_mDeg(CState *state, int nargs, CValue *args) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void cosmoB_loadMathLib(CState *state) {
|
void cosmoB_loadMathLib(CState *state)
|
||||||
const char *identifiers[] = {
|
{
|
||||||
"abs",
|
const char *identifiers[] = {"abs", "floor", "ceil", "sin", "cos", "tan",
|
||||||
"floor",
|
"asin", "acos", "atan", "rad", "deg"};
|
||||||
"ceil",
|
|
||||||
"sin",
|
|
||||||
"cos",
|
|
||||||
"tan",
|
|
||||||
"asin",
|
|
||||||
"acos",
|
|
||||||
"atan",
|
|
||||||
"rad",
|
|
||||||
"deg"
|
|
||||||
};
|
|
||||||
|
|
||||||
CosmoCFunction mathLib[] = {
|
CosmoCFunction mathLib[] = {cosmoB_mAbs, cosmoB_mFloor, cosmoB_mCeil, cosmoB_mSin,
|
||||||
cosmoB_mAbs,
|
cosmoB_mCos, cosmoB_mTan, cosmoB_mASin, cosmoB_mACos,
|
||||||
cosmoB_mFloor,
|
cosmoB_mATan, cosmoB_mRad, cosmoB_mDeg};
|
||||||
cosmoB_mCeil,
|
|
||||||
cosmoB_mSin,
|
|
||||||
cosmoB_mCos,
|
|
||||||
cosmoB_mTan,
|
|
||||||
cosmoB_mASin,
|
|
||||||
cosmoB_mACos,
|
|
||||||
cosmoB_mATan,
|
|
||||||
cosmoB_mRad,
|
|
||||||
cosmoB_mDeg
|
|
||||||
};
|
|
||||||
|
|
||||||
// make math library object
|
// make math library object
|
||||||
cosmoV_pushString(state, "math");
|
cosmoV_pushString(state, "math");
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i < sizeof(identifiers)/sizeof(identifiers[0]); i++) {
|
for (i = 0; i < sizeof(identifiers) / sizeof(identifiers[0]); i++) {
|
||||||
cosmoV_pushString(state, identifiers[i]);
|
cosmoV_pushString(state, identifiers[i]);
|
||||||
cosmoV_pushCFunction(state, mathLib[i]);
|
cosmoV_pushCFunction(state, mathLib[i]);
|
||||||
}
|
}
|
||||||
@ -852,41 +846,47 @@ void cosmoB_loadMathLib(CState *state) {
|
|||||||
cosmoV_register(state, 1);
|
cosmoV_register(state, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ================================================================ [VM.*] ================================================================
|
// ================================================================ [VM.*]
|
||||||
|
// ================================================================
|
||||||
|
|
||||||
// vm.__getter["globals"]
|
// vm.__getter["globals"]
|
||||||
int cosmoB_vgetGlobal(CState *state, int nargs, CValue *args) {
|
int cosmoB_vgetGlobal(CState *state, int nargs, CValue *args)
|
||||||
|
{
|
||||||
// this function doesn't need to check anything, just return the global table
|
// this function doesn't need to check anything, just return the global table
|
||||||
cosmoV_pushRef(state, (CObj*)state->globals);
|
cosmoV_pushRef(state, (CObj *)state->globals);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// vm.__setter["globals"]
|
// vm.__setter["globals"]
|
||||||
int cosmoB_vsetGlobal(CState *state, int nargs, CValue *args) {
|
int cosmoB_vsetGlobal(CState *state, int nargs, CValue *args)
|
||||||
|
{
|
||||||
if (nargs != 2) {
|
if (nargs != 2) {
|
||||||
cosmoV_error(state, "Expected 2 argumenst, got %d!", nargs);
|
cosmoV_error(state, "Expected 2 argumenst, got %d!", nargs);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!IS_TABLE(args[1])) {
|
if (!IS_TABLE(args[1])) {
|
||||||
cosmoV_typeError(state, "vm.__setter[\"globals\"]", "<object>, <table>", "%s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
|
cosmoV_typeError(state, "vm.__setter[\"globals\"]", "<object>, <table>", "%s, %s",
|
||||||
|
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// this makes me very nervous ngl
|
// this makes me very nervous ngl
|
||||||
CObjTable *tbl = (CObjTable*)cosmoV_readRef(args[1]);
|
CObjTable *tbl = (CObjTable *)cosmoV_readRef(args[1]);
|
||||||
state->globals = tbl;
|
state->globals = tbl;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int cosmoB_vindexBProto(CState *state, int nargs, CValue *args) {
|
int cosmoB_vindexBProto(CState *state, int nargs, CValue *args)
|
||||||
|
{
|
||||||
if (nargs != 2) {
|
if (nargs != 2) {
|
||||||
cosmoV_error(state, "Expected 2 arguments, got %d!", nargs);
|
cosmoV_error(state, "Expected 2 arguments, got %d!", nargs);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!IS_NUMBER(args[1])) {
|
if (!IS_NUMBER(args[1])) {
|
||||||
cosmoV_typeError(state, "baseProtos.__index", "<object>, <number>", "%s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
|
cosmoV_typeError(state, "baseProtos.__index", "<object>, <number>", "%s, %s",
|
||||||
|
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -898,21 +898,24 @@ int cosmoB_vindexBProto(CState *state, int nargs, CValue *args) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (state->protoObjects[indx] != NULL)
|
if (state->protoObjects[indx] != NULL)
|
||||||
cosmoV_pushRef(state, (CObj*)state->protoObjects[indx]);
|
cosmoV_pushRef(state, (CObj *)state->protoObjects[indx]);
|
||||||
else
|
else
|
||||||
cosmoV_pushNil(state);
|
cosmoV_pushNil(state);
|
||||||
|
|
||||||
return 1; // 1 value pushed, 1 value returned
|
return 1; // 1 value pushed, 1 value returned
|
||||||
}
|
}
|
||||||
|
|
||||||
int cosmoB_vnewindexBProto(CState *state, int nargs, CValue *args) {
|
int cosmoB_vnewindexBProto(CState *state, int nargs, CValue *args)
|
||||||
|
{
|
||||||
if (nargs != 3) {
|
if (nargs != 3) {
|
||||||
cosmoV_error(state, "Expected 3 arguments, got %d!", nargs);
|
cosmoV_error(state, "Expected 3 arguments, got %d!", nargs);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!IS_NUMBER(args[1]) || !IS_OBJECT(args[2])) {
|
if (!IS_NUMBER(args[1]) || !IS_OBJECT(args[2])) {
|
||||||
cosmoV_typeError(state, "baseProtos.__newindex", "<object>, <number>, <object>", "%s, %s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]), cosmoV_typeStr(args[2]));
|
cosmoV_typeError(state, "baseProtos.__newindex", "<object>, <number>, <object>",
|
||||||
|
"%s, %s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]),
|
||||||
|
cosmoV_typeStr(args[2]));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -929,10 +932,11 @@ int cosmoB_vnewindexBProto(CState *state, int nargs, CValue *args) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// vm.collect()
|
// vm.collect()
|
||||||
int cosmoB_vcollect(CState *state, int nargs, CValue *args) {
|
int cosmoB_vcollect(CState *state, int nargs, CValue *args)
|
||||||
|
{
|
||||||
// first, unfreeze the state (we start frozen on entry to any C Function)
|
// first, unfreeze the state (we start frozen on entry to any C Function)
|
||||||
cosmoM_unfreezeGC(state);
|
cosmoM_unfreezeGC(state);
|
||||||
|
|
||||||
// now force a garbage collection
|
// now force a garbage collection
|
||||||
cosmoM_collectGarbage(state);
|
cosmoM_collectGarbage(state);
|
||||||
|
|
||||||
@ -943,7 +947,8 @@ int cosmoB_vcollect(CState *state, int nargs, CValue *args) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void cosmoB_loadVM(CState *state) {
|
void cosmoB_loadVM(CState *state)
|
||||||
|
{
|
||||||
// make vm.* object
|
// make vm.* object
|
||||||
cosmoV_pushString(state, "vm");
|
cosmoV_pushString(state, "vm");
|
||||||
|
|
||||||
|
@ -32,7 +32,8 @@ COSMO_API void cosmoB_loadOSLib(CState *state);
|
|||||||
- string.char & <string>:char()
|
- string.char & <string>:char()
|
||||||
- string.rep & <string>:rep()
|
- string.rep & <string>:rep()
|
||||||
|
|
||||||
The base proto object for strings is also set, allowing you to invoke the string.* api through string objects, eg.
|
The base proto object for strings is also set, allowing you to invoke the string.* api through
|
||||||
|
string objects, eg.
|
||||||
`"hello world":split(" ")` is equivalent to `string.split("hello world", " ")`
|
`"hello world":split(" ")` is equivalent to `string.split("hello world", " ")`
|
||||||
*/
|
*/
|
||||||
COSMO_API void cosmoB_loadStrLib(CState *state);
|
COSMO_API void cosmoB_loadStrLib(CState *state);
|
||||||
@ -54,7 +55,7 @@ COSMO_API void cosmoB_loadMathLib(CState *state);
|
|||||||
*/
|
*/
|
||||||
COSMO_API void cosmoB_loadVM(CState *state);
|
COSMO_API void cosmoB_loadVM(CState *state);
|
||||||
|
|
||||||
#define cosmoV_typeError(state, name, expectedTypes, formatStr, ...) \
|
#define cosmoV_typeError(state, name, expectedTypes, formatStr, ...) \
|
||||||
cosmoV_error(state, name " expected (" expectedTypes "), got (" formatStr ")!", __VA_ARGS__);
|
cosmoV_error(state, name " expected (" expectedTypes "), got (" formatStr ")!", __VA_ARGS__);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
33
src/cchunk.c
33
src/cchunk.c
@ -1,27 +1,31 @@
|
|||||||
#include "cmem.h"
|
|
||||||
#include "cchunk.h"
|
#include "cchunk.h"
|
||||||
|
|
||||||
|
#include "cmem.h"
|
||||||
|
#include "cobj.h"
|
||||||
#include "cvalue.h"
|
#include "cvalue.h"
|
||||||
#include "cvm.h"
|
#include "cvm.h"
|
||||||
#include "cobj.h"
|
|
||||||
|
|
||||||
CChunk *newChunk(CState* state, size_t startCapacity) {
|
CChunk *newChunk(CState *state, size_t startCapacity)
|
||||||
|
{
|
||||||
CChunk *chunk = cosmoM_xmalloc(state, sizeof(CChunk));
|
CChunk *chunk = cosmoM_xmalloc(state, sizeof(CChunk));
|
||||||
initChunk(state, chunk, startCapacity);
|
initChunk(state, chunk, startCapacity);
|
||||||
return chunk;
|
return chunk;
|
||||||
}
|
}
|
||||||
|
|
||||||
void initChunk(CState* state, CChunk *chunk, size_t startCapacity) {
|
void initChunk(CState *state, CChunk *chunk, size_t startCapacity)
|
||||||
|
{
|
||||||
chunk->capacity = startCapacity;
|
chunk->capacity = startCapacity;
|
||||||
chunk->lineCapacity = startCapacity;
|
chunk->lineCapacity = startCapacity;
|
||||||
chunk->count = 0;
|
chunk->count = 0;
|
||||||
chunk->buf = NULL; // when writeByteChunk is called, it'll allocate the array for us
|
chunk->buf = NULL; // when writeByteChunk is called, it'll allocate the array for us
|
||||||
chunk->lineInfo = NULL;
|
chunk->lineInfo = NULL;
|
||||||
|
|
||||||
// constants
|
// constants
|
||||||
initValArray(state, &chunk->constants, ARRAY_START);
|
initValArray(state, &chunk->constants, ARRAY_START);
|
||||||
}
|
}
|
||||||
|
|
||||||
void cleanChunk(CState* state, CChunk *chunk) {
|
void cleanChunk(CState *state, CChunk *chunk)
|
||||||
|
{
|
||||||
// first, free the chunk buffer
|
// first, free the chunk buffer
|
||||||
cosmoM_freearray(state, INSTRUCTION, chunk->buf, chunk->capacity);
|
cosmoM_freearray(state, INSTRUCTION, chunk->buf, chunk->capacity);
|
||||||
// then the line info
|
// then the line info
|
||||||
@ -30,13 +34,15 @@ void cleanChunk(CState* state, CChunk *chunk) {
|
|||||||
cleanValArray(state, &chunk->constants);
|
cleanValArray(state, &chunk->constants);
|
||||||
}
|
}
|
||||||
|
|
||||||
void freeChunk(CState* state, CChunk *chunk) {
|
void freeChunk(CState *state, CChunk *chunk)
|
||||||
|
{
|
||||||
cleanChunk(state, chunk);
|
cleanChunk(state, chunk);
|
||||||
// now, free the wrapper struct
|
// now, free the wrapper struct
|
||||||
cosmoM_free(state, CChunk, chunk);
|
cosmoM_free(state, CChunk, chunk);
|
||||||
}
|
}
|
||||||
|
|
||||||
int addConstant(CState* state, CChunk *chunk, CValue value) {
|
int addConstant(CState *state, CChunk *chunk, CValue value)
|
||||||
|
{
|
||||||
// before adding the constant, check if we already have it
|
// before adding the constant, check if we already have it
|
||||||
for (size_t i = 0; i < chunk->constants.count; i++) {
|
for (size_t i = 0; i < chunk->constants.count; i++) {
|
||||||
if (cosmoV_equal(state, value, chunk->constants.values[i]))
|
if (cosmoV_equal(state, value, chunk->constants.values[i]))
|
||||||
@ -49,9 +55,11 @@ int addConstant(CState* state, CChunk *chunk, CValue value) {
|
|||||||
return chunk->constants.count - 1; // return the index of the new constants
|
return chunk->constants.count - 1; // return the index of the new constants
|
||||||
}
|
}
|
||||||
|
|
||||||
// ================================================================ [WRITE TO CHUNK] ================================================================
|
// ================================================================ [WRITE TO CHUNK]
|
||||||
|
// ================================================================
|
||||||
|
|
||||||
void writeu8Chunk(CState* state, CChunk *chunk, INSTRUCTION i, int line) {
|
void writeu8Chunk(CState *state, CChunk *chunk, INSTRUCTION i, int line)
|
||||||
|
{
|
||||||
// does the buffer need to be reallocated?
|
// does the buffer need to be reallocated?
|
||||||
cosmoM_growarray(state, INSTRUCTION, chunk->buf, chunk->count, chunk->capacity);
|
cosmoM_growarray(state, INSTRUCTION, chunk->buf, chunk->count, chunk->capacity);
|
||||||
cosmoM_growarray(state, int, chunk->lineInfo, chunk->count, chunk->lineCapacity);
|
cosmoM_growarray(state, int, chunk->lineInfo, chunk->count, chunk->lineCapacity);
|
||||||
@ -61,8 +69,9 @@ void writeu8Chunk(CState* state, CChunk *chunk, INSTRUCTION i, int line) {
|
|||||||
chunk->buf[chunk->count++] = i;
|
chunk->buf[chunk->count++] = i;
|
||||||
}
|
}
|
||||||
|
|
||||||
void writeu16Chunk(CState* state, CChunk *chunk, uint16_t i, int line) {
|
void writeu16Chunk(CState *state, CChunk *chunk, uint16_t i, int line)
|
||||||
INSTRUCTION *buffer = (INSTRUCTION*)(&i);
|
{
|
||||||
|
INSTRUCTION *buffer = (INSTRUCTION *)(&i);
|
||||||
int sz = sizeof(uint16_t) / sizeof(INSTRUCTION);
|
int sz = sizeof(uint16_t) / sizeof(INSTRUCTION);
|
||||||
|
|
||||||
for (int i = 0; i < sz; i++) {
|
for (int i = 0; i < sz; i++) {
|
||||||
|
34
src/cchunk.h
34
src/cchunk.h
@ -1,37 +1,39 @@
|
|||||||
#ifndef CCHUNK_H
|
#ifndef CCHUNK_H
|
||||||
#define CCHUNK_H
|
#define CCHUNK_H
|
||||||
|
|
||||||
#include "cosmo.h"
|
|
||||||
|
|
||||||
#include "coperators.h"
|
#include "coperators.h"
|
||||||
|
#include "cosmo.h"
|
||||||
#include "cvalue.h"
|
#include "cvalue.h"
|
||||||
|
|
||||||
struct CChunk {
|
struct CChunk
|
||||||
size_t capacity; // the amount of space we've allocated for
|
{
|
||||||
size_t count; // the space we're currently using
|
size_t capacity; // the amount of space we've allocated for
|
||||||
INSTRUCTION *buf; // whole chunk
|
size_t count; // the space we're currently using
|
||||||
|
INSTRUCTION *buf; // whole chunk
|
||||||
CValueArray constants; // holds constants
|
CValueArray constants; // holds constants
|
||||||
size_t lineCapacity;
|
size_t lineCapacity;
|
||||||
int *lineInfo;
|
int *lineInfo;
|
||||||
};
|
};
|
||||||
|
|
||||||
CChunk *newChunk(CState* state, size_t startCapacity);
|
CChunk *newChunk(CState *state, size_t startCapacity);
|
||||||
void initChunk(CState* state, CChunk *chunk, size_t startCapacity);
|
void initChunk(CState *state, CChunk *chunk, size_t startCapacity);
|
||||||
void cleanChunk(CState* state, CChunk *chunk); // frees everything but the struct
|
void cleanChunk(CState *state, CChunk *chunk); // frees everything but the struct
|
||||||
void freeChunk(CState* state, CChunk *chunk); // frees everything including the struct
|
void freeChunk(CState *state, CChunk *chunk); // frees everything including the struct
|
||||||
int addConstant(CState* state, CChunk *chunk, CValue value);
|
int addConstant(CState *state, CChunk *chunk, CValue value);
|
||||||
|
|
||||||
// write to chunk
|
// write to chunk
|
||||||
void writeu8Chunk(CState* state, CChunk *chunk, INSTRUCTION i, int line);
|
void writeu8Chunk(CState *state, CChunk *chunk, INSTRUCTION i, int line);
|
||||||
void writeu16Chunk(CState* state, CChunk *chunk, uint16_t i, int line);
|
void writeu16Chunk(CState *state, CChunk *chunk, uint16_t i, int line);
|
||||||
|
|
||||||
// read from chunk
|
// read from chunk
|
||||||
static inline INSTRUCTION readu8Chunk(CChunk *chunk, int offset) {
|
static inline INSTRUCTION readu8Chunk(CChunk *chunk, int offset)
|
||||||
|
{
|
||||||
return chunk->buf[offset];
|
return chunk->buf[offset];
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline uint16_t readu16Chunk(CChunk *chunk, int offset) {
|
static inline uint16_t readu16Chunk(CChunk *chunk, int offset)
|
||||||
return *((uint16_t*)(&chunk->buf[offset]));
|
{
|
||||||
|
return *((uint16_t *)(&chunk->buf[offset]));
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
289
src/cdebug.c
289
src/cdebug.c
@ -1,49 +1,62 @@
|
|||||||
#include "cdebug.h"
|
#include "cdebug.h"
|
||||||
#include "cvalue.h"
|
|
||||||
#include "cobj.h"
|
|
||||||
|
|
||||||
void printIndent(int indent) {
|
#include "cobj.h"
|
||||||
|
#include "cvalue.h"
|
||||||
|
|
||||||
|
void printIndent(int indent)
|
||||||
|
{
|
||||||
for (int i = 0; i < indent; i++)
|
for (int i = 0; i < indent; i++)
|
||||||
printf("\t");
|
printf("\t");
|
||||||
}
|
}
|
||||||
|
|
||||||
int simpleInstruction(const char *name, int offset) {
|
int simpleInstruction(const char *name, int offset)
|
||||||
|
{
|
||||||
printf("%s", name);
|
printf("%s", name);
|
||||||
return offset + 1; // consume opcode
|
return offset + 1; // consume opcode
|
||||||
}
|
}
|
||||||
|
|
||||||
int u8OperandInstruction(const char *name, CChunk *chunk, int offset) {
|
int u8OperandInstruction(const char *name, CChunk *chunk, int offset)
|
||||||
|
{
|
||||||
printf("%-16s [%03d]", name, readu8Chunk(chunk, offset + 1));
|
printf("%-16s [%03d]", name, readu8Chunk(chunk, offset + 1));
|
||||||
return offset + 2;
|
return offset + 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
int u16OperandInstruction(const char *name, CChunk *chunk, int offset) {
|
int u16OperandInstruction(const char *name, CChunk *chunk, int offset)
|
||||||
|
{
|
||||||
printf("%-16s [%05d]", name, readu16Chunk(chunk, offset + 1));
|
printf("%-16s [%05d]", name, readu16Chunk(chunk, offset + 1));
|
||||||
return offset + 1 + (sizeof(uint16_t) / sizeof(INSTRUCTION));
|
return offset + 1 + (sizeof(uint16_t) / sizeof(INSTRUCTION));
|
||||||
}
|
}
|
||||||
|
|
||||||
int JumpInstruction(const char *name, CChunk *chunk, int offset, int dir) {
|
int JumpInstruction(const char *name, CChunk *chunk, int offset, int dir)
|
||||||
|
{
|
||||||
int jmp = ((int)readu16Chunk(chunk, offset + 1)) * dir;
|
int jmp = ((int)readu16Chunk(chunk, offset + 1)) * dir;
|
||||||
printf("%-16s [%05d] - jumps to %04d", name, jmp, offset + 3 + jmp);
|
printf("%-16s [%05d] - jumps to %04d", name, jmp, offset + 3 + jmp);
|
||||||
return offset + 1 + (sizeof(uint16_t) / sizeof(INSTRUCTION));
|
return offset + 1 + (sizeof(uint16_t) / sizeof(INSTRUCTION));
|
||||||
}
|
}
|
||||||
|
|
||||||
int u8u8OperandInstruction(const char *name, CChunk *chunk, int offset) {
|
int u8u8OperandInstruction(const char *name, CChunk *chunk, int offset)
|
||||||
printf("%-16s [%03d] [%03d]", name, readu8Chunk(chunk, offset + 1), readu8Chunk(chunk, offset + 2));
|
{
|
||||||
|
printf("%-16s [%03d] [%03d]", name, readu8Chunk(chunk, offset + 1),
|
||||||
|
readu8Chunk(chunk, offset + 2));
|
||||||
return offset + 3; // op + u8 + u8
|
return offset + 3; // op + u8 + u8
|
||||||
}
|
}
|
||||||
|
|
||||||
int u8u16OperandInstruction(const char *name, CChunk *chunk, int offset) {
|
int u8u16OperandInstruction(const char *name, CChunk *chunk, int offset)
|
||||||
printf("%-16s [%03d] [%05d]", name, readu8Chunk(chunk, offset + 1), readu16Chunk(chunk, offset + 2));
|
{
|
||||||
|
printf("%-16s [%03d] [%05d]", name, readu8Chunk(chunk, offset + 1),
|
||||||
|
readu16Chunk(chunk, offset + 2));
|
||||||
return offset + 4; // op + u8 + u16
|
return offset + 4; // op + u8 + u16
|
||||||
}
|
}
|
||||||
|
|
||||||
int u8u8u16OperandInstruction(const char *name, CChunk *chunk, int offset) {
|
int u8u8u16OperandInstruction(const char *name, CChunk *chunk, int offset)
|
||||||
printf("%-16s [%03d] [%03d] [%05d]", name, readu8Chunk(chunk, offset + 1), readu8Chunk(chunk, offset + 2), readu16Chunk(chunk, offset + 3));
|
{
|
||||||
|
printf("%-16s [%03d] [%03d] [%05d]", name, readu8Chunk(chunk, offset + 1),
|
||||||
|
readu8Chunk(chunk, offset + 2), readu16Chunk(chunk, offset + 3));
|
||||||
return offset + 5; // op + u8 + u8 + u16
|
return offset + 5; // op + u8 + u8 + u16
|
||||||
}
|
}
|
||||||
|
|
||||||
int constInstruction(const char *name, CChunk *chunk, int offset) {
|
int constInstruction(const char *name, CChunk *chunk, int offset)
|
||||||
|
{
|
||||||
int index = readu16Chunk(chunk, offset + 1);
|
int index = readu16Chunk(chunk, offset + 1);
|
||||||
printf("%-16s [%05d] - ", name, index);
|
printf("%-16s [%05d] - ", name, index);
|
||||||
CValue val = chunk->constants.values[index];
|
CValue val = chunk->constants.values[index];
|
||||||
@ -55,7 +68,8 @@ int constInstruction(const char *name, CChunk *chunk, int offset) {
|
|||||||
|
|
||||||
// public methods in the cdebug.h header
|
// public methods in the cdebug.h header
|
||||||
|
|
||||||
void disasmChunk(CChunk *chunk, const char *name, int indent) {
|
void disasmChunk(CChunk *chunk, const char *name, int indent)
|
||||||
|
{
|
||||||
printIndent(indent);
|
printIndent(indent);
|
||||||
printf("===[[ disasm for %s ]]===\n", name);
|
printf("===[[ disasm for %s ]]===\n", name);
|
||||||
|
|
||||||
@ -65,7 +79,8 @@ void disasmChunk(CChunk *chunk, const char *name, int indent) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int disasmInstr(CChunk *chunk, int offset, int indent) {
|
int disasmInstr(CChunk *chunk, int offset, int indent)
|
||||||
|
{
|
||||||
printIndent(indent);
|
printIndent(indent);
|
||||||
printf("%04d ", offset);
|
printf("%04d ", offset);
|
||||||
|
|
||||||
@ -79,131 +94,131 @@ int disasmInstr(CChunk *chunk, int offset, int indent) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch (i) {
|
switch (i) {
|
||||||
case OP_LOADCONST:
|
case OP_LOADCONST:
|
||||||
return constInstruction("OP_LOADCONST", chunk, offset);
|
return constInstruction("OP_LOADCONST", chunk, offset);
|
||||||
case OP_SETGLOBAL:
|
case OP_SETGLOBAL:
|
||||||
return constInstruction("OP_SETGLOBAL", chunk, offset);
|
return constInstruction("OP_SETGLOBAL", chunk, offset);
|
||||||
case OP_GETGLOBAL:
|
case OP_GETGLOBAL:
|
||||||
return constInstruction("OP_GETGLOBAL", chunk, offset);
|
return constInstruction("OP_GETGLOBAL", chunk, offset);
|
||||||
case OP_SETLOCAL:
|
case OP_SETLOCAL:
|
||||||
return u8OperandInstruction("OP_SETLOCAL", chunk, offset);
|
return u8OperandInstruction("OP_SETLOCAL", chunk, offset);
|
||||||
case OP_GETLOCAL:
|
case OP_GETLOCAL:
|
||||||
return u8OperandInstruction("OP_GETLOCAL", chunk, offset);
|
return u8OperandInstruction("OP_GETLOCAL", chunk, offset);
|
||||||
case OP_SETUPVAL:
|
case OP_SETUPVAL:
|
||||||
return u8OperandInstruction("OP_SETUPVAL", chunk, offset);
|
return u8OperandInstruction("OP_SETUPVAL", chunk, offset);
|
||||||
case OP_GETUPVAL:
|
case OP_GETUPVAL:
|
||||||
return u8OperandInstruction("OP_GETUPVAL", chunk, offset);
|
return u8OperandInstruction("OP_GETUPVAL", chunk, offset);
|
||||||
case OP_PEJMP:
|
case OP_PEJMP:
|
||||||
return JumpInstruction("OP_PEJMP", chunk, offset, 1);
|
return JumpInstruction("OP_PEJMP", chunk, offset, 1);
|
||||||
case OP_EJMP:
|
case OP_EJMP:
|
||||||
return JumpInstruction("OP_EJMP", chunk, offset, 1);
|
return JumpInstruction("OP_EJMP", chunk, offset, 1);
|
||||||
case OP_JMP:
|
case OP_JMP:
|
||||||
return JumpInstruction("OP_JMP", chunk, offset, 1);
|
return JumpInstruction("OP_JMP", chunk, offset, 1);
|
||||||
case OP_JMPBACK:
|
case OP_JMPBACK:
|
||||||
return JumpInstruction("OP_JMPBACK", chunk, offset, -1);
|
return JumpInstruction("OP_JMPBACK", chunk, offset, -1);
|
||||||
case OP_POP:
|
case OP_POP:
|
||||||
return u8OperandInstruction("OP_POP", chunk, offset);
|
return u8OperandInstruction("OP_POP", chunk, offset);
|
||||||
case OP_CALL:
|
case OP_CALL:
|
||||||
return u8u8OperandInstruction("OP_CALL", chunk, offset);
|
return u8u8OperandInstruction("OP_CALL", chunk, offset);
|
||||||
case OP_CLOSURE: {
|
case OP_CLOSURE: {
|
||||||
int index = readu16Chunk(chunk, offset + 1);
|
int index = readu16Chunk(chunk, offset + 1);
|
||||||
printf("%-16s [%05d] - ", "OP_CLOSURE", index);
|
printf("%-16s [%05d] - ", "OP_CLOSURE", index);
|
||||||
CValue val = chunk->constants.values[index];
|
CValue val = chunk->constants.values[index];
|
||||||
CObjFunction *cobjFunc = (CObjFunction*)cosmoV_readRef(val);
|
CObjFunction *cobjFunc = (CObjFunction *)cosmoV_readRef(val);
|
||||||
offset += 3; // we consumed the opcode + u16
|
offset += 3; // we consumed the opcode + u16
|
||||||
|
|
||||||
printValue(val);
|
printValue(val);
|
||||||
printf("\n");
|
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// print the chunk
|
// list the upvalues/locals that are captured
|
||||||
disasmChunk(&cobjFunc->chunk, cobjFunc->name == NULL ? UNNAMEDCHUNK : cobjFunc->name->str, indent+1);
|
for (int i = 0; i < cobjFunc->upvals; i++) {
|
||||||
return offset;
|
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;
|
return 1;
|
||||||
}
|
}
|
||||||
|
480
src/clex.c
480
src/clex.c
@ -1,72 +1,81 @@
|
|||||||
#include "clex.h"
|
#include "clex.h"
|
||||||
|
|
||||||
#include "cmem.h"
|
#include "cmem.h"
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
CReservedWord reservedWords[] = {
|
CReservedWord reservedWords[] = {
|
||||||
{TOKEN_AND, "and", 3},
|
{ TOKEN_AND, "and", 3},
|
||||||
{TOKEN_BREAK, "break", 5},
|
{ TOKEN_BREAK, "break", 5},
|
||||||
{TOKEN_CONTINUE, "continue", 8},
|
{TOKEN_CONTINUE, "continue", 8},
|
||||||
{TOKEN_DO, "do", 2},
|
{ TOKEN_DO, "do", 2},
|
||||||
{TOKEN_ELSE, "else", 4},
|
{ TOKEN_ELSE, "else", 4},
|
||||||
{TOKEN_ELSEIF, "elseif", 6},
|
{ TOKEN_ELSEIF, "elseif", 6},
|
||||||
{TOKEN_END, "end", 3},
|
{ TOKEN_END, "end", 3},
|
||||||
{TOKEN_FALSE, "false", 5},
|
{ TOKEN_FALSE, "false", 5},
|
||||||
{TOKEN_FOR, "for", 3},
|
{ TOKEN_FOR, "for", 3},
|
||||||
{TOKEN_FUNCTION, "function", 8},
|
{TOKEN_FUNCTION, "function", 8},
|
||||||
{TOKEN_IF, "if", 2},
|
{ TOKEN_IF, "if", 2},
|
||||||
{TOKEN_IN, "in", 2},
|
{ TOKEN_IN, "in", 2},
|
||||||
{TOKEN_LOCAL, "local", 5},
|
{ TOKEN_LOCAL, "local", 5},
|
||||||
{TOKEN_NIL, "nil", 3},
|
{ TOKEN_NIL, "nil", 3},
|
||||||
{TOKEN_NOT, "not", 3},
|
{ TOKEN_NOT, "not", 3},
|
||||||
{TOKEN_OR, "or", 2},
|
{ TOKEN_OR, "or", 2},
|
||||||
{TOKEN_PROTO, "proto", 5},
|
{ TOKEN_PROTO, "proto", 5},
|
||||||
{TOKEN_RETURN, "return", 6},
|
{ TOKEN_RETURN, "return", 6},
|
||||||
{TOKEN_THEN, "then", 4},
|
{ TOKEN_THEN, "then", 4},
|
||||||
{TOKEN_TRUE, "true", 4},
|
{ TOKEN_TRUE, "true", 4},
|
||||||
{TOKEN_VAR, "var", 3},
|
{ TOKEN_VAR, "var", 3},
|
||||||
{TOKEN_WHILE, "while", 5}
|
{ TOKEN_WHILE, "while", 5}
|
||||||
};
|
};
|
||||||
|
|
||||||
// returns true if current token is a heap allocated buffer
|
// returns true if current token is a heap allocated buffer
|
||||||
static bool isBuffer(CLexState *state) {
|
static bool isBuffer(CLexState *state)
|
||||||
return state->buffer != NULL;
|
{
|
||||||
|
return state->buffer != NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// marks the current token as heap allocated & allocates the buffer
|
// marks the current token as heap allocated & allocates the buffer
|
||||||
static void makeBuffer(CLexState *state) {
|
static void makeBuffer(CLexState *state)
|
||||||
state->buffer = cosmoM_xmalloc(state->cstate, sizeof(char) * 32); // start with a 32 character long buffer
|
{
|
||||||
|
state->buffer =
|
||||||
|
cosmoM_xmalloc(state->cstate, sizeof(char) * 32); // start with a 32 character long buffer
|
||||||
state->bufCount = 0;
|
state->bufCount = 0;
|
||||||
state->bufCap = 32;
|
state->bufCap = 32;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void resetBuffer(CLexState *state) {
|
static void resetBuffer(CLexState *state)
|
||||||
|
{
|
||||||
state->buffer = NULL;
|
state->buffer = NULL;
|
||||||
state->bufCount = 0;
|
state->bufCount = 0;
|
||||||
state->bufCap = 0;
|
state->bufCap = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// cancels the token heap buffer and frees it
|
// cancels the token heap buffer and frees it
|
||||||
static void freeBuffer(CLexState *state) {
|
static void freeBuffer(CLexState *state)
|
||||||
|
{
|
||||||
cosmoM_freearray(state->cstate, char, state->buffer, state->bufCap);
|
cosmoM_freearray(state->cstate, char, state->buffer, state->bufCap);
|
||||||
|
|
||||||
resetBuffer(state);
|
resetBuffer(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
// adds character to buffer
|
// adds character to buffer
|
||||||
static void appendBuffer(CLexState *state, char c) {
|
static void appendBuffer(CLexState *state, char c)
|
||||||
|
{
|
||||||
cosmoM_growarray(state->cstate, char, state->buffer, state->bufCount, state->bufCap);
|
cosmoM_growarray(state->cstate, char, state->buffer, state->bufCount, state->bufCap);
|
||||||
|
|
||||||
state->buffer[state->bufCount++] = c;
|
state->buffer[state->bufCount++] = c;
|
||||||
}
|
}
|
||||||
|
|
||||||
// saves the current character to the buffer, grows the buffer as needed
|
// saves the current character to the buffer, grows the buffer as needed
|
||||||
static void saveBuffer(CLexState *state) {
|
static void saveBuffer(CLexState *state)
|
||||||
|
{
|
||||||
appendBuffer(state, *state->currentChar);
|
appendBuffer(state, *state->currentChar);
|
||||||
}
|
}
|
||||||
|
|
||||||
// resets the lex state buffer & returns the allocated buffer as a null terminated string
|
// resets the lex state buffer & returns the allocated buffer as a null terminated string
|
||||||
static char *cutBuffer(CLexState *state, int *length) {
|
static char *cutBuffer(CLexState *state, int *length)
|
||||||
|
{
|
||||||
// append the null terminator
|
// append the null terminator
|
||||||
appendBuffer(state, '\0');
|
appendBuffer(state, '\0');
|
||||||
|
|
||||||
@ -84,7 +93,8 @@ static char *cutBuffer(CLexState *state, int *length) {
|
|||||||
return cosmoM_reallocate(state->cstate, buf, cap, count);
|
return cosmoM_reallocate(state->cstate, buf, cap, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
static CToken makeToken(CLexState *state, CTokenType type) {
|
static CToken makeToken(CLexState *state, CTokenType type)
|
||||||
|
{
|
||||||
CToken token;
|
CToken token;
|
||||||
token.type = type;
|
token.type = type;
|
||||||
token.line = state->line;
|
token.line = state->line;
|
||||||
@ -95,38 +105,43 @@ static CToken makeToken(CLexState *state, CTokenType type) {
|
|||||||
token.start = state->startChar;
|
token.start = state->startChar;
|
||||||
token.length = state->currentChar - state->startChar; // delta between start & current
|
token.length = state->currentChar - state->startChar; // delta between start & current
|
||||||
}
|
}
|
||||||
|
|
||||||
state->lastType = type;
|
state->lastType = type;
|
||||||
|
|
||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
static CToken makeError(CLexState *state, const char *msg) {
|
static CToken makeError(CLexState *state, const char *msg)
|
||||||
|
{
|
||||||
CToken token;
|
CToken token;
|
||||||
token.type = TOKEN_ERROR;
|
token.type = TOKEN_ERROR;
|
||||||
token.start = (char*)msg;
|
token.start = (char *)msg;
|
||||||
token.length = strlen(msg);
|
token.length = strlen(msg);
|
||||||
token.line = state->line;
|
token.line = state->line;
|
||||||
|
|
||||||
if (isBuffer(state))
|
if (isBuffer(state))
|
||||||
freeBuffer(state);
|
freeBuffer(state);
|
||||||
|
|
||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool isEnd(CLexState *state) {
|
static inline bool isEnd(CLexState *state)
|
||||||
|
{
|
||||||
return *state->currentChar == '\0';
|
return *state->currentChar == '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool isNumerical(char c) {
|
static inline bool isNumerical(char c)
|
||||||
|
{
|
||||||
return c >= '0' && c <= '9';
|
return c >= '0' && c <= '9';
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool isAlpha(char c) {
|
static bool isAlpha(char c)
|
||||||
|
{
|
||||||
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'; // identifiers can have '_'
|
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'; // identifiers can have '_'
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool match(CLexState *state, char expected) {
|
static bool match(CLexState *state, char expected)
|
||||||
|
{
|
||||||
if (isEnd(state) || *state->currentChar != expected)
|
if (isEnd(state) || *state->currentChar != expected)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -135,35 +150,41 @@ static bool match(CLexState *state, char expected) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
char peek(CLexState *state) {
|
char peek(CLexState *state)
|
||||||
|
{
|
||||||
return *state->currentChar;
|
return *state->currentChar;
|
||||||
}
|
}
|
||||||
|
|
||||||
static char peekNext(CLexState *state) {
|
static char peekNext(CLexState *state)
|
||||||
if (isEnd(state))
|
{
|
||||||
|
if (isEnd(state))
|
||||||
return '\0';
|
return '\0';
|
||||||
|
|
||||||
return state->currentChar[1];
|
return state->currentChar[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
char next(CLexState *state) {
|
char next(CLexState *state)
|
||||||
|
{
|
||||||
if (isEnd(state))
|
if (isEnd(state))
|
||||||
return '\0'; // return a null terminator
|
return '\0'; // return a null terminator
|
||||||
state->currentChar++;
|
state->currentChar++;
|
||||||
return state->currentChar[-1];
|
return state->currentChar[-1];
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isHex(char c) {
|
bool isHex(char c)
|
||||||
|
{
|
||||||
return isNumerical(c) || ('A' <= c && 'F' >= c) || ('a' <= c && 'f' >= c);
|
return isNumerical(c) || ('A' <= c && 'F' >= c) || ('a' <= c && 'f' >= c);
|
||||||
}
|
}
|
||||||
|
|
||||||
CTokenType identifierType(CLexState *state) {
|
CTokenType identifierType(CLexState *state)
|
||||||
|
{
|
||||||
int length = state->currentChar - state->startChar;
|
int length = state->currentChar - state->startChar;
|
||||||
|
|
||||||
// check against reserved word list
|
// check against reserved word list
|
||||||
for (size_t i = 0; i < sizeof(reservedWords) / sizeof(CReservedWord); i++) {
|
for (size_t i = 0; i < sizeof(reservedWords) / sizeof(CReservedWord); i++) {
|
||||||
// it matches the reserved word
|
// it matches the reserved word
|
||||||
if (reservedWords[i].len == length && memcmp(state->startChar, reservedWords[i].word, length) == 0)
|
if (reservedWords[i].len == length &&
|
||||||
|
memcmp(state->startChar, reservedWords[i].word, length) == 0)
|
||||||
return reservedWords[i].type;
|
return reservedWords[i].type;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -171,161 +192,178 @@ CTokenType identifierType(CLexState *state) {
|
|||||||
return TOKEN_IDENTIFIER;
|
return TOKEN_IDENTIFIER;
|
||||||
}
|
}
|
||||||
|
|
||||||
void skipWhitespace(CLexState *state) {
|
void skipWhitespace(CLexState *state)
|
||||||
|
{
|
||||||
while (true) {
|
while (true) {
|
||||||
char c = peek(state);
|
char c = peek(state);
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case '\n': // mark new line
|
case '\n': // mark new line
|
||||||
state->line++;
|
state->line++;
|
||||||
case ' ':
|
case ' ':
|
||||||
case '\r':
|
case '\r':
|
||||||
case '\t':
|
case '\t':
|
||||||
next(state); // consume the whitespace
|
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;
|
break;
|
||||||
case '/': // consume comments
|
} else if (peekNext(state) == '*') { // multiline comments
|
||||||
if (peekNext(state) == '/') {
|
while (!isEnd(state) &&
|
||||||
// skip to next line (also let \n be consumed on the next iteration to properly handle that)
|
!(peek(state) == '*' &&
|
||||||
while (!isEnd(state) && peek(state) != '\n') // if it's not a newline or the end of the source
|
peekNext(state) ==
|
||||||
next(state);
|
'/')) // if it's the end of the comment or the end of the source
|
||||||
|
|
||||||
// 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);
|
|
||||||
next(state);
|
next(state);
|
||||||
|
|
||||||
// keep consuming whitespace
|
// consume the '*/'
|
||||||
break;
|
next(state);
|
||||||
}
|
next(state);
|
||||||
return; // it's a TOKEN_SLASH, let the main body handle that
|
|
||||||
default: // it's no longer whitespace, return!
|
// keep consuming whitespace
|
||||||
return;
|
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
|
makeBuffer(state); // buffer mode
|
||||||
while (peek(state) != '"' && !isEnd(state)) {
|
while (peek(state) != '"' && !isEnd(state)) {
|
||||||
switch (peek(state)) {
|
switch (peek(state)) {
|
||||||
case '\n': // strings can't stretch across lines
|
case '\n': // strings can't stretch across lines
|
||||||
return makeError(state, "Unterminated string!");
|
return makeError(state, "Unterminated string!");
|
||||||
case '\\': { // special character
|
case '\\': { // special character
|
||||||
next(state); // consume the '\' character
|
next(state); // consume the '\' character
|
||||||
|
|
||||||
switch (peek(state)) {
|
switch (peek(state)) {
|
||||||
case 'r': case 'n': appendBuffer(state, '\n'); break;
|
case 'r':
|
||||||
case 't': appendBuffer(state, '\t'); break;
|
case 'n':
|
||||||
case '\\': appendBuffer(state, '\\'); break;
|
appendBuffer(state, '\n');
|
||||||
case '"': appendBuffer(state, '"'); break;
|
break;
|
||||||
case 'x': // hexadecimal character encoding
|
case 't':
|
||||||
next(state); // skip 'x'
|
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))) {
|
if (isHex(peek(state))) {
|
||||||
char *numStart = state->currentChar;
|
char *numStart = state->currentChar;
|
||||||
|
|
||||||
// consume the hexnum
|
// consume the hexnum
|
||||||
while (isHex(peek(state)))
|
while (isHex(peek(state)))
|
||||||
next(state);
|
next(state);
|
||||||
state->currentChar--; // since next() is called after
|
state->currentChar--; // since next() is called after
|
||||||
|
|
||||||
unsigned int num = (unsigned int)strtoul(numStart, NULL, 16);
|
|
||||||
|
|
||||||
if (num > 255) // sanity check
|
unsigned int num = (unsigned int)strtoul(numStart, NULL, 16);
|
||||||
return makeError(state, "Character out of range! > 255!");
|
|
||||||
|
|
||||||
appendBuffer(state, num);
|
if (num > 255) // sanity check
|
||||||
break;
|
return makeError(state, "Character out of range! > 255!");
|
||||||
}
|
|
||||||
|
|
||||||
return makeError(state, "Unknown hexadecimal character encoding!");
|
|
||||||
case 'b': // binary character encoding
|
|
||||||
next(state); // skip 'b'
|
|
||||||
|
|
||||||
if (peek(state) == '0' || peek(state) == '1') {
|
appendBuffer(state, num);
|
||||||
char *numStart = state->currentChar;
|
break;
|
||||||
|
|
||||||
// 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?
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
next(state); // consume special character
|
return makeError(state, "Unknown hexadecimal character encoding!");
|
||||||
break;
|
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: {
|
default: {
|
||||||
saveBuffer(state); // save the character!
|
if (isNumerical(peek(state))) {
|
||||||
next(state); // consume
|
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))
|
if (isEnd(state))
|
||||||
return makeError(state, "Unterminated string!");
|
return makeError(state, "Unterminated string!");
|
||||||
|
|
||||||
next(state); // consume closing quote
|
next(state); // consume closing quote
|
||||||
return makeToken(state, TOKEN_STRING);
|
return makeToken(state, TOKEN_STRING);
|
||||||
}
|
}
|
||||||
|
|
||||||
CToken parseNumber(CLexState *state) {
|
CToken parseNumber(CLexState *state)
|
||||||
|
{
|
||||||
switch (peek(state)) {
|
switch (peek(state)) {
|
||||||
case 'x': // hexadecimal number
|
case 'x': // hexadecimal number
|
||||||
|
next(state);
|
||||||
|
|
||||||
|
while (isHex(peek(state)))
|
||||||
next(state);
|
next(state);
|
||||||
|
|
||||||
while (isHex(peek(state)))
|
return makeToken(state, TOKEN_HEXNUMBER);
|
||||||
next(state);
|
case 'b': // binary number
|
||||||
|
next(state);
|
||||||
|
|
||||||
return makeToken(state, TOKEN_HEXNUMBER);
|
while (peek(state) == '0' || peek(state) == '1')
|
||||||
case 'b': // binary number
|
|
||||||
next(state);
|
next(state);
|
||||||
|
|
||||||
while (peek(state) == '0' || peek(state) == '1')
|
return makeToken(state, TOKEN_BINNUMBER);
|
||||||
next(state);
|
default: // it's a one digit number!!!!!
|
||||||
|
if (!isNumerical(peek(state)) && !(peek(state) == '.'))
|
||||||
return makeToken(state, TOKEN_BINNUMBER);
|
return makeToken(state, TOKEN_NUMBER);
|
||||||
default: // it's a one digit number!!!!!
|
// if it is a number, fall through and parse normally
|
||||||
if (!isNumerical(peek(state)) && !(peek(state) == '.'))
|
|
||||||
return makeToken(state, TOKEN_NUMBER);
|
|
||||||
// if it is a number, fall through and parse normally
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// consume number
|
// consume number
|
||||||
while (isNumerical(peek(state))) {
|
while (isNumerical(peek(state))) {
|
||||||
next(state);
|
next(state);
|
||||||
@ -342,18 +380,20 @@ CToken parseNumber(CLexState *state) {
|
|||||||
return makeToken(state, TOKEN_NUMBER);
|
return makeToken(state, TOKEN_NUMBER);
|
||||||
}
|
}
|
||||||
|
|
||||||
CToken parseIdentifier(CLexState *state) {
|
CToken parseIdentifier(CLexState *state)
|
||||||
|
{
|
||||||
// read literal
|
// read literal
|
||||||
while ((isAlpha(peek(state)) || isNumerical(peek(state))) && !isEnd(state))
|
while ((isAlpha(peek(state)) || isNumerical(peek(state))) && !isEnd(state))
|
||||||
next(state);
|
next(state);
|
||||||
|
|
||||||
return makeToken(state, identifierType(state)); // is it a reserved word?
|
return makeToken(state, identifierType(state)); // is it a reserved word?
|
||||||
}
|
}
|
||||||
|
|
||||||
CLexState *cosmoL_newLexState(CState *cstate, const char *source) {
|
CLexState *cosmoL_newLexState(CState *cstate, const char *source)
|
||||||
|
{
|
||||||
CLexState *state = cosmoM_xmalloc(cstate, sizeof(CLexState));
|
CLexState *state = cosmoM_xmalloc(cstate, sizeof(CLexState));
|
||||||
state->startChar = (char*)source;
|
state->startChar = (char *)source;
|
||||||
state->currentChar = (char*)source;
|
state->currentChar = (char *)source;
|
||||||
state->line = 1;
|
state->line = 1;
|
||||||
state->lastLine = 0;
|
state->lastLine = 0;
|
||||||
state->lastType = TOKEN_ERROR;
|
state->lastType = TOKEN_ERROR;
|
||||||
@ -364,58 +404,82 @@ CLexState *cosmoL_newLexState(CState *cstate, const char *source) {
|
|||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
void cosmoL_freeLexState(CState *state, CLexState *lstate) {
|
void cosmoL_freeLexState(CState *state, CLexState *lstate)
|
||||||
|
{
|
||||||
cosmoM_free(state, CLexState, lstate);
|
cosmoM_free(state, CLexState, lstate);
|
||||||
}
|
}
|
||||||
|
|
||||||
CToken cosmoL_scanToken(CLexState *state) {
|
CToken cosmoL_scanToken(CLexState *state)
|
||||||
|
{
|
||||||
skipWhitespace(state);
|
skipWhitespace(state);
|
||||||
|
|
||||||
state->startChar = state->currentChar;
|
state->startChar = state->currentChar;
|
||||||
|
|
||||||
if (isEnd(state))
|
if (isEnd(state))
|
||||||
return makeToken(state, TOKEN_EOF);
|
return makeToken(state, TOKEN_EOF);
|
||||||
|
|
||||||
char c = next(state);
|
char c = next(state);
|
||||||
|
|
||||||
switch (c) {
|
switch (c) {
|
||||||
// single character tokens
|
// single character tokens
|
||||||
case '(': return makeToken(state, TOKEN_LEFT_PAREN);
|
case '(':
|
||||||
case ')': return makeToken(state, TOKEN_RIGHT_PAREN);
|
return makeToken(state, TOKEN_LEFT_PAREN);
|
||||||
case '{': return makeToken(state, TOKEN_LEFT_BRACE);
|
case ')':
|
||||||
case '}': return makeToken(state, TOKEN_RIGHT_BRACE);
|
return makeToken(state, TOKEN_RIGHT_PAREN);
|
||||||
case '[': return makeToken(state, TOKEN_LEFT_BRACKET);
|
case '{':
|
||||||
case ']': return makeToken(state, TOKEN_RIGHT_BRACKET);
|
return makeToken(state, TOKEN_LEFT_BRACE);
|
||||||
case ';': return makeToken(state, TOKEN_EOS);
|
case '}':
|
||||||
case ',': return makeToken(state, TOKEN_COMMA);
|
return makeToken(state, TOKEN_RIGHT_BRACE);
|
||||||
case ':': return makeToken(state, TOKEN_COLON);
|
case '[':
|
||||||
case '*': return makeToken(state, TOKEN_STAR);
|
return makeToken(state, TOKEN_LEFT_BRACKET);
|
||||||
case '%': return makeToken(state, TOKEN_PERCENT);
|
case ']':
|
||||||
case '^': return makeToken(state, TOKEN_CARROT);
|
return makeToken(state, TOKEN_RIGHT_BRACKET);
|
||||||
case '#': return makeToken(state, TOKEN_POUND);
|
case ';':
|
||||||
case '/': return makeToken(state, TOKEN_SLASH);
|
return makeToken(state, TOKEN_EOS);
|
||||||
// two character tokens
|
case ',':
|
||||||
case '+':
|
return makeToken(state, TOKEN_COMMA);
|
||||||
return match(state, '+') ? makeToken(state, TOKEN_PLUS_PLUS) : makeToken(state, TOKEN_PLUS);
|
case ':':
|
||||||
case '-':
|
return makeToken(state, TOKEN_COLON);
|
||||||
return match(state, '-') ? makeToken(state, TOKEN_MINUS_MINUS) : makeToken(state, TOKEN_MINUS);
|
case '*':
|
||||||
case '.':
|
return makeToken(state, TOKEN_STAR);
|
||||||
return match(state, '.') ? (match(state, '.') ? makeToken(state, TOKEN_DOT_DOT_DOT) : makeToken(state, TOKEN_DOT_DOT)) : makeToken(state, TOKEN_DOT);
|
case '%':
|
||||||
case '!':
|
return makeToken(state, TOKEN_PERCENT);
|
||||||
return match(state, '=') ? makeToken(state, TOKEN_BANG_EQUAL) : makeToken(state, TOKEN_BANG);
|
case '^':
|
||||||
case '=':
|
return makeToken(state, TOKEN_CARROT);
|
||||||
return match(state, '=') ? makeToken(state, TOKEN_EQUAL_EQUAL) : makeToken(state, TOKEN_EQUAL);
|
case '#':
|
||||||
case '>':
|
return makeToken(state, TOKEN_POUND);
|
||||||
return match(state, '=') ? makeToken(state, TOKEN_GREATER_EQUAL) : makeToken(state, TOKEN_GREATER);
|
case '/':
|
||||||
case '<':
|
return makeToken(state, TOKEN_SLASH);
|
||||||
return match(state, '=') ? makeToken(state, TOKEN_LESS_EQUAL) : makeToken(state, TOKEN_LESS);
|
// two character tokens
|
||||||
// literals
|
case '+':
|
||||||
case '"': return parseString(state);
|
return match(state, '+') ? makeToken(state, TOKEN_PLUS_PLUS) : makeToken(state, TOKEN_PLUS);
|
||||||
default:
|
case '-':
|
||||||
if (isNumerical(c))
|
return match(state, '-') ? makeToken(state, TOKEN_MINUS_MINUS)
|
||||||
return parseNumber(state);
|
: makeToken(state, TOKEN_MINUS);
|
||||||
if (isAlpha(c))
|
case '.':
|
||||||
return parseIdentifier(state);
|
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!");
|
return makeError(state, "Unknown symbol!");
|
||||||
|
19
src/clex.h
19
src/clex.h
@ -3,7 +3,8 @@
|
|||||||
|
|
||||||
#include "cosmo.h"
|
#include "cosmo.h"
|
||||||
|
|
||||||
typedef enum {
|
typedef enum
|
||||||
|
{
|
||||||
// single character tokens
|
// single character tokens
|
||||||
TOKEN_LEFT_PAREN,
|
TOKEN_LEFT_PAREN,
|
||||||
TOKEN_RIGHT_PAREN,
|
TOKEN_RIGHT_PAREN,
|
||||||
@ -72,26 +73,30 @@ typedef enum {
|
|||||||
TOKEN_EOF
|
TOKEN_EOF
|
||||||
} CTokenType;
|
} CTokenType;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct
|
||||||
|
{
|
||||||
CTokenType type;
|
CTokenType type;
|
||||||
const char *word;
|
const char *word;
|
||||||
int len;
|
int len;
|
||||||
} CReservedWord;
|
} CReservedWord;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct
|
||||||
|
{
|
||||||
CTokenType type;
|
CTokenType type;
|
||||||
char *start;
|
char *start;
|
||||||
int length;
|
int length;
|
||||||
int line;
|
int line;
|
||||||
} CToken;
|
} CToken;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct
|
||||||
|
{
|
||||||
char *currentChar;
|
char *currentChar;
|
||||||
char *startChar;
|
char *startChar;
|
||||||
char *buffer; // if non-NULL & bufCount > 0, token->start & token->length will be set to buffer & bufCount respectively
|
char *buffer; // if non-NULL & bufCount > 0, token->start & token->length will be set to buffer
|
||||||
|
// & bufCount respectively
|
||||||
size_t bufCount;
|
size_t bufCount;
|
||||||
size_t bufCap;
|
size_t bufCap;
|
||||||
int line; // current line
|
int line; // current line
|
||||||
int lastLine; // line of the previous consumed token
|
int lastLine; // line of the previous consumed token
|
||||||
bool isEnd;
|
bool isEnd;
|
||||||
CTokenType lastType;
|
CTokenType lastType;
|
||||||
|
214
src/cmem.c
214
src/cmem.c
@ -1,13 +1,15 @@
|
|||||||
#include "cmem.h"
|
#include "cmem.h"
|
||||||
#include "cstate.h"
|
|
||||||
#include "cvalue.h"
|
|
||||||
#include "ctable.h"
|
|
||||||
#include "cparse.h"
|
|
||||||
#include "cobj.h"
|
|
||||||
#include "cbaselib.h"
|
#include "cbaselib.h"
|
||||||
|
#include "cobj.h"
|
||||||
|
#include "cparse.h"
|
||||||
|
#include "cstate.h"
|
||||||
|
#include "ctable.h"
|
||||||
|
#include "cvalue.h"
|
||||||
|
|
||||||
// realloc wrapper
|
// realloc wrapper
|
||||||
void *cosmoM_reallocate(CState* state, void *buf, size_t oldSize, size_t newSize) {
|
void *cosmoM_reallocate(CState *state, void *buf, size_t oldSize, size_t newSize)
|
||||||
|
{
|
||||||
state->allocatedBytes += newSize - oldSize;
|
state->allocatedBytes += newSize - oldSize;
|
||||||
|
|
||||||
if (newSize == 0) { // it needs to be freed
|
if (newSize == 0) { // it needs to be freed
|
||||||
@ -19,11 +21,11 @@ void *cosmoM_reallocate(CState* state, void *buf, size_t oldSize, size_t newSize
|
|||||||
if (!(cosmoM_isFrozen(state)) && newSize > oldSize) {
|
if (!(cosmoM_isFrozen(state)) && newSize > oldSize) {
|
||||||
cosmoM_collectGarbage(state);
|
cosmoM_collectGarbage(state);
|
||||||
}
|
}
|
||||||
#ifdef GC_DEBUG
|
# ifdef GC_DEBUG
|
||||||
else {
|
else {
|
||||||
printf("GC event ignored! state frozen! [%d]\n", state->freezeGC);
|
printf("GC event ignored! state frozen! [%d]\n", state->freezeGC);
|
||||||
}
|
}
|
||||||
#endif
|
# endif
|
||||||
#else
|
#else
|
||||||
cosmoM_checkGarbage(state, 0);
|
cosmoM_checkGarbage(state, 0);
|
||||||
#endif
|
#endif
|
||||||
@ -39,7 +41,8 @@ void *cosmoM_reallocate(CState* state, void *buf, size_t oldSize, size_t newSize
|
|||||||
return newBuf;
|
return newBuf;
|
||||||
}
|
}
|
||||||
|
|
||||||
COSMO_API bool cosmoM_checkGarbage(CState *state, size_t needed) {
|
COSMO_API bool cosmoM_checkGarbage(CState *state, size_t needed)
|
||||||
|
{
|
||||||
if (!(cosmoM_isFrozen(state)) && state->allocatedBytes + needed > state->nextGC) {
|
if (!(cosmoM_isFrozen(state)) && state->allocatedBytes + needed > state->nextGC) {
|
||||||
cosmoM_collectGarbage(state); // cya lol
|
cosmoM_collectGarbage(state); // cya lol
|
||||||
return true;
|
return true;
|
||||||
@ -51,10 +54,11 @@ COSMO_API bool cosmoM_checkGarbage(CState *state, size_t needed) {
|
|||||||
void markObject(CState *state, CObj *obj);
|
void markObject(CState *state, CObj *obj);
|
||||||
void markValue(CState *state, CValue val);
|
void markValue(CState *state, CValue val);
|
||||||
|
|
||||||
void markTable(CState *state, CTable *tbl) {
|
void markTable(CState *state, CTable *tbl)
|
||||||
|
{
|
||||||
if (tbl->table == NULL) // table is still being initialized
|
if (tbl->table == NULL) // table is still being initialized
|
||||||
return;
|
return;
|
||||||
|
|
||||||
int cap = tbl->capacityMask + 1;
|
int cap = tbl->capacityMask + 1;
|
||||||
for (int i = 0; i < cap; i++) {
|
for (int i = 0; i < cap; i++) {
|
||||||
CTableEntry *entry = &tbl->table[i];
|
CTableEntry *entry = &tbl->table[i];
|
||||||
@ -64,14 +68,17 @@ void markTable(CState *state, CTable *tbl) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// frees white members from the table
|
// frees white members from the table
|
||||||
void tableRemoveWhite(CState *state, CTable *tbl) {
|
void tableRemoveWhite(CState *state, CTable *tbl)
|
||||||
|
{
|
||||||
if (tbl->table == NULL) // table is still being initialized
|
if (tbl->table == NULL) // table is still being initialized
|
||||||
return;
|
return;
|
||||||
|
|
||||||
int cap = tbl->capacityMask + 1;
|
int cap = tbl->capacityMask + 1;
|
||||||
for (int i = 0; i < cap; i++) {
|
for (int i = 0; i < cap; i++) {
|
||||||
CTableEntry *entry = &tbl->table[i];
|
CTableEntry *entry = &tbl->table[i];
|
||||||
if (IS_REF(entry->key) && !(cosmoV_readRef(entry->key))->isMarked) { // if the key is a object and it's white (unmarked), remove it from the table
|
if (IS_REF(entry->key) &&
|
||||||
|
!(cosmoV_readRef(entry->key))->isMarked) { // if the key is a object and it's white
|
||||||
|
// (unmarked), remove it from the table
|
||||||
cosmoT_remove(state, tbl, entry->key);
|
cosmoT_remove(state, tbl, entry->key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -79,7 +86,8 @@ void tableRemoveWhite(CState *state, CTable *tbl) {
|
|||||||
cosmoT_checkShrink(state, tbl); // recovers the memory we're no longer using
|
cosmoT_checkShrink(state, tbl); // recovers the memory we're no longer using
|
||||||
}
|
}
|
||||||
|
|
||||||
void markArray(CState *state, CValueArray *array) {
|
void markArray(CState *state, CValueArray *array)
|
||||||
|
{
|
||||||
for (size_t i = 0; i < array->count; i++) {
|
for (size_t i = 0; i < array->count; i++) {
|
||||||
markValue(state, array->values[i]);
|
markValue(state, array->values[i]);
|
||||||
}
|
}
|
||||||
@ -87,72 +95,74 @@ void markArray(CState *state, CValueArray *array) {
|
|||||||
|
|
||||||
// mark all references associated with the object
|
// mark all references associated with the object
|
||||||
// black = keep, white = discard
|
// black = keep, white = discard
|
||||||
void blackenObject(CState *state, CObj *obj) {
|
void blackenObject(CState *state, CObj *obj)
|
||||||
markObject(state, (CObj*)obj->proto);
|
{
|
||||||
|
markObject(state, (CObj *)obj->proto);
|
||||||
switch (obj->type) {
|
switch (obj->type) {
|
||||||
case COBJ_STRING:
|
case COBJ_STRING:
|
||||||
case COBJ_CFUNCTION:
|
case COBJ_CFUNCTION:
|
||||||
// stubbed
|
// stubbed
|
||||||
break;
|
break;
|
||||||
case COBJ_OBJECT: {
|
case COBJ_OBJECT: {
|
||||||
// mark everything this object is keeping track of
|
// mark everything this object is keeping track of
|
||||||
CObjObject *cobj = (CObjObject*)obj;
|
CObjObject *cobj = (CObjObject *)obj;
|
||||||
markTable(state, &cobj->tbl);
|
markTable(state, &cobj->tbl);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case COBJ_TABLE: { // tables are just wrappers for CTable
|
case COBJ_TABLE: { // tables are just wrappers for CTable
|
||||||
CObjTable *tbl = (CObjTable*)obj;
|
CObjTable *tbl = (CObjTable *)obj;
|
||||||
markTable(state, &tbl->tbl);
|
markTable(state, &tbl->tbl);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case COBJ_UPVALUE: {
|
case COBJ_UPVALUE: {
|
||||||
markValue(state, ((CObjUpval*)obj)->closed);
|
markValue(state, ((CObjUpval *)obj)->closed);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case COBJ_FUNCTION: {
|
case COBJ_FUNCTION: {
|
||||||
CObjFunction *func = (CObjFunction*)obj;
|
CObjFunction *func = (CObjFunction *)obj;
|
||||||
markObject(state, (CObj*)func->name);
|
markObject(state, (CObj *)func->name);
|
||||||
markObject(state, (CObj*)func->module);
|
markObject(state, (CObj *)func->module);
|
||||||
markArray(state, &func->chunk.constants);
|
markArray(state, &func->chunk.constants);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case COBJ_METHOD: {
|
case COBJ_METHOD: {
|
||||||
CObjMethod *method = (CObjMethod*)obj;
|
CObjMethod *method = (CObjMethod *)obj;
|
||||||
markValue(state, method->func);
|
markValue(state, method->func);
|
||||||
markObject(state, (CObj*)method->obj);
|
markObject(state, (CObj *)method->obj);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case COBJ_ERROR: {
|
case COBJ_ERROR: {
|
||||||
CObjError *err = (CObjError*)obj;
|
CObjError *err = (CObjError *)obj;
|
||||||
markValue(state, err->err);
|
markValue(state, err->err);
|
||||||
|
|
||||||
// mark callframes
|
// mark callframes
|
||||||
for (int i = 0; i < err->frameCount; i++)
|
for (int i = 0; i < err->frameCount; i++)
|
||||||
markObject(state, (CObj*)err->frames[i].closure);
|
markObject(state, (CObj *)err->frames[i].closure);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
case COBJ_CLOSURE: {
|
||||||
|
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
|
break;
|
||||||
for (int i = 0; i < closure->upvalueCount; i++) {
|
}
|
||||||
markObject(state, (CObj*)closure->upvalues[i]);
|
default:
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
#ifdef GC_DEBUG
|
#ifdef GC_DEBUG
|
||||||
printf("Unknown type in blackenObject with %p, type %d\n", (void*)obj, obj->type);
|
printf("Unknown type in blackenObject with %p, type %d\n", (void *)obj, obj->type);
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void markObject(CState *state, CObj *obj) {
|
void markObject(CState *state, CObj *obj)
|
||||||
|
{
|
||||||
if (obj == NULL || obj->isMarked) // skip if NULL or already marked
|
if (obj == NULL || obj->isMarked) // skip if NULL or already marked
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -165,33 +175,37 @@ void markObject(CState *state, CObj *obj) {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// they don't need to be added to the gray stack, they don't reference any other CObjs
|
// 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;
|
return;
|
||||||
|
|
||||||
// we can use cosmoM_growarray because we lock the GC when we entered in cosmoM_collectGarbage
|
// we can use cosmoM_growarray because we lock the GC when we entered in cosmoM_collectGarbage
|
||||||
cosmoM_growarray(state, CObj*, state->grayStack.array, state->grayStack.count, state->grayStack.capacity);
|
cosmoM_growarray(state, CObj *, state->grayStack.array, state->grayStack.count,
|
||||||
|
state->grayStack.capacity);
|
||||||
|
|
||||||
state->grayStack.array[state->grayStack.count++] = obj;
|
state->grayStack.array[state->grayStack.count++] = obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
void markValue(CState *state, CValue val) {
|
void markValue(CState *state, CValue val)
|
||||||
|
{
|
||||||
if (IS_REF(val))
|
if (IS_REF(val))
|
||||||
markObject(state, cosmoV_readRef(val));
|
markObject(state, cosmoV_readRef(val));
|
||||||
}
|
}
|
||||||
|
|
||||||
// trace our gray references
|
// trace our gray references
|
||||||
void traceGrays(CState *state) {
|
void traceGrays(CState *state)
|
||||||
|
{
|
||||||
while (state->grayStack.count > 0) {
|
while (state->grayStack.count > 0) {
|
||||||
CObj* obj = state->grayStack.array[--state->grayStack.count];
|
CObj *obj = state->grayStack.array[--state->grayStack.count];
|
||||||
blackenObject(state, obj);
|
blackenObject(state, obj);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void sweep(CState *state) {
|
void sweep(CState *state)
|
||||||
|
{
|
||||||
CObj *prev = NULL;
|
CObj *prev = NULL;
|
||||||
CObj *object = state->objects;
|
CObj *object = state->objects;
|
||||||
while (object != NULL) {
|
while (object != NULL) {
|
||||||
if (object->isMarked) { // skip over it
|
if (object->isMarked) { // skip over it
|
||||||
object->isMarked = false; // rest to white
|
object->isMarked = false; // rest to white
|
||||||
prev = object;
|
prev = object;
|
||||||
object = object->next;
|
object = object->next;
|
||||||
@ -210,7 +224,8 @@ void sweep(CState *state) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void markUserRoots(CState *state) {
|
void markUserRoots(CState *state)
|
||||||
|
{
|
||||||
CObj *root = state->userRoots;
|
CObj *root = state->userRoots;
|
||||||
|
|
||||||
// traverse userRoots and mark all the object
|
// traverse userRoots and mark all the object
|
||||||
@ -220,7 +235,8 @@ void markUserRoots(CState *state) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void markRoots(CState *state) {
|
void markRoots(CState *state)
|
||||||
|
{
|
||||||
// mark all values on the stack
|
// mark all values on the stack
|
||||||
for (StkPtr value = state->stack; value < state->top; value++) {
|
for (StkPtr value = state->stack; value < state->top; value++) {
|
||||||
markValue(state, *value);
|
markValue(state, *value);
|
||||||
@ -228,33 +244,34 @@ void markRoots(CState *state) {
|
|||||||
|
|
||||||
// mark all active callframe closures
|
// mark all active callframe closures
|
||||||
for (int i = 0; i < state->frameCount; i++) {
|
for (int i = 0; i < state->frameCount; i++) {
|
||||||
markObject(state, (CObj*)state->callFrame[i].closure);
|
markObject(state, (CObj *)state->callFrame[i].closure);
|
||||||
}
|
}
|
||||||
|
|
||||||
// mark all open upvalues
|
// mark all open upvalues
|
||||||
for (CObjUpval *upvalue = state->openUpvalues; upvalue != NULL; upvalue = upvalue->next) {
|
for (CObjUpval *upvalue = state->openUpvalues; upvalue != NULL; upvalue = upvalue->next) {
|
||||||
markObject(state, (CObj*)upvalue);
|
markObject(state, (CObj *)upvalue);
|
||||||
}
|
}
|
||||||
|
|
||||||
markObject(state, (CObj*)state->globals);
|
markObject(state, (CObj *)state->globals);
|
||||||
|
|
||||||
// mark all internal strings
|
// mark all internal strings
|
||||||
for (int i = 0; i < ISTRING_MAX; i++)
|
for (int i = 0; i < ISTRING_MAX; i++)
|
||||||
markObject(state, (CObj*)state->iStrings[i]);
|
markObject(state, (CObj *)state->iStrings[i]);
|
||||||
|
|
||||||
// mark the user defined roots
|
// mark the user defined roots
|
||||||
markUserRoots(state);
|
markUserRoots(state);
|
||||||
|
|
||||||
// mark other misc. internally reserved objects
|
// mark other misc. internally reserved objects
|
||||||
markObject(state, (CObj*)state->error);
|
markObject(state, (CObj *)state->error);
|
||||||
|
|
||||||
for (int i = 0; i < COBJ_MAX; i++)
|
for (int i = 0; i < COBJ_MAX; i++)
|
||||||
markObject(state, (CObj*)state->protoObjects[i]);
|
markObject(state, (CObj *)state->protoObjects[i]);
|
||||||
|
|
||||||
traceGrays(state);
|
traceGrays(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
COSMO_API void cosmoM_collectGarbage(CState *state) {
|
COSMO_API void cosmoM_collectGarbage(CState *state)
|
||||||
|
{
|
||||||
#ifdef GC_DEBUG
|
#ifdef GC_DEBUG
|
||||||
printf("-- GC start\n");
|
printf("-- GC start\n");
|
||||||
size_t start = state->allocatedBytes;
|
size_t start = state->allocatedBytes;
|
||||||
@ -263,41 +280,48 @@ COSMO_API void cosmoM_collectGarbage(CState *state) {
|
|||||||
|
|
||||||
markRoots(state);
|
markRoots(state);
|
||||||
|
|
||||||
tableRemoveWhite(state, &state->strings); // make sure we aren't referencing any strings that are about to be freed
|
tableRemoveWhite(
|
||||||
|
state,
|
||||||
|
&state->strings); // make sure we aren't referencing any strings that are about to be freed
|
||||||
// now finally, free all the unmarked objects
|
// now finally, free all the unmarked objects
|
||||||
sweep(state);
|
sweep(state);
|
||||||
|
|
||||||
// set our next GC event
|
// set our next GC event
|
||||||
cosmoM_updateThreshhold(state);
|
cosmoM_updateThreshhold(state);
|
||||||
|
|
||||||
state->freezeGC--; // we don't want to use cosmoM_unfreezeGC because that might trigger a GC event (if GC_STRESS is defined)
|
state->freezeGC--; // we don't want to use cosmoM_unfreezeGC because that might trigger a GC
|
||||||
|
// event (if GC_STRESS is defined)
|
||||||
#ifdef GC_DEBUG
|
#ifdef GC_DEBUG
|
||||||
printf("-- GC end, reclaimed %ld bytes (started at %ld, ended at %ld), next garbage collection scheduled at %ld bytes\n",
|
printf("-- GC end, reclaimed %ld bytes (started at %ld, ended at %ld), next garbage collection "
|
||||||
start - state->allocatedBytes, start, state->allocatedBytes, state->nextGC);
|
"scheduled at %ld bytes\n",
|
||||||
|
start - state->allocatedBytes, start, state->allocatedBytes, state->nextGC);
|
||||||
getchar(); // pauses execution
|
getchar(); // pauses execution
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
COSMO_API void cosmoM_updateThreshhold(CState *state) {
|
COSMO_API void cosmoM_updateThreshhold(CState *state)
|
||||||
|
{
|
||||||
state->nextGC = state->allocatedBytes * HEAP_GROW_FACTOR;
|
state->nextGC = state->allocatedBytes * HEAP_GROW_FACTOR;
|
||||||
}
|
}
|
||||||
|
|
||||||
COSMO_API void cosmoM_addRoot(CState *state, CObj *newRoot) {
|
COSMO_API void cosmoM_addRoot(CState *state, CObj *newRoot)
|
||||||
|
{
|
||||||
// first, check and make sure this root doesn't already exist in the list
|
// first, check and make sure this root doesn't already exist in the list
|
||||||
CObj *root = state->userRoots;
|
CObj *root = state->userRoots;
|
||||||
while (root != NULL) {
|
while (root != NULL) {
|
||||||
if (root == newRoot) // found in the list, abort
|
if (root == newRoot) // found in the list, abort
|
||||||
return;
|
return;
|
||||||
|
|
||||||
root = root->nextRoot;
|
root = root->nextRoot;
|
||||||
}
|
}
|
||||||
|
|
||||||
// adds root to userRoot linked list
|
// adds root to userRoot linked list
|
||||||
newRoot->nextRoot = state->userRoots;
|
newRoot->nextRoot = state->userRoots;
|
||||||
state->userRoots = newRoot;
|
state->userRoots = newRoot;
|
||||||
}
|
}
|
||||||
|
|
||||||
COSMO_API void cosmoM_removeRoot(CState *state, CObj *oldRoot) {
|
COSMO_API void cosmoM_removeRoot(CState *state, CObj *oldRoot)
|
||||||
|
{
|
||||||
CObj *prev = NULL;
|
CObj *prev = NULL;
|
||||||
CObj *root = state->userRoots;
|
CObj *root = state->userRoots;
|
||||||
|
|
||||||
|
75
src/cmem.h
75
src/cmem.h
@ -2,69 +2,67 @@
|
|||||||
#define CMEME_C // meme lol
|
#define CMEME_C // meme lol
|
||||||
|
|
||||||
#include "cosmo.h"
|
#include "cosmo.h"
|
||||||
|
|
||||||
#include "cstate.h"
|
#include "cstate.h"
|
||||||
|
|
||||||
//#define GC_STRESS
|
// #define GC_STRESS
|
||||||
//#define GC_DEBUG
|
// #define GC_DEBUG
|
||||||
// arrays *must* grow by a factor of 2
|
// arrays *must* grow by a factor of 2
|
||||||
#define GROW_FACTOR 2
|
#define GROW_FACTOR 2
|
||||||
#define HEAP_GROW_FACTOR 2
|
#define HEAP_GROW_FACTOR 2
|
||||||
#define ARRAY_START 8
|
#define ARRAY_START 8
|
||||||
|
|
||||||
#ifdef GC_DEBUG
|
#ifdef GC_DEBUG
|
||||||
#define cosmoM_freearray(state, type, buf, capacity) \
|
# define cosmoM_freearray(state, type, buf, capacity) \
|
||||||
printf("freeing array %p [size %lu] at %s:%d\n", buf, sizeof(type) * capacity, __FILE__, __LINE__); \
|
printf("freeing array %p [size %lu] at %s:%d\n", buf, sizeof(type) * capacity, __FILE__, \
|
||||||
cosmoM_reallocate(state, buf, sizeof(type) * capacity, 0)
|
__LINE__); \
|
||||||
|
cosmoM_reallocate(state, buf, sizeof(type) * capacity, 0)
|
||||||
#else
|
#else
|
||||||
#define cosmoM_freearray(state, type, buf, capacity) \
|
# define cosmoM_freearray(state, type, buf, capacity) \
|
||||||
cosmoM_reallocate(state, buf, sizeof(type) * capacity, 0)
|
cosmoM_reallocate(state, buf, sizeof(type) * capacity, 0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define cosmoM_growarray(state, type, buf, count, capacity) \
|
#define cosmoM_growarray(state, type, buf, count, capacity) \
|
||||||
if (count >= capacity || buf == NULL) { \
|
if (count >= capacity || buf == NULL) { \
|
||||||
int old = capacity; \
|
int old = capacity; \
|
||||||
capacity = old * GROW_FACTOR; \
|
capacity = old * GROW_FACTOR; \
|
||||||
buf = (type*)cosmoM_reallocate(state, buf, sizeof(type) *old, sizeof(type) *capacity); \
|
buf = (type *)cosmoM_reallocate(state, buf, sizeof(type) * old, sizeof(type) * capacity); \
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef GC_DEBUG
|
#ifdef GC_DEBUG
|
||||||
#define cosmoM_free(state, type, x) \
|
# define cosmoM_free(state, type, x) \
|
||||||
printf("freeing %p [size %lu] at %s:%d\n", x, sizeof(type), __FILE__, __LINE__); \
|
printf("freeing %p [size %lu] at %s:%d\n", x, sizeof(type), __FILE__, __LINE__); \
|
||||||
cosmoM_reallocate(state, x, sizeof(type), 0)
|
cosmoM_reallocate(state, x, sizeof(type), 0)
|
||||||
#else
|
#else
|
||||||
#define cosmoM_free(state, type, x) \
|
# define cosmoM_free(state, type, x) cosmoM_reallocate(state, x, sizeof(type), 0)
|
||||||
cosmoM_reallocate(state, x, sizeof(type), 0)
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define cosmoM_isFrozen(state) \
|
#define cosmoM_isFrozen(state) (state->freezeGC > 0)
|
||||||
(state->freezeGC > 0)
|
|
||||||
|
|
||||||
// if debugging, print the locations of when the state is frozen/unfrozen
|
// if debugging, print the locations of when the state is frozen/unfrozen
|
||||||
#ifdef GC_DEBUG
|
#ifdef GC_DEBUG
|
||||||
#define cosmoM_freezeGC(state) \
|
# define cosmoM_freezeGC(state) \
|
||||||
state->freezeGC++; \
|
state->freezeGC++; \
|
||||||
printf("freezing state at %s:%d [%d]\n", __FILE__, __LINE__, state->freezeGC)
|
printf("freezing state at %s:%d [%d]\n", __FILE__, __LINE__, state->freezeGC)
|
||||||
|
|
||||||
#define cosmoM_unfreezeGC(state) \
|
# define cosmoM_unfreezeGC(state) \
|
||||||
state->freezeGC--; \
|
state->freezeGC--; \
|
||||||
printf("unfreezing state at %s:%d [%d]\n", __FILE__, __LINE__, state->freezeGC); \
|
printf("unfreezing state at %s:%d [%d]\n", __FILE__, __LINE__, state->freezeGC); \
|
||||||
cosmoM_checkGarbage(state, 0)
|
cosmoM_checkGarbage(state, 0)
|
||||||
#else
|
#else
|
||||||
|
|
||||||
// freeze's the garbage collector until cosmoM_unfreezeGC is called
|
// freeze's the garbage collector until cosmoM_unfreezeGC is called
|
||||||
#define cosmoM_freezeGC(state) \
|
# define cosmoM_freezeGC(state) state->freezeGC++
|
||||||
state->freezeGC++
|
|
||||||
|
|
||||||
// unfreeze's the garbage collector and tries to run a garbage collection cycle
|
// unfreeze's the garbage collector and tries to run a garbage collection cycle
|
||||||
#define cosmoM_unfreezeGC(state) \
|
# define cosmoM_unfreezeGC(state) \
|
||||||
state->freezeGC--; \
|
state->freezeGC--; \
|
||||||
cosmoM_checkGarbage(state, 0)
|
cosmoM_checkGarbage(state, 0)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
COSMO_API void *cosmoM_reallocate(CState *state, void *buf, size_t oldSize, size_t newSize);
|
COSMO_API void *cosmoM_reallocate(CState *state, void *buf, size_t oldSize, size_t newSize);
|
||||||
COSMO_API bool cosmoM_checkGarbage(CState *state, size_t needed); // returns true if GC event was triggered
|
COSMO_API bool cosmoM_checkGarbage(CState *state,
|
||||||
|
size_t needed); // returns true if GC event was triggered
|
||||||
COSMO_API void cosmoM_collectGarbage(CState *state);
|
COSMO_API void cosmoM_collectGarbage(CState *state);
|
||||||
COSMO_API void cosmoM_updateThreshhold(CState *state);
|
COSMO_API void cosmoM_updateThreshhold(CState *state);
|
||||||
|
|
||||||
@ -75,7 +73,8 @@ COSMO_API void cosmoM_addRoot(CState *state, CObj *newRoot);
|
|||||||
COSMO_API void cosmoM_removeRoot(CState *state, CObj *oldRoot);
|
COSMO_API void cosmoM_removeRoot(CState *state, CObj *oldRoot);
|
||||||
|
|
||||||
// wrapper for cosmoM_reallocate so we can track our memory usage (it's also safer :P)
|
// wrapper for cosmoM_reallocate so we can track our memory usage (it's also safer :P)
|
||||||
static inline void *cosmoM_xmalloc(CState *state, size_t sz) {
|
static inline void *cosmoM_xmalloc(CState *state, size_t sz)
|
||||||
|
{
|
||||||
return cosmoM_reallocate(state, NULL, 0, sz);
|
return cosmoM_reallocate(state, NULL, 0, sz);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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"
|
#include "cosmo.h"
|
||||||
|
|
||||||
typedef enum CObjType {
|
typedef enum CObjType
|
||||||
|
{
|
||||||
COBJ_STRING,
|
COBJ_STRING,
|
||||||
COBJ_OBJECT,
|
COBJ_OBJECT,
|
||||||
COBJ_TABLE,
|
COBJ_TABLE,
|
||||||
@ -18,52 +19,59 @@ typedef enum CObjType {
|
|||||||
COBJ_MAX
|
COBJ_MAX
|
||||||
} CObjType;
|
} CObjType;
|
||||||
|
|
||||||
#include "cstate.h"
|
|
||||||
#include "cchunk.h"
|
#include "cchunk.h"
|
||||||
#include "cvalue.h"
|
#include "cstate.h"
|
||||||
#include "ctable.h"
|
#include "ctable.h"
|
||||||
|
#include "cvalue.h"
|
||||||
|
|
||||||
#define CommonHeader CObj _obj
|
#define CommonHeader CObj _obj
|
||||||
#define readFlag(x, flag) (x & (1u << flag))
|
#define readFlag(x, flag) (x & (1u << flag))
|
||||||
#define setFlagOn(x, flag) (x |= (1u << flag))
|
#define setFlagOn(x, flag) (x |= (1u << flag))
|
||||||
|
|
||||||
typedef int (*CosmoCFunction)(CState *state, int argCount, CValue *args);
|
typedef int (*CosmoCFunction)(CState *state, int argCount, CValue *args);
|
||||||
|
|
||||||
struct CObj {
|
struct CObj
|
||||||
|
{
|
||||||
struct CObj *next;
|
struct CObj *next;
|
||||||
struct CObj *nextRoot; // for the root linked list
|
struct CObj *nextRoot; // for the root linked list
|
||||||
struct CObjObject *proto; // protoobject, describes the behavior of the object
|
struct CObjObject *proto; // protoobject, describes the behavior of the object
|
||||||
CObjType type;
|
CObjType type;
|
||||||
bool isMarked; // for the GC
|
bool isMarked; // for the GC
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CObjString {
|
struct CObjString
|
||||||
CommonHeader; // "is a" CObj
|
{
|
||||||
char *str; // NULL termincated string
|
CommonHeader; // "is a" CObj
|
||||||
|
char *str; // NULL termincated string
|
||||||
uint32_t hash; // for hashtable lookup
|
uint32_t hash; // for hashtable lookup
|
||||||
int length;
|
int length;
|
||||||
bool isIString;
|
bool isIString;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CObjStream {
|
struct CObjStream
|
||||||
|
{
|
||||||
CommonHeader; // "is a" CObj
|
CommonHeader; // "is a" CObj
|
||||||
int fd; // handle to file descriptor, on POSIX compliant OSes this can also be a socket :pog:
|
int fd; // handle to file descriptor, on POSIX compliant OSes this can also be a socket :pog:
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CObjError {
|
struct CObjError
|
||||||
|
{
|
||||||
CommonHeader; // "is a" CObj
|
CommonHeader; // "is a" CObj
|
||||||
CValue err; // error string
|
CValue err; // error string
|
||||||
CCallFrame *frames;
|
CCallFrame *frames;
|
||||||
int frameCount;
|
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
|
bool parserError; // if true, cosmoV_printError will format the error to the lexer
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CObjObject {
|
struct CObjObject
|
||||||
|
{
|
||||||
CommonHeader; // "is a" CObj
|
CommonHeader; // "is a" CObj
|
||||||
CTable tbl;
|
CTable tbl;
|
||||||
cosmo_Flag istringFlags; // enables us to have a much faster lookup for reserved IStrings (like __init, __index, etc.)
|
cosmo_Flag istringFlags; // enables us to have a much faster lookup for reserved IStrings (like
|
||||||
union { // userdata (NULL by default)
|
// __init, __index, etc.)
|
||||||
|
union
|
||||||
|
{ // userdata (NULL by default)
|
||||||
void *userP;
|
void *userP;
|
||||||
int userI;
|
int userI;
|
||||||
};
|
};
|
||||||
@ -71,12 +79,14 @@ struct CObjObject {
|
|||||||
bool isLocked;
|
bool isLocked;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CObjTable { // table, a wrapper for CTable
|
struct CObjTable
|
||||||
|
{ // table, a wrapper for CTable
|
||||||
CommonHeader; // "is a" CObj
|
CommonHeader; // "is a" CObj
|
||||||
CTable tbl;
|
CTable tbl;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CObjFunction {
|
struct CObjFunction
|
||||||
|
{
|
||||||
CommonHeader; // "is a" CObj
|
CommonHeader; // "is a" CObj
|
||||||
CChunk chunk;
|
CChunk chunk;
|
||||||
CObjString *name;
|
CObjString *name;
|
||||||
@ -86,25 +96,29 @@ struct CObjFunction {
|
|||||||
bool variadic;
|
bool variadic;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CObjCFunction {
|
struct CObjCFunction
|
||||||
|
{
|
||||||
CommonHeader; // "is a" CObj
|
CommonHeader; // "is a" CObj
|
||||||
CosmoCFunction cfunc;
|
CosmoCFunction cfunc;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CObjClosure {
|
struct CObjClosure
|
||||||
|
{
|
||||||
CommonHeader; // "is a" CObj
|
CommonHeader; // "is a" CObj
|
||||||
CObjFunction *function;
|
CObjFunction *function;
|
||||||
CObjUpval **upvalues;
|
CObjUpval **upvalues;
|
||||||
int upvalueCount;
|
int upvalueCount;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CObjMethod {
|
struct CObjMethod
|
||||||
|
{
|
||||||
CommonHeader; // "is a " CObj
|
CommonHeader; // "is a " CObj
|
||||||
CValue func;
|
CValue func;
|
||||||
CObj *obj; // obj this method is bound too
|
CObj *obj; // obj this method is bound too
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CObjUpval {
|
struct CObjUpval
|
||||||
|
{
|
||||||
CommonHeader; // "is a" CObj
|
CommonHeader; // "is a" CObj
|
||||||
CValue closed;
|
CValue closed;
|
||||||
CValue *val;
|
CValue *val;
|
||||||
@ -113,38 +127,40 @@ struct CObjUpval {
|
|||||||
|
|
||||||
#undef CommonHeader
|
#undef CommonHeader
|
||||||
|
|
||||||
#define IS_STRING(x) isObjType(x, COBJ_STRING)
|
#define IS_STRING(x) isObjType(x, COBJ_STRING)
|
||||||
#define IS_OBJECT(x) isObjType(x, COBJ_OBJECT)
|
#define IS_OBJECT(x) isObjType(x, COBJ_OBJECT)
|
||||||
#define IS_STREAM(x) isObjType(x, COBJ_STREAM)
|
#define IS_STREAM(x) isObjType(x, COBJ_STREAM)
|
||||||
#define IS_TABLE(x) isObjType(x, COBJ_TABLE)
|
#define IS_TABLE(x) isObjType(x, COBJ_TABLE)
|
||||||
#define IS_FUNCTION(x) isObjType(x, COBJ_FUNCTION)
|
#define IS_FUNCTION(x) isObjType(x, COBJ_FUNCTION)
|
||||||
#define IS_CFUNCTION(x) isObjType(x, COBJ_CFUNCTION)
|
#define IS_CFUNCTION(x) isObjType(x, COBJ_CFUNCTION)
|
||||||
#define IS_METHOD(x) isObjType(x, COBJ_METHOD)
|
#define IS_METHOD(x) isObjType(x, COBJ_METHOD)
|
||||||
#define IS_CLOSURE(x) isObjType(x, COBJ_CLOSURE)
|
#define IS_CLOSURE(x) isObjType(x, COBJ_CLOSURE)
|
||||||
|
|
||||||
#define cosmoV_readString(x) ((CObjString*)cosmoV_readRef(x))
|
#define cosmoV_readString(x) ((CObjString *)cosmoV_readRef(x))
|
||||||
#define cosmoV_readCString(x) (((CObjString*)cosmoV_readRef(x))->str)
|
#define cosmoV_readCString(x) (((CObjString *)cosmoV_readRef(x))->str)
|
||||||
#define cosmoV_readFD(x) (((CObjStream*)cosmoV_readRef(x))->fd)
|
#define cosmoV_readFD(x) (((CObjStream *)cosmoV_readRef(x))->fd)
|
||||||
#define cosmoV_readObject(x) ((CObjObject*)cosmoV_readRef(x))
|
#define cosmoV_readObject(x) ((CObjObject *)cosmoV_readRef(x))
|
||||||
#define cosmoV_readTable(x) ((CObjTable*)cosmoV_readRef(x))
|
#define cosmoV_readTable(x) ((CObjTable *)cosmoV_readRef(x))
|
||||||
#define cosmoV_readFunction(x) ((CObjFunction*)cosmoV_readRef(x))
|
#define cosmoV_readFunction(x) ((CObjFunction *)cosmoV_readRef(x))
|
||||||
#define cosmoV_readCFunction(x) (((CObjCFunction*)cosmoV_readRef(x))->cfunc)
|
#define cosmoV_readCFunction(x) (((CObjCFunction *)cosmoV_readRef(x))->cfunc)
|
||||||
#define cosmoV_readMethod(x) ((CObjMethod*)cosmoV_readRef(x))
|
#define cosmoV_readMethod(x) ((CObjMethod *)cosmoV_readRef(x))
|
||||||
#define cosmoV_readClosure(x) ((CObjClosure*)cosmoV_readRef(x))
|
#define cosmoV_readClosure(x) ((CObjClosure *)cosmoV_readRef(x))
|
||||||
|
|
||||||
#define cosmoO_readCString(x) ((CObjString*)x)->str
|
#define cosmoO_readCString(x) ((CObjString *)x)->str
|
||||||
|
|
||||||
static inline bool isObjType(CValue val, CObjType type) {
|
static inline bool isObjType(CValue val, CObjType type)
|
||||||
|
{
|
||||||
return IS_REF(val) && cosmoV_readRef(val)->type == type;
|
return IS_REF(val) && cosmoV_readRef(val)->type == type;
|
||||||
}
|
}
|
||||||
|
|
||||||
// just protects against macro expansion
|
// just protects against macro expansion
|
||||||
static inline bool IS_CALLABLE(CValue val) {
|
static inline bool IS_CALLABLE(CValue val)
|
||||||
|
{
|
||||||
return IS_CLOSURE(val) || IS_CFUNCTION(val) || IS_METHOD(val);
|
return IS_CLOSURE(val) || IS_CFUNCTION(val) || IS_METHOD(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
void cosmoO_free(CState *state, CObj* obj);
|
void cosmoO_free(CState *state, CObj *obj);
|
||||||
bool cosmoO_equal(CState *state, CObj* obj1, CObj* obj2);
|
bool cosmoO_equal(CState *state, CObj *obj1, CObj *obj2);
|
||||||
|
|
||||||
// walks the protos of obj and checks for proto
|
// walks the protos of obj and checks for proto
|
||||||
bool cosmoO_isDescendant(CObj *obj, CObjObject *proto);
|
bool cosmoO_isDescendant(CObj *obj, CObjObject *proto);
|
||||||
@ -160,8 +176,9 @@ CObjClosure *cosmoO_newClosure(CState *state, CObjFunction *func);
|
|||||||
CObjUpval *cosmoO_newUpvalue(CState *state, CValue *val);
|
CObjUpval *cosmoO_newUpvalue(CState *state, CValue *val);
|
||||||
|
|
||||||
// grabs the base proto of the CObj* (if CObj is a CObjObject, that is returned)
|
// grabs the base proto of the CObj* (if CObj is a CObjObject, that is returned)
|
||||||
static inline CObjObject *cosmoO_grabProto(CObj *obj) {
|
static inline CObjObject *cosmoO_grabProto(CObj *obj)
|
||||||
return obj->type == COBJ_OBJECT ? (CObjObject*)obj : obj->proto;
|
{
|
||||||
|
return obj->type == COBJ_OBJECT ? (CObjObject *)obj : obj->proto;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cosmoO_getRawObject(CState *state, CObjObject *proto, CValue key, CValue *val, CObj *obj);
|
bool cosmoO_getRawObject(CState *state, CObjObject *proto, CValue key, CValue *val, CObj *obj);
|
||||||
@ -169,11 +186,13 @@ void cosmoO_setRawObject(CState *state, CObjObject *proto, CValue key, CValue va
|
|||||||
bool cosmoO_indexObject(CState *state, CObjObject *object, CValue key, CValue *val);
|
bool cosmoO_indexObject(CState *state, CObjObject *object, CValue key, CValue *val);
|
||||||
bool cosmoO_newIndexObject(CState *state, CObjObject *object, CValue key, CValue val);
|
bool cosmoO_newIndexObject(CState *state, CObjObject *object, CValue key, CValue val);
|
||||||
|
|
||||||
// sets the user-defined pointer, if a user-define integer is already defined it will be over written
|
// sets the user-defined pointer, if a user-define integer is already defined it will be over
|
||||||
|
// written
|
||||||
void cosmoO_setUserP(CObjObject *object, void *p);
|
void cosmoO_setUserP(CObjObject *object, void *p);
|
||||||
// gets the user-defined pointer
|
// gets the user-defined pointer
|
||||||
void *cosmoO_getUserP(CObjObject *object);
|
void *cosmoO_getUserP(CObjObject *object);
|
||||||
// sets the user-defined integer, if a user-define pointer is already defined it will be over written
|
// sets the user-defined integer, if a user-define pointer is already defined it will be over
|
||||||
|
// written
|
||||||
void cosmoO_setUserI(CObjObject *object, int i);
|
void cosmoO_setUserI(CObjObject *object, int i);
|
||||||
// gets the user-defined integer
|
// gets the user-defined integer
|
||||||
int cosmoO_getUserI(CObjObject *object);
|
int cosmoO_getUserI(CObjObject *object);
|
||||||
@ -189,10 +208,12 @@ void cosmoO_unlock(CObjObject *object);
|
|||||||
// internal string
|
// internal string
|
||||||
bool cosmoO_getIString(CState *state, CObjObject *object, int flag, CValue *val);
|
bool cosmoO_getIString(CState *state, CObjObject *object, int flag, CValue *val);
|
||||||
|
|
||||||
// copies the *str buffer to the heap and returns a CObjString struct which is also on the heap (length should not include the null terminator)
|
// copies the *str buffer to the heap and returns a CObjString struct which is also on the heap
|
||||||
|
// (length should not include the null terminator)
|
||||||
CObjString *cosmoO_copyString(CState *state, const char *str, size_t length);
|
CObjString *cosmoO_copyString(CState *state, const char *str, size_t length);
|
||||||
|
|
||||||
// length shouldn't include the null terminator! str should be a null terminated string! (char array should also have been allocated using cosmoM_xmalloc!)
|
// length shouldn't include the null terminator! str should be a null terminated string! (char array
|
||||||
|
// should also have been allocated using cosmoM_xmalloc!)
|
||||||
CObjString *cosmoO_takeString(CState *state, char *str, size_t length);
|
CObjString *cosmoO_takeString(CState *state, char *str, size_t length);
|
||||||
|
|
||||||
// allocates a CObjStruct pointing directly to *str
|
// allocates a CObjStruct pointing directly to *str
|
||||||
@ -209,7 +230,7 @@ CObjString *cosmoO_allocateString(CState *state, const char *str, size_t length,
|
|||||||
CObjString *cosmoO_pushVFString(CState *state, const char *format, va_list args);
|
CObjString *cosmoO_pushVFString(CState *state, const char *format, va_list args);
|
||||||
|
|
||||||
COSMO_API void printObject(CObj *o);
|
COSMO_API void printObject(CObj *o);
|
||||||
const char *cosmoO_typeStr(CObj* obj);
|
const char *cosmoO_typeStr(CObj *obj);
|
||||||
|
|
||||||
CObjString *cosmoO_toString(CState *state, CObj *obj);
|
CObjString *cosmoO_toString(CState *state, CObj *obj);
|
||||||
cosmo_Number cosmoO_toNumber(CState *state, CObj *obj);
|
cosmo_Number cosmoO_toNumber(CState *state, CObj *obj);
|
||||||
|
@ -3,24 +3,25 @@
|
|||||||
|
|
||||||
#include "cosmo.h"
|
#include "cosmo.h"
|
||||||
|
|
||||||
// instructions
|
// instructions
|
||||||
|
|
||||||
typedef enum {
|
typedef enum
|
||||||
|
{
|
||||||
// STACK/STATE MANIPULATION
|
// STACK/STATE MANIPULATION
|
||||||
OP_LOADCONST, // pushes const[uint8_t] to the stack
|
OP_LOADCONST, // pushes const[uint8_t] to the stack
|
||||||
OP_SETGLOBAL, // pops and sets global[const[uint16_t]]
|
OP_SETGLOBAL, // pops and sets global[const[uint16_t]]
|
||||||
OP_GETGLOBAL, // pushes global[const[uint16_t]]
|
OP_GETGLOBAL, // pushes global[const[uint16_t]]
|
||||||
OP_SETLOCAL, // pops and sets base[uint8_t]
|
OP_SETLOCAL, // pops and sets base[uint8_t]
|
||||||
OP_GETLOCAL, // pushes base[uint8_t]
|
OP_GETLOCAL, // pushes base[uint8_t]
|
||||||
OP_GETUPVAL, // pushes closure->upvals[uint8_t]
|
OP_GETUPVAL, // pushes closure->upvals[uint8_t]
|
||||||
OP_SETUPVAL, // pops and sets closure->upvals[uint8_t]
|
OP_SETUPVAL, // pops and sets closure->upvals[uint8_t]
|
||||||
OP_PEJMP, // pops, if false jumps uint16_t
|
OP_PEJMP, // pops, if false jumps uint16_t
|
||||||
OP_EJMP, // if peek(0) is falsey jumps uint16_t
|
OP_EJMP, // if peek(0) is falsey jumps uint16_t
|
||||||
OP_JMP, // always jumps uint16_t
|
OP_JMP, // always jumps uint16_t
|
||||||
OP_JMPBACK, // jumps -uint16_t
|
OP_JMPBACK, // jumps -uint16_t
|
||||||
OP_POP, // - pops[uint8_t] from stack
|
OP_POP, // - pops[uint8_t] from stack
|
||||||
OP_CALL, // calls top[-uint8_t] expecting uint8_t results
|
OP_CALL, // calls top[-uint8_t] expecting uint8_t results
|
||||||
OP_CLOSURE,
|
OP_CLOSURE,
|
||||||
OP_CLOSE,
|
OP_CLOSE,
|
||||||
OP_NEWTABLE,
|
OP_NEWTABLE,
|
||||||
OP_NEWARRAY, // really just a table
|
OP_NEWARRAY, // really just a table
|
||||||
@ -44,10 +45,10 @@ typedef enum {
|
|||||||
OP_NOT,
|
OP_NOT,
|
||||||
OP_NEGATE,
|
OP_NEGATE,
|
||||||
OP_COUNT,
|
OP_COUNT,
|
||||||
OP_CONCAT, // concats uint8_t vars on the stack
|
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_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_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_INCINDEX,
|
||||||
OP_INCOBJECT, // pushes old value to stack, adds (uint8_t-128) to obj[const[uint16_t]]
|
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
|
#ifndef COSMOMAIN_H
|
||||||
#define COSMOMAIN_H
|
#define COSMOMAIN_H
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <assert.h>
|
||||||
|
#include <stdbool.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include <assert.h>
|
/*
|
||||||
|
|
||||||
/*
|
|
||||||
SAFE_STACK:
|
SAFE_STACK:
|
||||||
if undefined, the stack will not be checked for stack overflows. This may improve performance, however
|
if undefined, the stack will not be checked for stack overflows. This may improve
|
||||||
this will produce undefined behavior as you reach the stack limit (and may cause a seg fault!). It is recommended to keep this enabled.
|
performance, however this will produce undefined behavior as you reach the stack limit (and may
|
||||||
|
cause a seg fault!). It is recommended to keep this enabled.
|
||||||
*/
|
*/
|
||||||
#define SAFE_STACK
|
#define SAFE_STACK
|
||||||
//#define NAN_BOXXED
|
// #define NAN_BOXXED
|
||||||
|
|
||||||
// forward declare *most* stuff so our headers are cleaner
|
// forward declare *most* stuff so our headers are cleaner
|
||||||
typedef struct CState CState;
|
typedef struct CState CState;
|
||||||
@ -54,7 +54,6 @@ typedef uint8_t INSTRUCTION;
|
|||||||
#define UNNAMEDCHUNK "_main"
|
#define UNNAMEDCHUNK "_main"
|
||||||
#define COSMOASSERT(x) assert(x)
|
#define COSMOASSERT(x) assert(x)
|
||||||
|
|
||||||
#define CERROR(err) \
|
#define CERROR(err) printf("%s : %s\n", "[ERROR]", err)
|
||||||
printf("%s : %s\n", "[ERROR]", err)
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
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
|
#ifndef CPARSE_H
|
||||||
#define CPARSE_H
|
#define CPARSE_H
|
||||||
|
|
||||||
#include "cosmo.h"
|
|
||||||
#include "clex.h"
|
#include "clex.h"
|
||||||
|
#include "cosmo.h"
|
||||||
|
|
||||||
// compiles source into CChunk, if NULL is returned, a syntaxical error has occurred and pushed onto the stack
|
// compiles source into CChunk, if NULL is returned, a syntaxical error has occurred and pushed onto
|
||||||
CObjFunction* cosmoP_compileString(CState *state, const char *source, const char *module);
|
// the stack
|
||||||
|
CObjFunction *cosmoP_compileString(CState *state, const char *source, const char *module);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
35
src/cstate.c
35
src/cstate.c
@ -1,12 +1,14 @@
|
|||||||
#include "cstate.h"
|
#include "cstate.h"
|
||||||
|
|
||||||
#include "cchunk.h"
|
#include "cchunk.h"
|
||||||
|
#include "cmem.h"
|
||||||
#include "cobj.h"
|
#include "cobj.h"
|
||||||
#include "cvm.h"
|
#include "cvm.h"
|
||||||
#include "cmem.h"
|
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
CState *cosmoV_newState() {
|
CState *cosmoV_newState()
|
||||||
|
{
|
||||||
// we use C's malloc because we don't want to trigger a GC with an invalid state
|
// we use C's malloc because we don't want to trigger a GC with an invalid state
|
||||||
CState *state = malloc(sizeof(CState));
|
CState *state = malloc(sizeof(CState));
|
||||||
|
|
||||||
@ -33,7 +35,7 @@ CState *cosmoV_newState() {
|
|||||||
state->openUpvalues = NULL;
|
state->openUpvalues = NULL;
|
||||||
|
|
||||||
state->error = NULL;
|
state->error = NULL;
|
||||||
|
|
||||||
// set default proto objects
|
// set default proto objects
|
||||||
for (int i = 0; i < COBJ_MAX; i++)
|
for (int i = 0; i < COBJ_MAX; i++)
|
||||||
state->protoObjects[i] = NULL;
|
state->protoObjects[i] = NULL;
|
||||||
@ -73,7 +75,8 @@ CState *cosmoV_newState() {
|
|||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
void cosmoV_freeState(CState *state) {
|
void cosmoV_freeState(CState *state)
|
||||||
|
{
|
||||||
#ifdef GC_DEBUG
|
#ifdef GC_DEBUG
|
||||||
printf("state %p is being free'd!\n", state);
|
printf("state %p is being free'd!\n", state);
|
||||||
#endif
|
#endif
|
||||||
@ -93,34 +96,36 @@ void cosmoV_freeState(CState *state) {
|
|||||||
|
|
||||||
// free our string table (the string table includes the internal VM strings)
|
// free our string table (the string table includes the internal VM strings)
|
||||||
cosmoT_clearTable(state, &state->strings);
|
cosmoT_clearTable(state, &state->strings);
|
||||||
|
|
||||||
// free our gray stack & finally free the state structure
|
// free our gray stack & finally free the state structure
|
||||||
cosmoM_freearray(state, CObj*, state->grayStack.array, state->grayStack.capacity);
|
cosmoM_freearray(state, CObj *, state->grayStack.array, state->grayStack.capacity);
|
||||||
|
|
||||||
// TODO: yeah idk, it looks like im missing 520 bytes somewhere? i'll look into it later
|
// TODO: yeah idk, it looks like im missing 520 bytes somewhere? i'll look into it later
|
||||||
/*#ifdef GC_DEBUG
|
/*#ifdef GC_DEBUG
|
||||||
if (state->allocatedBytes != sizeof(CState)) {
|
if (state->allocatedBytes != sizeof(CState)) {
|
||||||
printf("state->allocatedBytes doesn't match expected value (%lu), got %lu!", sizeof(CState), state->allocatedBytes);
|
printf("state->allocatedBytes doesn't match expected value (%lu), got %lu!",
|
||||||
exit(0);
|
sizeof(CState), state->allocatedBytes); exit(0);
|
||||||
}
|
}
|
||||||
#endif*/
|
#endif*/
|
||||||
free(state);
|
free(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
// expects 2*pairs values on the stack, each pair should consist of 1 key and 1 value
|
// expects 2*pairs values on the stack, each pair should consist of 1 key and 1 value
|
||||||
void cosmoV_register(CState *state, int pairs) {
|
void cosmoV_register(CState *state, int pairs)
|
||||||
|
{
|
||||||
for (int i = 0; i < pairs; i++) {
|
for (int i = 0; i < pairs; i++) {
|
||||||
StkPtr key = cosmoV_getTop(state, 1);
|
StkPtr key = cosmoV_getTop(state, 1);
|
||||||
StkPtr val = cosmoV_getTop(state, 0);
|
StkPtr val = cosmoV_getTop(state, 0);
|
||||||
|
|
||||||
CValue *oldVal = cosmoT_insert(state, &state->globals->tbl, *key);
|
CValue *oldVal = cosmoT_insert(state, &state->globals->tbl, *key);
|
||||||
*oldVal = *val;
|
*oldVal = *val;
|
||||||
|
|
||||||
cosmoV_setTop(state, 2); // pops the 2 values off the stack
|
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");
|
printf("==== [[ stack dump ]] ====\n");
|
||||||
for (CValue *top = state->top - 1; top >= state->stack; top--) {
|
for (CValue *top = state->top - 1; top >= state->stack; top--) {
|
||||||
printf("%d: ", (int)(top - state->stack));
|
printf("%d: ", (int)(top - state->stack));
|
||||||
|
62
src/cstate.h
62
src/cstate.h
@ -1,48 +1,55 @@
|
|||||||
#ifndef CSTATE_H
|
#ifndef CSTATE_H
|
||||||
#define CSTATE_H
|
#define CSTATE_H
|
||||||
|
|
||||||
#include "cosmo.h"
|
|
||||||
#include "cobj.h"
|
#include "cobj.h"
|
||||||
#include "cvalue.h"
|
#include "cosmo.h"
|
||||||
#include "ctable.h"
|
#include "ctable.h"
|
||||||
|
#include "cvalue.h"
|
||||||
|
|
||||||
struct CCallFrame {
|
struct CCallFrame
|
||||||
|
{
|
||||||
CObjClosure *closure;
|
CObjClosure *closure;
|
||||||
INSTRUCTION *pc;
|
INSTRUCTION *pc;
|
||||||
CValue* base;
|
CValue *base;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef enum IStringEnum {
|
typedef enum IStringEnum
|
||||||
ISTRING_INIT, // __init
|
{
|
||||||
ISTRING_TOSTRING, // __tostring
|
ISTRING_INIT, // __init
|
||||||
ISTRING_TONUMBER, // __tonumber
|
ISTRING_TOSTRING, // __tostring
|
||||||
ISTRING_EQUAL, // __equals
|
ISTRING_TONUMBER, // __tonumber
|
||||||
ISTRING_INDEX, // __index
|
ISTRING_EQUAL, // __equals
|
||||||
ISTRING_NEWINDEX, // __newindex
|
ISTRING_INDEX, // __index
|
||||||
ISTRING_COUNT, // __count
|
ISTRING_NEWINDEX, // __newindex
|
||||||
ISTRING_GETTER, // __getter
|
ISTRING_COUNT, // __count
|
||||||
ISTRING_SETTER, // __setter
|
ISTRING_GETTER, // __getter
|
||||||
ISTRING_ITER, // __iter
|
ISTRING_SETTER, // __setter
|
||||||
ISTRING_NEXT, // __next
|
ISTRING_ITER, // __iter
|
||||||
ISTRING_RESERVED, // __reserved
|
ISTRING_NEXT, // __next
|
||||||
ISTRING_MAX // if this becomes greater than 33, we are out of space in cosmo_Flag. you'll have to change that to uint64_t
|
ISTRING_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;
|
} IStringEnum;
|
||||||
|
|
||||||
typedef struct ArrayCObj {
|
typedef struct ArrayCObj
|
||||||
|
{
|
||||||
CObj **array;
|
CObj **array;
|
||||||
int count;
|
int count;
|
||||||
int capacity;
|
int capacity;
|
||||||
} ArrayCObj;
|
} ArrayCObj;
|
||||||
|
|
||||||
struct CState {
|
struct CState
|
||||||
|
{
|
||||||
bool panic;
|
bool panic;
|
||||||
int freezeGC; // when > 0, GC events will be ignored (for internal use)
|
int freezeGC; // when > 0, GC events will be ignored (for internal use)
|
||||||
int frameCount;
|
int frameCount;
|
||||||
|
|
||||||
CObjError *error; // NULL, unless panic is true
|
CObjError *error; // NULL, unless panic is true
|
||||||
CObj *objects; // tracks all of our allocated objects
|
CObj *objects; // tracks all of our allocated objects
|
||||||
CObj *userRoots; // user definable roots, this holds CObjs that should be considered "roots", lets the VM know you are holding a reference to a CObj in your code
|
CObj *userRoots; // user definable roots, this holds CObjs that should be considered "roots",
|
||||||
ArrayCObj grayStack; // keeps track of which objects *haven't yet* been traversed in our GC, but *have been* found
|
// lets the VM know you are holding a reference to a CObj in your code
|
||||||
|
ArrayCObj grayStack; // keeps track of which objects *haven't yet* been traversed in our GC, but
|
||||||
|
// *have been* found
|
||||||
size_t allocatedBytes;
|
size_t allocatedBytes;
|
||||||
size_t nextGC; // when allocatedBytes reaches this threshhold, trigger a GC event
|
size_t nextGC; // when allocatedBytes reaches this threshhold, trigger a GC event
|
||||||
|
|
||||||
@ -50,16 +57,17 @@ struct CState {
|
|||||||
CTable strings;
|
CTable strings;
|
||||||
CObjTable *globals;
|
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]
|
CObjObject *protoObjects[COBJ_MAX]; // proto object for each COBJ type [NULL = no default proto]
|
||||||
CObjString *iStrings[ISTRING_MAX]; // strings used internally by the VM, eg. __init, __index & friends
|
CObjString
|
||||||
|
*iStrings[ISTRING_MAX]; // strings used internally by the VM, eg. __init, __index & friends
|
||||||
CCallFrame callFrame[FRAME_MAX]; // call frames
|
CCallFrame callFrame[FRAME_MAX]; // call frames
|
||||||
CValue stack[STACK_MAX]; // stack
|
CValue stack[STACK_MAX]; // stack
|
||||||
};
|
};
|
||||||
|
|
||||||
COSMO_API CState *cosmoV_newState();
|
COSMO_API CState *cosmoV_newState();
|
||||||
// expects 2*pairs values on the stack, each pair should consist of 1 key and 1 value
|
// expects 2*pairs values on the stack, each pair should consist of 1 key and 1 value
|
||||||
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_freeState(CState *state);
|
||||||
COSMO_API void cosmoV_printStack(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 "ctable.h"
|
||||||
|
|
||||||
#include "cmem.h"
|
#include "cmem.h"
|
||||||
#include "cvalue.h"
|
|
||||||
#include "cobj.h"
|
#include "cobj.h"
|
||||||
|
#include "cvalue.h"
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#define MAX_TABLE_FILL 0.75
|
#define MAX_TABLE_FILL 0.75
|
||||||
// at 30% capacity with capacity > ARRAY_START, shrink the array
|
// at 30% capacity with capacity > ARRAY_START, shrink the array
|
||||||
#define MIN_TABLE_CAPACITY ARRAY_START
|
#define MIN_TABLE_CAPACITY ARRAY_START
|
||||||
|
|
||||||
// bit-twiddling hacks, gets the next power of 2
|
// bit-twiddling hacks, gets the next power of 2
|
||||||
unsigned int nextPow2(unsigned int x) {
|
unsigned int nextPow2(unsigned int x)
|
||||||
if (x <= ARRAY_START - 1) return ARRAY_START; // sanity check
|
{
|
||||||
|
if (x <= ARRAY_START - 1)
|
||||||
|
return ARRAY_START; // sanity check
|
||||||
x--;
|
x--;
|
||||||
|
|
||||||
int power = 2;
|
int power = 2;
|
||||||
while (x >>= 1) power <<= 1;
|
while (x >>= 1)
|
||||||
|
power <<= 1;
|
||||||
|
|
||||||
if (power < ARRAY_START)
|
if (power < ARRAY_START)
|
||||||
return ARRAY_START;
|
return ARRAY_START;
|
||||||
@ -23,7 +27,8 @@ unsigned int nextPow2(unsigned int x) {
|
|||||||
return power;
|
return power;
|
||||||
}
|
}
|
||||||
|
|
||||||
void cosmoT_initTable(CState *state, CTable *tbl, int startCap) {
|
void cosmoT_initTable(CState *state, CTable *tbl, int startCap)
|
||||||
|
{
|
||||||
startCap = startCap != 0 ? startCap : ARRAY_START; // sanity check :P
|
startCap = startCap != 0 ? startCap : ARRAY_START; // sanity check :P
|
||||||
|
|
||||||
tbl->capacityMask = startCap - 1;
|
tbl->capacityMask = startCap - 1;
|
||||||
@ -39,7 +44,8 @@ void cosmoT_initTable(CState *state, CTable *tbl, int startCap) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void cosmoT_addTable(CState *state, CTable *from, CTable *to) {
|
void cosmoT_addTable(CState *state, CTable *from, CTable *to)
|
||||||
|
{
|
||||||
int cap = from->capacityMask + 1;
|
int cap = from->capacityMask + 1;
|
||||||
for (int i = 0; i < cap; i++) {
|
for (int i = 0; i < cap; i++) {
|
||||||
CTableEntry *entry = &from->table[i];
|
CTableEntry *entry = &from->table[i];
|
||||||
@ -51,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));
|
cosmoM_freearray(state, CTableEntry, tbl->table, (tbl->capacityMask + 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t getObjectHash(CObj *obj) {
|
uint32_t getObjectHash(CObj *obj)
|
||||||
switch(obj->type) {
|
{
|
||||||
case COBJ_STRING:
|
switch (obj->type) {
|
||||||
return ((CObjString*)obj)->hash;
|
case COBJ_STRING:
|
||||||
default:
|
return ((CObjString *)obj)->hash;
|
||||||
return (uint32_t)obj; // just "hash" the pointer
|
default:
|
||||||
|
return (uint32_t)obj; // just "hash" the pointer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t getValueHash(CValue *val) {
|
uint32_t getValueHash(CValue *val)
|
||||||
|
{
|
||||||
switch (GET_TYPE(*val)) {
|
switch (GET_TYPE(*val)) {
|
||||||
case COSMO_TREF:
|
case COSMO_TREF:
|
||||||
return getObjectHash(cosmoV_readRef(*val));
|
return getObjectHash(cosmoV_readRef(*val));
|
||||||
case COSMO_TNUMBER: {
|
case COSMO_TNUMBER: {
|
||||||
uint32_t buf[sizeof(cosmo_Number)/sizeof(uint32_t)];
|
uint32_t buf[sizeof(cosmo_Number) / sizeof(uint32_t)];
|
||||||
cosmo_Number num = cosmoV_readNumber(*val);
|
cosmo_Number num = cosmoV_readNumber(*val);
|
||||||
|
|
||||||
if (num == 0)
|
if (num == 0)
|
||||||
return 0;
|
|
||||||
|
|
||||||
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;
|
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)
|
// mask should always be (capacity - 1)
|
||||||
static CTableEntry *findEntry(CState *state, CTableEntry *entries, int mask, CValue key) {
|
static CTableEntry *findEntry(CState *state, CTableEntry *entries, int mask, CValue key)
|
||||||
|
{
|
||||||
uint32_t hash = getValueHash(&key);
|
uint32_t hash = getValueHash(&key);
|
||||||
uint32_t indx = hash & mask; // since we know the capacity will *always* be a power of 2, we can use bitwise & to perform a MUCH faster mod operation
|
uint32_t indx = hash & mask; // since we know the capacity will *always* be a power of 2, we can
|
||||||
|
// use bitwise & to perform a MUCH faster mod operation
|
||||||
CTableEntry *tomb = NULL;
|
CTableEntry *tomb = NULL;
|
||||||
|
|
||||||
// keep looking for an open slot in the entries array
|
// keep looking for an open slot in the entries array
|
||||||
@ -112,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))
|
if (canShrink && cosmoT_checkShrink(state, tbl))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
size_t size = sizeof(CTableEntry) * newCapacity;
|
size_t size = sizeof(CTableEntry) * newCapacity;
|
||||||
int cachedCount = tbl->count;
|
int cachedCount = tbl->count;
|
||||||
int newCount, oldCap;
|
int newCount, oldCap;
|
||||||
|
|
||||||
cosmoM_checkGarbage(state, size); // if this allocation would cause a GC, run the GC
|
cosmoM_checkGarbage(state, size); // if this allocation would cause a GC, run the GC
|
||||||
|
|
||||||
if (tbl->count < cachedCount) // the GC removed some objects from this table and resized it, ignore our resize event!
|
if (tbl->count < cachedCount) // the GC removed some objects from this table and resized it,
|
||||||
|
// ignore our resize event!
|
||||||
return;
|
return;
|
||||||
|
|
||||||
CTableEntry *entries = cosmoM_xmalloc(state, size);
|
CTableEntry *entries = cosmoM_xmalloc(state, size);
|
||||||
@ -157,10 +171,14 @@ static void resizeTbl(CState *state, CTable *tbl, int newCapacity, bool canShrin
|
|||||||
tbl->tombstones = 0;
|
tbl->tombstones = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cosmoT_checkShrink(CState *state, CTable *tbl) {
|
bool cosmoT_checkShrink(CState *state, CTable *tbl)
|
||||||
// if count > 8 and active entries < tombstones
|
{
|
||||||
if (tbl->count > MIN_TABLE_CAPACITY && (tbl->count - tbl->tombstones < tbl->tombstones || tbl->tombstones > 50)) { // TODO: 50 should be a threshhold
|
// if count > 8 and active entries < tombstones
|
||||||
resizeTbl(state, tbl, nextPow2(tbl->count - tbl->tombstones) * GROW_FACTOR, false); // shrink based on active entries to the next pow of 2
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,7 +186,8 @@ bool cosmoT_checkShrink(CState *state, CTable *tbl) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// returns a pointer to the allocated value
|
// returns a pointer to the allocated value
|
||||||
COSMO_API CValue* cosmoT_insert(CState *state, CTable *tbl, CValue key) {
|
COSMO_API CValue *cosmoT_insert(CState *state, CTable *tbl, CValue key)
|
||||||
|
{
|
||||||
// make sure we have enough space allocated
|
// make sure we have enough space allocated
|
||||||
int cap = tbl->capacityMask + 1;
|
int cap = tbl->capacityMask + 1;
|
||||||
if (tbl->count + 1 > (int)(cap * MAX_TABLE_FILL)) {
|
if (tbl->count + 1 > (int)(cap * MAX_TABLE_FILL)) {
|
||||||
@ -178,7 +197,8 @@ COSMO_API CValue* cosmoT_insert(CState *state, CTable *tbl, CValue key) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// insert into the table
|
// insert into the table
|
||||||
CTableEntry *entry = findEntry(state, tbl->table, tbl->capacityMask, key); // -1 for our capacity mask
|
CTableEntry *entry =
|
||||||
|
findEntry(state, tbl->table, tbl->capacityMask, key); // -1 for our capacity mask
|
||||||
|
|
||||||
if (IS_NIL(entry->key)) {
|
if (IS_NIL(entry->key)) {
|
||||||
if (IS_NIL(entry->val)) // is it empty?
|
if (IS_NIL(entry->val)) // is it empty?
|
||||||
@ -191,22 +211,25 @@ COSMO_API CValue* cosmoT_insert(CState *state, CTable *tbl, CValue key) {
|
|||||||
return &entry->val;
|
return &entry->val;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cosmoT_get(CState *state, CTable *tbl, CValue key, CValue *val) {
|
bool cosmoT_get(CState *state, CTable *tbl, CValue key, CValue *val)
|
||||||
|
{
|
||||||
// sanity check
|
// sanity check
|
||||||
if (tbl->count == 0) {
|
if (tbl->count == 0) {
|
||||||
*val = cosmoV_newNil();
|
*val = cosmoV_newNil();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
CTableEntry *entry = findEntry(state, tbl->table, tbl->capacityMask, key);
|
CTableEntry *entry = findEntry(state, tbl->table, tbl->capacityMask, key);
|
||||||
*val = entry->val;
|
*val = entry->val;
|
||||||
|
|
||||||
// return if get was successful
|
// return if get was successful
|
||||||
return !(IS_NIL(entry->key));
|
return !(IS_NIL(entry->key));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cosmoT_remove(CState* state, CTable *tbl, CValue key) {
|
bool cosmoT_remove(CState *state, CTable *tbl, CValue key)
|
||||||
if (tbl->count == 0) return 0; // sanity check
|
{
|
||||||
|
if (tbl->count == 0)
|
||||||
|
return 0; // sanity check
|
||||||
|
|
||||||
CTableEntry *entry = findEntry(state, tbl->table, tbl->capacityMask, key);
|
CTableEntry *entry = findEntry(state, tbl->table, tbl->capacityMask, key);
|
||||||
if (IS_NIL(entry->key)) // sanity check
|
if (IS_NIL(entry->key)) // sanity check
|
||||||
@ -214,20 +237,26 @@ bool cosmoT_remove(CState* state, CTable *tbl, CValue key) {
|
|||||||
|
|
||||||
// crafts tombstone
|
// crafts tombstone
|
||||||
entry->key = cosmoV_newNil(); // this has to be nil
|
entry->key = cosmoV_newNil(); // this has to be nil
|
||||||
entry->val = cosmoV_newBoolean(false); // doesn't really matter what this is, as long as it isn't nil
|
entry->val =
|
||||||
|
cosmoV_newBoolean(false); // doesn't really matter what this is, as long as it isn't nil
|
||||||
tbl->tombstones++;
|
tbl->tombstones++;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns the active entry count
|
// returns the active entry count
|
||||||
COSMO_API int cosmoT_count(CTable *tbl) {
|
COSMO_API int cosmoT_count(CTable *tbl)
|
||||||
|
{
|
||||||
return tbl->count - tbl->tombstones;
|
return tbl->count - tbl->tombstones;
|
||||||
}
|
}
|
||||||
|
|
||||||
CObjString *cosmoT_lookupString(CTable *tbl, const char *str, int length, uint32_t hash) {
|
CObjString *cosmoT_lookupString(CTable *tbl, const char *str, int length, uint32_t hash)
|
||||||
if (tbl->count == 0) return 0; // sanity check
|
{
|
||||||
uint32_t indx = hash & tbl->capacityMask; // since we know the capacity will *always* be a power of 2, we can use bitwise & to perform a MUCH faster mod operation
|
if (tbl->count == 0)
|
||||||
|
return 0; // sanity check
|
||||||
|
uint32_t indx =
|
||||||
|
hash & tbl->capacityMask; // since we know the capacity will *always* be a power of 2, we
|
||||||
|
// can use bitwise & to perform a MUCH faster mod operation
|
||||||
|
|
||||||
// keep looking for an open slot in the entries array
|
// keep looking for an open slot in the entries array
|
||||||
while (true) {
|
while (true) {
|
||||||
@ -236,9 +265,10 @@ CObjString *cosmoT_lookupString(CTable *tbl, const char *str, int length, uint32
|
|||||||
// check if it's an empty slot (meaning we dont have it in the table)
|
// check if it's an empty slot (meaning we dont have it in the table)
|
||||||
if (IS_NIL(entry->key) && IS_NIL(entry->val)) {
|
if (IS_NIL(entry->key) && IS_NIL(entry->val)) {
|
||||||
return NULL;
|
return NULL;
|
||||||
} else if (IS_STRING(entry->key) && cosmoV_readString(entry->key)->length == length && memcmp(cosmoV_readString(entry->key)->str, str, length) == 0) {
|
} else if (IS_STRING(entry->key) && cosmoV_readString(entry->key)->length == length &&
|
||||||
|
memcmp(cosmoV_readString(entry->key)->str, str, length) == 0) {
|
||||||
// it's a match!
|
// it's a match!
|
||||||
return (CObjString*)cosmoV_readRef(entry->key);
|
return (CObjString *)cosmoV_readRef(entry->key);
|
||||||
}
|
}
|
||||||
|
|
||||||
indx = (indx + 1) & tbl->capacityMask; // fast mod here too
|
indx = (indx + 1) & tbl->capacityMask; // fast mod here too
|
||||||
@ -246,7 +276,8 @@ CObjString *cosmoT_lookupString(CTable *tbl, const char *str, int length, uint32
|
|||||||
}
|
}
|
||||||
|
|
||||||
// for debugging purposes
|
// for debugging purposes
|
||||||
void cosmoT_printTable(CTable *tbl, const char *name) {
|
void cosmoT_printTable(CTable *tbl, const char *name)
|
||||||
|
{
|
||||||
printf("==== [[%s]] ====\n", name);
|
printf("==== [[%s]] ====\n", name);
|
||||||
int cap = tbl->capacityMask + 1;
|
int cap = tbl->capacityMask + 1;
|
||||||
for (int i = 0; i < cap; i++) {
|
for (int i = 0; i < cap; i++) {
|
||||||
|
@ -1,17 +1,20 @@
|
|||||||
#ifndef CTABLE_H
|
#ifndef CTABLE_H
|
||||||
#define CTABLE_H
|
#define CTABLE_H
|
||||||
|
|
||||||
/* TODO: rewrite this table implementation. compared to other languages (including python!) this table is verrryyyy slow */
|
/* TODO: rewrite this table implementation. compared to other languages (including python!) this
|
||||||
|
* table is verrryyyy slow */
|
||||||
|
|
||||||
#include "cosmo.h"
|
#include "cosmo.h"
|
||||||
#include "cvalue.h"
|
#include "cvalue.h"
|
||||||
|
|
||||||
typedef struct CTableEntry {
|
typedef struct CTableEntry
|
||||||
|
{
|
||||||
CValue key;
|
CValue key;
|
||||||
CValue val;
|
CValue val;
|
||||||
} CTableEntry;
|
} CTableEntry;
|
||||||
|
|
||||||
typedef struct CTable {
|
typedef struct CTable
|
||||||
|
{
|
||||||
int count;
|
int count;
|
||||||
int capacityMask; // +1 to get the capacity
|
int capacityMask; // +1 to get the capacity
|
||||||
int tombstones;
|
int tombstones;
|
||||||
|
160
src/cvalue.c
160
src/cvalue.c
@ -1,105 +1,123 @@
|
|||||||
#include "cosmo.h"
|
|
||||||
#include "cmem.h"
|
|
||||||
#include "cvalue.h"
|
#include "cvalue.h"
|
||||||
#include "cobj.h"
|
|
||||||
|
|
||||||
void initValArray(CState *state, CValueArray *val, size_t startCapacity) {
|
#include "cmem.h"
|
||||||
|
#include "cobj.h"
|
||||||
|
#include "cosmo.h"
|
||||||
|
|
||||||
|
void initValArray(CState *state, CValueArray *val, size_t startCapacity)
|
||||||
|
{
|
||||||
val->count = 0;
|
val->count = 0;
|
||||||
val->capacity = startCapacity;
|
val->capacity = startCapacity;
|
||||||
val->values = NULL;
|
val->values = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void cleanValArray(CState *state, CValueArray *array) {
|
void cleanValArray(CState *state, CValueArray *array)
|
||||||
|
{
|
||||||
cosmoM_freearray(state, CValue, array->values, array->capacity);
|
cosmoM_freearray(state, CValue, array->values, array->capacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
void appendValArray(CState *state, CValueArray *array, CValue val) {
|
void appendValArray(CState *state, CValueArray *array, CValue val)
|
||||||
|
{
|
||||||
cosmoM_growarray(state, CValue, array->values, array->count, array->capacity);
|
cosmoM_growarray(state, CValue, array->values, array->count, array->capacity);
|
||||||
|
|
||||||
array->values[array->count++] = val;
|
array->values[array->count++] = val;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cosmoV_equal(CState *state, CValue valA, CValue valB) {
|
bool cosmoV_equal(CState *state, CValue valA, CValue valB)
|
||||||
|
{
|
||||||
if (GET_TYPE(valA) != GET_TYPE(valB)) // are they the same type?
|
if (GET_TYPE(valA) != GET_TYPE(valB)) // are they the same type?
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// compare
|
// compare
|
||||||
switch (GET_TYPE(valA)) {
|
switch (GET_TYPE(valA)) {
|
||||||
case COSMO_TBOOLEAN: return cosmoV_readBoolean(valA) == cosmoV_readBoolean(valB);
|
case COSMO_TBOOLEAN:
|
||||||
case COSMO_TNUMBER: return cosmoV_readNumber(valA) == cosmoV_readNumber(valB);
|
return cosmoV_readBoolean(valA) == cosmoV_readBoolean(valB);
|
||||||
case COSMO_TREF: return cosmoO_equal(state, cosmoV_readRef(valA), cosmoV_readRef(valB));
|
case COSMO_TNUMBER:
|
||||||
case COSMO_TNIL: return true;
|
return cosmoV_readNumber(valA) == cosmoV_readNumber(valB);
|
||||||
default:
|
case COSMO_TREF:
|
||||||
return false;
|
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)) {
|
switch (GET_TYPE(val)) {
|
||||||
case COSMO_TNUMBER: {
|
case COSMO_TNUMBER: {
|
||||||
char buf[32];
|
char buf[32];
|
||||||
int size = snprintf((char*)&buf, 32, "%.14g", cosmoV_readNumber(val));
|
int size = snprintf((char *)&buf, 32, "%.14g", cosmoV_readNumber(val));
|
||||||
return cosmoO_copyString(state, (char*)&buf, size);
|
return cosmoO_copyString(state, (char *)&buf, size);
|
||||||
}
|
}
|
||||||
case COSMO_TBOOLEAN: {
|
case COSMO_TBOOLEAN: {
|
||||||
return cosmoV_readBoolean(val) ? cosmoO_copyString(state, "true", 4) : cosmoO_copyString(state, "false", 5);
|
return cosmoV_readBoolean(val) ? cosmoO_copyString(state, "true", 4)
|
||||||
}
|
: cosmoO_copyString(state, "false", 5);
|
||||||
case COSMO_TREF: {
|
}
|
||||||
return cosmoO_toString(state, cosmoV_readRef(val));
|
case COSMO_TREF: {
|
||||||
}
|
return cosmoO_toString(state, cosmoV_readRef(val));
|
||||||
case COSMO_TNIL: {
|
}
|
||||||
return cosmoO_copyString(state, "nil", 3);
|
case COSMO_TNIL: {
|
||||||
}
|
return cosmoO_copyString(state, "nil", 3);
|
||||||
default:
|
}
|
||||||
return cosmoO_copyString(state, "<unkn val>", 10);
|
default:
|
||||||
|
return cosmoO_copyString(state, "<unkn val>", 10);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cosmo_Number cosmoV_toNumber(CState *state, CValue val) {
|
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) {
|
|
||||||
switch (GET_TYPE(val)) {
|
switch (GET_TYPE(val)) {
|
||||||
case COSMO_TNIL: return "<nil>";
|
case COSMO_TNUMBER: {
|
||||||
case COSMO_TBOOLEAN: return "<bool>";
|
return cosmoV_readNumber(val);
|
||||||
case COSMO_TNUMBER: return "<number>";
|
}
|
||||||
case COSMO_TREF: return cosmoO_typeStr(cosmoV_readRef(val));
|
case COSMO_TBOOLEAN: {
|
||||||
|
return cosmoV_readBoolean(val) ? 1 : 0;
|
||||||
default:
|
}
|
||||||
return "<unkn val>";
|
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)) {
|
switch (GET_TYPE(val)) {
|
||||||
case COSMO_TNUMBER:
|
case COSMO_TNIL:
|
||||||
printf("%g", cosmoV_readNumber(val));
|
return "<nil>";
|
||||||
break;
|
case COSMO_TBOOLEAN:
|
||||||
case COSMO_TBOOLEAN:
|
return "<bool>";
|
||||||
printf(cosmoV_readBoolean(val) ? "true" : "false");
|
case COSMO_TNUMBER:
|
||||||
break;
|
return "<number>";
|
||||||
case COSMO_TREF: {
|
case COSMO_TREF:
|
||||||
printObject(cosmoV_readRef(val));
|
return cosmoO_typeStr(cosmoV_readRef(val));
|
||||||
break;
|
|
||||||
}
|
default:
|
||||||
case COSMO_TNIL:
|
return "<unkn val>";
|
||||||
printf("nil");
|
}
|
||||||
break;
|
}
|
||||||
default:
|
|
||||||
printf("<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"
|
#include "cosmo.h"
|
||||||
|
|
||||||
typedef enum {
|
typedef enum
|
||||||
|
{
|
||||||
COSMO_TNUMBER, // number has to be 0 because NaN box
|
COSMO_TNUMBER, // number has to be 0 because NaN box
|
||||||
COSMO_TBOOLEAN,
|
COSMO_TBOOLEAN,
|
||||||
COSMO_TREF,
|
COSMO_TREF,
|
||||||
@ -18,8 +19,8 @@ typedef double cosmo_Number;
|
|||||||
|
|
||||||
#ifdef NAN_BOXXED
|
#ifdef NAN_BOXXED
|
||||||
/*
|
/*
|
||||||
NaN box, this is great for fitting more in the cpu cache on x86_64 or ARM64 architectures. If you don't know how this works please reference these
|
NaN box, this is great for fitting more in the cpu cache on x86_64 or ARM64 architectures.
|
||||||
two articles:
|
If you don't know how this works please reference these two articles:
|
||||||
|
|
||||||
https://leonardschuetz.ch/blog/nan-boxing/ and https://piotrduperas.com/posts/nan-boxing/
|
https://leonardschuetz.ch/blog/nan-boxing/ and https://piotrduperas.com/posts/nan-boxing/
|
||||||
|
|
||||||
@ -27,83 +28,88 @@ typedef double cosmo_Number;
|
|||||||
|
|
||||||
TL;DR: we can store payloads in the NaN value in the IEEE 754 standard.
|
TL;DR: we can store payloads in the NaN value in the IEEE 754 standard.
|
||||||
*/
|
*/
|
||||||
union CValue {
|
union CValue
|
||||||
|
{
|
||||||
uint64_t data;
|
uint64_t data;
|
||||||
cosmo_Number num;
|
cosmo_Number num;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define MASK_TYPE ((uint64_t)0x0007000000000000)
|
# define MASK_TYPE ((uint64_t)0x0007000000000000)
|
||||||
#define MASK_PAYLOAD ((uint64_t)0x0000ffffffffffff)
|
# define MASK_PAYLOAD ((uint64_t)0x0000ffffffffffff)
|
||||||
|
|
||||||
// 3 bits (low bits) are reserved for the type
|
// 3 bits (low bits) are reserved for the type
|
||||||
#define MAKE_PAYLOAD(x) ((uint64_t)(x) & MASK_PAYLOAD)
|
# define MAKE_PAYLOAD(x) ((uint64_t)(x)&MASK_PAYLOAD)
|
||||||
#define READ_PAYLOAD(x) ((x).data & MASK_PAYLOAD)
|
# define READ_PAYLOAD(x) ((x).data & MASK_PAYLOAD)
|
||||||
|
|
||||||
// The bits that must be set to indicate a quiet NaN.
|
// The bits that must be set to indicate a quiet NaN.
|
||||||
#define MASK_QUIETNAN ((uint64_t)0x7ff8000000000000)
|
# define MASK_QUIETNAN ((uint64_t)0x7ff8000000000000)
|
||||||
|
|
||||||
#define GET_TYPE(x) \
|
# define GET_TYPE(x) \
|
||||||
((((x).data & MASK_QUIETNAN) == MASK_QUIETNAN) ? (((x).data & MASK_TYPE) >> 48) : COSMO_TNUMBER)
|
((((x).data & MASK_QUIETNAN) == MASK_QUIETNAN) ? (((x).data & MASK_TYPE) >> 48) \
|
||||||
|
: COSMO_TNUMBER)
|
||||||
|
|
||||||
#define SIG_MASK (MASK_QUIETNAN | MASK_TYPE)
|
# define SIG_MASK (MASK_QUIETNAN | MASK_TYPE)
|
||||||
#define BOOL_SIG (MASK_QUIETNAN | ((uint64_t)(COSMO_TBOOLEAN) << 48))
|
# define BOOL_SIG (MASK_QUIETNAN | ((uint64_t)(COSMO_TBOOLEAN) << 48))
|
||||||
#define OBJ_SIG (MASK_QUIETNAN | ((uint64_t)(COSMO_TREF) << 48))
|
# define OBJ_SIG (MASK_QUIETNAN | ((uint64_t)(COSMO_TREF) << 48))
|
||||||
#define NIL_SIG (MASK_QUIETNAN | ((uint64_t)(COSMO_TNIL) << 48))
|
# define NIL_SIG (MASK_QUIETNAN | ((uint64_t)(COSMO_TNIL) << 48))
|
||||||
|
|
||||||
#define cosmoV_newNumber(x) ((CValue){.num = x})
|
# define cosmoV_newNumber(x) ((CValue){.num = x})
|
||||||
#define cosmoV_newBoolean(x) ((CValue){.data = MAKE_PAYLOAD(x) | BOOL_SIG})
|
# define cosmoV_newBoolean(x) ((CValue){.data = MAKE_PAYLOAD(x) | BOOL_SIG})
|
||||||
#define cosmoV_newRef(x) ((CValue){.data = MAKE_PAYLOAD((uintptr_t)x) | OBJ_SIG})
|
# define cosmoV_newRef(x) ((CValue){.data = MAKE_PAYLOAD((uintptr_t)x) | OBJ_SIG})
|
||||||
#define cosmoV_newNil() ((CValue){.data = NIL_SIG})
|
# define cosmoV_newNil() ((CValue){.data = NIL_SIG})
|
||||||
|
|
||||||
#define cosmoV_readNumber(x) ((x).num)
|
# define cosmoV_readNumber(x) ((x).num)
|
||||||
#define cosmoV_readBoolean(x) ((bool)READ_PAYLOAD(x))
|
# define cosmoV_readBoolean(x) ((bool)READ_PAYLOAD(x))
|
||||||
#define cosmoV_readRef(x) ((CObj*)READ_PAYLOAD(x))
|
# define cosmoV_readRef(x) ((CObj *)READ_PAYLOAD(x))
|
||||||
|
|
||||||
#define IS_NUMBER(x) (((x).data & MASK_QUIETNAN) != MASK_QUIETNAN)
|
# define IS_NUMBER(x) (((x).data & MASK_QUIETNAN) != MASK_QUIETNAN)
|
||||||
#define IS_BOOLEAN(x) (((x).data & SIG_MASK) == BOOL_SIG)
|
# define IS_BOOLEAN(x) (((x).data & SIG_MASK) == BOOL_SIG)
|
||||||
#define IS_NIL(x) (((x).data & SIG_MASK) == NIL_SIG)
|
# define IS_NIL(x) (((x).data & SIG_MASK) == NIL_SIG)
|
||||||
#define IS_REF(x) (((x).data & SIG_MASK) == OBJ_SIG)
|
# define IS_REF(x) (((x).data & SIG_MASK) == OBJ_SIG)
|
||||||
|
|
||||||
#else
|
#else
|
||||||
/*
|
/*
|
||||||
Tagged union, this is the best platform independent solution
|
Tagged union, this is the best platform independent solution
|
||||||
*/
|
*/
|
||||||
struct CValue {
|
struct CValue
|
||||||
|
{
|
||||||
CosmoType type;
|
CosmoType type;
|
||||||
union {
|
union
|
||||||
|
{
|
||||||
cosmo_Number num;
|
cosmo_Number num;
|
||||||
bool b; // boolean
|
bool b; // boolean
|
||||||
CObj *obj;
|
CObj *obj;
|
||||||
} val;
|
} val;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define GET_TYPE(x) ((x).type)
|
# define GET_TYPE(x) ((x).type)
|
||||||
|
|
||||||
// create CValues
|
// create CValues
|
||||||
|
|
||||||
#define cosmoV_newNumber(x) ((CValue){COSMO_TNUMBER, {.num = (x)}})
|
# define cosmoV_newNumber(x) ((CValue){COSMO_TNUMBER, {.num = (x)}})
|
||||||
#define cosmoV_newBoolean(x) ((CValue){COSMO_TBOOLEAN, {.b = (x)}})
|
# define cosmoV_newBoolean(x) ((CValue){COSMO_TBOOLEAN, {.b = (x)}})
|
||||||
#define cosmoV_newRef(x) ((CValue){COSMO_TREF, {.obj = (CObj*)(x)}})
|
# define cosmoV_newRef(x) ((CValue){COSMO_TREF, {.obj = (CObj *)(x)}})
|
||||||
#define cosmoV_newNil() ((CValue){COSMO_TNIL, {.num = 0}})
|
# define cosmoV_newNil() ((CValue){COSMO_TNIL, {.num = 0}})
|
||||||
|
|
||||||
// read CValues
|
// read CValues
|
||||||
|
|
||||||
#define cosmoV_readNumber(x) ((cosmo_Number)(x).val.num)
|
# define cosmoV_readNumber(x) ((cosmo_Number)(x).val.num)
|
||||||
#define cosmoV_readBoolean(x) ((bool)(x).val.b)
|
# define cosmoV_readBoolean(x) ((bool)(x).val.b)
|
||||||
|
|
||||||
// grabs the CObj* pointer from the CValue
|
// grabs the CObj* pointer from the CValue
|
||||||
#define cosmoV_readRef(x) ((CObj*)(x).val.obj)
|
# define cosmoV_readRef(x) ((CObj *)(x).val.obj)
|
||||||
|
|
||||||
#define IS_NUMBER(x) (GET_TYPE(x) == COSMO_TNUMBER)
|
# define IS_NUMBER(x) (GET_TYPE(x) == COSMO_TNUMBER)
|
||||||
#define IS_BOOLEAN(x) (GET_TYPE(x) == COSMO_TBOOLEAN)
|
# define IS_BOOLEAN(x) (GET_TYPE(x) == COSMO_TBOOLEAN)
|
||||||
#define IS_NIL(x) (GET_TYPE(x) == COSMO_TNIL)
|
# define IS_NIL(x) (GET_TYPE(x) == COSMO_TNIL)
|
||||||
#define IS_REF(x) (GET_TYPE(x) == COSMO_TREF)
|
# define IS_REF(x) (GET_TYPE(x) == COSMO_TREF)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef CValue* StkPtr;
|
typedef CValue *StkPtr;
|
||||||
|
|
||||||
struct CValueArray {
|
struct CValueArray
|
||||||
|
{
|
||||||
size_t capacity;
|
size_t capacity;
|
||||||
size_t count;
|
size_t count;
|
||||||
CValue *values;
|
CValue *values;
|
||||||
@ -117,6 +123,7 @@ void printValue(CValue val);
|
|||||||
COSMO_API bool cosmoV_equal(CState *state, CValue valA, CValue valB);
|
COSMO_API bool cosmoV_equal(CState *state, CValue valA, CValue valB);
|
||||||
COSMO_API CObjString *cosmoV_toString(CState *state, CValue val);
|
COSMO_API CObjString *cosmoV_toString(CState *state, CValue val);
|
||||||
COSMO_API cosmo_Number cosmoV_toNumber(CState *state, CValue val);
|
COSMO_API cosmo_Number cosmoV_toNumber(CState *state, CValue val);
|
||||||
COSMO_API const char *cosmoV_typeStr(CValue val); // return constant char array for corresponding type
|
COSMO_API const char *
|
||||||
|
cosmoV_typeStr(CValue val); // return constant char array for corresponding type
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
82
src/cvm.h
82
src/cvm.h
@ -1,14 +1,15 @@
|
|||||||
#ifndef COSMOVM_H
|
#ifndef COSMOVM_H
|
||||||
#define COSMOVM_H
|
#define COSMOVM_H
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include "cosmo.h"
|
#include "cosmo.h"
|
||||||
#include "cstate.h"
|
#include "cstate.h"
|
||||||
|
|
||||||
//#define VM_DEBUG
|
#include <string.h>
|
||||||
|
|
||||||
typedef enum {
|
// #define VM_DEBUG
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
COSMOVM_OK,
|
COSMOVM_OK,
|
||||||
COSMOVM_RUNTIME_ERR,
|
COSMOVM_RUNTIME_ERR,
|
||||||
COSMOVM_BUILDTIME_ERR
|
COSMOVM_BUILDTIME_ERR
|
||||||
@ -19,25 +20,26 @@ COSMO_API COSMOVMRESULT cosmoV_call(CState *state, int args, int nresults);
|
|||||||
COSMO_API COSMOVMRESULT cosmoV_pcall(CState *state, int args, int nresults);
|
COSMO_API COSMOVMRESULT cosmoV_pcall(CState *state, int args, int nresults);
|
||||||
|
|
||||||
// pushes new object onto the stack & returns a pointer to the new object
|
// pushes new object onto the stack & returns a pointer to the new object
|
||||||
COSMO_API CObjObject* cosmoV_makeObject(CState *state, int pairs);
|
COSMO_API CObjObject *cosmoV_makeObject(CState *state, int pairs);
|
||||||
COSMO_API void cosmoV_makeTable(CState *state, int pairs);
|
COSMO_API void cosmoV_makeTable(CState *state, int pairs);
|
||||||
COSMO_API void cosmoV_concat(CState *state, int vals);
|
COSMO_API void cosmoV_concat(CState *state, int vals);
|
||||||
COSMO_API void cosmoV_pushFString(CState *state, const char *format, ...);
|
COSMO_API void cosmoV_pushFString(CState *state, const char *format, ...);
|
||||||
COSMO_API void cosmoV_printError(CState *state, CObjError *err);
|
COSMO_API void cosmoV_printError(CState *state, CObjError *err);
|
||||||
COSMO_API CObjError* cosmoV_throw(CState *state);
|
COSMO_API CObjError *cosmoV_throw(CState *state);
|
||||||
COSMO_API void cosmoV_error(CState *state, const char *format, ...);
|
COSMO_API void cosmoV_error(CState *state, const char *format, ...);
|
||||||
COSMO_API void cosmo_insert(CState *state, int indx, CValue val);
|
COSMO_API void cosmo_insert(CState *state, int indx, CValue val);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Sets the default proto objects for the passed objType. Also walks through the object heap and updates protos
|
Sets the default proto objects for the passed objType. Also walks through the object heap and
|
||||||
for the passed objType if that CObj* has no proto.
|
updates protos for the passed objType if that CObj* has no proto.
|
||||||
|
|
||||||
returns true if replacing a previously registered proto object for this type
|
returns true if replacing a previously registered proto object for this type
|
||||||
*/
|
*/
|
||||||
COSMO_API bool cosmoV_registerProtoObject(CState *state, CObjType objType, CObjObject *obj);
|
COSMO_API bool cosmoV_registerProtoObject(CState *state, CObjType objType, CObjObject *obj);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
compiles string into a <closure>, if successful, <closure> will be pushed onto the stack otherwise the <error> will be pushed.
|
compiles string into a <closure>, if successful, <closure> will be pushed onto the stack
|
||||||
|
otherwise the <error> will be pushed.
|
||||||
|
|
||||||
returns:
|
returns:
|
||||||
false : <error> is at the top of the stack
|
false : <error> is at the top of the stack
|
||||||
@ -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);
|
COSMO_API bool cosmoV_compileString(CState *state, const char *src, const char *name);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
expects object to be pushed, then the key.
|
expects object to be pushed, then the key.
|
||||||
|
|
||||||
returns false if an error was thrown, returns true if the value was pushed onto the stack and the object and key were popped
|
returns false if an error was thrown, returns true if the value was pushed onto the stack and
|
||||||
|
the object and key were popped
|
||||||
*/
|
*/
|
||||||
COSMO_API bool cosmoV_get(CState *state);
|
COSMO_API bool cosmoV_get(CState *state);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
expects object to be pushed, then the key, and finally the new value.
|
expects object to be pushed, then the key, and finally the new value.
|
||||||
|
|
||||||
returns false if an error was thrown, returns true if the value was set and the object key, and value were popped
|
returns false if an error was thrown, returns true if the value was set and the object key, and
|
||||||
|
value were popped
|
||||||
*/
|
*/
|
||||||
COSMO_API bool cosmoV_set(CState *state);
|
COSMO_API bool cosmoV_set(CState *state);
|
||||||
|
|
||||||
// wraps the closure into a CObjMethod, so the function is called as an invoked method
|
// wraps the closure into a CObjMethod, so the function is called as an invoked method
|
||||||
COSMO_API bool cosmoV_getMethod(CState *state, CObj *obj, CValue key, CValue *val);
|
COSMO_API bool cosmoV_getMethod(CState *state, CObj *obj, CValue key, CValue *val);
|
||||||
|
|
||||||
// clears the stack, callstack and restores the state into a usable state after a calloverflow or another hard to recover error
|
// clears the stack, callstack and restores the state into a usable state after a calloverflow or
|
||||||
// (keeps the global table intact)
|
// another hard to recover error (keeps the global table intact)
|
||||||
COSMO_API bool cosmoV_restore(CState *state);
|
COSMO_API bool cosmoV_restore(CState *state);
|
||||||
|
|
||||||
// nice to have wrappers
|
// nice to have wrappers
|
||||||
|
|
||||||
// pushes a raw CValue to the stack, might throw an error if the stack is overflowed (with the SAFE_STACK macro on)
|
// pushes a raw CValue to the stack, might throw an error if the stack is overflowed (with the
|
||||||
static inline void cosmoV_pushValue(CState *state, CValue val) {
|
// SAFE_STACK macro on)
|
||||||
|
static inline void cosmoV_pushValue(CState *state, CValue val)
|
||||||
|
{
|
||||||
#ifdef SAFE_STACK
|
#ifdef SAFE_STACK
|
||||||
ptrdiff_t stackSize = state->top - state->stack;
|
ptrdiff_t stackSize = state->top - state->stack;
|
||||||
|
|
||||||
@ -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 (state->panic) { // we're in a panic state, let the 8 reserved slots be filled
|
||||||
if (stackSize < STACK_MAX)
|
if (stackSize < STACK_MAX)
|
||||||
*(state->top++) = val;
|
*(state->top++) = val;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,51 +97,61 @@ static inline void cosmoV_pushValue(CState *state, CValue val) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// sets stack->top to stack->top - indx
|
// sets stack->top to stack->top - indx
|
||||||
static inline StkPtr cosmoV_setTop(CState *state, int indx) {
|
static inline StkPtr cosmoV_setTop(CState *state, int indx)
|
||||||
|
{
|
||||||
state->top -= indx;
|
state->top -= indx;
|
||||||
return state->top;
|
return state->top;
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns stack->top - indx - 1
|
// returns stack->top - indx - 1
|
||||||
static inline StkPtr cosmoV_getTop(CState *state, int indx) {
|
static inline StkPtr cosmoV_getTop(CState *state, int indx)
|
||||||
|
{
|
||||||
return &state->top[-(indx + 1)];
|
return &state->top[-(indx + 1)];
|
||||||
}
|
}
|
||||||
|
|
||||||
// pops 1 value off the stack, returns the popped value
|
// pops 1 value off the stack, returns the popped value
|
||||||
static inline StkPtr cosmoV_pop(CState *state) {
|
static inline StkPtr cosmoV_pop(CState *state)
|
||||||
|
{
|
||||||
return cosmoV_setTop(state, 1);
|
return cosmoV_setTop(state, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// pushes a cosmo_Number to the stack
|
// pushes a cosmo_Number to the stack
|
||||||
static inline void cosmoV_pushNumber(CState *state, cosmo_Number num) {
|
static inline void cosmoV_pushNumber(CState *state, cosmo_Number num)
|
||||||
|
{
|
||||||
cosmoV_pushValue(state, cosmoV_newNumber(num));
|
cosmoV_pushValue(state, cosmoV_newNumber(num));
|
||||||
}
|
}
|
||||||
|
|
||||||
// pushes a boolean to the stack
|
// pushes a boolean to the stack
|
||||||
static inline void cosmoV_pushBoolean(CState *state, bool b) {
|
static inline void cosmoV_pushBoolean(CState *state, bool b)
|
||||||
|
{
|
||||||
cosmoV_pushValue(state, cosmoV_newBoolean(b));
|
cosmoV_pushValue(state, cosmoV_newBoolean(b));
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void cosmoV_pushRef(CState *state, CObj *obj) {
|
static inline void cosmoV_pushRef(CState *state, CObj *obj)
|
||||||
|
{
|
||||||
cosmoV_pushValue(state, cosmoV_newRef(obj));
|
cosmoV_pushValue(state, cosmoV_newRef(obj));
|
||||||
}
|
}
|
||||||
|
|
||||||
// pushes a C Function to the stack
|
// pushes a C Function to the stack
|
||||||
static inline void cosmoV_pushCFunction(CState *state, CosmoCFunction func) {
|
static inline void cosmoV_pushCFunction(CState *state, CosmoCFunction func)
|
||||||
cosmoV_pushRef(state, (CObj*)cosmoO_newCFunction(state, func));
|
{
|
||||||
|
cosmoV_pushRef(state, (CObj *)cosmoO_newCFunction(state, func));
|
||||||
}
|
}
|
||||||
|
|
||||||
// len is the length of the string without the NULL terminator
|
// len is the length of the string without the NULL terminator
|
||||||
static inline void cosmoV_pushLString(CState *state, const char *str, size_t len) {
|
static inline void cosmoV_pushLString(CState *state, const char *str, size_t len)
|
||||||
cosmoV_pushRef(state, (CObj*)cosmoO_copyString(state, str, len));
|
{
|
||||||
|
cosmoV_pushRef(state, (CObj *)cosmoO_copyString(state, str, len));
|
||||||
}
|
}
|
||||||
|
|
||||||
// accepts a null terminated string and copys the buffer to the VM heap
|
// accepts a null terminated string and copys the buffer to the VM heap
|
||||||
static inline void cosmoV_pushString(CState *state, const char *str) {
|
static inline void cosmoV_pushString(CState *state, const char *str)
|
||||||
|
{
|
||||||
cosmoV_pushLString(state, str, strlen(str));
|
cosmoV_pushLString(state, str, strlen(str));
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void cosmoV_pushNil(CState *state) {
|
static inline void cosmoV_pushNil(CState *state)
|
||||||
|
{
|
||||||
cosmoV_pushValue(state, cosmoV_newNil());
|
cosmoV_pushValue(state, cosmoV_newNil());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user