[med-svn] [Git][med-team/libarb][upstream] New upstream version 6.0.6

Andreas Tille gitlab at salsa.debian.org
Thu Jun 6 21:21:33 BST 2019



Andreas Tille pushed to branch upstream at Debian Med / libarb


Commits:
93facad9 by Andreas Tille at 2019-06-06T13:53:41Z
New upstream version 6.0.6
- - - - -


16 changed files:

- + AISC/Makefile
- + AISC/aisc.c
- + AISC/aisc.h
- + AISC/aisc_commands.c
- + AISC/aisc_def.h
- + AISC/aisc_eval.c
- + AISC/aisc_eval.h
- + AISC/aisc_inline.h
- + AISC/aisc_interpreter.h
- + AISC/aisc_location.h
- + AISC/aisc_mix.c
- + AISC/aisc_parser.c
- + AISC/aisc_parser.h
- + AISC/aisc_proto.h
- + AISC/aisc_token.h
- + AISC/aisc_var_ref.c


Changes:

=====================================
AISC/Makefile
=====================================
@@ -0,0 +1,134 @@
+# for variables passed from parent makefile see ../SOURCE_TOOLS/parent_make.txt
+
+.SUFFIXES: .o .c .depend
+SOURCES = $(wildcard *.c)
+OBJECTS = $(subst .c,.o,$(SOURCES))
+
+BINARY=aisc
+
+LOCAL_DEFINES=-DSIMPLE_ARB_ASSERT
+LOCAL_MAKEDEPENDFLAGS=$(MAKEDEPENDFLAGS) $(LOCAL_DEFINES)
+
+$(MAIN): proto
+	$(MAKE) $(BINARY)
+
+$(BINARY): $(OBJECTS)
+	$(LINK_EXECUTABLE) $@ $(OBJECTS) $(EXECLIBS)
+
+.c.o:
+	$(A_CXX) -x c++ $(cflags) $(cxxflags) $(LOCAL_DEFINES) -c $< $(CXX_INCLUDES) $(POST_COMPILE)
+
+DEPENDS = $(OBJECTS:.o=.depend)
+depends: $(DEPENDS)
+	@cat $(DEPENDS) | grep -v '^#' >>Makefile
+	@rm $(DEPENDS)
+$(DEPENDS): depend.init
+depend.init:
+	$(MAKEDEPEND) $(LOCAL_MAKEDEPENDFLAGS) 2>/dev/null # remove dependencies
+.c.depend:
+	$(MAKEDEPEND) -f- $(LOCAL_MAKEDEPENDFLAGS) $< 2>/dev/null >$@
+
+clean:
+	rm -f $(OBJECTS) $(BINARY)
+
+proto:
+	../AISC_MKPTPS/aisc_mkpt -P -G -w aisc_proto.h *.c >aisc_proto.h.tmp
+	../SOURCE_TOOLS/mv_if_diff aisc_proto.h.tmp aisc_proto.h
+
+# DO NOT DELETE THIS LINE -- make depend depends on it.
+
+# Do not add dependencies manually - use 'make depend' in $ARBHOME
+# For formatting issues see SOURCE_TOOLS/fix_depends.pl (from main)
+
+aisc.o: aisc_def.h
+aisc.o: aisc_inline.h
+aisc.o: aisc_interpreter.h
+aisc.o: aisc_location.h
+aisc.o: aisc_parser.h
+aisc.o: aisc_proto.h
+aisc.o: aisc_token.h
+aisc.o: $(ARBHOME)/INCLUDE/arb_assert.h
+aisc.o: $(ARBHOME)/INCLUDE/arb_simple_assert.h
+aisc.o: $(ARBHOME)/INCLUDE/arbtools.h
+aisc.o: $(ARBHOME)/INCLUDE/attributes.h
+aisc.o: $(ARBHOME)/INCLUDE/cxxforward.h
+aisc.o: $(ARBHOME)/INCLUDE/dupstr.h
+aisc.o: $(ARBHOME)/INCLUDE/gccver.h
+aisc.o: $(ARBHOME)/INCLUDE/test_global.h
+
+aisc_commands.o: aisc_def.h
+aisc_commands.o: aisc_eval.h
+aisc_commands.o: aisc_inline.h
+aisc_commands.o: aisc_interpreter.h
+aisc_commands.o: aisc_location.h
+aisc_commands.o: aisc_parser.h
+aisc_commands.o: aisc_proto.h
+aisc_commands.o: aisc_token.h
+aisc_commands.o: $(ARBHOME)/INCLUDE/arb_assert.h
+aisc_commands.o: $(ARBHOME)/INCLUDE/arb_simple_assert.h
+aisc_commands.o: $(ARBHOME)/INCLUDE/arbtools.h
+aisc_commands.o: $(ARBHOME)/INCLUDE/attributes.h
+aisc_commands.o: $(ARBHOME)/INCLUDE/cxxforward.h
+aisc_commands.o: $(ARBHOME)/INCLUDE/dupstr.h
+aisc_commands.o: $(ARBHOME)/INCLUDE/gccver.h
+aisc_commands.o: $(ARBHOME)/INCLUDE/test_global.h
+
+aisc_eval.o: aisc_def.h
+aisc_eval.o: aisc_eval.h
+aisc_eval.o: aisc_inline.h
+aisc_eval.o: aisc_location.h
+aisc_eval.o: aisc_proto.h
+aisc_eval.o: $(ARBHOME)/INCLUDE/arb_assert.h
+aisc_eval.o: $(ARBHOME)/INCLUDE/arb_simple_assert.h
+aisc_eval.o: $(ARBHOME)/INCLUDE/arbtools.h
+aisc_eval.o: $(ARBHOME)/INCLUDE/attributes.h
+aisc_eval.o: $(ARBHOME)/INCLUDE/cxxforward.h
+aisc_eval.o: $(ARBHOME)/INCLUDE/dupstr.h
+aisc_eval.o: $(ARBHOME)/INCLUDE/gccver.h
+aisc_eval.o: $(ARBHOME)/INCLUDE/test_global.h
+
+aisc_mix.o: aisc_def.h
+aisc_mix.o: aisc_inline.h
+aisc_mix.o: aisc_interpreter.h
+aisc_mix.o: aisc_location.h
+aisc_mix.o: aisc_parser.h
+aisc_mix.o: aisc_proto.h
+aisc_mix.o: aisc_token.h
+aisc_mix.o: $(ARBHOME)/INCLUDE/arb_assert.h
+aisc_mix.o: $(ARBHOME)/INCLUDE/arb_simple_assert.h
+aisc_mix.o: $(ARBHOME)/INCLUDE/arbtools.h
+aisc_mix.o: $(ARBHOME)/INCLUDE/attributes.h
+aisc_mix.o: $(ARBHOME)/INCLUDE/cxxforward.h
+aisc_mix.o: $(ARBHOME)/INCLUDE/dupstr.h
+aisc_mix.o: $(ARBHOME)/INCLUDE/gccver.h
+aisc_mix.o: $(ARBHOME)/INCLUDE/test_global.h
+
+aisc_parser.o: aisc_def.h
+aisc_parser.o: aisc_inline.h
+aisc_parser.o: aisc_location.h
+aisc_parser.o: aisc_parser.h
+aisc_parser.o: aisc_token.h
+aisc_parser.o: $(ARBHOME)/INCLUDE/arb_assert.h
+aisc_parser.o: $(ARBHOME)/INCLUDE/arb_simple_assert.h
+aisc_parser.o: $(ARBHOME)/INCLUDE/arbtools.h
+aisc_parser.o: $(ARBHOME)/INCLUDE/attributes.h
+aisc_parser.o: $(ARBHOME)/INCLUDE/cxxforward.h
+aisc_parser.o: $(ARBHOME)/INCLUDE/dupstr.h
+aisc_parser.o: $(ARBHOME)/INCLUDE/gccver.h
+aisc_parser.o: $(ARBHOME)/INCLUDE/test_global.h
+
+aisc_var_ref.o: aisc_def.h
+aisc_var_ref.o: aisc_inline.h
+aisc_var_ref.o: aisc_interpreter.h
+aisc_var_ref.o: aisc_location.h
+aisc_var_ref.o: aisc_parser.h
+aisc_var_ref.o: aisc_proto.h
+aisc_var_ref.o: aisc_token.h
+aisc_var_ref.o: $(ARBHOME)/INCLUDE/arb_assert.h
+aisc_var_ref.o: $(ARBHOME)/INCLUDE/arb_simple_assert.h
+aisc_var_ref.o: $(ARBHOME)/INCLUDE/arbtools.h
+aisc_var_ref.o: $(ARBHOME)/INCLUDE/attributes.h
+aisc_var_ref.o: $(ARBHOME)/INCLUDE/cxxforward.h
+aisc_var_ref.o: $(ARBHOME)/INCLUDE/dupstr.h
+aisc_var_ref.o: $(ARBHOME)/INCLUDE/gccver.h
+aisc_var_ref.o: $(ARBHOME)/INCLUDE/test_global.h


=====================================
AISC/aisc.c
=====================================
@@ -0,0 +1,386 @@
+// ================================================================
+/*                                                                  */
+//   File      : aisc.c
+//   Purpose   : ARB integrated source compiler
+/*                                                                  */
+//   Institute of Microbiology (Technical University Munich)
+//   http://www.arb-home.de
+/*                                                                  */
+// ================================================================
+
+#include "aisc_interpreter.h"
+#include <cctype>
+#include <list>
+#include <string>
+
+using namespace std;
+
+// AISC_MKPT_PROMOTE:#ifndef AISC_DEF_H
+// AISC_MKPT_PROMOTE:#include "aisc_def.h"
+// AISC_MKPT_PROMOTE:#endif
+
+char *read_aisc_file(const char *path, const Location *loc) {
+    char *buffer = 0;
+    FILE *input  = fopen(path, "rt");
+
+    if (!input) {
+        printf_error(loc, "file '%s' not found", path);
+    }
+    else {
+        if (fseek(input, 0, 2)==-1) {
+            printf_error(loc, "file '%s' not seekable", path);
+        }
+        else {
+            int data_size = (int)ftell(input);
+
+            if (data_size == 0) {
+                Location fileLoc(0, path);
+                print_error(&fileLoc, "file is empty");
+            }
+            else {
+                data_size++;
+                rewind(input);
+                buffer =  (char *)malloc(data_size+1);
+                data_size = fread(buffer, 1, data_size, input);
+                buffer[data_size] = 0;
+            }
+        }
+        fclose(input);
+    }
+    return buffer;
+}
+
+inline int max(int i, int j) { return i<j ? j : i; }
+
+void LineQueue::alignInto(LineQueue& dest) {
+    int offset = 0;
+    int len[count];
+
+    for (int i = 0; i<count; ++i) len[i] = strlen(queue[i]);
+
+    while (1) {
+        int max_mark_pos = -1;
+        int mark_pos[count];
+        for (int i = 0; i<count; ++i) {
+            char *line   = queue[i];
+            char *mark   = len[i]>offset ? strchr(line+offset, ALIGN_MARKER) : NULL;
+            mark_pos[i]  = mark ? mark-line : -1;
+            max_mark_pos = max(max_mark_pos, mark_pos[i]);
+        }
+        if (max_mark_pos == -1) break;
+
+        for (int i = 0; i<count; ++i) {
+            if (mark_pos[i] >= 0) {
+                int insert = max_mark_pos-mark_pos[i];
+                aisc_assert(insert >= 0);
+                if (insert >= 0) {
+                    int   new_len  = len[i]+insert;
+                    char *new_line = (char*)malloc(new_len+1);
+
+                    memcpy(new_line, queue[i], mark_pos[i]);
+                    memset(new_line+mark_pos[i], ' ', insert);
+                    strcpy(new_line+max_mark_pos, queue[i]+mark_pos[i]+1);
+
+                    len[i] = new_len;
+                    freeset(queue[i], new_line);
+                }
+            }
+        }
+
+        offset = max_mark_pos;
+    }
+
+    for (int i = 0; i<count; ++i) {
+        dest.add(queue[i]);
+        queue[i] = NULL;
+    }
+    count = 0;
+}
+
+struct queued_line {
+    string line;
+    int    indentation;
+    queued_line(const char *line_, int indent) : line(line_), indentation(indent) { }
+};
+
+typedef list<queued_line> PrintQueue;
+
+class PrintMaybe : virtual Noncopyable {
+    Output&          out;
+    const Location&  started_at;
+    bool             printed_sth;
+    PrintMaybe      *next;
+    PrintQueue       queue;
+
+public:
+    PrintMaybe(PrintMaybe *head, Output& out_, const Location& loc)
+        : out(out_),
+          started_at(loc), 
+          printed_sth(false),
+          next(head)
+    {
+    }
+    ~PrintMaybe() {
+    }
+
+    void add(const char *line) {
+        if (printed_sth) {
+            out.write(line);
+        }
+        else {
+            queue.push_back(queued_line(line, out.get_formatter().get_indent()));
+        }
+    }
+    void spool() {
+        aisc_assert(!printed_sth);
+        printed_sth = true;
+        if (!queue.empty()) {
+            Formatter& formatter  = out.get_formatter();
+            int        old_indent = formatter.get_indent();
+            for (PrintQueue::iterator i = queue.begin(); i != queue.end(); ++i) {
+                queued_line& ql = *i;
+                formatter.set_indent(ql.indentation);
+                out.write(ql.line.c_str());
+            }
+            formatter.set_indent(old_indent);
+            queue.clear();
+        }
+    }
+
+    void will_print() {
+        if (next) next->will_print();
+        if (!printed_sth) {
+            spool();
+        }
+    }
+
+    void not_destroyed_error() {
+        print_error(&started_at, "PMSTART without matching PMEND");
+        if (next) next->not_destroyed_error();
+    }
+
+    static void pop(PrintMaybe*& head) {
+        PrintMaybe *next = head->next;
+        head->next       = NULL;
+        delete head;
+        head             = next;
+    }
+};
+
+void Output::setup() {
+    fp    = NULL;
+    id    = NULL;
+    name  = NULL;
+    maybe = NULL;
+
+    have_open_loc = false;
+}
+void Output::cleanup() {
+    close_file();
+    free(id);
+    free(name);
+    if (maybe) {
+        maybe->not_destroyed_error();
+        delete maybe;
+    }
+}
+
+void Output::maybe_start() {
+    maybe = new PrintMaybe(maybe, *this, Interpreter::instance->at()->source);
+}
+int Output::maybe_write(const char *line) {
+    if (!maybe) {
+        print_error(Interpreter::instance->at(), "no PMSTART before PM");
+        return -1;
+    }
+    maybe->add(line);
+    return 0;
+}
+int Output::maybe_end() {
+    if (!maybe) {
+        print_error(Interpreter::instance->at(), "no PMSTART before PMEND");
+        return -1;
+    }
+    PrintMaybe::pop(maybe);
+    return 0;
+}
+
+int Output::write(const char *line) {
+    if (!inUse()) {
+        print_error(Interpreter::instance->at(), "Fatal: attempt to write to unused file");
+        return -1;
+    }
+
+    if (maybe) maybe->will_print();
+
+    int res = formatter.write(line);
+    formatter.flush(fp);
+    return res;
+}
+
+Formatter::Formatter()
+    : tabstop(4),
+      column(0),
+      indent(0),
+      printed_sth(false)
+{
+    set_tabstop(4);
+
+    for (int i = 0; i < 256; i++) {
+        outtab[i] = i;
+    }
+
+    outtab[(unsigned char)'n']  = '\n';
+    outtab[(unsigned char)'t']  = '\t';
+    outtab[(unsigned char)'|']  = ALIGN_MARKER;
+    outtab[(unsigned char)'0']  = 0;
+    outtab[(unsigned char)'1']  = 0;
+    outtab[(unsigned char)'2']  = 0;
+    outtab[(unsigned char)'3']  = 0;
+    outtab[(unsigned char)'4']  = 0;
+    outtab[(unsigned char)'5']  = 0;
+    outtab[(unsigned char)'6']  = 0;
+    outtab[(unsigned char)'7']  = 0;
+    outtab[(unsigned char)'8']  = 0;
+    outtab[(unsigned char)'9']  = 0;
+    outtab[(unsigned char)'\\'] = 0;
+}
+
+int Formatter::write(const char *str) {
+    int         no_nl = 0;
+    const char *p     = str;
+    char        c;
+
+    while ((c = *(p++))) {
+        if (c == '$') {
+            c = *(p++);
+            if (!c)
+                break;
+
+            if (!outtab[(unsigned)(c)]) {
+                if (c == '\\') {
+                    no_nl = 1;
+                }
+                else if (isdigit(c)) {
+                    int pos = tabs[c - '0'];
+                    tab_to_pos(pos);
+                }
+                continue;
+            }
+            else {
+                c = outtab[(unsigned)(c)];
+            }
+        }
+        if (c == '\t') {
+            int pos = ((column/tabstop)+1)*tabstop;
+            tab_to_pos(pos);
+        }
+        else if (c == '\n') {
+            if (no_nl) {
+                no_nl = 0;
+            }
+            else {
+                finish_line();
+            }
+        }
+        else if (c == '@') {
+            if (strncmp(p, "SETSOURCE", 9) == 0) { // skip '@SETSOURCE file, line@'
+                p = strchr(p, '@');
+                if (!p) {
+                    print_error(Interpreter::instance->at(), "expected '@' after '@SETSOURCE' (injected code)");
+                    return 1;
+                }
+                p++;
+            }
+            else {
+                print_char(c);
+            }
+        }
+        else {
+            print_char(c);
+        }
+    }
+    if (!no_nl) {
+        finish_line();
+    }
+    return 0;
+}
+
+bool Interpreter::set_data(const char *dataBlock, int offset_in_line) {
+    parser.set_line_start(dataBlock, offset_in_line);
+    parser.set_source(at()->source); // remove ? 
+    data.set_tokens(parser.parseTokenListBlock(dataBlock));
+    return data.get_tokens();
+}
+
+
+int Interpreter::launch(int argc, char ** argv) {
+    int exitcode = EXIT_FAILURE;
+
+    // save CL-arguments as variables (with names like 'argv[0]' ..) 
+    {
+        for (int i=0; i<argc; i++) {
+            write_var(formatted("argv[%i]", i), argv[i]);
+        }
+        write_var("argc", formatted("%i", argc));
+    }
+
+    {
+        char *buf = read_aisc_file(argv[1], NULL);
+        if (buf) {
+            prg = parser.parse_program(buf, argv[1]);
+            free(buf);
+        }
+    }
+    
+    if (!prg) {
+        fputs("Nothing to execute\n", stderr);
+    }
+    else {
+        if (compile_program()) {
+            fprintf(stderr, "Compilation of '%s' failed\n", argv[1]);
+        }
+        else {
+            if (run_program()) {
+                if (!Location::get_error_count()) {
+                    print_error(at(), "AISC compiler bailed out w/o error");
+                }
+                fputs("AISC reports errors\n", stderr);
+                for (int i = 0; i < OPENFILES; i++) output[i].close_and_unlink();
+                fflush(stdout);
+            }
+            else {
+                aisc_assert(!Location::get_error_count());
+                exitcode = EXIT_SUCCESS;
+            }
+        }
+    }
+
+    return exitcode;
+}
+
+int main(int argc, char ** argv) {
+    int exitcode = EXIT_FAILURE;
+
+    if (argc < 2) {
+        fprintf(stderr, "AISC - ARB integrated source compiler\n");
+        fprintf(stderr, "Usage: aisc [fileToCompile]+\n");
+        fprintf(stderr, "Error: missing file name\n");
+    }
+    else {
+        try {
+            exitcode = Interpreter().launch(argc, argv);
+        }
+        catch (const char *err) {
+            fprintf(stderr, "\nAISC: exception: %s [terminating]\n", err);
+            exitcode = EXIT_FAILURE;
+        }
+        catch (...) {
+            fprintf(stderr, "\nAISC: unknown exception [terminating]\n");
+            exitcode = EXIT_FAILURE;
+        }
+    }
+    return exitcode;
+}
+
+


=====================================
AISC/aisc.h
=====================================
@@ -0,0 +1,380 @@
+// ================================================================ //
+//                                                                  //
+//   File      : aisc.h                                             //
+//   Purpose   :                                                    //
+//                                                                  //
+//   Institute of Microbiology (Technical University Munich)        //
+//   http://www.arb-home.de/                                        //
+//                                                                  //
+// ================================================================ //
+
+#ifndef AISC_H
+#define AISC_H
+
+#ifndef ARB_SIMPLE_ASSERT_H
+#include <arb_simple_assert.h>
+#endif
+#ifndef DUPSTR_H
+#include <dupstr.h>
+#endif
+#ifndef _UNISTD_H
+#include <unistd.h>
+#endif
+#ifndef ATTRIBUTES_H
+#include <attributes.h>
+#endif
+
+
+#define aisc_assert(cond) arb_assert(cond)
+
+// ------------------------------------------------------------
+
+#define OPENFILES 16
+#define HASHSIZE  1024
+#define STACKSIZE 20
+
+enum aisc_commands {
+    no_command,
+    IF,
+    ENDIF,
+    ELSE,
+    FOR,
+    NEXT,
+    ENDFOR,
+    ELSEIF,
+    FUNCTION,
+    LABEL,
+    MAX_COM
+};
+
+struct hash_entry {
+    char       *key;
+    char       *val;
+    hash_entry *next;
+};
+struct hash {
+    int          size;
+    hash_entry **entries;
+};
+
+// ------------------------------------------------------------
+
+inline char *copy_string_part(const char *first, const char *last) {
+    int   size = last-first+1;
+    char *mem  = (char*)malloc(size+1);
+
+    memcpy(mem, first, size);
+    mem[size] = 0;
+
+    return mem;
+}
+
+// ------------------------------------------------------------
+// structures holding data read from *.aisc files
+
+class TokenList;
+class TokenListBlock;
+
+// --------------
+//      Token
+
+class Token {
+    Token     *next;                                // (owned)
+    TokenList *parent;
+
+    bool  isBlock;
+    char *key;
+    union {
+        TokenListBlock *sub;                        // (owned), NULL = empty block
+        char           *val;                        // NULL means ""
+    } content;
+
+public:
+    Token(const char *key_, const char *val_)
+        : next(NULL)
+        , parent(NULL)
+        , isBlock(false)
+        , key(strdup(key_))
+    {
+        content.val = val_ ? strdup(val_) : NULL;
+    }
+    Token(const char *key_, TokenListBlock *block_); // takes ownage of block_
+    ~Token();
+
+    void append(Token *tok) { next = tok; }
+    void set_parent(TokenList *list) { aisc_assert(!parent); parent = list; }
+
+    const char *get_key() const { return key; }
+
+    bool is_block() const { return isBlock; }
+    const TokenListBlock *get_content() const { aisc_assert(isBlock); return content.sub; }
+
+    bool has_value() const { return !is_block() && content.val; }
+    const char *get_value() const { aisc_assert(has_value()); return content.val; }
+
+    const Token *next_token() const { return next; }
+    const TokenList *parent_list() const { return parent; }
+    const Token *parent_block_token() const;
+};
+
+// ------------------
+//      TokenList
+
+class TokenList {
+    Token          *head;                           // list of tokens (owned)
+    Token          *tail;
+    TokenList      *next;                           // owned
+    TokenListBlock *parent;
+
+public:
+    TokenList() {
+        head   = NULL;
+        tail   = NULL;
+        next   = NULL;
+        parent = NULL;
+    }
+    ~TokenList() {
+        delete head;
+        delete next;
+    }
+
+    void append(Token *tok) {
+        if (!head) head = tok;
+        else tail->append(tok);
+
+        tail = tok;
+        tok->set_parent(this);
+    }
+    void append(TokenList *cmd) { next = cmd; }
+    void set_parent(TokenListBlock *block) { parent = block; }
+
+    bool empty() const { return !head; }
+    const Token *first_token() const { return head; }
+    const TokenList *next_list() const { return next; }
+    const TokenListBlock *parent_block() const { return parent; }
+};
+
+// -----------------------
+//      TokenListBlock
+
+class TokenListBlock {
+    TokenList   *head;                              // list of TokenLists (owned)
+    TokenList   *tail;
+    const Token *parent;
+
+public:
+    TokenListBlock() {
+        head   = NULL;
+        tail   = NULL;
+        parent = NULL;
+    }
+    ~TokenListBlock() { delete head; }
+
+    bool empty() const { return !head; }
+    void append(TokenList *cmd) {
+        if (!head) head = cmd;
+        else tail->append(cmd);
+
+        tail = cmd;
+        cmd->set_parent(this);
+    }
+    void set_block_token(Token *tok) { aisc_assert(!parent); parent = tok; }
+
+    const TokenList *first_list() const { return head; }
+    const Token *first_token() const { return head->first_token(); }
+    const Token *block_token() const { return parent; }
+};
+
+// ------------------------------------------------------------
+
+inline Token::Token(const char *key_, TokenListBlock *block_)
+    : next(NULL)
+    , parent(NULL)
+    , isBlock(true)
+    , key(strdup(key_))
+{
+    aisc_assert(block_);
+    content.sub = block_;
+    if (content.sub) {
+        content.sub->set_block_token(this);
+    }
+}
+inline Token::~Token() {
+    free(key);
+    if (isBlock) delete content.sub;
+    else free(content.val);
+    delete next;
+}
+inline const Token *Token::parent_block_token() const {
+    return parent_list()->parent_block()->block_token();
+}
+
+// ------------------------------------------------------------
+
+struct for_data {
+    char        *forstr;
+    const Token *forcursor;
+    long         forval;
+    long         forend;
+    for_data    *next;
+};
+
+
+
+typedef struct command_lines {
+    command_lines *next;
+    char          *str;
+    int            linenr;
+    char          *path;
+    aisc_commands  command;
+    for_data      *fd;
+    command_lines *IF;
+    command_lines *ELSE;
+    command_lines *ENDIF;
+    command_lines *FOR;
+    command_lines *NEXT;
+    command_lines *ENDFOR;
+} CL;
+
+struct stack {
+    const Token *cursor;
+    CL          *pc;
+    hash        *hs;
+    stack       *next;
+};
+
+class Output {
+    FILE *fp;
+    char *id;   // internal name used in AISC
+    char *name; // file-system name
+
+    bool wasOpened() const { return fp && !name; }
+
+    void close_file() {
+        if (wasOpened()) fclose(fp);
+        fp = NULL;
+    }
+    
+    void setup() { fp = NULL; id = NULL; name = NULL; }
+    void cleanup() { close_file(); free(id); free(name); }
+    void reuse() { cleanup(); setup(); }
+
+public:
+
+    Output() { setup(); }
+    ~Output() { cleanup(); }
+
+    bool inUse() const { return fp; }
+
+    void assign(FILE *fp_, const char *id_, const char *name_) {
+        aisc_assert(!inUse());
+        fp   = fp_;
+        id   = strdup(id_);
+        name = strdup(name_);
+    }
+    void assign_stdout(const char *id_) {
+        aisc_assert(!inUse());
+        fp   = stdout;
+        id   = strdup(id_);
+        name = NULL;
+    }
+
+    void close_and_unlink() {
+        close_file();
+        if (name) {
+            fprintf(stderr, "Unlinking %s\n", name);
+            unlink(name);
+        }
+        reuse();
+    }
+    void close() { reuse(); }
+
+    bool hasID(const char *Name) const { return id && strcmp(id, Name) == 0; }
+
+    void putchar(char c) {
+        putc(c, fp);
+    }
+};
+
+struct global {
+    int             error_flag;
+    char            outtab[256];
+    const Token    *cursor;
+    TokenListBlock *root;
+    CL             *prg;
+    CL             *pc;
+    CL             *nextpc;
+    stack          *st;
+    int             sp;
+    int             line_cnt;
+    const char     *last_line_start;
+    char           *line_path;
+    int             lastchar;
+    char           *linebuf;
+    int             bufsize;
+    int             s_pos;
+
+    Output  output[OPENFILES];
+    Output *current_output; // pointer to one element of 'output'
+
+
+    int   tabstop;
+    int   tabs[10];
+    hash *fns;
+    int   tabpos;
+
+};
+
+extern global *gl;
+extern char    string_buf[256];
+
+#define EOSTR    0
+#define BEG_STR1 '('
+#define BEG_STR2 '~'
+#define END_STR1 '~'
+#define END_STR2 ')'
+
+inline bool is_SPACE(char c) { return c == ' ' || c == '\t'; }
+inline bool is_SEP(char c)   { return c == ',' || c == ';'; }
+inline bool is_LF(char c)    { return c == '\n'; }
+inline bool is_EOS(char c)   { return c == EOSTR; }
+
+inline bool is_SPACE_LF(char c)         { return is_SPACE(c) || is_LF(c); }
+inline bool is_SPACE_LF_EOS(char c)     { return is_SPACE_LF(c) || is_EOS(c); }
+inline bool is_SPACE_SEP_LF_EOS(char c) { return is_SPACE_LF_EOS(c) || is_SEP(c); }
+inline bool is_LF_EOS(char c)           { return is_LF(c) || is_EOS(c); }
+inline bool is_SEP_LF_EOS(char c)       { return is_SEP(c) || is_LF_EOS(c); }
+
+inline void SKIP_SPACE_LF         (const char *& var) { while (is_SPACE_LF(*var)) ++var; }
+inline void SKIP_SPACE_LF         (char *& var)       { while (is_SPACE_LF(*var)) ++var; }
+inline void SKIP_SPACE_LF_BACKWARD(const char *& var) { while (is_SPACE_LF(*var)) --var; }
+inline void SKIP_SPACE_LF_BACKWARD(char *& var)       { while (is_SPACE_LF(*var)) --var; }
+
+enum LookupScope {
+    LOOKUP_LIST,
+    LOOKUP_BLOCK,
+    LOOKUP_BLOCK_REST,
+    LOOKUP_LIST_OR_PARENT_LIST,
+};
+
+#include "aisc_proto.h"
+
+// #define SHOW_CALLER // show where error was raised
+
+#ifdef SHOW_CALLER
+
+#define print_error(err)          print_error_internal(err, __FILE__, __LINE__)
+#define print_warning(err)        print_warning_internal(err, __FILE__, __LINE__)
+#define printf_error(format, arg) print_error_internal(formatted(format, arg), __FILE__, __LINE__)
+
+#else
+
+#define print_error(err)          print_error_internal(err, NULL, 0)
+#define print_warning(err)        print_warning_internal(err, NULL, 0)
+#define printf_error(format, arg) print_error_internal(formatted(format, arg), NULL, 0)
+
+#endif
+
+#else
+#error aisc.h included twice
+#endif // AISC_H


=====================================
AISC/aisc_commands.c
=====================================
@@ -0,0 +1,980 @@
+// ================================================================
+/*                                                                  */
+//   File      : aisc_commands.c
+//   Purpose   :
+/*                                                                  */
+//   Institute of Microbiology (Technical University Munich)
+//   http://www.arb-home.de/
+/*                                                                  */
+// ================================================================
+
+#include "aisc_interpreter.h"
+#include "aisc_eval.h"
+
+Location Location::exit_pc;
+int      Location::global_error_count = 0;
+
+const Location& Location::guess_pc() {
+    if (exit_pc.valid()) return exit_pc;
+
+    if (Interpreter::instance) {
+        const Code *pc = Interpreter::instance->at();
+        if (pc) return pc->source;
+    }
+
+    static Location dummy(0, "no_idea_where");
+    return dummy;
+}
+
+void Location::print_internal(const char *msg, const char *msg_type, const char *launcher_file, int launcher_line) const {
+#if defined(MASK_ERRORS)
+    fputs("(disabled) ", stderr);
+#endif
+    fputs("./", stderr);
+    fprint_location(stderr);
+
+    if (msg_type) {
+        fputs(msg_type, stderr);
+        fputs(": ", stderr);
+    }
+
+    if (msg) {
+        fputs(msg, stderr);
+        if (Interpreter::instance) {
+            const Data& data = Interpreter::instance->get_data();
+            if (data.get_cursor()) {
+                fputs(" (cursor ", stderr);
+                data.dump_cursor_pos(stderr);
+                fputs(")", stderr);
+            }
+        }
+        fputc('\n', stderr);
+    }
+
+    if (launcher_file) {
+#if defined(MASK_ERRORS)
+        fputs("(disabled) ", stderr);
+#endif
+        fputs("../AISC/", stderr);
+        fprint_location(launcher_file, launcher_line, stderr);
+        fprintf(stderr, "%s was launched from here\n", msg_type);
+    }
+}
+
+#define ERRBUFSIZE 500
+const char *formatted(const char *format, ...) {
+    // goes to header:  __ATTR__FORMAT(1)
+
+    static char *errbuf = 0;
+    if (!errbuf) { errbuf = (char*)malloc(ERRBUFSIZE+1); }
+
+    va_list argPtr;
+    va_start(argPtr, format);
+    int     chars = vsprintf(errbuf, format, argPtr);
+    if (chars>ERRBUFSIZE) {
+        fprintf(stderr, "%s:%i: Error: Buffer overflow!\n", __FILE__, __LINE__);
+        vfprintf(stderr, format, argPtr);
+        fputc('\n', stderr);
+
+        va_end(argPtr);
+        exit(EXIT_FAILURE);
+    }
+    va_end(argPtr);
+
+    return errbuf;
+}
+#undef ERRBUFSIZE
+
+inline void print_tabs(int count, FILE *out) {
+    while (count--) fputc('\t', out);
+}
+
+static void write_aisc(const TokenListBlock *block, FILE *out, int indentation) {
+    for (const TokenList *list = block->first_list(); list; list = list->next_list()) {
+        print_tabs(indentation, out);
+
+        const Token *first = list->first_token();
+        for (const Token *tok = first; tok; tok = tok->next_token()) {
+            if (tok != first) fputc(',', out);
+
+            if (tok->is_block()) {
+                fprintf(out, "'%s'={\n", tok->get_key());
+                const TokenListBlock *content = tok->get_content();
+                if (content) write_aisc(content, out, indentation + 1);
+                print_tabs(indentation+1, out);
+                fputc('}', out);
+            }
+            else {
+                fprintf(out, "\t'%s'=(~%s~)",
+                        tok->get_key(),
+                        tok->has_value() ? tok->get_value() : "");
+            }
+        }
+        fprintf(out, ";\n");
+    }
+}
+
+// --------------------
+//      Interpreter
+
+const Interpreter *Interpreter::instance = NULL;
+
+void Interpreter::define_fun(const char *name, const Code *co) {
+    const Code *exists = find_fun(name);
+    if (exists) {
+        printf_error(co, "Attempt to redefine '%s'", name);
+        print_error(exists, "first definition was here");
+    }
+    else {
+        char buffer[100];
+        sprintf(buffer, "%li", (long)co);
+        functions->write(name, buffer);
+    }
+}
+
+const Code *Interpreter::find_fun(const char *name) {
+    const char *fn = functions->read(name);
+    return fn ? (Code*)atol(fn) : NULL;
+}
+
+
+int Interpreter::do_dumpdata(const char *filename) {
+    if (!data.get_tokens()) {
+        print_error(at(), "DUMPDATA can only be used after DATA");
+        return 1;
+    }
+    FILE *out = stdout;
+    if (filename[0]) {
+        printf("Dumping data to '%s'\n", filename);
+        out = fopen(filename, "wt");
+        if (!out) {
+            printf_error(at(), "cant write to file '%s' (DUMPDATA)", filename);
+            return 1;
+        }
+    }
+    else {
+        puts("DUMPDATA:");
+    }
+    write_aisc(data.get_tokens(), out, 0);
+    if (filename[0]) fclose(out);
+    return 0;
+}
+
+int Interpreter::do_data(const char *str) {
+    if (strlen(str) < 2) {
+        printf_error(at(), "no parameter '%s'", at()->str);
+        return 1;
+    }
+
+    if (!set_data(str, 4)) { // hack to correct column reported on errors 
+        printf_error(at(), "occurred in following script-part: '%s'", at()->str);
+        return 1;
+    }
+    return 0;
+}
+
+int Interpreter::do_indent(const char *str) {
+    int diff       = atoi(str);
+    int indent     = current_formatter().get_indent();
+    int new_indent = indent+diff;
+
+    if (new_indent<0) {
+        printf_error(at(), "invalid resulting indentation %i", new_indent);
+        return 1;
+    }
+
+    current_formatter().set_indent(new_indent);
+    return 0;
+}
+
+int Interpreter::do_tabstop(const char *str) {
+    int ts = atoi(str);
+    if ((ts < 1) || (ts > 1024)) {
+        printf_error(at(), "illegal TABSTOP %i (legal: [1..1024])", ts);
+        return 1;
+    }
+
+    current_formatter().set_tabstop(ts);
+    return 0;
+}
+
+class ArgParser : virtual Noncopyable {
+    char            *args;
+    char            *strtok_arg;
+    const Location&  loc;
+
+    void init(const char *a) { strtok_arg = args = strdup(a); }
+public:
+    ArgParser(const char *arguments, const Location& loc_) : loc(loc_) { init(arguments); }
+    ArgParser(const char *arguments, const Code *co) : loc(co->source) { init(arguments); }
+    ~ArgParser() { free(args); }
+
+    const char *get(const char *paramDescription) {
+        const char *res = strtok(strtok_arg, " \t,;\n");
+        strtok_arg      = NULL;
+        if (!res) printf_error(&loc, "Missing argument '%s'", paramDescription);
+        return res;
+    }
+};
+
+int Interpreter::do_tab(const char *str) {
+    ArgParser args(str, at());
+
+    const char *s1 = args.get("TABCOUNT");
+    if (s1) {
+        const char *s2 = args.get("TABVALUE");
+        if (s2) {
+            int ts = atoi(s1);
+            if ((ts < 0) || (ts > 9)) print_error(at(), "wrong TABCOUNT");
+            else {
+                int val = atoi(s2);
+                if ((val < 0) || (val > 1000)) print_error(at(), "wrong TABVALUE");
+                else {
+                    current_formatter().set_tab(ts, val);
+                    return 0;
+                }
+            }
+        }
+    }
+    return 1;
+}
+
+int Interpreter::do_open(const char *str) {
+    ArgParser args(str, at());
+
+    const char *fileID = args.get("fileID");
+    if (fileID) {
+        const char *fn = args.get("filename");
+        if (strlen(fn) < 3) printf_error(at(), "Filename '%s' too short (<3)", fn);
+        else {
+            Output *alreadyOpen = find_output_for_ID(fileID);
+            if (alreadyOpen) printf_error(at(), "File '%s' already opened", fileID);
+            else {
+                int i;
+                for (i = 0; i < OPENFILES; i++) {
+                    if (!output[i].inUse()) break;
+                }
+                if (i == OPENFILES) print_error(at(), "Too many open files");
+                else {
+                    FILE *file = fopen(fn, "wt");
+                    if (file) {
+                        output[i].assign(file, fileID, fn, at());
+                        return 0;
+                    }
+                    printf_error(at(), "Cannot write to file '%s' (OPEN)", fn);
+                }
+            }
+        }
+    }
+    return 1;
+}
+
+int Interpreter::do_close(const char *str) {
+    ArgParser   args(str, at());
+    const char *fileID = args.get("fileID");
+
+    if (fileID) {
+        Output *known = find_output_for_ID(fileID);
+        if (known) {
+            known->close();
+            return 0;
+        }
+        printf_error(at(), "File '%s' not open or already closed", str);
+    }
+    return 1;
+}
+
+int Interpreter::do_out(const char *str) {
+    ArgParser   args(str, at());
+    const char *fileID = args.get("fileID");
+
+    if (fileID) {
+        Output *known = find_output_for_ID(fileID);
+        if (known) {
+            current_output = known;
+            return 0;
+        }
+        printf_error(at(), "File '%s' not open or already closed", str);
+    }
+    return 1;
+}
+
+inline char *get_ident(const char*& code, const Code *at) {
+    const char *start  = strstr(code, "$(");
+    if (!start) {
+        print_error(at, "expected to see '$(' while parsing for ident/selector");
+        return NULL;
+    }
+    const char *end = strchr(start+2, ')');
+    if (!end) {
+        print_error(at, "missing closing ')' after ident/selector");
+        return NULL;
+    }
+
+    code = end+1;
+
+    char *ident = copy_string_part(start+2, end-1);
+    if (!ident[0]) {
+        print_error(at, "Empty ident/selector");
+        freenull(ident);
+    }
+    return ident;
+}
+
+int Interpreter::do_moveto(const char *str) {
+    int   err      = 1;
+    char *selector = get_ident(str, at());
+    if (selector) {
+        LookupScope  scope = strrchr(selector, '/') ? LOOKUP_LIST : LOOKUP_BLOCK;
+        const Token *fo    = data.find_qualified_token(selector, scope);
+        if (!fo) {
+            printf_error(at(), "Could not find data '%s'", selector);
+        }
+        else {
+            data.set_cursor(fo);
+            err = 0;
+        }
+        free(selector);
+    }
+    return err;
+}
+
+void var_ref::const_violation() {
+    printf_error(Interpreter::instance->at(),
+                 "Attempt to modify write-protected variable '%s'", e->key);
+}
+
+int Interpreter::do_makeconst(const char *str) {
+    int   err   = 1;
+    char *ident = get_ident(str, at());
+
+    if (ident) {
+        var_ref var = get_local(ident);
+        if (!var) {
+            printf_error(at(), "undefined Ident '%s' in CONST (use CREATE first)", ident);
+        }
+        else {
+            var.write_protect();
+        }
+        free(ident);
+    }
+
+    return err;
+}
+
+int Interpreter::do_set(const char *str) {
+    int   err   = 1;
+    char *ident = get_ident(str, at());
+    if (ident) {
+        var_ref var = get_local(ident);
+        if (!var) {
+            printf_error(at(), "undefined Ident '%s' in SET (use CREATE first)", ident);
+        }
+        else {
+            SKIP_SPACE_LF(str);
+            if (str[0] == '=') {
+                ++str;
+                SKIP_SPACE_LF(str);
+            }
+            err = var.write(str);
+        }
+        free(ident);
+    }
+    return err;
+}
+
+int Interpreter::do_create(const char *str) {
+    char *var = get_ident(str, at());
+    const char *def = read_var(var);
+
+    int result = 1;
+    if (def) printf_error(at(), "Ident '%s' in CREATE already defined", var);
+    else {
+        SKIP_SPACE_LF(str);
+        if (*str == '=') {
+            str++;
+            SKIP_SPACE_LF(str);
+        }
+        write_var(var, str);
+        result = 0;
+    }
+    free(var);
+    return result;
+}
+
+int Interpreter::do_if(const char *str) {
+    int  err       = 0;
+    bool condition = false;
+
+    if (str) { // expression is not undefined
+        const char *cursor     = strpbrk(str, "=~<");
+        if (!cursor) condition = true; // if no operator found -> assume condition true (even if empty)
+        else {
+            char op = *cursor;
+            aisc_assert(cursor>str);
+            bool negate = cursor[-1] == '!';
+
+            const char *la = cursor - negate;
+            --la;
+            SKIP_SPACE_LF_BACKWARD(la, str);
+
+            char *left = copy_string_part(str, la);
+            cursor++;
+
+            char        right_buffer[strlen(cursor)+1];
+            const char *right = right_buffer;
+
+            while (cursor && !condition) {
+                SKIP_SPACE_LF(cursor);
+                const char *kom  = strchr(cursor, ',');
+                const char *next = kom;
+
+                if (kom) {
+                    next++;
+                    --kom;
+                    SKIP_SPACE_LF_BACKWARD(kom, str);
+
+                    int len    = kom-cursor+1;
+                    memcpy(right_buffer, cursor, len);
+                    right_buffer[len] = 0;
+                }
+                else {
+                    right = cursor;
+                }
+
+                switch (op) {
+                    case '=': condition = strcmp(left, right) == 0; break;
+                    case '~': condition = strstr(left, right) != 0; break;
+                    case '<': condition = atoi(left) < atoi(right); break;
+                    default:
+                        print_error(at(), formatted("Unhandled operator (op='%c') applied to '%s'", op, str));
+                        err = 1;
+                        break;
+                }
+
+                if (err) break;
+                if (negate) condition = !condition;
+                cursor = next;
+            }
+
+            free(left);
+        }
+    }
+    
+    if (!err && !condition) jump(at()->ELSE->next);
+    return err;
+}
+
+struct for_data {
+    char        *forstr;
+    const Token *forcursor;
+    long         forval;
+    long         forend;
+    for_data    *next;
+};
+
+inline for_data& add_for_data_to(const Code *co) {
+    for_data *fd = (for_data *)calloc(sizeof(for_data), 1);
+
+    fd->next = co->fd;
+    co->fd   = fd;
+
+    return *fd;
+}
+
+inline void remove_for_data_from(const Code *co) {
+    for_data *fd = co->fd;
+    free(fd->forstr);
+    co->fd = fd->next;
+    free(fd);
+}
+
+int Interpreter::do_push() {
+    if (stack_size++ >= STACKSIZE) {
+        print_error(at(), "Stack size exceeded");
+        return 1;
+    }
+
+    Stack *st = (Stack *)calloc(sizeof(Stack), 1);
+
+    st->cursor = data.get_cursor();
+    st->pc     = at();
+    st->hs     = new hash(HASHSIZE);
+    st->next   = stack;
+
+    stack = st;
+    
+    return 0;
+}
+
+void Interpreter::pop() {
+    aisc_assert(stack_size>0);
+
+    Stack *st = stack;
+    if (st) {
+        delete st->hs;
+        data.set_cursor(st->cursor);
+        stack = st->next;
+        free(st);
+        stack_size--;
+    }
+}
+
+int Interpreter::do_pop() {
+    if (stack_size<2) {
+        print_error(at(), "Nothing to Pop");
+        return 1;
+    }
+    pop();
+    return 0;
+}
+
+int Interpreter::do_gosub(const char *str) {
+    int result = 1;
+    if (do_push() == 0) {
+        char *params;
+        char *fun_name;
+
+        {
+            const char *s;
+            for (s = str; !is_SPACE_SEP_LF_EOS(*s); s++) ;
+
+            fun_name = copy_string_part(str, s-1);
+
+            if (*s) {
+                s++;
+                SKIP_SPACE_LF(s);
+                params = strdup(s);
+            }
+            else {
+                params = NULL;
+            }
+        }
+
+        const Code *fun = find_fun(fun_name);
+        if (!fun) {
+            printf_error(at(), "Function '%s' not found", fun_name);
+        }
+        else {
+            bool        eval_failed;
+            char       *fpara_eval = Expression(data, fun->source, fun->str, false).evaluate(eval_failed);
+
+            const char *fpara  = fpara_eval;
+            SKIP_SPACE_LF(fpara);
+            if (!*fpara) fpara = 0;
+
+            char       *npara  = 0;
+            const char *nfpara = 0;
+
+            int err = eval_failed;
+            for (char *para = params; !err && para; para=npara, fpara=nfpara) {
+                if (!fpara) {
+                    printf_error(at(), "Too many Parameters '%s'", para);
+                    printf_error(fun, "in call to '%s'", fun_name);
+
+                    err = -1;
+                }
+                else {
+                    {
+                        char *s;
+                        for (s = para; !is_SEP_LF_EOS(*s); s++) ;
+                        if (*s) {
+                            *s = 0;
+                            npara = s+1;
+                            SKIP_SPACE_LF(npara);
+                        }
+                        else npara = NULL;
+                    }
+
+                    char *fpara_name;
+                    {
+                        const char *s;
+                        for (s = fpara; !is_SEP_LF_EOS(*s); s++) ;
+                        fpara_name = copy_string_part(fpara, s-1);
+                        if (*s) {
+                            nfpara = s+1;
+                            SKIP_SPACE_LF(nfpara);
+                        }
+                        else nfpara = NULL;
+                    }
+
+                    char *para_eval = Expression(data, at()->source, para, false).evaluate(eval_failed);
+
+                    if (eval_failed) {
+                        print_error(at(), formatted("Could not evaluate expression '%s' for parameter '%s'", para, fpara_name));
+                    }
+                    else {
+                        const char *s = read_var(fpara_name);
+                        if (s) {
+                            print_error(fun, formatted("duplicated formal parameter '%s' in definition of function '%s'", fpara_name, fun_name));
+                            err = -1;
+                        }
+                        else {
+                            write_var(fpara_name, para_eval);
+                        }
+                    }
+                    free(para_eval);
+                    free(fpara_name);
+                }
+            }
+
+            if (!err && fpara) {
+                printf_error(at(), "Missing parameter '%s'", fpara);
+                printf_error(fun, "in call to '%s'", fun_name);
+                err = -1;
+            }
+
+            if (!err) jump(fun->next);
+            result = err;
+            free(fpara_eval);
+        }
+        free(fun_name);
+        free(params);
+    }
+    return result;
+}
+
+int Interpreter::do_goto(const char *str) {
+    const Code *func = find_fun(str);
+    if (!func) {
+        printf_error(at(), "Function '%s' not found", str);
+        return 1;
+    }
+    jump(func->next);
+    return 0;
+}
+
+int Interpreter::do_return() {
+    jump(stack->pc->next);
+    return do_pop();
+}
+
+int Interpreter::do_for(const char *str) {
+    int   err   = 1;
+    char *ident = get_ident(str, at());
+    if (ident) {
+        const char *eq = strchr(str, '=');
+        if (eq) {
+            ++eq;
+            SKIP_SPACE_LF(eq);
+
+            const char *to = strstr(eq, "TO");
+            if (!to) print_error(at(), "TO not found in FOR - expecting e.g. 'FOR $(i) = a TO b'");
+            else {
+                to  += 2;
+                SKIP_SPACE_LF(to);
+
+                for_data& fd = add_for_data_to(pc);
+                fd.forval    = atol(eq);
+                fd.forend    = atol(to);
+
+                if (fd.forval > fd.forend) {
+                    nextpc = pc->NEXT->next;
+                    remove_for_data_from(pc);
+                    err = 0;
+                }
+                else {
+                    var_ref var = get_local(ident);
+                    if (!var) {
+                        printf_error(at(), "Undefined Ident '%s' in FOR (use CREATE first)", ident);
+                    }
+                    else {
+                        err       = var.write(formatted("%li", fd.forval));
+                        fd.forstr = strdup(ident);
+                    }
+                }
+                // cppcheck-suppress memleak (fd.forstr is released from Interpreter::do_next)
+            }
+        }
+        else {
+            const char  *p  = strrchr(ident, '/');
+            const Token *fo = data.find_qualified_token(ident, p ? LOOKUP_LIST : LOOKUP_BLOCK);
+            if (!fo) {
+                nextpc = pc->NEXT->next;
+            }
+            else {
+                for_data& fd = add_for_data_to(pc);
+                fd.forstr    = strdup(p ? p+1 : ident);
+                fd.forcursor = data.get_cursor();
+                data.set_cursor(fo);
+                // cppcheck-suppress memleak (fd.forstr is released from Interpreter::do_next)
+            }
+            err = 0;
+        }
+        
+        free(ident);
+    }
+    return err;
+}
+
+int Interpreter::do_next() {
+    // handles NEXT (end of for-loop) and CONTINUE (loop from inside)
+
+    for_data& fd = *pc->FOR->fd;
+    if (fd.forcursor) {
+        const Token *fo = data.find_token(data.get_cursor(), fd.forstr, LOOKUP_BLOCK_REST);
+        if (fo) {
+            nextpc = pc->FOR->next;
+            data.set_cursor(fo);
+            return 0;
+        }
+        data.set_cursor(fd.forcursor);
+    }
+    else {
+        if (fd.forval < fd.forend) {
+            fd.forval++;
+            nextpc = pc->FOR->next;
+            return get_local(fd.forstr).write(formatted("%li", fd.forval));
+        }
+
+    }
+    nextpc = pc->FOR->ENDFOR->next;
+    remove_for_data_from(pc->FOR);
+    return 0;
+}
+
+// -------------------
+//      Dispatcher
+
+typedef int (Interpreter::*NoArgFun)();
+typedef int (Interpreter::*ArgFun)(const char *);
+
+enum CallMode {
+    STANDARD_CALL = 0,
+
+    // bit values:
+    DONT_EVAL           = 1,
+    EVAL_VAR_DECL       = 2,
+    EVAL_PLAIN          = 4,
+    TERMINATED_ON_ERROR = 8,
+};
+
+const int DISPATCH_LIMIT = 5000000; // abort after 5mio dispatches to one command-type (DEBUG only)
+
+struct Command {
+    virtual ~Command() {}
+    virtual bool matches(const char *code) const   = 0;
+    virtual int call(Interpreter& interpret) const = 0;
+
+    const Location& pc() const {
+        return Interpreter::instance->at()->source;
+    }
+};
+struct NoSuchCommand : public Command {
+    virtual bool matches(const char *) const OVERRIDE { return true; } // catch all
+    virtual int call(Interpreter& interpret) const OVERRIDE {
+        printf_error(interpret.at(), "Unknown command '%s'", interpret.at()->str);
+        return -1; // bail out
+    }
+};
+class NamedCommand : public Command, virtual Noncopyable {
+    const char *name;
+    int         len;
+    mutable int dispatched;
+protected:
+    virtual int check_result(int res, char *evaluated_args) const {
+        dispatched++;
+        if (dispatched>DISPATCH_LIMIT) {
+            print_error(&pc(), "possible deadlock detected");
+            res = -1;
+        }
+#if defined(TRACE)
+        pc().start_message("TRACE");
+        if (res) fprintf(stderr, "[result=%i]", res);
+        fputs(name, stderr);
+        if (evaluated_args) {
+            fputc(' ', stderr);
+            fputs(evaluated_args, stderr);
+        }
+        fputc('\n', stderr);
+        
+#endif
+        free(evaluated_args);
+        return res;
+    }
+public:
+    NamedCommand(const char *cmd) : name(cmd), len(strlen(name)), dispatched(0) {}
+    bool matches(const char *code) const OVERRIDE { return strncmp(code, name, len) == 0; }
+    int length() const { return len; }
+};
+
+class SimpleCmmd : public NamedCommand {
+    NoArgFun fun;
+public:
+    SimpleCmmd(const char *cmd, NoArgFun fun_) : NamedCommand(cmd), fun(fun_) {}
+    virtual int call(Interpreter& interpret) const OVERRIDE {
+        return check_result((interpret.*fun)(), NULL);
+    }
+};
+
+class ArgCommand : public NamedCommand {
+    ArgFun   fun;
+    CallMode emode;
+
+    char *eval(Interpreter& interpret, bool& failed) const {
+        int offset = length();
+        if (!(emode&EVAL_PLAIN)) offset++;
+
+        if (emode & DONT_EVAL) {
+            failed = false;
+            return strdup(interpret.at()->str+offset);
+        }
+
+        Expression expr(interpret.get_data(), interpret.at()->source, interpret.at()->str+offset, false);
+        return (emode&EVAL_VAR_DECL)
+            ? expr.evalVarDecl(failed)
+            : expr.evaluate(failed);
+    }
+    virtual int check_result(int res, char *evaluated_args) const OVERRIDE {
+        return NamedCommand::check_result(res && (emode&TERMINATED_ON_ERROR) ? -1 : res, evaluated_args);
+    }
+    
+public:
+    ArgCommand(const char *cmd, ArgFun fun_, CallMode emode_ = STANDARD_CALL)
+        : NamedCommand(cmd), fun(fun_), emode(emode_) {}
+    virtual int call(Interpreter& interpret) const OVERRIDE {
+        int   res  = 1;
+        bool eval_failed;
+        char *args = eval(interpret, eval_failed);
+        if (args && !eval_failed)  {
+            char *trimmed = args;
+            if (!(emode&EVAL_PLAIN)) SKIP_SPACE_LF(trimmed);
+            
+            res = (interpret.*fun)(trimmed);
+        }
+        else {
+            print_error(interpret.at(), "Failed to evaluate arguments");
+            res = -1;
+        }
+        return check_result(res, args);
+    }
+};
+
+
+void Interpreter::command_table_setup(bool setup) {
+    if (setup) {
+        command_table = new Command*[MAX_COMMANDS+1];
+
+        int i = 0;
+        
+        command_table[i++] = new ArgCommand("MOVETO",   &Interpreter::do_moveto,        EVAL_VAR_DECL);
+        command_table[i++] = new ArgCommand("SET",      &Interpreter::do_set,           EVAL_VAR_DECL);
+        command_table[i++] = new ArgCommand("CONST",    &Interpreter::do_makeconst,     EVAL_VAR_DECL);
+        command_table[i++] = new ArgCommand("CREATE",   &Interpreter::do_create,        EVAL_VAR_DECL);
+
+        command_table[i++] = new ArgCommand("PRINT",    &Interpreter::do_write_current, EVAL_PLAIN);
+        command_table[i++] = new ArgCommand("P ",       &Interpreter::do_write_current, EVAL_PLAIN);
+        command_table[i++] = new ArgCommand("PP",       &Interpreter::do_write_stdout);
+        command_table[i++] = new SimpleCmmd("--",       &Interpreter::do_newline);
+        command_table[i++] = new SimpleCmmd("PMSTART",  &Interpreter::do_write_maybe_start);
+        command_table[i++] = new SimpleCmmd("PMEND",    &Interpreter::do_write_maybe_end);
+        command_table[i++] = new ArgCommand("PM",       &Interpreter::do_write_maybe);
+
+        command_table[i++] = new ArgCommand("GOSUB",    &Interpreter::do_gosub,         DONT_EVAL);
+        command_table[i++] = new ArgCommand("CALL",     &Interpreter::do_gosub,         DONT_EVAL);
+        command_table[i++] = new ArgCommand("GOTO",     &Interpreter::do_goto);
+        command_table[i++] = new SimpleCmmd("RETURN",   &Interpreter::do_return);
+        command_table[i++] = new SimpleCmmd("PUSH",     &Interpreter::do_push);
+        command_table[i++] = new SimpleCmmd("POP",      &Interpreter::do_pop);
+        command_table[i++] = new SimpleCmmd("CONTINUE", &Interpreter::do_next);
+
+        command_table[i++] = new ArgCommand("OPEN",     &Interpreter::do_open,         TERMINATED_ON_ERROR);
+        command_table[i++] = new ArgCommand("CLOSE",    &Interpreter::do_close);
+        command_table[i++] = new ArgCommand("OUT",      &Interpreter::do_out,          TERMINATED_ON_ERROR);
+
+        command_table[i++] = new ArgCommand("INDENT",   &Interpreter::do_indent);
+        command_table[i++] = new ArgCommand("TABSTOP",  &Interpreter::do_tabstop);
+        command_table[i++] = new ArgCommand("TAB",      &Interpreter::do_tab);
+        command_table[i++] = new SimpleCmmd("EXIT",     &Interpreter::do_exit);
+
+        command_table[i++] = new ArgCommand("DATA",     &Interpreter::do_data,         TERMINATED_ON_ERROR);
+        command_table[i++] = new ArgCommand("DUMPDATA", &Interpreter::do_dumpdata,     TERMINATED_ON_ERROR);
+        command_table[i++] = new ArgCommand("ERROR",    &Interpreter::do_error,        TERMINATED_ON_ERROR);
+        command_table[i++] = new ArgCommand("WARNING",  &Interpreter::do_warning);
+
+        command_table[i++] = new NoSuchCommand; // should be last!
+        command_table[i++] = NULL;
+
+        aisc_assert(i<MAX_COMMANDS);
+    }
+    else { // cleanup
+        for (int i = 0; command_table[i]; ++i) delete command_table[i];
+        delete [] command_table;
+    }
+}
+
+const Command *Interpreter::find_command(const Code *co) {
+    for (int c = 0; command_table[c]; ++c) {
+        const Command& cmd = *command_table[c];
+        if (cmd.matches(co->str)) {
+            return &cmd;
+        }
+    }
+    aisc_assert(0); // NoSuchCommand missing ? 
+    return NULL;
+}
+
+int Interpreter::run_program() {
+    parser.set_source(prg->source.get_path(), 1);
+
+    for (pc = prg; pc; pc = nextpc) {
+        nextpc = pc->next;
+
+        switch (pc->command) {
+            case CT_IF: {
+                Expression  expr(data, pc->source, pc->str, true);
+                bool        eval_failed;
+                char       *val = expr.evaluate(eval_failed);
+                int         err = eval_failed;
+                if (!err) err = do_if(val);   // execute even with val == NULL!
+                free(val);
+                if  (err) return err;
+                break;
+            }
+
+            case CT_ELSE:
+                nextpc = pc->IF->ENDIF->next;
+            case CT_ENDIF:
+                break;
+
+            case CT_FOR: {
+                Expression  expr(data, pc->source, pc->str, false);
+                bool        eval_failed;
+                char       *val   = expr.evalVarDecl(eval_failed);
+                bool        abort = !val || eval_failed || do_for(val);
+                free(val);
+                if (abort) return 1;
+                break;
+            }
+
+            case CT_NEXT:
+                if (do_next()) return 1;
+            case CT_ENDFOR:
+                break;
+
+            case CT_FUNCTION:
+                print_error(at(), "fatal: ran into FUNCTION (missing EXIT?)");
+                break;
+
+                break;
+
+            case CT_OTHER_CMD: {
+                int res = pc->cmd->call(*this);
+                if (res == -1) return 1;
+                break;
+            }
+
+            case CT_LABEL:
+            case CT_ELSEIF:
+            case NO_COMMAND:
+                printf_error(at(), "internal error: Expected not to reach command type=%i", pc->command);
+                return 1;
+        }
+
+        if (!nextpc) { // end of execution ?
+            Location::announce_exit_pc(pc->source);
+        }
+    }
+    return Location::get_error_count();
+}
+


=====================================
AISC/aisc_def.h
=====================================
@@ -0,0 +1,55 @@
+//   Institute of Microbiology (Technical University Munich)     //
+//   http://www.arb-home.de/                                     //
+
+#ifndef AISC_DEF_H
+#define AISC_DEF_H
+
+#ifndef ARB_SIMPLE_ASSERT_H
+#include <arb_simple_assert.h>
+#endif
+
+#define aisc_assert(cond) arb_assert(cond)
+
+#if defined(DEBUG)
+// #define TRACE       // aisc-"debugger"
+// #define SHOW_CALLER // show where error was raised
+// #define MASK_ERRORS // useful to valgrind via makefile
+#endif
+
+#define OPENFILES 16
+#define HASHSIZE  1024
+#define STACKSIZE 20
+
+enum LookupScope {
+    LOOKUP_LIST,
+    LOOKUP_BLOCK,
+    LOOKUP_BLOCK_REST,
+    LOOKUP_LIST_OR_PARENT_LIST,
+};
+
+class Command;
+class Code;
+class hash;
+class Token;
+class Location;
+class Data;
+class Interpreter;
+
+
+#if defined(SHOW_CALLER)
+#define CALLER_FILE __FILE__
+#define CALLER_LINE __LINE__
+#else // !defined(SHOW_CALLER)
+#define CALLER_FILE NULL
+#define CALLER_LINE 0
+#endif
+
+#define print_error(code_or_loc, err)    (code_or_loc)->print_error_internal(err, CALLER_FILE, CALLER_LINE)
+#define print_warning(code_or_loc, err)  (code_or_loc)->print_warning_internal(err, CALLER_FILE, CALLER_LINE)
+
+#define printf_error(code_or_loc, format, arg)   print_error(code_or_loc, formatted(format, arg))
+#define printf_warning(code_or_loc, format, arg) print_warning(code_or_loc, formatted(format, arg))
+
+#else
+#error aisc_def.h included twice
+#endif // AISC_DEF_H


=====================================
AISC/aisc_eval.c
=====================================
@@ -0,0 +1,199 @@
+//   Institute of Microbiology (Technical University Munich)     //
+//   http://www.arb-home.de/                                     //
+
+#include "aisc_eval.h"
+#include "aisc_inline.h"
+#include "aisc_proto.h"
+
+char *Expression::eval_math(char *expr, char op_char) {
+    char sep[] = "?,;";
+    sep[0]     = op_char;
+    
+    char *brk = strpbrk(expr, sep);
+    if (!brk) {
+        print_error(&loc, formatted("Expected to see '%c', ',' or ';' in '%s'", op_char, expr));
+        return NULL;
+    }
+
+    brk[0] = 0;
+    int i1 = atoi(expr);
+    brk[0] = op_char;
+    int i2 = atoi(brk+1);
+    int r;
+    switch (op_char) {
+        case '+': r = i1+i2; break;
+        case '*': r = i1*i2; break;
+        default :
+            printf_error(&loc, "Unknown operator '%c'", op_char);
+            return NULL;
+    }
+    
+    return strdup(formatted("%i", r));
+}
+
+char *Expression::evalPart(int start, int end, int& resLen) {
+    aisc_assert(start>0);
+
+    char *res = NULL;
+    resLen = -1;
+
+    char  c           = ebuffer[end+1];
+    ebuffer[end+1] = 0;
+    char *part        = ebuffer+start;
+    SKIP_SPACE_LF(part);
+
+    if (start>end) { // empty "$()"
+        res    = strdup("");
+        resLen = 0;
+    }
+    else if (part[0] == '+' || part[0] == '*') {
+        res = eval_math(part+1, part[0]);
+    }
+    else if (part[0] == '#') {
+        if (strncmp(part, "#FILE", 5) == 0) {
+            char *path = part+5;
+            while (is_SPACE_LF_EOS(*path)) ++path;
+
+            char *file = read_aisc_file(path, &loc);
+            if (!file) {
+                printf_error(&loc, "couldn't read file '%s'", path);
+            }
+            else {
+                int         fileLen    = strlen(file);
+                const char *sourcename = loc.get_path();
+                int         sourceline = loc.get_linenr();
+                aisc_assert(sourcename);
+
+                int buflen = fileLen+strlen(path)+strlen(sourcename)+100;
+                res        = (char *)malloc(buflen+1);
+
+                // Inject code to restore correct code file and line (needed for proper error messages)
+                int printed = sprintf(res, "@SETSOURCE %s,%i@%s at SETSOURCE %s,%i@",
+                                      path, 1,
+                                      file,
+                                      sourcename, sourceline);
+                if (printed >= buflen) {
+                    fprintf(stderr, "%s:%i: Error: buffer overflow\n", __FILE__, __LINE__);
+                }
+                free(file);
+            }
+        }
+        else {
+            printf_error(&loc, "unknown eval-command '%s'", part);
+        }
+    }
+    else {
+        res = get_var_string(data, part, allow_missing_ref);
+    }
+
+    ebuffer[end+1] = c;
+
+    if (resLen == -1 && res) resLen = strlen(res);
+    return res;
+}
+
+bool Expression::evalRest(const int offset) {
+    // evaluate content of 'ebuffer' starting at offset
+    // return true on success
+    // may reallocate ebuffer - so old pointers might get invalidated
+
+    aisc_assert(strncmp(ebuffer+offset, "$(", 2) == 0);
+
+    {
+        const char *more_to_eval = strstr(ebuffer+offset+2, "$(");
+        if (more_to_eval) {
+            if (!evalRest(more_to_eval-ebuffer)) return false;
+        }
+    }
+
+    int closing_paren;
+    {
+        const char *paren = strchr(ebuffer+offset+2, ')');
+        if (!paren) {
+            print_error(&loc, "unbalanced parens; missing ')'");
+            return false;
+        }
+        closing_paren = paren-ebuffer;
+    }
+
+    int   eval_len;
+    char *evaluated;
+
+    if (offset>0 && ebuffer[offset-1] == '$') { // quoted "$$(...)"
+        eval_len  = closing_paren-offset;
+        evaluated = strduplen(ebuffer+offset+1, eval_len);
+    }
+    else {
+        evaluated = evalPart(offset+2, closing_paren-1, eval_len);
+    }
+
+    if (!evaluated) {
+        return false;
+    }
+
+    int org_len = closing_paren-offset+1;
+
+    char *rest    = ebuffer+closing_paren+1;
+    int   restlen = used-closing_paren;
+
+    if (eval_len <= org_len) {
+        if (eval_len < org_len) {
+            aisc_assert(closing_paren+1 <= used);
+            memmove(ebuffer+offset+eval_len, rest, restlen);
+            used -= (org_len-eval_len);
+        }
+        memcpy(ebuffer+offset, evaluated, eval_len);
+    }
+    else {
+        int growth = eval_len-org_len;
+        if ((used+growth+1)>bufsize) { // need to increase ebuffer size
+            int   new_bufsize = (used+growth+1)*3/2;
+            char *new_linebuf = (char*)malloc(new_bufsize);
+
+            memcpy(new_linebuf, ebuffer, offset);
+            memcpy(new_linebuf+offset, evaluated, eval_len);
+            aisc_assert(closing_paren+1 <= used);
+            memcpy(new_linebuf+offset+eval_len, rest, restlen);
+
+            free(ebuffer);
+            ebuffer = new_linebuf;
+            bufsize    = new_bufsize;
+        }
+        else {
+            aisc_assert(closing_paren+1 <= used);
+            memmove(ebuffer+offset+eval_len, rest, restlen);
+            memcpy(ebuffer+offset, evaluated, eval_len);
+        }
+        used += growth;
+        aisc_assert(used<bufsize);
+    }
+
+    aisc_assert(ebuffer[used] == 0);
+
+    free(evaluated);
+
+    char *ld = strstr(ebuffer+offset, "$(");
+    if (ld) {
+        int next_offset = ld-ebuffer;
+        if (next_offset == offset) { // infinite loop in evaluation
+            static int deadlock_offset;
+            static int deadlock_count;
+
+            if (next_offset == deadlock_offset) {
+                ++deadlock_count;
+                if (deadlock_count > 50) { // more than 50 evals at same offset in expression
+                    printf_error(&loc, "detected (endless?) recursive evaluation in expression '%s'", ld);
+                    return false;
+                }
+            }
+            else {
+                deadlock_offset = next_offset;
+                deadlock_count  = 1;
+            }
+        }
+        return evalRest(next_offset);
+    }
+
+    return true;
+}
+


=====================================
AISC/aisc_eval.h
=====================================
@@ -0,0 +1,99 @@
+//   Coded by Ralf Westram (coder at reallysoft.de) in March 2011   //
+//   Institute of Microbiology (Technical University Munich)     //
+//   http://www.arb-home.de/                                     //
+
+#ifndef AISC_EVAL_H
+#define AISC_EVAL_H
+
+#ifndef AISC_DEF_H
+#include "aisc_def.h"
+#endif
+#ifndef AISC_LOCATION_H
+#include "aisc_location.h"
+#endif
+#ifndef ARBTOOLS_H
+#include <arbtools.h>
+#endif
+
+class Expression : virtual Noncopyable {
+    const Data&     data;
+    const Location& loc;
+
+    int   used; // currently used size
+    int   bufsize; // including zero-byte
+    char *ebuffer;
+
+    bool allow_missing_ref;
+
+    bool was_evaluated;
+    int  errors_before;
+
+    char *eval_math(char *expr, char op_char);
+    char *evalPart(int start, int end, int& result_len);
+
+    bool evalRest(int offset);
+
+    char *evalBehind(int offset) {
+        aisc_assert(!was_evaluated);
+        char *toEval = strstr(ebuffer+offset, "$(");
+        return (!toEval || evalRest(toEval-ebuffer)) ? ebuffer : NULL;
+    }
+
+
+    char *report_result(char *s, bool& failed) {
+        int errors_after = Location::get_error_count();
+        failed           = errors_after != errors_before;
+
+        aisc_assert(!(failed && s));
+        if (s) {
+            aisc_assert(s == ebuffer);
+            ebuffer = NULL; // now owned by caller
+        }
+
+        was_evaluated = true;
+        return s;
+    }
+
+public:
+    Expression(const Data& data_, const Location& loc_, const char *str, bool allow_missing_ref_)
+        : data(data_),
+          loc(loc_),
+          allow_missing_ref(allow_missing_ref_), 
+          was_evaluated(false),
+          errors_before(Location::get_error_count())
+    {
+        used       = strlen(str);
+        bufsize    = used*2+1;
+        ebuffer = (char*)malloc(bufsize);
+        aisc_assert(used<bufsize);
+        memcpy(ebuffer, str, used+1);
+    }
+    ~Expression() {
+        aisc_assert(was_evaluated); // useless Expression
+        free(ebuffer);
+    }
+
+    char *evaluate(bool& failed) {
+        return report_result(evalBehind(0), failed);
+    }
+
+    char *evalVarDecl(bool& failed) {
+        // dont eval first '$('
+    
+        char *res = NULL;
+        char *varDecl  = strstr(ebuffer, "$(");
+        if (varDecl) {
+            res = evalBehind((varDecl+2)-ebuffer);
+        }
+        else {
+            print_error(&loc, "Expected identifier, i.e. '$(ident)'");
+        }
+        return report_result(res, failed);
+    }
+};
+
+
+
+#else
+#error aisc_eval.h included twice
+#endif // AISC_EVAL_H


=====================================
AISC/aisc_inline.h
=====================================
@@ -0,0 +1,54 @@
+//   Coded by Ralf Westram (coder at reallysoft.de)                 //
+//   Institute of Microbiology (Technical University Munich)     //
+//   http://www.arb-home.de/                                     //
+
+#ifndef AISC_INLINE_H
+#define AISC_INLINE_H
+
+#ifndef _STRING_H
+#include <string.h>
+#endif
+#ifndef _STDLIB_H
+#include <stdlib.h>
+#endif
+
+#define EOSTR    0
+#define BEG_STR1 '('
+#define BEG_STR2 '~'
+#define END_STR1 '~'
+#define END_STR2 ')'
+
+inline bool is_SPACE(char c) { return c == ' ' || c == '\t'; }
+inline bool is_SEP(char c)   { return c == ',' || c == ';'; }
+inline bool is_LF(char c)    { return c == '\n'; }
+inline bool is_EOS(char c)   { return c == EOSTR; }
+
+inline bool is_SPACE_LF(char c)         { return is_SPACE(c) || is_LF(c); }
+inline bool is_SPACE_LF_EOS(char c)     { return is_SPACE_LF(c) || is_EOS(c); }
+inline bool is_SPACE_SEP_LF_EOS(char c) { return is_SPACE_LF_EOS(c) || is_SEP(c); }
+inline bool is_LF_EOS(char c)           { return is_LF(c) || is_EOS(c); }
+inline bool is_SEP_LF_EOS(char c)       { return is_SEP(c) || is_LF_EOS(c); }
+
+inline void SKIP_SPACE_LF(const char *& var) { while (is_SPACE_LF(*var)) ++var; }
+inline void SKIP_SPACE_LF(char *& var)       { while (is_SPACE_LF(*var)) ++var; }
+
+inline void SKIP_SPACE_LF_BACKWARD(const char *& var, const char *strStart) { while (is_SPACE_LF(*var) && var>strStart) --var; }
+inline void SKIP_SPACE_LF_BACKWARD(char *& var, const char *strStart)       { while (is_SPACE_LF(*var) && var>strStart) --var; }
+
+
+
+inline char *strduplen(const char *s, int len) {
+    char *d = (char*)malloc(len+1);
+    memcpy(d, s, len);
+    d[len] = 0;
+    return d;
+}
+
+inline char *copy_string_part(const char *first, const char *last) {
+    return strduplen(first, last-first+1);
+}
+
+
+#else
+#error aisc_inline.h included twice
+#endif // AISC_INLINE_H


=====================================
AISC/aisc_interpreter.h
=====================================
@@ -0,0 +1,509 @@
+// ================================================================ //
+//                                                                  //
+//   File      : aisc.h                                             //
+//   Purpose   :                                                    //
+//                                                                  //
+//   Institute of Microbiology (Technical University Munich)        //
+//   http://www.arb-home.de/                                        //
+//                                                                  //
+// ================================================================ //
+
+#ifndef AISC_H
+#define AISC_H
+
+#ifndef AISC_PROTO_H
+#include "aisc_proto.h"
+#endif
+#ifndef AISC_PARSER_H
+#include "aisc_parser.h"
+#endif
+#ifndef _UNISTD_H
+#include <unistd.h>
+#endif
+
+// ------------------------------------------------------------
+
+struct hash_entry {
+    char       *key;
+    char       *val;
+    hash_entry *next;
+};
+
+class var_ref {
+    hash_entry *e;
+
+    void const_violation();
+
+public:
+    var_ref() : e(NULL) {}
+    var_ref(hash_entry *e_) : e(e_) {}
+
+    operator bool() const { return e; } // refer to existing variable ?
+
+    bool write_protected() const {
+        hash_entry *prot = e->next;
+        return prot && strcmp(prot->key, e->key) == 0 && prot->val == NULL;
+    }
+    void write_protect() {
+        if (!write_protected()) {
+            hash_entry *prot = (hash_entry *)calloc(sizeof(hash_entry), 1);
+
+            prot->key  = strdup(e->key);
+            prot->val  = NULL;
+            prot->next = e->next;
+
+            e->next = prot;
+        }
+    }
+
+    const char *read() const { return e ? e->val : NULL; }
+    int write(const char *val) __ATTR__USERESULT {
+        aisc_assert(e);
+
+        if (write_protected()) {
+            const_violation();
+            return -1;
+        }
+        freeset(e->val, nulldup(val));
+        return 0;
+    }
+
+};
+
+class hash : virtual Noncopyable {
+    int          size;
+    hash_entry **entries;
+
+    int Index(const char *key) const;
+
+    const hash_entry *find_entry(const char *key, int idx) const;
+    hash_entry *find_entry(const char *key, int idx) {
+        return const_cast<hash_entry*>(const_cast<const hash*>(this)->find_entry(key, idx));
+    }
+
+public:
+    hash(int size_);
+    ~hash();
+
+    var_ref ref(const char *key)  { return var_ref(find_entry(key, Index(key))); }
+    const var_ref ref(const char *key) const { return const_cast<hash*>(this)->ref(key); }
+
+    const char *read(const char *key) const { return ref(key).read(); }
+    void write(const char *key, const char *val);
+};
+
+// ------------------------------------------------------------
+
+static const int  LINEBUFSIZE  = 250;
+static const char ALIGN_MARKER = '\1';
+
+class LineBuf : virtual Noncopyable {
+    int   size;
+    int   used;
+    char *buf;
+    int   markers;
+
+    void clear() {
+        size    = 0;
+        used    = 0;
+        buf     = NULL;
+        markers = 0;
+    }
+
+public:
+    LineBuf() { clear(); }
+    ~LineBuf() { free(buf); }
+
+    void put(char c) {
+        if (used >= size) {
+            size = size*3/2+1;
+            if (size<LINEBUFSIZE) { size = LINEBUFSIZE; }
+            realloc_unleaked(buf, size);
+            if (!buf) throw "out of memory";
+        }
+        buf[used++] = c;
+        if (c == ALIGN_MARKER) ++markers;
+    }
+
+    int length() const { return used; }
+    char *take() {
+        put(0);
+        char *b = buf;
+        clear();
+        return b;
+    }
+
+    bool needsAlignment() const { return markers; }
+};
+
+class LineQueue : virtual Noncopyable {
+    int    count;
+    int    size;
+    char **queue;
+
+    void clear() {
+        for (int i = 0; i<count; ++i) freenull(queue[i]);
+        count = 0;
+    }
+public:
+    LineQueue()
+        : count(0),
+          size(10),
+          queue((char**)malloc(size*sizeof(*queue)))
+    {}
+    ~LineQueue() {
+        clear();
+        free(queue);
+    }
+
+    bool empty() const { return count == 0; }
+
+    void add(char *line) {
+        if (count >= size) {
+            size  = size*3/2+1;
+            realloc_unleaked(queue, size*sizeof(*queue));
+            if (!queue) throw "out of memory";
+        }
+        aisc_assert(line[strlen(line)-1] == '\n');
+        
+        queue[count++] = line;
+    }
+
+    void alignInto(LineQueue& dest);
+
+    void flush(FILE *out) {
+        for (int i = 0; i<count; ++i) {
+            fputs(queue[i], out);
+        }
+        clear();
+    }
+};
+
+class Formatter {
+    char outtab[256]; // decode table for $x (0 = handle special, character to print otherwise)
+    int  tabstop;     // normal tabstop (for $t)
+    int  tabs[10];    // predefined tabs ($0..$9) - default to multiples of 'tabstop' if not overridden
+    int  column;      // position in line during printing
+    int  indent;      // extra indentation
+    bool printed_sth;
+
+    LineBuf currentLine;
+    LineQueue toAlign; 
+    LineQueue spool; 
+
+    void outputchar(char c) {
+        currentLine.put(c);
+    }
+
+    void print_char(char c) {
+        if (!printed_sth && (indent || column)) {
+            int ipos = indent*tabstop + column;
+            for (int i = 0; i<ipos; ++i) outputchar(' ');
+        }
+        outputchar(c);
+        column++;
+        printed_sth = true;
+    }
+
+    void tab_to_pos(int pos) {
+        if (pos>column) {
+            if (printed_sth) {
+                while (column < pos) {
+                    outputchar(' ');
+                    column++;
+                }
+            }
+            else {
+                column = pos;
+            }
+        }
+    }
+
+    void align() { if (!toAlign.empty()) toAlign.alignInto(spool); }
+    
+    void finish_line() {
+        outputchar('\n');
+        column      = 0;
+        printed_sth = false;
+
+        if (currentLine.needsAlignment()) {
+            toAlign.add(currentLine.take());
+        }
+        else {
+            align();
+            spool.add(currentLine.take());
+        }
+    }
+
+public:
+
+    Formatter();
+
+    void set_tabstop(int ts) {
+        tabstop = ts;
+        for (int i = 0; i <= 9; i++) {
+            tabs[i] = i * ts;
+        }
+    }
+    void set_tab(int idx, int pos) {
+        aisc_assert(idx >= 0 && idx<10);
+        tabs[idx] = pos;
+    }
+
+    int get_indent() const { return indent; }
+    void set_indent(int indent_) { indent = indent_; }
+
+    int write(const char *str);
+    void flush(FILE *out) { spool.flush(out); }
+    void final_flush(FILE *out) { align(); flush(out); }
+};
+
+class Output : virtual Noncopyable {
+    FILE *fp;
+    char *id;   // internal name used in AISC
+    char *name; // file-system name
+
+    bool     have_open_loc; // opened from user code ?
+    Location open_loc;
+
+    bool terminating;
+
+    Formatter formatter;
+
+    class PrintMaybe *maybe;
+
+    bool wasOpened() const { return fp && name; }
+
+    void close_file() {
+        if (wasOpened()) {
+            formatter.final_flush(fp);
+            if (have_open_loc && terminating) {
+                print_error(&open_loc, "file opened here");
+                print_error(&Location::guess_pc(), "is still open on exit");
+            }
+            fclose(fp);
+        }
+        fp = NULL;
+    }
+
+    void setup();
+    void cleanup();
+    void reuse() { cleanup(); setup(); }
+
+public:
+
+    Output() { terminating = false; setup(); }
+    ~Output() { terminating = true; cleanup(); }
+
+    bool inUse() const { return fp; }
+
+    void assign(FILE *fp_, const char *id_, const char *name_, const Location *openedAt) {
+        aisc_assert(!inUse());
+        aisc_assert(fp_);
+        aisc_assert(id_);
+        fp   = fp_;
+        id   = strdup(id_);
+        name = strdup(name_);
+
+        have_open_loc = openedAt;
+        if (openedAt) open_loc = *openedAt;
+    }
+    void assign(FILE *fp_, const char *id_, const char *name_) {
+        assign(fp_, id_, name_, (const Location*)NULL);
+    }
+    void assign(FILE *fp_, const char *id_, const char *name_, const Code *openedAt_) {
+        assign(fp_, id_, name_, &openedAt_->source);
+    }
+    
+    void assign_stdout(const char *id_) {
+        aisc_assert(!inUse());
+        fp   = stdout;
+        id   = strdup(id_);
+        name = NULL;
+    }
+
+    void close_and_unlink() {
+        close_file();
+        if (name) {
+            fprintf(stderr, "Unlinking %s\n", name);
+            unlink(name);
+        }
+        reuse();
+    }
+    void close() { reuse(); }
+
+    bool hasID(const char *Name) const { return id && strcmp(id, Name) == 0; }
+
+    int write(const char *line);
+    Formatter& get_formatter() { return formatter; }
+
+    void maybe_start();
+    int maybe_write(const char *line);
+    int maybe_end();
+};
+
+struct Stack {
+    const Token *cursor;
+    
+    const Code  *pc;
+    hash  *hs;
+    Stack *next;
+};
+
+class Data : virtual Noncopyable {
+    TokenListBlock *rootBlock;
+    const Token    *cursor;
+public:
+
+    Data() {
+        cursor    = NULL;
+        rootBlock = NULL;
+    }
+    ~Data() { delete rootBlock; }
+
+    void set_tokens(TokenListBlock *newRoot) {
+        delete rootBlock;
+        rootBlock = newRoot;
+    }
+    const TokenListBlock *get_tokens() const { return rootBlock; }
+
+    const Token *get_cursor() const { return cursor; }
+    void set_cursor(const Token *newCursor) { cursor = newCursor; }
+
+    const Token *find_token(const Token *curs, const char *str, LookupScope scope) const;
+    const Token *find_qualified_token(const char *str, LookupScope scope) const;
+
+    void dump_cursor_pos(FILE *out) const;
+};
+
+class Interpreter : virtual Noncopyable {
+    Parser parser;
+
+    Data data;             // currently loaded data
+
+    Code       *prg;       // the complete program
+    const Code *pc;        // current program counter
+    const Code *nextpc;
+
+    int    stack_size;
+    Stack *stack;
+    hash  *functions;      // and labels
+
+    Output  output[OPENFILES];    // open files
+    Output *current_output;       // pointer to one element of 'output'
+
+    static const int MAX_COMMANDS = 32;
+    class Command **command_table;
+    void command_table_setup(bool setup);
+
+    void pop();
+
+    Output *find_output_for_ID(const char *fileID) {
+        if (fileID) {
+            for (int i = 0; i < OPENFILES; i++) {
+                if (output[i].hasID(fileID)) return &output[i];
+            }
+        }
+        return NULL;
+    }
+    Formatter& current_formatter() { return current_output->get_formatter(); }
+
+    void write_var(const char *name, const char *val) { stack->hs->write(name, val); }
+    const char *read_var(const char *name) { return stack->hs->read(name); }
+
+    void define_fun(const char *name, const Code *co);
+    const Code *find_fun(const char *name);
+
+    int run_program();
+
+    void jump(const Code *to) { nextpc = to; }
+
+    int do_close(const char *str);
+    int do_create(const char *str);
+    int do_data(const char *str);
+    int do_dumpdata(const char *filename);
+    int do_error(const char *str) { print_error(at(), str); return 1; }
+    int do_exit() { nextpc = 0; return 0; }
+    int do_for(const char *str);
+    int do_gosub(const char *str);
+    int do_goto(const char *str);
+    int do_if(const char *str);
+    int do_moveto(const char *str);
+    int do_next();
+    int do_open(const char *str);
+    int do_out(const char *str);
+    int do_pop();
+    int do_push();
+    int do_return();
+    int do_set(const char *str);
+    int do_makeconst(const char *str);
+    int do_tab(const char *str);
+    int do_tabstop(const char *str);
+    int do_indent(const char *str);
+    int do_warning(const char *str) { print_warning(at(), str); return 0; }
+
+    int do_write_current(const char *str) { return current_output->write(str); }
+    int do_newline() { return current_output->write(""); }
+    int do_write_stdout(const char *str) { return output[0].write(str); }
+
+    int do_write_maybe_start() { current_output->maybe_start(); return 0; }
+    int do_write_maybe(const char *str) { return current_output->maybe_write(str); }
+    int do_write_maybe_end() { return current_output->maybe_end(); }
+    
+    int            compile_program();
+    const Command *find_command(const Code *co);
+
+    bool set_data(const char *filename, int offset_in_line);
+
+public:
+
+    static const Interpreter *instance;
+
+    Interpreter() {
+        pc     = NULL;
+        nextpc = NULL;
+
+        command_table_setup(true);
+
+        stack_size = 0;
+        functions = new hash(HASHSIZE);
+
+        prg   = NULL;
+        stack = NULL;
+
+        output[0].assign_stdout("stdout");
+        output[1].assign_stdout("*");
+        current_output = &output[0];
+
+        ASSERT_RESULT(int, 0, do_push());
+        
+        aisc_assert(!instance); // singleton!
+        instance = this;
+    }
+    ~Interpreter() {
+        aisc_assert(instance == this);
+        instance = NULL;
+
+        delete prg;
+        delete functions;
+        while (stack) pop();
+        command_table_setup(false);
+
+    }
+
+    int launch(int argc, char ** argv);
+
+
+    const Code *at() const { return pc; }
+    const Data& get_data() const { return data; }
+
+    const char *read_local(const char *key) const;
+    var_ref get_local(const char *key);
+};
+
+#else
+#error aisc.h included twice
+#endif // AISC_H
+
+
+


=====================================
AISC/aisc_location.h
=====================================
@@ -0,0 +1,77 @@
+//   Coded by Ralf Westram (coder at reallysoft.de) in March 2011   //
+//   Institute of Microbiology (Technical University Munich)     //
+//   http://www.arb-home.de/                                     //
+
+#ifndef AISC_LOCATION_H
+#define AISC_LOCATION_H
+
+#ifndef DUPSTR_H
+#include <dupstr.h>
+#endif
+
+class Location {
+    int linenr;
+    char *path;
+    
+    void print_internal(const char *msg, const char *msg_type, const char *launcher_file, int launcher_line) const;
+
+    void fprint_location(const char *file, int line, FILE *fp) const {
+        fprintf(fp, "%s:%i: ", file, line);
+    }
+    void fprint_location(FILE *fp) const {
+        fprint_location(path, linenr, fp);
+    }
+
+    static Location exit_pc;
+    static int global_error_count;
+    
+public:
+
+    Location() : linenr(-666), path(NULL) {}
+    Location(int linenr_, const char *path_)
+        : linenr(linenr_), path(strdup(path_)) { }
+    Location(const Location& other)
+        : linenr(other.linenr), path(strdup(other.path)) { }
+    Location& operator=(const Location& other) {
+        linenr = other.linenr;
+        freedup(path, other.path);
+        return *this;
+    }
+    ~Location() { free(path); }
+
+    bool valid() const { return path != 0; }
+
+    const char *get_path() const { return path; }
+    int get_linenr() const { return linenr; }
+
+    void print_error_internal(const char *err, const char *launcher_file, int launcher_line) const {
+        print_internal(err, "Error", launcher_file, launcher_line);
+        global_error_count++;
+    }
+    void print_warning_internal(const char *msg, const char *launcher_file, int launcher_line) const {
+        print_internal(msg, "Warning", launcher_file, launcher_line);
+    }
+    void start_message(const char *prefix) const {
+        print_internal(NULL, prefix, NULL, 0);
+    }
+
+    static const Location& guess_pc();
+    static void announce_exit_pc(const Location& exitingHere) { exit_pc = exitingHere; }
+
+    static int get_error_count() { return global_error_count; }
+
+    Location& operator++() {
+        aisc_assert(valid());
+        linenr++;
+        return *this;
+    }
+    bool operator == (const Location& other) const {
+        if (!valid()) return !other.valid();
+        return linenr == other.linenr && strcmp(path, other.path) == 0;
+    }
+    bool operator != (const Location& other) const { return !(*this == other); }
+};
+
+#else
+#error aisc_location.h included twice
+#endif // AISC_LOCATION_H


=====================================
AISC/aisc_mix.c
=====================================
@@ -0,0 +1,270 @@
+// ================================================================
+/*                                                                  */
+//   File      : aisc_mix.c
+//   Purpose   :
+/*                                                                  */
+//   Institute of Microbiology (Technical University Munich)
+//   http://www.arb-home.de/
+/*                                                                  */
+// ================================================================
+
+#include "aisc_interpreter.h"
+
+static Code *aisc_calc_blocks(Code * co, Code * afor, Code * aif, int up) {
+    Code *oif;
+    Code *ofor;
+    Code *aelse;
+    Code *anext;
+
+    while (co) {
+        while (co && (!co->command)) {
+            co->IF = aif;
+            co->FOR = afor;
+            co = co->next;
+        }
+        if (!co) return 0;
+        co->IF = aif;
+        co->FOR = afor;
+        switch (co->command) {
+            case CT_NEXT:
+                if (!co->FOR) print_error(co, "NEXT without FOR");
+                return co;
+            case CT_ENDFOR:
+                if (!co->FOR) print_error(co, "ENDFOR without FOR");
+                return co;
+            case CT_ELSE:
+                if (!co->IF) print_error(co, "ELSE without IF");
+                return co;
+            case CT_ELSEIF:
+                if (!co->IF) print_error(co, "ELSEIF without IF");
+                return co;
+            case CT_ENDIF:
+                if (!co->IF) print_error(co, "ENDIF without IF");
+                return co;
+
+            case CT_IF:
+                oif = aif;
+                aif = co;
+                co  = aisc_calc_blocks(co->next, afor, aif, 0);
+                if (!co) {
+                    print_error(aif, "IF without ELSE or ENDIF");
+                    return 0;
+                }
+                if (co->command == CT_ELSE) {
+                    aif->ELSE=co;
+                    aelse = co;
+                    co = aisc_calc_blocks(co->next, afor, aif, 0);
+                    if (!co) {
+                        print_error(aif, "ELSE without ENDIF");
+                        return 0;
+                    }
+                    if  (co->command!=CT_ENDIF) {
+                        print_error(aif, "ELSE without ENDIF");
+                        print_error(co, "<detected here>");
+                        return 0;
+                    }
+                    aif->ENDIF=co;
+                    aelse->ENDIF=co;
+                    if (up) return co;
+                }
+                else if (co->command == CT_ELSEIF) {
+                    Code *co_if    = new Code(*co);
+                    co_if->command = CT_IF;   // when jumped to (from prev failed IF, act like IF)
+                    co->command    = CT_ELSE; // when running into, act like else
+                    co ->next      = co_if;
+                    freenull(co->str);
+                    aif->ELSE = co;
+                    aelse     = co;
+                    co        = aisc_calc_blocks(co_if, afor, aif, 1);
+                    if (!co) {
+                        print_error(aif->ELSE, "ELSEIF without ENDIF or ELSE");
+                        return 0;
+                    }
+                    if  (co->command!=CT_ENDIF) {
+                        print_error(aif->ELSE, "ELSEIF without ENDIF");
+                        print_error(co, "<detected here>");
+                        return 0;
+                    }
+                    aif->ENDIF   = co;
+                    co_if->ENDIF = co;
+                    aelse->ENDIF = co;
+                    if (up) return co;
+                }
+                else if (co->command == CT_ENDIF) {
+                    aif->ELSE  = co;
+                    aif->ENDIF = co;
+                    if (up) return co;
+                }
+                else {
+                    print_error(aif, "IF without ELSE or ENDIF");
+                    print_error(co, "<detected here>");
+                    return 0;
+                }
+                aif = oif;
+                break;
+            case CT_FOR:
+                ofor = afor;
+                afor = co;
+                co = aisc_calc_blocks(co->next, afor, aif, 0);
+                if (!co) {
+                    print_error(afor, "FOR without NEXT or ENDFOR");
+                    return 0;
+                }
+                if (co->command == CT_NEXT) {
+                    afor->NEXT=co;
+                    anext = co;
+                    co = aisc_calc_blocks(co->next, afor, aif, 0);
+                    if (!co) {
+                        print_error(afor->NEXT, "NEXT without ENDFOR");
+                        return 0;
+                    }
+                    if  (co->command!=CT_ENDFOR) {
+                        print_error(afor->NEXT, "NEXT without ENDFOR");
+                        print_error(co, "<detected here>");
+                        return 0;
+                    }
+                    afor->ENDFOR  = co;
+                    anext->ENDFOR = co;
+
+                }
+                else if (co->command == CT_ENDFOR) {
+                    afor->ENDFOR = co;
+                    afor->NEXT   = co;
+                    co->command  = CT_NEXT;
+                }
+                else {
+                    print_error(afor, "FOR without NEXT or ENDFOR");
+                    print_error(co, "<detected here>");
+                    return 0;
+                }
+                afor = ofor;
+                break;
+            default:
+                break;
+
+        }
+        co = co->next;
+    }
+    return 0;
+}
+
+int Interpreter::compile_program() {
+    for (Code *co=prg; co; co=co->next) {
+        if (!strncmp(co->str, "IF", 2))     { co->set_command(CT_IF, co->str+2); continue; }
+        if (!strncmp(co->str, "ELSEIF", 6)) { co->set_command(CT_ELSEIF, co->str+6); continue; }
+        if (!strncmp(co->str, "ELSE", 4))   { co->set_command(CT_ELSE, co->str+4); continue; }
+        if (!strncmp(co->str, "ENDIF", 5))  { co->set_command(CT_ENDIF, co->str+5); continue; }
+        if (!strncmp(co->str, "FOR", 3))    { co->set_command(CT_FOR, co->str+3); continue; }
+        if (!strncmp(co->str, "ENDFOR", 6)) { co->set_command(CT_ENDFOR, co->str+6); continue; }
+        if (!strncmp(co->str, "NEXT", 4))   { co->set_command(CT_NEXT, co->str+4); continue; }
+        
+        if (!strncmp(co->str, "LABEL", 5)) {
+            co->set_command(CT_LABEL, co->str+5);
+            define_fun(co->str, co);
+            continue;
+        }
+        if (!strncmp(co->str, "FUNCTION", 8)) {
+            char *buf1, *buf2;
+            char *s, *s2;
+            buf1 = co->str+8;
+            co->command = CT_FUNCTION;
+            SKIP_SPACE_LF(buf1);
+            for (s=buf1; !is_SPACE_SEP_LF_EOS(*s); s++) ;
+            if (*s) {
+                *s = 0;
+                s++;
+                SKIP_SPACE_LF(s);
+                s2 = strdup(s);
+            }
+            else {
+                s2 = strdup("");
+            }
+            buf2    = strdup(buf1);
+            free(co->str);
+            co->str = s2;
+            define_fun(buf2, co);
+            free(buf2);
+            continue;
+        }
+
+        co->command = CT_OTHER_CMD;
+        co->cmd     = find_command(co);
+    }
+
+    Code *co = aisc_calc_blocks(prg, 0, 0, 0);
+    if (co) {
+        print_error(co, "program is not well formed");
+    }
+
+    return 0;
+}
+
+// -------------
+//      hash
+
+hash::hash(int size_) {
+    size = size_;
+    entries = (hash_entry **)calloc(sizeof(hash_entry *), size);
+}
+hash::~hash() {
+    for (int i = 0; i<size; ++i) {
+        hash_entry *enext;
+        for (hash_entry *e=entries[i]; e; e=enext) {
+            if (e->val) free(e->val);
+            free(e->key);
+            enext = e->next;
+            free(e);
+        }
+    }
+    free(entries);
+}
+
+int hash::Index(const char *key) const {
+    const char *p = key;
+    int         x = 1;
+    char        c;
+    
+    while ((c=*(p++))) {
+        x = (x<<1) ^ c;
+    }
+    x %= size;
+    if (x<0) x += size;
+    return x;
+}
+
+const hash_entry *hash::find_entry(const char *key, int idx) const {
+    for (hash_entry *e = entries[idx]; e; e=e->next) {
+        if (!strcmp(e->key, key)) return e;
+    }
+    return 0;
+}
+
+void hash::write(const char *key, const char *val) {
+    int         idx = Index(key);
+    hash_entry *e   = find_entry(key, idx);
+    if (e) {
+        freeset(e->val, nulldup(val));
+    }
+    else {
+        e       = (hash_entry *)calloc(sizeof(hash_entry), 1);
+        e->next = entries[idx];
+        e->key  = strdup(key);
+        e->val  = val ? strdup(val) : 0;
+        
+        entries[idx] = e;
+    }
+}
+
+var_ref Interpreter::get_local(const char *key) {
+    var_ref ref;
+    for (Stack *s = stack; s && !ref; s = s->next) {
+        ref = s->hs->ref(key);
+    }
+    return ref;
+}
+
+const char *Interpreter::read_local(const char *key) const {
+    const var_ref local = const_cast<Interpreter*>(this)->get_local(key);
+    return local.read();
+}
+


=====================================
AISC/aisc_parser.c
=====================================
@@ -0,0 +1,424 @@
+//   Coded by Ralf Westram (coder at reallysoft.de) in March 2011   //
+//   Institute of Microbiology (Technical University Munich)     //
+//   http://www.arb-home.de/                                     //
+
+#include "aisc_parser.h"
+
+const char *Parser::currentLocation(const char *io) {
+    const int   LOCBUFFERSIZE = 1024;
+    static char loc_buf[LOCBUFFERSIZE+1];
+    int         printed;
+
+    if (last_line_start) {
+        int column = io - last_line_start + 1;
+
+        aisc_assert(column >= 0 && column<10000);
+        printed = sprintf(loc_buf, "%s:%i:%i", loc.get_path(), loc.get_linenr(), column);
+    }
+    else {
+        printed = sprintf(loc_buf, "%s:%i", loc.get_path(), loc.get_linenr());
+    }
+
+    if (printed>LOCBUFFERSIZE) {
+        fprintf(stderr, "AISC: Internal buffer overflow detected -- terminating [loc_buf]\n");
+        error_flag = 1;
+    }
+
+    return loc_buf;
+}
+
+void Parser::p_err(const char *io, const char *error) {
+    fprintf(stderr, "%s: Error: %s\n", currentLocation(io), error);
+    error_flag = 1;
+}
+
+#ifdef LINUX
+# define HAVE_VSNPRINTF
+#endif
+#ifdef HAVE_VSNPRINTF
+# define PRINT2BUFFER(buffer, bufsize, templat, parg) vsnprintf(buffer, bufsize, templat, parg);
+#else
+# define PRINT2BUFFER(buffer, bufsize, templat, parg) vsprintf(buffer, templat, parg);
+#endif
+
+#define ERRBUFFERSIZE 1024
+void Parser::p_errf(const char *io, const char *formatString, ...) {
+    static char buf[ERRBUFFERSIZE+1];
+    int         printed;
+    va_list     varArgs;
+
+    va_start(varArgs, formatString);
+    printed = PRINT2BUFFER(buf, ERRBUFFERSIZE, formatString, varArgs);
+    va_end(varArgs);
+
+    if (printed>ERRBUFFERSIZE) {
+        fprintf(stderr, "AISC: Internal buffer overflow detected -- terminating [err]\n");
+        error_flag = 1;
+    }
+    p_err(io, buf);
+}
+
+void Parser::copyTillQuotesTo(const char*& in, char*& out) {
+    // closing quotes are END_STR1 plus END_STR2
+    bool quotes_closed = false;
+    while (!quotes_closed) {
+        while ((lastchar != EOSTR) && (lastchar != END_STR1)) {
+            *(out++) = lastchar;
+            get_byte(in);
+        }
+
+        if (lastchar == END_STR1) {
+            get_byte(in);
+            if (lastchar == END_STR2) {
+                get_byte(in);
+                quotes_closed = true;
+            }
+            else {
+                *(out++) = END_STR1;
+            }
+        }
+    }
+}
+
+char *Parser::readWord(const char *& in) {
+    char  buf[1024];
+    char *cp = buf;
+
+    if (lastchar == BEG_STR1) {
+        get_byte(in);
+        if (lastchar == BEG_STR2) {
+            get_byte(in);
+            copyTillQuotesTo(in, cp);
+        }
+        else {
+            *(cp++) = BEG_STR1;
+            copyWordTo(in, cp);
+        }
+    }
+    else copyWordTo(in, cp);
+
+    if (lastchar != EOSTR) skip_over_spaces_and_comments(in);
+
+    char *result = NULL;
+    if (lastchar == EOSTR) {
+        p_err_exp_but_saw_EOF(in, "terminator after string");
+    }
+    else if (cp != buf) { // do not return empty string, return NULL instead
+        *cp    = 0;
+        result = strdup(buf);
+    }
+
+    return result;
+}
+
+char *Parser::SETSOURCE(const char *& in, enum TOKEN& foundTokenType) {
+    char       *result = NULL;
+    const char *space  = in+9;
+
+    if (*space != ' ') p_err(in, "space expected after '@SETSOURCE' (injected code)");
+    else {
+        in = space;
+        get_byte(in);
+        skip_over_spaces(in);
+
+        const char *file  = in-1;
+        const char *comma = strchr(file, ',');
+
+        if (!comma) p_err(in, "comma expected after '@SETSOURCE filename' (injected code)");
+        else {
+            const char *end = strchr(comma, '@');
+
+            if (!end) p_err(in, "'@' expected after '@SETSOURCE filename,line' (injected code)");
+            else {
+                char *filename = copy_string_part(file, comma-1);
+                set_source(filename, atoi(comma+1));
+                free(filename);
+
+                in = end+1;
+                get_byte(in);
+
+                result = parse_token(in, foundTokenType);
+            }
+        }
+    }
+    return result;
+}
+
+char *Parser::parse_token(const char *& in, enum TOKEN& foundTokenType) {
+    skip_over_spaces_and_comments_multiple_lines(in);
+
+    char *result   = NULL;
+    foundTokenType = TOK_INVALID;
+
+    switch (lastchar) {
+        case EOSTR: foundTokenType = TOK_EOS;         break;
+        case '{':   foundTokenType = TOK_BRACE_OPEN;  break;
+        case '}':   foundTokenType = TOK_BRACE_CLOSE; break;
+        case ',':   foundTokenType = TOK_COMMA;       break;
+        case ';':   foundTokenType = TOK_SEMI;        break;
+
+        case '@':
+            if (strncmp(in, "SETSOURCE", 9) == 0) {
+                result = SETSOURCE(in, foundTokenType);
+            }
+            else {
+                get_byte(in);
+                skip_over_spaces(in);
+                if (lastchar == EOSTR) {
+                    p_err_exp_but_saw_EOF(in, "ID behind '@'");
+                }
+                else {
+                    result         = readWord(in);
+                    foundTokenType = TOK_AT_WORD;
+                }
+            }
+            break;
+
+        default:
+            result         = readWord(in);
+            foundTokenType = TOK_WORD;
+            break;
+    }
+
+    return result;
+}
+
+class Header : virtual Noncopyable {
+    char   *key;
+    Header *next;
+ public:
+    Header(const char *key_) { key = strdup(key_); next = NULL; }
+    ~Header() { free(key); delete next; }
+
+    void set_next(Header *header) { aisc_assert(!next); next = header; }
+
+    const char *get_key() const { return key; }
+    const Header *next_header() const { return next; }
+};
+
+class HeaderList : virtual Noncopyable {
+    Header *head;
+    Header *tail;
+
+    char *loc_defined_at;
+
+ public:
+    HeaderList() {
+        head = tail = NULL;
+        loc_defined_at = NULL;
+    }
+    void reset() {
+        delete head;
+        head           = tail = NULL;
+        free(loc_defined_at);
+        loc_defined_at = NULL;
+    }
+    ~HeaderList() { reset(); }
+
+    void set_first_header(Header *header, const char *location) {
+        aisc_assert(!head);
+        head           = tail = header;
+        loc_defined_at = strdup(location);
+    }
+    void append(Header *header) {
+        aisc_assert(head);      // use set_first_header()
+        tail->set_next(header);
+        tail = header;
+    }
+
+    const Header *first_header() const { return head; }
+    const char *defined_at() const { return loc_defined_at; }
+};
+
+Token *Parser::parseBrace(const char *& in, const char *key) {
+    Token          *res     = NULL;
+    if (!error_flag) {
+        char           *openLoc = strdup(currentLocation(in));
+        TokenListBlock *block   = parseTokenListBlock(in);
+
+        if (block) {
+            res = new Token(key, block);
+            block = NULL;
+            expect_and_skip_closing_brace(in, openLoc);
+        }
+        else {
+            p_err_empty_braces(in);
+        }
+        delete block;
+        free(openLoc);
+    }
+    return res;
+}
+
+TokenList *Parser::parseTokenList(const char *& in, HeaderList& headerList) {
+    TokenList    *items  = new TokenList;
+    const Header *header = headerList.first_header();
+
+    get_byte(in);
+
+    bool reached_end_of_list = false;
+    while (!error_flag && !reached_end_of_list) {
+        TOKEN  foundTokenType;
+        char  *str = parse_token(in, foundTokenType);
+
+        if (!error_flag) {
+            switch (foundTokenType) {
+                case TOK_SEMI:
+                case TOK_BRACE_CLOSE:
+                    reached_end_of_list = true;
+                    break;
+
+                case TOK_BRACE_OPEN: {
+                    Token *sub = parseBrace(in, header ? header->get_key() : "{");
+                    if (sub) items->append(sub);
+                    break;
+                }
+                case TOK_COMMA:
+                    if (!header) p_err_exp_but_saw(in, "string", "','");
+                    else get_byte(in);
+                    break;
+
+                case TOK_AT_WORD:
+                    if (header != headerList.first_header()) {
+                        p_err_ill_atWord(in);
+                    }
+                    else {
+                        if (!str) {
+                            p_err_expected(in, "ID behind '@'");
+                        }
+                        else {
+                            headerList.reset();
+                            headerList.set_first_header(new Header(str), currentLocation(in));
+                            header = headerList.first_header();
+                        }
+                        while (lastchar == ',' && !error_flag) {
+                            get_byte(in);
+                            char *str2 = parse_token(in, foundTokenType);
+                            if (foundTokenType != TOK_AT_WORD) p_err_exp_atWord(in);
+                            else headerList.append(new Header(str2));
+                            free(str2);
+                        }
+                        if (!error_flag) expect_and_skip(in, ';');
+                        if (!error_flag) {
+                            aisc_assert(headerList.first_header()->get_key());
+                            reached_end_of_list = true;
+                        }
+                    }
+                    break;
+
+                case TOK_WORD: {
+                    Token *new_token = NULL;
+                    if (header) {
+                        new_token = new Token(header->get_key(), str);
+                        expect_line_terminator(in);
+                    }
+                    else {
+                        char *str2 = parse_token(in, foundTokenType);
+                        switch (foundTokenType) {
+                            case TOK_BRACE_OPEN: {
+                                new_token = parseBrace(in, str);
+                                break;
+                            }
+                            case TOK_WORD:
+                                new_token = new Token(str, str2);
+                                expect_line_terminator(in);
+                                break;
+
+                            case TOK_COMMA:
+                            case TOK_SEMI:
+                                new_token = new Token(str, "");
+                                break;
+
+                            case TOK_AT_WORD:     p_err_exp_string_but_saw(in, "'@'"); break;
+                            case TOK_BRACE_CLOSE: p_err_exp_string_but_saw(in, "'}'"); break;
+
+                            case TOK_INVALID: aisc_assert(0); break;
+                        }
+                        free(str2);
+                    }
+
+                    aisc_assert(new_token || error_flag);
+
+                    if (new_token) items->append(new_token);
+
+                    if (!error_flag) {
+                        if (lastchar == ';') {
+                            const Header *missingVal = header ? header->next_header() : NULL;
+                            if (missingVal) {
+                                char buf[1000];
+                                sprintf(buf, "value for @%s", missingVal->get_key());
+                                p_err_exp_but_saw(in, buf, "';'");
+                            }
+                            else reached_end_of_list = true;
+                            get_byte(in);
+                        }
+                        else get_byte(in);
+                    }
+                    break;
+                }
+
+                case TOK_INVALID:
+                    p_err(in, "Invalid token (internal error)");
+                    break;
+            }
+        }
+
+        if (!error_flag && header) header = header->next_header();
+
+        free(str);
+    }
+
+    if (error_flag || items->empty()) {
+        delete items;
+        items = NULL;
+    }
+
+    return items;
+}
+
+
+TokenListBlock *Parser::parseTokenListBlock(const char *& in) {
+    TokenListBlock *block = new TokenListBlock;
+    HeaderList      headerList;
+
+    while ((EOSTR != lastchar) && (lastchar != '}')) {
+        TokenList *list = parseTokenList(in, headerList);
+        if (!error_flag && list) {
+            block->append(list);
+        }
+    }
+
+    if (block->empty() || error_flag) {
+        delete block;
+        block = NULL;
+    }
+
+    return block;
+}
+
+Code *Parser::parse_program(const char *in, const char *filename) {
+    Code *first_cl = NULL;
+    Code *cl       = NULL;
+
+    set_source(filename, 0);
+
+    while (lastchar != EOSTR) {
+        skip_over_spaces_and_comments_multiple_lines(in);
+
+        if (lastchar == EOSTR) break;
+
+        const char *p = in-1;
+        while ((lastchar != EOSTR) && (lastchar != '\n')) get_byte(in);
+
+        {
+            Code    *hcl  = new Code;
+            Code *&  next = cl ? cl->next : first_cl;
+            cl          = next = hcl;
+        }
+        cl->str    = copy_string_part(p, in-2);
+        cl->source = Location(loc.get_linenr(), filename);
+    }
+
+    return first_cl;
+}
+


=====================================
AISC/aisc_parser.h
=====================================
@@ -0,0 +1,208 @@
+//   Coded by Ralf Westram (coder at reallysoft.de) in March 2011   //
+//   Institute of Microbiology (Technical University Munich)     //
+//   http://www.arb-home.de/                                     //
+
+#ifndef AISC_PARSER_H
+#define AISC_PARSER_H
+
+#ifndef AISC_TOKEN_H
+#include "aisc_token.h"
+#endif
+#ifndef AISC_LOCATION_H
+#include "aisc_location.h"
+#endif
+#ifndef AISC_INLINE_H
+#include "aisc_inline.h"
+#endif
+#ifndef ATTRIBUTES_H
+#include <attributes.h>
+#endif
+
+enum CommandType {
+    NO_COMMAND, 
+    CT_IF = 1,
+    CT_ENDIF,
+    CT_ELSE,
+    CT_FOR,
+    CT_NEXT,
+    CT_ENDFOR,
+    CT_ELSEIF,
+    CT_FUNCTION,
+    CT_LABEL,
+    CT_OTHER_CMD,
+};
+
+struct Code {
+    Code *next;
+    char *str;
+
+    Location source;
+
+    CommandType    command;
+    const Command *cmd;
+
+    mutable struct for_data *fd;
+
+    Code *IF;
+    Code *ELSE;
+    Code *ENDIF;
+    Code *FOR;
+    Code *NEXT;
+    Code *ENDFOR;
+
+    Code() {
+        memset(this, 0, sizeof(*this));
+    }
+    Code(const Code& other)
+        : next(other.next),
+          str(nulldup(other.str)),
+          source(other.source), 
+          command(other.command),
+          cmd(other.cmd),
+          fd(other.fd),
+          IF(other.IF),
+          ELSE(other.ELSE),
+          ENDIF(other.ENDIF),
+          FOR(other.FOR),
+          NEXT(other.NEXT),
+          ENDFOR(other.ENDFOR)
+    {}
+
+    DECLARE_ASSIGNMENT_OPERATOR(Code);
+    ~Code() {
+        delete next;
+        free(str);
+    }
+
+    void set_command(CommandType command_, const char *args) {
+        command = command_;
+        SKIP_SPACE_LF(args);
+        freedup(str, args);
+    }
+
+    void print_error_internal(const char *err, const char *launcher_file, int launcher_line) const {
+        source.print_error_internal(err, launcher_file, launcher_line);
+    }
+    void print_warning_internal(const char *warn, const char *launcher_file, int launcher_line) const {
+        source.print_warning_internal(warn, launcher_file, launcher_line);
+    }
+};
+
+
+class Parser : virtual Noncopyable {
+    // used to parse 'Data' and 'Code'
+    
+    int         lastchar;
+    const char *last_line_start;
+
+    Location loc;
+
+    int error_flag;
+
+    void get_byte(const char *& io) {
+        lastchar = *(io++);
+        if (is_LF(lastchar)) {
+            last_line_start = io;
+            ++loc;
+        }
+    }
+
+    const char *currentLocation(const char *io);
+
+    void p_err(const char *io, const char *error);
+    void p_errf(const char *io, const char *formatString, ...) __ATTR__FORMAT_MEMBER(2);
+    
+    void p_err_empty_braces(const char *io) { p_err(io, "{} found, missing contents"); }
+    void p_err_exp_line_terminator(const char *io) { p_err(io, "missing ',' or ';' or 'newline'"); }
+    void p_err_ill_atWord(const char *io) { p_err(io, "only header definitions may start with '@'"); }
+    void p_err_exp_atWord(const char *io) { p_err(io, "all words in header-definitions have to start with '@'"); }
+    
+    void p_err_expected(const char *io, const char *expect)                     { p_errf(io, "Expected to see %s", expect); }
+    void p_err_exp_but_saw(const char *io, const char *expect, const char *saw) { p_errf(io, "Expected to see %s, but saw %s", expect, saw); }
+
+    void p_err_exp_string_but_saw(const char *io, const char *saw) { p_err_exp_but_saw(io, "string", saw); }
+    void p_err_exp_but_saw_EOF(const char *io, const char *expect) { p_err_exp_but_saw(io, expect, "end of file"); }
+
+    inline void expect_line_terminator(const char *in) {
+        if (!is_SEP_LF_EOS(lastchar)) p_err_exp_line_terminator(in);
+    }
+
+    void expect_and_skip_closing_brace(const char *& in, const char *openingBraceLocation) {
+        if (lastchar != '}') {
+            p_err_expected(in, "'}'");
+            fprintf(stderr, "%s: opening brace was here\n", openingBraceLocation);
+        }
+        get_byte(in);
+    }
+    void expect_and_skip(const char *& in, char expect) {
+        if (lastchar != expect) {
+            char buf[] = "'x'";
+            buf[1]     = expect;
+            p_err_expected(in, buf);
+        }
+        get_byte(in);
+    }
+
+    void skip_over_spaces(const char *& in) { while (is_SPACE(lastchar)) get_byte(in); }
+    void skip_over_spaces_and_comments(const char *& in) {
+        skip_over_spaces(in);
+        if (lastchar == '#') { // comment -> skip rest of line
+            while (!is_LF_EOS(lastchar)) get_byte(in);
+        }
+    }
+    void skip_over_spaces_and_comments_multiple_lines(const char *& in) {
+        while (1) {
+            skip_over_spaces_and_comments(in);
+            if (!is_LF(lastchar)) break;
+            get_byte(in);
+        }
+    }
+
+    void copyWordTo(const char*& in, char*& out) {
+        while (!is_SPACE_SEP_LF_EOS(lastchar)) {
+            *(out++) = lastchar;
+            get_byte(in);
+        }
+    }
+
+    void  copyTillQuotesTo(const char*& in, char*& out);
+    char *readWord(const char *& in);
+
+    char *SETSOURCE(const char *& in, enum TOKEN& foundTokenType);
+    char *parse_token(const char *& in, enum TOKEN& foundTokenType);
+
+    Token *parseBrace(const char *& in, const char *key);
+    TokenList *parseTokenList(const char *& in, class HeaderList& headerList);
+
+public:
+    Parser() {
+        lastchar = ' ';
+        last_line_start = NULL;
+        error_flag      = 0;
+    }
+
+    int get_sourceline() const { return loc.get_linenr(); }
+    const char *get_sourcename() const { return loc.get_path(); }
+    const Location& get_location() const { return loc; }
+
+    void set_source(const Location& other) {
+        aisc_assert(loc != other);
+        loc = other;
+    }
+    void set_source(const char *path, int linenumber) {
+        set_source(Location(linenumber, path));
+    }
+
+    void set_line_start(const char *start, int offset_in_line) {
+        last_line_start = start-offset_in_line;
+        lastchar        = ' ';
+    }
+
+    TokenListBlock *parseTokenListBlock(const char *& in);
+    class Code *parse_program(const char *in, const char *filename);
+};
+
+
+#else
+#error aisc_parser.h included twice
+#endif // AISC_PARSER_H


=====================================
AISC/aisc_proto.h
=====================================
@@ -0,0 +1,30 @@
+/* This file is generated by aisc_mkpt.
+ * Any changes you make here will be overwritten later!
+ */
+
+#ifndef AISC_PROTO_H
+#define AISC_PROTO_H
+
+/* define ARB attributes: */
+#ifndef ATTRIBUTES_H
+# include <attributes.h>
+#endif
+
+
+/* aisc.c */
+
+#ifndef AISC_DEF_H
+#include "aisc_def.h"
+#endif
+
+char *read_aisc_file(const char *path, const Location *loc);
+
+/* aisc_commands.c */
+const char *formatted(const char *format, ...) __ATTR__FORMAT(1);
+
+/* aisc_var_ref.c */
+char *get_var_string(const Data& data, char *var, bool allow_missing_var);
+
+#else
+#error aisc_proto.h included twice
+#endif /* AISC_PROTO_H */


=====================================
AISC/aisc_token.h
=====================================
@@ -0,0 +1,171 @@
+//   Coded by Ralf Westram (coder at reallysoft.de) in March 2011   //
+//   Institute of Microbiology (Technical University Munich)     //
+//   http://www.arb-home.de/                                     //
+
+#ifndef AISC_TOKEN_H
+#define AISC_TOKEN_H
+
+#ifndef AISC_DEF_H
+#include "aisc_def.h"
+#endif
+#ifndef ARBTOOLS_H
+#include <arbtools.h>
+#endif
+
+// ------------------------------------------------------------
+// structures holding data read from *.aisc files
+
+enum TOKEN {
+    TOK_WORD = 100,             // normal words
+    TOK_AT_WORD,                // words starting with '@'
+    TOK_BRACE_CLOSE,
+    TOK_BRACE_OPEN,
+    TOK_COMMA,
+    TOK_SEMI,
+    TOK_EOS  = TOK_SEMI,        // simulate a semicolon at EOSTR
+
+    TOK_INVALID,
+};
+
+
+
+class TokenList;
+class TokenListBlock;
+
+// --------------
+//      Token
+
+class Token : virtual Noncopyable {
+    Token     *next;                                // (owned)
+    TokenList *parent;
+
+    bool  isBlock;
+    char *key;
+    union {
+        TokenListBlock *sub;                        // (owned), NULL = empty block
+        char           *val;                        // NULL means ""
+    } content;
+
+public:
+    Token(const char *key_, const char *val_)
+        : next(NULL)
+        , parent(NULL)
+        , isBlock(false)
+        , key(strdup(key_))
+    {
+        content.val = val_ ? strdup(val_) : NULL;
+    }
+    Token(const char *key_, TokenListBlock *block_); // takes ownage of block_
+    ~Token();
+
+    void append(Token *tok) { next = tok; }
+    void set_parent(TokenList *list) { aisc_assert(!parent); parent = list; }
+
+    const char *get_key() const { return key; }
+
+    bool is_block() const { return isBlock; }
+    const TokenListBlock *get_content() const { aisc_assert(isBlock); return content.sub; }
+
+    bool has_value() const { return !is_block() && content.val; }
+    const char *get_value() const { aisc_assert(has_value()); return content.val; }
+
+    const Token *next_token() const { return next; }
+    const TokenList *parent_list() const { return parent; }
+    const Token *parent_block_token() const;
+};
+
+// ------------------
+//      TokenList
+
+class TokenList : virtual Noncopyable {
+    Token          *head;                           // list of tokens (owned)
+    Token          *tail;
+    TokenList      *next;                           // owned
+    TokenListBlock *parent;
+
+public:
+    TokenList() {
+        head   = NULL;
+        tail   = NULL;
+        next   = NULL;
+        parent = NULL;
+    }
+    ~TokenList() {
+        delete head;
+        delete next;
+    }
+
+    void append(Token *tok) {
+        if (!head) head = tok;
+        else tail->append(tok);
+
+        tail = tok;
+        tok->set_parent(this);
+    }
+    void append(TokenList *cmd) { next = cmd; }
+    void set_parent(TokenListBlock *block) { parent = block; }
+
+    bool empty() const { return !head; }
+    const Token *first_token() const { return head; }
+    const TokenList *next_list() const { return next; }
+    const TokenListBlock *parent_block() const { return parent; }
+};
+
+// -----------------------
+//      TokenListBlock
+
+class TokenListBlock : virtual Noncopyable {
+    TokenList   *head;                              // list of TokenLists (owned)
+    TokenList   *tail;
+    const Token *parent;
+
+public:
+    TokenListBlock() {
+        head   = NULL;
+        tail   = NULL;
+        parent = NULL;
+    }
+    ~TokenListBlock() { delete head; }
+
+    bool empty() const { return !head; }
+    void append(TokenList *cmd) {
+        if (!head) head = cmd;
+        else tail->append(cmd);
+
+        tail = cmd;
+        cmd->set_parent(this);
+    }
+    void set_block_token(Token *tok) { aisc_assert(!parent); parent = tok; }
+
+    const TokenList *first_list() const { return head; }
+    const Token *first_token() const { return head->first_token(); }
+    const Token *block_token() const { return parent; }
+};
+
+// ------------------------------------------------------------
+
+inline Token::Token(const char *key_, TokenListBlock *block_)
+    : next(NULL)
+    , parent(NULL)
+    , isBlock(true)
+    , key(strdup(key_))
+{
+    aisc_assert(block_);
+    content.sub = block_;
+    if (content.sub) {
+        content.sub->set_block_token(this);
+    }
+}
+inline Token::~Token() {
+    free(key);
+    if (isBlock) delete content.sub;
+    else free(content.val);
+    delete next;
+}
+inline const Token *Token::parent_block_token() const {
+    return parent_list()->parent_block()->block_token();
+}
+
+#else
+#error aisc_token.h included twice
+#endif // AISC_TOKEN_H


=====================================
AISC/aisc_var_ref.c
=====================================
@@ -0,0 +1,283 @@
+// ================================================================
+/*                                                                  */
+//   File      : aisc_var_ref.c
+//   Purpose   :
+/*                                                                  */
+//   Institute of Microbiology (Technical University Munich)
+//   http://www.arb-home.de/
+ /*                                                                  */
+ // ================================================================
+
+#include "aisc_token.h"
+#include "aisc_inline.h"
+#include "aisc_interpreter.h"
+
+
+class TokenMatcher : virtual Noncopyable {
+    char *key;                  // NULL matches "any key"
+    char *value;                // NULL matches "any value"
+
+    char *copy_expression_part(const char *start, const char *end) {
+        if (start >= end || (end == (start+1) && start[0] == '*')) return NULL;
+        return copy_string_part(start, end);
+    }
+
+ public:
+    TokenMatcher(const char *search_expr) {
+        // search_expr is "key.value"
+        //
+        // "*.value"    searches value (regardless of key)
+        // "key.*"      searches key (regardless of value)
+        //
+        // "key"    = "key." = "key.*"
+        // ".value" = "*.value"
+
+        const char *dot = strchr(search_expr, '.');
+        if (!dot) {
+            key = strdup(search_expr);
+            value = NULL;
+        }
+        else {
+            const char *end = strchr(dot+1, 0);
+
+            key   = copy_expression_part(search_expr, dot-1);
+            value = copy_expression_part(dot+1, end-1);
+        }
+    }
+
+    ~TokenMatcher() {
+        free(key);
+        free(value);
+    }
+
+    bool matchesKeyOf(const Token *token) const {
+        return !key || strcmp(token->get_key(), key) == 0;
+    }
+    bool matchesValueOf(const Token *token) const {
+        return !value || (!token->is_block() && strcmp(value, token->get_value()) == 0);
+    }
+    bool matches(const Token *token) const {
+        return matchesKeyOf(token) && matchesValueOf(token);
+    }
+};
+
+static const Token *nextToken(const Token *tok, bool cont_in_next_list) {
+    const Token *next_token = tok->next_token();
+    if (!next_token && cont_in_next_list) {
+        const TokenList *next_list  = tok->parent_list()->next_list();
+        if (next_list) next_token = next_list->first_token();
+    }
+    return next_token;
+}
+
+static const Token *nextTokenMatching(const Token *tok, const TokenMatcher& wanted, bool cont_in_next_list) {
+    while (tok) {
+        if (wanted.matches(tok)) break;
+        tok = nextToken(tok, cont_in_next_list);
+    }
+    return tok;
+}
+
+const Token *Data::find_token(const Token *curs, const char *str, LookupScope scope) const {
+    if (!curs) {
+        const TokenListBlock *tokens = get_tokens();
+        if (!tokens) return NULL;
+        curs = tokens->first_token();
+    }
+
+    TokenMatcher  wanted(str);
+    const Token  *found = NULL;
+
+    while (curs && !found) {
+        bool cont_in_next_list = false;
+        switch (scope) {
+            case LOOKUP_LIST_OR_PARENT_LIST:
+            case LOOKUP_LIST:
+                curs              = curs->parent_list()->first_token();
+                break;
+            case LOOKUP_BLOCK:
+                curs              = curs->parent_list()->parent_block()->first_token();
+                cont_in_next_list = true;
+                break;
+            case LOOKUP_BLOCK_REST:
+                curs              = nextToken(curs, true);
+                cont_in_next_list = true;
+                break;
+        }
+
+        found = nextTokenMatching(curs, wanted, cont_in_next_list);
+
+        if (scope == LOOKUP_LIST_OR_PARENT_LIST) {
+            curs = curs->parent_block_token();
+        }
+        else {
+            curs = 0;
+        }
+    }
+
+    return found;
+}
+
+const Token *Data::find_qualified_token(const char *str, LookupScope scope) const {
+    const Token *at = cursor;
+    if (*str == '/') {
+        at = 0;
+        str++;
+    }
+
+    const char *slash = strchr(str, '/');
+    while (slash) {
+        char        *name  = copy_string_part(str, slash-1);
+        const Token *found = find_token(at, name, scope);
+        free(name);
+
+        if (!found || !found->is_block()) return 0;
+
+        at = found->get_content()->first_token();
+
+        str   = slash+1;
+        slash = strchr(str, '/');
+        scope = LOOKUP_BLOCK;
+    }
+    
+    return find_token(at, str, scope);
+}
+
+
+char *get_var_string(const Data& data, char *var, bool allow_missing_var) { 
+    // Calculates result for an expression like '$(IDENT:fdg|"sdfg")'
+    SKIP_SPACE_LF(var);
+    int use_path = 0;
+    while (var[0] == '&') {
+        use_path++;
+        var++;
+        SKIP_SPACE_LF(var);
+    }
+
+    char *doppelpunkt = strchr(var, ':'); if (doppelpunkt) *(doppelpunkt++) = 0;
+    char *bar         = strchr(var, '|'); if (bar) *(bar++) = 0;
+
+    const char *val = Interpreter::instance->read_local(var);
+    char       *in  = NULL;
+    if (val) {
+        in = strdup(val);
+    }
+    else {
+        const Token *cur = data.find_qualified_token(var, LOOKUP_LIST_OR_PARENT_LIST);
+        if (!cur) {
+            if (!bar) {
+                if (!allow_missing_var) {
+                    const Code *at = Interpreter::instance->at();
+                    printf_error(at, "Ident '%s' not found", var);
+                }
+                return 0;
+            }
+            return strdup(bar);
+        }
+        if (use_path) {
+            in = strdup(cur->get_key());
+            if (use_path>1) {
+                while (1) {
+                    const Token *up = cur->parent_block_token();
+                    if (!up) break;
+
+                    cur        = up;
+                    int   len  = strlen(in) + strlen(cur->get_key());
+                    char *buf1 = (char *) calloc(sizeof(char), len + 2);
+                    sprintf(buf1, "%s/%s", cur->get_key(), in);
+                    
+                    free(in);
+                    in = buf1;
+                }
+
+            }
+        }
+        else {
+            if (cur->is_block()) {
+                printf_error(Interpreter::instance->at(), "Ident '%s' is a hierarchical type", var);
+                return 0;
+            }
+            else {
+                in = strdup(cur->has_value() ? cur->get_value() : "");
+            }
+        }
+    }
+
+    aisc_assert(in); // 'in' has to point to a heap copy
+
+    if (doppelpunkt) {
+        int  len = strlen(in);
+        bool err = false;
+        while (doppelpunkt && !err) {
+            char *nextdp            = strchr(doppelpunkt, ':');
+            if (nextdp) *(nextdp++) = 0;
+            if (!doppelpunkt[0]) {
+                print_error(Interpreter::instance->at(), "Ident replacement is missing an ':'");
+                err = true;
+            }
+            else {
+                bar = strchr(doppelpunkt+1, '=');
+                if (!bar) {
+                    print_error(Interpreter::instance->at(), "Ident replacement is missing an '='");
+                    err = true;
+                }
+                else {
+                    *(bar++)     = 0;
+                    int findl    = strlen(doppelpunkt);
+                    int replacel = strlen(bar);
+
+                    char *buf2;
+                    for (char *finds = strstr(in, doppelpunkt); finds; finds = strstr(buf2, doppelpunkt)) {
+                        len += replacel - findl;
+
+                        char *buf1   = (char *) calloc(sizeof(char), len + 1);
+                        int   offset = finds - in;
+                        
+                        memcpy(buf1, in, offset);
+                        memcpy(buf1 + offset, bar, replacel);
+
+                        buf2 = buf1 + offset + replacel;
+                        memcpy(buf2, in + offset + findl, len - replacel - offset);
+
+                        free(in);
+                        in = buf1;
+                        
+                        buf2 = in + offset + replacel;
+                    }
+                    doppelpunkt = nextdp;
+                }
+            }
+        }
+        if (err) {
+            free(in);
+            in = NULL;
+        }
+    }
+    return in;
+}
+
+static void dump_token_recursive(const Token *tok, FILE *out) {
+    const Token *block = tok->parent_block_token();
+    if (block) {
+        const TokenList *parent = block->parent_list();
+        if (parent) {
+            const Token *first = parent->first_token();
+            if (first) {
+                dump_token_recursive(first, out);
+                fputc('/', out);
+            }
+        }
+    }
+
+    fputc('@', out);
+    fputs(tok->get_key(), out);
+
+    if (tok->has_value()) {
+        fputc('=', out);
+        fputs(tok->get_value(), out);
+    }
+}
+
+void Data::dump_cursor_pos(FILE *out) const {
+    dump_token_recursive(cursor, out);
+}



View it on GitLab: https://salsa.debian.org/med-team/libarb/commit/93facad9598658f907a6bf47009b4337f63d0428

-- 
View it on GitLab: https://salsa.debian.org/med-team/libarb/commit/93facad9598658f907a6bf47009b4337f63d0428
You're receiving this email because of your account on salsa.debian.org.

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/debian-med-commit/attachments/20190606/3fb39f92/attachment-0001.html>


More information about the debian-med-commit mailing list