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);
 | 
						|
    }
 | 
						|
}
 |