X-Git-Url: http://git.mutantstargoat.com/user/nuclear/?p=dosdemo;a=blobdiff_plain;f=src%2Fts_text.c;fp=src%2Fts_text.c;h=347c28cd0c5dafbd75a86633feeee0429f8d18fb;hp=0000000000000000000000000000000000000000;hb=ae96c724e905181ca10441a63b1dfe9c2fc5d2ea;hpb=091586fb9501d613f621fceb70fa21ff97c4898e diff --git a/src/ts_text.c b/src/ts_text.c new file mode 100644 index 0000000..347c28c --- /dev/null +++ b/src/ts_text.c @@ -0,0 +1,361 @@ +#include +#include +#include +#include +#include "treestor.h" +#include "dynarr.h" + +struct parser { + FILE *fp; + int nline; + char *token; +}; + +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 void print_attr(struct ts_attr *attr, FILE *fp, int level); +static void print_value(struct ts_value *value, FILE *fp); +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(FILE *fp) +{ + char *root_name; + struct parser pstate, *pst = &pstate; + struct ts_node *node = 0; + + pstate.fp = fp; + pstate.nline = 0; + if(!(pstate.token = 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"); + dynarr_free(pst->token); + return 0; + } + EXPECT_SYM('{'); + if(!(node = read_node(pst))) { + dynarr_free(pst->token); + return 0; + } + node->name = root_name; + +err: + 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; itoken); + + // skip whitespace + while((c = fgetc(pst->fp)) != -1) { + if(c == '#') { // skip to end of line + while((c = fgetc(pst->fp)) != -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 = fgetc(pst->fp)) != -1 && + (isdigit(c) || (c == '.' && !found_dot))) { + DYNARR_STRPUSH(pst->token, c); + if(c == '.') found_dot = 1; + } + if(c != -1) ungetc(c, pst->fp); + return TOK_NUM; + } + if(isalpha(c)) { + // token is an identifier + while((c = fgetc(pst->fp)) != -1 && (isalnum(c) || c == '_')) { + DYNARR_STRPUSH(pst->token, c); + } + if(c != -1) ungetc(c, pst->fp); + return TOK_ID; + } + if(c == '"') { + // token is a string constant + // remove the opening quote + DYNARR_STRPOP(pst->token); + while((c = fgetc(pst->fp)) != -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, FILE *fp) +{ + struct ts_node *c; + struct ts_attr *attr; + int lvl = tree_level(tree); + + fprintf(fp, "%s%s {\n", indent(lvl), tree->name); + + attr = tree->attr_list; + while(attr) { + print_attr(attr, fp, lvl); + attr = attr->next; + } + + c = tree->child_list; + while(c) { + ts_text_save(c, fp); + c = c->next; + } + + fprintf(fp, "%s}\n", indent(lvl)); + return 0; +} + +static void print_attr(struct ts_attr *attr, FILE *fp, int level) +{ + fprintf(fp, "%s%s = ", indent(level + 1), attr->name); + print_value(&attr->val, fp); + fputc('\n', fp); +} + +static void print_value(struct ts_value *value, FILE *fp) +{ + int i; + + switch(value->type) { + case TS_NUMBER: + fprintf(fp, "%g", value->fnum); + break; + + case TS_VECTOR: + fputc('[', fp); + for(i=0; ivec_size; i++) { + if(i == 0) { + fprintf(fp, "%g", value->vec[i]); + } else { + fprintf(fp, ", %g", value->vec[i]); + } + } + fputc(']', fp); + break; + + case TS_ARRAY: + fputc('[', fp); + for(i=0; iarray_size; i++) { + if(i > 0) { + fprintf(fp, ", "); + } + print_value(value->array + i, fp); + } + fputc(']', fp); + break; + + default: + fprintf(fp, "\"%s\"", value->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"; +}