libtreestore

changeset 4:bb873449cf59

fixed ts_set_value and added convenience value-get functions
author John Tsiombikas <nuclear@member.fsf.org>
date Sun, 13 Nov 2016 19:46:01 +0200
parents 48d75df3ef04
children f3ade599cfbb
files CMakeLists.txt include/treestore.h src/text.c src/treestore.c
diffstat 4 files changed, 251 insertions(+), 58 deletions(-) [+]
line diff
     1.1 --- a/CMakeLists.txt	Thu Nov 10 16:19:44 2016 +0200
     1.2 +++ b/CMakeLists.txt	Sun Nov 13 19:46:01 2016 +0200
     1.3 @@ -23,6 +23,9 @@
     1.4  target_include_directories(treestore PUBLIC include)
     1.5  target_include_directories(treestore-static PUBLIC include)
     1.6  
     1.7 +target_link_libraries(treestore -lexpat)
     1.8 +target_link_libraries(treestore-static -lexpat)
     1.9 +
    1.10  install(TARGETS treestore
    1.11  	RUNTIME DESTINATION bin
    1.12  	LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
     2.1 --- a/include/treestore.h	Thu Nov 10 16:19:44 2016 +0200
     2.2 +++ b/include/treestore.h	Sun Nov 13 19:46:01 2016 +0200
     2.3 @@ -4,18 +4,21 @@
     2.4  #include <stdarg.h>
     2.5  
     2.6  #ifdef __cplusplus
     2.7 +#define TS_DEFVAL(x) =(x)
     2.8  extern "C" {
     2.9 +#else
    2.10 +#define TS_DEFVAL(x)
    2.11  #endif
    2.12  
    2.13 -enum ts_value_type { TS_UNKNOWN, TS_NUMBER, TS_VECTOR, TS_ARRAY };
    2.14 +enum ts_value_type { TS_STRING, TS_NUMBER, TS_VECTOR, TS_ARRAY };
    2.15  
    2.16  /** treestore node attribute value */
    2.17  struct ts_value {
    2.18  	enum ts_value_type type;
    2.19  
    2.20 -	char *str;		/**< all values have a string representation */
    2.21 -	int inum;		/**< numeric values TS_NUMBER will have this set */
    2.22 -	float fnum;		/**< numeric values TS_NUMBER will have this set */
    2.23 +	char *str;		/**< string values will have this set */
    2.24 +	int inum;		/**< numeric values will have this set */
    2.25 +	float fnum;		/**< numeric values will have this set */
    2.26  
    2.27  	/** vector values (arrays containing ONLY numbers) will have this set */
    2.28  	float *vec;		/**< elements of the vector */
    2.29 @@ -46,7 +49,7 @@
    2.30  /** set a ts_value from a list of floats */
    2.31  int ts_set_valuefv(struct ts_value *tsv, int count, ...);
    2.32  int ts_set_valuefv_va(struct ts_value *tsv, int count, va_list ap);
    2.33 -int ts_set_valuef(struct ts_value *tsv, int fnum);	/**< equiv: ts_set_valuefv(val, 1, fnum) */
    2.34 +int ts_set_valuef(struct ts_value *tsv, float fnum);	/**< equiv: ts_set_valuefv(val, 1, fnum) */
    2.35  
    2.36  /** set a ts_value from a list of ts_value pointers. they are deep-copied as per ts_copy_value */
    2.37  int ts_set_valuev(struct ts_value *tsv, int count, ...);
    2.38 @@ -100,6 +103,18 @@
    2.39  void ts_add_attr(struct ts_node *node, struct ts_attr *attr);
    2.40  struct ts_attr *ts_get_attr(struct ts_node *node, const char *name);
    2.41  
    2.42 +const char *ts_get_attr_str(struct ts_node *node, const char *aname,
    2.43 +		const char *def_val TS_DEFVAL(0));
    2.44 +float ts_get_attr_num(struct ts_node *node, const char *aname,
    2.45 +		float def_val TS_DEFVAL(0.0f));
    2.46 +int ts_get_attr_int(struct ts_node *node, const char *aname,
    2.47 +		int def_val TS_DEFVAL(0.0f));
    2.48 +float *ts_get_attr_vec(struct ts_node *node, const char *aname,
    2.49 +		float *def_val TS_DEFVAL(0));
    2.50 +struct ts_value *ts_get_attr_array(struct ts_node *node, const char *aname,
    2.51 +		struct ts_value *def_val TS_DEFVAL(0));
    2.52 +
    2.53 +
    2.54  void ts_add_child(struct ts_node *node, struct ts_node *child);
    2.55  int ts_remove_child(struct ts_node *node, struct ts_node *child);
    2.56  struct ts_node *ts_get_child(struct ts_node *node, const char *name);
    2.57 @@ -107,6 +122,7 @@
    2.58  struct ts_node *ts_load(const char *fname);
    2.59  int ts_save(struct ts_node *tree, const char *fname);
    2.60  
    2.61 +
    2.62  #ifdef __cplusplus
    2.63  }
    2.64  #endif
     3.1 --- a/src/text.c	Thu Nov 10 16:19:44 2016 +0200
     3.2 +++ b/src/text.c	Sun Nov 13 19:46:01 2016 +0200
     3.3 @@ -4,46 +4,69 @@
     3.4  #include <ctype.h>
     3.5  #include "treestore.h"
     3.6  
     3.7 +#define MAX_TOKEN_SIZE	512
     3.8 +struct parser {
     3.9 +	FILE *fp;
    3.10 +	int nline;
    3.11 +	char token[MAX_TOKEN_SIZE];
    3.12 +};
    3.13 +
    3.14  enum { TOK_SYM, TOK_ID, TOK_NUM, TOK_STR };
    3.15  
    3.16 -static struct ts_node *read_node(FILE *fp);
    3.17 -static int next_token(FILE *fp, char *buf, int bsz);
    3.18 +static struct ts_node *read_node(struct parser *pstate);
    3.19 +static int read_array(struct parser *pstate, struct ts_value *tsv, char endsym);
    3.20 +static int next_token(struct parser *pstate);
    3.21  
    3.22  static void print_attr(struct ts_attr *attr, FILE *fp, int level);
    3.23  static void print_value(struct ts_value *value, FILE *fp);
    3.24  static int tree_level(struct ts_node *n);
    3.25  static const char *indent(int x);
    3.26 +static const char *toktypestr(int type);
    3.27 +
    3.28 +#define EXPECT(type) \
    3.29 +	do { \
    3.30 +		if(next_token(pst) != (type)) { \
    3.31 +			fprintf(stderr, "expected %s token\n", toktypestr(type)); \
    3.32 +			goto err; \
    3.33 +		} \
    3.34 +	} while(0)
    3.35 +
    3.36 +#define EXPECT_SYM(c) \
    3.37 +	do { \
    3.38 +		if(next_token(pst) != TOK_SYM || pst->token[0] != (c)) { \
    3.39 +			fprintf(stderr, "expected symbol: %c\n", c); \
    3.40 +			goto err; \
    3.41 +		} \
    3.42 +	} while(0)
    3.43 +
    3.44  
    3.45  struct ts_node *ts_text_load(FILE *fp)
    3.46  {
    3.47 -	char token[256];
    3.48 -	struct ts_node *node;
    3.49 +	char *root_name;
    3.50 +	struct parser pstate, *pst = &pstate;
    3.51 +	struct ts_node *node = 0;
    3.52  
    3.53 -	if(next_token(fp, token, sizeof token) != TOK_ID ||
    3.54 -			!(next_token(fp, token, sizeof token) == TOK_SYM && token[0] == '{')) {
    3.55 -		fprintf(stderr, "ts_text_load: invalid file format\n");
    3.56 +	pstate.fp = fp;
    3.57 +	pstate.nline = 0;
    3.58 +
    3.59 +	EXPECT(TOK_ID);
    3.60 +	if(!(root_name = strdup(pst->token))) {
    3.61 +		perror("failed to allocate root node name");
    3.62  		return 0;
    3.63  	}
    3.64 -	if(!(node = read_node(fp))) {
    3.65 +	EXPECT_SYM('{');
    3.66 +	if(!(node = read_node(pst))) {
    3.67  		return 0;
    3.68  	}
    3.69 -	if(!(node->name = strdup(token))) {
    3.70 -		perror("failed to allocate node name");
    3.71 -		free(node);
    3.72 -		return 0;
    3.73 -	}
    3.74 +	node->name = root_name;
    3.75 +
    3.76 +err:
    3.77  	return node;
    3.78  }
    3.79  
    3.80 -
    3.81 -#define EXPECT(type) \
    3.82 -	if(next_token(fp, token, sizeof token) != (type)) goto err
    3.83 -#define EXPECT_SYM(c) \
    3.84 -	if(next_token(fp, token, sizeof token) != TOK_SYM || token[0] != (c)) goto err
    3.85 -
    3.86 -static struct ts_node *read_node(FILE *fp)
    3.87 +static struct ts_node *read_node(struct parser *pst)
    3.88  {
    3.89 -	char token[256];
    3.90 +	int type;
    3.91  	struct ts_node *node;
    3.92  
    3.93  	if(!(node = ts_alloc_node())) {
    3.94 @@ -51,107 +74,143 @@
    3.95  		return 0;
    3.96  	}
    3.97  
    3.98 -	while(next_token(fp, token, sizeof token) == TOK_ID) {
    3.99 +	while((type = next_token(pst)) == TOK_ID) {
   3.100  		char *id;
   3.101  
   3.102 -		if(!(id = strdup(token))) {
   3.103 +		if(!(id = strdup(pst->token))) {
   3.104  			goto err;
   3.105  		}
   3.106  
   3.107  		EXPECT(TOK_SYM);
   3.108  
   3.109 -		if(token[0] == '=') {
   3.110 +		if(pst->token[0] == '=') {
   3.111  			/* attribute */
   3.112  			struct ts_attr *attr;
   3.113 -			int toktype;
   3.114 +			int type;
   3.115  
   3.116  			if(!(attr = ts_alloc_attr())) {
   3.117  				goto err;
   3.118  			}
   3.119  
   3.120 -			if((toktype = next_token(fp, token, sizeof token)) == -1) {
   3.121 +			if((type = next_token(pst)) == -1) {
   3.122  				ts_free_attr(attr);
   3.123 +				fprintf(stderr, "read_node: unexpected EOF\n");
   3.124  				goto err;
   3.125  			}
   3.126 +
   3.127 +			switch(type) {
   3.128 +			case TOK_NUM:
   3.129 +				ts_set_valuef(&attr->val, atof(pst->token));
   3.130 +				break;
   3.131 +
   3.132 +			case TOK_SYM:
   3.133 +				if(pst->token[0] == '[' || pst->token[0] == '{') {
   3.134 +					char endsym = pst->token[0] + 2; /* end symbol is dist 2 from either '[' or '{' */
   3.135 +					if(read_array(pst, &attr->val, endsym) == -1) {
   3.136 +						goto err;
   3.137 +					}
   3.138 +				} else {
   3.139 +					fprintf(stderr, "read_node: unexpected rhs symbol: %c\n", pst->token[0]);
   3.140 +				}
   3.141 +				break;
   3.142 +
   3.143 +			case TOK_ID:
   3.144 +			case TOK_STR:
   3.145 +			default:
   3.146 +				ts_set_value(&attr->val, pst->token);
   3.147 +			}
   3.148 +
   3.149  			attr->name = id;
   3.150 -			ts_set_value(&attr->val, token);
   3.151 -
   3.152  			ts_add_attr(node, attr);
   3.153  
   3.154 -		} else if(token[0] == '{') {
   3.155 +		} else if(pst->token[0] == '{') {
   3.156  			/* child */
   3.157  			struct ts_node *child;
   3.158  
   3.159 -			if(!(child = read_node(fp))) {
   3.160 +			if(!(child = read_node(pst))) {
   3.161  				ts_free_node(node);
   3.162  				return 0;
   3.163  			}
   3.164 +
   3.165  			child->name = id;
   3.166 -
   3.167  			ts_add_child(node, child);
   3.168  
   3.169  		} else {
   3.170 -			fprintf(stderr, "unexpected token: %s\n", token);
   3.171 +			fprintf(stderr, "unexpected token: %s\n", pst->token);
   3.172  			goto err;
   3.173  		}
   3.174  	}
   3.175  
   3.176 +	if(type != TOK_SYM || pst->token[0] != '}') {
   3.177 +		fprintf(stderr, "expected closing brace\n");
   3.178 +		goto err;
   3.179 +	}
   3.180 +	return node;
   3.181 +
   3.182  err:
   3.183  	fprintf(stderr, "treestore read_node failed\n");
   3.184  	ts_free_node(node);
   3.185  	return 0;
   3.186  }
   3.187  
   3.188 -static int next_token(FILE *fp, char *buf, int bsz)
   3.189 +static int read_array(struct parser *pst, struct ts_value *tsv, char endsym)
   3.190 +{
   3.191 +	// TODO implement
   3.192 +	return -1;
   3.193 +}
   3.194 +
   3.195 +static int next_token(struct parser *pst)
   3.196  {
   3.197  	int c;
   3.198 -	char *ptr;
   3.199 +	char *ptr, *bend = pst->token + MAX_TOKEN_SIZE - 1;	/* leave space for the terminator */
   3.200  
   3.201  	// skip whitespace
   3.202 -	while((c = fgetc(fp)) != -1 && isspace(c)) {
   3.203 +	while((c = fgetc(pst->fp)) != -1) {
   3.204  		if(c == '#') { // skip to end of line
   3.205 -			while((c = fgetc(fp)) != -1 && c != '\n');
   3.206 +			while((c = fgetc(pst->fp)) != -1 && c != '\n');
   3.207  			if(c == -1) return -1;
   3.208  		}
   3.209 +		if(!isspace(c)) break;
   3.210 +		if(c == '\n') ++pst->nline;
   3.211  	}
   3.212  	if(c == -1) return -1;
   3.213  
   3.214 -	buf[0] = c;
   3.215 -	buf[1] = 0;
   3.216 +	pst->token[0] = c;
   3.217 +	pst->token[1] = 0;
   3.218  
   3.219  	if(isdigit(c)) {
   3.220  		// token is a number
   3.221 -		ptr = buf + 1;
   3.222 -		while((c = fgetc(fp)) != -1 && isdigit(c)) {
   3.223 -			*ptr++ = c;
   3.224 +		ptr = pst->token + 1;
   3.225 +		while((c = fgetc(pst->fp)) != -1 && isdigit(c)) {
   3.226 +			if(ptr < bend) *ptr++ = c;
   3.227  		}
   3.228 -		if(c != -1) ungetc(c, fp);
   3.229 +		if(c != -1) ungetc(c, pst->fp);
   3.230  		*ptr = 0;
   3.231  		return TOK_NUM;
   3.232  	}
   3.233  	if(isalpha(c)) {
   3.234  		// token is an identifier
   3.235 -		ptr = buf + 1;
   3.236 -		while((c = fgetc(fp)) != -1 && isalnum(c)) {
   3.237 -			*ptr++ = c;
   3.238 +		ptr = pst->token + 1;
   3.239 +		while((c = fgetc(pst->fp)) != -1 && isalnum(c)) {
   3.240 +			if(ptr < bend) *ptr++ = c;
   3.241  		}
   3.242 -		if(c != -1) ungetc(c, fp);
   3.243 +		if(c != -1) ungetc(c, pst->fp);
   3.244  		*ptr = 0;
   3.245  		return TOK_ID;
   3.246  	}
   3.247  	if(c == '"') {
   3.248  		// token is a string constant
   3.249 -		ptr = buf;
   3.250 -		while((c = fgetc(fp)) != -1 && c != '"' && c != '\r' && c != '\n') {
   3.251 -			*ptr++ = c;
   3.252 +		ptr = pst->token;
   3.253 +		while((c = fgetc(pst->fp)) != -1 && c != '"' && c != '\r' && c != '\n') {
   3.254 +			if(ptr < bend) *ptr++ = c;
   3.255  		}
   3.256 +		if(c == '\n') ++pst->nline;
   3.257  		if(c != '"') {
   3.258  			return -1;
   3.259  		}
   3.260  		*ptr = 0;
   3.261  		return TOK_STR;
   3.262  	}
   3.263 -
   3.264  	return TOK_SYM;
   3.265  }
   3.266  
   3.267 @@ -172,6 +231,7 @@
   3.268  	c = tree->child_list;
   3.269  	while(c) {
   3.270  		ts_text_save(c, fp);
   3.271 +		c = c->next;
   3.272  	}
   3.273  
   3.274  	fprintf(fp, "%s}\n", indent(lvl));
   3.275 @@ -217,7 +277,7 @@
   3.276  		break;
   3.277  
   3.278  	default:
   3.279 -		fputs(value->str, fp);
   3.280 +		fprintf(fp, "\"%s\"", value->str);
   3.281  	}
   3.282  }
   3.283  
   3.284 @@ -229,7 +289,22 @@
   3.285  
   3.286  static const char *indent(int x)
   3.287  {
   3.288 -	static const char *buf = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
   3.289 +	static const char buf[] = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
   3.290  	const char *end = buf + sizeof buf - 1;
   3.291  	return x > sizeof buf - 1 ? buf : end - x;
   3.292  }
   3.293 +
   3.294 +static const char *toktypestr(int type)
   3.295 +{
   3.296 +	switch(type) {
   3.297 +	case TOK_ID:
   3.298 +		return "identifier";
   3.299 +	case TOK_NUM:
   3.300 +		return "number";
   3.301 +	case TOK_STR:
   3.302 +		return "string";
   3.303 +	case TOK_SYM:
   3.304 +		return "symbol";
   3.305 +	}
   3.306 +	return "unknown";
   3.307 +}
     4.1 --- a/src/treestore.c	Thu Nov 10 16:19:44 2016 +0200
     4.2 +++ b/src/treestore.c	Sun Nov 13 19:46:01 2016 +0200
     4.3 @@ -93,9 +93,15 @@
     4.4  	return -1;
     4.5  }
     4.6  
     4.7 +struct val_list_node {
     4.8 +	struct ts_value val;
     4.9 +	struct val_list_node *next;
    4.10 +};
    4.11  
    4.12  int ts_set_value(struct ts_value *tsv, const char *str)
    4.13  {
    4.14 +	char *endp;
    4.15 +
    4.16  	if(tsv->str) {
    4.17  		ts_destroy_value(tsv);
    4.18  		if(ts_init_value(tsv) == -1) {
    4.19 @@ -103,10 +109,58 @@
    4.20  		}
    4.21  	}
    4.22  
    4.23 +	tsv->type = TS_STRING;
    4.24  	if(!(tsv->str = malloc(strlen(str) + 1))) {
    4.25  		return -1;
    4.26  	}
    4.27  	strcpy(tsv->str, str);
    4.28 +
    4.29 +	/* try to parse the string and see if it fits any of the value types */
    4.30 +	if(*str == '[' || *str == '{') {
    4.31 +		/* try to parse as a vector */
    4.32 +		struct val_list_node *list = 0, *tail = 0, *node;
    4.33 +		int nelem = 0;
    4.34 +		char endsym = *str++ + 2;	/* ']' is '[' + 2 and '}' is '{' + 2 */
    4.35 +
    4.36 +		while(*str && *str != endsym) {
    4.37 +			float val = strtod(str, &endp);
    4.38 +			if(endp == str || !(node = malloc(sizeof *node))) {
    4.39 +				break;
    4.40 +			}
    4.41 +			ts_init_value(&node->val);
    4.42 +			ts_set_valuef(&node->val, val);
    4.43 +			node->next = 0;
    4.44 +
    4.45 +			if(list) {
    4.46 +				tail->next = node;
    4.47 +				tail = node;
    4.48 +			} else {
    4.49 +				list = tail = node;
    4.50 +			}
    4.51 +			++nelem;
    4.52 +			str = endp;
    4.53 +		}
    4.54 +
    4.55 +		if(nelem && (tsv->array = malloc(nelem * sizeof *tsv->array)) &&
    4.56 +				(tsv->vec = malloc(nelem * sizeof *tsv->vec))) {
    4.57 +			int idx = 0;
    4.58 +			while(list) {
    4.59 +				node = list;
    4.60 +				list = list->next;
    4.61 +
    4.62 +				tsv->array[idx] = node->val;
    4.63 +				tsv->vec[idx] = node->val.fnum;
    4.64 +				++idx;
    4.65 +				free(node);
    4.66 +			}
    4.67 +			tsv->type = TS_VECTOR;
    4.68 +		}
    4.69 +
    4.70 +	} else if((tsv->fnum = strtod(str, &endp)), endp != str) {
    4.71 +		/* it's a number I guess... */
    4.72 +		tsv->type = TS_NUMBER;
    4.73 +	}
    4.74 +
    4.75  	return 0;
    4.76  }
    4.77  
    4.78 @@ -226,7 +280,7 @@
    4.79  	return 0;
    4.80  }
    4.81  
    4.82 -int ts_set_valuef(struct ts_value *tsv, int fnum)
    4.83 +int ts_set_valuef(struct ts_value *tsv, float fnum)
    4.84  {
    4.85  	return ts_set_valuefv(tsv, 1, fnum);
    4.86  }
    4.87 @@ -387,6 +441,51 @@
    4.88  	return 0;
    4.89  }
    4.90  
    4.91 +const char *ts_get_attr_str(struct ts_node *node, const char *aname, const char *def_val)
    4.92 +{
    4.93 +	struct ts_attr *attr = ts_get_attr(node, aname);
    4.94 +	if(!attr || !attr->val.str) {
    4.95 +		return def_val;
    4.96 +	}
    4.97 +	return attr->val.str;
    4.98 +}
    4.99 +
   4.100 +float ts_get_attr_num(struct ts_node *node, const char *aname, float def_val)
   4.101 +{
   4.102 +	struct ts_attr *attr = ts_get_attr(node, aname);
   4.103 +	if(!attr || attr->val.type != TS_NUMBER) {
   4.104 +		return def_val;
   4.105 +	}
   4.106 +	return attr->val.fnum;
   4.107 +}
   4.108 +
   4.109 +int ts_get_attr_int(struct ts_node *node, const char *aname, int def_val)
   4.110 +{
   4.111 +	struct ts_attr *attr = ts_get_attr(node, aname);
   4.112 +	if(!attr || attr->val.type != TS_NUMBER) {
   4.113 +		return def_val;
   4.114 +	}
   4.115 +	return attr->val.inum;
   4.116 +}
   4.117 +
   4.118 +float *ts_get_attr_vec(struct ts_node *node, const char *aname, float *def_val)
   4.119 +{
   4.120 +	struct ts_attr *attr = ts_get_attr(node, aname);
   4.121 +	if(!attr || !attr->val.vec) {
   4.122 +		return def_val;
   4.123 +	}
   4.124 +	return attr->val.vec;
   4.125 +}
   4.126 +
   4.127 +struct ts_value *ts_get_attr_array(struct ts_node *node, const char *aname, struct ts_value *def_val)
   4.128 +{
   4.129 +	struct ts_attr *attr = ts_get_attr(node, aname);
   4.130 +	if(!attr || !attr->val.array) {
   4.131 +		return def_val;
   4.132 +	}
   4.133 +	return attr->val.array;
   4.134 +}
   4.135 +
   4.136  void ts_add_child(struct ts_node *node, struct ts_node *child)
   4.137  {
   4.138  	if(child->parent) {