mirror of
https://github.com/CPunch/Cosmo.git
synced 2024-11-22 07:20:05 +00:00
added clang-format
This commit is contained in:
parent
517b0b9532
commit
7279623e24
26
.clang-format
Normal file
26
.clang-format
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
---
|
||||||
|
Language: Cpp
|
||||||
|
# BasedOnStyle: Mozilla
|
||||||
|
AccessModifierOffset: -2
|
||||||
|
AlignAfterOpenBracket: Align
|
||||||
|
AlignArrayOfStructures: Right
|
||||||
|
AlignConsecutiveMacros: AcrossEmptyLinesAndComments
|
||||||
|
AlignConsecutiveAssignments: None
|
||||||
|
AlignConsecutiveBitFields: None
|
||||||
|
AlignConsecutiveDeclarations: None
|
||||||
|
AlignEscapedNewlines: Right
|
||||||
|
AlignOperands: Align
|
||||||
|
AlignTrailingComments: true
|
||||||
|
AllowAllArgumentsOnNextLine: true
|
||||||
|
AllowShortEnumsOnASingleLine: false
|
||||||
|
AllowShortFunctionsOnASingleLine: None
|
||||||
|
AllowShortBlocksOnASingleLine: Never
|
||||||
|
AllowShortIfStatementsOnASingleLine: Never
|
||||||
|
AlwaysBreakAfterReturnType: None
|
||||||
|
BreakBeforeBraces: Mozilla
|
||||||
|
IndentWidth: 4
|
||||||
|
ColumnLimit: 100
|
||||||
|
IncludeBlocks: Regroup
|
||||||
|
IndentPPDirectives: AfterHash
|
||||||
|
...
|
||||||
|
|
@ -9,7 +9,7 @@ proto Test
|
|||||||
end
|
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
|
303
src/cbaselib.c
303
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;
|
||||||
@ -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,7 +93,8 @@ 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;
|
||||||
@ -95,7 +104,8 @@ int cosmoB_tostring(CState *state, int nargs, CValue *args) {
|
|||||||
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,28 +140,13 @@ 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++) {
|
||||||
@ -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,7 +180,8 @@ 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;
|
||||||
@ -193,14 +192,16 @@ int cosmoB_ogetProto(CState *state, int nargs, CValue *args) {
|
|||||||
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");
|
||||||
@ -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,18 +334,11 @@ 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");
|
||||||
|
|
||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -588,26 +604,12 @@ int cosmoB_sRep(CState *state, int nargs, CValue *args) {
|
|||||||
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");
|
||||||
@ -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,34 +820,14 @@ 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");
|
||||||
@ -852,24 +846,28 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -879,14 +877,16 @@ int cosmoB_vsetGlobal(CState *state, int nargs, CValue *args) {
|
|||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -905,14 +905,17 @@ int cosmoB_vindexBProto(CState *state, int nargs, CValue *args) {
|
|||||||
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);
|
||||||
|
29
src/cchunk.c
29
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,7 +69,8 @@ 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);
|
||||||
|
|
||||||
|
12
src/cchunk.h
12
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
|
||||||
@ -26,11 +26,13 @@ void writeu8Chunk(CState* state, CChunk *chunk, INSTRUCTION i, int line);
|
|||||||
void writeu16Chunk(CState *state, CChunk *chunk, uint16_t i, int line);
|
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]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
51
src/cdebug.c
51
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);
|
||||||
|
|
||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
178
src/clex.c
178
src/clex.c
@ -1,4 +1,5 @@
|
|||||||
#include "clex.h"
|
#include "clex.h"
|
||||||
|
|
||||||
#include "cmem.h"
|
#include "cmem.h"
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@ -29,44 +30,52 @@ CReservedWord reservedWords[] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 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,7 +111,8 @@ 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;
|
||||||
@ -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,7 +389,8 @@ 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;
|
||||||
@ -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
|
||||||
|
76
src/cmem.c
76
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
|
||||||
@ -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,7 +95,8 @@ 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:
|
||||||
@ -152,7 +161,8 @@ void blackenObject(CState *state, CObj *obj) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void markObject(CState *state, CObj *obj) {
|
void markObject(CState *state, CObj *obj)
|
||||||
|
{
|
||||||
if (obj == NULL || obj->isMarked) // skip if NULL or already marked
|
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);
|
||||||
@ -254,7 +270,8 @@ void markRoots(CState *state) {
|
|||||||
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;
|
||||||
|
|
||||||
|
19
src/cmem.h
19
src/cmem.h
@ -2,7 +2,6 @@
|
|||||||
#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
|
||||||
@ -14,7 +13,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) \
|
||||||
@ -33,12 +33,10 @@
|
|||||||
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
|
||||||
@ -53,8 +51,7 @@
|
|||||||
#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) \
|
||||||
@ -64,7 +61,8 @@
|
|||||||
#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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
225
src/cobj.c
225
src/cobj.c
@ -1,16 +1,18 @@
|
|||||||
|
#include "cobj.h"
|
||||||
|
|
||||||
|
#include "clex.h"
|
||||||
|
#include "cmem.h"
|
||||||
#include "cstate.h"
|
#include "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;
|
||||||
|
|
||||||
@ -20,7 +22,8 @@ uint32_t hashString(const char *str, size_t sz) {
|
|||||||
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;
|
||||||
@ -36,7 +39,8 @@ 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);
|
||||||
@ -98,15 +102,18 @@ void cosmoO_free(CState *state, CObj *obj) {
|
|||||||
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,8 +123,9 @@ 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;
|
||||||
}
|
}
|
||||||
@ -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,7 +190,8 @@ _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
|
||||||
@ -188,14 +204,16 @@ CObjObject *cosmoO_newObject(CState *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)
|
||||||
@ -206,8 +224,10 @@ CObjTable *cosmoO_newTable(CState *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,13 +238,16 @@ 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;
|
||||||
@ -240,14 +263,16 @@ 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);
|
||||||
|
|
||||||
@ -255,7 +280,8 @@ CObjClosure *cosmoO_newClosure(CState *state, CObjFunction *func) {
|
|||||||
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,7 +289,8 @@ 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();
|
||||||
@ -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,29 +315,34 @@ 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
|
||||||
|
// *want* a GC event to happen)
|
||||||
cosmoV_pushRef(state, (CObj *)strObj);
|
cosmoV_pushRef(state, (CObj *)strObj);
|
||||||
cosmoT_insert(state, &state->strings, cosmoV_newRef((CObj *)strObj));
|
cosmoT_insert(state, &state->strings, cosmoV_newRef((CObj *)strObj));
|
||||||
cosmoV_pop(state);
|
cosmoV_pop(state);
|
||||||
@ -317,7 +350,8 @@ CObjString *cosmoO_allocateString(CState *state, const char *str, size_t sz, uin
|
|||||||
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,12 +396,14 @@ CObjString *cosmoO_pushVFString(CState *state, const char *format, va_list args)
|
|||||||
}
|
}
|
||||||
|
|
||||||
cosmoV_pushString(state, format); // push the rest of the string
|
cosmoV_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) {
|
||||||
@ -382,9 +418,12 @@ 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
|
||||||
@ -393,7 +432,8 @@ bool cosmoO_getRawObject(CState *state, CObjObject *proto, CValue key, CValue *v
|
|||||||
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,7 +454,8 @@ 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
|
||||||
@ -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,7 +541,8 @@ bool cosmoO_getIString(CState *state, CObjObject *object, int flag, CValue *val)
|
|||||||
return false; // obj->proto was false, the istring doesn't exist in this object chain
|
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
|
||||||
@ -505,7 +558,8 @@ 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)) {
|
||||||
@ -521,7 +575,8 @@ 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;
|
||||||
|
|
||||||
@ -535,7 +590,8 @@ CObjString *cosmoO_toString(CState *state, CObj *obj) {
|
|||||||
// 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -554,12 +610,14 @@ CObjString *cosmoO_toString(CState *state, CObj *obj) {
|
|||||||
}
|
}
|
||||||
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: {
|
||||||
@ -584,7 +642,8 @@ CObjString *cosmoO_toString(CState *state, CObj *obj) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cosmo_Number cosmoO_toNumber(CState *state, CObj *obj) {
|
cosmo_Number cosmoO_toNumber(CState *state, CObj *obj)
|
||||||
|
{
|
||||||
CObjObject *proto = cosmoO_grabProto(obj);
|
CObjObject *proto = cosmoO_grabProto(obj);
|
||||||
CValue res;
|
CValue res;
|
||||||
|
|
||||||
@ -596,7 +655,8 @@ cosmo_Number cosmoO_toNumber(CState *state, CObj *obj) {
|
|||||||
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -651,7 +714,8 @@ 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;
|
||||||
@ -709,16 +773,25 @@ void printObject(CObj *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:
|
||||||
|
67
src/cobj.h
67
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;
|
||||||
@ -134,12 +148,14 @@ struct CObjUpval {
|
|||||||
|
|
||||||
#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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -160,7 +176,8 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -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
|
||||||
|
@ -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]]
|
||||||
|
15
src/cosmo.h
15
src/cosmo.h
@ -1,18 +1,18 @@
|
|||||||
#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
|
||||||
@ -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
|
||||||
|
480
src/cparse.c
480
src/cparse.c
File diff suppressed because it is too large
Load Diff
@ -1,10 +1,11 @@
|
|||||||
#ifndef CPARSE_H
|
#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
|
||||||
|
// the stack
|
||||||
CObjFunction *cosmoP_compileString(CState *state, const char *source, const char *module);
|
CObjFunction *cosmoP_compileString(CState *state, const char *source, const char *module);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
19
src/cstate.c
19
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
|
||||||
@ -100,15 +103,16 @@ void cosmoV_freeState(CState *state) {
|
|||||||
// TODO: yeah idk, it looks like im missing 520 bytes somewhere? i'll look into it later
|
// 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));
|
||||||
|
28
src/cstate.h
28
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
|
||||||
};
|
};
|
||||||
|
89
src/ctable.c
89
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,11 +57,13 @@ void cosmoT_addTable(CState *state, CTable *from, CTable *to) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void cosmoT_clearTable(CState *state, CTable *tbl) {
|
void cosmoT_clearTable(CState *state, CTable *tbl)
|
||||||
|
{
|
||||||
cosmoM_freearray(state, CTableEntry, tbl->table, (tbl->capacityMask + 1));
|
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;
|
||||||
@ -64,7 +72,8 @@ uint32_t getObjectHash(CObj *obj) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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));
|
||||||
@ -76,7 +85,8 @@ uint32_t getValueHash(CValue *val) {
|
|||||||
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,7 +265,8 @@ CObjString *cosmoT_lookupString(CTable *tbl, const char *str, int length, uint32
|
|||||||
// check if it's an empty slot (meaning we dont have it in the table)
|
// 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);
|
||||||
}
|
}
|
||||||
@ -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;
|
||||||
|
58
src/cvalue.c
58
src/cvalue.c
@ -1,40 +1,50 @@
|
|||||||
#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];
|
||||||
@ -42,7 +52,8 @@ CObjString *cosmoV_toString(CState *state, CValue 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,7 +66,8 @@ CObjString *cosmoV_toString(CState *state, CValue val) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cosmo_Number cosmoV_toNumber(CState *state, CValue val) {
|
cosmo_Number cosmoV_toNumber(CState *state, CValue val)
|
||||||
|
{
|
||||||
switch (GET_TYPE(val)) {
|
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));
|
||||||
|
25
src/cvalue.h
25
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,7 +28,8 @@ 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;
|
||||||
};
|
};
|
||||||
@ -43,7 +45,8 @@ union CValue {
|
|||||||
# 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))
|
||||||
@ -68,9 +71,11 @@ union CValue {
|
|||||||
/*
|
/*
|
||||||
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;
|
||||||
@ -103,7 +108,8 @@ struct CValue {
|
|||||||
|
|
||||||
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
|
||||||
|
254
src/cvm.c
254
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,7 +18,8 @@ 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
|
||||||
@ -28,7 +30,8 @@ COSMO_API void cosmo_insert(CState *state, int indx, CValue 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,7 +39,8 @@ 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
|
||||||
|
// location put our closure
|
||||||
cosmoV_pushRef(state, (CObj *)func);
|
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;
|
||||||
@ -48,7 +52,8 @@ COSMO_API bool cosmoV_compileString(CState *state, const char *src, const char *
|
|||||||
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,15 +175,19 @@ 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);
|
||||||
|
|
||||||
@ -197,21 +217,24 @@ 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;
|
||||||
@ -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,13 +261,16 @@ static bool callCFunction(CState *state, CosmoCFunction cfunc, int args, int nre
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
calls a raw closure object with # args on the stack, nresults are pushed onto the stack upon return.
|
calls a raw closure object with # args on the stack, nresults are pushed onto the stack upon
|
||||||
|
return.
|
||||||
|
|
||||||
returns:
|
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
|
||||||
@ -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
|
||||||
@ -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);
|
||||||
@ -343,7 +373,8 @@ bool callCValue(CState *state, CValue func, int args, int nresults, int offset)
|
|||||||
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,7 +388,8 @@ 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);
|
||||||
@ -365,8 +397,10 @@ bool invokeMethod(CState* state, CObj *obj, CValue func, int args, int nresults,
|
|||||||
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)) {
|
||||||
@ -388,23 +422,27 @@ COSMOVMRESULT cosmoV_pcall(CState *state, int args, int nresults) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
calls a callable object at stack->top - args - 1, passing the # of args to the callable, and ensuring nresults are returned
|
calls a callable object at stack->top - args - 1, passing the # of args to the callable, and
|
||||||
|
ensuring nresults are returned
|
||||||
|
|
||||||
returns:
|
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
|
||||||
@ -424,7 +462,8 @@ COSMO_API CObjObject* cosmoV_makeObject(CState *state, int pairs) {
|
|||||||
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,7 +480,8 @@ 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
|
||||||
@ -460,13 +500,15 @@ COSMO_API void cosmoV_makeTable(CState *state, int pairs) {
|
|||||||
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;
|
||||||
}
|
}
|
||||||
@ -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,7 +581,8 @@ 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;
|
||||||
|
|
||||||
@ -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,7 +617,8 @@ 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);
|
||||||
@ -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,12 +648,14 @@ 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 :)
|
||||||
|
|
||||||
@ -613,7 +665,8 @@ int cosmoV_execute(CState *state) {
|
|||||||
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()) {
|
||||||
@ -736,7 +789,8 @@ int cosmoV_execute(CState *state) {
|
|||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -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,7 +847,8 @@ 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;
|
||||||
@ -799,7 +856,8 @@ int cosmoV_execute(CState *state) {
|
|||||||
|
|
||||||
*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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -909,18 +972,23 @@ int cosmoV_execute(CState *state) {
|
|||||||
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;
|
||||||
@ -944,7 +1012,8 @@ int cosmoV_execute(CState *state) {
|
|||||||
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,7 +1194,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 :)
|
||||||
|
|
||||||
// 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
|
||||||
@ -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;
|
||||||
|
60
src/cvm.h
60
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"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
// #define VM_DEBUG
|
// #define VM_DEBUG
|
||||||
|
|
||||||
typedef enum {
|
typedef enum
|
||||||
|
{
|
||||||
COSMOVM_OK,
|
COSMOVM_OK,
|
||||||
COSMOVM_RUNTIME_ERR,
|
COSMOVM_RUNTIME_ERR,
|
||||||
COSMOVM_BUILDTIME_ERR
|
COSMOVM_BUILDTIME_ERR
|
||||||
@ -29,15 +30,16 @@ COSMO_API void cosmoV_error(CState *state, const char *format, ...);
|
|||||||
COSMO_API void cosmo_insert(CState *state, int indx, CValue val);
|
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