home *** CD-ROM | disk | FTP | other *** search
/ Cricao de Sites - 650 Layouts Prontos / WebMasters.iso / Servidores / xampp-win32-1.6.7-installer.exe / php / PEAR / Text / Highlighter / Generator.php < prev    next >
Encoding:
PHP Script  |  2008-07-02  |  37.9 KB  |  1,292 lines

  1. <?php
  2. /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
  3. /**
  4. * Syntax highlighter class generator
  5. *
  6. * To simplify the process of creating new syntax highlighters
  7. * for different languages, {@link Text_Highlighter_Generator} class is
  8. * provided. It takes highlighting rules from XML file and generates
  9. * a code of a class inherited from {@link Text_Highlighter}.
  10. *
  11. * PHP versions 4 and 5
  12. *
  13. * LICENSE: This source file is subject to version 3.0 of the PHP license
  14. * that is available through the world-wide-web at the following URI:
  15. * http://www.php.net/license/3_0.txt.  If you did not receive a copy of
  16. * the PHP License and are unable to obtain it through the web, please
  17. * send a note to license@php.net so we can mail you a copy immediately.
  18. *
  19. * @category   Text
  20. * @package    Text_Highlighter
  21. * @author     Andrey Demenev <demenev@gmail.com>
  22. * @copyright  2004-2006 Andrey Demenev
  23. * @license    http://www.php.net/license/3_0.txt  PHP License
  24. * @version    CVS: $Id: Generator.php,v 1.1 2007/06/03 02:36:35 ssttoo Exp $
  25. * @link       http://pear.php.net/package/Text_Highlighter
  26. */
  27.  
  28. /**
  29. * @ignore
  30. */
  31. require_once 'PEAR.php';
  32. require_once 'XML/Parser.php';
  33.  
  34. // {{{ error codes
  35.  
  36. define ('TEXT_HIGHLIGHTER_EMPTY_RE',          1);
  37. define ('TEXT_HIGHLIGHTER_INVALID_RE',        2);
  38. define ('TEXT_HIGHLIGHTER_EMPTY_OR_MISSING',  3);
  39. define ('TEXT_HIGHLIGHTER_EMPTY',             4);
  40. define ('TEXT_HIGHLIGHTER_REGION_REGION',     5);
  41. define ('TEXT_HIGHLIGHTER_REGION_BLOCK',      6);
  42. define ('TEXT_HIGHLIGHTER_BLOCK_REGION',      7);
  43. define ('TEXT_HIGHLIGHTER_KEYWORD_BLOCK',     8);
  44. define ('TEXT_HIGHLIGHTER_KEYWORD_INHERITS',  9);
  45. define ('TEXT_HIGHLIGHTER_PARSE',            10);
  46. define ('TEXT_HIGHLIGHTER_FILE_WRITE',       11);
  47. define ('TEXT_HIGHLIGHTER_FILE_READ',        12);
  48. // }}}
  49.  
  50. /**
  51. * Syntax highliter class generator class
  52. *
  53. * This class is used to generate PHP classes
  54. * from XML files with highlighting rules
  55. *
  56. * Usage example
  57. * <code>
  58. *require_once 'Text/Highlighter/Generator.php';
  59. *$generator =& new Text_Highlighter_Generator('php.xml');
  60. *$generator->generate();
  61. *$generator->saveCode('PHP.php');
  62. * </code>
  63. *
  64. * A command line script <b>generate</b> is provided for
  65. * class generation (installs in scripts/Text/Highlighter).
  66. *
  67. * @author     Andrey Demenev <demenev@gmail.com>
  68. * @copyright  2004-2006 Andrey Demenev
  69. * @license    http://www.php.net/license/3_0.txt  PHP License
  70. * @version    Release: 0.7.1
  71. * @link       http://pear.php.net/package/Text_Highlighter
  72. */
  73.  
  74. class Text_Highlighter_Generator extends  XML_Parser
  75. {
  76.     // {{{ properties
  77.     /**
  78.     * Whether to do case folding.
  79.     * We have to declare it here, because XML_Parser
  80.     * sets case folding in constructor
  81.     *
  82.     * @var  boolean
  83.     */
  84.     var $folding = false;
  85.  
  86.     /**
  87.     * Holds name of file with highlighting rules
  88.     *
  89.     * @var string
  90.     * @access private
  91.     */
  92.     var $_syntaxFile;
  93.  
  94.     /**
  95.     * Current element being processed
  96.     *
  97.     * @var array
  98.     * @access private
  99.     */
  100.     var $_element;
  101.  
  102.     /**
  103.     * List of regions
  104.     *
  105.     * @var array
  106.     * @access private
  107.     */
  108.     var $_regions = array();
  109.  
  110.     /**
  111.     * List of blocks
  112.     *
  113.     * @var array
  114.     * @access private
  115.     */
  116.     var $_blocks = array();
  117.  
  118.     /**
  119.     * List of keyword groups
  120.     *
  121.     * @var array
  122.     * @access private
  123.     */
  124.     var $_keywords = array();
  125.  
  126.     /**
  127.     * List of authors
  128.     *
  129.     * @var array
  130.     * @access private
  131.     */
  132.     var $_authors = array();
  133.  
  134.     /**
  135.     * Name of language
  136.     *
  137.     * @var string
  138.     * @access public
  139.     */
  140.     var $language = '';
  141.  
  142.     /**
  143.     * Generated code
  144.     *
  145.     * @var string
  146.     * @access private
  147.     */
  148.     var $_code = '';
  149.  
  150.     /**
  151.     * Default class
  152.     *
  153.     * @var string
  154.     * @access private
  155.     */
  156.     var $_defClass = 'default';
  157.  
  158.     /**
  159.     * Comment
  160.     *
  161.     * @var string
  162.     * @access private
  163.     */
  164.     var $_comment = '';
  165.  
  166.     /**
  167.     * Flag for comment processing
  168.     *
  169.     * @var boolean
  170.     * @access private
  171.     */
  172.     var $_inComment = false;
  173.  
  174.     /**
  175.     * Sorting order of current block/region
  176.     *
  177.     * @var integer
  178.     * @access private
  179.     */
  180.     var $_blockOrder = 0;
  181.  
  182.     /**
  183.     * Generation errors
  184.     *
  185.     * @var array
  186.     * @access private
  187.     */
  188.     var $_errors;
  189.  
  190.     // }}}
  191.     // {{{ constructor
  192.  
  193.     /**
  194.     * PHP4 compatable constructor
  195.     *
  196.     * @param string $syntaxFile Name of XML file
  197.     * with syntax highlighting rules
  198.     *
  199.     * @access public
  200.     */
  201.  
  202.     function Text_Highlighter_Generator($syntaxFile = '')
  203.     {
  204.         return $this->__construct($syntaxFile);
  205.     }
  206.  
  207.     /**
  208.     * Constructor
  209.     *
  210.     * @param string $syntaxFile Name of XML file
  211.     * with syntax highlighting rules
  212.     *
  213.     * @access public
  214.     */
  215.  
  216.     function __construct($syntaxFile = '')
  217.     {
  218.         XML_Parser::XML_Parser(null, 'func');
  219.         $this->_errors = array();
  220.         $this->_declareErrorMessages();
  221.         if ($syntaxFile) {
  222.             $this->setInputFile($syntaxFile);
  223.         }
  224.     }
  225.  
  226.     // }}}
  227.     // {{{ _formatError
  228.  
  229.     /**
  230.     * Format error message
  231.     *
  232.     * @param int $code error code
  233.     * @param string $params parameters
  234.     * @param string $fileName file name
  235.     * @param int $lineNo line number
  236.     * @return  array
  237.     * @access  public
  238.     */
  239.     function _formatError($code, $params, $fileName, $lineNo)
  240.     {
  241.         $template = $this->_templates[$code];
  242.         $ret = call_user_func_array('sprintf', array_merge(array($template), $params));
  243.         if ($fileName) {
  244.             $ret = '[' . $fileName . '] ' . $ret;
  245.         }
  246.         if ($lineNo) {
  247.             $ret .= ' (line ' . $lineNo . ')';
  248.         }
  249.         return $ret;
  250.     }
  251.  
  252.     // }}}
  253.     // {{{ declareErrorMessages
  254.  
  255.     /**
  256.     * Set up error message templates
  257.     *
  258.     * @access  private
  259.     */
  260.     function _declareErrorMessages()
  261.     {
  262.         $this->_templates = array (
  263.         TEXT_HIGHLIGHTER_EMPTY_RE => 'Empty regular expression',
  264.         TEXT_HIGHLIGHTER_INVALID_RE => 'Invalid regular expression : %s',
  265.         TEXT_HIGHLIGHTER_EMPTY_OR_MISSING => 'Empty or missing %s',
  266.         TEXT_HIGHLIGHTER_EMPTY  => 'Empty %s',
  267.         TEXT_HIGHLIGHTER_REGION_REGION => 'Region %s refers undefined region %s',
  268.         TEXT_HIGHLIGHTER_REGION_BLOCK => 'Region %s refers undefined block %s',
  269.         TEXT_HIGHLIGHTER_BLOCK_REGION => 'Block %s refers undefined region %s',
  270.         TEXT_HIGHLIGHTER_KEYWORD_BLOCK => 'Keyword group %s refers undefined block %s',
  271.         TEXT_HIGHLIGHTER_KEYWORD_INHERITS => 'Keyword group %s inherits undefined block %s',
  272.         TEXT_HIGHLIGHTER_PARSE => '%s',
  273.         TEXT_HIGHLIGHTER_FILE_WRITE => 'Error writing file %s',
  274.         TEXT_HIGHLIGHTER_FILE_READ => '%s'
  275.         );
  276.     }
  277.  
  278.     // }}}
  279.     // {{{ setInputFile
  280.  
  281.     /**
  282.     * Sets the input xml file to be parsed
  283.     *
  284.     * @param    string      Filename (full path)
  285.     * @return   boolean
  286.     * @access   public
  287.     */
  288.     function setInputFile($file)
  289.     {
  290.         $this->_syntaxFile = $file;
  291.         $ret = parent::setInputFile($file);
  292.         if (PEAR::isError($ret)) {
  293.             $this->_error(TEXT_HIGHLIGHTER_FILE_READ, $ret->message);
  294.             return false;
  295.         }
  296.         return true;
  297.     }
  298.  
  299.     // }}}
  300.     // {{{ generate
  301.  
  302.     /**
  303.     * Generates class code
  304.     *
  305.     * @access public
  306.     */
  307.  
  308.     function generate()
  309.     {
  310.         $this->_regions    = array();
  311.         $this->_blocks     = array();
  312.         $this->_keywords   = array();
  313.         $this->language    = '';
  314.         $this->_code       = '';
  315.         $this->_defClass   = 'default';
  316.         $this->_comment    = '';
  317.         $this->_inComment  = false;
  318.         $this->_authors    = array();
  319.         $this->_blockOrder = 0;
  320.         $this->_errors   = array();
  321.  
  322.         $ret = $this->parse();
  323.         if (PEAR::isError($ret)) {
  324.             $this->_error(TEXT_HIGHLIGHTER_PARSE, $ret->message);
  325.             return false;
  326.         }
  327.         return true;
  328.     }
  329.  
  330.     // }}}
  331.     // {{{ getCode
  332.  
  333.     /**
  334.     * Returns generated code as a string.
  335.     *
  336.     * @return string Generated code
  337.     * @access public
  338.     */
  339.  
  340.     function getCode()
  341.     {
  342.         return $this->_code;
  343.     }
  344.  
  345.     // }}}
  346.     // {{{ saveCode
  347.  
  348.     /**
  349.     * Saves generated class to file. Note that {@link Text_Highlighter::factory()}
  350.     * assumes that filename is uppercase (SQL.php, DTD.php, etc), and file
  351.     * is located in Text/Highlighter
  352.     *
  353.     * @param string $filename Name of file to write the code to
  354.     * @return boolean true on success, false on failure
  355.     * @access public
  356.     */
  357.  
  358.     function saveCode($filename)
  359.     {
  360.         $f = @fopen($filename, 'wb');
  361.         if (!$f) {
  362.             $this->_error(TEXT_HIGHLIGHTER_FILE_WRITE, array('outfile'=>$filename));
  363.             return false;
  364.         }
  365.         fwrite ($f, $this->_code);
  366.         fclose($f);
  367.         return true;
  368.     }
  369.  
  370.     // }}}
  371.     // {{{ hasErrors
  372.  
  373.     /**
  374.     * Reports if there were errors
  375.     *
  376.     * @return boolean
  377.     * @access public
  378.     */
  379.  
  380.     function hasErrors()
  381.     {
  382.         return count($this->_errors) > 0;
  383.     }
  384.  
  385.     // }}}
  386.     // {{{ getErrors
  387.  
  388.     /**
  389.     * Returns errors
  390.     *
  391.     * @return array
  392.     * @access public
  393.     */
  394.  
  395.     function getErrors()
  396.     {
  397.         return $this->_errors;
  398.     }
  399.  
  400.     // }}}
  401.     // {{{ _sortBlocks
  402.  
  403.     /**
  404.     * Sorts blocks
  405.     *
  406.     * @access private
  407.     */
  408.  
  409.     function _sortBlocks($b1, $b2) {
  410.         return $b1['order'] - $b2['order'];
  411.     }
  412.  
  413.     // }}}
  414.     // {{{ _sortLookFor
  415.     /**
  416.     * Sort 'look for' list
  417.     * @return int
  418.     * @param string $b1
  419.     * @param string $b2
  420.     */
  421.     function _sortLookFor($b1, $b2) {
  422.         $o1 = isset($this->_blocks[$b1]) ? $this->_blocks[$b1]['order'] : $this->_regions[$b1]['order'];
  423.         $o2 = isset($this->_blocks[$b2]) ? $this->_blocks[$b2]['order'] : $this->_regions[$b2]['order'];
  424.         return $o1 - $o2;
  425.     }
  426.  
  427.     // }}}
  428.     // {{{ _makeRE
  429.  
  430.     /**
  431.     * Adds delimiters and modifiers to regular expression if necessary
  432.     *
  433.     * @param string $text Original RE
  434.     * @return string Final RE
  435.     * @access private
  436.     */
  437.     function _makeRE($text, $case = false)
  438.     {
  439.         if (!strlen($text)) {
  440.             $this->_error(TEXT_HIGHLIGHTER_EMPTY_RE);
  441.         }
  442.         if (!strlen($text) || $text{0} != '/') {
  443.             $text = '/' . $text . '/';
  444.         }
  445.         if (!$case) {
  446.             $text .= 'i';
  447.         }
  448.         $php_errormsg = '';
  449.         @preg_match($text, '');
  450.         if ($php_errormsg) {
  451.             $this->_error(TEXT_HIGHLIGHTER_INVALID_RE, $php_errormsg);
  452.         }
  453.         preg_match ('#^/(.+)/(.*)$#', $text, $m);
  454.         if (@$m[2]) {
  455.             $text = '(?' . $m[2] . ')' . $m[1];
  456.         } else {
  457.             $text = $m[1];
  458.         }
  459.         return $text;
  460.     }
  461.  
  462.     // }}}
  463.     // {{{ _exportArray
  464.  
  465.     /**
  466.     * Exports array as PHP code
  467.     *
  468.     * @param array $array
  469.     * @return string Code
  470.     * @access private
  471.     */
  472.     function _exportArray($array)
  473.     {
  474.         $array = var_export($array, true);
  475.         return trim(preg_replace('~^(\s*)~m','        \1\1',$array));
  476.     }
  477.  
  478.     // }}}
  479.     // {{{ _countSubpatterns
  480.     /**
  481.     * Find number of capturing suppaterns in regular expression
  482.     * @return int
  483.     * @param string $re Regular expression (without delimiters)
  484.     */
  485.     function _countSubpatterns($re)
  486.     {
  487.         preg_match_all('/' . $re . '/', '', $m);
  488.         return count($m)-1;
  489.     }
  490.  
  491.     // }}}
  492.  
  493.     /**#@+
  494.     * @access private
  495.     * @param resource $xp      XML parser resource
  496.     * @param string   $elem    XML element name
  497.     * @param array    $attribs XML element attributes
  498.     */
  499.  
  500.     // {{{ xmltag_Default
  501.  
  502.     /**
  503.     * start handler for <default> element
  504.     */
  505.     function xmltag_Default($xp, $elem, $attribs)
  506.     {
  507.         $this->_aliasAttributes($attribs);
  508.         if (!isset($attribs['innerGroup']) || $attribs['innerGroup'] === '') {
  509.             $this->_error(TEXT_HIGHLIGHTER_EMPTY_OR_MISSING, 'innerGroup');
  510.         }
  511.         $this->_defClass = @$attribs['innerGroup'];
  512.     }
  513.  
  514.     // }}}
  515.     // {{{ xmltag_Region
  516.  
  517.     /**
  518.     * start handler for <region> element
  519.     */
  520.     function xmltag_Region($xp, $elem, $attribs)
  521.     {
  522.         $this->_aliasAttributes($attribs);
  523.         if (!isset($attribs['name']) || $attribs['name'] === '') {
  524.             $this->_error(TEXT_HIGHLIGHTER_EMPTY_OR_MISSING, 'region name');
  525.         }
  526.         if (!isset($attribs['innerGroup']) || $attribs['innerGroup'] === '') {
  527.             $this->_error(TEXT_HIGHLIGHTER_EMPTY_OR_MISSING, 'innerGroup');
  528.         }
  529.         $this->_element = array('name' => $attribs['name']);
  530.         $this->_element['line'] = xml_get_current_line_number($this->parser);
  531.         if (isset($attribs['case'])) {
  532.             $this->_element['case'] = $attribs['case'] == 'yes';
  533.         } else {
  534.             $this->_element['case'] = $this->_case;
  535.         }
  536.         $this->_element['innerGroup'] = $attribs['innerGroup'];
  537.         $this->_element['delimGroup'] = isset($attribs['delimGroup']) ?
  538.         $attribs['delimGroup'] :
  539.         $attribs['innerGroup'];
  540.         $this->_element['start'] = $this->_makeRE(@$attribs['start'], $this->_element['case']);
  541.         $this->_element['end'] = $this->_makeRE(@$attribs['end'], $this->_element['case']);
  542.         $this->_element['contained'] = @$attribs['contained'] == 'yes';
  543.         $this->_element['never-contained'] = @$attribs['never-contained'] == 'yes';
  544.         $this->_element['remember'] = @$attribs['remember'] == 'yes';
  545.         if (isset($attribs['startBOL']) && $attribs['startBOL'] == 'yes') {
  546.             $this->_element['startBOL'] = true;
  547.         }
  548.         if (isset($attribs['endBOL']) && $attribs['endBOL'] == 'yes') {
  549.             $this->_element['endBOL'] = true;
  550.         }
  551.         if (isset($attribs['neverAfter'])) {
  552.             $this->_element['neverafter'] = $this->_makeRE($attribs['neverAfter']);
  553.         }
  554.     }
  555.  
  556.     // }}}
  557.     // {{{ xmltag_Block
  558.  
  559.     /**
  560.     * start handler for <block> element
  561.     */
  562.     function xmltag_Block($xp, $elem, $attribs)
  563.     {
  564.         $this->_aliasAttributes($attribs);
  565.         if (!isset($attribs['name']) || $attribs['name'] === '') {
  566.             $this->_error(TEXT_HIGHLIGHTER_EMPTY_OR_MISSING, 'block name');
  567.         }
  568.         if (isset($attribs['innerGroup']) && $attribs['innerGroup'] === '') {
  569.             $this->_error(TEXT_HIGHLIGHTER_EMPTY, 'innerGroup');
  570.         }
  571.         $this->_element = array('name' => $attribs['name']);
  572.         $this->_element['line'] = xml_get_current_line_number($this->parser);
  573.         if (isset($attribs['case'])) {
  574.             $this->_element['case'] = $attribs['case'] == 'yes';
  575.         } else {
  576.             $this->_element['case'] = $this->_case;
  577.         }
  578.         if (isset($attribs['innerGroup'])) {
  579.             $this->_element['innerGroup'] = @$attribs['innerGroup'];
  580.         }
  581.         $this->_element['match'] = $this->_makeRE($attribs['match'], $this->_element['case']);
  582.         $this->_element['contained'] = @$attribs['contained'] == 'yes';
  583.         $this->_element['multiline'] = @$attribs['multiline'] == 'yes';
  584.         if (isset($attribs['BOL']) && $attribs['BOL'] == 'yes') {
  585.             $this->_element['BOL'] = true;
  586.         }
  587.         if (isset($attribs['neverAfter'])) {
  588.             $this->_element['neverafter'] = $this->_makeRE($attribs['neverAfter']);
  589.         }
  590.     }
  591.  
  592.     // }}}
  593.     // {{{ cdataHandler
  594.  
  595.     /**
  596.     * Character data handler. Used for comment
  597.     */
  598.     function cdataHandler($xp, $cdata)
  599.     {
  600.         if ($this->_inComment) {
  601.             $this->_comment .= $cdata;
  602.         }
  603.     }
  604.  
  605.     // }}}
  606.     // {{{ xmltag_Comment
  607.  
  608.     /**
  609.     * start handler for <comment> element
  610.     */
  611.     function xmltag_Comment($xp, $elem, $attribs)
  612.     {
  613.         $this->_comment = '';
  614.         $this->_inComment = true;
  615.     }
  616.  
  617.     // }}}
  618.     // {{{ xmltag_PartGroup
  619.  
  620.     /**
  621.     * start handler for <partgroup> element
  622.     */
  623.     function xmltag_PartGroup($xp, $elem, $attribs)
  624.     {
  625.         $this->_aliasAttributes($attribs);
  626.         if (!isset($attribs['innerGroup']) || $attribs['innerGroup'] === '') {
  627.             $this->_error(TEXT_HIGHLIGHTER_EMPTY_OR_MISSING, 'innerGroup');
  628.         }
  629.         $this->_element['partClass'][$attribs['index']] = @$attribs['innerGroup'];
  630.     }
  631.  
  632.     // }}}
  633.     // {{{ xmltag_PartClass
  634.  
  635.     /**
  636.     * start handler for <partclass> element
  637.     */
  638.     function xmltag_PartClass($xp, $elem, $attribs)
  639.     {
  640.         $this->xmltag_PartGroup($xp, $elem, $attribs);
  641.     }
  642.  
  643.     // }}}
  644.     // {{{ xmltag_Keywords
  645.  
  646.     /**
  647.     * start handler for <keywords> element
  648.     */
  649.     function xmltag_Keywords($xp, $elem, $attribs)
  650.     {
  651.         $this->_aliasAttributes($attribs);
  652.         if (!isset($attribs['name']) || $attribs['name'] === '') {
  653.             $this->_error(TEXT_HIGHLIGHTER_EMPTY_OR_MISSING, 'keyword group name');
  654.         }
  655.         if (!isset($attribs['innerGroup']) || $attribs['innerGroup'] === '') {
  656.             $this->_error(TEXT_HIGHLIGHTER_EMPTY_OR_MISSING, 'innerGroup');
  657.         }
  658.         if (!isset($attribs['inherits']) || $attribs['inherits'] === '') {
  659.             $this->_error(TEXT_HIGHLIGHTER_EMPTY_OR_MISSING, 'inherits');
  660.         }
  661.         $this->_element = array('name'=>@$attribs['name']);
  662.         $this->_element['line'] = xml_get_current_line_number($this->parser);
  663.         $this->_element['innerGroup'] = @$attribs['innerGroup'];
  664.         if (isset($attribs['case'])) {
  665.             $this->_element['case'] = $attribs['case'] == 'yes';
  666.         } else {
  667.             $this->_element['case'] = $this->_case;
  668.         }
  669.         $this->_element['inherits'] = @$attribs['inherits'];
  670.         if (isset($attribs['otherwise'])) {
  671.             $this->_element['otherwise'] = $attribs['otherwise'];
  672.         }
  673.         if (isset($attribs['ifdef'])) {
  674.             $this->_element['ifdef'] = $attribs['ifdef'];
  675.         }
  676.         if (isset($attribs['ifndef'])) {
  677.             $this->_element['ifndef'] = $attribs['ifndef'];
  678.         }
  679.     }
  680.  
  681.     // }}}
  682.     // {{{ xmltag_Keyword
  683.  
  684.     /**
  685.     * start handler for <keyword> element
  686.     */
  687.     function xmltag_Keyword($xp, $elem, $attribs)
  688.     {
  689.         if (!isset($attribs['match']) || $attribs['match'] === '') {
  690.             $this->_error(TEXT_HIGHLIGHTER_EMPTY_OR_MISSING, 'match');
  691.         }
  692.         $keyword = @$attribs['match'];
  693.         if (!$this->_element['case']) {
  694.             $keyword = strtolower($keyword);
  695.         }
  696.         $this->_element['match'][$keyword] = true;
  697.     }
  698.  
  699.     // }}}
  700.     // {{{ xmltag_Contains
  701.  
  702.     /**
  703.     * start handler for <contains> element
  704.     */
  705.     function xmltag_Contains($xp, $elem, $attribs)
  706.     {
  707.         $this->_element['contains-all'] = @$attribs['all'] == 'yes';
  708.         if (isset($attribs['region'])) {
  709.             $this->_element['contains']['region'][$attribs['region']] =
  710.             xml_get_current_line_number($this->parser);
  711.         }
  712.         if (isset($attribs['block'])) {
  713.             $this->_element['contains']['block'][$attribs['block']] =
  714.             xml_get_current_line_number($this->parser);
  715.         }
  716.     }
  717.  
  718.     // }}}
  719.     // {{{ xmltag_But
  720.  
  721.     /**
  722.     * start handler for <but> element
  723.     */
  724.     function xmltag_But($xp, $elem, $attribs)
  725.     {
  726.         if (isset($attribs['region'])) {
  727.             $this->_element['not-contains']['region'][$attribs['region']] = true;
  728.         }
  729.         if (isset($attribs['block'])) {
  730.             $this->_element['not-contains']['block'][$attribs['block']] = true;
  731.         }
  732.     }
  733.  
  734.     // }}}
  735.     // {{{ xmltag_Onlyin
  736.  
  737.     /**
  738.     * start handler for <onlyin> element
  739.     */
  740.     function xmltag_Onlyin($xp, $elem, $attribs)
  741.     {
  742.         if (!isset($attribs['region']) || $attribs['region'] === '') {
  743.             $this->_error(TEXT_HIGHLIGHTER_EMPTY_OR_MISSING, 'region');
  744.         }
  745.         $this->_element['onlyin'][$attribs['region']] = xml_get_current_line_number($this->parser);
  746.     }
  747.  
  748.     // }}}
  749.     // {{{ xmltag_Author
  750.  
  751.     /**
  752.     * start handler for <author> element
  753.     */
  754.     function xmltag_Author($xp, $elem, $attribs)
  755.     {
  756.         if (!isset($attribs['name']) || $attribs['name'] === '') {
  757.             $this->_error(TEXT_HIGHLIGHTER_EMPTY_OR_MISSING, 'author name');
  758.         }
  759.         $this->_authors[] = array(
  760.         'name'  => @$attribs['name'],
  761.         'email' => (string)@$attribs['email']
  762.         );
  763.     }
  764.  
  765.     // }}}
  766.     // {{{ xmltag_Highlight
  767.  
  768.     /**
  769.     * start handler for <highlight> element
  770.     */
  771.     function xmltag_Highlight($xp, $elem, $attribs)
  772.     {
  773.         if (!isset($attribs['lang']) || $attribs['lang'] === '') {
  774.             $this->_error(TEXT_HIGHLIGHTER_EMPTY_OR_MISSING, 'language name');
  775.         }
  776.         $this->_code = '';
  777.         $this->language = strtoupper(@$attribs['lang']);
  778.         $this->_case = @$attribs['case'] == 'yes';
  779.     }
  780.  
  781.     // }}}
  782.  
  783.     /**#@-*/
  784.  
  785.     // {{{ _error
  786.  
  787.     /**
  788.     * Add an error message
  789.     *
  790.     * @param integer $code Error code
  791.     * @param mixed   $message Error message or array with error message parameters
  792.     * @param integer $lineNo Source code line number
  793.     * @access private
  794.     */
  795.     function _error($code, $params = array(), $lineNo = 0)
  796.     {
  797.         if (!$lineNo && !empty($this->parser)) {
  798.             $lineNo = xml_get_current_line_number($this->parser);
  799.         }
  800.         $this->_errors[] = $this->_formatError($code, $params, $this->_syntaxFile, $lineNo);
  801.     }
  802.  
  803.     // }}}
  804.     // {{{ _aliasAttributes
  805.  
  806.     /**
  807.     * BC trick
  808.     *
  809.     * @param array $attrs attributes
  810.     */
  811.     function _aliasAttributes(&$attrs)
  812.     {
  813.         if (isset($attrs['innerClass']) && !isset($attrs['innerGroup'])) {
  814.             $attrs['innerGroup'] = $attrs['innerClass'];
  815.         }
  816.         if (isset($attrs['delimClass']) && !isset($attrs['delimGroup'])) {
  817.             $attrs['delimGroup'] = $attrs['delimClass'];
  818.         }
  819.         if (isset($attrs['partClass']) && !isset($attrs['partGroup'])) {
  820.             $attrs['partGroup'] = $attrs['partClass'];
  821.         }
  822.     }
  823.  
  824.     // }}}
  825.  
  826.     /**#@+
  827.     * @access private
  828.     * @param resource $xp      XML parser resource
  829.     * @param string   $elem    XML element name
  830.     */
  831.  
  832.     // {{{ xmltag_Comment_
  833.  
  834.     /**
  835.     * end handler for <comment> element
  836.     */
  837.     function xmltag_Comment_($xp, $elem)
  838.     {
  839.         $this->_inComment = false;
  840.     }
  841.  
  842.     // }}}
  843.     // {{{ xmltag_Region_
  844.  
  845.     /**
  846.     * end handler for <region> element
  847.     */
  848.     function xmltag_Region_($xp, $elem)
  849.     {
  850.         $this->_element['type'] = 'region';
  851.         $this->_element['order'] = $this->_blockOrder ++;
  852.         $this->_regions[$this->_element['name']] = $this->_element;
  853.     }
  854.  
  855.     // }}}
  856.     // {{{ xmltag_Keywords_
  857.  
  858.     /**
  859.     * end handler for <keywords> element
  860.     */
  861.     function xmltag_Keywords_($xp, $elem)
  862.     {
  863.         $this->_keywords[$this->_element['name']] = $this->_element;
  864.     }
  865.  
  866.     // }}}
  867.     // {{{ xmltag_Block_
  868.  
  869.     /**
  870.     * end handler for <block> element
  871.     */
  872.     function xmltag_Block_($xp, $elem)
  873.     {
  874.         $this->_element['type'] = 'block';
  875.         $this->_element['order'] = $this->_blockOrder ++;
  876.         $this->_blocks[$this->_element['name']] = $this->_element;
  877.     }
  878.  
  879.     // }}}
  880.     // {{{ xmltag_Highlight_
  881.  
  882.     /**
  883.     * end handler for <highlight> element
  884.     */
  885.     function xmltag_Highlight_($xp, $elem)
  886.     {
  887.         $conditions = array();
  888.         $toplevel = array();
  889.         foreach ($this->_blocks as $i => $current) {
  890.             if (!$current['contained'] && !isset($current['onlyin'])) {
  891.                 $toplevel[] = $i;
  892.             }
  893.             foreach ((array)@$current['onlyin'] as $region => $lineNo) {
  894.                 if (!isset($this->_regions[$region])) {
  895.                     $this->_error(TEXT_HIGHLIGHTER_BLOCK_REGION,
  896.                     array(
  897.                     'block' => $current['name'],
  898.                     'region' => $region
  899.                     ));
  900.                 }
  901.             }
  902.         }
  903.         foreach ($this->_regions as $i=>$current) {
  904.             if (!$current['contained'] && !isset($current['onlyin'])) {
  905.                 $toplevel[] = $i;
  906.             }
  907.             foreach ((array)@$current['contains']['region'] as $region => $lineNo) {
  908.                 if (!isset($this->_regions[$region])) {
  909.                     $this->_error(TEXT_HIGHLIGHTER_REGION_REGION,
  910.                     array(
  911.                     'region1' => $current['name'],
  912.                     'region2' => $region
  913.                     ));
  914.                 }
  915.             }
  916.             foreach ((array)@$current['contains']['block'] as $region => $lineNo) {
  917.                 if (!isset($this->_blocks[$region])) {
  918.                     $this->_error(TEXT_HIGHLIGHTER_REGION_BLOCK,
  919.                     array(
  920.                     'block' => $current['name'],
  921.                     'region' => $region
  922.                     ));
  923.                 }
  924.             }
  925.             foreach ((array)@$current['onlyin'] as $region => $lineNo) {
  926.                 if (!isset($this->_regions[$region])) {
  927.                     $this->_error(TEXT_HIGHLIGHTER_REGION_REGION,
  928.                     array(
  929.                     'region1' => $current['name'],
  930.                     'region2' => $region
  931.                     ));
  932.                 }
  933.             }
  934.             foreach ($this->_regions as $j => $region) {
  935.                 if (isset($region['onlyin'])) {
  936.                     $suits = isset($region['onlyin'][$current['name']]);
  937.                 } elseif (isset($current['not-contains']['region'][$region['name']])) {
  938.                     $suits = false;
  939.                 } elseif (isset($current['contains']['region'][$region['name']])) {
  940.                     $suits = true;
  941.                 } else {
  942.                     $suits = @$current['contains-all'] && @!$region['never-contained'];
  943.                 }
  944.                 if ($suits) {
  945.                     $this->_regions[$i]['lookfor'][] = $j;
  946.                 }
  947.             }
  948.             foreach ($this->_blocks as $j=>$region) {
  949.                 if (isset($region['onlyin'])) {
  950.                     $suits = isset($region['onlyin'][$current['name']]);
  951.                 } elseif (isset($current['not-contains']['block'][$region['name']])) {
  952.                     $suits = false;
  953.                 } elseif (isset($current['contains']['block'][$region['name']])) {
  954.                     $suits = true;
  955.                 } else {
  956.                     $suits = @$current['contains-all'] && @!$region['never-contained'];
  957.                 }
  958.                 if ($suits) {
  959.                     $this->_regions[$i]['lookfor'][] = $j;
  960.                 }
  961.             }
  962.         }
  963.         foreach ($this->_blocks as $i=>$current) {
  964.             unset ($this->_blocks[$i]['never-contained']);
  965.             unset ($this->_blocks[$i]['contained']);
  966.             unset ($this->_blocks[$i]['contains-all']);
  967.             unset ($this->_blocks[$i]['contains']);
  968.             unset ($this->_blocks[$i]['onlyin']);
  969.             unset ($this->_blocks[$i]['line']);
  970.         }
  971.  
  972.         foreach ($this->_regions as $i=>$current) {
  973.             unset ($this->_regions[$i]['never-contained']);
  974.             unset ($this->_regions[$i]['contained']);
  975.             unset ($this->_regions[$i]['contains-all']);
  976.             unset ($this->_regions[$i]['contains']);
  977.             unset ($this->_regions[$i]['onlyin']);
  978.             unset ($this->_regions[$i]['line']);
  979.         }
  980.  
  981.         foreach ($this->_keywords as $name => $keyword) {
  982.             if (isset($keyword['ifdef'])) {
  983.                 $conditions[$keyword['ifdef']][] = array($name, true);
  984.             }
  985.             if (isset($keyword['ifndef'])) {
  986.                 $conditions[$keyword['ifndef']][] = array($name, false);
  987.             }
  988.             unset($this->_keywords[$name]['line']);
  989.             if (!isset($this->_blocks[$keyword['inherits']])) {
  990.                 $this->_error(TEXT_HIGHLIGHTER_KEYWORD_INHERITS,
  991.                 array(
  992.                 'keyword' => $keyword['name'],
  993.                 'block' => $keyword['inherits']
  994.                 ));
  995.             }
  996.             if (isset($keyword['otherwise']) && !isset($this->_blocks[$keyword['otherwise']]) ) {
  997.                 $this->_error(TEXT_HIGHLIGHTER_KEYWORD_BLOCK,
  998.                 array(
  999.                 'keyword' => $keyword['name'],
  1000.                 'block' => $keyword['inherits']
  1001.                 ));
  1002.             }
  1003.         }
  1004.  
  1005.         $syntax=array(
  1006.         'keywords'   => $this->_keywords,
  1007.         'blocks'     => array_merge($this->_blocks, $this->_regions),
  1008.         'toplevel'   => $toplevel,
  1009.         );
  1010.         uasort($syntax['blocks'], array(&$this, '_sortBlocks'));
  1011.         foreach ($syntax['blocks'] as $name => $block) {
  1012.             if ($block['type'] == 'block') {
  1013.                 continue;
  1014.             }
  1015.             if (is_array(@$syntax['blocks'][$name]['lookfor'])) {
  1016.                 usort($syntax['blocks'][$name]['lookfor'], array(&$this, '_sortLookFor'));
  1017.             }
  1018.         }
  1019.         usort($syntax['toplevel'], array(&$this, '_sortLookFor'));
  1020.         $syntax['case'] = $this->_case;
  1021.         $this->_code = <<<CODE
  1022. <?php
  1023. /**
  1024.  * Auto-generated class. {$this->language} syntax highlighting
  1025. CODE;
  1026.  
  1027.         if ($this->_comment) {
  1028.             $comment = preg_replace('~^~m',' * ',$this->_comment);
  1029.             $this->_code .= "\n * \n" . $comment;
  1030.         }
  1031.  
  1032.         $this->_code .= <<<CODE
  1033.  
  1034.  *
  1035.  * PHP version 4 and 5
  1036.  *
  1037.  * LICENSE: This source file is subject to version 3.0 of the PHP license
  1038.  * that is available through the world-wide-web at the following URI:
  1039.  * http://www.php.net/license/3_0.txt.  If you did not receive a copy of
  1040.  * the PHP License and are unable to obtain it through the web, please
  1041.  * send a note to license@php.net so we can mail you a copy immediately.
  1042.  *
  1043.  * @copyright  2004-2006 Andrey Demenev
  1044.  * @license    http://www.php.net/license/3_0.txt  PHP License
  1045.  * @link       http://pear.php.net/package/Text_Highlighter
  1046.  * @category   Text
  1047.  * @package    Text_Highlighter
  1048.  * @version    generated from: $this->_syntaxFile
  1049.  
  1050. CODE;
  1051.  
  1052.         foreach ($this->_authors as $author) {
  1053.             $this->_code .= ' * @author ' . $author['name'];
  1054.             if ($author['email']) {
  1055.                 $this->_code .= ' <' . $author['email'] . '>';
  1056.             }
  1057.             $this->_code .= "\n";
  1058.         }
  1059.  
  1060.         $this->_code .= <<<CODE
  1061.  *
  1062.  */
  1063.  
  1064. /**
  1065.  * @ignore
  1066.  */
  1067.  
  1068. require_once 'Text/Highlighter.php';
  1069.  
  1070. /**
  1071.  * Auto-generated class. {$this->language} syntax highlighting
  1072.  *
  1073.  
  1074. CODE;
  1075.         foreach ($this->_authors as $author) {
  1076.             $this->_code .= ' * @author ' . $author['name'];
  1077.             if ($author['email']) {
  1078.                 $this->_code .= ' <' . $author['email']. '>';
  1079.             }
  1080.             $this->_code .= "\n";
  1081.         }
  1082.  
  1083.  
  1084.         $this->_code .= <<<CODE
  1085.  * @category   Text
  1086.  * @package    Text_Highlighter
  1087.  * @copyright  2004-2006 Andrey Demenev
  1088.  * @license    http://www.php.net/license/3_0.txt  PHP License
  1089.  * @version    Release: 0.7.1
  1090.  * @link       http://pear.php.net/package/Text_Highlighter
  1091.  */
  1092. class  Text_Highlighter_{$this->language} extends Text_Highlighter
  1093. {
  1094.     
  1095. CODE;
  1096.         $this->_code .= 'var $_language = \'' . strtolower($this->language) . "';\n\n";
  1097.         $array = var_export($syntax, true);
  1098.         $array = trim(preg_replace('~^(\s*)~m','        \1\1',$array));
  1099.         //        \$this->_syntax = $array;
  1100.         $this->_code .= <<<CODE
  1101.     /**
  1102.      * PHP4 Compatible Constructor
  1103.      *
  1104.      * @param array  \$options
  1105.      * @access public
  1106.      */
  1107.     function Text_Highlighter_{$this->language}(\$options=array())
  1108.     {
  1109.         \$this->__construct(\$options);
  1110.     }
  1111.  
  1112.  
  1113.     /**
  1114.      *  Constructor
  1115.      *
  1116.      * @param array  \$options
  1117.      * @access public
  1118.      */
  1119.     function __construct(\$options=array())
  1120.     {
  1121.  
  1122. CODE;
  1123.         $this->_code .= <<<CODE
  1124.  
  1125.         \$this->_options = \$options;
  1126. CODE;
  1127.         $states = array();
  1128.         $i = 0;
  1129.         foreach ($syntax['blocks'] as $name => $block) {
  1130.             if ($block['type'] == 'region') {
  1131.                 $states[$name] = $i++;
  1132.             }
  1133.         }
  1134.         $regs = array();
  1135.         $counts = array();
  1136.         $delim = array();
  1137.         $inner = array();
  1138.         $end = array();
  1139.         $stat = array();
  1140.         $keywords = array();
  1141.         $parts = array();
  1142.         $kwmap = array();
  1143.         $subst = array();
  1144.         $re = array();
  1145.         $ce = array();
  1146.         $rd = array();
  1147.         $in = array();
  1148.         $st = array();
  1149.         $kw = array();
  1150.         $sb = array();
  1151.         foreach ($syntax['toplevel'] as $name) {
  1152.             $block = $syntax['blocks'][$name];
  1153.             if ($block['type'] == 'block') {
  1154.                 $kwm = array();
  1155.                 $re[] = '(' . $block['match'] . ')';
  1156.                 $ce[] = $this->_countSubpatterns($block['match']);
  1157.                 $rd[] = '';
  1158.                 $sb[] = false;;
  1159.                 $st[] = -1;
  1160.                 foreach ($syntax['keywords'] as $kwname => $kwgroup) {
  1161.                     if ($kwgroup['inherits'] != $name) {
  1162.                         continue;
  1163.                     }
  1164.                     $gre = implode('|', array_keys($kwgroup['match']));
  1165.                     if (!$kwgroup['case']) {
  1166.                         $gre = '(?i)' . $gre;
  1167.                     }
  1168.                     $kwm[$kwname][] =  $gre;
  1169.                     $kwmap[$kwname] = $kwgroup['innerGroup'];
  1170.                 }
  1171.                 foreach ($kwm as $g => $ma) {
  1172.                     $kwm[$g] = '/^(' . implode(')|(', $ma) . ')$/';
  1173.                 }
  1174.                 $kw[] = $kwm;
  1175.             } else {
  1176.                 $kw[] = -1;
  1177.                 $re[] = '(' . $block['start'] . ')';
  1178.                 $ce[] = $this->_countSubpatterns($block['start']);
  1179.                 $rd[] = $block['delimGroup'];
  1180.                 $st[] = $states[$name];
  1181.                 $sb[] = $block['remember'];
  1182.             }
  1183.             $in[] = $block['innerGroup'];
  1184.         }
  1185.         $re = implode('|', $re);
  1186.         $regs[-1] = '/' . $re . '/';
  1187.         $counts[-1] = $ce;
  1188.         $delim[-1] = $rd;
  1189.         $inner[-1] = $in;
  1190.         $stat[-1] = $st;
  1191.         $keywords[-1] = $kw;
  1192.         $subst[-1] = $sb;
  1193.  
  1194.         foreach ($syntax['blocks'] as $ablock) {
  1195.             if ($ablock['type'] != 'region') {
  1196.                 continue;
  1197.             }
  1198.             $end[] = '/' . $ablock['end'] . '/';
  1199.             $re = array();
  1200.             $ce = array();
  1201.             $rd = array();
  1202.             $in = array();
  1203.             $st = array();
  1204.             $kw = array();
  1205.             $pc = array();
  1206.             $sb = array();
  1207.             foreach ((array)@$ablock['lookfor'] as $name) {
  1208.                 $block = $syntax['blocks'][$name];
  1209.                 if (isset($block['partClass'])) {
  1210.                     $pc[] = $block['partClass'];
  1211.                 } else {
  1212.                     $pc[] = null;
  1213.                 }
  1214.                 if ($block['type'] == 'block') {
  1215.                     $kwm = array();;
  1216.                     $re[] = '(' . $block['match'] . ')';
  1217.                     $ce[] = $this->_countSubpatterns($block['match']);
  1218.                     $rd[] = '';
  1219.                     $sb[] = false;
  1220.                     $st[] = -1;
  1221.                     foreach ($syntax['keywords'] as $kwname => $kwgroup) {
  1222.                         if ($kwgroup['inherits'] != $name) {
  1223.                             continue;
  1224.                         }
  1225.                         $gre = implode('|', array_keys($kwgroup['match']));
  1226.                         if (!$kwgroup['case']) {
  1227.                             $gre = '(?i)' . $gre;
  1228.                         }
  1229.                         $kwm[$kwname][] =  $gre;
  1230.                         $kwmap[$kwname] = $kwgroup['innerGroup'];
  1231.                     }
  1232.                     foreach ($kwm as $g => $ma) {
  1233.                         $kwm[$g] = '/^(' . implode(')|(', $ma) . ')$/';
  1234.                     }
  1235.                     $kw[] = $kwm;
  1236.                 } else {
  1237.                     $sb[] = $block['remember'];
  1238.                     $kw[] = -1;
  1239.                     $re[] = '(' . $block['start'] . ')';
  1240.                     $ce[] = $this->_countSubpatterns($block['start']);
  1241.                     $rd[] = $block['delimGroup'];
  1242.                     $st[] = $states[$name];
  1243.                 }
  1244.                 $in[] = $block['innerGroup'];
  1245.             }
  1246.             $re = implode('|', $re);
  1247.             $regs[] = '/' . $re . '/';
  1248.             $counts[] = $ce;
  1249.             $delim[] = $rd;
  1250.             $inner[] = $in;
  1251.             $stat[] = $st;
  1252.             $keywords[] = $kw;
  1253.             $parts[] = $pc;
  1254.             $subst[] = $sb;
  1255.         }
  1256.  
  1257.  
  1258.         $this->_code .= "\n        \$this->_regs = " . $this->_exportArray($regs);
  1259.         $this->_code .= ";\n        \$this->_counts = " .$this->_exportArray($counts);
  1260.         $this->_code .= ";\n        \$this->_delim = " .$this->_exportArray($delim);
  1261.         $this->_code .= ";\n        \$this->_inner = " .$this->_exportArray($inner);
  1262.         $this->_code .= ";\n        \$this->_end = " .$this->_exportArray($end);
  1263.         $this->_code .= ";\n        \$this->_states = " .$this->_exportArray($stat);
  1264.         $this->_code .= ";\n        \$this->_keywords = " .$this->_exportArray($keywords);
  1265.         $this->_code .= ";\n        \$this->_parts = " .$this->_exportArray($parts);
  1266.         $this->_code .= ";\n        \$this->_subst = " .$this->_exportArray($subst);
  1267.         $this->_code .= ";\n        \$this->_conditions = " .$this->_exportArray($conditions);
  1268.         $this->_code .= ";\n        \$this->_kwmap = " .$this->_exportArray($kwmap);
  1269.         $this->_code .= ";\n        \$this->_defClass = '" .$this->_defClass . '\'';
  1270.         $this->_code .= <<<CODE
  1271. ;
  1272.         \$this->_checkDefines();
  1273.     }
  1274.     
  1275. }
  1276. CODE;
  1277. }
  1278.  
  1279. // }}}
  1280. }
  1281.  
  1282.  
  1283. /*
  1284. * Local variables:
  1285. * tab-width: 4
  1286. * c-basic-offset: 4
  1287. * c-hanging-comment-ender-p: nil
  1288. * End:
  1289. */
  1290.  
  1291. ?>
  1292.