source: branches/help/AISC/aisc_commands.c

Last change on this file was 18730, checked in by westram, 3 years ago
  • remove trailing whitespace from c source.
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 29.3 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 = NULp;
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 = NULp;
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) : NULp;
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      = NULp;
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 NULp;
307    }
308    const char *end = strchr(start+2, ')');
309    if (!end) {
310        print_error(at, "missing closing ')' after ident/selector");
311        return NULp;
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            if (la>str) {
419                --la;
420                SKIP_SPACE_LF_BACKWARD(la, str);
421            }
422
423            char *left = copy_string_part(str, la);
424            cursor++;
425
426            char        right_buffer[strlen(cursor)+1];
427            const char *right = right_buffer;
428
429            while (cursor && !condition) {
430                SKIP_SPACE_LF(cursor);
431                const char *kom  = strchr(cursor, ',');
432                const char *next = kom;
433
434                if (kom) {
435                    next++;
436                    --kom;
437                    aisc_assert(kom>=str);
438                    SKIP_SPACE_LF_BACKWARD(kom, str);
439
440                    int len    = kom-cursor+1;
441                    memcpy(right_buffer, cursor, len);
442                    right_buffer[len] = 0;
443                }
444                else {
445                    right = cursor;
446                }
447
448                switch (op) {
449                    case '=': condition = strcmp(left, right) == 0; break;
450                    case '~': condition = strstr(left, right) != NULp; break;
451                    case '<': condition = atoi(left) < atoi(right); break;
452                    default:
453                        print_error(at(), formatted("Unhandled operator (op='%c') applied to '%s'", op, str));
454                        err = 1;
455                        break;
456                }
457
458                if (err) break;
459                if (negate) condition = !condition;
460                cursor = next;
461            }
462
463            free(left);
464        }
465    }
466
467    if (!err && !condition) jump(at()->ELSE->next);
468    return err;
469}
470
471struct for_data {
472    char        *forstr;
473    const Token *forcursor;
474    long         forval;
475    long         forend;
476    for_data    *next;
477};
478
479inline for_data& add_for_data_to(const Code *co) {
480    for_data *fd = (for_data *)calloc(sizeof(for_data), 1);
481
482    fd->next = co->fd;
483    co->fd   = fd;
484
485    return *fd;
486}
487
488inline void remove_for_data_from(const Code *co) {
489    for_data *fd = co->fd;
490    free(fd->forstr);
491    co->fd = fd->next;
492    free(fd);
493}
494
495int Interpreter::do_push() {
496    if (stack_size++ >= STACKSIZE) {
497        print_error(at(), "Stack size exceeded");
498        return 1;
499    }
500
501    Stack *st = (Stack *)calloc(sizeof(Stack), 1);
502
503    st->cursor = data.get_cursor();
504    st->pc     = at();
505    st->hs     = new hash(HASHSIZE);
506    st->next   = stack;
507
508    stack = st;
509
510    return 0;
511}
512
513void Interpreter::pop() {
514    aisc_assert(stack_size>0);
515
516    Stack *st = stack;
517    if (st) {
518        delete st->hs;
519        data.set_cursor(st->cursor);
520        stack = st->next;
521        free(st);
522        stack_size--;
523    }
524}
525
526int Interpreter::do_pop() {
527    if (stack_size<2) {
528        print_error(at(), "Nothing to Pop");
529        return 1;
530    }
531    pop();
532    return 0;
533}
534
535int Interpreter::do_gosub(const char *str) {
536    int result = 1;
537    if (do_push() == 0) {
538        char *params;
539        char *fun_name;
540
541        {
542            const char *s;
543            for (s = str; !is_SPACE_SEP_LF_EOS(*s); s++) ;
544
545            fun_name = copy_string_part(str, s-1);
546
547            if (*s) {
548                s++;
549                SKIP_SPACE_LF(s);
550                params = strdup(s);
551            }
552            else {
553                params = NULp;
554            }
555        }
556
557        const Code *fun = find_fun(fun_name);
558        if (!fun) {
559            printf_error(at(), "Function '%s' not found", fun_name);
560        }
561        else {
562            bool        eval_failed;
563            char       *fpara_eval = Expression(data, fun->source, fun->str, false).evaluate(eval_failed);
564
565            const char *fpara  = fpara_eval;
566            SKIP_SPACE_LF(fpara);
567            if (!*fpara) fpara = NULp;
568
569            char       *npara  = NULp;
570            const char *nfpara = NULp;
571
572            int err = eval_failed;
573            for (char *para = params; !err && para; para=npara, fpara=nfpara) {
574                if (!fpara) {
575                    printf_error(at(), "Too many Parameters '%s'", para);
576                    printf_error(fun, "in call to '%s'", fun_name);
577
578                    err = -1;
579                }
580                else {
581                    {
582                        char *s;
583                        for (s = para; !is_SEP_LF_EOS(*s); s++) ;
584                        if (*s) {
585                            *s = 0;
586                            npara = s+1;
587                            SKIP_SPACE_LF(npara);
588                        }
589                        else npara = NULp;
590                    }
591
592                    char *fpara_name;
593                    {
594                        const char *s;
595                        for (s = fpara; !is_SEP_LF_EOS(*s); s++) ;
596                        fpara_name = copy_string_part(fpara, s-1);
597                        if (*s) {
598                            nfpara = s+1;
599                            SKIP_SPACE_LF(nfpara);
600                        }
601                        else nfpara = NULp;
602                    }
603
604                    char *para_eval = Expression(data, at()->source, para, false).evaluate(eval_failed);
605
606                    if (eval_failed) {
607                        print_error(at(), formatted("Could not evaluate expression '%s' for parameter '%s'", para, fpara_name));
608                    }
609                    else {
610                        const char *s = read_var(fpara_name);
611                        if (s) {
612                            print_error(fun, formatted("duplicated formal parameter '%s' in definition of function '%s'", fpara_name, fun_name));
613                            err = -1;
614                        }
615                        else {
616                            write_var(fpara_name, para_eval);
617                        }
618                    }
619                    free(para_eval);
620                    free(fpara_name);
621                }
622            }
623
624            if (!err && fpara) {
625                printf_error(at(), "Missing parameter '%s'", fpara);
626                printf_error(fun, "in call to '%s'", fun_name);
627                err = -1;
628            }
629
630            if (!err) jump(fun->next);
631            result = err;
632            free(fpara_eval);
633        }
634        free(fun_name);
635        free(params);
636    }
637    return result;
638}
639
640int Interpreter::do_goto(const char *str) {
641    const Code *func = find_fun(str);
642    if (!func) {
643        printf_error(at(), "Function '%s' not found", str);
644        return 1;
645    }
646    jump(func->next);
647    return 0;
648}
649
650int Interpreter::do_return() {
651    jump(stack->pc->next);
652    return do_pop();
653}
654
655int Interpreter::do_for(const char *str) {
656    int   err   = 1;
657    char *ident = get_ident(str, at());
658    if (ident) {
659        const char *eq = strchr(str, '=');
660        if (eq) {
661            ++eq;
662            SKIP_SPACE_LF(eq);
663
664            const char *to = strstr(eq, "TO");
665            if (!to) print_error(at(), "TO not found in FOR - expecting e.g. 'FOR $(i) = a TO b'");
666            else {
667                to  += 2;
668                SKIP_SPACE_LF(to);
669
670                for_data& fd = add_for_data_to(pc);
671                fd.forval    = atol(eq);
672                fd.forend    = atol(to);
673
674                if (fd.forval > fd.forend) {
675                    nextpc = pc->NEXT->next;
676                    remove_for_data_from(pc);
677                    err = 0;
678                }
679                else {
680                    var_ref var = get_local(ident);
681                    if (!var) {
682                        printf_error(at(), "Undefined Ident '%s' in FOR (use CREATE first)", ident);
683                    }
684                    else {
685                        err       = var.write(formatted("%li", fd.forval));
686                        fd.forstr = strdup(ident);
687                    }
688                }
689                // cppcheck-suppress memleak (fd.forstr is released from Interpreter::do_next)
690            }
691        }
692        else {
693            const char  *= strrchr(ident, '/');
694            const Token *fo = data.find_qualified_token(ident, p ? LOOKUP_LIST : LOOKUP_BLOCK);
695            if (!fo) {
696                nextpc = pc->NEXT->next;
697            }
698            else {
699                for_data& fd = add_for_data_to(pc);
700                fd.forstr    = strdup(p ? p+1 : ident);
701                fd.forcursor = data.get_cursor();
702                data.set_cursor(fo);
703                // cppcheck-suppress memleak (fd.forstr is released from Interpreter::do_next)
704            }
705            err = 0;
706        }
707
708        free(ident);
709    }
710    return err;
711}
712
713int Interpreter::do_next() {
714    // handles NEXT (end of for-loop) and CONTINUE (loop from inside)
715
716    for_data& fd = *pc->FOR->fd;
717    if (fd.forcursor) {
718        const Token *fo = data.find_token(data.get_cursor(), fd.forstr, LOOKUP_BLOCK_REST);
719        if (fo) {
720            nextpc = pc->FOR->next;
721            data.set_cursor(fo);
722            return 0;
723        }
724        data.set_cursor(fd.forcursor);
725    }
726    else {
727        if (fd.forval < fd.forend) {
728            fd.forval++;
729            nextpc = pc->FOR->next;
730            return get_local(fd.forstr).write(formatted("%li", fd.forval));
731        }
732
733    }
734    nextpc = pc->FOR->ENDFOR->next;
735    remove_for_data_from(pc->FOR);
736    return 0;
737}
738
739// -------------------
740//      Dispatcher
741
742typedef int (Interpreter::*NoArgFun)();
743typedef int (Interpreter::*ArgFun)(const char *);
744
745enum CallMode {
746    STANDARD_CALL = 0,
747
748    // bit values:
749    DONT_EVAL           = 1,
750    EVAL_VAR_DECL       = 2,
751    EVAL_PLAIN          = 4,
752    TERMINATED_ON_ERROR = 8,
753};
754
755const int DISPATCH_LIMIT = 5000000; // abort after 5mio dispatches to one command-type (DEBUG only)
756
757struct Command {
758    virtual ~Command() {}
759    virtual bool matches(const char *code) const   = 0;
760    virtual int call(Interpreter& interpret) const = 0;
761
762    const Location& pc() const {
763        return Interpreter::instance->at()->source;
764    }
765};
766struct NoSuchCommand : public Command {
767    bool matches(const char *) const OVERRIDE { return true; } // catch all
768    int call(Interpreter& interpret) const OVERRIDE {
769        printf_error(interpret.at(), "Unknown command '%s'", interpret.at()->str);
770        return -1; // bail out
771    }
772};
773class NamedCommand : public Command, virtual Noncopyable {
774    const char *name;
775    int         len;
776    mutable int dispatched;
777protected:
778    virtual int check_result(int res, char *evaluated_args) const {
779        dispatched++;
780        if (dispatched>DISPATCH_LIMIT) {
781            print_error(&pc(), "possible deadlock detected");
782            res = -1;
783        }
784#if defined(TRACE)
785        pc().start_message("TRACE");
786        if (res) fprintf(stderr, "[result=%i]", res);
787        fputs(name, stderr);
788        if (evaluated_args) {
789            fputc(' ', stderr);
790            fputs(evaluated_args, stderr);
791        }
792        fputc('\n', stderr);
793
794#endif
795        free(evaluated_args);
796        return res;
797    }
798public:
799    NamedCommand(const char *cmd) : name(cmd), len(strlen(name)), dispatched(0) {}
800    bool matches(const char *code) const OVERRIDE { return strncmp(code, name, len) == 0; }
801    int length() const { return len; }
802};
803
804class SimpleCmmd FINAL_TYPE : public NamedCommand {
805    NoArgFun fun;
806public:
807    SimpleCmmd(const char *cmd, NoArgFun fun_) : NamedCommand(cmd), fun(fun_) {}
808    int call(Interpreter& interpret) const OVERRIDE {
809        return check_result((interpret.*fun)(), NULp);
810    }
811};
812
813class ArgCommand FINAL_TYPE : public NamedCommand {
814    ArgFun   fun;
815    CallMode emode;
816
817    char *eval(Interpreter& interpret, bool& failed) const {
818        int offset = length();
819        if (!(emode&EVAL_PLAIN)) offset++;
820
821        if (emode & DONT_EVAL) {
822            failed = false;
823            return strdup(interpret.at()->str+offset);
824        }
825
826        Expression expr(interpret.get_data(), interpret.at()->source, interpret.at()->str+offset, false);
827        return (emode&EVAL_VAR_DECL)
828            ? expr.evalVarDecl(failed)
829            : expr.evaluate(failed);
830    }
831    int check_result(int res, char *evaluated_args) const OVERRIDE {
832        return NamedCommand::check_result(res && (emode&TERMINATED_ON_ERROR) ? -1 : res, evaluated_args);
833    }
834
835public:
836    ArgCommand(const char *cmd, ArgFun fun_, CallMode emode_ = STANDARD_CALL)
837        : NamedCommand(cmd), fun(fun_), emode(emode_) {}
838    int call(Interpreter& interpret) const OVERRIDE {
839        int   res  = 1;
840        bool eval_failed;
841        char *args = eval(interpret, eval_failed);
842        if (args && !eval_failed)  {
843            char *trimmed = args;
844            if (!(emode&EVAL_PLAIN)) SKIP_SPACE_LF(trimmed);
845
846            res = (interpret.*fun)(trimmed);
847        }
848        else {
849            print_error(interpret.at(), "Failed to evaluate arguments");
850            res = -1;
851        }
852        return check_result(res, args);
853    }
854};
855
856
857void Interpreter::command_table_setup(bool setup) {
858    if (setup) {
859        command_table = new Command*[MAX_COMMANDS+1];
860
861        int i = 0;
862
863        command_table[i++] = new ArgCommand("MOVETO",   &Interpreter::do_moveto,        EVAL_VAR_DECL);
864        command_table[i++] = new ArgCommand("SET",      &Interpreter::do_set,           EVAL_VAR_DECL);
865        command_table[i++] = new ArgCommand("CONST",    &Interpreter::do_makeconst,     EVAL_VAR_DECL);
866        command_table[i++] = new ArgCommand("CREATE",   &Interpreter::do_create,        EVAL_VAR_DECL);
867
868        command_table[i++] = new ArgCommand("PRINT",    &Interpreter::do_write_current, EVAL_PLAIN);
869        command_table[i++] = new ArgCommand("P ",       &Interpreter::do_write_current, EVAL_PLAIN);
870        command_table[i++] = new ArgCommand("PP",       &Interpreter::do_write_stdout);
871        command_table[i++] = new SimpleCmmd("--",       &Interpreter::do_newline);
872        command_table[i++] = new SimpleCmmd("PMSTART",  &Interpreter::do_write_maybe_start);
873        command_table[i++] = new SimpleCmmd("PMEND",    &Interpreter::do_write_maybe_end);
874        command_table[i++] = new ArgCommand("PM",       &Interpreter::do_write_maybe);
875
876        command_table[i++] = new ArgCommand("GOSUB",    &Interpreter::do_gosub,         DONT_EVAL);
877        command_table[i++] = new ArgCommand("CALL",     &Interpreter::do_gosub,         DONT_EVAL);
878        command_table[i++] = new ArgCommand("GOTO",     &Interpreter::do_goto);
879        command_table[i++] = new SimpleCmmd("RETURN",   &Interpreter::do_return);
880        command_table[i++] = new SimpleCmmd("PUSH",     &Interpreter::do_push);
881        command_table[i++] = new SimpleCmmd("POP",      &Interpreter::do_pop);
882        command_table[i++] = new SimpleCmmd("CONTINUE", &Interpreter::do_next);
883
884        command_table[i++] = new ArgCommand("OPEN",     &Interpreter::do_open,         TERMINATED_ON_ERROR);
885        command_table[i++] = new ArgCommand("CLOSE",    &Interpreter::do_close);
886        command_table[i++] = new ArgCommand("OUT",      &Interpreter::do_out,          TERMINATED_ON_ERROR);
887
888        command_table[i++] = new ArgCommand("INDENT",   &Interpreter::do_indent);
889        command_table[i++] = new ArgCommand("TABSTOP",  &Interpreter::do_tabstop);
890        command_table[i++] = new ArgCommand("TAB",      &Interpreter::do_tab);
891        command_table[i++] = new SimpleCmmd("EXIT",     &Interpreter::do_exit);
892
893        command_table[i++] = new ArgCommand("DATA",     &Interpreter::do_data,         TERMINATED_ON_ERROR);
894        command_table[i++] = new ArgCommand("DUMPDATA", &Interpreter::do_dumpdata,     TERMINATED_ON_ERROR);
895        command_table[i++] = new ArgCommand("ERROR",    &Interpreter::do_error,        TERMINATED_ON_ERROR);
896        command_table[i++] = new ArgCommand("WARNING",  &Interpreter::do_warning);
897
898        command_table[i++] = new NoSuchCommand; // should be last!
899        command_table[i++] = NULp;
900
901        aisc_assert(i<MAX_COMMANDS);
902    }
903    else { // cleanup
904        for (int i = 0; command_table[i]; ++i) delete command_table[i];
905        delete [] command_table;
906    }
907}
908
909const Command *Interpreter::find_command(const Code *co) {
910    for (int c = 0; command_table[c]; ++c) {
911        const Command& cmd = *command_table[c];
912        if (cmd.matches(co->str)) {
913            return &cmd;
914        }
915    }
916    aisc_assert(0); // NoSuchCommand missing ?
917    return NULp;
918}
919
920int Interpreter::run_program() {
921    parser.set_source(prg->source.get_path(), 1);
922
923    for (pc = prg; pc; pc = nextpc) {
924        nextpc = pc->next;
925
926        switch (pc->command) {
927            case CT_IF: {
928                Expression  expr(data, pc->source, pc->str, true);
929                bool        eval_failed;
930                char       *val = expr.evaluate(eval_failed);
931                int         err = eval_failed;
932                if (!err) err = do_if(val); // execute even if 'val' is NULp!
933                free(val);
934                if  (err) return err;
935                break;
936            }
937
938            case CT_ELSE:
939                nextpc = pc->IF->ENDIF->next;
940            case CT_ENDIF:
941                break;
942
943            case CT_FOR: {
944                Expression  expr(data, pc->source, pc->str, false);
945                bool        eval_failed;
946                char       *val   = expr.evalVarDecl(eval_failed);
947                bool        abort = !val || eval_failed || do_for(val);
948                free(val);
949                if (abort) return 1;
950                break;
951            }
952
953            case CT_NEXT:
954                if (do_next()) return 1;
955            case CT_ENDFOR:
956                break;
957
958            case CT_FUNCTION:
959                print_error(at(), "fatal: ran into FUNCTION (missing EXIT?)");
960                break;
961
962                break;
963
964            case CT_OTHER_CMD: {
965                int res = pc->cmd->call(*this);
966                if (res == -1) return 1;
967                break;
968            }
969
970            case CT_LABEL:
971            case CT_ELSEIF:
972            case NO_COMMAND:
973                printf_error(at(), "internal error: Expected not to reach command type=%i", pc->command);
974                return 1;
975        }
976
977        if (!nextpc) { // end of execution ?
978            Location::announce_exit_pc(pc->source);
979        }
980    }
981    return Location::get_error_count();
982}
983
Note: See TracBrowser for help on using the repository browser.