Re: [PHP4BETA] zend-parser.y shift/reduce conflicts and suggestions
| From: | Philippe Verdy | Date: | Wed, 18 Aug 1999 00:04:10 +0000 |
| Subject: | Re: [PHP4BETA] zend-parser.y shift/reduce conflicts and suggestions | ||
| References: | 1 | Groups: | php.version4 |
| Request: | Send a blank email to php-version4+get-3532@lists.php.net to get a copy of this message | ||
It was a suggestion, because my solution effectively removes 2 shift/reduce
conflicts, and removes about a hundred of parser states.
Removing them cleanly is always good prior to make significant additions to
the syntax.
Also I have some quirks in the syntax accepted by the language by looking at
nullable tokens. For example the grammar accepts several consecutive commas
with no expressions between them. This gives some strange statements like:
echo,,,,,,,,;
and
for(,,,,,,,,,;,,,,,,,,,;,,,,,,,,,);
The quirks on 'echo_expr' are harmless, because no expression means no code
generated. However the quirks on the 'for_expr' generates unuseful code that
produces a boolean true expression which gets evaluated at run-time.
Also I have noticed that the code generation for the 'switch/case/default'
statement was not efficient as it could, because it generates too many
ZEND_JMP and ZEND_JMPZ opcodes.
Don't you think that the opcodes associated to the switch labels could be
stored in another structure, so that the labelled statements that follow the
switch() could be generated as a whole single block with no added jumps ?
For that you would only need to generate an initial opcode for the switch,
with the condition expression in one branch, and the second branch reserved
for the jump table, that could be either stored in the main opcodes-array
after the code for the labelled statements.
A simple generation scheme could then be:
ZEND source address: opcodes (trigrams)
------------------ ---------------------------------------
switch(expr) { <result=expr>
<ZEND_SWITCH,result,jumptable>
case 1:
case 2:
statements; addr_1: <opcodes for statements>
break; <ZEND_JMP,end_switch,nil>
case 3:
statements; addr_2: <opcodes for statements>
/*falls thru*/
case 4:
statements: addr_3: <opcodes for statements>
default:
statements: addr_4: <opcodes for statements>
} <ZEND_JMP,end_switch,nil>
jumptable: <ZEND_CASE,addr_1,1>
<ZEND_CASE,addr_1,2>
<ZEND_CASE,addr_2,3>
<ZEND_CASE,addr_3,4>
<ZEND_JMP,addr_4,nil>
end_switch:
Note that this introduces a ZEND_SWITCH opcode, which is a special form
of branch like ZEND_JUMP. One of its branchs links to the expression, the
other branch links to the jump table. When executing this opcode, it simply
scans the jump table to find a match in the ZEND_CASE opcodes. If there is
no ZEND_CASE opcode which matches the expression, it simply jumps after the
list of ZEND_CASE opcodes. At that place there may be a classic ZEND_JMP
opcode that can bring it to the optional default case.
Note that the new ZEND_CASE opcode is then a trigram, unlike the ZEND_JMPZ
opcode used for now. A ZEND_CASE opcode cannot be executed alone by itself:
it's an invalid opcode anywhere in the zend_execute() loop, unless it is
scanned by a ZEND_SWITCH opcode which actually only reads it. A ZEND_CASE
opcode can also store source filename and linenumber for debugging and
exception handling like all other opcodes...
ZEND_SWITCH scans opcodes until it finds either a case match or another
opcode. If a match is found, it jumps to the address indicated by the opcode
operand. If no match is found (ZEND_SWITCH does not find a matching
ZEND_CASE opcode), then ZEND_SWITCH simply branches at the next opcode that
follows the last ZEND_CASE.
Note that the last opcode of the labelled statement block must be terminated
by an unconditional jump to the end of the switch, this avoids executing the
invalid ZEND_CASE opcodes and the possible ZEND_JMP for the default case.
For the parse-time code generation, all that is needed is to allocate a
dynamic array to store ZEND_CASE opcodes, while other statements in the
labelled block can be generated on the fly. Such an array will reasonably be
small because it only stores constants and opcode addresses within the
generated opcodes for the labelled block. This array cannot be global,
because there can be sevral embedded switch statements. However, its address
can be kept at the top of the zend parser stack already used to store the
state of embedded loops and switchs.
----- Original Message -----
From: Andi Gutmans <andi@zend.com>
To: Philippe Verdy <verdy_p@wanadoo.fr>; <php4beta@lists.php.net>
Sent: Tuesday, August 17, 1999 10:56 PM
Subject: Re: [PHP4BETA] zend-parser.y shift/reduce conflicts and suggestions
> I don't see a problem with the current parsing file.
> The if/elseif/else shift/reduce conflict is resolved correctly. It is
> shifted. This is the way the dangling else problem should be handled.
>
> Andi
> ---
> Andi Gutmans <andi@zend.com>
> http://www.zend.com/
>