├── .gitignore ├── .travis.yml ├── LICENSE.md ├── Makefile ├── Makefile.win32 ├── README.md ├── core ├── ast.c ├── ast.h ├── common.c ├── common.h ├── linenoise │ ├── linenoise.c │ └── linenoise.h ├── node.c ├── node.h ├── object.c ├── object.h ├── parser.l ├── parser.y ├── scanner_state.h ├── solid.c ├── solid.h ├── utils.c ├── utils.h ├── vm.c └── vm.h ├── etc └── solid-mode.el ├── lib ├── _fn.c ├── _math.c ├── fn.sol └── math.sol └── test.sol /.gitignore: -------------------------------------------------------------------------------- 1 | session.vim 2 | solid 3 | deps 4 | .ycm* 5 | *.o 6 | *.a 7 | *.so 8 | *.so.* 9 | parser.h 10 | parser.c 11 | lexer.h 12 | lexer.c 13 | *.exe 14 | lib_* 15 | *.dll 16 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | install: make deps 2 | script: make 3 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Samuel Breese 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | EXEC = solid 2 | OBJECTS = core/linenoise/linenoise.o core/solid.o core/utils.o core/node.o core/vm.o core/object.o core/ast.o core/common.o 3 | CFLAGS = -std=c99 -I/usr/local/include -I. -Icore -Wall -g -fPIC 4 | INSTALL = install 5 | INSTALL_PROGRAM = $(INSTALL) 6 | INSTALL_DATA = $(INSTALL) -m 644 7 | PREFIX_DIR ?= /usr/local 8 | INSTALL_DIR ?= $(PREFIX_DIR)/bin 9 | datarootdir = $(PREFIX_DIR)/share 10 | datadir = $(datarootdir) 11 | libdir = $(PREFIX_DIR)/lib 12 | includedir = $(PREFIX_DIR)/include 13 | 14 | all: $(EXEC) 15 | 16 | $(EXEC): deps parser.o lexer.o $(OBJECTS) 17 | $(CC) parser.o lexer.o $(OBJECTS) -ldl -g -Wall -Werror -o $(EXEC) 18 | 19 | parser.o: core/parser.y 20 | bison -d core/parser.y 21 | $(CC) $(CFLAGS) -Wno-implicit-function-declaration -c parser.c -o parser.o -g 22 | 23 | lexer.o: core/parser.l 24 | flex core/parser.l 25 | $(CC) $(CFLAGS) -Wno-unneeded-internal-declaration -Wno-implicit-function-declaration -c lexer.c -o lexer.o -g 26 | 27 | tarball: 28 | mkdir solid-$(VERSION) 29 | cp -r Makefile parser core solid-$(VERSION) 30 | tar -czf solid-$(VERSION).tar.gz solid-$(VERSION) 31 | 32 | install: all shared 33 | mkdir -p $(INSTALL_DIR) 34 | $(INSTALL_PROGRAM) solid $(INSTALL_DIR)/solid 35 | $(INSTALL_DATA) libsolid.so.1.0 $(libdir) 36 | ln -sf $(libdir)/libsolid.so.1.0 $(libdir)/libsolid.so.1 37 | ln -sf $(libdir)/libsolid.so.1.0 $(libdir)/libsolid.so 38 | mkdir -p $(includedir)/solid 39 | $(INSTALL_DATA) lexer.h $(includedir)/solid 40 | $(INSTALL_DATA) parser.h $(includedir)/solid 41 | $(INSTALL_DATA) core/solid.h $(includedir)/solid 42 | $(INSTALL_DATA) core/common.h $(includedir)/solid 43 | $(INSTALL_DATA) core/scanner_state.h $(includedir)/solid 44 | $(INSTALL_DATA) core/node.h $(includedir)/solid 45 | $(INSTALL_DATA) core/vm.h $(includedir)/solid 46 | $(INSTALL_DATA) core/ast.h $(includedir)/solid 47 | $(INSTALL_DATA) core/object.h $(includedir)/solid 48 | 49 | static: parser.o lexer.o $(OBJECTS) 50 | ar -cvq libsolid.a parser.o lexer.o $(OBJECTS) 51 | 52 | shared: parser.o lexer.o $(OBJECTS) 53 | $(CC) -shared -Wl,-soname,libsolid.so.1.0 -o libsolid.so.1.0 parser.o lexer.o $(OBJECTS) 54 | 55 | lib: lib/$(TARGET).o 56 | $(CC) -shared -Wl,-soname,$(TARGET).so -lsolid -lm -o lib/$(TARGET).so lib/$(TARGET).o 57 | 58 | deps: 59 | mkdir -p deps 60 | cd deps; git clone http://github.com/chameco/Cuttle; cd Cuttle; make && sudo make install 61 | 62 | uninstall: 63 | rm $(INSTALL_DIR)/solid 64 | rm $(includedir)/solid -r 65 | rm $(libdir)/libsolid.so 66 | rm $(libdir)/libsolid.so.1 67 | rm $(libdir)/libsolid.so.1.0 68 | 69 | clean: 70 | rm -f parser.c parser.h lexer.c lexer.h 71 | rm -f parser.o lexer.o $(OBJECTS) 72 | rm -f lib/*.o 73 | -------------------------------------------------------------------------------- /Makefile.win32: -------------------------------------------------------------------------------- 1 | EXEC = solid.exe 2 | CC = x86_64-w64-mingw32-gcc 3 | OBJECTS = core/solid.o core/node.o core/vm.o core/object.o core/ast.o core/common.o 4 | CFLAGS = -DWINDOWS -std=c99 -I/usr/local/include -I. -Icore -Wall -g -fPIC 5 | 6 | all: $(EXEC) 7 | 8 | $(EXEC): deps parser.o lexer.o $(OBJECTS) 9 | $(CC) parser.o lexer.o $(OBJECTS) -L. -lcuttle -g -Wall -Werror -o $(EXEC) 10 | 11 | parser.o: core/parser.y 12 | bison -d core/parser.y 13 | $(CC) $(CFLAGS) -Wno-implicit-function-declaration -c parser.c -o parser.o -g 14 | 15 | lexer.o: core/parser.l 16 | flex core/parser.l 17 | $(CC) $(CFLAGS) -Wno-unneeded-internal-declaration -Wno-implicit-function-declaration -c lexer.c -o lexer.o -g 18 | 19 | shared: parser.o lexer.o $(OBJECTS) 20 | $(CC) -shared -Wl,-soname,libsolid.dll -o libsolid.dll -Llib_mingw -lcuttle parser.o lexer.o $(OBJECTS) 21 | 22 | clean: 23 | rm -f parser.c parser.h lexer.c lexer.h 24 | rm -f parser.o lexer.o $(OBJECTS) 25 | rm -f lib/*.o 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | PLEASE DON'T USE THIS IN PRODUCTION 2 | =================================== 3 | 4 | I wrote this when I was 15. 5 | 6 | I'm leaving it here because I still think it's pretty neat. 7 | 8 | I'd do a lot of things differently today. 9 | 10 | I imagine it's riddled with security holes. 11 | 12 | If you need something similar, use [Lua](https://www.lua.org). 13 | 14 | If you need something more specialized, [contact me](https://chame.co) `;)`. 15 | 16 | solid - a minimalist language with a tiny VM 17 | ============================================ 18 | 19 | Solid is a simple, elegant language with a very user-friendly C API. Yes, that means you can embed it into your application or game with almost no effort. 20 | 21 | See the GitHub page at for source code. 22 | 23 | Installation 24 | ------------- 25 | 26 | You'll want Bison (3.0.0), Flex (2.5), and a C compiler (Clang and GCC are tested, because that's what I use, but everything is standard POSIX C99). 27 | 28 | The Makefile should automatically fetch any other dependencies. 29 | 30 | To tell `make` where you want it to place the binary, you can set 31 | `INSTALL_DIR` to a directory. Likewise, changing `PREFIX_DIR` to a 32 | directory will modify where `make` places libs and header files. 33 | `PREFIX_DIR` should contain the sub-directories `/share`, `/lib`, 34 | and `/include`. 35 | 36 | If not defined, `PREFIX_DIR` and `INSTALL_DIR` will default to 37 | `/usr/local` and `PREFIX_DIR/bin`, respectively. 38 | 39 | After you've fiddled with your setup, you can just run: 40 | 41 | git clone http://github.com/chameco/solid 42 | cd solid 43 | make && sudo make INSTALL_DIR=/foo/bar install 44 | 45 | To uninstall, just run `sudo make uninstall` from the source directory. If you changed `INSTALL_DIR`, you'll need to specify that again now. 46 | 47 | Usage 48 | ------ 49 | To start a REPL just run `solid`. 50 | Running a file is simple: `solid test.sol`. 51 | 52 | Syntax 53 | ------ 54 | Assignment: `x = 1`. 55 | 56 | Basic math: `1 + 2`, or since operators are just functions, `+(1, 2)`. 57 | 58 | Branching: `if 1 == 1 print("hello there")`. 59 | 60 | Looping: `while 1 == 1 print("solid is still the best")`. 61 | 62 | Blocks: Anywhere you can have a single expression, you can have a block. Blocks start with either `do` or `{`, and end with `end` or `}`. There are no formal rules about which to use, but I've taken to using `do` and `end` on multi-line blocks, and curlies for one-liners. Examples: 63 | 64 | c = 0; 65 | while c < 10 do 66 | print(c); 67 | c = c + 1; 68 | end; 69 | 70 | c = 0; 71 | while c < 10 {print(c); c = c + 1;}; 72 | 73 | Functions: You can get an anonymous function like so: `fn a a * a`. Solid is designed so there is only one core language element that has an intrinsic side effect: assignment. Obviously we've been using `print`, but that's not in the language core, rather it's a function written in C that works just like a normal function. More on that later, but now, let's define a longer function. 74 | 75 | f = fn a do 76 | print(a); 77 | a * a; 78 | end; 79 | 80 | print(f(10)); 81 | 82 | There is some special syntax for thunks to remove parsing ambiguity. Simply replace the argument list with a tilde (`~`) to build a thunk. 83 | 84 | Recursion: Since all functions are nameless, and the only method of assignment is ` = `, recursion is possible through a `$` variable saved inside the function's closure. You'll see in the next example. 85 | 86 | Inline functions: If a function only includes symbols in its name, you can call it inline. To derive from the previous example, consider the following. 87 | 88 | ^ = fn a, b do 89 | if b == 0 return 1; 90 | a * $(a, b - 1); 91 | end; 92 | 93 | print(10 ^ 2); 94 | 95 | Notice the recursion via use of `$`. 96 | 97 | Namespaces: A namespace is just a hash table. 98 | 99 | Math = ns do 100 | ^ = fn a, b do 101 | if b == 0 return 1; 102 | a * $(a, b - 1); 103 | end; 104 | end; 105 | 106 | print(Math.^(10, 2)); 107 | ^ = Math.^; 108 | print(10 ^ 2); 109 | 110 | A complete object system based on cloning namespaces is in the works, but right now feel free to call `clone` with a namespace argument to both derive classes and make instances. Don't worry about the overhead of having copies of functions in instances, as functions are represented as pointers internally. 111 | 112 | Lists: Make linked lists with the following syntax: `x = ["a", "b", "c", 1, 2, 3]`. Index them like so: `x !! 1`, which would evaluate to `"b"`. Lists are immutable, but you can add items with the cons operator, `:`. Another example: 113 | 114 | x = [2, 3, 4]; 115 | print(x !! 0); 116 | y = 1 : x; 117 | print(y !! 0); 118 | 119 | Dynamic Evaluation: Dynamically evaluating C code is facilitated by the `import` function in the standard library. Call `import("filename")` to load a file. Calling `import` on a file with a `.sol` extension will just execute whatever solid code is in that file in the current namespace, returning the result. However, calling it on a shared library (`.so`, not `.so.1`) will invoke the foreign function interface. More on that in the next section. 120 | 121 | Garbage Collection: It exists in a fairly primitive form. Run either `gc()` in solid or `solid_gc()` in C to run a very basic mark-and-sweep garbage collector. It might break things, or it might not. Tread carefully. 122 | 123 | Foreign Function Interface and API 124 | ----------------------------------- 125 | Now this is where it gets interesting. Solid exposes a complete C API that allows for incredibly easy embedding. All you really need to know: 126 | 127 | 1) To use the API, include `` and link with `-lsolid`. 128 | 129 | 2) Parse a file or expression into an AST with `solid_parse_file()` and `solid_parse_expr(`. Example: `solid_ast_node *n = solid_parse_expr("1 + 1");` 130 | 131 | 3) Compile an AST into a function with `solid_parse_tree()`. Example: `solid_object *func = solid_parse_tree(solid_parse_expr("1 + 1"));` 132 | 133 | 4) Make a virtual machine with `solid_make_vm()`. Example: `solid_vm *vm = solid_make_vm();` 134 | 135 | 5) Run code on a VM. Example: `solid_call_func(vm, func);` 136 | 137 | 6) Any expression you evaluate in solid puts the result in the return register, accessible at `vm->regs[255]`. However, this value will be of type `solid_object *`. To convert to C types, use `solid_get_str_value()`, `solid_get_int_value()`, and `solid_get_bool_value()`. 138 | 139 | 7) Convert C primitives to solid objects with `solid_str(vm, )`, `solid_int(vm, )`, and `solid_bool(vm, )`. 140 | 141 | 8) Use namespaces with `solid_get_namespace(, )` and `solid_set_namespace(, , )`. You can get the global namespace by calling `solid_get_current_namespace(vm)`. 142 | 143 | 9) Turn a C function with declaration `void (solid_vm *vm)` into a callable solid object function with `solid_define_c_function(vm, )`. You can access arguments by popping the VM stack (you'll get them in reverse order) with `solid_pop_stack(vm)`, and return a value by setting `vm->regs[255]`. 144 | 145 | To put it all together, here's a complete example of embedding solid into a C program: 146 | 147 | #include 148 | #include 149 | 150 | void hello_world(solid_vm *vm) 151 | { 152 | solid_object *argument = solid_pop_stack(vm); 153 | printf("Howdy, %s!\n", solid_get_str_value(argument)); 154 | vm->regs[255] = solid_int(vm, 1336) 155 | } 156 | int main() 157 | { 158 | solid_vm *vm = solid_make_vm(); 159 | solid_object *compiled_expr = solid_parse_tree(solid_parse_expr("1 + my_function()")); 160 | solid_set_namespace(solid_get_current_namespace(vm), solid_str(vm, "my_function"), solid_define_cfunc(vm, hello_world)); 161 | solid_call_func(vm, compiled_expr); 162 | printf("solid is super %d", solid_get_int_value(vm->regs[255])); 163 | } 164 | 165 | 166 | But wait, what if you want to use C from inside solid, rather than solid from inside C? Well, you do (almost) the exact same thing. The `import` function is capable of loading shared libraries with the extension `.so`. When loaded, solid will call an arbitrary, user-defined function named `solid_init` with the signature `void solid_init(solid_vm *vm)` inside the library, passing it the current VM. From there, you can do everything that we did above, defining functions, modifying namespaces, etc. Don't want to go through the trouble to manually build a shared library? Solid has you covered. Just throw your C file `whatever.c` containing `solid_init` in the `lib` folder of the solid source tree, and run `make lib TARGET=whatever`, and it will create a shared library called `whatever.so` in the solid source root, which can now be freely imported. 167 | 168 | Extras 169 | ------ 170 | Programmers using [GNU Emacs](https://www.gnu.org/software/emacs/) can load [`solid-mode`](etc/solid-mode.el), providing basic syntax highlighting and formatting for Solid scripts. 171 | 172 | Contributing 173 | ------------- 174 | Documentation is currently nonexistent outside of this file, but the code is pretty standard object-oriented C99 (generally one main struct per file, "methods" are functions that take a struct pointer as the first argument, everything is allocated with `malloc`). Start in ast.c and vm.c. 175 | 176 | License 177 | -------- 178 | Copyright 2013 Samuel Breese. 179 | Solid is distributed under the MIT license. See LICENSE.md. 180 | -------------------------------------------------------------------------------- /core/ast.c: -------------------------------------------------------------------------------- 1 | #include "ast.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "utils.h" 8 | #include "node.h" 9 | #include "vm.h" 10 | #include "object.h" 11 | 12 | #define dbc(I, A, B, M) bcode[i++] = solid_bc(I, A, B, M) 13 | #define pn(N) i = solid_parse_node(N, bcode, i) 14 | #define fdbc(I, A, B, M) function_bcode[fi++] = solid_bc(I, A, B, M) 15 | #define fpn(N) fi = solid_parse_node(N, function_bcode, fi) 16 | 17 | solid_object *solid_parse_tree(solid_vm *vm, solid_ast_node *tree) 18 | { 19 | solid_bytecode *bcode = (solid_bytecode *) malloc( 20 | sizeof(solid_bytecode) * 1024); 21 | int i = solid_parse_node(tree, bcode, 0); 22 | dbc(OP_END, 0, 0, NULL); 23 | return solid_define_function(vm, bcode); 24 | } 25 | int solid_parse_node(solid_ast_node *node, solid_bytecode *bcode, int i) 26 | { 27 | int temp[8] = {0}; 28 | solid_bytecode *function_bcode = NULL; 29 | int fi = 0; 30 | //debug("parsing node with ins %d", node->ins); 31 | switch (node->ins) { 32 | case STATEMENT_LIST: 33 | pn(node->arg1); 34 | if (node->arg2 != NULL) { 35 | pn(node->arg2); 36 | } 37 | break; 38 | case BLOCK: 39 | pn(node->arg1); 40 | break; 41 | case IDENTIFIER: 42 | log_err("IDENTIFIER node shouldn't be parsed."); 43 | exit(1); 44 | break; 45 | case NS_VAR: 46 | log_err("NS_VAR node shouldn't be parsed."); 47 | exit(1); 48 | break; 49 | case GET: 50 | if (node->arg1->arg2 != NULL) { 51 | pn(node->arg1->arg2); 52 | dbc(OP_MOV, 1, 255, NULL); 53 | } else { 54 | dbc(OP_LOCALNS, 1, 0, NULL); 55 | } 56 | dbc(OP_STORESTR, 2, 0, node->arg1->arg1->val.strval); 57 | dbc(OP_GET, 2, 1, NULL); 58 | dbc(OP_MOV, 255, 2, NULL); 59 | break; 60 | case GGET: 61 | dbc(OP_GLOBALNS, 1, 0, NULL); 62 | dbc(OP_STORESTR, 2, 0, node->arg1->val.strval); 63 | dbc(OP_GET, 2, 1, NULL); 64 | dbc(OP_MOV, 255, 2, NULL); 65 | break; 66 | case SET: 67 | pn(node->arg1); 68 | dbc(OP_PUSH, 255, 0, NULL); 69 | if (node->arg2->arg2 != NULL) { 70 | pn(node->arg2->arg2); 71 | dbc(OP_MOV, 1, 255, NULL); 72 | } else { 73 | dbc(OP_LOCALNS, 1, 0, NULL); 74 | } 75 | dbc(OP_POP, 255, 0, NULL); 76 | dbc(OP_SET, 255, 1, node->arg2->arg1->val.strval); 77 | break; 78 | case GSET: 79 | pn(node->arg1); 80 | dbc(OP_GLOBALNS, 1, 0, NULL); 81 | dbc(OP_SET, 255, 1, node->arg2->val.strval); 82 | break; 83 | case CALL: 84 | pn(node->arg2); 85 | pn(node->arg1); 86 | dbc(OP_CALL, 255, 0, NULL); 87 | break; 88 | case FUNC_ARGS: 89 | if (node->arg2 != NULL) { 90 | pn(node->arg2); 91 | } 92 | if (node->arg1 != NULL) { 93 | pn(node->arg1); 94 | dbc(OP_PUSH, 255, 0, NULL); 95 | } 96 | break; 97 | case CONST_INT: 98 | dbc(OP_STOREINT, 255, node->val.ival, NULL); 99 | break; 100 | case CONST_DOUBLE: 101 | dbc(OP_STOREDOUBLE, 255, 0, &(node->val.dval)); 102 | break; 103 | case CONST_STR: 104 | dbc(OP_STORESTR, 255, 0, node->val.strval); 105 | break; 106 | case CONST_BOOL: 107 | dbc(OP_STOREBOOL, 255, node->val.ival, NULL); 108 | break; 109 | case CONST_LIST: 110 | dbc(OP_STORELIST, 0, 0, NULL); 111 | pn(node->arg1); 112 | break; 113 | case LIST_BODY: 114 | if (node->arg1 != NULL) { 115 | pn(node->arg1); 116 | dbc(OP_PUSHLIST, 0, 255, NULL); 117 | } 118 | if (node->arg2 != NULL) { 119 | pn(node->arg2); 120 | } 121 | dbc(OP_MOV, 255, 0, NULL); 122 | break; 123 | case IF: 124 | pn(node->arg1); 125 | dbc(OP_MOV, 2, 255, NULL); 126 | dbc(OP_NOT, 2, 0, NULL); 127 | temp[0] = i; 128 | dbc(OP_NOP, 0, 0, NULL); 129 | pn(node->arg2); 130 | bcode[temp[0]] = solid_bc(OP_JMPIF, i, 2, NULL); 131 | break; 132 | case WHILE: 133 | temp[0] = i; 134 | pn(node->arg1); 135 | dbc(OP_MOV, 2, 255, NULL); 136 | dbc(OP_NOT, 2, 0, NULL); 137 | temp[1] = i; 138 | dbc(OP_NOP, 0, 0, NULL); 139 | pn(node->arg2); 140 | dbc(OP_JMP, temp[0], 0, NULL); 141 | bcode[temp[1]] = solid_bc(OP_JMPIF, i, 2, NULL); 142 | break; 143 | case FN: 144 | function_bcode = (solid_bytecode *) malloc( 145 | sizeof(solid_bytecode) * 1024); 146 | fi = 0; 147 | if (node->arg1 != NULL) { 148 | fpn(node->arg1); 149 | } 150 | fpn(node->arg2); 151 | dbc(OP_FN, 255, 0, function_bcode); 152 | function_bcode = NULL; 153 | fi = 0; 154 | break; 155 | case PARAM_LIST: 156 | if (node->arg1 != NULL) { 157 | dbc(OP_LOCALNS, 1, 0, NULL); 158 | dbc(OP_POP, 2, 0, NULL); 159 | dbc(OP_SET, 2, 1, node->arg1->val.strval); 160 | } 161 | if (node->arg2 != NULL) { 162 | pn(node->arg2); 163 | } 164 | break; 165 | case RET: 166 | pn(node->arg1); 167 | dbc(OP_END, 0, 0, NULL); 168 | break; 169 | case NS: 170 | dbc(OP_NS, 255, 0, NULL); 171 | pn(node->arg1); 172 | dbc(OP_ENDNS, 255, 0, NULL); 173 | } 174 | return i; 175 | } 176 | -------------------------------------------------------------------------------- /core/ast.h: -------------------------------------------------------------------------------- 1 | #ifndef SOLID_AST_H 2 | #define SOLID_AST_H 3 | 4 | #include "node.h" 5 | #include "vm.h" 6 | #include "object.h" 7 | 8 | solid_object *solid_parse_tree(solid_vm *vm, solid_ast_node *tree); 9 | int solid_parse_node(solid_ast_node *node, solid_bytecode *bcode, int i); 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /core/common.c: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include 3 | #include 4 | 5 | /* Print out a general error message in *string. Use perror if 6 | * errno is set, otherwise just print out *string plus a prefix. */ 7 | void report_error(char *string) 8 | { 9 | char *prefix = "Solid"; 10 | char *error = NULL; 11 | int len = strlen(prefix) + strlen(string); 12 | 13 | error = calloc(1, len + 1); 14 | if(error == NULL) { 15 | error = prefix; 16 | } else { 17 | sprintf(error, "%s: %s", prefix, string); 18 | } 19 | 20 | if(errno) { 21 | perror(error); 22 | } else { 23 | fputs(error, stderr); 24 | } 25 | 26 | free(error); 27 | } 28 | 29 | int yyerror(struct YYLTYPE *yylloc_param, void *scanner, struct ast_node **root, const char *s) 30 | { 31 | log_err("%s at line %d", s, yylloc_param->first_line); 32 | exit(1); 33 | return 0; 34 | } 35 | -------------------------------------------------------------------------------- /core/common.h: -------------------------------------------------------------------------------- 1 | #ifndef SOLID_COMMON_H 2 | #define SOLID_COMMON_H 3 | #include 4 | #include 5 | typedef struct YYLTYPE 6 | { 7 | int first_line; 8 | int first_column; 9 | int last_line; 10 | int last_column; 11 | } YYLTYPE; 12 | #define YYLTYPE_IS_DECLARED 1 13 | struct ast_node; 14 | 15 | int yyerror(struct YYLTYPE *yylloc_param, void *scanner, struct ast_node **root, const char *s); 16 | void report_error(char *string); 17 | #endif 18 | -------------------------------------------------------------------------------- /core/linenoise/linenoise.c: -------------------------------------------------------------------------------- 1 | /* linenoise.c -- guerrilla line editing library against the idea that a 2 | * line editing lib needs to be 20,000 lines of C code. 3 | * 4 | * You can find the latest source code at: 5 | * 6 | * http://github.com/antirez/linenoise 7 | * 8 | * Does a number of crazy assumptions that happen to be true in 99.9999% of 9 | * the 2010 UNIX computers around. 10 | * 11 | * ------------------------------------------------------------------------ 12 | * 13 | * Copyright (c) 2010-2013, Salvatore Sanfilippo 14 | * Copyright (c) 2010-2013, Pieter Noordhuis 15 | * 16 | * All rights reserved. 17 | * 18 | * Redistribution and use in source and binary forms, with or without 19 | * modification, are permitted provided that the following conditions are 20 | * met: 21 | * 22 | * * Redistributions of source code must retain the above copyright 23 | * notice, this list of conditions and the following disclaimer. 24 | * 25 | * * Redistributions in binary form must reproduce the above copyright 26 | * notice, this list of conditions and the following disclaimer in the 27 | * documentation and/or other materials provided with the distribution. 28 | * 29 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 30 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 31 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 32 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 33 | * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 34 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 35 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 36 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 37 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 38 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 39 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 40 | * 41 | * ------------------------------------------------------------------------ 42 | * 43 | * References: 44 | * - http://invisible-island.net/xterm/ctlseqs/ctlseqs.html 45 | * - http://www.3waylabs.com/nw/WWW/products/wizcon/vt220.html 46 | * 47 | * Todo list: 48 | * - Filter bogus Ctrl+ combinations. 49 | * - Win32 support 50 | * 51 | * Bloat: 52 | * - History search like Ctrl+r in readline? 53 | * 54 | * List of escape sequences used by this program, we do everything just 55 | * with three sequences. In order to be so cheap we may have some 56 | * flickering effect with some slow terminal, but the lesser sequences 57 | * the more compatible. 58 | * 59 | * CHA (Cursor Horizontal Absolute) 60 | * Sequence: ESC [ n G 61 | * Effect: moves cursor to column n 62 | * 63 | * EL (Erase Line) 64 | * Sequence: ESC [ n K 65 | * Effect: if n is 0 or missing, clear from cursor to end of line 66 | * Effect: if n is 1, clear from beginning of line to cursor 67 | * Effect: if n is 2, clear entire line 68 | * 69 | * CUF (CUrsor Forward) 70 | * Sequence: ESC [ n C 71 | * Effect: moves cursor forward of n chars 72 | * 73 | * When multi line mode is enabled, we also use an additional escape 74 | * sequence. However multi line editing is disabled by default. 75 | * 76 | * CUU (Cursor Up) 77 | * Sequence: ESC [ n A 78 | * Effect: moves cursor up of n chars. 79 | * 80 | * CUD (Cursor Down) 81 | * Sequence: ESC [ n B 82 | * Effect: moves cursor down of n chars. 83 | * 84 | * The following are used to clear the screen: ESC [ H ESC [ 2 J 85 | * This is actually composed of two sequences: 86 | * 87 | * cursorhome 88 | * Sequence: ESC [ H 89 | * Effect: moves the cursor to upper left corner 90 | * 91 | * ED2 (Clear entire screen) 92 | * Sequence: ESC [ 2 J 93 | * Effect: clear the whole screen 94 | * 95 | */ 96 | 97 | #include 98 | #include 99 | #include 100 | #include 101 | #include 102 | #include 103 | #include 104 | #include 105 | #include 106 | #include 107 | #include "linenoise.h" 108 | 109 | #define LINENOISE_DEFAULT_HISTORY_MAX_LEN 100 110 | #define LINENOISE_MAX_LINE 4096 111 | static char *unsupported_term[] = {"dumb","cons25",NULL}; 112 | static linenoiseCompletionCallback *completionCallback = NULL; 113 | 114 | static struct termios orig_termios; /* In order to restore at exit.*/ 115 | static int rawmode = 0; /* For atexit() function to check if restore is needed*/ 116 | static int mlmode = 0; /* Multi line mode. Default is single line. */ 117 | static int atexit_registered = 0; /* Register atexit just 1 time. */ 118 | static int history_max_len = LINENOISE_DEFAULT_HISTORY_MAX_LEN; 119 | static int history_len = 0; 120 | char **history = NULL; 121 | 122 | /* The linenoiseState structure represents the state during line editing. 123 | * We pass this state to functions implementing specific editing 124 | * functionalities. */ 125 | struct linenoiseState { 126 | int fd; /* Terminal file descriptor. */ 127 | char *buf; /* Edited line buffer. */ 128 | size_t buflen; /* Edited line buffer size. */ 129 | const char *prompt; /* Prompt to display. */ 130 | size_t plen; /* Prompt length. */ 131 | size_t pos; /* Current cursor position. */ 132 | size_t oldpos; /* Previous refresh cursor position. */ 133 | size_t len; /* Current edited line length. */ 134 | size_t cols; /* Number of columns in terminal. */ 135 | size_t maxrows; /* Maximum num of rows used so far (multiline mode) */ 136 | int history_index; /* The history index we are currently editing. */ 137 | }; 138 | 139 | static void linenoiseAtExit(void); 140 | int linenoiseHistoryAdd(const char *line); 141 | static void refreshLine(struct linenoiseState *l); 142 | 143 | /* ======================= Low level terminal handling ====================== */ 144 | 145 | /* Set if to use or not the multi line mode. */ 146 | void linenoiseSetMultiLine(int ml) { 147 | mlmode = ml; 148 | } 149 | 150 | /* Return true if the terminal name is in the list of terminals we know are 151 | * not able to understand basic escape sequences. */ 152 | static int isUnsupportedTerm(void) { 153 | char *term = getenv("TERM"); 154 | int j; 155 | 156 | if (term == NULL) return 0; 157 | for (j = 0; unsupported_term[j]; j++) 158 | if (!strcasecmp(term,unsupported_term[j])) return 1; 159 | return 0; 160 | } 161 | 162 | /* Raw mode: 1960 magic shit. */ 163 | static int enableRawMode(int fd) { 164 | struct termios raw; 165 | 166 | if (!isatty(STDIN_FILENO)) goto fatal; 167 | if (!atexit_registered) { 168 | atexit(linenoiseAtExit); 169 | atexit_registered = 1; 170 | } 171 | if (tcgetattr(fd,&orig_termios) == -1) goto fatal; 172 | 173 | raw = orig_termios; /* modify the original mode */ 174 | /* input modes: no break, no CR to NL, no parity check, no strip char, 175 | * no start/stop output control. */ 176 | raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); 177 | /* output modes - disable post processing */ 178 | raw.c_oflag &= ~(OPOST); 179 | /* control modes - set 8 bit chars */ 180 | raw.c_cflag |= (CS8); 181 | /* local modes - choing off, canonical off, no extended functions, 182 | * no signal chars (^Z,^C) */ 183 | raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG); 184 | /* control chars - set return condition: min number of bytes and timer. 185 | * We want read to return every single byte, without timeout. */ 186 | raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; /* 1 byte, no timer */ 187 | 188 | /* put terminal in raw mode after flushing */ 189 | if (tcsetattr(fd,TCSAFLUSH,&raw) < 0) goto fatal; 190 | rawmode = 1; 191 | return 0; 192 | 193 | fatal: 194 | errno = ENOTTY; 195 | return -1; 196 | } 197 | 198 | static void disableRawMode(int fd) { 199 | /* Don't even check the return value as it's too late. */ 200 | if (rawmode && tcsetattr(fd,TCSAFLUSH,&orig_termios) != -1) 201 | rawmode = 0; 202 | } 203 | 204 | /* Try to get the number of columns in the current terminal, or assume 80 205 | * if it fails. */ 206 | static int getColumns(void) { 207 | struct winsize ws; 208 | 209 | if (ioctl(1, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0) return 80; 210 | return ws.ws_col; 211 | } 212 | 213 | /* Clear the screen. Used to handle ctrl+l */ 214 | void linenoiseClearScreen(void) { 215 | if (write(STDIN_FILENO,"\x1b[H\x1b[2J",7) <= 0) { 216 | /* nothing to do, just to avoid warning. */ 217 | } 218 | } 219 | 220 | /* Beep, used for completion when there is nothing to complete or when all 221 | * the choices were already shown. */ 222 | static void linenoiseBeep(void) { 223 | fprintf(stderr, "\x7"); 224 | fflush(stderr); 225 | } 226 | 227 | /* ============================== Completion ================================ */ 228 | 229 | /* Free a list of completion option populated by linenoiseAddCompletion(). */ 230 | static void freeCompletions(linenoiseCompletions *lc) { 231 | size_t i; 232 | for (i = 0; i < lc->len; i++) 233 | free(lc->cvec[i]); 234 | if (lc->cvec != NULL) 235 | free(lc->cvec); 236 | } 237 | 238 | /* This is an helper function for linenoiseEdit() and is called when the 239 | * user types the key in order to complete the string currently in the 240 | * input. 241 | * 242 | * The state of the editing is encapsulated into the pointed linenoiseState 243 | * structure as described in the structure definition. */ 244 | static int completeLine(struct linenoiseState *ls) { 245 | linenoiseCompletions lc = { 0, NULL }; 246 | int nread, nwritten; 247 | char c = 0; 248 | 249 | completionCallback(ls->buf,&lc); 250 | if (lc.len == 0) { 251 | linenoiseBeep(); 252 | } else { 253 | size_t stop = 0, i = 0; 254 | 255 | while(!stop) { 256 | /* Show completion or original buffer */ 257 | if (i < lc.len) { 258 | struct linenoiseState saved = *ls; 259 | 260 | ls->len = ls->pos = strlen(lc.cvec[i]); 261 | ls->buf = lc.cvec[i]; 262 | refreshLine(ls); 263 | ls->len = saved.len; 264 | ls->pos = saved.pos; 265 | ls->buf = saved.buf; 266 | } else { 267 | refreshLine(ls); 268 | } 269 | 270 | nread = read(ls->fd,&c,1); 271 | if (nread <= 0) { 272 | freeCompletions(&lc); 273 | return -1; 274 | } 275 | 276 | switch(c) { 277 | case 9: /* tab */ 278 | i = (i+1) % (lc.len+1); 279 | if (i == lc.len) linenoiseBeep(); 280 | break; 281 | case 27: /* escape */ 282 | /* Re-show original buffer */ 283 | if (i < lc.len) refreshLine(ls); 284 | stop = 1; 285 | break; 286 | default: 287 | /* Update buffer and return */ 288 | if (i < lc.len) { 289 | nwritten = snprintf(ls->buf,ls->buflen,"%s",lc.cvec[i]); 290 | ls->len = ls->pos = nwritten; 291 | } 292 | stop = 1; 293 | break; 294 | } 295 | } 296 | } 297 | 298 | freeCompletions(&lc); 299 | return c; /* Return last read character */ 300 | } 301 | 302 | /* Register a callback function to be called for tab-completion. */ 303 | void linenoiseSetCompletionCallback(linenoiseCompletionCallback *fn) { 304 | completionCallback = fn; 305 | } 306 | 307 | /* This function is used by the callback function registered by the user 308 | * in order to add completion options given the input string when the 309 | * user typed . See the example.c source code for a very easy to 310 | * understand example. */ 311 | void linenoiseAddCompletion(linenoiseCompletions *lc, char *str) { 312 | size_t len = strlen(str); 313 | char *copy = malloc(len+1); 314 | memcpy(copy,str,len+1); 315 | lc->cvec = realloc(lc->cvec,sizeof(char*)*(lc->len+1)); 316 | lc->cvec[lc->len++] = copy; 317 | } 318 | 319 | /* =========================== Line editing ================================= */ 320 | 321 | /* Single line low level line refresh. 322 | * 323 | * Rewrite the currently edited line accordingly to the buffer content, 324 | * cursor position, and number of columns of the terminal. */ 325 | static void refreshSingleLine(struct linenoiseState *l) { 326 | char seq[64]; 327 | size_t plen = strlen(l->prompt); 328 | int fd = l->fd; 329 | char *buf = l->buf; 330 | size_t len = l->len; 331 | size_t pos = l->pos; 332 | 333 | while((plen+pos) >= l->cols) { 334 | buf++; 335 | len--; 336 | pos--; 337 | } 338 | while (plen+len > l->cols) { 339 | len--; 340 | } 341 | 342 | /* Cursor to left edge */ 343 | snprintf(seq,64,"\x1b[0G"); 344 | if (write(fd,seq,strlen(seq)) == -1) return; 345 | /* Write the prompt and the current buffer content */ 346 | if (write(fd,l->prompt,strlen(l->prompt)) == -1) return; 347 | if (write(fd,buf,len) == -1) return; 348 | /* Erase to right */ 349 | snprintf(seq,64,"\x1b[0K"); 350 | if (write(fd,seq,strlen(seq)) == -1) return; 351 | /* Move cursor to original position. */ 352 | snprintf(seq,64,"\x1b[0G\x1b[%dC", (int)(pos+plen)); 353 | if (write(fd,seq,strlen(seq)) == -1) return; 354 | } 355 | 356 | /* Multi line low level line refresh. 357 | * 358 | * Rewrite the currently edited line accordingly to the buffer content, 359 | * cursor position, and number of columns of the terminal. */ 360 | static void refreshMultiLine(struct linenoiseState *l) { 361 | char seq[64]; 362 | int plen = strlen(l->prompt); 363 | int rows = (plen+l->len+l->cols-1)/l->cols; /* rows used by current buf. */ 364 | int rpos = (plen+l->oldpos+l->cols)/l->cols; /* cursor relative row. */ 365 | int rpos2; /* rpos after refresh. */ 366 | int old_rows = l->maxrows; 367 | int fd = l->fd, j; 368 | 369 | /* Update maxrows if needed. */ 370 | if (rows > (int)l->maxrows) l->maxrows = rows; 371 | 372 | #ifdef LN_DEBUG 373 | FILE *fp = fopen("/tmp/debug.txt","a"); 374 | fprintf(fp,"[%d %d %d] p: %d, rows: %d, rpos: %d, max: %d, oldmax: %d", 375 | (int)l->len,(int)l->pos,(int)l->oldpos,plen,rows,rpos,(int)l->maxrows,old_rows); 376 | #endif 377 | 378 | /* First step: clear all the lines used before. To do so start by 379 | * going to the last row. */ 380 | if (old_rows-rpos > 0) { 381 | #ifdef LN_DEBUG 382 | fprintf(fp,", go down %d", old_rows-rpos); 383 | #endif 384 | snprintf(seq,64,"\x1b[%dB", old_rows-rpos); 385 | if (write(fd,seq,strlen(seq)) == -1) return; 386 | } 387 | 388 | /* Now for every row clear it, go up. */ 389 | for (j = 0; j < old_rows-1; j++) { 390 | #ifdef LN_DEBUG 391 | fprintf(fp,", clear+up"); 392 | #endif 393 | snprintf(seq,64,"\x1b[0G\x1b[0K\x1b[1A"); 394 | if (write(fd,seq,strlen(seq)) == -1) return; 395 | } 396 | 397 | /* Clean the top line. */ 398 | #ifdef LN_DEBUG 399 | fprintf(fp,", clear"); 400 | #endif 401 | snprintf(seq,64,"\x1b[0G\x1b[0K"); 402 | if (write(fd,seq,strlen(seq)) == -1) return; 403 | 404 | /* Write the prompt and the current buffer content */ 405 | if (write(fd,l->prompt,strlen(l->prompt)) == -1) return; 406 | if (write(fd,l->buf,l->len) == -1) return; 407 | 408 | /* If we are at the very end of the screen with our prompt, we need to 409 | * emit a newline and move the prompt to the first column. */ 410 | if (l->pos && 411 | l->pos == l->len && 412 | (l->pos+plen) % l->cols == 0) 413 | { 414 | #ifdef LN_DEBUG 415 | fprintf(fp,", "); 416 | #endif 417 | if (write(fd,"\n",1) == -1) return; 418 | snprintf(seq,64,"\x1b[0G"); 419 | if (write(fd,seq,strlen(seq)) == -1) return; 420 | rows++; 421 | if (rows > (int)l->maxrows) l->maxrows = rows; 422 | } 423 | 424 | /* Move cursor to right position. */ 425 | rpos2 = (plen+l->pos+l->cols)/l->cols; /* current cursor relative row. */ 426 | #ifdef LN_DEBUG 427 | fprintf(fp,", rpos2 %d", rpos2); 428 | #endif 429 | /* Go up till we reach the expected positon. */ 430 | if (rows-rpos2 > 0) { 431 | #ifdef LN_DEBUG 432 | fprintf(fp,", go-up %d", rows-rpos2); 433 | #endif 434 | snprintf(seq,64,"\x1b[%dA", rows-rpos2); 435 | if (write(fd,seq,strlen(seq)) == -1) return; 436 | } 437 | /* Set column. */ 438 | #ifdef LN_DEBUG 439 | fprintf(fp,", set col %d", 1+((plen+(int)l->pos) % (int)l->cols)); 440 | #endif 441 | snprintf(seq,64,"\x1b[%dG", 1+((plen+(int)l->pos) % (int)l->cols)); 442 | if (write(fd,seq,strlen(seq)) == -1) return; 443 | 444 | l->oldpos = l->pos; 445 | 446 | #ifdef LN_DEBUG 447 | fprintf(fp,"\n"); 448 | fclose(fp); 449 | #endif 450 | } 451 | 452 | /* Calls the two low level functions refreshSingleLine() or 453 | * refreshMultiLine() according to the selected mode. */ 454 | static void refreshLine(struct linenoiseState *l) { 455 | if (mlmode) 456 | refreshMultiLine(l); 457 | else 458 | refreshSingleLine(l); 459 | } 460 | 461 | /* Insert the character 'c' at cursor current position. 462 | * 463 | * On error writing to the terminal -1 is returned, otherwise 0. */ 464 | int linenoiseEditInsert(struct linenoiseState *l, int c) { 465 | if (l->len < l->buflen) { 466 | if (l->len == l->pos) { 467 | l->buf[l->pos] = c; 468 | l->pos++; 469 | l->len++; 470 | l->buf[l->len] = '\0'; 471 | if ((!mlmode && l->plen+l->len < l->cols) /* || mlmode */) { 472 | /* Avoid a full update of the line in the 473 | * trivial case. */ 474 | if (write(l->fd,&c,1) == -1) return -1; 475 | } else { 476 | refreshLine(l); 477 | } 478 | } else { 479 | memmove(l->buf+l->pos+1,l->buf+l->pos,l->len-l->pos); 480 | l->buf[l->pos] = c; 481 | l->len++; 482 | l->pos++; 483 | l->buf[l->len] = '\0'; 484 | refreshLine(l); 485 | } 486 | } 487 | return 0; 488 | } 489 | 490 | /* Move cursor on the left. */ 491 | void linenoiseEditMoveLeft(struct linenoiseState *l) { 492 | if (l->pos > 0) { 493 | l->pos--; 494 | refreshLine(l); 495 | } 496 | } 497 | 498 | /* Move cursor on the right. */ 499 | void linenoiseEditMoveRight(struct linenoiseState *l) { 500 | if (l->pos != l->len) { 501 | l->pos++; 502 | refreshLine(l); 503 | } 504 | } 505 | 506 | /* Substitute the currently edited line with the next or previous history 507 | * entry as specified by 'dir'. */ 508 | #define LINENOISE_HISTORY_NEXT 0 509 | #define LINENOISE_HISTORY_PREV 1 510 | void linenoiseEditHistoryNext(struct linenoiseState *l, int dir) { 511 | if (history_len > 1) { 512 | /* Update the current history entry before to 513 | * overwrite it with the next one. */ 514 | free(history[history_len - 1 - l->history_index]); 515 | history[history_len - 1 - l->history_index] = strdup(l->buf); 516 | /* Show the new entry */ 517 | l->history_index += (dir == LINENOISE_HISTORY_PREV) ? 1 : -1; 518 | if (l->history_index < 0) { 519 | l->history_index = 0; 520 | return; 521 | } else if (l->history_index >= history_len) { 522 | l->history_index = history_len-1; 523 | return; 524 | } 525 | strncpy(l->buf,history[history_len - 1 - l->history_index],l->buflen); 526 | l->buf[l->buflen-1] = '\0'; 527 | l->len = l->pos = strlen(l->buf); 528 | refreshLine(l); 529 | } 530 | } 531 | 532 | /* Delete the character at the right of the cursor without altering the cursor 533 | * position. Basically this is what happens with the "Delete" keyboard key. */ 534 | void linenoiseEditDelete(struct linenoiseState *l) { 535 | if (l->len > 0 && l->pos < l->len) { 536 | memmove(l->buf+l->pos,l->buf+l->pos+1,l->len-l->pos-1); 537 | l->len--; 538 | l->buf[l->len] = '\0'; 539 | refreshLine(l); 540 | } 541 | } 542 | 543 | /* Backspace implementation. */ 544 | void linenoiseEditBackspace(struct linenoiseState *l) { 545 | if (l->pos > 0 && l->len > 0) { 546 | memmove(l->buf+l->pos-1,l->buf+l->pos,l->len-l->pos); 547 | l->pos--; 548 | l->len--; 549 | l->buf[l->len] = '\0'; 550 | refreshLine(l); 551 | } 552 | } 553 | 554 | /* Delete the previosu word, maintaining the cursor at the start of the 555 | * current word. */ 556 | void linenoiseEditDeletePrevWord(struct linenoiseState *l) { 557 | size_t old_pos = l->pos; 558 | size_t diff; 559 | 560 | while (l->pos > 0 && l->buf[l->pos-1] == ' ') 561 | l->pos--; 562 | while (l->pos > 0 && l->buf[l->pos-1] != ' ') 563 | l->pos--; 564 | diff = old_pos - l->pos; 565 | memmove(l->buf+l->pos,l->buf+old_pos,l->len-old_pos+1); 566 | l->len -= diff; 567 | refreshLine(l); 568 | } 569 | 570 | /* This function is the core of the line editing capability of linenoise. 571 | * It expects 'fd' to be already in "raw mode" so that every key pressed 572 | * will be returned ASAP to read(). 573 | * 574 | * The resulting string is put into 'buf' when the user type enter, or 575 | * when ctrl+d is typed. 576 | * 577 | * The function returns the length of the current buffer. */ 578 | static int linenoiseEdit(int fd, char *buf, size_t buflen, const char *prompt) 579 | { 580 | struct linenoiseState l; 581 | 582 | /* Populate the linenoise state that we pass to functions implementing 583 | * specific editing functionalities. */ 584 | l.fd = fd; 585 | l.buf = buf; 586 | l.buflen = buflen; 587 | l.prompt = prompt; 588 | l.plen = strlen(prompt); 589 | l.oldpos = l.pos = 0; 590 | l.len = 0; 591 | l.cols = getColumns(); 592 | l.maxrows = 0; 593 | l.history_index = 0; 594 | 595 | /* Buffer starts empty. */ 596 | buf[0] = '\0'; 597 | buflen--; /* Make sure there is always space for the nulterm */ 598 | 599 | /* The latest history entry is always our current buffer, that 600 | * initially is just an empty string. */ 601 | linenoiseHistoryAdd(""); 602 | 603 | if (write(fd,prompt,l.plen) == -1) return -1; 604 | while(1) { 605 | char c; 606 | int nread; 607 | char seq[2], seq2[2]; 608 | 609 | nread = read(fd,&c,1); 610 | if (nread <= 0) return l.len; 611 | 612 | /* Only autocomplete when the callback is set. It returns < 0 when 613 | * there was an error reading from fd. Otherwise it will return the 614 | * character that should be handled next. */ 615 | if (c == 9 && completionCallback != NULL) { 616 | c = completeLine(&l); 617 | /* Return on errors */ 618 | if (c < 0) return l.len; 619 | /* Read next character when 0 */ 620 | if (c == 0) continue; 621 | } 622 | 623 | switch(c) { 624 | case 13: /* enter */ 625 | history_len--; 626 | free(history[history_len]); 627 | return (int)l.len; 628 | case 3: /* ctrl-c */ 629 | errno = EAGAIN; 630 | return -1; 631 | case 127: /* backspace */ 632 | case 8: /* ctrl-h */ 633 | linenoiseEditBackspace(&l); 634 | break; 635 | case 4: /* ctrl-d, remove char at right of cursor, or of the 636 | line is empty, act as end-of-file. */ 637 | if (l.len > 0) { 638 | linenoiseEditDelete(&l); 639 | } else { 640 | history_len--; 641 | free(history[history_len]); 642 | return -1; 643 | } 644 | break; 645 | case 20: /* ctrl-t, swaps current character with previous. */ 646 | if (l.pos > 0 && l.pos < l.len) { 647 | int aux = buf[l.pos-1]; 648 | buf[l.pos-1] = buf[l.pos]; 649 | buf[l.pos] = aux; 650 | if (l.pos != l.len-1) l.pos++; 651 | refreshLine(&l); 652 | } 653 | break; 654 | case 2: /* ctrl-b */ 655 | linenoiseEditMoveLeft(&l); 656 | break; 657 | case 6: /* ctrl-f */ 658 | linenoiseEditMoveRight(&l); 659 | break; 660 | case 16: /* ctrl-p */ 661 | linenoiseEditHistoryNext(&l, LINENOISE_HISTORY_PREV); 662 | break; 663 | case 14: /* ctrl-n */ 664 | linenoiseEditHistoryNext(&l, LINENOISE_HISTORY_NEXT); 665 | break; 666 | case 27: /* escape sequence */ 667 | /* Read the next two bytes representing the escape sequence. */ 668 | if (read(fd,seq,2) == -1) break; 669 | 670 | if (seq[0] == 91 && seq[1] == 68) { 671 | /* Left arrow */ 672 | linenoiseEditMoveLeft(&l); 673 | } else if (seq[0] == 91 && seq[1] == 67) { 674 | /* Right arrow */ 675 | linenoiseEditMoveRight(&l); 676 | } else if (seq[0] == 91 && (seq[1] == 65 || seq[1] == 66)) { 677 | /* Up and Down arrows */ 678 | linenoiseEditHistoryNext(&l, 679 | (seq[1] == 65) ? LINENOISE_HISTORY_PREV : 680 | LINENOISE_HISTORY_NEXT); 681 | } else if (seq[0] == 91 && seq[1] > 48 && seq[1] < 55) { 682 | /* extended escape, read additional two bytes. */ 683 | if (read(fd,seq2,2) == -1) break; 684 | if (seq[1] == 51 && seq2[0] == 126) { 685 | /* Delete key. */ 686 | linenoiseEditDelete(&l); 687 | } 688 | } 689 | break; 690 | default: 691 | if (linenoiseEditInsert(&l,c)) return -1; 692 | break; 693 | case 21: /* Ctrl+u, delete the whole line. */ 694 | buf[0] = '\0'; 695 | l.pos = l.len = 0; 696 | refreshLine(&l); 697 | break; 698 | case 11: /* Ctrl+k, delete from current to end of line. */ 699 | buf[l.pos] = '\0'; 700 | l.len = l.pos; 701 | refreshLine(&l); 702 | break; 703 | case 1: /* Ctrl+a, go to the start of the line */ 704 | l.pos = 0; 705 | refreshLine(&l); 706 | break; 707 | case 5: /* ctrl+e, go to the end of the line */ 708 | l.pos = l.len; 709 | refreshLine(&l); 710 | break; 711 | case 12: /* ctrl+l, clear screen */ 712 | linenoiseClearScreen(); 713 | refreshLine(&l); 714 | break; 715 | case 23: /* ctrl+w, delete previous word */ 716 | linenoiseEditDeletePrevWord(&l); 717 | break; 718 | } 719 | } 720 | return l.len; 721 | } 722 | 723 | /* This function calls the line editing function linenoiseEdit() using 724 | * the STDIN file descriptor set in raw mode. */ 725 | static int linenoiseRaw(char *buf, size_t buflen, const char *prompt) { 726 | int fd = STDIN_FILENO; 727 | int count; 728 | 729 | if (buflen == 0) { 730 | errno = EINVAL; 731 | return -1; 732 | } 733 | if (!isatty(STDIN_FILENO)) { 734 | if (fgets(buf, buflen, stdin) == NULL) return -1; 735 | count = strlen(buf); 736 | if (count && buf[count-1] == '\n') { 737 | count--; 738 | buf[count] = '\0'; 739 | } 740 | } else { 741 | if (enableRawMode(fd) == -1) return -1; 742 | count = linenoiseEdit(fd, buf, buflen, prompt); 743 | disableRawMode(fd); 744 | printf("\n"); 745 | } 746 | return count; 747 | } 748 | 749 | /* The high level function that is the main API of the linenoise library. 750 | * This function checks if the terminal has basic capabilities, just checking 751 | * for a blacklist of stupid terminals, and later either calls the line 752 | * editing function or uses dummy fgets() so that you will be able to type 753 | * something even in the most desperate of the conditions. */ 754 | char *linenoise(const char *prompt) { 755 | char buf[LINENOISE_MAX_LINE]; 756 | int count; 757 | 758 | if (isUnsupportedTerm()) { 759 | size_t len; 760 | 761 | printf("%s",prompt); 762 | fflush(stdout); 763 | if (fgets(buf,LINENOISE_MAX_LINE,stdin) == NULL) return NULL; 764 | len = strlen(buf); 765 | while(len && (buf[len-1] == '\n' || buf[len-1] == '\r')) { 766 | len--; 767 | buf[len] = '\0'; 768 | } 769 | return strdup(buf); 770 | } else { 771 | count = linenoiseRaw(buf,LINENOISE_MAX_LINE,prompt); 772 | if (count == -1) return NULL; 773 | return strdup(buf); 774 | } 775 | } 776 | 777 | /* ================================ History ================================= */ 778 | 779 | /* Free the history, but does not reset it. Only used when we have to 780 | * exit() to avoid memory leaks are reported by valgrind & co. */ 781 | static void freeHistory(void) { 782 | if (history) { 783 | int j; 784 | 785 | for (j = 0; j < history_len; j++) 786 | free(history[j]); 787 | free(history); 788 | } 789 | } 790 | 791 | /* At exit we'll try to fix the terminal to the initial conditions. */ 792 | static void linenoiseAtExit(void) { 793 | disableRawMode(STDIN_FILENO); 794 | freeHistory(); 795 | } 796 | 797 | /* Using a circular buffer is smarter, but a bit more complex to handle. */ 798 | int linenoiseHistoryAdd(const char *line) { 799 | char *linecopy; 800 | 801 | if (history_max_len == 0) return 0; 802 | if (history == NULL) { 803 | history = malloc(sizeof(char*)*history_max_len); 804 | if (history == NULL) return 0; 805 | memset(history,0,(sizeof(char*)*history_max_len)); 806 | } 807 | linecopy = strdup(line); 808 | if (!linecopy) return 0; 809 | if (history_len == history_max_len) { 810 | free(history[0]); 811 | memmove(history,history+1,sizeof(char*)*(history_max_len-1)); 812 | history_len--; 813 | } 814 | history[history_len] = linecopy; 815 | history_len++; 816 | return 1; 817 | } 818 | 819 | /* Set the maximum length for the history. This function can be called even 820 | * if there is already some history, the function will make sure to retain 821 | * just the latest 'len' elements if the new history length value is smaller 822 | * than the amount of items already inside the history. */ 823 | int linenoiseHistorySetMaxLen(int len) { 824 | char **new; 825 | 826 | if (len < 1) return 0; 827 | if (history) { 828 | int tocopy = history_len; 829 | 830 | new = malloc(sizeof(char*)*len); 831 | if (new == NULL) return 0; 832 | 833 | /* If we can't copy everything, free the elements we'll not use. */ 834 | if (len < tocopy) { 835 | int j; 836 | 837 | for (j = 0; j < tocopy-len; j++) free(history[j]); 838 | tocopy = len; 839 | } 840 | memset(new,0,sizeof(char*)*len); 841 | memcpy(new,history+(history_len-tocopy), sizeof(char*)*tocopy); 842 | free(history); 843 | history = new; 844 | } 845 | history_max_len = len; 846 | if (history_len > history_max_len) 847 | history_len = history_max_len; 848 | return 1; 849 | } 850 | 851 | /* Save the history in the specified file. On success 0 is returned 852 | * otherwise -1 is returned. */ 853 | int linenoiseHistorySave(char *filename) { 854 | FILE *fp = fopen(filename,"w"); 855 | int j; 856 | 857 | if (fp == NULL) return -1; 858 | for (j = 0; j < history_len; j++) 859 | fprintf(fp,"%s\n",history[j]); 860 | fclose(fp); 861 | return 0; 862 | } 863 | 864 | /* Load the history from the specified file. If the file does not exist 865 | * zero is returned and no operation is performed. 866 | * 867 | * If the file exists and the operation succeeded 0 is returned, otherwise 868 | * on error -1 is returned. */ 869 | int linenoiseHistoryLoad(char *filename) { 870 | FILE *fp = fopen(filename,"r"); 871 | char buf[LINENOISE_MAX_LINE]; 872 | 873 | if (fp == NULL) return -1; 874 | 875 | while (fgets(buf,LINENOISE_MAX_LINE,fp) != NULL) { 876 | char *p; 877 | 878 | p = strchr(buf,'\r'); 879 | if (!p) p = strchr(buf,'\n'); 880 | if (p) *p = '\0'; 881 | linenoiseHistoryAdd(buf); 882 | } 883 | fclose(fp); 884 | return 0; 885 | } 886 | -------------------------------------------------------------------------------- /core/linenoise/linenoise.h: -------------------------------------------------------------------------------- 1 | /* linenoise.h -- guerrilla line editing library against the idea that a 2 | * line editing lib needs to be 20,000 lines of C code. 3 | * 4 | * See linenoise.c for more information. 5 | * 6 | * ------------------------------------------------------------------------ 7 | * 8 | * Copyright (c) 2010, Salvatore Sanfilippo 9 | * Copyright (c) 2010, Pieter Noordhuis 10 | * 11 | * All rights reserved. 12 | * 13 | * Redistribution and use in source and binary forms, with or without 14 | * modification, are permitted provided that the following conditions are 15 | * met: 16 | * 17 | * * Redistributions of source code must retain the above copyright 18 | * notice, this list of conditions and the following disclaimer. 19 | * 20 | * * Redistributions in binary form must reproduce the above copyright 21 | * notice, this list of conditions and the following disclaimer in the 22 | * documentation and/or other materials provided with the distribution. 23 | * 24 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 25 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 26 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 27 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 28 | * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 29 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 30 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 31 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 32 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 33 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 34 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 35 | */ 36 | 37 | #ifndef __LINENOISE_H 38 | #define __LINENOISE_H 39 | 40 | typedef struct linenoiseCompletions { 41 | size_t len; 42 | char **cvec; 43 | } linenoiseCompletions; 44 | 45 | typedef void(linenoiseCompletionCallback)(const char *, linenoiseCompletions *); 46 | void linenoiseSetCompletionCallback(linenoiseCompletionCallback *); 47 | void linenoiseAddCompletion(linenoiseCompletions *, char *); 48 | 49 | char *linenoise(const char *prompt); 50 | int linenoiseHistoryAdd(const char *line); 51 | int linenoiseHistorySetMaxLen(int len); 52 | int linenoiseHistorySave(char *filename); 53 | int linenoiseHistoryLoad(char *filename); 54 | void linenoiseClearScreen(void); 55 | void linenoiseSetMultiLine(int ml); 56 | 57 | #endif /* __LINENOISE_H */ 58 | -------------------------------------------------------------------------------- /core/node.c: -------------------------------------------------------------------------------- 1 | #include "node.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "utils.h" 8 | 9 | solid_node_val NULL_VALUE = {0}; 10 | 11 | solid_node_val solid_null_value() 12 | { 13 | return NULL_VALUE; 14 | } 15 | 16 | solid_node_val solid_int_value(int i) 17 | { 18 | solid_node_val r; 19 | r.ival = i; 20 | return r; 21 | } 22 | 23 | solid_node_val solid_double_value(double d) 24 | { 25 | solid_node_val r; 26 | r.dval = d; 27 | return r; 28 | } 29 | 30 | solid_node_val solid_str_value(char *s) 31 | { 32 | solid_node_val r; 33 | if (s != NULL) { 34 | memset((void *) r.strval, 0x0, 256); 35 | memcpy(r.strval, s, strlen(s)); 36 | } 37 | return r; 38 | } 39 | 40 | solid_ast_node *solid_make_node(solid_node_ins ins, solid_ast_node *arg1, solid_ast_node *arg2, solid_node_val val) 41 | { 42 | solid_ast_node *r; 43 | if ((r = (solid_ast_node *) malloc(sizeof(solid_ast_node)))) { 44 | r->ins = ins; 45 | r->arg1 = arg1; 46 | r->arg2 = arg2; 47 | r->val = val; 48 | return r; 49 | } 50 | log_err("malloc failure in make_node"); 51 | exit(1); 52 | } 53 | 54 | solid_ast_node *solid_const_integer_node(int i) 55 | { 56 | return solid_make_node(CONST_INT, NULL, NULL, solid_int_value(i)); 57 | } 58 | 59 | solid_ast_node *solid_const_double_node(double d) 60 | { 61 | return solid_make_node(CONST_DOUBLE, NULL, NULL, solid_double_value(d)); 62 | } 63 | 64 | solid_ast_node *solid_const_string_node(char *s) 65 | { 66 | return solid_make_node(CONST_STR, NULL, NULL, solid_str_value(s)); 67 | } 68 | 69 | solid_ast_node *solid_const_bool_node(int i) 70 | { 71 | return solid_make_node(CONST_BOOL, NULL, NULL, solid_int_value(i)); 72 | } 73 | 74 | solid_ast_node *solid_identifier_node(char *name) 75 | { 76 | return solid_make_node(IDENTIFIER, NULL, NULL, solid_str_value(name)); 77 | } 78 | -------------------------------------------------------------------------------- /core/node.h: -------------------------------------------------------------------------------- 1 | #ifndef SOLID_NODE_H 2 | #define SOLID_NODE_H 3 | 4 | typedef enum solid_node_ins { 5 | STATEMENT_LIST, 6 | BLOCK, 7 | IDENTIFIER, 8 | NS_VAR, 9 | GGET, 10 | GET, 11 | SET, 12 | GSET, 13 | CALL, 14 | FUNC_ARGS, 15 | CONST_INT, 16 | CONST_DOUBLE, 17 | CONST_STR, 18 | CONST_BOOL, 19 | CONST_LIST, 20 | LIST_BODY, 21 | IF, 22 | WHILE, 23 | FN, 24 | PARAM_LIST, 25 | RET, 26 | NS 27 | } solid_node_ins; 28 | 29 | typedef union solid_node_val { 30 | int ival; 31 | double dval; 32 | char strval[256]; 33 | } solid_node_val; 34 | 35 | typedef struct solid_ast_node { 36 | solid_node_ins ins; 37 | struct solid_ast_node *arg1; 38 | struct solid_ast_node *arg2; 39 | solid_node_val val; 40 | } solid_ast_node; 41 | 42 | typedef union YYSTYPE { 43 | int token; 44 | solid_ast_node *node; 45 | } YYSTYPE; 46 | 47 | solid_node_val solid_null_value(); 48 | solid_node_val solid_int_value(int i); 49 | solid_node_val solid_double_value(double d); 50 | solid_node_val solid_str_value(char *s); 51 | 52 | solid_ast_node *solid_make_node(solid_node_ins ins, solid_ast_node *arg1, solid_ast_node *arg2, solid_node_val val); 53 | 54 | solid_ast_node *solid_const_integer_node(int i); 55 | solid_ast_node *solid_double_integer_node(double d); 56 | solid_ast_node *solid_const_string_node(char *s); 57 | solid_ast_node *solid_const_bool_node(int i); 58 | solid_ast_node *solid_identifier_node(char *name); 59 | 60 | #endif 61 | -------------------------------------------------------------------------------- /core/object.c: -------------------------------------------------------------------------------- 1 | #include "object.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "utils.h" 9 | 10 | void solid_set_namespace(solid_object *ns, solid_object *name, solid_object *o) 11 | { 12 | if (ns->type != T_INSTANCE) { 13 | debug("ns->type: %d", ns->type); 14 | log_err("Namespace is not an instance"); 15 | exit(1); 16 | } else { 17 | hash_map *h = ns->data.instance; 18 | set_hash(h, solid_get_str_value(name), (void *) o); 19 | } 20 | } 21 | 22 | solid_object *solid_get_namespace(solid_object *ns, solid_object *name) 23 | { 24 | if (ns->type != T_INSTANCE) { 25 | debug("ns->type: %d", ns->type); 26 | log_err("Namespace is not an instance"); 27 | exit(1); 28 | } else { 29 | hash_map *h = ns->data.instance; 30 | solid_object *ret = get_hash(h, solid_get_str_value(name)); 31 | if (ret == NULL) { 32 | log_err("Variable \"%s\" not in namespace", solid_get_str_value(name)); 33 | exit(1); 34 | } 35 | return ret; 36 | } 37 | } 38 | 39 | int solid_namespace_has(solid_object *ns, solid_object *name) 40 | { 41 | if (ns->type != T_INSTANCE) { 42 | debug("ns->type: %d", ns->type); 43 | log_err("Namespace is not an instance"); 44 | exit(1); 45 | } else { 46 | hash_map *h = ns->data.instance; 47 | solid_object *ret = get_hash(h, solid_get_str_value(name)); 48 | if (ret == NULL) { 49 | return 0; 50 | } 51 | return 1; 52 | } 53 | } 54 | 55 | solid_object *solid_make_object(solid_vm *vm) 56 | { 57 | solid_object *ret = (solid_object *) malloc(sizeof(solid_object)); 58 | ret->type = T_NULL; 59 | ret->marked = 0; 60 | ret->data_size = 0; 61 | solid_gc_add_object(vm, ret); 62 | return ret; 63 | } 64 | 65 | solid_object *solid_instance(solid_vm *vm) 66 | { 67 | solid_object *ret = solid_make_object(vm); 68 | ret->type = T_INSTANCE; 69 | ret->data_size = sizeof(hash_map *); 70 | ret->data.instance = make_hash_map(); 71 | return ret; 72 | } 73 | 74 | solid_object *solid_int(solid_vm *vm, int val) 75 | { 76 | solid_object *ret = solid_make_object(vm); 77 | ret->type = T_INT; 78 | ret->data_size = sizeof(int); 79 | ret->data.i = val; 80 | return ret; 81 | } 82 | 83 | solid_object *solid_double(solid_vm *vm, double val) 84 | { 85 | solid_object *ret = solid_make_object(vm); 86 | ret->type = T_DOUBLE; 87 | ret->data_size = sizeof(double); 88 | ret->data.d = val; 89 | return ret; 90 | } 91 | 92 | solid_object *solid_str(solid_vm *vm, char *val) 93 | { 94 | solid_object *ret = solid_make_object(vm); 95 | size_t len = strlen(val) + sizeof(char); //Null byte at the end 96 | ret->type = T_STR; 97 | ret->data_size = len; 98 | ret->data.str = malloc(len); 99 | strcpy(ret->data.str, val); 100 | return ret; 101 | } 102 | 103 | solid_object *solid_bool(solid_vm *vm, bool val) 104 | { 105 | solid_object *ret = solid_make_object(vm); 106 | ret->type = T_BOOL; 107 | ret->data_size = sizeof(int); 108 | ret->data.b = val; 109 | return ret; 110 | } 111 | 112 | solid_object *solid_list(solid_vm *vm, list_node *l) 113 | { 114 | solid_object *ret = solid_make_object(vm); 115 | ret->type = T_LIST; 116 | ret->data_size = sizeof(list_node *); 117 | ret->data.list = l; 118 | return ret; 119 | } 120 | 121 | solid_object *solid_func(solid_vm *vm) 122 | { 123 | solid_object *ret = solid_make_object(vm); 124 | ret->type = T_FUNC; 125 | ret->data_size = 0; 126 | return ret; //We don't do anything here: all bytecode will be added later 127 | } 128 | 129 | solid_object *solid_cfunc(solid_vm *vm) 130 | { 131 | solid_object *ret = solid_make_object(vm); 132 | ret->type = T_CFUNC; 133 | return ret; 134 | } 135 | 136 | solid_object *solid_struct(solid_vm *vm, void *val) 137 | { 138 | solid_object *ret = solid_make_object(vm); 139 | ret->type = T_STRUCT; 140 | ret->data.cstruct = val; 141 | return ret; 142 | } 143 | 144 | void solid_mark_object(solid_object *o, unsigned char m) 145 | { 146 | if (o != NULL) { 147 | if (o->marked != 0) { 148 | return; 149 | } 150 | 151 | o->marked = m; 152 | 153 | switch (o->type) { 154 | case T_INSTANCE: 155 | solid_mark_hash(o->data.instance, m); 156 | break; 157 | case T_LIST: 158 | solid_mark_list(o->data.list, m); 159 | break; 160 | default: 161 | break; 162 | } 163 | } 164 | } 165 | 166 | void solid_mark_list(list_node *l, unsigned char m) 167 | { 168 | if (l != NULL) { 169 | list_node *c; 170 | for (c = l->next; c != NULL; c = c->next) { 171 | if (c->data != NULL) { 172 | solid_mark_object((solid_object *) c->data, m); 173 | } 174 | } 175 | } 176 | } 177 | 178 | void solid_mark_hash(hash_map *h, unsigned char m) 179 | { 180 | int c = 0; 181 | list_node *l; 182 | list_node *cur; 183 | hash_val *hv; 184 | for (c = 0; c < 256; ++c) { 185 | l = h->buckets[c]; 186 | if (l != NULL) { 187 | for (cur = l->next; cur != NULL; cur = cur->next) { 188 | if (cur->data != NULL) { 189 | hv = (hash_val *) cur->data; 190 | solid_mark_object((solid_object *) hv->val, m); 191 | } 192 | } 193 | } 194 | } 195 | } 196 | 197 | void solid_delete_object(solid_vm *vm, solid_object *o) 198 | { 199 | switch (o->type) { 200 | case T_NULL: 201 | break; 202 | case T_INSTANCE: 203 | //solid_delete_hash(vm, o->data.instance); 204 | break; 205 | case T_LIST: 206 | solid_delete_list(vm, o->data.list); 207 | break; 208 | case T_INT: 209 | case T_DOUBLE: 210 | case T_STR: 211 | case T_BOOL: 212 | break; 213 | case T_FUNC: 214 | case T_CFUNC: 215 | return; 216 | case T_STRUCT: 217 | break; 218 | } 219 | free(o); 220 | } 221 | 222 | void solid_delete_list(solid_vm *vm, list_node *l) 223 | { 224 | if (l != NULL) { 225 | list_node *c; 226 | for (c = l->next; c != NULL; c = c->next) { 227 | if (c->data != NULL) { 228 | solid_delete_object(vm, (solid_object *) c->data); 229 | } 230 | free(c); 231 | } 232 | } 233 | free(l); 234 | } 235 | 236 | void solid_delete_hash(solid_vm *vm, hash_map *h) 237 | { 238 | int c = 0; 239 | list_node *l; 240 | list_node *cur; 241 | hash_val *hv; 242 | for (c = 0; c < 256; ++c) { 243 | l = h->buckets[c]; 244 | if (l != NULL) { 245 | for (cur = l->next; cur != NULL; cur = cur->next) { 246 | if (cur->data != NULL) { 247 | hv = (hash_val *) cur->data; 248 | solid_delete_object(vm, (solid_object *) hv->val); 249 | free(hv); 250 | } 251 | free(cur); 252 | } 253 | } 254 | free(l); 255 | } 256 | } 257 | 258 | solid_object *solid_clone_object(solid_vm *vm, solid_object *class) 259 | { 260 | solid_object *ret; 261 | switch (class->type) { 262 | case T_INSTANCE: 263 | ret = solid_make_object(vm); 264 | ret->type = T_INSTANCE; 265 | ret->data_size = sizeof(hash_map *); 266 | ret->data.instance = solid_clone_hash(vm, class->data.instance); 267 | break; 268 | case T_LIST: 269 | ret = solid_list(vm, solid_clone_list(vm, class->data.list)); 270 | break; 271 | case T_FUNC: 272 | case T_CFUNC: 273 | ret = class; 274 | break; 275 | default: 276 | ret = solid_make_object(vm); 277 | ret->type = class->type; 278 | ret->marked = class->marked; 279 | ret->data_size = class->data_size; 280 | ret->data = class->data; 281 | break; 282 | } 283 | return ret; 284 | } 285 | 286 | list_node *solid_clone_list(solid_vm *vm, list_node *l) 287 | { 288 | list_node *ret = make_list(); 289 | if (l != NULL) { 290 | list_node *c; 291 | for (c = l->next; c != NULL; c = c->next) { 292 | if (c->data != NULL) { 293 | insert_list(ret, solid_clone_object(vm, (solid_object *) c->data)); 294 | } 295 | } 296 | } 297 | return ret; 298 | } 299 | 300 | hash_map *solid_clone_hash(solid_vm *vm, hash_map *h) 301 | { 302 | int c = 0; 303 | hash_map *ret = make_hash_map(); 304 | list_node *l; 305 | for (c = 0; c < 256; ++c) { 306 | l = h->buckets[c]; 307 | if (l != NULL) { 308 | ret->buckets[c] = make_list(); 309 | if (l != NULL) { 310 | list_node *cur; 311 | for (cur = l->next; cur != NULL; cur = cur->next) { 312 | if (cur->data != NULL) { 313 | hash_val *hv = (hash_val *) malloc(sizeof(hash_val)); 314 | strncpy(hv->key, ((hash_val *) cur->data)->key, 256); 315 | hv->val = solid_clone_object(vm, (solid_object *) ((hash_val *) cur->data)->val); 316 | insert_list(ret->buckets[c], hv); 317 | } 318 | } 319 | } 320 | } 321 | } 322 | return ret; 323 | } 324 | 325 | 326 | int solid_get_int_value(solid_object *o) 327 | { 328 | if (o->type == T_INT) { 329 | return o->data.i; 330 | } 331 | log_err("Object not of integral type"); 332 | exit(1); 333 | } 334 | 335 | double solid_get_double_value(solid_object *o) 336 | { 337 | if (o->type == T_DOUBLE) { 338 | return o->data.d; 339 | } else if (o->type == T_INT) { 340 | return (double) o->data.i; 341 | } 342 | log_err("Object not of numeric type"); 343 | exit(1); 344 | } 345 | 346 | char *solid_get_str_value(solid_object *o) 347 | { 348 | if (o->type == T_STR) { 349 | return o->data.str; 350 | } 351 | log_err("Object not of string type"); 352 | exit(1); 353 | } 354 | 355 | bool solid_get_bool_value(solid_object *o) 356 | { 357 | if (o->type == T_BOOL) { 358 | return o->data.b; 359 | } else if (o->type == T_INT) { 360 | return (bool) o->data.i; 361 | } 362 | log_err("Object not of boolean type"); 363 | exit(1); 364 | } 365 | 366 | list_node *solid_get_list_value(solid_object *o) 367 | { 368 | if (o->type == T_LIST) { 369 | return o->data.list; 370 | } 371 | log_err("Object not of list type"); 372 | exit(1); 373 | } 374 | 375 | void *solid_get_struct_value(solid_object *o) 376 | { 377 | if (o->type == T_STRUCT) { 378 | return o->data.list; 379 | } 380 | log_err("Object not of structure type"); 381 | exit(1); 382 | } 383 | -------------------------------------------------------------------------------- /core/object.h: -------------------------------------------------------------------------------- 1 | #ifndef SOLID_OBJECT_H 2 | #define SOLID_OBJECT_H 3 | 4 | #include 5 | #include 6 | 7 | typedef struct solid_object solid_object; 8 | 9 | #include "utils.h" 10 | #include "vm.h" 11 | 12 | typedef enum solid_type { 13 | T_NULL, //Throw an error when detected. 14 | T_INSTANCE, 15 | T_INT, 16 | T_DOUBLE, 17 | T_STR, 18 | T_BOOL, 19 | T_LIST, 20 | T_FUNC, 21 | T_CFUNC, 22 | T_STRUCT, //Arbitrary C struct. 23 | } solid_type; 24 | 25 | typedef union solid_object_data { 26 | hash_map *instance; 27 | int i; 28 | double d; 29 | char *str; 30 | bool b; 31 | list_node *list; 32 | void *func; 33 | void (*cfunc)(solid_vm *); 34 | void *cstruct; 35 | } solid_object_data; 36 | 37 | struct solid_object { 38 | solid_type type; 39 | char marked; 40 | size_t data_size; 41 | solid_object_data data; 42 | }; 43 | 44 | void solid_set_namespace(solid_object *ns, solid_object *name, solid_object *o); 45 | solid_object *solid_get_namespace(solid_object *ns, solid_object *name); 46 | int solid_namespace_has(solid_object *ns, solid_object *name); 47 | solid_object *solid_make_object(solid_vm *vm); 48 | solid_object *solid_instance(solid_vm *vm); 49 | solid_object *solid_int(solid_vm *vm, int val); 50 | solid_object *solid_double(solid_vm *vm, double val); 51 | solid_object *solid_str(solid_vm *vm, char *val); 52 | solid_object *solid_bool(solid_vm *vm, bool val); 53 | solid_object *solid_list(solid_vm *vm, list_node *l); 54 | solid_object *solid_func(solid_vm *vm); 55 | solid_object *solid_cfunc(solid_vm *vm); 56 | solid_object *solid_struct(solid_vm *vm, void *val); 57 | 58 | void solid_mark_object(solid_object *o, unsigned char m); 59 | void solid_mark_list(list_node *l, unsigned char m); 60 | void solid_mark_hash(hash_map *l, unsigned char m); 61 | 62 | void solid_delete_object(solid_vm *vm, solid_object *o); 63 | void solid_delete_list(solid_vm *vm, list_node *l); 64 | void solid_delete_hash(solid_vm *vm, hash_map *l); 65 | 66 | solid_object *solid_clone_object(solid_vm *vm, solid_object *class); 67 | list_node *solid_clone_list(solid_vm *vm, list_node *l); 68 | hash_map *solid_clone_hash(solid_vm *vm, hash_map *l); 69 | 70 | int solid_get_int_value(solid_object *o); 71 | double solid_get_double_value(solid_object *o); 72 | char *solid_get_str_value(solid_object *o); 73 | bool solid_get_bool_value(solid_object *o); 74 | list_node *solid_get_list_value(solid_object *o); 75 | void *solid_get_struct_value(solid_object *o); 76 | 77 | #endif 78 | -------------------------------------------------------------------------------- /core/parser.l: -------------------------------------------------------------------------------- 1 | %{ 2 | #include "utils.h" 3 | #include "scanner_state.h" 4 | #include "node.h" 5 | #include 6 | #include 7 | 8 | #include "parser.h" /* This is where it gets the definition for yylloc from */ 9 | #define YY_USER_ACTION yylloc->first_line = yylloc->last_line = yylineno; 10 | 11 | int f(int token, yyscan_t scanner); 12 | 13 | %} 14 | 15 | %option outfile="lexer.c" header-file="lexer.h" 16 | 17 | %option warn nodefault 18 | 19 | %option reentrant noyywrap never-interactive nounistd 20 | %option bison-bridge bison-locations 21 | %option yylineno 22 | %option extra-type="struct scanner_state *" 23 | 24 | %% 25 | 26 | [ \t\r\n] {} 27 | #.* {} 28 | "if" {return TIF;} 29 | "while" {return TWHILE;} 30 | "fn" {return TFN;} 31 | "ns" {return TNS;} 32 | "return" {return TRETURN;} 33 | "true" {return TTRUE;} 34 | "false" {return TFALSE;} 35 | "=" {return TEQUALS;} 36 | ":=" {return TGLOBAL;} 37 | "@" {return TAT;} 38 | "$" {return TDOLLAR;} 39 | "(" {return TLPAREN;} 40 | ")" {return TRPAREN;} 41 | "[" {return TLSQUARE;} 42 | "]" {return TRSQUARE;} 43 | \{|do {return TLBRACE;} 44 | \}|end {return TRBRACE;} 45 | -?[0-9]+ {yylval->node = solid_const_integer_node(atoi(yytext)); return TINTEGER;} 46 | -?[0-9]+\.[0-9]+ {yylval->node = solid_const_double_node(atof(yytext)); return TDOUBLE;} 47 | "~" {return TTILDE;} 48 | "." {return TDOT;} 49 | "," {return TCOMMA;} 50 | ";" {return TSEMICOLON;} 51 | (-|[+*/<>=^&%:!])* {yylval->node = solid_identifier_node(yytext); return TINLINE_IDENTIFIER;} 52 | [a-zA-Z_][a-zA-Z0-9_]* {yylval->node = solid_identifier_node(yytext); return TIDENTIFIER;} 53 | \"([^\\\"]|\\.)*\" {yytext[strlen(yytext) - 1] = '\0'; yylval->node = solid_const_string_node(yytext+1); return TSTRING;} 54 | . {log_err("Unknown token %s", yytext); yyterminate();} 55 | 56 | %% 57 | -------------------------------------------------------------------------------- /core/parser.y: -------------------------------------------------------------------------------- 1 | %{ 2 | #define YYERROR_VERBOSE 1 3 | #include "node.h" 4 | #include 5 | %} 6 | 7 | %output "parser.c" 8 | %defines "parser.h" 9 | 10 | %locations 11 | %define api.pure 12 | %lex-param {void *scanner} 13 | %parse-param {solid_ast_node **root} 14 | %parse-param {void *scanner} 15 | 16 | %token TIDENTIFIER TINLINE_IDENTIFIER TSTRING TINTEGER TDOUBLE 17 | %token TTRUE TFALSE 18 | 19 | %token TSEMICOLON; 20 | %token TLPAREN TRPAREN TLSQUARE TRSQUARE TLBRACE TRBRACE TAT TDOLLAR TCOMMA TDOT TTILDE 21 | 22 | %token TIF TWHILE TEQUALS TGLOBAL TFN TNS TRETURN 23 | 24 | %type program 25 | 26 | %type stmt_list ns_var expr constant identifier func_args param_list list_items 27 | 28 | %start program 29 | 30 | %% 31 | 32 | program : stmt_list {*root = $1;} 33 | ; 34 | 35 | stmt_list : expr TSEMICOLON {$$ = solid_make_node(STATEMENT_LIST, $1, NULL, solid_null_value());} 36 | | expr TSEMICOLON stmt_list {$$ = solid_make_node(STATEMENT_LIST, $1, $3, solid_null_value());} 37 | | expr error TSEMICOLON stmt_list {$$ = solid_make_node(STATEMENT_LIST, $1, $4, solid_null_value());} 38 | ; 39 | 40 | ns_var : identifier {$$ = solid_make_node(NS_VAR, $1, NULL, solid_null_value());} 41 | | TDOLLAR {$$ = solid_make_node(NS_VAR, solid_identifier_node("$"), NULL, solid_null_value());} 42 | | expr TDOT identifier {$$ = solid_make_node(NS_VAR, $3, $1, solid_null_value());} 43 | ; 44 | 45 | 46 | expr : constant {$$ = $1;} 47 | | TAT identifier {$$ = solid_make_node(GGET, $2, NULL, solid_null_value());} 48 | | expr TINLINE_IDENTIFIER expr {$$ = solid_make_node(CALL, 49 | solid_make_node(GET, solid_make_node(NS_VAR, $2, NULL, solid_null_value()), NULL, solid_null_value()), 50 | solid_make_node(FUNC_ARGS, $3, solid_make_node(FUNC_ARGS, $1, NULL, solid_null_value()), solid_null_value()), solid_null_value());} 51 | | expr TLPAREN func_args TRPAREN {$$ = solid_make_node(CALL, $1, $3, solid_null_value());} 52 | | ns_var {$$ = solid_make_node(GET, $1, NULL, solid_null_value());} 53 | | ns_var TEQUALS expr {$$ = solid_make_node(SET, $3, $1, solid_null_value());} 54 | | identifier TGLOBAL expr {$$ = solid_make_node(GSET, $3, $1, solid_null_value());} 55 | | TLBRACE stmt_list TRBRACE {$$ = solid_make_node(BLOCK, $2, NULL, solid_null_value());} 56 | | TIF expr expr {$$ = solid_make_node(IF, $2, $3, solid_null_value());} 57 | | TWHILE expr expr {$$ = solid_make_node(WHILE, $2, $3, solid_null_value());} 58 | | TFN param_list expr {$$ = solid_make_node(FN, $2, $3, solid_null_value());} 59 | | TNS expr {$$ = solid_make_node(NS, $2, NULL, solid_null_value());} 60 | | TRETURN expr {$$ = solid_make_node(RET, $2, NULL, solid_null_value());} 61 | ; 62 | 63 | constant : TSTRING {$$ = $1;} 64 | | TINTEGER {$$ = $1;} 65 | | TDOUBLE {$$ = $1;} 66 | | TTRUE {$$ = solid_const_bool_node(1);} 67 | | TFALSE {$$ = solid_const_bool_node(0);} 68 | | TLSQUARE list_items TRSQUARE {$$ = solid_make_node(CONST_LIST, $2, NULL, solid_null_value());} 69 | ; 70 | 71 | identifier : TIDENTIFIER | TINLINE_IDENTIFIER {$$ = $1;} 72 | ; 73 | 74 | param_list : /* blank */ {$$ = solid_make_node(PARAM_LIST, NULL, NULL, solid_null_value());} 75 | | TTILDE {$$ = solid_make_node(PARAM_LIST, NULL, NULL, solid_null_value());} 76 | | TIDENTIFIER {$$ = solid_make_node(PARAM_LIST, $1, NULL, solid_null_value());} 77 | | param_list TCOMMA TIDENTIFIER {$$ = solid_make_node(PARAM_LIST, $3, $1, solid_null_value());} 78 | ; 79 | 80 | func_args : /* blank */ {$$ = solid_make_node(FUNC_ARGS, NULL, NULL, solid_null_value());} 81 | | expr {$$ = solid_make_node(FUNC_ARGS, $1, NULL, solid_null_value());} 82 | | func_args TCOMMA expr {$$ = solid_make_node(FUNC_ARGS, $3, $1, solid_null_value());} 83 | ; 84 | 85 | list_items : /* blank */ {$$ = solid_make_node(LIST_BODY, NULL, NULL, solid_null_value());} 86 | | expr {$$ = solid_make_node(LIST_BODY, $1, NULL, solid_null_value());} 87 | | list_items TCOMMA expr {$$ = solid_make_node(LIST_BODY, $3, $1, solid_null_value());} 88 | ; 89 | 90 | %% 91 | -------------------------------------------------------------------------------- /core/scanner_state.h: -------------------------------------------------------------------------------- 1 | #ifndef SOLID_SCANNER_STATE_H 2 | #define SOLID_SCANNER_STATE_H 3 | 4 | typedef struct scanner_state { 5 | int insert; 6 | } scanner_state; 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /core/solid.c: -------------------------------------------------------------------------------- 1 | #include "solid.h" 2 | #ifndef WINDOWS 3 | #include "linenoise/linenoise.h" 4 | #endif 5 | 6 | int yyparse(solid_ast_node **expression, yyscan_t scanner); 7 | 8 | solid_ast_node *solid_parse_expr(char *expr) 9 | { 10 | solid_ast_node *ret = NULL; 11 | yyscan_t scanner; 12 | YY_BUFFER_STATE state; 13 | 14 | scanner_state scan_state = {.insert = 0}; 15 | yylex_init_extra(&scan_state, &scanner); 16 | state = yy_scan_string(expr, scanner); 17 | yyparse(&ret, scanner); 18 | yy_delete_buffer(state, scanner); 19 | yylex_destroy(scanner); 20 | 21 | return ret; 22 | } 23 | 24 | solid_ast_node *solid_parse_file(char *path) 25 | { 26 | FILE *f = fopen(path, "r"); 27 | 28 | if(f == NULL) { 29 | return NULL; 30 | } 31 | 32 | char buffer[1024 * 1024] = {0}; 33 | fread(buffer, sizeof(char), 1024 * 1024, f); 34 | fclose(f); 35 | return solid_parse_expr(buffer); 36 | } 37 | 38 | void solid_compile(solid_vm *vm) 39 | { 40 | vm->regs[255] = solid_parse_tree(vm, solid_parse_expr(solid_get_str_value(solid_pop_stack(vm)))); 41 | } 42 | 43 | void solid_import(solid_vm *vm) 44 | { 45 | char *input = solid_get_str_value(solid_pop_stack(vm)); 46 | char *dot = strrchr(input, '.'); 47 | char *extension; 48 | if (!dot || dot == input) { 49 | extension = ""; 50 | } 51 | extension = dot + 1; 52 | if (strcmp(extension, "sol") == 0) { 53 | solid_object *func = solid_parse_tree(vm, solid_parse_file(input)); 54 | solid_call_func(vm, func); 55 | } else if (strcmp(extension, "so") == 0) { 56 | #ifndef WINDOWS 57 | char buffer[256]; 58 | getcwd(buffer, 256); 59 | strcat(buffer, "/"); 60 | strcat(buffer, input); 61 | void *handle = dlopen(buffer, RTLD_LAZY); 62 | void (*init)(solid_vm *); 63 | if (handle == NULL) { 64 | log_err("Loading external library %s failed with error %s", input, dlerror()); 65 | exit(1); 66 | } 67 | dlerror(); 68 | *(void **) (&init) = dlsym(handle, "solid_init"); 69 | init(vm); 70 | //dlclose(handle); 71 | #endif 72 | } 73 | } 74 | 75 | #ifndef WINDOWS 76 | void solid_repl() 77 | { 78 | char *input; 79 | char *buffer; 80 | solid_vm *vm = solid_make_vm(); 81 | solid_set_namespace(solid_get_current_namespace(vm), solid_str(vm, "compile"), solid_define_c_function(vm, solid_compile)); 82 | solid_set_namespace(solid_get_current_namespace(vm), solid_str(vm, "import"), solid_define_c_function(vm, solid_import)); 83 | while ((input = linenoise("solid> ")) != NULL) { 84 | if (input[0] != '\0') { 85 | buffer = calloc(strlen(input) + 1, sizeof(char)); 86 | strcpy(buffer, input); 87 | buffer[strlen(input)] = '\n'; 88 | linenoiseHistoryAdd(input); 89 | solid_object *curexpr = solid_parse_tree(vm, solid_parse_expr(buffer)); 90 | //free(buffer); 91 | solid_call_func(vm, curexpr); 92 | solid_push_stack(vm, vm->regs[255]); 93 | solid_print(vm); 94 | } 95 | //free(input); 96 | } 97 | } 98 | #endif 99 | 100 | int main(int argc, char *argv[]) 101 | { 102 | if (argc > 1) { 103 | solid_ast_node *tree = solid_parse_file(argv[1]); 104 | if(tree == NULL) { 105 | report_error(argv[1]); 106 | exit(EXIT_FAILURE); 107 | } 108 | 109 | solid_vm *vm = solid_make_vm(); 110 | solid_object *mainfunc = solid_parse_tree(vm, tree); 111 | solid_set_namespace(solid_get_current_namespace(vm), solid_str(vm, "compile"), solid_define_c_function(vm, solid_compile)); 112 | solid_set_namespace(solid_get_current_namespace(vm), solid_str(vm, "import"), solid_define_c_function(vm, solid_import)); 113 | solid_call_func(vm, mainfunc); 114 | 115 | /* [FIXME: Track down all the other 'allocs that need to be free'd] */ 116 | //free(vm); 117 | } 118 | #ifndef WINDOWS 119 | else { 120 | solid_repl(); 121 | } 122 | #endif 123 | 124 | return 0; 125 | } 126 | -------------------------------------------------------------------------------- /core/solid.h: -------------------------------------------------------------------------------- 1 | #ifndef SOLID_SOLID_H 2 | #define SOLID_SOLID_H 3 | #define _GNU_SOURCE 4 | #include 5 | #include 6 | #include 7 | #include 8 | #ifndef WINDOWS 9 | #include 10 | #endif 11 | #include 12 | 13 | #include "utils.h" 14 | #include "scanner_state.h" 15 | #include "node.h" 16 | #include "vm.h" 17 | #include "ast.h" 18 | #include "common.h" 19 | #include "lexer.h" 20 | 21 | int yyparse(solid_ast_node **expression, yyscan_t scanner); 22 | 23 | solid_ast_node *solid_parse_expr(char *expr); 24 | 25 | solid_ast_node *solid_parse_file(char *path); 26 | 27 | void solid_compile(solid_vm *vm); 28 | 29 | void solid_import(solid_vm *vm); 30 | #endif 31 | -------------------------------------------------------------------------------- /core/utils.c: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | 3 | list_node *make_list_node(void *d) 4 | { 5 | list_node *ret = (list_node *) malloc(sizeof(list_node)); 6 | ret->data = d; 7 | return ret; 8 | } 9 | 10 | list_node *make_list() 11 | { 12 | list_node *start = (list_node *) malloc(sizeof(list_node)); 13 | list_node *end = (list_node *) malloc(sizeof(list_node)); 14 | start->prev = NULL; 15 | start->next = end; 16 | start->data = NULL; 17 | end->prev = start; 18 | end->next = NULL; 19 | end->data = NULL; 20 | return start; 21 | } 22 | 23 | int find_list(list_node *l, void *data) 24 | { 25 | list_node *c; 26 | for (c = l; c->next != NULL; c = c->next) { 27 | if (c->data == data) { 28 | return 1; 29 | } 30 | } 31 | return 0; 32 | } 33 | 34 | void insert_list(list_node *l, void *data) 35 | { 36 | list_node *ins = (list_node *) malloc(sizeof(list_node)); 37 | ins->data = data; 38 | ins->next = l->next; 39 | l->next = ins; 40 | ins->next->prev = ins; 41 | ins->prev = l; 42 | } 43 | 44 | void remove_list(list_node *l, void *data) 45 | { 46 | list_node *c; 47 | for (c = l; c->next != NULL; c = c->next) { 48 | if (c->data == data) { 49 | c->prev->next = c->next; 50 | c->next->prev = c->prev; 51 | } 52 | } 53 | } 54 | 55 | int length_list(list_node *l) 56 | { 57 | int a = 0; 58 | list_node *c; 59 | for (c = l; c->next != NULL; c = c->next) { 60 | if (c->data != NULL) { 61 | a++; 62 | } 63 | } 64 | return a; 65 | } 66 | 67 | hash_map *make_hash_map() 68 | { 69 | hash_map *ret = (hash_map *) malloc(sizeof(hash_map)); 70 | int c; 71 | for (c = 0; c < 256; c++) { 72 | ret->buckets[c] = NULL; 73 | } 74 | return ret; 75 | } 76 | 77 | int hash(char *key) 78 | { 79 | int h = 5381; 80 | 81 | unsigned char c; 82 | for (c = *key; c != '\0'; c = *++key) 83 | h = h * 33 + c; 84 | 85 | return abs(h % 256); 86 | } 87 | 88 | void *get_hash(hash_map *m, char *key) 89 | { 90 | list_node *cur = m->buckets[hash(key)]; 91 | if (cur == NULL) return NULL; 92 | for (; cur->next != NULL; cur = cur->next) { 93 | if (cur->data != NULL) { 94 | if (strcmp(key, ((hash_val *) cur->data)->key) == 0) { 95 | return ((hash_val *) cur->data)->val; 96 | } 97 | } 98 | } 99 | return NULL; 100 | } 101 | 102 | void set_hash(hash_map *m, char *key, void *val) 103 | { 104 | hash_val *hv = (hash_val *) malloc(sizeof(hash_val)); 105 | strcpy(hv->key, key); 106 | hv->val = val; 107 | int hk = hash(key); 108 | if (m->buckets[hk] == NULL) { 109 | m->buckets[hk] = make_list(); 110 | } else { 111 | list_node *c; 112 | for (c = m->buckets[hk]; c->next != NULL; c = c->next) { 113 | if (c->data != NULL) { 114 | if (strcmp(((hash_val *) c->data)->key, key) == 0) { 115 | free(c->data); 116 | c->data = (void *) hv; 117 | return; 118 | } 119 | } 120 | } 121 | } 122 | insert_list(m->buckets[hk], (void *) hv); 123 | } 124 | 125 | hash_map *copy_hash(hash_map *m) 126 | { 127 | hash_map *ret = make_hash_map(); 128 | int i; 129 | for (i = 0; i < 256; i++) { 130 | list_node *c = m->buckets[i]; 131 | if (c != NULL) { 132 | for (; c->next != NULL; c = c->next) { 133 | if (c->data != NULL) { 134 | set_hash(ret, ((hash_val *) c->data)->key, ((hash_val *) c->data)->val); 135 | } 136 | } 137 | } 138 | } 139 | return ret; 140 | } 141 | -------------------------------------------------------------------------------- /core/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef SOLID_UTILS_H 2 | #define SOLID_UTILS_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #ifdef NDEBUG 11 | 12 | #define debug(M, ...) 13 | #else 14 | #define debug(M, ...) fprintf(stdout, "DEBUG %s:%d: " M "\n", __FILE__, __LINE__, ##__VA_ARGS__) 15 | 16 | #ifndef REGISTER_DEBUG 17 | #define regdebug(M, ...) 18 | #else 19 | #define regdebug(M, ...) fprintf(stdout, "REGDEBUG %s:%d: " M "\n", __FILE__, __LINE__, ##__VA_ARGS__) 20 | #endif 21 | 22 | #endif 23 | 24 | #define clean_errno() (errno == 0 ? "None" : strerror(errno)) 25 | 26 | #define log_err(M, ...) { fprintf(stderr, "Error: " M "\n", ##__VA_ARGS__); } 27 | 28 | #define log_warn(M, ...) fprintf(stderr, "[WARN] (%s:%d: errno: %s) " M "\n", __FILE__, __LINE__, clean_errno(), ##__VA_ARGS__) 29 | 30 | #define log_info(M, ...) fprintf(stderr, "[INFO] (%s:%d) " M "\n", __FILE__, __LINE__, ##__VA_ARGS__) 31 | 32 | #define check(A, M, ...) if(!(A)) { log_err(M, ##__VA_ARGS__); errno=0; goto error; } 33 | 34 | #define sentinel(M, ...) { log_err(M, ##__VA_ARGS__); errno=0; goto error; } 35 | 36 | #define check_mem(A) check((A), "Out of memory.") 37 | 38 | #define check_debug(A, M, ...) if(!(A)) { debug(M, ##__VA_ARGS__); errno=0; goto error; } 39 | 40 | typedef struct list_node { 41 | struct list_node *prev; 42 | struct list_node *next; 43 | void *data; 44 | } list_node; 45 | 46 | list_node *make_list_node(void *d); 47 | list_node *make_list(); 48 | int find_list(list_node *l, void *data); 49 | void insert_list(list_node *l, void *data); 50 | void remove_list(list_node *l, void *data); 51 | int length_list(list_node *l); 52 | 53 | typedef struct hash_val { 54 | char key[256]; 55 | void *val; 56 | } hash_val; 57 | 58 | typedef struct hash_map { 59 | list_node *buckets[256]; 60 | } hash_map; 61 | 62 | hash_map *make_hash_map(); 63 | int hash(char *key); 64 | void *get_hash(hash_map *m, char *key); 65 | void set_hash(hash_map *m, char *key, void *val); 66 | hash_map *copy_hash(hash_map *m); 67 | 68 | typedef struct hash_set { 69 | list_node *buckets; 70 | } hash_set; 71 | 72 | #endif 73 | -------------------------------------------------------------------------------- /core/vm.c: -------------------------------------------------------------------------------- 1 | #include "vm.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "utils.h" 8 | #include "object.h" 9 | 10 | solid_vm *solid_make_vm() 11 | { 12 | solid_vm *ret = (solid_vm *) malloc(sizeof(solid_vm)); 13 | ret->stack = make_list(); 14 | ret->all_objects = make_list(); 15 | memset(ret->regs, 0, 256); 16 | memset(ret->namespace_stack, 0, 256); 17 | ret->namespace_stack[0] = solid_instance(ret); 18 | ret->namespace_stack_pointer = 0; 19 | solid_set_namespace(solid_get_current_namespace(ret), solid_str(ret, "!!"), solid_define_c_function(ret, solid_nth_list)); 20 | solid_set_namespace(solid_get_current_namespace(ret), solid_str(ret, "print"), solid_define_c_function(ret, solid_print)); 21 | solid_set_namespace(solid_get_current_namespace(ret), solid_str(ret, "clone"), solid_define_c_function(ret, solid_clone)); 22 | solid_set_namespace(solid_get_current_namespace(ret), solid_str(ret, ":"), solid_define_c_function(ret, solid_cons)); 23 | solid_set_namespace(solid_get_current_namespace(ret), solid_str(ret, "+"), solid_define_c_function(ret, solid_add)); 24 | solid_set_namespace(solid_get_current_namespace(ret), solid_str(ret, "-"), solid_define_c_function(ret, solid_sub)); 25 | solid_set_namespace(solid_get_current_namespace(ret), solid_str(ret, "*"), solid_define_c_function(ret, solid_mul)); 26 | solid_set_namespace(solid_get_current_namespace(ret), solid_str(ret, "/"), solid_define_c_function(ret, solid_div)); 27 | solid_set_namespace(solid_get_current_namespace(ret), solid_str(ret, "=="), solid_define_c_function(ret, solid_eq)); 28 | solid_set_namespace(solid_get_current_namespace(ret), solid_str(ret, "<"), solid_define_c_function(ret, solid_lt)); 29 | solid_set_namespace(solid_get_current_namespace(ret), solid_str(ret, "<="), solid_define_c_function(ret, solid_lte)); 30 | solid_set_namespace(solid_get_current_namespace(ret), solid_str(ret, ">"), solid_define_c_function(ret, solid_gt)); 31 | solid_set_namespace(solid_get_current_namespace(ret), solid_str(ret, ">="), solid_define_c_function(ret, solid_gte)); 32 | solid_set_namespace(solid_get_current_namespace(ret), solid_str(ret, "gc"), solid_define_c_function(ret, solid_gc)); 33 | return ret; 34 | } 35 | 36 | void solid_push_stack(solid_vm *vm, solid_object *o) 37 | { 38 | insert_list(vm->stack, (void *) o); 39 | } 40 | 41 | solid_object *solid_pop_stack(solid_vm *vm) 42 | { 43 | list_node *n = vm->stack->next; 44 | if (n->data == NULL) { 45 | log_err("Stack is empty"); 46 | exit(1); 47 | } else { 48 | n->prev->next = n->next; 49 | n->next->prev = n->prev; 50 | solid_object *ret = (solid_object *) n->data; 51 | free(n); 52 | return ret; 53 | } 54 | } 55 | 56 | void solid_push_list(solid_object *list, solid_object *o) 57 | { 58 | insert_list(list->data.list, (void *) o); 59 | } 60 | 61 | solid_object *solid_pop_list(solid_object *list) 62 | { 63 | list_node *n = ((list_node *) list)->next; 64 | if (n->data == NULL) { 65 | log_err("List is empty"); 66 | exit(1); 67 | } else { 68 | n->prev->next = n->next; 69 | n->next->prev = n->prev; 70 | solid_object *ret = (solid_object *) n->data; 71 | return ret; 72 | } 73 | } 74 | 75 | void solid_push_namespace(solid_vm *vm) 76 | { 77 | if (vm->namespace_stack_pointer >= 255) { 78 | log_err("Namespace stack overflow"); 79 | exit(1); 80 | } 81 | vm->namespace_stack[vm->namespace_stack_pointer+1] = solid_clone_object(vm, vm->namespace_stack[vm->namespace_stack_pointer]); 82 | vm->namespace_stack_pointer++; 83 | } 84 | 85 | void solid_pop_namespace(solid_vm *vm) 86 | { 87 | if (vm->namespace_stack_pointer == 0) { 88 | log_err("Namespace stack underflow"); 89 | exit(1); 90 | } 91 | solid_delete_object(vm, vm->namespace_stack[vm->namespace_stack_pointer--]); 92 | } 93 | 94 | void solid_push_predefined_namespace(solid_vm *vm, solid_object *namespace) 95 | { 96 | if (vm->namespace_stack_pointer >= 255) { 97 | log_err("Namespace stack overflow"); 98 | exit(1); 99 | } 100 | vm->namespace_stack[++vm->namespace_stack_pointer] = namespace; 101 | } 102 | 103 | solid_object *solid_pop_predefined_namespace(solid_vm *vm) 104 | { 105 | if (vm->namespace_stack_pointer == 0) { 106 | log_err("Namespace stack underflow"); 107 | exit(1); 108 | } 109 | return vm->namespace_stack[vm->namespace_stack_pointer--]; 110 | } 111 | 112 | solid_object *solid_get_current_namespace(solid_vm *vm) 113 | { 114 | return vm->namespace_stack[vm->namespace_stack_pointer]; 115 | } 116 | 117 | void solid_gc_add_object(solid_vm *vm, solid_object *o) 118 | { 119 | insert_list(vm->all_objects, (void *) o); 120 | } 121 | 122 | void solid_gc(solid_vm *vm) 123 | { 124 | for (int i = vm->namespace_stack_pointer; i >= 0; i--) { 125 | solid_mark_object(vm->namespace_stack[i], 1); 126 | } 127 | 128 | list_node *c; 129 | solid_object *cur; 130 | for (c = vm->all_objects->next; c != NULL; c = c->next) { 131 | if (c->data != NULL) { 132 | cur = (solid_object *) c->data; 133 | if (cur->marked == 0) { 134 | solid_delete_object(vm, cur); 135 | list_node *prev = c->prev; 136 | c->prev->next = c->next; 137 | c->next->prev = c->prev; 138 | free(c); 139 | c = prev; 140 | } else if (cur->marked == 1) { 141 | cur->marked = 0; 142 | } 143 | } 144 | } 145 | vm->regs[255] = solid_str(vm, "Trash compactor completing operation"); 146 | } 147 | 148 | solid_object *solid_define_function(solid_vm *vm, solid_bytecode *inslist) 149 | { 150 | solid_object *ret = solid_func(vm); 151 | solid_function *fval = (solid_function *) malloc(sizeof(solid_function)); 152 | fval->bcode = inslist; 153 | ret->data.func = fval; 154 | return ret; 155 | } 156 | 157 | solid_object *solid_define_c_function(solid_vm *vm, void (*function)(solid_vm *vm)) 158 | { 159 | solid_object *ret = solid_cfunc(vm); 160 | ret->data.cfunc = function; 161 | return ret; 162 | } 163 | 164 | void solid_nth_list(solid_vm *vm) 165 | { 166 | solid_object *index = solid_pop_stack(vm); 167 | int i = solid_get_int_value(index); 168 | solid_object *list = solid_pop_stack(vm); 169 | list_node *l = list->data.list; 170 | int counter = 0; 171 | list_node *c; 172 | for (c = l->next; c->next != NULL; c = c->next) { 173 | if (c->data != NULL) { 174 | if (counter == i) { 175 | vm->regs[255] = (solid_object *) c->data; 176 | return; 177 | } 178 | counter++; 179 | } 180 | } 181 | log_err("List index out of bounds"); 182 | exit(1); 183 | } 184 | 185 | void solid_print(solid_vm *vm) 186 | { 187 | solid_object *in = solid_pop_stack(vm); 188 | if (in->type == T_INT) { 189 | fprintf(stdout, "%d\n", solid_get_int_value(in)); 190 | } else if (in->type == T_DOUBLE) { 191 | fprintf(stdout, "%lf\n", solid_get_double_value(in)); 192 | } else if (in->type == T_STR) { 193 | fprintf(stdout, "%s\n", solid_get_str_value(in)); 194 | } else if (in->type == T_FUNC) { 195 | fprintf(stdout, "%s\n", "Function"); 196 | } else if (in->type == T_INSTANCE) { 197 | fprintf(stdout, "%s\n", "Object"); 198 | } else { 199 | fprintf(stdout, "Unknown of type %d\n", in->type); 200 | } 201 | vm->regs[255] = in; 202 | } 203 | 204 | void solid_clone(solid_vm *vm) 205 | { 206 | solid_object *ns = solid_pop_stack(vm); 207 | vm->regs[255] = solid_clone_object(vm, ns); 208 | } 209 | 210 | void solid_cons(solid_vm *vm) 211 | { 212 | solid_object *list = solid_pop_stack(vm); 213 | solid_object *elem = solid_pop_stack(vm); 214 | list_node *ret = make_list(); 215 | insert_list(ret, (void *) elem); 216 | ret->next->next = list->data.list; 217 | list->data.list->prev = ret->next; 218 | vm->regs[255] = solid_list(vm, ret); 219 | } 220 | 221 | void solid_add(solid_vm *vm) 222 | { 223 | solid_object *b = solid_pop_stack(vm); 224 | solid_object *a = solid_pop_stack(vm); 225 | if ((a->type != T_INT && a->type != T_DOUBLE) || (b->type != T_INT && b->type != T_DOUBLE)) { 226 | log_err("Attempt to apply operator \"+\" on invalid types"); 227 | exit(1); 228 | } 229 | if (a->type == T_DOUBLE || b->type == T_DOUBLE) { 230 | vm->regs[255] = solid_double(vm, solid_get_double_value(a) + solid_get_double_value(b)); 231 | } else { 232 | vm->regs[255] = solid_int(vm, solid_get_int_value(a) + solid_get_int_value(b)); 233 | } 234 | } 235 | 236 | void solid_sub(solid_vm *vm) 237 | { 238 | solid_object *b = solid_pop_stack(vm); 239 | solid_object *a = solid_pop_stack(vm); 240 | if ((a->type != T_INT && a->type != T_DOUBLE) || (b->type != T_INT && b->type != T_DOUBLE)) { 241 | log_err("Attempt to apply operator \"-\" on invalid types"); 242 | exit(1); 243 | } 244 | if (a->type == T_DOUBLE || b->type == T_DOUBLE) { 245 | vm->regs[255] = solid_double(vm, solid_get_double_value(a) - solid_get_double_value(b)); 246 | } else { 247 | vm->regs[255] = solid_int(vm, solid_get_int_value(a) - solid_get_int_value(b)); 248 | } 249 | } 250 | 251 | void solid_mul(solid_vm *vm) 252 | { 253 | solid_object *b = solid_pop_stack(vm); 254 | solid_object *a = solid_pop_stack(vm); 255 | if ((a->type != T_INT && a->type != T_DOUBLE) || (b->type != T_INT && b->type != T_DOUBLE)) { 256 | log_err("Attempt to apply operator \"*\" on invalid types"); 257 | exit(1); 258 | } 259 | if (a->type == T_DOUBLE || b->type == T_DOUBLE) { 260 | vm->regs[255] = solid_double(vm, solid_get_double_value(a) * solid_get_double_value(b)); 261 | } else { 262 | vm->regs[255] = solid_int(vm, solid_get_int_value(a) * solid_get_int_value(b)); 263 | } 264 | } 265 | 266 | void solid_div(solid_vm *vm) 267 | { 268 | solid_object *b = solid_pop_stack(vm); 269 | solid_object *a = solid_pop_stack(vm); 270 | if ((a->type != T_INT && a->type != T_DOUBLE) || (b->type != T_INT && b->type != T_DOUBLE)) { 271 | log_err("Attempt to apply operator \"\\\" on invalid types"); 272 | exit(1); 273 | } 274 | if (a->type == T_DOUBLE || b->type == T_DOUBLE) { 275 | vm->regs[255] = solid_double(vm, solid_get_double_value(a) / solid_get_double_value(b)); 276 | } else { 277 | vm->regs[255] = solid_int(vm, solid_get_int_value(a) / solid_get_int_value(b)); 278 | } 279 | } 280 | 281 | void solid_eq(solid_vm *vm) 282 | { 283 | solid_object *b = solid_pop_stack(vm); 284 | solid_object *a = solid_pop_stack(vm); 285 | if (a->type != T_INT || b->type != T_INT) { 286 | log_err("Attempt to apply operator \"==\" on invalid types"); 287 | exit(1); 288 | } 289 | vm->regs[255] = solid_bool(vm, solid_get_int_value(a) == solid_get_int_value(b)); 290 | } 291 | 292 | void solid_lt(solid_vm *vm) 293 | { 294 | solid_object *b = solid_pop_stack(vm); 295 | solid_object *a = solid_pop_stack(vm); 296 | if ((a->type != T_INT && a->type != T_DOUBLE) || (b->type != T_INT && b->type != T_DOUBLE)) { 297 | log_err("Attempt to apply operator \"<\" on invalid types"); 298 | exit(1); 299 | } 300 | if (a->type == T_DOUBLE || b->type == T_DOUBLE) { 301 | vm->regs[255] = solid_bool(vm, solid_get_double_value(a) < solid_get_double_value(b)); 302 | } else { 303 | vm->regs[255] = solid_bool(vm, solid_get_int_value(a) < solid_get_int_value(b)); 304 | } 305 | } 306 | 307 | void solid_lte(solid_vm *vm) 308 | { 309 | solid_object *b = solid_pop_stack(vm); 310 | solid_object *a = solid_pop_stack(vm); 311 | if ((a->type != T_INT && a->type != T_DOUBLE) || (b->type != T_INT && b->type != T_DOUBLE)) { 312 | log_err("Attempt to apply operator \"<=\" on invalid types"); 313 | exit(1); 314 | } 315 | if (a->type == T_DOUBLE || b->type == T_DOUBLE) { 316 | vm->regs[255] = solid_bool(vm, solid_get_double_value(a) <= solid_get_double_value(b)); 317 | } else { 318 | vm->regs[255] = solid_bool(vm, solid_get_int_value(a) <= solid_get_int_value(b)); 319 | } 320 | } 321 | 322 | void solid_gt(solid_vm *vm) 323 | { 324 | solid_object *b = solid_pop_stack(vm); 325 | solid_object *a = solid_pop_stack(vm); 326 | if ((a->type != T_INT && a->type != T_DOUBLE) || (b->type != T_INT && b->type != T_DOUBLE)) { 327 | log_err("Attempt to apply operator \">\" on invalid types"); 328 | exit(1); 329 | } 330 | if (a->type == T_DOUBLE || b->type == T_DOUBLE) { 331 | vm->regs[255] = solid_bool(vm, solid_get_double_value(a) > solid_get_double_value(b)); 332 | } else { 333 | vm->regs[255] = solid_bool(vm, solid_get_int_value(a) > solid_get_int_value(b)); 334 | } 335 | } 336 | 337 | void solid_gte(solid_vm *vm) 338 | { 339 | solid_object *b = solid_pop_stack(vm); 340 | solid_object *a = solid_pop_stack(vm); 341 | if ((a->type != T_INT && a->type != T_DOUBLE) || (b->type != T_INT && b->type != T_DOUBLE)) { 342 | log_err("Attempt to apply operator \">=\" on invalid types"); 343 | exit(1); 344 | } 345 | if (a->type == T_DOUBLE || b->type == T_DOUBLE) { 346 | vm->regs[255] = solid_bool(vm, solid_get_double_value(a) >= solid_get_double_value(b)); 347 | } else { 348 | vm->regs[255] = solid_bool(vm, solid_get_int_value(a) >= solid_get_int_value(b)); 349 | } 350 | } 351 | 352 | solid_object *solid_not(solid_vm *vm, solid_object *o) 353 | { 354 | if (o->type != T_BOOL && o->type != T_INT) { 355 | log_err("Attempt to negate invalid type"); 356 | exit(1); 357 | } 358 | return solid_bool(vm, !solid_get_bool_value(o)); 359 | } 360 | 361 | solid_bytecode solid_bc(solid_ins i, int a, int b, void *meta) 362 | { 363 | solid_bytecode ret; 364 | ret.ins = i; 365 | ret.a = a; 366 | ret.b = b; 367 | ret.meta = meta; 368 | return ret; 369 | } 370 | 371 | void solid_call_func(solid_vm *vm, solid_object *func) 372 | { 373 | if (func->type == T_FUNC) { 374 | solid_push_namespace(vm); 375 | solid_set_namespace(solid_get_current_namespace(vm), solid_str(vm, "$"), func); 376 | solid_bytecode *inslist = ((solid_function *) func->data.func)->bcode; 377 | solid_bytecode cur; 378 | int pos; 379 | for (pos = 0, cur = inslist[pos]; cur.ins != OP_END; cur = inslist[++pos]) { 380 | //debug("ins: %d, stack height: %d", cur.ins, length_list(vm->stack)); 381 | switch(cur.ins) { 382 | case OP_END: 383 | return; 384 | break; 385 | case OP_NOP: 386 | break; 387 | case OP_PUSH: 388 | solid_push_stack(vm, vm->regs[cur.a]); 389 | break; 390 | case OP_POP: 391 | vm->regs[cur.a] = solid_pop_stack(vm); 392 | break; 393 | case OP_GET: 394 | vm->regs[cur.a] = solid_get_namespace(vm->regs[cur.b], vm->regs[cur.a]); 395 | break; 396 | case OP_SET: 397 | solid_set_namespace(vm->regs[cur.b], solid_str(vm, (char *) cur.meta), vm->regs[cur.a]); 398 | break; 399 | case OP_STOREINT: 400 | vm->regs[cur.a] = solid_int(vm, cur.b); 401 | break; 402 | case OP_STOREDOUBLE: 403 | vm->regs[cur.a] = solid_double(vm, *((double *) cur.meta)); 404 | break; 405 | case OP_STORESTR: 406 | vm->regs[cur.a] = solid_str(vm, (char *) cur.meta); 407 | break; 408 | case OP_STOREBOOL: 409 | vm->regs[cur.a] = solid_bool(vm, cur.b); 410 | break; 411 | case OP_STORELIST: 412 | vm->regs[cur.a] = solid_list(vm, make_list()); 413 | break; 414 | case OP_PUSHLIST: 415 | solid_push_list(vm->regs[cur.a], vm->regs[cur.b]); 416 | break; 417 | case OP_POPLIST: 418 | vm->regs[cur.a] = solid_pop_list(vm->regs[cur.b]); 419 | break; 420 | case OP_MOV: 421 | vm->regs[cur.a] = vm->regs[cur.b]; 422 | break; 423 | case OP_GLOBALNS: 424 | vm->regs[cur.a] = vm->namespace_stack[0]; 425 | break; 426 | case OP_LOCALNS: 427 | vm->regs[cur.a] = solid_get_current_namespace(vm); 428 | break; 429 | case OP_FN: 430 | vm->regs[cur.a] = solid_define_function(vm, (solid_bytecode *) cur.meta); 431 | break; 432 | case OP_NS: 433 | vm->regs[cur.a] = solid_clone_object(vm, solid_get_current_namespace(vm)); 434 | solid_push_predefined_namespace(vm, vm->regs[cur.a]); 435 | break; 436 | case OP_ENDNS: 437 | vm->regs[cur.a] = solid_pop_predefined_namespace(vm); 438 | break; 439 | case OP_JMP: 440 | pos = cur.a - 1; 441 | break; 442 | case OP_JMPIF: 443 | if (solid_get_bool_value(vm->regs[cur.b])) { 444 | pos = cur.a - 1; 445 | } 446 | break; 447 | case OP_CALL: 448 | solid_call_func(vm, vm->regs[cur.a]); 449 | break; 450 | case OP_NOT: 451 | vm->regs[cur.a] = solid_not(vm, vm->regs[cur.a]); 452 | break; 453 | } 454 | } 455 | solid_pop_namespace(vm); 456 | } else if (func->type == T_CFUNC) { 457 | ((void (*)(solid_vm *))(func->data.func))(vm); 458 | } else { 459 | debug("func->type: %d", func->type); 460 | log_err("Object not a function"); 461 | exit(1); 462 | } 463 | } 464 | -------------------------------------------------------------------------------- /core/vm.h: -------------------------------------------------------------------------------- 1 | #ifndef SOLID_VM_H 2 | #define SOLID_VM_H 3 | 4 | typedef struct solid_vm solid_vm; 5 | 6 | #include "utils.h" 7 | #include "object.h" 8 | 9 | typedef enum solid_ins { 10 | OP_END, 11 | OP_NOP, 12 | OP_PUSH, 13 | OP_POP, 14 | OP_GET, 15 | OP_SET, 16 | OP_STOREINT, 17 | OP_STOREDOUBLE, 18 | OP_STORESTR, 19 | OP_STOREBOOL, 20 | OP_STORELIST, 21 | OP_PUSHLIST, 22 | OP_POPLIST, 23 | OP_MOV, 24 | OP_GLOBALNS, 25 | OP_LOCALNS, 26 | OP_FN, 27 | OP_NS, 28 | OP_ENDNS, 29 | OP_JMP, 30 | OP_JMPIF, 31 | OP_CALL, 32 | OP_NOT, 33 | } solid_ins; 34 | 35 | typedef struct solid_bytecode { 36 | solid_ins ins; 37 | int a; 38 | int b; 39 | void *meta; 40 | } solid_bytecode; 41 | 42 | typedef struct solid_function { 43 | solid_bytecode *bcode; 44 | //solid_object *closure; 45 | } solid_function; 46 | 47 | struct solid_vm { 48 | list_node *stack; 49 | list_node *all_objects; 50 | solid_object *regs[256]; 51 | solid_object *namespace_stack[256]; 52 | int namespace_stack_pointer; 53 | }; 54 | 55 | solid_vm *solid_make_vm(); 56 | 57 | void solid_push_stack(solid_vm *vm, solid_object *o); 58 | solid_object *solid_pop_stack(solid_vm *vm); 59 | 60 | void solid_push_list(solid_object *list, solid_object *o); 61 | solid_object *solid_pop_list(solid_object *list); 62 | 63 | void solid_push_namespace(solid_vm *vm); 64 | void solid_pop_namespace(solid_vm *vm); 65 | 66 | void solid_push_predefined_namespace(solid_vm *vm, solid_object *namespace); 67 | solid_object *solid_pop_predefined_namespace(solid_vm *vm); 68 | 69 | solid_object *solid_get_current_namespace(solid_vm *vm); 70 | 71 | void solid_gc_add_object(solid_vm *vm, solid_object *o); 72 | void solid_gc(solid_vm *vm); 73 | 74 | solid_object *solid_define_function(solid_vm *vm, solid_bytecode *inslist); 75 | solid_object *solid_define_c_function(solid_vm *vm, void (*function)(solid_vm *vm)); 76 | 77 | void solid_nth_list(solid_vm *vm); 78 | void solid_print(solid_vm *vm); 79 | void solid_clone(solid_vm *vm); 80 | void solid_cons(solid_vm *vm); 81 | void solid_add(solid_vm *vm); 82 | void solid_sub(solid_vm *vm); 83 | void solid_mul(solid_vm *vm); 84 | void solid_div(solid_vm *vm); 85 | void solid_eq(solid_vm *vm); 86 | void solid_lt(solid_vm *vm); 87 | void solid_lte(solid_vm *vm); 88 | void solid_gt(solid_vm *vm); 89 | void solid_gte(solid_vm *vm); 90 | 91 | solid_object *solid_not(solid_vm *vm, solid_object *o); 92 | 93 | solid_bytecode solid_bc(solid_ins i, int a, int b, void *meta); 94 | 95 | void solid_call_func(solid_vm *vm, solid_object *func); 96 | 97 | #endif 98 | -------------------------------------------------------------------------------- /etc/solid-mode.el: -------------------------------------------------------------------------------- 1 | ;;; solid-mode.el --- Major mode for writing Solid source code 2 | ;; 3 | ;; Copyright 2015 Eric James Michael Ritz 4 | ;; 5 | ;; Author: Eric James Michael Ritz 6 | ;; URL: https://github.com/ejmr/Solid/blob/master/etc/solid-mode.el 7 | ;; Version: 1.0.0 8 | ;; Package-Requires: ((emacs "24")) 9 | ;; Keywords: languages, programming 10 | ;; 11 | ;; 12 | ;; 13 | ;;; License: 14 | ;; 15 | ;; This file is free software; you can redistribute it and/or modify 16 | ;; it under the terms of the GNU General Public License as published 17 | ;; by the Free Software Foundation; either version 3 of the License, 18 | ;; or (at your option) any later version. 19 | ;; 20 | ;; This file is distributed in the hope that it will be useful, but 21 | ;; WITHOUT ANY WARRANTY; without even the implied warranty of 22 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 23 | ;; General Public License for more details. 24 | ;; 25 | ;; You should have received a copy of the GNU General Public License 26 | ;; along with this file; if not, write to the Free Software 27 | ;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 28 | ;; 02110-1301, USA. 29 | ;; 30 | ;; 31 | ;; 32 | ;;; Commentary: 33 | ;; 34 | ;; This mode adds basic support to Emacs for writing software using 35 | ;; the Solid programming language, available at: 36 | ;; 37 | ;; https://github.com/chameco/Solid 38 | ;; 39 | ;; This package associates Solid Mode with files that have the "*.sol" 40 | ;; filename extension. 41 | ;; 42 | ;; 43 | ;; 44 | ;;; Code: 45 | 46 | (defconst solid-mode-version-number "1.0.0" 47 | "Solid Mode version number. 48 | 49 | This number adheres to Semantic Versioning (`http://semver.org/').") 50 | 51 | ;;;###autoload 52 | (define-generic-mode 'solid-mode 53 | ;; Comment Syntax 54 | '("#") 55 | ;; Keywords 56 | '("if" "while" "fn" "do" "end" "return" "$" "ns") 57 | ;; Operators and Other Syntax 58 | '(("=" . font-lock-operator) 59 | ("!!" . font-lock-operator) 60 | ("~" . font-lock-builtin) 61 | ("[" . font-lock-builtin) 62 | ("]" . font-lock-builtin) 63 | (";" . font-lock-builtin)) 64 | ;; Files 65 | '("\\.sol$") 66 | ;; Other Functions 67 | nil 68 | ;; Docstring 69 | "Major mode for the Solid programming language. 70 | 71 | `https://github.com/chameco/Solid'") 72 | 73 | (provide 'solid-mode) 74 | 75 | ;;; solid-mode.el ends here 76 | -------------------------------------------------------------------------------- /lib/_fn.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | void solid_map(solid_vm *vm) 6 | { 7 | solid_object *list = solid_pop_stack(vm); 8 | if (list->type != T_LIST) { 9 | log_err("Attempt to map over non-list object"); 10 | exit(1); 11 | } 12 | list_node *l = (list_node *) list->data; 13 | solid_object *f = solid_pop_stack(vm); 14 | if (f->type != T_FUNC && f->type != T_CFUNC) { 15 | log_err("Non-function passed to map"); 16 | exit(1); 17 | } 18 | list_node *ret = make_list(); 19 | list_node *ins = ret; 20 | list_node *c; 21 | for (c = l->next; c->next != NULL; c = c->next) { 22 | if (c->data != NULL) { 23 | solid_push_stack(vm, (solid_object *) c->data); 24 | solid_call_func(vm, f); 25 | insert_list(ins, vm->regs[255]); 26 | ins = ins->next; 27 | } 28 | } 29 | vm->regs[255] = solid_list(vm, ret); 30 | } 31 | 32 | void solid_reduce(solid_vm *vm) 33 | { 34 | solid_object *list = solid_pop_stack(vm); 35 | if (list->type != T_LIST) { 36 | log_err("Attempt to reduce non-list object"); 37 | exit(1); 38 | } 39 | list_node *l = (list_node *) list->data; 40 | solid_object *acc = solid_pop_stack(vm); 41 | solid_object *f = solid_pop_stack(vm); 42 | if (f->type != T_FUNC && f->type != T_CFUNC) { 43 | log_err("Non-function passed to reduce"); 44 | log_err("Type is %d", f->type); 45 | exit(1); 46 | } 47 | list_node *c; 48 | for (c = l->next; c->next != NULL; c = c->next) { 49 | if (c->data != NULL) { 50 | solid_push_stack(vm, (solid_object *) c->data); 51 | solid_push_stack(vm, acc); 52 | solid_call_func(vm, f); 53 | acc = vm->regs[255]; 54 | } 55 | } 56 | vm->regs[255] = acc; 57 | } 58 | 59 | void solid_init(solid_vm *vm) 60 | { 61 | solid_set_namespace(solid_get_current_namespace(vm), solid_str(vm, "map"), solid_define_c_function(vm, solid_map)); 62 | solid_set_namespace(solid_get_current_namespace(vm), solid_str(vm, "reduce"), solid_define_c_function(vm, solid_reduce)); 63 | } 64 | -------------------------------------------------------------------------------- /lib/_math.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void solid_sin(solid_vm *vm) 5 | { 6 | double x = solid_get_double_value(solid_pop_stack(vm)); 7 | vm->regs[255] = solid_double(vm, sin(x)); 8 | } 9 | 10 | void solid_cos(solid_vm *vm) 11 | { 12 | double x = solid_get_double_value(solid_pop_stack(vm)); 13 | vm->regs[255] = solid_double(vm, cos(x)); 14 | } 15 | 16 | void solid_tan(solid_vm *vm) 17 | { 18 | double x = solid_get_double_value(solid_pop_stack(vm)); 19 | vm->regs[255] = solid_double(vm, tan(x)); 20 | } 21 | 22 | void solid_sqrt(solid_vm *vm) 23 | { 24 | double x = solid_get_double_value(solid_pop_stack(vm)); 25 | vm->regs[255] = solid_double(vm, sqrt(x)); 26 | } 27 | 28 | void solid_init(solid_vm *vm) 29 | { 30 | solid_set_namespace(solid_get_current_namespace(vm), solid_str(vm, "sin"), solid_define_c_function(vm, solid_sin)); 31 | solid_set_namespace(solid_get_current_namespace(vm), solid_str(vm, "cos"), solid_define_c_function(vm, solid_cos)); 32 | solid_set_namespace(solid_get_current_namespace(vm), solid_str(vm, "tan"), solid_define_c_function(vm, solid_tan)); 33 | solid_set_namespace(solid_get_current_namespace(vm), solid_str(vm, "sqrt"), solid_define_c_function(vm, solid_sqrt)); 34 | } 35 | -------------------------------------------------------------------------------- /lib/fn.sol: -------------------------------------------------------------------------------- 1 | Fn = ns { 2 | import("lib/_fn.so"); 3 | }; 4 | -------------------------------------------------------------------------------- /lib/math.sol: -------------------------------------------------------------------------------- 1 | Math = ns { 2 | import("lib/_math.so"); 3 | factorial = fn x { 4 | if x == 1 return 1; 5 | return x * this(x - 1); 6 | }; 7 | }; 8 | -------------------------------------------------------------------------------- /test.sol: -------------------------------------------------------------------------------- 1 | fib_recursive = fn n { 2 | if <(n, 2) return n; 3 | return $(n - 1) + $(n - 2); 4 | }; 5 | 6 | print(fib_recursive(20)); 7 | --------------------------------------------------------------------------------