├── .todo ├── Makefile ├── README.md ├── samples ├── error.asm ├── gc_test.asm ├── imported.asm ├── linux-64.asm └── osx-32.asm ├── src ├── allocator.c ├── allocator.h ├── allocator_ops.c ├── allocator_ops.h ├── array.c ├── array.h ├── dict.c ├── dict.h ├── directives.c ├── directives.h ├── dis.c ├── dis.h ├── earing.c ├── error.c ├── error.h ├── function.c ├── function.h ├── gen.c ├── grammar.c ├── grammar.h ├── grammar.out ├── grammar.y ├── hash.c ├── hash.h ├── instructions.txt ├── label.c ├── label.h ├── lempar.c ├── lexer.c ├── lexer.rl ├── list.c ├── list.h ├── module.c ├── module.h ├── ops.c ├── ops.h ├── ops_gen.py ├── repl.c ├── repl.h ├── struct.c ├── struct.h ├── token.c ├── token.h ├── tst.c ├── tst.h ├── types.c ├── types.h ├── unsupported.h ├── util.c └── util.h └── tests ├── allocator_tests.c ├── array_tests.c ├── dict_tests.c ├── error_tests.c ├── hash_tests.c ├── list_tests.c ├── module_tests.c ├── repl_tests.c ├── struct_tests.c ├── test_data.txt ├── token_tests.c ├── tst_tests.c ├── types_tests.c └── util_tests.c /.todo: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Implement calling defined functions. 5 | 6 | 7 | Implement jmp instruction and the special move patching needed. 8 | 9 | 10 | Bring over some TST code for the named storage of functions/labels/etc. 11 | 12 | 13 | Setup constant/static data storage primitives for modules. 14 | 15 | 16 | Create the ability to import other modules into a namespace system of some kind. 17 | 18 | 19 | Write a little REPL interpreter using gnu readline. 20 | 21 | todo -d 13 22 | 23 | 24 | 25 | Develop a simple compiled file format that is saved similar to Python's 26 | 27 | totally didn't fucking work. 28 | 29 | 30 | 31 | Create the directives system for registering new directives. 32 | 33 | 34 | Implement a directive for loading shared libs and getting functions from them. 35 | 36 | 37 | Need to have forward declarations so that we can have recursive functions. 38 | 39 | 40 | String and char tokens should strip off the enclosing dquot/squot chars. 41 | 42 | 43 | Create a package format that can hold a bunch of modules with all the things java got wrong. 44 | 45 | 46 | Add namespace stuff to the imports. 47 | 48 | 49 | Gonna need to create a free list that referenced strings all go into, as part of the constants management stuff. 50 | 51 | todo 52 | 53 | 54 | 55 | Restructure the data.c into it's meaningful parts 56 | 57 | 58 | Put the code gen stuff into a separate .c to speed compiling. 59 | 60 | 61 | Add productions to the grammar for leaf functions. 62 | 63 | 64 | Have leaf functions blow up if you try a call. 65 | 66 | 67 | Copy over the defend.h and reuse it here. 68 | 69 | not needed 70 | 71 | 72 | 73 | Double check for right recursion in the grammar. 74 | 75 | 76 | Implement a function call syntax, which will allow for dereferencing functions. 77 | 78 | not needed 79 | 80 | 81 | 82 | Work on the REPL more to make readline work better and to allow function calls. 83 | 84 | 85 | add begin/end markers to function struct 86 | 87 | 88 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS=-g -Wall -I/usr/local/include -Isrc 2 | 3 | OBJECTS=src/tst.o src/repl.o src/dict.o src/list.o src/hash.o src/array.o src/token.o src/struct.o src/types.o src/label.o src/function.o src/gen.o src/error.o src/allocator.o 4 | MAIN=src/earing.o 5 | TESTS=tests/tst_tests.o tests/repl_tests.o tests/module_tests.o tests/array_tests.o tests/token_tests.o tests/struct_tests.o tests/hash_tests.o tests/dict_tests.o tests/list_tests.o tests/error_tests.o tests/allocator_tests.o 6 | TESTS_SRC=tests/tst_tests.c tests/repl_tests.c tests/module_tests.c tests/array_tests.c tests/token_tests.c tests/struct_tests.c tests/hash_tests.c tests/dict_tests.c tests/list_tests.c tests/error_tests.c tests/allocator_tests.c 7 | 8 | # CUT sucks, it should be replaced with minunit 9 | # check: tests/cutcheck.o ${OBJECTS} ${TESTS} 10 | # gcc -Isrc tests/cutcheck.c -o build/cutcheck -lcut -lgc -lcord -ldl ${OBJECTS} ${TESTS} 11 | # ./build/cutcheck 12 | # 13 | # tests/cutcheck.c: ${TESTS_SRC} 14 | # software/cut-2.6/cutgen ${TESTS_SRC} -o tests/cutcheck.c 15 | 16 | src/lexer.c: src/lexer.rl 17 | ragel -C -G2 src/lexer.rl 18 | 19 | src/grammar.c: src/grammar.y 20 | cd src && ../software/lemon -s grammar.y 21 | 22 | gen: 23 | python src/ops_gen.py 24 | 25 | clean: 26 | rm -f tests/*.o src/*.o tests/cutcheck* 27 | 28 | earing: ${OBJECTS} 29 | gcc ${CFLAGS} -L/usr/local/lib -lcord -lgc -ldl -o build/earing ${OBJECST} ${MAIN} /usr/local/lib/libudis86.a 30 | 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | EaRing: It All Started As A Joke 2 | ================================ 3 | 4 | *NOTICE:* This is only on github as a kind of historical record. It hasn't been touched since about 2008 and so probably would need some serious updating to be useful. It will probably not build without some work on your part, and I don't have time. 5 | 6 | WELCOME 7 | ======= 8 | 9 | This is a small project I tinkered on in my spare time as a joke for the 10 | RubyEnRails 2008 conference. It was a riot, but the project actually became 11 | fun to just work on in its own right. 12 | 13 | You'll find many similarities between EaRing and LLVM, except EaRing has fewer 14 | features, is kind of weird, run on fewer platforms, and is tiny by comparison. 15 | 16 | I wouldn't recommend using it in anything serious right now, but it is fun to 17 | check it out and see how it was built. 18 | 19 | WHAT IS IT? 20 | =========== 21 | 22 | EaRing is a dynamic assembler, think of it as a scripting language that uses a 23 | generic CPU-neutral assembly language as the script. It actually does compile 24 | your script on the fly to assembler for your platform, so it run at the speed of 25 | your CPU. 26 | 27 | The important thing about EaRing is that it builds everything on the fly very 28 | fast, and then provides a simple data structure you can use to run the code, 29 | disassemble it, examine the results, etc. Because everything that get generated 30 | is held in data structures there's also some things that EaRing can do other 31 | assemblers can't: 32 | 33 | * You can swap the contents of functions or alter them as if they were data. 34 | * You can plug in any memory management you need. 35 | * It can open dynamic libraries and run the functions like they are another module. 36 | * You can ship the code to other machines and they'll just run it. 37 | * It's still fast because, after the first compile, it run at CPU speed. 38 | 39 | I would consider EaRing a tool for learning assembler and not really something 40 | you'd do a full blown language on. That's what I plan on using it for in the 41 | future. 42 | 43 | 44 | FEATURES 45 | ======== 46 | 47 | * Simple API with good examples. Look at earing.c 48 | * Embeddable into your programs (there are no global variables). 49 | * Simple syntax using a real LR parser with good error messages. 50 | * Interactive REPL and you can make it a #! script too. Yeah, that's weird. 51 | * Constants defined in the assembler are actually not constant. 52 | * Ability to load .so libs and other .asm files and treat them the same. 53 | * You can combine modules as long as the functions don't clash. 54 | * Valgrind pure, so it doesn't leak (except for dlopen and readline's leaks). 55 | * Fast data structures for handling names based on a Ternary Search Tree. 56 | * Written for educational purposes, so the code is easy to read (although not commented yet). 57 | * Uses GNU lightning so runs on a few processors, not tested though. 58 | * Actually works, which is kind of amazing. 59 | * The ability to compile your modules, and then disassemble them to the real x86 bytecodes being used. 60 | 61 | 62 | PLATFORMS 63 | ========= 64 | 65 | Since this is an initial release, I only really got it to work on a few 66 | platforms, but it should run on others: 67 | 68 | * Linux x86-64 AMD. 69 | * Mac OSX x86-32 Intel. Disassembly probably won't work here. 70 | 71 | 72 | LICENSE 73 | ======= 74 | 75 | Copyright (c) 2008, Zed A. Shaw 76 | All rights reserved. 77 | 78 | Redistribution and use in source and binary forms, with or without 79 | modification, are permitted provided that the following conditions are met: 80 | 81 | 1. Redistributions of source code must retain the above copyright 82 | notice, this list of conditions and the following disclaimer. 83 | 2. Redistributions in binary form must reproduce the above copyright 84 | notice, this list of conditions and the following disclaimer in the 85 | documentation and/or other materials provided with the distribution. 86 | 3. All advertising materials mentioning features or use of this software 87 | must display the following acknowledgement: 88 | This product includes software developed by the . 89 | 4. Neither the name of the nor the 90 | names of its contributors may be used to endorse or promote products 91 | derived from this software without specific prior written permission. 92 | 93 | THIS SOFTWARE IS PROVIDED BY ZED A. SHAW ''AS IS'' AND ANY 94 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 95 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 96 | DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 97 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 98 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 99 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 100 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 101 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 102 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 103 | 104 | 105 | INSTALLATION 106 | ============ 107 | 108 | Don't bother, it's bitrot by now so you'd have to work on it. 109 | 110 | There's a bug in how double/float types are handled on 32-bit vs. 64-bit. Still 111 | have to track it down, but you can't use float on 64-bit, and can't use double 112 | on 32-bit. I probably have to change the size of the values in tokens. Anyway, 113 | look at the osx-32.asm to see how libraries are loaded, and check out its main 114 | to see how to do float calls to library functions. Look at linux-64.asm for the 115 | inverse example. 116 | 117 | There was readline support, but readline behaves differently between Linux and 118 | OSX so I removed it until I can learn more about it. 119 | 120 | 121 | CONTRIBUTING 122 | ============ 123 | 124 | If you do pull requests I'll include them, otherwise, this is just a historical 125 | record. 126 | 127 | 128 | -------------------------------------------------------------------------------- /samples/error.asm: -------------------------------------------------------------------------------- 1 | function badoptype : v 2 | movi.blah(R0,R1) 3 | end 4 | 5 | function badfunctype : not 6 | movi.ui(R0,1) 7 | end 8 | 9 | function unterminated : v 10 | 11 | 12 | function recovers : v 13 | 14 | end 15 | 16 | function badtokens : i 17 | movi.ui($$,$$) 18 | end 19 | 20 | function notbad : v 21 | movi.ui(RET, 1) 22 | ret 23 | end 24 | -------------------------------------------------------------------------------- /samples/gc_test.asm: -------------------------------------------------------------------------------- 1 | # This is a little EaRing sample file showing the new GC setup I've added. 2 | # It's pretty rough right now, but if I run this file using: 3 | # 4 | # earing $ build/earing -f forever samples/gc_test.asm > /dev/null 5 | # 6 | # Then it'll loop forever, allocating ram and printing the results, but after 7 | # 2 minutes this is the usage: 8 | # 9 | # 35136 earing 99.3% 1:55.19 1 13 28 388K 184K 1152K 19M 10 | # 11 | # So it's allocating and the GC is working just fine. Boehm is the man. 12 | # 13 | 14 | %library("libc.dylib", "libc") 15 | 16 | ### these are constants, they stick around for the life of the module, but they're more 17 | ### like ASM data regions, meaning you can modify them and shove stuff in them. 18 | 19 | # this is a 'constant', but we can modify it 20 | NUMBERS = "01234567890" 21 | # this is a constant for the length 22 | NUMBER_LEN = 11 # includes the \0 23 | # yep, another one 24 | ONE_K_OF_RAM = 1024 25 | # what's this? just a sample showing using the [size] syntax to make a constant blob of ram 26 | A_CHUNK = [ONE_K_OF_RAM] 27 | 28 | 29 | # This function tests out the new allocator ops by making some ram, reallocing it, 30 | # forcing it freed, forcing the GC to collect, then making and realloc again. To make 31 | # sure the ram was made, it then copies from NUMBERS into this new ram and printing it 32 | # out. 33 | function allocator_test() : void 34 | movi.ui(V1, 0) 35 | 36 | # do a bunch of stupid GC operations 37 | sizeof.i(R2) 38 | sizeof(R2, NUMBERS) 39 | alloc(R2, 1024) # makes 1k, currently have to use a number 40 | realloc(R2, 1) # realloc it to 1 byte 41 | free(R2) # do a force free (not necessary) 42 | alloc(R2, 1024) # allocate some more 43 | realloc(R2, 30) # reallocate it to 30 bytes 44 | 45 | # now we copy NUMBERS into our new chunk of ram 46 | next: 47 | ldxi.c(R0, V1, NUMBERS) 48 | stxr.c(R2, V1, R0) 49 | addi.ui(V1, V1, 1) 50 | blti.ui(next:, V1, NUMBER_LEN) 51 | 52 | # and finally, let's print that out 53 | prepare.i(1) 54 | pusharg.p(R2) 55 | finish(libc.puts) 56 | end 57 | 58 | 59 | # This is a function that just loops forever and runs allocator_test. 60 | # It forces a call to the GC in the loop to test that out. 61 | function forever() : void 62 | again: 63 | calli(self.allocator_test) 64 | jmpi(again:) 65 | end 66 | 67 | # The main funciton just runs allocator_test 1000 times and exits. Easy. 68 | function main() : void 69 | movi.ui(V0, 0) 70 | 71 | again: 72 | calli(self.allocator_test) 73 | addi.ui(V0, V0, 1) 74 | blti.ui(again:, V0, 1000) 75 | end 76 | -------------------------------------------------------------------------------- /samples/imported.asm: -------------------------------------------------------------------------------- 1 | # this is imported by samples/simpler.asm to demonstrate the import system 2 | 3 | function imported_setup() : int 4 | movi.i(RET, 20000) 5 | ret 6 | end 7 | 8 | -------------------------------------------------------------------------------- /samples/linux-64.asm: -------------------------------------------------------------------------------- 1 | %library("libm.so", "math") 2 | %library("/lib/libc.so.6", "libc") 3 | %import("samples/imported.asm", "imported") 4 | # this changes the default code size just before we start the next function 5 | # we can call it at any time before a function to change its code size 6 | %code_size(200) 7 | 8 | # these are part of this module, you can also have them in a function 9 | MY_NAME = "Zed A. Shaw" 10 | MY_AGE = 1200 11 | MY_INITIAL = 'A' 12 | 13 | function puts_sample() : int 14 | # this one is part of a function 15 | another_name = "Joe Blow" 16 | 17 | # prints a simple string with puts 18 | movi.p(R0, another_name) 19 | prepare.i(1) 20 | pusharg.p(R0) 21 | finish(libc.puts) 22 | 23 | # this is a global that we've set 24 | movi.p(R0, MY_NAME) 25 | prepare.i(1) 26 | pusharg.p(R0) 27 | finish(libc.puts) 28 | end 29 | 30 | # Ok, this is just fucking weird, but could be cool as hell when coroutines are in. 31 | # You can access the constants that other functions declare. 32 | function data_sharing_test() : int 33 | movi.p(R0, self.puts_sample.another_name) 34 | prepare.i(1) 35 | pusharg.p(R0) 36 | finish(libc.puts) 37 | end 38 | 39 | %leaf 40 | function test1() : int 41 | movi.i(R1, 10000) 42 | movi.i(R0, 20) 43 | 44 | mulr.i(RET, R1, R0) 45 | end 46 | 47 | %leaf 48 | function test2(): int 49 | movi.i(RET, 100) 50 | end 51 | 52 | function ram_test() : void 53 | movi.ui(R0, 1024) 54 | prepare.i(1) 55 | pusharg.ui(R0) 56 | finish(libc.malloc) 57 | movr.ui(R0, RET) 58 | 59 | prepare.i(1) 60 | pusharg.ui(R0) 61 | finish(libc.free) 62 | end 63 | 64 | # yes, this replaces the code in test1 with test2 and calls it 65 | function make_test1_test2() : int 66 | movi.ui(R1, 0) 67 | 68 | next: 69 | ldxi.c(R0, R1, self.test2) 70 | stxi.c(self.test1, R1, R0) 71 | addi.ui(R1, R1, 1) 72 | blti.ui(next:, R1, 21) 73 | 74 | calli(self.test1) 75 | end 76 | 77 | %leaf 78 | function incrementer() : int 79 | movi.i(R0, 0) 80 | 81 | again: 82 | addi.i(R1, R0, 1) 83 | movr.i(R0, R1) 84 | 85 | blti.i(again:, R0, 10) 86 | 87 | movr.i(RET, R0) 88 | end 89 | 90 | %leaf 91 | function reflect(in : uint) : uint 92 | getarg.ui(RET, in) 93 | end 94 | 95 | %leaf 96 | function fibit(in : ulong) : ulong 97 | getarg.ul(R2, in) 98 | movi.ul(R1, 1) 99 | 100 | blti.ul(exit:, R2, 2) 101 | 102 | subi.ul(R2, R2, 1) 103 | movi.ul(R0, 1) 104 | 105 | loop: 106 | subi.ul(R2, R2, 1) 107 | addr.ul(V0, R0, R1) 108 | movr.ul(R0, R1) 109 | addi.ul(R1, V0, 1) 110 | bnei.ul(loop:, R2, 0) 111 | 112 | exit: 113 | movr.ul(RET, R1) 114 | end 115 | 116 | function fibit_huge_loop() : ulong 117 | movi.ui(V1, 5000000) 118 | 119 | again: 120 | prepare.i(1) 121 | movi.ul(R0, 93) 122 | pusharg.ul(R0) 123 | finish(self.fibit) 124 | retval.ul(R0) 125 | 126 | # decrement and repeat 127 | subi.ui(V1, V1, 1) 128 | bgti.ui(again:, V1, 0) 129 | 130 | # grab the last value as a check 131 | movr.ul(RET, R0) 132 | end 133 | 134 | function print_fibit10() : ulong 135 | prepare.i(1) 136 | movi.ul(R0, 93) 137 | pusharg.ul(R0) 138 | finish(self.fibit) 139 | # can also just be returned right away since RET already has it 140 | retval.ul(R0) 141 | movr.ul(RET, R0) 142 | end 143 | 144 | 145 | function print_reflect() : uint 146 | prepare.i(1) 147 | movi.ui(R0, 93) 148 | pusharg.ui(R0) 149 | finish(self.reflect) 150 | # since we return right away we don't need to do the retval/movr 151 | end 152 | 153 | 154 | function nfibs(in : uint) : uint 155 | getarg.ui(V0, in) # V0 = n 156 | 157 | blti.ui(exit:, V0, 2) 158 | subi.ui(V1, V0, 1) # V1 = n - 1 159 | subi.ui(V2, V0, 2) # V2 = n - 2 160 | 161 | # call first recursion 162 | prepare.i(1) 163 | pusharg.ui(V1) # V1 = nfibs(n-1) 164 | finish(self.nfibs) 165 | retval.i(V1) 166 | 167 | # call second recursion 168 | prepare.i(1) 169 | pusharg.ui(V2) # V2 = nfibs(n-2) 170 | finish(self.nfibs) 171 | retval.i(V2) 172 | 173 | addi.ui(V1, V1, 1) # V1++ 174 | addr.ui(RET, V1, V2) # V1 + V2 + 1 175 | 176 | exit: 177 | movi.i(RET, 1) 178 | end 179 | 180 | 181 | function print_nfibs10() : uint 182 | prepare.i(1) 183 | movi.ui(R0, 10) 184 | pusharg.ui(R0) 185 | finish(self.nfibs) 186 | retval.ui(R0) 187 | movr.ui(RET, R0) 188 | end 189 | 190 | function lots_of_parameters(one : ui, two : ui, three : ui) : void 191 | end 192 | 193 | 194 | function main() : double 195 | calli(self.incrementer) 196 | calli(self.test1) 197 | calli(self.test2) 198 | calli(self.make_test1_test2) 199 | calli(self.test1) 200 | calli(self.print_reflect) 201 | calli(self.print_nfibs10) 202 | calli(self.fibit_huge_loop) 203 | calli(imported.imported_setup) 204 | 205 | movi.d(R0, 10.0) 206 | 207 | prepare.i(1) 208 | pusharg.d(R0) 209 | finish(math.cos) 210 | retval.d(R0) 211 | 212 | movr.d(RET, R0) 213 | end 214 | 215 | -------------------------------------------------------------------------------- /samples/osx-32.asm: -------------------------------------------------------------------------------- 1 | %library("libm.dylib", "math") 2 | %library("libc.dylib", "libc") 3 | %import("samples/imported.asm", "imported") 4 | # this changes the default code size just before we start the next function 5 | # we can call it at any time before a function to change its code size 6 | %code_size(200) 7 | 8 | 9 | # these are part of this module, you can also have them in a function 10 | MY_NAME = "Zed A. Shaw" 11 | MY_AGE = 1200 12 | MY_INITIAL = 'A' 13 | 14 | 15 | 16 | function puts_sample() : int 17 | # this one is part of a function 18 | another_name = "Joe Blow" 19 | 20 | # prints a simple string with puts 21 | movi.p(R0, another_name) 22 | prepare.i(1) 23 | pusharg.p(R0) 24 | finish(libc.puts) 25 | 26 | # this is a global that we've set 27 | movi.p(R0, MY_NAME) 28 | prepare.i(1) 29 | pusharg.p(R0) 30 | finish(libc.puts) 31 | end 32 | 33 | # Ok, this is just fucking weird, but could be cool as hell when coroutines are in. 34 | # You can access the constants that other functions declare. 35 | function data_sharing_test() : int 36 | movi.p(R0, self.puts_sample.another_name) 37 | prepare.i(1) 38 | pusharg.p(R0) 39 | finish(libc.puts) 40 | end 41 | 42 | %leaf 43 | function test1() : int 44 | movi.i(R1, 10000) 45 | movi.i(R0, 20) 46 | 47 | mulr.i(RET, R1, R0) 48 | end 49 | 50 | %leaf 51 | function test2(): int 52 | movi.i(RET, 100) 53 | end 54 | 55 | 56 | 57 | function malloc_test() : void 58 | movi.ui(R0, 1024) 59 | prepare.i(1) 60 | pusharg.ui(R0) 61 | finish(libc.malloc) 62 | movr.ui(R0, RET) 63 | 64 | prepare.i(1) 65 | pusharg.ui(R0) 66 | finish(libc.free) 67 | end 68 | 69 | # yes, this replaces the code in test1 with test2 and calls it 70 | function make_test1_test2() : int 71 | movi.ui(R1, 0) 72 | 73 | next: 74 | ldxi.c(R0, R1, self.test2) 75 | stxi.c(self.test1, R1, R0) 76 | addi.ui(R1, R1, 1) 77 | blti.ui(next:, R1, 21) 78 | 79 | calli(self.test1) 80 | end 81 | 82 | %leaf 83 | function incrementer() : int 84 | movi.i(R0, 0) 85 | 86 | again: 87 | addi.i(R1, R0, 1) 88 | movr.i(R0, R1) 89 | 90 | blti.i(again:, R0, 10) 91 | 92 | movr.i(RET, R0) 93 | end 94 | 95 | %leaf 96 | function reflect(in : uint) : uint 97 | getarg.ui(RET, in) 98 | end 99 | 100 | %leaf 101 | function fibit(in : ulong) : ulong 102 | getarg.ul(R2, in) 103 | movi.ul(R1, 1) 104 | 105 | blti.ul(exit:, R2, 2) 106 | 107 | subi.ul(R2, R2, 1) 108 | movi.ul(R0, 1) 109 | 110 | loop: 111 | subi.ul(R2, R2, 1) 112 | addr.ul(V0, R0, R1) 113 | movr.ul(R0, R1) 114 | addi.ul(R1, V0, 1) 115 | bnei.ul(loop:, R2, 0) 116 | 117 | exit: 118 | movr.ul(RET, R1) 119 | end 120 | 121 | function fibit_huge_loop() : ulong 122 | movi.ui(V1, 5000000) 123 | 124 | again: 125 | prepare.i(1) 126 | movi.ul(R0, 93) 127 | pusharg.ul(R0) 128 | finish(self.fibit) 129 | retval.ul(R0) 130 | 131 | # decrement and repeat 132 | subi.ui(V1, V1, 1) 133 | bgti.ui(again:, V1, 0) 134 | 135 | # grab the last value as a check 136 | movr.ul(RET, R0) 137 | end 138 | 139 | function print_fibit10() : ulong 140 | prepare.i(1) 141 | movi.ul(R0, 93) 142 | pusharg.ul(R0) 143 | finish(self.fibit) 144 | # can also just be returned right away since RET already has it 145 | retval.ul(R0) 146 | movr.ul(RET, R0) 147 | end 148 | 149 | 150 | function print_reflect() : uint 151 | prepare.i(1) 152 | movi.ui(R0, 93) 153 | pusharg.ui(R0) 154 | finish(self.reflect) 155 | # since we return right away we don't need to do the retval/movr 156 | end 157 | 158 | 159 | function nfibs(in : uint) : uint 160 | getarg.ui(V0, in) # V0 = n 161 | 162 | blti.ui(exit:, V0, 2) 163 | subi.ui(V1, V0, 1) # V1 = n - 1 164 | subi.ui(V2, V0, 2) # V2 = n - 2 165 | 166 | # call first recursion 167 | prepare.i(1) 168 | pusharg.ui(V1) # V1 = nfibs(n-1) 169 | finish(self.nfibs) 170 | retval.i(V1) 171 | 172 | # call second recursion 173 | prepare.i(1) 174 | pusharg.ui(V2) # V2 = nfibs(n-2) 175 | finish(self.nfibs) 176 | retval.i(V2) 177 | 178 | addi.ui(V1, V1, 1) # V1++ 179 | addr.ui(RET, V1, V2) # V1 + V2 + 1 180 | 181 | exit: 182 | movi.i(RET, 1) 183 | end 184 | 185 | 186 | function print_nfibs10() : uint 187 | prepare.i(1) 188 | movi.ui(R0, 10) 189 | pusharg.ui(R0) 190 | finish(self.nfibs) 191 | retval.ui(R0) 192 | movr.ui(RET, R0) 193 | end 194 | 195 | function lots_of_parameters(one : ui, two : ui, three : ui) : void 196 | end 197 | 198 | 199 | function main() : float 200 | calli(self.incrementer) 201 | calli(self.test1) 202 | calli(self.test2) 203 | calli(self.make_test1_test2) 204 | calli(self.test1) 205 | calli(self.print_reflect) 206 | calli(self.print_nfibs10) 207 | calli(self.fibit_huge_loop) 208 | calli(imported.imported_setup) 209 | 210 | movi.f(R0, 10.0) 211 | 212 | prepare.i(1) 213 | pusharg.f(R0) 214 | finish(math.cosf) 215 | retval.f(R0) 216 | 217 | movr.f(RET, R0) 218 | end 219 | 220 | -------------------------------------------------------------------------------- /src/allocator.c: -------------------------------------------------------------------------------- 1 | #include "allocator.h" 2 | #include 3 | #include "error.h" 4 | 5 | 6 | Token *Allocator_expression(Module *state, Token *tk) 7 | { 8 | if(!tk) { 9 | die(state, "invalid identifier given to allocation"); 10 | return NULL; 11 | } else { 12 | tk->value = (int)GC_MALLOC(tk->value); 13 | return tk; 14 | } 15 | } 16 | 17 | 18 | void *Allocator_malloc(int size) 19 | { 20 | return GC_MALLOC(size); 21 | } 22 | 23 | void *Allocator_realloc(void *mem, int new_size) 24 | { 25 | return GC_REALLOC(mem, new_size); 26 | } 27 | 28 | void Allocator_free(void *mem) 29 | { 30 | GC_FREE(mem); 31 | } 32 | 33 | void Allocator_collect() 34 | { 35 | GC_gcollect(); 36 | } 37 | -------------------------------------------------------------------------------- /src/allocator.h: -------------------------------------------------------------------------------- 1 | #ifndef allocator_h 2 | #define allocator_h 3 | 4 | #include "token.h" 5 | #include "module.h" 6 | 7 | Token *Allocator_expression(Module *state, Token *tk); 8 | void *Allocator_malloc(int size); 9 | void *Allocator_realloc(void *mem, int new_size); 10 | void Allocator_free(void *mem); 11 | void Allocator_collect(); 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /src/allocator_ops.c: -------------------------------------------------------------------------------- 1 | /* these are included directly into src/ops.c */ 2 | 3 | #include "allocator.h" 4 | 5 | int inst_size_of(Module *state, Token *type, array_t *params) 6 | { 7 | if(type) { 8 | if(array_length(params) != 1) 9 | die(state, "sizeof requires 1 parameter for a type."); 10 | } else { 11 | if(array_length(params) != 2) 12 | die(state, "sizeof requires 2 parameters for data."); 13 | } 14 | 15 | return 1; 16 | } 17 | 18 | int inst_alloc(Module *state, Token *type, array_t *params) 19 | { 20 | if(array_length(params) != 2) 21 | die(state, "alloc requires 2 parameters."); 22 | if(type) die(state, "Operation alloc does not take a type."); 23 | 24 | int size = array_of(Token, params, 1)->value; 25 | 26 | jit_movi_i(array_of(Token, params, 0)->value, size); 27 | jit_prepare_i(1); 28 | jit_pusharg_p(array_of(Token, params, 0)->value); 29 | jit_finish(Allocator_malloc); 30 | jit_retval(array_of(Token, params, 0)->value); 31 | 32 | return 1; 33 | } 34 | 35 | 36 | int inst_realloc(Module *state, Token *type, array_t *params) 37 | { 38 | if(array_length(params) != 2) 39 | die(state, "realloc requires 2 parameters."); 40 | if(type) die(state, "Operation realloc does not take a type."); 41 | 42 | jit_movi_i(JIT_R0, array_of(Token, params, 1)->value); 43 | jit_prepare_i(2); 44 | jit_pusharg_i(JIT_R0); 45 | jit_pusharg_p(array_of(Token, params, 0)->value); 46 | 47 | jit_finish(Allocator_realloc); 48 | jit_retval(array_of(Token, params, 0)->value); 49 | 50 | return 1; 51 | } 52 | 53 | 54 | int inst_free(Module *state, Token *type, array_t *params) 55 | { 56 | if(array_length(params) != 1) 57 | die(state, "free requires 1 parameters."); 58 | if(type) die(state, "Operation free does not take a type."); 59 | 60 | jit_prepare_i(1); 61 | jit_pusharg_p(array_of(Token, params, 0)->value); 62 | jit_calli(Allocator_free); 63 | 64 | return 1; 65 | } 66 | 67 | int inst_collect(Module *state, Token *type, array_t *params) 68 | { 69 | if(array_length(params) != 0) 70 | die(state, "collect requires 0 parameters."); 71 | if(type) die(state, "Operation collect does not take a type."); 72 | 73 | jit_calli(Allocator_collect); 74 | 75 | return 1; 76 | } 77 | -------------------------------------------------------------------------------- /src/allocator_ops.h: -------------------------------------------------------------------------------- 1 | 2 | int inst_size_of(Module *state, Token *type, array_t *args); 3 | int inst_alloc(Module *state, Token *type, array_t *args); 4 | int inst_realloc(Module *state, Token *type, array_t *args); 5 | int inst_free(Module *state, Token *type, array_t *args); 6 | int inst_collect(Module *state, Token *type, array_t *args); 7 | 8 | -------------------------------------------------------------------------------- /src/array.c: -------------------------------------------------------------------------------- 1 | #include "array.h" 2 | #include 3 | #include 4 | 5 | 6 | array_t *array_create(size_t size) 7 | { 8 | array_t *ary = GC_MALLOC(sizeof(array_t) + size * sizeof(void *)); 9 | ary->size = size; 10 | ary->end = 0; 11 | 12 | return ary; 13 | } 14 | 15 | 16 | size_t array_add(array_t *ary, void *el) 17 | { 18 | assert(ary != NULL && "array_t can't be NULL."); 19 | assert(!array_isfull(ary) && "array_t is full."); 20 | assert(el != NULL && "can't put a NULL value in."); 21 | 22 | ary->data[ary->end] = el; 23 | ary->end++; 24 | 25 | return array_length(ary); 26 | } 27 | 28 | 29 | void *array_pop(array_t *ary) 30 | { 31 | assert(ary != NULL && "array_t can't be NULL."); 32 | 33 | if(ary->end == 0) { 34 | return NULL; 35 | } else { 36 | ary->end--; 37 | return ary->data[ary->end]; 38 | } 39 | } 40 | 41 | 42 | array_t *array_extend(array_t *ary, size_t by) 43 | { 44 | assert(ary != NULL && "array_t can't be NULL"); 45 | assert(by != 0 && "can't extend by 0."); 46 | 47 | ary->size += by; 48 | 49 | ary = GC_REALLOC(ary, sizeof(array_t) + ary->size * sizeof(void *)); 50 | 51 | return ary; 52 | } 53 | 54 | 55 | array_t *array_clamp(array_t *ary) 56 | { 57 | assert(ary != NULL && "array_t can't be NULL"); 58 | assert(!array_isempty(ary) && "can't clamp an empty array."); 59 | 60 | ary->size = ary->end; 61 | ary = GC_REALLOC(ary, sizeof(array_t) + ary->size * sizeof(void *)); 62 | 63 | return ary; 64 | } 65 | 66 | 67 | -------------------------------------------------------------------------------- /src/array.h: -------------------------------------------------------------------------------- 1 | #ifndef array_h 2 | #define array_h 3 | 4 | #include 5 | 6 | typedef struct array_t { 7 | size_t size; 8 | size_t end; 9 | 10 | void *data[]; 11 | } array_t; 12 | 13 | array_t *array_create(); 14 | void *array_pop(array_t *ary); 15 | size_t array_add(array_t *ary, void *el); 16 | array_t *array_extend(array_t *ary, size_t by); 17 | array_t *array_clamp(array_t *ary); 18 | 19 | #define array_length(A) ((A)->end) 20 | #define array_max(A) ((A)->size) 21 | #define array_isfull(A) ((A)->end == (A)->size) 22 | #define array_isempty(A) ((A)->end == 0) 23 | #define array_at(A,I) ((A)->data[(I)]) 24 | #define array_push(A, I) array_add((A), (I)) 25 | #define array_last(A) array_at((A), (A)->end-1) 26 | #define array_set(A,I,V) array_at(A,I) = (V) 27 | #define array_of(T, A, I) ((T *)array_at(A, I)) 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /src/dict.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Dictionary Abstract Data Type 3 | * Copyright (C) 1997 Kaz Kylheku 4 | * 5 | * Free Software License: 6 | * 7 | * All rights are reserved by the author, with the following exceptions: 8 | * Permission is granted to freely reproduce and distribute this software, 9 | * possibly in exchange for a fee, provided that this copyright notice appears 10 | * intact. Permission is also granted to adapt this software to produce 11 | * derivative works, as long as the modified versions carry this copyright 12 | * notice and additional notices stating that the work has been modified. 13 | * This source code may be translated into executable form and incorporated 14 | * into proprietary software; there is no requirement for such software to 15 | * contain a copyright notice related to this source. 16 | * 17 | * $Id: dict.h,v 1.22.2.6 2000/11/13 01:36:44 kaz Exp $ 18 | * $Name: kazlib_1_20 $ 19 | */ 20 | 21 | #ifndef DICT_H 22 | #define DICT_H 23 | 24 | #include 25 | #ifdef KAZLIB_SIDEEFFECT_DEBUG 26 | #include "sfx.h" 27 | #endif 28 | 29 | /* 30 | * Blurb for inclusion into C++ translation units 31 | */ 32 | 33 | #ifdef __cplusplus 34 | extern "C" { 35 | #endif 36 | 37 | typedef unsigned long dictcount_t; 38 | #define DICTCOUNT_T_MAX ULONG_MAX 39 | 40 | /* 41 | * The dictionary is implemented as a red-black tree 42 | */ 43 | 44 | typedef enum { dnode_red, dnode_black } dnode_color_t; 45 | 46 | typedef struct dnode_t { 47 | #if defined(DICT_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG) 48 | struct dnode_t *dict_left; 49 | struct dnode_t *dict_right; 50 | struct dnode_t *dict_parent; 51 | dnode_color_t dict_color; 52 | const void *dict_key; 53 | void *dict_data; 54 | #else 55 | int dict_dummy; 56 | #endif 57 | } dnode_t; 58 | 59 | typedef int (*dict_comp_t)(const void *, const void *); 60 | typedef dnode_t *(*dnode_alloc_t)(void *); 61 | typedef void (*dnode_free_t)(dnode_t *, void *); 62 | 63 | typedef struct dict_t { 64 | #if defined(DICT_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG) 65 | dnode_t dict_nilnode; 66 | dictcount_t dict_nodecount; 67 | dictcount_t dict_maxcount; 68 | dict_comp_t dict_compare; 69 | dnode_alloc_t dict_allocnode; 70 | dnode_free_t dict_freenode; 71 | void *dict_context; 72 | int dict_dupes; 73 | #else 74 | int dict_dummmy; 75 | #endif 76 | } dict_t; 77 | 78 | typedef void (*dnode_process_t)(dict_t *, dnode_t *, void *); 79 | 80 | typedef struct dict_load_t { 81 | #if defined(DICT_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG) 82 | dict_t *dict_dictptr; 83 | dnode_t dict_nilnode; 84 | #else 85 | int dict_dummmy; 86 | #endif 87 | } dict_load_t; 88 | 89 | extern dict_t *dict_create(dictcount_t, dict_comp_t); 90 | extern void dict_set_allocator(dict_t *, dnode_alloc_t, dnode_free_t, void *); 91 | extern void dict_destroy(dict_t *); 92 | extern void dict_free_nodes(dict_t *); 93 | extern void dict_free(dict_t *); 94 | extern dict_t *dict_init(dict_t *, dictcount_t, dict_comp_t); 95 | extern void dict_init_like(dict_t *, const dict_t *); 96 | extern int dict_verify(dict_t *); 97 | extern int dict_similar(const dict_t *, const dict_t *); 98 | extern dnode_t *dict_lookup(dict_t *, const void *); 99 | extern dnode_t *dict_lower_bound(dict_t *, const void *); 100 | extern dnode_t *dict_upper_bound(dict_t *, const void *); 101 | extern void dict_insert(dict_t *, dnode_t *, const void *); 102 | extern dnode_t *dict_delete(dict_t *, dnode_t *); 103 | extern int dict_alloc_insert(dict_t *, const void *, void *); 104 | extern void dict_delete_free(dict_t *, dnode_t *); 105 | extern dnode_t *dict_first(dict_t *); 106 | extern dnode_t *dict_last(dict_t *); 107 | extern dnode_t *dict_next(dict_t *, dnode_t *); 108 | extern dnode_t *dict_prev(dict_t *, dnode_t *); 109 | extern dictcount_t dict_count(dict_t *); 110 | extern int dict_isempty(dict_t *); 111 | extern int dict_isfull(dict_t *); 112 | extern int dict_contains(dict_t *, dnode_t *); 113 | extern void dict_allow_dupes(dict_t *); 114 | extern int dnode_is_in_a_dict(dnode_t *); 115 | extern dnode_t *dnode_create(void *); 116 | extern dnode_t *dnode_init(dnode_t *, void *); 117 | extern void dnode_destroy(dnode_t *); 118 | extern void *dnode_get(dnode_t *); 119 | extern const void *dnode_getkey(dnode_t *); 120 | extern void dnode_put(dnode_t *, void *); 121 | extern void dict_process(dict_t *, void *, dnode_process_t); 122 | extern void dict_load_begin(dict_load_t *, dict_t *); 123 | extern void dict_load_next(dict_load_t *, dnode_t *, const void *); 124 | extern void dict_load_end(dict_load_t *); 125 | extern void dict_merge(dict_t *, dict_t *); 126 | 127 | #if defined(DICT_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG) 128 | #ifdef KAZLIB_SIDEEFFECT_DEBUG 129 | #define dict_isfull(D) (SFX_CHECK(D)->dict_nodecount == (D)->dict_maxcount) 130 | #else 131 | #define dict_isfull(D) ((D)->dict_nodecount == (D)->dict_maxcount) 132 | #endif 133 | #define dict_count(D) ((D)->dict_nodecount) 134 | #define dict_isempty(D) ((D)->dict_nodecount == 0) 135 | #define dnode_get(N) ((N)->dict_data) 136 | #define dnode_getkey(N) ((N)->dict_key) 137 | #define dnode_put(N, X) ((N)->dict_data = (X)) 138 | #endif 139 | 140 | #ifdef __cplusplus 141 | } 142 | #endif 143 | 144 | #endif 145 | -------------------------------------------------------------------------------- /src/directives.c: -------------------------------------------------------------------------------- 1 | #include "directives.h" 2 | #include "grammar.h" 3 | #include 4 | #include 5 | #include 6 | #include "tst.h" 7 | #include "util.h" 8 | #include "module.h" 9 | #include "error.h" 10 | #include 11 | #include 12 | #include "list.h" 13 | 14 | 15 | #define ARITY(d,n) assert(list_count(params) == (n) && "Directive " # d " takes " # n " parameter(s).") 16 | #define ARITY_MIN(d,n) assert(list_count(params) >= (n) && "Directive " # d " takes at least " # n " parameter(s).") 17 | #define REQUIRES(d,n,t) assert(TYPE((n),t) && "Directive " # d " takes an " # t " parameter."); 18 | 19 | 20 | #define MAX_LIB_NAME 512 21 | 22 | typedef struct Library { 23 | int len; 24 | char name[MAX_LIB_NAME]; 25 | void *handle; 26 | } Library; 27 | 28 | 29 | /** Finds a FUNCTION that is in a library which has been loaded. */ 30 | void *Library_search(Module *state, Token *module, Token *name) 31 | { 32 | Library *lib = tst_search(state->libraries, module->start, module->len); 33 | if(lib) { 34 | // found it try to find the method 35 | char fname[MAX_LIB_NAME]; 36 | if(Token_copy(name, fname, MAX_LIB_NAME, TK_IDENT) == -1) { 37 | die(state, "Library file name %.*s is too long.", name->len, name->start); 38 | return NULL; 39 | } 40 | void *ptr = dlsym(lib->handle, fname); 41 | 42 | if(ptr) { 43 | return ptr; 44 | } else { 45 | die(state, "module %.*s, error loading library '%.*s': %s.", 46 | module->len, module->start, name->len, name->start, dlerror()); 47 | return NULL; 48 | } 49 | } else { 50 | // not loaded yet, error 51 | return NULL; 52 | } 53 | } 54 | 55 | Library *Library_create(Module *state, Token *name) 56 | { 57 | Library *lib = GC_MALLOC(sizeof(Library)); 58 | lib->len = name->len; 59 | 60 | if(Token_copy(name, lib->name, MAX_LIB_NAME, TK_STR) == -1) { 61 | die(state, "Library name %.*s is too long.", name->len, name->start); 62 | } 63 | 64 | lib->handle = dlopen(lib->name, RTLD_NOW); 65 | 66 | if(!lib->handle) { 67 | die(state, "failed opening library %s: %s", lib->name, dlerror()); 68 | } 69 | 70 | return lib; 71 | } 72 | 73 | void library_call(Module *state, array_t *params) 74 | { 75 | ARITY(library_call, 2); 76 | REQUIRES(library, 0, STR); 77 | REQUIRES(library, 1, STR); 78 | 79 | Token *ident = ARG(0); 80 | Token *as = ARG(1); 81 | 82 | Library *lib = Library_create(state, ident); 83 | 84 | if(lib) { 85 | // came out alright, so now set the name in our list as the second parameter 86 | lib->len = Token_copy(as, lib->name, MAX_LIB_NAME, TK_STR); 87 | if(lib->len == -1) { 88 | die(state, "requested module name %.*s is too long, must be less than %d.", 89 | as->len, as->start, MAX_LIB_NAME); 90 | } 91 | 92 | // store in the global library list 93 | state->libraries = tst_insert(state->libraries, lib->name, lib->len, lib); 94 | } else { 95 | exit(1); 96 | } 97 | } 98 | 99 | 100 | void code_size(Module *state, Parameters *params) 101 | { 102 | ARITY(code_size, 1); 103 | REQUIRES(code_size, 0, INT); 104 | state->max_code_size = VALUE(0, int); 105 | } 106 | 107 | void leaf(Module *state, Parameters *params) 108 | { 109 | ARITY(leaf, 0); 110 | state->current_is_leaf = 1; 111 | } 112 | 113 | void import(Module *state, array_t *params) 114 | { 115 | size_t length = 0; 116 | CORD input = NULL; 117 | Module *target = NULL; 118 | char in_file[MAX_LIB_NAME]; 119 | 120 | ARITY(import, 2); 121 | REQUIRES(import, 0, STR); 122 | REQUIRES(import, 1, STR); 123 | 124 | if(tst_search(state->imports, ARG(1)->data)) { 125 | // already loaded so just skip it 126 | return; 127 | } 128 | 129 | if(Token_copy(ARG(0), in_file, MAX_LIB_NAME, TK_STR) == -1) { 130 | die(state, "requested module name %s is too long, must be less than %d.", 131 | in_file, MAX_LIB_NAME); 132 | } 133 | 134 | input = mmap_file(in_file, &length); 135 | assert(input && "Failed to open the file you requested."); 136 | 137 | // compile the other file using the code size specified by the parent 138 | target = Module_create(in_file, state->max_code_size); 139 | 140 | // have to add it here to prevent recursive loads from going in a loop 141 | state->imports = tst_insert(state->imports, ARG(1)->start, ARG(1)->len, target); 142 | assert(state->imports && "Error importing into the parent namespace."); 143 | 144 | if(!Module_compile(target, input, length)) { 145 | die(target, "error parsing imported module %s.", in_file); 146 | } 147 | } 148 | 149 | 150 | 151 | void Module_register_default_directives(Module *state) 152 | { 153 | Module_register_directive(state, "code_size", strlen("code_size"), code_size, NULL); 154 | Module_register_directive(state, "import", strlen("import"), import, NULL); 155 | Module_register_directive(state, "leaf", strlen("leaf"), leaf, NULL); 156 | Module_register_directive(state, "library", strlen("library"), library_call, NULL); 157 | } 158 | -------------------------------------------------------------------------------- /src/directives.h: -------------------------------------------------------------------------------- 1 | #ifndef directives_h 2 | #define directives_h 3 | 4 | #include "module.h" 5 | 6 | typedef struct Directive { 7 | int len; 8 | CORD name; 9 | Module_directive_cb call; 10 | Module_directive_cb destroy; 11 | } Directive; 12 | 13 | void Module_register_default_directives(Module *state); 14 | 15 | void *Library_search(Module *state, Token *module, Token *name); 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /src/dis.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "module.h" 3 | #include "sglib.h" 4 | 5 | 6 | void dis_code_bytes(Function *func, ud_t *ud_obj) 7 | { 8 | ud_set_input_buffer(ud_obj, func->code, func->code_len); 9 | printf("function %s(", func->name); 10 | 11 | int count; 12 | SGLIB_SORTED_LIST_MAP_ON_ELEMENTS(FunctionParam, func->params, i, next, { 13 | printf("%.*s : %s", i->len, i->name, OpType_to_str(i->type)); 14 | if(count++ < func->nparams-1) printf(", "); 15 | }); 16 | 17 | printf(") : %s\n", OpType_to_str(func->type)); 18 | 19 | while(ud_disassemble(ud_obj)) { 20 | printf("\t%s\n", ud_insn_asm(ud_obj)); 21 | } 22 | printf("end\n\n"); 23 | } 24 | 25 | void dis_functions(Module *state) 26 | { 27 | ud_t ud_obj; 28 | 29 | ud_init(&ud_obj); 30 | ud_set_mode(&ud_obj, 64); 31 | ud_set_syntax(&ud_obj, UD_SYN_INTEL); 32 | ud_set_vendor(&ud_obj, UD_VENDOR_AMD); 33 | 34 | tst_traverse(state->functions, (tst_traverse_cb)dis_code_bytes, &ud_obj); 35 | } 36 | 37 | -------------------------------------------------------------------------------- /src/dis.h: -------------------------------------------------------------------------------- 1 | #ifndef dis_h 2 | #define dis_h 3 | 4 | void dis_functions(Module *state); 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /src/earing.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "directives.h" 12 | #include "module.h" 13 | #include "util.h" 14 | #include "repl.h" 15 | #include 16 | #include "dis.h" 17 | #include 18 | #include "error.h" 19 | #include 20 | #include 21 | 22 | Module *current_module = NULL; 23 | 24 | void query_functions(Function *func, void *ignored) 25 | { 26 | printf("%.*s: %zu bytes of code, %d params defined\n", 27 | func->len, func->name, func->code_len, func->nparams); 28 | } 29 | 30 | void run_repl(Module *state, const char *in_file) { 31 | printf("EaRing. Copyright 2008 Zed A. Shaw.\n"); 32 | printf("Done compiling %s. Enter ? to get the function list.\n", in_file); 33 | char *cmd = NULL; 34 | current_module = state; 35 | repl_init(NULL); 36 | 37 | while((cmd = repl_prompt())) { 38 | if(cmd[0] == '?') { 39 | tst_traverse(state->functions, (tst_traverse_cb)query_functions, NULL); 40 | } else { 41 | run_function(state, cmd); 42 | } 43 | } 44 | } 45 | 46 | 47 | int main(int argc, char *argv[]) 48 | { 49 | CORD input = NULL; 50 | const char *in_file_name = NULL; 51 | FILE *in_file = NULL; 52 | Module *state = NULL; 53 | int opt; 54 | int disassemble = 0; 55 | const char *func = "main"; 56 | int interactive = 0; 57 | int listing = 0; 58 | 59 | while((opt = getopt(argc, argv, "lhdf:i")) != -1) { 60 | switch(opt) { 61 | case 'd': disassemble = 1; break; 62 | case 'f': func = optarg; break; 63 | case 'i': interactive = 1; break; 64 | case 'l': listing = 1; break; 65 | case 'h': /// fall through! 66 | default: 67 | die(NULL, "USAGE: earing [-d | -i] [-f function] \n"); 68 | return 1; 69 | } 70 | } 71 | 72 | if(optind >= argc) { 73 | die(NULL, "You have to give a file. Use -h to see the usage."); 74 | return 1; 75 | } 76 | 77 | GC_INIT(); 78 | 79 | in_file_name = argv[optind]; 80 | in_file = fopen(in_file_name, "r"); 81 | 82 | if(!in_file) { 83 | die(NULL, "Failed to open the input file %s", in_file_name); 84 | } 85 | 86 | input = CORD_from_file(in_file); 87 | 88 | state = Module_create(in_file_name, 1024); 89 | Module_register_default_directives(state); 90 | 91 | if(!Module_compile(state, CORD_to_const_char_star(input), CORD_len(input))) { 92 | die(state, "Parsing failed with %d errors.\n", state->errors); 93 | return 1; 94 | } else { 95 | if(listing) { 96 | tst_traverse(state->functions, (tst_traverse_cb)query_functions, NULL); 97 | } else if(disassemble) { 98 | dis_functions(state); 99 | } else if(interactive) { 100 | // go into interactive mode with the repl 101 | run_repl(state, in_file_name); 102 | } else { 103 | // run the given function or the "main" default 104 | run_function(state, func); 105 | } 106 | } 107 | 108 | return 0; 109 | } 110 | -------------------------------------------------------------------------------- /src/error.c: -------------------------------------------------------------------------------- 1 | #include "error.h" 2 | #include 3 | #include 4 | 5 | static void print_location(Module *state) 6 | { 7 | if(state) { 8 | printf("%s:%d: ", state->module_name, state->curline); 9 | if(state->current) { 10 | CORD_printf("in function %r, ", state->current->name); 11 | } 12 | } 13 | } 14 | 15 | void error(Module *state, const char *format, ...) 16 | { 17 | va_list ap; 18 | va_start(ap, format); 19 | 20 | print_location(state); 21 | CORD_vprintf(format, ap); 22 | 23 | printf("\n"); 24 | } 25 | 26 | 27 | void die(Module *state, const char *format, ...) 28 | { 29 | va_list ap; 30 | va_start(ap, format); 31 | 32 | if(state) { 33 | print_location(state); 34 | } 35 | 36 | CORD_vprintf(format, ap); 37 | printf("\n"); 38 | 39 | exit(1); 40 | } 41 | -------------------------------------------------------------------------------- /src/error.h: -------------------------------------------------------------------------------- 1 | #ifndef error_h 2 | #define error_h 3 | 4 | #include "module.h" 5 | 6 | void error(Module *state, const char *format, ...); 7 | void die(Module *state, const char *format, ...); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /src/function.c: -------------------------------------------------------------------------------- 1 | #include "module.h" 2 | #include "directives.h" 3 | #include "grammar.h" 4 | #include "error.h" 5 | #include "array.h" 6 | #include "hash.h" 7 | #include 8 | #include 9 | #include 10 | 11 | 12 | Function *Function_find(Module *state, CORD name) 13 | { 14 | hnode_t *hn = hash_lookup(state->functions, name); 15 | return hn ? hnode_get(hn) : NULL; 16 | } 17 | 18 | 19 | void FunctionParam_create(Function *func, Token *ident, Token *type) 20 | { 21 | FunctionParam *p = GC_MALLOC(sizeof(FunctionParam)); 22 | assert(p && "Failed to allocate FunctionParam."); 23 | 24 | p->name = ident->data; 25 | p->type = type->value; 26 | 27 | array_add(func->params, p); 28 | } 29 | 30 | 31 | /* Returns a -1 when it doesn't find that parameter. */ 32 | int FunctionParam_index(Function *func, Token *ident) 33 | { 34 | FunctionParam *param = NULL; 35 | size_t i = 0; 36 | 37 | for(i = 0; i < array_length(func->params); i++) { 38 | param = (CORD)array_at(func->params, i); 39 | if(CORD_cmp(param->name, ident->data) == 0) { 40 | return i; 41 | } 42 | } 43 | 44 | return -1; 45 | } 46 | 47 | 48 | Token *Function_resolve_param(Module *state, Function *func, Token *ident) 49 | { 50 | if(!func) return ident; // skip any processing if not in a function 51 | 52 | unsigned long value = (unsigned long)FunctionParam_index(func, ident); 53 | if(value != -1) { 54 | // it's a function parameter 55 | ident->value = value; 56 | } else { 57 | return NULL; 58 | } 59 | 60 | return ident; 61 | } 62 | -------------------------------------------------------------------------------- /src/function.h: -------------------------------------------------------------------------------- 1 | #ifndef function_h 2 | #define function_h 3 | 4 | #include 5 | #include 6 | #include "types.h" 7 | #include "array.h" 8 | #include "hash.h" 9 | #include "token.h" 10 | 11 | struct Module; 12 | 13 | typedef struct FunctionParam { 14 | int index; 15 | CORD name; 16 | OpType type; 17 | } FunctionParam; 18 | 19 | typedef struct Function { 20 | jit_insn *code; 21 | size_t code_len; 22 | jit_code fp; 23 | OpType type; 24 | CORD name; 25 | array_t *params; 26 | 27 | hash_t *data; 28 | hash_t *labels; 29 | } Function; 30 | 31 | Function *Function_create(struct Module *state, OpType type, CORD name, array_t *params); 32 | 33 | Function *Function_find(struct Module *state, CORD name); 34 | 35 | Function *Function_finalize(struct Module *state, Function *func, Token *end); 36 | 37 | Token *Function_resolve_param(struct Module *state, Function *func, Token *ident); 38 | 39 | void FunctionParam_create(Function *func, Token *ident, Token *type); 40 | 41 | int FunctionParam_index(Function *func, Token *ident); 42 | 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /src/gen.c: -------------------------------------------------------------------------------- 1 | #include "module.h" 2 | #include "label.h" 3 | #include "function.h" 4 | #include "grammar.h" 5 | #include "error.h" 6 | #include "hash.h" 7 | #include 8 | #include 9 | 10 | // because of the way GNU lightning is designed we have to keep ALL of the 11 | // code generation tasks in the same file. Since the function management is 12 | // here we also have to include the code ops.c here. 13 | #include "ops.c" 14 | 15 | void Function_synthesize_param(FunctionParam *p) 16 | { 17 | switch(p->type) { 18 | case OpType_i: 19 | p->index = jit_arg_i(); break; 20 | case OpType_ui: 21 | p->index = jit_arg_ui(); break; 22 | case OpType_l: 23 | p->index = jit_arg_l(); break; 24 | case OpType_ul: 25 | p->index = jit_arg_ul(); break; 26 | case OpType_p: 27 | p->index = jit_arg_p(); break; 28 | case OpType_f: 29 | p->index = jit_arg_f(); break; 30 | case OpType_d: 31 | p->index = jit_arg_d(); break; 32 | case OpType_c: 33 | p->index = jit_arg_c(); break; 34 | case OpType_uc: 35 | p->index = jit_arg_uc(); break; 36 | case OpType_s: 37 | p->index = jit_arg_s(); break; 38 | case OpType_us: 39 | p->index = jit_arg_us(); break; 40 | default: 41 | assert(!"Invalid type given for function parameter."); 42 | } 43 | } 44 | 45 | Function *Function_create(struct Module *state, OpType type, CORD name, array_t *params) 46 | { 47 | size_t i = 0; 48 | 49 | if(Function_find(state, name)) { 50 | die(state, "%r is already defined, what you think this is Ruby?.", name); 51 | } 52 | 53 | Function *func = GC_MALLOC(sizeof(Function)); 54 | assert(func && "Failed to allocate the function."); 55 | func->code = GC_MALLOC(state->max_code_size * sizeof(jit_insn)); 56 | func->code_len = state->max_code_size; 57 | func->name = name; 58 | func->type = type; 59 | func->params = params; 60 | 61 | func->fp = jit_set_ip(func->code); 62 | 63 | if(state->current_is_leaf) { 64 | jit_leaf(func->nparams); 65 | } else { 66 | jit_prolog(func->nparams); 67 | } 68 | 69 | if(func->params) { 70 | for(i = 0; i < array_length(func->params); i++) { 71 | Function_synthesize_param(array_at(func->params, i)); 72 | } 73 | } 74 | 75 | // now we just record ourselves in the state for further processing 76 | hash_alloc_insert(state->functions, func->name, func); 77 | state->current = func; 78 | 79 | return func; 80 | } 81 | 82 | Function *Function_finalize(Module *state, Function *func, Token *end) 83 | { 84 | jit_ret(); 85 | 86 | func->code_len = jit_get_ip().ptr - (char *)func->code; 87 | jit_flush_code(func->code, jit_get_ip().ptr); 88 | 89 | state->current = NULL; 90 | state->current_is_leaf = 0; 91 | 92 | return func; 93 | } 94 | 95 | void Label_patch_back_refs(Label *label) 96 | { 97 | int i = 0; 98 | jit_insn *ref = NULL; 99 | 100 | for(i = 0; i < array_length(label->back_refs); i++) { 101 | ref = array_at(label->back_refs, i); 102 | assert(ref && "back_ref was NULL and shouldn't be."); 103 | 104 | jit_patch(ref); 105 | array_set(label->back_refs, i, NULL); 106 | } 107 | } 108 | 109 | Label *Label_create_and_add(Function *func, Token *tk, int realized) 110 | { 111 | assert(tk && "Must give a tk."); 112 | assert(func && "Must have an active function."); 113 | 114 | Label *result = GC_MALLOC(sizeof(Label)); 115 | result->name = tk->data; 116 | result->ref = jit_get_label(); 117 | result->realized = realized; 118 | hash_alloc_insert(func->labels, result->name, result); 119 | 120 | return result; 121 | } 122 | 123 | void Label_statement(Function *func, Token *tk) 124 | { 125 | Label *label = Label_find(func, tk); 126 | if(!label) { 127 | // forward label statement, create realized 128 | label = Label_create_and_add(func, tk, 1); 129 | } else { 130 | // backward label statement 131 | assert(!label->realized && "You've already used this label."); 132 | 133 | // patch the back references 134 | label->realized = 1; 135 | Label_patch_back_refs(label); 136 | label->ref = jit_get_label(); 137 | } 138 | } 139 | 140 | static int Call_param_is(array_t *params, int i, int tk) { 141 | assert(array_length(params) <= i && "array bounds error."); 142 | Token *param = array_at(params, i); 143 | 144 | return param->id == tk; 145 | } 146 | 147 | void Label_add_back_ref(Label *label, jit_insn *ref) 148 | { 149 | array_add(label->back_refs, ref); 150 | } 151 | 152 | void Call_operation(Module *state, Function *func, Token *op, Token *type, array_t *params) 153 | { 154 | // TODO: adapt this to handle any parameter as a label 155 | Label *label = NULL; 156 | Token *label_tk = NULL; 157 | 158 | if(Call_param_is(params, 0, TK_LABEL)) { 159 | label = Label_find(func, array_at(params, 0)); 160 | assert(label && "Label should be found."); 161 | 162 | if(!label->realized) { 163 | // adjust the value of this parameter to the jit_forward for the call 164 | label_tk = array_at(params, 0); 165 | 166 | label_tk->value = (unsigned long)jit_forward(); 167 | } // otherwise value was set by Label_expression 168 | } 169 | 170 | // TODO: expand this 171 | assert(jit_get_ip().ptr - (char *)func->code < func->code_len && "You exceeded the space specified for function code blocks."); 172 | 173 | op->call(state, type, params); 174 | 175 | if(Call_param_is(params, 0, TK_LABEL) && !label->realized) { 176 | // we have to update backrefs here, and not in the label code 177 | // since the label is created outside the opcode run 178 | Label_add_back_ref(label, jit_get_label()); 179 | } 180 | } 181 | 182 | -------------------------------------------------------------------------------- /src/grammar.h: -------------------------------------------------------------------------------- 1 | #define TK_EOL 1 2 | #define TK_IDENT 2 3 | #define TK_EQ 3 4 | #define TK_LPAREN 4 5 | #define TK_RPAREN 5 6 | #define TK_COMMA 6 7 | #define TK_OP 7 8 | #define TK_DOT 8 9 | #define TK_TYPE 9 10 | #define TK_HEX 10 11 | #define TK_FLOAT 11 12 | #define TK_INT 12 13 | #define TK_STR 13 14 | #define TK_CHR 14 15 | #define TK_REG 15 16 | #define TK_LABEL 16 17 | #define TK_LBRACK 17 18 | #define TK_RBRACK 18 19 | #define TK_PERCENT 19 20 | #define TK_FUNCTION 20 21 | #define TK_END 21 22 | #define TK_COLON 22 23 | -------------------------------------------------------------------------------- /src/grammar.out: -------------------------------------------------------------------------------- 1 | State 0: 2 | module ::= * statements 3 | statements ::= * statement 4 | statements ::= * statements statement 5 | statement ::= * directive EOL 6 | statement ::= * constant EOL 7 | statement ::= * function EOL 8 | statement ::= * EOL 9 | constant ::= * IDENT EQ expr 10 | directive ::= * PERCENT IDENT parameters 11 | function ::= * FUNCTION function_decl block END 12 | statements ::= * statements error statement 13 | 14 | EOL shift 47 15 | IDENT shift 21 16 | PERCENT shift 27 17 | FUNCTION shift 11 18 | function shift 20 19 | module accept 20 | statements shift 1 21 | statement shift 54 22 | directive shift 18 23 | constant shift 19 24 | 25 | State 1: 26 | (0) module ::= statements * 27 | statements ::= statements * statement 28 | statement ::= * directive EOL 29 | statement ::= * constant EOL 30 | statement ::= * function EOL 31 | statement ::= * EOL 32 | constant ::= * IDENT EQ expr 33 | directive ::= * PERCENT IDENT parameters 34 | function ::= * FUNCTION function_decl block END 35 | statements ::= statements * error statement 36 | 37 | $ reduce 0 38 | EOL shift 47 39 | IDENT shift 21 40 | PERCENT shift 27 41 | FUNCTION shift 11 42 | error shift 3 43 | function shift 20 44 | statement shift 59 45 | directive shift 18 46 | constant shift 19 47 | 48 | State 2: 49 | operation ::= * OP DOT TYPE parameters 50 | operation ::= * OP parameters 51 | directive ::= * PERCENT IDENT parameters 52 | function ::= FUNCTION function_decl * block END 53 | block ::= * element EOL 54 | block ::= * block element EOL 55 | element ::= * operation 56 | element ::= * directive 57 | element ::= * LABEL 58 | element ::= * IDENT EQ expr 59 | (40) element ::= * 60 | 61 | IDENT shift 30 62 | OP shift 9 63 | LABEL shift 53 64 | PERCENT shift 27 65 | directive shift 52 66 | operation shift 51 67 | block shift 5 68 | element shift 31 69 | {default} reduce 40 70 | 71 | State 3: 72 | statement ::= * directive EOL 73 | statement ::= * constant EOL 74 | statement ::= * function EOL 75 | statement ::= * EOL 76 | constant ::= * IDENT EQ expr 77 | directive ::= * PERCENT IDENT parameters 78 | function ::= * FUNCTION function_decl block END 79 | statements ::= statements error * statement 80 | 81 | EOL shift 47 82 | IDENT shift 21 83 | PERCENT shift 27 84 | FUNCTION shift 11 85 | function shift 20 86 | statement shift 43 87 | directive shift 18 88 | constant shift 19 89 | 90 | State 4: 91 | parameters ::= LPAREN * args RPAREN 92 | args ::= * args COMMA expr 93 | args ::= * expr 94 | (12) args ::= * 95 | expr ::= * HEX 96 | expr ::= * FLOAT 97 | expr ::= * INT 98 | expr ::= * STR 99 | expr ::= * CHR 100 | expr ::= * REG 101 | expr ::= * IDENT 102 | expr ::= * function_reference 103 | expr ::= * function_reference DOT IDENT 104 | expr ::= * LABEL 105 | expr ::= * LBRACK INT RBRACK 106 | expr ::= * LBRACK IDENT RBRACK 107 | function_reference ::= * IDENT DOT IDENT 108 | 109 | IDENT shift 17 110 | HEX shift 42 111 | FLOAT shift 41 112 | INT shift 74 113 | STR shift 71 114 | CHR shift 69 115 | REG shift 68 116 | LABEL shift 60 117 | LBRACK shift 14 118 | args shift 15 119 | function_reference shift 23 120 | expr shift 73 121 | {default} reduce 12 122 | 123 | State 5: 124 | operation ::= * OP DOT TYPE parameters 125 | operation ::= * OP parameters 126 | directive ::= * PERCENT IDENT parameters 127 | function ::= FUNCTION function_decl block * END 128 | block ::= block * element EOL 129 | element ::= * operation 130 | element ::= * directive 131 | element ::= * LABEL 132 | element ::= * IDENT EQ expr 133 | (40) element ::= * 134 | 135 | IDENT shift 30 136 | OP shift 9 137 | LABEL shift 53 138 | PERCENT shift 27 139 | END shift 48 140 | directive shift 52 141 | operation shift 51 142 | element shift 29 143 | {default} reduce 40 144 | 145 | State 6: 146 | args ::= args COMMA * expr 147 | expr ::= * HEX 148 | expr ::= * FLOAT 149 | expr ::= * INT 150 | expr ::= * STR 151 | expr ::= * CHR 152 | expr ::= * REG 153 | expr ::= * IDENT 154 | expr ::= * function_reference 155 | expr ::= * function_reference DOT IDENT 156 | expr ::= * LABEL 157 | expr ::= * LBRACK INT RBRACK 158 | expr ::= * LBRACK IDENT RBRACK 159 | function_reference ::= * IDENT DOT IDENT 160 | 161 | IDENT shift 17 162 | HEX shift 42 163 | FLOAT shift 41 164 | INT shift 74 165 | STR shift 71 166 | CHR shift 69 167 | REG shift 68 168 | LABEL shift 60 169 | LBRACK shift 14 170 | function_reference shift 23 171 | expr shift 72 172 | 173 | State 7: 174 | expr ::= * HEX 175 | expr ::= * FLOAT 176 | expr ::= * INT 177 | expr ::= * STR 178 | expr ::= * CHR 179 | expr ::= * REG 180 | expr ::= * IDENT 181 | expr ::= * function_reference 182 | expr ::= * function_reference DOT IDENT 183 | expr ::= * LABEL 184 | expr ::= * LBRACK INT RBRACK 185 | expr ::= * LBRACK IDENT RBRACK 186 | function_reference ::= * IDENT DOT IDENT 187 | element ::= IDENT EQ * expr 188 | 189 | IDENT shift 17 190 | HEX shift 42 191 | FLOAT shift 41 192 | INT shift 74 193 | STR shift 71 194 | CHR shift 69 195 | REG shift 68 196 | LABEL shift 60 197 | LBRACK shift 14 198 | function_reference shift 23 199 | expr shift 56 200 | 201 | State 8: 202 | constant ::= IDENT EQ * expr 203 | expr ::= * HEX 204 | expr ::= * FLOAT 205 | expr ::= * INT 206 | expr ::= * STR 207 | expr ::= * CHR 208 | expr ::= * REG 209 | expr ::= * IDENT 210 | expr ::= * function_reference 211 | expr ::= * function_reference DOT IDENT 212 | expr ::= * LABEL 213 | expr ::= * LBRACK INT RBRACK 214 | expr ::= * LBRACK IDENT RBRACK 215 | function_reference ::= * IDENT DOT IDENT 216 | 217 | IDENT shift 17 218 | HEX shift 42 219 | FLOAT shift 41 220 | INT shift 74 221 | STR shift 71 222 | CHR shift 69 223 | REG shift 68 224 | LABEL shift 60 225 | LBRACK shift 14 226 | function_reference shift 23 227 | expr shift 44 228 | 229 | State 9: 230 | parameters ::= * LPAREN args RPAREN 231 | (9) parameters ::= * 232 | operation ::= OP * DOT TYPE parameters 233 | operation ::= OP * parameters 234 | 235 | LPAREN shift 4 236 | DOT shift 28 237 | parameters shift 46 238 | {default} reduce 9 239 | 240 | State 10: 241 | function_decl ::= IDENT LPAREN * function_params RPAREN COLON TYPE 242 | function_params ::= * IDENT COLON TYPE 243 | function_params ::= * function_params COMMA IDENT COLON TYPE 244 | (33) function_params ::= * 245 | 246 | IDENT shift 38 247 | function_params shift 16 248 | {default} reduce 33 249 | 250 | State 11: 251 | function ::= FUNCTION * function_decl block END 252 | function_decl ::= * IDENT LPAREN function_params RPAREN COLON TYPE 253 | 254 | IDENT shift 32 255 | function_decl shift 2 256 | 257 | State 12: 258 | parameters ::= * LPAREN args RPAREN 259 | (9) parameters ::= * 260 | directive ::= PERCENT IDENT * parameters 261 | 262 | LPAREN shift 4 263 | parameters shift 40 264 | {default} reduce 9 265 | 266 | State 13: 267 | parameters ::= * LPAREN args RPAREN 268 | (9) parameters ::= * 269 | operation ::= OP DOT TYPE * parameters 270 | 271 | LPAREN shift 4 272 | parameters shift 45 273 | {default} reduce 9 274 | 275 | State 14: 276 | expr ::= LBRACK * INT RBRACK 277 | expr ::= LBRACK * IDENT RBRACK 278 | 279 | IDENT shift 26 280 | INT shift 25 281 | 282 | State 15: 283 | parameters ::= LPAREN args * RPAREN 284 | args ::= args * COMMA expr 285 | 286 | RPAREN shift 70 287 | COMMA shift 6 288 | 289 | State 16: 290 | function_decl ::= IDENT LPAREN function_params * RPAREN COLON TYPE 291 | function_params ::= function_params * COMMA IDENT COLON TYPE 292 | 293 | RPAREN shift 33 294 | COMMA shift 35 295 | 296 | State 17: 297 | (21) expr ::= IDENT * 298 | function_reference ::= IDENT * DOT IDENT 299 | 300 | DOT shift 22 301 | {default} reduce 21 302 | 303 | State 18: 304 | statement ::= directive * EOL 305 | 306 | EOL shift 64 307 | 308 | State 19: 309 | statement ::= constant * EOL 310 | 311 | EOL shift 62 312 | 313 | State 20: 314 | statement ::= function * EOL 315 | 316 | EOL shift 55 317 | 318 | State 21: 319 | constant ::= IDENT * EQ expr 320 | 321 | EQ shift 8 322 | 323 | State 22: 324 | function_reference ::= IDENT DOT * IDENT 325 | 326 | IDENT shift 67 327 | 328 | State 23: 329 | (22) expr ::= function_reference * 330 | expr ::= function_reference * DOT IDENT 331 | 332 | DOT shift 24 333 | {default} reduce 22 334 | 335 | State 24: 336 | expr ::= function_reference DOT * IDENT 337 | 338 | IDENT shift 61 339 | 340 | State 25: 341 | expr ::= LBRACK INT * RBRACK 342 | 343 | RBRACK shift 63 344 | 345 | State 26: 346 | expr ::= LBRACK IDENT * RBRACK 347 | 348 | RBRACK shift 65 349 | 350 | State 27: 351 | directive ::= PERCENT * IDENT parameters 352 | 353 | IDENT shift 12 354 | 355 | State 28: 356 | operation ::= OP DOT * TYPE parameters 357 | 358 | TYPE shift 13 359 | 360 | State 29: 361 | block ::= block element * EOL 362 | 363 | EOL shift 50 364 | 365 | State 30: 366 | element ::= IDENT * EQ expr 367 | 368 | EQ shift 7 369 | 370 | State 31: 371 | block ::= element * EOL 372 | 373 | EOL shift 58 374 | 375 | State 32: 376 | function_decl ::= IDENT * LPAREN function_params RPAREN COLON TYPE 377 | 378 | LPAREN shift 10 379 | 380 | State 33: 381 | function_decl ::= IDENT LPAREN function_params RPAREN * COLON TYPE 382 | 383 | COLON shift 34 384 | 385 | State 34: 386 | function_decl ::= IDENT LPAREN function_params RPAREN COLON * TYPE 387 | 388 | TYPE shift 49 389 | 390 | State 35: 391 | function_params ::= function_params COMMA * IDENT COLON TYPE 392 | 393 | IDENT shift 36 394 | 395 | State 36: 396 | function_params ::= function_params COMMA IDENT * COLON TYPE 397 | 398 | COLON shift 37 399 | 400 | State 37: 401 | function_params ::= function_params COMMA IDENT COLON * TYPE 402 | 403 | TYPE shift 66 404 | 405 | State 38: 406 | function_params ::= IDENT * COLON TYPE 407 | 408 | COLON shift 39 409 | 410 | State 39: 411 | function_params ::= IDENT COLON * TYPE 412 | 413 | TYPE shift 57 414 | 415 | State 40: 416 | (28) directive ::= PERCENT IDENT parameters * 417 | 418 | {default} reduce 28 419 | 420 | State 41: 421 | (16) expr ::= FLOAT * 422 | 423 | {default} reduce 16 424 | 425 | State 42: 426 | (15) expr ::= HEX * 427 | 428 | {default} reduce 15 429 | 430 | State 43: 431 | (41) statements ::= statements error statement * 432 | 433 | {default} reduce 41 434 | 435 | State 44: 436 | (7) constant ::= IDENT EQ expr * 437 | 438 | {default} reduce 7 439 | 440 | State 45: 441 | (13) operation ::= OP DOT TYPE parameters * 442 | 443 | {default} reduce 13 444 | 445 | State 46: 446 | (14) operation ::= OP parameters * 447 | 448 | {default} reduce 14 449 | 450 | State 47: 451 | (6) statement ::= EOL * 452 | 453 | {default} reduce 6 454 | 455 | State 48: 456 | (29) function ::= FUNCTION function_decl block END * 457 | 458 | {default} reduce 29 459 | 460 | State 49: 461 | (30) function_decl ::= IDENT LPAREN function_params RPAREN COLON TYPE * 462 | 463 | {default} reduce 30 464 | 465 | State 50: 466 | (35) block ::= block element EOL * 467 | 468 | {default} reduce 35 469 | 470 | State 51: 471 | (36) element ::= operation * 472 | 473 | {default} reduce 36 474 | 475 | State 52: 476 | (37) element ::= directive * 477 | 478 | {default} reduce 37 479 | 480 | State 53: 481 | (38) element ::= LABEL * 482 | 483 | {default} reduce 38 484 | 485 | State 54: 486 | (1) statements ::= statement * 487 | 488 | {default} reduce 1 489 | 490 | State 55: 491 | (5) statement ::= function EOL * 492 | 493 | {default} reduce 5 494 | 495 | State 56: 496 | (39) element ::= IDENT EQ expr * 497 | 498 | {default} reduce 39 499 | 500 | State 57: 501 | (31) function_params ::= IDENT COLON TYPE * 502 | 503 | {default} reduce 31 504 | 505 | State 58: 506 | (34) block ::= element EOL * 507 | 508 | {default} reduce 34 509 | 510 | State 59: 511 | (2) statements ::= statements statement * 512 | 513 | {default} reduce 2 514 | 515 | State 60: 516 | (24) expr ::= LABEL * 517 | 518 | {default} reduce 24 519 | 520 | State 61: 521 | (23) expr ::= function_reference DOT IDENT * 522 | 523 | {default} reduce 23 524 | 525 | State 62: 526 | (4) statement ::= constant EOL * 527 | 528 | {default} reduce 4 529 | 530 | State 63: 531 | (25) expr ::= LBRACK INT RBRACK * 532 | 533 | {default} reduce 25 534 | 535 | State 64: 536 | (3) statement ::= directive EOL * 537 | 538 | {default} reduce 3 539 | 540 | State 65: 541 | (26) expr ::= LBRACK IDENT RBRACK * 542 | 543 | {default} reduce 26 544 | 545 | State 66: 546 | (32) function_params ::= function_params COMMA IDENT COLON TYPE * 547 | 548 | {default} reduce 32 549 | 550 | State 67: 551 | (27) function_reference ::= IDENT DOT IDENT * 552 | 553 | {default} reduce 27 554 | 555 | State 68: 556 | (20) expr ::= REG * 557 | 558 | {default} reduce 20 559 | 560 | State 69: 561 | (19) expr ::= CHR * 562 | 563 | {default} reduce 19 564 | 565 | State 70: 566 | (8) parameters ::= LPAREN args RPAREN * 567 | 568 | {default} reduce 8 569 | 570 | State 71: 571 | (18) expr ::= STR * 572 | 573 | {default} reduce 18 574 | 575 | State 72: 576 | (10) args ::= args COMMA expr * 577 | 578 | {default} reduce 10 579 | 580 | State 73: 581 | (11) args ::= expr * 582 | 583 | {default} reduce 11 584 | 585 | State 74: 586 | (17) expr ::= INT * 587 | 588 | {default} reduce 17 589 | 590 | ---------------------------------------------------- 591 | Symbols: 592 | 0: $: 593 | 1: EOL 594 | 2: IDENT 595 | 3: EQ 596 | 4: LPAREN 597 | 5: RPAREN 598 | 6: COMMA 599 | 7: OP 600 | 8: DOT 601 | 9: TYPE 602 | 10: HEX 603 | 11: FLOAT 604 | 12: INT 605 | 13: STR 606 | 14: CHR 607 | 15: REG 608 | 16: LABEL 609 | 17: LBRACK 610 | 18: RBRACK 611 | 19: PERCENT 612 | 20: FUNCTION 613 | 21: END 614 | 22: COLON 615 | 23: error: 616 | 24: parameters: LPAREN 617 | 25: args: IDENT COMMA HEX FLOAT INT STR CHR REG LABEL LBRACK 618 | 26: function: FUNCTION 619 | 27: function_decl: IDENT 620 | 28: function_params: IDENT COMMA 621 | 29: label: 622 | 30: function_reference: IDENT 623 | 31: module: EOL IDENT PERCENT FUNCTION 624 | 32: statements: EOL IDENT PERCENT FUNCTION 625 | 33: statement: EOL IDENT PERCENT FUNCTION 626 | 34: directive: PERCENT 627 | 35: constant: IDENT 628 | 36: expr: IDENT HEX FLOAT INT STR CHR REG LABEL LBRACK 629 | 37: operation: OP 630 | 38: block: EOL IDENT OP LABEL PERCENT 631 | 39: element: IDENT OP LABEL PERCENT 632 | -------------------------------------------------------------------------------- /src/grammar.y: -------------------------------------------------------------------------------- 1 | // Zed A. Shaw -- A weird assembler. Copyright 2008. */ 2 | 3 | %include { 4 | #include 5 | #include 6 | #include 7 | #include "allocator.h" 8 | } 9 | %token_destructor { GC_FREE($$); } 10 | 11 | %parse_accept { 12 | } 13 | 14 | %syntax_error { 15 | state->errors++; 16 | 17 | if(state->current) { 18 | printf("%s: In function '%.*s':\n", state->module_name, 19 | state->current->len, state->current->name); 20 | state->current = NULL; 21 | } 22 | 23 | printf("%s:%d: error at %s token in [", 24 | state->module_name, state->curline, yyTokenName[yymajor]); 25 | 26 | int i; 27 | for(i=1; i<=yypParser->yyidx; i++) 28 | printf(" %s",yyTokenName[yypParser->yystack[i].major]); 29 | 30 | printf(" | %s ] unexpected '%.*s'\n", 31 | yyTokenName[yymajor], 32 | yymajor != 0 ? TOKEN->len : 7, 33 | yymajor != 0 ? TOKEN->start : "$ (EOF)"); 34 | } 35 | 36 | %stack_overflow { 37 | printf("Stack overflowed, you suck after line: %u.\n", state->curline); 38 | } 39 | 40 | %token_prefix TK_ 41 | 42 | %extra_argument { Module *state } 43 | %token_type { Token* } 44 | 45 | // type and destructor specifications 46 | %type parameters { Parameters *} 47 | %type args { Parameters * } 48 | 49 | // the function stuff is all taken care of by the Module_destroy 50 | %type function {Function *} 51 | %type function_decl {Function *} 52 | %type function_params { FunctionParam *} 53 | // FunctionParams are freed by the function destructor 54 | %type label {Label *} 55 | // labels are freed by the function destructor 56 | %type function_reference {Token *} 57 | 58 | module ::= statements. 59 | 60 | statements ::= statement. 61 | statements ::= statements statement. 62 | 63 | statement ::= directive EOL. 64 | statement ::= constant EOL. 65 | statement ::= function EOL. 66 | statement ::= EOL. 67 | 68 | constant ::= IDENT(I) EQ expr(E). { 69 | Module_create_constant(state, I, E); 70 | } 71 | 72 | parameters(P) ::= LPAREN args(A) RPAREN. { P = A; } 73 | parameters(P) ::= . { P = GC_MALLOC(sizeof(Parameters)); } 74 | 75 | args(A) ::= args(B) COMMA expr(E). { B->args[B->i++] = E; A=B; } 76 | args(A) ::= expr(E). { A = GC_MALLOC(sizeof(Parameters)); A->args[A->i++] = E;} 77 | args(A) ::= . { A = GC_MALLOC(sizeof(Parameters)); } 78 | 79 | operation ::= OP(O) DOT TYPE(T) parameters(P). 80 | { Call_operation(state, state->current, O, T, P); } 81 | operation ::= OP(O) parameters(P). 82 | { Call_operation(state, state->current, O, NULL, P); } 83 | 84 | expr(E) ::= HEX(I). { E = I; } 85 | expr(E) ::= FLOAT(I). { E = I; } 86 | expr(E) ::= INT(I). { E = I; } 87 | expr(E) ::= STR(I). { E = I; } 88 | expr(E) ::= CHR(I). { E = I; } 89 | expr(E) ::= REG(I). { E = I; } 90 | expr(E) ::= IDENT(I). { E = Module_resolve_data(state, I); } 91 | expr(E) ::= function_reference(I). { E = I; } 92 | expr(E) ::= function_reference(F) DOT IDENT(I). { E = Module_outside_function_data(state, F, I); } 93 | expr(E) ::= LABEL(I). { E = Label_expression(state->current, I); } 94 | expr(E) ::= LBRACK INT(I) RBRACK. { E = Allocator_expression(state, I); } 95 | expr(E) ::= LBRACK IDENT(I) RBRACK. 96 | { E = Allocator_expression(state, Module_resolve_data(state, I)); } 97 | 98 | function_reference(R) ::= IDENT(M) DOT IDENT(F). 99 | { R = Module_resolve_function(state, M, F); } 100 | 101 | directive ::= PERCENT IDENT(I) parameters(P). 102 | { Module_call_directive(state, I, P); } 103 | 104 | function(F) ::= FUNCTION function_decl(D) block END(E). 105 | { F = Function_finalize(state, D, E); } 106 | 107 | function_decl(F) ::= IDENT(I) LPAREN function_params(P) RPAREN COLON TYPE(T). 108 | { F = Function_create(state, T->value, I->start, I->len, P); } 109 | 110 | function_params(P) ::= IDENT(I) COLON TYPE(T) . 111 | { P = FunctionParam_create(NULL, I, T); } 112 | function_params(P) ::= function_params(N) COMMA IDENT(I) COLON TYPE(T). 113 | { P = FunctionParam_create(N, I, T); } 114 | function_params(P) ::= . { P = NULL; } 115 | 116 | block ::= element EOL. 117 | block ::= block element EOL. 118 | 119 | element ::= operation. 120 | element ::= directive. 121 | element ::= LABEL(L). { Label_statement(state->current, L); } 122 | element ::= IDENT(I) EQ expr(E). { 123 | Module_create_function_constant(state, I, E); 124 | } 125 | element ::= . 126 | 127 | 128 | // error recovery productions 129 | statements ::= statements error statement. 130 | 131 | -------------------------------------------------------------------------------- /src/hash.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Hash Table Data Type 3 | * Copyright (C) 1997 Kaz Kylheku 4 | * 5 | * Free Software License: 6 | * 7 | * All rights are reserved by the author, with the following exceptions: 8 | * Permission is granted to freely reproduce and distribute this software, 9 | * possibly in exchange for a fee, provided that this copyright notice appears 10 | * intact. Permission is also granted to adapt this software to produce 11 | * derivative works, as long as the modified versions carry this copyright 12 | * notice and additional notices stating that the work has been modified. 13 | * This source code may be translated into executable form and incorporated 14 | * into proprietary software; there is no requirement for such software to 15 | * contain a copyright notice related to this source. 16 | * 17 | * $Id: hash.c,v 1.36.2.11 2000/11/13 01:36:45 kaz Exp $ 18 | * $Name: kazlib_1_20 $ 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #define HASH_IMPLEMENTATION 26 | #include "hash.h" 27 | 28 | #ifdef KAZLIB_RCSID 29 | static const char rcsid[] = "$Id: hash.c,v 1.36.2.11 2000/11/13 01:36:45 kaz Exp $"; 30 | #endif 31 | 32 | #define INIT_BITS 6 33 | #define INIT_SIZE (1UL << (INIT_BITS)) /* must be power of two */ 34 | #define INIT_MASK ((INIT_SIZE) - 1) 35 | 36 | #define next hash_next 37 | #define key hash_key 38 | #define data hash_data 39 | #define hkey hash_hkey 40 | 41 | #define table hash_table 42 | #define nchains hash_nchains 43 | #define nodecount hash_nodecount 44 | #define maxcount hash_maxcount 45 | #define highmark hash_highmark 46 | #define lowmark hash_lowmark 47 | #define compare hash_compare 48 | #define function hash_function 49 | #define allocnode hash_allocnode 50 | #define freenode hash_freenode 51 | #define context hash_context 52 | #define mask hash_mask 53 | #define dynamic hash_dynamic 54 | 55 | #define table hash_table 56 | #define chain hash_chain 57 | 58 | static hnode_t *hnode_alloc(void *context); 59 | static void hnode_free(hnode_t *node, void *context); 60 | static hash_val_t hash_fun_default(const void *key); 61 | static int hash_comp_default(const void *key1, const void *key2); 62 | 63 | int hash_val_t_bit; 64 | 65 | /* 66 | * Compute the number of bits in the hash_val_t type. We know that hash_val_t 67 | * is an unsigned integral type. Thus the highest value it can hold is a 68 | * Mersenne number (power of two, less one). We initialize a hash_val_t 69 | * object with this value and then shift bits out one by one while counting. 70 | * Notes: 71 | * 1. HASH_VAL_T_MAX is a Mersenne number---one that is one less than a power 72 | * of two. This means that its binary representation consists of all one 73 | * bits, and hence ``val'' is initialized to all one bits. 74 | * 2. While bits remain in val, we increment the bit count and shift it to the 75 | * right, replacing the topmost bit by zero. 76 | */ 77 | 78 | static void compute_bits(void) 79 | { 80 | hash_val_t val = HASH_VAL_T_MAX; /* 1 */ 81 | int bits = 0; 82 | 83 | while (val) { /* 2 */ 84 | bits++; 85 | val >>= 1; 86 | } 87 | 88 | hash_val_t_bit = bits; 89 | } 90 | 91 | /* 92 | * Verify whether the given argument is a power of two. 93 | */ 94 | 95 | static int is_power_of_two(hash_val_t arg) 96 | { 97 | if (arg == 0) 98 | return 0; 99 | while ((arg & 1) == 0) 100 | arg >>= 1; 101 | return (arg == 1); 102 | } 103 | 104 | /* 105 | * Compute a shift amount from a given table size 106 | */ 107 | 108 | static hash_val_t compute_mask(hashcount_t size) 109 | { 110 | assert (is_power_of_two(size)); 111 | assert (size >= 2); 112 | 113 | return size - 1; 114 | } 115 | 116 | /* 117 | * Initialize the table of pointers to null. 118 | */ 119 | 120 | static void clear_table(hash_t *hash) 121 | { 122 | hash_val_t i; 123 | 124 | for (i = 0; i < hash->nchains; i++) 125 | hash->table[i] = NULL; 126 | } 127 | 128 | /* 129 | * Double the size of a dynamic table. This works as follows. Each chain splits 130 | * into two adjacent chains. The shift amount increases by one, exposing an 131 | * additional bit of each hashed key. For each node in the original chain, the 132 | * value of this newly exposed bit will decide which of the two new chains will 133 | * receive the node: if the bit is 1, the chain with the higher index will have 134 | * the node, otherwise the lower chain will receive the node. In this manner, 135 | * the hash table will continue to function exactly as before without having to 136 | * rehash any of the keys. 137 | * Notes: 138 | * 1. Overflow check. 139 | * 2. The new number of chains is twice the old number of chains. 140 | * 3. The new mask is one bit wider than the previous, revealing a 141 | * new bit in all hashed keys. 142 | * 4. Allocate a new table of chain pointers that is twice as large as the 143 | * previous one. 144 | * 5. If the reallocation was successful, we perform the rest of the growth 145 | * algorithm, otherwise we do nothing. 146 | * 6. The exposed_bit variable holds a mask with which each hashed key can be 147 | * AND-ed to test the value of its newly exposed bit. 148 | * 7. Now loop over each chain in the table and sort its nodes into two 149 | * chains based on the value of each node's newly exposed hash bit. 150 | * 8. The low chain replaces the current chain. The high chain goes 151 | * into the corresponding sister chain in the upper half of the table. 152 | * 9. We have finished dealing with the chains and nodes. We now update 153 | * the various bookeeping fields of the hash structure. 154 | */ 155 | 156 | static void grow_table(hash_t *hash) 157 | { 158 | hnode_t **newtable; 159 | 160 | assert (2 * hash->nchains > hash->nchains); /* 1 */ 161 | 162 | newtable = realloc(hash->table, 163 | sizeof *newtable * hash->nchains * 2); /* 4 */ 164 | 165 | if (newtable) { /* 5 */ 166 | hash_val_t mask = (hash->mask << 1) | 1; /* 3 */ 167 | hash_val_t exposed_bit = mask ^ hash->mask; /* 6 */ 168 | hash_val_t chain; 169 | 170 | assert (mask != hash->mask); 171 | 172 | for (chain = 0; chain < hash->nchains; chain++) { /* 7 */ 173 | hnode_t *low_chain = 0, *high_chain = 0, *hptr, *next; 174 | 175 | for (hptr = newtable[chain]; hptr != 0; hptr = next) { 176 | next = hptr->next; 177 | 178 | if (hptr->hkey & exposed_bit) { 179 | hptr->next = high_chain; 180 | high_chain = hptr; 181 | } else { 182 | hptr->next = low_chain; 183 | low_chain = hptr; 184 | } 185 | } 186 | 187 | newtable[chain] = low_chain; /* 8 */ 188 | newtable[chain + hash->nchains] = high_chain; 189 | } 190 | 191 | hash->table = newtable; /* 9 */ 192 | hash->mask = mask; 193 | hash->nchains *= 2; 194 | hash->lowmark *= 2; 195 | hash->highmark *= 2; 196 | } 197 | assert (hash_verify(hash)); 198 | } 199 | 200 | /* 201 | * Cut a table size in half. This is done by folding together adjacent chains 202 | * and populating the lower half of the table with these chains. The chains are 203 | * simply spliced together. Once this is done, the whole table is reallocated 204 | * to a smaller object. 205 | * Notes: 206 | * 1. It is illegal to have a hash table with one slot. This would mean that 207 | * hash->shift is equal to hash_val_t_bit, an illegal shift value. 208 | * Also, other things could go wrong, such as hash->lowmark becoming zero. 209 | * 2. Looping over each pair of sister chains, the low_chain is set to 210 | * point to the head node of the chain in the lower half of the table, 211 | * and high_chain points to the head node of the sister in the upper half. 212 | * 3. The intent here is to compute a pointer to the last node of the 213 | * lower chain into the low_tail variable. If this chain is empty, 214 | * low_tail ends up with a null value. 215 | * 4. If the lower chain is not empty, we simply tack the upper chain onto it. 216 | * If the upper chain is a null pointer, nothing happens. 217 | * 5. Otherwise if the lower chain is empty but the upper one is not, 218 | * If the low chain is empty, but the high chain is not, then the 219 | * high chain is simply transferred to the lower half of the table. 220 | * 6. Otherwise if both chains are empty, there is nothing to do. 221 | * 7. All the chain pointers are in the lower half of the table now, so 222 | * we reallocate it to a smaller object. This, of course, invalidates 223 | * all pointer-to-pointers which reference into the table from the 224 | * first node of each chain. 225 | * 8. Though it's unlikely, the reallocation may fail. In this case we 226 | * pretend that the table _was_ reallocated to a smaller object. 227 | * 9. Finally, update the various table parameters to reflect the new size. 228 | */ 229 | 230 | static void shrink_table(hash_t *hash) 231 | { 232 | hash_val_t chain, nchains; 233 | hnode_t **newtable, *low_tail, *low_chain, *high_chain; 234 | 235 | assert (hash->nchains >= 2); /* 1 */ 236 | nchains = hash->nchains / 2; 237 | 238 | for (chain = 0; chain < nchains; chain++) { 239 | low_chain = hash->table[chain]; /* 2 */ 240 | high_chain = hash->table[chain + nchains]; 241 | for (low_tail = low_chain; low_tail && low_tail->next; low_tail = low_tail->next) 242 | ; /* 3 */ 243 | if (low_chain != 0) /* 4 */ 244 | low_tail->next = high_chain; 245 | else if (high_chain != 0) /* 5 */ 246 | hash->table[chain] = high_chain; 247 | else 248 | assert (hash->table[chain] == NULL); /* 6 */ 249 | } 250 | newtable = realloc(hash->table, 251 | sizeof *newtable * nchains); /* 7 */ 252 | if (newtable) /* 8 */ 253 | hash->table = newtable; 254 | hash->mask >>= 1; /* 9 */ 255 | hash->nchains = nchains; 256 | hash->lowmark /= 2; 257 | hash->highmark /= 2; 258 | assert (hash_verify(hash)); 259 | } 260 | 261 | 262 | /* 263 | * Create a dynamic hash table. Both the hash table structure and the table 264 | * itself are dynamically allocated. Furthermore, the table is extendible in 265 | * that it will automatically grow as its load factor increases beyond a 266 | * certain threshold. 267 | * Notes: 268 | * 1. If the number of bits in the hash_val_t type has not been computed yet, 269 | * we do so here, because this is likely to be the first function that the 270 | * user calls. 271 | * 2. Allocate a hash table control structure. 272 | * 3. If a hash table control structure is successfully allocated, we 273 | * proceed to initialize it. Otherwise we return a null pointer. 274 | * 4. We try to allocate the table of hash chains. 275 | * 5. If we were able to allocate the hash chain table, we can finish 276 | * initializing the hash structure and the table. Otherwise, we must 277 | * backtrack by freeing the hash structure. 278 | * 6. INIT_SIZE should be a power of two. The high and low marks are always set 279 | * to be twice the table size and half the table size respectively. When the 280 | * number of nodes in the table grows beyond the high size (beyond load 281 | * factor 2), it will double in size to cut the load factor down to about 282 | * about 1. If the table shrinks down to or beneath load factor 0.5, 283 | * it will shrink, bringing the load up to about 1. However, the table 284 | * will never shrink beneath INIT_SIZE even if it's emptied. 285 | * 7. This indicates that the table is dynamically allocated and dynamically 286 | * resized on the fly. A table that has this value set to zero is 287 | * assumed to be statically allocated and will not be resized. 288 | * 8. The table of chains must be properly reset to all null pointers. 289 | */ 290 | 291 | hash_t *hash_create(hashcount_t maxcount, hash_comp_t compfun, 292 | hash_fun_t hashfun) 293 | { 294 | hash_t *hash; 295 | 296 | if (hash_val_t_bit == 0) /* 1 */ 297 | compute_bits(); 298 | 299 | hash = malloc(sizeof *hash); /* 2 */ 300 | 301 | if (hash) { /* 3 */ 302 | hash->table = malloc(sizeof *hash->table * INIT_SIZE); /* 4 */ 303 | if (hash->table) { /* 5 */ 304 | hash->nchains = INIT_SIZE; /* 6 */ 305 | hash->highmark = INIT_SIZE * 2; 306 | hash->lowmark = INIT_SIZE / 2; 307 | hash->nodecount = 0; 308 | hash->maxcount = maxcount; 309 | hash->compare = compfun ? compfun : hash_comp_default; 310 | hash->function = hashfun ? hashfun : hash_fun_default; 311 | hash->allocnode = hnode_alloc; 312 | hash->freenode = hnode_free; 313 | hash->context = NULL; 314 | hash->mask = INIT_MASK; 315 | hash->dynamic = 1; /* 7 */ 316 | clear_table(hash); /* 8 */ 317 | assert (hash_verify(hash)); 318 | return hash; 319 | } 320 | free(hash); 321 | } 322 | 323 | return NULL; 324 | } 325 | 326 | /* 327 | * Select a different set of node allocator routines. 328 | */ 329 | 330 | void hash_set_allocator(hash_t *hash, hnode_alloc_t al, 331 | hnode_free_t fr, void *context) 332 | { 333 | assert (hash_count(hash) == 0); 334 | assert ((al == 0 && fr == 0) || (al != 0 && fr != 0)); 335 | 336 | hash->allocnode = al ? al : hnode_alloc; 337 | hash->freenode = fr ? fr : hnode_free; 338 | hash->context = context; 339 | } 340 | 341 | /* 342 | * Free every node in the hash using the hash->freenode() function pointer, and 343 | * cause the hash to become empty. 344 | */ 345 | 346 | void hash_free_nodes(hash_t *hash) 347 | { 348 | hscan_t hs; 349 | hnode_t *node; 350 | hash_scan_begin(&hs, hash); 351 | while ((node = hash_scan_next(&hs))) { 352 | hash_scan_delete(hash, node); 353 | hash->freenode(node, hash->context); 354 | } 355 | hash->nodecount = 0; 356 | clear_table(hash); 357 | } 358 | 359 | /* 360 | * Obsolescent function for removing all nodes from a table, 361 | * freeing them and then freeing the table all in one step. 362 | */ 363 | 364 | void hash_free(hash_t *hash) 365 | { 366 | #ifdef KAZLIB_OBSOLESCENT_DEBUG 367 | assert ("call to obsolescent function hash_free()" && 0); 368 | #endif 369 | hash_free_nodes(hash); 370 | hash_destroy(hash); 371 | } 372 | 373 | /* 374 | * Free a dynamic hash table structure. 375 | */ 376 | 377 | void hash_destroy(hash_t *hash) 378 | { 379 | assert (hash_val_t_bit != 0); 380 | assert (hash_isempty(hash)); 381 | free(hash->table); 382 | free(hash); 383 | } 384 | 385 | /* 386 | * Initialize a user supplied hash structure. The user also supplies a table of 387 | * chains which is assigned to the hash structure. The table is static---it 388 | * will not grow or shrink. 389 | * 1. See note 1. in hash_create(). 390 | * 2. The user supplied array of pointers hopefully contains nchains nodes. 391 | * 3. See note 7. in hash_create(). 392 | * 4. We must dynamically compute the mask from the given power of two table 393 | * size. 394 | * 5. The user supplied table can't be assumed to contain null pointers, 395 | * so we reset it here. 396 | */ 397 | 398 | hash_t *hash_init(hash_t *hash, hashcount_t maxcount, 399 | hash_comp_t compfun, hash_fun_t hashfun, hnode_t **table, 400 | hashcount_t nchains) 401 | { 402 | if (hash_val_t_bit == 0) /* 1 */ 403 | compute_bits(); 404 | 405 | assert (is_power_of_two(nchains)); 406 | 407 | hash->table = table; /* 2 */ 408 | hash->nchains = nchains; 409 | hash->nodecount = 0; 410 | hash->maxcount = maxcount; 411 | hash->compare = compfun ? compfun : hash_comp_default; 412 | hash->function = hashfun ? hashfun : hash_fun_default; 413 | hash->dynamic = 0; /* 3 */ 414 | hash->mask = compute_mask(nchains); /* 4 */ 415 | clear_table(hash); /* 5 */ 416 | 417 | assert (hash_verify(hash)); 418 | 419 | return hash; 420 | } 421 | 422 | /* 423 | * Reset the hash scanner so that the next element retrieved by 424 | * hash_scan_next() shall be the first element on the first non-empty chain. 425 | * Notes: 426 | * 1. Locate the first non empty chain. 427 | * 2. If an empty chain is found, remember which one it is and set the next 428 | * pointer to refer to its first element. 429 | * 3. Otherwise if a chain is not found, set the next pointer to NULL 430 | * so that hash_scan_next() shall indicate failure. 431 | */ 432 | 433 | void hash_scan_begin(hscan_t *scan, hash_t *hash) 434 | { 435 | hash_val_t nchains = hash->nchains; 436 | hash_val_t chain; 437 | 438 | scan->table = hash; 439 | 440 | /* 1 */ 441 | 442 | for (chain = 0; chain < nchains && hash->table[chain] == 0; chain++) 443 | ; 444 | 445 | if (chain < nchains) { /* 2 */ 446 | scan->chain = chain; 447 | scan->next = hash->table[chain]; 448 | } else { /* 3 */ 449 | scan->next = NULL; 450 | } 451 | } 452 | 453 | /* 454 | * Retrieve the next node from the hash table, and update the pointer 455 | * for the next invocation of hash_scan_next(). 456 | * Notes: 457 | * 1. Remember the next pointer in a temporary value so that it can be 458 | * returned. 459 | * 2. This assertion essentially checks whether the module has been properly 460 | * initialized. The first point of interaction with the module should be 461 | * either hash_create() or hash_init(), both of which set hash_val_t_bit to 462 | * a non zero value. 463 | * 3. If the next pointer we are returning is not NULL, then the user is 464 | * allowed to call hash_scan_next() again. We prepare the new next pointer 465 | * for that call right now. That way the user is allowed to delete the node 466 | * we are about to return, since we will no longer be needing it to locate 467 | * the next node. 468 | * 4. If there is a next node in the chain (next->next), then that becomes the 469 | * new next node, otherwise ... 470 | * 5. We have exhausted the current chain, and must locate the next subsequent 471 | * non-empty chain in the table. 472 | * 6. If a non-empty chain is found, the first element of that chain becomes 473 | * the new next node. Otherwise there is no new next node and we set the 474 | * pointer to NULL so that the next time hash_scan_next() is called, a null 475 | * pointer shall be immediately returned. 476 | */ 477 | 478 | 479 | hnode_t *hash_scan_next(hscan_t *scan) 480 | { 481 | hnode_t *next = scan->next; /* 1 */ 482 | hash_t *hash = scan->table; 483 | hash_val_t chain = scan->chain + 1; 484 | hash_val_t nchains = hash->nchains; 485 | 486 | assert (hash_val_t_bit != 0); /* 2 */ 487 | 488 | if (next) { /* 3 */ 489 | if (next->next) { /* 4 */ 490 | scan->next = next->next; 491 | } else { 492 | while (chain < nchains && hash->table[chain] == 0) /* 5 */ 493 | chain++; 494 | if (chain < nchains) { /* 6 */ 495 | scan->chain = chain; 496 | scan->next = hash->table[chain]; 497 | } else { 498 | scan->next = NULL; 499 | } 500 | } 501 | } 502 | return next; 503 | } 504 | 505 | /* 506 | * Insert a node into the hash table. 507 | * Notes: 508 | * 1. It's illegal to insert more than the maximum number of nodes. The client 509 | * should verify that the hash table is not full before attempting an 510 | * insertion. 511 | * 2. The same key may not be inserted into a table twice. 512 | * 3. If the table is dynamic and the load factor is already at >= 2, 513 | * grow the table. 514 | * 4. We take the bottom N bits of the hash value to derive the chain index, 515 | * where N is the base 2 logarithm of the size of the hash table. 516 | */ 517 | 518 | void hash_insert(hash_t *hash, hnode_t *node, const void *key) 519 | { 520 | hash_val_t hkey, chain; 521 | 522 | assert (hash_val_t_bit != 0); 523 | assert (node->next == NULL); 524 | assert (hash->nodecount < hash->maxcount); /* 1 */ 525 | assert (hash_lookup(hash, key) == NULL); /* 2 */ 526 | 527 | if (hash->dynamic && hash->nodecount >= hash->highmark) /* 3 */ 528 | grow_table(hash); 529 | 530 | hkey = hash->function(key); 531 | chain = hkey & hash->mask; /* 4 */ 532 | 533 | node->key = key; 534 | node->hkey = hkey; 535 | node->next = hash->table[chain]; 536 | hash->table[chain] = node; 537 | hash->nodecount++; 538 | 539 | assert (hash_verify(hash)); 540 | } 541 | 542 | /* 543 | * Find a node in the hash table and return a pointer to it. 544 | * Notes: 545 | * 1. We hash the key and keep the entire hash value. As an optimization, when 546 | * we descend down the chain, we can compare hash values first and only if 547 | * hash values match do we perform a full key comparison. 548 | * 2. To locate the chain from among 2^N chains, we look at the lower N bits of 549 | * the hash value by anding them with the current mask. 550 | * 3. Looping through the chain, we compare the stored hash value inside each 551 | * node against our computed hash. If they match, then we do a full 552 | * comparison between the unhashed keys. If these match, we have located the 553 | * entry. 554 | */ 555 | 556 | hnode_t *hash_lookup(hash_t *hash, const void *key) 557 | { 558 | hash_val_t hkey, chain; 559 | hnode_t *nptr; 560 | 561 | hkey = hash->function(key); /* 1 */ 562 | chain = hkey & hash->mask; /* 2 */ 563 | 564 | for (nptr = hash->table[chain]; nptr; nptr = nptr->next) { /* 3 */ 565 | if (nptr->hkey == hkey && hash->compare(nptr->key, key) == 0) 566 | return nptr; 567 | } 568 | 569 | return NULL; 570 | } 571 | 572 | /* 573 | * Delete the given node from the hash table. Since the chains 574 | * are singly linked, we must locate the start of the node's chain 575 | * and traverse. 576 | * Notes: 577 | * 1. The node must belong to this hash table, and its key must not have 578 | * been tampered with. 579 | * 2. If this deletion will take the node count below the low mark, we 580 | * shrink the table now. 581 | * 3. Determine which chain the node belongs to, and fetch the pointer 582 | * to the first node in this chain. 583 | * 4. If the node being deleted is the first node in the chain, then 584 | * simply update the chain head pointer. 585 | * 5. Otherwise advance to the node's predecessor, and splice out 586 | * by updating the predecessor's next pointer. 587 | * 6. Indicate that the node is no longer in a hash table. 588 | */ 589 | 590 | hnode_t *hash_delete(hash_t *hash, hnode_t *node) 591 | { 592 | hash_val_t chain; 593 | hnode_t *hptr; 594 | 595 | assert (hash_lookup(hash, node->key) == node); /* 1 */ 596 | assert (hash_val_t_bit != 0); 597 | 598 | if (hash->dynamic && hash->nodecount <= hash->lowmark 599 | && hash->nodecount > INIT_SIZE) 600 | shrink_table(hash); /* 2 */ 601 | 602 | chain = node->hkey & hash->mask; /* 3 */ 603 | hptr = hash->table[chain]; 604 | 605 | if (hptr == node) { /* 4 */ 606 | hash->table[chain] = node->next; 607 | } else { 608 | while (hptr->next != node) { /* 5 */ 609 | assert (hptr != 0); 610 | hptr = hptr->next; 611 | } 612 | assert (hptr->next == node); 613 | hptr->next = node->next; 614 | } 615 | 616 | hash->nodecount--; 617 | assert (hash_verify(hash)); 618 | 619 | node->next = NULL; /* 6 */ 620 | return node; 621 | } 622 | 623 | int hash_alloc_insert(hash_t *hash, const void *key, void *data) 624 | { 625 | hnode_t *node = hash->allocnode(hash->context); 626 | 627 | if (node) { 628 | hnode_init(node, data); 629 | hash_insert(hash, node, key); 630 | return 1; 631 | } 632 | return 0; 633 | } 634 | 635 | void hash_delete_free(hash_t *hash, hnode_t *node) 636 | { 637 | hash_delete(hash, node); 638 | hash->freenode(node, hash->context); 639 | } 640 | 641 | /* 642 | * Exactly like hash_delete, except does not trigger table shrinkage. This is to be 643 | * used from within a hash table scan operation. See notes for hash_delete. 644 | */ 645 | 646 | hnode_t *hash_scan_delete(hash_t *hash, hnode_t *node) 647 | { 648 | hash_val_t chain; 649 | hnode_t *hptr; 650 | 651 | assert (hash_lookup(hash, node->key) == node); 652 | assert (hash_val_t_bit != 0); 653 | 654 | chain = node->hkey & hash->mask; 655 | hptr = hash->table[chain]; 656 | 657 | if (hptr == node) { 658 | hash->table[chain] = node->next; 659 | } else { 660 | while (hptr->next != node) 661 | hptr = hptr->next; 662 | hptr->next = node->next; 663 | } 664 | 665 | hash->nodecount--; 666 | assert (hash_verify(hash)); 667 | node->next = NULL; 668 | 669 | return node; 670 | } 671 | 672 | /* 673 | * Like hash_delete_free but based on hash_scan_delete. 674 | */ 675 | 676 | void hash_scan_delfree(hash_t *hash, hnode_t *node) 677 | { 678 | hash_scan_delete(hash, node); 679 | hash->freenode(node, hash->context); 680 | } 681 | 682 | /* 683 | * Verify whether the given object is a valid hash table. This means 684 | * Notes: 685 | * 1. If the hash table is dynamic, verify whether the high and 686 | * low expansion/shrinkage thresholds are powers of two. 687 | * 2. Count all nodes in the table, and test each hash value 688 | * to see whether it is correct for the node's chain. 689 | */ 690 | 691 | int hash_verify(hash_t *hash) 692 | { 693 | hashcount_t count = 0; 694 | hash_val_t chain; 695 | hnode_t *hptr; 696 | 697 | if (hash->dynamic) { /* 1 */ 698 | if (hash->lowmark >= hash->highmark) 699 | return 0; 700 | if (!is_power_of_two(hash->highmark)) 701 | return 0; 702 | if (!is_power_of_two(hash->lowmark)) 703 | return 0; 704 | } 705 | 706 | for (chain = 0; chain < hash->nchains; chain++) { /* 2 */ 707 | for (hptr = hash->table[chain]; hptr != 0; hptr = hptr->next) { 708 | if ((hptr->hkey & hash->mask) != chain) 709 | return 0; 710 | count++; 711 | } 712 | } 713 | 714 | if (count != hash->nodecount) 715 | return 0; 716 | 717 | return 1; 718 | } 719 | 720 | /* 721 | * Test whether the hash table is full and return 1 if this is true, 722 | * 0 if it is false. 723 | */ 724 | 725 | #undef hash_isfull 726 | int hash_isfull(hash_t *hash) 727 | { 728 | return hash->nodecount == hash->maxcount; 729 | } 730 | 731 | /* 732 | * Test whether the hash table is empty and return 1 if this is true, 733 | * 0 if it is false. 734 | */ 735 | 736 | #undef hash_isempty 737 | int hash_isempty(hash_t *hash) 738 | { 739 | return hash->nodecount == 0; 740 | } 741 | 742 | static hnode_t *hnode_alloc(void *context) 743 | { 744 | return malloc(sizeof *hnode_alloc(NULL)); 745 | } 746 | 747 | static void hnode_free(hnode_t *node, void *context) 748 | { 749 | free(node); 750 | } 751 | 752 | 753 | /* 754 | * Create a hash table node dynamically and assign it the given data. 755 | */ 756 | 757 | hnode_t *hnode_create(void *data) 758 | { 759 | hnode_t *node = malloc(sizeof *node); 760 | if (node) { 761 | node->data = data; 762 | node->next = NULL; 763 | } 764 | return node; 765 | } 766 | 767 | /* 768 | * Initialize a client-supplied node 769 | */ 770 | 771 | hnode_t *hnode_init(hnode_t *hnode, void *data) 772 | { 773 | hnode->data = data; 774 | hnode->next = NULL; 775 | return hnode; 776 | } 777 | 778 | /* 779 | * Destroy a dynamically allocated node. 780 | */ 781 | 782 | void hnode_destroy(hnode_t *hnode) 783 | { 784 | free(hnode); 785 | } 786 | 787 | #undef hnode_put 788 | void hnode_put(hnode_t *node, void *data) 789 | { 790 | node->data = data; 791 | } 792 | 793 | #undef hnode_get 794 | void *hnode_get(hnode_t *node) 795 | { 796 | return node->data; 797 | } 798 | 799 | #undef hnode_getkey 800 | const void *hnode_getkey(hnode_t *node) 801 | { 802 | return node->key; 803 | } 804 | 805 | #undef hash_count 806 | hashcount_t hash_count(hash_t *hash) 807 | { 808 | return hash->nodecount; 809 | } 810 | 811 | #undef hash_size 812 | hashcount_t hash_size(hash_t *hash) 813 | { 814 | return hash->nchains; 815 | } 816 | 817 | static hash_val_t hash_fun_default(const void *key) 818 | { 819 | static unsigned long randbox[] = { 820 | 0x49848f1bU, 0xe6255dbaU, 0x36da5bdcU, 0x47bf94e9U, 821 | 0x8cbcce22U, 0x559fc06aU, 0xd268f536U, 0xe10af79aU, 822 | 0xc1af4d69U, 0x1d2917b5U, 0xec4c304dU, 0x9ee5016cU, 823 | 0x69232f74U, 0xfead7bb3U, 0xe9089ab6U, 0xf012f6aeU, 824 | }; 825 | 826 | const unsigned char *str = key; 827 | hash_val_t acc = 0; 828 | 829 | while (*str) { 830 | acc ^= randbox[(*str + acc) & 0xf]; 831 | acc = (acc << 1) | (acc >> 31); 832 | acc &= 0xffffffffU; 833 | acc ^= randbox[((*str++ >> 4) + acc) & 0xf]; 834 | acc = (acc << 2) | (acc >> 30); 835 | acc &= 0xffffffffU; 836 | } 837 | return acc; 838 | } 839 | 840 | static int hash_comp_default(const void *key1, const void *key2) 841 | { 842 | return strcmp(key1, key2); 843 | } 844 | 845 | -------------------------------------------------------------------------------- /src/hash.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Hash Table Data Type 3 | * Copyright (C) 1997 Kaz Kylheku 4 | * 5 | * Free Software License: 6 | * 7 | * All rights are reserved by the author, with the following exceptions: 8 | * Permission is granted to freely reproduce and distribute this software, 9 | * possibly in exchange for a fee, provided that this copyright notice appears 10 | * intact. Permission is also granted to adapt this software to produce 11 | * derivative works, as long as the modified versions carry this copyright 12 | * notice and additional notices stating that the work has been modified. 13 | * This source code may be translated into executable form and incorporated 14 | * into proprietary software; there is no requirement for such software to 15 | * contain a copyright notice related to this source. 16 | * 17 | * $Id: hash.h,v 1.22.2.7 2000/11/13 01:36:45 kaz Exp $ 18 | * $Name: kazlib_1_20 $ 19 | */ 20 | 21 | #ifndef HASH_H 22 | #define HASH_H 23 | 24 | #include 25 | #ifdef KAZLIB_SIDEEFFECT_DEBUG 26 | #include "sfx.h" 27 | #endif 28 | 29 | /* 30 | * Blurb for inclusion into C++ translation units 31 | */ 32 | 33 | #ifdef __cplusplus 34 | extern "C" { 35 | #endif 36 | 37 | typedef unsigned long hashcount_t; 38 | #define HASHCOUNT_T_MAX ULONG_MAX 39 | 40 | typedef unsigned long hash_val_t; 41 | #define HASH_VAL_T_MAX ULONG_MAX 42 | 43 | extern int hash_val_t_bit; 44 | 45 | #ifndef HASH_VAL_T_BIT 46 | #define HASH_VAL_T_BIT ((int) hash_val_t_bit) 47 | #endif 48 | 49 | /* 50 | * Hash chain node structure. 51 | * Notes: 52 | * 1. This preprocessing directive is for debugging purposes. The effect is 53 | * that if the preprocessor symbol KAZLIB_OPAQUE_DEBUG is defined prior to the 54 | * inclusion of this header, then the structure shall be declared as having 55 | * the single member int __OPAQUE__. This way, any attempts by the 56 | * client code to violate the principles of information hiding (by accessing 57 | * the structure directly) can be diagnosed at translation time. However, 58 | * note the resulting compiled unit is not suitable for linking. 59 | * 2. This is a pointer to the next node in the chain. In the last node of a 60 | * chain, this pointer is null. 61 | * 3. The key is a pointer to some user supplied data that contains a unique 62 | * identifier for each hash node in a given table. The interpretation of 63 | * the data is up to the user. When creating or initializing a hash table, 64 | * the user must supply a pointer to a function for comparing two keys, 65 | * and a pointer to a function for hashing a key into a numeric value. 66 | * 4. The value is a user-supplied pointer to void which may refer to 67 | * any data object. It is not interpreted in any way by the hashing 68 | * module. 69 | * 5. The hashed key is stored in each node so that we don't have to rehash 70 | * each key when the table must grow or shrink. 71 | */ 72 | 73 | typedef struct hnode_t { 74 | #if defined(HASH_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG) /* 1 */ 75 | struct hnode_t *hash_next; /* 2 */ 76 | const void *hash_key; /* 3 */ 77 | void *hash_data; /* 4 */ 78 | hash_val_t hash_hkey; /* 5 */ 79 | #else 80 | int hash_dummy; 81 | #endif 82 | } hnode_t; 83 | 84 | /* 85 | * The comparison function pointer type. A comparison function takes two keys 86 | * and produces a value of -1 if the left key is less than the right key, a 87 | * value of 0 if the keys are equal, and a value of 1 if the left key is 88 | * greater than the right key. 89 | */ 90 | 91 | typedef int (*hash_comp_t)(const void *, const void *); 92 | 93 | /* 94 | * The hashing function performs some computation on a key and produces an 95 | * integral value of type hash_val_t based on that key. For best results, the 96 | * function should have a good randomness properties in *all* significant bits 97 | * over the set of keys that are being inserted into a given hash table. In 98 | * particular, the most significant bits of hash_val_t are most significant to 99 | * the hash module. Only as the hash table expands are less significant bits 100 | * examined. Thus a function that has good distribution in its upper bits but 101 | * not lower is preferrable to one that has poor distribution in the upper bits 102 | * but not the lower ones. 103 | */ 104 | 105 | typedef hash_val_t (*hash_fun_t)(const void *); 106 | 107 | /* 108 | * allocator functions 109 | */ 110 | 111 | typedef hnode_t *(*hnode_alloc_t)(void *); 112 | typedef void (*hnode_free_t)(hnode_t *, void *); 113 | 114 | /* 115 | * This is the hash table control structure. It keeps track of information 116 | * about a hash table, as well as the hash table itself. 117 | * Notes: 118 | * 1. Pointer to the hash table proper. The table is an array of pointers to 119 | * hash nodes (of type hnode_t). If the table is empty, every element of 120 | * this table is a null pointer. A non-null entry points to the first 121 | * element of a chain of nodes. 122 | * 2. This member keeps track of the size of the hash table---that is, the 123 | * number of chain pointers. 124 | * 3. The count member maintains the number of elements that are presently 125 | * in the hash table. 126 | * 4. The maximum count is the greatest number of nodes that can populate this 127 | * table. If the table contains this many nodes, no more can be inserted, 128 | * and the hash_isfull() function returns true. 129 | * 5. The high mark is a population threshold, measured as a number of nodes, 130 | * which, if exceeded, will trigger a table expansion. Only dynamic hash 131 | * tables are subject to this expansion. 132 | * 6. The low mark is a minimum population threshold, measured as a number of 133 | * nodes. If the table population drops below this value, a table shrinkage 134 | * will occur. Only dynamic tables are subject to this reduction. No table 135 | * will shrink beneath a certain absolute minimum number of nodes. 136 | * 7. This is the a pointer to the hash table's comparison function. The 137 | * function is set once at initialization or creation time. 138 | * 8. Pointer to the table's hashing function, set once at creation or 139 | * initialization time. 140 | * 9. The current hash table mask. If the size of the hash table is 2^N, 141 | * this value has its low N bits set to 1, and the others clear. It is used 142 | * to select bits from the result of the hashing function to compute an 143 | * index into the table. 144 | * 10. A flag which indicates whether the table is to be dynamically resized. It 145 | * is set to 1 in dynamically allocated tables, 0 in tables that are 146 | * statically allocated. 147 | */ 148 | 149 | typedef struct hash_t { 150 | #if defined(HASH_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG) 151 | struct hnode_t **hash_table; /* 1 */ 152 | hashcount_t hash_nchains; /* 2 */ 153 | hashcount_t hash_nodecount; /* 3 */ 154 | hashcount_t hash_maxcount; /* 4 */ 155 | hashcount_t hash_highmark; /* 5 */ 156 | hashcount_t hash_lowmark; /* 6 */ 157 | hash_comp_t hash_compare; /* 7 */ 158 | hash_fun_t hash_function; /* 8 */ 159 | hnode_alloc_t hash_allocnode; 160 | hnode_free_t hash_freenode; 161 | void *hash_context; 162 | hash_val_t hash_mask; /* 9 */ 163 | int hash_dynamic; /* 10 */ 164 | #else 165 | int hash_dummy; 166 | #endif 167 | } hash_t; 168 | 169 | /* 170 | * Hash scanner structure, used for traversals of the data structure. 171 | * Notes: 172 | * 1. Pointer to the hash table that is being traversed. 173 | * 2. Reference to the current chain in the table being traversed (the chain 174 | * that contains the next node that shall be retrieved). 175 | * 3. Pointer to the node that will be retrieved by the subsequent call to 176 | * hash_scan_next(). 177 | */ 178 | 179 | typedef struct hscan_t { 180 | #if defined(HASH_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG) 181 | hash_t *hash_table; /* 1 */ 182 | hash_val_t hash_chain; /* 2 */ 183 | hnode_t *hash_next; /* 3 */ 184 | #else 185 | int hash_dummy; 186 | #endif 187 | } hscan_t; 188 | 189 | extern hash_t *hash_create(hashcount_t, hash_comp_t, hash_fun_t); 190 | extern void hash_set_allocator(hash_t *, hnode_alloc_t, hnode_free_t, void *); 191 | extern void hash_destroy(hash_t *); 192 | extern void hash_free_nodes(hash_t *); 193 | extern void hash_free(hash_t *); 194 | extern hash_t *hash_init(hash_t *, hashcount_t, hash_comp_t, 195 | hash_fun_t, hnode_t **, hashcount_t); 196 | extern void hash_insert(hash_t *, hnode_t *, const void *); 197 | extern hnode_t *hash_lookup(hash_t *, const void *); 198 | extern hnode_t *hash_delete(hash_t *, hnode_t *); 199 | extern int hash_alloc_insert(hash_t *, const void *, void *); 200 | extern void hash_delete_free(hash_t *, hnode_t *); 201 | 202 | extern void hnode_put(hnode_t *, void *); 203 | extern void *hnode_get(hnode_t *); 204 | extern const void *hnode_getkey(hnode_t *); 205 | extern hashcount_t hash_count(hash_t *); 206 | extern hashcount_t hash_size(hash_t *); 207 | 208 | extern int hash_isfull(hash_t *); 209 | extern int hash_isempty(hash_t *); 210 | 211 | extern void hash_scan_begin(hscan_t *, hash_t *); 212 | extern hnode_t *hash_scan_next(hscan_t *); 213 | extern hnode_t *hash_scan_delete(hash_t *, hnode_t *); 214 | extern void hash_scan_delfree(hash_t *, hnode_t *); 215 | 216 | extern int hash_verify(hash_t *); 217 | 218 | extern hnode_t *hnode_create(void *); 219 | extern hnode_t *hnode_init(hnode_t *, void *); 220 | extern void hnode_destroy(hnode_t *); 221 | 222 | #if defined(HASH_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG) 223 | #ifdef KAZLIB_SIDEEFFECT_DEBUG 224 | #define hash_isfull(H) (SFX_CHECK(H)->hash_nodecount == (H)->hash_maxcount) 225 | #else 226 | #define hash_isfull(H) ((H)->hash_nodecount == (H)->hash_maxcount) 227 | #endif 228 | #define hash_isempty(H) ((H)->hash_nodecount == 0) 229 | #define hash_count(H) ((H)->hash_nodecount) 230 | #define hash_size(H) ((H)->hash_nchains) 231 | #define hnode_get(N) ((N)->hash_data) 232 | #define hnode_getkey(N) ((N)->hash_key) 233 | #define hnode_put(N, V) ((N)->hash_data = (V)) 234 | #endif 235 | 236 | #ifdef __cplusplus 237 | } 238 | #endif 239 | 240 | #endif 241 | -------------------------------------------------------------------------------- /src/instructions.txt: -------------------------------------------------------------------------------- 1 | addr i ui l ul p f d | O1 = O2 + O3 2 | addi i ui l ul p | O1 = O2 + O3 3 | addxr i ui | O1 = O2 + (O3 + carry) 4 | addxi i ui | O1 = O2 + (O3 + carry) 5 | addcr i ui | O1 = O2 + O3, set carry 6 | addci i ui | O1 = O2 + O3, set carry 7 | subr i ui l ul p f d | O1 = O2 - O3 8 | subi i ui l ul p | O1 = O2 - O3 9 | subxr i ui | O1 = O2 - (O3 + carry) 10 | subxi i ui | O1 = O2 - (O3 + carry) 11 | subcr i ui | O1 = O2 - O3, set carry 12 | subci i ui | O1 = O2 - O3, set carry 13 | rsbr i ui l ul p f d | O1 = O3 - O2 14 | rsbi i ui l ul p | O1 = O3 - O2 15 | mulr i ui l ul f d | O1 = O2 * O3 16 | muli i ui l ul | O1 = O2 * O3 17 | hmulr i ui | O1 = high bits of O2 * O3 18 | hmuli i ui l ul | O1 = high bits of O2 * O3 19 | divr i ui l ul f d | O1 = O2 / O3 20 | divi i ui l ul | O1 = O2 / O3 21 | modr i ui l ul | O1 = O2 % O3 22 | modi i ui l ul | O1 = O2 % O3 23 | andr i ui l ul | O1 = O2 & O3 24 | andi i ui l ul | O1 = O2 & O3 25 | orr i ui l ul | O1 = O2 or O3 26 | ori i ui l ul | O1 = O2 or O3 27 | xorr i ui l ul | O1 = O2 ^ O3 28 | xori i ui l ul | O1 = O2 ^ O3 29 | lshr i ui l ul | O1 = O2 << O3 30 | lshi i ui l ul | O1 = O2 << O3 31 | rshr i ui l ul | O1 = O2 >> O31 32 | rshi i ui l ul | O1 = O2 >> O32 33 | negr i l | O1 = -O2 34 | notr i ui l ul | O1 = ~O2 35 | ltr i ui l ul p f d | O1 = (O2 < O3) 36 | lti i ui l ul p | O1 = (O2 < O3) 37 | ler i ui l ul p f d | O1 = (O2 <= O3) 38 | lei i ui l ul p | O1 = (O2 <= O3) 39 | gtr i ui l ul p f d | O1 = (O2 > O3) 40 | gti i ui l ul p | O1 = (O2 > O3) 41 | ger i ui l ul p f d | O1 = (O2 >= O3) 42 | gei i ui l ul p | O1 = (O2 >= O3) 43 | eqr i ui l ul p f d | O1 = (O2 == O3) 44 | eqi i ui l ul p | O1 = (O2 == O3) 45 | ner i ui l ul p f d | O1 = (O2 != O3) 46 | nei i ui l ul p | O1 = (O2 != O3) 47 | unltr f d | O1 = !(O2 >= O3) 48 | unler f d | O1 = !(O2 > O3) 49 | ungtr f d | O1 = !(O2 <= O3) 50 | unger f d | O1 = !(O2 < O3) 51 | uneqr f d | O1 = !(O2 < O3) && !(o2 > o3) 52 | ltgtr f d | O1 = !(O2 >= O3) or !(o2 <= o3) 53 | ordr f d | O1 = (O2 == O2) && (o3 == o3) 54 | unordr f d | O1 = (O2 != O2) or (o3 != o3) 55 | movr i ui l ul p f d | O1 = O2 56 | movi i ui l ul p f d | O1 = O2 57 | hton us ui | Host-to-network (big endian) order O1 <- O2 58 | ntoh us ui | Network-to-host order O1 <- O2 59 | ldr c uc s us i ui l ul p f d | O1 = *O2 60 | ldi c uc s us i ui l ul p f d | O1 = *O2 61 | ldxr c uc s us i ui l ul p f d | O1 = *(O2+O3) 62 | ldxi c uc s us i ui l ul p f d | O1 = *(O2+O3) 63 | str c uc s us i ui l ul p f d | O1 = O2 64 | sti c uc s us i ui l ul p f d | O1 = O2 65 | stxr c uc s us i ui l ul p f d | (O1+O2) = O3 66 | stxi c uc s us i ui l ul p f d | (O1+O2) = O3 67 | prepare i f d | O1 args to call 68 | pusharg c uc s us i ui l ul p f d | push argument O1 on 69 | getarg c uc s us i ui l ul p f d | O1 <- arg variable O2 70 | blti i ui l ul p | if (O2 < O3) goto O1 71 | blei i ui l ul p | if (O2 <= O3) goto O1 72 | bgti i ui l ul p | if (O2 > O3) goto O1 73 | bgei i ui l ul p | if (O2 >= O3) goto O1 74 | beqi i ui l ul p | if (O2 == O3) goto O1 75 | bnei i ui l ul p | if (O2 != O3) goto O1 76 | bmsr i ui l ul | if O2 & O3 goto O1 77 | bmsi i ui l ul | if O2 & O3 goto O1 78 | bmcr i ui l ul | if !(O2 & O3) goto O1 79 | bmci i ui l ul | if !(O2 & O3) goto O14 80 | calli | function call to O1 81 | callr | function call to a register O1 82 | finish | function call to O1 83 | finishr | function call to a register O1 84 | jmpi | unconditional jump to O1 85 | jmpr | unconditional jump to O1 86 | ret | return from subroutine 87 | retval c uc s us i ui l ul p f d | move return value O1 88 | 89 | -------------------------------------------------------------------------------- /src/label.c: -------------------------------------------------------------------------------- 1 | #include "function.h" 2 | #include "label.h" 3 | #include 4 | 5 | Label *Label_find(Function *func, Token *tk) 6 | { 7 | hnode_t *hn = hash_lookup(func->labels, tk->data); 8 | return hn ? hnode_get(hn) : NULL; 9 | } 10 | 11 | 12 | Token *Label_expression(Function *func, Token *tk) 13 | { 14 | Label *label = Label_find(func, tk); 15 | 16 | if(!label) { 17 | // forward label expression, create but not realized 18 | // label = Label_create_and_add(func, tk, 0); 19 | assert(label && "You must fix this Zed."); 20 | } else { 21 | if(label->realized) { 22 | // backward label expression 23 | tk->value = (unsigned long)label->ref; 24 | } else { 25 | // forward label expression that's being used again 26 | // do nothing with it, Call_operation will handle the gore 27 | } 28 | } 29 | 30 | return tk; 31 | } 32 | 33 | -------------------------------------------------------------------------------- /src/label.h: -------------------------------------------------------------------------------- 1 | #ifndef label_h 2 | #define label_h 3 | 4 | #include "function.h" 5 | #include 6 | 7 | typedef struct Label { 8 | jit_insn *ref; 9 | CORD name; 10 | int realized; 11 | array_t *back_refs; 12 | } Label; 13 | 14 | 15 | Label *Label_find_or_create(Function *func, Token *ident); 16 | 17 | Token *Label_expression(Function *func, Token *tk); 18 | 19 | void Label_statement(Function *func, Token *tk); 20 | 21 | Label *Label_find(Function *func, Token *tk); 22 | 23 | Label *Label_create_and_add(Function *func, Token *tk, int realized); 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /src/lexer.rl: -------------------------------------------------------------------------------- 1 | /** Zed A. Shaw -- A weird assembler. Copyright 2008. */ 2 | 3 | #include "module.h" 4 | #include "grammar.h" 5 | #include "tokenize.h" 6 | #include "grammar.c" 7 | #include "ops.h" 8 | #include "error.h" 9 | #include "allocator.h" 10 | 11 | %%{ 12 | machine lexer; 13 | 14 | newline = '\n' @{state->curline++; SYM(EOL)}; 15 | any_count_line = any | newline; 16 | 17 | c_comment := any_count_line* :>> '*/' @{fgoto main;}; 18 | 19 | main := |* 20 | 21 | alnum_u = alnum | '_'; 22 | alpha_u = alpha | '_'; 23 | 24 | # symbols used 25 | "." { SYM(DOT) }; 26 | "(" { SYM(LPAREN) }; 27 | ")" { SYM(RPAREN) }; 28 | "," { SYM(COMMA) }; 29 | ":" { SYM(COLON) }; 30 | "=" { SYM(EQ) }; 31 | "%" { SYM(PERCENT) }; 32 | ";" { SYM(EOL) }; 33 | "[" { SYM(LBRACK) }; 34 | "]" { SYM(RBRACK) }; 35 | 36 | # keywords for structures 37 | "function" { KW(FUNCTION) }; 38 | "end" { KW(END) }; 39 | 40 | # registers start with a % like in gas 41 | "NOREG" {REG(NOREG) }; 42 | "R0" {REG(R0) }; 43 | "R1" {REG(R1) }; 44 | "R2" {REG(R2) }; 45 | "V0" {REG(V0) }; 46 | "V1" {REG(V1) }; 47 | "V2" {REG(V2) }; 48 | "FP" {REG(FP) }; 49 | "RET" { REG(RET) }; 50 | 51 | # types used for operations and for function returns 52 | ('i'|'int') { TYPE(i) }; 53 | ('ui'|'uint') { TYPE(ui) }; 54 | ('l'|'long') { TYPE(l) }; 55 | ('ul'|'ulong') { TYPE(ul) }; 56 | ('p'|'ptr') { TYPE(p) }; 57 | ('f'|'float') { TYPE(f) }; 58 | ('d'|'double') { TYPE(d) }; 59 | ('v'|'void') { TYPE(v) }; 60 | ('c'|'char') { TYPE(c) }; 61 | ('uc'|'uchar') { TYPE(uc) }; 62 | ('s'|'short') { TYPE(s) }; 63 | ('us'|'ushort') { TYPE(us) }; 64 | 65 | # operations that are available 66 | "addr" {OP(addr) }; 67 | "addi" {OP(addi) }; 68 | "addxr" {OP(addxr) }; 69 | "addxi" {OP(addxi) }; 70 | "addcret" {OP(addcr) }; 71 | "addciet" {OP(addci) }; 72 | "subr" {OP(subr) }; 73 | "subi" {OP(subi) }; 74 | "subxr" {OP(subxr) }; 75 | "subxi" {OP(subxi) }; 76 | "subcr" {OP(subcr) }; 77 | "subci" {OP(subci) }; 78 | "rsbr" {OP(rsbr) }; 79 | "rsbi" {OP(rsbi) }; 80 | "mulr" {OP(mulr) }; 81 | "muli" {OP(muli) }; 82 | "hmulr" {OP(hmulr) }; 83 | "hmuli" {OP(hmuli) }; 84 | "divr" {OP(divr) }; 85 | "divi" {OP(divi) }; 86 | "modr" {OP(modr) }; 87 | "modi" {OP(modi) }; 88 | "andr" {OP(andr) }; 89 | "andi" {OP(andi) }; 90 | "orr" {OP(orr) }; 91 | "ori" {OP(ori) }; 92 | "xorr" {OP(xorr) }; 93 | "xori" {OP(xori) }; 94 | "lshr" {OP(lshr) }; 95 | "lshi" {OP(lshi) }; 96 | "rshr" {OP(rshr) }; 97 | "rshi" {OP(rshi) }; 98 | "negr" {OP(negr) }; 99 | "notr" {OP(notr) }; 100 | "ltr" {OP(ltr) }; 101 | "lti" {OP(lti) }; 102 | "ler" {OP(ler) }; 103 | "lei" {OP(lei) }; 104 | "gtr" {OP(gtr) }; 105 | "gti" {OP(gti) }; 106 | "ger" {OP(ger) }; 107 | "gei" {OP(gei) }; 108 | "eqr" {OP(eqr) }; 109 | "eqi" {OP(eqi) }; 110 | "ner" {OP(ner) }; 111 | "nei" {OP(nei) }; 112 | "unltr" {OP(unltr) }; 113 | "unler" {OP(unler) }; 114 | "ungtr" {OP(ungtr) }; 115 | "unger" {OP(unger) }; 116 | "uneqr" {OP(uneqr) }; 117 | "ltgtr" {OP(ltgtr) }; 118 | "ordr" {OP(ordr) }; 119 | "unordr" {OP(unordr) }; 120 | "movr" {OP(movr) }; 121 | "movi" {OP(movi) }; 122 | "hton" {OP(hton) }; 123 | "ntoh" {OP(ntoh) }; 124 | "ldr" {OP(ldr) }; 125 | "ldi" {OP(ldi) }; 126 | "ldxr" {OP(ldxr) }; 127 | "ldxi" {OP(ldxi) }; 128 | "str" {OP(str) }; 129 | "sti" {OP(sti) }; 130 | "stxr" {OP(stxr) }; 131 | "stxi" {OP(stxi) }; 132 | "prepare" {OP(prepare) }; 133 | "pusharg" {OP(pusharg) }; 134 | "getarg" {OP(getarg) }; 135 | "retval" {OP(retval) }; 136 | "blti" {OP(blti) }; 137 | "blei" {OP(blei) }; 138 | "bgti" {OP(bgti) }; 139 | "bgei" {OP(bgei) }; 140 | "beqi" {OP(beqi) }; 141 | "bnei" {OP(bnei) }; 142 | "bmsi" {OP(bmsi) }; 143 | "bmci" {OP(bmci) }; 144 | "jmpi" {OP(jmpi) }; 145 | "jmpr" {OP(jmpr) }; 146 | "ret" {OP(ret) }; 147 | 148 | # these are psuedo ops for working the GC 149 | "sizeof" { OP(size_of) }; 150 | "alloc" { OP(alloc) }; 151 | "realloc" { OP(realloc) }; 152 | "free" { OP(free) }; 153 | "collect" { OP(collect) }; 154 | 155 | # these are special in that we don't want to allow them in 156 | # leaf functions 157 | "calli" { OP(calli); NO_LEAF(calli); }; 158 | "callr" {OP(callr); NO_LEAF(callr); }; 159 | "finish" {OP(finish); NO_LEAF(finish); }; 160 | "finishr" {OP(finishr); NO_LEAF(finishr); }; 161 | 162 | # identifiers for things like variables and such 163 | alpha_u alnum_u* { IDENT() }; 164 | alpha_u alnum_u* ':' { LABEL() }; 165 | 166 | # single quoted string for single literal char 167 | sliteralChar = [^'\\] | newline | ( '\\' . any_count_line ); 168 | '\'' . sliteralChar* . '\'' { CHR() }; 169 | 170 | # double quoted string 171 | dliteralChar = [^"\\] | newline | ( '\\' any_count_line ); 172 | '"' . dliteralChar* . '"' { STR() }; 173 | 174 | # Whitespace is standard ws, newlines and control codes. 175 | any_count_line - 0x21..0x7e; 176 | 177 | # more sane EOL comments, and the only ones we need 178 | '#' [^\n]* newline; 179 | '/*' { fgoto c_comment; }; 180 | 181 | # integers, floats, and hex encoded values 182 | "-"? digit+ { INT() }; 183 | "-"? digit+ '.' digit+ { FLOAT() }; 184 | '0x' xdigit+ { HEX() }; 185 | 186 | *|; 187 | }%% 188 | 189 | %% write data nofinal; 190 | 191 | int Module_compile(Module *state, CORD source) 192 | { 193 | int cs, act; 194 | const char *ts = 0, *te = 0; 195 | void *pParser = ParseAlloc(Allocator_malloc); 196 | Token *tk = NULL; 197 | const char *p = CORD_to_const_char_star(source); 198 | 199 | %% write init; 200 | 201 | const char *pe = p+CORD_len(source); 202 | const char *eof = pe; 203 | 204 | %% write exec; 205 | 206 | if(cs == lexer_error) { 207 | state->errors++; 208 | error(state, "invalid character '%c'\n", p[0]); 209 | } else { 210 | Parse(pParser, 0, NULL, state); 211 | } 212 | 213 | ParseFree(pParser, Allocator_free); 214 | 215 | return state->errors > 0 ? 0 : 1; 216 | } 217 | 218 | 219 | -------------------------------------------------------------------------------- /src/list.c: -------------------------------------------------------------------------------- 1 | /* 2 | * List Abstract Data Type 3 | * Copyright (C) 1997 Kaz Kylheku 4 | * 5 | * Free Software License: 6 | * 7 | * All rights are reserved by the author, with the following exceptions: 8 | * Permission is granted to freely reproduce and distribute this software, 9 | * possibly in exchange for a fee, provided that this copyright notice appears 10 | * intact. Permission is also granted to adapt this software to produce 11 | * derivative works, as long as the modified versions carry this copyright 12 | * notice and additional notices stating that the work has been modified. 13 | * This source code may be translated into executable form and incorporated 14 | * into proprietary software; there is no requirement for such software to 15 | * contain a copyright notice related to this source. 16 | * 17 | * $Id: list.c,v 1.19.2.1 2000/04/17 01:07:21 kaz Exp $ 18 | * $Name: kazlib_1_20 $ 19 | */ 20 | 21 | 22 | #include 23 | #include 24 | #include 25 | #define LIST_IMPLEMENTATION 26 | #include "list.h" 27 | 28 | #define next list_next 29 | #define prev list_prev 30 | #define data list_data 31 | 32 | #define pool list_pool 33 | #define fre list_free 34 | #define size list_size 35 | 36 | #define nilnode list_nilnode 37 | #define nodecount list_nodecount 38 | #define maxcount list_maxcount 39 | 40 | #define list_nil(L) (&(L)->nilnode) 41 | #define list_first_priv(L) ((L)->nilnode.next) 42 | #define list_last_priv(L) ((L)->nilnode.prev) 43 | #define lnode_next(N) ((N)->next) 44 | #define lnode_prev(N) ((N)->prev) 45 | 46 | #ifdef KAZLIB_RCSID 47 | static const char rcsid[] = "$Id: list.c,v 1.19.2.1 2000/04/17 01:07:21 kaz Exp $"; 48 | #endif 49 | 50 | /* 51 | * Initialize a list object supplied by the client such that it becomes a valid 52 | * empty list. If the list is to be ``unbounded'', the maxcount should be 53 | * specified as LISTCOUNT_T_MAX, or, alternately, as -1. The value zero 54 | * is not permitted. 55 | */ 56 | 57 | list_t *list_init(list_t *list, listcount_t maxcount) 58 | { 59 | assert (maxcount != 0); 60 | list->nilnode.next = &list->nilnode; 61 | list->nilnode.prev = &list->nilnode; 62 | list->nodecount = 0; 63 | list->maxcount = maxcount; 64 | return list; 65 | } 66 | 67 | /* 68 | * Dynamically allocate a list object using malloc(), and initialize it so that 69 | * it is a valid empty list. If the list is to be ``unbounded'', the maxcount 70 | * should be specified as LISTCOUNT_T_MAX, or, alternately, as -1. 71 | */ 72 | 73 | list_t *list_create(listcount_t maxcount) 74 | { 75 | list_t *new = malloc(sizeof *new); 76 | if (new) { 77 | assert (maxcount != 0); 78 | new->nilnode.next = &new->nilnode; 79 | new->nilnode.prev = &new->nilnode; 80 | new->nodecount = 0; 81 | new->maxcount = maxcount; 82 | } 83 | return new; 84 | } 85 | 86 | /* 87 | * Destroy a dynamically allocated list object. 88 | * The client must remove the nodes first. 89 | */ 90 | 91 | void list_destroy(list_t *list) 92 | { 93 | assert (list_isempty(list)); 94 | free(list); 95 | } 96 | 97 | /* 98 | * Free all of the nodes of a list. The list must contain only 99 | * dynamically allocated nodes. After this call, the list 100 | * is empty. 101 | */ 102 | 103 | void list_destroy_nodes(list_t *list) 104 | { 105 | lnode_t *lnode = list_first_priv(list), *nil = list_nil(list), *tmp; 106 | 107 | while (lnode != nil) { 108 | tmp = lnode->next; 109 | lnode->next = NULL; 110 | lnode->prev = NULL; 111 | lnode_destroy(lnode); 112 | lnode = tmp; 113 | } 114 | 115 | list_init(list, list->maxcount); 116 | } 117 | 118 | /* 119 | * Return all of the nodes of a list to a node pool. The nodes in 120 | * the list must all have come from the same pool. 121 | */ 122 | 123 | void list_return_nodes(list_t *list, lnodepool_t *pool) 124 | { 125 | lnode_t *lnode = list_first_priv(list), *tmp, *nil = list_nil(list); 126 | 127 | while (lnode != nil) { 128 | tmp = lnode->next; 129 | lnode->next = NULL; 130 | lnode->prev = NULL; 131 | lnode_return(pool, lnode); 132 | lnode = tmp; 133 | } 134 | 135 | list_init(list, list->maxcount); 136 | } 137 | 138 | /* 139 | * Insert the node ``new'' into the list immediately after ``this'' node. 140 | */ 141 | 142 | void list_ins_after(list_t *list, lnode_t *new, lnode_t *this) 143 | { 144 | lnode_t *that = this->next; 145 | 146 | assert (new != NULL); 147 | assert (!list_contains(list, new)); 148 | assert (!lnode_is_in_a_list(new)); 149 | assert (this == list_nil(list) || list_contains(list, this)); 150 | assert (list->nodecount + 1 > list->nodecount); 151 | 152 | new->prev = this; 153 | new->next = that; 154 | that->prev = new; 155 | this->next = new; 156 | list->nodecount++; 157 | 158 | assert (list->nodecount <= list->maxcount); 159 | } 160 | 161 | /* 162 | * Insert the node ``new'' into the list immediately before ``this'' node. 163 | */ 164 | 165 | void list_ins_before(list_t *list, lnode_t *new, lnode_t *this) 166 | { 167 | lnode_t *that = this->prev; 168 | 169 | assert (new != NULL); 170 | assert (!list_contains(list, new)); 171 | assert (!lnode_is_in_a_list(new)); 172 | assert (this == list_nil(list) || list_contains(list, this)); 173 | assert (list->nodecount + 1 > list->nodecount); 174 | 175 | new->next = this; 176 | new->prev = that; 177 | that->next = new; 178 | this->prev = new; 179 | list->nodecount++; 180 | 181 | assert (list->nodecount <= list->maxcount); 182 | } 183 | 184 | /* 185 | * Delete the given node from the list. 186 | */ 187 | 188 | lnode_t *list_delete(list_t *list, lnode_t *del) 189 | { 190 | lnode_t *next = del->next; 191 | lnode_t *prev = del->prev; 192 | 193 | assert (list_contains(list, del)); 194 | 195 | prev->next = next; 196 | next->prev = prev; 197 | list->nodecount--; 198 | 199 | del->next = del->prev = NULL; 200 | 201 | return del; 202 | } 203 | 204 | /* 205 | * For each node in the list, execute the given function. The list, 206 | * current node and the given context pointer are passed on each 207 | * call to the function. 208 | */ 209 | 210 | void list_process(list_t *list, void *context, 211 | void (* function)(list_t *list, lnode_t *lnode, void *context)) 212 | { 213 | lnode_t *node = list_first_priv(list), *next, *nil = list_nil(list); 214 | 215 | while (node != nil) { 216 | /* check for callback function deleting */ 217 | /* the next node from under us */ 218 | assert (list_contains(list, node)); 219 | next = node->next; 220 | function(list, node, context); 221 | node = next; 222 | } 223 | } 224 | 225 | /* 226 | * Dynamically allocate a list node and assign it the given piece of data. 227 | */ 228 | 229 | lnode_t *lnode_create(void *data) 230 | { 231 | lnode_t *new = malloc(sizeof *new); 232 | if (new) { 233 | new->data = data; 234 | new->next = NULL; 235 | new->prev = NULL; 236 | } 237 | return new; 238 | } 239 | 240 | /* 241 | * Initialize a user-supplied lnode. 242 | */ 243 | 244 | lnode_t *lnode_init(lnode_t *lnode, void *data) 245 | { 246 | lnode->data = data; 247 | lnode->next = NULL; 248 | lnode->prev = NULL; 249 | return lnode; 250 | } 251 | 252 | /* 253 | * Destroy a dynamically allocated node. 254 | */ 255 | 256 | void lnode_destroy(lnode_t *lnode) 257 | { 258 | assert (!lnode_is_in_a_list(lnode)); 259 | free(lnode); 260 | } 261 | 262 | /* 263 | * Initialize a node pool object to use a user-supplied set of nodes. 264 | * The ``nodes'' pointer refers to an array of lnode_t objects, containing 265 | * ``n'' elements. 266 | */ 267 | 268 | lnodepool_t *lnode_pool_init(lnodepool_t *pool, lnode_t *nodes, listcount_t n) 269 | { 270 | listcount_t i; 271 | 272 | assert (n != 0); 273 | 274 | pool->pool = nodes; 275 | pool->fre = nodes; 276 | pool->size = n; 277 | for (i = 0; i < n - 1; i++) { 278 | nodes[i].next = nodes + i + 1; 279 | } 280 | nodes[i].next = NULL; 281 | nodes[i].prev = nodes; /* to make sure node is marked ``on list'' */ 282 | return pool; 283 | } 284 | 285 | /* 286 | * Create a dynamically allocated pool of n nodes. 287 | */ 288 | 289 | lnodepool_t *lnode_pool_create(listcount_t n) 290 | { 291 | lnodepool_t *pool; 292 | lnode_t *nodes; 293 | 294 | assert (n != 0); 295 | 296 | pool = malloc(sizeof *pool); 297 | if (!pool) 298 | return NULL; 299 | nodes = malloc(n * sizeof *nodes); 300 | if (!nodes) { 301 | free(pool); 302 | return NULL; 303 | } 304 | lnode_pool_init(pool, nodes, n); 305 | return pool; 306 | } 307 | 308 | /* 309 | * Determine whether the given pool is from this pool. 310 | */ 311 | 312 | int lnode_pool_isfrom(lnodepool_t *pool, lnode_t *node) 313 | { 314 | listcount_t i; 315 | 316 | /* this is carefully coded this way because ANSI C forbids pointers 317 | to different objects from being subtracted or compared other 318 | than for exact equality */ 319 | 320 | for (i = 0; i < pool->size; i++) { 321 | if (pool->pool + i == node) 322 | return 1; 323 | } 324 | return 0; 325 | } 326 | 327 | /* 328 | * Destroy a dynamically allocated pool of nodes. 329 | */ 330 | 331 | void lnode_pool_destroy(lnodepool_t *p) 332 | { 333 | free(p->pool); 334 | free(p); 335 | } 336 | 337 | /* 338 | * Borrow a node from a node pool. Returns a null pointer if the pool 339 | * is exhausted. 340 | */ 341 | 342 | lnode_t *lnode_borrow(lnodepool_t *pool, void *data) 343 | { 344 | lnode_t *new = pool->fre; 345 | if (new) { 346 | pool->fre = new->next; 347 | new->data = data; 348 | new->next = NULL; 349 | new->prev = NULL; 350 | } 351 | return new; 352 | } 353 | 354 | /* 355 | * Return a node to a node pool. A node must be returned to the pool 356 | * from which it came. 357 | */ 358 | 359 | void lnode_return(lnodepool_t *pool, lnode_t *node) 360 | { 361 | assert (lnode_pool_isfrom(pool, node)); 362 | assert (!lnode_is_in_a_list(node)); 363 | 364 | node->next = pool->fre; 365 | node->prev = node; 366 | pool->fre = node; 367 | } 368 | 369 | /* 370 | * Determine whether the given list contains the given node. 371 | * According to this function, a list does not contain its nilnode. 372 | */ 373 | 374 | int list_contains(list_t *list, lnode_t *node) 375 | { 376 | lnode_t *n, *nil = list_nil(list); 377 | 378 | for (n = list_first_priv(list); n != nil; n = lnode_next(n)) { 379 | if (node == n) 380 | return 1; 381 | } 382 | 383 | return 0; 384 | } 385 | 386 | /* 387 | * A more generalized variant of list_transfer. This one removes a 388 | * ``slice'' from the source list and appends it to the destination 389 | * list. 390 | */ 391 | 392 | void list_extract(list_t *dest, list_t *source, lnode_t *first, lnode_t *last) 393 | { 394 | listcount_t moved = 1; 395 | 396 | assert (first == NULL || list_contains(source, first)); 397 | assert (last == NULL || list_contains(source, last)); 398 | 399 | if (first == NULL || last == NULL) 400 | return; 401 | 402 | /* adjust the destination list so that the slice is spliced out */ 403 | 404 | first->prev->next = last->next; 405 | last->next->prev = first->prev; 406 | 407 | /* graft the splice at the end of the dest list */ 408 | 409 | last->next = &dest->nilnode; 410 | first->prev = dest->nilnode.prev; 411 | dest->nilnode.prev->next = first; 412 | dest->nilnode.prev = last; 413 | 414 | while (first != last) { 415 | first = first->next; 416 | assert (first != list_nil(source)); /* oops, last before first! */ 417 | moved++; 418 | } 419 | 420 | /* assert no overflows */ 421 | assert (source->nodecount - moved <= source->nodecount); 422 | assert (dest->nodecount + moved >= dest->nodecount); 423 | 424 | /* assert no weirdness */ 425 | assert (moved <= source->nodecount); 426 | 427 | source->nodecount -= moved; 428 | dest->nodecount += moved; 429 | 430 | /* assert list sanity */ 431 | assert (list_verify(source)); 432 | assert (list_verify(dest)); 433 | } 434 | 435 | 436 | /* 437 | * Split off a trailing sequence of nodes from the source list and relocate 438 | * them to the tail of the destination list. The trailing sequence begins 439 | * with node ``first'' and terminates with the last node of the source 440 | * list. The nodes are added to the end of the new list in their original 441 | * order. 442 | */ 443 | 444 | void list_transfer(list_t *dest, list_t *source, lnode_t *first) 445 | { 446 | listcount_t moved = 1; 447 | lnode_t *last; 448 | 449 | assert (first == NULL || list_contains(source, first)); 450 | 451 | if (first == NULL) 452 | return; 453 | 454 | last = source->nilnode.prev; 455 | 456 | source->nilnode.prev = first->prev; 457 | first->prev->next = &source->nilnode; 458 | 459 | last->next = &dest->nilnode; 460 | first->prev = dest->nilnode.prev; 461 | dest->nilnode.prev->next = first; 462 | dest->nilnode.prev = last; 463 | 464 | while (first != last) { 465 | first = first->next; 466 | moved++; 467 | } 468 | 469 | /* assert no overflows */ 470 | assert (source->nodecount - moved <= source->nodecount); 471 | assert (dest->nodecount + moved >= dest->nodecount); 472 | 473 | /* assert no weirdness */ 474 | assert (moved <= source->nodecount); 475 | 476 | source->nodecount -= moved; 477 | dest->nodecount += moved; 478 | 479 | /* assert list sanity */ 480 | assert (list_verify(source)); 481 | assert (list_verify(dest)); 482 | } 483 | 484 | void list_merge(list_t *dest, list_t *sour, 485 | int compare (const void *, const void *)) 486 | { 487 | lnode_t *dn, *sn, *tn; 488 | lnode_t *d_nil = list_nil(dest), *s_nil = list_nil(sour); 489 | 490 | /* Nothing to do if source and destination list are the same. */ 491 | if (dest == sour) 492 | return; 493 | 494 | /* overflow check */ 495 | assert (list_count(sour) + list_count(dest) >= list_count(sour)); 496 | 497 | /* lists must be sorted */ 498 | assert (list_is_sorted(sour, compare)); 499 | assert (list_is_sorted(dest, compare)); 500 | 501 | dn = list_first_priv(dest); 502 | sn = list_first_priv(sour); 503 | 504 | while (dn != d_nil && sn != s_nil) { 505 | if (compare(lnode_get(dn), lnode_get(sn)) >= 0) { 506 | tn = lnode_next(sn); 507 | list_delete(sour, sn); 508 | list_ins_before(dest, sn, dn); 509 | sn = tn; 510 | } else { 511 | dn = lnode_next(dn); 512 | } 513 | } 514 | 515 | if (dn != d_nil) 516 | return; 517 | 518 | if (sn != s_nil) 519 | list_transfer(dest, sour, sn); 520 | } 521 | 522 | void list_sort(list_t *list, int compare(const void *, const void *)) 523 | { 524 | list_t extra; 525 | listcount_t middle; 526 | lnode_t *node; 527 | 528 | if (list_count(list) > 1) { 529 | middle = list_count(list) / 2; 530 | node = list_first_priv(list); 531 | 532 | list_init(&extra, list_count(list) - middle); 533 | 534 | while (middle--) 535 | node = lnode_next(node); 536 | 537 | list_transfer(&extra, list, node); 538 | list_sort(list, compare); 539 | list_sort(&extra, compare); 540 | list_merge(list, &extra, compare); 541 | } 542 | assert (list_is_sorted(list, compare)); 543 | } 544 | 545 | lnode_t *list_find(list_t *list, const void *key, int compare(const void *, const void *)) 546 | { 547 | lnode_t *node; 548 | 549 | for (node = list_first_priv(list); node != list_nil(list); node = node->next) { 550 | if (compare(lnode_get(node), key) == 0) 551 | return node; 552 | } 553 | 554 | return 0; 555 | } 556 | 557 | 558 | /* 559 | * Return 1 if the list is in sorted order, 0 otherwise 560 | */ 561 | 562 | int list_is_sorted(list_t *list, int compare(const void *, const void *)) 563 | { 564 | lnode_t *node, *next, *nil; 565 | 566 | next = nil = list_nil(list); 567 | node = list_first_priv(list); 568 | 569 | if (node != nil) 570 | next = lnode_next(node); 571 | 572 | for (; next != nil; node = next, next = lnode_next(next)) { 573 | if (compare(lnode_get(node), lnode_get(next)) > 0) 574 | return 0; 575 | } 576 | 577 | return 1; 578 | } 579 | 580 | /* 581 | * Get rid of macro functions definitions so they don't interfere 582 | * with the actual definitions 583 | */ 584 | 585 | #undef list_isempty 586 | #undef list_isfull 587 | #undef lnode_pool_isempty 588 | #undef list_append 589 | #undef list_prepend 590 | #undef list_first 591 | #undef list_last 592 | #undef list_next 593 | #undef list_prev 594 | #undef list_count 595 | #undef list_del_first 596 | #undef list_del_last 597 | #undef lnode_put 598 | #undef lnode_get 599 | 600 | /* 601 | * Return 1 if the list is empty, 0 otherwise 602 | */ 603 | 604 | int list_isempty(list_t *list) 605 | { 606 | return list->nodecount == 0; 607 | } 608 | 609 | /* 610 | * Return 1 if the list is full, 0 otherwise 611 | * Permitted only on bounded lists. 612 | */ 613 | 614 | int list_isfull(list_t *list) 615 | { 616 | return list->nodecount == list->maxcount; 617 | } 618 | 619 | /* 620 | * Check if the node pool is empty. 621 | */ 622 | 623 | int lnode_pool_isempty(lnodepool_t *pool) 624 | { 625 | return (pool->fre == NULL); 626 | } 627 | 628 | /* 629 | * Add the given node at the end of the list 630 | */ 631 | 632 | void list_append(list_t *list, lnode_t *node) 633 | { 634 | list_ins_before(list, node, &list->nilnode); 635 | } 636 | 637 | /* 638 | * Add the given node at the beginning of the list. 639 | */ 640 | 641 | void list_prepend(list_t *list, lnode_t *node) 642 | { 643 | list_ins_after(list, node, &list->nilnode); 644 | } 645 | 646 | /* 647 | * Retrieve the first node of the list 648 | */ 649 | 650 | lnode_t *list_first(list_t *list) 651 | { 652 | if (list->nilnode.next == &list->nilnode) 653 | return NULL; 654 | return list->nilnode.next; 655 | } 656 | 657 | /* 658 | * Retrieve the last node of the list 659 | */ 660 | 661 | lnode_t *list_last(list_t *list) 662 | { 663 | if (list->nilnode.prev == &list->nilnode) 664 | return NULL; 665 | return list->nilnode.prev; 666 | } 667 | 668 | /* 669 | * Retrieve the count of nodes in the list 670 | */ 671 | 672 | listcount_t list_count(list_t *list) 673 | { 674 | return list->nodecount; 675 | } 676 | 677 | /* 678 | * Remove the first node from the list and return it. 679 | */ 680 | 681 | lnode_t *list_del_first(list_t *list) 682 | { 683 | return list_delete(list, list->nilnode.next); 684 | } 685 | 686 | /* 687 | * Remove the last node from the list and return it. 688 | */ 689 | 690 | lnode_t *list_del_last(list_t *list) 691 | { 692 | return list_delete(list, list->nilnode.prev); 693 | } 694 | 695 | 696 | /* 697 | * Associate a data item with the given node. 698 | */ 699 | 700 | void lnode_put(lnode_t *lnode, void *data) 701 | { 702 | lnode->data = data; 703 | } 704 | 705 | /* 706 | * Retrieve the data item associated with the node. 707 | */ 708 | 709 | void *lnode_get(lnode_t *lnode) 710 | { 711 | return lnode->data; 712 | } 713 | 714 | /* 715 | * Retrieve the node's successor. If there is no successor, 716 | * NULL is returned. 717 | */ 718 | 719 | lnode_t *list_next(list_t *list, lnode_t *lnode) 720 | { 721 | assert (list_contains(list, lnode)); 722 | 723 | if (lnode->next == list_nil(list)) 724 | return NULL; 725 | return lnode->next; 726 | } 727 | 728 | /* 729 | * Retrieve the node's predecessor. See comment for lnode_next(). 730 | */ 731 | 732 | lnode_t *list_prev(list_t *list, lnode_t *lnode) 733 | { 734 | assert (list_contains(list, lnode)); 735 | 736 | if (lnode->prev == list_nil(list)) 737 | return NULL; 738 | return lnode->prev; 739 | } 740 | 741 | /* 742 | * Return 1 if the lnode is in some list, otherwise return 0. 743 | */ 744 | 745 | int lnode_is_in_a_list(lnode_t *lnode) 746 | { 747 | return (lnode->next != NULL || lnode->prev != NULL); 748 | } 749 | 750 | 751 | int list_verify(list_t *list) 752 | { 753 | lnode_t *node = list_first_priv(list), *nil = list_nil(list); 754 | listcount_t count = list_count(list); 755 | 756 | if (node->prev != nil) 757 | return 0; 758 | 759 | if (count > list->maxcount) 760 | return 0; 761 | 762 | while (node != nil && count--) { 763 | if (node->next->prev != node) 764 | return 0; 765 | node = node->next; 766 | } 767 | 768 | if (count != 0 || node != nil) 769 | return 0; 770 | 771 | return 1; 772 | } 773 | 774 | -------------------------------------------------------------------------------- /src/list.h: -------------------------------------------------------------------------------- 1 | /* 2 | * List Abstract Data Type 3 | * Copyright (C) 1997 Kaz Kylheku 4 | * 5 | * Free Software License: 6 | * 7 | * All rights are reserved by the author, with the following exceptions: 8 | * Permission is granted to freely reproduce and distribute this software, 9 | * possibly in exchange for a fee, provided that this copyright notice appears 10 | * intact. Permission is also granted to adapt this software to produce 11 | * derivative works, as long as the modified versions carry this copyright 12 | * notice and additional notices stating that the work has been modified. 13 | * This source code may be translated into executable form and incorporated 14 | * into proprietary software; there is no requirement for such software to 15 | * contain a copyright notice related to this source. 16 | * 17 | * $Id: list.h,v 1.19 1999/11/14 20:46:19 kaz Exp $ 18 | * $Name: kazlib_1_20 $ 19 | */ 20 | 21 | #ifndef LIST_H 22 | #define LIST_H 23 | 24 | #include 25 | 26 | #ifdef KAZLIB_SIDEEFFECT_DEBUG 27 | #include "sfx.h" 28 | #define LIST_SFX_CHECK(E) SFX_CHECK(E) 29 | #else 30 | #define LIST_SFX_CHECK(E) (E) 31 | #endif 32 | 33 | /* 34 | * Blurb for inclusion into C++ translation units 35 | */ 36 | 37 | #ifdef __cplusplus 38 | extern "C" { 39 | #endif 40 | 41 | typedef unsigned long listcount_t; 42 | #define LISTCOUNT_T_MAX ULONG_MAX 43 | 44 | typedef struct lnode_t { 45 | #if defined(LIST_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG) 46 | struct lnode_t *list_next; 47 | struct lnode_t *list_prev; 48 | void *list_data; 49 | #else 50 | int list_dummy; 51 | #endif 52 | } lnode_t; 53 | 54 | typedef struct lnodepool_t { 55 | #if defined(LIST_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG) 56 | struct lnode_t *list_pool; 57 | struct lnode_t *list_free; 58 | listcount_t list_size; 59 | #else 60 | int list_dummy; 61 | #endif 62 | } lnodepool_t; 63 | 64 | typedef struct list_t { 65 | #if defined(LIST_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG) 66 | lnode_t list_nilnode; 67 | listcount_t list_nodecount; 68 | listcount_t list_maxcount; 69 | #else 70 | int list_dummy; 71 | #endif 72 | } list_t; 73 | 74 | lnode_t *lnode_create(void *); 75 | lnode_t *lnode_init(lnode_t *, void *); 76 | void lnode_destroy(lnode_t *); 77 | void lnode_put(lnode_t *, void *); 78 | void *lnode_get(lnode_t *); 79 | int lnode_is_in_a_list(lnode_t *); 80 | 81 | #if defined(LIST_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG) 82 | #define lnode_put(N, D) ((N)->list_data = (D)) 83 | #define lnode_get(N) ((N)->list_data) 84 | #endif 85 | 86 | lnodepool_t *lnode_pool_init(lnodepool_t *, lnode_t *, listcount_t); 87 | lnodepool_t *lnode_pool_create(listcount_t); 88 | void lnode_pool_destroy(lnodepool_t *); 89 | lnode_t *lnode_borrow(lnodepool_t *, void *); 90 | void lnode_return(lnodepool_t *, lnode_t *); 91 | int lnode_pool_isempty(lnodepool_t *); 92 | int lnode_pool_isfrom(lnodepool_t *, lnode_t *); 93 | 94 | list_t *list_init(list_t *, listcount_t); 95 | list_t *list_create(listcount_t); 96 | void list_destroy(list_t *); 97 | void list_destroy_nodes(list_t *); 98 | void list_return_nodes(list_t *, lnodepool_t *); 99 | 100 | listcount_t list_count(list_t *); 101 | int list_isempty(list_t *); 102 | int list_isfull(list_t *); 103 | int list_contains(list_t *, lnode_t *); 104 | 105 | void list_append(list_t *, lnode_t *); 106 | void list_prepend(list_t *, lnode_t *); 107 | void list_ins_before(list_t *, lnode_t *, lnode_t *); 108 | void list_ins_after(list_t *, lnode_t *, lnode_t *); 109 | 110 | lnode_t *list_first(list_t *); 111 | lnode_t *list_last(list_t *); 112 | lnode_t *list_next(list_t *, lnode_t *); 113 | lnode_t *list_prev(list_t *, lnode_t *); 114 | 115 | lnode_t *list_del_first(list_t *); 116 | lnode_t *list_del_last(list_t *); 117 | lnode_t *list_delete(list_t *, lnode_t *); 118 | 119 | void list_process(list_t *, void *, void (*)(list_t *, lnode_t *, void *)); 120 | 121 | int list_verify(list_t *); 122 | 123 | #if defined(LIST_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG) 124 | #define lnode_pool_isempty(P) ((P)->list_free == 0) 125 | #define list_count(L) ((L)->list_nodecount) 126 | #define list_isempty(L) ((L)->list_nodecount == 0) 127 | #define list_isfull(L) (LIST_SFX_CHECK(L)->list_nodecount == (L)->list_maxcount) 128 | #define list_next(L, N) (LIST_SFX_CHECK(N)->list_next == &(L)->list_nilnode ? NULL : (N)->list_next) 129 | #define list_prev(L, N) (LIST_SFX_CHECK(N)->list_prev == &(L)->list_nilnode ? NULL : (N)->list_prev) 130 | #define list_first(L) list_next(LIST_SFX_CHECK(L), &(L)->list_nilnode) 131 | #define list_last(L) list_prev(LIST_SFX_CHECK(L), &(L)->list_nilnode) 132 | #endif 133 | 134 | #if defined(LIST_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG) 135 | #define list_append(L, N) list_ins_before(LIST_SFX_CHECK(L), N, &(L)->list_nilnode) 136 | #define list_prepend(L, N) list_ins_after(LIST_SFX_CHECK(L), N, &(L)->list_nilnode) 137 | #define list_del_first(L) list_delete(LIST_SFX_CHECK(L), list_first(L)) 138 | #define list_del_last(L) list_delete(LIST_SFX_CHECK(L), list_last(L)) 139 | #endif 140 | 141 | /* destination list on the left, source on the right */ 142 | 143 | void list_extract(list_t *, list_t *, lnode_t *, lnode_t *); 144 | void list_transfer(list_t *, list_t *, lnode_t *first); 145 | void list_merge(list_t *, list_t *, int (const void *, const void *)); 146 | void list_sort(list_t *, int (const void *, const void *)); 147 | lnode_t *list_find(list_t *, const void *, int (const void *, const void *)); 148 | int list_is_sorted(list_t *, int (const void *, const void *)); 149 | 150 | #ifdef __cplusplus 151 | } 152 | #endif 153 | 154 | #endif 155 | -------------------------------------------------------------------------------- /src/module.c: -------------------------------------------------------------------------------- 1 | #include "module.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "directives.h" 9 | #include "grammar.h" 10 | #include 11 | #include "error.h" 12 | #include 13 | #include 14 | 15 | Module *Module_create(const char *name, size_t max_code_size) 16 | { 17 | Module *state = GC_MALLOC( sizeof(Module)); 18 | assert(state && "Memory alloc failure for Module."); 19 | state->curline = 1; 20 | state->module_name = strdup(name); 21 | state->max_code_size = max_code_size; 22 | 23 | return state; 24 | } 25 | 26 | Module *Module_resolve_module(Module *state, Token *ident) 27 | { 28 | if(CORD_cmp(ident->data, "self")) { 29 | return state; 30 | } else { 31 | return (Module *)tst_search(state->imports, ident->start, ident->len); 32 | } 33 | } 34 | 35 | Token *Module_resolve_function_data(Function *func, Token *ident) 36 | { 37 | Token *expr = tst_search(func->data, ident->start, ident->len); 38 | if(expr) { 39 | *ident = *expr; // copy it over reusing the ident 40 | return ident; 41 | } else { 42 | return NULL; 43 | } 44 | } 45 | 46 | Token *Module_outside_function_data(Module *state, Token *func_ref, Token *ident) 47 | { 48 | Token *expr = NULL; 49 | Function *func = Function_find(state, func_ref->start, func_ref->len); 50 | 51 | if(func) expr = Module_resolve_function_data(func, ident); 52 | 53 | if(!expr) { 54 | die(state, "function %.*s doesn't have data named %.*s", 55 | func_ref->len, func_ref->start, ident->len, ident->start); 56 | } 57 | 58 | return expr; 59 | } 60 | 61 | Token *Module_resolve_data(Module *state, Token *ident) 62 | { 63 | if(!state->current) { 64 | Token *expr = tst_search(state->data, ident->start, ident->len); 65 | if(expr) { 66 | return expr; 67 | } else { 68 | die(state, "failed to find %.*s module constant.", ident->len, ident->start); 69 | return NULL; 70 | } 71 | } 72 | 73 | Token *expr = Module_resolve_function_data(state->current, ident); 74 | 75 | if(expr) { 76 | return expr; 77 | } else { 78 | expr = Function_resolve_param(state, state->current, ident); 79 | 80 | if(expr) { 81 | return expr; 82 | } else { 83 | // try to find it in the module 84 | expr = tst_search(state->data, ident->start, ident->len); 85 | 86 | if(expr) { 87 | *ident = *expr; 88 | return ident; 89 | } else { 90 | die(state, "failed to resolve %.*s, not a parameter or constant\n", 91 | ident->len, ident->start); 92 | return NULL; 93 | } 94 | } 95 | } 96 | } 97 | 98 | Token *Module_resolve_function(Module *state, Token *module, Token *ident) 99 | { 100 | void *result = NULL; 101 | 102 | Module *mod = Module_resolve_module(state, module); 103 | if(mod) { 104 | Function *func = Function_find(mod, ident->start, ident->len); 105 | if(func) { 106 | result = func->code; 107 | } 108 | } else { 109 | result = Library_search(state, module, ident); 110 | } 111 | 112 | if(result != NULL) { 113 | // it's a function defined in this module 114 | ident->value = (unsigned long)result; 115 | } else { 116 | // it's not found so not a possible ident 117 | die(state, "did not find function %.*s in a library or module named %.*s.", 118 | ident->len, ident->start, 119 | module->len, module->start); 120 | } 121 | 122 | return ident; 123 | } 124 | 125 | void Module_call_directive(Module *state, Token *ident, Parameters *params) 126 | { 127 | Directive *to_call = (Directive *)tst_search(state->directives, ident->start, ident->len); 128 | 129 | if(to_call) { 130 | to_call->call(state, params); 131 | } else { 132 | die(state, "referenced directive %.*s which doesn't exist.", ident->len, ident->start); 133 | } 134 | } 135 | 136 | int Module_register_directive(Module *state, const char *name, int length, 137 | Module_directive_cb call, Module_directive_cb destroy) 138 | { 139 | Directive *d = GC_MALLOC(sizeof(Directive)); 140 | assert(d && "Memory allocation failed for Directive."); 141 | 142 | if(tst_search(state->directives, name, length)) { 143 | die(state, "Directive %.*s already exists in the directive list.", length, name); 144 | return 0; 145 | } else { 146 | d->name = name; 147 | d->len = length; 148 | d->call = call; 149 | d->destroy = destroy; 150 | 151 | state->directives = tst_insert(state->directives, name, length, d); 152 | 153 | return 1; 154 | } 155 | } 156 | 157 | void Module_create_constant(Module *state, Token *ident, Token *expr) 158 | { 159 | if(tst_search(state->data, ident->start, ident->len)) { 160 | die(state, "you already defined a constant named %.*s in this module.", 161 | ident->len, ident->start); 162 | } else { 163 | state->data = tst_insert(state->data, ident->start, ident->len, expr); 164 | assert(state->data && "Unable to insert an element into the module data store."); 165 | 166 | if(expr->id == TK_STR) { 167 | // need to disconnect this from its actual source for reference later. 168 | expr->value = (unsigned long)strndup(expr->start, expr->len); 169 | } 170 | } 171 | } 172 | 173 | void Module_create_function_constant(Module *state, Token *ident, Token *expr) 174 | { 175 | if(tst_search(state->current->data, ident->start, ident->len)) { 176 | die(state, "you already defined a function constant named %.*s", 177 | ident->len, ident->start); 178 | } else { 179 | state->current->data = tst_insert(state->current->data, ident->start, ident->len, expr); 180 | assert(state->current->data && "Unable to insert an element into the module data store."); 181 | 182 | if(expr->id == TK_STR) { 183 | // need to disconnect this from its actual source for reference later. 184 | expr->value = (unsigned long)strndup(expr->start, expr->len); 185 | } 186 | } 187 | } 188 | 189 | void Module_dump_directives(Module *state, FILE *out) 190 | { 191 | void dump(void *p, void *ignored) { 192 | Directive *d = p; 193 | fprintf(out, "directive: %.*s\n", d->len, d->name); 194 | } 195 | 196 | tst_traverse(state->directives, dump, NULL); 197 | } 198 | 199 | -------------------------------------------------------------------------------- /src/module.h: -------------------------------------------------------------------------------- 1 | #ifndef module_h 2 | #define module_h 3 | 4 | #include 5 | #include 6 | #include "tst.h" 7 | #include "token.h" 8 | #include 9 | #include "array.h" 10 | #include "hash.h" 11 | #include "types.h" 12 | #include "function.h" 13 | 14 | typedef struct Machine { 15 | tst_t *directives; 16 | tst_t *libraries; 17 | tst_t *modules; 18 | } Machine; 19 | 20 | 21 | typedef struct Module { 22 | CORD module_name; 23 | 24 | unsigned int curline; 25 | int errors; 26 | int current_is_leaf; // kind of a hack, need something else 27 | Function *current; 28 | size_t max_code_size; 29 | 30 | hash_t *data; 31 | hash_t *imports; 32 | hash_t *libraries; 33 | hash_t *functions; 34 | } Module; 35 | 36 | 37 | typedef void(*Module_directive_cb)(Module *state, array_t *params); 38 | typedef void(*Module_default_directives_cb)(); 39 | 40 | int Module_compile(Module *state, CORD source); 41 | 42 | int Module_register_directive(Module *state, CORD name, 43 | Module_directive_cb call, Module_directive_cb destroy); 44 | 45 | void Module_dump_directives(Module *state, FILE *out); 46 | void Module_call_directive(Module *state, Token *ident, array_t *params); 47 | 48 | Token *Module_resolve_function(Module *state, Token *module, Token *ident); 49 | Token *Module_resolve_data(Module *state, Token *ident); 50 | Token *Module_outside_function_data(Module *state, Token *func_ref, Token *ident); 51 | 52 | Module *Module_resolve_module(Module *state, Token *ident); 53 | 54 | Module *Module_create(const char *name, size_t max_code_size); 55 | 56 | void Module_create_constant(Module *state, Token *ident, Token *expr); 57 | void Module_create_function_constant(Module *state, Token *ident, Token *expr); 58 | 59 | void Call_operation(Module *state, Function *func, Token *op, Token *type, array_t *params); 60 | 61 | #endif 62 | 63 | 64 | -------------------------------------------------------------------------------- /src/ops.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef ops_h 3 | #define ops_h 4 | 5 | #include "module.h" 6 | #include "array.h" 7 | 8 | #include "allocator_ops.h" 9 | 10 | int inst_addr(Module *state, Token *type, array_t *params); 11 | int inst_addi(Module *state, Token *type, array_t *params); 12 | int inst_addxr(Module *state, Token *type, array_t *params); 13 | int inst_addxi(Module *state, Token *type, array_t *params); 14 | int inst_addcr(Module *state, Token *type, array_t *params); 15 | int inst_addci(Module *state, Token *type, array_t *params); 16 | int inst_subr(Module *state, Token *type, array_t *params); 17 | int inst_subi(Module *state, Token *type, array_t *params); 18 | int inst_subxr(Module *state, Token *type, array_t *params); 19 | int inst_subxi(Module *state, Token *type, array_t *params); 20 | int inst_subcr(Module *state, Token *type, array_t *params); 21 | int inst_subci(Module *state, Token *type, array_t *params); 22 | int inst_rsbr(Module *state, Token *type, array_t *params); 23 | int inst_rsbi(Module *state, Token *type, array_t *params); 24 | int inst_mulr(Module *state, Token *type, array_t *params); 25 | int inst_muli(Module *state, Token *type, array_t *params); 26 | int inst_hmulr(Module *state, Token *type, array_t *params); 27 | int inst_hmuli(Module *state, Token *type, array_t *params); 28 | int inst_divr(Module *state, Token *type, array_t *params); 29 | int inst_divi(Module *state, Token *type, array_t *params); 30 | int inst_modr(Module *state, Token *type, array_t *params); 31 | int inst_modi(Module *state, Token *type, array_t *params); 32 | int inst_andr(Module *state, Token *type, array_t *params); 33 | int inst_andi(Module *state, Token *type, array_t *params); 34 | int inst_orr(Module *state, Token *type, array_t *params); 35 | int inst_ori(Module *state, Token *type, array_t *params); 36 | int inst_xorr(Module *state, Token *type, array_t *params); 37 | int inst_xori(Module *state, Token *type, array_t *params); 38 | int inst_lshr(Module *state, Token *type, array_t *params); 39 | int inst_lshi(Module *state, Token *type, array_t *params); 40 | int inst_rshr(Module *state, Token *type, array_t *params); 41 | int inst_rshi(Module *state, Token *type, array_t *params); 42 | int inst_negr(Module *state, Token *type, array_t *params); 43 | int inst_notr(Module *state, Token *type, array_t *params); 44 | int inst_ltr(Module *state, Token *type, array_t *params); 45 | int inst_lti(Module *state, Token *type, array_t *params); 46 | int inst_ler(Module *state, Token *type, array_t *params); 47 | int inst_lei(Module *state, Token *type, array_t *params); 48 | int inst_gtr(Module *state, Token *type, array_t *params); 49 | int inst_gti(Module *state, Token *type, array_t *params); 50 | int inst_ger(Module *state, Token *type, array_t *params); 51 | int inst_gei(Module *state, Token *type, array_t *params); 52 | int inst_eqr(Module *state, Token *type, array_t *params); 53 | int inst_eqi(Module *state, Token *type, array_t *params); 54 | int inst_ner(Module *state, Token *type, array_t *params); 55 | int inst_nei(Module *state, Token *type, array_t *params); 56 | int inst_unltr(Module *state, Token *type, array_t *params); 57 | int inst_unler(Module *state, Token *type, array_t *params); 58 | int inst_ungtr(Module *state, Token *type, array_t *params); 59 | int inst_unger(Module *state, Token *type, array_t *params); 60 | int inst_uneqr(Module *state, Token *type, array_t *params); 61 | int inst_ltgtr(Module *state, Token *type, array_t *params); 62 | int inst_ordr(Module *state, Token *type, array_t *params); 63 | int inst_unordr(Module *state, Token *type, array_t *params); 64 | int inst_movr(Module *state, Token *type, array_t *params); 65 | int inst_movi(Module *state, Token *type, array_t *params); 66 | int inst_hton(Module *state, Token *type, array_t *params); 67 | int inst_ntoh(Module *state, Token *type, array_t *params); 68 | int inst_ldr(Module *state, Token *type, array_t *params); 69 | int inst_ldi(Module *state, Token *type, array_t *params); 70 | int inst_ldxr(Module *state, Token *type, array_t *params); 71 | int inst_ldxi(Module *state, Token *type, array_t *params); 72 | int inst_str(Module *state, Token *type, array_t *params); 73 | int inst_sti(Module *state, Token *type, array_t *params); 74 | int inst_stxr(Module *state, Token *type, array_t *params); 75 | int inst_stxi(Module *state, Token *type, array_t *params); 76 | int inst_prepare(Module *state, Token *type, array_t *params); 77 | int inst_pusharg(Module *state, Token *type, array_t *params); 78 | int inst_getarg(Module *state, Token *type, array_t *params); 79 | int inst_blti(Module *state, Token *type, array_t *params); 80 | int inst_blei(Module *state, Token *type, array_t *params); 81 | int inst_bgti(Module *state, Token *type, array_t *params); 82 | int inst_bgei(Module *state, Token *type, array_t *params); 83 | int inst_beqi(Module *state, Token *type, array_t *params); 84 | int inst_bnei(Module *state, Token *type, array_t *params); 85 | int inst_bmsr(Module *state, Token *type, array_t *params); 86 | int inst_bmsi(Module *state, Token *type, array_t *params); 87 | int inst_bmcr(Module *state, Token *type, array_t *params); 88 | int inst_bmci(Module *state, Token *type, array_t *params); 89 | int inst_calli(Module *state, Token *type, array_t *params); 90 | int inst_callr(Module *state, Token *type, array_t *params); 91 | int inst_finish(Module *state, Token *type, array_t *params); 92 | int inst_finishr(Module *state, Token *type, array_t *params); 93 | int inst_jmpi(Module *state, Token *type, array_t *params); 94 | int inst_jmpr(Module *state, Token *type, array_t *params); 95 | int inst_ret(Module *state, Token *type, array_t *params); 96 | int inst_retval(Module *state, Token *type, array_t *params); 97 | 98 | 99 | #endif 100 | -------------------------------------------------------------------------------- /src/ops_gen.py: -------------------------------------------------------------------------------- 1 | import re 2 | import sys 3 | 4 | header = """ 5 | #ifndef ops_h 6 | #define ops_h 7 | 8 | #include "module.h" 9 | #include "array.h" 10 | 11 | #include "allocator_ops.h" 12 | 13 | %s 14 | 15 | #endif 16 | """ 17 | 18 | function_decl = "int inst_%(op)s(Module *state, Token *type, array_t *params);\n" 19 | 20 | typed_function = """ 21 | int inst_%(op)s(Module *state, Token *type, array_t *params) { 22 | if(array_length(params) != %(pcount)s) 23 | die(state, "%(op)s requires %(pcount)s parameters."); 24 | if(type) die(state, "Operation %(op)s does not take a type."); 25 | 26 | jit_%(op)s (%(args)s); 27 | 28 | return 1; 29 | } 30 | """ 31 | 32 | 33 | type_case = """ 34 | case OpType_%(type)s: 35 | jit_%(op)s_%(type)s (%(args)s); break; 36 | """ 37 | 38 | untyped_function = """ 39 | int inst_%(op)s(Module *state, Token *type, array_t *params) { 40 | if(array_length(params) != %(pcount)s) 41 | die(state, "%(op)s requires %(pcount)s parameters."); 42 | if(!type) 43 | die(state, "%(op)s requires type of: %(types)s."); 44 | 45 | switch(type->value) { 46 | %(type_case)s 47 | default: 48 | die(state, "Invalid type given for operation %(op)s, only %(types)s allowed."); 49 | } 50 | return 1; 51 | } 52 | """ 53 | 54 | 55 | instructions = open("src/instructions.txt").read().strip() 56 | 57 | declarations = [] 58 | 59 | lines = instructions.split("\n") 60 | commented = [x.split("|") for x in lines] 61 | spaces = re.compile(" +") 62 | ops_and_types = [(spaces.split(x[0].strip()), x[1].strip()) for x in commented] 63 | ops_and_types = [(x[0][0], x[0][1:], x[1]) for x in ops_and_types] 64 | counted = [(x[0], x[1], x[2], x[2].count("O")) for x in ops_and_types] 65 | final = [{"op": x[0], "types": x[1], "comment": x[2], "pcount": x[3]} for x in 66 | counted] 67 | 68 | defs = open("src/ops.c", "w") 69 | decls = open("src/ops.h", "w") 70 | 71 | defs.write('#include "module.h"\n#include "allocator_ops.c"\n') 72 | 73 | for op in final: 74 | op["args"] = ", ".join(["array_of(Token, params, %d)->value" % x for x in range(0,op["pcount"])]) 75 | op["type_case"] = "".join([type_case % {"op": op["op"], "type": type, "args": 76 | op["args"]} for type in op["types"]]) 77 | 78 | declarations.append(function_decl % op) 79 | 80 | if len(op["types"]) > 0: 81 | defs.write(untyped_function % op) 82 | else: 83 | defs.write(typed_function % op) 84 | 85 | decls.write(header % "".join(declarations)) 86 | 87 | defs.close() 88 | decls.close() 89 | 90 | -------------------------------------------------------------------------------- /src/repl.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "repl.h" 3 | #include 4 | #include 5 | #include 6 | 7 | void repl_init(complete_function func) 8 | { 9 | // does nothing until I can figure out how to portably use readline 10 | } 11 | 12 | char *repl_prompt() 13 | { 14 | char *line = GC_MALLOC(1024 * sizeof(char)); 15 | 16 | printf(">>> "); 17 | 18 | char *result = fgets(line, 1023, stdin); 19 | 20 | if(result) { 21 | result[strlen(result)-1] = '\0'; 22 | } 23 | 24 | return result; 25 | } 26 | -------------------------------------------------------------------------------- /src/repl.h: -------------------------------------------------------------------------------- 1 | #ifndef repl_h 2 | #define repl_h 3 | 4 | typedef char **(*complete_function)(const char *string, int start, int end); 5 | 6 | void repl_init(complete_function func); 7 | char *repl_prompt(); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /src/struct.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This program attempts to determine struct layout rules for the compiler 3 | * in use. It is only approximate, of course. It looks for: 4 | * packed (no gaps) 5 | * aligned on 4 bytes 6 | * aligned on 8 bytes 7 | * aligned on sizeof(largest member) 8 | * Suggestions for further improvements are welcome; send to 9 | * mpi-bugs@mcs.anl.gov 10 | * 11 | * The IBM AIX compiler xlC will produce multiple packings. Try 12 | * xlC -qalign=twobyte structlayout.c 13 | * xlC -qalign=packed structlayout.c 14 | * xlC -qalign=power structlayout.c 15 | */ 16 | #include "struct.h" 17 | 18 | 19 | 20 | StructAlign StructAlign_create(void) 21 | { 22 | StructAlign sa = { 23 | .is_packed = 1, 24 | .is_two = 1, 25 | .is_four = 1, 26 | .is_eight = 1, 27 | .is_largest = 1, 28 | .size = 0, 29 | .extent = 0 30 | }; 31 | 32 | struct { char a; int b; } char_int; 33 | struct { char a; short b; } char_short; 34 | struct { char a; long b; } char_long; 35 | struct { char a; float b; } char_float; 36 | struct { char a; double b; } char_double; 37 | struct { char a; int b; char c; } char_int_char; 38 | struct { char a; short b; char c; } char_short_char; 39 | struct { char a; long double b; } char_long_double; 40 | 41 | sa.size = sizeof(char) + sizeof(int); 42 | sa.extent = sizeof(char_int); 43 | if (sa.size != sa.extent) sa.is_packed = 0; 44 | if ( (sa.extent % sizeof(int)) != 0) sa.is_largest = 0; 45 | if ( (sa.extent % 2) != 0) sa.is_two = 0; 46 | if ( (sa.extent % 4) != 0) sa.is_four = 0; 47 | if (sizeof(int) == 8 && (sa.extent % 8) != 0) sa.is_eight = 0; 48 | 49 | sa.size = sizeof(char) + sizeof(short); 50 | sa.extent = sizeof(char_short); 51 | if (sa.size != sa.extent) sa.is_packed = 0; 52 | if ( (sa.extent % sizeof(short)) != 0) sa.is_largest = 0; 53 | if ( (sa.extent % 2) != 0) sa.is_two = 0; 54 | if (sizeof(short) == 4 && (sa.extent % 4) != 0) sa.is_four = 0; 55 | if (sizeof(short) == 8 && (sa.extent % 8) != 0) sa.is_eight = 0; 56 | 57 | sa.size = sizeof(char) + sizeof(long); 58 | sa.extent = sizeof(char_long); 59 | if (sa.size != sa.extent) sa.is_packed = 0; 60 | if ( (sa.extent % sizeof(long)) != 0) sa.is_largest = 0; 61 | if ( (sa.extent % 2) != 0) sa.is_two = 0; 62 | if ( (sa.extent % 4) != 0) sa.is_four = 0; 63 | if (sizeof(long) == 8 && (sa.extent % 8) != 0) sa.is_eight = 0; 64 | 65 | sa.size = sizeof(char) + sizeof(float); 66 | sa.extent = sizeof(char_float); 67 | if (sa.size != sa.extent) sa.is_packed = 0; 68 | if ( (sa.extent % sizeof(float)) != 0) sa.is_largest = 0; 69 | if ( (sa.extent % 2) != 0) sa.is_two = 0; 70 | if ( (sa.extent % 4) != 0) sa.is_four = 0; 71 | if (sizeof(float) == 8 && (sa.extent % 8) != 0) sa.is_eight = 0; 72 | 73 | sa.size = sizeof(char) + sizeof(double); 74 | sa.extent = sizeof(char_double); 75 | if (sa.size != sa.extent) sa.is_packed = 0; 76 | if ( (sa.extent % sizeof(double)) != 0) sa.is_largest = 0; 77 | if ( (sa.extent % 2) != 0) sa.is_two = 0; 78 | if ( (sa.extent % 4) != 0) sa.is_four = 0; 79 | if (sizeof(double) == 8 && (sa.extent % 8) != 0) sa.is_eight = 0; 80 | 81 | sa.size = sizeof(char) + sizeof(long double); 82 | sa.extent = sizeof(char_long_double); 83 | if (sa.size != sa.extent) sa.is_packed = 0; 84 | if ( (sa.extent % sizeof(long double)) != 0) sa.is_largest = 0; 85 | if ( (sa.extent % 2) != 0) sa.is_two = 0; 86 | if ( (sa.extent % 4) != 0) sa.is_four = 0; 87 | if (sizeof(long double) >= 8 && (sa.extent % 8) != 0) sa.is_eight = 0; 88 | 89 | /* char int char helps separate largest from 4/8 aligned */ 90 | sa.size = sizeof(char) + sizeof(int) + sizeof(char); 91 | sa.extent = sizeof(char_int_char); 92 | if (sa.size != sa.extent) sa.is_packed = 0; 93 | if ( (sa.extent % sizeof(int)) != 0) sa.is_largest = 0; 94 | if ( (sa.extent % 2) != 0) sa.is_two = 0; 95 | if ( (sa.extent % 4) != 0) sa.is_four = 0; 96 | if (sizeof(int) == 8 && (sa.extent % 8) != 0) sa.is_eight = 0; 97 | 98 | /* char short char helps separate largest from 4/8 aligned */ 99 | sa.size = sizeof(char) + sizeof(short) + sizeof(char); 100 | sa.extent = sizeof(char_short_char); 101 | if (sa.size != sa.extent) sa.is_packed = 0; 102 | if ( (sa.extent % sizeof(short)) != 0) sa.is_largest = 0; 103 | if ( (sa.extent % 2) != 0) sa.is_two = 0; 104 | if (sizeof(short) == 4 && (sa.extent % 4) != 0) sa.is_four = 0; 105 | if (sizeof(short) == 8 && (sa.extent % 8) != 0) sa.is_eight = 0; 106 | 107 | /* If aligned mod 8, it will be aligned mod 4 */ 108 | if (sa.is_eight) { sa.is_four = 0; sa.is_two = 0; } 109 | 110 | if (sa.is_four) sa.is_two = 0; 111 | 112 | /* largest superceeds eight */ 113 | if (sa.is_largest) sa.is_eight = 0; 114 | 115 | return sa; 116 | } 117 | 118 | 119 | int StructAlign_indeterminate(StructAlign sa) { 120 | if (sa.is_packed + sa.is_largest + sa.is_two + sa.is_four + sa.is_eight == 0) { 121 | return 1; 122 | } else { 123 | return 0; 124 | } 125 | } 126 | 127 | int StructAlign_multiple_cases(StructAlign sa) { 128 | if (sa.is_packed + sa.is_largest + sa.is_two + sa.is_four + sa.is_eight != 1) { 129 | return 1; 130 | } else { 131 | return 0; 132 | } 133 | } 134 | 135 | -------------------------------------------------------------------------------- /src/struct.h: -------------------------------------------------------------------------------- 1 | #ifndef struct_h 2 | #define struct_h 3 | 4 | typedef struct StructAlign { 5 | int is_packed; 6 | int is_two; 7 | int is_four; 8 | int is_eight; 9 | int is_largest; 10 | int size; 11 | int extent; 12 | } StructAlign; 13 | 14 | StructAlign StructAlign_create(void); 15 | int StructAlign_indeterminate(StructAlign sa); 16 | int StructAlign_multiple_cases(StructAlign sa); 17 | 18 | #define StructAlign_is_packed(A) ((A).is_packed) 19 | #define StructAlign_is_largest(A) ((A).is_largest) 20 | #define StructAlign_is_two(A) ((A).is_two) 21 | #define StructAlign_is_four(A) ((A).is_four) 22 | #define StructAlign_is_eight(A) ((A).is_eight) 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /src/token.c: -------------------------------------------------------------------------------- 1 | #include "token.h" 2 | #include 3 | #include "grammar.h" 4 | #include 5 | #include 6 | #include 7 | 8 | 9 | Token *Token_create(int id, CORD data, f_inst_call call) 10 | { 11 | Token *tk = GC_MALLOC(sizeof(Token)); 12 | tk->id = id; 13 | tk->data = data; 14 | tk->call = call; 15 | 16 | switch(id) { 17 | case TK_CHR: 18 | tk->value = (int)CORD_fetch(tk->data, 1); 19 | break; 20 | case TK_STR: 21 | tk->value = (unsigned long)CORD_to_char_star(tk->data); 22 | break; 23 | case TK_INT: 24 | tk->value = atoi(CORD_to_const_char_star(tk->data)); 25 | break; 26 | case TK_FLOAT: 27 | tk->value = atof(CORD_to_const_char_star(tk->data)); 28 | break; 29 | case TK_HEX: 30 | tk->value = (int)strtol(CORD_to_const_char_star(tk->data), NULL, 16); 31 | break; 32 | default: 33 | tk->value = 0; 34 | } 35 | 36 | return tk; 37 | } 38 | 39 | -------------------------------------------------------------------------------- /src/token.h: -------------------------------------------------------------------------------- 1 | #ifndef token_h 2 | #define token_h 3 | 4 | #include "array.h" 5 | #include 6 | 7 | struct Module; 8 | struct Token; 9 | 10 | /** 11 | * This is attached to operation tokens and is called later to do the actual byte code 12 | * generation. 13 | */ 14 | typedef int (*f_inst_call)(struct Module *state, struct Token *type, array_t *params); 15 | 16 | /** 17 | * A Token holds almost all of the data, since an assembler is so simple 18 | * we can actually put the values inside the token. 19 | */ 20 | typedef struct Token { 21 | // all tokens have this 22 | int id; 23 | CORD data; 24 | 25 | // different tokens have these set mutually exclusive 26 | f_inst_call call; 27 | // lightning apparently does lots of casting, so we just need the biggest numeric for storage 28 | unsigned long value; 29 | } Token; 30 | 31 | Token *Token_create(int id, CORD data, f_inst_call call); 32 | 33 | #define TK(I,C) (Token_create(I, CORD_from_char_star(te-ts,ts), C)) 34 | #define KW(T) Parse(pParser, TK_##T, TK(TK_##T,0), state); 35 | #define SYM(T) Parse(pParser, TK_##T, TK(TK_##T,0), state); 36 | #define IDENT() Parse(pParser, TK_IDENT, TK(TK_IDENT,0), state); 37 | #define OP(T) Parse(pParser, TK_OP, TK(TK_OP,inst_##T), state); 38 | #define TYPE(T) tk = TK(TK_TYPE,0); tk->value = OpType_##T; Parse(pParser, TK_TYPE, tk, state); 39 | #define LABEL() Parse(pParser, TK_LABEL, TK(TK_LABEL,0), state); 40 | #define NO_LEAF(name) if(state->current_is_leaf)\ 41 | die(state, "You cannot do " # name " in a function declared %%leaf."); 42 | #define REG(T) tk = TK(TK_REG,0); tk->value = JIT_##T; Parse(pParser, TK_REG, tk, state); 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /src/tst.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "tst.h" 3 | #include 4 | #include 5 | #include 6 | 7 | static void tst_collect_build(void *value, tst_collect_t *results) 8 | { 9 | if(results->length < 100) { 10 | results->values[results->length++] = value; 11 | } 12 | } 13 | 14 | tst_collect_t tst_collect(tst_t *root, const char *s, int len) 15 | { 16 | tst_collect_t results = {.length = 0, .values = NULL}; 17 | tst_t *p = root; 18 | int i = 0; 19 | results.values = GC_MALLOC(100 * sizeof(void *)); 20 | 21 | // first we get to where we match the prefix 22 | while(i < len && p) { 23 | if (s[i] < p->splitchar) { 24 | p = p->low; 25 | } else if (s[i] == p->splitchar) { 26 | i++; 27 | p = p->equal; 28 | } else { 29 | p = p->high; 30 | } 31 | } 32 | 33 | if(p) { 34 | // we found node matching this prefix, so traverse and collect 35 | tst_traverse(p, (tst_traverse_cb)tst_collect_build, &results); 36 | } 37 | 38 | return results; 39 | } 40 | 41 | void *tst_search(tst_t *root, const char *s, int len) 42 | { 43 | tst_t *p = root; 44 | int i = 0; 45 | 46 | while(i < len && p) { 47 | if (s[i] < p->splitchar) { 48 | p = p->low; 49 | } else if (s[i] == p->splitchar) { 50 | i++; 51 | if(i < len) p = p->equal; 52 | } else { 53 | p = p->high; 54 | } 55 | } 56 | 57 | if(p) { 58 | return p->value; 59 | } else { 60 | return NULL; 61 | } 62 | } 63 | 64 | tst_t *tst_insert(tst_t *p, const char *s, int len, void *value) 65 | { 66 | if (p == NULL) { 67 | p = (tst_t *) GC_MALLOC(sizeof(tst_t)); 68 | p->splitchar = *s; 69 | } 70 | 71 | if (*s < p->splitchar) { 72 | p->low = tst_insert(p->low, s, len, value); 73 | } else if (*s == p->splitchar) { 74 | if (len > 1) { 75 | // not done yet, keep going but one less 76 | p->equal = tst_insert(p->equal, s+1, len - 1, value); 77 | } else { 78 | p->value = value; 79 | } 80 | } else { 81 | p->high = tst_insert(p->high, s, len, value); 82 | } 83 | 84 | return p; 85 | } 86 | 87 | void tst_traverse(tst_t *p, tst_traverse_cb cb, void *data) 88 | { 89 | if (!p) return; 90 | 91 | if(p->low) tst_traverse(p->low, cb, data); 92 | 93 | if (p->equal) { 94 | tst_traverse(p->equal, cb, data); 95 | } 96 | 97 | if(p->high) tst_traverse(p->high, cb, data); 98 | 99 | if(p->value) cb(p->value, data); 100 | } 101 | 102 | -------------------------------------------------------------------------------- /src/tst.h: -------------------------------------------------------------------------------- 1 | #ifndef tst_h 2 | #define tst_h 3 | 4 | #include 5 | 6 | typedef struct tst_t { 7 | char splitchar; 8 | struct tst_t *low; 9 | struct tst_t *equal; 10 | struct tst_t *high; 11 | void *value; 12 | } tst_t; 13 | 14 | typedef struct tst_collect_t { 15 | size_t length; 16 | void **values; 17 | } tst_collect_t; 18 | 19 | typedef void (*tst_traverse_cb)(void *value, void *data); 20 | 21 | void *tst_search(tst_t *root, const char *s, int len); 22 | 23 | tst_t *tst_insert(tst_t *p, const char *s, int len, void *value); 24 | 25 | void tst_traverse(tst_t *p, tst_traverse_cb cb, void *data); 26 | 27 | tst_collect_t tst_collect(tst_t *root, const char *s, int len); 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /src/types.c: -------------------------------------------------------------------------------- 1 | #include "types.h" 2 | #include 3 | 4 | 5 | const char *OpType_strings[] = { 6 | "int", "uint", "long", "ulong", 7 | "ptr", "float", "double", "void", 8 | "char", "uchar", "short", "ushort" 9 | }; 10 | 11 | const char *OpType_to_str(OpType type) 12 | { 13 | assert(type >= 0 && type < 12 && "Invalid OpType_code"); 14 | return OpType_strings[type]; 15 | } 16 | -------------------------------------------------------------------------------- /src/types.h: -------------------------------------------------------------------------------- 1 | #ifndef types_h 2 | #define types_h 3 | 4 | /** 5 | * These are used to determine the types of both operations and function returns. 6 | * For an operation it follows a '.' as in movi.ui(...). For functions it follows 7 | * the function's name after a ':' as in function adder : int. The lexer will actually 8 | * allow shorthand (i,ui,s,v) or long hand forms (int, uint, short, void) for 9 | * both functions and operations. 10 | */ 11 | typedef enum OpType { 12 | OpType_i, 13 | OpType_ui, 14 | OpType_l, 15 | OpType_ul, 16 | OpType_p, 17 | OpType_f, 18 | OpType_d, 19 | OpType_v, 20 | OpType_c, 21 | OpType_uc, 22 | OpType_s, 23 | OpType_us 24 | } OpType; 25 | 26 | const char *OpType_to_str(OpType type); 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /src/util.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "module.h" 8 | #include "error.h" 9 | #include 10 | #include "list.h" 11 | 12 | void run_function(Module *state, const char *named) 13 | { 14 | Function *func = NULL; 15 | 16 | func = tst_search(state->functions, named, strlen(named)); 17 | 18 | if(!func) { 19 | printf("Undefined function: '%s'\n", named); 20 | } else { 21 | if(list_isempty(func->params)) { 22 | printf("Sorry, this function takes parameters so it's not supported to call it yet.\n"); 23 | return; 24 | } 25 | 26 | switch(func->type) { 27 | case OpType_v: 28 | printf(">>\n"); func->fp.vptr(); break; 29 | case OpType_c: 30 | printf(">> %c\n", func->fp.cptr()); break; 31 | case OpType_uc: 32 | printf(">> %c\n", func->fp.ucptr()); break; 33 | case OpType_s: 34 | printf(">> %i\n", func->fp.sptr()); break; 35 | case OpType_us: 36 | printf(">> %i\n", func->fp.usptr()); break; 37 | case OpType_i: 38 | printf(">> %d\n", func->fp.iptr()); break; 39 | case OpType_ui: 40 | printf(">> %u\n", func->fp.uiptr()); break; 41 | case OpType_l: 42 | printf(">> %ld\n", func->fp.lptr()); break; 43 | case OpType_ul: 44 | printf(">> %lu\n", func->fp.ulptr()); break; 45 | case OpType_p: 46 | printf(">> %p\n", func->fp.pptr()); break; 47 | case OpType_f: 48 | printf(">> %f\n", func->fp.fptr()); break; 49 | case OpType_d: 50 | printf(">> %f\n", func->fp.dptr()); break; 51 | } 52 | } 53 | } 54 | 55 | -------------------------------------------------------------------------------- /src/util.h: -------------------------------------------------------------------------------- 1 | #ifndef util_h 2 | #define util_h 3 | 4 | char *mmap_file(const char *fname, size_t *length); 5 | void run_function(Module *state, const char *named); 6 | 7 | #endif 8 | 9 | 10 | -------------------------------------------------------------------------------- /tests/allocator_tests.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "allocator.h" 3 | #include 4 | 5 | void __CUT_allocator_Bringup(void) 6 | { 7 | GC_INIT(); 8 | } 9 | 10 | void __CUT__Allocator_expression( void ) 11 | { 12 | Token alloc = {.value = 100L}; 13 | 14 | Token *res = Allocator_expression(NULL, &alloc); 15 | ASSERT(res != NULL, "Failed to run allocator expression."); 16 | ASSERT(res->value, "Value wasn't set."); 17 | } 18 | 19 | 20 | void __CUT__Allocator_malloc(void) 21 | { 22 | void *data = Allocator_malloc(100); 23 | ASSERT(data != NULL, "Failed to allocate."); 24 | } 25 | 26 | 27 | void __CUT__Allocator_realloc(void) 28 | { 29 | void *data = Allocator_malloc(100); 30 | ASSERT(data != NULL, "Failed to allocate."); 31 | 32 | data = Allocator_realloc(data, 1000); 33 | ASSERT(data != NULL, "Failed to reallocate."); 34 | } 35 | 36 | void __CUT__Allocator_free(void) 37 | { 38 | void *data = Allocator_malloc(100); 39 | Allocator_free(data); 40 | } 41 | 42 | void __CUT__Allocator_collect(void) 43 | { 44 | Allocator_collect(); 45 | 46 | } 47 | -------------------------------------------------------------------------------- /tests/array_tests.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "array.h" 3 | #include 4 | 5 | #define ARRAY_BASE_SIZE 10 6 | 7 | void __CUT__array_Bringup(void) 8 | { 9 | GC_INIT(); 10 | } 11 | 12 | void __CUT__array_create( void ) 13 | { 14 | array_t *ary = array_create(ARRAY_BASE_SIZE); 15 | 16 | ASSERT(ary != NULL, "Must create an array." ); 17 | ASSERT(ary->size == ARRAY_BASE_SIZE, "Wrong size."); 18 | ASSERT(ary->end == 0, "End isn't at the beginning."); 19 | ASSERT(ary->data != NULL, "Data should be pointing somewhere."); 20 | } 21 | 22 | 23 | void __CUT__array_status(void) 24 | { 25 | array_t *ary = array_create(ARRAY_BASE_SIZE); 26 | 27 | ASSERT(array_max(ary) == ARRAY_BASE_SIZE, "Wrong length."); 28 | ASSERT(!array_isfull(ary), "Should not be full."); 29 | ASSERT(array_isempty(ary), "Should be empty."); 30 | ASSERT(array_length(ary) == 0, "End should be right at 0."); 31 | } 32 | 33 | 34 | void __CUT__array_operations(void) 35 | { 36 | array_t *ary = array_create(ARRAY_BASE_SIZE); 37 | int i = 0; 38 | size_t end = 0; 39 | 40 | end = array_add(ary, &i); 41 | ASSERT(end == 1, "array end should be 1."); 42 | ASSERT(!array_isempty(ary), "Array shouldn't be empty."); 43 | ASSERT(!array_isfull(ary), "Array shouldn't be full."); 44 | ASSERT(array_at(ary, 0) == &i, "Not the right value."); 45 | ASSERT(array_last(ary) == &i, "Not the right last."); 46 | 47 | void *ptr = array_pop(ary); 48 | ASSERT(array_isempty(ary), "Array should be empty."); 49 | ASSERT(ptr == &i, "Wrong value."); 50 | 51 | end = array_push(ary, &i); 52 | ASSERT(end == 1, "should be 1."); 53 | 54 | ary = array_create(1); 55 | end = array_push(ary, &i); 56 | ASSERT(array_isfull(ary), "Should be full."); 57 | ASSERT(!array_isempty(ary), "Should be empty."); 58 | ASSERT(array_at(ary, 0) == &i, "wrong value."); 59 | 60 | ptr = array_pop(ary); 61 | ASSERT(ptr == &i, "wrong value."); 62 | ASSERT(array_isempty(ary), "Should be empty."); 63 | ASSERT(array_pop(ary) == NULL, "pop should return NULL."); 64 | 65 | array_at(ary, 0) = (void *)4; 66 | ASSERT(array_at(ary, 0) == (void *)4, "Should be 4."); 67 | array_set(ary, 0, &i); 68 | ASSERT(array_at(ary, 0) == &i, "wrong value after set."); 69 | } 70 | 71 | 72 | void __CUT__array_extend(void) 73 | { 74 | array_t *ary = array_create(1); 75 | int i = 0; 76 | 77 | array_add(ary, &i); 78 | ASSERT(array_isfull(ary), "Should be full."); 79 | 80 | ary = array_extend(ary, 1); 81 | ASSERT(!array_isfull(ary), "Should not be full."); 82 | 83 | array_add(ary, &i+1); 84 | ASSERT(array_isfull(ary), "Should be full."); 85 | 86 | ASSERT(array_pop(ary) == &i+1, "first pop wrong."); 87 | ASSERT(array_pop(ary) == &i, "second pop wrong."); 88 | 89 | ASSERT(!array_isfull(ary), "should not be full."); 90 | ASSERT(array_isempty(ary), "should be empty."); 91 | 92 | array_add(ary, &i); 93 | array_add(ary, &i+1); 94 | ary = array_extend(ary, 100); 95 | 96 | ASSERT(array_max(ary) == 102, "wrong length"); 97 | ASSERT(array_length(ary) == 2, "wrong end."); 98 | ary = array_clamp(ary); 99 | 100 | ASSERT(array_max(ary) == 2, "wrong length after clamp."); 101 | } 102 | 103 | 104 | -------------------------------------------------------------------------------- /tests/dict_tests.c: -------------------------------------------------------------------------------- 1 | #include "dict.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | typedef char input_t[256]; 10 | 11 | static int tokenize(char *string, ...) 12 | { 13 | char **tokptr; 14 | va_list arglist; 15 | int tokcount = 0; 16 | 17 | va_start(arglist, string); 18 | tokptr = va_arg(arglist, char **); 19 | while (tokptr) { 20 | while (*string && isspace((unsigned char) *string)) 21 | string++; 22 | if (!*string) 23 | break; 24 | *tokptr = string; 25 | while (*string && !isspace((unsigned char) *string)) 26 | string++; 27 | tokptr = va_arg(arglist, char **); 28 | tokcount++; 29 | if (!*string) 30 | break; 31 | *string++ = 0; 32 | } 33 | va_end(arglist); 34 | 35 | return tokcount; 36 | } 37 | 38 | static int comparef(const void *key1, const void *key2) 39 | { 40 | return strcmp(key1, key2); 41 | } 42 | 43 | static char *dupstring(char *str) 44 | { 45 | int sz = strlen(str) + 1; 46 | char *new = malloc(sz); 47 | if (new) 48 | memcpy(new, str, sz); 49 | return new; 50 | } 51 | 52 | static dnode_t *new_node(void *c) 53 | { 54 | static dnode_t few[5]; 55 | static int count; 56 | 57 | if (count < 5) 58 | return few + count++; 59 | 60 | return NULL; 61 | } 62 | 63 | static void del_node(dnode_t *n, void *c) 64 | { 65 | } 66 | 67 | static int prompt = 0; 68 | 69 | static void construct(dict_t *d) 70 | { 71 | input_t in; 72 | int done = 0; 73 | dict_load_t dl; 74 | dnode_t *dn; 75 | char *tok1, *tok2, *val; 76 | const char *key; 77 | char *help = 78 | "p turn prompt on\n" 79 | "q finish construction\n" 80 | "a add new entry\n"; 81 | 82 | if (!dict_isempty(d)) 83 | puts("warning: dictionary not empty!"); 84 | 85 | dict_load_begin(&dl, d); 86 | 87 | while (!done) { 88 | if (prompt) 89 | putchar('>'); 90 | fflush(stdout); 91 | 92 | if (!fgets(in, sizeof(input_t), stdin)) 93 | break; 94 | 95 | switch (in[0]) { 96 | case '?': 97 | puts(help); 98 | break; 99 | case 'p': 100 | prompt = 1; 101 | break; 102 | case 'q': 103 | done = 1; 104 | break; 105 | case 'a': 106 | if (tokenize(in+1, &tok1, &tok2, (char **) 0) != 2) { 107 | puts("what?"); 108 | break; 109 | } 110 | key = dupstring(tok1); 111 | val = dupstring(tok2); 112 | dn = dnode_create(val); 113 | 114 | ASSERT(key && val && dn, "out of memory"); 115 | dict_load_next(&dl, dn, key); 116 | break; 117 | default: 118 | putchar('?'); 119 | putchar('\n'); 120 | break; 121 | } 122 | } 123 | 124 | dict_load_end(&dl); 125 | } 126 | 127 | void __CUT__dict_big_test(void) 128 | { 129 | input_t in; 130 | dict_t darray[10]; 131 | dict_t *d = &darray[0]; 132 | dnode_t *dn; 133 | int i; 134 | char *tok1, *tok2, *val; 135 | const char *key; 136 | 137 | fclose(stdin); 138 | stdin = fopen("tests/test_data.txt", "r"); 139 | ASSERT(stdin != NULL, "failed to open dict test data: tests/test_data.txt"); 140 | 141 | 142 | for (i = 0; i < sizeof darray / sizeof *darray; i++) 143 | dict_init(&darray[i], DICTCOUNT_T_MAX, comparef); 144 | 145 | for (;;) { 146 | if (prompt) 147 | putchar('>'); 148 | fflush(stdout); 149 | 150 | if (!fgets(in, sizeof(input_t), stdin)) 151 | break; 152 | 153 | switch(in[0]) { 154 | case 'a': 155 | if (tokenize(in+1, &tok1, &tok2, (char **) 0) != 2) { 156 | puts("what?"); 157 | break; 158 | } 159 | key = dupstring(tok1); 160 | val = dupstring(tok2); 161 | 162 | ASSERT(key && val, "out of memory"); 163 | 164 | ASSERT(dict_alloc_insert(d, key, val), "dict_alloc_insert failed"); 165 | break; 166 | case 'd': 167 | if (tokenize(in+1, &tok1, (char **) 0) != 1) { 168 | puts("what?"); 169 | break; 170 | } 171 | dn = dict_lookup(d, tok1); 172 | ASSERT(dn != NULL, "dict_lookup failed"); 173 | val = dnode_get(dn); 174 | key = dnode_getkey(dn); 175 | dict_delete_free(d, dn); 176 | 177 | free(val); 178 | free((void *) key); 179 | break; 180 | case 'f': 181 | dict_free(d); 182 | break; 183 | case 'l': 184 | case '(': 185 | case ')': 186 | if (tokenize(in+1, &tok1, (char **) 0) != 1) { 187 | puts("what?"); 188 | break; 189 | } 190 | dn = 0; 191 | switch (in[0]) { 192 | case 'l': 193 | dn = dict_lookup(d, tok1); 194 | break; 195 | case '(': 196 | dn = dict_lower_bound(d, tok1); 197 | break; 198 | case ')': 199 | dn = dict_upper_bound(d, tok1); 200 | break; 201 | } 202 | ASSERT(!dn, "lookup failed"); 203 | val = dnode_get(dn); 204 | puts(val); 205 | break; 206 | case 'm': 207 | construct(d); 208 | break; 209 | case 'k': 210 | dict_allow_dupes(d); 211 | break; 212 | case 'c': 213 | printf("%lu\n", (unsigned long) dict_count(d)); 214 | break; 215 | case 't': 216 | for (dn = dict_first(d); dn; dn = dict_next(d, dn)) { 217 | printf("%s\t%s\n", (char *) dnode_getkey(dn), 218 | (char *) dnode_get(dn)); 219 | } 220 | break; 221 | case 'q': 222 | break; 223 | case '\0': 224 | break; 225 | case 'p': 226 | prompt = 1; 227 | break; 228 | case 's': 229 | dict_set_allocator(d, new_node, del_node, NULL); 230 | break; 231 | case '#': 232 | if (tokenize(in+1, &tok1, (char **) 0) != 1) { 233 | puts("what?"); 234 | break; 235 | } else { 236 | int dictnum = atoi(tok1); 237 | if (dictnum < 0 || dictnum > 9) { 238 | puts("invalid number"); 239 | break; 240 | } 241 | d = &darray[dictnum]; 242 | } 243 | break; 244 | case 'j': 245 | if (tokenize(in+1, &tok1, &tok2, (char **) 0) != 2) { 246 | puts("what?"); 247 | break; 248 | } else { 249 | int dict1 = atoi(tok1), dict2 = atoi(tok2); 250 | if (dict1 < 0 || dict1 > 9 || dict2 < 0 || dict2 > 9) { 251 | puts("invalid number"); 252 | break; 253 | } 254 | dict_merge(&darray[dict1], &darray[dict2]); 255 | } 256 | break; 257 | default: 258 | putchar('?'); 259 | putchar('\n'); 260 | break; 261 | } 262 | } 263 | } 264 | 265 | -------------------------------------------------------------------------------- /tests/error_tests.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "error.h" 3 | #include 4 | 5 | void __CUT_error_Bringup(void) 6 | { 7 | GC_INIT(); 8 | } 9 | 10 | void __CUT__error_test(void) 11 | { 12 | CORD name = CORD_from_char_star("Zed A. Shaw"); 13 | int tested = 10; 14 | 15 | error(NULL, "%d times I called %r", tested, name); 16 | } 17 | -------------------------------------------------------------------------------- /tests/hash_tests.c: -------------------------------------------------------------------------------- 1 | #include "hash.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | typedef char input_t[256]; 10 | 11 | static int tokenize(char *string, ...) 12 | { 13 | char **tokptr; 14 | va_list arglist; 15 | int tokcount = 0; 16 | 17 | va_start(arglist, string); 18 | tokptr = va_arg(arglist, char **); 19 | while (tokptr) { 20 | while (*string && isspace((unsigned char) *string)) 21 | string++; 22 | if (!*string) 23 | break; 24 | *tokptr = string; 25 | while (*string && !isspace((unsigned char) *string)) 26 | string++; 27 | tokptr = va_arg(arglist, char **); 28 | tokcount++; 29 | if (!*string) 30 | break; 31 | *string++ = 0; 32 | } 33 | va_end(arglist); 34 | 35 | return tokcount; 36 | } 37 | 38 | static char *dupstring(char *str) 39 | { 40 | int sz = strlen(str) + 1; 41 | char *new = malloc(sz); 42 | if (new) 43 | memcpy(new, str, sz); 44 | return new; 45 | } 46 | 47 | static hnode_t *new_node(void *c) 48 | { 49 | static hnode_t few[5]; 50 | static int count; 51 | 52 | if (count < 5) 53 | return few + count++; 54 | 55 | return NULL; 56 | } 57 | 58 | static void del_node(hnode_t *n, void *c) 59 | { 60 | } 61 | 62 | void __CUT__hash_big_test(void) 63 | { 64 | input_t in; 65 | hash_t *h = hash_create(HASHCOUNT_T_MAX, 0, 0); 66 | hnode_t *hn; 67 | hscan_t hs; 68 | char *tok1, *tok2, *val; 69 | const char *key; 70 | int prompt = 0; 71 | 72 | fclose(stdin); 73 | stdin = fopen("tests/test_data.txt", "r"); 74 | ASSERT(stdin != NULL, "Couldn't open hash test data: tests/test_data.txt."); 75 | ASSERT(h != NULL, "hash_create failed"); 76 | 77 | for (;;) { 78 | if (prompt) 79 | putchar('>'); 80 | fflush(stdout); 81 | 82 | if (!fgets(in, sizeof(input_t), stdin)) 83 | break; 84 | 85 | switch(in[0]) { 86 | case 'b': 87 | printf("%d\n", hash_val_t_bit); 88 | break; 89 | case 'a': 90 | if (tokenize(in+1, &tok1, &tok2, (char **) 0) != 2) { 91 | puts("what?"); 92 | break; 93 | } 94 | key = dupstring(tok1); 95 | val = dupstring(tok2); 96 | 97 | ASSERT(key && val, "out of memory"); 98 | ASSERT(hash_alloc_insert(h, key, val), "hash_alloc_insert failed"); 99 | break; 100 | case 'd': 101 | if (tokenize(in+1, &tok1, (char **) 0) != 1) { 102 | puts("what?"); 103 | break; 104 | } 105 | hn = hash_lookup(h, tok1); 106 | ASSERT(hn != NULL, "hash_lookup failed"); 107 | 108 | val = hnode_get(hn); 109 | key = hnode_getkey(hn); 110 | hash_scan_delfree(h, hn); 111 | free((void *) key); 112 | free(val); 113 | break; 114 | case 'l': 115 | if (tokenize(in+1, &tok1, (char **) 0) != 1) { 116 | puts("what?"); 117 | break; 118 | } 119 | hn = hash_lookup(h, tok1); 120 | ASSERT(hn != NULL, "hash_lookup failed"); 121 | val = hnode_get(hn); 122 | puts(val); 123 | break; 124 | case 'n': 125 | printf("%lu\n", (unsigned long) hash_size(h)); 126 | break; 127 | case 'c': 128 | printf("%lu\n", (unsigned long) hash_count(h)); 129 | break; 130 | case 't': 131 | hash_scan_begin(&hs, h); 132 | while ((hn = hash_scan_next(&hs))) 133 | printf("%s\t%s\n", (char*) hnode_getkey(hn), 134 | (char*) hnode_get(hn)); 135 | break; 136 | case 'q': 137 | exit(0); 138 | break; 139 | case '\0': 140 | break; 141 | case 'p': 142 | prompt = 1; 143 | break; 144 | case 's': 145 | hash_set_allocator(h, new_node, del_node, NULL); 146 | break; 147 | default: 148 | putchar('?'); 149 | putchar('\n'); 150 | break; 151 | } 152 | } 153 | } 154 | 155 | -------------------------------------------------------------------------------- /tests/list_tests.c: -------------------------------------------------------------------------------- 1 | #include "list.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | typedef char input_t[256]; 10 | 11 | static int tokenize(char *string, ...) 12 | { 13 | char **tokptr; 14 | va_list arglist; 15 | int tokcount = 0; 16 | 17 | va_start(arglist, string); 18 | tokptr = va_arg(arglist, char **); 19 | while (tokptr) { 20 | while (*string && isspace((unsigned char) *string)) 21 | string++; 22 | if (!*string) 23 | break; 24 | *tokptr = string; 25 | while (*string && !isspace((unsigned char) *string)) 26 | string++; 27 | tokptr = va_arg(arglist, char **); 28 | tokcount++; 29 | if (!*string) 30 | break; 31 | *string++ = 0; 32 | } 33 | va_end(arglist); 34 | 35 | return tokcount; 36 | } 37 | 38 | static int comparef(const void *key1, const void *key2) 39 | { 40 | return strcmp(key1, key2); 41 | } 42 | 43 | static char *dupstring(char *str) 44 | { 45 | int sz = strlen(str) + 1; 46 | char *new = malloc(sz); 47 | if (new) 48 | memcpy(new, str, sz); 49 | return new; 50 | } 51 | 52 | void __CUT__list_big_test(void) 53 | { 54 | input_t in; 55 | list_t *l = list_create(LISTCOUNT_T_MAX); 56 | lnode_t *ln; 57 | char *tok1, *val; 58 | int prompt = 0; 59 | 60 | char *help = 61 | "a append value to list\n" 62 | "d delete value from list\n" 63 | "l lookup value in list\n" 64 | "s sort list\n" 65 | "c show number of entries\n" 66 | "t dump whole list\n" 67 | "p turn prompt on\n" 68 | "q quit"; 69 | 70 | if (!l) 71 | puts("list_create failed"); 72 | 73 | for (;;) { 74 | if (prompt) 75 | putchar('>'); 76 | fflush(stdout); 77 | 78 | if (!fgets(in, sizeof(input_t), stdin)) 79 | break; 80 | 81 | switch(in[0]) { 82 | case '?': 83 | puts(help); 84 | break; 85 | case 'a': 86 | if (tokenize(in+1, &tok1, (char **) 0) != 1) { 87 | puts("what?"); 88 | break; 89 | } 90 | val = dupstring(tok1); 91 | ln = lnode_create(val); 92 | 93 | if (!val || !ln) { 94 | puts("allocation failure"); 95 | if (ln) 96 | lnode_destroy(ln); 97 | free(val); 98 | break; 99 | } 100 | 101 | list_append(l, ln); 102 | break; 103 | case 'd': 104 | if (tokenize(in+1, &tok1, (char **) 0) != 1) { 105 | puts("what?"); 106 | break; 107 | } 108 | ln = list_find(l, tok1, comparef); 109 | if (!ln) { 110 | puts("list_find failed"); 111 | break; 112 | } 113 | list_delete(l, ln); 114 | val = lnode_get(ln); 115 | lnode_destroy(ln); 116 | free(val); 117 | break; 118 | case 'l': 119 | if (tokenize(in+1, &tok1, (char **) 0) != 1) { 120 | puts("what?"); 121 | break; 122 | } 123 | ln = list_find(l, tok1, comparef); 124 | if (!ln) 125 | puts("list_find failed"); 126 | else 127 | puts("found"); 128 | break; 129 | case 's': 130 | list_sort(l, comparef); 131 | break; 132 | case 'c': 133 | printf("%lu\n", (unsigned long) list_count(l)); 134 | break; 135 | case 't': 136 | for (ln = list_first(l); ln != 0; ln = list_next(l, ln)) 137 | puts(lnode_get(ln)); 138 | break; 139 | case 'q': 140 | case '\0': 141 | break; 142 | case 'p': 143 | prompt = 1; 144 | break; 145 | default: 146 | putchar('?'); 147 | putchar('\n'); 148 | break; 149 | } 150 | } 151 | } 152 | 153 | -------------------------------------------------------------------------------- /tests/module_tests.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | /* A pointless module -- tests nothing useful, will never fail. 4 | * This nonetheless shows the basic form of tests. */ 5 | 6 | void __CUT__module1( void ) 7 | { 8 | ASSERT( 1, "Any non-zero value must always pass an ASSERT." ); 9 | } 10 | 11 | /* A more complex test, consisting of a bringup, a takedown, 12 | * and the test body. */ 13 | 14 | static int module_broughtup = 0; 15 | 16 | void __CUT_BRINGUP__moduleBringup( void ) 17 | { 18 | module_broughtup = 1; 19 | } 20 | 21 | void __CUT__module2( void ) 22 | { 23 | /* ASSERT explanations are usually phrased as mandatory statements. */ 24 | ASSERT( 1 == module_broughtup, "We must be initialized by now." ); 25 | } 26 | 27 | void __CUT_TAKEDOWN__moduleBringup( void ) 28 | { 29 | /* ASSERTs in takedowns are a very bad idea; takedowns have to be able to 30 | * run at any time, including when a test has thrown an exception. */ 31 | module_broughtup = 0; 32 | } 33 | 34 | void __CUT__module3( void ) 35 | { 36 | ASSERT( 0 == module_broughtup, "We must be uninitialized by now." ); 37 | } 38 | 39 | -------------------------------------------------------------------------------- /tests/repl_tests.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void __CUT__repl_test( void ) 4 | { 5 | ASSERT( 1, "Any non-zero value must always pass an ASSERT." ); 6 | } 7 | 8 | -------------------------------------------------------------------------------- /tests/struct_tests.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "struct.h" 3 | #include 4 | #include 5 | 6 | 7 | void __CUT__StructAlign_create( void ) 8 | { 9 | StructAlign sa = StructAlign_create(); 10 | ASSERT(!StructAlign_indeterminate(sa), "Indeterminate structure alignment."); 11 | ASSERT(!StructAlign_multiple_cases(sa), "Mulitple possible cases for structure alignment."); 12 | } 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /tests/token_tests.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "token.h" 3 | #include "ops.h" 4 | #include "grammar.h" 5 | #include 6 | #include 7 | #include 8 | 9 | void __CUT__Token_createBringup(void) 10 | { 11 | GC_INIT(); 12 | } 13 | 14 | void __CUT__Token_create( void ) 15 | { 16 | CORD data = CORD_from_char_star("100"); 17 | Token *tk = Token_create(TK_INT, data, NULL); 18 | 19 | ASSERT(tk->id == TK_INT, "wrong id."); 20 | ASSERT(tk->value == 100, "wrong value."); 21 | 22 | tk = Token_create(TK_CHR, CORD_from_char_star("'A'"), NULL); 23 | ASSERT(tk->id == TK_CHR, "wrong id."); 24 | ASSERT(tk->value == 'A', "wrong value."); 25 | 26 | tk = Token_create(TK_FLOAT, CORD_from_char_star("1.1"), NULL); 27 | ASSERT(tk->id == TK_FLOAT, "wrong id."); 28 | ASSERT(tk->value <= 2.0f && tk->value >= 0.0f, "wrong value"); 29 | 30 | tk = Token_create(TK_HEX, CORD_from_char_star("0xAE"), NULL); 31 | ASSERT(tk->id == TK_HEX, "wrong id."); 32 | ASSERT(tk->value == 0xAE, "wrong value"); 33 | 34 | tk = Token_create(TK_REG, CORD_from_char_star("R0"), NULL); 35 | ASSERT(tk->id = TK_REG, "wrong id."); 36 | ASSERT(tk->value == 0, "wrong value."); 37 | } 38 | 39 | 40 | -------------------------------------------------------------------------------- /tests/tst_tests.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "tst.h" 3 | #include 4 | 5 | 6 | void __CUT__tst_Bringup(void) 7 | { 8 | GC_INIT(); 9 | } 10 | 11 | void __CUT__tst_create(void) 12 | { 13 | tst_t *t = NULL; 14 | void *result = NULL; 15 | void *result2 = NULL; 16 | int val = 10; 17 | tst_collect_t vals; 18 | 19 | t = tst_insert(t, "test", 4, &val); 20 | ASSERT(t != NULL, "Failed to create."); 21 | 22 | result = tst_search(t, "test", 4); 23 | ASSERT(result == &val, "Wrong value returned."); 24 | 25 | t = tst_insert(t, "testit", 6, &result); 26 | ASSERT(t != NULL, "Failed to add more."); 27 | result2 = tst_search(t, "testit", 6); 28 | ASSERT(result2 == &result, "Second result wasn't right."); 29 | 30 | vals = tst_collect(t, "te", 2); 31 | 32 | ASSERT(vals.length == 2, "Didn't find all of them on collect."); 33 | ASSERT(vals.values[1] == &val, "Wrong first value."); 34 | ASSERT(vals.values[0] == &result, "Wrong second value."); 35 | } 36 | 37 | 38 | -------------------------------------------------------------------------------- /tests/types_tests.c: -------------------------------------------------------------------------------- 1 | #include "types.h" 2 | #include 3 | 4 | void __CUT__OpType_to_str_test(void) 5 | { 6 | const char *name = OpType_to_str(OpType_i); 7 | ASSERT(name != NULL, "Bad OpType."); 8 | ASSERT(name == "int", "Wrong one for int."); 9 | 10 | name = Opttype_to_str(OpType_c); 11 | ASSERT(name != NULL, "Bad OpType."); 12 | ASSERT(name == "char", "Wrong one for char."); 13 | } 14 | -------------------------------------------------------------------------------- /tests/util_tests.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void __CUT__util_test( void ) 4 | { 5 | ASSERT( 1, "Any non-zero value must always pass an ASSERT." ); 6 | } 7 | 8 | --------------------------------------------------------------------------------