So here goes:
CODE
// Generates a random string of length len
// from the characters passed to it.
string randomStr(string chars, integer len) {
integer numChars = llStringLength(chars);
string ret;
integer i;
for (i = 0; i < len; i++) {
integer randIndex = llFloor(llFrand(numChars));
ret += llGetSubString(chars, randIndex, randIndex);
}
return ret;
}
string SEPERATOR_CHARS = "`~!@#$%^&*()-_+[]{}\|'\";/?.>,<";
integer SEPERATOR_LEN = 3;
string dumpList2String(list src) {
// Generate a seperator not present in any of the
// elements in the list.
string chars = (string) src; // Squashes all elements together.
string seperator;
do {
seperator = randomStr(SEPERATOR_CHARS, SEPERATOR_LEN);
} while (llSubStringIndex(chars, seperator) != -1);
return seperator + llDumpList2String(src, seperator);
}
list parseStringKeepNulls(string src) {
// The seperator should be the first SEPERATOR_LEN
// characters in the string.
return llParseStringKeepNulls(llDeleteSubString(src, 0, SEPERATOR_LEN - 1),
[llGetSubString(src, 0, SEPERATOR_LEN - 1)], []);
}
string NULL = "";
list listReplaceList(list dest, list src, integer start, integer ensureLocation) {
if (ensureLocation && llGetListEntryType(dest, start - 1) == TYPE_INVALID) {
// Fill with nulls until we get to the starting position.
integer len = llGetListLength(dest);
for (len = llGetListLength(dest); len < start; ++len) {
dest += NULL;
}
}
return llListReplaceList(dest, src, start, start + llGetListLength(src) - 1);
}
list getElementAt(list array, list index){
if (index == [])
return array;
integer numIndicies = llGetListLength(index);
list src = array;
integer i;
for (i = 0; i < numIndicies - 1; ++i) {
integer listIndex = llList2Integer(index, i);
string element = llList2String(src, listIndex);
if (llGetSubString(element, 0, 0) == "l")
element = llDeleteSubString(element, 0, 0);
src = parseStringKeepNulls(element);
}
string element = llList2String(src, llList2Integer(index, -1));
if (llGetSubString(element, 0, 0) == "l") {
// Caller is retreiving a dimension.
return parseStringKeepNulls(llDeleteSubString(element, 0, 0));
} else {
return [element];
}
}
// To set an element, we need to extract
// each list starting from where exactly the
// new data will be. In a set of russian dolls, this
// would be equivelant to grabbing the littlest one
// first. then gradually adding back on the layers over it.
// I think its easiest to do this recursively.
list setElementAt(list array, list data, list index) {
string element;
if (llGetListLength(data) > 1) {
// Data is a new dimension:
element = "l" + dumpList2String(data);
} else {
// Data is a single element
element = llList2String(data, 0);
}
if (llGetListLength(index) > 1) {
// index is in the form [a,b,c,d]
// here, we grab the list that d is in.
list containerIndex = llDeleteSubList(index, -1, -1);
list dest = getElementAt(array, containerIndex);
integer listIndex = llList2Integer(index, -1); // Grab d
dest = listReplaceList(dest, [element], listIndex, TRUE); // replace the element in d's list.
// Make sure the recursion treats the container like a list, not an element.
if (llGetListLength(dest) == 1)
dest += "";
return setElementAt(array, dest, containerIndex);
} else {
return listReplaceList(array, [element], llList2Integer(index, 0), TRUE);
}
}
// And a sample usage:
default {
state_entry() {
llOwnerSay("Compiled.");
list array;
array = setElementAt(array, ["fool", "barn"], [0, 0]);
array = setElementAt(array, ["ACK", "FARN!", "MER!"], [0, 0, 1]);
array = setElementAt(array, ["blah"], [0, 1]);
array = setElementAt(array, ["garage", "flack"], [1]);
array = setElementAt(array, [], [0, 0, 1]);
llOwnerSay("array == [" + llList2CSV(array) + "]");
llOwnerSay("[" + llList2CSV(getElementAt(array, [1, 0])) + "]");
}
}
Features:
1. Dynamic arrays: No need to specify dimension length!
2. "Ragged" arrays: Its truly lists in lists - any size list can be put into any other list.
Testing welcome

... and before you say "didn't you do this already?" -- eh, nevermind

==Chris