diff --git a/eeshow/Makefile b/eeshow/Makefile index db3a01b..02e0b11 100644 --- a/eeshow/Makefile +++ b/eeshow/Makefile @@ -15,7 +15,7 @@ SHELL = /bin/bash NAME = eeshow OBJS = main.o version.o \ kicad/sch-parse.o kicad/sch-render.o kicad/lib-parse.o \ - kicad/lib-render.o kicad/dwg.o kicad/delta.o \ + kicad/lib-render.o kicad/dwg.o kicad/delta.o kicad/sexpr.o \ gui/gui.o gui/over.o gui/style.o gui/aoi.o gui/fmt-pango.o gui/input.o \ gui/progress.o gui/glabel.o gui/sheet.o gui/history.o gui/render.o \ gui/help.o gui/icons.o \ diff --git a/eeshow/kicad/sexpr.c b/eeshow/kicad/sexpr.c new file mode 100644 index 0000000..373b757 --- /dev/null +++ b/eeshow/kicad/sexpr.c @@ -0,0 +1,335 @@ +/* + * kicad/sexpr.c - S-expression parser + * + * Written 2016 by Werner Almesberger + * Copyright 2016 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 "misc/util.h" +#include "misc/diag.h" +#include "kicad/sexpr.h" + + +struct stack { + struct expr **next; + struct stack *prev; +}; + +struct sexpr_ctx { + enum sexpr_state { + idle, + token, + string, + escape, + failed + } state; + struct expr *e; /* expression */ + const char *p; /* beginning of string */ + struct stack *sp; + struct stack stack; +}; + + +/* ----- Parser ------------------------------------------------------------ */ + + +static void new_expr(struct sexpr_ctx *ctx) +{ + struct stack *st = alloc_type(struct stack); + struct expr *e = alloc_type(struct expr); + + e->s = NULL; + e->e = NULL; + e->next = NULL; + + *ctx->sp->next = e; + ctx->sp->next = &e->next; + + st->prev = ctx->sp; + st->next = &e->e; + ctx->sp = st; +} + + +static void add_string(struct sexpr_ctx *ctx, const char *end) +{ + struct expr *e; + unsigned old; + unsigned new = end - ctx->p; + + e = *ctx->sp->next; + if (e) { + old = strlen(e->s); + } else { + e = alloc_type(struct expr); + e->s = NULL; + e->e = NULL; + e->next = NULL; + + *ctx->sp->next = e; + old = 0; + } + + if (!new) + return; + + e->s = realloc(e->s, old + new + 1); + if (!e->s) + diag_pfatal("realloc"); + memcpy(e->s + old, ctx->p, new); + e->s[old + new] = 0; +} + + +static void end_string(struct sexpr_ctx *ctx, const char *end) +{ + struct expr *e; + char *s, *t; + + add_string(ctx, end); + e = *ctx->sp->next; + ctx->sp->next = &e->next; + + for (s = t = e->s; *s; s++) { + if (*s == '\\') + switch (*++s) { + case 'n': + *t++ = '\n'; + continue; + case 't': + *t++ = '\t'; + continue; + case 0: + abort(); + default: + break; + } + *t++ = *s; + } + *t = 0; +} + + +bool sexpr_parse(struct sexpr_ctx *ctx, const char *s) +{ + ctx->p = s; + while (*s) { + switch (ctx->state) { + case idle: + switch (*s) { + case ' ': + case '\t': + case '\r': + case '\n': + break; + case '(': + new_expr(ctx); + break; + case ')': + if (!ctx->sp->prev) { + ctx->state = failed; + error("too many\n )"); + break; + } + ctx->sp = ctx->sp->prev; + break; + case '"': + ctx->state = string; + ctx->p = s + 1; + break; + default: + ctx->p = s; + ctx->state = token; + break; + } + break; + case token: + switch (*s) { + case ' ': + case '\t': + case '\r': + case '\n': + ctx->state = idle; + end_string(ctx, s); + break; + case '(': + case ')': + case '"': + ctx->state = idle; + end_string(ctx, s); + continue; + default: + break; + } + break; + case string: + switch (*s) { + case '\r': + case '\n': + ctx->state = failed; + error("newline in string\n"); + break; + case '"': + ctx->state = idle; + add_string(ctx, s); + break; + case '\\': + ctx->state = escape; + break; + default: + break; + } + break; + case escape: + switch (*s) { + case '\r': + case '\n': + ctx->state = failed; + error("newline in string\n"); + break; + default: + ctx->state = string; + break; + } + break; + case failed: + return 0; + default: + abort(); + } + s++; + } + + switch (ctx->state) { + case token: + case string: + case escape: + add_string(ctx, s); + break; + default: + break; + } + + return 1; +} + + +/* ----- Dumping ----------------------------------------------------------- */ + + +static void do_dump_expr(const struct expr *e, unsigned depth) +{ + bool need_nl = 0; + + if (depth) + printf("%*s(", (depth - 1) * 2, ""); + while (e) { + if (e->s) { + if (need_nl) { + printf("\n%*s", depth * 2, ""); + need_nl = 0; + } + printf("\"%s\" ", e->s); /* @@@ escaping */ + } + if (e->e) { + putchar('\n'); + do_dump_expr(e->e, depth + 1); + need_nl = 1; + } + e = e->next; + } + if (depth) + putchar(')'); +} + + +void dump_expr(const struct expr *e) +{ + do_dump_expr(e, 0); + putchar('\n'); +} + + +/* ----- Cleanup ----------------------------------------------------------- */ + + +static void free_stack(struct sexpr_ctx *ctx) +{ + struct stack *prev; + + while (ctx->sp != &ctx->stack) { + prev = ctx->sp->prev; + free(ctx->sp); + ctx->sp = prev; + } +} + + +void free_expr(struct expr *e) +{ + struct expr *next; + + while (e) { + next = e->next; + if (e->s) + free(e->s); + else if (e->e) + free_expr(e->e); + free(e); + e = next; + } +} + + +/* ----- Parser creation --------------------------------------------------- */ + + +struct sexpr_ctx *sexpr_new(void) +{ + struct sexpr_ctx *ctx; + + ctx = alloc_type(struct sexpr_ctx); + ctx->state = idle; + ctx->e = NULL; + ctx->p = NULL; + ctx->sp = &ctx->stack; + ctx->stack.next = &ctx->e; + ctx->stack.prev = NULL; + return ctx; +} + + +/* ----- Termination ------------------------------------------------------- */ + + +bool sexpr_finish(struct sexpr_ctx *ctx, struct expr **res) +{ + if (ctx->sp != &ctx->stack) { + error("not enough )\n"); + ctx->state = failed; + free_stack(ctx); + } + if (ctx->state != idle && ctx->state != failed) + error("invalid end state %d\n", ctx->state); + if (ctx->state == idle) { + if (res) + *res = ctx->e; + else + free_expr(ctx->e); + free(ctx); + return 1; + } + free_expr(ctx->e); + free(ctx); + return 0; +} diff --git a/eeshow/kicad/sexpr.h b/eeshow/kicad/sexpr.h new file mode 100644 index 0000000..8c47f28 --- /dev/null +++ b/eeshow/kicad/sexpr.h @@ -0,0 +1,47 @@ +/* + * kicad/sexpr.h - S-expression parser + * + * Written 2016 by Werner Almesberger + * Copyright 2016 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. + */ + + +#ifndef KICAD_SEXPR_H +#define KICAD_SEXPR_H + +#include + + +/* + * Expression type: + * + * s e Meaning + * ------------ -------- ---------- + * NULL NULL "" or () + * non-NULL NULL foo or "foo" + * NULL non-NULL (...) + * non-NULL non-NULL trouble + */ + +struct expr { + char *s; /* string or NULL */ + struct expr *e; /* sub-sexpr or NULL*/ + struct expr *next; +}; + +struct sexpr_ctx; + + +void dump_expr(const struct expr *e); +void free_expr(struct expr *e); + +struct sexpr_ctx *sexpr_new(void); +bool sexpr_parse(struct sexpr_ctx *ctx, const char *s); +bool sexpr_finish(struct sexpr_ctx *ctx, struct expr **res); + +#endif /* !KICAD_SEXPR_H */