| 1 | // ================================================================ |
|---|
| 2 | /* */ |
|---|
| 3 | // File : aisc_var_ref.c |
|---|
| 4 | // Purpose : |
|---|
| 5 | /* */ |
|---|
| 6 | // Institute of Microbiology (Technical University Munich) |
|---|
| 7 | // http://www.arb-home.de/ |
|---|
| 8 | /* */ |
|---|
| 9 | // ================================================================ |
|---|
| 10 | |
|---|
| 11 | #include "aisc_token.h" |
|---|
| 12 | #include "aisc_inline.h" |
|---|
| 13 | #include "aisc_interpreter.h" |
|---|
| 14 | |
|---|
| 15 | |
|---|
| 16 | class TokenMatcher : virtual Noncopyable { |
|---|
| 17 | char *key; // NULp matches "any key" |
|---|
| 18 | char *value; // NULp matches "any value" |
|---|
| 19 | |
|---|
| 20 | char *copy_expression_part(const char *start, const char *end) { |
|---|
| 21 | if (start >= end || (end == (start+1) && start[0] == '*')) return NULp; |
|---|
| 22 | return copy_string_part(start, end); |
|---|
| 23 | } |
|---|
| 24 | |
|---|
| 25 | public: |
|---|
| 26 | TokenMatcher(const char *search_expr) { |
|---|
| 27 | // search_expr is "key.value" |
|---|
| 28 | // |
|---|
| 29 | // "*.value" searches value (regardless of key) |
|---|
| 30 | // "key.*" searches key (regardless of value) |
|---|
| 31 | // |
|---|
| 32 | // "key" = "key." = "key.*" |
|---|
| 33 | // ".value" = "*.value" |
|---|
| 34 | |
|---|
| 35 | const char *dot = strchr(search_expr, '.'); |
|---|
| 36 | if (!dot) { |
|---|
| 37 | key = strdup(search_expr); |
|---|
| 38 | value = NULp; |
|---|
| 39 | } |
|---|
| 40 | else { |
|---|
| 41 | const char *end = strchr(dot+1, 0); |
|---|
| 42 | |
|---|
| 43 | key = copy_expression_part(search_expr, dot-1); |
|---|
| 44 | value = copy_expression_part(dot+1, end-1); |
|---|
| 45 | } |
|---|
| 46 | } |
|---|
| 47 | |
|---|
| 48 | ~TokenMatcher() { |
|---|
| 49 | free(key); |
|---|
| 50 | free(value); |
|---|
| 51 | } |
|---|
| 52 | |
|---|
| 53 | bool matchesKeyOf(const Token *token) const { |
|---|
| 54 | return !key || strcmp(token->get_key(), key) == 0; |
|---|
| 55 | } |
|---|
| 56 | bool matchesValueOf(const Token *token) const { |
|---|
| 57 | return !value || (!token->is_block() && strcmp(value, token->get_value()) == 0); |
|---|
| 58 | } |
|---|
| 59 | bool matches(const Token *token) const { |
|---|
| 60 | return matchesKeyOf(token) && matchesValueOf(token); |
|---|
| 61 | } |
|---|
| 62 | }; |
|---|
| 63 | |
|---|
| 64 | static const Token *nextToken(const Token *tok, bool cont_in_next_list) { |
|---|
| 65 | const Token *next_token = tok->next_token(); |
|---|
| 66 | if (!next_token && cont_in_next_list) { |
|---|
| 67 | const TokenList *next_list = tok->parent_list()->next_list(); |
|---|
| 68 | if (next_list) next_token = next_list->first_token(); |
|---|
| 69 | } |
|---|
| 70 | return next_token; |
|---|
| 71 | } |
|---|
| 72 | |
|---|
| 73 | static const Token *nextTokenMatching(const Token *tok, const TokenMatcher& wanted, bool cont_in_next_list) { |
|---|
| 74 | while (tok) { |
|---|
| 75 | if (wanted.matches(tok)) break; |
|---|
| 76 | tok = nextToken(tok, cont_in_next_list); |
|---|
| 77 | } |
|---|
| 78 | return tok; |
|---|
| 79 | } |
|---|
| 80 | |
|---|
| 81 | const Token *Data::find_token(const Token *curs, const char *str, LookupScope scope) const { |
|---|
| 82 | if (!curs) { |
|---|
| 83 | const TokenListBlock *tokens = get_tokens(); |
|---|
| 84 | if (!tokens) return NULp; |
|---|
| 85 | curs = tokens->first_token(); |
|---|
| 86 | } |
|---|
| 87 | |
|---|
| 88 | TokenMatcher wanted(str); |
|---|
| 89 | const Token *found = NULp; |
|---|
| 90 | |
|---|
| 91 | while (curs && !found) { |
|---|
| 92 | bool cont_in_next_list = false; |
|---|
| 93 | switch (scope) { |
|---|
| 94 | case LOOKUP_LIST_OR_PARENT_LIST: |
|---|
| 95 | case LOOKUP_LIST: |
|---|
| 96 | curs = curs->parent_list()->first_token(); |
|---|
| 97 | break; |
|---|
| 98 | case LOOKUP_BLOCK: |
|---|
| 99 | curs = curs->parent_list()->parent_block()->first_token(); |
|---|
| 100 | cont_in_next_list = true; |
|---|
| 101 | break; |
|---|
| 102 | case LOOKUP_BLOCK_REST: |
|---|
| 103 | curs = nextToken(curs, true); |
|---|
| 104 | cont_in_next_list = true; |
|---|
| 105 | break; |
|---|
| 106 | } |
|---|
| 107 | |
|---|
| 108 | found = nextTokenMatching(curs, wanted, cont_in_next_list); |
|---|
| 109 | |
|---|
| 110 | if (scope == LOOKUP_LIST_OR_PARENT_LIST) { |
|---|
| 111 | curs = curs->parent_block_token(); |
|---|
| 112 | } |
|---|
| 113 | else { |
|---|
| 114 | curs = NULp; |
|---|
| 115 | } |
|---|
| 116 | } |
|---|
| 117 | |
|---|
| 118 | return found; |
|---|
| 119 | } |
|---|
| 120 | |
|---|
| 121 | const Token *Data::find_qualified_token(const char *str, LookupScope scope) const { |
|---|
| 122 | const Token *at = cursor; |
|---|
| 123 | if (*str == '/') { |
|---|
| 124 | at = NULp; |
|---|
| 125 | str++; |
|---|
| 126 | } |
|---|
| 127 | |
|---|
| 128 | const char *slash = strchr(str, '/'); |
|---|
| 129 | while (slash) { |
|---|
| 130 | char *name = copy_string_part(str, slash-1); |
|---|
| 131 | const Token *found = find_token(at, name, scope); |
|---|
| 132 | free(name); |
|---|
| 133 | |
|---|
| 134 | if (!found || !found->is_block()) return NULp; |
|---|
| 135 | |
|---|
| 136 | at = found->get_content()->first_token(); |
|---|
| 137 | |
|---|
| 138 | str = slash+1; |
|---|
| 139 | slash = strchr(str, '/'); |
|---|
| 140 | scope = LOOKUP_BLOCK; |
|---|
| 141 | } |
|---|
| 142 | |
|---|
| 143 | return find_token(at, str, scope); |
|---|
| 144 | } |
|---|
| 145 | |
|---|
| 146 | |
|---|
| 147 | char *get_var_string(const Data& data, char *var, bool allow_missing_var) { |
|---|
| 148 | // Calculates result for an expression like '$(IDENT:fdg|"sdfg")' |
|---|
| 149 | SKIP_SPACE_LF(var); |
|---|
| 150 | int use_path = 0; |
|---|
| 151 | while (var[0] == '&') { |
|---|
| 152 | use_path++; |
|---|
| 153 | var++; |
|---|
| 154 | SKIP_SPACE_LF(var); |
|---|
| 155 | } |
|---|
| 156 | |
|---|
| 157 | char *doppelpunkt = strchr(var, ':'); if (doppelpunkt) *(doppelpunkt++) = 0; |
|---|
| 158 | char *bar = strchr(var, '|'); if (bar) *(bar++) = 0; |
|---|
| 159 | |
|---|
| 160 | const char *val = Interpreter::instance->read_local(var); |
|---|
| 161 | char *in = NULp; |
|---|
| 162 | if (val) { |
|---|
| 163 | in = strdup(val); |
|---|
| 164 | } |
|---|
| 165 | else { |
|---|
| 166 | const Token *cur = data.find_qualified_token(var, LOOKUP_LIST_OR_PARENT_LIST); |
|---|
| 167 | if (!cur) { |
|---|
| 168 | if (!bar) { |
|---|
| 169 | if (!allow_missing_var) { |
|---|
| 170 | const Code *at = Interpreter::instance->at(); |
|---|
| 171 | printf_error(at, "Ident '%s' not found", var); |
|---|
| 172 | } |
|---|
| 173 | return NULp; |
|---|
| 174 | } |
|---|
| 175 | return strdup(bar); |
|---|
| 176 | } |
|---|
| 177 | if (use_path) { |
|---|
| 178 | in = strdup(cur->get_key()); |
|---|
| 179 | if (use_path>1) { |
|---|
| 180 | while (1) { |
|---|
| 181 | const Token *up = cur->parent_block_token(); |
|---|
| 182 | if (!up) break; |
|---|
| 183 | |
|---|
| 184 | cur = up; |
|---|
| 185 | int len = strlen(in) + strlen(cur->get_key()); |
|---|
| 186 | char *buf1 = (char *) calloc(sizeof(char), len + 2); |
|---|
| 187 | sprintf(buf1, "%s/%s", cur->get_key(), in); |
|---|
| 188 | |
|---|
| 189 | free(in); |
|---|
| 190 | in = buf1; |
|---|
| 191 | } |
|---|
| 192 | |
|---|
| 193 | } |
|---|
| 194 | } |
|---|
| 195 | else { |
|---|
| 196 | if (cur->is_block()) { |
|---|
| 197 | printf_error(Interpreter::instance->at(), "Ident '%s' is a hierarchical type", var); |
|---|
| 198 | return NULp; |
|---|
| 199 | } |
|---|
| 200 | else { |
|---|
| 201 | in = strdup(cur->has_value() ? cur->get_value() : ""); |
|---|
| 202 | } |
|---|
| 203 | } |
|---|
| 204 | } |
|---|
| 205 | |
|---|
| 206 | aisc_assert(in); // 'in' has to point to a heap copy |
|---|
| 207 | |
|---|
| 208 | if (doppelpunkt) { |
|---|
| 209 | int len = strlen(in); |
|---|
| 210 | bool err = false; |
|---|
| 211 | while (doppelpunkt && !err) { |
|---|
| 212 | char *nextdp = strchr(doppelpunkt, ':'); |
|---|
| 213 | if (nextdp) *(nextdp++) = 0; |
|---|
| 214 | if (!doppelpunkt[0]) { |
|---|
| 215 | print_error(Interpreter::instance->at(), "Ident replacement is missing an ':'"); |
|---|
| 216 | err = true; |
|---|
| 217 | } |
|---|
| 218 | else { |
|---|
| 219 | bar = strchr(doppelpunkt+1, '='); |
|---|
| 220 | if (!bar) { |
|---|
| 221 | print_error(Interpreter::instance->at(), "Ident replacement is missing an '='"); |
|---|
| 222 | err = true; |
|---|
| 223 | } |
|---|
| 224 | else { |
|---|
| 225 | *(bar++) = 0; |
|---|
| 226 | int findl = strlen(doppelpunkt); |
|---|
| 227 | int replacel = strlen(bar); |
|---|
| 228 | |
|---|
| 229 | char *buf2; |
|---|
| 230 | for (char *finds = strstr(in, doppelpunkt); finds; finds = strstr(buf2, doppelpunkt)) { |
|---|
| 231 | len += replacel - findl; |
|---|
| 232 | |
|---|
| 233 | char *buf1 = (char *) calloc(sizeof(char), len + 1); |
|---|
| 234 | int offset = finds - in; |
|---|
| 235 | |
|---|
| 236 | memcpy(buf1, in, offset); |
|---|
| 237 | memcpy(buf1 + offset, bar, replacel); |
|---|
| 238 | |
|---|
| 239 | buf2 = buf1 + offset + replacel; |
|---|
| 240 | memcpy(buf2, in + offset + findl, len - replacel - offset); |
|---|
| 241 | |
|---|
| 242 | free(in); |
|---|
| 243 | in = buf1; |
|---|
| 244 | |
|---|
| 245 | buf2 = in + offset + replacel; |
|---|
| 246 | } |
|---|
| 247 | doppelpunkt = nextdp; |
|---|
| 248 | } |
|---|
| 249 | } |
|---|
| 250 | } |
|---|
| 251 | if (err) { |
|---|
| 252 | free(in); |
|---|
| 253 | in = NULp; |
|---|
| 254 | } |
|---|
| 255 | } |
|---|
| 256 | return in; |
|---|
| 257 | } |
|---|
| 258 | |
|---|
| 259 | static void dump_token_recursive(const Token *tok, FILE *out) { |
|---|
| 260 | const Token *block = tok->parent_block_token(); |
|---|
| 261 | if (block) { |
|---|
| 262 | const TokenList *parent = block->parent_list(); |
|---|
| 263 | if (parent) { |
|---|
| 264 | const Token *first = parent->first_token(); |
|---|
| 265 | if (first) { |
|---|
| 266 | dump_token_recursive(first, out); |
|---|
| 267 | fputc('/', out); |
|---|
| 268 | } |
|---|
| 269 | } |
|---|
| 270 | } |
|---|
| 271 | |
|---|
| 272 | fputc('@', out); |
|---|
| 273 | fputs(tok->get_key(), out); |
|---|
| 274 | |
|---|
| 275 | if (tok->has_value()) { |
|---|
| 276 | fputc('=', out); |
|---|
| 277 | fputs(tok->get_value(), out); |
|---|
| 278 | } |
|---|
| 279 | } |
|---|
| 280 | |
|---|
| 281 | void Data::dump_cursor_pos(FILE *out) const { |
|---|
| 282 | dump_token_recursive(cursor, out); |
|---|
| 283 | } |
|---|