mirror of
				https://github.com/CPunch/Cosmo.git
				synced 2025-10-26 19:00:05 +00:00 
			
		
		
		
	Compare commits
	
		
			102 Commits
		
	
	
		
			error-hand
			...
			7f5e3ae8dc
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 7f5e3ae8dc | |||
| 7a54230cb9 | |||
| 1a7d6caec6 | |||
| 1678194941 | |||
| 3ea653b26d | |||
| d3de4c0e66 | |||
| d66d4807b3 | |||
| 1fcb35168f | |||
| 611162b3be | |||
| b3587f48a2 | |||
| bf36412699 | |||
| 6701a63a63 | |||
| ffff01e9d1 | |||
| 89be01aaf6 | |||
| cc9eb4a5ec | |||
| 789c5210b4 | |||
| dfdd97e739 | |||
| 096d80d8df | |||
| f7bc8e0471 | |||
| fce568addc | |||
| f5e75f09b9 | |||
| fe136f84b5 | |||
| de8cd481c3 | |||
| 6654c3b91c | |||
| 21f7ea5c14 | |||
| e1591ae3fd | |||
| bfdd33e01d | |||
| c0893b8a14 | |||
| d30bcace9a | |||
| 6a47c82179 | |||
| d41126e75f | |||
| 9f19fd4f31 | |||
| 6126b50941 | |||
| 7fa7eb8d94 | |||
| 0633e87aa6 | |||
| 75d27afe2c | |||
| 3f39211081 | |||
| c5e4305ef8 | |||
| 1a78a9ab5f | |||
| 2f0f675159 | |||
| 7c5d2f6b65 | |||
| f76f2ffa92 | |||
| 155e0829fb | |||
| 2b3825d258 | |||
| 7bca6927a9 | |||
| d3647c161b | |||
| d27d94975e | |||
| 2d0e63f706 | |||
| dfcf0c92b5 | |||
| 447f874eff | |||
| 7b1bd1c9fc | |||
| 9537a2c7b8 | |||
| c44dc88972 | |||
| d581e68166 | |||
| 2271681cec | |||
| cf18bbbe54 | |||
| 3a872fb83f | |||
| 4ed1c79b50 | |||
| bc6eb9b6dc | |||
| 635f31863f | |||
| 49a7f68239 | |||
| 8efecf71a4 | |||
| 395f352c6e | |||
| 65d37838cd | |||
| 3b13ae1624 | |||
| d1a16d990c | |||
| 0a4d36f2f4 | |||
| 8ac8085d20 | |||
| e335fd95d6 | |||
| b902ac90de | |||
| 6056f8eb5b | |||
| 88284a0b6e | |||
|   | 7998c2ab41 | ||
| 7b5825668d | |||
| d13cc398c8 | |||
| 012d3702bf | |||
| d761970f17 | |||
| 0e730b9c51 | |||
| bff2799bb6 | |||
| 07ca82f968 | |||
| b545e8e5e3 | |||
| 55e6453589 | |||
| c83dca2ab2 | |||
| 3890c9dd1e | |||
| 40739e9bea | |||
|   | 1eec23035f | ||
|   | c0274d1d77 | ||
|   | fec26ac380 | ||
| 35466f691f | |||
| 71c8dc7e34 | |||
| 7a6e00be41 | |||
| 14b091b691 | |||
| 5c71efbe40 | |||
| 1fff6c7fe9 | |||
| 1a96e411f2 | |||
| fdd0d19308 | |||
| 33da88a18a | |||
|   | 50b19e9f4f | ||
|   | 472a0ea4c1 | ||
|   | 76574c7860 | ||
|   | 8b931fa4a7 | ||
|   | ce844dc110 | 
							
								
								
									
										45
									
								
								.github/workflows/check_build.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								.github/workflows/check_build.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,45 @@ | |||||||
|  | name: Check Builds | ||||||
|  |  | ||||||
|  | on: | ||||||
|  |   push: | ||||||
|  |     paths: | ||||||
|  |         - src/** | ||||||
|  |         - util/** | ||||||
|  |         - main.c | ||||||
|  |         - Makefile | ||||||
|  |         - CMakeLists.txt | ||||||
|  |         - .github/workflows/check_build.yaml | ||||||
|  |   workflow_dispatch: | ||||||
|  |  | ||||||
|  | jobs: | ||||||
|  |   ubuntu-build: | ||||||
|  |     runs-on: ubuntu-latest | ||||||
|  |     steps: | ||||||
|  |       - uses: actions/checkout@v2 | ||||||
|  |         with: | ||||||
|  |           submodules: recursive | ||||||
|  |       - name: CMake | ||||||
|  |         run: cmake -B build -DCMAKE_BUILD_TYPE=Release | ||||||
|  |       - name: Build | ||||||
|  |         run: cmake --build build --config Release | ||||||
|  |       - name: Upload build artifact | ||||||
|  |         uses: actions/upload-artifact@v2 | ||||||
|  |         with: | ||||||
|  |           name: Cosmo-Ubuntu | ||||||
|  |           path: build/bin | ||||||
|  |  | ||||||
|  |   windows-build: | ||||||
|  |     runs-on: windows-latest | ||||||
|  |     steps: | ||||||
|  |       - uses: actions/checkout@v2 | ||||||
|  |         with: | ||||||
|  |           submodules: recursive | ||||||
|  |       - name: Create CMake build files | ||||||
|  |         run: cmake -B build -DCMAKE_BUILD_TYPE=MinSizeRel | ||||||
|  |       - name: Check compilation | ||||||
|  |         run: cmake --build build --config MinSizeRel | ||||||
|  |       - name: Upload build artifact | ||||||
|  |         uses: actions/upload-artifact@v2 | ||||||
|  |         with: | ||||||
|  |           name: Cosmo-Windows | ||||||
|  |           path: build/bin | ||||||
| @@ -16,7 +16,7 @@ set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT c | |||||||
| include(FetchContent) | include(FetchContent) | ||||||
|  |  | ||||||
| file(GLOB sources CONFIGURE_DEPENDS ${PROJECT_SOURCE_DIR}/src/*.c) | file(GLOB sources CONFIGURE_DEPENDS ${PROJECT_SOURCE_DIR}/src/*.c) | ||||||
| add_executable(${PROJECT_NAME} main.c) | add_executable(${PROJECT_NAME} main.c ${PROJECT_SOURCE_DIR}/util/linenoise.c) | ||||||
| target_sources(${PROJECT_NAME} PRIVATE ${sources}) | target_sources(${PROJECT_NAME} PRIVATE ${sources}) | ||||||
|  |  | ||||||
| IF (NOT WIN32) | IF (NOT WIN32) | ||||||
|   | |||||||
							
								
								
									
										4
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								Makefile
									
									
									
									
									
								
							| @@ -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 | ||||||
|  |  | ||||||
| @@ -21,6 +21,7 @@ CHDR=\ | |||||||
| 	src/cbaselib.h\ | 	src/cbaselib.h\ | ||||||
| 	src/cdump.h\ | 	src/cdump.h\ | ||||||
| 	src/cundump.h\ | 	src/cundump.h\ | ||||||
|  | 	util/linenoise.h\ | ||||||
|  |  | ||||||
| CSRC=\ | CSRC=\ | ||||||
| 	src/cchunk.c\ | 	src/cchunk.c\ | ||||||
| @@ -37,6 +38,7 @@ CSRC=\ | |||||||
| 	src/cbaselib.c\ | 	src/cbaselib.c\ | ||||||
| 	src/cdump.c\ | 	src/cdump.c\ | ||||||
| 	src/cundump.c\ | 	src/cundump.c\ | ||||||
|  | 	util/linenoise.c\ | ||||||
| 	main.c\ | 	main.c\ | ||||||
|  |  | ||||||
| COBJ=$(CSRC:.c=.o) | COBJ=$(CSRC:.c=.o) | ||||||
|   | |||||||
| @@ -1,5 +1,5 @@ | |||||||
| # Cosmo | # Cosmo | ||||||
| [](https://ci.appveyor.com/project/CPunch/Cosmo) | [](https://github.com/CPunch/Cosmo/actions/workflows/check_build.yaml) | ||||||
|  |  | ||||||
| ## Usage | ## Usage | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										33
									
								
								appveyor.yml
									
									
									
									
									
								
							
							
						
						
									
										33
									
								
								appveyor.yml
									
									
									
									
									
								
							| @@ -1,33 +0,0 @@ | |||||||
| version: 'cosmo-0.1.{build}' |  | ||||||
|  |  | ||||||
| # we compile every commit under all branches |  | ||||||
| #branch: |  | ||||||
| # only: |  | ||||||
| #  - main |  | ||||||
|  |  | ||||||
| # only run CI if we changed actual code |  | ||||||
| only_commits: |  | ||||||
|   files: |  | ||||||
|     - src/ |  | ||||||
|     - main.c |  | ||||||
|     - Makefile |  | ||||||
|     - CMakeLists.txt |  | ||||||
|     - appveyor.yml |  | ||||||
|  |  | ||||||
| # images we're using |  | ||||||
| image: |  | ||||||
|   - Ubuntu2004 |  | ||||||
|  |  | ||||||
| platform: |  | ||||||
|   - x64 |  | ||||||
|  |  | ||||||
| install: |  | ||||||
|   - sudo apt install clang cmake -y |  | ||||||
|  |  | ||||||
| build_script: |  | ||||||
|   - make && ./bin/cosmo examples/testsuite.cosmo -s examples/getters_setters.cosmo |  | ||||||
|  |  | ||||||
| artifacts: |  | ||||||
|   - path: bin |  | ||||||
|     name: ubuntu20_04-bin-x64 |  | ||||||
|     type: zip |  | ||||||
| @@ -59,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
									
								
							
							
						
						
									
										6
									
								
								examples/reader.cosmo
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										6
									
								
								examples/writer.cosmo
									
									
									
									
									
										Normal 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") | ||||||
							
								
								
									
										15
									
								
								main.c
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								main.c
									
									
									
									
									
								
							| @@ -8,6 +8,8 @@ | |||||||
| #include "cundump.h" | #include "cundump.h" | ||||||
| #include "cvm.h" | #include "cvm.h" | ||||||
|  |  | ||||||
|  | #include "util/linenoise.h" | ||||||
|  |  | ||||||
| #include <stdio.h> | #include <stdio.h> | ||||||
| #include <stdlib.h> | #include <stdlib.h> | ||||||
| #ifdef _WIN32 | #ifdef _WIN32 | ||||||
| @@ -63,7 +65,7 @@ static bool interpret(CState *state, const char *script, const char *mod) | |||||||
|  |  | ||||||
| static void repl(CState *state) | static void repl(CState *state) | ||||||
| { | { | ||||||
|     char line[1024]; |     char *line; | ||||||
|     _ACTIVE = true; |     _ACTIVE = true; | ||||||
|  |  | ||||||
|     // add our custom REPL functions |     // add our custom REPL functions | ||||||
| @@ -73,17 +75,16 @@ 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) { | ||||||
|         printf("> "); |         if (!(line = linenoise("> "))) { // better than gets() | ||||||
|  |  | ||||||
|         if (!fgets(line, sizeof(line), stdin)) { // better than gets() |  | ||||||
|             printf("\n> "); |  | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         linenoiseHistoryAdd(line); | ||||||
|         interpret(state, line, "REPL"); |         interpret(state, line, "REPL"); | ||||||
|  |         free(line); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -129,7 +130,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); | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										242
									
								
								src/cbaselib.c
									
									
									
									
									
								
							
							
						
						
									
										242
									
								
								src/cbaselib.c
									
									
									
									
									
								
							| @@ -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,17 @@ 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 |     return 1; // 1 result | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -230,54 +239,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 != 1) { |     if (nargs != 2) { | ||||||
|         cosmoV_error(state, "os.read() expected 1 argument, got %d!", nargs); |         cosmoV_error(state, "file:read() expected 2 arguments, 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:read()", "<file>, <number> or \"a\"", "%s, %s", | ||||||
|  |                          cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1])); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     CObjString *str = cosmoV_readString(args[0]); |     CObjObject *fileObj = cosmoV_readObject(args[0]); | ||||||
|  |     FILE *file = cosmoO_getUserP(fileObj); | ||||||
|  |  | ||||||
|     // open file |     if (IS_NUMBER(args[1])) { | ||||||
|     FILE *file = fopen(str->str, "rb"); |         CValue temp; | ||||||
|     char *buf; |         char *buffer; | ||||||
|     size_t size, bRead; |         cosmo_Number length = cosmoV_readNumber(args[1]); | ||||||
|  |  | ||||||
|     if (file == NULL) { |         // make sure the length is within the bounds of the file | ||||||
|         // return nil, file doesn't exist |         if (length < 0) { | ||||||
|         return 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, length + 1); | ||||||
|  |  | ||||||
|  |         // read the data | ||||||
|  |         fread(buffer, sizeof(char), length, file); | ||||||
|  |         buffer[(int)length] = '\0'; // write the NULL terminator | ||||||
|  |  | ||||||
|  |         // push the read data | ||||||
|  |         temp = cosmoV_newRef(cosmoO_takeString(state, buffer, 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, length + 1); | ||||||
|  |  | ||||||
|  |             // read the data | ||||||
|  |             fread(buffer, sizeof(char), length, file); | ||||||
|  |             buffer[length] = '\0'; // write the NULL terminator | ||||||
|  |  | ||||||
|  |             // push the read data | ||||||
|  |             temp = cosmoV_newRef(cosmoO_takeString(state, buffer, 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])); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // grab the size of the file |  | ||||||
|     fseek(file, 0L, SEEK_END); |  | ||||||
|     size = ftell(file); |  | ||||||
|     rewind(file); |  | ||||||
|  |  | ||||||
|     buf = cosmoM_xmalloc(state, size + 1);        // +1 for the NULL terminator |  | ||||||
|     bRead = fread(buf, sizeof(char), size, file); // read the file into the buffer |  | ||||||
|  |  | ||||||
|     if (bRead < size) { |  | ||||||
|         // an error occured! we don't need to really throw an error, returning a nil is good enough |  | ||||||
|         return 0; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     buf[bRead] = '\0'; // place the NULL terminator at the end of the buffer |  | ||||||
|  |  | ||||||
|     // push the string to the stack to return |  | ||||||
|     cosmoV_pushValue(state, cosmoV_newRef(cosmoO_takeString(state, buf, bRead))); |  | ||||||
|     return 1; |     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) { | ||||||
|  |         cosmoV_error(state, "file:read() expected 1 argument, got %d!", nargs); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (!cosmoV_isValueUserType(state, args[0], COSMO_USER_FILE)) { | ||||||
|  |         cosmoV_typeError(state, "file:__gc()", "<file>", "%s", | ||||||
|  |                          cosmoV_typeStr(args[0])); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     CObjObject *fileObj = cosmoV_readObject(args[0]); | ||||||
|  |     FILE *file = cosmoO_getUserP(fileObj); | ||||||
|  |  | ||||||
|  |     fclose(file); | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | CObjObject *pushFileObj(CState *state, FILE *file) | ||||||
|  | { | ||||||
|  |     CObjObject *fileObj = cosmoO_newObject(state); | ||||||
|  |     cosmoV_pushRef(state, (CObj *)fileObj); | ||||||
|  |     cosmoO_setUserP(fileObj, file); | ||||||
|  |     cosmoO_setUserT(fileObj, COSMO_USER_FILE); | ||||||
|  |  | ||||||
|  |     // grab and set proto from the registry | ||||||
|  |     cosmoV_pushRef(state, (CObj *)fileObj); | ||||||
|  |     cosmoV_pushString(state, "file"); | ||||||
|  |     cosmoV_getRegistry(state); | ||||||
|  |     cosmoV_setProto(state); | ||||||
|  |  | ||||||
|  |     cosmoO_lock(fileObj); | ||||||
|  |     return fileObj; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int cosmoB_osOpen(CState *state, int nargs, CValue *args) | ||||||
|  | { | ||||||
|  |     const char *filePath, *mode = "rb"; | ||||||
|  |     FILE *file; | ||||||
|  |  | ||||||
|  |     if (nargs >= 1) { | ||||||
|  |         if (!IS_STRING(args[0])) { | ||||||
|  |             cosmoV_typeError(state, "os.open()", "<string>", "%s", cosmoV_typeStr(args[0])); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (nargs == 2) { | ||||||
|  |             if (!IS_STRING(args[1])) { | ||||||
|  |                 cosmoV_typeError(state, "os.open()", "<string>, <string>", "%s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1])); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             mode = cosmoV_readCString(args[1]); | ||||||
|  |         } else if (nargs != 1) { | ||||||
|  |             cosmoV_error(state, "os.open() expected 1 or 2 arguments, got %d!", nargs); | ||||||
|  |         } | ||||||
|  |     } else { | ||||||
|  |         cosmoV_error(state, "os.open() expected 1 or 2 arguments, got %d!", nargs); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     filePath = cosmoV_readCString(args[0]); | ||||||
|  |     file = fopen(filePath, mode); | ||||||
|  |     if (file == NULL) { | ||||||
|  |         cosmoV_pushBoolean(state, true); | ||||||
|  |         cosmoV_pushFString(state, "Failed to open %s!", filePath); | ||||||
|  |         return 2; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     cosmoV_pushBoolean(state, false); | ||||||
|  |     pushFileObj(state, file); | ||||||
|  |     return 2; | ||||||
|  | } | ||||||
|  |  | ||||||
| // os.time() | // os.time() | ||||||
| int cosmoB_osTime(CState *state, int nargs, CValue *args) | int cosmoB_osTime(CState *state, int nargs, CValue *args) | ||||||
| { | { | ||||||
| @@ -309,9 +440,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 +452,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 +699,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 +732,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 +927,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 +1078,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); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -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 | ||||||
|   | |||||||
| @@ -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) | ||||||
|   | |||||||
							
								
								
									
										79
									
								
								src/cmem.c
									
									
									
									
									
								
							
							
						
						
									
										79
									
								
								src/cmem.c
									
									
									
									
									
								
							| @@ -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) | ||||||
| @@ -56,7 +57,7 @@ void *cosmoM_reallocate(CState *state, void *buf, size_t oldSize, size_t newSize | |||||||
| #endif | #endif | ||||||
|  |  | ||||||
|     if (newBuf == NULL) { |     if (newBuf == NULL) { | ||||||
|         CERROR("failed to allocate memory!"); |         printf("[ERROR] failed to allocate memory!"); | ||||||
|         exit(1); |         exit(1); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -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; | ||||||
|     } |     } | ||||||
| @@ -246,22 +248,23 @@ static void sweep(CState *state) | |||||||
|                 prev->next = object; |                 prev->next = object; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|  |             // call __gc on the object | ||||||
|  |             CObjObject *protoObject = cosmoO_grabProto(oldObj); | ||||||
|  |             CValue res; | ||||||
|  |  | ||||||
|  |             // use user-defined __gc | ||||||
|  |             if (protoObject != NULL && cosmoO_getIString(state, protoObject, ISTRING_GC, &res)) { | ||||||
|  |                 cosmoV_pushValue(state, res); | ||||||
|  |                 cosmoV_pushRef(state, (CObj *)oldObj); | ||||||
|  |                 cosmoV_call(state, 1, 0); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |  | ||||||
|             cosmoO_free(state, oldObj); |             cosmoO_free(state, oldObj); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| static void 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 +285,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 +327,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; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|   | |||||||
| @@ -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) | ||||||
| { | { | ||||||
|   | |||||||
							
								
								
									
										41
									
								
								src/cobj.c
									
									
									
									
									
								
							
							
						
						
									
										41
									
								
								src/cobj.c
									
									
									
									
									
								
							| @@ -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) | ||||||
| @@ -493,6 +493,7 @@ void cosmoO_unlock(CObjObject *object) | |||||||
|     object->isLocked = false; |     object->isLocked = false; | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| bool rawgetIString(CState *state, CObjObject *object, int flag, CValue *val) | bool rawgetIString(CState *state, CObjObject *object, int flag, CValue *val) | ||||||
| { | { | ||||||
|     if (readFlag(object->istringFlags, flag)) |     if (readFlag(object->istringFlags, flag)) | ||||||
| @@ -519,7 +520,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 +528,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 +543,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) | ||||||
|   | |||||||
| @@ -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 | ||||||
| @@ -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 | ||||||
|   | |||||||
| @@ -64,6 +64,4 @@ typedef int (*cosmo_Writer)(CState *state, const void *data, size_t size, const | |||||||
| #define UNNAMEDCHUNK    "_main" | #define UNNAMEDCHUNK    "_main" | ||||||
| #define COSMOASSERT(x)  assert(x) | #define COSMOASSERT(x)  assert(x) | ||||||
|  |  | ||||||
| #define CERROR(err)     printf("%s : %s\n", "[ERROR]", err) |  | ||||||
|  |  | ||||||
| #endif | #endif | ||||||
|   | |||||||
							
								
								
									
										64
									
								
								src/cstate.c
									
									
									
									
									
								
							
							
						
						
									
										64
									
								
								src/cstate.c
									
									
									
									
									
								
							| @@ -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; | ||||||
|  |  | ||||||
| @@ -32,7 +33,7 @@ CState *cosmoV_newState() | |||||||
|     CState *state = malloc(sizeof(CState)); |     CState *state = malloc(sizeof(CState)); | ||||||
|  |  | ||||||
|     if (state == NULL) { |     if (state == NULL) { | ||||||
|         CERROR("failed to allocate memory!"); |         printf("[ERROR] failed to allocate memory!"); | ||||||
|         exit(1); |         exit(1); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -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,6 +153,48 @@ 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"); | ||||||
|   | |||||||
							
								
								
									
										20
									
								
								src/cstate.h
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								src/cstate.h
									
									
									
									
									
								
							| @@ -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,6 +57,7 @@ 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 | ||||||
|  |  | ||||||
| @@ -62,14 +65,12 @@ struct CState | |||||||
|     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); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -87,8 +87,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 | ||||||
|   | |||||||
							
								
								
									
										166
									
								
								src/cvm.c
									
									
									
									
									
								
							
							
						
						
									
										166
									
								
								src/cvm.c
									
									
									
									
									
								
							| @@ -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_printError(CState *state, CObjError *err) | ||||||
| { | { | ||||||
|     // print stack trace |     // print stack trace | ||||||
|     for (int i = 0; i < err->frameCount; i++) { |     for (int i = 0; i < err->frameCount; i++) { | ||||||
| @@ -122,6 +123,7 @@ 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 { | ||||||
| @@ -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 | ||||||
| @@ -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,17 @@ 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 +696,7 @@ static inline uint16_t READUINT(CCallFrame *frame) | |||||||
| #    define SWITCH switch (READBYTE(frame)) | #    define SWITCH switch (READBYTE(frame)) | ||||||
| #    define DEFAULT                                                                                \ | #    define DEFAULT                                                                                \ | ||||||
|     default:                                                                                       \ |     default:                                                                                       \ | ||||||
|         CERROR("unknown opcode!");                                                                 \ |         printf("[ERROR] unknown opcode!");                                                         \ | ||||||
|         exit(0) |         exit(0) | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| @@ -849,18 +849,16 @@ 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) : | ||||||
|   | |||||||
							
								
								
									
										25
									
								
								src/cvm.h
									
									
									
									
									
								
							
							
						
						
									
										25
									
								
								src/cvm.h
									
									
									
									
									
								
							| @@ -36,11 +36,11 @@ COSMO_API void cosmoV_pushFString(CState *state, const char *format, ...); | |||||||
| COSMO_API void cosmoV_printError(CState *state, CObjError *err); | COSMO_API void cosmoV_printError(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,20 @@ 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
									
								
							
							
						
						
									
										379
									
								
								util/linenoise-win32.c
									
									
									
									
									
										Normal 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(¤t) == 0) { | ||||||
|  |         COORD topleft = { 0, 0 }; | ||||||
|  |         DWORD n; | ||||||
|  |  | ||||||
|  |         FillConsoleOutputCharacter(current.outh, ' ', | ||||||
|  |             current.cols * current.rows, topleft, &n); | ||||||
|  |         FillConsoleOutputAttribute(current.outh, | ||||||
|  |             FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN, | ||||||
|  |             current.cols * current.rows, topleft, &n); | ||||||
|  |         SetConsoleCursorPosition(current.outh, topleft); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void cursorToLeft(struct current *current) | ||||||
|  | { | ||||||
|  |     COORD pos; | ||||||
|  |     DWORD n; | ||||||
|  |  | ||||||
|  |     pos.X = 0; | ||||||
|  |     pos.Y = (SHORT)current->y; | ||||||
|  |  | ||||||
|  |     FillConsoleOutputAttribute(current->outh, | ||||||
|  |         FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN, current->cols, pos, &n); | ||||||
|  |     current->x = 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #ifdef USE_UTF8 | ||||||
|  | static void flush_ubuf(struct current *current) | ||||||
|  | { | ||||||
|  |     COORD pos; | ||||||
|  |     DWORD nwritten; | ||||||
|  |     pos.Y = (SHORT)current->y; | ||||||
|  |     pos.X = (SHORT)current->x; | ||||||
|  |     SetConsoleCursorPosition(current->outh, pos); | ||||||
|  |     WriteConsoleW(current->outh, current->ubuf, current->ubuflen, &nwritten, 0); | ||||||
|  |     current->x += current->ubufcols; | ||||||
|  |     current->ubuflen = 0; | ||||||
|  |     current->ubufcols = 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void add_ubuf(struct current *current, int ch) | ||||||
|  | { | ||||||
|  |     /* This code originally by: Author: Mark E. Davis, 1994. */ | ||||||
|  |     static const int halfShift  = 10; /* used for shifting by 10 bits */ | ||||||
|  |  | ||||||
|  |     static const DWORD halfBase = 0x0010000UL; | ||||||
|  |     static const DWORD halfMask = 0x3FFUL; | ||||||
|  |  | ||||||
|  |     #define UNI_SUR_HIGH_START  0xD800 | ||||||
|  |     #define UNI_SUR_HIGH_END    0xDBFF | ||||||
|  |     #define UNI_SUR_LOW_START   0xDC00 | ||||||
|  |     #define UNI_SUR_LOW_END     0xDFFF | ||||||
|  |  | ||||||
|  |     #define UNI_MAX_BMP 0x0000FFFF | ||||||
|  |  | ||||||
|  |     if (ch > UNI_MAX_BMP) { | ||||||
|  |         /* convert from unicode to utf16 surrogate pairs | ||||||
|  |          * There is always space for one extra word in ubuf | ||||||
|  |          */ | ||||||
|  |         ch -= halfBase; | ||||||
|  |         current->ubuf[current->ubuflen++] = (WORD)((ch >> halfShift) + UNI_SUR_HIGH_START); | ||||||
|  |         current->ubuf[current->ubuflen++] = (WORD)((ch & halfMask) + UNI_SUR_LOW_START); | ||||||
|  |     } | ||||||
|  |     else { | ||||||
|  |         current->ubuf[current->ubuflen++] = ch; | ||||||
|  |     } | ||||||
|  |     current->ubufcols += utf8_width(ch); | ||||||
|  |     if (current->ubuflen >= UBUF_MAX_CHARS) { | ||||||
|  |         flush_ubuf(current); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | static int flushOutput(struct current *current) | ||||||
|  | { | ||||||
|  |     const char *pt = sb_str(current->output); | ||||||
|  |     int len = sb_len(current->output); | ||||||
|  |  | ||||||
|  | #ifdef USE_UTF8 | ||||||
|  |     /* convert utf8 in current->output into utf16 in current->ubuf | ||||||
|  |      */ | ||||||
|  |     while (len) { | ||||||
|  |         int ch; | ||||||
|  |         int n = utf8_tounicode(pt, &ch); | ||||||
|  |  | ||||||
|  |         pt += n; | ||||||
|  |         len -= n; | ||||||
|  |  | ||||||
|  |         add_ubuf(current, ch); | ||||||
|  |     } | ||||||
|  |     flush_ubuf(current); | ||||||
|  | #else | ||||||
|  |     DWORD nwritten; | ||||||
|  |     COORD pos; | ||||||
|  |  | ||||||
|  |     pos.Y = (SHORT)current->y; | ||||||
|  |     pos.X = (SHORT)current->x; | ||||||
|  |  | ||||||
|  |     SetConsoleCursorPosition(current->outh, pos); | ||||||
|  |     WriteConsoleA(current->outh, pt, len, &nwritten, 0); | ||||||
|  |  | ||||||
|  |     current->x += len; | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |     sb_clear(current->output); | ||||||
|  |  | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int outputChars(struct current *current, const char *buf, int len) | ||||||
|  | { | ||||||
|  |     if (len < 0) { | ||||||
|  |         len = strlen(buf); | ||||||
|  |     } | ||||||
|  |     assert(current->output); | ||||||
|  |  | ||||||
|  |     sb_append_len(current->output, buf, len); | ||||||
|  |  | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void outputNewline(struct current *current) | ||||||
|  | { | ||||||
|  |     /* On the last row output a newline to force a scroll */ | ||||||
|  |     if (current->y + 1 == current->rows) { | ||||||
|  |         outputChars(current, "\n", 1); | ||||||
|  |     } | ||||||
|  |     flushOutput(current); | ||||||
|  |     current->x = 0; | ||||||
|  |     current->y++; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void setOutputHighlight(struct current *current, const int *props, int nprops) | ||||||
|  | { | ||||||
|  |     int colour = FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN; | ||||||
|  |     int bold = 0; | ||||||
|  |     int reverse = 0; | ||||||
|  |     int i; | ||||||
|  |  | ||||||
|  |     for (i = 0; i < nprops; i++) { | ||||||
|  |         switch (props[i]) { | ||||||
|  |             case 0: | ||||||
|  |                colour = FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN; | ||||||
|  |                bold = 0; | ||||||
|  |                reverse = 0; | ||||||
|  |                break; | ||||||
|  |             case 1: | ||||||
|  |                bold = FOREGROUND_INTENSITY; | ||||||
|  |                break; | ||||||
|  |             case 7: | ||||||
|  |                reverse = 1; | ||||||
|  |                break; | ||||||
|  |             case 30: | ||||||
|  |                colour = 0; | ||||||
|  |                break; | ||||||
|  |             case 31: | ||||||
|  |                colour = FOREGROUND_RED; | ||||||
|  |                break; | ||||||
|  |             case 32: | ||||||
|  |                colour = FOREGROUND_GREEN; | ||||||
|  |                break; | ||||||
|  |             case 33: | ||||||
|  |                colour = FOREGROUND_RED | FOREGROUND_GREEN; | ||||||
|  |                break; | ||||||
|  |             case 34: | ||||||
|  |                colour = FOREGROUND_BLUE; | ||||||
|  |                break; | ||||||
|  |             case 35: | ||||||
|  |                colour = FOREGROUND_RED | FOREGROUND_BLUE; | ||||||
|  |                break; | ||||||
|  |             case 36: | ||||||
|  |                colour = FOREGROUND_BLUE | FOREGROUND_GREEN; | ||||||
|  |                break; | ||||||
|  |             case 37: | ||||||
|  |                colour = FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN; | ||||||
|  |                break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     flushOutput(current); | ||||||
|  |  | ||||||
|  |     if (reverse) { | ||||||
|  |         SetConsoleTextAttribute(current->outh, BACKGROUND_INTENSITY); | ||||||
|  |     } | ||||||
|  |     else { | ||||||
|  |         SetConsoleTextAttribute(current->outh, colour | bold); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void eraseEol(struct current *current) | ||||||
|  | { | ||||||
|  |     COORD pos; | ||||||
|  |     DWORD n; | ||||||
|  |  | ||||||
|  |     pos.X = (SHORT) current->x; | ||||||
|  |     pos.Y = (SHORT) current->y; | ||||||
|  |  | ||||||
|  |     FillConsoleOutputCharacter(current->outh, ' ', current->cols - current->x, pos, &n); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void setCursorXY(struct current *current) | ||||||
|  | { | ||||||
|  |     COORD pos; | ||||||
|  |  | ||||||
|  |     pos.X = (SHORT) current->x; | ||||||
|  |     pos.Y = (SHORT) current->y; | ||||||
|  |  | ||||||
|  |     SetConsoleCursorPosition(current->outh, pos); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static void setCursorPos(struct current *current, int x) | ||||||
|  | { | ||||||
|  |     current->x = x; | ||||||
|  |     setCursorXY(current); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void cursorUp(struct current *current, int n) | ||||||
|  | { | ||||||
|  |     current->y -= n; | ||||||
|  |     setCursorXY(current); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void cursorDown(struct current *current, int n) | ||||||
|  | { | ||||||
|  |     current->y += n; | ||||||
|  |     setCursorXY(current); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int fd_read(struct current *current) | ||||||
|  | { | ||||||
|  |     while (1) { | ||||||
|  |         INPUT_RECORD irec; | ||||||
|  |         DWORD n; | ||||||
|  |         if (WaitForSingleObject(current->inh, INFINITE) != WAIT_OBJECT_0) { | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         if (!ReadConsoleInputW(current->inh, &irec, 1, &n)) { | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         if (irec.EventType == KEY_EVENT) { | ||||||
|  |             KEY_EVENT_RECORD *k = &irec.Event.KeyEvent; | ||||||
|  |             if (k->bKeyDown || k->wVirtualKeyCode == VK_MENU) { | ||||||
|  |                 if (k->dwControlKeyState & ENHANCED_KEY) { | ||||||
|  |                     switch (k->wVirtualKeyCode) { | ||||||
|  |                      case VK_LEFT: | ||||||
|  |                         return SPECIAL_LEFT; | ||||||
|  |                      case VK_RIGHT: | ||||||
|  |                         return SPECIAL_RIGHT; | ||||||
|  |                      case VK_UP: | ||||||
|  |                         return SPECIAL_UP; | ||||||
|  |                      case VK_DOWN: | ||||||
|  |                         return SPECIAL_DOWN; | ||||||
|  |                      case VK_INSERT: | ||||||
|  |                         return SPECIAL_INSERT; | ||||||
|  |                      case VK_DELETE: | ||||||
|  |                         return SPECIAL_DELETE; | ||||||
|  |                      case VK_HOME: | ||||||
|  |                         return SPECIAL_HOME; | ||||||
|  |                      case VK_END: | ||||||
|  |                         return SPECIAL_END; | ||||||
|  |                      case VK_PRIOR: | ||||||
|  |                         return SPECIAL_PAGE_UP; | ||||||
|  |                      case VK_NEXT: | ||||||
|  |                         return SPECIAL_PAGE_DOWN; | ||||||
|  |                      case VK_RETURN: | ||||||
|  |                         return k->uChar.UnicodeChar; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 /* Note that control characters are already translated in AsciiChar */ | ||||||
|  |                 else if (k->wVirtualKeyCode == VK_CONTROL) | ||||||
|  |                     continue; | ||||||
|  |                 else { | ||||||
|  |                     return k->uChar.UnicodeChar; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return -1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int getWindowSize(struct current *current) | ||||||
|  | { | ||||||
|  |     CONSOLE_SCREEN_BUFFER_INFO info; | ||||||
|  |     if (!GetConsoleScreenBufferInfo(current->outh, &info)) { | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |     current->cols = info.dwSize.X; | ||||||
|  |     current->rows = info.dwSize.Y; | ||||||
|  |     if (current->cols <= 0 || current->rows <= 0) { | ||||||
|  |         current->cols = 80; | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |     current->y = info.dwCursorPosition.Y; | ||||||
|  |     current->x = info.dwCursorPosition.X; | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
							
								
								
									
										2786
									
								
								util/linenoise.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2786
									
								
								util/linenoise.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										152
									
								
								util/linenoise.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										152
									
								
								util/linenoise.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,152 @@ | |||||||
|  | /* linenoise.h -- guerrilla line editing library against the idea that a | ||||||
|  |  * line editing lib needs to be 20,000 lines of C code. | ||||||
|  |  * | ||||||
|  |  * See linenoise.c for more information. | ||||||
|  |  * | ||||||
|  |  * ------------------------------------------------------------------------ | ||||||
|  |  * | ||||||
|  |  * Copyright (c) 2010, Salvatore Sanfilippo <antirez at gmail dot com> | ||||||
|  |  * Copyright (c) 2010, Pieter Noordhuis <pcnoordhuis at gmail dot com> | ||||||
|  |  * | ||||||
|  |  * All rights reserved. | ||||||
|  |  * | ||||||
|  |  * Redistribution and use in source and binary forms, with or without | ||||||
|  |  * modification, are permitted provided that the following conditions are | ||||||
|  |  * met: | ||||||
|  |  * | ||||||
|  |  *  *  Redistributions of source code must retain the above copyright | ||||||
|  |  *     notice, this list of conditions and the following disclaimer. | ||||||
|  |  * | ||||||
|  |  *  *  Redistributions in binary form must reproduce the above copyright | ||||||
|  |  *     notice, this list of conditions and the following disclaimer in the | ||||||
|  |  *     documentation and/or other materials provided with the distribution. | ||||||
|  |  * | ||||||
|  |  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||||
|  |  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||||
|  |  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||||
|  |  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||||
|  |  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||||
|  |  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||||
|  |  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||||
|  |  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||||
|  |  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||||
|  |  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||||
|  |  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #ifndef __LINENOISE_H | ||||||
|  | #define __LINENOISE_H | ||||||
|  |  | ||||||
|  | #ifdef __cplusplus | ||||||
|  | extern "C" { | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #include <inttypes.h> | ||||||
|  | #include <string.h> | ||||||
|  |  | ||||||
|  | #ifndef NO_COMPLETION | ||||||
|  | typedef struct linenoiseCompletions { | ||||||
|  |   size_t len; | ||||||
|  |   char **cvec; | ||||||
|  | } linenoiseCompletions; | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * The callback type for tab completion handlers. | ||||||
|  |  */ | ||||||
|  | typedef void(linenoiseCompletionCallback)(const char *prefix, linenoiseCompletions *comp, void *userdata); | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Sets the current tab completion handler and returns the previous one, or NULL | ||||||
|  |  * if no prior one has been set. | ||||||
|  |  */ | ||||||
|  | linenoiseCompletionCallback * linenoiseSetCompletionCallback(linenoiseCompletionCallback *comp, void *userdata); | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Adds a copy of the given string to the given completion list. The copy is owned | ||||||
|  |  * by the linenoiseCompletions object. | ||||||
|  |  */ | ||||||
|  | void linenoiseAddCompletion(linenoiseCompletions *comp, const char *str); | ||||||
|  |  | ||||||
|  | typedef char*(linenoiseHintsCallback)(const char *, int *color, int *bold, void *userdata); | ||||||
|  | typedef void(linenoiseFreeHintsCallback)(void *hint, void *userdata); | ||||||
|  | void linenoiseSetHintsCallback(linenoiseHintsCallback *callback, void *userdata); | ||||||
|  | void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *callback); | ||||||
|  |  | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Prompts for input using the given string as the input | ||||||
|  |  * prompt. Returns when the user has tapped ENTER or (on an empty | ||||||
|  |  * line) EOF (Ctrl-D on Unix, Ctrl-Z on Windows). Returns either | ||||||
|  |  * a copy of the entered string (for ENTER) or NULL (on EOF).  The | ||||||
|  |  * caller owns the returned string and must eventually free() it. | ||||||
|  |  */ | ||||||
|  | char *linenoise(const char *prompt); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Like linenoise() but starts with an initial buffer. | ||||||
|  |  */ | ||||||
|  | char *linenoiseWithInitial(const char *prompt, const char *initial); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Clear the screen. | ||||||
|  |  */ | ||||||
|  | void linenoiseClearScreen(void); | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Adds a copy of the given line of the command history. | ||||||
|  |  */ | ||||||
|  | int linenoiseHistoryAdd(const char *line); | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Sets the maximum length of the command history, in lines. | ||||||
|  |  * If the history is currently longer, it will be trimmed, | ||||||
|  |  * retaining only the most recent entries. If len is 0 or less | ||||||
|  |  * then this function does nothing. | ||||||
|  |  */ | ||||||
|  | int linenoiseHistorySetMaxLen(int len); | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Returns the current maximum length of the history, in lines. | ||||||
|  |  */ | ||||||
|  | int linenoiseHistoryGetMaxLen(void); | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Saves the current contents of the history to the given file. | ||||||
|  |  * Returns 0 on success. | ||||||
|  |  */ | ||||||
|  | int linenoiseHistorySave(const char *filename); | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Replaces the current history with the contents | ||||||
|  |  * of the given file.  Returns 0 on success. | ||||||
|  |  */ | ||||||
|  | int linenoiseHistoryLoad(const char *filename); | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Frees all history entries, clearing the history. | ||||||
|  |  */ | ||||||
|  | void linenoiseHistoryFree(void); | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Returns a pointer to the list of history entries, writing its | ||||||
|  |  * length to *len if len is not NULL. The memory is owned by linenoise | ||||||
|  |  * and must not be freed. | ||||||
|  |  */ | ||||||
|  | char **linenoiseHistory(int *len); | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Returns the number of display columns in the current terminal. | ||||||
|  |  */ | ||||||
|  | int linenoiseColumns(void); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Enable or disable multiline mode (disabled by default) | ||||||
|  |  */ | ||||||
|  | void linenoiseSetMultiLine(int enableml); | ||||||
|  |  | ||||||
|  | #ifdef __cplusplus | ||||||
|  | } | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #endif /* __LINENOISE_H */ | ||||||
		Reference in New Issue
	
	Block a user