I should explain my theorie inmore detail (you might want to copy-paste the ascii graphics into a notepad).
Example:
Code: Select all
(
echo some
:label1
:label2
echo text
)
The optimized parse tree probably looks somehow like that (i removed some parts that are unneccessary for my explaination, to keep that example small):
Code: Select all
┌─────┐
│ ( ) │
└┬┬┬┬┬┘
┌─────────────────┘│││└────────────────────────────┐
│ ┌────────┘│└───────────┐ │
┌───┴──┐ ┌──┴──┐ ┌──┴───┐ ┌───┴───┐ ┌──┴──┐
│ CRLF │ │ Cmd │ │ CRLF │ │ Label │ │ Cmd │
└──────┘ └┬───┬┘ └──────┘ └───┬───┘ └┬───┬┘
┌──┘ └──┐ │ ┌──┘ └──┐
┌───┴──┐ ┌──┴───┐ ┌────┴────┐ ┌───┴──┐ ┌──┴───┐
│ echo │ │ some │ │ :label2 │ │ echo │ │ text │
└──────┘ └──────┘ └─────────┘ └──────┘ └──────┘
Stored in two arrays:
tree[] := [ (), CRLF, Cmd, CRLF, Label, Cmd, echo, some, :label2, echo, text ]
children[] := [ (1, 5), null, (6, 7), null, (8,
8), (9, 10), null, null, null, null, null ]
One advantage of such a representation is, that you could optimize easily, without knowing sublevels of the tree.
Because :label1 was part od the initial command there must be an intermediate parse tree:
Code: Select all
┌───────┐
│ ( ) │
└┬┬┬┬┬┬┬┘
┌────────────────────────────┘│││││└─────────────────────────────────────┐
│ ┌───────────────────┘│││└────────────────────┐ │
│ │ ┌──────────┘│└─────────┐ │ │
┌───┴──┐ ┌──┴──┐ ┌──┴───┐ ┌───┴───┐ ┌──┴───┐ ┌───┴───┐ ┌──┴──┐
│ CRLF │ │ Cmd │ │ CRLF │ │ Label │ │ CRLF │ │ Label │ │ Cmd │
└──────┘ └┬───┬┘ └──────┘ └───┬───┘ └──────┘ └───┬───┘ └┬───┬┘
┌──┘ └──┐ │ │ ┌──┘ └──┐
┌───┴──┐ ┌──┴───┐ ┌────┴────┐ ┌────┴────┐ ┌───┴──┐ ┌──┴───┐
│ echo │ │ some │ │ :label1 │ │ :label2 │ │ echo │ │ text │
└──────┘ └──────┘ └─────────┘ └─────────┘ └──────┘ └──────┘
stored
tree[] := [ (), CRLF, Cmd, CRLF, Label, CRLF, Label, Cmd, echo, "some", :label1, :label2, echo, "text" ]
children[] := [ (1, 7), null, (8, 9), null, (10, 10), null, (11, 11), (12, 13), null, null, null, null, null, null ]
So the opimization rule most probably was "remove [Label, CRLF]" (which would be the default rule to use in such a case):
If the rule was "remove [CRLF, Label]" it would have removed both labels,
If the rule was "remove [Label]" it would have removed both labels, too.
It is very unusual to use two command tokens without a delimiter, if delimiters are valid tokens.
It is also very unusual to start with a delimiter.
So what i would have expected the intermediate parse tree should look like:
Code: Select all
┌───────┐
│ ( ) │
└┬┬┬┬┬┬┬┘
┌─────────────────────────────┘│││││└──────────────────────────────┐
│ ┌───────────────────┘│││└────────────────────┐ │
│ │ ┌─────────┘│└──────────┐ │ │
┌──┴──┐ ┌───┴──┐ ┌───┴───┐ ┌──┴───┐ ┌───┴───┐ ┌──┴───┐ ┌──┴──┐
│ Cmd │ │ CRLF │ │ Label │ │ CRLF │ │ Label │ │ CRLF │ │ Cmd │
└┬───┬┘ └──────┘ └───┬───┘ └──────┘ └───┬───┘ └──────┘ └┬───┬┘
┌──┘ └──┐ │ │ ┌──┘ └──┐
┌───┴──┐ ┌──┴───┐ ┌────┴────┐ ┌────┴────┐ ┌───┴──┐ ┌──┴───┐
│ echo │ │ some │ │ :label1 │ │ :label2 │ │ echo │ │ text │
└──────┘ └──────┘ └─────────┘ └─────────┘ └──────┘ └──────┘
stored:
tree[] := [ (), Cmd, CRLF, Label, CRLF, Label, CRLF, Cmd, echo, some, :label1, :label2, echo, text ]
children[] := [ (1, 7), (8, 9), null, (10, 10), null, (11, 11), null, (12, 13), null, null, null, null, null, null ]
The label removement would have worked correctly, no matter the rule used.
However, it is possible that that may explain the above behaviour:
In case that parse tree is build from bottom to top, the " ^" part may end up as an argument for the "( )" command (if there is a phase or "read argument"-code in which the compound command is read in which the "^" is no special character).
But I don't know anything about that building process, so that's just an unproven hypothesis based on the (from programmers viewpoint) misplaced CRLF and a label that isn't removed properly from that parse tree while another is.
The only thing i know is, that such errors are typical to programmers and are hard to find unless you are searching for that specifically.
penpen