M2000 Interpreter Json Class
M2000 Interpreter
MODULE LIB1 {
Class ParserClass {
Private:
Class bStream {
Private:
cnt, Buffer A
Public:
Value (&c) {Try {c=eval(.A, .cnt) : .cnt++:=true}}
Class:
Module Final bStream (a$){
Buffer .A as Integer*Len(a$)
Return .A, 0:=a$
}
}
Func=Lambda->false
char=0
obj=Stack
Function Final IsId {
If .char=34 Then =.IsString(false)
}
Function Final IsTrue {
If .char=0x74 Then If .func() Then If .char=0x72 Then If .func() Then If .char=0x75 Then If .func() Then If .char=0x65 Then PushIt() : =True
Sub PushIt()
Stack .obj {
Push .Boolean(True)
}
End Sub
}
Function Final IsFalse {
If .char=0x66 Then If .func() Then If .char=0x61 Then If .func() Then If .char=0x6c Then If .func() Then If .char=0x73 Then If .func() Then If .char=0x65 Then PushIt() : =True
Sub PushIt()
Stack .obj {
Push .Boolean(False)
}
End Sub
}
Function Final IsNull {
If .char=0x6e Then If .func() Then If .char=0x75 Then If .func() Then If .char=0x6c Then If .func() Then If .char=0x6c Then PushIt() : =True
Sub PushIt()
Stack .obj {
Push .Null()
}
End Sub
}
Function Final IsSemiCol {
If .char=0x3a Then =true
}
Function Final IsComma {
If .char=0x2c Then =true
}
Function Final IsObject {
If .char=123 Else exit
inventory objinv
Stack .obj { Push .Object(objinv)}
.Trim
While .IsId() {
.Trim
If .IsSemiCol() Then {
.Trim
If .IsValue() Then {
Stack .obj {
Shift 2: Append objinv, Letter$:=Group
}
}
} Else Exit
.Trim
If not .IsComma() Then exit
.Trim
}
If .char=125 Then { =true } Else .obj<=Stack : .func<=lambda->0
}
Function Final IsValue {
If .IsString(True) Then {
=True
} Else.if .IsNumber() Then {
=True
} Else.If .IsTrue() Then {
=True
} Else.If .IsFalse() Then {
=True
} Else.If .IsNull() Then {
=True
} Else.if .IsArray() Then {
=True
} Else.if .IsObject() Then {
=True
} Else {
Print "what", .char
Stack .obj { Stack}
.func<=lambda->0
}
}
Function Final Digits (private_stack){
While .func() {
Select Case .char
Case 48 to 57
{
=true
Stack private_stack { Data .char}
}
Else
break
End Select
}
}
Function Final IsNumber {
a=Stack
Select Case .char
Case 45 ' -
{
oldfunc=.func
Stack a { Data .char}
If .Func() Then {
Select Case .char
Case 48
{
Stack a { Data .char}
If .func() Then {
If .char=46 Then {
Fraction()
Exponent()
}
}
}
Case 49 to 57
{
Stack a { Data .char}
If .Digits(a) Then {}
Fraction()
Exponent()
}
Else
a=stack
End Select
}
}
Case 48
{
oldfunc=.func
Stack a { Data .char}
If .func() Then {
If .char=46 Then {
Fraction()
Exponent()
}
}
}
Case 49 to 57
{
oldfunc=.func
Stack a { Data .char}
If .Digits(a) Then {}
Fraction()
Exponent()
}
End Select
If len(a)>0 Then {
b=each(a)
Document D$
While b {
D$=chrcode$(StackItem(b))
}
.func<=oldfunc
If len(D$)>1 Then For i=2 to len(D$) { .Trim}
Stack .obj { Push .Numeric(D$) }
=True
}
' here is an auto exit from function. Sub as command is an exit
Sub Fraction()
If .char=46 Then Stack a { Data .char}
If .Digits(a) Then { }
End Sub
Sub Exponent()
If .char=101 or .char=61 Then {
Stack a { Data .char}
If .func() Then {
If .char=43 or .char=45 Then {
Stack a { Data .char }
If .Digits(a) Else {
a=Stack
}
} Else.If .char>47 and .char<58 Then {
Stack a { Data .char}
If .Digits(a) Then {}
} Else { a=Stack }
}
}
End Sub
}
Function Final IsString (as_object){
If .char=34 Else exit
Document D$
While .func() {
If .char=34 Then 2000
If .char=92 Then {
' special care
If .func() Then {
Select Case .Char
Case 117 'u
GetHex()
Case 114 ' r
.char<=0x0d
Case 110 ' n
.char<=0x0a
Case 116 ' t
.char<=0x09
Case 98 ' b
.char<=0x08
Case 102 ' f
.char<=0x0c
Case 0x22, 0x2f , 0x5c
rem ' need a line always - revision 4
Else
Exit ' not normal
End Select
}
}
D$=chrcode$(.char)
}
Exit
2000 Stack .obj {
Print D$
If as_object Then {Push .JString$(D$)} Else Push D$
} : =True
Sub GetHex()
Local D$
Document D$="0x"
For i=1 to 4 {
If .func() Then {
If Chrcode$(.char) ~ "[0123456789ABCDEFabcdef]" Then {
D$=Chrcode$(.char)
} Else 3000
}
}
If i<>5 Then 3000
.Char<=Eval(D$)
3000 End Sub
}
Function Final IsArray {
If .char=91 Else exit
Dim Gr()
.Trim
If .char=93 Then =true : Stack .obj { Push .Arr(Gr())} : exit
While .IsValue() {
Stack .obj {
Dim Gr(Len(Gr())+1)
Gr(len(Gr())-1)=Group
}
.Trim
If not .IsComma() Then exit
.Trim
}
If .char=93 Then { =true : Stack .obj { Push .Arr(Gr())} } Else .Func<=lambda->false
}
Module Final Trim {
While .func() {
If .char<33 or .char=160 Else exit
}
}
Function Final IsContainer {
.Trim
Select Case chrcode$(.char)
Case "{"
=.IsObject()
Case "["
=.IsArray()
end select
}
Module Final ReadArrayItem (temp, object){
Select Case temp.type$
Case "String","Boolean","Number", "Null"
{
If object Then Error "No object "+quote$(temp.type$)
Push temp.str$
}
Case "Object"
{
If not Empty Then {
Call .ReadObject temp, object, letter$
} Else {
If object Then Push Temp : exit
Push .ser$(group(temp),0)
}
}
Case "Array"
{
If not Empty Then {
' recursion only with Call statement for modules
Call .ReadArrayItem, Array(temp, number), object
} Else {
If object Then Push Temp : exit
Push .ser$(group(temp),0)
}
}
End Select
}
Module Final ReadObject (json, object){
If type$(json)="Inventory" Then {
If exist(json, Letter$) Then {
temp=eval(json)
} Else {
push "none"
Break ' exit Module Final (Break do something Else in Select End Select)
}
} Else temp=json
Select Case temp.type$
Case "String","Boolean","Number", "Null"
{
If object Then Error "No object "+quote$(temp.type$)
Push temp.str$
}
Case "Object"
{
If not Empty Then {
Call .ReadObject temp, object
} Else {
If object Then Push Temp : exit
Push .ser$(group(temp),0)
}
}
Case "Array"
{
If not Empty Then {
Call .ReadArrayItem array(temp, number), object
} Else {
If object Then Push Temp : exit
Push .ser$(group(temp),0)
}
}
End Select
}
Module Final Worker (object){
If match("IN") Or match("IS") Then {
Push object : ShiftBack 2
.ReadObject
} Else {
read Temp
If Type$(Temp)="mArray" Then {
If not Empty Then {
Call .ReadArrayItem, Array(Temp, number), object
} Else {
If object Then Push Temp : exit
Push .ser$(Temp,0)
}
} Else {
If not Empty Then {
Call .ReadObject Temp, object
} Else {
If not Empty Then {
Call .ReadObject Temp, object
} Else {
If object Then Push Temp : exit
If Type$(Temp)="Inventory" Then {
Push .ser$(.Object(Temp),0)
} Else {
Push .ser$(group(Temp),0)
}
}
}
}
}
}
Public:
Class Arr {
Private:
MyValue
Public:
Property Type$ {Value} ="Array"
Value {
=.MyValue
}
Class:
Module Final Arr (.MyValue) {}
}
Class Null {
Property Type$ {Value} ="Null"
Property Str$ {Value}="null"
Value { =0}
}
Class JString$ {
Private:
MyValue$=""
Public:
Property Type$ {Value} ="String"
Property Str$ {
Value{
Link parent MyValue$ to MyValue$
value$=quote$(string$(MyValue$ as json))
}
}
Value {
=.MyValue$
}
Class:
Module Final JString (.MyValue$) {}
}
Class Numeric {
Private:
MyValue$=""
Public:
Property Type$ {Value} ="Number"
Property Str$ {
Value{
Link parent MyValue$ to MyValue$
value$=MyValue$
}
}
Value {
=Val(.MyValue$)
}
Class:
Module Final Numeric {
If match("S") Then {
Read .MyValue$
} Else {
.Myvalue$<=trim$(str$(Number, 1033))
}
}
}
Class Boolean {
Private:
MyValue=false
Public:
Property Type$ {Value} ="Boolean"
Property Str$ {
Value{
Link parent MyValue to MyValue
If MyValue Then {
value$="true"
} Else value$="false"
}
}
Value {
=.MyValue
}
Class:
Module Final Boolean (.MyValue) {}
}
Class Object {
Private:
Inventory MyValue
Public:
Property Type$ {Value} ="Object"
Value {
=.MyValue
}
Class:
Module Final Object (.MyValue) {}
}
Group Ser$
Module Final SetSpace (.ser.space) {
}
Function Final UseDecimalPoint$ {
=str$(val(letter$),"")
}
Function Final ReadNumber$ {
.Worker false
=.UseDecimalPoint$( Letter$)
}
Function Final ReadAnyString$ {
.Worker false
=Letter$
}
Function Final ReadAny {
.Worker true
Read A
=A
}
Function Final Eval {
.func<=Lambda z=.bStream(Letter$) -> {
link .char to c
=z(&c)
}
Stack .obj { Flush}
.char<=0
If .IsContainer() Then {
=StackItem(.obj)
.obj<=Stack
} Else {
inventory emptinv
=.Object(emptinv)
}
}
Group StringValue$ {
Add=false
Del=false
Set (temp) {
Read temp1
If type$(temp)<>"Group" Then error "Need a group"
If not valid(temp.type$="") Then error "not a proper group"
If not valid(temp1.type$="") Then error "not a proper group for value"
Link parent Null() to MyNull()
Null=MyNull()
Dim Base 1, A(1)
b=(,) : Link b to bb()
A(1)=Group(temp)
Do {
again=false
Select Case A(1).type$
Case "Array"
{
If match("N") Then {
Read where
If len(A(1))<=where and Empty Then {
If .add and not .del Then {
cursize=Len(A(1))
b=A(1) ' A(1) has a pointer so now b has the same pointer
Dim bb(where+1) ' need one more because all "automatic arrays" have base 0
Stock bb(cursize) sweep Len(b)-cursize, Group(Null)
} Else Error "Index out of limits"+str$(where)
} Else If where<0 Then Error "Index out of limits "+str$(where)
If Empty Then {
If .del Then {
cursize=Len(A(1))
b=A(1) ' A(1) has a pointer so now b has the same pointer
If where<cursize-1 Then {
Stock bb(where+1) Keep cursize-where, bb(where)
}
Dim bb(cursize-1) ' bb(0) is an empty array
} Else Return A(1), where:=Group(temp1)
} Else {
A(1)=Array(A(1),where)
again=True
}
} Else Error "No Index Found"
}
Case "Object"
{
If match("S") Then {
Read k$
If Exist(A(1), k$) Then {
If Empty Then {
If .del Then {
Delete A(1) , k$
} else {
Return A(1), k$:=Group(temp1)
}
} Else {
A(1)=Eval(A(1))
again=True
}
} else.if .add and not .del Then {
If Empty Then {
Append A(1), k$:=Group(temp1)
} Else Error "No such Tag "+k$
} Else Error "No such Tag "+k$
} Else Error "No Tag Found"
}
End Select
} until not again
}
Value (temp) {
If type$(temp)<>"Group" Then error "Need a group"
If not valid(temp.type$="") Then error "not a proper group"
Dim Base 1, A(1)
A(1)=Group(temp)
Do {
again=false
Select Case A(1).type$
Case "String", "Number", "Null", "Boolean"
Exit
Case "Array"
{
If match("N") Then {
A(1)=Array(A(1), Number)
} Else Error "No Index Found"
again=True
}
Case "Object"
{
If match("S") Then {
If Exist(A(1), Letter$) Then {
A(1)=Eval(A(1))
} Else Error "No such Tag"
} Else Error "No Tag Found"
again=True
}
End Select
} until not again
=A(1).str$
}
}
Class:
Class CreatSerialize$ {
Private:
usen=0
n=0
nl1$={
}
Function Final Jarray$ (json1, n){
A=json1
nl$=.nl1$
If .usen>0 Then {
nl$=nl$+string$(" ", n+.space)
}
document a$
a$="["
If Len(A)>0 Then {
If .usen>0 Then a$=nl$
k=each(A)
M=len(A)-1
while k {
For This {
Temp=array(k)
select Case temp.type$
Case "Number", "Null","Boolean", "String"
a$=temp.str$
Case "Array"
{
nn=0
If .usen>0 Then {
nn=n +.space
}
a$=.Jarray$(Temp, nn, "")
}
Case "Object"
{
nn=0
If .usen>0 Then {
nn=n +.space
}
a$=.Jobject$(Temp, nn,"")
}
Else
a$=" "+temp.type$
end select
If k^<M Then {
a$=", "
If .usen>0 Then a$=nl$
} Else {
If .usen>0 Then a$=.nl1$
}
}
}
} else If .usen>0 Then a$=.nl1$
If .usen>0 Then a$=string$(" ", n)
a$="]"
=a$+letter$
}
Function Final Jobject$ (json1, n){
json=json1
nl$=.nl1$
If .usen>0 Then {
nl$=nl$+string$(" ", n+.space)
}
document a$
a$="{"
If .usen>0 Then a$=nl$
k=each(json)
M=len(json)-1
while k {
a$=quote$(eval$(json, k^)) +" : "
select Case json(k^!).type$
Case "Array"
{
nn=0
If .usen>0 Then {
nn=n +.space
}
a$=.Jarray$(eval(k), nn, "")
}
Case "Boolean", "Null", "Number", "String"
a$=json(k^!).str$
Case "Object"
{
nn=0
If .usen>0 Then {
nn=n +.space
}
a$=.Jobject$(eval(k), nn, "")
}
Else
a$=" "+json( k^!).type$
end select
If k^<M Then {
a$=", "
If .usen>0 Then a$=nl$
} Else {
If .usen>0 Then a$=.nl1$
}
}
If .usen>0 Then a$=string$(" ", n)
a$="}"
=a$+letter$
}
Class Object {
Private:
Inventory MyValue
Public:
Property Type$ {Value} ="Object"
Value {
=.MyValue
}
Class:
Module Final Object (.MyValue) {}
}
Public:
space=10
Value (json, n) {
a$=.nl1$
b$=""
.usen<=n
n--
If n<=0 Then { a$="" : n=0 } Else b$=string$(" ", n)
If type$(json)<>"Group" Then {
If type$(json)="Inventory" Then {
=b$+.Jobject$(.Object(json),n, a$)
} else.if type$(json)="mArray" Then {
=b$+.Jarray$(json, n, a$)
}
} Else {
If json.type$="Object" Then {
=b$+.Jobject$(json, n,a$)
} else.if json.type$="Array" Then {
=b$+.Jarray$(json, n, a$)
}
}
}
}
Module Final ParserClass {
Let .Ser=.CreatSerialize$()
}
}
}