PageRenderTime 23ms CodeModel.GetById 1ms RepoModel.GetById 0ms app.codeStats 0ms

/yii/framework/vendors/TextHighlighter/Text/Highlighter/Generator.php

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