diff options
Diffstat (limited to 'json.c')
-rw-r--r-- | json.c | 248 |
1 files changed, 248 insertions, 0 deletions
@@ -0,0 +1,248 @@ +#include<u.h> +#include<libc.h> +#include<json.h> + +static void* +erealloc(void *ptr, ulong sz) +{ + ptr = realloc(ptr, sz); + if(ptr == nil) + sysfatal("realloc: %r"); + return ptr; +} + +void +Jinit(Jparser *p) +{ + memset(p, 0, sizeof *p); +} + +void +Jterm(Jparser *p) +{ + if(p->mtok > 0 || p->tokens != nil) + free(p->tokens); +} + +static Jtok* +gettok(Jparser *p) +{ + uint i; + + if(p->ntok == p->mtok){ + p->mtok = (p->ntok << 1) + 1; + p->tokens = erealloc(p->tokens, p->mtok * sizeof *p->tokens); + for(i = p->ntok; i < p->mtok; i++) + memset(&p->tokens[i], 0, sizeof *p->tokens); + } + + return &p->tokens[p->ntok++]; +} + +static char * +parsestr(Jparser *p, char *s) +{ + Jtok *tok; + + tok = gettok(p); + tok->type = JStr; + for(tok->start = ++s; *s != '\0' && utfrune("\"", *s) == nil; s++) + if(utfrune("\\", *s) != nil) + s++; + tok->end = s; + return s; +} + +static char * +saccept(char *src, char *str) +{ + while(*src != '\0' && utfrune(str, *src) != nil) + src++; + return src; +} + +static void +incnsub(Jparser *p) +{ + if(p->stktop > 0) + p->tokens[p->tokstk[p->stktop - 1]].nsub++; +} + +int +Jtokenise(Jparser *p, char *s) +{ + Jtok *tok; + Jtype type; + + memset(p->tokens, 0, p->mtok * sizeof *p->tokens); + + for(; *s != '\0'; s++){ + if(utfrune("\t\r\n ", *s) != nil) + continue; + + if(utfrune(":", *s) != nil) + if(p->ntok > 0 && p->tokens[p->ntok - 1].type == JStr) + continue; + else{ + werrstr("unexpected :"); + return -1; + } + + if(utfrune(",", *s) != nil) + if(p->stktop != 0) + continue; + else{ + werrstr(", outside of object or array"); + return -1; + } + + if(utfrune("{[", *s) != nil){ + tok = gettok(p); + tok->start = s; + if(p->stktop == JStksz){ + werrstr("stack overflow"); + return -1; + } + incnsub(p); + p->tokstk[p->stktop++] = p->ntok - 1; + tok->type = utfrune("{", *s) != nil ? JObj:JArr; + continue; + } + + if(utfrune("}]", *s) != nil){ + type = utfrune("}", *s) != nil ? JObj:JArr; + if(p->ntok == 0) + goto Jmatcherr; + for(tok = &p->tokens[p->ntok - 1]; tok >= p->tokens; tok--){ + if(tok->start != nil && tok->end == nil){ + if(tok->type != type){ + werrstr("expected %c", type == JObj ? '}':']'); + return -1; + } + if(p->stktop == 0){ + werrstr("stack underflow"); + return -1; + } + p->stktop--; + tok->end = s + 1; + goto Jnext; + } + } +Jmatcherr: + werrstr("no matching %c", type == JObj ? '{':'['); + return -1; + } + + if(utfrune("\"", *s) != nil){ + s = parsestr(p, s); + if(*s == '\0' || utfrune("\"", *s) == nil){ + werrstr("expected \""); + return -1; + } + incnsub(p); + continue; + } + + if(utfrune("tf", *s) != nil){ + tok = gettok(p); + tok->type = JBool; + tok->start = s; + tok->end = s + 1; + incnsub(p); + continue; + } + + if(utfrune("n", *s) != nil){ + tok = gettok(p); + tok->type = JNil; + tok->start = s; + tok->end = s + 1; + incnsub(p); + continue; + } + + /* Try and parse a number */ + tok = gettok(p); + tok->type = JNum; + tok->start = s; + s = saccept(s, "+-"); + s = saccept(s, "0123456789"); + s = saccept(s, "."); + s = saccept(s, "0123456789"); + s = saccept(s, "eE"); + s = saccept(s, "+-"); + s = saccept(s, "0123456789"); + tok->end = s; + + if(*s != '\0' && utfrune("\t\r\n }],", *s) == nil){ + werrstr("number format error"); + return -1; + } + + incnsub(p); + s--; +Jnext:; + } + + if(p->stktop != 0){ + werrstr("unexpected end of input"); + return -1; + } + + return 0; +} + +char * +Jtokstr(Jtok *t) +{ + static char *buf; + static ulong sz = 0; + ulong len; + char *src, *dst; + + len = t->end - t->start; + if(sz <= len){ + sz = len + 1; + buf = erealloc(buf, sz); + } + + for(src = t->start, dst = buf; len > 0; len--){ + if(*src == '\\') + src++, len--; + *dst++ = *src++; + } + *dst = '\0'; + return buf; +} + +uint +Jnext(Jparser *p, uint i) +{ + int j; + if(i >= p->ntok) + return i; + if(p->tokens[i].type != JObj && p->tokens[i].type != JArr) + return i + 1; + for(j = i; j < p->ntok && p->tokens[i].end > p->tokens[j].start; j++); + return j; +} + +int +Jfind(Jparser *p, uint r, char *name) +{ + uint i, j; + assert(r < p->ntok); + assert(p->tokens[r].type == JObj); + assert(p->tokens[r].nsub % 2 == 0); + + for(i = 0, j = r + 1; i < p->tokens[r].nsub; i += 2){ + assert(p->tokens[j].type == JStr); + if(strcmp(Jtokstr(&p->tokens[j]), name) == 0) + return j + 1; + j = Jnext(p, j + 1); + } + + if(j >= p->ntok) + return -1; + return j; +} |