Experimental: Added references to static methods/properties of objects (Requested...
[haanga.git] / lib / Haanga / Compiler / Parser.y
blobc7449e61891b0065dca37ed0051d7630c0e4c94d
1 %name Haanga_
2 %include {
3 /*
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. |
11 | |
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. |
15 | |
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. |
19 | |
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. |
23 | |
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 }
41 %include_class {
42 protected $lex;
43 protected $file;
45 function __construct($lex, $file='')
47 $this->lex = $lex;
48 $this->file = $file;
51 function Error($text)
53 throw new Haanga_Compiler_Exception($text.' in '.$this->file.':'.$this->lex->getLine());
58 %parse_accept {
61 %right T_TAG_OPEN.
62 %right T_NOT.
63 %left T_AND.
64 %left T_OR.
65 %nonassoc T_EQ T_NE.
66 %nonassoc T_GT T_GE T_LT T_LE.
67 %nonassoc T_IN.
68 %left T_PLUS T_MINUS T_CONCAT.
69 %left T_TIMES T_DIV T_MOD.
70 %left T_PIPE T_BITWISE.
72 %syntax_error {
73 $expect = array();
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. {
110 B = strtolower(B);
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);
120 /* Statement */
122 /* CUSTOM TAGS */
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);
136 /* tags as blocks */
137 custom_tag(A) ::= T_CUSTOM_BLOCK(B) T_TAG_CLOSE body(X) T_TAG_OPEN T_CUSTOM_END(C) T_TAG_CLOSE. {
138 if ('end'.B != C) {
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. {
144 if ('end'.B != C) {
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");
173 require_once B;
176 /* FOR loop */
178 for_def(A) ::= T_FOR varname(B) T_IN filtered_var(C) T_TAG_CLOSE . {
179 A = array('operation' => 'loop', 'variable' => B, 'index' => NULL, 'array' => C);
182 for_def(A) ::= T_FOR varname(I) T_COMMA varname(B) T_IN filtered_var(C) T_TAG_CLOSE . {
183 A = array('operation' => 'loop', 'variable' => B, 'index' => I, 'array' => C);
187 for_stmt(A) ::= for_def(B) body(D) T_TAG_OPEN T_CUSTOM_END(Z) T_TAG_CLOSE. {
188 if (Z != "endfor") {
189 $this->Error("Unexpected ".Z.", expecting endfor");
191 A = B;
192 A['body'] = D;
195 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. {
196 if (Z != "endfor") {
197 $this->Error("Unexpected ".Z.", expecting endfor");
199 A = array('operation' => 'loop', 'variable' => B, 'range' => X, 'body' => E, 'variable' => B, 'step' => 1);
202 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. {
203 if (Z != "endfor") {
204 $this->Error("Unexpected ".Z.", expecting endfor");
206 A = array('operation' => 'loop', 'variable' => B, 'range' => X, 'body' => E, 'variable' => B, 'step' => S);
209 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. {
210 if (Z != "endfor") {
211 $this->Error("Unexpected ".Z.", expecting endfor");
213 A = B;
214 A['body'] = D;
215 A['empty'] = E;
217 /* IF */
218 if_stmt(A) ::= T_IF expr(B) T_TAG_CLOSE body(X) T_TAG_OPEN T_CUSTOM_END(Z) T_TAG_CLOSE. {
219 if (Z != "endif") {
220 $this->Error("Unexpected ".Z.", expecting endif");
222 A = array('operation' => 'if', 'expr' => B, 'body' => X);
224 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. {
225 if (Z != "endif") {
226 $this->Error("Unexpected ".Z.", expecting endif");
228 A = array('operation' => 'if', 'expr' => B, 'body' => X, 'else' => Y);
231 /* ifchanged */
232 ifchanged_stmt(A) ::= T_IFCHANGED T_TAG_CLOSE body(B) T_TAG_OPEN T_CUSTOM_END(Z) T_TAG_CLOSE. {
233 if (Z != "endifchanged") {
234 $this->Error("Unexpected ".Z.", expecting endifchanged");
236 A = array('operation' => 'ifchanged', 'body' => B);
239 ifchanged_stmt(A) ::= T_IFCHANGED params(X) T_TAG_CLOSE body(B) T_TAG_OPEN T_CUSTOM_END(Z) T_TAG_CLOSE. {
240 if (Z != "endifchanged") {
241 $this->Error("Unexpected ".Z.", expecting endifchanged");
243 A = array('operation' => 'ifchanged', 'body' => B, 'check' => X);
245 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. {
246 if (Z != "endifchanged") {
247 $this->Error("Unexpected ".Z.", expecting endifchanged");
249 A = array('operation' => 'ifchanged', 'body' => B, 'else' => C);
252 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. {
253 if (Z != "endifchanged") {
254 $this->Error("Unexpected ".Z.", expecting endifchanged");
256 A = array('operation' => 'ifchanged', 'body' => B, 'check' => X, 'else' => C);
259 /* ifequal */
260 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. {
261 if (Z != "endifequal") {
262 $this->Error("Unexpected ".Z.", expecting endifequal");
264 A = array('operation' => 'ifequal', 'cmp' => '==', 1 => B, 2 => C, 'body' => X);
266 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. {
267 if (Z != "endifequal") {
268 $this->Error("Unexpected ".Z.", expecting endifequal");
270 A = array('operation' => 'ifequal', 'cmp' => '==', 1 => B, 2 => C, 'body' => X, 'else' => Y);
272 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. {
273 if (Z != "endifnotequal") {
274 $this->Error("Unexpected ".Z.", expecting endifnotequal");
276 A = array('operation' => 'ifequal', 'cmp' => '!=', 1 => B, 2 => C, 'body' => X);
278 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. {
279 if (Z != "endifnotequal") {
280 $this->Error("Unexpected ".Z.", expecting endifnotequal");
282 A = array('operation' => 'ifequal', 'cmp' => '!=', 1 => B, 2 => C, 'body' => X, 'else' => Y);
285 /* block stmt */
286 block_stmt(A) ::= T_BLOCK varname(B) T_TAG_CLOSE body(C) T_TAG_OPEN T_CUSTOM_END(Z) T_TAG_CLOSE. {
287 if (Z != "endblock") {
288 $this->Error("Unexpected ".Z.", expecting endblock");
290 A = array('operation' => 'block', 'name' => B, 'body' => C);
293 block_stmt(A) ::= T_BLOCK varname(B) T_TAG_CLOSE body(C) T_TAG_OPEN T_CUSTOM_END(Z) varname T_TAG_CLOSE. {
294 if (Z != "endblock") {
295 $this->Error("Unexpected ".Z.", expecting endblock");
297 A = array('operation' => 'block', 'name' => B, 'body' => C);
300 block_stmt(A) ::= T_BLOCK number(B) T_TAG_CLOSE body(C) T_TAG_OPEN T_CUSTOM_END(Z) T_TAG_CLOSE. {
301 if (Z != "endblock") {
302 $this->Error("Unexpected ".Z.", expecting endblock");
304 A = array('operation' => 'block', 'name' => B, 'body' => C);
307 block_stmt(A) ::= T_BLOCK number(B) T_TAG_CLOSE body(C) T_TAG_OPEN T_CUSTOM_END(Z) number T_TAG_CLOSE. {
308 if (Z != "endblock") {
309 $this->Error("Unexpected ".Z.", expecting endblock");
311 A = array('operation' => 'block', 'name' => B, 'body' => C);
314 /* filter stmt */
315 filter_stmt(A) ::= T_FILTER filtered_var(B) T_TAG_CLOSE body(X) T_TAG_OPEN T_CUSTOM_END(Z) T_TAG_CLOSE. {
316 if (Z != "endfilter") {
317 $this->Error("Unexpected ".Z.", expecting endfilter");
319 A = array('operation' => 'filter', 'functions' => B, 'body' => X);
322 /* regroup stmt */
323 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); }
325 /* variables with filters */
326 filtered_var(A) ::= filtered_var(B) T_PIPE varname_args(C). { A = B; A[] = C; }
327 filtered_var(A) ::= varname_args(B). { A = array(B); }
329 varname_args(A) ::= varname(B) T_COLON var_or_string(X) . { A = array(B, 'args'=>array(X)); }
330 varname_args(A) ::= varname(B). { A = B; }
332 /* List of variables */
333 params(A) ::= params(B) var_or_string(C). { A = B; A[] = C; }
334 params(A) ::= params(B) T_COMMA var_or_string(C). { A = B; A[] = C; }
335 params(A) ::= var_or_string(B). { A = array(B); }
338 /* variable or string (used on params) */
339 var_or_string(A) ::= varname(B). { A = array('var' => B); }
340 var_or_string(A) ::= number(B). { A = array('number' => B); }
341 var_or_string(A) ::= T_TRUE|T_FALSE(B). { A = trim(@B); }
342 var_or_string(A) ::= string(B). { A = array('string' => B); }
344 /* filtered variables */
345 fvar_or_string(A) ::= filtered_var(B). { A = array('var_filter' => B); }
346 fvar_or_string(A) ::= number(B). { A = array('number' => B); }
347 fvar_or_string(A) ::= T_TRUE|T_FALSE(B). { A = trim(@B); }
348 fvar_or_string(A) ::= string(B). { A = array('string' => B); }
350 /* */
351 string(A) ::= T_STRING(B). { A = B; }
352 string(A) ::= T_INTL T_STRING(B) T_RPARENT. { A = B; }
354 /* expr */
355 expr(A) ::= T_NOT expr(B). { A = array('op_expr' => 'not', B); }
356 expr(A) ::= expr(B) T_AND(X) expr(C). { A = array('op_expr' => @X, B, C); }
357 expr(A) ::= expr(B) T_OR(X) expr(C). { A = array('op_expr' => @X, B, C); }
358 expr(A) ::= expr(B) T_PLUS|T_MINUS|T_CONCAT(X) expr(C). { A = array('op_expr' => @X, B, C); }
359 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); }
360 expr(A) ::= expr(B) T_TIMES|T_DIV|T_MOD(X) expr(C). { A = array('op_expr' => @X, B, C); }
361 expr(A) ::= expr(B) T_BITWISE|T_PIPE(X) expr(C). { A = array('op_expr' => 'expr', array('op_expr' => @X, B, C)); }
362 expr(A) ::= T_LPARENT expr(B) T_RPARENT. { A = array('op_expr' => 'expr', B); }
363 expr(A) ::= fvar_or_string(B). { A = B; }
365 /* Variable name */
366 varname(A) ::= varname(B) T_OBJ|T_DOT T_ALPHA|T_CUSTOM_TAG|T_CUSTOM_BLOCK(C). {
367 if (!is_array(B)) { A = array(B); }
368 else { A = B; } A[]=array('object' => C);
370 varname(A) ::= varname(B) T_CLASS T_ALPHA|T_CUSTOM_TAG|T_CUSTOM_BLOCK(C). {
371 if (!is_array(B)) { A = array(B); }
372 else { A = B; } A[]=array('class' => C);
374 varname(A) ::= varname(B) T_BRACKETS_OPEN var_or_string(C) T_BRACKETS_CLOSE. {
375 if (!is_array(B)) { A = array(B); }
376 else { A = B; } A[]=C;
378 varname(A) ::= T_ALPHA(B). { A = B; }
379 /* T_BLOCK|T_CUSTOM|T_CUSTOM_BLOCK are also T_ALPHA */
380 varname(A) ::= T_BLOCK|T_CUSTOM_TAG|T_CUSTOM_BLOCK(B). { A = B; }
382 range(A) ::= numvar(B) T_DOTDOT numvar(C). { A = array(B, C); }
384 numvar(A) ::= number(B). { A = B; }
385 numvar(A) ::= varname(B). { A = array('var' => B); }
387 number(A) ::= T_NUMERIC(B). { A = B; }
388 number(A) ::= T_MINUS T_NUMERIC(B). { A = -1 * (B); }