source: tags/ms_r16q3/AISC/aisc_commands.c

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