- Improved variables support
[haanga.git] / lib / Haanga / Compiler / Parser.y
blob64c9a2548a8d43b58d46bb88843bb67ad97dba1f
1 %name Haanga_
2 %include {
3 /*
4 +---------------------------------------------------------------------------------+
5 | Copyright (c) 2010 Haanga |
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 {
45 %parse_accept {
48 %right T_OPEN_TAG.
49 %left T_AND.
50 %left T_OR.
51 %nonassoc T_EQ T_NE.
52 %nonassoc T_GT T_GE T_LT T_LE.
53 %nonassoc T_IN.
54 %left T_PLUS T_MINUS.
55 %left T_TIMES T_DIV T_MOD.
57 %syntax_error {
58 $expect = array();
59 foreach ($this->yy_get_expected_tokens($yymajor) as $token) {
60 $expect[] = self::$yyTokenName[$token];
62 throw new Exception('Unexpected ' . $this->tokenName($yymajor) . '(' . $TOKEN. '), expected one of: ' . implode(',', $expect));
66 start ::= body(B). { $this->body = B; }
68 body(A) ::= body(B) code(C). { A=B; A[] = C; }
69 body(A) ::= . { A = array(); }
71 /* List of statements */
72 code(A) ::= T_OPEN_TAG stmts(B). { A = B; }
73 code(A) ::= T_HTML(B). { A = array('operation' => 'html', 'html' => B); }
74 code(A) ::= T_COMMENT_OPEN T_COMMENT(B). { B=rtrim(B); A = array('operation' => 'comment', 'comment' => substr(B, 0, strlen(B)-2)); }
75 code(A) ::= T_PRINT_OPEN filtered_var(B) T_PRINT_CLOSE. { A = array('operation' => 'print_var', 'variable' => B); }
77 stmts(A) ::= T_EXTENDS var_or_string(B) T_CLOSE_TAG. { A = array('operation' => 'base', B); }
78 stmts(A) ::= stmt(B) T_CLOSE_TAG. { A = B; }
79 stmts(A) ::= for_stmt(B). { A = B; }
80 stmts(A) ::= ifchanged_stmt(B). { A = B; }
81 stmts(A) ::= block_stmt(B). { A = B; }
82 stmts(A) ::= filter_stmt(B). { A = B; }
83 stmts(A) ::= if_stmt(B). { A = B; }
84 stmts(A) ::= T_INCLUDE var_or_string(B) T_CLOSE_TAG. { A = array('operation' => 'include', B); }
85 stmts(A) ::= custom_tag(B). { A = B; }
86 stmts(A) ::= alias(B). { A = B; }
87 stmts(A) ::= ifequal(B). { A = B; }
88 stmts(A) ::= T_AUTOESCAPE T_OFF|T_ON(B) T_CLOSE_TAG body(X) T_OPEN_TAG T_END_AUTOESCAPE T_CLOSE_TAG. { A = array('operation' => 'autoescape', 'value' => strtolower(@B), 'body' => X); }
90 /* Statement */
92 /* CUSTOM TAGS */
93 custom_tag(A) ::= T_CUSTOM_TAG(B) T_CLOSE_TAG. { A = array('operation' => 'custom_tag', 'name' => B, 'list'=>array()); }
94 custom_tag(A) ::= T_CUSTOM_TAG(B) T_AS varname(C) T_CLOSE_TAG. { A = array('operation' => 'custom_tag', 'name' => B, 'as' => C, 'list'=>array()); }
95 custom_tag(A) ::= T_CUSTOM_TAG(B) var_list(X) T_CLOSE_TAG. { A = array('operation' => 'custom_tag', 'name' => B, 'list' => X); }
96 custom_tag(A) ::= T_CUSTOM_TAG(B) var_list(X) T_AS varname(C) T_CLOSE_TAG. { A = array('operation' => 'custom_tag', 'name' => B, 'as' => C, 'list' => X); }
97 /* tags as blocks */
98 custom_tag(A) ::= T_CUSTOM_BLOCK(B) T_CLOSE_TAG body(X) T_OPEN_TAG T_CUSTOM_END(C) T_CLOSE_TAG. { if ('end'.B != C) { throw new Exception("Unexpected ".C); } A = array('operation' => 'custom_tag', 'name' => B, 'body' => X, 'list' => array());}
100 /* variable alias */
101 alias(A) ::= T_WITH varname(B) T_AS varname(C) T_CLOSE_TAG body(X) T_OPEN_TAG T_ENDWITH T_CLOSE_TAG. { A = array('operation' => 'alias', 'var' => B, 'as' => C, 'body' => X); }
103 /* Simple statements (don't require a end_tag or a body ) */
104 stmt(A) ::= regroup(B). { A = B; }
105 stmt ::= T_LOAD string(B). {
106 if (!is_file(B)) {
107 throw new Haanga_Compiler_Exception(B." is not a valid file");
109 require_once B;
112 /* FOR loop */
113 for_def(A) ::= T_FOR varname(B) T_IN filtered_var(C) T_CLOSE_TAG . {
114 $this->compiler->set_context(B, array());
115 A = array('operation' => 'loop', 'variable' => B, 'index' => NULL, 'array' => C);
118 for_def(A) ::= T_FOR varname(I) T_COMMA varname(B) T_IN filtered_var(C) T_CLOSE_TAG . {
119 $this->compiler->set_context(B, array());
120 A = array('operation' => 'loop', 'variable' => B, 'index' => I, 'array' => C);
124 for_stmt(A) ::= for_def(B) body(D) T_OPEN_TAG T_CLOSEFOR T_CLOSE_TAG. {
125 A = B;
126 A['body'] = D;
129 for_stmt(A) ::= for_def(B) body(D) T_OPEN_TAG T_EMPTY T_CLOSE_TAG body(E) T_OPEN_TAG T_CLOSEFOR T_CLOSE_TAG. {
130 A = B;
131 A['body'] = D;
132 A['empty'] = E;
134 /* IF */
135 if_stmt(A) ::= T_IF expr(B) T_CLOSE_TAG body(X) T_OPEN_TAG T_ENDIF T_CLOSE_TAG. { A = array('operation' => 'if', 'expr' => B, 'body' => X); }
136 if_stmt(A) ::= T_IF expr(B) T_CLOSE_TAG body(X) T_OPEN_TAG T_ELSE T_CLOSE_TAG body(Y) T_OPEN_TAG T_ENDIF T_CLOSE_TAG. { A = array('operation' => 'if', 'expr' => B, 'body' => X, 'else' => Y); }
138 /* ifchanged */
139 ifchanged_stmt(A) ::= T_IFCHANGED T_CLOSE_TAG body(B) T_OPEN_TAG T_ENDIFCHANGED T_CLOSE_TAG. {
140 A = array('operation' => 'ifchanged', 'body' => B);
143 ifchanged_stmt(A) ::= T_IFCHANGED var_list(X) T_CLOSE_TAG body(B) T_OPEN_TAG T_ENDIFCHANGED T_CLOSE_TAG. {
144 A = array('operation' => 'ifchanged', 'body' => B, 'check' => X);
146 ifchanged_stmt(A) ::= T_IFCHANGED T_CLOSE_TAG body(B) T_OPEN_TAG T_ELSE T_CLOSE_TAG body(C) T_OPEN_TAG T_ENDIFCHANGED T_CLOSE_TAG. {
147 A = array('operation' => 'ifchanged', 'body' => B, 'else' => C);
150 ifchanged_stmt(A) ::= T_IFCHANGED var_list(X) T_CLOSE_TAG body(B) T_OPEN_TAG T_ELSE T_CLOSE_TAG body(C) T_OPEN_TAG T_ENDIFCHANGED T_CLOSE_TAG. {
151 A = array('operation' => 'ifchanged', 'body' => B, 'check' => X, 'else' => C);
154 /* ifequal */
155 ifequal(A) ::= T_IFEQUAL fvar_or_string(B) fvar_or_string(C) T_CLOSE_TAG body(X) T_OPEN_TAG T_END_IFEQUAL T_CLOSE_TAG. { A = array('operation' => 'ifequal', 'cmp' => '==', 1 => B, 2 => C, 'body' => X); }
156 ifequal(A) ::= T_IFEQUAL fvar_or_string(B) fvar_or_string(C) T_CLOSE_TAG body(X) T_OPEN_TAG T_ELSE T_CLOSE_TAG body(Y) T_OPEN_TAG T_END_IFEQUAL T_CLOSE_TAG. { A = array('operation' => 'ifequal', 'cmp' => '==', 1 => B, 2 => C, 'body' => X, 'else' => Y); }
157 ifequal(A) ::= T_IFNOTEQUAL fvar_or_string(B) fvar_or_string(C) T_CLOSE_TAG body(X) T_OPEN_TAG T_END_IFNOTEQUAL T_CLOSE_TAG. { A = array('operation' => 'ifequal', 'cmp' => '!=', 1 => B, 2 => C, 'body' => X); }
158 ifequal(A) ::= T_IFNOTEQUAL fvar_or_string(B) fvar_or_string(C) T_CLOSE_TAG body(X) T_OPEN_TAG T_ELSE T_CLOSE_TAG body(Y) T_OPEN_TAG T_END_IFNOTEQUAL T_CLOSE_TAG. { A = array('operation' => 'ifequal', 'cmp' => '!=', 1 => B, 2 => C, 'body' => X, 'else' => Y); }
161 /* block stmt */
162 block_stmt(A) ::= T_BLOCK varname(B) T_CLOSE_TAG body(C) T_OPEN_TAG T_END_BLOCK T_CLOSE_TAG. { A = array('operation' => 'block', 'name' => B, 'body' => C); }
164 block_stmt(A) ::= T_BLOCK varname(B) T_CLOSE_TAG body(C) T_OPEN_TAG T_END_BLOCK varname T_CLOSE_TAG. { A = array('operation' => 'block', 'name' => B, 'body' => C); }
166 block_stmt(A) ::= T_BLOCK T_NUMERIC(B) T_CLOSE_TAG body(C) T_OPEN_TAG T_END_BLOCK T_CLOSE_TAG. { A = array('operation' => 'block', 'name' => B, 'body' => C); }
168 block_stmt(A) ::= T_BLOCK T_NUMERIC(B) T_CLOSE_TAG body(C) T_OPEN_TAG T_END_BLOCK T_NUMERIC T_CLOSE_TAG. { A = array('operation' => 'block', 'name' => B, 'body' => C); }
170 /* filter stmt */
171 filter_stmt(A) ::= T_FILTER filtered_var(B) T_CLOSE_TAG body(X) T_OPEN_TAG T_END_FILTER T_CLOSE_TAG. { A = array('operation' => 'filter', 'functions' => B, 'body' => X); }
173 /* regroup stmt */
174 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); }
176 /* variables with filters */
177 filtered_var(A) ::= filtered_var(B) T_PIPE varname_args(C). { A = B; A[] = C; }
178 filtered_var(A) ::= varname_args(B). { A = array(B); }
180 varname_args(A) ::= varname(B) T_COLON var_or_string(X) . { A = array(B, 'args'=>array(X)); }
181 varname_args(A) ::= varname(B). { A = B; }
183 /* List of variables */
184 var_list(A) ::= var_list(B) var_or_string(C). { A = B; A[] = C; }
185 var_list(A) ::= var_list(B) T_COMMA var_or_string(C). { A = B; A[] = C; }
186 var_list(A) ::= var_or_string(B). { A = array(B); }
189 /* variable or string (used on var_list) */
190 var_or_string(A) ::= varname(B). { A = array('var' => B); }
191 var_or_string(A) ::= T_NUMERIC(B). { A = array('number' => B); }
192 var_or_string(A) ::= string(B). { A = array('string' => B); }
194 fvar_or_string(A) ::= filtered_var(B). { A = array('var_filter' => B); }
195 fvar_or_string(A) ::= T_NUMERIC(B). { A = array('number' => B); }
196 fvar_or_string(A) ::= string(B). { A = array('string' => B); }
198 /* */
199 string(A) ::= T_INTL string(B) T_RPARENT. { A = B; }
200 string(A) ::= T_STRING_SINGLE_INIT T_STRING_SINGLE_END. { A = ""; }
201 string(A) ::= T_STRING_DOUBLE_INIT T_STRING_DOUBLE_END. { A = ""; }
202 string(A) ::= T_STRING_SINGLE_INIT s_content(B) T_STRING_SINGLE_END. { A = B; }
203 string(A) ::= T_STRING_DOUBLE_INIT s_content(B) T_STRING_DOUBLE_END. { A = B; }
204 s_content(A) ::= s_content(B) T_STRING_CONTENT(C). { A = B.C; }
205 s_content(A) ::= T_STRING_CONTENT(B). { A = B; }
207 /* expr */
208 expr(A) ::= expr(B) T_AND(X) expr(C). { A = array('op_expr' => @X, B, C); }
209 expr(A) ::= expr(B) T_OR(X) expr(C). { A = array('op_expr' => @X, B, C); }
210 expr(A) ::= expr(B) T_PLUS|T_MINUS(X) expr(C). { A = array('op_expr' => @X, B, C); }
211 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); }
212 expr(A) ::= expr(B) T_TIMES|T_DIV|T_MOD(X) expr(C). { A = array('op_expr' => @X, B, C); }
213 expr(A) ::= T_LPARENT expr(B) T_RPARENT. { A = array('op_expr' => 'expr', B); }
214 expr(A) ::= fvar_or_string(B). { A = B; }
216 /* Variable name */
217 varname(A) ::= varname(B) T_OBJ T_ALPHA(C). { if (!is_array(B)) { A = array(B); } else { A = B; } A[]=array('object' => C);}
218 varname(A) ::= varname(B) T_DOT T_ALPHA(C). { if (!is_array(B)) { A = array(B); } else { A = B; } A[] = ($this->compiler->var_is_object(A)) ? array('object' => C) : C;}
219 varname(A) ::= varname(B) T_BRACKETS_OPEN var_or_string(C) T_BRACKETS_CLOSE. { if (!is_array(B)) { A = array(B); } else { A = B; } A[]=C;}
220 varname(A) ::= T_ALPHA(B). { A = B; }
221 /* T_CUSTOM|T_CUSTOM_BLOCK are also T_ALPHA */
222 varname(A) ::= T_CUSTOM_TAG|T_CUSTOM_BLOCK(B). { A = B; }