Crumbs! I've written my parser and, in a world first for WarpyCode(tm), it worked first time! Seriously, the very first time I ran it it parsed an example from the json.org site!
This is UNPRECEDENTED. I need to go and lie down now.
Type jsondecoder Field txt$ Field i Field curchr$ Field things:TList Method New() things=New TList End Method Method getnext(tokens$[],onlywhitespace=1) oldi=i While i<Len(txt) c$=Chr(txt[i]) i:+1 For token$=EachIn tokens If c=token curchr=c Return 1 EndIf Next If onlywhitespace And (Not (c=" " Or c="~t" Or c="~n" Or c="~r")) i:-1 Return 0 EndIf Wend i=oldi Return 0 End Method Function Create:jsondecoder(txt$) j:jsondecoder=New jsondecoder j.txt=txt j.i=i Return j End Function Method parse() While getnext(["{","["]) Select curchr Case "{" 'new object o:jsonobject=parseobject() If Not o Print "error - couldn't parse object" EndIf things.addlast o Case "[" 'new array a:jsonarray=parsearray() If Not a Print "error - couldn't parse array" EndIf things.addlast a End Select Wend End Method Method parseobject:jsonobject() o:jsonobject=New jsonobject While getnext(["~q","}"]) Select curchr Case "~q" p:jsonpair=parsepair() If Not p Print "error reading pair" EndIf o.pairs.addlast p If Not getnext([",","}"]) Print "error after reading pair - expected either , or }" EndIf If curchr="}" Return o EndIf Case "}" Return o End Select Wend Print "error reading Object - expected a } at least!" End Method Method parsepair:jsonpair() p:jsonpair=New jsonpair p.name=parsestring() If Not getnext([":"]) Print "error reading pair - expected a :" EndIf v:jsonvalue=parsevalue() If Not v Print "error reading pair - couldn't read a value" EndIf p.value=v Return p End Method Method parsearray:jsonarray() a:jsonarray=New jsonarray While getnext(["~q","-","0","1","2","3","4","5","6","7","8","9","{","[","t","f","n","]"]) Select curchr Case "~q","-","0","1","2","3","4","5","6","7","8","9","{","[","t","f","n" i:-1 v:jsonvalue=parsevalue() a.values.addlast v If Not getnext([",","]"]) Print "error - expecting , or ]" EndIf If curchr="]" Return a EndIf Case "]" Return a End Select Wend Print "error - expecting a value or ]" End Method Method parsestring$() oldi=i s$="" While getnext(["~q",""],0) s:+txt[oldi..i-1] Select curchr Case "~q" Return s Case "" Select Chr(txt[i]) Case "~q" s:+"~q" Case "" s:+"" Case "/" s:+"/" Case "b" s:+Chr(8) Case "f" s:+Chr(12) Case "n" s:+"~n" Case "r" s:+"~r" Case "t" s:+"~t" Case "u" s:+parseunicode() End Select i:+1 End Select Wend End Method Method parseunicode$() n:Short=0 For t=1 To 4 n:*16 c=txt[i+t] If c>48 And c<57 n:+c-48 ElseIf c>=65 And c<=70 n:+c-55 ElseIf c>=97 And c<=102 n:+c-87 EndIf Next i:+4 Return Chr(n) End Method Method parsevalue:jsonvalue() If Not getnext(["~q","-","0","1","2","3","4","5","6","7","8","9","{","[","t","f","n"]) Print "error - expecting the beginning of a value" EndIf Select curchr Case "~q" s$=parsestring() Return jsonstringvalue.Create(s) Case "-","0","1","2","3","4","5","6","7","8","9" n:Double=parsenumber() Return jsonnumbervalue.Create(n) Case "{" o:jsonobject=parseobject() Return o Case "[" a:jsonarray=parsearray() Return a Case "t" i:+3 Return jsonliteralvalue.Create(1) Case "f" i:+4 Return jsonliteralvalue.Create(0) Case "n" i:+2 Return jsonliteralvalue.Create(-1) End Select End Method Method parsenumber:Double() i:-1 sign=1 n:Double=0 Select Chr(txt[i]) Case "-" i:+2 Return parsenumber()*(-1) Case "0" i:+1 If getnext(["."]) n=parsefraction() EndIf Case "1","2","3","4","5","6","7","8","9" n=parseinteger() If getnext(["."]) n:+parsefraction() EndIf End Select If Chr(txt[i])="e" Or Chr(txt[i])="E" i:+1 Select Chr(txt[i]) Case "+" sign=1 Case "-" sign=-1 Default Print "error - not a + or - when reading exponent in number" End Select e=parseinteger() n:*10^(sign*e) EndIf Return n End Method Method parsefraction:Double() digits=1 n:Double=0 While txt[i]>=48 And txt[i]<=57 And i<Len(txt) n:*10 n:+txt[i]-48 i:+1 digits:+1 Wend n:/digits If i=Len(txt) Print "error - reached EOF while reading number" EndIf Return n End Method Method parseinteger:Double() n:Double=0 While txt[i]>=48 And txt[i]<=57 And i<Len(txt) n:*10 n:+txt[i]-48 i:+1 Wend If i=Len(txt) Print "error - reached EOF while reading number" EndIf Return n End Method End Type Type jsonvalue Method repr$() Return "" End Method End Type Type jsonobject Extends jsonvalue Field pairs:TList Method New() pairs=New TList End Method Method repr$() t$="{ " op:jsonpair=Null For p:jsonpair=EachIn pairs If op Then t:+" , " t:+p.repr() op=p Next t:+" }" Return t End Method End Type Type jsonpair Field name$,value:jsonvalue Method repr$() Return "~q"+name+"~q : "+value.repr() End Method End Type Type jsonarray Extends jsonvalue Field values:TList Method New() values=New TList End Method Method repr$() t$="[ " ov:jsonvalue=Null For v:jsonvalue=EachIn values If ov Then t:+" , " t:+v.repr() ov=v Next t:+" ]" Return t End Method End Type Type jsonstringvalue Extends jsonvalue Field txt$ Function Create:jsonstringvalue(txt$) jsv:jsonstringvalue=New jsonstringvalue jsv.txt=txt Return jsv End Function Method repr$() Return "~q"+txt+"~q" End Method End Type Type jsonnumbervalue Extends jsonvalue Field number:Double Function Create:jsonnumbervalue(n:Double) jnv:jsonnumbervalue=New jsonnumbervalue jnv.number=n Return jnv End Function Method repr$() Return String(number) End Method End Type Type jsonliteralvalue Extends jsonvalue Field value '1 - true '0 - false '-1 - nil Function Create:jsonliteralvalue(value) jlv:jsonliteralvalue=New jsonliteralvalue jlv.value=value Return jlv End Function Method repr$() Select value Case 1 Return "true" Case 0 Return "false" Case -1 Return "nil" End Select End Method End Type 'EXAMPLE f:TStream=ReadFile("json.txt") txt$="" While Not Eof(f) txt:+f.ReadLine() Wend j:jsondecoder=jsondecoder.Create(txt) j.parse() For v:jsonvalue=EachIn j.things Print v.repr() Next
This is UNPRECEDENTED. I need to go and lie down now.