-- LIST HANDLERS -- -- -- © 1998 - 2005, OpenSpark Interactive Ltd -- -- ---------------------------------------------------------------------- -- 050824 JN: Added ConvertToIndexedList(), AddToSubList() and -- DeleteFromSubList ---------------------------------------------------------------------- on ListToString(aList) ----------------------------------------------- -- INPUT: must be a linear or property list -- OUTPUT: a string where each item of appears on a separate -- line -- NOTE: Contains no error checking -------------------------------------------------------------------- aString = "" tCount = aList.count() repeat with i = 1 to tCount put RETURN & aList[i] after aString end repeat delete aString.char[1] -- initial RETURN char return aString end ListToString on listToString2(aList, delimiter) if not listP(aList) then return #invalidList end if if not stringP(delimiter) then delimiter = "" end if delimiterLength = delimiter.char.count aString = "" i = aList.count() repeat while i aString = delimiter & aList[i] & aString i = i - 1 end repeat if delimiterLength then delete aString.char[1..delimiterLength] end if return aString end listToString on nestedListToString(nestedList, level) ----------------------------- -- Converts nestedList to a string, using the TAB character to -- indicate the hierarchical level of each element and the RETURN -- character to distinguish betweeen elements. -------------------------------------------------------------------- nestedString = "" if ilk(nestedList) <> #propList then -- There is no nested list to treat return nestedString end if tabs = "" i = level repeat while i tabs = tabs & TAB i = i - 1 end repeat i = nestedList.count() repeat while i theProp = nestedList.getPropAt(i) theValue = nestedList[i] if ilk(theValue) = #propList then -- Use recursion to list the contents of the sub-list subLevel = level + 1 theValue = nestedListToString(theValue, subLevel) nestedString = RETURN & theValue & nestedString end if nestedString = RETURN & tabs & theProp & nestedString i = i - 1 end repeat delete nestedString.char[1] -- Initial return char return nestedString end nestedListToString on StringToList(aString, aDelimiter) --------------------------------- -- INPUT: should be a string -- should be a string, if not RETURN is used by -- default. -- OUTPUT: Converts a string to a linear list, separating items at -- -------------------------------------------------------------------- tList = [] if not stringP(aString) then return tList else if aString = "" then return tList else if not stringP(aDelimiter) then aDelimiter = RETURN end if tDelimiter = the itemDelimiter the itemDelimiter = aDelimiter tCount = aString.item.count repeat with i = 1 to tCount tElement = aString.item[i] -- Remove any preceding and trailing spaces repeat while tElement.char[1] = " " delete tElement.char[1] end repeat repeat while the last char of tElement = " " delete the last char of tElement end repeat tList.append(tElement) end repeat the itemDelimiter = tDelimiter return tList end StringToList on ConvertToIndexedList(aListOfLists, aProp) ------------------------- -- INPUT: will be a linear list of property lists, -- with the format: -- [[: -- #propertyNameToIndex: , -- : ], ...] -- will be the symbol property name to use as an -- index -- ACTION: Creates a sorted property list with the format: -- [ [: , -- #propertyNameToIndex: , -- : ], ...] -- OUTPUT: The new property list. -------------------------------------------------------------------- tList = [:] tList.sort() if listP(aListOfLists) then i = aListOfLists.count repeat while i tSubList = aListOfLists[i] if ilk(tSubList, #propList) then tIndex = tSubList[aProp] tList.addProp(tIndex, tSubList) end if i = i - 1 end repeat end if return tList end ConvertToIndexedList on AddToSubList(aPropList, aProp, aValue) ---------------------------- -- INPUT: should be a property list whose values are -- linear lists -- can be any Lingo value except -- can be any Lingo value -- ACTION: Adds aValue to the aProp sublist of aPropList -- SAMPLE: vList = [#a: [1, 2]] -- AddToSubList(vList, #a, 3) -- put vList -- -- [#a: [1, 2, 3]] -- AddToSubList(vList, #b, 4) -- put vList -- -- [#a: [1, 2, 3], #b: [4]] -------------------------------------------------------------------- case ilk(aPropList) of #propList: if voidP(aProp) then return #propertyExpected end if vSubList = aPropList.getaProp(aProp) if ilk(vSubList) <> #list then vSubList = [] aPropList.addProp(aProp, vSubList) end if vSubList.append(aValue) otherwise: return #listExpected end case return 0 -- no error end AddToSubList on AddToKeyList(aList, aKey, aProp, aValue, aSorted) ----------------- -- INPUT: should be a list whose values are themselves lists -- Three different cases are possible: -- 1. a linear list of property lists -- 2. a property list of property lists -- 3. a property list of linear lists (cf. AddToSubList()) -- should be an integer if aList is a linear list. If -- aList is a linear list, it can be any value except -- . In this case, if aKey is an integer, and aList -- contains integer properties, then the property with the -- given value will be given preference to the value at the -- given index. -- can be any Lingo value (except if the -- sublists are property lists) -- can be any Lingo value. If the sublists are -- linear lists then aProp will be placed in the sublist -- instead of aValue, which will be ignored. -- has no effect unless it is TRUE or #sorted. In -- this case new sublists are sorted before being added to -- aList. -- ACTION: Adds aValue to the aKey sublist of aList -- SAMPLE: 1. vList = [[#a: 1, #b: 2]] -- AddToKeyList(vList, 1, #c, 3) -- extend existing -- put vList -- -- [[#a: 1, #b: 2, #c: 3]] -- -- AddToKeyList(vList, 2, #d, 4) -- add new -- put vList -- -- [[#a: 1, #b: 2, #c: 3], [#d: 4]] -- -- 2. vList = [2: [#ii: "two"], #two: [#b: 2]] -- AddToKeyList(vList, 2, 2, "tutu too") -- put vList -- -- [2: [#ii: "two", 2: "tutu too"], #two: [#b: 2]] -- -- AddToKeyList(vList, 4, 4, 4) -- put vList -- -- [2: [#ii: "two", 2: "tutu too"], ..., 4: [4: 4]] -- -- 3. vList = [#a: [1, 2]] -- AddToKeyList(vList, #a, 3) -- only 3 parameters -- put vList -- -- [#a: [1, 2, 3]] -- -- AddToKeyList(vList, #b, 4) -- put vList -- -- [#a: [1, 2, 3], #b: [4]] -------------------------------------------------------------------- case ilk(aList) of #list: -- [[: , ...], [: , ...], ...] if not integerP(aKey) then return #integerExpected else if aKey < 1 then return #indexOutOfRange else if aKey > aList.count then -- Add a sub propList at the given index vSubList = [:] case aSorted of TRUE, #sorted: vSubList.sort() end case aList[aKey] = vSubList -- intermediate values set to 0 end if vSubList = aList[aKey] case ilk(vSubList) of #propList: -- continue #integer: if not vSubList then -- An intermediate value of 0 was added at this position -- when a sublist was added at a higher index value vSubList = [:] case aSorted of TRUE, #sorted: vSubList.sort() end case aList[aKey] = vSubList else return #propListExpected end if otherwise: return #propListExpected end case vSubList.addProp(aProp, aValue) #propList: if voidP(aKey) then return #propertyExpected end if vSubList = aList.getaProp(aKey) case ilk(vSubList) of #propList: -- [: [: , ...], ...] if voidP(aProp) then return #propertyExpected else vSubList.addProp(aProp, aValue) end if #list: -- [: [, , ...], ...] -- Duplicates the functionality of AddToSubList() vSubList.append(aProp) -- not aValue otherwise: case aValue of void: -- Assume that aProp is the value and that it is to be -- added to a linear list vSubList = [] case aSorted of TRUE, #sorted: vSubList.sort() end case aList.addProp(aKey, vSubList) vSubList.append(aProp) -- not aValue otherwise: vSubList = [:] case aSorted of TRUE, #sorted: vSubList.sort() end case aList.addProp(aKey, vSubList) vSubList.addProp(aProp, aValue) end case end case otherwise: return #listExpected end case return 0 -- no error end AddToKeyList on DeleteFromSubList(aPropList, aProp, aValue) ----------------------- -- INPUT: should be a property list whose values are -- linear lists -- can be any Lingo value except -- can be any Lingo value. -- ACTION: If aValue is #deleteParentList then the aProp sublist of -- aPropList is deleted. In all other cases, the first -- occurrence of aValue is deleted from the aProp sublist of -- aPropList, -- SAMPLE: vList = [#a: [1, 2, 3], #b: [4]] -- DeleteFromSubList(vList, #a, 3) -- put vList -- -- [#a: [1, 2], #b: [4]] -- DeleteFromSubList(vList, #b, #deleteParentList) -- put vList -- -- [#a: [1, 2]] -- put DeleteFromSubList(vList, #c, 5) -- -- 0 -------------------------------------------------------------------- if ilk(aPropList) <> #propList then return #propListExpected else if voidP(aProp) then return #propertyExpected end if vSubList = aPropList.getaProp(aProp) if ilk(vSubList) = #list then if aValue = #deleteParentList then aPropList.deleteProp(aProp) else vSubList.deleteOne(aValue) end if end if return 0 -- no error end DeleteFromSubList on FlattenList(nestedList, flatList) --------------------------------- -- Returns a non-nested list with the items in the same order as -- nested list. Each branch is be searched in order. The returned -- list only includes non-list values. -- -- Example: -- put FlattenList([1: [2: "two", #3: [4: "four"], 5: "five"]]) -- -- [2: "two", 4: "four", 5: "five"] -- -- The parameter need not be used: it is required for -- recursive calls. -------------------------------------------------------------------- if not ilk(nestedList, #propList) then return else if not ilk(flatList, #propList) then flatList = [:] end if counter = nestedList.count() repeat with i = 1 to counter theValue = nestedList[i] if ilk(theValue, #propList) then FlattenList(theValue, flatList) else theProp = nestedList.getPropAt(i) flatList.addProp(theProp, theValue) end if end repeat return flatList end FlattenList on caseFreeGetPos(aList, aString) ------------------------------------ -- Returns the position of in , regardless of any -- case differences. If the same string appears more than once, -- the number of an item with identical case is returned. If no -- string with identical case exists, the number of the first item -- with the same characters is returned. If no matching string is -- found, the returned value is zero. -------------------------------------------------------------------- i = aList.getPos(aString) if i then -- Identical case return i end if -- The case may be different: test each item separately counter = aList.count() repeat with i = 1 to counter if aList[i] = aString then return i end if end repeat return 0 end caseFreeGetPos on AddPropAt(propertyList, index, propertyName, propertyValue) ------- -- Adds a property named with a value -- at position of . If is greater -- than the number of elements in then the new -- property is added at the end of the list. -- -- An error is returned if is not a propList, or if -- is negative. -- -- The original list is modified, and a pointer to it is returned. -------------------------------------------------------------------- if ilk(propertyList, #propList) then return #invalidPropList else if index < 0 then return #invalidIndex end if tempList = [:] i = propertyList.count() repeat while i >= index tempList.addProp(propertyList.getPropAt(i), propertyList[i]) propertyList.deleteAt(i) i = i - 1 end repeat propertyList.addProp(propertyName, propertyValue) i = tempList.count() repeat while i propertyList.addProp(tempList.getPropAt(i), tempList[i]) i = i - 1 end repeat return propertyList -- Unnecessary but nice end AddPropAt on AddProp_At(propertyList, index, propertyName, propertyValue) -- Conciser but marginally slower alternative tempList = duplicate(propertyList) propertyList.deleteAll() itemCount = tempList.count() repeat with i = 1 to itemCount if i = index then propertyList.addProp(propertyName, propertyValue) end if propertyList.addProp(tempList.getPropAt(i), tempList[i]) end repeat return propertyList -- Unnecessary but nice end AddProp_At on addItems(propList1, propList2) ------------------------------------- -- Adds any property/value pairs which occur only in propList2 to -- propList1 -------------------------------------------------------------------- if ilk(propList1) <> #propList then return #invalidPropList else if ilk(propList2) <> #propList then return #invalidPropList end if i = propList2.count() repeat while i theProp = propList2.getPropAt(i) if not propList1.findPos(theProp) then propList1.addProp(theProp, propList2[i]) end if i = i - 1 end repeat end addItems on getProps(propertyList) -------------------------------------------- -- Returns a linear list containing the property names in -- . -------------------------------------------------------------------- if ilk(propertyList) <> #propList then return #invalidPropList end if propsList = [] i = propertyList.count() repeat while i propsList[i] = propertyList.getPropAt(i) i = i - 1 end repeat return propsList end getProps on multiGet(sourceList, accessList) ---------------------------------- -- Returns the subList of identified by the index -- numbers or property names in . -- -- Example: -- sourceList = [1: [2, [#three: [#a, #b, #c, #d]]]]) -- -- ^ ^ ^ ^ -- accessList = [1, 2, #three, 4] -- put multiGet(sourceList, accessList) -- -- #d -- -- Note that integer index numbers have priority over property -- names: if you are using integer property names, then unexpected -- results may be returned. -------------------------------------------------------------------- if not listP(sourceList) then return VOID -- #invalidList else if not listP(accessList) then return VOID -- #invalidList end if counter = accessList.count() repeat with i = 1 to counter if not listP(sourceList) then return VOID -- #invalidLevel else index = accessList[i] if integerP(index) then if index > sourceList.count() or index < 1 then return VOID -- #indexOutOfRange end if else -- index might be a property name: convert to integer if ilk(sourceList) = #propList then index = sourceList.findPos(index) if not integerP(index) then return VOID -- #invalidProperty end if else return VOID -- #invalidIndex end if end if end if sourceList = sourceList[index] end repeat return sourceList end multiGet on multilevelGet theList set listPointer = theList set countParams = the paramCount repeat with i = 2 to countParams set listPointer = getAt(listPointer, param(i)) end repeat return listPointer end multilevelGet -- USE THIS SYNTAX -- multilevelGet(nestedList, 5,3,2) -- returns the 2nd item of the 3rd item of the 5th item of nestedList on multiSet(sourceList, accessList, newValue) ------------------- -- Sets the element of identified by the index numbers -- or property names in to the value identified by -- newValue. -- -- Returns 0 if no error is encountered or an error symbol -- (#indexOutOfRange | #invalidLevel | #invalidIndex). An error -- will occur if any of the intermediate-level lists expected by -- do not exist, or are of the wrong type. If the -- last value in cannot be found in the final-level -- list it will be created. -- -- Example: -- sourceList = [1: [2, [#three: [#a, #b, #c ]]]] -- -- ^ ^ ^ ^ ^ -- accessList = [1, 2, #three, 5] -- put multiSet(sourceList, accessList, #e) -- -- 0 -- put sourceList -- -- [1: [2, [#three: [#a, #b, #c, 0, #e]]]] -- -- Note that integer index numbers have priority over property -- names: if you are using integer property names, then unexpected -- results may occur. -------------------------------------------------------------------- if not listP(accessList) then return #invalidList else -- Create a copy of accessList so that it can be modified accessList = duplicate(accessList) end if counter = accessList.count() if not counter then return #invalidLevel end if finalIndex = accessList.getLast() accessList.deleteAt(counter) -- counter = counter - 1 -- repeat with i = 1 to counter -- index = accessList[i] -- if integerP(index) then -- -- index gives the sequential position in sourceList -- case ilk(sourceList) of -- #list, #propList: -- if sourceList.count() < index then -- return #indexOutOfRange -- end if -- otherwise -- return #invalidLevel -- end case -- -- else -- -- index is be a property name: sourceList must be a propList -- if ilk(sourceList) = #propList then -- index = sourceList.findPos(index) -- if not integerP(index) then -- return #invalidProperty -- end if -- else -- return #invalidIndex -- end if -- end if -- -- sourceList = sourceList[index] -- end repeat sourceList = multiGet(sourceList, accessList) if integerP(finalIndex) then case ilk(sourceList) of #list: sourceList[finalIndex] = newValue #propList: if sourceList.count() < finalIndex then -- Create a dummy property sourceList.addProp(finalIndex, newValue) else sourceList[finalIndex] = newValue end if otherwise return #invalidLevel end case else -- finalIndex is not an integer: sourceList must be a propList case ilk(sourceList) of #list: return #invalidIndex #propList: sourceList.setaProp(finalIndex, newValue) otherwise return #invalidLevel end case end if return 0 -- No error end multiSet on listsAreSimilar(modelList, otherList) ----------------------------- -- Called recursively if is a nested list. -- -- * Returns TRUE if all elements in also appear in -- . The elements may appear in a different order, -- and may contain elements which are missing in -- . -- -- If is not a list, a straight comparison between the -- two parameters is performed. If the two parameters are different -- instances of the same object, and have the same properties, they -- could be considered to be "similar". However, this handler will -- treat them as different. -------------------------------------------------------------------- case ilk(modelList) of #list: i = modelList.count() repeat while i if not otherList.getPos(modelList[i]) then return FALSE end if i = i - 1 end repeat -- All the expected values are present return TRUE #propList: i = modelList.count() repeat while i modelProp = modelList.getPropAt(i) if not otherList.findPos(modelProp) then return FALSE end if modelValue = modelList[i] otherValue = otherList.getProp(modelProp) if not listsAreSimilar(modelValue,otherValue) then return FALSE end if i = i - 1 end repeat -- All the expected properties and values are present return TRUE otherwise return modelList = otherList end case end listsAreSimilar on listsAreDuplicate(list1, list2) ----------------------------------- -- Called recursively. -- -- * Returns TRUE if all elements in appear in in -- the same order. Properties and values must match. -- -- If is not a list, a straight comparison between the -- two parameters is performed. If the two parameters are different -- instances of the same object, and have the same properties, they -- could be considered to be "duplicates". However, this handler -- will treat them as different. -------------------------------------------------------------------- listIlk = ilk(list1) if ilk(list2) <> listIlk then return FALSE end if case listIlk of #list, #propList: -- continue otherwise -- May need to be adjusted return (list1 = list2) end case i = list1.count() if i <> list2.count() then return FALSE end if if listIlk = #list then repeat while i --if not listsAreIdentical(list1[i], list2[i]) then if not listsAreDuplicate(list1[i], list2[i]) then return FALSE end if --end if i = i - 1 end repeat else repeat while i if list1.getPropAt(i) <> list2.getPropAt(i) then return FALSE else --if not listsAreIdentical(list1[i], list2[i]) then if not listsAreDuplicate(list1[i], list2[i]) then return FALSE end if --end if end if i = i - 1 end repeat end if return TRUE end listsAreDuplicate on listsAreIdentical(list1, list2) ----------------------------------- -- * Returns TRUE if and are pointers to the same -- list. Returns FALSE in all other cases. -- -- The test is carried out by adding a new item to and -- checking if it appears in the same place in . The chances -- that happens to contain the test element in the correct -- position are slim indeed. -------------------------------------------------------------------- listIlk = ilk(list1) if ilk(list2) <> listIlk then return FALSE end if case listIlk of #list: identity = symbol("listCheck"&the ticks) list1.add(identity) index = list1.getPos(identity) if index > list2.count() then identity = FALSE else identity = (list2[index] = identity) end if list1.deleteAt(index) return identity #propList: identity = symbol("listCheck"&the ticks) list1.addProp(identity, identity) index = list1.findPos(identity) if index > list2.count() then identity = FALSE else identity = (list2[index] = identity) end if list1.deleteAt(index) return identity otherwise return FALSE end case end listsAreIdentical on SaveAsString(aValue) ---------------------------------------------- -- INPUT: can be any Lingo value, except for transforms and -- property lists with properties whose value is . -- To be precise, it can be any Lingo value which can be -- saved as a string and restored using value(). -- OUTPUT: Returns a string whose value() is equivalent to the -- original. -------------------------------------------------------------------- case ilk(aValue) of -- #transform: -- can't be restored using value() #list: tString = "[" tCount = aValue.count repeat with i = 1 to tCount tValue = ConvertToString(aValue[i]) put tValue&"," after tString end repeat if tCount then delete the last char of tString end if put "]" after tString #propList: tString = "[" tCount = aValue.count repeat with i = 1 to tCount tProp = SaveAsString(aValue.getPropAt(i)) tValue = SaveAsString(aValue[i]) put tProp&":"&tValue&"," after tString end repeat if tCount then delete the last char of tString end if put "]" after tString #void: tString = "void" #symbol: tString = "#"&aValue #string: tString = ReplaceAll(aValue, QUOTE, QUOTE&""E&""E) tString = QUOTE&tString"E otherwise: tString = string(aValue) end case return tString end SaveAsString -- -- on FindItem theList, theProp, theValue ------------------------------- -- theList = \ --[\ -- #propA: ["valueA1", "valueA2", ...], \ -- #propB: ["valueB1", "valueB2", ...], \ -- ... \ --] -- theProp = one of the props of theList -- theValue = a value in the subList getProp (theList, theProp) -- dataList = [#propA: "valueAn", #propB: "valueBn", ...] -------------------------------------------------------------------- set subList = getProp (theList, theProp) set itemNumber = getPos (subList, theValue) set dataList = [:] if itemNumber then set counter = count (theList) repeat while counter set theProp = getPropAt (theList, counter) set subList = getAt (theList, counter) set theValue = getAt (subList, itemNumber) addProp (dataList, theProp, theValue) set counter = counter - 1 end repeat end if return dataList end on AddItem theList, dataList -- No error checking for mismatching props set counter = count (dataList) repeat while counter set theProp = getPropAt (dataList, counter) set theValue = getAt (dataList, counter) set subList = getProp (theList, theProp) append (subList, theValue) set counter = counter - 1 end repeat end on DeleteItem theList, itemNumber -- No error checking for misplaced items set counter = count (theList) repeat while counter set subList = getAt (theList, counter) deleteAt (subList, itemNumber) set counter = counter - 1 end repeat end on FindItemNumber theList, theProp, theValue set subList = getProp (theList, theProp) return getPos (subList, theValue) end FindItemNumber -- -- on GetStringList(aList) ---------------------------------------------- -- INPUT: should be a linear or property list -- OUTPUT: Returns a string whose value is the original list. The -- use of embedded quotes in strings and the appearance of -- are explicitly treated. Void in sublists will -- be correctly treated, but strings with embedded quotes -- will only be treated in the outermost values -------------------------------------------------------------------- if listP(aList) then aList = aList.duplicate() -- so that we don't change the original i = aList.count repeat while i tValue = aList[i] case ilk(tValue) of #string: if tValue contains QUOTE then -- Replace embedded quotes aList[i] = ReplaceAll(tValue,QUOTE,QUOTE&""E&""E) end if end case i = i - 1 end repeat end if tString = string(aList) -- Replace any occurrences of with Void tString = ReplaceAll(tString, "", "Void") return tString end GetStringList on RemoveFromActorList(anInstance) ----------------------------------- -- INPUT: is an object instance -- ACTION: When the #stepFrame event is sent to the actorList, it is -- sent to each instance in the list in turn, as counted by -- index number. If the #stepFrame event is used to remove -- instance n from the list, the following instance will -- become instance n, but the #stepFrame event will be sent -- next to instance n+1, so the following instance will miss -- the event. This handler passes the #stepFrame event -- manually to the next instance, if necessary. -------------------------------------------------------------------- tIndex = the actorList.getPos(anInstance) if not tIndex then -- This instance is not currently on the actorList exit end if (the actorList).deleteAt(tIndex) if tIndex > the actorList.count() then -- This instance was the last instance on the actorList else -- The instance now at will not receive the #stepFrame -- event automatically. Send it manually: call(#stepFrame, [the actorList[tIndex]]) end if end RemoveFromActorList on RemoveFromList(anInstance, aList, anEvent) ------------------------ -- INPUT: is an object instance -- is a list such as the actorList or a -- scriptInstanceList. If this parameter is VOID then the -- actorList is used by default -- is a symbol, the event that is used to remove -- from . If VOID #stepFrame is used by -- default -- ACTION: When an event is used to "call" a list, that event is -- sent to each instance in the list in turn, as counted by -- index number. If the event is used to remove instance n -- on the list, the following instance will become instance -- n, but the event will be sent next to instance n+1, so -- the following instance will miss the event. If this -- handler is used to remove instances, the event will be -- passed manually to the next instance, if necessary. -------------------------------------------------------------------- if not listP(aList) then aList = the actorList end if if not symbolP(anEvent) then anEvent = #stepFrame end if tIndex = aList.getPos(anInstance) if not tIndex then -- This instance is not currently on aList exit end if aList.deleteAt(tIndex) if tIndex > aList.count() then -- This instance was the last instance on aList else -- The instance now at will not receive the event -- automatically. Send it manually: call(anEvent, [aList[tIndex]]) end if end RemoveFromList on ReplaceAll(aString, aSubString, aReplacement) --------------------- -- INPUT: is the main string to search in -- is a string which may appear in -- is a string which is to replace all -- occurrences of in -- OUTPUT: returns an updated version of where all -- occurrences of have been replaced by -- -------------------------------------------------------------------- if aSubString = "" then return aString end if tTreatedString = "" tLengthAdjust = the number of chars of aSubString - 1 repeat while TRUE tOffset = offset(aSubString, aString) if tOffset then if tOffset - 1 then put chars(aString, 1, (tOffset - 1)) after tTreatedString end if put aReplacement after tTreatedString delete char 1 to (tOffset + tLengthAdjust) of aString else -- there are no more occurrences put aString after tTreatedString return tTreatedString end if end repeat end ReplaceAll --34567890123456789012345678901234567890123456789012345678901234567890