Skip to content
Commits on Source (7)
# 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
// ================================================================
/* */
// 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;
}
// ================================================================ //
// //
// 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
This diff is collapsed.
// 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
// 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@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;
}
// Coded by Ralf Westram (coder@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
// Coded by Ralf Westram (coder@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
// ================================================================ //
// //
// 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
// Coded by Ralf Westram (coder@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
// ================================================================
/* */
// 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();
}
// Coded by Ralf Westram (coder@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;
}
// Coded by Ralf Westram (coder@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
/* 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 */
// Coded by Ralf Westram (coder@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
// ================================================================
/* */
// 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);
}
......@@ -3,9 +3,7 @@ Upstream-Name: Arb
Upstream-Contact: Arb development team <devel@arb-home.de>,
Ralf Westram <ralf@arb-home.de>
Source: http://download.arb-home.de/release/
Files-Excluded: arbsrc*/AISC/aisc*
arbsrc*/AISC/Makefile
arbsrc*/AISC_COM/C
Files-Excluded: arbsrc*/AISC_COM/C
arbsrc*/AISC_COM/Makefile
arbsrc*/AISC_COM/AISC/Makefile
arbsrc*/AISC_COM/AISC/aisc*
......
--- a/Makefile
+++ b/Makefile
@@ -705,7 +705,7 @@ ifeq ($(strip $(CONFIG_MAKEFILE_FOUND)),
@echo '$(ARBHOME)/$@:1: has been generated.'
@echo 'Please edit $@ to configure your system!'
@echo --------------------------------------------------------------------------------
- @false
+ # @false
else
@echo '$(ARBHOME)/$<:1: is more recent than'
@echo '$(ARBHOME)/$@:1:'
@@ -805,7 +805,7 @@ check_TOOLS:
"$(LINK_SHARED_LIB)" \
......@@ -83,20 +92,18 @@
# communication libs need aisc and aisc_mkpts:
@@ -1290,10 +1242,9 @@ WINDOW/WINDOW.dummy: target_is_missing_l
@@ -1291,9 +1243,8 @@ WINDOW/WINDOW.dummy: target_is_missing_l
# Additional dependencies for subtargets:
-PROBE_COM/PROBE_COM.dummy : comtools
PROBE_COM/PROBE_COM.dummy : comtools
-NAMES_COM/NAMES_COM.dummy : comtools
+PROBE_COM/PROBE_COM.dummy :
-com: PROBE_COM/PROBE_COM.dummy NAMES_COM/NAMES_COM.dummy
+com: PROBE_COM/PROBE_COM.dummy
PROBE_COM/server.dummy:
@echo Unwanted request to make target $<
@@ -1557,8 +1508,8 @@ clrdotdepends:
@@ -1557,14 +1508,12 @@ clrdotdepends:
comdepends: comtools clrdotdepends
@echo "$(SEP) Partially build com interface"
......@@ -107,16 +114,55 @@
depends: genheaders comdepends
@echo "$(SEP) Updating other dependencies"
@@ -1652,7 +1603,7 @@ tags: $(TAG_SOURCE_LISTS)
$(MAKE) $(subst NAMES_COM/server.depends,,$(subst PROBE_COM/server.depends,,$(ARCHS:.a=.depends))) \
- HELP_SOURCE/HELP_SOURCE.depends \
- SOURCE_TOOLS/COMPILE_COMPAT/COMPILE_COMPAT.depends \
$(MAKE) libdepends
@@ -1652,8 +1601,8 @@ tags: $(TAG_SOURCE_LISTS)
LINKSTAMP=SOURCE_TOOLS/stamp.generate_all_links
-links: checks $(LINKSTAMP) arbmainwrapper
-links_no_checks: $(LINKSTAMP) arbmainwrapper
+links: checks # $(LINKSTAMP) arbmainwrapper
links_no_checks: $(LINKSTAMP) arbmainwrapper
+links_no_checks: # $(LINKSTAMP) arbmainwrapper
forcelinks:
@@ -1847,7 +1798,7 @@ clean2: $(ARCHS:.a=.clean) \
-rm $(LINKSTAMP)
@@ -1810,7 +1759,7 @@ rmbak:
-exec rm -v {} \;
bin_reinit:
- $(MAKE) bin/bin.clean
+ #$(MAKE) bin/bin.clean
$(MAKE) -C "bin" all
clean_directories:
@@ -1826,19 +1775,14 @@ objclean:
# bin.clean and HELP_SOURCE.clean interfere
clean3:
- $(MAKE) bin/bin.clean
- $(MAKE) HELP_SOURCE/HELP_SOURCE.clean
+ # $(MAKE) bin/bin.clean
+ # $(MAKE) HELP_SOURCE/HELP_SOURCE.clean
clean2: $(ARCHS:.a=.clean) \
clean3 \
rmbak \
libclean \
objclean \
- lib/lib.clean \
- GDEHELP/GDEHELP.clean \
- HEADERLIBS/HEADERLIBS.clean \
- SOURCE_TOOLS/SOURCE_TOOLS.clean \
- SOURCE_TOOLS/COMPILE_COMPAT/COMPILE_COMPAT.clean \
UNIT_TESTER/UNIT_TESTER.clean \
TEMPLATES/TEMPLATES.clean \
perl_clean \
@@ -1847,7 +1791,7 @@ clean2: $(ARCHS:.a=.clean) \
rm -f *.last_gcc *.last_compiler config.makefile.bak
# links are needed for cleanup
......@@ -125,16 +171,45 @@
$(MAKE) clean2
$(MAKE) clean_cov_all clean_links
@@ -1981,7 +1932,7 @@ arb_external: convert tools gde readseq
@@ -1860,8 +1804,6 @@ reloc_clean: links
$(MAKE) \
perl_clean \
GDEHELP/GDEHELP.clean \
- HELP_SOURCE/genhelp/genhelp.clean \
- bin/bin.clean \
libclean \
objclean
@@ -1896,7 +1838,7 @@ perl4ever: clean
rebuild: clean
$(MAKE) all
-relink: bin/bin.clean libclean
+relink: libclean
$(MAKE) build
tarfile: rebuild
@@ -1979,9 +1921,9 @@ arbapplications: nt pa e4 wetc pt na nal
arb_external: convert tools gde readseq tg pst xmlin
arb_no_perl: arbapplications help arb_external
-arb_no_perl: arbapplications help arb_external
+arb_no_perl: arbapplications arb_external
-arb: motif_xpm_hack
+arb:
$(MAKE) "WITHPERL=1" perl arb_no_perl
motif_xpm_hack:
@@ -2312,7 +2263,7 @@ post_commit_check:
@@ -2121,7 +2063,6 @@ UNITS_TESTED = \
SL/FAST_ALIGNER/FAST_ALIGNER.test \
SL/PRONUC/PRONUC.test \
WINDOW/libWINDOW.test \
- HELP_SOURCE/arb_help2xml.test \
CONVERTALN/CONVERTALN.test \
SL/SEQIO/SEQIO.test \
SL/PTCLEAN/PTCLEAN.test \
@@ -2312,7 +2253,7 @@ post_commit_check:
# --------------------------------------------------------------------------------
build: arb
......@@ -143,3 +218,26 @@
all:
@echo "Build time" > $(TIMELOG)
--- a/PROBE_COM/Makefile
+++ b/PROBE_COM/Makefile
@@ -20,7 +20,11 @@ AISC_COMPILER=../AISC/aisc
AISC_PROTOTYPER=../AISC_MKPTPS/aisc_mkpt
AISC_DEPENDS = $(wildcard AISC/*.pa) $(AISC_COMPILER) $(AISC_PROTOTYPER)
+ifeq (,$(wildcard AISC/export2sub))
+include ../AISC_COM/AISC/export2sub
+else
include AISC/export2sub
+endif
$(MAIN): server.a
@@ -33,7 +37,7 @@ depends:
clean:
@rm -f .depends
- @$(MAKE) -r -f AISC/Makefile clean
+ #@$(MAKE) -r -f AISC/Makefile clean
# DO NOT DELETE
......@@ -171,3 +171,12 @@
preplib:
(cd lib;$(MAKE) all)
@@ -1933,7 +1933,7 @@ arb_external: convert tools gde readseq
arb_no_perl: arbapplications help arb_external
arb:
- $(MAKE) "WITHPERL=1" perl arb_no_perl
+ $(MAKE) "WITHPERL=1" perl # arb_no_perl
motif_xpm_hack:
$(MAKE) -r -C "lib/motifHack" all
......@@ -39,6 +39,7 @@ override_dh_auto_build:
ln -s TEMPLATES INCLUDE
for header in UNIT_TESTER/*.h ; do ln -s ../$${header} INCLUDE/`basename $${header}` ; done
ln -s /usr/include/valgrind/valgrind.h INCLUDE/valgrind.h
ln -s ../AISC_COM/AISC PROBE_COM/AISC
mkdir bin
mkdir lib
dh_auto_build -- all # --sourcedirectory=CORE
......@@ -48,9 +49,14 @@ override_dh_auto_configure: config.makefile
override_dh_auto_clean: config.makefile
# config.makefile is required to run make clean, hence the dependency
# on ..._auto_configure.
$(MAKE) clean || true
if [ ! -e config.makefile ] ; then echo "config.makefile missing" ; exit 1 ; fi
ln -s ../AISC_COM/AISC PROBE_COM/AISC
$(MAKE) clean
rm -rf bin
rm -rf lib
rm -rf NAMES_COM
rm -rf PROBE_COM/AISC
rm -rf INCLUDE
rm config.makefile
# ARB does not have "distclean" or "realclean". Remove some leftovers:
rm -f UNIT_TESTER/Makefile.setup.local
......