mirror of
https://github.com/KevinMidboe/linguist.git
synced 2025-10-29 09:40:21 +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;
|
|
}
|
|
|