mirror of
git://projects.qi-hardware.com/eda-tools.git
synced 2024-11-26 15:15:20 +02:00
350 lines
6.0 KiB
C
350 lines
6.0 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 "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;
|
|
}
|
|
|
|
|
|
struct subst *subst_match(const char *src, const char *re)
|
|
{
|
|
char error[1000];
|
|
struct subst *sub;
|
|
char *tmp;
|
|
int err, len;
|
|
|
|
sub = alloc_subst(st_match);
|
|
sub->u.match.src = src;
|
|
len = strlen(re);
|
|
tmp = alloc_size(len+3);
|
|
tmp[0] = '^';
|
|
memcpy(tmp+1, re, len);
|
|
tmp[len+1] = '$';
|
|
tmp[len+2] = 0;
|
|
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);
|
|
}
|