# HG changeset patch # User John Tsiombikas # Date 1479059161 -7200 # Node ID bb873449cf5963e93e57f8a9f29dccef0e1be822 # Parent 48d75df3ef04b06bf67dccfad21d5666701154c8 fixed ts_set_value and added convenience value-get functions diff -r 48d75df3ef04 -r bb873449cf59 CMakeLists.txt --- a/CMakeLists.txt Thu Nov 10 16:19:44 2016 +0200 +++ b/CMakeLists.txt Sun Nov 13 19:46:01 2016 +0200 @@ -23,6 +23,9 @@ target_include_directories(treestore PUBLIC include) target_include_directories(treestore-static PUBLIC include) +target_link_libraries(treestore -lexpat) +target_link_libraries(treestore-static -lexpat) + install(TARGETS treestore RUNTIME DESTINATION bin LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} diff -r 48d75df3ef04 -r bb873449cf59 include/treestore.h --- a/include/treestore.h Thu Nov 10 16:19:44 2016 +0200 +++ b/include/treestore.h Sun Nov 13 19:46:01 2016 +0200 @@ -4,18 +4,21 @@ #include #ifdef __cplusplus +#define TS_DEFVAL(x) =(x) extern "C" { +#else +#define TS_DEFVAL(x) #endif -enum ts_value_type { TS_UNKNOWN, TS_NUMBER, TS_VECTOR, TS_ARRAY }; +enum ts_value_type { TS_STRING, TS_NUMBER, TS_VECTOR, TS_ARRAY }; /** treestore node attribute value */ struct ts_value { enum ts_value_type type; - char *str; /**< all values have a string representation */ - int inum; /**< numeric values TS_NUMBER will have this set */ - float fnum; /**< numeric values TS_NUMBER will have this set */ + char *str; /**< string values will have this set */ + int inum; /**< numeric values will have this set */ + float fnum; /**< numeric values will have this set */ /** vector values (arrays containing ONLY numbers) will have this set */ float *vec; /**< elements of the vector */ @@ -46,7 +49,7 @@ /** set a ts_value from a list of floats */ int ts_set_valuefv(struct ts_value *tsv, int count, ...); int ts_set_valuefv_va(struct ts_value *tsv, int count, va_list ap); -int ts_set_valuef(struct ts_value *tsv, int fnum); /**< equiv: ts_set_valuefv(val, 1, fnum) */ +int ts_set_valuef(struct ts_value *tsv, float fnum); /**< equiv: ts_set_valuefv(val, 1, fnum) */ /** set a ts_value from a list of ts_value pointers. they are deep-copied as per ts_copy_value */ int ts_set_valuev(struct ts_value *tsv, int count, ...); @@ -100,6 +103,18 @@ void ts_add_attr(struct ts_node *node, struct ts_attr *attr); struct ts_attr *ts_get_attr(struct ts_node *node, const char *name); +const char *ts_get_attr_str(struct ts_node *node, const char *aname, + const char *def_val TS_DEFVAL(0)); +float ts_get_attr_num(struct ts_node *node, const char *aname, + float def_val TS_DEFVAL(0.0f)); +int ts_get_attr_int(struct ts_node *node, const char *aname, + int def_val TS_DEFVAL(0.0f)); +float *ts_get_attr_vec(struct ts_node *node, const char *aname, + float *def_val TS_DEFVAL(0)); +struct ts_value *ts_get_attr_array(struct ts_node *node, const char *aname, + struct ts_value *def_val TS_DEFVAL(0)); + + void ts_add_child(struct ts_node *node, struct ts_node *child); int ts_remove_child(struct ts_node *node, struct ts_node *child); struct ts_node *ts_get_child(struct ts_node *node, const char *name); @@ -107,6 +122,7 @@ struct ts_node *ts_load(const char *fname); int ts_save(struct ts_node *tree, const char *fname); + #ifdef __cplusplus } #endif diff -r 48d75df3ef04 -r bb873449cf59 src/text.c --- a/src/text.c Thu Nov 10 16:19:44 2016 +0200 +++ b/src/text.c Sun Nov 13 19:46:01 2016 +0200 @@ -4,46 +4,69 @@ #include #include "treestore.h" +#define MAX_TOKEN_SIZE 512 +struct parser { + FILE *fp; + int nline; + char token[MAX_TOKEN_SIZE]; +}; + enum { TOK_SYM, TOK_ID, TOK_NUM, TOK_STR }; -static struct ts_node *read_node(FILE *fp); -static int next_token(FILE *fp, char *buf, int bsz); +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 token[256]; - struct ts_node *node; + char *root_name; + struct parser pstate, *pst = &pstate; + struct ts_node *node = 0; - if(next_token(fp, token, sizeof token) != TOK_ID || - !(next_token(fp, token, sizeof token) == TOK_SYM && token[0] == '{')) { - fprintf(stderr, "ts_text_load: invalid file format\n"); + pstate.fp = fp; + pstate.nline = 0; + + EXPECT(TOK_ID); + if(!(root_name = strdup(pst->token))) { + perror("failed to allocate root node name"); return 0; } - if(!(node = read_node(fp))) { + EXPECT_SYM('{'); + if(!(node = read_node(pst))) { return 0; } - if(!(node->name = strdup(token))) { - perror("failed to allocate node name"); - free(node); - return 0; - } + node->name = root_name; + +err: return node; } - -#define EXPECT(type) \ - if(next_token(fp, token, sizeof token) != (type)) goto err -#define EXPECT_SYM(c) \ - if(next_token(fp, token, sizeof token) != TOK_SYM || token[0] != (c)) goto err - -static struct ts_node *read_node(FILE *fp) +static struct ts_node *read_node(struct parser *pst) { - char token[256]; + int type; struct ts_node *node; if(!(node = ts_alloc_node())) { @@ -51,107 +74,143 @@ return 0; } - while(next_token(fp, token, sizeof token) == TOK_ID) { + while((type = next_token(pst)) == TOK_ID) { char *id; - if(!(id = strdup(token))) { + if(!(id = strdup(pst->token))) { goto err; } EXPECT(TOK_SYM); - if(token[0] == '=') { + if(pst->token[0] == '=') { /* attribute */ struct ts_attr *attr; - int toktype; + int type; if(!(attr = ts_alloc_attr())) { goto err; } - if((toktype = next_token(fp, token, sizeof token)) == -1) { + if((type = next_token(pst)) == -1) { ts_free_attr(attr); + fprintf(stderr, "read_node: unexpected EOF\n"); goto err; } + + switch(type) { + case TOK_NUM: + ts_set_valuef(&attr->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, &attr->val, endsym) == -1) { + goto err; + } + } else { + fprintf(stderr, "read_node: unexpected rhs symbol: %c\n", pst->token[0]); + } + break; + + case TOK_ID: + case TOK_STR: + default: + ts_set_value(&attr->val, pst->token); + } + attr->name = id; - ts_set_value(&attr->val, token); - ts_add_attr(node, attr); - } else if(token[0] == '{') { + } else if(pst->token[0] == '{') { /* child */ struct ts_node *child; - if(!(child = read_node(fp))) { + 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", token); + 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 next_token(FILE *fp, char *buf, int bsz) +static int read_array(struct parser *pst, struct ts_value *tsv, char endsym) +{ + // TODO implement + return -1; +} + +static int next_token(struct parser *pst) { int c; - char *ptr; + char *ptr, *bend = pst->token + MAX_TOKEN_SIZE - 1; /* leave space for the terminator */ // skip whitespace - while((c = fgetc(fp)) != -1 && isspace(c)) { + while((c = fgetc(pst->fp)) != -1) { if(c == '#') { // skip to end of line - while((c = fgetc(fp)) != -1 && c != '\n'); + 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; - buf[0] = c; - buf[1] = 0; + pst->token[0] = c; + pst->token[1] = 0; if(isdigit(c)) { // token is a number - ptr = buf + 1; - while((c = fgetc(fp)) != -1 && isdigit(c)) { - *ptr++ = c; + ptr = pst->token + 1; + while((c = fgetc(pst->fp)) != -1 && isdigit(c)) { + if(ptr < bend) *ptr++ = c; } - if(c != -1) ungetc(c, fp); + if(c != -1) ungetc(c, pst->fp); *ptr = 0; return TOK_NUM; } if(isalpha(c)) { // token is an identifier - ptr = buf + 1; - while((c = fgetc(fp)) != -1 && isalnum(c)) { - *ptr++ = c; + ptr = pst->token + 1; + while((c = fgetc(pst->fp)) != -1 && isalnum(c)) { + if(ptr < bend) *ptr++ = c; } - if(c != -1) ungetc(c, fp); + if(c != -1) ungetc(c, pst->fp); *ptr = 0; return TOK_ID; } if(c == '"') { // token is a string constant - ptr = buf; - while((c = fgetc(fp)) != -1 && c != '"' && c != '\r' && c != '\n') { - *ptr++ = c; + ptr = pst->token; + while((c = fgetc(pst->fp)) != -1 && c != '"' && c != '\r' && c != '\n') { + if(ptr < bend) *ptr++ = c; } + if(c == '\n') ++pst->nline; if(c != '"') { return -1; } *ptr = 0; return TOK_STR; } - return TOK_SYM; } @@ -172,6 +231,7 @@ c = tree->child_list; while(c) { ts_text_save(c, fp); + c = c->next; } fprintf(fp, "%s}\n", indent(lvl)); @@ -217,7 +277,7 @@ break; default: - fputs(value->str, fp); + fprintf(fp, "\"%s\"", value->str); } } @@ -229,7 +289,22 @@ 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"; + 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"; +} diff -r 48d75df3ef04 -r bb873449cf59 src/treestore.c --- a/src/treestore.c Thu Nov 10 16:19:44 2016 +0200 +++ b/src/treestore.c Sun Nov 13 19:46:01 2016 +0200 @@ -93,9 +93,15 @@ return -1; } +struct val_list_node { + struct ts_value val; + struct val_list_node *next; +}; int ts_set_value(struct ts_value *tsv, const char *str) { + char *endp; + if(tsv->str) { ts_destroy_value(tsv); if(ts_init_value(tsv) == -1) { @@ -103,10 +109,58 @@ } } + tsv->type = TS_STRING; if(!(tsv->str = malloc(strlen(str) + 1))) { return -1; } strcpy(tsv->str, str); + + /* try to parse the string and see if it fits any of the value types */ + if(*str == '[' || *str == '{') { + /* try to parse as a vector */ + struct val_list_node *list = 0, *tail = 0, *node; + int nelem = 0; + char endsym = *str++ + 2; /* ']' is '[' + 2 and '}' is '{' + 2 */ + + while(*str && *str != endsym) { + float val = strtod(str, &endp); + if(endp == str || !(node = malloc(sizeof *node))) { + break; + } + ts_init_value(&node->val); + ts_set_valuef(&node->val, val); + node->next = 0; + + if(list) { + tail->next = node; + tail = node; + } else { + list = tail = node; + } + ++nelem; + str = endp; + } + + if(nelem && (tsv->array = malloc(nelem * sizeof *tsv->array)) && + (tsv->vec = malloc(nelem * sizeof *tsv->vec))) { + int idx = 0; + while(list) { + node = list; + list = list->next; + + tsv->array[idx] = node->val; + tsv->vec[idx] = node->val.fnum; + ++idx; + free(node); + } + tsv->type = TS_VECTOR; + } + + } else if((tsv->fnum = strtod(str, &endp)), endp != str) { + /* it's a number I guess... */ + tsv->type = TS_NUMBER; + } + return 0; } @@ -226,7 +280,7 @@ return 0; } -int ts_set_valuef(struct ts_value *tsv, int fnum) +int ts_set_valuef(struct ts_value *tsv, float fnum) { return ts_set_valuefv(tsv, 1, fnum); } @@ -387,6 +441,51 @@ return 0; } +const char *ts_get_attr_str(struct ts_node *node, const char *aname, const char *def_val) +{ + struct ts_attr *attr = ts_get_attr(node, aname); + if(!attr || !attr->val.str) { + return def_val; + } + return attr->val.str; +} + +float ts_get_attr_num(struct ts_node *node, const char *aname, float def_val) +{ + struct ts_attr *attr = ts_get_attr(node, aname); + if(!attr || attr->val.type != TS_NUMBER) { + return def_val; + } + return attr->val.fnum; +} + +int ts_get_attr_int(struct ts_node *node, const char *aname, int def_val) +{ + struct ts_attr *attr = ts_get_attr(node, aname); + if(!attr || attr->val.type != TS_NUMBER) { + return def_val; + } + return attr->val.inum; +} + +float *ts_get_attr_vec(struct ts_node *node, const char *aname, float *def_val) +{ + struct ts_attr *attr = ts_get_attr(node, aname); + if(!attr || !attr->val.vec) { + return def_val; + } + return attr->val.vec; +} + +struct ts_value *ts_get_attr_array(struct ts_node *node, const char *aname, struct ts_value *def_val) +{ + struct ts_attr *attr = ts_get_attr(node, aname); + if(!attr || !attr->val.array) { + return def_val; + } + return attr->val.array; +} + void ts_add_child(struct ts_node *node, struct ts_node *child) { if(child->parent) {