[Proposed Script] ds_list_contains()

The following script (and ancillary scripts) returns true or false depending on whether a value is present in the current ds_list.
Code:
/// @function {bool} ds_list_contains(id,expression,[search_"whole_variable"=default_or_"words"_incl_num_or_"chars"_incl_digits])
///    @description returns whether the ds_list contains a specific value or a value from an array (or the array itself if in "expression" mode. Returns Boolean.
//    Accepts real, int, string, or array. If you input a ds-type, script will only check id of ds-type due to drawback in GML.
//    Drawback: Cannot presently parse input ds-types and automatically to search their content--this presently only works with array indices. Until YYG get's its
//        act together an implements a sounder way to determing a ds-type, this drawback will remain. Sorry for this.
/// @param {real} id ds-list id
/// @param {expression} expression Input data
/// @param {string} [search_"expression"_or_"whole_variable"=default_or_"words"_incl_num_or_"chars"_incl_digits]
//    Enter "expression" to search a memory address of an array or the expression of a ds-type. If an array is input into ds_list as a variable reference,
//        entering the same array variable in the search will return true. Copying and pasting the array indices into the search will return false.
//    "whole_variable" will search entire input against input ds_list[|index] Example: if ds_list[|0] == "Some input" searching "Some input" returns true "some" returns false
//    "word" to search entire number or word ignoring spaces (spaces may be searched. if a space exists in the input string, a search for " " will return true)
//    "chars" to search digits or chars in input will also search up to 9 decimal places in a float Example: if ds_list[|0] == 1.23456789 searching "9" should return true
//---Assert Error messages---
var onErrorCurrProcess = "================!!AN ERROR OCCURRED IN ds_list_contains()!!==================\r\n\r\n";
#region---Get Data and Set Data---
//get ds_list
var currList = argument[0];
//check if is a ds_list
if (!ds_exists(currList,ds_type_list))
{
    show_error(onErrorCurrProcess+"VALUE ERROR:\r\n\""+string(currList)+"\" does not exist as a ds_list.\r\nHas the ds_list been created?",true);
    exit;
}
else if (ds_exists(currList,ds_type_list) && ds_list_empty(currList))
{
    show_debug_message(onErrorCurrProcess+"VALUE ERROR:\r\n\""+string(currList)+"\" does not contain any data!\r\nHave you called ds_list_contains() too soon?");
    return false;
}
//Get input data
var inputData = argument[1]
//get scope of how to search
//defualt is whole-variable
var useWhat = "whole_variable";
//Methodologies
//the input expression (best used to check for an array memory address)
var _expression = 0;
//the whole variable in an input (or an array index in its entirety)
//the search will include spaces and puncutation if string
var _whole_variable = 1;
//entire number entry or individual word (puncutation and non-printing chars are also searched but not as a part of a word)
//Note: "-" and "_" are included in word search
var _words = 2;
//individual chars
var _chars = 3;
//defaults to whole-variable
var willUse = _whole_variable;
//in case of float input and "chars" search used, get number of decimal places (9 is max that seems stable)
var _numDecimals = 9;
//create temporary ds_list which will then be used to compare currList ds_list values
var getDataAsList = ds_list_create();
//count number of times a nested array was seen (max value is 256)
var nestedArrayGovernor = ds_list_create();
//create a parsing map for individualized searching (esp for "words", "chars" search)
var parseOfCurrList = ds_map_create();
//get ending bool for convenience
var _returnTrue = false;
#region set scope of how to search if user has so selected. Default is "whole_variable"; Next, get parsed data for search comparison
if (argument_count > 2)
{
    //get what to use (defualt is "whole_variable")
    useWhat = string_lower(string(argument[2]));
    //check for errors
    //if input is unrecognized, show debug message and reset value to "whole_variable"
    if (useWhat != "expression" && useWhat != "whole_expression" && useWhat != "whole_variable" && useWhat != "whole_variables" && useWhat != "whole_var" && useWhat != "whole_vars" && useWhat != "whole" && useWhat != "variable" && useWhat != "words" && useWhat != "word" && useWhat != "chars" && useWhat != "char" && useWhat != "chrs" && useWhat != "chr")
    {
        show_debug_message(onErrorCurrProcess+"VALUE ERROR:\r\nargument[3]\""+useWhat+"\" unrecognized! Deferring to \"whole_variable\" search!"
            +"\r\nAcceptable inputs are:\r\n\"whole_expression\", and \"expression\"--for expressions like arrays and other ds-types added to indices in ds_list--"
            +"\r\n\"whole_variable\", \"whole_variables\", \"whole_var\", \"whole_vars\", \"whole\", and \"variable\"\r\n--this is the default search type--\r\nwill search entire number or the entire string entry in the input in its entirety (as a whole including spaces)."
            +"\r\nwords\", and \"word\"--will search entire number or each individual word in string input (ignoring spaces)--"
            +"\r\nand \"chars\", \"char\", \"chrs\", and \"chr\"--will search each digit or char in each entry\r\n(repeated chars will not be double-searched)--.");
        useWhat = "whole_variable";
        willUse = _whole_variable;
    }   
}
//if input recognized, useWhat...
//Expression
if (useWhat == "expression" || useWhat == "whole_expression")
{
    willUse = _expression;
}
//Whole variable
else if (useWhat == "whole_variable" || useWhat == "whole_variables" || useWhat == "whole_var" || useWhat == "whole_vars" || useWhat == "whole" || useWhat == "variable")
{
    willUse = _whole_variable;
    //parse current list
    //must check current type of entry
    for (var i = 0; i < ds_list_size(currList); ++i)
    {
        //Is a number
        if (is_real(currList[|i]) || is_int32(currList[|i]) || is_int64(currList[|i]))
        {
            if (is_real(currList[|i]))
            {
                ds_map_add(parseOfCurrList, real(currList[|i]), real(currList[|i]));
            }
            else if (is_int32(currList[|i]) || is_int64(currList[|i]))
            {
                ds_map_add(parseOfCurrList, int64(currList[|i]), int64(currList[|i]));
            }
        }
        //Is a string
        //get entire string including spaces
        else if (is_string(currList[|i]))
        {
            ds_map_add(parseOfCurrList, string(currList[|i]), string(currList[|i]));
        }
        //Is an array
        else if (is_array(currList[|i]))
        {
            ds_list_parse_array_to_map_whole_variable(parseOfCurrList,currList[|i],nestedArrayGovernor);
            //Clear array-depth limiter
            ds_list_clear(nestedArrayGovernor);
        }
        //Is a bool
        else if (is_bool(currList[|i]))
        {
            ds_map_add(parseOfCurrList, bool(currList[|i]), bool(currList[|i]));
        }
        //Is some other value (may cause undesired operation)
        else
        {
            ds_map_add(parseOfCurrList, currList[|i], currList[|i]);
        }
    }
}
//Words
else if (useWhat == "words" || useWhat == "word")
{
    willUse = _words;
    //parse current list
    //must check current type of entry
    for (var i = 0; i < ds_list_size(currList); ++i)
    {
        //Is a number
        if (is_real(currList[|i]) || is_int32(currList[|i]) || is_int64(currList[|i]))
        {
            if (is_real(currList[|i]))
            {
                ds_map_add(parseOfCurrList, real(currList[|i]), real(currList[|i]));
            }
            else if (is_int32(currList[|i]) || is_int64(currList[|i]))
            {
                ds_map_add(parseOfCurrList, int64(currList[|i]), int64(currList[|i]));
            }
        }
        //Is a string
        else if (is_string(currList[|i]))
        {
            var currWords = "";
            for (var j = 1; j <= string_length(currList[|i]); ++j)
            {
                currWords += string_lettersdigits_ext(string_char_at(currList[|i],j),["-","_"]);
                if (j >= string_length(currList[|i])
                || string_length(string_lettersdigits_ext(string_char_at(currList[|i],j),["-","_"])) == 0)
                {
                    if (currWords != "")
                    {
                        ds_map_add(parseOfCurrList, currWords, currWords);
                        currWords = "";
                    }
                           
                    if (currWords == "" && string_length(string_lettersdigits_ext(string_char_at(currList[|i],j),["-","_"])) == 0)
                    {
                        ds_map_add(parseOfCurrList, string_char_at(currList[|i],j), string_char_at(currList[|i],j));
                    }
                }
            }
        }
        //Is an array
        else if (is_array(currList[|i]))
        {
            ds_list_parse_array_to_map_words(parseOfCurrList,currList[|i],nestedArrayGovernor);
            //Clear array-depth limiter
            ds_list_clear(nestedArrayGovernor);
        }
        //Is a bool
        else if (is_bool(currList[|i]))
        {
            ds_map_add(parseOfCurrList, bool(currList[|i]), bool(currList[|i]));
        }
        //Is some other value (may cause undesired operation)
        else
        {
            var currChars = currList[|i];
            ds_map_add(parseOfCurrList, currChars,currChars);
        }
    }
}
//Chars
else if (useWhat == "chars" || useWhat == "char" || useWhat == "chrs" || useWhat == "chr")
{
    willUse = _chars;
    //parse current list
    //must check current type of entry
    for (var i = 0; i < ds_list_size(currList); ++i)
    {
        //Is a number
        if (is_real(currList[|i]) || is_int32(currList[|i]) || is_int64(currList[|i]))
        {
            var currChars = string_digits(string_trim(string_format(currList[|i],1,_numDecimals),"right","0"));
            for (var j = 1; j <= string_length(currChars); ++j)
            {
                if (is_real(currList[|i]))
                {
                    ds_map_add(parseOfCurrList, real(string_char_at(currChars,j)),real(string_char_at(currChars,j)));
                }
                else if (is_int32(currList[|i]) || is_int64(currList[|i]))
                {
                    ds_map_add(parseOfCurrList, is_int64(string_char_at(currChars,j)),is_int64(string_char_at(currChars,j)));
                }
            }
        }
        //Is a string
        else if (is_string(currList[|i]))
        {
            var currChars = string(currList[|i]);
            for (var j = 1; j <= string_length(currChars); ++j)
            {
                ds_map_add(parseOfCurrList, string_char_at(currChars,j),string_char_at(currChars,j));
            }
        }
        //Is an array
        else if (is_array(currList[|i]))
        {
            ds_list_parse_array_to_map_chars(parseOfCurrList,currList[|i],nestedArrayGovernor);
            //Clear array-depth limiter
            ds_list_clear(nestedArrayGovernor);
        }
        //Is a bool
        else if (is_bool(currList[|i]))
        {
            var currChars = string(currList[|i]);
            for (var j = 1; j <= string_length(currChars); ++j)
            {
                ds_map_add(parseOfCurrList, bool(string_char_at(currChars,j)),bool(string_char_at(currChars,j)));
            }
        }
        //Is some other value (may cause undesired operation)
        else
        {
            var currChars = currList[|i];
            ds_map_add(parseOfCurrList, currChars,currChars);
        }
    }
}
//An unknown error occurred, use default value
else
{
    willUse = _whole_variable
}
   
#endregion
//Error check: ensure willUse is usable as either _expression = 0, _whole_variable = 1, _words = 2, or _chars = 3
//If error, default to _words
while(willUse < _expression || willUse > _chars) {willUse = _whole_variable;}
#endregion
#region---Get type of inputData---
//--Is a real-number or ds-type--
if (is_real(inputData) || is_int32(inputData) || is_int64(inputData))
{
    #region Get num values
    //if not chars, search as entire number
    if (willUse != _chars)
    {
        //get real
        if (is_real(inputData))
        {
            ds_list_add(getDataAsList,real(inputData));
        }
        //get int
        else if (is_int32(inputData) || is_int64(inputData))
        {
            ds_list_add(getDataAsList,int64(inputData));
        }
    }
    //Search as individual chars
    else if (willUse = _chars)
    {
        //get individual digits (gets up to 9 decimal places)
        var getDigitsAsString = string_digits(string_trim(string_format(inputData,1,_numDecimals),"right","0"));
        for (var i = 1; i <= string_length(getDigitsAsString); ++i) {
            if (is_real(inputData))
            {
                ds_list_add(getDataAsList,real(string_char_at(getDigitsAsString,i)));
            }
            else if (is_int32(inputData) || is_int64(inputData))
            {
                ds_list_add(getDataAsList,is_int64(string_char_at(getDigitsAsString,i)));
            }
        }
    }
    #endregion
}
//--Is a string--
else if (is_string(inputData))
{
    #region Is a string
    //Search as expression or whole-variable
    if (willUse = _expression || willUse = _whole_variable)
    {
        ds_list_add(getDataAsList,inputData);
    }
    //Search as individual words
    else if (willUse == _words)
    {
        var currWords = "";
        for (var i = 1; i <= string_length(inputData); ++i)
        {
            //concatonate currWords iff is a letter, digit, or "-" or "_"
            currWords += string_lettersdigits_ext(string_char_at(inputData,i),["-","_"]);
            if (i >= string_length(inputData)
            || string_length(string_lettersdigits_ext(string_char_at(inputData,i),["-","_"])) == 0)
            {
                //add words to list if they consist of at least 1 letter or digit and clear currWords
                if (currWords != "")
                {
                    ds_list_add(getDataAsList,currWords);
                    currWords = "";
                }
           
                //get other chars if currWord empty and counter currently on an "other" char
                if (currWords == "" && string_length(string_lettersdigits_ext(string_char_at(inputData,i),["-","_"])) == 0)
                {
                    ds_list_add(getDataAsList,string_char_at(inputData,i));
                }
            }
        }
    }
    //Search as individual chars
    else if (willUse = _chars)
    {
        //get all individual chars
        var getCharsAsString = inputData;
        for (var i = 1; i <= string_length(getCharsAsString); ++i) {
            ds_list_add(getDataAsList,string_char_at(getCharsAsString,i));
        }
    }
    #endregion
}
//--Is an array--
else if (is_array(inputData))
{
    //get entire array
    ds_list_add(getDataAsList,inputData);
}
//--Is a bool--
else if (is_bool(inputData))
{
    #region Is a bool
    //Search as whole variable or word
    if (willUse != _chars)
    {
        //get entire bool (should be only 1 or 0)
        ds_list_add(getDataAsList,bool(inputData));
    }
    //Search as individual chars
    else if (willUse = _chars)
    {
        //get individual digits
        var getDigitsAsString = inputData;
        for (var i = 1; i <= string_length(getDigitsAsString); ++i) {
            ds_list_add(getDataAsList,bool(string_char_at(getDigitsAsString,i)));
        }
    }
    #endregion
}
//--Is some other type-- (may cause undesired operation)
else
{
    //get entire entry (entries here may be of unknown types and may cause errors)
    ds_list_add(getDataAsList,inputData);
}
#endregion
#region---Search Input Data---
//If searchable data is not empty...
if (!ds_list_empty(getDataAsList))
{
    #region---Expression mode---
    if (willUse == _expression)
    {
        //search each variable in obtained list
        for (var i = 0; i < ds_list_size(getDataAsList); ++i)
        {
            //if current variable is not an array...
            if !is_array(getDataAsList[|i])
            {
                //check entry against current list and return true if found
                var checkEntry = getDataAsList[|i];
                if (!is_undefined(currList[|ds_list_find_index(currList,checkEntry)])
                && typeof(currList[|ds_list_find_index(currList,checkEntry)]) == typeof(checkEntry))
                {
                    _returnTrue = true;
                }
            }
            //if is an array, enter array script and perform operations
            else
            {
                //if input array matches memory address, will return true
                if (ds_list_contains_array_check_expression(currList,getDataAsList[|i],nestedArrayGovernor) == true)
                {
                    _returnTrue = true;
                }
            }
        }
    }
    #endregion
    #region --Whole-variable or Word mode--
    else if (willUse == _whole_variable || willUse == _words)
    {
        //search each word in obtained list
        for (var i = 0; i < ds_list_size(getDataAsList); ++i)
        {
            //create a general variable
            var currListEntryAsWords;
            //Was a number
            if(is_real(getDataAsList[|i]) || is_int32(getDataAsList[|i]) || is_int64(getDataAsList[|i]))
            {
                //checks if was a real
                if (is_real(getDataAsList[|i]))
                {
                    var checkEntry = real(getDataAsList[|i]);
                    if(!is_undefined(parseOfCurrList[? checkEntry])
                    && typeof(parseOfCurrList[? checkEntry]) == typeof(checkEntry))
                    {
                        _returnTrue = true;
                    }
                }
                //checks if was an int
                else if (is_int32(getDataAsList[|i]) || is_int64(getDataAsList[|i]))
                {
                    var checkEntry = int64(getDataAsList[|i]);
                    if(!is_undefined(parseOfCurrList[? checkEntry])
                    && typeof(parseOfCurrList[? checkEntry]) == typeof(checkEntry))
                    {
                        _returnTrue = true;
                    }
                }
            }
            //Was a string
            else if(is_string(getDataAsList[|i]))
            {
                //get string at current indext and check whole words against current list
                currListEntryAsWords = string(getDataAsList[|i]);
                if(!is_undefined(parseOfCurrList[? currListEntryAsWords])
                && typeof(parseOfCurrList[? currListEntryAsWords]) == typeof(currListEntryAsWords))
                {
                    _returnTrue = true;
                }
           
            }
            //Was an array
            else if(is_array(getDataAsList[|i]))
            {
                //if was an array, enter special array script and check for word matches
                if (ds_list_contains_array_check_all_words(parseOfCurrList,getDataAsList[|i],nestedArrayGovernor) == true)
                {
                    _returnTrue = true;
                    //Clear array-depth limiter
                    ds_list_clear(nestedArrayGovernor);
                }
            }
            //Was a bool
            else if(is_bool(getDataAsList[|i]))
            {
                var checkEntry = bool(getDataAsList[|i]);
                if(!is_undefined(parseOfCurrList[? checkEntry])
                && typeof(parseOfCurrList[? checkEntry]) == typeof(checkEntry))
                {
                    _returnTrue = true;
                }
            }
            //Was some other type
            else
            {
                currListEntryAsWords = getDataAsList[|i];
                if(!is_undefined(parseOfCurrList[? currListEntryAsWords])
                && typeof(parseOfCurrList[? currListEntryAsWords]) == typeof(currListEntryAsWords))
                {
                    _returnTrue = true;
                }
            }
        }
    }
    #endregion
    #region --Chars mode--
    else if (willUse = _chars)
    {
        //search each char in obtained list
        for (var i = 0; i < ds_list_size(getDataAsList); ++i)
        {
            //create a general variable
            var currListEntryAsChars;
            //Was a number
            if (is_real(getDataAsList[|i]) || is_int32(getDataAsList[|i]) || is_int64(getDataAsList[|i]))
            {
                //get number as digits and parse
                currListEntryAsChars = string_digits(string_trim(string_format(getDataAsList[|i],1,_numDecimals),"right","0"));
                //parse
                for (var j = 1; j <= string_length(currListEntryAsChars); ++j)
                {
                    //If was a real, check as reals
                    if (is_real(getDataAsList[|i]))
                    {
                        var checkEntry = real(string_char_at(currListEntryAsChars,j));
                        if(!is_undefined(parseOfCurrList[? checkEntry])
                        && typeof(parseOfCurrList[? checkEntry]) == typeof(checkEntry))
                        {
                            _returnTrue = true;
                        }
                    }
                    //If was int32 or int64, check as respective types
                    else if (is_int32(getDataAsList[|i]) || int64(getDataAsList[|i]))
                    {
                        var checkEntry = int64(string_char_at(currListEntryAsChars,j));
                        if(!is_undefined(parseOfCurrList[? checkEntry])
                        && typeof(parseOfCurrList[? checkEntry]) == typeof(checkEntry))
                        {
                            _returnTrue = true;
                        }
                    }
                }
            }
            //Was a string
            else if (is_string(getDataAsList[|i]))
            {
                //Get string and parse
                currListEntryAsChars = string(getDataAsList[|i]);
                //parse
                for (var j = 1; j <= string_length(currListEntryAsChars); ++j)
                {
                    var checkEntry = string_char_at(currListEntryAsChars,j);
                    if(!is_undefined(parseOfCurrList[? checkEntry])
                    && typeof(parseOfCurrList[? checkEntry]) == typeof(checkEntry))
                    {
                        _returnTrue = true;
                    }
                }
            }
            //Was an array
            else if (is_array(getDataAsList[|i]))
            {
                //If was an array, enter special array script and check all chars
                if (ds_list_contains_array_check_all_chars(parseOfCurrList,getDataAsList[|i],nestedArrayGovernor) == true)
                {
                    _returnTrue = true;
                    //Clear array-depth limiter
                    ds_list_clear(nestedArrayGovernor);
                }
            }
            //Was a bool
            else if(is_bool(getDataAsList[|i]))
            {
                //Get as bool(s) (Should ever only be one input value)
                currListEntryAsChars = string(getDataAsList[|i]);
                //parse
                for (var j = 1; j <= string_length(currListEntryAsChars); ++j)
                {
                    var checkEntry = bool(string_char_at(currListEntryAsChars,j));
                    if(!is_undefined(parseOfCurrList[? checkEntry])
                    && typeof(parseOfCurrList[? checkEntry]) == typeof(checkEntry))
                    {
                        _returnTrue = true;
                    }
                }
            }
            //Was some other value
            else
            {
                currListEntryAsChars = getDataAsList[|i];
                if(!is_undefined(parseOfCurrList[? currListEntryAsChars])
                && typeof(parseOfCurrList[? currListEntryAsChars]) == typeof(currListEntryAsChars))
                {
                    _returnTrue = true;
                }
            }
        }
    }
    #endregion
}
//No data was input or none could be checked
else
{
    show_debug_message(onErrorCurrProcess+"VALUE ERROR:\r\nNo data to check!");
    _returnTrue = false;
}
#endregion
#region---Return---
if (_returnTrue)
{
    ds_list_destroy(getDataAsList);
    ds_list_destroy(nestedArrayGovernor);
    ds_map_destroy(parseOfCurrList);
    return true;
}
else
{
    ds_list_destroy(getDataAsList);
    ds_list_destroy(nestedArrayGovernor);
    ds_map_destroy(parseOfCurrList);
    return false;
}
#endregion

Ancillary scripts:

Code:
/// @function {bool} ds_list_contains_array_check_all_chars(current_map, current_array, limiter)
///    @description Checks all chars within nested array. Repeats if nested array seen.
/// @param {real} current_map ds-list id
/// @param {real} current_array array
/// @param {ds_list} limiter Limits number of nested array checks to 256
//---Assert Error messages---
var onErrorCurrProcess = "================!!AN ERROR OCCURRED IN ds_list_contains_array_check_all_chars()!!==================\r\n\r\n";
//Get data
var currMap = argument0;
var getDataAsArray = argument1;
var nestedArrayGovernor = argument2;
//in case of float input, get number of decimal places (9 is max that seems stable)
var _numDecimals = 9;
//check that arrays are not 256-deep
ds_list_add(nestedArrayGovernor,ds_list_size(nestedArrayGovernor)+1);
if (ds_list_size(nestedArrayGovernor) >= 256)
{
    show_debug_message(onErrorCurrProcess+"VALUE ERROR\r\nCould not find a matching value within nested arrays 256-deep!\r\nAre you sure the value exists in an array?\r\nEnding current process.");
    exit;
}
//iterate input array
for (var i = 0; i < array_length_1d(getDataAsArray); ++i)
{
    //create general variable
    var currArrEntriesAsChars;
    //Was a number
    if(is_real(getDataAsArray[i]) || is_int32(getDataAsArray[i]) || is_int64(getDataAsArray[i]))
    {
        //get numbers as digits
        currArrEntriesAsChars = string_digits(string_trim(string_format(getDataAsArray[i],1,_numDecimals),"right","0"));
        //parse each
        for (var j = 1; j <= string_length(currArrEntriesAsChars); ++j)
        {
            //was a real
            if (is_real(getDataAsArray[i]))
            {
                var checkEntry = real(string_char_at(currArrEntriesAsChars,j));
                if(!is_undefined(currMap[? checkEntry])
                && typeof(currMap[? checkEntry]) == typeof(checkEntry))
                {
                    return true;
                }
            }
            //was an int32 or int64 (previouly converted to int64--will check as int64)
            else if (is_int32(getDataAsArray[i]) || is_int64(getDataAsArray[i]))
            {
                var checkEntry = int64(string_char_at(currArrEntriesAsChars,j));
                if(!is_undefined(currMap[? checkEntry])
                && typeof(currMap[? checkEntry]) == typeof(checkEntry))
                {
                    return true;
                }
            }
        }
    }
    //Was a string
    else if(is_string(getDataAsArray[i]))
    {
        currArrEntriesAsChars = string(getDataAsArray[i]);
        for (var j = 1; j <= string_length(currArrEntriesAsChars); ++j)
        {
            var checkEntry = string_char_at(currArrEntriesAsChars,j);
            if(!is_undefined(currMap[? checkEntry])
            && typeof(currMap[? checkEntry]) == typeof(checkEntry))
            {
                return true;
            }
        }
    }
    //Was an array
    else if(is_array(getDataAsArray[i]))
    {
        //If nested array is seen, re-enter script and check values
        if (ds_list_contains_array_check_all_chars(currMap, getDataAsArray[i], nestedArrayGovernor) == true)
        {
            return true;
        }
    }
    //Was a bool
    else if(is_bool(getDataAsArray[i]))
    {
        currArrEntriesAsChars = string(getDataAsArray[i]);
        for (var j = 1; j <= string_length(currArrEntriesAsChars); ++j)
        {
            var checkEntry = bool(string_char_at(currArrEntriesAsChars,j));
            if(!is_undefined(currMap[? checkEntry])
            && typeof(currMap[? checkEntry]) == typeof(checkEntry))
            {
                return true;
            }
        }
    }
    //Was some other type of value
    else
    {
        currArrEntriesAsChars = getDataAsArray[i];
        if(!is_undefined(currMap[? currArrEntriesAsChars])
            && typeof(currMap[? currArrEntriesAsChars]) == typeof(currArrEntriesAsChars))
        {
            return true;
        }
    }
}
//remove last entry as moving out of current array
ds_list_delete(nestedArrayGovernor,ds_list_size(nestedArrayGovernor)-1);
//Exit script
exit;
Code:
/// @function {bool} ds_list_parse_array_to_map_chars(current_map, current_array, limiter)
///    @description Checks all chars within nested array. Repeats if nested array seen.
/// @param {real} current_map ds-map id
/// @param {real} current_array array
/// @param {ds_list} limiter Limits number of nested array checks to 256
//---Assert Error messages---
var onErrorCurrProcess = "================!!AN ERROR OCCURRED IN ds_list_parse_array_to_map_chars()!!==================\r\n\r\n";
//Get data
var currMap = argument0;
var getDataAsArray = argument1;
var nestedArrayGovernor = argument2;
//in case of float input, get number of decimal places (9 is max that seems stable)
var _numDecimals = 9;
//check that not 256-deep into nested arrays
ds_list_add(nestedArrayGovernor,ds_list_size(nestedArrayGovernor)+1);
if (ds_list_size(nestedArrayGovernor) >= 256)
{
    show_debug_message(onErrorCurrProcess+"VALUE ERROR\r\nCould not find a matching value within nested arrays 256-deep!\r\nAre you sure the value exists in an array?\r\nEnding current process.");
    exit;
}
//iterate input array
for (var i = 0; i < array_length_1d(getDataAsArray); ++i)
{
    //Is a number
    if (is_real(getDataAsArray[i]) || is_int32(getDataAsArray[i]) || is_int64(getDataAsArray[i]))
    {
        //get number as digits (up to _numDecimals)
        var currChars = string_digits(string_trim(string_format(getDataAsArray[i],1,_numDecimals),"right","0"));
        for (var j = 1; j <= string_length(currChars); ++j)
        {
            //Is a real
            if (is_real(getDataAsArray[i]))
            {
                ds_map_add(currMap, real(string_char_at(currChars,j)),real(string_char_at(currChars,j)));
            }
            //Is an int32 or int64
            else if (is_int32(getDataAsArray[i]) || is_int64(getDataAsArray[i]))
            {
                ds_map_add(currMap, is_int64(string_char_at(currChars,j)),is_int64(string_char_at(currChars,j)));
            }
        }
    }
    //Is a string
    else if (is_string(getDataAsArray[i]))
    {
        //check each char
        var currChars = string(getDataAsArray[i]);
        for (var j = 1; j <= string_length(currChars); ++j)
        {
            ds_map_add(currMap, string_char_at(currChars,j),string_char_at(currChars,j));
        }
    }
    //Is a nested array
    else if (is_array(getDataAsArray[i]))
    {
        ds_list_parse_array_to_map_chars(currMap,getDataAsArray[i],nestedArrayGovernor);
    }
    //Is a bool
    else if (is_bool(getDataAsArray[i]))
    {
        var currChars = string(getDataAsArray[i]);
        for (var j = 1; j <= string_length(currChars); ++j)
        {
            ds_map_add(currMap, bool(string_char_at(currChars,j)),bool(string_char_at(currChars,j)));
        }
    }
    //Is some other value type
    else
    {
        var currChars = getDataAsArray[i];
        ds_map_add(currMap, currChars,currChars);
    }
}
//remove last entry as moving out of current array
ds_list_delete(nestedArrayGovernor,ds_list_size(nestedArrayGovernor)-1);
//Exit script
exit;
Code:
/// @function {bool} ds_list_parse_array_to_map_words(current_map, current_array, limiter)
///    @description Checks all chars within nested array. Repeats if nested array seen.
/// @param {real} current_map ds-map id
/// @param {real} current_array array
/// @param {ds_list} limiter Limits number of nested array checks to 256
//---Assert Error messages---
var onErrorCurrProcess = "================!!AN ERROR OCCURRED IN ds_list_parse_array_to_map_words()!!==================\r\n\r\n";
//Get data
var currMap = argument0;
var getDataAsArray = argument1;
var nestedArrayGovernor = argument2;
//check that not 256-deep into nested arrays
ds_list_add(nestedArrayGovernor,ds_list_size(nestedArrayGovernor)+1);
if (ds_list_size(nestedArrayGovernor) >= 256)
{
    show_debug_message(onErrorCurrProcess+"VALUE ERROR\r\nCould not find a matching value within nested arrays 256-deep!\r\nAre you sure the value exists in an array?\r\nEnding current process.");
    exit;
}
//iterate input array
for (var i = 0; i < array_length_1d(getDataAsArray); ++i)
{
    //Is a number
    if (is_real(getDataAsArray[i]) || is_int32(getDataAsArray[i]) || is_int64(getDataAsArray[i]))
    {
        //Is a real
        if (is_real(getDataAsArray[i]))
        {
            ds_map_add(currMap, real(getDataAsArray[i]), real(getDataAsArray[i]));
        }
        //Is an int32 or int64
        else if (is_int32(getDataAsArray[i]) || is_int64(getDataAsArray[i]))
        {
            ds_map_add(currMap, int64(getDataAsArray[i]), int64(getDataAsArray[i]));
        }
    }
    //Is a string
    else if (is_string(getDataAsArray[i]))
    {
        //get current words
        var currWords = "";
        //iterate index
        for (var j = 1; j <= string_length(getDataAsArray[i]); ++j)
        {
            //concatenate current word
            currWords += string_lettersdigits_ext(string_char_at(getDataAsArray[i],j),["-","_"]);
            //check if end of word reached or if "other" char is seen
            if (j >= string_length(getDataAsArray[i])
            || string_length(string_lettersdigits_ext(string_char_at(getDataAsArray[i],j),["-","_"])) == 0)
            {
                //if a word has been parsed, add to map and clear current word
                if (currWords != "")
                {
                    ds_map_add(currMap, currWords, currWords);
                    currWords = "";
                }
           
                //If "other" char was seen, get
                if (currWords == "" && string_length(string_lettersdigits_ext(string_char_at(getDataAsArray[i],j),["-","_"])) == 0)
                {
                    ds_map_add(currMap, string_char_at(getDataAsArray[i],j), string_char_at(getDataAsArray[i],j));
                }
            }
        }
    }
    //Is a nested array
    else if (is_array(getDataAsArray[i]))
    {
        ds_list_parse_array_to_map_words(currMap,getDataAsArray[i],nestedArrayGovernor);
    }
    //Is a bool
    else if (is_bool(getDataAsArray[i]))
    {
        ds_map_add(currMap, bool(getDataAsArray[i]), bool(getDataAsArray[i]));
    }
    //Is another value type
    else
    {
        var currChars = getDataAsArray[i];
        ds_map_add(currMap, currChars,currChars);
    }
}
//remove last entry as moving out of current array
ds_list_delete(nestedArrayGovernor,ds_list_size(nestedArrayGovernor)-1);
//exit script
exit;
Code:
/// @function {bool} ds_list_parse_array_to_map_whole_variable(current_map, current_array, limiter)
///    @description Checks all chars within nested array. Repeats if nested array seen.
/// @param {real} current_map ds-map id
/// @param {real} current_array array
/// @param {ds_list} limiter Limits number of nested array checks to 256
//---Assert Error messages---
var onErrorCurrProcess = "================!!AN ERROR OCCURRED IN ds_list_parse_array_to_map_whole_variable()!!==================\r\n\r\n";
//Get data
var currMap = argument0;
var getDataAsArray = argument1;
var nestedArrayGovernor = argument2;
//check that not 256-deep into nested arrays
ds_list_add(nestedArrayGovernor,ds_list_size(nestedArrayGovernor)+1);
if (ds_list_size(nestedArrayGovernor) >= 256)
{
    show_debug_message(onErrorCurrProcess+"VALUE ERROR\r\nCould not find a matching value within nested arrays 256-deep!\r\nAre you sure the value exists in an array?\r\nEnding current process.");
    exit;
}
//iterate input array
for (var i = 0; i < array_length_1d(getDataAsArray); ++i)
{
    //Is a number
    if (is_real(getDataAsArray[i]) || is_int32(getDataAsArray[i]) || is_int64(getDataAsArray[i]))
    {
        if (is_real(getDataAsArray[i]))
        {
            ds_map_add(currMap, real(getDataAsArray[i]), real(getDataAsArray[i]));
        }
        else if (is_int32(getDataAsArray[i]) || is_int64(getDataAsArray[i]))
        {
            ds_map_add(currMap, int64(getDataAsArray[i]), int64(getDataAsArray[i]));
        }
    }
    //Is a string
    //get entire string including spaces
    else if (is_string(getDataAsArray[i]))
    {
        ds_map_add(currMap, string(getDataAsArray[i]), string(getDataAsArray[i]));
    }
    //Is an array
    else if (is_array(getDataAsArray[i]))
    {
        ds_list_parse_array_to_map_whole_variable(currMap,getDataAsArray[i],nestedArrayGovernor);
        //Clear array-depth limiter
        ds_list_clear(nestedArrayGovernor);
    }
    //Is a bool
    else if (is_bool(getDataAsArray[i]))
    {
        ds_map_add(currMap, bool(getDataAsArray[i]), bool(getDataAsArray[i]));
    }
    //Is some other value (may cause undesired operation)
    else
    {
        ds_map_add(currMap, getDataAsArray[i], getDataAsArray[i]);
    }
}
//remove last entry as moving out of current array
ds_list_delete(nestedArrayGovernor,ds_list_size(nestedArrayGovernor)-1);
//exit script
exit;
Code:
/// @function {bool} ds_list_contains_array_check_expression(current_list, current_array, limiter)
///    @description Checks all within nested array. Repeats if nested array seen.
/// @param {real} current_list ds-list id
/// @param {real} current_array array
/// @param {ds_list} limiter Limits number of nested array checks to 256
//---Assert Error messages---
var onErrorCurrProcess = "================!!AN ERROR OCCURRED IN ds_list_contains_array_check_expression()!!==================\r\n\r\n";
//get arguments
var currList = argument0;
var getDataAsArray = argument1;
var nestedArrayGovernor = argument2;
//check that not 256-deep into nested arrays
ds_list_add(nestedArrayGovernor,ds_list_size(nestedArrayGovernor)+1);
if (ds_list_size(nestedArrayGovernor) >= 256)
{
    show_debug_message(onErrorCurrProcess+"VALUE ERROR\r\nCould not find a matching value within nested arrays 256-deep!\r\nAre you sure the value exists in an array?\r\nEnding current process.");
    exit;
}
//check that array matches value in memory. If so, will return true.
if is_array(getDataAsArray)
{
    var checkEntry = getDataAsArray;
    if (!is_undefined(currList[|ds_list_find_index(currList,checkEntry)])
    && typeof(currList[|ds_list_find_index(currList,checkEntry)]) == typeof(checkEntry))
    {
        return true;
    }
}
//remove last entry as moving out of current array
ds_list_delete(nestedArrayGovernor,ds_list_size(nestedArrayGovernor)-1);
//Exit script if no match found
exit;
Code:
/// @function {bool} ds_list_contains_array_check_all_words(current_map, current_array, limiter)
///    @description Checks all chars within nested array. Repeats if nested array seen.
/// @param {real} current_map ds-list id
/// @param {real} current_array array
/// @param {ds_list} limiter Limits number of nested array checks to 256
//---Assert Error messages---
var onErrorCurrProcess = "================!!AN ERROR OCCURRED IN ds_list_contains_array_check_all_words()!!==================\r\n\r\n";
//Get data
var currMap = argument0;
var getDataAsArray = argument1;
var nestedArrayGovernor = argument2;
//check that arrays are not 256-deep
ds_list_add(nestedArrayGovernor,ds_list_size(nestedArrayGovernor)+1);
if (ds_list_size(nestedArrayGovernor) >= 256)
{
    show_debug_message(onErrorCurrProcess+"VALUE ERROR\r\nCould not find a matching value within nested arrays 256-deep!\r\nAre you sure the value exists in an array?\r\nEnding current process.");
    exit;
}
//iterate input array
for (var i = 0; i < array_length_1d(getDataAsArray); ++i)
{
    //create general variable
    var currArrEntriesAsWords;
    //Was a number
    if(is_real(getDataAsArray[i]) || is_int32(getDataAsArray[i]) || is_int64(getDataAsArray[i]))
    {
        //Was real
        if (is_real(getDataAsArray[i]))
        {
            if(!is_undefined(currMap[? getDataAsArray[i]])
            && typeof(currMap[? getDataAsArray[i]]) == typeof(getDataAsArray[i]))
            {
                return true;
            }
        }
        //was an int32 or int64 (previouly converted to int64--will check as int64)
        else if (is_int32(getDataAsArray[i]) || is_int64(getDataAsArray[i]))
        {
            if(!is_undefined(currMap[? getDataAsArray[i]])
            && typeof(currMap[? getDataAsArray[i]]) == typeof(getDataAsArray[i]))
            {
                return true;
            }
        }
    }
    //Was a string
    else if(is_string(getDataAsArray[i]))
    {
        currArrEntriesAsWords = string(getDataAsArray[i]);
        if(!is_undefined(currMap[? currArrEntriesAsWords])
            && typeof(currMap[? currArrEntriesAsWords]) == typeof(currArrEntriesAsWords))
        {
            return true;
        }
    }
    //Was an array
    else if(is_array(getDataAsArray[i]))
    {
        //If nested array is seen, re-enter script and check values
        if (ds_list_contains_array_check_all_words(currMap, getDataAsArray[i], nestedArrayGovernor) == true)
        {
            return true;
        }
    }
    //Was a bool
    else if(is_bool(getDataAsArray[i]))
    {
        if(!is_undefined(currMap[? getDataAsArray[i]])
        && typeof(currMap[? getDataAsArray[i]]) == typeof(getDataAsArray[i]))
        {
            return true;
        }
    }
    //Was some other type of value
    else
    {
        currArrEntriesAsWords = getDataAsArray[i];
        if(!is_undefined(currMap[? currArrEntriesAsWords])
            && typeof(currMap[? currArrEntriesAsWords]) == typeof(currArrEntriesAsWords))
        {
            return true;
        }
    }
}
//remove last entry as moving out of current array
ds_list_delete(nestedArrayGovernor,ds_list_size(nestedArrayGovernor)-1);
//Exit script
exit;

Other supporting scripts:
string_lettersdigits_ext() which can be found here
and string_trim()

Examples of what might be returned:
Code:
    //Some random code situations:
    var someMap = ds_map_create();
    ds_map_add(someMap, "threesCompany","Come and knock on our door");
    var someNum = 170.2082878885364
    var testArr = [0,15,"Dog",["The ","Mask","of","Zorro"],"Cat","living", "together","mass hysteria!",someNum,bool(true)];
    var arrOfPossibleDelimiterChars = ["$","(",")","+",":","=",">","@","[","]","^","~"];
    var test_ds_list = ds_list_create();
 
    ds_list_add(test_ds_list,someMap);
    ds_list_add(test_ds_list,"a:active");
    ds_list_add(test_ds_list,testArr);
 
    //Examples of finding a non-alphanumeric in an input string (Here, ":" would be used from the array of possible delimiters:
    ds_list_contains(test_ds_list,"a:active","expression"); // true because it's one of the expressions in the list
     ds_list_contains(test_ds_list,"a:active","whole_variable"); // true because it's one of the whole_variables in the list
     ds_list_contains(test_ds_list,arrOfErroneousDelimiters,"expression"); // false
     ds_list_contains(test_ds_list,arrOfErroneousDelimiters,"whole_variable"); /*returns false because "a:active" IS the whole variable and no instances in the input array or other indices in the list contain as a singular character value any of the indices in arrOfPossibleDelimiterChars */
    ds_list_contains(test_ds_list,arrOfErroneousDelimiters,"word"); /*returns true because ":"--a non-aphanumeric--is part of the entry and is parsed as a separate "word" when word mode is enabled*/
    ds_list_contains(test_ds_list,arrOfErroneousDelimiters,"char"); /*returns true because ":" is most certainly a char that exists in one of the indices in the list*/
//Searching an input ds_map
//sorry, no dice. I tried and failed to make this work correctly. ds_exists just doesn't quite fit the bill
//You end up with too many false-positives
//hence:
    ds_list_contains(test_ds_list,"Come and knock on our door","whole_variable"); //Returns false... BUT
    ds_list_contains(test_ds_list,someMap,"whole_variable"); //Returns true   BUT
    ds_list_contains(test_ds_list,/*The real number id of someMap*/,"whole_variable"); //Returns true!!! ARAGH!!! Quite a dilemma!
//Searching an array
//The array itself
     ds_list_contains(test_ds_list,testArr,"expression"); //Returns true (as would "whole_variable", "words", and "chars"
//But what if you just copied and pasted the array directly into the search?
     ds_list_contains(test_ds_list,[0,15,"Dog",["The ","Mask","of","Zorro"],"Cat","living", "together","mass hysteria!",someNum,bool(true)],"expression"); //Returns false because the memory address of the array being compared DOES NOT match the memory address of testArr
//How about the Indicies in the input array?
    ds_list_contains(test_ds_list,"mass hysteria!","whole_variable"); // true
     ds_list_contains(test_ds_list,"mass hysteria!","word"); // true because "mass" exists as a word in the array and is therefore already a part of the ds_list
     ds_list_contains(test_ds_list,"mass hysteria!","chars") // true because "m" exists in the list
//What about a [space] character (chr(32))?
    ds_list_contains(test_ds_list," ","whole_variable") // false
    ds_list_contains(test_ds_list," ","word") // true because spaces are parsed as individual "words"
    ds_list_contains(test_ds_list," ","chars") // true likewise
//What about a value deep in a nested array?
//No problem!
     ds_list_contains(test_ds_list,"Zorro","whole_variable")// true
     ds_list_contains(test_ds_list,"zorro","whole_variable")// false
     ds_list_contains(test_ds_list,"Zorro","word")// true
     ds_list_contains(test_ds_list,"zorro","word")// false
//Chars?
     ds_list_contains(test_ds_list,"Zorro","chars")// true
     //Careful here!
     ds_list_contains(test_ds_list,"zorro","chars")// TRUE!! because "o" most certainly IS in there
     ///But...
     ds_list_contains(test_ds_list,"z","chars")// false because there is no singular lower-case "z" in the input array
//Numbers?
     ds_list_contains(test_ds_list,someNum,"expression")// false (it's an index in the input array, not a value placed directly into the list)
     ds_list_contains(test_ds_list,someNum,"whole_variable")// true
     ds_list_contains(test_ds_list,someNum,"word")// true
     ds_list_contains(test_ds_list,someNum,"char")// true
//What if we wanted to see if 9 was in someNum? Be careful here!
     ds_list_contains(test_ds_list,9,"chars")// true because 173.2082878885364 "5" is in the 10th decimal place and therefore rounds 8 to 9
//let's say sumNum was this 170.123456789
      ds_list_contains(test_ds_list,6,"chars")// true because 6 is in there. Careful! If "6" was anywhere else in the list, you would get a false positive!
I spent a fair amount of time getting this to work. Again, I would appreciate any suggestions.
Thank you for all that you do.
 

Attachments

Last edited:
If you had a ds_list that contained an array as a member, ds_list_find_value(list, ds_list_find_index(list, someArray)) would yield the entire array, not an index in the array.

BUT:
if you had an array that was input into the ds_list:
var someArray = ["someString", . . . ];
ds_list_add(list,someArray);

And then later wanted to check a value in the list that might have been a member of the input array:
ds_list_contains(list, "someString", "whole_value") would yield true because "someString" was in the input array which is a member of list.

let's say you had certain characters you needed to check for in a list of strings being added to the list.
Well, you would create your array:
var arrayOfChars = ["!","@","#","$","%","^","&","*","(",")","-","_","+"];


Then you would add members to the list
ds_list_add(list,"Merry","had","a","little","lamb","who had an AK-47!");

Let's say you wanted to know if this list contained a specific word or char
Well, you could use the traditional method: ds_list_find_value(list, ds_list_find_index(list, "lamb")) and that would certainly work for an index. But what if you knew there was a specific word in there that the traditional method wouldn't uncover? Say, finding the word "an". You would have to iterate through the entire list until you found the index containing "who had an AK-47!" and then use string_length("an") > 0. That would work but it might get cumbersome.

ds_list_contains() allows you to do the following:
ds_list_contains(list,"an","word"); returns true
or
ds_list_contains(list,arrayOfChars,"chars"); This also returns true because "who had an AK-47!" contains "-" and "!" which are members of the array being used to search the list.

Does that answer your question?

Not sure of the operational complexity. Should be efficient for most implementations.
 

Yal

🐧 *penguin noises*
GMC Elder
But what if you knew there was a specific word in there that the traditional method wouldn't uncover? Say, finding the word "an". You would have to iterate through the entire list until you found the index containing "who had an AK-47!" and then use string_length("an") > 0. That would work but it might get cumbersome.
Or, if you know you're searching for a sequential number of values, you check the values just before and after the returned value first (say, if the ds_list_find() search returns 163, you check all of 153-173 for the neighbors and abandon that result if there's no match in any of those).

Also, there's a function for searching a string for substrings of arbitrary length, which seems more suitable for your example than putting a string piecemeal into a ds_list...

Arrays also are first-order citizens now, so if you handle data that are grouped together a lot, you could put small arrays with X values in them into arrays and get the entire grouped array at once, rather than trying to find and extract the group from the array after it has been split up.
 
Or, if you know you're searching for a sequential number of values, you check the values just before and after the returned value first (say, if the ds_list_find() search returns 163, you check all of 153-173 for the neighbors and abandon that result if there's no match in any of those).

Also, there's a function for searching a string for substrings of arbitrary length, which seems more suitable for your example than putting a string piecemeal into a ds_list...

Arrays also are first-order citizens now, so if you handle data that are grouped together a lot, you could put small arrays with X values in them into arrays and get the entire grouped array at once, rather than trying to find and extract the group from the array after it has been split up.
That was just a layman's example. Of course there are many string functions. But, what if you were parsing a CSS file to JSON (which is what I'm presently knee-deep in), you would want a way of knowing if there are delimiters used in the CSS file which may be beyond the scope of your current project and you would want to inform the user of this fact.

For example: the CSS selector a[href^="https"] is beyond the scope of my current project which basically will allow the user to designate certain spans in a verbatim string to have XML-like markup. It might be nice to know whether there are any unusable delimiters in the ds_list which is having members added during the parse, such as "[". Hence, if I could quickly pass in an array containing the delims I don't want to use (or frankly can't use), I could throw an error letting the user know that that CSS selector cannot be used.

Example:

.ClassName
{
color: blue;
font-weight:bold;
}

This would parse "ClassName" into the ds_list as a class-level selector which is then marked as a map later on. What you ultimately end up with is {".ClassName":{color:blue;font-weight:bold;}} (or something to that effect).

But, if the user had already created a website and wanted to see if he or she could recreate the website using the program I am creating, I need a way of informing the user that there are certian CSS selectors which cannot be used.

Like:

div + p
{
color: blue;
font-weight:bold;
}

I'll want a way to quickly ascertain whether that ds_list contains one of the unusable delimiters, "+" for example.

Does that make sense?
 

Yal

🐧 *penguin noises*
GMC Elder
Whenever someone mentions JSON, I always instantly stop listening :p

Anyway, if you want to parse a hierarchical structure like XML / JSON, wouldn't it make more sense to use a stack to keep track of delimiters than a ds_list? Whenever there's a delimiter, push any previous unfinished tokens on the current hierarchical level to the stack, then gradually build a tree (note: not necessarily a binary tree - there's no limit to how many sub-nodes the nodes of a tree based on JSON or XML may have) with the finished data whenever you're ready to pop a node from the stack (i.e., when you find a matching closing delimiter to the one behind the topmost stack-push). Keeps all tokens belonging to one hierarchical level together.
 
Whenever someone mentions JSON, I always instantly stop listening :p

Anyway, if you want to parse a hierarchical structure like XML / JSON, wouldn't it make more sense to use a stack to keep track of delimiters than a ds_list? Whenever there's a delimiter, push any previous unfinished tokens on the current hierarchical level to the stack, then gradually build a tree (note: not necessarily a binary tree - there's no limit to how many sub-nodes the nodes of a tree based on JSON or XML may have) with the finished data whenever you're ready to pop a node from the stack (i.e., when you find a matching closing delimiter to the one behind the topmost stack-push). Keeps all tokens belonging to one hierarchical level together.
Not in the parsing process, sadly. When it's eventually called to modify drawn text, yes. CSS has a lot of weird exceptions and putting it in a stack to build the JSON string would be less-effective for this step. I thought about doing it that way many times, though.
 
Top