Location: PHPKode > projects > Content*Builder > cb_pear/Text/Highlighter/Generator.php
<?php
//
// +----------------------------------------------------------------------+
// | Copyright (c) 2004 The PHP Group                                     |
// +----------------------------------------------------------------------+
// | This source file is subject to version 3.0 of the PHP license,       |
// | that is bundled with this package in the file LICENSE, and is        |
// | available at through the world-wide-web at                           |
// | http://www.php.net/license/3_0.txt.                                  |
// | If you did not receive a copy of the PHP license and are unable to   |
// | obtain it through the world-wide-web, please send a note to          |
// | hide@address.com so we can mail you a copy immediately.               |
// +----------------------------------------------------------------------+
// | Author: Andrey Demenev <hide@address.com>                      |
// +----------------------------------------------------------------------+
// $Id: Generator.php,v 1.1 2004/05/21 10:48:19 cb_fog Exp $

/**
 * Syntax highlighter class generator
 *
 * To simplify the process of creating new syntax highlighters
 * for different languages, {@link Text_Highlighter_Generator} class is
 * provided. It takes highlighting rules from XML file and generates
 * a code of a class inherited from {@link Text_Highlighter}.
 *
 * @package Text_Highlighter
 */

/**
 * @ignore
 */
require_once 'PEAR.php';
require_once 'XML/Parser.php';

/**
 * Syntax highliter class generator class
 *
 * This class is used to generate PHP classes
 * from XML files with highlighting rules
 *
 * Usage example
 * <code>
 *require_once 'Text/Highlighter/Generator.php';
 *$generator =& new Text_Highlighter_Generator('php.xml');
 *$generator->generate();
 *$generator->saveCode('PHP.php');
 * </code>
 *
 * A command line script <b>generate</b> is provided for 
 * class generation (installs in scripts/Text/Highlighter).
 *
 * @see 
 * @package Text_Highlighter
 */
class Text_Highlighter_Generator extends  XML_Parser
{

    /**
     * Whether to do case folding.
     * We have to declare it here, because XML_Parser
     * sets case folding in constructor
     *
     * @var  boolean
     */
    var $folding = false;

    /**
     * Holds name of file with highlighting rules
     *
     * @var string
     * @access private
     */
     var $_syntaxFile;

    /**
     * Current element being processed
     *
     * @var array
     * @access private
     */
     var $_element;

    /**
     * List of regions
     *
     * @var array
     * @access private
     */
     var $_regions = array();

    /**
     * List of blocks
     *
     * @var array
     * @access private
     */
     var $_blocks = array();

    /**
     * List of keyword groups
     *
     * @var array
     * @access private
     */
     var $_keywords = array();

    /**
     * Name of language
     *
     * @var string
     * @access public
     */
     var $language = '';

    /**
     * Generated code
     *
     * @var string
     * @access private
     */
     var $_code = '';

    /**
     * Default color group
     *
     * @var string
     * @access private
     */
     var $_defcolor = 'default';

    /**
     * Custom color groups
     *
     * @var array
     * @access private
     */
     var $_colors = array();

    /**
     * PHP4 compatable constructor
     *
     * @param string $syntaxFile Name of XML file
     * with syntax highlighting rules
     *
     * @access public
     */

    function Text_Highlighter_Generator($syntaxFile = '')
    {
        return $this->__construct($syntaxFile);
    }
    
    /**
     * Constructor
     *
     * @param string $syntaxFile Name of XML file
     * with syntax highlighting rules
     *
     * @access public
     */

    function __construct($syntaxFile)
    {
        XML_Parser::XML_Parser(null, "func");
        $this->setInputFile($syntaxFile);
    }
    
    /**
     * Generates class code
     * 
     * @access public
     */

    function generate()
    {
        return $this->parse();
    }

    /**
     * Returns generated code as a string.
     * 
     * @return string Generated code
     * @access public
     */

    function getCode()
    {
        return $this->_code;
    }

    /**
     * Saves generated class to file. Note that {@link Text_Highlighter::factory()}
     * assumes that filename is uppercase (SQL.php, DTD.php, etc), and file
     * is located in Text/Highlighter
     * 
     * @param string $filename Name of file to write the code to
     * @return mixed true on success, PEAR error object on failure
     * @access public
     */

    function saveCode($filename)
    {
        $f = @fopen($filename, 'wb');
        if (!$f) {
            $erromessage = sprintf(
                'Unable to open file %s for writing.',
                $filename
            );
            return PEAR::raiseError($erromessage);
        }
        fwrite ($f, $this->_code);
        fclose($f);
        return true;
    }

    /**
     * Adds delimiters and modifiers to regular expression if necessary
     *
     * @param string $text Original RE
     * @return string Final RE
     * @access private
     */
    function _makeRE($text, $case = false)
    {
        if ($text{0} != '/') {
            $text = '/' . $text . '/';
        }
        if (!$case) $text .= 'i';
        return $text;
    }

    /**#@+
     * @access private
     * @param resource $xp      XML parser resource
     * @param string   $elem    XML element name
     * @param array    $attribs XML element attributes
     */

    /**
     * start handler for <default> element 
     */
    function xmltag_Default($xp, $elem, $attribs)
    {
        $this->_defcolor = $attribs['innerColor'];
    }

    /**
     * start handler for <region> element 
     */
    function xmltag_Region($xp, $elem, $attribs)
    {
        $this->_element = array('name' => $attribs['name']);
        if (isset($attribs['case'])) {
            $this->_element['case'] = $attribs['case'] == 'yes';
        } else {
            $this->_element['case'] = $this->_case;
        }
        $this->_element['innerColor'] = $attribs['innerColor'];
        $this->_element['delimColor'] = isset($attribs['delimColor']) ?
                                       $attribs['delimColor'] :
                                       $attribs['innerColor'];
        $this->_element['start'] = $this->_makeRE($attribs['start'], $this->_element['case']);
        $this->_element['end'] = $this->_makeRE($attribs['end'], $this->_element['case']);
        $this->_element['contained'] = @$attribs['contained'] == 'yes';
        $this->_element['never-contained'] = @$attribs['never-contained'] == 'yes';
        $this->_element['remember'] = @$attribs['remember'] == 'yes';
    }

    /**
     * start handler for <block> element 
     */
    function xmltag_Block($xp, $elem, $attribs)
    {
        $this->_element = array('name' => $attribs['name']);
        if (isset($attribs['case'])) {
            $this->_element['case'] = $attribs['case'] == 'yes';
        } else {
            $this->_element['case'] = $this->_case;
        }
        $this->_element['innerColor'] = $attribs['innerColor'];
        $this->_element['match'] = $this->_makeRE($attribs['match'], $this->_element['case']);
        $this->_element['contained'] = @$attribs['contained'] == 'yes';
        $this->_element['multiline'] = @$attribs['multiline'] == 'yes';
    }

    /**
     * start handler for <partcolor> element 
     */
    function xmltag_Partcolor($xp, $elem, $attribs)
    {
        $this->_element['partcolor'][$attribs['index']] = @$attribs['innerColor'];
    }
    /**
     * start handler for <keywords> element 
     */
    function xmltag_Keywords($xp, $elem, $attribs)
    {
        $this->_element = array('name'=>$attribs['name']);
        $this->_element['innerColor'] = $attribs['innerColor'];
        if (isset($attribs['case'])) {
            $this->_element['case'] = $attribs['case'] == 'yes';
        } else {
            $this->_element['case'] = $this->_case;
        }
        $this->_element['inherits'] = $attribs['inherits'];
        if (isset($attribs['otherwise'])) {
            $this->_element['otherwise'] = $attribs['otherwise'];
        }
        $this->_element['inherits'] = $attribs['inherits'];
    }

    /**
     * start handler for <keyword> element 
     */
    function xmltag_Keyword($xp, $elem, $attribs)
    {
        $keyword = $attribs['match'];
        if (!$this->_element['case']) {
            $keyword = strtolower($keyword);
        }
        $this->_element['match'][$keyword] = true;
    }

    /**
     * start handler for <colorgroup> element 
     */
    function xmltag_Colorgroup($xp, $elem, $attribs)
    {
        $this->_colors[] = $attribs['name'];
    }

    /**
     * start handler for <contains> element 
     */
    function xmltag_Contains($xp, $elem, $attribs)
    {
        $this->_element['contains-all'] = @$attribs['all'] == 'yes';
        if (isset($attribs['region'])) {
            $this->_element['contains']['region'][$attribs['region']] = true;
        }
        if (isset($attribs['block'])) {
            $this->_element['contains']['block'][$attribs['block']] = true;
        }
    }

    /**
     * start handler for <but> element 
     */
    function xmltag_But($xp, $elem, $attribs)
    {
        if (isset($attribs['region'])) {
            $this->_element['not-contains']['region'][$attribs['region']] = true;
        }
        if (isset($attribs['block'])) {
            $this->_element['not-contains']['block'][$attribs['block']] = true;
        }
    }

    /**
     * start handler for <onlyin> element 
     */
    function xmltag_Onlyin($xp, $elem, $attribs)
    {
        $this->_element['onlyin'][$attribs['region']] = true;
    }

    /**
     * start handler for <author> element 
     */
    function xmltag_Author($xp, $elem, $attribs)
    {
        $this->_authors[] = array(
                'name'  => $attribs['name'],
                'email' => $attribs['email']
                );
    }

    /**
     * start handler for <highlight> element 
     */
    function xmltag_Highlight($xp, $elem, $attribs)
    {
        $this->_code = '';
        $this->language = strtoupper($attribs['lang']);
        $this->_case = @$attribs['case'] == 'yes';
    }
    /**#@-*/

    /**#@+
     * @access private
     * @param resource $xp      XML parser resource
     * @param string   $elem    XML element name
     */
    /**
     * end handler for <region> element 
     */
    function xmltag_Region_($xp, $elem)
    {
        $this->_regions[$this->_element['name']] = $this->_element;
    }

    /**
     * end handler for <keywords> element 
     */
    function xmltag_Keywords_($xp, $elem)
    {
        $this->_keywords[$this->_element['name']] = $this->_element;
    }

    /**
     * end handler for <block> element 
     */
    function xmltag_Block_($xp, $elem)
    {
        $this->_blocks[$this->_element['name']] = $this->_element;
    }

    /**
     * end handler for <highlight> element 
     */
    function xmltag_Highlight_($xp, $elem)
    {
        $toplevel = array();
        foreach ($this->_blocks as $i => $current) {
            if (!$current['contained']) {
                $toplevel['blocks'][] = $i;
            }
        }
        foreach ($this->_regions as $i=>$current) {
            if (!$current['contained']) {
                $toplevel['regions'][] = $i;
            }
            foreach ($this->_regions as $j => $region) {
                if (isset($region['onlyin'])) {
                    $suits = isset($region['onlyin'][$current['name']]);
                } elseif (isset($current['not-contains']['region'][$region['name']])) {
                    $suits = false;
                } elseif (isset($current['contains']['region'][$region['name']])) {
                    $suits = true;
                } else {
                    $suits = @$current['contains-all'] && @!$region['never-contained'];
                }
                if ($suits) {
                    $this->_regions[$i]['lookfor']['regions'][] = $j;
                }
            }
            foreach ($this->_blocks as $j=>$region) {
                if (isset($region['onlyin'])) {
                    $suits = isset($region['onlyin'][$current['name']]);
                } elseif (isset($current['not-contains']['block'][$region['name']])) {
                    $suits = false;
                } elseif (isset($current['contains']['block'][$region['name']])) {
                    $suits = true;
                } else {
                    $suits = @$current['contains-all'] && @!$region['never-contained'];
                }
                if ($suits) {
                    $this->_regions[$i]['lookfor']['blocks'][] = $j;
                }
            }
        }
        foreach ($this->_blocks as $i=>$current) {
            unset ($this->_blocks[$i]['never-contained']);
            unset ($this->_blocks[$i]['contained']);
            unset ($this->_blocks[$i]['contains-all']);
            unset ($this->_blocks[$i]['contains']);
            unset ($this->_blocks[$i]['onlyin']);
        }

        foreach ($this->_regions as $i=>$current) {
            unset ($this->_regions[$i]['never-contained']);
            unset ($this->_regions[$i]['contained']);
            unset ($this->_regions[$i]['contains-all']);
            unset ($this->_regions[$i]['contains']);
            unset ($this->_regions[$i]['onlyin']);
        }

        $syntax=array(
            "regions"  => $this->_regions,
            "keywords" => $this->_keywords,
            "blocks"   => $this->_blocks,
            "toplevel" => $toplevel
        );
        $syntax['case'] = $this->_case;
        $syntax['defcolor'] = $this->_defcolor;
        $this->_code = <<<CODE
<?php
/**
 * Auto-generated class. {$this->language} syntax highlighting
 * @package Text_Highlighter
 *

CODE;
            foreach ($this->_authors as $author) {
                $this->_code .= " * @author {$author['name']}"; 
                if ($author['email']) {
                    $this->_code .= " <{$author['email']}>";
                }
                $this->_code .= "\n";
            }
            $this->_code .= <<<CODE
 */

/**
 * @ignore
 */

require_once 'Text/Highlighter.php';

/**
 * Auto-generated class. {$this->language} syntax highlighting
 *

CODE;
        if (count($this->_colors)) {
            $this->_code .= " *\n * Custom color groups defined by this highlighter :\n *\n";
            foreach ($this->_colors as $color) {
                $this->_code .= " * $color\n";
            }
            $this->_code .= " *\n";
        }
        foreach ($this->_authors as $author) {
            $this->_code .= " * @author {$author['name']}"; 
            if ($author['email']) {
                $this->_code .= " <{$author['email']}>";
            }
            $this->_code .= "\n";
        }

        
        $this->_code .= <<<CODE
 * @package Text_Highlighter
 */
class  Text_Highlighter_{$this->language} extends Text_Highlighter
{
CODE;
        ob_start();
        var_export($syntax);
        $array = ob_get_contents();
        ob_end_clean();
        $array = trim(preg_replace('~^(\s*)~m','        \1\1',$array));
        $this->_code .= <<<CODE
    /**
     * PHP4 Compatible Constructor
     *
     * @param array  \$options
     * @access public
     */
    function Text_Highlighter_{$this->language}(\$options=array())
    {
        \$this->__construct(\$options);
    }


    /**
     *  Constructor
     *
     * @param array  \$options
     * @access public
     */
    function __construct(\$options=array())
    {
        \$this->_syntax = $array;

CODE;
        $this->_code .= <<<CODE

        parent::_init(\$options);
    }
}

?>

CODE;
    }
    /**#@-*/

}

?>
Return current item: Content*Builder