PageRenderTime 49ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 1ms

/script/lib/Text/Highlighter/Generator.php

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