├── .gitignore ├── .gitlab-ci.yml ├── .gitmodules ├── Doxyfile ├── LICENSE ├── Makefile ├── README.md ├── RECIPES ├── SUMMARY.md ├── ast.h ├── astprint.c ├── build.sh ├── buildgrammar.sh ├── builtins.c ├── cdata.c ├── cdata.h ├── conf.py ├── data └── test_out.txt ├── doc.sh ├── doc ├── index.rst ├── lang │ ├── abs_basics.rst │ ├── index.rst │ └── simple_exprs.rst └── src │ ├── api_conventions.rst │ ├── ast_h.rst │ ├── index.rst │ └── sol_h.rst ├── dsdebug.gdb ├── foo ├── gc.c ├── gcstat.py ├── install_sol.md ├── lex.yy.c ├── object.c ├── old-sol-gdb.py ├── parser.output ├── parser.tab.c ├── parser.tab.h ├── parser.y ├── programs ├── a.sol ├── b.sol ├── chip8.sol ├── dump.sol ├── fizzbuzz.sol ├── http.sol ├── interp.sol ├── monty.sol ├── package.sol ├── server.sol ├── solid.sol ├── solid_run.sol ├── subtest.sol ├── test.sol └── test_monty.sol ├── runtime.c ├── ser.c ├── sol.h ├── sol_help.txt ├── solrun.c ├── state.c ├── tests ├── _fails.sol ├── _lib.sol ├── always_passes.sol ├── basic_control.sol ├── basic_equality.sol ├── basic_len.sol ├── basic_logic.sol ├── basic_vars.sol ├── bt_range.sol ├── crasher_apply_noargs.sol ├── crasher_embedded_nul.sol ├── crasher_init_comma.sol ├── crasher_multiple_loops.sol ├── crasher_range_noargs.sol ├── crasher_tco.sol ├── lang_anno.sol ├── lang_bparen.sol ├── lang_elseif.sol ├── lang_forex.sol ├── lang_ifex.sol ├── lang_macro.sol ├── lang_method.sol ├── lang_scoping.sol └── lang_variadic.sol ├── tokenizer.lex └── util.c /.gitignore: -------------------------------------------------------------------------------- 1 | /sol* 2 | !sol_help.txt 3 | *.o 4 | *.a 5 | stdout 6 | .submodule_stamp 7 | *.orig 8 | *.sw? 9 | gclog.txt 10 | gcstat.txt 11 | iss* 12 | /*.sol 13 | *.slc 14 | _build 15 | doxyxml 16 | valgrind.log 17 | *_INFO 18 | prof 19 | /afl* 20 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | stages: 2 | - build 3 | - test 4 | 5 | before_script: 6 | - git submodule init 7 | - git submodule update 8 | 9 | build: 10 | stage: build 11 | script: 12 | - make 13 | 14 | run: 15 | stage: test 16 | script: 17 | - make 18 | - echo "exit()" | ./sol r programs/interp.sol 19 | 20 | test_boot: 21 | stage: test 22 | script: 23 | - make 24 | - ./sol r programs/test.sol 25 | 26 | all_tests: 27 | stage: test 28 | script: 29 | - make 30 | - make test 31 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "dsl"] 2 | path = dsl 3 | url = https://github.com/Grissess/dsl.git 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Boost Software License - Version 1.0 - August 17th, 2003 2 | 3 | Permission is hereby granted, free of charge, to any person or organization 4 | obtaining a copy of the software and accompanying documentation covered by 5 | this license (the "Software") to use, reproduce, display, distribute, 6 | execute, and transmit the Software, and to prepare derivative works of the 7 | Software, and to permit third-parties to whom the Software is furnished to 8 | do so, all subject to the following: 9 | 10 | The copyright notices in the Software and this entire statement, including 11 | the above license grant, this restriction and the following disclaimer, 12 | must be included in all copies of the Software, in whole or in part, and 13 | all derivative works of the Software, unless such copies or derivative 14 | works are solely in the form of machine-executable object code generated by 15 | a source language processor. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 20 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 21 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | 25 | The following exception is granted: these shall not be considered "derivative 26 | works": 27 | - source programs written in the Sol language; 28 | - compiled programs encoded in the Sol bytecode, including those generated by 29 | this program; 30 | - the output generated as a direct result of executing a Sol program. 31 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | _CFLAGS= -g $(BUILD_DEFINES) $(CFLAGS) 2 | _LDFLAGS= -lfl -lm -ldl -lreadline $(LDFLAGS) 3 | OBJ= lex.yy.o parser.tab.o dsl/seq.o dsl/list.o dsl/array.o dsl/generic.o astprint.o runtime.o gc.o object.o state.o builtins.o solrun.o ser.o sol_help.o 4 | 5 | ifndef CC 6 | CC:= gcc 7 | endif 8 | 9 | ifndef OBJCOPY 10 | OBJCOPY:= objcopy 11 | endif 12 | 13 | ifndef OBJDUMP 14 | OBJDUMP:= objdump 15 | endif 16 | 17 | ifndef DESTDIR 18 | DESTDIR:= /usr/local/ 19 | endif 20 | 21 | ifndef STDOUT_FILENAME 22 | STDOUT_FILENAME:=/dev/fd/1 23 | endif 24 | 25 | ifneq (,$(findstring -DNO_READLINE,$(_CFLAGS))) 26 | _LDFLAGS := $(filter-out -lreadline,$(_LDFLAGS)) 27 | endif 28 | 29 | ifneq (,$(findstring -DNO_HELP,$(_CFLAGS))) 30 | OBJ := $(filter-out sol_help.o,$(OBJ)) 31 | endif 32 | 33 | include VERSION_INFO 34 | include ARCH_INFO 35 | 36 | BUILD_DEFINES:= -DSOL_BUILD_HOST="\"$(shell uname -n)\"" -DSOL_BUILD_KERNEL="\"$(shell uname -s)\"" -DSOL_BUILD_ARCH="\"$(shell uname -m)\"" -DSOL_BUILD_REV="\"$(shell git rev-parse --short HEAD)$(shell git diff-index --quiet HEAD || echo '-dirty')\"" 37 | 38 | SOL_VER:=$(MAJOR).$(MINOR)$(RELEASE)$(PATCH)$(SUFFIX) 39 | LINKED_VERS:=sol sol$(MAJOR) sol$(MAJOR).$(MINOR) 40 | 41 | .PHONY: install install_bin install_bindir install_lib install_libdir uninstall uninstall_bin uninstall_lib all test clean docs 42 | 43 | all: dsl libsol.a $(LINKED_VERS) 44 | 45 | install: install_bindir install_libdir install_bin install_lib 46 | 47 | install_bin: sol$(SOL_VER) $(LINKED_VERS) 48 | install $? $(DESTDIR)/bin/ 49 | 50 | install_lib: programs tests 51 | cp -r $? $(DESTDIR)/lib/sol$(SOL_VER)/ 52 | 53 | install_bindir: 54 | install -d $(DESTDIR)/bin/ 55 | 56 | install_libdir: 57 | install -d $(DESTDIR)/lib/sol$(SOL_VER)/ 58 | 59 | uninstall: uninstall_bin uninstall_lib 60 | 61 | uninstall_bin: 62 | rm $(DESTDIR)/bin/sol$(SOL_VER) || true 63 | for fname in $(LINKED_VERS); do rm $(DESTDIR)/bin/$$fname || true; done 64 | 65 | uninstall_lib: 66 | rm -r $(DESTDIR)/lib/sol$(SOL_VER)/{programs,tests} 67 | 68 | $(LINKED_VERS): sol$(SOL_VER) 69 | rm $@; ln -s $? $@ 70 | 71 | sol$(SOL_VER): $(OBJ) 72 | $(CC) $(_CFLAGS) $(_LDFLAGS) $^ -o $@ 73 | 74 | libsol.a: $(OBJ) 75 | $(AR) rcs $@ $^ 76 | 77 | test: all $(sort $(patsubst tests/%.sol,test_%,$(filter-out tests/_%,$(wildcard tests/*.sol)))) $(sort $(patsubst tests/%.sol,testcomp_%,$(filter-out tests/_%,$(wildcard tests/*.sol)))) 78 | 79 | 80 | test_%: tests/%.sol 81 | ./sol r $? 82 | 83 | testcomp_%: tests/%.sol 84 | ./sol rc $? $(STDOUT_FILENAME) | ./sol C 85 | 86 | profile: all prof profile-boot $(sort $(patsubst tests/%.sol,profile_%,$(wildcard tests/*.sol))) profilecomp-boot $(sort $(patsubst tests/%.sol,profilecomp_%,$(wildcard tests/*.sol))) 87 | 88 | prof: 89 | mkdir prof 90 | 91 | profile-boot: programs/test.sol 92 | LLVM_PROFILE_FILE=prof/boot.prof ./sol r $? 93 | 94 | profile_%: tests/%.sol 95 | LLVM_PROFILE_FILE=prof/sol.$(basename $(notdir $?)).prof ./sol r $? 96 | 97 | profilecomp-boot: programs/test.sol 98 | LLVM_PROFILE_FILE=prof/bootcomp.comp.prof ./sol rc $? $(STDOUT_FILENAME) | LLVM_PROFILE_FILE=prof/bootcomp.run.prof ./sol C 99 | 100 | profilecomp_%: tests/%.sol 101 | LLVM_PROFILE_FILE=prof/solcomp.$(basename $(notdir $?)).comp.prof ./sol rc $? $(STDOUT_FILENAME) | LLVM_PROFILE_FILE=prof/solcomp.$(basename $(notdir $?)).run.prof ./sol C 102 | 103 | prof/sol.html: profile 104 | llvm-profdata merge -sparse prof/*.prof -o prof/sol.prof.merged 105 | llvm-cov show -instr-profile prof/sol.prof.merged ./sol -format=html > $@ 106 | 107 | all-profile: prof/sol.html 108 | 109 | dsl: 110 | git submodule init && git submodule sync && git submodule update 111 | 112 | VERSION_INFO: sol.h 113 | perl -n -e '/#define SOL_VERSION "([[:digit:]]+)\.([[:digit:]]+)(.)([[:digit:]]+)"/ && print "MAJOR:=$$1\nMINOR:=$$2\nRELEASE:=$$3\nPATCH:=$$4\n"' $? > $@ 114 | 115 | ARCH_INFO: gc.o 116 | $(OBJDUMP) -f $? | perl -n -e '/file format ([^-]+-(.+))$$/ && print "HOST_ARCH:=$$2\nHOST_ELF:=$$1\n"' > $@ 117 | 118 | %.o: %.c 119 | $(CC) -c -o $@ $? $(_CFLAGS) 120 | 121 | %.o: %.txt | ARCH_INFO 122 | $(OBJCOPY) -B i386 -I binary -O $(HOST_ELF) $? $@ 123 | 124 | %.tab.c %.tab.h: %.y 125 | bison -rall -fall -d $? 126 | 127 | lex.yy.c: tokenizer.lex parser.tab.h 128 | flex $< 129 | 130 | clean: 131 | rm -f *.o dsl/*.o sol 132 | 133 | docs: Doxyfile 134 | doxygen Doxyfile 135 | sphinx-build -b html . ./_build 136 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | sol 2 | === 3 | 4 | - **Stable Repo**: https://github.com/sol-lang/sol 5 | - **Dev Repo**: https://gitlab.cosi.clarkson.edu/grissess/sol-lang 6 | 7 | Sol a scripting language (whose name is subject to change) that aims to take the best of Lua and Python, and look a little bit like Javascript in the process :) . Sol's most outstanding features include: 8 | 9 | * A rather lightweight, [mostly] re-entrant, multi-user runtime that is in the process of being optimized and made cross-platform. 10 | * A Flex/Bison compiler--yes, it is a compiled language, not an interpreted one, but the runtime has access to the compiler--with the source files distributed (you'll need Flex and Bison to rebuild the grammar, but you don't need it to build the rest of the language). 11 | * A dead simple API that's a bit reminiscent of Python's, but without all the calisthenics. 12 | * Support for multiple paradigms, including object orientation. 13 | * Near-total type agnosticism in the runtime: objects are usually tested for features, not types. 14 | * Some neat language features, like C-style scoping and closures. 15 | 16 | Here's a taste of what the language itself looks like: 17 | 18 | ```lua 19 | -- This is a comment, and there's an assignment below! 20 | a = 1 21 | while a < 10 do 22 | print("a is:", a) 23 | a += 1 24 | end 25 | 26 | -- "Pythonic" for loops; any expression can be used, so long as it 27 | -- returns a function that will be called until it returns None 28 | for i in range(10) do print(i) end -- Note Lua-ish keyword delimiting 29 | 30 | -- "func" seems like a good compromise between "def" and "function" :D 31 | -- (This is a currying add function) 32 | func outer(a) 33 | return func inner(b, a=a) 34 | return a+b 35 | end 36 | end 37 | 38 | -- Alternatively... 39 | outer = lambda(a) lambda(b, a=a) a + b end end 40 | 41 | -- (...and this is an iterator) 42 | func count(j) 43 | inner = func (i = 0, max = j) -- Function definitions are expressions, like in Lua 44 | if i >= max then 45 | return -- If no value is given, None is implied 46 | else 47 | i += 1 48 | return i - 1 49 | end 50 | end 51 | return inner 52 | end 53 | 54 | -- Python-style list definitions (and a separate list type), and Lua-style 55 | -- method calls--an eclectic mix. 56 | print([1, 2, 3, 4, 5]:map(func (i) return i*3 end)) 57 | 58 | -- Lua-like map definitions 59 | print({a=1, b=2, ["c"]=3, [4]=5, ["with more spaces"]={health=100, speed=2}}) 60 | -- ...and metamethods 61 | object = {__index = func(obj, idx) print("Index", obj, "with", idx); return idx end, 62 | __setindex = func(obj, idx, val) print("Set index", idx, "of", obj, "to", val) end, 63 | __call = func(obj, arg1, arg2) print("Called", obj, "with args", arg1, arg2); return arg1+arg2 end} 64 | -- No metatables, but __index can be assigned to another map, which has nearly the same effect 65 | 66 | -- Lua-ish error handling 67 | func bad(x) 68 | x.i=5 69 | return "thing" 70 | end 71 | 72 | res = try(bad, {}) -- Returns 1 (or true), "thing" 73 | res = try(bad, None) -- Reurns 0 (or false), "Undefined method" -- you can't index None 74 | res = try(bad) -- Also fails; unbound arguments are assigned to None by default 75 | res = try(error, 1234) -- Fails, returning [0, 1234] -- errors can be any object 76 | 77 | -- Full suppport for self-modifying code, and invoking the compiler 78 | func a() return 0 end 79 | func b() return 2 end 80 | 81 | temp = a.stmt 82 | a.stmt = b.stmt 83 | b.stmt = temp 84 | -- a now returns 2, and b now returns 0 85 | 86 | code = parse('print("Hi!"); return 4') 87 | a.stmt = code 88 | b.stmt = code 89 | -- a and b now return 4 (and print "Hi!") 90 | 91 | parse('print("Good day!")')() -- Does the thing; shortcut is "exec" 92 | q = parse('8 + 13 * 2').stmtlist[0].expr() -- Returns the value (should be...34?); shortcut "eval" 93 | -- Runs the thing in the environment; the passed map can be modified by the code 94 | -- (as if the keys were local variables) 95 | z = parse('8 + a - b').stmtlist[0].expr({a=5, b=7}) 96 | ``` 97 | 98 | That's a really brief taste; you can look at the `test.sol` file for a larger collection of Sol code, which also happens to be the test suite. If you'd like to run it on your machine, read on. 99 | 100 | Buiding/Installation 101 | -------------------- 102 | 103 | First off, *you should clone this repository with --recursive* if you want to get the submodules in one go. If you've already cloned it, don't worry; just run this: 104 | 105 | ``` 106 | git submodule init 107 | git submodule update 108 | ``` 109 | 110 | This should pull in the requisite build dependencies. (For more on this, see git's documentation on [Cloning a Project with Submodules](http://git-scm.com/book/en/v2/Git-Tools-Submodules#Cloning-a-Project-with-Submodules).) 111 | 112 | A typical `make` should suffice to build the project. There is no `make install` yet, as there's no agreed-upon location for its support files. The executable runtime `sol` should be in the working directory (it is explicitly ignored, and won't be committed to the repo). 113 | 114 | Running 115 | ------- 116 | 117 | The entire `main` function is inside the tiny `solrun.c` file--you can appreciate how simple the API is by the meager size of this file :) . This file is painfully simple; it expects the program as input on stdin, compiles it when it reaches the end of file, and (assuming there are no syntax errors) runs the program afterward. You can easily see how this can be embedded in an existing application, which leads me to the next section... 118 | 119 | Extending 120 | --------- 121 | 122 | If you're looking to embed Sol in an application (which is more-or-less what it was designed for), you'll want to read both `sol.h` and `solrun.c`; most of the API is fairly intuitive. The whole process of setting up an extended Sol environment (inside an application, for example), can be roughly described by this process: 123 | 124 | 1. **Initialize the state.** Allocate a `sol_state_t` somewhere (the stack is fine) and pass a pointer to `sol_state_init`. This large function (in `state.c`) initializes the default runtime and all of its amenities. 125 | 2. **Define custom types.** Sol supports a special object type called `SOL_CDATA` whose `sol_ops_t` structure--representation which operations the object supports--is user defined. You can use this to implement custom types; the most straightforward methods are probably `index` and `setindex`, as well as `call`; see `builtins.c` for some implementations. You'll note that almost all of the members are `sol_cfunc_t`; I'll get to this in the next step. 126 | 3. **Define functions.** Functions that can be called from the runtime are mostly `sol_cfunc_t`, which take a `sol_state_t` pointer to the state (with all the runtime-global information) and a `sol_object_t` pointer to a `SOL_LIST` containing the arguments. The arguments are well-defined for all operations except call (again, see the builtins), and all functions implemented in the `sol_ops_t` structure can expect to have an object of the appropriate type as the first argument (item in the list). Functions can also be exposed directly to the runtime (as callable functions) by creating a `SOL_CFUNCTION` object (using `sol_new_cfunc`), in which case the argument list is entirely dictated by the Sol program. All functions are expected to return a value, even if the value is unused by the runtime (e.g., the `setindex` call). If no particular value is appropriate, return `sol_incref(state->None)`. 127 | 4. **Bind names.** You can assign names in the global scope by using `sol_state_assign` and `sol_state_assign_name`. (You can also assign them in the local scope, which starts out the same as the global scope, by using the `_l` variants of these functions.) If you'd prefer not to pollute the global namespace with your functions (a good choice), you can use an intermediate map to hold your functions and assign it, or register it as a module using `sol_register_module` (and its `_name` variant). The only difference in this approach is that modules are always available at *above* the global level and cannot be overwritten by Sol programs (they can be shadowed by locals, though). 128 | 5. **Compile and execute programs.** Compiling Sol programs is state-agnostic, and is accessible via the functions in `ast.h` (notably, `sol_compile` and `sol_compile_file`). These return `stmt_node` pointers which contain the entirety of the Sol program (a type also defined in `ast.h`). You can pass this to `sol_exec` (with the runtime state) to run the program. You can also use `sol_comp_as_expr` to get an `expr_node` pointer (or `NULL`, if this cannot be done), and evaluate the expression with `sol_eval` to get a `sol_object_t` value back. 129 | 6. **Clean up.** Call `sol_state_cleanup` with the state when you're done for the day. 130 | 131 | Although step 1 should definitely precede steps 5 and 6, steps 2-4 are optional (though helpful), and this process can be repeated with independent states as many times as needed, and as many programs as needed can be executed arbitrarily many times in step 5 before cleaning up. It should be noted that Sol code can also do arbitrary code execution of your functions and yet more Sol code (including compilation), so you should treat anything Sol can modify as volatile at the execution points, and check the state for errors on return (the `sol_has_error` macro and `sol_clear_error` function will be your friends)--a state with errors will refuse to run any code, for potentially mysterious reasons. 132 | 133 | Todo 134 | ---- 135 | 136 | Most of what needs to be done is addressing these issues: 137 | 138 | **Sol leaks memory.** As confirmed with Valgrind (running memcheck), a normal Sol run loses some objects. A great deal of work has been invested in finding and fixing these bugs, but they remain elusive. If you have any insight into something which causes Sol to leak, please file an issue! 139 | 140 | **Sol is slow.** Sol pressures the heap pretty heavily by creating a new object ("returning a new reference") for most operations. At least one bottleneck was addressed with the "icache" (integer cache), which speeds up comparisons significantly, but Sol nonetheless rapidly creates, destroys, and copies strings in very critical execution paths. The map implementation is particularly egregious, and is currently an associative array. 141 | 142 | **The API is unstable.** There will definitely be some changes to the API, mostly to *help* with integration. At present, some operations on behalf of Sol embedders are a little messy and require intrinsic knowledge of the language specifics. The refcounting scheme, as mentioned previously, requires about four lines of code per function that should be a one-liner, for example. 143 | 144 | **C-strings are deprecated.** At some point, Sol will move over to Pascal-ish strings (length/pointer to buffer). This will allow embedded NUL characters. 145 | 146 | **The parser is broken.** Bison (Yacc) reports "reduce/reduce" conflicts (a lot of them, actually). This is not a good thing, though things appear to work somehow. Also, this code is responsible for the pointer clone problem which needs to be addressed. Finally, the tokenizer does not properly interpret escapes (doing it the "right" way somehow breaks the parser), making it difficult to embed things like double quotes. 147 | 148 | After those (but more practically, with them), the following will be coming: 149 | 150 | **Proper foreign-function support.** I'm adding a few new object types to allow calling into libraries (shared objects/dynamic libs). These will need Windows implementations that will come later. Presently, the C-style memory management works, but requires the Sol executable to be compiled with the appropriate compiler to generate the "sizeof" entries properly. 151 | 152 | **Type registration.** Currently the `sol_objtype_t` enumeration contains all the types that Sol defines intrinsically, though most of the code only cares about which operations are supported. Embedders cannot specify additional members of this enumeration, borrowing the `SOL_CDATA` type instead. This may be fixed later. 153 | 154 | **Code consolidation and refactoring.** There are quite a few instances of repetitive code that need to be addressed, and can be refactored out into functions in their own right. This should also improve usability. 155 | 156 | Contributing 157 | ------------ 158 | 159 | By all means, use Sol in whatever project you care to use it in, however you care to link it in, if you think it will be useful in the slightest! If you find bugs, or have features you want to see, submit issues or (better yet) pull requests, and I'll get to them when I can. Finally, feel free to send me messages (however you care, though my email at grahamnorthup at yahoo.com will work :P). Happy programming! 160 | -------------------------------------------------------------------------------- /RECIPES: -------------------------------------------------------------------------------- 1 | WASM/asm.js: 2 | make CC=emcc CFLAGS="-DNO_READLINE -DNO_HELP" 3 | 4 | clang for profiling: 5 | make CC=clang CFLAGS="-fprofile-instr-generate -fcoverage-mapping" 6 | 7 | use Boehm GC where available: 8 | make LDFLAGS=-lgc 9 | 10 | AFL fuzzing (use afl-fuzz on the resulting instrumented binary): 11 | make CC=afl-gcc 12 | -------------------------------------------------------------------------------- /SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | * [Install Sol](install_sol.md) 4 | 5 | -------------------------------------------------------------------------------- /ast.h: -------------------------------------------------------------------------------- 1 | #ifndef AST_H 2 | #define AST_H 3 | 4 | #include "sol.h" 5 | 6 | #define FUNC_IS_MACRO 1 7 | 8 | #include 9 | 10 | /** Locator structure. 11 | * 12 | * Contains the location of a symbol; available on all `stmt_node`s and `expr_node`s as `loc`. 13 | */ 14 | typedef struct { 15 | size_t line; 16 | size_t col; 17 | } loc_t; 18 | 19 | struct tag_expr_node; 20 | typedef struct tag_expr_node expr_node; 21 | 22 | struct tag_stmt_node; 23 | typedef struct tag_stmt_node stmt_node; 24 | 25 | /** Literal type 26 | * 27 | * Defines the types of literals that may appear in a source program. 28 | */ 29 | typedef enum {LIT_INT=1024, LIT_FLOAT, LIT_STRING, LIT_BUFFER, LIT_NONE} lit_t; 30 | /** Literal node 31 | * 32 | * Represents a literal in a source program. 33 | */ 34 | typedef struct { 35 | lit_t type; ///< The type of literal. 36 | union { 37 | long ival; ///< Integer value for `LIT_INT`. 38 | double fval; ///< Floating-point value for `LIT_FLOAT`. 39 | char *str; ///< String value for `LIT_STRING`. 40 | unsigned long *buf; ///< Buffer value for `LIT_BUFFER`; points to. (char *)(buf + 1) points to the first byte in the buffer. There are *buf bytes starting there. See also LENGTH_OF and BYTES_OF. 41 | }; 42 | } lit_node; 43 | 44 | /** Returns the length (as an unsigned long) of the buffer in bytes, not including the length itself. */ 45 | #define LENGTH_OF(buf) (*((unsigned long *) (buf))) 46 | /** Returns a (char *) pointing to the first byte in the buffer. */ 47 | #define BYTES_OF(buf) ((char *) (((unsigned long *) (buf)) + 1)) 48 | 49 | /** Binary operation type 50 | * 51 | * Defines the types of binary operators that may occur in a source program. 52 | */ 53 | typedef enum {OP_ADD=512, OP_SUB, OP_MUL, OP_DIV, OP_MOD, OP_POW, OP_BAND, OP_BOR, OP_BXOR, OP_LAND, OP_LOR, OP_EQUAL, OP_NEQUAL, OP_LESS, OP_GREATER, OP_LESSEQ, OP_GREATEREQ, OP_LSHIFT, OP_RSHIFT, OP_TBANG} binop_t; 54 | /** Binary operation node 55 | * 56 | * Represents a binary operator in a source program. 57 | */ 58 | typedef struct { 59 | binop_t type; ///< The type of binary operation 60 | expr_node *left; ///< The left hand side. 61 | expr_node *right; ///< The right hand side. 62 | } binop_node; 63 | 64 | /** Unary operation type 65 | * 66 | * Defines the types of unary operators that may occur in a source program. 67 | */ 68 | typedef enum {OP_NEG=768, OP_BNOT, OP_LNOT, OP_LEN} unop_t; 69 | /** Unary opreation node 70 | * 71 | * Represents a unary operator in a source program. 72 | */ 73 | typedef struct { 74 | unop_t type; ///< The type of unary operation. 75 | expr_node *expr; ///< The value to which it is applied. 76 | } unop_node; 77 | 78 | /** Index node 79 | * 80 | * Represents an index operation in a source program. 81 | */ 82 | typedef struct { 83 | expr_node *expr; ///< Expression to index. 84 | expr_node *index; ///< Expression to index by. 85 | } index_node; 86 | 87 | /** Setindex node 88 | * 89 | * Represents a setindex operation in a source program. 90 | */ 91 | typedef struct { 92 | expr_node *expr; ///< Expression to set the index of. 93 | expr_node *index; ///< Expression to index by. 94 | expr_node *value; ///< Value to set said index to. 95 | } setindex_node; 96 | 97 | typedef struct { 98 | char *ident; 99 | expr_node *value; 100 | } assign_node; 101 | 102 | typedef struct { 103 | char *ident; 104 | } ref_node; 105 | 106 | typedef struct tag_exprlist_node { 107 | expr_node *expr; 108 | struct tag_exprlist_node *next; 109 | } exprlist_node; 110 | 111 | typedef struct { 112 | expr_node *key; 113 | expr_node *value; 114 | } associtem_node; 115 | 116 | typedef struct tag_assoclist_node { 117 | associtem_node *item; 118 | struct tag_assoclist_node *next; 119 | } assoclist_node; 120 | 121 | typedef struct { 122 | exprlist_node *list; 123 | } listgen_node; 124 | 125 | typedef struct { 126 | assoclist_node *map; 127 | } mapgen_node; 128 | 129 | typedef struct { 130 | expr_node *expr; 131 | exprlist_node *args; 132 | char *method; 133 | } call_node; 134 | 135 | typedef struct tag_identlist_node { 136 | char *ident; 137 | struct tag_identlist_node *next; 138 | } identlist_node; 139 | 140 | typedef struct { 141 | identlist_node *args; 142 | exprlist_node *annos; 143 | identlist_node *clkeys; 144 | exprlist_node *clvalues; 145 | char *rest; 146 | } paramlist_node; 147 | 148 | typedef struct { 149 | char *name; 150 | paramlist_node *params; 151 | expr_node *anno; 152 | stmt_node *body; 153 | unsigned short flags; // FUNC_IS_MACRO 154 | } funcdecl_node; 155 | 156 | typedef struct { 157 | expr_node *cond; 158 | stmt_node *iftrue; 159 | stmt_node *iffalse; 160 | } ifelse_node; 161 | 162 | typedef struct { 163 | expr_node *cond; 164 | stmt_node *loop; 165 | } loop_node; 166 | 167 | typedef struct { 168 | char *var; 169 | expr_node *iter; 170 | stmt_node *loop; 171 | } iter_node; 172 | 173 | typedef enum {EX_LIT=256, EX_LISTGEN, EX_MAPGEN, EX_BINOP, EX_UNOP, EX_INDEX, EX_SETINDEX, EX_ASSIGN, EX_REF, EX_CALL, EX_FUNCDECL, EX_IFELSE, EX_LOOP, EX_ITER} expr_t; 174 | typedef struct tag_expr_node { 175 | expr_t type; 176 | loc_t loc; 177 | union { 178 | lit_node *lit; 179 | listgen_node *listgen; 180 | mapgen_node *mapgen; 181 | binop_node *binop; 182 | unop_node *unop; 183 | index_node *index; 184 | setindex_node *setindex; 185 | assign_node *assign; 186 | ref_node *ref; 187 | call_node *call; 188 | funcdecl_node *funcdecl; 189 | ifelse_node *ifelse; 190 | loop_node *loop; 191 | iter_node *iter; 192 | }; 193 | } expr_node; 194 | 195 | typedef struct { 196 | expr_node *ret; 197 | } ret_node; 198 | 199 | typedef struct { 200 | expr_node *val; 201 | } cont_node; 202 | 203 | typedef struct { 204 | expr_node *val; 205 | } break_node; 206 | 207 | typedef struct tag_stmtlist_node { 208 | stmt_node *stmt; 209 | struct tag_stmtlist_node *next; 210 | } stmtlist_node; 211 | 212 | typedef enum {ST_EXPR, ST_LIST, ST_RET, ST_CONT, ST_BREAK} stmt_t; 213 | typedef struct tag_stmt_node { 214 | stmt_t type; 215 | loc_t loc; 216 | union { 217 | expr_node *expr; 218 | stmtlist_node *stmtlist; 219 | ret_node *ret; 220 | cont_node *cont; 221 | break_node *brk; 222 | }; 223 | } stmt_node; 224 | 225 | typedef enum { 226 | BC_NULL, 227 | BC_ST_EXPR, 228 | BC_ST_LIST, 229 | BC_ST_RET, 230 | BC_ST_CONT, 231 | BC_ST_BREAK, 232 | BC_EX_LIT, 233 | BC_EX_LISTGEN, 234 | BC_EX_MAPGEN, 235 | BC_EX_BINOP, 236 | BC_EX_UNOP, 237 | BC_EX_INDEX, 238 | BC_EX_SETINDEX, 239 | BC_EX_ASSIGN, 240 | BC_EX_REF, 241 | BC_EX_CALL, 242 | BC_EX_FUNCDECL, 243 | BC_EX_IFELSE, 244 | BC_EX_LOOP, 245 | BC_EX_ITER, 246 | BC_LIT_INT, 247 | BC_LIT_FLOAT, 248 | BC_LIT_STRING, 249 | BC_LIT_BUFFER, 250 | BC_LIT_NONE, 251 | BC_INT, 252 | BC_FLOAT, 253 | BC_STRING, 254 | BC_BUFFER, 255 | BC_LIST_ST, 256 | BC_LIST_EX, 257 | BC_LIST_AS, 258 | BC_LIST_ID, 259 | BC_LIST_PM, 260 | BC_ENDLIST, 261 | } bytecode; 262 | 263 | #define AS_ST(arg) ((stmt_node *) (arg)) 264 | #define AS_EX(arg) ((expr_node *) (arg)) 265 | #define AS(arg, tp) ((tp *) (arg)) 266 | #define NEW_ST() malloc(sizeof(stmt_node)) 267 | #define NEW_EX() malloc(sizeof(expr_node)) 268 | #define SET_LOC(node, l) do { (node)->loc.line = (l).first_line; (node)->loc.col = (l).first_column; } while(0) 269 | #define NEW(arg) malloc(sizeof(arg)) 270 | #define MAKE_REF_BINOP(nd, tp, name, val) nd = NEW_EX(); \ 271 | nd->type = EX_BINOP; \ 272 | nd->binop = NEW(binop_node); \ 273 | nd->binop->type = tp; \ 274 | nd->binop->left = NEW_EX(); \ 275 | nd->binop->left->type = EX_REF; \ 276 | nd->binop->left->ref = NEW(ref_node); \ 277 | nd->binop->left->ref->ident = strdup(name); \ 278 | nd->binop->right = val 279 | #define MAKE_IDX_BINOP(nd, tp, obj, idx, val) nd = NEW_EX(); \ 280 | nd->type = EX_BINOP; \ 281 | nd->binop = NEW(binop_node); \ 282 | nd->binop->type = tp; \ 283 | nd->binop->left = NEW_EX(); \ 284 | nd->binop->left->type = EX_INDEX; \ 285 | nd->binop->left->index = NEW(index_node); \ 286 | nd->binop->left->index->expr = ex_copy(obj); \ 287 | nd->binop->left->index->index = ex_copy(idx); \ 288 | nd->binop->right = val 289 | #define BOOL_TO_INT(cond) ((cond)?1:0) 290 | #define CALL_METHOD(state, obj, meth, args) ({\ 291 | sol_object_t *res;\ 292 | state->calling_type = obj->ops->tname;\ 293 | state->calling_meth = #meth;\ 294 | res = obj->ops->meth(state, args);\ 295 | state->calling_type = "(none)";\ 296 | state->calling_meth = "(none)";\ 297 | res;\ 298 | }) 299 | 300 | sol_object_t *sol_new_func(sol_state_t *, identlist_node *, stmt_node *, char *, paramlist_node *, expr_node *, unsigned short); 301 | sol_object_t *sol_new_stmtnode(sol_state_t *, stmt_node *); 302 | sol_object_t *sol_new_exprnode(sol_state_t *, expr_node *); 303 | 304 | // runtime.c 305 | 306 | stmt_node *sol_compile(const char *); 307 | stmt_node *sol_compile_buffer(const char *, size_t); 308 | stmt_node *sol_compile_file(FILE *); 309 | void sol_write_html(FILE *); 310 | expr_node *sol_comp_as_expr(stmt_node *); 311 | void sol_compile_free(stmt_node *); 312 | 313 | stmt_node *st_copy(stmt_node *); 314 | expr_node *ex_copy(expr_node *); 315 | stmtlist_node *stl_copy(stmtlist_node *); 316 | exprlist_node *exl_copy(exprlist_node *); 317 | assoclist_node *asl_copy(assoclist_node *); 318 | identlist_node *idl_copy(identlist_node *); 319 | paramlist_node *pl_copy(paramlist_node *); 320 | 321 | void st_free(stmt_node *); 322 | void ex_free(expr_node *); 323 | void stl_free(stmtlist_node *); 324 | void exl_free(exprlist_node *); 325 | void asl_free(assoclist_node *); 326 | void idl_free(identlist_node *); 327 | void pl_free(paramlist_node *); 328 | 329 | void st_print(sol_state_t *, stmt_node *); 330 | void ex_print(sol_state_t *, expr_node *); 331 | void ob_print(sol_object_t *); 332 | 333 | sol_object_t *sol_eval(sol_state_t *, expr_node *); 334 | void sol_exec(sol_state_t *, stmt_node *); 335 | 336 | // ser.c 337 | 338 | void sol_ser_stmt(FILE *, stmt_node *); 339 | void sol_ser_expr(FILE *, expr_node *); 340 | void sol_ser_stl(FILE *, stmtlist_node *); 341 | void sol_ser_exl(FILE *, exprlist_node *); 342 | void sol_ser_asl(FILE *, assoclist_node *); 343 | void sol_ser_idl(FILE *, identlist_node *); 344 | void sol_ser_pl(FILE *, paramlist_node *); 345 | void sol_ser_lit(FILE *, lit_node *); 346 | void sol_ser_int(FILE *, long); 347 | void sol_ser_float(FILE *, double); 348 | void sol_ser_str(FILE *, const char *); 349 | void sol_ser_buf(FILE *, unsigned long *); 350 | 351 | void *sol_deser(FILE *); 352 | void *sol_deser_checked(FILE *, bytecode); 353 | void *sol_deser_stmt(FILE *); 354 | void *sol_deser_expr(FILE *); 355 | void *sol_deser_lit(FILE *); 356 | 357 | #endif 358 | -------------------------------------------------------------------------------- /astprint.c: -------------------------------------------------------------------------------- 1 | #include "ast.h" 2 | #include "parser.tab.h" 3 | 4 | #include 5 | #include 6 | 7 | void prlev(sol_state_t *state, int lev, const char *fmt, ...) { 8 | va_list vl; 9 | int i; 10 | 11 | for(i = 0; i < lev; i++) { 12 | sol_putchar(state, '|'); 13 | sol_putchar(state, ' '); 14 | } 15 | va_start(vl, fmt); 16 | sol_vprintf(state, fmt, vl); 17 | va_end(vl); 18 | sol_putchar(state, '\n'); 19 | } 20 | 21 | void prex( sol_state_t *, expr_node *, int); 22 | 23 | void prst(sol_state_t *state, stmt_node *node, int lev) { 24 | if(!node) { 25 | prlev(state, lev, ""); 26 | return; 27 | } 28 | switch(node->type) { 29 | case ST_EXPR: 30 | prlev(state, lev, "Stmt:"); 31 | prex(state, node->expr, lev + 1); 32 | break; 33 | 34 | case ST_LIST: 35 | prlev(state, lev, "Stmt:"); 36 | stmtlist_node *cur = node->stmtlist; 37 | while(cur && cur->stmt) { 38 | prst(state, cur->stmt, lev + 1); 39 | cur = cur->next; 40 | } 41 | break; 42 | 43 | case ST_RET: 44 | prlev(state, lev, "Stmt:"); 45 | prex(state, node->ret->ret, lev + 1); 46 | break; 47 | 48 | case ST_CONT: 49 | prlev(state, lev, "Stmt:"); 50 | prex(state, node->cont->val, lev + 1); 51 | break; 52 | 53 | case ST_BREAK: 54 | prlev(state, lev, "Stmt:"); 55 | prex(state, node->brk->val, lev + 1); 56 | break; 57 | } 58 | } 59 | 60 | void prex(sol_state_t *state, expr_node *node, int lev) { 61 | assoclist_node *cura; 62 | exprlist_node *cure; 63 | identlist_node *curi; 64 | if(!node) { 65 | prlev(state, lev, ""); 66 | return; 67 | } 68 | switch(node->type) { 69 | case EX_LIT: 70 | prlev(state, lev, "Literal:"); 71 | lev++; 72 | switch(node->lit->type) { 73 | case LIT_INT: 74 | prlev(state, lev, "Int: %ld", node->lit->ival); 75 | break; 76 | 77 | case LIT_FLOAT: 78 | prlev(state, lev, "Float: %f", node->lit->fval); 79 | break; 80 | 81 | case LIT_STRING: 82 | prlev(state, lev, "String: %s", node->lit->str); 83 | break; 84 | 85 | case LIT_BUFFER: 86 | prlev(state, lev, "Buffer of %lu bytes:", LENGTH_OF(node->lit->buf)); 87 | prlev(state, lev + 1, "%s", BYTES_OF(node->lit->buf)); 88 | 89 | case LIT_NONE: 90 | prlev(state, lev, "None"); 91 | break; 92 | } 93 | break; 94 | 95 | case EX_LISTGEN: 96 | prlev(state, lev, "ListGen:"); 97 | cure = node->listgen->list; 98 | while(cure && cure->expr) { 99 | prex(state, cure->expr, lev + 1); 100 | cure = cure->next; 101 | } 102 | break; 103 | 104 | case EX_MAPGEN: 105 | prlev(state, lev, "MapGen:"); 106 | lev++; 107 | cura = node->mapgen->map; 108 | while(cura && cura->item) { 109 | prlev(state, lev, ":"); 110 | prex(state, cura->item->key, lev + 1); 111 | prlev(state, lev, ":"); 112 | prex(state, cura->item->value, lev + 1); 113 | cura = cura->next; 114 | } 115 | break; 116 | 117 | case EX_BINOP: 118 | prlev(state, lev, "BinOp:"); 119 | lev++; 120 | switch(node->binop->type) { 121 | case OP_ADD: 122 | prlev(state, lev, "Op: +"); 123 | break; 124 | 125 | case OP_SUB: 126 | prlev(state, lev, "Op: -"); 127 | break; 128 | 129 | case OP_MUL: 130 | prlev(state, lev, "Op: *"); 131 | break; 132 | 133 | case OP_DIV: 134 | prlev(state, lev, "Op: /"); 135 | break; 136 | 137 | case OP_MOD: 138 | prlev(state, lev, "Op: %"); 139 | break; 140 | 141 | case OP_POW: 142 | prlev(state, lev, "Op: **"); 143 | break; 144 | 145 | case OP_TBANG: 146 | prlev(state, lev, "Op: !!!"); 147 | break; 148 | 149 | case OP_BAND: 150 | prlev(state, lev, "Op: &"); 151 | break; 152 | 153 | case OP_BOR: 154 | prlev(state, lev, "Op: |"); 155 | break; 156 | 157 | case OP_BXOR: 158 | prlev(state, lev, "Op: ^"); 159 | break; 160 | 161 | case OP_LAND: 162 | prlev(state, lev, "Op: &&"); 163 | break; 164 | 165 | case OP_LOR: 166 | prlev(state, lev, "Op: ||"); 167 | break; 168 | 169 | case OP_EQUAL: 170 | prlev(state, lev, "Op: =="); 171 | break; 172 | 173 | case OP_NEQUAL: 174 | prlev(state, lev, "Op: !="); 175 | break; 176 | 177 | case OP_LESS: 178 | prlev(state, lev, "Op: <"); 179 | break; 180 | 181 | case OP_GREATER: 182 | prlev(state, lev, "Op: >"); 183 | break; 184 | 185 | case OP_LESSEQ: 186 | prlev(state, lev, "Op: <="); 187 | break; 188 | 189 | case OP_GREATEREQ: 190 | prlev(state, lev, "Op: >="); 191 | break; 192 | 193 | case OP_LSHIFT: 194 | prlev(state, lev, "Op: <<"); 195 | break; 196 | 197 | case OP_RSHIFT: 198 | prlev(state, lev, "Op: >>"); 199 | break; 200 | } 201 | prlev(state, lev, "Left:"); 202 | prex(state, node->binop->left, lev + 1); 203 | prlev(state, lev, "Right:"); 204 | prex(state, node->binop->right, lev + 1); 205 | break; 206 | 207 | case EX_UNOP: 208 | prlev(state, lev, "UnOp:"); 209 | lev++; 210 | switch(node->unop->type) { 211 | case OP_NEG: 212 | prlev(state, lev, "Op: -"); 213 | break; 214 | 215 | case OP_BNOT: 216 | prlev(state, lev, "Op: ~"); 217 | break; 218 | 219 | case OP_LNOT: 220 | prlev(state, lev, "Op: !"); 221 | break; 222 | 223 | case OP_LEN: 224 | prlev(state, lev, "Op: #"); 225 | break; 226 | } 227 | prlev(state, lev, "Expr:"); 228 | prex(state, node->unop->expr, lev + 1); 229 | break; 230 | 231 | case EX_INDEX: 232 | prlev(state, lev, "Index:"); 233 | lev++; 234 | prlev(state, lev, "Expr:"); 235 | prex(state, node->index->expr, lev + 1); 236 | prlev(state, lev, "Index:"); 237 | prex(state, node->index->index, lev + 1); 238 | break; 239 | 240 | case EX_SETINDEX: 241 | prlev(state, lev, "SetIndex:"); 242 | lev++; 243 | prlev(state, lev, "Expr:"); 244 | prex(state, node->setindex->expr, lev + 1); 245 | prlev(state, lev, "Index:"); 246 | prex(state, node->setindex->index, lev + 1); 247 | prlev(state, lev, "Value:"); 248 | prex(state, node->setindex->value, lev + 1); 249 | break; 250 | 251 | case EX_ASSIGN: 252 | prlev(state, lev, "Assign:"); 253 | lev++; 254 | prlev(state, lev, "Ident: %s", node->assign->ident); 255 | prlev(state, lev, "Value:"); 256 | prex(state, node->assign->value, lev + 1); 257 | break; 258 | 259 | case EX_REF: 260 | prlev(state, lev, "Ref: %s", node->ref->ident); 261 | break; 262 | 263 | case EX_CALL: 264 | prlev(state, lev, "Call:"); 265 | lev++; 266 | prlev(state, lev, "Expr:"); 267 | prex(state, node->call->expr, lev + 1); 268 | prlev(state, lev, "Method: %s", node->call->method); 269 | prlev(state, lev, "Args:"); 270 | cure = node->call->args; 271 | while(cure && cure->expr) { 272 | prex(state, cure->expr, lev + 1); 273 | cure = cure->next; 274 | } 275 | break; 276 | 277 | case EX_FUNCDECL: 278 | prlev(state, lev, "FuncDecl:"); 279 | lev++; 280 | prlev(state, lev, "Name: %s", node->funcdecl->name); 281 | prlev(state, lev, "Params:"); 282 | if(!node->funcdecl->params) { 283 | prlev(state, lev + 1, ""); 284 | } else { 285 | prlev(state, lev + 1, "Args:"); 286 | curi = node->funcdecl->params->args; 287 | while(curi && curi->ident) { 288 | prlev(state, lev + 2, curi->ident); 289 | curi = curi->next; 290 | } 291 | prlev(state, lev + 1, "ClKeys:"); 292 | curi = node->funcdecl->params->clkeys; 293 | while(curi && curi->ident) { 294 | prlev(state, lev + 2, curi->ident); 295 | curi = curi->next; 296 | } 297 | prlev(state, lev + 1, "ClValues:"); 298 | cure = node->funcdecl->params->clvalues; 299 | while(cure && cure->expr) { 300 | prex(state, cure->expr, lev + 2); 301 | cure = cure->next; 302 | } 303 | prlev(state, lev + 1, "Rest: %s", node->funcdecl->params->rest); 304 | } 305 | prlev(state, lev, "Body:"); 306 | prst(state, node->funcdecl->body, lev + 1); 307 | break; 308 | 309 | case EX_IFELSE: 310 | prlev(state, lev, "Expr:"); 311 | lev++; 312 | prlev(state, lev, "Cond:"); 313 | prex(state, node->ifelse->cond, lev + 1); 314 | prlev(state, lev, "IfTrue:"); 315 | prst(state, node->ifelse->iftrue, lev + 1); 316 | prlev(state, lev, "IfFalse:"); 317 | prst(state, node->ifelse->iffalse, lev + 1); 318 | break; 319 | 320 | case EX_LOOP: 321 | prlev(state, lev, "Expr:"); 322 | lev++; 323 | prlev(state, lev, "Cond:"); 324 | prex(state, node->loop->cond, lev + 1); 325 | prlev(state, lev, "Loop:"); 326 | prst(state, node->loop->loop, lev + 1); 327 | break; 328 | 329 | case EX_ITER: 330 | prlev(state, lev, "Expr:"); 331 | lev++; 332 | prlev(state, lev, "Var: %s", node->iter->var); 333 | prlev(state, lev, "Iter:"); 334 | prex(state, node->iter->iter, lev + 1); 335 | prlev(state, lev, "Loop:"); 336 | prst(state, node->iter->loop, lev + 1); 337 | break; 338 | } 339 | } 340 | 341 | void st_print(sol_state_t *state, stmt_node *stmt) { 342 | prst(state, stmt, 0); 343 | } 344 | 345 | void ex_print(sol_state_t *state, expr_node *expr) { 346 | prex(state, expr, 0); 347 | } 348 | 349 | /*int main(int argc, char **argv) { 350 | stmt_node *program = NULL; 351 | 352 | if(argc>1) yydebug = 1; 353 | 354 | if(yyparse(&program)) { 355 | printf("Syntax error (somewhere)\n"); 356 | printf("Partial tree:\n"); 357 | prst(state, program, 0); 358 | return 1; 359 | } 360 | 361 | prst(state, program, 0); 362 | return 0; 363 | }*/ 364 | 365 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | if [ ! -f .submodule_stamp ]; then 3 | git submodule init && git submodule sync && git submodule update 4 | touch .submodule_stamp 5 | fi 6 | 7 | if [ -z "$CFLAGS" ]; then 8 | # Valid flags to add here: 9 | # -DDEBUG_GC : Turn on debug GC (all memory allocates/frees go to a file, use gcstat.py to get statistics) 10 | # -DSOL_ICACHE_MIN : Minimum integer to cache in the state 11 | # -DSOL_ICACHE_MAX : Maximum integer to cache in the state (if MAX < MIN, caching is disabled) 12 | CFLAGS="-g -DDEBUG_GC" 13 | fi 14 | CFLAGS="-lreadline" 15 | 16 | gcc -c $CFLAGS dsl/seq.c 17 | gcc -c $CFLAGS dsl/list.c 18 | gcc -c $CFLAGS dsl/array.c 19 | gcc -c $CFLAGS dsl/generic.c 20 | gcc -c $CFLAGS lex.yy.c 21 | gcc -c $CFLAGS parser.tab.c 22 | gcc -c $CFLAGS astprint.c 23 | gcc -c $CFLAGS runtime.c 24 | gcc -c $CFLAGS gc.c 25 | gcc -c $CFLAGS object.c 26 | gcc -c $CFLAGS state.c 27 | gcc -c $CFLAGS builtins.c 28 | gcc -c $CFLAGS solrun.c 29 | gcc $CFLAGS *.o -o sol -lm -ldl 30 | -------------------------------------------------------------------------------- /buildgrammar.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | bison -rall -fall -d parser.y 3 | flex tokenizer.lex 4 | -------------------------------------------------------------------------------- /cdata.c: -------------------------------------------------------------------------------- 1 | #include "cdata.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #define AS(arg, tp) ((tp *) (arg)) 8 | #define AT(loc, tp, off) (*((tp *) ((char *) loc) + off)) 9 | #define NEW(tp) malloc(sizeof(tp)) 10 | #define ENSURE() if(!sol_cstruct_is_init) sol_cstruct_init() 11 | 12 | int sol_cstruct_is_init = 0; 13 | sol_ops_t sol_cstruct_spec_ops; 14 | sol_ops_t sol_cstruct_ops; 15 | 16 | void sol_cstruct_init(void) { 17 | sol_ops_init(&sol_cstruct_spec_ops); 18 | sol_ops_init(&sol_cstruct_ops); 19 | 20 | sol_cstruct_ops.index = sol_f_cstruct_index; 21 | sol_cstruct_ops.setindex = sol_f_cstruct_setindex; 22 | sol_cstruct_is_init = 1; 23 | } 24 | 25 | sol_object_t *sol_new_cstruct_specs(sol_state_t *state) { 26 | return sol_new_map(state); 27 | } 28 | 29 | sol_object_t *sol_new_cstruct_spec(sol_state_t *state, sol_spec_t type) { 30 | ENSURE(); 31 | sol_object_t *res = sol_new_cdata(state, NEW(sol_memspec_t), &sol_cstruct_spec_ops); 32 | AS(res->cdata, sol_memspec_t)->type = type; 33 | return res; 34 | } 35 | 36 | void sol_cstruct_add_member(sol_state_t *state, sol_object_t *specs, sol_object_t *key, sol_memtype_t memtype, int offset) { 37 | sol_object_t *spec = sol_new_cstruct_spec(state, SOL_CS_MEMBER); 38 | AS(spec->cdata, sol_memspec_t)->memtype = memtype; 39 | AS(spec->cdata, sol_memspec_t)->offset = offset; 40 | sol_map_set(state, specs, key, spec); 41 | sol_obj_free(spec); 42 | } 43 | 44 | void sol_cstruct_add_member_name(sol_state_t *state, sol_object_t *specs, char *name, sol_memtype_t memtype, int offset) { 45 | sol_object_t *spec = sol_new_cstruct_spec(state, SOL_CS_MEMBER); 46 | AS(spec->cdata, sol_memspec_t)->memtype = memtype; 47 | AS(spec->cdata, sol_memspec_t)->offset = offset; 48 | sol_map_set_name(state, specs, name, spec); 49 | sol_obj_free(spec); 50 | } 51 | 52 | void sol_cstruct_add_pointer(sol_state_t *state, sol_object_t *pspecs, sol_object_t *key, sol_object_t *specs, int offset) { 53 | sol_object_t *spec = sol_new_cstruct_spec(state, SOL_CS_MEMBER); 54 | AS(spec->cdata, sol_memspec_t)->memtype = SOL_MT_PTR; 55 | AS(spec->cdata, sol_memspec_t)->offset = offset; 56 | AS(spec->cdata, sol_memspec_t)->specs = pspecs; 57 | sol_map_set(state, specs, key, spec); 58 | sol_obj_free(spec); 59 | } 60 | 61 | void sol_cstruct_add_pointer_name(sol_state_t *state, sol_object_t *pspecs, char *name, sol_object_t *specs, int offset) { 62 | sol_object_t *spec = sol_new_cstruct_spec(state, SOL_CS_MEMBER); 63 | AS(spec->cdata, sol_memspec_t)->memtype = SOL_MT_PTR; 64 | AS(spec->cdata, sol_memspec_t)->offset = offset; 65 | AS(spec->cdata, sol_memspec_t)->specs = pspecs; 66 | sol_map_set_name(state, specs, name, spec); 67 | sol_obj_free(spec); 68 | } 69 | 70 | void sol_cstruct_add_func(sol_state_t *state, sol_object_t *specs, sol_object_t *key, sol_cfunc_t cfunc) { 71 | sol_object_t *spec = sol_new_cstruct_spec(state, SOL_CS_CFUNC); 72 | AS(spec->cdata, sol_memspec_t)->cfunc = cfunc; 73 | sol_map_set(state, specs, key, spec); 74 | sol_obj_free(spec); 75 | } 76 | 77 | void sol_cstruct_add_func_name(sol_state_t *state, sol_object_t *specs, char *name, sol_cfunc_t cfunc) { 78 | sol_object_t *spec = sol_new_cstruct_spec(state, SOL_CS_CFUNC); 79 | AS(spec->cdata, sol_memspec_t)->cfunc = cfunc; 80 | sol_map_set_name(state, specs, name, spec); 81 | sol_obj_free(spec); 82 | } 83 | 84 | sol_object_t *sol_new_cstruct(sol_state_t *state, void *data, sol_object_t *specs) { 85 | sol_object_t *res = sol_new_cdata(state, NEW(sol_cstruct_t), &sol_cstruct_ops); 86 | AS(res->cdata, sol_cstruct_t)->data = data; 87 | AS(res->cdata, sol_cstruct_t)->specs = sol_incref(specs); 88 | return res; 89 | } 90 | 91 | sol_object_t *sol_f_cstruct_index(sol_state_t *state, sol_object_t *args) { 92 | sol_object_t *cstructobj = sol_list_get_index(state, args, 0), *idx = sol_list_get_index(state, args, 1); 93 | sol_cstruct_t *cstruct = cstructobj->cdata; 94 | sol_object_t *specobj = sol_map_get(state, cstruct->specs, idx), *res = NULL; 95 | sol_memspec_t *spec; 96 | char cbuf[2] = {0, 0}; 97 | if(sol_is_none(state, specobj)) { 98 | sol_obj_free(specobj); 99 | sol_obj_free(idx); 100 | sol_obj_free(cstructobj); 101 | return sol_set_error_string(state, "No such CStruct index"); 102 | } 103 | spec = specobj->cdata; 104 | switch(spec->type) { 105 | case SOL_CS_MEMBER: 106 | switch(spec->memtype) { 107 | case SOL_MT_INT: 108 | res = sol_new_int(state, AT(cstruct->data, int, spec->offset)); 109 | break; 110 | 111 | case SOL_MT_INT8: 112 | res = sol_new_int(state, AT(cstruct->data, int8_t, spec->offset)); 113 | break; 114 | 115 | case SOL_MT_INT16: 116 | res = sol_new_int(state, AT(cstruct->data, int16_t, spec->offset)); 117 | break; 118 | 119 | case SOL_MT_INT32: 120 | res = sol_new_int(state, AT(cstruct->data, int32_t, spec->offset)); 121 | break; 122 | 123 | case SOL_MT_INT64: 124 | res = sol_new_int(state, AT(cstruct->data, int64_t, spec->offset)); 125 | break; 126 | 127 | case SOL_MT_UINT: 128 | res = sol_new_int(state, AT(cstruct->data, unsigned int, spec->offset)); 129 | break; 130 | 131 | case SOL_MT_UINT8: 132 | res = sol_new_int(state, AT(cstruct->data, uint8_t, spec->offset)); 133 | break; 134 | 135 | case SOL_MT_UINT16: 136 | res = sol_new_int(state, AT(cstruct->data, uint16_t, spec->offset)); 137 | break; 138 | 139 | case SOL_MT_UINT32: 140 | res = sol_new_int(state, AT(cstruct->data, uint32_t, spec->offset)); 141 | break; 142 | 143 | /*case SOL_MT_UINT64: 144 | res = sol_new_int(state, AT(cstruct->data, uint64_t, spec->offset)); 145 | break;*/ 146 | 147 | case SOL_MT_FLOAT: 148 | res = sol_new_float(state, AT(cstruct->data, float, spec->offset)); 149 | break; 150 | 151 | case SOL_MT_DOUBLE: 152 | res = sol_new_float(state, AT(cstruct->data, double, spec->offset)); 153 | break; 154 | 155 | case SOL_MT_CHAR: 156 | cbuf[0] = AT(cstruct->data, char, spec->offset); 157 | res = sol_new_string(state, cbuf); 158 | break; 159 | 160 | case SOL_MT_CSTR: 161 | res = sol_new_string(state, AT(cstruct->data, char *, spec->offset)); 162 | break; 163 | 164 | case SOL_MT_CFUNC: 165 | res = sol_new_cfunc(state, AT(cstruct->data, sol_cfunc_t, spec->offset), NULL); 166 | break; 167 | 168 | case SOL_MT_PTR: 169 | res = sol_new_cstruct(state, AT(cstruct->data, void *, spec->offset), spec->specs); 170 | break; 171 | } 172 | break; 173 | 174 | case SOL_CS_CFUNC: 175 | res = sol_new_cfunc(state, spec->cfunc, NULL); 176 | break; 177 | } 178 | if(!res) { 179 | res = sol_incref(state->None); 180 | } 181 | return res; 182 | } 183 | 184 | sol_object_t *sol_f_cstruct_setindex(sol_state_t *state, sol_object_t *args) { 185 | sol_object_t *cstructobj = sol_list_get_index(state, args, 0), *idx = sol_list_get_index(state, args, 1), *val = sol_list_get_index(state, args, 2); 186 | sol_cstruct_t *cstruct = cstructobj->cdata; 187 | sol_object_t *specobj = sol_map_get(state, cstruct->specs, idx); 188 | sol_memspec_t *spec; 189 | char cbuf[2] = {0, 0}; 190 | if(sol_is_none(state, specobj)) { 191 | sol_obj_free(specobj); 192 | sol_obj_free(idx); 193 | sol_obj_free(cstructobj); 194 | return sol_set_error_string(state, "No such CStruct index"); 195 | } 196 | spec = specobj->cdata; 197 | switch(spec->type) { 198 | case SOL_CS_MEMBER: 199 | switch(spec->memtype) { 200 | case SOL_MT_INT: 201 | AT(cstruct->data, int, spec->offset) = sol_cast_int(state, val)->ival; 202 | break; 203 | 204 | case SOL_MT_INT8: 205 | AT(cstruct->data, int8_t, spec->offset) = sol_cast_int(state, val)->ival; 206 | break; 207 | 208 | case SOL_MT_INT16: 209 | AT(cstruct->data, int16_t, spec->offset) = sol_cast_int(state, val)->ival; 210 | break; 211 | 212 | case SOL_MT_INT32: 213 | AT(cstruct->data, int32_t, spec->offset) = sol_cast_int(state, val)->ival; 214 | break; 215 | 216 | case SOL_MT_INT64: 217 | AT(cstruct->data, int64_t, spec->offset) = sol_cast_int(state, val)->ival; 218 | break; 219 | 220 | case SOL_MT_UINT: 221 | AT(cstruct->data, unsigned int, spec->offset) = sol_cast_int(state, val)->ival; 222 | break; 223 | 224 | case SOL_MT_UINT8: 225 | AT(cstruct->data, uint8_t, spec->offset) = sol_cast_int(state, val)->ival; 226 | break; 227 | 228 | case SOL_MT_UINT16: 229 | AT(cstruct->data, uint16_t, spec->offset) = sol_cast_int(state, val)->ival; 230 | break; 231 | 232 | case SOL_MT_UINT32: 233 | AT(cstruct->data, uint32_t, spec->offset) = sol_cast_int(state, val)->ival; 234 | break; 235 | 236 | /*case SOL_MT_UINT64: 237 | AT(cstruct->data, uint64_t, spec->offset) = sol_cast_int(state, val)->ival; 238 | break;*/ 239 | 240 | case SOL_MT_FLOAT: 241 | AT(cstruct->data, float, spec->offset) = sol_cast_float(state, val)->fval; 242 | break; 243 | 244 | case SOL_MT_DOUBLE: 245 | AT(cstruct->data, double, spec->offset) = sol_cast_float(state, val)->fval; 246 | break; 247 | 248 | case SOL_MT_CHAR: 249 | AT(cstruct->data, char, spec->offset) = sol_cast_string(state, val)->str[0]; 250 | break; 251 | 252 | case SOL_MT_CSTR: 253 | AT(cstruct->data, char *, spec->offset) = strdup(sol_cast_string(state, val)->str); 254 | break; 255 | 256 | case SOL_MT_CFUNC: 257 | return sol_set_error_string(state, "Can't assign CFunc members"); 258 | break; 259 | 260 | case SOL_MT_PTR: 261 | if(!sol_is_cdata(val) || spec->specs != AS(val->cdata, sol_cstruct_t)->specs) { 262 | return sol_set_error_string(state, "Invalid type for PTR assignment"); 263 | } 264 | AT(cstruct->data, void *, spec->offset) = AS(val->cdata, sol_cstruct_t)->data; 265 | } 266 | break; 267 | 268 | case SOL_CS_CFUNC: 269 | return sol_set_error_string(state, "Can't assign CFunc members"); 270 | break; 271 | } 272 | return sol_incref(state->None); 273 | } 274 | -------------------------------------------------------------------------------- /cdata.h: -------------------------------------------------------------------------------- 1 | #ifndef CDATA_H 2 | #define CDATA_H 3 | 4 | #include "sol.h" 5 | 6 | typedef enum { 7 | SOL_MT_INT, 8 | SOL_MT_INT8, 9 | SOL_MT_INT16, 10 | SOL_MT_INT32, 11 | SOL_MT_INT64, 12 | SOL_MT_UINT, 13 | SOL_MT_UINT8, 14 | SOL_MT_UINT16, 15 | SOL_MT_UINT32, 16 | // SOL_MT_UINT64, TODO: Not yet supported 17 | SOL_MT_FLOAT, 18 | SOL_MT_DOUBLE, 19 | SOL_MT_CHAR, 20 | SOL_MT_CSTR, 21 | SOL_MT_CFUNC, 22 | SOL_MT_PTR // Don't use this in add_member--use add_pointer 23 | } sol_memtype_t; 24 | 25 | typedef enum { 26 | SOL_CS_MEMBER, 27 | SOL_CS_CFUNC 28 | } sol_spec_t; 29 | 30 | typedef struct { 31 | sol_spec_t type; 32 | union { 33 | struct { 34 | sol_memtype_t memtype; 35 | int offset; 36 | sol_object_t *specs; 37 | }; 38 | sol_cfunc_t cfunc; 39 | }; 40 | } sol_memspec_t; 41 | 42 | typedef struct { 43 | void *data; 44 | sol_object_t *specs; 45 | } sol_cstruct_t; 46 | 47 | sol_object_t *sol_new_cstruct_specs(sol_state_t *); 48 | void sol_cstruct_add_member(sol_state_t *, sol_object_t *, sol_object_t *, sol_memtype_t, int); 49 | void sol_cstruct_add_member_name(sol_state_t *, sol_object_t *, char *, sol_memtype_t, int); 50 | void sol_cstruct_add_pointer(sol_state_t *, sol_object_t *, sol_object_t *, sol_object_t *, int); 51 | void sol_cstruct_add_pointer_name(sol_state_t *, sol_object_t *, char *, sol_object_t *, int); 52 | void sol_cstruct_add_func(sol_state_t *, sol_object_t *, sol_object_t *, sol_cfunc_t); 53 | void sol_cstruct_add_func_name(sol_state_t *, sol_object_t *, char *, sol_cfunc_t); 54 | 55 | sol_object_t *sol_new_cstruct(sol_state_t *, void *, sol_object_t *); 56 | 57 | extern sol_ops_t sol_cstruct_spec_ops; 58 | extern sol_ops_t sol_cstruct_ops; 59 | 60 | sol_object_t *sol_f_cstruct_index(sol_state_t *, sol_object_t *); 61 | sol_object_t *sol_f_cstruct_setindex(sol_state_t *, sol_object_t *); 62 | 63 | #endif 64 | -------------------------------------------------------------------------------- /conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # Sol documentation build configuration file, created by 4 | # sphinx-quickstart on Mon Oct 19 11:40:28 2015. 5 | # 6 | # This file is execfile()d with the current directory set to its 7 | # containing dir. 8 | # 9 | # Note that not all possible configuration values are present in this 10 | # autogenerated file. 11 | # 12 | # All configuration values have a default; values that are commented out 13 | # serve to show the default. 14 | 15 | import sys 16 | import os 17 | import shlex 18 | 19 | # If extensions (or modules to document with autodoc) are in another directory, 20 | # add these directories to sys.path here. If the directory is relative to the 21 | # documentation root, use os.path.abspath to make it absolute, like shown here. 22 | #sys.path.insert(0, os.path.abspath('.')) 23 | 24 | # -- General configuration ------------------------------------------------ 25 | 26 | # If your documentation needs a minimal Sphinx version, state it here. 27 | #needs_sphinx = '1.0' 28 | 29 | # Add any Sphinx extension module names here, as strings. They can be 30 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 31 | # ones. 32 | extensions = [ 33 | 'sphinx.ext.autodoc', 34 | 'sphinx.ext.todo', 35 | 'sphinx.ext.coverage', 36 | 'sphinx.ext.mathjax', 37 | 'sphinx.ext.viewcode', 38 | 'breathe', 39 | ] 40 | 41 | # Add any paths that contain templates here, relative to this directory. 42 | templates_path = ['_templates'] 43 | 44 | # The suffix(es) of source filenames. 45 | # You can specify multiple suffix as a list of string: 46 | # source_suffix = ['.rst', '.md'] 47 | source_suffix = '.rst' 48 | 49 | # The encoding of source files. 50 | #source_encoding = 'utf-8-sig' 51 | 52 | # The master toctree document. 53 | master_doc = 'doc/index' 54 | 55 | # General information about the project. 56 | project = u'Sol' 57 | copyright = u'2015, Grissess et. al.' 58 | author = u'Grissess et. al.' 59 | 60 | # The version info for the project you're documenting, acts as replacement for 61 | # |version| and |release|, also used in various other places throughout the 62 | # built documents. 63 | # 64 | # The short X.Y version. 65 | version = '0.1' 66 | # The full version, including alpha/beta/rc tags. 67 | release = '0.1a4' 68 | 69 | # Breathe's own information about the project. 70 | breathe_projects = { 71 | project: 'doxyxml/xml', 72 | } 73 | breathe_default_project = project 74 | breathe_projects_souce = { 75 | project: '.', 76 | } 77 | 78 | # The language for content autogenerated by Sphinx. Refer to documentation 79 | # for a list of supported languages. 80 | # 81 | # This is also used if you do content translation via gettext catalogs. 82 | # Usually you set "language" from the command line for these cases. 83 | language = None 84 | 85 | # There are two options for replacing |today|: either, you set today to some 86 | # non-false value, then it is used: 87 | #today = '' 88 | # Else, today_fmt is used as the format for a strftime call. 89 | #today_fmt = '%B %d, %Y' 90 | 91 | # List of patterns, relative to source directory, that match files and 92 | # directories to ignore when looking for source files. 93 | exclude_patterns = ['_build'] 94 | 95 | # The reST default role (used for this markup: `text`) to use for all 96 | # documents. 97 | default_role = 'any' 98 | 99 | # If true, '()' will be appended to :func: etc. cross-reference text. 100 | #add_function_parentheses = True 101 | 102 | # If true, the current module name will be prepended to all description 103 | # unit titles (such as .. function::). 104 | #add_module_names = True 105 | 106 | # If true, sectionauthor and moduleauthor directives will be shown in the 107 | # output. They are ignored by default. 108 | #show_authors = False 109 | 110 | # The name of the Pygments (syntax highlighting) style to use. 111 | pygments_style = 'sphinx' 112 | 113 | # A list of ignored prefixes for module index sorting. 114 | #modindex_common_prefix = [] 115 | 116 | # If true, keep warnings as "system message" paragraphs in the built documents. 117 | #keep_warnings = False 118 | 119 | # If true, `todo` and `todoList` produce output, else they produce nothing. 120 | todo_include_todos = True 121 | 122 | 123 | # -- Options for HTML output ---------------------------------------------- 124 | 125 | # The theme to use for HTML and HTML Help pages. See the documentation for 126 | # a list of builtin themes. 127 | html_theme = 'alabaster' 128 | 129 | # Theme options are theme-specific and customize the look and feel of a theme 130 | # further. For a list of options available for each theme, see the 131 | # documentation. 132 | #html_theme_options = {} 133 | 134 | # Add any paths that contain custom themes here, relative to this directory. 135 | #html_theme_path = [] 136 | 137 | # The name for this set of Sphinx documents. If None, it defaults to 138 | # " v documentation". 139 | #html_title = None 140 | 141 | # A shorter title for the navigation bar. Default is the same as html_title. 142 | #html_short_title = None 143 | 144 | # The name of an image file (relative to this directory) to place at the top 145 | # of the sidebar. 146 | #html_logo = None 147 | 148 | # The name of an image file (within the static path) to use as favicon of the 149 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 150 | # pixels large. 151 | #html_favicon = None 152 | 153 | # Add any paths that contain custom static files (such as style sheets) here, 154 | # relative to this directory. They are copied after the builtin static files, 155 | # so a file named "default.css" will overwrite the builtin "default.css". 156 | html_static_path = ['_static'] 157 | 158 | # Add any extra paths that contain custom files (such as robots.txt or 159 | # .htaccess) here, relative to this directory. These files are copied 160 | # directly to the root of the documentation. 161 | #html_extra_path = [] 162 | 163 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 164 | # using the given strftime format. 165 | #html_last_updated_fmt = '%b %d, %Y' 166 | 167 | # If true, SmartyPants will be used to convert quotes and dashes to 168 | # typographically correct entities. 169 | #html_use_smartypants = True 170 | 171 | # Custom sidebar templates, maps document names to template names. 172 | #html_sidebars = {} 173 | 174 | # Additional templates that should be rendered to pages, maps page names to 175 | # template names. 176 | #html_additional_pages = {} 177 | 178 | # If false, no module index is generated. 179 | #html_domain_indices = True 180 | 181 | # If false, no index is generated. 182 | #html_use_index = True 183 | 184 | # If true, the index is split into individual pages for each letter. 185 | #html_split_index = False 186 | 187 | # If true, links to the reST sources are added to the pages. 188 | #html_show_sourcelink = True 189 | 190 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 191 | #html_show_sphinx = True 192 | 193 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 194 | #html_show_copyright = True 195 | 196 | # If true, an OpenSearch description file will be output, and all pages will 197 | # contain a tag referring to it. The value of this option must be the 198 | # base URL from which the finished HTML is served. 199 | #html_use_opensearch = '' 200 | 201 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 202 | #html_file_suffix = None 203 | 204 | # Language to be used for generating the HTML full-text search index. 205 | # Sphinx supports the following languages: 206 | # 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' 207 | # 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr' 208 | #html_search_language = 'en' 209 | 210 | # A dictionary with options for the search language support, empty by default. 211 | # Now only 'ja' uses this config value 212 | #html_search_options = {'type': 'default'} 213 | 214 | # The name of a javascript file (relative to the configuration directory) that 215 | # implements a search results scorer. If empty, the default will be used. 216 | #html_search_scorer = 'scorer.js' 217 | 218 | # Output file base name for HTML help builder. 219 | htmlhelp_basename = 'Soldoc' 220 | 221 | # -- Options for LaTeX output --------------------------------------------- 222 | 223 | latex_elements = { 224 | # The paper size ('letterpaper' or 'a4paper'). 225 | #'papersize': 'letterpaper', 226 | 227 | # The font size ('10pt', '11pt' or '12pt'). 228 | #'pointsize': '10pt', 229 | 230 | # Additional stuff for the LaTeX preamble. 231 | #'preamble': '', 232 | 233 | # Latex figure (float) alignment 234 | #'figure_align': 'htbp', 235 | } 236 | 237 | # Grouping the document tree into LaTeX files. List of tuples 238 | # (source start file, target name, title, 239 | # author, documentclass [howto, manual, or own class]). 240 | latex_documents = [ 241 | (master_doc, 'Sol.tex', u'Sol Documentation', 242 | u'Grissess et. al.', 'manual'), 243 | ] 244 | 245 | # The name of an image file (relative to this directory) to place at the top of 246 | # the title page. 247 | #latex_logo = None 248 | 249 | # For "manual" documents, if this is true, then toplevel headings are parts, 250 | # not chapters. 251 | #latex_use_parts = False 252 | 253 | # If true, show page references after internal links. 254 | #latex_show_pagerefs = False 255 | 256 | # If true, show URL addresses after external links. 257 | #latex_show_urls = False 258 | 259 | # Documents to append as an appendix to all manuals. 260 | #latex_appendices = [] 261 | 262 | # If false, no module index is generated. 263 | #latex_domain_indices = True 264 | 265 | 266 | # -- Options for manual page output --------------------------------------- 267 | 268 | # One entry per manual page. List of tuples 269 | # (source start file, name, description, authors, manual section). 270 | man_pages = [ 271 | (master_doc, 'sol', u'Sol Documentation', 272 | [author], 1) 273 | ] 274 | 275 | # If true, show URL addresses after external links. 276 | #man_show_urls = False 277 | 278 | 279 | # -- Options for Texinfo output ------------------------------------------- 280 | 281 | # Grouping the document tree into Texinfo files. List of tuples 282 | # (source start file, target name, title, author, 283 | # dir menu entry, description, category) 284 | texinfo_documents = [ 285 | (master_doc, 'Sol', u'Sol Documentation', 286 | author, 'Sol', 'One line description of project.', 287 | 'Miscellaneous'), 288 | ] 289 | 290 | # Documents to append as an appendix to all manuals. 291 | #texinfo_appendices = [] 292 | 293 | # If false, no module index is generated. 294 | #texinfo_domain_indices = True 295 | 296 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 297 | #texinfo_show_urls = 'footnote' 298 | 299 | # If true, do not generate a @detailmenu in the "Top" node's menu. 300 | #texinfo_no_detailmenu = False 301 | 302 | # Following are site-local extensions for this project: 303 | -------------------------------------------------------------------------------- /data/test_out.txt: -------------------------------------------------------------------------------- 1 | --- Empty functions 2 | None 3 | --- While loop 4 | 1 5 | 2 6 | 3 7 | 4 8 | 5 9 | 6 10 | 7 11 | 8 12 | 9 13 | --- Range 14 | [0, 1, 2, 3, 4] 15 | --- Iter list 16 | 1 17 | 2 18 | 3 19 | 4 20 | 5 21 | --- Index list 22 | 0 1 23 | 1 2 24 | 2 3 25 | 3 4 26 | 4 5 27 | --- mul9 28 | 9 29 | 18 30 | 27 31 | 36 32 | 45 33 | --- Iter mul9 34 | 0 9 35 | 1 18 36 | 2 27 37 | 3 36 38 | 4 45 39 | --- Mapgen 40 | {[10] = ... (0x1409410), [6] = ... (0x1409310), ["this time with spaces"] = 6.283185, ["sublist"] = [1, ... (0x14091d0), 2, 3, 5, 8], ["submap"] = {["health"] = 42.000000, ["stamina"] = 100}, ["string"] = "hello", ["integer"] = ... (0x14091d0)} 41 | --- Map iter 42 | 10 10 43 | 6 6 44 | this time with spaces 6.283185 45 | sublist [1, ... (0x14091d0), 2, 3, 5, 8] 46 | submap {["health"] = 42.000000, ["stamina"] = 100} 47 | string hello 48 | integer 1 49 | --- try 50 | {["c"] = } 51 | [1, 15] 52 | {} 53 | [0, "Undefined method (call on singlet)", [[, {["bad"] = , ["x"] = {}}], [, ... (0x145db20)]]] 54 | {["c"] = } 55 | 15 56 | --- Induced errors 57 | [0, "lp0 on fire", [[, {["raise"] = , ["x"] = ... (0x145e2c0)}], [, ... (0x145f060)]]] 58 | --- Indexing 59 | 1 60 | 1 61 | 6 62 | --- Function binding 63 | 8 9 10 64 | 11 12 13 65 | --- Iterators 66 | 1 67 | 2 68 | 3 69 | 4 70 | 5 71 | 6 72 | 7 73 | 8 74 | 9 75 | 10 76 | --- Method calls 77 | 1 2 78 | {["a"] = } 3 79 | --- Special methods 80 | Index {["__call"] = , ["__setindex"] = , ["__index"] = } 3 81 | Index {["__call"] = , ["__setindex"] = , ["__index"] = } 5 82 | 3 5 83 | SetIndex {["__call"] = , ["__setindex"] = , ["__index"] = } a 7 84 | Call {["__call"] = , ["__setindex"] = , ["__index"] = } q r 85 | q 86 | {["__setindex"] = {["b"] = 2, ["a"] = 1}, ["__index"] = ... (0x1464400)} 1 2 87 | {["__setindex"] = {["b"] = 2, ["a"] = 1}, ["__index"] = ... (0x1464400)} {... (0x1465400), ... (0x1465140)} 88 | --- Data sharing 89 | {["b"] = [1, 2, 3, 4, 5], ["a"] = ... (0x1465980)} 90 | {["b"] = [1, 2, 7, "c", 5], ["a"] = ... (0x1465980)} 91 | {["b"] = [1, 7, "c", "f", "q"], ["a"] = ... (0x1465980)} 92 | --- Arithmetic structure operations 93 | abcd 94 | lolololololololololololololololololololololololololololololololol 95 | [1, 2, 3, 4, 5] 96 | [1, 2, 3, ... (0x14091d0), ... (0x1409210), ... (0x1409250), ... (0x14091d0), ... (0x1409210), ... (0x1409250), ... (0x14091d0), ... (0x1409210), ... (0x1409250), ... (0x14091d0), ... (0x1409210), ... (0x1409250)] 97 | {["c"] = 3, ["b"] = 2, ["a"] = 1} 98 | --- Map/filter 99 | [1, 2, 3, 4, 5] 100 | [3, 6, 9, 12, 15] 101 | [3, 9, 15] 102 | --- Map/filter chain 103 | [3, 9, 15] 104 | --- Exec/eval 105 | Hello from exec! 106 | 8 107 | Hello from subtest! 108 | --- Modulus 109 | 2 110 | 3 111 | 0 112 | --- Special function manipulation 113 | 114 | 115 | [1, 2, 3] 116 | ["b", 1, 2, 3] 117 | --- Function body swapping 118 | 0 119 | 2 120 | 121 | 122 | 2 123 | 0 124 | 125 | 4 126 | 4 127 | --- More complicated ASTs 128 | [, , ] 129 | --- Exec- and eval-by-parse 130 | Hello from parse()! 131 | 8 132 | --- Mutating ASTs 133 | 12 134 | 12 135 | a= 1 , b= 2 136 | None 137 | a= 1 , b= 2 138 | None 139 | --- AST Environments 140 | " 141 | a is and b is 142 | {["b"] = 2, ["a"] = 1} 143 | a is 1 and b is 2 144 | {["b"] = 5, ["a"] = 4} 145 | {["b"] = ["world"], ["a"] = "hello"} 146 | a is hello and b is ["world"] 147 | {["b"] = 5, ["a"] = 4} 148 | {["__index"] = {["b"] = 2, ["a"] = 1}} 149 | {["b"] = 2, ["a"] = 1} 150 | a is 1 and b is 2 151 | {["b"] = 5, ["a"] = 4, ["__index"] = {["b"] = 2, ["a"] = 1}} 152 | {["b"] = 2, ["a"] = 1} 153 | --- Basic buffers 154 | (buffer.fromstring = ) 155 | ) 157 | Hello, world! 158 | QeLlo, world! 159 | , world! 160 | 1816946001 161 | 1886545252 162 | derpo, world! 163 | derpo, world! 164 | Goodbye! Goodbye! 165 | "A string!" 166 | 167 | ...is a SOL_STRING 168 | (buffer.sizeof.ptr = 8 ) 169 | (buffer.sizeof.int = 4 ) 170 | (buffer.sizeof.int*2 = 8 ) 171 | (buffer.sizeof.int*2 + buffer.sizeof.ptr = 16 ) 172 | ...string buffer: 173 | ...with value: A string! 174 | --- IO redirection 175 | A line! 176 | An object: {["c"] = "turkey", ["b"] = 2, ["a"] = 1} 177 | Something mysterious :o 178 | Writing directly to a file :D...restored stdout. 179 | Buffered output was: 180 | "" 181 | 182 | ...second time. 183 | Hey there! 184 | lolololololololololololololololololololololololololololololololol 185 | ...restored. 186 | Output was: 187 | "" 188 | 189 | --- Substrings 190 | "This is a test!" 191 | 192 | "his is a test" 193 | 194 | "s is a te" 195 | 196 | "s " 197 | 198 | "s is a t" 199 | 200 | "" 201 | 202 | --- Splitting 203 | "This is a test!" 204 | 205 | ["This", "is", "a", "test!"] 206 | 207 | ["Th", "s ", "s a test!"] 208 | 209 | ["This is a test!"] 210 | 211 | ["Th", "s ", "s ", " t", "st!"] 212 | 213 | "This" "string" 214 | 215 | "is" "string" 216 | 217 | "a" "string" 218 | 219 | "test!" "string" 220 | 221 | --- Continue/break 222 | 0 223 | 1 224 | 2 225 | 3 226 | 4 227 | 5 228 | --- 229 | 1 230 | 3 231 | 5 232 | 7 233 | 9 234 | --- Control expressions 235 | 1 is true 236 | 0 is false 237 | [1000, 1002, 1004, 1006, 1008, 1010, 1012, 1014, 1016, 1018] 238 | ( 0 ) 239 | ( 1 ) 240 | ( 2 ) 241 | ( 3 ) 242 | ( 4 ) 243 | ( 5 ) 244 | 5 245 | --- All done! 246 | -------------------------------------------------------------------------------- /doc.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Required PyPI packages: sphinx, breathe 4 | # Required ext packages: doxygen 5 | 6 | doxygen Doxyfile 7 | sphinx-build -b html . ./_build 8 | -------------------------------------------------------------------------------- /doc/index.rst: -------------------------------------------------------------------------------- 1 | .. Sol documentation master file, created by 2 | sphinx-quickstart on Mon Oct 19 11:40:28 2015. 3 | 4 | Welcome to Sol's documentation! 5 | =============================== 6 | 7 | Sol is an eccentric language modelled on the best of Python and Lua--so it 8 | looks (and performs) a little bit like Javascript! The entire source tree is 9 | fairly small and suitable for static linking, containing just a (featureful) 10 | core runtime and a Bison-Flex parser. 11 | 12 | Sol is an interpreted language that uses a reference-counting garbage 13 | collector; it was initially built for `iiag `_, 14 | and can still be found as a working proof-of-concept for integration in its 15 | `legacy "sol" branch `_. 16 | 17 | Contents: 18 | 19 | .. toctree:: 20 | :maxdepth: 2 21 | 22 | User's Manual 23 | Developer's Manual 24 | genindex 25 | -------------------------------------------------------------------------------- /doc/lang/abs_basics.rst: -------------------------------------------------------------------------------- 1 | Absolute Basics 2 | =============== 3 | 4 | This chapter is intended to give you just enough orientation of the Sol language to read the examples to follow. 5 | 6 | Sol has just one comment type, and it is a line comment; it is preceded by two dashes (--), and appears as follows:: 7 | 8 | -- This is a comment 9 | --Whitespace to the right is recommended, but not required. 10 | -- Anything to the right of the dashes until the end of the line is completely ignored by the parser. 11 | 12 | Interactively, and in many examples, the `prepr` `function ` is used to print convenient forms of the value or values given to it as parameters:: 13 | 14 | prepr(1) -- Outputs "1" (without enclosing quotes) to stdout 15 | prepr(13.3) -- Outputs "13.3" to stdout" 16 | prepr("hi!") -- Outputs '"hi!"' (without enclosing single quotes) to stdout 17 | 18 | Alternatively, the `interpreter` will print the resulting value of any evaluation entered at its prompt. 19 | -------------------------------------------------------------------------------- /doc/lang/index.rst: -------------------------------------------------------------------------------- 1 | Language Reference 2 | ================== 3 | 4 | The Sol language is an interpreted language with fair syntactic similarity to 5 | Lua and Python, but has enough quirks that some constructs might not be clear. 6 | This guide should be considered definitive to the syntax of the language, but 7 | you should be aware that the namespace under which the language is run may 8 | differ between applications, and that you should consult those applications' 9 | manuals for more information. Only under the basic interpreter (`solrun.c`, or 10 | `sol` from the command line), the `standard library ` is well 11 | defined. 12 | 13 | .. toctree:: 14 | 15 | abs_basics 16 | simple_exprs 17 | control_flow 18 | lists_maps 19 | functions 20 | stdlib 21 | -------------------------------------------------------------------------------- /doc/lang/simple_exprs.rst: -------------------------------------------------------------------------------- 1 | Simple Expressions 2 | ================== 3 | 4 | Literals 5 | -------- 6 | 7 | A *literal* is a leaf in an expression; it is a value in the source of a Sol program which evaluates to itself. Literals in Sol come in exactly four flavors: 8 | 9 | * Integer literals, represented by contiguous digits, possibly preceded by a 10 | negation, like `0`, `123456`, `-314159`, and the like. 11 | * Floating point literals, represented by a pair of contiguous digits separated 12 | by one radix mark, again possibly preceded by a negation, like `1.0`, 13 | `-0.256`, `999.9`, and the like. 14 | * String literals, bound on either side by a single or double quote (they must 15 | match), and containing every interim character (including newline), like 16 | `"hello world"`, `'this string has two backslashes: \\'`, and the like. 17 | Naturally, a string may not contain its own terminator, though it can be 18 | introduced by concatenating strings with different terminators. 19 | * The special literal `None`, a value only ever equal to itself and which 20 | cannot be reassigned by the user program. It is frequently used to represent 21 | the absence of a value, such as the value of a function which does not 22 | explicitly return a value. 23 | 24 | The following source fragment demonstrates some of the literals:: 25 | 26 | a = 123 27 | -------------------------------------------------------------------------------- /doc/src/api_conventions.rst: -------------------------------------------------------------------------------- 1 | API Conventions 2 | =============== 3 | 4 | This document is intended to be a quick rundown on the necessary information to 5 | start extending and modifying Sol in its source form. 6 | 7 | Reference Counting 8 | ------------------ 9 | 10 | Objects in Sol are *reference-counted* for memory management--that is, when 11 | initially allocated (in `sol_alloc_object`), they are set to a reference count 12 | (see the refcnt member) of 1. Every call to `sol_obj_free` will decrement the 13 | reference count of that object, and, when that reference count reaches zero, 14 | the memory is freed (on the assumption that no other references to that object 15 | exist). This does *not* prevent the occurrence of cycles, and Sol has no 16 | protection against this at the moment. 17 | 18 | As a general rule, *all* functions that return object pointers return a *new 19 | reference*--the *caller* is expected to `sol_obj_free` that reference when it 20 | is no longer needed (including immediately, for example). It is also the rule 21 | that functions that take object pointers will *borrow* those references as 22 | needed; for example, data structures that store objects will hold a reference. 23 | 24 | Sol State 25 | --------- 26 | 27 | The Sol state (type `sol_state_t`) is a structure that is passed to nearly all 28 | functions in the API, and is responsible for managing all the global state of 29 | the interpreter. Due to this separation, Sol is fully reentrant. Designers 30 | looking to modify Sol in a way that requires the introduction of data across 31 | scopes and call frames may add fields to the state; extenders may use or refer 32 | to the state uniquely to determine context, or use objects stored within that 33 | state to accomplish that task. 34 | 35 | Naming 36 | ------ 37 | 38 | Names exported in `sol.h` should be safe to use publicly--that is, they should 39 | have a low chance of conflicting with names used in other programs; the typical 40 | convention is to prefix every such name with "sol_". Names in `ast.h` are not so 41 | restricted, and are typically included in many of the internal C files. `ast.h` 42 | includes `sol.h`, and so only one of the two headers needs to be used. Note that 43 | some references in structures defined in `sol.h` refer to types in `ast.h`, and 44 | these are intentionally left as void pointers; Comments and other documentation 45 | should indicate their true type. 46 | 47 | File Structure 48 | -------------- 49 | 50 | This list represents the file structure at this time, and may not be kept up to 51 | date: 52 | 53 | - object.c contains all routines for the runtime, including creation, 54 | destruction, and manipulation of most objects. 55 | - builtins.c contains the vast majority of built-in functionality, including 56 | most of the type methods as included in the `sol_ops_t` structures. 57 | - runtime.c contains the interpreter and related routines, including execution 58 | and evaluation, ones defining the calling convention, and the manipulation of 59 | ASTs. 60 | - state.c contains state management routines, such as those for initializing 61 | and finalizing a state, getting or setting errors, and resolving names in 62 | scopes. Module, type method, and other initialization is done here. 63 | - gc.c contains the memory management routines. 64 | - astprint.c contains routines for displaying ASTs on stdout. 65 | - solrun.c contains the main() function for the interpreter. 66 | - cdata.c contains definitions of various CDATA (C interface) types--presently, 67 | only the CStruct (a way for C code to specify struct layouts to Sol 68 | programs). 69 | - util.c contains utility routines--importantly, a safe call/return trampoline 70 | for extension code. 71 | -------------------------------------------------------------------------------- /doc/src/ast_h.rst: -------------------------------------------------------------------------------- 1 | Internal Definitions (ast.h) 2 | ============================ 3 | 4 | For historical reasons, this header file is called "ast.h"--the definitions of 5 | the AST nodes are not namespace-safe (they aren't prefixed with `sol_`), and so 6 | this file is not generally meant to be included by end users unless strictly 7 | required. 8 | 9 | .. doxygenfile:: ast.h 10 | -------------------------------------------------------------------------------- /doc/src/index.rst: -------------------------------------------------------------------------------- 1 | Source Documentation 2 | ==================== 3 | 4 | This section of the documentation is intended for integrators looking to 5 | incorporate Sol into one of their projects, and for developers looking to 6 | extend, maintain, or port Sol. 7 | 8 | Many of the pages in this section are automatically documented from source file 9 | comments; if you are writing or rewriting this documentation, please consult 10 | the documentation on "doxygenfile". 11 | 12 | .. toctree:: 13 | 14 | api_conventions 15 | sol_h 16 | ast_h 17 | -------------------------------------------------------------------------------- /doc/src/sol_h.rst: -------------------------------------------------------------------------------- 1 | User Includes (sol.h) 2 | ===================== 3 | 4 | This header file includes all the standard functionality of Sol in such a way 5 | that user applications can safely call into the library. All types and 6 | functions exported from here are prefixed with `sol_` (possibly further 7 | prefixed by a number of underscores), so as to minimize namespace collision. 8 | 9 | .. doxygenfile:: sol.h 10 | -------------------------------------------------------------------------------- /dsdebug.gdb: -------------------------------------------------------------------------------- 1 | break object.c:277 2 | condition $bpnum sol_validate_list(list) 3 | break object.c:304 4 | condition $bpnum sol_validate_list(list) 5 | -------------------------------------------------------------------------------- /foo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sol-lang/sol/f42f57d6632fc8fb70f121877a5fc495a5486893/foo -------------------------------------------------------------------------------- /gc.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "sol.h" 5 | 6 | /** Allocates and returns a new reference to a typeless object. 7 | * 8 | * This is an internal function. Users should use `sol_alloc_object` instead. 9 | */ 10 | 11 | sol_object_t *_sol_gc_alloc_object(sol_state_t *state) { 12 | sol_object_t *res = malloc(sizeof(sol_object_t)); 13 | if(!res) { 14 | sol_set_error(state, state->OutOfMemory); 15 | return sol_incref(state->None); 16 | } 17 | res->refcnt = 0; 18 | res->ops = &(state->NullOps); 19 | return sol_incref(res); 20 | } 21 | 22 | /** Frees a reference to an object. 23 | * 24 | * This is an internal function. Users should use `sol_obj_free` instead. 25 | */ 26 | 27 | void _sol_gc_obj_free(sol_object_t *obj) { 28 | if(!obj) { 29 | /*printf("WARNING: Attempt to free NULL\n");*/ 30 | return; 31 | } 32 | if(sol_decref(obj) <= 0) { 33 | if(obj->refcnt < 0) { 34 | printf("WARNING: Encountered refcnt < 0!\nObject %p type %d ref %d\n", obj, obj->type, obj->refcnt); 35 | } else { 36 | sol_obj_release(obj); 37 | } 38 | } 39 | } 40 | 41 | /** Increments the reference count of an object, and return it. 42 | * 43 | * This function is exactly an identity function, but it increments the 44 | * object's reference count. 45 | * 46 | * It is intended for use in places where a function is required, such as 47 | * assigning to a function pointer. 48 | * 49 | * Users with the ability to should use `sol_incref` instead. 50 | */ 51 | 52 | sol_object_t *sol_obj_acquire(sol_object_t *obj) { 53 | return sol_incref(obj); 54 | } 55 | 56 | #ifdef DEBUG_GC 57 | 58 | static FILE *gclog = NULL; 59 | static int gcrefcnt = 0; 60 | 61 | static char gctime[64]; 62 | 63 | /* 64 | char *prtime() { 65 | time_t t; 66 | struct tm t2; 67 | time(&t); 68 | localtime_r(&t, &t2); 69 | strftime(gctime, 64, "%Y/%m/%d %T", &t2); 70 | return gctime; 71 | } 72 | */ 73 | 74 | char *prtime() {return "";} 75 | 76 | void sol_mm_initialize(sol_state_t *state) { 77 | if(gclog) { 78 | fprintf(gclog, " === Reopened at %s ===\n", prtime()); 79 | } else { 80 | gclog = fopen("gclog.txt", "a"); 81 | fprintf(gclog, "=== Opened at %s ===\n", prtime()); 82 | } 83 | gcrefcnt++; 84 | } 85 | 86 | void sol_mm_finalize(sol_state_t *state) { 87 | gcrefcnt--; 88 | fprintf(gclog, "=== Closed at %s ===\n", prtime()); 89 | if(gcrefcnt <= 0) { 90 | fflush(gclog); 91 | fclose(gclog); 92 | gclog = NULL; 93 | } 94 | } 95 | 96 | sol_object_t *_int_sol_alloc_object(const char *func, sol_state_t *state) { 97 | fprintf(gclog, "%s\tA\n", func); 98 | return _sol_gc_alloc_object(state); 99 | } 100 | 101 | sol_object_t *_int_sol_incref(const char *func, sol_object_t *obj) { 102 | int oldref = obj->refcnt++; 103 | fprintf(gclog, "%s\tI\t%s\t%p\t%d\t->\t%d\n", func, obj->ops->tname, obj, oldref, obj->refcnt); 104 | return obj; 105 | } 106 | 107 | void _int_sol_obj_free(const char *func, sol_object_t *obj) { 108 | fprintf(gclog, "%s\tD\t%s\t%p\t%d\t->\t%d\n", func, obj->ops->tname, obj, obj->refcnt, obj->refcnt - 1); 109 | _sol_gc_obj_free(obj); 110 | } 111 | 112 | 113 | void sol_obj_release(sol_object_t *obj) { 114 | fprintf(gclog, "\tF\t%s\t%p\n", obj->ops->tname, obj); 115 | if(obj->ops->free) obj->ops->free(NULL, obj); 116 | free(obj); 117 | } 118 | 119 | sol_object_t *_sol_gc_dsl_copier(sol_object_t *obj) { 120 | fprintf(gclog, "\tI\t%s\t%p\t%d\t->\t%d\n", obj->ops->tname, obj, obj->refcnt, ++obj->refcnt); 121 | return obj; 122 | } 123 | 124 | void _sol_gc_dsl_destructor(sol_object_t *obj) { 125 | fprintf(gclog, "\tD\t%s\t%p\t%d\t->\t%d\n", obj->ops->tname, obj, obj->refcnt, obj->refcnt - 1); 126 | _sol_gc_obj_free(obj); 127 | } 128 | 129 | #else 130 | 131 | /** Allocates and returns a new reference to a typeless object. 132 | * 133 | * This function is intended to be called from object constructors that will 134 | * ultimately set the type and operations on the object. 135 | * 136 | * The returned reference is initially the only reference to this object. 137 | */ 138 | 139 | sol_object_t *sol_alloc_object(sol_state_t *state) { 140 | return _sol_gc_alloc_object(state); 141 | } 142 | 143 | /** Frees a reference to an object. 144 | * 145 | * If the given reference is the last reference to this object, the memory is 146 | * freed, after any type-specific destructors are called. 147 | */ 148 | 149 | void sol_obj_free(sol_object_t *obj) { 150 | _sol_gc_obj_free(obj); 151 | } 152 | 153 | /** Destroys an object. 154 | * 155 | * This function is called on an object whose last reference has been freed; it 156 | * is responsible for implementing the destructor protocol (and so calling the 157 | * necessary methods). In general, it should not be used by user code, as it 158 | * may introduce use-after-free. 159 | */ 160 | 161 | void sol_obj_release(sol_object_t *obj) { 162 | if(obj->ops->free) { 163 | obj->ops->free(NULL, obj); 164 | } 165 | free(obj); 166 | } 167 | 168 | /** Initialize the memory manager for a state. 169 | * 170 | * You normally do not need to call this; it is also done in `sol_state_init`. 171 | */ 172 | 173 | void sol_mm_initialize(sol_state_t *state) {} 174 | 175 | /** Finalize the memory manager for a state. 176 | * 177 | * You normally do not need to call this; it is also done in 178 | * `sol_state_cleanup`. 179 | */ 180 | 181 | void sol_mm_finalize(sol_state_t *state) {} 182 | 183 | #endif 184 | -------------------------------------------------------------------------------- /gcstat.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from collections import defaultdict 3 | 4 | f = open('gclog.txt', 'r') 5 | 6 | incs = defaultdict(lambda: 0) 7 | decs = defaultdict(lambda: 0) 8 | refs = defaultdict(lambda: 0) 9 | seenrefs = {} 10 | types = {} 11 | 12 | lineno = 0 13 | 14 | for line in f: 15 | lineno += 1 16 | if lineno % 10000 == 0: 17 | print 'Processing line', lineno 18 | if line[0] == '=': 19 | continue 20 | parts = line.split('\t') 21 | if parts[1] == 'I': 22 | incs[parts[0]] += 1 23 | refs[parts[3]] += 1 24 | types[parts[3]] = parts[2] 25 | seenrefs[parts[3]] = parts[6] 26 | elif parts[1] == 'D': 27 | decs[parts[0]] += 1 28 | refs[parts[3]] -= 1 29 | types[parts[3]] = parts[2] 30 | seenrefs[parts[3]] = parts[6] 31 | 32 | incpairs = incs.items() 33 | decpairs = decs.items() 34 | refpairs = refs.items() 35 | 36 | incpairs.sort(key=lambda it: it[1], reverse=True) 37 | decpairs.sort(key=lambda it: it[1], reverse=True) 38 | refpairs.sort(key=lambda it: it[1], reverse=True) 39 | 40 | for k in seenrefs: 41 | seenrefs[k] = int(seenrefs[k]) 42 | 43 | totincs = sum((i[1] for i in incpairs)) 44 | totdecs = sum((i[1] for i in decpairs)) 45 | totliving = sum((i[1] for i in refpairs)) 46 | totsol = sum(seenrefs.itervalues()) 47 | 48 | out = open('gcstat.txt', 'w') 49 | sys.stdout = out 50 | 51 | print '=== Totals ===' 52 | print '= Increfs:', totincs 53 | print '= Decrefs:', totdecs 54 | print '= Diff:', totincs - totdecs 55 | print '= Living (our estimate):', totliving 56 | print '= Living (according to Sol):', totsol 57 | 58 | print '=== Functions, sorted by increments ===' 59 | print '= %-30s%-8s%-8s'%('name', 'incs', 'decs') 60 | for func, inccnt in incpairs: 61 | print '%-32s%-8d%-8d'%(func, inccnt, decs[func]) 62 | 63 | print '=== Functions, sorted by decrements ===' 64 | print '= %-30s%-8s%-8s'%('name', 'decs', 'incs') 65 | for func, deccnt in decpairs: 66 | print '%-32s%-8d%-8d'%(func, deccnt, incs[func]) 67 | 68 | print '=== Objects alive at cleanup ===' 69 | print '= %-14s%-16s%-8s%-8s'%('addr', 'type', 'refs', 'solrefs') 70 | for addr, refcnt in refpairs: 71 | print '%-16s%-16s%-8d%-8d'%(addr, types[addr], refcnt, seenrefs[addr]) 72 | -------------------------------------------------------------------------------- /install_sol.md: -------------------------------------------------------------------------------- 1 | # Install Sol 2 | 3 | Let's start off with getting sol ready. Head over to the [Sol](https://github.com/sol-lang/sol) repository on Github and either download a zip file of it or use ```git clone```. 4 | 5 | Open up the directory and run the Makefile with ```make```. This should download any dependencies and begin compiling Sol. If it is successful, you should have a ./Solace executable. Now you can either begin writing sol and running programs or using the interpreter. -------------------------------------------------------------------------------- /object.c: -------------------------------------------------------------------------------- 1 | #include "ast.h" // For CALL_METHOD 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | sol_object_t *sol_cast_int(sol_state_t *state, sol_object_t *obj) { 11 | sol_object_t *res, *ls; 12 | if(sol_is_int(obj)) { 13 | return sol_incref(obj); 14 | } 15 | ls = sol_new_list(state); 16 | sol_list_insert(state, ls, 0, obj); 17 | res = CALL_METHOD(state, obj, toint, ls); 18 | sol_obj_free(ls); 19 | return res; 20 | } 21 | 22 | sol_object_t *sol_cast_float(sol_state_t *state, sol_object_t *obj) { 23 | sol_object_t *res, *ls; 24 | if(sol_is_float(obj)) { 25 | return sol_incref(obj); 26 | } 27 | ls = sol_new_list(state); 28 | sol_list_insert(state, ls, 0, obj); 29 | res = CALL_METHOD(state, obj, tofloat, ls); 30 | sol_obj_free(ls); 31 | return res; 32 | } 33 | 34 | sol_object_t *sol_cast_buffer(sol_state_t *state, sol_object_t *obj) { 35 | sol_object_t *res, *ls; 36 | if(sol_is_buffer(obj)) { 37 | return sol_incref(obj); 38 | } 39 | ls = sol_new_list(state); 40 | sol_list_insert(state, ls, 0, obj); 41 | res = CALL_METHOD(state, obj, tobuffer, ls); 42 | sol_obj_free(ls); 43 | return res; 44 | } 45 | 46 | sol_object_t *sol_cast_string(sol_state_t *state, sol_object_t *obj) { 47 | sol_object_t *res, *ls; 48 | if(sol_is_string(obj)) { 49 | return sol_incref(obj); 50 | } 51 | ls = sol_new_list(state); 52 | sol_list_insert(state, ls, 0, obj); 53 | res = CALL_METHOD(state, obj, tostring, ls); 54 | sol_obj_free(ls); 55 | return res; 56 | } 57 | 58 | sol_object_t *sol_cast_repr(sol_state_t *state, sol_object_t *obj) { 59 | sol_object_t *res, *ls = sol_new_list(state); 60 | sol_list_insert(state, ls, 0, obj); 61 | res = CALL_METHOD(state, obj, repr, ls); 62 | sol_obj_free(ls); 63 | return res; 64 | } 65 | 66 | // This will not fail here; error checking is done in sol_state_init(). 67 | 68 | sol_object_t *sol_new_singlet(sol_state_t *state, const char *name) { 69 | sol_object_t *res = malloc(sizeof(sol_object_t)); 70 | if(res) { 71 | res->type = SOL_SINGLET; 72 | res->refcnt = 0; 73 | res->ops = &(state->SingletOps); 74 | res->str = strdup(name); 75 | } 76 | return sol_incref(res); // XXX Segfault 77 | } 78 | 79 | sol_object_t *sol_f_singlet_free(sol_state_t *state, sol_object_t *singlet) { 80 | free(singlet->str); 81 | } 82 | 83 | // And, now, for the rest of the checked stuff... 84 | 85 | void sol_init_object(sol_state_t *state, sol_object_t *obj) { 86 | if(obj->ops->init) { 87 | obj->ops->init(state, obj); 88 | } 89 | } 90 | 91 | sol_object_t *sol_new_int(sol_state_t *state, long i) { 92 | sol_object_t *res; 93 | #ifdef SOL_ICACHE 94 | if(!state->icache_bypass && i >= SOL_ICACHE_MIN && i <= SOL_ICACHE_MAX) { 95 | res = sol_incref(state->icache[i - SOL_ICACHE_MIN]); 96 | if(res->ival != i) { 97 | printf("WARNING: Integer at %ld mutated to %ld! Resetting...\n", i, res->ival); 98 | res->ival = i; 99 | } 100 | return res; 101 | } 102 | #endif 103 | res = sol_alloc_object(state); 104 | res->type = SOL_INTEGER; 105 | res->ival = i; 106 | res->ops = &(state->IntOps); 107 | sol_init_object(state, res); 108 | return res; 109 | } 110 | 111 | sol_object_t *sol_new_float(sol_state_t *state, double f) { 112 | sol_object_t *res = sol_alloc_object(state); 113 | res->type = SOL_FLOAT; 114 | res->fval = f; 115 | res->ops = &(state->FloatOps); 116 | sol_init_object(state, res); 117 | return res; 118 | } 119 | 120 | sol_object_t *sol_new_string(sol_state_t *state, const char *s) { 121 | sol_object_t *res = sol_alloc_object(state); 122 | res->type = SOL_STRING; 123 | res->str = strdup(s); 124 | if(!res->str) { 125 | sol_obj_free(res); 126 | sol_set_error(state, state->OutOfMemory); 127 | return sol_incref(state->None); 128 | } 129 | res->ops = &(state->StringOps); 130 | sol_init_object(state, res); 131 | return res; 132 | } 133 | 134 | int sol_string_cmp(sol_state_t *state, sol_object_t *str, const char *s) { 135 | return strcmp(str->str, s); 136 | } 137 | 138 | sol_object_t *sol_string_concat(sol_state_t *state, sol_object_t *a, sol_object_t *b) { 139 | sol_object_t *res, *sa = sol_cast_string(state, a), *sb = sol_cast_string(state, b); 140 | int n = strlen(sa->str) + strlen(sb->str) + 1; 141 | char *s = malloc(n); 142 | res = sol_new_string(state, strncat(strncpy(s, a->str, n), b->str, n)); 143 | sol_obj_free(sa); 144 | sol_obj_free(sb); 145 | free(s); 146 | return res; 147 | } 148 | 149 | sol_object_t *sol_string_concat_cstr(sol_state_t *state, sol_object_t *a, char *s) { 150 | sol_object_t *b = sol_new_string(state, s); 151 | sol_object_t *res = sol_string_concat(state, a, b); 152 | sol_obj_free(b); 153 | return res; 154 | } 155 | 156 | sol_object_t *sol_f_str_free(sol_state_t *state, sol_object_t *obj) { 157 | free(obj->str); 158 | return obj; 159 | } 160 | 161 | sol_object_t *sol_new_list(sol_state_t *state) { 162 | sol_object_t *res = sol_alloc_object(state); 163 | res->type = SOL_LIST; 164 | res->seq = dsl_seq_new_array(NULL, &(state->obfuncs)); 165 | res->ops = &(state->ListOps); 166 | sol_init_object(state, res); 167 | return res; 168 | } 169 | 170 | sol_object_t *sol_list_from_seq(sol_state_t *state, dsl_seq *seq) { 171 | sol_object_t *res = sol_alloc_object(state); 172 | res->type = SOL_LIST; 173 | res->seq = seq; 174 | res->ops = &(state->ListOps); 175 | sol_init_object(state, res); 176 | return res; 177 | } 178 | 179 | int sol_list_len(sol_state_t *state, sol_object_t *list) { 180 | return dsl_seq_len(list->seq); 181 | } 182 | 183 | sol_object_t *sol_list_sublist(sol_state_t *state, sol_object_t *list, int idx) { 184 | int i = 0; 185 | dsl_seq *subl; 186 | if(idx < 0) { 187 | return sol_set_error_string(state, "Create sublist at negative index"); 188 | } 189 | subl = dsl_seq_copy(list->seq); 190 | for(i = 0; i < idx; i++) { 191 | dsl_seq_delete(subl, 0); 192 | } 193 | return sol_list_from_seq(state, subl); 194 | } 195 | 196 | sol_object_t *sol_list_get_index(sol_state_t *state, sol_object_t *list, int idx) { 197 | if(idx < 0 || idx >= dsl_seq_len(list->seq)) { 198 | return sol_incref(state->None); 199 | } 200 | return sol_incref(AS_OBJ(dsl_seq_get(list->seq, idx))); 201 | } 202 | 203 | void sol_list_set_index(sol_state_t *state, sol_object_t *list, int idx, sol_object_t *obj) { 204 | if(idx < 0 || idx >= dsl_seq_len(list->seq)) { 205 | return; 206 | } 207 | dsl_seq_set(list->seq, idx, obj); 208 | } 209 | 210 | void sol_list_insert(sol_state_t *state, sol_object_t *list, int idx, sol_object_t *obj) { 211 | if(idx < 0 || idx > dsl_seq_len(list->seq)) { 212 | return; 213 | } 214 | dsl_seq_insert(list->seq, idx, obj); 215 | } 216 | 217 | sol_object_t *sol_list_remove(sol_state_t *state, sol_object_t *list, int idx) { 218 | if(idx < 0 || idx >= dsl_seq_len(list->seq)) { 219 | return sol_incref(state->None); 220 | } 221 | return dsl_seq_remove(list->seq, idx); 222 | } 223 | 224 | sol_object_t *sol_list_copy(sol_state_t *state, sol_object_t *list) { 225 | return sol_list_from_seq(state, dsl_seq_copy(list->seq)); 226 | } 227 | 228 | sol_object_t *sol_list_truncate(sol_state_t *state, sol_object_t *list, int len) { 229 | dsl_seq *newseq = dsl_seq_copy(list->seq); 230 | dsl_seq_iter *iter = dsl_new_seq_iter(newseq); 231 | int pos = dsl_seq_iter_seek(iter, len); 232 | int sz = dsl_seq_len(newseq); 233 | int i; 234 | if(pos >= len) { 235 | for(i = 0; i < sz - pos; i++) { 236 | dsl_seq_iter_delete_at(iter); 237 | } 238 | } 239 | dsl_free_seq_iter(iter); 240 | return sol_list_from_seq(state, newseq); 241 | } 242 | 243 | void sol_list_append(sol_state_t *state, sol_object_t *dest, sol_object_t *src) { 244 | dsl_seq *oldseq = dest->seq; 245 | dest->seq = dsl_seq_append(dest->seq, src->seq); 246 | dsl_free_seq(oldseq); 247 | } 248 | 249 | sol_object_t *sol_f_list_free(sol_state_t *state, sol_object_t *list) { 250 | dsl_free_seq(list->seq); 251 | return list; 252 | } 253 | 254 | /*int sol_test_cycle(sol_state_t *state, sol_object_t *seq) { 255 | sol_object_t *seen[1024]={}; 256 | sol_object_t *cur = seq, **item; 257 | while(cur) { 258 | item = seen; 259 | while(*item) { 260 | if(*item == cur) return 1; 261 | item++; 262 | } 263 | *item = cur; 264 | if(sol_is_list(seq)) { 265 | cur = cur->lnext; 266 | } else { 267 | cur = cur->mnext; 268 | } 269 | } 270 | return 0; 271 | } 272 | 273 | int sol_validate_list(sol_state_t *state, sol_object_t *list) { 274 | sol_object_t *cur = list; 275 | int i = 0; 276 | char msg[128]; 277 | while(cur) { 278 | if(!sol_is_list(cur)) { 279 | snprintf(msg, 128, "Node at index %d not a list node", i); 280 | sol_obj_free(sol_set_error_string(state, msg)); 281 | return 1; 282 | } 283 | /*if(cur->lnext && !cur->lvalue) { 284 | snprintf(msg, 128, "Node at index %d has a next node but NULL value", i); 285 | sol_obj_free(sol_set_error_string(state, msg)); 286 | return 1; 287 | }*//* 288 | cur = cur->lnext; 289 | i++; 290 | } 291 | if(sol_test_cycle(state, list)) { 292 | snprintf(msg, 128, "Cycle detected"); 293 | sol_obj_free(sol_set_error_string(state, msg)); 294 | return 1; 295 | } 296 | return 0; 297 | }*/ 298 | 299 | sol_object_t *sol_new_map(sol_state_t *state) { 300 | sol_object_t *map = sol_alloc_object(state); 301 | map->type = SOL_MAP; 302 | map->ops = &(state->MapOps); 303 | map->seq = dsl_seq_new_array(NULL, &(state->obfuncs)); 304 | sol_init_object(state, map); 305 | return map; 306 | } 307 | 308 | sol_object_t *sol_map_from_seq(sol_state_t *state, dsl_seq *seq) { 309 | sol_object_t *map = sol_alloc_object(state); 310 | if(sol_has_error(state)) { 311 | return sol_incref(state->None); 312 | } 313 | map->type = SOL_MAP; 314 | map->ops = &(state->MapOps); 315 | map->seq = seq; 316 | return map; 317 | } 318 | 319 | int sol_map_len(sol_state_t *state, sol_object_t *map) { 320 | return dsl_seq_len(map->seq); 321 | } 322 | 323 | sol_object_t *sol_map_mcell_index(sol_state_t *state, sol_object_t *map, int index) { 324 | sol_object_t *res = dsl_seq_get(map->seq, index); 325 | if(res) { 326 | return sol_incref(res); 327 | } 328 | return sol_incref(state->None); 329 | } 330 | 331 | sol_object_t *sol_map_mcell(sol_state_t *state, sol_object_t *map, sol_object_t *key) { 332 | sol_object_t *list, *cmp, *icmp, *res = NULL; 333 | dsl_seq_iter *iter; 334 | if(!sol_is_map(map)) { 335 | printf("WARNING: Attempt to index non-map as map\n"); 336 | return sol_incref(state->None); 337 | } 338 | list = sol_new_list(state); 339 | iter = dsl_new_seq_iter(map->seq); 340 | if(sol_has_error(state)) { 341 | dsl_free_seq_iter(iter); 342 | sol_obj_free(list); 343 | return sol_incref(state->None); 344 | } 345 | sol_list_insert(state, list, 0, state->None); 346 | sol_list_insert(state, list, 1, key); 347 | while(!res && !dsl_seq_iter_is_invalid(iter)) { 348 | sol_list_set_index(state, list, 0, AS_OBJ(dsl_seq_iter_at(iter))->key); 349 | cmp = CALL_METHOD(state, AS_OBJ(dsl_seq_iter_at(iter))->key, cmp, list); 350 | if(sol_has_error(state)) { 351 | sol_obj_free(cmp); 352 | sol_clear_error(state); 353 | continue; 354 | } 355 | icmp = sol_cast_int(state, cmp); 356 | sol_obj_free(cmp); 357 | if(icmp->ival == 0) { 358 | res = AS_OBJ(dsl_seq_iter_at(iter)); 359 | } 360 | sol_obj_free(icmp); 361 | dsl_seq_iter_next(iter); 362 | } 363 | dsl_free_seq_iter(iter); 364 | sol_obj_free(list); 365 | if(res) { 366 | return sol_incref(res); 367 | } 368 | return sol_incref(state->None); 369 | } 370 | 371 | int sol_map_has(sol_state_t *state, sol_object_t *map, sol_object_t *key) { 372 | sol_object_t *mcell = sol_map_mcell(state, map, key); 373 | int res = !sol_is_none(state, mcell); 374 | sol_decref(mcell); 375 | return res; 376 | } 377 | 378 | sol_object_t *sol_map_get(sol_state_t *state, sol_object_t *map, sol_object_t *key) { 379 | sol_object_t *mcell = sol_map_mcell(state, map, key), *ret; 380 | if(sol_is_none(state, mcell)) { 381 | ret = mcell; 382 | } else { 383 | ret = mcell->val; 384 | } 385 | sol_obj_free(mcell); 386 | return sol_incref(ret); 387 | } 388 | 389 | sol_object_t *sol_map_get_name(sol_state_t *state, sol_object_t *map, char *name) { 390 | sol_object_t *key = sol_new_buffer(state, name, strlen(name), OWN_NONE, NULL, NULL); 391 | sol_object_t *res = sol_map_get(state, map, key); 392 | sol_obj_free(key); 393 | return res; 394 | } 395 | 396 | void sol_map_set(sol_state_t *state, sol_object_t *map, sol_object_t *key, sol_object_t *val) { 397 | sol_object_t *mcell = sol_map_mcell(state, map, key), *newcell, *temp; 398 | if(sol_is_none(state, val)) { 399 | if(!sol_is_none(state, mcell)) { 400 | // XXX hacky 401 | dsl_seq_iter *iter = dsl_new_seq_iter(map->seq); 402 | while(!dsl_seq_iter_is_invalid(iter)) { 403 | if(mcell == dsl_seq_iter_at(iter)) { 404 | dsl_seq_iter_delete_at(iter); 405 | break; 406 | } 407 | dsl_seq_iter_next(iter); 408 | } 409 | } 410 | return; 411 | } 412 | if(sol_is_none(state, mcell)) { 413 | newcell = sol_alloc_object(state); 414 | newcell->type = SOL_MCELL; 415 | newcell->ops = &(state->MCellOps); 416 | newcell->key = sol_incref(key); 417 | newcell->val = sol_incref(val); 418 | dsl_seq_insert(map->seq, 0, newcell); 419 | sol_obj_free(newcell); 420 | } else { 421 | temp = mcell->val; 422 | mcell->val = sol_incref(val); 423 | sol_obj_free(temp); 424 | } 425 | sol_obj_free(mcell); 426 | } 427 | 428 | void sol_map_set_name(sol_state_t *state, sol_object_t *map, char *name, sol_object_t *val) { 429 | sol_object_t *key = sol_new_buffer(state, name, strlen(name), OWN_NONE, NULL, NULL); 430 | sol_map_set(state, map, key, val); 431 | sol_obj_free(key); 432 | } 433 | 434 | void sol_map_set_existing(sol_state_t *state, sol_object_t *map, sol_object_t *key, sol_object_t *val) { 435 | sol_object_t *mcell = sol_map_mcell(state, map, key), *temp; 436 | if(!sol_is_none(state, mcell)) { 437 | temp = mcell->val; 438 | mcell->val = sol_incref(val); 439 | sol_obj_free(temp); 440 | } 441 | sol_obj_free(mcell); 442 | } 443 | 444 | sol_object_t *sol_map_copy(sol_state_t *state, sol_object_t *map) { 445 | return sol_map_from_seq(state, dsl_seq_copy(map->seq)); 446 | } 447 | 448 | void sol_map_merge(sol_state_t *state, sol_object_t *dest, sol_object_t *src) { 449 | dsl_seq_iter *iter = dsl_new_seq_iter(src->seq); 450 | while(!dsl_seq_iter_is_invalid(iter)) { 451 | sol_map_set(state, dest, AS_OBJ(dsl_seq_iter_at(iter))->key, AS_OBJ(dsl_seq_iter_at(iter))->val); 452 | dsl_seq_iter_next(iter); 453 | } 454 | dsl_free_seq_iter(iter); 455 | } 456 | 457 | void sol_map_merge_existing(sol_state_t *state, sol_object_t *dest, sol_object_t *src) { 458 | dsl_seq_iter *iter = dsl_new_seq_iter(src->seq); 459 | while(!dsl_seq_iter_is_invalid(iter)) { 460 | sol_map_set_existing(state, dest, AS_OBJ(dsl_seq_iter_at(iter))->key, AS_OBJ(dsl_seq_iter_at(iter))->val); 461 | dsl_seq_iter_next(iter); 462 | } 463 | dsl_free_seq_iter(iter); 464 | } 465 | 466 | void sol_map_invert(sol_state_t *state, sol_object_t *map) { 467 | dsl_seq *pairs = dsl_seq_copy(map->seq); 468 | dsl_seq_iter *iter = dsl_new_seq_iter(pairs); 469 | sol_object_t *mcell; 470 | while(!dsl_seq_iter_is_invalid(iter)) { 471 | mcell = dsl_seq_iter_at(iter); 472 | sol_map_set(state, map, mcell->val, mcell->key); 473 | dsl_seq_iter_next(iter); 474 | } 475 | dsl_free_seq_iter(iter); 476 | dsl_free_seq(pairs); 477 | } 478 | 479 | sol_object_t *sol_f_map_free(sol_state_t *state, sol_object_t *map) { 480 | dsl_free_seq(map->seq); 481 | return map; 482 | } 483 | 484 | sol_object_t *sol_f_mcell_free(sol_state_t *state, sol_object_t *mcell) { 485 | if(mcell->key) { 486 | sol_obj_free(mcell->key); 487 | } else { 488 | printf("WARNING: Freed mcell with NULL key\n"); 489 | } 490 | if(mcell->val) { 491 | sol_obj_free(mcell->val); 492 | } else { 493 | printf("WARNING: Freed mcell with NULL value\n"); 494 | } 495 | return mcell; 496 | } 497 | 498 | /*int sol_validate_map(sol_state_t *state, sol_object_t *map) { 499 | sol_object_t *cur = map; 500 | int i = 0; 501 | char msg[128]; 502 | while(cur) { 503 | if(!sol_is_map(cur)) { 504 | snprintf(msg, 128, "Node at index %d not a map node", i); 505 | sol_obj_free(sol_set_error_string(state, msg)); 506 | return 1; 507 | } 508 | if(cur->mnext && (!cur->mkey || !cur->mval)) { 509 | snprintf(msg, 128, "Node at index %d has a next node but NULL key or value", i); 510 | sol_obj_free(sol_set_error_string(state, msg)); 511 | return 1; 512 | } 513 | cur = cur->mnext; 514 | i++; 515 | } 516 | return 0; 517 | }*/ 518 | 519 | sol_object_t *sol_new_cfunc(sol_state_t *state, sol_cfunc_t cfunc, char *name) { 520 | sol_object_t *res = sol_alloc_object(state); 521 | res->type = SOL_CFUNCTION; 522 | res->ops = &(state->CFuncOps); 523 | res->cfunc = cfunc; 524 | res->cfname = name ? strdup(name) : NULL; 525 | sol_init_object(state, res); 526 | return res; 527 | } 528 | 529 | sol_object_t *sol_new_cmacro(sol_state_t *state, sol_cfunc_t cfunc, char *name) { 530 | sol_object_t *res = sol_alloc_object(state); 531 | res->type = SOL_CMACRO; 532 | res->ops = &(state->CMacroOps); 533 | res->cfunc = cfunc; 534 | res->cfname = name ? strdup(name) : NULL; 535 | sol_init_object(state, res); 536 | return res; 537 | } 538 | 539 | sol_object_t *sol_f_cfunc_free(sol_state_t *state, sol_object_t *cfunc) { 540 | free(cfunc->cfname); 541 | return cfunc; 542 | } 543 | 544 | sol_object_t *sol_new_cdata(sol_state_t *state, void *cdata, sol_ops_t *ops) { 545 | sol_object_t *res = sol_alloc_object(state); 546 | res->type = SOL_CDATA; 547 | res->ops = ops; 548 | res->cdata = cdata; 549 | sol_init_object(state, res); 550 | return res; 551 | } 552 | 553 | sol_object_t *sol_f_astnode_free(sol_state_t *state, sol_object_t *node) { 554 | switch(node->type) { 555 | case SOL_STMT: 556 | st_free((stmt_node *) node->node); 557 | break; 558 | 559 | case SOL_EXPR: 560 | ex_free((expr_node *) node->node); 561 | break; 562 | } 563 | return node; 564 | } 565 | 566 | sol_object_t *sol_new_buffer(sol_state_t *state, void *buffer, ssize_t sz, sol_owntype_t own, sol_freefunc_t freef, sol_movefunc_t movef) { 567 | sol_object_t *res = sol_alloc_object(state); 568 | res->type = SOL_BUFFER; 569 | res->ops = &(state->BufferOps); 570 | res->buffer = buffer; 571 | res->sz = sz; 572 | res->own = own; 573 | res->freef = freef; 574 | res->movef = movef; 575 | sol_init_object(state, res); 576 | return res; 577 | } 578 | 579 | int sol_buffer_cmp(sol_state_t *state, sol_object_t *buf, const char *s) { 580 | size_t len = strlen(s); 581 | if(buf->sz != -1 && buf->sz < len) len = buf->sz; 582 | return memcmp(buf->buffer, s, len); 583 | } 584 | 585 | sol_object_t *sol_buffer_concat(sol_state_t *state, sol_object_t *a, sol_object_t *b) { 586 | sol_object_t *ba = sol_cast_buffer(state, a), *bb = sol_cast_buffer(state, b); 587 | char *buf; 588 | size_t total; 589 | if(ba->sz < 0 || bb->sz < 0) { 590 | sol_obj_free(ba); 591 | sol_obj_free(bb); 592 | return sol_set_error_string(state, "Concatenate unsized buffer"); 593 | } 594 | total = ba->sz + bb->sz; 595 | buf = malloc(sizeof(char) * total); 596 | if(!buf) { 597 | sol_obj_free(ba); 598 | sol_obj_free(bb); 599 | return sol_incref(state->OutOfMemory); 600 | } 601 | memcpy(buf, ba->buffer, ba->sz); 602 | memcpy(buf + ba->sz, bb->buffer, bb->sz); 603 | sol_obj_free(ba); 604 | sol_obj_free(bb); 605 | return sol_new_buffer(state, buf, total, OWN_FREE, NULL, NULL); 606 | } 607 | 608 | sol_object_t *sol_buffer_concat_cstr(sol_state_t *state, sol_object_t *a, char *b) { 609 | sol_object_t *buf = sol_new_buffer(state, b, strlen(b), OWN_NONE, NULL, NULL); 610 | sol_object_t *res = sol_buffer_concat(state, a, buf); 611 | sol_obj_free(buf); 612 | return res; 613 | } 614 | 615 | char *sol_buffer_strdup(sol_object_t *a) { 616 | char *b; 617 | if(a->sz < 0) return NULL; 618 | b = malloc(a->sz + 1); 619 | if(!b) return NULL; 620 | strncpy(b, a->buffer, a->sz); 621 | b[a->sz] = '\0'; 622 | return b; 623 | } 624 | 625 | sol_object_t *sol_f_buffer_free(sol_state_t *state, sol_object_t *buf) { 626 | switch(buf->own) { 627 | case OWN_FREE: 628 | free(buf->buffer); 629 | break; 630 | 631 | case OWN_CALLF: 632 | if(buf->freef) buf->freef(buf->buffer, buf->sz); 633 | break; 634 | } 635 | return buf; 636 | } 637 | 638 | sol_object_t *sol_new_dylib(sol_state_t *state, void *handle) { 639 | sol_object_t *res = sol_alloc_object(state); 640 | res->type = SOL_DYLIB; 641 | res->ops = &(state->DyLibOps); 642 | res->dlhandle = handle; 643 | sol_init_object(state, res); 644 | return res; 645 | } 646 | 647 | sol_object_t *sol_f_dylib_free(sol_state_t *state, sol_object_t *dylib) { 648 | dlclose(dylib->dlhandle); 649 | return dylib; 650 | } 651 | 652 | sol_object_t *sol_new_dysym(sol_state_t *state, void *sym, dsl_seq *argtp, sol_buftype_t rettp) { 653 | sol_object_t *res = sol_alloc_object(state); 654 | res->type = SOL_DYSYM; 655 | res->ops = &(state->DySymOps); 656 | res->dlsym = sym; 657 | if(argtp) { 658 | res->argtp = dsl_seq_copy(argtp); 659 | } else { 660 | res->argtp = dsl_seq_new_array(NULL, &(state->obfuncs)); 661 | } 662 | res->rettp = rettp; 663 | sol_init_object(state, res); 664 | return res; 665 | } 666 | 667 | sol_object_t *sol_new_stream(sol_state_t *state, FILE *stream, sol_modes_t modes) { 668 | sol_object_t *res = sol_alloc_object(state); 669 | res->type = SOL_STREAM; 670 | res->ops = &(state->StreamOps); 671 | res->stream = stream; 672 | res->modes = modes; 673 | sol_init_object(state, res); 674 | return res; 675 | } 676 | 677 | size_t sol_stream_printf(sol_state_t *state, sol_object_t *stream, const char *fmt, ...) { 678 | va_list va; 679 | size_t res; 680 | if(!(stream->modes & MODE_WRITE)) { 681 | if(state) { 682 | sol_obj_free(sol_set_error_string(state, "Write to non-writable stream")); 683 | } 684 | return 0; 685 | } 686 | va_start(va, fmt); 687 | //res = vfprintf(stream->stream, fmt, va); 688 | res = vprintf(fmt, va); 689 | va_end(va); 690 | return res; 691 | } 692 | 693 | size_t sol_stream_vprintf(sol_state_t *state, sol_object_t *stream, const char *fmt, va_list va) { 694 | if(!(stream->modes & MODE_WRITE)) { 695 | if(state) { 696 | sol_obj_free(sol_set_error_string(state, "Write to non-writable stream")); 697 | } 698 | return 0; 699 | } 700 | //return vfprintf(stream->stream, fmt, va); 701 | return vprintf(fmt, va); 702 | } 703 | 704 | size_t sol_stream_scanf(sol_state_t *state, sol_object_t *stream, const char *fmt, ...) { 705 | va_list va; 706 | size_t res; 707 | if(!(stream->modes & MODE_READ)) { 708 | if(state) { 709 | sol_obj_free(sol_set_error_string(state, "Read from non-readable stream")); 710 | } 711 | return 0; 712 | } 713 | va_start(va, fmt); 714 | res = vfscanf(stream->stream, fmt, va); 715 | va_end(va); 716 | return res; 717 | } 718 | 719 | size_t sol_stream_fread(sol_state_t *state, sol_object_t *stream, char *buffer, size_t sz, size_t memb) { 720 | if(!(stream->modes & MODE_READ)) { 721 | if(state) { 722 | sol_obj_free(sol_set_error_string(state, "Read from non-readable stream")); 723 | } 724 | return 0; 725 | } 726 | return fread(buffer, sz, memb, stream->stream); 727 | } 728 | 729 | size_t sol_stream_fwrite(sol_state_t *state, sol_object_t *stream, char *buffer, size_t sz, size_t memb) { 730 | if(!(stream->modes & MODE_WRITE)) { 731 | if(state) { 732 | sol_obj_free(sol_set_error_string(state, "Write to non-writable stream")); 733 | } 734 | return 0; 735 | } 736 | return fwrite(buffer, sz, memb, stream->stream); 737 | } 738 | 739 | char *sol_stream_fgets(sol_state_t *state, sol_object_t *stream, char *buffer, size_t sz) { 740 | if(!(stream->modes & MODE_READ)) { 741 | if(state) { 742 | sol_obj_free(sol_set_error_string(state, "Read from non-readable stream")); 743 | } 744 | return NULL; 745 | } 746 | return fgets(buffer, sz, stream->stream); 747 | } 748 | 749 | int sol_stream_fputc(sol_state_t *state, sol_object_t *stream, int ch) { 750 | if(!(stream->modes & MODE_WRITE)) { 751 | if(state) { 752 | sol_obj_free(sol_set_error_string(state, "Write to non-writable stream")); 753 | } 754 | return 0; 755 | } 756 | return fputc(ch, stream->stream); 757 | } 758 | 759 | int sol_stream_feof(sol_state_t *state, sol_object_t *stream) { 760 | return feof(stream->stream); 761 | } 762 | 763 | int sol_stream_ferror(sol_state_t *state, sol_object_t *stream) { 764 | return ferror(stream->stream); 765 | } 766 | 767 | int sol_stream_fseek(sol_state_t *state, sol_object_t *stream, long offset, int whence) { 768 | return fseek(stream->stream, offset, whence); 769 | } 770 | 771 | long sol_stream_ftell(sol_state_t *state, sol_object_t *stream) { 772 | return ftell(stream->stream); 773 | } 774 | 775 | int sol_stream_fflush(sol_state_t *state, sol_object_t *stream) { 776 | return fflush(stream->stream); 777 | } 778 | 779 | sol_object_t *sol_f_stream_free(sol_state_t *state, sol_object_t *stream) { 780 | //printf("IO: Closing open file\n"); 781 | fclose(stream->stream); 782 | return stream; 783 | } 784 | -------------------------------------------------------------------------------- /old-sol-gdb.py: -------------------------------------------------------------------------------- 1 | # GDB Sol extensions 2 | 3 | import gdb.printing 4 | import sys 5 | import traceback 6 | 7 | class _CatchExceptions(object): 8 | def __init__(self): 9 | self.excs = [] 10 | def print_eidx(self, idx): 11 | traceback.print_exception(*self.excs[idx]) 12 | def __enter__(self): 13 | pass 14 | def __exit__(self, t, v, tb): 15 | if t is not None: 16 | self.excs.append((t, v, tb)) 17 | guard = _CatchExceptions() 18 | 19 | class DslArray(gdb.Function): 20 | '''Returns the (DSL_DATATYPE[]) array base of a dsl_array or dsl_seq (if it wraps an array), else NULL''' 21 | 22 | def __init__(self): 23 | super(DslArray, self).__init__('dsl_array') 24 | 25 | def invoke(self, val): 26 | with guard: 27 | stype = str(val.type) 28 | tp = gdb.lookup_type('sol_object_t').pointer() 29 | if str(val.type).startswith('dsl_seq'): 30 | if str(val['type']) != 'DSL_SEQ_ARRAY': 31 | return gdb.Value(0).cast(tp.pointer()) 32 | val = val['array'] 33 | if str(val.type).startswith('dsl_array'): 34 | return val['data'].dereference().cast(tp.array(val['len'])) 35 | return gdb.Value(0).cast(tp.pointer()) 36 | DslArray.instance = DslArray() 37 | 38 | class DslLen(gdb.Function): 39 | '''Returns the length of a DSL sequence''' 40 | 41 | def __init__(self): 42 | super(DslLen, self).__init__('dsl_array') 43 | 44 | def invoke(self, val): 45 | with guard: 46 | stype = str(val.type) 47 | if stype.startswith('dsl_seq'): 48 | return int(gdb.lookup_symbol('dsl_seq_len')[0].value()(val)) 49 | if stype.startswith('dsl_array'): 50 | return int(gdb.lookup_symbol('dsl_array_len')[0].value()(val)) 51 | if stype.startswith('dsl_list'): 52 | return int(gdb.lookup_symbol('dsl_list_len')[0].value()(val)) 53 | return -1 54 | DslLen.instance = DslLen() 55 | 56 | class SolObj(gdb.Function): 57 | '''Casts the argument to a sol_object_t *''' 58 | 59 | def __init__(self): 60 | super(SolObj, self).__init__('sol_obj') 61 | 62 | def invoke(self, val): 63 | with guard: 64 | return val.cast(gdb.lookup_type('sol_object_t').pointer()) 65 | SolObj.instance = SolObj() 66 | 67 | class SolObjectPrettyPrinter(object): 68 | STYPE_TO_DISPHINT = { 69 | 'SOL_SINGLET': 'string', 70 | 'SOL_INTEGER': 'number', 71 | 'SOL_FLOAT': 'number', 72 | 'SOL_STRING': 'string', 73 | 'SOL_LIST': 'array', 74 | 'SOL_MAP': 'map', 75 | 'SOL_MCELL': 'map', 76 | 'SOL_FUNCTION': 'function', 77 | 'SOL_CFUNCTION': 'function', 78 | 'SOL_STMT': 'syntax', 79 | 'SOL_EXPR': 'syntax', 80 | } 81 | 82 | def __init__(self, obj): 83 | self.obj = obj 84 | 85 | def display_hint(self): 86 | return self.STYPE_TO_DISPHINT.get(str(self.obj['type']), 'string') 87 | 88 | def to_string(self): 89 | with guard: 90 | return getattr(self, 'str_'+str(self.obj['type']), self.str_default)(self.obj) 91 | 92 | def str_default(self, obj): 93 | return ''%(obj, str(obj['type'])) 94 | 95 | def str_SOL_SINGLET(self, obj): 96 | return obj['str'] 97 | 98 | def str_SOL_INTEGER(self, obj): 99 | return str(obj['ival']) 100 | 101 | def str_SOL_FLOAT(self, obj): 102 | return str(obj['fval']) 103 | 104 | def str_SOL_STRING(self, obj): 105 | return obj['str'] 106 | 107 | def str_SOL_LIST(self, obj): 108 | return '[List len=%d]'%(DslLen.instance.invoke(obj['seq']),) 109 | 110 | def str_SOL_MAP(self, obj): 111 | return '{Map len=%d}'%(DslLen.instance.invoke(obj['seq']),) 112 | 113 | def str_SOL_MCELL(self, obj): 114 | return '<{[%s] = %s}>'%(SolObjectPrettyPrinter(obj['key']).to_string(), SolObjectPrettyPrinter(obj['val']).to_string()) 115 | 116 | def str_SOL_FUNCTION(self, obj): 117 | return ''%(obj['fname'],) 118 | 119 | def str_SOL_CFUNCTION(self, obj): 120 | return ''%(obj['cfunc'],) 121 | 122 | def str_SOL_STMT(self, obj): 123 | return str(obj['node'].cast(gdb.lookup_type('stmt_node').pointer())['type']) 124 | 125 | def str_SOL_EXPR(self, obj): 126 | return str(obj['node'].cast(gdb.lookup_type('expr_node').pointer())['type']) 127 | 128 | def children(self): 129 | with guard: 130 | stype = str(self.obj['type']) 131 | if stype in ('SOL_LIST', 'SOL_MAP'): 132 | if str(self.obj['seq']['type']) == 'DSL_SEQ_ARRAY': 133 | tp = gdb.lookup_type('sol_object_t').pointer() 134 | lseq = DslLen.instance.invoke(self.obj['seq']) 135 | arr = self.obj['seq']['array']['data'].dereference().cast(tp.array(lseq)) 136 | if stype == 'SOL_LIST': 137 | return [(str(i), arr[i]) for i in range(lseq)] 138 | else: 139 | return sum([[(str(i)+'k', arr[i]['key']), (str(i)+'v', arr[i]['val'])] for i in range(lseq)], []) 140 | if stype == 'SOL_FUNCTION': 141 | return [('stmt', self.obj['func'].cast(gdb.lookup_type('stmt_node').pointer))] 142 | return [] 143 | 144 | @classmethod 145 | def check_printable(cls, obj): 146 | with guard: 147 | stype = str(obj.type) 148 | if stype.startswith('sol_object_t') or stype.startswith('struct sol_tag_object_t'): 149 | return cls(obj) 150 | return None 151 | 152 | # pp = gdb.printing.RegexpCollectionPrettyPrinter('Sol') 153 | # pp.add_printer('sol_object_t', '^sol_object_t.*$', SolObjectPrettyPrinter) 154 | # pp.add_printer('sol_tag_object_t', '^struct sol_tag_object_t.*$', SolObjectPrettyPrinter) 155 | # gdb.printing.register_pretty_printer(gdb.current_objfile(), pp) 156 | gdb.pretty_printers.append(SolObjectPrettyPrinter.check_printable) 157 | 158 | print('Sol extensions loaded!') 159 | -------------------------------------------------------------------------------- /parser.tab.h: -------------------------------------------------------------------------------- 1 | /* A Bison parser, made by GNU Bison 3.0.4. */ 2 | 3 | /* Bison interface for Yacc-like parsers in C 4 | 5 | Copyright (C) 1984, 1989-1990, 2000-2015 Free Software Foundation, Inc. 6 | 7 | This program is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . */ 19 | 20 | /* As a special exception, you may create a larger work that contains 21 | part or all of the Bison parser skeleton and distribute that work 22 | under terms of your choice, so long as that work isn't itself a 23 | parser generator using the skeleton or a modified version thereof 24 | as a parser skeleton. Alternatively, if you modify or redistribute 25 | the parser skeleton itself, you may (at your option) remove this 26 | special exception, which will cause the skeleton and the resulting 27 | Bison output files to be licensed under the GNU General Public 28 | License without this special exception. 29 | 30 | This special exception was added by the Free Software Foundation in 31 | version 2.2 of Bison. */ 32 | 33 | #ifndef YY_YY_PARSER_TAB_H_INCLUDED 34 | # define YY_YY_PARSER_TAB_H_INCLUDED 35 | /* Debug traces. */ 36 | #ifndef YYDEBUG 37 | # define YYDEBUG 1 38 | #endif 39 | #if YYDEBUG 40 | extern int yydebug; 41 | #endif 42 | 43 | /* Token type. */ 44 | #ifndef YYTOKENTYPE 45 | # define YYTOKENTYPE 46 | enum yytokentype 47 | { 48 | IF = 258, 49 | THEN = 259, 50 | ELSEIF = 260, 51 | ELSE = 261, 52 | WHILE = 262, 53 | FOR = 263, 54 | IN = 264, 55 | DO = 265, 56 | FUNC = 266, 57 | MACRO = 267, 58 | LAMBDA = 268, 59 | RETURN = 269, 60 | BREAK = 270, 61 | CONTINUE = 271, 62 | END = 272, 63 | NONE = 273, 64 | IDENT = 274, 65 | INT = 275, 66 | FLOAT = 276, 67 | STRING = 277, 68 | PLUS = 278, 69 | MINUS = 279, 70 | STAR = 280, 71 | SLASH = 281, 72 | PERCENT = 282, 73 | DSTAR = 283, 74 | BAND = 284, 75 | BOR = 285, 76 | BXOR = 286, 77 | BNOT = 287, 78 | LAND = 288, 79 | LOR = 289, 80 | LNOT = 290, 81 | ASSIGN = 291, 82 | ASSIGNPLUS = 292, 83 | ASSIGNMINUS = 293, 84 | ASSIGNSTAR = 294, 85 | ASSIGNSLASH = 295, 86 | ASSIGNDSTAR = 296, 87 | ASSIGNBAND = 297, 88 | ASSIGNBOR = 298, 89 | ASSIGNBXOR = 299, 90 | EQUAL = 300, 91 | NEQUAL = 301, 92 | LESS = 302, 93 | GREATER = 303, 94 | LESSEQ = 304, 95 | GREATEREQ = 305, 96 | RSHIFT = 306, 97 | LSHIFT = 307, 98 | LBRACE = 308, 99 | RBRACE = 309, 100 | BLPAREN = 310, 101 | LPAREN = 311, 102 | RPAREN = 312, 103 | LBRACKET = 313, 104 | RBRACKET = 314, 105 | DOT = 315, 106 | COLON = 316, 107 | SEMICOLON = 317, 108 | COMMA = 318, 109 | POUND = 319, 110 | TBANG = 320 111 | }; 112 | #endif 113 | 114 | /* Value type. */ 115 | #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED 116 | typedef int YYSTYPE; 117 | # define YYSTYPE_IS_TRIVIAL 1 118 | # define YYSTYPE_IS_DECLARED 1 119 | #endif 120 | 121 | /* Location type. */ 122 | #if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED 123 | typedef struct YYLTYPE YYLTYPE; 124 | struct YYLTYPE 125 | { 126 | int first_line; 127 | int first_column; 128 | int last_line; 129 | int last_column; 130 | }; 131 | # define YYLTYPE_IS_DECLARED 1 132 | # define YYLTYPE_IS_TRIVIAL 1 133 | #endif 134 | 135 | 136 | 137 | int yyparse (stmt_node **program); 138 | 139 | #endif /* !YY_YY_PARSER_TAB_H_INCLUDED */ 140 | -------------------------------------------------------------------------------- /programs/a.sol: -------------------------------------------------------------------------------- 1 | package:import('b') 2 | -------------------------------------------------------------------------------- /programs/b.sol: -------------------------------------------------------------------------------- 1 | package:import('a') 2 | -------------------------------------------------------------------------------- /programs/chip8.sol: -------------------------------------------------------------------------------- 1 | chr2hex_t = {} 2 | hex2chr_t = {} 3 | 4 | for i in range(10) do 5 | c = chr(ord('0') + i) 6 | chr2hex_t[c] = i 7 | hex2chr_t[i] = c 8 | end 9 | 10 | for i in [10, 11, 12, 13, 14, 15] do 11 | c = chr(ord('A') + i - 10) 12 | chr2hex_t[c] = i 13 | hex2chr_t[i] = c 14 | end 15 | 16 | func new_reg() 17 | return { 18 | V = [0] * 16, 19 | I = 0, 20 | IP = 512, 21 | ST = 0, 22 | DT = 0, 23 | } 24 | end 25 | 26 | func new_mem() 27 | return [ 28 | 240, 144, 144, 144, 240, -- 0 29 | 32, 96, 32, 32, 112, -- 1 30 | 240, 16, 240, 128, 240, -- 2 31 | 240, 16, 240, 16, 240, -- 3 32 | 144, 144, 240, 16, 16, -- 4 33 | 240, 128, 240, 16, 240, -- 5 34 | 240, 128, 240, 144, 240, -- 6 35 | 240, 16, 32, 64, 64, -- 7 36 | 240, 144, 240, 144, 240, -- 8 37 | 240, 144, 240, 16, 240, -- 9 38 | 240, 144, 240, 144, 144, -- A 39 | 224, 144, 224, 144, 224, -- B 40 | 240, 128, 128, 128, 240, -- C 41 | 224, 144, 144, 144, 224, -- D 42 | 240, 128, 240, 128, 240, -- E 43 | 240, 128, 240, 128, 128, -- F 44 | ] + [0] * 4016 45 | end 46 | 47 | func mem_int(mem, i, bytes) 48 | r = 0 49 | while (bytes > 0) && (i < 4096) do 50 | r = (r << 8) | mem[i] 51 | i += 1 52 | bytes -= 1 53 | end 54 | return r 55 | end 56 | 57 | func load_buffer(mem, addr, s) 58 | if None == addr then addr = 512 end 59 | idx = 0 60 | while (idx < s:size()) && (addr < 4096) do 61 | mem[addr] = s:get(buffer.type.byte, idx) 62 | addr += 1 63 | idx += 1 64 | end 65 | end 66 | 67 | func load_stream(mem, addr, str) 68 | load_buffer(mem, addr, str:read_buffer(io.ALL)) 69 | end 70 | 71 | func get_bit(v, i) 72 | return (v >> i) & 1 73 | end 74 | 75 | func new_disp(w, h) 76 | if w == None then w = 64 end 77 | if h == None then h = 32 end 78 | return { 79 | fb = [0] * (64 * 32), 80 | w = w, 81 | h = h, 82 | } 83 | end 84 | 85 | func disp_blit(b, x, y, v) 86 | idx = y * (b.w) + x 87 | changed = 0 88 | for bit in range(8) do 89 | if (idx >= 0) && (idx < (b.w) * (b.h)) then 90 | oldb = b.fb[idx] 91 | b.fb[idx] ^= if v & 128 then 1 else 0 end 92 | changed = changed || ((!oldb) && (b.fb[idx])) 93 | idx += 1 94 | v = v << 1 95 | end 96 | end 97 | return changed 98 | end 99 | 100 | func print_sprite(m, i, n) 101 | if None == n then n = 15 end 102 | fake_fb = new_disp(8, n) 103 | for idx in range(n) do 104 | disp_blit(fake_fb, 0, idx, m[i + idx]) 105 | end 106 | print(disp_render(fake_fb)) 107 | end 108 | 109 | NL = chr(10) 110 | CSI = chr(27) + '[' 111 | 112 | func clear_screen() 113 | io.stdout:write(CSI+'H'+CSI+'2J') 114 | end 115 | 116 | func disp_render(d) 117 | s = '' 118 | for row in range(d.h) do 119 | s = s + disp_render_row(d, row) + NL 120 | end 121 | return s 122 | end 123 | 124 | func disp_render_row(d, r) 125 | t = '' 126 | for col in range(d.w) do 127 | idx = r * (d.w) + col 128 | if d.fb[idx] then 129 | t += '*' 130 | else 131 | t += ' ' 132 | end 133 | end 134 | return t 135 | end 136 | 137 | func unused_syscall(state, n) 138 | state:log("unknown syscall " + tostring(n)) 139 | end 140 | 141 | func clear_disp(state, n) 142 | state.disp = new_disp() 143 | end 144 | 145 | func ret(state, n) 146 | state:jump(state:pop()) 147 | end 148 | 149 | func new_sys() 150 | sys = [unused_syscall] * 4096 151 | sys[224] = clear_disp 152 | sys[238] = ret 153 | return sys 154 | end 155 | 156 | func new_state() 157 | return { 158 | reg = new_reg(), 159 | mem = new_mem(), 160 | disp = new_disp(), 161 | sys = new_sys(), 162 | jumped = false, 163 | trace = {}, 164 | jump = func(self, ip) 165 | self.reg.IP = ip 166 | self.jumped = true 167 | end, 168 | skip = func(self) 169 | self.reg.IP += 2 170 | end, 171 | stack = [], 172 | push = func(self, v) 173 | self.stack:insert(#(self.stack), v) 174 | if (#self.stack) > 16 then 175 | self:log("stack overflow: " + tostring(self.stack)) 176 | end 177 | end, 178 | pop = func(self) 179 | if (#self.stack) == 0 then 180 | self:log("stack underflow") 181 | return 0 182 | end 183 | return self.stack:remove((#self.stack) - 1) 184 | end, 185 | keys = [0] * 16, 186 | buffer = [], 187 | log = func(self, msg) 188 | self.buffer:insert(#(self.buffer), msg) 189 | end, 190 | breaks = [], 191 | -- wchan = None 192 | } 193 | end 194 | 195 | func new_insn(n) 196 | return { 197 | nib = [(n >> 12) & 15, (n >> 8) & 15, (n >> 4) & 15, n & 15], 198 | byte = {high = (n >> 8) & 255, low = n & 255}, 199 | addr = n & 4095, 200 | n = n, 201 | } 202 | end 203 | 204 | func rand(n=0) 205 | n = ((n * 17) + 1569752) % 3707765549 206 | n = (n << 1) | (((n >> 3) & 1) ^ ((n >> 6) & 1)) 207 | n = (n << 1) | (((n >> 8) & 1) ^ ((n >> 5) & 1)) 208 | n = ((n * 7) + 67293512) % 3747882443 209 | n = (n << 1) | (((n >> 7) & 1) ^ ((n >> 2) & 1)) 210 | n = (n << 1) | (((n >> 4) & 1) ^ ((n >> 3) & 1)) 211 | return n 212 | end 213 | 214 | func seed(n) 215 | rand.closure.n = n 216 | end 217 | 218 | func bad_op(state, ins) 219 | state:log("bad insn: " + tostring(ins.n) + " " + tostring(ins.nib)) 220 | end 221 | 222 | insns = { 223 | [0] = func insn0(state, ins) 224 | state.sys[ins.addr](state, ins.addr) 225 | end, 226 | [1] = func insn1(state, ins) 227 | state:jump(ins.addr) 228 | end, 229 | [2] = func insn2(state, ins) 230 | state:push(state.reg.IP + 2) 231 | state:jump(ins.addr) 232 | end, 233 | [3] = func insn3(state, ins) 234 | if state.reg.V[ins.nib[1]] == ins.byte.low then 235 | state:skip() 236 | end 237 | end, 238 | [4] = func insn4(state, ins) 239 | if state.reg.V[ins.nib[1]] != ins.byte.low then 240 | state:skip() 241 | end 242 | end, 243 | [5] = func insn5(state, ins) 244 | if state.reg.V[ins.nib[1] ]== state.reg.V[ins.nib[2]] then 245 | state:skip() 246 | end 247 | end, 248 | [6] = func insn6(state, ins) 249 | state.reg.V[ins.nib[1]] = ins.byte.low 250 | end, 251 | [7] = func insn7(state, ins) 252 | state.reg.V[ins.nib[1]] = (state.reg.V[ins.nib[1]] + (ins.byte.low)) & 255 253 | end, 254 | [8] = func insn8(state, ins) 255 | { 256 | [0] = func insn8_0(state, ins) 257 | state.reg.V[ins.nib[1]] = state.reg.V[ins.nib[2]] 258 | end, 259 | [1] = func insn8_1(state, ins) 260 | state.reg.V[ins.nib[1]] |= state.reg.V[ins.nib[2]] 261 | end, 262 | [2] = func insn8_2(state, ins) 263 | state.reg.V[ins.nib[1]] &= state.reg.V[ins.nib[2]] 264 | end, 265 | [3] = func insn8_3(state, ins) 266 | state.reg.V[ins.nib[1]] ^= state.reg.V[ins.nib[2]] 267 | end, 268 | [4] = func insn8_4(state, ins) 269 | sum = state.reg.V[ins.nib[1]] + (state.reg.V[ins.nib[2]]) 270 | state.reg.V[ins.nib[1]] = sum & 255 271 | state.reg.V[15] = if (sum >> 8) then 1 else 0 end 272 | end, 273 | [5] = func insn8_5(state, ins) 274 | diff = state.reg.V[ins.nib[1]] - (state.reg.V[ins.nib[2]]) 275 | state.reg.V[ins.nib[1]] = diff & 255 276 | state.reg.V[15] = if (diff >> 8) then 1 else 0 end 277 | end, 278 | [6] = func insn8_6(state, ins) 279 | state.reg.V[15] = state.reg.V[ins.nib[1]] & 1 280 | state.reg.V[ins.nib[1]] = (state.reg.V[ins.nib[1]] >> 1) 281 | end, 282 | [7] = func insn8_7(state, ins) 283 | state.reg.V[15] = if state.reg.V[ins.nib[2]] > (state.reg.V[ins.nib[1]]) then 1 else 0 end 284 | state.reg.V[ins.nib[1]] = (stat.reg.V[ins.nib[2]] - (state.reg.V[ins.nib[1]])) 285 | end, 286 | [8] = bad_op, [9] = bad_op, [10] = bad_op, [11] = bad_op, [12] = bad_op, [13] = bad_op, 287 | [14] = func insn8_14(state, ins) 288 | state.reg.V[15] = (state.reg.V[ins.nib[1]] & 128) >> 7 289 | state.reg.V[ins.nib[1]] = (state.reg.V[ins.nib[1]] << 1) & 255 290 | end, 291 | [15] = bad_op, 292 | }[ins.nib[3]](state, ins) 293 | end, 294 | [9] = func insn9(state, ins) 295 | if state.reg.V[ins.nib[1]] != state.reg.V[ins.nib[2]] then 296 | state:skip() 297 | end 298 | end, 299 | [10] = func insn10(state, ins) 300 | state.reg.I = ins.addr 301 | end, 302 | [11] = func insn11(state, ins) 303 | state:jump(ins.addr + state.reg.V[0]) 304 | end, 305 | [12] = func insn12(state, ins) 306 | state.reg.V[ins.nib[1]] = rand() & ins.byte.low 307 | end, 308 | [13] = func insn13(state, ins) 309 | x = state.reg.V[ins.nib[1]] 310 | y = state.reg.V[ins.nib[2]] 311 | n = ins.nib[3] 312 | i = state.reg.I 313 | c = 0 314 | for idx in range(n) do 315 | if (i >= 0) && (i < 4095) then 316 | c = c || disp_blit(state.disp, x, y, state.mem[i]) 317 | end 318 | i += 1 319 | y += 1 320 | end 321 | state.reg.V[15] = c 322 | if None != state.trace.draw then state:log(disas(state, ins) + ' ;; Drew sprite at ' + tostring(state.reg.I) + ' - ' + tostring(i) + ' to x=' + tostring(x) + ', y=' + tostring(state.reg.V[ins.nib[2]]) + '-' + y + ' in ' + tostring(n) + ' steps, changed=' + tostring(c)) end 323 | end, 324 | [14] = func insn14(state, ins) 325 | if ins.byte.low == 158 then 326 | if state.keys[ins.nib[1]] then 327 | state:skip() 328 | end 329 | return 330 | end 331 | if ins.byte.low == 161 then 332 | if !(state.keys[ins.nib[1]]) then 333 | state:skip() 334 | end 335 | return 336 | end 337 | bad_op(state, ins) 338 | end, 339 | [15] = func insn15(state, ins) 340 | if ins.byte.low == 7 then 341 | state.reg.V[ins.nib[1]] = state.reg.DT 342 | return 343 | end 344 | if ins.byte.low == 10 then 345 | state:log("waiting on key...") 346 | state.wchan = {type="key", reg=ins.nib[1]} 347 | return 348 | end 349 | if ins.byte.low == 21 then 350 | state.reg.DT = state.reg.V[ins.nib[1]] 351 | return 352 | end 353 | if ins.byte.low == 24 then 354 | state.reg.ST = state.reg.V[ins.nib[1]] 355 | return 356 | end 357 | if ins.byte.low == 30 then 358 | state.reg.I = (state.reg.I + (state.reg.V[ins.nib[1]])) & 65535 359 | return 360 | end 361 | if ins.byte.low == 41 then 362 | state.reg.I = 5 * (state.reg.V[ins.nib[1]] & 15) 363 | return 364 | end 365 | if ins.byte.low == 51 then 366 | if state.reg.I >= 4093 then 367 | state:log('BCD OOB @' + (state.reg.IP) + ': I=' + (state.reg.I)) 368 | return 369 | end 370 | bcd = tostring(state.reg.V[ins.nib[1]]) 371 | while (#bcd) < 3 do bcd = '0' + bcd end 372 | state.mem[state.reg.I] = ord(bcd[0]) - ord('0') 373 | state.mem[state.reg.I + 1] = ord(bcd[1]) - ord('0') 374 | state.mem[state.reg.I + 2] = ord(bcd[2]) - ord('0') 375 | return 376 | end 377 | if (ins.byte.low == 85) || (ins.byte.low == 101)then 378 | if state.reg.I + (ins.nib[1]) >= 4096 then 379 | state:log((if ins.byte.low == 85 then 'S' else 'R' end) + 'VC OOB @' + (state.reg.IP) + ': I=' + (state.reg.I) + ', n=' + (ins.nib[1])) 380 | return 381 | end 382 | for n in range(ins.nib[1] + 1) do 383 | if ins.byte.low == 85 then 384 | state.mem[state.reg.I + n] = state.reg.V[n] 385 | else 386 | state.reg.V[n] = state.mem[state.reg.I + n] 387 | end 388 | end 389 | return 390 | end 391 | bad_op(state, ins) 392 | end, 393 | } 394 | 395 | func bad_disas(state, ins) 396 | nibs = for n in ins.nib do continue n end 397 | return '.DB ' + nibs:map(lambda(n) hex2chr_t[n] end):reduce(lambda(x, y) x + y end, "") 398 | end 399 | 400 | disas = { 401 | [0] = func disas0(state, ins) 402 | return 'SYS ' + tostring(ins.addr) + ' ; (' + tostring(state.sys[ins.addr]) + ')' 403 | end, 404 | [1] = func disas1(state, ins) 405 | return 'JMP ' + tostring(ins.addr) 406 | end, 407 | [2] = func disas2(state, ins) 408 | return 'CAL ' + tostring(ins.addr) 409 | end, 410 | [3] = func disas3(state, ins) 411 | return 'SKE V' + tostring(ins.nib[1]) + ', ' + tostring(ins.byte.low) 412 | end, 413 | [4] = func disas4(state, ins) 414 | return 'SKN V' + tostring(ins.nib[1]) + ', ' + tostring(ins.byte.low) 415 | end, 416 | [5] = func disas5(state, ins) 417 | return 'SKE V' + tostring(ins.nib[1]) + ', V' + tostring(ins.nib[2]) 418 | end, 419 | [6] = func disas6(state, ins) 420 | return 'MOV V' + tostring(ins.nib[1]) + ', ' + tostring(ins.byte.low) 421 | end, 422 | [7] = func disas7(state, ins) 423 | return 'ADD V' + tostring(ins.nib[1]) + ', ' + tostring(ins.byte.low) 424 | end, 425 | [8] = func disas8(state, ins) 426 | return { 427 | [0] = func disas8_0(state, ins) 428 | return 'MOV V' + tostring(ins.nib[1]) + ', V' + tostring(ins.nib[2]) 429 | end, 430 | [1] = func disas8_1(state, ins) 431 | return 'BOR V' + tostring(ins.nib[1]) + ', V' + tostring(ins.nib[2]) 432 | end, 433 | [2] = func disas8_2(state, ins) 434 | return 'BAN V' + tostring(ins.nib[1]) + ', V' + tostring(ins.nib[2]) 435 | end, 436 | [3] = func disas8_3(state, ins) 437 | return 'BXR V' + tostring(ins.nib[1]) + ', V' + tostring(ins.nib[2]) 438 | end, 439 | [4] = func disas8_4(state, ins) 440 | return 'ADD V' + tostring(ins.nib[1]) + ', V' + tostring(ins.nib[2]) 441 | end, 442 | [5] = func disas8_5(state, ins) 443 | return 'SUB V' + tostring(ins.nib[1]) + ', V' + tostring(ins.nib[2]) 444 | end, 445 | [6] = func disas8_6(state, ins) 446 | return 'SHR V' + tostring(ins.nib[1]) 447 | end, 448 | [7] = func disas8_7(state, ins) 449 | return 'SBR V' + tostring(ins.nib[1]) + ', V' + tostring(ins.nib[2]) + ' ; (SUB V' + tostring(ins.nib[2]) + ', V' + tostring(ins.nib[1]) + ')' 450 | end, 451 | [8] = bad_disas, [9] = bad_disas, [10] = bad_disas, [11] = bad_disas, [12] = bad_disas, [13] = bad_disas, 452 | [14] = func disas8_14(state, ins) 453 | return 'SHL V' + tostring(ins.nib[1]) 454 | end, 455 | [15] = bad_disas 456 | }[ins.nib[3]](state, ins) 457 | end, 458 | [9] = func disas9(state, ins) 459 | return 'SKN V' + tostring(ins.nib[1]) + ', V' + tostring(ins.nib[2]) 460 | end, 461 | [10] = func disas10(state, ins) 462 | return 'MOV I, ' + tostring(ins.addr) 463 | end, 464 | [11] = func disas11(state, ins) 465 | return 'JR0 ' + tostring(ins.addr) 466 | end, 467 | [12] = func disas12(state, ins) 468 | return 'RND V' + tostring(ins.nib[1]) + ', ' + tostring(ins.byte.low) 469 | end, 470 | [13] = func disas13(state, ins) 471 | return 'BLT V' + tostring(ins.nib[1]) + ', V' + tostring(ins.nib[2]) + ', ' + tostring(ins.nib[3]) 472 | end, 473 | [14] = func disas14(state, ins) 474 | if ins.byte.low == 158 then 475 | return 'SKK ' + tostring(ins.nib[1]) 476 | end 477 | if ins.byte.low == 161 then 478 | return 'SNK ' + tostring(ins.nib[1]) 479 | end 480 | return bad_disas(state, ins) 481 | end, 482 | [15] = func disas15(state, ins) 483 | if ins.byte.low == 7 then 484 | return 'MOV V' + tostring(ins.nib[1]) + ', DT' 485 | end 486 | if ins.byte.low == 10 then 487 | return 'WAK V' + tostring(ins.nib[1]) 488 | end 489 | if ins.byte.low == 21 then 490 | return 'MOV DT, V' + tostring(ins.nib[1]) 491 | end 492 | if ins.byte.low == 24 then 493 | return 'MOV ST, V' + tostring(ins.nib[1]) 494 | end 495 | if ins.byte.low == 30 then 496 | return 'ADD I, V' + tostring(ins.nib[1]) 497 | end 498 | if ins.byte.low == 41 then 499 | return 'LDF V' + tostring(ins.nib[1]) 500 | end 501 | if ins.byte.low == 51 then 502 | return 'BCD V' + tostring(ins.nib[1]) 503 | end 504 | if ins.byte.low == 85 then 505 | return 'SVC ' + tostring(ins.nib[1]) 506 | end 507 | if ins.byte.low == 101 then 508 | return 'RVC ' + tostring(ins.nib[1]) 509 | end 510 | return bad_disas(state, ins) 511 | end, 512 | __call = func(self, state, ins) 513 | return (for n in ins.nib do continue hex2chr_t[n] end):reduce(lambda(x, y) x + y end, '') + ': ' + (self[ins.nib[0]](state, ins)) 514 | end, 515 | } 516 | 517 | func descr_list(l, prf, bias) 518 | if None == bias then bias = 0 end 519 | idx = -1 520 | return (for i in l do idx += 1; continue prf + tostring(idx + bias) + '=' + tostring(i) end):reduce(lambda(x, y) x + " " + y end, ""):sub(1) 521 | end 522 | 523 | func descr_state(state, DESCR_CNT = range(15)) 524 | return 'IP=' + tostring(state.reg.IP) + ' I=' + tostring(state.reg.I) + ' [' + descr_list(for idx in DESCR_CNT do continue state.mem[state.reg.I + idx - 7] end, 'M', state.reg.I - 7) + '] {' + descr_list(state.reg.V, 'V') + '} DT=' + tostring(state.reg.DT) + ' ST=' + tostring(state.reg.ST) + ' stack=' + tostring(state.stack) 525 | end 526 | 527 | func step_cpu(state, force) 528 | ins = new_insn(mem_int(state.mem, state.reg.IP, 2)) 529 | if None != force then 530 | for brk in state.breaks do 531 | if brk(state, ins) then 532 | state.wchan = {type="break", brk=brk} 533 | return 534 | end 535 | end 536 | end 537 | if None != state.trace.all then state:log(disas(state, ins) + ' ;; ' + descr_state(state)) end 538 | insns[ins.nib[0]](state, ins) 539 | if !(state.jumped) then 540 | state.reg.IP += 2 541 | end 542 | state.jumped = false 543 | end 544 | 545 | DISP_HEIGHT = (func() 546 | buf = buffer.new(8) 547 | io.stdin:ioctl(io.TIOCGWINSZ, buf) 548 | return buf:get(buffer.type.uint16, 0) 549 | end)() 550 | 551 | DEBUG_VALS = { 552 | s = lambda() step_cpu(state, true) end, -- XXX Dynamic scope access 553 | d = lambda(n) for i in range(n) do 554 | ridx = i - n / 2 555 | rip = IP + 2 * ridx 556 | print( 557 | if rip == IP then '=>' else ' ' end, 558 | rip, ':', 559 | disas(state, new_insn(mem_int(mem, IP + 2 * (i - (n / 2)), 2))) 560 | ) 561 | end end, 562 | q = lambda() state.running = false end, 563 | } 564 | 565 | func run_cpu(state, incnt, tmcnt) 566 | if None == incnt then incnt = 20 end 567 | if None == tmcnt then tmcnt = 15 end 568 | icntrange = range(incnt) 569 | state.running = true 570 | while state.running do 571 | if None == state.wchan then 572 | for ictr in icntrange do 573 | step_cpu(state) 574 | if None != state.wchan then break end 575 | end 576 | end 577 | clear_screen() 578 | print(disp_render(state.disp)) 579 | io.stdout:write(descr_state(state) + ' ') 580 | if state.reg.DT > 0 then 581 | state.reg.DT -= tmcnt 582 | if state.reg.DT < 0 then state.reg.DT = 0 end 583 | end 584 | if state.reg.ST > 0 then 585 | print('***BEEP***') 586 | state.reg.ST -= tmcnt 587 | if state.reg.ST < 0 then state.reg.ST = 0 end 588 | else 589 | print('') 590 | end 591 | if (None != state.wchan) then 592 | if state.wchan.type == "key" then 593 | print('waiting on a key (0-15): ') 594 | v = toint(io.stdin:read(io.LINE)) 595 | state.reg.V[state.wchan.reg] = v & 255 596 | elseif state.wchan.type == "break" then 597 | -- NOP 598 | else 599 | error('Unknown wait channel!') 600 | end 601 | end 602 | if (#(state.buffer)) > 0 then 603 | lctr = state.disp.h + 1 604 | rem = #(state.buffer) 605 | midx = 0 606 | for msg in state.buffer do 607 | if lctr >= DISP_HEIGHT - 1 then 608 | io.stdout:write('(...and', rem, 'more [Enter/q])') 609 | if io.stdin:read(io.LINE):sub(0, -1) == 'q' then break end 610 | lctr = 0 611 | end 612 | print(midx, ':', msg) 613 | rem -= 1 614 | midx += 1 615 | lctr += 1 616 | end 617 | while true do 618 | io.stdout:write('(acknowledge/eval?) ') 619 | ln = io.stdin:read(io.LINE):sub(0, -1) 620 | if (#ln) > 0 then 621 | res = try(parse, ln) 622 | if res[0] then 623 | res = try(lambda (nd) nd.stmtlist[0].expr(state.reg + state + DEBUG_VALS) end, res[1]) 624 | if res[0] then 625 | print(res[1]) 626 | else 627 | print('Exec error:', res[1]) 628 | end 629 | else 630 | print('Parse error:', res[1]) 631 | end 632 | else 633 | break 634 | end 635 | end 636 | state.buffer = [] 637 | end 638 | end 639 | end 640 | 641 | SEPARATOR = ('=#' * 38) + '=' 642 | 643 | func main() 644 | print('Enter filename to load: ') 645 | fname = io.stdin:read(io.LINE):sub(0, -1) 646 | strm = io.open(fname, io.MODE_READ|io.MODE_BINARY) 647 | state = new_state() 648 | load_stream(state.mem, None, strm) 649 | while true do 650 | io.stdout:write("(init)> ") 651 | ln = io.stdin:read(io.LINE):sub(0, -1) 652 | if (#ln) > 0 then 653 | print(parse(ln).stmtlist[0].expr(state.reg + state)) 654 | else 655 | break 656 | end 657 | end 658 | res = try(run_cpu, state) 659 | if !res[0] then 660 | print('Error occurred:', res[1]) 661 | print(SEPARATOR) 662 | print('Buffered logs not dumped before error:') 663 | for msg in state.buffer do print(msg) end 664 | print(SEPARATOR) 665 | print('Starting post-mortem debugger...') 666 | execfile('solid.sol') 667 | debst = new_debug_state(res) 668 | postmortem(debst) 669 | end 670 | end 671 | 672 | main() 673 | -------------------------------------------------------------------------------- /programs/dump.sol: -------------------------------------------------------------------------------- 1 | func dump(obj, indent, seen = {}) 2 | if None == indent then 3 | indent = 0 4 | seen = {} 5 | end 6 | io.stdout:write(" "*indent) 7 | if type(obj) == "list" then 8 | buf = buffer.fromobject(obj) 9 | addr = buf:address() 10 | if None != seen[obj] then 11 | print("...("+addr+")") 12 | return 13 | end 14 | seen[obj] = 1 15 | print("[") 16 | for elem in obj do 17 | dump(elem, indent+2) 18 | end 19 | print(" "*indent+"] =("+addr+")") 20 | return 21 | end 22 | if type(obj) == "map" then 23 | buf = buffer.fromobject(obj) 24 | addr = buf:address() 25 | if None != seen[obj] then 26 | print("...("+addr+")") 27 | return 28 | end 29 | seen[obj] = 1 30 | print("{") 31 | for key in obj do 32 | io.stdout:write(" "*(indent+2)) 33 | prepr(key, ":") 34 | dump(obj[key], indent+4) 35 | end 36 | print(" "*indent+"} =("+addr+")") 37 | return 38 | end 39 | prepr(obj) 40 | end 41 | -------------------------------------------------------------------------------- /programs/fizzbuzz.sol: -------------------------------------------------------------------------------- 1 | ID = lambda(x) x end 2 | FIZZ = lambda(x) 'FIZZ' end 3 | BUZZ = lambda(x) 'BUZZ' end 4 | FIZZBUZZ = lambda(x) 'FIZZBUZZ' end 5 | L = [FIZZBUZZ] + (for i in range(14) do if !((i + 1) % 3) then continue FIZZ end if !((i + 1) % 5) then continue BUZZ end continue ID end) 6 | func fizzbuzz(s, e) return for i in range(e-s) do continue L[(i + s)%(#L)](i + s) end end 7 | 8 | for i in fizzbuzz(1, 101) do print(i) end 9 | -------------------------------------------------------------------------------- /programs/http.sol: -------------------------------------------------------------------------------- 1 | func printerr(a) 2 | io.stderr:write(tostring(a)) 3 | end 4 | 5 | while 1 do 6 | printerr(io.stdin:read(io.LINE)) 7 | end 8 | -------------------------------------------------------------------------------- /programs/interp.sol: -------------------------------------------------------------------------------- 1 | -- The Solterpreter! A simple command-line interface for the compiler. 2 | 3 | print('Solterpreter/Viperpreter v0.1') 4 | print('(Runtime version ', debug.version, ')') 5 | 6 | __interp = { 7 | running = 1, 8 | buffer = '', 9 | ps1 = '>>> ', 10 | ps2 = '... ', 11 | stmt_stack=0, 12 | } 13 | 14 | func exit() 15 | __interp.running=0 16 | end 17 | 18 | quit = exit 19 | 20 | while __interp.running do 21 | if #__interp.buffer then 22 | __interp.prompt = __interp.ps2 23 | else 24 | __interp.prompt = __interp.ps1 25 | end 26 | if None != readline then 27 | __interp.line = readline.readline(__interp.prompt) 28 | if #__interp.line then readline.add_history(__interp.line) end 29 | else 30 | io.stdout:write(__interp.prompt) 31 | io.stdout:flush() 32 | __interp.line = io.stdin:read(io.LINE):sub(0, -1) 33 | end 34 | 7 35 | --prepr(__interp.line) 36 | --prepr(__interp) 37 | if (__interp.line:sub(-4, None)=="then") then 38 | __interp.buffer+=__interp.line+" " 39 | __interp.stmt_stack+=1 40 | else 41 | if (__interp.line:sub(-2, None)=="do") then 42 | __interp.buffer+=__interp.line+" " 43 | __interp.stmt_stack-=1 44 | else 45 | if __interp.line:sub(-1, None)=="\" then 46 | __interp.buffer+=__interp.line:sub(0, -1)+" " 47 | else 48 | __interp.buffer+=__interp.line+" " 49 | if __interp.line:sub(-3, None)=="end" then 50 | __interp.stmt_stack-=1 51 | end 52 | if __interp.stmt_stack<=0 then 53 | __interp.stmt_stack=0 54 | __interp.program = try(parse, __interp.buffer) 55 | if !__interp.program[0] then 56 | print('Syntax error') 57 | else 58 | if !(try(func() __interp.program[1].stmtlist[0].type end)[0]) then 59 | print('NULL program error') 60 | else 61 | if __interp.program[1].stmtlist[0].type == ast.ST_EXPR then 62 | __interp.program[1] = __interp.program[1].stmtlist[0].expr 63 | __interp.isexpr = 1 64 | else 65 | __interp.isexpr = 0 66 | end 67 | __interp.result = try(__interp.program[1]) 68 | if !__interp.result[0] then 69 | print(__interp.result[1]) 70 | print(__interp.result[2]) 71 | for ent in __interp.result[2] do 72 | st = ent[0] 73 | scope = ent[1] 74 | fun = ent[2] 75 | if st.type == ast.ST_LIST then continue end 76 | print('In', fun, 'at', st.loc.line, ',', st.loc.col, ':') 77 | ast.print(st) 78 | print(scope) 79 | print('---') 80 | end 81 | else 82 | if __interp.isexpr then 83 | prepr(__interp.result[1]) 84 | _ = __interp.result[1] 85 | end 86 | end 87 | end 88 | end 89 | __interp.buffer='' 90 | end 91 | end 92 | end 93 | end 94 | end 95 | -------------------------------------------------------------------------------- /programs/monty.sol: -------------------------------------------------------------------------------- 1 | TOK = {LPAREN = 1, RPAREN = 2, INT = 3, BOOL = 4, NAME = 5, QUOTE = 6, EOF = 7} 2 | keys = [] 3 | for k in TOK do keys:insert(#keys, k) end 4 | for k in keys do TOK[TOK[k]]=k end 5 | 6 | token = { 7 | new = func (type, value) 8 | return {type = type, value = value, __index = token} 9 | end, 10 | pretty = func(self) 11 | tname = TOK[self.type] 12 | tval = tostring(self.value) 13 | return '{'+tname+':'+tval+'}' 14 | end 15 | } 16 | 17 | tokenizer = { 18 | WS = " "+chr(8)+chr(9)+chr(10), 19 | NAMESET = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ=+-*/.<>?!@$%^~", 20 | DIGITS = "0123456789", 21 | EOF = {}, 22 | new = func (str) 23 | res = {str = str, pushed = None, __index = tokenizer} 24 | res:init() 25 | return res 26 | end, 27 | init = func(self) 28 | --print('In init, self is', self) 29 | res.cur = res:token() 30 | res.next = res:token() 31 | end, 32 | next_char = func(self) 33 | if self.pushed == None then 34 | --print('In next_char, self is', self) 35 | --print('In next_char, self.str is', self.str) 36 | if self.str:eof() then return self.EOF end 37 | res = self.str:read(1) 38 | else 39 | --print('Retrieving from pushback', self.pushed) 40 | res = self.pushed[0] 41 | self.pushed = self.pushed:sub(1) 42 | if self.pushed == "" then self.pushed = None end 43 | end 44 | --print(res) 45 | return res 46 | end, 47 | push_back = func(self, s) 48 | --print('Pushing back', s) 49 | if s == self.EOF then print('WARNING: Attempted to push_back EOF'); return end 50 | if self.pushed == None then 51 | self.pushed = s 52 | else 53 | self.pushed = s + self.pushed 54 | end 55 | --print('self.pushed:', self.pushed) 56 | end, 57 | token = func (self) 58 | --print('In token, self is', self) 59 | --print('In token, self.str is', self.str) 60 | c = self:next_char() 61 | while !(c == self.EOF) do 62 | if c == "" then return token.new(TOK.EOF, None) end 63 | if c == "(" then return token.new(TOK.LPAREN, c) end 64 | if c == ")" then return token.new(TOK.RPAREN, c) end 65 | if self.NAMESET:find(c) >= 0 then 66 | --print('{NAME}') 67 | name = c 68 | c = self:next_char() 69 | while 1 do 70 | found = 0 71 | if self.NAMESET:find(c) >= 0 then found = 1 end 72 | if self.DIGITS:find(c) >= 0 then found = 1 end 73 | if !found then break end 74 | name += c 75 | c = self:next_char() 76 | if c == self.EOF then continue end 77 | end 78 | self:push_back(c) 79 | return token.new(TOK.NAME, name) 80 | end 81 | if self.DIGITS:find(c) >= 0 then 82 | val = c 83 | c = self:next_char() 84 | while self.DIGITS:find(c) >= 0 do 85 | val += c 86 | c = self:next_char() 87 | if c == self.EOF then continue end 88 | end 89 | self:push_back(c) 90 | return token.new(TOK.INT, toint(val)) 91 | end 92 | if c == "#" then 93 | c = self:next_char() 94 | if c == "t" then return token.new(TOK.BOOL, 1) end 95 | if c == "f" then return token.new(TOK.BOOL, 0) end 96 | error("Invalid value for bool literal: "+c) 97 | end 98 | if c == "'" then return token.new(TOK.QUOTE, c) end 99 | if self.WS:find(c) >= 0 then 100 | c = self:next_char() 101 | continue 102 | end 103 | if c == ";" then 104 | c = self:next_char() 105 | while 1 do 106 | if c == chr(10) then break end 107 | c = self:next_char() 108 | end 109 | c = self:next_char() 110 | continue 111 | end 112 | error("Invalid character in token stream: "+c) 113 | end 114 | return token.new(TOK.EOF, None) 115 | end, 116 | advance = func(self) 117 | self.cur = self.next 118 | self.next = self:token() 119 | end 120 | } 121 | 122 | 123 | 124 | ttreegen = { 125 | new = func(tok) 126 | return {tok = tok, __index = ttreegen} 127 | end, 128 | generate = func(self, consume) 129 | res = self.TT_DISPATCH[self.tok.cur.type](self, self.tok.cur) 130 | if None == consume then self.tok:advance() end 131 | return res 132 | end, 133 | TT_DISPATCH = { 134 | [TOK.LPAREN] = func(self, tok) 135 | toklist = [] 136 | self.tok:advance() 137 | tok = self.tok.cur 138 | while 1 do 139 | if tok.type == TOK.RPAREN then break end 140 | if tok.type == TOK.EOF then error('Encountered EOF while matching delimiter') end 141 | toklist:insert(#toklist, self.TT_DISPATCH[tok.type](self, tok)) 142 | self.tok:advance() 143 | tok = self.tok.cur 144 | end 145 | return toklist 146 | end, 147 | [TOK.RPAREN] = func(self, tok) 148 | error("Unexpected right parenthesis") 149 | end, 150 | [TOK.INT] = func(self, tok) 151 | return tok 152 | end, 153 | [TOK.BOOL] = func(self, tok) 154 | return tok 155 | end, 156 | [TOK.NAME] = func(self, tok) 157 | return tok 158 | end, 159 | [TOK.QUOTE] = func(self, tok) 160 | self.tok:advance() 161 | tok.quoting = self:generate(0) 162 | return tok 163 | end, 164 | [TOK.EOF] = func(self, tok) 165 | return None 166 | end 167 | } 168 | } 169 | 170 | EX = {CALL=1, ASSIGN=2, FUNCDECL=3, SCOPE=4, IFELSE=5, DATUM=6, LIT=7, REF=8, LIST=9} 171 | keys = [] 172 | for k in EX do keys:insert(#keys, k) end 173 | for k in keys do EX[EX[k]]=k end 174 | 175 | node = { 176 | new = func(type, value) 177 | return {type=type, value=value, __index=node} 178 | end, 179 | pretty = func(self) return self.PRETTY_DISPATCH[self.type](self) end 180 | PRETTY_DISPATCH = { 181 | [EX.CALL] = func(self) 182 | return '' 183 | end, 184 | [EX.ASSIGN] = func(self) 185 | return '' 186 | end, 187 | [EX.FUNCDECL] = func(self) 188 | return '' 189 | end, 190 | [EX.SCOPE] = func(self) 191 | return '' 192 | end, 193 | [EX.IFELSE] = func(self) 194 | return '' 195 | end, 196 | [EX.DATUM] = func(self) 197 | return '#'+tostring(self.value) 198 | end, 199 | [EX.LIT] = func(self) 200 | if type(self.value) == 'list' then 201 | return '/'+tostring(self.value:copy():map(func(i) return i:pretty() end)) 202 | end 203 | return '/'+tostring(self.value) 204 | end, 205 | [EX.REF] = func(self) 206 | --print('In EX.REF, self is', self) 207 | res = '@'+tostring(self.value) 208 | --print('In EX.REF, returning', res) 209 | return res 210 | end, 211 | [EX.LIST] = func(self) 212 | --print('In EX.LIST, self is', self) 213 | return '' 214 | end 215 | } 216 | } 217 | 218 | parser = { 219 | new = func(ttgen) 220 | return {ttgen = ttgen, __index = parser} 221 | end, 222 | parse = func(self, tt) 223 | if type(tt) == 'map' then 224 | --print('In parse, self is', self) 225 | --print('In parse, tt is', tt) 226 | --print('In parse, dispatch to', self.TT_PARSE_DISPATCH[tt.type]) 227 | res = self.TT_PARSE_DISPATCH[tt.type](self, tt) 228 | else 229 | name = tt[0] 230 | if !(name.type == TOK.NAME) then 231 | error('Expected name as first element of expression-list') 232 | end 233 | rest = tt:copy() 234 | rest:remove(0) 235 | sc = self.SCALL_DISPATCH[name.value] 236 | if !(None == sc) then 237 | sc = None 238 | res = self.SCALL_DISPATCH[name.value](self, rest) 239 | else 240 | res = node.new(EX.CALL, {name=name.value, args=rest:map(func(i) return self:parse(i) end)}) 241 | end 242 | end 243 | --print('In parse, returning', res:pretty()) 244 | return res 245 | end, 246 | TT_PARSE_DISPATCH = { 247 | [TOK.INT] = func(self, tok) 248 | return node.new(EX.LIT, tok.value) 249 | end, 250 | [TOK.BOOL] = func(self, tok) 251 | return node.new(EX.LIT, tok.value) 252 | end, 253 | [TOK.NAME] = func(self, tok) 254 | --print('In TOK.NAME, tok is', tok) 255 | res = node.new(EX.REF, tok.value) 256 | --print('In TOK.NAME, returning', res) 257 | return res 258 | end, 259 | [TOK.QUOTE] = func(self, tok) 260 | return self:parse_datum(tok.quoting) 261 | end 262 | }, 263 | SCALL_DISPATCH = { 264 | define = func(self, args) 265 | name = args[0] 266 | if !(name.type == TOK.NAME) then error('Define: expected name as first argument') end 267 | value = self:parse(args[1]) 268 | return node.new(EX.ASSIGN, {name=name.value, value=value}) 269 | end, 270 | ["if"] = func(self, args) 271 | cond = self:parse(args[0]) 272 | ift = self:parse(args[1]) 273 | iff = self:parse(args[2]) 274 | return node.new(EX.IFELSE, {cond=cond, ift=ift, iff=iff}) 275 | end, 276 | begin = func(self, args) 277 | args:map(func(i) return self:parse(i) end) 278 | return node.new(EX.LIST, args) 279 | end, 280 | lambda = func(self, args) 281 | --print('Lambda args:', args) 282 | params = args[0] 283 | if !(type(params) == 'list') then error('Lambda: expected parameters as first argument (got '+tostring(params)+')') end 284 | params:map(func(i) 285 | if !(type(i) == 'map') then error('Lambda: expected name token in argument list (got sublist)') end 286 | if !(i.type == TOK.NAME) then error('Lambda: expected name token in argument list (got '+(i:pretty())+')') end 287 | return i.value 288 | end) 289 | body = args:copy() 290 | body:remove(0) 291 | --print('Lambda body:', body) 292 | body:map(func(i) return self:parse(i) end) 293 | return node.new(EX.FUNCDECL, {params=params, body=node.new(EX.LIST, body)}) 294 | end, 295 | let = func(self, args) 296 | defs = args[0] 297 | if !(type(defs) == 'list') then error('Let: expected list of bindings are first argument') end 298 | defs:map(func(i) 299 | if !(type(i) == 'list') then error('Let: expected a binding entry') end 300 | return self.SCALL_DISPATCH.define(self, i) 301 | end) 302 | body = args:copy() 303 | body:remove(0) 304 | body:map(func(i) return self:parse(i) end) 305 | return node.new(EX.SCOPE, defs+body) 306 | end, 307 | letrec = func(self, args) 308 | defs = args[0] 309 | if !(type(defs) == 'list') then error('Let: expected list of bindings are first argument') end 310 | defs:map(func(i) 311 | if !(type(i) == 'list') then error('Let: expected a binding entry') end 312 | return self.SCALL_DISPATCH.define(self, i) 313 | end) 314 | body = args:copy() 315 | body:remove(0) 316 | body:map(func(i) return self:parse(i) end) 317 | return node.new(EX.LIST, defs+body) 318 | end 319 | } 320 | parse_datum = func(self, tt) 321 | if type(tt) == 'map' then 322 | return self.TT_PARSE_DATUM_DISPATCH[tt.type](self, tt) 323 | else 324 | list = [] 325 | for tok in tt do 326 | list:insert(#list, self:parse_datum(tok)) 327 | end 328 | return node.new(EX.LIT, list) 329 | end 330 | end, 331 | TT_PARSE_DATUM_DISPATCH = { 332 | [TOK.INT] = func(self, tok) 333 | return node.new(EX.LIT, tok.value) 334 | end, 335 | [TOK.BOOL] = func(self, tok) 336 | return node.new(EX.LIT, tok.value) 337 | end, 338 | [TOK.NAME] = func(self, tok) 339 | return node.new(EX.LIT, tok.value) 340 | end, 341 | [TOK.QUOTE] = func(self, tok) 342 | return self:parse(tok.quoting) 343 | end 344 | }, 345 | run = func(self) 346 | tt = self.ttgen:generate() 347 | list = [] 348 | while 1 do 349 | if None == tt then break end 350 | --if type(tt) == 'list' then 351 | list:insert(#list, self:parse(tt)) 352 | --end 353 | tt = self.ttgen:generate() 354 | end 355 | --print('In run, list is', list) 356 | return node.new(EX.LIST, list) 357 | end 358 | } 359 | 360 | converter = { 361 | new = func(p) 362 | return {parser=p, __index = converter} 363 | end, 364 | make = func(self, node) 365 | --print('In make, node is a', EX[node.type], 'of value', node) 366 | res = self.MAKE_DISPATCH[node.type](self, node) 367 | --print('In make, returning', res) 368 | if type(res) == "astnode" then ast.print(res) end 369 | return res 370 | end, 371 | MAKE_DISPATCH = { 372 | [EX.CALL] = func(self, node) 373 | e = parse('f()').stmtlist[0].expr 374 | e.expr.ident = node.value.name 375 | args = node.value.args:copy():map(func(i) return self:make(i) end) 376 | --print('In EX.CALL, replacement args are', args) 377 | e.args = args 378 | --print('In EX.CALL, args are', e.args) 379 | return e 380 | end, 381 | [EX.ASSIGN] = func(self, node) 382 | e = parse('a = b').stmtlist[0].expr 383 | e.ident = node.value.name 384 | e.value = self:make(node.value.value) 385 | return e 386 | end, 387 | [EX.FUNCDECL] = func(self, node) 388 | e = parse('func() None None end').stmtlist[0].expr 389 | params = node.value.params 390 | --print('In EX.FUNCDECL, params are', params) 391 | e.args = params 392 | --print('In EX.FUNCDECL, args are', e.args) 393 | e.body.stmtlist = self:make(node.value.body) 394 | return e 395 | end, 396 | [EX.SCOPE] = func(self, node) 397 | e = parse('(func() None None end)()').stmtlist[0].expr 398 | node.type = EX.LIST 399 | e.expr.body.stmtlist = self:make(node) 400 | node.type = EX.SCOPE 401 | return e 402 | end, 403 | [EX.IFELSE] = func(self, node) 404 | e = parse('(func() if None then return None else return None end end)()').stmtlist[0].expr 405 | e.expr.body.stmtlist[0].cond = self:make(node.value.cond) 406 | e.expr.body.stmtlist[0].iftrue.stmtlist[0].ret = self:make(node.value.ift) 407 | e.expr.body.stmtlist[0].iffalse.stmtlist[0].ret = self:make(node.value.iff) 408 | return e 409 | end, 410 | [EX.DATUM] = func(self, node) error('EX.DATUM: Not implemented') end, 411 | [EX.LIT] = func(self, node) 412 | if type(node.value) == 'list' then 413 | e = parse('[None]').stmtlist[0].expr 414 | e.list = node.value:copy():map(func(i) return self:make(i) end) 415 | else 416 | e = parse('None').stmtlist[0].expr 417 | if type(node.value) == "int" then 418 | e.littype = ast.LIT_INT 419 | e.ival = node.value 420 | end 421 | if type(node.value) == "string" then 422 | e.littype = ast.LIT_STRING 423 | e.str = node.value 424 | end 425 | end 426 | return e 427 | end, 428 | [EX.REF] = func(self, node) 429 | e = parse('a').stmtlist[0].expr 430 | e.ident = node.value 431 | return e 432 | end, 433 | [EX.LIST] = func(self, node) 434 | e = parse('func() None end').stmtlist[0].expr 435 | l = node.value:copy() 436 | l:map(func(i) 437 | s = parse('None').stmtlist[0] 438 | s.expr = self:make(i) 439 | return s 440 | end) 441 | lastidx = (#l) - 1 442 | r = parse('return None').stmtlist[0] 443 | r.ret = l[lastidx].expr 444 | l[lastidx] = r 445 | --print('In EX.LIST, e is now' e) 446 | e.body.stmtlist = l 447 | return e.body.stmtlist 448 | end 449 | }, 450 | run = func(self) 451 | list = self:make(self.parser:run()) 452 | res = parse('(func() None None end)()') 453 | --print('In run, list is', list) 454 | --for i in list do ast.print(i) end 455 | res.stmtlist[0].expr.expr.body.stmtlist = list 456 | return res 457 | end 458 | } 459 | 460 | _G = debug.globals() 461 | _G['+'] = func(a, b) return a + b end 462 | _G['-'] = func(a, b) return a - b end 463 | _G['*'] = func(a, b) return a * b end 464 | _G['/'] = func(a, b) return a / b end 465 | _G['<'] = func(a, b) return a < b end 466 | _G['>'] = func(a, b) return a > b end 467 | _G['<='] = func(a, b) return a <= b end 468 | _G['>='] = func(a, b) return a >= b end 469 | _G['=='] = func(a, b) return a == b end 470 | _G['eq'] = _G['=='] 471 | _G['or'] = func(a, b) return a || b end 472 | _G['and'] = func(a, b) return a && b end 473 | -------------------------------------------------------------------------------- /programs/package.sol: -------------------------------------------------------------------------------- 1 | -- Simple package manager script 2 | 3 | package = { 4 | path = ['./', '/usr/share/sol/'], 5 | extensions = ['.sol', ''], 6 | loaded = {}, 7 | loading = {}, 8 | load = func(self, file) 9 | result = try(io.open, file, io.MODE_READ) 10 | if !result[0] then return None end 11 | stream = result[1] 12 | program = parse(stream:read(io.ALL)) 13 | return self:load_node(program) 14 | end, 15 | load_node = func(self, node) 16 | environ = {} 17 | node(environ) 18 | return environ 19 | end, 20 | import = func(self, name) 21 | if None != self.loading[name] then 22 | error('Already loading: '+name) 23 | end 24 | self.loading[name] = True 25 | if None != self.loaded[name] then return self.loaded[name] end 26 | for path in self.path do 27 | for extension in self.extensions do 28 | module = self:load(path + name + extension) 29 | if None != module then 30 | self.loaded[name] = module 31 | self.loading[name] = None 32 | return module 33 | end 34 | end 35 | end 36 | self.loading[name] = None 37 | error('Module not found') 38 | end, 39 | } 40 | 41 | --None -- Separator 42 | 43 | func() 44 | stream = try(io.open, '/etc/sol/path', io.MODE_READ) 45 | if stream[0] then 46 | stream = stream[1] 47 | while !stream:eof() do 48 | line = stream:read(io.LINE) 49 | line = line:sub(0, -1) 50 | if #line then 51 | package.path:insert(#package.path, line) 52 | end 53 | end 54 | end 55 | end() 56 | -------------------------------------------------------------------------------- /programs/server.sol: -------------------------------------------------------------------------------- 1 | http = { 2 | ERRORS = { 3 | [400] = "Bad Request", 4 | [500] = "Server Error", 5 | } 6 | } 7 | 8 | NL = ' 9 | ' 10 | CR = chr(13) 11 | 12 | func get_request() 13 | result = {} 14 | 15 | httpline = io.stdin:read(io.LINE) 16 | httpline = httpline:split(NL)[0] 17 | httpline = httpline:split(CR)[0] 18 | parts = httpline:split(" ") 19 | 20 | if (3 != (#parts)) || (parts[2] != "HTTP/1.1") then 21 | error(400) 22 | end 23 | 24 | result.method = parts[0] 25 | result.path = parts[1] 26 | result.protocol = parts[2] 27 | 28 | return result 29 | end 30 | 31 | out = try(get_request) 32 | success = out[0] 33 | value = out[1] 34 | 35 | if success then 36 | print('HTTP/1.1 200 OK 37 | Content-type: text/plain 38 | 39 | Hello from Sol! You sent in these values:', value) 40 | else 41 | if type(value) == "int" then 42 | print('HTTP/1.1 ' + tostring(value) + ' ' + (http.ERRORS[value]) + ' 43 | Content-type: text/plain 44 | 45 | Error ' + tostring(value) + ': ' + (http.ERRORS[value])) 46 | else 47 | print('HTTP/1.1 500 Server Error 48 | Content-type: text/plain 49 | 50 | Internal error: ' + tostring(value)) 51 | end 52 | end 53 | 54 | -------------------------------------------------------------------------------- /programs/solid.sol: -------------------------------------------------------------------------------- 1 | func new_debug_state(res) 2 | return { 3 | tb = res[2], 4 | framenum = (#(res[2])) - 1, 5 | err = res[1], 6 | frame = func(self) 7 | return self.tb[self.framenum] 8 | end, 9 | fun = func(self) 10 | return self:frame()[2] 11 | end, 12 | locals = func(self) 13 | return self:frame()[1] 14 | end, 15 | node = func(self) 16 | return self:frame()[0] 17 | end, 18 | goto = func(self, fn) 19 | if (fn < 0) || (fn >= (#self.tb)) then 20 | print('Bad frame number') 21 | else 22 | self.framenum = fn 23 | end 24 | end, 25 | } 26 | end 27 | 28 | func list_join(l, sep) 29 | res = l:reduce(lambda(x, y) x + sep + y end, '') 30 | return res:sub(#sep, #res) 31 | end 32 | 33 | func list_slice(l, frm, to) 34 | if frm < 0 then frm += (#l) end 35 | if to < 0 then to += (#l) end 36 | dist = to - frm 37 | return for idx in range(dist) do 38 | continue l[frm + idx] 39 | end 40 | end 41 | 42 | commands = { 43 | print = func(ds, line) 44 | nd = parse(line).stmtlist[0].expr 45 | print(nd(ds:locals())) 46 | end, 47 | error = func(ds, line) 48 | print(ds.err) 49 | end, 50 | fun = func(ds, line) 51 | print(ds:fun()) 52 | end, 53 | backtrace = func(ds, line) 54 | idx = 0 55 | for frm in ds.tb do 56 | print(if idx == ds.framenum then '=> ' else ' ' end,'In', frm[2], 'at', frm[0].loc.line, ',', frm[0].loc.col, ':', frm[0]) 57 | idx += 1 58 | end 59 | end, 60 | locals = func(ds, line) 61 | loc = ds:locals() 62 | for var in loc do 63 | print(var, ':', type(loc[var])) 64 | end 65 | end, 66 | up = func(ds, line) 67 | ds:goto(ds.framenum - 1) 68 | end, 69 | down = func(ds, line) 70 | ds:goto(ds.framenum + 1) 71 | end, 72 | to = func(ds, line) 73 | ds:goto(toint(line)) 74 | end, 75 | code = func(ds, line) 76 | ast.print(ds:node()) 77 | end, 78 | eval = func(ds, line) 79 | nd = parse(line) 80 | nd(ds:locals()) 81 | end, 82 | cont = func(ds, line) 83 | ds.running = false 84 | end, 85 | help = func(ds, line) 86 | print('Valid commands:') 87 | for k in commands do 88 | print(k) 89 | end 90 | end, 91 | } 92 | 93 | commands.p = commands.print 94 | commands.u = commands.up 95 | commands.d = commands.down 96 | commands.t = commands.to 97 | commands.c = commands.code 98 | commands['!'] = commands.eval 99 | commands['continue'] = commands.cont 100 | commands['func'] = commands.fun 101 | commands.f = commands.fun 102 | commands.bt = commands.backtrace 103 | 104 | func postmortem(ds) 105 | ds.running = true 106 | while ds.running do 107 | io.stdout:write('==> ') 108 | ln = io.stdin:read(io.LINE):sub(0, -1) 109 | parts = ln:split(" ") 110 | cmd = parts[0] 111 | arg = list_join(list_slice(parts, 1, #parts), " ") 112 | cfunc = commands[cmd] 113 | if None == cfunc then 114 | try(commands.help, ds, '') 115 | continue 116 | end 117 | res = try(cfunc, ds, arg) 118 | if !(res[0]) then print("Postmortem Internal Error:", res[1]) end 119 | end 120 | end 121 | -------------------------------------------------------------------------------- /programs/solid_run.sol: -------------------------------------------------------------------------------- 1 | execfile('solid.sol') 2 | io.stdout:write('Enter filename to run: ') 3 | __fname = io.stdin:read(io.LINE):sub(0, -1) 4 | __result = try(execfile, __fname) 5 | if __result[0] then 6 | print('Subprogram exited successfully') 7 | else 8 | print('Subprogram terminated with an error: ', __result[1]) 9 | __debst = new_debug_state(__result) 10 | postmortem(__debst) 11 | end 12 | -------------------------------------------------------------------------------- /programs/subtest.sol: -------------------------------------------------------------------------------- 1 | print("Hello from subtest!") 2 | -------------------------------------------------------------------------------- /programs/test.sol: -------------------------------------------------------------------------------- 1 | print('--- Empty functions') 2 | 3 | func f() end 4 | 5 | print(f()) 6 | 7 | print('--- While loop') 8 | a = 1 9 | while a < 10 do 10 | print(a) 11 | a += 1 12 | end 13 | 14 | print("--- Range") 15 | 16 | func mul9(b) 17 | for i in range(#b) do 18 | b[i] *= 9 19 | end 20 | end 21 | 22 | l = [1 2 3 4 5] 23 | print(range(#l)) 24 | print("--- Iter list") 25 | for i in l do print(i) end 26 | print("--- Index list") 27 | for i in range(#l) do print(i, l[i]) end 28 | print('--- mul9') 29 | mul9(l) 30 | for i in l do print(i) end 31 | print('--- Iter mul9') 32 | for i in range(#l) do print(i, l[i]) end 33 | 34 | print("--- Mapgen") 35 | 36 | PI = 3.14159265358979 37 | 38 | d = { 39 | integer = 1 40 | string = "hello" 41 | submap = { 42 | stamina = 100 43 | health = 42.0 44 | } 45 | sublist = [1 1 2 3 5 8], 46 | ["this time with spaces"] = PI*2, 47 | [1 + 5] = 1 + 5, 48 | [1 + 9] = 1 + 9 49 | } 50 | 51 | print(d) 52 | print('--- Map iter') 53 | for i in d do print(i, d[i]) end 54 | 55 | print('--- try') 56 | 57 | func bad(x) 58 | print(x) 59 | return x.c() 60 | end 61 | 62 | test1 = {c = func() return 15 end} 63 | test2 = {} 64 | 65 | print(try(bad, test1)) 66 | print(try(bad, test2)) 67 | 68 | print(bad(test1)) 69 | --print(bad(test2)) 70 | 71 | print('--- Induced errors') 72 | 73 | func raise(x) 74 | error(x) 75 | end 76 | 77 | print(try(raise, "lp0 on fire")) 78 | 79 | print('--- Indexing') 80 | 81 | print(d["integer"]) 82 | print(d.integer) 83 | d.integer += 5 84 | print(d.integer) 85 | 86 | print('--- Function binding') 87 | 88 | func outer(a) 89 | func inner(b) 90 | return a+b 91 | end 92 | inner.closure.a = a 93 | return inner 94 | end 95 | 96 | i = outer(5) 97 | print(i(3), i(4), i(5)) 98 | j = outer(8) 99 | print(j(3), j(4), j(5)) 100 | 101 | print('--- Iterators') 102 | 103 | func myiter() 104 | if i>10 then return StopIteration end 105 | i+=1 106 | return i-1 107 | end 108 | myiter.closure.i = 1 109 | 110 | for i in myiter do print(i) end 111 | 112 | print('--- Method calls') 113 | 114 | d = {a = func(a, b) print(a, b) end} 115 | 116 | d.a(1, 2) 117 | d:a(3) 118 | 119 | print('--- Special methods') 120 | 121 | d = {__index = func(obj, key) print('Index', obj, key) return key end, 122 | __setindex = func(obj, key, val) print('SetIndex', obj, key, val) end, 123 | __call = func(obj, arg1, arg2) print('Call', obj, arg1, arg2) return arg1 end} 124 | 125 | print(d[3], d[5]) 126 | 127 | d.a = 7 128 | 129 | print(d("q", "r")) 130 | 131 | e = {a=1, b=2} 132 | d = {__index = e, __setindex = e} 133 | 134 | print(d, d.a, d.b) 135 | 136 | d.c = 5 137 | d.b = 7 138 | 139 | print(d, e) 140 | 141 | print('--- Data sharing') 142 | 143 | d = {} 144 | e = [1 2 3 4 5] 145 | d.a = e 146 | d.b = e 147 | 148 | print(d) 149 | 150 | e[2]=7 151 | e[3]="c" 152 | 153 | print(d) 154 | 155 | d.a:insert(#(d.a), "q") 156 | d.b:remove(1) 157 | d.a[3]="f" 158 | 159 | print(d) 160 | 161 | print('--- Arithmetic structure operations') 162 | 163 | print('ab'+'cd') 164 | print('l'+'ol'*32) 165 | print([1 2 3]+[4 5]) 166 | print([1 2 3]*5) 167 | print({a=1 b=2}+{c=3}) 168 | 169 | print('--- Map/filter') 170 | 171 | l = [1 2 3 4 5] 172 | print(l) 173 | 174 | l:map(func (i) return i*3 end) 175 | print(l) 176 | 177 | l:filter(func (i) return i & 1 end) 178 | print(l) 179 | 180 | print('--- Map/filter chain') 181 | 182 | print([1 2 3 4 5]:map(func (i) return i * 3 end):filter(func (i) return i & 1 end)) 183 | 184 | print('--- Exec/eval') 185 | print('(removed from core language)') 186 | --exec('print("Hello from exec!")') 187 | --print(eval('5 + 3')) 188 | --execfile('programs/subtest.sol') 189 | 190 | print('--- Modulus') 191 | 192 | print(5%3) 193 | print(13%5) 194 | print(15%15) 195 | 196 | print('--- Special function manipulation') 197 | 198 | func foo(x) 199 | return x 200 | end 201 | 202 | print(foo) 203 | foo.name = "bar" 204 | print(foo) 205 | 206 | func something() 207 | return i 208 | end 209 | 210 | something.closure = {i=[1, 2, 3]} 211 | print(something()) 212 | 213 | cl = something.closure 214 | cl.i:insert(0, "b") 215 | print(something()) 216 | 217 | print('--- Function body swapping') 218 | 219 | func a() 220 | return 0 221 | end 222 | 223 | func b() 224 | return 2 225 | end 226 | 227 | print(a, a()) 228 | print(b, b()) 229 | 230 | print(a.stmt) 231 | print(b.stmt) 232 | 233 | temp = a.stmt 234 | a.stmt = b.stmt 235 | b.stmt = temp 236 | 237 | print(a, a()) 238 | print(b, b()) 239 | 240 | newbody = parse('return 4') 241 | print(newbody) 242 | 243 | a.stmt = newbody 244 | b.stmt = newbody 245 | 246 | print(a, a()) 247 | print(b, b()) 248 | 249 | print('--- More complicated ASTs') 250 | 251 | print(outer, outer.stmt, outer.stmt.stmtlist) 252 | 253 | print('--- Exec- and eval-by-parse') 254 | 255 | parse('print("Hello from parse()!")')() 256 | print(parse('5 + 3').stmtlist[0].expr()) 257 | 258 | print('--- Mutating ASTs') 259 | 260 | func f() 261 | return 5 + 7 262 | end 263 | 264 | print(f, f.stmt, f()) 265 | 266 | f.stmt.stmtlist[0].ret.right.ival = 11 267 | 268 | print(f, f.stmt, f()) 269 | 270 | func g() 271 | a=1 272 | b=2 273 | print("a=", a, ", b=", b) 274 | end 275 | 276 | print(g, g.stmt, g()) 277 | 278 | g.stmt.stmtlist[1].expr.value = parse('a').stmtlist[0].expr 279 | 280 | print(g, g.stmt, g()) 281 | 282 | print('--- AST Environments') 283 | 284 | code = parse('print("a is", a, "and b is", b"); a = 4; b = 5') 285 | print(code) 286 | code() 287 | 288 | d = {a=1, b=2} 289 | print(d) 290 | code(d) 291 | print(d) 292 | e = {a="hello", b=["world"]} 293 | print(e) 294 | code(e) 295 | print(e) 296 | 297 | e = {a=1, b=2} 298 | d = {__index = e} 299 | print(d) 300 | print(e) 301 | code(d) 302 | print(d) 303 | print(e) 304 | 305 | print('--- Basic buffers') 306 | 307 | print('(buffer.fromstring = ', buffer.fromstring, ')') 308 | b = buffer.fromstring("Hello, world!") 309 | print(b) 310 | print('(b.get = ', b.get, ')') 311 | print(b:get(buffer.type.cstr)) 312 | b:set(buffer.type.char, "Q") 313 | b:set(buffer.type.char, "L", 2) 314 | print(b:get(buffer.type.cstr)) 315 | print(b:get(buffer.type.cstr, 5)) 316 | print(b:get(buffer.type.uint32)) 317 | --b:set(buffer.type.double, 1.243416560929) 318 | b:set(buffer.type.uint32, 1886545252) 319 | print(b:get(buffer.type.uint32)) 320 | print(b:get(buffer.type.cstr)) 321 | q = buffer.fromaddress(b:address(), b:size()) 322 | print(q:get(buffer.type.cstr)) 323 | q:set(buffer.type.cstr, "Goodbye!") 324 | print(q:get(buffer.type.cstr), b:get(buffer.type.cstr)) 325 | 326 | s = "A string!" 327 | b = buffer.fromobject(s) 328 | prepr(s) 329 | print('...is a', buffer.objtype[b:get(buffer.type.int, 0)]) 330 | print('(buffer.sizeof.ptr = ', buffer.sizeof.ptr, ')') 331 | print('(buffer.sizeof.int = ', buffer.sizeof.int, ')') 332 | print('(buffer.sizeof.int*2 = ', buffer.sizeof.int*2, ')') 333 | print('(buffer.sizeof.int*2 + buffer.sizeof.ptr = ', buffer.sizeof.int*2 + (buffer.sizeof.ptr), ')') 334 | --bs = b:get(buffer.type.ptr, buffer.sizeof.int*2 + (buffer.sizeof.ptr)) 335 | --print('...string buffer:', bs) 336 | --print('...with value:', bs:get(buffer.type.cstr)) 337 | 338 | print('--- IO redirection') 339 | 340 | oldstdout = io.stdout 341 | io.stdout = io.open('stdout', io.MODE_WRITE|io.MODE_TRUNCATE) 342 | 343 | print('A line!') 344 | print('An object:', {a=1, b=2, c="turkey"}) 345 | print('Something mysterious :o') 346 | io.stdout:write('Writing directly to a file :D') 347 | io.stdout:flush() 348 | 349 | io.stdout = oldstdout 350 | 351 | print('...restored stdout.') 352 | 353 | f = io.open('stdout', io.MODE_READ) 354 | s = f:read(io.ALL) 355 | print('Buffered output was:') 356 | prepr(s) 357 | f = None 358 | 359 | print('...second time.') 360 | 361 | io.stdout = io.open('stdout', io.MODE_WRITE|io.MODE_TRUNCATE) 362 | 363 | print('Hey there!') 364 | print('l'+'ol'*32) 365 | io.stdout:flush() 366 | 367 | io.stdout = oldstdout 368 | 369 | print('...restored.') 370 | print('Output was:') 371 | prepr(io.open('stdout', io.MODE_READ):read(io.ALL)) 372 | 373 | print('--- Substrings') 374 | 375 | s = 'This is a test!' 376 | prepr(s) 377 | prepr(s:sub(1, -1)) 378 | prepr(s:sub(3, -3)) 379 | prepr(s:sub(3, 5)) 380 | prepr(s:sub(3, 11)) 381 | prepr(s:sub(-1000, -1000)) 382 | 383 | print('--- Splitting') 384 | s = 'This is a test!' 385 | prepr(s) 386 | prepr(s:split(' ')) 387 | prepr(s:split('i')) 388 | prepr(s:split('0')) 389 | prepr(s:split('aeiou')) 390 | 391 | l = s:split(' ') 392 | for i in l do 393 | prepr(i, type(i)) 394 | end 395 | 396 | print('--- Continue/break') 397 | 398 | l = range(10) 399 | for i in l do 400 | print(i) 401 | if i >= 5 then break end 402 | end 403 | 404 | print('---') 405 | 406 | for i in l do 407 | if i%2 == 0 then continue end 408 | print(i) 409 | end 410 | 411 | print('--- Control expressions') 412 | 413 | print('1 is', if 1 then 'true' else 'false' end) 414 | print('0 is', if 0 then 'true' else 'false' end) 415 | 416 | print(for i in l do continue 1000 + 2 * i end) 417 | print(for i in l do print('(', i, ')') if i >= 5 then break i end end) 418 | 419 | print('--- All done!') 420 | -------------------------------------------------------------------------------- /programs/test_monty.sol: -------------------------------------------------------------------------------- 1 | execfile('monty.sol') 2 | t = tokenizer.new(io.open('/tmp/monty', io.MODE_READ)) 3 | ttg = ttreegen.new(t) 4 | p = parser.new(ttg) 5 | c = converter.new(p) 6 | stmt = c:run() 7 | print('Resulting statement:') 8 | ast.print(stmt) 9 | print('---Running results begin here---') 10 | stmt() 11 | io.stdin:read(io.LINE) 12 | -------------------------------------------------------------------------------- /ser.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "ast.h" 5 | 6 | extern int yydebug; 7 | 8 | char *sol_BytecodeNames[] = { 9 | "BC_NULL", 10 | "BC_ST_EXPR", 11 | "BC_ST_LIST", 12 | "BC_ST_RET", 13 | "BC_ST_CONT", 14 | "BC_ST_BREAK", 15 | "BC_EX_LIT", 16 | "BC_EX_LISTGEN", 17 | "BC_EX_MAPGEN", 18 | "BC_EX_BINOP", 19 | "BC_EX_UNOP", 20 | "BC_EX_INDEX", 21 | "BC_EX_SETINDEX", 22 | "BC_EX_ASSIGN", 23 | "BC_EX_REF", 24 | "BC_EX_CALL", 25 | "BC_EX_FUNCDECL", 26 | "BC_EX_IFELSE", 27 | "BC_EX_LOOP", 28 | "BC_EX_ITER", 29 | "BC_LIT_INT", 30 | "BC_LIT_FLOAT", 31 | "BC_LIT_STRING", 32 | "BC_LIT_BUFFER", 33 | "BC_LIT_NONE", 34 | "BC_INT", 35 | "BC_FLOAT", 36 | "BC_STRING", 37 | "BC_BUFFER", 38 | "BC_LIST_ST", 39 | "BC_LIST_EX", 40 | "BC_LIST_AS", 41 | "BC_LIST_ID", 42 | "BC_LIST_PM", 43 | "BC_ENDLIST", 44 | }; 45 | 46 | void sol_ser_stmt(FILE *io, stmt_node *st) { 47 | if(!st) { 48 | fputc(BC_NULL, io); 49 | return; 50 | } 51 | switch(st->type) { 52 | case ST_EXPR: 53 | fputc(BC_ST_EXPR, io); 54 | sol_ser_expr(io, st->expr); 55 | break; 56 | 57 | case ST_LIST: 58 | fputc(BC_ST_LIST, io); 59 | sol_ser_stl(io, st->stmtlist); 60 | break; 61 | 62 | case ST_RET: 63 | fputc(BC_ST_RET, io); 64 | sol_ser_expr(io, st->ret->ret); 65 | break; 66 | 67 | case ST_CONT: 68 | fputc(BC_ST_CONT, io); 69 | sol_ser_expr(io, st->cont->val); 70 | break; 71 | 72 | case ST_BREAK: 73 | fputc(BC_ST_BREAK, io); 74 | sol_ser_expr(io, st->brk->val); 75 | break; 76 | 77 | default: 78 | printf("WARNING: Unknown statement type to serialize: %d\n", st->type); 79 | break; 80 | } 81 | } 82 | 83 | void sol_ser_stl(FILE *io, stmtlist_node *stl) { 84 | fputc(BC_LIST_ST, io); 85 | while(stl) { 86 | sol_ser_stmt(io, stl->stmt); 87 | stl = stl->next; 88 | } 89 | fputc(BC_ENDLIST, io); 90 | } 91 | 92 | void sol_ser_expr(FILE *io, expr_node *ex) { 93 | if(!ex) { 94 | fputc(BC_NULL, io); 95 | return; 96 | } 97 | switch(ex->type) { 98 | case EX_LIT: 99 | fputc(BC_EX_LIT, io); 100 | sol_ser_lit(io, ex->lit); 101 | break; 102 | 103 | case EX_LISTGEN: 104 | fputc(BC_EX_LISTGEN, io); 105 | sol_ser_exl(io, ex->listgen->list); 106 | break; 107 | 108 | case EX_MAPGEN: 109 | fputc(BC_EX_MAPGEN, io); 110 | sol_ser_asl(io, ex->mapgen->map); 111 | break; 112 | 113 | case EX_BINOP: 114 | fputc(BC_EX_BINOP, io); 115 | fputc(ex->binop->type - OP_ADD, io); 116 | sol_ser_expr(io, ex->binop->left); 117 | sol_ser_expr(io, ex->binop->right); 118 | break; 119 | 120 | case EX_UNOP: 121 | fputc(BC_EX_UNOP, io); 122 | fputc(ex->unop->type - OP_NEG, io); 123 | sol_ser_expr(io, ex->unop->expr); 124 | break; 125 | 126 | case EX_INDEX: 127 | fputc(BC_EX_INDEX, io); 128 | sol_ser_expr(io, ex->index->expr); 129 | sol_ser_expr(io, ex->index->index); 130 | break; 131 | 132 | case EX_SETINDEX: 133 | fputc(BC_EX_SETINDEX, io); 134 | sol_ser_expr(io, ex->setindex->expr); 135 | sol_ser_expr(io, ex->setindex->index); 136 | sol_ser_expr(io, ex->setindex->value); 137 | break; 138 | 139 | case EX_ASSIGN: 140 | fputc(BC_EX_ASSIGN, io); 141 | sol_ser_str(io, ex->assign->ident); 142 | sol_ser_expr(io, ex->assign->value); 143 | break; 144 | 145 | case EX_REF: 146 | fputc(BC_EX_REF, io); 147 | sol_ser_str(io, ex->ref->ident); 148 | break; 149 | 150 | case EX_CALL: 151 | fputc(BC_EX_CALL, io); 152 | sol_ser_expr(io, ex->call->expr); 153 | sol_ser_exl(io, ex->call->args); 154 | sol_ser_str(io, ex->call->method); 155 | break; 156 | 157 | case EX_FUNCDECL: 158 | fputc(BC_EX_FUNCDECL, io); 159 | sol_ser_str(io, ex->funcdecl->name); 160 | sol_ser_pl(io, ex->funcdecl->params); 161 | sol_ser_expr(io, ex->funcdecl->anno); 162 | sol_ser_stmt(io, ex->funcdecl->body); 163 | fwrite(&ex->funcdecl->flags, sizeof(unsigned short), 1, io); 164 | break; 165 | 166 | case EX_IFELSE: 167 | fputc(BC_EX_IFELSE, io); 168 | sol_ser_expr(io, ex->ifelse->cond); 169 | sol_ser_stmt(io, ex->ifelse->iftrue); 170 | sol_ser_stmt(io, ex->ifelse->iffalse); 171 | break; 172 | 173 | case EX_LOOP: 174 | fputc(BC_EX_LOOP, io); 175 | sol_ser_expr(io, ex->loop->cond); 176 | sol_ser_stmt(io, ex->loop->loop); 177 | break; 178 | 179 | case EX_ITER: 180 | fputc(BC_EX_ITER, io); 181 | sol_ser_str(io, ex->iter->var); 182 | sol_ser_expr(io, ex->iter->iter); 183 | sol_ser_stmt(io, ex->iter->loop); 184 | break; 185 | 186 | default: 187 | printf("WARNING: Unknown expression type to serialize: %d\n", ex->type); 188 | break; 189 | } 190 | } 191 | 192 | void sol_ser_exl(FILE *io, exprlist_node *exn) { 193 | fputc(BC_LIST_EX, io); 194 | while(exn) { 195 | sol_ser_expr(io, exn->expr); 196 | exn = exn->next; 197 | } 198 | fputc(BC_ENDLIST, io); 199 | } 200 | 201 | void sol_ser_asl(FILE *io, assoclist_node *asl) { 202 | fputc(BC_LIST_AS, io); 203 | while(asl) { 204 | if(asl->item) { 205 | sol_ser_expr(io, asl->item->key); 206 | sol_ser_expr(io, asl->item->value); 207 | } 208 | asl = asl->next; 209 | } 210 | fputc(BC_ENDLIST, io); 211 | } 212 | 213 | void sol_ser_idl(FILE *io, identlist_node *idl) { 214 | fputc(BC_LIST_ID, io); 215 | while(idl) { 216 | sol_ser_str(io, idl->ident); 217 | idl = idl->next; 218 | } 219 | fputc(BC_ENDLIST, io); 220 | } 221 | 222 | void sol_ser_pl(FILE *io, paramlist_node *pl) { 223 | if(!pl) { 224 | fputc(BC_NULL, io); 225 | return; 226 | } 227 | fputc(BC_LIST_PM, io); 228 | sol_ser_idl(io, pl->args); 229 | sol_ser_exl(io, pl->annos); 230 | sol_ser_idl(io, pl->clkeys); 231 | sol_ser_exl(io, pl->clvalues); 232 | sol_ser_str(io, pl->rest); 233 | fputc(BC_ENDLIST, io); 234 | } 235 | 236 | void sol_ser_lit(FILE *io, lit_node *lit) { 237 | if(!lit) { 238 | fputc(BC_NULL, io); 239 | return; 240 | } 241 | switch(lit->type) { 242 | case LIT_INT: 243 | fputc(BC_LIT_INT, io); 244 | sol_ser_int(io, lit->ival); 245 | break; 246 | 247 | case LIT_FLOAT: 248 | fputc(BC_LIT_FLOAT, io); 249 | sol_ser_float(io, lit->fval); 250 | break; 251 | 252 | case LIT_STRING: 253 | fputc(BC_LIT_STRING, io); 254 | sol_ser_str(io, lit->str); 255 | break; 256 | 257 | case LIT_BUFFER: 258 | fputc(BC_LIT_BUFFER, io); 259 | sol_ser_buf(io, lit->buf); 260 | break; 261 | 262 | case LIT_NONE: 263 | fputc(BC_LIT_NONE, io); 264 | break; 265 | 266 | default: 267 | printf("WARNING: Unknown literal type to serialize: %d\n", lit->type); 268 | break; 269 | } 270 | } 271 | 272 | void sol_ser_str(FILE *io, const char *s) { 273 | size_t len; 274 | if(!s) { 275 | fputc(BC_NULL, io); 276 | return; 277 | } 278 | fputc(BC_STRING, io); 279 | len = strlen(s); 280 | fwrite(&len, sizeof(size_t), 1, io); 281 | fwrite(s, sizeof(char), len, io); 282 | } 283 | 284 | void sol_ser_buf(FILE *io, unsigned long *buf) { 285 | fputc(BC_BUFFER, io); 286 | fwrite(buf, sizeof(unsigned long), 1, io); 287 | fwrite(BYTES_OF(buf), sizeof(char), LENGTH_OF(buf), io); 288 | } 289 | 290 | void sol_ser_int(FILE *io, long i) { 291 | fputc(BC_INT, io); 292 | fwrite(&i, sizeof(long), 1, io); 293 | } 294 | 295 | void sol_ser_float(FILE *io, double f) { 296 | fputc(BC_FLOAT, io); 297 | fwrite(&f, sizeof(double), 1, io); 298 | } 299 | 300 | void *sol_deser_checked(FILE *io, bytecode b) { 301 | int c = fgetc(io); 302 | if(c != b && c != BC_NULL) { 303 | printf("WARNING: Deserialization failed; expected %d, got %d\n", b, c); 304 | } 305 | ungetc(c, io); 306 | return sol_deser(io); 307 | } 308 | 309 | void *sol_deser_stmt(FILE *io) { 310 | int c = fgetc(io); 311 | switch(c) { 312 | default: 313 | printf("WARNING: Deserialization failed; expected stmt type, got %d\n", c); 314 | break; 315 | 316 | case BC_NULL: 317 | case BC_ST_EXPR: 318 | case BC_ST_LIST: 319 | case BC_ST_RET: 320 | case BC_ST_CONT: 321 | case BC_ST_BREAK: 322 | ; 323 | } 324 | ungetc(c, io); 325 | return sol_deser(io); 326 | } 327 | 328 | void *sol_deser_expr(FILE *io) { 329 | int c = fgetc(io); 330 | switch(c) { 331 | default: 332 | printf("WARNING: Deserialization failed; expected expr type, got %d\n", c); 333 | break; 334 | 335 | case BC_NULL: 336 | case BC_EX_LIT: 337 | case BC_EX_LISTGEN: 338 | case BC_EX_MAPGEN: 339 | case BC_EX_BINOP: 340 | case BC_EX_UNOP: 341 | case BC_EX_INDEX: 342 | case BC_EX_SETINDEX: 343 | case BC_EX_ASSIGN: 344 | case BC_EX_REF: 345 | case BC_EX_CALL: 346 | case BC_EX_FUNCDECL: 347 | case BC_EX_IFELSE: 348 | case BC_EX_LOOP: 349 | case BC_EX_ITER: 350 | ; 351 | } 352 | ungetc(c, io); 353 | return sol_deser(io); 354 | } 355 | 356 | void *sol_deser_lit(FILE *io) { 357 | int c = fgetc(io); 358 | switch(c) { 359 | default: 360 | printf("WARNING: Deserialization failed; expected lit type, got %d\n", c); 361 | break; 362 | 363 | case BC_NULL: 364 | case BC_LIT_INT: 365 | case BC_LIT_FLOAT: 366 | case BC_LIT_STRING: 367 | case BC_LIT_BUFFER: 368 | case BC_LIT_NONE: 369 | ; 370 | } 371 | ungetc(c, io); 372 | return sol_deser(io); 373 | } 374 | 375 | void *sol_deser(FILE *io) { 376 | bytecode b = fgetc(io); 377 | void *obj = NULL, *node = NULL; 378 | 379 | if(yydebug) { 380 | fprintf(stderr, "Encountered BC %s", sol_BytecodeNames[b]); 381 | } 382 | 383 | switch(b) { 384 | case BC_NULL: 385 | return NULL; 386 | break; 387 | 388 | case BC_ST_EXPR: 389 | obj = NEW(stmt_node); 390 | AS_ST(obj)->type = ST_EXPR; 391 | AS_ST(obj)->expr = sol_deser_expr(io); 392 | return obj; 393 | break; 394 | 395 | case BC_ST_LIST: 396 | obj = NEW(stmt_node); 397 | AS_ST(obj)->type = ST_LIST; 398 | AS_ST(obj)->stmtlist = sol_deser_checked(io, BC_LIST_ST); 399 | return obj; 400 | 401 | case BC_ST_RET: 402 | obj = NEW(stmt_node); 403 | AS_ST(obj)->type = ST_RET; 404 | AS_ST(obj)->ret = NEW(ret_node); 405 | AS_ST(obj)->ret->ret = sol_deser_expr(io); 406 | return obj; 407 | 408 | case BC_ST_CONT: 409 | obj = NEW(stmt_node); 410 | AS_ST(obj)->type = ST_CONT; 411 | AS_ST(obj)->cont = NEW(cont_node); 412 | AS_ST(obj)->cont->val = sol_deser_expr(io); 413 | return obj; 414 | 415 | case BC_ST_BREAK: 416 | obj = NEW(stmt_node); 417 | AS_ST(obj)->type = ST_BREAK; 418 | AS_ST(obj)->brk = NEW(break_node); 419 | AS_ST(obj)->brk->val = sol_deser_expr(io); 420 | return obj; 421 | 422 | case BC_EX_LIT: 423 | obj = NEW(expr_node); 424 | AS_EX(obj)->type = EX_LIT; 425 | AS_EX(obj)->lit = sol_deser_lit(io); 426 | return obj; 427 | 428 | case BC_EX_LISTGEN: 429 | obj = NEW(expr_node); 430 | AS_EX(obj)->type = EX_LISTGEN; 431 | AS_EX(obj)->listgen = NEW(listgen_node); 432 | AS_EX(obj)->listgen->list = sol_deser_checked(io, BC_LIST_EX); 433 | return obj; 434 | 435 | case BC_EX_MAPGEN: 436 | obj = NEW(expr_node); 437 | AS_EX(obj)->type = EX_MAPGEN; 438 | AS_EX(obj)->mapgen = NEW(mapgen_node); 439 | AS_EX(obj)->mapgen->map = sol_deser_checked(io, BC_LIST_AS); 440 | return obj; 441 | 442 | case BC_EX_BINOP: 443 | obj = NEW(expr_node); 444 | AS_EX(obj)->type = EX_BINOP; 445 | AS_EX(obj)->binop = NEW(binop_node); 446 | AS_EX(obj)->binop->type = OP_ADD + fgetc(io); 447 | AS_EX(obj)->binop->left = sol_deser_expr(io); 448 | AS_EX(obj)->binop->right = sol_deser_expr(io); 449 | return obj; 450 | 451 | case BC_EX_UNOP: 452 | obj = NEW(expr_node); 453 | AS_EX(obj)->type = EX_UNOP; 454 | AS_EX(obj)->unop = NEW(unop_node); 455 | AS_EX(obj)->unop->type = OP_NEG + fgetc(io); 456 | AS_EX(obj)->unop->expr = sol_deser_expr(io); 457 | return obj; 458 | 459 | case BC_EX_INDEX: 460 | obj = NEW(expr_node); 461 | AS_EX(obj)->type = EX_INDEX; 462 | AS_EX(obj)->index = NEW(index_node); 463 | AS_EX(obj)->index->expr = sol_deser_expr(io); 464 | AS_EX(obj)->index->index = sol_deser_expr(io); 465 | return obj; 466 | 467 | case BC_EX_SETINDEX: 468 | obj = NEW(expr_node); 469 | AS_EX(obj)->type = EX_SETINDEX; 470 | AS_EX(obj)->setindex = NEW(setindex_node); 471 | AS_EX(obj)->setindex->expr = sol_deser_expr(io); 472 | AS_EX(obj)->setindex->index = sol_deser_expr(io); 473 | AS_EX(obj)->setindex->value = sol_deser_expr(io); 474 | return obj; 475 | 476 | case BC_EX_ASSIGN: 477 | obj = NEW(expr_node); 478 | AS_EX(obj)->type = EX_ASSIGN; 479 | AS_EX(obj)->assign = NEW(assign_node); 480 | AS_EX(obj)->assign->ident = sol_deser_checked(io, BC_STRING); 481 | AS_EX(obj)->assign->value = sol_deser_expr(io); 482 | return obj; 483 | 484 | case BC_EX_REF: 485 | obj = NEW(expr_node); 486 | AS_EX(obj)->type = EX_REF; 487 | AS_EX(obj)->ref = NEW(ref_node); 488 | AS_EX(obj)->ref->ident = sol_deser_checked(io, BC_STRING); 489 | return obj; 490 | 491 | case BC_EX_CALL: 492 | obj = NEW(expr_node); 493 | AS_EX(obj)->type = EX_CALL; 494 | AS_EX(obj)->call = NEW(call_node); 495 | AS_EX(obj)->call->expr = sol_deser_expr(io); 496 | AS_EX(obj)->call->args = sol_deser_checked(io, BC_LIST_EX); 497 | AS_EX(obj)->call->method = sol_deser_checked(io, BC_STRING); 498 | return obj; 499 | 500 | case BC_EX_FUNCDECL: 501 | obj = NEW(expr_node); 502 | AS_EX(obj)->type = EX_FUNCDECL; 503 | AS_EX(obj)->funcdecl = NEW(funcdecl_node); 504 | AS_EX(obj)->funcdecl->name = sol_deser_checked(io, BC_STRING); 505 | AS_EX(obj)->funcdecl->params = sol_deser_checked(io, BC_LIST_PM); 506 | AS_EX(obj)->funcdecl->anno = sol_deser_expr(io); 507 | AS_EX(obj)->funcdecl->body = sol_deser_stmt(io); 508 | fread(&node, sizeof(unsigned short), 1, io); 509 | AS_EX(obj)->funcdecl->flags = (unsigned short) node; 510 | return obj; 511 | 512 | case BC_EX_IFELSE: 513 | obj = NEW(expr_node); 514 | AS_EX(obj)->type = EX_IFELSE; 515 | AS_EX(obj)->ifelse = NEW(ifelse_node); 516 | AS_EX(obj)->ifelse->cond = sol_deser_expr(io); 517 | AS_EX(obj)->ifelse->iftrue = sol_deser_stmt(io); 518 | AS_EX(obj)->ifelse->iffalse = sol_deser_stmt(io); 519 | return obj; 520 | 521 | case BC_EX_LOOP: 522 | obj = NEW(expr_node); 523 | AS_EX(obj)->type = EX_LOOP; 524 | AS_EX(obj)->loop = NEW(loop_node); 525 | AS_EX(obj)->loop->cond = sol_deser_expr(io); 526 | AS_EX(obj)->loop->loop = sol_deser_stmt(io); 527 | return obj; 528 | 529 | case BC_EX_ITER: 530 | obj = NEW(expr_node); 531 | AS_EX(obj)->type = EX_ITER; 532 | AS_EX(obj)->iter = NEW(iter_node); 533 | AS_EX(obj)->iter->var = sol_deser_checked(io, BC_STRING); 534 | AS_EX(obj)->iter->iter = sol_deser_expr(io); 535 | AS_EX(obj)->iter->loop = sol_deser_stmt(io); 536 | return obj; 537 | 538 | case BC_LIT_INT: 539 | obj = NEW(lit_node); 540 | AS(obj, lit_node)->type = LIT_INT; 541 | node = sol_deser_checked(io, BC_INT); 542 | AS(obj, lit_node)->ival = *AS(node, long); 543 | free(node); 544 | return obj; 545 | 546 | case BC_LIT_FLOAT: 547 | obj = NEW(lit_node); 548 | AS(obj, lit_node)->type = LIT_FLOAT; 549 | node = sol_deser_checked(io, BC_FLOAT); 550 | AS(obj, lit_node)->fval = *AS(node, double); 551 | free(node); 552 | return obj; 553 | 554 | case BC_LIT_STRING: 555 | obj = NEW(lit_node); 556 | AS(obj, lit_node)->type = LIT_STRING; 557 | AS(obj, lit_node)->str = sol_deser_checked(io, BC_STRING); 558 | return obj; 559 | 560 | case BC_LIT_BUFFER: 561 | obj = NEW(lit_node); 562 | AS(obj, lit_node)->type = LIT_BUFFER; 563 | AS(obj, lit_node)->buf = sol_deser_checked(io, BC_BUFFER); 564 | return obj; 565 | 566 | case BC_LIT_NONE: 567 | obj = NEW(lit_node); 568 | AS(obj, lit_node)->type = LIT_NONE; 569 | return obj; 570 | 571 | case BC_INT: 572 | obj = NEW(long); 573 | fread(obj, sizeof(long), 1, io); 574 | return obj; 575 | 576 | case BC_FLOAT: 577 | obj = NEW(double); 578 | fread(obj, sizeof(double), 1, io); 579 | return obj; 580 | 581 | case BC_STRING: 582 | node = NEW(size_t); 583 | fread(node, sizeof(size_t), 1, io); 584 | obj = malloc(*AS(node, size_t) + 1); 585 | fread(obj, sizeof(char), *AS(node, size_t), io); 586 | AS(obj, char)[*AS(node, size_t)] = 0; 587 | free(node); 588 | return obj; 589 | 590 | case BC_BUFFER: 591 | node = NEW(unsigned long); 592 | fread(node, sizeof(unsigned long), 1, io); 593 | obj = malloc(sizeof(unsigned long) + sizeof(char) * (*AS(node, unsigned long))); 594 | LENGTH_OF(obj) = *AS(node, unsigned long); 595 | fread(BYTES_OF(obj), sizeof(char), LENGTH_OF(obj), io); 596 | free(node); 597 | return obj; 598 | 599 | case BC_LIST_ST: 600 | while((b = fgetc(io)) != BC_ENDLIST) { 601 | ungetc(b, io); 602 | if(!node) { 603 | node = NEW(stmtlist_node); 604 | obj = node; 605 | } else { 606 | AS(node, stmtlist_node)->next = NEW(stmtlist_node); 607 | node = AS(node, stmtlist_node)->next; 608 | } 609 | AS(node, stmtlist_node)->stmt = sol_deser_stmt(io); 610 | AS(node, stmtlist_node)->next = NULL; 611 | } 612 | return obj; 613 | 614 | case BC_LIST_EX: 615 | while((b = fgetc(io)) != BC_ENDLIST) { 616 | ungetc(b, io); 617 | if(!node) { 618 | node = NEW(exprlist_node); 619 | obj = node; 620 | } else { 621 | AS(node, exprlist_node)->next = NEW(exprlist_node); 622 | node = AS(node, exprlist_node)->next; 623 | } 624 | AS(node, exprlist_node)->expr = sol_deser_expr(io); 625 | AS(node, exprlist_node)->next = NULL; 626 | } 627 | return obj; 628 | 629 | case BC_LIST_AS: 630 | while((b = fgetc(io)) != BC_ENDLIST) { 631 | ungetc(b, io); 632 | if(!node) { 633 | node = NEW(assoclist_node); 634 | obj = node; 635 | } else { 636 | AS(node, assoclist_node)->next = NEW(assoclist_node); 637 | node = AS(node, assoclist_node)->next; 638 | } 639 | AS(node, assoclist_node)->item = NEW(associtem_node); 640 | AS(node, assoclist_node)->item->key = sol_deser_expr(io); 641 | AS(node, assoclist_node)->item->value = sol_deser_expr(io); 642 | AS(node, assoclist_node)->next = NULL; 643 | } 644 | return obj; 645 | 646 | case BC_LIST_ID: 647 | while((b = fgetc(io)) != BC_ENDLIST) { 648 | ungetc(b, io); 649 | if(!node) { 650 | node = NEW(identlist_node); 651 | obj = node; 652 | } else { 653 | AS(node, identlist_node)->next = NEW(identlist_node); 654 | node = AS(node, identlist_node)->next; 655 | } 656 | AS(node, identlist_node)->ident = sol_deser_checked(io, BC_STRING); 657 | AS(node, identlist_node)->next = NULL; 658 | } 659 | return obj; 660 | 661 | case BC_LIST_PM: 662 | obj = NEW(paramlist_node); 663 | AS(obj, paramlist_node)->args = sol_deser_checked(io, BC_LIST_ID); 664 | AS(obj, paramlist_node)->annos = sol_deser_checked(io, BC_LIST_EX); 665 | AS(obj, paramlist_node)->clkeys = sol_deser_checked(io, BC_LIST_ID); 666 | AS(obj, paramlist_node)->clvalues = sol_deser_checked(io, BC_LIST_EX); 667 | AS(obj, paramlist_node)->rest = sol_deser_checked(io, BC_STRING); 668 | if(fgetc(io) != BC_ENDLIST) { 669 | printf("WARNING: Missed ENDLIST for paramlist_node\n"); 670 | } 671 | return obj; 672 | 673 | default: 674 | printf("WARNING: Illegal bytecode %d\n", b); 675 | break; 676 | } 677 | return NULL; 678 | } 679 | -------------------------------------------------------------------------------- /sol_help.txt: -------------------------------------------------------------------------------- 1 | sol [ [ [ ...]]] 2 | 3 | Sol Executive - compiles and/or runs a Sol program. 4 | 5 | Options given to Sol are given via the first argument. For options that take 6 | parameters, they are read in the order specified in the option argument; e.g., 7 | "./sol ab a.foo b.bar" and "./sol ba b.bar a.foo" are equivalent, assuming 8 | options "a" and "b" take arguments. 9 | 10 | Sol currently recognizes the following options: 11 | 12 | -d: Sets yydebug=1, which enables debug tracing of the parser/lexer. 13 | -D: Sets debugging within the language runtime itself. 14 | -t: Prints the Sol syntax tree after program loading. 15 | -i: Ignore any initialization files. See the information below for the 16 | initialization files this version of Sol is compiled to use. 17 | -r : Read the program from the file, instead of the default (stdin). 18 | Programs run this way have access to stdin via `io.stdin` in the global 19 | environment. 20 | -c : After loading the program, write bytecode to the file, instead of 21 | running the program. Such files can be later loaded using the C option. 22 | -C: Assume that the program file is a compiled bytecode stream instead of Sol 23 | source text. 24 | 25 | Sol's exit status is determined by the following, in this order: 26 | - 0, on printing this help; 27 | - 2, if an argument, parsing, or initialization error occurred; 28 | - 0, if the file was to be compiled and was written successfully; 29 | - 1, if the program in the file encountered an error at the top level; 30 | - the integer value if an integer was returned at the top level; 31 | - 0 otherwise. 32 | 33 | For more information about the language itself, please refer to the source 34 | repository and/or `make docs` therein. 35 | 36 | Sol is Free Software under the Boost Software License, version 1.0; see LICENSE 37 | in the source repository for more details. 38 | -------------------------------------------------------------------------------- /solrun.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "ast.h" 3 | #include "parser.tab.h" 4 | 5 | #ifndef NO_HELP 6 | extern char _binary_sol_help_txt_start, _binary_sol_help_txt_end; 7 | #endif 8 | 9 | int main(int argc, char **argv) { 10 | stmt_node *program; 11 | sol_state_t state; 12 | char *c; 13 | int printtree = 0, clean = 1, argidx = 2; 14 | FILE *prgstream = stdin, *compstream = NULL; 15 | int result = 0, compile = 0, compiled = 0, html = 0; 16 | unsigned i; 17 | 18 | state.features = 0; 19 | 20 | if(argc > 1) { 21 | c = argv[1]; 22 | while(*c) { 23 | switch(*c) { 24 | case 'd': 25 | yydebug = 1; 26 | break; 27 | 28 | case 'D': 29 | state.features |= SOL_FT_DEBUG; 30 | break; 31 | 32 | case 't': 33 | printtree = 1; 34 | break; 35 | 36 | case 'r': 37 | if(argc < argidx) { 38 | printf("r option requires file\n"); 39 | return 2; 40 | } 41 | prgstream = fopen(argv[argidx++], "r"); 42 | break; 43 | 44 | case 'i': 45 | state.features |= SOL_FT_NO_USR_INIT; 46 | break; 47 | 48 | case 'c': 49 | compile = 1; 50 | if(argc < argidx) { 51 | printf("c option requires file\n"); 52 | return 2; 53 | } 54 | compstream = fopen(argv[argidx++], "wb"); 55 | break; 56 | 57 | case 'C': 58 | compiled = 1; 59 | break; 60 | 61 | case 'V': 62 | printf(SOL_BUILD_ID "\n"); 63 | case 'h': 64 | #ifndef NO_HELP 65 | fwrite( 66 | &_binary_sol_help_txt_start, 67 | 1, 68 | &_binary_sol_help_txt_end - &_binary_sol_help_txt_start, 69 | stdout 70 | ); 71 | #else 72 | printf("Sorry, no help in this version (-DNO_HELP)!\n"); 73 | #endif 74 | printf("\nThis Sol is configured to use the following initialization files (it is never an\nerror if these files cannot be read, but errors propagate normally):\n"); 75 | for(i = 0; i < sizeof(sol_AbsInitPaths) / sizeof(sol_AbsInitPaths[0]); i++) 76 | if(sol_AbsInitPaths[i]) 77 | printf("- %s\n", sol_AbsInitPaths[i]); 78 | for(i = 0; i < sizeof(sol_HomeInitPaths) / sizeof(sol_HomeInitPaths[0]); i++) 79 | if(sol_HomeInitPaths[i]) 80 | printf("- $HOME%s\n", sol_HomeInitPaths[i]); 81 | return 0; 82 | break; 83 | 84 | case 'H': 85 | html = 1; 86 | break; 87 | } 88 | c++; 89 | } 90 | } 91 | 92 | if(!prgstream) { 93 | printf("No input program (check filenames)\n"); 94 | return 2; 95 | } 96 | 97 | if(compiled) { 98 | program = sol_deser_stmt(prgstream); 99 | } else { 100 | if(html) { 101 | sol_write_html(prgstream); 102 | return 0; 103 | } 104 | program = sol_compile_file(prgstream); 105 | } 106 | 107 | if(!program) { 108 | printf("NULL program (probably a syntax error)\n"); 109 | return 2; 110 | } 111 | 112 | if(prgstream != stdin) { 113 | fclose(prgstream); 114 | } 115 | 116 | if(compile) { 117 | sol_ser_stmt(compstream, program); 118 | return 0; 119 | } 120 | 121 | if(!sol_state_init(&state)) { 122 | printf("State init error (internal bug)\n"); 123 | result = 2; 124 | clean = 0; 125 | goto out_results; 126 | } 127 | 128 | if(printtree) { 129 | st_print(&state, program); 130 | } 131 | 132 | sol_exec(&state, program); 133 | 134 | out_results: 135 | 136 | if(sol_has_error(&state)) { 137 | printf("Error: "); 138 | ob_print(state.error); 139 | printf("\n"); 140 | result = 1; 141 | } 142 | 143 | if(state.ret) { 144 | printf("Toplevel return: "); 145 | ob_print(state.ret); 146 | printf("\n"); 147 | if(sol_is_int(state.ret)) { 148 | result = state.ret->ival; 149 | } 150 | } 151 | st_free(program); 152 | if(clean) sol_state_cleanup(&state); 153 | 154 | return result; 155 | } 156 | -------------------------------------------------------------------------------- /tests/_fails.sol: -------------------------------------------------------------------------------- 1 | execfile("tests/_lib.sol") 2 | 3 | -- Exit with failure 4 | return 254 5 | -------------------------------------------------------------------------------- /tests/_lib.sol: -------------------------------------------------------------------------------- 1 | func assert(x, msg, _test_count = 0) 2 | _test_count += 1 3 | io.stdout:write('Test ' + tostring(_test_count) + ': ' + msg) 4 | if !x then 5 | print("") 6 | error("Assertion failed: " + tostring(msg)) 7 | end 8 | print("...passed") 9 | end 10 | 11 | func assert_eq(x, y, msg) 12 | assert(x == y, "equality: " + tostring(x) + " == " + tostring(y) + ": " + msg) 13 | end 14 | 15 | func assert_neq(x, y, msg) 16 | assert(x != y, "inequality: " + tostring(x) + " != " + tostring(y) + ": " + msg) 17 | end 18 | 19 | func assert_none(x, msg) 20 | assert(None == x, "None: " + tostring(x) + ": " + msg) 21 | end 22 | 23 | func assert_not_none(x, msg) 24 | assert(None != x, "Not None: " + tostring(x) + ": " + msg) 25 | end 26 | 27 | func warn(msg) 28 | io.stderr:write("Warning: " + tostring(msg) + "\n") 29 | end 30 | 31 | func warn_if(cond, msg) 32 | if cond then 33 | warn(msg) 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /tests/always_passes.sol: -------------------------------------------------------------------------------- 1 | execfile("tests/_lib.sol") 2 | 3 | -- Exit successfully 4 | return 0 5 | -------------------------------------------------------------------------------- /tests/basic_control.sol: -------------------------------------------------------------------------------- 1 | execfile("tests/_lib.sol") 2 | 3 | if 0 then 4 | error("Iftrue on a false constant") 5 | else 6 | print("Iffalse on a false contant (pass)") 7 | end 8 | 9 | if 1 then 10 | print("iftrue on a true constant (pass)") 11 | else 12 | error("iffalse on a true constant") 13 | end 14 | 15 | iters = 0 16 | for i in range(5) do iters += 1 end 17 | assert_eq(iters, 5, "for over range") 18 | 19 | func count(start, stop, step) 20 | if None == step then step = 1 end 21 | func inner() 22 | if i >= stop then return StopIteration end 23 | i += step 24 | return i - 1 25 | end 26 | inner.closure.stop = stop 27 | inner.closure.i = start 28 | inner.closure.step = step 29 | return inner 30 | end 31 | 32 | iters = 0 33 | for i in count(0, 10) do iters += 1 end 34 | assert_eq(iters, 10, "for over iterator function") 35 | 36 | iters = 0 37 | for i in count(0, 10, 2) do iters += 1 end 38 | assert_eq(iters, 5, "for over iterator function") 39 | -------------------------------------------------------------------------------- /tests/basic_equality.sol: -------------------------------------------------------------------------------- 1 | execfile("tests/_lib.sol") 2 | 3 | assert_eq(1, 1, "int cmp") 4 | assert_eq(-3695, -3695, "int cmp") 5 | assert_eq([1, 2, 3], [1, 2, 3], "list cmp") 6 | assert_neq([1, 2, 3], [4, 5, 6], "list cmp (inverse same len)") 7 | assert_neq([1, 2, 3], [], "list cmp (with empty)") 8 | assert_neq([1, 2, 3], [1, 2], "list cmp (with prefix)") 9 | assert_neq([1, 2, 3], [1, 2, 3, 4], "list cmp (as prefix)") 10 | -------------------------------------------------------------------------------- /tests/basic_len.sol: -------------------------------------------------------------------------------- 1 | execfile("tests/_lib.sol") 2 | 3 | assert_eq(0, #[], "empty list") 4 | assert_eq(0, #{}, "empty map") 5 | 6 | assert_eq(2, #[1, 2], "list len") 7 | assert_eq(3, #{a=1, b=2, c=[3, 4, 5]}, "map len") 8 | -------------------------------------------------------------------------------- /tests/basic_logic.sol: -------------------------------------------------------------------------------- 1 | execfile("tests/_lib.sol") 2 | 3 | assert_eq(0 || 0, 0, "LOR") 4 | assert_eq(1 || 0, 1, "LOR") 5 | assert_eq(0 || 1, 1, "LOR") 6 | assert_eq(1 || 1, 1, "LOR") 7 | 8 | assert_eq(0 && 0, 0, "LAND") 9 | assert_eq(1 && 0, 0, "LAND") 10 | assert_eq(0 && 1, 0, "LAND") 11 | assert_eq(1 && 1, 1, "LAND") 12 | -------------------------------------------------------------------------------- /tests/basic_vars.sol: -------------------------------------------------------------------------------- 1 | execfile("tests/_lib.sol") 2 | 3 | a = 3 4 | assert_eq(a, a, "a (reflexive)") 5 | assert_eq(3, a, "a") 6 | assert_eq(a, 3, "a") 7 | 8 | l = [0 9 3 1] 9 | assert_eq(l, l, "l (reflexive)") 10 | assert_eq([0, 9, 3, 1], l, "l") 11 | -------------------------------------------------------------------------------- /tests/bt_range.sol: -------------------------------------------------------------------------------- 1 | execfile("tests/_lib.sol") 2 | 3 | assert_eq([0, 1, 2, 3, 4], range(5), "range 5") 4 | assert_eq(50, #range(50), "len range 50") 5 | assert_eq(0, #range(0), "Empty range") 6 | -------------------------------------------------------------------------------- /tests/crasher_apply_noargs.sol: -------------------------------------------------------------------------------- 1 | execfile("tests/_lib.sol") 2 | 3 | assert_eq(try(apply)[0], 0, "apply with no args causes error") 4 | assert_eq(try(apply, print, 7)[0], 0, "apply with non-list (int) causes error") 5 | assert_eq(try(apply, print, {})[0], 0, "apply with non-list (map) causes error") 6 | assert_eq(try(apply, print, "foo")[0], 0, "apply with non-list (strbuf) causes error") 7 | -------------------------------------------------------------------------------- /tests/crasher_embedded_nul.sol: -------------------------------------------------------------------------------- 1 | execfile("tests/_lib.sol") 2 | 3 | s = "abcdefghijklmnopqrstuvwxyz" 4 | q = "000000000000000000000000" 5 | 6 | assert_eq(s, s, "string equality with embedded NUL") 7 | assert_eq(#s, 27, "string length with embedded NUL") 8 | 9 | assert_eq(q, q, "string equality with embedded NUL 2") 10 | assert_eq(#q, 25, "string length with embedded NUL 2") 11 | -------------------------------------------------------------------------------- /tests/crasher_init_comma.sol: -------------------------------------------------------------------------------- 1 | execfile("tests/_lib.sol") 2 | 3 | assert_eq([0], [,,,,0], "initial comma in list") 4 | d = {,,a=7} 5 | assert_eq(d.a, 7, "initial comma in map") 6 | 7 | ident = lambda(,x) x end 8 | 9 | assert_eq("hi", ident(,"hi"), "initial comma in funcdecl/call") 10 | -------------------------------------------------------------------------------- /tests/crasher_multiple_loops.sol: -------------------------------------------------------------------------------- 1 | execfile("tests/_lib.sol") 2 | 3 | func rec_tostring(v) 4 | return for i in v do 5 | continue if type(i) == "list" then 6 | rec_tostring(i) 7 | else 8 | tostring(i) 9 | end 10 | end 11 | end 12 | 13 | l = [ [1, 2, 3], [4, 5, 6], [7, 8, 9] ] 14 | q = [ ["1", "2", "3"], ["4", "5", "6"], ["7", "8", "9"] ] 15 | 16 | assert_eq(rec_tostring(l), q, "nested continue-map fors") 17 | -------------------------------------------------------------------------------- /tests/crasher_range_noargs.sol: -------------------------------------------------------------------------------- 1 | execfile('tests/_lib.sol') 2 | 3 | assert_eq(try(range)[0], 0, "range with no args causes error") 4 | assert_eq(try(range, [])[0], 0, "range with list arg causes error") 5 | assert_eq(try(range, {})[0], 0, "range with map arg causes error") 6 | -------------------------------------------------------------------------------- /tests/crasher_tco.sol: -------------------------------------------------------------------------------- 1 | execfile("tests/_lib.sol") 2 | 3 | func blow_up_stack(n, accum) 4 | if n > 0 then 5 | return blow_up_stack(n - 1, accum + 1) 6 | else 7 | return accum 8 | end 9 | end 10 | 11 | print('TCO test (will take a long time, sorry)') 12 | assert_eq(blow_up_stack(5, 0), 5, "blow_up_stack 5 deep") 13 | assert_eq(blow_up_stack(5000, 0), 5000, "blow_up_stack 5000 deep") 14 | assert_eq(blow_up_stack(50000, 0), 50000, "blow_up_stack 50000 deep") 15 | assert_eq(assert.closure._test_count, 3, "ran three tests") 16 | -------------------------------------------------------------------------------- /tests/lang_anno.sol: -------------------------------------------------------------------------------- 1 | execfile("tests/_lib.sol") 2 | 3 | func f() end 4 | assert_eq(0, #(f.annos), "no annotations") 5 | 6 | func f(a: 12): -37 end 7 | assert_eq(12, f.annos.a, "arg annotation (int)") 8 | assert_eq(-37, f.annos[f], "func annotation (int)") 9 | 10 | func f(a: "herp"): "derp" end 11 | assert_eq("herp", f.annos.a, "arg annotation (str)") 12 | assert_eq("derp", f.annos[f], "func annotation (str)") 13 | 14 | func f(a: [1, 2], b: {c = [3, 4]}): {d = [12, "herps"]} return "asdfghjkl" end 15 | assert_eq([1, 2], f.annos.a, "composite anno a") 16 | assert_eq([3, 4], f.annos.b.c, "composite anno b") 17 | assert_eq([12, "herps"], f.annos[f].d, "composite func anno") 18 | assert_eq("asdfghjkl", f(), "correct call to anno func") 19 | 20 | func tc(f) 21 | return func(*args, f=f) 22 | for idx in range(#args) do 23 | argn = f.args[idx] 24 | if None == argn then continue end 25 | tspec = f.annos[argn] 26 | if None == tspec then continue end 27 | if type(args[idx]) != tspec then 28 | error("Calling " + tostring(f.name)+ ": arg " + argn + " should be " + tspec) 29 | end 30 | end 31 | return apply(f, args) 32 | end 33 | end 34 | 35 | iadd = tc(lambda(a: 'int', b: 'int'): 'int' a + b end) 36 | 37 | assert_eq(5, iadd(2, 3), "conformance") 38 | assert_eq(0, try(iadd, 2.2, 3)[0], "float a") 39 | assert_eq(0, try(iadd, 3, "string")[0], "str b") 40 | assert_eq(0, try(iadd, [], {})[0], "composite a/b") 41 | -------------------------------------------------------------------------------- /tests/lang_bparen.sol: -------------------------------------------------------------------------------- 1 | execfile("tests/_lib.sol") 2 | 3 | func dont_call_me(a) error("dont_call_me was called: " + tostring(a)) end 4 | 5 | ('after func_decl') -- SHOULD be just a ST_EXPR 6 | assert(1, "After funcdecl") 7 | 8 | dont_call_me 9 | ('after st_expr') 10 | assert(1, "After st_expr") 11 | -------------------------------------------------------------------------------- /tests/lang_elseif.sol: -------------------------------------------------------------------------------- 1 | execfile("tests/_lib.sol") 2 | 3 | f = lambda(x) 4 | if x == 0 then 5 | "foo" 6 | elseif x == 1 then 7 | "bar" 8 | else 9 | "baz" 10 | end 11 | end 12 | 13 | g = lambda(x) 14 | if x == 'a' then 15 | 7 16 | elseif x == 'b' then 17 | 14 18 | end 19 | end 20 | 21 | assert_eq(f(0), "foo", "if/elseif/else, if branch") 22 | assert_eq(f(1), "bar", "if/elseif/else, elseif branch") 23 | assert_eq(f(2), "baz", "if/elseif/else, else branch 1") 24 | assert_eq(f(7), "baz", "if/elseif/else, else branch 2") 25 | 26 | assert_eq(g('a'), 7, "if/elseif, if branch") 27 | assert_eq(g('b'), 14, "if/elseif, elseif branch") 28 | assert_eq(g(3), None, "if/elseif, no branch") 29 | -------------------------------------------------------------------------------- /tests/lang_forex.sol: -------------------------------------------------------------------------------- 1 | execfile("tests/_lib.sol") 2 | 3 | assert_eq(5, for i in range(10) do if i == 5 then break i end end, "break with value") 4 | assert_eq([0, 2, 4, 6, 8], for i in range(5) do continue 2 * i end, "continue with value") 5 | -------------------------------------------------------------------------------- /tests/lang_ifex.sol: -------------------------------------------------------------------------------- 1 | execfile("tests/_lib.sol") 2 | 3 | assert_eq("true", if 1 then "true" else "false" end, "iftrue") 4 | assert_eq("false", if 0 then "true" else "false" end, "iffalse") 5 | -------------------------------------------------------------------------------- /tests/lang_macro.sol: -------------------------------------------------------------------------------- 1 | execfile("tests/_lib.sol") 2 | 3 | macro shitty_eval(x, y) 4 | --ast.print(x) 5 | return [x(), y()] 6 | end 7 | 8 | assert_eq(shitty_eval(3 + 2, 7 + 9), [5, 16], "macro 1") 9 | 10 | macro stupid(ex) 11 | return ex({this_var_does_not_exist = 77}) 12 | end 13 | 14 | assert_eq(stupid(this_var_does_not_exist - 7), 70, "macro 2") 15 | 16 | stuff = macro lambda(n) n({q = 3}) end 17 | assert_eq(stuff(q), 3, "macro lambda 1") 18 | -------------------------------------------------------------------------------- /tests/lang_method.sol: -------------------------------------------------------------------------------- 1 | execfile("tests/_lib.sol") 2 | 3 | func count(v, c=0, lv=0) 4 | c += 1 5 | lv = v 6 | end 7 | 8 | d = {method = count} 9 | d:method() 10 | 11 | assert_eq(d, count.closure.lv, "method called with self") 12 | assert_eq(1, count.closure.c, "method evaluated once (expr is map)") 13 | 14 | func count(c = 0) 15 | c += 1 16 | return {v = c, method = func(self) return self.v end} 17 | end 18 | 19 | assert_eq(count():method(), count.closure.c, "method evaluation consistent") 20 | assert_eq(1, count.closure.c, "method evaluated once (expr is call)") 21 | -------------------------------------------------------------------------------- /tests/lang_scoping.sol: -------------------------------------------------------------------------------- 1 | execfile("tests/_lib.sol") 2 | 3 | _G = debug.globals() 4 | _G.a = 10 5 | assert_eq(a, 10, "debug.globals write") 6 | 7 | b = 7 8 | assert_eq(_G.b, 7, "debug.globals read") 9 | 10 | func() 11 | assert_eq(a, 10, "scope inheritance (a)") 12 | assert_eq(b, 7, "scope inheritance (b)") 13 | 14 | _L = debug.locals() 15 | _L.a = 17 16 | assert_eq(a, 17, "debug.locals write") 17 | 18 | b = 3 19 | assert_eq(_L.b, 3, "debug.locals read") 20 | 21 | assert_eq(#debug.scopes(), 2, "debug scope count") 22 | end() 23 | 24 | assert_eq(a, 10, "scope leak (direct)") 25 | assert_eq(b, 7, "scope leak (direct)") 26 | assert_eq(_G.a, 10, "scope leak (indirect)") 27 | assert_eq(_G.b, 7, "scope leak (indirect)") 28 | 29 | func() 30 | _LG = debug.globals() 31 | _LG.a = 13 32 | _LG.b = 14 33 | assert_eq(a, 13, "scope inheritance after outer write") 34 | assert_eq(b, 14, "scope inheritance after outer write") 35 | 36 | a = 9 37 | b = 7 38 | assert_eq(a, 9, "local write") 39 | assert_eq(b, 7, "local write") 40 | end() 41 | 42 | assert_eq(a, 13, "outer write persistence") 43 | assert_eq(b, 14, "outer write persistence") 44 | 45 | 46 | -- FIXME: Attempting to repr these scopes causes an inf recursion 47 | assert(debug.locals() == debug.globals(), "root scope locals == globals") 48 | -------------------------------------------------------------------------------- /tests/lang_variadic.sol: -------------------------------------------------------------------------------- 1 | execfile("tests/_lib.sol") 2 | 3 | func pop2(a, b, *l) return l end 4 | 5 | assert_eq(pop2(1, 2, 3, 4, 5), [3, 4, 5], "rest param") 6 | assert_eq(pop2(1, 2), [], "rest param (at affinity)") 7 | assert_eq(pop2(), [], "rest param, (below affinity)") 8 | 9 | func count(i = 0) i += 1; return i end 10 | 11 | assert_eq(count(), 1, "count") 12 | assert_eq(count(), 2, "count") 13 | assert_eq(count(), 3, "count") 14 | 15 | func rg(start, stop) return func(i=start, stop=stop) if i >= stop then return end; i += 1; return i - 1 end end 16 | 17 | assert_eq(for i in rg(3, 10) do continue i end, [3, 4, 5, 6, 7, 8, 9], "iter over count") 18 | -------------------------------------------------------------------------------- /tokenizer.lex: -------------------------------------------------------------------------------- 1 | %{ 2 | #define YYSTYPE void * 3 | 4 | #include "ast.h" 5 | #include "parser.tab.h" 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | void yyerror(YYLTYPE *, stmt_node **, char *); 12 | int yywrap(void); 13 | 14 | char *str, *curptr; 15 | int cursz, chars; 16 | #define SZMUL 128 17 | int writing_html = 0; 18 | 19 | void str_init(void) { 20 | str = malloc(SZMUL); 21 | curptr = str; 22 | cursz = SZMUL; 23 | chars = 0; 24 | } 25 | 26 | void str_putc(char c) { 27 | *curptr++ = c; 28 | chars++; 29 | if(chars >= cursz) { 30 | str = realloc(str, cursz + SZMUL); 31 | curptr = str + chars; 32 | cursz += SZMUL; 33 | } 34 | } 35 | 36 | /* http://stackoverflow.com/questions/656703/how-does-flex-support-bison-location-exactly */ 37 | /* Many thanks to hugomg and David Elson! */ 38 | 39 | static void update_loc(YYLTYPE *yylloc, char *yytext){ 40 | int curr_line; 41 | int curr_col; 42 | 43 | curr_line = yylloc->first_line = yylloc->last_line; 44 | curr_col = yylloc->first_column = yylloc->last_column; 45 | 46 | {char * s; for(s = yytext; *s != '\0'; s++){ 47 | if(*s == '\n'){ 48 | curr_line++; 49 | curr_col = 1; 50 | }else{ 51 | curr_col++; 52 | } 53 | }} 54 | 55 | yylloc->last_line = curr_line; 56 | yylloc->last_column = curr_col-1; 57 | } 58 | 59 | char *FONTS[] = { 60 | "Adobe Courier", 61 | "Adobe Helvetica", 62 | "Adobe New Century Schoolbook", 63 | "Adobe Times", 64 | "Andale Mono", 65 | "Arial", 66 | "Arial Black", 67 | "C059", 68 | "Cantarell", 69 | "Century Schoolbook L", 70 | "Comic Sans MS", 71 | "Courier New", 72 | "cursor.pcf", 73 | "D050000L", 74 | "DejaVu Math TeX Gyre", 75 | "DejaVu Sans", 76 | "DejaVu Sans,DejaVu Sans Condensed", 77 | "DejaVu Sans,DejaVu Sans Light", 78 | "DejaVu Sans Mono", 79 | "DejaVu Serif", 80 | "DejaVu Serif,DejaVu Serif Condensed", 81 | "Denemo", 82 | "Dingbats", 83 | "Emmentaler", 84 | "feta26", 85 | "Georgia", 86 | "GNU Unifont", 87 | "GNU Unifont CSUR", 88 | "GNU Unifont Sample", 89 | "Impact", 90 | "Misc Fixed", 91 | "Misc Fixed Wide", 92 | "Nimbus Mono L", 93 | "Nimbus Mono PS", 94 | "Nimbus Roman", 95 | "Nimbus Roman No9 L", 96 | "NimbusSans", 97 | "Nimbus Sans", 98 | "Nimbus Sans L", 99 | "Nimbus Sans Narrow", 100 | "P052", 101 | "Standard Symbols L", 102 | "Standard Symbols PS", 103 | "Times New Roman", 104 | "Trebuchet MS", 105 | "Unifont", 106 | "Unifont CSUR", 107 | "Unifont Sample", 108 | "Unifont Upper", 109 | "URW Bookman", 110 | "URW Bookman L", 111 | "URW Chancery L", 112 | "URW Gothic", 113 | "URW Gothic L", 114 | "URW Palladio L", 115 | "Verdana", 116 | "Webdings", 117 | "Z003", 118 | }; 119 | 120 | static void write_html(char *yytext) { 121 | if(writing_html) { 122 | printf("%s", 123 | FONTS[rand() % (sizeof(FONTS) / sizeof(*FONTS))], 124 | rand() & 1 ? "font-weight: bold;" : "", 125 | rand() & 1 ? "font-style: italic;" : "", 126 | rand() & 1 ? "text-decoration: underline;" : "", 127 | rand() & 1 ? "font-variant: small-caps;" : "", 128 | yytext 129 | ); 130 | } 131 | } 132 | 133 | #define YY_USER_ACTION update_loc(yylloc, yytext); write_html(yytext); 134 | 135 | %} 136 | 137 | DIGIT [0-9] 138 | HEXDIGIT [0-9a-fA-F] 139 | ALPHA [a-zA-Z] 140 | IDENT [a-zA-Z_][a-zA-Z0-9_]* 141 | 142 | /* This is the right way to do it, but it keeps generating token $undefined. 143 | 144 | %x STRING 145 | 146 | \" { str_init(); BEGIN STRING; } 147 | \\n { str_putc('\n'); } 148 | \\t { str_putc('\t'); } 149 | \\b { str_putc('\b'); } 150 | \\r { str_putc('\r'); } 151 | \\x{HEXDIGIT}{HEXDIGIT} { str_putc(strtol(yytext+2, NULL, 16)); } 152 | \\\" { str_putc('"'); } 153 | \" { str_putc('\0'); yylval = str; BEGIN 0; return STRING; } 154 | . { str_putc(*yytext); } 155 | 156 | */ 157 | 158 | %option bison-bridge bison-locations 159 | 160 | %% 161 | 162 | {DIGIT}+"."{DIGIT}* { *yylval = malloc(sizeof(double)); *((double *) *yylval) = atof(yytext); return FLOAT; } 163 | 164 | {DIGIT}+ { *yylval = malloc(sizeof(long)); *((long *) *yylval) = atol(yytext); return INT; } 165 | 166 | \"[^"]*\" | 167 | \'[^']*\' { *yylval = malloc(sizeof(unsigned long) + (yyleng - 2) * sizeof(char)); *((unsigned long *) *yylval) = yyleng - 2; memcpy(((char *) *yylval) + sizeof(unsigned long), yytext + 1, yyleng - 2); return STRING; } 168 | 169 | if { return IF; } 170 | 171 | then { return THEN; } 172 | 173 | else { return ELSE; } 174 | 175 | elseif { return ELSEIF; } 176 | 177 | while { return WHILE; } 178 | 179 | for { return FOR; } 180 | 181 | in { return IN; } 182 | 183 | do { return DO; } 184 | 185 | func { return FUNC; } 186 | 187 | macro { return MACRO; } 188 | 189 | lambda { return LAMBDA; } 190 | 191 | return { return RETURN; } 192 | 193 | break { return BREAK; } 194 | 195 | continue { return CONTINUE; } 196 | 197 | end { return END; } 198 | 199 | None { return NONE; } 200 | 201 | "+" { return PLUS; } 202 | 203 | "-" { return MINUS; } 204 | 205 | "*" { return STAR; } 206 | 207 | "/" { return SLASH; } 208 | 209 | "%" { return PERCENT; } 210 | 211 | "mod" { return PERCENT; } 212 | 213 | "**" { return DSTAR; } 214 | 215 | "&" { return BAND; } 216 | 217 | "|" { return BOR; } 218 | 219 | "^" { return BXOR; } 220 | 221 | "~" { return BNOT; } 222 | 223 | "&&" { return LAND; } 224 | 225 | "and" { return LAND; } 226 | 227 | "||" { return LOR; } 228 | 229 | "or" { return LOR; } 230 | 231 | "!" { return LNOT; } 232 | 233 | "not" { return LNOT; } 234 | 235 | "true" { *yylval = malloc(sizeof(long)); *((long *) *yylval) = 1; return INT; } 236 | 237 | "True" { *yylval = malloc(sizeof(long)); *((long *) *yylval) = 1; return INT; } 238 | 239 | "false" { *yylval = malloc(sizeof(long)); *((long *) *yylval) = 0; return INT; } 240 | 241 | "False" { *yylval = malloc(sizeof(long)); *((long *) *yylval) = 0; return INT; } 242 | 243 | "=" { return ASSIGN; } 244 | 245 | "+=" { return ASSIGNPLUS; } 246 | 247 | "-=" { return ASSIGNMINUS; } 248 | 249 | "*=" { return ASSIGNSTAR; } 250 | 251 | "/=" { return ASSIGNSLASH; } 252 | 253 | "**=" { return ASSIGNDSTAR; } 254 | 255 | "&=" { return ASSIGNBAND; } 256 | 257 | "|=" { return ASSIGNBOR; } 258 | 259 | "^=" { return ASSIGNBXOR; } 260 | 261 | "==" { return EQUAL; } 262 | 263 | "!=" { return NEQUAL; } 264 | 265 | "<" { return LESS; } 266 | 267 | ">" { return GREATER; } 268 | 269 | "<=" { return LESSEQ; } 270 | 271 | ">=" { return GREATEREQ; } 272 | 273 | ">>" { return RSHIFT; } 274 | 275 | "<<" { return LSHIFT; } 276 | 277 | "{" { return LBRACE; } 278 | 279 | "}" { return RBRACE; } 280 | 281 | "[" { return LBRACKET; } 282 | 283 | "]" { return RBRACKET; } 284 | 285 | ^[ \t]*"(" { return BLPAREN; } /* "Breaking" paren, not allowed to introduce a call_expr */ 286 | 287 | "(" { return LPAREN; } 288 | 289 | ")" { return RPAREN; } 290 | 291 | "." { return DOT; } 292 | 293 | ":" { return COLON; } 294 | 295 | ";" { return SEMICOLON; } 296 | 297 | "," { return COMMA; } 298 | 299 | "#" { return POUND; } 300 | 301 | "!!!" { return TBANG; } 302 | 303 | {IDENT} { *yylval = (void *) strdup(yytext); return IDENT; } 304 | 305 | --[^\n]*\n /* Skip comments */ 306 | 307 | [ \t\n]+ /* Skip whitespace */ 308 | 309 | %% 310 | 311 | int yywrap(void) { 312 | return 1; 313 | } 314 | 315 | void yyerror(YYLTYPE *locp, stmt_node **prog, char *err) { 316 | fputs(err, stderr); 317 | fprintf(stderr, "\n(at lines %d-%d, cols %d-%d)\n", locp->first_line, locp->last_line, locp->first_column, locp->last_column); 318 | if(prog && *prog) { 319 | fprintf(stderr, "(while building a stmt of type %d)\n", (*prog)->type); 320 | } 321 | } 322 | 323 | stmt_node *sol_compile(const char *prgstr) { 324 | stmt_node *program = NULL; 325 | YY_BUFFER_STATE buf = yy_scan_string(prgstr); 326 | yyparse(&program); 327 | yy_delete_buffer(buf); 328 | return program; 329 | } 330 | 331 | stmt_node *sol_compile_buffer(const char *prgbuf, size_t sz) { 332 | stmt_node *program = NULL; 333 | YY_BUFFER_STATE buf = yy_scan_bytes(prgbuf, sz); 334 | yyparse(&program); 335 | yy_delete_buffer(buf); 336 | return program; 337 | } 338 | 339 | stmt_node *sol_compile_file(FILE *prgfile) { 340 | stmt_node *program = NULL; 341 | YY_BUFFER_STATE buf = yy_create_buffer(prgfile, YY_BUF_SIZE); 342 | yy_switch_to_buffer(buf); 343 | yyparse(&program); 344 | yy_delete_buffer(buf); 345 | return program; 346 | } 347 | 348 | void sol_write_html(FILE *prgfile) { 349 | stmt_node *program = NULL; 350 | YY_BUFFER_STATE buf = yy_create_buffer(prgfile, YY_BUF_SIZE); 351 | writing_html = 1; 352 | printf("Sol Source File\n"); 353 | yy_switch_to_buffer(buf); 354 | yyparse(&program); 355 | yy_delete_buffer(buf); 356 | //stmt_free(program); 357 | printf("\n"); 358 | } 359 | -------------------------------------------------------------------------------- /util.c: -------------------------------------------------------------------------------- 1 | #include "sol.h" 2 | 3 | #include 4 | 5 | sol_object_t *sol_util_call(sol_state_t *state, sol_object_t *func, int *error, int elems, ...) { 6 | va_list va; 7 | sol_object_t *args = sol_new_list(state), *res = NULL; 8 | int i; 9 | 10 | if(sol_has_error(state)) { 11 | return sol_incref(state->None); 12 | } 13 | if(error) { 14 | *error = 0; 15 | } 16 | 17 | sol_list_insert(state, args, 0, func); 18 | 19 | va_start(va, elems); 20 | for(i = 0; i < elems; i++) { 21 | sol_list_insert(state, args, i + 1, va_arg(va, sol_object_t *)); 22 | } 23 | va_end(va); 24 | 25 | if(!func->ops->call) { 26 | return sol_incref(state->None); 27 | } 28 | res = func->ops->call(state, args); 29 | if(!res) { 30 | res = sol_incref(state->None); 31 | } 32 | if(sol_has_error(state)) { 33 | sol_object_t *err = sol_get_error(state); 34 | sol_clear_error(state); 35 | sol_obj_free(res); 36 | if(error) { 37 | *error = 1; 38 | } 39 | res = err; 40 | } 41 | 42 | return res; 43 | } 44 | --------------------------------------------------------------------------------