mirror of
git://projects.qi-hardware.com/eda-tools.git
synced 2024-11-22 15:10:38 +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
|
||||
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 \
|
||||
|
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