mirror of
https://github.com/CPunch/Cosmo.git
synced 2024-11-22 07:20:05 +00:00
added clang-format
This commit is contained in:
parent
517b0b9532
commit
7279623e24
26
.clang-format
Normal file
26
.clang-format
Normal file
@ -0,0 +1,26 @@
|
||||
---
|
||||
Language: Cpp
|
||||
# BasedOnStyle: Mozilla
|
||||
AccessModifierOffset: -2
|
||||
AlignAfterOpenBracket: Align
|
||||
AlignArrayOfStructures: Right
|
||||
AlignConsecutiveMacros: AcrossEmptyLinesAndComments
|
||||
AlignConsecutiveAssignments: None
|
||||
AlignConsecutiveBitFields: None
|
||||
AlignConsecutiveDeclarations: None
|
||||
AlignEscapedNewlines: Right
|
||||
AlignOperands: Align
|
||||
AlignTrailingComments: true
|
||||
AllowAllArgumentsOnNextLine: true
|
||||
AllowShortEnumsOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: None
|
||||
AllowShortBlocksOnASingleLine: Never
|
||||
AllowShortIfStatementsOnASingleLine: Never
|
||||
AlwaysBreakAfterReturnType: None
|
||||
BreakBeforeBraces: Mozilla
|
||||
IndentWidth: 4
|
||||
ColumnLimit: 100
|
||||
IncludeBlocks: Regroup
|
||||
IndentPPDirectives: AfterHash
|
||||
...
|
||||
|
@ -9,7 +9,7 @@ proto Test
|
||||
end
|
||||
|
||||
// stressing the GC
|
||||
for (var i = 0; ; i++) do
|
||||
for (var i = 0; i < 100000; i++) do
|
||||
var x = Test("Hello world " .. i)
|
||||
x:print()
|
||||
end
|
303
src/cbaselib.c
303
src/cbaselib.c
@ -1,15 +1,18 @@
|
||||
#include "cbaselib.h"
|
||||
#include "cvm.h"
|
||||
#include "cvalue.h"
|
||||
#include "cobj.h"
|
||||
|
||||
#include "cmem.h"
|
||||
#include "cobj.h"
|
||||
#include "cvalue.h"
|
||||
#include "cvm.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
// ================================================================ [BASELIB] ================================================================
|
||||
// ================================================================ [BASELIB]
|
||||
// ================================================================
|
||||
|
||||
int cosmoB_print(CState *state, int nargs, CValue *args) {
|
||||
int cosmoB_print(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
for (int i = 0; i < nargs; i++) {
|
||||
if (IS_REF(args[i])) { // if its a CObj*, generate the CObjString
|
||||
CObjString *str = cosmoV_toString(state, args[i]);
|
||||
@ -23,7 +26,8 @@ int cosmoB_print(CState *state, int nargs, CValue *args) {
|
||||
return 0; // print doesn't return any args
|
||||
}
|
||||
|
||||
int cosmoB_assert(CState *state, int nargs, CValue *args) {
|
||||
int cosmoB_assert(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs < 1 || nargs > 2) {
|
||||
cosmoV_error(state, "assert() expected 1 or 2 arguments, got %d!", nargs);
|
||||
return 0; // nothing pushed onto the stack to return
|
||||
@ -31,7 +35,8 @@ int cosmoB_assert(CState *state, int nargs, CValue *args) {
|
||||
|
||||
if (!IS_BOOLEAN(args[0]) || (nargs == 2 && !IS_STRING(args[1]))) {
|
||||
if (nargs == 2) {
|
||||
cosmoV_typeError(state, "assert()", "<boolean>, <string>", "%s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
|
||||
cosmoV_typeError(state, "assert()", "<boolean>, <string>", "%s, %s",
|
||||
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
|
||||
} else {
|
||||
cosmoV_typeError(state, "assert()", "<boolean>", "%s", cosmoV_typeStr(args[0]));
|
||||
}
|
||||
@ -44,7 +49,8 @@ int cosmoB_assert(CState *state, int nargs, CValue *args) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cosmoB_type(CState *state, int nargs, CValue *args) {
|
||||
int cosmoB_type(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "type() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
@ -55,7 +61,8 @@ int cosmoB_type(CState *state, int nargs, CValue *args) {
|
||||
return 1; // 1 return value, the type string :D
|
||||
}
|
||||
|
||||
int cosmoB_pcall(CState *state, int nargs, CValue *args) {
|
||||
int cosmoB_pcall(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs < 1) {
|
||||
cosmoV_error(state, "pcall() expected at least 1 argument!");
|
||||
return 0;
|
||||
@ -75,7 +82,8 @@ int cosmoB_pcall(CState *state, int nargs, CValue *args) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
int cosmoB_tonumber(CState *state, int nargs, CValue *args) {
|
||||
int cosmoB_tonumber(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "tonumber() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
@ -85,7 +93,8 @@ int cosmoB_tonumber(CState *state, int nargs, CValue *args) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int cosmoB_tostring(CState *state, int nargs, CValue *args) {
|
||||
int cosmoB_tostring(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "tostring() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
@ -95,7 +104,8 @@ int cosmoB_tostring(CState *state, int nargs, CValue *args) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int cosmoB_loadstring(CState *state, int nargs, CValue *args) {
|
||||
int cosmoB_loadstring(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "loadstring() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
@ -113,7 +123,8 @@ int cosmoB_loadstring(CState *state, int nargs, CValue *args) {
|
||||
return 2; // <boolean>, <closure> or <error>
|
||||
}
|
||||
|
||||
int cosmoB_error(CState *state, int nargs, CValue *args) {
|
||||
int cosmoB_error(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "error() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
@ -129,28 +140,13 @@ int cosmoB_error(CState *state, int nargs, CValue *args) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cosmoB_loadLibrary(CState *state) {
|
||||
const char *identifiers[] = {
|
||||
"print",
|
||||
"assert",
|
||||
"type",
|
||||
"pcall",
|
||||
"tonumber",
|
||||
"tostring",
|
||||
"loadstring",
|
||||
"error"
|
||||
};
|
||||
void cosmoB_loadLibrary(CState *state)
|
||||
{
|
||||
const char *identifiers[] = {"print", "assert", "type", "pcall",
|
||||
"tonumber", "tostring", "loadstring", "error"};
|
||||
|
||||
CosmoCFunction baseLib[] = {
|
||||
cosmoB_print,
|
||||
cosmoB_assert,
|
||||
cosmoB_type,
|
||||
cosmoB_pcall,
|
||||
cosmoB_tonumber,
|
||||
cosmoB_tostring,
|
||||
cosmoB_loadstring,
|
||||
cosmoB_error
|
||||
};
|
||||
CosmoCFunction baseLib[] = {cosmoB_print, cosmoB_assert, cosmoB_type, cosmoB_pcall,
|
||||
cosmoB_tonumber, cosmoB_tostring, cosmoB_loadstring, cosmoB_error};
|
||||
|
||||
int i;
|
||||
for (i = 0; i < sizeof(identifiers) / sizeof(identifiers[0]); i++) {
|
||||
@ -167,9 +163,11 @@ void cosmoB_loadLibrary(CState *state) {
|
||||
cosmoB_loadMathLib(state);
|
||||
}
|
||||
|
||||
// ================================================================ [OBJECT.*] ================================================================
|
||||
// ================================================================ [OBJECT.*]
|
||||
// ================================================================
|
||||
|
||||
int cosmoB_osetProto(CState *state, int nargs, CValue *args) {
|
||||
int cosmoB_osetProto(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs == 2) {
|
||||
CObj *obj = cosmoV_readRef(args[0]); // object to set proto too
|
||||
CObjObject *proto = cosmoV_readObject(args[1]);
|
||||
@ -182,7 +180,8 @@ int cosmoB_osetProto(CState *state, int nargs, CValue *args) {
|
||||
return 0; // nothing
|
||||
}
|
||||
|
||||
int cosmoB_ogetProto(CState *state, int nargs, CValue *args) {
|
||||
int cosmoB_ogetProto(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "Expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
@ -193,14 +192,16 @@ int cosmoB_ogetProto(CState *state, int nargs, CValue *args) {
|
||||
return 1; // 1 result
|
||||
}
|
||||
|
||||
int cosmoB_oisChild(CState *state, int nargs, CValue *args) {
|
||||
int cosmoB_oisChild(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 2) {
|
||||
cosmoV_error(state, "object.ischild() expected 2 arguments, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!IS_REF(args[0]) || !IS_OBJECT(args[1])) {
|
||||
cosmoV_typeError(state, "object.ischild()", "<reference obj>, <object>", "%s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
|
||||
cosmoV_typeError(state, "object.ischild()", "<reference obj>, <object>", "%s, %s",
|
||||
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -212,14 +213,11 @@ int cosmoB_oisChild(CState *state, int nargs, CValue *args) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
COSMO_API void cosmoB_loadObjLib(CState *state) {
|
||||
const char *identifiers[] = {
|
||||
"ischild"
|
||||
};
|
||||
COSMO_API void cosmoB_loadObjLib(CState *state)
|
||||
{
|
||||
const char *identifiers[] = {"ischild"};
|
||||
|
||||
CosmoCFunction objLib[] = {
|
||||
cosmoB_oisChild
|
||||
};
|
||||
CosmoCFunction objLib[] = {cosmoB_oisChild};
|
||||
|
||||
// make object library object
|
||||
cosmoV_pushString(state, "object");
|
||||
@ -256,10 +254,12 @@ COSMO_API void cosmoB_loadObjLib(CState *state) {
|
||||
cosmoV_register(state, 1);
|
||||
}
|
||||
|
||||
// ================================================================ [OS.*] ================================================================
|
||||
// ================================================================ [OS.*]
|
||||
// ================================================================
|
||||
|
||||
// os.read()
|
||||
int cosmoB_osRead(CState *state, int nargs, CValue *args) {
|
||||
int cosmoB_osRead(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "os.read() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
@ -303,7 +303,8 @@ int cosmoB_osRead(CState *state, int nargs, CValue *args) {
|
||||
}
|
||||
|
||||
// os.time()
|
||||
int cosmoB_osTime(CState *state, int nargs, CValue *args) {
|
||||
int cosmoB_osTime(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
struct timeval time;
|
||||
if (nargs > 0) {
|
||||
cosmoV_error(state, "os.time() expected no arguments, got %d!", nargs);
|
||||
@ -316,7 +317,8 @@ int cosmoB_osTime(CState *state, int nargs, CValue *args) {
|
||||
}
|
||||
|
||||
// os.system()
|
||||
int cosmoB_osSystem(CState *state, int nargs, CValue *args) {
|
||||
int cosmoB_osSystem(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "os.system() expects 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
@ -332,18 +334,11 @@ int cosmoB_osSystem(CState *state, int nargs, CValue *args) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
COSMO_API void cosmoB_loadOSLib(CState *state) {
|
||||
const char *identifiers[] = {
|
||||
"read",
|
||||
"time",
|
||||
"system"
|
||||
};
|
||||
COSMO_API void cosmoB_loadOSLib(CState *state)
|
||||
{
|
||||
const char *identifiers[] = {"read", "time", "system"};
|
||||
|
||||
CosmoCFunction osLib[] = {
|
||||
cosmoB_osRead,
|
||||
cosmoB_osTime,
|
||||
cosmoB_osSystem
|
||||
};
|
||||
CosmoCFunction osLib[] = {cosmoB_osRead, cosmoB_osTime, cosmoB_osSystem};
|
||||
|
||||
cosmoV_pushString(state, "os");
|
||||
|
||||
@ -357,13 +352,16 @@ COSMO_API void cosmoB_loadOSLib(CState *state) {
|
||||
cosmoV_register(state, 1); // register the os.* object to the global table
|
||||
}
|
||||
|
||||
// ================================================================ [STRING.*] ================================================================
|
||||
// ================================================================ [STRING.*]
|
||||
// ================================================================
|
||||
|
||||
// string.sub
|
||||
int cosmoB_sSub(CState *state, int nargs, CValue *args) {
|
||||
int cosmoB_sSub(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs == 2) {
|
||||
if (!IS_STRING(args[0]) || !IS_NUMBER(args[1])) {
|
||||
cosmoV_typeError(state, "string.sub()", "<string>, <number>", "%s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
|
||||
cosmoV_typeError(state, "string.sub()", "<string>, <number>", "%s, %s",
|
||||
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -372,14 +370,17 @@ int cosmoB_sSub(CState *state, int nargs, CValue *args) {
|
||||
|
||||
// make sure we stay within memory
|
||||
if (indx < 0 || indx >= str->length) {
|
||||
cosmoV_error(state, "string.sub() expected index to be 0-%d, got %d!", str->length - 1, indx);
|
||||
cosmoV_error(state, "string.sub() expected index to be 0-%d, got %d!", str->length - 1,
|
||||
indx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
cosmoV_pushLString(state, str->str + ((int)indx), str->length - ((int)indx));
|
||||
} else if (nargs == 3) {
|
||||
if (!IS_STRING(args[0]) || !IS_NUMBER(args[1]) || !IS_NUMBER(args[2])) {
|
||||
cosmoV_typeError(state, "string.sub()", "<string>, <number>, <number>", "%s, %s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]), cosmoV_typeStr(args[2]));
|
||||
cosmoV_typeError(state, "string.sub()", "<string>, <number>, <number>", "%s, %s, %s",
|
||||
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]),
|
||||
cosmoV_typeStr(args[2]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -389,7 +390,9 @@ int cosmoB_sSub(CState *state, int nargs, CValue *args) {
|
||||
|
||||
// make sure we stay within memory
|
||||
if (indx + length < 0 || indx + length >= str->length || indx < 0 || indx >= str->length) {
|
||||
cosmoV_error(state, "string.sub() expected subbed string goes out of bounds, max length is %d!", str->length);
|
||||
cosmoV_error(
|
||||
state, "string.sub() expected subbed string goes out of bounds, max length is %d!",
|
||||
str->length);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -403,10 +406,12 @@ int cosmoB_sSub(CState *state, int nargs, CValue *args) {
|
||||
}
|
||||
|
||||
// string.find
|
||||
int cosmoB_sFind(CState *state, int nargs, CValue *args) {
|
||||
int cosmoB_sFind(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs == 2) {
|
||||
if (!IS_STRING(args[0]) || !IS_STRING(args[1])) {
|
||||
cosmoV_typeError(state, "string.find()", "<string>, <string>", "%s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
|
||||
cosmoV_typeError(state, "string.find()", "<string>, <string>", "%s, %s",
|
||||
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -425,7 +430,9 @@ int cosmoB_sFind(CState *state, int nargs, CValue *args) {
|
||||
cosmoV_pushNumber(state, indx - str->str);
|
||||
} else if (nargs == 3) {
|
||||
if (!IS_STRING(args[0]) || !IS_STRING(args[1]) || !IS_NUMBER(args[2])) {
|
||||
cosmoV_typeError(state, "string.find()", "<string>, <string>, <number>", "%s, %s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]), cosmoV_typeStr(args[2]));
|
||||
cosmoV_typeError(state, "string.find()", "<string>, <string>, <number>", "%s, %s, %s",
|
||||
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]),
|
||||
cosmoV_typeStr(args[2]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -452,14 +459,16 @@ int cosmoB_sFind(CState *state, int nargs, CValue *args) {
|
||||
}
|
||||
|
||||
// string.split
|
||||
int cosmoB_sSplit(CState *state, int nargs, CValue *args) {
|
||||
int cosmoB_sSplit(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 2) {
|
||||
cosmoV_error(state, "string.split() expected 2 arguments, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!IS_STRING(args[0]) || !IS_STRING(args[1])) {
|
||||
cosmoV_typeError(state, "string.split()", "<string>, <string>", "%s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
|
||||
cosmoV_typeError(state, "string.split()", "<string>, <string>", "%s, %s",
|
||||
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -475,7 +484,8 @@ int cosmoB_sSplit(CState *state, int nargs, CValue *args) {
|
||||
nIndx = strstr(indx, ptrn->str);
|
||||
|
||||
cosmoV_pushNumber(state, nEntries++);
|
||||
cosmoV_pushLString(state, indx, nIndx == NULL ? str->length - (indx - str->str) : nIndx - indx);
|
||||
cosmoV_pushLString(state, indx,
|
||||
nIndx == NULL ? str->length - (indx - str->str) : nIndx - indx);
|
||||
|
||||
indx = nIndx + ptrn->length;
|
||||
} while (nIndx != NULL);
|
||||
@ -486,7 +496,8 @@ int cosmoB_sSplit(CState *state, int nargs, CValue *args) {
|
||||
}
|
||||
|
||||
// string.byte
|
||||
int cosmoB_sByte(CState *state, int nargs, CValue *args) {
|
||||
int cosmoB_sByte(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "string.byte() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
@ -500,8 +511,8 @@ int cosmoB_sByte(CState *state, int nargs, CValue *args) {
|
||||
CObjString *str = cosmoV_readString(args[0]);
|
||||
|
||||
if (str->length < 1) {
|
||||
// the length of the string is less than 1, in the future I might throw an error for this, but
|
||||
// for now im going to copy lua and just return a nil
|
||||
// the length of the string is less than 1, in the future I might throw an error for this,
|
||||
// but for now im going to copy lua and just return a nil
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -511,7 +522,8 @@ int cosmoB_sByte(CState *state, int nargs, CValue *args) {
|
||||
}
|
||||
|
||||
// string.char
|
||||
int cosmoB_sChar(CState *state, int nargs, CValue *args) {
|
||||
int cosmoB_sChar(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "string.char() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
@ -522,7 +534,8 @@ int cosmoB_sChar(CState *state, int nargs, CValue *args) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// small side effect of truncating the number, but ignoring the decimal instead of throwing an error is the better option imo
|
||||
// small side effect of truncating the number, but ignoring the decimal instead of throwing an
|
||||
// error is the better option imo
|
||||
int num = (int)cosmoV_readNumber(args[0]);
|
||||
char c = num;
|
||||
|
||||
@ -536,7 +549,8 @@ int cosmoB_sChar(CState *state, int nargs, CValue *args) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int cosmoB_sLen(CState *state, int nargs, CValue *args) {
|
||||
int cosmoB_sLen(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs < 1) {
|
||||
cosmoV_error(state, "string.len() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
@ -552,7 +566,8 @@ int cosmoB_sLen(CState *state, int nargs, CValue *args) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int cosmoB_sRep(CState *state, int nargs, CValue *args) {
|
||||
int cosmoB_sRep(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 2) {
|
||||
cosmoV_error(state, "string.rep() expected 2 arguments, got %d!", nargs);
|
||||
return 0;
|
||||
@ -560,7 +575,8 @@ int cosmoB_sRep(CState *state, int nargs, CValue *args) {
|
||||
|
||||
// expects <string>, <number>
|
||||
if (!IS_STRING(args[0]) || !IS_NUMBER(args[1])) {
|
||||
cosmoV_typeError(state, "string.rep", "<string>, <number>", "%s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
|
||||
cosmoV_typeError(state, "string.rep", "<string>, <number>", "%s, %s",
|
||||
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -588,26 +604,12 @@ int cosmoB_sRep(CState *state, int nargs, CValue *args) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
void cosmoB_loadStrLib(CState *state) {
|
||||
const char *identifiers[] = {
|
||||
"sub",
|
||||
"find",
|
||||
"split",
|
||||
"byte",
|
||||
"char",
|
||||
"len",
|
||||
"rep"
|
||||
};
|
||||
void cosmoB_loadStrLib(CState *state)
|
||||
{
|
||||
const char *identifiers[] = {"sub", "find", "split", "byte", "char", "len", "rep"};
|
||||
|
||||
CosmoCFunction strLib[] = {
|
||||
cosmoB_sSub,
|
||||
cosmoB_sFind,
|
||||
cosmoB_sSplit,
|
||||
cosmoB_sByte,
|
||||
cosmoB_sChar,
|
||||
cosmoB_sLen,
|
||||
cosmoB_sRep
|
||||
};
|
||||
CosmoCFunction strLib[] = {cosmoB_sSub, cosmoB_sFind, cosmoB_sSplit, cosmoB_sByte,
|
||||
cosmoB_sChar, cosmoB_sLen, cosmoB_sRep};
|
||||
|
||||
// make string library object
|
||||
cosmoV_pushString(state, "string");
|
||||
@ -626,10 +628,12 @@ void cosmoB_loadStrLib(CState *state) {
|
||||
cosmoV_register(state, 1);
|
||||
}
|
||||
|
||||
// ================================================================ [MATH] ================================================================
|
||||
// ================================================================ [MATH]
|
||||
// ================================================================
|
||||
|
||||
// math.abs
|
||||
int cosmoB_mAbs(CState *state, int nargs, CValue *args) {
|
||||
int cosmoB_mAbs(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "math.abs() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
@ -645,7 +649,8 @@ int cosmoB_mAbs(CState *state, int nargs, CValue *args) {
|
||||
}
|
||||
|
||||
// math.floor
|
||||
int cosmoB_mFloor(CState *state, int nargs, CValue *args) {
|
||||
int cosmoB_mFloor(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "math.floor() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
@ -661,7 +666,8 @@ int cosmoB_mFloor(CState *state, int nargs, CValue *args) {
|
||||
}
|
||||
|
||||
// math.ceil
|
||||
int cosmoB_mCeil(CState *state, int nargs, CValue *args) {
|
||||
int cosmoB_mCeil(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "math.ceil() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
@ -684,7 +690,8 @@ int cosmoB_mCeil(CState *state, int nargs, CValue *args) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int cosmoB_mSin(CState *state, int nargs, CValue *args) {
|
||||
int cosmoB_mSin(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "math.sin() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
@ -699,7 +706,8 @@ int cosmoB_mSin(CState *state, int nargs, CValue *args) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int cosmoB_mCos(CState *state, int nargs, CValue *args) {
|
||||
int cosmoB_mCos(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "math.cos() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
@ -714,7 +722,8 @@ int cosmoB_mCos(CState *state, int nargs, CValue *args) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int cosmoB_mTan(CState *state, int nargs, CValue *args) {
|
||||
int cosmoB_mTan(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "math.tan() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
@ -729,7 +738,8 @@ int cosmoB_mTan(CState *state, int nargs, CValue *args) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int cosmoB_mASin(CState *state, int nargs, CValue *args) {
|
||||
int cosmoB_mASin(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "math.asin() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
@ -744,7 +754,8 @@ int cosmoB_mASin(CState *state, int nargs, CValue *args) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int cosmoB_mACos(CState *state, int nargs, CValue *args) {
|
||||
int cosmoB_mACos(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "math.acos() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
@ -759,7 +770,8 @@ int cosmoB_mACos(CState *state, int nargs, CValue *args) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int cosmoB_mATan(CState *state, int nargs, CValue *args) {
|
||||
int cosmoB_mATan(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "math.atan() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
@ -774,7 +786,8 @@ int cosmoB_mATan(CState *state, int nargs, CValue *args) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int cosmoB_mRad(CState *state, int nargs, CValue *args) {
|
||||
int cosmoB_mRad(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "math.rad() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
@ -790,7 +803,8 @@ int cosmoB_mRad(CState *state, int nargs, CValue *args) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int cosmoB_mDeg(CState *state, int nargs, CValue *args) {
|
||||
int cosmoB_mDeg(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "math.deg() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
@ -806,34 +820,14 @@ int cosmoB_mDeg(CState *state, int nargs, CValue *args) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
void cosmoB_loadMathLib(CState *state) {
|
||||
const char *identifiers[] = {
|
||||
"abs",
|
||||
"floor",
|
||||
"ceil",
|
||||
"sin",
|
||||
"cos",
|
||||
"tan",
|
||||
"asin",
|
||||
"acos",
|
||||
"atan",
|
||||
"rad",
|
||||
"deg"
|
||||
};
|
||||
void cosmoB_loadMathLib(CState *state)
|
||||
{
|
||||
const char *identifiers[] = {"abs", "floor", "ceil", "sin", "cos", "tan",
|
||||
"asin", "acos", "atan", "rad", "deg"};
|
||||
|
||||
CosmoCFunction mathLib[] = {
|
||||
cosmoB_mAbs,
|
||||
cosmoB_mFloor,
|
||||
cosmoB_mCeil,
|
||||
cosmoB_mSin,
|
||||
cosmoB_mCos,
|
||||
cosmoB_mTan,
|
||||
cosmoB_mASin,
|
||||
cosmoB_mACos,
|
||||
cosmoB_mATan,
|
||||
cosmoB_mRad,
|
||||
cosmoB_mDeg
|
||||
};
|
||||
CosmoCFunction mathLib[] = {cosmoB_mAbs, cosmoB_mFloor, cosmoB_mCeil, cosmoB_mSin,
|
||||
cosmoB_mCos, cosmoB_mTan, cosmoB_mASin, cosmoB_mACos,
|
||||
cosmoB_mATan, cosmoB_mRad, cosmoB_mDeg};
|
||||
|
||||
// make math library object
|
||||
cosmoV_pushString(state, "math");
|
||||
@ -852,24 +846,28 @@ void cosmoB_loadMathLib(CState *state) {
|
||||
cosmoV_register(state, 1);
|
||||
}
|
||||
|
||||
// ================================================================ [VM.*] ================================================================
|
||||
// ================================================================ [VM.*]
|
||||
// ================================================================
|
||||
|
||||
// vm.__getter["globals"]
|
||||
int cosmoB_vgetGlobal(CState *state, int nargs, CValue *args) {
|
||||
int cosmoB_vgetGlobal(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
// this function doesn't need to check anything, just return the global table
|
||||
cosmoV_pushRef(state, (CObj *)state->globals);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// vm.__setter["globals"]
|
||||
int cosmoB_vsetGlobal(CState *state, int nargs, CValue *args) {
|
||||
int cosmoB_vsetGlobal(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 2) {
|
||||
cosmoV_error(state, "Expected 2 argumenst, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!IS_TABLE(args[1])) {
|
||||
cosmoV_typeError(state, "vm.__setter[\"globals\"]", "<object>, <table>", "%s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
|
||||
cosmoV_typeError(state, "vm.__setter[\"globals\"]", "<object>, <table>", "%s, %s",
|
||||
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -879,14 +877,16 @@ int cosmoB_vsetGlobal(CState *state, int nargs, CValue *args) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cosmoB_vindexBProto(CState *state, int nargs, CValue *args) {
|
||||
int cosmoB_vindexBProto(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 2) {
|
||||
cosmoV_error(state, "Expected 2 arguments, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!IS_NUMBER(args[1])) {
|
||||
cosmoV_typeError(state, "baseProtos.__index", "<object>, <number>", "%s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
|
||||
cosmoV_typeError(state, "baseProtos.__index", "<object>, <number>", "%s, %s",
|
||||
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -905,14 +905,17 @@ int cosmoB_vindexBProto(CState *state, int nargs, CValue *args) {
|
||||
return 1; // 1 value pushed, 1 value returned
|
||||
}
|
||||
|
||||
int cosmoB_vnewindexBProto(CState *state, int nargs, CValue *args) {
|
||||
int cosmoB_vnewindexBProto(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 3) {
|
||||
cosmoV_error(state, "Expected 3 arguments, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!IS_NUMBER(args[1]) || !IS_OBJECT(args[2])) {
|
||||
cosmoV_typeError(state, "baseProtos.__newindex", "<object>, <number>, <object>", "%s, %s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]), cosmoV_typeStr(args[2]));
|
||||
cosmoV_typeError(state, "baseProtos.__newindex", "<object>, <number>, <object>",
|
||||
"%s, %s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]),
|
||||
cosmoV_typeStr(args[2]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -929,7 +932,8 @@ int cosmoB_vnewindexBProto(CState *state, int nargs, CValue *args) {
|
||||
}
|
||||
|
||||
// vm.collect()
|
||||
int cosmoB_vcollect(CState *state, int nargs, CValue *args) {
|
||||
int cosmoB_vcollect(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
// first, unfreeze the state (we start frozen on entry to any C Function)
|
||||
cosmoM_unfreezeGC(state);
|
||||
|
||||
@ -943,7 +947,8 @@ int cosmoB_vcollect(CState *state, int nargs, CValue *args) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cosmoB_loadVM(CState *state) {
|
||||
void cosmoB_loadVM(CState *state)
|
||||
{
|
||||
// make vm.* object
|
||||
cosmoV_pushString(state, "vm");
|
||||
|
||||
|
@ -32,7 +32,8 @@ COSMO_API void cosmoB_loadOSLib(CState *state);
|
||||
- string.char & <string>:char()
|
||||
- string.rep & <string>:rep()
|
||||
|
||||
The base proto object for strings is also set, allowing you to invoke the string.* api through string objects, eg.
|
||||
The base proto object for strings is also set, allowing you to invoke the string.* api through
|
||||
string objects, eg.
|
||||
`"hello world":split(" ")` is equivalent to `string.split("hello world", " ")`
|
||||
*/
|
||||
COSMO_API void cosmoB_loadStrLib(CState *state);
|
||||
|
29
src/cchunk.c
29
src/cchunk.c
@ -1,16 +1,19 @@
|
||||
#include "cmem.h"
|
||||
#include "cchunk.h"
|
||||
|
||||
#include "cmem.h"
|
||||
#include "cobj.h"
|
||||
#include "cvalue.h"
|
||||
#include "cvm.h"
|
||||
#include "cobj.h"
|
||||
|
||||
CChunk *newChunk(CState* state, size_t startCapacity) {
|
||||
CChunk *newChunk(CState *state, size_t startCapacity)
|
||||
{
|
||||
CChunk *chunk = cosmoM_xmalloc(state, sizeof(CChunk));
|
||||
initChunk(state, chunk, startCapacity);
|
||||
return chunk;
|
||||
}
|
||||
|
||||
void initChunk(CState* state, CChunk *chunk, size_t startCapacity) {
|
||||
void initChunk(CState *state, CChunk *chunk, size_t startCapacity)
|
||||
{
|
||||
chunk->capacity = startCapacity;
|
||||
chunk->lineCapacity = startCapacity;
|
||||
chunk->count = 0;
|
||||
@ -21,7 +24,8 @@ void initChunk(CState* state, CChunk *chunk, size_t startCapacity) {
|
||||
initValArray(state, &chunk->constants, ARRAY_START);
|
||||
}
|
||||
|
||||
void cleanChunk(CState* state, CChunk *chunk) {
|
||||
void cleanChunk(CState *state, CChunk *chunk)
|
||||
{
|
||||
// first, free the chunk buffer
|
||||
cosmoM_freearray(state, INSTRUCTION, chunk->buf, chunk->capacity);
|
||||
// then the line info
|
||||
@ -30,13 +34,15 @@ void cleanChunk(CState* state, CChunk *chunk) {
|
||||
cleanValArray(state, &chunk->constants);
|
||||
}
|
||||
|
||||
void freeChunk(CState* state, CChunk *chunk) {
|
||||
void freeChunk(CState *state, CChunk *chunk)
|
||||
{
|
||||
cleanChunk(state, chunk);
|
||||
// now, free the wrapper struct
|
||||
cosmoM_free(state, CChunk, chunk);
|
||||
}
|
||||
|
||||
int addConstant(CState* state, CChunk *chunk, CValue value) {
|
||||
int addConstant(CState *state, CChunk *chunk, CValue value)
|
||||
{
|
||||
// before adding the constant, check if we already have it
|
||||
for (size_t i = 0; i < chunk->constants.count; i++) {
|
||||
if (cosmoV_equal(state, value, chunk->constants.values[i]))
|
||||
@ -49,9 +55,11 @@ int addConstant(CState* state, CChunk *chunk, CValue value) {
|
||||
return chunk->constants.count - 1; // return the index of the new constants
|
||||
}
|
||||
|
||||
// ================================================================ [WRITE TO CHUNK] ================================================================
|
||||
// ================================================================ [WRITE TO CHUNK]
|
||||
// ================================================================
|
||||
|
||||
void writeu8Chunk(CState* state, CChunk *chunk, INSTRUCTION i, int line) {
|
||||
void writeu8Chunk(CState *state, CChunk *chunk, INSTRUCTION i, int line)
|
||||
{
|
||||
// does the buffer need to be reallocated?
|
||||
cosmoM_growarray(state, INSTRUCTION, chunk->buf, chunk->count, chunk->capacity);
|
||||
cosmoM_growarray(state, int, chunk->lineInfo, chunk->count, chunk->lineCapacity);
|
||||
@ -61,7 +69,8 @@ void writeu8Chunk(CState* state, CChunk *chunk, INSTRUCTION i, int line) {
|
||||
chunk->buf[chunk->count++] = i;
|
||||
}
|
||||
|
||||
void writeu16Chunk(CState* state, CChunk *chunk, uint16_t i, int line) {
|
||||
void writeu16Chunk(CState *state, CChunk *chunk, uint16_t i, int line)
|
||||
{
|
||||
INSTRUCTION *buffer = (INSTRUCTION *)(&i);
|
||||
int sz = sizeof(uint16_t) / sizeof(INSTRUCTION);
|
||||
|
||||
|
12
src/cchunk.h
12
src/cchunk.h
@ -1,12 +1,12 @@
|
||||
#ifndef CCHUNK_H
|
||||
#define CCHUNK_H
|
||||
|
||||
#include "cosmo.h"
|
||||
|
||||
#include "coperators.h"
|
||||
#include "cosmo.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
|
||||
INSTRUCTION *buf; // whole chunk
|
||||
@ -26,11 +26,13 @@ void writeu8Chunk(CState* state, CChunk *chunk, INSTRUCTION i, int line);
|
||||
void writeu16Chunk(CState *state, CChunk *chunk, uint16_t i, int line);
|
||||
|
||||
// read from chunk
|
||||
static inline INSTRUCTION readu8Chunk(CChunk *chunk, int offset) {
|
||||
static inline INSTRUCTION readu8Chunk(CChunk *chunk, int offset)
|
||||
{
|
||||
return chunk->buf[offset];
|
||||
}
|
||||
|
||||
static inline uint16_t readu16Chunk(CChunk *chunk, int offset) {
|
||||
static inline uint16_t readu16Chunk(CChunk *chunk, int offset)
|
||||
{
|
||||
return *((uint16_t *)(&chunk->buf[offset]));
|
||||
}
|
||||
|
||||
|
51
src/cdebug.c
51
src/cdebug.c
@ -1,49 +1,62 @@
|
||||
#include "cdebug.h"
|
||||
#include "cvalue.h"
|
||||
#include "cobj.h"
|
||||
|
||||
void printIndent(int indent) {
|
||||
#include "cobj.h"
|
||||
#include "cvalue.h"
|
||||
|
||||
void printIndent(int indent)
|
||||
{
|
||||
for (int i = 0; i < indent; i++)
|
||||
printf("\t");
|
||||
}
|
||||
|
||||
int simpleInstruction(const char *name, int offset) {
|
||||
int simpleInstruction(const char *name, int offset)
|
||||
{
|
||||
printf("%s", name);
|
||||
return offset + 1; // consume opcode
|
||||
}
|
||||
|
||||
int u8OperandInstruction(const char *name, CChunk *chunk, int offset) {
|
||||
int u8OperandInstruction(const char *name, CChunk *chunk, int offset)
|
||||
{
|
||||
printf("%-16s [%03d]", name, readu8Chunk(chunk, offset + 1));
|
||||
return offset + 2;
|
||||
}
|
||||
|
||||
int u16OperandInstruction(const char *name, CChunk *chunk, int offset) {
|
||||
int u16OperandInstruction(const char *name, CChunk *chunk, int offset)
|
||||
{
|
||||
printf("%-16s [%05d]", name, readu16Chunk(chunk, offset + 1));
|
||||
return offset + 1 + (sizeof(uint16_t) / sizeof(INSTRUCTION));
|
||||
}
|
||||
|
||||
int JumpInstruction(const char *name, CChunk *chunk, int offset, int dir) {
|
||||
int JumpInstruction(const char *name, CChunk *chunk, int offset, int dir)
|
||||
{
|
||||
int jmp = ((int)readu16Chunk(chunk, offset + 1)) * dir;
|
||||
printf("%-16s [%05d] - jumps to %04d", name, jmp, offset + 3 + jmp);
|
||||
return offset + 1 + (sizeof(uint16_t) / sizeof(INSTRUCTION));
|
||||
}
|
||||
|
||||
int u8u8OperandInstruction(const char *name, CChunk *chunk, int offset) {
|
||||
printf("%-16s [%03d] [%03d]", name, readu8Chunk(chunk, offset + 1), readu8Chunk(chunk, offset + 2));
|
||||
int u8u8OperandInstruction(const char *name, CChunk *chunk, int offset)
|
||||
{
|
||||
printf("%-16s [%03d] [%03d]", name, readu8Chunk(chunk, offset + 1),
|
||||
readu8Chunk(chunk, offset + 2));
|
||||
return offset + 3; // op + u8 + u8
|
||||
}
|
||||
|
||||
int u8u16OperandInstruction(const char *name, CChunk *chunk, int offset) {
|
||||
printf("%-16s [%03d] [%05d]", name, readu8Chunk(chunk, offset + 1), readu16Chunk(chunk, offset + 2));
|
||||
int u8u16OperandInstruction(const char *name, CChunk *chunk, int offset)
|
||||
{
|
||||
printf("%-16s [%03d] [%05d]", name, readu8Chunk(chunk, offset + 1),
|
||||
readu16Chunk(chunk, offset + 2));
|
||||
return offset + 4; // op + u8 + u16
|
||||
}
|
||||
|
||||
int u8u8u16OperandInstruction(const char *name, CChunk *chunk, int offset) {
|
||||
printf("%-16s [%03d] [%03d] [%05d]", name, readu8Chunk(chunk, offset + 1), readu8Chunk(chunk, offset + 2), readu16Chunk(chunk, offset + 3));
|
||||
int u8u8u16OperandInstruction(const char *name, CChunk *chunk, int offset)
|
||||
{
|
||||
printf("%-16s [%03d] [%03d] [%05d]", name, readu8Chunk(chunk, offset + 1),
|
||||
readu8Chunk(chunk, offset + 2), readu16Chunk(chunk, offset + 3));
|
||||
return offset + 5; // op + u8 + u8 + u16
|
||||
}
|
||||
|
||||
int constInstruction(const char *name, CChunk *chunk, int offset) {
|
||||
int constInstruction(const char *name, CChunk *chunk, int offset)
|
||||
{
|
||||
int index = readu16Chunk(chunk, offset + 1);
|
||||
printf("%-16s [%05d] - ", name, index);
|
||||
CValue val = chunk->constants.values[index];
|
||||
@ -55,7 +68,8 @@ int constInstruction(const char *name, CChunk *chunk, int offset) {
|
||||
|
||||
// public methods in the cdebug.h header
|
||||
|
||||
void disasmChunk(CChunk *chunk, const char *name, int indent) {
|
||||
void disasmChunk(CChunk *chunk, const char *name, int indent)
|
||||
{
|
||||
printIndent(indent);
|
||||
printf("===[[ disasm for %s ]]===\n", name);
|
||||
|
||||
@ -65,7 +79,8 @@ void disasmChunk(CChunk *chunk, const char *name, int indent) {
|
||||
}
|
||||
}
|
||||
|
||||
int disasmInstr(CChunk *chunk, int offset, int indent) {
|
||||
int disasmInstr(CChunk *chunk, int offset, int indent)
|
||||
{
|
||||
printIndent(indent);
|
||||
printf("%04d ", offset);
|
||||
|
||||
@ -124,7 +139,8 @@ int disasmInstr(CChunk *chunk, int offset, int indent) {
|
||||
}
|
||||
|
||||
// print the chunk
|
||||
disasmChunk(&cobjFunc->chunk, cobjFunc->name == NULL ? UNNAMEDCHUNK : cobjFunc->name->str, indent+1);
|
||||
disasmChunk(&cobjFunc->chunk, cobjFunc->name == NULL ? UNNAMEDCHUNK : cobjFunc->name->str,
|
||||
indent + 1);
|
||||
return offset;
|
||||
}
|
||||
case OP_CLOSE:
|
||||
@ -204,6 +220,5 @@ int disasmInstr(CChunk *chunk, int offset, int indent) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
178
src/clex.c
178
src/clex.c
@ -1,4 +1,5 @@
|
||||
#include "clex.h"
|
||||
|
||||
#include "cmem.h"
|
||||
|
||||
#include <string.h>
|
||||
@ -29,44 +30,52 @@ CReservedWord reservedWords[] = {
|
||||
};
|
||||
|
||||
// returns true if current token is a heap allocated buffer
|
||||
static bool isBuffer(CLexState *state) {
|
||||
static bool isBuffer(CLexState *state)
|
||||
{
|
||||
return state->buffer != NULL;
|
||||
}
|
||||
|
||||
// marks the current token as heap allocated & allocates the buffer
|
||||
static void makeBuffer(CLexState *state) {
|
||||
state->buffer = cosmoM_xmalloc(state->cstate, sizeof(char) * 32); // start with a 32 character long buffer
|
||||
static void makeBuffer(CLexState *state)
|
||||
{
|
||||
state->buffer =
|
||||
cosmoM_xmalloc(state->cstate, sizeof(char) * 32); // start with a 32 character long buffer
|
||||
state->bufCount = 0;
|
||||
state->bufCap = 32;
|
||||
}
|
||||
|
||||
static void resetBuffer(CLexState *state) {
|
||||
static void resetBuffer(CLexState *state)
|
||||
{
|
||||
state->buffer = NULL;
|
||||
state->bufCount = 0;
|
||||
state->bufCap = 0;
|
||||
}
|
||||
|
||||
// cancels the token heap buffer and frees it
|
||||
static void freeBuffer(CLexState *state) {
|
||||
static void freeBuffer(CLexState *state)
|
||||
{
|
||||
cosmoM_freearray(state->cstate, char, state->buffer, state->bufCap);
|
||||
|
||||
resetBuffer(state);
|
||||
}
|
||||
|
||||
// adds character to buffer
|
||||
static void appendBuffer(CLexState *state, char c) {
|
||||
static void appendBuffer(CLexState *state, char c)
|
||||
{
|
||||
cosmoM_growarray(state->cstate, char, state->buffer, state->bufCount, state->bufCap);
|
||||
|
||||
state->buffer[state->bufCount++] = c;
|
||||
}
|
||||
|
||||
// saves the current character to the buffer, grows the buffer as needed
|
||||
static void saveBuffer(CLexState *state) {
|
||||
static void saveBuffer(CLexState *state)
|
||||
{
|
||||
appendBuffer(state, *state->currentChar);
|
||||
}
|
||||
|
||||
// resets the lex state buffer & returns the allocated buffer as a null terminated string
|
||||
static char *cutBuffer(CLexState *state, int *length) {
|
||||
static char *cutBuffer(CLexState *state, int *length)
|
||||
{
|
||||
// append the null terminator
|
||||
appendBuffer(state, '\0');
|
||||
|
||||
@ -84,7 +93,8 @@ static char *cutBuffer(CLexState *state, int *length) {
|
||||
return cosmoM_reallocate(state->cstate, buf, cap, count);
|
||||
}
|
||||
|
||||
static CToken makeToken(CLexState *state, CTokenType type) {
|
||||
static CToken makeToken(CLexState *state, CTokenType type)
|
||||
{
|
||||
CToken token;
|
||||
token.type = type;
|
||||
token.line = state->line;
|
||||
@ -101,7 +111,8 @@ static CToken makeToken(CLexState *state, CTokenType type) {
|
||||
return token;
|
||||
}
|
||||
|
||||
static CToken makeError(CLexState *state, const char *msg) {
|
||||
static CToken makeError(CLexState *state, const char *msg)
|
||||
{
|
||||
CToken token;
|
||||
token.type = TOKEN_ERROR;
|
||||
token.start = (char *)msg;
|
||||
@ -114,19 +125,23 @@ static CToken makeError(CLexState *state, const char *msg) {
|
||||
return token;
|
||||
}
|
||||
|
||||
static inline bool isEnd(CLexState *state) {
|
||||
static inline bool isEnd(CLexState *state)
|
||||
{
|
||||
return *state->currentChar == '\0';
|
||||
}
|
||||
|
||||
static inline bool isNumerical(char c) {
|
||||
static inline bool isNumerical(char c)
|
||||
{
|
||||
return c >= '0' && c <= '9';
|
||||
}
|
||||
|
||||
static bool isAlpha(char c) {
|
||||
static bool isAlpha(char c)
|
||||
{
|
||||
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'; // identifiers can have '_'
|
||||
}
|
||||
|
||||
static bool match(CLexState *state, char expected) {
|
||||
static bool match(CLexState *state, char expected)
|
||||
{
|
||||
if (isEnd(state) || *state->currentChar != expected)
|
||||
return false;
|
||||
|
||||
@ -135,35 +150,41 @@ static bool match(CLexState *state, char expected) {
|
||||
return true;
|
||||
}
|
||||
|
||||
char peek(CLexState *state) {
|
||||
char peek(CLexState *state)
|
||||
{
|
||||
return *state->currentChar;
|
||||
}
|
||||
|
||||
static char peekNext(CLexState *state) {
|
||||
static char peekNext(CLexState *state)
|
||||
{
|
||||
if (isEnd(state))
|
||||
return '\0';
|
||||
|
||||
return state->currentChar[1];
|
||||
}
|
||||
|
||||
char next(CLexState *state) {
|
||||
char next(CLexState *state)
|
||||
{
|
||||
if (isEnd(state))
|
||||
return '\0'; // return a null terminator
|
||||
state->currentChar++;
|
||||
return state->currentChar[-1];
|
||||
}
|
||||
|
||||
bool isHex(char c) {
|
||||
bool isHex(char c)
|
||||
{
|
||||
return isNumerical(c) || ('A' <= c && 'F' >= c) || ('a' <= c && 'f' >= c);
|
||||
}
|
||||
|
||||
CTokenType identifierType(CLexState *state) {
|
||||
CTokenType identifierType(CLexState *state)
|
||||
{
|
||||
int length = state->currentChar - state->startChar;
|
||||
|
||||
// check against reserved word list
|
||||
for (size_t i = 0; i < sizeof(reservedWords) / sizeof(CReservedWord); i++) {
|
||||
// it matches the reserved word
|
||||
if (reservedWords[i].len == length && memcmp(state->startChar, reservedWords[i].word, length) == 0)
|
||||
if (reservedWords[i].len == length &&
|
||||
memcmp(state->startChar, reservedWords[i].word, length) == 0)
|
||||
return reservedWords[i].type;
|
||||
}
|
||||
|
||||
@ -171,7 +192,8 @@ CTokenType identifierType(CLexState *state) {
|
||||
return TOKEN_IDENTIFIER;
|
||||
}
|
||||
|
||||
void skipWhitespace(CLexState *state) {
|
||||
void skipWhitespace(CLexState *state)
|
||||
{
|
||||
while (true) {
|
||||
char c = peek(state);
|
||||
switch (c) {
|
||||
@ -184,14 +206,19 @@ void skipWhitespace(CLexState *state) {
|
||||
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
|
||||
// skip to next line (also let \n be consumed on the next iteration to properly
|
||||
// handle that)
|
||||
while (!isEnd(state) &&
|
||||
peek(state) != '\n') // if it's not a newline or the end of the source
|
||||
next(state);
|
||||
|
||||
// keep consuming whitespace
|
||||
break;
|
||||
} else if (peekNext(state) == '*') { // multiline comments
|
||||
while (!isEnd(state) && !(peek(state) == '*' && peekNext(state) == '/')) // if it's the end of the comment or the end of the source
|
||||
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 '*/'
|
||||
@ -208,7 +235,8 @@ void skipWhitespace(CLexState *state) {
|
||||
}
|
||||
}
|
||||
|
||||
CToken parseString(CLexState *state) {
|
||||
CToken parseString(CLexState *state)
|
||||
{
|
||||
makeBuffer(state); // buffer mode
|
||||
while (peek(state) != '"' && !isEnd(state)) {
|
||||
switch (peek(state)) {
|
||||
@ -218,10 +246,19 @@ CToken parseString(CLexState *state) {
|
||||
next(state); // consume the '\' character
|
||||
|
||||
switch (peek(state)) {
|
||||
case 'r': case 'n': appendBuffer(state, '\n'); break;
|
||||
case 't': appendBuffer(state, '\t'); break;
|
||||
case '\\': appendBuffer(state, '\\'); break;
|
||||
case '"': appendBuffer(state, '"'); break;
|
||||
case 'r':
|
||||
case 'n':
|
||||
appendBuffer(state, '\n');
|
||||
break;
|
||||
case 't':
|
||||
appendBuffer(state, '\t');
|
||||
break;
|
||||
case '\\':
|
||||
appendBuffer(state, '\\');
|
||||
break;
|
||||
case '"':
|
||||
appendBuffer(state, '"');
|
||||
break;
|
||||
case 'x': // hexadecimal character encoding
|
||||
next(state); // skip 'x'
|
||||
|
||||
@ -282,7 +319,8 @@ CToken parseString(CLexState *state) {
|
||||
break;
|
||||
}
|
||||
|
||||
return makeError(state, "Unknown special character!"); // TODO: maybe a more descriptive error?
|
||||
return makeError(
|
||||
state, "Unknown special character!"); // TODO: maybe a more descriptive error?
|
||||
}
|
||||
}
|
||||
|
||||
@ -303,7 +341,8 @@ CToken parseString(CLexState *state) {
|
||||
return makeToken(state, TOKEN_STRING);
|
||||
}
|
||||
|
||||
CToken parseNumber(CLexState *state) {
|
||||
CToken parseNumber(CLexState *state)
|
||||
{
|
||||
switch (peek(state)) {
|
||||
case 'x': // hexadecimal number
|
||||
next(state);
|
||||
@ -325,7 +364,6 @@ CToken parseNumber(CLexState *state) {
|
||||
// if it is a number, fall through and parse normally
|
||||
}
|
||||
|
||||
|
||||
// consume number
|
||||
while (isNumerical(peek(state))) {
|
||||
next(state);
|
||||
@ -342,7 +380,8 @@ CToken parseNumber(CLexState *state) {
|
||||
return makeToken(state, TOKEN_NUMBER);
|
||||
}
|
||||
|
||||
CToken parseIdentifier(CLexState *state) {
|
||||
CToken parseIdentifier(CLexState *state)
|
||||
{
|
||||
// read literal
|
||||
while ((isAlpha(peek(state)) || isNumerical(peek(state))) && !isEnd(state))
|
||||
next(state);
|
||||
@ -350,7 +389,8 @@ CToken parseIdentifier(CLexState *state) {
|
||||
return makeToken(state, identifierType(state)); // is it a reserved word?
|
||||
}
|
||||
|
||||
CLexState *cosmoL_newLexState(CState *cstate, const char *source) {
|
||||
CLexState *cosmoL_newLexState(CState *cstate, const char *source)
|
||||
{
|
||||
CLexState *state = cosmoM_xmalloc(cstate, sizeof(CLexState));
|
||||
state->startChar = (char *)source;
|
||||
state->currentChar = (char *)source;
|
||||
@ -364,11 +404,13 @@ CLexState *cosmoL_newLexState(CState *cstate, const char *source) {
|
||||
return state;
|
||||
}
|
||||
|
||||
void cosmoL_freeLexState(CState *state, CLexState *lstate) {
|
||||
void cosmoL_freeLexState(CState *state, CLexState *lstate)
|
||||
{
|
||||
cosmoM_free(state, CLexState, lstate);
|
||||
}
|
||||
|
||||
CToken cosmoL_scanToken(CLexState *state) {
|
||||
CToken cosmoL_scanToken(CLexState *state)
|
||||
{
|
||||
skipWhitespace(state);
|
||||
|
||||
state->startChar = state->currentChar;
|
||||
@ -380,37 +422,59 @@ CToken cosmoL_scanToken(CLexState *state) {
|
||||
|
||||
switch (c) {
|
||||
// single character tokens
|
||||
case '(': return makeToken(state, TOKEN_LEFT_PAREN);
|
||||
case ')': return makeToken(state, TOKEN_RIGHT_PAREN);
|
||||
case '{': return makeToken(state, TOKEN_LEFT_BRACE);
|
||||
case '}': return makeToken(state, TOKEN_RIGHT_BRACE);
|
||||
case '[': return makeToken(state, TOKEN_LEFT_BRACKET);
|
||||
case ']': return makeToken(state, TOKEN_RIGHT_BRACKET);
|
||||
case ';': return makeToken(state, TOKEN_EOS);
|
||||
case ',': return makeToken(state, TOKEN_COMMA);
|
||||
case ':': return makeToken(state, TOKEN_COLON);
|
||||
case '*': return makeToken(state, TOKEN_STAR);
|
||||
case '%': return makeToken(state, TOKEN_PERCENT);
|
||||
case '^': return makeToken(state, TOKEN_CARROT);
|
||||
case '#': return makeToken(state, TOKEN_POUND);
|
||||
case '/': return makeToken(state, TOKEN_SLASH);
|
||||
case '(':
|
||||
return makeToken(state, TOKEN_LEFT_PAREN);
|
||||
case ')':
|
||||
return makeToken(state, TOKEN_RIGHT_PAREN);
|
||||
case '{':
|
||||
return makeToken(state, TOKEN_LEFT_BRACE);
|
||||
case '}':
|
||||
return makeToken(state, TOKEN_RIGHT_BRACE);
|
||||
case '[':
|
||||
return makeToken(state, TOKEN_LEFT_BRACKET);
|
||||
case ']':
|
||||
return makeToken(state, TOKEN_RIGHT_BRACKET);
|
||||
case ';':
|
||||
return makeToken(state, TOKEN_EOS);
|
||||
case ',':
|
||||
return makeToken(state, TOKEN_COMMA);
|
||||
case ':':
|
||||
return makeToken(state, TOKEN_COLON);
|
||||
case '*':
|
||||
return makeToken(state, TOKEN_STAR);
|
||||
case '%':
|
||||
return makeToken(state, TOKEN_PERCENT);
|
||||
case '^':
|
||||
return makeToken(state, TOKEN_CARROT);
|
||||
case '#':
|
||||
return makeToken(state, TOKEN_POUND);
|
||||
case '/':
|
||||
return makeToken(state, TOKEN_SLASH);
|
||||
// two character tokens
|
||||
case '+':
|
||||
return match(state, '+') ? makeToken(state, TOKEN_PLUS_PLUS) : makeToken(state, TOKEN_PLUS);
|
||||
case '-':
|
||||
return match(state, '-') ? makeToken(state, TOKEN_MINUS_MINUS) : makeToken(state, TOKEN_MINUS);
|
||||
return match(state, '-') ? makeToken(state, TOKEN_MINUS_MINUS)
|
||||
: makeToken(state, TOKEN_MINUS);
|
||||
case '.':
|
||||
return match(state, '.') ? (match(state, '.') ? makeToken(state, TOKEN_DOT_DOT_DOT) : makeToken(state, TOKEN_DOT_DOT)) : makeToken(state, TOKEN_DOT);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
return match(state, '=') ? makeToken(state, TOKEN_LESS_EQUAL)
|
||||
: makeToken(state, TOKEN_LESS);
|
||||
// literals
|
||||
case '"': return parseString(state);
|
||||
case '"':
|
||||
return parseString(state);
|
||||
default:
|
||||
if (isNumerical(c))
|
||||
return parseNumber(state);
|
||||
|
15
src/clex.h
15
src/clex.h
@ -3,7 +3,8 @@
|
||||
|
||||
#include "cosmo.h"
|
||||
|
||||
typedef enum {
|
||||
typedef enum
|
||||
{
|
||||
// single character tokens
|
||||
TOKEN_LEFT_PAREN,
|
||||
TOKEN_RIGHT_PAREN,
|
||||
@ -72,23 +73,27 @@ typedef enum {
|
||||
TOKEN_EOF
|
||||
} CTokenType;
|
||||
|
||||
typedef struct {
|
||||
typedef struct
|
||||
{
|
||||
CTokenType type;
|
||||
const char *word;
|
||||
int len;
|
||||
} CReservedWord;
|
||||
|
||||
typedef struct {
|
||||
typedef struct
|
||||
{
|
||||
CTokenType type;
|
||||
char *start;
|
||||
int length;
|
||||
int line;
|
||||
} CToken;
|
||||
|
||||
typedef struct {
|
||||
typedef struct
|
||||
{
|
||||
char *currentChar;
|
||||
char *startChar;
|
||||
char *buffer; // if non-NULL & bufCount > 0, token->start & token->length will be set to buffer & bufCount respectively
|
||||
char *buffer; // if non-NULL & bufCount > 0, token->start & token->length will be set to buffer
|
||||
// & bufCount respectively
|
||||
size_t bufCount;
|
||||
size_t bufCap;
|
||||
int line; // current line
|
||||
|
76
src/cmem.c
76
src/cmem.c
@ -1,13 +1,15 @@
|
||||
#include "cmem.h"
|
||||
#include "cstate.h"
|
||||
#include "cvalue.h"
|
||||
#include "ctable.h"
|
||||
#include "cparse.h"
|
||||
#include "cobj.h"
|
||||
|
||||
#include "cbaselib.h"
|
||||
#include "cobj.h"
|
||||
#include "cparse.h"
|
||||
#include "cstate.h"
|
||||
#include "ctable.h"
|
||||
#include "cvalue.h"
|
||||
|
||||
// realloc wrapper
|
||||
void *cosmoM_reallocate(CState* state, void *buf, size_t oldSize, size_t newSize) {
|
||||
void *cosmoM_reallocate(CState *state, void *buf, size_t oldSize, size_t newSize)
|
||||
{
|
||||
state->allocatedBytes += newSize - oldSize;
|
||||
|
||||
if (newSize == 0) { // it needs to be freed
|
||||
@ -39,7 +41,8 @@ void *cosmoM_reallocate(CState* state, void *buf, size_t oldSize, size_t newSize
|
||||
return newBuf;
|
||||
}
|
||||
|
||||
COSMO_API bool cosmoM_checkGarbage(CState *state, size_t needed) {
|
||||
COSMO_API bool cosmoM_checkGarbage(CState *state, size_t needed)
|
||||
{
|
||||
if (!(cosmoM_isFrozen(state)) && state->allocatedBytes + needed > state->nextGC) {
|
||||
cosmoM_collectGarbage(state); // cya lol
|
||||
return true;
|
||||
@ -51,7 +54,8 @@ COSMO_API bool cosmoM_checkGarbage(CState *state, size_t needed) {
|
||||
void markObject(CState *state, CObj *obj);
|
||||
void markValue(CState *state, CValue val);
|
||||
|
||||
void markTable(CState *state, CTable *tbl) {
|
||||
void markTable(CState *state, CTable *tbl)
|
||||
{
|
||||
if (tbl->table == NULL) // table is still being initialized
|
||||
return;
|
||||
|
||||
@ -64,14 +68,17 @@ void markTable(CState *state, CTable *tbl) {
|
||||
}
|
||||
|
||||
// frees white members from the table
|
||||
void tableRemoveWhite(CState *state, CTable *tbl) {
|
||||
void tableRemoveWhite(CState *state, CTable *tbl)
|
||||
{
|
||||
if (tbl->table == NULL) // table is still being initialized
|
||||
return;
|
||||
|
||||
int cap = tbl->capacityMask + 1;
|
||||
for (int i = 0; i < cap; i++) {
|
||||
CTableEntry *entry = &tbl->table[i];
|
||||
if (IS_REF(entry->key) && !(cosmoV_readRef(entry->key))->isMarked) { // if the key is a object and it's white (unmarked), remove it from the table
|
||||
if (IS_REF(entry->key) &&
|
||||
!(cosmoV_readRef(entry->key))->isMarked) { // if the key is a object and it's white
|
||||
// (unmarked), remove it from the table
|
||||
cosmoT_remove(state, tbl, entry->key);
|
||||
}
|
||||
}
|
||||
@ -79,7 +86,8 @@ void tableRemoveWhite(CState *state, CTable *tbl) {
|
||||
cosmoT_checkShrink(state, tbl); // recovers the memory we're no longer using
|
||||
}
|
||||
|
||||
void markArray(CState *state, CValueArray *array) {
|
||||
void markArray(CState *state, CValueArray *array)
|
||||
{
|
||||
for (size_t i = 0; i < array->count; i++) {
|
||||
markValue(state, array->values[i]);
|
||||
}
|
||||
@ -87,7 +95,8 @@ void markArray(CState *state, CValueArray *array) {
|
||||
|
||||
// mark all references associated with the object
|
||||
// black = keep, white = discard
|
||||
void blackenObject(CState *state, CObj *obj) {
|
||||
void blackenObject(CState *state, CObj *obj)
|
||||
{
|
||||
markObject(state, (CObj *)obj->proto);
|
||||
switch (obj->type) {
|
||||
case COBJ_STRING:
|
||||
@ -152,7 +161,8 @@ void blackenObject(CState *state, CObj *obj) {
|
||||
}
|
||||
}
|
||||
|
||||
void markObject(CState *state, CObj *obj) {
|
||||
void markObject(CState *state, CObj *obj)
|
||||
{
|
||||
if (obj == NULL || obj->isMarked) // skip if NULL or already marked
|
||||
return;
|
||||
|
||||
@ -169,25 +179,29 @@ void markObject(CState *state, CObj *obj) {
|
||||
return;
|
||||
|
||||
// we can use cosmoM_growarray because we lock the GC when we entered in cosmoM_collectGarbage
|
||||
cosmoM_growarray(state, CObj*, state->grayStack.array, state->grayStack.count, state->grayStack.capacity);
|
||||
cosmoM_growarray(state, CObj *, state->grayStack.array, state->grayStack.count,
|
||||
state->grayStack.capacity);
|
||||
|
||||
state->grayStack.array[state->grayStack.count++] = obj;
|
||||
}
|
||||
|
||||
void markValue(CState *state, CValue val) {
|
||||
void markValue(CState *state, CValue val)
|
||||
{
|
||||
if (IS_REF(val))
|
||||
markObject(state, cosmoV_readRef(val));
|
||||
}
|
||||
|
||||
// trace our gray references
|
||||
void traceGrays(CState *state) {
|
||||
void traceGrays(CState *state)
|
||||
{
|
||||
while (state->grayStack.count > 0) {
|
||||
CObj *obj = state->grayStack.array[--state->grayStack.count];
|
||||
blackenObject(state, obj);
|
||||
}
|
||||
}
|
||||
|
||||
void sweep(CState *state) {
|
||||
void sweep(CState *state)
|
||||
{
|
||||
CObj *prev = NULL;
|
||||
CObj *object = state->objects;
|
||||
while (object != NULL) {
|
||||
@ -210,7 +224,8 @@ void sweep(CState *state) {
|
||||
}
|
||||
}
|
||||
|
||||
void markUserRoots(CState *state) {
|
||||
void markUserRoots(CState *state)
|
||||
{
|
||||
CObj *root = state->userRoots;
|
||||
|
||||
// traverse userRoots and mark all the object
|
||||
@ -220,7 +235,8 @@ void markUserRoots(CState *state) {
|
||||
}
|
||||
}
|
||||
|
||||
void markRoots(CState *state) {
|
||||
void markRoots(CState *state)
|
||||
{
|
||||
// mark all values on the stack
|
||||
for (StkPtr value = state->stack; value < state->top; value++) {
|
||||
markValue(state, *value);
|
||||
@ -254,7 +270,8 @@ void markRoots(CState *state) {
|
||||
traceGrays(state);
|
||||
}
|
||||
|
||||
COSMO_API void cosmoM_collectGarbage(CState *state) {
|
||||
COSMO_API void cosmoM_collectGarbage(CState *state)
|
||||
{
|
||||
#ifdef GC_DEBUG
|
||||
printf("-- GC start\n");
|
||||
size_t start = state->allocatedBytes;
|
||||
@ -263,26 +280,32 @@ COSMO_API void cosmoM_collectGarbage(CState *state) {
|
||||
|
||||
markRoots(state);
|
||||
|
||||
tableRemoveWhite(state, &state->strings); // make sure we aren't referencing any strings that are about to be freed
|
||||
tableRemoveWhite(
|
||||
state,
|
||||
&state->strings); // make sure we aren't referencing any strings that are about to be freed
|
||||
// now finally, free all the unmarked objects
|
||||
sweep(state);
|
||||
|
||||
// set our next GC event
|
||||
cosmoM_updateThreshhold(state);
|
||||
|
||||
state->freezeGC--; // we don't want to use cosmoM_unfreezeGC because that might trigger a GC event (if GC_STRESS is defined)
|
||||
state->freezeGC--; // we don't want to use cosmoM_unfreezeGC because that might trigger a GC
|
||||
// event (if GC_STRESS is defined)
|
||||
#ifdef GC_DEBUG
|
||||
printf("-- GC end, reclaimed %ld bytes (started at %ld, ended at %ld), next garbage collection scheduled at %ld bytes\n",
|
||||
printf("-- GC end, reclaimed %ld bytes (started at %ld, ended at %ld), next garbage collection "
|
||||
"scheduled at %ld bytes\n",
|
||||
start - state->allocatedBytes, start, state->allocatedBytes, state->nextGC);
|
||||
getchar(); // pauses execution
|
||||
#endif
|
||||
}
|
||||
|
||||
COSMO_API void cosmoM_updateThreshhold(CState *state) {
|
||||
COSMO_API void cosmoM_updateThreshhold(CState *state)
|
||||
{
|
||||
state->nextGC = state->allocatedBytes * HEAP_GROW_FACTOR;
|
||||
}
|
||||
|
||||
COSMO_API void cosmoM_addRoot(CState *state, CObj *newRoot) {
|
||||
COSMO_API void cosmoM_addRoot(CState *state, CObj *newRoot)
|
||||
{
|
||||
// first, check and make sure this root doesn't already exist in the list
|
||||
CObj *root = state->userRoots;
|
||||
while (root != NULL) {
|
||||
@ -297,7 +320,8 @@ COSMO_API void cosmoM_addRoot(CState *state, CObj *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 *root = state->userRoots;
|
||||
|
||||
|
19
src/cmem.h
19
src/cmem.h
@ -2,7 +2,6 @@
|
||||
#define CMEME_C // meme lol
|
||||
|
||||
#include "cosmo.h"
|
||||
|
||||
#include "cstate.h"
|
||||
|
||||
// #define GC_STRESS
|
||||
@ -14,7 +13,8 @@
|
||||
|
||||
#ifdef GC_DEBUG
|
||||
# define cosmoM_freearray(state, type, buf, capacity) \
|
||||
printf("freeing array %p [size %lu] at %s:%d\n", buf, sizeof(type) * capacity, __FILE__, __LINE__); \
|
||||
printf("freeing array %p [size %lu] at %s:%d\n", buf, sizeof(type) * capacity, __FILE__, \
|
||||
__LINE__); \
|
||||
cosmoM_reallocate(state, buf, sizeof(type) * capacity, 0)
|
||||
#else
|
||||
# define cosmoM_freearray(state, type, buf, capacity) \
|
||||
@ -33,12 +33,10 @@
|
||||
printf("freeing %p [size %lu] at %s:%d\n", x, sizeof(type), __FILE__, __LINE__); \
|
||||
cosmoM_reallocate(state, x, sizeof(type), 0)
|
||||
#else
|
||||
#define cosmoM_free(state, type, x) \
|
||||
cosmoM_reallocate(state, x, sizeof(type), 0)
|
||||
# define cosmoM_free(state, type, x) cosmoM_reallocate(state, x, sizeof(type), 0)
|
||||
#endif
|
||||
|
||||
#define cosmoM_isFrozen(state) \
|
||||
(state->freezeGC > 0)
|
||||
#define cosmoM_isFrozen(state) (state->freezeGC > 0)
|
||||
|
||||
// if debugging, print the locations of when the state is frozen/unfrozen
|
||||
#ifdef GC_DEBUG
|
||||
@ -53,8 +51,7 @@
|
||||
#else
|
||||
|
||||
// freeze's the garbage collector until cosmoM_unfreezeGC is called
|
||||
#define cosmoM_freezeGC(state) \
|
||||
state->freezeGC++
|
||||
# define cosmoM_freezeGC(state) state->freezeGC++
|
||||
|
||||
// unfreeze's the garbage collector and tries to run a garbage collection cycle
|
||||
# define cosmoM_unfreezeGC(state) \
|
||||
@ -64,7 +61,8 @@
|
||||
#endif
|
||||
|
||||
COSMO_API void *cosmoM_reallocate(CState *state, void *buf, size_t oldSize, size_t newSize);
|
||||
COSMO_API bool cosmoM_checkGarbage(CState *state, size_t needed); // returns true if GC event was triggered
|
||||
COSMO_API bool cosmoM_checkGarbage(CState *state,
|
||||
size_t needed); // returns true if GC event was triggered
|
||||
COSMO_API void cosmoM_collectGarbage(CState *state);
|
||||
COSMO_API void cosmoM_updateThreshhold(CState *state);
|
||||
|
||||
@ -75,7 +73,8 @@ COSMO_API void cosmoM_addRoot(CState *state, CObj *newRoot);
|
||||
COSMO_API void cosmoM_removeRoot(CState *state, CObj *oldRoot);
|
||||
|
||||
// wrapper for cosmoM_reallocate so we can track our memory usage (it's also safer :P)
|
||||
static inline void *cosmoM_xmalloc(CState *state, size_t sz) {
|
||||
static inline void *cosmoM_xmalloc(CState *state, size_t sz)
|
||||
{
|
||||
return cosmoM_reallocate(state, NULL, 0, sz);
|
||||
}
|
||||
|
||||
|
225
src/cobj.c
225
src/cobj.c
@ -1,16 +1,18 @@
|
||||
#include "cobj.h"
|
||||
|
||||
#include "clex.h"
|
||||
#include "cmem.h"
|
||||
#include "cstate.h"
|
||||
#include "ctable.h"
|
||||
#include "cobj.h"
|
||||
#include "cmem.h"
|
||||
#include "cvm.h"
|
||||
#include "clex.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
// we don't actually hash the whole string :eyes:
|
||||
uint32_t hashString(const char *str, size_t sz) {
|
||||
uint32_t hashString(const char *str, size_t sz)
|
||||
{
|
||||
uint32_t hash = sz;
|
||||
size_t step = (sz >> 5) + 1;
|
||||
|
||||
@ -20,7 +22,8 @@ uint32_t hashString(const char *str, size_t sz) {
|
||||
return hash;
|
||||
}
|
||||
|
||||
CObj *cosmoO_allocateBase(CState *state, size_t sz, CObjType type) {
|
||||
CObj *cosmoO_allocateBase(CState *state, size_t sz, CObjType type)
|
||||
{
|
||||
CObj *obj = (CObj *)cosmoM_xmalloc(state, sz);
|
||||
obj->type = type;
|
||||
obj->isMarked = false;
|
||||
@ -36,7 +39,8 @@ CObj *cosmoO_allocateBase(CState *state, size_t sz, CObjType type) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
void cosmoO_free(CState *state, CObj *obj) {
|
||||
void cosmoO_free(CState *state, CObj *obj)
|
||||
{
|
||||
#ifdef GC_DEBUG
|
||||
printf("freeing %p [", obj);
|
||||
printObject(obj);
|
||||
@ -98,15 +102,18 @@ void cosmoO_free(CState *state, CObj *obj) {
|
||||
break;
|
||||
}
|
||||
case COBJ_MAX:
|
||||
default: { /* stubbed, should never happen */ }
|
||||
default: { /* stubbed, should never happen */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool cosmoO_equal(CState *state, CObj *obj1, CObj *obj2) {
|
||||
bool cosmoO_equal(CState *state, CObj *obj1, CObj *obj2)
|
||||
{
|
||||
CObjObject *proto1, *proto2;
|
||||
CValue eq1, eq2;
|
||||
|
||||
if (obj1 == obj2) // its the same reference, this compares strings for us since they're interned anyways :)
|
||||
if (obj1 == obj2) // its the same reference, this compares strings for us since they're interned
|
||||
// anyways :)
|
||||
return true;
|
||||
|
||||
// its not the same type, maybe both <ref>'s have the same '__equal' metamethod in their protos?
|
||||
@ -116,8 +123,9 @@ bool cosmoO_equal(CState *state, CObj *obj1, CObj *obj2) {
|
||||
switch (obj1->type) {
|
||||
case COBJ_STRING: {
|
||||
/*
|
||||
we already compared the pointers at the top of the function, this prevents the `__equal` metamethod
|
||||
from being checked. If you plan on using `__equal` with strings just remove this case!
|
||||
we already compared the pointers at the top of the function, this prevents the `__equal`
|
||||
metamethod from being checked. If you plan on using `__equal` with strings just remove
|
||||
this case!
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
@ -148,11 +156,17 @@ bool cosmoO_equal(CState *state, CObj *obj1, CObj *obj2) {
|
||||
}
|
||||
|
||||
_eqFail:
|
||||
// this is pretty expensive (bad lookup caching helps a lot), but it only all gets run if both objects have protos & both have the `__equal` metamethod defined so...
|
||||
// it should stay light for the majority of cases
|
||||
if ((proto1 = cosmoO_grabProto(obj1)) != NULL && (proto2 = cosmoO_grabProto(obj2)) != NULL && // make sure both protos exist
|
||||
cosmoO_getIString(state, proto1, ISTRING_EQUAL, &eq1) && // grab the `__equal` metamethod from the first proto, if fail abort
|
||||
cosmoO_getIString(state, proto2, ISTRING_EQUAL, &eq2) && // grab the `__equal` metamethod from the second proto, if fail abort
|
||||
// this is pretty expensive (bad lookup caching helps a lot), but it only all gets run if both
|
||||
// objects have protos & both have the `__equal` metamethod defined so... it should stay light
|
||||
// for the majority of cases
|
||||
if ((proto1 = cosmoO_grabProto(obj1)) != NULL &&
|
||||
(proto2 = cosmoO_grabProto(obj2)) != NULL && // make sure both protos exist
|
||||
cosmoO_getIString(
|
||||
state, proto1, ISTRING_EQUAL,
|
||||
&eq1) && // grab the `__equal` metamethod from the first proto, if fail abort
|
||||
cosmoO_getIString(
|
||||
state, proto2, ISTRING_EQUAL,
|
||||
&eq2) && // grab the `__equal` metamethod from the second proto, if fail abort
|
||||
cosmoV_equal(state, eq1, eq2)) { // compare the two `__equal` metamethods
|
||||
|
||||
// now finally, call the `__equal` metamethod (<object>, <object>)
|
||||
@ -164,7 +178,8 @@ _eqFail:
|
||||
|
||||
// check return value and make sure it's a boolean
|
||||
if (!IS_BOOLEAN(*cosmoV_getTop(state, 0))) {
|
||||
cosmoV_error(state, "__equal expected to return <boolean>, got %s!", cosmoV_typeStr(*cosmoV_pop(state)));
|
||||
cosmoV_error(state, "__equal expected to return <boolean>, got %s!",
|
||||
cosmoV_typeStr(*cosmoV_pop(state)));
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -175,7 +190,8 @@ _eqFail:
|
||||
return false;
|
||||
}
|
||||
|
||||
CObjObject *cosmoO_newObject(CState *state) {
|
||||
CObjObject *cosmoO_newObject(CState *state)
|
||||
{
|
||||
CObjObject *obj = (CObjObject *)cosmoO_allocateBase(state, sizeof(CObjObject), COBJ_OBJECT);
|
||||
obj->istringFlags = 0;
|
||||
obj->userP = NULL; // reserved for C API
|
||||
@ -188,14 +204,16 @@ CObjObject *cosmoO_newObject(CState *state) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
CObjStream *cosmoO_newStream(CState *state, int fd) {
|
||||
CObjStream *cosmoO_newStream(CState *state, int fd)
|
||||
{
|
||||
CObjStream *strm = (CObjStream *)cosmoO_allocateBase(state, sizeof(CObjStream), COBJ_STREAM);
|
||||
strm->fd = fd;
|
||||
|
||||
return strm;
|
||||
}
|
||||
|
||||
CObjTable *cosmoO_newTable(CState *state) {
|
||||
CObjTable *cosmoO_newTable(CState *state)
|
||||
{
|
||||
CObjTable *obj = (CObjTable *)cosmoO_allocateBase(state, sizeof(CObjTable), COBJ_TABLE);
|
||||
|
||||
// init the table (might cause a GC event)
|
||||
@ -206,8 +224,10 @@ CObjTable *cosmoO_newTable(CState *state) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
CObjFunction *cosmoO_newFunction(CState *state) {
|
||||
CObjFunction *func = (CObjFunction*)cosmoO_allocateBase(state, sizeof(CObjFunction), COBJ_FUNCTION);
|
||||
CObjFunction *cosmoO_newFunction(CState *state)
|
||||
{
|
||||
CObjFunction *func =
|
||||
(CObjFunction *)cosmoO_allocateBase(state, sizeof(CObjFunction), COBJ_FUNCTION);
|
||||
func->args = 0;
|
||||
func->upvals = 0;
|
||||
func->variadic = false;
|
||||
@ -218,13 +238,16 @@ CObjFunction *cosmoO_newFunction(CState *state) {
|
||||
return func;
|
||||
}
|
||||
|
||||
CObjCFunction *cosmoO_newCFunction(CState *state, CosmoCFunction func) {
|
||||
CObjCFunction *cfunc = (CObjCFunction*)cosmoO_allocateBase(state, sizeof(CObjCFunction), COBJ_CFUNCTION);
|
||||
CObjCFunction *cosmoO_newCFunction(CState *state, CosmoCFunction func)
|
||||
{
|
||||
CObjCFunction *cfunc =
|
||||
(CObjCFunction *)cosmoO_allocateBase(state, sizeof(CObjCFunction), COBJ_CFUNCTION);
|
||||
cfunc->cfunc = func;
|
||||
return cfunc;
|
||||
}
|
||||
|
||||
CObjError *cosmoO_newError(CState *state, CValue err) {
|
||||
CObjError *cosmoO_newError(CState *state, CValue err)
|
||||
{
|
||||
CObjError *cerror = (CObjError *)cosmoO_allocateBase(state, sizeof(CObjError), COBJ_ERROR);
|
||||
cerror->err = err;
|
||||
cerror->frameCount = state->frameCount;
|
||||
@ -240,14 +263,16 @@ CObjError *cosmoO_newError(CState *state, CValue err) {
|
||||
return cerror;
|
||||
}
|
||||
|
||||
CObjMethod *cosmoO_newMethod(CState *state, CValue func, CObj *obj) {
|
||||
CObjMethod *cosmoO_newMethod(CState *state, CValue func, CObj *obj)
|
||||
{
|
||||
CObjMethod *method = (CObjMethod *)cosmoO_allocateBase(state, sizeof(CObjMethod), COBJ_METHOD);
|
||||
method->func = func;
|
||||
method->obj = obj;
|
||||
return method;
|
||||
}
|
||||
|
||||
CObjClosure *cosmoO_newClosure(CState *state, CObjFunction *func) {
|
||||
CObjClosure *cosmoO_newClosure(CState *state, CObjFunction *func)
|
||||
{
|
||||
// initialize array of pointers
|
||||
CObjUpval **upvalues = cosmoM_xmalloc(state, sizeof(CObjUpval *) * func->upvals);
|
||||
|
||||
@ -255,7 +280,8 @@ CObjClosure *cosmoO_newClosure(CState *state, CObjFunction *func) {
|
||||
upvalues[i] = NULL;
|
||||
}
|
||||
|
||||
CObjClosure *closure = (CObjClosure*)cosmoO_allocateBase(state, sizeof(CObjClosure), COBJ_CLOSURE);
|
||||
CObjClosure *closure =
|
||||
(CObjClosure *)cosmoO_allocateBase(state, sizeof(CObjClosure), COBJ_CLOSURE);
|
||||
closure->function = func;
|
||||
closure->upvalues = upvalues;
|
||||
closure->upvalueCount = func->upvals;
|
||||
@ -263,7 +289,8 @@ CObjClosure *cosmoO_newClosure(CState *state, CObjFunction *func) {
|
||||
return closure;
|
||||
}
|
||||
|
||||
CObjUpval *cosmoO_newUpvalue(CState *state, CValue *val) {
|
||||
CObjUpval *cosmoO_newUpvalue(CState *state, CValue *val)
|
||||
{
|
||||
CObjUpval *upval = (CObjUpval *)cosmoO_allocateBase(state, sizeof(CObjUpval), COBJ_UPVALUE);
|
||||
upval->val = val;
|
||||
upval->closed = cosmoV_newNil();
|
||||
@ -272,7 +299,8 @@ CObjUpval *cosmoO_newUpvalue(CState *state, CValue *val) {
|
||||
return upval;
|
||||
}
|
||||
|
||||
CObjString *cosmoO_copyString(CState *state, const char *str, size_t length) {
|
||||
CObjString *cosmoO_copyString(CState *state, const char *str, size_t length)
|
||||
{
|
||||
uint32_t hash = hashString(str, length);
|
||||
CObjString *lookup = cosmoT_lookupString(&state->strings, str, length, hash);
|
||||
|
||||
@ -287,29 +315,34 @@ CObjString *cosmoO_copyString(CState *state, const char *str, size_t length) {
|
||||
return cosmoO_allocateString(state, buf, length, hash);
|
||||
}
|
||||
|
||||
// length shouldn't include the null terminator! str should be a null terminated string! (char array should also have been allocated using cosmoM_xmalloc!)
|
||||
CObjString *cosmoO_takeString(CState *state, 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!)
|
||||
CObjString *cosmoO_takeString(CState *state, char *str, size_t length)
|
||||
{
|
||||
uint32_t hash = hashString(str, length);
|
||||
|
||||
CObjString *lookup = cosmoT_lookupString(&state->strings, str, length, hash);
|
||||
|
||||
// have we already interned this string?
|
||||
if (lookup != NULL) {
|
||||
cosmoM_freearray(state, char, str, length + 1); // free our passed character array, it's unneeded!
|
||||
cosmoM_freearray(state, char, str,
|
||||
length + 1); // free our passed character array, it's unneeded!
|
||||
return lookup;
|
||||
}
|
||||
|
||||
return cosmoO_allocateString(state, str, length, hash);
|
||||
}
|
||||
|
||||
CObjString *cosmoO_allocateString(CState *state, const char *str, size_t sz, uint32_t hash) {
|
||||
CObjString *cosmoO_allocateString(CState *state, const char *str, size_t sz, uint32_t hash)
|
||||
{
|
||||
CObjString *strObj = (CObjString *)cosmoO_allocateBase(state, sizeof(CObjString), COBJ_STRING);
|
||||
strObj->isIString = false;
|
||||
strObj->str = (char *)str;
|
||||
strObj->length = sz;
|
||||
strObj->hash = hash;
|
||||
|
||||
// we push & pop the string so our GC can find it (we don't use freezeGC/unfreezeGC because we *want* a GC event to happen)
|
||||
// we push & pop the string so our GC can find it (we don't use freezeGC/unfreezeGC because we
|
||||
// *want* a GC event to happen)
|
||||
cosmoV_pushRef(state, (CObj *)strObj);
|
||||
cosmoT_insert(state, &state->strings, cosmoV_newRef((CObj *)strObj));
|
||||
cosmoV_pop(state);
|
||||
@ -317,7 +350,8 @@ CObjString *cosmoO_allocateString(CState *state, const char *str, size_t sz, uin
|
||||
return strObj;
|
||||
}
|
||||
|
||||
CObjString *cosmoO_pushVFString(CState *state, const char *format, va_list args) {
|
||||
CObjString *cosmoO_pushVFString(CState *state, const char *format, va_list args)
|
||||
{
|
||||
StkPtr start = state->top;
|
||||
const char *end;
|
||||
char c;
|
||||
@ -362,12 +396,14 @@ CObjString *cosmoO_pushVFString(CState *state, const char *format, va_list args)
|
||||
}
|
||||
|
||||
cosmoV_pushString(state, format); // push the rest of the string
|
||||
cosmoV_concat(state, state->top - start); // use cosmoV_concat to concat all the strings on the stack
|
||||
cosmoV_concat(state,
|
||||
state->top - start); // use cosmoV_concat to concat all the strings on the stack
|
||||
return cosmoV_readString(*start); // start should be state->top - 1
|
||||
}
|
||||
|
||||
// walks the protos of obj and checks for proto
|
||||
bool cosmoO_isDescendant(CObj *obj, CObjObject *proto) {
|
||||
bool cosmoO_isDescendant(CObj *obj, CObjObject *proto)
|
||||
{
|
||||
CObjObject *curr = obj->proto;
|
||||
|
||||
while (curr != NULL) {
|
||||
@ -382,9 +418,12 @@ bool cosmoO_isDescendant(CObj *obj, CObjObject *proto) {
|
||||
}
|
||||
|
||||
// returns false if error thrown
|
||||
bool cosmoO_getRawObject(CState *state, CObjObject *proto, CValue key, CValue *val, CObj *obj) {
|
||||
if (!cosmoT_get(state, &proto->tbl, key, val)) { // if the field doesn't exist in the object, check the proto
|
||||
if (cosmoO_getIString(state, proto, ISTRING_GETTER, val) && IS_TABLE(*val) && cosmoT_get(state, &cosmoV_readTable(*val)->tbl, key, val)) {
|
||||
bool cosmoO_getRawObject(CState *state, CObjObject *proto, CValue key, CValue *val, CObj *obj)
|
||||
{
|
||||
if (!cosmoT_get(state, &proto->tbl, key,
|
||||
val)) { // if the field doesn't exist in the object, check the proto
|
||||
if (cosmoO_getIString(state, proto, ISTRING_GETTER, val) && IS_TABLE(*val) &&
|
||||
cosmoT_get(state, &cosmoV_readTable(*val)->tbl, key, val)) {
|
||||
cosmoV_pushValue(state, *val); // push function
|
||||
cosmoV_pushRef(state, (CObj *)obj); // push object
|
||||
if (cosmoV_call(state, 1, 1) != COSMOVM_OK) // call the function with the 1 argument
|
||||
@ -393,7 +432,8 @@ bool cosmoO_getRawObject(CState *state, CObjObject *proto, CValue key, CValue *v
|
||||
return true;
|
||||
}
|
||||
|
||||
if (proto->_obj.proto != NULL && cosmoO_getRawObject(state, proto->_obj.proto, key, val, obj))
|
||||
if (proto->_obj.proto != NULL &&
|
||||
cosmoO_getRawObject(state, proto->_obj.proto, key, val, obj))
|
||||
return true;
|
||||
|
||||
*val = cosmoV_newNil();
|
||||
@ -403,7 +443,8 @@ bool cosmoO_getRawObject(CState *state, CObjObject *proto, CValue key, CValue *v
|
||||
return true;
|
||||
}
|
||||
|
||||
void cosmoO_setRawObject(CState *state, CObjObject *proto, CValue key, CValue val, CObj *obj) {
|
||||
void cosmoO_setRawObject(CState *state, CObjObject *proto, CValue key, CValue val, CObj *obj)
|
||||
{
|
||||
CValue ret;
|
||||
|
||||
// if the object is locked, throw an error
|
||||
@ -413,7 +454,8 @@ void cosmoO_setRawObject(CState *state, CObjObject *proto, CValue key, CValue va
|
||||
}
|
||||
|
||||
// check for __setters
|
||||
if (cosmoO_getIString(state, proto, ISTRING_SETTER, &ret) && IS_TABLE(ret) && cosmoT_get(state, &cosmoV_readTable(ret)->tbl, key, &ret)) {
|
||||
if (cosmoO_getIString(state, proto, ISTRING_SETTER, &ret) && IS_TABLE(ret) &&
|
||||
cosmoT_get(state, &cosmoV_readTable(ret)->tbl, key, &ret)) {
|
||||
cosmoV_pushValue(state, ret); // push function
|
||||
cosmoV_pushRef(state, (CObj *)obj); // push object
|
||||
cosmoV_pushValue(state, val); // push new value
|
||||
@ -433,39 +475,48 @@ void cosmoO_setRawObject(CState *state, CObjObject *proto, CValue key, CValue va
|
||||
}
|
||||
}
|
||||
|
||||
void cosmoO_setUserP(CObjObject *object, void *p) {
|
||||
void cosmoO_setUserP(CObjObject *object, void *p)
|
||||
{
|
||||
object->userP = p;
|
||||
}
|
||||
|
||||
void *cosmoO_getUserP(CObjObject *object) {
|
||||
void *cosmoO_getUserP(CObjObject *object)
|
||||
{
|
||||
return object->userP;
|
||||
}
|
||||
|
||||
void cosmoO_setUserI(CObjObject *object, int i) {
|
||||
void cosmoO_setUserI(CObjObject *object, int i)
|
||||
{
|
||||
object->userI = i;
|
||||
}
|
||||
|
||||
int cosmoO_getUserI(CObjObject *object) {
|
||||
int cosmoO_getUserI(CObjObject *object)
|
||||
{
|
||||
return object->userI;
|
||||
}
|
||||
|
||||
void cosmoO_setUserT(CObjObject *object, int t) {
|
||||
void cosmoO_setUserT(CObjObject *object, int t)
|
||||
{
|
||||
object->userT = t;
|
||||
}
|
||||
|
||||
int cosmoO_getUserT(CObjObject *object) {
|
||||
int cosmoO_getUserT(CObjObject *object)
|
||||
{
|
||||
return object->userT;
|
||||
}
|
||||
|
||||
void cosmoO_lock(CObjObject *object) {
|
||||
void cosmoO_lock(CObjObject *object)
|
||||
{
|
||||
object->isLocked = true;
|
||||
}
|
||||
|
||||
void cosmoO_unlock(CObjObject *object) {
|
||||
void cosmoO_unlock(CObjObject *object)
|
||||
{
|
||||
object->isLocked = false;
|
||||
}
|
||||
|
||||
bool rawgetIString(CState *state, CObjObject *object, int flag, CValue *val) {
|
||||
bool rawgetIString(CState *state, CObjObject *object, int flag, CValue *val)
|
||||
{
|
||||
if (readFlag(object->istringFlags, flag))
|
||||
return false; // it's been cached as bad
|
||||
|
||||
@ -478,7 +529,8 @@ bool rawgetIString(CState *state, CObjObject *object, int flag, CValue *val) {
|
||||
return true; // :)
|
||||
}
|
||||
|
||||
bool cosmoO_getIString(CState *state, CObjObject *object, int flag, CValue *val) {
|
||||
bool cosmoO_getIString(CState *state, CObjObject *object, int flag, CValue *val)
|
||||
{
|
||||
CObjObject *obj = object;
|
||||
|
||||
do {
|
||||
@ -489,7 +541,8 @@ bool cosmoO_getIString(CState *state, CObjObject *object, int flag, CValue *val)
|
||||
return false; // obj->proto was false, the istring doesn't exist in this object chain
|
||||
}
|
||||
|
||||
bool cosmoO_indexObject(CState *state, CObjObject *object, CValue key, CValue *val) {
|
||||
bool cosmoO_indexObject(CState *state, CObjObject *object, CValue key, CValue *val)
|
||||
{
|
||||
if (cosmoO_getIString(state, object, ISTRING_INDEX, val)) {
|
||||
cosmoV_pushValue(state, *val); // push function
|
||||
cosmoV_pushRef(state, (CObj *)object); // push object
|
||||
@ -505,7 +558,8 @@ bool cosmoO_indexObject(CState *state, CObjObject *object, CValue key, CValue *v
|
||||
return false;
|
||||
}
|
||||
|
||||
bool cosmoO_newIndexObject(CState *state, CObjObject *object, CValue key, CValue val) {
|
||||
bool cosmoO_newIndexObject(CState *state, CObjObject *object, CValue key, CValue val)
|
||||
{
|
||||
CValue ret; // return value for cosmoO_getIString
|
||||
|
||||
if (cosmoO_getIString(state, object, ISTRING_NEWINDEX, &ret)) {
|
||||
@ -521,7 +575,8 @@ bool cosmoO_newIndexObject(CState *state, CObjObject *object, CValue key, CValue
|
||||
return false;
|
||||
}
|
||||
|
||||
CObjString *cosmoO_toString(CState *state, CObj *obj) {
|
||||
CObjString *cosmoO_toString(CState *state, CObj *obj)
|
||||
{
|
||||
CObjObject *protoObject = cosmoO_grabProto(obj);
|
||||
CValue res;
|
||||
|
||||
@ -535,7 +590,8 @@ CObjString *cosmoO_toString(CState *state, CObj *obj) {
|
||||
// make sure the __tostring function returned a string
|
||||
StkPtr ret = cosmoV_getTop(state, 0);
|
||||
if (!IS_STRING(*ret)) {
|
||||
cosmoV_error(state, "__tostring expected to return <string>, got %s!", cosmoV_typeStr(*ret));
|
||||
cosmoV_error(state, "__tostring expected to return <string>, got %s!",
|
||||
cosmoV_typeStr(*ret));
|
||||
return cosmoO_copyString(state, "<err>", 5);
|
||||
}
|
||||
|
||||
@ -554,12 +610,14 @@ CObjString *cosmoO_toString(CState *state, CObj *obj) {
|
||||
}
|
||||
case COBJ_FUNCTION: {
|
||||
CObjFunction *func = (CObjFunction *)obj;
|
||||
return func->name != NULL ? func->name : cosmoO_copyString(state, UNNAMEDCHUNK, strlen(UNNAMEDCHUNK));
|
||||
return func->name != NULL ? func->name
|
||||
: cosmoO_copyString(state, UNNAMEDCHUNK, strlen(UNNAMEDCHUNK));
|
||||
}
|
||||
case COBJ_CFUNCTION: {
|
||||
CObjCFunction *cfunc = (CObjCFunction *)obj;
|
||||
char buf[64];
|
||||
int sz = sprintf(buf, "<c function> %p", (void*)cfunc->cfunc) + 1; // +1 for the null character
|
||||
int sz =
|
||||
sprintf(buf, "<c function> %p", (void *)cfunc->cfunc) + 1; // +1 for the null character
|
||||
return cosmoO_copyString(state, buf, sz);
|
||||
}
|
||||
case COBJ_OBJECT: {
|
||||
@ -584,7 +642,8 @@ CObjString *cosmoO_toString(CState *state, CObj *obj) {
|
||||
}
|
||||
}
|
||||
|
||||
cosmo_Number cosmoO_toNumber(CState *state, CObj *obj) {
|
||||
cosmo_Number cosmoO_toNumber(CState *state, CObj *obj)
|
||||
{
|
||||
CObjObject *proto = cosmoO_grabProto(obj);
|
||||
CValue res;
|
||||
|
||||
@ -596,7 +655,8 @@ cosmo_Number cosmoO_toNumber(CState *state, CObj *obj) {
|
||||
|
||||
StkPtr temp = cosmoV_getTop(state, 0);
|
||||
if (!IS_NUMBER(*temp)) {
|
||||
cosmoV_error(state, "__tonumber expected to return <number>, got %s!", cosmoV_typeStr(*temp));
|
||||
cosmoV_error(state, "__tonumber expected to return <number>, got %s!",
|
||||
cosmoV_typeStr(*temp));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -615,19 +675,22 @@ cosmo_Number cosmoO_toNumber(CState *state, CObj *obj) {
|
||||
}
|
||||
}
|
||||
|
||||
int cosmoO_count(CState *state, CObj *obj) {
|
||||
int cosmoO_count(CState *state, CObj *obj)
|
||||
{
|
||||
CObjObject *proto = cosmoO_grabProto(obj);
|
||||
CValue res;
|
||||
|
||||
if (proto != NULL && cosmoO_getIString(state, proto, ISTRING_COUNT, &res)) {
|
||||
cosmoV_pushValue(state, res);
|
||||
cosmoV_pushRef(state, (CObj *)obj);
|
||||
if (cosmoV_call(state, 1, 1) != COSMOVM_OK) // call res, we expect 1 return value of type <number>
|
||||
if (cosmoV_call(state, 1, 1) !=
|
||||
COSMOVM_OK) // call res, we expect 1 return value of type <number>
|
||||
return 0;
|
||||
|
||||
StkPtr ret = cosmoV_getTop(state, 0);
|
||||
if (!IS_NUMBER(*ret)) {
|
||||
cosmoV_error(state, "__count expected to return <number>, got %s!", cosmoV_typeStr(*ret));
|
||||
cosmoV_error(state, "__count expected to return <number>, got %s!",
|
||||
cosmoV_typeStr(*ret));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -651,7 +714,8 @@ int cosmoO_count(CState *state, CObj *obj) {
|
||||
}
|
||||
}
|
||||
|
||||
void printObject(CObj *o) {
|
||||
void printObject(CObj *o)
|
||||
{
|
||||
switch (o->type) {
|
||||
case COBJ_STRING: {
|
||||
CObjString *objStr = (CObjString *)o;
|
||||
@ -709,16 +773,25 @@ void printObject(CObj *o) {
|
||||
}
|
||||
}
|
||||
|
||||
const char *cosmoO_typeStr(CObj* obj) {
|
||||
const char *cosmoO_typeStr(CObj *obj)
|
||||
{
|
||||
switch (obj->type) {
|
||||
case COBJ_STRING: return "<string>";
|
||||
case COBJ_OBJECT: return "<object>";
|
||||
case COBJ_TABLE: return "<table>";
|
||||
case COBJ_FUNCTION: return "<function>";
|
||||
case COBJ_CFUNCTION: return "<c function>";
|
||||
case COBJ_METHOD: return "<method>";
|
||||
case COBJ_CLOSURE: return "<closure>";
|
||||
case COBJ_UPVALUE: return "<upvalue>";
|
||||
case COBJ_STRING:
|
||||
return "<string>";
|
||||
case COBJ_OBJECT:
|
||||
return "<object>";
|
||||
case COBJ_TABLE:
|
||||
return "<table>";
|
||||
case COBJ_FUNCTION:
|
||||
return "<function>";
|
||||
case COBJ_CFUNCTION:
|
||||
return "<c function>";
|
||||
case COBJ_METHOD:
|
||||
return "<method>";
|
||||
case COBJ_CLOSURE:
|
||||
return "<closure>";
|
||||
case COBJ_UPVALUE:
|
||||
return "<upvalue>";
|
||||
|
||||
default:
|
||||
return "<unkn obj>"; // TODO: maybe panic? could be a malformed object :eyes:
|
||||
|
67
src/cobj.h
67
src/cobj.h
@ -3,7 +3,8 @@
|
||||
|
||||
#include "cosmo.h"
|
||||
|
||||
typedef enum CObjType {
|
||||
typedef enum CObjType
|
||||
{
|
||||
COBJ_STRING,
|
||||
COBJ_OBJECT,
|
||||
COBJ_TABLE,
|
||||
@ -18,10 +19,10 @@ typedef enum CObjType {
|
||||
COBJ_MAX
|
||||
} CObjType;
|
||||
|
||||
#include "cstate.h"
|
||||
#include "cchunk.h"
|
||||
#include "cvalue.h"
|
||||
#include "cstate.h"
|
||||
#include "ctable.h"
|
||||
#include "cvalue.h"
|
||||
|
||||
#define CommonHeader CObj _obj
|
||||
#define readFlag(x, flag) (x & (1u << flag))
|
||||
@ -29,7 +30,8 @@ typedef enum CObjType {
|
||||
|
||||
typedef int (*CosmoCFunction)(CState *state, int argCount, CValue *args);
|
||||
|
||||
struct CObj {
|
||||
struct CObj
|
||||
{
|
||||
struct CObj *next;
|
||||
struct CObj *nextRoot; // for the root linked list
|
||||
struct CObjObject *proto; // protoobject, describes the behavior of the object
|
||||
@ -37,7 +39,8 @@ struct CObj {
|
||||
bool isMarked; // for the GC
|
||||
};
|
||||
|
||||
struct CObjString {
|
||||
struct CObjString
|
||||
{
|
||||
CommonHeader; // "is a" CObj
|
||||
char *str; // NULL termincated string
|
||||
uint32_t hash; // for hashtable lookup
|
||||
@ -45,12 +48,14 @@ struct CObjString {
|
||||
bool isIString;
|
||||
};
|
||||
|
||||
struct CObjStream {
|
||||
struct CObjStream
|
||||
{
|
||||
CommonHeader; // "is a" CObj
|
||||
int fd; // handle to file descriptor, on POSIX compliant OSes this can also be a socket :pog:
|
||||
};
|
||||
|
||||
struct CObjError {
|
||||
struct CObjError
|
||||
{
|
||||
CommonHeader; // "is a" CObj
|
||||
CValue err; // error string
|
||||
CCallFrame *frames;
|
||||
@ -59,11 +64,14 @@ struct CObjError {
|
||||
bool parserError; // if true, cosmoV_printError will format the error to the lexer
|
||||
};
|
||||
|
||||
struct CObjObject {
|
||||
struct CObjObject
|
||||
{
|
||||
CommonHeader; // "is a" CObj
|
||||
CTable tbl;
|
||||
cosmo_Flag istringFlags; // enables us to have a much faster lookup for reserved IStrings (like __init, __index, etc.)
|
||||
union { // userdata (NULL by default)
|
||||
cosmo_Flag istringFlags; // enables us to have a much faster lookup for reserved IStrings (like
|
||||
// __init, __index, etc.)
|
||||
union
|
||||
{ // userdata (NULL by default)
|
||||
void *userP;
|
||||
int userI;
|
||||
};
|
||||
@ -71,12 +79,14 @@ struct CObjObject {
|
||||
bool isLocked;
|
||||
};
|
||||
|
||||
struct CObjTable { // table, a wrapper for CTable
|
||||
struct CObjTable
|
||||
{ // table, a wrapper for CTable
|
||||
CommonHeader; // "is a" CObj
|
||||
CTable tbl;
|
||||
};
|
||||
|
||||
struct CObjFunction {
|
||||
struct CObjFunction
|
||||
{
|
||||
CommonHeader; // "is a" CObj
|
||||
CChunk chunk;
|
||||
CObjString *name;
|
||||
@ -86,25 +96,29 @@ struct CObjFunction {
|
||||
bool variadic;
|
||||
};
|
||||
|
||||
struct CObjCFunction {
|
||||
struct CObjCFunction
|
||||
{
|
||||
CommonHeader; // "is a" CObj
|
||||
CosmoCFunction cfunc;
|
||||
};
|
||||
|
||||
struct CObjClosure {
|
||||
struct CObjClosure
|
||||
{
|
||||
CommonHeader; // "is a" CObj
|
||||
CObjFunction *function;
|
||||
CObjUpval **upvalues;
|
||||
int upvalueCount;
|
||||
};
|
||||
|
||||
struct CObjMethod {
|
||||
struct CObjMethod
|
||||
{
|
||||
CommonHeader; // "is a " CObj
|
||||
CValue func;
|
||||
CObj *obj; // obj this method is bound too
|
||||
};
|
||||
|
||||
struct CObjUpval {
|
||||
struct CObjUpval
|
||||
{
|
||||
CommonHeader; // "is a" CObj
|
||||
CValue closed;
|
||||
CValue *val;
|
||||
@ -134,12 +148,14 @@ struct CObjUpval {
|
||||
|
||||
#define cosmoO_readCString(x) ((CObjString *)x)->str
|
||||
|
||||
static inline bool isObjType(CValue val, CObjType type) {
|
||||
static inline bool isObjType(CValue val, CObjType type)
|
||||
{
|
||||
return IS_REF(val) && cosmoV_readRef(val)->type == type;
|
||||
}
|
||||
|
||||
// just protects against macro expansion
|
||||
static inline bool IS_CALLABLE(CValue val) {
|
||||
static inline bool IS_CALLABLE(CValue val)
|
||||
{
|
||||
return IS_CLOSURE(val) || IS_CFUNCTION(val) || IS_METHOD(val);
|
||||
}
|
||||
|
||||
@ -160,7 +176,8 @@ CObjClosure *cosmoO_newClosure(CState *state, CObjFunction *func);
|
||||
CObjUpval *cosmoO_newUpvalue(CState *state, CValue *val);
|
||||
|
||||
// grabs the base proto of the CObj* (if CObj is a CObjObject, that is returned)
|
||||
static inline CObjObject *cosmoO_grabProto(CObj *obj) {
|
||||
static inline CObjObject *cosmoO_grabProto(CObj *obj)
|
||||
{
|
||||
return obj->type == COBJ_OBJECT ? (CObjObject *)obj : obj->proto;
|
||||
}
|
||||
|
||||
@ -169,11 +186,13 @@ void cosmoO_setRawObject(CState *state, CObjObject *proto, CValue key, CValue va
|
||||
bool cosmoO_indexObject(CState *state, CObjObject *object, CValue key, CValue *val);
|
||||
bool cosmoO_newIndexObject(CState *state, CObjObject *object, CValue key, CValue val);
|
||||
|
||||
// sets the user-defined pointer, if a user-define integer is already defined it will be over written
|
||||
// sets the user-defined pointer, if a user-define integer is already defined it will be over
|
||||
// written
|
||||
void cosmoO_setUserP(CObjObject *object, void *p);
|
||||
// gets the user-defined pointer
|
||||
void *cosmoO_getUserP(CObjObject *object);
|
||||
// sets the user-defined integer, if a user-define pointer is already defined it will be over written
|
||||
// sets the user-defined integer, if a user-define pointer is already defined it will be over
|
||||
// written
|
||||
void cosmoO_setUserI(CObjObject *object, int i);
|
||||
// gets the user-defined integer
|
||||
int cosmoO_getUserI(CObjObject *object);
|
||||
@ -189,10 +208,12 @@ void cosmoO_unlock(CObjObject *object);
|
||||
// internal string
|
||||
bool cosmoO_getIString(CState *state, CObjObject *object, int flag, CValue *val);
|
||||
|
||||
// copies the *str buffer to the heap and returns a CObjString struct which is also on the heap (length should not include the null terminator)
|
||||
// copies the *str buffer to the heap and returns a CObjString struct which is also on the heap
|
||||
// (length should not include the null terminator)
|
||||
CObjString *cosmoO_copyString(CState *state, const char *str, size_t length);
|
||||
|
||||
// length shouldn't include the null terminator! str should be a null terminated string! (char array should also have been allocated using cosmoM_xmalloc!)
|
||||
// length shouldn't include the null terminator! str should be a null terminated string! (char array
|
||||
// should also have been allocated using cosmoM_xmalloc!)
|
||||
CObjString *cosmoO_takeString(CState *state, char *str, size_t length);
|
||||
|
||||
// allocates a CObjStruct pointing directly to *str
|
||||
|
@ -5,7 +5,8 @@
|
||||
|
||||
// instructions
|
||||
|
||||
typedef enum {
|
||||
typedef enum
|
||||
{
|
||||
// STACK/STATE MANIPULATION
|
||||
OP_LOADCONST, // pushes const[uint8_t] to the stack
|
||||
OP_SETGLOBAL, // pops and sets global[const[uint16_t]]
|
||||
|
15
src/cosmo.h
15
src/cosmo.h
@ -1,18 +1,18 @@
|
||||
#ifndef COSMOMAIN_H
|
||||
#define COSMOMAIN_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/*
|
||||
SAFE_STACK:
|
||||
if undefined, the stack will not be checked for stack overflows. This may improve performance, however
|
||||
this will produce undefined behavior as you reach the stack limit (and may cause a seg fault!). It is recommended to keep this enabled.
|
||||
if undefined, the stack will not be checked for stack overflows. This may improve
|
||||
performance, however this will produce undefined behavior as you reach the stack limit (and may
|
||||
cause a seg fault!). It is recommended to keep this enabled.
|
||||
*/
|
||||
#define SAFE_STACK
|
||||
// #define NAN_BOXXED
|
||||
@ -54,7 +54,6 @@ typedef uint8_t INSTRUCTION;
|
||||
#define UNNAMEDCHUNK "_main"
|
||||
#define COSMOASSERT(x) assert(x)
|
||||
|
||||
#define CERROR(err) \
|
||||
printf("%s : %s\n", "[ERROR]", err)
|
||||
#define CERROR(err) printf("%s : %s\n", "[ERROR]", err)
|
||||
|
||||
#endif
|
||||
|
480
src/cparse.c
480
src/cparse.c
File diff suppressed because it is too large
Load Diff
@ -1,10 +1,11 @@
|
||||
#ifndef CPARSE_H
|
||||
#define CPARSE_H
|
||||
|
||||
#include "cosmo.h"
|
||||
#include "clex.h"
|
||||
#include "cosmo.h"
|
||||
|
||||
// compiles source into CChunk, if NULL is returned, a syntaxical error has occurred and pushed onto the stack
|
||||
// compiles source into CChunk, if NULL is returned, a syntaxical error has occurred and pushed onto
|
||||
// the stack
|
||||
CObjFunction *cosmoP_compileString(CState *state, const char *source, const char *module);
|
||||
|
||||
#endif
|
||||
|
19
src/cstate.c
19
src/cstate.c
@ -1,12 +1,14 @@
|
||||
#include "cstate.h"
|
||||
|
||||
#include "cchunk.h"
|
||||
#include "cmem.h"
|
||||
#include "cobj.h"
|
||||
#include "cvm.h"
|
||||
#include "cmem.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
CState *cosmoV_newState() {
|
||||
CState *cosmoV_newState()
|
||||
{
|
||||
// we use C's malloc because we don't want to trigger a GC with an invalid state
|
||||
CState *state = malloc(sizeof(CState));
|
||||
|
||||
@ -73,7 +75,8 @@ CState *cosmoV_newState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
void cosmoV_freeState(CState *state) {
|
||||
void cosmoV_freeState(CState *state)
|
||||
{
|
||||
#ifdef GC_DEBUG
|
||||
printf("state %p is being free'd!\n", state);
|
||||
#endif
|
||||
@ -100,15 +103,16 @@ void cosmoV_freeState(CState *state) {
|
||||
// TODO: yeah idk, it looks like im missing 520 bytes somewhere? i'll look into it later
|
||||
/*#ifdef GC_DEBUG
|
||||
if (state->allocatedBytes != sizeof(CState)) {
|
||||
printf("state->allocatedBytes doesn't match expected value (%lu), got %lu!", sizeof(CState), state->allocatedBytes);
|
||||
exit(0);
|
||||
printf("state->allocatedBytes doesn't match expected value (%lu), got %lu!",
|
||||
sizeof(CState), state->allocatedBytes); exit(0);
|
||||
}
|
||||
#endif*/
|
||||
free(state);
|
||||
}
|
||||
|
||||
// expects 2*pairs values on the stack, each pair should consist of 1 key and 1 value
|
||||
void cosmoV_register(CState *state, int pairs) {
|
||||
void cosmoV_register(CState *state, int pairs)
|
||||
{
|
||||
for (int i = 0; i < pairs; i++) {
|
||||
StkPtr key = cosmoV_getTop(state, 1);
|
||||
StkPtr val = cosmoV_getTop(state, 0);
|
||||
@ -120,7 +124,8 @@ void cosmoV_register(CState *state, int pairs) {
|
||||
}
|
||||
}
|
||||
|
||||
void cosmoV_printStack(CState *state) {
|
||||
void cosmoV_printStack(CState *state)
|
||||
{
|
||||
printf("==== [[ stack dump ]] ====\n");
|
||||
for (CValue *top = state->top - 1; top >= state->stack; top--) {
|
||||
printf("%d: ", (int)(top - state->stack));
|
||||
|
28
src/cstate.h
28
src/cstate.h
@ -1,18 +1,20 @@
|
||||
#ifndef CSTATE_H
|
||||
#define CSTATE_H
|
||||
|
||||
#include "cosmo.h"
|
||||
#include "cobj.h"
|
||||
#include "cvalue.h"
|
||||
#include "cosmo.h"
|
||||
#include "ctable.h"
|
||||
#include "cvalue.h"
|
||||
|
||||
struct CCallFrame {
|
||||
struct CCallFrame
|
||||
{
|
||||
CObjClosure *closure;
|
||||
INSTRUCTION *pc;
|
||||
CValue *base;
|
||||
};
|
||||
|
||||
typedef enum IStringEnum {
|
||||
typedef enum IStringEnum
|
||||
{
|
||||
ISTRING_INIT, // __init
|
||||
ISTRING_TOSTRING, // __tostring
|
||||
ISTRING_TONUMBER, // __tonumber
|
||||
@ -25,24 +27,29 @@ typedef enum IStringEnum {
|
||||
ISTRING_ITER, // __iter
|
||||
ISTRING_NEXT, // __next
|
||||
ISTRING_RESERVED, // __reserved
|
||||
ISTRING_MAX // if this becomes greater than 33, we are out of space in cosmo_Flag. you'll have to change that to uint64_t
|
||||
ISTRING_MAX // if this becomes greater than 33, we are out of space in cosmo_Flag. you'll have
|
||||
// to change that to uint64_t
|
||||
} IStringEnum;
|
||||
|
||||
typedef struct ArrayCObj {
|
||||
typedef struct ArrayCObj
|
||||
{
|
||||
CObj **array;
|
||||
int count;
|
||||
int capacity;
|
||||
} ArrayCObj;
|
||||
|
||||
struct CState {
|
||||
struct CState
|
||||
{
|
||||
bool panic;
|
||||
int freezeGC; // when > 0, GC events will be ignored (for internal use)
|
||||
int frameCount;
|
||||
|
||||
CObjError *error; // NULL, unless panic is true
|
||||
CObj *objects; // tracks all of our allocated objects
|
||||
CObj *userRoots; // user definable roots, this holds CObjs that should be considered "roots", lets the VM know you are holding a reference to a CObj in your code
|
||||
ArrayCObj grayStack; // keeps track of which objects *haven't yet* been traversed in our GC, but *have been* found
|
||||
CObj *userRoots; // user definable roots, this holds CObjs that should be considered "roots",
|
||||
// lets the VM know you are holding a reference to a CObj in your code
|
||||
ArrayCObj grayStack; // keeps track of which objects *haven't yet* been traversed in our GC, but
|
||||
// *have been* found
|
||||
size_t allocatedBytes;
|
||||
size_t nextGC; // when allocatedBytes reaches this threshhold, trigger a GC event
|
||||
|
||||
@ -52,7 +59,8 @@ struct CState {
|
||||
|
||||
CValue *top; // top of the stack
|
||||
CObjObject *protoObjects[COBJ_MAX]; // proto object for each COBJ type [NULL = no default proto]
|
||||
CObjString *iStrings[ISTRING_MAX]; // strings used internally by the VM, eg. __init, __index & friends
|
||||
CObjString
|
||||
*iStrings[ISTRING_MAX]; // strings used internally by the VM, eg. __init, __index & friends
|
||||
CCallFrame callFrame[FRAME_MAX]; // call frames
|
||||
CValue stack[STACK_MAX]; // stack
|
||||
};
|
||||
|
89
src/ctable.c
89
src/ctable.c
@ -1,7 +1,8 @@
|
||||
#include "ctable.h"
|
||||
|
||||
#include "cmem.h"
|
||||
#include "cvalue.h"
|
||||
#include "cobj.h"
|
||||
#include "cvalue.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
@ -10,12 +11,15 @@
|
||||
#define MIN_TABLE_CAPACITY ARRAY_START
|
||||
|
||||
// bit-twiddling hacks, gets the next power of 2
|
||||
unsigned int nextPow2(unsigned int x) {
|
||||
if (x <= ARRAY_START - 1) return ARRAY_START; // sanity check
|
||||
unsigned int nextPow2(unsigned int x)
|
||||
{
|
||||
if (x <= ARRAY_START - 1)
|
||||
return ARRAY_START; // sanity check
|
||||
x--;
|
||||
|
||||
int power = 2;
|
||||
while (x >>= 1) power <<= 1;
|
||||
while (x >>= 1)
|
||||
power <<= 1;
|
||||
|
||||
if (power < ARRAY_START)
|
||||
return ARRAY_START;
|
||||
@ -23,7 +27,8 @@ unsigned int nextPow2(unsigned int x) {
|
||||
return power;
|
||||
}
|
||||
|
||||
void cosmoT_initTable(CState *state, CTable *tbl, int startCap) {
|
||||
void cosmoT_initTable(CState *state, CTable *tbl, int startCap)
|
||||
{
|
||||
startCap = startCap != 0 ? startCap : ARRAY_START; // sanity check :P
|
||||
|
||||
tbl->capacityMask = startCap - 1;
|
||||
@ -39,7 +44,8 @@ void cosmoT_initTable(CState *state, CTable *tbl, int startCap) {
|
||||
}
|
||||
}
|
||||
|
||||
void cosmoT_addTable(CState *state, CTable *from, CTable *to) {
|
||||
void cosmoT_addTable(CState *state, CTable *from, CTable *to)
|
||||
{
|
||||
int cap = from->capacityMask + 1;
|
||||
for (int i = 0; i < cap; i++) {
|
||||
CTableEntry *entry = &from->table[i];
|
||||
@ -51,11 +57,13 @@ void cosmoT_addTable(CState *state, CTable *from, CTable *to) {
|
||||
}
|
||||
}
|
||||
|
||||
void cosmoT_clearTable(CState *state, CTable *tbl) {
|
||||
void cosmoT_clearTable(CState *state, CTable *tbl)
|
||||
{
|
||||
cosmoM_freearray(state, CTableEntry, tbl->table, (tbl->capacityMask + 1));
|
||||
}
|
||||
|
||||
uint32_t getObjectHash(CObj *obj) {
|
||||
uint32_t getObjectHash(CObj *obj)
|
||||
{
|
||||
switch (obj->type) {
|
||||
case COBJ_STRING:
|
||||
return ((CObjString *)obj)->hash;
|
||||
@ -64,7 +72,8 @@ uint32_t getObjectHash(CObj *obj) {
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t getValueHash(CValue *val) {
|
||||
uint32_t getValueHash(CValue *val)
|
||||
{
|
||||
switch (GET_TYPE(*val)) {
|
||||
case COSMO_TREF:
|
||||
return getObjectHash(cosmoV_readRef(*val));
|
||||
@ -76,7 +85,8 @@ uint32_t getValueHash(CValue *val) {
|
||||
return 0;
|
||||
|
||||
memcpy(buf, &num, sizeof(buf));
|
||||
for (size_t i = 0; i < sizeof(cosmo_Number)/sizeof(uint32_t); i++) buf[0] += buf[i];
|
||||
for (size_t i = 0; i < sizeof(cosmo_Number) / sizeof(uint32_t); i++)
|
||||
buf[0] += buf[i];
|
||||
return buf[0];
|
||||
}
|
||||
// TODO: add support for other types
|
||||
@ -86,9 +96,11 @@ uint32_t getValueHash(CValue *val) {
|
||||
}
|
||||
|
||||
// mask should always be (capacity - 1)
|
||||
static CTableEntry *findEntry(CState *state, CTableEntry *entries, int mask, CValue key) {
|
||||
static CTableEntry *findEntry(CState *state, CTableEntry *entries, int mask, CValue key)
|
||||
{
|
||||
uint32_t hash = getValueHash(&key);
|
||||
uint32_t indx = hash & mask; // since we know the capacity will *always* be a power of 2, we can use bitwise & to perform a MUCH faster mod operation
|
||||
uint32_t indx = hash & mask; // since we know the capacity will *always* be a power of 2, we can
|
||||
// use bitwise & to perform a MUCH faster mod operation
|
||||
CTableEntry *tomb = NULL;
|
||||
|
||||
// keep looking for an open slot in the entries array
|
||||
@ -112,7 +124,8 @@ static CTableEntry *findEntry(CState *state, CTableEntry *entries, int mask, CVa
|
||||
}
|
||||
}
|
||||
|
||||
static void resizeTbl(CState *state, CTable *tbl, int newCapacity, bool canShrink) {
|
||||
static void resizeTbl(CState *state, CTable *tbl, int newCapacity, bool canShrink)
|
||||
{
|
||||
if (canShrink && cosmoT_checkShrink(state, tbl))
|
||||
return;
|
||||
|
||||
@ -122,7 +135,8 @@ static void resizeTbl(CState *state, CTable *tbl, int newCapacity, bool canShrin
|
||||
|
||||
cosmoM_checkGarbage(state, size); // if this allocation would cause a GC, run the GC
|
||||
|
||||
if (tbl->count < cachedCount) // the GC removed some objects from this table and resized it, ignore our resize event!
|
||||
if (tbl->count < cachedCount) // the GC removed some objects from this table and resized it,
|
||||
// ignore our resize event!
|
||||
return;
|
||||
|
||||
CTableEntry *entries = cosmoM_xmalloc(state, size);
|
||||
@ -157,10 +171,14 @@ static void resizeTbl(CState *state, CTable *tbl, int newCapacity, bool canShrin
|
||||
tbl->tombstones = 0;
|
||||
}
|
||||
|
||||
bool cosmoT_checkShrink(CState *state, CTable *tbl) {
|
||||
bool cosmoT_checkShrink(CState *state, CTable *tbl)
|
||||
{
|
||||
// if count > 8 and active entries < tombstones
|
||||
if (tbl->count > MIN_TABLE_CAPACITY && (tbl->count - tbl->tombstones < tbl->tombstones || tbl->tombstones > 50)) { // TODO: 50 should be a threshhold
|
||||
resizeTbl(state, tbl, nextPow2(tbl->count - tbl->tombstones) * GROW_FACTOR, false); // shrink based on active entries to the next pow of 2
|
||||
if (tbl->count > MIN_TABLE_CAPACITY &&
|
||||
(tbl->count - tbl->tombstones < tbl->tombstones ||
|
||||
tbl->tombstones > 50)) { // TODO: 50 should be a threshhold
|
||||
resizeTbl(state, tbl, nextPow2(tbl->count - tbl->tombstones) * GROW_FACTOR,
|
||||
false); // shrink based on active entries to the next pow of 2
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -168,7 +186,8 @@ bool cosmoT_checkShrink(CState *state, CTable *tbl) {
|
||||
}
|
||||
|
||||
// returns a pointer to the allocated value
|
||||
COSMO_API CValue* cosmoT_insert(CState *state, CTable *tbl, CValue key) {
|
||||
COSMO_API CValue *cosmoT_insert(CState *state, CTable *tbl, CValue key)
|
||||
{
|
||||
// make sure we have enough space allocated
|
||||
int cap = tbl->capacityMask + 1;
|
||||
if (tbl->count + 1 > (int)(cap * MAX_TABLE_FILL)) {
|
||||
@ -178,7 +197,8 @@ COSMO_API CValue* cosmoT_insert(CState *state, CTable *tbl, CValue key) {
|
||||
}
|
||||
|
||||
// insert into the table
|
||||
CTableEntry *entry = findEntry(state, tbl->table, tbl->capacityMask, key); // -1 for our capacity mask
|
||||
CTableEntry *entry =
|
||||
findEntry(state, tbl->table, tbl->capacityMask, key); // -1 for our capacity mask
|
||||
|
||||
if (IS_NIL(entry->key)) {
|
||||
if (IS_NIL(entry->val)) // is it empty?
|
||||
@ -191,7 +211,8 @@ COSMO_API CValue* cosmoT_insert(CState *state, CTable *tbl, CValue key) {
|
||||
return &entry->val;
|
||||
}
|
||||
|
||||
bool cosmoT_get(CState *state, CTable *tbl, CValue key, CValue *val) {
|
||||
bool cosmoT_get(CState *state, CTable *tbl, CValue key, CValue *val)
|
||||
{
|
||||
// sanity check
|
||||
if (tbl->count == 0) {
|
||||
*val = cosmoV_newNil();
|
||||
@ -205,8 +226,10 @@ bool cosmoT_get(CState *state, CTable *tbl, CValue key, CValue *val) {
|
||||
return !(IS_NIL(entry->key));
|
||||
}
|
||||
|
||||
bool cosmoT_remove(CState* state, CTable *tbl, CValue key) {
|
||||
if (tbl->count == 0) return 0; // sanity check
|
||||
bool cosmoT_remove(CState *state, CTable *tbl, CValue key)
|
||||
{
|
||||
if (tbl->count == 0)
|
||||
return 0; // sanity check
|
||||
|
||||
CTableEntry *entry = findEntry(state, tbl->table, tbl->capacityMask, key);
|
||||
if (IS_NIL(entry->key)) // sanity check
|
||||
@ -214,20 +237,26 @@ bool cosmoT_remove(CState* state, CTable *tbl, CValue key) {
|
||||
|
||||
// crafts tombstone
|
||||
entry->key = cosmoV_newNil(); // this has to be nil
|
||||
entry->val = cosmoV_newBoolean(false); // doesn't really matter what this is, as long as it isn't nil
|
||||
entry->val =
|
||||
cosmoV_newBoolean(false); // doesn't really matter what this is, as long as it isn't nil
|
||||
tbl->tombstones++;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// returns the active entry count
|
||||
COSMO_API int cosmoT_count(CTable *tbl) {
|
||||
COSMO_API int cosmoT_count(CTable *tbl)
|
||||
{
|
||||
return tbl->count - tbl->tombstones;
|
||||
}
|
||||
|
||||
CObjString *cosmoT_lookupString(CTable *tbl, const char *str, int length, uint32_t hash) {
|
||||
if (tbl->count == 0) return 0; // sanity check
|
||||
uint32_t indx = hash & tbl->capacityMask; // since we know the capacity will *always* be a power of 2, we can use bitwise & to perform a MUCH faster mod operation
|
||||
CObjString *cosmoT_lookupString(CTable *tbl, const char *str, int length, uint32_t hash)
|
||||
{
|
||||
if (tbl->count == 0)
|
||||
return 0; // sanity check
|
||||
uint32_t indx =
|
||||
hash & tbl->capacityMask; // since we know the capacity will *always* be a power of 2, we
|
||||
// can use bitwise & to perform a MUCH faster mod operation
|
||||
|
||||
// keep looking for an open slot in the entries array
|
||||
while (true) {
|
||||
@ -236,7 +265,8 @@ CObjString *cosmoT_lookupString(CTable *tbl, const char *str, int length, uint32
|
||||
// check if it's an empty slot (meaning we dont have it in the table)
|
||||
if (IS_NIL(entry->key) && IS_NIL(entry->val)) {
|
||||
return NULL;
|
||||
} else if (IS_STRING(entry->key) && cosmoV_readString(entry->key)->length == length && memcmp(cosmoV_readString(entry->key)->str, str, length) == 0) {
|
||||
} else if (IS_STRING(entry->key) && cosmoV_readString(entry->key)->length == length &&
|
||||
memcmp(cosmoV_readString(entry->key)->str, str, length) == 0) {
|
||||
// it's a match!
|
||||
return (CObjString *)cosmoV_readRef(entry->key);
|
||||
}
|
||||
@ -246,7 +276,8 @@ CObjString *cosmoT_lookupString(CTable *tbl, const char *str, int length, uint32
|
||||
}
|
||||
|
||||
// for debugging purposes
|
||||
void cosmoT_printTable(CTable *tbl, const char *name) {
|
||||
void cosmoT_printTable(CTable *tbl, const char *name)
|
||||
{
|
||||
printf("==== [[%s]] ====\n", name);
|
||||
int cap = tbl->capacityMask + 1;
|
||||
for (int i = 0; i < cap; i++) {
|
||||
|
@ -1,17 +1,20 @@
|
||||
#ifndef CTABLE_H
|
||||
#define CTABLE_H
|
||||
|
||||
/* TODO: rewrite this table implementation. compared to other languages (including python!) this table is verrryyyy slow */
|
||||
/* TODO: rewrite this table implementation. compared to other languages (including python!) this
|
||||
* table is verrryyyy slow */
|
||||
|
||||
#include "cosmo.h"
|
||||
#include "cvalue.h"
|
||||
|
||||
typedef struct CTableEntry {
|
||||
typedef struct CTableEntry
|
||||
{
|
||||
CValue key;
|
||||
CValue val;
|
||||
} CTableEntry;
|
||||
|
||||
typedef struct CTable {
|
||||
typedef struct CTable
|
||||
{
|
||||
int count;
|
||||
int capacityMask; // +1 to get the capacity
|
||||
int tombstones;
|
||||
|
58
src/cvalue.c
58
src/cvalue.c
@ -1,40 +1,50 @@
|
||||
#include "cosmo.h"
|
||||
#include "cmem.h"
|
||||
#include "cvalue.h"
|
||||
#include "cobj.h"
|
||||
|
||||
void initValArray(CState *state, CValueArray *val, size_t startCapacity) {
|
||||
#include "cmem.h"
|
||||
#include "cobj.h"
|
||||
#include "cosmo.h"
|
||||
|
||||
void initValArray(CState *state, CValueArray *val, size_t startCapacity)
|
||||
{
|
||||
val->count = 0;
|
||||
val->capacity = startCapacity;
|
||||
val->values = NULL;
|
||||
}
|
||||
|
||||
void cleanValArray(CState *state, CValueArray *array) {
|
||||
void cleanValArray(CState *state, CValueArray *array)
|
||||
{
|
||||
cosmoM_freearray(state, CValue, array->values, array->capacity);
|
||||
}
|
||||
|
||||
void appendValArray(CState *state, CValueArray *array, CValue val) {
|
||||
void appendValArray(CState *state, CValueArray *array, CValue val)
|
||||
{
|
||||
cosmoM_growarray(state, CValue, array->values, array->count, array->capacity);
|
||||
|
||||
array->values[array->count++] = val;
|
||||
}
|
||||
|
||||
bool cosmoV_equal(CState *state, CValue valA, CValue valB) {
|
||||
bool cosmoV_equal(CState *state, CValue valA, CValue valB)
|
||||
{
|
||||
if (GET_TYPE(valA) != GET_TYPE(valB)) // are they the same type?
|
||||
return false;
|
||||
|
||||
// compare
|
||||
switch (GET_TYPE(valA)) {
|
||||
case COSMO_TBOOLEAN: return cosmoV_readBoolean(valA) == cosmoV_readBoolean(valB);
|
||||
case COSMO_TNUMBER: return cosmoV_readNumber(valA) == cosmoV_readNumber(valB);
|
||||
case COSMO_TREF: return cosmoO_equal(state, cosmoV_readRef(valA), cosmoV_readRef(valB));
|
||||
case COSMO_TNIL: return true;
|
||||
case COSMO_TBOOLEAN:
|
||||
return cosmoV_readBoolean(valA) == cosmoV_readBoolean(valB);
|
||||
case COSMO_TNUMBER:
|
||||
return cosmoV_readNumber(valA) == cosmoV_readNumber(valB);
|
||||
case COSMO_TREF:
|
||||
return cosmoO_equal(state, cosmoV_readRef(valA), cosmoV_readRef(valB));
|
||||
case COSMO_TNIL:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
CObjString *cosmoV_toString(CState *state, CValue val) {
|
||||
CObjString *cosmoV_toString(CState *state, CValue val)
|
||||
{
|
||||
switch (GET_TYPE(val)) {
|
||||
case COSMO_TNUMBER: {
|
||||
char buf[32];
|
||||
@ -42,7 +52,8 @@ CObjString *cosmoV_toString(CState *state, CValue val) {
|
||||
return cosmoO_copyString(state, (char *)&buf, size);
|
||||
}
|
||||
case COSMO_TBOOLEAN: {
|
||||
return cosmoV_readBoolean(val) ? cosmoO_copyString(state, "true", 4) : cosmoO_copyString(state, "false", 5);
|
||||
return cosmoV_readBoolean(val) ? cosmoO_copyString(state, "true", 4)
|
||||
: cosmoO_copyString(state, "false", 5);
|
||||
}
|
||||
case COSMO_TREF: {
|
||||
return cosmoO_toString(state, cosmoV_readRef(val));
|
||||
@ -55,7 +66,8 @@ CObjString *cosmoV_toString(CState *state, CValue val) {
|
||||
}
|
||||
}
|
||||
|
||||
cosmo_Number cosmoV_toNumber(CState *state, CValue val) {
|
||||
cosmo_Number cosmoV_toNumber(CState *state, CValue val)
|
||||
{
|
||||
switch (GET_TYPE(val)) {
|
||||
case COSMO_TNUMBER: {
|
||||
return cosmoV_readNumber(val);
|
||||
@ -72,19 +84,25 @@ cosmo_Number cosmoV_toNumber(CState *state, CValue val) {
|
||||
}
|
||||
}
|
||||
|
||||
const char *cosmoV_typeStr(CValue val) {
|
||||
const char *cosmoV_typeStr(CValue val)
|
||||
{
|
||||
switch (GET_TYPE(val)) {
|
||||
case COSMO_TNIL: return "<nil>";
|
||||
case COSMO_TBOOLEAN: return "<bool>";
|
||||
case COSMO_TNUMBER: return "<number>";
|
||||
case COSMO_TREF: return cosmoO_typeStr(cosmoV_readRef(val));
|
||||
case COSMO_TNIL:
|
||||
return "<nil>";
|
||||
case COSMO_TBOOLEAN:
|
||||
return "<bool>";
|
||||
case COSMO_TNUMBER:
|
||||
return "<number>";
|
||||
case COSMO_TREF:
|
||||
return cosmoO_typeStr(cosmoV_readRef(val));
|
||||
|
||||
default:
|
||||
return "<unkn val>";
|
||||
}
|
||||
}
|
||||
|
||||
void printValue(CValue val) {
|
||||
void printValue(CValue val)
|
||||
{
|
||||
switch (GET_TYPE(val)) {
|
||||
case COSMO_TNUMBER:
|
||||
printf("%g", cosmoV_readNumber(val));
|
||||
|
25
src/cvalue.h
25
src/cvalue.h
@ -3,7 +3,8 @@
|
||||
|
||||
#include "cosmo.h"
|
||||
|
||||
typedef enum {
|
||||
typedef enum
|
||||
{
|
||||
COSMO_TNUMBER, // number has to be 0 because NaN box
|
||||
COSMO_TBOOLEAN,
|
||||
COSMO_TREF,
|
||||
@ -18,8 +19,8 @@ typedef double cosmo_Number;
|
||||
|
||||
#ifdef NAN_BOXXED
|
||||
/*
|
||||
NaN box, this is great for fitting more in the cpu cache on x86_64 or ARM64 architectures. If you don't know how this works please reference these
|
||||
two articles:
|
||||
NaN box, this is great for fitting more in the cpu cache on x86_64 or ARM64 architectures.
|
||||
If you don't know how this works please reference these two articles:
|
||||
|
||||
https://leonardschuetz.ch/blog/nan-boxing/ and https://piotrduperas.com/posts/nan-boxing/
|
||||
|
||||
@ -27,7 +28,8 @@ typedef double cosmo_Number;
|
||||
|
||||
TL;DR: we can store payloads in the NaN value in the IEEE 754 standard.
|
||||
*/
|
||||
union CValue {
|
||||
union CValue
|
||||
{
|
||||
uint64_t data;
|
||||
cosmo_Number num;
|
||||
};
|
||||
@ -43,7 +45,8 @@ union CValue {
|
||||
# define MASK_QUIETNAN ((uint64_t)0x7ff8000000000000)
|
||||
|
||||
# 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 BOOL_SIG (MASK_QUIETNAN | ((uint64_t)(COSMO_TBOOLEAN) << 48))
|
||||
@ -68,9 +71,11 @@ union CValue {
|
||||
/*
|
||||
Tagged union, this is the best platform independent solution
|
||||
*/
|
||||
struct CValue {
|
||||
struct CValue
|
||||
{
|
||||
CosmoType type;
|
||||
union {
|
||||
union
|
||||
{
|
||||
cosmo_Number num;
|
||||
bool b; // boolean
|
||||
CObj *obj;
|
||||
@ -103,7 +108,8 @@ struct CValue {
|
||||
|
||||
typedef CValue *StkPtr;
|
||||
|
||||
struct CValueArray {
|
||||
struct CValueArray
|
||||
{
|
||||
size_t capacity;
|
||||
size_t count;
|
||||
CValue *values;
|
||||
@ -117,6 +123,7 @@ void printValue(CValue val);
|
||||
COSMO_API bool cosmoV_equal(CState *state, CValue valA, CValue valB);
|
||||
COSMO_API CObjString *cosmoV_toString(CState *state, CValue val);
|
||||
COSMO_API cosmo_Number cosmoV_toNumber(CState *state, CValue val);
|
||||
COSMO_API const char *cosmoV_typeStr(CValue val); // return constant char array for corresponding type
|
||||
COSMO_API const char *
|
||||
cosmoV_typeStr(CValue val); // return constant char array for corresponding type
|
||||
|
||||
#endif
|
||||
|
254
src/cvm.c
254
src/cvm.c
@ -1,15 +1,16 @@
|
||||
#include "cvm.h"
|
||||
#include "cstate.h"
|
||||
|
||||
#include "cdebug.h"
|
||||
#include "cmem.h"
|
||||
#include "cparse.h"
|
||||
#include "cstate.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <math.h>
|
||||
|
||||
COSMO_API void cosmoV_pushFString(CState *state, const char *format, ...) {
|
||||
COSMO_API void cosmoV_pushFString(CState *state, const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
cosmoO_pushVFString(state, format, args);
|
||||
@ -17,7 +18,8 @@ COSMO_API void cosmoV_pushFString(CState *state, const char *format, ...) {
|
||||
}
|
||||
|
||||
// inserts val at state->top - indx - 1, moving everything else up
|
||||
COSMO_API void cosmo_insert(CState *state, int indx, CValue val) {
|
||||
COSMO_API void cosmo_insert(CState *state, int indx, CValue val)
|
||||
{
|
||||
StkPtr tmp = cosmoV_getTop(state, indx);
|
||||
|
||||
// moves everything up
|
||||
@ -28,7 +30,8 @@ COSMO_API void cosmo_insert(CState *state, int indx, CValue val) {
|
||||
state->top++;
|
||||
}
|
||||
|
||||
COSMO_API bool cosmoV_compileString(CState *state, const char *src, const char *name) {
|
||||
COSMO_API bool cosmoV_compileString(CState *state, const char *src, const char *name)
|
||||
{
|
||||
CObjFunction *func;
|
||||
|
||||
if ((func = cosmoP_compileString(state, src, name)) != NULL) {
|
||||
@ -36,7 +39,8 @@ COSMO_API bool cosmoV_compileString(CState *state, const char *src, const char *
|
||||
#ifdef VM_DEBUG
|
||||
disasmChunk(&func->chunk, func->module->str, 0);
|
||||
#endif
|
||||
// push function onto the stack so it doesn't it cleaned up by the GC, at the same stack location put our closure
|
||||
// push function onto the stack so it doesn't it cleaned up by the GC, at the same stack
|
||||
// location put our closure
|
||||
cosmoV_pushRef(state, (CObj *)func);
|
||||
*(cosmoV_getTop(state, 0)) = cosmoV_newRef(cosmoO_newClosure(state, func));
|
||||
return true;
|
||||
@ -48,7 +52,8 @@ COSMO_API bool cosmoV_compileString(CState *state, const char *src, const char *
|
||||
return false;
|
||||
}
|
||||
|
||||
COSMO_API void cosmoV_printError(CState *state, CObjError *err) {
|
||||
COSMO_API void cosmoV_printError(CState *state, CObjError *err)
|
||||
{
|
||||
// print stack trace
|
||||
for (int i = 0; i < err->frameCount; i++) {
|
||||
CCallFrame *frame = &err->frames[i];
|
||||
@ -57,8 +62,11 @@ COSMO_API void cosmoV_printError(CState *state, CObjError *err) {
|
||||
|
||||
int line = chunk->lineInfo[frame->pc - chunk->buf - 1];
|
||||
|
||||
if (i == err->frameCount - 1 && !err->parserError) // it's the last call frame (and not a parser error), prepare for the objection to be printed
|
||||
fprintf(stderr, "Objection in %.*s on [line %d] in ", function->module->length, function->module->str, line);
|
||||
if (i == err->frameCount - 1 &&
|
||||
!err->parserError) // it's the last call frame (and not a parser error), prepare for the
|
||||
// objection to be printed
|
||||
fprintf(stderr, "Objection in %.*s on [line %d] in ", function->module->length,
|
||||
function->module->str, line);
|
||||
else
|
||||
fprintf(stderr, "[line %d] in ", line);
|
||||
|
||||
@ -78,11 +86,12 @@ COSMO_API void cosmoV_printError(CState *state, CObjError *err) {
|
||||
}
|
||||
|
||||
/*
|
||||
takes value on top of the stack and wraps an CObjError around it, state->error is set to that value
|
||||
the value on the stack is *expected* to be a string, but not required, so
|
||||
yes, this means you could throw a nil value if you really wanted too..
|
||||
takes value on top of the stack and wraps an CObjError around it, state->error is set to that
|
||||
value the value on the stack is *expected* to be a string, but not required, so yes, this means
|
||||
you could throw a nil value if you really wanted too..
|
||||
*/
|
||||
CObjError* cosmoV_throw(CState *state) {
|
||||
CObjError *cosmoV_throw(CState *state)
|
||||
{
|
||||
StkPtr temp = cosmoV_getTop(state, 0);
|
||||
|
||||
CObjError *error = cosmoO_newError(state, *temp);
|
||||
@ -93,7 +102,8 @@ CObjError* cosmoV_throw(CState *state) {
|
||||
return error;
|
||||
}
|
||||
|
||||
void cosmoV_error(CState *state, const char *format, ...) {
|
||||
void cosmoV_error(CState *state, const char *format, ...)
|
||||
{
|
||||
if (state->panic)
|
||||
return;
|
||||
|
||||
@ -110,11 +120,13 @@ void cosmoV_error(CState *state, const char *format, ...) {
|
||||
cosmoV_throw(state);
|
||||
}
|
||||
|
||||
CObjUpval *captureUpvalue(CState *state, CValue *local) {
|
||||
CObjUpval *captureUpvalue(CState *state, CValue *local)
|
||||
{
|
||||
CObjUpval *prev = NULL;
|
||||
CObjUpval *upvalue = state->openUpvalues;
|
||||
|
||||
while (upvalue != NULL && upvalue->val > local) { // while upvalue exists and is higher on the stack than local
|
||||
while (upvalue != NULL &&
|
||||
upvalue->val > local) { // while upvalue exists and is higher on the stack than local
|
||||
prev = upvalue;
|
||||
upvalue = upvalue->next;
|
||||
}
|
||||
@ -136,8 +148,11 @@ CObjUpval *captureUpvalue(CState *state, CValue *local) {
|
||||
return newUpval;
|
||||
}
|
||||
|
||||
void closeUpvalues(CState *state, CValue *local) {
|
||||
while (state->openUpvalues != NULL && state->openUpvalues->val >= local) { // for every upvalue that points to the local or anything above it
|
||||
void closeUpvalues(CState *state, CValue *local)
|
||||
{
|
||||
while (state->openUpvalues != NULL &&
|
||||
state->openUpvalues->val >=
|
||||
local) { // for every upvalue that points to the local or anything above it
|
||||
CObjUpval *upvalue = state->openUpvalues;
|
||||
upvalue->closed = *upvalue->val;
|
||||
upvalue->val = &upvalue->closed; // upvalue now points to itself :P
|
||||
@ -145,7 +160,8 @@ void closeUpvalues(CState *state, CValue *local) {
|
||||
}
|
||||
}
|
||||
|
||||
void pushCallFrame(CState *state, CObjClosure *closure, int args) {
|
||||
void pushCallFrame(CState *state, CObjClosure *closure, int args)
|
||||
{
|
||||
#ifdef SAFE_STACK
|
||||
if (state->frameCount >= FRAME_MAX) {
|
||||
cosmoV_error(state, "Callframe overflow!");
|
||||
@ -159,15 +175,19 @@ void pushCallFrame(CState *state, CObjClosure *closure, int args) {
|
||||
frame->closure = closure;
|
||||
}
|
||||
|
||||
// offset is the offset of the callframe base we set the state->top back too (useful for passing values in the stack as arguments, like methods)
|
||||
void popCallFrame(CState *state, int offset) {
|
||||
closeUpvalues(state, state->callFrame[state->frameCount - 1].base); // close any upvalue still open
|
||||
// offset is the offset of the callframe base we set the state->top back too (useful for passing
|
||||
// values in the stack as arguments, like methods)
|
||||
void popCallFrame(CState *state, int offset)
|
||||
{
|
||||
closeUpvalues(state,
|
||||
state->callFrame[state->frameCount - 1].base); // close any upvalue still open
|
||||
|
||||
state->top = state->callFrame[state->frameCount - 1].base + offset; // resets the stack
|
||||
state->frameCount--;
|
||||
}
|
||||
|
||||
void cosmoV_concat(CState *state, int vals) {
|
||||
void cosmoV_concat(CState *state, int vals)
|
||||
{
|
||||
StkPtr start = state->top - vals;
|
||||
StkPtr end = cosmoV_getTop(state, 0);
|
||||
|
||||
@ -197,21 +217,24 @@ int cosmoV_execute(CState *state);
|
||||
bool invokeMethod(CState *state, CObj *obj, CValue func, int args, int nresults, int offset);
|
||||
|
||||
/*
|
||||
calls a native C Function with # args on the stack, nresults are pushed onto the stack upon return.
|
||||
calls a native C Function with # args on the stack, nresults are pushed onto the stack upon
|
||||
return.
|
||||
|
||||
returns:
|
||||
false: state paniced during C Function, error is at state->error
|
||||
true: state->top is moved to base + offset + nresults, with nresults pushed onto the stack from base + offset
|
||||
true: state->top is moved to base + offset + nresults, with nresults pushed onto the stack
|
||||
from base + offset
|
||||
*/
|
||||
static bool callCFunction(CState *state, CosmoCFunction cfunc, int args, int nresults, int offset) {
|
||||
static bool callCFunction(CState *state, CosmoCFunction cfunc, int args, int nresults, int offset)
|
||||
{
|
||||
StkPtr savedBase = cosmoV_getTop(state, args);
|
||||
|
||||
// we don't want a GC event during c api because we don't actually trust the user to know how to evade the GC
|
||||
// we don't want a GC event during c api because we don't actually trust the user to know how to
|
||||
// evade the GC
|
||||
cosmoM_freezeGC(state);
|
||||
int nres = cfunc(state, args, savedBase + 1);
|
||||
cosmoM_unfreezeGC(state);
|
||||
|
||||
|
||||
// caller function wasn't expecting this many return values, cap it
|
||||
if (nres > nresults)
|
||||
nres = nresults;
|
||||
@ -226,7 +249,8 @@ static bool callCFunction(CState *state, CosmoCFunction cfunc, int args, int nre
|
||||
return false;
|
||||
|
||||
// push the return value back onto the stack
|
||||
memmove(state->top, results, sizeof(CValue) * nres); // copies the return values to the top of the stack
|
||||
memmove(state->top, results,
|
||||
sizeof(CValue) * nres); // copies the return values to the top of the stack
|
||||
state->top += nres; // and make sure to move state->top to match
|
||||
|
||||
// now, if the caller function expected more return values, push nils onto the stack
|
||||
@ -237,13 +261,16 @@ static bool callCFunction(CState *state, CosmoCFunction cfunc, int args, int nre
|
||||
}
|
||||
|
||||
/*
|
||||
calls a raw closure object with # args on the stack, nresults are pushed onto the stack upon return.
|
||||
calls a raw closure object with # args on the stack, nresults are pushed onto the stack upon
|
||||
return.
|
||||
|
||||
returns:
|
||||
false: state paniced, error is at state->error
|
||||
true: stack->top is moved to base + offset + nresults, with nresults pushed onto the stack from base + offset
|
||||
true: stack->top is moved to base + offset + nresults, with nresults pushed onto the stack
|
||||
from base + offset
|
||||
*/
|
||||
static bool rawCall(CState *state, CObjClosure *closure, int args, int nresults, int offset) {
|
||||
static bool rawCall(CState *state, CObjClosure *closure, int args, int nresults, int offset)
|
||||
{
|
||||
CObjFunction *func = closure->function;
|
||||
|
||||
// if the function is variadic and theres more args than parameters, push the args into a table
|
||||
@ -263,7 +290,9 @@ static bool rawCall(CState *state, CObjClosure *closure, int args, int nresults,
|
||||
|
||||
pushCallFrame(state, closure, func->args + 1);
|
||||
} else if (args != func->args) { // mismatched args
|
||||
cosmoV_error(state, "Expected %d arguments for %s, got %d!", closure->function->args, closure->function->name == NULL ? UNNAMEDCHUNK : closure->function->name->str, args);
|
||||
cosmoV_error(state, "Expected %d arguments for %s, got %d!", closure->function->args,
|
||||
closure->function->name == NULL ? UNNAMEDCHUNK : closure->function->name->str,
|
||||
args);
|
||||
return false;
|
||||
} else {
|
||||
// load function into callframe
|
||||
@ -298,7 +327,8 @@ static bool rawCall(CState *state, CObjClosure *closure, int args, int nresults,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool callCValue(CState *state, CValue func, int args, int nresults, int offset) {
|
||||
bool callCValue(CState *state, CValue func, int args, int nresults, int offset)
|
||||
{
|
||||
#ifdef VM_DEBUG
|
||||
printf("\n");
|
||||
printIndent(state->frameCount - 1);
|
||||
@ -343,7 +373,8 @@ bool callCValue(CState *state, CValue func, int args, int nresults, int offset)
|
||||
cosmoV_pushRef(state, (CObj *)newObj);
|
||||
|
||||
// push the nils to fill up the expected return values
|
||||
for (int i = 0; i < nresults - 1; i++) { // -1 since the we already pushed the important value
|
||||
for (int i = 0; i < nresults - 1;
|
||||
i++) { // -1 since the we already pushed the important value
|
||||
cosmoV_pushValue(state, cosmoV_newNil());
|
||||
}
|
||||
}
|
||||
@ -357,7 +388,8 @@ bool callCValue(CState *state, CValue func, int args, int nresults, int offset)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool invokeMethod(CState* state, CObj *obj, CValue func, int args, int nresults, int offset) {
|
||||
bool invokeMethod(CState *state, CObj *obj, CValue func, int args, int nresults, int offset)
|
||||
{
|
||||
// first, set the first argument to the object
|
||||
StkPtr temp = cosmoV_getTop(state, args);
|
||||
*temp = cosmoV_newRef(obj);
|
||||
@ -365,8 +397,10 @@ bool invokeMethod(CState* state, CObj *obj, CValue func, int args, int nresults,
|
||||
return callCValue(state, func, args + 1, nresults, offset);
|
||||
}
|
||||
|
||||
// wraps cosmoV_call in a protected state, CObjError will be pushed onto the stack if function call failed, else return values are passed
|
||||
COSMOVMRESULT cosmoV_pcall(CState *state, int args, int nresults) {
|
||||
// wraps cosmoV_call in a protected state, CObjError will be pushed onto the stack if function call
|
||||
// failed, else return values are passed
|
||||
COSMOVMRESULT cosmoV_pcall(CState *state, int args, int nresults)
|
||||
{
|
||||
StkPtr base = cosmoV_getTop(state, args);
|
||||
|
||||
if (!callCValue(state, *base, args, nresults, 0)) {
|
||||
@ -388,23 +422,27 @@ COSMOVMRESULT cosmoV_pcall(CState *state, int args, int nresults) {
|
||||
}
|
||||
|
||||
/*
|
||||
calls a callable object at stack->top - args - 1, passing the # of args to the callable, and ensuring nresults are returned
|
||||
calls a callable object at stack->top - args - 1, passing the # of args to the callable, and
|
||||
ensuring nresults are returned
|
||||
|
||||
returns:
|
||||
COSMOVM_OK: callable object exited normally
|
||||
COSMOVM_RUNTIME_ERR: an error occurred, grab the error from state->error
|
||||
*/
|
||||
COSMOVMRESULT cosmoV_call(CState *state, int args, int nresults) {
|
||||
COSMOVMRESULT cosmoV_call(CState *state, int args, int nresults)
|
||||
{
|
||||
StkPtr val = cosmoV_getTop(state, args); // function will always be right above the args
|
||||
|
||||
return callCValue(state, *val, args, nresults, 0) ? COSMOVM_OK : COSMOVM_RUNTIME_ERR;
|
||||
}
|
||||
|
||||
static inline bool isFalsey(StkPtr val) {
|
||||
static inline bool isFalsey(StkPtr val)
|
||||
{
|
||||
return IS_NIL(*val) || (IS_BOOLEAN(*val) && !cosmoV_readBoolean(*val));
|
||||
}
|
||||
|
||||
COSMO_API CObjObject* cosmoV_makeObject(CState *state, int pairs) {
|
||||
COSMO_API CObjObject *cosmoV_makeObject(CState *state, int pairs)
|
||||
{
|
||||
StkPtr key, val;
|
||||
CObjObject *newObj = cosmoO_newObject(state);
|
||||
cosmoV_pushRef(state, (CObj *)newObj); // so our GC doesn't free our new object
|
||||
@ -424,7 +462,8 @@ COSMO_API CObjObject* cosmoV_makeObject(CState *state, int pairs) {
|
||||
return newObj;
|
||||
}
|
||||
|
||||
COSMO_API bool cosmoV_registerProtoObject(CState *state, CObjType objType, CObjObject *obj) {
|
||||
COSMO_API bool cosmoV_registerProtoObject(CState *state, CObjType objType, CObjObject *obj)
|
||||
{
|
||||
bool replaced = state->protoObjects[objType] != NULL;
|
||||
state->protoObjects[objType] = obj;
|
||||
|
||||
@ -441,7 +480,8 @@ COSMO_API bool cosmoV_registerProtoObject(CState *state, CObjType objType, CObjO
|
||||
return replaced;
|
||||
}
|
||||
|
||||
COSMO_API void cosmoV_makeTable(CState *state, int pairs) {
|
||||
COSMO_API void cosmoV_makeTable(CState *state, int pairs)
|
||||
{
|
||||
StkPtr key, val;
|
||||
CObjTable *newObj = cosmoO_newTable(state);
|
||||
cosmoV_pushRef(state, (CObj *)newObj); // so our GC doesn't free our new table
|
||||
@ -460,13 +500,15 @@ COSMO_API void cosmoV_makeTable(CState *state, int pairs) {
|
||||
cosmoV_pushRef(state, (CObj *)newObj);
|
||||
}
|
||||
|
||||
bool cosmoV_rawget(CState *state, CObj *_obj, CValue key, CValue *val) {
|
||||
bool cosmoV_rawget(CState *state, CObj *_obj, CValue key, CValue *val)
|
||||
{
|
||||
CObjObject *object = cosmoO_grabProto(_obj);
|
||||
|
||||
// no proto to get from
|
||||
if (object == NULL) {
|
||||
CObjString *field = cosmoV_toString(state, key);
|
||||
cosmoV_error(state, "No proto defined! Couldn't get field '%s' from type %s", field->str, cosmoO_typeStr(_obj));
|
||||
cosmoV_error(state, "No proto defined! Couldn't get field '%s' from type %s", field->str,
|
||||
cosmoO_typeStr(_obj));
|
||||
*val = cosmoV_newNil();
|
||||
return false;
|
||||
}
|
||||
@ -483,13 +525,15 @@ bool cosmoV_rawget(CState *state, CObj *_obj, CValue key, CValue *val) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool cosmoV_rawset(CState *state, CObj *_obj, CValue key, CValue val) {
|
||||
bool cosmoV_rawset(CState *state, CObj *_obj, CValue key, CValue val)
|
||||
{
|
||||
CObjObject *object = cosmoO_grabProto(_obj);
|
||||
|
||||
// no proto to set to
|
||||
if (object == NULL) {
|
||||
CObjString *field = cosmoV_toString(state, key);
|
||||
cosmoV_error(state, "No proto defined! Couldn't set field '%s' to type %s", field->str, cosmoO_typeStr(_obj));
|
||||
cosmoV_error(state, "No proto defined! Couldn't set field '%s' to type %s", field->str,
|
||||
cosmoO_typeStr(_obj));
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -497,7 +541,8 @@ bool cosmoV_rawset(CState *state, CObj *_obj, CValue key, CValue val) {
|
||||
return true;
|
||||
}
|
||||
|
||||
COSMO_API bool cosmoV_get(CState *state) {
|
||||
COSMO_API bool cosmoV_get(CState *state)
|
||||
{
|
||||
CValue val;
|
||||
StkPtr obj = cosmoV_getTop(state, 1); // object was pushed first
|
||||
StkPtr key = cosmoV_getTop(state, 0); // then the key
|
||||
@ -517,7 +562,8 @@ COSMO_API bool cosmoV_get(CState *state) {
|
||||
}
|
||||
|
||||
// yes, this would technically make it possible to set fields of types other than <string>. go crazy
|
||||
COSMO_API bool cosmoV_set(CState *state) {
|
||||
COSMO_API bool cosmoV_set(CState *state)
|
||||
{
|
||||
StkPtr obj = cosmoV_getTop(state, 2); // object was pushed first
|
||||
StkPtr key = cosmoV_getTop(state, 1); // then the key
|
||||
StkPtr val = cosmoV_getTop(state, 0); // and finally the value
|
||||
@ -535,7 +581,8 @@ COSMO_API bool cosmoV_set(CState *state) {
|
||||
return true;
|
||||
}
|
||||
|
||||
COSMO_API bool cosmoV_getMethod(CState *state, CObj *obj, CValue key, CValue *val) {
|
||||
COSMO_API bool cosmoV_getMethod(CState *state, CObj *obj, CValue key, CValue *val)
|
||||
{
|
||||
if (!cosmoV_rawget(state, obj, key, val))
|
||||
return false;
|
||||
|
||||
@ -551,7 +598,8 @@ COSMO_API bool cosmoV_getMethod(CState *state, CObj *obj, CValue key, CValue *va
|
||||
return true;
|
||||
}
|
||||
|
||||
int _tbl__next(CState *state, int nargs, CValue *args) {
|
||||
int _tbl__next(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "Expected 1 parameter, %d received!", nargs);
|
||||
return 0;
|
||||
@ -569,7 +617,8 @@ int _tbl__next(CState *state, int nargs, CValue *args) {
|
||||
cosmoO_getIString(state, obj, ISTRING_RESERVED, &val);
|
||||
|
||||
if (!IS_TABLE(val)) {
|
||||
return 0; // someone set the __reserved member to something else. this will exit the iterator loop
|
||||
return 0; // someone set the __reserved member to something else. this will exit the
|
||||
// iterator loop
|
||||
}
|
||||
|
||||
CObjTable *table = (CObjTable *)cosmoV_readRef(val);
|
||||
@ -582,7 +631,8 @@ int _tbl__next(CState *state, int nargs, CValue *args) {
|
||||
} while (IS_NIL(entry->key) && index < cap);
|
||||
cosmoO_setUserI(obj, index); // update the userdata
|
||||
|
||||
if (index < cap && !IS_NIL(entry->key)) { // if the entry is valid, return it's key and value pair
|
||||
if (index < cap &&
|
||||
!IS_NIL(entry->key)) { // if the entry is valid, return it's key and value pair
|
||||
cosmoV_pushValue(state, entry->key);
|
||||
cosmoV_pushValue(state, entry->val);
|
||||
return 2; // we pushed 2 values onto the stack for the return values
|
||||
@ -598,12 +648,14 @@ int _tbl__next(CState *state, int nargs, CValue *args) {
|
||||
cosmoV_setTop(state, 2); /* pop the 2 values */ \
|
||||
cosmoV_pushValue(state, typeConst(cosmoV_readNumber(*valA) op cosmoV_readNumber(*valB))); \
|
||||
} else { \
|
||||
cosmoV_error(state, "Expected numbers, got %s and %s!", cosmoV_typeStr(*valA), cosmoV_typeStr(*valB)); \
|
||||
cosmoV_error(state, "Expected numbers, got %s and %s!", cosmoV_typeStr(*valA), \
|
||||
cosmoV_typeStr(*valB)); \
|
||||
return -1; \
|
||||
} \
|
||||
}
|
||||
|
||||
// returns -1 if panic
|
||||
int cosmoV_execute(CState *state) {
|
||||
int cosmoV_execute(CState *state)
|
||||
{
|
||||
CCallFrame *frame = &state->callFrame[state->frameCount - 1]; // grabs the current frame
|
||||
CValue *constants = frame->closure->function->chunk.constants.values; // cache the pointer :)
|
||||
|
||||
@ -613,7 +665,8 @@ int cosmoV_execute(CState *state) {
|
||||
while (!state->panic) {
|
||||
#ifdef VM_DEBUG
|
||||
cosmoV_printStack(state);
|
||||
disasmInstr(&frame->closure->function->chunk, frame->pc - frame->closure->function->chunk.buf, state->frameCount - 1);
|
||||
disasmInstr(&frame->closure->function->chunk,
|
||||
frame->pc - frame->closure->function->chunk.buf, state->frameCount - 1);
|
||||
printf("\n");
|
||||
#endif
|
||||
switch (READBYTE()) {
|
||||
@ -736,7 +789,8 @@ int cosmoV_execute(CState *state) {
|
||||
val = cosmoV_getTop(state, i + 1);
|
||||
|
||||
// set key/value pair
|
||||
CValue *newVal = cosmoT_insert(state, &newObj->tbl, cosmoV_newNumber(pairs - i - 1));
|
||||
CValue *newVal =
|
||||
cosmoT_insert(state, &newObj->tbl, cosmoV_newNumber(pairs - i - 1));
|
||||
*newVal = *val;
|
||||
}
|
||||
|
||||
@ -761,14 +815,16 @@ int cosmoV_execute(CState *state) {
|
||||
|
||||
if (proto != NULL) {
|
||||
// check for __index metamethod
|
||||
if (!cosmoO_indexObject(state, proto, *key, &val)) // if returns false, cosmoV_error was called
|
||||
if (!cosmoO_indexObject(state, proto, *key,
|
||||
&val)) // if returns false, cosmoV_error was called
|
||||
return -1;
|
||||
} else if (obj->type == COBJ_TABLE) {
|
||||
CObjTable *tbl = (CObjTable *)obj;
|
||||
|
||||
cosmoT_get(state, &tbl->tbl, *key, &val);
|
||||
} else {
|
||||
cosmoV_error(state, "No proto defined! Couldn't __index from type %s", cosmoV_typeStr(*temp));
|
||||
cosmoV_error(state, "No proto defined! Couldn't __index from type %s",
|
||||
cosmoV_typeStr(*temp));
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -791,7 +847,8 @@ int cosmoV_execute(CState *state) {
|
||||
CObjObject *proto = cosmoO_grabProto(obj);
|
||||
|
||||
if (proto != NULL) {
|
||||
if (!cosmoO_newIndexObject(state, proto, *key, *value)) // if it returns false, cosmoV_error was called
|
||||
if (!cosmoO_newIndexObject(state, proto, *key,
|
||||
*value)) // if it returns false, cosmoV_error was called
|
||||
return -1;
|
||||
} else if (obj->type == COBJ_TABLE) {
|
||||
CObjTable *tbl = (CObjTable *)obj;
|
||||
@ -799,7 +856,8 @@ int cosmoV_execute(CState *state) {
|
||||
|
||||
*newVal = *value; // set the index
|
||||
} else {
|
||||
cosmoV_error(state, "No proto defined! Couldn't __newindex from type %s", cosmoV_typeStr(*temp));
|
||||
cosmoV_error(state, "No proto defined! Couldn't __newindex from type %s",
|
||||
cosmoV_typeStr(*temp));
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -823,7 +881,8 @@ int cosmoV_execute(CState *state) {
|
||||
return -1;
|
||||
} else {
|
||||
CObjString *field = cosmoV_toString(state, constants[ident]);
|
||||
cosmoV_error(state, "Couldn't set field '%s' on type %s!", field->str, cosmoV_typeStr(*temp));
|
||||
cosmoV_error(state, "Couldn't set field '%s' on type %s!", field->str,
|
||||
cosmoV_typeStr(*temp));
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -842,7 +901,8 @@ int cosmoV_execute(CState *state) {
|
||||
return -1;
|
||||
} else {
|
||||
CObjString *field = cosmoV_toString(state, constants[ident]);
|
||||
cosmoV_error(state, "Couldn't get field '%s' from type %s!", field->str, cosmoV_typeStr(*temp));
|
||||
cosmoV_error(state, "Couldn't get field '%s' from type %s!", field->str,
|
||||
cosmoV_typeStr(*temp));
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -855,13 +915,15 @@ int cosmoV_execute(CState *state) {
|
||||
StkPtr temp = cosmoV_getTop(state, 0); // that should be the object
|
||||
uint16_t ident = READUINT(); // use for the key
|
||||
|
||||
// this is almost identical to GETOBJECT, however cosmoV_getMethod is used instead of just cosmoV_get
|
||||
// this is almost identical to GETOBJECT, however cosmoV_getMethod is used instead of
|
||||
// just cosmoV_get
|
||||
if (IS_REF(*temp)) {
|
||||
if (!cosmoV_getMethod(state, cosmoV_readRef(*temp), constants[ident], &val))
|
||||
return -1;
|
||||
} else {
|
||||
CObjString *field = cosmoV_toString(state, constants[ident]);
|
||||
cosmoV_error(state, "Couldn't get field '%s' from type %s!", field->str, cosmoV_typeStr(*temp));
|
||||
cosmoV_error(state, "Couldn't get field '%s' from type %s!", field->str,
|
||||
cosmoV_typeStr(*temp));
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -895,7 +957,8 @@ int cosmoV_execute(CState *state) {
|
||||
StkPtr temp = cosmoV_getTop(state, 0); // should be the object/table
|
||||
|
||||
if (!IS_REF(*temp)) {
|
||||
cosmoV_error(state, "Couldn't iterate over non-iterator type %s!", cosmoV_typeStr(*temp));
|
||||
cosmoV_error(state, "Couldn't iterate over non-iterator type %s!",
|
||||
cosmoV_typeStr(*temp));
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -909,18 +972,23 @@ int cosmoV_execute(CState *state) {
|
||||
cosmoV_pop(state); // pop the object from the stack
|
||||
cosmoV_pushValue(state, val);
|
||||
cosmoV_pushRef(state, (CObj *)obj);
|
||||
if (cosmoV_call(state, 1, 1) != COSMOVM_OK) // we expect 1 return value on the stack, the iterable object
|
||||
if (cosmoV_call(state, 1, 1) !=
|
||||
COSMOVM_OK) // we expect 1 return value on the stack, the iterable object
|
||||
return -1;
|
||||
|
||||
StkPtr iObj = cosmoV_getTop(state, 0);
|
||||
|
||||
if (!IS_OBJECT(*iObj)) {
|
||||
cosmoV_error(state, "Expected iterable object! '__iter' returned %s, expected <object>!", cosmoV_typeStr(*iObj));
|
||||
cosmoV_error(
|
||||
state,
|
||||
"Expected iterable object! '__iter' returned %s, expected <object>!",
|
||||
cosmoV_typeStr(*iObj));
|
||||
return -1;
|
||||
}
|
||||
|
||||
// get __next method and place it at the top of the stack
|
||||
cosmoV_getMethod(state, cosmoV_readRef(*iObj), cosmoV_newRef(state->iStrings[ISTRING_NEXT]), iObj);
|
||||
cosmoV_getMethod(state, cosmoV_readRef(*iObj),
|
||||
cosmoV_newRef(state->iStrings[ISTRING_NEXT]), iObj);
|
||||
} else {
|
||||
cosmoV_error(state, "Expected iterable object! '__iter' not defined!");
|
||||
return -1;
|
||||
@ -944,7 +1012,8 @@ int cosmoV_execute(CState *state) {
|
||||
cosmoV_setTop(state, 2); // pops the object & the tbl
|
||||
cosmoV_pushRef(state, (CObj *)method); // pushes the method for OP_NEXT
|
||||
} else {
|
||||
cosmoV_error(state, "No proto defined! Couldn't get from type %s", cosmoO_typeStr(obj));
|
||||
cosmoV_error(state, "No proto defined! Couldn't get from type %s",
|
||||
cosmoO_typeStr(obj));
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -956,7 +1025,8 @@ int cosmoV_execute(CState *state) {
|
||||
StkPtr temp = cosmoV_getTop(state, 0); // we don't actually pop this off the stack
|
||||
|
||||
if (!IS_METHOD(*temp)) {
|
||||
cosmoV_error(state, "Expected '__next' to be a method, got type %s!", cosmoV_typeStr(*temp));
|
||||
cosmoV_error(state, "Expected '__next' to be a method, got type %s!",
|
||||
cosmoV_typeStr(*temp));
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -964,7 +1034,8 @@ int cosmoV_execute(CState *state) {
|
||||
if (cosmoV_call(state, 0, nresults) != COSMOVM_OK)
|
||||
return -1;
|
||||
|
||||
if (IS_NIL(*(cosmoV_getTop(state, 0)))) { // __next returned a nil, which means to exit the loop
|
||||
if (IS_NIL(*(cosmoV_getTop(
|
||||
state, 0)))) { // __next returned a nil, which means to exit the loop
|
||||
cosmoV_setTop(state, nresults); // pop the return values
|
||||
frame->pc += jump;
|
||||
}
|
||||
@ -991,9 +1062,11 @@ int cosmoV_execute(CState *state) {
|
||||
StkPtr valB = cosmoV_getTop(state, 0);
|
||||
if (IS_NUMBER(*valA) && IS_NUMBER(*valB)) {
|
||||
cosmoV_setTop(state, 2); /* pop the 2 values */
|
||||
cosmoV_pushValue(state, cosmoV_newNumber(fmod(cosmoV_readNumber(*valA), cosmoV_readNumber(*valB))));
|
||||
cosmoV_pushValue(state, cosmoV_newNumber(fmod(cosmoV_readNumber(*valA),
|
||||
cosmoV_readNumber(*valB))));
|
||||
} else {
|
||||
cosmoV_error(state, "Expected numbers, got %s and %s!", cosmoV_typeStr(*valA), cosmoV_typeStr(*valB));
|
||||
cosmoV_error(state, "Expected numbers, got %s and %s!", cosmoV_typeStr(*valA),
|
||||
cosmoV_typeStr(*valB));
|
||||
return -1;
|
||||
}
|
||||
continue;
|
||||
@ -1003,9 +1076,11 @@ int cosmoV_execute(CState *state) {
|
||||
StkPtr valB = cosmoV_getTop(state, 0);
|
||||
if (IS_NUMBER(*valA) && IS_NUMBER(*valB)) {
|
||||
cosmoV_setTop(state, 2); /* pop the 2 values */
|
||||
cosmoV_pushValue(state, cosmoV_newNumber(pow(cosmoV_readNumber(*valA), cosmoV_readNumber(*valB))));
|
||||
cosmoV_pushValue(state, cosmoV_newNumber(pow(cosmoV_readNumber(*valA),
|
||||
cosmoV_readNumber(*valB))));
|
||||
} else {
|
||||
cosmoV_error(state, "Expected numbers, got %s and %s!", cosmoV_typeStr(*valA), cosmoV_typeStr(*valB));
|
||||
cosmoV_error(state, "Expected numbers, got %s and %s!", cosmoV_typeStr(*valA),
|
||||
cosmoV_typeStr(*valB));
|
||||
return -1;
|
||||
}
|
||||
continue;
|
||||
@ -1119,7 +1194,8 @@ int cosmoV_execute(CState *state) {
|
||||
cosmoV_pushValue(state, val); // pushes old value onto the stack :)
|
||||
|
||||
// call __newindex
|
||||
if (!cosmoO_newIndexObject(state, proto, *key, cosmoV_newNumber(cosmoV_readNumber(val) + inc)))
|
||||
if (!cosmoO_newIndexObject(state, proto, *key,
|
||||
cosmoV_newNumber(cosmoV_readNumber(val) + inc)))
|
||||
return -1;
|
||||
} else
|
||||
return -1; // cosmoO_indexObject failed and threw an error
|
||||
@ -1137,7 +1213,8 @@ int cosmoV_execute(CState *state) {
|
||||
cosmoV_pushValue(state, *val); // pushes old value onto the stack :)
|
||||
*val = cosmoV_newNumber(cosmoV_readNumber(*val) + inc); // sets table index
|
||||
} else {
|
||||
cosmoV_error(state, "No proto defined! Couldn't __index from type %s", cosmoV_typeStr(*temp));
|
||||
cosmoV_error(state, "No proto defined! Couldn't __index from type %s",
|
||||
cosmoV_typeStr(*temp));
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -1163,7 +1240,8 @@ int cosmoV_execute(CState *state) {
|
||||
// check that it's a number value
|
||||
if (IS_NUMBER(val)) {
|
||||
cosmoV_pushValue(state, val); // pushes old value onto the stack :)
|
||||
if (!cosmoV_rawset(state, obj, ident, cosmoV_newNumber(cosmoV_readNumber(val) + inc)))
|
||||
if (!cosmoV_rawset(state, obj, ident,
|
||||
cosmoV_newNumber(cosmoV_readNumber(val) + inc)))
|
||||
return -1;
|
||||
} else {
|
||||
cosmoV_error(state, "Expected number, got %s!", cosmoV_typeStr(val));
|
||||
@ -1201,9 +1279,15 @@ int cosmoV_execute(CState *state) {
|
||||
NUMBEROP(cosmoV_newBoolean, <=)
|
||||
continue;
|
||||
}
|
||||
case OP_TRUE: cosmoV_pushBoolean(state, true); continue;
|
||||
case OP_FALSE: cosmoV_pushBoolean(state, false); continue;
|
||||
case OP_NIL: cosmoV_pushValue(state, cosmoV_newNil()); continue;
|
||||
case OP_TRUE:
|
||||
cosmoV_pushBoolean(state, true);
|
||||
continue;
|
||||
case OP_FALSE:
|
||||
cosmoV_pushBoolean(state, false);
|
||||
continue;
|
||||
case OP_NIL:
|
||||
cosmoV_pushValue(state, cosmoV_newNil());
|
||||
continue;
|
||||
case OP_RETURN: {
|
||||
uint8_t res = READBYTE();
|
||||
return res;
|
||||
|
60
src/cvm.h
60
src/cvm.h
@ -1,14 +1,15 @@
|
||||
#ifndef COSMOVM_H
|
||||
#define COSMOVM_H
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "cosmo.h"
|
||||
#include "cstate.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
// #define VM_DEBUG
|
||||
|
||||
typedef enum {
|
||||
typedef enum
|
||||
{
|
||||
COSMOVM_OK,
|
||||
COSMOVM_RUNTIME_ERR,
|
||||
COSMOVM_BUILDTIME_ERR
|
||||
@ -29,15 +30,16 @@ COSMO_API void cosmoV_error(CState *state, const char *format, ...);
|
||||
COSMO_API void cosmo_insert(CState *state, int indx, CValue val);
|
||||
|
||||
/*
|
||||
Sets the default proto objects for the passed objType. Also walks through the object heap and updates protos
|
||||
for the passed objType if that CObj* has no proto.
|
||||
Sets the default proto objects for the passed objType. Also walks through the object heap and
|
||||
updates protos for the passed objType if that CObj* has no proto.
|
||||
|
||||
returns true if replacing a previously registered proto object for this type
|
||||
*/
|
||||
COSMO_API bool cosmoV_registerProtoObject(CState *state, CObjType objType, CObjObject *obj);
|
||||
|
||||
/*
|
||||
compiles string into a <closure>, if successful, <closure> will be pushed onto the stack otherwise the <error> will be pushed.
|
||||
compiles string into a <closure>, if successful, <closure> will be pushed onto the stack
|
||||
otherwise the <error> will be pushed.
|
||||
|
||||
returns:
|
||||
false : <error> is at the top of the stack
|
||||
@ -48,28 +50,32 @@ COSMO_API bool cosmoV_compileString(CState *state, const char *src, const char *
|
||||
/*
|
||||
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);
|
||||
|
||||
/*
|
||||
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);
|
||||
|
||||
// wraps the closure into a CObjMethod, so the function is called as an invoked method
|
||||
COSMO_API bool cosmoV_getMethod(CState *state, CObj *obj, CValue key, CValue *val);
|
||||
|
||||
// clears the stack, callstack and restores the state into a usable state after a calloverflow or another hard to recover error
|
||||
// (keeps the global table intact)
|
||||
// clears the stack, callstack and restores the state into a usable state after a calloverflow or
|
||||
// another hard to recover error (keeps the global table intact)
|
||||
COSMO_API bool cosmoV_restore(CState *state);
|
||||
|
||||
// nice to have wrappers
|
||||
|
||||
// pushes a raw CValue to the stack, might throw an error if the stack is overflowed (with the SAFE_STACK macro on)
|
||||
static inline void cosmoV_pushValue(CState *state, CValue val) {
|
||||
// pushes a raw CValue to the stack, might throw an error if the stack is overflowed (with the
|
||||
// SAFE_STACK macro on)
|
||||
static inline void cosmoV_pushValue(CState *state, CValue val)
|
||||
{
|
||||
#ifdef SAFE_STACK
|
||||
ptrdiff_t stackSize = state->top - state->stack;
|
||||
|
||||
@ -91,51 +97,61 @@ static inline void cosmoV_pushValue(CState *state, CValue val) {
|
||||
}
|
||||
|
||||
// sets stack->top to stack->top - indx
|
||||
static inline StkPtr cosmoV_setTop(CState *state, int indx) {
|
||||
static inline StkPtr cosmoV_setTop(CState *state, int indx)
|
||||
{
|
||||
state->top -= indx;
|
||||
return state->top;
|
||||
}
|
||||
|
||||
// returns stack->top - indx - 1
|
||||
static inline StkPtr cosmoV_getTop(CState *state, int indx) {
|
||||
static inline StkPtr cosmoV_getTop(CState *state, int indx)
|
||||
{
|
||||
return &state->top[-(indx + 1)];
|
||||
}
|
||||
|
||||
// pops 1 value off the stack, returns the popped value
|
||||
static inline StkPtr cosmoV_pop(CState *state) {
|
||||
static inline StkPtr cosmoV_pop(CState *state)
|
||||
{
|
||||
return cosmoV_setTop(state, 1);
|
||||
}
|
||||
|
||||
// pushes a cosmo_Number to the stack
|
||||
static inline void cosmoV_pushNumber(CState *state, cosmo_Number num) {
|
||||
static inline void cosmoV_pushNumber(CState *state, cosmo_Number num)
|
||||
{
|
||||
cosmoV_pushValue(state, cosmoV_newNumber(num));
|
||||
}
|
||||
|
||||
// pushes a boolean to the stack
|
||||
static inline void cosmoV_pushBoolean(CState *state, bool b) {
|
||||
static inline void cosmoV_pushBoolean(CState *state, bool b)
|
||||
{
|
||||
cosmoV_pushValue(state, cosmoV_newBoolean(b));
|
||||
}
|
||||
|
||||
static inline void cosmoV_pushRef(CState *state, CObj *obj) {
|
||||
static inline void cosmoV_pushRef(CState *state, CObj *obj)
|
||||
{
|
||||
cosmoV_pushValue(state, cosmoV_newRef(obj));
|
||||
}
|
||||
|
||||
// pushes a C Function to the stack
|
||||
static inline void cosmoV_pushCFunction(CState *state, CosmoCFunction func) {
|
||||
static inline void cosmoV_pushCFunction(CState *state, CosmoCFunction func)
|
||||
{
|
||||
cosmoV_pushRef(state, (CObj *)cosmoO_newCFunction(state, func));
|
||||
}
|
||||
|
||||
// len is the length of the string without the NULL terminator
|
||||
static inline void cosmoV_pushLString(CState *state, const char *str, size_t len) {
|
||||
static inline void cosmoV_pushLString(CState *state, const char *str, size_t len)
|
||||
{
|
||||
cosmoV_pushRef(state, (CObj *)cosmoO_copyString(state, str, len));
|
||||
}
|
||||
|
||||
// accepts a null terminated string and copys the buffer to the VM heap
|
||||
static inline void cosmoV_pushString(CState *state, const char *str) {
|
||||
static inline void cosmoV_pushString(CState *state, const char *str)
|
||||
{
|
||||
cosmoV_pushLString(state, str, strlen(str));
|
||||
}
|
||||
|
||||
static inline void cosmoV_pushNil(CState *state) {
|
||||
static inline void cosmoV_pushNil(CState *state)
|
||||
{
|
||||
cosmoV_pushValue(state, cosmoV_newNil());
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user