PageRenderTime 52ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/h2o/parser.php

http://github.com/speedmax/h2o-php
PHP | 290 lines | 255 code | 25 blank | 10 comment | 29 complexity | 888b5e965627a77013f94b7f30120ee0 MD5 | raw file
  1. <?php
  2. class H2o_Lexer {
  3. function __construct($options = array()) {
  4. $this->options = $options;
  5. $trim = '';
  6. if ($this->options['TRIM_TAGS'])
  7. $trim = '(?:\r?\n)?';
  8. $this->pattern = ('/\G(.*?)(?:' .
  9. preg_quote($this->options['BLOCK_START']). '(.*?)' .preg_quote($this->options['BLOCK_END']) . $trim . '|' .
  10. preg_quote($this->options['VARIABLE_START']). '(.*?)' .preg_quote($this->options['VARIABLE_END']) . '|' .
  11. preg_quote($this->options['COMMENT_START']). '(.*?)' .preg_quote($this->options['COMMENT_END']) . $trim . ')/sm'
  12. );
  13. }
  14. function tokenize($source) {
  15. $result = new TokenStream;
  16. $pos = 0;
  17. $matches = array();
  18. preg_match_all($this->pattern, $source, $matches, PREG_SET_ORDER);
  19. foreach ($matches as $match) {
  20. if ($match[1])
  21. $result->feed('text', $match[1], $pos);
  22. $tagpos = $pos + strlen($match[1]);
  23. if ($match[2])
  24. $result->feed('block', trim($match[2]), $tagpos);
  25. elseif ($match[3])
  26. $result->feed('variable', trim($match[3]), $tagpos);
  27. elseif ($match[4])
  28. $result->feed('comment', trim($match[4]), $tagpos);
  29. $pos += strlen($match[0]);
  30. }
  31. if ($pos < strlen($source)){
  32. $result->feed('text', substr($source, $pos), $pos);
  33. }
  34. $result->close();
  35. return $result;
  36. }
  37. }
  38. class H2o_Parser {
  39. var $first;
  40. var $storage = array();
  41. var $filename;
  42. var $runtime;
  43. function __construct($source, $filename, $runtime, $options) {
  44. $this->options = $options;
  45. //$this->source = $source;
  46. $this->runtime = $runtime;
  47. $this->filename = $filename;
  48. $this->first = true;
  49. $this->lexer = new H2o_Lexer($options);
  50. $this->tokenstream = $this->lexer->tokenize($source);
  51. $this->storage = array(
  52. 'blocks' => array(),
  53. 'templates' => array(),
  54. 'included' => array()
  55. );
  56. }
  57. function &parse() {
  58. $until = func_get_args();
  59. $nodelist = new NodeList($this);
  60. while($token = $this->tokenstream->next()) {
  61. //$token = $this->tokenstream->current();
  62. switch($token->type) {
  63. case 'text' :
  64. $node = new TextNode($token->content, $token->position);
  65. break;
  66. case 'variable' :
  67. $args = H2o_Parser::parseArguments($token->content, $token->position);
  68. $variable = array_shift($args);
  69. $filters = $args;
  70. $node = new VariableNode($variable, $filters, $token->position);
  71. break;
  72. case 'comment' :
  73. $node = new CommentNode($token->content);
  74. break;
  75. case 'block' :
  76. if (in_array($token->content, $until)) {
  77. $this->token = $token;
  78. return $nodelist;
  79. }
  80. $temp = preg_split('/\s+/',$token->content, 2);
  81. $name = $temp[0];
  82. $args = (count($temp) > 1 ? $temp[1] : null);
  83. $node = H2o::createTag($name, $args, $this, $token->position);
  84. $this->token = $token;
  85. }
  86. $this->searching = join(',',$until);
  87. $this->first = false;
  88. $nodelist->append($node);
  89. }
  90. if ($until) {
  91. throw new TemplateSyntaxError('Unclose tag, expecting '. $until[0]);
  92. }
  93. return $nodelist;
  94. }
  95. function skipTo($until) {
  96. $this->parse($until);
  97. return null;
  98. }
  99. # Parse arguments
  100. static function parseArguments($source = null, $fpos = 0){
  101. $parser = new ArgumentLexer($source, $fpos);
  102. $result = array();
  103. $current_buffer = &$result;
  104. $filter_buffer = array();
  105. $tokens = $parser->parse();
  106. foreach ($tokens as $token) {
  107. list($token, $data) = $token;
  108. if ($token == 'filter_start') {
  109. $filter_buffer = array();
  110. $current_buffer = &$filter_buffer;
  111. }
  112. elseif ($token == 'filter_end') {
  113. if (count($filter_buffer)) {
  114. $i = count($result)-1;
  115. if ( is_array($result[$i]) ) $result[$i]['filters'][] = $filter_buffer;
  116. else $result[$i] = array(0 => $result[$i], 'filters' => array($filter_buffer));
  117. }
  118. $current_buffer = &$result;
  119. }
  120. elseif ($token == 'boolean') {
  121. $current_buffer[] = ($data === 'true'? true : false);
  122. }
  123. elseif ($token == 'name') {
  124. $current_buffer[] = symbol($data);
  125. }
  126. elseif ($token == 'number' || $token == 'string') {
  127. $current_buffer[] = $data;
  128. }
  129. elseif ($token == 'named_argument') {
  130. $last = $current_buffer[count($current_buffer) - 1];
  131. if (!is_array($last))
  132. $current_buffer[] = array();
  133. $namedArgs =& $current_buffer[count($current_buffer) - 1];
  134. list($name,$value) = array_map('trim', explode(':', $data, 2));
  135. # if argument value is variable mark it
  136. $value = self::parseArguments($value);
  137. $namedArgs[$name] = $value[0];
  138. }
  139. elseif( $token == 'operator') {
  140. $current_buffer[] = array('operator'=>$data);
  141. }
  142. }
  143. return $result;
  144. }
  145. }
  146. class H2O_RE {
  147. static $whitespace, $seperator, $parentheses, $pipe, $filter_end, $operator, $boolean, $number, $string, $i18n_string, $name, $named_args;
  148. static function init() {
  149. $r = 'strip_regex';
  150. self::$whitespace = '/\s+/m';
  151. self::$parentheses = '/\(|\)/m';
  152. self::$filter_end = '/;/';
  153. self::$boolean = '/true|false/';
  154. self::$seperator = '/,/';
  155. self::$pipe = '/\|/';
  156. self::$operator = '/\s?(>|<|>=|<=|!=|==|!|and |not |or )\s?/i';
  157. self::$number = '/\d+(\.\d*)?/';
  158. self::$name = '/[a-zA-Z][a-zA-Z0-9-_]*(?:\.[a-zA-Z_0-9][a-zA-Z0-9_-]*)*/';
  159. self::$string = '/(?:
  160. "([^"\\\\]*(?:\\\\.[^"\\\\]*)*)" | # Double Quote string
  161. \'([^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\' # Single Quote String
  162. )/xsm';
  163. self::$i18n_string = "/_\({$r(self::$string)}\) | {$r(self::$string)}/xsm";
  164. self::$named_args = "{
  165. ({$r(self::$name)})(?:{$r(self::$whitespace)})?
  166. :
  167. (?:{$r(self::$whitespace)})?({$r(self::$i18n_string)}|{$r(self::$number)}|{$r(self::$name)})
  168. }x";
  169. }
  170. }
  171. H2O_RE::init();
  172. class ArgumentLexer {
  173. private $source;
  174. private $match;
  175. private $pos = 0, $fpos, $eos;
  176. private $operator_map = array(
  177. '!' => 'not', '!='=> 'ne', '==' => 'eq', '>' => 'gt', '<' => 'lt', '<=' => 'le', '>=' => 'ge'
  178. );
  179. function __construct($source, $fpos = 0){
  180. if (!is_null($source))
  181. $this->source = $source;
  182. $this->fpos=$fpos;
  183. }
  184. function parse(){
  185. $result = array();
  186. $filtering = false;
  187. while (!$this->eos()) {
  188. $this->scan(H2O_RE::$whitespace);
  189. if (!$filtering) {
  190. if ($this->scan(H2O_RE::$operator)){
  191. $operator = trim($this->match);
  192. if(isset($this->operator_map[$operator]))
  193. $operator = $this->operator_map[$operator];
  194. $result[] = array('operator', $operator);
  195. }
  196. elseif ($this->scan(H2O_RE::$boolean))
  197. $result[] = array('boolean', $this->match);
  198. elseif ($this->scan(H2O_RE::$named_args))
  199. $result[] = array('named_argument', $this->match);
  200. elseif ($this->scan(H2O_RE::$name))
  201. $result[] = array('name', $this->match);
  202. elseif ($this->scan(H2O_RE::$pipe)) {
  203. $filtering = true;
  204. $result[] = array('filter_start', $this->match);
  205. }
  206. elseif ($this->scan(H2O_RE::$seperator))
  207. $result[] = array('separator', null);
  208. elseif ($this->scan(H2O_RE::$i18n_string))
  209. $result[] = array('string', $this->match);
  210. elseif ($this->scan(H2O_RE::$number))
  211. $result[] = array('number', $this->match);
  212. else
  213. throw new TemplateSyntaxError('unexpected character in filters : "'. $this->source[$this->pos]. '" at '.$this->getPosition());
  214. }
  215. else {
  216. // parse filters, with chaining and ";" as filter end character
  217. if ($this->scan(H2O_RE::$pipe)) {
  218. $result[] = array('filter_end', null);
  219. $result[] = array('filter_start', null);
  220. }
  221. elseif ($this->scan(H2O_RE::$seperator))
  222. $result[] = array('separator', null);
  223. elseif ($this->scan(H2O_RE::$filter_end)) {
  224. $result[] = array('filter_end', null);
  225. $filtering = false;
  226. }
  227. elseif ($this->scan(H2O_RE::$boolean))
  228. $result[] = array('boolean', $this->match);
  229. elseif ($this->scan(H2O_RE::$named_args))
  230. $result[] = array('named_argument', $this->match);
  231. elseif ($this->scan(H2O_RE::$name))
  232. $result[] = array('name', $this->match);
  233. elseif ($this->scan(H2O_RE::$i18n_string))
  234. $result[] = array('string', $this->match);
  235. elseif ($this->scan(H2O_RE::$number))
  236. $result[] = array('number', $this->match);
  237. else
  238. throw new TemplateSyntaxError('unexpected character in filters : "'. $this->source[$this->pos]. '" at '.$this->getPosition());
  239. }
  240. }
  241. // if we are still in the filter state, we add a filter_end token.
  242. if ($filtering)
  243. $result[] = array('filter_end', null);
  244. return $result;
  245. }
  246. # String scanner
  247. function scan($regexp) {
  248. if (preg_match($regexp . 'A', $this->source, $match, null, $this->pos)) {
  249. $this->match = $match[0];
  250. $this->pos += strlen($this->match);
  251. return true;
  252. }
  253. return false;
  254. }
  255. function eos() {
  256. return $this->pos >= strlen($this->source);
  257. }
  258. /**
  259. * return the position in the template
  260. */
  261. function getPosition() {
  262. return $this->fpos + $this->pos;
  263. }
  264. }
  265. ?>