aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2015-12-18 17:33:16 +0200
committerSergey Poznyakoff <gray@gnu.org.ua>2015-12-18 18:27:03 +0200
commit9c0d1b58fd370bed9442ad8859d9c6e1f02cc643 (patch)
tree993dfff1d8236d6f1b81cd0de1325452b4ae84e6 /src
parentd51a70af75b14720a1756d90ec53132596aa3ebf (diff)
downloadgrecs-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')
-rw-r--r--src/.gitignore5
-rw-r--r--src/Make-inst.am2
-rw-r--r--src/Make-shared.am7
-rw-r--r--src/Make-static.am4
-rw-r--r--src/Make.am12
-rw-r--r--src/json-gram.y371
-rw-r--r--src/json-lex.l204
-rw-r--r--src/json.h68
-rw-r--r--src/yytrans1
9 files changed, 669 insertions, 5 deletions
diff --git a/src/.gitignore b/src/.gitignore
index 322178c..11738ff 100644
--- a/src/.gitignore
+++ b/src/.gitignore
@@ -20 +20,6 @@ Make-shared.in
Make-static.in
+json-gram.c
+json-gram.h
+json-gram.output
+json-lex.c
+
diff --git a/src/Make-inst.am b/src/Make-inst.am
index daf1c6f..f9cae0e 100644
--- a/src/Make-inst.am
+++ b/src/Make-inst.am
@@ -19,3 +19,3 @@ lib_LTLIBRARIES=libgrecs.la
libgrecs_la_SOURCES = $(GRECS_SRC)
-include_HEADERS = grecs.h grecsopt.h wordsplit.h
+include_HEADERS = $(GRECS_HDR)
m4datadir = $(datadir)/aclocal
diff --git a/src/Make-shared.am b/src/Make-shared.am
index 292218b..8f698c4 100644
--- a/src/Make-shared.am
+++ b/src/Make-shared.am
@@ -18,7 +18,8 @@ noinst_LTLIBRARIES=libgrecs.la
libgrecs_la_SOURCES = $(GRECS_SRC)
+
if GRECS_COND_INSTALLHEADERS
grecsincludedir = @GRECS_INCLUDE_DIR@
- grecsinclude_HEADERS = grecs.h grecsopt.h wordsplit.h
+ grecsinclude_HEADERS = $(GRECS_HDR)
else
- noinst_HEADERS += grecs.h grecsopt.h wordsplit.h
-endif \ No newline at end of file
+ noinst_HEADERS += $(GRECS_HDR)
+endif
diff --git a/src/Make-static.am b/src/Make-static.am
index 63b6338..4050e47 100644
--- a/src/Make-static.am
+++ b/src/Make-static.am
@@ -18,2 +18,4 @@ noinst_LIBRARIES=libgrecs.a
libgrecs_a_SOURCES = $(GRECS_SRC)
-noinst_HEADERS += grecs.h grecsopt.h wordsplit.h
+noinst_HEADERS += $(GRECS_HDR)
+
+
diff --git a/src/Make.am b/src/Make.am
index 11ef6df..15e0075 100644
--- a/src/Make.am
+++ b/src/Make.am
@@ -41,2 +41,7 @@ endif
+if GRECS_COND_JSON
+ GRECS_JSON = json-gram.y json-lex.l
+ GRECS_EXTRA_JSON = json-gram.h
+endif
+
GRECS_SRC = \
@@ -66,2 +71,3 @@ GRECS_SRC = \
wordsplit.c\
+ $(GRECS_JSON)\
$(GRECS_PARSER_BIND)\
@@ -71,2 +77,7 @@ GRECS_SRC = \
+GRECS_HDR = grecs.h grecsopt.h wordsplit.h
+if GRECS_COND_JSON
+ GRECS_HDR += json.h
+endif
+
if GRECS_COND_SOCKADDR_LIST
@@ -82,2 +93,3 @@ EXTRA_DIST=\
$(GRECS_EXTRA_DHCPD)\
+ $(GRECS_EXTRA_JSON)\
$(GRECS_EXTRA_META1)\
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;
+}
+
+
+
+
+
diff --git a/src/json-lex.l b/src/json-lex.l
new file mode 100644
index 0000000..2a08fd0
--- /dev/null
+++ b/src/json-lex.l
@@ -0,0 +1,204 @@
+%option noinput
+%option nounput
+%top {
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+}
+%{
+/* 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/>. */
+
+#include "grecs.h"
+#include <grecs-locus.h>
+#include "json-gram.h"
+#include "json.h"
+
+static char const *input_ptr;
+static size_t input_size;
+struct grecs_locus_point json_current_locus_point; /* Input location */
+
+char const *json_err_diag;
+struct grecs_locus json_err_locus;
+
+#undef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+ do { \
+ size_t n = (max_size > input_size) ? input_size : max_size; \
+ if (n) { \
+ memcpy(buf, input_ptr, n); \
+ input_ptr += n; \
+ input_size -= n; \
+ } \
+ result = n; \
+ } while(0)
+
+#define YY_USER_ACTION do { \
+ if (YYSTATE == 0) { \
+ yylloc.beg = json_current_locus_point; \
+ yylloc.beg.col++; \
+ } \
+ json_current_locus_point.col += yyleng; \
+ yylloc.end = json_current_locus_point; \
+ } while (0);
+
+void
+jsonlex_setup(char const *s, size_t l)
+{
+ input_ptr = s;
+ input_size = l;
+ json_current_locus_point.file = NULL;
+ json_current_locus_point.line = 1;
+ json_current_locus_point.col = 0;
+ json_err_diag = NULL;
+ yy_flex_debug = 0;
+}
+
+void
+jsonlex_diag(const char *s)
+{
+ if (!json_err_diag) {
+ json_err_diag = s;
+ json_err_locus = yylloc;
+ }
+}
+
+static int
+utf8_wctomb(char *u)
+{
+ unsigned int wc = strtoul(u, NULL, 16);
+ int count;
+ char r[6];
+
+ if (wc < 0x80)
+ count = 1;
+ else if (wc < 0x800)
+ count = 2;
+ else if (wc < 0x10000)
+ count = 3;
+ else if (wc < 0x200000)
+ count = 4;
+ else if (wc < 0x4000000)
+ count = 5;
+ else if (wc <= 0x7fffffff)
+ count = 6;
+ else
+ return -1;
+
+ switch (count) {
+ /* Note: code falls through cases! */
+ case 6:
+ r[5] = 0x80 | (wc & 0x3f);
+ wc = wc >> 6;
+ wc |= 0x4000000;
+ case 5:
+ r[4] = 0x80 | (wc & 0x3f);
+ wc = wc >> 6;
+ wc |= 0x200000;
+ case 4:
+ r[3] = 0x80 | (wc & 0x3f);
+ wc = wc >> 6;
+ wc |= 0x10000;
+ case 3:
+ r[2] = 0x80 | (wc & 0x3f);
+ wc = wc >> 6;
+ wc |= 0x800;
+ case 2:
+ r[1] = 0x80 | (wc & 0x3f);
+ wc = wc >> 6;
+ wc |= 0xc0;
+ case 1:
+ r[0] = wc;
+ }
+ grecs_line_acc_grow(r, count);
+ return count;
+}
+
+int
+json_unescape(int c, int *o)
+{
+ static char transtab[] = "\\\\\"\"//b\bf\fn\nr\rt\t";
+ char *p;
+
+ for (p = transtab; *p; p += 2) {
+ if (p[0] == c) {
+ *o = p[1];
+ return 0;
+ }
+ }
+ return -1;
+}
+
+#define YY_SKIP_YYWRAP 1
+static int
+yywrap()
+{
+ return 1;
+}
+%}
+D [0-9]
+X [0-9a-fA-F]
+%x STR
+%%
+"-"?{D}{D}*(.{D}{D}*)?([eE][-+]?{D}{D}*)? {
+ yylval.n = strtod(yytext, NULL);
+ return T_NUMBER;
+ }
+\"[^\\\"]*\" { grecs_line_begin();
+ grecs_line_add(yytext + 1, yyleng - 2);
+ yylval.s = grecs_line_finish();
+ return T_STRING; }
+\"[^\\\"]*\\{X}{4} { BEGIN(STR);
+ grecs_line_begin();
+ grecs_line_add(yytext + 1, yyleng - 5);
+ utf8_wctomb(yytext + yyleng - 4);
+ }
+\"[^\\\"]*\\. { int c;
+ BEGIN(STR);
+ grecs_line_begin();
+ grecs_line_acc_grow(yytext + 1, yyleng - 3);
+ if (json_unescape(yytext[yyleng - 1], &c)) {
+ jsonlex_diag("invalid UTF-8 codepoint");
+ return T_ERR;
+ }
+ grecs_line_acc_grow_char(c);
+ }
+<STR>[^\\\"]*\" { BEGIN(INITIAL);
+ if (yyleng > 1)
+ grecs_line_acc_grow(yytext, yyleng - 1);
+ yylval.s = grecs_line_finish();
+ return T_STRING; }
+<STR>[^\\\"]*\\{X}{4} {
+ grecs_line_add(yytext, yyleng - 5);
+ utf8_wctomb(yytext + yyleng - 4);
+}
+<STR>[^\\\"]*\\. {
+ int c;
+ grecs_line_acc_grow(yytext, yyleng - 2);
+ if (json_unescape(yytext[yyleng - 1], &c)) {
+ jsonlex_diag("invalid UTF-8 codepoint");
+ return T_ERR;
+ }
+ grecs_line_acc_grow_char(c); }
+
+null { return T_NULL; }
+true { yylval.b = 1; return T_BOOL; }
+false { yylval.b = 0; return T_BOOL; }
+"{"|"}"|"["|"]"|":"|"," return yytext[0];
+[ \t]* ;
+\n grecs_locus_point_advance_line(json_current_locus_point);
+. { jsonlex_diag("bogus character");
+ return T_ERR; }
diff --git a/src/json.h b/src/json.h
new file mode 100644
index 0000000..e089692
--- /dev/null
+++ b/src/json.h
@@ -0,0 +1,68 @@
+/* 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/>. */
+
+#include <grecs.h>
+
+enum json_value_type
+{
+ json_null,
+ json_bool,
+ json_number,
+ json_string,
+ json_arr,
+ json_object
+};
+
+struct json_value;
+struct json_array {
+ size_t oc;
+ struct json_value **ov;
+};
+
+struct json_value {
+ enum json_value_type type;
+ union {
+ int b; /* json_bool */
+ double n; /* json_number */
+ char *s; /* json_string */
+ struct json_array *a; /* json_arr */
+ struct grecs_symtab *o; /* json_object */
+ } v;
+};
+
+struct json_pair {
+ char *k;
+ struct json_value *v;
+};
+
+extern char const *json_err_diag;
+extern struct grecs_locus json_err_locus;
+extern struct json_value *json_return_obj;
+
+void jsonlex_setup(char const *s, size_t l);
+void jsonlex_diag(const char *s);
+int json_unescape(int c, int *o);
+
+struct json_value *json_value_create(int type);
+struct grecs_symtab *json_assoc_create(void);
+void json_value_free(struct json_value *obj);
+
+struct json_value *json_parse_string(char const *input, size_t len);
+
+struct json_value *json_value_lookup(struct json_value *obj,
+ const char *ident);
+
+
diff --git a/src/yytrans b/src/yytrans
index 9e3c0df..f8f893d 100644
--- a/src/yytrans
+++ b/src/yytrans
@@ -21 +21,2 @@ meta1 grecs_meta1_
bind grecs_bind_
+json grecs_json_ \ No newline at end of file

Return to:

Send suggestions and report system problems to the System administrator.