v2 doesnt have an autoexecute section anymore, so u can include it pretty much anywhere it would syntactically make sense to do so(eg
not inside a class definition where the instance vars go or in unreachable code paths, eg after an explicit
return of the autoexec thread). of course, trying to call the methods
before the prototype has been adjusted, would still throw an exception. in any case, ud want to place ur
#Include somewhere towards the very top of the script
although, if the
/include cmd line switch build makes it to v2, i suppose u could use that instead. it would depend on the implementation, but i assume
Array will have been initialized by the time the
/include includes get to execute(havent actually tried the test build or checked to see how its supposed to work, so thats just speculation on my part)
and also is it possible to switch the base index to 0 as that would make switching to v2 a lot easier.
Code: Select all
#Requires AutoHotkey v2.0-a134-d3d43350
ApplyZeroBasedArrayIndexPatches()
ApplyZeroBasedArrayIndexPatches() {
OneBased__Item := Array.Prototype.GetOwnPropDesc('__Item') ; retrieve the default __Item implementation, ie {Get: the default getter, Set: the default setter}
; store in a variable, so it can be captured by closure further down.
; this is done, so u can retain a reference to the default implementation
; at all times, no matter what client code tries to do (eg delete/override it),
; since our functionality depends on it
OneBasedGet := OneBased__Item.Get
OneBasedSet := OneBased__Item.Set ; store in a variable, so it can be captured by closure
Array.Prototype.DefineProp('__Item', {Get: ZeroBasedGet, Set: ZeroBasedSet}) ; override the default _Item with ours
ZeroBasedGet(this, zeroBasedIndex) {
try
{
; since were using the default one based implementation to do the work,
; we have to convert the zero based index back into a one based index, but
; do it in a way that preserves negative index semantics(ie array lookup from
; the end of the array, -1 last element, -2 second to last and so on...)
oneBasedIndex := (zeroBasedIndex >= 0) ? zeroBasedIndex + 1 : zeroBasedIndex
return OneBasedGet(this, oneBasedIndex) ; delegate to the default implementation
}
catch IndexError as Err ; need to adjust the index in the error msg here, otherwise the default exception would report a wrong index...
throw IndexError(Err.Message, -2, oneBasedIndex) ; ...eg 1 instead of 0, given an empty Array := [] and indexing with Array[0] into it
catch TypeError as Err ; this one is triggered when u try to index with strings for example Array['asdfasdf']. only added it cause i dont want the default exception
throw TypeError(Err.Message, -2, Err.Extra) ; to point to this function's internals instead of the callsite where the exception actually occurred(see -2)
; catch ???
; implement more exception handling as u see fit
}
ZeroBasedSet(this, value, zeroBasedIndex) {
try
{
oneBasedIndex := (zeroBasedIndex >= 0) ? zeroBasedIndex + 1 : zeroBasedIndex
return OneBasedSet(this, value, oneBasedIndex)
}
catch IndexError as Err
throw IndexError(Err.Message, -2, oneBasedIndex)
catch TypeError as Err
throw TypeError(Err.Message, -2, Err.Extra)
}
; store in a variable, so it can be captured by closure further down.
; same premise as was done for __Item
OneBasedEnumDispatcher := Array.Prototype.GetOwnPropDesc('__Enum').Call
Array.Prototype.DefineProp('__Enum', {Call: ZeroBasedEnumDispatcher}) ; override the default dispatcher with ours
ZeroBasedEnumDispatcher(this, numberOfVars) {
; have the original dispatcher provide us with the original one based enumerator implementation
OneBasedEnum := OneBasedEnumDispatcher(this, numberOfVars)
switch numberOfVars
{
case 1: return OneBasedEnum ; no special handling needed, since it enumerates the array's VALUES only, eg for Val in Array
case 2: return ZeroBasedIndexValueEnum ; for Idx, Val in Array
ZeroBasedIndexValueEnum(&zeroBasedIndex, &value) {
if OneBasedEnum(&oneBasedIndex, &value) ; while Array has Items, retrieve one based index and assign Item to for-loop's second OutputVar
{
zeroBasedIndex := oneBasedIndex - 1 ; convert one based index back into zero based, and assign to for-loop's first OutputVar
return true ; continue enumerating, since an Item had been returned
}
}
; case 3-19: ; implement as u see fit... or dont(see below)
default: throw Error('No matching Array Enumerator found for this many for-loop variables.', -2, numberOfVars)
}
}
}
most anything is possible with v2