xref: /inferno-os/appl/lib/json.b (revision 32038f425fe093998bd875b939ccdc3d88b5acfc)
1ee1a8f4aSCharles.Forsythimplement JSON;
2ee1a8f4aSCharles.Forsyth
3ee1a8f4aSCharles.Forsyth#
4ee1a8f4aSCharles.Forsyth# Javascript `Object' Notation (JSON): RFC4627
5ee1a8f4aSCharles.Forsyth#
6ee1a8f4aSCharles.Forsyth
7ee1a8f4aSCharles.Forsythinclude "sys.m";
8ee1a8f4aSCharles.Forsyth	sys: Sys;
9ee1a8f4aSCharles.Forsyth
10ee1a8f4aSCharles.Forsythinclude "bufio.m";
11ee1a8f4aSCharles.Forsyth	bufio: Bufio;
12ee1a8f4aSCharles.Forsyth	Iobuf: import bufio;
13ee1a8f4aSCharles.Forsyth
14ee1a8f4aSCharles.Forsythinclude "json.m";
15ee1a8f4aSCharles.Forsyth
16ee1a8f4aSCharles.Forsythinit(b: Bufio)
17ee1a8f4aSCharles.Forsyth{
18ee1a8f4aSCharles.Forsyth	sys = load Sys Sys->PATH;
19ee1a8f4aSCharles.Forsyth	bufio = b;
20ee1a8f4aSCharles.Forsyth}
21ee1a8f4aSCharles.Forsyth
22ee1a8f4aSCharles.Forsythjvarray(a: array of ref JValue): ref JValue.Array
23ee1a8f4aSCharles.Forsyth{
24ee1a8f4aSCharles.Forsyth	return ref JValue.Array(a);
25ee1a8f4aSCharles.Forsyth}
26ee1a8f4aSCharles.Forsyth
27ee1a8f4aSCharles.Forsythjvbig(i: big): ref JValue.Int
28ee1a8f4aSCharles.Forsyth{
29ee1a8f4aSCharles.Forsyth	return ref JValue.Int(i);
30ee1a8f4aSCharles.Forsyth}
31ee1a8f4aSCharles.Forsyth
32ee1a8f4aSCharles.Forsythjvfalse(): ref JValue.False
33ee1a8f4aSCharles.Forsyth{
34ee1a8f4aSCharles.Forsyth	return ref JValue.False;
35ee1a8f4aSCharles.Forsyth}
36ee1a8f4aSCharles.Forsyth
37ee1a8f4aSCharles.Forsythjvint(i: int): ref JValue.Int
38ee1a8f4aSCharles.Forsyth{
39ee1a8f4aSCharles.Forsyth	return ref JValue.Int(big i);
40ee1a8f4aSCharles.Forsyth}
41ee1a8f4aSCharles.Forsyth
42ee1a8f4aSCharles.Forsythjvnull(): ref JValue.Null
43ee1a8f4aSCharles.Forsyth{
44ee1a8f4aSCharles.Forsyth	return ref JValue.Null;
45ee1a8f4aSCharles.Forsyth}
46ee1a8f4aSCharles.Forsyth
47ee1a8f4aSCharles.Forsythjvobject(m: list of (string, ref JValue)): ref JValue.Object
48ee1a8f4aSCharles.Forsyth{
49ee1a8f4aSCharles.Forsyth	# could `uniq' the labels
50ee1a8f4aSCharles.Forsyth	return ref JValue.Object(m);
51ee1a8f4aSCharles.Forsyth}
52ee1a8f4aSCharles.Forsyth
53ee1a8f4aSCharles.Forsythjvreal(r: real): ref JValue.Real
54ee1a8f4aSCharles.Forsyth{
55ee1a8f4aSCharles.Forsyth	return ref JValue.Real(r);
56ee1a8f4aSCharles.Forsyth}
57ee1a8f4aSCharles.Forsyth
58ee1a8f4aSCharles.Forsythjvstring(s: string): ref JValue.String
59ee1a8f4aSCharles.Forsyth{
60ee1a8f4aSCharles.Forsyth	return ref JValue.String(s);
61ee1a8f4aSCharles.Forsyth}
62ee1a8f4aSCharles.Forsyth
63ee1a8f4aSCharles.Forsythjvtrue(): ref JValue.True
64ee1a8f4aSCharles.Forsyth{
65ee1a8f4aSCharles.Forsyth	return ref JValue.True;
66ee1a8f4aSCharles.Forsyth}
67ee1a8f4aSCharles.Forsyth
68ee1a8f4aSCharles.ForsythSyntax: exception(string);
69ee1a8f4aSCharles.ForsythBadwrite: exception;
70ee1a8f4aSCharles.Forsyth
71ee1a8f4aSCharles.Forsythreadjson(fd: ref Iobuf): (ref JValue, string)
72ee1a8f4aSCharles.Forsyth{
73ee1a8f4aSCharles.Forsyth	{
74ee1a8f4aSCharles.Forsyth		p := Parse.mk(fd);
75ee1a8f4aSCharles.Forsyth		c := p.getns();
76ee1a8f4aSCharles.Forsyth		if(c == Bufio->EOF)
77ee1a8f4aSCharles.Forsyth			return (nil, nil);
78ee1a8f4aSCharles.Forsyth		p.unget(c);
79ee1a8f4aSCharles.Forsyth		return (readval(p), nil);
80ee1a8f4aSCharles.Forsyth	}exception e{
81ee1a8f4aSCharles.Forsyth	Syntax =>
82ee1a8f4aSCharles.Forsyth		return (nil, sys->sprint("JSON syntax error (offset %bd): %s", fd.offset(), e));
83ee1a8f4aSCharles.Forsyth	}
84ee1a8f4aSCharles.Forsyth}
85ee1a8f4aSCharles.Forsyth
86ee1a8f4aSCharles.Forsythwritejson(fd: ref Iobuf, val: ref JValue): int
87ee1a8f4aSCharles.Forsyth{
88ee1a8f4aSCharles.Forsyth	{
89ee1a8f4aSCharles.Forsyth		writeval(fd, val);
90ee1a8f4aSCharles.Forsyth		return 0;
91ee1a8f4aSCharles.Forsyth	}exception{
92ee1a8f4aSCharles.Forsyth	Badwrite =>
93ee1a8f4aSCharles.Forsyth		return -1;
94ee1a8f4aSCharles.Forsyth	}
95ee1a8f4aSCharles.Forsyth}
96ee1a8f4aSCharles.Forsyth
97ee1a8f4aSCharles.Forsyth#
98ee1a8f4aSCharles.Forsyth# value ::= string | number | object | array | 'true' | 'false' | 'null'
99ee1a8f4aSCharles.Forsyth#
100ee1a8f4aSCharles.Forsythreadval(p: ref Parse): ref JValue raises(Syntax)
101ee1a8f4aSCharles.Forsyth{
102ee1a8f4aSCharles.Forsyth	{
103ee1a8f4aSCharles.Forsyth		while((c := p.getc()) == ' ' || c == '\t' || c == '\n' || c == '\r')
104ee1a8f4aSCharles.Forsyth			{}
105ee1a8f4aSCharles.Forsyth		if(c < 0){
106ee1a8f4aSCharles.Forsyth			if(c == Bufio->EOF)
107ee1a8f4aSCharles.Forsyth				raise Syntax("unexpected end-of-input");
108ee1a8f4aSCharles.Forsyth			raise Syntax(sys->sprint("read error: %r"));
109ee1a8f4aSCharles.Forsyth		}
110ee1a8f4aSCharles.Forsyth		case c {
111ee1a8f4aSCharles.Forsyth		'{' =>
112ee1a8f4aSCharles.Forsyth			# object ::= '{' [pair (',' pair)*] '}'
113ee1a8f4aSCharles.Forsyth			l:  list of (string, ref JValue);
114ee1a8f4aSCharles.Forsyth			if((c = p.getns()) != '}'){
115ee1a8f4aSCharles.Forsyth				p.unget(c);
116ee1a8f4aSCharles.Forsyth				rl: list of (string, ref JValue);
117ee1a8f4aSCharles.Forsyth				do{
118ee1a8f4aSCharles.Forsyth					# pair ::= string ':' value
119ee1a8f4aSCharles.Forsyth					c = p.getns();
120ee1a8f4aSCharles.Forsyth					if(c != '"')
121ee1a8f4aSCharles.Forsyth						raise Syntax("missing member name");
122ee1a8f4aSCharles.Forsyth					name := readstring(p, c);
123ee1a8f4aSCharles.Forsyth					if(p.getns() != ':')
124ee1a8f4aSCharles.Forsyth						raise Syntax("missing ':'");
125ee1a8f4aSCharles.Forsyth					rl = (name, readval(p)) :: rl;
126ee1a8f4aSCharles.Forsyth				}while((c = p.getns()) == ',');
127ee1a8f4aSCharles.Forsyth				for(; rl != nil; rl = tl rl)
128ee1a8f4aSCharles.Forsyth					l = hd rl :: l;
129ee1a8f4aSCharles.Forsyth			}
130ee1a8f4aSCharles.Forsyth			if(c != '}')
131ee1a8f4aSCharles.Forsyth				raise Syntax("missing '}' at end of object");
132ee1a8f4aSCharles.Forsyth			return ref JValue.Object(l);
133ee1a8f4aSCharles.Forsyth		'[' =>
134ee1a8f4aSCharles.Forsyth			#	array ::= '[' [value (',' value)*] ']'
135ee1a8f4aSCharles.Forsyth			l: list of ref JValue;
136ee1a8f4aSCharles.Forsyth			n := 0;
137ee1a8f4aSCharles.Forsyth			if((c = p.getns()) != ']'){
138ee1a8f4aSCharles.Forsyth				p.unget(c);
139ee1a8f4aSCharles.Forsyth				do{
140ee1a8f4aSCharles.Forsyth					l = readval(p) :: l;
141ee1a8f4aSCharles.Forsyth					n++;
142ee1a8f4aSCharles.Forsyth				}while((c = p.getns()) == ',');
143ee1a8f4aSCharles.Forsyth			}
144ee1a8f4aSCharles.Forsyth			if(c != ']')
145ee1a8f4aSCharles.Forsyth				raise Syntax("missing ']' at end of array");
146ee1a8f4aSCharles.Forsyth			a := array[n] of ref JValue;
147ee1a8f4aSCharles.Forsyth			for(; --n >= 0; l = tl l)
148ee1a8f4aSCharles.Forsyth				a[n] = hd l;
149ee1a8f4aSCharles.Forsyth			return ref JValue.Array(a);
150ee1a8f4aSCharles.Forsyth		'"' =>
151ee1a8f4aSCharles.Forsyth			return ref JValue.String(readstring(p, c));
152ee1a8f4aSCharles.Forsyth		'-' or '0' to '9' =>
153ee1a8f4aSCharles.Forsyth			#	number ::=	int frac? exp?
154ee1a8f4aSCharles.Forsyth			#	int ::= '-'? [0-9] | [1-9][0-9]+
155ee1a8f4aSCharles.Forsyth			#	frac ::= '.' [0-9]+
156ee1a8f4aSCharles.Forsyth			#	exp ::= [eE][-+]? [0-9]+
157ee1a8f4aSCharles.Forsyth			if(c == '-')
158ee1a8f4aSCharles.Forsyth				intp := "-";
159ee1a8f4aSCharles.Forsyth			else
160ee1a8f4aSCharles.Forsyth				p.unget(c);
161ee1a8f4aSCharles.Forsyth			intp += readdigits(p);		# we don't enforce the absence of leading zeros
162ee1a8f4aSCharles.Forsyth			fracp: string;
163ee1a8f4aSCharles.Forsyth			c = p.getc();
164ee1a8f4aSCharles.Forsyth			if(c == '.'){
165ee1a8f4aSCharles.Forsyth				fracp = readdigits(p);
166ee1a8f4aSCharles.Forsyth				c = p.getc();
167ee1a8f4aSCharles.Forsyth			}
168ee1a8f4aSCharles.Forsyth			exp := "";
169ee1a8f4aSCharles.Forsyth			if(c == 'e' || c == 'E'){
170ee1a8f4aSCharles.Forsyth				exp[0] = c;
171ee1a8f4aSCharles.Forsyth				c = p.getc();
172ee1a8f4aSCharles.Forsyth				if(c == '-' || c == '+')
173ee1a8f4aSCharles.Forsyth					exp[1] = c;
174ee1a8f4aSCharles.Forsyth				else
175ee1a8f4aSCharles.Forsyth					p.unget(c);
176ee1a8f4aSCharles.Forsyth				exp += readdigits(p);
177ee1a8f4aSCharles.Forsyth			}else
178ee1a8f4aSCharles.Forsyth				p.unget(c);
179ee1a8f4aSCharles.Forsyth			if(fracp != nil || exp != nil)
180ee1a8f4aSCharles.Forsyth				return ref JValue.Real(real (intp+"."+fracp+exp));
181ee1a8f4aSCharles.Forsyth			return ref JValue.Int(big intp);
182ee1a8f4aSCharles.Forsyth		'a' to 'z' =>
183ee1a8f4aSCharles.Forsyth			# 'true' | 'false' | 'null'
184ee1a8f4aSCharles.Forsyth			s: string;
185ee1a8f4aSCharles.Forsyth			do{
186ee1a8f4aSCharles.Forsyth				s[len s] = c;
187ee1a8f4aSCharles.Forsyth			}while((c = p.getc()) >= 'a' && c <= 'z');
188ee1a8f4aSCharles.Forsyth			p.unget(c);
189ee1a8f4aSCharles.Forsyth			case s {
190ee1a8f4aSCharles.Forsyth			"true" =>	return ref JValue.True();
191ee1a8f4aSCharles.Forsyth			"false" =>	return ref JValue.False();
192ee1a8f4aSCharles.Forsyth			"null" =>	return ref JValue.Null();
193ee1a8f4aSCharles.Forsyth			* =>	raise Syntax("invalid literal: "+s);
194ee1a8f4aSCharles.Forsyth			}
195ee1a8f4aSCharles.Forsyth		* =>
196ee1a8f4aSCharles.Forsyth			raise Syntax(sys->sprint("unexpected character #%.4ux", c));
197ee1a8f4aSCharles.Forsyth		}
198ee1a8f4aSCharles.Forsyth	}exception{
199ee1a8f4aSCharles.Forsyth	Syntax =>
200ee1a8f4aSCharles.Forsyth		raise;
201ee1a8f4aSCharles.Forsyth	}
202ee1a8f4aSCharles.Forsyth}
203ee1a8f4aSCharles.Forsyth
204ee1a8f4aSCharles.Forsyth# string ::= '"' char* '"'
205ee1a8f4aSCharles.Forsyth# char ::= [^\x00-\x1F"\\] | '\"' | '\/' | '\b' | '\f' | '\n' | '\r' | '\t' | '\u' hex hex hex hex
206ee1a8f4aSCharles.Forsythreadstring(p: ref Parse, delim: int): string raises(Syntax)
207ee1a8f4aSCharles.Forsyth{
208ee1a8f4aSCharles.Forsyth	{
209ee1a8f4aSCharles.Forsyth		s := "";
210ee1a8f4aSCharles.Forsyth		while((c := p.getc()) != delim && c >= 0){
211ee1a8f4aSCharles.Forsyth			if(c == '\\'){
212ee1a8f4aSCharles.Forsyth				c = p.getc();
213ee1a8f4aSCharles.Forsyth				if(c < 0)
214ee1a8f4aSCharles.Forsyth					break;
215ee1a8f4aSCharles.Forsyth				case c {
216ee1a8f4aSCharles.Forsyth				'b' =>	c =  '\b';
217ee1a8f4aSCharles.Forsyth				'f' =>		c =  '\f';
218ee1a8f4aSCharles.Forsyth				'n' =>	c =  '\n';
219ee1a8f4aSCharles.Forsyth				'r' =>		c =  '\r';
220ee1a8f4aSCharles.Forsyth				't' =>		c =  '\t';
221ee1a8f4aSCharles.Forsyth				'u' =>
222ee1a8f4aSCharles.Forsyth					c = 0;
223ee1a8f4aSCharles.Forsyth					for(i := 0; i < 4; i++)
224ee1a8f4aSCharles.Forsyth						c = (c<<4) | hex(p.getc());
225ee1a8f4aSCharles.Forsyth				* =>		;	# identity, including '"', '/', and '\'
226ee1a8f4aSCharles.Forsyth				}
227ee1a8f4aSCharles.Forsyth			}
228ee1a8f4aSCharles.Forsyth			s[len s] = c;
229ee1a8f4aSCharles.Forsyth		}
230ee1a8f4aSCharles.Forsyth		if(c < 0){
231ee1a8f4aSCharles.Forsyth			if(c == Bufio->ERROR)
232ee1a8f4aSCharles.Forsyth				raise Syntax(sys->sprint("read error: %r"));
233ee1a8f4aSCharles.Forsyth			raise Syntax("unterminated string");
234ee1a8f4aSCharles.Forsyth		}
235ee1a8f4aSCharles.Forsyth		return s;
236ee1a8f4aSCharles.Forsyth	}exception{
237ee1a8f4aSCharles.Forsyth	Syntax =>
238ee1a8f4aSCharles.Forsyth		raise;
239ee1a8f4aSCharles.Forsyth	}
240ee1a8f4aSCharles.Forsyth}
241ee1a8f4aSCharles.Forsyth
242ee1a8f4aSCharles.Forsyth# hex ::= [0-9a-fA-F]
243ee1a8f4aSCharles.Forsythhex(c: int): int raises(Syntax)
244ee1a8f4aSCharles.Forsyth{
245ee1a8f4aSCharles.Forsyth	case c {
246ee1a8f4aSCharles.Forsyth	'0' to '9' =>
247ee1a8f4aSCharles.Forsyth		return c-'0';
248ee1a8f4aSCharles.Forsyth	'a' to 'f' =>
249ee1a8f4aSCharles.Forsyth		return 10+(c-'a');
250ee1a8f4aSCharles.Forsyth	'A' to 'F' =>
251ee1a8f4aSCharles.Forsyth		return 10+(c-'A');
252ee1a8f4aSCharles.Forsyth	* =>
253ee1a8f4aSCharles.Forsyth		raise Syntax("invalid hex digit");
254ee1a8f4aSCharles.Forsyth	}
255ee1a8f4aSCharles.Forsyth}
256ee1a8f4aSCharles.Forsyth
257ee1a8f4aSCharles.Forsyth# digits ::= [0-9]+
258ee1a8f4aSCharles.Forsythreaddigits(p: ref Parse): string raises(Syntax)
259ee1a8f4aSCharles.Forsyth{
260ee1a8f4aSCharles.Forsyth	c := p.getc();
261ee1a8f4aSCharles.Forsyth	if(!(c >= '0' && c <= '9'))
262ee1a8f4aSCharles.Forsyth		raise Syntax("expected integer literal");
263ee1a8f4aSCharles.Forsyth	s := "";
264ee1a8f4aSCharles.Forsyth	s[0] = c;
265ee1a8f4aSCharles.Forsyth	while((c = p.getc()) >= '0' && c <= '9')
266ee1a8f4aSCharles.Forsyth		s[len s] = c;
267ee1a8f4aSCharles.Forsyth	p.unget(c);
268ee1a8f4aSCharles.Forsyth	return s;
269ee1a8f4aSCharles.Forsyth}
270ee1a8f4aSCharles.Forsyth
271ee1a8f4aSCharles.Forsythwriteval(out: ref Iobuf, o: ref JValue) raises(Badwrite)
272ee1a8f4aSCharles.Forsyth{
273ee1a8f4aSCharles.Forsyth	{
274ee1a8f4aSCharles.Forsyth		if(o == nil){
275ee1a8f4aSCharles.Forsyth			puts(out, "null");
276ee1a8f4aSCharles.Forsyth			return;
277ee1a8f4aSCharles.Forsyth		}
278ee1a8f4aSCharles.Forsyth		pick r := o {
279ee1a8f4aSCharles.Forsyth		String =>
280ee1a8f4aSCharles.Forsyth			writestring(out, r.s);
281ee1a8f4aSCharles.Forsyth		Int =>
282*32038f42Sforsyth			puts(out, r.text());
283ee1a8f4aSCharles.Forsyth		Real =>
284*32038f42Sforsyth			puts(out, r.text());
285ee1a8f4aSCharles.Forsyth		Object =>	# '{' [pair (',' pair)*] '}'
286ee1a8f4aSCharles.Forsyth			putc(out, '{');
287ee1a8f4aSCharles.Forsyth			for(l := r.mem; l != nil; l = tl l){
288ee1a8f4aSCharles.Forsyth				if(l != r.mem)
289ee1a8f4aSCharles.Forsyth					putc(out, ',');
290ee1a8f4aSCharles.Forsyth				(n, v) := hd l;
291ee1a8f4aSCharles.Forsyth				writestring(out, n);
292ee1a8f4aSCharles.Forsyth				putc(out, ':');
293ee1a8f4aSCharles.Forsyth				writeval(out, v);
294ee1a8f4aSCharles.Forsyth			}
295ee1a8f4aSCharles.Forsyth			putc(out, '}');
296ee1a8f4aSCharles.Forsyth		Array =>	# '[' [value (',' value)*] ']'
297ee1a8f4aSCharles.Forsyth			putc(out, '[');
298ee1a8f4aSCharles.Forsyth			for(i := 0; i < len r.a; i++){
299ee1a8f4aSCharles.Forsyth				if(i != 0)
300ee1a8f4aSCharles.Forsyth					putc(out, ',');
301ee1a8f4aSCharles.Forsyth				writeval(out, r.a[i]);
302ee1a8f4aSCharles.Forsyth			}
303ee1a8f4aSCharles.Forsyth			putc(out, ']');
304ee1a8f4aSCharles.Forsyth		True =>
305ee1a8f4aSCharles.Forsyth			puts(out, "true");
306ee1a8f4aSCharles.Forsyth		False =>
307ee1a8f4aSCharles.Forsyth			puts(out, "false");
308ee1a8f4aSCharles.Forsyth		Null =>
309ee1a8f4aSCharles.Forsyth			puts(out, "null");
310ee1a8f4aSCharles.Forsyth		* =>
311ee1a8f4aSCharles.Forsyth			raise "writeval: unknown value";	# can't happen
312ee1a8f4aSCharles.Forsyth		}
313ee1a8f4aSCharles.Forsyth	}exception{
314ee1a8f4aSCharles.Forsyth	Badwrite =>
315ee1a8f4aSCharles.Forsyth		raise;
316ee1a8f4aSCharles.Forsyth	}
317ee1a8f4aSCharles.Forsyth}
318ee1a8f4aSCharles.Forsyth
319ee1a8f4aSCharles.Forsythwritestring(out: ref Iobuf, s: string) raises(Badwrite)
320ee1a8f4aSCharles.Forsyth{
321ee1a8f4aSCharles.Forsyth	{
322ee1a8f4aSCharles.Forsyth		putc(out, '"');
323ee1a8f4aSCharles.Forsyth		for(i := 0; i < len s; i++){
324ee1a8f4aSCharles.Forsyth			c := s[i];
325ee1a8f4aSCharles.Forsyth			if(needesc(c))
326ee1a8f4aSCharles.Forsyth				puts(out, escout(c));
327ee1a8f4aSCharles.Forsyth			else
328ee1a8f4aSCharles.Forsyth				putc(out, c);
329ee1a8f4aSCharles.Forsyth		}
330ee1a8f4aSCharles.Forsyth		putc(out, '"');
331ee1a8f4aSCharles.Forsyth	}exception{
332ee1a8f4aSCharles.Forsyth	Badwrite =>
333ee1a8f4aSCharles.Forsyth		raise;
334ee1a8f4aSCharles.Forsyth	}
335ee1a8f4aSCharles.Forsyth}
336ee1a8f4aSCharles.Forsyth
337ee1a8f4aSCharles.Forsythescout(c: int): string
338ee1a8f4aSCharles.Forsyth{
339ee1a8f4aSCharles.Forsyth	case c {
340ee1a8f4aSCharles.Forsyth	'"' =>		return "\\\"";
341ee1a8f4aSCharles.Forsyth	'\\' =>	return "\\\\";
342ee1a8f4aSCharles.Forsyth	'/' =>	return "\\/";
343ee1a8f4aSCharles.Forsyth	'\b' =>	return "\\b";
344ee1a8f4aSCharles.Forsyth	'\f' =>	return "\\f";
345ee1a8f4aSCharles.Forsyth	'\n' =>	return "\\n";
346ee1a8f4aSCharles.Forsyth	'\t' =>	return "\\t";
347ee1a8f4aSCharles.Forsyth	'\r' =>	return "\\r";
348ee1a8f4aSCharles.Forsyth	* =>		return sys->sprint("\\u%.4ux", c);
349ee1a8f4aSCharles.Forsyth	}
350ee1a8f4aSCharles.Forsyth}
351ee1a8f4aSCharles.Forsyth
352ee1a8f4aSCharles.Forsythputs(out: ref Iobuf, s: string) raises(Badwrite)
353ee1a8f4aSCharles.Forsyth{
354ee1a8f4aSCharles.Forsyth	if(out.puts(s) == Bufio->ERROR)
355ee1a8f4aSCharles.Forsyth		raise Badwrite;
356ee1a8f4aSCharles.Forsyth}
357ee1a8f4aSCharles.Forsyth
358ee1a8f4aSCharles.Forsythputc(out: ref Iobuf, c: int) raises(Badwrite)
359ee1a8f4aSCharles.Forsyth{
360ee1a8f4aSCharles.Forsyth	if(out.putc(c) == Bufio->ERROR)
361ee1a8f4aSCharles.Forsyth		raise Badwrite;
362ee1a8f4aSCharles.Forsyth}
363ee1a8f4aSCharles.Forsyth
364ee1a8f4aSCharles.ForsythParse: adt {
365ee1a8f4aSCharles.Forsyth	input:	ref Iobuf;
366ee1a8f4aSCharles.Forsyth	eof:		int;
367ee1a8f4aSCharles.Forsyth
368ee1a8f4aSCharles.Forsyth	mk:		fn(io: ref Iobuf): ref Parse;
369ee1a8f4aSCharles.Forsyth	getc:		fn(nil: self ref Parse): int;
370ee1a8f4aSCharles.Forsyth	unget:	fn(nil: self ref Parse, c: int);
371ee1a8f4aSCharles.Forsyth	getns:	fn(nil: self ref Parse): int;
372ee1a8f4aSCharles.Forsyth};
373ee1a8f4aSCharles.Forsyth
374ee1a8f4aSCharles.ForsythParse.mk(io: ref Iobuf): ref Parse
375ee1a8f4aSCharles.Forsyth{
376ee1a8f4aSCharles.Forsyth	return ref Parse(io, 0);
377ee1a8f4aSCharles.Forsyth}
378ee1a8f4aSCharles.Forsyth
379ee1a8f4aSCharles.ForsythParse.getc(p: self ref Parse): int
380ee1a8f4aSCharles.Forsyth{
381ee1a8f4aSCharles.Forsyth	if(p.eof)
382ee1a8f4aSCharles.Forsyth		return p.eof;
383ee1a8f4aSCharles.Forsyth	c := p.input.getc();
384ee1a8f4aSCharles.Forsyth	if(c < 0)
385ee1a8f4aSCharles.Forsyth		p.eof = c;
386ee1a8f4aSCharles.Forsyth	return c;
387ee1a8f4aSCharles.Forsyth}
388ee1a8f4aSCharles.Forsyth
389ee1a8f4aSCharles.ForsythParse.unget(p: self ref Parse, c: int)
390ee1a8f4aSCharles.Forsyth{
391ee1a8f4aSCharles.Forsyth	if(c >= 0)
392ee1a8f4aSCharles.Forsyth		p.input.ungetc();
393ee1a8f4aSCharles.Forsyth}
394ee1a8f4aSCharles.Forsyth
395ee1a8f4aSCharles.Forsyth# skip white space
396ee1a8f4aSCharles.ForsythParse.getns(p: self ref Parse): int
397ee1a8f4aSCharles.Forsyth{
398ee1a8f4aSCharles.Forsyth	while((c := p.getc()) == ' ' || c == '\t' || c == '\n' || c == '\r')
399ee1a8f4aSCharles.Forsyth		{}
400ee1a8f4aSCharles.Forsyth	return c;
401ee1a8f4aSCharles.Forsyth}
402ee1a8f4aSCharles.Forsyth
403ee1a8f4aSCharles.ForsythJValue.isarray(v: self ref JValue): int
404ee1a8f4aSCharles.Forsyth{
405ee1a8f4aSCharles.Forsyth	return tagof v == tagof JValue.Array;
406ee1a8f4aSCharles.Forsyth}
407ee1a8f4aSCharles.Forsyth
408ee1a8f4aSCharles.ForsythJValue.isint(v: self ref JValue): int
409ee1a8f4aSCharles.Forsyth{
410ee1a8f4aSCharles.Forsyth	return tagof v == tagof JValue.Int;
411ee1a8f4aSCharles.Forsyth}
412ee1a8f4aSCharles.Forsyth
413ee1a8f4aSCharles.ForsythJValue.isnumber(v: self ref JValue): int
414ee1a8f4aSCharles.Forsyth{
415ee1a8f4aSCharles.Forsyth	return tagof v == tagof JValue.Int || tagof v == tagof JValue.Real;
416ee1a8f4aSCharles.Forsyth}
417ee1a8f4aSCharles.Forsyth
418ee1a8f4aSCharles.ForsythJValue.isobject(v: self ref JValue): int
419ee1a8f4aSCharles.Forsyth{
420ee1a8f4aSCharles.Forsyth	return tagof v == tagof JValue.Object;
421ee1a8f4aSCharles.Forsyth}
422ee1a8f4aSCharles.Forsyth
423ee1a8f4aSCharles.ForsythJValue.isreal(v: self ref JValue): int
424ee1a8f4aSCharles.Forsyth{
425ee1a8f4aSCharles.Forsyth	return tagof v == tagof JValue.Real;
426ee1a8f4aSCharles.Forsyth}
427ee1a8f4aSCharles.Forsyth
428ee1a8f4aSCharles.ForsythJValue.isstring(v: self ref JValue): int
429ee1a8f4aSCharles.Forsyth{
430ee1a8f4aSCharles.Forsyth	return tagof v == tagof JValue.String;
431ee1a8f4aSCharles.Forsyth}
432ee1a8f4aSCharles.Forsyth
433ee1a8f4aSCharles.ForsythJValue.istrue(v: self ref JValue): int
434ee1a8f4aSCharles.Forsyth{
435ee1a8f4aSCharles.Forsyth	return tagof v == tagof JValue.True;
436ee1a8f4aSCharles.Forsyth}
437ee1a8f4aSCharles.Forsyth
438ee1a8f4aSCharles.ForsythJValue.isfalse(v: self ref JValue): int
439ee1a8f4aSCharles.Forsyth{
440ee1a8f4aSCharles.Forsyth	return tagof v == tagof JValue.False;
441ee1a8f4aSCharles.Forsyth}
442ee1a8f4aSCharles.Forsyth
443ee1a8f4aSCharles.ForsythJValue.isnull(v: self ref JValue): int
444ee1a8f4aSCharles.Forsyth{
445ee1a8f4aSCharles.Forsyth	return tagof v == tagof JValue.Null;
446ee1a8f4aSCharles.Forsyth}
447ee1a8f4aSCharles.Forsyth
448ee1a8f4aSCharles.ForsythJValue.copy(v: self ref JValue): ref JValue
449ee1a8f4aSCharles.Forsyth{
450ee1a8f4aSCharles.Forsyth	pick r := v {
451ee1a8f4aSCharles.Forsyth	True or False or Null =>
452ee1a8f4aSCharles.Forsyth		return ref *r;
453ee1a8f4aSCharles.Forsyth	Int =>
454ee1a8f4aSCharles.Forsyth		return ref *r;
455ee1a8f4aSCharles.Forsyth	Real =>
456ee1a8f4aSCharles.Forsyth		return ref *r;
457ee1a8f4aSCharles.Forsyth	String =>
458ee1a8f4aSCharles.Forsyth		return ref *r;
459ee1a8f4aSCharles.Forsyth	Array =>
460ee1a8f4aSCharles.Forsyth		a := array[len r.a] of ref JValue;
461ee1a8f4aSCharles.Forsyth		a[0:] = r.a;
462ee1a8f4aSCharles.Forsyth		return ref JValue.Array(a);
463ee1a8f4aSCharles.Forsyth	Object =>
464ee1a8f4aSCharles.Forsyth		return ref *r;
465ee1a8f4aSCharles.Forsyth	* =>
466ee1a8f4aSCharles.Forsyth		raise "json: bad copy";	# can't happen
467ee1a8f4aSCharles.Forsyth	}
468ee1a8f4aSCharles.Forsyth}
469ee1a8f4aSCharles.Forsyth
470ee1a8f4aSCharles.ForsythJValue.eq(a: self ref JValue, b: ref JValue): int
471ee1a8f4aSCharles.Forsyth{
472ee1a8f4aSCharles.Forsyth	if(a == b)
473ee1a8f4aSCharles.Forsyth		return 1;
474ee1a8f4aSCharles.Forsyth	if(a == nil || b == nil || tagof a != tagof b)
475ee1a8f4aSCharles.Forsyth		return 0;
476ee1a8f4aSCharles.Forsyth	pick r := a {
477ee1a8f4aSCharles.Forsyth	True or False or Null =>
478ee1a8f4aSCharles.Forsyth		return 1;	# tags were equal above
479ee1a8f4aSCharles.Forsyth	Int =>
480ee1a8f4aSCharles.Forsyth		pick s := b {
481ee1a8f4aSCharles.Forsyth		Int =>
482ee1a8f4aSCharles.Forsyth			return r.value == s.value;
483ee1a8f4aSCharles.Forsyth		}
484ee1a8f4aSCharles.Forsyth	Real =>
485ee1a8f4aSCharles.Forsyth		pick s := b {
486ee1a8f4aSCharles.Forsyth		Real =>
487ee1a8f4aSCharles.Forsyth			return r.value == s.value;
488ee1a8f4aSCharles.Forsyth		}
489ee1a8f4aSCharles.Forsyth	String =>
490ee1a8f4aSCharles.Forsyth		pick s := b {
491ee1a8f4aSCharles.Forsyth		String =>
492ee1a8f4aSCharles.Forsyth			return r.s == s.s;
493ee1a8f4aSCharles.Forsyth		}
494ee1a8f4aSCharles.Forsyth	Array =>
495ee1a8f4aSCharles.Forsyth		pick s := b {
496ee1a8f4aSCharles.Forsyth		Array =>
497ee1a8f4aSCharles.Forsyth			if(len r.a != len s.a)
498ee1a8f4aSCharles.Forsyth				return 0;
499ee1a8f4aSCharles.Forsyth			for(i := 0; i < len r.a; i++)
500ee1a8f4aSCharles.Forsyth				if(r.a[i] == nil){
501ee1a8f4aSCharles.Forsyth					if(s.a[i] != nil)
502ee1a8f4aSCharles.Forsyth						return 0;
503ee1a8f4aSCharles.Forsyth				}else if(!r.a[i].eq(s.a[i]))
504ee1a8f4aSCharles.Forsyth					return 0;
505ee1a8f4aSCharles.Forsyth			return 1;
506ee1a8f4aSCharles.Forsyth		}
507ee1a8f4aSCharles.Forsyth	Object =>
508ee1a8f4aSCharles.Forsyth		pick s := b {
509ee1a8f4aSCharles.Forsyth		Object =>
510ee1a8f4aSCharles.Forsyth			ls := s.mem;
511ee1a8f4aSCharles.Forsyth			for(lr := r.mem; lr != nil; lr = tl lr){
512ee1a8f4aSCharles.Forsyth				if(ls == nil)
513ee1a8f4aSCharles.Forsyth					return 0;
514ee1a8f4aSCharles.Forsyth				(rn, rv) := hd lr;
515ee1a8f4aSCharles.Forsyth				(sn, sv) := hd ls;
516ee1a8f4aSCharles.Forsyth				if(rn != sn)
517ee1a8f4aSCharles.Forsyth					return 0;
518ee1a8f4aSCharles.Forsyth				if(rv == nil){
519ee1a8f4aSCharles.Forsyth					if(sv != nil)
520ee1a8f4aSCharles.Forsyth						return 0;
521ee1a8f4aSCharles.Forsyth				}else if(!rv.eq(sv))
522ee1a8f4aSCharles.Forsyth					return 0;
523ee1a8f4aSCharles.Forsyth			}
524ee1a8f4aSCharles.Forsyth			return ls == nil;
525ee1a8f4aSCharles.Forsyth		}
526ee1a8f4aSCharles.Forsyth	}
527ee1a8f4aSCharles.Forsyth	return 0;
528ee1a8f4aSCharles.Forsyth}
529ee1a8f4aSCharles.Forsyth
530ee1a8f4aSCharles.ForsythJValue.get(v: self ref JValue, mem: string): ref JValue
531ee1a8f4aSCharles.Forsyth{
532ee1a8f4aSCharles.Forsyth	pick r := v {
533ee1a8f4aSCharles.Forsyth	Object =>
534ee1a8f4aSCharles.Forsyth		for(l := r.mem; l != nil; l = tl l)
535ee1a8f4aSCharles.Forsyth			if((hd l).t0 == mem)
536ee1a8f4aSCharles.Forsyth				return (hd l).t1;
537279831cdSCharles.Forsyth		return nil;
538ee1a8f4aSCharles.Forsyth	* =>
539ee1a8f4aSCharles.Forsyth		return nil;
540ee1a8f4aSCharles.Forsyth	}
541ee1a8f4aSCharles.Forsyth}
542ee1a8f4aSCharles.Forsyth
543ee1a8f4aSCharles.Forsyth# might be better if the interface were applicative?
544ee1a8f4aSCharles.Forsyth# this is similar to behaviour of Limbo's own ref adt, though
545ee1a8f4aSCharles.ForsythJValue.set(v: self ref JValue, mem: string, val: ref JValue)
546ee1a8f4aSCharles.Forsyth{
547ee1a8f4aSCharles.Forsyth	pick j := v {
548ee1a8f4aSCharles.Forsyth	Object =>
549ee1a8f4aSCharles.Forsyth		ol: list of (string, ref JValue);
550ee1a8f4aSCharles.Forsyth		for(l := j.mem; l != nil; l = tl l)
551ee1a8f4aSCharles.Forsyth			if((hd l).t0 == mem){
552ee1a8f4aSCharles.Forsyth				l = tl l;
553ee1a8f4aSCharles.Forsyth				for(; ol != nil; ol = tl ol)
554ee1a8f4aSCharles.Forsyth					l = hd ol :: l;
555ee1a8f4aSCharles.Forsyth				j.mem = l;
556ee1a8f4aSCharles.Forsyth				return;
557ee1a8f4aSCharles.Forsyth			}else
558ee1a8f4aSCharles.Forsyth				ol = hd l :: ol;
559ee1a8f4aSCharles.Forsyth		j.mem = (mem, val) :: j.mem;
560ee1a8f4aSCharles.Forsyth	* =>
561ee1a8f4aSCharles.Forsyth		raise "json: set non-object";
562ee1a8f4aSCharles.Forsyth	}
563ee1a8f4aSCharles.Forsyth}
564ee1a8f4aSCharles.Forsyth
565ee1a8f4aSCharles.ForsythJValue.text(v: self ref JValue): string
566ee1a8f4aSCharles.Forsyth{
567ee1a8f4aSCharles.Forsyth	if(v == nil)
568ee1a8f4aSCharles.Forsyth		return "null";
569ee1a8f4aSCharles.Forsyth	pick r := v {
570ee1a8f4aSCharles.Forsyth	True =>
571ee1a8f4aSCharles.Forsyth		return "true";
572ee1a8f4aSCharles.Forsyth	False =>
573ee1a8f4aSCharles.Forsyth		return "false";
574ee1a8f4aSCharles.Forsyth	Null =>
575ee1a8f4aSCharles.Forsyth		return "null";
576ee1a8f4aSCharles.Forsyth	Int =>
577ee1a8f4aSCharles.Forsyth		return string r.value;
578ee1a8f4aSCharles.Forsyth	Real =>
579d764bd87Sforsyth		return sys->sprint("%f", r.value);
580ee1a8f4aSCharles.Forsyth	String =>
581ee1a8f4aSCharles.Forsyth		return quote(r.s);		# quoted, or not?
582ee1a8f4aSCharles.Forsyth	Array =>
583ee1a8f4aSCharles.Forsyth		s := "[";
584ee1a8f4aSCharles.Forsyth		for(i := 0; i < len r.a; i++){
585ee1a8f4aSCharles.Forsyth			if(i != 0)
586ee1a8f4aSCharles.Forsyth				s += ", ";
587ee1a8f4aSCharles.Forsyth			s += r.a[i].text();
588ee1a8f4aSCharles.Forsyth		}
589ee1a8f4aSCharles.Forsyth		return s+"]";
590ee1a8f4aSCharles.Forsyth	Object =>
591ee1a8f4aSCharles.Forsyth		s := "{";
592ee1a8f4aSCharles.Forsyth		for(l := r.mem; l != nil; l = tl l){
593ee1a8f4aSCharles.Forsyth			if(l != r.mem)
594ee1a8f4aSCharles.Forsyth				s += ", ";
595ee1a8f4aSCharles.Forsyth			s += quote((hd l).t0)+": "+(hd l).t1.text();
596ee1a8f4aSCharles.Forsyth		}
597ee1a8f4aSCharles.Forsyth		return s+"}";
598ee1a8f4aSCharles.Forsyth	* =>
599ee1a8f4aSCharles.Forsyth		return nil;
600ee1a8f4aSCharles.Forsyth	}
601ee1a8f4aSCharles.Forsyth}
602ee1a8f4aSCharles.Forsyth
603ee1a8f4aSCharles.Forsythquote(s: string): string
604ee1a8f4aSCharles.Forsyth{
605ee1a8f4aSCharles.Forsyth	ns := "\"";
606ee1a8f4aSCharles.Forsyth	for(i := 0; i < len s; i++){
607ee1a8f4aSCharles.Forsyth		c := s[i];
608ee1a8f4aSCharles.Forsyth		if(needesc(c))
609ee1a8f4aSCharles.Forsyth			ns += escout(c);
610ee1a8f4aSCharles.Forsyth		else
611ee1a8f4aSCharles.Forsyth			ns[len ns] = c;
612ee1a8f4aSCharles.Forsyth	}
613ee1a8f4aSCharles.Forsyth	return ns+"\"";
614ee1a8f4aSCharles.Forsyth}
615ee1a8f4aSCharles.Forsyth
616ee1a8f4aSCharles.Forsythneedesc(c: int): int
617ee1a8f4aSCharles.Forsyth{
618ee1a8f4aSCharles.Forsyth	return c == '"' || c == '\\' || c == '/' || c <= 16r1F;  # '/' is escaped to prevent "</xyz>" looking like an XML end tag(!)
619ee1a8f4aSCharles.Forsyth}
620