This may be an issue with other node types too, but I noticed it with path when writing some evaluation code and I think I've seen at least two cases of it "in the wild".
In the evaluation code, you have the following code:
if(ii === 0 && step.consarray) {
resultSequence = yield * evaluate(step, inputSequence, environment);
} else {
resultSequence = yield * evaluateStep(step, inputSequence, environment);
}
Basically this is saying the when faced with a path, you should treat them as transformations of the input
value (because resultSequence
becomes inputSequence
as you loop over this code) unless the first one is an array constructor. In that case, you should set the result to the result of evaluating the array constructor.
It seems to me (and I could be totally wrong here), that this is actually a defect in the AST design. The reason I use the word "defect" is because when I looked at this code I thought of other ways it could fail and I think I'm able to demonstrate at least two different ones. But I can imagine an alternative way of designing the AST that could avoid this special handling (and the two failures I've seen).
The issue is with the implicit assumption in the path
type that the current context value as the initial value for all each step
. The array constructor breaks this because it defines an entirely new context. But the problem I see is...it isn't alone.
Consider, for example, evaluating this:
[{"size": 1}, {"size": 2}].size
From this we get [1, 2]
. However, wrapping our array construction in a block
using parentheses, i.e.,
([{"size": 1}, {"size": 2}]).size
Now gives us this: [1,2,1,2,1,2]
. ??? (even though if we drop the .size
from both expressions, we get exactly the same answer).
Note it isn't just block
nodes either. I suspect a function evaluation could trigger the same issue in some circumstances. I suspect there are other cases as well.
It seems to me that a clean way to address this would be to have an explicit head
AST node associated with each path
node. This head
node would represent the initial value in the loop over the steps and in most cases, its value would be the equivalent of $
(the current context). But the key thing here is that the parser could determine this not the evaluator. I don't know for certain because I'm not that familiar with the grammar, but my guess is that the parser can determine unambiguously what the "head" of the expression is and encode that in the AST so that we don't need to worry about these special cases while tree-walking.
Or am I completely off base here? In any case, I think there is a bug here (see my examples above). My point is that I think this can be addressed cleanly at the AST level.