Charles.Forsyth20080130-0949a1fa9c718 years agoHistory
619 lines · 10.9 KB · Brainfuck
RawDownload
implement JSON; ## Javascript `Object' Notation (JSON): RFC4627# include "sys.m";	sys: Sys; include "bufio.m";	bufio: Bufio;	Iobuf: import bufio; include "json.m"; init(b: Bufio){	sys = load Sys Sys->PATH;	bufio = b;} jvarray(a: array of ref JValue): ref JValue.Array{	return ref JValue.Array(a);} jvbig(i: big): ref JValue.Int{	return ref JValue.Int(i);} jvfalse(): ref JValue.False{	return ref JValue.False;} jvint(i: int): ref JValue.Int{	return ref JValue.Int(big i);} jvnull(): ref JValue.Null{	return ref JValue.Null;} jvobject(m: list of (string, ref JValue)): ref JValue.Object{	# could `uniq' the labels	return ref JValue.Object(m);} jvreal(r: real): ref JValue.Real{	return ref JValue.Real(r);} jvstring(s: string): ref JValue.String{	return ref JValue.String(s);} jvtrue(): ref JValue.True{	return ref JValue.True;} Syntax: exception(string);Badwrite: exception; readjson(fd: ref Iobuf): (ref JValue, string){	{		p := Parse.mk(fd);		c := p.getns();		if(c == Bufio->EOF)			return (nil, nil);		p.unget(c);		return (readval(p), nil);	}exception e{	Syntax =>		return (nil, sys->sprint("JSON syntax error (offset %bd): %s", fd.offset(), e));	}} writejson(fd: ref Iobuf, val: ref JValue): int{	{		writeval(fd, val);		return 0;	}exception{	Badwrite =>		return -1;	}} ## value ::= string | number | object | array | 'true' | 'false' | 'null'#readval(p: ref Parse): ref JValue raises(Syntax){	{		while((c := p.getc()) == ' ' || c == '\t' || c == '\n' || c == '\r')			{}		if(c < 0){			if(c == Bufio->EOF)				raise Syntax("unexpected end-of-input");			raise Syntax(sys->sprint("read error: %r"));		}		case c {		'{' =>			# object ::= '{' [pair (',' pair)*] '}'			l:  list of (string, ref JValue);			if((c = p.getns()) != '}'){				p.unget(c);				rl: list of (string, ref JValue);				do{					# pair ::= string ':' value					c = p.getns();					if(c != '"')						raise Syntax("missing member name");					name := readstring(p, c);					if(p.getns() != ':')						raise Syntax("missing ':'");					rl = (name, readval(p)) :: rl;				}while((c = p.getns()) == ',');				for(; rl != nil; rl = tl rl)					l = hd rl :: l;			}			if(c != '}')				raise Syntax("missing '}' at end of object");			return ref JValue.Object(l);		'[' =>			#	array ::= '[' [value (',' value)*] ']'			l: list of ref JValue;			n := 0;			if((c = p.getns()) != ']'){				p.unget(c);				do{					l = readval(p) :: l;					n++;				}while((c = p.getns()) == ',');			}			if(c != ']')				raise Syntax("missing ']' at end of array");						a := array[n] of ref JValue;			for(; --n >= 0; l = tl l)				a[n] = hd l;			return ref JValue.Array(a);		'"' =>			return ref JValue.String(readstring(p, c));		'-' or '0' to '9' =>			#	number ::=	int frac? exp?			#	int ::= '-'? [0-9] | [1-9][0-9]+			#	frac ::= '.' [0-9]+			#	exp ::= [eE][-+]? [0-9]+			if(c == '-')				intp := "-";			else				p.unget(c);			intp += readdigits(p);		# we don't enforce the absence of leading zeros			fracp: string;			c = p.getc();			if(c == '.'){				fracp = readdigits(p);				c = p.getc();			}			exp := "";			if(c == 'e' || c == 'E'){				exp[0] = c;				c = p.getc();				if(c == '-' || c == '+')					exp[1] = c;				else					p.unget(c);				exp += readdigits(p);			}else				p.unget(c);			if(fracp != nil || exp != nil)				return ref JValue.Real(real (intp+"."+fracp+exp));			return ref JValue.Int(big intp);		'a' to 'z' =>			# 'true' | 'false' | 'null'			s: string;			do{				s[len s] = c;			}while((c = p.getc()) >= 'a' && c <= 'z');			p.unget(c);			case s {			"true" =>	return ref JValue.True();			"false" =>	return ref JValue.False();			"null" =>	return ref JValue.Null();			* =>	raise Syntax("invalid literal: "+s);			}		* =>			raise Syntax(sys->sprint("unexpected character #%.4ux", c));		}	}exception{	Syntax =>		raise;	}} # string ::= '"' char* '"'# char ::= [^\x00-\x1F"\\] | '\"' | '\/' | '\b' | '\f' | '\n' | '\r' | '\t' | '\u' hex hex hex hexreadstring(p: ref Parse, delim: int): string raises(Syntax){	{		s := "";		while((c := p.getc()) != delim && c >= 0){			if(c == '\\'){				c = p.getc();				if(c < 0)					break;				case c {				'b' =>	c =  '\b';				'f' =>		c =  '\f';				'n' =>	c =  '\n';				'r' =>		c =  '\r';				't' =>		c =  '\t';				'u' =>					c = 0;					for(i := 0; i < 4; i++)						c = (c<<4) | hex(p.getc());				* =>		;	# identity, including '"', '/', and '\'				}			}			s[len s] = c;		}		if(c < 0){			if(c == Bufio->ERROR)				raise Syntax(sys->sprint("read error: %r"));			raise Syntax("unterminated string");		}		return s;	}exception{	Syntax =>		raise;	}} # hex ::= [0-9a-fA-F]hex(c: int): int raises(Syntax){	case c {	'0' to '9' =>		return c-'0';	'a' to 'f' =>		return 10+(c-'a');	'A' to 'F' =>		return 10+(c-'A');	* =>		raise Syntax("invalid hex digit");	}} # digits ::= [0-9]+readdigits(p: ref Parse): string raises(Syntax){	c := p.getc();	if(!(c >= '0' && c <= '9'))		raise Syntax("expected integer literal");	s := "";	s[0] = c;	while((c = p.getc()) >= '0' && c <= '9')		s[len s] = c;	p.unget(c);	return s;} writeval(out: ref Iobuf, o: ref JValue) raises(Badwrite){	{		if(o == nil){			puts(out, "null");			return;		}		pick r := o {		String =>			writestring(out, r.s);		Int =>			puts(out, string r.value);		Real =>			puts(out, string r.value);		Object =>	# '{' [pair (',' pair)*] '}'			putc(out, '{');			for(l := r.mem; l != nil; l = tl l){				if(l != r.mem)					putc(out, ',');				(n, v) := hd l;				writestring(out, n);				putc(out, ':');				writeval(out, v);			}			putc(out, '}');		Array =>	# '[' [value (',' value)*] ']'			putc(out, '[');			for(i := 0; i < len r.a; i++){				if(i != 0)					putc(out, ',');				writeval(out, r.a[i]);			}			putc(out, ']');		True =>			puts(out, "true");		False =>			puts(out, "false");		Null =>			puts(out, "null");		* =>			raise "writeval: unknown value";	# can't happen		}	}exception{	Badwrite =>		raise;	}} writestring(out: ref Iobuf, s: string) raises(Badwrite){	{		putc(out, '"');		for(i := 0; i < len s; i++){			c := s[i];			if(needesc(c))				puts(out, escout(c));			else				putc(out, c);		}		putc(out, '"');	}exception{	Badwrite =>		raise;	}} escout(c: int): string{	case c {	'"' =>		return "\\\"";	'\\' =>	return "\\\\";	'/' =>	return "\\/";	'\b' =>	return "\\b";	'\f' =>	return "\\f";	'\n' =>	return "\\n";	'\t' =>	return "\\t";	'\r' =>	return "\\r";	* =>		return sys->sprint("\\u%.4ux", c);	}} puts(out: ref Iobuf, s: string) raises(Badwrite){	if(out.puts(s) == Bufio->ERROR)		raise Badwrite;} putc(out: ref Iobuf, c: int) raises(Badwrite){	if(out.putc(c) == Bufio->ERROR)		raise Badwrite;} Parse: adt {	input:	ref Iobuf;	eof:		int; 	mk:		fn(io: ref Iobuf): ref Parse;	getc:		fn(nil: self ref Parse): int;	unget:	fn(nil: self ref Parse, c: int);	getns:	fn(nil: self ref Parse): int;}; Parse.mk(io: ref Iobuf): ref Parse{	return ref Parse(io, 0);} Parse.getc(p: self ref Parse): int{	if(p.eof)		return p.eof;	c := p.input.getc();	if(c < 0)		p.eof = c;	return c;} Parse.unget(p: self ref Parse, c: int){	if(c >= 0)		p.input.ungetc();} # skip white spaceParse.getns(p: self ref Parse): int{	while((c := p.getc()) == ' ' || c == '\t' || c == '\n' || c == '\r')		{}	return c;} JValue.isarray(v: self ref JValue): int{	return tagof v == tagof JValue.Array;} JValue.isint(v: self ref JValue): int{	return tagof v == tagof JValue.Int;} JValue.isnumber(v: self ref JValue): int{	return tagof v == tagof JValue.Int || tagof v == tagof JValue.Real;} JValue.isobject(v: self ref JValue): int{	return tagof v == tagof JValue.Object;} JValue.isreal(v: self ref JValue): int{	return tagof v == tagof JValue.Real;} JValue.isstring(v: self ref JValue): int{	return tagof v == tagof JValue.String;} JValue.istrue(v: self ref JValue): int{	return tagof v == tagof JValue.True;} JValue.isfalse(v: self ref JValue): int{	return tagof v == tagof JValue.False;} JValue.isnull(v: self ref JValue): int{	return tagof v == tagof JValue.Null;} JValue.copy(v: self ref JValue): ref JValue{	pick r := v {	True or False or Null =>		return ref *r;	Int =>		return ref *r;	Real =>		return ref *r;	String =>		return ref *r;	Array =>		a := array[len r.a] of ref JValue;		a[0:] = r.a;		return ref JValue.Array(a);	Object =>		return ref *r;	* =>		raise "json: bad copy";	# can't happen	}} JValue.eq(a: self ref JValue, b: ref JValue): int{	if(a == b)		return 1;	if(a == nil || b == nil || tagof a != tagof b)		return 0;	pick r := a {	True or False or Null =>		return 1;	# tags were equal above	Int =>		pick s := b {		Int =>			return r.value == s.value;		}	Real =>		pick s := b {		Real =>			return r.value == s.value;		}	String =>		pick s := b {		String =>			return r.s == s.s;		}	Array =>		pick s := b {		Array =>			if(len r.a != len s.a)				return 0;			for(i := 0; i < len r.a; i++)				if(r.a[i] == nil){

Lines 1–500 of 619.