Compare commits

..

49 Commits

Author SHA1 Message Date
c945c56482 these values can be defined at the top of the function 2023-05-28 00:08:28 -05:00
89d443d767 formatting changes 2023-05-28 00:03:50 -05:00
54a959438b added cdump.[ch] and cundump.[ch] to makefile 2023-05-27 23:17:17 -05:00
355842989b cvm.c: added cosmoV_undump 2023-05-27 23:16:47 -05:00
45f36e6e87 cdump.c: major refactoring; various fixes 2023-05-27 23:15:58 -05:00
819e76b711 cdump.c: fix instruction and line info dumping 2023-05-27 23:15:12 -05:00
f116efa02c started cundump.c:cosmoD_undump(); minor fixes 2023-05-25 21:12:21 -05:00
465f4d5e4a started cdump.c:cosmoD_dump() 2023-05-25 19:41:13 -05:00
3efee51224 minor refactoring and typos 2023-05-25 19:40:15 -05:00
2836de090b removed stale COBJ_STREAM usage 2023-05-25 18:34:39 -05:00
8e278e3a7d update Docs to reflect keyword changes 2023-02-10 20:46:05 -06:00
c8cae03604 syntax: 'var'->'let' 'function'->'func'
- 'var' has some weird scoping connotations with users of JS. better to change it to 'let', which will decide whether to make the variable a local or a global
- 'func' looks visually appealing lol
- some minor refactoring done in cparse.c
2023-02-09 15:58:25 -06:00
5d805e258b minor formatting fixes 2023-02-09 12:52:36 -06:00
8df4cc65e3 removed CObjStream 2023-02-09 12:42:09 -06:00
7279623e24 added clang-format 2023-02-09 12:32:48 -06:00
517b0b9532 Removed '-Werror' to the Makefile 2021-06-11 15:01:53 -05:00
1df2e212cb Merge pull request #4 from NeoInversion/dev
Commit authored by https://github.com/NeoInversion
2021-03-28 01:18:39 -05:00
Inversion
84ec5d2aee Add documentation for the OS library 2021-03-27 23:11:47 -07:00
6859ec98ad Added boilerplate for CObjStream 2021-03-20 01:44:03 -05:00
057716e0d4 Added os.system() to the os.* library 2021-03-20 01:02:13 -05:00
b9e9dedac6 Updated Appveyor to test testsuite 2021-03-19 22:25:23 -05:00
1813bbeb1b Added minimal testsuite for IC
- main.c will now report errors for passed scripts
2021-03-19 22:23:04 -05:00
471589d379 Added svg to README, whitelisted commits only effecting the binary 2021-03-16 15:05:20 -05:00
70f931df18 Added AppVeyor CI 2021-03-16 14:54:52 -05:00
c13db54d7d Added '__equal' metamethod docs 2021-02-24 12:52:31 -06:00
da85d640ce Added another example script 'compare.comso'
- This stresses the table implementation as well as cosmoV_equals
2021-02-23 11:50:41 -06:00
6bc4ec6b04 Improved cosmoO_equals performance for strings 2021-02-23 11:49:57 -06:00
f92ffcecbd Added '__equal' metamethod, slightly refactored cosmoO_equal
- ISTRING_EQUAL has been added
2021-02-20 12:42:13 -06:00
66d77bc54b Refactored cosmoO_equals
This sets up room for the '__equal' metamethod to be added

- cosmoO_equals now requires the state to be passed
- cosmoV_equals now requires the state to be passed
- cosmoT_get now requires the state to be passed
2021-02-19 17:04:23 -06:00
afac75753f cparse.c now throws an error on illegal statements 2021-02-19 12:47:12 -06:00
92b2db9678 Added <string> arg to assert()
Commit authored by https://github.com/NeoInversion
2021-02-16 18:15:45 -06:00
Inversion
b30616bb3c Temporary fix for possible bug 2021-02-16 16:07:10 -08:00
Inversion
9e6c6038f1 Update standard library documentation 2021-02-16 15:53:20 -08:00
Inversion
1200e2d512 Add optional custom error message to assert 2021-02-16 15:53:07 -08:00
2050359d2f Added C99 support, refactored headers 2021-02-15 16:20:04 -06:00
47051575cb Added basic standard library docs 2021-02-15 14:25:08 -06:00
7c7a2ed8d9 Added math.rad() and math.deg() 2021-02-15 14:06:43 -06:00
d1ea5c9703 Added trig. functions to the math library 2021-02-15 14:00:26 -06:00
7c6c075c2a Added OP_POW to cdebug.c; refactored cosmoV_registerProtoObject
- cosmoV_registerProtoObject now walks the object list and updates the proto's for objects of the objType which have a NULL proto.
- misc. comment changes in cvm.h as well.
2021-02-13 20:08:35 -06:00
14539057aa Added string.rep() to the string library 2021-02-13 19:07:47 -06:00
461e1d0c15 Minor Makefile fix 2021-02-11 20:35:42 -06:00
2e07715a7d Added CMake support
should make integration into visual studio easier for people
2021-02-11 20:34:04 -06:00
bc43eaaa75 Minor cbaselib.c cleanup 2021-02-11 00:58:12 -06:00
43a278e12d Added error, string.len, and cosmoV_readCString
Commit authored by https://github.com/NeoInversion
2021-02-11 00:27:23 -06:00
Inversion
44f1674b09 Added argument type check to string.len 2021-02-10 22:24:31 -08:00
Inversion
27aedd2969 Updated baselib in accordance with cosmoV_readCString 2021-02-10 22:14:58 -08:00
Inversion
7caa696aa2 Added cosmoV_readCString for convenience 2021-02-10 22:14:31 -08:00
Inversion
2e395065f8 Add string.len to base library 2021-02-10 22:02:35 -08:00
Inversion
5b8dc30bb8 Added error to the base library 2021-02-10 21:48:41 -08:00
43 changed files with 1434 additions and 5787 deletions

View File

@@ -1,45 +0,0 @@
name: Check Builds
on:
push:
paths:
- src/**
- util/**
- main.c
- Makefile
- CMakeLists.txt
- .github/workflows/check_build.yaml
workflow_dispatch:
jobs:
ubuntu-build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
submodules: recursive
- name: CMake
run: cmake -B build -DCMAKE_BUILD_TYPE=Release
- name: Build
run: cmake --build build --config Release
- name: Upload build artifact
uses: actions/upload-artifact@v2
with:
name: Cosmo-Ubuntu
path: build/bin
windows-build:
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
with:
submodules: recursive
- name: Create CMake build files
run: cmake -B build -DCMAKE_BUILD_TYPE=MinSizeRel
- name: Check compilation
run: cmake --build build --config MinSizeRel
- name: Upload build artifact
uses: actions/upload-artifact@v2
with:
name: Cosmo-Windows
path: build/bin

1
.gitignore vendored
View File

@@ -1,5 +1,4 @@
*.o *.o
bin bin
build
.vscode .vscode
CMakeFiles CMakeFiles

View File

@@ -3,25 +3,20 @@ project(cosmo VERSION 0.1.0 LANGUAGES C)
set(CMAKE_C_STANDARD 99) set(CMAKE_C_STANDARD 99)
set(CMAKE_C_STANDARD_REQUIRED True) 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_RUNTIME_OUTPUT_DIRECTORY bin)
IF (NOT WIN32) set (CMAKE_C_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g -Wall")
set (CMAKE_C_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g") set (CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG} -fsanitize=address")
set (CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG} -fsanitize=address") set (CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O3 -Wall")
set (CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O3 -Wall")
ENDIF()
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT cosmo) set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT cosmo)
include(FetchContent) include(FetchContent)
file(GLOB sources CONFIGURE_DEPENDS ${PROJECT_SOURCE_DIR}/src/*.c) file(GLOB sources CONFIGURE_DEPENDS ${PROJECT_SOURCE_DIR}/src/*.c)
add_executable(${PROJECT_NAME} main.c ${PROJECT_SOURCE_DIR}/util/linenoise.c) add_executable(${PROJECT_NAME} main.c)
target_sources(${PROJECT_NAME} PRIVATE ${sources}) 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_include_directories(${PROJECT_NAME} PUBLIC ${PROJECT_SOURCE_DIR}/src)
target_compile_features(${PROJECT_NAME} PRIVATE c_std_99) target_compile_features(${PROJECT_NAME} PRIVATE c_std_99)

View File

@@ -1,8 +1,8 @@
# make clean && make && ./bin/cosmo # make clean && make && ./bin/cosmo
CC=clang CC=clang
CFLAGS=-fPIE -Wall -Isrc -O3 #-g -fsanitize=address CFLAGS=-fPIE -Wall -O0 -Isrc -std=c99 -g
LDFLAGS=-lm #-fsanitize=address LDFLAGS=-lm -fsanitize=address
OUT=bin/cosmo OUT=bin/cosmo
CHDR=\ CHDR=\
@@ -21,7 +21,6 @@ CHDR=\
src/cbaselib.h\ src/cbaselib.h\
src/cdump.h\ src/cdump.h\
src/cundump.h\ src/cundump.h\
util/linenoise.h\
CSRC=\ CSRC=\
src/cchunk.c\ src/cchunk.c\
@@ -38,7 +37,6 @@ CSRC=\
src/cbaselib.c\ src/cbaselib.c\
src/cdump.c\ src/cdump.c\
src/cundump.c\ src/cundump.c\
util/linenoise.c\
main.c\ main.c\
COBJ=$(CSRC:.c=.o) COBJ=$(CSRC:.c=.o)

View File

@@ -1,23 +1,5 @@
# Cosmo # Cosmo
[![AppVeyor](https://ci.appveyor.com/api/projects/status/github/CPunch/Cosmo?svg=true)](https://ci.appveyor.com/project/CPunch/Cosmo)
```
Usage: ./bin/cosmo [-clsr] [args]
available options are:
-c <in> <out> compile <in> and dump to <out>
-l <in> load dump from <in>
-s <in...> compile and run <in...> script(s)
-r start the repl
```
<p align="center">
<a href="https://github.com/CPunch/Cosmo/actions/workflows/check_build.yaml"><img src="https://github.com/CPunch/Cosmo/actions/workflows/check_build.yaml/badge.svg?branch=main" alt="Workflow"></a>
<a href="https://github.com/CPunch/Cosmo/blob/main/LICENSE.md"><img src="https://img.shields.io/github/license/CPunch/Cosmo" alt="License"></a>
<br>
<a href="https://asciinema.org/a/629355" target="_blank"><img src="https://asciinema.org/a/629355.svg" /></a>
</p>
## What is a 'cosmo'?
Cosmo is a portable scripting language loosely based off of Lua. Cosmo easily allows the user to extend the language through the use of Proto objects, which describe the behavior of Objects. For example the following is a simple Vector Proto which describes behavior for a Vector-like object. Cosmo is a portable scripting language loosely based off of Lua. Cosmo easily allows the user to extend the language through the use of Proto objects, which describe the behavior of Objects. For example the following is a simple Vector Proto which describes behavior for a Vector-like object.
@@ -58,3 +40,6 @@ end
1 : 2 1 : 2
0 : 3 0 : 3
``` ```
# C API
The Cosmo C API is currently undocumented, however as soon as development has reached a stable state documentation on full language features and the C API will start.

33
appveyor.yml Normal file
View File

@@ -0,0 +1,33 @@
version: 'cosmo-0.1.{build}'
# we compile every commit under all branches
#branch:
# only:
# - main
# only run CI if we changed actual code
only_commits:
files:
- src/
- main.c
- Makefile
- CMakeLists.txt
- appveyor.yml
# images we're using
image:
- Ubuntu2004
platform:
- x64
install:
- sudo apt install clang cmake -y
build_script:
- make && ./bin/cosmo examples/testsuite.cosmo examples/getters_setters.cosmo
artifacts:
- path: bin
name: ubuntu20_04-bin-x64
type: zip

View File

@@ -59,14 +59,7 @@ Includes functions that interact with the operating system.
| Name | Type | Behavior | Example | | Name | Type | Behavior | Example |
| ------------ | ------------------------------------------------ | ------------------------------------------------------------------------ | ------------------------ | | ------------ | ------------------------------------------------ | ------------------------------------------------------------------------ | ------------------------ |
| os.open | `(path<string>[, mode<string>])` -> `<bool>, <obj>` | Opens a file at `path` and returns a file object. Specify mode to be "r" or "w" optionally, defaults to "r". | `os.open("test.txt")` -> `true, <file>` | | os.read | `(path<string>)` -> `<string>` or `<nil>` | Returns a file's contents or nil if it doesn't exist/an error occurred | `os.read("path")` -> `Hello, World!`|
| os.time | `()` -> `<number>` | Returns the system time in Epoch format | `os.time()` -> `1.61691e+09` | | os.time | `()` -> `<number>` | Returns the system time in Epoch format | `os.time()` -> `1.61691e+09` |
| os.system | `(cmd<string>)` -> `<number>` | Runs a system command as if it were a terminal and returns the exit code | `os.system("mkdir test")` -> `0` | | os.system | `(cmd<string>)` -> `<number>` | Runs a system command as if it were a terminal and returns the exit code | `os.system("mkdir test")` -> `0` |
> -> means 'returns' > -> means 'returns'
File objects have the following methods:
| Name | Type | Behavior | Example |
| ------------ | ------------------------------------------------ | ------------------------------------------------------------------------ | ------------------------ |
| file:read | `(amt<number> or "a")` -> `<string>` | Reads `amt` bytes from the file and returns them as a string. If `"a"` is passed, the entire file is read. | `file:read("a")` -> `"Hello world!"` |
| file:write | `(data<string>)` -> `<nil>` | Writes `data` to file. | `file:write("hello world!")` -> `<nil>` |
> -> means 'returns'

View File

@@ -1,13 +1,13 @@
let object = { let object = {
__setter = [ __setter = [
"field1" = func(self, val) "field1" = function(self, val)
print("setter for field1 called!") print("setter for field1 called!")
self.x = val self.x = val
end end
], ],
__getter = [ __getter = [
"field1" = func(self) "field1" = function(self)
print("getter for field1 called!") print("getter for field1 called!")
return self.x + 1 return self.x + 1

View File

@@ -1,6 +0,0 @@
local err, file = os.open("LICENSE.md")
if err then
print("failed to open file")
end
print(file:read("a"))

View File

@@ -6,7 +6,7 @@ end
// instance of test // instance of test
let obj = test() let obj = test()
test.__index = func(self, key) test.__index = function(self, key)
print("__index called!") print("__index called!")
if (key == "lol") then if (key == "lol") then
return 9001 return 9001

View File

@@ -1,6 +0,0 @@
local err, file = os.open("test.md", "w")
if err then
print("failed to open file")
end
file:write("hello world")

202
main.c
View File

@@ -1,34 +1,21 @@
#include "cbaselib.h" #include "cosmo.h"
#include "cchunk.h" #include "cchunk.h"
#include "cdebug.h" #include "cdebug.h"
#include "cdump.h"
#include "cmem.h"
#include "cosmo.h"
#include "cparse.h"
#include "cundump.h"
#include "cvm.h" #include "cvm.h"
#include "cparse.h"
#include "cbaselib.h"
#include "util/linenoise.h" #include "cmem.h"
#include <stdio.h>
#include <stdlib.h>
#ifdef _WIN32
# include "util/getopt.h"
#else
# include <getopt.h>
#endif
static bool _ACTIVE = false; static bool _ACTIVE = false;
int cosmoB_quitRepl(CState *state, int nargs, CValue *args) int cosmoB_quitRepl(CState *state, int nargs, CValue *args) {
{
_ACTIVE = false; _ACTIVE = false;
return 0; // we don't return anything return 0; // we don't return anything
} }
int cosmoB_input(CState *state, int nargs, CValue *args) int cosmoB_input(CState *state, int nargs, CValue *args) {
{
// input() accepts the same params as print()! // input() accepts the same params as print()!
for (int i = 0; i < nargs; i++) { for (int i = 0; i < nargs; i++) {
CObjString *str = cosmoV_toString(state, args[i]); CObjString *str = cosmoV_toString(state, args[i]);
@@ -39,46 +26,39 @@ int cosmoB_input(CState *state, int nargs, CValue *args)
char line[1024]; char line[1024];
fgets(line, sizeof(line), stdin); fgets(line, sizeof(line), stdin);
cosmoV_pushRef(state, cosmoV_pushRef(state, (CObj*)cosmoO_copyString(state, line, strlen(line)-1)); // -1 for the \n
(CObj *)cosmoO_copyString(state, line, strlen(line) - 1)); // -1 for the \n
return 1; // 1 return value return 1; // 1 return value
} }
static bool interpret(CState *state, const char *script, const char *mod) 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) // cosmoV_compileString pushes the result onto the stack (COBJ_ERROR or COBJ_CLOSURE)
if (cosmoV_compileString(state, script, mod)) { if (cosmoV_compileString(state, script, mod)) {
cosmoG_disassemble(cosmoV_readClosure(*cosmoV_getTop(state, 0))); COSMOVMRESULT res = cosmoV_call(state, 0, 0); // 0 args being passed, 0 results expected
if (!cosmoV_pcall(state, 0, 1)) {
cosmoV_printBacktrace(state, cosmoV_readError(*cosmoV_pop(state)));
return false;
}
// if the result is nil, we don't print it if (res == COSMOVM_RUNTIME_ERR)
if (IS_NIL(*cosmoV_getTop(state, 0))) { cosmoV_printError(state, state->error);
cosmoV_pop(state);
return true;
}
// otherwise, we print the result
cosmoV_printValue(*cosmoV_getTop(state, 0));
printf("\n");
cosmoV_pop(state);
} else { } else {
cosmoV_printBacktrace(state, cosmoV_readError(*cosmoV_pop(state))); cosmoV_pop(state); // pop the error off the stack
return false; cosmoV_printError(state, state->error);
} }
return true; ret = state->panic;
state->panic = false; // so our repl isn't broken
return !ret;
} }
static void repl(CState *state) static void repl() {
{ char line[1024];
char *line;
_ACTIVE = true; _ACTIVE = true;
CState *state = cosmoV_newState();
cosmoB_loadLibrary(state);
cosmoB_loadOSLib(state);
cosmoB_loadVM(state);
// add our custom REPL functions // add our custom REPL functions
cosmoV_pushString(state, "quit"); cosmoV_pushString(state, "quit");
cosmoV_pushCFunction(state, cosmoB_quitRepl); cosmoV_pushCFunction(state, cosmoB_quitRepl);
@@ -86,22 +66,24 @@ static void repl(CState *state)
cosmoV_pushString(state, "input"); cosmoV_pushString(state, "input");
cosmoV_pushCFunction(state, cosmoB_input); cosmoV_pushCFunction(state, cosmoB_input);
cosmoV_addGlobals(state, 2); cosmoV_register(state, 2);
while (_ACTIVE) { while (_ACTIVE) {
if (!(line = linenoise("> "))) { // better than gets() printf("> ");
if (!fgets(line, sizeof(line), stdin)) { // better than gets()
printf("\n> ");
break; break;
} }
linenoiseHistoryAdd(line);
interpret(state, line, "REPL"); interpret(state, line, "REPL");
free(line);
} }
cosmoV_freeState(state);
} }
static char *readFile(const char *path) static char *readFile(const char* path) {
{ FILE* file = fopen(path, "rb");
FILE *file = fopen(path, "rb");
if (file == NULL) { if (file == NULL) {
fprintf(stderr, "Could not open file \"%s\".\n", path); fprintf(stderr, "Could not open file \"%s\".\n", path);
exit(74); exit(74);
@@ -112,7 +94,7 @@ static char *readFile(const char *path)
size_t fileSize = ftell(file); size_t fileSize = ftell(file);
rewind(file); rewind(file);
char *buffer = (char *)malloc(fileSize + 1); // make room for the null byte char *buffer = (char*)malloc(fileSize + 1); // make room for the null byte
if (buffer == NULL) { if (buffer == NULL) {
fprintf(stderr, "failed to allocate!"); fprintf(stderr, "failed to allocate!");
exit(1); exit(1);
@@ -132,126 +114,36 @@ static char *readFile(const char *path)
return buffer; return buffer;
} }
static bool runFile(CState *state, const char *fileName) static bool runFile(const char* fileName) {
{
bool ret; bool ret;
char *script = readFile(fileName); char* script = readFile(fileName);
CState *state = cosmoV_newState();
cosmoB_loadLibrary(state);
cosmoB_loadOSLib(state);
// add our input() function to the global table // add our input() function to the global table
cosmoV_pushString(state, "input"); cosmoV_pushString(state, "input");
cosmoV_pushCFunction(state, cosmoB_input); cosmoV_pushCFunction(state, cosmoB_input);
cosmoV_addGlobals(state, 1); cosmoV_register(state, 1);
ret = interpret(state, script, fileName); ret = interpret(state, script, fileName);
cosmoV_freeState(state);
free(script); free(script);
return ret; // let the caller know if the script failed return ret; // let the caller know if the script failed
} }
int fileWriter(CState *state, const void *data, size_t size, const void *ud) int main(int argc, const char *argv[]) {
{ if (argc == 1) {
return !fwrite(data, size, 1, (FILE *)ud); repl();
} } else if (argc >= 2) { // they passed a file (or more lol)
for (int i = 1; i < argc; i++) {
int fileReader(CState *state, void *data, size_t size, const void *ud) if (!runFile(argv[i])) {
{
return fread(data, size, 1, (FILE *)ud) != 1;
}
void compileScript(CState *state, const char *in, const char *out)
{
char *script = readFile(in);
FILE *fout = fopen(out, "wb");
if (cosmoV_compileString(state, script, in)) {
CObjFunction *func = cosmoV_readClosure(*cosmoV_getTop(state, 0))->function;
cosmoD_dump(state, func, fileWriter, (void *)fout);
} else {
cosmoV_printBacktrace(state, cosmoV_readError(*cosmoV_pop(state)));
}
free(script);
fclose(fout);
printf("[!] compiled %s to %s successfully!\n", in, out);
}
void loadScript(CState *state, const char *in)
{
FILE *file = fopen(in, "rb");
if (!cosmoV_undump(state, fileReader, file)) {
cosmoV_printBacktrace(state, cosmoV_readError(*cosmoV_pop(state)));
return;
};
printf("[!] loaded %s!\n", in);
if (!cosmoV_pcall(state, 0, 0))
cosmoV_printBacktrace(state, cosmoV_readError(*cosmoV_pop(state)));
fclose(file);
}
void printUsage(const char *name)
{
printf("Usage: %s [-clsr] [args]\n\n", name);
printf("available options are:\n"
"-c <in> <out>\tcompile <in> and dump to <out>\n"
"-l <in>\t\tload dump from <in>\n"
"-s <in...>\tcompile and run <in...> script(s)\n"
"-r\t\tstart the repl\n\n");
}
int main(int argc, char *const argv[])
{
CState *state = cosmoV_newState();
cosmoB_loadLibrary(state);
cosmoB_loadOS(state);
cosmoB_loadVM(state);
int opt;
bool isValid = false;
while ((opt = getopt(argc, argv, "clsr")) != -1) {
switch (opt) {
case 'c':
if (optind >= argc - 1) {
printf("Usage: %s -c <in> <out>\n", argv[0]);
exit(EXIT_FAILURE);
} else {
compileScript(state, argv[optind], argv[optind + 1]);
}
isValid = true;
break;
case 'l':
if (optind >= argc) {
printf("Usage: %s -l <in>\n", argv[0]);
exit(EXIT_FAILURE);
} else {
loadScript(state, argv[optind]);
}
isValid = true;
break;
case 's':
for (int i = optind; i < argc; i++) {
if (!runFile(state, argv[i])) {
printf("failed to run %s!\n", argv[i]);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
} }
isValid = true;
break;
case 'r':
repl(state);
isValid = true;
break;
}
} }
if (!isValid) {
printUsage(argv[0]);
}
cosmoV_freeState(state);
return 0; return 0;
} }

View File

@@ -1,56 +0,0 @@
#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

View File

@@ -1,13 +1,12 @@
#include "cbaselib.h" #include "cbaselib.h"
#include "_time.h"
#include "cdebug.h"
#include "cmem.h" #include "cmem.h"
#include "cobj.h" #include "cobj.h"
#include "cvalue.h" #include "cvalue.h"
#include "cvm.h" #include "cvm.h"
#include <math.h> #include <math.h>
#include <sys/time.h>
// ================================================================ [BASELIB] // ================================================================ [BASELIB]
@@ -18,7 +17,7 @@ int cosmoB_print(CState *state, int nargs, CValue *args)
CObjString *str = cosmoV_toString(state, args[i]); CObjString *str = cosmoV_toString(state, args[i]);
printf("%s", cosmoO_readCString(str)); printf("%s", cosmoO_readCString(str));
} else { // else, thats pretty expensive for primitives, just print the raw value } else { // else, thats pretty expensive for primitives, just print the raw value
cosmoV_printValue(args[i]); printValue(args[i]);
} }
} }
printf("\n"); printf("\n");
@@ -30,6 +29,7 @@ int cosmoB_assert(CState *state, int nargs, CValue *args)
{ {
if (nargs < 1 || nargs > 2) { if (nargs < 1 || nargs > 2) {
cosmoV_error(state, "assert() expected 1 or 2 arguments, got %d!", nargs); cosmoV_error(state, "assert() expected 1 or 2 arguments, got %d!", nargs);
return 0; // nothing pushed onto the stack to return
} }
if (!IS_BOOLEAN(args[0]) || (nargs == 2 && !IS_STRING(args[1]))) { if (!IS_BOOLEAN(args[0]) || (nargs == 2 && !IS_STRING(args[1]))) {
@@ -39,6 +39,7 @@ int cosmoB_assert(CState *state, int nargs, CValue *args)
} else { } else {
cosmoV_typeError(state, "assert()", "<boolean>", "%s", cosmoV_typeStr(args[0])); cosmoV_typeError(state, "assert()", "<boolean>", "%s", cosmoV_typeStr(args[0]));
} }
return 0;
} }
if (!cosmoV_readBoolean(args[0])) // expression passed was false, error! if (!cosmoV_readBoolean(args[0])) // expression passed was false, error!
@@ -51,6 +52,7 @@ int cosmoB_type(CState *state, int nargs, CValue *args)
{ {
if (nargs != 1) { if (nargs != 1) {
cosmoV_error(state, "type() expected 1 argument, got %d!", nargs); cosmoV_error(state, "type() expected 1 argument, got %d!", nargs);
return 0;
} }
// push the type string to the stack // push the type string to the stack
@@ -62,14 +64,20 @@ int cosmoB_pcall(CState *state, int nargs, CValue *args)
{ {
if (nargs < 1) { if (nargs < 1) {
cosmoV_error(state, "pcall() expected at least 1 argument!"); cosmoV_error(state, "pcall() expected at least 1 argument!");
return 0;
} }
// call the passed callable, the passed arguments are already in the // unfreeze the state GC before calling the function
// proper order lol, so we can just call it cosmoM_unfreezeGC(state);
bool res = cosmoV_pcall(state, nargs - 1, 1);
// call the passed callable
COSMOVMRESULT res = cosmoV_pcall(state, nargs - 1, 1);
// insert false before the result // insert false before the result
cosmoV_insert(state, 0, cosmoV_newBoolean(res)); cosmo_insert(state, 0, cosmoV_newBoolean(res == COSMOVM_OK));
// refreeze the state GC so it can be properly unfrozen
cosmoM_freezeGC(state);
return 2; return 2;
} }
@@ -77,6 +85,7 @@ int cosmoB_tonumber(CState *state, int nargs, CValue *args)
{ {
if (nargs != 1) { if (nargs != 1) {
cosmoV_error(state, "tonumber() expected 1 argument, got %d!", nargs); cosmoV_error(state, "tonumber() expected 1 argument, got %d!", nargs);
return 0;
} }
cosmoV_pushNumber(state, cosmoV_toNumber(state, args[0])); cosmoV_pushNumber(state, cosmoV_toNumber(state, args[0]));
@@ -85,8 +94,10 @@ int cosmoB_tonumber(CState *state, int nargs, CValue *args)
int cosmoB_tostring(CState *state, int nargs, CValue *args) int cosmoB_tostring(CState *state, int nargs, CValue *args)
{ {
if (nargs != 1) if (nargs != 1) {
cosmoV_error(state, "tostring() expected 1 argument, got %d!", nargs); cosmoV_error(state, "tostring() expected 1 argument, got %d!", nargs);
return 0;
}
cosmoV_pushRef(state, (CObj *)cosmoV_toString(state, args[0])); cosmoV_pushRef(state, (CObj *)cosmoV_toString(state, args[0]));
return 1; return 1;
@@ -96,16 +107,18 @@ int cosmoB_loadstring(CState *state, int nargs, CValue *args)
{ {
if (nargs != 1) { if (nargs != 1) {
cosmoV_error(state, "loadstring() expected 1 argument, got %d!", nargs); cosmoV_error(state, "loadstring() expected 1 argument, got %d!", nargs);
return 0;
} }
if (!IS_STRING(args[0])) { if (!IS_STRING(args[0])) {
cosmoV_typeError(state, "loadstring()", "<string>", "%s", cosmoV_typeStr(args[0])); cosmoV_typeError(state, "loadstring()", "<string>", "%s", cosmoV_typeStr(args[0]));
return 0;
} }
CObjString *str = cosmoV_readString(args[0]); CObjString *str = cosmoV_readString(args[0]);
bool res = cosmoV_compileString(state, str->str, ""); bool res = cosmoV_compileString(state, str->str, "");
cosmoV_insert(state, 0, cosmoV_newBoolean(res)); cosmo_insert(state, 0, cosmoV_newBoolean(res));
return 2; // <boolean>, <closure> or <error> return 2; // <boolean>, <closure> or <error>
} }
@@ -113,10 +126,12 @@ int cosmoB_error(CState *state, int nargs, CValue *args)
{ {
if (nargs != 1) { if (nargs != 1) {
cosmoV_error(state, "error() expected 1 argument, got %d!", nargs); cosmoV_error(state, "error() expected 1 argument, got %d!", nargs);
return 0;
} }
if (!IS_STRING(args[0])) { if (!IS_STRING(args[0])) {
cosmoV_typeError(state, "error()", "<string>", "%s", cosmoV_typeStr(args[0])); cosmoV_typeError(state, "error()", "<string>", "%s", cosmoV_typeStr(args[0]));
return 0;
} }
cosmoV_error(state, "%s", cosmoV_readCString(args[0])); cosmoV_error(state, "%s", cosmoV_readCString(args[0]));
@@ -139,7 +154,7 @@ void cosmoB_loadLibrary(CState *state)
} }
// register all the pushed c functions and the strings as globals // register all the pushed c functions and the strings as globals
cosmoV_addGlobals(state, i); cosmoV_register(state, i);
// load other libraries // load other libraries
cosmoB_loadObjLib(state); cosmoB_loadObjLib(state);
@@ -165,46 +180,13 @@ int cosmoB_osetProto(CState *state, int nargs, CValue *args)
int cosmoB_ogetProto(CState *state, int nargs, CValue *args) int cosmoB_ogetProto(CState *state, int nargs, CValue *args)
{ {
if (nargs != 1) if (nargs != 1) {
cosmoV_error(state, "Expected 1 argument, got %d!", nargs); cosmoV_error(state, "Expected 1 argument, got %d!", nargs);
return 0;
if (!IS_REF(args[0])) {
cosmoV_typeError(state, "__getter.__proto", "<object>", "%s", cosmoV_typeStr(args[0]));
} }
CObj *proto = (CObj *)cosmoV_readRef(args[0])->proto; cosmoV_pushRef(state, (CObj *)cosmoV_readObject(args[0])->_obj.proto); // just return the proto
if (proto == NULL) {
cosmoV_pushNil(state);
return 1;
}
cosmoV_pushRef(state, proto); // just return the proto
return 1; // 1 result
}
int cosmoB_ogetKeys(CState *state, int nargs, CValue *args)
{
if (nargs != 1)
cosmoV_error(state, "Expected 1 argument, got %d!", nargs);
if (!IS_OBJECT(args[0])) {
cosmoV_typeError(state, "object.__keys", "<object>", "%s", cosmoV_typeStr(args[0]));
}
// push keys
CObjObject *obj = cosmoV_readObject(args[0]);
int cap = cosmoT_getCapacity(&obj->tbl);
int indx = 0;
for (int i = 0; i < cap; i++) {
CTableEntry *entry = &obj->tbl.table[i];
if (IS_NIL(entry->key))
continue;
cosmoV_pushNumber(state, indx++);
cosmoV_pushValue(state, entry->key);
}
cosmoV_makeTable(state, indx);
return 1; // 1 result return 1; // 1 result
} }
@@ -212,11 +194,13 @@ int cosmoB_oisChild(CState *state, int nargs, CValue *args)
{ {
if (nargs != 2) { if (nargs != 2) {
cosmoV_error(state, "object.ischild() expected 2 arguments, got %d!", nargs); cosmoV_error(state, "object.ischild() expected 2 arguments, got %d!", nargs);
return 0;
} }
if (!IS_REF(args[0]) || !IS_OBJECT(args[1])) { if (!IS_REF(args[0]) || !IS_OBJECT(args[1])) {
cosmoV_typeError(state, "object.ischild()", "<reference obj>, <object>", "%s, %s", cosmoV_typeError(state, "object.ischild()", "<reference obj>, <object>", "%s, %s",
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1])); cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
return 0;
} }
CObj *obj = cosmoV_readRef(args[0]); CObj *obj = cosmoV_readRef(args[0]);
@@ -229,9 +213,9 @@ int cosmoB_oisChild(CState *state, int nargs, CValue *args)
COSMO_API void cosmoB_loadObjLib(CState *state) COSMO_API void cosmoB_loadObjLib(CState *state)
{ {
const char *identifiers[] = {"ischild", "keys"}; const char *identifiers[] = {"ischild"};
CosmoCFunction objLib[] = {cosmoB_oisChild, cosmoB_ogetKeys}; CosmoCFunction objLib[] = {cosmoB_oisChild};
// make object library object // make object library object
cosmoV_pushString(state, "object"); cosmoV_pushString(state, "object");
@@ -239,7 +223,7 @@ COSMO_API void cosmoB_loadObjLib(CState *state)
// make __getter object for debug proto // make __getter object for debug proto
cosmoV_pushString(state, "__getter"); cosmoV_pushString(state, "__getter");
// key & value pairs // key & value pair
cosmoV_pushString(state, "__proto"); // key cosmoV_pushString(state, "__proto"); // key
cosmoV_pushCFunction(state, cosmoB_ogetProto); // value cosmoV_pushCFunction(state, cosmoB_ogetProto); // value
@@ -265,176 +249,54 @@ COSMO_API void cosmoB_loadObjLib(CState *state)
cosmoV_registerProtoObject(state, COBJ_OBJECT, obj); cosmoV_registerProtoObject(state, COBJ_OBJECT, obj);
// register "object" to the global table // register "object" to the global table
cosmoV_addGlobals(state, 1); cosmoV_register(state, 1);
} }
// ================================================================ [OS.*] // ================================================================ [OS.*]
int fileB_read(CState *state, int nargs, CValue *args) // os.read()
{ int cosmoB_osRead(CState *state, int nargs, CValue *args)
if (nargs != 2) {
cosmoV_error(state, "file:read() expected 2 arguments, got %d!", nargs);
}
if (!cosmoV_isValueUserType(state, args[0], COSMO_USER_FILE)) {
cosmoV_typeError(state, "file:read()", "<file>, <number> or \"a\"", "%s, %s",
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
}
CObjObject *fileObj = cosmoV_readObject(args[0]);
FILE *file = cosmoO_getUserP(fileObj);
if (IS_NUMBER(args[1])) {
CValue temp;
char *buffer;
cosmo_Number length = cosmoV_readNumber(args[1]);
// make sure the length is within the bounds of the file
if (length < 0) {
cosmoV_error(state, "file:read() expected length to be >= 0, got %d!", length);
}
// allocate a buffer for the read data
buffer = cosmoM_xmalloc(state, (size_t)length + 1);
// read the data
fread(buffer, sizeof(char), (size_t)length, file);
buffer[(int)length] = '\0'; // write the NULL terminator
// push the read data
temp = cosmoV_newRef(cosmoO_takeString(state, buffer, (size_t)length));
cosmoV_pushValue(state, temp);
} else if (IS_STRING(args[1])) {
if (strcmp(cosmoV_readCString(args[1]), "a") == 0) {
CValue temp;
char *buffer;
long length;
// get the length of the file
fseek(file, 0, SEEK_END);
length = ftell(file);
fseek(file, 0, SEEK_SET);
// allocate a buffer for the read data
buffer = cosmoM_xmalloc(state, (size_t)length + 1);
// read the data
fread(buffer, sizeof(char), (size_t)length, file);
buffer[length] = '\0'; // write the NULL terminator
// push the read data
temp = cosmoV_newRef(cosmoO_takeString(state, buffer, (size_t)length));
cosmoV_pushValue(state, temp);
} else {
cosmoV_error(state, "file:read() expected \"a\" or <number>, got \"%s\"!",
cosmoV_readCString(args[1]));
}
} else {
cosmoV_typeError(state, "file:read()", "<file>, <number> or \"a\"", "%s, %s",
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
}
return 1;
}
int fileB_write(CState *state, int nargs, CValue *args)
{
CObjObject *fileObj;
CObjString *str;
FILE *file;
if (nargs != 2) {
cosmoV_error(state, "file:write() expected 2 arguments, got %d!", nargs);
}
if (!cosmoV_isValueUserType(state, args[0], COSMO_USER_FILE)) {
cosmoV_typeError(state, "file:write()", "<file>, <string>", "%s, %s",
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
}
if (!IS_STRING(args[1])) {
cosmoV_typeError(state, "file:write()", "<file>, <string>", "%s, %s",
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
}
fileObj = cosmoV_readObject(args[0]);
str = cosmoV_readString(args[1]);
file = (FILE *)cosmoO_getUserP(fileObj);
fwrite(str->str, sizeof(char), str->length, file);
return 0;
}
int fileB_gc(CState *state, int nargs, CValue *args)
{ {
if (nargs != 1) { if (nargs != 1) {
cosmoV_error(state, "file:read() expected 1 argument, got %d!", nargs); cosmoV_error(state, "os.read() expected 1 argument, got %d!", nargs);
}
if (!cosmoV_isValueUserType(state, args[0], COSMO_USER_FILE)) {
cosmoV_typeError(state, "file:__gc()", "<file>", "%s", cosmoV_typeStr(args[0]));
}
CObjObject *fileObj = cosmoV_readObject(args[0]);
FILE *file = cosmoO_getUserP(fileObj);
fclose(file);
return 0; return 0;
} }
CObjObject *pushFileObj(CState *state, FILE *file)
{
CObjObject *fileObj = cosmoO_newObject(state);
cosmoV_pushRef(state, (CObj *)fileObj);
cosmoO_setUserP(fileObj, file);
cosmoO_setUserT(fileObj, COSMO_USER_FILE);
// grab and set proto from the registry
cosmoV_pushRef(state, (CObj *)fileObj);
cosmoV_pushString(state, "file");
cosmoV_getRegistry(state);
cosmoV_setProto(state);
cosmoO_lock(fileObj);
return fileObj;
}
int cosmoB_osOpen(CState *state, int nargs, CValue *args)
{
const char *filePath, *mode = "rb";
FILE *file;
if (nargs >= 1) {
if (!IS_STRING(args[0])) { if (!IS_STRING(args[0])) {
cosmoV_typeError(state, "os.open()", "<string>", "%s", cosmoV_typeStr(args[0])); cosmoV_typeError(state, "os.read()", "<string>", "%s", cosmoV_typeStr(args[0]));
return 0;
} }
if (nargs == 2) { CObjString *str = cosmoV_readString(args[0]);
if (!IS_STRING(args[1])) {
cosmoV_typeError(state, "os.open()", "<string>, <string>", "%s, %s",
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
}
mode = cosmoV_readCString(args[1]); // open file
} else if (nargs != 1) { FILE *file = fopen(str->str, "rb");
cosmoV_error(state, "os.open() expected 1 or 2 arguments, got %d!", nargs); char *buf;
} size_t size, bRead;
} else {
cosmoV_error(state, "os.open() expected 1 or 2 arguments, got %d!", nargs);
}
filePath = cosmoV_readCString(args[0]);
file = fopen(filePath, mode);
if (file == NULL) { if (file == NULL) {
cosmoV_pushBoolean(state, true); // return nil, file doesn't exist
cosmoV_pushFString(state, "Failed to open %s!", filePath); return 0;
return 2;
} }
cosmoV_pushBoolean(state, false); // grab the size of the file
pushFileObj(state, file); fseek(file, 0L, SEEK_END);
return 2; size = ftell(file);
rewind(file);
buf = cosmoM_xmalloc(state, size + 1); // +1 for the NULL terminator
bRead = fread(buf, sizeof(char), size, file); // read the file into the buffer
if (bRead < size) {
// an error occured! we don't need to really throw an error, returning a nil is good enough
return 0;
}
buf[bRead] = '\0'; // place the NULL terminator at the end of the buffer
// push the string to the stack to return
cosmoV_pushValue(state, cosmoV_newRef(cosmoO_takeString(state, buf, bRead)));
return 1;
} }
// os.time() // os.time()
@@ -443,6 +305,7 @@ int cosmoB_osTime(CState *state, int nargs, CValue *args)
struct timeval time; struct timeval time;
if (nargs > 0) { if (nargs > 0) {
cosmoV_error(state, "os.time() expected no arguments, got %d!", nargs); cosmoV_error(state, "os.time() expected no arguments, got %d!", nargs);
return 0;
} }
gettimeofday(&time, NULL); gettimeofday(&time, NULL);
@@ -455,10 +318,12 @@ int cosmoB_osSystem(CState *state, int nargs, CValue *args)
{ {
if (nargs != 1) { if (nargs != 1) {
cosmoV_error(state, "os.system() expects 1 argument, got %d!", nargs); cosmoV_error(state, "os.system() expects 1 argument, got %d!", nargs);
return 0;
} }
if (!IS_STRING(args[0])) { if (!IS_STRING(args[0])) {
cosmoV_typeError(state, "os.system()", "<string>", "%s", cosmoV_typeStr(args[0])); cosmoV_typeError(state, "os.system()", "<string>", "%s", cosmoV_typeStr(args[0]));
return 0;
} }
// run the command and return the exit code // run the command and return the exit code
@@ -466,10 +331,11 @@ int cosmoB_osSystem(CState *state, int nargs, CValue *args)
return 1; return 1;
} }
COSMO_API void cosmoB_loadOS(CState *state) COSMO_API void cosmoB_loadOSLib(CState *state)
{ {
const char *identifiers[] = {"open", "time", "system"}; const char *identifiers[] = {"read", "time", "system"};
CosmoCFunction osLib[] = {cosmoB_osOpen, cosmoB_osTime, cosmoB_osSystem};
CosmoCFunction osLib[] = {cosmoB_osRead, cosmoB_osTime, cosmoB_osSystem};
cosmoV_pushString(state, "os"); cosmoV_pushString(state, "os");
@@ -480,30 +346,7 @@ COSMO_API void cosmoB_loadOS(CState *state)
} }
cosmoV_makeObject(state, i); cosmoV_makeObject(state, i);
cosmoV_addGlobals(state, 1); // register the os.* object to the global table cosmoV_register(state, 1); // register the os.* object to the global table
// make file proto
cosmoV_pushString(state, "file");
CObjObject *fileProto = cosmoO_newObject(state);
cosmoV_pushRef(state, (CObj *)fileProto);
cosmoV_pushRef(state, (CObj *)fileProto);
cosmoV_pushString(state, "read");
cosmoV_pushCFunction(state, fileB_read);
cosmoV_set(state);
cosmoV_pushRef(state, (CObj *)fileProto);
cosmoV_pushString(state, "write");
cosmoV_pushCFunction(state, fileB_write);
cosmoV_set(state);
cosmoV_pushRef(state, (CObj *)fileProto);
cosmoV_pushString(state, "__gc");
cosmoV_pushCFunction(state, fileB_gc);
cosmoV_set(state);
cosmoV_addRegistry(state, 1);
} }
// ================================================================ [STRING.*] // ================================================================ [STRING.*]
@@ -515,6 +358,7 @@ int cosmoB_sSub(CState *state, int nargs, CValue *args)
if (!IS_STRING(args[0]) || !IS_NUMBER(args[1])) { if (!IS_STRING(args[0]) || !IS_NUMBER(args[1])) {
cosmoV_typeError(state, "string.sub()", "<string>, <number>", "%s, %s", cosmoV_typeError(state, "string.sub()", "<string>, <number>", "%s, %s",
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1])); cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
return 0;
} }
CObjString *str = cosmoV_readString(args[0]); CObjString *str = cosmoV_readString(args[0]);
@@ -524,6 +368,7 @@ int cosmoB_sSub(CState *state, int nargs, CValue *args)
if (indx < 0 || indx >= str->length) { if (indx < 0 || indx >= str->length) {
cosmoV_error(state, "string.sub() expected index to be 0-%d, got %d!", str->length - 1, cosmoV_error(state, "string.sub() expected index to be 0-%d, got %d!", str->length - 1,
indx); indx);
return 0;
} }
cosmoV_pushLString(state, str->str + ((int)indx), str->length - ((int)indx)); cosmoV_pushLString(state, str->str + ((int)indx), str->length - ((int)indx));
@@ -532,6 +377,7 @@ int cosmoB_sSub(CState *state, int nargs, CValue *args)
cosmoV_typeError(state, "string.sub()", "<string>, <number>, <number>", "%s, %s, %s", cosmoV_typeError(state, "string.sub()", "<string>, <number>, <number>", "%s, %s, %s",
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]), cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]),
cosmoV_typeStr(args[2])); cosmoV_typeStr(args[2]));
return 0;
} }
CObjString *str = cosmoV_readString(args[0]); CObjString *str = cosmoV_readString(args[0]);
@@ -543,11 +389,13 @@ int cosmoB_sSub(CState *state, int nargs, CValue *args)
cosmoV_error( cosmoV_error(
state, "string.sub() expected subbed string goes out of bounds, max length is %d!", state, "string.sub() expected subbed string goes out of bounds, max length is %d!",
str->length); str->length);
return 0;
} }
cosmoV_pushLString(state, str->str + ((int)indx), ((int)length)); cosmoV_pushLString(state, str->str + ((int)indx), ((int)length));
} else { } else {
cosmoV_error(state, "string.sub() expected 2 or 3 arguments, got %d!", nargs); cosmoV_error(state, "string.sub() expected 2 or 3 arguments, got %d!", nargs);
return 0;
} }
return 1; return 1;
@@ -560,6 +408,7 @@ int cosmoB_sFind(CState *state, int nargs, CValue *args)
if (!IS_STRING(args[0]) || !IS_STRING(args[1])) { if (!IS_STRING(args[0]) || !IS_STRING(args[1])) {
cosmoV_typeError(state, "string.find()", "<string>, <string>", "%s, %s", cosmoV_typeError(state, "string.find()", "<string>, <string>", "%s, %s",
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1])); cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
return 0;
} }
CObjString *str = cosmoV_readString(args[0]); CObjString *str = cosmoV_readString(args[0]);
@@ -574,12 +423,13 @@ int cosmoB_sFind(CState *state, int nargs, CValue *args)
} }
// success! push the index // success! push the index
cosmoV_pushNumber(state, (cosmo_Number)(indx - str->str)); cosmoV_pushNumber(state, indx - str->str);
} else if (nargs == 3) { } else if (nargs == 3) {
if (!IS_STRING(args[0]) || !IS_STRING(args[1]) || !IS_NUMBER(args[2])) { if (!IS_STRING(args[0]) || !IS_STRING(args[1]) || !IS_NUMBER(args[2])) {
cosmoV_typeError(state, "string.find()", "<string>, <string>, <number>", "%s, %s, %s", cosmoV_typeError(state, "string.find()", "<string>, <string>, <number>", "%s, %s, %s",
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]), cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]),
cosmoV_typeStr(args[2])); cosmoV_typeStr(args[2]));
return 0;
} }
CObjString *str = cosmoV_readString(args[0]); CObjString *str = cosmoV_readString(args[0]);
@@ -595,9 +445,10 @@ int cosmoB_sFind(CState *state, int nargs, CValue *args)
} }
// success! push the index // success! push the index
cosmoV_pushNumber(state, (cosmo_Number)(indx - str->str)); cosmoV_pushNumber(state, indx - str->str);
} else { } else {
cosmoV_error(state, "string.find() expected 2 or 3 arguments, got %d!", nargs); cosmoV_error(state, "string.find() expected 2 or 3 arguments, got %d!", nargs);
return 0;
} }
return 1; return 1;
@@ -608,11 +459,13 @@ int cosmoB_sSplit(CState *state, int nargs, CValue *args)
{ {
if (nargs != 2) { if (nargs != 2) {
cosmoV_error(state, "string.split() expected 2 arguments, got %d!", nargs); cosmoV_error(state, "string.split() expected 2 arguments, got %d!", nargs);
return 0;
} }
if (!IS_STRING(args[0]) || !IS_STRING(args[1])) { if (!IS_STRING(args[0]) || !IS_STRING(args[1])) {
cosmoV_typeError(state, "string.split()", "<string>, <string>", "%s, %s", cosmoV_typeError(state, "string.split()", "<string>, <string>", "%s, %s",
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1])); cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
return 0;
} }
CObjString *str = cosmoV_readString(args[0]); CObjString *str = cosmoV_readString(args[0]);
@@ -643,10 +496,12 @@ int cosmoB_sByte(CState *state, int nargs, CValue *args)
{ {
if (nargs != 1) { if (nargs != 1) {
cosmoV_error(state, "string.byte() expected 1 argument, got %d!", nargs); cosmoV_error(state, "string.byte() expected 1 argument, got %d!", nargs);
return 0;
} }
if (!IS_STRING(args[0])) { if (!IS_STRING(args[0])) {
cosmoV_typeError(state, "string.byte", "<string>", "%s", cosmoV_typeStr(args[0])); cosmoV_typeError(state, "string.byte", "<string>", "%s", cosmoV_typeStr(args[0]));
return 0;
} }
CObjString *str = cosmoV_readString(args[0]); CObjString *str = cosmoV_readString(args[0]);
@@ -667,10 +522,12 @@ int cosmoB_sChar(CState *state, int nargs, CValue *args)
{ {
if (nargs != 1) { if (nargs != 1) {
cosmoV_error(state, "string.char() expected 1 argument, got %d!", nargs); cosmoV_error(state, "string.char() expected 1 argument, got %d!", nargs);
return 0;
} }
if (!IS_NUMBER(args[0])) { if (!IS_NUMBER(args[0])) {
cosmoV_typeError(state, "string.char", "<number>", "%s", cosmoV_typeStr(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 // small side effect of truncating the number, but ignoring the decimal instead of throwing an
@@ -680,6 +537,7 @@ int cosmoB_sChar(CState *state, int nargs, CValue *args)
if (num > 255 || num < 0) { if (num > 255 || num < 0) {
cosmoV_error(state, "Character expected to be in range 0-255, got %d!", num); 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 // basically, treat the character value on the C stack as an """"array"""" with a length of 1
@@ -691,13 +549,15 @@ int cosmoB_sLen(CState *state, int nargs, CValue *args)
{ {
if (nargs < 1) { if (nargs < 1) {
cosmoV_error(state, "string.len() expected 1 argument, got %d!", nargs); cosmoV_error(state, "string.len() expected 1 argument, got %d!", nargs);
return 0;
} }
if (!IS_STRING(args[0])) { if (!IS_STRING(args[0])) {
cosmoV_typeError(state, "string.len", "<string>", "%s", cosmoV_typeStr(args[0])); cosmoV_typeError(state, "string.len", "<string>", "%s", cosmoV_typeStr(args[0]));
return 0;
} }
cosmoV_pushNumber(state, (cosmo_Number)strlen(cosmoV_readCString(args[0]))); cosmoV_pushNumber(state, strlen(cosmoV_readCString(args[0])));
return 1; return 1;
} }
@@ -706,12 +566,14 @@ int cosmoB_sRep(CState *state, int nargs, CValue *args)
{ {
if (nargs != 2) { if (nargs != 2) {
cosmoV_error(state, "string.rep() expected 2 arguments, got %d!", nargs); cosmoV_error(state, "string.rep() expected 2 arguments, got %d!", nargs);
return 0;
} }
// expects <string>, <number> // expects <string>, <number>
if (!IS_STRING(args[0]) || !IS_NUMBER(args[1])) { if (!IS_STRING(args[0]) || !IS_NUMBER(args[1])) {
cosmoV_typeError(state, "string.rep", "<string>, <number>", "%s, %s", cosmoV_typeError(state, "string.rep", "<string>, <number>", "%s, %s",
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1])); cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
return 0;
} }
CObjString *str = cosmoV_readString(args[0]); CObjString *str = cosmoV_readString(args[0]);
@@ -727,9 +589,8 @@ int cosmoB_sRep(CState *state, int nargs, CValue *args)
char *newStr = cosmoM_xmalloc(state, length + 1); // + 1 for the NULL terminator char *newStr = cosmoM_xmalloc(state, length + 1); // + 1 for the NULL terminator
// copy the string over the new buffer // copy the string over the new buffer
for (int i = 0; i < times; i++) { for (int i = 0; i < times; i++)
memcpy(&newStr[i * str->length], str->str, str->length); memcpy(&newStr[i * str->length], str->str, str->length);
}
// write the NULL terminator // write the NULL terminator
newStr[length] = '\0'; newStr[length] = '\0';
@@ -760,7 +621,7 @@ void cosmoB_loadStrLib(CState *state)
cosmoV_registerProtoObject(state, COBJ_STRING, obj); cosmoV_registerProtoObject(state, COBJ_STRING, obj);
// register "string" to the global table // register "string" to the global table
cosmoV_addGlobals(state, 1); cosmoV_register(state, 1);
} }
// ================================================================ [MATH] // ================================================================ [MATH]
@@ -770,10 +631,12 @@ int cosmoB_mAbs(CState *state, int nargs, CValue *args)
{ {
if (nargs != 1) { if (nargs != 1) {
cosmoV_error(state, "math.abs() expected 1 argument, got %d!", nargs); cosmoV_error(state, "math.abs() expected 1 argument, got %d!", nargs);
return 0;
} }
if (!IS_NUMBER(args[0])) { if (!IS_NUMBER(args[0])) {
cosmoV_typeError(state, "math.abs", "<number>", "%s", cosmoV_typeStr(args[0])); cosmoV_typeError(state, "math.abs", "<number>", "%s", cosmoV_typeStr(args[0]));
return 0;
} }
cosmoV_pushNumber(state, fabs(cosmoV_readNumber(args[0]))); cosmoV_pushNumber(state, fabs(cosmoV_readNumber(args[0])));
@@ -785,10 +648,12 @@ int cosmoB_mFloor(CState *state, int nargs, CValue *args)
{ {
if (nargs != 1) { if (nargs != 1) {
cosmoV_error(state, "math.floor() expected 1 argument, got %d!", nargs); cosmoV_error(state, "math.floor() expected 1 argument, got %d!", nargs);
return 0;
} }
if (!IS_NUMBER(args[0])) { if (!IS_NUMBER(args[0])) {
cosmoV_typeError(state, "math.floor", "<number>", "%s", cosmoV_typeStr(args[0])); cosmoV_typeError(state, "math.floor", "<number>", "%s", cosmoV_typeStr(args[0]));
return 0;
} }
cosmoV_pushNumber(state, (int)cosmoV_readNumber(args[0])); cosmoV_pushNumber(state, (int)cosmoV_readNumber(args[0]));
@@ -800,10 +665,12 @@ int cosmoB_mCeil(CState *state, int nargs, CValue *args)
{ {
if (nargs != 1) { if (nargs != 1) {
cosmoV_error(state, "math.ceil() expected 1 argument, got %d!", nargs); cosmoV_error(state, "math.ceil() expected 1 argument, got %d!", nargs);
return 0;
} }
if (!IS_NUMBER(args[0])) { if (!IS_NUMBER(args[0])) {
cosmoV_typeError(state, "math.ceil", "<number>", "%s", cosmoV_typeStr(args[0])); cosmoV_typeError(state, "math.ceil", "<number>", "%s", cosmoV_typeStr(args[0]));
return 0;
} }
int roundedDown = (int)cosmoV_readNumber(args[0]); int roundedDown = (int)cosmoV_readNumber(args[0]);
@@ -822,10 +689,12 @@ int cosmoB_mSin(CState *state, int nargs, CValue *args)
{ {
if (nargs != 1) { if (nargs != 1) {
cosmoV_error(state, "math.sin() expected 1 argument, got %d!", nargs); cosmoV_error(state, "math.sin() expected 1 argument, got %d!", nargs);
return 0;
} }
if (!IS_NUMBER(args[0])) { if (!IS_NUMBER(args[0])) {
cosmoV_typeError(state, "math.sin", "<number>", "%s", cosmoV_typeStr(args[0])); cosmoV_typeError(state, "math.sin", "<number>", "%s", cosmoV_typeStr(args[0]));
return 0;
} }
cosmoV_pushNumber(state, sin(cosmoV_readNumber(args[0]))); cosmoV_pushNumber(state, sin(cosmoV_readNumber(args[0])));
@@ -836,10 +705,12 @@ int cosmoB_mCos(CState *state, int nargs, CValue *args)
{ {
if (nargs != 1) { if (nargs != 1) {
cosmoV_error(state, "math.cos() expected 1 argument, got %d!", nargs); cosmoV_error(state, "math.cos() expected 1 argument, got %d!", nargs);
return 0;
} }
if (!IS_NUMBER(args[0])) { if (!IS_NUMBER(args[0])) {
cosmoV_typeError(state, "math.cos", "<number>", "%s", cosmoV_typeStr(args[0])); cosmoV_typeError(state, "math.cos", "<number>", "%s", cosmoV_typeStr(args[0]));
return 0;
} }
cosmoV_pushNumber(state, cos(cosmoV_readNumber(args[0]))); cosmoV_pushNumber(state, cos(cosmoV_readNumber(args[0])));
@@ -850,10 +721,12 @@ int cosmoB_mTan(CState *state, int nargs, CValue *args)
{ {
if (nargs != 1) { if (nargs != 1) {
cosmoV_error(state, "math.tan() expected 1 argument, got %d!", nargs); cosmoV_error(state, "math.tan() expected 1 argument, got %d!", nargs);
return 0;
} }
if (!IS_NUMBER(args[0])) { if (!IS_NUMBER(args[0])) {
cosmoV_typeError(state, "math.tan", "<number>", "%s", cosmoV_typeStr(args[0])); cosmoV_typeError(state, "math.tan", "<number>", "%s", cosmoV_typeStr(args[0]));
return 0;
} }
cosmoV_pushNumber(state, tan(cosmoV_readNumber(args[0]))); cosmoV_pushNumber(state, tan(cosmoV_readNumber(args[0])));
@@ -864,10 +737,12 @@ int cosmoB_mASin(CState *state, int nargs, CValue *args)
{ {
if (nargs != 1) { if (nargs != 1) {
cosmoV_error(state, "math.asin() expected 1 argument, got %d!", nargs); cosmoV_error(state, "math.asin() expected 1 argument, got %d!", nargs);
return 0;
} }
if (!IS_NUMBER(args[0])) { if (!IS_NUMBER(args[0])) {
cosmoV_typeError(state, "math.asin", "<number>", "%s", cosmoV_typeStr(args[0])); cosmoV_typeError(state, "math.asin", "<number>", "%s", cosmoV_typeStr(args[0]));
return 0;
} }
cosmoV_pushNumber(state, asin(cosmoV_readNumber(args[0]))); cosmoV_pushNumber(state, asin(cosmoV_readNumber(args[0])));
@@ -878,10 +753,12 @@ int cosmoB_mACos(CState *state, int nargs, CValue *args)
{ {
if (nargs != 1) { if (nargs != 1) {
cosmoV_error(state, "math.acos() expected 1 argument, got %d!", nargs); cosmoV_error(state, "math.acos() expected 1 argument, got %d!", nargs);
return 0;
} }
if (!IS_NUMBER(args[0])) { if (!IS_NUMBER(args[0])) {
cosmoV_typeError(state, "math.acos", "<number>", "%s", cosmoV_typeStr(args[0])); cosmoV_typeError(state, "math.acos", "<number>", "%s", cosmoV_typeStr(args[0]));
return 0;
} }
cosmoV_pushNumber(state, acos(cosmoV_readNumber(args[0]))); cosmoV_pushNumber(state, acos(cosmoV_readNumber(args[0])));
@@ -892,10 +769,12 @@ int cosmoB_mATan(CState *state, int nargs, CValue *args)
{ {
if (nargs != 1) { if (nargs != 1) {
cosmoV_error(state, "math.atan() expected 1 argument, got %d!", nargs); cosmoV_error(state, "math.atan() expected 1 argument, got %d!", nargs);
return 0;
} }
if (!IS_NUMBER(args[0])) { if (!IS_NUMBER(args[0])) {
cosmoV_typeError(state, "math.atan", "<number>", "%s", cosmoV_typeStr(args[0])); cosmoV_typeError(state, "math.atan", "<number>", "%s", cosmoV_typeStr(args[0]));
return 0;
} }
cosmoV_pushNumber(state, atan(cosmoV_readNumber(args[0]))); cosmoV_pushNumber(state, atan(cosmoV_readNumber(args[0])));
@@ -906,10 +785,12 @@ int cosmoB_mRad(CState *state, int nargs, CValue *args)
{ {
if (nargs != 1) { if (nargs != 1) {
cosmoV_error(state, "math.rad() expected 1 argument, got %d!", nargs); cosmoV_error(state, "math.rad() expected 1 argument, got %d!", nargs);
return 0;
} }
if (!IS_NUMBER(args[0])) { if (!IS_NUMBER(args[0])) {
cosmoV_typeError(state, "math.rad", "<number>", "%s", cosmoV_typeStr(args[0])); cosmoV_typeError(state, "math.rad", "<number>", "%s", cosmoV_typeStr(args[0]));
return 0;
} }
// convert the degree to radians // convert the degree to radians
@@ -921,10 +802,12 @@ int cosmoB_mDeg(CState *state, int nargs, CValue *args)
{ {
if (nargs != 1) { if (nargs != 1) {
cosmoV_error(state, "math.deg() expected 1 argument, got %d!", nargs); cosmoV_error(state, "math.deg() expected 1 argument, got %d!", nargs);
return 0;
} }
if (!IS_NUMBER(args[0])) { if (!IS_NUMBER(args[0])) {
cosmoV_typeError(state, "math.deg", "<number>", "%s", cosmoV_typeStr(args[0])); cosmoV_typeError(state, "math.deg", "<number>", "%s", cosmoV_typeStr(args[0]));
return 0;
} }
// convert the degree to radians // convert the degree to radians
@@ -940,10 +823,10 @@ void cosmoB_loadMathLib(CState *state)
CosmoCFunction mathLib[] = {cosmoB_mAbs, cosmoB_mFloor, cosmoB_mCeil, cosmoB_mSin, CosmoCFunction mathLib[] = {cosmoB_mAbs, cosmoB_mFloor, cosmoB_mCeil, cosmoB_mSin,
cosmoB_mCos, cosmoB_mTan, cosmoB_mASin, cosmoB_mACos, cosmoB_mCos, cosmoB_mTan, cosmoB_mASin, cosmoB_mACos,
cosmoB_mATan, cosmoB_mRad, cosmoB_mDeg}; cosmoB_mATan, cosmoB_mRad, cosmoB_mDeg};
int i;
// make math library object // make math library object
cosmoV_pushString(state, "math"); cosmoV_pushString(state, "math");
int i;
for (i = 0; i < sizeof(identifiers) / sizeof(identifiers[0]); i++) { for (i = 0; i < sizeof(identifiers) / sizeof(identifiers[0]); i++) {
cosmoV_pushString(state, identifiers[i]); cosmoV_pushString(state, identifiers[i]);
cosmoV_pushCFunction(state, mathLib[i]); cosmoV_pushCFunction(state, mathLib[i]);
@@ -955,7 +838,7 @@ void cosmoB_loadMathLib(CState *state)
// make the object and register it as a global to the state // make the object and register it as a global to the state
cosmoV_makeObject(state, i); cosmoV_makeObject(state, i);
cosmoV_addGlobals(state, 1); cosmoV_register(state, 1);
} }
// ================================================================ [VM.*] // ================================================================ [VM.*]
@@ -973,11 +856,13 @@ int cosmoB_vsetGlobal(CState *state, int nargs, CValue *args)
{ {
if (nargs != 2) { if (nargs != 2) {
cosmoV_error(state, "Expected 2 argumenst, got %d!", nargs); cosmoV_error(state, "Expected 2 argumenst, got %d!", nargs);
return 0;
} }
if (!IS_TABLE(args[1])) { if (!IS_TABLE(args[1])) {
cosmoV_typeError(state, "vm.__setter[\"globals\"]", "<object>, <table>", "%s, %s", cosmoV_typeError(state, "vm.__setter[\"globals\"]", "<object>, <table>", "%s, %s",
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1])); cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
return 0;
} }
// this makes me very nervous ngl // this makes me very nervous ngl
@@ -986,37 +871,17 @@ int cosmoB_vsetGlobal(CState *state, int nargs, CValue *args)
return 0; 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) int cosmoB_vindexBProto(CState *state, int nargs, CValue *args)
{ {
if (nargs != 2) { if (nargs != 2) {
cosmoV_error(state, "Expected 2 arguments, got %d!", nargs); cosmoV_error(state, "Expected 2 arguments, got %d!", nargs);
return 0;
} }
if (!IS_NUMBER(args[1])) { if (!IS_NUMBER(args[1])) {
cosmoV_typeError(state, "baseProtos.__index", "<object>, <number>", "%s, %s", cosmoV_typeError(state, "baseProtos.__index", "<object>, <number>", "%s, %s",
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1])); cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
return 0;
} }
int indx = (int)cosmoV_readNumber(args[1]); int indx = (int)cosmoV_readNumber(args[1]);
@@ -1038,12 +903,14 @@ int cosmoB_vnewindexBProto(CState *state, int nargs, CValue *args)
{ {
if (nargs != 3) { if (nargs != 3) {
cosmoV_error(state, "Expected 3 arguments, got %d!", nargs); cosmoV_error(state, "Expected 3 arguments, got %d!", nargs);
return 0;
} }
if (!IS_NUMBER(args[1]) || !IS_OBJECT(args[2])) { if (!IS_NUMBER(args[1]) || !IS_OBJECT(args[2])) {
cosmoV_typeError(state, "baseProtos.__newindex", "<object>, <number>, <object>", cosmoV_typeError(state, "baseProtos.__newindex", "<object>, <number>, <object>",
"%s, %s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]), "%s, %s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]),
cosmoV_typeStr(args[2])); cosmoV_typeStr(args[2]));
return 0;
} }
int indx = (int)cosmoV_readNumber(args[1]); int indx = (int)cosmoV_readNumber(args[1]);
@@ -1051,6 +918,7 @@ int cosmoB_vnewindexBProto(CState *state, int nargs, CValue *args)
if (indx >= COBJ_MAX || indx < 0) { if (indx >= COBJ_MAX || indx < 0) {
cosmoV_error(state, "index out of range! expected 0 - %d, got %d!", COBJ_MAX, indx); cosmoV_error(state, "index out of range! expected 0 - %d, got %d!", COBJ_MAX, indx);
return 0;
} }
cosmoV_registerProtoObject(state, indx, proto); cosmoV_registerProtoObject(state, indx, proto);
@@ -1060,8 +928,16 @@ int cosmoB_vnewindexBProto(CState *state, int nargs, CValue *args)
// vm.collect() // vm.collect()
int cosmoB_vcollect(CState *state, int nargs, CValue *args) int cosmoB_vcollect(CState *state, int nargs, CValue *args)
{ {
// force a garbage collection // first, unfreeze the state (we start frozen on entry to any C Function)
cosmoM_unfreezeGC(state);
// now force a garbage collection
cosmoM_collectGarbage(state); cosmoM_collectGarbage(state);
// and re-freeze the state
cosmoM_freezeGC(state);
// the end!
return 0; return 0;
} }
@@ -1100,11 +976,10 @@ void cosmoB_loadVM(CState *state)
cosmoV_pushString(state, "collect"); cosmoV_pushString(state, "collect");
cosmoV_pushCFunction(state, cosmoB_vcollect); cosmoV_pushCFunction(state, cosmoB_vcollect);
cosmoV_pushString(state, "disassemble"); cosmoV_makeObject(state, 4); // makes the vm object
cosmoV_pushCFunction(state, cosmoB_vdisassemble);
cosmoV_makeObject(state, 5); // makes the vm object
// register "vm" to the global table // register "vm" to the global table
cosmoV_addGlobals(state, 1); cosmoV_register(state, 1);
printf("[WARNING] the vm.* library has been loaded!\n");
} }

View File

@@ -3,13 +3,6 @@
#include "cstate.h" #include "cstate.h"
enum
{
COSMO_USER_NONE, // CObjObject is not a userdata object
COSMO_USER_FILE, // CObjObject is a file object (see cosmoB_osOpen)
COSMO_USER_START // the first user type for user-defined userdata
};
/* loads all of the base library, including: /* loads all of the base library, including:
- base library ("print", "assert", "type", "pcall", "loadstring", etc.) - base library ("print", "assert", "type", "pcall", "loadstring", etc.)
- object library - object library
@@ -29,7 +22,7 @@ COSMO_API void cosmoB_loadObjLib(CState *state);
- os.system() - os.system()
- os.time() - os.time()
*/ */
COSMO_API void cosmoB_loadOS(CState *state); COSMO_API void cosmoB_loadOSLib(CState *state);
/* loads the base string library, including: /* loads the base string library, including:
- string.sub & <string>:sub() - string.sub & <string>:sub()
@@ -57,7 +50,6 @@ COSMO_API void cosmoB_loadMathLib(CState *state);
- manually setting/grabbing base protos of any object (vm.baseProtos) - manually setting/grabbing base protos of any object (vm.baseProtos)
- manually setting/grabbing the global table (vm.globals) - manually setting/grabbing the global table (vm.globals)
- manually invoking a garbage collection event (vm.collect()) - 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 for this reason, it is recommended to NOT load this library in production
*/ */

View File

@@ -27,9 +27,9 @@ void initChunk(CState *state, CChunk *chunk, size_t startCapacity)
void cleanChunk(CState *state, CChunk *chunk) void cleanChunk(CState *state, CChunk *chunk)
{ {
// first, free the chunk buffer // first, free the chunk buffer
cosmoM_freeArray(state, INSTRUCTION, chunk->buf, chunk->capacity); cosmoM_freearray(state, INSTRUCTION, chunk->buf, chunk->capacity);
// then the line info // then the line info
cosmoM_freeArray(state, int, chunk->lineInfo, chunk->capacity); cosmoM_freearray(state, int, chunk->lineInfo, chunk->capacity);
// free the constants // free the constants
cleanValArray(state, &chunk->constants); cleanValArray(state, &chunk->constants);
} }
@@ -49,10 +49,9 @@ int addConstant(CState *state, CChunk *chunk, CValue value)
return i; // we already have a matching constant! return i; // we already have a matching constant!
} }
cosmoV_pushValue(state, value); // push the value to the stack so our GC can see it cosmoM_freezeGC(state); // so our GC doesn't free it
appendValArray(state, &chunk->constants, value); appendValArray(state, &chunk->constants, value);
cosmoV_pop(state); cosmoM_unfreezeGC(state);
return chunk->constants.count - 1; // return the index of the new constants return chunk->constants.count - 1; // return the index of the new constants
} }
@@ -61,8 +60,8 @@ int addConstant(CState *state, CChunk *chunk, CValue value)
void writeu8Chunk(CState *state, CChunk *chunk, INSTRUCTION i, int line) void writeu8Chunk(CState *state, CChunk *chunk, INSTRUCTION i, int line)
{ {
// does the buffer need to be reallocated? // does the buffer need to be reallocated?
cosmoM_growArray(state, INSTRUCTION, chunk->buf, chunk->count, chunk->capacity); cosmoM_growarray(state, INSTRUCTION, chunk->buf, chunk->count, chunk->capacity);
cosmoM_growArray(state, int, chunk->lineInfo, chunk->count, chunk->lineCapacity); cosmoM_growarray(state, int, chunk->lineInfo, chunk->count, chunk->lineCapacity);
// write data to the chunk :) // write data to the chunk :)
chunk->lineInfo[chunk->count] = line; chunk->lineInfo[chunk->count] = line;

View File

@@ -21,8 +21,6 @@ void cleanChunk(CState *state, CChunk *chunk); // frees everything but the struc
void freeChunk(CState *state, CChunk *chunk); // frees everything including the struct void freeChunk(CState *state, CChunk *chunk); // frees everything including the struct
int addConstant(CState *state, CChunk *chunk, CValue value); int addConstant(CState *state, CChunk *chunk, CValue value);
bool validateChunk(CState *state, CChunk *chunk);
// write to chunk // write to chunk
void writeu8Chunk(CState *state, CChunk *chunk, INSTRUCTION i, int line); void writeu8Chunk(CState *state, CChunk *chunk, INSTRUCTION i, int line);
void writeu16Chunk(CState *state, CChunk *chunk, uint16_t i, int line); void writeu16Chunk(CState *state, CChunk *chunk, uint16_t i, int line);

View File

@@ -5,64 +5,63 @@
void printIndent(int indent) void printIndent(int indent)
{ {
for (int i = 0; i < indent; i++) { for (int i = 0; i < indent; i++)
printf("\t"); printf("\t");
}
} }
static int simpleInstruction(const char *name, int offset) int simpleInstruction(const char *name, int offset)
{ {
printf("%s", name); printf("%s", name);
return offset + 1; // consume opcode return offset + 1; // consume opcode
} }
static int u8OperandInstruction(const char *name, CChunk *chunk, int offset) int u8OperandInstruction(const char *name, CChunk *chunk, int offset)
{ {
printf("%-16s [%03d]", name, readu8Chunk(chunk, offset + 1)); printf("%-16s [%03d]", name, readu8Chunk(chunk, offset + 1));
return offset + 2; return offset + 2;
} }
static int u16OperandInstruction(const char *name, CChunk *chunk, int offset) int u16OperandInstruction(const char *name, CChunk *chunk, int offset)
{ {
printf("%-16s [%05d]", name, readu16Chunk(chunk, offset + 1)); printf("%-16s [%05d]", name, readu16Chunk(chunk, offset + 1));
return offset + 1 + (sizeof(uint16_t) / sizeof(INSTRUCTION)); return offset + 1 + (sizeof(uint16_t) / sizeof(INSTRUCTION));
} }
static int JumpInstruction(const char *name, CChunk *chunk, int offset, int dir) int JumpInstruction(const char *name, CChunk *chunk, int offset, int dir)
{ {
int jmp = ((int)readu16Chunk(chunk, offset + 1)) * dir; int jmp = ((int)readu16Chunk(chunk, offset + 1)) * dir;
printf("%-16s [%05d] - jumps to %04d", name, jmp, offset + 3 + jmp); printf("%-16s [%05d] - jumps to %04d", name, jmp, offset + 3 + jmp);
return offset + 1 + (sizeof(uint16_t) / sizeof(INSTRUCTION)); return offset + 1 + (sizeof(uint16_t) / sizeof(INSTRUCTION));
} }
static int u8u8OperandInstruction(const char *name, CChunk *chunk, int offset) int u8u8OperandInstruction(const char *name, CChunk *chunk, int offset)
{ {
printf("%-16s [%03d] [%03d]", name, readu8Chunk(chunk, offset + 1), printf("%-16s [%03d] [%03d]", name, readu8Chunk(chunk, offset + 1),
readu8Chunk(chunk, offset + 2)); readu8Chunk(chunk, offset + 2));
return offset + 3; // op + u8 + u8 return offset + 3; // op + u8 + u8
} }
static int u8u16OperandInstruction(const char *name, CChunk *chunk, int offset) int u8u16OperandInstruction(const char *name, CChunk *chunk, int offset)
{ {
printf("%-16s [%03d] [%05d]", name, readu8Chunk(chunk, offset + 1), printf("%-16s [%03d] [%05d]", name, readu8Chunk(chunk, offset + 1),
readu16Chunk(chunk, offset + 2)); readu16Chunk(chunk, offset + 2));
return offset + 4; // op + u8 + u16 return offset + 4; // op + u8 + u16
} }
static int u8u8u16OperandInstruction(const char *name, CChunk *chunk, int offset) int u8u8u16OperandInstruction(const char *name, CChunk *chunk, int offset)
{ {
printf("%-16s [%03d] [%03d] [%05d]", name, readu8Chunk(chunk, offset + 1), printf("%-16s [%03d] [%03d] [%05d]", name, readu8Chunk(chunk, offset + 1),
readu8Chunk(chunk, offset + 2), readu16Chunk(chunk, offset + 3)); readu8Chunk(chunk, offset + 2), readu16Chunk(chunk, offset + 3));
return offset + 5; // op + u8 + u8 + u16 return offset + 5; // op + u8 + u8 + u16
} }
static int constInstruction(const char *name, CChunk *chunk, int offset) int constInstruction(const char *name, CChunk *chunk, int offset)
{ {
int index = readu16Chunk(chunk, offset + 1); int index = readu16Chunk(chunk, offset + 1);
printf("%-16s [%05d] - ", name, index); printf("%-16s [%05d] - ", name, index);
CValue val = chunk->constants.values[index]; CValue val = chunk->constants.values[index];
cosmoV_printValue(val); printValue(val);
return offset + 1 + (sizeof(uint16_t) / sizeof(INSTRUCTION)); // consume opcode + uint return offset + 1 + (sizeof(uint16_t) / sizeof(INSTRUCTION)); // consume opcode + uint
} }
@@ -128,7 +127,7 @@ int disasmInstr(CChunk *chunk, int offset, int indent)
CObjFunction *cobjFunc = (CObjFunction *)cosmoV_readRef(val); CObjFunction *cobjFunc = (CObjFunction *)cosmoV_readRef(val);
offset += 3; // we consumed the opcode + u16 offset += 3; // we consumed the opcode + u16
cosmoV_printValue(val); printValue(val);
printf("\n"); printf("\n");
// list the upvalues/locals that are captured // list the upvalues/locals that are captured
@@ -223,8 +222,3 @@ int disasmInstr(CChunk *chunk, int offset, int indent)
return 1; return 1;
} }
void cosmoG_disassemble(CObjClosure *closure)
{
disasmChunk(&closure->function->chunk, closure->function->name == NULL ? UNNAMEDCHUNK : closure->function->name->str, 0);
}

View File

@@ -2,13 +2,10 @@
#define CDEBUG_H #define CDEBUG_H
#include "cchunk.h" #include "cchunk.h"
#include "cobj.h"
COSMO_API void disasmChunk(CChunk *chunk, const char *name, int indent); COSMO_API void disasmChunk(CChunk *chunk, const char *name, int indent);
COSMO_API int disasmInstr(CChunk *chunk, int offset, int indent); COSMO_API int disasmInstr(CChunk *chunk, int offset, int indent);
void printIndent(int indent); void printIndent(int indent);
COSMO_API void cosmoG_disassemble(CObjClosure *closure);
#endif #endif

View File

@@ -4,7 +4,6 @@
#include "cmem.h" #include "cmem.h"
#include "cobj.h" #include "cobj.h"
#include "cvalue.h" #include "cvalue.h"
#include "cvm.h"
typedef struct typedef struct
{ {
@@ -14,12 +13,7 @@ typedef struct
int writerStatus; int writerStatus;
} DumpState; } DumpState;
static bool writeCValue(DumpState *dstate, CValue val); static void writeCValue(DumpState *dstate, CValue val);
#define check(e) \
if (!e) { \
return false; \
}
static void initDumpState(CState *state, DumpState *dstate, cosmo_Writer writer, static void initDumpState(CState *state, DumpState *dstate, cosmo_Writer writer,
const void *userData) const void *userData)
@@ -30,92 +24,82 @@ static void initDumpState(CState *state, DumpState *dstate, cosmo_Writer writer,
dstate->writerStatus = 0; dstate->writerStatus = 0;
} }
static bool writeBlock(DumpState *dstate, const void *data, size_t size) static void writeBlock(DumpState *dstate, const void *data, size_t size)
{ {
if (dstate->writerStatus == 0) { if (dstate->writerStatus == 0) {
dstate->writerStatus = dstate->writer(dstate->state, data, size, dstate->userData); dstate->writerStatus = dstate->writer(dstate->state, data, size, dstate->userData);
} }
return dstate->writerStatus == 0;
} }
static bool writeu8(DumpState *dstate, uint8_t d) static void writeu8(DumpState *dstate, uint8_t d)
{ {
return writeBlock(dstate, &d, sizeof(uint8_t)); writeBlock(dstate, &d, sizeof(uint8_t));
} }
static bool writeu32(DumpState *dstate, uint32_t d) static void writeu32(DumpState *dstate, uint32_t d)
{ {
return writeBlock(dstate, &d, sizeof(uint32_t)); writeBlock(dstate, &d, sizeof(uint32_t));
} }
static bool writeSize(DumpState *dstate, size_t d) static void writeSize(DumpState *dstate, size_t d)
{ {
return writeBlock(dstate, &d, sizeof(size_t)); writeBlock(dstate, &d, sizeof(size_t));
} }
static bool writeVector(DumpState *dstate, const void *data, size_t size, size_t count) static void writeVector(DumpState *dstate, const void *data, size_t size, size_t count)
{ {
check(writeSize(dstate, count)); writeSize(dstate, count);
check(writeBlock(dstate, data, size * count)); writeBlock(dstate, data, size * count);
return true;
} }
static bool writeHeader(DumpState *dstate) static void writeHeader(DumpState *dstate)
{ {
check(writeBlock(dstate, COSMO_MAGIC, COSMO_MAGIC_LEN)); writeBlock(dstate, COSMO_MAGIC, COSMO_MAGIC_LEN);
/* after the magic, we write some platform information */ /* after the magic, we write some platform information */
check(writeu8(dstate, cosmoD_isBigEndian())); writeu8(dstate, cosmoD_isBigEndian());
check(writeu8(dstate, sizeof(cosmo_Number))); writeu8(dstate, sizeof(cosmo_Number));
check(writeu8(dstate, sizeof(size_t))); writeu8(dstate, sizeof(size_t));
check(writeu8(dstate, sizeof(int))); writeu8(dstate, sizeof(int));
return true;
} }
static bool writeCObjString(DumpState *dstate, CObjString *obj) static void writeCObjString(DumpState *dstate, CObjString *obj)
{ {
if (obj == NULL) { /* this is in case cobjfunction's name or module strings are null */ if (obj == NULL) { /* this is in case cobjfunction's name or module strings are null */
check(writeu32(dstate, 0)); writeu32(dstate, 0);
return true; return;
} }
/* write string length */ /* write string length */
check(writeu32(dstate, obj->length)); writeu32(dstate, obj->length);
/* write string data */ /* write string data */
check(writeBlock(dstate, obj->str, obj->length)); writeBlock(dstate, obj->str, obj->length);
return true;
} }
static bool writeCObjFunction(DumpState *dstate, CObjFunction *obj) static void writeCObjFunction(DumpState *dstate, CObjFunction *obj)
{ {
check(writeCObjString(dstate, obj->name)); writeCObjString(dstate, obj->name);
check(writeCObjString(dstate, obj->module)); writeCObjString(dstate, obj->module);
check(writeu32(dstate, obj->args)); writeu32(dstate, obj->args);
check(writeu32(dstate, obj->upvals)); writeu32(dstate, obj->upvals);
check(writeu8(dstate, obj->variadic)); writeu8(dstate, obj->variadic);
/* write chunk info */ /* write chunk info */
check(writeVector(dstate, obj->chunk.buf, sizeof(uint8_t), obj->chunk.count)); writeVector(dstate, obj->chunk.buf, sizeof(uint8_t), obj->chunk.count);
/* write line info */ /* write line info */
check(writeVector(dstate, obj->chunk.lineInfo, sizeof(int), obj->chunk.count)); writeVector(dstate, obj->chunk.lineInfo, sizeof(int), obj->chunk.count);
/* write constants */ /* write constants */
check(writeSize(dstate, obj->chunk.constants.count)); writeSize(dstate, obj->chunk.constants.count);
for (int i = 0; i < obj->chunk.constants.count; i++) { for (int i = 0; i < obj->chunk.constants.count; i++) {
check(writeCValue(dstate, obj->chunk.constants.values[i])); writeCValue(dstate, obj->chunk.constants.values[i]);
} }
return true;
} }
static bool writeCObj(DumpState *dstate, CObj *obj) static void writeCObj(DumpState *dstate, CObj *obj)
{ {
/* /*
we can kind of cheat here since our parser only emits a few very limited CObjs... we can kind of cheat here since our parser only emits a few very limited CObjs...
@@ -133,27 +117,24 @@ static bool writeCObj(DumpState *dstate, CObj *obj)
/* write object payload/body */ /* write object payload/body */
switch (t) { switch (t) {
case COBJ_STRING: case COBJ_STRING:
check(writeCObjString(dstate, (CObjString *)obj)); writeCObjString(dstate, (CObjString *)obj);
break; break;
case COBJ_FUNCTION: case COBJ_FUNCTION:
check(writeCObjFunction(dstate, (CObjFunction *)obj)); writeCObjFunction(dstate, (CObjFunction *)obj);
break; break;
default: default:
cosmoV_error(dstate->state, "invalid cobj type: %d", t); break;
return false;
} }
return true;
} }
#define WRITE_VAR(dstate, type, expression) \ #define WRITE_VAR(dstate, type, expression) \
{ \ { \
type _tmp = expression; \ type _tmp = expression; \
check(writeBlock(dstate, &_tmp, sizeof(_tmp))); \ writeBlock(dstate, &_tmp, sizeof(_tmp)); \
break; \ break; \
} }
static bool writeCValue(DumpState *dstate, CValue val) static void writeCValue(DumpState *dstate, CValue val)
{ {
CosmoType t = GET_TYPE(val); CosmoType t = GET_TYPE(val);
@@ -167,16 +148,12 @@ static bool writeCValue(DumpState *dstate, CValue val)
case COSMO_TBOOLEAN: case COSMO_TBOOLEAN:
WRITE_VAR(dstate, bool, cosmoV_readBoolean(val)) WRITE_VAR(dstate, bool, cosmoV_readBoolean(val))
case COSMO_TREF: case COSMO_TREF:
check(writeCObj(dstate, cosmoV_readRef(val))); writeCObj(dstate, cosmoV_readRef(val));
break;
case COSMO_TNIL: /* no body */
break; break;
case COSMO_TNIL: /* fallthrough, no body */
default: default:
cosmoV_error(dstate->state, "invalid value type: %d", t); break;
return false;
} }
return true;
} }
#undef WRITE_VAR #undef WRITE_VAR
@@ -197,8 +174,8 @@ int cosmoD_dump(CState *state, CObjFunction *func, cosmo_Writer writer, const vo
DumpState dstate; DumpState dstate;
initDumpState(state, &dstate, writer, userData); initDumpState(state, &dstate, writer, userData);
check(writeHeader(&dstate)); writeHeader(&dstate);
check(writeCObjFunction(&dstate, func)); writeCObjFunction(&dstate, func);
return dstate.writerStatus; return dstate.writerStatus;
} }

View File

@@ -54,7 +54,7 @@ static void resetBuffer(CLexState *state)
// cancels the token heap buffer and frees it // cancels the token heap buffer and frees it
static void freeBuffer(CLexState *state) static void freeBuffer(CLexState *state)
{ {
cosmoM_freeArray(state->cstate, char, state->buffer, state->bufCap); cosmoM_freearray(state->cstate, char, state->buffer, state->bufCap);
resetBuffer(state); resetBuffer(state);
} }
@@ -62,7 +62,7 @@ static void freeBuffer(CLexState *state)
// adds character to buffer // adds character to buffer
static void appendBuffer(CLexState *state, char c) static void appendBuffer(CLexState *state, char c)
{ {
cosmoM_growArray(state->cstate, char, state->buffer, state->bufCount, state->bufCap); cosmoM_growarray(state->cstate, char, state->buffer, state->bufCount, state->bufCap);
state->buffer[state->bufCount++] = c; state->buffer[state->bufCount++] = c;
} }
@@ -150,7 +150,7 @@ static bool match(CLexState *state, char expected)
return true; return true;
} }
static char peek(CLexState *state) char peek(CLexState *state)
{ {
return *state->currentChar; return *state->currentChar;
} }
@@ -163,7 +163,7 @@ static char peekNext(CLexState *state)
return state->currentChar[1]; return state->currentChar[1];
} }
static char next(CLexState *state) char next(CLexState *state)
{ {
if (isEnd(state)) if (isEnd(state))
return '\0'; // return a null terminator return '\0'; // return a null terminator
@@ -171,12 +171,12 @@ static char next(CLexState *state)
return state->currentChar[-1]; return state->currentChar[-1];
} }
static bool isHex(char c) bool isHex(char c)
{ {
return isNumerical(c) || ('A' <= c && 'F' >= c) || ('a' <= c && 'f' >= c); return isNumerical(c) || ('A' <= c && 'F' >= c) || ('a' <= c && 'f' >= c);
} }
static CTokenType identifierType(CLexState *state) CTokenType identifierType(CLexState *state)
{ {
int length = state->currentChar - state->startChar; int length = state->currentChar - state->startChar;
@@ -192,7 +192,7 @@ static CTokenType identifierType(CLexState *state)
return TOKEN_IDENTIFIER; return TOKEN_IDENTIFIER;
} }
static void skipWhitespace(CLexState *state) void skipWhitespace(CLexState *state)
{ {
while (true) { while (true) {
char c = peek(state); char c = peek(state);
@@ -235,7 +235,7 @@ static void skipWhitespace(CLexState *state)
} }
} }
static CToken parseString(CLexState *state) CToken parseString(CLexState *state)
{ {
makeBuffer(state); // buffer mode makeBuffer(state); // buffer mode
while (peek(state) != '"' && !isEnd(state)) { while (peek(state) != '"' && !isEnd(state)) {
@@ -341,7 +341,7 @@ static CToken parseString(CLexState *state)
return makeToken(state, TOKEN_STRING); return makeToken(state, TOKEN_STRING);
} }
static CToken parseNumber(CLexState *state) CToken parseNumber(CLexState *state)
{ {
switch (peek(state)) { switch (peek(state)) {
case 'x': // hexadecimal number case 'x': // hexadecimal number
@@ -380,7 +380,7 @@ static CToken parseNumber(CLexState *state)
return makeToken(state, TOKEN_NUMBER); return makeToken(state, TOKEN_NUMBER);
} }
static CToken parseIdentifier(CLexState *state) CToken parseIdentifier(CLexState *state)
{ {
// read literal // read literal
while ((isAlpha(peek(state)) || isNumerical(peek(state))) && !isEnd(state)) while ((isAlpha(peek(state)) || isNumerical(peek(state))) && !isEnd(state))
@@ -389,8 +389,9 @@ static CToken parseIdentifier(CLexState *state)
return makeToken(state, identifierType(state)); // is it a reserved word? return makeToken(state, identifierType(state)); // is it a reserved word?
} }
void cosmoL_initLexState(CState *cstate, CLexState *state, const char *source) CLexState *cosmoL_newLexState(CState *cstate, const char *source)
{ {
CLexState *state = cosmoM_xmalloc(cstate, sizeof(CLexState));
state->startChar = (char *)source; state->startChar = (char *)source;
state->currentChar = (char *)source; state->currentChar = (char *)source;
state->line = 1; state->line = 1;
@@ -399,11 +400,13 @@ void cosmoL_initLexState(CState *cstate, CLexState *state, const char *source)
state->cstate = cstate; state->cstate = cstate;
resetBuffer(state); resetBuffer(state);
return state;
} }
void cosmoL_cleanupLexState(CState *state, CLexState *lstate) void cosmoL_freeLexState(CState *state, CLexState *lstate)
{ {
// stubbed cosmoM_free(state, CLexState, lstate);
} }
CToken cosmoL_scanToken(CLexState *state) CToken cosmoL_scanToken(CLexState *state)

View File

@@ -93,7 +93,7 @@ typedef struct
char *currentChar; char *currentChar;
char *startChar; char *startChar;
char *buffer; // if non-NULL & bufCount > 0, token->start & token->length will be set to buffer char *buffer; // if non-NULL & bufCount > 0, token->start & token->length will be set to buffer
// & bufCount respectively. used exclusively for string literals // & bufCount respectively
size_t bufCount; size_t bufCount;
size_t bufCap; size_t bufCap;
int line; // current line int line; // current line
@@ -103,8 +103,8 @@ typedef struct
CState *cstate; CState *cstate;
} CLexState; } CLexState;
void cosmoL_initLexState(CState *cstate, CLexState *state, const char *source); CLexState *cosmoL_newLexState(CState *state, const char *source);
void cosmoL_cleanupLexState(CState *state, CLexState *lstate); void cosmoL_freeLexState(CState *state, CLexState *lstate);
CToken cosmoL_scanToken(CLexState *state); CToken cosmoL_scanToken(CLexState *state);

View File

@@ -6,29 +6,11 @@
#include "cstate.h" #include "cstate.h"
#include "ctable.h" #include "ctable.h"
#include "cvalue.h" #include "cvalue.h"
#include "cvm.h"
// realloc wrapper // realloc wrapper
void *cosmoM_reallocate(CState *state, void *buf, size_t oldSize, size_t newSize) void *cosmoM_reallocate(CState *state, void *buf, size_t oldSize, size_t newSize)
{ {
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; 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 if (newSize == 0) { // it needs to be freed
free(buf); free(buf);
@@ -51,13 +33,8 @@ void *cosmoM_reallocate(CState *state, void *buf, size_t oldSize, size_t newSize
// if NULL is passed, realloc() acts like malloc() // if NULL is passed, realloc() acts like malloc()
void *newBuf = realloc(buf, newSize); 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) { if (newBuf == NULL) {
printf("[ERROR] failed to allocate memory!"); CERROR("failed to allocate memory!");
exit(1); exit(1);
} }
@@ -74,15 +51,15 @@ COSMO_API bool cosmoM_checkGarbage(CState *state, size_t needed)
return false; return false;
} }
static void markObject(CState *state, CObj *obj); void markObject(CState *state, CObj *obj);
static void markValue(CState *state, CValue val); void markValue(CState *state, CValue val);
static void markTable(CState *state, CTable *tbl) void markTable(CState *state, CTable *tbl)
{ {
if (tbl->table == NULL) // table is still being initialized if (tbl->table == NULL) // table is still being initialized
return; return;
int cap = cosmoT_getCapacity(tbl); int cap = tbl->capacityMask + 1;
for (int i = 0; i < cap; i++) { for (int i = 0; i < cap; i++) {
CTableEntry *entry = &tbl->table[i]; CTableEntry *entry = &tbl->table[i];
markValue(state, entry->key); markValue(state, entry->key);
@@ -90,18 +67,13 @@ static void markTable(CState *state, CTable *tbl)
} }
} }
// removes white members from the table // frees white members from the table
static void tableRemoveWhite(CState *state, CTable *tbl) void tableRemoveWhite(CState *state, CTable *tbl)
{ {
if (tbl->table == NULL) // table is still being initialized if (tbl->table == NULL) // table is still being initialized
return; return;
int cap = cosmoT_getCapacity(tbl); int cap = tbl->capacityMask + 1;
#ifdef GC_DEBUG
printf("tableRemoveWhite: %p, cap: %d\n", tbl, cap);
#endif
for (int i = 0; i < cap; i++) { for (int i = 0; i < cap; i++) {
CTableEntry *entry = &tbl->table[i]; CTableEntry *entry = &tbl->table[i];
if (IS_REF(entry->key) && if (IS_REF(entry->key) &&
@@ -114,7 +86,7 @@ static void tableRemoveWhite(CState *state, CTable *tbl)
cosmoT_checkShrink(state, tbl); // recovers the memory we're no longer using cosmoT_checkShrink(state, tbl); // recovers the memory we're no longer using
} }
static void markArray(CState *state, CValueArray *array) void markArray(CState *state, CValueArray *array)
{ {
for (size_t i = 0; i < array->count; i++) { for (size_t i = 0; i < array->count; i++) {
markValue(state, array->values[i]); markValue(state, array->values[i]);
@@ -123,7 +95,7 @@ static void markArray(CState *state, CValueArray *array)
// mark all references associated with the object // mark all references associated with the object
// black = keep, white = discard // black = keep, white = discard
static void blackenObject(CState *state, CObj *obj) void blackenObject(CState *state, CObj *obj)
{ {
markObject(state, (CObj *)obj->proto); markObject(state, (CObj *)obj->proto);
switch (obj->type) { switch (obj->type) {
@@ -165,9 +137,8 @@ static void blackenObject(CState *state, CObj *obj)
markValue(state, err->err); markValue(state, err->err);
// mark callframes // mark callframes
for (int i = 0; i < err->frameCount; i++) { for (int i = 0; i < err->frameCount; i++)
markObject(state, (CObj *)err->frames[i].closure); markObject(state, (CObj *)err->frames[i].closure);
}
break; break;
} }
@@ -190,7 +161,7 @@ static void blackenObject(CState *state, CObj *obj)
} }
} }
static void markObject(CState *state, CObj *obj) void markObject(CState *state, CObj *obj)
{ {
if (obj == NULL || obj->isMarked) // skip if NULL or already marked if (obj == NULL || obj->isMarked) // skip if NULL or already marked
return; return;
@@ -208,20 +179,20 @@ static void markObject(CState *state, CObj *obj)
return; return;
// we can use cosmoM_growarray because we lock the GC when we entered in cosmoM_collectGarbage // we can use cosmoM_growarray because we lock the GC when we entered in cosmoM_collectGarbage
cosmoM_growArray(state, CObj *, state->grayStack.array, state->grayStack.count, cosmoM_growarray(state, CObj *, state->grayStack.array, state->grayStack.count,
state->grayStack.capacity); state->grayStack.capacity);
state->grayStack.array[state->grayStack.count++] = obj; state->grayStack.array[state->grayStack.count++] = obj;
} }
static void markValue(CState *state, CValue val) void markValue(CState *state, CValue val)
{ {
if (IS_REF(val)) if (IS_REF(val))
markObject(state, cosmoV_readRef(val)); markObject(state, cosmoV_readRef(val));
} }
// trace our gray references // trace our gray references
static void traceGrays(CState *state) void traceGrays(CState *state)
{ {
while (state->grayStack.count > 0) { while (state->grayStack.count > 0) {
CObj *obj = state->grayStack.array[--state->grayStack.count]; CObj *obj = state->grayStack.array[--state->grayStack.count];
@@ -229,13 +200,13 @@ static void traceGrays(CState *state)
} }
} }
static void sweep(CState *state) void sweep(CState *state)
{ {
CObj *prev = NULL, *object = state->objects; CObj *prev = NULL;
CObj *object = state->objects;
while (object != NULL) { while (object != NULL) {
if (object->isMarked) { // skip over it if (object->isMarked) { // skip over it
object->isMarked = false; // reset to white object->isMarked = false; // rest to white
prev = object; prev = object;
object = object->next; object = object->next;
} else { // free it! } else { // free it!
@@ -248,23 +219,23 @@ static void sweep(CState *state)
prev->next = object; prev->next = object;
} }
// call __gc on the object
CObjObject *protoObject = cosmoO_grabProto(oldObj);
CValue res;
// use user-defined __gc
if (protoObject != NULL && cosmoO_getIString(state, protoObject, ISTRING_GC, &res)) {
cosmoV_pushValue(state, res);
cosmoV_pushRef(state, (CObj *)oldObj);
cosmoV_call(state, 1, 0);
}
cosmoO_free(state, oldObj); cosmoO_free(state, oldObj);
} }
} }
} }
static void markRoots(CState *state) void markUserRoots(CState *state)
{
CObj *root = state->userRoots;
// traverse userRoots and mark all the object
while (root != NULL) {
markObject(state, root);
root = root->nextRoot;
}
}
void markRoots(CState *state)
{ {
// mark all values on the stack // mark all values on the stack
for (StkPtr value = state->stack; value < state->top; value++) { for (StkPtr value = state->stack; value < state->top; value++) {
@@ -284,26 +255,29 @@ static void markRoots(CState *state)
markObject(state, (CObj *)state->globals); markObject(state, (CObj *)state->globals);
// mark all internal strings // mark all internal strings
for (int i = 0; i < ISTRING_MAX; i++) { for (int i = 0; i < ISTRING_MAX; i++)
markObject(state, (CObj *)state->iStrings[i]); markObject(state, (CObj *)state->iStrings[i]);
}
markTable(state, &state->registry); // mark the user defined roots
markUserRoots(state);
for (int i = 0; i < COBJ_MAX; i++) { // 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]); markObject(state, (CObj *)state->protoObjects[i]);
}
traceGrays(state); traceGrays(state);
} }
COSMO_API void cosmoM_collectGarbage(CState *state) COSMO_API void cosmoM_collectGarbage(CState *state)
{ {
cosmoM_freezeGC(state);
#ifdef GC_DEBUG #ifdef GC_DEBUG
printf("-- GC start\n"); printf("-- GC start\n");
size_t start = state->allocatedBytes; size_t start = state->allocatedBytes;
#endif #endif
cosmoM_freezeGC(state); // we don't want a recursive garbage collection event!
markRoots(state); markRoots(state);
tableRemoveWhite( tableRemoveWhite(
@@ -314,15 +288,59 @@ COSMO_API void cosmoM_collectGarbage(CState *state)
// set our next GC event // set our next GC event
cosmoM_updateThreshhold(state); cosmoM_updateThreshhold(state);
state->freezeGC--; // we don't want to use cosmoM_unfreezeGC because that might trigger a GC
// event (if GC_STRESS is defined)
#ifdef GC_DEBUG #ifdef GC_DEBUG
printf("-- GC end, reclaimed %ld bytes (started at %ld, ended at %ld), next garbage collection " printf("-- GC end, reclaimed %ld bytes (started at %ld, ended at %ld), next garbage collection "
"scheduled at %ld bytes\n", "scheduled at %ld bytes\n",
start - state->allocatedBytes, start, state->allocatedBytes, state->nextGC); start - state->allocatedBytes, start, state->allocatedBytes, state->nextGC);
getchar(); // pauses execution
#endif #endif
cosmoM_unfreezeGC(state);
} }
COSMO_API void cosmoM_updateThreshhold(CState *state) COSMO_API void cosmoM_updateThreshhold(CState *state)
{ {
state->nextGC = state->allocatedBytes * HEAP_GROW_FACTOR; state->nextGC = state->allocatedBytes * HEAP_GROW_FACTOR;
} }
COSMO_API void cosmoM_addRoot(CState *state, CObj *newRoot)
{
// first, check and make sure this root doesn't already exist in the list
CObj *root = state->userRoots;
while (root != NULL) {
if (root == newRoot) // found in the list, abort
return;
root = root->nextRoot;
}
// adds root to userRoot linked list
newRoot->nextRoot = state->userRoots;
state->userRoots = newRoot;
}
COSMO_API void cosmoM_removeRoot(CState *state, CObj *oldRoot)
{
CObj *prev = NULL;
CObj *root = state->userRoots;
// traverse the userRoot linked list
while (root != NULL) {
if (root == oldRoot) { // found root in list
// remove from the linked list
if (prev == NULL) {
state->userRoots = root->nextRoot;
} else {
prev->nextRoot = root->nextRoot;
}
root->nextRoot = NULL;
break;
}
prev = root;
root = root->nextRoot;
}
}

View File

@@ -12,16 +12,16 @@
#define ARRAY_START 8 #define ARRAY_START 8
#ifdef GC_DEBUG #ifdef GC_DEBUG
# define cosmoM_freeArray(state, type, buf, capacity) \ # define cosmoM_freearray(state, type, buf, capacity) \
printf("freeing array %p [size %lu] at %s:%d\n", buf, sizeof(type) * capacity, __FILE__, \ printf("freeing array %p [size %lu] at %s:%d\n", buf, sizeof(type) * capacity, __FILE__, \
__LINE__); \ __LINE__); \
cosmoM_reallocate(state, buf, sizeof(type) * capacity, 0) cosmoM_reallocate(state, buf, sizeof(type) * capacity, 0)
#else #else
# define cosmoM_freeArray(state, type, buf, capacity) \ # define cosmoM_freearray(state, type, buf, capacity) \
cosmoM_reallocate(state, buf, sizeof(type) * capacity, 0) cosmoM_reallocate(state, buf, sizeof(type) * capacity, 0)
#endif #endif
#define cosmoM_growArray(state, type, buf, count, capacity) \ #define cosmoM_growarray(state, type, buf, count, capacity) \
if (count >= capacity || buf == NULL) { \ if (count >= capacity || buf == NULL) { \
int old = capacity; \ int old = capacity; \
capacity = old * GROW_FACTOR; \ capacity = old * GROW_FACTOR; \
@@ -38,7 +38,6 @@
#define cosmoM_isFrozen(state) (state->freezeGC > 0) #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 // if debugging, print the locations of when the state is frozen/unfrozen
#ifdef GC_DEBUG #ifdef GC_DEBUG
# define cosmoM_freezeGC(state) \ # define cosmoM_freezeGC(state) \
@@ -67,7 +66,13 @@ COSMO_API bool cosmoM_checkGarbage(CState *state,
COSMO_API void cosmoM_collectGarbage(CState *state); COSMO_API void cosmoM_collectGarbage(CState *state);
COSMO_API void cosmoM_updateThreshhold(CState *state); COSMO_API void cosmoM_updateThreshhold(CState *state);
// wrapper for cosmoM_reallocate so we can track our memory usage // lets the VM know you are holding a reference to a CObj and to not free it
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)
static inline void *cosmoM_xmalloc(CState *state, size_t sz) static inline void *cosmoM_xmalloc(CState *state, size_t sz)
{ {
return cosmoM_reallocate(state, NULL, 0, sz); return cosmoM_reallocate(state, NULL, 0, sz);

View File

@@ -8,6 +8,7 @@
#include <stdarg.h> #include <stdarg.h>
#include <string.h> #include <string.h>
#include <unistd.h>
// we don't actually hash the whole string :eyes: // we don't actually hash the whole string :eyes:
uint32_t hashString(const char *str, size_t sz) uint32_t hashString(const char *str, size_t sz)
@@ -15,9 +16,8 @@ uint32_t hashString(const char *str, size_t sz)
uint32_t hash = sz; uint32_t hash = sz;
size_t step = (sz >> 5) + 1; size_t step = (sz >> 5) + 1;
for (size_t i = sz; i >= step; i -= step) { for (size_t i = sz; i >= step; i -= step)
hash = ((hash << 5) + (hash >> 2)) + str[i - 1]; hash = ((hash << 5) + (hash >> 2)) + str[i - 1];
}
return hash; return hash;
} }
@@ -32,8 +32,9 @@ CObj *cosmoO_allocateBase(CState *state, size_t sz, CObjType type)
obj->next = state->objects; obj->next = state->objects;
state->objects = obj; state->objects = obj;
obj->nextRoot = NULL;
#ifdef GC_DEBUG #ifdef GC_DEBUG
printf("allocated %s %p\n", cosmoO_typeStr(obj), obj); printf("allocated %p with OBJ_TYPE %d\n", obj, type);
#endif #endif
return obj; return obj;
} }
@@ -41,19 +42,21 @@ CObj *cosmoO_allocateBase(CState *state, size_t sz, CObjType type)
void cosmoO_free(CState *state, CObj *obj) void cosmoO_free(CState *state, CObj *obj)
{ {
#ifdef GC_DEBUG #ifdef GC_DEBUG
printf("freeing %s %p\n", cosmoO_typeStr(obj), obj); printf("freeing %p [", obj);
printObject(obj);
printf("]\n");
#endif #endif
switch (obj->type) { switch (obj->type) {
case COBJ_STRING: { case COBJ_STRING: {
CObjString *objStr = (CObjString *)obj; CObjString *objStr = (CObjString *)obj;
cosmoM_freeArray(state, char, objStr->str, objStr->length + 1); cosmoM_freearray(state, char, objStr->str, objStr->length + 1);
cosmoM_free(state, CObjString, objStr); cosmoM_free(state, CObjString, objStr);
break; break;
} }
case COBJ_OBJECT: { case COBJ_OBJECT: {
CObjObject *objObj = (CObjObject *)obj; CObjObject *objTbl = (CObjObject *)obj;
cosmoT_clearTable(state, &objObj->tbl); cosmoT_clearTable(state, &objTbl->tbl);
cosmoM_free(state, CObjObject, objObj); cosmoM_free(state, CObjObject, objTbl);
break; break;
} }
case COBJ_TABLE: { case COBJ_TABLE: {
@@ -82,13 +85,13 @@ void cosmoO_free(CState *state, CObj *obj)
} }
case COBJ_ERROR: { case COBJ_ERROR: {
CObjError *err = (CObjError *)obj; CObjError *err = (CObjError *)obj;
cosmoM_freeArray(state, CCallFrame, err->frames, err->frameCount); cosmoM_freearray(state, CCallFrame, err->frames, err->frameCount);
cosmoM_free(state, CObjError, obj); cosmoM_free(state, CObjError, obj);
break; break;
} }
case COBJ_CLOSURE: { case COBJ_CLOSURE: {
CObjClosure *closure = (CObjClosure *)obj; CObjClosure *closure = (CObjClosure *)obj;
cosmoM_freeArray(state, CObjUpval *, closure->upvalues, closure->upvalueCount); cosmoM_freearray(state, CObjUpval *, closure->upvalues, closure->upvalueCount);
cosmoM_free(state, CObjClosure, closure); cosmoM_free(state, CObjClosure, closure);
break; break;
} }
@@ -164,12 +167,14 @@ _eqFail:
cosmoV_pushValue(state, eq1); cosmoV_pushValue(state, eq1);
cosmoV_pushRef(state, obj1); cosmoV_pushRef(state, obj1);
cosmoV_pushRef(state, obj2); cosmoV_pushRef(state, obj2);
cosmoV_call(state, 2, 1); if (cosmoV_call(state, 2, 1) != COSMOVM_OK)
return false;
// check return value and make sure it's a boolean // check return value and make sure it's a boolean
if (!IS_BOOLEAN(*cosmoV_getTop(state, 0))) { if (!IS_BOOLEAN(*cosmoV_getTop(state, 0))) {
cosmoV_error(state, "__equal expected to return <boolean>, got %s!", cosmoV_error(state, "__equal expected to return <boolean>, got %s!",
cosmoV_typeStr(*cosmoV_pop(state))); cosmoV_typeStr(*cosmoV_pop(state)));
return false;
} }
// return the result // return the result
@@ -186,10 +191,10 @@ CObjObject *cosmoO_newObject(CState *state)
obj->userP = NULL; // reserved for C API obj->userP = NULL; // reserved for C API
obj->userT = 0; obj->userT = 0;
obj->isLocked = false; obj->isLocked = false;
cosmoV_pushRef(state, (CObj *)obj); // so our GC can keep track of it cosmoV_pushRef(state, (CObj *)obj); // so our GC can keep track of it
cosmoT_initTable(state, &obj->tbl, ARRAY_START); cosmoT_initTable(state, &obj->tbl, ARRAY_START);
cosmoV_pop(state); cosmoV_pop(state);
return obj; return obj;
} }
@@ -229,17 +234,17 @@ CObjCFunction *cosmoO_newCFunction(CState *state, CosmoCFunction func)
CObjError *cosmoO_newError(CState *state, CValue err) 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); CObjError *cerror = (CObjError *)cosmoO_allocateBase(state, sizeof(CObjError), COBJ_ERROR);
cerror->err = err; cerror->err = err;
cerror->frameCount = state->frameCount; cerror->frameCount = state->frameCount;
cerror->frames = frames;
cerror->parserError = false; cerror->parserError = false;
// allocate the callframe
cerror->frames = cosmoM_xmalloc(state, sizeof(CCallFrame) * cerror->frameCount);
// clone the call frame // clone the call frame
for (int i = 0; i < state->frameCount; i++) { for (int i = 0; i < state->frameCount; i++)
cerror->frames[i] = state->callFrame[i]; cerror->frames[i] = state->callFrame[i];
}
return cerror; return cerror;
} }
@@ -306,7 +311,7 @@ CObjString *cosmoO_takeString(CState *state, char *str, size_t length)
// have we already interned this string? // have we already interned this string?
if (lookup != NULL) { if (lookup != NULL) {
cosmoM_freeArray(state, char, str, cosmoM_freearray(state, char, str,
length + 1); // free our passed character array, it's unneeded! length + 1); // free our passed character array, it's unneeded!
return lookup; return lookup;
} }
@@ -322,7 +327,8 @@ CObjString *cosmoO_allocateString(CState *state, const char *str, size_t sz, uin
strObj->length = sz; strObj->length = sz;
strObj->hash = hash; strObj->hash = hash;
// push/pop to make sure GC doesn't collect it // we push & pop the string so our GC can find it (we don't use freezeGC/unfreezeGC because we
// *want* a GC event to happen)
cosmoV_pushRef(state, (CObj *)strObj); cosmoV_pushRef(state, (CObj *)strObj);
cosmoT_insert(state, &state->strings, cosmoV_newRef((CObj *)strObj)); cosmoT_insert(state, &state->strings, cosmoV_newRef((CObj *)strObj));
cosmoV_pop(state); cosmoV_pop(state);
@@ -398,7 +404,7 @@ bool cosmoO_isDescendant(CObj *obj, CObjObject *proto)
} }
// returns false if error thrown // returns false if error thrown
void cosmoO_getRawObject(CState *state, CObjObject *proto, CValue key, CValue *val, CObj *obj) bool cosmoO_getRawObject(CState *state, CObjObject *proto, CValue key, CValue *val, CObj *obj)
{ {
if (!cosmoT_get(state, &proto->tbl, key, if (!cosmoT_get(state, &proto->tbl, key,
val)) { // if the field doesn't exist in the object, check the proto val)) { // if the field doesn't exist in the object, check the proto
@@ -406,19 +412,21 @@ void cosmoO_getRawObject(CState *state, CObjObject *proto, CValue key, CValue *v
cosmoT_get(state, &cosmoV_readTable(*val)->tbl, key, val)) { cosmoT_get(state, &cosmoV_readTable(*val)->tbl, key, val)) {
cosmoV_pushValue(state, *val); // push function cosmoV_pushValue(state, *val); // push function
cosmoV_pushRef(state, (CObj *)obj); // push object cosmoV_pushRef(state, (CObj *)obj); // push object
cosmoV_call(state, 1, 1); // call the function with the 1 argument 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 *val = *cosmoV_pop(state); // set value to the return value of __index
return; return true;
} }
// maybe the field is defined in the proto? if (proto->_obj.proto != NULL &&
if (proto->_obj.proto != NULL) { cosmoO_getRawObject(state, proto->_obj.proto, key, val, obj))
cosmoO_getRawObject(state, proto->_obj.proto, key, val, obj); return true;
return;
}
*val = cosmoV_newNil(); *val = cosmoV_newNil();
return true; // no protoobject to check against / key not found
} }
return true;
} }
void cosmoO_setRawObject(CState *state, CObjObject *proto, CValue key, CValue val, CObj *obj) void cosmoO_setRawObject(CState *state, CObjObject *proto, CValue key, CValue val, CObj *obj)
@@ -519,20 +527,24 @@ bool cosmoO_getIString(CState *state, CObjObject *object, int flag, CValue *val)
return false; // obj->proto was false, the istring doesn't exist in this object chain return false; // obj->proto was false, the istring doesn't exist in this object chain
} }
void cosmoO_indexObject(CState *state, CObjObject *object, CValue key, CValue *val) bool cosmoO_indexObject(CState *state, CObjObject *object, CValue key, CValue *val)
{ {
if (cosmoO_getIString(state, object, ISTRING_INDEX, val)) { if (cosmoO_getIString(state, object, ISTRING_INDEX, val)) {
cosmoV_pushValue(state, *val); // push function cosmoV_pushValue(state, *val); // push function
cosmoV_pushRef(state, (CObj *)object); // push object cosmoV_pushRef(state, (CObj *)object); // push object
cosmoV_pushValue(state, key); // push key cosmoV_pushValue(state, key); // push key
cosmoV_call(state, 2, 1); // call the function with the 2 arguments 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 *val = *cosmoV_pop(state); // set value to the return value of __index
return true;
} else { // there's no __index function defined! } else { // there's no __index function defined!
cosmoV_error(state, "Couldn't index object without __index function!"); cosmoV_error(state, "Couldn't index object without __index function!");
} }
return false;
} }
void cosmoO_newIndexObject(CState *state, CObjObject *object, CValue key, CValue val) bool cosmoO_newIndexObject(CState *state, CObjObject *object, CValue key, CValue val)
{ {
CValue ret; // return value for cosmoO_getIString CValue ret; // return value for cosmoO_getIString
@@ -541,10 +553,12 @@ void cosmoO_newIndexObject(CState *state, CObjObject *object, CValue key, CValue
cosmoV_pushRef(state, (CObj *)object); // push object cosmoV_pushRef(state, (CObj *)object); // push object
cosmoV_pushValue(state, key); // push key & value pair cosmoV_pushValue(state, key); // push key & value pair
cosmoV_pushValue(state, val); cosmoV_pushValue(state, val);
cosmoV_call(state, 3, 0); return cosmoV_call(state, 3, 0) == COSMOVM_OK;
} else { // there's no __newindex function defined } else { // there's no __newindex function defined
cosmoV_error(state, "Couldn't set index on object without __newindex function!"); cosmoV_error(state, "Couldn't set index on object without __newindex function!");
} }
return false;
} }
CObjString *cosmoO_toString(CState *state, CObj *obj) CObjString *cosmoO_toString(CState *state, CObj *obj)
@@ -556,7 +570,8 @@ CObjString *cosmoO_toString(CState *state, CObj *obj)
if (protoObject != NULL && cosmoO_getIString(state, protoObject, ISTRING_TOSTRING, &res)) { if (protoObject != NULL && cosmoO_getIString(state, protoObject, ISTRING_TOSTRING, &res)) {
cosmoV_pushValue(state, res); cosmoV_pushValue(state, res);
cosmoV_pushRef(state, (CObj *)obj); cosmoV_pushRef(state, (CObj *)obj);
cosmoV_call(state, 1, 1); if (cosmoV_call(state, 1, 1) != COSMOVM_OK)
return cosmoO_copyString(state, "<err>", 5);
// make sure the __tostring function returned a string // make sure the __tostring function returned a string
StkPtr ret = cosmoV_getTop(state, 0); StkPtr ret = cosmoV_getTop(state, 0);
@@ -621,12 +636,14 @@ cosmo_Number cosmoO_toNumber(CState *state, CObj *obj)
if (proto != NULL && cosmoO_getIString(state, proto, ISTRING_TONUMBER, &res)) { if (proto != NULL && cosmoO_getIString(state, proto, ISTRING_TONUMBER, &res)) {
cosmoV_pushValue(state, res); cosmoV_pushValue(state, res);
cosmoV_pushRef(state, (CObj *)obj); cosmoV_pushRef(state, (CObj *)obj);
cosmoV_call(state, 1, 1); // call res, expect 1 return val of <number> if (cosmoV_call(state, 1, 1) != COSMOVM_OK) // call res, expect 1 return val of <number>
return 0;
StkPtr temp = cosmoV_getTop(state, 0); StkPtr temp = cosmoV_getTop(state, 0);
if (!IS_NUMBER(*temp)) { if (!IS_NUMBER(*temp)) {
cosmoV_error(state, "__tonumber expected to return <number>, got %s!", cosmoV_error(state, "__tonumber expected to return <number>, got %s!",
cosmoV_typeStr(*temp)); cosmoV_typeStr(*temp));
return 0;
} }
// return number // return number
@@ -652,7 +669,9 @@ int cosmoO_count(CState *state, CObj *obj)
if (proto != NULL && cosmoO_getIString(state, proto, ISTRING_COUNT, &res)) { if (proto != NULL && cosmoO_getIString(state, proto, ISTRING_COUNT, &res)) {
cosmoV_pushValue(state, res); cosmoV_pushValue(state, res);
cosmoV_pushRef(state, (CObj *)obj); cosmoV_pushRef(state, (CObj *)obj);
cosmoV_call(state, 1, 1); // call res, we expect 1 return value of type <number> if (cosmoV_call(state, 1, 1) !=
COSMOVM_OK) // call res, we expect 1 return value of type <number>
return 0;
StkPtr ret = cosmoV_getTop(state, 0); StkPtr ret = cosmoV_getTop(state, 0);
if (!IS_NUMBER(*ret)) { if (!IS_NUMBER(*ret)) {
@@ -683,61 +702,60 @@ int cosmoO_count(CState *state, CObj *obj)
void printObject(CObj *o) void printObject(CObj *o)
{ {
printf("%s ", cosmoO_typeStr(o));
switch (o->type) { switch (o->type) {
case COBJ_STRING: { case COBJ_STRING: {
CObjString *objStr = (CObjString *)o; CObjString *objStr = (CObjString *)o;
printf("\"%.*s\"", objStr->length, objStr->str); printf("<string> \"%.*s\"", objStr->length, objStr->str);
break; break;
} }
case COBJ_OBJECT: { case COBJ_OBJECT: {
printf("%p", (void *)o); printf("<obj> %p", (void *)o);
break; break;
} }
case COBJ_TABLE: { case COBJ_TABLE: {
CObjTable *tbl = (CObjTable *)o; CObjTable *tbl = (CObjTable *)o;
printf("%p", (void *)tbl); printf("<tbl> %p", (void *)tbl);
break; break;
} }
case COBJ_FUNCTION: { case COBJ_FUNCTION: {
CObjFunction *objFunc = (CObjFunction *)o; CObjFunction *objFunc = (CObjFunction *)o;
if (objFunc->name != NULL) if (objFunc->name != NULL)
printf("%.*s", objFunc->name->length, objFunc->name->str); printf("<function> %.*s", objFunc->name->length, objFunc->name->str);
else else
printf("%s", UNNAMEDCHUNK); printf("<function> %s", UNNAMEDCHUNK);
break; break;
} }
case COBJ_CFUNCTION: { case COBJ_CFUNCTION: {
CObjCFunction *objCFunc = (CObjCFunction *)o; CObjCFunction *objCFunc = (CObjCFunction *)o;
printf("%p", (void *)objCFunc->cfunc); printf("<c function> %p", (void *)objCFunc->cfunc);
break; break;
} }
case COBJ_ERROR: { case COBJ_ERROR: {
CObjError *err = (CObjError *)o; CObjError *err = (CObjError *)o;
printf("%p -> ", (void *)o); printf("<error> %p -> ", (void *)o);
cosmoV_printValue(err->err); printValue(err->err);
break; break;
} }
case COBJ_METHOD: { case COBJ_METHOD: {
CObjMethod *method = (CObjMethod *)o; CObjMethod *method = (CObjMethod *)o;
printf("%p -> ", (void *)method); printf("<method> %p -> ", (void *)method);
cosmoV_printValue(method->func); printValue(method->func);
break; break;
} }
case COBJ_CLOSURE: { case COBJ_CLOSURE: {
CObjClosure *closure = (CObjClosure *)o; CObjClosure *closure = (CObjClosure *)o;
printf("%p -> ", (void *)closure); printf("<closure> %p -> ", (void *)closure);
printObject((CObj *)closure->function); // just print the function printObject((CObj *)closure->function); // just print the function
break; break;
} }
case COBJ_UPVALUE: { case COBJ_UPVALUE: {
CObjUpval *upval = (CObjUpval *)o; CObjUpval *upval = (CObjUpval *)o;
printf("%p -> ", (void *)upval->val); printf("<upvalue> %p -> ", (void *)upval->val);
cosmoV_printValue(*upval->val); printValue(*upval->val);
break; break;
} }
default: default:
printf("%p", (void *)o); printf("<unkn obj %p>", (void *)o);
} }
} }
@@ -754,8 +772,6 @@ const char *cosmoO_typeStr(CObj *obj)
return "<function>"; return "<function>";
case COBJ_CFUNCTION: case COBJ_CFUNCTION:
return "<c function>"; return "<c function>";
case COBJ_ERROR:
return "<error>";
case COBJ_METHOD: case COBJ_METHOD:
return "<method>"; return "<method>";
case COBJ_CLOSURE: case COBJ_CLOSURE:

View File

@@ -3,8 +3,6 @@
#include "cosmo.h" #include "cosmo.h"
#include <stdarg.h>
typedef enum CObjType typedef enum CObjType
{ {
COBJ_STRING, COBJ_STRING,
@@ -34,6 +32,7 @@ typedef int (*CosmoCFunction)(CState *state, int argCount, CValue *args);
struct CObj struct CObj
{ {
struct CObj *next; struct CObj *next;
struct CObj *nextRoot; // for the root linked list
struct CObjObject *proto; // protoobject, describes the behavior of the object struct CObjObject *proto; // protoobject, describes the behavior of the object
CObjType type; CObjType type;
bool isMarked; // for the GC bool isMarked; // for the GC
@@ -55,7 +54,7 @@ struct CObjError
CCallFrame *frames; CCallFrame *frames;
int frameCount; int frameCount;
int line; // reserved for parser errors int line; // reserved for parser errors
bool parserError; // if true, cosmoV_printBacktrace will format the error to the lexer bool parserError; // if true, cosmoV_printError will format the error to the lexer
}; };
struct CObjObject struct CObjObject
@@ -137,7 +136,6 @@ struct CObjUpval
#define cosmoV_readCFunction(x) (((CObjCFunction *)cosmoV_readRef(x))->cfunc) #define cosmoV_readCFunction(x) (((CObjCFunction *)cosmoV_readRef(x))->cfunc)
#define cosmoV_readMethod(x) ((CObjMethod *)cosmoV_readRef(x)) #define cosmoV_readMethod(x) ((CObjMethod *)cosmoV_readRef(x))
#define cosmoV_readClosure(x) ((CObjClosure *)cosmoV_readRef(x)) #define cosmoV_readClosure(x) ((CObjClosure *)cosmoV_readRef(x))
#define cosmoV_readError(x) ((CObjError *)cosmoV_readRef(x))
#define cosmoO_readCString(x) ((CObjString *)x)->str #define cosmoO_readCString(x) ((CObjString *)x)->str
#define cosmoO_readType(x) ((CObj *)x)->type #define cosmoO_readType(x) ((CObj *)x)->type
@@ -147,6 +145,7 @@ static inline bool isObjType(CValue val, CObjType type)
return IS_REF(val) && cosmoV_readRef(val)->type == type; return IS_REF(val) && cosmoV_readRef(val)->type == type;
} }
// just protects against macro expansion
static inline bool IS_CALLABLE(CValue val) static inline bool IS_CALLABLE(CValue val)
{ {
return IS_CLOSURE(val) || IS_CFUNCTION(val) || IS_METHOD(val); return IS_CLOSURE(val) || IS_CFUNCTION(val) || IS_METHOD(val);
@@ -173,10 +172,10 @@ static inline CObjObject *cosmoO_grabProto(CObj *obj)
return obj->type == COBJ_OBJECT ? (CObjObject *)obj : obj->proto; return obj->type == COBJ_OBJECT ? (CObjObject *)obj : obj->proto;
} }
void cosmoO_getRawObject(CState *state, CObjObject *proto, CValue key, CValue *val, CObj *obj); bool cosmoO_getRawObject(CState *state, CObjObject *proto, CValue key, CValue *val, CObj *obj);
void cosmoO_setRawObject(CState *state, CObjObject *proto, CValue key, CValue val, CObj *obj); void cosmoO_setRawObject(CState *state, CObjObject *proto, CValue key, CValue val, CObj *obj);
void cosmoO_indexObject(CState *state, CObjObject *object, CValue key, CValue *val); bool cosmoO_indexObject(CState *state, CObjObject *object, CValue key, CValue *val);
void cosmoO_newIndexObject(CState *state, CObjObject *object, CValue key, CValue val); bool cosmoO_newIndexObject(CState *state, CObjObject *object, CValue key, CValue val);
// sets the user-defined pointer, if a user-define integer is already defined it will be over // sets the user-defined pointer, if a user-define integer is already defined it will be over
// written // written

View File

@@ -64,7 +64,7 @@ typedef enum
OP_FALSE, OP_FALSE,
OP_NIL, OP_NIL,
OP_RETURN, OP_RETURN
} COPCODE; // there can be a max of 256 instructions } COPCODE; // there can be a max of 256 instructions
#endif #endif

View File

@@ -14,15 +14,7 @@
performance, however this will produce undefined behavior as you reach the stack limit (and may 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. 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 // #define NAN_BOXXED
// forward declare *most* stuff so our headers are cleaner // forward declare *most* stuff so our headers are cleaner
@@ -64,4 +56,6 @@ typedef int (*cosmo_Writer)(CState *state, const void *data, size_t size, const
#define UNNAMEDCHUNK "_main" #define UNNAMEDCHUNK "_main"
#define COSMOASSERT(x) assert(x) #define COSMOASSERT(x) assert(x)
#define CERROR(err) printf("%s : %s\n", "[ERROR]", err)
#endif #endif

View File

@@ -59,15 +59,14 @@ typedef struct CCompilerState
typedef struct typedef struct
{ {
CLexState lex;
CState *state; CState *state;
CLexState *lex;
CCompilerState *compiler; CCompilerState *compiler;
CObjString *module; // name of the module CObjString *module; // name of the module
CToken current; CToken current;
CToken previous; // token right after the current token CToken previous; // token right after the current token
int workingStackCount; // we push CValues of objects we need onto the stack so the garbage bool hadError;
// collector can see them. this is the count of those values so we'll bool panic;
// know how many to pop off when we're done
} CParseState; } CParseState;
typedef enum typedef enum
@@ -102,18 +101,14 @@ static int expressionPrecedence(CParseState *pstate, int needed, Precedence prec
// returns # of pushed values onto the stack // returns # of pushed values onto the stack
static int expression(CParseState *pstate, int needed, bool forceNeeded); static int expression(CParseState *pstate, int needed, bool forceNeeded);
static void statement(CParseState *pstate); static void statement(CParseState *pstate);
static void declaration(CParseState *pstate);
static void parseFunction(CParseState *pstate, FunctionType type); static void parseFunction(CParseState *pstate, FunctionType type);
static void expressionStatement(CParseState *pstate);
static ParseRule *getRule(CTokenType type); static ParseRule *getRule(CTokenType type);
static CObjFunction *endCompiler(CParseState *pstate); static CObjFunction *endCompiler(CParseState *pstate);
// ================================================================ [FRONT END/TALK TO LEXER] // ================================================================ [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, static void initCompilerState(CParseState *pstate, CCompilerState *ccstate, FunctionType type,
CCompilerState *enclosing) CCompilerState *enclosing)
{ {
@@ -129,8 +124,6 @@ static void initCompilerState(CParseState *pstate, CCompilerState *ccstate, Func
ccstate->function = cosmoO_newFunction(pstate->state); ccstate->function = cosmoO_newFunction(pstate->state);
ccstate->function->module = pstate->module; ccstate->function->module = pstate->module;
keepTrackOf(pstate, cosmoV_newRef((CObj *)ccstate->function));
ccstate->loop.scope = -1; // there is no loop yet ccstate->loop.scope = -1; // there is no loop yet
if (type != FTYPE_SCRIPT) { if (type != FTYPE_SCRIPT) {
@@ -153,27 +146,27 @@ static void initCompilerState(CParseState *pstate, CCompilerState *ccstate, Func
static void initParseState(CParseState *pstate, CCompilerState *ccstate, CState *s, static void initParseState(CParseState *pstate, CCompilerState *ccstate, CState *s,
const char *source, const char *module) const char *source, const char *module)
{ {
cosmoL_initLexState(s, &pstate->lex, source); pstate->lex = cosmoL_newLexState(s, source);
pstate->state = s; pstate->state = s;
pstate->hadError = false;
pstate->panic = false;
pstate->compiler = ccstate; pstate->compiler = ccstate;
pstate->module = cosmoO_copyString(s, module, strlen(module)); 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 initCompilerState(pstate, ccstate, FTYPE_SCRIPT, NULL); // enclosing starts as NULL
} }
static void freeParseState(CParseState *pstate) static void freeParseState(CParseState *pstate)
{ {
cosmoL_cleanupLexState(pstate->state, &pstate->lex); cosmoL_freeLexState(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) static void errorAt(CParseState *pstate, CToken *token, const char *format, va_list args)
{ {
if (pstate->hadError)
return;
if (token->type == TOKEN_EOF) { if (token->type == TOKEN_EOF) {
cosmoV_pushString(pstate->state, "At end: "); cosmoV_pushString(pstate->state, "At end: ");
} else if (!(token->type == TOKEN_ERROR)) { } else if (!(token->type == TOKEN_ERROR)) {
@@ -184,9 +177,14 @@ static void errorAt(CParseState *pstate, CToken *token, const char *format, va_l
cosmoO_pushVFString(pstate->state, format, args); cosmoO_pushVFString(pstate->state, format, args);
// throw complete error string cosmoV_concat(pstate->state, 2); // concats the two strings together
cosmoV_concat(pstate->state, 2);
cosmoV_throw(pstate->state); CObjError *err = cosmoV_throw(pstate->state);
err->line = token->line;
err->parserError = true;
pstate->hadError = true;
pstate->panic = true;
} }
static void errorAtCurrent(CParseState *pstate, const char *format, ...) static void errorAtCurrent(CParseState *pstate, const char *format, ...)
@@ -208,7 +206,7 @@ static void error(CParseState *pstate, const char *format, ...)
static void advance(CParseState *pstate) static void advance(CParseState *pstate)
{ {
pstate->previous = pstate->current; pstate->previous = pstate->current;
pstate->current = cosmoL_scanToken(&pstate->lex); pstate->current = cosmoL_scanToken(pstate->lex);
if (pstate->current.type == TOKEN_ERROR) { if (pstate->current.type == TOKEN_ERROR) {
errorAtCurrent(pstate, pstate->current.start); errorAtCurrent(pstate, pstate->current.start);
@@ -282,6 +280,7 @@ uint16_t makeConstant(CParseState *pstate, CValue val)
int indx = addConstant(pstate->state, getChunk(pstate), val); int indx = addConstant(pstate->state, getChunk(pstate), val);
if (indx > UINT16_MAX) { if (indx > UINT16_MAX) {
error(pstate, "UInt overflow! Too many constants in one chunk!"); error(pstate, "UInt overflow! Too many constants in one chunk!");
return 0;
} }
return (uint16_t)indx; return (uint16_t)indx;
@@ -351,6 +350,7 @@ static void addLocal(CParseState *pstate, CToken name)
{ {
if (pstate->compiler->localCount > UINT8_MAX) { if (pstate->compiler->localCount > UINT8_MAX) {
error(pstate, "UInt overflow! Too many locals in scope!"); error(pstate, "UInt overflow! Too many locals in scope!");
return;
} }
Local *local = &pstate->compiler->locals[pstate->compiler->localCount++]; Local *local = &pstate->compiler->locals[pstate->compiler->localCount++];
@@ -365,6 +365,7 @@ static int addUpvalue(CParseState *pstate, CCompilerState *ccstate, uint8_t indx
if (upvals > UINT8_MAX) { if (upvals > UINT8_MAX) {
error(pstate, "UInt overflow! Too many upvalues in scope!"); error(pstate, "UInt overflow! Too many upvalues in scope!");
return -1;
} }
// check and make sure we haven't already captured it // check and make sure we haven't already captured it
@@ -480,7 +481,6 @@ static void string(CParseState *pstate, bool canAssign, Precedence prec)
{ {
CObjString *strObj = CObjString *strObj =
cosmoO_takeString(pstate->state, pstate->previous.start, pstate->previous.length); cosmoO_takeString(pstate->state, pstate->previous.start, pstate->previous.length);
keepTrackOf(pstate, cosmoV_newRef((CObj *)strObj));
writeConstant(pstate, cosmoV_newRef((CObj *)strObj)); writeConstant(pstate, cosmoV_newRef((CObj *)strObj));
} }
@@ -590,6 +590,8 @@ static void group(CParseState *pstate, bool canAssign, Precedence prec)
consume(pstate, TOKEN_RIGHT_PAREN, "Expected ')'"); consume(pstate, TOKEN_RIGHT_PAREN, "Expected ')'");
} }
#define WRITE_GLOBAL_OP(pstate, op, arg)
static void _etterAB(CParseState *pstate, uint8_t a, int b, bool isGlobal) static void _etterAB(CParseState *pstate, uint8_t a, int b, bool isGlobal)
{ {
writeu8(pstate, a); writeu8(pstate, a);
@@ -766,6 +768,7 @@ static void table(CParseState *pstate, bool canAssign, Precedence prec)
tblType = 1; // array-like tblType = 1; // array-like
} else { } else {
error(pstate, "Can't change table description type mid-definition!"); error(pstate, "Can't change table description type mid-definition!");
return;
} }
entries++; entries++;
@@ -815,7 +818,7 @@ static void object(CParseState *pstate, bool canAssign, Precedence prec)
// "pop" the 1 value // "pop" the 1 value
valuePopped(pstate, 1); valuePopped(pstate, 1);
entries++; entries++;
} while (match(pstate, TOKEN_COMMA)); } while (match(pstate, TOKEN_COMMA) && !pstate->hadError);
consume(pstate, TOKEN_RIGHT_BRACE, "Expected '}' to end object definition."); consume(pstate, TOKEN_RIGHT_BRACE, "Expected '}' to end object definition.");
} }
@@ -1097,19 +1100,18 @@ static ParseRule *getRule(CTokenType type)
// returns true if it got past the first token (aka prefix wasn't null) // returns true if it got past the first token (aka prefix wasn't null)
static bool parsePrecedence(CParseState *pstate, Precedence prec) static bool parsePrecedence(CParseState *pstate, Precedence prec)
{ {
bool canAssign;
ParseFunc prefix, infix;
advance(pstate); advance(pstate);
if ((prefix = getRule(pstate->previous.type)->prefix) == NULL)
ParseFunc prefix = getRule(pstate->previous.type)->prefix;
if (prefix == NULL)
return false; return false;
canAssign = prec <= PREC_ASSIGNMENT; bool canAssign = prec <= PREC_ASSIGNMENT;
prefix(pstate, canAssign, prec); prefix(pstate, canAssign, prec);
while (prec <= getRule(pstate->current.type)->level) {
if ((infix = getRule(pstate->current.type)->infix) == NULL)
break;
while (prec <= getRule(pstate->current.type)->level) {
ParseFunc infix = getRule(pstate->current.type)->infix;
advance(pstate); advance(pstate);
infix(pstate, canAssign, prec); infix(pstate, canAssign, prec);
} }
@@ -1156,6 +1158,9 @@ static uint16_t parseVariable(CParseState *pstate, const char *errorMessage, boo
static void defineVariable(CParseState *pstate, uint16_t global, bool forceLocal) static void defineVariable(CParseState *pstate, uint16_t global, bool forceLocal)
{ {
if (pstate->hadError)
return;
if (pstate->compiler->scopeDepth > 0 || forceLocal) { if (pstate->compiler->scopeDepth > 0 || forceLocal) {
markInitialized(pstate, global); markInitialized(pstate, global);
valuePopped(pstate, 1); // the local stays on the stack! valuePopped(pstate, 1); // the local stays on the stack!
@@ -1172,7 +1177,7 @@ static void _proto(CParseState *pstate)
{ {
int entries = 0; int entries = 0;
while (!match(pstate, TOKEN_END) && !match(pstate, TOKEN_EOF)) { while (!match(pstate, TOKEN_END) && !match(pstate, TOKEN_EOF) && !pstate->hadError) {
if (match(pstate, TOKEN_FUNC)) { if (match(pstate, TOKEN_FUNC)) {
// define method // define method
consume(pstate, TOKEN_IDENTIFIER, "Expected identifier for method!"); consume(pstate, TOKEN_IDENTIFIER, "Expected identifier for method!");
@@ -1219,6 +1224,9 @@ static void localProto(CParseState *pstate)
static void popLocals(CParseState *pstate, int toScope) static void popLocals(CParseState *pstate, int toScope)
{ {
if (pstate->hadError)
return;
// count the locals in scope to pop // count the locals in scope to pop
int localsToPop = 0; int localsToPop = 0;
@@ -1263,7 +1271,7 @@ static void endScope(CParseState *pstate)
static void block(CParseState *pstate) static void block(CParseState *pstate)
{ {
while (!check(pstate, TOKEN_END) && !check(pstate, TOKEN_EOF) && !check(pstate, TOKEN_ERROR)) { while (!check(pstate, TOKEN_END) && !check(pstate, TOKEN_EOF) && !check(pstate, TOKEN_ERROR)) {
statement(pstate); declaration(pstate);
} }
consume(pstate, TOKEN_END, "'end' expected to end block.'"); consume(pstate, TOKEN_END, "'end' expected to end block.'");
@@ -1320,7 +1328,7 @@ static void ifStatement(CParseState *pstate)
while (!check(pstate, TOKEN_END) && !check(pstate, TOKEN_ELSE) && while (!check(pstate, TOKEN_END) && !check(pstate, TOKEN_ELSE) &&
!check(pstate, TOKEN_ELSEIF) && !check(pstate, TOKEN_EOF) && !check(pstate, TOKEN_ELSEIF) && !check(pstate, TOKEN_EOF) &&
!check(pstate, TOKEN_ERROR)) { !check(pstate, TOKEN_ERROR)) {
statement(pstate); declaration(pstate);
} }
endScope(pstate); endScope(pstate);
@@ -1368,7 +1376,7 @@ static void endLoop(CParseState *pstate)
patchJmp(pstate, pstate->compiler->loop.breaks[--pstate->compiler->loop.breakCount]); 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); pstate->compiler->loop.breakCapacity);
} }
@@ -1470,10 +1478,10 @@ static void functionDeclaration(CParseState *pstate)
static void returnStatement(CParseState *pstate) static void returnStatement(CParseState *pstate)
{ {
// if (pstate->compiler->type != FTYPE_FUNCTION && pstate->compiler->type != FTYPE_METHOD) { if (pstate->compiler->type != FTYPE_FUNCTION && pstate->compiler->type != FTYPE_METHOD) {
// error(pstate, "Expected 'return' in function!"); error(pstate, "Expected 'return' in function!");
// return; return;
// } }
if (blockFollow(pstate->current)) { // does this return have a value if (blockFollow(pstate->current)) { // does this return have a value
writeu8(pstate, OP_NIL); writeu8(pstate, OP_NIL);
@@ -1588,7 +1596,7 @@ static void forLoop(CParseState *pstate)
// parse initializer // parse initializer
if (!match(pstate, TOKEN_EOS)) { if (!match(pstate, TOKEN_EOS)) {
statement(pstate); expressionStatement(pstate);
consume(pstate, TOKEN_EOS, "Expected ';' after initializer"); consume(pstate, TOKEN_EOS, "Expected ';' after initializer");
} }
@@ -1657,7 +1665,7 @@ static void breakStatement(CParseState *pstate)
pstate->compiler->localCount = savedLocals; pstate->compiler->localCount = savedLocals;
// add break to loop // 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.breakCount, pstate->compiler->loop.breakCapacity);
pstate->compiler->loop.breaks[pstate->compiler->loop.breakCount++] = writeJmp(pstate, OP_JMP); pstate->compiler->loop.breaks[pstate->compiler->loop.breakCount++] = writeJmp(pstate, OP_JMP);
} }
@@ -1678,6 +1686,18 @@ static void continueStatement(CParseState *pstate)
writeJmpBack(pstate, pstate->compiler->loop.startBytecode); 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) static int expressionPrecedence(CParseState *pstate, int needed, Precedence prec, bool forceNeeded)
{ {
int lastExpected = pstate->compiler->expectedValues; int lastExpected = pstate->compiler->expectedValues;
@@ -1704,7 +1724,7 @@ static int expression(CParseState *pstate, int needed, bool forceNeeded)
forceNeeded); // anything above assignments are an expression forceNeeded); // anything above assignments are an expression
} }
static void statement(CParseState *pstate) static void expressionStatement(CParseState *pstate)
{ {
int savedPushed = pstate->compiler->pushedValues; int savedPushed = pstate->compiler->pushedValues;
@@ -1759,6 +1779,20 @@ static void statement(CParseState *pstate)
alignStack(pstate, savedPushed); alignStack(pstate, savedPushed);
} }
static void statement(CParseState *pstate)
{
expressionStatement(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) static CObjFunction *endCompiler(CParseState *pstate)
{ {
popLocals(pstate, pstate->compiler->scopeDepth + 1); // remove the locals from other scopes popLocals(pstate, pstate->compiler->scopeDepth + 1); // remove the locals from other scopes
@@ -1778,22 +1812,37 @@ CObjFunction *cosmoP_compileString(CState *state, const char *source, const char
{ {
CParseState parser; CParseState parser;
CCompilerState compiler; CCompilerState compiler;
cosmoM_freezeGC(state); // ignore all GC events while compiling
initParseState(&parser, &compiler, state, source, module); initParseState(&parser, &compiler, state, source, module);
advance(&parser); advance(&parser);
while (!match(&parser, TOKEN_EOF)) { while (!match(&parser, TOKEN_EOF)) {
statement(&parser); declaration(&parser);
} }
consume(&parser, TOKEN_EOF, "End of file expected!"); consume(&parser, TOKEN_EOF, "End of file expected!");
popLocals(&parser, 0); 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; CObjFunction *resFunc = compiler.function;
// finally free out parser states // finally free out parser states
endCompiler(&parser); endCompiler(&parser);
freeParseState(&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; return resFunc;
} }

View File

@@ -4,7 +4,8 @@
#include "clex.h" #include "clex.h"
#include "cosmo.h" #include "cosmo.h"
// compiles source into CChunk // compiles source into CChunk, if NULL is returned, a syntaxical error has occurred and pushed onto
// the stack
CObjFunction *cosmoP_compileString(CState *state, const char *source, const char *module); CObjFunction *cosmoP_compileString(CState *state, const char *source, const char *module);
#endif #endif

View File

@@ -7,45 +7,26 @@
#include <string.h> #include <string.h>
CPanic *cosmoV_newPanic(CState *state)
{
CPanic *panic = cosmoM_xmalloc(state, sizeof(CPanic));
panic->top = state->top;
panic->frameCount = state->frameCount;
panic->freezeGC = state->freezeGC;
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() CState *cosmoV_newState()
{ {
// we use C's malloc because we don't want to trigger a GC with an invalid state // we use C's malloc because we don't want to trigger a GC with an invalid state
CState *state = malloc(sizeof(CState)); CState *state = malloc(sizeof(CState));
if (state == NULL) { if (state == NULL) {
printf("[ERROR] failed to allocate memory!"); CERROR("failed to allocate memory!");
exit(1); exit(1);
} }
state->panic = false;
state->freezeGC = 1; // we start frozen state->freezeGC = 1; // we start frozen
state->panic = NULL;
// GC // GC
state->objects = NULL; state->objects = NULL;
state->userRoots = NULL;
state->grayStack.count = 0; state->grayStack.count = 0;
state->grayStack.capacity = 2; state->grayStack.capacity = 2;
state->grayStack.array = NULL; state->grayStack.array = NULL;
state->allocatedBytes = 0; state->allocatedBytes = sizeof(CState);
state->nextGC = 1024 * 8; // threshhold starts at 8kb state->nextGC = 1024 * 8; // threshhold starts at 8kb
// init stack // init stack
@@ -53,23 +34,21 @@ CState *cosmoV_newState()
state->frameCount = 0; state->frameCount = 0;
state->openUpvalues = NULL; state->openUpvalues = NULL;
// set default proto objects state->error = NULL;
for (int i = 0; i < COBJ_MAX; i++) {
state->protoObjects[i] = NULL;
}
for (int i = 0; i < ISTRING_MAX; i++) { // set default proto objects
for (int i = 0; i < COBJ_MAX; i++)
state->protoObjects[i] = NULL;
for (int i = 0; i < ISTRING_MAX; i++)
state->iStrings[i] = NULL; state->iStrings[i] = NULL;
}
cosmoT_initTable(state, &state->strings, 16); // init string table cosmoT_initTable(state, &state->strings, 16); // init string table
cosmoT_initTable(state, &state->registry, 16);
state->globals = cosmoO_newTable(state); // init global table state->globals = cosmoO_newTable(state); // init global table
// setup all strings used by the VM // setup all strings used by the VM
state->iStrings[ISTRING_INIT] = cosmoO_copyString(state, "__init", 6); state->iStrings[ISTRING_INIT] = cosmoO_copyString(state, "__init", 6);
state->iStrings[ISTRING_GC] = cosmoO_copyString(state, "__gc", 4);
state->iStrings[ISTRING_TOSTRING] = cosmoO_copyString(state, "__tostring", 10); state->iStrings[ISTRING_TOSTRING] = cosmoO_copyString(state, "__tostring", 10);
state->iStrings[ISTRING_TONUMBER] = cosmoO_copyString(state, "__tonumber", 10); state->iStrings[ISTRING_TONUMBER] = cosmoO_copyString(state, "__tonumber", 10);
state->iStrings[ISTRING_INDEX] = cosmoO_copyString(state, "__index", 7); state->iStrings[ISTRING_INDEX] = cosmoO_copyString(state, "__index", 7);
@@ -89,9 +68,8 @@ CState *cosmoV_newState()
state->iStrings[ISTRING_RESERVED] = cosmoO_copyString(state, "__reserved", 10); state->iStrings[ISTRING_RESERVED] = cosmoO_copyString(state, "__reserved", 10);
// set the IString flags // set the IString flags
for (int i = 0; i < ISTRING_MAX; i++) { for (int i = 0; i < ISTRING_MAX; i++)
state->iStrings[i]->isIString = true; state->iStrings[i]->isIString = true;
}
state->freezeGC = 0; // unfreeze the state state->freezeGC = 0; // unfreeze the state
return state; return state;
@@ -102,45 +80,38 @@ void cosmoV_freeState(CState *state)
#ifdef GC_DEBUG #ifdef GC_DEBUG
printf("state %p is being free'd!\n", state); printf("state %p is being free'd!\n", state);
#endif #endif
cosmoM_freezeGC(state);
// frees all the objects // frees all the objects
CObj *objs = state->objects; CObj *objs = state->objects;
while (objs != NULL) { while (objs != NULL) {
CObj *next = objs->next; CObj *next = objs->next;
#ifdef GC_DEBUG
printf("STATE FREEING %p\n", objs);
fflush(stdout);
#endif
cosmoO_free(state, objs); cosmoO_free(state, objs);
objs = next; objs = next;
} }
// mark our internal VM strings NULL // mark our internal VM strings NULL
for (int i = 0; i < ISTRING_MAX; i++) { for (int i = 0; i < ISTRING_MAX; i++)
state->iStrings[i] = NULL; state->iStrings[i] = NULL;
}
// free our string table (the string table includes the internal VM strings) // free our string table (the string table includes the internal VM strings)
cosmoT_clearTable(state, &state->strings); cosmoT_clearTable(state, &state->strings);
cosmoT_clearTable(state, &state->registry);
// free our gray stack & finally free the state structure // free our gray stack & finally free the state structure
cosmoM_freeArray(state, CObj *, state->grayStack.array, state->grayStack.capacity); cosmoM_freearray(state, CObj *, state->grayStack.array, state->grayStack.capacity);
#ifdef GC_DEBUG // TODO: yeah idk, it looks like im missing 520 bytes somewhere? i'll look into it later
if (state->allocatedBytes != 0) { /*#ifdef GC_DEBUG
printf("state->allocatedBytes doesn't match, got %lu\n", state->allocatedBytes); if (state->allocatedBytes != sizeof(CState)) {
printf("state->allocatedBytes doesn't match expected value (%lu), got %lu!",
sizeof(CState), state->allocatedBytes); exit(0);
} }
#endif #endif*/
free(state); free(state);
} }
// expects 2*pairs values on the stack, each pair should consist of 1 key and 1 value // expects 2*pairs values on the stack, each pair should consist of 1 key and 1 value
void cosmoV_addGlobals(CState *state, int pairs) void cosmoV_register(CState *state, int pairs)
{ {
for (int i = 0; i < pairs; i++) { for (int i = 0; i < pairs; i++) {
StkPtr key = cosmoV_getTop(state, 1); StkPtr key = cosmoV_getTop(state, 1);
@@ -153,57 +124,12 @@ void cosmoV_addGlobals(CState *state, int pairs)
} }
} }
// expects 2*pairs values on the stack, each pair should consist of 1 key and 1 value
void cosmoV_addRegistry(CState *state, int pairs)
{
for (int i = 0; i < pairs; i++) {
StkPtr key = cosmoV_getTop(state, 1);
StkPtr val = cosmoV_getTop(state, 0);
CValue *oldVal = cosmoT_insert(state, &state->registry, *key);
*oldVal = *val;
cosmoV_setTop(state, 2); // pops the 2 values off the stack
}
}
// expects 1 key on the stack, pushes result
void cosmoV_getRegistry(CState *state)
{
CValue key = *cosmoV_pop(state);
CValue val;
if (!cosmoT_get(state, &state->registry, key, &val)) {
cosmoV_error(state, "failed to grab %s from registry", cosmoV_typeStr(key));
}
cosmoV_pushValue(state, val);
}
void cosmoV_setProto(CState *state)
{
StkPtr objVal = cosmoV_getTop(state, 1);
StkPtr protoVal = cosmoV_getTop(state, 0);
if (!IS_REF(*objVal) || !IS_OBJECT(*protoVal)) {
cosmoV_error(state, "cannot set %s to proto of type %s", cosmoV_typeStr(*objVal),
cosmoV_typeStr(*protoVal));
}
// actually set the protos
CObj *obj = cosmoV_readRef(*objVal);
CObjObject *proto = cosmoV_readObject(*protoVal);
obj->proto = proto;
cosmoV_setTop(state, 2);
}
void cosmoV_printStack(CState *state) void cosmoV_printStack(CState *state)
{ {
printf("==== [[ stack dump ]] ====\n"); printf("==== [[ stack dump ]] ====\n");
for (CValue *top = state->top - 1; top >= state->stack; top--) { for (CValue *top = state->top - 1; top >= state->stack; top--) {
printf("%d: ", (int)(top - state->stack)); printf("%d: ", (int)(top - state->stack));
cosmoV_printValue(*top); printValue(*top);
printf("\n"); printf("\n");
} }
} }

View File

@@ -6,8 +6,6 @@
#include "ctable.h" #include "ctable.h"
#include "cvalue.h" #include "cvalue.h"
#include <setjmp.h>
struct CCallFrame struct CCallFrame
{ {
CObjClosure *closure; CObjClosure *closure;
@@ -18,7 +16,6 @@ struct CCallFrame
typedef enum IStringEnum typedef enum IStringEnum
{ {
ISTRING_INIT, // __init ISTRING_INIT, // __init
ISTRING_GC, // __gc
ISTRING_TOSTRING, // __tostring ISTRING_TOSTRING, // __tostring
ISTRING_TONUMBER, // __tonumber ISTRING_TONUMBER, // __tonumber
ISTRING_EQUAL, // __equals ISTRING_EQUAL, // __equals
@@ -41,56 +38,36 @@ typedef struct ArrayCObj
int capacity; int capacity;
} ArrayCObj; } ArrayCObj;
typedef struct CPanic
{
jmp_buf jmp;
StkPtr top;
struct CPanic *prev;
int frameCount;
int freezeGC;
} CPanic;
struct CState struct CState
{ {
CCallFrame callFrame[FRAME_MAX]; // call frames bool panic;
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;
CTable registry;
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
CPanic *panic;
size_t allocatedBytes;
size_t nextGC; // when allocatedBytes reaches this threshhold, trigger a GC event
int freezeGC; // when > 0, GC events will be ignored (for internal use) int freezeGC; // when > 0, GC events will be ignored (for internal use)
int frameCount; int frameCount;
CObjError *error; // NULL, unless panic is true
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
}; };
CPanic *cosmoV_newPanic(CState *state);
void cosmoV_freePanic(CState *state);
COSMO_API CState *cosmoV_newState(); COSMO_API CState *cosmoV_newState();
// expects 2*pairs values on the stack, each pair should consist of 1 key and 1 value
COSMO_API void cosmoV_register(CState *state, int pairs);
COSMO_API void cosmoV_freeState(CState *state); 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_addGlobals(CState *state, int pairs);
// expects 2*pairs values on the stack, each pair should consist of 1 key and 1 value
COSMO_API void cosmoV_addRegistry(CState *state, int pairs);
// expects 1 key on the stack, pushes result
COSMO_API void cosmoV_getRegistry(CState *state);
// expects <object>->proto = <object> (2 total) to be on the stack
COSMO_API void cosmoV_setProto(CState *state);
COSMO_API void cosmoV_printStack(CState *state); COSMO_API void cosmoV_printStack(CState *state);
#endif #endif

View File

@@ -11,7 +11,7 @@
#define MIN_TABLE_CAPACITY ARRAY_START #define MIN_TABLE_CAPACITY ARRAY_START
// bit-twiddling hacks, gets the next power of 2 // bit-twiddling hacks, gets the next power of 2
static unsigned int nextPow2(unsigned int x) unsigned int nextPow2(unsigned int x)
{ {
if (x <= ARRAY_START - 1) if (x <= ARRAY_START - 1)
return ARRAY_START; // sanity check return ARRAY_START; // sanity check
@@ -34,7 +34,6 @@ void cosmoT_initTable(CState *state, CTable *tbl, int startCap)
tbl->capacityMask = startCap - 1; tbl->capacityMask = startCap - 1;
tbl->count = 0; tbl->count = 0;
tbl->tombstones = 0; tbl->tombstones = 0;
tbl->tombThreshold = 32;
tbl->table = NULL; // to let out GC know we're initalizing tbl->table = NULL; // to let out GC know we're initalizing
tbl->table = cosmoM_xmalloc(state, sizeof(CTableEntry) * startCap); tbl->table = cosmoM_xmalloc(state, sizeof(CTableEntry) * startCap);
@@ -47,36 +46,33 @@ void cosmoT_initTable(CState *state, CTable *tbl, int startCap)
void cosmoT_addTable(CState *state, CTable *from, CTable *to) void cosmoT_addTable(CState *state, CTable *from, CTable *to)
{ {
CTableEntry *entry; int cap = from->capacityMask + 1;
int cap = cosmoT_getCapacity(from);
for (int i = 0; i < cap; i++) { for (int i = 0; i < cap; i++) {
entry = &from->table[i]; CTableEntry *entry = &from->table[i];
if (!(IS_NIL(entry->key))) { if (!(IS_NIL(entry->key))) {
*cosmoT_insert(state, to, entry->key) = entry->val; CValue *newVal = cosmoT_insert(state, to, entry->key);
*newVal = entry->val;
} }
} }
} }
void cosmoT_clearTable(CState *state, CTable *tbl) void cosmoT_clearTable(CState *state, CTable *tbl)
{ {
cosmoM_freeArray(state, CTableEntry, tbl->table, cosmoT_getCapacity(tbl)); cosmoM_freearray(state, CTableEntry, tbl->table, (tbl->capacityMask + 1));
} }
static uint32_t getObjectHash(CObj *obj) uint32_t getObjectHash(CObj *obj)
{ {
switch (obj->type) { switch (obj->type) {
case COBJ_STRING: case COBJ_STRING:
return ((CObjString *)obj)->hash; return ((CObjString *)obj)->hash;
case COBJ_CFUNCTION:
return (uint32_t)((CObjCFunction *)obj)->cfunc;
default: default:
return (uint32_t)obj; // just "hash" the pointer return (uint32_t)obj; // just "hash" the pointer
} }
} }
static uint32_t getValueHash(CValue *val) uint32_t getValueHash(CValue *val)
{ {
switch (GET_TYPE(*val)) { switch (GET_TYPE(*val)) {
case COSMO_TREF: case COSMO_TREF:
@@ -89,9 +85,8 @@ static uint32_t getValueHash(CValue *val)
return 0; return 0;
memcpy(buf, &num, sizeof(buf)); memcpy(buf, &num, sizeof(buf));
for (size_t i = 0; i < sizeof(cosmo_Number) / sizeof(uint32_t); i++) { for (size_t i = 0; i < sizeof(cosmo_Number) / sizeof(uint32_t); i++)
buf[0] += buf[i]; buf[0] += buf[i];
}
return buf[0]; return buf[0];
} }
// TODO: add support for other types // TODO: add support for other types
@@ -117,10 +112,10 @@ static CTableEntry *findEntry(CState *state, CTableEntry *entries, int mask, CVa
if (IS_NIL(entry->val)) { if (IS_NIL(entry->val)) {
// it's empty! if we found a tombstone, return that so it'll be reused // it's empty! if we found a tombstone, return that so it'll be reused
return tomb != NULL ? tomb : entry; return tomb != NULL ? tomb : entry;
} } else {
// its a tombstone! // its a tombstone!
tomb = entry; tomb = entry;
}
} else if (cosmoV_equal(state, entry->key, key)) { } else if (cosmoV_equal(state, entry->key, key)) {
return entry; return entry;
} }
@@ -145,7 +140,7 @@ static void resizeTbl(CState *state, CTable *tbl, int newCapacity, bool canShrin
return; return;
CTableEntry *entries = cosmoM_xmalloc(state, size); CTableEntry *entries = cosmoM_xmalloc(state, size);
oldCap = cosmoT_getCapacity(tbl); oldCap = tbl->capacityMask + 1;
newCount = 0; newCount = 0;
// set all nodes as NIL : NIL // set all nodes as NIL : NIL
@@ -168,7 +163,7 @@ static void resizeTbl(CState *state, CTable *tbl, int newCapacity, bool canShrin
} }
// free the old table // free the old table
cosmoM_freeArray(state, CTableEntry, tbl->table, oldCap); cosmoM_freearray(state, CTableEntry, tbl->table, oldCap);
tbl->table = entries; tbl->table = entries;
tbl->capacityMask = newCapacity - 1; tbl->capacityMask = newCapacity - 1;
@@ -180,10 +175,10 @@ bool cosmoT_checkShrink(CState *state, CTable *tbl)
{ {
// if count > 8 and active entries < tombstones // if count > 8 and active entries < tombstones
if (tbl->count > MIN_TABLE_CAPACITY && if (tbl->count > MIN_TABLE_CAPACITY &&
(tbl->count - tbl->tombstones < tbl->tombstones || tbl->tombstones > tbl->tombThreshold)) { (tbl->count - tbl->tombstones < tbl->tombstones ||
// shrink based on active entries to the next pow of 2 tbl->tombstones > 50)) { // TODO: 50 should be a threshhold
resizeTbl(state, tbl, nextPow2(tbl->count - tbl->tombstones) * GROW_FACTOR, false); resizeTbl(state, tbl, nextPow2(tbl->count - tbl->tombstones) * GROW_FACTOR,
tbl->tombThreshold = tbl->count / 4; false); // shrink based on active entries to the next pow of 2
return true; return true;
} }
@@ -194,7 +189,7 @@ bool cosmoT_checkShrink(CState *state, CTable *tbl)
COSMO_API CValue *cosmoT_insert(CState *state, CTable *tbl, CValue key) COSMO_API CValue *cosmoT_insert(CState *state, CTable *tbl, CValue key)
{ {
// make sure we have enough space allocated // make sure we have enough space allocated
int cap = cosmoT_getCapacity(tbl); int cap = tbl->capacityMask + 1;
if (tbl->count + 1 > (int)(cap * MAX_TABLE_FILL)) { if (tbl->count + 1 > (int)(cap * MAX_TABLE_FILL)) {
// grow table // grow table
int newCap = cap * GROW_FACTOR; int newCap = cap * GROW_FACTOR;
@@ -202,7 +197,8 @@ COSMO_API CValue *cosmoT_insert(CState *state, CTable *tbl, CValue key)
} }
// insert into the table // insert into the table
CTableEntry *entry = findEntry(state, tbl->table, tbl->capacityMask, key); CTableEntry *entry =
findEntry(state, tbl->table, tbl->capacityMask, key); // -1 for our capacity mask
if (IS_NIL(entry->key)) { if (IS_NIL(entry->key)) {
if (IS_NIL(entry->val)) // is it empty? if (IS_NIL(entry->val)) // is it empty?
@@ -258,10 +254,9 @@ CObjString *cosmoT_lookupString(CTable *tbl, const char *str, int length, uint32
{ {
if (tbl->count == 0) if (tbl->count == 0)
return 0; // sanity check return 0; // sanity check
uint32_t indx =
// since we know the capacity will *always* be a power of 2, we 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 // 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 // keep looking for an open slot in the entries array
while (true) { while (true) {
@@ -284,13 +279,13 @@ CObjString *cosmoT_lookupString(CTable *tbl, const char *str, int length, uint32
void cosmoT_printTable(CTable *tbl, const char *name) void cosmoT_printTable(CTable *tbl, const char *name)
{ {
printf("==== [[%s]] ====\n", name); printf("==== [[%s]] ====\n", name);
int cap = cosmoT_getCapacity(tbl); int cap = tbl->capacityMask + 1;
for (int i = 0; i < cap; i++) { for (int i = 0; i < cap; i++) {
CTableEntry *entry = &tbl->table[i]; CTableEntry *entry = &tbl->table[i];
if (!(IS_NIL(entry->key))) { if (!(IS_NIL(entry->key))) {
cosmoV_printValue(entry->key); printValue(entry->key);
printf(" - "); printf(" - ");
cosmoV_printValue(entry->val); printValue(entry->val);
printf("\n"); printf("\n");
} }
} }

View File

@@ -18,12 +18,9 @@ typedef struct CTable
int count; int count;
int capacityMask; // +1 to get the capacity int capacityMask; // +1 to get the capacity
int tombstones; int tombstones;
int tombThreshold;
CTableEntry *table; CTableEntry *table;
} CTable; } CTable;
#define cosmoT_getCapacity(tbl) ((tbl)->capacityMask + 1)
COSMO_API void cosmoT_initTable(CState *state, CTable *tbl, int startCap); COSMO_API void cosmoT_initTable(CState *state, CTable *tbl, int startCap);
COSMO_API void cosmoT_clearTable(CState *state, CTable *tbl); COSMO_API void cosmoT_clearTable(CState *state, CTable *tbl);
COSMO_API int cosmoT_count(CTable *tbl); COSMO_API int cosmoT_count(CTable *tbl);

View File

@@ -17,6 +17,7 @@ static bool readCValue(UndumpState *udstate, CValue *val);
#define check(e) \ #define check(e) \
if (!e) { \ if (!e) { \
printf("FAILED %d\n", __LINE__); \
return false; \ return false; \
} }
@@ -32,7 +33,6 @@ static void initUndumpState(CState *state, UndumpState *udstate, cosmo_Reader re
static bool readBlock(UndumpState *udstate, void *data, size_t size) static bool readBlock(UndumpState *udstate, void *data, size_t size)
{ {
if (udstate->readerStatus == 0) { if (udstate->readerStatus == 0) {
/* if reader returns 1, we expect an error was thrown */
udstate->readerStatus = udstate->reader(udstate->state, data, size, udstate->userData); udstate->readerStatus = udstate->reader(udstate->state, data, size, udstate->userData);
} }
@@ -74,7 +74,7 @@ static bool checkHeader(UndumpState *udstate)
uint8_t tmp; uint8_t tmp;
/* check header */ /* check header */
check(readBlock(udstate, magic, COSMO_MAGIC_LEN)); readBlock(udstate, magic, COSMO_MAGIC_LEN);
if (memcmp(magic, COSMO_MAGIC, COSMO_MAGIC_LEN) != 0) { if (memcmp(magic, COSMO_MAGIC, COSMO_MAGIC_LEN) != 0) {
cosmoV_error(udstate->state, "bad header!"); cosmoV_error(udstate->state, "bad header!");
return false; return false;
@@ -117,9 +117,6 @@ static bool readCObjFunction(UndumpState *udstate, CObjFunction **func)
*func = cosmoO_newFunction(udstate->state); *func = cosmoO_newFunction(udstate->state);
/* make sure our GC can see that we're currently using this function (and the values it uses) */
cosmoV_pushRef(udstate->state, (CObj *)*func);
check(readCObjString(udstate, &(*func)->name)); check(readCObjString(udstate, &(*func)->name));
check(readCObjString(udstate, &(*func)->module)); check(readCObjString(udstate, &(*func)->module));
@@ -140,8 +137,6 @@ static bool readCObjFunction(UndumpState *udstate, CObjFunction **func)
addConstant(udstate->state, &(*func)->chunk, val); addConstant(udstate->state, &(*func)->chunk, val);
} }
/* pop function off stack */
cosmoV_pop(udstate->state);
return true; return true;
} }
@@ -173,10 +168,10 @@ static bool readCObj(UndumpState *udstate, CObj **obj)
static bool readCValue(UndumpState *udstate, CValue *val) static bool readCValue(UndumpState *udstate, CValue *val)
{ {
uint8_t t; uint8_t type;
check(readu8(udstate, &t)); check(readu8(udstate, &type));
switch (t) { switch (type) {
case COSMO_TNUMBER: case COSMO_TNUMBER:
READ_VAR(udstate, val, cosmo_Number, cosmoV_newNumber) READ_VAR(udstate, val, cosmo_Number, cosmoV_newNumber)
case COSMO_TBOOLEAN: case COSMO_TBOOLEAN:
@@ -191,26 +186,24 @@ static bool readCValue(UndumpState *udstate, CValue *val)
*val = cosmoV_newNil(); *val = cosmoV_newNil();
break; break;
default: default:
cosmoV_error(udstate->state, "invalid value type: %d", t); break;
return false;
} }
return true; return true;
} }
#undef READ_VAR
#undef check
int cosmoD_undump(CState *state, cosmo_Reader reader, const void *userData, CObjFunction **func) int cosmoD_undump(CState *state, cosmo_Reader reader, const void *userData, CObjFunction **func)
{ {
UndumpState udstate; UndumpState udstate;
initUndumpState(state, &udstate, reader, userData); initUndumpState(state, &udstate, reader, userData);
if (!checkHeader(&udstate)) { if (!checkHeader(&udstate)) {
cosmoV_pushNil(state);
return 1; return 1;
} }
if (!readCObjFunction(&udstate, func)) { if (!readCObjFunction(&udstate, func)) {
cosmoV_pushNil(state);
return 1; return 1;
} }

View File

@@ -13,12 +13,12 @@ void initValArray(CState *state, CValueArray *val, size_t startCapacity)
void cleanValArray(CState *state, CValueArray *array) void cleanValArray(CState *state, CValueArray *array)
{ {
cosmoM_freeArray(state, CValue, array->values, array->capacity); cosmoM_freearray(state, CValue, array->values, array->capacity);
} }
void appendValArray(CState *state, CValueArray *array, CValue val) void appendValArray(CState *state, CValueArray *array, CValue val)
{ {
cosmoM_growArray(state, CValue, array->values, array->count, array->capacity); cosmoM_growarray(state, CValue, array->values, array->count, array->capacity);
array->values[array->count++] = val; array->values[array->count++] = val;
} }
@@ -101,7 +101,7 @@ const char *cosmoV_typeStr(CValue val)
} }
} }
void cosmoV_printValue(CValue val) void printValue(CValue val)
{ {
switch (GET_TYPE(val)) { switch (GET_TYPE(val)) {
case COSMO_TNUMBER: case COSMO_TNUMBER:

View File

@@ -119,7 +119,7 @@ void initValArray(CState *state, CValueArray *val, size_t startCapacity);
void cleanValArray(CState *state, CValueArray *array); // cleans array void cleanValArray(CState *state, CValueArray *array); // cleans array
void appendValArray(CState *state, CValueArray *array, CValue val); void appendValArray(CState *state, CValueArray *array, CValue val);
void cosmoV_printValue(CValue val); void printValue(CValue val);
COSMO_API bool cosmoV_equal(CState *state, CValue valA, CValue valB); COSMO_API bool cosmoV_equal(CState *state, CValue valA, CValue valB);
COSMO_API CObjString *cosmoV_toString(CState *state, CValue val); COSMO_API CObjString *cosmoV_toString(CState *state, CValue val);
COSMO_API cosmo_Number cosmoV_toNumber(CState *state, CValue val); COSMO_API cosmo_Number cosmoV_toNumber(CState *state, CValue val);

698
src/cvm.c

File diff suppressed because it is too large Load Diff

View File

@@ -8,35 +8,26 @@
// #define VM_DEBUG // #define VM_DEBUG
/* typedef enum
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 COSMOVM_OK,
your compiler supports computed gotos, you can define VM_JUMPTABLE COSMOVM_RUNTIME_ERR,
COSMOVM_BUILDTIME_ERR
although, this is disabled when VM_DEBUG is defined, since it can cause } COSMOVMRESULT;
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 // args = # of pass parameters, nresults = # of expected results
COSMO_API void cosmoV_call(CState *state, int args, int nresults); COSMO_API COSMOVMRESULT cosmoV_call(CState *state, int args, int nresults);
COSMO_API bool cosmoV_pcall(CState *state, int args, int nresults); COSMO_API COSMOVMRESULT cosmoV_pcall(CState *state, int args, int nresults);
// pushes new object onto the stack & returns a pointer to the new object // pushes new object onto the stack & returns a pointer to the new object
COSMO_API CObjObject *cosmoV_makeObject(CState *state, int pairs); COSMO_API CObjObject *cosmoV_makeObject(CState *state, int pairs);
COSMO_API void cosmoV_makeTable(CState *state, int pairs); COSMO_API void cosmoV_makeTable(CState *state, int pairs);
COSMO_API void cosmoV_concat(CState *state, int vals); COSMO_API void cosmoV_concat(CState *state, int vals);
COSMO_API void cosmoV_pushFString(CState *state, const char *format, ...); COSMO_API void cosmoV_pushFString(CState *state, const char *format, ...);
COSMO_API void cosmoV_printBacktrace(CState *state, CObjError *err); COSMO_API void cosmoV_printError(CState *state, CObjError *err);
COSMO_API void cosmoV_throw(CState *state); COSMO_API CObjError *cosmoV_throw(CState *state);
COSMO_API void cosmoV_error(CState *state, const char *format, ...); COSMO_API void cosmoV_error(CState *state, const char *format, ...);
COSMO_API void cosmoV_insert(CState *state, int indx, CValue val); COSMO_API void cosmo_insert(CState *state, int indx, CValue val);
/* /*
Sets the default proto objects for the passed objType. Also walks through the object heap and Sets the default proto objects for the passed objType. Also walks through the object heap and
@@ -67,21 +58,27 @@ COSMO_API bool cosmoV_compileString(CState *state, const char *src, const char *
COSMO_API bool cosmoV_undump(CState *state, cosmo_Reader reader, const void *ud); COSMO_API bool cosmoV_undump(CState *state, cosmo_Reader reader, const void *ud);
/* /*
expects object to be pushed, then the key. pops the key & object and pushes the value expects object to be pushed, then the key.
returns false if an error was thrown, returns true if the value was pushed onto the stack and
the object and key were popped
*/ */
COSMO_API void cosmoV_get(CState *state); COSMO_API bool cosmoV_get(CState *state);
/* /*
expects object to be pushed, then the key, and finally the new value. pops the object, key & expects object to be pushed, then the key, and finally the new value.
value
returns false if an error was thrown, returns true if the value was set and the object key, and
value were popped
*/ */
COSMO_API void cosmoV_set(CState *state); COSMO_API bool cosmoV_set(CState *state);
// wraps the closure into a CObjMethod, so the function is called as an invoked method // wraps the closure into a CObjMethod, so the function is called as an invoked method
COSMO_API void cosmoV_getMethod(CState *state, CObj *obj, CValue key, CValue *val); COSMO_API bool cosmoV_getMethod(CState *state, CObj *obj, CValue key, CValue *val);
// check if the value at the top of the stack is a <obj> user type // clears the stack, callstack and restores the state into a usable state after a calloverflow or
COSMO_API bool cosmoV_isValueUserType(CState *state, CValue val, int userType); // another hard to recover error (keeps the global table intact)
COSMO_API bool cosmoV_restore(CState *state);
// nice to have wrappers // nice to have wrappers
@@ -94,6 +91,13 @@ 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 // we reserve 8 slots for the error string and whatever c api we might be in
if (stackSize >= STACK_MAX - 8) { 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!"); cosmoV_error(state, "Stack overflow!");
return; return;
} }

View File

@@ -1,653 +0,0 @@
#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__) */

View File

@@ -1,379 +0,0 @@
/* this code is not standalone
* it is included into linenoise.c
* for windows.
* It is deliberately kept separate so that
* applications that have no need for windows
* support can omit this
*/
static DWORD orig_consolemode = 0;
static int flushOutput(struct current *current);
static void outputNewline(struct current *current);
static void refreshStart(struct current *current)
{
(void)current;
}
static void refreshEnd(struct current *current)
{
(void)current;
}
static void refreshStartChars(struct current *current)
{
assert(current->output == NULL);
/* We accumulate all output here */
current->output = sb_alloc();
#ifdef USE_UTF8
current->ubuflen = 0;
#endif
}
static void refreshNewline(struct current *current)
{
DRL("<nl>");
outputNewline(current);
}
static void refreshEndChars(struct current *current)
{
assert(current->output);
flushOutput(current);
sb_free(current->output);
current->output = NULL;
}
static int enableRawMode(struct current *current) {
DWORD n;
INPUT_RECORD irec;
current->outh = GetStdHandle(STD_OUTPUT_HANDLE);
current->inh = GetStdHandle(STD_INPUT_HANDLE);
if (!PeekConsoleInput(current->inh, &irec, 1, &n)) {
return -1;
}
if (getWindowSize(current) != 0) {
return -1;
}
if (GetConsoleMode(current->inh, &orig_consolemode)) {
SetConsoleMode(current->inh, ENABLE_PROCESSED_INPUT);
}
#ifdef USE_UTF8
/* XXX is this the right thing to do? */
SetConsoleCP(65001);
#endif
return 0;
}
static void disableRawMode(struct current *current)
{
SetConsoleMode(current->inh, orig_consolemode);
}
void linenoiseClearScreen(void)
{
/* XXX: This is ugly. Should just have the caller pass a handle */
struct current current;
current.outh = GetStdHandle(STD_OUTPUT_HANDLE);
if (getWindowSize(&current) == 0) {
COORD topleft = { 0, 0 };
DWORD n;
FillConsoleOutputCharacter(current.outh, ' ',
current.cols * current.rows, topleft, &n);
FillConsoleOutputAttribute(current.outh,
FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN,
current.cols * current.rows, topleft, &n);
SetConsoleCursorPosition(current.outh, topleft);
}
}
static void cursorToLeft(struct current *current)
{
COORD pos;
DWORD n;
pos.X = 0;
pos.Y = (SHORT)current->y;
FillConsoleOutputAttribute(current->outh,
FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN, current->cols, pos, &n);
current->x = 0;
}
#ifdef USE_UTF8
static void flush_ubuf(struct current *current)
{
COORD pos;
DWORD nwritten;
pos.Y = (SHORT)current->y;
pos.X = (SHORT)current->x;
SetConsoleCursorPosition(current->outh, pos);
WriteConsoleW(current->outh, current->ubuf, current->ubuflen, &nwritten, 0);
current->x += current->ubufcols;
current->ubuflen = 0;
current->ubufcols = 0;
}
static void add_ubuf(struct current *current, int ch)
{
/* This code originally by: Author: Mark E. Davis, 1994. */
static const int halfShift = 10; /* used for shifting by 10 bits */
static const DWORD halfBase = 0x0010000UL;
static const DWORD halfMask = 0x3FFUL;
#define UNI_SUR_HIGH_START 0xD800
#define UNI_SUR_HIGH_END 0xDBFF
#define UNI_SUR_LOW_START 0xDC00
#define UNI_SUR_LOW_END 0xDFFF
#define UNI_MAX_BMP 0x0000FFFF
if (ch > UNI_MAX_BMP) {
/* convert from unicode to utf16 surrogate pairs
* There is always space for one extra word in ubuf
*/
ch -= halfBase;
current->ubuf[current->ubuflen++] = (WORD)((ch >> halfShift) + UNI_SUR_HIGH_START);
current->ubuf[current->ubuflen++] = (WORD)((ch & halfMask) + UNI_SUR_LOW_START);
}
else {
current->ubuf[current->ubuflen++] = ch;
}
current->ubufcols += utf8_width(ch);
if (current->ubuflen >= UBUF_MAX_CHARS) {
flush_ubuf(current);
}
}
#endif
static int flushOutput(struct current *current)
{
const char *pt = sb_str(current->output);
int len = sb_len(current->output);
#ifdef USE_UTF8
/* convert utf8 in current->output into utf16 in current->ubuf
*/
while (len) {
int ch;
int n = utf8_tounicode(pt, &ch);
pt += n;
len -= n;
add_ubuf(current, ch);
}
flush_ubuf(current);
#else
DWORD nwritten;
COORD pos;
pos.Y = (SHORT)current->y;
pos.X = (SHORT)current->x;
SetConsoleCursorPosition(current->outh, pos);
WriteConsoleA(current->outh, pt, len, &nwritten, 0);
current->x += len;
#endif
sb_clear(current->output);
return 0;
}
static int outputChars(struct current *current, const char *buf, int len)
{
if (len < 0) {
len = strlen(buf);
}
assert(current->output);
sb_append_len(current->output, buf, len);
return 0;
}
static void outputNewline(struct current *current)
{
/* On the last row output a newline to force a scroll */
if (current->y + 1 == current->rows) {
outputChars(current, "\n", 1);
}
flushOutput(current);
current->x = 0;
current->y++;
}
static void setOutputHighlight(struct current *current, const int *props, int nprops)
{
int colour = FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN;
int bold = 0;
int reverse = 0;
int i;
for (i = 0; i < nprops; i++) {
switch (props[i]) {
case 0:
colour = FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN;
bold = 0;
reverse = 0;
break;
case 1:
bold = FOREGROUND_INTENSITY;
break;
case 7:
reverse = 1;
break;
case 30:
colour = 0;
break;
case 31:
colour = FOREGROUND_RED;
break;
case 32:
colour = FOREGROUND_GREEN;
break;
case 33:
colour = FOREGROUND_RED | FOREGROUND_GREEN;
break;
case 34:
colour = FOREGROUND_BLUE;
break;
case 35:
colour = FOREGROUND_RED | FOREGROUND_BLUE;
break;
case 36:
colour = FOREGROUND_BLUE | FOREGROUND_GREEN;
break;
case 37:
colour = FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN;
break;
}
}
flushOutput(current);
if (reverse) {
SetConsoleTextAttribute(current->outh, BACKGROUND_INTENSITY);
}
else {
SetConsoleTextAttribute(current->outh, colour | bold);
}
}
static void eraseEol(struct current *current)
{
COORD pos;
DWORD n;
pos.X = (SHORT) current->x;
pos.Y = (SHORT) current->y;
FillConsoleOutputCharacter(current->outh, ' ', current->cols - current->x, pos, &n);
}
static void setCursorXY(struct current *current)
{
COORD pos;
pos.X = (SHORT) current->x;
pos.Y = (SHORT) current->y;
SetConsoleCursorPosition(current->outh, pos);
}
static void setCursorPos(struct current *current, int x)
{
current->x = x;
setCursorXY(current);
}
static void cursorUp(struct current *current, int n)
{
current->y -= n;
setCursorXY(current);
}
static void cursorDown(struct current *current, int n)
{
current->y += n;
setCursorXY(current);
}
static int fd_read(struct current *current)
{
while (1) {
INPUT_RECORD irec;
DWORD n;
if (WaitForSingleObject(current->inh, INFINITE) != WAIT_OBJECT_0) {
break;
}
if (!ReadConsoleInputW(current->inh, &irec, 1, &n)) {
break;
}
if (irec.EventType == KEY_EVENT) {
KEY_EVENT_RECORD *k = &irec.Event.KeyEvent;
if (k->bKeyDown || k->wVirtualKeyCode == VK_MENU) {
if (k->dwControlKeyState & ENHANCED_KEY) {
switch (k->wVirtualKeyCode) {
case VK_LEFT:
return SPECIAL_LEFT;
case VK_RIGHT:
return SPECIAL_RIGHT;
case VK_UP:
return SPECIAL_UP;
case VK_DOWN:
return SPECIAL_DOWN;
case VK_INSERT:
return SPECIAL_INSERT;
case VK_DELETE:
return SPECIAL_DELETE;
case VK_HOME:
return SPECIAL_HOME;
case VK_END:
return SPECIAL_END;
case VK_PRIOR:
return SPECIAL_PAGE_UP;
case VK_NEXT:
return SPECIAL_PAGE_DOWN;
case VK_RETURN:
return k->uChar.UnicodeChar;
}
}
/* Note that control characters are already translated in AsciiChar */
else if (k->wVirtualKeyCode == VK_CONTROL)
continue;
else {
return k->uChar.UnicodeChar;
}
}
}
}
return -1;
}
static int getWindowSize(struct current *current)
{
CONSOLE_SCREEN_BUFFER_INFO info;
if (!GetConsoleScreenBufferInfo(current->outh, &info)) {
return -1;
}
current->cols = info.dwSize.X;
current->rows = info.dwSize.Y;
if (current->cols <= 0 || current->rows <= 0) {
current->cols = 80;
return -1;
}
current->y = info.dwCursorPosition.Y;
current->x = info.dwCursorPosition.X;
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,152 +0,0 @@
/* linenoise.h -- guerrilla line editing library against the idea that a
* line editing lib needs to be 20,000 lines of C code.
*
* See linenoise.c for more information.
*
* ------------------------------------------------------------------------
*
* Copyright (c) 2010, Salvatore Sanfilippo <antirez at gmail dot com>
* Copyright (c) 2010, Pieter Noordhuis <pcnoordhuis at gmail dot com>
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * 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 COPYRIGHT HOLDERS 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 COPYRIGHT
* HOLDER 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.
*/
#ifndef __LINENOISE_H
#define __LINENOISE_H
#ifdef __cplusplus
extern "C" {
#endif
#include <inttypes.h>
#include <string.h>
#ifndef NO_COMPLETION
typedef struct linenoiseCompletions {
size_t len;
char **cvec;
} linenoiseCompletions;
/*
* The callback type for tab completion handlers.
*/
typedef void(linenoiseCompletionCallback)(const char *prefix, linenoiseCompletions *comp, void *userdata);
/*
* Sets the current tab completion handler and returns the previous one, or NULL
* if no prior one has been set.
*/
linenoiseCompletionCallback * linenoiseSetCompletionCallback(linenoiseCompletionCallback *comp, void *userdata);
/*
* Adds a copy of the given string to the given completion list. The copy is owned
* by the linenoiseCompletions object.
*/
void linenoiseAddCompletion(linenoiseCompletions *comp, const char *str);
typedef char*(linenoiseHintsCallback)(const char *, int *color, int *bold, void *userdata);
typedef void(linenoiseFreeHintsCallback)(void *hint, void *userdata);
void linenoiseSetHintsCallback(linenoiseHintsCallback *callback, void *userdata);
void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *callback);
#endif
/*
* Prompts for input using the given string as the input
* prompt. Returns when the user has tapped ENTER or (on an empty
* line) EOF (Ctrl-D on Unix, Ctrl-Z on Windows). Returns either
* a copy of the entered string (for ENTER) or NULL (on EOF). The
* caller owns the returned string and must eventually free() it.
*/
char *linenoise(const char *prompt);
/**
* Like linenoise() but starts with an initial buffer.
*/
char *linenoiseWithInitial(const char *prompt, const char *initial);
/**
* Clear the screen.
*/
void linenoiseClearScreen(void);
/*
* Adds a copy of the given line of the command history.
*/
int linenoiseHistoryAdd(const char *line);
/*
* Sets the maximum length of the command history, in lines.
* If the history is currently longer, it will be trimmed,
* retaining only the most recent entries. If len is 0 or less
* then this function does nothing.
*/
int linenoiseHistorySetMaxLen(int len);
/*
* Returns the current maximum length of the history, in lines.
*/
int linenoiseHistoryGetMaxLen(void);
/*
* Saves the current contents of the history to the given file.
* Returns 0 on success.
*/
int linenoiseHistorySave(const char *filename);
/*
* Replaces the current history with the contents
* of the given file. Returns 0 on success.
*/
int linenoiseHistoryLoad(const char *filename);
/*
* Frees all history entries, clearing the history.
*/
void linenoiseHistoryFree(void);
/*
* Returns a pointer to the list of history entries, writing its
* length to *len if len is not NULL. The memory is owned by linenoise
* and must not be freed.
*/
char **linenoiseHistory(int *len);
/*
* Returns the number of display columns in the current terminal.
*/
int linenoiseColumns(void);
/**
* Enable or disable multiline mode (disabled by default)
*/
void linenoiseSetMultiLine(int enableml);
#ifdef __cplusplus
}
#endif
#endif /* __LINENOISE_H */