Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /// json_decode2(json, [objecttypesmap, arraytypesmap])
- /// @arg json String to be decoded
- /// @arg [objecttypesmap] If a map is supplied, the keys will be set to copies of the loaded maps containing the types of their elements.
- /// @arg [arraytypesmap] If a map is supplied, the keys will be set to copies of the loaded lists containing the types of their elements.
- ///
- /// @desc The string will be decoded into a value/data structure according to the JSON specification at http://www.json.org/.
- //// Objects are translated into ds_maps and arrays into ds_lists. If you destroy the top data structure all below will
- /// automatically be freed as well.
- ///
- /// Unlike the built-in function, this will throw a descriptive error whenever an invalid character appears.
- /// This version also supports entering a single value without an enclosing map, for instance json_decode2("12.234")
- /// will return a real number of 12.234, and json_decode2("[1, 2, 3]") will return a single ds_list.
- /// The type of each object/array element in the loaded maps/lists can also be checked by entering two maps into the
- /// objecttypesmap and arraytypesmap arguments. During loading, these maps (if supplied) will be filled using the
- /// e_json_value_type enumerator defined below.
- ///
- /// http://www.stuffbydavid.com
- globalvar json_string, json_token, json_token_queue, json_pos, json_line, json_col,
- json_value, json_value_type, json_object_value_type_map, json_array_value_type_map,
- json_error, json_debug;
- //json_string = string_replace_all(argument[0], "\r\n", "\n")
- json_token = ""
- json_token_queue = ds_queue_create()
- json_pos = 1
- json_line = 1
- json_col = 1
- json_error = false
- json_debug = false
- if (argument_count > 1)
- {
- json_object_value_type_map = argument[1]
- json_array_value_type_map = argument[2]
- }
- else
- {
- json_object_value_type_map = undefined
- json_array_value_type_map = undefined
- }
- // Parse string into queue of tokens
- var str, strlen, instring, line, col;
- str = argument0
- strlen = string_length(str)
- line = 1
- col = 0
- instring = false
- for (var i = 0; i < strlen; i++)
- {
- var char = string_char_at(str, i);
- if (char = "\t")
- col += 4
- else
- col++
- if (!instring && (char = " " || char = "\t"))
- continue
- if (char = "\r" && string_char_at(str, i + 1) = "\n")
- {
- char = "\n"
- i++
- }
- if (char = "\n")
- {
- line++
- col = 0
- if (!instring)
- continue
- }
- if (char = "\"")
- instring = !instring
- var token;
- token[0] = char
- token[1] = line
- token[2] = col
- ds_queue_enqueue(json_token_queue, token)
- }
- // Value types
- enum e_json_value_type
- {
- OBJECT,
- ARRAY,
- STRING,
- NUMBER,
- BOOL,
- NULL
- }
- // Read root value
- if (!json_decode2_value())
- return undefined
- return json_value
- /// json_decode2_value()
- /// @desc DO NOT USE DIRECTLY!
- if (json_debug)
- show_debug_message("json_decode2_value")
- var next = json_decode2_peek();
- if (next = "{")
- return json_decode2_object()
- else if (next = "[")
- return json_decode2_array()
- else if (next = "\"")
- return json_decode2_string()
- else if (next = "-" || string_digits(next) != "")
- return json_decode2_number()
- else if (next != "")
- return json_decode2_word(next)
- return false
- /// json_decode2_object()
- /// @desc DO NOT USE DIRECTLY!
- if (json_debug)
- show_debug_message("json_decode2_object")
- if (!json_decode2_next("{"))
- return false
- var map = ds_map_create();
- if (!is_undefined(json_object_value_type_map))
- ds_map_add_map(json_object_value_type_map, map, ds_map_create())
- // Read name/value pairs
- while (json_decode2_peek() != "}")
- {
- // Read name
- if (!json_decode2_string())
- break
- var name = json_value;
- if (!json_decode2_next(":"))
- break
- // Read value
- if (!json_decode2_value())
- break
- // Add type to map
- if (!is_undefined(json_object_value_type_map))
- ds_map_add(json_object_value_type_map[?map], name, json_value_type)
- // Add to map
- switch (json_value_type)
- {
- case e_json_value_type.OBJECT:
- ds_map_add_map(map, name, json_value)
- break
- case e_json_value_type.ARRAY:
- ds_map_add_list(map, name, json_value)
- break
- default:
- map[?name] = json_value
- }
- // No more pairs?
- if (json_decode2_peek() != ",")
- break
- json_decode2_next(",")
- }
- if (!json_error)
- json_decode2_next("}")
- // Clean up on error
- if (json_error)
- {
- ds_map_destroy(map)
- return false
- }
- json_value = map
- json_value_type = e_json_value_type.OBJECT
- return true
- /// json_decode2_array()
- /// @desc DO NOT USE DIRECTLY!
- if (json_debug)
- show_debug_message("json_decode2_array")
- if (!json_decode2_next("["))
- return false
- var list = ds_list_create();
- if (!is_undefined(json_array_value_type_map))
- ds_map_add_list(json_array_value_type_map, list, ds_list_create())
- // Read values
- while (json_decode2_peek() != "]")
- {
- // Read value
- if (!json_decode2_value())
- break
- // Add to list
- ds_list_add(list, json_value)
- // Add type to list
- if (!is_undefined(json_array_value_type_map))
- ds_list_add(json_array_value_type_map[?list], json_value_type)
- // Mark type
- switch (json_value_type)
- {
- case e_json_value_type.OBJECT:
- ds_list_mark_as_map(list, ds_list_size(list) - 1)
- break
- case e_json_value_type.ARRAY:
- ds_list_mark_as_list(list, ds_list_size(list) - 1)
- break
- }
- // No more pairs?
- if (json_decode2_peek() != ",")
- break
- json_decode2_next(",")
- }
- if (!json_error)
- json_decode2_next("]")
- // Clean up on error
- if (json_error)
- {
- ds_list_destroy(list)
- return false
- }
- json_value = list
- json_value_type = e_json_value_type.ARRAY
- return true
- /// json_decode2_string()
- /// @desc DO NOT USE DIRECTLY!
- if (json_debug)
- show_debug_message("json_decode2_string")
- if (!json_decode2_next("\""))
- return false
- var str = "";
- while (true)
- {
- var char = string_char_at(json_string, json_pos);
- if (char = "\"" || char = "") // End of string/file
- break
- json_pos++
- if (char = "\t") // Tab is four spaces
- json_col += 4
- else
- json_col++
- if (char = "\\") // Special character
- {
- var next = string_char_at(json_string, json_pos);
- if (next = "\"")
- char = "\""
- else if (next = "\\")
- char = "\\"
- else if (next = "/")
- char = "/"
- else if (next = "b")
- char = "\b"
- else if (next = "f")
- char = "\f"
- else if (next = "n")
- char = "\n"
- else if (next = "r")
- char = "\r"
- else if (next = "t")
- char = "\t"
- else
- char += next
- json_pos++
- json_col++
- }
- else if (char = "\n") // New line
- {
- json_line++
- json_col = 1
- }
- str += char
- }
- if (!json_decode2_next("\""))
- return false
- json_value = str
- json_value_type = e_json_value_type.STRING
- if (json_debug)
- show_debug_message("json_decode2_string: " + json_value)
- return true
- /// json_decode2_number()
- /// @desc DO NOT USE DIRECTLY!
- if (json_debug)
- show_debug_message("json_decode2_number")
- var str = "";
- while (true)
- {
- var char = json_decode2_peek();
- // We found the end of the number
- if (string_lettersdigits(char) = "" && char != "." && char != "-" && char != "+")
- break
- json_decode2_next(char)
- str += char
- }
- json_value = string_get_real(str)
- json_value_type = e_json_value_type.NUMBER;
- if (is_undefined(json_value))
- {
- json_decode2_error("Invalid number '" + str + "'")
- return false
- }
- if (json_debug)
- show_debug_message("json_value: " + json_value)
- return true
- /// json_decode2_word(next)
- /// @arg next
- /// @desc DO NOT USE DIRECTLY!
- var next = argument0;
- if (json_debug)
- show_debug_message("json_decode2_other")
- if (next = "t")
- {
- json_decode2_next("t")
- json_decode2_next("r")
- json_decode2_next("u")
- json_decode2_next("e")
- json_value = true
- json_value_type = e_json_value_type.BOOL
- if (json_debug)
- show_debug_message("json_value: true")
- }
- else if (next = "f")
- {
- json_decode2_next("f")
- json_decode2_next("a")
- json_decode2_next("l")
- json_decode2_next("s")
- json_decode2_next("e")
- json_value = false
- json_value_type = e_json_value_type.BOOL
- if (json_debug)
- show_debug_message("json_value: false")
- }
- else if (next = "n")
- {
- json_decode2_next("n")
- json_decode2_next("u")
- json_decode2_next("l")
- json_decode2_next("l")
- json_value = undefined
- json_value_type = e_json_value_type.NULL
- if (json_debug)
- show_debug_message("json_value: null")
- }
- else
- return json_decode2_error("Unrecognized word")
- return true
- /// json_decode2_peek()
- /// @desc DO NOT USE DIRECTLY!
- /// Returns the next token character, "" if EOF
- return ds_queue_first(json_token_queue)
- /// json_decode2_next(expect)
- /// @arg expect The expected character
- /// @desc DO NOT USE DIRECTLY!
- var expect, token;
- expect = argument0
- if (json_debug)
- show_debug_message("json_decode2_next, expected: " + expect)
- if (ds_queue_size() = 0)
- return json_decode2_error("Unexpected end of file")
- token = ds_queue_dequeue(json_token_queue)
- if (token[0] != expect)
- return error..
- return true
- /// json_decode2_error(message)
- /// @arg message
- /// @desc DO NOT USE DIRECTLY!
- show_message("JSON error: " + argument0 + " at line " + string(json_line) + ", column " + string(json_col))
- json_error = true
- return false
- /// string_get_real(string, [invalid]):
- /// @arg string
- /// @arg [invalid]
- /// @desc An acceptable real number takes the following form:
- /// [whitespaces] [sign] [digits] [. [digits]] [{e | E} [sign] [digits]] [whitespaces]
- /// At least one digit or a decimal point must be present in prior to the exponent part
- var str, inv, len, state;
- str = argument[0]
- if (argument_count > 1)
- inv = argument[1]
- else
- inv = undefined
- // Trim white spaces at the end (to make it easier to check the final state)
- len = string_length(str)
- while (len > 1 && string_char_at(str, len) == " ")
- len--
- // Now check the string with a state machine
- state = 0
- for (var i = 1; i <= len; i++)
- {
- var c = string_char_at(str, i)
- if (c = " ") // Ignore white spaces at the beginning
- {
- if (state = 0)
- continue
- else
- return inv
- }
- else if (c = "-" || c = "+") // A sign is allowed at the beginning or after 'E'
- {
- if (state = 0 || state = 5)
- state++
- else
- return inv
- }
- else if (ord(c) >= ord("0") && ord(c) <= ord("9")) // A digit is allowed at serveral places...
- {
- if (state <= 2) // At the beginning, after a sign or another digit
- state = 2
- else if (state <= 4) // After a decimal point
- state = 4
- else if (state <= 7) // After an exponent
- state = 7
- else // Not allowed in other places
- return inv
- }
- else if (c = ".") // A decimal point is allowed at the beginning, after a sign or a digit
- {
- if (state <= 2)
- state = 3
- else return inv
- }
- else // No other letters are allowed
- {
- return inv
- }
- }
- // Finally, check that the string ends with at least one digit or a decimal point
- if (state >= 2)
- return real(str)
- return inv
Advertisement
Add Comment
Please, Sign In to add comment