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 | } |
---|