- Added fixes to AST
[haanga.git] / lib / Haanga / AST.php
blob37429604b00f596058a46eeb010549ac506e141a
1 <?php
2 /*
3 +---------------------------------------------------------------------------------+
4 | Copyright (c) 2010 Haanga |
5 +---------------------------------------------------------------------------------+
6 | Redistribution and use in source and binary forms, with or without |
7 | modification, are permitted provided that the following conditions are met: |
8 | 1. Redistributions of source code must retain the above copyright |
9 | notice, this list of conditions and the following disclaimer. |
10 | |
11 | 2. Redistributions in binary form must reproduce the above copyright |
12 | notice, this list of conditions and the following disclaimer in the |
13 | documentation and/or other materials provided with the distribution. |
14 | |
15 | 3. All advertising materials mentioning features or use of this software |
16 | must display the following acknowledgement: |
17 | This product includes software developed by César D. Rodas. |
18 | |
19 | 4. Neither the name of the César D. Rodas nor the |
20 | names of its contributors may be used to endorse or promote products |
21 | derived from this software without specific prior written permission. |
22 | |
23 | THIS SOFTWARE IS PROVIDED BY CÉSAR D. RODAS ''AS IS'' AND ANY |
24 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
25 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
26 | DISCLAIMED. IN NO EVENT SHALL CÉSAR D. RODAS BE LIABLE FOR ANY |
27 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
28 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
29 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
30 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
31 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
32 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE |
33 +---------------------------------------------------------------------------------+
34 | Authors: César Rodas <crodas@php.net> |
35 +---------------------------------------------------------------------------------+
38 /**
39 * Simple AST (abstract syntax tree) helper class. This
40 * helps to generate array structure that is then translated by
41 * the Haanga_Generator class.
44 class Haanga_AST
46 public $stack = array();
47 public $current = array();
48 public $doesPrint = FALSE;
51 // getLast() {{{
52 /**
53 * Return a refernce to the last element
54 * of the AST stack.
56 * @return array
58 function & getLast()
60 $f = array();
61 if (count($this->stack) == 0) {
62 return $f;
64 return $this->stack[count($this->stack)-1];
66 // }}}
69 static protected function check_type($obj, $type)
71 if (is_string($obj)) {
72 return FALSE;
74 if (is_object($obj)) {
75 $obj = $obj->getArray();
77 return isset($obj[$type]);
80 public static function is_str($arr)
82 return self::check_type($arr, 'string');
85 public static function is_var($arr)
87 return self::check_type($arr, 'var');
90 public static function is_exec($arr)
92 return self::check_type($arr, 'exec');
95 public static function is_expr($arr)
97 return self::check_type($arr, 'op_expr');
101 function str($string)
103 return array("string" => $string);
106 function num($number)
108 return array("number" => $number);
111 function stack_size()
113 return count($this->stack);
116 function append_ast(Haanga_AST $obj)
118 $this->end();
119 $obj->end();
120 $this->stack = array_merge($this->stack, $obj->stack);
122 return $this;
125 function constant($str)
127 return array('constant' => $str);
130 function comment($str)
132 $this->stack[] = array("op" => "comment", 'comment' => $str);
134 return $this;
137 function declare_function($name)
139 $this->stack[] = array('op' => 'function', 'name' => $name);
141 return $this;
144 function do_return($name)
146 $this->getValue($name, $expr);
147 $this->stack[] = array('op' => 'return', $expr);
149 return $this;
152 function do_if($expr)
154 $this->getValue($expr, $vexpr);
155 $this->stack[] = array('op' => 'if', 'expr' => $vexpr);
157 return $this;
160 function do_else()
162 $this->stack[] = array('op' => 'else');
164 return $this;
167 function do_endif()
169 $this->stack[] = array('op' => 'end_if');
171 return $this;
174 function do_endfunction()
176 $this->stack[] = array('op' => 'end_function');
178 return $this;
181 function v()
183 $var = array();
184 foreach (func_get_args() as $id => $def) {
185 if ($id == 0) {
186 $var[$id] = $def;
187 } else {
188 $this->getValue($def, $value);
189 $var[$id] = $value;
192 $this->current = array('var' => $var);
194 return $this;
197 final function __get($property)
199 $property = strtolower($property);
200 if (isset($this->current[$property])) {
201 return $this->current[$property];
203 return FALSE;
206 static function fromArrayGetAST($obj)
208 $class = __CLASS__;
209 if ($obj InstanceOf $class) {
210 return $obj;
212 foreach (array('op_expr', 'expr_cond', 'exec', 'var', 'string', 'number', 'constant') as $type) {
213 if (isset($obj[$type])) {
214 $nobj = new $class;
215 $nobj->stack[] = $obj;
216 return $nobj;
221 static function getValue($obj, &$value, $get_all=FALSE)
223 $class = __CLASS__;
225 if ($obj InstanceOf $class) {
226 $value = $obj->getArray($get_all);
227 } else if (is_string($obj)) {
228 $value = self::str($obj);
229 } else if (is_numeric($obj) or $obj === 0) {
230 $value = self::num($obj);
231 } else if ($obj === FALSE) {
232 $value = array('expr' => FALSE);
233 } else if ($obj === TRUE) {
234 $value = array('expr' => TRUE);
235 } else if (is_array($obj)) {
236 foreach (array('exec', 'var', 'string', 'number', 'constant') as $type) {
237 if (isset($obj[$type])) {
238 $value = $obj;
239 return;
242 $h = hcode()->arr();
243 $first = 0;
244 foreach($obj as $key => $value) {
245 if ($key === $first) {
246 $key = NULL;
247 $first++;
249 $h->element($key, $value);
251 $value = $h->getArray();
252 } else if ($obj === NULL) {
253 $value = array();
254 } else {
255 var_Dump($obj);
256 throw new Exception("Imposible to get the value of the object");
260 function getArray($get_all=FALSE)
262 $this->end();
263 if ($get_all) {
264 return $this->stack;
266 return isset($this->stack[0]) ? $this->stack[0] : NULL;
269 function do_foreach($array, $value, $key, Haanga_AST $body)
271 foreach (array('array', 'value', 'key') as $var) {
272 if ($$var === NULL) {
273 continue;
275 $var1 = & $$var;
276 if (is_string($var1)) {
277 $var1 = hvar($var1);
279 if (is_object($var1)) {
280 $var1 = $var1->getArray();
282 $var1 = $var1['var'];
284 $def = array('op' => 'foreach', 'array' => $array, 'value' => $value);
285 if ($key) {
286 $def['key'] = $key;
288 $this->stack[] = $def;
289 $this->stack = array_merge($this->stack, $body->getArray(TRUE));
290 $this->stack[] = array('op' => 'end_foreach');
292 return $this;
295 function do_echo($stmt)
297 $this->getValue($stmt, $value);
298 $this->stack[] = array('op' => 'print', $value);
299 return $this;
302 function do_exec()
304 $params = func_get_args();
305 $exec = call_user_func_array('hexec', $params);
306 $this->stack[] = array('op' => 'expr', $exec->getArray());
308 return $this;
311 function exec($function)
313 $this->current = array('exec' => $function, 'args' => array());
314 foreach (func_get_args() as $id => $param) {
315 if ($id > 0) {
316 $this->param($param);
319 return $this;
322 function expr($operation, $term1, $term2=NULL)
324 $this->getValue($term1, $value1);
325 if ($term2 !== NULL) {
326 $this->getValue($term2, $value2);
327 } else {
328 $value2 = NULL;
330 $this->current = array('op_expr' => $operation, $value1, $value2);
332 return $this;
335 function expr_cond($expr, $if_true, $if_false)
337 $this->getValue($expr, $vExpr);
338 $this->getValue($if_true, $vIfTrue);
339 $this->getValue($if_false, $vIfFalse);
341 $this->current = array('expr_cond' => $vExpr, 'true' => $vIfTrue, 'false' => $vIfFalse);
343 return $this;
347 function arr()
349 $this->current = array('array' => array());
351 return $this;
354 function element($key=NULL, $value)
356 $last = & $this->current;
358 if (!isset($last['array'])) {
359 throw new Exception("Invalid call to element()");
362 $this->getValue($value, $val);
363 if ($key !== NULL) {
364 $this->getValue($key, $kval);
365 $val = array('key' => array($kval, $val));
367 $last['array'][] = $val;
370 function decl($name, $value)
372 if (is_string($name)) {
373 $name = hvar($name);
375 $this->getValue($name, $name);
376 $array = array('op' => 'declare', 'name' => $name['var']);
377 foreach (func_get_args() as $id => $value) {
378 if ($id != 0) {
379 $this->getValue($value, $stmt);
380 $array[] = $stmt;
383 $this->stack[] = $array;
384 return $this;
387 function append($name, $value)
389 if (is_string($name)) {
390 $name = hvar($name);
392 $this->getValue($value, $stmt);
393 $this->getValue($name, $name);
394 $this->stack[] = array('op' => 'append_var', 'name' => $name['var'], $stmt);
395 return $this;
398 function param($param)
400 $last = & $this->current;
402 if (!isset($last['exec'])) {
403 throw new Exception("Invalid call to param()");
406 $this->getValue($param, $value);
407 $last['args'][] = $value;
409 return $this;
412 function end()
414 if (count($this->current) > 0) {
415 $this->stack[] = $this->current;
416 $this->current = array();
419 return $this;
423 function hcode()
425 return new Haanga_AST;
428 function hexpr($term1, $op='expr', $term2=NULL, $op2=NULL)
430 $code = hcode();
431 switch ($op2) {
432 case '+':
433 case '-':
434 case '/':
435 case '*':
436 case '%':
437 case '||':
438 case '&&':
439 case '<':
440 case '>':
441 case '<=':
442 case '>=':
443 case '==':
444 case '!=':
445 /* call recursive to resolve term2 */
446 $args = func_get_args();
447 $term2 = call_user_func_array('hexpr', array_slice($args, 2));
448 break;
450 return $code->expr($op, $term1, $term2);
453 function hexpr_cond($expr, $if_true, $if_false)
455 $code = hcode();
456 $code->expr_cond($expr, $if_true, $if_false);
458 return $code;
461 function hexec()
463 $code = hcode();
464 $args = func_get_args();
465 return call_user_func_array(array($code, 'exec'), $args);
468 function hconst($str)
470 return Haanga_AST::Constant($str);
473 // hvar() {{{
475 * Create the representation of a variable
477 * @return Haanga_AST
479 function hvar()
481 $args = func_get_args();
482 return hvar_ex($args);
485 function hvar_ex($args)
487 $code = hcode();
488 return call_user_func_array(array($code, 'v'), $args);
490 // }}}
493 * Local variables:
494 * tab-width: 4
495 * c-basic-offset: 4
496 * End:
497 * vim600: sw=4 ts=4 fdm=marker
498 * vim<600: sw=4 ts=4