Compare commits

..

116 Commits

Author SHA1 Message Date
5711ca218e improved readme, added asciinema demo 2023-12-29 18:18:22 -06:00
5cd3049d66 cdebug: added cosmoG_disassemble
for now cosmo will disassemble each script before running
2023-12-29 18:17:52 -06:00
cd37cfdae5 cparse: removed stale expressionStatement forward declare 2023-12-29 18:01:33 -06:00
e0455902b0 'return' is now a valid statement outside of a function
lets us do some cool things later. also the repl can print returned values. eg.

`return "hello world"` will print `<string> "hello world"` to the console
2023-12-28 23:41:10 -06:00
43d79a456e cparse.c: removed expressionStatement() 2023-12-28 23:27:08 -06:00
105a3d70c3 cparse.c: refactoring
- remove declaration(), replace calls with statement()
- fix possible UB in parsePrecedence; no idea how this *wasn't* causing a segfault tbh
2023-12-28 23:20:19 -06:00
93f3ae1106 cvm.c:cosmoV_printError -> cosmoV_printBacktrace 2023-12-28 22:56:57 -06:00
4816e64612 cvalue.c:printValue renamed to cosmoV_printValue 2023-12-28 22:52:20 -06:00
0df56bd42a ctable: hash COBJ_CFUNCTION based on cfunc and not the actual <obj> 2023-12-28 19:56:25 -06:00
e7b2d7d833 cbaselib.c:fileB_read() fix MSVC warnings 2023-12-27 21:12:12 -06:00
39060a67e9 fix cosmoB_loadObjLib()
- wrong # of pairs passed to cosmoV_makeTable
- minor refactoring of cmem.c:sweep()
2023-12-26 12:27:45 -06:00
5296495e47 formatting fixes 2023-12-26 10:59:42 -06:00
a337e26229 added <obj>:keys() method
this method returns a table of keys or 'fields' available in the object
2023-12-25 19:08:04 -06:00
b7bb0773b1 removed stale WRITE_GLOBAL_OP macro 2023-12-25 19:08:04 -06:00
7f5e3ae8dc lol oops 2023-11-03 22:59:31 -05:00
7a54230cb9 hated the styling of this
consistency is nice to have
2023-11-03 22:36:59 -05:00
1a7d6caec6 fix __proto object getter 2023-09-11 22:17:35 -05:00
1678194941 minor refactoring 2023-09-06 20:23:18 -05:00
3ea653b26d added file:write() & an optional mode param to os.open() 2023-09-06 17:29:38 -05:00
d3de4c0e66 removed more debug prints 2023-09-05 14:45:03 -05:00
d66d4807b3 removed debug prints, oops 2023-09-05 14:43:50 -05:00
1fcb35168f fix makefile 2023-09-05 14:42:29 -05:00
611162b3be documented the new os.open and file objects 2023-09-05 14:41:59 -05:00
b3587f48a2 added os.open(); new file object
eg.
```lua
local err, file = os.open("LICENSE.md")
if err then
    // do error handling stuff
end

// passing "a" to read() will read the whole file
print(file:read("a"))
```
2023-09-05 14:38:24 -05:00
bf36412699 removed 'roots', replaced with a registry table
- removed cosmoM_addRoot
- removed cosmoM_removeRoot
- renamed cosmoV_register to cosmoV_addGlobals
- added cosmoV_addRegistry
- added cosmoV_getRegistry
- added cosmoV_setProto
- added cosmoV_isValueUserType
2023-09-05 14:35:29 -05:00
6701a63a63 capture freezeGC in CPanic 2023-09-05 02:23:31 -05:00
ffff01e9d1 build a release build for linux as well 2023-09-04 20:15:15 -05:00
89be01aaf6 even more error handling refactoring
removing all of these useless checks has actually made cosmoV_execute just a
lil bit faster :)
2023-09-04 20:14:53 -05:00
cc9eb4a5ec lol oops 2023-09-01 17:16:10 -05:00
789c5210b4 switched to a better linenoise fork
https://github.com/msteveb/linenoise

this version has several benefits, namely win32 support :D
2023-09-01 17:12:39 -05:00
dfdd97e739 fix artifacts path 2023-09-01 14:43:20 -05:00
096d80d8df better repl input, using linenoise 2023-08-31 23:17:13 -05:00
f7bc8e0471 include util path 2023-08-31 23:16:28 -05:00
fce568addc whoops, wrong path to the workflow file 2023-08-30 21:30:20 -05:00
f5e75f09b9 ig that runner doesn't work lol 2023-08-30 21:28:50 -05:00
fe136f84b5 removed CERROR 2023-08-30 20:14:03 -05:00
de8cd481c3 allow manual runs 2023-08-30 20:12:06 -05:00
6654c3b91c wrong output path for windows build 2023-08-30 20:09:06 -05:00
21f7ea5c14 oops 2023-08-30 20:03:30 -05:00
e1591ae3fd switched from appveyor to github workflow 2023-08-30 20:02:04 -05:00
bfdd33e01d fix vm.collect()
we don't freeze the vm on entry to C functions now
2023-08-26 15:03:56 -05:00
c0893b8a14 more refactoring; things seem to work fine
all example scripts run fine with GC_STRESS enabled
2023-08-30 12:00:52 -05:00
d30bcace9a don't freezeGC during GC cycle 2023-08-29 23:32:25 -05:00
6a47c82179 fix more GC bugs 2023-08-29 23:21:52 -05:00
d41126e75f fix cparse.c gc bug 2023-08-29 23:01:47 -05:00
9f19fd4f31 fix this test script 2023-08-29 16:51:04 -05:00
6126b50941 cosmoV_throw() now resets the vm stack as well
also a minor GC bug in cosmoO_newError was fixed.

i'm going to try to phase out cosmoM_freezeGC & friends
since that would cause hell with this new
error handling solution. the only thing still using it is the GC.
2023-08-29 16:48:38 -05:00
7fa7eb8d94 fixed minor memory leak in cparse
we keep track of internal values used by the parser by pushing them onto the stack
and popping them off once complete.
2023-08-29 15:27:22 -05:00
0633e87aa6 WIP: removed stale error handling
currently, scripts seem to run fine. however I'm a bit worried about stack related issues. maybe i'll need to reset state->top as well? but not entirely sure
2023-08-29 14:07:45 -05:00
75d27afe2c WIP: major error handling refactoring
switching to setjmp instead of the really bad global 'panic' flag
2023-08-28 21:13:00 -05:00
3f39211081 minor refactoring 2023-08-25 23:34:21 -05:00
c5e4305ef8 refactored cobj.c:printObject()
uses obj type strings from cosmoO_typeStr
2023-08-25 21:28:41 -05:00
1a78a9ab5f finally fixed this memory bug
we were accidentally tracking frees of stuff that was never
allocated lol
2023-08-25 21:22:10 -05:00
2f0f675159 more debug related refactoring 2023-08-25 20:44:24 -05:00
7c5d2f6b65 minor CTable refactoring 2023-08-25 19:57:16 -05:00
f76f2ffa92 refactoring and cleanup
cosmoB_loadOSLib -> cosmoB_loadOS
2023-08-24 23:36:32 -05:00
155e0829fb minor refactoring 2023-06-03 01:39:35 -05:00
2b3825d258 implemented jump table dispatch
- currently only enabled on supported platforms (GNU C Compiler + Clang)
- when enabled, sped up examples/fibtest.cosmo by about 20% (avg of 11.179s prior and 8.799 after)

NOTE: malicious dumps can trivially cause crashes now by having junk function chunks
2023-06-03 01:17:28 -05:00
7bca6927a9 fixed formatting 2023-06-01 22:28:07 -05:00
d3647c161b added vm.disassemble() 2023-06-01 19:04:12 -05:00
d27d94975e fixed MSVC support 2023-06-01 22:22:44 -05:00
2d0e63f706 forgot to update this a while back 2023-05-28 21:16:30 -05:00
dfcf0c92b5 whoops, need to update the command to run the testsuite 2023-05-28 21:13:51 -05:00
447f874eff update README.md 2023-05-28 21:11:52 -05:00
7b1bd1c9fc minor usage fix 2023-05-28 20:57:53 -05:00
9537a2c7b8 major main.c refactoring
- now takes command line arguments:
Usage: ./bin/cosmo [-clsr] [args]

available options are:
-c <in> <out>   compile <in> and dump to <out>
-l <in>         load dump from <in>
-s <in...>              compile and run <in...> script(s)
-r                      start the repl
2023-05-28 20:52:12 -05:00
c44dc88972 minor stack fixes 2023-05-28 12:19:52 -05:00
d581e68166 proper error handling for dump edgecases 2023-05-28 12:16:00 -05:00
2271681cec undef macros 2023-05-28 12:03:49 -05:00
cf18bbbe54 fixed GC bug in cundump.c 2023-05-28 11:55:48 -05:00
3a872fb83f these values can be defined at the top of the function 2023-05-28 00:08:28 -05:00
4ed1c79b50 formatting changes 2023-05-28 00:03:50 -05:00
bc6eb9b6dc added cdump.[ch] and cundump.[ch] to makefile 2023-05-27 23:17:17 -05:00
635f31863f cvm.c: added cosmoV_undump 2023-05-27 23:16:47 -05:00
49a7f68239 cdump.c: major refactoring; various fixes 2023-05-27 23:15:58 -05:00
8efecf71a4 cdump.c: fix instruction and line info dumping 2023-05-27 23:15:12 -05:00
395f352c6e started cundump.c:cosmoD_undump(); minor fixes 2023-05-25 21:12:21 -05:00
65d37838cd started cdump.c:cosmoD_dump() 2023-05-25 19:41:13 -05:00
3b13ae1624 minor refactoring and typos 2023-05-25 19:40:15 -05:00
d1a16d990c removed stale COBJ_STREAM usage 2023-05-25 18:34:39 -05:00
0a4d36f2f4 update Docs to reflect keyword changes 2023-02-10 20:46:05 -06:00
8ac8085d20 syntax: 'var'->'let' 'function'->'func'
- 'var' has some weird scoping connotations with users of JS. better to change it to 'let', which will decide whether to make the variable a local or a global
- 'func' looks visually appealing lol
- some minor refactoring done in cparse.c
2023-02-09 15:58:25 -06:00
e335fd95d6 minor formatting fixes 2023-02-09 12:52:36 -06:00
b902ac90de removed CObjStream 2023-02-09 12:42:09 -06:00
6056f8eb5b added clang-format 2023-02-09 12:32:48 -06:00
88284a0b6e Removed '-Werror' to the Makefile 2021-06-11 15:01:53 -05:00
Inversion
7998c2ab41 Add documentation for the OS library 2023-11-03 22:55:24 -05:00
7b5825668d Added boilerplate for CObjStream 2021-03-20 01:44:03 -05:00
d13cc398c8 Added os.system() to the os.* library 2021-03-20 01:02:13 -05:00
012d3702bf Updated Appveyor to test testsuite 2021-03-19 22:25:23 -05:00
d761970f17 Added minimal testsuite for IC
- main.c will now report errors for passed scripts
2021-03-19 22:23:04 -05:00
0e730b9c51 Added svg to README, whitelisted commits only effecting the binary 2021-03-16 15:05:20 -05:00
bff2799bb6 Added AppVeyor CI 2021-03-16 14:54:44 -05:00
07ca82f968 Added '__equal' metamethod docs 2021-02-24 12:52:31 -06:00
b545e8e5e3 Added another example script 'compare.comso'
- This stresses the table implementation as well as cosmoV_equals
2021-02-23 11:50:41 -06:00
55e6453589 Improved cosmoO_equals performance for strings 2021-02-23 11:49:57 -06:00
c83dca2ab2 Added '__equal' metamethod, slightly refactored cosmoO_equal
- ISTRING_EQUAL has been added
2021-02-20 12:42:13 -06:00
3890c9dd1e Refactored cosmoO_equals
This sets up room for the '__equal' metamethod to be added

- cosmoO_equals now requires the state to be passed
- cosmoV_equals now requires the state to be passed
- cosmoT_get now requires the state to be passed
2021-02-19 17:04:23 -06:00
40739e9bea cparse.c now throws an error on illegal statements 2021-02-19 12:47:12 -06:00
Inversion
1eec23035f Temporary fix for possible bug 2023-11-03 22:55:19 -05:00
Inversion
c0274d1d77 Update standard library documentation 2023-11-03 22:55:14 -05:00
Inversion
fec26ac380 Add optional custom error message to assert 2023-11-03 22:55:09 -05:00
35466f691f Added C99 support, refactored headers 2021-02-15 16:20:04 -06:00
71c8dc7e34 Added basic standard library docs 2021-02-15 14:25:08 -06:00
7a6e00be41 Added math.rad() and math.deg() 2021-02-15 14:06:43 -06:00
14b091b691 Added trig. functions to the math library 2021-02-15 14:00:26 -06:00
5c71efbe40 Added OP_POW to cdebug.c; refactored cosmoV_registerProtoObject
- cosmoV_registerProtoObject now walks the object list and updates the proto's for objects of the objType which have a NULL proto.
- misc. comment changes in cvm.h as well.
2021-02-13 20:08:35 -06:00
1fff6c7fe9 Added string.rep() to the string library 2021-02-13 19:07:47 -06:00
1a96e411f2 Minor Makefile fix 2021-02-11 20:35:42 -06:00
fdd0d19308 Added CMake support
should make integration into visual studio easier for people
2021-02-11 20:34:04 -06:00
33da88a18a Minor cbaselib.c cleanup 2021-02-11 00:58:12 -06:00
Inversion
50b19e9f4f Added argument type check to string.len 2023-11-03 22:55:03 -05:00
Inversion
472a0ea4c1 Updated baselib in accordance with cosmoV_readCString 2023-11-03 22:54:56 -05:00
Inversion
76574c7860 Added cosmoV_readCString for convenience 2023-11-03 22:54:51 -05:00
Inversion
8b931fa4a7 Add string.len to base library 2023-11-03 22:54:45 -05:00
Inversion
ce844dc110 Added error to the base library 2023-11-03 22:53:59 -05:00
26 changed files with 3508 additions and 1461 deletions

View File

@@ -19,9 +19,9 @@ jobs:
with: with:
submodules: recursive submodules: recursive
- name: CMake - name: CMake
run: cmake -B build run: cmake -B build -DCMAKE_BUILD_TYPE=Release
- name: Build - name: Build
run: cmake --build build run: cmake --build build --config Release
- name: Upload build artifact - name: Upload build artifact
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v2
with: with:

View File

@@ -1,7 +1,7 @@
# make clean && make && ./bin/cosmo # make clean && make && ./bin/cosmo
CC=clang CC=clang
CFLAGS=-fPIE -Wall -Isrc -O3 -std=c99 #-g -fsanitize=address CFLAGS=-fPIE -Wall -Isrc -O3 #-g -fsanitize=address
LDFLAGS=-lm #-fsanitize=address LDFLAGS=-lm #-fsanitize=address
OUT=bin/cosmo OUT=bin/cosmo

View File

@@ -1,7 +1,4 @@
# Cosmo # Cosmo
[![Check Build](https://github.com/CPunch/Cosmo/actions/workflows/check_build.yaml/badge.svg?branch=main)](https://github.com/CPunch/Cosmo/actions/workflows/check_build.yaml)
## Usage
``` ```
Usage: ./bin/cosmo [-clsr] [args] Usage: ./bin/cosmo [-clsr] [args]
@@ -13,6 +10,13 @@ available options are:
-r start the repl -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'? ## What is a 'cosmo'?
Cosmo is a portable scripting language loosely based off of Lua. Cosmo easily allows the user to extend the language through the use of Proto objects, which describe the behavior of Objects. For example the following is a simple Vector Proto which describes behavior for a Vector-like object. Cosmo is a portable scripting language loosely based off of Lua. Cosmo easily allows the user to extend the language through the use of Proto objects, which describe the behavior of Objects. For example the following is a simple Vector Proto which describes behavior for a Vector-like object.

View File

@@ -59,7 +59,14 @@ Includes functions that interact with the operating system.
| Name | Type | Behavior | Example | | Name | Type | Behavior | Example |
| ------------ | ------------------------------------------------ | ------------------------------------------------------------------------ | ------------------------ | | ------------ | ------------------------------------------------ | ------------------------------------------------------------------------ | ------------------------ |
| 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.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.time | `()` -> `<number>` | Returns the system time in Epoch format | `os.time()` -> `1.61691e+09` | | os.time | `()` -> `<number>` | Returns the system time in Epoch format | `os.time()` -> `1.61691e+09` |
| os.system | `(cmd<string>)` -> `<number>` | Runs a system command as if it were a terminal and returns the exit code | `os.system("mkdir test")` -> `0` | | os.system | `(cmd<string>)` -> `<number>` | Runs a system command as if it were a terminal and returns the exit code | `os.system("mkdir test")` -> `0` |
> -> means 'returns' > -> means 'returns'
File objects have the following methods:
| Name | Type | Behavior | Example |
| ------------ | ------------------------------------------------ | ------------------------------------------------------------------------ | ------------------------ |
| file:read | `(amt<number> or "a")` -> `<string>` | Reads `amt` bytes from the file and returns them as a string. If `"a"` is passed, the entire file is read. | `file:read("a")` -> `"Hello world!"` |
| file:write | `(data<string>)` -> `<nil>` | Writes `data` to file. | `file:write("hello world!")` -> `<nil>` |
> -> means 'returns'

6
examples/reader.cosmo Normal file
View File

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

6
examples/writer.cosmo Normal file
View File

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

31
main.c
View File

@@ -50,13 +50,24 @@ static bool interpret(CState *state, const char *script, const char *mod)
// cosmoV_compileString pushes the result onto the stack (COBJ_ERROR or COBJ_CLOSURE) // cosmoV_compileString pushes the result onto the stack (COBJ_ERROR or COBJ_CLOSURE)
if (cosmoV_compileString(state, script, mod)) { if (cosmoV_compileString(state, script, mod)) {
// 0 args being passed, 0 results expected cosmoG_disassemble(cosmoV_readClosure(*cosmoV_getTop(state, 0)));
if (!cosmoV_pcall(state, 0, 0)) { if (!cosmoV_pcall(state, 0, 1)) {
cosmoV_printError(state, cosmoV_readError(*cosmoV_pop(state))); cosmoV_printBacktrace(state, cosmoV_readError(*cosmoV_pop(state)));
return false; 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);
} else { } else {
cosmoV_printError(state, cosmoV_readError(*cosmoV_pop(state))); cosmoV_printBacktrace(state, cosmoV_readError(*cosmoV_pop(state)));
return false; return false;
} }
@@ -75,7 +86,7 @@ static void repl(CState *state)
cosmoV_pushString(state, "input"); cosmoV_pushString(state, "input");
cosmoV_pushCFunction(state, cosmoB_input); cosmoV_pushCFunction(state, cosmoB_input);
cosmoV_register(state, 2); cosmoV_addGlobals(state, 2);
while (_ACTIVE) { while (_ACTIVE) {
if (!(line = linenoise("> "))) { // better than gets() if (!(line = linenoise("> "))) { // better than gets()
@@ -84,7 +95,7 @@ static void repl(CState *state)
linenoiseHistoryAdd(line); linenoiseHistoryAdd(line);
interpret(state, line, "REPL"); interpret(state, line, "REPL");
linenoiseFree(line); free(line);
} }
} }
@@ -130,7 +141,7 @@ static bool runFile(CState *state, const char *fileName)
cosmoV_pushString(state, "input"); cosmoV_pushString(state, "input");
cosmoV_pushCFunction(state, cosmoB_input); cosmoV_pushCFunction(state, cosmoB_input);
cosmoV_register(state, 1); cosmoV_addGlobals(state, 1);
ret = interpret(state, script, fileName); ret = interpret(state, script, fileName);
@@ -158,7 +169,7 @@ void compileScript(CState *state, const char *in, const char *out)
CObjFunction *func = cosmoV_readClosure(*cosmoV_getTop(state, 0))->function; CObjFunction *func = cosmoV_readClosure(*cosmoV_getTop(state, 0))->function;
cosmoD_dump(state, func, fileWriter, (void *)fout); cosmoD_dump(state, func, fileWriter, (void *)fout);
} else { } else {
cosmoV_printError(state, cosmoV_readError(*cosmoV_pop(state))); cosmoV_printBacktrace(state, cosmoV_readError(*cosmoV_pop(state)));
} }
free(script); free(script);
@@ -171,13 +182,13 @@ void loadScript(CState *state, const char *in)
{ {
FILE *file = fopen(in, "rb"); FILE *file = fopen(in, "rb");
if (!cosmoV_undump(state, fileReader, file)) { if (!cosmoV_undump(state, fileReader, file)) {
cosmoV_printError(state, cosmoV_readError(*cosmoV_pop(state))); cosmoV_printBacktrace(state, cosmoV_readError(*cosmoV_pop(state)));
return; return;
}; };
printf("[!] loaded %s!\n", in); printf("[!] loaded %s!\n", in);
if (!cosmoV_pcall(state, 0, 0)) if (!cosmoV_pcall(state, 0, 0))
cosmoV_printError(state, cosmoV_readError(*cosmoV_pop(state))); cosmoV_printBacktrace(state, cosmoV_readError(*cosmoV_pop(state)));
fclose(file); fclose(file);
} }

View File

@@ -18,7 +18,7 @@ int cosmoB_print(CState *state, int nargs, CValue *args)
CObjString *str = cosmoV_toString(state, args[i]); CObjString *str = cosmoV_toString(state, args[i]);
printf("%s", cosmoO_readCString(str)); printf("%s", cosmoO_readCString(str));
} else { // else, thats pretty expensive for primitives, just print the raw value } else { // else, thats pretty expensive for primitives, just print the raw value
printValue(args[i]); cosmoV_printValue(args[i]);
} }
} }
printf("\n"); printf("\n");
@@ -69,7 +69,7 @@ int cosmoB_pcall(CState *state, int nargs, CValue *args)
bool res = cosmoV_pcall(state, nargs - 1, 1); bool res = cosmoV_pcall(state, nargs - 1, 1);
// insert false before the result // insert false before the result
cosmo_insert(state, 0, cosmoV_newBoolean(res)); cosmoV_insert(state, 0, cosmoV_newBoolean(res));
return 2; return 2;
} }
@@ -105,7 +105,7 @@ int cosmoB_loadstring(CState *state, int nargs, CValue *args)
CObjString *str = cosmoV_readString(args[0]); CObjString *str = cosmoV_readString(args[0]);
bool res = cosmoV_compileString(state, str->str, ""); bool res = cosmoV_compileString(state, str->str, "");
cosmo_insert(state, 0, cosmoV_newBoolean(res)); cosmoV_insert(state, 0, cosmoV_newBoolean(res));
return 2; // <boolean>, <closure> or <error> return 2; // <boolean>, <closure> or <error>
} }
@@ -139,7 +139,7 @@ void cosmoB_loadLibrary(CState *state)
} }
// register all the pushed c functions and the strings as globals // register all the pushed c functions and the strings as globals
cosmoV_register(state, i); cosmoV_addGlobals(state, i);
// load other libraries // load other libraries
cosmoB_loadObjLib(state); cosmoB_loadObjLib(state);
@@ -168,8 +168,43 @@ int cosmoB_ogetProto(CState *state, int nargs, CValue *args)
if (nargs != 1) if (nargs != 1)
cosmoV_error(state, "Expected 1 argument, got %d!", nargs); cosmoV_error(state, "Expected 1 argument, got %d!", nargs);
cosmoV_pushRef(state, (CObj *)cosmoV_readObject(args[0])->_obj.proto); // just return the proto if (!IS_REF(args[0])) {
cosmoV_typeError(state, "__getter.__proto", "<object>", "%s", cosmoV_typeStr(args[0]));
}
CObj *proto = (CObj *)cosmoV_readRef(args[0])->proto;
if (proto == NULL) {
cosmoV_pushNil(state);
return 1;
}
cosmoV_pushRef(state, proto); // just return the proto
return 1; // 1 result
}
int cosmoB_ogetKeys(CState *state, int nargs, CValue *args)
{
if (nargs != 1)
cosmoV_error(state, "Expected 1 argument, got %d!", nargs);
if (!IS_OBJECT(args[0])) {
cosmoV_typeError(state, "object.__keys", "<object>", "%s", cosmoV_typeStr(args[0]));
}
// push keys
CObjObject *obj = cosmoV_readObject(args[0]);
int cap = cosmoT_getCapacity(&obj->tbl);
int indx = 0;
for (int i = 0; i < cap; i++) {
CTableEntry *entry = &obj->tbl.table[i];
if (IS_NIL(entry->key))
continue;
cosmoV_pushNumber(state, indx++);
cosmoV_pushValue(state, entry->key);
}
cosmoV_makeTable(state, indx);
return 1; // 1 result return 1; // 1 result
} }
@@ -194,9 +229,9 @@ int cosmoB_oisChild(CState *state, int nargs, CValue *args)
COSMO_API void cosmoB_loadObjLib(CState *state) COSMO_API void cosmoB_loadObjLib(CState *state)
{ {
const char *identifiers[] = {"ischild"}; const char *identifiers[] = {"ischild", "keys"};
CosmoCFunction objLib[] = {cosmoB_oisChild}; CosmoCFunction objLib[] = {cosmoB_oisChild, cosmoB_ogetKeys};
// make object library object // make object library object
cosmoV_pushString(state, "object"); cosmoV_pushString(state, "object");
@@ -204,7 +239,7 @@ COSMO_API void cosmoB_loadObjLib(CState *state)
// make __getter object for debug proto // make __getter object for debug proto
cosmoV_pushString(state, "__getter"); cosmoV_pushString(state, "__getter");
// key & value pair // key & value pairs
cosmoV_pushString(state, "__proto"); // key cosmoV_pushString(state, "__proto"); // key
cosmoV_pushCFunction(state, cosmoB_ogetProto); // value cosmoV_pushCFunction(state, cosmoB_ogetProto); // value
@@ -230,52 +265,176 @@ COSMO_API void cosmoB_loadObjLib(CState *state)
cosmoV_registerProtoObject(state, COBJ_OBJECT, obj); cosmoV_registerProtoObject(state, COBJ_OBJECT, obj);
// register "object" to the global table // register "object" to the global table
cosmoV_register(state, 1); cosmoV_addGlobals(state, 1);
} }
// ================================================================ [OS.*] // ================================================================ [OS.*]
// os.read() int fileB_read(CState *state, int nargs, CValue *args)
int cosmoB_osRead(CState *state, int nargs, CValue *args) {
if (nargs != 2) {
cosmoV_error(state, "file:read() expected 2 arguments, got %d!", nargs);
}
if (!cosmoV_isValueUserType(state, args[0], COSMO_USER_FILE)) {
cosmoV_typeError(state, "file:read()", "<file>, <number> or \"a\"", "%s, %s",
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
}
CObjObject *fileObj = cosmoV_readObject(args[0]);
FILE *file = cosmoO_getUserP(fileObj);
if (IS_NUMBER(args[1])) {
CValue temp;
char *buffer;
cosmo_Number length = cosmoV_readNumber(args[1]);
// make sure the length is within the bounds of the file
if (length < 0) {
cosmoV_error(state, "file:read() expected length to be >= 0, got %d!", length);
}
// allocate a buffer for the read data
buffer = cosmoM_xmalloc(state, (size_t)length + 1);
// read the data
fread(buffer, sizeof(char), (size_t)length, file);
buffer[(int)length] = '\0'; // write the NULL terminator
// push the read data
temp = cosmoV_newRef(cosmoO_takeString(state, buffer, (size_t)length));
cosmoV_pushValue(state, temp);
} else if (IS_STRING(args[1])) {
if (strcmp(cosmoV_readCString(args[1]), "a") == 0) {
CValue temp;
char *buffer;
long length;
// get the length of the file
fseek(file, 0, SEEK_END);
length = ftell(file);
fseek(file, 0, SEEK_SET);
// allocate a buffer for the read data
buffer = cosmoM_xmalloc(state, (size_t)length + 1);
// read the data
fread(buffer, sizeof(char), (size_t)length, file);
buffer[length] = '\0'; // write the NULL terminator
// push the read data
temp = cosmoV_newRef(cosmoO_takeString(state, buffer, (size_t)length));
cosmoV_pushValue(state, temp);
} else {
cosmoV_error(state, "file:read() expected \"a\" or <number>, got \"%s\"!",
cosmoV_readCString(args[1]));
}
} else {
cosmoV_typeError(state, "file:read()", "<file>, <number> or \"a\"", "%s, %s",
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
}
return 1;
}
int fileB_write(CState *state, int nargs, CValue *args)
{
CObjObject *fileObj;
CObjString *str;
FILE *file;
if (nargs != 2) {
cosmoV_error(state, "file:write() expected 2 arguments, got %d!", nargs);
}
if (!cosmoV_isValueUserType(state, args[0], COSMO_USER_FILE)) {
cosmoV_typeError(state, "file:write()", "<file>, <string>", "%s, %s",
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
}
if (!IS_STRING(args[1])) {
cosmoV_typeError(state, "file:write()", "<file>, <string>", "%s, %s",
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
}
fileObj = cosmoV_readObject(args[0]);
str = cosmoV_readString(args[1]);
file = (FILE *)cosmoO_getUserP(fileObj);
fwrite(str->str, sizeof(char), str->length, file);
return 0;
}
int fileB_gc(CState *state, int nargs, CValue *args)
{ {
if (nargs != 1) { if (nargs != 1) {
cosmoV_error(state, "os.read() expected 1 argument, got %d!", nargs); cosmoV_error(state, "file:read() expected 1 argument, got %d!", nargs);
} }
if (!IS_STRING(args[0])) { if (!cosmoV_isValueUserType(state, args[0], COSMO_USER_FILE)) {
cosmoV_typeError(state, "os.read()", "<string>", "%s", cosmoV_typeStr(args[0])); cosmoV_typeError(state, "file:__gc()", "<file>", "%s", cosmoV_typeStr(args[0]));
} }
CObjString *str = cosmoV_readString(args[0]); CObjObject *fileObj = cosmoV_readObject(args[0]);
FILE *file = cosmoO_getUserP(fileObj);
// open file fclose(file);
FILE *file = fopen(str->str, "rb"); return 0;
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) { if (file == NULL) {
// return nil, file doesn't exist cosmoV_pushBoolean(state, true);
return 0; cosmoV_pushFString(state, "Failed to open %s!", filePath);
return 2;
} }
// grab the size of the file cosmoV_pushBoolean(state, false);
fseek(file, 0L, SEEK_END); pushFileObj(state, file);
size = ftell(file); return 2;
rewind(file);
buf = cosmoM_xmalloc(state, size + 1); // +1 for the NULL terminator
bRead = fread(buf, sizeof(char), size, file); // read the file into the buffer
if (bRead < size) {
// an error occured! we don't need to really throw an error, returning a nil is good enough
return 0;
}
buf[bRead] = '\0'; // place the NULL terminator at the end of the buffer
// push the string to the stack to return
cosmoV_pushValue(state, cosmoV_newRef(cosmoO_takeString(state, buf, bRead)));
return 1;
} }
// os.time() // os.time()
@@ -309,9 +468,8 @@ int cosmoB_osSystem(CState *state, int nargs, CValue *args)
COSMO_API void cosmoB_loadOS(CState *state) COSMO_API void cosmoB_loadOS(CState *state)
{ {
const char *identifiers[] = {"read", "time", "system"}; const char *identifiers[] = {"open", "time", "system"};
CosmoCFunction osLib[] = {cosmoB_osOpen, cosmoB_osTime, cosmoB_osSystem};
CosmoCFunction osLib[] = {cosmoB_osRead, cosmoB_osTime, cosmoB_osSystem};
cosmoV_pushString(state, "os"); cosmoV_pushString(state, "os");
@@ -322,7 +480,30 @@ COSMO_API void cosmoB_loadOS(CState *state)
} }
cosmoV_makeObject(state, i); cosmoV_makeObject(state, i);
cosmoV_register(state, 1); // register the os.* object to the global table 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);
} }
// ================================================================ [STRING.*] // ================================================================ [STRING.*]
@@ -546,8 +727,9 @@ int cosmoB_sRep(CState *state, int nargs, CValue *args)
char *newStr = cosmoM_xmalloc(state, length + 1); // + 1 for the NULL terminator char *newStr = cosmoM_xmalloc(state, length + 1); // + 1 for the NULL terminator
// copy the string over the new buffer // copy the string over the new buffer
for (int i = 0; i < times; i++) for (int i = 0; i < times; i++) {
memcpy(&newStr[i * str->length], str->str, str->length); memcpy(&newStr[i * str->length], str->str, str->length);
}
// write the NULL terminator // write the NULL terminator
newStr[length] = '\0'; newStr[length] = '\0';
@@ -578,7 +760,7 @@ void cosmoB_loadStrLib(CState *state)
cosmoV_registerProtoObject(state, COBJ_STRING, obj); cosmoV_registerProtoObject(state, COBJ_STRING, obj);
// register "string" to the global table // register "string" to the global table
cosmoV_register(state, 1); cosmoV_addGlobals(state, 1);
} }
// ================================================================ [MATH] // ================================================================ [MATH]
@@ -773,7 +955,7 @@ void cosmoB_loadMathLib(CState *state)
// make the object and register it as a global to the state // make the object and register it as a global to the state
cosmoV_makeObject(state, i); cosmoV_makeObject(state, i);
cosmoV_register(state, 1); cosmoV_addGlobals(state, 1);
} }
// ================================================================ [VM.*] // ================================================================ [VM.*]
@@ -924,5 +1106,5 @@ void cosmoB_loadVM(CState *state)
cosmoV_makeObject(state, 5); // makes the vm object cosmoV_makeObject(state, 5); // makes the vm object
// register "vm" to the global table // register "vm" to the global table
cosmoV_register(state, 1); cosmoV_addGlobals(state, 1);
} }

View File

@@ -3,6 +3,13 @@
#include "cstate.h" #include "cstate.h"
enum
{
COSMO_USER_NONE, // CObjObject is not a userdata object
COSMO_USER_FILE, // CObjObject is a file object (see cosmoB_osOpen)
COSMO_USER_START // the first user type for user-defined userdata
};
/* loads all of the base library, including: /* loads all of the base library, including:
- base library ("print", "assert", "type", "pcall", "loadstring", etc.) - base library ("print", "assert", "type", "pcall", "loadstring", etc.)
- object library - object library

View File

@@ -5,8 +5,9 @@
void printIndent(int indent) void printIndent(int indent)
{ {
for (int i = 0; i < indent; i++) for (int i = 0; i < indent; i++) {
printf("\t"); printf("\t");
}
} }
static int simpleInstruction(const char *name, int offset) static int simpleInstruction(const char *name, int offset)
@@ -61,7 +62,7 @@ static int constInstruction(const char *name, CChunk *chunk, int offset)
printf("%-16s [%05d] - ", name, index); printf("%-16s [%05d] - ", name, index);
CValue val = chunk->constants.values[index]; CValue val = chunk->constants.values[index];
printValue(val); cosmoV_printValue(val);
return offset + 1 + (sizeof(uint16_t) / sizeof(INSTRUCTION)); // consume opcode + uint return offset + 1 + (sizeof(uint16_t) / sizeof(INSTRUCTION)); // consume opcode + uint
} }
@@ -127,7 +128,7 @@ int disasmInstr(CChunk *chunk, int offset, int indent)
CObjFunction *cobjFunc = (CObjFunction *)cosmoV_readRef(val); CObjFunction *cobjFunc = (CObjFunction *)cosmoV_readRef(val);
offset += 3; // we consumed the opcode + u16 offset += 3; // we consumed the opcode + u16
printValue(val); cosmoV_printValue(val);
printf("\n"); printf("\n");
// list the upvalues/locals that are captured // list the upvalues/locals that are captured
@@ -222,3 +223,8 @@ int disasmInstr(CChunk *chunk, int offset, int indent)
return 1; return 1;
} }
void cosmoG_disassemble(CObjClosure *closure)
{
disasmChunk(&closure->function->chunk, closure->function->name == NULL ? UNNAMEDCHUNK : closure->function->name->str, 0);
}

View File

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

View File

@@ -6,6 +6,7 @@
#include "cstate.h" #include "cstate.h"
#include "ctable.h" #include "ctable.h"
#include "cvalue.h" #include "cvalue.h"
#include "cvm.h"
// realloc wrapper // realloc wrapper
void *cosmoM_reallocate(CState *state, void *buf, size_t oldSize, size_t newSize) void *cosmoM_reallocate(CState *state, void *buf, size_t oldSize, size_t newSize)
@@ -164,8 +165,9 @@ static void blackenObject(CState *state, CObj *obj)
markValue(state, err->err); markValue(state, err->err);
// mark callframes // mark callframes
for (int i = 0; i < err->frameCount; i++) for (int i = 0; i < err->frameCount; i++) {
markObject(state, (CObj *)err->frames[i].closure); markObject(state, (CObj *)err->frames[i].closure);
}
break; break;
} }
@@ -229,11 +231,11 @@ static void traceGrays(CState *state)
static void sweep(CState *state) static void sweep(CState *state)
{ {
CObj *prev = NULL; CObj *prev = NULL, *object = state->objects;
CObj *object = state->objects;
while (object != NULL) { while (object != NULL) {
if (object->isMarked) { // skip over it if (object->isMarked) { // skip over it
object->isMarked = false; // rest to white object->isMarked = false; // reset to white
prev = object; prev = object;
object = object->next; object = object->next;
} else { // free it! } else { // free it!
@@ -246,22 +248,22 @@ static void sweep(CState *state)
prev->next = object; prev->next = object;
} }
// call __gc on the object
CObjObject *protoObject = cosmoO_grabProto(oldObj);
CValue res;
// use user-defined __gc
if (protoObject != NULL && cosmoO_getIString(state, protoObject, ISTRING_GC, &res)) {
cosmoV_pushValue(state, res);
cosmoV_pushRef(state, (CObj *)oldObj);
cosmoV_call(state, 1, 0);
}
cosmoO_free(state, oldObj); cosmoO_free(state, oldObj);
} }
} }
} }
static void 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) static void markRoots(CState *state)
{ {
// mark all values on the stack // mark all values on the stack
@@ -282,14 +284,15 @@ static void markRoots(CState *state)
markObject(state, (CObj *)state->globals); markObject(state, (CObj *)state->globals);
// mark all internal strings // mark all internal strings
for (int i = 0; i < ISTRING_MAX; i++) for (int i = 0; i < ISTRING_MAX; i++) {
markObject(state, (CObj *)state->iStrings[i]); markObject(state, (CObj *)state->iStrings[i]);
}
// mark the user defined roots markTable(state, &state->registry);
markUserRoots(state);
for (int i = 0; i < COBJ_MAX; i++) for (int i = 0; i < COBJ_MAX; i++) {
markObject(state, (CObj *)state->protoObjects[i]); markObject(state, (CObj *)state->protoObjects[i]);
}
traceGrays(state); traceGrays(state);
} }
@@ -323,44 +326,3 @@ COSMO_API void cosmoM_updateThreshhold(CState *state)
{ {
state->nextGC = state->allocatedBytes * HEAP_GROW_FACTOR; state->nextGC = state->allocatedBytes * HEAP_GROW_FACTOR;
} }
COSMO_API void cosmoM_addRoot(CState *state, CObj *newRoot)
{
// first, check and make sure this root doesn't already exist in the list
CObj *root = state->userRoots;
while (root != NULL) {
if (root == newRoot) // found in the list, abort
return;
root = root->nextRoot;
}
// adds root to userRoot linked list
newRoot->nextRoot = state->userRoots;
state->userRoots = newRoot;
}
COSMO_API void cosmoM_removeRoot(CState *state, CObj *oldRoot)
{
CObj *prev = NULL;
CObj *root = state->userRoots;
// traverse the userRoot linked list
while (root != NULL) {
if (root == oldRoot) { // found root in list
// remove from the linked list
if (prev == NULL) {
state->userRoots = root->nextRoot;
} else {
prev->nextRoot = root->nextRoot;
}
root->nextRoot = NULL;
break;
}
prev = root;
root = root->nextRoot;
}
}

View File

@@ -67,13 +67,6 @@ COSMO_API bool cosmoM_checkGarbage(CState *state,
COSMO_API void cosmoM_collectGarbage(CState *state); COSMO_API void cosmoM_collectGarbage(CState *state);
COSMO_API void cosmoM_updateThreshhold(CState *state); COSMO_API void cosmoM_updateThreshhold(CState *state);
// 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 // wrapper for cosmoM_reallocate so we can track our memory usage
static inline void *cosmoM_xmalloc(CState *state, size_t sz) static inline void *cosmoM_xmalloc(CState *state, size_t sz)
{ {

View File

@@ -15,8 +15,9 @@ uint32_t hashString(const char *str, size_t sz)
uint32_t hash = sz; uint32_t hash = sz;
size_t step = (sz >> 5) + 1; size_t step = (sz >> 5) + 1;
for (size_t i = sz; i >= step; i -= step) for (size_t i = sz; i >= step; i -= step) {
hash = ((hash << 5) + (hash >> 2)) + str[i - 1]; hash = ((hash << 5) + (hash >> 2)) + str[i - 1];
}
return hash; return hash;
} }
@@ -31,7 +32,6 @@ CObj *cosmoO_allocateBase(CState *state, size_t sz, CObjType type)
obj->next = state->objects; obj->next = state->objects;
state->objects = obj; state->objects = obj;
obj->nextRoot = NULL;
#ifdef GC_DEBUG #ifdef GC_DEBUG
printf("allocated %s %p\n", cosmoO_typeStr(obj), obj); printf("allocated %s %p\n", cosmoO_typeStr(obj), obj);
#endif #endif
@@ -51,9 +51,9 @@ void cosmoO_free(CState *state, CObj *obj)
break; break;
} }
case COBJ_OBJECT: { case COBJ_OBJECT: {
CObjObject *objTbl = (CObjObject *)obj; CObjObject *objObj = (CObjObject *)obj;
cosmoT_clearTable(state, &objTbl->tbl); cosmoT_clearTable(state, &objObj->tbl);
cosmoM_free(state, CObjObject, objTbl); cosmoM_free(state, CObjObject, objObj);
break; break;
} }
case COBJ_TABLE: { case COBJ_TABLE: {
@@ -237,8 +237,9 @@ CObjError *cosmoO_newError(CState *state, CValue err)
cerror->parserError = false; cerror->parserError = false;
// clone the call frame // clone the call frame
for (int i = 0; i < state->frameCount; i++) for (int i = 0; i < state->frameCount; i++) {
cerror->frames[i] = state->callFrame[i]; cerror->frames[i] = state->callFrame[i];
}
return cerror; return cerror;
} }
@@ -397,7 +398,7 @@ bool cosmoO_isDescendant(CObj *obj, CObjObject *proto)
} }
// returns false if error thrown // returns false if error thrown
bool cosmoO_getRawObject(CState *state, CObjObject *proto, CValue key, CValue *val, CObj *obj) void cosmoO_getRawObject(CState *state, CObjObject *proto, CValue key, CValue *val, CObj *obj)
{ {
if (!cosmoT_get(state, &proto->tbl, key, if (!cosmoT_get(state, &proto->tbl, key,
val)) { // if the field doesn't exist in the object, check the proto val)) { // if the field doesn't exist in the object, check the proto
@@ -407,18 +408,17 @@ bool cosmoO_getRawObject(CState *state, CObjObject *proto, CValue key, CValue *v
cosmoV_pushRef(state, (CObj *)obj); // push object cosmoV_pushRef(state, (CObj *)obj); // push object
cosmoV_call(state, 1, 1); // call the function with the 1 argument cosmoV_call(state, 1, 1); // call the function with the 1 argument
*val = *cosmoV_pop(state); // set value to the return value of __index *val = *cosmoV_pop(state); // set value to the return value of __index
return true; return;
} }
if (proto->_obj.proto != NULL && // maybe the field is defined in the proto?
cosmoO_getRawObject(state, proto->_obj.proto, key, val, obj)) if (proto->_obj.proto != NULL) {
return true; cosmoO_getRawObject(state, proto->_obj.proto, key, val, obj);
return;
}
*val = cosmoV_newNil(); *val = cosmoV_newNil();
return true; // no protoobject to check against / key not found
} }
return true;
} }
void cosmoO_setRawObject(CState *state, CObjObject *proto, CValue key, CValue val, CObj *obj) void cosmoO_setRawObject(CState *state, CObjObject *proto, CValue key, CValue val, CObj *obj)
@@ -519,7 +519,7 @@ bool cosmoO_getIString(CState *state, CObjObject *object, int flag, CValue *val)
return false; // obj->proto was false, the istring doesn't exist in this object chain return false; // obj->proto was false, the istring doesn't exist in this object chain
} }
bool cosmoO_indexObject(CState *state, CObjObject *object, CValue key, CValue *val) void cosmoO_indexObject(CState *state, CObjObject *object, CValue key, CValue *val)
{ {
if (cosmoO_getIString(state, object, ISTRING_INDEX, val)) { if (cosmoO_getIString(state, object, ISTRING_INDEX, val)) {
cosmoV_pushValue(state, *val); // push function cosmoV_pushValue(state, *val); // push function
@@ -527,15 +527,12 @@ bool cosmoO_indexObject(CState *state, CObjObject *object, CValue key, CValue *v
cosmoV_pushValue(state, key); // push key cosmoV_pushValue(state, key); // push key
cosmoV_call(state, 2, 1); // call the function with the 2 arguments cosmoV_call(state, 2, 1); // call the function with the 2 arguments
*val = *cosmoV_pop(state); // set value to the return value of __index *val = *cosmoV_pop(state); // set value to the return value of __index
return true; } else { // there's no __index function defined!
} else { // there's no __index function defined!
cosmoV_error(state, "Couldn't index object without __index function!"); cosmoV_error(state, "Couldn't index object without __index function!");
} }
return false;
} }
bool cosmoO_newIndexObject(CState *state, CObjObject *object, CValue key, CValue val) void cosmoO_newIndexObject(CState *state, CObjObject *object, CValue key, CValue val)
{ {
CValue ret; // return value for cosmoO_getIString CValue ret; // return value for cosmoO_getIString
@@ -545,12 +542,9 @@ bool cosmoO_newIndexObject(CState *state, CObjObject *object, CValue key, CValue
cosmoV_pushValue(state, key); // push key & value pair cosmoV_pushValue(state, key); // push key & value pair
cosmoV_pushValue(state, val); cosmoV_pushValue(state, val);
cosmoV_call(state, 3, 0); cosmoV_call(state, 3, 0);
return true;
} else { // there's no __newindex function defined } else { // there's no __newindex function defined
cosmoV_error(state, "Couldn't set index on object without __newindex function!"); cosmoV_error(state, "Couldn't set index on object without __newindex function!");
} }
return false;
} }
CObjString *cosmoO_toString(CState *state, CObj *obj) CObjString *cosmoO_toString(CState *state, CObj *obj)
@@ -721,13 +715,13 @@ void printObject(CObj *o)
case COBJ_ERROR: { case COBJ_ERROR: {
CObjError *err = (CObjError *)o; CObjError *err = (CObjError *)o;
printf("%p -> ", (void *)o); printf("%p -> ", (void *)o);
printValue(err->err); cosmoV_printValue(err->err);
break; break;
} }
case COBJ_METHOD: { case COBJ_METHOD: {
CObjMethod *method = (CObjMethod *)o; CObjMethod *method = (CObjMethod *)o;
printf("%p -> ", (void *)method); printf("%p -> ", (void *)method);
printValue(method->func); cosmoV_printValue(method->func);
break; break;
} }
case COBJ_CLOSURE: { case COBJ_CLOSURE: {
@@ -739,7 +733,7 @@ void printObject(CObj *o)
case COBJ_UPVALUE: { case COBJ_UPVALUE: {
CObjUpval *upval = (CObjUpval *)o; CObjUpval *upval = (CObjUpval *)o;
printf("%p -> ", (void *)upval->val); printf("%p -> ", (void *)upval->val);
printValue(*upval->val); cosmoV_printValue(*upval->val);
break; break;
} }
default: default:

View File

@@ -3,6 +3,8 @@
#include "cosmo.h" #include "cosmo.h"
#include <stdarg.h>
typedef enum CObjType typedef enum CObjType
{ {
COBJ_STRING, COBJ_STRING,
@@ -32,7 +34,6 @@ typedef int (*CosmoCFunction)(CState *state, int argCount, CValue *args);
struct CObj struct CObj
{ {
struct CObj *next; struct CObj *next;
struct CObj *nextRoot; // for the root linked list
struct CObjObject *proto; // protoobject, describes the behavior of the object struct CObjObject *proto; // protoobject, describes the behavior of the object
CObjType type; CObjType type;
bool isMarked; // for the GC bool isMarked; // for the GC
@@ -54,7 +55,7 @@ struct CObjError
CCallFrame *frames; CCallFrame *frames;
int frameCount; int frameCount;
int line; // reserved for parser errors int line; // reserved for parser errors
bool parserError; // if true, cosmoV_printError will format the error to the lexer bool parserError; // if true, cosmoV_printBacktrace will format the error to the lexer
}; };
struct CObjObject struct CObjObject
@@ -172,10 +173,10 @@ static inline CObjObject *cosmoO_grabProto(CObj *obj)
return obj->type == COBJ_OBJECT ? (CObjObject *)obj : obj->proto; return obj->type == COBJ_OBJECT ? (CObjObject *)obj : obj->proto;
} }
bool cosmoO_getRawObject(CState *state, CObjObject *proto, CValue key, CValue *val, CObj *obj); void cosmoO_getRawObject(CState *state, CObjObject *proto, CValue key, CValue *val, CObj *obj);
void cosmoO_setRawObject(CState *state, CObjObject *proto, CValue key, CValue val, CObj *obj); void cosmoO_setRawObject(CState *state, CObjObject *proto, CValue key, CValue val, CObj *obj);
bool cosmoO_indexObject(CState *state, CObjObject *object, CValue key, CValue *val); void cosmoO_indexObject(CState *state, CObjObject *object, CValue key, CValue *val);
bool cosmoO_newIndexObject(CState *state, CObjObject *object, CValue key, CValue val); void cosmoO_newIndexObject(CState *state, CObjObject *object, CValue key, CValue val);
// sets the user-defined pointer, if a user-define integer is already defined it will be over // sets the user-defined pointer, if a user-define integer is already defined it will be over
// written // written

View File

@@ -102,9 +102,7 @@ static int expressionPrecedence(CParseState *pstate, int needed, Precedence prec
// returns # of pushed values onto the stack // returns # of pushed values onto the stack
static int expression(CParseState *pstate, int needed, bool forceNeeded); static int expression(CParseState *pstate, int needed, bool forceNeeded);
static void statement(CParseState *pstate); static void statement(CParseState *pstate);
static void declaration(CParseState *pstate);
static void parseFunction(CParseState *pstate, FunctionType type); static void parseFunction(CParseState *pstate, FunctionType type);
static void expressionStatement(CParseState *pstate);
static ParseRule *getRule(CTokenType type); static ParseRule *getRule(CTokenType type);
static CObjFunction *endCompiler(CParseState *pstate); static CObjFunction *endCompiler(CParseState *pstate);
@@ -592,8 +590,6 @@ static void group(CParseState *pstate, bool canAssign, Precedence prec)
consume(pstate, TOKEN_RIGHT_PAREN, "Expected ')'"); consume(pstate, TOKEN_RIGHT_PAREN, "Expected ')'");
} }
#define WRITE_GLOBAL_OP(pstate, op, arg)
static void _etterAB(CParseState *pstate, uint8_t a, int b, bool isGlobal) static void _etterAB(CParseState *pstate, uint8_t a, int b, bool isGlobal)
{ {
writeu8(pstate, a); writeu8(pstate, a);
@@ -1101,18 +1097,19 @@ static ParseRule *getRule(CTokenType type)
// returns true if it got past the first token (aka prefix wasn't null) // returns true if it got past the first token (aka prefix wasn't null)
static bool parsePrecedence(CParseState *pstate, Precedence prec) static bool parsePrecedence(CParseState *pstate, Precedence prec)
{ {
bool canAssign;
ParseFunc prefix, infix;
advance(pstate); advance(pstate);
if ((prefix = getRule(pstate->previous.type)->prefix) == NULL)
ParseFunc prefix = getRule(pstate->previous.type)->prefix;
if (prefix == NULL)
return false; return false;
bool canAssign = prec <= PREC_ASSIGNMENT; canAssign = prec <= PREC_ASSIGNMENT;
prefix(pstate, canAssign, prec); prefix(pstate, canAssign, prec);
while (prec <= getRule(pstate->current.type)->level) { while (prec <= getRule(pstate->current.type)->level) {
ParseFunc infix = getRule(pstate->current.type)->infix; if ((infix = getRule(pstate->current.type)->infix) == NULL)
break;
advance(pstate); advance(pstate);
infix(pstate, canAssign, prec); infix(pstate, canAssign, prec);
} }
@@ -1266,7 +1263,7 @@ static void endScope(CParseState *pstate)
static void block(CParseState *pstate) static void block(CParseState *pstate)
{ {
while (!check(pstate, TOKEN_END) && !check(pstate, TOKEN_EOF) && !check(pstate, TOKEN_ERROR)) { while (!check(pstate, TOKEN_END) && !check(pstate, TOKEN_EOF) && !check(pstate, TOKEN_ERROR)) {
declaration(pstate); statement(pstate);
} }
consume(pstate, TOKEN_END, "'end' expected to end block.'"); consume(pstate, TOKEN_END, "'end' expected to end block.'");
@@ -1323,7 +1320,7 @@ static void ifStatement(CParseState *pstate)
while (!check(pstate, TOKEN_END) && !check(pstate, TOKEN_ELSE) && while (!check(pstate, TOKEN_END) && !check(pstate, TOKEN_ELSE) &&
!check(pstate, TOKEN_ELSEIF) && !check(pstate, TOKEN_EOF) && !check(pstate, TOKEN_ELSEIF) && !check(pstate, TOKEN_EOF) &&
!check(pstate, TOKEN_ERROR)) { !check(pstate, TOKEN_ERROR)) {
declaration(pstate); statement(pstate);
} }
endScope(pstate); endScope(pstate);
@@ -1473,10 +1470,10 @@ static void functionDeclaration(CParseState *pstate)
static void returnStatement(CParseState *pstate) static void returnStatement(CParseState *pstate)
{ {
if (pstate->compiler->type != FTYPE_FUNCTION && pstate->compiler->type != FTYPE_METHOD) { // if (pstate->compiler->type != FTYPE_FUNCTION && pstate->compiler->type != FTYPE_METHOD) {
error(pstate, "Expected 'return' in function!"); // error(pstate, "Expected 'return' in function!");
return; // return;
} // }
if (blockFollow(pstate->current)) { // does this return have a value if (blockFollow(pstate->current)) { // does this return have a value
writeu8(pstate, OP_NIL); writeu8(pstate, OP_NIL);
@@ -1591,7 +1588,7 @@ static void forLoop(CParseState *pstate)
// parse initializer // parse initializer
if (!match(pstate, TOKEN_EOS)) { if (!match(pstate, TOKEN_EOS)) {
expressionStatement(pstate); statement(pstate);
consume(pstate, TOKEN_EOS, "Expected ';' after initializer"); consume(pstate, TOKEN_EOS, "Expected ';' after initializer");
} }
@@ -1707,7 +1704,7 @@ static int expression(CParseState *pstate, int needed, bool forceNeeded)
forceNeeded); // anything above assignments are an expression forceNeeded); // anything above assignments are an expression
} }
static void expressionStatement(CParseState *pstate) static void statement(CParseState *pstate)
{ {
int savedPushed = pstate->compiler->pushedValues; int savedPushed = pstate->compiler->pushedValues;
@@ -1762,16 +1759,6 @@ static void expressionStatement(CParseState *pstate)
alignStack(pstate, savedPushed); alignStack(pstate, savedPushed);
} }
static void statement(CParseState *pstate)
{
expressionStatement(pstate);
}
static void declaration(CParseState *pstate)
{
statement(pstate);
}
static CObjFunction *endCompiler(CParseState *pstate) static CObjFunction *endCompiler(CParseState *pstate)
{ {
popLocals(pstate, pstate->compiler->scopeDepth + 1); // remove the locals from other scopes popLocals(pstate, pstate->compiler->scopeDepth + 1); // remove the locals from other scopes
@@ -1796,7 +1783,7 @@ CObjFunction *cosmoP_compileString(CState *state, const char *source, const char
advance(&parser); advance(&parser);
while (!match(&parser, TOKEN_EOF)) { while (!match(&parser, TOKEN_EOF)) {
declaration(&parser); statement(&parser);
} }
consume(&parser, TOKEN_EOF, "End of file expected!"); consume(&parser, TOKEN_EOF, "End of file expected!");

View File

@@ -12,6 +12,7 @@ CPanic *cosmoV_newPanic(CState *state)
CPanic *panic = cosmoM_xmalloc(state, sizeof(CPanic)); CPanic *panic = cosmoM_xmalloc(state, sizeof(CPanic));
panic->top = state->top; panic->top = state->top;
panic->frameCount = state->frameCount; panic->frameCount = state->frameCount;
panic->freezeGC = state->freezeGC;
panic->prev = state->panic; panic->prev = state->panic;
state->panic = panic; state->panic = panic;
@@ -41,7 +42,6 @@ CState *cosmoV_newState()
// GC // GC
state->objects = NULL; state->objects = NULL;
state->userRoots = NULL;
state->grayStack.count = 0; state->grayStack.count = 0;
state->grayStack.capacity = 2; state->grayStack.capacity = 2;
state->grayStack.array = NULL; state->grayStack.array = NULL;
@@ -54,18 +54,22 @@ CState *cosmoV_newState()
state->openUpvalues = NULL; state->openUpvalues = NULL;
// set default proto objects // set default proto objects
for (int i = 0; i < COBJ_MAX; i++) for (int i = 0; i < COBJ_MAX; i++) {
state->protoObjects[i] = NULL; state->protoObjects[i] = NULL;
}
for (int i = 0; i < ISTRING_MAX; i++) for (int i = 0; i < ISTRING_MAX; i++) {
state->iStrings[i] = NULL; state->iStrings[i] = NULL;
}
cosmoT_initTable(state, &state->strings, 16); // init string table cosmoT_initTable(state, &state->strings, 16); // init string table
cosmoT_initTable(state, &state->registry, 16);
state->globals = cosmoO_newTable(state); // init global table state->globals = cosmoO_newTable(state); // init global table
// setup all strings used by the VM // setup all strings used by the VM
state->iStrings[ISTRING_INIT] = cosmoO_copyString(state, "__init", 6); state->iStrings[ISTRING_INIT] = cosmoO_copyString(state, "__init", 6);
state->iStrings[ISTRING_GC] = cosmoO_copyString(state, "__gc", 4);
state->iStrings[ISTRING_TOSTRING] = cosmoO_copyString(state, "__tostring", 10); state->iStrings[ISTRING_TOSTRING] = cosmoO_copyString(state, "__tostring", 10);
state->iStrings[ISTRING_TONUMBER] = cosmoO_copyString(state, "__tonumber", 10); state->iStrings[ISTRING_TONUMBER] = cosmoO_copyString(state, "__tonumber", 10);
state->iStrings[ISTRING_INDEX] = cosmoO_copyString(state, "__index", 7); state->iStrings[ISTRING_INDEX] = cosmoO_copyString(state, "__index", 7);
@@ -85,8 +89,9 @@ CState *cosmoV_newState()
state->iStrings[ISTRING_RESERVED] = cosmoO_copyString(state, "__reserved", 10); state->iStrings[ISTRING_RESERVED] = cosmoO_copyString(state, "__reserved", 10);
// set the IString flags // set the IString flags
for (int i = 0; i < ISTRING_MAX; i++) for (int i = 0; i < ISTRING_MAX; i++) {
state->iStrings[i]->isIString = true; state->iStrings[i]->isIString = true;
}
state->freezeGC = 0; // unfreeze the state state->freezeGC = 0; // unfreeze the state
return state; return state;
@@ -113,12 +118,15 @@ void cosmoV_freeState(CState *state)
} }
// mark our internal VM strings NULL // mark our internal VM strings NULL
for (int i = 0; i < ISTRING_MAX; i++) for (int i = 0; i < ISTRING_MAX; i++) {
state->iStrings[i] = NULL; state->iStrings[i] = NULL;
}
// free our string table (the string table includes the internal VM strings) // free our string table (the string table includes the internal VM strings)
cosmoT_clearTable(state, &state->strings); cosmoT_clearTable(state, &state->strings);
cosmoT_clearTable(state, &state->registry);
// free our gray stack & finally free the state structure // free our gray stack & finally free the state structure
cosmoM_freeArray(state, CObj *, state->grayStack.array, state->grayStack.capacity); cosmoM_freeArray(state, CObj *, state->grayStack.array, state->grayStack.capacity);
@@ -132,7 +140,7 @@ void cosmoV_freeState(CState *state)
} }
// expects 2*pairs values on the stack, each pair should consist of 1 key and 1 value // expects 2*pairs values on the stack, each pair should consist of 1 key and 1 value
void cosmoV_register(CState *state, int pairs) void cosmoV_addGlobals(CState *state, int pairs)
{ {
for (int i = 0; i < pairs; i++) { for (int i = 0; i < pairs; i++) {
StkPtr key = cosmoV_getTop(state, 1); StkPtr key = cosmoV_getTop(state, 1);
@@ -145,12 +153,57 @@ void cosmoV_register(CState *state, int pairs)
} }
} }
// expects 2*pairs values on the stack, each pair should consist of 1 key and 1 value
void cosmoV_addRegistry(CState *state, int pairs)
{
for (int i = 0; i < pairs; i++) {
StkPtr key = cosmoV_getTop(state, 1);
StkPtr val = cosmoV_getTop(state, 0);
CValue *oldVal = cosmoT_insert(state, &state->registry, *key);
*oldVal = *val;
cosmoV_setTop(state, 2); // pops the 2 values off the stack
}
}
// expects 1 key on the stack, pushes result
void cosmoV_getRegistry(CState *state)
{
CValue key = *cosmoV_pop(state);
CValue val;
if (!cosmoT_get(state, &state->registry, key, &val)) {
cosmoV_error(state, "failed to grab %s from registry", cosmoV_typeStr(key));
}
cosmoV_pushValue(state, val);
}
void cosmoV_setProto(CState *state)
{
StkPtr objVal = cosmoV_getTop(state, 1);
StkPtr protoVal = cosmoV_getTop(state, 0);
if (!IS_REF(*objVal) || !IS_OBJECT(*protoVal)) {
cosmoV_error(state, "cannot set %s to proto of type %s", cosmoV_typeStr(*objVal),
cosmoV_typeStr(*protoVal));
}
// actually set the protos
CObj *obj = cosmoV_readRef(*objVal);
CObjObject *proto = cosmoV_readObject(*protoVal);
obj->proto = proto;
cosmoV_setTop(state, 2);
}
void cosmoV_printStack(CState *state) void cosmoV_printStack(CState *state)
{ {
printf("==== [[ stack dump ]] ====\n"); printf("==== [[ stack dump ]] ====\n");
for (CValue *top = state->top - 1; top >= state->stack; top--) { for (CValue *top = state->top - 1; top >= state->stack; top--) {
printf("%d: ", (int)(top - state->stack)); printf("%d: ", (int)(top - state->stack));
printValue(*top); cosmoV_printValue(*top);
printf("\n"); printf("\n");
} }
} }

View File

@@ -18,6 +18,7 @@ struct CCallFrame
typedef enum IStringEnum typedef enum IStringEnum
{ {
ISTRING_INIT, // __init ISTRING_INIT, // __init
ISTRING_GC, // __gc
ISTRING_TOSTRING, // __tostring ISTRING_TOSTRING, // __tostring
ISTRING_TONUMBER, // __tonumber ISTRING_TONUMBER, // __tonumber
ISTRING_EQUAL, // __equals ISTRING_EQUAL, // __equals
@@ -46,6 +47,7 @@ typedef struct CPanic
StkPtr top; StkPtr top;
struct CPanic *prev; struct CPanic *prev;
int frameCount; int frameCount;
int freezeGC;
} CPanic; } CPanic;
struct CState struct CState
@@ -55,21 +57,20 @@ struct CState
CObjObject *protoObjects[COBJ_MAX]; // proto object for each COBJ type [NULL = no default proto] CObjObject *protoObjects[COBJ_MAX]; // proto object for each COBJ type [NULL = no default proto]
CObjString *iStrings[ISTRING_MAX]; // strings used internally by the VM, eg. __init, __index CObjString *iStrings[ISTRING_MAX]; // strings used internally by the VM, eg. __init, __index
CTable strings; CTable strings;
CTable registry;
ArrayCObj grayStack; // keeps track of which objects *haven't yet* been traversed in our GC, but ArrayCObj grayStack; // keeps track of which objects *haven't yet* been traversed in our GC, but
// *have been* found // *have been* found
CObjUpval *openUpvalues; // tracks all of our still open (meaning still on the stack) upvalues CObjUpval *openUpvalues; // tracks all of our still open (meaning still on the stack) upvalues
CObjTable *globals; CObjTable *globals;
CValue *top; // top of the stack CValue *top; // top of the stack
CObj *objects; // tracks all of our allocated objects CObj *objects; // tracks all of our allocated objects
CObj *userRoots; // user definable roots, this holds CObjs that should be considered "roots",
// lets the VM know you are holding a reference to a CObj in your code
CPanic *panic; CPanic *panic;
int freezeGC; // when > 0, GC events will be ignored (for internal use)
int frameCount;
size_t allocatedBytes; size_t allocatedBytes;
size_t nextGC; // when allocatedBytes reaches this threshhold, trigger a GC event size_t nextGC; // when allocatedBytes reaches this threshhold, trigger a GC event
int freezeGC; // when > 0, GC events will be ignored (for internal use)
int frameCount;
}; };
CPanic *cosmoV_newPanic(CState *state); CPanic *cosmoV_newPanic(CState *state);
@@ -79,7 +80,16 @@ COSMO_API CState *cosmoV_newState();
COSMO_API void cosmoV_freeState(CState *state); COSMO_API void cosmoV_freeState(CState *state);
// expects 2*pairs values on the stack, each pair should consist of 1 key and 1 value // 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_addGlobals(CState *state, int pairs);
// expects 2*pairs values on the stack, each pair should consist of 1 key and 1 value
COSMO_API void cosmoV_addRegistry(CState *state, int pairs);
// expects 1 key on the stack, pushes result
COSMO_API void cosmoV_getRegistry(CState *state);
// expects <object>->proto = <object> (2 total) to be on the stack
COSMO_API void cosmoV_setProto(CState *state);
COSMO_API void cosmoV_printStack(CState *state); COSMO_API void cosmoV_printStack(CState *state);

View File

@@ -69,6 +69,8 @@ static uint32_t getObjectHash(CObj *obj)
switch (obj->type) { switch (obj->type) {
case COBJ_STRING: case COBJ_STRING:
return ((CObjString *)obj)->hash; return ((CObjString *)obj)->hash;
case COBJ_CFUNCTION:
return (uint32_t)((CObjCFunction *)obj)->cfunc;
default: default:
return (uint32_t)obj; // just "hash" the pointer return (uint32_t)obj; // just "hash" the pointer
} }
@@ -87,8 +89,9 @@ static uint32_t getValueHash(CValue *val)
return 0; return 0;
memcpy(buf, &num, sizeof(buf)); memcpy(buf, &num, sizeof(buf));
for (size_t i = 0; i < sizeof(cosmo_Number) / sizeof(uint32_t); i++) for (size_t i = 0; i < sizeof(cosmo_Number) / sizeof(uint32_t); i++) {
buf[0] += buf[i]; buf[0] += buf[i];
}
return buf[0]; return buf[0];
} }
// TODO: add support for other types // TODO: add support for other types
@@ -285,9 +288,9 @@ void cosmoT_printTable(CTable *tbl, const char *name)
for (int i = 0; i < cap; i++) { for (int i = 0; i < cap; i++) {
CTableEntry *entry = &tbl->table[i]; CTableEntry *entry = &tbl->table[i];
if (!(IS_NIL(entry->key))) { if (!(IS_NIL(entry->key))) {
printValue(entry->key); cosmoV_printValue(entry->key);
printf(" - "); printf(" - ");
printValue(entry->val); cosmoV_printValue(entry->val);
printf("\n"); printf("\n");
} }
} }

View File

@@ -101,7 +101,7 @@ const char *cosmoV_typeStr(CValue val)
} }
} }
void printValue(CValue val) void cosmoV_printValue(CValue val)
{ {
switch (GET_TYPE(val)) { switch (GET_TYPE(val)) {
case COSMO_TNUMBER: case COSMO_TNUMBER:

View File

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

170
src/cvm.c
View File

@@ -12,7 +12,7 @@
#define cosmoV_protect(panic) setjmp(panic->jmp) == 0 #define cosmoV_protect(panic) setjmp(panic->jmp) == 0
COSMO_API void cosmoV_pushFString(CState *state, const char *format, ...) void cosmoV_pushFString(CState *state, const char *format, ...)
{ {
va_list args; va_list args;
va_start(args, format); va_start(args, format);
@@ -21,19 +21,20 @@ COSMO_API void cosmoV_pushFString(CState *state, const char *format, ...)
} }
// inserts val at state->top - indx - 1, moving everything else up // inserts val at state->top - indx - 1, moving everything else up
COSMO_API void cosmo_insert(CState *state, int indx, CValue val) void cosmoV_insert(CState *state, int indx, CValue val)
{ {
StkPtr tmp = cosmoV_getTop(state, indx); StkPtr tmp = cosmoV_getTop(state, indx);
// moves everything up // moves everything up
for (StkPtr i = state->top; i > tmp; i--) for (StkPtr i = state->top; i > tmp; i--) {
*i = *(i - 1); *i = *(i - 1);
}
*tmp = val; *tmp = val;
state->top++; state->top++;
} }
COSMO_API bool cosmoV_undump(CState *state, cosmo_Reader reader, const void *ud) bool cosmoV_undump(CState *state, cosmo_Reader reader, const void *ud)
{ {
CObjFunction *func; CObjFunction *func;
@@ -54,7 +55,7 @@ COSMO_API bool cosmoV_undump(CState *state, cosmo_Reader reader, const void *ud)
// returns false if failed, error will be on the top of the stack. true if successful, closure will // 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 // be on the top of the stack
COSMO_API bool cosmoV_compileString(CState *state, const char *src, const char *name) bool cosmoV_compileString(CState *state, const char *src, const char *name)
{ {
CObjFunction *func; CObjFunction *func;
CPanic *panic = cosmoV_newPanic(state); CPanic *panic = cosmoV_newPanic(state);
@@ -77,7 +78,7 @@ COSMO_API bool cosmoV_compileString(CState *state, const char *src, const char *
return false; return false;
} }
COSMO_API void cosmoV_printError(CState *state, CObjError *err) void cosmoV_printBacktrace(CState *state, CObjError *err)
{ {
// print stack trace // print stack trace
for (int i = 0; i < err->frameCount; i++) { for (int i = 0; i < err->frameCount; i++) {
@@ -122,12 +123,13 @@ void cosmoV_throw(CState *state)
if (state->panic) { if (state->panic) {
state->top = state->panic->top; state->top = state->panic->top;
state->frameCount = state->panic->frameCount; state->frameCount = state->panic->frameCount;
state->freezeGC = state->panic->freezeGC;
cosmoV_pushValue(state, val); cosmoV_pushValue(state, val);
longjmp(state->panic->jmp, 1); longjmp(state->panic->jmp, 1);
} else { } else {
cosmoV_pushValue(state, val); cosmoV_pushValue(state, val);
fprintf(stderr, "Unhandled panic! "); fprintf(stderr, "Unhandled panic! ");
cosmoV_printError(state, error); cosmoV_printBacktrace(state, error);
exit(1); exit(1);
} }
} }
@@ -267,8 +269,9 @@ static void callCFunction(CState *state, CosmoCFunction cfunc, int args, int nre
state->top += nres; // and make sure to move state->top to match state->top += nres; // and make sure to move state->top to match
// now, if the caller function expected more return values, push nils onto the stack // now, if the caller function expected more return values, push nils onto the stack
for (int i = nres; i < nresults; i++) for (int i = nres; i < nresults; i++) {
cosmoV_pushValue(state, cosmoV_newNil()); cosmoV_pushValue(state, cosmoV_newNil());
}
} }
/* /*
@@ -326,8 +329,9 @@ static void rawCall(CState *state, CObjClosure *closure, int args, int nresults,
state->top += nres; // and make sure to move state->top to match state->top += nres; // and make sure to move state->top to match
// now, if the caller function expected more return values, push nils onto the stack // now, if the caller function expected more return values, push nils onto the stack
for (int i = nres; i < nresults; i++) for (int i = nres; i < nresults; i++) {
cosmoV_pushValue(state, cosmoV_newNil()); cosmoV_pushValue(state, cosmoV_newNil());
}
} }
// returns true if successful, false if error // returns true if successful, false if error
@@ -336,7 +340,7 @@ void callCValue(CState *state, CValue func, int args, int nresults, int offset)
#ifdef VM_DEBUG #ifdef VM_DEBUG
printf("\n"); printf("\n");
printIndent(state->frameCount - 1); printIndent(state->frameCount - 1);
printValue(func); cosmoV_printValue(func);
printf("(%d args)\n", args); printf("(%d args)\n", args);
#endif #endif
@@ -376,9 +380,9 @@ void callCValue(CState *state, CValue func, int args, int nresults, int offset)
if (nresults > 0) { if (nresults > 0) {
cosmoV_pushRef(state, (CObj *)newObj); cosmoV_pushRef(state, (CObj *)newObj);
// push the nils to fill up the expected return values // push the nils to fill up the expected return values.
for (int i = 0; i < nresults - 1; // -1 since the we already pushed the important value
i++) { // -1 since the we already pushed the important value for (int i = 0; i < nresults - 1; i++) {
cosmoV_pushValue(state, cosmoV_newNil()); cosmoV_pushValue(state, cosmoV_newNil());
} }
} }
@@ -414,8 +418,9 @@ bool cosmoV_pcall(CState *state, int args, int nresults)
if (nresults > 0) { if (nresults > 0) {
// push other expected results onto the stack // push other expected results onto the stack
for (int i = 0; i < nresults - 1; i++) for (int i = 0; i < nresults - 1; i++) {
cosmoV_pushValue(state, cosmoV_newNil()); cosmoV_pushValue(state, cosmoV_newNil());
}
} }
cosmoV_freePanic(state); cosmoV_freePanic(state);
@@ -438,7 +443,7 @@ static inline bool isFalsey(StkPtr val)
return IS_NIL(*val) || (IS_BOOLEAN(*val) && !cosmoV_readBoolean(*val)); return IS_NIL(*val) || (IS_BOOLEAN(*val) && !cosmoV_readBoolean(*val));
} }
COSMO_API CObjObject *cosmoV_makeObject(CState *state, int pairs) CObjObject *cosmoV_makeObject(CState *state, int pairs)
{ {
StkPtr key, val; StkPtr key, val;
CObjObject *newObj = cosmoO_newObject(state); CObjObject *newObj = cosmoO_newObject(state);
@@ -459,7 +464,7 @@ COSMO_API CObjObject *cosmoV_makeObject(CState *state, int pairs)
return newObj; return newObj;
} }
COSMO_API bool cosmoV_registerProtoObject(CState *state, CObjType objType, CObjObject *obj) bool cosmoV_registerProtoObject(CState *state, CObjType objType, CObjObject *obj)
{ {
bool replaced = state->protoObjects[objType] != NULL; bool replaced = state->protoObjects[objType] != NULL;
state->protoObjects[objType] = obj; state->protoObjects[objType] = obj;
@@ -468,7 +473,7 @@ COSMO_API bool cosmoV_registerProtoObject(CState *state, CObjType objType, CObjO
CObj *curr = state->objects; CObj *curr = state->objects;
while (curr != NULL) { while (curr != NULL) {
// update the proto // update the proto
if (curr->type == objType && curr->proto != NULL) { if (curr != (CObj *)obj && curr->type == objType && curr->proto != NULL) {
curr->proto = obj; curr->proto = obj;
} }
curr = curr->next; curr = curr->next;
@@ -477,7 +482,7 @@ COSMO_API bool cosmoV_registerProtoObject(CState *state, CObjType objType, CObjO
return replaced; return replaced;
} }
COSMO_API void cosmoV_makeTable(CState *state, int pairs) void cosmoV_makeTable(CState *state, int pairs)
{ {
StkPtr key, val; StkPtr key, val;
CObjTable *newObj = cosmoO_newTable(state); CObjTable *newObj = cosmoO_newTable(state);
@@ -497,7 +502,7 @@ COSMO_API void cosmoV_makeTable(CState *state, int pairs)
cosmoV_pushRef(state, (CObj *)newObj); cosmoV_pushRef(state, (CObj *)newObj);
} }
bool cosmoV_rawget(CState *state, CObj *_obj, CValue key, CValue *val) void cosmoV_rawget(CState *state, CObj *_obj, CValue key, CValue *val)
{ {
CObjObject *object = cosmoO_grabProto(_obj); CObjObject *object = cosmoO_grabProto(_obj);
@@ -506,23 +511,16 @@ bool cosmoV_rawget(CState *state, CObj *_obj, CValue key, CValue *val)
CObjString *field = cosmoV_toString(state, key); CObjString *field = cosmoV_toString(state, key);
cosmoV_error(state, "No proto defined! Couldn't get field '%s' from type %s", field->str, cosmoV_error(state, "No proto defined! Couldn't get field '%s' from type %s", field->str,
cosmoO_typeStr(_obj)); cosmoO_typeStr(_obj));
*val = cosmoV_newNil();
return false;
} }
// push the object onto the stack so the GC can find it // push the object onto the stack so the GC can find it
cosmoV_pushRef(state, (CObj *)object); cosmoV_pushRef(state, (CObj *)object);
if (cosmoO_getRawObject(state, object, key, val, _obj)) { cosmoO_getRawObject(state, object, key, val, _obj);
// *val now equals the response, pop the object
cosmoV_pop(state);
return true;
}
cosmoV_pop(state); cosmoV_pop(state);
return false;
} }
bool cosmoV_rawset(CState *state, CObj *_obj, CValue key, CValue val) void cosmoV_rawset(CState *state, CObj *_obj, CValue key, CValue val)
{ {
CObjObject *object = cosmoO_grabProto(_obj); CObjObject *object = cosmoO_grabProto(_obj);
@@ -531,14 +529,12 @@ bool cosmoV_rawset(CState *state, CObj *_obj, CValue key, CValue val)
CObjString *field = cosmoV_toString(state, key); CObjString *field = cosmoV_toString(state, key);
cosmoV_error(state, "No proto defined! Couldn't set field '%s' to type %s", field->str, cosmoV_error(state, "No proto defined! Couldn't set field '%s' to type %s", field->str,
cosmoO_typeStr(_obj)); cosmoO_typeStr(_obj));
return false;
} }
cosmoO_setRawObject(state, object, key, val, _obj); cosmoO_setRawObject(state, object, key, val, _obj);
return true;
} }
COSMO_API bool cosmoV_get(CState *state) void cosmoV_get(CState *state)
{ {
CValue val; CValue val;
StkPtr obj = cosmoV_getTop(state, 1); // object was pushed first StkPtr obj = cosmoV_getTop(state, 1); // object was pushed first
@@ -546,20 +542,17 @@ COSMO_API bool cosmoV_get(CState *state)
if (!IS_REF(*obj)) { if (!IS_REF(*obj)) {
cosmoV_error(state, "Couldn't get field from type %s!", cosmoV_typeStr(*obj)); cosmoV_error(state, "Couldn't get field from type %s!", cosmoV_typeStr(*obj));
return false;
} }
if (!cosmoV_rawget(state, cosmoV_readRef(*obj), *key, &val)) cosmoV_rawget(state, cosmoV_readRef(*obj), *key, &val);
return false;
// pop the obj & key, push the value // pop the obj & key, push the value
cosmoV_setTop(state, 2); cosmoV_setTop(state, 2);
cosmoV_pushValue(state, val); cosmoV_pushValue(state, val);
return true;
} }
// yes, this would technically make it possible to set fields of types other than <string>. go crazy // yes, this would technically make it possible to set fields of types other than <string>. go crazy
COSMO_API bool cosmoV_set(CState *state) void cosmoV_set(CState *state)
{ {
StkPtr obj = cosmoV_getTop(state, 2); // object was pushed first StkPtr obj = cosmoV_getTop(state, 2); // object was pushed first
StkPtr key = cosmoV_getTop(state, 1); // then the key StkPtr key = cosmoV_getTop(state, 1); // then the key
@@ -567,21 +560,17 @@ COSMO_API bool cosmoV_set(CState *state)
if (!IS_REF(*obj)) { if (!IS_REF(*obj)) {
cosmoV_error(state, "Couldn't set field on type %s!", cosmoV_typeStr(*obj)); cosmoV_error(state, "Couldn't set field on type %s!", cosmoV_typeStr(*obj));
return false;
} }
if (!cosmoV_rawset(state, cosmoV_readRef(*obj), *key, *val)) cosmoV_rawset(state, cosmoV_readRef(*obj), *key, *val);
return false;
// pop the obj, key & value // pop the obj, key & value
cosmoV_setTop(state, 3); cosmoV_setTop(state, 3);
return true;
} }
COSMO_API bool cosmoV_getMethod(CState *state, CObj *obj, CValue key, CValue *val) void cosmoV_getMethod(CState *state, CObj *obj, CValue key, CValue *val)
{ {
if (!cosmoV_rawget(state, obj, key, val)) cosmoV_rawget(state, obj, key, val);
return false;
// if the result is callable, wrap it in an method // if the result is callable, wrap it in an method
if (IS_CALLABLE(*val)) { if (IS_CALLABLE(*val)) {
@@ -591,6 +580,18 @@ COSMO_API bool cosmoV_getMethod(CState *state, CObj *obj, CValue key, CValue *va
cosmoV_pop(state); // pop the object cosmoV_pop(state); // pop the object
*val = cosmoV_newRef(method); *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; return true;
} }
@@ -696,7 +697,7 @@ static inline uint16_t READUINT(CCallFrame *frame)
# define SWITCH switch (READBYTE(frame)) # define SWITCH switch (READBYTE(frame))
# define DEFAULT \ # define DEFAULT \
default: \ default: \
printf("[ERROR] unknown opcode!"); \ printf("[ERROR] unknown opcode!"); \
exit(0) exit(0)
#endif #endif
@@ -849,18 +850,15 @@ int cosmoV_execute(CState *state)
// sanity check // sanity check
if (!IS_REF(*temp)) { if (!IS_REF(*temp)) {
cosmoV_error(state, "Couldn't index type %s!", cosmoV_typeStr(*temp)); cosmoV_error(state, "Couldn't index type %s!", cosmoV_typeStr(*temp));
return -1;
} }
CObj *obj = cosmoV_readRef(*temp); CObj *obj = cosmoV_readRef(*temp);
CObjObject *proto = cosmoO_grabProto(obj); CObjObject *proto = cosmoO_grabProto(obj);
CValue val; // to hold our value CValue val = cosmoV_newNil(); // to hold our value
if (proto != NULL) { if (proto != NULL) {
// check for __index metamethod // check for __index metamethod
if (!cosmoO_indexObject(state, proto, *key, cosmoO_indexObject(state, proto, *key, &val);
&val)) // if returns false, cosmoV_error was called
return -1;
} else if (obj->type == COBJ_TABLE) { } else if (obj->type == COBJ_TABLE) {
CObjTable *tbl = (CObjTable *)obj; CObjTable *tbl = (CObjTable *)obj;
@@ -868,7 +866,6 @@ int cosmoV_execute(CState *state)
} else { } else {
cosmoV_error(state, "No proto defined! Couldn't __index from type %s", cosmoV_error(state, "No proto defined! Couldn't __index from type %s",
cosmoV_typeStr(*temp)); cosmoV_typeStr(*temp));
return -1;
} }
cosmoV_setTop(state, 2); // pops the table & the key cosmoV_setTop(state, 2); // pops the table & the key
@@ -883,17 +880,13 @@ int cosmoV_execute(CState *state)
// sanity check // sanity check
if (!IS_REF(*temp)) { if (!IS_REF(*temp)) {
cosmoV_error(state, "Couldn't set index with type %s!", cosmoV_typeStr(*temp)); cosmoV_error(state, "Couldn't set index with type %s!", cosmoV_typeStr(*temp));
return -1;
} }
CObj *obj = cosmoV_readRef(*temp); CObj *obj = cosmoV_readRef(*temp);
CObjObject *proto = cosmoO_grabProto(obj); CObjObject *proto = cosmoO_grabProto(obj);
if (proto != NULL) { if (proto != NULL) {
if (!cosmoO_newIndexObject( cosmoO_newIndexObject(state, proto, *key, *value);
state, proto, *key,
*value)) // if it returns false, cosmoV_error was called
return -1;
} else if (obj->type == COBJ_TABLE) { } else if (obj->type == COBJ_TABLE) {
CObjTable *tbl = (CObjTable *)obj; CObjTable *tbl = (CObjTable *)obj;
CValue *newVal = cosmoT_insert(state, &tbl->tbl, *key); CValue *newVal = cosmoT_insert(state, &tbl->tbl, *key);
@@ -902,7 +895,6 @@ int cosmoV_execute(CState *state)
} else { } else {
cosmoV_error(state, "No proto defined! Couldn't __newindex from type %s", cosmoV_error(state, "No proto defined! Couldn't __newindex from type %s",
cosmoV_typeStr(*temp)); cosmoV_typeStr(*temp));
return -1;
} }
// pop everything off the stack // pop everything off the stack
@@ -921,13 +913,11 @@ int cosmoV_execute(CState *state)
// sanity check // sanity check
if (IS_REF(*temp)) { if (IS_REF(*temp)) {
if (!cosmoV_rawset(state, cosmoV_readRef(*temp), constants[ident], *value)) cosmoV_rawset(state, cosmoV_readRef(*temp), constants[ident], *value);
return -1;
} else { } else {
CObjString *field = cosmoV_toString(state, constants[ident]); CObjString *field = cosmoV_toString(state, constants[ident]);
cosmoV_error(state, "Couldn't set field '%s' on type %s!", field->str, cosmoV_error(state, "Couldn't set field '%s' on type %s!", field->str,
cosmoV_typeStr(*temp)); cosmoV_typeStr(*temp));
return -1;
} }
// pop everything off the stack // pop everything off the stack
@@ -935,19 +925,17 @@ int cosmoV_execute(CState *state)
} }
CASE(OP_GETOBJECT) : CASE(OP_GETOBJECT) :
{ {
CValue val; // to hold our value CValue val = cosmoV_newNil(); // to hold our value
StkPtr temp = cosmoV_getTop(state, 0); // that should be the object StkPtr temp = cosmoV_getTop(state, 0); // that should be the object
uint16_t ident = READUINT(frame); // use for the key uint16_t ident = READUINT(frame); // use for the key
// sanity check // sanity check
if (IS_REF(*temp)) { if (IS_REF(*temp)) {
if (!cosmoV_rawget(state, cosmoV_readRef(*temp), constants[ident], &val)) cosmoV_rawget(state, cosmoV_readRef(*temp), constants[ident], &val);
return -1;
} else { } else {
CObjString *field = cosmoV_toString(state, constants[ident]); CObjString *field = cosmoV_toString(state, constants[ident]);
cosmoV_error(state, "Couldn't get field '%s' from type %s!", field->str, cosmoV_error(state, "Couldn't get field '%s' from type %s!", field->str,
cosmoV_typeStr(*temp)); cosmoV_typeStr(*temp));
return -1;
} }
cosmoV_setTop(state, 1); // pops the object cosmoV_setTop(state, 1); // pops the object
@@ -955,20 +943,18 @@ int cosmoV_execute(CState *state)
} }
CASE(OP_GETMETHOD) : CASE(OP_GETMETHOD) :
{ {
CValue val; // to hold our value CValue val = cosmoV_newNil(); // to hold our value
StkPtr temp = cosmoV_getTop(state, 0); // that should be the object StkPtr temp = cosmoV_getTop(state, 0); // that should be the object
uint16_t ident = READUINT(frame); // use for the key uint16_t ident = READUINT(frame); // use for the key
// this is almost identical to GETOBJECT, however cosmoV_getMethod is used instead // this is almost identical to GETOBJECT, however cosmoV_getMethod is used instead
// of just cosmoV_get // of just cosmoV_get
if (IS_REF(*temp)) { if (IS_REF(*temp)) {
if (!cosmoV_getMethod(state, cosmoV_readRef(*temp), constants[ident], &val)) cosmoV_getMethod(state, cosmoV_readRef(*temp), constants[ident], &val);
return -1;
} else { } else {
CObjString *field = cosmoV_toString(state, constants[ident]); CObjString *field = cosmoV_toString(state, constants[ident]);
cosmoV_error(state, "Couldn't get field '%s' from type %s!", field->str, cosmoV_error(state, "Couldn't get field '%s' from type %s!", field->str,
cosmoV_typeStr(*temp)); cosmoV_typeStr(*temp));
return -1;
} }
cosmoV_setTop(state, 1); // pops the object cosmoV_setTop(state, 1); // pops the object
@@ -985,14 +971,12 @@ int cosmoV_execute(CState *state)
// sanity check // sanity check
if (IS_REF(*temp)) { if (IS_REF(*temp)) {
// get the field from the object // get the field from the object
if (!cosmoV_rawget(state, cosmoV_readRef(*temp), constants[ident], &val)) cosmoV_rawget(state, cosmoV_readRef(*temp), constants[ident], &val);
return -1;
// now invoke the method! // now invoke the method!
invokeMethod(state, cosmoV_readRef(*temp), val, args, nres, 1); invokeMethod(state, cosmoV_readRef(*temp), val, args, nres, 1);
} else { } else {
cosmoV_error(state, "Couldn't get from type %s!", cosmoV_typeStr(*temp)); cosmoV_error(state, "Couldn't get from type %s!", cosmoV_typeStr(*temp));
return -1;
} }
} }
CASE(OP_ITER) : CASE(OP_ITER) :
@@ -1002,7 +986,6 @@ int cosmoV_execute(CState *state)
if (!IS_REF(*temp)) { if (!IS_REF(*temp)) {
cosmoV_error(state, "Couldn't iterate over non-iterator type %s!", cosmoV_error(state, "Couldn't iterate over non-iterator type %s!",
cosmoV_typeStr(*temp)); cosmoV_typeStr(*temp));
return -1;
} }
CObj *obj = cosmoV_readRef(*temp); CObj *obj = cosmoV_readRef(*temp);
@@ -1026,7 +1009,6 @@ int cosmoV_execute(CState *state)
"Expected iterable object! '__iter' returned %s, expected " "Expected iterable object! '__iter' returned %s, expected "
"<object>!", "<object>!",
cosmoV_typeStr(*iObj)); cosmoV_typeStr(*iObj));
return -1;
} }
// get __next method and place it at the top of the stack // get __next method and place it at the top of the stack
@@ -1034,7 +1016,6 @@ int cosmoV_execute(CState *state)
cosmoV_newRef(state->iStrings[ISTRING_NEXT]), iObj); cosmoV_newRef(state->iStrings[ISTRING_NEXT]), iObj);
} else { } else {
cosmoV_error(state, "Expected iterable object! '__iter' not defined!"); cosmoV_error(state, "Expected iterable object! '__iter' not defined!");
return -1;
} }
} else if (obj->type == COBJ_TABLE) { } else if (obj->type == COBJ_TABLE) {
CObjTable *tbl = (CObjTable *)obj; CObjTable *tbl = (CObjTable *)obj;
@@ -1059,7 +1040,6 @@ int cosmoV_execute(CState *state)
} else { } else {
cosmoV_error(state, "No proto defined! Couldn't get from type %s", cosmoV_error(state, "No proto defined! Couldn't get from type %s",
cosmoO_typeStr(obj)); cosmoO_typeStr(obj));
return -1;
} }
} }
CASE(OP_NEXT) : CASE(OP_NEXT) :
@@ -1071,7 +1051,6 @@ int cosmoV_execute(CState *state)
if (!IS_METHOD(*temp)) { if (!IS_METHOD(*temp)) {
cosmoV_error(state, "Expected '__next' to be a method, got type %s!", cosmoV_error(state, "Expected '__next' to be a method, got type %s!",
cosmoV_typeStr(*temp)); cosmoV_typeStr(*temp));
return -1;
} }
cosmoV_pushValue(state, *temp); cosmoV_pushValue(state, *temp);
@@ -1114,7 +1093,6 @@ int cosmoV_execute(CState *state)
} else { } else {
cosmoV_error(state, "Expected numbers, got %s and %s!", cosmoV_typeStr(*valA), cosmoV_error(state, "Expected numbers, got %s and %s!", cosmoV_typeStr(*valA),
cosmoV_typeStr(*valB)); cosmoV_typeStr(*valB));
return -1;
} }
} }
CASE(OP_POW) : CASE(OP_POW) :
@@ -1128,7 +1106,6 @@ int cosmoV_execute(CState *state)
} else { } else {
cosmoV_error(state, "Expected numbers, got %s and %s!", cosmoV_typeStr(*valA), cosmoV_error(state, "Expected numbers, got %s and %s!", cosmoV_typeStr(*valA),
cosmoV_typeStr(*valB)); cosmoV_typeStr(*valB));
return -1;
} }
} }
CASE(OP_NOT) : CASE(OP_NOT) :
@@ -1144,7 +1121,6 @@ int cosmoV_execute(CState *state)
cosmoV_pushNumber(state, -(cosmoV_readNumber(*val))); cosmoV_pushNumber(state, -(cosmoV_readNumber(*val)));
} else { } else {
cosmoV_error(state, "Expected number, got %s!", cosmoV_typeStr(*val)); cosmoV_error(state, "Expected number, got %s!", cosmoV_typeStr(*val));
return -1;
} }
} }
CASE(OP_COUNT) : CASE(OP_COUNT) :
@@ -1153,7 +1129,6 @@ int cosmoV_execute(CState *state)
if (!IS_REF(*temp)) { if (!IS_REF(*temp)) {
cosmoV_error(state, "Expected non-primitive, got %s!", cosmoV_typeStr(*temp)); cosmoV_error(state, "Expected non-primitive, got %s!", cosmoV_typeStr(*temp));
return -1;
} }
int count = cosmoO_count(state, cosmoV_readRef(*temp)); int count = cosmoO_count(state, cosmoV_readRef(*temp));
@@ -1178,7 +1153,6 @@ int cosmoV_execute(CState *state)
*val = cosmoV_newNumber(cosmoV_readNumber(*val) + inc); *val = cosmoV_newNumber(cosmoV_readNumber(*val) + inc);
} else { } else {
cosmoV_error(state, "Expected number, got %s!", cosmoV_typeStr(*val)); cosmoV_error(state, "Expected number, got %s!", cosmoV_typeStr(*val));
return -1;
} }
} }
CASE(OP_INCGLOBAL) : CASE(OP_INCGLOBAL) :
@@ -1194,7 +1168,6 @@ int cosmoV_execute(CState *state)
*val = cosmoV_newNumber(cosmoV_readNumber(*val) + inc); *val = cosmoV_newNumber(cosmoV_readNumber(*val) + inc);
} else { } else {
cosmoV_error(state, "Expected number, got %s!", cosmoV_typeStr(*val)); cosmoV_error(state, "Expected number, got %s!", cosmoV_typeStr(*val));
return -1;
} }
} }
CASE(OP_INCUPVAL) : CASE(OP_INCUPVAL) :
@@ -1209,7 +1182,6 @@ int cosmoV_execute(CState *state)
*val = cosmoV_newNumber(cosmoV_readNumber(*val) + inc); *val = cosmoV_newNumber(cosmoV_readNumber(*val) + inc);
} else { } else {
cosmoV_error(state, "Expected number, got %s!", cosmoV_typeStr(*val)); cosmoV_error(state, "Expected number, got %s!", cosmoV_typeStr(*val));
return -1;
} }
} }
CASE(OP_INCINDEX) : CASE(OP_INCINDEX) :
@@ -1221,7 +1193,6 @@ int cosmoV_execute(CState *state)
if (!IS_REF(*temp)) { if (!IS_REF(*temp)) {
cosmoV_error(state, "Couldn't index non-indexable type %s!", cosmoV_error(state, "Couldn't index non-indexable type %s!",
cosmoV_typeStr(*temp)); cosmoV_typeStr(*temp));
return -1;
} }
CObj *obj = cosmoV_readRef(*temp); CObj *obj = cosmoV_readRef(*temp);
@@ -1230,27 +1201,23 @@ int cosmoV_execute(CState *state)
// call __index if the proto was found // call __index if the proto was found
if (proto != NULL) { if (proto != NULL) {
if (cosmoO_indexObject(state, proto, *key, &val)) { cosmoO_indexObject(state, proto, *key, &val);
if (!IS_NUMBER(val)) {
cosmoV_error(state, "Expected number, got %s!", cosmoV_typeStr(val));
return -1;
}
cosmoV_pushValue(state, val); // pushes old value onto the stack :) if (!IS_NUMBER(val)) {
cosmoV_error(state, "Expected number, got %s!", cosmoV_typeStr(val));
}
// call __newindex cosmoV_pushValue(state, val); // pushes old value onto the stack :)
if (!cosmoO_newIndexObject(state, proto, *key,
cosmoV_newNumber(cosmoV_readNumber(val) + inc))) // call __newindex
return -1; cosmoO_newIndexObject(state, proto, *key,
} else cosmoV_newNumber(cosmoV_readNumber(val) + inc));
return -1; // cosmoO_indexObject failed and threw an error
} else if (obj->type == COBJ_TABLE) { } else if (obj->type == COBJ_TABLE) {
CObjTable *tbl = (CObjTable *)obj; CObjTable *tbl = (CObjTable *)obj;
CValue *val = cosmoT_insert(state, &tbl->tbl, *key); CValue *val = cosmoT_insert(state, &tbl->tbl, *key);
if (!IS_NUMBER(*val)) { if (!IS_NUMBER(*val)) {
cosmoV_error(state, "Expected number, got %s!", cosmoV_typeStr(*val)); cosmoV_error(state, "Expected number, got %s!", cosmoV_typeStr(*val));
return -1;
} }
// pops tbl & key from stack // pops tbl & key from stack
@@ -1260,7 +1227,6 @@ int cosmoV_execute(CState *state)
} else { } else {
cosmoV_error(state, "No proto defined! Couldn't __index from type %s", cosmoV_error(state, "No proto defined! Couldn't __index from type %s",
cosmoV_typeStr(*temp)); cosmoV_typeStr(*temp));
return -1;
} }
} }
CASE(OP_INCOBJECT) : CASE(OP_INCOBJECT) :
@@ -1275,8 +1241,7 @@ int cosmoV_execute(CState *state)
CObj *obj = cosmoV_readRef(*temp); CObj *obj = cosmoV_readRef(*temp);
CValue val; CValue val;
if (!cosmoV_rawget(state, obj, ident, &val)) cosmoV_rawget(state, obj, ident, &val);
return -1;
// pop the object off the stack // pop the object off the stack
cosmoV_pop(state); cosmoV_pop(state);
@@ -1284,16 +1249,13 @@ int cosmoV_execute(CState *state)
// check that it's a number value // check that it's a number value
if (IS_NUMBER(val)) { if (IS_NUMBER(val)) {
cosmoV_pushValue(state, val); // pushes old value onto the stack :) cosmoV_pushValue(state, val); // pushes old value onto the stack :)
if (!cosmoV_rawset(state, obj, ident, cosmoV_rawset(state, obj, ident,
cosmoV_newNumber(cosmoV_readNumber(val) + inc))) cosmoV_newNumber(cosmoV_readNumber(val) + inc));
return -1;
} else { } else {
cosmoV_error(state, "Expected number, got %s!", cosmoV_typeStr(val)); cosmoV_error(state, "Expected number, got %s!", cosmoV_typeStr(val));
return -1;
} }
} else { } else {
cosmoV_error(state, "Couldn't set a field on type %s!", cosmoV_typeStr(*temp)); cosmoV_error(state, "Couldn't set a field on type %s!", cosmoV_typeStr(*temp));
return -1;
} }
} }
CASE(OP_EQUAL) : CASE(OP_EQUAL) :

View File

@@ -33,14 +33,14 @@ COSMO_API CObjObject *cosmoV_makeObject(CState *state, int pairs);
COSMO_API void cosmoV_makeTable(CState *state, int pairs); COSMO_API void cosmoV_makeTable(CState *state, int pairs);
COSMO_API void cosmoV_concat(CState *state, int vals); COSMO_API void cosmoV_concat(CState *state, int vals);
COSMO_API void cosmoV_pushFString(CState *state, const char *format, ...); COSMO_API void cosmoV_pushFString(CState *state, const char *format, ...);
COSMO_API void cosmoV_printError(CState *state, CObjError *err); COSMO_API void cosmoV_printBacktrace(CState *state, CObjError *err);
COSMO_API void cosmoV_throw(CState *state); COSMO_API void cosmoV_throw(CState *state);
COSMO_API void cosmoV_error(CState *state, const char *format, ...); COSMO_API void cosmoV_error(CState *state, const char *format, ...);
COSMO_API void cosmo_insert(CState *state, int indx, CValue val); COSMO_API void cosmoV_insert(CState *state, int indx, CValue val);
/* /*
Sets the default proto objects for the passed objType. Also walks through the object heap and Sets the default proto objects for the passed objType. Also walks through the object heap and
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 returns true if replacing a previously registered proto object for this type
*/ */
@@ -67,27 +67,21 @@ COSMO_API bool cosmoV_compileString(CState *state, const char *src, const char *
COSMO_API bool cosmoV_undump(CState *state, cosmo_Reader reader, const void *ud); COSMO_API bool cosmoV_undump(CState *state, cosmo_Reader reader, const void *ud);
/* /*
expects object to be pushed, then the key. expects object to be pushed, then the key. pops the key & object and pushes the value
returns false if an error was thrown, returns true if the value was pushed onto the stack and
the object and key were popped
*/ */
COSMO_API bool cosmoV_get(CState *state); COSMO_API void cosmoV_get(CState *state);
/* /*
expects object to be pushed, then the key, and finally the new value. expects object to be pushed, then the key, and finally the new value. pops the object, key &
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 bool cosmoV_set(CState *state); COSMO_API void cosmoV_set(CState *state);
// wraps the closure into a CObjMethod, so the function is called as an invoked method // wraps the closure into a CObjMethod, so the function is called as an invoked method
COSMO_API bool cosmoV_getMethod(CState *state, CObj *obj, CValue key, CValue *val); COSMO_API void cosmoV_getMethod(CState *state, CObj *obj, CValue key, CValue *val);
// clears the stack, callstack and restores the state into a usable state after a calloverflow or // check if the value at the top of the stack is a <obj> user type
// another hard to recover error (keeps the global table intact) COSMO_API bool cosmoV_isValueUserType(CState *state, CValue val, int userType);
COSMO_API bool cosmoV_restore(CState *state);
// nice to have wrappers // nice to have wrappers

379
util/linenoise-win32.c Normal file
View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -1,14 +1,12 @@
/* linenoise.h -- VERSION 1.0 /* linenoise.h -- guerrilla line editing library against the idea that a
* * line editing lib needs to be 20,000 lines of C code.
* 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. * See linenoise.c for more information.
* *
* ------------------------------------------------------------------------ * ------------------------------------------------------------------------
* *
* Copyright (c) 2010-2023, Salvatore Sanfilippo <antirez at gmail dot com> * Copyright (c) 2010, Salvatore Sanfilippo <antirez at gmail dot com>
* Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis at gmail dot com> * Copyright (c) 2010, Pieter Noordhuis <pcnoordhuis at gmail dot com>
* *
* All rights reserved. * All rights reserved.
* *
@@ -43,68 +41,109 @@
extern "C" { extern "C" {
#endif #endif
#include <stddef.h> /* For size_t. */ #include <inttypes.h>
#include <string.h>
extern char *linenoiseEditMore;
/* The linenoiseState structure represents the state during line editing.
* We pass this state to functions implementing specific editing
* functionalities. */
struct linenoiseState {
int in_completion; /* The user pressed TAB and we are now in completion
* mode, so input is handled by completeLine(). */
size_t completion_idx; /* Index of next completion to propose. */
int ifd; /* Terminal stdin file descriptor. */
int ofd; /* Terminal stdout file descriptor. */
char *buf; /* Edited line buffer. */
size_t buflen; /* Edited line buffer size. */
const char *prompt; /* Prompt to display. */
size_t plen; /* Prompt length. */
size_t pos; /* Current cursor position. */
size_t oldpos; /* Previous refresh cursor position. */
size_t len; /* Current edited line length. */
size_t cols; /* Number of columns in terminal. */
size_t oldrows; /* Rows used by last refrehsed line (multiline mode) */
int history_index; /* The history index we are currently editing. */
};
#ifndef NO_COMPLETION
typedef struct linenoiseCompletions { typedef struct linenoiseCompletions {
size_t len; size_t len;
char **cvec; char **cvec;
} linenoiseCompletions; } linenoiseCompletions;
/* Non blocking API. */ /*
int linenoiseEditStart(struct linenoiseState *l, int stdin_fd, int stdout_fd, char *buf, size_t buflen, const char *prompt); * The callback type for tab completion handlers.
char *linenoiseEditFeed(struct linenoiseState *l); */
void linenoiseEditStop(struct linenoiseState *l); typedef void(linenoiseCompletionCallback)(const char *prefix, linenoiseCompletions *comp, void *userdata);
void linenoiseHide(struct linenoiseState *l);
void linenoiseShow(struct linenoiseState *l);
/* Blocking API. */ /*
* 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); char *linenoise(const char *prompt);
void linenoiseFree(void *ptr);
/* Completion API. */ /**
typedef void(linenoiseCompletionCallback)(const char *, linenoiseCompletions *); * Like linenoise() but starts with an initial buffer.
typedef char*(linenoiseHintsCallback)(const char *, int *color, int *bold); */
typedef void(linenoiseFreeHintsCallback)(void *); char *linenoiseWithInitial(const char *prompt, const char *initial);
void linenoiseSetCompletionCallback(linenoiseCompletionCallback *);
void linenoiseSetHintsCallback(linenoiseHintsCallback *);
void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *);
void linenoiseAddCompletion(linenoiseCompletions *, const char *);
/* History API. */ /**
* Clear the screen.
*/
void linenoiseClearScreen(void);
/*
* Adds a copy of the given line of the command history.
*/
int linenoiseHistoryAdd(const char *line); 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); 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); 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); int linenoiseHistoryLoad(const char *filename);
/* Other utilities. */ /*
void linenoiseClearScreen(void); * Frees all history entries, clearing the history.
void linenoiseSetMultiLine(int ml); */
void linenoisePrintKeyCodes(void); void linenoiseHistoryFree(void);
void linenoiseMaskModeEnable(void);
void linenoiseMaskModeDisable(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 #ifdef __cplusplus
} }