1 | // ================================================================ |
---|
2 | /* */ |
---|
3 | // File : aisc.c |
---|
4 | // Purpose : ARB integrated source compiler |
---|
5 | /* */ |
---|
6 | // Institute of Microbiology (Technical University Munich) |
---|
7 | // http://www.arb-home.de |
---|
8 | /* */ |
---|
9 | // ================================================================ |
---|
10 | |
---|
11 | #include "aisc_interpreter.h" |
---|
12 | #include <cctype> |
---|
13 | #include <list> |
---|
14 | #include <string> |
---|
15 | |
---|
16 | using namespace std; |
---|
17 | |
---|
18 | // AISC_MKPT_PROMOTE:#ifndef AISC_DEF_H |
---|
19 | // AISC_MKPT_PROMOTE:#include "aisc_def.h" |
---|
20 | // AISC_MKPT_PROMOTE:#endif |
---|
21 | |
---|
22 | char *read_aisc_file(const char *path, const Location *loc) { |
---|
23 | char *buffer = NULp; |
---|
24 | FILE *input = fopen(path, "rt"); |
---|
25 | |
---|
26 | if (!input) { |
---|
27 | printf_error(loc, "file '%s' not found", path); |
---|
28 | } |
---|
29 | else { |
---|
30 | if (fseek(input, 0, 2)==-1) { |
---|
31 | printf_error(loc, "file '%s' not seekable", path); |
---|
32 | } |
---|
33 | else { |
---|
34 | int data_size = (int)ftell(input); |
---|
35 | |
---|
36 | if (data_size == 0) { |
---|
37 | Location fileLoc(0, path); |
---|
38 | print_error(&fileLoc, "file is empty"); |
---|
39 | } |
---|
40 | else { |
---|
41 | data_size++; |
---|
42 | rewind(input); |
---|
43 | buffer = (char *)malloc(data_size+1); |
---|
44 | data_size = fread(buffer, 1, data_size, input); |
---|
45 | buffer[data_size] = 0; |
---|
46 | } |
---|
47 | } |
---|
48 | fclose(input); |
---|
49 | } |
---|
50 | return buffer; |
---|
51 | } |
---|
52 | |
---|
53 | inline int max(int i, int j) { return i<j ? j : i; } |
---|
54 | |
---|
55 | void LineQueue::alignInto(LineQueue& dest) { |
---|
56 | int offset = 0; |
---|
57 | int len[count]; |
---|
58 | |
---|
59 | for (int i = 0; i<count; ++i) len[i] = strlen(queue[i]); |
---|
60 | |
---|
61 | while (1) { |
---|
62 | int max_mark_pos = -1; |
---|
63 | int mark_pos[count]; |
---|
64 | for (int i = 0; i<count; ++i) { |
---|
65 | char *line = queue[i]; |
---|
66 | char *mark = len[i]>offset ? strchr(line+offset, ALIGN_MARKER) : NULp; |
---|
67 | mark_pos[i] = mark ? mark-line : -1; |
---|
68 | max_mark_pos = max(max_mark_pos, mark_pos[i]); |
---|
69 | } |
---|
70 | if (max_mark_pos == -1) break; |
---|
71 | |
---|
72 | for (int i = 0; i<count; ++i) { |
---|
73 | if (mark_pos[i] >= 0) { |
---|
74 | int insert = max_mark_pos-mark_pos[i]; |
---|
75 | aisc_assert(insert >= 0); |
---|
76 | if (insert >= 0) { |
---|
77 | int new_len = len[i]+insert; |
---|
78 | char *new_line = (char*)malloc(new_len+1); |
---|
79 | |
---|
80 | memcpy(new_line, queue[i], mark_pos[i]); |
---|
81 | memset(new_line+mark_pos[i], ' ', insert); |
---|
82 | strcpy(new_line+max_mark_pos, queue[i]+mark_pos[i]+1); |
---|
83 | |
---|
84 | len[i] = new_len; |
---|
85 | freeset(queue[i], new_line); |
---|
86 | } |
---|
87 | } |
---|
88 | } |
---|
89 | |
---|
90 | offset = max_mark_pos; |
---|
91 | } |
---|
92 | |
---|
93 | for (int i = 0; i<count; ++i) { |
---|
94 | dest.add(queue[i]); |
---|
95 | queue[i] = NULp; |
---|
96 | } |
---|
97 | count = 0; |
---|
98 | } |
---|
99 | |
---|
100 | struct queued_line { |
---|
101 | string line; |
---|
102 | int indentation; |
---|
103 | queued_line(const char *line_, int indent) : line(line_), indentation(indent) { } |
---|
104 | }; |
---|
105 | |
---|
106 | typedef list<queued_line> PrintQueue; |
---|
107 | |
---|
108 | class PrintMaybe : virtual Noncopyable { |
---|
109 | Output& out; |
---|
110 | const Location& started_at; |
---|
111 | bool printed_sth; |
---|
112 | PrintMaybe *next; |
---|
113 | PrintQueue queue; |
---|
114 | |
---|
115 | public: |
---|
116 | PrintMaybe(PrintMaybe *head, Output& out_, const Location& loc) |
---|
117 | : out(out_), |
---|
118 | started_at(loc), |
---|
119 | printed_sth(false), |
---|
120 | next(head) |
---|
121 | {} |
---|
122 | |
---|
123 | void add(const char *line) { |
---|
124 | if (printed_sth) { |
---|
125 | out.write(line); |
---|
126 | } |
---|
127 | else { |
---|
128 | queue.push_back(queued_line(line, out.get_formatter().get_indent())); |
---|
129 | } |
---|
130 | } |
---|
131 | void spool() { |
---|
132 | aisc_assert(!printed_sth); |
---|
133 | printed_sth = true; |
---|
134 | if (!queue.empty()) { |
---|
135 | Formatter& formatter = out.get_formatter(); |
---|
136 | int old_indent = formatter.get_indent(); |
---|
137 | for (PrintQueue::iterator i = queue.begin(); i != queue.end(); ++i) { |
---|
138 | queued_line& ql = *i; |
---|
139 | formatter.set_indent(ql.indentation); |
---|
140 | out.write(ql.line.c_str()); |
---|
141 | } |
---|
142 | formatter.set_indent(old_indent); |
---|
143 | queue.clear(); |
---|
144 | } |
---|
145 | } |
---|
146 | |
---|
147 | void will_print() { |
---|
148 | if (next) next->will_print(); |
---|
149 | if (!printed_sth) { |
---|
150 | spool(); |
---|
151 | } |
---|
152 | } |
---|
153 | |
---|
154 | void not_destroyed_error() { |
---|
155 | print_error(&started_at, "PMSTART without matching PMEND"); |
---|
156 | if (next) next->not_destroyed_error(); |
---|
157 | } |
---|
158 | |
---|
159 | static void pop(PrintMaybe*& head) { |
---|
160 | PrintMaybe *next = head->next; |
---|
161 | head->next = NULp; |
---|
162 | delete head; |
---|
163 | head = next; |
---|
164 | } |
---|
165 | }; |
---|
166 | |
---|
167 | void Output::setup() { |
---|
168 | fp = NULp; |
---|
169 | id = NULp; |
---|
170 | name = NULp; |
---|
171 | maybe = NULp; |
---|
172 | |
---|
173 | have_open_loc = false; |
---|
174 | } |
---|
175 | void Output::cleanup() { |
---|
176 | close_file(); |
---|
177 | free(id); |
---|
178 | free(name); |
---|
179 | if (maybe) { |
---|
180 | maybe->not_destroyed_error(); |
---|
181 | delete maybe; |
---|
182 | } |
---|
183 | } |
---|
184 | |
---|
185 | void Output::maybe_start() { |
---|
186 | maybe = new PrintMaybe(maybe, *this, Interpreter::instance->at()->source); |
---|
187 | } |
---|
188 | int Output::maybe_write(const char *line) { |
---|
189 | if (!maybe) { |
---|
190 | print_error(Interpreter::instance->at(), "no PMSTART before PM"); |
---|
191 | return -1; |
---|
192 | } |
---|
193 | maybe->add(line); |
---|
194 | return 0; |
---|
195 | } |
---|
196 | int Output::maybe_end() { |
---|
197 | if (!maybe) { |
---|
198 | print_error(Interpreter::instance->at(), "no PMSTART before PMEND"); |
---|
199 | return -1; |
---|
200 | } |
---|
201 | PrintMaybe::pop(maybe); |
---|
202 | return 0; |
---|
203 | } |
---|
204 | |
---|
205 | int Output::write(const char *line) { |
---|
206 | if (!inUse()) { |
---|
207 | print_error(Interpreter::instance->at(), "Fatal: attempt to write to unused file"); |
---|
208 | return -1; |
---|
209 | } |
---|
210 | |
---|
211 | if (maybe) maybe->will_print(); |
---|
212 | |
---|
213 | int res = formatter.write(line); |
---|
214 | formatter.flush(fp); |
---|
215 | return res; |
---|
216 | } |
---|
217 | |
---|
218 | Formatter::Formatter() |
---|
219 | : tabstop(4), |
---|
220 | column(0), |
---|
221 | indent(0), |
---|
222 | printed_sth(false) |
---|
223 | { |
---|
224 | set_tabstop(4); |
---|
225 | |
---|
226 | for (int i = 0; i < 256; i++) { |
---|
227 | outtab[i] = i; |
---|
228 | } |
---|
229 | |
---|
230 | outtab[(unsigned char)'n'] = '\n'; |
---|
231 | outtab[(unsigned char)'t'] = '\t'; |
---|
232 | outtab[(unsigned char)'|'] = ALIGN_MARKER; |
---|
233 | outtab[(unsigned char)'0'] = 0; |
---|
234 | outtab[(unsigned char)'1'] = 0; |
---|
235 | outtab[(unsigned char)'2'] = 0; |
---|
236 | outtab[(unsigned char)'3'] = 0; |
---|
237 | outtab[(unsigned char)'4'] = 0; |
---|
238 | outtab[(unsigned char)'5'] = 0; |
---|
239 | outtab[(unsigned char)'6'] = 0; |
---|
240 | outtab[(unsigned char)'7'] = 0; |
---|
241 | outtab[(unsigned char)'8'] = 0; |
---|
242 | outtab[(unsigned char)'9'] = 0; |
---|
243 | outtab[(unsigned char)'\\'] = 0; |
---|
244 | } |
---|
245 | |
---|
246 | int Formatter::write(const char *str) { |
---|
247 | int no_nl = 0; |
---|
248 | const char *p = str; |
---|
249 | char c; |
---|
250 | |
---|
251 | while ((c = *(p++))) { |
---|
252 | if (c == '$') { |
---|
253 | c = *(p++); |
---|
254 | if (!c) |
---|
255 | break; |
---|
256 | |
---|
257 | if (!outtab[(unsigned)(c)]) { |
---|
258 | if (c == '\\') { |
---|
259 | no_nl = 1; |
---|
260 | } |
---|
261 | else if (isdigit(c)) { |
---|
262 | int pos = tabs[c - '0']; |
---|
263 | tab_to_pos(pos); |
---|
264 | } |
---|
265 | continue; |
---|
266 | } |
---|
267 | else { |
---|
268 | c = outtab[(unsigned)(c)]; |
---|
269 | } |
---|
270 | } |
---|
271 | if (c == '\t') { |
---|
272 | int pos = ((column/tabstop)+1)*tabstop; |
---|
273 | tab_to_pos(pos); |
---|
274 | } |
---|
275 | else if (c == '\n') { |
---|
276 | if (no_nl) { |
---|
277 | no_nl = 0; |
---|
278 | } |
---|
279 | else { |
---|
280 | finish_line(); |
---|
281 | } |
---|
282 | } |
---|
283 | else if (c == '@') { |
---|
284 | if (strncmp(p, "SETSOURCE", 9) == 0) { // skip '@SETSOURCE file, line@' |
---|
285 | p = strchr(p, '@'); |
---|
286 | if (!p) { |
---|
287 | print_error(Interpreter::instance->at(), "expected '@' after '@SETSOURCE' (injected code)"); |
---|
288 | return 1; |
---|
289 | } |
---|
290 | p++; |
---|
291 | } |
---|
292 | else { |
---|
293 | print_char(c); |
---|
294 | } |
---|
295 | } |
---|
296 | else { |
---|
297 | print_char(c); |
---|
298 | } |
---|
299 | } |
---|
300 | if (!no_nl) { |
---|
301 | finish_line(); |
---|
302 | } |
---|
303 | return 0; |
---|
304 | } |
---|
305 | |
---|
306 | bool Interpreter::set_data(const char *dataBlock, int offset_in_line) { |
---|
307 | parser.set_line_start(dataBlock, offset_in_line); |
---|
308 | parser.set_source(at()->source); // remove ? |
---|
309 | data.set_tokens(parser.parseTokenListBlock(dataBlock)); |
---|
310 | return data.get_tokens(); |
---|
311 | } |
---|
312 | |
---|
313 | |
---|
314 | int Interpreter::launch(int argc, char ** argv) { |
---|
315 | int exitcode = EXIT_FAILURE; |
---|
316 | |
---|
317 | // save CL-arguments as variables (with names like 'argv[0]' ..) |
---|
318 | { |
---|
319 | for (int i=0; i<argc; i++) { |
---|
320 | write_var(formatted("argv[%i]", i), argv[i]); |
---|
321 | } |
---|
322 | write_var("argc", formatted("%i", argc)); |
---|
323 | } |
---|
324 | |
---|
325 | { |
---|
326 | char *buf = read_aisc_file(argv[1], NULp); |
---|
327 | if (buf) { |
---|
328 | prg = parser.parse_program(buf, argv[1]); |
---|
329 | free(buf); |
---|
330 | } |
---|
331 | } |
---|
332 | |
---|
333 | if (!prg) { |
---|
334 | fputs("Nothing to execute\n", stderr); |
---|
335 | } |
---|
336 | else { |
---|
337 | if (compile_program()) { |
---|
338 | fprintf(stderr, "Compilation of '%s' failed\n", argv[1]); |
---|
339 | } |
---|
340 | else { |
---|
341 | if (run_program()) { |
---|
342 | if (!Location::get_error_count()) { |
---|
343 | print_error(at(), "AISC compiler bailed out w/o error"); |
---|
344 | } |
---|
345 | fputs("AISC reports errors\n", stderr); |
---|
346 | for (int i = 0; i < OPENFILES; i++) output[i].close_and_unlink(); |
---|
347 | fflush(stdout); |
---|
348 | } |
---|
349 | else { |
---|
350 | aisc_assert(!Location::get_error_count()); |
---|
351 | exitcode = EXIT_SUCCESS; |
---|
352 | } |
---|
353 | } |
---|
354 | } |
---|
355 | |
---|
356 | return exitcode; |
---|
357 | } |
---|
358 | |
---|
359 | int main(int argc, char ** argv) { |
---|
360 | int exitcode = EXIT_FAILURE; |
---|
361 | |
---|
362 | if (argc < 2) { |
---|
363 | fprintf(stderr, "AISC - ARB integrated source compiler\n"); |
---|
364 | fprintf(stderr, "Usage: aisc [fileToCompile]+\n"); |
---|
365 | fprintf(stderr, "Error: missing file name\n"); |
---|
366 | } |
---|
367 | else { |
---|
368 | try { |
---|
369 | exitcode = Interpreter().launch(argc, argv); |
---|
370 | } |
---|
371 | catch (const char *err) { |
---|
372 | fprintf(stderr, "\nAISC: exception: %s [terminating]\n", err); |
---|
373 | exitcode = EXIT_FAILURE; |
---|
374 | } |
---|
375 | catch (...) { |
---|
376 | fprintf(stderr, "\nAISC: unknown exception [terminating]\n"); |
---|
377 | exitcode = EXIT_FAILURE; |
---|
378 | } |
---|
379 | } |
---|
380 | return exitcode; |
---|
381 | } |
---|
382 | |
---|
383 | |
---|