2 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
4 * Syntax highlighter class generator
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}.
11 * PHP versions 4 and 5
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.
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
25 * @link http://pear.php.net/package/Text_Highlighter
31 require_once 'PEAR.php';
32 require_once 'XML/Parser.php';
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);
51 * Syntax highliter class generator class
53 * This class is used to generate PHP classes
54 * from XML files with highlighting rules
58 *require_once 'Text/Highlighter/Generator.php';
59 *$generator = new Text_Highlighter_Generator('php.xml');
60 *$generator->generate();
61 *$generator->saveCode('PHP.php');
64 * A command line script <b>generate</b> is provided for
65 * class generation (installs in scripts/Text/Highlighter).
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: @package_version@
71 * @link http://pear.php.net/package/Text_Highlighter
74 class Text_Highlighter_Generator extends XML_Parser
78 * Whether to do case folding.
79 * We have to declare it here, because XML_Parser
80 * sets case folding in constructor
87 * Holds name of file with highlighting rules
95 * Current element being processed
108 var $_regions = array();
116 var $_blocks = array();
119 * List of keyword groups
124 var $_keywords = array();
132 var $_authors = array();
156 var $_defClass = 'default';
167 * Flag for comment processing
172 var $_inComment = false;
175 * Sorting order of current block/region
180 var $_blockOrder = 0;
194 * PHP4 compatable constructor
196 * @param string $syntaxFile Name of XML file
197 * with syntax highlighting rules
202 function Text_Highlighter_Generator($syntaxFile = '')
204 return $this->__construct($syntaxFile);
210 * @param string $syntaxFile Name of XML file
211 * with syntax highlighting rules
216 function __construct($syntaxFile = '')
218 XML_Parser::XML_Parser(null, 'func');
219 $this->_errors = array();
220 $this->_declareErrorMessages();
222 $this->setInputFile($syntaxFile);
230 * Format error message
232 * @param int $code error code
233 * @param string $params parameters
234 * @param string $fileName file name
235 * @param int $lineNo line number
239 function _formatError($code, $params, $fileName, $lineNo)
241 $template = $this->_templates[$code];
242 $ret = call_user_func_array('sprintf', array_merge(array($template), $params));
244 $ret = '[' . $fileName . '] ' . $ret;
247 $ret .= ' (line ' . $lineNo . ')';
253 // {{{ declareErrorMessages
256 * Set up error message templates
260 function _declareErrorMessages()
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'
282 * Sets the input xml file to be parsed
284 * @param string Filename (full path)
288 function setInputFile($file)
290 $this->_syntaxFile = $file;
291 $ret = parent::setInputFile($file);
292 if (PEAR::isError($ret)) {
293 $this->_error(TEXT_HIGHLIGHTER_FILE_READ, $ret->message);
303 * Generates class code
310 $this->_regions = array();
311 $this->_blocks = array();
312 $this->_keywords = array();
313 $this->language = '';
315 $this->_defClass = 'default';
316 $this->_comment = '';
317 $this->_inComment = false;
318 $this->_authors = array();
319 $this->_blockOrder = 0;
320 $this->_errors = array();
322 $ret = $this->parse();
323 if (PEAR::isError($ret)) {
324 $this->_error(TEXT_HIGHLIGHTER_PARSE, $ret->message);
334 * Returns generated code as a string.
336 * @return string Generated code
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
353 * @param string $filename Name of file to write the code to
354 * @return boolean true on success, false on failure
358 function saveCode($filename)
360 $f = @fopen($filename, 'wb');
362 $this->_error(TEXT_HIGHLIGHTER_FILE_WRITE, array('outfile'=>$filename));
365 fwrite ($f, $this->_code);
374 * Reports if there were errors
382 return count($this->_errors) > 0;
397 return $this->_errors;
409 function _sortBlocks($b1, $b2) {
410 return $b1['order'] - $b2['order'];
416 * Sort 'look for' list
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'];
431 * Adds delimiters and modifiers to regular expression if necessary
433 * @param string $text Original RE
434 * @return string Final RE
437 function _makeRE($text, $case = false)
439 if (!strlen($text)) {
440 $this->_error(TEXT_HIGHLIGHTER_EMPTY_RE);
442 if (!strlen($text) || $text{0} != '/') {
443 $text = '/' . $text . '/';
449 @preg_match($text, '');
451 $this->_error(TEXT_HIGHLIGHTER_INVALID_RE, $php_errormsg);
453 preg_match ('#^/(.+)/(.*)$#', $text, $m);
455 $text = '(?' . $m[2] . ')' . $m[1];
466 * Exports array as PHP code
468 * @param array $array
469 * @return string Code
472 function _exportArray($array)
474 $array = var_export($array, true);
475 return trim(preg_replace('~^(\s*)~m',' \1\1',$array));
479 // {{{ _countSubpatterns
481 * Find number of capturing suppaterns in regular expression
483 * @param string $re Regular expression (without delimiters)
485 function _countSubpatterns($re)
487 preg_match_all('/' . $re . '/', '', $m);
495 * @param resource $xp XML parser resource
496 * @param string $elem XML element name
497 * @param array $attribs XML element attributes
500 // {{{ xmltag_Default
503 * start handler for <default> element
505 function xmltag_Default($xp, $elem, $attribs)
507 $this->_aliasAttributes($attribs);
508 if (!isset($attribs['innerGroup']) || $attribs['innerGroup'] === '') {
509 $this->_error(TEXT_HIGHLIGHTER_EMPTY_OR_MISSING, 'innerGroup');
511 $this->_defClass = @$attribs['innerGroup'];
518 * start handler for <region> element
520 function xmltag_Region($xp, $elem, $attribs)
522 $this->_aliasAttributes($attribs);
523 if (!isset($attribs['name']) || $attribs['name'] === '') {
524 $this->_error(TEXT_HIGHLIGHTER_EMPTY_OR_MISSING, 'region name');
526 if (!isset($attribs['innerGroup']) || $attribs['innerGroup'] === '') {
527 $this->_error(TEXT_HIGHLIGHTER_EMPTY_OR_MISSING, 'innerGroup');
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';
534 $this->_element['case'] = $this->_case;
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;
548 if (isset($attribs['endBOL']) && $attribs['endBOL'] == 'yes') {
549 $this->_element['endBOL'] = true;
551 if (isset($attribs['neverAfter'])) {
552 $this->_element['neverafter'] = $this->_makeRE($attribs['neverAfter']);
560 * start handler for <block> element
562 function xmltag_Block($xp, $elem, $attribs)
564 $this->_aliasAttributes($attribs);
565 if (!isset($attribs['name']) || $attribs['name'] === '') {
566 $this->_error(TEXT_HIGHLIGHTER_EMPTY_OR_MISSING, 'block name');
568 if (isset($attribs['innerGroup']) && $attribs['innerGroup'] === '') {
569 $this->_error(TEXT_HIGHLIGHTER_EMPTY, 'innerGroup');
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';
576 $this->_element['case'] = $this->_case;
578 if (isset($attribs['innerGroup'])) {
579 $this->_element['innerGroup'] = @$attribs['innerGroup'];
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;
587 if (isset($attribs['neverAfter'])) {
588 $this->_element['neverafter'] = $this->_makeRE($attribs['neverAfter']);
596 * Character data handler. Used for comment
598 function cdataHandler($xp, $cdata)
600 if ($this->_inComment) {
601 $this->_comment .= $cdata;
606 // {{{ xmltag_Comment
609 * start handler for <comment> element
611 function xmltag_Comment($xp, $elem, $attribs)
613 $this->_comment = '';
614 $this->_inComment = true;
618 // {{{ xmltag_PartGroup
621 * start handler for <partgroup> element
623 function xmltag_PartGroup($xp, $elem, $attribs)
625 $this->_aliasAttributes($attribs);
626 if (!isset($attribs['innerGroup']) || $attribs['innerGroup'] === '') {
627 $this->_error(TEXT_HIGHLIGHTER_EMPTY_OR_MISSING, 'innerGroup');
629 $this->_element['partClass'][$attribs['index']] = @$attribs['innerGroup'];
633 // {{{ xmltag_PartClass
636 * start handler for <partclass> element
638 function xmltag_PartClass($xp, $elem, $attribs)
640 $this->xmltag_PartGroup($xp, $elem, $attribs);
644 // {{{ xmltag_Keywords
647 * start handler for <keywords> element
649 function xmltag_Keywords($xp, $elem, $attribs)
651 $this->_aliasAttributes($attribs);
652 if (!isset($attribs['name']) || $attribs['name'] === '') {
653 $this->_error(TEXT_HIGHLIGHTER_EMPTY_OR_MISSING, 'keyword group name');
655 if (!isset($attribs['innerGroup']) || $attribs['innerGroup'] === '') {
656 $this->_error(TEXT_HIGHLIGHTER_EMPTY_OR_MISSING, 'innerGroup');
658 if (!isset($attribs['inherits']) || $attribs['inherits'] === '') {
659 $this->_error(TEXT_HIGHLIGHTER_EMPTY_OR_MISSING, 'inherits');
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';
667 $this->_element['case'] = $this->_case;
669 $this->_element['inherits'] = @$attribs['inherits'];
670 if (isset($attribs['otherwise'])) {
671 $this->_element['otherwise'] = $attribs['otherwise'];
673 if (isset($attribs['ifdef'])) {
674 $this->_element['ifdef'] = $attribs['ifdef'];
676 if (isset($attribs['ifndef'])) {
677 $this->_element['ifndef'] = $attribs['ifndef'];
682 // {{{ xmltag_Keyword
685 * start handler for <keyword> element
687 function xmltag_Keyword($xp, $elem, $attribs)
689 if (!isset($attribs['match']) || $attribs['match'] === '') {
690 $this->_error(TEXT_HIGHLIGHTER_EMPTY_OR_MISSING, 'match');
692 $keyword = @$attribs['match'];
693 if (!$this->_element['case']) {
694 $keyword = strtolower($keyword);
696 $this->_element['match'][$keyword] = true;
700 // {{{ xmltag_Contains
703 * start handler for <contains> element
705 function xmltag_Contains($xp, $elem, $attribs)
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);
712 if (isset($attribs['block'])) {
713 $this->_element['contains']['block'][$attribs['block']] =
714 xml_get_current_line_number($this->parser);
722 * start handler for <but> element
724 function xmltag_But($xp, $elem, $attribs)
726 if (isset($attribs['region'])) {
727 $this->_element['not-contains']['region'][$attribs['region']] = true;
729 if (isset($attribs['block'])) {
730 $this->_element['not-contains']['block'][$attribs['block']] = true;
738 * start handler for <onlyin> element
740 function xmltag_Onlyin($xp, $elem, $attribs)
742 if (!isset($attribs['region']) || $attribs['region'] === '') {
743 $this->_error(TEXT_HIGHLIGHTER_EMPTY_OR_MISSING, 'region');
745 $this->_element['onlyin'][$attribs['region']] = xml_get_current_line_number($this->parser);
752 * start handler for <author> element
754 function xmltag_Author($xp, $elem, $attribs)
756 if (!isset($attribs['name']) || $attribs['name'] === '') {
757 $this->_error(TEXT_HIGHLIGHTER_EMPTY_OR_MISSING, 'author name');
759 $this->_authors[] = array(
760 'name' => @$attribs['name'],
761 'email' => (string)@$attribs['email']
766 // {{{ xmltag_Highlight
769 * start handler for <highlight> element
771 function xmltag_Highlight($xp, $elem, $attribs)
773 if (!isset($attribs['lang']) || $attribs['lang'] === '') {
774 $this->_error(TEXT_HIGHLIGHTER_EMPTY_OR_MISSING, 'language name');
777 $this->language = strtoupper(@$attribs['lang']);
778 $this->_case = @$attribs['case'] == 'yes';
788 * Add an error message
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
795 function _error($code, $params = array(), $lineNo = 0)
797 if (!$lineNo && !empty($this->parser)) {
798 $lineNo = xml_get_current_line_number($this->parser);
800 $this->_errors[] = $this->_formatError($code, $params, $this->_syntaxFile, $lineNo);
804 // {{{ _aliasAttributes
809 * @param array $attrs attributes
811 function _aliasAttributes(&$attrs)
813 if (isset($attrs['innerClass']) && !isset($attrs['innerGroup'])) {
814 $attrs['innerGroup'] = $attrs['innerClass'];
816 if (isset($attrs['delimClass']) && !isset($attrs['delimGroup'])) {
817 $attrs['delimGroup'] = $attrs['delimClass'];
819 if (isset($attrs['partClass']) && !isset($attrs['partGroup'])) {
820 $attrs['partGroup'] = $attrs['partClass'];
828 * @param resource $xp XML parser resource
829 * @param string $elem XML element name
832 // {{{ xmltag_Comment_
835 * end handler for <comment> element
837 function xmltag_Comment_($xp, $elem)
839 $this->_inComment = false;
843 // {{{ xmltag_Region_
846 * end handler for <region> element
848 function xmltag_Region_($xp, $elem)
850 $this->_element['type'] = 'region';
851 $this->_element['order'] = $this->_blockOrder ++;
852 $this->_regions[$this->_element['name']] = $this->_element;
856 // {{{ xmltag_Keywords_
859 * end handler for <keywords> element
861 function xmltag_Keywords_($xp, $elem)
863 $this->_keywords[$this->_element['name']] = $this->_element;
870 * end handler for <block> element
872 function xmltag_Block_($xp, $elem)
874 $this->_element['type'] = 'block';
875 $this->_element['order'] = $this->_blockOrder ++;
876 $this->_blocks[$this->_element['name']] = $this->_element;
880 // {{{ xmltag_Highlight_
883 * end handler for <highlight> element
885 function xmltag_Highlight_($xp, $elem)
887 $conditions = array();
889 foreach ($this->_blocks as $i => $current) {
890 if (!$current['contained'] && !isset($current['onlyin'])) {
893 foreach ((array)@$current['onlyin'] as $region => $lineNo) {
894 if (!isset($this->_regions[$region])) {
895 $this->_error(TEXT_HIGHLIGHTER_BLOCK_REGION,
897 'block' => $current['name'],
903 foreach ($this->_regions as $i=>$current) {
904 if (!$current['contained'] && !isset($current['onlyin'])) {
907 foreach ((array)@$current['contains']['region'] as $region => $lineNo) {
908 if (!isset($this->_regions[$region])) {
909 $this->_error(TEXT_HIGHLIGHTER_REGION_REGION,
911 'region1' => $current['name'],
916 foreach ((array)@$current['contains']['block'] as $region => $lineNo) {
917 if (!isset($this->_blocks[$region])) {
918 $this->_error(TEXT_HIGHLIGHTER_REGION_BLOCK,
920 'block' => $current['name'],
925 foreach ((array)@$current['onlyin'] as $region => $lineNo) {
926 if (!isset($this->_regions[$region])) {
927 $this->_error(TEXT_HIGHLIGHTER_REGION_REGION,
929 'region1' => $current['name'],
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']])) {
939 } elseif (isset($current['contains']['region'][$region['name']])) {
942 $suits = @$current['contains-all'] && @!$region['never-contained'];
945 $this->_regions[$i]['lookfor'][] = $j;
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']])) {
953 } elseif (isset($current['contains']['block'][$region['name']])) {
956 $suits = @$current['contains-all'] && @!$region['never-contained'];
959 $this->_regions[$i]['lookfor'][] = $j;
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']);
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']);
981 foreach ($this->_keywords as $name => $keyword) {
982 if (isset($keyword['ifdef'])) {
983 $conditions[$keyword['ifdef']][] = array($name, true);
985 if (isset($keyword['ifndef'])) {
986 $conditions[$keyword['ifndef']][] = array($name, false);
988 unset($this->_keywords[$name]['line']);
989 if (!isset($this->_blocks[$keyword['inherits']])) {
990 $this->_error(TEXT_HIGHLIGHTER_KEYWORD_INHERITS,
992 'keyword' => $keyword['name'],
993 'block' => $keyword['inherits']
996 if (isset($keyword['otherwise']) && !isset($this->_blocks[$keyword['otherwise']]) ) {
997 $this->_error(TEXT_HIGHLIGHTER_KEYWORD_BLOCK,
999 'keyword' => $keyword['name'],
1000 'block' => $keyword['inherits']
1006 'keywords' => $this->_keywords,
1007 'blocks' => array_merge($this->_blocks, $this->_regions),
1008 'toplevel' => $toplevel,
1010 uasort($syntax['blocks'], array(&$this, '_sortBlocks'));
1011 foreach ($syntax['blocks'] as $name => $block) {
1012 if ($block['type'] == 'block') {
1015 if (is_array(@$syntax['blocks'][$name]['lookfor'])) {
1016 usort($syntax['blocks'][$name]['lookfor'], array(&$this, '_sortLookFor'));
1019 usort($syntax['toplevel'], array(&$this, '_sortLookFor'));
1020 $syntax['case'] = $this->_case;
1021 $this->_code = <<<CODE
1024 * Auto-generated class. {$this->language} syntax highlighting
1027 if ($this->_comment) {
1028 $comment = preg_replace('~^~m',' * ',$this->_comment);
1029 $this->_code .= "\n * \n" . $comment;
1032 $this->_code .= <<<CODE
1035 * PHP version 4 and 5
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.
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
1047 * @package Text_Highlighter
1048 * @version generated from: $this->_syntaxFile
1052 foreach ($this->_authors as $author) {
1053 $this->_code .= ' * @author ' . $author['name'];
1054 if ($author['email']) {
1055 $this->_code .= ' <' . $author['email'] . '>';
1057 $this->_code .= "\n";
1060 $this->_code .= <<<CODE
1068 require_once 'Text/Highlighter.php';
1071 * Auto-generated class. {$this->language} syntax highlighting
1075 foreach ($this->_authors as $author) {
1076 $this->_code .= ' * @author ' . $author['name'];
1077 if ($author['email']) {
1078 $this->_code .= ' <' . $author['email']. '>';
1080 $this->_code .= "\n";
1084 $this->_code .= <<<CODE
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: @package_version@
1090 * @link http://pear.php.net/package/Text_Highlighter
1092 class Text_Highlighter_{$this->language} extends Text_Highlighter
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
1102 * PHP4 Compatible Constructor
1104 * @param array \$options
1107 function Text_Highlighter_{$this->language}(\$options=array())
1109 \$this->__construct(\$options);
1116 * @param array \$options
1119 function __construct(\$options=array())
1123 $this->_code .= <<<CODE
1125 \$this->_options = \$options;
1129 foreach ($syntax['blocks'] as $name => $block) {
1130 if ($block['type'] == 'region') {
1131 $states[$name] = $i++;
1140 $keywords = array();
1151 foreach ($syntax['toplevel'] as $name) {
1152 $block = $syntax['blocks'][$name];
1153 if ($block['type'] == 'block') {
1155 $re[] = '(' . $block['match'] . ')';
1156 $ce[] = $this->_countSubpatterns($block['match']);
1160 foreach ($syntax['keywords'] as $kwname => $kwgroup) {
1161 if ($kwgroup['inherits'] != $name) {
1164 $gre = implode('|', array_keys($kwgroup['match']));
1165 if (!$kwgroup['case']) {
1166 $gre = '(?i)' . $gre;
1168 $kwm[$kwname][] = $gre;
1169 $kwmap[$kwname] = $kwgroup['innerGroup'];
1171 foreach ($kwm as $g => $ma) {
1172 $kwm[$g] = '/^(' . implode(')|(', $ma) . ')$/';
1177 $re[] = '(' . $block['start'] . ')';
1178 $ce[] = $this->_countSubpatterns($block['start']);
1179 $rd[] = $block['delimGroup'];
1180 $st[] = $states[$name];
1181 $sb[] = $block['remember'];
1183 $in[] = $block['innerGroup'];
1185 $re = implode('|', $re);
1186 $regs[-1] = '/' . $re . '/';
1191 $keywords[-1] = $kw;
1194 foreach ($syntax['blocks'] as $ablock) {
1195 if ($ablock['type'] != 'region') {
1198 $end[] = '/' . $ablock['end'] . '/';
1207 foreach ((array)@$ablock['lookfor'] as $name) {
1208 $block = $syntax['blocks'][$name];
1209 if (isset($block['partClass'])) {
1210 $pc[] = $block['partClass'];
1214 if ($block['type'] == 'block') {
1216 $re[] = '(' . $block['match'] . ')';
1217 $ce[] = $this->_countSubpatterns($block['match']);
1221 foreach ($syntax['keywords'] as $kwname => $kwgroup) {
1222 if ($kwgroup['inherits'] != $name) {
1225 $gre = implode('|', array_keys($kwgroup['match']));
1226 if (!$kwgroup['case']) {
1227 $gre = '(?i)' . $gre;
1229 $kwm[$kwname][] = $gre;
1230 $kwmap[$kwname] = $kwgroup['innerGroup'];
1232 foreach ($kwm as $g => $ma) {
1233 $kwm[$g] = '/^(' . implode(')|(', $ma) . ')$/';
1237 $sb[] = $block['remember'];
1239 $re[] = '(' . $block['start'] . ')';
1240 $ce[] = $this->_countSubpatterns($block['start']);
1241 $rd[] = $block['delimGroup'];
1242 $st[] = $states[$name];
1244 $in[] = $block['innerGroup'];
1246 $re = implode('|', $re);
1247 $regs[] = '/' . $re . '/';
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
1272 \$this->_checkDefines();
1287 * c-hanging-comment-ender-p: nil