import System.Collections import Harmony.TraditionalBridge .ifdef DBLNET .define STRING_AT(inputValue, index) inputValue[index] .else .define STRING_AT(inputValue, index) inputValue(index:1) .endc .define IsNumber(ch) (ch >= '0' && ch <= '9') .define MaybeLog(priority, msg) if((priority) <= Harmony.TraditionalBridge.Logger.LogLevel) Harmony.TraditionalBridge.Logger.Instance.Log(msg) namespace Json enum JSON_TYPE OBJ, ARRAY_VAL, TEXT, BOOL, NULL, INT_VAL, NUMBER, endenum class Json private inputValue, @string private blankSet, @string private endOfBlockSet, @string public method ParseJson, @JsonValue req in inputValue, @string endparams record readPosition, int proc readPosition = 1 blankSet = " " + %char(10) + %char(13) + %char(9) + %char(12) endOfBlockSet = "}]" this.inputValue = inputValue mreturn ReadValue(readPosition) endmethod private method FindNumber, int req in currentPos, int endparams record tempPos, int ch, a1 proc tempPos = currentPos while(tempPos <= inputValue.Length) begin ch = STRING_AT(inputValue, tempPos) if(!IsNumber(ch)) mreturn tempPos incr(tempPos) end mreturn 0 endmethod private static method Substring, @string req in startPos, int req in endPos, int req in inputValue, @string proc if((endpos - startPos) > 0) then mreturn inputValue(startPos:(endpos - startPos)) else mreturn "" endmethod private method IsEndOfPrimitive, boolean req in currentPos, int endparams record ch, a1 ;was char caused corrupt handle proc ch = STRING_AT(inputValue, currentPos) mreturn ch == ',' || endOfBlockSet.Contains(ch) || blankSet.Contains(ch) endmethod private method GetEscape, a req in value, a record byteVal, i1 record, x aVal, a1 proc aVal = value case byteVal of begincase 34: mreturn '"' 92: mreturn '\' 47: mreturn '/' 98: mreturn %char(8) 102: mreturn %char(12) 110: mreturn %char(10) 114: mreturn %char(13) 116: mreturn %char(9) endcase else mreturn aVal endmethod private method IsHexDigit, boolean req in value, char proc if ( value < '0' ) mreturn false if ( value <= '9' ) mreturn true if ( value < 'A' ) mreturn false if ( value <= 'F' ) mreturn true if ( value < 'a' ) mreturn false if ( value <= 'f' ) mreturn true mreturn false endmethod ;;this method wont really work properly on traditional private method GetCharFromUnicode, char req in currentPos, int record i, int proc i = 0 while(i < 4) begin if (!IsHexDigit(STRING_AT(inputValue, i))) mreturn %char(0); incr i end mreturn '?'; endmethod private method ParseString, @string req inout currentPos, int record headPos, int bufPos, int stringBuf, @string proc stringBuf = ^null bufPos = currentPos headPos = currentPos; while(inputValue.Length > headPos && STRING_AT(inputValue, headPos) >= ' ') begin if(STRING_AT(inputValue, headPos) == '"') then begin currentPos = headPos if(stringBuf != ^null) then mreturn stringBuf + Substring(bufPos, headPos, inputValue) else mreturn Substring(bufPos, headPos, inputValue) if(stringBuf == ^null) then begin if(bufPos < (headPos - 1)) then mreturn stringBuf + Substring(bufPos, headPos, inputValue) else mreturn stringBuf end else begin if(bufPos < (headPos - 1)) then mreturn Substring(bufPos, headPos, inputValue) else mreturn "" end end else if(STRING_AT(inputValue, headPos) == '\') begin incr headPos if(STRING_AT(inputValue, headPos) == 'u') then begin throw new Exception("unicode not implemented") ;data ch = getCharFromUnicode(headPos + 1) ;if(ch == %char(0)) mreturn 0; ;STRING_AT(inputValue, tailPos) = ch ;headPos += 4 end else begin data esc, a1, GetEscape(STRING_AT(inputValue, headPos)) if(^i(esc) == 0) mreturn ^null if(stringBuf == ^null) then begin if(bufPos < (headPos - 1)) then stringBuf = Substring(bufPos, (headPos - 1), inputValue) + esc else stringBuf = esc end else begin if(bufPos < (headPos - 1)) then stringBuf += Substring(bufPos, (headPos - 1), inputValue) + esc else stringBuf += esc end bufPos = headPos + 1 end end incr headPos end mreturn ^null endmethod private method ReadPropertyName, @string req inout currentPos, int endparams record parsedString, @string proc incr currentPos parsedString = ParseString(currentPos) if(parsedString == ^null) mreturn ^null currentPos = SkipAny(currentPos, blankSet) if(!currentPos) mreturn ^null incr currentPos if(STRING_AT(inputValue, currentPos) != ':') mreturn ^null currentPos = SkipAny(currentPos, blankSet) mreturn parsedString endmethod private method TextValue, @JsonText req inout currentPos, int endparams record parsedString, @string result, @JsonText proc incr currentPos parsedString = ParseString(currentPos) if(parsedString == ^null) mreturn ^null result = new JsonText() { Value = parsedString, JsonType = JSON_TYPE.TEXT } mreturn result endmethod private method ValidateString, int req in firstPos, int req in secondInput, @string endparams record firstCurrent, int secondCurrent, int proc secondCurrent = 1 firstCurrent = firstPos while( secondInput.Length >= secondCurrent && inputValue.Length >= firstCurrent) begin data inputChar, a1, STRING_AT(inputValue, firstCurrent) data secondChar, a1, STRING_AT(secondInput, secondCurrent) upcase inputChar upcase secondChar if(inputChar != secondChar) mreturn 0 incr firstCurrent incr secondCurrent end mreturn firstCurrent endmethod private method PrimitiveValue, @JsonValue req inout currentPos, int req in value, @string req in jsonType, JSON_TYPE proc currentPos = ValidateString(currentPos, value) if(!currentPos || !IsEndOfPrimitive(currentPos)) then mreturn ^null else begin if(jsonType == JSON_TYPE.BOOL) then begin data result, @JsonBoolean data boolValue = false if(value == "true") boolValue = true MaybeLog(6, "got bool primative value " + value) result = new JsonBoolean() { Value = boolValue, JsonType = jsonType } mreturn result end else if(jsonType == JSON_TYPE.NULL) then begin data result, @JsonNull result = new JsonNull() result.JsonType = jsonType ; result.value = ^null ; { Value = value == "" ? true : false, JsonType = jsonType } mreturn result end else throw new Exception("Not Implemented") end endmethod private method ExponentValue, int req in currentPos, int endparams record tempCurrentPos, int ch, a1 proc tempCurrentPos = currentPos ch = STRING_AT(inputValue, tempCurrentPos) if(ch == '-' || ch == '+') begin incr tempCurrentPos ch = STRING_AT(inputValue, tempCurrentPos) end if(!IsNumber(ch)) mreturn 0 mreturn FindNumber(tempCurrentPos) endmethod private method FractionValue, int req in currentPos, int endparams record tempCurrentPos, int ch, a1 proc tempCurrentPos = currentPos ch = STRING_AT(inputValue, tempCurrentPos) if(!IsNumber(ch)) mreturn 0 incr tempCurrentPos mreturn FindNumber(tempCurrentPos) endmethod private method NumberValue, @JsonValue req inout currentPos, int endparams record jsonType, JSON_TYPE startPos, int value, @string ch, a1 numberPos, i4 proc startPos = currentPos ch = STRING_AT(inputValue, currentPos) if(ch == '-') begin incr currentPos ch = STRING_AT(inputValue, currentPos) end if(!IsNumber(ch)) ReportFailure(inputValue, currentPos, "Not a number") if(ch != '0') then begin numberPos = FindNumber(currentPos) if(!numberPos) ReportFailure(inputValue, currentPos, "A Number") currentPos = numberPos ch = STRING_AT(inputValue, currentPos) end else begin incr currentPos ch = STRING_AT(inputValue, currentPos) if(IsNumber(ch)) begin ReportFailure(inputValue, currentPos, "A Number") end end jsonType = JSON_TYPE.INT_VAL if(ch == '.') begin incr currentPos numberPos = FractionValue(currentPos) if(!numberPos) ReportFailure(inputValue, currentPos, "Fraction Value") currentPos = numberPos ch = STRING_AT(inputValue, currentPos) jsonType = JSON_TYPE.NUMBER end if(ch == 'e' || ch == 'E') begin incr currentPos numberPos = ExponentValue(currentPos) if(!numberPos) ReportFailure(inputValue, currentPos, "Exponent Value") currentPos = numberPos ch = STRING_AT(inputValue, currentPos) jsonType = JSON_TYPE.NUMBER end if(!IsEndOfPrimitive(currentPos)) ReportFailure(inputValue, currentPos, "End Of Primative") value = Substring(startPos, currentPos, inputValue) if(jsonType == JSON_TYPE.INT_VAL) then begin data numericValue, d28, %implied((a)value) data negative, boolean, STRING_AT(inputValue, startPos) == '-' data maxDigits, int;, negative ? 20 : 19 data len, int, currentPos - startPos data resultInt, @JsonInt if (negative) then maxDigits = 20 else maxDigits = 19 if(len > maxDigits) ReportFailure(inputValue, currentPos, "Fewer digits") if(len == maxDigits) begin if(numericValue > 9223372036854775808 || numericValue < -9223372036854775807) ReportFailure(inputValue, currentPos, "Smaller number") end resultInt = new JsonInt() { Value = numericValue, JsonType = jsonType } mreturn resultInt end else begin data resultNumber, @JsonNumber data dvar, double dvar = %implied((a)value) resultNumber = new JsonNumber() { JsonType = jsonType, Value = dvar } mreturn resultNumber end endmethod private method ReadValue, @JsonValue req inout currentPos, int endparams record jsonType, JSON_TYPE resultValue, @JsonValue parentValues, @ArrayList currentValue, @JsonValue currentProperty, @string casevar, int proc parentValues = new ArrayList() jsonType = JSON_TYPE.OBJ currentPos = SkipAny(currentPos, blankSet) if(STRING_AT(inputValue, currentPos) == '{') then begin jsonType = JSON_TYPE.OBJ MaybePushResult(currentValue, parentValues, resultValue, new JsonObject()) end else if(STRING_AT(inputValue, currentPos) == '[') then begin jsonType = JSON_TYPE.ARRAY_VAL MaybePushResult(currentValue, parentValues, resultValue, new JsonArray()) end else ReportFailure(inputValue, currentPos, "Object or Array") incr currentPos while(currentPos <= inputValue.Length) begin data endChar = '}' data readChar, a1 currentPos = SkipAny(currentPos, blankSet) if(!currentPos) ReportFailure(inputValue, currentPos, "Unknown") if(STRING_AT(inputValue, currentPos) == ',') begin incr currentPos nextloop end if(jsonType != JSON_TYPE.OBJ) endChar = ']' readChar = STRING_AT(inputValue, currentPos) if(STRING_AT(inputValue, currentPos) == endChar) then begin ;;data parentValue = parentValues.Count > 0 ? (JsonObject)parentValues[parentValues.Count - 1] : (JsonObject)^null data parentValue, @JsonValue, ^null if(parentValues.Count > 0) begin parentValue = (JsonValue)parentValues[parentValues.Count - 1] parentValues.RemoveAt(parentValues.Count - 1) end currentValue = parentValue currentProperty = ^null jsonType = currentValue != ^null ? currentValue.JsonType : JSON_TYPE.OBJ end else begin if(jsonType != JSON_TYPE.ARRAY_VAL && currentProperty == ^null) begin if(STRING_AT(inputValue, currentPos) != '"') ReportFailure(inputValue, currentPos, '"') currentProperty = ReadPropertyName(currentPos) if(currentProperty == ^null) ReportFailure(inputValue, currentPos, "PROPERTY_NAME") end casevar = ^i((a)STRING_AT(inputValue, currentPos)) case (casevar) of begincase 58: ; colon nop 44: ; comma nop 123: ; { begin data currentValueTemp = currentValue jsonType = JSON_TYPE.OBJ currentValue = MaybeAdd(currentProperty, currentValueTemp, MaybePushResult(currentValue, parentValues, resultValue, new JsonObject())) end 124: begin end 125: ; } begin jsonType = JSON_TYPE.ARRAY_VAL end 91: ; [ begin data currentValueTemp = currentValue jsonType = JSON_TYPE.ARRAY_VAL currentValue = MaybeAdd(currentProperty, currentValueTemp, MaybePushResult(currentValue, parentValues, resultValue, new JsonArray())) end 93: ; ] begin jsonType = JSON_TYPE.ARRAY_VAL end 34: ; " MaybeAdd(currentProperty, currentValue, TextValue(currentPos)) 84: ; T begin MaybeAdd(currentProperty, currentValue, PrimitiveValue(currentPos, "true", JSON_TYPE.BOOL)) nextloop end 116:; t begin MaybeAdd(currentProperty, currentValue, PrimitiveValue(currentPos, "true", JSON_TYPE.BOOL)) nextloop end 70: ; F begin MaybeAdd(currentProperty, currentValue, PrimitiveValue(currentPos, "false", JSON_TYPE.BOOL)) nextloop end 102:; f begin MaybeAdd(currentProperty, currentValue, PrimitiveValue(currentPos, "false", JSON_TYPE.BOOL)) nextloop end 110:; n begin MaybeAdd(currentProperty, currentValue, PrimitiveValue(currentPos, "null", JSON_TYPE.NULL)) nextloop end 78: ; N begin MaybeAdd(currentProperty, currentValue, PrimitiveValue(currentPos, "Null", JSON_TYPE.NULL)) nextloop end endcase else begin MaybeAdd(currentProperty, currentValue, NumberValue(currentPos)) nextloop end end incr currentPos end mreturn resultValue endmethod private static method ReportFailure, void req in inputValue, @string req in currentPos, int req in expected, @string endparams record startReportString, int endReportString, int proc startReportString = currentPos - 50 endReportString = currentPos + 50 if(startReportString < 0) startReportString = 0 if(endReportString > inputValue.Length) endReportString = inputValue.Length throw new Exception("Parser was expecting " + expected + " recived " + STRING_AT(inputValue, currentPos) + " at or near position " + %string(currentPos) + ' surrounded by "' + Substring(startReportString, endReportString, inputValue) + '"') endmethod private static method MaybePushResult, @JsonValue req inout currentValue, @JsonValue req in parentValues, @ArrayList req inout resultValue, @JsonValue req in newValue, @JsonValue proc if(resultValue == ^null) resultValue = currentValue if(currentValue != ^null) parentValues.Add(currentValue) currentValue = newValue if(resultValue == ^null) ;;if current was null we need to set it again here resultValue = currentValue mreturn newValue endmethod private static method MaybeAdd, @JsonValue req inout currentProperty, @string req in targetParent, @JsonValue req in val, @JsonValue endparams proc if(currentProperty != ^null) then begin if(targetParent .is. JsonObject) begin ((JsonObject)targetParent).AddProperty(currentProperty, val) end currentProperty = ^null end else if(targetParent .is. JsonArray) begin ((JsonArray)targetParent).Add(val) end mreturn val endmethod private method SkipAny, int req in currentPos, int req in setString, @string endparams record tempPos, int proc tempPos = currentPos while(tempPos <= inputValue.Length) begin data ch, a1, STRING_AT(inputValue, tempPos) if(!setString.Contains(ch)) mreturn tempPos incr tempPos end mreturn tempPos endmethod endclass class JsonObject extends JsonValue public propertyNames, @ArrayList public propertyValues, @ArrayList public method JsonObject proc JsonType = JSON_TYPE.OBJ propertyNames = new ArrayList() propertyValues = new ArrayList() endmethod public method AddProperty, void req in name, @string req in value, @JsonValue proc propertyNames.Add(name) propertyValues.Add(value) endmethod public method HasProperty, boolean req in name, @string endparams record foundNameIndex, int proc mreturn propertyNames.IndexOf(name) >= 0 endmethod public method GetProperty, @JsonValue req in name, @string endparams record foundNameIndex, int proc foundNameIndex = propertyNames.IndexOf(name) if(foundNameIndex >= 0) then begin mreturn (@JsonValue)propertyValues[foundNameIndex] end else begin throw new IndexOutOfRangeException() end endmethod public method TryGetProperty, boolean req in name, @string req out value, @JsonValue endparams record foundNameIndex, int proc foundNameIndex = propertyNames.IndexOf(name) if(foundNameIndex >= 0) then begin value = (@JsonValue)propertyValues[foundNameIndex] mreturn true end else begin MaybeLog(6, "failed to find prop: " + name) mreturn false end endmethod public method TryGetProperty, boolean req in name, @string req out value, i endparams record foundValue, @JsonValue proc if(TryGetProperty(name, foundValue)) begin value = ((@JsonInt)foundValue).Value mreturn true end mreturn false endmethod public method TryGetProperty, boolean req in name, @string req out value, a endparams record foundValue, @JsonValue proc if(TryGetProperty(name, foundValue)) begin value = ((@JsonText)foundValue).Value mreturn true end mreturn false endmethod public method TryGetProperty, boolean req in name, @string req out value, @string endparams record foundValue, @JsonValue proc if(TryGetProperty(name, foundValue)) begin value = ((@JsonText)foundValue).Value mreturn true end mreturn false endmethod public method TryGetBoolProperty, boolean req in name, @string req out value, boolean endparams record foundValue, @JsonValue proc if(TryGetProperty(name, foundValue)) begin value = ((@JsonBoolean)foundValue).Value MaybeLog(6, "name -> " + %string(value)) mreturn true end mreturn false endmethod endclass class JsonArray extends JsonValue public arrayValues, @ArrayList public method JsonArray proc JsonType = JSON_TYPE.ARRAY_VAL arrayValues = new ArrayList() endmethod public method Add, void value, @JsonValue proc arrayValues.Add(value) endmethod endclass class JsonValue public JsonType, JSON_TYPE public method GetProperty, @JsonValue propertyName, @string proc if(JsonType == JSON_TYPE.OBJ) then begin mreturn ((@JsonObject)this).GetProperty(propertyName) end else throw new InvalidOperationException() endmethod public method TryGetProperty, boolean propertyName, @string out value, @JsonValue proc if(JsonType == JSON_TYPE.OBJ) then begin mreturn ((@JsonObject)this).TryGetProperty(propertyName, value) end else throw new InvalidOperationException() endmethod public property ValueKind, JSON_TYPE method get proc mreturn JsonType endmethod endproperty public method GetArrayLength, int proc if(JsonType == JSON_TYPE.ARRAY_VAL) then begin mreturn ((@JsonArray)this).arrayValues.Count end else throw new InvalidOperationException() endmethod public property Indexer, @JsonValue in index, int method get proc if(JsonType == JSON_TYPE.ARRAY_VAL) then begin mreturn (@JsonValue)((@JsonArray)this).arrayValues[index] end else throw new InvalidOperationException() end endproperty public method GetBoolean, boolean proc if(JsonType == JSON_TYPE.BOOL) then begin mreturn ((@JsonBoolean)this).Value end else throw new InvalidOperationException() endmethod public method GetByte, i1 proc if(JsonType == JSON_TYPE.NUMBER) then begin mreturn ((@JsonNumber)this).Value end else throw new InvalidOperationException() endmethod public method GetStringFromBase64, @string proc if(JsonType == JSON_TYPE.TEXT) then begin mreturn Convert.FromBase64String(((@JsonText)this).Value) end else throw new InvalidOperationException() endmethod public method GetDecimal, decimal proc if(JsonType == JSON_TYPE.NUMBER) then begin mreturn ((@JsonNumber)this).Value end else if(JsonType == JSON_TYPE.INT_VAL) then begin mreturn ((@JsonInt)this).Value end else throw new InvalidOperationException() endmethod public method GetInt16, short proc if(JsonType == JSON_TYPE.INT_VAL) then begin mreturn ((@JsonInt)this).Value end else throw new InvalidOperationException() endmethod public method GetInt32, int proc if(JsonType == JSON_TYPE.INT_VAL) then begin mreturn ((@JsonInt)this).Value end else throw new InvalidOperationException() endmethod public method GetInt64, i8 proc if(JsonType == JSON_TYPE.INT_VAL) then begin mreturn ((@JsonInt)this).Value end else throw new InvalidOperationException() endmethod public method GetString, @string proc if(JsonType == JSON_TYPE.TEXT) then begin mreturn ((@JsonText)this).Value end else throw new InvalidOperationException() endmethod endclass class jsonNull extends JsonValue public value, @Object endclass class JsonText extends JsonValue public Value, @string endclass class JsonInt extends JsonValue public Value, long endclass class JsonNumber extends JsonValue public Value, double endclass class JsonBoolean extends JsonValue public Value, boolean endclass endnamespace