4 +---------------------------------------------------------------------------------+
5 | Copyright (c) 2010 César Rodas and Menéame Comunicacions S.L. |
6 +---------------------------------------------------------------------------------+
7 | Redistribution and use in source and binary forms, with or without |
8 | modification, are permitted provided that the following conditions are met: |
9 | 1. Redistributions of source code must retain the above copyright |
10 | notice, this list of conditions and the following disclaimer. |
12 | 2. Redistributions in binary form must reproduce the above copyright |
13 | notice, this list of conditions and the following disclaimer in the |
14 | documentation and/or other materials provided with the distribution. |
16 | 3. All advertising materials mentioning features or use of this software |
17 | must display the following acknowledgement: |
18 | This product includes software developed by César D. Rodas. |
20 | 4. Neither the name of the César D. Rodas nor the |
21 | names of its contributors may be used to endorse or promote products |
22 | derived from this software without specific prior written permission. |
24 | THIS SOFTWARE IS PROVIDED BY CÉSAR D. RODAS ''AS IS'' AND ANY |
25 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
26 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
27 | DISCLAIMED. IN NO EVENT SHALL CÉSAR D. RODAS BE LIABLE FOR ANY |
28 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
29 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
30 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
31 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
32 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
33 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE |
34 +---------------------------------------------------------------------------------+
35 | Authors: César Rodas <crodas@php.net> |
36 +---------------------------------------------------------------------------------+
40 %declare_class
{ class Haanga_Compiler_Parser
}
45 function __construct
($lex, $file='')
53 throw new Haanga_Compiler_Exception
($text.
' in '.
$this->file.
':'.
$this->lex
->getLine
());
66 %nonassoc T_GT T_GE T_LT T_LE.
68 %left T_PLUS T_MINUS T_CONCAT.
69 %left T_TIMES T_DIV T_MOD.
70 %left T_PIPE T_BITWISE.
74 foreach
($this->yy_get_expected_tokens
($yymajor) as
$token) {
75 $expect[] = self
::$yyTokenName[$token];
77 $this->Error
('Unexpected ' .
$this->tokenName
($yymajor) .
'(' .
$TOKEN.
'), expected one of: ' . implode
(',', $expect));
81 start
::= body
(B
).
{ $this->body
= B
; }
83 body
(A
) ::= body
(B
) code
(C
).
{ A
=B
; A
[] = C
; }
84 body
(A
) ::= .
{ A
= array
(); }
86 /* List of statements */
87 code
(A
) ::= T_TAG_OPEN stmts
(B
).
{ if
(count
(B
)) B
['line'] = $this->lex
->getLine
(); A
= B
; }
88 code
(A
) ::= T_HTML
(B
).
{
89 A
= array
('operation' => 'html', 'html' => B
, 'line' => $this->lex
->getLine
() );
91 code
(A
) ::= T_COMMENT
(B
).
{
92 B
=rtrim
(B
); A
= array
('operation' => 'comment', 'comment' => B
);
94 code
(A
) ::= T_PRINT_OPEN filtered_var
(B
) T_PRINT_CLOSE.
{
95 A
= array
('operation' => 'print_var', 'variable' => B
, 'line' => $this->lex
->getLine
() );
98 stmts
(A
) ::= T_EXTENDS var_or_string
(B
) T_TAG_CLOSE.
{ A
= array
('operation' => 'base', B
); }
99 stmts
(A
) ::= stmt
(B
) T_TAG_CLOSE.
{ A
= B
; }
100 stmts
(A
) ::= for_stmt
(B
).
{ A
= B
; }
101 stmts
(A
) ::= ifchanged_stmt
(B
).
{ A
= B
; }
102 stmts
(A
) ::= block_stmt
(B
).
{ A
= B
; }
103 stmts
(A
) ::= filter_stmt
(B
).
{ A
= B
; }
104 stmts
(A
) ::= if_stmt
(B
).
{ A
= B
; }
105 stmts
(A
) ::= T_INCLUDE var_or_string
(B
) T_TAG_CLOSE.
{ A
= array
('operation' => 'include', B
); }
106 stmts
(A
) ::= custom_tag
(B
).
{ A
= B
; }
107 stmts
(A
) ::= alias
(B
).
{ A
= B
; }
108 stmts
(A
) ::= ifequal
(B
).
{ A
= B
; }
109 stmts
(A
) ::= T_AUTOESCAPE varname
(B
) T_TAG_CLOSE body
(X
) T_TAG_OPEN T_CUSTOM_END
(E
) T_TAG_CLOSE.
{
111 if
(B
!= 'on' && B
!= 'off') {
112 $this->Error
("Invalid autoescape param (".B.
"), it must be on or off");
114 if
(E
!= "endautoescape") {
115 $this->Error
("Invalid close tag ".E.
", it must be endautoescape");
117 A
= array
('operation' => 'autoescape', 'value' => B
, 'body' => X
);
123 custom_tag
(A
) ::= T_CUSTOM_TAG
(B
) T_TAG_CLOSE.
{
124 A
= array
('operation' => 'custom_tag', 'name' => B
, 'list'=>array
());
126 custom_tag
(A
) ::= T_CUSTOM_TAG
(B
) T_AS varname
(C
) T_TAG_CLOSE.
{
127 A
= array
('operation' => 'custom_tag', 'name' => B
, 'as' => C
, 'list'=>array
());
129 custom_tag
(A
) ::= T_CUSTOM_TAG
(B
) params
(X
) T_TAG_CLOSE.
{
130 A
= array
('operation' => 'custom_tag', 'name' => B
, 'list' => X
);
132 custom_tag
(A
) ::= T_CUSTOM_TAG
(B
) params
(X
) T_AS varname
(C
) T_TAG_CLOSE.
{
133 A
= array
('operation' => 'custom_tag', 'name' => B
, 'as' => C
, 'list' => X
);
137 custom_tag
(A
) ::= T_CUSTOM_BLOCK
(B
) T_TAG_CLOSE body
(X
) T_TAG_OPEN T_CUSTOM_END
(C
) T_TAG_CLOSE.
{
139 $this->error("Unexpected ".C
);
141 A
= array
('operation' => 'custom_tag', 'name' => B
, 'body' => X
, 'list' => array
());
143 custom_tag
(A
) ::= T_CUSTOM_BLOCK
(B
) params
(L
) T_TAG_CLOSE body
(X
) T_TAG_OPEN T_CUSTOM_END
(C
) T_TAG_CLOSE.
{
145 $this->error("Unexpected ".C
);
147 A
= array
('operation' => 'custom_tag', 'name' => B
, 'body' => X
, 'list' => L
);
150 /* Spacefull is very special, and it is handled in the compiler class */
151 custom_tag
(A
) ::= T_SPACEFULL T_TAG_CLOSE body
(X
) T_TAG_OPEN T_CUSTOM_END
(C
) T_TAG_CLOSE.
{
152 if
('endspacefull' != C
) {
153 $this->error("Unexpected ".C
);
155 A
= array
('operation' => 'spacefull', 'body' => X
);
158 /* variable alias (easier to handle in the compiler class) */
159 alias
(A
) ::= T_WITH varname
(B
) T_AS varname
(C
) T_TAG_CLOSE body
(X
) T_TAG_OPEN T_CUSTOM_END
(Z
) T_TAG_CLOSE.
{
160 if
(Z
!= "endwith") {
161 $this->Error
("Unexpected ".Z.
", expecting endwith");
163 A
= array
('operation' => 'alias', 'var' => B
, 'as' => C
, 'body' => X
);
166 /* Simple statements (don't require a end_tag or a body ) */
167 stmt
(A
) ::= T_SET varname
(C
) T_ASSIGN expr
(X
).
{ A
= array
('operation' => 'set', 'var' => C
,'expr' => X
); }
168 stmt
(A
) ::= regroup
(B
).
{ A
= B
; }
169 stmt
::= T_LOAD
string(B
).
{
170 if
(!is_file
(B
) ||
!Haanga_Compiler
::getOption
('enable_load')) {
171 $this->error(B.
" is not a valid file");
178 for_def
(A
) ::= T_FOR varname
(B
) T_IN filtered_var
(C
) T_TAG_CLOSE .
{
179 $var = $this->compiler
->get_context
(C
[0]);
180 if
(is_array
($var) ||
$var instanceof Iterator
) {
181 /* let's check if it is an object or array */
182 $this->compiler
->set_context
(B
, current
($var));
184 A
= array
('operation' => 'loop', 'variable' => B
, 'index' => NULL
, 'array' => C
);
187 for_def
(A
) ::= T_FOR varname
(I
) T_COMMA varname
(B
) T_IN filtered_var
(C
) T_TAG_CLOSE .
{
188 $var = $this->compiler
->get_context
(C
[0]);
189 if
(is_array
($var) ||
$var instanceof Iterator
) {
190 /* let's check if it is an object or array */
191 $this->compiler
->set_context
(B
, current
($var));
193 A
= array
('operation' => 'loop', 'variable' => B
, 'index' => I
, 'array' => C
);
198 for_stmt
(A
) ::= for_def
(B
) body
(D
) T_TAG_OPEN T_CUSTOM_END
(Z
) T_TAG_CLOSE.
{
200 $this->Error
("Unexpected ".Z.
", expecting endfor");
206 for_stmt
(A
) ::= T_FOR varname
(B
) T_IN range
(X
) T_TAG_CLOSE body
(E
) T_TAG_OPEN T_CUSTOM_END
(Z
) T_TAG_CLOSE.
{
208 $this->Error
("Unexpected ".Z.
", expecting endfor");
210 A
= array
('operation' => 'loop', 'variable' => B
, 'range' => X
, 'body' => E
, 'variable' => B
, 'step' => 1);
213 for_stmt
(A
) ::= T_FOR varname
(B
) T_IN range
(X
) T_STEP numvar
(S
) T_TAG_CLOSE body
(E
) T_TAG_OPEN T_CUSTOM_END
(Z
) T_TAG_CLOSE.
{
215 $this->Error
("Unexpected ".Z.
", expecting endfor");
217 A
= array
('operation' => 'loop', 'variable' => B
, 'range' => X
, 'body' => E
, 'variable' => B
, 'step' => S
);
220 for_stmt
(A
) ::= for_def
(B
) body
(D
) T_TAG_OPEN T_EMPTY T_TAG_CLOSE body
(E
) T_TAG_OPEN T_CUSTOM_END
(Z
) T_TAG_CLOSE.
{
222 $this->Error
("Unexpected ".Z.
", expecting endfor");
229 if_stmt
(A
) ::= T_IF expr
(B
) T_TAG_CLOSE body
(X
) T_TAG_OPEN T_CUSTOM_END
(Z
) T_TAG_CLOSE.
{
231 $this->Error
("Unexpected ".Z.
", expecting endif");
233 A
= array
('operation' => 'if', 'expr' => B
, 'body' => X
);
235 if_stmt
(A
) ::= T_IF expr
(B
) T_TAG_CLOSE body
(X
) T_TAG_OPEN T_ELSE T_TAG_CLOSE body
(Y
) T_TAG_OPEN T_CUSTOM_END
(Z
) T_TAG_CLOSE.
{
237 $this->Error
("Unexpected ".Z.
", expecting endif");
239 A
= array
('operation' => 'if', 'expr' => B
, 'body' => X
, 'else' => Y
);
243 ifchanged_stmt
(A
) ::= T_IFCHANGED T_TAG_CLOSE body
(B
) T_TAG_OPEN T_CUSTOM_END
(Z
) T_TAG_CLOSE.
{
244 if
(Z
!= "endifchanged") {
245 $this->Error
("Unexpected ".Z.
", expecting endifchanged");
247 A
= array
('operation' => 'ifchanged', 'body' => B
);
250 ifchanged_stmt
(A
) ::= T_IFCHANGED params
(X
) T_TAG_CLOSE body
(B
) T_TAG_OPEN T_CUSTOM_END
(Z
) T_TAG_CLOSE.
{
251 if
(Z
!= "endifchanged") {
252 $this->Error
("Unexpected ".Z.
", expecting endifchanged");
254 A
= array
('operation' => 'ifchanged', 'body' => B
, 'check' => X
);
256 ifchanged_stmt
(A
) ::= T_IFCHANGED T_TAG_CLOSE body
(B
) T_TAG_OPEN T_ELSE T_TAG_CLOSE body
(C
) T_TAG_OPEN T_CUSTOM_END
(Z
) T_TAG_CLOSE.
{
257 if
(Z
!= "endifchanged") {
258 $this->Error
("Unexpected ".Z.
", expecting endifchanged");
260 A
= array
('operation' => 'ifchanged', 'body' => B
, 'else' => C
);
263 ifchanged_stmt
(A
) ::= T_IFCHANGED params
(X
) T_TAG_CLOSE body
(B
) T_TAG_OPEN T_ELSE T_TAG_CLOSE body
(C
) T_TAG_OPEN T_CUSTOM_END
(Z
) T_TAG_CLOSE.
{
264 if
(Z
!= "endifchanged") {
265 $this->Error
("Unexpected ".Z.
", expecting endifchanged");
267 A
= array
('operation' => 'ifchanged', 'body' => B
, 'check' => X
, 'else' => C
);
271 ifequal
(A
) ::= T_IFEQUAL fvar_or_string
(B
) fvar_or_string
(C
) T_TAG_CLOSE body
(X
) T_TAG_OPEN T_CUSTOM_END
(Z
) T_TAG_CLOSE.
{
272 if
(Z
!= "endifequal") {
273 $this->Error
("Unexpected ".Z.
", expecting endifequal");
275 A
= array
('operation' => 'ifequal', 'cmp' => '==', 1 => B
, 2 => C
, 'body' => X
);
277 ifequal
(A
) ::= T_IFEQUAL fvar_or_string
(B
) fvar_or_string
(C
) T_TAG_CLOSE body
(X
) T_TAG_OPEN T_ELSE T_TAG_CLOSE body
(Y
) T_TAG_OPEN T_CUSTOM_END
(Z
) T_TAG_CLOSE.
{
278 if
(Z
!= "endifequal") {
279 $this->Error
("Unexpected ".Z.
", expecting endifequal");
281 A
= array
('operation' => 'ifequal', 'cmp' => '==', 1 => B
, 2 => C
, 'body' => X
, 'else' => Y
);
283 ifequal
(A
) ::= T_IFNOTEQUAL fvar_or_string
(B
) fvar_or_string
(C
) T_TAG_CLOSE body
(X
) T_TAG_OPEN T_CUSTOM_END
(Z
) T_TAG_CLOSE.
{
284 if
(Z
!= "endifnotequal") {
285 $this->Error
("Unexpected ".Z.
", expecting endifnotequal");
287 A
= array
('operation' => 'ifequal', 'cmp' => '!=', 1 => B
, 2 => C
, 'body' => X
);
289 ifequal
(A
) ::= T_IFNOTEQUAL fvar_or_string
(B
) fvar_or_string
(C
) T_TAG_CLOSE body
(X
) T_TAG_OPEN T_ELSE T_TAG_CLOSE body
(Y
) T_TAG_OPEN T_CUSTOM_END
(Z
) T_TAG_CLOSE.
{
290 if
(Z
!= "endifnotequal") {
291 $this->Error
("Unexpected ".Z.
", expecting endifnotequal");
293 A
= array
('operation' => 'ifequal', 'cmp' => '!=', 1 => B
, 2 => C
, 'body' => X
, 'else' => Y
);
297 block_stmt
(A
) ::= T_BLOCK varname
(B
) T_TAG_CLOSE body
(C
) T_TAG_OPEN T_CUSTOM_END
(Z
) T_TAG_CLOSE.
{
298 if
(Z
!= "endblock") {
299 $this->Error
("Unexpected ".Z.
", expecting endblock");
301 A
= array
('operation' => 'block', 'name' => B
, 'body' => C
);
304 block_stmt
(A
) ::= T_BLOCK varname
(B
) T_TAG_CLOSE body
(C
) T_TAG_OPEN T_CUSTOM_END
(Z
) varname T_TAG_CLOSE.
{
305 if
(Z
!= "endblock") {
306 $this->Error
("Unexpected ".Z.
", expecting endblock");
308 A
= array
('operation' => 'block', 'name' => B
, 'body' => C
);
311 block_stmt
(A
) ::= T_BLOCK number
(B
) T_TAG_CLOSE body
(C
) T_TAG_OPEN T_CUSTOM_END
(Z
) T_TAG_CLOSE.
{
312 if
(Z
!= "endblock") {
313 $this->Error
("Unexpected ".Z.
", expecting endblock");
315 A
= array
('operation' => 'block', 'name' => B
, 'body' => C
);
318 block_stmt
(A
) ::= T_BLOCK number
(B
) T_TAG_CLOSE body
(C
) T_TAG_OPEN T_CUSTOM_END
(Z
) number T_TAG_CLOSE.
{
319 if
(Z
!= "endblock") {
320 $this->Error
("Unexpected ".Z.
", expecting endblock");
322 A
= array
('operation' => 'block', 'name' => B
, 'body' => C
);
326 filter_stmt
(A
) ::= T_FILTER filtered_var
(B
) T_TAG_CLOSE body
(X
) T_TAG_OPEN T_CUSTOM_END
(Z
) T_TAG_CLOSE.
{
327 if
(Z
!= "endfilter") {
328 $this->Error
("Unexpected ".Z.
", expecting endfilter");
330 A
= array
('operation' => 'filter', 'functions' => B
, 'body' => X
);
334 regroup
(A
) ::= T_REGROUP filtered_var
(B
) T_BY varname
(C
) T_AS varname
(X
).
{ A
=array
('operation' => 'regroup', 'array' => B
, 'row' => C
, 'as' => X
); }
336 /* variables with filters */
337 filtered_var
(A
) ::= filtered_var
(B
) T_PIPE varname_args
(C
).
{ A
= B
; A
[] = C
; }
338 filtered_var
(A
) ::= varname_args
(B
).
{ A
= array
(B
); }
340 varname_args
(A
) ::= varname
(B
) T_COLON var_or_string
(X
) .
{ A
= array
(B
, 'args'=>array
(X
)); }
341 varname_args
(A
) ::= varname
(B
).
{ A
= B
; }
343 /* List of variables */
344 params
(A
) ::= params
(B
) var_or_string
(C
).
{ A
= B
; A
[] = C
; }
345 params
(A
) ::= params
(B
) T_COMMA var_or_string
(C
).
{ A
= B
; A
[] = C
; }
346 params
(A
) ::= var_or_string
(B
).
{ A
= array
(B
); }
349 /* variable or string (used on params) */
350 var_or_string
(A
) ::= varname
(B
).
{ A
= array
('var' => B
); }
351 var_or_string
(A
) ::= number
(B
).
{ A
= array
('number' => B
); }
352 var_or_string
(A
) ::= T_TRUE|T_FALSE
(B
).
{ A
= trim
(@B
); }
353 var_or_string
(A
) ::= string(B
).
{ A
= array
('string' => B
); }
355 /* filtered variables */
356 fvar_or_string
(A
) ::= filtered_var
(B
).
{ A
= array
('var_filter' => B
); }
357 fvar_or_string
(A
) ::= number
(B
).
{ A
= array
('number' => B
); }
358 fvar_or_string
(A
) ::= T_TRUE|T_FALSE
(B
).
{ A
= trim
(@B
); }
359 fvar_or_string
(A
) ::= string(B
).
{ A
= array
('string' => B
); }
362 string(A
) ::= T_STRING
(B
).
{ A
= B
; }
363 string(A
) ::= T_INTL T_STRING
(B
) T_RPARENT.
{ A
= B
; }
366 expr
(A
) ::= T_NOT expr
(B
).
{ A
= array
('op_expr' => 'not', B
); }
367 expr
(A
) ::= expr
(B
) T_AND
(X
) expr
(C
).
{ A
= array
('op_expr' => @X
, B
, C
); }
368 expr
(A
) ::= expr
(B
) T_OR
(X
) expr
(C
).
{ A
= array
('op_expr' => @X
, B
, C
); }
369 expr
(A
) ::= expr
(B
) T_PLUS|T_MINUS|T_CONCAT
(X
) expr
(C
).
{ A
= array
('op_expr' => @X
, B
, C
); }
370 expr
(A
) ::= expr
(B
) T_EQ|T_NE|T_GT|T_GE|T_LT|T_LE|T_IN
(X
) expr
(C
).
{ A
= array
('op_expr' => trim
(@X
), B
, C
); }
371 expr
(A
) ::= expr
(B
) T_TIMES|T_DIV|T_MOD
(X
) expr
(C
).
{ A
= array
('op_expr' => @X
, B
, C
); }
372 expr
(A
) ::= expr
(B
) T_BITWISE|T_PIPE
(X
) expr
(C
).
{ A
= array
('op_expr' => 'expr', array
('op_expr' => @X
, B
, C
)); }
373 expr
(A
) ::= T_LPARENT expr
(B
) T_RPARENT.
{ A
= array
('op_expr' => 'expr', B
); }
374 expr
(A
) ::= fvar_or_string
(B
).
{ A
= B
; }
378 varname
(A
) ::= varpart
(B
).
{ A
= current
($this->compiler
->generate_variable_name
(B
, false
)); }
379 varpart
(A
) ::= varname
(B
) T_OBJ|T_DOT T_ALPHA|T_CUSTOM_TAG|T_CUSTOM_BLOCK
(C
).
{
380 if
(!is_array
(B
)) { A
= array
(B
); }
381 else
{ A
= B
; } A
[]=array
('object' => C
);
383 varpart
(A
) ::= varname
(B
) T_CLASS T_ALPHA|T_CUSTOM_TAG|T_CUSTOM_BLOCK
(C
).
{
384 if
(!is_array
(B
)) { A
= array
(B
); }
385 else
{ A
= B
; } A
[]=array
('class' => '$'.C
);
387 varpart
(A
) ::= varname
(B
) T_BRACKETS_OPEN var_or_string
(C
) T_BRACKETS_CLOSE.
{
388 if
(!is_array
(B
)) { A
= array
(B
); }
389 else
{ A
= B
; } A
[]=C
;
391 varpart
(A
) ::= T_ALPHA
(B
).
{ A
= B
; }
392 /* T_BLOCK|T_CUSTOM|T_CUSTOM_BLOCK are also T_ALPHA */
393 varpart
(A
) ::= T_BLOCK|T_CUSTOM_TAG|T_CUSTOM_BLOCK
(B
).
{ A
= B
; }
395 range
(A
) ::= numvar
(B
) T_DOTDOT numvar
(C
).
{ A
= array
(B
, C
); }
397 numvar
(A
) ::= number
(B
).
{ A
= B
; }
398 numvar
(A
) ::= varname
(B
).
{ A
= array
('var' => B
); }
400 number
(A
) ::= T_NUMERIC
(B
).
{ A
= B
; }
401 number
(A
) ::= T_MINUS T_NUMERIC
(B
).
{ A
= -1 * (B
); }