256 lines
		
	
	
		
			7.4 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			256 lines
		
	
	
		
			7.4 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php namespace Sieve;
 | |
| 
 | |
| include_once 'SieveTree.php';
 | |
| include_once 'SieveScanner.php';
 | |
| include_once 'SieveSemantics.php';
 | |
| include_once 'SieveException.php';
 | |
| 
 | |
| class SieveParser
 | |
| {
 | |
|     protected $scanner_;
 | |
|     protected $script_;
 | |
|     protected $tree_;
 | |
|     protected $status_;
 | |
| 
 | |
|     public function __construct($script = null)
 | |
|     {
 | |
|         if (isset($script))
 | |
|             $this->parse($script);
 | |
|     }
 | |
| 
 | |
|     public function GetParseTree()
 | |
|     {
 | |
|         return $this->tree_;
 | |
|     }
 | |
| 
 | |
|     public function dumpParseTree()
 | |
|     {
 | |
|         return $this->tree_->dump();
 | |
|     }
 | |
| 
 | |
|     public function getScriptText()
 | |
|     {
 | |
|         return $this->tree_->getText();
 | |
|     }
 | |
| 
 | |
|     protected function getPrevToken_($parent_id)
 | |
|     {
 | |
|         $childs = $this->tree_->getChilds($parent_id);
 | |
| 
 | |
|         for ($i = count($childs); $i > 0; --$i)
 | |
|         {
 | |
|             $prev = $this->tree_->getNode($childs[$i-1]);
 | |
|             if ($prev->is(SieveToken::Comment|SieveToken::Whitespace))
 | |
|                 continue;
 | |
| 
 | |
|             // use command owning a block or list instead of previous
 | |
|             if ($prev->is(SieveToken::BlockStart|SieveToken::Comma|SieveToken::LeftParenthesis))
 | |
|                 $prev = $this->tree_->getNode($parent_id);
 | |
| 
 | |
|             return $prev;
 | |
|         }
 | |
| 
 | |
|         return $this->tree_->getNode($parent_id);
 | |
|     }
 | |
| 
 | |
|     /*******************************************************************************
 | |
|      * methods for recursive descent start below
 | |
|      */
 | |
|     public function passthroughWhitespaceComment($token)
 | |
|     {
 | |
|         return 0;
 | |
|     }
 | |
| 
 | |
|     public function passthroughFunction($token)
 | |
|     {
 | |
|         $this->tree_->addChild($token);
 | |
|     }
 | |
| 
 | |
|     public function parse($script)
 | |
|     {
 | |
|         $this->script_ = $script;
 | |
| 
 | |
|         $this->scanner_ = new SieveScanner($this->script_);
 | |
| 
 | |
|         // Define what happens with passthrough tokens like whitespacs and comments
 | |
|         $this->scanner_->setPassthroughFunc(
 | |
|             array(
 | |
|                 $this, 'passthroughWhitespaceComment'
 | |
|             )
 | |
|         );
 | |
| 
 | |
|         $this->tree_ = new SieveTree('tree');
 | |
| 
 | |
|         $this->commands_($this->tree_->getRoot());
 | |
| 
 | |
|         if (!$this->scanner_->nextTokenIs(SieveToken::ScriptEnd)) {
 | |
|             $token = $this->scanner_->nextToken();
 | |
|             throw new SieveException($token, SieveToken::ScriptEnd);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     protected function commands_($parent_id)
 | |
|     {
 | |
|         while (true)
 | |
|         {
 | |
|             if (!$this->scanner_->nextTokenIs(SieveToken::Identifier))
 | |
|                 break;
 | |
| 
 | |
|             // Get and check a command token
 | |
|             $token = $this->scanner_->nextToken();
 | |
|             $semantics = new SieveSemantics($token, $this->getPrevToken_($parent_id));
 | |
| 
 | |
|             // Process eventual arguments
 | |
|             $this_node = $this->tree_->addChildTo($parent_id, $token);
 | |
|             $this->arguments_($this_node, $semantics);
 | |
| 
 | |
|             $token = $this->scanner_->nextToken();
 | |
|             if (!$token->is(SieveToken::Semicolon))
 | |
|             {
 | |
|                 // TODO: check if/when semcheck is needed here
 | |
|                 $semantics->validateToken($token);
 | |
| 
 | |
|                 if ($token->is(SieveToken::BlockStart))
 | |
|                 {
 | |
|                     $this->tree_->addChildTo($this_node, $token);
 | |
|                     $this->block_($this_node, $semantics);
 | |
|                     continue;
 | |
|                 }
 | |
| 
 | |
|                 throw new SieveException($token, SieveToken::Semicolon);
 | |
|             }
 | |
| 
 | |
|             $semantics->done($token);
 | |
|             $this->tree_->addChildTo($this_node, $token);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     protected function arguments_($parent_id, &$semantics)
 | |
|     {
 | |
|         while (true)
 | |
|         {
 | |
|             if ($this->scanner_->nextTokenIs(SieveToken::Number|SieveToken::Tag))
 | |
|             {
 | |
|                 // Check if semantics allow a number or tag
 | |
|                 $token = $this->scanner_->nextToken();
 | |
|                 $semantics->validateToken($token);
 | |
|                 $this->tree_->addChildTo($parent_id, $token);
 | |
|             }
 | |
|             else if ($this->scanner_->nextTokenIs(SieveToken::StringList))
 | |
|             {
 | |
|                 $this->stringlist_($parent_id, $semantics);
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 break;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         if ($this->scanner_->nextTokenIs(SieveToken::TestList))
 | |
|         {
 | |
|             $this->testlist_($parent_id, $semantics);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     protected function stringlist_($parent_id, &$semantics)
 | |
|     {
 | |
|         if (!$this->scanner_->nextTokenIs(SieveToken::LeftBracket))
 | |
|         {
 | |
|             $this->string_($parent_id, $semantics);
 | |
|             return;
 | |
|         }
 | |
| 
 | |
|         $token = $this->scanner_->nextToken();
 | |
|         $semantics->startStringList($token);
 | |
|         $this->tree_->addChildTo($parent_id, $token);
 | |
|         
 | |
|         if($this->scanner_->nextTokenIs(SieveToken::RightBracket)) {
 | |
|             //allow empty lists
 | |
|             $token = $this->scanner_->nextToken();
 | |
|             $this->tree_->addChildTo($parent_id, $token);
 | |
|             $semantics->endStringList();
 | |
|             return;
 | |
|         }
 | |
| 
 | |
|         do
 | |
|         {
 | |
|             $this->string_($parent_id, $semantics);
 | |
|             $token = $this->scanner_->nextToken();
 | |
| 
 | |
|             if (!$token->is(SieveToken::Comma|SieveToken::RightBracket))
 | |
|                 throw new SieveException($token, array(SieveToken::Comma, SieveToken::RightBracket));
 | |
| 
 | |
|             if ($token->is(SieveToken::Comma))
 | |
|                 $semantics->continueStringList();
 | |
| 
 | |
|             $this->tree_->addChildTo($parent_id, $token);
 | |
|         }
 | |
|         while (!$token->is(SieveToken::RightBracket));
 | |
| 
 | |
|         $semantics->endStringList();
 | |
|     }
 | |
| 
 | |
|     protected function string_($parent_id, &$semantics)
 | |
|     {
 | |
|         $token = $this->scanner_->nextToken();
 | |
|         $semantics->validateToken($token);
 | |
|         $this->tree_->addChildTo($parent_id, $token);
 | |
|     }
 | |
| 
 | |
|     protected function testlist_($parent_id, &$semantics)
 | |
|     {
 | |
|         if (!$this->scanner_->nextTokenIs(SieveToken::LeftParenthesis))
 | |
|         {
 | |
|             $this->test_($parent_id, $semantics);
 | |
|             return;
 | |
|         }
 | |
| 
 | |
|         $token = $this->scanner_->nextToken();
 | |
|         $semantics->validateToken($token);
 | |
|         $this->tree_->addChildTo($parent_id, $token);
 | |
| 
 | |
|         do
 | |
|         {
 | |
|             $this->test_($parent_id, $semantics);
 | |
| 
 | |
|             $token = $this->scanner_->nextToken();
 | |
|             if (!$token->is(SieveToken::Comma|SieveToken::RightParenthesis))
 | |
|             {
 | |
|                 throw new SieveException($token, array(SieveToken::Comma, SieveToken::RightParenthesis));
 | |
|             }
 | |
|             $this->tree_->addChildTo($parent_id, $token);
 | |
|         }
 | |
|         while (!$token->is(SieveToken::RightParenthesis));
 | |
|     }
 | |
| 
 | |
|     protected function test_($parent_id, &$semantics)
 | |
|     {
 | |
|         // Check if semantics allow an identifier
 | |
|         $token = $this->scanner_->nextToken();
 | |
|         $semantics->validateToken($token);
 | |
| 
 | |
|         // Get semantics for this test command
 | |
|         $this_semantics = new SieveSemantics($token, $this->getPrevToken_($parent_id));
 | |
|         $this_node = $this->tree_->addChildTo($parent_id, $token);
 | |
| 
 | |
|         // Consume eventual argument tokens
 | |
|         $this->arguments_($this_node, $this_semantics);
 | |
| 
 | |
|         // Check that all required arguments were there
 | |
|         $token = $this->scanner_->peekNextToken();
 | |
|         $this_semantics->done($token);
 | |
|     }
 | |
| 
 | |
|     protected function block_($parent_id, &$semantics)
 | |
|     {
 | |
|         $this->commands_($parent_id, $semantics);
 | |
| 
 | |
|         $token = $this->scanner_->nextToken();
 | |
|         if (!$token->is(SieveToken::BlockEnd))
 | |
|         {
 | |
|             throw new SieveException($token, SieveToken::BlockEnd);
 | |
|         }
 | |
|         $this->tree_->addChildTo($parent_id, $token);
 | |
|     }
 | |
| }
 | 
