RosettaCodeData/Task/S-expressions/C/s-expressions-1.c

227 lines
3.6 KiB
C

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
enum { S_NONE, S_LIST, S_STRING, S_SYMBOL };
typedef struct {
int type;
size_t len;
void *buf;
} s_expr, *expr;
void whine(const char *s)
{
fprintf(stderr, "parse error before ==>%.10s\n", s);
}
expr parse_string(const char *s, char **e)
{
expr ex = calloc(sizeof(s_expr), 1);
char buf[256] = {0};
int i = 0;
while (*s) {
if (i >= 256) {
fprintf(stderr, "string too long:\n");
whine(s);
goto fail;
}
switch (*s) {
case '\\':
switch (*++s) {
case '\\':
case '"': buf[i++] = *s++;
continue;
default: whine(s);
goto fail;
}
case '"': goto success;
default: buf[i++] = *s++;
}
}
fail:
free(ex);
return 0;
success:
*(const char **)e = s + 1;
ex->type = S_STRING;
ex->buf = strdup(buf);
ex->len = strlen(buf);
return ex;
}
expr parse_symbol(const char *s, char **e)
{
expr ex = calloc(sizeof(s_expr), 1);
char buf[256] = {0};
int i = 0;
while (*s) {
if (i >= 256) {
fprintf(stderr, "symbol too long:\n");
whine(s);
goto fail;
}
if (isspace(*s)) goto success;
if (*s == ')' || *s == '(') {
s--;
goto success;
}
switch (*s) {
case '\\':
switch (*++s) {
case '\\': case '"': case '(': case ')':
buf[i++] = *s++;
continue;
default: whine(s);
goto fail;
}
case '"': whine(s);
goto success;
default: buf[i++] = *s++;
}
}
fail:
free(ex);
return 0;
success:
*(const char **)e = s + 1;
ex->type = S_SYMBOL;
ex->buf = strdup(buf);
ex->len = strlen(buf);
return ex;
}
void append(expr list, expr ele)
{
list->buf = realloc(list->buf, sizeof(expr) * ++list->len);
((expr*)(list->buf))[list->len - 1] = ele;
}
expr parse_list(const char *s, char **e)
{
expr ex = calloc(sizeof(s_expr), 1), chld;
char *next;
ex->len = 0;
while (*s) {
if (isspace(*s)) {
s++;
continue;
}
switch (*s) {
case '"':
chld = parse_string(s+1, &next);
if (!chld) goto fail;
append(ex, chld);
s = next;
continue;
case '(':
chld = parse_list(s+1, &next);
if (!chld) goto fail;
append(ex, chld);
s = next;
continue;
case ')':
goto success;
default:
chld = parse_symbol(s, &next);
if (!chld) goto fail;
append(ex, chld);
s = next;
continue;
}
}
fail:
whine(s);
free(ex);
return 0;
success:
*(const char **)e = s+1;
ex->type = S_LIST;
return ex;
}
expr parse_term(const char *s, char **e)
{
while (*s) {
if (isspace(*s)) {
s++;
continue;
}
switch(*s) {
case '(':
return parse_list(s+1, e);
case '"':
return parse_string(s+1, e);
default:
return parse_symbol(s+1, e);
}
}
return 0;
}
void print_expr(expr e, int depth)
{
#define sep() for(i = 0; i < depth; i++) printf(" ")
int i;
if (!e) return;
switch(e->type) {
case S_LIST:
sep();
puts("(");
for (i = 0; i < e->len; i++)
print_expr(((expr*)e->buf)[i], depth + 1);
sep();
puts(")");
return;
case S_SYMBOL:
case S_STRING:
sep();
if (e->type == S_STRING) putchar('"');
for (i = 0; i < e->len; i++) {
switch(((char*)e->buf)[i]) {
case '"':
case '\\':
putchar('\\');
break;
case ')': case '(':
if (e->type == S_SYMBOL)
putchar('\\');
}
putchar(((char*)e->buf)[i]);
}
if (e->type == S_STRING) putchar('"');
putchar('\n');
return;
}
}
int main()
{
char *next;
const char *in = "((data da\\(\\)ta \"quot\\\\ed data\" 123 4.5)\n"
" (\"data\" (!@# (4.5) \"(mo\\\"re\" \"data)\")))";
expr x = parse_term(in, &next);
printf("input is:\n%s\n", in);
printf("parsed as:\n");
print_expr(x, 0);
return 0;
}