summaryrefslogtreecommitdiff
path: root/sys/src/libjson
diff options
context:
space:
mode:
authorBurnZeZ <brz-9dev@intma.in>2013-10-27 15:44:33 -0400
committerBurnZeZ <brz-9dev@intma.in>2013-10-27 15:44:33 -0400
commit2dc7e311f43c41ecc412c237cbcb6293953c17e9 (patch)
treec78a94a222c4eabf97db33d83061df7625897afd /sys/src/libjson
parent632b7adffbd137d9cbe95431fa6919f875c47e5b (diff)
make libjson from /sys/src/cmd/btc/json.c
Diffstat (limited to 'sys/src/libjson')
-rw-r--r--sys/src/libjson/json.c326
-rw-r--r--sys/src/libjson/mkfile10
2 files changed, 336 insertions, 0 deletions
diff --git a/sys/src/libjson/json.c b/sys/src/libjson/json.c
new file mode 100644
index 000000000..850b94af7
--- /dev/null
+++ b/sys/src/libjson/json.c
@@ -0,0 +1,326 @@
+#include <u.h>
+#include <libc.h>
+#include <ctype.h>
+#include <json.h>
+
+typedef struct Lex Lex;
+
+enum {
+ TEOF,
+ TSTRING = (1<<(8*sizeof(Rune)))+1,
+ TNUM,
+ TNULL,
+ TFALSE,
+ TTRUE,
+};
+
+struct Lex
+{
+ char *s;
+ int t;
+ double n;
+ char buf[4096];
+ Rune peeked;
+ jmp_buf jmp;
+ int canjmp;
+};
+
+static Rune
+getch(Lex *l)
+{
+ Rune r;
+
+ if(l->peeked){
+ r = l->peeked;
+ l->peeked = 0;
+ return r;
+ }
+ l->s += chartorune(&r, l->s);
+ return r;
+}
+
+static Rune
+peekch(Lex *l)
+{
+ if(!l->peeked)
+ l->peeked = getch(l);
+ return l->peeked;
+}
+
+static int
+lex(Lex *l)
+{
+ Rune r;
+ char *t;
+
+ for(;;){
+ r = peekch(l);
+ if(r != 0x20 && r != 0x09 && r != 0x0A && r != 0x0D)
+ break;
+ getch(l);
+ }
+ r = getch(l);
+ if(r == ']' && l->canjmp)
+ longjmp(l->jmp, 1);
+ l->canjmp = 0;
+ if(r == 0 || r == '{' || r == '[' || r == ']' || r == '}' || r == ':' || r == ','){
+ l->t = r;
+ return 0;
+ }
+ if(r >= 0x80 || isalpha(r)){
+ t = l->buf;
+ for(;;){
+ t += runetochar(t, &r);
+ if(t >= l->buf + sizeof(l->buf)){
+ werrstr("json: literal too long");
+ return -1;
+ }
+ r = peekch(l);
+ if(r < 0x80 && !isalpha(r))
+ break;
+ getch(l);
+ }
+ *t = 0;
+ if(strcmp(l->buf, "true") == 0)
+ l->t = TTRUE;
+ else if(strcmp(l->buf, "false") == 0)
+ l->t = TFALSE;
+ else if(strcmp(l->buf, "null") == 0)
+ l->t = TNULL;
+ else{
+ werrstr("json: invalid literal");
+ return -1;
+ }
+ return 0;
+ }
+ if(isdigit(r) || r == '-'){
+ l->n = strtod(l->s-1, &l->s);
+ l->t = TNUM;
+ return 0;
+ }
+ if(r == '"'){
+ t = l->buf;
+ for(;;){
+ r = getch(l);
+ if(r == '"')
+ break;
+ if(r < ' '){
+ werrstr("json: invalid char in string %x", r);
+ return -1;
+ }
+ if(r == '\\'){
+ r = getch(l);
+ switch(r){
+ case 'n':
+ r = '\n';
+ break;
+ case 'r':
+ r = '\r';
+ break;
+ case 't':
+ r = '\t';
+ break;
+ case 'f':
+ r = '\f';
+ break;
+ case 'b':
+ r = '\b';
+ break;
+ case '"': case '/': case '\\':
+ break;
+ default:
+ werrstr("json: invalid escape sequence \\%C", r);
+ return -1;
+ }
+ }
+ t += runetochar(t, &r);
+ if(t >= l->buf + sizeof(l->buf)){
+ werrstr("json: string too long");
+ return -1;
+ }
+ }
+ *t = 0;
+ l->t = TSTRING;
+ return 0;
+ }
+ werrstr("json: invalid char %C", peekch(l));
+ return -1;
+}
+
+static JSON*
+jsonobj(Lex *l)
+{
+ JSON *j;
+ JSONEl *e;
+ JSONEl **ln;
+ int obj;
+
+ j = mallocz(sizeof(*j), 1);
+ if(j == nil)
+ return nil;
+ if(lex(l) < 0){
+error:
+ free(j);
+ return nil;
+ }
+ switch(l->t){
+ case TEOF:
+ werrstr("json: unexpected eof");
+ goto error;
+ case TNULL:
+ j->t = JSONNull;
+ break;
+ case TTRUE:
+ j->t = JSONBool;
+ j->n = 1;
+ break;
+ case TFALSE:
+ j->t = JSONBool;
+ j->n = 0;
+ break;
+ case TSTRING:
+ j->t = JSONString;
+ j->s = strdup(l->buf);
+ if(j->s == nil)
+ goto error;
+ break;
+ case TNUM:
+ j->t = JSONNumber;
+ j->n = l->n;
+ break;
+ case '{':
+ case '[':
+ obj = l->t == '{';
+ ln = &j->first;
+ e = nil;
+ if(obj){
+ j->t = JSONObject;
+ if(lex(l) < 0)
+ goto abort;
+ if(l->t == '}')
+ return j;
+ goto firstobj;
+ }else{
+ j->t = JSONArray;
+ l->canjmp = 1;
+ if(setjmp(l->jmp) > 0){
+ free(e);
+ return j;
+ }
+ }
+ for(;;){
+ if(obj){
+ if(lex(l) < 0)
+ goto abort;
+ firstobj:
+ if(l->t != TSTRING){
+ werrstr("json: syntax error, not string");
+ goto abort;
+ }
+ e = mallocz(sizeof(*e), 1);
+ if(e == nil)
+ goto abort;
+ e->name = strdup(l->buf);
+ if(e->name == nil || lex(l) < 0){
+ free(e);
+ goto abort;
+ }
+ if(l->t != ':'){
+ werrstr("json: syntax error, not colon");
+ free(e);
+ goto abort;
+ }
+ }else{
+ e = mallocz(sizeof(*e), 1);
+ if(e == nil)
+ goto abort;
+ }
+ e->val = jsonobj(l);
+ if(e->val == nil){
+ free(e);
+ goto abort;
+ }
+ *ln = e;
+ ln = &e->next;
+ if(lex(l) < 0)
+ goto abort;
+ if(l->t == (obj ? '}' : ']'))
+ break;
+ if(l->t != ','){
+ werrstr("json: syntax error, neither comma nor ending paren");
+ goto abort;
+ }
+ }
+ break;
+ abort:
+ jsonfree(j);
+ return nil;
+ case ']': case '}': case ',': case ':':
+ werrstr("json: unexpected %C", l->t);
+ goto error;
+ default:
+ werrstr("json: the front fell off");
+ goto error;
+ }
+ return j;
+}
+
+JSON*
+jsonparse(char *s)
+{
+ Lex l;
+
+ memset(&l, 0, sizeof(l));
+ l.s = s;
+ return jsonobj(&l);
+}
+
+void
+jsonfree(JSON *j)
+{
+ JSONEl *e, *f;
+
+ switch(j->t){
+ case JSONString:
+ if(j->s)
+ free(j->s);
+ break;
+ case JSONArray: case JSONObject:
+ for(e = j->first; e != nil; e = f){
+ if(e->name)
+ free(e->name);
+ jsonfree(e->val);
+ f = e->next;
+ free(e);
+ }
+ }
+ free(j);
+}
+
+JSON *
+jsonbyname(JSON *j, char *n)
+{
+ JSONEl *e;
+
+ if(j->t != JSONObject){
+ werrstr("not an object");
+ return nil;
+ }
+ for(e = j->first; e != nil; e = e->next)
+ if(strcmp(e->name, n) == 0)
+ return e->val;
+ werrstr("key '%s' not found", n);
+ return nil;
+}
+
+char *
+jsonstr(JSON *j)
+{
+ if(j == nil)
+ return nil;
+ if(j->t != JSONString){
+ werrstr("not a string");
+ return nil;
+ }
+ return j->s;
+}
diff --git a/sys/src/libjson/mkfile b/sys/src/libjson/mkfile
new file mode 100644
index 000000000..3b61d9c89
--- /dev/null
+++ b/sys/src/libjson/mkfile
@@ -0,0 +1,10 @@
+</$objtype/mkfile
+
+LIB=/$objtype/lib/libjson.a
+OFILES=\
+ json.$O
+
+HFILES=\
+ /sys/include/json.h
+
+</sys/src/cmd/mksyslib