PageRenderTime 27ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/akelos_utils/doc_builder/models/source_parser.php

https://github.com/bermi/akelos
PHP | 357 lines | 226 code | 36 blank | 95 comment | 19 complexity | 22ea2d6ae75b100e636d36d8d9f97c77 MD5 | raw file
  1. <?php
  2. # This file is part of the Akelos Framework
  3. # (Copyright) 2004-2010 Bermi Ferrer bermi a t bermilabs com
  4. # See LICENSE and CREDITS for details
  5. class SourceLexer extends AkLexer
  6. {
  7. public function __construct(&$Parser)
  8. {
  9. parent::__construct($Parser, 'Text');
  10. $this->mapHandler('Text', 'Text');
  11. $this->addPhpTokens();
  12. }
  13. public function addPhpTokens()
  14. {
  15. $this->addEntryPattern('<\?','Text','PhpCode');
  16. $this->addClassName();
  17. $this->addCategories();
  18. $this->addJavaDocTokens();
  19. $this->addFunctionDeclaration();
  20. $this->addFunctionDefaultOptions();
  21. $this->addExitPattern('\?>','PhpCode');
  22. }
  23. public function addCategories()
  24. {
  25. $this->addEntryPattern('\x2f\x2a\x2a[ \n\t]*[A-Za-z _0-9]+[ \n\t]*\x3d{60,}','PhpCode','CategoryStart');
  26. $this->addPattern('(?<=\x3d{60}\n)See also:[ \n\t]*[a-z,A-Z _0-9]+[ \n\t]*\x2e', 'CategoryStart');
  27. $this->addEntryPattern('(?<=\n)[ \t]*\* ','CategoryStart','CategoryDetails');
  28. $this->addEntryPattern('\* ','CategoryStart','CategoryDetails');
  29. $this->addExitPattern('\n','CategoryDetails');
  30. $this->addExitPattern('\x2a\x2f','CategoryStart');
  31. $this->addSpecialPattern('\x2f\x2a\x2f[A-Za-z _0-9]+\x2a\x2f','PhpCode','CategoryFinish');
  32. }
  33. public function addJavaDocTokens()
  34. {
  35. $this->addEntryPattern('\x2f\x2a\x2a','PhpCode','JavaDoc');
  36. $this->addExitPattern('\x2a\x2f','JavaDoc');
  37. $this->addJavaDocLines();
  38. }
  39. public function addJavaDocLines()
  40. {
  41. $this->addEntryPattern('(?<=\n)[ \t]*\*','JavaDoc','JavaDocLine');
  42. $this->addEntryPattern('\*','JavaDoc','JavaDocLine');
  43. $this->addSpecialPattern('\x40[A-Za-z]+ ','JavaDocLine','JavaDocAttribute');
  44. $this->addExitPattern('\n','JavaDocLine');
  45. }
  46. public function addClassName()
  47. {
  48. $this->addEntryPattern('(?<=\n)[ \t]*class[ \n\t]*', 'PhpCode', 'ClassName');
  49. $this->addPattern('(?!extends)[ \n\t]*[a-z]+[ \n\t]*', 'ClassName');
  50. $this->addEntryPattern('[ \n\t]*extends[ \n\t]*', 'ClassName', 'ParentClassName');
  51. $this->addExitPattern('[ \n\t]*[a-z]+[ \n\t]*', 'ParentClassName');
  52. $this->addExitPattern('[ \n\t]*{', 'ClassName');
  53. }
  54. public function addFunctionDeclaration()
  55. {
  56. $this->addEntryPattern('(?<=\n)[ \t]*function[ \n\t][\x26a-z_]+[ \n\t]*\x28', 'PhpCode', 'FunctionName');
  57. //$this->addPattern('[ \n\t]*[a-z]+[ \n\t]*(?=\x28)', 'FunctionName');
  58. $this->addEntryPattern('\x26?[ \t]*\x24', 'FunctionName', 'FunctionParameter');
  59. $this->addExitPattern(',[ \t]*(?=\x26?[ \t]*\x24)', 'FunctionParameter');
  60. $this->addExitPattern('\x29', 'FunctionParameter');
  61. $this->addExitPattern('\x29?[ \n\t]*{', 'FunctionName');
  62. }
  63. public function addFunctionDefaultOptions()
  64. {
  65. $this->addEntryPattern('\x24default_options[ \t]*\x3d[ \t]*array[ \t]*\x28', 'PhpCode', 'DefaultOptions');
  66. $this->addEntryPattern('\x27', 'DefaultOptions', 'DefaultOption');
  67. $this->addExitPattern('\x27', 'DefaultOption');
  68. $this->addExitPattern(';', 'DefaultOptions');
  69. }
  70. }
  71. class SourceParser
  72. {
  73. public $output = '';
  74. public $input = '';
  75. public $_Lexer;
  76. public $parsed = array();
  77. public $_current_category = 'none';
  78. public $_current_category_details = '';
  79. public $_current_category_relations = array();
  80. public $_current_class = null;
  81. public $_current_class_extends = null;
  82. public $_current_method = null;
  83. public $_current_params = array();
  84. public $_latest_attributes = array();
  85. public $_current_javadoc_attribute = false;
  86. public $_latest_docs = null;
  87. public $_is_reference = null;
  88. public $package = 'Active Support';
  89. public $subpackage = 'Utils';
  90. public function SourceParser($code)
  91. {
  92. $this->input = $code;
  93. $this->_Lexer = new SourceLexer($this);
  94. }
  95. public function parse()
  96. {
  97. $this->beforeParsing();
  98. $this->_Lexer->parse($this->input);
  99. $this->afterParsing();
  100. return $this->parsed;
  101. }
  102. public function afterParsing()
  103. {
  104. $this->parsed['details'] = array();
  105. $this->parsed['details']['package'] = $this->package;
  106. $this->parsed['details']['subpackage'] = $this->subpackage;
  107. }
  108. public function beforeParsing()
  109. {
  110. $this->input = preg_replace("/\n[ \t]*/","\n", $this->input);
  111. }
  112. public function PhpCode($match, $state)
  113. {
  114. //AkDebug::trace(__FUNCTION__.' '.$match.' '.$state);
  115. if($match == AK_LEXER_UNMATCHED){
  116. }
  117. return true;
  118. }
  119. public function ClassName($match, $state)
  120. {
  121. if(AK_LEXER_MATCHED == $state){
  122. $this->_current_class = trim($match);
  123. }elseif(AK_LEXER_EXIT == $state){
  124. if(!empty($this->_current_class)){
  125. $this->parsed['classes'][$this->_current_class] = array(
  126. 'doc' => trim($this->_latest_docs, "\n\t "),
  127. 'doc_metadata' => $this->_latest_attributes,
  128. 'class_name' => $this->_current_class,
  129. 'extends' => trim($this->_current_class_extends),
  130. 'methods' => array(),
  131. //'category' => $this->_current_category,
  132. );
  133. }
  134. $this->_current_class_extends = null;
  135. $this->_latest_docs = '';
  136. $this->_is_reference = false;
  137. }elseif (AK_LEXER_ENTER == $state){
  138. $this->_is_reference = strstr($match,'&');
  139. }
  140. //AkDebug::trace(__FUNCTION__.' '.$match.' '.$state);
  141. return true;
  142. }
  143. public function ParentClassName($match, $state)
  144. {
  145. if(AK_LEXER_EXIT == $state){
  146. $this->_current_class_extends = trim($match);
  147. }
  148. //AkDebug::trace(__FUNCTION__.' '.$match.' '.$state);
  149. return true;
  150. }
  151. public function FunctionName($match, $state)
  152. {
  153. if(AK_LEXER_ENTER === $state){
  154. $this->_current_method_returns_reference = strstr($match,'&') != '';
  155. $this->_current_method = trim(str_replace(array('function ','(', '&'),'',$match));
  156. }elseif(AK_LEXER_EXIT == $state){
  157. if(!empty($this->_current_class) && !empty($this->_current_method)){
  158. $this->parsed['classes'][$this->_current_class]['methods'][$this->_current_method] = array(
  159. 'doc' => trim($this->_latest_docs, "\n\t "),
  160. 'doc_metadata' => $this->_latest_attributes,
  161. 'method_name' => $this->_current_method,
  162. 'is_private' => substr($this->_current_method,0,1) == '_',
  163. 'returns_reference' => $this->_current_method_returns_reference,
  164. 'params' => $this->_current_params,
  165. 'category' => $this->_current_category,
  166. 'category_details' => $this->_current_category_details,
  167. 'category_relations' => $this->_current_category_relations
  168. );
  169. }
  170. //AkDebug::trace($this->_current_category_details);
  171. $this->_current_method = null;
  172. $this->_current_params = array();
  173. $this->_latest_docs = '';
  174. $this->_current_category = 'none';
  175. $this->_current_category_details = '';
  176. $this->_current_category_relations = array();
  177. }
  178. //AkDebug::trace(__FUNCTION__.' '.$match.' '.$state);
  179. return true;
  180. }
  181. public function FunctionParameter($match, $state)
  182. {
  183. if(AK_LEXER_UNMATCHED == $state){
  184. list($param,$value) = explode('=',$match.'=');
  185. $type = null;
  186. if(strstr($value,'array')){
  187. $type = 'array';
  188. $value = '';
  189. }elseif(strstr($value,'"') || strstr($value,"'")){
  190. $type = 'string';
  191. $value = trim($value,'"\' ');
  192. }elseif(strstr($value,'true') || strstr($value,'false')){
  193. $type = 'bool';
  194. }elseif(is_numeric(trim($value))){
  195. $type = 'int';
  196. }elseif(strstr($value,'null') || empty($value)){
  197. $type = null;
  198. $value = null;
  199. }elseif (preg_match('/[A-Z]/', @$value[0])){
  200. $type = preg_match('/[A-Z]/', @$value[1]) ? 'constant' : 'object';
  201. }
  202. array_push($this->_current_params, array(
  203. 'name' => trim($param),
  204. 'type' => $type,
  205. 'value' => trim($value),
  206. ));
  207. }
  208. //AkDebug::trace(__FUNCTION__.' '.$match.' '.$state);
  209. return true;
  210. }
  211. public function CategoryStart($match, $state)
  212. {
  213. if(AK_LEXER_ENTER === $state){
  214. $this->_current_category = trim($match,"\n= /*");
  215. $this->parsed['categories'][$this->_current_category] = array();
  216. }elseif (AK_LEXER_UNMATCHED == $state){
  217. $match = trim(str_replace(array('See also',"*","\n","\t",'.',':'),'',$match));
  218. if(!empty($match)){
  219. $this->_current_category_relations = array_map('trim', explode(',',$match));
  220. $this->parsed['categories'][$this->_current_category]['relations'] = $this->_current_category_relations;
  221. }
  222. }
  223. // AkDebug::trace(__FUNCTION__.' '.$match.' '.$state);
  224. return true;
  225. }
  226. public function CategoryDetails($match, $state)
  227. {
  228. if(AK_LEXER_UNMATCHED == $state){
  229. $this->_current_category = 'none';
  230. $this->_current_category_relations = array();
  231. }
  232. $this->_current_category_details .= trim($match, '/*');
  233. // AkDebug::trace(__FUNCTION__.' '.$match.' '.$state);
  234. return true;
  235. }
  236. public function CategoryFinish($match, $state)
  237. {
  238. if(AK_LEXER_SPECIAL == $state){
  239. $this->_current_category_details .= trim($match, '/* ');
  240. }
  241. //AkDebug::trace($this->_current_category_details);
  242. return true;
  243. }
  244. public function DefaultOptions($match, $state)
  245. {
  246. //AkDebug::trace(__FUNCTION__.' '.$match.' '.$state);
  247. return true;
  248. }
  249. public function DefaultOption($match, $state)
  250. {
  251. //AkDebug::trace(__FUNCTION__.' '.$match.' '.$state);
  252. return true;
  253. }
  254. public function JavaDoc($match, $state)
  255. {
  256. if(AK_LEXER_ENTER === $state){
  257. $this->_current_javadoc_attribute = false;
  258. $this->_latest_attributes = array();
  259. }elseif(AK_LEXER_UNMATCHED == $state){
  260. $this->_latest_docs .= (trim($match) == '*'?'':$match)."\n";
  261. }
  262. //AkDebug::trace(__FUNCTION__.' '.$match.' '.$state);
  263. return true;
  264. }
  265. public function JavaDocLine($match, $state)
  266. {
  267. if(AK_LEXER_UNMATCHED == $state){
  268. $match = (trim($match) == '*'?'':$match)."\n";
  269. if(!$this->_current_javadoc_attribute){
  270. $this->_latest_docs .= $match;
  271. }elseif(trim($match) != ''){
  272. $handler_name = 'handle'.AkInflector::camelize($this->_current_javadoc_attribute);
  273. if(method_exists($this,$handler_name)){
  274. $this->$handler_name($match);
  275. }else{
  276. $this->_latest_attributes[$this->_current_javadoc_attribute] = $match;
  277. }
  278. }
  279. }
  280. //AkDebug::trace(__FUNCTION__.$match.$state);
  281. return true;
  282. }
  283. public function JavaDocAttribute($match, $state)
  284. {
  285. $match = trim($match,'@ ');
  286. $this->_current_javadoc_attribute = empty($match) ? false : $match;
  287. return true;
  288. }
  289. public function Text($text)
  290. {
  291. $this->output .= $text;
  292. return true;
  293. }
  294. public function handlePackage($package_description)
  295. {
  296. $this->package = AkInflector::titleize($package_description);
  297. //AkDebug::trace($this->package);
  298. }
  299. public function handleSubpackage($subpackage_description)
  300. {
  301. $this->subpackage = AkInflector::titleize($subpackage_description);
  302. //AkDebug::trace($this->subpackage);
  303. }
  304. }