mirror of
https://github.com/CPunch/Cosmo.git
synced 2025-10-21 00:10:18 +00:00
Compare commits
23 Commits
5169aca6d0
...
error-hand
Author | SHA1 | Date | |
---|---|---|---|
bfde2d25cf | |||
97d40765ce | |||
37e4653d40 | |||
1ae473383d | |||
6ed5589513 | |||
1408a07b23 | |||
27818b3788 | |||
37e42eb60b | |||
cd3047c271 | |||
f26376e6f5 | |||
409937c1fa | |||
1d2ba217af | |||
e28ffe1c6c | |||
9c5270124d | |||
5fc9af5564 | |||
a1c58647ba | |||
5c3e24fc39 | |||
dcf6a09dae | |||
e0faa14b35 | |||
861607d6a8 | |||
10c9f92a06 | |||
8dfd7744c2 | |||
7f7bc7c6ad |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,4 +1,5 @@
|
||||
*.o
|
||||
bin
|
||||
build
|
||||
.vscode
|
||||
CMakeFiles
|
@@ -3,12 +3,13 @@ project(cosmo VERSION 0.1.0 LANGUAGES C)
|
||||
|
||||
set(CMAKE_C_STANDARD 99)
|
||||
set(CMAKE_C_STANDARD_REQUIRED True)
|
||||
set(CMAKE_DISABLE_SOURCE_CHANGES ON CACHE BOOL "Prevent writing files to CMAKE_SOURCE_DIR under configure")
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY bin)
|
||||
|
||||
set (CMAKE_C_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g -Wall")
|
||||
set (CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG} -fsanitize=address")
|
||||
set (CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O3 -Wall")
|
||||
IF (NOT WIN32)
|
||||
set (CMAKE_C_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g")
|
||||
set (CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG} -fsanitize=address")
|
||||
set (CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O3 -Wall")
|
||||
ENDIF()
|
||||
|
||||
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT cosmo)
|
||||
|
||||
@@ -17,6 +18,10 @@ include(FetchContent)
|
||||
file(GLOB sources CONFIGURE_DEPENDS ${PROJECT_SOURCE_DIR}/src/*.c)
|
||||
add_executable(${PROJECT_NAME} main.c)
|
||||
target_sources(${PROJECT_NAME} PRIVATE ${sources})
|
||||
target_link_libraries(${PROJECT_NAME} m)
|
||||
|
||||
IF (NOT WIN32)
|
||||
target_link_libraries(${PROJECT_NAME} m)
|
||||
ENDIF()
|
||||
|
||||
target_include_directories(${PROJECT_NAME} PUBLIC ${PROJECT_SOURCE_DIR}/src)
|
||||
target_compile_features(${PROJECT_NAME} PRIVATE c_std_99)
|
||||
|
2
Makefile
2
Makefile
@@ -1,7 +1,7 @@
|
||||
# make clean && make && ./bin/cosmo
|
||||
|
||||
CC=clang
|
||||
CFLAGS=-fPIE -Wall -O3 -Isrc -std=c99
|
||||
CFLAGS=-fPIE -Wall -Isrc -O3 -std=c99 #-g -fsanitize=address
|
||||
LDFLAGS=-lm #-fsanitize=address
|
||||
OUT=bin/cosmo
|
||||
|
||||
|
@@ -6,7 +6,7 @@ end
|
||||
// instance of test
|
||||
let obj = test()
|
||||
|
||||
test.__index = function(self, key)
|
||||
test.__index = func(self, key)
|
||||
print("__index called!")
|
||||
if (key == "lol") then
|
||||
return 9001
|
||||
|
43
main.c
43
main.c
@@ -10,8 +10,11 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <getopt.h>
|
||||
#ifdef _WIN32
|
||||
# include "util/getopt.h"
|
||||
#else
|
||||
# include <getopt.h>
|
||||
#endif
|
||||
|
||||
static bool _ACTIVE = false;
|
||||
|
||||
@@ -42,22 +45,20 @@ int cosmoB_input(CState *state, int nargs, CValue *args)
|
||||
|
||||
static bool interpret(CState *state, const char *script, const char *mod)
|
||||
{
|
||||
bool ret;
|
||||
|
||||
// cosmoV_compileString pushes the result onto the stack (COBJ_ERROR or COBJ_CLOSURE)
|
||||
if (cosmoV_compileString(state, script, mod)) {
|
||||
COSMOVMRESULT res = cosmoV_call(state, 0, 0); // 0 args being passed, 0 results expected
|
||||
|
||||
if (res == COSMOVM_RUNTIME_ERR)
|
||||
cosmoV_printError(state, state->error);
|
||||
// 0 args being passed, 0 results expected
|
||||
if (!cosmoV_pcall(state, 0, 0)) {
|
||||
cosmoV_printError(state, cosmoV_readError(*cosmoV_pop(state)));
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
cosmoV_pop(state); // pop the error off the stack
|
||||
cosmoV_printError(state, state->error);
|
||||
cosmoV_printError(state, cosmoV_readError(*cosmoV_pop(state)));
|
||||
return false;
|
||||
}
|
||||
|
||||
ret = state->panic;
|
||||
state->panic = false; // so our repl isn't broken
|
||||
return !ret;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void repl(CState *state)
|
||||
@@ -156,8 +157,7 @@ void compileScript(CState *state, const char *in, const char *out)
|
||||
CObjFunction *func = cosmoV_readClosure(*cosmoV_getTop(state, 0))->function;
|
||||
cosmoD_dump(state, func, fileWriter, (void *)fout);
|
||||
} else {
|
||||
cosmoV_pop(state); // pop the error off the stack
|
||||
cosmoV_printError(state, state->error);
|
||||
cosmoV_printError(state, cosmoV_readError(*cosmoV_pop(state)));
|
||||
}
|
||||
|
||||
free(script);
|
||||
@@ -168,20 +168,15 @@ void compileScript(CState *state, const char *in, const char *out)
|
||||
|
||||
void loadScript(CState *state, const char *in)
|
||||
{
|
||||
FILE *file;
|
||||
|
||||
file = fopen(in, "rb");
|
||||
FILE *file = fopen(in, "rb");
|
||||
if (!cosmoV_undump(state, fileReader, file)) {
|
||||
cosmoV_pop(state); // pop the error off the stack
|
||||
cosmoV_printError(state, state->error);
|
||||
cosmoV_printError(state, cosmoV_readError(*cosmoV_pop(state)));
|
||||
return;
|
||||
};
|
||||
|
||||
printf("[!] loaded %s!\n", in);
|
||||
COSMOVMRESULT res = cosmoV_call(state, 0, 0); // 0 args being passed, 0 results expected
|
||||
|
||||
if (res == COSMOVM_RUNTIME_ERR)
|
||||
cosmoV_printError(state, state->error);
|
||||
if (!cosmoV_pcall(state, 0, 0))
|
||||
cosmoV_printError(state, cosmoV_readError(*cosmoV_pop(state)));
|
||||
|
||||
fclose(file);
|
||||
}
|
||||
@@ -200,7 +195,7 @@ int main(int argc, char *const argv[])
|
||||
{
|
||||
CState *state = cosmoV_newState();
|
||||
cosmoB_loadLibrary(state);
|
||||
cosmoB_loadOSLib(state);
|
||||
cosmoB_loadOS(state);
|
||||
cosmoB_loadVM(state);
|
||||
|
||||
int opt;
|
||||
|
56
src/_time.h
Normal file
56
src/_time.h
Normal file
@@ -0,0 +1,56 @@
|
||||
#ifndef COSMO_TIME_H
|
||||
#define COSMO_TIME_H
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
# include <time.h>
|
||||
# include <windows.h>
|
||||
|
||||
# if defined(_MSC_VER) || defined(_MSC_EXTENSIONS)
|
||||
# define DELTA_EPOCH_IN_MICROSECS 116444736000000000Ui64
|
||||
# else
|
||||
# define DELTA_EPOCH_IN_MICROSECS 116444736000000000ULL
|
||||
# endif
|
||||
|
||||
struct timezone
|
||||
{
|
||||
int tz_minuteswest; /* minutes W of Greenwich */
|
||||
int tz_dsttime; /* type of dst correction */
|
||||
};
|
||||
|
||||
int gettimeofday(struct timeval *tv, struct timezone *tz)
|
||||
{
|
||||
FILETIME ft;
|
||||
unsigned __int64 tmpres = 0;
|
||||
static int tzflag;
|
||||
|
||||
if (NULL != tv) {
|
||||
GetSystemTimeAsFileTime(&ft);
|
||||
|
||||
tmpres |= ft.dwHighDateTime;
|
||||
tmpres <<= 32;
|
||||
tmpres |= ft.dwLowDateTime;
|
||||
|
||||
/*converting file time to unix epoch*/
|
||||
tmpres /= 10; /*convert into microseconds*/
|
||||
tmpres -= DELTA_EPOCH_IN_MICROSECS;
|
||||
tv->tv_sec = (long)(tmpres / 1000000UL);
|
||||
tv->tv_usec = (long)(tmpres % 1000000UL);
|
||||
}
|
||||
|
||||
if (NULL != tz) {
|
||||
if (!tzflag) {
|
||||
_tzset();
|
||||
tzflag++;
|
||||
}
|
||||
tz->tz_minuteswest = _timezone / 60;
|
||||
tz->tz_dsttime = _daylight;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
# include <sys/time.h>
|
||||
#endif
|
||||
|
||||
#endif
|
135
src/cbaselib.c
135
src/cbaselib.c
@@ -1,12 +1,13 @@
|
||||
#include "cbaselib.h"
|
||||
|
||||
#include "_time.h"
|
||||
#include "cdebug.h"
|
||||
#include "cmem.h"
|
||||
#include "cobj.h"
|
||||
#include "cvalue.h"
|
||||
#include "cvm.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
// ================================================================ [BASELIB]
|
||||
|
||||
@@ -29,7 +30,6 @@ int cosmoB_assert(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs < 1 || nargs > 2) {
|
||||
cosmoV_error(state, "assert() expected 1 or 2 arguments, got %d!", nargs);
|
||||
return 0; // nothing pushed onto the stack to return
|
||||
}
|
||||
|
||||
if (!IS_BOOLEAN(args[0]) || (nargs == 2 && !IS_STRING(args[1]))) {
|
||||
@@ -39,7 +39,6 @@ int cosmoB_assert(CState *state, int nargs, CValue *args)
|
||||
} else {
|
||||
cosmoV_typeError(state, "assert()", "<boolean>", "%s", cosmoV_typeStr(args[0]));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!cosmoV_readBoolean(args[0])) // expression passed was false, error!
|
||||
@@ -52,7 +51,6 @@ int cosmoB_type(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "type() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// push the type string to the stack
|
||||
@@ -64,20 +62,14 @@ int cosmoB_pcall(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs < 1) {
|
||||
cosmoV_error(state, "pcall() expected at least 1 argument!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// unfreeze the state GC before calling the function
|
||||
cosmoM_unfreezeGC(state);
|
||||
|
||||
// call the passed callable
|
||||
COSMOVMRESULT res = cosmoV_pcall(state, nargs - 1, 1);
|
||||
// call the passed callable, the passed arguments are already in the
|
||||
// proper order lol, so we can just call it
|
||||
bool res = cosmoV_pcall(state, nargs - 1, 1);
|
||||
|
||||
// insert false before the result
|
||||
cosmo_insert(state, 0, cosmoV_newBoolean(res == COSMOVM_OK));
|
||||
|
||||
// refreeze the state GC so it can be properly unfrozen
|
||||
cosmoM_freezeGC(state);
|
||||
cosmo_insert(state, 0, cosmoV_newBoolean(res));
|
||||
return 2;
|
||||
}
|
||||
|
||||
@@ -85,7 +77,6 @@ int cosmoB_tonumber(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "tonumber() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
cosmoV_pushNumber(state, cosmoV_toNumber(state, args[0]));
|
||||
@@ -94,10 +85,8 @@ int cosmoB_tonumber(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);
|
||||
return 0;
|
||||
}
|
||||
|
||||
cosmoV_pushRef(state, (CObj *)cosmoV_toString(state, args[0]));
|
||||
return 1;
|
||||
@@ -107,12 +96,10 @@ int cosmoB_loadstring(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "loadstring() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!IS_STRING(args[0])) {
|
||||
cosmoV_typeError(state, "loadstring()", "<string>", "%s", cosmoV_typeStr(args[0]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
CObjString *str = cosmoV_readString(args[0]);
|
||||
@@ -126,12 +113,10 @@ int cosmoB_error(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "error() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!IS_STRING(args[0])) {
|
||||
cosmoV_typeError(state, "error()", "<string>", "%s", cosmoV_typeStr(args[0]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
cosmoV_error(state, "%s", cosmoV_readCString(args[0]));
|
||||
@@ -180,10 +165,8 @@ int cosmoB_osetProto(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);
|
||||
return 0;
|
||||
}
|
||||
|
||||
cosmoV_pushRef(state, (CObj *)cosmoV_readObject(args[0])->_obj.proto); // just return the proto
|
||||
|
||||
@@ -194,13 +177,11 @@ int cosmoB_oisChild(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 2) {
|
||||
cosmoV_error(state, "object.ischild() expected 2 arguments, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!IS_REF(args[0]) || !IS_OBJECT(args[1])) {
|
||||
cosmoV_typeError(state, "object.ischild()", "<reference obj>, <object>", "%s, %s",
|
||||
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
CObj *obj = cosmoV_readRef(args[0]);
|
||||
@@ -259,12 +240,10 @@ int cosmoB_osRead(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "os.read() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!IS_STRING(args[0])) {
|
||||
cosmoV_typeError(state, "os.read()", "<string>", "%s", cosmoV_typeStr(args[0]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
CObjString *str = cosmoV_readString(args[0]);
|
||||
@@ -305,7 +284,6 @@ int cosmoB_osTime(CState *state, int nargs, CValue *args)
|
||||
struct timeval time;
|
||||
if (nargs > 0) {
|
||||
cosmoV_error(state, "os.time() expected no arguments, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
gettimeofday(&time, NULL);
|
||||
@@ -318,12 +296,10 @@ int cosmoB_osSystem(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "os.system() expects 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!IS_STRING(args[0])) {
|
||||
cosmoV_typeError(state, "os.system()", "<string>", "%s", cosmoV_typeStr(args[0]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
// run the command and return the exit code
|
||||
@@ -331,7 +307,7 @@ int cosmoB_osSystem(CState *state, int nargs, CValue *args)
|
||||
return 1;
|
||||
}
|
||||
|
||||
COSMO_API void cosmoB_loadOSLib(CState *state)
|
||||
COSMO_API void cosmoB_loadOS(CState *state)
|
||||
{
|
||||
const char *identifiers[] = {"read", "time", "system"};
|
||||
|
||||
@@ -358,7 +334,6 @@ int cosmoB_sSub(CState *state, int nargs, CValue *args)
|
||||
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]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
CObjString *str = cosmoV_readString(args[0]);
|
||||
@@ -368,7 +343,6 @@ int cosmoB_sSub(CState *state, int nargs, CValue *args)
|
||||
if (indx < 0 || indx >= str->length) {
|
||||
cosmoV_error(state, "string.sub() expected index to be 0-%d, got %d!", str->length - 1,
|
||||
indx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
cosmoV_pushLString(state, str->str + ((int)indx), str->length - ((int)indx));
|
||||
@@ -377,7 +351,6 @@ int cosmoB_sSub(CState *state, int nargs, CValue *args)
|
||||
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;
|
||||
}
|
||||
|
||||
CObjString *str = cosmoV_readString(args[0]);
|
||||
@@ -389,13 +362,11 @@ int cosmoB_sSub(CState *state, int nargs, CValue *args)
|
||||
cosmoV_error(
|
||||
state, "string.sub() expected subbed string goes out of bounds, max length is %d!",
|
||||
str->length);
|
||||
return 0;
|
||||
}
|
||||
|
||||
cosmoV_pushLString(state, str->str + ((int)indx), ((int)length));
|
||||
} else {
|
||||
cosmoV_error(state, "string.sub() expected 2 or 3 arguments, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
@@ -408,7 +379,6 @@ int cosmoB_sFind(CState *state, int nargs, CValue *args)
|
||||
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]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
CObjString *str = cosmoV_readString(args[0]);
|
||||
@@ -423,13 +393,12 @@ int cosmoB_sFind(CState *state, int nargs, CValue *args)
|
||||
}
|
||||
|
||||
// success! push the index
|
||||
cosmoV_pushNumber(state, indx - str->str);
|
||||
cosmoV_pushNumber(state, (cosmo_Number)(indx - str->str));
|
||||
} else if (nargs == 3) {
|
||||
if (!IS_STRING(args[0]) || !IS_STRING(args[1]) || !IS_NUMBER(args[2])) {
|
||||
cosmoV_typeError(state, "string.find()", "<string>, <string>, <number>", "%s, %s, %s",
|
||||
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]),
|
||||
cosmoV_typeStr(args[2]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
CObjString *str = cosmoV_readString(args[0]);
|
||||
@@ -445,10 +414,9 @@ int cosmoB_sFind(CState *state, int nargs, CValue *args)
|
||||
}
|
||||
|
||||
// success! push the index
|
||||
cosmoV_pushNumber(state, indx - str->str);
|
||||
cosmoV_pushNumber(state, (cosmo_Number)(indx - str->str));
|
||||
} else {
|
||||
cosmoV_error(state, "string.find() expected 2 or 3 arguments, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
@@ -459,13 +427,11 @@ int cosmoB_sSplit(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 2) {
|
||||
cosmoV_error(state, "string.split() expected 2 arguments, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!IS_STRING(args[0]) || !IS_STRING(args[1])) {
|
||||
cosmoV_typeError(state, "string.split()", "<string>, <string>", "%s, %s",
|
||||
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
CObjString *str = cosmoV_readString(args[0]);
|
||||
@@ -496,12 +462,10 @@ int cosmoB_sByte(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "string.byte() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!IS_STRING(args[0])) {
|
||||
cosmoV_typeError(state, "string.byte", "<string>", "%s", cosmoV_typeStr(args[0]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
CObjString *str = cosmoV_readString(args[0]);
|
||||
@@ -522,12 +486,10 @@ int cosmoB_sChar(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "string.char() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!IS_NUMBER(args[0])) {
|
||||
cosmoV_typeError(state, "string.char", "<number>", "%s", cosmoV_typeStr(args[0]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
// small side effect of truncating the number, but ignoring the decimal instead of throwing an
|
||||
@@ -537,7 +499,6 @@ int cosmoB_sChar(CState *state, int nargs, CValue *args)
|
||||
|
||||
if (num > 255 || num < 0) {
|
||||
cosmoV_error(state, "Character expected to be in range 0-255, got %d!", num);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// basically, treat the character value on the C stack as an """"array"""" with a length of 1
|
||||
@@ -549,15 +510,13 @@ int cosmoB_sLen(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs < 1) {
|
||||
cosmoV_error(state, "string.len() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!IS_STRING(args[0])) {
|
||||
cosmoV_typeError(state, "string.len", "<string>", "%s", cosmoV_typeStr(args[0]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
cosmoV_pushNumber(state, strlen(cosmoV_readCString(args[0])));
|
||||
cosmoV_pushNumber(state, (cosmo_Number)strlen(cosmoV_readCString(args[0])));
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -566,14 +525,12 @@ int cosmoB_sRep(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 2) {
|
||||
cosmoV_error(state, "string.rep() expected 2 arguments, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// expects <string>, <number>
|
||||
if (!IS_STRING(args[0]) || !IS_NUMBER(args[1])) {
|
||||
cosmoV_typeError(state, "string.rep", "<string>, <number>", "%s, %s",
|
||||
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
CObjString *str = cosmoV_readString(args[0]);
|
||||
@@ -631,12 +588,10 @@ int cosmoB_mAbs(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "math.abs() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!IS_NUMBER(args[0])) {
|
||||
cosmoV_typeError(state, "math.abs", "<number>", "%s", cosmoV_typeStr(args[0]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
cosmoV_pushNumber(state, fabs(cosmoV_readNumber(args[0])));
|
||||
@@ -648,12 +603,10 @@ int cosmoB_mFloor(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "math.floor() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!IS_NUMBER(args[0])) {
|
||||
cosmoV_typeError(state, "math.floor", "<number>", "%s", cosmoV_typeStr(args[0]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
cosmoV_pushNumber(state, (int)cosmoV_readNumber(args[0]));
|
||||
@@ -665,12 +618,10 @@ int cosmoB_mCeil(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "math.ceil() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!IS_NUMBER(args[0])) {
|
||||
cosmoV_typeError(state, "math.ceil", "<number>", "%s", cosmoV_typeStr(args[0]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int roundedDown = (int)cosmoV_readNumber(args[0]);
|
||||
@@ -689,12 +640,10 @@ int cosmoB_mSin(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "math.sin() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!IS_NUMBER(args[0])) {
|
||||
cosmoV_typeError(state, "math.sin", "<number>", "%s", cosmoV_typeStr(args[0]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
cosmoV_pushNumber(state, sin(cosmoV_readNumber(args[0])));
|
||||
@@ -705,12 +654,10 @@ int cosmoB_mCos(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "math.cos() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!IS_NUMBER(args[0])) {
|
||||
cosmoV_typeError(state, "math.cos", "<number>", "%s", cosmoV_typeStr(args[0]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
cosmoV_pushNumber(state, cos(cosmoV_readNumber(args[0])));
|
||||
@@ -721,12 +668,10 @@ int cosmoB_mTan(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "math.tan() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!IS_NUMBER(args[0])) {
|
||||
cosmoV_typeError(state, "math.tan", "<number>", "%s", cosmoV_typeStr(args[0]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
cosmoV_pushNumber(state, tan(cosmoV_readNumber(args[0])));
|
||||
@@ -737,12 +682,10 @@ int cosmoB_mASin(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "math.asin() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!IS_NUMBER(args[0])) {
|
||||
cosmoV_typeError(state, "math.asin", "<number>", "%s", cosmoV_typeStr(args[0]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
cosmoV_pushNumber(state, asin(cosmoV_readNumber(args[0])));
|
||||
@@ -753,12 +696,10 @@ int cosmoB_mACos(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "math.acos() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!IS_NUMBER(args[0])) {
|
||||
cosmoV_typeError(state, "math.acos", "<number>", "%s", cosmoV_typeStr(args[0]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
cosmoV_pushNumber(state, acos(cosmoV_readNumber(args[0])));
|
||||
@@ -769,12 +710,10 @@ int cosmoB_mATan(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "math.atan() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!IS_NUMBER(args[0])) {
|
||||
cosmoV_typeError(state, "math.atan", "<number>", "%s", cosmoV_typeStr(args[0]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
cosmoV_pushNumber(state, atan(cosmoV_readNumber(args[0])));
|
||||
@@ -785,12 +724,10 @@ int cosmoB_mRad(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "math.rad() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!IS_NUMBER(args[0])) {
|
||||
cosmoV_typeError(state, "math.rad", "<number>", "%s", cosmoV_typeStr(args[0]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
// convert the degree to radians
|
||||
@@ -802,12 +739,10 @@ int cosmoB_mDeg(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "math.deg() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!IS_NUMBER(args[0])) {
|
||||
cosmoV_typeError(state, "math.deg", "<number>", "%s", cosmoV_typeStr(args[0]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
// convert the degree to radians
|
||||
@@ -823,10 +758,10 @@ void cosmoB_loadMathLib(CState *state)
|
||||
CosmoCFunction mathLib[] = {cosmoB_mAbs, cosmoB_mFloor, cosmoB_mCeil, cosmoB_mSin,
|
||||
cosmoB_mCos, cosmoB_mTan, cosmoB_mASin, cosmoB_mACos,
|
||||
cosmoB_mATan, cosmoB_mRad, cosmoB_mDeg};
|
||||
int i;
|
||||
|
||||
// make math library object
|
||||
cosmoV_pushString(state, "math");
|
||||
int i;
|
||||
for (i = 0; i < sizeof(identifiers) / sizeof(identifiers[0]); i++) {
|
||||
cosmoV_pushString(state, identifiers[i]);
|
||||
cosmoV_pushCFunction(state, mathLib[i]);
|
||||
@@ -856,13 +791,11 @@ int cosmoB_vsetGlobal(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 2) {
|
||||
cosmoV_error(state, "Expected 2 argumenst, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!IS_TABLE(args[1])) {
|
||||
cosmoV_typeError(state, "vm.__setter[\"globals\"]", "<object>, <table>", "%s, %s",
|
||||
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
// this makes me very nervous ngl
|
||||
@@ -871,17 +804,37 @@ int cosmoB_vsetGlobal(CState *state, int nargs, CValue *args)
|
||||
return 0;
|
||||
}
|
||||
|
||||
// vm.disassemble()
|
||||
int cosmoB_vdisassemble(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
CObjClosure *closure;
|
||||
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "Expected 1 argument, got %d!", nargs);
|
||||
}
|
||||
|
||||
// get the closure
|
||||
if (!IS_CLOSURE(args[0])) {
|
||||
cosmoV_typeError(state, "vm.disassemble", "<closure>", "%s", cosmoV_typeStr(args[0]));
|
||||
}
|
||||
|
||||
closure = cosmoV_readClosure(args[0]);
|
||||
|
||||
// print the disasembly
|
||||
disasmChunk(&closure->function->chunk,
|
||||
closure->function->name ? closure->function->name->str : UNNAMEDCHUNK, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cosmoB_vindexBProto(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 2) {
|
||||
cosmoV_error(state, "Expected 2 arguments, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!IS_NUMBER(args[1])) {
|
||||
cosmoV_typeError(state, "baseProtos.__index", "<object>, <number>", "%s, %s",
|
||||
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int indx = (int)cosmoV_readNumber(args[1]);
|
||||
@@ -903,14 +856,12 @@ int cosmoB_vnewindexBProto(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 3) {
|
||||
cosmoV_error(state, "Expected 3 arguments, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!IS_NUMBER(args[1]) || !IS_OBJECT(args[2])) {
|
||||
cosmoV_typeError(state, "baseProtos.__newindex", "<object>, <number>, <object>",
|
||||
"%s, %s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]),
|
||||
cosmoV_typeStr(args[2]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int indx = (int)cosmoV_readNumber(args[1]);
|
||||
@@ -918,7 +869,6 @@ int cosmoB_vnewindexBProto(CState *state, int nargs, CValue *args)
|
||||
|
||||
if (indx >= COBJ_MAX || indx < 0) {
|
||||
cosmoV_error(state, "index out of range! expected 0 - %d, got %d!", COBJ_MAX, indx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
cosmoV_registerProtoObject(state, indx, proto);
|
||||
@@ -928,16 +878,8 @@ int cosmoB_vnewindexBProto(CState *state, int nargs, CValue *args)
|
||||
// vm.collect()
|
||||
int cosmoB_vcollect(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
// first, unfreeze the state (we start frozen on entry to any C Function)
|
||||
cosmoM_unfreezeGC(state);
|
||||
|
||||
// now force a garbage collection
|
||||
// force a garbage collection
|
||||
cosmoM_collectGarbage(state);
|
||||
|
||||
// and re-freeze the state
|
||||
cosmoM_freezeGC(state);
|
||||
|
||||
// the end!
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -976,7 +918,10 @@ void cosmoB_loadVM(CState *state)
|
||||
cosmoV_pushString(state, "collect");
|
||||
cosmoV_pushCFunction(state, cosmoB_vcollect);
|
||||
|
||||
cosmoV_makeObject(state, 4); // makes the vm object
|
||||
cosmoV_pushString(state, "disassemble");
|
||||
cosmoV_pushCFunction(state, cosmoB_vdisassemble);
|
||||
|
||||
cosmoV_makeObject(state, 5); // makes the vm object
|
||||
|
||||
// register "vm" to the global table
|
||||
cosmoV_register(state, 1);
|
||||
|
@@ -22,7 +22,7 @@ COSMO_API void cosmoB_loadObjLib(CState *state);
|
||||
- os.system()
|
||||
- os.time()
|
||||
*/
|
||||
COSMO_API void cosmoB_loadOSLib(CState *state);
|
||||
COSMO_API void cosmoB_loadOS(CState *state);
|
||||
|
||||
/* loads the base string library, including:
|
||||
- string.sub & <string>:sub()
|
||||
@@ -50,6 +50,7 @@ COSMO_API void cosmoB_loadMathLib(CState *state);
|
||||
- manually setting/grabbing base protos of any object (vm.baseProtos)
|
||||
- manually setting/grabbing the global table (vm.globals)
|
||||
- manually invoking a garbage collection event (vm.collect())
|
||||
- printing closure disassemblies (vm.disassemble())
|
||||
|
||||
for this reason, it is recommended to NOT load this library in production
|
||||
*/
|
||||
|
13
src/cchunk.c
13
src/cchunk.c
@@ -27,9 +27,9 @@ void initChunk(CState *state, CChunk *chunk, size_t startCapacity)
|
||||
void cleanChunk(CState *state, CChunk *chunk)
|
||||
{
|
||||
// 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
|
||||
cosmoM_freearray(state, int, chunk->lineInfo, chunk->capacity);
|
||||
cosmoM_freeArray(state, int, chunk->lineInfo, chunk->capacity);
|
||||
// free the constants
|
||||
cleanValArray(state, &chunk->constants);
|
||||
}
|
||||
@@ -49,9 +49,10 @@ int addConstant(CState *state, CChunk *chunk, CValue value)
|
||||
return i; // we already have a matching constant!
|
||||
}
|
||||
|
||||
cosmoM_freezeGC(state); // so our GC doesn't free it
|
||||
cosmoV_pushValue(state, value); // push the value to the stack so our GC can see it
|
||||
appendValArray(state, &chunk->constants, value);
|
||||
cosmoM_unfreezeGC(state);
|
||||
cosmoV_pop(state);
|
||||
|
||||
return chunk->constants.count - 1; // return the index of the new constants
|
||||
}
|
||||
|
||||
@@ -60,8 +61,8 @@ int addConstant(CState *state, CChunk *chunk, CValue value)
|
||||
void writeu8Chunk(CState *state, CChunk *chunk, INSTRUCTION i, int line)
|
||||
{
|
||||
// does the buffer need to be reallocated?
|
||||
cosmoM_growarray(state, INSTRUCTION, chunk->buf, chunk->count, chunk->capacity);
|
||||
cosmoM_growarray(state, int, chunk->lineInfo, chunk->count, chunk->lineCapacity);
|
||||
cosmoM_growArray(state, INSTRUCTION, chunk->buf, chunk->count, chunk->capacity);
|
||||
cosmoM_growArray(state, int, chunk->lineInfo, chunk->count, chunk->lineCapacity);
|
||||
|
||||
// write data to the chunk :)
|
||||
chunk->lineInfo[chunk->count] = line;
|
||||
|
@@ -21,6 +21,8 @@ void cleanChunk(CState *state, CChunk *chunk); // frees everything but the struc
|
||||
void freeChunk(CState *state, CChunk *chunk); // frees everything including the struct
|
||||
int addConstant(CState *state, CChunk *chunk, CValue value);
|
||||
|
||||
bool validateChunk(CState *state, CChunk *chunk);
|
||||
|
||||
// write to chunk
|
||||
void writeu8Chunk(CState *state, CChunk *chunk, INSTRUCTION i, int line);
|
||||
void writeu16Chunk(CState *state, CChunk *chunk, uint16_t i, int line);
|
||||
|
16
src/cdebug.c
16
src/cdebug.c
@@ -9,53 +9,53 @@ void printIndent(int indent)
|
||||
printf("\t");
|
||||
}
|
||||
|
||||
int simpleInstruction(const char *name, int offset)
|
||||
static int simpleInstruction(const char *name, int offset)
|
||||
{
|
||||
printf("%s", name);
|
||||
return offset + 1; // consume opcode
|
||||
}
|
||||
|
||||
int u8OperandInstruction(const char *name, CChunk *chunk, int offset)
|
||||
static int u8OperandInstruction(const char *name, CChunk *chunk, int offset)
|
||||
{
|
||||
printf("%-16s [%03d]", name, readu8Chunk(chunk, offset + 1));
|
||||
return offset + 2;
|
||||
}
|
||||
|
||||
int u16OperandInstruction(const char *name, CChunk *chunk, int offset)
|
||||
static int u16OperandInstruction(const char *name, CChunk *chunk, int offset)
|
||||
{
|
||||
printf("%-16s [%05d]", name, readu16Chunk(chunk, offset + 1));
|
||||
return offset + 1 + (sizeof(uint16_t) / sizeof(INSTRUCTION));
|
||||
}
|
||||
|
||||
int JumpInstruction(const char *name, CChunk *chunk, int offset, int dir)
|
||||
static int JumpInstruction(const char *name, CChunk *chunk, int offset, int dir)
|
||||
{
|
||||
int jmp = ((int)readu16Chunk(chunk, offset + 1)) * dir;
|
||||
printf("%-16s [%05d] - jumps to %04d", name, jmp, offset + 3 + jmp);
|
||||
return offset + 1 + (sizeof(uint16_t) / sizeof(INSTRUCTION));
|
||||
}
|
||||
|
||||
int u8u8OperandInstruction(const char *name, CChunk *chunk, int offset)
|
||||
static int u8u8OperandInstruction(const char *name, CChunk *chunk, int offset)
|
||||
{
|
||||
printf("%-16s [%03d] [%03d]", name, readu8Chunk(chunk, offset + 1),
|
||||
readu8Chunk(chunk, offset + 2));
|
||||
return offset + 3; // op + u8 + u8
|
||||
}
|
||||
|
||||
int u8u16OperandInstruction(const char *name, CChunk *chunk, int offset)
|
||||
static int u8u16OperandInstruction(const char *name, CChunk *chunk, int offset)
|
||||
{
|
||||
printf("%-16s [%03d] [%05d]", name, readu8Chunk(chunk, offset + 1),
|
||||
readu16Chunk(chunk, offset + 2));
|
||||
return offset + 4; // op + u8 + u16
|
||||
}
|
||||
|
||||
int u8u8u16OperandInstruction(const char *name, CChunk *chunk, int offset)
|
||||
static int u8u8u16OperandInstruction(const char *name, CChunk *chunk, int offset)
|
||||
{
|
||||
printf("%-16s [%03d] [%03d] [%05d]", name, readu8Chunk(chunk, offset + 1),
|
||||
readu8Chunk(chunk, offset + 2), readu16Chunk(chunk, offset + 3));
|
||||
return offset + 5; // op + u8 + u8 + u16
|
||||
}
|
||||
|
||||
int constInstruction(const char *name, CChunk *chunk, int offset)
|
||||
static int constInstruction(const char *name, CChunk *chunk, int offset)
|
||||
{
|
||||
int index = readu16Chunk(chunk, offset + 1);
|
||||
printf("%-16s [%05d] - ", name, index);
|
||||
|
29
src/clex.c
29
src/clex.c
@@ -54,7 +54,7 @@ static void resetBuffer(CLexState *state)
|
||||
// cancels the token heap buffer and frees it
|
||||
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);
|
||||
}
|
||||
@@ -62,7 +62,7 @@ static void freeBuffer(CLexState *state)
|
||||
// adds character to buffer
|
||||
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;
|
||||
}
|
||||
@@ -150,7 +150,7 @@ static bool match(CLexState *state, char expected)
|
||||
return true;
|
||||
}
|
||||
|
||||
char peek(CLexState *state)
|
||||
static char peek(CLexState *state)
|
||||
{
|
||||
return *state->currentChar;
|
||||
}
|
||||
@@ -163,7 +163,7 @@ static char peekNext(CLexState *state)
|
||||
return state->currentChar[1];
|
||||
}
|
||||
|
||||
char next(CLexState *state)
|
||||
static char next(CLexState *state)
|
||||
{
|
||||
if (isEnd(state))
|
||||
return '\0'; // return a null terminator
|
||||
@@ -171,12 +171,12 @@ char next(CLexState *state)
|
||||
return state->currentChar[-1];
|
||||
}
|
||||
|
||||
bool isHex(char c)
|
||||
static bool isHex(char c)
|
||||
{
|
||||
return isNumerical(c) || ('A' <= c && 'F' >= c) || ('a' <= c && 'f' >= c);
|
||||
}
|
||||
|
||||
CTokenType identifierType(CLexState *state)
|
||||
static CTokenType identifierType(CLexState *state)
|
||||
{
|
||||
int length = state->currentChar - state->startChar;
|
||||
|
||||
@@ -192,7 +192,7 @@ CTokenType identifierType(CLexState *state)
|
||||
return TOKEN_IDENTIFIER;
|
||||
}
|
||||
|
||||
void skipWhitespace(CLexState *state)
|
||||
static void skipWhitespace(CLexState *state)
|
||||
{
|
||||
while (true) {
|
||||
char c = peek(state);
|
||||
@@ -235,7 +235,7 @@ void skipWhitespace(CLexState *state)
|
||||
}
|
||||
}
|
||||
|
||||
CToken parseString(CLexState *state)
|
||||
static CToken parseString(CLexState *state)
|
||||
{
|
||||
makeBuffer(state); // buffer mode
|
||||
while (peek(state) != '"' && !isEnd(state)) {
|
||||
@@ -341,7 +341,7 @@ CToken parseString(CLexState *state)
|
||||
return makeToken(state, TOKEN_STRING);
|
||||
}
|
||||
|
||||
CToken parseNumber(CLexState *state)
|
||||
static CToken parseNumber(CLexState *state)
|
||||
{
|
||||
switch (peek(state)) {
|
||||
case 'x': // hexadecimal number
|
||||
@@ -380,7 +380,7 @@ CToken parseNumber(CLexState *state)
|
||||
return makeToken(state, TOKEN_NUMBER);
|
||||
}
|
||||
|
||||
CToken parseIdentifier(CLexState *state)
|
||||
static CToken parseIdentifier(CLexState *state)
|
||||
{
|
||||
// read literal
|
||||
while ((isAlpha(peek(state)) || isNumerical(peek(state))) && !isEnd(state))
|
||||
@@ -389,9 +389,8 @@ CToken parseIdentifier(CLexState *state)
|
||||
return makeToken(state, identifierType(state)); // is it a reserved word?
|
||||
}
|
||||
|
||||
CLexState *cosmoL_newLexState(CState *cstate, const char *source)
|
||||
void cosmoL_initLexState(CState *cstate, CLexState *state, const char *source)
|
||||
{
|
||||
CLexState *state = cosmoM_xmalloc(cstate, sizeof(CLexState));
|
||||
state->startChar = (char *)source;
|
||||
state->currentChar = (char *)source;
|
||||
state->line = 1;
|
||||
@@ -400,13 +399,11 @@ CLexState *cosmoL_newLexState(CState *cstate, const char *source)
|
||||
state->cstate = cstate;
|
||||
|
||||
resetBuffer(state);
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
void cosmoL_freeLexState(CState *state, CLexState *lstate)
|
||||
void cosmoL_cleanupLexState(CState *state, CLexState *lstate)
|
||||
{
|
||||
cosmoM_free(state, CLexState, lstate);
|
||||
// stubbed
|
||||
}
|
||||
|
||||
CToken cosmoL_scanToken(CLexState *state)
|
||||
|
@@ -93,7 +93,7 @@ typedef struct
|
||||
char *currentChar;
|
||||
char *startChar;
|
||||
char *buffer; // if non-NULL & bufCount > 0, token->start & token->length will be set to buffer
|
||||
// & bufCount respectively
|
||||
// & bufCount respectively. used exclusively for string literals
|
||||
size_t bufCount;
|
||||
size_t bufCap;
|
||||
int line; // current line
|
||||
@@ -103,8 +103,8 @@ typedef struct
|
||||
CState *cstate;
|
||||
} CLexState;
|
||||
|
||||
CLexState *cosmoL_newLexState(CState *state, const char *source);
|
||||
void cosmoL_freeLexState(CState *state, CLexState *lstate);
|
||||
void cosmoL_initLexState(CState *cstate, CLexState *state, const char *source);
|
||||
void cosmoL_cleanupLexState(CState *state, CLexState *lstate);
|
||||
|
||||
CToken cosmoL_scanToken(CLexState *state);
|
||||
|
||||
|
70
src/cmem.c
70
src/cmem.c
@@ -10,7 +10,24 @@
|
||||
// realloc wrapper
|
||||
void *cosmoM_reallocate(CState *state, void *buf, size_t oldSize, size_t newSize)
|
||||
{
|
||||
if (buf == NULL)
|
||||
oldSize = 0;
|
||||
|
||||
#ifdef GC_DEBUG
|
||||
printf("old allocated bytes: %ld\n", state->allocatedBytes);
|
||||
if (buf) {
|
||||
if (newSize == 0) {
|
||||
printf("freeing %p, reclaiming %ld bytes...\n", buf, oldSize);
|
||||
} else {
|
||||
printf("realloc %p, byte difference: %ld\n", buf, newSize - oldSize);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
state->allocatedBytes += newSize - oldSize;
|
||||
#ifdef GC_DEBUG
|
||||
printf("new allocated bytes: %ld\n", state->allocatedBytes);
|
||||
fflush(stdout);
|
||||
#endif
|
||||
|
||||
if (newSize == 0) { // it needs to be freed
|
||||
free(buf);
|
||||
@@ -33,6 +50,11 @@ void *cosmoM_reallocate(CState *state, void *buf, size_t oldSize, size_t newSize
|
||||
// if NULL is passed, realloc() acts like malloc()
|
||||
void *newBuf = realloc(buf, newSize);
|
||||
|
||||
#ifdef GC_DEBUG
|
||||
printf("allocating new buffer of size %ld at %p\n", newSize - oldSize, newBuf);
|
||||
fflush(stdout);
|
||||
#endif
|
||||
|
||||
if (newBuf == NULL) {
|
||||
CERROR("failed to allocate memory!");
|
||||
exit(1);
|
||||
@@ -51,15 +73,15 @@ COSMO_API bool cosmoM_checkGarbage(CState *state, size_t needed)
|
||||
return false;
|
||||
}
|
||||
|
||||
void markObject(CState *state, CObj *obj);
|
||||
void markValue(CState *state, CValue val);
|
||||
static void markObject(CState *state, CObj *obj);
|
||||
static void markValue(CState *state, CValue val);
|
||||
|
||||
void markTable(CState *state, CTable *tbl)
|
||||
static void markTable(CState *state, CTable *tbl)
|
||||
{
|
||||
if (tbl->table == NULL) // table is still being initialized
|
||||
return;
|
||||
|
||||
int cap = tbl->capacityMask + 1;
|
||||
int cap = cosmoT_getCapacity(tbl);
|
||||
for (int i = 0; i < cap; i++) {
|
||||
CTableEntry *entry = &tbl->table[i];
|
||||
markValue(state, entry->key);
|
||||
@@ -67,13 +89,18 @@ void markTable(CState *state, CTable *tbl)
|
||||
}
|
||||
}
|
||||
|
||||
// frees white members from the table
|
||||
void tableRemoveWhite(CState *state, CTable *tbl)
|
||||
// removes white members from the table
|
||||
static void tableRemoveWhite(CState *state, CTable *tbl)
|
||||
{
|
||||
if (tbl->table == NULL) // table is still being initialized
|
||||
return;
|
||||
|
||||
int cap = tbl->capacityMask + 1;
|
||||
int cap = cosmoT_getCapacity(tbl);
|
||||
|
||||
#ifdef GC_DEBUG
|
||||
printf("tableRemoveWhite: %p, cap: %d\n", tbl, cap);
|
||||
#endif
|
||||
|
||||
for (int i = 0; i < cap; i++) {
|
||||
CTableEntry *entry = &tbl->table[i];
|
||||
if (IS_REF(entry->key) &&
|
||||
@@ -86,7 +113,7 @@ void tableRemoveWhite(CState *state, CTable *tbl)
|
||||
cosmoT_checkShrink(state, tbl); // recovers the memory we're no longer using
|
||||
}
|
||||
|
||||
void markArray(CState *state, CValueArray *array)
|
||||
static void markArray(CState *state, CValueArray *array)
|
||||
{
|
||||
for (size_t i = 0; i < array->count; i++) {
|
||||
markValue(state, array->values[i]);
|
||||
@@ -95,7 +122,7 @@ void markArray(CState *state, CValueArray *array)
|
||||
|
||||
// mark all references associated with the object
|
||||
// black = keep, white = discard
|
||||
void blackenObject(CState *state, CObj *obj)
|
||||
static void blackenObject(CState *state, CObj *obj)
|
||||
{
|
||||
markObject(state, (CObj *)obj->proto);
|
||||
switch (obj->type) {
|
||||
@@ -161,7 +188,7 @@ void blackenObject(CState *state, CObj *obj)
|
||||
}
|
||||
}
|
||||
|
||||
void markObject(CState *state, CObj *obj)
|
||||
static void markObject(CState *state, CObj *obj)
|
||||
{
|
||||
if (obj == NULL || obj->isMarked) // skip if NULL or already marked
|
||||
return;
|
||||
@@ -179,20 +206,20 @@ void markObject(CState *state, CObj *obj)
|
||||
return;
|
||||
|
||||
// we can use cosmoM_growarray because we lock the GC when we entered in cosmoM_collectGarbage
|
||||
cosmoM_growarray(state, CObj *, state->grayStack.array, state->grayStack.count,
|
||||
cosmoM_growArray(state, CObj *, state->grayStack.array, state->grayStack.count,
|
||||
state->grayStack.capacity);
|
||||
|
||||
state->grayStack.array[state->grayStack.count++] = obj;
|
||||
}
|
||||
|
||||
void markValue(CState *state, CValue val)
|
||||
static void markValue(CState *state, CValue val)
|
||||
{
|
||||
if (IS_REF(val))
|
||||
markObject(state, cosmoV_readRef(val));
|
||||
}
|
||||
|
||||
// trace our gray references
|
||||
void traceGrays(CState *state)
|
||||
static void traceGrays(CState *state)
|
||||
{
|
||||
while (state->grayStack.count > 0) {
|
||||
CObj *obj = state->grayStack.array[--state->grayStack.count];
|
||||
@@ -200,7 +227,7 @@ void traceGrays(CState *state)
|
||||
}
|
||||
}
|
||||
|
||||
void sweep(CState *state)
|
||||
static void sweep(CState *state)
|
||||
{
|
||||
CObj *prev = NULL;
|
||||
CObj *object = state->objects;
|
||||
@@ -224,7 +251,7 @@ void sweep(CState *state)
|
||||
}
|
||||
}
|
||||
|
||||
void markUserRoots(CState *state)
|
||||
static void markUserRoots(CState *state)
|
||||
{
|
||||
CObj *root = state->userRoots;
|
||||
|
||||
@@ -235,7 +262,7 @@ void markUserRoots(CState *state)
|
||||
}
|
||||
}
|
||||
|
||||
void markRoots(CState *state)
|
||||
static void markRoots(CState *state)
|
||||
{
|
||||
// mark all values on the stack
|
||||
for (StkPtr value = state->stack; value < state->top; value++) {
|
||||
@@ -261,9 +288,6 @@ void markRoots(CState *state)
|
||||
// mark the user defined roots
|
||||
markUserRoots(state);
|
||||
|
||||
// mark other misc. internally reserved objects
|
||||
markObject(state, (CObj *)state->error);
|
||||
|
||||
for (int i = 0; i < COBJ_MAX; i++)
|
||||
markObject(state, (CObj *)state->protoObjects[i]);
|
||||
|
||||
@@ -272,12 +296,11 @@ void markRoots(CState *state)
|
||||
|
||||
COSMO_API void cosmoM_collectGarbage(CState *state)
|
||||
{
|
||||
cosmoM_freezeGC(state);
|
||||
#ifdef GC_DEBUG
|
||||
printf("-- GC start\n");
|
||||
size_t start = state->allocatedBytes;
|
||||
#endif
|
||||
cosmoM_freezeGC(state); // we don't want a recursive garbage collection event!
|
||||
|
||||
markRoots(state);
|
||||
|
||||
tableRemoveWhite(
|
||||
@@ -288,15 +311,12 @@ COSMO_API void cosmoM_collectGarbage(CState *state)
|
||||
|
||||
// set our next GC event
|
||||
cosmoM_updateThreshhold(state);
|
||||
|
||||
state->freezeGC--; // we don't want to use cosmoM_unfreezeGC because that might trigger a GC
|
||||
// event (if GC_STRESS is defined)
|
||||
#ifdef GC_DEBUG
|
||||
printf("-- GC end, reclaimed %ld bytes (started at %ld, ended at %ld), next garbage collection "
|
||||
"scheduled at %ld bytes\n",
|
||||
start - state->allocatedBytes, start, state->allocatedBytes, state->nextGC);
|
||||
getchar(); // pauses execution
|
||||
#endif
|
||||
cosmoM_unfreezeGC(state);
|
||||
}
|
||||
|
||||
COSMO_API void cosmoM_updateThreshhold(CState *state)
|
||||
|
10
src/cmem.h
10
src/cmem.h
@@ -12,16 +12,16 @@
|
||||
#define ARRAY_START 8
|
||||
|
||||
#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__); \
|
||||
cosmoM_reallocate(state, buf, sizeof(type) * capacity, 0)
|
||||
#else
|
||||
# define cosmoM_freearray(state, type, buf, capacity) \
|
||||
# define cosmoM_freeArray(state, type, buf, capacity) \
|
||||
cosmoM_reallocate(state, buf, sizeof(type) * capacity, 0)
|
||||
#endif
|
||||
|
||||
#define cosmoM_growarray(state, type, buf, count, capacity) \
|
||||
#define cosmoM_growArray(state, type, buf, count, capacity) \
|
||||
if (count >= capacity || buf == NULL) { \
|
||||
int old = capacity; \
|
||||
capacity = old * GROW_FACTOR; \
|
||||
@@ -38,6 +38,7 @@
|
||||
|
||||
#define cosmoM_isFrozen(state) (state->freezeGC > 0)
|
||||
|
||||
// cosmoM_freezeGC should only be used in the garbage collector !
|
||||
// if debugging, print the locations of when the state is frozen/unfrozen
|
||||
#ifdef GC_DEBUG
|
||||
# define cosmoM_freezeGC(state) \
|
||||
@@ -67,12 +68,13 @@ COSMO_API void cosmoM_collectGarbage(CState *state);
|
||||
COSMO_API void cosmoM_updateThreshhold(CState *state);
|
||||
|
||||
// lets the VM know you are holding a reference to a CObj and to not free it
|
||||
// NOTE: prefer to use the stack when possible
|
||||
COSMO_API void cosmoM_addRoot(CState *state, CObj *newRoot);
|
||||
|
||||
// lets the VM know this root is no longer held in a reference and is able to be freed
|
||||
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
|
||||
static inline void *cosmoM_xmalloc(CState *state, size_t sz)
|
||||
{
|
||||
return cosmoM_reallocate(state, NULL, 0, sz);
|
||||
|
88
src/cobj.c
88
src/cobj.c
@@ -8,7 +8,6 @@
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
// we don't actually hash the whole string :eyes:
|
||||
uint32_t hashString(const char *str, size_t sz)
|
||||
@@ -34,7 +33,7 @@ CObj *cosmoO_allocateBase(CState *state, size_t sz, CObjType type)
|
||||
|
||||
obj->nextRoot = NULL;
|
||||
#ifdef GC_DEBUG
|
||||
printf("allocated %p with OBJ_TYPE %d\n", obj, type);
|
||||
printf("allocated %s %p\n", cosmoO_typeStr(obj), obj);
|
||||
#endif
|
||||
return obj;
|
||||
}
|
||||
@@ -42,14 +41,12 @@ CObj *cosmoO_allocateBase(CState *state, size_t sz, CObjType type)
|
||||
void cosmoO_free(CState *state, CObj *obj)
|
||||
{
|
||||
#ifdef GC_DEBUG
|
||||
printf("freeing %p [", obj);
|
||||
printObject(obj);
|
||||
printf("]\n");
|
||||
printf("freeing %s %p\n", cosmoO_typeStr(obj), obj);
|
||||
#endif
|
||||
switch (obj->type) {
|
||||
case COBJ_STRING: {
|
||||
CObjString *objStr = (CObjString *)obj;
|
||||
cosmoM_freearray(state, char, objStr->str, objStr->length + 1);
|
||||
cosmoM_freeArray(state, char, objStr->str, objStr->length + 1);
|
||||
cosmoM_free(state, CObjString, objStr);
|
||||
break;
|
||||
}
|
||||
@@ -85,13 +82,13 @@ void cosmoO_free(CState *state, CObj *obj)
|
||||
}
|
||||
case COBJ_ERROR: {
|
||||
CObjError *err = (CObjError *)obj;
|
||||
cosmoM_freearray(state, CCallFrame, err->frames, err->frameCount);
|
||||
cosmoM_freeArray(state, CCallFrame, err->frames, err->frameCount);
|
||||
cosmoM_free(state, CObjError, obj);
|
||||
break;
|
||||
}
|
||||
case COBJ_CLOSURE: {
|
||||
CObjClosure *closure = (CObjClosure *)obj;
|
||||
cosmoM_freearray(state, CObjUpval *, closure->upvalues, closure->upvalueCount);
|
||||
cosmoM_freeArray(state, CObjUpval *, closure->upvalues, closure->upvalueCount);
|
||||
cosmoM_free(state, CObjClosure, closure);
|
||||
break;
|
||||
}
|
||||
@@ -167,14 +164,12 @@ _eqFail:
|
||||
cosmoV_pushValue(state, eq1);
|
||||
cosmoV_pushRef(state, obj1);
|
||||
cosmoV_pushRef(state, obj2);
|
||||
if (cosmoV_call(state, 2, 1) != COSMOVM_OK)
|
||||
return false;
|
||||
cosmoV_call(state, 2, 1);
|
||||
|
||||
// check return value and make sure it's a boolean
|
||||
if (!IS_BOOLEAN(*cosmoV_getTop(state, 0))) {
|
||||
cosmoV_error(state, "__equal expected to return <boolean>, got %s!",
|
||||
cosmoV_typeStr(*cosmoV_pop(state)));
|
||||
return false;
|
||||
}
|
||||
|
||||
// return the result
|
||||
@@ -191,10 +186,10 @@ CObjObject *cosmoO_newObject(CState *state)
|
||||
obj->userP = NULL; // reserved for C API
|
||||
obj->userT = 0;
|
||||
obj->isLocked = false;
|
||||
|
||||
cosmoV_pushRef(state, (CObj *)obj); // so our GC can keep track of it
|
||||
cosmoT_initTable(state, &obj->tbl, ARRAY_START);
|
||||
cosmoV_pop(state);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
@@ -234,14 +229,13 @@ CObjCFunction *cosmoO_newCFunction(CState *state, CosmoCFunction func)
|
||||
|
||||
CObjError *cosmoO_newError(CState *state, CValue err)
|
||||
{
|
||||
CCallFrame *frames = cosmoM_xmalloc(state, sizeof(CCallFrame) * state->frameCount);
|
||||
CObjError *cerror = (CObjError *)cosmoO_allocateBase(state, sizeof(CObjError), COBJ_ERROR);
|
||||
cerror->err = err;
|
||||
cerror->frameCount = state->frameCount;
|
||||
cerror->frames = frames;
|
||||
cerror->parserError = false;
|
||||
|
||||
// allocate the callframe
|
||||
cerror->frames = cosmoM_xmalloc(state, sizeof(CCallFrame) * cerror->frameCount);
|
||||
|
||||
// clone the call frame
|
||||
for (int i = 0; i < state->frameCount; i++)
|
||||
cerror->frames[i] = state->callFrame[i];
|
||||
@@ -311,7 +305,7 @@ CObjString *cosmoO_takeString(CState *state, char *str, size_t length)
|
||||
|
||||
// have we already interned this string?
|
||||
if (lookup != NULL) {
|
||||
cosmoM_freearray(state, char, str,
|
||||
cosmoM_freeArray(state, char, str,
|
||||
length + 1); // free our passed character array, it's unneeded!
|
||||
return lookup;
|
||||
}
|
||||
@@ -327,8 +321,7 @@ CObjString *cosmoO_allocateString(CState *state, const char *str, size_t sz, uin
|
||||
strObj->length = sz;
|
||||
strObj->hash = hash;
|
||||
|
||||
// we push & pop the string so our GC can find it (we don't use freezeGC/unfreezeGC because we
|
||||
// *want* a GC event to happen)
|
||||
// push/pop to make sure GC doesn't collect it
|
||||
cosmoV_pushRef(state, (CObj *)strObj);
|
||||
cosmoT_insert(state, &state->strings, cosmoV_newRef((CObj *)strObj));
|
||||
cosmoV_pop(state);
|
||||
@@ -410,11 +403,10 @@ bool cosmoO_getRawObject(CState *state, CObjObject *proto, CValue key, CValue *v
|
||||
val)) { // if the field doesn't exist in the object, check the proto
|
||||
if (cosmoO_getIString(state, proto, ISTRING_GETTER, val) && IS_TABLE(*val) &&
|
||||
cosmoT_get(state, &cosmoV_readTable(*val)->tbl, key, val)) {
|
||||
cosmoV_pushValue(state, *val); // push function
|
||||
cosmoV_pushRef(state, (CObj *)obj); // push object
|
||||
if (cosmoV_call(state, 1, 1) != COSMOVM_OK) // call the function with the 1 argument
|
||||
return false;
|
||||
*val = *cosmoV_pop(state); // set value to the return value of __index
|
||||
cosmoV_pushValue(state, *val); // push function
|
||||
cosmoV_pushRef(state, (CObj *)obj); // push object
|
||||
cosmoV_call(state, 1, 1); // call the function with the 1 argument
|
||||
*val = *cosmoV_pop(state); // set value to the return value of __index
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -530,12 +522,11 @@ bool cosmoO_getIString(CState *state, CObjObject *object, int flag, CValue *val)
|
||||
bool cosmoO_indexObject(CState *state, CObjObject *object, CValue key, CValue *val)
|
||||
{
|
||||
if (cosmoO_getIString(state, object, ISTRING_INDEX, val)) {
|
||||
cosmoV_pushValue(state, *val); // push function
|
||||
cosmoV_pushRef(state, (CObj *)object); // push object
|
||||
cosmoV_pushValue(state, key); // push key
|
||||
if (cosmoV_call(state, 2, 1) != COSMOVM_OK) // call the function with the 2 arguments
|
||||
return false;
|
||||
*val = *cosmoV_pop(state); // set value to the return value of __index
|
||||
cosmoV_pushValue(state, *val); // push function
|
||||
cosmoV_pushRef(state, (CObj *)object); // push object
|
||||
cosmoV_pushValue(state, key); // push key
|
||||
cosmoV_call(state, 2, 1); // call the function with the 2 arguments
|
||||
*val = *cosmoV_pop(state); // set value to the return value of __index
|
||||
return true;
|
||||
} else { // there's no __index function defined!
|
||||
cosmoV_error(state, "Couldn't index object without __index function!");
|
||||
@@ -553,7 +544,8 @@ bool cosmoO_newIndexObject(CState *state, CObjObject *object, CValue key, CValue
|
||||
cosmoV_pushRef(state, (CObj *)object); // push object
|
||||
cosmoV_pushValue(state, key); // push key & value pair
|
||||
cosmoV_pushValue(state, val);
|
||||
return cosmoV_call(state, 3, 0) == COSMOVM_OK;
|
||||
cosmoV_call(state, 3, 0);
|
||||
return true;
|
||||
} else { // there's no __newindex function defined
|
||||
cosmoV_error(state, "Couldn't set index on object without __newindex function!");
|
||||
}
|
||||
@@ -570,8 +562,7 @@ CObjString *cosmoO_toString(CState *state, CObj *obj)
|
||||
if (protoObject != NULL && cosmoO_getIString(state, protoObject, ISTRING_TOSTRING, &res)) {
|
||||
cosmoV_pushValue(state, res);
|
||||
cosmoV_pushRef(state, (CObj *)obj);
|
||||
if (cosmoV_call(state, 1, 1) != COSMOVM_OK)
|
||||
return cosmoO_copyString(state, "<err>", 5);
|
||||
cosmoV_call(state, 1, 1);
|
||||
|
||||
// make sure the __tostring function returned a string
|
||||
StkPtr ret = cosmoV_getTop(state, 0);
|
||||
@@ -636,14 +627,12 @@ cosmo_Number cosmoO_toNumber(CState *state, CObj *obj)
|
||||
if (proto != NULL && cosmoO_getIString(state, proto, ISTRING_TONUMBER, &res)) {
|
||||
cosmoV_pushValue(state, res);
|
||||
cosmoV_pushRef(state, (CObj *)obj);
|
||||
if (cosmoV_call(state, 1, 1) != COSMOVM_OK) // call res, expect 1 return val of <number>
|
||||
return 0;
|
||||
cosmoV_call(state, 1, 1); // call res, expect 1 return val of <number>
|
||||
|
||||
StkPtr temp = cosmoV_getTop(state, 0);
|
||||
if (!IS_NUMBER(*temp)) {
|
||||
cosmoV_error(state, "__tonumber expected to return <number>, got %s!",
|
||||
cosmoV_typeStr(*temp));
|
||||
return 0;
|
||||
}
|
||||
|
||||
// return number
|
||||
@@ -669,9 +658,7 @@ int cosmoO_count(CState *state, CObj *obj)
|
||||
if (proto != NULL && cosmoO_getIString(state, proto, ISTRING_COUNT, &res)) {
|
||||
cosmoV_pushValue(state, res);
|
||||
cosmoV_pushRef(state, (CObj *)obj);
|
||||
if (cosmoV_call(state, 1, 1) !=
|
||||
COSMOVM_OK) // call res, we expect 1 return value of type <number>
|
||||
return 0;
|
||||
cosmoV_call(state, 1, 1); // call res, we expect 1 return value of type <number>
|
||||
|
||||
StkPtr ret = cosmoV_getTop(state, 0);
|
||||
if (!IS_NUMBER(*ret)) {
|
||||
@@ -702,60 +689,61 @@ int cosmoO_count(CState *state, CObj *obj)
|
||||
|
||||
void printObject(CObj *o)
|
||||
{
|
||||
printf("%s ", cosmoO_typeStr(o));
|
||||
switch (o->type) {
|
||||
case COBJ_STRING: {
|
||||
CObjString *objStr = (CObjString *)o;
|
||||
printf("<string> \"%.*s\"", objStr->length, objStr->str);
|
||||
printf("\"%.*s\"", objStr->length, objStr->str);
|
||||
break;
|
||||
}
|
||||
case COBJ_OBJECT: {
|
||||
printf("<obj> %p", (void *)o);
|
||||
printf("%p", (void *)o);
|
||||
break;
|
||||
}
|
||||
case COBJ_TABLE: {
|
||||
CObjTable *tbl = (CObjTable *)o;
|
||||
printf("<tbl> %p", (void *)tbl);
|
||||
printf("%p", (void *)tbl);
|
||||
break;
|
||||
}
|
||||
case COBJ_FUNCTION: {
|
||||
CObjFunction *objFunc = (CObjFunction *)o;
|
||||
if (objFunc->name != NULL)
|
||||
printf("<function> %.*s", objFunc->name->length, objFunc->name->str);
|
||||
printf("%.*s", objFunc->name->length, objFunc->name->str);
|
||||
else
|
||||
printf("<function> %s", UNNAMEDCHUNK);
|
||||
printf("%s", UNNAMEDCHUNK);
|
||||
break;
|
||||
}
|
||||
case COBJ_CFUNCTION: {
|
||||
CObjCFunction *objCFunc = (CObjCFunction *)o;
|
||||
printf("<c function> %p", (void *)objCFunc->cfunc);
|
||||
printf("%p", (void *)objCFunc->cfunc);
|
||||
break;
|
||||
}
|
||||
case COBJ_ERROR: {
|
||||
CObjError *err = (CObjError *)o;
|
||||
printf("<error> %p -> ", (void *)o);
|
||||
printf("%p -> ", (void *)o);
|
||||
printValue(err->err);
|
||||
break;
|
||||
}
|
||||
case COBJ_METHOD: {
|
||||
CObjMethod *method = (CObjMethod *)o;
|
||||
printf("<method> %p -> ", (void *)method);
|
||||
printf("%p -> ", (void *)method);
|
||||
printValue(method->func);
|
||||
break;
|
||||
}
|
||||
case COBJ_CLOSURE: {
|
||||
CObjClosure *closure = (CObjClosure *)o;
|
||||
printf("<closure> %p -> ", (void *)closure);
|
||||
printf("%p -> ", (void *)closure);
|
||||
printObject((CObj *)closure->function); // just print the function
|
||||
break;
|
||||
}
|
||||
case COBJ_UPVALUE: {
|
||||
CObjUpval *upval = (CObjUpval *)o;
|
||||
printf("<upvalue> %p -> ", (void *)upval->val);
|
||||
printf("%p -> ", (void *)upval->val);
|
||||
printValue(*upval->val);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
printf("<unkn obj %p>", (void *)o);
|
||||
printf("%p", (void *)o);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -772,6 +760,8 @@ const char *cosmoO_typeStr(CObj *obj)
|
||||
return "<function>";
|
||||
case COBJ_CFUNCTION:
|
||||
return "<c function>";
|
||||
case COBJ_ERROR:
|
||||
return "<error>";
|
||||
case COBJ_METHOD:
|
||||
return "<method>";
|
||||
case COBJ_CLOSURE:
|
||||
|
@@ -136,6 +136,7 @@ struct CObjUpval
|
||||
#define cosmoV_readCFunction(x) (((CObjCFunction *)cosmoV_readRef(x))->cfunc)
|
||||
#define cosmoV_readMethod(x) ((CObjMethod *)cosmoV_readRef(x))
|
||||
#define cosmoV_readClosure(x) ((CObjClosure *)cosmoV_readRef(x))
|
||||
#define cosmoV_readError(x) ((CObjError *)cosmoV_readRef(x))
|
||||
|
||||
#define cosmoO_readCString(x) ((CObjString *)x)->str
|
||||
#define cosmoO_readType(x) ((CObj *)x)->type
|
||||
@@ -145,7 +146,6 @@ static inline bool isObjType(CValue val, CObjType type)
|
||||
return IS_REF(val) && cosmoV_readRef(val)->type == type;
|
||||
}
|
||||
|
||||
// just protects against macro expansion
|
||||
static inline bool IS_CALLABLE(CValue val)
|
||||
{
|
||||
return IS_CLOSURE(val) || IS_CFUNCTION(val) || IS_METHOD(val);
|
||||
|
@@ -64,7 +64,7 @@ typedef enum
|
||||
OP_FALSE,
|
||||
OP_NIL,
|
||||
|
||||
OP_RETURN
|
||||
OP_RETURN,
|
||||
} COPCODE; // there can be a max of 256 instructions
|
||||
|
||||
#endif
|
||||
|
10
src/cosmo.h
10
src/cosmo.h
@@ -14,7 +14,15 @@
|
||||
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
|
||||
|
||||
/*
|
||||
NAN_BOXXED:
|
||||
if undefined, the interpreter will use a tagged union to store values. This is the default.
|
||||
Note that even though the sizeof(CValue) is 8 bytes for NAN_BOXXED (as opposed to 16 bytes for
|
||||
the tagged union) no performance benefits were measured. I recommend keeping this undefined for
|
||||
now.
|
||||
*/
|
||||
// #define NAN_BOXXED
|
||||
|
||||
// forward declare *most* stuff so our headers are cleaner
|
||||
|
94
src/cparse.c
94
src/cparse.c
@@ -59,14 +59,15 @@ typedef struct CCompilerState
|
||||
|
||||
typedef struct
|
||||
{
|
||||
CLexState lex;
|
||||
CState *state;
|
||||
CLexState *lex;
|
||||
CCompilerState *compiler;
|
||||
CObjString *module; // name of the module
|
||||
CToken current;
|
||||
CToken previous; // token right after the current token
|
||||
bool hadError;
|
||||
bool panic;
|
||||
CToken previous; // token right after the current token
|
||||
int workingStackCount; // we push CValues of objects we need onto the stack so the garbage
|
||||
// collector can see them. this is the count of those values so we'll
|
||||
// know how many to pop off when we're done
|
||||
} CParseState;
|
||||
|
||||
typedef enum
|
||||
@@ -109,6 +110,12 @@ static CObjFunction *endCompiler(CParseState *pstate);
|
||||
|
||||
// ================================================================ [FRONT END/TALK TO LEXER]
|
||||
|
||||
static void keepTrackOf(CParseState *pstate, CValue val)
|
||||
{
|
||||
pstate->workingStackCount++;
|
||||
cosmoV_pushValue(pstate->state, val);
|
||||
}
|
||||
|
||||
static void initCompilerState(CParseState *pstate, CCompilerState *ccstate, FunctionType type,
|
||||
CCompilerState *enclosing)
|
||||
{
|
||||
@@ -124,6 +131,8 @@ static void initCompilerState(CParseState *pstate, CCompilerState *ccstate, Func
|
||||
ccstate->function = cosmoO_newFunction(pstate->state);
|
||||
ccstate->function->module = pstate->module;
|
||||
|
||||
keepTrackOf(pstate, cosmoV_newRef((CObj *)ccstate->function));
|
||||
|
||||
ccstate->loop.scope = -1; // there is no loop yet
|
||||
|
||||
if (type != FTYPE_SCRIPT) {
|
||||
@@ -146,27 +155,27 @@ static void initCompilerState(CParseState *pstate, CCompilerState *ccstate, Func
|
||||
static void initParseState(CParseState *pstate, CCompilerState *ccstate, CState *s,
|
||||
const char *source, const char *module)
|
||||
{
|
||||
pstate->lex = cosmoL_newLexState(s, source);
|
||||
cosmoL_initLexState(s, &pstate->lex, source);
|
||||
|
||||
pstate->state = s;
|
||||
pstate->hadError = false;
|
||||
pstate->panic = false;
|
||||
pstate->compiler = ccstate;
|
||||
pstate->module = cosmoO_copyString(s, module, strlen(module));
|
||||
pstate->workingStackCount = 0;
|
||||
|
||||
keepTrackOf(pstate, cosmoV_newRef((CObj *)pstate->module));
|
||||
initCompilerState(pstate, ccstate, FTYPE_SCRIPT, NULL); // enclosing starts as NULL
|
||||
}
|
||||
|
||||
static void freeParseState(CParseState *pstate)
|
||||
{
|
||||
cosmoL_freeLexState(pstate->state, pstate->lex);
|
||||
cosmoL_cleanupLexState(pstate->state, &pstate->lex);
|
||||
|
||||
// pop our working values off the stack
|
||||
cosmoV_setTop(pstate->state, pstate->workingStackCount);
|
||||
}
|
||||
|
||||
static void errorAt(CParseState *pstate, CToken *token, const char *format, va_list args)
|
||||
{
|
||||
if (pstate->hadError)
|
||||
return;
|
||||
|
||||
if (token->type == TOKEN_EOF) {
|
||||
cosmoV_pushString(pstate->state, "At end: ");
|
||||
} else if (!(token->type == TOKEN_ERROR)) {
|
||||
@@ -177,14 +186,9 @@ static void errorAt(CParseState *pstate, CToken *token, const char *format, va_l
|
||||
|
||||
cosmoO_pushVFString(pstate->state, format, args);
|
||||
|
||||
cosmoV_concat(pstate->state, 2); // concats the two strings together
|
||||
|
||||
CObjError *err = cosmoV_throw(pstate->state);
|
||||
err->line = token->line;
|
||||
err->parserError = true;
|
||||
|
||||
pstate->hadError = true;
|
||||
pstate->panic = true;
|
||||
// throw complete error string
|
||||
cosmoV_concat(pstate->state, 2);
|
||||
cosmoV_throw(pstate->state);
|
||||
}
|
||||
|
||||
static void errorAtCurrent(CParseState *pstate, const char *format, ...)
|
||||
@@ -206,7 +210,7 @@ static void error(CParseState *pstate, const char *format, ...)
|
||||
static void advance(CParseState *pstate)
|
||||
{
|
||||
pstate->previous = pstate->current;
|
||||
pstate->current = cosmoL_scanToken(pstate->lex);
|
||||
pstate->current = cosmoL_scanToken(&pstate->lex);
|
||||
|
||||
if (pstate->current.type == TOKEN_ERROR) {
|
||||
errorAtCurrent(pstate, pstate->current.start);
|
||||
@@ -280,7 +284,6 @@ uint16_t makeConstant(CParseState *pstate, CValue val)
|
||||
int indx = addConstant(pstate->state, getChunk(pstate), val);
|
||||
if (indx > UINT16_MAX) {
|
||||
error(pstate, "UInt overflow! Too many constants in one chunk!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (uint16_t)indx;
|
||||
@@ -350,7 +353,6 @@ static void addLocal(CParseState *pstate, CToken name)
|
||||
{
|
||||
if (pstate->compiler->localCount > UINT8_MAX) {
|
||||
error(pstate, "UInt overflow! Too many locals in scope!");
|
||||
return;
|
||||
}
|
||||
|
||||
Local *local = &pstate->compiler->locals[pstate->compiler->localCount++];
|
||||
@@ -365,7 +367,6 @@ static int addUpvalue(CParseState *pstate, CCompilerState *ccstate, uint8_t indx
|
||||
|
||||
if (upvals > UINT8_MAX) {
|
||||
error(pstate, "UInt overflow! Too many upvalues in scope!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// check and make sure we haven't already captured it
|
||||
@@ -481,6 +482,7 @@ static void string(CParseState *pstate, bool canAssign, Precedence prec)
|
||||
{
|
||||
CObjString *strObj =
|
||||
cosmoO_takeString(pstate->state, pstate->previous.start, pstate->previous.length);
|
||||
keepTrackOf(pstate, cosmoV_newRef((CObj *)strObj));
|
||||
writeConstant(pstate, cosmoV_newRef((CObj *)strObj));
|
||||
}
|
||||
|
||||
@@ -768,7 +770,6 @@ static void table(CParseState *pstate, bool canAssign, Precedence prec)
|
||||
tblType = 1; // array-like
|
||||
} else {
|
||||
error(pstate, "Can't change table description type mid-definition!");
|
||||
return;
|
||||
}
|
||||
|
||||
entries++;
|
||||
@@ -818,7 +819,7 @@ static void object(CParseState *pstate, bool canAssign, Precedence prec)
|
||||
// "pop" the 1 value
|
||||
valuePopped(pstate, 1);
|
||||
entries++;
|
||||
} while (match(pstate, TOKEN_COMMA) && !pstate->hadError);
|
||||
} while (match(pstate, TOKEN_COMMA));
|
||||
|
||||
consume(pstate, TOKEN_RIGHT_BRACE, "Expected '}' to end object definition.");
|
||||
}
|
||||
@@ -1158,9 +1159,6 @@ static uint16_t parseVariable(CParseState *pstate, const char *errorMessage, boo
|
||||
|
||||
static void defineVariable(CParseState *pstate, uint16_t global, bool forceLocal)
|
||||
{
|
||||
if (pstate->hadError)
|
||||
return;
|
||||
|
||||
if (pstate->compiler->scopeDepth > 0 || forceLocal) {
|
||||
markInitialized(pstate, global);
|
||||
valuePopped(pstate, 1); // the local stays on the stack!
|
||||
@@ -1177,7 +1175,7 @@ static void _proto(CParseState *pstate)
|
||||
{
|
||||
int entries = 0;
|
||||
|
||||
while (!match(pstate, TOKEN_END) && !match(pstate, TOKEN_EOF) && !pstate->hadError) {
|
||||
while (!match(pstate, TOKEN_END) && !match(pstate, TOKEN_EOF)) {
|
||||
if (match(pstate, TOKEN_FUNC)) {
|
||||
// define method
|
||||
consume(pstate, TOKEN_IDENTIFIER, "Expected identifier for method!");
|
||||
@@ -1224,9 +1222,6 @@ static void localProto(CParseState *pstate)
|
||||
|
||||
static void popLocals(CParseState *pstate, int toScope)
|
||||
{
|
||||
if (pstate->hadError)
|
||||
return;
|
||||
|
||||
// count the locals in scope to pop
|
||||
int localsToPop = 0;
|
||||
|
||||
@@ -1376,7 +1371,7 @@ static void endLoop(CParseState *pstate)
|
||||
patchJmp(pstate, pstate->compiler->loop.breaks[--pstate->compiler->loop.breakCount]);
|
||||
}
|
||||
|
||||
cosmoM_freearray(pstate->state, int, pstate->compiler->loop.breaks,
|
||||
cosmoM_freeArray(pstate->state, int, pstate->compiler->loop.breaks,
|
||||
pstate->compiler->loop.breakCapacity);
|
||||
}
|
||||
|
||||
@@ -1665,7 +1660,7 @@ static void breakStatement(CParseState *pstate)
|
||||
pstate->compiler->localCount = savedLocals;
|
||||
|
||||
// add break to loop
|
||||
cosmoM_growarray(pstate->state, int, pstate->compiler->loop.breaks,
|
||||
cosmoM_growArray(pstate->state, int, pstate->compiler->loop.breaks,
|
||||
pstate->compiler->loop.breakCount, pstate->compiler->loop.breakCapacity);
|
||||
pstate->compiler->loop.breaks[pstate->compiler->loop.breakCount++] = writeJmp(pstate, OP_JMP);
|
||||
}
|
||||
@@ -1686,18 +1681,6 @@ static void continueStatement(CParseState *pstate)
|
||||
writeJmpBack(pstate, pstate->compiler->loop.startBytecode);
|
||||
}
|
||||
|
||||
static void synchronize(CParseState *pstate)
|
||||
{
|
||||
pstate->panic = false;
|
||||
|
||||
while (pstate->current.type != TOKEN_EOF) {
|
||||
if (pstate->previous.type == TOKEN_EOS)
|
||||
return;
|
||||
|
||||
advance(pstate);
|
||||
}
|
||||
}
|
||||
|
||||
static int expressionPrecedence(CParseState *pstate, int needed, Precedence prec, bool forceNeeded)
|
||||
{
|
||||
int lastExpected = pstate->compiler->expectedValues;
|
||||
@@ -1787,10 +1770,6 @@ static void statement(CParseState *pstate)
|
||||
static void declaration(CParseState *pstate)
|
||||
{
|
||||
statement(pstate);
|
||||
|
||||
// if we paniced, skip the whole statement!
|
||||
if (pstate->panic)
|
||||
synchronize(pstate);
|
||||
}
|
||||
|
||||
static CObjFunction *endCompiler(CParseState *pstate)
|
||||
@@ -1812,7 +1791,6 @@ CObjFunction *cosmoP_compileString(CState *state, const char *source, const char
|
||||
{
|
||||
CParseState parser;
|
||||
CCompilerState compiler;
|
||||
cosmoM_freezeGC(state); // ignore all GC events while compiling
|
||||
initParseState(&parser, &compiler, state, source, module);
|
||||
|
||||
advance(&parser);
|
||||
@@ -1825,24 +1803,10 @@ CObjFunction *cosmoP_compileString(CState *state, const char *source, const char
|
||||
|
||||
popLocals(&parser, 0);
|
||||
|
||||
if (parser.hadError) { // we don't free the function, the state already has a reference to it in
|
||||
// it's linked list of objects!
|
||||
endCompiler(&parser);
|
||||
freeParseState(&parser);
|
||||
|
||||
cosmoM_unfreezeGC(state);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CObjFunction *resFunc = compiler.function;
|
||||
|
||||
// finally free out parser states
|
||||
endCompiler(&parser);
|
||||
freeParseState(&parser);
|
||||
|
||||
// push the funciton onto the stack so if we cause an GC event, it won't be free'd
|
||||
cosmoV_pushRef(state, (CObj *)resFunc);
|
||||
cosmoM_unfreezeGC(state);
|
||||
cosmoV_pop(state);
|
||||
return resFunc;
|
||||
}
|
||||
|
@@ -4,8 +4,7 @@
|
||||
#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
|
||||
CObjFunction *cosmoP_compileString(CState *state, const char *source, const char *module);
|
||||
|
||||
#endif
|
||||
|
47
src/cstate.c
47
src/cstate.c
@@ -7,6 +7,25 @@
|
||||
|
||||
#include <string.h>
|
||||
|
||||
CPanic *cosmoV_newPanic(CState *state)
|
||||
{
|
||||
CPanic *panic = cosmoM_xmalloc(state, sizeof(CPanic));
|
||||
panic->top = state->top;
|
||||
panic->frameCount = state->frameCount;
|
||||
panic->prev = state->panic;
|
||||
state->panic = panic;
|
||||
|
||||
return panic;
|
||||
}
|
||||
|
||||
void cosmoV_freePanic(CState *state)
|
||||
{
|
||||
CPanic *panic = state->panic;
|
||||
state->panic = panic->prev;
|
||||
|
||||
cosmoM_free(state, CPanic, panic);
|
||||
}
|
||||
|
||||
CState *cosmoV_newState()
|
||||
{
|
||||
// we use C's malloc because we don't want to trigger a GC with an invalid state
|
||||
@@ -17,8 +36,8 @@ CState *cosmoV_newState()
|
||||
exit(1);
|
||||
}
|
||||
|
||||
state->panic = false;
|
||||
state->freezeGC = 1; // we start frozen
|
||||
state->panic = NULL;
|
||||
|
||||
// GC
|
||||
state->objects = NULL;
|
||||
@@ -26,7 +45,7 @@ CState *cosmoV_newState()
|
||||
state->grayStack.count = 0;
|
||||
state->grayStack.capacity = 2;
|
||||
state->grayStack.array = NULL;
|
||||
state->allocatedBytes = sizeof(CState);
|
||||
state->allocatedBytes = 0;
|
||||
state->nextGC = 1024 * 8; // threshhold starts at 8kb
|
||||
|
||||
// init stack
|
||||
@@ -34,8 +53,6 @@ CState *cosmoV_newState()
|
||||
state->frameCount = 0;
|
||||
state->openUpvalues = NULL;
|
||||
|
||||
state->error = NULL;
|
||||
|
||||
// set default proto objects
|
||||
for (int i = 0; i < COBJ_MAX; i++)
|
||||
state->protoObjects[i] = NULL;
|
||||
@@ -80,12 +97,17 @@ void cosmoV_freeState(CState *state)
|
||||
#ifdef GC_DEBUG
|
||||
printf("state %p is being free'd!\n", state);
|
||||
#endif
|
||||
cosmoM_freezeGC(state);
|
||||
|
||||
// frees all the objects
|
||||
CObj *objs = state->objects;
|
||||
while (objs != NULL) {
|
||||
CObj *next = objs->next;
|
||||
|
||||
#ifdef GC_DEBUG
|
||||
printf("STATE FREEING %p\n", objs);
|
||||
fflush(stdout);
|
||||
#endif
|
||||
|
||||
cosmoO_free(state, objs);
|
||||
objs = next;
|
||||
}
|
||||
@@ -98,15 +120,14 @@ void cosmoV_freeState(CState *state)
|
||||
cosmoT_clearTable(state, &state->strings);
|
||||
|
||||
// free our gray stack & finally free the state structure
|
||||
cosmoM_freearray(state, CObj *, state->grayStack.array, state->grayStack.capacity);
|
||||
cosmoM_freeArray(state, CObj *, state->grayStack.array, state->grayStack.capacity);
|
||||
|
||||
#ifdef GC_DEBUG
|
||||
if (state->allocatedBytes != 0) {
|
||||
printf("state->allocatedBytes doesn't match, got %lu\n", state->allocatedBytes);
|
||||
}
|
||||
#endif
|
||||
|
||||
// TODO: yeah idk, it looks like im missing 520 bytes somewhere? i'll look into it later
|
||||
/*#ifdef GC_DEBUG
|
||||
if (state->allocatedBytes != sizeof(CState)) {
|
||||
printf("state->allocatedBytes doesn't match expected value (%lu), got %lu!",
|
||||
sizeof(CState), state->allocatedBytes); exit(0);
|
||||
}
|
||||
#endif*/
|
||||
free(state);
|
||||
}
|
||||
|
||||
|
55
src/cstate.h
55
src/cstate.h
@@ -6,6 +6,8 @@
|
||||
#include "ctable.h"
|
||||
#include "cvalue.h"
|
||||
|
||||
#include <setjmp.h>
|
||||
|
||||
struct CCallFrame
|
||||
{
|
||||
CObjClosure *closure;
|
||||
@@ -38,36 +40,47 @@ typedef struct ArrayCObj
|
||||
int capacity;
|
||||
} ArrayCObj;
|
||||
|
||||
typedef struct CPanic
|
||||
{
|
||||
jmp_buf jmp;
|
||||
StkPtr top;
|
||||
struct CPanic *prev;
|
||||
int frameCount;
|
||||
} CPanic;
|
||||
|
||||
struct CState
|
||||
{
|
||||
bool panic;
|
||||
int freezeGC; // when > 0, GC events will be ignored (for internal use)
|
||||
int frameCount;
|
||||
|
||||
CObjError *error; // NULL, unless panic is true
|
||||
CObj *objects; // tracks all of our allocated objects
|
||||
CObj *userRoots; // user definable roots, this holds CObjs that should be considered "roots",
|
||||
// lets the VM know you are holding a reference to a CObj in your code
|
||||
ArrayCObj grayStack; // keeps track of which objects *haven't yet* been traversed in our GC, but
|
||||
// *have been* found
|
||||
size_t allocatedBytes;
|
||||
size_t nextGC; // when allocatedBytes reaches this threshhold, trigger a GC event
|
||||
|
||||
CObjUpval *openUpvalues; // tracks all of our still open (meaning still on the stack) upvalues
|
||||
CTable strings;
|
||||
CObjTable *globals;
|
||||
|
||||
CValue *top; // top of the stack
|
||||
CObjObject *protoObjects[COBJ_MAX]; // proto object for each COBJ type [NULL = no default proto]
|
||||
CObjString *iStrings[ISTRING_MAX]; // strings used internally by the VM, eg. __init, __index
|
||||
CCallFrame callFrame[FRAME_MAX]; // call frames
|
||||
CValue stack[STACK_MAX]; // stack
|
||||
CObjObject *protoObjects[COBJ_MAX]; // proto object for each COBJ type [NULL = no default proto]
|
||||
CObjString *iStrings[ISTRING_MAX]; // strings used internally by the VM, eg. __init, __index
|
||||
CTable strings;
|
||||
ArrayCObj grayStack; // keeps track of which objects *haven't yet* been traversed in our GC, but
|
||||
// *have been* found
|
||||
|
||||
CObjUpval *openUpvalues; // tracks all of our still open (meaning still on the stack) upvalues
|
||||
CObjTable *globals;
|
||||
CValue *top; // top of the stack
|
||||
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
|
||||
CPanic *panic;
|
||||
|
||||
int freezeGC; // when > 0, GC events will be ignored (for internal use)
|
||||
int frameCount;
|
||||
size_t allocatedBytes;
|
||||
size_t nextGC; // when allocatedBytes reaches this threshhold, trigger a GC event
|
||||
};
|
||||
|
||||
CPanic *cosmoV_newPanic(CState *state);
|
||||
void cosmoV_freePanic(CState *state);
|
||||
|
||||
COSMO_API CState *cosmoV_newState();
|
||||
COSMO_API void cosmoV_freeState(CState *state);
|
||||
|
||||
// expects 2*pairs values on the stack, each pair should consist of 1 key and 1 value
|
||||
COSMO_API void cosmoV_register(CState *state, int pairs);
|
||||
COSMO_API void cosmoV_freeState(CState *state);
|
||||
|
||||
COSMO_API void cosmoV_printStack(CState *state);
|
||||
|
||||
#endif
|
||||
|
50
src/ctable.c
50
src/ctable.c
@@ -11,7 +11,7 @@
|
||||
#define MIN_TABLE_CAPACITY ARRAY_START
|
||||
|
||||
// bit-twiddling hacks, gets the next power of 2
|
||||
unsigned int nextPow2(unsigned int x)
|
||||
static unsigned int nextPow2(unsigned int x)
|
||||
{
|
||||
if (x <= ARRAY_START - 1)
|
||||
return ARRAY_START; // sanity check
|
||||
@@ -34,6 +34,7 @@ void cosmoT_initTable(CState *state, CTable *tbl, int startCap)
|
||||
tbl->capacityMask = startCap - 1;
|
||||
tbl->count = 0;
|
||||
tbl->tombstones = 0;
|
||||
tbl->tombThreshold = 32;
|
||||
tbl->table = NULL; // to let out GC know we're initalizing
|
||||
tbl->table = cosmoM_xmalloc(state, sizeof(CTableEntry) * startCap);
|
||||
|
||||
@@ -46,23 +47,24 @@ void cosmoT_initTable(CState *state, CTable *tbl, int startCap)
|
||||
|
||||
void cosmoT_addTable(CState *state, CTable *from, CTable *to)
|
||||
{
|
||||
int cap = from->capacityMask + 1;
|
||||
CTableEntry *entry;
|
||||
int cap = cosmoT_getCapacity(from);
|
||||
|
||||
for (int i = 0; i < cap; i++) {
|
||||
CTableEntry *entry = &from->table[i];
|
||||
entry = &from->table[i];
|
||||
|
||||
if (!(IS_NIL(entry->key))) {
|
||||
CValue *newVal = cosmoT_insert(state, to, entry->key);
|
||||
*newVal = entry->val;
|
||||
*cosmoT_insert(state, to, entry->key) = entry->val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cosmoT_clearTable(CState *state, CTable *tbl)
|
||||
{
|
||||
cosmoM_freearray(state, CTableEntry, tbl->table, (tbl->capacityMask + 1));
|
||||
cosmoM_freeArray(state, CTableEntry, tbl->table, cosmoT_getCapacity(tbl));
|
||||
}
|
||||
|
||||
uint32_t getObjectHash(CObj *obj)
|
||||
static uint32_t getObjectHash(CObj *obj)
|
||||
{
|
||||
switch (obj->type) {
|
||||
case COBJ_STRING:
|
||||
@@ -72,7 +74,7 @@ uint32_t getObjectHash(CObj *obj)
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t getValueHash(CValue *val)
|
||||
static uint32_t getValueHash(CValue *val)
|
||||
{
|
||||
switch (GET_TYPE(*val)) {
|
||||
case COSMO_TREF:
|
||||
@@ -112,10 +114,10 @@ static CTableEntry *findEntry(CState *state, CTableEntry *entries, int mask, CVa
|
||||
if (IS_NIL(entry->val)) {
|
||||
// it's empty! if we found a tombstone, return that so it'll be reused
|
||||
return tomb != NULL ? tomb : entry;
|
||||
} else {
|
||||
// its a tombstone!
|
||||
tomb = entry;
|
||||
}
|
||||
|
||||
// its a tombstone!
|
||||
tomb = entry;
|
||||
} else if (cosmoV_equal(state, entry->key, key)) {
|
||||
return entry;
|
||||
}
|
||||
@@ -140,7 +142,7 @@ static void resizeTbl(CState *state, CTable *tbl, int newCapacity, bool canShrin
|
||||
return;
|
||||
|
||||
CTableEntry *entries = cosmoM_xmalloc(state, size);
|
||||
oldCap = tbl->capacityMask + 1;
|
||||
oldCap = cosmoT_getCapacity(tbl);
|
||||
newCount = 0;
|
||||
|
||||
// set all nodes as NIL : NIL
|
||||
@@ -163,7 +165,7 @@ static void resizeTbl(CState *state, CTable *tbl, int newCapacity, bool canShrin
|
||||
}
|
||||
|
||||
// free the old table
|
||||
cosmoM_freearray(state, CTableEntry, tbl->table, oldCap);
|
||||
cosmoM_freeArray(state, CTableEntry, tbl->table, oldCap);
|
||||
|
||||
tbl->table = entries;
|
||||
tbl->capacityMask = newCapacity - 1;
|
||||
@@ -175,10 +177,10 @@ bool cosmoT_checkShrink(CState *state, CTable *tbl)
|
||||
{
|
||||
// if count > 8 and active entries < tombstones
|
||||
if (tbl->count > MIN_TABLE_CAPACITY &&
|
||||
(tbl->count - tbl->tombstones < tbl->tombstones ||
|
||||
tbl->tombstones > 50)) { // TODO: 50 should be a threshhold
|
||||
resizeTbl(state, tbl, nextPow2(tbl->count - tbl->tombstones) * GROW_FACTOR,
|
||||
false); // shrink based on active entries to the next pow of 2
|
||||
(tbl->count - tbl->tombstones < tbl->tombstones || tbl->tombstones > tbl->tombThreshold)) {
|
||||
// shrink based on active entries to the next pow of 2
|
||||
resizeTbl(state, tbl, nextPow2(tbl->count - tbl->tombstones) * GROW_FACTOR, false);
|
||||
tbl->tombThreshold = tbl->count / 4;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -189,7 +191,7 @@ bool cosmoT_checkShrink(CState *state, CTable *tbl)
|
||||
COSMO_API CValue *cosmoT_insert(CState *state, CTable *tbl, CValue key)
|
||||
{
|
||||
// make sure we have enough space allocated
|
||||
int cap = tbl->capacityMask + 1;
|
||||
int cap = cosmoT_getCapacity(tbl);
|
||||
if (tbl->count + 1 > (int)(cap * MAX_TABLE_FILL)) {
|
||||
// grow table
|
||||
int newCap = cap * GROW_FACTOR;
|
||||
@@ -197,8 +199,7 @@ COSMO_API CValue *cosmoT_insert(CState *state, CTable *tbl, CValue key)
|
||||
}
|
||||
|
||||
// insert into the table
|
||||
CTableEntry *entry =
|
||||
findEntry(state, tbl->table, tbl->capacityMask, key); // -1 for our capacity mask
|
||||
CTableEntry *entry = findEntry(state, tbl->table, tbl->capacityMask, key);
|
||||
|
||||
if (IS_NIL(entry->key)) {
|
||||
if (IS_NIL(entry->val)) // is it empty?
|
||||
@@ -254,9 +255,10 @@ CObjString *cosmoT_lookupString(CTable *tbl, const char *str, int length, uint32
|
||||
{
|
||||
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
|
||||
|
||||
// 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 & tbl->capacityMask;
|
||||
|
||||
// keep looking for an open slot in the entries array
|
||||
while (true) {
|
||||
@@ -279,7 +281,7 @@ CObjString *cosmoT_lookupString(CTable *tbl, const char *str, int length, uint32
|
||||
void cosmoT_printTable(CTable *tbl, const char *name)
|
||||
{
|
||||
printf("==== [[%s]] ====\n", name);
|
||||
int cap = tbl->capacityMask + 1;
|
||||
int cap = cosmoT_getCapacity(tbl);
|
||||
for (int i = 0; i < cap; i++) {
|
||||
CTableEntry *entry = &tbl->table[i];
|
||||
if (!(IS_NIL(entry->key))) {
|
||||
|
@@ -18,9 +18,12 @@ typedef struct CTable
|
||||
int count;
|
||||
int capacityMask; // +1 to get the capacity
|
||||
int tombstones;
|
||||
int tombThreshold;
|
||||
CTableEntry *table;
|
||||
} CTable;
|
||||
|
||||
#define cosmoT_getCapacity(tbl) ((tbl)->capacityMask + 1)
|
||||
|
||||
COSMO_API void cosmoT_initTable(CState *state, CTable *tbl, int startCap);
|
||||
COSMO_API void cosmoT_clearTable(CState *state, CTable *tbl);
|
||||
COSMO_API int cosmoT_count(CTable *tbl);
|
||||
|
@@ -13,12 +13,12 @@ void initValArray(CState *state, CValueArray *val, size_t startCapacity)
|
||||
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
34
src/cvm.h
34
src/cvm.h
@@ -8,16 +8,25 @@
|
||||
|
||||
// #define VM_DEBUG
|
||||
|
||||
typedef enum
|
||||
{
|
||||
COSMOVM_OK,
|
||||
COSMOVM_RUNTIME_ERR,
|
||||
COSMOVM_BUILDTIME_ERR
|
||||
} COSMOVMRESULT;
|
||||
/*
|
||||
if we're using GNUC or clang, we can use computed gotos which speeds up
|
||||
cosmoV_execute by about 20% from benchmarking. of course, if you know
|
||||
your compiler supports computed gotos, you can define VM_JUMPTABLE
|
||||
|
||||
although, this is disabled when VM_DEBUG is defined, since it can cause
|
||||
issues with debugging
|
||||
|
||||
BTW: be weary of maliciously crafted cosmo dumps!! it's very easy to crash
|
||||
cosmo with this enabled and reading invalid opcodes due to us just using the
|
||||
opcode as an index into the jump table
|
||||
*/
|
||||
#if (defined(__GNUC__) || defined(__clang__)) && !defined(VM_DEBUG)
|
||||
# define VM_JUMPTABLE
|
||||
#endif
|
||||
|
||||
// args = # of pass parameters, nresults = # of expected results
|
||||
COSMO_API COSMOVMRESULT cosmoV_call(CState *state, int args, int nresults);
|
||||
COSMO_API COSMOVMRESULT cosmoV_pcall(CState *state, int args, int nresults);
|
||||
COSMO_API void cosmoV_call(CState *state, int args, int nresults);
|
||||
COSMO_API bool cosmoV_pcall(CState *state, int args, int nresults);
|
||||
|
||||
// pushes new object onto the stack & returns a pointer to the new object
|
||||
COSMO_API CObjObject *cosmoV_makeObject(CState *state, int pairs);
|
||||
@@ -25,7 +34,7 @@ COSMO_API void cosmoV_makeTable(CState *state, int pairs);
|
||||
COSMO_API void cosmoV_concat(CState *state, int vals);
|
||||
COSMO_API void cosmoV_pushFString(CState *state, const char *format, ...);
|
||||
COSMO_API void cosmoV_printError(CState *state, CObjError *err);
|
||||
COSMO_API CObjError *cosmoV_throw(CState *state);
|
||||
COSMO_API void cosmoV_throw(CState *state);
|
||||
COSMO_API void cosmoV_error(CState *state, const char *format, ...);
|
||||
COSMO_API void cosmo_insert(CState *state, int indx, CValue val);
|
||||
|
||||
@@ -91,13 +100,6 @@ static inline void cosmoV_pushValue(CState *state, CValue val)
|
||||
|
||||
// we reserve 8 slots for the error string and whatever c api we might be in
|
||||
if (stackSize >= STACK_MAX - 8) {
|
||||
if (state->panic) { // we're in a panic state, let the 8 reserved slots be filled
|
||||
if (stackSize < STACK_MAX)
|
||||
*(state->top++) = val;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
cosmoV_error(state, "Stack overflow!");
|
||||
return;
|
||||
}
|
||||
|
653
util/getopt.h
Normal file
653
util/getopt.h
Normal file
@@ -0,0 +1,653 @@
|
||||
#ifndef __GETOPT_H__
|
||||
/**
|
||||
* DISCLAIMER
|
||||
* This file is part of the mingw-w64 runtime package.
|
||||
*
|
||||
* The mingw-w64 runtime package and its code is distributed in the hope that it
|
||||
* will be useful but WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESSED OR
|
||||
* IMPLIED ARE HEREBY DISCLAIMED. This includes but is not limited to
|
||||
* warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2002 Todd C. Miller <Todd.Miller@courtesan.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* Sponsored in part by the Defense Advanced Research Projects
|
||||
* Agency (DARPA) and Air Force Research Laboratory, Air Force
|
||||
* Materiel Command, USAF, under agreement number F39502-99-1-0512.
|
||||
*/
|
||||
/*-
|
||||
* Copyright (c) 2000 The NetBSD Foundation, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to The NetBSD Foundation
|
||||
* by Dieter Baron and Thomas Klausner.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#pragma warning(disable:4996)
|
||||
|
||||
#define __GETOPT_H__
|
||||
|
||||
/* All the headers include this file. */
|
||||
#include <crtdefs.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <windows.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define REPLACE_GETOPT /* use this getopt as the system getopt(3) */
|
||||
|
||||
#ifdef REPLACE_GETOPT
|
||||
int opterr = 1; /* if error message should be printed */
|
||||
int optind = 1; /* index into parent argv vector */
|
||||
int optopt = '?'; /* character checked for validity */
|
||||
#undef optreset /* see getopt.h */
|
||||
#define optreset __mingw_optreset
|
||||
int optreset; /* reset getopt */
|
||||
char *optarg; /* argument associated with option */
|
||||
#endif
|
||||
|
||||
//extern int optind; /* index of first non-option in argv */
|
||||
//extern int optopt; /* single option character, as parsed */
|
||||
//extern int opterr; /* flag to enable built-in diagnostics... */
|
||||
// /* (user may set to zero, to suppress) */
|
||||
//
|
||||
//extern char *optarg; /* pointer to argument of current option */
|
||||
|
||||
#define PRINT_ERROR ((opterr) && (*options != ':'))
|
||||
|
||||
#define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */
|
||||
#define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */
|
||||
#define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */
|
||||
|
||||
/* return values */
|
||||
#define BADCH (int)'?'
|
||||
#define BADARG ((*options == ':') ? (int)':' : (int)'?')
|
||||
#define INORDER (int)1
|
||||
|
||||
#ifndef __CYGWIN__
|
||||
#define __progname __argv[0]
|
||||
#else
|
||||
extern char __declspec(dllimport) *__progname;
|
||||
#endif
|
||||
|
||||
#ifdef __CYGWIN__
|
||||
static char EMSG[] = "";
|
||||
#else
|
||||
#define EMSG ""
|
||||
#endif
|
||||
|
||||
static int getopt_internal(int, char * const *, const char *,
|
||||
const struct option *, int *, int);
|
||||
static int parse_long_options(char * const *, const char *,
|
||||
const struct option *, int *, int);
|
||||
static int gcd(int, int);
|
||||
static void permute_args(int, int, int, char * const *);
|
||||
|
||||
static char *place = EMSG; /* option letter processing */
|
||||
|
||||
/* XXX: set optreset to 1 rather than these two */
|
||||
static int nonopt_start = -1; /* first non option argument (for permute) */
|
||||
static int nonopt_end = -1; /* first option after non options (for permute) */
|
||||
|
||||
/* Error messages */
|
||||
static const char recargchar[] = "option requires an argument -- %c";
|
||||
static const char recargstring[] = "option requires an argument -- %s";
|
||||
static const char ambig[] = "ambiguous option -- %.*s";
|
||||
static const char noarg[] = "option doesn't take an argument -- %.*s";
|
||||
static const char illoptchar[] = "unknown option -- %c";
|
||||
static const char illoptstring[] = "unknown option -- %s";
|
||||
|
||||
static void
|
||||
_vwarnx(const char *fmt,va_list ap)
|
||||
{
|
||||
(void)fprintf(stderr,"%s: ",__progname);
|
||||
if (fmt != NULL)
|
||||
(void)vfprintf(stderr,fmt,ap);
|
||||
(void)fprintf(stderr,"\n");
|
||||
}
|
||||
|
||||
static void
|
||||
warnx(const char *fmt,...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap,fmt);
|
||||
_vwarnx(fmt,ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
/*
|
||||
* Compute the greatest common divisor of a and b.
|
||||
*/
|
||||
static int
|
||||
gcd(int a, int b)
|
||||
{
|
||||
int c;
|
||||
|
||||
c = a % b;
|
||||
while (c != 0) {
|
||||
a = b;
|
||||
b = c;
|
||||
c = a % b;
|
||||
}
|
||||
|
||||
return (b);
|
||||
}
|
||||
|
||||
/*
|
||||
* Exchange the block from nonopt_start to nonopt_end with the block
|
||||
* from nonopt_end to opt_end (keeping the same order of arguments
|
||||
* in each block).
|
||||
*/
|
||||
static void
|
||||
permute_args(int panonopt_start, int panonopt_end, int opt_end,
|
||||
char * const *nargv)
|
||||
{
|
||||
int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
|
||||
char *swap;
|
||||
|
||||
/*
|
||||
* compute lengths of blocks and number and size of cycles
|
||||
*/
|
||||
nnonopts = panonopt_end - panonopt_start;
|
||||
nopts = opt_end - panonopt_end;
|
||||
ncycle = gcd(nnonopts, nopts);
|
||||
cyclelen = (opt_end - panonopt_start) / ncycle;
|
||||
|
||||
for (i = 0; i < ncycle; i++) {
|
||||
cstart = panonopt_end+i;
|
||||
pos = cstart;
|
||||
for (j = 0; j < cyclelen; j++) {
|
||||
if (pos >= panonopt_end)
|
||||
pos -= nnonopts;
|
||||
else
|
||||
pos += nopts;
|
||||
swap = nargv[pos];
|
||||
/* LINTED const cast */
|
||||
((char **) nargv)[pos] = nargv[cstart];
|
||||
/* LINTED const cast */
|
||||
((char **)nargv)[cstart] = swap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef REPLACE_GETOPT
|
||||
/*
|
||||
* getopt --
|
||||
* Parse argc/argv argument vector.
|
||||
*
|
||||
* [eventually this will replace the BSD getopt]
|
||||
*/
|
||||
int
|
||||
getopt(int nargc, char * const *nargv, const char *options)
|
||||
{
|
||||
|
||||
/*
|
||||
* We don't pass FLAG_PERMUTE to getopt_internal() since
|
||||
* the BSD getopt(3) (unlike GNU) has never done this.
|
||||
*
|
||||
* Furthermore, since many privileged programs call getopt()
|
||||
* before dropping privileges it makes sense to keep things
|
||||
* as simple (and bug-free) as possible.
|
||||
*/
|
||||
return (getopt_internal(nargc, nargv, options, NULL, NULL, 0));
|
||||
}
|
||||
#endif /* REPLACE_GETOPT */
|
||||
|
||||
//extern int getopt(int nargc, char * const *nargv, const char *options);
|
||||
|
||||
#ifdef _BSD_SOURCE
|
||||
/*
|
||||
* BSD adds the non-standard `optreset' feature, for reinitialisation
|
||||
* of `getopt' parsing. We support this feature, for applications which
|
||||
* proclaim their BSD heritage, before including this header; however,
|
||||
* to maintain portability, developers are advised to avoid it.
|
||||
*/
|
||||
# define optreset __mingw_optreset
|
||||
extern int optreset;
|
||||
#endif
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
/*
|
||||
* POSIX requires the `getopt' API to be specified in `unistd.h';
|
||||
* thus, `unistd.h' includes this header. However, we do not want
|
||||
* to expose the `getopt_long' or `getopt_long_only' APIs, when
|
||||
* included in this manner. Thus, close the standard __GETOPT_H__
|
||||
* declarations block, and open an additional __GETOPT_LONG_H__
|
||||
* specific block, only when *not* __UNISTD_H_SOURCED__, in which
|
||||
* to declare the extended API.
|
||||
*/
|
||||
#endif /* !defined(__GETOPT_H__) */
|
||||
|
||||
#if !defined(__UNISTD_H_SOURCED__) && !defined(__GETOPT_LONG_H__)
|
||||
#define __GETOPT_LONG_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct option /* specification for a long form option... */
|
||||
{
|
||||
const char *name; /* option name, without leading hyphens */
|
||||
int has_arg; /* does it take an argument? */
|
||||
int *flag; /* where to save its status, or NULL */
|
||||
int val; /* its associated status value */
|
||||
};
|
||||
|
||||
enum /* permitted values for its `has_arg' field... */
|
||||
{
|
||||
no_argument = 0, /* option never takes an argument */
|
||||
required_argument, /* option always requires an argument */
|
||||
optional_argument /* option may take an argument */
|
||||
};
|
||||
|
||||
/*
|
||||
* parse_long_options --
|
||||
* Parse long options in argc/argv argument vector.
|
||||
* Returns -1 if short_too is set and the option does not match long_options.
|
||||
*/
|
||||
static int
|
||||
parse_long_options(char * const *nargv, const char *options,
|
||||
const struct option *long_options, int *idx, int short_too)
|
||||
{
|
||||
char *current_argv, *has_equal;
|
||||
size_t current_argv_len;
|
||||
int i, ambiguous, match;
|
||||
|
||||
#define IDENTICAL_INTERPRETATION(_x, _y) \
|
||||
(long_options[(_x)].has_arg == long_options[(_y)].has_arg && \
|
||||
long_options[(_x)].flag == long_options[(_y)].flag && \
|
||||
long_options[(_x)].val == long_options[(_y)].val)
|
||||
|
||||
current_argv = place;
|
||||
match = -1;
|
||||
ambiguous = 0;
|
||||
|
||||
optind++;
|
||||
|
||||
if ((has_equal = strchr(current_argv, '=')) != NULL) {
|
||||
/* argument found (--option=arg) */
|
||||
current_argv_len = has_equal - current_argv;
|
||||
has_equal++;
|
||||
} else
|
||||
current_argv_len = strlen(current_argv);
|
||||
|
||||
for (i = 0; long_options[i].name; i++) {
|
||||
/* find matching long option */
|
||||
if (strncmp(current_argv, long_options[i].name,
|
||||
current_argv_len))
|
||||
continue;
|
||||
|
||||
if (strlen(long_options[i].name) == current_argv_len) {
|
||||
/* exact match */
|
||||
match = i;
|
||||
ambiguous = 0;
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* If this is a known short option, don't allow
|
||||
* a partial match of a single character.
|
||||
*/
|
||||
if (short_too && current_argv_len == 1)
|
||||
continue;
|
||||
|
||||
if (match == -1) /* partial match */
|
||||
match = i;
|
||||
else if (!IDENTICAL_INTERPRETATION(i, match))
|
||||
ambiguous = 1;
|
||||
}
|
||||
if (ambiguous) {
|
||||
/* ambiguous abbreviation */
|
||||
if (PRINT_ERROR)
|
||||
warnx(ambig, (int)current_argv_len,
|
||||
current_argv);
|
||||
optopt = 0;
|
||||
return (BADCH);
|
||||
}
|
||||
if (match != -1) { /* option found */
|
||||
if (long_options[match].has_arg == no_argument
|
||||
&& has_equal) {
|
||||
if (PRINT_ERROR)
|
||||
warnx(noarg, (int)current_argv_len,
|
||||
current_argv);
|
||||
/*
|
||||
* XXX: GNU sets optopt to val regardless of flag
|
||||
*/
|
||||
if (long_options[match].flag == NULL)
|
||||
optopt = long_options[match].val;
|
||||
else
|
||||
optopt = 0;
|
||||
return (BADARG);
|
||||
}
|
||||
if (long_options[match].has_arg == required_argument ||
|
||||
long_options[match].has_arg == optional_argument) {
|
||||
if (has_equal)
|
||||
optarg = has_equal;
|
||||
else if (long_options[match].has_arg ==
|
||||
required_argument) {
|
||||
/*
|
||||
* optional argument doesn't use next nargv
|
||||
*/
|
||||
optarg = nargv[optind++];
|
||||
}
|
||||
}
|
||||
if ((long_options[match].has_arg == required_argument)
|
||||
&& (optarg == NULL)) {
|
||||
/*
|
||||
* Missing argument; leading ':' indicates no error
|
||||
* should be generated.
|
||||
*/
|
||||
if (PRINT_ERROR)
|
||||
warnx(recargstring,
|
||||
current_argv);
|
||||
/*
|
||||
* XXX: GNU sets optopt to val regardless of flag
|
||||
*/
|
||||
if (long_options[match].flag == NULL)
|
||||
optopt = long_options[match].val;
|
||||
else
|
||||
optopt = 0;
|
||||
--optind;
|
||||
return (BADARG);
|
||||
}
|
||||
} else { /* unknown option */
|
||||
if (short_too) {
|
||||
--optind;
|
||||
return (-1);
|
||||
}
|
||||
if (PRINT_ERROR)
|
||||
warnx(illoptstring, current_argv);
|
||||
optopt = 0;
|
||||
return (BADCH);
|
||||
}
|
||||
if (idx)
|
||||
*idx = match;
|
||||
if (long_options[match].flag) {
|
||||
*long_options[match].flag = long_options[match].val;
|
||||
return (0);
|
||||
} else
|
||||
return (long_options[match].val);
|
||||
#undef IDENTICAL_INTERPRETATION
|
||||
}
|
||||
|
||||
/*
|
||||
* getopt_internal --
|
||||
* Parse argc/argv argument vector. Called by user level routines.
|
||||
*/
|
||||
static int
|
||||
getopt_internal(int nargc, char * const *nargv, const char *options,
|
||||
const struct option *long_options, int *idx, int flags)
|
||||
{
|
||||
char *oli; /* option letter list index */
|
||||
int optchar, short_too;
|
||||
static int posixly_correct = -1;
|
||||
|
||||
if (options == NULL)
|
||||
return (-1);
|
||||
|
||||
/*
|
||||
* XXX Some GNU programs (like cvs) set optind to 0 instead of
|
||||
* XXX using optreset. Work around this braindamage.
|
||||
*/
|
||||
if (optind == 0)
|
||||
optind = optreset = 1;
|
||||
|
||||
/*
|
||||
* Disable GNU extensions if POSIXLY_CORRECT is set or options
|
||||
* string begins with a '+'.
|
||||
*
|
||||
* CV, 2009-12-14: Check POSIXLY_CORRECT anew if optind == 0 or
|
||||
* optreset != 0 for GNU compatibility.
|
||||
*/
|
||||
if (posixly_correct == -1 || optreset != 0)
|
||||
posixly_correct = (getenv("POSIXLY_CORRECT") != NULL);
|
||||
if (*options == '-')
|
||||
flags |= FLAG_ALLARGS;
|
||||
else if (posixly_correct || *options == '+')
|
||||
flags &= ~FLAG_PERMUTE;
|
||||
if (*options == '+' || *options == '-')
|
||||
options++;
|
||||
|
||||
optarg = NULL;
|
||||
if (optreset)
|
||||
nonopt_start = nonopt_end = -1;
|
||||
start:
|
||||
if (optreset || !*place) { /* update scanning pointer */
|
||||
optreset = 0;
|
||||
if (optind >= nargc) { /* end of argument vector */
|
||||
place = EMSG;
|
||||
if (nonopt_end != -1) {
|
||||
/* do permutation, if we have to */
|
||||
permute_args(nonopt_start, nonopt_end,
|
||||
optind, nargv);
|
||||
optind -= nonopt_end - nonopt_start;
|
||||
}
|
||||
else if (nonopt_start != -1) {
|
||||
/*
|
||||
* If we skipped non-options, set optind
|
||||
* to the first of them.
|
||||
*/
|
||||
optind = nonopt_start;
|
||||
}
|
||||
nonopt_start = nonopt_end = -1;
|
||||
return (-1);
|
||||
}
|
||||
if (*(place = nargv[optind]) != '-' ||
|
||||
(place[1] == '\0' && strchr(options, '-') == NULL)) {
|
||||
place = EMSG; /* found non-option */
|
||||
if (flags & FLAG_ALLARGS) {
|
||||
/*
|
||||
* GNU extension:
|
||||
* return non-option as argument to option 1
|
||||
*/
|
||||
optarg = nargv[optind++];
|
||||
return (INORDER);
|
||||
}
|
||||
if (!(flags & FLAG_PERMUTE)) {
|
||||
/*
|
||||
* If no permutation wanted, stop parsing
|
||||
* at first non-option.
|
||||
*/
|
||||
return (-1);
|
||||
}
|
||||
/* do permutation */
|
||||
if (nonopt_start == -1)
|
||||
nonopt_start = optind;
|
||||
else if (nonopt_end != -1) {
|
||||
permute_args(nonopt_start, nonopt_end,
|
||||
optind, nargv);
|
||||
nonopt_start = optind -
|
||||
(nonopt_end - nonopt_start);
|
||||
nonopt_end = -1;
|
||||
}
|
||||
optind++;
|
||||
/* process next argument */
|
||||
goto start;
|
||||
}
|
||||
if (nonopt_start != -1 && nonopt_end == -1)
|
||||
nonopt_end = optind;
|
||||
|
||||
/*
|
||||
* If we have "-" do nothing, if "--" we are done.
|
||||
*/
|
||||
if (place[1] != '\0' && *++place == '-' && place[1] == '\0') {
|
||||
optind++;
|
||||
place = EMSG;
|
||||
/*
|
||||
* We found an option (--), so if we skipped
|
||||
* non-options, we have to permute.
|
||||
*/
|
||||
if (nonopt_end != -1) {
|
||||
permute_args(nonopt_start, nonopt_end,
|
||||
optind, nargv);
|
||||
optind -= nonopt_end - nonopt_start;
|
||||
}
|
||||
nonopt_start = nonopt_end = -1;
|
||||
return (-1);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Check long options if:
|
||||
* 1) we were passed some
|
||||
* 2) the arg is not just "-"
|
||||
* 3) either the arg starts with -- we are getopt_long_only()
|
||||
*/
|
||||
if (long_options != NULL && place != nargv[optind] &&
|
||||
(*place == '-' || (flags & FLAG_LONGONLY))) {
|
||||
short_too = 0;
|
||||
if (*place == '-')
|
||||
place++; /* --foo long option */
|
||||
else if (*place != ':' && strchr(options, *place) != NULL)
|
||||
short_too = 1; /* could be short option too */
|
||||
|
||||
optchar = parse_long_options(nargv, options, long_options,
|
||||
idx, short_too);
|
||||
if (optchar != -1) {
|
||||
place = EMSG;
|
||||
return (optchar);
|
||||
}
|
||||
}
|
||||
|
||||
if ((optchar = (int)*place++) == (int)':' ||
|
||||
(optchar == (int)'-' && *place != '\0') ||
|
||||
(oli = (char*)strchr(options, optchar)) == NULL) {
|
||||
/*
|
||||
* If the user specified "-" and '-' isn't listed in
|
||||
* options, return -1 (non-option) as per POSIX.
|
||||
* Otherwise, it is an unknown option character (or ':').
|
||||
*/
|
||||
if (optchar == (int)'-' && *place == '\0')
|
||||
return (-1);
|
||||
if (!*place)
|
||||
++optind;
|
||||
if (PRINT_ERROR)
|
||||
warnx(illoptchar, optchar);
|
||||
optopt = optchar;
|
||||
return (BADCH);
|
||||
}
|
||||
if (long_options != NULL && optchar == 'W' && oli[1] == ';') {
|
||||
/* -W long-option */
|
||||
if (*place) /* no space */
|
||||
/* NOTHING */;
|
||||
else if (++optind >= nargc) { /* no arg */
|
||||
place = EMSG;
|
||||
if (PRINT_ERROR)
|
||||
warnx(recargchar, optchar);
|
||||
optopt = optchar;
|
||||
return (BADARG);
|
||||
} else /* white space */
|
||||
place = nargv[optind];
|
||||
optchar = parse_long_options(nargv, options, long_options,
|
||||
idx, 0);
|
||||
place = EMSG;
|
||||
return (optchar);
|
||||
}
|
||||
if (*++oli != ':') { /* doesn't take argument */
|
||||
if (!*place)
|
||||
++optind;
|
||||
} else { /* takes (optional) argument */
|
||||
optarg = NULL;
|
||||
if (*place) /* no white space */
|
||||
optarg = place;
|
||||
else if (oli[1] != ':') { /* arg not optional */
|
||||
if (++optind >= nargc) { /* no arg */
|
||||
place = EMSG;
|
||||
if (PRINT_ERROR)
|
||||
warnx(recargchar, optchar);
|
||||
optopt = optchar;
|
||||
return (BADARG);
|
||||
} else
|
||||
optarg = nargv[optind];
|
||||
}
|
||||
place = EMSG;
|
||||
++optind;
|
||||
}
|
||||
/* dump back option letter */
|
||||
return (optchar);
|
||||
}
|
||||
|
||||
/*
|
||||
* getopt_long --
|
||||
* Parse argc/argv argument vector.
|
||||
*/
|
||||
int
|
||||
getopt_long(int nargc, char * const *nargv, const char *options,
|
||||
const struct option *long_options, int *idx)
|
||||
{
|
||||
|
||||
return (getopt_internal(nargc, nargv, options, long_options, idx,
|
||||
FLAG_PERMUTE));
|
||||
}
|
||||
|
||||
/*
|
||||
* getopt_long_only --
|
||||
* Parse argc/argv argument vector.
|
||||
*/
|
||||
int
|
||||
getopt_long_only(int nargc, char * const *nargv, const char *options,
|
||||
const struct option *long_options, int *idx)
|
||||
{
|
||||
|
||||
return (getopt_internal(nargc, nargv, options, long_options, idx,
|
||||
FLAG_PERMUTE|FLAG_LONGONLY));
|
||||
}
|
||||
|
||||
//extern int getopt_long(int nargc, char * const *nargv, const char *options,
|
||||
// const struct option *long_options, int *idx);
|
||||
//extern int getopt_long_only(int nargc, char * const *nargv, const char *options,
|
||||
// const struct option *long_options, int *idx);
|
||||
/*
|
||||
* Previous MinGW implementation had...
|
||||
*/
|
||||
#ifndef HAVE_DECL_GETOPT
|
||||
/*
|
||||
* ...for the long form API only; keep this for compatibility.
|
||||
*/
|
||||
# define HAVE_DECL_GETOPT 1
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* !defined(__UNISTD_H_SOURCED__) && !defined(__GETOPT_LONG_H__) */
|
Reference in New Issue
Block a user