mirror of
git://projects.qi-hardware.com/eda-tools.git
synced 2024-11-26 14:04:39 +02:00
eeshow/kicad/sexpr.c, sexpr.h: simple parser for S-expressions
This commit is contained in:
parent
328ddd9ca7
commit
12f66ec47e
@ -15,7 +15,7 @@ SHELL = /bin/bash
|
|||||||
NAME = eeshow
|
NAME = eeshow
|
||||||
OBJS = main.o version.o \
|
OBJS = main.o version.o \
|
||||||
kicad/sch-parse.o kicad/sch-render.o kicad/lib-parse.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/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/progress.o gui/glabel.o gui/sheet.o gui/history.o gui/render.o \
|
||||||
gui/help.o gui/icons.o \
|
gui/help.o gui/icons.o \
|
||||||
|
335
eeshow/kicad/sexpr.c
Normal file
335
eeshow/kicad/sexpr.c
Normal file
@ -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 <stdbool.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
47
eeshow/kicad/sexpr.h
Normal file
47
eeshow/kicad/sexpr.h
Normal file
@ -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 <stdbool.h>
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 */
|
Loading…
Reference in New Issue
Block a user