aboutsummaryrefslogtreecommitdiff
path: root/json.c
diff options
context:
space:
mode:
Diffstat (limited to 'json.c')
-rw-r--r--json.c248
1 files changed, 248 insertions, 0 deletions
diff --git a/json.c b/json.c
new file mode 100644
index 0000000..9f52bc7
--- /dev/null
+++ b/json.c
@@ -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;
+}