PageRenderTime 30ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/libs/Haanga/AST.php

https://bitbucket.org/jonarano/joneame
PHP | 542 lines | 400 code | 77 blank | 65 comment | 48 complexity | 2d5c6c3c5af14c7a8fb3ca51833c2835 MD5 | raw file
Possible License(s): AGPL-1.0
  1. <?php
  2. /*
  3. +---------------------------------------------------------------------------------+
  4. | Copyright (c) 2010 César Rodas and Menéame Comunicacions S.L. |
  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. +---------------------------------------------------------------------------------+
  36. */
  37. /**
  38. * Simple AST (abstract syntax tree) helper class. This
  39. * helps to generate array structure that is then translated by
  40. * the Haanga_Generator class.
  41. *
  42. */
  43. class Haanga_AST
  44. {
  45. public $stack = array();
  46. public $current = array();
  47. public $doesPrint = FALSE;
  48. // getLast() {{{
  49. /**
  50. * Return a refernce to the last element
  51. * of the AST stack.
  52. *
  53. * @return array
  54. */
  55. function & getLast()
  56. {
  57. $f = array();
  58. if (count($this->stack) == 0) {
  59. return $f;
  60. }
  61. return $this->stack[count($this->stack)-1];
  62. }
  63. // }}}
  64. static protected function check_type($obj, $type)
  65. {
  66. if (is_string($obj)) {
  67. return FALSE;
  68. }
  69. if (is_object($obj)) {
  70. $obj = $obj->getArray();
  71. }
  72. return isset($obj[$type]);
  73. }
  74. public static function is_str($arr)
  75. {
  76. return self::check_type($arr, 'string');
  77. }
  78. public static function is_var($arr)
  79. {
  80. return self::check_type($arr, 'var');
  81. }
  82. public static function is_exec($arr)
  83. {
  84. return self::check_type($arr, 'exec');
  85. }
  86. public static function is_expr($arr)
  87. {
  88. return self::check_type($arr, 'op_expr');
  89. }
  90. public static function str($string)
  91. {
  92. return array("string" => $string);
  93. }
  94. public static function num($number)
  95. {
  96. return array("number" => $number);
  97. }
  98. function stack_size()
  99. {
  100. return count($this->stack);
  101. }
  102. function append_ast(Haanga_AST $obj)
  103. {
  104. $this->end();
  105. $obj->end();
  106. $this->stack = array_merge($this->stack, $obj->stack);
  107. return $this;
  108. }
  109. static function constant($str)
  110. {
  111. return array('constant' => $str);
  112. }
  113. function comment($str)
  114. {
  115. $this->stack[] = array("op" => "comment", 'comment' => $str);
  116. return $this;
  117. }
  118. function declare_function($name)
  119. {
  120. $this->stack[] = array('op' => 'function', 'name' => $name);
  121. return $this;
  122. }
  123. function do_return($name)
  124. {
  125. $this->getValue($name, $expr);
  126. $this->stack[] = array('op' => 'return', $expr);
  127. return $this;
  128. }
  129. function do_if($expr)
  130. {
  131. $this->getValue($expr, $vexpr);
  132. $this->stack[] = array('op' => 'if', 'expr' => $vexpr);
  133. return $this;
  134. }
  135. function do_else()
  136. {
  137. $this->stack[] = array('op' => 'else');
  138. return $this;
  139. }
  140. function do_endif()
  141. {
  142. $this->stack[] = array('op' => 'end_if');
  143. return $this;
  144. }
  145. function do_endfunction()
  146. {
  147. $this->stack[] = array('op' => 'end_function');
  148. return $this;
  149. }
  150. function v()
  151. {
  152. $var = array();
  153. foreach (func_get_args() as $id => $def) {
  154. if ($id == 0) {
  155. $var[$id] = $def;
  156. } else {
  157. $this->getValue($def, $value);
  158. $var[$id] = $value;
  159. }
  160. }
  161. if (count($var) == 1) {
  162. $var = $var[0];
  163. }
  164. $this->current = array('var' => $var);
  165. return $this;
  166. }
  167. final function __get($property)
  168. {
  169. $property = strtolower($property);
  170. if (isset($this->current[$property])) {
  171. return $this->current[$property];
  172. }
  173. return FALSE;
  174. }
  175. static function fromArrayGetAST($obj)
  176. {
  177. $class = __CLASS__;
  178. if ($obj InstanceOf $class) {
  179. return $obj;
  180. }
  181. foreach (array('op_expr', 'expr_cond', 'exec', 'var', 'string', 'number', 'constant') as $type) {
  182. if (isset($obj[$type])) {
  183. $nobj = new $class;
  184. $nobj->stack[] = $obj;
  185. return $nobj;
  186. }
  187. }
  188. }
  189. static function getValue($obj, &$value, $get_all=FALSE)
  190. {
  191. $class = __CLASS__;
  192. if ($obj InstanceOf $class) {
  193. $value = $obj->getArray($get_all);
  194. } else if (is_string($obj)) {
  195. $value = self::str($obj);
  196. } else if (is_numeric($obj) or $obj === 0) {
  197. $value = self::num($obj);
  198. } else if ($obj === FALSE) {
  199. $value = array('expr' => FALSE);
  200. } else if ($obj === TRUE) {
  201. $value = array('expr' => TRUE);
  202. } else if (is_array($obj)) {
  203. foreach (array('exec', 'var', 'string', 'number', 'constant') as $type) {
  204. if (isset($obj[$type])) {
  205. $value = $obj;
  206. return;
  207. }
  208. }
  209. $h = hcode()->arr();
  210. $first = 0;
  211. foreach($obj as $key => $value) {
  212. if ($key === $first) {
  213. $key = NULL;
  214. $first++;
  215. }
  216. $h->element($key, $value);
  217. }
  218. $value = $h->getArray();
  219. } else if ($obj === NULL) {
  220. $value = array();
  221. } else {
  222. var_Dump($obj);
  223. throw new Exception("Imposible to get the value of the object");
  224. }
  225. }
  226. function getArray($get_all=FALSE)
  227. {
  228. $this->end();
  229. if ($get_all) {
  230. return $this->stack;
  231. }
  232. return isset($this->stack[0]) ? $this->stack[0] : NULL;
  233. }
  234. function do_for($index, $min, $max, $step, Haanga_AST $body)
  235. {
  236. $def = array(
  237. 'op' => 'for',
  238. 'index' => $index,
  239. 'min' => $min,
  240. 'max' => $max,
  241. 'step' => $step,
  242. );
  243. $this->stack[] = $def;
  244. $this->stack = array_merge($this->stack, $body->getArray(TRUE));
  245. $this->stack[] = array('op' => 'end_for');
  246. return $this;
  247. }
  248. function do_foreach($array, $value, $key, Haanga_AST $body)
  249. {
  250. foreach (array('array', 'value', 'key') as $var) {
  251. if ($$var === NULL) {
  252. continue;
  253. }
  254. $var1 = & $$var;
  255. if (is_string($var1)) {
  256. $var1 = hvar($var1);
  257. }
  258. if (is_object($var1)) {
  259. $var1 = $var1->getArray();
  260. }
  261. $var1 = $var1['var'];
  262. }
  263. $def = array('op' => 'foreach', 'array' => $array, 'value' => $value);
  264. if ($key) {
  265. $def['key'] = $key;
  266. }
  267. $this->stack[] = $def;
  268. $this->stack = array_merge($this->stack, $body->getArray(TRUE));
  269. $this->stack[] = array('op' => 'end_foreach');
  270. return $this;
  271. }
  272. function do_echo($stmt)
  273. {
  274. $this->getValue($stmt, $value);
  275. $this->stack[] = array('op' => 'print', $value);
  276. return $this;
  277. }
  278. function do_global($array)
  279. {
  280. $this->stack[] = array('op' => 'global', 'vars' => $array);
  281. return $this;
  282. }
  283. function do_exec()
  284. {
  285. $params = func_get_args();
  286. $exec = call_user_func_array('hexec', $params);
  287. $this->stack[] = array('op' => 'expr', $exec->getArray());
  288. return $this;
  289. }
  290. function exec($function)
  291. {
  292. $this->current = array('exec' => $function, 'args' => array());
  293. foreach (func_get_args() as $id => $param) {
  294. if ($id > 0) {
  295. $this->param($param);
  296. }
  297. }
  298. return $this;
  299. }
  300. function expr($operation, $term1, $term2=NULL)
  301. {
  302. $this->getValue($term1, $value1);
  303. if ($term2 !== NULL) {
  304. $this->getValue($term2, $value2);
  305. } else {
  306. $value2 = NULL;
  307. }
  308. $this->current = array('op_expr' => $operation, $value1, $value2);
  309. return $this;
  310. }
  311. function expr_cond($expr, $if_true, $if_false)
  312. {
  313. $this->getValue($expr, $vExpr);
  314. $this->getValue($if_true, $vIfTrue);
  315. $this->getValue($if_false, $vIfFalse);
  316. $this->current = array('expr_cond' => $vExpr, 'true' => $vIfTrue, 'false' => $vIfFalse);
  317. return $this;
  318. }
  319. function arr()
  320. {
  321. $this->current = array('array' => array());
  322. return $this;
  323. }
  324. function element($key=NULL, $value)
  325. {
  326. $last = & $this->current;
  327. if (!isset($last['array'])) {
  328. throw new Exception("Invalid call to element()");
  329. }
  330. $this->getValue($value, $val);
  331. if ($key !== NULL) {
  332. $this->getValue($key, $kval);
  333. $val = array('key' => array($kval, $val));
  334. }
  335. $last['array'][] = $val;
  336. }
  337. function decl_raw($name, $value)
  338. {
  339. if (is_string($name)) {
  340. $name = hvar($name);
  341. }
  342. $this->getValue($name, $name);
  343. $array = array('op' => 'declare', 'name' => $name['var']);
  344. foreach (func_get_args() as $id => $value) {
  345. if ($id != 0) {
  346. $array[] = $value;
  347. }
  348. }
  349. $this->stack[] = $array;
  350. return $this;
  351. }
  352. function decl($name, $value)
  353. {
  354. if (is_string($name)) {
  355. $name = hvar($name);
  356. }
  357. $this->getValue($name, $name);
  358. $array = array('op' => 'declare', 'name' => $name['var']);
  359. foreach (func_get_args() as $id => $value) {
  360. if ($id != 0) {
  361. $this->getValue($value, $stmt);
  362. $array[] = $stmt;
  363. }
  364. }
  365. $this->stack[] = $array;
  366. return $this;
  367. }
  368. function append($name, $value)
  369. {
  370. if (is_string($name)) {
  371. $name = hvar($name);
  372. }
  373. $this->getValue($value, $stmt);
  374. $this->getValue($name, $name);
  375. $this->stack[] = array('op' => 'append_var', 'name' => $name['var'], $stmt);
  376. return $this;
  377. }
  378. function param($param)
  379. {
  380. $last = & $this->current;
  381. if (!isset($last['exec'])) {
  382. throw new Exception("Invalid call to param()");
  383. }
  384. $this->getValue($param, $value);
  385. $last['args'][] = $value;
  386. return $this;
  387. }
  388. function end()
  389. {
  390. if (count($this->current) > 0) {
  391. $this->stack[] = $this->current;
  392. $this->current = array();
  393. }
  394. return $this;
  395. }
  396. }
  397. function hcode()
  398. {
  399. return new Haanga_AST;
  400. }
  401. function hexpr($term1, $op='expr', $term2=NULL, $op2=NULL)
  402. {
  403. $code = hcode();
  404. switch ($op2) {
  405. case '+':
  406. case '-':
  407. case '/':
  408. case '*':
  409. case '%':
  410. case '||':
  411. case '&&':
  412. case '<':
  413. case '>':
  414. case '<=':
  415. case '>=':
  416. case '==':
  417. case '!=':
  418. /* call recursive to resolve term2 */
  419. $args = func_get_args();
  420. $term2 = call_user_func_array('hexpr', array_slice($args, 2));
  421. break;
  422. }
  423. return $code->expr($op, $term1, $term2);
  424. }
  425. function hexpr_cond($expr, $if_true, $if_false)
  426. {
  427. $code = hcode();
  428. $code->expr_cond($expr, $if_true, $if_false);
  429. return $code;
  430. }
  431. function hexec()
  432. {
  433. $code = hcode();
  434. $args = func_get_args();
  435. return call_user_func_array(array($code, 'exec'), $args);
  436. }
  437. function hconst($str)
  438. {
  439. return Haanga_AST::Constant($str);
  440. }
  441. // hvar() {{{
  442. /**
  443. * Create the representation of a variable
  444. *
  445. * @return Haanga_AST
  446. */
  447. function hvar()
  448. {
  449. $args = func_get_args();
  450. return hvar_ex($args);
  451. }
  452. function hvar_ex($args)
  453. {
  454. $code = hcode();
  455. return call_user_func_array(array($code, 'v'), $args);
  456. }
  457. // }}}
  458. /*
  459. * Local variables:
  460. * tab-width: 4
  461. * c-basic-offset: 4
  462. * End:
  463. * vim600: sw=4 ts=4 fdm=marker
  464. * vim<600: sw=4 ts=4
  465. */