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