1
2 /**
3 * @file tpParse.c
4 *
5 * This module will load a template and return a template structure.
6 *
7 * @addtogroup autogen
8 * @{
9 */
10 /*
11 * This file is part of AutoGen.
12 * Copyright (C) 1992-2018 Bruce Korb - all rights reserved
13 *
14 * AutoGen is free software: you can redistribute it and/or modify it
15 * under the terms of the GNU General Public License as published by the
16 * Free Software Foundation, either version 3 of the License, or
17 * (at your option) any later version.
18 *
19 * AutoGen is distributed in the hope that it will be useful, but
20 * WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
22 * See the GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License along
25 * with this program. If not, see <http://www.gnu.org/licenses/>.
26 */
27
28 #if defined(DEBUG_ENABLED)
29 static char const zTUndef[] = "%-10s (%d) line %d - MARKER\n";
30
31 static int tpl_nest_lvl = 0;
32
33 static char const tpl_def_fmt[] = "%-10s (%d) line %d end=%d, strlen=%d\n";
34 #endif
35
36 /**
37 * Return the enumerated function type corresponding
38 * to a name pointed to by the input argument.
39 */
40 static mac_func_t
func_code(char const ** pscan)41 func_code(char const ** pscan)
42 {
43 fn_name_type_t const * pNT;
44 char const * pzFuncName = *pscan;
45 int hi, lo, av;
46 int cmp;
47
48 /*
49 * IF the name starts with a punctuation, then it is some sort of
50 * alias. Find the function in the alias portion of the table.
51 */
52 if (IS_PUNCTUATION_CHAR(*pzFuncName)) {
53 hi = FUNC_ALIAS_HIGH_INDEX;
54 lo = FUNC_ALIAS_LOW_INDEX;
55 do {
56 av = (hi + lo)/2;
57 pNT = fn_name_types + av;
58 cmp = (int)(*(pNT->pName)) - (int)(*pzFuncName);
59
60 /*
61 * For strings that start with a punctuation, we
62 * do not need to test for the end of token
63 * We will not strip off the marker and the load function
64 * will figure out what to do with the code.
65 */
66 if (cmp == 0)
67 return pNT->fType;
68 if (cmp > 0)
69 hi = av - 1;
70 else lo = av + 1;
71 } while (hi >= lo);
72 return FTYP_BOGUS;
73 }
74
75 if (! IS_VAR_FIRST_CHAR(*pzFuncName))
76 return FTYP_BOGUS;
77
78 hi = FUNC_NAMES_HIGH_INDEX;
79 lo = FUNC_NAMES_LOW_INDEX;
80
81 do {
82 av = (hi + lo)/2;
83 pNT = fn_name_types + av;
84 cmp = strneqvcmp(pNT->pName, pzFuncName, (int)pNT->cmpLen);
85 if (cmp == 0) {
86 /*
87 * Make sure we matched to the end of the token.
88 */
89 if (IS_VARIABLE_NAME_CHAR(pzFuncName[pNT->cmpLen]))
90 break;
91
92 /*
93 * Advance the scanner past the macro name.
94 * The name is encoded in the "fType".
95 */
96 *pscan = pzFuncName + pNT->cmpLen;
97 return pNT->fType;
98 }
99 if (cmp > 0)
100 hi = av - 1;
101 else lo = av + 1;
102 } while (hi >= lo);
103
104 /*
105 * Save the name for later lookup
106 */
107 cur_macro->md_name_off =
108 (size_t)(current_tpl->td_scan - current_tpl->td_text);
109 {
110 char * pzCopy = current_tpl->td_scan;
111 char * pe = SPN_VALUE_NAME_CHARS(pzFuncName);
112 size_t l = (size_t)(pe - pzFuncName);
113 memcpy(pzCopy, pzFuncName, l);
114 pzCopy += l;
115 pzFuncName += l;
116
117 /*
118 * Names are allowed to contain colons, but not end with them.
119 */
120 if (pzCopy[-1] == ':')
121 pzCopy--, pzFuncName--;
122
123 *(pzCopy++) = NUL;
124 *pscan = pzFuncName;
125 current_tpl->td_scan = pzCopy;
126 }
127
128 /*
129 * "Unknown" means we have to check again before we
130 * know whether to assign it to "FTYP_INVOKE" or "FTYP_COND".
131 * That depends on whether or not we find a named template
132 * at template instantiation time.
133 */
134 return FTYP_UNKNOWN;
135 }
136
137 static char const *
find_mac_end(char const ** ppzMark)138 find_mac_end(char const ** ppzMark)
139 {
140 char const * pzMark = *ppzMark + st_mac_len;
141 char const * pzFunc;
142 char const * pzNextMark;
143 char const * pzEndMark;
144
145 /*
146 * Set our pointers to the start of the macro text
147 */
148 for (;;) {
149 pzMark = SPN_NON_NL_WHITE_CHARS(pzMark);
150 if (*pzMark != NL)
151 break;
152 tpl_line++;
153 pzMark++;
154 }
155
156 pzFunc = pzMark;
157 cur_macro->md_code = func_code(&pzMark);
158 cur_macro->md_line = tpl_line;
159 *ppzMark = pzMark;
160
161 /*
162 * Find the end. (We must.) If the thing is empty, treat as a comment,
163 * but warn about it.
164 */
165 pzEndMark = strstr(pzMark, end_mac_mark);
166 if (pzEndMark == NULL)
167 AG_ABEND(FIND_MAC_END_NOPE);
168
169 if (pzEndMark == pzFunc) {
170 cur_macro->md_code = FTYP_COMMENT;
171 fprintf(trace_fp, FIND_MAC_END_EMPTY,
172 current_tpl->td_file, tpl_line);
173 return pzEndMark;
174 }
175
176 /*
177 * Back up over a preceding backslash. It is a flag to indicate the
178 * removal of the end of line white space.
179 */
180 if (pzEndMark[-1] == '\\')
181 pzEndMark--;
182
183 pzNextMark = strstr(pzMark, st_mac_mark);
184 if (pzNextMark == NULL)
185 return pzEndMark;
186
187 if (pzEndMark > pzNextMark)
188 AG_ABEND(FIND_MAC_END_NESTED);
189
190 return pzEndMark;
191 }
192
193 static char const *
find_mac_start(char const * pz,macro_t ** ppm,templ_t * tpl)194 find_mac_start(char const * pz, macro_t ** ppm, templ_t * tpl)
195 {
196 char * pzCopy;
197 char const * pzEnd;
198 char const * res = strstr(pz, st_mac_mark);
199 macro_t * mac = *ppm;
200
201 if (res == pz)
202 return res;
203
204 /*
205 * There is some text here. Make a text macro entry.
206 */
207 pzCopy = tpl->td_scan;
208 pzEnd = (res != NULL) ? res : pz + strlen(pz);
209 mac->md_txt_off = (uintptr_t)(pzCopy - tpl->td_text);
210 mac->md_code = FTYP_TEXT;
211 mac->md_line = tpl_line;
212
213 #if defined(DEBUG_ENABLED)
214 if (HAVE_OPT(SHOW_DEFS)) {
215 int ct = tpl_nest_lvl;
216 fprintf(trace_fp, "%3u ", (unsigned int)(mac - tpl->td_macros));
217 do { fputs(" ", trace_fp); } while (--ct > 0);
218
219 fprintf(trace_fp, tpl_def_fmt, ag_fun_names[ FTYP_TEXT ], FTYP_TEXT,
220 mac->md_line, mac->md_end_idx, (unsigned int)(pzEnd - pz));
221 }
222 #endif
223
224 do {
225 if ((*(pzCopy++) = *(pz++)) == NL)
226 tpl_line++;
227 } while (pz < pzEnd);
228
229 *(pzCopy++) = NUL;
230 *ppm = mac + 1;
231 tpl->td_scan = pzCopy;
232
233 return res; /* may be NULL, if there are no more macros */
234 }
235
236 static char const *
find_macro(templ_t * tpl,macro_t ** ppm,char const ** pscan)237 find_macro(templ_t * tpl, macro_t ** ppm, char const ** pscan)
238 {
239 char const * scan = *pscan;
240 char const * pzMark;
241
242 pzMark = find_mac_start(scan, ppm, tpl);
243
244 /*
245 * IF no more macro marks are found, THEN we are done...
246 */
247 if (pzMark == NULL)
248 return pzMark;
249
250 /*
251 * Find the macro code and the end of the macro invocation
252 */
253 cur_macro = *ppm;
254 scan = find_mac_end(&pzMark);
255
256 /*
257 * Count the lines in the macro text and advance the
258 * text pointer to after the marker.
259 */
260 {
261 char const * pzMacEnd = scan;
262 char const * pz = pzMark;
263
264 for (;;pz++) {
265 pz = strchr(pz, NL);
266 if ((pz == NULL) || (pz > pzMacEnd))
267 break;
268 tpl_line++;
269 }
270
271 /*
272 * Strip white space from the macro
273 */
274 pzMark = SPN_WHITESPACE_CHARS(pzMark);
275
276 if (pzMark != pzMacEnd) {
277 pzMacEnd = SPN_WHITESPACE_BACK( pzMark, pzMacEnd);
278 (*ppm)->md_txt_off = (uintptr_t)pzMark;
279 (*ppm)->md_res = (uintptr_t)(pzMacEnd - pzMark);
280 }
281 }
282
283 /*
284 * IF the end macro mark was preceded by a backslash, then we remove
285 * trailing white space from there to the end of the line.
286 */
287 if ((*scan != '\\') || (strncmp(end_mac_mark, scan, end_mac_len) == 0))
288 scan += end_mac_len;
289
290 else {
291 char const * pz;
292 scan += end_mac_len + 1;
293 pz = SPN_NON_NL_WHITE_CHARS(scan);
294 if (*pz == NL) {
295 scan = pz + 1;
296 tpl_line++;
297 }
298 }
299
300 *pscan = scan;
301 return pzMark;
302 }
303
304 #if defined(DEBUG_ENABLED)
305 static void
print_indentation(templ_t * tpl,macro_t * mac,int idx)306 print_indentation(templ_t * tpl, macro_t * mac, int idx)
307 {
308 static char const fmt_fmt[] = "%%%us";
309 char fmt[16];
310
311 if (idx < 0)
312 fputs(" ", trace_fp);
313 else fprintf(trace_fp, "%3u ", (unsigned int)idx);
314 snprintf(fmt, sizeof(fmt), fmt_fmt, tpl_nest_lvl);
315 fprintf(trace_fp, fmt, "");
316 (void)tpl;
317 (void)mac;
318 }
319
320 static void
print_ag_defs(templ_t * tpl,macro_t * mac)321 print_ag_defs(templ_t * tpl, macro_t * mac)
322 {
323 mac_func_t ft = mac->md_code;
324 int ln = mac->md_line;
325 int idx = (mac->md_code == FTYP_BOGUS) ? -1 : (int)(mac - tpl->td_macros);
326
327 print_indentation(tpl, mac, idx);
328
329 if (mac->md_code == FTYP_BOGUS)
330 fprintf(trace_fp, zTUndef, ag_fun_names[ ft ], ft, ln);
331 else {
332 char const * pz;
333 if (ft >= FUNC_CT)
334 ft = FTYP_SELECT;
335 pz = (mac->md_txt_off == 0)
336 ? zNil
337 : (tpl->td_text + mac->md_txt_off);
338 fprintf(trace_fp, tpl_def_fmt, ag_fun_names[ft], mac->md_code,
339 ln, mac->md_end_idx, (unsigned int)strlen(pz));
340 }
341 }
342 #endif
343
344 /**
345 * Parse the template.
346 * @param[out] mac array of macro descriptors to fill in
347 * @param[in,out] p_scan pointer to string scanning address
348 */
349 static macro_t *
parse_tpl(macro_t * mac,char const ** p_scan)350 parse_tpl(macro_t * mac, char const ** p_scan)
351 {
352 char const * scan = *p_scan;
353 templ_t * tpl = current_tpl;
354
355 #if defined(DEBUG_ENABLED)
356
357 #define DEBUG_DEC(l) l--
358
359 if ( ((tpl_nest_lvl++) > 0)
360 && HAVE_OPT(SHOW_DEFS)) {
361 int idx = (int)(mac - tpl->td_macros);
362 macro_t * m = mac - 1;
363
364 print_indentation(tpl, m, idx);
365
366 fprintf(trace_fp, zTUndef, ag_fun_names[m->md_code],
367 m->md_code, m->md_line);
368 }
369 #else
370 #define DEBUG_DEC(l)
371 #endif
372
373 while (find_macro(tpl, &mac, &scan) != NULL) {
374 /*
375 * IF the called function returns a NULL next macro pointer,
376 * THEN some block has completed. The returned scanning pointer
377 * will be non-NULL.
378 */
379 load_proc_p_t const fn = load_proc_table[mac->md_code];
380 macro_t * nxt_mac = fn(tpl, mac, &scan);
381
382 #if defined(DEBUG_ENABLED)
383 if (HAVE_OPT(SHOW_DEFS))
384 print_ag_defs(tpl, mac);
385 #endif
386
387 if (nxt_mac == NULL) {
388 *p_scan = scan;
389 DEBUG_DEC(tpl_nest_lvl);
390 return mac;
391 }
392 mac = nxt_mac;
393 }
394
395 DEBUG_DEC(tpl_nest_lvl);
396
397 /*
398 * We reached the end of the input string.
399 * Return a NULL scanning pointer and a pointer to the end.
400 */
401 *p_scan = NULL;
402 return mac;
403 }
404 /**
405 * @}
406 *
407 * Local Variables:
408 * mode: C
409 * c-file-style: "stroustrup"
410 * indent-tabs-mode: nil
411 * End:
412 * end of agen5/tpParse.c */
413