mirror of
				https://github.com/KevinMidboe/linguist.git
				synced 2025-10-29 17:50:22 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			1862 lines
		
	
	
		
			56 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			1862 lines
		
	
	
		
			56 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
| // Originally from /jsonion.gml in JSOnion
 | |
| // JSOnion v1.0.0d is licensed under the MIT licence. You may freely adapt and use this library in commercial and non-commercial projects.
 | |
| 
 | |
| #define __jso_gmt_tuple
 | |
| {
 | |
| 
 | |
|   /**
 | |
|   tuple(>element_0<, >element_1<, ..., >element_n<): Return an n-tuple
 | |
|   @author: GameGeisha
 | |
|   @version: 1.2 (GMTuple)
 | |
|   */
 | |
| 
 | |
|   //Position, address table and data
 | |
|   var pos, addr_table, data;
 | |
|   pos = 6*argument_count+4;
 | |
|   addr_table = "";
 | |
|   data = "";
 | |
|   
 | |
|   //Build the tuple element-by-element
 | |
|   var i, ca, isstr, datastr;
 | |
|   for (i=0; i<argument_count; i+=1) {
 | |
|     //Check the argument's type
 | |
|     ca = argument[i];
 | |
|     isstr = is_string(ca);
 | |
|     if (isstr) { //Save strings as-is
 | |
|       datastr = ca;
 | |
|     }
 | |
|     else { //Save reals in scientific notation, 15 significant digits
 | |
|       datastr = __jso_gmt_numtostr(ca);
 | |
|     }
 | |
|     //Add entry in address table and data
 | |
|     addr_table += chr(isstr+$30)
 | |
|     addr_table += string_format(pos, 5, 0);
 | |
|     pos += string_length(datastr);
 | |
|     data += datastr;
 | |
|   }
 | |
|   
 | |
|   //Return the tuple, with size header character, address table and data
 | |
|   return string_format(argument_count, 3, 0)+addr_table+data;
 | |
|   
 | |
| }
 | |
| 
 | |
| #define __jso_gmt_elem
 | |
| {
 | |
| 
 | |
|   /**
 | |
|   elem(tuple_source, n): Return the <n>th element of <tuple_source>
 | |
|   @author: GameGeisha
 | |
|   @version: 1.2 (GMTuple)
 | |
|   */
 | |
| 
 | |
|   //Capture arguments
 | |
|   var t, n, size;
 | |
|   t = argument0;
 | |
|   n = argument1;
 | |
|   size = __jso_gmt_size(t);
 | |
|   
 | |
|   //Search for the bounding positions for the <n>th element in the address table
 | |
|   var start, afterend, isstr;
 | |
|   isstr = ord(string_char_at(t, 4+6*n))-$30;
 | |
|   start = real(string_copy(t, 5+6*n, 5));
 | |
|   if (n < size-1) {
 | |
|     afterend = real(string_copy(t, 11+6*n, 5));
 | |
|   } else {
 | |
|     afterend = string_length(t)+1;
 | |
|   }
 | |
|   
 | |
|   //Return the <n>th element with the correct type
 | |
|   if (isstr) {
 | |
|     return string_copy(t, start, afterend-start);
 | |
|   }
 | |
|   else {
 | |
|     return real(string_copy(t, start, afterend-start));
 | |
|   }
 | |
| 
 | |
| }
 | |
| 
 | |
| #define __jso_gmt_size
 | |
| {
 | |
|   
 | |
|   /**
 | |
|   size(tuple_source): Return the size of <tuple_source>
 | |
|   @author: GameGeisha
 | |
|   @version: 1.2 (GMTuple)
 | |
|   */
 | |
| 
 | |
|   return real(string_copy(argument0, 1, 3));
 | |
| 
 | |
| }
 | |
| 
 | |
| #define __jso_gmt_numtostr
 | |
| {
 | |
| 
 | |
|   /**
 | |
|   __gmt_numtostr(num): Return string representation of <num>. Decimal numbers expressed as scientific notation
 | |
|   with double precision (i.e. 15 digits)
 | |
|   @author: GameGeisha
 | |
|   @version: 1.2 (edited)
 | |
|   */
 | |
|   if (frac(argument0) == 0) {
 | |
|     return string(argument0);
 | |
|   }
 | |
|   
 | |
|     var mantissa, exponent;
 | |
|     exponent = floor(log10(abs(argument0)));
 | |
|     mantissa = string_format(argument0/power(10,exponent), 15, 14);
 | |
|     var i, ca;
 | |
|     i = string_length(mantissa);
 | |
|     do {
 | |
|       ca = string_char_at(mantissa, i);
 | |
|       i -= 1;
 | |
|     } until (ca != "0")
 | |
|     if (ca != ".") {
 | |
|         mantissa = string_copy(mantissa, 1, i+1);
 | |
|     }
 | |
|     else {
 | |
|         mantissa = string_copy(mantissa, 1, i);
 | |
|     }
 | |
|     if (exponent != 0) {
 | |
|       return mantissa + "e" + string(exponent);
 | |
|     }
 | |
|     else {
 | |
|       return mantissa;
 | |
|     }
 | |
| 
 | |
| }
 | |
| 
 | |
| #define __jso_gmt_test_all
 | |
| {
 | |
| 
 | |
|   /**
 | |
|   test_all(): Runs all test suites
 | |
|   @author: GameGeisha
 | |
|   @version: 1.2 (GMTuple)
 | |
|   */
 | |
|   
 | |
|   //Automated testing sequence
 | |
|   __jso_gmt_test_elem();
 | |
|   __jso_gmt_test_size();
 | |
|   __jso_gmt_test_numtostr();
 | |
| 
 | |
| }
 | |
| 
 | |
| #define __jso_gmt_test_numtostr
 | |
| {
 | |
|   
 | |
|   /**
 | |
|   _test_numtostr(): Runs number-to-string tests
 | |
|   @author: GameGeisha
 | |
|   @version: 1.2 (GMTuple)
 | |
|   */
 | |
| 
 | |
|   var tolerance;
 | |
|   tolerance = 1/10000000000;
 | |
| 
 | |
|   if (real(__jso_gmt_numtostr(9)) != 9) {
 | |
|     show_message("Scientific notation conversion failed for 1-digit integer! Result: " + __jso_gmt_numtostr(9));
 | |
|   }
 | |
|   if (real(__jso_gmt_numtostr(500)) != 500) {
 | |
|     show_message("Scientific notation conversion failed for 3-digit integer! Result: " + __jso_gmt_numtostr(500));
 | |
|   }
 | |
|   if (abs(real(__jso_gmt_numtostr(pi))-pi) > tolerance) {
 | |
|     show_message("Scientific notation conversion failed for pi! Result: " + __jso_gmt_numtostr(pi));
 | |
|   }
 | |
|   if (abs(real(__jso_gmt_numtostr(104729.903455))-104729.903455) > tolerance) {
 | |
|     show_message("Scientific notation conversion failed for large decimal number! Result: " + __jso_gmt_numtostr(104729.903455));
 | |
|   }
 | |
|   if (abs(real(__jso_gmt_numtostr(-pi))+pi) > tolerance) {
 | |
|     show_message("Scientific notation conversion failed for -pi! Result: " + __jso_gmt_numtostr(-pi));
 | |
|   }
 | |
|   if (abs(real(__jso_gmt_numtostr(1/pi))-1/pi) > tolerance) {
 | |
|     show_message("Scientific notation conversion failed for 1/pi! Result: " + __jso_gmt_numtostr(1/pi));
 | |
|   }
 | |
| 
 | |
| }
 | |
| 
 | |
| #define __jso_gmt_test_elem
 | |
| {
 | |
| 
 | |
|   /**
 | |
|   _test_elem(): Runs tuple element retrieval tests
 | |
|   @author: GameGeisha
 | |
|   @version: 1.2 (GMTuple)
 | |
|   */
 | |
| 
 | |
|   if (__jso_gmt_elem(__jso_gmt_tuple("Qblock", "kll"), 0) != "Qblock") {
 | |
|     show_message("Element retrieval failed for simple string. #Should be:Qblock#Actual:" + __jso_gmt_elem(__jso_gmt_tuple("Qblock"), 0));
 | |
|   }
 | |
|   if (__jso_gmt_elem(__jso_gmt_tuple(9, "Q", -7), 0) != 9) {
 | |
|     show_message("Element retrieval failed for simple number. #Should be 9#Actual:" + string(__jso_gmt_elem(__jso_gmt_tuple(9, "Q", 7), 0)));
 | |
|   }
 | |
|   if (__jso_gmt_elem(__jso_gmt_tuple("Qblock", "", "Negg"), 1) != "") {
 | |
|     show_message("Element retrieval failed for empty string#Should be empty string#Actual:"+string(__jso_gmt_elem(__jso_gmt_tuple("Qblock", "", "Negg"), 0)));
 | |
|   }
 | |
|   
 | |
|   if (__jso_gmt_elem(__jso_gmt_elem(__jso_gmt_tuple("Not this", __jso_gmt_tuple(0, 1, 2, 3), "Waahoo"), 1), 3) != 3) {
 | |
|     show_message("Element retrieval failed in nested tuple. #Should be 3#Actual:" + string(__jso_gmt_elem(__jso_gmt_elem(__jso_gmt_tuple("Not this", __jso_gmt_tuple(0, 1, 2, 3), "Waahoo"), 1), 3)));
 | |
|   }  
 | |
| 
 | |
| }
 | |
| 
 | |
| #define __jso_gmt_test_size
 | |
| {
 | |
| 
 | |
|   /**
 | |
|   _test_size(): Runs tuple size tests
 | |
|   @author: GameGeisha
 | |
|   @version: 1.2 (GMTuple)
 | |
|   */
 | |
| 
 | |
|   if (__jso_gmt_size(__jso_gmt_tuple("Waahoo", "Negg", 0)) != 3) {
 | |
|     show_message("Bad size for 3-tuple");
 | |
|   }
 | |
|   if (__jso_gmt_size(__jso_gmt_tuple()) != 0) {
 | |
|     show_message("Bad size for null tuple");
 | |
|   }
 | |
|   if (__jso_gmt_size(__jso_gmt_tuple(7)) != 1) {
 | |
|     show_message("Bad size for 1-tuple");
 | |
|   }
 | |
|   if (__jso_gmt_size(__jso_gmt_tuple(1,2,3,4,5,6,7,8,9,10)) != 10) {
 | |
|     show_message("Bad size for 10-tuple");
 | |
|   }
 | |
| 
 | |
| }
 | |
| 
 | |
| #define jso_new_map
 | |
| {
 | |
|     /**
 | |
|     jso_new_map(): Create a new map.
 | |
|     JSOnion version: 1.0.0d
 | |
|     */
 | |
|     return ds_map_create();
 | |
| }
 | |
| 
 | |
| #define jso_new_list
 | |
| {
 | |
|     /**
 | |
|     jso_new_list(): Create a new list.
 | |
|     JSOnion version: 1.0.0d
 | |
|     */
 | |
|     return ds_list_create();
 | |
| }
 | |
| 
 | |
| #define jso_map_add_real
 | |
| {
 | |
|     /**
 | |
|     jso_map_add_real(map, key, val): Add the key-value pair <key>:<val> to <map>, where <val> is a real value.
 | |
|     JSOnion version: 1.0.0d
 | |
|     */
 | |
|     ds_map_add(argument0, argument1, argument2);
 | |
| }
 | |
| 
 | |
| #define jso_map_add_string
 | |
| {
 | |
|     /**
 | |
|     jso_map_add_string(map, key, str): Add the key-value pair <key>:<str> to <map>, where <str> is a string value.
 | |
|     JSOnion version: 1.0.0d
 | |
|     */
 | |
|     ds_map_add(argument0, argument1, "s" + argument2);
 | |
| }
 | |
| 
 | |
| #define jso_map_add_sublist
 | |
| {
 | |
|     /**
 | |
|     jso_map_add_sublist(map, key, sublist): Add the key-value pair <key>:<sublist> to <map>, where <sublist> is a list.
 | |
|     JSOnion version: 1.0.0d
 | |
|     */
 | |
|     ds_map_add(argument0, argument1, "l" + string(argument2));
 | |
| }
 | |
| 
 | |
| #define jso_map_add_submap
 | |
| {
 | |
|     /**
 | |
|     jso_map_add_submap(map, key, submap): Add the key-value pair <key>:<submap> to <map>, where <submap> is a map.
 | |
|     JSOnion version: 1.0.0d
 | |
|     */
 | |
|     ds_map_add(argument0, argument1, "m" + string(argument2));
 | |
| }
 | |
| 
 | |
| #define jso_map_add_integer
 | |
| {
 | |
|     /**
 | |
|     jso_map_add_integer(map, key, int): Add the key-value pair <key>:<int> to <map>, where <int> is a integer value.
 | |
|     JSOnion version: 1.0.0d
 | |
|     */
 | |
|     ds_map_add(argument0, argument1, floor(argument2));
 | |
| }
 | |
| 
 | |
| #define jso_map_add_boolean
 | |
| {
 | |
|     /**
 | |
|     jso_map_add_boolean(map, key, bool): Add the key-value pair <key>:<bool> to <map>, where <bool> is a boolean value.
 | |
|     JSOnion version: 1.0.0d
 | |
|     */
 | |
|     if (argument2) {
 | |
|         ds_map_add(argument0, argument1, "btrue");
 | |
|     } else {
 | |
|         ds_map_add(argument0, argument1, "bfalse");
 | |
|     }
 | |
| }
 | |
| 
 | |
| #define jso_list_add_real
 | |
| {
 | |
|     /**
 | |
|     jso_list_add_real(list, val): Append the real value <val> to <list>.
 | |
|     JSOnion version: 1.0.0d
 | |
|     */
 | |
|     ds_list_add(argument0, argument1);
 | |
| }
 | |
| 
 | |
| #define jso_list_add_string
 | |
| {
 | |
|     /**
 | |
|     jso_list_add_string(list, str): Append the string value <str> to <list>.
 | |
|     JSOnion version: 1.0.0d
 | |
|     */
 | |
|     ds_list_add(argument0, "s" + argument1);
 | |
| }
 | |
| 
 | |
| #define jso_list_add_sublist
 | |
| {
 | |
|     /**
 | |
|     jso_list_add_sublist(list, sublist): Append the list <sublist> to <list>.
 | |
|     JSOnion version: 1.0.0d
 | |
|     */
 | |
|     ds_list_add(argument0, "l" + string(argument1));
 | |
| }
 | |
| 
 | |
| #define jso_list_add_submap
 | |
| {
 | |
|     /**
 | |
|     jso_list_add_submap(list, submap): Append the map <submap> to <list>.
 | |
|     JSOnion version: 1.0.0d
 | |
|     */
 | |
|     ds_list_add(argument0, "m" + string(argument1));
 | |
| }
 | |
| 
 | |
| #define jso_list_add_integer
 | |
| {
 | |
|     /**
 | |
|     jso_list_add_integer(list, int): Append the integer <int> to <list>.
 | |
|     JSOnion version: 1.0.0d
 | |
|     */
 | |
|     ds_list_add(argument0, floor(argument1));
 | |
| }
 | |
| 
 | |
| #define jso_list_add_boolean
 | |
| {
 | |
|     /**
 | |
|     jso_list_add_boolean(list, bool): Append the boolean value <bool> to <list>.
 | |
|     JSOnion version: 1.0.0d
 | |
|     */
 | |
|     if (argument1) {
 | |
|         ds_list_add(argument0, "btrue");
 | |
|     } else {
 | |
|         ds_list_add(argument0, "bfalse");
 | |
|     }
 | |
| }
 | |
| 
 | |
| #define jso_map_get
 | |
| {
 | |
|     /**
 | |
|     jso_map_get(map, key): Retrieve the value stored in <map> with the key value <key>, with the correct type.
 | |
|     JSOnion version: 1.0.0d
 | |
|     */
 | |
| 
 | |
|     //Grab the value
 | |
|     var v;
 | |
|     v = ds_map_find_value(argument0, argument1);
 | |
|     
 | |
|     //String; could be string, map or list
 | |
|     if (is_string(v)) {
 | |
|         switch (string_char_at(v, 1)) {
 | |
|             case "s":
 | |
|                 return string_delete(v, 1, 1);
 | |
|             break;
 | |
|             case "l": case "m":
 | |
|                 return real(string_delete(v, 1, 1));
 | |
|             break;
 | |
|             case "b":
 | |
|                 if (v == "btrue") {
 | |
|                     return true;
 | |
|                 }
 | |
|                 else if (v == "bfalse") {
 | |
|                     return false;
 | |
|                 }
 | |
|                 else {
 | |
|                     show_error("Invalid boolean value.", true);
 | |
|                 }
 | |
|             break;
 | |
|             default: show_error("Invalid map contents.", true); break;
 | |
|         }    
 | |
|     }
 | |
|     
 | |
|     //Real; return real value as-is
 | |
|     else {
 | |
|         return v;
 | |
|     }
 | |
| }
 | |
| 
 | |
| #define jso_map_get_type
 | |
| {
 | |
|     /**
 | |
|     jso_map_get_type(map, key): Return the type of value to which the key value <key> is mapped to in <map>.
 | |
|     JSOnion version: 1.0.0d
 | |
|     */
 | |
| 
 | |
|     //Grab the value
 | |
|     var v;
 | |
|     v = ds_map_find_value(argument0, argument1);
 | |
|     
 | |
|     //String; could be string, map or list
 | |
|     if (is_string(v)) {
 | |
|         switch (string_char_at(v, 1)) {
 | |
|             case "s":
 | |
|                 return jso_type_string;
 | |
|             break;
 | |
|             case "l":
 | |
|                 return jso_type_list;
 | |
|             break;
 | |
|             case "m":
 | |
|                 return jso_type_map;
 | |
|             break;
 | |
|             case "b":
 | |
|                 return jso_type_boolean;
 | |
|             break;
 | |
|             default: show_error("Invalid map content type.", true); break;
 | |
|         }    
 | |
|     }
 | |
|     
 | |
|     //Real
 | |
|     else {
 | |
|         return jso_type_real;
 | |
|     }
 | |
| }
 | |
| 
 | |
| #define jso_list_get
 | |
| {
 | |
|     /**
 | |
|     jso_list_get(list, index): Retrieve the value stored in <list> at position <index>, with the correct type.
 | |
|     JSOnion version: 1.0.0d
 | |
|     */
 | |
| 
 | |
|     //Grab the value
 | |
|     var v;
 | |
|     v = ds_list_find_value(argument0, argument1);
 | |
|     
 | |
|     //String; could be string, map or list
 | |
|     if (is_string(v)) {
 | |
|         switch (string_char_at(v, 1)) {
 | |
|             case "s":
 | |
|                 return string_delete(v, 1, 1);
 | |
|             break;
 | |
|             case "l": case "m":
 | |
|                 return real(string_delete(v, 1, 1));
 | |
|             break;
 | |
|             case "b":
 | |
|                 if (v == "btrue") {
 | |
|                     return true;
 | |
|                 }
 | |
|                 else if (v == "bfalse") {
 | |
|                     return false;
 | |
|                 }
 | |
|                 else {
 | |
|                     show_error("Invalid boolean value.", true);
 | |
|                 }
 | |
|             break;
 | |
|             default: show_error("Invalid list contents.", true); break;
 | |
|         }    
 | |
|     }
 | |
|     
 | |
|     //Real; return real value as-is
 | |
|     else {
 | |
|         return v;
 | |
|     }
 | |
| }
 | |
| 
 | |
| #define jso_list_get_type
 | |
| {
 | |
|     /**
 | |
|     jso_list_get_type(list, index): Retrieve the type of value found at position <index> of <list>.
 | |
|     JSOnion version: 1.0.0d
 | |
|     */
 | |
|     
 | |
|     //Grab the value
 | |
|     var v;
 | |
|     v = ds_list_find_value(argument0, argument1);
 | |
|     
 | |
|     //String; could be string, map or list
 | |
|     if (is_string(v)) {
 | |
|         switch (string_char_at(v, 1)) {
 | |
|             case "s":
 | |
|                 return jso_type_string;
 | |
|             break;
 | |
|             case "l":
 | |
|                 return jso_type_list;
 | |
|             break;
 | |
|             case "m":
 | |
|                 return jso_type_map;
 | |
|             break;
 | |
|             case "b":
 | |
|                 return jso_type_boolean;
 | |
|             break;
 | |
|             default: show_error("Invalid list content type.", true); break;
 | |
|         }    
 | |
|     }
 | |
|     
 | |
|     //Real
 | |
|     else {
 | |
|         return jso_type_real;
 | |
|     }
 | |
| }
 | |
| 
 | |
| #define jso_cleanup_map
 | |
| {
 | |
|     /**
 | |
|     jso_cleanup_map(map): Recursively free up <map>.
 | |
|     JSOnion version: 1.0.0d
 | |
|     */
 | |
|     
 | |
|     //Loop through all keys
 | |
|     var i, l, k;
 | |
|     l = ds_map_size(argument0);
 | |
|     k = ds_map_find_first(argument0);
 | |
|     for (i=0; i<l; i+=1) {
 | |
|     
 | |
|         //Look for values that need to be recursed    
 | |
|         switch (jso_map_get_type(argument0, k)) {
 | |
|             //Maps
 | |
|             case jso_type_map:
 | |
|                 jso_cleanup_map(jso_map_get(argument0, k));
 | |
|             break;
 | |
|             //Lists
 | |
|             case jso_type_list:
 | |
|                 jso_cleanup_list(jso_map_get(argument0, k));
 | |
|             break;
 | |
|         }
 | |
|         
 | |
|         //Find next key
 | |
|         k = ds_map_find_next(argument0, k);
 | |
|     }
 | |
|     
 | |
|     //Done, clean up
 | |
|     ds_map_destroy(argument0);
 | |
| }
 | |
| 
 | |
| #define jso_cleanup_list
 | |
| {
 | |
|     /**
 | |
|     jso_cleanup_list(list): Recursively free up <list>.
 | |
|     JSOnion version: 1.0.0d
 | |
|     */
 | |
|     
 | |
|     //Loop through all elements
 | |
|     var i, l, v;
 | |
|     l = ds_list_size(argument0);
 | |
|     for (i=0; i<l; i+=1) {
 | |
|         //Look for elements that need to be recursed
 | |
|         switch (jso_list_get_type(argument0, i)) {
 | |
|             //Maps
 | |
|             case jso_type_map:
 | |
|                 jso_cleanup_map(jso_list_get(argument0, i));
 | |
|             break;
 | |
|             //Lists
 | |
|             case jso_type_list:
 | |
|                 jso_cleanup_list(jso_list_get(argument0, i));
 | |
|             break;
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     //Done, clean up
 | |
|     ds_list_destroy(argument0);
 | |
| }
 | |
| 
 | |
| #define jso_encode_real
 | |
| {
 | |
|     /**
 | |
|     jso_encode_real(<real>): Return a JSON-encoded version of real value <real>.
 | |
|     This uses scientific notation with up to 15 significant digits for decimal values.
 | |
|     For integers, it just uses string().
 | |
|     This is adapted from the same algorithm used in GMTuple.
 | |
|     JSOnion version: 1.0.0d
 | |
|     */
 | |
|     
 | |
|     return __jso_gmt_numtostr(argument0);
 | |
| }
 | |
| 
 | |
| #define jso_encode_string
 | |
| {
 | |
|     /**
 | |
|     jso_encode_string(str): Return a JSON-encoded version of string <str>.
 | |
|     JSOnion version: 1.0.0d
 | |
|     */
 | |
|     
 | |
|     //Iteratively reconstruct the string
 | |
|     var i, l, s, c;
 | |
|     s = "";
 | |
|     l = string_length(argument0);
 | |
|     for (i=1; i<=l; i+=1) {
 | |
|         //Replace escape characters
 | |
|         c = string_char_at(argument0, i);
 | |
|         switch (ord(c)) {
 | |
|             case 34: case 92: case 47: //Double quotes, backslashes and slashes
 | |
|                 s += "\" + c;
 | |
|             break;
 | |
|             case 8: //Backspace
 | |
|                 s += "\b";
 | |
|             break;
 | |
|             case 12: //Form feed
 | |
|                 s += "\f";
 | |
|             break;
 | |
|             case 10: //New line
 | |
|                 s += "\n";
 | |
|             break;
 | |
|             case 13: //Carriage return
 | |
|                 s += "\r";
 | |
|             break;
 | |
|             case 9: //Horizontal tab
 | |
|                 s += "\t";
 | |
|             break;
 | |
|             default: //Not an escape character
 | |
|                 s += c;
 | |
|             break;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     //Add quotes
 | |
|     return '"' + s + '"';
 | |
| }
 | |
| 
 | |
| #define jso_encode_list
 | |
| {
 | |
|     /**
 | |
|     jso_encode_list(list): Return a JSON-encoded version of list <list>.
 | |
|     JSOnion version: 1.0.0d
 | |
|     */
 | |
|     
 | |
|     //Iteratively encode each element
 | |
|     var i, l, s;
 | |
|     s = "";
 | |
|     l = ds_list_size(argument0);
 | |
|     for (i=0; i<l; i+=1) {
 | |
|         //Prepend comma except for the first element
 | |
|         if (i > 0) {
 | |
|             s += ",";
 | |
|         }
 | |
|         //Select correct encoding for each element, then recursively encode each
 | |
|         switch (jso_list_get_type(argument0, i)) {
 | |
|             case jso_type_real:
 | |
|                 s += jso_encode_real(jso_list_get(argument0, i));
 | |
|             break;
 | |
|             case jso_type_string:
 | |
|                 s += jso_encode_string(jso_list_get(argument0, i));
 | |
|             break;
 | |
|             case jso_type_map:
 | |
|                 s += jso_encode_map(jso_list_get(argument0, i));
 | |
|             break;
 | |
|             case jso_type_list:
 | |
|                 s += jso_encode_list(jso_list_get(argument0, i));
 | |
|             break;
 | |
|             case jso_type_boolean:
 | |
|                 s += jso_encode_boolean(jso_list_get(argument0, i));
 | |
|             break;
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     //Done, add square brackets
 | |
|     return "[" + s + "]";
 | |
| }
 | |
| 
 | |
| #define jso_encode_map
 | |
| {
 | |
|     /**
 | |
|     jso_encode_map(map): Return a JSON-encoded version of map <map>.
 | |
|     JSOnion version: 1.0.0d
 | |
|     */
 | |
|     
 | |
|     //Go through every key-value pair
 | |
|     var i, l, k, s;
 | |
|     s = "";
 | |
|     l = ds_map_size(argument0);
 | |
|     k = ds_map_find_first(argument0);
 | |
|     for (i=0; i<l; i+=1) {
 | |
|         //Prefix , if there is preceding item
 | |
|         if (i > 0) {
 | |
|             s += ",";
 | |
|         }
 | |
|         //Find the key and encode it
 | |
|         if (is_real(k)) {
 | |
|             s += jso_encode_real(k);
 | |
|         } else {
 | |
|             s += jso_encode_string(k);
 | |
|         }
 | |
|         //Add the : separator
 | |
|         s += ":";
 | |
|         //Select correct encoding for each value, then recursively encode each   
 | |
|         switch (jso_map_get_type(argument0, k)) {
 | |
|             case jso_type_real:
 | |
|                 s += jso_encode_real(jso_map_get(argument0, k));
 | |
|             break;
 | |
|             case jso_type_string:
 | |
|                 s += jso_encode_string(jso_map_get(argument0, k));
 | |
|             break;
 | |
|             case jso_type_map:
 | |
|                 s += jso_encode_map(jso_map_get(argument0, k));
 | |
|             break;
 | |
|             case jso_type_list:
 | |
|                 s += jso_encode_list(jso_map_get(argument0, k));
 | |
|             break;
 | |
|             case jso_type_boolean:
 | |
|                 s += jso_encode_boolean(jso_map_get(argument0, k));
 | |
|             break;
 | |
|         }
 | |
|         //Get next key
 | |
|         k = ds_map_find_next(argument0, k);
 | |
|     }
 | |
|     
 | |
|     //Done, add braces
 | |
|     return "{" + s + "}";
 | |
| }
 | |
| 
 | |
| #define jso_encode_integer
 | |
| {
 | |
|     /**
 | |
|     jso_encode_integer(int): Return a JSON-encoded version of the integer value <int>.
 | |
|     JSOnion version: 1.0.0d
 | |
|     */
 | |
|     return string(floor(argument0));
 | |
| }
 | |
| 
 | |
| #define jso_encode_boolean
 | |
| {
 | |
|     /**
 | |
|     jso_encode_boolean(bool): Return a JSON-encoded version of the boolean value <bool>.
 | |
|     JSOnion version: 1.0.0d
 | |
|     */
 | |
|     if (argument0) {
 | |
|         return "true";
 | |
|     } else {
 | |
|         return "false";
 | |
|     }
 | |
| }
 | |
| 
 | |
| #define jso_decode_map
 | |
| {
 | |
|     /**
 | |
|     jso_decode_map(json): Return a JSOnion-compatible map representing the JSON string <json>.
 | |
|     JSOnion version: 1.0.0d
 | |
|     */
 | |
|     return __jso_gmt_elem(_jso_decode_map(argument0, 1), 0);
 | |
| }
 | |
| 
 | |
| #define jso_decode_list
 | |
| {
 | |
|     /**
 | |
|     jso_decode_list(json): Return a JSOnion-compatible list representing the JSON string <json>.
 | |
|     JSOnion version: 1.0.0d
 | |
|     */
 | |
|     return __jso_gmt_elem(_jso_decode_list(argument0, 1), 0);
 | |
| }
 | |
| 
 | |
| #define jso_decode_string
 | |
| {
 | |
|     /**
 | |
|     jso_decode_string(json): Return a string representing the JSON string <json>.
 | |
|     JSOnion version: 1.0.0d
 | |
|     */
 | |
|     return __jso_gmt_elem(_jso_decode_string(argument0, 1), 0);
 | |
| }
 | |
| 
 | |
| #define jso_decode_boolean
 | |
| {
 | |
|     /**
 | |
|     jso_decode_boolean(json): Return a boolean value representing the JSON string <json>.
 | |
|     JSOnion version: 1.0.0d
 | |
|     */
 | |
|     return __jso_gmt_elem(_jso_decode_boolean(argument0, 1), 0);
 | |
| }
 | |
| 
 | |
| #define jso_decode_real
 | |
| {
 | |
|     /**
 | |
|     jso_decode_real(json): Return a real value representing the JSON string <json>.
 | |
|     JSOnion version: 1.0.0d
 | |
|     */
 | |
|     return __jso_gmt_elem(_jso_decode_real(argument0, 1), 0);
 | |
| }
 | |
| 
 | |
| #define jso_decode_integer
 | |
| {
 | |
|     /**
 | |
|     jso_decode_integer(json): Return an integer value representing the JSON string <json>.
 | |
|     JSOnion version: 1.0.0d
 | |
|     */
 | |
|     return __jso_gmt_elem(_jso_decode_integer(argument0, 1), 0);
 | |
| }
 | |
| 
 | |
| #define _jso_decode_map
 | |
| {
 | |
|     /**
 | |
|     _jso_decode_map(json, startindex): Extract a map from JSON string <json> starting at position <startindex>.
 | |
|     Return a 2-tuple of the extracted map handle and the position after the ending }.
 | |
|     JSOnion version: 1.0.0d
 | |
|     */
 | |
|     var i, len, map;
 | |
|     i = argument1;
 | |
|     len = string_length(argument0);
 | |
|     map = jso_new_map();
 | |
|     
 | |
|     //Seek to first {
 | |
|     var c;
 | |
|     c = string_char_at(argument0, i);
 | |
|     if (c != "{") {
 | |
|         do {
 | |
|             i += 1;
 | |
|             c = string_char_at(argument0, i);
 | |
|             if (!_jso_is_whitespace_char(c)) && (c != "{") {
 | |
|                 show_error("Cannot parse map at position " + string(i), true);
 | |
|             }
 | |
|         } until (c == "{")
 | |
|     }
 | |
|     i += 1;
 | |
|     
 | |
|     //Read until end of JSON or ending }
 | |
|     var found_end, state, found, current_key;
 | |
|     found_end = false;
 | |
|     state = 0;
 | |
|     for (i=i; i<=len && !found_end; i+=1) {
 | |
|         c = string_char_at(argument0, i);
 | |
|         switch (state) {
 | |
|             //0: Looking for a key or closing }
 | |
|             case 0:
 | |
|                 switch (c) {
 | |
|                     case "}":
 | |
|                         found_end = true;
 | |
|                     break;
 | |
|                     case '"':
 | |
|                         found = _jso_decode_string(argument0, i);
 | |
|                         current_key = __jso_gmt_elem(found, 0);
 | |
|                         i = __jso_gmt_elem(found, 1)-1;
 | |
|                         state = 1;
 | |
|                     break;
 | |
|                     case "0": case "1": case "2": case "3": case "4": case "5": case "6": case "7": case "8": case "9": case "+": case "-":
 | |
|                         found = _jso_decode_real(argument0, i);
 | |
|                         current_key = __jso_gmt_elem(found, 0);
 | |
|                         i = __jso_gmt_elem(found, 1)-1;
 | |
|                         state = 1;
 | |
|                     break;
 | |
|                     default:
 | |
|                         if (!_jso_is_whitespace_char(c)) {
 | |
|                             show_error("Unexpected character at position " + string(i) + ".", true);
 | |
|                         }
 | |
|                     break;
 | |
|                 }
 | |
|             break;
 | |
|             //1: Looking for the : separator
 | |
|             case 1:
 | |
|                 switch (c) {
 | |
|                     case ":":
 | |
|                         state = 2;
 | |
|                     break;
 | |
|                     default:
 | |
|                         if (!_jso_is_whitespace_char(c)) {
 | |
|                             show_error("Unexpected character at position " + string(i) + ".", true);
 | |
|                         }
 | |
|                     break;
 | |
|                 }
 | |
|             break;
 | |
|             //2: Looking for a value
 | |
|             case 2:
 | |
|                 switch (c) {
 | |
|                     case "[":
 | |
|                         found = _jso_decode_list(argument0, i);
 | |
|                         jso_map_add_sublist(map, current_key, __jso_gmt_elem(found, 0));
 | |
|                         i = __jso_gmt_elem(found, 1)-1;
 | |
|                         state = 3;
 | |
|                     break;
 | |
|                     case "{":
 | |
|                         found = _jso_decode_map(argument0, i);
 | |
|                         jso_map_add_submap(map, current_key, __jso_gmt_elem(found, 0));
 | |
|                         i = __jso_gmt_elem(found, 1)-1;
 | |
|                         state = 3;
 | |
|                     break;
 | |
|                     case '"':
 | |
|                         found = _jso_decode_string(argument0, i);
 | |
|                         jso_map_add_string(map, current_key, __jso_gmt_elem(found, 0));
 | |
|                         i = __jso_gmt_elem(found, 1)-1;
 | |
|                         state = 3;
 | |
|                     break;
 | |
|                     case "0": case "1": case "2": case "3": case "4": case "5": case "6": case "7": case "8": case "9": case "+": case "-":
 | |
|                         found = _jso_decode_real(argument0, i);
 | |
|                         jso_map_add_real(map, current_key, __jso_gmt_elem(found, 0));
 | |
|                         i = __jso_gmt_elem(found, 1)-1;
 | |
|                         state = 3;
 | |
|                     break;
 | |
|                     case "t": case "f":
 | |
|                         found = _jso_decode_boolean(argument0, i);
 | |
|                         jso_map_add_boolean(map, current_key, __jso_gmt_elem(found, 0));
 | |
|                         i = __jso_gmt_elem(found, 1)-1;
 | |
|                         state = 3;
 | |
|                     break;
 | |
|                     default:
 | |
|                         if (!_jso_is_whitespace_char(c)) {
 | |
|                             show_error("Unexpected character at position " + string(i) + ".", true);
 | |
|                         }
 | |
|                     break;
 | |
|                 }
 | |
|             break;
 | |
|             //3: Done looking for an entry, want comma or }
 | |
|             case 3:
 | |
|                 switch (c) {
 | |
|                     case "}":
 | |
|                         found_end = true;
 | |
|                     break;
 | |
|                     case ",":
 | |
|                         state = 0;
 | |
|                     break;
 | |
|                     default:
 | |
|                         if (!_jso_is_whitespace_char(c)) {
 | |
|                             show_error("Unexpected character at position " + string(i) + ".", true);
 | |
|                         }
 | |
|                     break;
 | |
|                 }
 | |
|             break;
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     //Return extracted map with ending position if the ending } is found
 | |
|     if (found_end) {
 | |
|         return __jso_gmt_tuple(map, i);
 | |
|     }
 | |
|     //Ended too early, throw error
 | |
|     else {
 | |
|         show_error("Unexpected end of map in JSON string.", true);
 | |
|     }
 | |
| }
 | |
| 
 | |
| #define _jso_decode_list
 | |
| {
 | |
|     /**
 | |
|     _jso_decode_list(json, startindex): Extract a list from JSON string <json> starting at position <startindex>.
 | |
|     Return a 2-tuple of the extracted list handle and the position after the ending ].
 | |
|     JSOnion version: 1.0.0d
 | |
|     */
 | |
|     var i, len, list;
 | |
|     i = argument1;
 | |
|     len = string_length(argument0);
 | |
|     list = jso_new_list();
 | |
|     
 | |
|     //Seek to first [
 | |
|     var c;
 | |
|     c = string_char_at(argument0, i);
 | |
|     if (c != "[") {
 | |
|         do {
 | |
|             i += 1;
 | |
|             c = string_char_at(argument0, i);
 | |
|             if (!_jso_is_whitespace_char(c)) && (c != "[") {
 | |
|                 show_error("Cannot parse list at position " + string(i), true);
 | |
|             }
 | |
|         } until (c == "[")
 | |
|     }
 | |
|     i += 1;
 | |
|     
 | |
|     //Read until end of JSON or ending ]
 | |
|     var found_end, state, found;
 | |
|     found_end = false;
 | |
|     state = 0;
 | |
|     for (i=i; i<=len && !found_end; i+=1) {
 | |
|         c = string_char_at(argument0, i);
 | |
|         switch (state) {
 | |
|             //0: Looking for an item
 | |
|             case 0:
 | |
|                 switch (c) {
 | |
|                     case "]":
 | |
|                         found_end = true;
 | |
|                     break;
 | |
|                     case "[":
 | |
|                         found = _jso_decode_list(argument0, i);
 | |
|                         jso_list_add_sublist(list, __jso_gmt_elem(found, 0));
 | |
|                         i = __jso_gmt_elem(found, 1)-1;
 | |
|                         state = 1;
 | |
|                     break;
 | |
|                     case "{":
 | |
|                         found = _jso_decode_map(argument0, i);
 | |
|                         jso_list_add_submap(list, __jso_gmt_elem(found, 0));
 | |
|                         i = __jso_gmt_elem(found, 1)-1;
 | |
|                         state = 1;
 | |
|                     break;
 | |
|                     case '"':
 | |
|                         found = _jso_decode_string(argument0, i);
 | |
|                         jso_list_add_string(list, __jso_gmt_elem(found, 0));
 | |
|                         i = __jso_gmt_elem(found, 1)-1;
 | |
|                         state = 1;
 | |
|                     break;
 | |
|                     case "0": case "1": case "2": case "3": case "4": case "5": case "6": case "7": case "8": case "9": case "+": case "-":
 | |
|                         found = _jso_decode_real(argument0, i);
 | |
|                         jso_list_add_real(list, __jso_gmt_elem(found, 0));
 | |
|                         i = __jso_gmt_elem(found, 1)-1;
 | |
|                         state = 1;
 | |
|                     break;
 | |
|                     case "t": case "f":
 | |
|                         found = _jso_decode_boolean(argument0, i);
 | |
|                         jso_list_add_boolean(list, __jso_gmt_elem(found, 0));
 | |
|                         i = __jso_gmt_elem(found, 1)-1;
 | |
|                         state = 1;
 | |
|                     break;
 | |
|                     default:
 | |
|                         if (!_jso_is_whitespace_char(c)) {
 | |
|                             show_error("Unexpected character at position " + string(i) + ".", true);
 | |
|                         }
 | |
|                     break;
 | |
|                 }
 | |
|             break;
 | |
|             //1: Done looking for an item, want comma or ]
 | |
|             case 1:
 | |
|                 switch (c) {
 | |
|                     case "]":
 | |
|                         found_end = true;
 | |
|                     break;
 | |
|                     case ",":
 | |
|                         state = 0;
 | |
|                     break;
 | |
|                     default:
 | |
|                         if (!_jso_is_whitespace_char(c)) {
 | |
|                             show_error("Unexpected character at position " + string(i) + ".", true);
 | |
|                         }
 | |
|                     break;
 | |
|                 }
 | |
|             break;
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     //Return extracted list with ending position if the ending ] is found
 | |
|     if (found_end) {
 | |
|         return __jso_gmt_tuple(list, i);
 | |
|     }
 | |
|     //Ended too early, throw error
 | |
|     else {
 | |
|         show_error("Unexpected end of list in JSON string.", true);
 | |
|     }
 | |
| }
 | |
| 
 | |
| #define _jso_decode_string
 | |
| {
 | |
|     /**
 | |
|     _jso_decode_string(json, startindex): Extract a string from JSON string <json> starting at position <startindex>.
 | |
|     Return a 2-tuple of the extracted string and the position after the ending double quote.
 | |
|     JSOnion version: 1.0.0d
 | |
|     */
 | |
|     var i, len, str;
 | |
|     i = argument1;
 | |
|     len = string_length(argument0);
 | |
|     str = "";
 | |
|     
 | |
|     //Seek to first double quote
 | |
|     var c;
 | |
|     c = string_char_at(argument0, i);
 | |
|     if (c != '"') {
 | |
|         do {
 | |
|             i += 1;
 | |
|             c = string_char_at(argument0, i);
 | |
|         } until (c == '"')
 | |
|     }
 | |
|     i += 1;
 | |
|     
 | |
|     //Read until end of JSON or ending double quote
 | |
|     var found_end, escape_mode;
 | |
|     found_end = false;
 | |
|     escape_mode = false;
 | |
|     for (i=i; i<=len && !found_end; i+=1) {
 | |
|         c = string_char_at(argument0, i);
 | |
|         //Escape mode
 | |
|         if (escape_mode) {
 | |
|             switch (c) {
 | |
|                 case '"': case "\": case "/":
 | |
|                     str += c;
 | |
|                     escape_mode = false;
 | |
|                 break;
 | |
|                 case "b":
 | |
|                     str += chr(8);
 | |
|                     escape_mode = false;
 | |
|                 break;
 | |
|                 case "f":
 | |
|                     str += chr(12);
 | |
|                     escape_mode = false;
 | |
|                 break;
 | |
|                 case "n":
 | |
|                     str += chr(10);
 | |
|                     escape_mode = false;
 | |
|                 break;
 | |
|                 case "r":
 | |
|                     str += chr(13);
 | |
|                     escape_mode = false;
 | |
|                 break;
 | |
|                 case "t":
 | |
|                     str += chr(9);
 | |
|                     escape_mode = false;
 | |
|                 break;
 | |
|                 case "u":
 | |
|                     var u;
 | |
|                     if (len-i < 5) {
 | |
|                         show_error("Invalid escape character at position " + string(i) + ".", true);
 | |
|                     } else {
 | |
|                         str += chr(_jso_hex_to_decimal(string_copy(argument0, i+1, 4)));
 | |
|                         escape_mode = false;
 | |
|                         i += 4;
 | |
|                     }
 | |
|                 break;
 | |
|                 default:
 | |
|                     show_error("Invalid escape character at position " + string(i) + ".", true);
 | |
|                 break;
 | |
|             }
 | |
|         }
 | |
|         //Regular mode
 | |
|         else {
 | |
|             switch (c) {
 | |
|                 case '"': found_end = true; break;
 | |
|                 case "\": escape_mode = true; break;
 | |
|                 default: str += c; break;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     //Return extracted string with ending position if the ending double quote is found
 | |
|     if (found_end) {
 | |
|         return __jso_gmt_tuple(str, i);
 | |
|     }
 | |
|     //Ended too early, throw error
 | |
|     else {
 | |
|         show_error("Unexpected end of string in JSON string.", true);
 | |
|     }
 | |
| }
 | |
| 
 | |
| #define _jso_decode_boolean
 | |
| {
 | |
|     /**
 | |
|     _jso_decode_boolean(json, startindex): Extract a boolean from JSON string <json> starting at position <startindex>.
 | |
|     Return a 2-tuple of the extracted boolean and the position after the last e.
 | |
|     JSOnion version: 1.0.0d
 | |
|     */
 | |
|     var i, len, str;
 | |
|     i = argument1;
 | |
|     len = string_length(argument0);
 | |
|     
 | |
|     //Seek to first t or f that can be found
 | |
|     var c;
 | |
|     c = string_char_at(argument0, i);
 | |
|     if (c != "t") && (c != "f") {
 | |
|         do {
 | |
|             i += 1;
 | |
|             c = string_char_at(argument0, i);
 | |
|             if (!_jso_is_whitespace_char(c)) && (c != "t") && (c != "f") {
 | |
|                 show_error("Cannot parse boolean value at position " + string(i), true);
 | |
|             }
 | |
|         } until (c == "t") || (c == "f")
 | |
|     }
 | |
|     
 | |
|     //Look for true if t is found
 | |
|     if (c == "t") && (string_copy(argument0, i, 4) == "true") {
 | |
|         return __jso_gmt_tuple(true, i+4);
 | |
|     }
 | |
|     //Look for false if f is found
 | |
|     else if (c == "f") && (string_copy(argument0, i, 5) == "false") {
 | |
|         return __jso_gmt_tuple(false, i+5);
 | |
|     }
 | |
|     //Error: unexpected ending
 | |
|     else {
 | |
|         show_error("Unexpected end of boolean in JSON string.", true);
 | |
|     }
 | |
| }
 | |
| 
 | |
| #define _jso_decode_real
 | |
| {
 | |
|     /**
 | |
|     _jso_decode_real(json, startindex): Extract a real value from JSON string <json> starting at position <startindex>.
 | |
|     Return a 2-tuple of the extracted real value and the position after the real value.
 | |
|     JSOnion version: 1.0.0d
 | |
|     */
 | |
|     var i, len, str;
 | |
|     i = argument1;
 | |
|     len = string_length(argument0);
 | |
|     str = "";
 | |
|     
 | |
|     //Seek to first character: +, -, or 0-9
 | |
|     var c;
 | |
|     c = string_char_at(argument0, i);
 | |
|     if (string_pos(c, "0123456789+-") == 0) {
 | |
|         do {
 | |
|             i += 1;
 | |
|             c = string_char_at(argument0, i);
 | |
|             if (!_jso_is_whitespace_char(c)) && (string_pos(c, "0123456789+-") == 0) {
 | |
|                 show_error("Cannot parse real value at position " + string(i), true);
 | |
|             }
 | |
|         } until (string_pos(c, "0123456789+-") > 0)
 | |
|     }
 | |
|     
 | |
|     //Determine starting state
 | |
|     var state;
 | |
|     switch (c) {
 | |
|         case "+": case "-":
 | |
|             state = 0;
 | |
|         break;
 | |
|         default:
 | |
|             state = 1;
 | |
|         break;
 | |
|     }
 | |
|     str += c;
 | |
|     i += 1;
 | |
|     
 | |
|     //Loop until no more digits found
 | |
|     var done;
 | |
|     done = false;
 | |
|     for (i=i; i<=len && !done; i+=1) {
 | |
|         c = string_char_at(argument0, i);
 | |
|         switch (state) {
 | |
|             //0: Found a sign, looking for a starting number
 | |
|             case 0:
 | |
|                 switch (c) {
 | |
|                     case "0": case "1": case "2": case "3": case "4": case "5": case "6": case "7": case "8": case "9":
 | |
|                         str += c;
 | |
|                         state = 1;
 | |
|                     break;
 | |
|                     default:
 | |
|                         show_error("Unexpected character at position " + string(i) + ", expecting a digit.", true);
 | |
|                     break;
 | |
|                 }
 | |
|             break;
 | |
|             //1: Found a starting digit, looking for decimal dot, e, E, or more digits
 | |
|             case 1:
 | |
|                 if (_jso_is_whitespace_char(c)) || (string_pos(c, ":,]}") > 0) {
 | |
|                     done = true;
 | |
|                     i -= 1;
 | |
|                 } else {
 | |
|                     switch (c) {
 | |
|                         case "0": case "1": case "2": case "3": case "4": case "5": case "6": case "7": case "8": case "9":
 | |
|                             str += c;
 | |
|                         break;
 | |
|                         case ".":
 | |
|                             str += c;
 | |
|                             state = 2;
 | |
|                         break;
 | |
|                         case "e": case "E":
 | |
|                             str += c;
 | |
|                             state = 3;
 | |
|                         break;
 | |
|                         default:
 | |
|                             show_error("Unexpected character at position " + string(i) + ", expecting a dot, e, E or a digit.", true);
 | |
|                         break;
 | |
|                     }
 | |
|                 }
 | |
|             break;
 | |
|             //2: Found a decimal dot, looking for more digits
 | |
|             case 2:
 | |
|                 switch (c) {
 | |
|                     case "0": case "1": case "2": case "3": case "4": case "5": case "6": case "7": case "8": case "9":
 | |
|                         str += c;
 | |
|                         state = -2;
 | |
|                     break;
 | |
|                     default:
 | |
|                         show_error("Unexpected character at position " + string(i) + ", expecting a digit.", true);
 | |
|                     break;
 | |
|                 }
 | |
|             break;
 | |
|             //-2: Found a decimal dot and a digit after it, looking for more digits, e, or E
 | |
|             case -2:
 | |
|                 if (_jso_is_whitespace_char(c)) || (string_pos(c, ":,]}") > 0) {
 | |
|                     done = true;
 | |
|                     i -= 1;
 | |
|                 } else {
 | |
|                     switch (c) {
 | |
|                         case "0": case "1": case "2": case "3": case "4": case "5": case "6": case "7": case "8": case "9":
 | |
|                             str += c;
 | |
|                         break;
 | |
|                         case "e": case "E":
 | |
|                             str += c;
 | |
|                             state = 3;
 | |
|                         break;
 | |
|                         default:
 | |
|                             show_error("Unexpected character at position " + string(i) + ", expecting an e, E or a digit.", true);
 | |
|                         break;
 | |
|                     }
 | |
|                 }
 | |
|             break;
 | |
|             //3: Found an e/E, looking for +, - or more digits
 | |
|             case 3:
 | |
|                 switch (c) {
 | |
|                     case "+": case "-":
 | |
|                         str += c;
 | |
|                         state = 4;
 | |
|                     break;
 | |
|                     case "0": case "1": case "2": case "3": case "4": case "5": case "6": case "7": case "8": case "9":
 | |
|                         str += c;
 | |
|                         state = 5;
 | |
|                     break;
 | |
|                     default:
 | |
|                         show_error("Unexpected character at position " + string(i) + ", expecting a +, - or a digit.", true);
 | |
|                     break;
 | |
|                 }
 | |
|             break;
 | |
|             //4: Found an e/E exponent sign, looking for more digits
 | |
|             case 4:
 | |
|                 switch (c) {
 | |
|                     case "0": case "1": case "2": case "3": case "4": case "5": case "6": case "7": case "8": case "9":
 | |
|                         str += c;
 | |
|                         state = 5;
 | |
|                     break;
 | |
|                     default:
 | |
|                         show_error("Unexpected character at position " + string(i) + ", expecting a digit.", true);
 | |
|                     break;
 | |
|                 }
 | |
|             break;
 | |
|             //5: Looking for final digits of the exponent
 | |
|             case 5:
 | |
|                 if (_jso_is_whitespace_char(c)) || (string_pos(c, ":,]}") > 0) {
 | |
|                     done = true;
 | |
|                     i -= 1;
 | |
|                 } else {
 | |
|                     switch (c) {
 | |
|                         case "0": case "1": case "2": case "3": case "4": case "5": case "6": case "7": case "8": case "9":
 | |
|                             str += c;
 | |
|                             state = 5;
 | |
|                         break;
 | |
|                         default:
 | |
|                             show_error("Unexpected character at position " + string(i) + ", expecting a digit.", true);
 | |
|                         break;
 | |
|                     }
 | |
|                 }
 | |
|             break;
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     //Am I still expecting more characters?
 | |
|     if (done) || (state == 1) || (state == -2) || (state == 5) {
 | |
|         return __jso_gmt_tuple(real(str), i);
 | |
|     }
 | |
|     //Error: unexpected ending
 | |
|     else {
 | |
|         show_error("Unexpected end of real in JSON string.", true);
 | |
|     }
 | |
| }
 | |
| 
 | |
| #define _jso_decode_integer
 | |
| {
 | |
|     /**
 | |
|     _jso_decode_real(json, startindex): Extract a real value from JSON string <json> starting at position <startindex>.
 | |
|     Return a 2-tuple of the extracted integer value (with leading i) and the position after the real value.
 | |
|     JSOnion version: 1.0.0d
 | |
|     */
 | |
|     var i, len, str;
 | |
|     i = argument1;
 | |
|     len = string_length(argument0);
 | |
|     str = "";
 | |
|     
 | |
|     //Seek to first character: +, -, or 0-9
 | |
|     var c;
 | |
|     c = string_char_at(argument0, i);
 | |
|     if (string_pos(c, "0123456789+-") == 0) {
 | |
|         do {
 | |
|             i += 1;
 | |
|             c = string_char_at(argument0, i);
 | |
|             if (!_jso_is_whitespace_char(c)) && (string_pos(c, "0123456789+-") == 0) {
 | |
|                 show_error("Cannot parse integer value at position " + string(i), true);
 | |
|             }
 | |
|         } until (string_pos(c, "0123456789+-") > 0)
 | |
|     }
 | |
|     
 | |
|     //Determine starting state
 | |
|     var state;
 | |
|     switch (c) {
 | |
|         case "+": case "-":
 | |
|             state = 0;
 | |
|         break;
 | |
|         default:
 | |
|             state = 1;
 | |
|         break;
 | |
|     }
 | |
|     str += c;
 | |
|     i += 1;
 | |
|     
 | |
|     //Loop until no more digits found
 | |
|     var done;
 | |
|     done = false;
 | |
|     for (i=i; i<=len && !done; i+=1) {
 | |
|         c = string_char_at(argument0, i);
 | |
|         switch (state) {
 | |
|             //0: Found a sign, looking for a starting number
 | |
|             case 0:
 | |
|                 switch (c) {
 | |
|                     case "0": case "1": case "2": case "3": case "4": case "5": case "6": case "7": case "8": case "9":
 | |
|                         str += c;
 | |
|                         state = 1;
 | |
|                     break;
 | |
|                     default:
 | |
|                         show_error("Unexpected character at position " + string(i) + ", expecting a digit.", true);
 | |
|                     break;
 | |
|                 }
 | |
|             break;
 | |
|             //1: Found a starting digit, looking for decimal dot, e, E, or more digits
 | |
|             case 1:
 | |
|                 if (_jso_is_whitespace_char(c)) || (string_pos(c, ":,]}") > 0) {
 | |
|                     done = true;
 | |
|                     i -= 1;
 | |
|                 } else {
 | |
|                     switch (c) {
 | |
|                         case "0": case "1": case "2": case "3": case "4": case "5": case "6": case "7": case "8": case "9":
 | |
|                             str += c;
 | |
|                         break;
 | |
|                         default:
 | |
|                             show_error("Unexpected character at position " + string(i) + ", expecting a digit.", true);
 | |
|                         break;
 | |
|                     }
 | |
|                 }
 | |
|             break;
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     //Am I still expecting more characters?
 | |
|     if (done) || (state == 1) {
 | |
|         return __jso_gmt_tuple(floor(real(str)), i);
 | |
|     }
 | |
|     //Error: unexpected ending
 | |
|     else {
 | |
|         show_error("Unexpected end of integer in JSON string.", true);
 | |
|     }
 | |
| }
 | |
| 
 | |
| #define jso_compare_maps
 | |
| {
 | |
|     /**
 | |
|     jso_compare_maps(map1, map2): Return whether the contents of JSOnion-compatible maps <map1> and <map2> are the same.
 | |
|     JSOnion version: 1.0.0d
 | |
|     */
 | |
|     
 | |
|     //If they aren't the same size, they can't be the same
 | |
|     var size;
 | |
|     size = ds_map_size(argument0);
 | |
|     if (size != ds_map_size(argument1)) {
 | |
|         return false;
 | |
|     }
 | |
|     
 | |
|     //Compare contents pairwise
 | |
|     var i, k, type, a, b;
 | |
|     k = ds_map_find_first(argument0);
 | |
|     for (i=0; i<size; i+=1) {
 | |
|         //Check that key exists on both sides
 | |
|         if (!ds_map_exists(argument1, k)) {
 | |
|             return false;
 | |
|         }
 | |
|         //Check type
 | |
|         type = jso_map_get_type(argument0, k);
 | |
|         if (jso_map_get_type(argument1, k) != type) {
 | |
|             return false;
 | |
|         }
 | |
|         //Check content
 | |
|         a = jso_map_get(argument0, k);
 | |
|         b = jso_map_get(argument1, k);
 | |
|         switch (type) {
 | |
|             case jso_type_map:
 | |
|                 if (!jso_compare_maps(a, b)) {
 | |
|                     return false;
 | |
|                 }
 | |
|             break;
 | |
|             case jso_type_list:
 | |
|                 if (!jso_compare_lists(a, b)) {
 | |
|                     return false;
 | |
|                 }
 | |
|             break;
 | |
|             default:
 | |
|                 if (a != b) {
 | |
|                     return false;
 | |
|                 }
 | |
|             break;
 | |
|         }
 | |
|         //Advance to next key
 | |
|         k = ds_map_find_next(argument0, k);
 | |
|     }
 | |
|     
 | |
|     //No mismatches, return true
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| #define jso_compare_lists
 | |
| {
 | |
|     /**
 | |
|     jso_compare_lists(list1, list2): Return whether the contents of JSOnion-compatible lists <list1> and <list2> are the same.
 | |
|     JSOnion version: 1.0.0d
 | |
|     */
 | |
|     
 | |
|     //If they aren't the same size, they can't be the same
 | |
|     var size;
 | |
|     size = ds_list_size(argument0);
 | |
|     if (size != ds_list_size(argument1)) {
 | |
|         return false;
 | |
|     }
 | |
|     
 | |
|     //Compare contents pairwise
 | |
|     var i, type, a, b;
 | |
|     for (i=0; i<size; i+=1) {
 | |
|         //Check type
 | |
|         type = jso_list_get_type(argument0, i);
 | |
|         if (jso_list_get_type(argument1, i) != type) {
 | |
|             return false;
 | |
|         }
 | |
|         //Check content
 | |
|         a = jso_list_get(argument0, i);
 | |
|         b = jso_list_get(argument1, i);
 | |
|         switch (type) {
 | |
|             case jso_type_map:
 | |
|                 if (!jso_compare_maps(a, b)) {
 | |
|                     return false;
 | |
|                 }
 | |
|             break;
 | |
|             case jso_type_list:
 | |
|                 if (!jso_compare_lists(a, b)) {
 | |
|                     return false;
 | |
|                 }
 | |
|             break;
 | |
|             default:
 | |
|                 if (a != b) {
 | |
|                     return false;
 | |
|                 }
 | |
|             break;
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     //No mismatches, return true
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| #define jso_map_check
 | |
| {
 | |
|     /**
 | |
|     jso_map_check(map, key1, key2, ...): Recursively look up keys/indices in the top-level map <map>, return whether a value exists there.
 | |
|     JSOnion version: 1.0.0d
 | |
|     */
 | |
|     
 | |
|     //Catch empty calls
 | |
|     if (argument_count < 2) {
 | |
|         show_error("Expected at least 2 arguments, got " + string(argument_count) + ".", true);
 | |
|     }
 | |
|     
 | |
|     //Build list of keys/indices
 | |
|     var i, key_list;
 | |
|     key_list = ds_list_create();
 | |
|     for (i=1; i<argument_count; i+=1) {
 | |
|         ds_list_add(key_list, argument[i]);
 | |
|     }
 | |
|     
 | |
|     //Call lookup kernel and cleanup
 | |
|     var result;
 | |
|     result = _jso_lookup_kernel(argument[0], jso_type_map, 0, key_list);
 | |
|     ds_list_destroy(key_list);
 | |
|     
 | |
|     //Done
 | |
|     return result;
 | |
| }
 | |
| 
 | |
| #define jso_list_check
 | |
| {
 | |
|     /**
 | |
|     jso_list_check(list, key1, key2, ...): Recursively look up keys/indices in the top-level list <list>, return whether a value exists there.
 | |
|     JSOnion version: 1.0.0d
 | |
|     */
 | |
|     
 | |
|     //Catch empty calls
 | |
|     if (argument_count < 2) {
 | |
|         show_error("Expected at least 2 arguments, got " + string(argument_count) + ".", true);
 | |
|     }
 | |
|     
 | |
|     //Build list of keys/indices
 | |
|     var i, key_list;
 | |
|     key_list = ds_list_create();
 | |
|     for (i=1; i<argument_count; i+=1) {
 | |
|         ds_list_add(key_list, argument[i]);
 | |
|     }
 | |
|     
 | |
|     //Call lookup kernel and cleanup
 | |
|     var result;
 | |
|     result = _jso_lookup_kernel(argument[0], jso_type_list, 0, key_list);
 | |
|     ds_list_destroy(key_list);
 | |
|     
 | |
|     //Done
 | |
|     return result;
 | |
| }
 | |
| 
 | |
| #define jso_map_lookup
 | |
| {
 | |
|     /**
 | |
|     jso_map_lookup(map, key1, key2, ...): Recursively look up keys/indices in the top-level map <map>, return the value that exists there.
 | |
|     JSOnion version: 1.0.0d
 | |
|     */
 | |
|     
 | |
|     //Catch empty calls
 | |
|     if (argument_count < 2) {
 | |
|         show_error("Expected at least 2 arguments, got " + string(argument_count) + ".", true);
 | |
|     }
 | |
|     
 | |
|     //Build list of keys/indices
 | |
|     var i, key_list;
 | |
|     key_list = ds_list_create();
 | |
|     for (i=1; i<argument_count; i+=1) {
 | |
|         ds_list_add(key_list, argument[i]);
 | |
|     }
 | |
|     
 | |
|     //Call lookup kernel and cleanup
 | |
|     var result;
 | |
|     result = _jso_lookup_kernel(argument[0], jso_type_map, 1, key_list);
 | |
|     ds_list_destroy(key_list);
 | |
|     
 | |
|     //Done
 | |
|     return result;
 | |
| }
 | |
| 
 | |
| #define jso_map_lookup_type
 | |
| {
 | |
|     /**
 | |
|     jso_map_lookup_type(map, key1, key2, ...): Recursively look up keys/indices in the top-level map <map>, return the type of value that exists there.
 | |
|     JSOnion version: 1.0.0d
 | |
|     */
 | |
|     
 | |
|     //Catch empty calls
 | |
|     if (argument_count < 2) {
 | |
|         show_error("Expected at least 2 arguments, got " + string(argument_count) + ".", true);
 | |
|     }
 | |
|     
 | |
|     //Build list of keys/indices
 | |
|     var i, key_list;
 | |
|     key_list = ds_list_create();
 | |
|     for (i=1; i<argument_count; i+=1) {
 | |
|         ds_list_add(key_list, argument[i]);
 | |
|     }
 | |
|     
 | |
|     //Call lookup kernel and cleanup
 | |
|     var result;
 | |
|     result = _jso_lookup_kernel(argument[0], jso_type_map, 2, key_list);
 | |
|     ds_list_destroy(key_list);
 | |
|     
 | |
|     //Done
 | |
|     return result;
 | |
| }
 | |
| 
 | |
| #define jso_list_lookup
 | |
| {
 | |
|     /**
 | |
|     jso_list_lookup(list, key1, key2, ...): Recursively look up keys/indices in the top-level list <list>, return the value that exists there.
 | |
|     JSOnion version: 1.0.0d
 | |
|     */
 | |
|     
 | |
|     //Catch empty calls
 | |
|     if (argument_count < 2) {
 | |
|         show_error("Expected at least 2 arguments, got " + string(argument_count) + ".", true);
 | |
|     }
 | |
|     
 | |
|     //Build list of keys/indices
 | |
|     var i, key_list;
 | |
|     key_list = ds_list_create();
 | |
|     for (i=1; i<argument_count; i+=1) {
 | |
|         ds_list_add(key_list, argument[i]);
 | |
|     }
 | |
|     
 | |
|     //Call lookup kernel and cleanup
 | |
|     var result;
 | |
|     result = _jso_lookup_kernel(argument[0], jso_type_list, 1, key_list);
 | |
|     ds_list_destroy(key_list);
 | |
|     
 | |
|     //Done
 | |
|     return result;
 | |
| }
 | |
| 
 | |
| #define jso_list_lookup_type
 | |
| {
 | |
|     /**
 | |
|     jso_list_lookup_type(list, key1, key2, ...): Recursively look up keys/indices in the top-level list <list>, return the type of value that exists there.
 | |
|     JSOnion version: 1.0.0d
 | |
|     */
 | |
|     
 | |
|     //Catch empty calls
 | |
|     if (argument_count < 2) {
 | |
|         show_error("Expected at least 2 arguments, got " + string(argument_count) + ".", true);
 | |
|     }
 | |
|     
 | |
|     //Build list of keys/indices
 | |
|     var i, key_list;
 | |
|     key_list = ds_list_create();
 | |
|     for (i=1; i<argument_count; i+=1) {
 | |
|         ds_list_add(key_list, argument[i]);
 | |
|     }
 | |
|     
 | |
|     //Call lookup kernel and cleanup
 | |
|     var result;
 | |
|     result = _jso_lookup_kernel(argument[0], jso_type_list, 2, key_list);
 | |
|     ds_list_destroy(key_list);
 | |
|     
 | |
|     //Done
 | |
|     return result;
 | |
| }
 | |
| 
 | |
| #define _jso_lookup_kernel
 | |
| {
 | |
|     /**
 | |
|     _jso_lookup_kernel(jso_ds, jso_type, task_type, ds_args_list): Kernel of the check and lookup functions.
 | |
|     - jso_ds: The JSOnion-compatible data structure handle.
 | |
|     - jso_type: The type of the JSOnion-compatible (jso_type_list or jso_type_map)
 | |
|     - task_type: 0=check, 1=lookup, 2=lookup type
 | |
|     - ds_args_list: ds_list of arguments passed in.
 | |
|     
 | |
|     PLEASE DO NOT CALL DIRECTLY --- use regular jso_*() functions.
 | |
|     JSOnion version: 1.0.0d
 | |
|     */
 | |
|     
 | |
|     var i, k, data, type, type_string, task_type, keys_size, ds_args_list;
 | |
|     data = argument0;
 | |
|     type = argument1;
 | |
|     if (type == jso_type_map) {
 | |
|         type_string = "map";
 | |
|     } else {
 | |
|         type_string = "list";
 | |
|     }
 | |
|     task_type = argument2;
 | |
|     ds_args_list = argument3;
 | |
|     keys_size = ds_list_size(ds_args_list);
 | |
|     
 | |
|     //Iteratively go through arguments in ds_args_list
 | |
|     for (i=0; i<keys_size; i+=1) {
 | |
|         k = ds_list_find_value(argument3, i);
 | |
|         switch (type) {
 | |
|             //Check for existence of the key in the current map
 | |
|             case jso_type_map:
 | |
|                 if (!ds_map_exists(data, k)) {
 | |
|                     switch (task_type) {
 | |
|                         case 0:
 | |
|                             return false;
 | |
|                         break;
 | |
|                         default:
 | |
|                             show_error("Cannot find value in " + type_string + " lookup.", true);
 | |
|                         break;
 | |
|                     }
 | |
|                 }
 | |
|                 type = jso_map_get_type(data, k);
 | |
|                 data = jso_map_get(data, k);
 | |
|             break;
 | |
|             //Check for existence of the index in the current list
 | |
|             case jso_type_list:
 | |
|                 if (is_string(k)) {
 | |
|                     switch (task_type) {
 | |
|                         case 0:
 | |
|                             return false;
 | |
|                         break;
 | |
|                         default:
 | |
|                             show_error("Cannot use string indices for nested lists in " + type_string + " lookup.", true);
 | |
|                         break;
 | |
|                     }
 | |
|                 }
 | |
|                 if (k >= ds_list_size(data)) {
 | |
|                     switch (task_type) {
 | |
|                         case 0:
 | |
|                             return false;
 | |
|                         break;
 | |
|                         default:
 | |
|                             show_error("Index overflow for nested lists in " + type_string + " lookup.", true);
 | |
|                         break;
 | |
|                     }
 | |
|                 }
 | |
|                 type = jso_list_get_type(data, k);
 | |
|                 data = jso_list_get(data, k);
 | |
|             break;
 | |
|             //Trying to go through a leaf; don't attempt to look further
 | |
|             default:
 | |
|                 switch (task_type) {
 | |
|                     case 0:
 | |
|                         return false;
 | |
|                     break;
 | |
|                     default:
 | |
|                         show_error("Recursive overflow in " + type_string + " lookup.", true);
 | |
|                     break;
 | |
|                 }
 | |
|             break;
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     //Can find something, return the value requested by the task
 | |
|     switch (task_type) {
 | |
|         case 0:
 | |
|             return true;
 | |
|         break;
 | |
|         case 1:
 | |
|             return data;
 | |
|         break;
 | |
|         case 2:
 | |
|             return type;
 | |
|         break;
 | |
|     }
 | |
| }
 | |
| 
 | |
| #define _jso_is_whitespace_char
 | |
| {
 | |
|     /**
 | |
|     _jso_is_whitespace_char(char): Return whether <char> is a whitespace character.
 | |
|     Definition of whitespace is given by Unicode 6.0, Chapter 4.6.
 | |
|     JSOnion version: 1.0.0d
 | |
|     */
 | |
|     switch (ord(argument0)) {
 | |
|         case $0009:
 | |
|         case $000A:
 | |
|         case $000B:
 | |
|         case $000C:
 | |
|         case $000D:
 | |
|         case $0020:
 | |
|         case $0085:
 | |
|         case $00A0:
 | |
|         case $1680:
 | |
|         case $180E:
 | |
|         case $2000:
 | |
|         case $2001:
 | |
|         case $2002:
 | |
|         case $2003:
 | |
|         case $2004:
 | |
|         case $2005:
 | |
|         case $2006:
 | |
|         case $2007:
 | |
|         case $2008:
 | |
|         case $2009:
 | |
|         case $200A:
 | |
|         case $2028:
 | |
|         case $2029:
 | |
|         case $202F:
 | |
|         case $205F:
 | |
|         case $3000:
 | |
|             return true;
 | |
|     }
 | |
|     return false;
 | |
| }
 | |
| 
 | |
| #define _jso_hex_to_decimal
 | |
| {
 | |
|     /**
 | |
|     _jso_hex_to_decimal(hex_string): Return the decimal value of the hex number represented by <hex_string>
 | |
|     JSOnion version: 1.0.0d
 | |
|     */
 | |
|     var hex_string, hex_digits;
 | |
|     hex_string = string_lower(argument0);
 | |
|     hex_digits = "0123456789abcdef";
 | |
|     
 | |
|     //Convert digit-by-digit
 | |
|     var i, len, digit_value, num;
 | |
|     len = string_length(hex_string);
 | |
|     num = 0;
 | |
|     for (i=1; i<=len; i+=1) {
 | |
|         digit_value = string_pos(string_char_at(hex_string, i), hex_digits)-1;
 | |
|         if (digit_value >= 0) {
 | |
|             num *= 16;
 | |
|             num += digit_value;
 | |
|         } 
 | |
|         //Unknown character
 | |
|         else {
 | |
|             show_error("Invalid hex number: " + argument0, true);
 | |
|         }
 | |
|     }
 | |
|     return num;
 | |
| }
 | |
| 
 |