mirror of
https://github.com/CPunch/Cosmo.git
synced 2025-10-17 22:52:14 +00:00
Compare commits
66 Commits
main
...
5c3e24fc39
Author | SHA1 | Date | |
---|---|---|---|
5c3e24fc39 | |||
dcf6a09dae | |||
e0faa14b35 | |||
861607d6a8 | |||
10c9f92a06 | |||
8dfd7744c2 | |||
7f7bc7c6ad | |||
5169aca6d0 | |||
c34c5850e2 | |||
07ba4c731e | |||
788911130d | |||
c464a76849 | |||
8c6ba18848 | |||
93a09698a9 | |||
be49ec5af5 | |||
4fe437ea4e | |||
e854c5dbb3 | |||
c945c56482 | |||
89d443d767 | |||
54a959438b | |||
355842989b | |||
45f36e6e87 | |||
819e76b711 | |||
f116efa02c | |||
465f4d5e4a | |||
3efee51224 | |||
2836de090b | |||
8e278e3a7d | |||
c8cae03604 | |||
5d805e258b | |||
8df4cc65e3 | |||
7279623e24 | |||
517b0b9532 | |||
1df2e212cb | |||
![]() |
84ec5d2aee | ||
6859ec98ad | |||
057716e0d4 | |||
b9e9dedac6 | |||
1813bbeb1b | |||
471589d379 | |||
70f931df18 | |||
c13db54d7d | |||
da85d640ce | |||
6bc4ec6b04 | |||
f92ffcecbd | |||
66d77bc54b | |||
afac75753f | |||
92b2db9678 | |||
![]() |
b30616bb3c | ||
![]() |
9e6c6038f1 | ||
![]() |
1200e2d512 | ||
2050359d2f | |||
47051575cb | |||
7c7a2ed8d9 | |||
d1ea5c9703 | |||
7c6c075c2a | |||
14539057aa | |||
461e1d0c15 | |||
2e07715a7d | |||
bc43eaaa75 | |||
43a278e12d | |||
![]() |
44f1674b09 | ||
![]() |
27aedd2969 | ||
![]() |
7caa696aa2 | ||
![]() |
2e395065f8 | ||
![]() |
5b8dc30bb8 |
45
.github/workflows/check_build.yaml
vendored
45
.github/workflows/check_build.yaml
vendored
@@ -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
|
@@ -16,7 +16,7 @@ set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT c
|
||||
include(FetchContent)
|
||||
|
||||
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})
|
||||
|
||||
IF (NOT WIN32)
|
||||
|
4
Makefile
4
Makefile
@@ -1,7 +1,7 @@
|
||||
# make clean && make && ./bin/cosmo
|
||||
|
||||
CC=clang
|
||||
CFLAGS=-fPIE -Wall -Isrc -O3 #-g -fsanitize=address
|
||||
CFLAGS=-fPIE -Wall -Isrc -O3 -std=c99
|
||||
LDFLAGS=-lm #-fsanitize=address
|
||||
OUT=bin/cosmo
|
||||
|
||||
@@ -21,7 +21,6 @@ CHDR=\
|
||||
src/cbaselib.h\
|
||||
src/cdump.h\
|
||||
src/cundump.h\
|
||||
util/linenoise.h\
|
||||
|
||||
CSRC=\
|
||||
src/cchunk.c\
|
||||
@@ -38,7 +37,6 @@ CSRC=\
|
||||
src/cbaselib.c\
|
||||
src/cdump.c\
|
||||
src/cundump.c\
|
||||
util/linenoise.c\
|
||||
main.c\
|
||||
|
||||
COBJ=$(CSRC:.c=.o)
|
||||
|
10
README.md
10
README.md
@@ -1,4 +1,7 @@
|
||||
# Cosmo
|
||||
[](https://ci.appveyor.com/project/CPunch/Cosmo)
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
Usage: ./bin/cosmo [-clsr] [args]
|
||||
@@ -10,13 +13,6 @@ available options are:
|
||||
-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.
|
||||
|
33
appveyor.yml
Normal file
33
appveyor.yml
Normal 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 -s examples/getters_setters.cosmo
|
||||
|
||||
artifacts:
|
||||
- path: bin
|
||||
name: ubuntu20_04-bin-x64
|
||||
type: zip
|
@@ -59,14 +59,7 @@ Includes functions that interact with the operating system.
|
||||
|
||||
| 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.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'
|
||||
|
||||
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'
|
||||
> -> means 'returns'
|
@@ -1,6 +0,0 @@
|
||||
local err, file = os.open("LICENSE.md")
|
||||
if err then
|
||||
print("failed to open file")
|
||||
end
|
||||
|
||||
print(file:read("a"))
|
@@ -6,7 +6,7 @@ end
|
||||
// instance of test
|
||||
let obj = test()
|
||||
|
||||
test.__index = func(self, key)
|
||||
test.__index = function(self, key)
|
||||
print("__index called!")
|
||||
if (key == "lol") then
|
||||
return 9001
|
||||
|
@@ -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")
|
53
main.c
53
main.c
@@ -8,8 +8,6 @@
|
||||
#include "cundump.h"
|
||||
#include "cvm.h"
|
||||
|
||||
#include "util/linenoise.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#ifdef _WIN32
|
||||
@@ -47,36 +45,26 @@ int cosmoB_input(CState *state, int nargs, CValue *args)
|
||||
|
||||
static bool interpret(CState *state, const char *script, const char *mod)
|
||||
{
|
||||
bool ret;
|
||||
|
||||
// cosmoV_compileString pushes the result onto the stack (COBJ_ERROR or COBJ_CLOSURE)
|
||||
if (cosmoV_compileString(state, script, mod)) {
|
||||
cosmoG_disassemble(cosmoV_readClosure(*cosmoV_getTop(state, 0)));
|
||||
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 (IS_NIL(*cosmoV_getTop(state, 0))) {
|
||||
cosmoV_pop(state);
|
||||
return true;
|
||||
}
|
||||
|
||||
// otherwise, we print the result
|
||||
cosmoV_printValue(*cosmoV_getTop(state, 0));
|
||||
printf("\n");
|
||||
cosmoV_pop(state);
|
||||
// 0 args being passed, 0 results expected
|
||||
if (!cosmoV_call(state, 0, 0))
|
||||
cosmoV_printError(state, state->error);
|
||||
} else {
|
||||
cosmoV_printBacktrace(state, cosmoV_readError(*cosmoV_pop(state)));
|
||||
return false;
|
||||
cosmoV_pop(state); // pop the error off the stack
|
||||
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)
|
||||
{
|
||||
char *line;
|
||||
char line[1024];
|
||||
_ACTIVE = true;
|
||||
|
||||
// add our custom REPL functions
|
||||
@@ -86,16 +74,17 @@ static void repl(CState *state)
|
||||
cosmoV_pushString(state, "input");
|
||||
cosmoV_pushCFunction(state, cosmoB_input);
|
||||
|
||||
cosmoV_addGlobals(state, 2);
|
||||
cosmoV_register(state, 2);
|
||||
|
||||
while (_ACTIVE) {
|
||||
if (!(line = linenoise("> "))) { // better than gets()
|
||||
printf("> ");
|
||||
|
||||
if (!fgets(line, sizeof(line), stdin)) { // better than gets()
|
||||
printf("\n> ");
|
||||
break;
|
||||
}
|
||||
|
||||
linenoiseHistoryAdd(line);
|
||||
interpret(state, line, "REPL");
|
||||
free(line);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,7 +130,7 @@ static bool runFile(CState *state, const char *fileName)
|
||||
cosmoV_pushString(state, "input");
|
||||
cosmoV_pushCFunction(state, cosmoB_input);
|
||||
|
||||
cosmoV_addGlobals(state, 1);
|
||||
cosmoV_register(state, 1);
|
||||
|
||||
ret = interpret(state, script, fileName);
|
||||
|
||||
@@ -169,7 +158,8 @@ void compileScript(CState *state, const char *in, const char *out)
|
||||
CObjFunction *func = cosmoV_readClosure(*cosmoV_getTop(state, 0))->function;
|
||||
cosmoD_dump(state, func, fileWriter, (void *)fout);
|
||||
} else {
|
||||
cosmoV_printBacktrace(state, cosmoV_readError(*cosmoV_pop(state)));
|
||||
cosmoV_pop(state); // pop the error off the stack
|
||||
cosmoV_printError(state, state->error);
|
||||
}
|
||||
|
||||
free(script);
|
||||
@@ -182,13 +172,14 @@ 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)));
|
||||
cosmoV_pop(state); // pop the error off the stack
|
||||
cosmoV_printError(state, state->error);
|
||||
return;
|
||||
};
|
||||
|
||||
printf("[!] loaded %s!\n", in);
|
||||
if (!cosmoV_pcall(state, 0, 0))
|
||||
cosmoV_printBacktrace(state, cosmoV_readError(*cosmoV_pop(state)));
|
||||
if (!cosmoV_call(state, 0, 0))
|
||||
cosmoV_printError(state, state->error);
|
||||
|
||||
fclose(file);
|
||||
}
|
||||
|
358
src/cbaselib.c
358
src/cbaselib.c
@@ -18,7 +18,7 @@ int cosmoB_print(CState *state, int nargs, CValue *args)
|
||||
CObjString *str = cosmoV_toString(state, args[i]);
|
||||
printf("%s", cosmoO_readCString(str));
|
||||
} else { // else, thats pretty expensive for primitives, just print the raw value
|
||||
cosmoV_printValue(args[i]);
|
||||
printValue(args[i]);
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
@@ -30,6 +30,7 @@ int cosmoB_assert(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs < 1 || nargs > 2) {
|
||||
cosmoV_error(state, "assert() expected 1 or 2 arguments, got %d!", nargs);
|
||||
return 0; // nothing pushed onto the stack to return
|
||||
}
|
||||
|
||||
if (!IS_BOOLEAN(args[0]) || (nargs == 2 && !IS_STRING(args[1]))) {
|
||||
@@ -39,6 +40,7 @@ int cosmoB_assert(CState *state, int nargs, CValue *args)
|
||||
} else {
|
||||
cosmoV_typeError(state, "assert()", "<boolean>", "%s", cosmoV_typeStr(args[0]));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!cosmoV_readBoolean(args[0])) // expression passed was false, error!
|
||||
@@ -51,6 +53,7 @@ int cosmoB_type(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "type() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// push the type string to the stack
|
||||
@@ -62,6 +65,7 @@ int cosmoB_pcall(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs < 1) {
|
||||
cosmoV_error(state, "pcall() expected at least 1 argument!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// call the passed callable, the passed arguments are already in the
|
||||
@@ -69,7 +73,7 @@ int cosmoB_pcall(CState *state, int nargs, CValue *args)
|
||||
bool res = cosmoV_pcall(state, nargs - 1, 1);
|
||||
|
||||
// insert false before the result
|
||||
cosmoV_insert(state, 0, cosmoV_newBoolean(res));
|
||||
cosmo_insert(state, 0, cosmoV_newBoolean(res));
|
||||
return 2;
|
||||
}
|
||||
|
||||
@@ -77,6 +81,7 @@ int cosmoB_tonumber(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "tonumber() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
cosmoV_pushNumber(state, cosmoV_toNumber(state, args[0]));
|
||||
@@ -85,8 +90,10 @@ int cosmoB_tonumber(CState *state, int nargs, CValue *args)
|
||||
|
||||
int cosmoB_tostring(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1)
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "tostring() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
cosmoV_pushRef(state, (CObj *)cosmoV_toString(state, args[0]));
|
||||
return 1;
|
||||
@@ -96,16 +103,18 @@ int cosmoB_loadstring(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "loadstring() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!IS_STRING(args[0])) {
|
||||
cosmoV_typeError(state, "loadstring()", "<string>", "%s", cosmoV_typeStr(args[0]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
CObjString *str = cosmoV_readString(args[0]);
|
||||
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>
|
||||
}
|
||||
|
||||
@@ -113,10 +122,12 @@ int cosmoB_error(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "error() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!IS_STRING(args[0])) {
|
||||
cosmoV_typeError(state, "error()", "<string>", "%s", cosmoV_typeStr(args[0]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
cosmoV_error(state, "%s", cosmoV_readCString(args[0]));
|
||||
@@ -139,7 +150,7 @@ void cosmoB_loadLibrary(CState *state)
|
||||
}
|
||||
|
||||
// register all the pushed c functions and the strings as globals
|
||||
cosmoV_addGlobals(state, i);
|
||||
cosmoV_register(state, i);
|
||||
|
||||
// load other libraries
|
||||
cosmoB_loadObjLib(state);
|
||||
@@ -165,46 +176,13 @@ int cosmoB_osetProto(CState *state, int nargs, CValue *args)
|
||||
|
||||
int cosmoB_ogetProto(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1)
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "Expected 1 argument, got %d!", nargs);
|
||||
|
||||
if (!IS_REF(args[0])) {
|
||||
cosmoV_typeError(state, "__getter.__proto", "<object>", "%s", cosmoV_typeStr(args[0]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
CObj *proto = (CObj *)cosmoV_readRef(args[0])->proto;
|
||||
if (proto == NULL) {
|
||||
cosmoV_pushNil(state);
|
||||
return 1;
|
||||
}
|
||||
cosmoV_pushRef(state, (CObj *)cosmoV_readObject(args[0])->_obj.proto); // just return the proto
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
@@ -212,11 +190,13 @@ int cosmoB_oisChild(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 2) {
|
||||
cosmoV_error(state, "object.ischild() expected 2 arguments, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!IS_REF(args[0]) || !IS_OBJECT(args[1])) {
|
||||
cosmoV_typeError(state, "object.ischild()", "<reference obj>, <object>", "%s, %s",
|
||||
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
CObj *obj = cosmoV_readRef(args[0]);
|
||||
@@ -229,9 +209,9 @@ int cosmoB_oisChild(CState *state, int nargs, CValue *args)
|
||||
|
||||
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
|
||||
cosmoV_pushString(state, "object");
|
||||
@@ -239,7 +219,7 @@ COSMO_API void cosmoB_loadObjLib(CState *state)
|
||||
// make __getter object for debug proto
|
||||
cosmoV_pushString(state, "__getter");
|
||||
|
||||
// key & value pairs
|
||||
// key & value pair
|
||||
cosmoV_pushString(state, "__proto"); // key
|
||||
cosmoV_pushCFunction(state, cosmoB_ogetProto); // value
|
||||
|
||||
@@ -265,176 +245,54 @@ COSMO_API void cosmoB_loadObjLib(CState *state)
|
||||
cosmoV_registerProtoObject(state, COBJ_OBJECT, obj);
|
||||
|
||||
// register "object" to the global table
|
||||
cosmoV_addGlobals(state, 1);
|
||||
cosmoV_register(state, 1);
|
||||
}
|
||||
|
||||
// ================================================================ [OS.*]
|
||||
|
||||
int fileB_read(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)
|
||||
// os.read()
|
||||
int cosmoB_osRead(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
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);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!cosmoV_isValueUserType(state, args[0], COSMO_USER_FILE)) {
|
||||
cosmoV_typeError(state, "file:__gc()", "<file>", "%s", cosmoV_typeStr(args[0]));
|
||||
if (!IS_STRING(args[0])) {
|
||||
cosmoV_typeError(state, "os.read()", "<string>", "%s", cosmoV_typeStr(args[0]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
CObjObject *fileObj = cosmoV_readObject(args[0]);
|
||||
FILE *file = cosmoO_getUserP(fileObj);
|
||||
CObjString *str = cosmoV_readString(args[0]);
|
||||
|
||||
fclose(file);
|
||||
return 0;
|
||||
}
|
||||
// open file
|
||||
FILE *file = fopen(str->str, "rb");
|
||||
char *buf;
|
||||
size_t size, bRead;
|
||||
|
||||
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])) {
|
||||
cosmoV_typeError(state, "os.open()", "<string>", "%s", cosmoV_typeStr(args[0]));
|
||||
}
|
||||
|
||||
if (nargs == 2) {
|
||||
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]);
|
||||
} else if (nargs != 1) {
|
||||
cosmoV_error(state, "os.open() expected 1 or 2 arguments, got %d!", nargs);
|
||||
}
|
||||
} 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) {
|
||||
cosmoV_pushBoolean(state, true);
|
||||
cosmoV_pushFString(state, "Failed to open %s!", filePath);
|
||||
return 2;
|
||||
// return nil, file doesn't exist
|
||||
return 0;
|
||||
}
|
||||
|
||||
cosmoV_pushBoolean(state, false);
|
||||
pushFileObj(state, file);
|
||||
return 2;
|
||||
// grab the size of the file
|
||||
fseek(file, 0L, SEEK_END);
|
||||
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()
|
||||
@@ -443,6 +301,7 @@ int cosmoB_osTime(CState *state, int nargs, CValue *args)
|
||||
struct timeval time;
|
||||
if (nargs > 0) {
|
||||
cosmoV_error(state, "os.time() expected no arguments, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
gettimeofday(&time, NULL);
|
||||
@@ -455,10 +314,12 @@ int cosmoB_osSystem(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "os.system() expects 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!IS_STRING(args[0])) {
|
||||
cosmoV_typeError(state, "os.system()", "<string>", "%s", cosmoV_typeStr(args[0]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
// run the command and return the exit code
|
||||
@@ -468,8 +329,9 @@ int cosmoB_osSystem(CState *state, int nargs, CValue *args)
|
||||
|
||||
COSMO_API void cosmoB_loadOS(CState *state)
|
||||
{
|
||||
const char *identifiers[] = {"open", "time", "system"};
|
||||
CosmoCFunction osLib[] = {cosmoB_osOpen, cosmoB_osTime, cosmoB_osSystem};
|
||||
const char *identifiers[] = {"read", "time", "system"};
|
||||
|
||||
CosmoCFunction osLib[] = {cosmoB_osRead, cosmoB_osTime, cosmoB_osSystem};
|
||||
|
||||
cosmoV_pushString(state, "os");
|
||||
|
||||
@@ -480,30 +342,7 @@ COSMO_API void cosmoB_loadOS(CState *state)
|
||||
}
|
||||
|
||||
cosmoV_makeObject(state, i);
|
||||
cosmoV_addGlobals(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);
|
||||
cosmoV_register(state, 1); // register the os.* object to the global table
|
||||
}
|
||||
|
||||
// ================================================================ [STRING.*]
|
||||
@@ -515,6 +354,7 @@ int cosmoB_sSub(CState *state, int nargs, CValue *args)
|
||||
if (!IS_STRING(args[0]) || !IS_NUMBER(args[1])) {
|
||||
cosmoV_typeError(state, "string.sub()", "<string>, <number>", "%s, %s",
|
||||
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
CObjString *str = cosmoV_readString(args[0]);
|
||||
@@ -524,6 +364,7 @@ int cosmoB_sSub(CState *state, int nargs, CValue *args)
|
||||
if (indx < 0 || indx >= str->length) {
|
||||
cosmoV_error(state, "string.sub() expected index to be 0-%d, got %d!", str->length - 1,
|
||||
indx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
cosmoV_pushLString(state, str->str + ((int)indx), str->length - ((int)indx));
|
||||
@@ -532,6 +373,7 @@ int cosmoB_sSub(CState *state, int nargs, CValue *args)
|
||||
cosmoV_typeError(state, "string.sub()", "<string>, <number>, <number>", "%s, %s, %s",
|
||||
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]),
|
||||
cosmoV_typeStr(args[2]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
CObjString *str = cosmoV_readString(args[0]);
|
||||
@@ -543,11 +385,13 @@ int cosmoB_sSub(CState *state, int nargs, CValue *args)
|
||||
cosmoV_error(
|
||||
state, "string.sub() expected subbed string goes out of bounds, max length is %d!",
|
||||
str->length);
|
||||
return 0;
|
||||
}
|
||||
|
||||
cosmoV_pushLString(state, str->str + ((int)indx), ((int)length));
|
||||
} else {
|
||||
cosmoV_error(state, "string.sub() expected 2 or 3 arguments, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
@@ -560,6 +404,7 @@ int cosmoB_sFind(CState *state, int nargs, CValue *args)
|
||||
if (!IS_STRING(args[0]) || !IS_STRING(args[1])) {
|
||||
cosmoV_typeError(state, "string.find()", "<string>, <string>", "%s, %s",
|
||||
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
CObjString *str = cosmoV_readString(args[0]);
|
||||
@@ -580,6 +425,7 @@ int cosmoB_sFind(CState *state, int nargs, CValue *args)
|
||||
cosmoV_typeError(state, "string.find()", "<string>, <string>, <number>", "%s, %s, %s",
|
||||
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]),
|
||||
cosmoV_typeStr(args[2]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
CObjString *str = cosmoV_readString(args[0]);
|
||||
@@ -598,6 +444,7 @@ int cosmoB_sFind(CState *state, int nargs, CValue *args)
|
||||
cosmoV_pushNumber(state, (cosmo_Number)(indx - str->str));
|
||||
} else {
|
||||
cosmoV_error(state, "string.find() expected 2 or 3 arguments, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
@@ -608,11 +455,13 @@ int cosmoB_sSplit(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 2) {
|
||||
cosmoV_error(state, "string.split() expected 2 arguments, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!IS_STRING(args[0]) || !IS_STRING(args[1])) {
|
||||
cosmoV_typeError(state, "string.split()", "<string>, <string>", "%s, %s",
|
||||
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
CObjString *str = cosmoV_readString(args[0]);
|
||||
@@ -643,10 +492,12 @@ int cosmoB_sByte(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "string.byte() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!IS_STRING(args[0])) {
|
||||
cosmoV_typeError(state, "string.byte", "<string>", "%s", cosmoV_typeStr(args[0]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
CObjString *str = cosmoV_readString(args[0]);
|
||||
@@ -667,10 +518,12 @@ int cosmoB_sChar(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "string.char() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!IS_NUMBER(args[0])) {
|
||||
cosmoV_typeError(state, "string.char", "<number>", "%s", cosmoV_typeStr(args[0]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
// small side effect of truncating the number, but ignoring the decimal instead of throwing an
|
||||
@@ -680,6 +533,7 @@ int cosmoB_sChar(CState *state, int nargs, CValue *args)
|
||||
|
||||
if (num > 255 || num < 0) {
|
||||
cosmoV_error(state, "Character expected to be in range 0-255, got %d!", num);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// basically, treat the character value on the C stack as an """"array"""" with a length of 1
|
||||
@@ -691,10 +545,12 @@ int cosmoB_sLen(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs < 1) {
|
||||
cosmoV_error(state, "string.len() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!IS_STRING(args[0])) {
|
||||
cosmoV_typeError(state, "string.len", "<string>", "%s", cosmoV_typeStr(args[0]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
cosmoV_pushNumber(state, (cosmo_Number)strlen(cosmoV_readCString(args[0])));
|
||||
@@ -706,12 +562,14 @@ int cosmoB_sRep(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 2) {
|
||||
cosmoV_error(state, "string.rep() expected 2 arguments, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// expects <string>, <number>
|
||||
if (!IS_STRING(args[0]) || !IS_NUMBER(args[1])) {
|
||||
cosmoV_typeError(state, "string.rep", "<string>, <number>", "%s, %s",
|
||||
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
CObjString *str = cosmoV_readString(args[0]);
|
||||
@@ -727,9 +585,8 @@ int cosmoB_sRep(CState *state, int nargs, CValue *args)
|
||||
char *newStr = cosmoM_xmalloc(state, length + 1); // + 1 for the NULL terminator
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
// write the NULL terminator
|
||||
newStr[length] = '\0';
|
||||
@@ -760,7 +617,7 @@ void cosmoB_loadStrLib(CState *state)
|
||||
cosmoV_registerProtoObject(state, COBJ_STRING, obj);
|
||||
|
||||
// register "string" to the global table
|
||||
cosmoV_addGlobals(state, 1);
|
||||
cosmoV_register(state, 1);
|
||||
}
|
||||
|
||||
// ================================================================ [MATH]
|
||||
@@ -770,10 +627,12 @@ int cosmoB_mAbs(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "math.abs() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!IS_NUMBER(args[0])) {
|
||||
cosmoV_typeError(state, "math.abs", "<number>", "%s", cosmoV_typeStr(args[0]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
cosmoV_pushNumber(state, fabs(cosmoV_readNumber(args[0])));
|
||||
@@ -785,10 +644,12 @@ int cosmoB_mFloor(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "math.floor() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!IS_NUMBER(args[0])) {
|
||||
cosmoV_typeError(state, "math.floor", "<number>", "%s", cosmoV_typeStr(args[0]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
cosmoV_pushNumber(state, (int)cosmoV_readNumber(args[0]));
|
||||
@@ -800,10 +661,12 @@ int cosmoB_mCeil(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "math.ceil() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!IS_NUMBER(args[0])) {
|
||||
cosmoV_typeError(state, "math.ceil", "<number>", "%s", cosmoV_typeStr(args[0]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int roundedDown = (int)cosmoV_readNumber(args[0]);
|
||||
@@ -822,10 +685,12 @@ int cosmoB_mSin(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "math.sin() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!IS_NUMBER(args[0])) {
|
||||
cosmoV_typeError(state, "math.sin", "<number>", "%s", cosmoV_typeStr(args[0]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
cosmoV_pushNumber(state, sin(cosmoV_readNumber(args[0])));
|
||||
@@ -836,10 +701,12 @@ int cosmoB_mCos(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "math.cos() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!IS_NUMBER(args[0])) {
|
||||
cosmoV_typeError(state, "math.cos", "<number>", "%s", cosmoV_typeStr(args[0]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
cosmoV_pushNumber(state, cos(cosmoV_readNumber(args[0])));
|
||||
@@ -850,10 +717,12 @@ int cosmoB_mTan(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "math.tan() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!IS_NUMBER(args[0])) {
|
||||
cosmoV_typeError(state, "math.tan", "<number>", "%s", cosmoV_typeStr(args[0]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
cosmoV_pushNumber(state, tan(cosmoV_readNumber(args[0])));
|
||||
@@ -864,10 +733,12 @@ int cosmoB_mASin(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "math.asin() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!IS_NUMBER(args[0])) {
|
||||
cosmoV_typeError(state, "math.asin", "<number>", "%s", cosmoV_typeStr(args[0]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
cosmoV_pushNumber(state, asin(cosmoV_readNumber(args[0])));
|
||||
@@ -878,10 +749,12 @@ int cosmoB_mACos(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "math.acos() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!IS_NUMBER(args[0])) {
|
||||
cosmoV_typeError(state, "math.acos", "<number>", "%s", cosmoV_typeStr(args[0]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
cosmoV_pushNumber(state, acos(cosmoV_readNumber(args[0])));
|
||||
@@ -892,10 +765,12 @@ int cosmoB_mATan(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "math.atan() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!IS_NUMBER(args[0])) {
|
||||
cosmoV_typeError(state, "math.atan", "<number>", "%s", cosmoV_typeStr(args[0]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
cosmoV_pushNumber(state, atan(cosmoV_readNumber(args[0])));
|
||||
@@ -906,10 +781,12 @@ int cosmoB_mRad(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "math.rad() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!IS_NUMBER(args[0])) {
|
||||
cosmoV_typeError(state, "math.rad", "<number>", "%s", cosmoV_typeStr(args[0]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
// convert the degree to radians
|
||||
@@ -921,10 +798,12 @@ int cosmoB_mDeg(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "math.deg() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!IS_NUMBER(args[0])) {
|
||||
cosmoV_typeError(state, "math.deg", "<number>", "%s", cosmoV_typeStr(args[0]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
// convert the degree to radians
|
||||
@@ -955,7 +834,7 @@ void cosmoB_loadMathLib(CState *state)
|
||||
|
||||
// make the object and register it as a global to the state
|
||||
cosmoV_makeObject(state, i);
|
||||
cosmoV_addGlobals(state, 1);
|
||||
cosmoV_register(state, 1);
|
||||
}
|
||||
|
||||
// ================================================================ [VM.*]
|
||||
@@ -973,11 +852,13 @@ int cosmoB_vsetGlobal(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 2) {
|
||||
cosmoV_error(state, "Expected 2 argumenst, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!IS_TABLE(args[1])) {
|
||||
cosmoV_typeError(state, "vm.__setter[\"globals\"]", "<object>, <table>", "%s, %s",
|
||||
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
// this makes me very nervous ngl
|
||||
@@ -993,11 +874,13 @@ int cosmoB_vdisassemble(CState *state, int nargs, CValue *args)
|
||||
|
||||
if (nargs != 1) {
|
||||
cosmoV_error(state, "Expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// get the closure
|
||||
if (!IS_CLOSURE(args[0])) {
|
||||
cosmoV_typeError(state, "vm.disassemble", "<closure>", "%s", cosmoV_typeStr(args[0]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
closure = cosmoV_readClosure(args[0]);
|
||||
@@ -1012,11 +895,13 @@ int cosmoB_vindexBProto(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 2) {
|
||||
cosmoV_error(state, "Expected 2 arguments, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!IS_NUMBER(args[1])) {
|
||||
cosmoV_typeError(state, "baseProtos.__index", "<object>, <number>", "%s, %s",
|
||||
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int indx = (int)cosmoV_readNumber(args[1]);
|
||||
@@ -1038,12 +923,14 @@ int cosmoB_vnewindexBProto(CState *state, int nargs, CValue *args)
|
||||
{
|
||||
if (nargs != 3) {
|
||||
cosmoV_error(state, "Expected 3 arguments, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!IS_NUMBER(args[1]) || !IS_OBJECT(args[2])) {
|
||||
cosmoV_typeError(state, "baseProtos.__newindex", "<object>, <number>, <object>",
|
||||
"%s, %s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]),
|
||||
cosmoV_typeStr(args[2]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int indx = (int)cosmoV_readNumber(args[1]);
|
||||
@@ -1051,6 +938,7 @@ int cosmoB_vnewindexBProto(CState *state, int nargs, CValue *args)
|
||||
|
||||
if (indx >= COBJ_MAX || indx < 0) {
|
||||
cosmoV_error(state, "index out of range! expected 0 - %d, got %d!", COBJ_MAX, indx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
cosmoV_registerProtoObject(state, indx, proto);
|
||||
@@ -1060,8 +948,16 @@ int cosmoB_vnewindexBProto(CState *state, int nargs, CValue *args)
|
||||
// vm.collect()
|
||||
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);
|
||||
|
||||
// and re-freeze the state
|
||||
cosmoM_freezeGC(state);
|
||||
|
||||
// the end!
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1106,5 +1002,5 @@ void cosmoB_loadVM(CState *state)
|
||||
cosmoV_makeObject(state, 5); // makes the vm object
|
||||
|
||||
// register "vm" to the global table
|
||||
cosmoV_addGlobals(state, 1);
|
||||
cosmoV_register(state, 1);
|
||||
}
|
||||
|
@@ -3,13 +3,6 @@
|
||||
|
||||
#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:
|
||||
- base library ("print", "assert", "type", "pcall", "loadstring", etc.)
|
||||
- object library
|
||||
|
@@ -27,9 +27,9 @@ void initChunk(CState *state, CChunk *chunk, size_t startCapacity)
|
||||
void cleanChunk(CState *state, CChunk *chunk)
|
||||
{
|
||||
// first, free the chunk buffer
|
||||
cosmoM_freeArray(state, INSTRUCTION, chunk->buf, chunk->capacity);
|
||||
cosmoM_freearray(state, INSTRUCTION, chunk->buf, chunk->capacity);
|
||||
// then the line info
|
||||
cosmoM_freeArray(state, int, chunk->lineInfo, chunk->capacity);
|
||||
cosmoM_freearray(state, int, chunk->lineInfo, chunk->capacity);
|
||||
// free the constants
|
||||
cleanValArray(state, &chunk->constants);
|
||||
}
|
||||
@@ -61,8 +61,8 @@ int addConstant(CState *state, CChunk *chunk, CValue value)
|
||||
void writeu8Chunk(CState *state, CChunk *chunk, INSTRUCTION i, int line)
|
||||
{
|
||||
// does the buffer need to be reallocated?
|
||||
cosmoM_growArray(state, INSTRUCTION, chunk->buf, chunk->count, chunk->capacity);
|
||||
cosmoM_growArray(state, int, chunk->lineInfo, chunk->count, chunk->lineCapacity);
|
||||
cosmoM_growarray(state, INSTRUCTION, chunk->buf, chunk->count, chunk->capacity);
|
||||
cosmoM_growarray(state, int, chunk->lineInfo, chunk->count, chunk->lineCapacity);
|
||||
|
||||
// write data to the chunk :)
|
||||
chunk->lineInfo[chunk->count] = line;
|
||||
|
12
src/cdebug.c
12
src/cdebug.c
@@ -5,9 +5,8 @@
|
||||
|
||||
void printIndent(int indent)
|
||||
{
|
||||
for (int i = 0; i < indent; i++) {
|
||||
for (int i = 0; i < indent; i++)
|
||||
printf("\t");
|
||||
}
|
||||
}
|
||||
|
||||
static int simpleInstruction(const char *name, int offset)
|
||||
@@ -62,7 +61,7 @@ static int constInstruction(const char *name, CChunk *chunk, int offset)
|
||||
printf("%-16s [%05d] - ", name, index);
|
||||
CValue val = chunk->constants.values[index];
|
||||
|
||||
cosmoV_printValue(val);
|
||||
printValue(val);
|
||||
|
||||
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);
|
||||
offset += 3; // we consumed the opcode + u16
|
||||
|
||||
cosmoV_printValue(val);
|
||||
printValue(val);
|
||||
printf("\n");
|
||||
|
||||
// list the upvalues/locals that are captured
|
||||
@@ -223,8 +222,3 @@ int disasmInstr(CChunk *chunk, int offset, int indent)
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void cosmoG_disassemble(CObjClosure *closure)
|
||||
{
|
||||
disasmChunk(&closure->function->chunk, closure->function->name == NULL ? UNNAMEDCHUNK : closure->function->name->str, 0);
|
||||
}
|
@@ -2,13 +2,10 @@
|
||||
#define CDEBUG_H
|
||||
|
||||
#include "cchunk.h"
|
||||
#include "cobj.h"
|
||||
|
||||
COSMO_API void disasmChunk(CChunk *chunk, const char *name, int indent);
|
||||
COSMO_API int disasmInstr(CChunk *chunk, int offset, int indent);
|
||||
|
||||
void printIndent(int indent);
|
||||
|
||||
COSMO_API void cosmoG_disassemble(CObjClosure *closure);
|
||||
|
||||
#endif
|
||||
|
13
src/clex.c
13
src/clex.c
@@ -54,7 +54,7 @@ static void resetBuffer(CLexState *state)
|
||||
// cancels the token heap buffer and frees it
|
||||
static void freeBuffer(CLexState *state)
|
||||
{
|
||||
cosmoM_freeArray(state->cstate, char, state->buffer, state->bufCap);
|
||||
cosmoM_freearray(state->cstate, char, state->buffer, state->bufCap);
|
||||
|
||||
resetBuffer(state);
|
||||
}
|
||||
@@ -62,7 +62,7 @@ static void freeBuffer(CLexState *state)
|
||||
// adds character to buffer
|
||||
static void appendBuffer(CLexState *state, char c)
|
||||
{
|
||||
cosmoM_growArray(state->cstate, char, state->buffer, state->bufCount, state->bufCap);
|
||||
cosmoM_growarray(state->cstate, char, state->buffer, state->bufCount, state->bufCap);
|
||||
|
||||
state->buffer[state->bufCount++] = c;
|
||||
}
|
||||
@@ -389,8 +389,9 @@ static CToken parseIdentifier(CLexState *state)
|
||||
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->currentChar = (char *)source;
|
||||
state->line = 1;
|
||||
@@ -399,11 +400,13 @@ void cosmoL_initLexState(CState *cstate, CLexState *state, const char *source)
|
||||
state->cstate = cstate;
|
||||
|
||||
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)
|
||||
|
@@ -103,8 +103,8 @@ typedef struct
|
||||
CState *cstate;
|
||||
} CLexState;
|
||||
|
||||
void cosmoL_initLexState(CState *cstate, CLexState *state, const char *source);
|
||||
void cosmoL_cleanupLexState(CState *state, CLexState *lstate);
|
||||
CLexState *cosmoL_newLexState(CState *state, const char *source);
|
||||
void cosmoL_freeLexState(CState *state, CLexState *lstate);
|
||||
|
||||
CToken cosmoL_scanToken(CLexState *state);
|
||||
|
||||
|
130
src/cmem.c
130
src/cmem.c
@@ -6,29 +6,11 @@
|
||||
#include "cstate.h"
|
||||
#include "ctable.h"
|
||||
#include "cvalue.h"
|
||||
#include "cvm.h"
|
||||
|
||||
// realloc wrapper
|
||||
void *cosmoM_reallocate(CState *state, void *buf, size_t oldSize, size_t newSize)
|
||||
{
|
||||
if (buf == NULL)
|
||||
oldSize = 0;
|
||||
|
||||
#ifdef GC_DEBUG
|
||||
printf("old allocated bytes: %ld\n", state->allocatedBytes);
|
||||
if (buf) {
|
||||
if (newSize == 0) {
|
||||
printf("freeing %p, reclaiming %ld bytes...\n", buf, oldSize);
|
||||
} else {
|
||||
printf("realloc %p, byte difference: %ld\n", buf, newSize - oldSize);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
state->allocatedBytes += newSize - oldSize;
|
||||
#ifdef GC_DEBUG
|
||||
printf("new allocated bytes: %ld\n", state->allocatedBytes);
|
||||
fflush(stdout);
|
||||
#endif
|
||||
|
||||
if (newSize == 0) { // it needs to be freed
|
||||
free(buf);
|
||||
@@ -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()
|
||||
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) {
|
||||
printf("[ERROR] failed to allocate memory!");
|
||||
CERROR("failed to allocate memory!");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@@ -82,7 +59,7 @@ static void markTable(CState *state, CTable *tbl)
|
||||
if (tbl->table == NULL) // table is still being initialized
|
||||
return;
|
||||
|
||||
int cap = cosmoT_getCapacity(tbl);
|
||||
int cap = tbl->capacityMask + 1;
|
||||
for (int i = 0; i < cap; i++) {
|
||||
CTableEntry *entry = &tbl->table[i];
|
||||
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)
|
||||
{
|
||||
if (tbl->table == NULL) // table is still being initialized
|
||||
return;
|
||||
|
||||
int cap = cosmoT_getCapacity(tbl);
|
||||
|
||||
#ifdef GC_DEBUG
|
||||
printf("tableRemoveWhite: %p, cap: %d\n", tbl, cap);
|
||||
#endif
|
||||
|
||||
int cap = tbl->capacityMask + 1;
|
||||
for (int i = 0; i < cap; i++) {
|
||||
CTableEntry *entry = &tbl->table[i];
|
||||
if (IS_REF(entry->key) &&
|
||||
@@ -165,9 +137,8 @@ static void blackenObject(CState *state, CObj *obj)
|
||||
markValue(state, err->err);
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -208,7 +179,7 @@ static void markObject(CState *state, CObj *obj)
|
||||
return;
|
||||
|
||||
// we can use cosmoM_growarray because we lock the GC when we entered in cosmoM_collectGarbage
|
||||
cosmoM_growArray(state, CObj *, state->grayStack.array, state->grayStack.count,
|
||||
cosmoM_growarray(state, CObj *, state->grayStack.array, state->grayStack.count,
|
||||
state->grayStack.capacity);
|
||||
|
||||
state->grayStack.array[state->grayStack.count++] = obj;
|
||||
@@ -231,11 +202,11 @@ static void traceGrays(CState *state)
|
||||
|
||||
static void sweep(CState *state)
|
||||
{
|
||||
CObj *prev = NULL, *object = state->objects;
|
||||
|
||||
CObj *prev = NULL;
|
||||
CObj *object = state->objects;
|
||||
while (object != NULL) {
|
||||
if (object->isMarked) { // skip over it
|
||||
object->isMarked = false; // reset to white
|
||||
object->isMarked = false; // rest to white
|
||||
prev = object;
|
||||
object = object->next;
|
||||
} else { // free it!
|
||||
@@ -248,22 +219,22 @@ static void sweep(CState *state)
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void markUserRoots(CState *state)
|
||||
{
|
||||
CObj *root = state->userRoots;
|
||||
|
||||
// traverse userRoots and mark all the object
|
||||
while (root != NULL) {
|
||||
markObject(state, root);
|
||||
root = root->nextRoot;
|
||||
}
|
||||
}
|
||||
|
||||
static void markRoots(CState *state)
|
||||
{
|
||||
// mark all values on the stack
|
||||
@@ -284,26 +255,29 @@ static void markRoots(CState *state)
|
||||
markObject(state, (CObj *)state->globals);
|
||||
|
||||
// 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]);
|
||||
}
|
||||
|
||||
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]);
|
||||
}
|
||||
|
||||
traceGrays(state);
|
||||
}
|
||||
|
||||
COSMO_API void cosmoM_collectGarbage(CState *state)
|
||||
{
|
||||
cosmoM_freezeGC(state);
|
||||
#ifdef GC_DEBUG
|
||||
printf("-- GC start\n");
|
||||
size_t start = state->allocatedBytes;
|
||||
#endif
|
||||
cosmoM_freezeGC(state); // we don't want a recursive garbage collection event!
|
||||
|
||||
markRoots(state);
|
||||
|
||||
tableRemoveWhite(
|
||||
@@ -314,15 +288,59 @@ COSMO_API void cosmoM_collectGarbage(CState *state)
|
||||
|
||||
// set our next GC event
|
||||
cosmoM_updateThreshhold(state);
|
||||
|
||||
state->freezeGC--; // we don't want to use cosmoM_unfreezeGC because that might trigger a GC
|
||||
// event (if GC_STRESS is defined)
|
||||
#ifdef GC_DEBUG
|
||||
printf("-- GC end, reclaimed %ld bytes (started at %ld, ended at %ld), next garbage collection "
|
||||
"scheduled at %ld bytes\n",
|
||||
start - state->allocatedBytes, start, state->allocatedBytes, state->nextGC);
|
||||
getchar(); // pauses execution
|
||||
#endif
|
||||
cosmoM_unfreezeGC(state);
|
||||
}
|
||||
|
||||
COSMO_API void cosmoM_updateThreshhold(CState *state)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
16
src/cmem.h
16
src/cmem.h
@@ -12,16 +12,16 @@
|
||||
#define ARRAY_START 8
|
||||
|
||||
#ifdef GC_DEBUG
|
||||
# define cosmoM_freeArray(state, type, buf, capacity) \
|
||||
# define cosmoM_freearray(state, type, buf, capacity) \
|
||||
printf("freeing array %p [size %lu] at %s:%d\n", buf, sizeof(type) * capacity, __FILE__, \
|
||||
__LINE__); \
|
||||
cosmoM_reallocate(state, buf, sizeof(type) * capacity, 0)
|
||||
#else
|
||||
# define cosmoM_freeArray(state, type, buf, capacity) \
|
||||
# define cosmoM_freearray(state, type, buf, capacity) \
|
||||
cosmoM_reallocate(state, buf, sizeof(type) * capacity, 0)
|
||||
#endif
|
||||
|
||||
#define cosmoM_growArray(state, type, buf, count, capacity) \
|
||||
#define cosmoM_growarray(state, type, buf, count, capacity) \
|
||||
if (count >= capacity || buf == NULL) { \
|
||||
int old = capacity; \
|
||||
capacity = old * GROW_FACTOR; \
|
||||
@@ -38,7 +38,6 @@
|
||||
|
||||
#define cosmoM_isFrozen(state) (state->freezeGC > 0)
|
||||
|
||||
// cosmoM_freezeGC should only be used in the garbage collector !
|
||||
// if debugging, print the locations of when the state is frozen/unfrozen
|
||||
#ifdef GC_DEBUG
|
||||
# define cosmoM_freezeGC(state) \
|
||||
@@ -67,7 +66,14 @@ COSMO_API bool cosmoM_checkGarbage(CState *state,
|
||||
COSMO_API void cosmoM_collectGarbage(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
|
||||
// NOTE: prefer to use the stack when possible
|
||||
COSMO_API void cosmoM_addRoot(CState *state, CObj *newRoot);
|
||||
|
||||
// lets the VM know this root is no longer held in a reference and is able to be freed
|
||||
COSMO_API void cosmoM_removeRoot(CState *state, CObj *oldRoot);
|
||||
|
||||
// wrapper for cosmoM_reallocate so we can track our memory usage (it's also safer :P)
|
||||
static inline void *cosmoM_xmalloc(CState *state, size_t sz)
|
||||
{
|
||||
return cosmoM_reallocate(state, NULL, 0, sz);
|
||||
|
130
src/cobj.c
130
src/cobj.c
@@ -15,9 +15,8 @@ uint32_t hashString(const char *str, size_t sz)
|
||||
uint32_t hash = sz;
|
||||
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];
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
@@ -32,8 +31,9 @@ CObj *cosmoO_allocateBase(CState *state, size_t sz, CObjType type)
|
||||
obj->next = state->objects;
|
||||
state->objects = obj;
|
||||
|
||||
obj->nextRoot = NULL;
|
||||
#ifdef GC_DEBUG
|
||||
printf("allocated %s %p\n", cosmoO_typeStr(obj), obj);
|
||||
printf("allocated %p with OBJ_TYPE %d\n", obj, type);
|
||||
#endif
|
||||
return obj;
|
||||
}
|
||||
@@ -41,19 +41,21 @@ CObj *cosmoO_allocateBase(CState *state, size_t sz, CObjType type)
|
||||
void cosmoO_free(CState *state, CObj *obj)
|
||||
{
|
||||
#ifdef GC_DEBUG
|
||||
printf("freeing %s %p\n", cosmoO_typeStr(obj), obj);
|
||||
printf("freeing %p [", obj);
|
||||
printObject(obj);
|
||||
printf("]\n");
|
||||
#endif
|
||||
switch (obj->type) {
|
||||
case COBJ_STRING: {
|
||||
CObjString *objStr = (CObjString *)obj;
|
||||
cosmoM_freeArray(state, char, objStr->str, objStr->length + 1);
|
||||
cosmoM_freearray(state, char, objStr->str, objStr->length + 1);
|
||||
cosmoM_free(state, CObjString, objStr);
|
||||
break;
|
||||
}
|
||||
case COBJ_OBJECT: {
|
||||
CObjObject *objObj = (CObjObject *)obj;
|
||||
cosmoT_clearTable(state, &objObj->tbl);
|
||||
cosmoM_free(state, CObjObject, objObj);
|
||||
CObjObject *objTbl = (CObjObject *)obj;
|
||||
cosmoT_clearTable(state, &objTbl->tbl);
|
||||
cosmoM_free(state, CObjObject, objTbl);
|
||||
break;
|
||||
}
|
||||
case COBJ_TABLE: {
|
||||
@@ -82,13 +84,13 @@ void cosmoO_free(CState *state, CObj *obj)
|
||||
}
|
||||
case COBJ_ERROR: {
|
||||
CObjError *err = (CObjError *)obj;
|
||||
cosmoM_freeArray(state, CCallFrame, err->frames, err->frameCount);
|
||||
cosmoM_freearray(state, CCallFrame, err->frames, err->frameCount);
|
||||
cosmoM_free(state, CObjError, obj);
|
||||
break;
|
||||
}
|
||||
case COBJ_CLOSURE: {
|
||||
CObjClosure *closure = (CObjClosure *)obj;
|
||||
cosmoM_freeArray(state, CObjUpval *, closure->upvalues, closure->upvalueCount);
|
||||
cosmoM_freearray(state, CObjUpval *, closure->upvalues, closure->upvalueCount);
|
||||
cosmoM_free(state, CObjClosure, closure);
|
||||
break;
|
||||
}
|
||||
@@ -164,12 +166,14 @@ _eqFail:
|
||||
cosmoV_pushValue(state, eq1);
|
||||
cosmoV_pushRef(state, obj1);
|
||||
cosmoV_pushRef(state, obj2);
|
||||
cosmoV_call(state, 2, 1);
|
||||
if (!cosmoV_call(state, 2, 1))
|
||||
return false;
|
||||
|
||||
// check return value and make sure it's a boolean
|
||||
if (!IS_BOOLEAN(*cosmoV_getTop(state, 0))) {
|
||||
cosmoV_error(state, "__equal expected to return <boolean>, got %s!",
|
||||
cosmoV_typeStr(*cosmoV_pop(state)));
|
||||
return false;
|
||||
}
|
||||
|
||||
// return the result
|
||||
@@ -186,10 +190,10 @@ CObjObject *cosmoO_newObject(CState *state)
|
||||
obj->userP = NULL; // reserved for C API
|
||||
obj->userT = 0;
|
||||
obj->isLocked = false;
|
||||
|
||||
cosmoV_pushRef(state, (CObj *)obj); // so our GC can keep track of it
|
||||
cosmoT_initTable(state, &obj->tbl, ARRAY_START);
|
||||
cosmoV_pop(state);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
@@ -229,17 +233,17 @@ CObjCFunction *cosmoO_newCFunction(CState *state, CosmoCFunction func)
|
||||
|
||||
CObjError *cosmoO_newError(CState *state, CValue err)
|
||||
{
|
||||
CCallFrame *frames = cosmoM_xmalloc(state, sizeof(CCallFrame) * state->frameCount);
|
||||
CObjError *cerror = (CObjError *)cosmoO_allocateBase(state, sizeof(CObjError), COBJ_ERROR);
|
||||
cerror->err = err;
|
||||
cerror->frameCount = state->frameCount;
|
||||
cerror->frames = frames;
|
||||
cerror->parserError = false;
|
||||
|
||||
// allocate the callframe
|
||||
cerror->frames = cosmoM_xmalloc(state, sizeof(CCallFrame) * cerror->frameCount);
|
||||
|
||||
// clone the call frame
|
||||
for (int i = 0; i < state->frameCount; i++) {
|
||||
for (int i = 0; i < state->frameCount; i++)
|
||||
cerror->frames[i] = state->callFrame[i];
|
||||
}
|
||||
|
||||
return cerror;
|
||||
}
|
||||
@@ -306,7 +310,7 @@ CObjString *cosmoO_takeString(CState *state, char *str, size_t length)
|
||||
|
||||
// have we already interned this string?
|
||||
if (lookup != NULL) {
|
||||
cosmoM_freeArray(state, char, str,
|
||||
cosmoM_freearray(state, char, str,
|
||||
length + 1); // free our passed character array, it's unneeded!
|
||||
return lookup;
|
||||
}
|
||||
@@ -322,7 +326,8 @@ CObjString *cosmoO_allocateString(CState *state, const char *str, size_t sz, uin
|
||||
strObj->length = sz;
|
||||
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);
|
||||
cosmoT_insert(state, &state->strings, cosmoV_newRef((CObj *)strObj));
|
||||
cosmoV_pop(state);
|
||||
@@ -398,27 +403,29 @@ bool cosmoO_isDescendant(CObj *obj, CObjObject *proto)
|
||||
}
|
||||
|
||||
// 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,
|
||||
val)) { // if the field doesn't exist in the object, check the proto
|
||||
if (cosmoO_getIString(state, proto, ISTRING_GETTER, val) && IS_TABLE(*val) &&
|
||||
cosmoT_get(state, &cosmoV_readTable(*val)->tbl, key, val)) {
|
||||
cosmoV_pushValue(state, *val); // push function
|
||||
cosmoV_pushRef(state, (CObj *)obj); // push object
|
||||
cosmoV_call(state, 1, 1); // call the function with the 1 argument
|
||||
*val = *cosmoV_pop(state); // set value to the return value of __index
|
||||
return;
|
||||
cosmoV_pushValue(state, *val); // push function
|
||||
cosmoV_pushRef(state, (CObj *)obj); // push object
|
||||
if (!cosmoV_call(state, 1, 1)) // call the function with the 1 argument
|
||||
return false;
|
||||
*val = *cosmoV_pop(state); // set value to the return value of __index
|
||||
return true;
|
||||
}
|
||||
|
||||
// maybe the field is defined in the proto?
|
||||
if (proto->_obj.proto != NULL) {
|
||||
cosmoO_getRawObject(state, proto->_obj.proto, key, val, obj);
|
||||
return;
|
||||
}
|
||||
if (proto->_obj.proto != NULL &&
|
||||
cosmoO_getRawObject(state, proto->_obj.proto, key, val, obj))
|
||||
return true;
|
||||
|
||||
*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)
|
||||
@@ -519,20 +526,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
|
||||
}
|
||||
|
||||
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)) {
|
||||
cosmoV_pushValue(state, *val); // push function
|
||||
cosmoV_pushRef(state, (CObj *)object); // push object
|
||||
cosmoV_pushValue(state, key); // push key
|
||||
cosmoV_call(state, 2, 1); // call the function with the 2 arguments
|
||||
*val = *cosmoV_pop(state); // set value to the return value of __index
|
||||
} else { // there's no __index function defined!
|
||||
cosmoV_pushValue(state, *val); // push function
|
||||
cosmoV_pushRef(state, (CObj *)object); // push object
|
||||
cosmoV_pushValue(state, key); // push key
|
||||
if (!cosmoV_call(state, 2, 1)) // call the function with the 2 arguments
|
||||
return false;
|
||||
*val = *cosmoV_pop(state); // set value to the return value of __index
|
||||
return true;
|
||||
} else { // there's no __index function defined!
|
||||
cosmoV_error(state, "Couldn't index object without __index function!");
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@@ -541,10 +552,12 @@ void cosmoO_newIndexObject(CState *state, CObjObject *object, CValue key, CValue
|
||||
cosmoV_pushRef(state, (CObj *)object); // push object
|
||||
cosmoV_pushValue(state, key); // push key & value pair
|
||||
cosmoV_pushValue(state, val);
|
||||
cosmoV_call(state, 3, 0);
|
||||
return cosmoV_call(state, 3, 0);
|
||||
} else { // there's no __newindex function defined
|
||||
cosmoV_error(state, "Couldn't set index on object without __newindex function!");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
CObjString *cosmoO_toString(CState *state, CObj *obj)
|
||||
@@ -556,7 +569,8 @@ CObjString *cosmoO_toString(CState *state, CObj *obj)
|
||||
if (protoObject != NULL && cosmoO_getIString(state, protoObject, ISTRING_TOSTRING, &res)) {
|
||||
cosmoV_pushValue(state, res);
|
||||
cosmoV_pushRef(state, (CObj *)obj);
|
||||
cosmoV_call(state, 1, 1);
|
||||
if (!cosmoV_call(state, 1, 1))
|
||||
return cosmoO_copyString(state, "<err>", 5);
|
||||
|
||||
// make sure the __tostring function returned a string
|
||||
StkPtr ret = cosmoV_getTop(state, 0);
|
||||
@@ -621,12 +635,14 @@ cosmo_Number cosmoO_toNumber(CState *state, CObj *obj)
|
||||
if (proto != NULL && cosmoO_getIString(state, proto, ISTRING_TONUMBER, &res)) {
|
||||
cosmoV_pushValue(state, res);
|
||||
cosmoV_pushRef(state, (CObj *)obj);
|
||||
cosmoV_call(state, 1, 1); // call res, expect 1 return val of <number>
|
||||
if (!cosmoV_call(state, 1, 1)) // call res, expect 1 return val of <number>
|
||||
return 0;
|
||||
|
||||
StkPtr temp = cosmoV_getTop(state, 0);
|
||||
if (!IS_NUMBER(*temp)) {
|
||||
cosmoV_error(state, "__tonumber expected to return <number>, got %s!",
|
||||
cosmoV_typeStr(*temp));
|
||||
return 0;
|
||||
}
|
||||
|
||||
// return number
|
||||
@@ -652,7 +668,8 @@ int cosmoO_count(CState *state, CObj *obj)
|
||||
if (proto != NULL && cosmoO_getIString(state, proto, ISTRING_COUNT, &res)) {
|
||||
cosmoV_pushValue(state, res);
|
||||
cosmoV_pushRef(state, (CObj *)obj);
|
||||
cosmoV_call(state, 1, 1); // call res, we expect 1 return value of type <number>
|
||||
if (!cosmoV_call(state, 1, 1)) // call res, we expect 1 return value of type <number>
|
||||
return 0;
|
||||
|
||||
StkPtr ret = cosmoV_getTop(state, 0);
|
||||
if (!IS_NUMBER(*ret)) {
|
||||
@@ -683,61 +700,60 @@ int cosmoO_count(CState *state, CObj *obj)
|
||||
|
||||
void printObject(CObj *o)
|
||||
{
|
||||
printf("%s ", cosmoO_typeStr(o));
|
||||
switch (o->type) {
|
||||
case COBJ_STRING: {
|
||||
CObjString *objStr = (CObjString *)o;
|
||||
printf("\"%.*s\"", objStr->length, objStr->str);
|
||||
printf("<string> \"%.*s\"", objStr->length, objStr->str);
|
||||
break;
|
||||
}
|
||||
case COBJ_OBJECT: {
|
||||
printf("%p", (void *)o);
|
||||
printf("<obj> %p", (void *)o);
|
||||
break;
|
||||
}
|
||||
case COBJ_TABLE: {
|
||||
CObjTable *tbl = (CObjTable *)o;
|
||||
printf("%p", (void *)tbl);
|
||||
printf("<tbl> %p", (void *)tbl);
|
||||
break;
|
||||
}
|
||||
case COBJ_FUNCTION: {
|
||||
CObjFunction *objFunc = (CObjFunction *)o;
|
||||
if (objFunc->name != NULL)
|
||||
printf("%.*s", objFunc->name->length, objFunc->name->str);
|
||||
printf("<function> %.*s", objFunc->name->length, objFunc->name->str);
|
||||
else
|
||||
printf("%s", UNNAMEDCHUNK);
|
||||
printf("<function> %s", UNNAMEDCHUNK);
|
||||
break;
|
||||
}
|
||||
case COBJ_CFUNCTION: {
|
||||
CObjCFunction *objCFunc = (CObjCFunction *)o;
|
||||
printf("%p", (void *)objCFunc->cfunc);
|
||||
printf("<c function> %p", (void *)objCFunc->cfunc);
|
||||
break;
|
||||
}
|
||||
case COBJ_ERROR: {
|
||||
CObjError *err = (CObjError *)o;
|
||||
printf("%p -> ", (void *)o);
|
||||
cosmoV_printValue(err->err);
|
||||
printf("<error> %p -> ", (void *)o);
|
||||
printValue(err->err);
|
||||
break;
|
||||
}
|
||||
case COBJ_METHOD: {
|
||||
CObjMethod *method = (CObjMethod *)o;
|
||||
printf("%p -> ", (void *)method);
|
||||
cosmoV_printValue(method->func);
|
||||
printf("<method> %p -> ", (void *)method);
|
||||
printValue(method->func);
|
||||
break;
|
||||
}
|
||||
case COBJ_CLOSURE: {
|
||||
CObjClosure *closure = (CObjClosure *)o;
|
||||
printf("%p -> ", (void *)closure);
|
||||
printf("<closure> %p -> ", (void *)closure);
|
||||
printObject((CObj *)closure->function); // just print the function
|
||||
break;
|
||||
}
|
||||
case COBJ_UPVALUE: {
|
||||
CObjUpval *upval = (CObjUpval *)o;
|
||||
printf("%p -> ", (void *)upval->val);
|
||||
cosmoV_printValue(*upval->val);
|
||||
printf("<upvalue> %p -> ", (void *)upval->val);
|
||||
printValue(*upval->val);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
printf("%p", (void *)o);
|
||||
printf("<unkn obj %p>", (void *)o);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -754,8 +770,6 @@ const char *cosmoO_typeStr(CObj *obj)
|
||||
return "<function>";
|
||||
case COBJ_CFUNCTION:
|
||||
return "<c function>";
|
||||
case COBJ_ERROR:
|
||||
return "<error>";
|
||||
case COBJ_METHOD:
|
||||
return "<method>";
|
||||
case COBJ_CLOSURE:
|
||||
|
12
src/cobj.h
12
src/cobj.h
@@ -3,8 +3,6 @@
|
||||
|
||||
#include "cosmo.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
typedef enum CObjType
|
||||
{
|
||||
COBJ_STRING,
|
||||
@@ -34,6 +32,7 @@ typedef int (*CosmoCFunction)(CState *state, int argCount, CValue *args);
|
||||
struct CObj
|
||||
{
|
||||
struct CObj *next;
|
||||
struct CObj *nextRoot; // for the root linked list
|
||||
struct CObjObject *proto; // protoobject, describes the behavior of the object
|
||||
CObjType type;
|
||||
bool isMarked; // for the GC
|
||||
@@ -55,7 +54,7 @@ struct CObjError
|
||||
CCallFrame *frames;
|
||||
int frameCount;
|
||||
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
|
||||
@@ -137,7 +136,6 @@ struct CObjUpval
|
||||
#define cosmoV_readCFunction(x) (((CObjCFunction *)cosmoV_readRef(x))->cfunc)
|
||||
#define cosmoV_readMethod(x) ((CObjMethod *)cosmoV_readRef(x))
|
||||
#define cosmoV_readClosure(x) ((CObjClosure *)cosmoV_readRef(x))
|
||||
#define cosmoV_readError(x) ((CObjError *)cosmoV_readRef(x))
|
||||
|
||||
#define cosmoO_readCString(x) ((CObjString *)x)->str
|
||||
#define cosmoO_readType(x) ((CObj *)x)->type
|
||||
@@ -173,10 +171,10 @@ static inline CObjObject *cosmoO_grabProto(CObj *obj)
|
||||
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_indexObject(CState *state, CObjObject *object, CValue key, CValue *val);
|
||||
void cosmoO_newIndexObject(CState *state, CObjObject *object, CValue key, CValue val);
|
||||
bool cosmoO_indexObject(CState *state, CObjObject *object, CValue key, CValue *val);
|
||||
bool cosmoO_newIndexObject(CState *state, CObjObject *object, CValue key, CValue val);
|
||||
|
||||
// sets the user-defined pointer, if a user-define integer is already defined it will be over
|
||||
// written
|
||||
|
@@ -64,4 +64,6 @@ typedef int (*cosmo_Writer)(CState *state, const void *data, size_t size, const
|
||||
#define UNNAMEDCHUNK "_main"
|
||||
#define COSMOASSERT(x) assert(x)
|
||||
|
||||
#define CERROR(err) printf("%s : %s\n", "[ERROR]", err)
|
||||
|
||||
#endif
|
||||
|
141
src/cparse.c
141
src/cparse.c
@@ -59,15 +59,14 @@ typedef struct CCompilerState
|
||||
|
||||
typedef struct
|
||||
{
|
||||
CLexState lex;
|
||||
CState *state;
|
||||
CLexState *lex;
|
||||
CCompilerState *compiler;
|
||||
CObjString *module; // name of the module
|
||||
CToken current;
|
||||
CToken previous; // token right after the current token
|
||||
int workingStackCount; // we push CValues of objects we need onto the stack so the garbage
|
||||
// collector can see them. this is the count of those values so we'll
|
||||
// know how many to pop off when we're done
|
||||
CToken previous; // token right after the current token
|
||||
bool hadError;
|
||||
bool panic;
|
||||
} CParseState;
|
||||
|
||||
typedef enum
|
||||
@@ -102,18 +101,14 @@ static int expressionPrecedence(CParseState *pstate, int needed, Precedence prec
|
||||
// returns # of pushed values onto the stack
|
||||
static int expression(CParseState *pstate, int needed, bool forceNeeded);
|
||||
static void statement(CParseState *pstate);
|
||||
static void declaration(CParseState *pstate);
|
||||
static void parseFunction(CParseState *pstate, FunctionType type);
|
||||
static void expressionStatement(CParseState *pstate);
|
||||
static ParseRule *getRule(CTokenType type);
|
||||
static CObjFunction *endCompiler(CParseState *pstate);
|
||||
|
||||
// ================================================================ [FRONT END/TALK TO LEXER]
|
||||
|
||||
static void keepTrackOf(CParseState *pstate, CValue val)
|
||||
{
|
||||
pstate->workingStackCount++;
|
||||
cosmoV_pushValue(pstate->state, val);
|
||||
}
|
||||
|
||||
static void initCompilerState(CParseState *pstate, CCompilerState *ccstate, FunctionType type,
|
||||
CCompilerState *enclosing)
|
||||
{
|
||||
@@ -129,8 +124,6 @@ static void initCompilerState(CParseState *pstate, CCompilerState *ccstate, Func
|
||||
ccstate->function = cosmoO_newFunction(pstate->state);
|
||||
ccstate->function->module = pstate->module;
|
||||
|
||||
keepTrackOf(pstate, cosmoV_newRef((CObj *)ccstate->function));
|
||||
|
||||
ccstate->loop.scope = -1; // there is no loop yet
|
||||
|
||||
if (type != FTYPE_SCRIPT) {
|
||||
@@ -153,27 +146,27 @@ static void initCompilerState(CParseState *pstate, CCompilerState *ccstate, Func
|
||||
static void initParseState(CParseState *pstate, CCompilerState *ccstate, CState *s,
|
||||
const char *source, const char *module)
|
||||
{
|
||||
cosmoL_initLexState(s, &pstate->lex, source);
|
||||
pstate->lex = cosmoL_newLexState(s, source);
|
||||
|
||||
pstate->state = s;
|
||||
pstate->hadError = false;
|
||||
pstate->panic = false;
|
||||
pstate->compiler = ccstate;
|
||||
pstate->module = cosmoO_copyString(s, module, strlen(module));
|
||||
pstate->workingStackCount = 0;
|
||||
|
||||
keepTrackOf(pstate, cosmoV_newRef((CObj *)pstate->module));
|
||||
initCompilerState(pstate, ccstate, FTYPE_SCRIPT, NULL); // enclosing starts as NULL
|
||||
}
|
||||
|
||||
static void freeParseState(CParseState *pstate)
|
||||
{
|
||||
cosmoL_cleanupLexState(pstate->state, &pstate->lex);
|
||||
|
||||
// pop our working values off the stack
|
||||
cosmoV_setTop(pstate->state, pstate->workingStackCount);
|
||||
cosmoL_freeLexState(pstate->state, pstate->lex);
|
||||
}
|
||||
|
||||
static void errorAt(CParseState *pstate, CToken *token, const char *format, va_list args)
|
||||
{
|
||||
if (pstate->hadError)
|
||||
return;
|
||||
|
||||
if (token->type == TOKEN_EOF) {
|
||||
cosmoV_pushString(pstate->state, "At end: ");
|
||||
} else if (!(token->type == TOKEN_ERROR)) {
|
||||
@@ -184,9 +177,14 @@ static void errorAt(CParseState *pstate, CToken *token, const char *format, va_l
|
||||
|
||||
cosmoO_pushVFString(pstate->state, format, args);
|
||||
|
||||
// throw complete error string
|
||||
cosmoV_concat(pstate->state, 2);
|
||||
cosmoV_throw(pstate->state);
|
||||
cosmoV_concat(pstate->state, 2); // concats the two strings together
|
||||
|
||||
CObjError *err = cosmoV_throw(pstate->state);
|
||||
err->line = token->line;
|
||||
err->parserError = true;
|
||||
|
||||
pstate->hadError = true;
|
||||
pstate->panic = true;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
pstate->previous = pstate->current;
|
||||
pstate->current = cosmoL_scanToken(&pstate->lex);
|
||||
pstate->current = cosmoL_scanToken(pstate->lex);
|
||||
|
||||
if (pstate->current.type == TOKEN_ERROR) {
|
||||
errorAtCurrent(pstate, pstate->current.start);
|
||||
@@ -282,6 +280,7 @@ uint16_t makeConstant(CParseState *pstate, CValue val)
|
||||
int indx = addConstant(pstate->state, getChunk(pstate), val);
|
||||
if (indx > UINT16_MAX) {
|
||||
error(pstate, "UInt overflow! Too many constants in one chunk!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (uint16_t)indx;
|
||||
@@ -351,6 +350,7 @@ static void addLocal(CParseState *pstate, CToken name)
|
||||
{
|
||||
if (pstate->compiler->localCount > UINT8_MAX) {
|
||||
error(pstate, "UInt overflow! Too many locals in scope!");
|
||||
return;
|
||||
}
|
||||
|
||||
Local *local = &pstate->compiler->locals[pstate->compiler->localCount++];
|
||||
@@ -365,6 +365,7 @@ static int addUpvalue(CParseState *pstate, CCompilerState *ccstate, uint8_t indx
|
||||
|
||||
if (upvals > UINT8_MAX) {
|
||||
error(pstate, "UInt overflow! Too many upvalues in scope!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// check and make sure we haven't already captured it
|
||||
@@ -480,7 +481,6 @@ static void string(CParseState *pstate, bool canAssign, Precedence prec)
|
||||
{
|
||||
CObjString *strObj =
|
||||
cosmoO_takeString(pstate->state, pstate->previous.start, pstate->previous.length);
|
||||
keepTrackOf(pstate, cosmoV_newRef((CObj *)strObj));
|
||||
writeConstant(pstate, cosmoV_newRef((CObj *)strObj));
|
||||
}
|
||||
|
||||
@@ -590,6 +590,8 @@ static void group(CParseState *pstate, bool canAssign, Precedence prec)
|
||||
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)
|
||||
{
|
||||
writeu8(pstate, a);
|
||||
@@ -766,6 +768,7 @@ static void table(CParseState *pstate, bool canAssign, Precedence prec)
|
||||
tblType = 1; // array-like
|
||||
} else {
|
||||
error(pstate, "Can't change table description type mid-definition!");
|
||||
return;
|
||||
}
|
||||
|
||||
entries++;
|
||||
@@ -815,7 +818,7 @@ static void object(CParseState *pstate, bool canAssign, Precedence prec)
|
||||
// "pop" the 1 value
|
||||
valuePopped(pstate, 1);
|
||||
entries++;
|
||||
} while (match(pstate, TOKEN_COMMA));
|
||||
} while (match(pstate, TOKEN_COMMA) && !pstate->hadError);
|
||||
|
||||
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)
|
||||
static bool parsePrecedence(CParseState *pstate, Precedence prec)
|
||||
{
|
||||
bool canAssign;
|
||||
ParseFunc prefix, infix;
|
||||
|
||||
advance(pstate);
|
||||
if ((prefix = getRule(pstate->previous.type)->prefix) == NULL)
|
||||
|
||||
ParseFunc prefix = getRule(pstate->previous.type)->prefix;
|
||||
|
||||
if (prefix == NULL)
|
||||
return false;
|
||||
|
||||
canAssign = prec <= PREC_ASSIGNMENT;
|
||||
bool canAssign = prec <= PREC_ASSIGNMENT;
|
||||
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);
|
||||
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)
|
||||
{
|
||||
if (pstate->hadError)
|
||||
return;
|
||||
|
||||
if (pstate->compiler->scopeDepth > 0 || forceLocal) {
|
||||
markInitialized(pstate, global);
|
||||
valuePopped(pstate, 1); // the local stays on the stack!
|
||||
@@ -1172,7 +1177,7 @@ static void _proto(CParseState *pstate)
|
||||
{
|
||||
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)) {
|
||||
// define 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)
|
||||
{
|
||||
if (pstate->hadError)
|
||||
return;
|
||||
|
||||
// count the locals in scope to pop
|
||||
int localsToPop = 0;
|
||||
|
||||
@@ -1263,7 +1271,7 @@ static void endScope(CParseState *pstate)
|
||||
static void block(CParseState *pstate)
|
||||
{
|
||||
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.'");
|
||||
@@ -1320,7 +1328,7 @@ static void ifStatement(CParseState *pstate)
|
||||
while (!check(pstate, TOKEN_END) && !check(pstate, TOKEN_ELSE) &&
|
||||
!check(pstate, TOKEN_ELSEIF) && !check(pstate, TOKEN_EOF) &&
|
||||
!check(pstate, TOKEN_ERROR)) {
|
||||
statement(pstate);
|
||||
declaration(pstate);
|
||||
}
|
||||
|
||||
endScope(pstate);
|
||||
@@ -1368,7 +1376,7 @@ static void endLoop(CParseState *pstate)
|
||||
patchJmp(pstate, pstate->compiler->loop.breaks[--pstate->compiler->loop.breakCount]);
|
||||
}
|
||||
|
||||
cosmoM_freeArray(pstate->state, int, pstate->compiler->loop.breaks,
|
||||
cosmoM_freearray(pstate->state, int, pstate->compiler->loop.breaks,
|
||||
pstate->compiler->loop.breakCapacity);
|
||||
}
|
||||
|
||||
@@ -1470,10 +1478,10 @@ static void functionDeclaration(CParseState *pstate)
|
||||
|
||||
static void returnStatement(CParseState *pstate)
|
||||
{
|
||||
// if (pstate->compiler->type != FTYPE_FUNCTION && pstate->compiler->type != FTYPE_METHOD) {
|
||||
// error(pstate, "Expected 'return' in function!");
|
||||
// return;
|
||||
// }
|
||||
if (pstate->compiler->type != FTYPE_FUNCTION && pstate->compiler->type != FTYPE_METHOD) {
|
||||
error(pstate, "Expected 'return' in function!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (blockFollow(pstate->current)) { // does this return have a value
|
||||
writeu8(pstate, OP_NIL);
|
||||
@@ -1588,7 +1596,7 @@ static void forLoop(CParseState *pstate)
|
||||
|
||||
// parse initializer
|
||||
if (!match(pstate, TOKEN_EOS)) {
|
||||
statement(pstate);
|
||||
expressionStatement(pstate);
|
||||
consume(pstate, TOKEN_EOS, "Expected ';' after initializer");
|
||||
}
|
||||
|
||||
@@ -1657,7 +1665,7 @@ static void breakStatement(CParseState *pstate)
|
||||
pstate->compiler->localCount = savedLocals;
|
||||
|
||||
// add break to loop
|
||||
cosmoM_growArray(pstate->state, int, pstate->compiler->loop.breaks,
|
||||
cosmoM_growarray(pstate->state, int, pstate->compiler->loop.breaks,
|
||||
pstate->compiler->loop.breakCount, pstate->compiler->loop.breakCapacity);
|
||||
pstate->compiler->loop.breaks[pstate->compiler->loop.breakCount++] = writeJmp(pstate, OP_JMP);
|
||||
}
|
||||
@@ -1678,6 +1686,18 @@ static void continueStatement(CParseState *pstate)
|
||||
writeJmpBack(pstate, pstate->compiler->loop.startBytecode);
|
||||
}
|
||||
|
||||
static void synchronize(CParseState *pstate)
|
||||
{
|
||||
pstate->panic = false;
|
||||
|
||||
while (pstate->current.type != TOKEN_EOF) {
|
||||
if (pstate->previous.type == TOKEN_EOS)
|
||||
return;
|
||||
|
||||
advance(pstate);
|
||||
}
|
||||
}
|
||||
|
||||
static int expressionPrecedence(CParseState *pstate, int needed, Precedence prec, bool forceNeeded)
|
||||
{
|
||||
int lastExpected = pstate->compiler->expectedValues;
|
||||
@@ -1704,7 +1724,7 @@ static int expression(CParseState *pstate, int needed, bool forceNeeded)
|
||||
forceNeeded); // anything above assignments are an expression
|
||||
}
|
||||
|
||||
static void statement(CParseState *pstate)
|
||||
static void expressionStatement(CParseState *pstate)
|
||||
{
|
||||
int savedPushed = pstate->compiler->pushedValues;
|
||||
|
||||
@@ -1759,6 +1779,20 @@ static void statement(CParseState *pstate)
|
||||
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)
|
||||
{
|
||||
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;
|
||||
CCompilerState compiler;
|
||||
cosmoM_freezeGC(state); // ignore all GC events while compiling
|
||||
initParseState(&parser, &compiler, state, source, module);
|
||||
|
||||
advance(&parser);
|
||||
|
||||
while (!match(&parser, TOKEN_EOF)) {
|
||||
statement(&parser);
|
||||
declaration(&parser);
|
||||
}
|
||||
|
||||
consume(&parser, TOKEN_EOF, "End of file expected!");
|
||||
|
||||
popLocals(&parser, 0);
|
||||
|
||||
if (parser.hadError) { // we don't free the function, the state already has a reference to it in
|
||||
// it's linked list of objects!
|
||||
endCompiler(&parser);
|
||||
freeParseState(&parser);
|
||||
|
||||
cosmoM_unfreezeGC(state);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CObjFunction *resFunc = compiler.function;
|
||||
|
||||
// finally free out parser states
|
||||
endCompiler(&parser);
|
||||
freeParseState(&parser);
|
||||
|
||||
// push the funciton onto the stack so if we cause an GC event, it won't be free'd
|
||||
cosmoV_pushRef(state, (CObj *)resFunc);
|
||||
cosmoM_unfreezeGC(state);
|
||||
cosmoV_pop(state);
|
||||
return resFunc;
|
||||
}
|
||||
|
@@ -4,7 +4,8 @@
|
||||
#include "clex.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);
|
||||
|
||||
#endif
|
||||
|
120
src/cstate.c
120
src/cstate.c
@@ -7,45 +7,26 @@
|
||||
|
||||
#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()
|
||||
{
|
||||
// we use C's malloc because we don't want to trigger a GC with an invalid state
|
||||
CState *state = malloc(sizeof(CState));
|
||||
|
||||
if (state == NULL) {
|
||||
printf("[ERROR] failed to allocate memory!");
|
||||
CERROR("failed to allocate memory!");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
state->panic = false;
|
||||
state->freezeGC = 1; // we start frozen
|
||||
state->panic = NULL;
|
||||
|
||||
// GC
|
||||
state->objects = NULL;
|
||||
state->userRoots = NULL;
|
||||
state->grayStack.count = 0;
|
||||
state->grayStack.capacity = 2;
|
||||
state->grayStack.array = NULL;
|
||||
state->allocatedBytes = 0;
|
||||
state->allocatedBytes = sizeof(CState);
|
||||
state->nextGC = 1024 * 8; // threshhold starts at 8kb
|
||||
|
||||
// init stack
|
||||
@@ -53,23 +34,21 @@ CState *cosmoV_newState()
|
||||
state->frameCount = 0;
|
||||
state->openUpvalues = NULL;
|
||||
|
||||
// set default proto objects
|
||||
for (int i = 0; i < COBJ_MAX; i++) {
|
||||
state->protoObjects[i] = NULL;
|
||||
}
|
||||
state->error = 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;
|
||||
}
|
||||
|
||||
cosmoT_initTable(state, &state->strings, 16); // init string table
|
||||
cosmoT_initTable(state, &state->registry, 16);
|
||||
|
||||
state->globals = cosmoO_newTable(state); // init global table
|
||||
|
||||
// setup all strings used by the VM
|
||||
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_TONUMBER] = cosmoO_copyString(state, "__tonumber", 10);
|
||||
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);
|
||||
|
||||
// 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->freezeGC = 0; // unfreeze the state
|
||||
return state;
|
||||
@@ -102,45 +80,38 @@ void cosmoV_freeState(CState *state)
|
||||
#ifdef GC_DEBUG
|
||||
printf("state %p is being free'd!\n", state);
|
||||
#endif
|
||||
cosmoM_freezeGC(state);
|
||||
|
||||
// frees all the objects
|
||||
CObj *objs = state->objects;
|
||||
while (objs != NULL) {
|
||||
CObj *next = objs->next;
|
||||
|
||||
#ifdef GC_DEBUG
|
||||
printf("STATE FREEING %p\n", objs);
|
||||
fflush(stdout);
|
||||
#endif
|
||||
|
||||
cosmoO_free(state, objs);
|
||||
objs = next;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// free our string table (the string table includes the internal VM strings)
|
||||
cosmoT_clearTable(state, &state->strings);
|
||||
|
||||
cosmoT_clearTable(state, &state->registry);
|
||||
|
||||
// free our gray stack & finally free the state structure
|
||||
cosmoM_freeArray(state, CObj *, state->grayStack.array, state->grayStack.capacity);
|
||||
|
||||
#ifdef GC_DEBUG
|
||||
if (state->allocatedBytes != 0) {
|
||||
printf("state->allocatedBytes doesn't match, got %lu\n", state->allocatedBytes);
|
||||
}
|
||||
#endif
|
||||
cosmoM_freearray(state, CObj *, state->grayStack.array, state->grayStack.capacity);
|
||||
|
||||
// TODO: yeah idk, it looks like im missing 520 bytes somewhere? i'll look into it later
|
||||
/*#ifdef GC_DEBUG
|
||||
if (state->allocatedBytes != sizeof(CState)) {
|
||||
printf("state->allocatedBytes doesn't match expected value (%lu), got %lu!",
|
||||
sizeof(CState), state->allocatedBytes); exit(0);
|
||||
}
|
||||
#endif*/
|
||||
free(state);
|
||||
}
|
||||
|
||||
// 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++) {
|
||||
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)
|
||||
{
|
||||
printf("==== [[ stack dump ]] ====\n");
|
||||
for (CValue *top = state->top - 1; top >= state->stack; top--) {
|
||||
printf("%d: ", (int)(top - state->stack));
|
||||
cosmoV_printValue(*top);
|
||||
printValue(*top);
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
63
src/cstate.h
63
src/cstate.h
@@ -6,8 +6,6 @@
|
||||
#include "ctable.h"
|
||||
#include "cvalue.h"
|
||||
|
||||
#include <setjmp.h>
|
||||
|
||||
struct CCallFrame
|
||||
{
|
||||
CObjClosure *closure;
|
||||
@@ -18,7 +16,6 @@ struct CCallFrame
|
||||
typedef enum IStringEnum
|
||||
{
|
||||
ISTRING_INIT, // __init
|
||||
ISTRING_GC, // __gc
|
||||
ISTRING_TOSTRING, // __tostring
|
||||
ISTRING_TONUMBER, // __tonumber
|
||||
ISTRING_EQUAL, // __equals
|
||||
@@ -41,56 +38,36 @@ typedef struct ArrayCObj
|
||||
int capacity;
|
||||
} ArrayCObj;
|
||||
|
||||
typedef struct CPanic
|
||||
{
|
||||
jmp_buf jmp;
|
||||
StkPtr top;
|
||||
struct CPanic *prev;
|
||||
int frameCount;
|
||||
int freezeGC;
|
||||
} CPanic;
|
||||
|
||||
struct CState
|
||||
{
|
||||
CCallFrame callFrame[FRAME_MAX]; // call frames
|
||||
CValue stack[STACK_MAX]; // stack
|
||||
CObjObject *protoObjects[COBJ_MAX]; // proto object for each COBJ type [NULL = no default proto]
|
||||
CObjString *iStrings[ISTRING_MAX]; // strings used internally by the VM, eg. __init, __index
|
||||
CTable strings;
|
||||
CTable registry;
|
||||
bool panic;
|
||||
int freezeGC; // when > 0, GC events will be ignored (for internal use)
|
||||
int frameCount;
|
||||
|
||||
CObjError *error; // NULL, unless panic is true
|
||||
CObj *objects; // tracks all of our allocated objects
|
||||
CObj *userRoots; // user definable roots, this holds CObjs that should be considered "roots",
|
||||
// lets the VM know you are holding a reference to a CObj in your code
|
||||
ArrayCObj grayStack; // keeps track of which objects *haven't yet* been traversed in our GC, but
|
||||
// *have been* found
|
||||
|
||||
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 frameCount;
|
||||
|
||||
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();
|
||||
// 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);
|
||||
|
||||
// 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);
|
||||
|
||||
#endif
|
||||
|
46
src/ctable.c
46
src/ctable.c
@@ -34,7 +34,6 @@ void cosmoT_initTable(CState *state, CTable *tbl, int startCap)
|
||||
tbl->capacityMask = startCap - 1;
|
||||
tbl->count = 0;
|
||||
tbl->tombstones = 0;
|
||||
tbl->tombThreshold = 32;
|
||||
tbl->table = NULL; // to let out GC know we're initalizing
|
||||
tbl->table = cosmoM_xmalloc(state, sizeof(CTableEntry) * startCap);
|
||||
|
||||
@@ -48,7 +47,7 @@ void cosmoT_initTable(CState *state, CTable *tbl, int startCap)
|
||||
void cosmoT_addTable(CState *state, CTable *from, CTable *to)
|
||||
{
|
||||
CTableEntry *entry;
|
||||
int cap = cosmoT_getCapacity(from);
|
||||
int cap = from->capacityMask + 1;
|
||||
|
||||
for (int i = 0; i < cap; i++) {
|
||||
entry = &from->table[i];
|
||||
@@ -61,7 +60,7 @@ void cosmoT_addTable(CState *state, CTable *from, CTable *to)
|
||||
|
||||
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)
|
||||
@@ -69,8 +68,6 @@ static uint32_t getObjectHash(CObj *obj)
|
||||
switch (obj->type) {
|
||||
case COBJ_STRING:
|
||||
return ((CObjString *)obj)->hash;
|
||||
case COBJ_CFUNCTION:
|
||||
return (uint32_t)((CObjCFunction *)obj)->cfunc;
|
||||
default:
|
||||
return (uint32_t)obj; // just "hash" the pointer
|
||||
}
|
||||
@@ -89,9 +86,8 @@ static uint32_t getValueHash(CValue *val)
|
||||
return 0;
|
||||
|
||||
memcpy(buf, &num, sizeof(buf));
|
||||
for (size_t i = 0; i < sizeof(cosmo_Number) / sizeof(uint32_t); i++) {
|
||||
for (size_t i = 0; i < sizeof(cosmo_Number) / sizeof(uint32_t); i++)
|
||||
buf[0] += buf[i];
|
||||
}
|
||||
return buf[0];
|
||||
}
|
||||
// TODO: add support for other types
|
||||
@@ -117,10 +113,10 @@ static CTableEntry *findEntry(CState *state, CTableEntry *entries, int mask, CVa
|
||||
if (IS_NIL(entry->val)) {
|
||||
// it's empty! if we found a tombstone, return that so it'll be reused
|
||||
return tomb != NULL ? tomb : entry;
|
||||
} else {
|
||||
// its a tombstone!
|
||||
tomb = entry;
|
||||
}
|
||||
|
||||
// its a tombstone!
|
||||
tomb = entry;
|
||||
} else if (cosmoV_equal(state, entry->key, key)) {
|
||||
return entry;
|
||||
}
|
||||
@@ -145,7 +141,7 @@ static void resizeTbl(CState *state, CTable *tbl, int newCapacity, bool canShrin
|
||||
return;
|
||||
|
||||
CTableEntry *entries = cosmoM_xmalloc(state, size);
|
||||
oldCap = cosmoT_getCapacity(tbl);
|
||||
oldCap = tbl->capacityMask + 1;
|
||||
newCount = 0;
|
||||
|
||||
// set all nodes as NIL : NIL
|
||||
@@ -168,7 +164,7 @@ static void resizeTbl(CState *state, CTable *tbl, int newCapacity, bool canShrin
|
||||
}
|
||||
|
||||
// free the old table
|
||||
cosmoM_freeArray(state, CTableEntry, tbl->table, oldCap);
|
||||
cosmoM_freearray(state, CTableEntry, tbl->table, oldCap);
|
||||
|
||||
tbl->table = entries;
|
||||
tbl->capacityMask = newCapacity - 1;
|
||||
@@ -180,10 +176,10 @@ bool cosmoT_checkShrink(CState *state, CTable *tbl)
|
||||
{
|
||||
// if count > 8 and active entries < tombstones
|
||||
if (tbl->count > MIN_TABLE_CAPACITY &&
|
||||
(tbl->count - tbl->tombstones < tbl->tombstones || tbl->tombstones > tbl->tombThreshold)) {
|
||||
// shrink based on active entries to the next pow of 2
|
||||
resizeTbl(state, tbl, nextPow2(tbl->count - tbl->tombstones) * GROW_FACTOR, false);
|
||||
tbl->tombThreshold = tbl->count / 4;
|
||||
(tbl->count - tbl->tombstones < tbl->tombstones ||
|
||||
tbl->tombstones > 50)) { // TODO: 50 should be a threshhold
|
||||
resizeTbl(state, tbl, nextPow2(tbl->count - tbl->tombstones) * GROW_FACTOR,
|
||||
false); // shrink based on active entries to the next pow of 2
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -194,7 +190,7 @@ bool cosmoT_checkShrink(CState *state, CTable *tbl)
|
||||
COSMO_API CValue *cosmoT_insert(CState *state, CTable *tbl, CValue key)
|
||||
{
|
||||
// make sure we have enough space allocated
|
||||
int cap = cosmoT_getCapacity(tbl);
|
||||
int cap = tbl->capacityMask + 1;
|
||||
if (tbl->count + 1 > (int)(cap * MAX_TABLE_FILL)) {
|
||||
// grow table
|
||||
int newCap = cap * GROW_FACTOR;
|
||||
@@ -202,7 +198,8 @@ COSMO_API CValue *cosmoT_insert(CState *state, CTable *tbl, CValue key)
|
||||
}
|
||||
|
||||
// insert into the table
|
||||
CTableEntry *entry = findEntry(state, tbl->table, tbl->capacityMask, key);
|
||||
CTableEntry *entry =
|
||||
findEntry(state, tbl->table, tbl->capacityMask, key); // -1 for our capacity mask
|
||||
|
||||
if (IS_NIL(entry->key)) {
|
||||
if (IS_NIL(entry->val)) // is it empty?
|
||||
@@ -258,10 +255,9 @@ CObjString *cosmoT_lookupString(CTable *tbl, const char *str, int length, uint32
|
||||
{
|
||||
if (tbl->count == 0)
|
||||
return 0; // sanity check
|
||||
|
||||
// since we know the capacity will *always* be a power of 2, we
|
||||
// can use bitwise & to perform a MUCH faster mod operation
|
||||
uint32_t indx = hash & tbl->capacityMask;
|
||||
uint32_t indx =
|
||||
hash & tbl->capacityMask; // since we know the capacity will *always* be a power of 2, we
|
||||
// can use bitwise & to perform a MUCH faster mod operation
|
||||
|
||||
// keep looking for an open slot in the entries array
|
||||
while (true) {
|
||||
@@ -284,13 +280,13 @@ CObjString *cosmoT_lookupString(CTable *tbl, const char *str, int length, uint32
|
||||
void cosmoT_printTable(CTable *tbl, const char *name)
|
||||
{
|
||||
printf("==== [[%s]] ====\n", name);
|
||||
int cap = cosmoT_getCapacity(tbl);
|
||||
int cap = tbl->capacityMask + 1;
|
||||
for (int i = 0; i < cap; i++) {
|
||||
CTableEntry *entry = &tbl->table[i];
|
||||
if (!(IS_NIL(entry->key))) {
|
||||
cosmoV_printValue(entry->key);
|
||||
printValue(entry->key);
|
||||
printf(" - ");
|
||||
cosmoV_printValue(entry->val);
|
||||
printValue(entry->val);
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
@@ -18,12 +18,9 @@ typedef struct CTable
|
||||
int count;
|
||||
int capacityMask; // +1 to get the capacity
|
||||
int tombstones;
|
||||
int tombThreshold;
|
||||
CTableEntry *table;
|
||||
} CTable;
|
||||
|
||||
#define cosmoT_getCapacity(tbl) ((tbl)->capacityMask + 1)
|
||||
|
||||
COSMO_API void cosmoT_initTable(CState *state, CTable *tbl, int startCap);
|
||||
COSMO_API void cosmoT_clearTable(CState *state, CTable *tbl);
|
||||
COSMO_API int cosmoT_count(CTable *tbl);
|
||||
|
@@ -13,12 +13,12 @@ void initValArray(CState *state, CValueArray *val, size_t startCapacity)
|
||||
|
||||
void cleanValArray(CState *state, CValueArray *array)
|
||||
{
|
||||
cosmoM_freeArray(state, CValue, array->values, array->capacity);
|
||||
cosmoM_freearray(state, CValue, array->values, array->capacity);
|
||||
}
|
||||
|
||||
void appendValArray(CState *state, CValueArray *array, CValue val)
|
||||
{
|
||||
cosmoM_growArray(state, CValue, array->values, array->count, array->capacity);
|
||||
cosmoM_growarray(state, CValue, array->values, array->count, array->capacity);
|
||||
|
||||
array->values[array->count++] = val;
|
||||
}
|
||||
@@ -101,7 +101,7 @@ const char *cosmoV_typeStr(CValue val)
|
||||
}
|
||||
}
|
||||
|
||||
void cosmoV_printValue(CValue val)
|
||||
void printValue(CValue val)
|
||||
{
|
||||
switch (GET_TYPE(val)) {
|
||||
case COSMO_TNUMBER:
|
||||
|
@@ -119,7 +119,7 @@ void initValArray(CState *state, CValueArray *val, size_t startCapacity);
|
||||
void cleanValArray(CState *state, CValueArray *array); // cleans array
|
||||
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 CObjString *cosmoV_toString(CState *state, CValue val);
|
||||
COSMO_API cosmo_Number cosmoV_toNumber(CState *state, CValue val);
|
||||
|
313
src/cvm.c
313
src/cvm.c
@@ -10,9 +10,7 @@
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
|
||||
#define cosmoV_protect(panic) setjmp(panic->jmp) == 0
|
||||
|
||||
void cosmoV_pushFString(CState *state, const char *format, ...)
|
||||
COSMO_API void cosmoV_pushFString(CState *state, const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
@@ -21,24 +19,26 @@ void cosmoV_pushFString(CState *state, const char *format, ...)
|
||||
}
|
||||
|
||||
// inserts val at state->top - indx - 1, moving everything else up
|
||||
void cosmoV_insert(CState *state, int indx, CValue val)
|
||||
COSMO_API void cosmo_insert(CState *state, int indx, CValue val)
|
||||
{
|
||||
StkPtr tmp = cosmoV_getTop(state, indx);
|
||||
|
||||
// moves everything up
|
||||
for (StkPtr i = state->top; i > tmp; i--) {
|
||||
for (StkPtr i = state->top; i > tmp; i--)
|
||||
*i = *(i - 1);
|
||||
}
|
||||
|
||||
*tmp = val;
|
||||
state->top++;
|
||||
}
|
||||
|
||||
bool cosmoV_undump(CState *state, cosmo_Reader reader, const void *ud)
|
||||
COSMO_API bool cosmoV_undump(CState *state, cosmo_Reader reader, const void *ud)
|
||||
{
|
||||
CObjFunction *func;
|
||||
|
||||
if (cosmoD_undump(state, reader, ud, &func)) {
|
||||
// fail recovery
|
||||
state->panic = false;
|
||||
cosmoV_pushRef(state, (CObj *)state->error);
|
||||
return false;
|
||||
};
|
||||
|
||||
@@ -53,20 +53,15 @@ bool cosmoV_undump(CState *state, cosmo_Reader reader, const void *ud)
|
||||
return true;
|
||||
}
|
||||
|
||||
// returns false if failed, error will be on the top of the stack. true if successful, closure will
|
||||
// be on the top of the stack
|
||||
bool cosmoV_compileString(CState *state, const char *src, const char *name)
|
||||
COSMO_API bool cosmoV_compileString(CState *state, const char *src, const char *name)
|
||||
{
|
||||
CObjFunction *func;
|
||||
CPanic *panic = cosmoV_newPanic(state);
|
||||
|
||||
if (cosmoV_protect(panic)) {
|
||||
func = cosmoP_compileString(state, src, name);
|
||||
if ((func = cosmoP_compileString(state, src, name)) != NULL) {
|
||||
// success
|
||||
#ifdef VM_DEBUG
|
||||
disasmChunk(&func->chunk, func->module->str, 0);
|
||||
#endif
|
||||
cosmoV_freePanic(state);
|
||||
|
||||
// push function onto the stack so it doesn't it cleaned up by the GC, at the same stack
|
||||
// location put our closure
|
||||
cosmoV_pushRef(state, (CObj *)func);
|
||||
@@ -74,11 +69,13 @@ bool cosmoV_compileString(CState *state, const char *src, const char *name)
|
||||
return true;
|
||||
}
|
||||
|
||||
cosmoV_freePanic(state);
|
||||
// fail recovery
|
||||
state->panic = false;
|
||||
cosmoV_pushRef(state, (CObj *)state->error);
|
||||
return false;
|
||||
}
|
||||
|
||||
void cosmoV_printBacktrace(CState *state, CObjError *err)
|
||||
COSMO_API void cosmoV_printError(CState *state, CObjError *err)
|
||||
{
|
||||
// print stack trace
|
||||
for (int i = 0; i < err->frameCount; i++) {
|
||||
@@ -112,30 +109,30 @@ void cosmoV_printBacktrace(CState *state, CObjError *err)
|
||||
}
|
||||
|
||||
/*
|
||||
takes value on top of the stack and wraps an CObjError around it, then throws it
|
||||
takes value on top of the stack and wraps an CObjError around it, state->error is set to that
|
||||
value the value on the stack is *expected* to be a string, but not required, so yes, this means
|
||||
you could throw a nil value if you really wanted too..
|
||||
*/
|
||||
void cosmoV_throw(CState *state)
|
||||
CObjError *cosmoV_throw(CState *state)
|
||||
{
|
||||
StkPtr temp = cosmoV_getTop(state, 0);
|
||||
CObjError *error = cosmoO_newError(state, *temp);
|
||||
|
||||
CValue val = cosmoV_newRef((CObj *)cosmoO_newError(state, *temp));
|
||||
if (state->panic) {
|
||||
state->top = state->panic->top;
|
||||
state->frameCount = state->panic->frameCount;
|
||||
state->freezeGC = state->panic->freezeGC;
|
||||
cosmoV_pushValue(state, val);
|
||||
longjmp(state->panic->jmp, 1);
|
||||
} else {
|
||||
cosmoV_pushValue(state, val);
|
||||
fprintf(stderr, "Unhandled panic! ");
|
||||
cosmoV_printBacktrace(state, error);
|
||||
exit(1);
|
||||
}
|
||||
CObjError *error = cosmoO_newError(state, *temp);
|
||||
state->error = error;
|
||||
state->panic = true;
|
||||
|
||||
cosmoV_pop(state); // pops thrown value off the stack
|
||||
return error;
|
||||
}
|
||||
|
||||
void cosmoV_error(CState *state, const char *format, ...)
|
||||
{
|
||||
if (state->panic)
|
||||
return;
|
||||
|
||||
// i set panic before calling cosmoO_pushVFString, since that can also call cosmoV_error
|
||||
state->panic = true;
|
||||
|
||||
// format the error string and push it onto the stack
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
@@ -240,16 +237,18 @@ void cosmoV_concat(CState *state, int vals)
|
||||
}
|
||||
|
||||
int cosmoV_execute(CState *state);
|
||||
void invokeMethod(CState *state, CObj *obj, CValue func, int args, int nresults, int offset);
|
||||
bool invokeMethod(CState *state, CObj *obj, CValue func, int args, int nresults, int offset);
|
||||
|
||||
/*
|
||||
calls a native C Function with # args on the stack, nresults are pushed onto the stack upon
|
||||
return.
|
||||
return.
|
||||
|
||||
state->top is moved to base + offset + nresults, with nresults pushed onto the stack
|
||||
from base + offset
|
||||
returns:
|
||||
false: state paniced during C Function, error is at state->error
|
||||
true: state->top is moved to base + offset + nresults, with nresults pushed onto the stack
|
||||
from base + offset
|
||||
*/
|
||||
static void callCFunction(CState *state, CosmoCFunction cfunc, int args, int nresults, int offset)
|
||||
static bool callCFunction(CState *state, CosmoCFunction cfunc, int args, int nresults, int offset)
|
||||
{
|
||||
StkPtr savedBase = cosmoV_getTop(state, args);
|
||||
|
||||
@@ -261,27 +260,35 @@ static void callCFunction(CState *state, CosmoCFunction cfunc, int args, int nre
|
||||
|
||||
// remember where the return values are
|
||||
StkPtr results = cosmoV_getTop(state, nres - 1);
|
||||
|
||||
state->top = savedBase + offset; // set stack
|
||||
|
||||
// if the state paniced during the c function, return false
|
||||
if (state->panic)
|
||||
return false;
|
||||
|
||||
// push the return value back onto the stack
|
||||
memmove(state->top, results,
|
||||
sizeof(CValue) * nres); // copies the return values to the top of the stack
|
||||
state->top += nres; // and make sure to move state->top to match
|
||||
|
||||
// now, if the caller function expected more return values, push nils onto the stack
|
||||
for (int i = nres; i < nresults; i++) {
|
||||
for (int i = nres; i < nresults; i++)
|
||||
cosmoV_pushValue(state, cosmoV_newNil());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
calls a raw closure object with # args on the stack, nresults are pushed onto the stack upon
|
||||
return.
|
||||
return.
|
||||
|
||||
stack->top is moved to base + offset + nresults, with nresults pushed onto the stack
|
||||
from base + offset
|
||||
returns:
|
||||
false: state paniced, error is at state->error
|
||||
true: stack->top is moved to base + offset + nresults, with nresults pushed onto the stack
|
||||
from base + offset
|
||||
*/
|
||||
static void rawCall(CState *state, CObjClosure *closure, int args, int nresults, int offset)
|
||||
static bool rawCall(CState *state, CObjClosure *closure, int args, int nresults, int offset)
|
||||
{
|
||||
CObjFunction *func = closure->function;
|
||||
|
||||
@@ -305,6 +312,7 @@ static void rawCall(CState *state, CObjClosure *closure, int args, int nresults,
|
||||
cosmoV_error(state, "Expected %d arguments for %s, got %d!", closure->function->args,
|
||||
closure->function->name == NULL ? UNNAMEDCHUNK : closure->function->name->str,
|
||||
args);
|
||||
return false;
|
||||
} else {
|
||||
// load function into callframe
|
||||
pushCallFrame(state, closure, func->args);
|
||||
@@ -322,6 +330,9 @@ static void rawCall(CState *state, CObjClosure *closure, int args, int nresults,
|
||||
// pop the callframe and return results :)
|
||||
popCallFrame(state, offset);
|
||||
|
||||
if (state->panic) // panic state
|
||||
return false;
|
||||
|
||||
// push the return values back onto the stack
|
||||
for (int i = 0; i < nres; i++) {
|
||||
state->top[i] = results[i];
|
||||
@@ -329,36 +340,35 @@ static void rawCall(CState *state, CObjClosure *closure, int args, int nresults,
|
||||
state->top += nres; // and make sure to move state->top to match
|
||||
|
||||
// now, if the caller function expected more return values, push nils onto the stack
|
||||
for (int i = nres; i < nresults; i++) {
|
||||
for (int i = nres; i < nresults; i++)
|
||||
cosmoV_pushValue(state, cosmoV_newNil());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// returns true if successful, false if error
|
||||
void callCValue(CState *state, CValue func, int args, int nresults, int offset)
|
||||
bool callCValue(CState *state, CValue func, int args, int nresults, int offset)
|
||||
{
|
||||
#ifdef VM_DEBUG
|
||||
printf("\n");
|
||||
printIndent(state->frameCount - 1);
|
||||
cosmoV_printValue(func);
|
||||
printValue(func);
|
||||
printf("(%d args)\n", args);
|
||||
#endif
|
||||
|
||||
if (!IS_REF(func)) {
|
||||
cosmoV_error(state, "Cannot call non-callable type %s!", cosmoV_typeStr(func));
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (cosmoV_readRef(func)->type) {
|
||||
case COBJ_CLOSURE:
|
||||
rawCall(state, cosmoV_readClosure(func), args, nresults, offset);
|
||||
break;
|
||||
return rawCall(state, cosmoV_readClosure(func), args, nresults, offset);
|
||||
case COBJ_CFUNCTION:
|
||||
callCFunction(state, cosmoV_readCFunction(func), args, nresults, offset);
|
||||
break;
|
||||
return callCFunction(state, cosmoV_readCFunction(func), args, nresults, offset);
|
||||
case COBJ_METHOD: {
|
||||
CObjMethod *method = (CObjMethod *)cosmoV_readRef(func);
|
||||
invokeMethod(state, method->obj, method->func, args, nresults, offset + 1);
|
||||
break;
|
||||
return invokeMethod(state, method->obj, method->func, args, nresults, offset + 1);
|
||||
}
|
||||
case COBJ_OBJECT: { // object is being instantiated, making another object
|
||||
CObjObject *protoObj = (CObjObject *)cosmoV_readRef(func);
|
||||
@@ -371,18 +381,20 @@ void callCValue(CState *state, CValue func, int args, int nresults, int offset)
|
||||
|
||||
// check if they defined an initializer (we accept 0 return values)
|
||||
if (cosmoO_getIString(state, protoObj, ISTRING_INIT, &ret)) {
|
||||
invokeMethod(state, (CObj *)newObj, ret, args, 0, offset + 1);
|
||||
if (!invokeMethod(state, (CObj *)newObj, ret, args, 0, offset + 1))
|
||||
return false;
|
||||
} else {
|
||||
// no default initializer
|
||||
cosmoV_error(state, "Expected __init() in proto, object cannot be instantiated!");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (nresults > 0) {
|
||||
cosmoV_pushRef(state, (CObj *)newObj);
|
||||
|
||||
// push the nils to fill up the expected return values.
|
||||
// -1 since the we already pushed the important value
|
||||
for (int i = 0; i < nresults - 1; i++) {
|
||||
// push the nils to fill up the expected return values
|
||||
for (int i = 0; i < nresults - 1;
|
||||
i++) { // -1 since the we already pushed the important value
|
||||
cosmoV_pushValue(state, cosmoV_newNil());
|
||||
}
|
||||
}
|
||||
@@ -390,16 +402,19 @@ void callCValue(CState *state, CValue func, int args, int nresults, int offset)
|
||||
}
|
||||
default:
|
||||
cosmoV_error(state, "Cannot call non-callable type %s!", cosmoV_typeStr(func));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void invokeMethod(CState *state, CObj *obj, CValue func, int args, int nresults, int offset)
|
||||
bool invokeMethod(CState *state, CObj *obj, CValue func, int args, int nresults, int offset)
|
||||
{
|
||||
// first, set the first argument to the object
|
||||
StkPtr temp = cosmoV_getTop(state, args);
|
||||
*temp = cosmoV_newRef(obj);
|
||||
|
||||
callCValue(state, func, args + 1, nresults, offset);
|
||||
return callCValue(state, func, args + 1, nresults, offset);
|
||||
}
|
||||
|
||||
// wraps cosmoV_call in a protected state, CObjError will be pushed onto the stack if function call
|
||||
@@ -407,35 +422,34 @@ void invokeMethod(CState *state, CObj *obj, CValue func, int args, int nresults,
|
||||
// returns false if function call failed, true if function call succeeded
|
||||
bool cosmoV_pcall(CState *state, int args, int nresults)
|
||||
{
|
||||
CPanic *panic = cosmoV_newPanic(state);
|
||||
StkPtr base = cosmoV_getTop(state, args);
|
||||
|
||||
if (cosmoV_protect(panic)) {
|
||||
cosmoV_call(state, args, nresults);
|
||||
cosmoV_freePanic(state);
|
||||
return true;
|
||||
} else {
|
||||
// if cosmoV_protect returns false, the error is already on the top of the stack
|
||||
if (!callCValue(state, *base, args, nresults, 0)) {
|
||||
// restore panic state
|
||||
state->panic = false;
|
||||
|
||||
if (nresults > 0) {
|
||||
cosmoV_pushRef(state, (CObj *)state->error);
|
||||
|
||||
// push other expected results onto the stack
|
||||
for (int i = 0; i < nresults - 1; i++) {
|
||||
for (int i = 0; i < nresults - 1; i++)
|
||||
cosmoV_pushValue(state, cosmoV_newNil());
|
||||
}
|
||||
}
|
||||
|
||||
cosmoV_freePanic(state);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// calls a callable object at stack->top - args - 1, passing the # of args to the callable, and
|
||||
// ensuring nresults are returned
|
||||
// returns false if an error was thrown, else true if successful
|
||||
void cosmoV_call(CState *state, int args, int nresults)
|
||||
bool cosmoV_call(CState *state, int args, int nresults)
|
||||
{
|
||||
StkPtr val = cosmoV_getTop(state, args); // function will always be right above the args
|
||||
|
||||
callCValue(state, *val, args, nresults, 0);
|
||||
return callCValue(state, *val, args, nresults, 0);
|
||||
}
|
||||
|
||||
static inline bool isFalsey(StkPtr val)
|
||||
@@ -443,7 +457,7 @@ static inline bool isFalsey(StkPtr val)
|
||||
return IS_NIL(*val) || (IS_BOOLEAN(*val) && !cosmoV_readBoolean(*val));
|
||||
}
|
||||
|
||||
CObjObject *cosmoV_makeObject(CState *state, int pairs)
|
||||
COSMO_API CObjObject *cosmoV_makeObject(CState *state, int pairs)
|
||||
{
|
||||
StkPtr key, val;
|
||||
CObjObject *newObj = cosmoO_newObject(state);
|
||||
@@ -464,7 +478,7 @@ CObjObject *cosmoV_makeObject(CState *state, int pairs)
|
||||
return newObj;
|
||||
}
|
||||
|
||||
bool cosmoV_registerProtoObject(CState *state, CObjType objType, CObjObject *obj)
|
||||
COSMO_API bool cosmoV_registerProtoObject(CState *state, CObjType objType, CObjObject *obj)
|
||||
{
|
||||
bool replaced = state->protoObjects[objType] != NULL;
|
||||
state->protoObjects[objType] = obj;
|
||||
@@ -473,7 +487,7 @@ bool cosmoV_registerProtoObject(CState *state, CObjType objType, CObjObject *obj
|
||||
CObj *curr = state->objects;
|
||||
while (curr != NULL) {
|
||||
// update the proto
|
||||
if (curr != (CObj *)obj && curr->type == objType && curr->proto != NULL) {
|
||||
if (curr->type == objType && curr->proto != NULL) {
|
||||
curr->proto = obj;
|
||||
}
|
||||
curr = curr->next;
|
||||
@@ -482,7 +496,7 @@ bool cosmoV_registerProtoObject(CState *state, CObjType objType, CObjObject *obj
|
||||
return replaced;
|
||||
}
|
||||
|
||||
void cosmoV_makeTable(CState *state, int pairs)
|
||||
COSMO_API void cosmoV_makeTable(CState *state, int pairs)
|
||||
{
|
||||
StkPtr key, val;
|
||||
CObjTable *newObj = cosmoO_newTable(state);
|
||||
@@ -502,7 +516,7 @@ void cosmoV_makeTable(CState *state, int pairs)
|
||||
cosmoV_pushRef(state, (CObj *)newObj);
|
||||
}
|
||||
|
||||
void cosmoV_rawget(CState *state, CObj *_obj, CValue key, CValue *val)
|
||||
bool cosmoV_rawget(CState *state, CObj *_obj, CValue key, CValue *val)
|
||||
{
|
||||
CObjObject *object = cosmoO_grabProto(_obj);
|
||||
|
||||
@@ -511,16 +525,23 @@ void cosmoV_rawget(CState *state, CObj *_obj, CValue key, CValue *val)
|
||||
CObjString *field = cosmoV_toString(state, key);
|
||||
cosmoV_error(state, "No proto defined! Couldn't get field '%s' from type %s", field->str,
|
||||
cosmoO_typeStr(_obj));
|
||||
*val = cosmoV_newNil();
|
||||
return false;
|
||||
}
|
||||
|
||||
// push the object onto the stack so the GC can find it
|
||||
cosmoV_pushRef(state, (CObj *)object);
|
||||
cosmoO_getRawObject(state, object, key, val, _obj);
|
||||
if (cosmoO_getRawObject(state, object, key, val, _obj)) {
|
||||
// *val now equals the response, pop the object
|
||||
cosmoV_pop(state);
|
||||
return true;
|
||||
}
|
||||
|
||||
cosmoV_pop(state);
|
||||
return false;
|
||||
}
|
||||
|
||||
void cosmoV_rawset(CState *state, CObj *_obj, CValue key, CValue val)
|
||||
bool cosmoV_rawset(CState *state, CObj *_obj, CValue key, CValue val)
|
||||
{
|
||||
CObjObject *object = cosmoO_grabProto(_obj);
|
||||
|
||||
@@ -529,12 +550,14 @@ void cosmoV_rawset(CState *state, CObj *_obj, CValue key, CValue val)
|
||||
CObjString *field = cosmoV_toString(state, key);
|
||||
cosmoV_error(state, "No proto defined! Couldn't set field '%s' to type %s", field->str,
|
||||
cosmoO_typeStr(_obj));
|
||||
return false;
|
||||
}
|
||||
|
||||
cosmoO_setRawObject(state, object, key, val, _obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
void cosmoV_get(CState *state)
|
||||
COSMO_API bool cosmoV_get(CState *state)
|
||||
{
|
||||
CValue val;
|
||||
StkPtr obj = cosmoV_getTop(state, 1); // object was pushed first
|
||||
@@ -542,17 +565,20 @@ void cosmoV_get(CState *state)
|
||||
|
||||
if (!IS_REF(*obj)) {
|
||||
cosmoV_error(state, "Couldn't get field from type %s!", cosmoV_typeStr(*obj));
|
||||
return false;
|
||||
}
|
||||
|
||||
cosmoV_rawget(state, cosmoV_readRef(*obj), *key, &val);
|
||||
if (!cosmoV_rawget(state, cosmoV_readRef(*obj), *key, &val))
|
||||
return false;
|
||||
|
||||
// pop the obj & key, push the value
|
||||
cosmoV_setTop(state, 2);
|
||||
cosmoV_pushValue(state, val);
|
||||
return true;
|
||||
}
|
||||
|
||||
// yes, this would technically make it possible to set fields of types other than <string>. go crazy
|
||||
void cosmoV_set(CState *state)
|
||||
COSMO_API bool cosmoV_set(CState *state)
|
||||
{
|
||||
StkPtr obj = cosmoV_getTop(state, 2); // object was pushed first
|
||||
StkPtr key = cosmoV_getTop(state, 1); // then the key
|
||||
@@ -560,17 +586,21 @@ void cosmoV_set(CState *state)
|
||||
|
||||
if (!IS_REF(*obj)) {
|
||||
cosmoV_error(state, "Couldn't set field on type %s!", cosmoV_typeStr(*obj));
|
||||
return false;
|
||||
}
|
||||
|
||||
cosmoV_rawset(state, cosmoV_readRef(*obj), *key, *val);
|
||||
if (!cosmoV_rawset(state, cosmoV_readRef(*obj), *key, *val))
|
||||
return false;
|
||||
|
||||
// pop the obj, key & value
|
||||
cosmoV_setTop(state, 3);
|
||||
return true;
|
||||
}
|
||||
|
||||
void cosmoV_getMethod(CState *state, CObj *obj, CValue key, CValue *val)
|
||||
COSMO_API bool cosmoV_getMethod(CState *state, CObj *obj, CValue key, CValue *val)
|
||||
{
|
||||
cosmoV_rawget(state, obj, key, val);
|
||||
if (!cosmoV_rawget(state, obj, key, val))
|
||||
return false;
|
||||
|
||||
// if the result is callable, wrap it in an method
|
||||
if (IS_CALLABLE(*val)) {
|
||||
@@ -580,18 +610,6 @@ void cosmoV_getMethod(CState *state, CObj *obj, CValue key, CValue *val)
|
||||
cosmoV_pop(state); // pop the object
|
||||
*val = cosmoV_newRef(method);
|
||||
}
|
||||
}
|
||||
|
||||
bool cosmoV_isValueUserType(CState *state, CValue val, int userType)
|
||||
{
|
||||
if (!IS_OBJECT(val)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CObjObject *obj = cosmoV_readObject(val);
|
||||
if (obj->userT != userType) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -622,7 +640,7 @@ int _tbl__next(CState *state, int nargs, CValue *args)
|
||||
CObjTable *table = (CObjTable *)cosmoV_readRef(val);
|
||||
|
||||
// while the entry is invalid, go to the next entry
|
||||
int cap = cosmoT_getCapacity(&table->tbl);
|
||||
int cap = table->tbl.capacityMask + 1;
|
||||
CTableEntry *entry;
|
||||
do {
|
||||
entry = &table->tbl.table[index++];
|
||||
@@ -697,7 +715,7 @@ static inline uint16_t READUINT(CCallFrame *frame)
|
||||
# define SWITCH switch (READBYTE(frame))
|
||||
# define DEFAULT \
|
||||
default: \
|
||||
printf("[ERROR] unknown opcode!"); \
|
||||
CERROR("unknown opcode!"); \
|
||||
exit(0)
|
||||
#endif
|
||||
|
||||
@@ -707,7 +725,7 @@ int cosmoV_execute(CState *state)
|
||||
CCallFrame *frame = &state->callFrame[state->frameCount - 1]; // grabs the current frame
|
||||
CValue *constants = frame->closure->function->chunk.constants.values; // cache the pointer :)
|
||||
|
||||
for (;;) {
|
||||
while (!state->panic) {
|
||||
#ifdef VM_DEBUG
|
||||
cosmoV_printStack(state);
|
||||
disasmInstr(&frame->closure->function->chunk,
|
||||
@@ -746,6 +764,7 @@ int cosmoV_execute(CState *state)
|
||||
{
|
||||
uint8_t indx = READBYTE(frame);
|
||||
cosmoV_pushValue(state, frame->base[indx]);
|
||||
continue;
|
||||
}
|
||||
CASE(OP_GETUPVAL) :
|
||||
{
|
||||
@@ -791,7 +810,9 @@ int cosmoV_execute(CState *state)
|
||||
{
|
||||
uint8_t args = READBYTE(frame);
|
||||
uint8_t nres = READBYTE(frame);
|
||||
cosmoV_call(state, args, nres);
|
||||
if (!cosmoV_call(state, args, nres)) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
CASE(OP_CLOSURE) :
|
||||
{
|
||||
@@ -850,15 +871,18 @@ int cosmoV_execute(CState *state)
|
||||
// sanity check
|
||||
if (!IS_REF(*temp)) {
|
||||
cosmoV_error(state, "Couldn't index type %s!", cosmoV_typeStr(*temp));
|
||||
return -1;
|
||||
}
|
||||
|
||||
CObj *obj = cosmoV_readRef(*temp);
|
||||
CObjObject *proto = cosmoO_grabProto(obj);
|
||||
CValue val = cosmoV_newNil(); // to hold our value
|
||||
CValue val; // to hold our value
|
||||
|
||||
if (proto != NULL) {
|
||||
// check for __index metamethod
|
||||
cosmoO_indexObject(state, proto, *key, &val);
|
||||
if (!cosmoO_indexObject(state, proto, *key,
|
||||
&val)) // if returns false, cosmoV_error was called
|
||||
return -1;
|
||||
} else if (obj->type == COBJ_TABLE) {
|
||||
CObjTable *tbl = (CObjTable *)obj;
|
||||
|
||||
@@ -866,6 +890,7 @@ int cosmoV_execute(CState *state)
|
||||
} else {
|
||||
cosmoV_error(state, "No proto defined! Couldn't __index from type %s",
|
||||
cosmoV_typeStr(*temp));
|
||||
return -1;
|
||||
}
|
||||
|
||||
cosmoV_setTop(state, 2); // pops the table & the key
|
||||
@@ -880,13 +905,17 @@ int cosmoV_execute(CState *state)
|
||||
// sanity check
|
||||
if (!IS_REF(*temp)) {
|
||||
cosmoV_error(state, "Couldn't set index with type %s!", cosmoV_typeStr(*temp));
|
||||
return -1;
|
||||
}
|
||||
|
||||
CObj *obj = cosmoV_readRef(*temp);
|
||||
CObjObject *proto = cosmoO_grabProto(obj);
|
||||
|
||||
if (proto != NULL) {
|
||||
cosmoO_newIndexObject(state, proto, *key, *value);
|
||||
if (!cosmoO_newIndexObject(
|
||||
state, proto, *key,
|
||||
*value)) // if it returns false, cosmoV_error was called
|
||||
return -1;
|
||||
} else if (obj->type == COBJ_TABLE) {
|
||||
CObjTable *tbl = (CObjTable *)obj;
|
||||
CValue *newVal = cosmoT_insert(state, &tbl->tbl, *key);
|
||||
@@ -895,6 +924,7 @@ int cosmoV_execute(CState *state)
|
||||
} else {
|
||||
cosmoV_error(state, "No proto defined! Couldn't __newindex from type %s",
|
||||
cosmoV_typeStr(*temp));
|
||||
return -1;
|
||||
}
|
||||
|
||||
// pop everything off the stack
|
||||
@@ -913,11 +943,13 @@ int cosmoV_execute(CState *state)
|
||||
|
||||
// sanity check
|
||||
if (IS_REF(*temp)) {
|
||||
cosmoV_rawset(state, cosmoV_readRef(*temp), constants[ident], *value);
|
||||
if (!cosmoV_rawset(state, cosmoV_readRef(*temp), constants[ident], *value))
|
||||
return -1;
|
||||
} else {
|
||||
CObjString *field = cosmoV_toString(state, constants[ident]);
|
||||
cosmoV_error(state, "Couldn't set field '%s' on type %s!", field->str,
|
||||
cosmoV_typeStr(*temp));
|
||||
return -1;
|
||||
}
|
||||
|
||||
// pop everything off the stack
|
||||
@@ -925,17 +957,19 @@ int cosmoV_execute(CState *state)
|
||||
}
|
||||
CASE(OP_GETOBJECT) :
|
||||
{
|
||||
CValue val = cosmoV_newNil(); // to hold our value
|
||||
CValue val; // to hold our value
|
||||
StkPtr temp = cosmoV_getTop(state, 0); // that should be the object
|
||||
uint16_t ident = READUINT(frame); // use for the key
|
||||
|
||||
// sanity check
|
||||
if (IS_REF(*temp)) {
|
||||
cosmoV_rawget(state, cosmoV_readRef(*temp), constants[ident], &val);
|
||||
if (!cosmoV_rawget(state, cosmoV_readRef(*temp), constants[ident], &val))
|
||||
return -1;
|
||||
} else {
|
||||
CObjString *field = cosmoV_toString(state, constants[ident]);
|
||||
cosmoV_error(state, "Couldn't get field '%s' from type %s!", field->str,
|
||||
cosmoV_typeStr(*temp));
|
||||
return -1;
|
||||
}
|
||||
|
||||
cosmoV_setTop(state, 1); // pops the object
|
||||
@@ -943,18 +977,20 @@ int cosmoV_execute(CState *state)
|
||||
}
|
||||
CASE(OP_GETMETHOD) :
|
||||
{
|
||||
CValue val = cosmoV_newNil(); // to hold our value
|
||||
CValue val; // to hold our value
|
||||
StkPtr temp = cosmoV_getTop(state, 0); // that should be the object
|
||||
uint16_t ident = READUINT(frame); // use for the key
|
||||
|
||||
// this is almost identical to GETOBJECT, however cosmoV_getMethod is used instead
|
||||
// of just cosmoV_get
|
||||
if (IS_REF(*temp)) {
|
||||
cosmoV_getMethod(state, cosmoV_readRef(*temp), constants[ident], &val);
|
||||
if (!cosmoV_getMethod(state, cosmoV_readRef(*temp), constants[ident], &val))
|
||||
return -1;
|
||||
} else {
|
||||
CObjString *field = cosmoV_toString(state, constants[ident]);
|
||||
cosmoV_error(state, "Couldn't get field '%s' from type %s!", field->str,
|
||||
cosmoV_typeStr(*temp));
|
||||
return -1;
|
||||
}
|
||||
|
||||
cosmoV_setTop(state, 1); // pops the object
|
||||
@@ -971,12 +1007,14 @@ int cosmoV_execute(CState *state)
|
||||
// sanity check
|
||||
if (IS_REF(*temp)) {
|
||||
// get the field from the object
|
||||
cosmoV_rawget(state, cosmoV_readRef(*temp), constants[ident], &val);
|
||||
if (!cosmoV_rawget(state, cosmoV_readRef(*temp), constants[ident], &val))
|
||||
return -1;
|
||||
|
||||
// now invoke the method!
|
||||
invokeMethod(state, cosmoV_readRef(*temp), val, args, nres, 1);
|
||||
} else {
|
||||
cosmoV_error(state, "Couldn't get from type %s!", cosmoV_typeStr(*temp));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
CASE(OP_ITER) :
|
||||
@@ -986,6 +1024,7 @@ int cosmoV_execute(CState *state)
|
||||
if (!IS_REF(*temp)) {
|
||||
cosmoV_error(state, "Couldn't iterate over non-iterator type %s!",
|
||||
cosmoV_typeStr(*temp));
|
||||
return -1;
|
||||
}
|
||||
|
||||
CObj *obj = cosmoV_readRef(*temp);
|
||||
@@ -998,9 +1037,8 @@ int cosmoV_execute(CState *state)
|
||||
cosmoV_pop(state); // pop the object from the stack
|
||||
cosmoV_pushValue(state, val);
|
||||
cosmoV_pushRef(state, (CObj *)obj);
|
||||
cosmoV_call(
|
||||
state, 1,
|
||||
1); // we expect 1 return value on the stack, the iterable object
|
||||
if (!cosmoV_call(state, 1, 1)) // we expect 1 return value on the stack, the iterable object
|
||||
return -1;
|
||||
|
||||
StkPtr iObj = cosmoV_getTop(state, 0);
|
||||
|
||||
@@ -1009,6 +1047,7 @@ int cosmoV_execute(CState *state)
|
||||
"Expected iterable object! '__iter' returned %s, expected "
|
||||
"<object>!",
|
||||
cosmoV_typeStr(*iObj));
|
||||
return -1;
|
||||
}
|
||||
|
||||
// get __next method and place it at the top of the stack
|
||||
@@ -1016,6 +1055,7 @@ int cosmoV_execute(CState *state)
|
||||
cosmoV_newRef(state->iStrings[ISTRING_NEXT]), iObj);
|
||||
} else {
|
||||
cosmoV_error(state, "Expected iterable object! '__iter' not defined!");
|
||||
return -1;
|
||||
}
|
||||
} else if (obj->type == COBJ_TABLE) {
|
||||
CObjTable *tbl = (CObjTable *)obj;
|
||||
@@ -1040,6 +1080,7 @@ int cosmoV_execute(CState *state)
|
||||
} else {
|
||||
cosmoV_error(state, "No proto defined! Couldn't get from type %s",
|
||||
cosmoO_typeStr(obj));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
CASE(OP_NEXT) :
|
||||
@@ -1051,10 +1092,12 @@ int cosmoV_execute(CState *state)
|
||||
if (!IS_METHOD(*temp)) {
|
||||
cosmoV_error(state, "Expected '__next' to be a method, got type %s!",
|
||||
cosmoV_typeStr(*temp));
|
||||
return -1;
|
||||
}
|
||||
|
||||
cosmoV_pushValue(state, *temp);
|
||||
cosmoV_call(state, 0, nresults);
|
||||
if (!cosmoV_call(state, 0, nresults))
|
||||
return -1;
|
||||
|
||||
if (IS_NIL(*(cosmoV_getTop(
|
||||
state, 0)))) { // __next returned a nil, which means to exit the loop
|
||||
@@ -1093,6 +1136,7 @@ int cosmoV_execute(CState *state)
|
||||
} else {
|
||||
cosmoV_error(state, "Expected numbers, got %s and %s!", cosmoV_typeStr(*valA),
|
||||
cosmoV_typeStr(*valB));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
CASE(OP_POW) :
|
||||
@@ -1106,6 +1150,7 @@ int cosmoV_execute(CState *state)
|
||||
} else {
|
||||
cosmoV_error(state, "Expected numbers, got %s and %s!", cosmoV_typeStr(*valA),
|
||||
cosmoV_typeStr(*valB));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
CASE(OP_NOT) :
|
||||
@@ -1121,6 +1166,7 @@ int cosmoV_execute(CState *state)
|
||||
cosmoV_pushNumber(state, -(cosmoV_readNumber(*val)));
|
||||
} else {
|
||||
cosmoV_error(state, "Expected number, got %s!", cosmoV_typeStr(*val));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
CASE(OP_COUNT) :
|
||||
@@ -1129,6 +1175,7 @@ int cosmoV_execute(CState *state)
|
||||
|
||||
if (!IS_REF(*temp)) {
|
||||
cosmoV_error(state, "Expected non-primitive, got %s!", cosmoV_typeStr(*temp));
|
||||
return -1;
|
||||
}
|
||||
|
||||
int count = cosmoO_count(state, cosmoV_readRef(*temp));
|
||||
@@ -1153,6 +1200,7 @@ int cosmoV_execute(CState *state)
|
||||
*val = cosmoV_newNumber(cosmoV_readNumber(*val) + inc);
|
||||
} else {
|
||||
cosmoV_error(state, "Expected number, got %s!", cosmoV_typeStr(*val));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
CASE(OP_INCGLOBAL) :
|
||||
@@ -1168,6 +1216,7 @@ int cosmoV_execute(CState *state)
|
||||
*val = cosmoV_newNumber(cosmoV_readNumber(*val) + inc);
|
||||
} else {
|
||||
cosmoV_error(state, "Expected number, got %s!", cosmoV_typeStr(*val));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
CASE(OP_INCUPVAL) :
|
||||
@@ -1182,6 +1231,7 @@ int cosmoV_execute(CState *state)
|
||||
*val = cosmoV_newNumber(cosmoV_readNumber(*val) + inc);
|
||||
} else {
|
||||
cosmoV_error(state, "Expected number, got %s!", cosmoV_typeStr(*val));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
CASE(OP_INCINDEX) :
|
||||
@@ -1193,6 +1243,7 @@ int cosmoV_execute(CState *state)
|
||||
if (!IS_REF(*temp)) {
|
||||
cosmoV_error(state, "Couldn't index non-indexable type %s!",
|
||||
cosmoV_typeStr(*temp));
|
||||
return -1;
|
||||
}
|
||||
|
||||
CObj *obj = cosmoV_readRef(*temp);
|
||||
@@ -1201,23 +1252,27 @@ int cosmoV_execute(CState *state)
|
||||
|
||||
// call __index if the proto was found
|
||||
if (proto != NULL) {
|
||||
cosmoO_indexObject(state, proto, *key, &val);
|
||||
if (cosmoO_indexObject(state, proto, *key, &val)) {
|
||||
if (!IS_NUMBER(val)) {
|
||||
cosmoV_error(state, "Expected number, got %s!", cosmoV_typeStr(val));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!IS_NUMBER(val)) {
|
||||
cosmoV_error(state, "Expected number, got %s!", cosmoV_typeStr(val));
|
||||
}
|
||||
cosmoV_pushValue(state, val); // pushes old value onto the stack :)
|
||||
|
||||
cosmoV_pushValue(state, val); // pushes old value onto the stack :)
|
||||
|
||||
// call __newindex
|
||||
cosmoO_newIndexObject(state, proto, *key,
|
||||
cosmoV_newNumber(cosmoV_readNumber(val) + inc));
|
||||
// call __newindex
|
||||
if (!cosmoO_newIndexObject(state, proto, *key,
|
||||
cosmoV_newNumber(cosmoV_readNumber(val) + inc)))
|
||||
return -1;
|
||||
} else
|
||||
return -1; // cosmoO_indexObject failed and threw an error
|
||||
} else if (obj->type == COBJ_TABLE) {
|
||||
CObjTable *tbl = (CObjTable *)obj;
|
||||
CValue *val = cosmoT_insert(state, &tbl->tbl, *key);
|
||||
|
||||
if (!IS_NUMBER(*val)) {
|
||||
cosmoV_error(state, "Expected number, got %s!", cosmoV_typeStr(*val));
|
||||
return -1;
|
||||
}
|
||||
|
||||
// pops tbl & key from stack
|
||||
@@ -1227,6 +1282,7 @@ int cosmoV_execute(CState *state)
|
||||
} else {
|
||||
cosmoV_error(state, "No proto defined! Couldn't __index from type %s",
|
||||
cosmoV_typeStr(*temp));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
CASE(OP_INCOBJECT) :
|
||||
@@ -1241,7 +1297,8 @@ int cosmoV_execute(CState *state)
|
||||
CObj *obj = cosmoV_readRef(*temp);
|
||||
CValue val;
|
||||
|
||||
cosmoV_rawget(state, obj, ident, &val);
|
||||
if (!cosmoV_rawget(state, obj, ident, &val))
|
||||
return -1;
|
||||
|
||||
// pop the object off the stack
|
||||
cosmoV_pop(state);
|
||||
@@ -1249,13 +1306,16 @@ int cosmoV_execute(CState *state)
|
||||
// check that it's a number value
|
||||
if (IS_NUMBER(val)) {
|
||||
cosmoV_pushValue(state, val); // pushes old value onto the stack :)
|
||||
cosmoV_rawset(state, obj, ident,
|
||||
cosmoV_newNumber(cosmoV_readNumber(val) + inc));
|
||||
if (!cosmoV_rawset(state, obj, ident,
|
||||
cosmoV_newNumber(cosmoV_readNumber(val) + inc)))
|
||||
return -1;
|
||||
} else {
|
||||
cosmoV_error(state, "Expected number, got %s!", cosmoV_typeStr(val));
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
cosmoV_error(state, "Couldn't set a field on type %s!", cosmoV_typeStr(*temp));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
CASE(OP_EQUAL) :
|
||||
@@ -1295,6 +1355,7 @@ int cosmoV_execute(CState *state)
|
||||
}
|
||||
}
|
||||
|
||||
// we'll only reach this if state->panic is true
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
44
src/cvm.h
44
src/cvm.h
@@ -13,19 +13,16 @@
|
||||
cosmoV_execute by about 20% from benchmarking. of course, if you know
|
||||
your compiler supports computed gotos, you can define VM_JUMPTABLE
|
||||
|
||||
although, this is disabled when VM_DEBUG is defined, since it can cause
|
||||
issues with debugging
|
||||
|
||||
BTW: be weary of maliciously crafted cosmo dumps!! it's very easy to crash
|
||||
cosmo with this enabled and reading invalid opcodes due to us just using the
|
||||
opcode as an index into the jump table
|
||||
*/
|
||||
#if (defined(__GNUC__) || defined(__clang__)) && !defined(VM_DEBUG)
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
# define VM_JUMPTABLE
|
||||
#endif
|
||||
|
||||
// args = # of pass parameters, nresults = # of expected results
|
||||
COSMO_API void cosmoV_call(CState *state, int args, int nresults);
|
||||
COSMO_API bool cosmoV_call(CState *state, int args, int nresults);
|
||||
COSMO_API bool cosmoV_pcall(CState *state, int args, int nresults);
|
||||
|
||||
// pushes new object onto the stack & returns a pointer to the new object
|
||||
@@ -33,14 +30,14 @@ COSMO_API CObjObject *cosmoV_makeObject(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_pushFString(CState *state, const char *format, ...);
|
||||
COSMO_API void cosmoV_printBacktrace(CState *state, CObjError *err);
|
||||
COSMO_API void cosmoV_throw(CState *state);
|
||||
COSMO_API void cosmoV_printError(CState *state, CObjError *err);
|
||||
COSMO_API CObjError *cosmoV_throw(CState *state);
|
||||
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
|
||||
updates protos for the passed objType if that CObj* has no proto.
|
||||
updates protos for the passed objType if that CObj* has no proto.
|
||||
|
||||
returns true if replacing a previously registered proto object for this type
|
||||
*/
|
||||
@@ -67,21 +64,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);
|
||||
|
||||
/*
|
||||
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 &
|
||||
value
|
||||
expects object to be pushed, then the key, and finally the new value.
|
||||
|
||||
returns false if an error was thrown, returns true if the value was set and the object key, and
|
||||
value were popped
|
||||
*/
|
||||
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
|
||||
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
|
||||
COSMO_API bool cosmoV_isValueUserType(CState *state, CValue val, int userType);
|
||||
// clears the stack, callstack and restores the state into a usable state after a calloverflow or
|
||||
// another hard to recover error (keeps the global table intact)
|
||||
COSMO_API bool cosmoV_restore(CState *state);
|
||||
|
||||
// nice to have wrappers
|
||||
|
||||
@@ -94,6 +97,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
|
||||
if (stackSize >= STACK_MAX - 8) {
|
||||
if (state->panic) { // we're in a panic state, let the 8 reserved slots be filled
|
||||
if (stackSize < STACK_MAX)
|
||||
*(state->top++) = val;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
cosmoV_error(state, "Stack overflow!");
|
||||
return;
|
||||
}
|
||||
|
@@ -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(¤t) == 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;
|
||||
}
|
2786
util/linenoise.c
2786
util/linenoise.c
File diff suppressed because it is too large
Load Diff
152
util/linenoise.h
152
util/linenoise.h
@@ -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 */
|
Reference in New Issue
Block a user