/* satu: string analyzing table generator vi: set ts=8 sw=8: */
/* satu--string analyzing table generator
* Copyright (C) 2003 Peter Backes
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include
#include
#include
#include
#include
#define TABCAP 128 /* Should be plenty */
typedef unsigned char tab_t[256];
static struct variab {
char ident[16];
tab_t data;
} table[TABCAP];
struct parstat {
char error[84];
int ln;
const char *fn;
FILE *co, *ho;
};
/* Sets range to specified values.
* f: field
* b: begin
* e: end
* s: start value
* r: increment value
*/
void setrng(tab_t *f, int b, int e, int s, int r)
{
for (e = e < b ? b : e; b <= e; s += r)
(*f)[b++] = s;
}
/* % comment * ^val rise operator
* & bitwise and * | bitwise or
* ! logical complement * #a m)
m = (*l)[i];
for (i = 0; i < sizeof *r; i++)
if ((*r)[i] != s)
(*r)[i] = m + (*r)[i];
}
void rotate(tab_t *l)
{
int i;
for (i = 0; i < sizeof *l; i++)
if ((*l)[i])
(*l)[i]--;
else
(*l)[i] = 0xFF;
}
void comple(tab_t *l)
{
int i;
for (i = 0; i < sizeof *l; i++)
(*l)[i] = !(*l)[i];
}
void bitwor(tab_t *l, /*const*/ tab_t *r)
{
int i;
for (i = 0; i < sizeof *l; i++)
(*l)[i] |= (*r)[i];
}
void bitwnd(tab_t *l, /*const*/ tab_t *r)
{
int i;
for (i = 0; i < sizeof *l; i++)
(*l)[i] &= (*r)[i];
}
/* Multiplies table with a scalar. */
void settab(tab_t *l, /*const*/ tab_t *r, int s)
{
int i;
for (i = 0; i < sizeof *l; i++)
(*l)[i] = (*r)[i] * s;
}
struct variab *findvar(char *ident)
{
struct variab *curr;
for (curr = table; curr < table + TABCAP; curr++)
if (!strcmp(curr->ident, ident))
return curr;
else if (!*curr->ident)
break;
return NULL;
}
int isident(int c)
{
return isalnum(c) || c == '_';
}
const char *lex(const char *b, char *s, int (*c)(int), int (*d)(int), int l)
{
/* b: bottom of input.
* s: symbol storage buffer.
* c: character set constraining topmost character.
* d: character set of word to scan, NULL if nothing
* is to be matched.
* l: symbol length restriction.
*/
register const char *t = b;
/* Skip leading space. */
while (isspace(*t) && *t != '\r' && *t != '\n')
t++;
/* On zero length restriction, only report position behind space. */
if (!l)
return t;
if (!(*c)(*t))
return NULL;
for (b = t, *s++ = *t++; t - b < l && d && (*d)(*t); *s++ = *t++);
*s++ = '\0';
while (d && (*d)(*t))
t++;
return t;
}
int getesc(int i)
{
switch (i) {
case 'a': return '\a'; case 'b': return '\b';
case 'f': return '\f'; case 'n': return '\n';
case 'r': return '\r'; case 't': return '\t';
case 'v': return '\v'; case '0': return '\0';
default: return i;
}
}
const char *gettab(tab_t *f, const char *top, struct parstat *ps)
{
int inve; /* invert flag. */
top += inve = *top == '!';
if (*top == '\'' && ((top[1] == '\\' && top[2] != '\0'
&& top[2] != '\n' && top[3] == '\'') || (top[1] != '\\'
&& top[1] != '\0' && top[1] != '\n' && top[2] == '\'')))
{
int b = (unsigned char) (*++top == '\\'
? getesc(*++top) : *top), e = b, r = 0, s;
top++;
if ((*++top == '-' || *top == '>') && top[1] == '\''
&& ((top[2] == '\\' && top[3] != '\0' && top[3] != '\n'
&& top[4] == '\'') || (top[2] != '\\'
&& top[2] != '\0' && top[2] != '\n'
&& top[3] == '\'')))
{
r = *top++ == '>';
e = (unsigned char) *++top == '\\'
? getesc(*++top) : *top;
top += 2;
}
if (!(s = *top != '=') && isdigit(*++top))
while (isdigit(*top))
s = *top++ - '0' + s * 10;
setrng(f, b, e, s, r);
} else if (isdigit(*top)) {
int b = 0, e = 0, r = 0, s;
while (isdigit(*top))
e = b = *top++ - '0' + b * 10;
if ((*top == '-' || *top == '>') && isdigit(top[1])) {
r = *top++ == '>';
e = 0;
while (isdigit(*top))
e = *top++ - '0' + e * 10;
}
if (!(s = *top != '=') && isdigit(*++top))
while (isdigit(*top))
s = *top++ - '0' + s * 10;
setrng(f, b, e, s, r);
} else if (isalpha(*top)) {
char ident[16], *dst = ident;
struct variab *v;
while (isalnum(*top) && dst < ident + sizeof ident - 1)
*dst++ = *top++;
*dst++ = '\0';
while (isalpha(*top))
top++;
if ((v = findvar(ident)) == NULL) {
snprintf(ps->error, sizeof ps->error, "undefined "
"identifier '%s'", ident);
return NULL;
}
if (*top == '=' && isdigit(*++top)) {
int s = 0;
while (isdigit(*top))
s = *top++ - '0' + s * 10;
settab(f, &v->data, s);
} else
memcpy(f, v->data, sizeof v->data);
} else
return NULL;
if (inve)
comple(f);
return top;
}
void output(struct parstat *ps, const char *n, const struct variab *v, int s)
{
int i;
fprintf(ps->ho, "extern const unsigned char %s[%d];\n", n, s);
fprintf(ps->co, "const unsigned char %s[%d] = {\n\t", n, s);
switch (s) {
case 32:
for (i = 0; i < 32; i++) {
/* Data base. */
const unsigned char *b = v->data + i * 8;
fprintf(ps->co, "0x%02X", b[0] | b[1] << 1
| b[2] << 2 | b[3] << 3
| b[4] << 4 | b[5] << 5
| b[6] << 6 | b[7] << 7);
if (i != 31)
fprintf(ps->co, i % 8 == 7 ? ", \n\t" : ", ");
}
break;
case 64:
for (i = 0; i < 64; i++) {
const unsigned char *b = v->data + i * 4;
fprintf(ps->co, "0x%02X", b[0] | b[1] << 2
| b[2] << 4 | b[3] << 6);
if (i != 63)
fprintf(ps->co, i % 8 == 7 ? ", \n\t" : ", ");
}
break;
case 128:
for (i = 0; i < 128; i++) {
const unsigned char *b = v->data + i * 2;
fprintf(ps->co, "0x%02X", b[0] | b[1] << 4);
if (i != 127)
fprintf(ps->co, i % 8 == 7 ? ", \n\t" : ", ");
}
break;
case 256:
for (i = 0; i < 256; i++) {
fprintf(ps->co, "0x%02X", v->data[i]);
if (i != 255)
fprintf(ps->co, i % 8 == 7 ? ", \n\t" : ", ");
}
break;
}
fprintf(ps->co, "\n};\n");
}
const char *gen_dlst(const char *top, struct parstat *ps)
{
char name[32], buf[16], ident[16];
struct variab *v;
int s = 0;
top = lex(top, name, &isalpha, &isident, sizeof name - 1);
if (top == NULL)
return NULL;
top = lex(top, buf, &ispunct, NULL, sizeof buf - 1);
if (top == NULL || *buf != '[')
return NULL;
top = lex(top, buf, &isdigit, &isdigit, sizeof buf - 1);
if (top == NULL)
return NULL;
else
s = atoi(buf);
top = lex(top, buf, &ispunct, NULL, sizeof buf - 1);
if (top == NULL || *buf != ']')
return NULL;
top = lex(top, buf, &ispunct, NULL, sizeof buf - 1);
if (top == NULL || *buf != '=')
return NULL;
top = lex(top, ident, &isalpha, &isalnum, sizeof ident - 1);
if (top == NULL)
return NULL;
printf("[%s[%d] <== %s]\n", name, s, ident);
if ((v = findvar(ident)) == NULL)
printf("%s:%d: identifier %s undefined\n",
ps->fn, ps->ln, ident);
else
output(ps, name, v, s);
return lex(top, NULL, NULL, NULL, 0);
}
const char *gen_data(const char *top, struct parstat *ps)
{
/* data name[size]=ident, */
do
if ((top = gen_dlst(top, ps)) == NULL)
return NULL;
while (*top++ == ',');
return *--top != '%' && *top != '\n' && *top != '\0' ? NULL : top;
}
const char *gen_defl(const char *top, struct parstat *ps, tab_t *l)
{
unsigned char r[256] = {};
void (*op)(tab_t *l, /*const*/ tab_t *r);
int rise; /* rise flag */
switch (*top++) {
case '|':
op = &bitwor;
break;
case '&':
op = &bitwnd;
break;
default:
return NULL;
}
top = lex(top, NULL, NULL, NULL, 0);
top += rise = *top == '^';
if ((top = gettab(&r, top, ps)) == NULL)
return NULL;
if (rise)
serial(l, &r, 0);
(*op)(l, &r);
return lex(top, NULL, NULL, NULL, 0);
}
const char *gen_def(const char *top, struct parstat *ps)
{
char ident[16];
struct variab *v;
int rota; /* rotate flag */
unsigned char l[256] = {};
top = lex(top, ident, &isalpha, &isalnum, sizeof ident - 1);
if (top == NULL)
return NULL;
top = lex(top, NULL, NULL, NULL, 0);
top += rota = *top == '^';
if ((top = gettab(&l, top, ps)) == NULL)
return NULL;
top = lex(top, NULL, NULL, NULL, 0);
while (*top == '|' || *top == '&')
if ((top = gen_defl(top, ps, &l)) == NULL)
return NULL;
if (rota)
rotate(&l);
if ((v = findvar(ident)) == NULL && (v = findvar("")) == NULL) {
printf("%s:%d: table capacity exceeded, no room for '%s'\n",
ps->fn, ps->ln, ident);
return top;
}
printf("[%s%s%s]\n", v->ident, v->ident ? "" : " <-- ", ident);
strcpy(v->ident, ident);
memcpy(v->data, l, sizeof l);
return *top != '%' && *top != '\n' && *top != '\0' ? NULL : top;
}
void gen(const char *fn, FILE *si, FILE *co, FILE *ho)
{
struct parstat ps = {{}, 0, fn, co, ho};
char line[512], cmd[16];
const char *top;
while ((top = fgets(line, sizeof line, si)) != NULL) {
ps.ln++;
top = lex(top, NULL, NULL, NULL, 0);
if (*top == '%' || *top == '\n' || *top == '\0')
continue;
top = lex(top, cmd, &isalnum, &isalnum, sizeof cmd - 1);
if (top != NULL) {
if (!strcasecmp(cmd, "def"))
top = gen_def(top, &ps);
else if (!strcasecmp(cmd, "data"))
top = gen_data(top, &ps);
else
printf("%s:%d: unknown keyword '%s'\n", ps.fn,
ps.ln, cmd);
}
if (top == NULL) {
printf("%s:%d: %s\n", fn, ps.ln, *ps.error
? ps.error : "syntax error");
*ps.error = '\0';
continue;
}
}
}
int main(int argc, char *argv[])
{
const char *cofn = NULL, *hofn = NULL; /* c and h output file name */
FILE *si, *co, *ho; /* source input, c output, h output */
int opt;
while ((opt = getopt(argc, argv, "o:h:")) != -1)
switch (opt) {
case 'o':
cofn = optarg;
break;
case 'h':
hofn = optarg;
break;
}
if (cofn == NULL || hofn == NULL) {
fprintf(stderr, "output unspecified.\n");
return EXIT_FAILURE;
}
if ((co = fopen(cofn, "w")) == NULL) {
fprintf(stderr, "Can't open %s for output.\n", cofn);
return EXIT_FAILURE;
}
if ((ho = fopen(hofn, "w")) == NULL) {
fprintf(stderr, "Can't open %s for output.\n", hofn);
fclose(co);
return EXIT_FAILURE;
}
for (;optind < argc; optind++) {
if ((si = fopen(argv[optind], "r")) == NULL) {
fprintf(stderr, "Couldn't open %s, skipped.\n",
argv[optind]);
continue;
}
gen(argv[optind], si, co, ho);
fclose(si);
}
fclose(ho);
fclose(co);
return EXIT_SUCCESS;
}