diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2015-12-18 17:33:16 +0200 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2015-12-18 18:27:03 +0200 |
commit | 9c0d1b58fd370bed9442ad8859d9c6e1f02cc643 (patch) | |
tree | 993dfff1d8236d6f1b81cd0de1325452b4ae84e6 /src/json-gram.y | |
parent | d51a70af75b14720a1756d90ec53132596aa3ebf (diff) | |
download | grecs-9c0d1b58fd370bed9442ad8859d9c6e1f02cc643.tar.gz grecs-9c0d1b58fd370bed9442ad8859d9c6e1f02cc643.tar.bz2 |
Add basic JSON support functions.
* src/Make-inst.am: Add json.h
* src/Make-shared.am: Likewise.
* src/Make-static.am: Likewise.
* src/Make.am: Likewise.
* src/json-gram.y: New file.
* src/json-lex.l: New file.
* src/json.h: New file.
* src/yytrans: Translate json prefixes.
* src/.gitignore: Update.
* tests/json.c: New file.
* tests/json00.at: New file.
* tests/json01.at: New file.
* tests/Makefile.am: Add new tests; build json
* tests/testsuite.at: Add new tests.
* tests/.gitignore: Update.
* am/grecs.m4: New flag "json"
* configure.ac (GRECS_SETUP): Require json
* src/Make-inst.am (include_HEADERS): Assign GRECS_HDR value.
* src/Make-shared.a [GRECS_COND_INSTALLHEADERS] (grecsinclude_HEADERS)
[!GRECS_COND_INSTALLHEADERS] (noinst_HEADERS): Likewise.
* src/Make-static.am (noinst_HEADERS): Likewise.
* src/Make.am [GRECS_COND_JSON]: Define GRECS_JSON and GRECS_EXTRA_JSON.
Diffstat (limited to 'src/json-gram.y')
-rw-r--r-- | src/json-gram.y | 371 |
1 files changed, 371 insertions, 0 deletions
diff --git a/src/json-gram.y b/src/json-gram.y new file mode 100644 index 0000000..e92ad35 --- /dev/null +++ b/src/json-gram.y @@ -0,0 +1,371 @@ +%{ +/* This file is part of Grecs. + Copyright (C) 2012-2015 Sergey Poznyakoff. + + Grecs is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + Grecs is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Grecs. If not, see <http://www.gnu.org/licenses/>. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif +#include "grecs.h" +#include <grecs-locus.h> +#include <string.h> +#include "json-gram.h" +#include "json.h" + +struct json_value *json_return_obj; + +extern int yylex(void); +static int yyerror(char const *s); + +static void +pairfree(void *ptr) +{ + struct json_pair *p = ptr; + free(p->k); + json_value_free(p->v); + free(p); +} + +static void +objfree(void *ptr) +{ + struct json_value *o = ptr; + json_value_free(o); +} + +%} + +%error-verbose +%locations + +%token <n> T_NUMBER +%token <s> T_STRING +%token <b> T_BOOL +%token T_NULL T_ERR + +%type <a> array +%type <list> objects objlist pairs pairlist +%type <p> pair +%type <obj> object +%type <o> assoc + +%union { + int b; + double n; + char *s; + struct json_array *a; + struct grecs_symtab *o; + struct json_value *obj; + struct grecs_list *list; + struct json_pair *p; +} +%% + +input : object + { + json_return_obj = $1; + } + ; + +object : T_NUMBER + { + $$ = json_value_create(json_number); + $$->v.n = $1; + } + | T_STRING + { + $$ = json_value_create(json_string); + $$->v.s = $1; + } + | T_BOOL + { + $$ = json_value_create(json_bool); + $$->v.b = $1; + } + | T_NULL + { + $$ = json_value_create(json_null); + } + | array + { + $$ = json_value_create(json_arr); + $$->v.a = $1; + } + | assoc + { + $$ = json_value_create(json_object); + $$->v.o = $1; + } + ; + +array : '[' objects ']' + { + struct json_array *a = grecs_malloc(sizeof(*a)); + if (!$2) { + a->oc = 0; + a->ov = NULL; + } else { + size_t i; + struct grecs_list_entry *ep; + a->oc = $2->count; + a->ov = grecs_calloc(a->oc, sizeof(a->ov)); + for (i = 0, ep = $2->head; ep; i++, ep = ep->next) { + struct json_value *p = ep->data; + a->ov[i] = p; + } + } + $$ = a; + } + ; + +objects : /* empty */ + { + $$ = NULL; + } + | objlist + ; + +objlist : object + { + $$ = grecs_list_create(); + $$->free_entry = objfree; + grecs_list_append($$, $1); + } + | objlist ',' object + { + grecs_list_append($1, $3); + } + ; + +assoc : '{' pairs '}' + { + struct grecs_symtab *s; + + s = json_assoc_create(); + if ($2) { + struct grecs_list_entry *ep; + for (ep = $2->head; ep; ep = ep->next) { + struct json_pair *p = ep->data; + int install = 1; + grecs_symtab_lookup_or_install(s, p, &install); + if (install) { + p->k = NULL; + p->v = NULL; + } + } + grecs_list_free($2); + } + $$ = s; + } + ; + +pairs : /* empty */ + { + $$ = NULL; + } + | pairlist + ; + +pairlist: pair + { + $$ = grecs_list_create(); + $$->free_entry = pairfree; + grecs_list_append($$, $1); + } + | pairlist ',' pair + { + grecs_list_append($1, $3); + } + ; + +pair : T_STRING ':' object + { + struct json_pair *p = grecs_malloc(sizeof(*p)); + p->k = $1; + p->v = $3; + $$ = p; + } + ; +%% + +static int +yyerror(char const *s) +{ + jsonlex_diag(s); + return 0; +} + +struct json_value * +json_value_create(int type) +{ + struct json_value *obj = grecs_zalloc(sizeof(*obj)); + obj->type = type; + return obj; +} + +void +json_value_free(struct json_value *obj) +{ + size_t i; + + if (!obj) + return; + + switch (obj->type) { + case json_null: + case json_bool: + case json_number: + break; + case json_string: + free(obj->v.s); + break; + case json_arr: + for (i = 0; i < obj->v.a->oc; i++) + json_value_free(obj->v.a->ov[i]); + free(obj->v.a); + break; + case json_object: + grecs_symtab_free(obj->v.o); + } + free(obj); +} + +static unsigned +json_st_hash(void *data, unsigned long n_buckets) +{ + struct json_pair *p = data; + return grecs_hash_string(p->k, n_buckets); +} + +static int +json_st_cmp(const void *a, const void *b) +{ + struct json_pair const *pa = a; + struct json_pair const *pb = b; + return strcmp(pa->k, pb->k); +} + +static int +json_st_copy(void *a, void *b) +{ + struct json_pair *pa = a; + struct json_pair *pb = b; + memcpy(pa, pb, sizeof(*pa)); + return 0; +} + +static void +json_st_free(void *ptr) +{ + struct json_pair *p = ptr; + free(p->k); + json_value_free(p->v); + free(p); +} + +struct grecs_symtab * +json_assoc_create() +{ + return grecs_symtab_create(sizeof(struct json_pair), + json_st_hash, + json_st_cmp, + json_st_copy, + NULL, + json_st_free); +} + +struct json_value * +json_parse_string(char const *input, size_t len) +{ + jsonlex_setup(input, len); + if (yyparse()) { + /* FIXME: error recovery */ + return NULL; + } + return json_return_obj; +} + +struct json_value * +json_value_lookup(struct json_value *obj, const char *ident) +{ + char *qbuf = NULL; + size_t qlen = 0; + + while (obj && *ident) { + char const *p; + char *q; + size_t l; + + for (p = ident; *p; p++) { + if (*p == '\\') + ++p; + else if (*p == '.') + break; + } + l = p - ident + 1; + if (l > qlen) { + qlen = l; + qbuf = grecs_realloc(qbuf, qlen); + } + q = qbuf; + while (*ident) { + if (*ident == '\\') { + int c; + ++ident; + if (json_unescape(*ident, &c)) + *q++ = *ident++; + else + *q++ = c; + } else if (*ident == '.') { + ++ident; + break; + } else + *q++ = *ident++; + } + *q = 0; + + switch (obj->type) { + case json_null: + case json_bool: + case json_number: + case json_string: + obj = NULL; + break; + case json_arr: + l = strtoul(qbuf, &q, 10); + if (*q == 0 && l < obj->v.a->oc) + obj = obj->v.a->ov[l]; + else + obj = NULL; + break; + case json_object: { + struct json_pair key, *match; + key.k = qbuf; + match = grecs_symtab_lookup_or_install(obj->v.o, + &key, NULL); + obj = match ? match->v : NULL; + } + } + } + if (*ident) + obj = NULL; + free(qbuf); + return obj; +} + + + + + |