SieveParser.php 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. <?php namespace Sieve;
  2. include_once 'SieveTree.php';
  3. include_once 'SieveScanner.php';
  4. include_once 'SieveSemantics.php';
  5. include_once 'SieveException.php';
  6. class SieveParser
  7. {
  8. protected $scanner_;
  9. protected $script_;
  10. protected $tree_;
  11. protected $status_;
  12. public function __construct($script = null)
  13. {
  14. if (isset($script))
  15. $this->parse($script);
  16. }
  17. public function GetParseTree()
  18. {
  19. return $this->tree_;
  20. }
  21. public function dumpParseTree()
  22. {
  23. return $this->tree_->dump();
  24. }
  25. public function getScriptText()
  26. {
  27. return $this->tree_->getText();
  28. }
  29. protected function getPrevToken_($parent_id)
  30. {
  31. $childs = $this->tree_->getChilds($parent_id);
  32. for ($i = count($childs); $i > 0; --$i)
  33. {
  34. $prev = $this->tree_->getNode($childs[$i-1]);
  35. if ($prev->is(SieveToken::Comment|SieveToken::Whitespace))
  36. continue;
  37. // use command owning a block or list instead of previous
  38. if ($prev->is(SieveToken::BlockStart|SieveToken::Comma|SieveToken::LeftParenthesis))
  39. $prev = $this->tree_->getNode($parent_id);
  40. return $prev;
  41. }
  42. return $this->tree_->getNode($parent_id);
  43. }
  44. /*******************************************************************************
  45. * methods for recursive descent start below
  46. */
  47. public function passthroughWhitespaceComment($token)
  48. {
  49. return 0;
  50. }
  51. public function passthroughFunction($token)
  52. {
  53. $this->tree_->addChild($token);
  54. }
  55. public function parse($script)
  56. {
  57. $this->script_ = $script;
  58. $this->scanner_ = new SieveScanner($this->script_);
  59. // Define what happens with passthrough tokens like whitespacs and comments
  60. $this->scanner_->setPassthroughFunc(
  61. array(
  62. $this, 'passthroughWhitespaceComment'
  63. )
  64. );
  65. $this->tree_ = new SieveTree('tree');
  66. $this->commands_($this->tree_->getRoot());
  67. if (!$this->scanner_->nextTokenIs(SieveToken::ScriptEnd)) {
  68. $token = $this->scanner_->nextToken();
  69. throw new SieveException($token, SieveToken::ScriptEnd);
  70. }
  71. }
  72. protected function commands_($parent_id)
  73. {
  74. while (true)
  75. {
  76. if (!$this->scanner_->nextTokenIs(SieveToken::Identifier))
  77. break;
  78. // Get and check a command token
  79. $token = $this->scanner_->nextToken();
  80. $semantics = new SieveSemantics($token, $this->getPrevToken_($parent_id));
  81. // Process eventual arguments
  82. $this_node = $this->tree_->addChildTo($parent_id, $token);
  83. $this->arguments_($this_node, $semantics);
  84. $token = $this->scanner_->nextToken();
  85. if (!$token->is(SieveToken::Semicolon))
  86. {
  87. // TODO: check if/when semcheck is needed here
  88. $semantics->validateToken($token);
  89. if ($token->is(SieveToken::BlockStart))
  90. {
  91. $this->tree_->addChildTo($this_node, $token);
  92. $this->block_($this_node, $semantics);
  93. continue;
  94. }
  95. throw new SieveException($token, SieveToken::Semicolon);
  96. }
  97. $semantics->done($token);
  98. $this->tree_->addChildTo($this_node, $token);
  99. }
  100. }
  101. protected function arguments_($parent_id, &$semantics)
  102. {
  103. while (true)
  104. {
  105. if ($this->scanner_->nextTokenIs(SieveToken::Number|SieveToken::Tag))
  106. {
  107. // Check if semantics allow a number or tag
  108. $token = $this->scanner_->nextToken();
  109. $semantics->validateToken($token);
  110. $this->tree_->addChildTo($parent_id, $token);
  111. }
  112. else if ($this->scanner_->nextTokenIs(SieveToken::StringList))
  113. {
  114. $this->stringlist_($parent_id, $semantics);
  115. }
  116. else
  117. {
  118. break;
  119. }
  120. }
  121. if ($this->scanner_->nextTokenIs(SieveToken::TestList))
  122. {
  123. $this->testlist_($parent_id, $semantics);
  124. }
  125. }
  126. protected function stringlist_($parent_id, &$semantics)
  127. {
  128. if (!$this->scanner_->nextTokenIs(SieveToken::LeftBracket))
  129. {
  130. $this->string_($parent_id, $semantics);
  131. return;
  132. }
  133. $token = $this->scanner_->nextToken();
  134. $semantics->startStringList($token);
  135. $this->tree_->addChildTo($parent_id, $token);
  136. if($this->scanner_->nextTokenIs(SieveToken::RightBracket)) {
  137. //allow empty lists
  138. $token = $this->scanner_->nextToken();
  139. $this->tree_->addChildTo($parent_id, $token);
  140. $semantics->endStringList();
  141. return;
  142. }
  143. do
  144. {
  145. $this->string_($parent_id, $semantics);
  146. $token = $this->scanner_->nextToken();
  147. if (!$token->is(SieveToken::Comma|SieveToken::RightBracket))
  148. throw new SieveException($token, array(SieveToken::Comma, SieveToken::RightBracket));
  149. if ($token->is(SieveToken::Comma))
  150. $semantics->continueStringList();
  151. $this->tree_->addChildTo($parent_id, $token);
  152. }
  153. while (!$token->is(SieveToken::RightBracket));
  154. $semantics->endStringList();
  155. }
  156. protected function string_($parent_id, &$semantics)
  157. {
  158. $token = $this->scanner_->nextToken();
  159. $semantics->validateToken($token);
  160. $this->tree_->addChildTo($parent_id, $token);
  161. }
  162. protected function testlist_($parent_id, &$semantics)
  163. {
  164. if (!$this->scanner_->nextTokenIs(SieveToken::LeftParenthesis))
  165. {
  166. $this->test_($parent_id, $semantics);
  167. return;
  168. }
  169. $token = $this->scanner_->nextToken();
  170. $semantics->validateToken($token);
  171. $this->tree_->addChildTo($parent_id, $token);
  172. do
  173. {
  174. $this->test_($parent_id, $semantics);
  175. $token = $this->scanner_->nextToken();
  176. if (!$token->is(SieveToken::Comma|SieveToken::RightParenthesis))
  177. {
  178. throw new SieveException($token, array(SieveToken::Comma, SieveToken::RightParenthesis));
  179. }
  180. $this->tree_->addChildTo($parent_id, $token);
  181. }
  182. while (!$token->is(SieveToken::RightParenthesis));
  183. }
  184. protected function test_($parent_id, &$semantics)
  185. {
  186. // Check if semantics allow an identifier
  187. $token = $this->scanner_->nextToken();
  188. $semantics->validateToken($token);
  189. // Get semantics for this test command
  190. $this_semantics = new SieveSemantics($token, $this->getPrevToken_($parent_id));
  191. $this_node = $this->tree_->addChildTo($parent_id, $token);
  192. // Consume eventual argument tokens
  193. $this->arguments_($this_node, $this_semantics);
  194. // Check that all required arguments were there
  195. $token = $this->scanner_->peekNextToken();
  196. $this_semantics->done($token);
  197. }
  198. protected function block_($parent_id, &$semantics)
  199. {
  200. $this->commands_($parent_id, $semantics);
  201. $token = $this->scanner_->nextToken();
  202. if (!$token->is(SieveToken::BlockEnd))
  203. {
  204. throw new SieveException($token, SieveToken::BlockEnd);
  205. }
  206. $this->tree_->addChildTo($parent_id, $token);
  207. }
  208. }