ast
13 years ago
cache
13 years ago
XmlImportConfig.php
13 years ago
XmlImportCsvParse.php
13 years ago
XmlImportException.php
13 years ago
XmlImportParser.php
13 years ago
XmlImportReaderInterface.php
13 years ago
XmlImportStringReader.php
13 years ago
XmlImportTemplate.php
13 years ago
XmlImportTemplateCodeGenerator.php
13 years ago
XmlImportTemplateParser.php
13 years ago
XmlImportTemplateScanner.php
13 years ago
XmlImportToken.php
13 years ago
pclzip.lib.php
13 years ago
XmlImportTemplateParser.php
399 lines
| 1 | <?php |
| 2 | /** |
| 3 | * @author Olexandr Zanichkovsky <olexandr.zanichkovsky@zophiatech.com> |
| 4 | * @package General |
| 5 | */ |
| 6 | |
| 7 | require_once dirname(__FILE__) . '/ast/XmlImportAstSequence.php'; |
| 8 | require_once dirname(__FILE__) . '/ast/XmlImportAstPrint.php'; |
| 9 | require_once dirname(__FILE__) . '/ast/XmlImportAstText.php'; |
| 10 | require_once dirname(__FILE__) . '/ast/XmlImportAstWith.php'; |
| 11 | require_once dirname(__FILE__) . '/ast/XmlImportAstForeach.php'; |
| 12 | require_once dirname(__FILE__) . '/ast/XmlImportAstIf.php'; |
| 13 | require_once dirname(__FILE__) . '/ast/XmlImportAstMath.php'; |
| 14 | require_once dirname(__FILE__) . '/ast/XmlImportAstSpintax.php'; |
| 15 | |
| 16 | require_once dirname(__FILE__) . '/ast/XmlImportAstXPath.php'; |
| 17 | require_once dirname(__FILE__) . '/ast/XmlImportAstString.php'; |
| 18 | require_once dirname(__FILE__) . '/ast/XmlImportAstInteger.php'; |
| 19 | require_once dirname(__FILE__) . '/ast/XmlImportAstFloat.php'; |
| 20 | |
| 21 | /** |
| 22 | * Parses a list of nodes into AST (Abstract Syntax Tree) |
| 23 | */ |
| 24 | class XmlImportTemplateParser |
| 25 | { |
| 26 | /** |
| 27 | * List of tokens |
| 28 | * |
| 29 | * @var array |
| 30 | */ |
| 31 | private $tokens; |
| 32 | |
| 33 | /** |
| 34 | * Current index |
| 35 | * |
| 36 | * @var int |
| 37 | */ |
| 38 | private $index = -1; |
| 39 | |
| 40 | /** |
| 41 | * Stack that stores possible block endings |
| 42 | * |
| 43 | * @var array |
| 44 | */ |
| 45 | private $clauseStack = array(); |
| 46 | |
| 47 | /** |
| 48 | * Stack of sequences |
| 49 | * |
| 50 | * @var array |
| 51 | */ |
| 52 | private $sequenceStack = array(); |
| 53 | |
| 54 | /** |
| 55 | * Whether else subclause is allowed |
| 56 | * |
| 57 | * @var bool |
| 58 | */ |
| 59 | private $elseAllowed = false; |
| 60 | |
| 61 | /** |
| 62 | * Creates new instance |
| 63 | * |
| 64 | * @param array $tokens |
| 65 | */ |
| 66 | public function __construct(array $tokens) |
| 67 | { |
| 68 | $this->tokens = $tokens; |
| 69 | } |
| 70 | |
| 71 | /** |
| 72 | * Parses the list of tokens into AST tree |
| 73 | * |
| 74 | * @return XmlImportAstSequence |
| 75 | */ |
| 76 | public function parse() |
| 77 | { |
| 78 | $result = $this->parseSequence(); |
| 79 | |
| 80 | if (count($this->clauseStack) > 0) |
| 81 | throw new XmlImportException("Unexpected end of template."); |
| 82 | |
| 83 | return $result; |
| 84 | } |
| 85 | |
| 86 | /** |
| 87 | * Parses sequence |
| 88 | * |
| 89 | * @return XmlImportAstSequence |
| 90 | */ |
| 91 | private function parseSequence() |
| 92 | { |
| 93 | if (($this->index + 1) == count($this->tokens)) |
| 94 | throw new XmlImportException("Reached end of template but statement sequence expected"); |
| 95 | $sequence = new XmlImportAstSequence(); |
| 96 | array_push($this->sequenceStack, $sequence); |
| 97 | if (count($this->clauseStack) == 0) |
| 98 | { |
| 99 | while (($this->index + 1) < count($this->tokens)) |
| 100 | { |
| 101 | $sequence->addStatement($this->parseStatement()); |
| 102 | } |
| 103 | } |
| 104 | else |
| 105 | { |
| 106 | while (($this->index + 1) < count($this->tokens)) |
| 107 | { |
| 108 | if ($this->tokens[$this->index + 1]->getKind() == $this->clauseStack[count($this->clauseStack) - 1]) |
| 109 | { |
| 110 | $this->index++; |
| 111 | array_pop($this->clauseStack); |
| 112 | break; |
| 113 | } |
| 114 | $statement = $this->parseStatement(); |
| 115 | if (is_null($statement)) |
| 116 | return $sequence; |
| 117 | $sequence->addStatement($statement); |
| 118 | } |
| 119 | } |
| 120 | array_pop($this->sequenceStack); |
| 121 | |
| 122 | return $sequence; |
| 123 | } |
| 124 | |
| 125 | /** |
| 126 | * Parses statement |
| 127 | * |
| 128 | * @return XmlImportAstText |
| 129 | */ |
| 130 | private function parseStatement() |
| 131 | { |
| 132 | if ($this->tokens[$this->index + 1]->getKind() == XmlImportToken::KIND_TEXT) |
| 133 | { |
| 134 | return new XmlImportAstText($this->tokens[++$this->index]->getValue()); |
| 135 | } |
| 136 | elseif ($this->tokens[$this->index + 1]->getKind() == XmlImportToken::KIND_PRINT) |
| 137 | { |
| 138 | $this->index++; |
| 139 | return new XmlImportAstPrint($this->parseExpression()); |
| 140 | } |
| 141 | elseif ($this->tokens[$this->index + 1]->getKind() == XmlImportToken::KIND_WITH) |
| 142 | { |
| 143 | return $this->parseWith(); |
| 144 | } |
| 145 | elseif ($this->tokens[$this->index + 1]->getKind() == XmlImportToken::KIND_FOREACH) |
| 146 | { |
| 147 | return $this->parseForeach(); |
| 148 | } |
| 149 | elseif($this->tokens[$this->index + 1]->getKind() == XmlImportToken::KIND_MATH) |
| 150 | { |
| 151 | return new XmlImportAstPrint($this->parseExpression()); |
| 152 | } |
| 153 | elseif($this->tokens[$this->index + 1]->getKind() == XmlImportToken::KIND_SPINTAX) |
| 154 | { |
| 155 | return new XmlImportAstPrint($this->parseExpression()); |
| 156 | } |
| 157 | elseif ($this->tokens[$this->index + 1]->getKind() == XmlImportToken::KIND_IF) |
| 158 | { |
| 159 | return $this->parseIf(); |
| 160 | } |
| 161 | elseif($this->clauseStack[count($this->clauseStack) - 1] == XmlImportToken::KIND_ENDIF && |
| 162 | in_array($this->tokens[$this->index + 1]->getKind(), array(XmlImportToken::KIND_ELSE, XmlImportToken::KIND_ELSEIF))) |
| 163 | { |
| 164 | if ($this->elseAllowed) |
| 165 | { |
| 166 | if ($this->tokens[$this->index + 1]->getKind() == XmlImportToken::KIND_ELSE) |
| 167 | $this->elseAllowed = false; |
| 168 | } |
| 169 | else |
| 170 | { |
| 171 | throw new XmlImportException("ELSEIF or ELSE is not allowed again after ELSE"); |
| 172 | } |
| 173 | return null; |
| 174 | } |
| 175 | else |
| 176 | throw new XmlImportException ("Unexpected token {$this->tokens[$this->index + 1]->getKind()}, statement was expected."); |
| 177 | } |
| 178 | |
| 179 | /** |
| 180 | * Parses expression |
| 181 | * |
| 182 | * @return XmlImportAstXPath |
| 183 | */ |
| 184 | private function parseExpression() |
| 185 | { |
| 186 | if ($this->index + 1 == count($this->tokens)) |
| 187 | throw new XmlImportException("Reached end of template but expression was expected"); |
| 188 | |
| 189 | if($this->tokens[$this->index + 1]->getKind() == XmlImportToken::KIND_MATH) |
| 190 | { |
| 191 | return $this->parseMath(); |
| 192 | } |
| 193 | elseif($this->tokens[$this->index + 1]->getKind() == XmlImportToken::KIND_SPINTAX) |
| 194 | { |
| 195 | return $this->parseSpintax(); |
| 196 | } |
| 197 | elseif ($this->tokens[$this->index + 1]->getKind() == XmlImportToken::KIND_XPATH) |
| 198 | { |
| 199 | $xpath = new XmlImportAstXPath($this->tokens[++$this->index]->getValue()); |
| 200 | $this->sequenceStack[count($this->sequenceStack) - 1]->addVariable($xpath); |
| 201 | return $xpath; |
| 202 | } |
| 203 | elseif ($this->tokens[$this->index + 1]->getKind() == XmlImportToken::KIND_STRING || $this->tokens[$this->index + 1]->getKind() == XmlImportToken::KIND_OPERATION) |
| 204 | { |
| 205 | return new XmlImportAstString($this->tokens[++$this->index]->getValue()); |
| 206 | } |
| 207 | elseif ($this->tokens[$this->index + 1]->getKind() == XmlImportToken::KIND_INT) |
| 208 | { |
| 209 | return new XmlImportAstInteger($this->tokens[++$this->index]->getValue()); |
| 210 | } |
| 211 | elseif ($this->tokens[$this->index + 1]->getKind() == XmlImportToken::KIND_FLOAT) |
| 212 | { |
| 213 | return new XmlImportAstFloat($this->tokens[++$this->index]->getValue()); |
| 214 | } |
| 215 | else |
| 216 | throw new XmlImportException("Unexpected token " . $this->tokens[$this->index + 1]->getKind()); |
| 217 | } |
| 218 | |
| 219 | /** |
| 220 | * Parses function |
| 221 | * |
| 222 | * @return XmlImportAstFunction |
| 223 | */ |
| 224 | private function parseMath() |
| 225 | { |
| 226 | $math = new XmlImportAstMath($this->tokens[++$this->index]->getValue()); |
| 227 | if ($this->tokens[$this->index + 1]->getKind() != XmlImportToken::KIND_OPEN) |
| 228 | throw new XmlImportException ("Open brace expected instead of " . $this->tokens[$this->index + 1]->getKind()); |
| 229 | $this->index++; |
| 230 | if ($this->tokens[$this->index + 1]->getKind() == XmlImportToken::KIND_CLOSE) |
| 231 | { |
| 232 | $this->index++; |
| 233 | return $math; |
| 234 | } |
| 235 | else |
| 236 | { |
| 237 | while ($this->index < count($this->tokens) - 2) |
| 238 | { |
| 239 | $math->addArgument($this->parseExpression()); |
| 240 | if ($this->tokens[$this->index + 1]->getKind() == XmlImportToken::KIND_CLOSE) |
| 241 | { |
| 242 | $this->index++; |
| 243 | return $math; |
| 244 | break; |
| 245 | } |
| 246 | elseif ($this->tokens[$this->index + 1]->getKind() == XmlImportToken::KIND_COMMA) |
| 247 | $this->index++; |
| 248 | else |
| 249 | throw new XmlImportException("Comma or closing brace expected instead of " . $this->tokens[$this->index + 1]->getKind()); |
| 250 | } |
| 251 | throw new XmlImportException("Unexpected end of MATH argument list"); |
| 252 | } |
| 253 | } |
| 254 | |
| 255 | /** |
| 256 | * Parses function |
| 257 | * |
| 258 | * @return XmlImportSpintaxFunction |
| 259 | */ |
| 260 | private function parseSpintax() |
| 261 | { |
| 262 | $spintax = new XmlImportAstSpintax($this->tokens[++$this->index]->getValue()); |
| 263 | if ($this->tokens[$this->index + 1]->getKind() != XmlImportToken::KIND_OPEN) |
| 264 | throw new XmlImportException ("Open brace expected instead of " . $this->tokens[$this->index + 1]->getKind()); |
| 265 | $this->index++; |
| 266 | if ($this->tokens[$this->index + 1]->getKind() == XmlImportToken::KIND_CLOSE) |
| 267 | { |
| 268 | $this->index++; |
| 269 | return $spintax; |
| 270 | } |
| 271 | else |
| 272 | { |
| 273 | while ($this->index < count($this->tokens) - 2) |
| 274 | { |
| 275 | $spintax->addArgument($this->parseExpression()); |
| 276 | if ($this->tokens[$this->index + 1]->getKind() == XmlImportToken::KIND_CLOSE) |
| 277 | { |
| 278 | $this->index++; |
| 279 | return $spintax; |
| 280 | break; |
| 281 | } |
| 282 | elseif ($this->tokens[$this->index + 1]->getKind() == XmlImportToken::KIND_COMMA) |
| 283 | $this->index++; |
| 284 | else |
| 285 | throw new XmlImportException("Comma or closing brace expected instead of " . $this->tokens[$this->index + 1]->getKind()); |
| 286 | } |
| 287 | throw new XmlImportException("Unexpected end of {$function->getName()} function argument list"); |
| 288 | } |
| 289 | } |
| 290 | |
| 291 | /** |
| 292 | * Parses clause that uses XPath and returns XPath |
| 293 | * |
| 294 | * @return XmlImportAstXPath |
| 295 | */ |
| 296 | private function parseXPathDependant() |
| 297 | { |
| 298 | $this->index++; |
| 299 | |
| 300 | if ($this->index + 1 == count($this->tokens)) |
| 301 | throw new XmlImportException("Reached end of template but expression was expected"); |
| 302 | |
| 303 | if ($this->tokens[$this->index + 1]->getKind() == XmlImportToken::KIND_OPEN) |
| 304 | $this->index++; |
| 305 | else |
| 306 | throw new XmlImportException("Open brace expected instead of " . $this->tokens[$this->index + 1]->getKind()); |
| 307 | if ($this->tokens[$this->index + 1]->getKind() == XmlImportToken::KIND_XPATH) |
| 308 | { |
| 309 | $xpath = new XmlImportAstXPath($this->tokens[++$this->index]->getValue()); |
| 310 | $this->sequenceStack[count($this->sequenceStack) - 1]->addVariable($xpath); |
| 311 | } |
| 312 | else |
| 313 | throw new XmlImportException("XPath expression expected instead of " . $this->tokens[$this->index + 1]->getKind()); |
| 314 | if ($this->tokens[$this->index + 1]->getKind() == XmlImportToken::KIND_CLOSE) |
| 315 | $this->index++; |
| 316 | else |
| 317 | throw new XmlImportException("Close brace expected instead of " . $this->tokens[$this->index + 1]->getKind()); |
| 318 | return $xpath; |
| 319 | } |
| 320 | |
| 321 | /** |
| 322 | * Parses WITH clause |
| 323 | * |
| 324 | * @return XmlImportAstWith |
| 325 | */ |
| 326 | private function parseWith() |
| 327 | { |
| 328 | $xpath = $this->parseXPathDependant(); |
| 329 | //store sequence exit |
| 330 | array_push($this->clauseStack, XmlImportToken::KIND_ENDWITH); |
| 331 | return new XmlImportAstWith($xpath, $this->parseSequence()); |
| 332 | } |
| 333 | |
| 334 | /** |
| 335 | * Parses FOREACH clause |
| 336 | * |
| 337 | * @return XmlImportAstForeach |
| 338 | */ |
| 339 | private function parseForeach() |
| 340 | { |
| 341 | $xpath = $this->parseXPathDependant(); |
| 342 | |
| 343 | array_push($this->clauseStack, XmlImportToken::KIND_ENDFOREACH); |
| 344 | return new XmlImportAstForeach($xpath, $this->parseSequence()); |
| 345 | } |
| 346 | |
| 347 | /** |
| 348 | * Parses IF clause |
| 349 | * |
| 350 | * @return XmlImportAstIf |
| 351 | */ |
| 352 | private function parseIf() |
| 353 | { |
| 354 | $this->index++; |
| 355 | $this->elseAllowed = true; |
| 356 | array_push($this->clauseStack, XmlImportToken::KIND_ENDIF); |
| 357 | |
| 358 | if ($this->index + 1 == count($this->tokens)) |
| 359 | throw new XmlImportException("Reached end of template but expression was expected"); |
| 360 | |
| 361 | if ($this->tokens[$this->index + 1]->getKind() == XmlImportToken::KIND_OPEN) |
| 362 | $this->index++; |
| 363 | else |
| 364 | throw new XmlImportException("Open brace expected instead of " . $this->tokens[$this->index + 1]->getKind()); |
| 365 | $if = new XmlImportAstIf($this->parseExpression()); |
| 366 | if ($this->tokens[$this->index + 1]->getKind() == XmlImportToken::KIND_CLOSE) |
| 367 | $this->index++; |
| 368 | else |
| 369 | throw new XmlImportException("Close brace expected instead of " . $this->tokens[$this->index + 1]->getKind()); |
| 370 | $if->addIfBody($this->parseSequence()); |
| 371 | if ($this->index + 1 != count($this->tokens)) |
| 372 | { |
| 373 | while ($this->tokens[$this->index + 1]->getKind() == XmlImportToken::KIND_ELSEIF) |
| 374 | { |
| 375 | $this->index++; |
| 376 | if ($this->tokens[$this->index + 1]->getKind() == XmlImportToken::KIND_OPEN) |
| 377 | $this->index++; |
| 378 | else |
| 379 | throw new XmlImportException("Open brace expected instead of " . $this->tokens[$this->index + 1]->getKind()); |
| 380 | $condition = $this->parseExpression(); |
| 381 | if ($this->tokens[$this->index + 1]->getKind() == XmlImportToken::KIND_CLOSE) |
| 382 | $this->index++; |
| 383 | else |
| 384 | throw new XmlImportException("Close brace expected instead of " . $this->tokens[$this->index + 1]->getKind()); |
| 385 | $elseif = new XmlImportAstElseif($condition, $this->parseSequence()); |
| 386 | $if->addElseif($elseif); |
| 387 | if ($this->index + 1 == count($this->tokens)) |
| 388 | break; |
| 389 | } |
| 390 | if ($this->index + 1 < count($this->tokens) && $this->tokens[$this->index + 1]->getKind() == XmlImportToken::KIND_ELSE) |
| 391 | { |
| 392 | $this->index++; |
| 393 | $if->addElseBody($this->parseSequence()); |
| 394 | } |
| 395 | } |
| 396 | |
| 397 | return $if; |
| 398 | } |
| 399 | } |