1
0
mirror of git://projects.qi-hardware.com/eda-tools.git synced 2024-10-01 11:46:22 +03:00
eda-tools/b2/subst.c
2012-05-20 20:57:18 -03:00

358 lines
6.1 KiB
C

/*
* subst.c - Substitution rules
*
* 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 <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <sys/types.h>
#include <regex.h>
#include <assert.h>
#include "util.h"
#include "vstring.h"
#include "lang.h"
#include "subst.h"
/* ----- Rule set construction --------------------------------------------- */
static struct subst *alloc_subst(enum subst_type type)
{
struct subst *sub;
sub = alloc_type(struct subst);
sub->type = type;
sub->next = NULL;
return sub;
}
static char *prepare_re(const char *re)
{
char *res = NULL;
int res_len = 0;
append_char(&res, &res_len, '^');
append(&res, &res_len, re);
append_char(&res, &res_len, '$');
return res;
}
struct subst *subst_match(const char *src, const char *re)
{
char error[1000];
struct subst *sub;
char *tmp;
int err;
sub = alloc_subst(st_match);
sub->u.match.src = src;
tmp = prepare_re(re);
err = regcomp(&sub->u.match.re, tmp, REG_EXTENDED);
free(tmp);
if (err) {
regerror(err, &sub->u.match.re, error, sizeof(error));
yyerrorf("%s", error);
}
return sub;
}
static void end_chunk(struct chunk ***last, const char *start, const char *s)
{
struct chunk *c;
if (s == start)
return;
c = alloc_type(struct chunk);
c->type = ct_string;
c->u.s = stralloc_n(start, s-start);;
c->unit = NULL;
c->next = NULL;
**last = c;
*last = &c->next;
}
static const char *parse_var(struct chunk *c, const char *s)
{
const char *t;
int braced;
if (!*s)
yyerror("trailing dollar sign");
braced = *s == '{';
if (braced)
s++;
t = s;
while (*t) {
if (braced && *t == '}')
break;
if (s == t && *t == '$') {
t++;
break;
}
if (!isalnum(*t))
break;
t++;
}
if (s == t)
yyerror("invalid variable name");
if (braced && !*t)
yyerror("unterminated variable name");
if (isdigit(*s)) {
if (t != s+1 || *s == '0')
yyerror("invalid variable name");
c->type = ct_sub;
c->u.sub = *s-'0';
} else if (isalnum(*s)) {
char *tmp;
c->type = ct_var;
tmp = stralloc_n(s, t-s);
c->u.var = unique(tmp);
free(tmp);
} else {
c->type = ct_sub;
c->u.sub = 0;
}
if (*t != '#') {
if (braced) {
assert(*t == '}');
t++;
}
return t;
}
s = ++t;
while (*t) {
if (braced && *t == '}')
break;
if (!braced && t != s)
break;
t++;
}
if (s == t)
yyerror("invalid unit");
c->unit = stralloc_n(s, t-s);
if (braced) {
if (!*t)
yyerror("unterminated unit");
t++;
}
return t;
}
static struct chunk *parse_pattern(const char *s)
{
struct chunk *res = NULL, **last = &res;
struct chunk *c;
const char *start = s;
while (*s) {
if (*s == '\\') {
if (!s[1])
yyerror("trailing backslash");
end_chunk(&last, start, s);
start = s+1;
s += 2;
continue;
}
if (*s != '$') {
s++;
continue;
}
end_chunk(&last, start, s);
c = alloc_type(struct chunk);
c->unit = NULL;
c->next = NULL;
*last = c;
last = &c->next;
start = s = parse_var(c, s+1);
}
end_chunk(&last, start, s);
return res;
}
struct subst *subst_assign(const char *dst, const char *pat)
{
struct subst *sub;
sub = alloc_subst(st_assign);
sub->u.assign.dst = dst;
sub->u.assign.pat = parse_pattern(pat);
return sub;
}
struct subst *subst_end(void)
{
return alloc_subst(st_end);
}
struct subst *subst_break(const char *block)
{
struct subst *sub;
sub = alloc_subst(st_break);
sub->u.tmp = block;
return sub;
}
struct subst *subst_again(const char *block)
{
struct subst *sub;
sub = alloc_subst(st_again);
sub->u.tmp = block;
return sub;
}
/* ----- Jump resolution --------------------------------------------------- */
struct parent {
const struct subst *sub;
const struct parent *parent;
};
static const struct subst *resolve_jump(const char *name,
const struct parent *parent)
{
while (parent) {
assert(parent->sub->type == st_match);
if (!strcmp(name, parent->sub->u.match.src))
return parent->sub;
parent = parent->parent;
}
yyerrorf("cannot find \"%s\"", name);
}
static void recurse_fin(struct subst *sub, const struct parent *parent)
{
struct parent next = {
.sub = sub,
.parent = parent,
};
while (sub) {
switch (sub->type) {
case st_match:
recurse_fin(sub->u.match.block, &next);
case st_assign:
case st_end:
break;
case st_break:
case st_again:
sub->u.jump = resolve_jump(sub->u.tmp, parent);
break;
default:
abort();
}
sub = sub->next;
}
}
void subst_finalize(struct subst *sub)
{
recurse_fin(sub, NULL);
}
/* ----- Dumping ----------------------------------------------------------- */
#define INDENT 4
static void dump_chunks(FILE *file, const struct chunk *c)
{
while (c) {
switch (c->type) {
case ct_string:
fprintf(file, "%s", c->u.s);
break;
case ct_var:
fprintf(file, "${%s", c->u.var);
break;
case ct_sub:
if (c->u.sub)
fprintf(file, "${%d", c->u.sub);
else
fprintf(file, "${$");
break;
default:
abort();
}
if (c->type != ct_string) {
if (c->unit)
fprintf(file, "#%s", c->unit);
fprintf(file, "}");
}
c = c->next;
}
}
static void recurse_dump(FILE *file, const struct subst *sub, int level)
{
while (sub) {
fprintf(file, "%*s", INDENT*level, "");
switch (sub->type) {
case st_match:
fprintf(file, "%s=RE {\n", sub->u.match.src);
recurse_dump(file, sub->u.match.block, level+1);
fprintf(file, "%*s}\n", INDENT*level, "");
break;
case st_assign:
fprintf(file, "%s=", sub->u.assign.dst);
dump_chunks(file, sub->u.assign.pat);
fprintf(file, "\n");
break;
case st_end:
fprintf(file, "end\n");
break;
case st_break:
fprintf(file, "break %s\n", sub->u.jump->u.match.src);
break;
case st_again:
fprintf(file, "again %s\n", sub->u.jump->u.match.src);
break;
default:
abort();
}
sub = sub->next;
}
}
void subst_dump(FILE *file, const struct subst *sub)
{
recurse_dump(file, sub, 0);
}