391 lines · 5.9 KB · C
RawDownload
#include <u.h>#include <libc.h>#include <ctype.h>#include <json.h> typedef struct Lex Lex; enum {	TEOF,	TSTRING = Runemax+1,	TNUM,	TNULL,	TFALSE,	TTRUE,}; struct Lex{	char *s;	ulong slen;	int t;	double n;	char *buf;	Rune peeked;}; static Runegetch(Lex *l){	Rune r; 	if(l->peeked){		r = l->peeked;		l->peeked = 0;		return r;	}	if(l->s[0] == '\0')		return 0;	l->s += chartorune(&r, l->s);	return r;} static Runepeekch(Lex *l){	if(!l->peeked)		l->peeked = getch(l);	return l->peeked;} static Runepeeknonspace(Lex *l){	Rune r; 	for(;;){		r = peekch(l);		if(r != 0x20 && r != 0x09 && r != 0x0A && r != 0x0D)			break;		getch(l);	}	return r;} static intfixsurrogate(Rune *rp, Rune r2){	Rune r1; 	r1 = *rp;	if(r1 >= 0xD800 && r1 <= 0xDBFF){		if(r2 >= 0xDC00 && r2 <= 0xDFFF){			*rp = 0x10000 + (((r1 - 0xD800)<<10) | (r2 - 0xDC00));			return 0;		}		return 1;	} else	if(r1 >= 0xDC00 && r1 <= 0xDFFF){		if(r2 >= 0xD800 && r2 <= 0xDBFF){			*rp = 0x10000 + (((r2 - 0xD800)<<10) | (r1 - 0xDC00));			return 0;		}		return 1;	}	return 0;} static intlex(Lex *l){	Rune r, r2;	char *t;	int i;	char c; 	peeknonspace(l);	r = getch(l);	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 + l->slen){				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 == '"'){		r2 = 0;		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 'u':					r = 0;					for(i = 0; i < 4; i++){						if(!isxdigit(peekch(l)))							break; 						c = getch(l);						r *= 16;						if(c >= '0' && c <= '9')							r += c - '0';						else if(c >= 'a' && c <= 'f')							r += c - 'a' + 10;						else if(c >= 'A' && c <= 'F')							r += c - 'A' + 10;					}					if(fixsurrogate(&r, r2)){						r2 = r;						continue;					}					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;				}			}			r2 = 0;			t += runetochar(t, &r);			if(t >= l->buf + l->slen){				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; 	if((j = mallocz(sizeof(*j), 1)) == 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;		if((j->s = strdup(l->buf)) == nil)			goto error;		break;	case TNUM:		j->t = JSONNumber;		j->n = l->n;		break;	case '{':	case '[':		obj = l->t == '{';		ln = &j->first;		if(obj){			j->t = JSONObject;			if(lex(l) < 0)				goto abort;			if(l->t == '}')				return j;			goto firstobj;		}else{			j->t = JSONArray;			if(peeknonspace(l) == ']'){				getch(l);				return j;			}		}		for(;;){			if(obj){				if(lex(l) < 0)					goto abort;			firstobj:				if(l->t != TSTRING){					werrstr("json: syntax error, not string");					goto abort;				}				if((e = mallocz(sizeof(*e), 1)) == 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{				if((e = mallocz(sizeof(*e), 1)) == 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){	JSON *j;	Lex l; 	memset(&l, 0, sizeof(l));	l.s = s;	l.slen = strlen(s);	if((l.buf = mallocz(l.slen+UTFmax+1, 1)) == nil)		return nil; 	j = jsonobj(&l);	free(l.buf); 	if(peeknonspace(&l) != 0){		jsonfree(j);		werrstr("json: unexpected trailing data");		return nil;	} 	return j;} voidjsonfree(JSON *j){	JSONEl *e, *f; 	if(j == nil)		return;	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;}