root/trunk/AISC/aisc_commands.c

Revision 7811, 28.9 KB (checked in by westram, 10 months ago)

merge from dev [7748] [7749] [7750]

  • comments (C->C++ style)
  • fixed umlauts in TREEGEN
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1// ================================================================
2/*                                                                  */
3//   File      : aisc_commands.c
4//   Purpose   :
5/*                                                                  */
6//   Institute of Microbiology (Technical University Munich)
7//   http://www.arb-home.de/
8/*                                                                  */
9// ================================================================
10
11#include "aisc_interpreter.h"
12#include "aisc_eval.h"
13
14Location Location::exit_pc;
15int      Location::global_error_count = 0;
16
17const Location& Location::guess_pc() {
18    if (exit_pc.valid()) return exit_pc;
19
20    const Code *pc = Interpreter::instance->at();
21    if (pc) return pc->source;
22
23    static Location dummy(0, "no_idea_where");
24    return dummy;
25}
26
27void Location::print_internal(const char *msg, const char *msg_type, const char *launcher_file, int launcher_line) const {
28#if defined(MASK_ERRORS)
29    fputs("(disabled) ", stderr);
30#endif
31    fputs("./", stderr);
32    fprint_location(stderr);
33
34    if (msg_type) {
35        fputs(msg_type, stderr);
36        fputs(": ", stderr);
37    }
38
39    if (msg) {
40        fputs(msg, stderr);
41        const Data& data = Interpreter::instance->get_data();
42        if (data.get_cursor()) {
43            fputs(" (cursor ", stderr);
44            data.dump_cursor_pos(stderr);
45            fputs(")", stderr);
46        }
47        fputc('\n', stderr);
48    }
49
50    if (launcher_file) {
51#if defined(MASK_ERRORS)
52        fputs("(disabled) ", stderr);
53#endif
54        fputs("../AISC/", stderr);
55        fprint_location(launcher_file, launcher_line, stderr);
56        fprintf(stderr, "%s was launched from here\n", msg_type);
57    }
58}
59
60#define ERRBUFSIZE 500
61const char *formatted(const char *format, ...) {
62    // goes to header:  __ATTR__FORMAT(1)
63
64    static char *errbuf = 0;
65    if (!errbuf) { errbuf = (char*)malloc(ERRBUFSIZE+1); }
66
67    va_list argPtr;
68    va_start(argPtr, format);
69    int     chars = vsprintf(errbuf, format, argPtr);
70    if (chars>ERRBUFSIZE) {
71        fprintf(stderr, "%s:%i: Error: Buffer overflow!\n", __FILE__, __LINE__);
72        vfprintf(stderr, format, argPtr);
73        fputc('\n', stderr);
74
75        va_end(argPtr);
76        exit(EXIT_FAILURE);
77    }
78    va_end(argPtr);
79
80    return errbuf;
81}
82#undef ERRBUFSIZE
83
84inline void print_tabs(int count, FILE *out) {
85    while (count--) fputc('\t', out);
86}
87
88static void write_aisc(const TokenListBlock *block, FILE *out, int indentation) {
89    for (const TokenList *list = block->first_list(); list; list = list->next_list()) {
90        print_tabs(indentation, out);
91
92        const Token *first = list->first_token();
93        for (const Token *tok = first; tok; tok = tok->next_token()) {
94            if (tok != first) fputc(',', out);
95
96            if (tok->is_block()) {
97                fprintf(out, "'%s'={\n", tok->get_key());
98                const TokenListBlock *content = tok->get_content();
99                if (content) write_aisc(content, out, indentation + 1);
100                print_tabs(indentation+1, out);
101                fputc('}', out);
102            }
103            else {
104                fprintf(out, "\t'%s'=(~%s~)",
105                        tok->get_key(),
106                        tok->has_value() ? tok->get_value() : "");
107            }
108        }
109        fprintf(out, ";\n");
110    }
111}
112
113// --------------------
114//      Interpreter
115
116const Interpreter *Interpreter::instance = NULL;
117
118void Interpreter::define_fun(const char *name, const Code *co) {
119    const Code *exists = find_fun(name);
120    if (exists) {
121        printf_error(co, "Attempt to redefine '%s'", name);
122        print_error(exists, "first definition was here");
123    }
124    else {
125        char buffer[100];
126        sprintf(buffer, "%li", (long)co);
127        functions->write(name, buffer);
128    }
129}
130
131const Code *Interpreter::find_fun(const char *name) {
132    const char *fn = functions->read(name);
133    return fn ? (Code*)atol(fn) : NULL;
134}
135
136
137int Interpreter::do_dumpdata(const char *filename) {
138    if (!data.get_tokens()) {
139        print_error(at(), "DUMPDATA can only be used after DATA");
140        return 1;
141    }
142    FILE *out = stdout;
143    if (filename[0]) {
144        printf("Dumping data to '%s'\n", filename);
145        out = fopen(filename, "wt");
146        if (!out) {
147            printf_error(at(), "cant write to file '%s' (DUMPDATA)", filename);
148            return 1;
149        }
150    }
151    else {
152        puts("DUMPDATA:");
153    }
154    write_aisc(data.get_tokens(), out, 0);
155    if (filename[0]) fclose(out);
156    return 0;
157}
158
159int Interpreter::do_data(const char *str) {
160    if (strlen(str) < 2) {
161        printf_error(at(), "no parameter '%s'", at()->str);
162        return 1;
163    }
164
165    if (!set_data(str, 4)) { // hack to correct column reported on errors
166        printf_error(at(), "occurred in following script-part: '%s'", at()->str);
167        return 1;
168    }
169    return 0;
170}
171
172int Interpreter::do_indent(const char *str) {
173    int diff       = atoi(str);
174    int indent     = current_formatter().get_indent();
175    int new_indent = indent+diff;
176
177    if (new_indent<0) {
178        printf_error(at(), "invalid resulting indentation %i", new_indent);
179        return 1;
180    }
181
182    current_formatter().set_indent(new_indent);
183    return 0;
184}
185
186int Interpreter::do_tabstop(const char *str) {
187    int ts = atoi(str);
188    if ((ts < 1) || (ts > 1024)) {
189        printf_error(at(), "illegal TABSTOP %i (legal: [1..1024])", ts);
190        return 1;
191    }
192
193    current_formatter().set_tabstop(ts);
194    return 0;
195}
196
197class ArgParser : virtual Noncopyable {
198    char            *args;
199    char            *strtok_arg;
200    const Location&  loc;
201
202    void init(const char *a) { strtok_arg = args = strdup(a); }
203public:
204    ArgParser(const char *arguments, const Location& loc_) : loc(loc_) { init(arguments); }
205    ArgParser(const char *arguments, const Code *co) : loc(co->source) { init(arguments); }
206    ~ArgParser() { free(args); }
207
208    const char *get(const char *paramDescription) {
209        const char *res = strtok(strtok_arg, " \t,;\n");
210        strtok_arg      = NULL;
211        if (!res) printf_error(&loc, "Missing argument '%s'", paramDescription);
212        return res;
213    }
214};
215
216int Interpreter::do_tab(const char *str) {
217    ArgParser args(str, at());
218
219    const char *s1 = args.get("TABCOUNT");
220    if (s1) {
221        const char *s2 = args.get("TABVALUE");
222        if (s2) {
223            int ts = atoi(s1);
224            if ((ts < 0) || (ts > 9)) print_error(at(), "wrong TABCOUNT");
225            else {
226                int val = atoi(s2);
227                if ((val < 0) || (val > 1000)) print_error(at(), "wrong TABVALUE");
228                else {
229                    current_formatter().set_tab(ts, val);
230                    return 0;
231                }
232            }
233        }
234    }
235    return 1;
236}
237
238int Interpreter::do_open(const char *str) {
239    ArgParser args(str, at());
240
241    const char *fileID = args.get("fileID");
242    if (fileID) {
243        const char *fn = args.get("filename");
244        if (strlen(fn) < 3) printf_error(at(), "Filename '%s' too short (<3)", fn);
245        else {
246            Output *alreadyOpen = find_output_for_ID(fileID);
247            if (alreadyOpen) printf_error(at(), "File '%s' already opened", fileID);
248            else {
249                int i;
250                for (i = 0; i < OPENFILES; i++) {
251                    if (!output[i].inUse()) break;
252                }
253                if (i == OPENFILES) print_error(at(), "Too many open files");
254                else {
255                    FILE *file = fopen(fn, "wt");
256                    if (file) {
257                        output[i].assign(file, fileID, fn, at());
258                        return 0;
259                    }
260                    printf_error(at(), "Cannot write to file '%s' (OPEN)", fn);
261                }
262            }
263        }
264    }
265    return 1;
266}
267
268int Interpreter::do_close(const char *str) {
269    ArgParser   args(str, at());
270    const char *fileID = args.get("fileID");
271
272    if (fileID) {
273        Output *known = find_output_for_ID(fileID);
274        if (known) {
275            known->close();
276            return 0;
277        }
278        printf_error(at(), "File '%s' not open or already closed", str);
279    }
280    return 1;
281}
282
283int Interpreter::do_out(const char *str) {
284    ArgParser   args(str, at());
285    const char *fileID = args.get("fileID");
286
287    if (fileID) {
288        Output *known = find_output_for_ID(fileID);
289        if (known) {
290            current_output = known;
291            return 0;
292        }
293        printf_error(at(), "File '%s' not open or already closed", str);
294    }
295    return 1;
296}
297
298inline char *get_ident(const char*& code, const Code *at) {
299    const char *start  = strstr(code, "$(");
300    if (!start) {
301        print_error(at, "expected to see '$(' while parsing for ident/selector");
302        return NULL;
303    }
304    const char *end = strchr(start+2, ')');
305    if (!end) {
306        print_error(at, "missing closing ')' after ident/selector");
307        return NULL;
308    }
309
310    code = end+1;
311
312    char *ident = copy_string_part(start+2, end-1);
313    if (!ident[0]) {
314        print_error(at, "Empty ident/selector");
315        freenull(ident);
316    }
317    return ident;
318}
319
320int Interpreter::do_moveto(const char *str) {
321    int   err      = 1;
322    char *selector = get_ident(str, at());
323    if (selector) {
324        LookupScope  scope = strrchr(selector, '/') ? LOOKUP_LIST : LOOKUP_BLOCK;
325        const Token *fo    = data.find_qualified_token(selector, scope);
326        if (!fo) {
327            printf_error(at(), "Could not find data '%s'", selector);
328        }
329        else {
330            data.set_cursor(fo);
331            err = 0;
332        }
333        free(selector);
334    }
335    return err;
336}
337
338void var_ref::const_violation() {
339    printf_error(Interpreter::instance->at(),
340                 "Attempt to modify write-protected variable '%s'", e->key);
341}
342
343int Interpreter::do_makeconst(const char *str) {
344    int   err   = 1;
345    char *ident = get_ident(str, at());
346
347    if (ident) {
348        var_ref var = get_local(ident);
349        if (!var) {
350            printf_error(at(), "undefined Ident '%s' in CONST (use CREATE first)", ident);
351        }
352        else {
353            var.write_protect();
354        }
355        free(ident);
356    }
357
358    return err;
359}
360
361int Interpreter::do_set(const char *str) {
362    int   err   = 1;
363    char *ident = get_ident(str, at());
364    if (ident) {
365        var_ref var = get_local(ident);
366        if (!var) {
367            printf_error(at(), "undefined Ident '%s' in SET (use CREATE first)", ident);
368        }
369        else {
370            SKIP_SPACE_LF(str);
371            if (str[0] == '=') {
372                ++str;
373                SKIP_SPACE_LF(str);
374            }
375            err = var.write(str);
376        }
377        free(ident);
378    }
379    return err;
380}
381
382int Interpreter::do_create(const char *str) {
383    char *var = get_ident(str, at());
384    const char *def = read_var(var);
385
386    int result = 1;
387    if (def) printf_error(at(), "Ident '%s' in CREATE already defined", var);
388    else {
389        SKIP_SPACE_LF(str);
390        if (*str == '=') {
391            str++;
392            SKIP_SPACE_LF(str);
393        }
394        write_var(var, str);
395        result = 0;
396    }
397    free(var);
398    return result;
399}
400
401int Interpreter::do_if(const char *str) {
402    int  err       = 0;
403    bool condition = false;
404
405    if (str) { // expression is not undefined
406        const char *cursor     = strpbrk(str, "=~<");
407        if (!cursor) condition = true; // if no operator found -> assume condition true (even if empty)
408        else {
409            char op = *cursor;
410            aisc_assert(cursor>str);
411            bool negate = cursor[-1] == '!';
412
413            const char *la = cursor - negate;
414            --la;
415            SKIP_SPACE_LF_BACKWARD(la, str);
416
417            char *left = copy_string_part(str, la);
418            cursor++;
419
420            char        right_buffer[strlen(cursor)+1];
421            const char *right = right_buffer;
422
423            while (cursor && !condition) {
424                SKIP_SPACE_LF(cursor);
425                const char *kom  = strchr(cursor, ',');
426                const char *next = kom;
427
428                if (kom) {
429                    next++;
430                    --kom;
431                    SKIP_SPACE_LF_BACKWARD(kom, str);
432
433                    int len    = kom-cursor+1;
434                    memcpy(right_buffer, cursor, len);
435                    right_buffer[len] = 0;
436                }
437                else {
438                    right = cursor;
439                }
440
441                switch (op) {
442                    case '=': condition = strcmp(left, right) == 0; break;
443                    case '~': condition = strstr(left, right) != 0; break;
444                    case '<': condition = atoi(left) < atoi(right); break;
445                    default:
446                        print_error(at(), formatted("Unhandled operator (op='%c') applied to '%s'", op, str));
447                        err = 1;
448                        break;
449                }
450
451                if (err) break;
452                if (negate) condition = !condition;
453                cursor = next;
454            }
455
456            free(left);
457        }
458    }
459   
460    if (!err && !condition) jump(at()->ELSE->next);
461    return err;
462}
463
464struct for_data {
465    char        *forstr;
466    const Token *forcursor;
467    long         forval;
468    long         forend;
469    for_data    *next;
470};
471
472inline for_data& add_for_data_to(const Code *co) {
473    for_data *fd = (for_data *)calloc(sizeof(for_data), 1);
474
475    fd->next = co->fd;
476    co->fd   = fd;
477
478    return *fd;
479}
480
481inline void remove_for_data_from(const Code *co) {
482    for_data *fd = co->fd;
483    free(fd->forstr);
484    co->fd = fd->next;
485    free(fd);
486}
487
488int Interpreter::do_push() {
489    if (stack_size++ >= STACKSIZE) {
490        print_error(at(), "Stack size exceeded");
491        return 1;
492    }
493
494    Stack *st = (Stack *)calloc(sizeof(Stack), 1);
495
496    st->cursor = data.get_cursor();
497    st->pc     = at();
498    st->hs     = new hash(HASHSIZE);
499    st->next   = stack;
500
501    stack = st;
502   
503    return 0;
504}
505
506void Interpreter::pop() {
507    aisc_assert(stack_size>0);
508
509    Stack *st = stack;
510    if (st) {
511        delete st->hs;
512        data.set_cursor(st->cursor);
513        stack = st->next;
514        free(st);
515        stack_size--;
516    }
517}
518
519int Interpreter::do_pop() {
520    if (stack_size<2) {
521        print_error(at(), "Nothing to Pop");
522        return 1;
523    }
524    pop();
525    return 0;
526}
527
528int Interpreter::do_gosub(const char *str) {
529    int result = 1;
530    if (do_push() == 0) {
531        char *params;
532        char *fun_name;
533
534        {
535            const char *s;
536            for (s = str; !is_SPACE_SEP_LF_EOS(*s); s++) ;
537
538            fun_name = copy_string_part(str, s-1);
539
540            if (*s) {
541                s++;
542                SKIP_SPACE_LF(s);
543                params = strdup(s);
544            }
545            else {
546                params = NULL;
547            }
548        }
549
550        const Code *fun = find_fun(fun_name);
551        if (!fun) {
552            printf_error(at(), "Function '%s' not found", fun_name);
553        }
554        else {
555            bool        eval_failed;
556            char       *fpara_eval = Expression(data, fun->source, fun->str, false).evaluate(eval_failed);
557
558            const char *fpara  = fpara_eval;
559            SKIP_SPACE_LF(fpara);
560            if (!*fpara) fpara = 0;
561
562            char       *npara  = 0;
563            const char *nfpara = 0;
564
565            int err = eval_failed;
566            for (char *para = params; !err && para; para=npara, fpara=nfpara) {
567                if (!fpara) {
568                    printf_error(at(), "Too many Parameters '%s'", para);
569                    printf_error(fun, "in call to '%s'", fun_name);
570
571                    err = -1;
572                }
573                else {
574                    {
575                        char *s;
576                        for (s = para; !is_SEP_LF_EOS(*s); s++) ;
577                        if (*s) {
578                            *s = 0;
579                            npara = s+1;
580                            SKIP_SPACE_LF(npara);
581                        }
582                        else npara = NULL;
583                    }
584
585                    char *fpara_name;
586                    {
587                        const char *s;
588                        for (s = fpara; !is_SEP_LF_EOS(*s); s++) ;
589                        fpara_name = copy_string_part(fpara, s-1);
590                        if (*s) {
591                            nfpara = s+1;
592                            SKIP_SPACE_LF(nfpara);
593                        }
594                        else nfpara = NULL;
595                    }
596
597                    char *para_eval = Expression(data, at()->source, para, false).evaluate(eval_failed);
598
599                    if (eval_failed) {
600                        print_error(at(), formatted("Could not evaluate expression '%s' for parameter '%s'", para, fpara_name));
601                    }
602                    else {
603                        const char *s = read_var(fpara_name);
604                        if (s) {
605                            print_error(fun, formatted("duplicated formal parameter '%s' in definition of function '%s'", fpara_name, fun_name));
606                            err = -1;
607                        }
608                        else {
609                            write_var(fpara_name, para_eval);
610                        }
611                    }
612                    free(para_eval);
613                    free(fpara_name);
614                }
615            }
616
617            if (!err && fpara) {
618                printf_error(at(), "Missing parameter '%s'", fpara);
619                printf_error(fun, "in call to '%s'", fun_name);
620                err = -1;
621            }
622
623            if (!err) jump(fun->next);
624            result = err;
625            free(fpara_eval);
626        }
627        free(fun_name);
628        free(params);
629    }
630    return result;
631}
632
633int Interpreter::do_goto(const char *str) {
634    const Code *func = find_fun(str);
635    if (!func) {
636        printf_error(at(), "Function '%s' not found", str);
637        return 1;
638    }
639    jump(func->next);
640    return 0;
641}
642
643int Interpreter::do_return() {
644    jump(stack->pc->next);
645    return do_pop();
646}
647
648int Interpreter::do_for(const char *str) {
649    int   err   = 1;
650    char *ident = get_ident(str, at());
651    if (ident) {
652        const char *eq = strchr(str, '=');
653        if (eq) {
654            ++eq;
655            SKIP_SPACE_LF(eq);
656
657            const char *to = strstr(eq, "TO");
658            if (!to) print_error(at(), "TO not found in FOR - expecting e.g. 'FOR $(i) = a TO b'");
659            else {
660                to  += 2;
661                SKIP_SPACE_LF(to);
662
663                for_data& fd = add_for_data_to(pc);
664                fd.forval    = atol(eq);
665                fd.forend    = atol(to);
666
667                if (fd.forval > fd.forend) {
668                    nextpc = pc->NEXT->next;
669                    remove_for_data_from(pc);
670                    err = 0;
671                }
672                else {
673                    var_ref var = get_local(ident);
674                    if (!var) {
675                        printf_error(at(), "Undefined Ident '%s' in FOR (use CREATE first)", ident);
676                    }
677                    else {
678                        err       = var.write(formatted("%li", fd.forval));
679                        fd.forstr = strdup(ident);
680                    }
681                }
682            }
683        }
684        else {
685            const char  *= strrchr(ident, '/');
686            const Token *fo = data.find_qualified_token(ident, p ? LOOKUP_LIST : LOOKUP_BLOCK);
687            if (!fo) {
688                nextpc = pc->NEXT->next;
689            }
690            else {
691                for_data& fd = add_for_data_to(pc);
692                fd.forstr    = strdup(p ? p+1 : ident);
693                fd.forcursor = data.get_cursor();
694                data.set_cursor(fo);
695            }
696            err = 0;
697        }
698       
699        free(ident);
700    }
701    return err;
702}
703
704int Interpreter::do_next() {
705    // handles NEXT (end of for-loop) and CONTINUE (loop from inside)
706
707    for_data& fd = *pc->FOR->fd;
708    if (fd.forcursor) {
709        const Token *fo = data.find_token(data.get_cursor(), fd.forstr, LOOKUP_BLOCK_REST);
710        if (fo) {
711            nextpc = pc->FOR->next;
712            data.set_cursor(fo);
713            return 0;
714        }
715        data.set_cursor(fd.forcursor);
716    }
717    else {
718        if (fd.forval < fd.forend) {
719            fd.forval++;
720            nextpc = pc->FOR->next;
721            return get_local(fd.forstr).write(formatted("%li", fd.forval));
722        }
723
724    }
725    nextpc = pc->FOR->ENDFOR->next;
726    remove_for_data_from(pc->FOR);
727    return 0;
728}
729
730// -------------------
731//      Dispatcher
732
733typedef int (Interpreter::*NoArgFun)();
734typedef int (Interpreter::*ArgFun)(const char *);
735
736enum CallMode {
737    STANDARD_CALL = 0,
738
739    // bit values:
740    DONT_EVAL           = 1,
741    EVAL_VAR_DECL       = 2,
742    EVAL_PLAIN          = 4,
743    TERMINATED_ON_ERROR = 8,
744};
745
746const int DISPATCH_LIMIT = 5000000; // abort after 5mio dispatches to one command-type (DEBUG only)
747
748class Command {
749public:
750    virtual ~Command() {}
751    virtual bool matches(const char *code) const   = 0;
752    virtual int call(Interpreter& interpret) const = 0;
753
754    const Location& pc() const {
755        return Interpreter::instance->at()->source;
756    }
757};
758struct NoSuchCommand : public Command {
759    virtual bool matches(const char *) const { return true; } // catch all
760    virtual int call(Interpreter& interpret) const {
761        printf_error(interpret.at(), "Unknown command '%s'", interpret.at()->str);
762        return -1; // bail out
763    }
764};
765class NamedCommand : public Command, virtual Noncopyable {
766    const char *name;
767    int         len;
768    mutable int dispatched;
769protected:
770    virtual int check_result(int res, char *evaluated_args) const {
771        dispatched++;
772        if (dispatched>DISPATCH_LIMIT) {
773            print_error(&pc(), "possible deadlock detected");
774            res = -1;
775        }
776#if defined(TRACE)
777        pc().start_message("TRACE");
778        if (res) fprintf(stderr, "[result=%i]", res);
779        fputs(name, stderr);
780        if (evaluated_args) {
781            fputc(' ', stderr);
782            fputs(evaluated_args, stderr);
783        }
784        fputc('\n', stderr);
785       
786#endif
787        free(evaluated_args);
788        return res;
789    }
790public:
791    NamedCommand(const char *cmd) : name(cmd), len(strlen(name)), dispatched(0) {}
792    bool matches(const char *code) const { return strncmp(code, name, len) == 0; }
793    int length() const { return len; }
794};
795
796class SimpleCmmd : public NamedCommand {
797    NoArgFun fun;
798public:
799    SimpleCmmd(const char *cmd, NoArgFun fun_) : NamedCommand(cmd), fun(fun_) {}
800    virtual int call(Interpreter& interpret) const {
801        return check_result((interpret.*fun)(), NULL);
802    }
803};
804
805class ArgCommand : public NamedCommand {
806    ArgFun   fun;
807    CallMode emode;
808
809    char *eval(Interpreter& interpret, bool& failed) const {
810        int offset = length();
811        if (!(emode&EVAL_PLAIN)) offset++;
812
813        if (emode & DONT_EVAL) {
814            failed = false;
815            return strdup(interpret.at()->str+offset);
816        }
817
818        Expression expr(interpret.get_data(), interpret.at()->source, interpret.at()->str+offset, false);
819        return (emode&EVAL_VAR_DECL)
820            ? expr.evalVarDecl(failed)
821            : expr.evaluate(failed);
822    }
823    virtual int check_result(int res, char *evaluated_args) const {
824        return NamedCommand::check_result(res && (emode&TERMINATED_ON_ERROR) ? -1 : res, evaluated_args);
825    }
826   
827public:
828    ArgCommand(const char *cmd, ArgFun fun_, CallMode emode_ = STANDARD_CALL)
829        : NamedCommand(cmd), fun(fun_), emode(emode_) {}
830    virtual int call(Interpreter& interpret) const {
831        int   res  = 1;
832        bool eval_failed;
833        char *args = eval(interpret, eval_failed);
834        if (args && !eval_failed)  {
835            char *trimmed = args;
836            if (!(emode&EVAL_PLAIN)) SKIP_SPACE_LF(trimmed);
837           
838            res = (interpret.*fun)(trimmed);
839        }
840        else {
841            print_error(interpret.at(), "Failed to evaluate arguments");
842            res = -1;
843        }
844        return check_result(res, args);
845    }
846};
847
848
849void Interpreter::command_table_setup(bool setup) {
850    if (setup) {
851        command_table = new Command*[MAX_COMMANDS+1];
852
853        int i = 0;
854       
855        command_table[i++] = new ArgCommand("MOVETO",   &Interpreter::do_moveto,        EVAL_VAR_DECL);
856        command_table[i++] = new ArgCommand("SET",      &Interpreter::do_set,           EVAL_VAR_DECL);
857        command_table[i++] = new ArgCommand("CONST",    &Interpreter::do_makeconst,     EVAL_VAR_DECL);
858        command_table[i++] = new ArgCommand("CREATE",   &Interpreter::do_create,        EVAL_VAR_DECL);
859
860        command_table[i++] = new ArgCommand("PRINT",    &Interpreter::do_write_current, EVAL_PLAIN);
861        command_table[i++] = new ArgCommand("P ",       &Interpreter::do_write_current, EVAL_PLAIN);
862        command_table[i++] = new ArgCommand("PP",       &Interpreter::do_write_stdout);
863        command_table[i++] = new SimpleCmmd("--",       &Interpreter::do_newline);
864        command_table[i++] = new SimpleCmmd("PMSTART",  &Interpreter::do_write_maybe_start);
865        command_table[i++] = new SimpleCmmd("PMEND",    &Interpreter::do_write_maybe_end);
866        command_table[i++] = new ArgCommand("PM",       &Interpreter::do_write_maybe);
867
868        command_table[i++] = new ArgCommand("GOSUB",    &Interpreter::do_gosub,         DONT_EVAL);
869        command_table[i++] = new ArgCommand("CALL",     &Interpreter::do_gosub,         DONT_EVAL);
870        command_table[i++] = new ArgCommand("GOTO",     &Interpreter::do_goto);
871        command_table[i++] = new SimpleCmmd("RETURN",   &Interpreter::do_return);
872        command_table[i++] = new SimpleCmmd("PUSH",     &Interpreter::do_push);
873        command_table[i++] = new SimpleCmmd("POP",      &Interpreter::do_pop);
874        command_table[i++] = new SimpleCmmd("CONTINUE", &Interpreter::do_next);
875
876        command_table[i++] = new ArgCommand("OPEN",     &Interpreter::do_open,         TERMINATED_ON_ERROR);
877        command_table[i++] = new ArgCommand("CLOSE",    &Interpreter::do_close);
878        command_table[i++] = new ArgCommand("OUT",      &Interpreter::do_out,          TERMINATED_ON_ERROR);
879
880        command_table[i++] = new ArgCommand("INDENT",   &Interpreter::do_indent);
881        command_table[i++] = new ArgCommand("TABSTOP",  &Interpreter::do_tabstop);
882        command_table[i++] = new ArgCommand("TAB",      &Interpreter::do_tab);
883        command_table[i++] = new SimpleCmmd("EXIT",     &Interpreter::do_exit);
884
885        command_table[i++] = new ArgCommand("DATA",     &Interpreter::do_data,         TERMINATED_ON_ERROR);
886        command_table[i++] = new ArgCommand("DUMPDATA", &Interpreter::do_dumpdata,     TERMINATED_ON_ERROR);
887        command_table[i++] = new ArgCommand("ERROR",    &Interpreter::do_error,        TERMINATED_ON_ERROR);
888        command_table[i++] = new ArgCommand("WARNING",  &Interpreter::do_warning);
889
890        command_table[i++] = new NoSuchCommand(); // should be last!
891        command_table[i++] = NULL;
892
893        aisc_assert(i<MAX_COMMANDS);
894    }
895    else { // cleanup
896        for (int i = 0; command_table[i]; ++i) delete command_table[i];
897        delete [] command_table;
898    }
899}
900
901const Command *Interpreter::find_command(const Code *co) {
902    for (int c = 0; command_table[c]; ++c) {
903        const Command& cmd = *command_table[c];
904        if (cmd.matches(co->str)) {
905            return &cmd;
906        }
907    }
908    aisc_assert(0); // NoSuchCommand missing ?
909    return NULL;
910}
911
912int Interpreter::run_program() {
913    parser.set_source(prg->source.get_path(), 1);
914
915    for (pc = prg; pc; pc = nextpc) {
916        nextpc = pc->next;
917
918        switch (pc->command) {
919            case IF: {
920                Expression  expr(data, pc->source, pc->str, true);
921                bool        eval_failed;
922                char       *val = expr.evaluate(eval_failed);
923                int         err = eval_failed;
924                if (!err) err = do_if(val);   // execute even with val == NULL!
925                free(val);
926                if  (err) return err;
927                break;
928            }
929
930            case ELSE:
931                nextpc = pc->IF->ENDIF->next;
932            case ENDIF:
933                break;
934
935            case FOR: {
936                Expression  expr(data, pc->source, pc->str, false);
937                bool        eval_failed;
938                char       *val   = expr.evalVarDecl(eval_failed);
939                bool        abort = !val || eval_failed || do_for(val);
940                free(val);
941                if (abort) return 1;
942                break;
943            }
944
945            case NEXT:
946                if (do_next()) return 1;
947            case ENDFOR:
948                break;
949
950            case FUNCTION:
951                print_error(at(), "fatal: ran into FUNCTION (missing EXIT?)");
952                break;
953
954                break;
955
956            case OTHER_CMD: {
957                int res = pc->cmd->call(*this);
958                if (res == -1) return 1;
959                break;
960            }
961
962            case LABEL:
963            case ELSEIF: //
964            case NO_COMMAND:
965                printf_error(at(), "internal error: Expected not to reach command type=%i", pc->command);
966                return 1;
967        }
968
969        if (!nextpc) { // end of execution ?
970            Location::announce_exit_pc(pc->source);
971        }
972    }
973    return Location::get_error_count();
974}
Note: See TracBrowser for help on using the browser.