Tell me if I'm reading this implementation correctly.
scoperefs is a C array of LinkList pointers. It will always be as
large as the greatest locallevel (deepest nested scope) in which a
nameref has pointed to a nested parameter, during the lifetime of the
shell. Therefore there may be NULL (and/or empty list) elements
scattered throughout scoperefs.
Each element of scoperefs is a linked list of named references (Param
pointers) that need to be re-scoped when that locallevel is exited.
Yes, that's correct.
Since a given element of scoperefs can only be referenced on the way
down or back up the call stack, maybe a linked list of linked lists
would be preferable?
One issue with a list of lists is that it can become expensive to find the list of params of a given scope. In the example below each call to "rec" needs to insert the "r" of the enclosing scope into the list of the current scope. That's cheap as that would be the list at the head of the list of lists. However, the last call to "rec" needs to insert "ref" into the list of the scope of "inner". That's expensive because it's behind the 100 lists corresponding to the 100 scopes of "rec".
outer() {
typeset -n ref
inner() {
typeset var=foo
typeset -n r
rec() {
typeset v=bar
r=v
typeset -n r
if (($1)); then rec $(($1-1)); else
ref=var
fi
}
rec 100
}
inner
}
outer
The advantage with an array of lists is that finding the list of a given scope is always done in constant time.
The example above is rather convoluted and obviously doesn't do anything useful. Are there useful scripts that would notice a difference? I don't know. Are there scripts that would suffer from a catastrophic slowdown? My guess is that it's rather unlikely. So, I guess a list of lists could also be an option.
Another issue with a list of lists arises in case you only want to store non-null/empty lists. Then you need an additional data structure to tell you to which scope each list of params corresponds to. Indeed, you can't rely on the "base" field of the first param of the list because that field may change after the param was inserted into the list (see reply below to your other question).
In the comments you say
> "Param" instances get reused when variables with
> the same name are redefined in the same scope
That would never apply to a parameter in a surrounding scope (it would
always become the pm->old of the new parameter in the nested scope).
You can "unset" an enclosing parameter and then redefine/reuse it with "typeset -g".
In what circumstances, then, does the base of a given parameter need
to be reset more than once to different values?
The example below shows how the "ref" parameter defined in "scope1" can be added to the list of params to reset of "scope2" AND of "scope3".
scope1() {
typeset -n ref
scope2() {
typeset var2=22
ref=var2 # scope1's ref is inserted into scope2's list
scope3() {
typeset var3=33
unset -n ref
typeset -g -n ref=var3 # scope1's ref is inserted into scope3's list
}
scope3
}
scope2
}
scope1
Does one of the
"Transitive references" tests in K01 cover such a circumstance?
I'm not sure. I'll have to check. Anyway, I should probably add some explicit tests for the following cases:
- Ref added to multiple scope lists
- Ref added multiple times to the same scope list
- Ref added to a scope list is now an integer with base=N
- Ref added to a scope list was unset (btw, the code should probably explicitly skip params with PM_UNSET)
Philippe