/* * subex.c - Substitution (execution) * * Copyright 2012 by Werner Almesberger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. */ #include #include #include #include #include #include "util.h" #include "lang.h" #include "subst.h" #include "subex.h" #define FIELDS 10 static const char *fn = NULL, *f[FIELDS]; /* Jump target that can never be reached. */ static struct subst jump_end; static void append_n(char **res, int *res_len, const char *s, int len) { if (!len) return; *res = realloc(*res, *res_len+len+1); if (!*res) abort(); memcpy(*res+*res_len, s, len); *res_len += len; (*res)[*res_len] = 0; } static void append(char **res, int *res_len, const char *s) { append_n(res, res_len, s, strlen(s)); } #if 0 static void append_char(char **res, int *res_len, char c) { append_n(res, res_len, &c, 1); } #endif /* * TODO: decide what to do with units */ static char *compose(const struct chunk *c, const struct var *in, const struct var *out, const char *s, const regmatch_t *match) { char *res = stralloc(""); int res_len = 0; const char *val; int n; while (c) { switch (c->type) { case ct_string: append(&res, &res_len, c->u.s); break; case ct_var: val = var_lookup(in, c->u.var); if (!val) val = var_lookup(out, c->u.var); if (!val) yyerrorf("unknown variable \"%s\"", c->u.var); append(&res, &res_len, val); break; case ct_sub: n = c->u.sub; if (!s) yyerrorf("$%c without prior match", n ? n+'0' : '$'); if (!n) { append(&res, &res_len, s); break; } if (match[n-1].rm_so == -1) yyerrorf("substring $%d out of range", n); #if 0 len = match[n-1].rm_eo-match[n-1].rm_so); tmp = alloc_size(len); memcpy(tmp, s+match[n-1].rm_so, len); tmp[len] = 0; tmp2 = canonicalize(tmp, c->unit); append(&res, &res_len, tmp2); #endif append_n(&res, &res_len, s+match[n-1].rm_so, match[n-1].rm_eo-match[n-1].rm_so); break; default: abort(); } c = c->next; } return res; } struct var *make_var(const char *name, const char *val) { struct var *var; var = alloc_type(struct var); var->name = unique(name); var->value = unique(val); var->next = NULL; return var; } static void do_assign(const char *name, struct var **out, const char *val) { while (*out) { if ((*out)->name == name) break; out = &(*out)->next; } if (*out) { free((void *) (*out)->value); (*out)->value = val; } else { *out = make_var(name, val); } } static int do_match_1(const char *var, const regex_t *re, const struct var *in, const struct var *out, const char **val, regmatch_t *match) { *val = var_lookup(out, var); if (!*val) *val = var_lookup(in, var); if (!*val) return -1; return regexec(re, *val, 10, match, 0); } static int do_match(const char *var, const regex_t *re, const struct var *in, const struct var *out, const char **val, regmatch_t *match) { int i; if (var != fn) return do_match_1(var, re, in, out, val, match); for (i = 0; i != FIELDS; i++) if (!do_match_1(f[i], re, in, out, val, match)) return 0; return -1; } static const struct subst *recurse_sub(const struct subst *sub, const struct var *in, const char *s, const regmatch_t *match, struct var **out) { const struct subst *jump; regmatch_t m_tmp[10]; const char *val; char *tmp; while (sub) { switch (sub->type) { case st_match: if (do_match(sub->u.match.src, &sub->u.match.re, in, *out, &val, m_tmp)) break; jump = recurse_sub(sub->u.match.block, in, val, m_tmp, out); if (jump && jump != sub) return jump; break; case st_assign: tmp = compose(sub->u.assign.pat, in, *out, s, match); do_assign(sub->u.assign.dst, out, tmp); break; case st_end: return &jump_end; case st_break: sub = sub->u.jump->next; continue; case st_again: sub = sub->u.jump->u.match.block; continue; default: abort(); } sub = sub->next; } return NULL; } struct var *substitute(const struct subst *sub, const struct var *in) { struct var *out = NULL; int i; char tmp[4]; if (!fn) { fn = unique("FN"); for (i = 0; i != FIELDS; i++) { sprintf(tmp, "F%d", i); f[i] = unique(tmp); } } recurse_sub(sub, in, NULL, NULL, &out); return out; } const char *var_lookup(const struct var *vars, const char *name) { while (vars) { if (vars->name == name) return vars->value; vars = vars->next; } return NULL; } void free_vars(struct var *vars) { struct var *next; while (vars) { next = vars->next; free(vars); vars = next; } }