/* * 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 #include #include "util.h" #include "vstring.h" #include "lang.h" #include "relop.h" #include "subst.h" #include "subex.h" #define FIELDS 10 /* fields in KiCad schematics */ static const char *fn = NULL, *f[FIELDS]; /* Jump targets that can never be reached. */ static struct subst jump_end; static struct subst jump_ignore; static char *canonicalize(const char *s, char unit) { char *res; int res_len = 0; int seen_dot = 0; int seen_unit = 0; char mult = 0; if (!unit) return stralloc(s); res = stralloc(""); while (*s) { if (*s == unit) { assert(!seen_unit); if (!s[1]) break; if (!seen_dot) append_char(&res, &res_len, '.'); seen_unit = seen_dot = 1; s++; continue; } if (*s == '.') { assert(!seen_dot); append_char(&res, &res_len, '.'); seen_dot = 1; } else if (*s == '-' || isdigit(*s)) { append_char(&res, &res_len, *s); s++; continue; } else if (strchr(MULT_CHARS, *s)) { assert(!seen_unit); assert(!mult); mult = *s; if (!seen_dot) append_char(&res, &res_len, '.'); seen_dot = 1; } else { abort(); } s++; } if (res_len && res[res_len-1] == '.') res_len--; if (mult) append_char(&res, &res_len, mult); append_char(&res, &res_len, unit); return res; } static char *compose(const struct chunk *c, const struct param *in, const struct param *out, const char *s, const regmatch_t *match, const char *units) { char *res = stralloc(""); int res_len = 0, len; const char *val; char *tmp, *tmp2; 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); assert(val); append(&res, &res_len, val); break; case ct_sub: n = c->u.sub; assert(s); if (!n) { append(&res, &res_len, s); break; } if (match[n].rm_so == -1) break; len = match[n].rm_eo-match[n].rm_so; tmp = alloc_size(len); memcpy(tmp, s+match[n].rm_so, len); tmp[len] = 0; tmp2 = canonicalize(tmp, units ? units[n-1] : 0); append(&res, &res_len, tmp2); free(tmp); free(tmp2); break; default: abort(); } c = c->next; } return res; } static void do_assign(const char *name, struct param **out, enum relop op, const char *val) { while (*out) { if ((*out)->u.name == name) break; out = &(*out)->next; } if (*out) (*out)->value.u.s = val; else *out = make_var(name, op, val); } static int do_match_1(const char *var, const regex_t *re, const struct param *in, const struct param *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, NMATCH, match, 0); } static int do_match(const char **var, const regex_t *re, const struct param *in, const struct param *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)) { *var = f[i]; return 0; } return -1; } static const struct subst *recurse_sub(const struct subst *sub, const struct param *in, const char *matched_var, const char *matched_val, const regmatch_t *match, const char *units, struct param **out, enum subst_type *cause) { const struct subst *jump; regmatch_t m_tmp[NMATCH]; const char *var, *val; char *tmp; while (sub) { switch (sub->type) { case st_match: var = sub->u.match.src; if (var == dollar) var = matched_var; if (do_match(&var, &sub->u.match.re, in, *out, &val, m_tmp)) break; jump = recurse_sub(sub->u.match.block, in, var, val, m_tmp, sub->u.match.units, out, cause); if (jump) { if (jump != sub) return jump; if (*cause == st_continue) continue; } break; case st_assign: tmp = compose(sub->u.assign.pat, in, *out, matched_val, match, units); var = sub->u.assign.dst; if (var == dollar) var = matched_var; do_assign(var, out, sub->u.assign.op, tmp); break; case st_end: return &jump_end; case st_ignore: return &jump_ignore; case st_break: case st_continue: *cause = sub->type; return sub->u.jump; default: abort(); } sub = sub->next; } return NULL; } int substitute(const struct subst *sub, const struct param *in, struct param **out) { int i; char tmp[4]; enum subst_type cause = 0; if (!fn) { fn = unique("FN"); for (i = 0; i != FIELDS; i++) { sprintf(tmp, "F%d", i); f[i] = unique(tmp); } } *out = NULL; return recurse_sub(sub, in, NULL, NULL, NULL, NULL, out, &cause) != &jump_ignore; }