* @copyright 2004-2006 Andrey Demenev * @license http://www.php.net/license/3_0.txt PHP License * @version CVS: $Id$ * @link http://pear.php.net/package/Text_Highlighter */ /** * @ignore */ require_once 'PEAR.php'; require_once 'XML/Parser.php'; // {{{ error codes define ('TEXT_HIGHLIGHTER_EMPTY_RE', 1); define ('TEXT_HIGHLIGHTER_INVALID_RE', 2); define ('TEXT_HIGHLIGHTER_EMPTY_OR_MISSING', 3); define ('TEXT_HIGHLIGHTER_EMPTY', 4); define ('TEXT_HIGHLIGHTER_REGION_REGION', 5); define ('TEXT_HIGHLIGHTER_REGION_BLOCK', 6); define ('TEXT_HIGHLIGHTER_BLOCK_REGION', 7); define ('TEXT_HIGHLIGHTER_KEYWORD_BLOCK', 8); define ('TEXT_HIGHLIGHTER_KEYWORD_INHERITS', 9); define ('TEXT_HIGHLIGHTER_PARSE', 10); define ('TEXT_HIGHLIGHTER_FILE_WRITE', 11); define ('TEXT_HIGHLIGHTER_FILE_READ', 12); // }}} /** * Syntax highliter class generator class * * This class is used to generate PHP classes * from XML files with highlighting rules * * Usage example * *require_once 'Text/Highlighter/Generator.php'; *$generator = new Text_Highlighter_Generator('php.xml'); *$generator->generate(); *$generator->saveCode('PHP.php'); * * * A command line script generate is provided for * class generation (installs in scripts/Text/Highlighter). * * @author Andrey Demenev * @copyright 2004-2006 Andrey Demenev * @license http://www.php.net/license/3_0.txt PHP License * @version Release: @package_version@ * @link http://pear.php.net/package/Text_Highlighter */ class Text_Highlighter_Generator extends XML_Parser { // {{{ properties /** * 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(); /** * List of authors * * @var array * @access private */ var $_authors = array(); /** * Name of language * * @var string * @access public */ var $language = ''; /** * Generated code * * @var string * @access private */ var $_code = ''; /** * Default class * * @var string * @access private */ var $_defClass = 'default'; /** * Comment * * @var string * @access private */ var $_comment = ''; /** * Flag for comment processing * * @var boolean * @access private */ var $_inComment = false; /** * Sorting order of current block/region * * @var integer * @access private */ var $_blockOrder = 0; /** * Generation errors * * @var array * @access private */ var $_errors; // }}} // {{{ constructor /** * 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->_errors = array(); $this->_declareErrorMessages(); if ($syntaxFile) { $this->setInputFile($syntaxFile); } } // }}} // {{{ _formatError /** * Format error message * * @param int $code error code * @param string $params parameters * @param string $fileName file name * @param int $lineNo line number * @return array * @access public */ function _formatError($code, $params, $fileName, $lineNo) { $template = $this->_templates[$code]; $ret = call_user_func_array('sprintf', array_merge(array($template), $params)); if ($fileName) { $ret = '[' . $fileName . '] ' . $ret; } if ($lineNo) { $ret .= ' (line ' . $lineNo . ')'; } return $ret; } // }}} // {{{ declareErrorMessages /** * Set up error message templates * * @access private */ function _declareErrorMessages() { $this->_templates = array ( TEXT_HIGHLIGHTER_EMPTY_RE => 'Empty regular expression', TEXT_HIGHLIGHTER_INVALID_RE => 'Invalid regular expression : %s', TEXT_HIGHLIGHTER_EMPTY_OR_MISSING => 'Empty or missing %s', TEXT_HIGHLIGHTER_EMPTY => 'Empty %s', TEXT_HIGHLIGHTER_REGION_REGION => 'Region %s refers undefined region %s', TEXT_HIGHLIGHTER_REGION_BLOCK => 'Region %s refers undefined block %s', TEXT_HIGHLIGHTER_BLOCK_REGION => 'Block %s refers undefined region %s', TEXT_HIGHLIGHTER_KEYWORD_BLOCK => 'Keyword group %s refers undefined block %s', TEXT_HIGHLIGHTER_KEYWORD_INHERITS => 'Keyword group %s inherits undefined block %s', TEXT_HIGHLIGHTER_PARSE => '%s', TEXT_HIGHLIGHTER_FILE_WRITE => 'Error writing file %s', TEXT_HIGHLIGHTER_FILE_READ => '%s' ); } // }}} // {{{ setInputFile /** * Sets the input xml file to be parsed * * @param string Filename (full path) * @return boolean * @access public */ function setInputFile($file) { $this->_syntaxFile = $file; $ret = parent::setInputFile($file); if (PEAR::isError($ret)) { $this->_error(TEXT_HIGHLIGHTER_FILE_READ, $ret->message); return false; } return true; } // }}} // {{{ generate /** * Generates class code * * @access public */ function generate() { $this->_regions = array(); $this->_blocks = array(); $this->_keywords = array(); $this->language = ''; $this->_code = ''; $this->_defClass = 'default'; $this->_comment = ''; $this->_inComment = false; $this->_authors = array(); $this->_blockOrder = 0; $this->_errors = array(); $ret = $this->parse(); if (PEAR::isError($ret)) { $this->_error(TEXT_HIGHLIGHTER_PARSE, $ret->message); return false; } return true; } // }}} // {{{ getCode /** * Returns generated code as a string. * * @return string Generated code * @access public */ function getCode() { return $this->_code; } // }}} // {{{ saveCode /** * 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 boolean true on success, false on failure * @access public */ function saveCode($filename) { $f = @fopen($filename, 'wb'); if (!$f) { $this->_error(TEXT_HIGHLIGHTER_FILE_WRITE, array('outfile'=>$filename)); return false; } fwrite ($f, $this->_code); fclose($f); return true; } // }}} // {{{ hasErrors /** * Reports if there were errors * * @return boolean * @access public */ function hasErrors() { return count($this->_errors) > 0; } // }}} // {{{ getErrors /** * Returns errors * * @return array * @access public */ function getErrors() { return $this->_errors; } // }}} // {{{ _sortBlocks /** * Sorts blocks * * @access private */ function _sortBlocks($b1, $b2) { return $b1['order'] - $b2['order']; } // }}} // {{{ _sortLookFor /** * Sort 'look for' list * @return int * @param string $b1 * @param string $b2 */ function _sortLookFor($b1, $b2) { $o1 = isset($this->_blocks[$b1]) ? $this->_blocks[$b1]['order'] : $this->_regions[$b1]['order']; $o2 = isset($this->_blocks[$b2]) ? $this->_blocks[$b2]['order'] : $this->_regions[$b2]['order']; return $o1 - $o2; } // }}} // {{{ _makeRE /** * 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 (!strlen($text)) { $this->_error(TEXT_HIGHLIGHTER_EMPTY_RE); } if (!strlen($text) || $text{0} != '/') { $text = '/' . $text . '/'; } if (!$case) { $text .= 'i'; } $php_errormsg = ''; @preg_match($text, ''); if ($php_errormsg) { $this->_error(TEXT_HIGHLIGHTER_INVALID_RE, $php_errormsg); } preg_match ('#^/(.+)/(.*)$#', $text, $m); if (@$m[2]) { $text = '(?' . $m[2] . ')' . $m[1]; } else { $text = $m[1]; } return $text; } // }}} // {{{ _exportArray /** * Exports array as PHP code * * @param array $array * @return string Code * @access private */ function _exportArray($array) { $array = var_export($array, true); return trim(preg_replace('~^(\s*)~m',' \1\1',$array)); } // }}} // {{{ _countSubpatterns /** * Find number of capturing suppaterns in regular expression * @return int * @param string $re Regular expression (without delimiters) */ function _countSubpatterns($re) { preg_match_all('/' . $re . '/', '', $m); return count($m)-1; } // }}} /**#@+ * @access private * @param resource $xp XML parser resource * @param string $elem XML element name * @param array $attribs XML element attributes */ // {{{ xmltag_Default /** * start handler for element */ function xmltag_Default($xp, $elem, $attribs) { $this->_aliasAttributes($attribs); if (!isset($attribs['innerGroup']) || $attribs['innerGroup'] === '') { $this->_error(TEXT_HIGHLIGHTER_EMPTY_OR_MISSING, 'innerGroup'); } $this->_defClass = @$attribs['innerGroup']; } // }}} // {{{ xmltag_Region /** * start handler for element */ function xmltag_Region($xp, $elem, $attribs) { $this->_aliasAttributes($attribs); if (!isset($attribs['name']) || $attribs['name'] === '') { $this->_error(TEXT_HIGHLIGHTER_EMPTY_OR_MISSING, 'region name'); } if (!isset($attribs['innerGroup']) || $attribs['innerGroup'] === '') { $this->_error(TEXT_HIGHLIGHTER_EMPTY_OR_MISSING, 'innerGroup'); } $this->_element = array('name' => $attribs['name']); $this->_element['line'] = xml_get_current_line_number($this->parser); if (isset($attribs['case'])) { $this->_element['case'] = $attribs['case'] == 'yes'; } else { $this->_element['case'] = $this->_case; } $this->_element['innerGroup'] = $attribs['innerGroup']; $this->_element['delimGroup'] = isset($attribs['delimGroup']) ? $attribs['delimGroup'] : $attribs['innerGroup']; $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'; if (isset($attribs['startBOL']) && $attribs['startBOL'] == 'yes') { $this->_element['startBOL'] = true; } if (isset($attribs['endBOL']) && $attribs['endBOL'] == 'yes') { $this->_element['endBOL'] = true; } if (isset($attribs['neverAfter'])) { $this->_element['neverafter'] = $this->_makeRE($attribs['neverAfter']); } } // }}} // {{{ xmltag_Block /** * start handler for element */ function xmltag_Block($xp, $elem, $attribs) { $this->_aliasAttributes($attribs); if (!isset($attribs['name']) || $attribs['name'] === '') { $this->_error(TEXT_HIGHLIGHTER_EMPTY_OR_MISSING, 'block name'); } if (isset($attribs['innerGroup']) && $attribs['innerGroup'] === '') { $this->_error(TEXT_HIGHLIGHTER_EMPTY, 'innerGroup'); } $this->_element = array('name' => $attribs['name']); $this->_element['line'] = xml_get_current_line_number($this->parser); if (isset($attribs['case'])) { $this->_element['case'] = $attribs['case'] == 'yes'; } else { $this->_element['case'] = $this->_case; } if (isset($attribs['innerGroup'])) { $this->_element['innerGroup'] = @$attribs['innerGroup']; } $this->_element['match'] = $this->_makeRE($attribs['match'], $this->_element['case']); $this->_element['contained'] = @$attribs['contained'] == 'yes'; $this->_element['multiline'] = @$attribs['multiline'] == 'yes'; if (isset($attribs['BOL']) && $attribs['BOL'] == 'yes') { $this->_element['BOL'] = true; } if (isset($attribs['neverAfter'])) { $this->_element['neverafter'] = $this->_makeRE($attribs['neverAfter']); } } // }}} // {{{ cdataHandler /** * Character data handler. Used for comment */ function cdataHandler($xp, $cdata) { if ($this->_inComment) { $this->_comment .= $cdata; } } // }}} // {{{ xmltag_Comment /** * start handler for element */ function xmltag_Comment($xp, $elem, $attribs) { $this->_comment = ''; $this->_inComment = true; } // }}} // {{{ xmltag_PartGroup /** * start handler for element */ function xmltag_PartGroup($xp, $elem, $attribs) { $this->_aliasAttributes($attribs); if (!isset($attribs['innerGroup']) || $attribs['innerGroup'] === '') { $this->_error(TEXT_HIGHLIGHTER_EMPTY_OR_MISSING, 'innerGroup'); } $this->_element['partClass'][$attribs['index']] = @$attribs['innerGroup']; } // }}} // {{{ xmltag_PartClass /** * start handler for element */ function xmltag_PartClass($xp, $elem, $attribs) { $this->xmltag_PartGroup($xp, $elem, $attribs); } // }}} // {{{ xmltag_Keywords /** * start handler for element */ function xmltag_Keywords($xp, $elem, $attribs) { $this->_aliasAttributes($attribs); if (!isset($attribs['name']) || $attribs['name'] === '') { $this->_error(TEXT_HIGHLIGHTER_EMPTY_OR_MISSING, 'keyword group name'); } if (!isset($attribs['innerGroup']) || $attribs['innerGroup'] === '') { $this->_error(TEXT_HIGHLIGHTER_EMPTY_OR_MISSING, 'innerGroup'); } if (!isset($attribs['inherits']) || $attribs['inherits'] === '') { $this->_error(TEXT_HIGHLIGHTER_EMPTY_OR_MISSING, 'inherits'); } $this->_element = array('name'=>@$attribs['name']); $this->_element['line'] = xml_get_current_line_number($this->parser); $this->_element['innerGroup'] = @$attribs['innerGroup']; 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']; } if (isset($attribs['ifdef'])) { $this->_element['ifdef'] = $attribs['ifdef']; } if (isset($attribs['ifndef'])) { $this->_element['ifndef'] = $attribs['ifndef']; } } // }}} // {{{ xmltag_Keyword /** * start handler for element */ function xmltag_Keyword($xp, $elem, $attribs) { if (!isset($attribs['match']) || $attribs['match'] === '') { $this->_error(TEXT_HIGHLIGHTER_EMPTY_OR_MISSING, 'match'); } $keyword = @$attribs['match']; if (!$this->_element['case']) { $keyword = strtolower($keyword); } $this->_element['match'][$keyword] = true; } // }}} // {{{ xmltag_Contains /** * start handler for element */ function xmltag_Contains($xp, $elem, $attribs) { $this->_element['contains-all'] = @$attribs['all'] == 'yes'; if (isset($attribs['region'])) { $this->_element['contains']['region'][$attribs['region']] = xml_get_current_line_number($this->parser); } if (isset($attribs['block'])) { $this->_element['contains']['block'][$attribs['block']] = xml_get_current_line_number($this->parser); } } // }}} // {{{ xmltag_But /** * start handler for 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; } } // }}} // {{{ xmltag_Onlyin /** * start handler for element */ function xmltag_Onlyin($xp, $elem, $attribs) { if (!isset($attribs['region']) || $attribs['region'] === '') { $this->_error(TEXT_HIGHLIGHTER_EMPTY_OR_MISSING, 'region'); } $this->_element['onlyin'][$attribs['region']] = xml_get_current_line_number($this->parser); } // }}} // {{{ xmltag_Author /** * start handler for element */ function xmltag_Author($xp, $elem, $attribs) { if (!isset($attribs['name']) || $attribs['name'] === '') { $this->_error(TEXT_HIGHLIGHTER_EMPTY_OR_MISSING, 'author name'); } $this->_authors[] = array( 'name' => @$attribs['name'], 'email' => (string)@$attribs['email'] ); } // }}} // {{{ xmltag_Highlight /** * start handler for element */ function xmltag_Highlight($xp, $elem, $attribs) { if (!isset($attribs['lang']) || $attribs['lang'] === '') { $this->_error(TEXT_HIGHLIGHTER_EMPTY_OR_MISSING, 'language name'); } $this->_code = ''; $this->language = strtoupper(@$attribs['lang']); $this->_case = @$attribs['case'] == 'yes'; } // }}} /**#@-*/ // {{{ _error /** * Add an error message * * @param integer $code Error code * @param mixed $message Error message or array with error message parameters * @param integer $lineNo Source code line number * @access private */ function _error($code, $params = array(), $lineNo = 0) { if (!$lineNo && !empty($this->parser)) { $lineNo = xml_get_current_line_number($this->parser); } $this->_errors[] = $this->_formatError($code, $params, $this->_syntaxFile, $lineNo); } // }}} // {{{ _aliasAttributes /** * BC trick * * @param array $attrs attributes */ function _aliasAttributes(&$attrs) { if (isset($attrs['innerClass']) && !isset($attrs['innerGroup'])) { $attrs['innerGroup'] = $attrs['innerClass']; } if (isset($attrs['delimClass']) && !isset($attrs['delimGroup'])) { $attrs['delimGroup'] = $attrs['delimClass']; } if (isset($attrs['partClass']) && !isset($attrs['partGroup'])) { $attrs['partGroup'] = $attrs['partClass']; } } // }}} /**#@+ * @access private * @param resource $xp XML parser resource * @param string $elem XML element name */ // {{{ xmltag_Comment_ /** * end handler for element */ function xmltag_Comment_($xp, $elem) { $this->_inComment = false; } // }}} // {{{ xmltag_Region_ /** * end handler for element */ function xmltag_Region_($xp, $elem) { $this->_element['type'] = 'region'; $this->_element['order'] = $this->_blockOrder ++; $this->_regions[$this->_element['name']] = $this->_element; } // }}} // {{{ xmltag_Keywords_ /** * end handler for element */ function xmltag_Keywords_($xp, $elem) { $this->_keywords[$this->_element['name']] = $this->_element; } // }}} // {{{ xmltag_Block_ /** * end handler for element */ function xmltag_Block_($xp, $elem) { $this->_element['type'] = 'block'; $this->_element['order'] = $this->_blockOrder ++; $this->_blocks[$this->_element['name']] = $this->_element; } // }}} // {{{ xmltag_Highlight_ /** * end handler for element */ function xmltag_Highlight_($xp, $elem) { $conditions = array(); $toplevel = array(); foreach ($this->_blocks as $i => $current) { if (!$current['contained'] && !isset($current['onlyin'])) { $toplevel[] = $i; } foreach ((array)@$current['onlyin'] as $region => $lineNo) { if (!isset($this->_regions[$region])) { $this->_error(TEXT_HIGHLIGHTER_BLOCK_REGION, array( 'block' => $current['name'], 'region' => $region )); } } } foreach ($this->_regions as $i=>$current) { if (!$current['contained'] && !isset($current['onlyin'])) { $toplevel[] = $i; } foreach ((array)@$current['contains']['region'] as $region => $lineNo) { if (!isset($this->_regions[$region])) { $this->_error(TEXT_HIGHLIGHTER_REGION_REGION, array( 'region1' => $current['name'], 'region2' => $region )); } } foreach ((array)@$current['contains']['block'] as $region => $lineNo) { if (!isset($this->_blocks[$region])) { $this->_error(TEXT_HIGHLIGHTER_REGION_BLOCK, array( 'block' => $current['name'], 'region' => $region )); } } foreach ((array)@$current['onlyin'] as $region => $lineNo) { if (!isset($this->_regions[$region])) { $this->_error(TEXT_HIGHLIGHTER_REGION_REGION, array( 'region1' => $current['name'], 'region2' => $region )); } } 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'][] = $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'][] = $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']); unset ($this->_blocks[$i]['line']); } 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']); unset ($this->_regions[$i]['line']); } foreach ($this->_keywords as $name => $keyword) { if (isset($keyword['ifdef'])) { $conditions[$keyword['ifdef']][] = array($name, true); } if (isset($keyword['ifndef'])) { $conditions[$keyword['ifndef']][] = array($name, false); } unset($this->_keywords[$name]['line']); if (!isset($this->_blocks[$keyword['inherits']])) { $this->_error(TEXT_HIGHLIGHTER_KEYWORD_INHERITS, array( 'keyword' => $keyword['name'], 'block' => $keyword['inherits'] )); } if (isset($keyword['otherwise']) && !isset($this->_blocks[$keyword['otherwise']]) ) { $this->_error(TEXT_HIGHLIGHTER_KEYWORD_BLOCK, array( 'keyword' => $keyword['name'], 'block' => $keyword['inherits'] )); } } $syntax=array( 'keywords' => $this->_keywords, 'blocks' => array_merge($this->_blocks, $this->_regions), 'toplevel' => $toplevel, ); uasort($syntax['blocks'], array(&$this, '_sortBlocks')); foreach ($syntax['blocks'] as $name => $block) { if ($block['type'] == 'block') { continue; } if (is_array(@$syntax['blocks'][$name]['lookfor'])) { usort($syntax['blocks'][$name]['lookfor'], array(&$this, '_sortLookFor')); } } usort($syntax['toplevel'], array(&$this, '_sortLookFor')); $syntax['case'] = $this->_case; $this->_code = <<language} syntax highlighting CODE; if ($this->_comment) { $comment = preg_replace('~^~m',' * ',$this->_comment); $this->_code .= "\n * \n" . $comment; } $this->_code .= <<_syntaxFile CODE; foreach ($this->_authors as $author) { $this->_code .= ' * @author ' . $author['name']; if ($author['email']) { $this->_code .= ' <' . $author['email'] . '>'; } $this->_code .= "\n"; } $this->_code .= <<language} syntax highlighting * CODE; foreach ($this->_authors as $author) { $this->_code .= ' * @author ' . $author['name']; if ($author['email']) { $this->_code .= ' <' . $author['email']. '>'; } $this->_code .= "\n"; } $this->_code .= <<language} extends Text_Highlighter { CODE; $this->_code .= 'var $_language = \'' . strtolower($this->language) . "';\n\n"; $array = var_export($syntax, true); $array = trim(preg_replace('~^(\s*)~m',' \1\1',$array)); // \$this->_syntax = $array; $this->_code .= <<language}(\$options=array()) { \$this->__construct(\$options); } /** * Constructor * * @param array \$options * @access public */ function __construct(\$options=array()) { CODE; $this->_code .= <<_options = \$options; CODE; $states = array(); $i = 0; foreach ($syntax['blocks'] as $name => $block) { if ($block['type'] == 'region') { $states[$name] = $i++; } } $regs = array(); $counts = array(); $delim = array(); $inner = array(); $end = array(); $stat = array(); $keywords = array(); $parts = array(); $kwmap = array(); $subst = array(); $re = array(); $ce = array(); $rd = array(); $in = array(); $st = array(); $kw = array(); $sb = array(); foreach ($syntax['toplevel'] as $name) { $block = $syntax['blocks'][$name]; if ($block['type'] == 'block') { $kwm = array(); $re[] = '(' . $block['match'] . ')'; $ce[] = $this->_countSubpatterns($block['match']); $rd[] = ''; $sb[] = false;; $st[] = -1; foreach ($syntax['keywords'] as $kwname => $kwgroup) { if ($kwgroup['inherits'] != $name) { continue; } $gre = implode('|', array_keys($kwgroup['match'])); if (!$kwgroup['case']) { $gre = '(?i)' . $gre; } $kwm[$kwname][] = $gre; $kwmap[$kwname] = $kwgroup['innerGroup']; } foreach ($kwm as $g => $ma) { $kwm[$g] = '/^(' . implode(')|(', $ma) . ')$/'; } $kw[] = $kwm; } else { $kw[] = -1; $re[] = '(' . $block['start'] . ')'; $ce[] = $this->_countSubpatterns($block['start']); $rd[] = $block['delimGroup']; $st[] = $states[$name]; $sb[] = $block['remember']; } $in[] = $block['innerGroup']; } $re = implode('|', $re); $regs[-1] = '/' . $re . '/'; $counts[-1] = $ce; $delim[-1] = $rd; $inner[-1] = $in; $stat[-1] = $st; $keywords[-1] = $kw; $subst[-1] = $sb; foreach ($syntax['blocks'] as $ablock) { if ($ablock['type'] != 'region') { continue; } $end[] = '/' . $ablock['end'] . '/'; $re = array(); $ce = array(); $rd = array(); $in = array(); $st = array(); $kw = array(); $pc = array(); $sb = array(); foreach ((array)@$ablock['lookfor'] as $name) { $block = $syntax['blocks'][$name]; if (isset($block['partClass'])) { $pc[] = $block['partClass']; } else { $pc[] = null; } if ($block['type'] == 'block') { $kwm = array();; $re[] = '(' . $block['match'] . ')'; $ce[] = $this->_countSubpatterns($block['match']); $rd[] = ''; $sb[] = false; $st[] = -1; foreach ($syntax['keywords'] as $kwname => $kwgroup) { if ($kwgroup['inherits'] != $name) { continue; } $gre = implode('|', array_keys($kwgroup['match'])); if (!$kwgroup['case']) { $gre = '(?i)' . $gre; } $kwm[$kwname][] = $gre; $kwmap[$kwname] = $kwgroup['innerGroup']; } foreach ($kwm as $g => $ma) { $kwm[$g] = '/^(' . implode(')|(', $ma) . ')$/'; } $kw[] = $kwm; } else { $sb[] = $block['remember']; $kw[] = -1; $re[] = '(' . $block['start'] . ')'; $ce[] = $this->_countSubpatterns($block['start']); $rd[] = $block['delimGroup']; $st[] = $states[$name]; } $in[] = $block['innerGroup']; } $re = implode('|', $re); $regs[] = '/' . $re . '/'; $counts[] = $ce; $delim[] = $rd; $inner[] = $in; $stat[] = $st; $keywords[] = $kw; $parts[] = $pc; $subst[] = $sb; } $this->_code .= "\n \$this->_regs = " . $this->_exportArray($regs); $this->_code .= ";\n \$this->_counts = " .$this->_exportArray($counts); $this->_code .= ";\n \$this->_delim = " .$this->_exportArray($delim); $this->_code .= ";\n \$this->_inner = " .$this->_exportArray($inner); $this->_code .= ";\n \$this->_end = " .$this->_exportArray($end); $this->_code .= ";\n \$this->_states = " .$this->_exportArray($stat); $this->_code .= ";\n \$this->_keywords = " .$this->_exportArray($keywords); $this->_code .= ";\n \$this->_parts = " .$this->_exportArray($parts); $this->_code .= ";\n \$this->_subst = " .$this->_exportArray($subst); $this->_code .= ";\n \$this->_conditions = " .$this->_exportArray($conditions); $this->_code .= ";\n \$this->_kwmap = " .$this->_exportArray($kwmap); $this->_code .= ";\n \$this->_defClass = '" .$this->_defClass . '\''; $this->_code .= <<_checkDefines(); } } CODE; } // }}} } /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * c-hanging-comment-ender-p: nil * End: */ ?>