b2/subex.c |
| 1 | /* |
| 2 | * subex.c - Substitution (execution) |
| 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 <sys/types.h> |
| 17 | #include <regex.h> |
| 18 | |
| 19 | #include "util.h" |
| 20 | #include "lang.h" |
| 21 | #include "subst.h" |
| 22 | #include "subex.h" |
| 23 | |
| 24 | |
| 25 | #define FIELDS 10 |
| 26 | |
| 27 | |
| 28 | static const char *fn = NULL, *f[FIELDS]; |
| 29 | |
| 30 | /* Jump target that can never be reached. */ |
| 31 | static struct subst jump_end; |
| 32 | |
| 33 | |
| 34 | static void append_n(char **res, int *res_len, const char *s, int len) |
| 35 | { |
| 36 | if (!len) |
| 37 | return; |
| 38 | *res = realloc(*res, *res_len+len+1); |
| 39 | if (!*res) |
| 40 | abort(); |
| 41 | memcpy(*res+*res_len, s, len); |
| 42 | *res_len += len; |
| 43 | (*res)[*res_len] = 0; |
| 44 | } |
| 45 | |
| 46 | |
| 47 | static void append(char **res, int *res_len, const char *s) |
| 48 | { |
| 49 | append_n(res, res_len, s, strlen(s)); |
| 50 | } |
| 51 | |
| 52 | |
| 53 | #if 0 |
| 54 | static void append_char(char **res, int *res_len, char c) |
| 55 | { |
| 56 | append_n(res, res_len, &c, 1); |
| 57 | } |
| 58 | #endif |
| 59 | |
| 60 | |
| 61 | /* |
| 62 | * TODO: decide what to do with units |
| 63 | */ |
| 64 | |
| 65 | static char *compose(const struct chunk *c, |
| 66 | const struct var *in, const struct var *out, |
| 67 | const char *s, const regmatch_t *match) |
| 68 | { |
| 69 | char *res = stralloc(""); |
| 70 | int res_len = 0; |
| 71 | const char *val; |
| 72 | int n; |
| 73 | |
| 74 | while (c) { |
| 75 | switch (c->type) { |
| 76 | case ct_string: |
| 77 | append(&res, &res_len, c->u.s); |
| 78 | break; |
| 79 | case ct_var: |
| 80 | val = var_lookup(in, c->u.var); |
| 81 | if (!val) |
| 82 | val = var_lookup(out, c->u.var); |
| 83 | if (!val) |
| 84 | yyerrorf("unknown variable \"%s\"", c->u.var); |
| 85 | append(&res, &res_len, val); |
| 86 | break; |
| 87 | case ct_sub: |
| 88 | n = c->u.sub; |
| 89 | if (!s) |
| 90 | yyerrorf("$%c without prior match", |
| 91 | n ? n+'0' : '$'); |
| 92 | if (!n) { |
| 93 | append(&res, &res_len, s); |
| 94 | break; |
| 95 | } |
| 96 | if (match[n-1].rm_so == -1) |
| 97 | yyerrorf("substring $%d out of range", n); |
| 98 | #if 0 |
| 99 | len = match[n-1].rm_eo-match[n-1].rm_so); |
| 100 | tmp = alloc_size(len); |
| 101 | memcpy(tmp, s+match[n-1].rm_so, len); |
| 102 | tmp[len] = 0; |
| 103 | tmp2 = canonicalize(tmp, c->unit); |
| 104 | append(&res, &res_len, tmp2); |
| 105 | #endif |
| 106 | append_n(&res, &res_len, s+match[n-1].rm_so, |
| 107 | match[n-1].rm_eo-match[n-1].rm_so); |
| 108 | break; |
| 109 | default: |
| 110 | abort(); |
| 111 | } |
| 112 | c = c->next; |
| 113 | } |
| 114 | return res; |
| 115 | } |
| 116 | |
| 117 | |
| 118 | struct var *make_var(const char *name, const char *val) |
| 119 | { |
| 120 | struct var *var; |
| 121 | |
| 122 | var = alloc_type(struct var); |
| 123 | var->name = unique(name); |
| 124 | var->value = unique(val); |
| 125 | var->next = NULL; |
| 126 | return var; |
| 127 | } |
| 128 | |
| 129 | |
| 130 | static void do_assign(const char *name, struct var **out, const char *val) |
| 131 | { |
| 132 | while (*out) { |
| 133 | if ((*out)->name == name) |
| 134 | break; |
| 135 | out = &(*out)->next; |
| 136 | } |
| 137 | if (*out) { |
| 138 | free((void *) (*out)->value); |
| 139 | (*out)->value = val; |
| 140 | } else { |
| 141 | *out = make_var(name, val); |
| 142 | } |
| 143 | } |
| 144 | |
| 145 | |
| 146 | static int do_match_1(const char *var, const regex_t *re, |
| 147 | const struct var *in, const struct var *out, |
| 148 | const char **val, regmatch_t *match) |
| 149 | { |
| 150 | *val = var_lookup(out, var); |
| 151 | if (!*val) |
| 152 | *val = var_lookup(in, var); |
| 153 | if (!*val) |
| 154 | return -1; |
| 155 | return regexec(re, *val, 10, match, 0); |
| 156 | } |
| 157 | |
| 158 | |
| 159 | static int do_match(const char *var, const regex_t *re, |
| 160 | const struct var *in, const struct var *out, |
| 161 | const char **val, regmatch_t *match) |
| 162 | { |
| 163 | int i; |
| 164 | |
| 165 | if (var != fn) |
| 166 | return do_match_1(var, re, in, out, val, match); |
| 167 | for (i = 0; i != FIELDS; i++) |
| 168 | if (!do_match_1(f[i], re, in, out, val, match)) |
| 169 | return 0; |
| 170 | return -1; |
| 171 | } |
| 172 | |
| 173 | |
| 174 | static const struct subst *recurse_sub(const struct subst *sub, |
| 175 | const struct var *in, const char *s, const regmatch_t *match, |
| 176 | struct var **out) |
| 177 | { |
| 178 | const struct subst *jump; |
| 179 | regmatch_t m_tmp[10]; |
| 180 | const char *val; |
| 181 | char *tmp; |
| 182 | |
| 183 | while (sub) { |
| 184 | switch (sub->type) { |
| 185 | case st_match: |
| 186 | if (do_match(sub->u.match.src, &sub->u.match.re, |
| 187 | in, *out, &val, m_tmp)) |
| 188 | break; |
| 189 | jump = recurse_sub(sub->u.match.block, in, val, m_tmp, |
| 190 | out); |
| 191 | if (jump && jump != sub) |
| 192 | return jump; |
| 193 | break; |
| 194 | case st_assign: |
| 195 | tmp = compose(sub->u.assign.pat, in, *out, s, match); |
| 196 | do_assign(sub->u.assign.dst, out, tmp); |
| 197 | break; |
| 198 | case st_end: |
| 199 | return &jump_end; |
| 200 | case st_break: |
| 201 | sub = sub->u.jump->next; |
| 202 | continue; |
| 203 | case st_again: |
| 204 | sub = sub->u.jump->u.match.block; |
| 205 | continue; |
| 206 | default: |
| 207 | abort(); |
| 208 | } |
| 209 | sub = sub->next; |
| 210 | } |
| 211 | return NULL; |
| 212 | } |
| 213 | |
| 214 | |
| 215 | struct var *substitute(const struct subst *sub, const struct var *in) |
| 216 | { |
| 217 | struct var *out = NULL; |
| 218 | int i; |
| 219 | char tmp[4]; |
| 220 | |
| 221 | if (!fn) { |
| 222 | fn = unique("FN"); |
| 223 | for (i = 0; i != FIELDS; i++) { |
| 224 | sprintf(tmp, "F%d", i); |
| 225 | f[i] = unique(tmp); |
| 226 | } |
| 227 | } |
| 228 | recurse_sub(sub, in, NULL, NULL, &out); |
| 229 | return out; |
| 230 | } |
| 231 | |
| 232 | |
| 233 | const char *var_lookup(const struct var *vars, const char *name) |
| 234 | { |
| 235 | while (vars) { |
| 236 | if (vars->name == name) |
| 237 | return vars->value; |
| 238 | vars = vars->next; |
| 239 | } |
| 240 | return NULL; |
| 241 | } |
| 242 | |
| 243 | |
| 244 | void free_vars(struct var *vars) |
| 245 | { |
| 246 | struct var *next; |
| 247 | |
| 248 | while (vars) { |
| 249 | next = vars->next; |
| 250 | free(vars); |
| 251 | vars = next; |
| 252 | } |
| 253 | } |