[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