Page 2 of 2

Re: New plugin: Json Parser

Posted: Mon Jan 07, 2019 9:00 am
by midwan
Hi Allanon!

I'm currently in the middle of a few other projects, so it will take me a while to come back to this one - but if you do have something that works, I'd love it if you could share. It doesn't matter if the code is "dirty" as you say, if it works it's always helpful as a starting point or a different perspective also. :)

Re: New plugin: Json Parser

Posted: Thu Feb 14, 2019 12:34 pm
by midwan
Ping!

I'd like to revisit this soon, now that some of my other bigger projects are going out of the way.
@Allanon, if you have something you'd be willing to share, it would be great!

Otherwise, I will get started with the samples I found so far and post back any updates here.

Re: New plugin: Json Parser

Posted: Mon Apr 29, 2019 10:49 pm
by BeChris
Hello, I adapted an Lua json decoder from https://github.com/rxi/json.lua

Here it is:

Code: Select all

/*
 * Converted from json.lua code at https://github.com/rxi/json.lua
 * Conversion done by Christophe Gouiran (bechris13250@gmail.com)
 *
 * Original copyright:
 *
 * json.lua
 *
 * Copyright (c) 2019 rxi
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
 * of the Software, and to permit persons to whom the Software is furnished to do
 * so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 */


Function escape_char_map_inv(c)
	Local escape_char_map = {
		[ "\\/" ] = "/",
		[ "\\" ] = "\\\\",
		[ "\"" ] = "\\\"",
		[ "\b" ] = "\\b",
		[ "\f" ] = "\\f",
		[ "\n" ] = "\\n",
		[ "\r" ] = "\\r",
		[ "\t" ] = "\\t",
	}
	
	Return (RawGet(escape_char_map, c))
EndFunction
	

Function create_set(...)
	Local res = {}

	For Local i = 0 To arg.n - 1
		res[ arg[i] ] = True
	Next
	Return (res)
EndFunction

space_chars  = create_set(" ", "\t", "\r", "\n")
delim_chars  = create_set(" ", "\t", "\r", "\n", "]", "}", ",")
escape_chars = create_set("\\", "/", "\"", "b", "f", "n", "r", "t", "u")
literals     = create_set("true", "false", "null")

literal_map = {
	[ "true"  ] = True,
	[ "false" ] = False,
	[ "null"  ] = "null",
}

Function next_char(str, idx, set, negate)
	
	For Local i = idx To StrLen(str) - 1
		
		Local t = MidStr(str, i, 1)
		
		If RawGet(set, t) <> negate
			Return (i)
		EndIf
	Next
	Return (StrLen(str) + 1)
EndFunction


Function decode_error(str, idx, msg)

	Local len = StrLen(str)

	Local line_count = 1
	Local col_count = 1
	For i = 0 To idx - 1
		If i >= len
			Break
		EndIf
		col_count = col_count + 1
		If ByteAsc(str, i) = 10
			line_count = line_count + 1
			col_count = 1
		EndIf
	Next
	Error( FormatStr("%s at line %d col %d", msg, line_count, col_count) )
EndFunction


Function codepoint_to_utf8(n)
	; http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=iws-appendixa
	Local f = Floor
	If n <= 0x7f
		Return Chr(n, #ENCODING_RAW)
	ElseIf n <= 0x7ff
		Return (Chr(f(n / 64) + 192, #ENCODING_RAW) .. Chr(n % 64 + 128, #ENCODING_RAW))
	ElseIf n <= 0xffff
		Return (Chr(f(n / 4096) + 224, #ENCODING_RAW) .. Chr(f(n % 4096 / 64) + 128, #ENCODING_RAW) .. Chr(n % 64 + 128, #ENCODING_RAW))
	ElseIf n <= 0x10ffff
		Return (Chr(f(n / 262144) + 240, #ENCODING_RAW) .. Chr(f(n % 262144 / 4096) + 128, #ENCODING_RAW) .. Chr(f(n % 4096 / 64) + 128, #ENCODING_RAW) .. Chr(n % 64 + 128, #ENCODING_RAW))
	EndIf
	Error( FormatStr("invalid unicode codepoint '%x'", n) )
EndFunction


Function parse_unicode_escape(s)
	
	Local n1 = ToNumber( MidStr(s, 2, 4), 16)
	Local n2 = ToNumber( MidStr(s, 8, 4), 16)

	; Surrogate pair?
	If n2
		Return (codepoint_to_utf8((n1 - 0xd800) * 0x400 + (n2 - 0xdc00) + 0x10000))
	Else
		Return (codepoint_to_utf8(n1))
	EndIf

EndFunction


Function parse_string(str, i)
	
	Local has_unicode_escape = False
	Local has_surrogate_escape = False
	Local has_escape = False
	Local last
	For j = i + 1 To StrLen(str) - 1
		Local x = ByteAsc(str, j)

		If x < 32
			decode_error(str, j, "control character in string")
		EndIf

		If last = 92 ; "\\" (escape char)
			If x = 117 ; "u" (unicode escape sequence)
				Local hex = MidStr(str, j + 1, 4)
				If PatternFindStrDirect(hex, "%x%x%x%x") = -1
					decode_error(str, j, "invalid unicode escape in string")
				EndIf
				If PatternFindStrDirect(hex, "^[dD][89aAbB]") = 0
					has_surrogate_escape = True
				Else
					has_unicode_escape = True
				EndIf
			Else
				Local c = Chr(x)
				If Not escape_chars[c]
					decode_error(str, j, "invalid escape char '" .. c .. "' in string")
				EndIf
				has_escape = True
			EndIf
			last = Nil

		ElseIf x = 34 ; '"' (end of string)
			Local s = MidStr(str, i + 1, j - i - 1)
			If has_surrogate_escape
				s = PatternReplaceStr(s, "\\u[dD][89aAbB]..\\u....", parse_unicode_escape)
			EndIf
			If has_unicode_escape
				s = PatternReplaceStr(s, "\\u....", parse_unicode_escape)
			EndIf
			If has_escape
				s = PatternReplaceStr(s, "\\.", escape_char_map_inv)
			EndIf
			Return (s, j + 1)

		Else
			last = x
		EndIf
	Next
	decode_error(str, i, "expected closing quote for string")
EndFunction


Function parse_number(str, i)

	Local x = next_char(str, i, delim_chars)
	Local s = MidStr(str, i, x - i)

	Local n = ToNumber(s)
	If IsNil(n)
		decode_error(str, i, "invalid number '" .. s .. "'")
	EndIf
	Return (n, x)
EndFunction


Function parse_literal(str, i)

	Local x = next_char(str, i, delim_chars)
	Local word = MidStr(str, i, x - i)
	If Not HaveItem(literals, word)
		decode_error(str, i, "invalid literal '" .. word .. "'")
	EndIf
	Return (literal_map[word], x)
EndFunction


Function parse_array(str, i)

	Local res = {}
	Local n = 1
	i = i + 1
	Repeat
		Local x
		i = next_char(str, i, space_chars, True)
		; Empty / End of array?
		If MidStr(str, i, 1) = "]"
			i = i + 1
			Break
		EndIf
		; Read token
		x, i = parse(str, i)
		res[n] = x
		n = n + 1
		; Next token
		i = next_char(str, i, space_chars, True)
		Local c = MidStr(str, i, 1)
		i = i + 1
		If c = "]" Then Break
		If c <> "," Then decode_error(str, i, "expected ']' or ','")
	Forever
	Return (res, i)
EndFunction


Function parse_object(str, i)
	
	Local res = {}
	i = i + 1

	Repeat
		Local key, value
		i = next_char(str, i, space_chars, True)

		; Empty / End of object?
		If MidStr(str, i, 1) = "}"
			i = i + 1
			Break
		EndIf
		; Read key
		If MidStr(str, i, 1) <> "\""
			decode_error(str, i, "expected string for key")
		EndIf
		key, i = parse(str, i)
		; Read ':' delimiter
		i = next_char(str, i, space_chars, True)
		If MidStr(str, i, 1) <> ":"
			decode_error(str, i, "expected ':' after key")
		EndIf
		i = next_char(str, i + 1, space_chars, True)
		; Read value
		value, i = parse(str, i)
		; Set
		res[key] = value
		; next token
		i = next_char(str, i, space_chars, True)
		Local c = MidStr(str, i, 1)
		i = i + 1
		If c = "}" Then Break
		If c <> "," Then decode_error(str, i, "expected '}' or ','")
	Forever
	Return (res, i)
EndFunction


Function parse(str, idx)
	
	Local char_func_map = {
		[ "\"" ] = parse_string,
		[ "0" ] = parse_number,
		[ "1" ] = parse_number,
		[ "2" ] = parse_number,
		[ "3" ] = parse_number,
		[ "4" ] = parse_number,
		[ "5" ] = parse_number,
		[ "6" ] = parse_number,
		[ "7" ] = parse_number,
		[ "8" ] = parse_number,
		[ "9" ] = parse_number,
		[ "-" ] = parse_number,
		[ "t" ] = parse_literal,
		[ "f" ] = parse_literal,
		[ "n" ] = parse_literal,
		[ "[" ] = parse_array,
		[ "{" ] = parse_object,
	}
	
	Local c = MidStr(str, idx, 1)
	
	If HaveItem(char_func_map, c)
		Local f = char_func_map[c]
	
		If f
			Return (f(str, idx))
		EndIf
	EndIf
	decode_error(str, idx, "unexpected character '" .. c .. "'")
	
EndFunction


Function decode(str)
	
	/* Check str parameter is a string */
	Local str_type = GetType(str)
	If str_type <> #STRING
		Local type_as_string = "unknown type"
		Switch str_type
			Case #NUMBER
				type_as_string = "number"
			Case #TABLE
				type_as_string = "table"
			Case #FUNCTION
				type_as_string = "function"
			Case #USERDATA
				type_as_string = "userdata"
			Case #LIGHTUSERDATA
				type_as_string = "lightuserdata"
			Case #THREAD
				type_as_string = "thread"
			Case #NIL
				type_as_string = "nil"
		EndSwitch
			
		Error("expected argument of type string, got " .. type_as_string)
	EndIf
	
	Local res, idx = parse(str, next_char(str, 0, space_chars, True))
	idx = next_char(str, idx, space_chars, True)
	If idx <= StrLen(str)
		decode_error(str, idx, "trailing garbage")
	EndIf
	Return (res)	
EndFunction

Function MyDebug(...)

	Local Function PrettyPrint(item, name, indent)

		DebugPrintNR(RepeatStr(" ", indent))

		If GetType(item) = #TABLE
			If StrLen(name) <> 0
				DebugPrintNR(name, " : ")
			EndIf
		Else
			If StrLen(name) = 0
				DebugPrintNR(item, " ")
			Else
				DebugPrintNR(name, " : ", item)
			EndIf	
		EndIf
	
		Switch GetType(item)
			Case #NUMBER
				DebugPrint("")
			Case #STRING
				DebugPrint("")
			Case #TABLE
				DebugPrint("{")

				For k,v In Pairs(item)
					PrettyPrint(v, k, indent + 2)
				Next
		 
				DebugPrintNR(RepeatStr(" ", indent) .. "}")
				
				If StrLen(name) <> 0
					DebugPrint(" // ", name)
				Else
					DebugPrint("")
				EndIf
				
			Case #FUNCTION
				DebugPrint(" (function)")
			Case #USERDATA
				DebugPrint(" (userdata)")
			Case #LIGHTUSERDATA
				DebugPrint(" (lightuserdata)")
			Case #THREAD
				DebugPrint(" (thread)")
			Case #NIL
				DebugPrint(" (nil)")
		EndSwitch
	
	
	EndFunction

	For k, v In IPairs(arg)
		PrettyPrint(v, "param" .. k+1, 0)
		DebugPrint("")
	Next
	
EndFunction

;MyDebug(decode(FileToString("test2.json")))

the decode fonction can be given a json structure as string and returns the corresponding Hollywood table.

MyDebug function was only necessary during debugging of this code. It can be safely removed.

Re: New plugin: Json Parser

Posted: Tue Apr 30, 2019 9:11 pm
by airsoftsoftwair
Thanks for posting this!

Re: New plugin: Json Parser

Posted: Wed May 01, 2019 1:16 am
by p-OS
Thanx for the port, might be quite useful for reading data from public web services..

Re: New plugin: Json Parser

Posted: Mon Jun 03, 2019 9:21 am
by Clyde
Thanks for the port as well, BeChris!

Not sure about other opinions, but I think you should update you code at one line. In the function "parse_array(str, i)" I would set Local n to 0 (zero) not 1. I know that in the original Lua code this is set to 1, but IMHO Hollywood uses zero-based indices (at least when watching some examples in the documentation) and IMHO it is kind of a standard in programming to use the zero-based approach.

So, I tried the json decode function and it works nicely. But I have one problem when accessing the properties of the object after I read the json. Let's assume I have this json definition in a json file:

Code: Select all

{
    "myProp": 1
}
After I read the file and decoded it I want to access the property like this in Hollywood:

Code: Select all

myTable.myProp
But this does not work (error message in Hollywood which I can't remember currently, sth like "myProp is not defined"). I just can access it like so:

Code: Select all

myTable["myProp"]
I don't know whether this is "a problem" with the json decode code that can be fixed or something I have to live with (I prefer the dot notation).

Thanks in advance!

Re: New plugin: Json Parser

Posted: Fri Aug 02, 2019 11:43 pm
by BeChris
Clyde wrote: Mon Jun 03, 2019 9:21 am Thanks for the port as well, BeChris!

Not sure about other opinions, but I think you should update you code at one line. In the function "parse_array(str, i)" I would set Local n to 0 (zero) not 1. I know that in the original Lua code this is set to 1, but IMHO Hollywood uses zero-based indices (at least when watching some examples in the documentation) and IMHO it is kind of a standard in programming to use the zero-based approach.

So, I tried the json decode function and it works nicely. But I have one problem when accessing the properties of the object after I read the json. Let's assume I have this json definition in a json file:

Code: Select all

{
    "myProp": 1
}
After I read the file and decoded it I want to access the property like this in Hollywood:

Code: Select all

myTable.myProp
But this does not work (error message in Hollywood which I can't remember currently, sth like "myProp is not defined"). I just can access it like so:

Code: Select all

myTable["myProp"]
I don't know whether this is "a problem" with the json decode code that can be fixed or something I have to live with (I prefer the dot notation).

Thanks in advance!
Hello Clyde, you are right and I might have written Local n = 0
I'm going to update my original post.

Good catch !

For the second problem (access via dot notation) I will take a look at it later.

Re: New plugin: Json Parser

Posted: Fri Aug 02, 2019 11:47 pm
by BeChris
BeChris wrote: Mon Apr 29, 2019 10:49 pm Hello, I adapted an Lua json decoder from https://github.com/rxi/json.lua

Here it is:

Code: Select all

/*
 * Converted from json.lua code at https://github.com/rxi/json.lua
 * Conversion done by Christophe Gouiran (bechris13250@gmail.com)
 *
 * Original copyright:
 *
 * json.lua
 *
 * Copyright (c) 2019 rxi
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
 * of the Software, and to permit persons to whom the Software is furnished to do
 * so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 */


Function escape_char_map_inv(c)
	Local escape_char_map = {
		[ "\\/" ] = "/",
		[ "\\" ] = "\\\\",
		[ "\"" ] = "\\\"",
		[ "\b" ] = "\\b",
		[ "\f" ] = "\\f",
		[ "\n" ] = "\\n",
		[ "\r" ] = "\\r",
		[ "\t" ] = "\\t",
	}
	
	Return (RawGet(escape_char_map, c))
EndFunction
	

Function create_set(...)
	Local res = {}

	For Local i = 0 To arg.n - 1
		res[ arg[i] ] = True
	Next
	Return (res)
EndFunction

space_chars  = create_set(" ", "\t", "\r", "\n")
delim_chars  = create_set(" ", "\t", "\r", "\n", "]", "}", ",")
escape_chars = create_set("\\", "/", "\"", "b", "f", "n", "r", "t", "u")
literals     = create_set("true", "false", "null")

literal_map = {
	[ "true"  ] = True,
	[ "false" ] = False,
	[ "null"  ] = "null",
}

Function next_char(str, idx, set, negate)
	
	For Local i = idx To StrLen(str) - 1
		
		Local t = MidStr(str, i, 1)
		
		If RawGet(set, t) <> negate
			Return (i)
		EndIf
	Next
	Return (StrLen(str) + 1)
EndFunction


Function decode_error(str, idx, msg)

	Local len = StrLen(str)

	Local line_count = 1
	Local col_count = 1
	For i = 0 To idx - 1
		If i >= len
			Break
		EndIf
		col_count = col_count + 1
		If ByteAsc(str, i) = 10
			line_count = line_count + 1
			col_count = 1
		EndIf
	Next
	Error( FormatStr("%s at line %d col %d", msg, line_count, col_count) )
EndFunction


Function codepoint_to_utf8(n)
	; http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=iws-appendixa
	Local f = Floor
	If n <= 0x7f
		Return Chr(n, #ENCODING_RAW)
	ElseIf n <= 0x7ff
		Return (Chr(f(n / 64) + 192, #ENCODING_RAW) .. Chr(n % 64 + 128, #ENCODING_RAW))
	ElseIf n <= 0xffff
		Return (Chr(f(n / 4096) + 224, #ENCODING_RAW) .. Chr(f(n % 4096 / 64) + 128, #ENCODING_RAW) .. Chr(n % 64 + 128, #ENCODING_RAW))
	ElseIf n <= 0x10ffff
		Return (Chr(f(n / 262144) + 240, #ENCODING_RAW) .. Chr(f(n % 262144 / 4096) + 128, #ENCODING_RAW) .. Chr(f(n % 4096 / 64) + 128, #ENCODING_RAW) .. Chr(n % 64 + 128, #ENCODING_RAW))
	EndIf
	Error( FormatStr("invalid unicode codepoint '%x'", n) )
EndFunction


Function parse_unicode_escape(s)
	
	Local n1 = ToNumber( MidStr(s, 2, 4), 16)
	Local n2 = ToNumber( MidStr(s, 8, 4), 16)

	; Surrogate pair?
	If n2
		Return (codepoint_to_utf8((n1 - 0xd800) * 0x400 + (n2 - 0xdc00) + 0x10000))
	Else
		Return (codepoint_to_utf8(n1))
	EndIf

EndFunction


Function parse_string(str, i)
	
	Local has_unicode_escape = False
	Local has_surrogate_escape = False
	Local has_escape = False
	Local last
	For j = i + 1 To StrLen(str) - 1
		Local x = ByteAsc(str, j)

		If x < 32
			decode_error(str, j, "control character in string")
		EndIf

		If last = 92 ; "\\" (escape char)
			If x = 117 ; "u" (unicode escape sequence)
				Local hex = MidStr(str, j + 1, 4)
				If PatternFindStrDirect(hex, "%x%x%x%x") = -1
					decode_error(str, j, "invalid unicode escape in string")
				EndIf
				If PatternFindStrDirect(hex, "^[dD][89aAbB]") = 0
					has_surrogate_escape = True
				Else
					has_unicode_escape = True
				EndIf
			Else
				Local c = Chr(x)
				If Not escape_chars[c]
					decode_error(str, j, "invalid escape char '" .. c .. "' in string")
				EndIf
				has_escape = True
			EndIf
			last = Nil

		ElseIf x = 34 ; '"' (end of string)
			Local s = MidStr(str, i + 1, j - i - 1)
			If has_surrogate_escape
				s = PatternReplaceStr(s, "\\u[dD][89aAbB]..\\u....", parse_unicode_escape)
			EndIf
			If has_unicode_escape
				s = PatternReplaceStr(s, "\\u....", parse_unicode_escape)
			EndIf
			If has_escape
				s = PatternReplaceStr(s, "\\.", escape_char_map_inv)
			EndIf
			Return (s, j + 1)

		Else
			last = x
		EndIf
	Next
	decode_error(str, i, "expected closing quote for string")
EndFunction


Function parse_number(str, i)

	Local x = next_char(str, i, delim_chars)
	Local s = MidStr(str, i, x - i)

	Local n = ToNumber(s)
	If IsNil(n)
		decode_error(str, i, "invalid number '" .. s .. "'")
	EndIf
	Return (n, x)
EndFunction


Function parse_literal(str, i)

	Local x = next_char(str, i, delim_chars)
	Local word = MidStr(str, i, x - i)
	If Not HaveItem(literals, word)
		decode_error(str, i, "invalid literal '" .. word .. "'")
	EndIf
	Return (literal_map[word], x)
EndFunction


Function parse_array(str, i)

	Local res = {}
	Local n = 0
	i = i + 1
	Repeat
		Local x
		i = next_char(str, i, space_chars, True)
		; Empty / End of array?
		If MidStr(str, i, 1) = "]"
			i = i + 1
			Break
		EndIf
		; Read token
		x, i = parse(str, i)
		res[n] = x
		n = n + 1
		; Next token
		i = next_char(str, i, space_chars, True)
		Local c = MidStr(str, i, 1)
		i = i + 1
		If c = "]" Then Break
		If c <> "," Then decode_error(str, i, "expected ']' or ','")
	Forever
	Return (res, i)
EndFunction


Function parse_object(str, i)
	
	Local res = {}
	i = i + 1

	Repeat
		Local key, value
		i = next_char(str, i, space_chars, True)

		; Empty / End of object?
		If MidStr(str, i, 1) = "}"
			i = i + 1
			Break
		EndIf
		; Read key
		If MidStr(str, i, 1) <> "\""
			decode_error(str, i, "expected string for key")
		EndIf
		key, i = parse(str, i)
		; Read ':' delimiter
		i = next_char(str, i, space_chars, True)
		If MidStr(str, i, 1) <> ":"
			decode_error(str, i, "expected ':' after key")
		EndIf
		i = next_char(str, i + 1, space_chars, True)
		; Read value
		value, i = parse(str, i)
		; Set
		res[key] = value
		; next token
		i = next_char(str, i, space_chars, True)
		Local c = MidStr(str, i, 1)
		i = i + 1
		If c = "}" Then Break
		If c <> "," Then decode_error(str, i, "expected '}' or ','")
	Forever
	Return (res, i)
EndFunction


Function parse(str, idx)
	
	Local char_func_map = {
		[ "\"" ] = parse_string,
		[ "0" ] = parse_number,
		[ "1" ] = parse_number,
		[ "2" ] = parse_number,
		[ "3" ] = parse_number,
		[ "4" ] = parse_number,
		[ "5" ] = parse_number,
		[ "6" ] = parse_number,
		[ "7" ] = parse_number,
		[ "8" ] = parse_number,
		[ "9" ] = parse_number,
		[ "-" ] = parse_number,
		[ "t" ] = parse_literal,
		[ "f" ] = parse_literal,
		[ "n" ] = parse_literal,
		[ "[" ] = parse_array,
		[ "{" ] = parse_object,
	}
	
	Local c = MidStr(str, idx, 1)
	
	If HaveItem(char_func_map, c)
		Local f = char_func_map[c]
	
		If f
			Return (f(str, idx))
		EndIf
	EndIf
	decode_error(str, idx, "unexpected character '" .. c .. "'")
	
EndFunction


Function decode(str)
	
	/* Check str parameter is a string */
	Local str_type = GetType(str)
	If str_type <> #STRING
		Local type_as_string = "unknown type"
		Switch str_type
			Case #NUMBER
				type_as_string = "number"
			Case #TABLE
				type_as_string = "table"
			Case #FUNCTION
				type_as_string = "function"
			Case #USERDATA
				type_as_string = "userdata"
			Case #LIGHTUSERDATA
				type_as_string = "lightuserdata"
			Case #THREAD
				type_as_string = "thread"
			Case #NIL
				type_as_string = "nil"
		EndSwitch
			
		Error("expected argument of type string, got " .. type_as_string)
	EndIf
	
	Local res, idx = parse(str, next_char(str, 0, space_chars, True))
	idx = next_char(str, idx, space_chars, True)
	If idx <= StrLen(str)
		decode_error(str, idx, "trailing garbage")
	EndIf
	Return (res)	
EndFunction

Function MyDebug(...)

	Local Function PrettyPrint(item, name, indent)

		DebugPrintNR(RepeatStr(" ", indent))

		If GetType(item) = #TABLE
			If StrLen(name) <> 0
				DebugPrintNR(name, " : ")
			EndIf
		Else
			If StrLen(name) = 0
				DebugPrintNR(item, " ")
			Else
				DebugPrintNR(name, " : ", item)
			EndIf	
		EndIf
	
		Switch GetType(item)
			Case #NUMBER
				DebugPrint("")
			Case #STRING
				DebugPrint("")
			Case #TABLE
				DebugPrint("{")

				For k,v In Pairs(item)
					PrettyPrint(v, k, indent + 2)
				Next
		 
				DebugPrintNR(RepeatStr(" ", indent) .. "}")
				
				If StrLen(name) <> 0
					DebugPrint(" // ", name)
				Else
					DebugPrint("")
				EndIf
				
			Case #FUNCTION
				DebugPrint(" (function)")
			Case #USERDATA
				DebugPrint(" (userdata)")
			Case #LIGHTUSERDATA
				DebugPrint(" (lightuserdata)")
			Case #THREAD
				DebugPrint(" (thread)")
			Case #NIL
				DebugPrint(" (nil)")
		EndSwitch
	
	
	EndFunction

	For k, v In IPairs(arg)
		PrettyPrint(v, "param" .. k+1, 0)
		DebugPrint("")
	Next
	
EndFunction

;MyDebug(decode(FileToString("test2.json")))

the decode fonction can be given a json structure as string and returns the corresponding Hollywood table.

MyDebug function was only necessary during debugging of this code. It can be safely removed.
parse_array fixed after bug detected by Clyde.

Re: New plugin: Json Parser

Posted: Tue Aug 27, 2019 3:32 pm
by Clyde
Have a look here: viewtopic.php?f=7&t=2240

Re: New plugin: Json Parser

Posted: Wed Oct 30, 2019 4:45 pm
by airsoftsoftwair

Code: Select all

- New: Added JSON support; SerializeTable() and DeserializeTable() will now (de)serialize its data to/
  from the JSON format by default; you can change this behaviour by specifying the optional adapter
  argument; ReadTable(), WriteTable(), LoadPrefs(), and SavePrefs() will continue to use Hollywood's
  proprietary table serialization data format for compatibility reasons; you can force those functions
  to use JSON, however, by passing "default" in the "Adapter" table field; Hollywood's proprietary table
  serializer can be accessed by passing "inbuilt" in the "Adapter" table field