diff options
author | BurnZeZ <brz-9dev@intma.in> | 2013-10-27 15:44:33 -0400 |
---|---|---|
committer | BurnZeZ <brz-9dev@intma.in> | 2013-10-27 15:44:33 -0400 |
commit | 2dc7e311f43c41ecc412c237cbcb6293953c17e9 (patch) | |
tree | c78a94a222c4eabf97db33d83061df7625897afd /sys/src/libjson | |
parent | 632b7adffbd137d9cbe95431fa6919f875c47e5b (diff) |
make libjson from /sys/src/cmd/btc/json.c
Diffstat (limited to 'sys/src/libjson')
-rw-r--r-- | sys/src/libjson/json.c | 326 | ||||
-rw-r--r-- | sys/src/libjson/mkfile | 10 |
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 |