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