]> git.mxchange.org Git - friendica.git/blob - library/Text_Highlighter/Text/Highlighter/Generator.php
Merge remote-tracking branch 'upstream/master' into develop
[friendica.git] / library / Text_Highlighter / Text / Highlighter / Generator.php
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$
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: @package_version@
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: @package_version@
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 ?>