git @ Cat's Eye Technologies Kosheri / f3a6969
Initial import of Kosheri sources. Cat's Eye Technologies 12 years ago
52 changed file(s) with 6140 addition(s) and 0 deletion(s). Raw diff Collapse all Expand all
0 Kosheri
1 =======
2
3 Kosheri is a stack-based virtual machine. Here's what it offers:
4
5 * Garbage-collection, tagged tuples, dictionaries, function values,
6 coroutines, lightweight processes, textual and binary serialization,
7 and an assembler and disassembler. See "Features", below, for
8 more information.
9 * A really small code footprint. On Ubuntu 10.04 LTS, a minimal VM
10 executor compiles to an executable less than 16K in size.
11 * A clean, almost pedantic implementation. See "Implementation", below.
12 * Very few build dependencies. See "Build requirements", below.
13 * An extremely orthogonal architecture. See "Architecture", below.
14 * A reasonably efficient implementation. See "Performance", below.
15
16 Features
17 --------
18
19 * Ability to manipulate values of boolean, 32-bit integer,
20 process reference ("pid"), immutable string ("symbol"), and
21 tagged tuple type.
22 * Support for manipulating certain tagged tuple types:
23 dictionaries, function values, and activation records.
24 * Support for closures and coroutines via appropriate use of
25 activation records. (ARs retain some state after being
26 called; if this is not cleared, they can be continued.)
27 * A simple, mark-and-sweep garbage collector (for tuples and
28 symbols; everything else lives on the stack.)
29 * Concurrent operation. Each lightweight process can be a
30 VM process or a native process. Native processes are used to
31 implement interfaces to the rest of the world. Multitasking
32 is pre-emptive for VM processes, and co-operative for native
33 processes. Concurrency is implemented in the VM; system threads
34 and processes are not used. Interprocess communication is done
35 with Erlang-style messaging to processes' mailboxes.
36
37 Implementation
38 --------------
39
40 The code is generally written in BSD style(9). It compiles under
41 `-ansi -pedantic` with almost all `gcc` warnings enabled and treated as
42 errors. The core interfaces are `const`ified. `assert`s are
43 plentiful. I've tried to limit the amount of typecasting used in the
44 code, but of course there are some places where it is necessary.
45
46 Functions are often written in a straightforward, almost pedantic
47 fashion; there are of course some exceptions here too, like the
48 direct threading support.
49
50 Build requirements
51 ------------------
52
53 Only the following tools are required to build the VM and its tools:
54
55 * an ANSI C compiler (default is `gcc`) and linker (`ranlib`, `ar`)
56 * GNU Make
57
58 I have vague plans to remove even the dependency on GNU Make (the
59 code is so small that rebuilding the whole thing is not a huge burden,
60 so it could be done with a shell script or a custom build tool.)
61
62 Even `libc` is not a strict requirement for building the VM; a
63 few functions from `libc` that the code uses are implemented
64 independently in the code. The `standalone` target builds the
65 system with `-nostdlib`. However, while it builds it currently
66 does not link, as it requires some memory allocator to link to,
67 and it doesn't have one... yet.
68
69 Architecture
70 ------------
71
72 From inside the virtual machine, it's a truism that "everything's
73 a value, and every value that's not an atomic value is a tagged tuple."
74 The latter is true even for VM code and activation records and
75 dictionaries.
76
77 Moreover, this truism largely holds in the C code as well. In many
78 places in the assembler and elsewhere, we don't just use C structs
79 to hold our data, we use `struct value`s.
80
81 Every value has defined external representations, in human-readable and
82 compact binary forms, which can be generated and parsed.
83
84 The rest of this section is kind of scattered...
85
86 Some more notes on how the runtime is written in C...
87
88 Every C function in the Kosheri runtime is run in some environment.
89
90 We try to assume as little about the surrounding environment (e.g., OS)
91 as possible, and to present the world to these functions in as Kosheri-
92 like a way as possible.
93
94 Maybe not every function, but lots of functions. The ones that really
95 matter. Top-level functions especially -- (the ones that would be
96 command-line utilities in a Unix system.)
97
98 Every such function is passed a runtime environment. This runtime
99 environment consist of:
100
101 -- arguments that were passed to the function in the form of a Kosheri
102 dictionary (value_dict). The function may also have a data structure
103 associated with it (the "argdecl") that facilitates checking these
104 arguments for syntactic correctness and automatically returning an error
105 if they don't meet those checks. (this part's not done yet)
106
107 -- environment variables that were made available to the function.
108 (This is possibly not OK. Environment variables have a way of hanging
109 around that arguments don't. Some kind of adapter is called for that
110 translates (relevant) env vars to arguments first.)
111
112 -- streams available to the function, in the form of Kosheri processes.
113 The function should not assume the presence of a particular stream, or
114 at least, a function which does assume the presence of a particular
115 stream should not be assumed to always work. The function should also
116 not assume that the streams that are made available to it behave in a
117 particular way (for example, that a stream is directed to a vt100
118 terminal.) Instead it should look for a stream that abstractly presents
119 the behaviour that it wants.
120
121 Performance
122 -----------
123
124 Kosheri was designed to be reasonably efficient, for a virtual machine
125 without a JIT compiler.
126
127 The design decisions for performance are kind of all over the place,
128 sometimes being extremely optimized, sometimes sacrificing pure
129 performance for flexibility.
130
131 * "Direct threading" is possible with C compilers that support it,
132 such as `gcc`. This basically optimizes the main instruction-
133 selection `switch` into a computed `goto`.
134
135 * The compiled VM is small, really small. This means it can usually
136 fit entirely in the cache, and stay there. This can sometimes result
137 in a significant performance benefit.
138
139 On the other hand...
140
141 * The orthogonality of "everything is a tuple" extends far and wide.
142 All values are 8 bytes, including VM instructions. VM code size
143 could be reduced by packing 2 or 4 instructions into that 8 bytes,
144 but we don't do that yet.
145
146 * Input and output are modelled as processes, which means there is
147 some small overhead (to pack and unpack a message) added to I/O;
148 but I/O is I/O-bound anyway, so this is probably not something to
149 worry about.
150
151
152 Tour of the distribution
153 ------------------------
154
155 bin/
156
157 Where compiled binaries go.
158
159 eg/
160
161 Example assembly language files.
162
163 lib/
164
165 Where compiled libraries (static and dynamic) go.
166
167 src/
168
169 Source code for the virtual machine and tools.
170
171 src/build/
172
173 Source code for tools used during build-time, and not thereafter.
174
175 tests/
176
177 Tests.
178
179
180 Tour of the source
181 ------------------
182
183 assemble.c
184
185 Main program for the assembler.
186
187 chain.c
188 chain.h
189
190 Utilities to accumulate a linked list of values, then turn them
191 into a tuple when accumulation has finished.
192
193 cmdline.c
194 cmdline.h
195
196 Harness which translates command-line arguments to dictionaries,
197 and otherwise provides facilities so that programs can be written
198 in a "Kosheri view" of the outside world.
199
200 disasm.c
201
202 Main program for the disassembler.
203
204 discern.c
205 discern.h
206
207 Routines to parse human-readable representation of tuples into
208 the internal format.
209
210 file.c
211 file.h
212
213 Native implementation of processes supporting the stream-like interface
214 and which are backed with C's stdio.
215
216 freeze.c
217
218 Main program for the human-readable-value-to-compact-binary
219 serializer.
220
221 gen.c
222 gen.h
223
224 Routines for generating VM instructions (used by assembler, but
225 would also be useful for compilers targeting this VM.)
226
227 geninstr.c
228
229 A build tool which generates instrtab.c and instrenum.h from
230 vm.c.
231
232 instrtab.h
233
234 Header file for the generated instrtab.c.
235
236 lib.c
237 lib.h
238
239 Clean-room re-implementations of the few libc-supplied functions
240 which the VM uses.
241
242 load.c
243 load.h
244
245 Routines to parse (unserialize) the compact binary representation
246 of values.
247
248 portray.c
249 portray.h
250
251 Routines to format values into a human-readable representation.
252
253 process.c
254 process.h
255
256 Routines for communicating and switching between co-operative
257 lightweight concurrent processes.
258
259 render.c
260 render.h
261
262 Routines for rendering terms to file-like processes.
263
264 report.c
265 report.h
266
267 Routines for reporting errors, successes, etc. for the assembler,
268 disassembler, freezer and thawer.
269
270 run.c
271
272 Main program for the VM runner. Takes a VM program in the compact
273 binary representation and executes it.
274
275 save.c
276 save.h
277
278 Routines to generate (serialize) the compact binary representation
279 of values.
280
281 scan.c
282 scan.h
283
284 Lexical scanner, used by the assembler, and by the discern routines.
285 Fairly general, so could also be used to parse a language being
286 compiled to the VM.
287
288 stream.c
289 stream.h
290
291 Routines to communicate with processes which support the stream-like
292 interface; you can read from them, write to them, check for eof, and
293 close them.
294
295 thaw.c
296
297 Main program for the compact-binary-to-human-readable-value
298 unserializer.
299
300 value.c
301 value.h
302
303 Data structure (and associated functions) which internally
304 represents values, including atomic values (booleans, integers,
305 process id's, direct-threaded opcodes) and structured values
306 (tagged tuples and symbols.)
307
308 vm.c
309 vm.h
310
311 The virtual machine implementation itself.
312
313 vmproc.c
314 vmproc.h
315
316 The implementation of lightweight processes which uses the virtual machine
317 to execute the process.
0 TODO
1 ====
2
3 Correctness
4 -----------
5
6 * RECV instruction.
7
8 * Investigate why seemingly big-enough ARs are actually not big enough.
9
10 * Immutable values. This will help solve the problems of using
11 values as keys in a dictionary, and the problem of (not) sharing
12 data between processes.
13
14 * (BIG) Stack type checker in assembler.
15
16 * (BIG) Fix file processes to really be concurrent, esp. in read.
17 Use `select` (maybe export fd's to the scheduler.)
18
19 * (BIG) Error handling. Instead of just `assert()` (which isn't even there
20 when you define `NDEBUG`), throw an exception if the unexpected
21 happens. Or, send a message to the process which created this
22 process. (This will require `self` be accessible throughout the code
23 somehow.)
24
25 Testing
26 -------
27
28 * Falderal tests for all variants of conditionals.
29
30 * Falderal test for closures.
31
32 * Falderal test for sending and receiving messages.
33
34 * Falderal tests for dictionaries.
35
36 * Falderal test for portraying cyclic values.
37
38 * Falderal test for saving cycling values.
39
40 Accessibility
41 -------------
42
43 * Document C APIs (header files).
44
45 * enum success { SUCCESS = 1; FAILURE = 0; } and return this instead of int.
46
47 * Portray values from stream_render: %v or similar.
48
49 * Figure out way to debug messages received by file process
50 (can't write to file process, or you'll get an infinite loop.)
51
52 Portability
53 -----------
54
55 * Measure sizeof struct value et al in buildinfo. Spit out defines.
56 Think about how 64-bit systems will handle all this.
57
58 Footprint
59 ---------
60
61 * "small immediates" embedded in appropriate VM instructions.
62
63 * Go back to having "functional values", but basically say
64 that these are ARs with zero space allocated for arguments / locals.
65 Then have a VM instruction to create an AR from a functional value.
66 (Maybe also have a VM instruction to create a full AR instead of a
67 functional value, from a label, for when you want to call it one-off.)
68
69 Performance
70 -----------
71
72 * Allow symbols (/strings) to be interned. There is of course a
73 tradeoff here, so "allow" rather than "require".
74 Maybe have VALUE_TAG being "small strings" (fit in a struct value,
75 so, like, 7 characters). Then many problems go away...?
76
77 * Option to create a (non-interned) symbol from a const string in
78 a way that does not copy the const string.
79
80 * More efficient access of free variables. Split each AR into two sections: bound variables
81 and free variables. The bound variables are stored in the AR itself. Free variables are
82 stored in some other AR; pointers to them are stored in this AR. Then accessing a bound
83 variable is only a single indirection. Tradeoff is that more work needs to be done when
84 creating a functional value.
85
86 * In ARs, store top-of-stack pointer as a machine pointer,
87 not a tuple index.
0 #!/bin/sh
1
2 cd src
3 echo "Building 'debug' version..."
4 make clean debug >ERRORS 2>&1
5 if [ $? != 0 ]; then
6 cat ERRORS
7 rm -f ERRORS
8 exit 1
9 fi
10 rm -f ERRORS
11 ./assemble --asmfile ../$1 --vmfile out.kvm
12 gdb --args ./run --vmfile out.kvm
13 rm -f out.kvm
0 NEW_AR #10
1 GOTO :past_q
2 :q
3 ; no need to reserve space for parameters
4 GETI #0 ; local #0 = 1st parameter = a
5 STDOUT
6 PORTRAY
7
8 GETI #1 ; local #1 = 2nd parameter = b
9 STDOUT
10 PORTRAY
11
12 GETI #0
13 GETI #1
14 ADD_INT
15
16 YIELD #1 ; pass the result back to our caller
17 RET ; end of this function
18
19 :past_q
20
21 PUSH #1
22
23 PUSH #2
24 PUSH #3
25 PUSH #10 ; this function will need 4 slots: 2 args, 2 stack
26 FUN :q ; push a closure for q onto the stack
27 CALL #2 ; call it with two args
28
29 PUSH #10 ; this function will need 4 slots: 2 args, 2 stack
30 FUN :q ; push a closure for q onto the stack
31 CALL #2 ; call it with two args
32
33 STDOUT
34 PORTRAY
35
36 HALT
0 NEW_AR #3
1 PUSH #11 ; statically initialize our local
2 :label
3 GETI #0
4 STDOUT
5 PORTRAY
6 GETI #0
7 PUSH #1
8 SUB_INT
9 SETI #0
10 GETI #0
11 PUSH #0
12 JNE :label
13 HALT
0 NEW_AR #7
1 SPAWN :label
2 REST
3 PUSH #main
4 STDOUT
5 PORTRAY
6 HALT
7 :label
8 NEW_AR #7
9 PUSH #worker
10 STDOUT
11 PORTRAY
12 HALT
0 # Makefile for Kosheri.
1 # $Id: Makefile 146 2008-12-06 19:59:54Z catseye $
2
3 CC?=gcc
4 AR?=ar
5 RANLIB?=ranlib
6
7 O?=.o
8 EXE?=
9
10 OD?=./
11
12 DEBUG_PORTRAY_O?=
13
14 WARNS= -Werror -W -Wall -Wstrict-prototypes -Wmissing-prototypes \
15 -Wpointer-arith -Wno-uninitialized -Wreturn-type -Wcast-qual \
16 -Wwrite-strings -Wswitch -Wshadow -Wcast-align -Wchar-subscripts \
17 -Winline -Wnested-externs -Wredundant-decls
18 LIBS=-L. -lruntime
19 CFLAGS+=-ansi -pedantic ${WARNS} ${EXTRA_CFLAGS}
20
21 RUNTIME_OBJS= ${OD}lib${O} ${OD}value${O} \
22 ${OD}process${O} ${OD}file${O} ${OD}stream${O} \
23 ${DEBUG_PORTRAY_O} \
24 ${OD}render${O}
25 # ${OD}portray${O}
26
27 # portray and save are only needed for non-raw output
28 # instrtab is only needed for direct threading conversion
29 RUN_OBJS= ${OD}run${O} \
30 ${OD}load${O} \
31 ${OD}vm${O} ${OD}vmproc${O} \
32 ${OD}instrtab${O} \
33 ${OD}portray${O} \
34 ${OD}save${O} \
35 ${OD}cmdline${O}
36
37 ASSEMBLE_OBJS= ${OD}assemble${O} \
38 ${OD}instrtab${O} \
39 ${OD}report${O} ${OD}scan${O} ${OD}discern${O} ${OD}chain${O} \
40 ${OD}gen${O} ${OD}save${O} \
41 ${OD}portray${O} \
42 ${OD}cmdline${O}
43
44 DISASM_OBJS= ${OD}disasm${O} \
45 ${OD}instrtab${O} \
46 ${OD}report${O} \
47 ${OD}load${O} \
48 ${OD}portray${O} \
49 ${OD}chain${O} \
50 ${OD}cmdline${O}
51
52 FREEZE_OBJS= ${OD}freeze${O} \
53 ${OD}save${O} \
54 ${OD}report${O} ${OD}scan${O} \
55 ${OD}discern${O} ${OD}chain${O} \
56 ${OD}cmdline${O}
57
58 THAW_OBJS= ${OD}thaw${O} \
59 ${OD}load${O} \
60 ${OD}report${O} \
61 ${OD}portray${O} \
62 ${OD}cmdline${O}
63
64 BUILDINFO_OBJS= ${OD}buildinfo${O} \
65 ${OD}process${O} \
66 ${OD}cmdline${O}
67
68 PROGS=run${EXE} assemble${EXE} disasm${EXE} freeze${EXE} thaw${EXE} \
69 buildinfo${EXE}
70
71 all: ${PROGS}
72
73 geninstr${EXE}: geninstr.o
74 ${CC} geninstr.o -o geninstr${EXE}
75
76 instrtab.c instrenum.h instrlab.h localtypes.h: vm.c geninstr
77 ./geninstr vm.c
78
79 value.h: localtypes.h
80
81 value.c: value.h
82
83 vm.h: instrenum.h instrlab.h value.h
84
85 libruntime.a: ${RUNTIME_OBJS}
86 ${AR} rc libruntime.a ${RUNTIME_OBJS}
87 ${RANLIB} libruntime.a
88
89 run.c: vm.h
90
91 run${EXE}: ${RUN_OBJS} libruntime.a
92 ${CC} ${RUN_OBJS} ${LIBS} -o run${EXE}
93 assemble${EXE}: ${ASSEMBLE_OBJS} libruntime.a
94 ${CC} ${ASSEMBLE_OBJS} ${LIBS} -o assemble${EXE}
95 disasm${EXE}: ${DISASM_OBJS} libruntime.a
96 ${CC} ${DISASM_OBJS} ${LIBS} -o disasm${EXE}
97 freeze${EXE}: ${FREEZE_OBJS} libruntime.a
98 ${CC} ${FREEZE_OBJS} ${LIBS} -o freeze${EXE}
99 thaw${EXE}: ${THAW_OBJS} libruntime.a
100 ${CC} ${THAW_OBJS} ${LIBS} -o thaw${EXE}
101
102 buildinfo${EXE}: ${BUILDINFO_OBJS} libruntime.a
103 ${CC} ${BUILDINFO_OBJS} ${LIBS} -o buildinfo${EXE}
104
105
106 # when DEBUG is defined, save.o, load.o, and parse.o depend on portray.o
107 debug: clean
108 ${MAKE} DEBUG_PORTRAY_O="${OD}portray${O}" \
109 EXTRA_CFLAGS="-g -DDEBUG"
110
111 profiled: clean
112 ${MAKE} EXTRA_CFLAGS="-pg"
113
114 tool: clean
115 ${MAKE} EXTRA_CFLAGS="-DNDEBUG -Os" LIBS="-L. -lruntime -s"
116
117 # -finline-functions ?
118
119 static: clean
120 ${MAKE} EXTRA_CFLAGS="-DNDEBUG -Os" LIBS="-L. -lruntime -s -static"
121
122 standalone: clean
123 ${MAKE} EXTRA_CFLAGS="-DNDEBUG -DSTANDALONE -DCTYPE_IS_BUILTIN -Os -static -nostdlib" LIBS="-L. -lruntime -nostdlib -s"
124
125 win: clean
126 ${MAKE} EXE=.exe EXTRA_CFLAGS="-DNDEBUG -mno-cygwin" all
127
128 wintool: clean
129 ${MAKE} EXE=.exe EXTRA_CFLAGS="-DNDEBUG -Os -static -mno-cygwin" LIBS="-L. -lruntime -s"
130
131 clean:
132 rm -rf ${OD}*${O} *.so *.a *.core *.vm *.exe instrtab.c instrenum.h instrlab.h geninstr *.stackdump ${PROGS} foo.* LISTING OUTPUT
0 /*
1 * assemble.c
2 * Assembler for virtual machine.
3 * $Id: assemble.c 146 2008-12-06 19:59:54Z catseye $
4 */
5
6 #include "lib.h"
7 #include "cmdline.h"
8 #include "value.h"
9
10 #include "stream.h"
11 #include "file.h"
12
13 #include "scan.h"
14 #include "discern.h"
15
16 #include "report.h"
17
18 #include "gen.h"
19 #include "instrtab.h"
20 #include "save.h"
21
22 static struct value labels;
23
24 /* Routines */
25
26 static struct value *
27 fetch_label(const char *token, unsigned int length)
28 {
29 struct value lab_text;
30
31 value_symbol_new(&lab_text, token, length);
32 return value_dict_fetch(&labels, &lab_text);
33 }
34
35 static void
36 store_label(const char *token, unsigned int length, struct value *label)
37 {
38 struct value lab_text;
39
40 value_symbol_new(&lab_text, token, length);
41 value_dict_store(&labels, &lab_text, label);
42 }
43
44 static void
45 assemble(struct scanner *sc, struct value *gen)
46 {
47 struct opcode_entry *oe;
48 struct value label;
49 int count;
50
51 while (!scanner_eof(sc)) {
52 if (scanner_tokeq(sc, ";")) { /* A comment - ignore rest of line. */
53 scanner_scanline(sc);
54 continue;
55 }
56 if (scanner_tokeq(sc, ":")) { /* A label - associate string with addr. */
57 const char *str;
58 int len;
59
60 scanner_scan(sc);
61 str = scanner_token_string(sc);
62 len = scanner_token_length(sc);
63 value_copy(&label, fetch_label(str, len));
64 /* This may cause backpatching */
65 if (gen_define_label(gen, &label)) {
66 store_label(str, len, &label);
67 } else {
68 scanner_report(sc, REPORT_ERROR, "Label already defined");
69 }
70 scanner_scan(sc);
71 continue;
72 }
73
74 for (oe = opcode_table; oe->token != NULL; oe++) {
75 if (scanner_tokeq(sc, oe->token))
76 break;
77 }
78 if (oe->token == NULL) {
79 scanner_report(sc, REPORT_ERROR, "Unrecognized token");
80 scanner_scan(sc);
81 continue;
82 }
83
84 gen_integer(gen, oe->opcode);
85 scanner_scan(sc);
86
87 for (count = 0; count < oe->arity; count++) {
88 if (scanner_tokeq(sc, ":")) {
89 const char *str;
90 int len;
91
92 scanner_scan(sc);
93 str = scanner_token_string(sc);
94 len = scanner_token_length(sc);
95 value_copy(&label, fetch_label(str, len));
96 gen_gen_label_ref(gen, &label);
97 store_label(str, len, &label);
98 scanner_scan(sc);
99 continue;
100 } else if (scanner_tokeq(sc, "#")) {
101 struct value v;
102
103 scanner_scan(sc);
104 value_discern(&v, sc);
105 gen_value(gen, &v);
106 continue;
107 } else {
108 scanner_report(sc, REPORT_WARNING,
109 "Unrecognized argument");
110 scanner_scan(sc);
111 }
112 }
113 }
114 }
115
116 static void
117 assemble_main(struct value *args, struct value *result)
118 {
119 struct process *out;
120 struct scanner *sc;
121 struct reporter *r;
122 struct value gen, flat; /* the generator that we will use to build vm code */
123 struct value *asmfile, *vmfile;
124 struct value asmfile_sym, vmfile_sym;
125
126 r = reporter_new("Assembly", NULL, 1);
127
128 value_symbol_new(&asmfile_sym, "asmfile", 7);
129 value_symbol_new(&vmfile_sym, "vmfile", 6);
130
131 assert(value_is_tuple(args));
132 asmfile = value_dict_fetch(args, &asmfile_sym);
133 vmfile = value_dict_fetch(args, &vmfile_sym);
134
135 /*
136 * Generate.
137 */
138 sc = scanner_new(r);
139
140 if (!scanner_open(sc, value_symbol_get_token(asmfile))) {
141 value_integer_set(result, 1);
142 return;
143 }
144
145 gen_new_default(&gen);
146 value_dict_new(&labels, 8);
147 assemble(sc, &gen);
148 gen_integer(&gen, INSTR_EOF);
149 scanner_close(sc);
150 scanner_free(sc);
151
152 /*
153 * Write out.
154 */
155 out = file_open(value_symbol_get_token(vmfile), "w");
156 gen_flatten(&gen, &flat);
157 value_save(out, &flat);
158 stream_close(NULL, out);
159
160 value_integer_set(result, reporter_has_errors(r) ? 1 : 0);
161 reporter_free(r);
162 }
163
164 MAIN(assemble_main)
0 /*
1 * buildinfo.c
2 * Show some info about the built binary (also test for process-based I/O)
3 */
4
5 #include "lib.h"
6
7 #include "render.h"
8 #include "cmdline.h"
9
10 #include "process.h"
11 #include "file.h"
12
13 /* Main Program / Driver */
14
15 static void
16 buildinfo_main(struct value *args, struct value *result)
17 {
18 struct process *out;
19
20 out = file_open("*stdout", "w");
21 args = args;
22 process_render(out,
23 "sizeof(struct value) == %d\n", sizeof(struct value));
24 value_integer_set(result, 0);
25 }
26
27 MAIN(buildinfo_main)
0 /*
1 * chain.c
2 * Routines for collecting chains.
3 * $Id: chain.c 143 2008-07-18 06:42:22Z catseye $
4 */
5
6 #include "lib.h"
7 #include "chain.h"
8 #include "value.h"
9
10 struct chain {
11 struct chain *next;
12 struct value value;
13 };
14
15 struct chain *
16 add_to_chain(struct chain *c, struct value *v)
17 {
18 struct chain *n;
19
20 n = malloc(sizeof(struct chain));
21 n->next = NULL;
22 value_copy(&n->value, v);
23 if (c != NULL) {
24 c->next = n;
25 }
26 return n;
27 }
28
29 void
30 populate_tuple_from_chain(struct value *v, struct chain *c)
31 {
32 unsigned int i = 0;
33
34 while (c != NULL) {
35 value_tuple_store(v, i, &c->value);
36 i++;
37 c = c->next;
38 }
39 }
40
41 struct value *
42 search_chain(struct chain *c, struct value *f)
43 {
44 while (c != NULL) {
45 if (value_equal(f, &c->value))
46 return &c->value;
47 c = c->next;
48 }
49 return NULL;
50 }
51
52 void
53 free_chain(struct chain *c)
54 {
55 struct chain *next;
56
57 while (c != NULL) {
58 next = c->next;
59 free(c);
60 c = next;
61 }
62 }
0 /*
1 * chain.h
2 * Prototypes for chains.
3 */
4
5 #ifndef __CHAIN_H_
6 #define __CHAIN_H_
7
8 #include "value.h"
9
10 struct chain;
11
12 struct chain *add_to_chain(struct chain *, struct value *);
13 void populate_tuple_from_chain(struct value *, struct chain *);
14 struct value *search_chain(struct chain *, struct value *);
15 void free_chain(struct chain *);
16
17 #endif /* !__CHAIN_H_ */
0 /*
1 * cmdline.c
2 * Implementation of common command-line parsing functionality.
3 * $Id$
4 */
5
6 #include "lib.h"
7 #include "render.h"
8 #include "process.h"
9 #include "file.h"
10
11 #include "cmdline.h"
12
13 struct process *process_std = NULL;
14 struct process *process_err = NULL;
15
16 /*
17 static const char *progname = NULL;
18 static const struct argdecl *argdecl = NULL;
19 */
20
21 /* eventually this should parse an optional cmdline schema to validate the cmd line */
22 /* const struct argdecl *ad, */
23
24 int
25 cmdline_parse(struct value *dict, int argc, char **argv)
26 {
27 unsigned int arg_count, i;
28 struct value name, value;
29
30 assert(argc > 0);
31 arg_count = (unsigned int)argc;
32
33 value_dict_new(dict, 16); /* xxx */
34
35 for (i = 1; i < arg_count; i++) {
36 if (strlen(argv[i]) > 2 && argv[i][0] == '-' && argv[i][1] == '-') {
37 const char *arg_name = argv[i] + (2 * sizeof(char));
38 value_symbol_new(&name, arg_name, strlen(arg_name));
39 i++;
40 if (i < arg_count) {
41 value_symbol_new(&value, argv[i], strlen(argv[i]));
42 value_dict_store(dict, &name, &value);
43 } else {
44 /* complain */
45 }
46 } else {
47 /* complain */
48 }
49 }
50
51 return 1;
52 }
53
54 /* eventually this should parse an optional cmdline schema to validate the cmd line */
55
56 void
57 cmdline_usage(void)
58 {
59 exit(1);
60 }
61
62 int
63 cmdline_driver(void (*main)(struct value *, struct value *),
64 int argc, char **argv)
65 {
66 struct value cmdline, result;
67
68 cmdline_parse(&cmdline, argc, argv);
69 assert(value_is_tuple(&cmdline));
70
71 main(&cmdline, &result);
72
73 return value_get_integer(&result);
74 }
0 /*
1 * cmdline.h
2 * Prototypes for common command-line parsing functionality.
3 * $Id$
4 */
5
6 /*
7 * In the future, this should be progenv.h, which determines a
8 * program environment. This should include an "entry point"
9 * (not necessarily "main", since it should be able to operate
10 * in standalone and other contexts where a "command line" is
11 * not meaningful.)
12 */
13
14 #ifndef __CMDLINE_H_
15 #define __CMDLINE_H_
16
17 #include "value.h"
18
19 #include "file.h" /* ... because. */
20
21 struct process;
22
23 struct runenv {
24 struct handler *handler_std;
25 /*
26 struct stream *stream_std;
27 struct stream *stream_err;
28 */
29 const char *progname;
30 };
31
32 extern struct process *process_err;
33
34 int cmdline_driver(void (*)(struct value *, struct value *),
35 int, char **);
36 int cmdline_parse(struct value *, int, char **);
37 void cmdline_usage(void);
38
39 /*
40 * STANDALONE no longer supported / not yet re-supported
41 */
42 #ifdef STANDALONE
43 #define MAIN(function) \
44 int main(int argc, char **argv) \
45 { \
46 process_err = NULL; \
47 return cmdline_driver(function, argc, argv); \
48 }
49 #else
50 #define MAIN(function) \
51 int main(int argc, char **argv) \
52 { \
53 process_err = file_open("*stderr", "w"); \
54 return cmdline_driver(function, argc, argv); \
55 }
56 #endif /* STANDALONE */
57
58 #endif /* !__CMDLINE_H_ */
0 /*
1 * disasm.c
2 * Disassembler for virtual machine.
3 */
4
5 #include "lib.h"
6 #include "cmdline.h"
7
8 #include "value.h"
9 #include "chain.h"
10
11 #include "file.h"
12 #include "stream.h"
13 #include "render.h"
14
15 #include "report.h"
16 #include "instrtab.h"
17 #include "load.h"
18 #include "portray.h"
19
20 /* Routines */
21
22 static void
23 disassemble(struct reporter *r, struct process *p, struct value *code)
24 {
25 struct opcode_entry *oe;
26 int count, pc, opcode;
27 struct value *val;
28 struct value t;
29 struct chain *back, *front;
30
31 value_integer_set(&t, 0);
32
33 /* first pass: find label destination */
34
35 pc = 0;
36 back = front = add_to_chain(NULL, &t);
37 for (;;) {
38 val = value_tuple_fetch(code, pc);
39 if (value_is_integer(val)) {
40 opcode = value_get_integer(val);
41 if (opcode < 0 || opcode > INSTR_EOF) {
42 report(r, REPORT_ERROR, "Opcode not in range 0..%d", INSTR_EOF);
43 pc++;
44 } else if (opcode == INSTR_EOF) {
45 break;
46 } else {
47 oe = &opcode_table[opcode];
48 count = oe->arity;
49 pc++;
50 val = value_tuple_fetch(code, pc);
51 while (count > 0) {
52 if (oe->optype == OPTYPE_ADDR)
53 back = add_to_chain(back, val);
54 pc++;
55 val = value_tuple_fetch(code, pc);
56 count--;
57 }
58 }
59 } else {
60 report(r, REPORT_ERROR, "Opcode is not an integer");
61 pc++;
62 }
63 }
64
65 if (reporter_has_errors(r))
66 return;
67
68 /* second pass */
69 pc = 0;
70 for (;;) {
71 val = value_tuple_fetch(code, pc);
72 opcode = value_get_integer(val);
73 if (opcode == INSTR_EOF)
74 break;
75
76 value_integer_set(&t, pc);
77 if (search_chain(front, &t) != NULL) {
78 process_render(p, ":L%d\n", pc);
79 }
80
81 oe = &opcode_table[opcode];
82 process_render(p, "%s ", oe->token);
83 count = oe->arity;
84
85 pc++;
86 val = value_tuple_fetch(code, pc);
87
88 if (count == -1) {
89 report(r, REPORT_WARNING, "Unrecognized opcode");
90 process_render(p, "??? ");
91 } else {
92 while (count > 0) {
93 if (oe->optype == OPTYPE_ADDR) {
94 process_render(p, ":L%d ",
95 value_get_integer(val)
96 );
97 } else {
98 process_render(p, "#");
99 value_portray(p, val);
100 }
101 pc++;
102 val = value_tuple_fetch(code, pc);
103 count--;
104 }
105 }
106
107 process_render(p, "\n");
108 }
109
110 free_chain(front);
111 }
112
113 static void
114 disassemble_main(struct value *args, struct value *result)
115 {
116 struct reporter *r;
117 struct process *p;
118 struct value code; /* virtual machine code we will dump */
119 struct value *asmfile, *vmfile;
120 struct value asmfile_sym, vmfile_sym;
121
122 value_symbol_new(&asmfile_sym, "asmfile", 7);
123 value_symbol_new(&vmfile_sym, "vmfile", 6);
124
125 asmfile = value_dict_fetch(args, &asmfile_sym);
126 vmfile = value_dict_fetch(args, &vmfile_sym);
127
128 r = reporter_new("Disassembly", NULL, 1);
129
130 /*
131 * Load.
132 */
133 p = file_open(value_symbol_get_token(vmfile), "r");
134 value_load(&code, p);
135 stream_close(NULL, p);
136
137 p = file_open(value_symbol_get_token(asmfile), "w");
138 disassemble(r, p, &code);
139 stream_close(NULL, p);
140
141 value_integer_set(result, reporter_has_errors(r) ? 1 : 0);
142 reporter_free(r);
143 }
144
145 MAIN(disassemble_main)
0 /*
1 * discern.c
2 * Routines for parsing values (only; not program source) from a file-like process.
3 * $Id: discern.c 143 2008-07-18 06:42:22Z catseye $
4 */
5
6 #include "lib.h"
7
8 #include "scan.h"
9 #include "value.h"
10 #include "discern.h"
11 #include "chain.h"
12
13 /*
14 * A parser for values, inspired somewhat by Scheme/LISP S-expressions
15 * and Prolog/Erlang terms.
16 */
17 int
18 value_discern(struct value *top, struct scanner *sc)
19 {
20 if (scanner_tokeq(sc, "<")) {
21 struct chain *front, *back;
22 struct value tag, inner;
23 unsigned int size = 1;
24
25 /* Tuple. */
26 scanner_scan(sc);
27 value_discern(&tag, sc);
28 scanner_expect(sc, ":");
29 value_discern(&inner, sc);
30 back = front = add_to_chain(NULL, &inner);
31 while (scanner_tokeq(sc, ",")) {
32 scanner_scan(sc);
33 value_discern(&inner, sc);
34 back = add_to_chain(back, &inner);
35 size++;
36 }
37 scanner_expect(sc, ">");
38 value_tuple_new(top, &tag, size);
39 populate_tuple_from_chain(top, front);
40 free_chain(front);
41 return 1;
42 } else if (scanner_tokeq(sc, "[")) {
43 struct value inner, *left, right;
44
45 /* List: Sequence of tail-nested Pairs. */
46 scanner_scan(sc);
47 if (scanner_tokeq(sc, "]")) {
48 scanner_scan(sc);
49 value_copy(top, &VNULL);
50 return 1;
51 }
52 value_tuple_new(top, &tag_list, 2);
53 value_discern(&inner, sc);
54 value_tuple_store(top, 0, &inner);
55 /*value_tuple_store(top, 1, VNULL);*/
56 left = top;
57 while (scanner_tokeq(sc, ",")) {
58 scanner_scan(sc);
59 value_tuple_new(&right, &tag_list, 2);
60 value_discern(&inner, sc);
61 value_tuple_store(&right, 0, &inner);
62 /*value_set_index(right, 1, VNULL);*/
63 value_tuple_store(left, 1, &right);
64 left = value_tuple_fetch(left, 1);
65 }
66 if (scanner_tokeq(sc, "|")) {
67 scanner_scan(sc);
68 value_discern(&right, sc);
69 value_tuple_store(left, 1, &right);
70 }
71 scanner_expect(sc, "]");
72 return 1;
73 } else if (scanner_tokeq(sc, "{")) {
74 struct value left, right;
75
76 /* Dictionary: associations between keys and values. */
77 scanner_scan(sc);
78 value_dict_new(top, 31);
79
80 value_discern(&left, sc);
81 scanner_expect(sc, "=");
82 value_discern(&right, sc);
83 value_dict_store(top, &left, &right);
84 while (scanner_tokeq(sc, ",")) {
85 scanner_scan(sc);
86 value_discern(&left, sc);
87 scanner_expect(sc, "=");
88 value_discern(&right, sc);
89 value_dict_store(top, &left, &right);
90 }
91 scanner_expect(sc, "}");
92 return 1;
93 } else if (k_isdigit(scanner_token_string(sc)[0])) {
94 /* Integer. */
95 value_integer_set(top, k_atoi(scanner_token_string(sc),
96 scanner_token_length(sc)));
97 scanner_scan(sc);
98 return 1;
99 } else {
100 /* Symbol. */
101 value_symbol_new(top, scanner_token_string(sc),
102 scanner_token_length(sc));
103 scanner_scan(sc);
104 return 1;
105 }
106 }
0 /*
1 * discern.h
2 * Prototypes and structures for parsing values (only; not full
3 * program source code) from textual streams.
4 */
5
6 #ifndef __DISCERN_H_
7 #define __DISCERN_H_
8
9 struct value;
10 struct scanner;
11
12 int value_discern(struct value *, struct scanner *);
13
14 #endif /* !__DISCERN_H_ */
0 /*
1 * file.c
2 * Native processes for communicating with (reading, writing) files,
3 * exposing a stream-like interface.
4 */
5
6 #include <stdio.h>
7
8 #include "lib.h"
9 #include "process.h"
10 #include "file.h"
11
12 #ifdef DEBUG
13 #include "stream.h"
14 #include "portray.h"
15 #include "cmdline.h"
16 #endif
17
18 static void run(struct process *p)
19 {
20 struct value msg;
21 struct value response;
22 struct process *sender;
23 size_t result;
24 char *buffer;
25 size_t size;
26
27 assert(p != NULL);
28 assert(p->aux != NULL);
29
30 while (process_dequeue(p, &msg)) {
31 #ifdef DEBUG
32 /*
33 stream_write(NULL, process_err, "-->", 3);
34 value_portray(process_err, &msg);
35 stream_write(NULL, process_err, "<--", 3);
36 */
37 #endif
38 if (value_is_tuple(&msg)) {
39 const char *tag = value_symbol_get_token(value_tuple_get_tag(&msg));
40 if (strcmp(tag, "write") == 0) {
41 struct value *payload = value_tuple_fetch(&msg, 0);
42
43 result = fwrite(
44 value_symbol_get_token(payload),
45 value_symbol_get_length(payload),
46 1, (FILE *)p->aux
47 );
48 if (result) {
49 /* some kind of error occurred */
50 }
51 } else if (strcmp(tag, "read") == 0) {
52 sender = value_get_process(value_tuple_fetch(&msg, 0));
53 size = value_get_integer(value_tuple_fetch(&msg, 1));
54
55 buffer = value_symbol_new_buffer(&response, size);
56 result = fread(buffer, size, 1, (FILE *)p->aux);
57 if (result) {
58 /* some kind of error occurred, or we just need more */
59 }
60 process_enqueue(sender, &response);
61 } else if (strcmp(tag, "eof") == 0) {
62 sender = value_get_process(value_tuple_fetch(&msg, 0));
63
64 value_boolean_set(&response, feof((FILE *)p->aux));
65 process_enqueue(sender, &response);
66 } else if (strcmp(tag, "close") == 0) {
67 fclose((FILE *)p->aux);
68 p->aux = NULL;
69 }
70 }
71 }
72
73 p->waiting = 1;
74 }
75
76 static struct process *
77 file_fopen(FILE *file)
78 {
79 struct process *p;
80
81 p = process_new();
82 p->run = run;
83 p->aux = file;
84
85 return p;
86 }
87
88 struct process *
89 file_open(const char *locator, const char *mode)
90 {
91 FILE *f;
92
93 if (strcmp(locator, "*stdin") == 0) {
94 return file_fopen(stdin);
95 }
96 if (strcmp(locator, "*stdout") == 0) {
97 return file_fopen(stdout);
98 }
99 if (strcmp(locator, "*stderr") == 0) {
100 return file_fopen(stderr);
101 }
102
103 if ((f = fopen(locator, mode)) == NULL) {
104 /* XXX only if mode contains a 'strict' char flag... */
105 /* XXX this should use report.c somehow someday */
106 fprintf(stderr,
107 "Could not open '%s' in mode '%s'", locator, mode);
108 exit(1);
109 }
110
111 return file_fopen(f);
112 }
0 /*
1 * file.h
2 * Native processes for communicating with (reading, writing) files,
3 * exposing a stream-like interface.
4 */
5
6 #ifndef __FILE_H_
7 #define __FILE_H_
8
9 struct process;
10
11 struct process *file_open(const char *, const char *);
12
13 #endif /* !__FILE_H_ */
0 /*
1 * freeze.c
2 * Parse a constant term from a text file and
3 * write it out to a binary termfile.
4 * $Id: freeze.c 146 2008-12-06 19:59:54Z catseye $
5 */
6
7 #include "lib.h"
8 #include "cmdline.h"
9
10 #include "stream.h"
11 #include "file.h"
12
13 #include "report.h"
14 #include "value.h"
15
16 #include "discern.h"
17 #include "scan.h"
18 #include "save.h"
19
20 /* Main Program / Driver */
21
22 static void
23 freeze_main(struct value *args, struct value *result)
24 {
25 struct process *p;
26 struct scanner *sc;
27 struct reporter *r;
28 struct value term;
29
30 struct value *termfile, *binfile;
31 struct value termfile_sym, binfile_sym;
32
33 value_symbol_new(&termfile_sym, "termfile", 8);
34 value_symbol_new(&binfile_sym, "binfile", 7);
35 termfile = value_dict_fetch(args, &termfile_sym);
36 binfile = value_dict_fetch(args, &binfile_sym);
37
38 r = reporter_new("Freezing", NULL, 1);
39
40 /*
41 * Parse.
42 */
43 sc = scanner_new(r);
44 if (!scanner_open(sc, value_symbol_get_token(termfile))) {
45 value_integer_set(result, 1);
46 return;
47 }
48 if (!value_discern(&term, sc)) {
49 report(r, REPORT_ERROR, "Could not parse input term");
50 }
51
52 /*
53 * Write out.
54 */
55 p = file_open(value_symbol_get_token(binfile), "w");
56 if (!value_save(p, &term)) {
57 report(r, REPORT_ERROR,
58 "Could not write term to '%s'", binfile);
59 }
60 stream_close(NULL, p);
61
62 /*
63 * Finish up.
64 */
65 scanner_close(sc);
66 scanner_free(sc);
67
68 value_integer_set(result, reporter_has_errors(r) ? 1 : 0);
69 reporter_free(r);
70 }
71
72 MAIN(freeze_main)
0 /*
1 * gen.c
2 * Accumulate values in tuples with extents.
3 */
4
5 #include "lib.h"
6
7 #include "gen.h"
8
9 #ifdef DEBUG
10 #include "cmdline.h"
11 #include "portray.h"
12 #include "render.h"
13 #endif
14
15 /*
16 * This was originally intended to generate VM instructions, however
17 * it has more general applicability.
18 */
19
20 #define GEN_ROOT_TUPLE 0 /* The top tuple of the hierarchy */
21 #define GEN_CURRENT_TUPLE 1 /* The tuple currently being gen'ed into */
22 #define GEN_GLOBAL_POS 2 /* The global position to be gen'ed into */
23 #define GEN_LOCAL_POS 3 /* Gen offset into current tuple */
24
25 #define GEN_SIZE 4
26
27 int
28 gen_new(struct value *gen, struct value *tuple, unsigned int pos)
29 {
30 struct value gen_tag;
31
32 value_symbol_new(&gen_tag, "gen", 3);
33 if (!value_tuple_new(gen, &gen_tag, GEN_SIZE))
34 return 0;
35 value_tuple_store(gen, GEN_ROOT_TUPLE, tuple);
36 value_tuple_store(gen, GEN_CURRENT_TUPLE, tuple);
37 value_tuple_store_integer(gen, GEN_GLOBAL_POS, pos);
38 value_tuple_store_integer(gen, GEN_LOCAL_POS, pos);
39 return 1;
40 }
41
42 int
43 gen_new_default(struct value *gen)
44 {
45 struct value tuple, code_sym;
46
47 value_symbol_new(&code_sym, "code", 4);
48 value_tuple_new(&tuple, &code_sym, 8192);
49 return gen_new(gen, &tuple, 0);
50 }
51
52 /*
53 * Accumulate a value to a tuple.
54 */
55 void
56 gen_value(struct value *gen, struct value *value)
57 {
58 unsigned int pos = value_tuple_fetch_integer(gen, GEN_LOCAL_POS);
59 struct value *tuple = value_tuple_fetch(gen, GEN_CURRENT_TUPLE);
60 unsigned int size = value_tuple_get_size(tuple);
61 struct value new_tuple;
62
63 if (pos == (size - 1)) {
64 /* Last slot. Allocate a new tuple of twice the running size and plug it in. */
65 struct value *tag = value_tuple_get_tag(tuple);
66
67 value_tuple_new(&new_tuple, tag, size * 2);
68 value_tuple_store(tuple, pos, &new_tuple);
69 value_tuple_store(gen, GEN_CURRENT_TUPLE, &new_tuple);
70 pos = 0;
71 tuple = &new_tuple;
72 }
73 value_tuple_store(tuple, pos, value);
74 pos++;
75 value_tuple_store_integer(gen, GEN_LOCAL_POS, pos);
76
77 pos = value_tuple_fetch_integer(gen, GEN_GLOBAL_POS);
78 pos++;
79 value_tuple_store_integer(gen, GEN_GLOBAL_POS, pos);
80 }
81
82 /*
83 * Accumulate a value to a tuple.
84 */
85 void
86 gen_integer(struct value *gen, int i)
87 {
88 struct value val;
89
90 value_integer_set(&val, i);
91 gen_value(gen, &val);
92 }
93
94 /*
95 * Labels - forward reference and backpatching mechanism.
96 * This is designed to be used internally. External clients
97 * (that is, where labels must be human-readable, such as the
98 * assembler) should maintain a dictionary associating strings
99 * with labels.
100 *
101 * Use cases:
102 *
103 * 1) Backward reference (requires no backpatching):
104 *
105 * label = gen_define_label(gen, NULL);
106 * ...
107 * gen_integer(gen, INSTR_GOTO);
108 * gen_gen_label_ref(gen, label);
109 *
110 * 2) Forward reference (requires backpatching):
111 *
112 * gen_integer(gen, INSTR_GOTO);
113 * label = gen_gen_label_ref(gen, NULL);
114 * ...
115 * gen_define_label(gen, label);
116 *
117 */
118
119 #define GEN_LABEL_TUPLE 0 /* Tuple into which the label points */
120 #define GEN_LABEL_LOCAL_POS 1 /* local pos to which label refers */
121 #define GEN_LABEL_GLOBAL_POS 2 /* global pos to which label refers */
122 #define GEN_LABEL_NEXT 3 /* list of backpatches to apply */
123
124 #define GEN_LABEL_SIZE 4
125
126 static int
127 gen_label_new(struct value *gen_label)
128 {
129 struct value gen_label_tag;
130
131 value_symbol_new(&gen_label_tag, "genlab", 3);
132 if (!value_tuple_new(gen_label, &gen_label_tag, GEN_LABEL_SIZE))
133 return 0;
134 value_tuple_store(gen_label, GEN_LABEL_TUPLE, &VNULL);
135 value_tuple_store_integer(gen_label, GEN_LABEL_LOCAL_POS, 0);
136 value_tuple_store_integer(gen_label, GEN_LABEL_GLOBAL_POS, 0);
137 value_tuple_store(gen_label, GEN_LABEL_NEXT, &VNULL);
138
139 return 1;
140 }
141
142 /*
143 * Generate a reference to a label into the tuple, for instance as the
144 * immediate argument of a branch instruction. If the label parameter
145 * is NULL, this will generate and return a forward reference which
146 * should be resolved by subsequently passing it to gen_define_label().
147 */
148 void
149 gen_gen_label_ref(struct value *gen, struct value *gen_label)
150 {
151 struct value *bp;
152 struct value next;
153 int global_pos;
154
155 assert(gen_label != NULL);
156
157 if (value_is_null(gen_label)) {
158 /* Not yet allocated, so allocate a new undefined one. */
159 gen_label_new(gen_label);
160 }
161
162 global_pos = value_tuple_fetch_integer(gen_label, GEN_LABEL_GLOBAL_POS);
163 if (global_pos != 0) {
164 /* Already defined, so just use it. */
165 gen_integer(gen, global_pos);
166 return;
167 }
168
169 /*
170 * The label is newly allocated, or at least has not been defined
171 * yet. So, we remember that we will need to backpatch here in the
172 * future (by adding an entry to the label's backpatch list) and,
173 * for now, generate a NULL in its slot.
174 */
175
176 value_copy(&next, value_tuple_fetch(gen_label, GEN_LABEL_NEXT));
177 bp = value_tuple_fetch(gen_label, GEN_LABEL_NEXT);
178 gen_label_new(bp);
179 value_tuple_store(bp, GEN_LABEL_TUPLE, value_tuple_fetch(gen, GEN_CURRENT_TUPLE));
180 value_tuple_store(bp, GEN_LABEL_LOCAL_POS, value_tuple_fetch(gen, GEN_LOCAL_POS));
181 value_tuple_store(bp, GEN_LABEL_GLOBAL_POS, value_tuple_fetch(gen, GEN_GLOBAL_POS));
182 value_tuple_store(bp, GEN_LABEL_NEXT, &next);
183
184 gen_value(gen, &VNULL);
185 }
186
187 /*
188 * Define a label. If the label is already allocated, this will
189 * cause previously-generated forward references to this label to be
190 * backpatched with the now-known location.
191 */
192 int
193 gen_define_label(struct value *gen, struct value *gen_label)
194 {
195 struct value *tuple = value_tuple_fetch(gen, GEN_CURRENT_TUPLE);
196 unsigned int local_pos = value_tuple_fetch_integer(gen, GEN_LOCAL_POS);
197 unsigned int global_pos = value_tuple_fetch_integer(gen, GEN_GLOBAL_POS);
198
199 struct value *bp;
200
201 assert(gen_label != NULL);
202
203 if (value_is_null(gen_label)) {
204 /* Not yet allocated, so allocate a new undefined one. */
205 gen_label_new(gen_label);
206 } else {
207 /* Fail if we try to redefine a label. */
208 if (value_tuple_fetch_integer(gen_label, GEN_LABEL_GLOBAL_POS) != 0) {
209 return 0;
210 }
211 }
212
213 value_tuple_store(gen_label, GEN_LABEL_TUPLE, tuple);
214 value_tuple_store_integer(gen_label, GEN_LABEL_LOCAL_POS, local_pos);
215 value_tuple_store_integer(gen_label, GEN_LABEL_GLOBAL_POS, global_pos);
216
217 /*
218 * Resolve any previous forward references by backpatching.
219 */
220 bp = value_tuple_fetch(gen_label, GEN_LABEL_NEXT);
221 while (!value_is_null(bp)) {
222 /*
223 * Backpatch, by placing the current global position into
224 * the slot named by the bp entry, then remove entry from list.
225 */
226 value_tuple_store_integer(
227 value_tuple_fetch(bp, GEN_LABEL_TUPLE),
228 value_tuple_fetch_integer(bp, GEN_LABEL_LOCAL_POS),
229 global_pos
230 );
231 value_tuple_store(gen_label, GEN_LABEL_NEXT,
232 value_tuple_fetch(bp, GEN_LABEL_NEXT));
233 bp = value_tuple_fetch(gen_label, GEN_LABEL_NEXT);
234 }
235
236 return 1;
237 }
238
239 /*
240 * Return a single tuple containing the contents of the gen'ed tuple chain.
241 */
242 void
243 gen_flatten(struct value *gen, struct value *dest)
244 {
245 unsigned int dest_size = value_tuple_fetch_integer(gen, GEN_GLOBAL_POS);
246 struct value *current = value_tuple_fetch(gen, GEN_ROOT_TUPLE);
247 unsigned int j = 0;
248
249 value_tuple_new(dest, value_tuple_get_tag(current), dest_size);
250
251 while (!value_is_null(current)) {
252 unsigned int i = 0;
253 unsigned int current_size = value_tuple_get_size(current);
254 struct value *x;
255 for (i = 0; i < (current_size - 1) && i < dest_size; i++) {
256 x = value_tuple_fetch(current, i);
257
258 #ifdef DEBUG
259 process_render(process_err, "(flatten %d/%d -> %d/%d: ", i, current_size, j, dest_size);
260 value_portray(process_err, x);
261 process_render(process_err, ")\n");
262 #endif
263
264 value_tuple_store(dest, j, x);
265 j++;
266 }
267 current = value_tuple_fetch(current, (current_size - 1));
268 }
269 }
0 /*
1 * gen.h
2 * Accumulate values in tuples with extents.
3 * $Id: gen.h 144 2008-07-19 01:57:04Z catseye $
4 */
5
6 #ifndef __GEN_H_
7 #define __GEN_H_
8
9 #include "value.h"
10
11 int gen_new(struct value *, struct value *, unsigned int);
12 int gen_new_default(struct value *);
13 void gen_value(struct value *, struct value *);
14 void gen_integer(struct value *, int);
15 int gen_define_label(struct value *, struct value *);
16 void gen_gen_label_ref(struct value *, struct value *);
17 void gen_flatten(struct value *, struct value *);
18
19 #endif /* !__GEN_H_ */
0 /*
1 * geninstr.c
2 */
3
4 #include <ctype.h>
5 #include <stdio.h>
6 #include <stdlib.h>
7
8 static int
9 parse_descriptor_line(const char *line, char *name, char *mode)
10 {
11 while (isspace((int)*line) && (*line != '\0')) {
12 line++;
13 }
14 if (*line != '%')
15 return 0;
16 line++;
17 while (isspace((int)*line) && (*line != '\0')) {
18 line++;
19 }
20 while (!isspace((int)*line) && (*line != '\0')) {
21 *name = *line;
22 name++;
23 line++;
24 }
25 *name = '\0';
26 while (isspace((int)*line) && (*line != '\0')) {
27 line++;
28 }
29 while (!isspace((int)*line) && (*line != '\0')) {
30 *mode = *line;
31 mode++;
32 line++;
33 }
34 *mode = '\0';
35 return 1;
36 }
37
38 int
39 main(int argc, char **argv)
40 {
41 FILE *instrtab, *instrenum, *instrlab, *vm, *localtypes;
42 static char line[512], name[80], mode[80];
43
44 argc = argc;
45 argv = argv;
46
47 if ((instrtab = fopen("instrtab.c", "w")) == NULL) {
48 perror("Couldn't open instrtab.c for writing");
49 exit(1);
50 }
51 if ((instrenum = fopen("instrenum.h", "w")) == NULL) {
52 perror("Couldn't open instrenum.h for writing");
53 exit(1);
54 }
55 if ((instrlab = fopen("instrlab.h", "w")) == NULL) {
56 perror("Couldn't open instrlab.h for writing");
57 exit(1);
58 }
59 if ((vm = fopen("vm.c", "r")) == NULL) {
60 perror("Couldn't open vm.c for reading");
61 exit(1);
62 }
63 fputs(
64 "/*\n"
65 " * instrtab.c\n"
66 " * Table of VM instructions, mapping names to opcodes.\n"
67 " * NOTE: THIS FILE WAS AUTOMATICALLY GENERATED from vm.c by geninstr\n"
68 " */\n"
69 "\n"
70 "#include \"lib.h\"\n"
71 "\n"
72 "#include \"instrtab.h\"\n"
73 "\n"
74 "struct opcode_entry opcode_table[] = {\n", instrtab);
75 fputs(
76 "/*\n"
77 " * instrenum.h\n"
78 " * C 'enum' type for VM instructions.\n"
79 " * NOTE: THIS FILE WAS AUTOMATICALLY GENERATED from vm.c by geninstr\n"
80 " */\n"
81 "\n"
82 "#ifndef __INSTRENUM_H_\n"
83 "#define __INSTRENUM_H_\n"
84 "\n"
85 "enum opcode {\n", instrenum);
86 fputs(
87 "/*\n"
88 " * instrlab.h\n"
89 " * gcc labels for VM instructions, to support direct threading.\n"
90 " * NOTE: THIS FILE WAS AUTOMATICALLY GENERATED from vm.c by geninstr\n"
91 " */\n"
92 "\n"
93 "#ifndef __INSTRLAB_H_\n"
94 "#define __INSTRLAB_H_\n"
95 "\n"
96 "static clabel instr_label[] = {\n", instrlab);
97
98 while (fgets(line, 510, vm)) {
99 int arity = 1;
100 const char *optype = "NONE";
101
102 if (!parse_descriptor_line(line, name, mode))
103 continue;
104
105 switch (mode[0]) {
106 case 'i':
107 optype = "INT";
108 break;
109 case 'a':
110 optype = "ADDR";
111 break;
112 case 'v':
113 optype = "VALUE";
114 break;
115 default:
116 arity = 0;
117 break;
118 }
119
120 fprintf(instrtab, "\t{ \"%s\",\tINSTR_%s,\t%d,\tOPTYPE_%s\t},\n",
121 name, name, arity, optype);
122 fprintf(instrenum, "\tINSTR_%s,\n", name);
123 fprintf(instrlab, "\t&&LABEL_INSTR_%s,\n", name);
124 }
125 fclose(vm);
126 fputs("\t{ NULL,\t\tINSTR_NULL,\t0,\tOPTYPE_NONE }\n};\n", instrtab);
127 fclose(instrtab);
128 fputs("\tINSTR_NULL\n};\n\n#endif /* !__INSTRENUM_H_ */\n", instrenum);
129 fclose(instrenum);
130 fputs("\tNULL\n};\n\n#endif /* !__INSTRLAB_H_ */\n", instrlab);
131 fclose(instrlab);
132
133 if ((localtypes = fopen("localtypes.h", "w")) == NULL) {
134 perror("Couldn't open localtypes.h for writing");
135 exit(1);
136 }
137 fputs(
138 "/*\n"
139 " * localtypes.h\n"
140 " * Define some types for the local system.\n"
141 " * NOTE: THIS FILE WAS AUTOMATICALLY GENERATED by geninstr\n"
142 " */\n"
143 "\n"
144 "#ifndef __LOCALTYPES_H_\n"
145 "#define __LOCALTYPES_H_\n"
146 "\n", localtypes);
147 if (sizeof(void *) == sizeof(unsigned int)) {
148 fputs("#define PTR_INT unsigned int\n", localtypes);
149 } else if (sizeof(void *) == sizeof(unsigned long int)) {
150 fputs("#define PTR_INT unsigned long int\n", localtypes);
151 } else {
152 fputs("#define PTR_INT not_supported\n", localtypes);
153 }
154 fputs("\n#endif /* !__LOCALTYPES_H_ */\n", localtypes);
155 fclose(localtypes);
156
157 exit(0);
158 }
0 /*
1 * instrtab.h
2 * Table of VM instructions, mapping names to opcodes.
3 * $Id: instrtab.h 95 2006-02-14 01:33:18Z catseye $
4 */
5
6 #ifndef __INSTRTAB_H_
7 #define __INSTRTAB_H_
8
9 #include "instrenum.h"
10
11 enum optype {
12 OPTYPE_NONE,
13 OPTYPE_INT,
14 OPTYPE_ADDR,
15 OPTYPE_VALUE
16 };
17
18 struct opcode_entry {
19 const char *token;
20 enum opcode opcode;
21 int arity;
22 enum optype optype;
23 };
24
25 extern struct opcode_entry opcode_table[];
26
27 #endif /* !__INSTRTAB_H_ */
0 /*
1 * lib.c
2 * Common functions.
3 * $Id: lib.c 114 2007-02-27 02:09:02Z catseye $
4 */
5
6 #include "lib.h"
7
8 #ifdef STANDALONE
9 /*
10 * To quote the FreeBSD manpage (despite that this is a reimplementation):
11 * The strncpy() function copies not more than len characters from src into
12 * dst, appending `\0' characters if src is less than len characters long,
13 * and not terminating dst otherwise.
14 */
15 #ifndef USE_SYSTEM_STRNCPY
16 char *
17 strncpy(char *dst, const char *src, unsigned int len)
18 {
19 char *r = dst;
20
21 while (*src != '\0' && len > 0) {
22 *dst = *src;
23 dst++;
24 src++;
25 len--;
26 }
27 while (len > 0) {
28 /* append \0 characters */
29 *dst = '\0';
30 dst++;
31 len--;
32 }
33
34 return r;
35 }
36 #endif /* !USE_SYSTEM_STRNCPY */
37
38 int
39 k_isspace(char c)
40 {
41 return c == ' ' || c == '\t' || c == '\n' || c == '\r';
42 }
43
44 int
45 k_isdigit(char c)
46 {
47 return c >= '0' && c <= '9';
48 }
49
50 int
51 k_isalpha(char c)
52 {
53 return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
54 }
55
56 #ifndef USE_SYSTEM_STRCMP
57 int
58 strcmp(const char *s1, const char *s2)
59 {
60 while (*s1 == *s2 && *s1 != '\0' && *s2 != '\0') {
61 s1++;
62 s2++;
63 }
64 if (*s1 == '\0' && *s2 == '\0') {
65 return 0;
66 }
67 if (*s1 > *s2) {
68 return 1;
69 } else {
70 return -1;
71 }
72 }
73 #endif /* !USE_SYSTEM_STRCMP */
74
75 #endif /* STANDALONE */
76
77 int
78 k_atoi(const char *s, unsigned int len)
79 {
80 int acc = 0;
81
82 while (k_isspace(*s) && len > 0) {
83 s++;
84 len--;
85 }
86 while (k_isdigit(*s) && len > 0) {
87 acc = acc * 10 + (*s - '0');
88 s++;
89 len--;
90 }
91
92 return acc;
93 }
0 /*
1 * lib.h
2 * Common definitions and function prototypes.
3 * $Id: lib.h 145 2008-11-26 10:05:16Z catseye $
4 */
5
6 #ifndef __LIB_H_
7 #define __LIB_H_
8
9 #ifndef NULL
10 #define NULL 0
11 #endif
12
13 #ifndef NDEBUG
14 #include <stdio.h>
15 #define assert(cond) { \
16 if (!(cond)) { \
17 fprintf(stderr, \
18 "assertion failed (" __FILE__ "): " #cond "\n"); \
19 abort(); \
20 } \
21 }
22 #else
23 #define assert(cond)
24 #endif
25
26 #ifdef STANDALONE
27 /* stdlib.h */
28 void *malloc(unsigned int);
29 void free(void *);
30 void exit(int);
31 void abort(void);
32
33 /* string.h */
34 int strcmp(const char *, const char *);
35 int strncmp(const char *, const char *, unsigned int);
36 char *strncpy(char *, const char *, unsigned int);
37 int strlen(const char *);
38 void *memset(void *, int, unsigned int);
39
40 /* ctype.h */
41 int k_isspace(char);
42 int k_isdigit(char);
43 int k_isalpha(char);
44 #else
45 #include <stdlib.h>
46 #include <string.h>
47 #include <ctype.h>
48 #define k_isspace(x) isspace((int)x)
49 #define k_isdigit(x) isdigit((int)x)
50 #define k_isalpha(x) isalpha((int)x)
51 #endif
52
53 int k_atoi(const char *, unsigned int);
54
55 #endif /* !__LIB_H_ */
0 /*
1 * load.c
2 * Load values from a stream-like process.
3 */
4
5 #include "lib.h"
6
7 #include "load.h"
8 #include "value.h"
9 #include "stream.h"
10
11 #ifdef DEBUG
12 #include "cmdline.h"
13 #include "portray.h"
14 #include "render.h"
15 #endif
16
17 int
18 value_load(struct value *value, struct process *p)
19 {
20 unsigned int length, i;
21 char *buffer;
22 unsigned char squeeze;
23
24 stream_read(NULL, p, &squeeze, sizeof(squeeze));
25 value->type = (enum value_type)squeeze;
26
27 #ifdef DEBUG
28 process_render(process_err, "(load:%s ", type_name_table[value->type]);
29 #endif
30
31 if ((value->type & VALUE_STRUCTURED) == 0) {
32 stream_read(NULL, p, &value->value, sizeof(value->value));
33 } else {
34 switch (value->type) {
35 case VALUE_SYMBOL:
36 {
37 stream_read(NULL, p, &length, sizeof(length));
38 buffer = malloc(length);
39 stream_read(NULL, p, buffer, sizeof(char) * length);
40 if (!value_symbol_new(value, buffer, length)) {
41 free(buffer);
42 return 0;
43 }
44 free(buffer);
45 break;
46 }
47 case VALUE_TUPLE:
48 {
49 struct value tag;
50 if (!value_load(&tag, p))
51 return 0;
52 /* XXX should eventually dispatch to a handler based on tag? */
53 if (value_equal(&tag, &tag_dict)) {
54 struct value key, val;
55
56 stream_read(NULL, p, &length, sizeof(length)); /* length is layer_size here */
57 value_dict_new(value, length); /* xxx */
58 stream_read(NULL, p, &length, sizeof(length)); /* length is num entries here */
59 for (i = 0; i < length; i++) {
60 value_load(&key, p);
61 value_load(&val, p);
62 value_dict_store(value, &key, &val);
63 }
64 } else {
65 struct value val;
66
67 stream_read(NULL, p, &length, sizeof(length));
68 value_tuple_new(value, &tag, length);
69 for (i = 0; i < length; i++) {
70 value_load(&val, p);
71 value_tuple_store(value, i, &val);
72 }
73 }
74 break;
75 }
76 default:
77 assert(value->type == VALUE_SYMBOL ||
78 value->type == VALUE_TUPLE);
79 return 0;
80 }
81 }
82
83 #ifdef DEBUG
84 value_portray(process_err, value);
85 process_render(process_err, ")\n");
86 #endif
87 return 1;
88 }
0 /*
1 * load.h
2 * Structures and prototypes for loading values from file-like processes.
3 * $Id: load.h 107 2006-03-16 23:55:56Z catseye $
4 */
5
6 #ifndef __LOAD_H_
7 #define __LOAD_H_
8
9 struct process;
10 struct value;
11
12 int value_load(struct value *, struct process *);
13
14 #endif /* !__LOAD_H_ */
0 /*
1 * portray.c
2 * Routines for parsing and rendering values.
3 * $Id: portray.c 143 2008-07-18 06:42:22Z catseye $
4 */
5
6 #include "lib.h"
7
8 #include "value.h"
9 #include "portray.h"
10 #include "render.h"
11
12 static void value_portray_nodups(struct process *, struct value *, struct value *);
13
14 /*
15 * Portray a value (i.e. render it in a human-readable way) to a process.
16 */
17 void
18 value_portray(struct process *p, struct value *v)
19 {
20 struct value seen;
21 value_dict_new(&seen, 31);
22 value_portray_nodups(p, v, &seen);
23 }
24
25 /*
26 * Recursive portion of value_portray(). Tracks a dictionary of all
27 * tuple values that have been seen, to avoid recursing infinitely
28 * into cyclic chains of nested tuples.
29 */
30 static void
31 value_portray_nodups(struct process *p, struct value *v, struct value *seen)
32 {
33 switch (v->type) {
34 case VALUE_NULL:
35 process_render(p, "[]");
36 break;
37 case VALUE_INTEGER:
38 process_render(p, "%d", value_get_integer(v));
39 break;
40 case VALUE_BOOLEAN:
41 process_render(p, "%s", value_get_boolean(v) ? "true" : "false");
42 break;
43 case VALUE_PROCESS:
44 process_render(p, "PROCESS#[0x%08x]", (long int)value_get_process(v));
45 break;
46 case VALUE_LABEL:
47 process_render(p, "LABEL#[0x%08x]", (long int)value_get_label(v));
48 break;
49 case VALUE_SYMBOL:
50 process_render(p, "%s", value_symbol_get_token(v));
51 break;
52 case VALUE_TUPLE:
53 {
54 struct value *tag = value_tuple_get_tag(v);
55 unsigned int max = value_tuple_get_size(v);
56 unsigned int i;
57
58 if (!value_is_null(seen))
59 value_dict_store(seen, v, &VTRUE);
60
61 /* XXX should eventually dispatch to a handler based on tag. */
62 if (value_equal(tag, &tag_dict)) {
63 struct value dict_iter;
64 struct value *key;
65
66 value_dict_new_iter(&dict_iter, v);
67
68 process_render(p, "{");
69 key = value_dict_iter_get_current_key(&dict_iter);
70 while (!value_is_null(key)) {
71 value_portray_nodups(p, key, seen);
72 process_render(p, "=");
73 /* XXX not so good; use iter */
74 value_portray_nodups(p, value_dict_fetch(v, key), seen);
75 value_dict_iter_advance(&dict_iter);
76 key = value_dict_iter_get_current_key(&dict_iter);
77 if (!value_is_null(key)) {
78 process_render(p, ", ");
79 }
80 }
81 process_render(p, "}");
82 } else {
83 process_render(p, "<");
84 value_portray(p, value_tuple_get_tag(v));
85 process_render(p, ": ");
86 for (i = 0; i < max; i++) {
87 struct value *k = value_tuple_fetch(v, i);
88 if (value_is_null(seen) ||
89 value_is_null(value_dict_fetch(seen, k))) {
90 value_portray_nodups(p, k, seen);
91 } else {
92 process_render(p, "TUPLE#[0x%08x]",
93 value_get_unique_id(v));
94 }
95 if (i < (max - 1))
96 process_render(p, ", ");
97 }
98 process_render(p, ">");
99 }
100 break;
101 }
102 }
103 }
0 /*
1 * portray.h
2 * Prototypes and structures for rendering values to processes.
3 * $Id: portray.h 107 2006-03-16 23:55:56Z catseye $
4 */
5
6 #ifndef __PORTRAY_H_
7 #define __PORTRAY_H_
8
9 struct value;
10 struct process;
11
12 void value_portray(struct process *, struct value *);
13
14 #endif /* !__PORTRAY_H_ */
0 /*
1 * process.c
2 * Cooperative processes.
3 */
4
5
6 #include "lib.h"
7
8 #include "process.h"
9
10 struct process *
11 process_new(void)
12 {
13 struct process *p;
14
15 p = malloc(sizeof(struct process));
16 p->head = NULL;
17 p->tail = NULL;
18 p->run = NULL;
19 p->aux = NULL;
20 p->waiting = 0;
21 p->done = 0;
22 p->next = NULL;
23
24 return p;
25 }
26
27 void
28 process_enqueue(struct process *p, const struct value *v)
29 {
30 struct message *m;
31
32 m = malloc(sizeof(struct message));
33 value_copy(&m->value, v);
34 m->prev = NULL;
35 m->next = p->head;
36 if (p->head != NULL)
37 p->head->prev = m;
38 p->head = m;
39 if (p->tail == NULL)
40 p->tail = m;
41 }
42
43 int
44 process_dequeue(struct process *p, struct value *v)
45 {
46 struct message *m;
47
48 m = p->tail;
49 if (m == NULL)
50 return 0;
51 value_copy(v, &m->value);
52
53 /* remove message from queue */
54 assert(m->next == NULL);
55 if (m->prev == NULL)
56 p->head = NULL;
57 else
58 m->prev->next = NULL;
59 p->tail = m->prev;
60
61 free(m);
62
63 return 1;
64 }
65
66 void
67 process_run(struct process *p)
68 {
69 assert(p->run != NULL);
70 p->run(p);
71 }
72
73 void
74 process_free(struct process *p)
75 {
76 struct message *m, *n;
77
78 /* assert(p->done); */
79 m = p->head;
80 while (m != NULL) {
81 n = m->next;
82 free(m);
83 m = n;
84 }
85 free(p);
86 }
0 /*
1 * process.h
2 * Cooperative concurrent processes.
3 */
4
5 #ifndef __PROCESS_H_
6 #define __PROCESS_H_
7
8 #include "value.h"
9
10 struct process;
11
12 typedef void (*runfunc)(struct process *);
13
14 struct process {
15 int waiting;
16 int done;
17 runfunc run;
18 void *aux;
19 struct value aux_value;
20 struct message *head;
21 struct message *tail;
22 struct process *next;
23 };
24
25 struct message {
26 struct message *next;
27 struct message *prev;
28 struct value value;
29 };
30
31 /* Prototypes */
32
33 struct process *process_new(void);
34
35 /*
36 * places the value in the process's mailbox. This is what the VM instruction SEND
37 * does. SEND also checks if the destination process is currently waiting, (and if
38 * its mailbox is empty?), and if it is, it wakes it up so that it can deal immediately
39 * with the message. Maybe. I mean, this behaviour sounds good, but maybe we shouldn't
40 * guarantee it.
41 *
42 * We should also prevent side-effecting of that value. Maybe values can be "owned"...
43 */
44 void process_enqueue(struct process *, const struct value *);
45
46 /*
47 * retrieves a value from the process's mailbox. p should be self. This is what
48 * the RECV VM instruction does. If there is no message, the process should set its
49 * waiting flag to true, and yield.
50 */
51 int process_dequeue(struct process *, struct value *);
52
53 /*
54 * Let the process p execute for a bit. Concurrency is cooperative here, so p promises
55 * that it will return from this function "in a little while". For the VM, this is not
56 * hard; we return after executing 100 or so instructions. For native code, it should
57 * be carefully written!
58 */
59 void process_run(struct process *);
60
61 void process_free(struct process *);
62
63 #endif /* !__PROCESS_H_ */
64
0 /*
1 * render.c
2 * printf-like formatting to a stream-like process.
3 */
4
5 #include <stdarg.h>
6
7 #include "lib.h"
8 #include "stream.h"
9
10 #include "render.h"
11
12 static unsigned int
13 extract_int(const char *str, unsigned int *pos)
14 {
15 unsigned int val = 0;
16
17 while (k_isdigit(str[*pos])) {
18 val = val * 10 + (str[*pos] - '0');
19 (*pos)++;
20 }
21
22 return val;
23 }
24
25 /*
26 * Note that there is no trailing NUL in the buf!
27 */
28 static unsigned int
29 render_int(char *buf, unsigned int bufsize, int val)
30 {
31 int pos = bufsize - 1;
32 int sign = 0;
33
34 if (val == 0) {
35 buf[pos] = '0';
36 return pos;
37 }
38 if (val < 0) {
39 val *= -1;
40 sign = -1;
41 }
42 while (pos >= 0 && val > 0) {
43 buf[pos] = (char)((val % 10) + '0');
44 pos--;
45 val = val / 10;
46 }
47 if (pos >= 0 && sign == -1) {
48 buf[pos] = '-';
49 pos--;
50 }
51 if (pos < 0) {
52 buf[0] = '?'; /* signify incorrectly rendered number */
53 }
54
55 return pos + 1;
56 }
57
58 #define MAX_DIGITS 32
59
60 /*
61 * Similar to, but different from, C's printf().
62 * The size specifier of each field gives the maximum number
63 * of characters that will be rendered into that field, but
64 * does not (at the moment) cause the field to have a fixed
65 * display width.
66 */
67 void
68 process_render(struct process *p, const char *fmt, ...)
69 {
70 unsigned int pos = 0, length;
71 va_list args;
72
73 assert(p != NULL);
74 va_start(args, fmt);
75
76 while (fmt[pos] != '\0') {
77 if (fmt[pos] != '%') {
78 stream_write(NULL, p, &fmt[pos], 1);
79 pos++;
80 continue;
81 }
82
83 /* found a %. now get length */
84 pos++;
85 length = extract_int(fmt, &pos);
86 length = length;
87 /* find formatting code and select formatting */
88 switch (fmt[pos]) {
89 case '%':
90 stream_write(NULL, p, &fmt[pos], 1);
91 break;
92 case 'c':
93 {
94 char c = (char)va_arg(args, int);
95
96 stream_write(NULL, p, &c, 1);
97 break;
98 }
99 case 's':
100 {
101 char *arg = va_arg(args, char *);
102
103 stream_write(NULL, p, arg, strlen(arg));
104 break;
105 }
106 case 'd': case 'x': /* XXX for now! */
107 {
108 int val = va_arg(args, int);
109 unsigned int digit_pos;
110 char *digits;
111
112 digits = malloc(MAX_DIGITS);
113 digit_pos = render_int(digits, MAX_DIGITS, val);
114 stream_write(NULL, p, (digits + digit_pos), (MAX_DIGITS - digit_pos));
115 free(digits);
116
117 break;
118 }
119 default:
120 assert("undefined formatting code" == NULL);
121 break;
122 }
123
124 pos++;
125 }
126 }
0 /*
1 * render.h
2 * Prototypes for stream rendering (printf-like formatting).
3 * $Id: stream.h 116 2007-03-26 00:43:44Z catseye $
4 */
5
6 #ifndef __RENDER_H_
7 #define __RENDER_H_
8
9 struct process;
10
11 void process_render(struct process *, const char *, ...);
12
13 #endif /* !__RENDER_H_ */
0 /*
1 * report.c
2 * Error/warning reporter.
3 * $Id: report.c 139 2008-07-16 09:56:31Z catseye $
4 */
5
6 #include <stdarg.h>
7
8 #include "lib.h"
9
10 #include "process.h"
11 #include "stream.h"
12 #include "file.h"
13
14 #include "render.h"
15
16 #include "report.h"
17
18 struct reporter {
19 int finished;
20 int verbose;
21 int errors;
22 int warnings;
23 struct process *stream;
24 const char *phase;
25 };
26
27 /*
28 * phase should always point to a literal constant string like "Parsing"
29 */
30 struct reporter *
31 reporter_new(const char *phase, struct process *stream, int verbose)
32 {
33 struct reporter *r;
34
35 if ((r = malloc(sizeof(struct reporter))) == NULL) {
36 return NULL;
37 }
38
39 r->verbose = verbose;
40 r->errors = 0;
41 r->warnings = 0;
42 r->finished = 0;
43 r->stream = stream;
44 if (r->stream == NULL) {
45 r->stream = file_open("*stderr", "w");
46 }
47 r->phase = phase;
48
49 return r;
50
51 if (verbose) {
52 process_render(r->stream, "%s started\n", r->phase);
53 }
54 }
55
56 void
57 reporter_free(struct reporter *r)
58 {
59 if (!r->finished) {
60 reporter_finish(r);
61 }
62 stream_close(NULL, r->stream);
63 free(r);
64 }
65
66 int
67 reporter_finish(struct reporter *r)
68 {
69 if (r->verbose) {
70 process_render(r->stream,
71 "%s finished with %d errors and %d warnings\n",
72 r->phase, r->errors, r->warnings);
73 }
74 r->phase = NULL;
75 r->finished = 1;
76 return r->errors;
77 }
78
79 struct process *
80 reporter_stream(struct reporter *r)
81 {
82 return r->stream;
83 }
84
85 int
86 reporter_has_errors(struct reporter *r)
87 {
88 return r->errors > 0;
89 }
90
91 void
92 report(struct reporter *r, enum report_type rtype, const char *fmt, ...)
93 {
94 va_list args;
95
96 va_start(args, fmt);
97 report_va_list(r, rtype, fmt, args);
98 va_end(args);
99 }
100
101 void
102 report_va_list(struct reporter *r, enum report_type rtype, const char *fmt, va_list args)
103 {
104 int i;
105
106 assert(!r->finished);
107
108 process_render(r->stream, "%s %s: ",
109 r->phase, rtype == REPORT_ERROR ? "Error" : "Warning");
110
111 for (i = 0; fmt[i] != '\0'; i++) {
112 if (fmt[i] == '%') {
113 i++;
114 switch (fmt[i]) {
115 case 's':
116 process_render(r->stream, "%s",
117 va_arg(args, char *));
118 break;
119 case 'd':
120 process_render(r->stream, "%d",
121 va_arg(args, int));
122 break;
123 }
124 } else {
125 process_render(r->stream, "%c", fmt[i]);
126 }
127 }
128
129 process_render(r->stream, ".\n");
130
131 switch (rtype) {
132 case REPORT_ERROR:
133 r->errors++;
134 break;
135 case REPORT_WARNING:
136 r->warnings++;
137 break;
138 }
139 }
0 /*
1 * report.h
2 * Error/warning reporter.
3 * $Id: report.h 139 2008-07-16 09:56:31Z catseye $
4 */
5
6 #ifndef __REPORT_H_
7 #define __REPORT_H_
8
9 #include <stdarg.h>
10
11 struct process;
12
13 enum report_type {
14 REPORT_WARNING,
15 REPORT_ERROR
16 };
17
18 struct reporter;
19
20 struct reporter *reporter_new(const char *, struct process *, int);
21 int reporter_finish(struct reporter *);
22 void reporter_free(struct reporter *);
23
24 struct process *reporter_stream(struct reporter *);
25 int reporter_has_errors(struct reporter *);
26
27 void report(struct reporter *, enum report_type, const char *, ...);
28 void report_va_list(struct reporter *, enum report_type, const char *, va_list);
29
30 #endif /* !__REPORT_H_ */
0 /*
1 * run.c
2 * Main program segment of Kosheri virtual machine.
3 */
4
5 #include "lib.h"
6 #include "file.h"
7 #include "stream.h"
8 #include "cmdline.h"
9
10 #include "process.h"
11 #include "vmproc.h"
12 #include "load.h"
13
14 #include "value.h"
15
16 #ifdef DEBUG
17 #include "render.h"
18 #endif
19
20 static void
21 run_main(struct value *args, struct value *result)
22 {
23 struct value vm; /* virtual machine we will run */
24 struct value vmfile_sym;
25
26 struct value code; /* code for the virtual machine */
27 struct process *in; /* file process we will load it from */
28 struct process *first; /* main process the VM will run in */
29 struct process *curr; /* current process in our schedule */
30 struct process *next;
31 struct value *vmfile;
32
33 value_symbol_new(&vmfile_sym, "vmfile", 6);
34 vmfile = value_dict_fetch(args, &vmfile_sym);
35
36 in = file_open(value_symbol_get_token(vmfile), "r");
37 value_load(&code, in);
38 stream_close(NULL, in);
39
40 value_vm_new(&vm, &code);
41 curr = first = vmproc_new(&vm);
42 while (first != NULL) {
43 #ifdef DEBUG
44 process_render(process_err, "Running process %d\n", curr);
45 #endif
46 process_run(curr);
47 next = curr->next;
48 while (next != NULL && next->done) {
49 curr->next = next->next;
50 process_free(next);
51 next = curr->next;
52 }
53 curr = next;
54 if (curr == NULL) {
55 while (first != NULL && first->done) {
56 next = first->next;
57 process_free(first);
58 first = next;
59 }
60 curr = first;
61 }
62 }
63
64 value_integer_set(result, 0);
65 }
66
67 MAIN(run_main)
0 /*
1 * save.c
2 * Save values to a stream-like process.
3 */
4
5 #include "lib.h"
6 #include "stream.h"
7
8 #include "save.h"
9 #include "value.h"
10
11 #ifdef DEBUG
12 #include "cmdline.h"
13 #include "portray.h"
14 #include "render.h"
15 #endif
16
17 int
18 value_save(struct process *p, struct value *value)
19 {
20 unsigned char squeeze = (unsigned char)value->type;
21
22 #ifdef DEBUG
23 process_render(process_err, "(save:%s ", type_name_table[(int)squeeze]);
24 if (value->type == VALUE_SYMBOL) {
25 process_render(process_err, "[%d] ", value_symbol_get_length(value));
26 }
27 value_portray(process_err, value);
28 process_render(process_err, ")\n");
29 #endif
30
31 stream_write(NULL, p, &squeeze, sizeof(squeeze));
32
33 if ((value->type & VALUE_STRUCTURED) == 0) {
34 stream_write(NULL, p, &value->value, sizeof(value->value));
35 } else {
36 unsigned int length, i;
37
38 switch (value->type) {
39 case VALUE_SYMBOL:
40 {
41 const char *token = value_symbol_get_token(value);
42 length = value_symbol_get_length(value);
43 stream_write(NULL, p, &length, sizeof(length));
44 stream_write(NULL, p, token, sizeof(char) * length);
45 break;
46 }
47 case VALUE_TUPLE:
48 {
49 struct value *tag = value_tuple_get_tag(value);
50 value_save(p, tag);
51 /* XXX should eventually dispatch to a handler based on tag. */
52 if (value_equal(tag, &tag_dict)) {
53 struct value dict_iter;
54 struct value *key;
55
56 value_dict_new_iter(&dict_iter, value);
57
58 length = value_dict_get_layer_size(value);
59 stream_write(NULL, p, &length, sizeof(length));
60 length = value_dict_get_length(value);
61 stream_write(NULL, p, &length, sizeof(length));
62
63 key = value_dict_iter_get_current_key(&dict_iter);
64 while (!value_is_null(key)) {
65 value_save(p, key);
66 value_save(p, value_dict_fetch(value, key)); /* XXX not so good; use iter */
67 value_dict_iter_advance(&dict_iter);
68 key = value_dict_iter_get_current_key(&dict_iter);
69 }
70 } else {
71 length = value_tuple_get_size(value);
72 stream_write(NULL, p, &length, sizeof(length));
73 for (i = 0; i < length; i++) {
74 value_save(p, value_tuple_fetch(value, i));
75 }
76 }
77 break;
78 }
79 default:
80 assert(value->type == VALUE_SYMBOL ||
81 value->type == VALUE_TUPLE);
82 break;
83 }
84 }
85
86 return 1;
87 }
0 /*
1 * save.h
2 * Structures and prototypes for saving values to processes.
3 */
4
5 #ifndef __SAVE_H_
6 #define __SAVE_H_
7
8 struct process;
9 struct value;
10
11 int value_save(struct process *, struct value *);
12
13 #endif /* !__SAVE_H_ */
0 /*
1 * scan.c
2 * Lexical scanner.
3 * $Id: scan.c 139 2008-07-16 09:56:31Z catseye $
4 */
5
6 #include <stdarg.h>
7
8 #include "lib.h"
9
10 #include "stream.h"
11 #include "file.h"
12
13 #include "scan.h"
14 #include "report.h"
15 #include "render.h"
16
17 enum token_type {
18 TOKEN_EOF,
19 TOKEN_NUMBER,
20 TOKEN_BAREWORD,
21 TOKEN_SYMBOL,
22 TOKEN_QUOTED_STRING
23 };
24
25 struct scanner {
26 struct reporter *reporter;
27 struct process *input; /* file process from which we are scanning */
28 const char *filename; /* name of file scanning from */
29 char *token; /* text content of token we just scanned */
30 enum token_type token_type; /* type of token that was scanned */
31 unsigned int token_length; /* length of the token that was scanned */
32 int line; /* current line number, 1-based */
33 int column; /* current column number, 1-based */
34 int last_column; /* for putback */
35 char *putback_buf; /* buffer of characters put back */
36 int putback_pos; /* position within putback buffer */
37 };
38
39 #define PUTBACK_SIZE 80
40
41 struct scanner *
42 scanner_new(struct reporter *r)
43 {
44 struct scanner *sc;
45
46 if ((sc = malloc(sizeof(struct scanner))) == NULL) {
47 return NULL;
48 }
49 if ((sc->token = malloc(256 * sizeof(char))) == NULL) {
50 free(sc);
51 return NULL;
52 }
53
54 sc->reporter = r;
55 sc->filename = NULL;
56 sc->input = NULL;
57 sc->putback_buf = malloc(PUTBACK_SIZE * sizeof(char));
58 sc->putback_pos = 0;
59
60 return sc;
61 }
62
63 void
64 scanner_free(struct scanner *sc)
65 {
66 scanner_close(sc);
67 free(sc->token);
68 free(sc);
69 }
70
71 void
72 scanner_reset(struct scanner *sc)
73 {
74 sc->line = 1;
75 sc->column = 1;
76 sc->last_column = 0;
77 scanner_scan(sc); /* prime the pump */
78 }
79
80 /*
81 * caller is responsible for freeing the filename
82 */
83 int
84 scanner_open(struct scanner *sc, const char *filename)
85 {
86 sc->filename = filename;
87 if ((sc->input = file_open(filename, "r")) == NULL) {
88 scanner_report(sc, REPORT_ERROR,
89 "Can't open '%s' for reading", filename);
90 return 0;
91 }
92 scanner_reset(sc);
93 return 1;
94 }
95
96 /*
97 * caller is responsible for freeing the filename
98 */
99 int
100 scanner_attach(struct scanner *sc, struct process *p, const char *filename)
101 {
102 sc->filename = filename;
103 sc->input = p;
104 scanner_reset(sc);
105 return 1;
106 }
107
108 void
109 scanner_close(struct scanner *sc)
110 {
111 if (sc->filename != NULL) {
112 sc->filename = NULL;
113 }
114 if (sc->input != NULL) {
115 stream_close(NULL, sc->input);
116 sc->input = NULL; /* ? */
117 }
118 }
119
120 /*
121 * x is not a string, it is a pointer to a single character.
122 */
123 static void
124 scan_char(struct scanner *sc, char *x)
125 {
126 sc->last_column = sc->column;
127
128 /* do a 'getc' */
129 if (sc->putback_pos > 0) {
130 *x = sc->putback_buf[sc->putback_pos--];
131 } else {
132 stream_read(NULL, sc->input, x, sizeof(char));
133 }
134
135 if (*x == '\n') {
136 sc->column = 1;
137 sc->line++;
138 } else if (*x == '\t') {
139 sc->column++;
140 while (sc->column % 8 != 0)
141 sc->column++;
142 } else {
143 sc->column++;
144 }
145 }
146
147 static void
148 putback(struct scanner *sc, char x)
149 {
150 if (stream_is_at_end(NULL, sc->input))
151 return;
152
153 /* do a 'ungetc' */
154 if (sc->putback_pos < (PUTBACK_SIZE - 1)) {
155 sc->putback_buf[++sc->putback_pos] = x;
156 } else {
157 scanner_report(sc, REPORT_ERROR,
158 "Putback buffer size exceeded on '%s'", sc->filename);
159 }
160
161 sc->column = sc->last_column;
162 if (x == '\n')
163 sc->line--;
164 }
165
166 static void
167 real_scan(struct scanner *sc)
168 {
169 char x;
170 int i = 0;
171
172 sc->token[0] = '\0';
173 sc->token_length = 0;
174 if (stream_is_at_end(NULL, sc->input)) {
175 sc->token_type = TOKEN_EOF;
176 return;
177 }
178
179 scan_char(sc, &x);
180
181 /* Skip whitespace. */
182
183 top:
184 while (k_isspace(x) && !stream_is_at_end(NULL, sc->input)) {
185 scan_char(sc, &x);
186 }
187
188 /* Skip comments. */
189
190 if (x == '/') {
191 scan_char(sc, &x);
192 if (x == '/') {
193 while (x != '\n' && !stream_is_at_end(NULL, sc->input)) {
194 scan_char(sc, &x);
195 }
196 goto top;
197 } else {
198 putback(sc, x);
199 x = '/';
200 /* falls through to the bottom of scan() */
201 }
202 }
203
204 if (stream_is_at_end(NULL, sc->input)) {
205 sc->token[0] = '\0';
206 sc->token_type = TOKEN_EOF;
207 return;
208 }
209
210 /*
211 * Scan decimal numbers. Must start with a
212 * digit (not a sign or decimal point.)
213 */
214 if (k_isdigit(x)) {
215 while ((k_isdigit(x) || x == '.') && !stream_is_at_end(NULL, sc->input)) {
216 sc->token[i++] = x;
217 sc->token_length++;
218 scan_char(sc, &x);
219 }
220 putback(sc, x);
221 sc->token[i] = '\0';
222 sc->token_type = TOKEN_NUMBER;
223 return;
224 }
225
226 /*
227 * Scan quoted strings.
228 */
229 if (x == '"') {
230 scan_char(sc, &x);
231 while (x != '"' && !stream_is_at_end(NULL, sc->input) && i < 255) {
232 sc->token[i++] = x;
233 sc->token_length++;
234 scan_char(sc, &x);
235 }
236 sc->token[i] = '\0';
237 sc->token_type = TOKEN_QUOTED_STRING;
238 return;
239 }
240
241 /*
242 * Scan alphanumeric ("bareword") tokens.
243 */
244 if (k_isalpha(x) || x == '_') {
245 while ((k_isalpha(x) || k_isdigit(x) || x == '_') && !stream_is_at_end(NULL, sc->input)) {
246 sc->token[i++] = x;
247 sc->token_length++;
248 scan_char(sc, &x);
249 }
250 putback(sc, x);
251 sc->token[i] = '\0';
252 sc->token_type = TOKEN_BAREWORD;
253 return;
254 }
255
256 /*
257 * Scan multi-character symbols.
258 */
259 if (x == '>' || x == '<' || x == '!') {
260 sc->token[i++] = x;
261 sc->token_length++;
262 scan_char(sc, &x);
263 if (x == '=' && !stream_is_at_end(NULL, sc->input)) {
264 sc->token[i++] = x;
265 sc->token_length++;
266 scan_char(sc, &x);
267 } else {
268 putback(sc, x);
269 }
270 sc->token[i] = '\0';
271 sc->token_type = TOKEN_SYMBOL;
272 return;
273 }
274
275 /*
276 * Degenerate case: scan single symbols.
277 */
278 sc->token[0] = x;
279 sc->token[1] = '\0';
280 sc->token_length = 1;
281 sc->token_type = TOKEN_SYMBOL;
282 }
283
284 void
285 scanner_scan(struct scanner *sc)
286 {
287 real_scan(sc);
288 #ifdef DEBUG
289 printf("scanned -> '%s'\n", sc->token);
290 #endif
291 }
292
293 void
294 scanner_expect(struct scanner *sc, const char *x)
295 {
296 if (strcmp(sc->token, x) == 0) {
297 scanner_scan(sc);
298 } else {
299 scanner_report(sc, REPORT_ERROR, "Expected '%s'", x);
300 }
301 }
302
303 void
304 scanner_scanline(struct scanner *sc)
305 {
306 char x;
307
308 scan_char(sc, &x);
309 while (x != '\n' && !stream_is_at_end(NULL, sc->input)) {
310 scan_char(sc, &x);
311 }
312 real_scan(sc);
313 }
314
315 int
316 scanner_tokeq(struct scanner *sc, const char *token)
317 {
318 return strcmp(sc->token, token) == 0;
319 }
320
321 const char *
322 scanner_token_string(struct scanner *sc)
323 {
324 return sc->token;
325 }
326
327 int
328 scanner_token_length(struct scanner *sc)
329 {
330 return sc->token_length;
331 }
332
333 int
334 scanner_eof(struct scanner *sc)
335 {
336 return sc->token_type == TOKEN_EOF;
337 }
338
339 const char *
340 scanner_filename(struct scanner *sc)
341 {
342 return sc->filename != NULL ? sc->filename : "<no file>";
343 }
344
345 int
346 scanner_line(struct scanner *sc)
347 {
348 return sc->line;
349 }
350
351 int
352 scanner_column(struct scanner *sc)
353 {
354 return sc->column;
355 }
356
357 void
358 scanner_report(struct scanner *sc, enum report_type rtype, const char *fmt, ...)
359 {
360 va_list args;
361
362 /*
363 * Breaking abstraction just to have a nicely-formatted error message...
364 */
365 process_render(reporter_stream(sc->reporter),
366 "(%s, line %d, column %d, token '%s'): ",
367 scanner_filename(sc), scanner_line(sc),
368 scanner_column(sc), scanner_token_string(sc));
369
370 va_start(args, fmt);
371 report_va_list(sc->reporter, rtype, fmt, args);
372 va_end(args);
373 }
0 /*
1 * scan.h
2 * Lexical scanner structures and prototypes.
3 * $Id: scan.h 139 2008-07-16 09:56:31Z catseye $
4 */
5
6 #ifndef __SCAN_H_
7 #define __SCAN_H_
8
9 #include "report.h"
10
11 struct scanner;
12
13 struct scanner *scanner_new(struct reporter *);
14 void scanner_free(struct scanner *);
15
16 void scanner_reset(struct scanner *);
17 int scanner_attach(struct scanner *, struct process *, const char *);
18 int scanner_open(struct scanner *, const char *);
19 void scanner_close(struct scanner *);
20
21 void scanner_scan(struct scanner *);
22 void scanner_expect(struct scanner *, const char *);
23 void scanner_scanline(struct scanner *);
24
25 int scanner_tokeq(struct scanner *, const char *);
26 const char *scanner_token_string(struct scanner *);
27 int scanner_token_length(struct scanner *);
28
29 int scanner_eof(struct scanner *);
30 const char *scanner_filename(struct scanner *);
31 int scanner_line(struct scanner *);
32 int scanner_column(struct scanner *);
33
34 void scanner_report(struct scanner *, enum report_type, const char *, ...);
35
36 #endif /* !__SCAN_H_ */
0 /*
1 * stream.c
2 * Routines for communicating with stream-like processes.
3 */
4
5 #include "lib.h"
6
7 #include "process.h"
8
9 #include "stream.h"
10
11 static void
12 read_receiver_run(struct process *self)
13 {
14 struct value msg;
15 const char *token;
16 int length;
17 int i;
18
19 assert(self != NULL);
20 assert(self->aux != NULL);
21
22 while (process_dequeue(self, &msg)) {
23 token = value_symbol_get_token(&msg);
24 length = value_symbol_get_length(&msg);
25 for (i = 0; i < length; i++) {
26 ((char *)self->aux)[i] = token[i];
27 }
28 /* XXX assert the queue is empty? */
29 }
30 }
31
32 static struct process *
33 read_receiver_new(void *buffer)
34 {
35 struct process *p;
36
37 p = process_new();
38 p->run = read_receiver_run;
39 p->aux = buffer;
40
41 return p;
42 }
43
44 static void
45 eof_receiver_run(struct process *self)
46 {
47 struct value msg;
48
49 assert(self != NULL);
50 assert(self->aux != NULL);
51
52 while (process_dequeue(self, &msg)) {
53 *(int *)(self->aux) = value_get_boolean(&msg);
54 /* XXX assert the queue is empty? */
55 }
56 }
57
58 static struct process *
59 eof_receiver_new(int *result)
60 {
61 struct process *p;
62
63 p = process_new();
64 p->run = eof_receiver_run;
65 p->aux = result;
66
67 return p;
68 }
69
70 void
71 stream_write(struct process *self, struct process *p, const void *data, unsigned int size)
72 {
73 struct value msg, tag;
74
75 self = self;
76
77 value_symbol_new(&tag, "write", 5);
78 value_tuple_new(&msg, &tag, 1);
79 value_symbol_new(value_tuple_fetch(&msg, 0), data, size);
80 process_enqueue(p, &msg);
81 process_run(p);
82 }
83
84 void
85 stream_read(struct process *self, struct process *p, void *buffer, unsigned int size)
86 {
87 struct value msg, tag;
88 struct process *receiver;
89
90 if (self == NULL) {
91 receiver = read_receiver_new(buffer);
92 } else {
93 receiver = self;
94 }
95
96 value_symbol_new(&tag, "read", 4);
97 value_tuple_new(&msg, &tag, 2);
98 value_process_set(value_tuple_fetch(&msg, 0), receiver);
99 value_integer_set(value_tuple_fetch(&msg, 1), size);
100 process_enqueue(p, &msg);
101 process_run(p);
102
103 if (receiver == self) {
104 return;
105 }
106
107 process_run(receiver);
108 process_free(receiver);
109 }
110
111 int
112 stream_is_at_end(struct process *self, struct process *p)
113 {
114 struct value msg, tag;
115 struct process *receiver;
116 int result;
117
118 if (self == NULL) {
119 receiver = eof_receiver_new(&result);
120 } else {
121 receiver = self;
122 }
123
124 value_symbol_new(&tag, "eof", 3);
125 value_tuple_new(&msg, &tag, 1);
126 value_process_set(value_tuple_fetch(&msg, 0), receiver);
127 process_enqueue(p, &msg);
128 process_run(p);
129
130 if (receiver == self) {
131 return 0;
132 }
133
134 process_run(receiver);
135 process_free(receiver);
136
137 return result;
138 }
139
140 void
141 stream_close(struct process *self, struct process *p)
142 {
143 struct value msg, tag;
144
145 self = self;
146
147 value_symbol_new(&tag, "close", 5);
148 value_tuple_new(&msg, &tag, 0);
149 process_enqueue(p, &msg);
150 process_run(p);
151 }
0 /*
1 * stream.h
2 * Routines for communicating with stream-like processes.
3 */
4
5 #ifndef __STREAM_H_
6 #define __STREAM_H_
7
8 struct process;
9
10 /*
11 * The first argument is the process that is calling the stream-
12 * like process, a.k.a. "self". It may be NULL, to communicate
13 * with a stream from a non-process context.
14 */
15
16 /*
17 * Send a message of the form <write: data-in-symbol-form> to the stream.
18 */
19 void stream_write(struct process *, struct process *, const void *, unsigned int);
20
21 /*
22 * Send a message of the form <read: receiver, length> to the stream.
23 * If self was given, it will be used as the receiver, and it will receive
24 * a response message in the form of a symbol.
25 * If self is NULL, a provisional pseudo-receiver process will be supplied
26 * by this function, and data will be returned in the void *.
27 */
28 void stream_read(struct process *, struct process *, void *, unsigned int);
29
30 /*
31 * Send a message of the form <eof: receiver> to the stream.
32 * If self was given, it will be used as the receiver, and it will receive
33 * a response message in the form of a boolean.
34 * If self is NULL, a provisional pseudo-receiver process will be supplied
35 * by this function, and the boolean will be returned by the function.
36 */
37 int stream_is_at_end(struct process *, struct process *);
38
39 /*
40 * Send a message of the form <close:> to the stream.
41 */
42 void stream_close(struct process *, struct process *);
43
44 #endif /* !__FILE_H_ */
0 /*
1 * thaw.c
2 * Load a term from a binary termfile and
3 * write out a textual version.
4 */
5
6 #include "lib.h"
7 #include "cmdline.h"
8
9 #include "stream.h"
10 #include "file.h"
11
12 #include "report.h"
13 #include "value.h"
14
15 #include "load.h"
16 #include "portray.h"
17
18 /* Main Program / Driver */
19
20 static void
21 thaw_main(struct value *args, struct value *result)
22 {
23 struct process *p;
24 struct reporter *r;
25 struct value term;
26
27 struct value *termfile, *binfile;
28 struct value termfile_sym, binfile_sym;
29
30 value_symbol_new(&termfile_sym, "termfile", 8);
31 value_symbol_new(&binfile_sym, "binfile", 7);
32 termfile = value_dict_fetch(args, &termfile_sym);
33 binfile = value_dict_fetch(args, &binfile_sym);
34
35 r = reporter_new("Thawing", NULL, 1);
36
37 /*
38 * Read in.
39 */
40 p = file_open(value_symbol_get_token(binfile), "r");
41 value_load(&term, p);
42 stream_close(NULL, p);
43
44 /*
45 * Write out.
46 */
47 p = file_open(value_symbol_get_token(termfile), "w");
48 value_portray(p, &term);
49 stream_close(NULL, p);
50
51 /*
52 * Finish up.
53 */
54 value_integer_set(result, reporter_has_errors(r) ? 1 : 0);
55 reporter_free(r);
56 }
57
58 MAIN(thaw_main)
0 /*
1 * value.c
2 * Values.
3 */
4
5 #include "lib.h"
6
7 #include "value.h"
8
9 #ifdef DEBUG
10 #include "cmdline.h"
11 #include "portray.h"
12 #include "render.h"
13 #endif
14
15 /*
16 * Structured values - strings, function values, and the like.
17 * These are dynamically allocated, garbage collected, and so forth.
18 */
19 struct structured_value {
20 unsigned char admin; /* ADMIN_ flags */
21 struct structured_value *next;
22 };
23
24 #define ADMIN_FREE 1 /* on the free list */
25 #define ADMIN_MARKED 2 /* marked, during gc */
26
27 struct value VNULL = { VALUE_NULL, { 0 } };
28
29 struct value VFALSE = { VALUE_BOOLEAN, { 0 } };
30 struct value VTRUE = { VALUE_BOOLEAN, { 1 } };
31
32 struct value tag_vm = { VALUE_INTEGER, { 1 } };
33 struct value tag_ar = { VALUE_INTEGER, { 3 } };
34 struct value tag_dict = { VALUE_INTEGER, { 4 } };
35 struct value tag_list = { VALUE_INTEGER, { 5 } };
36 struct value tag_iter = { VALUE_INTEGER, { 6 } };
37
38 #ifdef DEBUG
39 const char *type_name_table[] = {
40 "NULL",
41 "INTEGER",
42 "BOOLEAN",
43 "PROCESS",
44 "LABEL",
45 "???5???",
46 "???6???",
47 "???7???",
48 "???8???",
49 "SYMBOL",
50 "TUPLE"
51 };
52 #endif
53
54 /*
55 * List of structured values; used for sweep phase of GC.
56 */
57 static struct structured_value *sv_head = NULL;
58
59 /*** unstructured values ***/
60
61 void
62 value_integer_set(struct value *v, int i)
63 {
64 v->type = VALUE_INTEGER;
65 v->value.integer = i;
66 }
67
68 void
69 value_boolean_set(struct value *v, int b)
70 {
71 v->type = VALUE_BOOLEAN;
72 v->value.boolean = b;
73 }
74
75 void
76 value_process_set(struct value *v, struct process *p)
77 {
78 v->type = VALUE_PROCESS;
79 v->value.process = p;
80 }
81
82 void
83 value_label_set(struct value *v, clabel l)
84 {
85 v->type = VALUE_LABEL;
86 v->value.label = l;
87 }
88
89 /*** structured values ***/
90
91 /*
92 * Initialize a structured value by link it up into the
93 * garbage-collection list.
94 */
95 static void
96 structured_value_init(struct structured_value *sv)
97 {
98 sv->admin = 0;
99 sv->next = sv_head;
100 sv_head = sv;
101 }
102
103 /***** symbols *****/
104
105 struct symbol {
106 struct structured_value sv;
107 unsigned int length; /* number of characters in symbol */
108 /* char token[]; */ /* lexeme of this symbol */
109 };
110
111 int
112 value_symbol_new(struct value *v, const char *token, unsigned int len)
113 {
114 char *buffer;
115
116 assert(token != NULL);
117
118 buffer = value_symbol_new_buffer(v, len);
119 if (buffer == NULL)
120 return 0;
121 strncpy(buffer, token, len);
122
123 return 1;
124 }
125
126 char *
127 value_symbol_new_buffer(struct value *v, unsigned int len)
128 {
129 struct symbol *sym;
130
131 assert(v != NULL);
132
133 sym = malloc(sizeof(struct symbol) + len + 1);
134 if (sym == NULL)
135 return NULL;
136 sym->length = len;
137 ((char *)(sym + 1))[len] = '\0';
138
139 v->type = VALUE_SYMBOL;
140 v->value.structured = (struct structured_value *)sym;
141 structured_value_init((struct structured_value *)sym);
142
143 return (char *)(sym + 1);
144 }
145
146
147 /*
148 * Returned string is NUL-terminated.
149 */
150 const char *
151 value_symbol_get_token(const struct value *v)
152 {
153 struct symbol *sym;
154
155 assert(v->type == VALUE_SYMBOL);
156 assert(v->value.structured != NULL);
157 sym = (struct symbol *)v->value.structured;
158 return ((const char *)(sym + 1));
159 }
160
161 unsigned int
162 value_symbol_get_length(const struct value *v)
163 {
164 struct symbol *sym;
165
166 assert(v->type == VALUE_SYMBOL);
167 assert(v->value.structured != NULL);
168 sym = (struct symbol *)v->value.structured;
169 return sym->length;
170 }
171
172 /***** tuples *****/
173
174 struct tuple {
175 struct structured_value sv;
176 struct value tag;
177 unsigned int size; /* in # of values contained */
178 /* struct value vector[]; */
179 };
180
181 int
182 value_tuple_new(struct value *v, struct value *tag, unsigned int size)
183 {
184 struct tuple *tuple;
185
186 unsigned int bytes = sizeof(struct tuple) +
187 sizeof(struct value) * size;
188
189 if ((tuple = malloc(bytes)) == NULL)
190 return 0;
191
192 memset(tuple, 0, bytes);
193 value_copy(&tuple->tag, tag);
194 tuple->size = size;
195
196 v->type = VALUE_TUPLE;
197 v->value.structured = (struct structured_value *)tuple;
198 structured_value_init((struct structured_value *)tuple);
199
200 return 1;
201 }
202
203 int
204 value_is_tuple(const struct value *v)
205 {
206 return v->type == VALUE_TUPLE;
207 }
208
209 static struct tuple *
210 value_get_tuple(const struct value *v)
211 {
212 assert(value_is_tuple(v));
213 assert(v->value.structured != NULL);
214 return (struct tuple *)v->value.structured;
215 }
216
217 struct value *
218 value_tuple_get_tag(const struct value *v)
219 {
220 struct tuple *t = value_get_tuple(v);
221 return &t->tag;
222 }
223
224 unsigned int
225 value_tuple_get_size(const struct value *v)
226 {
227 struct tuple *t = value_get_tuple(v);
228 return t->size;
229 }
230
231 struct value *
232 value_tuple_fetch(const struct value *v, unsigned int at)
233 {
234 struct tuple *t = value_get_tuple(v);
235 assert(at < t->size);
236 return (struct value *)(t + 1) + at;
237 }
238
239 void
240 value_tuple_store(struct value *v, unsigned int at, const struct value *src)
241 {
242 struct value *dst = value_tuple_fetch(v, at);
243
244 value_copy(dst, src);
245 }
246
247 int
248 value_tuple_fetch_integer(const struct value *v, unsigned int at)
249 {
250 struct tuple *t = value_get_tuple(v);
251 assert(at < t->size);
252 assert(((struct value *)(t + 1) + at)->type == VALUE_INTEGER);
253 return ((struct value *)(t + 1) + at)->value.integer;
254 }
255
256 clabel
257 value_tuple_fetch_label(const struct value *v, unsigned int at)
258 {
259 struct tuple *t = value_get_tuple(v);
260 assert(at < t->size);
261 assert(((struct value *)(t + 1) + at)->type == VALUE_LABEL);
262 return ((struct value *)(t + 1) + at)->value.label;
263 }
264
265 /*
266 * Convenience method to directly store an integer in a tuple.
267 */
268 void
269 value_tuple_store_integer(struct value *v, unsigned int at, int src)
270 {
271 struct value *dst = value_tuple_fetch(v, at);
272
273 value_integer_set(dst, src);
274 }
275
276 /*** ACCESSORS ***/
277
278 /* Unstructured values */
279
280 int
281 value_get_integer(const struct value *v)
282 {
283 assert(v->type == VALUE_INTEGER);
284 return v->value.integer;
285 }
286
287 int
288 value_is_integer(const struct value *v)
289 {
290 return v->type == VALUE_INTEGER;
291 }
292
293 int
294 value_get_boolean(const struct value *v)
295 {
296 assert(v->type == VALUE_BOOLEAN);
297 return v->value.boolean;
298 }
299
300 struct process *
301 value_get_process(const struct value *v)
302 {
303 assert(v->type == VALUE_PROCESS);
304 return v->value.process;
305 }
306
307 clabel
308 value_get_label(const struct value *v)
309 {
310 assert(v->type == VALUE_LABEL);
311 return v->value.label;
312 }
313
314 PTR_INT
315 value_get_unique_id(const struct value *v)
316 {
317 assert(v->type & VALUE_STRUCTURED);
318 return (PTR_INT)v->value.structured;
319 }
320
321
322 /* Tuples as activation records */
323
324 int
325 value_ar_new(struct value *v, unsigned int size,
326 struct value *caller, struct value *enclosing, unsigned int pc)
327 {
328 if (!value_tuple_new(v, &tag_ar, size + AR_HEADER_SIZE))
329 return 0;
330
331 value_tuple_store(v, AR_CALLER, caller);
332 value_tuple_store(v, AR_ENCLOSING, enclosing);
333 value_tuple_store_integer(v, AR_PC, pc);
334 value_tuple_store_integer(v, AR_TOP, AR_HEADER_SIZE);
335
336 return 1;
337 }
338
339 struct value *
340 value_ar_pop(struct value *ar)
341 {
342 int idx;
343 struct value *v;
344
345 idx = value_tuple_fetch_integer(ar, AR_TOP);
346 v = value_tuple_fetch(ar, --idx);
347 value_tuple_store_integer(ar, AR_TOP, idx);
348
349 return v;
350 }
351
352 void
353 value_ar_push(struct value *ar, struct value *v)
354 {
355 int idx;
356
357 assert(value_is_tuple(ar));
358 idx = value_tuple_fetch_integer(ar, AR_TOP);
359 value_tuple_store(ar, idx++, v);
360 value_tuple_store_integer(ar, AR_TOP, idx);
361 }
362
363 void
364 value_ar_xfer(struct value *from, struct value *to, int count)
365 {
366 int i;
367 int from_top = value_tuple_fetch_integer(from, AR_TOP);
368 int to_top = value_tuple_fetch_integer(to, AR_TOP);
369
370 for (i = 0; i < count; i++) {
371 value_tuple_store(to, to_top + i,
372 value_tuple_fetch(from, (from_top - count) + i));
373 }
374
375 value_tuple_store_integer(to, AR_TOP, to_top + count);
376 value_tuple_store_integer(from, AR_TOP, from_top - count);
377 }
378
379 /***** tuples as dictionaries *****/
380
381 /*
382 * Dictionaries are implemented with "layered" hash tables.
383 * A layered hash table works like so:
384 *
385 * Instead of N chains of buckets, there are arrays of size N, called
386 * 'layers'. Each layer is chained together. This is wasteful in
387 * space when there are only a few entries AND they hash-collide, since
388 * there may be one (or rarely, more) layers which are mostly empty.
389 * BUT, this is possibly not as bad as it sounds:
390 *
391 * - When there are fewer than N entries, and there are no collisions,
392 * space usage is less than the chained version - because the chained
393 * version needs an array of N bucket-heads which is the same size as
394 * a layer - while it also needs buckets.
395 * - For M * N entries with no collisions, there are M layers allocated,
396 * and only 2 * M pointers; in the chained version, there are M * N
397 * (next-bucket) pointers allocated.
398 * - Allocations for large items (layers) happen less often than
399 * allocations for small items (buckets,) so this may be an advantage
400 * if malloc() is on the slow side.
401 */
402
403 /*
404 * Each layer of a dictionary is represented by a tuple with the
405 * following components:
406 *
407 * <usage-count, next-layer, ...>
408 *
409 * where ... represents the actual entries.
410 */
411
412 #define LAYER_USAGE 0
413 #define LAYER_NEXT 1
414 #define LAYER_HEADER_SIZE 2
415
416 /*
417 * Compute the hash value of the given value.
418 */
419 static unsigned int
420 value_hash(const struct value *v)
421 {
422 switch (v->type) {
423 case VALUE_NULL:
424 return 0;
425 case VALUE_INTEGER:
426 return (unsigned int)v->value.integer;
427 case VALUE_BOOLEAN:
428 return (unsigned int)v->value.boolean;
429 case VALUE_PROCESS:
430 /* XXX not stable between load/save */
431 return (PTR_INT)v->value.process;
432 case VALUE_LABEL:
433 /* XXX not stable between load/save */
434 return (PTR_INT)v->value.label;
435 case VALUE_SYMBOL:
436 {
437 unsigned int i, hash_val = 0, len = value_symbol_get_length(v);
438 const char *str = value_symbol_get_token(v);
439
440 for (i = 0; i < len; i++) {
441 hash_val += str[i] << (i & 0xf);
442 }
443
444 return hash_val;
445 }
446 case VALUE_TUPLE:
447 /* XXX not stable between load/save */
448 return (PTR_INT)v->value.structured;
449 }
450 /* should never be reached */
451 assert(v->type == VALUE_NULL);
452 return 0;
453 }
454
455 int
456 value_dict_new(struct value *table, unsigned int layer_size)
457 {
458 if (!value_tuple_new(table, &tag_dict,
459 layer_size + LAYER_HEADER_SIZE)) {
460 return 0;
461 }
462
463 value_tuple_store_integer(table, LAYER_USAGE, 0);
464 value_tuple_store(table, LAYER_NEXT, &VNULL);
465
466 return 1;
467 }
468
469 static struct value *
470 value_fetch_hash(const struct value *dict, const struct value *key,
471 unsigned int (*hash_fn)(const struct value *))
472 {
473 const struct value *layer;
474 unsigned int layer_size;
475 unsigned int slot;
476
477 layer = dict;
478 layer_size = value_tuple_get_size(layer) - LAYER_HEADER_SIZE;
479 slot = (hash_fn(key) % (layer_size >> 1)) << 1;
480 slot += LAYER_HEADER_SIZE; /* skip over administrivia in tuple */
481
482 while (!value_is_null(layer)) {
483 if (value_equal(key, value_tuple_fetch(layer, slot)))
484 return value_tuple_fetch(layer, slot + 1);
485 layer = value_tuple_fetch(layer, LAYER_NEXT);
486 assert(value_is_null(layer) || value_is_tuple(layer));
487 }
488
489 return &VNULL;
490 }
491
492 struct value *
493 value_dict_fetch(const struct value *dict, const struct value *key)
494 {
495 assert(value_is_tuple(dict));
496 return value_fetch_hash(dict, key, value_hash);
497 }
498
499 static void
500 value_store_hash(struct value *dict, struct value *key, struct value *value,
501 unsigned int (*hash_fn)(const struct value *))
502 {
503 struct value *layer = dict;
504 struct value *prev_layer = &VNULL;
505 struct value *next_layer = &VNULL;
506 struct value new_layer;
507 unsigned int layer_size = value_tuple_get_size(layer) - LAYER_HEADER_SIZE;
508 unsigned int slot = (hash_fn(key) % (layer_size >> 1)) << 1;
509 int usage;
510 int delta = 0;
511
512 slot += LAYER_HEADER_SIZE; /* skip over administrivia in tuple */
513
514 for (;;) {
515 struct value *v = value_tuple_fetch(layer, slot);
516 if (value_is_null(v)) {
517 /*
518 * Okay to insert into empty slot, so
519 * just break out of the loop.
520 */
521 if (!value_is_null(value)) {
522 delta = 1; /* insert */
523 }
524 break;
525 }
526 if (value_equal(key, v)) {
527 /*
528 * Okay to overwrite, so
529 * just break out of the loop.
530 */
531 if (value_is_null(value)) {
532 delta = -1; /* delete */
533 }
534 break;
535 }
536 prev_layer = layer;
537 next_layer = value_tuple_fetch(layer, LAYER_NEXT);
538 assert(value_is_null(layer) || value_is_tuple(layer));
539 if (!value_is_null(next_layer)) {
540 layer = next_layer;
541 /* and try again */
542 continue;
543 } else {
544 value_dict_new(&new_layer, layer_size);
545 assert(value_is_tuple(&new_layer));
546 value_tuple_store(layer, LAYER_NEXT, &new_layer);
547 layer = &new_layer;
548 delta = 1; /* insert */
549 break;
550 }
551 }
552
553 value_tuple_store(layer, slot, key);
554 value_tuple_store(layer, slot + 1, value);
555
556 if (delta != 0) {
557 usage = value_tuple_fetch_integer(layer, LAYER_USAGE);
558 usage += delta;
559 assert (usage >= 0);
560 if (usage == 0) {
561 /*
562 * This layer is now empty. Unlink it,
563 * and the garbage collector will delete it.
564 */
565 if (!value_is_null(prev_layer)) {
566 value_tuple_store(prev_layer, LAYER_NEXT,
567 value_tuple_fetch(layer, LAYER_NEXT));
568 }
569 } else {
570 value_tuple_store_integer(layer, LAYER_USAGE, usage);
571 }
572 }
573 }
574
575 void
576 value_dict_store(struct value *dict, struct value *key, struct value *value)
577 {
578 assert(value_is_tuple(dict));
579 value_store_hash(dict, key, value, value_hash);
580 }
581
582 unsigned int
583 value_dict_get_length(const struct value *dict)
584 {
585 const struct value *layer = dict;
586 unsigned int size = 0;
587
588 while (!value_is_null(layer)) {
589 size += value_tuple_fetch_integer(layer, LAYER_USAGE);
590 layer = value_tuple_fetch(layer, LAYER_NEXT);
591 }
592
593 return size;
594 }
595
596 unsigned int
597 value_dict_get_layer_size(const struct value *dict)
598 {
599 return value_tuple_get_size(dict) - LAYER_HEADER_SIZE;
600 }
601
602 #define DICT_ITER_LAYER 0
603 #define DICT_ITER_POS 1
604
605 int
606 value_dict_new_iter(struct value *v, struct value *dict)
607 {
608 if (!value_tuple_new(v, &tag_iter, 2))
609 return 0;
610
611 value_tuple_store(v, DICT_ITER_LAYER, dict);
612 value_tuple_store_integer(v, DICT_ITER_POS, LAYER_HEADER_SIZE);
613
614 return 1;
615 }
616
617 /* returns VNULL if there are no (more) entries in the dict. */
618 struct value *
619 value_dict_iter_get_current_key(struct value *dict_iter)
620 {
621 struct value *layer = value_tuple_fetch(dict_iter, DICT_ITER_LAYER);
622 unsigned int pos = value_tuple_fetch_integer(dict_iter, DICT_ITER_POS);
623 unsigned int layer_size = value_tuple_get_size(layer);
624 struct value *key;
625
626 if (pos >= layer_size) {
627 layer = value_tuple_fetch(layer, LAYER_NEXT);
628 if (value_is_null(layer)) {
629 return &VNULL;
630 }
631 pos = LAYER_HEADER_SIZE;
632 }
633
634 key = value_tuple_fetch(layer, pos);
635 while (value_is_null(key)) {
636 pos += 2;
637 if (pos >= layer_size) {
638 layer = value_tuple_fetch(layer, LAYER_NEXT);
639 if (value_is_null(layer)) {
640 return &VNULL;
641 }
642 pos = LAYER_HEADER_SIZE;
643 }
644 key = value_tuple_fetch(layer, pos);
645 }
646
647 value_tuple_store(dict_iter, DICT_ITER_LAYER, layer);
648 value_tuple_store_integer(dict_iter, DICT_ITER_POS, pos);
649
650 return key;
651 }
652
653 void
654 value_dict_iter_advance(struct value *dict_iter)
655 {
656 unsigned int pos = value_tuple_fetch_integer(dict_iter, DICT_ITER_POS);
657
658 value_tuple_store_integer(dict_iter, DICT_ITER_POS, pos + 2);
659 }
660
661 /***** tuples as virtual machines *****/
662
663 int
664 value_vm_new(struct value *vm, struct value *code_tuple)
665 {
666 if (!value_tuple_new(vm, &tag_vm, VM_SIZE))
667 return 0;
668
669 value_tuple_store(vm, VM_CODE, code_tuple);
670 value_tuple_store(vm, VM_IS_DIRECT, &VFALSE);
671 value_vm_reset(vm);
672
673 return 1;
674 }
675
676 void
677 value_vm_reset(struct value *vm)
678 {
679 value_tuple_store_integer(vm, VM_PC, 0);
680 value_tuple_store(vm, VM_AR, &VNULL);
681 }
682
683 /*** GENERAL OPERATIONS ***/
684
685 void
686 value_copy(struct value *dst, const struct value *src)
687 {
688 dst->type = src->type;
689 dst->value = src->value;
690 }
691
692 int
693 value_is_null(const struct value *v)
694 {
695 return v->type == VALUE_NULL;
696 }
697
698 static enum comparison
699 value_compare_nodups(const struct value *a, const struct value *b, struct value *seen)
700 {
701 unsigned int i;
702 struct value hidden_a, hidden_b;
703
704 if (a->type != b->type)
705 return CMP_INCOMPARABLE;
706
707 switch (a->type) {
708 case VALUE_NULL:
709 return CMP_EQ;
710 case VALUE_INTEGER:
711 if (a->value.integer > b->value.integer) {
712 return CMP_GT;
713 }
714 if (a->value.integer < b->value.integer) {
715 return CMP_LT;
716 }
717 return CMP_EQ;
718 case VALUE_BOOLEAN:
719 if (a->value.boolean == b->value.boolean) {
720 return CMP_EQ;
721 }
722 return CMP_INCOMPARABLE;
723 case VALUE_PROCESS:
724 if (a->value.process == b->value.process) {
725 return CMP_EQ;
726 }
727 return CMP_INCOMPARABLE;
728 case VALUE_LABEL:
729 if (a->value.label == b->value.label) {
730 return CMP_EQ;
731 }
732 return CMP_INCOMPARABLE;
733 case VALUE_SYMBOL:
734 {
735 int k = strcmp(value_symbol_get_token(a),
736 value_symbol_get_token(b));
737 if (k > 0) {
738 return CMP_GT;
739 }
740 if (k < 0) {
741 return CMP_LT;
742 }
743 return CMP_EQ;
744 }
745 case VALUE_TUPLE:
746 /*
747 * First, to handle the case where tuples contain
748 * cyclic links, check to see if these are, in fact,
749 * the same tuple.
750 */
751 if ((struct tuple *)(a->value.structured) ==
752 (struct tuple *)(b->value.structured))
753 return CMP_EQ;
754
755 /*
756 * XXX I would almost think that if the tags are not
757 * the same, then the tuples are incomparable...
758 */
759 switch (value_compare(value_tuple_get_tag(a), value_tuple_get_tag(b))) {
760 case CMP_GT:
761 return CMP_GT;
762 case CMP_LT:
763 return CMP_LT;
764 case CMP_INCOMPARABLE:
765 return CMP_INCOMPARABLE;
766 case CMP_EQ:
767 break;
768 }
769
770 if (((struct tuple *)(a->value.structured))->size >
771 ((struct tuple *)(b->value.structured))->size) {
772 return CMP_GT;
773 }
774 if (((struct tuple *)(a->value.structured))->size <
775 ((struct tuple *)(b->value.structured))->size) {
776 return CMP_LT;
777 }
778
779 /*
780 * This more prudent check keeps in mind that we are
781 * recursively testing for equality, and stores
782 * tuples in a 'have seen' dictionary, later recursing
783 * into them only if they have not yet been seen.
784 *
785 * Note, however, that because value_dict_store() internally
786 * uses value_equal(), we can get into a different kind
787 * of inifnite cycle - mutual recursion. To avoid that,
788 * we store encoded values based on the tuples, instead
789 * of the tuples themselves, in the 'have seen' dict.
790 */
791 value_process_set(&hidden_a, (struct process *)value_get_unique_id(a));
792 value_process_set(&hidden_b, (struct process *)value_get_unique_id(b));
793 value_dict_store(seen, &hidden_a, &VTRUE);
794 value_dict_store(seen, &hidden_b, &VTRUE);
795
796 for (i = 0; i < ((struct tuple *)(a->value.structured))->size; i++) {
797 struct value *na = value_tuple_fetch(a, i);
798 struct value *nb = value_tuple_fetch(b, i);
799 enum comparison c;
800
801 if ((!value_is_null(value_dict_fetch(seen, na))) ||
802 (!value_is_null(value_dict_fetch(seen, nb))))
803 return CMP_INCOMPARABLE; /* XXX ? */
804
805 c = value_compare(na, nb);
806 if (c != CMP_EQ)
807 return c;
808 }
809 return CMP_EQ;
810 }
811 /* should never be reached */
812 assert(a->type == VALUE_NULL);
813 return 0;
814 }
815
816 enum comparison
817 value_compare(const struct value *a, const struct value *b)
818 {
819 struct value d;
820 value_dict_new(&d, 31);
821 return value_compare_nodups(a, b, &d);
822 }
823
824 int
825 value_equal(const struct value *a, const struct value *b)
826 {
827 return value_compare(a, b) == CMP_EQ;
828 }
829
830 /***** gc *****/
831
832 /*
833 * Garbage collector. Not a cheesy little reference counter, but
834 * a real meat-and-potatoes mark-and-sweep.
835 *
836 * This is not particularly sophisticated; I'm more concerned with
837 * correctness than performance here.
838 */
839
840 static void
841 mark_tuple(struct value *v)
842 {
843 struct value *k;
844 unsigned int i;
845
846 assert(value_is_tuple(v));
847 for (i = 0; i < value_tuple_get_size(v); i++) {
848 k = value_tuple_fetch(v, i);
849 /*
850 * If the contained value is also structured,
851 * and it hasn't been marked yet, mark it too.
852 */
853 if (k->type & VALUE_STRUCTURED) {
854 struct structured_value *sv = k->value.structured;
855 if (k->type == VALUE_TUPLE &&
856 (!(sv->admin & ADMIN_MARKED))) {
857 /*
858 * It can contain other values and
859 * it hasn't been marked yet, so
860 * recursively mark its contents.
861 */
862 mark_tuple(k);
863 } else {
864 sv->admin |= ADMIN_MARKED;
865 }
866 }
867 }
868 }
869
870 /*
871 * Public interface to garbage collector.
872 */
873
874 void
875 value_gc(struct value *root)
876 {
877 struct structured_value *sv, *sv_next, *temp_sv_head = NULL;
878
879 /*
880 * Mark...
881 */
882 mark_tuple(root);
883
884 /*
885 * ...and sweep
886 */
887 for (sv = sv_head; sv != NULL; sv = sv_next) {
888 sv_next = sv->next;
889 if (sv->admin & ADMIN_MARKED) {
890 sv->admin &= ~ADMIN_MARKED;
891 sv->next = temp_sv_head;
892 temp_sv_head = sv;
893 } else {
894 /*
895 * Found an unreachable SV!
896 * Not much special knowledge is required to
897 * free a structured value block, so we just
898 * (un-abstractedly) inline the process here.
899 */
900 free(sv);
901 }
902 }
903
904 sv_head = temp_sv_head;
905 }
0 /*
1 * value.h
2 * Values.
3 */
4
5 #ifndef __VALUE_H_
6 #define __VALUE_H_
7
8 #include "localtypes.h"
9
10 /*
11 * Types of values.
12 */
13 #define VALUE_STRUCTURED 8
14 enum value_type {
15 VALUE_NULL = 0,
16 VALUE_INTEGER = 1,
17 VALUE_BOOLEAN = 2,
18 VALUE_PROCESS = 3,
19 VALUE_LABEL = 4,
20
21 VALUE_SYMBOL = (VALUE_STRUCTURED | 1),
22 VALUE_TUPLE = (VALUE_STRUCTURED | 2)
23 };
24
25 #ifdef DEBUG
26 extern const char *type_name_table[];
27 #endif
28
29 typedef void * clabel;
30
31 struct process;
32
33 /*
34 * Simple values.
35 * These exist directly on the stack, and are not garbage-collected.
36 */
37 struct value {
38 enum value_type type; /* VALUE_ */
39 union {
40 int integer;
41 int boolean;
42 struct process *process;
43 clabel label;
44 struct structured_value *structured;
45 } value;
46 };
47
48 extern struct value VNULL;
49 extern struct value VFALSE;
50 extern struct value VTRUE;
51
52 extern struct value tag_ar;
53 extern struct value tag_dict;
54 extern struct value tag_list;
55 extern struct value tag_vm;
56
57 /*
58 * Describe how two values compare.
59 */
60 enum comparison {
61 CMP_EQ,
62 CMP_LT,
63 CMP_GT,
64 CMP_INCOMPARABLE
65 };
66
67 /* Prototypes */
68
69 /*
70 * 'struct value *' parameters may NEVER be NULL.
71 *
72 * Only functions whose names end in _new allocate a struct value.
73 */
74
75 /*
76 * General functions.
77 */
78 void value_copy(struct value *, const struct value *);
79
80 int value_is_null(const struct value *);
81 int value_is_integer(const struct value *);
82 int value_is_tuple(const struct value *);
83
84 int value_equal(const struct value *, const struct value *);
85 enum comparison value_compare(const struct value *, const struct value *);
86
87 /* public interface to garbage collector */
88 void value_gc(struct value *);
89
90 /*
91 * Unstructured values.
92 */
93
94 void value_integer_set(struct value *, int);
95 void value_boolean_set(struct value *, int);
96 void value_process_set(struct value *, struct process *);
97 void value_label_set(struct value *, clabel);
98
99 int value_get_integer(const struct value *);
100 int value_get_boolean(const struct value *);
101 clabel value_get_label(const struct value *);
102 struct process *value_get_process(const struct value *);
103
104 /*
105 * Structured values.
106 */
107
108 /*
109 * Retrieve an integer code that uniquely identifies this value.
110 * Intended for debug output only.
111 * Precondition: value is a structured value.
112 */
113 PTR_INT value_get_unique_id(const struct value *);
114
115 /*
116 * Symbols.
117 */
118
119 /*
120 * Allocate a new symbol value, with the given character string
121 * (with the given length) as its token, and set the given value to it.
122 * Returns true upon success, false if memory could not be allocated.
123 * Precondition: value is not null, and the token is not null.
124 */
125 int value_symbol_new(struct value *, const char *, unsigned int);
126
127 /*
128 * Allocate a new symbol value with the given length and set the
129 * given value to it. Return a pointer to the start of the character
130 * data for the symbol, for later population by the caller.
131 * Returns null if memory could not be allocated.
132 * Precondition: value is not null, and the token is not null.
133 */
134 char *value_symbol_new_buffer(struct value *, unsigned int);
135 const char *value_symbol_get_token(const struct value *);
136 unsigned int value_symbol_get_length(const struct value *);
137
138 /*
139 * Tuples.
140 */
141
142 int value_tuple_new(struct value *, struct value *, unsigned int);
143 struct value *value_tuple_get_tag(const struct value *);
144 unsigned int value_tuple_get_size(const struct value *);
145 struct value *value_tuple_fetch(const struct value *, unsigned int);
146 void value_tuple_store(struct value *, unsigned int, const struct value *);
147 int value_tuple_fetch_integer(const struct value *, unsigned int);
148 void value_tuple_store_integer(struct value *, unsigned int, int);
149 clabel value_tuple_fetch_label(const struct value *, unsigned int);
150
151 /*
152 * Dictionaries.
153 * Dictionaries are represented by a linked list of "layers", where
154 * each layer is a tuple of the given size plus some header slots.
155 */
156
157 int value_dict_new(struct value *, unsigned int);
158 struct value *value_dict_fetch(const struct value *, const struct value *);
159 void value_dict_store(struct value *, struct value *, struct value *);
160 unsigned int value_dict_get_length(const struct value *);
161 unsigned int value_dict_get_layer_size(const struct value *);
162
163 /*
164 * Dictionary iterators.
165 */
166
167 int value_dict_new_iter(struct value *, struct value *);
168 struct value *value_dict_iter_get_current_key(struct value *);
169 void value_dict_iter_advance(struct value *);
170
171 /*
172 * Activation records.
173 * Activation records are represented by tuple with 4 header entries.
174 * The remainder of the entries are local variables stored in the AR.
175 */
176
177 #define AR_CALLER 0 /* ar: the ar that called us */
178 #define AR_ENCLOSING 1 /* ar: the ar lexically outside us */
179 #define AR_PC 2 /* integer: code pos when resumed */
180 #define AR_TOP 3 /* integer: current top of stack (init. 4) */
181
182 #define AR_HEADER_SIZE 4
183
184 int value_ar_new(struct value *, unsigned int, struct value *, struct value *, unsigned int);
185 struct value *value_ar_pop(struct value *);
186 void value_ar_push(struct value *, struct value *);
187 void value_ar_xfer(struct value *, struct value *, int);
188
189 /*
190 * Virtual machines.
191 * A virtual machine is represented by a tuple with 4 entries:
192 * - the first is an integer offset: the program counter
193 * - the second is a tuple representing the current activation record
194 * - the third is a boolean: is this code direct-threaded or not?
195 * - the fourth is a tuple containing the code as VM instructions
196 */
197
198 #define VM_PC 0
199 #define VM_AR 1
200 #define VM_IS_DIRECT 2
201 #define VM_CODE 3
202
203 #define VM_SIZE 4
204
205 int value_vm_new(struct value *, struct value *);
206 void value_vm_reset(struct value *);
207
208 #endif /* !__VALUE_H_ */
209
0 /*
1 * vm.c
2 * Virtual machine.
3 */
4
5 #include "lib.h"
6 #include "process.h"
7 #include "stream.h"
8 #include "file.h"
9 #include "vmproc.h"
10
11 #include "vm.h"
12 #include "value.h"
13 #include "portray.h"
14 #include "save.h"
15
16 #include "instrenum.h"
17
18 #ifdef DEBUG
19 #include "cmdline.h" /* for process_err */
20 #include "render.h"
21 #define VM_DEBUG(x) process_render(process_err, "EXEC: %s\n", # x);
22 #define VM_DEBUG_PC() process_render(process_err, "VM PC: %04d --> ", pc);
23 #define VM_DUMP_AR() \
24 if (value_is_null(&ar)) { \
25 process_render(process_err, "(NO AR) "); \
26 } else { \
27 process_render(process_err, "AR: "); \
28 value_portray(process_err, &ar); \
29 process_render(process_err, " "); \
30 }
31 #else
32 #define VM_DEBUG(x)
33 #define VM_DEBUG_PC()
34 #define VM_DUMP_AR()
35 #endif
36
37 #ifdef DIRECT_THREADING
38
39 #include "instrtab.h"
40
41 #define VM_TOP() TOP:
42 #define VM_BEGIN_DISPATCH() goto *value_tuple_fetch_label(code, pc);
43 #define VM_END_DISPATCH()
44 #define VM_OPLAB(x) LABEL_ ## x: VM_DEBUG(x)
45 #define VM_NEXT() goto TOP;
46 #define VM_STOP() cycles = 1; goto TOP;
47
48 #else
49
50 #define VM_TOP()
51 #define VM_BEGIN_DISPATCH() switch (value_tuple_fetch_integer(code, pc)) {
52 #define VM_END_DISPATCH() }
53 #define VM_OPLAB(x) case x: VM_DEBUG(x)
54 #define VM_NEXT() break;
55 #define VM_STOP() cycles = 1; break;
56
57 #endif
58
59 #define POP_VALUE() value_ar_pop(&ar)
60 #define PUSH_VALUE(v) value_ar_push(&ar, v)
61
62 #define GET_VALUE(i) value_ar_push(&ar, \
63 value_tuple_fetch(&ar, \
64 i + AR_HEADER_SIZE))
65 #define SET_VALUE(i) value_tuple_store(&ar, \
66 i + AR_HEADER_SIZE, \
67 value_ar_pop(&ar))
68
69 #define XFER_VALUES(from, to, count) value_ar_xfer(from, to, count)
70
71 #define IMM_VAL() (value_tuple_fetch(code, pc))
72 #define IMM_INT() (value_tuple_fetch_integer(code, pc))
73 #define IMM_ADDR() (value_tuple_fetch_integer(code, pc))
74
75 void
76 vm_run(struct value *vm, struct process *self, unsigned int cycles)
77 {
78 struct value t1; /* temporary */
79
80 struct value *a; /* register, generally used for 1st argument */
81 struct value *b; /* register, generally used for 2nd argument */
82 struct value *v; /* register, generally used for result */
83
84 struct value ar; /* contains currently active activation record */
85 struct value *code; /* tuple containing VM instructions */
86
87 unsigned int pc; /* pointer into code to currently exec instr */
88
89 #ifdef DIRECT_THREADING
90 #include "instrlab.h"
91
92 if (!value_get_boolean(value_tuple_fetch(vm, VM_IS_DIRECT))) {
93 struct opcode_entry *oe;
94 enum opcode opcode;
95
96 /* convert opcodes to labels */
97 pc = 0;
98 code = value_tuple_fetch(vm, VM_CODE);
99 a = value_tuple_fetch(code, pc);
100 opcode = (enum opcode)value_get_integer(a);
101 while (opcode != INSTR_EOF) {
102 value_label_set(a, instr_label[value_get_integer(a)]);
103 #ifdef DEBUG
104 process_render(process_err, "At %d, replaced %d with ", pc, opcode);
105 value_portray(process_err, value_tuple_fetch(code, pc));
106 process_render(process_err, "\n");
107 #endif
108 pc += opcode_table[opcode].arity;
109 pc++;
110 a = value_tuple_fetch(code, pc);
111 opcode = (enum opcode)value_get_integer(a);
112 }
113 value_tuple_store(vm, VM_IS_DIRECT, &VTRUE);
114 }
115 #endif
116
117 value_copy(&ar, value_tuple_fetch(vm, VM_AR));
118 code = value_tuple_fetch(vm, VM_CODE);
119 pc = value_tuple_fetch_integer(vm, VM_PC);
120 pc--;
121
122 for (;;) {
123 VM_TOP()
124
125 pc++;
126 if (--cycles == 0) break;
127 VM_DEBUG_PC()
128 VM_DUMP_AR()
129
130 VM_BEGIN_DISPATCH()
131
132 /*** STACK MANIPULATION ***/
133
134 /*
135 % PUSH v : -> v
136 * Push the immediate value onto the stack.
137 */
138 VM_OPLAB(INSTR_PUSH)
139 pc++;
140 PUSH_VALUE(IMM_VAL());
141 VM_NEXT()
142
143 /*
144 % POP : v ->
145 * Pop a value off the stack and discard it.
146 */
147 VM_OPLAB(INSTR_POP)
148 POP_VALUE();
149 VM_NEXT()
150
151 /*
152 % GET : i -> v
153 * Push the value of a local variable, given
154 * by the index popped from the stack, onto the
155 * stack. i = 0 indicates the first local,
156 * which is stored at the bottom of the stack.
157 */
158 VM_OPLAB(INSTR_GET)
159 a = POP_VALUE();
160 GET_VALUE(value_get_integer(a));
161 VM_NEXT()
162
163 /*
164 % SET : v i ->
165 * Alter the value of a local variable, given
166 * by the index popped from the stack, to be
167 * the value subsequently popped from the stack.
168 */
169 VM_OPLAB(INSTR_SET)
170 a = POP_VALUE();
171 SET_VALUE(value_get_integer(a));
172 VM_NEXT()
173
174 /*
175 % GETI i : -> v
176 * Push the value of a local variable, given
177 * by the immediate integer index, onto the stack.
178 */
179 VM_OPLAB(INSTR_GETI)
180 pc++;
181 GET_VALUE(IMM_INT());
182 VM_NEXT()
183
184 /*
185 % SETI i : v ->
186 * Alter the value of a local variable, given
187 * by the immediate integer index, to be
188 * the value popped from the stack.
189 */
190 VM_OPLAB(INSTR_SETI)
191 pc++;
192 SET_VALUE(IMM_INT());
193 VM_NEXT()
194
195 /*** TUPLE OPERATIONS ***/
196
197 /*
198 % NEW_TUPLE i : v -> t
199 * Push a new, empty tuple onto the stack.
200 * The immediate integer gives the size.
201 * The value on the stack gives the tuple's tag.
202 */
203 VM_OPLAB(INSTR_NEW_TUPLE)
204 pc++;
205 value_tuple_new(&t1, POP_VALUE(), IMM_INT());
206 PUSH_VALUE(&t1);
207 VM_NEXT()
208
209 /*
210 % FETCH_TUPLE : i t -> v
211 * Pop a tuple value from the stack, then
212 * an integer index value, then push the
213 * value stored at that index in the tuple.
214 */
215 VM_OPLAB(INSTR_FETCH_TUPLE)
216 a = POP_VALUE(); /* tuple */
217 b = POP_VALUE(); /* index */
218 PUSH_VALUE(value_tuple_fetch(a, value_get_integer(b)));
219 VM_NEXT()
220
221 /*
222 % STORE_TUPLE : v i t ->
223 * Pop a tuple value from the stack, then
224 * an integer index value, then a target value;
225 * store the target in the tuple at that index.
226 */
227 VM_OPLAB(INSTR_STORE_TUPLE)
228 a = POP_VALUE(); /* tuple */
229 b = POP_VALUE(); /* index */
230 v = POP_VALUE(); /* value */
231 value_tuple_store(a, value_get_integer(b), v);
232 VM_NEXT()
233
234 /*** DICTIONARY OPERATIONS ***/
235
236 /*
237 % NEW_DICT i : -> d
238 * Push a new, empty dictionary onto the stack.
239 * The immediate integer gives the load factor.
240 */
241 VM_OPLAB(INSTR_NEW_DICT)
242 pc++;
243 value_dict_new(&t1, IMM_INT());
244 PUSH_VALUE(&t1);
245 VM_NEXT()
246
247 /*
248 % FETCH_DICT : k d -> v
249 * Pop a dictionary value from the stack, then
250 * a key value, then push the associated stored
251 * value retrieved from the dictionary onto the
252 * stack.
253 */
254 VM_OPLAB(INSTR_FETCH_DICT)
255 a = POP_VALUE(); /* dictionary */
256 b = POP_VALUE(); /* key */
257 PUSH_VALUE(value_dict_fetch(a, b));
258 VM_NEXT()
259
260 /*
261 % STORE_DICT : v k d ->
262 * Pop a dictionary value from the stack, then
263 * a key value, then a target value; associate
264 * the key with the target in the dictionary.
265 */
266 VM_OPLAB(INSTR_STORE_DICT)
267 a = POP_VALUE(); /* dictionary */
268 b = POP_VALUE(); /* key */
269 v = POP_VALUE(); /* value */
270 value_dict_store(a, b, v);
271 VM_NEXT()
272
273 /*** BOOLEAN OPERATORS ***/
274
275 /*
276 % NOT : b -> b
277 * Pop a boolean, and push Boolean NOT of it.
278 */
279 VM_OPLAB(INSTR_NOT)
280 a = POP_VALUE();
281 value_boolean_set(&t1, !value_get_boolean(a));
282 PUSH_VALUE(&t1);
283 VM_NEXT()
284
285 /*
286 % AND : b b -> b
287 * Pop two booleans, and push Boolean AND of them.
288 */
289 VM_OPLAB(INSTR_AND)
290 b = POP_VALUE();
291 a = POP_VALUE();
292 value_boolean_set(&t1,
293 value_get_boolean(a) && value_get_boolean(b)
294 );
295 PUSH_VALUE(&t1);
296 VM_NEXT()
297
298 /*
299 % OR : b b -> b
300 * Pop two booleans, and push Boolean OR of them.
301 */
302 VM_OPLAB(INSTR_OR)
303 b = POP_VALUE();
304 a = POP_VALUE();
305 value_boolean_set(&t1,
306 value_get_boolean(a) || value_get_boolean(b)
307 );
308 PUSH_VALUE(&t1);
309 VM_NEXT()
310
311 /*** COMPARISON OPERATORS ***/
312
313 /*
314 % EQU : v v -> b
315 * Pop two value, and push a new boolean value;
316 * true if the two values are equal, false if not.
317 */
318 VM_OPLAB(INSTR_EQU)
319 b = POP_VALUE();
320 a = POP_VALUE();
321 value_boolean_set(&t1,
322 value_equal(a, b)
323 );
324 PUSH_VALUE(&t1);
325 VM_NEXT()
326
327 /*
328 % NEQ : v v -> b
329 * Shorthand for EQU NOT.
330 */
331 VM_OPLAB(INSTR_NEQ)
332 b = POP_VALUE();
333 a = POP_VALUE();
334 value_boolean_set(&t1,
335 !value_equal(a, b)
336 );
337 PUSH_VALUE(&t1);
338 VM_NEXT()
339
340 /*** ARITHMETIC OPERATORS ***/
341
342 /*
343 % ADD_INT : i i -> i
344 * Pop two integers, and push their sum as an integer.
345 */
346 VM_OPLAB(INSTR_ADD_INT)
347 b = POP_VALUE();
348 a = POP_VALUE();
349 value_integer_set(&t1,
350 value_get_integer(a) + value_get_integer(b)
351 );
352 PUSH_VALUE(&t1);
353 VM_NEXT()
354
355 /*
356 % MUL_INT : i i -> i
357 * Pop two integers, and push their product as an integer.
358 */
359 VM_OPLAB(INSTR_MUL_INT)
360 b = POP_VALUE();
361 a = POP_VALUE();
362 value_integer_set(&t1,
363 value_get_integer(a) * value_get_integer(b)
364 );
365 PUSH_VALUE(&t1);
366 VM_NEXT()
367
368 /*
369 % SUB_INT : i i -> i
370 * Pop two integers, and push their difference as an integer.
371 * The difference is the second popped minus the first.
372 */
373 VM_OPLAB(INSTR_SUB_INT)
374 b = POP_VALUE();
375 a = POP_VALUE();
376 value_integer_set(&t1,
377 value_get_integer(a) - value_get_integer(b)
378 );
379 PUSH_VALUE(&t1);
380 VM_NEXT()
381
382 /*
383 % DIV_INT : i i -> i
384 * Pop two integers, and push their quotient as an integer.
385 * The quotient is the second popped divided by the first.
386 */
387 VM_OPLAB(INSTR_DIV_INT)
388 b = POP_VALUE();
389 a = POP_VALUE();
390 value_integer_set(&t1,
391 value_get_integer(a) / value_get_integer(b)
392 );
393 PUSH_VALUE(&t1);
394 VM_NEXT()
395
396 /*
397 % MOD_INT : i i -> i
398 * Pop two integers, and push their remainder as an integer.
399 * The remainder is what is left over after dividing the
400 * second value popped by the first value popped.
401 */
402 VM_OPLAB(INSTR_MOD_INT)
403 b = POP_VALUE();
404 a = POP_VALUE();
405 value_integer_set(&t1,
406 value_get_integer(a) % value_get_integer(b)
407 );
408 PUSH_VALUE(&t1);
409 VM_NEXT()
410
411 /*** CONTROL FLOW INSTRUCTIONS ***/
412
413 /*
414 % GOTO a : ->
415 * Unilaterally transfer control to a different
416 * code address. This does not change the
417 * stack or activation record in any way.
418 */
419 VM_OPLAB(INSTR_GOTO)
420 pc++;
421 pc = IMM_ADDR();
422 pc--;
423 VM_NEXT()
424
425 /*
426 % FUN a : i -> f
427 * Create a new "functional value" (initial AR) from
428 * the code address given by the operand, the size of
429 * the activation record that will be required when it is
430 * invoked (given on the stack,) and the current
431 * activation record, (considered to be this fun's
432 * "enclosing" AR.) Push this new fun onto the stack.
433 */
434 VM_OPLAB(INSTR_FUN)
435 pc++;
436 a = POP_VALUE();
437 value_ar_new(&t1, value_get_integer(a),
438 &VNULL, /* not set until called */
439 &ar, IMM_ADDR());
440 PUSH_VALUE(&t1);
441 VM_NEXT()
442
443 /*
444 % NEW_AR i : ->
445 * Create a new activation record and use it for our
446 * execution context (stack etc.)
447 *
448 * This is generally only needed in the global
449 * scope, since INSTR_CALL takes care of creating
450 * AR's for itself.
451 *
452 * This implementation uses the 't1' value register
453 * temporarily to store the new AR.
454 */
455 VM_OPLAB(INSTR_NEW_AR)
456 pc++;
457 value_ar_new(&ar, IMM_INT(), &ar, &VNULL, pc);
458 VM_NEXT()
459
460 /*
461 % CALL i : X v ->
462 * Pop a function-value from the stack and call
463 * it, passing a number of arguments given by
464 * the immediate integer.
465 */
466 VM_OPLAB(INSTR_CALL)
467 /*
468 * XXX should assert that it's an AR here.
469 */
470 v = POP_VALUE();
471 assert(value_is_tuple(v));
472
473 pc++; /* point at operand */
474
475 /*
476 * Save the current program position in
477 * the current activation record.
478 */
479 value_tuple_store_integer(&ar, AR_PC, pc + 1);
480
481 value_tuple_store(v, AR_CALLER, &ar);
482
483 /*
484 * Pass parameters to the new AR.
485 */
486 XFER_VALUES(&ar, v, IMM_INT());
487
488 /*
489 * Set the activation record and program
490 * counter up as our own.
491 */
492 value_copy(&ar, v);
493 pc = value_tuple_fetch_integer(&ar, AR_PC);
494 pc--;
495 VM_NEXT()
496
497 /*
498 % RESUME i : X v ->
499 * Pop an AR from the stack and resume
500 * it, passing a number of arguments given by
501 * the immediate integer.
502 */
503 VM_OPLAB(INSTR_RESUME)
504 /*
505 * XXX should assert that it's right kind of tuple here.
506 */
507 v = POP_VALUE();
508
509 pc++; /* point at operand */
510
511 /*
512 * Save the current program position in
513 * the current activation record.
514 */
515 value_tuple_store_integer(&ar, AR_PC, pc + 1);
516
517 /*
518 * Use the existing AR for this fun.
519 */
520
521 /*
522 * Pass parameters to the new AR.
523 * XXX note we should deal with
524 * resumes more cleanly.
525 */
526 XFER_VALUES(&ar, v, IMM_INT());
527
528 value_copy(&ar, v);
529 pc = value_tuple_fetch_integer(v, AR_PC);
530 pc--;
531 VM_NEXT()
532
533 /*
534 % YIELD i : X ->
535 * Transfer a number of values, given by the
536 * immediate integer, back up to the caller.
537 */
538 VM_OPLAB(INSTR_YIELD)
539 pc++;
540 XFER_VALUES(&ar, value_tuple_fetch(&ar, AR_CALLER),
541 IMM_INT());
542 VM_NEXT()
543
544 /*
545 % RET : ->
546 * Transfer control back to the caller.
547 */
548 VM_OPLAB(INSTR_RET)
549 value_tuple_store_integer(&ar, AR_PC, pc + 1); /* save pc in our ar */
550 value_copy(&ar, value_tuple_fetch(&ar, AR_CALLER)); /* switch ar to caller */
551 pc = value_tuple_fetch_integer(&ar, AR_PC); /* move pc to caller */
552 pc--; /* adjust for advance */
553 VM_NEXT()
554
555 /*
556 % REST : ->
557 * Yield to other processes in the system
558 */
559 VM_OPLAB(INSTR_REST)
560 VM_STOP()
561
562 /*
563 % HALT : ->
564 * Stop this virtual machine.
565 */
566 VM_OPLAB(INSTR_HALT)
567 self->done = 1;
568 VM_STOP()
569
570 /*** CONDITIONAL CONTROL FLOW INSTRUCTIONS ***/
571
572 /*
573 % JEQ a : v v ->
574 * Pop two values from the stack, and if they are
575 * equal, branch to a new location in the program
576 * given by the immediate address.
577 */
578 VM_OPLAB(INSTR_JEQ)
579 b = POP_VALUE();
580 a = POP_VALUE();
581 pc++;
582 if (value_equal(a, b)) {
583 pc = IMM_ADDR();
584 pc--;
585 }
586 VM_NEXT()
587
588 /*
589 % JNE a : v v ->
590 * Pop two values from the stack, and if they are not
591 * equal, branch to a new location in the program
592 * given by the immediate address.
593 */
594 VM_OPLAB(INSTR_JNE)
595 b = POP_VALUE();
596 a = POP_VALUE();
597 pc++;
598 if (!value_equal(a, b)) {
599 pc = IMM_ADDR();
600 pc--;
601 }
602 VM_NEXT()
603
604 /*
605 % JLT a : v v ->
606 * Pop two values from the stack, and if the second is
607 * less than the first, branch to a new location in the
608 * program given by the immediate address.
609 */
610 VM_OPLAB(INSTR_JLT)
611 b = POP_VALUE();
612 a = POP_VALUE();
613 pc++;
614 if (value_compare(a, b) == CMP_LT) {
615 pc = IMM_ADDR();
616 pc--;
617 }
618 VM_NEXT()
619
620 /*
621 % JLE a : v v ->
622 * Pop two values from the stack, and if the second is
623 * less than or equal to the first, branch to a new
624 * location in the program given by the immediate address.
625 */
626 VM_OPLAB(INSTR_JLE)
627 b = POP_VALUE();
628 a = POP_VALUE();
629 pc++;
630 if (value_compare(a, b) != CMP_GT) { /* XXX */
631 pc = IMM_ADDR();
632 pc--;
633 }
634 VM_NEXT()
635
636 /*
637 % JGT a : v v ->
638 * Pop two values from the stack, and if the second is
639 * greater than the first, branch to a new location in
640 * the program given by the immediate address.
641 */
642 VM_OPLAB(INSTR_JGT)
643 b = POP_VALUE();
644 a = POP_VALUE();
645 pc++;
646 if (value_compare(a, b) == CMP_GT) {
647 pc = IMM_ADDR();
648 pc--;
649 }
650 VM_NEXT()
651
652 /*
653 % JGE a : v v ->
654 * Pop two values from the stack, and if the second is
655 * greater than or equal to the first, branch to a new
656 * location in the program given by the immediate address.
657 */
658 VM_OPLAB(INSTR_JGE)
659 b = POP_VALUE();
660 a = POP_VALUE();
661 pc++;
662 if (value_compare(a, b) != CMP_LT) { /* XXX */
663 pc = IMM_ADDR();
664 pc--;
665 }
666 VM_NEXT()
667
668 /*** PROCESSES ***/
669
670 /*
671 % OPEN : n n -> p
672 * Pop a name and a mode off the stack, open a
673 * file process for that name, and push it onto the
674 * stack.
675 */
676 VM_OPLAB(INSTR_OPEN)
677 {
678 struct process *p;
679
680 b = POP_VALUE(); /* mode */
681 a = POP_VALUE(); /* name */
682 p = file_open(value_symbol_get_token(a), value_symbol_get_token(b));
683 value_process_set(&t1, p);
684 PUSH_VALUE(&t1);
685 }
686 VM_NEXT()
687
688 /*
689 % STDOUT : -> p
690 * Push a stream process representing the "standard" output
691 * onto the stack. Note that this is not a good
692 * interface, and should be replaced asap, possibly
693 * by OPEN "*stdout" or something.
694 */
695 VM_OPLAB(INSTR_STDOUT)
696 {
697 struct process *p = file_open("*stdout", "w");
698
699 value_process_set(&t1, p);
700 PUSH_VALUE(&t1);
701 }
702 VM_NEXT()
703
704 /*
705 % CLOSE : p ->
706 * Pop a stream process off the stack and close it.
707 */
708 VM_OPLAB(INSTR_CLOSE)
709 a = POP_VALUE();
710 stream_close(self, value_get_process(a));
711 VM_NEXT()
712
713 /*
714 % SPAWN a : -> p
715 * Create a new VM process based on the current
716 * process and start it in the scheduler. The new
717 * process will share the code from this VM and
718 * will begin executing at the given address, but
719 * will not have any ARs of its own, nor will it have
720 * access to this process's ARs.
721 */
722 VM_OPLAB(INSTR_SPAWN)
723 {
724 struct process *spawned;
725
726 value_vm_new(&t1, value_tuple_fetch(vm, VM_CODE));
727 value_tuple_store(&t1, VM_AR, &VNULL);
728 value_tuple_store(&t1, VM_IS_DIRECT, value_tuple_fetch(vm, VM_IS_DIRECT));
729 pc++;
730 value_tuple_store_integer(&t1, VM_PC, IMM_ADDR());
731
732 spawned = vmproc_new(&t1);
733 /* schedule!! */
734 spawned->next = self->next;
735 self->next = spawned;
736
737 value_process_set(&t1, spawned);
738 PUSH_VALUE(&t1);
739 }
740 VM_NEXT()
741
742 /*** INTER-PROCESS COMMUNICATION ***/
743
744 /*
745 % WRITE : v s ->
746 * Pop a stream and a value from the stack
747 * and write the value in a 'raw' manner into
748 * the stream. The value must be a tuple of
749 * small integers, which are interpreted as bytes.
750 * TODO: could also be a symbol, eh?
751 */
752 VM_OPLAB(INSTR_WRITE)
753 {
754 unsigned int i;
755 unsigned char c;
756
757 a = POP_VALUE();
758 v = POP_VALUE();
759
760 /*
761 * XXX this could certainly be optimized
762 * to use scatter/gather I/O and such
763 */
764 for (i = 0; i < value_tuple_get_size(v); i++) {
765 c = (unsigned char)value_get_integer(
766 value_tuple_fetch(v, i)
767 );
768 stream_write(self, value_get_process(a), &c, 1);
769 }
770 }
771 VM_NEXT()
772
773 /*
774 % SEND : v p ->
775 * Pop a process and a value from the stack
776 * and send the value to the process. The value
777 * will be packaged in such a way that a Kosheri
778 * process on the other end will be able to easily
779 * unpackage it to retrieve an exact copy of the
780 * original value.
781 */
782 VM_OPLAB(INSTR_SEND)
783 a = POP_VALUE();
784 v = POP_VALUE();
785
786 value_save(value_get_process(a), v);
787 VM_NEXT()
788
789 /*
790 % PORTRAY : v s ->
791 * Pop a stream and a value from the stack
792 * and render the value in a human-readable
793 * way, into the stream.
794 */
795 VM_OPLAB(INSTR_PORTRAY)
796 a = POP_VALUE();
797 v = POP_VALUE();
798 value_portray(value_get_process(a), v);
799 VM_NEXT()
800
801 /*** ADMINISTRATIVE ***/
802
803 /*
804 % NOP : ->
805 * Explicitly do nothing. Used for padding.
806 */
807 VM_OPLAB(INSTR_NOP)
808 VM_NEXT()
809
810 /*
811 % EOF : ->
812 * Used to indicate the end of the VM bytecode array
813 * in certain contexts. Should never be executed.
814 */
815 VM_OPLAB(INSTR_EOF)
816 assert(IMM_INT() != INSTR_EOF);
817 VM_NEXT()
818
819 VM_END_DISPATCH()
820 }
821
822 value_tuple_store(vm, VM_AR, &ar);
823 value_tuple_store_integer(vm, VM_PC, pc);
824 }
0 /*
1 * vm.h
2 * Virtual machine structures and prototypes.
3 * $Id: vm.h 100 2006-02-25 02:20:09Z catseye $
4 */
5
6 #ifndef __VM_H_
7 #define __VM_H_
8
9 #include "value.h"
10
11 struct process;
12
13 void vm_run(struct value *, struct process *, unsigned int);
14
15 #endif /* !__VM_H_ */
0 /*
1 * vmproc.c
2 * Virtual machine code backed processes.
3 */
4
5 #include "lib.h"
6
7 #include "vmproc.h"
8
9 #include "process.h"
10 #include "vm.h"
11
12 static void
13 run(struct process *p)
14 {
15 vm_run(&p->aux_value, p, 100);
16 }
17
18 struct process *
19 vmproc_new(struct value *vm)
20 {
21 struct process *p;
22
23 p = process_new();
24 p->run = run;
25 value_copy(&p->aux_value, vm);
26 p->waiting = 0;
27
28 return p;
29 }
0 /*
1 * vmproc.h
2 * Virtual machine code backed processes.
3 */
4
5 #ifndef __VMPROC_H_
6 #define __VMPROC_H_
7
8 struct value;
9 struct process;
10
11 struct process *vmproc_new(struct value *);
12
13 #endif /* !__VMPROC_H_ */
14
0 #!/bin/sh
1
2 cd src
3
4 make clean all >ERRORS 2>&1
5 if [ $? != 0 ]; then
6 cat ERRORS
7 rm -f ERRORS
8 exit 1
9 fi
10 echo "Testing default build..."
11 falderal -b test ../tests/Assembly.markdown ../tests/Term.markdown
12
13 make clean all CFLAGS=-DDIRECT_THREADING >ERRORS 2>&1
14 if [ $? != 0 ]; then
15 cat ERRORS
16 rm -f ERRORS
17 exit 1
18 fi
19 echo "Testing direct threading build..."
20 falderal -b test ../tests/Assembly.markdown ../tests/Term.markdown
21
22 make clean tool >ERRORS 2>&1
23 if [ $? != 0 ]; then
24 cat ERRORS
25 rm -f ERRORS
26 exit 1
27 fi
28 echo "Testing 'tool' build..."
29 falderal -b test ../tests/Assembly.markdown ../tests/Term.markdown
30
31 make clean static >ERRORS 2>&1
32 if [ $? != 0 ]; then
33 cat ERRORS
34 rm -f ERRORS
35 exit 1
36 fi
37 echo "Testing 'static' build..."
38 falderal -b test ../tests/Assembly.markdown ../tests/Term.markdown
39
40 echo "Building 'debug' version..."
41 make clean debug >ERRORS 2>&1
42 if [ $? != 0 ]; then
43 cat ERRORS
44 rm -f ERRORS
45 exit 1
46 fi
47 make clean
48 rm -f ERRORS
0 Kosheri Assembly
1 ================
2
3 -> Functionality "Round-trip Kosheri Assembly" is implemented by shell command
4 -> "./assemble --asmfile %(test-file) --vmfile foo.kvm && ./disasm --vmfile foo.kvm --asmfile %(output-file)"
5
6 -> Tests for functionality "Round-trip Kosheri Assembly"
7
8 | NEW_AR #10
9 | GOTO :past_q
10 | :q
11 | ; no need to reserve space for parameters
12 | GETI #0 ; local #0 = 1st parameter = a
13 | STDOUT
14 | PORTRAY
15 |
16 | GETI #1 ; local #1 = 2nd parameter = b
17 | STDOUT
18 | PORTRAY
19 |
20 | GETI #0
21 | GETI #1
22 | ADD_INT
23 |
24 | YIELD #1 ; pass the result back to our caller
25 | RET ; end of this function
26 |
27 | :past_q
28 |
29 | PUSH #1
30 |
31 | PUSH #2
32 | PUSH #3
33 | PUSH #10 ; this function will need 4 slots: 2 args, 2 stack
34 | FUN :q ; push a closure for q onto the stack
35 | CALL #2 ; call it with two args
36 |
37 | PUSH #10 ; this function will need 4 slots: 2 args, 2 stack
38 | FUN :q ; push a closure for q onto the stack
39 | CALL #2 ; call it with two args
40 |
41 | STDOUT
42 | PORTRAY
43 |
44 | HALT
45 = :L0
46 = NEW_AR #10
47 = GOTO :L20
48 = :L4
49 = GETI #0
50 = STDOUT
51 = PORTRAY
52 = GETI #1
53 = STDOUT
54 = PORTRAY
55 = GETI #0
56 = GETI #1
57 = ADD_INT
58 = YIELD #1
59 = RET
60 = :L20
61 = PUSH #1
62 = PUSH #2
63 = PUSH #3
64 = PUSH #10
65 = FUN :L4
66 = CALL #2
67 = PUSH #10
68 = FUN :L4
69 = CALL #2
70 = STDOUT
71 = PORTRAY
72 = HALT
73 =
74
75 Unused labels aren't produced by the disassembler.
76
77 | NEW_AR #5
78 | :here
79 | PUSH #123
80 | STDOUT
81 | PORTRAY
82 | HALT
83 = :L0
84 = NEW_AR #5
85 = PUSH #123
86 = STDOUT
87 = PORTRAY
88 = HALT
89 =
90
91 Redefining a label is an error.
92
93 | NEW_AR #5
94 | :here
95 | PUSH #123
96 | STDOUT
97 | PORTRAY
98 | :here
99 | HALT
100 ? line 6, column 6, token 'here'): Assembly Error: Label already defined.
101 ? Assembly finished with 1 errors and 0 warnings
102
103 You can include literal terms of all kinds in an assembly file.
104
105 | NEW_AR #5
106 | PUSH #<tuple: THIS, IS, 1, TUPLE>
107 | STDOUT
108 | PORTRAY
109 | HALT
110 = :L0
111 = NEW_AR #5
112 = PUSH #<tuple: THIS, IS, 1, TUPLE>
113 = STDOUT
114 = PORTRAY
115 = HALT
116 =
117
118 -> Functionality "Run Kosheri Assembly" is implemented by shell command
119 -> "./assemble --asmfile %(test-file) --vmfile foo.kvm >/dev/null 2>&1 && ./run --vmfile foo.kvm"
120
121 -> Tests for functionality "Run Kosheri Assembly"
122
123 Hello, world-ing
124 ----------------
125
126 You can include literal terms of all kinds in an assembly file.
127
128 | NEW_AR #5
129 | PUSH #<tuple: THIS, IS, 1, TUPLE>
130 | STDOUT
131 | PORTRAY
132 | HALT
133 = <tuple: THIS, IS, 1, TUPLE>
134
135 Basic Arithmetic
136 ----------------
137
138 Add eight to two and output the answer.
139
140 | NEW_AR #2
141 | PUSH #8
142 | PUSH #2
143 | ADD_INT
144 | STDOUT
145 | PORTRAY
146 | HALT
147 = 10
148
149 Subtract three from twelve and output the answer.
150
151 | NEW_AR #2
152 | PUSH #12
153 | PUSH #3
154 | SUB_INT
155 | STDOUT
156 | PORTRAY
157 | HALT
158 = 9
159
160 Multiply 6 by 7.
161
162 | NEW_AR #2
163 | PUSH #6
164 | PUSH #7
165 | MUL_INT
166 | STDOUT
167 | PORTRAY
168 | HALT
169 = 42
170
171 Divide 40 by 5.
172
173 | NEW_AR #2
174 | PUSH #40
175 | PUSH #5
176 | DIV_INT
177 | STDOUT
178 | PORTRAY
179 | HALT
180 = 8
181
182 Basic Looping
183 -------------
184
185 Count down from 11 to 1.
186
187 | NEW_AR #3
188 | PUSH #11 ; statically initialize our local
189 | :label
190 | GETI #0
191 | STDOUT
192 | PORTRAY
193 | GETI #0
194 | PUSH #1
195 | SUB_INT
196 | SETI #0
197 | GETI #0
198 | PUSH #0
199 | JNE :label
200 | HALT
201 = 1110987654321
202
203 Function calling
204 ----------------
205
206 Call a simple function.
207
208 local q = function(a)
209 print a
210 return 5
211 end
212 print q(4)
213
214 | NEW_AR #5
215 | PUSH #0 ; move stack pointer up for one local, q
216 | GOTO :past_q
217 | :q
218 | ; no need to reserve space for parameters
219 | GETI #0 ; local #0 = 1st parameter = a
220 | STDOUT
221 | PORTRAY
222 |
223 | PUSH #5
224 | YIELD #1 ; pass the result back to our caller
225 | RET ; end of this function
226 |
227 | :past_q
228 | PUSH #5 ; this function will need 2 slots: 1 arg, 1 stack
229 | FUN :q ; push a closure for q onto the stack
230 | SETI #0 ; set local #0 = q
231 |
232 | PUSH #4 ; put argument to pass on the stack
233 | GETI #0 ; put function (AR) to call (activate) on the stack
234 | CALL #1 ; call function, with one argument
235 |
236 | STDOUT ; return value is on the stack. print it
237 | PORTRAY
238 |
239 | HALT
240 = 45
241
242 Make two calls to a function.
243
244 local q = function(a, b)
245 print a
246 print b
247 return a + b
248 end
249 print q(1, q(2, 3))
250
251 | NEW_AR #10
252 | GOTO :past_q
253 | :q
254 | ; no need to reserve space for parameters
255 | GETI #0 ; local #0 = 1st parameter = a
256 | STDOUT
257 | PORTRAY
258 |
259 | GETI #1 ; local #1 = 2nd parameter = b
260 | STDOUT
261 | PORTRAY
262 |
263 | GETI #0
264 | GETI #1
265 | ADD_INT
266 |
267 | YIELD #1 ; pass the result back to our caller
268 | RET ; end of this function
269 |
270 | :past_q
271 |
272 | PUSH #1
273 |
274 | PUSH #2
275 | PUSH #3
276 | PUSH #10 ; this function will need 4 slots: 2 args, 2 stack
277 | FUN :q ; push a closure for q onto the stack
278 | CALL #2 ; call it with two args
279 |
280 | PUSH #10 ; this function will need 4 slots: 2 args, 2 stack
281 | FUN :q ; push a closure for q onto the stack
282 | CALL #2 ; call it with two args
283 |
284 | STDOUT
285 | PORTRAY
286 |
287 | HALT
288 = 23156
289
290 Call an iterator.
291
292 local q = function()
293 yield 1
294 yield 3
295 yield 5
296 end
297 print q()
298 print q()
299 print q()
300
301 | NEW_AR #7
302 | PUSH #0 ; move stack pointer up for one local, q
303 | GOTO :past_q
304 | :q
305 | PUSH #1
306 | YIELD #1 ; yield one value
307 | RET
308 | PUSH #3
309 | YIELD #1 ; yield another value
310 | RET
311 | PUSH #5
312 | YIELD #1
313 | RET
314 |
315 | :past_q
316 | PUSH #7 ; this function will need 1 local
317 | FUN :q ; push a closure for q onto the stack
318 | SETI #0 ; set local #0 = q
319 |
320 | GETI #0 ; call q three times; should get a different
321 | CALL #0 ; result each time.
322 | STDOUT
323 | PORTRAY
324 |
325 | GETI #0
326 | RESUME #0
327 | STDOUT
328 | PORTRAY
329 |
330 | GETI #0
331 | RESUME #0
332 | STDOUT
333 | PORTRAY
334 |
335 | HALT
336 = 135
337
338 Return a function from a function.
339
340 local f = function(a)
341 print a
342 local g = function(b)
343 return b
344 end
345 return g
346 end
347 h = f(3)
348 print h(9)
349
350 | NEW_AR #7
351 | PUSH #0
352 | PUSH #0
353 | GOTO :past_f
354 | :f
355 | GETI #0
356 | STDOUT
357 | PORTRAY
358 |
359 | GOTO :past_g
360 | :g
361 | GETI #0
362 | YIELD #1
363 | RET
364 |
365 | :past_g
366 | PUSH #7
367 | FUN :g
368 | YIELD #1
369 | RET
370 |
371 | :past_f
372 |
373 | PUSH #7 ; this function will need 1 local
374 | FUN :f ; push a closure for f onto the stack
375 | SETI #0
376 |
377 | PUSH #3
378 | GETI #0
379 | CALL #1 ; now we'll have a fun on the stack
380 |
381 | SETI #1
382 | PUSH #9
383 | GETI #1
384 | CALL #1
385 |
386 | STDOUT
387 | PORTRAY
388 |
389 | HALT
390 = 39
391
392 Call a closure.
393
394 local f = function(a)
395 local g = function(b)
396 return a + b
397 end
398 return g
399 end
400 h = f(3)
401 print h(9)
402
403 | HALT
404 =
405
406 Spawn a process!
407
408 The behaviour of this might rely on multithreading details...
409 like that we execute 100 cycles before switching.
410
411 | NEW_AR #7
412 | SPAWN :label
413 | REST
414 | PUSH #main
415 | STDOUT
416 | PORTRAY
417 | HALT
418 | :label
419 | NEW_AR #7
420 | PUSH #worker
421 | STDOUT
422 | PORTRAY
423 | HALT
424 = workermain
0 Kosheri Terms
1 =============
2
3 -> Functionality "Freeze and Thaw Kosheri Term" is implemented by shell command
4 -> "./freeze --termfile %(test-file) --binfile foo.bin && ./thaw --binfile foo.bin --termfile %(output-file)"
5
6 -> Tests for functionality "Freeze and Thaw Kosheri Term"
7
8 Freeze and thaw a well-formed, complex term.
9
10 | [150, symbol, <tuple: THIS, IS, A, TUPLE > , 611,
11 | <0: 1, <snaaa: 2, <pair: 3, 5>, <singleton: t>, <singleton: t>>>,
12 | <0: 1, 2, 3, <0: 2, 3, <pair: 3, snaaa>, 4, 5>>,
13 | [8, 9, 10, jack, queen | king],
14 | 999
15 | ]
16 = <5: 150, <5: symbol, <5: <tuple: THIS, IS, A, TUPLE>, <5: 611, <5: <0: 1, <snaaa: 2, <pair: 3, 5>, <singleton: t>, <singleton: t>>>, <5: <0: 1, 2, 3, <0: 2, 3, <pair: 3, snaaa>, 4, 5>>, <5: <5: 8, <5: 9, <5: 10, <5: jack, <5: queen, king>>>>>, <5: 999, []>>>>>>>>
17
18 | { dict = wonderful, powerful = 3, nested = { dict = 5, pict = rict }, 7 = quaint }
19 = {nested={pict=rict, dict=5}, 7=quaint, powerful=3, dict=wonderful}