2 lread.c: simple sexp-like data structures in C.
3 useful for communication between emacs and C client programs
5 Copyright (C) 1992 Nick Thompson (nix@cs.cmu.edu)
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 add tag checking on CAR, CDR, etc?
30 #include <string.h> /* for strlen() */
33 vmake_cons(Value *car, Value *cdr)
35 Value *v = ALLOC_VALUE();
43 vmake_symbol(int length, char *data)
45 Value *v = ALLOC_VALUE();
53 vmake_symbol_c(char *s)
55 Value *v = ALLOC_VALUE();
57 VSLENGTH(v) = strlen(s);
63 vmake_string(int length, char *data)
65 Value *v = ALLOC_VALUE();
73 vmake_string_c(char *s)
75 Value *v = ALLOC_VALUE();
77 VSLENGTH(v) = strlen(s);
83 vextract_string_c(Value *v)
85 char *s = (char *) malloc(VSLENGTH(v) + 1);
86 memcpy(s, VSDATA(v), VSLENGTH(v));
87 s[VSLENGTH(v)] = '\0';
94 Value *v = ALLOC_VALUE();
101 vmake_var(enum Vtag tag, void **value)
103 Value *v = ALLOC_VALUE();
114 for (i=0; VTAG(l) == cons; i++, l = VCDR(l))
120 jmp_buf abort; /* nonlocal exit for abort */
122 char *input_string; /* input string */
123 int buflen; /* amount left in input string */
124 char *buf; /* pointer into input */
126 int strbuflen; /* length of scratch buffer */
127 char *strbuf; /* scratch buffer for building strings */
130 Value *read_value(Globals *g);
131 Value *read_list(Globals *g);
133 #define PEEK_CHAR(g) (*(g)->buf)
134 #define NEXT_CHAR(g) ((g)->buflen > 0 ? \
135 (void) ((g)->buf++,((g)->buflen--)) : \
136 (void) (ABORT(g, 23)))
137 #define ABORT(g, code) longjmp((g)->abort, (code))
139 /* A pox on languages without coroutines. */
140 /* I don't feel like putting the entire state of the parser in data
141 * structures that I can save and restore myself, so if EOF is
142 * encountered while parsing the parser will have to start from
143 * scratch when it gets more data */
146 expand_strbuf(Globals *g)
148 if (g->strbuflen == 0) {
150 g->strbuf = (char *) malloc(g->strbuflen);
153 int newbuflen = 3 * g->strbuflen / 2;
154 char *newbuf = (char *) malloc(newbuflen);
155 memcpy(newbuf, g->strbuf, g->strbuflen);
158 g->strbuflen = newbuflen;
162 int parse(int slen, char *s, Value **v)
167 if (0 == (jmpret = setjmp(g.abort))) { /* successful parse */
170 g.buf = g.input_string;
175 return g.buf - g.input_string;
177 else { /* return from nonlocal abort */
185 read_escape(Globals *g, char *c)
188 int nc = PEEK_CHAR(g);
204 if (nc >= '0' && nc <= '7') {
206 /* handle octal \nnn notation */
208 for (digits = 0; digits < 3; digits++) {
209 if (nc < '0' || nc > '7')
211 *c = (*c * 8) + (nc - '0');
216 /* backslash followed by some random char, like \q.
217 * (some of these are actually valid, but I don't think prin1 will
218 * produce them, so it's not too critical). */
228 read_string(Globals *g)
234 #define ADD_CHAR(c) \
235 if (strpos >= g->strbuflen) \
237 g->strbuf[strpos++] = (c)
240 switch (PEEK_CHAR(g)) {
245 v->value.s.length = strpos;
246 v->value.s.string = (char *) malloc(v->value.s.length);
247 memcpy(v->value.s.string, g->strbuf, v->value.s.length);
252 if (read_escape(g, &c))
256 ADD_CHAR(PEEK_CHAR(g));
274 read_num_or_symbol(Globals *g)
281 #define ADD_CHAR(c) \
282 if (strpos >= g->strbuflen) \
284 g->strbuf[strpos++] = (c)
286 while (g->buflen > 0) {
287 switch (PEEK_CHAR(g)) {
300 ADD_CHAR(PEEK_CHAR(g));
304 ADD_CHAR(PEEK_CHAR(g));
312 /* is this a number or a symbol? */
313 /* assume integer to start */
316 /* assume no empty strings? */
318 /* if the first character is '+' or '-' and that's not the only */
319 /* character it can still be an integer */
322 if (g->strbuf[0] == '-' || g->strbuf[0] == '+') {
331 while (is_integer && i < strpos) {
332 if (g->strbuf[i] < '0' || g->strbuf[i] > '9')
338 /* it's an integer */
342 v->value.integer.i = atoi(g->strbuf);
347 !memcmp(g->strbuf, "nil", 3)) {
352 v->value.s.length = strpos;
353 v->value.s.string = (char *) malloc(v->value.s.length);
354 memcpy(v->value.s.string, g->strbuf, v->value.s.length);
361 read_value(Globals *g)
363 while (g->buflen > 0) {
364 switch (PEEK_CHAR(g)) {
371 case '\"': /* begin string */
373 return read_string(g);
384 return read_num_or_symbol(g);
392 read_list(Globals *g)
399 while (g->buflen > 0) {
400 if (NULL == (v = read_value(g))) {
401 switch (PEEK_CHAR(g)) {
404 if (tail != NULL) { /* if no last cdr yet, use nil */
411 case '.': /* set last cdr explicitly */
413 *tail = read_value(g);
415 /* badly formed input ??? */
422 /* badly formed input ??? */
427 else { /* read a value, add it to the list */
429 /* two values after a . in a list. very bad! ??? */
432 *tail = ALLOC_VALUE();
434 (*tail)->value.cons.car = v;
435 tail = &(*tail)->value.cons.cdr;
438 ABORT(g, 23); /* added this -dkindred */
441 void free_value(Value *v)
445 free_value(v->value.cons.car);
446 free_value(v->value.cons.cdr);
450 free(v->value.s.string);
458 void prin(FILE *f, Value *v);
461 prinlis(FILE *f, Value *v, int first)
464 case cons: /* continue printing list */
467 prin(f, v->value.cons.car);
468 prinlis(f, v->value.cons.cdr, 0);
470 case nil: /* last elt in list */
473 default: /* dotted pair */
484 prin(FILE *f, Value *v)
495 /* ??? do quoting of '"' ??? */
497 fwrite(v->value.s.string, 1, v->value.s.length, f);
501 /* ??? do quoting of all whitespace and special chars ??? */
502 fwrite(v->value.s.string, 1, v->value.s.length, f);
505 fprintf(f, "%ld", v->value.integer.i);
513 #define CHECK_TAG(v, t) if (VTAG(v) != (t)) return 0
516 eqv(Value *v1, Value *v2)
531 return (eqv(VCAR(v1), VCAR(v2)) &&
532 eqv(VCDR(v1), VCDR(v2)));
535 CHECK_TAG(v2, string);
536 return (VSLENGTH(v1) == VSLENGTH(v2) &&
537 0 == memcmp(VSDATA(v1), VSDATA(v2), VSLENGTH(v1)));
540 CHECK_TAG(v2, symbol);
541 return (VSLENGTH(v1) == VSLENGTH(v2) &&
542 0 == memcmp(VSDATA(v1), VSDATA(v2), VSLENGTH(v1)));
545 CHECK_TAG(v2, integer);
546 return (VINTEGER(v1) == VINTEGER(v2));
549 if (VVTAG(v1) != any)
550 CHECK_TAG(v2, VVTAG(v1));
554 fprintf(stderr,"eqv(): bad tag: %d\n",(int)(v1->tag));
562 assqv(Value *key, Value *assoc)
567 while (VTAG(assoc) == cons) {
569 if (VTAG(pair) == cons && eqv(VCAR(pair), key)) {
578 destructure(Value *pattern, Value *match)
580 switch (VTAG(pattern)) {
585 CHECK_TAG(match, nil);
589 CHECK_TAG(match, cons);
590 return (destructure(VCAR(pattern), VCAR(match)) &&
591 destructure(VCDR(pattern), VCDR(match)));
594 CHECK_TAG(match, string);
595 return (VSLENGTH(pattern) == VSLENGTH(match) &&
596 0 == memcmp(VSDATA(pattern), VSDATA(match), VSLENGTH(pattern)));
599 CHECK_TAG(match, symbol);
600 return (VSLENGTH(pattern) == VSLENGTH(match) &&
601 0 == memcmp(VSDATA(pattern), VSDATA(match), VSLENGTH(pattern)));
604 CHECK_TAG(match, integer);
605 return (VINTEGER(pattern) == VINTEGER(match));
608 if (VVTAG(pattern) != any)
609 CHECK_TAG(match, VVTAG(pattern));
610 if (VVDATA(pattern) != NULL)
611 *VVDATA(pattern) = (void *) match;
615 fprintf(stderr,"destructure(): bad tag: %d\n",(int)VTAG(pattern));
627 char buf[BUFLEN]; /* this will have to be dynamically expanded */
632 Value *pattern = vmake_cons(vmake_symbol_c("integer"),
633 vmake_var(integer, (void **) &match_data));
636 ret = read(0, buf + bufpos, BUFLEN - bufpos);
649 ret = parse(bufpos, buf, &v);
651 memmove(buf, buf + ret, bufpos - ret);
657 if (destructure(pattern, v)) {
658 printf("match_data = ");
659 prin(stdout, match_data);
663 printf("destructure failed\n");
675 main(int argc, char *argv[])
683 v->value.cons.car = ALLOC_VALUE();
684 v->value.cons.car->tag = symbol;
685 v->value.cons.car->value.s.length = 6;
686 v->value.cons.car->value.s.string = "symbol";
688 v->value.cons.cdr = ALLOC_VALUE();
689 v->value.cons.cdr->tag = cons;
691 v->value.cons.cdr->value.cons.car = ALLOC_VALUE();
692 v->value.cons.cdr->value.cons.car->tag = string;
693 v->value.cons.cdr->value.cons.car->value.s.length = 6;
694 v->value.cons.cdr->value.cons.car->value.s.string = "string";
696 v->value.cons.cdr->value.cons.cdr = ALLOC_VALUE();
697 v->value.cons.cdr->value.cons.cdr->tag = integer;
698 v->value.cons.cdr->value.cons.cdr->value.integer.i = 23;