]> andersk Git - zcommit.git/blame - src/zsend-0.0.1/lread.c
Tweak the Zephyr formatting.
[zcommit.git] / src / zsend-0.0.1 / lread.c
CommitLineData
a0223729
GB
1/*
2 lread.c: simple sexp-like data structures in C.
3 useful for communication between emacs and C client programs
4
5 Copyright (C) 1992 Nick Thompson (nix@cs.cmu.edu)
6
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.
11
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.
16
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.
20
21 TODO
22
23 add tag checking on CAR, CDR, etc?
24 */
25
26#include <setjmp.h>
27
28#include "lread.h"
29#include <stdio.h>
30#include <string.h> /* for strlen() */
31
32Value *
33vmake_cons(Value *car, Value *cdr)
34{
35 Value *v = ALLOC_VALUE();
36 v->tag = cons;
37 VCAR(v) = car;
38 VCDR(v) = cdr;
39 return v;
40}
41
42Value *
43vmake_symbol(int length, char *data)
44{
45 Value *v = ALLOC_VALUE();
46 v->tag = symbol;
47 VSLENGTH(v) = length;
48 VSDATA(v) = data;
49 return v;
50}
51
52Value *
53vmake_symbol_c(char *s)
54{
55 Value *v = ALLOC_VALUE();
56 v->tag = symbol;
57 VSLENGTH(v) = strlen(s);
58 VSDATA(v) = s;
59 return v;
60}
61
62Value *
63vmake_string(int length, char *data)
64{
65 Value *v = ALLOC_VALUE();
66 v->tag = string;
67 VSLENGTH(v) = length;
68 VSDATA(v) = data;
69 return v;
70}
71
72Value *
73vmake_string_c(char *s)
74{
75 Value *v = ALLOC_VALUE();
76 v->tag = string;
77 VSLENGTH(v) = strlen(s);
78 VSDATA(v) = s;
79 return v;
80}
81
82char *
83vextract_string_c(Value *v)
84{
85 char *s = (char *) malloc(VSLENGTH(v) + 1);
86 memcpy(s, VSDATA(v), VSLENGTH(v));
87 s[VSLENGTH(v)] = '\0';
88 return s;
89}
90
91Value *
92vmake_integer(int n)
93{
94 Value *v = ALLOC_VALUE();
95 v->tag = integer;
96 VINTEGER(v) = n;
97 return v;
98}
99
100Value *
101vmake_var(enum Vtag tag, void **value)
102{
103 Value *v = ALLOC_VALUE();
104 v->tag = var;
105 VVTAG(v) = tag;
106 VVDATA(v) = value;
107 return v;
108}
109
110int
111vlength(Value *l)
112{
113 int i;
114 for (i=0; VTAG(l) == cons; i++, l = VCDR(l))
115 ;
116 return i;
117}
118
119typedef struct {
120 jmp_buf abort; /* nonlocal exit for abort */
121
122 char *input_string; /* input string */
123 int buflen; /* amount left in input string */
124 char *buf; /* pointer into input */
125
126 int strbuflen; /* length of scratch buffer */
127 char *strbuf; /* scratch buffer for building strings */
128} Globals;
129
130Value *read_value(Globals *g);
131Value *read_list(Globals *g);
132
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))
138
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 */
144
145void
146expand_strbuf(Globals *g)
147{
148 if (g->strbuflen == 0) {
149 g->strbuflen = 128;
150 g->strbuf = (char *) malloc(g->strbuflen);
151 }
152 else {
153 int newbuflen = 3 * g->strbuflen / 2;
154 char *newbuf = (char *) malloc(newbuflen);
155 memcpy(newbuf, g->strbuf, g->strbuflen);
156 free(g->strbuf);
157 g->strbuf = newbuf;
158 g->strbuflen = newbuflen;
159 }
160}
161
162int parse(int slen, char *s, Value **v)
163{
164 Globals g;
165 int jmpret;
166
167 if (0 == (jmpret = setjmp(g.abort))) { /* successful parse */
168 g.input_string = s;
169 g.buflen = slen;
170 g.buf = g.input_string;
171 g.strbuflen = 0;
172 g.strbuf = NULL;
173 expand_strbuf(&g);
174 *v = read_value(&g);
175 return g.buf - g.input_string;
176 }
177 else { /* return from nonlocal abort */
178 free(g.strbuf);
179 *v = NULL;
180 return 0;
181 }
182}
183
184int
185read_escape(Globals *g, char *c)
186{
187 int valid = 1;
188 int nc = PEEK_CHAR(g);
189
190 switch (nc) {
191 case '\n':
192 valid = 0;
193 NEXT_CHAR(g);
194 break;
195 case 'n':
196 *c = '\n';
197 NEXT_CHAR(g);
198 break;
199 case 't':
200 *c = '\t';
201 NEXT_CHAR(g);
202 break;
203 default:
204 if (nc >= '0' && nc <= '7') {
205 int digits;
206 /* handle octal \nnn notation */
207 *c = 0;
208 for (digits = 0; digits < 3; digits++) {
209 if (nc < '0' || nc > '7')
210 break;
211 *c = (*c * 8) + (nc - '0');
212 NEXT_CHAR(g);
213 nc = PEEK_CHAR(g);
214 }
215 } else {
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). */
219 *c = nc;
220 NEXT_CHAR(g);
221 }
222 break;
223 }
224 return valid;
225}
226
227Value *
228read_string(Globals *g)
229{
230 int strpos = 0;
231 Value *v;
232 char c;
233
234#define ADD_CHAR(c) \
235 if (strpos >= g->strbuflen) \
236 expand_strbuf(g); \
237 g->strbuf[strpos++] = (c)
238
239 while (1) {
240 switch (PEEK_CHAR(g)) {
241 case '\"':
242 NEXT_CHAR(g);
243 v = ALLOC_VALUE();
244 v->tag = string;
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);
248 return v;
249 break;
250 case '\\':
251 NEXT_CHAR(g);
252 if (read_escape(g, &c))
253 ADD_CHAR(c);
254 break;
255 default:
256 ADD_CHAR(PEEK_CHAR(g));
257 NEXT_CHAR(g);
258 break;
259 }
260 }
261}
262
263/* characters
264(
265)
266"
267\
268<white>
269<character>
270<number>
271 */
272
273Value *
274read_num_or_symbol(Globals *g)
275{
276 Value *v;
277 int strpos = 0;
278 int i;
279 int is_integer;
280
281#define ADD_CHAR(c) \
282 if (strpos >= g->strbuflen) \
283 expand_strbuf(g); \
284 g->strbuf[strpos++] = (c)
285
286 while (g->buflen > 0) {
287 switch (PEEK_CHAR(g)) {
288 case ' ':
289 case '\t':
290 case '\n':
291 case '\0':
292 case '\"':
293 case '(':
294 case ')':
295 case '.':
296 goto done;
297 break;
298 case '\\':
299 NEXT_CHAR(g);
300 ADD_CHAR(PEEK_CHAR(g));
301 NEXT_CHAR(g);
302 break;
303 default:
304 ADD_CHAR(PEEK_CHAR(g));
305 NEXT_CHAR(g);
306 break;
307 }
308 }
309 ABORT(g, 23);
310
311 done:
312 /* is this a number or a symbol? */
313 /* assume integer to start */
314 is_integer = 1;
315
316 /* assume no empty strings? */
317
318 /* if the first character is '+' or '-' and that's not the only */
319 /* character it can still be an integer */
320 i = 0;
321 if (strpos > 0) {
322 if (g->strbuf[0] == '-' || g->strbuf[0] == '+') {
323 if (strpos > 1) {
324 i = 1;
325 } else {
326 is_integer = 0;
327 }
328 }
329 }
330
331 while (is_integer && i < strpos) {
332 if (g->strbuf[i] < '0' || g->strbuf[i] > '9')
333 is_integer = 0;
334 i++;
335 }
336
337 if (is_integer) {
338 /* it's an integer */
339 v = ALLOC_VALUE();
340 v->tag = integer;
341 ADD_CHAR('\0');
342 v->value.integer.i = atoi(g->strbuf);
343 }
344 else {
345 /* it's a symbol */
346 if (3 == strpos &&
347 !memcmp(g->strbuf, "nil", 3)) {
348 v = NULL;
349 } else {
350 v = ALLOC_VALUE();
351 v->tag = symbol;
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);
355 }
356 }
357 return v;
358}
359
360Value *
361read_value(Globals *g)
362{
363 while (g->buflen > 0) {
364 switch (PEEK_CHAR(g)) {
365 case ' ':
366 case '\t':
367 case '\n':
368 case '\0':
369 NEXT_CHAR(g);
370 break;
371 case '\"': /* begin string */
372 NEXT_CHAR(g);
373 return read_string(g);
374 break;
375 case '(':
376 NEXT_CHAR(g);
377 return read_list(g);
378 break;
379 case ')':
380 case '.':
381 return NULL;
382 break;
383 default:
384 return read_num_or_symbol(g);
385 break;
386 }
387 }
388 ABORT(g, 23);
389}
390
391Value *
392read_list(Globals *g)
393{
394 Value *list;
395 Value **tail;
396 Value *v;
397
398 tail = &list;
399 while (g->buflen > 0) {
400 if (NULL == (v = read_value(g))) {
401 switch (PEEK_CHAR(g)) {
402
403 case ')':
404 if (tail != NULL) { /* if no last cdr yet, use nil */
405 *tail = NULL;
406 }
407 NEXT_CHAR(g);
408 return list;
409 break;
410
411 case '.': /* set last cdr explicitly */
412 NEXT_CHAR(g);
413 *tail = read_value(g);
414 if (*tail == NULL) {
415 /* badly formed input ??? */
416 ABORT(g, 13);
417 }
418 tail = NULL;
419 break;
420
421 default:
422 /* badly formed input ??? */
423 ABORT(g, 13);
424 break;
425 }
426 }
427 else { /* read a value, add it to the list */
428 if (NULL == tail) {
429 /* two values after a . in a list. very bad! ??? */
430 ABORT(g, 13);
431 }
432 *tail = ALLOC_VALUE();
433 (*tail)->tag = cons;
434 (*tail)->value.cons.car = v;
435 tail = &(*tail)->value.cons.cdr;
436 }
437 }
438 ABORT(g, 23); /* added this -dkindred */
439}
440
441void free_value(Value *v)
442{
443 switch(VTAG(v)) {
444 case cons:
445 free_value(v->value.cons.car);
446 free_value(v->value.cons.cdr);
447 break;
448 case string:
449 case symbol:
450 free(v->value.s.string);
451 break;
452 default:
453 break;
454 }
455 free(v);
456}
457
458void prin(FILE *f, Value *v);
459
460void
461prinlis(FILE *f, Value *v, int first)
462{
463 switch(VTAG(v)) {
464 case cons: /* continue printing list */
465 if (! first)
466 putc(' ', f);
467 prin(f, v->value.cons.car);
468 prinlis(f, v->value.cons.cdr, 0);
469 break;
470 case nil: /* last elt in list */
471 putc(')', f);
472 break;
473 default: /* dotted pair */
474 putc(' ', f);
475 putc('.', f);
476 putc(' ', f);
477 prin(f, v);
478 putc(')', f);
479 break;
480 }
481}
482
483void
484prin(FILE *f, Value *v)
485{
486 switch (VTAG(v)) {
487 case nil:
488 fputs("\'()", f);
489 break;
490 case cons:
491 putc('(', f);
492 prinlis(f, v, 1);
493 break;
494 case string:
495 /* ??? do quoting of '"' ??? */
496 putc('\"', f);
497 fwrite(v->value.s.string, 1, v->value.s.length, f);
498 putc('\"', f);
499 break;
500 case symbol:
501 /* ??? do quoting of all whitespace and special chars ??? */
502 fwrite(v->value.s.string, 1, v->value.s.length, f);
503 break;
504 case integer:
505 fprintf(f, "%ld", v->value.integer.i);
506 break;
507 default:
508 fputs("#<huh?>", f);
509 break;
510 }
511}
512
513#define CHECK_TAG(v, t) if (VTAG(v) != (t)) return 0
514
515int
516eqv(Value *v1, Value *v2)
517{
518
519 switch (v1->tag) {
520/*
521 case any:
522 return 1;
523 break;
524 */
525 case nil:
526 CHECK_TAG(v2, nil);
527 return 1;
528 break;
529 case cons:
530 CHECK_TAG(v2, cons);
531 return (eqv(VCAR(v1), VCAR(v2)) &&
532 eqv(VCDR(v1), VCDR(v2)));
533 break;
534 case string:
535 CHECK_TAG(v2, string);
536 return (VSLENGTH(v1) == VSLENGTH(v2) &&
537 0 == memcmp(VSDATA(v1), VSDATA(v2), VSLENGTH(v1)));
538 break;
539 case symbol:
540 CHECK_TAG(v2, symbol);
541 return (VSLENGTH(v1) == VSLENGTH(v2) &&
542 0 == memcmp(VSDATA(v1), VSDATA(v2), VSLENGTH(v1)));
543 break;
544 case integer:
545 CHECK_TAG(v2, integer);
546 return (VINTEGER(v1) == VINTEGER(v2));
547 break;
548 case var:
549 if (VVTAG(v1) != any)
550 CHECK_TAG(v2, VVTAG(v1));
551 return 1;
552 break;
553 default:
554 fprintf(stderr,"eqv(): bad tag: %d\n",(int)(v1->tag));
555 /* die? */
556 return 0;
557 break;
558 }
559}
560
561Value *
562assqv(Value *key, Value *assoc)
563{
564 Value *pair;
565
566 /* cdr on through */
567 while (VTAG(assoc) == cons) {
568 pair = VCAR(assoc);
569 if (VTAG(pair) == cons && eqv(VCAR(pair), key)) {
570 return pair;
571 }
572 assoc = VCDR(assoc);
573 }
574 return NULL;
575}
576
577int
578destructure(Value *pattern, Value *match)
579{
580 switch (VTAG(pattern)) {
581 case any:
582 return 1;
583 break;
584 case nil:
585 CHECK_TAG(match, nil);
586 return 1;
587 break;
588 case cons:
589 CHECK_TAG(match, cons);
590 return (destructure(VCAR(pattern), VCAR(match)) &&
591 destructure(VCDR(pattern), VCDR(match)));
592 break;
593 case string:
594 CHECK_TAG(match, string);
595 return (VSLENGTH(pattern) == VSLENGTH(match) &&
596 0 == memcmp(VSDATA(pattern), VSDATA(match), VSLENGTH(pattern)));
597 break;
598 case symbol:
599 CHECK_TAG(match, symbol);
600 return (VSLENGTH(pattern) == VSLENGTH(match) &&
601 0 == memcmp(VSDATA(pattern), VSDATA(match), VSLENGTH(pattern)));
602 break;
603 case integer:
604 CHECK_TAG(match, integer);
605 return (VINTEGER(pattern) == VINTEGER(match));
606 break;
607 case var:
608 if (VVTAG(pattern) != any)
609 CHECK_TAG(match, VVTAG(pattern));
610 if (VVDATA(pattern) != NULL)
611 *VVDATA(pattern) = (void *) match;
612 return 1;
613 break;
614 default:
615 fprintf(stderr,"destructure(): bad tag: %d\n",(int)VTAG(pattern));
616 /* die? */
617 return 0;
618 break;
619 }
620}
621
622#ifdef TEST
623
624read_and_parse()
625{
626#define BUFLEN 512
627 char buf[BUFLEN]; /* this will have to be dynamically expanded */
628 int bufpos = 0;
629 int ret;
630 Value *v = NULL;
631 Value *match_data;
632 Value *pattern = vmake_cons(vmake_symbol_c("integer"),
633 vmake_var(integer, (void **) &match_data));
634
635 while (1) {
636 ret = read(0, buf + bufpos, BUFLEN - bufpos);
637 if (ret < 0) {
638 perror("read");
639 exit(1);
640 }
641 else {
642 bufpos += ret;
643
644 do {
645 if (v != NULL) {
646 free_value(v);
647 v = NULL;
648 }
649 ret = parse(bufpos, buf, &v);
650 if (ret > 0) {
651 memmove(buf, buf + ret, bufpos - ret);
652 bufpos -= ret;
653 printf("parsed: ");
654 prin(stdout, v);
655 fputc('\n', stdout);
656
657 if (destructure(pattern, v)) {
658 printf("match_data = ");
659 prin(stdout, match_data);
660 fputc('\n', stdout);
661 }
662 else {
663 printf("destructure failed\n");
664 }
665
666 free_value(v);
667 }
668 else
669 printf("EOF\n");
670 } while (ret > 0);
671 }
672 }
673}
674
675main(int argc, char *argv[])
676{
677 read_and_parse();
678#if 0
679 Value *v;
680 v = ALLOC_VALUE();
681
682 v->tag = cons;
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";
687
688 v->value.cons.cdr = ALLOC_VALUE();
689 v->value.cons.cdr->tag = cons;
690
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";
695
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;
699 prin(stdout, v);
700 fputc('\n', stdout);
701#endif
702}
703#endif
This page took 0.17785 seconds and 5 git commands to generate.