1 | // ================================================================ // |
---|
2 | // // |
---|
3 | // File : aisc_interpreter.h // |
---|
4 | // Purpose : // |
---|
5 | // // |
---|
6 | // Institute of Microbiology (Technical University Munich) // |
---|
7 | // http://www.arb-home.de/ // |
---|
8 | // // |
---|
9 | // ================================================================ // |
---|
10 | |
---|
11 | #ifndef AISC_H |
---|
12 | #define AISC_H |
---|
13 | |
---|
14 | #ifndef AISC_PROTO_H |
---|
15 | #include "aisc_proto.h" |
---|
16 | #endif |
---|
17 | #ifndef AISC_PARSER_H |
---|
18 | #include "aisc_parser.h" |
---|
19 | #endif |
---|
20 | #ifndef _UNISTD_H |
---|
21 | #include <unistd.h> |
---|
22 | #endif |
---|
23 | |
---|
24 | // ------------------------------------------------------------ |
---|
25 | |
---|
26 | struct hash_entry { |
---|
27 | char *key; |
---|
28 | char *val; |
---|
29 | hash_entry *next; |
---|
30 | }; |
---|
31 | |
---|
32 | class var_ref { |
---|
33 | hash_entry *e; |
---|
34 | |
---|
35 | void const_violation(); |
---|
36 | |
---|
37 | public: |
---|
38 | var_ref() : e(NULp) {} |
---|
39 | var_ref(hash_entry *e_) : e(e_) {} |
---|
40 | |
---|
41 | operator bool() const { return e; } // refer to existing variable ? |
---|
42 | |
---|
43 | bool write_protected() const { |
---|
44 | hash_entry *prot = e->next; |
---|
45 | return prot && strcmp(prot->key, e->key) == 0 && !prot->val; |
---|
46 | } |
---|
47 | void write_protect() { |
---|
48 | if (!write_protected()) { |
---|
49 | hash_entry *prot = (hash_entry *)calloc(sizeof(hash_entry), 1); |
---|
50 | |
---|
51 | prot->key = strdup(e->key); |
---|
52 | prot->val = NULp; |
---|
53 | prot->next = e->next; |
---|
54 | |
---|
55 | e->next = prot; |
---|
56 | } |
---|
57 | } |
---|
58 | |
---|
59 | const char *read() const { return e ? e->val : NULp; } |
---|
60 | int write(const char *val) __ATTR__USERESULT { |
---|
61 | aisc_assert(e); |
---|
62 | |
---|
63 | if (write_protected()) { |
---|
64 | const_violation(); |
---|
65 | return -1; |
---|
66 | } |
---|
67 | freeset(e->val, nulldup(val)); |
---|
68 | return 0; |
---|
69 | } |
---|
70 | |
---|
71 | }; |
---|
72 | |
---|
73 | class hash : virtual Noncopyable { |
---|
74 | int size; |
---|
75 | hash_entry **entries; |
---|
76 | |
---|
77 | int Index(const char *key) const; |
---|
78 | |
---|
79 | const hash_entry *find_entry(const char *key, int idx) const; |
---|
80 | hash_entry *find_entry(const char *key, int idx) { |
---|
81 | return const_cast<hash_entry*>(const_cast<const hash*>(this)->find_entry(key, idx)); |
---|
82 | } |
---|
83 | |
---|
84 | public: |
---|
85 | hash(int size_); |
---|
86 | ~hash(); |
---|
87 | |
---|
88 | var_ref ref(const char *key) { return var_ref(find_entry(key, Index(key))); } |
---|
89 | const var_ref ref(const char *key) const { return const_cast<hash*>(this)->ref(key); } |
---|
90 | |
---|
91 | const char *read(const char *key) const { return ref(key).read(); } |
---|
92 | void write(const char *key, const char *val); |
---|
93 | }; |
---|
94 | |
---|
95 | // ------------------------------------------------------------ |
---|
96 | |
---|
97 | template<typename T> |
---|
98 | inline void realloc_unleaked(T*& ptr, size_t new_size) { |
---|
99 | T *new_ptr = (T*)realloc(ptr, new_size); |
---|
100 | if (!new_ptr) { |
---|
101 | free(ptr); |
---|
102 | throw "out of memory"; |
---|
103 | } |
---|
104 | ptr = new_ptr; |
---|
105 | } |
---|
106 | |
---|
107 | // ------------------------------------------------------------ |
---|
108 | |
---|
109 | static const int LINEBUFSIZE = 250; |
---|
110 | static const char ALIGN_MARKER = '\1'; |
---|
111 | |
---|
112 | class LineBuf : virtual Noncopyable { |
---|
113 | int size; |
---|
114 | int used; |
---|
115 | char *buf; |
---|
116 | int markers; |
---|
117 | |
---|
118 | void clear() { |
---|
119 | size = 0; |
---|
120 | used = 0; |
---|
121 | buf = NULp; |
---|
122 | markers = 0; |
---|
123 | } |
---|
124 | |
---|
125 | public: |
---|
126 | LineBuf() { clear(); } |
---|
127 | ~LineBuf() { free(buf); } |
---|
128 | |
---|
129 | void put(char c) { |
---|
130 | if (used >= size) { |
---|
131 | size = size*3/2+1; |
---|
132 | if (size<LINEBUFSIZE) { size = LINEBUFSIZE; } |
---|
133 | realloc_unleaked(buf, size); |
---|
134 | } |
---|
135 | buf[used++] = c; |
---|
136 | if (c == ALIGN_MARKER) ++markers; |
---|
137 | } |
---|
138 | |
---|
139 | int length() const { return used; } |
---|
140 | char *take() { |
---|
141 | put(0); |
---|
142 | char *b = buf; |
---|
143 | clear(); |
---|
144 | return b; |
---|
145 | } |
---|
146 | |
---|
147 | bool needsAlignment() const { return markers; } |
---|
148 | }; |
---|
149 | |
---|
150 | class LineQueue : virtual Noncopyable { |
---|
151 | int count; |
---|
152 | int size; |
---|
153 | char **queue; |
---|
154 | |
---|
155 | void clear() { |
---|
156 | for (int i = 0; i<count; ++i) freenull(queue[i]); |
---|
157 | count = 0; |
---|
158 | } |
---|
159 | public: |
---|
160 | LineQueue() |
---|
161 | : count(0), |
---|
162 | size(10), |
---|
163 | queue((char**)malloc(size*sizeof(*queue))) |
---|
164 | {} |
---|
165 | ~LineQueue() { |
---|
166 | clear(); |
---|
167 | free(queue); |
---|
168 | } |
---|
169 | |
---|
170 | bool empty() const { return count == 0; } |
---|
171 | |
---|
172 | void add(char *line) { |
---|
173 | if (count >= size) { |
---|
174 | size = size*3/2+1; |
---|
175 | realloc_unleaked(queue, size*sizeof(*queue)); |
---|
176 | } |
---|
177 | aisc_assert(line[strlen(line)-1] == '\n'); |
---|
178 | |
---|
179 | queue[count++] = line; |
---|
180 | } |
---|
181 | |
---|
182 | void alignInto(LineQueue& dest); |
---|
183 | |
---|
184 | void flush(FILE *out) { |
---|
185 | for (int i = 0; i<count; ++i) { |
---|
186 | fputs(queue[i], out); |
---|
187 | } |
---|
188 | clear(); |
---|
189 | } |
---|
190 | }; |
---|
191 | |
---|
192 | class Formatter { |
---|
193 | char outtab[256]; // decode table for $x (0 = handle special, character to print otherwise) |
---|
194 | int tabstop; // normal tabstop (for $t) |
---|
195 | int tabs[10]; // predefined tabs ($0..$9) - default to multiples of 'tabstop' if not overridden |
---|
196 | int column; // position in line during printing |
---|
197 | int indent; // extra indentation |
---|
198 | bool printed_sth; |
---|
199 | |
---|
200 | LineBuf currentLine; |
---|
201 | LineQueue toAlign; |
---|
202 | LineQueue spool; |
---|
203 | |
---|
204 | void outputchar(char c) { |
---|
205 | currentLine.put(c); |
---|
206 | } |
---|
207 | |
---|
208 | void print_char(char c) { |
---|
209 | if (!printed_sth && (indent || column)) { |
---|
210 | int ipos = indent*tabstop + column; |
---|
211 | for (int i = 0; i<ipos; ++i) outputchar(' '); |
---|
212 | } |
---|
213 | outputchar(c); |
---|
214 | column++; |
---|
215 | printed_sth = true; |
---|
216 | } |
---|
217 | |
---|
218 | void tab_to_pos(int pos) { |
---|
219 | if (pos>column) { |
---|
220 | if (printed_sth) { |
---|
221 | while (column < pos) { |
---|
222 | outputchar(' '); |
---|
223 | column++; |
---|
224 | } |
---|
225 | } |
---|
226 | else { |
---|
227 | column = pos; |
---|
228 | } |
---|
229 | } |
---|
230 | } |
---|
231 | |
---|
232 | void align() { if (!toAlign.empty()) toAlign.alignInto(spool); } |
---|
233 | |
---|
234 | void finish_line() { |
---|
235 | outputchar('\n'); |
---|
236 | column = 0; |
---|
237 | printed_sth = false; |
---|
238 | |
---|
239 | if (currentLine.needsAlignment()) { |
---|
240 | toAlign.add(currentLine.take()); |
---|
241 | } |
---|
242 | else { |
---|
243 | align(); |
---|
244 | spool.add(currentLine.take()); |
---|
245 | } |
---|
246 | } |
---|
247 | |
---|
248 | public: |
---|
249 | |
---|
250 | Formatter(); |
---|
251 | |
---|
252 | void set_tabstop(int ts) { |
---|
253 | tabstop = ts; |
---|
254 | for (int i = 0; i <= 9; i++) { |
---|
255 | tabs[i] = i * ts; |
---|
256 | } |
---|
257 | } |
---|
258 | void set_tab(int idx, int pos) { |
---|
259 | aisc_assert(idx >= 0 && idx<10); |
---|
260 | tabs[idx] = pos; |
---|
261 | } |
---|
262 | |
---|
263 | int get_indent() const { return indent; } |
---|
264 | void set_indent(int indent_) { indent = indent_; } |
---|
265 | |
---|
266 | int write(const char *str); |
---|
267 | void flush(FILE *out) { spool.flush(out); } |
---|
268 | void final_flush(FILE *out) { align(); flush(out); } |
---|
269 | }; |
---|
270 | |
---|
271 | class Output : virtual Noncopyable { |
---|
272 | FILE *fp; |
---|
273 | char *id; // internal name used in AISC |
---|
274 | char *name; // file-system name |
---|
275 | |
---|
276 | bool have_open_loc; // opened from user code ? |
---|
277 | Location open_loc; |
---|
278 | |
---|
279 | bool terminating; |
---|
280 | |
---|
281 | Formatter formatter; |
---|
282 | |
---|
283 | class PrintMaybe *maybe; |
---|
284 | |
---|
285 | bool wasOpened() const { return fp && name; } |
---|
286 | |
---|
287 | void close_file() { |
---|
288 | if (wasOpened()) { |
---|
289 | formatter.final_flush(fp); |
---|
290 | if (have_open_loc && terminating) { |
---|
291 | print_error(&open_loc, "file opened here"); |
---|
292 | print_error(&Location::guess_pc(), "is still open on exit"); |
---|
293 | } |
---|
294 | fclose(fp); |
---|
295 | } |
---|
296 | fp = NULp; |
---|
297 | } |
---|
298 | |
---|
299 | void setup(); |
---|
300 | void cleanup(); |
---|
301 | void reuse() { cleanup(); setup(); } |
---|
302 | |
---|
303 | public: |
---|
304 | |
---|
305 | Output() { terminating = false; setup(); } |
---|
306 | ~Output() { terminating = true; cleanup(); } |
---|
307 | |
---|
308 | bool inUse() const { return fp; } |
---|
309 | |
---|
310 | void assign(FILE *fp_, const char *id_, const char *name_, const Location *openedAt) { |
---|
311 | aisc_assert(!inUse()); |
---|
312 | aisc_assert(fp_); |
---|
313 | aisc_assert(id_); |
---|
314 | fp = fp_; |
---|
315 | id = strdup(id_); |
---|
316 | name = strdup(name_); |
---|
317 | |
---|
318 | have_open_loc = openedAt; |
---|
319 | if (openedAt) open_loc = *openedAt; |
---|
320 | } |
---|
321 | void assign(FILE *fp_, const char *id_, const char *name_) { |
---|
322 | assign(fp_, id_, name_, (const Location*)NULp); |
---|
323 | } |
---|
324 | void assign(FILE *fp_, const char *id_, const char *name_, const Code *openedAt_) { |
---|
325 | assign(fp_, id_, name_, &openedAt_->source); |
---|
326 | } |
---|
327 | |
---|
328 | void assign_stdout(const char *id_) { |
---|
329 | aisc_assert(!inUse()); |
---|
330 | fp = stdout; |
---|
331 | id = strdup(id_); |
---|
332 | name = NULp; |
---|
333 | } |
---|
334 | |
---|
335 | void close_and_unlink() { |
---|
336 | close_file(); |
---|
337 | if (name) { |
---|
338 | fprintf(stderr, "Unlinking %s\n", name); |
---|
339 | unlink(name); |
---|
340 | } |
---|
341 | reuse(); |
---|
342 | } |
---|
343 | void close() { reuse(); } |
---|
344 | |
---|
345 | bool hasID(const char *Name) const { return id && strcmp(id, Name) == 0; } |
---|
346 | |
---|
347 | int write(const char *line); |
---|
348 | Formatter& get_formatter() { return formatter; } |
---|
349 | |
---|
350 | void maybe_start(); |
---|
351 | int maybe_write(const char *line); |
---|
352 | int maybe_end(); |
---|
353 | }; |
---|
354 | |
---|
355 | struct Stack { |
---|
356 | const Token *cursor; |
---|
357 | |
---|
358 | const Code *pc; |
---|
359 | hash *hs; |
---|
360 | Stack *next; |
---|
361 | }; |
---|
362 | |
---|
363 | class Data : virtual Noncopyable { |
---|
364 | TokenListBlock *rootBlock; |
---|
365 | const Token *cursor; |
---|
366 | public: |
---|
367 | |
---|
368 | Data() { |
---|
369 | cursor = NULp; |
---|
370 | rootBlock = NULp; |
---|
371 | } |
---|
372 | ~Data() { delete rootBlock; } |
---|
373 | |
---|
374 | void set_tokens(TokenListBlock *newRoot) { |
---|
375 | delete rootBlock; |
---|
376 | rootBlock = newRoot; |
---|
377 | } |
---|
378 | const TokenListBlock *get_tokens() const { return rootBlock; } |
---|
379 | |
---|
380 | const Token *get_cursor() const { return cursor; } |
---|
381 | void set_cursor(const Token *newCursor) { cursor = newCursor; } |
---|
382 | |
---|
383 | const Token *find_token(const Token *curs, const char *str, LookupScope scope) const; |
---|
384 | const Token *find_qualified_token(const char *str, LookupScope scope) const; |
---|
385 | |
---|
386 | void dump_cursor_pos(FILE *out) const; |
---|
387 | }; |
---|
388 | |
---|
389 | class Interpreter : virtual Noncopyable { |
---|
390 | Parser parser; |
---|
391 | |
---|
392 | Data data; // currently loaded data |
---|
393 | |
---|
394 | Code *prg; // the complete program |
---|
395 | const Code *pc; // current program counter |
---|
396 | const Code *nextpc; |
---|
397 | |
---|
398 | int stack_size; |
---|
399 | Stack *stack; |
---|
400 | hash *functions; // and labels |
---|
401 | |
---|
402 | Output output[OPENFILES]; // open files |
---|
403 | Output *current_output; // pointer to one element of 'output' |
---|
404 | |
---|
405 | static const int MAX_COMMANDS = 32; |
---|
406 | class Command **command_table; |
---|
407 | void command_table_setup(bool setup); |
---|
408 | |
---|
409 | void pop(); |
---|
410 | |
---|
411 | Output *find_output_for_ID(const char *fileID) { |
---|
412 | if (fileID) { |
---|
413 | for (int i = 0; i < OPENFILES; i++) { |
---|
414 | if (output[i].hasID(fileID)) return &output[i]; |
---|
415 | } |
---|
416 | } |
---|
417 | return NULp; |
---|
418 | } |
---|
419 | Formatter& current_formatter() { return current_output->get_formatter(); } |
---|
420 | |
---|
421 | void write_var(const char *name, const char *val) { stack->hs->write(name, val); } |
---|
422 | const char *read_var(const char *name) { return stack->hs->read(name); } |
---|
423 | |
---|
424 | void define_fun(const char *name, const Code *co); |
---|
425 | const Code *find_fun(const char *name); |
---|
426 | |
---|
427 | int run_program(); |
---|
428 | |
---|
429 | void jump(const Code *to) { nextpc = to; } |
---|
430 | |
---|
431 | int do_close(const char *str); |
---|
432 | int do_create(const char *str); |
---|
433 | int do_data(const char *str); |
---|
434 | int do_dumpdata(const char *filename); |
---|
435 | int do_error(const char *str) { print_error(at(), str); return 1; } |
---|
436 | int do_exit() { nextpc = NULp; return 0; } |
---|
437 | int do_for(const char *str); |
---|
438 | int do_gosub(const char *str); |
---|
439 | int do_goto(const char *str); |
---|
440 | int do_if(const char *str); |
---|
441 | int do_moveto(const char *str); |
---|
442 | int do_next(); |
---|
443 | int do_open(const char *str); |
---|
444 | int do_out(const char *str); |
---|
445 | int do_pop(); |
---|
446 | int do_push(); |
---|
447 | int do_return(); |
---|
448 | int do_set(const char *str); |
---|
449 | int do_makeconst(const char *str); |
---|
450 | int do_tab(const char *str); |
---|
451 | int do_tabstop(const char *str); |
---|
452 | int do_indent(const char *str); |
---|
453 | int do_warning(const char *str) { print_warning(at(), str); return 0; } |
---|
454 | |
---|
455 | int do_write_current(const char *str) { return current_output->write(str); } |
---|
456 | int do_newline() { return current_output->write(""); } |
---|
457 | int do_write_stdout(const char *str) { return output[0].write(str); } |
---|
458 | |
---|
459 | int do_write_maybe_start() { current_output->maybe_start(); return 0; } |
---|
460 | int do_write_maybe(const char *str) { return current_output->maybe_write(str); } |
---|
461 | int do_write_maybe_end() { return current_output->maybe_end(); } |
---|
462 | |
---|
463 | int compile_program(); |
---|
464 | const Command *find_command(const Code *co); |
---|
465 | |
---|
466 | bool set_data(const char *filename, int offset_in_line); |
---|
467 | |
---|
468 | public: |
---|
469 | |
---|
470 | static const Interpreter *instance; |
---|
471 | |
---|
472 | Interpreter() { |
---|
473 | pc = NULp; |
---|
474 | nextpc = NULp; |
---|
475 | |
---|
476 | command_table_setup(true); |
---|
477 | |
---|
478 | stack_size = 0; |
---|
479 | functions = new hash(HASHSIZE); |
---|
480 | |
---|
481 | prg = NULp; |
---|
482 | stack = NULp; |
---|
483 | |
---|
484 | output[0].assign_stdout("stdout"); |
---|
485 | output[1].assign_stdout("*"); |
---|
486 | current_output = &output[0]; |
---|
487 | |
---|
488 | ASSERT_RESULT(int, 0, do_push()); |
---|
489 | |
---|
490 | aisc_assert(!instance); // singleton! |
---|
491 | instance = this; |
---|
492 | } |
---|
493 | ~Interpreter() { |
---|
494 | aisc_assert(instance == this); |
---|
495 | instance = NULp; |
---|
496 | |
---|
497 | delete prg; |
---|
498 | delete functions; |
---|
499 | while (stack) pop(); |
---|
500 | command_table_setup(false); |
---|
501 | |
---|
502 | } |
---|
503 | |
---|
504 | int launch(int argc, char ** argv); |
---|
505 | |
---|
506 | |
---|
507 | const Code *at() const { return pc; } |
---|
508 | const Data& get_data() const { return data; } |
---|
509 | |
---|
510 | const char *read_local(const char *key) const; |
---|
511 | var_ref get_local(const char *key); |
---|
512 | }; |
---|
513 | |
---|
514 | #else |
---|
515 | #error aisc.h included twice |
---|
516 | #endif // AISC_H |
---|
517 | |
---|
518 | |
---|
519 | |
---|