Root/b2/subst.c

1/*
2 * subst.c - Substitution rules
3 *
4 * Copyright 2012 by Werner Almesberger
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 */
11
12
13#include <stdlib.h>
14#include <stdio.h>
15#include <string.h>
16#include <ctype.h>
17#include <sys/types.h>
18#include <regex.h>
19#include <assert.h>
20
21#include "util.h"
22#include "vstring.h"
23#include "lang.h"
24#include "relop.h"
25#include "subst.h"
26
27
28const char *fn;
29
30
31/* ----- Rule set construction --------------------------------------------- */
32
33
34static struct subst *alloc_subst(enum subst_type type)
35{
36    struct subst *sub;
37
38    sub = alloc_type(struct subst);
39    sub->type = type;
40    sub->next = NULL;
41    return sub;
42}
43
44
45/*
46 * With M the SI multiplier prefixes and U the unit character, our regexp
47 * is
48 *
49 * (-?[0-9]+\.?[[0-9]*M?U?|-?[0-9]+[UM][0-9]*)
50 *
51 * The first part is for things like 10, 1.2k, 3.3V, -2mA, etc.
52 * The second part is for things like 1k20, 1R2, etc.
53 */
54
55static void unit_expr(char **res, int *res_len, char unit)
56{
57    append(res, res_len, "(-?[0-9]+\\.?[0-9]*[" MULT_CHARS "]?");
58    if (unit != '#')
59        append_char(res, res_len, unit);
60    append(res, res_len, "?|-?[0-9]+[");
61    if (unit != '#')
62        append_char(res, res_len, unit);
63    append(res, res_len, MULT_CHARS "][0-9]*)");
64}
65
66
67static char *prepare_re(const char *re, char *units)
68{
69    char *res = NULL;
70    int res_len = 0;
71    int parens = 0;
72
73    memset(units, 0, 10);
74    append_char(&res, &res_len, '^');
75    while (*re) {
76        switch (*re) {
77        case '.':
78            append_n(&res, &res_len, "\\.", 2);
79            break;
80        case '*':
81            append_n(&res, &res_len, ".*", 2);
82            break;
83        case '?':
84            append_char(&res, &res_len, '.');
85            break;
86        case '\\':
87            if (!re[1])
88                yyerrorf("regexp ends with backslash");
89            append_n(&res, &res_len, re, 2);
90            re++;
91            break;
92        case '(':
93            parens++;
94            if (re[1] == '#' && re[2]) {
95                if ((!isalpha(re[2]) &&
96                    re[2] != '%' && re[2] != '#') ||
97                    re[3] != ')')
98                    yyerrorf("invalid (#unit) syntax");
99                units[parens-1] = re[2];
100                unit_expr(&res, &res_len, re[2]);
101                re += 3;
102                break;
103            }
104            /* fall through */
105        default:
106            append_char(&res, &res_len, *re);
107        }
108        re++;
109    }
110    append(&res, &res_len, re);
111    append_char(&res, &res_len, '$');
112    return res;
113}
114
115
116struct subst *subst_match(const char *src, const char *re, char **res)
117{
118    char error[1000];
119    struct subst *sub;
120    char *tmp;
121    int err;
122
123    sub = alloc_subst(st_match);
124    sub->u.match.src = src;
125    tmp = prepare_re(re, sub->u.match.units);
126    err = regcomp(&sub->u.match.re, tmp, REG_EXTENDED);
127    if (res)
128        *res = tmp;
129    else
130        free(tmp);
131    if (err) {
132        regerror(err, &sub->u.match.re, error, sizeof(error));
133        yyerrorf("%s", error);
134    }
135    return sub;
136}
137
138
139static void end_chunk(struct chunk ***last, const char *start, const char *s)
140{
141    struct chunk *c;
142
143    if (s == start)
144        return;
145
146    c = alloc_type(struct chunk);
147    c->type = ct_string;
148    c->u.s = stralloc_n(start, s-start);;
149    c->next = NULL;
150    **last = c;
151    *last = &c->next;
152}
153
154
155static const char *parse_var(struct chunk *c, const char *s)
156{
157    const char *t;
158    int braced;
159
160    if (!*s)
161        yyerror("trailing dollar sign");
162
163    braced = *s == '{';
164    if (braced)
165        s++;
166
167    t = s;
168    while (*t) {
169        if (braced && *t == '}')
170            break;
171        if (s == t && *t == '$') {
172            t++;
173            break;
174        }
175        if (!isalnum(*t))
176            break;
177        t++;
178    }
179    if (s == t)
180        yyerror("invalid variable name");
181    if (braced && !*t)
182        yyerror("unterminated variable name");
183    if (isdigit(*s)) {
184        if (t != s+1 || *s == '0')
185            yyerror("invalid variable name");
186        c->type = ct_sub;
187        c->u.sub = *s-'0';
188    } else if (isalnum(*s)) {
189        c->type = ct_var;
190        c->u.var = unique_n(s, t-s);
191    } else {
192        c->type = ct_sub;
193        c->u.sub = 0;
194    }
195
196    if (braced) {
197        if (*t != '}')
198            yyerror("invalid variable name");
199        t++;
200    }
201    return t;
202}
203
204
205static struct chunk *parse_pattern(const char *s)
206{
207    struct chunk *res = NULL, **last = &res;
208    struct chunk *c;
209    const char *start = s;
210
211    while (*s) {
212        if (*s == '\\') {
213            if (!s[1])
214                yyerror("trailing backslash");
215            end_chunk(&last, start, s);
216            start = s+1;
217            s += 2;
218            continue;
219        }
220        if (*s != '$') {
221            s++;
222            continue;
223        }
224
225        end_chunk(&last, start, s);
226        c = alloc_type(struct chunk);
227        c->next = NULL;
228        *last = c;
229        last = &c->next;
230        start = s = parse_var(c, s+1);
231    }
232    end_chunk(&last, start, s);
233    return res;
234}
235
236
237struct subst *subst_assign(const char *dst, enum relop op, const char *pat)
238{
239    struct subst *sub;
240
241    if (dst == fn)
242        yyerror("can't assign to pseudo-variable FN");
243    sub = alloc_subst(st_assign);
244    sub->u.assign.dst = dst;
245    sub->u.assign.op = op;
246    sub->u.assign.pat = parse_pattern(pat);
247    return sub;
248}
249
250
251struct subst *subst_print(const char *var)
252{
253    struct subst *sub;
254
255    sub = alloc_subst(st_print);
256    sub->u.print = var;
257    return sub;
258}
259
260
261struct subst *subst_end(void)
262{
263    return alloc_subst(st_end);
264}
265
266
267struct subst *subst_ignore(void)
268{
269    return alloc_subst(st_ignore);
270}
271
272
273struct subst *subst_break(const char *block)
274{
275    struct subst *sub;
276
277    sub = alloc_subst(st_break);
278    sub->u.tmp = block;
279    return sub;
280}
281
282
283struct subst *subst_continue(const char *block)
284{
285    struct subst *sub;
286
287    sub = alloc_subst(st_continue);
288    sub->u.tmp = block;
289    return sub;
290}
291
292
293/* ----- Jump resolution --------------------------------------------------- */
294
295
296struct parent {
297    const struct subst *sub;
298    const struct parent *parent;
299};
300
301
302static const struct subst *resolve_jump(const char *name,
303    const struct parent *parent)
304{
305    if (!name)
306        return parent->sub;
307    while (parent) {
308        assert(parent->sub->type == st_match);
309        if (name == parent->sub->u.match.src)
310            return parent->sub;
311        parent = parent->parent;
312    }
313    yyerrorf("cannot find \"%s\"", name);
314}
315
316
317static int find_var_use(const char *var, const struct subst *sub)
318{
319    while (sub) {
320        switch (sub->type) {
321        case st_match:
322            if (sub->u.match.src == var && var != fn)
323                return 1;
324            break;
325        case st_assign:
326            if (sub->u.assign.dst == var)
327                return 1;
328            break;
329        default:
330            break;
331        }
332        sub = sub->prev;
333    }
334    return 0;
335}
336
337
338static void check_chunks(const struct chunk *c, const struct parent *parent,
339    const struct subst *prev)
340{
341    int parens;
342
343    while (c) {
344        switch (c->type) {
345        case ct_sub:
346            if (!parent)
347                yyerrorf("$%c without match",
348                    c->u.sub ? c->u.sub+'0' : '$');
349            parens = parent->sub->u.match.re.re_nsub;
350            if (c->u.sub > parens)
351                yyerrorf("$%d but only %d parenthes%s",
352                    c->u.sub, parens,
353                    parens == 1 ? "is" : "es");
354            break;
355        case ct_var:
356            if (!find_var_use(c->u.var, prev))
357                yyerrorf("$%s may be undefined", c->u.var);
358            break;
359        default:
360            break;
361        }
362        c = c->next;
363    }
364}
365
366
367static void recurse_fin(struct subst *sub, const struct parent *parent)
368{
369    struct parent next = {
370        .parent = parent,
371    };
372    const struct subst *prev;
373
374    prev = parent ? parent->sub : NULL;
375    while (sub) {
376        sub->prev = prev;
377        switch (sub->type) {
378        case st_match:
379            if (!parent && sub->u.match.src == dollar)
380                yyerror("$ without match");
381            next.sub = sub;
382            recurse_fin(sub->u.match.block, &next);
383            break;
384        case st_assign:
385            if (!parent && sub->u.assign.dst == dollar)
386                yyerror("$ without match");
387            check_chunks(sub->u.assign.pat, parent, prev);
388            break;
389        case st_print:
390            break;
391        case st_end:
392            break;
393        case st_ignore:
394            break;
395        case st_break:
396            /* fall through */
397        case st_continue:
398            if (!parent)
399                yyerror("jump without block");
400            sub->u.jump = resolve_jump(sub->u.tmp, parent);
401            break;
402        default:
403            abort();
404        }
405        prev = sub;
406        sub = sub->next;
407    }
408}
409
410
411void subst_finalize(struct subst *sub)
412{
413    recurse_fin(sub, NULL);
414}
415
416
417/* ----- Dumping ----------------------------------------------------------- */
418
419
420#define INDENT 4
421
422
423static void dump_chunks(FILE *file, const struct chunk *c)
424{
425    while (c) {
426        switch (c->type) {
427        case ct_string:
428            fprintf(file, "%s", c->u.s);
429            break;
430        case ct_var:
431            fprintf(file, "${%s}", c->u.var);
432            break;
433        case ct_sub:
434            if (c->u.sub)
435                fprintf(file, "$%d", c->u.sub);
436            else
437                fprintf(file, "$$");
438            break;
439        default:
440            abort();
441        }
442        c = c->next;
443    }
444}
445
446
447static void recurse_dump(FILE *file, const struct subst *sub, int level)
448{
449    while (sub) {
450        fprintf(file, "%*s", INDENT*level, "");
451        switch (sub->type) {
452        case st_match:
453            fprintf(file, "%s=RE {\n", sub->u.match.src);
454            recurse_dump(file, sub->u.match.block, level+1);
455            fprintf(file, "%*s}\n", INDENT*level, "");
456            break;
457        case st_assign:
458            fprintf(file, "%s", sub->u.assign.dst);
459            dump_relop(file, sub->u.assign.op);
460            dump_chunks(file, sub->u.assign.pat);
461            fprintf(file, "\n");
462            break;
463        case st_print:
464            fprintf(file, "print %s\n", sub->u.print);
465            break;
466        case st_end:
467            fprintf(file, "end\n");
468            break;
469        case st_ignore:
470            fprintf(file, "ignore\n");
471            break;
472        case st_break:
473            fprintf(file, "break %s\n", sub->u.jump->u.match.src);
474            break;
475        case st_continue:
476            fprintf(file, "continue %s\n",
477                sub->u.jump->u.match.src);
478            break;
479        default:
480            abort();
481        }
482        sub = sub->next;
483    }
484}
485
486
487void subst_dump(FILE *file, const struct subst *sub)
488{
489    recurse_dump(file, sub, 0);
490}
491
492
493void subst_init(void)
494{
495    fn = unique("FN");
496}
497

Archive Download this file

Branches:
master



interactive