├── .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 |
--------------------------------------------------------------------------------