X-Git-Url: http://git.mutantstargoat.com/user/nuclear/?p=demo_prior;a=blobdiff_plain;f=libs%2Ftreestore%2Fsrc%2Ftext.c;fp=libs%2Ftreestore%2Fsrc%2Ftext.c;h=b7433124f372d582da537cbc0e55b8dfebba7ab5;hp=0000000000000000000000000000000000000000;hb=44a7a61d2bec54ed741930572e63e5015326daca;hpb=ec776ad8bf37d25b9308e2c770d66247135b46ea diff --git a/libs/treestore/src/text.c b/libs/treestore/src/text.c new file mode 100644 index 0000000..b743312 --- /dev/null +++ b/libs/treestore/src/text.c @@ -0,0 +1,465 @@ +#include +#include +#include +#include +#include +#include "treestore.h" +#include "dynarr.h" + +struct parser { + struct ts_io *io; + int nline; + char *token; + int nextc; +}; + +enum { TOK_SYM, TOK_ID, TOK_NUM, TOK_STR }; + +static struct ts_node *read_node(struct parser *pstate); +static int read_array(struct parser *pstate, struct ts_value *tsv, char endsym); +static int next_token(struct parser *pstate); + +static int print_attr(struct ts_attr *attr, struct ts_io *io, int level); +static char *value_to_str(struct ts_value *value); +static int tree_level(struct ts_node *n); +static const char *indent(int x); +static const char *toktypestr(int type); + +#define EXPECT(type) \ + do { \ + if(next_token(pst) != (type)) { \ + fprintf(stderr, "expected %s token\n", toktypestr(type)); \ + goto err; \ + } \ + } while(0) + +#define EXPECT_SYM(c) \ + do { \ + if(next_token(pst) != TOK_SYM || pst->token[0] != (c)) { \ + fprintf(stderr, "expected symbol: %c\n", c); \ + goto err; \ + } \ + } while(0) + + +struct ts_node *ts_text_load(struct ts_io *io) +{ + char *root_name; + struct parser pstate, *pst = &pstate; + struct ts_node *node = 0; + + pstate.io = io; + pstate.nline = 0; + pstate.nextc = -1; + if(!(pstate.token = ts_dynarr_alloc(0, 1))) { + perror("failed to allocate token string"); + return 0; + } + + EXPECT(TOK_ID); + if(!(root_name = strdup(pst->token))) { + perror("failed to allocate root node name"); + ts_dynarr_free(pst->token); + return 0; + } + EXPECT_SYM('{'); + if(!(node = read_node(pst))) { + ts_dynarr_free(pst->token); + return 0; + } + node->name = root_name; + +err: + ts_dynarr_free(pst->token); + return node; +} + +static int read_value(struct parser *pst, int toktype, struct ts_value *val) +{ + switch(toktype) { + case TOK_NUM: + ts_set_valuef(val, atof(pst->token)); + break; + + case TOK_SYM: + if(pst->token[0] == '[' || pst->token[0] == '{') { + char endsym = pst->token[0] + 2; /* end symbol is dist 2 from either '[' or '{' */ + if(read_array(pst, val, endsym) == -1) { + return -1; + } + } else { + fprintf(stderr, "read_node: unexpected rhs symbol: %c\n", pst->token[0]); + } + break; + + case TOK_ID: + case TOK_STR: + default: + ts_set_value_str(val, pst->token); + } + + return 0; +} + +static struct ts_node *read_node(struct parser *pst) +{ + int type; + struct ts_node *node; + + if(!(node = ts_alloc_node())) { + perror("failed to allocate treestore node"); + return 0; + } + + while((type = next_token(pst)) == TOK_ID) { + char *id; + + if(!(id = strdup(pst->token))) { + goto err; + } + + EXPECT(TOK_SYM); + + if(pst->token[0] == '=') { + /* attribute */ + struct ts_attr *attr; + int type; + + if(!(attr = ts_alloc_attr())) { + goto err; + } + + if((type = next_token(pst)) == -1) { + ts_free_attr(attr); + fprintf(stderr, "read_node: unexpected EOF\n"); + goto err; + } + + if(read_value(pst, type, &attr->val) == -1) { + ts_free_attr(attr); + fprintf(stderr, "failed to read value\n"); + goto err; + } + attr->name = id; + ts_add_attr(node, attr); + + } else if(pst->token[0] == '{') { + /* child */ + struct ts_node *child; + + if(!(child = read_node(pst))) { + ts_free_node(node); + return 0; + } + + child->name = id; + ts_add_child(node, child); + + } else { + fprintf(stderr, "unexpected token: %s\n", pst->token); + goto err; + } + } + + if(type != TOK_SYM || pst->token[0] != '}') { + fprintf(stderr, "expected closing brace\n"); + goto err; + } + return node; + +err: + fprintf(stderr, "treestore read_node failed\n"); + ts_free_node(node); + return 0; +} + +static int read_array(struct parser *pst, struct ts_value *tsv, char endsym) +{ + int type; + struct ts_value values[32]; + int i, nval = 0; + int res; + + while((type = next_token(pst)) != -1) { + ts_init_value(values + nval); + if(read_value(pst, type, values + nval) == -1) { + return -1; + } + if(nval < 31) { + ++nval; + } else { + ts_destroy_value(values + nval); + } + + type = next_token(pst); + if(!(type == TOK_SYM && (pst->token[0] == ',' || pst->token[0] == endsym))) { + fprintf(stderr, "read_array: expected comma or end symbol ('%c')\n", endsym); + return -1; + } + if(pst->token[0] == endsym) { + break; /* we're done */ + } + } + + if(!nval) { + return -1; + } + + res = ts_set_value_arr(tsv, nval, values); + + for(i=0; inextc >= 0) { + c = pst->nextc; + pst->nextc = -1; + } else { + if(pst->io->read(&c, 1, pst->io->data) < 1) { + return -1; + } + } + return c; +} + +static void ungetchar(char c, struct parser *pst) +{ + assert(pst->nextc == -1); + pst->nextc = c; +} + +static int next_token(struct parser *pst) +{ + int c; + + DYNARR_CLEAR(pst->token); + + /* skip whitespace */ + while((c = nextchar(pst)) != -1) { + if(c == '#') { /* skip to end of line */ + while((c = nextchar(pst)) != -1 && c != '\n'); + if(c == -1) return -1; + } + if(!isspace(c)) break; + if(c == '\n') ++pst->nline; + } + if(c == -1) return -1; + + DYNARR_STRPUSH(pst->token, c); + + if(isdigit(c) || c == '-' || c == '+') { + /* token is a number */ + int found_dot = 0; + while((c = nextchar(pst)) != -1 && + (isdigit(c) || (c == '.' && !found_dot))) { + DYNARR_STRPUSH(pst->token, c); + if(c == '.') found_dot = 1; + } + if(c != -1) ungetchar(c, pst); + return TOK_NUM; + } + if(isalpha(c)) { + /* token is an identifier */ + while((c = nextchar(pst)) != -1 && (isalnum(c) || c == '_')) { + DYNARR_STRPUSH(pst->token, c); + } + if(c != -1) ungetchar(c, pst); + return TOK_ID; + } + if(c == '"') { + /* token is a string constant, remove the opening quote */ + DYNARR_STRPOP(pst->token); + while((c = nextchar(pst)) != -1 && c != '"') { + DYNARR_STRPUSH(pst->token, c); + if(c == '\n') ++pst->nline; + } + if(c != '"') { + return -1; + } + return TOK_STR; + } + return TOK_SYM; +} + +int ts_text_save(struct ts_node *tree, struct ts_io *io) +{ + char *buf; + struct ts_node *c; + struct ts_attr *attr; + int lvl = tree_level(tree); + int sz, inline_attr, res = -1; + + if(!(buf = malloc(lvl + strlen(tree->name) + 4))) { + perror("ts_text_save failed to allocate buffer"); + goto end; + } + + if(tree->child_list || (tree->attr_list && tree->attr_list->next)) { + inline_attr = 0; + } else { + inline_attr = 1; + } + + sz = sprintf(buf, "%s%s {", indent(lvl), tree->name); + if(!inline_attr) { + strcat(buf, "\n"); + sz++; + } + if(io->write(buf, sz, io->data) < sz) { + goto end; + } + + attr = tree->attr_list; + while(attr) { + if(print_attr(attr, io, inline_attr ? -1 : lvl) == -1) { + goto end; + } + attr = attr->next; + } + + c = tree->child_list; + while(c) { + if(ts_text_save(c, io) == -1) { + goto end; + } + c = c->next; + } + + if(inline_attr) { + sz = sprintf(buf, "}\n"); + } else { + sz = sprintf(buf, "%s}\n", indent(lvl)); + } + if(io->write(buf, sz, io->data) < sz) { + goto end; + } + res = 0; +end: + free(buf); + return res; +} + +static int print_attr(struct ts_attr *attr, struct ts_io *io, int level) +{ + char *buf, *val; + int sz; + + if(!(val = value_to_str(&attr->val))) { + return -1; + } + + sz = (level >= 0 ? level : 0) + strlen(attr->name) + ts_dynarr_size(val) + 5; + if(!(buf = malloc(sz))) { + perror("print_attr: failed to allocate name buffer"); + ts_dynarr_free(val); + } + + if(level >= 0) { + sz = sprintf(buf, "%s%s = %s\n", indent(level + 1), attr->name, val); + } else { + sz = sprintf(buf, " %s = %s ", attr->name, val); + } + if(io->write(buf, sz, io->data) < sz) { + ts_dynarr_free(val); + free(buf); + return -1; + } + ts_dynarr_free(val); + free(buf); + return 0; +} + +static char *append_dynstr(char *dest, char *s) +{ + while(*s) { + DYNARR_STRPUSH(dest, *s++); + } + return dest; +} + +static char *value_to_str(struct ts_value *value) +{ + int i; + char buf[128]; + char *str, *valstr; + + if(!(str = ts_dynarr_alloc(0, 1))) { + return 0; + } + + switch(value->type) { + case TS_NUMBER: + sprintf(buf, "%g", value->fnum); + str = append_dynstr(str, buf); + break; + + case TS_VECTOR: + DYNARR_STRPUSH(str, '['); + for(i=0; ivec_size; i++) { + if(i == 0) { + sprintf(buf, "%g", value->vec[i]); + } else { + sprintf(buf, ", %g", value->vec[i]); + } + str = append_dynstr(str, buf); + } + DYNARR_STRPUSH(str, ']'); + break; + + case TS_ARRAY: + DYNARR_STRPUSH(str, '['); + for(i=0; iarray_size; i++) { + if(i > 0) { + str = append_dynstr(str, ", "); + } + if(!(valstr = value_to_str(value->array + i))) { + ts_dynarr_free(str); + return 0; + } + str = append_dynstr(str, valstr); + ts_dynarr_free(valstr); + } + DYNARR_STRPUSH(str, ']'); + break; + + default: + sprintf(buf, "\"%s\"", value->str); + str = append_dynstr(str, buf); + } + + return str; +} + +static int tree_level(struct ts_node *n) +{ + if(!n->parent) return 0; + return tree_level(n->parent) + 1; +} + +static const char *indent(int x) +{ + static const char buf[] = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"; + const char *end = buf + sizeof buf - 1; + return x > sizeof buf - 1 ? buf : end - x; +} + +static const char *toktypestr(int type) +{ + switch(type) { + case TOK_ID: + return "identifier"; + case TOK_NUM: + return "number"; + case TOK_STR: + return "string"; + case TOK_SYM: + return "symbol"; + } + return "unknown"; +}