objp_parser.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560
  1. <?php
  2. class TObjPParser extends TPasCocoaParser {
  3. var $objc_id = "id"; // Default type for generic objects
  4. var $objc_id_real = "id"; // The real type of generic objects (id)
  5. var $sel_string = "SEL";
  6. var $trailing_underscore = true;
  7. var $print_header_references = false;
  8. // ignore these classes when testing for ivar size
  9. var $ignore_class_ivar_comparison = array( "NSNibOutletConnector", "NSNibConnector", "NSNibControlConnector", "NSPredicateEditorRowTemplate", "NSSegmentedCell",
  10. "NSSimpleHorizontalTypesetter", "NSInvocation", "NSPointerFunctions", "NSConstantString");
  11. var $reserved_keywords = array( "const", "object", "string", "array", "var", "set", "interface", "classname", "unit",
  12. "self", "type", "raise", "property", "to", "for", "with", "function", "procedure", "result",
  13. "pointer", "create", "new", "dispose", "label", "packed", "record", "char", "class", "implementation",
  14. // identifiers from NSObject
  15. "zone",
  16. );
  17. var $replace_types = array( "void"=>"Pointer", "BOOL"=>"Boolean", "long"=>"clong", "int"=>"cint",
  18. "unsigned long"=>"culong", "unsigned short"=>"cushort", "void *"=>"Pointer", "unsigned int"=>"cuint",
  19. "Class"=>"Pobjc_class", "uint"=>"cuint",
  20. "uint8_t"=>"byte", "signed int"=>"cint", "const char"=>"char", "const void"=>"Pointer",
  21. "const uint8_t"=>"byte", "unsigned"=>"cuint", "int32_t"=>"longint", "float"=>"single",
  22. "unsigned long long"=>"culonglong", "int64_t"=>"clonglong", "uint32_t"=>"cardinal", "uint16_t"=>"word",
  23. "unsigned char"=>"char", "short"=>"cshort", "double"=>"double", "long long"=>"clonglong",
  24. // ??? new in instance var parser: (add to main section eventually)
  25. "signed char"=>"char", "uint64_t"=>"qword",
  26. // work-arounds - the type replacement needs regex to handle with spaces I guess
  27. "void*"=>"Pointer",
  28. // macros
  29. "IBAction"=>"void", "IBOutlet"=>"",
  30. // special pointers
  31. "const id *"=>"NSObjectArrayOfObjectsPtr", "Protocol *"=>"Protocol", "NSObject *"=>"NSObject",
  32. "const char *"=>"PChar", "const void *"=>"Pointer", "unsigned char *"=>"Pointer", "char *"=>"PChar",
  33. "unsigned *"=>"Pointer", "unichar *"=>"PWideChar", "const unichar *"=>"PWideChar",
  34. );
  35. // These methods require that the last parameter append a trailing underscore (if $trailing_underscore is on)
  36. var $trailing_underscore_methods = array("- (void)copy:(id)sender;", "- (void)setNeedsDisplay:(BOOL)flag;");
  37. // We use direct Cocoa classes now always
  38. var $toll_free_bridge = array();
  39. var $ignore_methods = array("observationInfo");
  40. // Converts an Objective-c method name to Pascal
  41. function ConvertObjcMethodName ($method) {
  42. $params = explode(":", $method);
  43. $name = "";
  44. if (count($params) > 1) {
  45. foreach ($params as $value) {
  46. if (eregi("([a-zA-Z0-9]+)$", $value, $captures)) $name .= $captures[1]."_";
  47. }
  48. } else {
  49. if (eregi("([a-zA-Z0-9]+)(;)*$", $params[0], $captures)) $name .= $captures[1]."_";
  50. }
  51. // clean it up
  52. if ($this->trailing_underscore) {
  53. if (!in_array($method, $this->trailing_underscore_methods)) $name = trim($name, "_");
  54. }
  55. $name = $this->ReplaceObjcType($name);
  56. return $name;
  57. }
  58. // We use direct objc classes now so we don't need to replace them with references like in PasCocoa
  59. function ReplaceNSTypesWithRef ($string) {
  60. return $string;
  61. }
  62. // Converts an Objective-C method to Pascal format
  63. function ConvertObjcMethodToPascal ($class, $source, $parts, $protected_keywords, $has_params) {
  64. // remove deprecated macros from method source
  65. $source = eregi_replace("[[:space:]]*DEPRECATED_IN_MAC_OS_X_VERSION_[0-9]+_[0-9]+_AND_LATER", "", $source);
  66. // replace "hinted" params comment with hinted type
  67. if ($this->replace_hinted_params) {
  68. // param string
  69. if (eregi("(/\*[[:space:]]*(.*)[[:space:]]*\*/)", $parts[4], $captures)) {
  70. // ??? change the parameter to the hinted type
  71. //$parts[4] = eregi_replace("(/\*.*\*/)", $captures[2], $parts[4]);
  72. //$parts[4] = trim($parts[4], " ");
  73. }
  74. // return type
  75. if (eregi("(/\*[[:space:]]*(.*)[[:space:]]*\*/)", $parts[2], $captures)) $parts[2] = $captures[2];
  76. //print_r($parts);
  77. } else { // remmove comments from params and return type
  78. $parts[4] = eregi_replace("(/\*.*\*/)", "", $parts[4]);
  79. $parts[4] = trim($parts[4], " ");
  80. $parts[2] = eregi_replace("(/\*.*\*/)", "", $parts[2]);
  81. $parts[2] = trim($parts[2], " ");
  82. }
  83. $return_type_clean = $parts[2];
  84. // perform preformatting before attempting to protect keywords
  85. $parts[2] = $this->FormatObjcType($parts[2], $modifiers);
  86. $parts[4] = $this->FormatObjcParams($parts[4]);
  87. // protect keywords in the parameter and return type
  88. if (count($protected_keywords) > 0) {
  89. foreach ($protected_keywords as $keyword) {
  90. $parts[4] = istr_replace_word($keyword, $keyword."_", $parts[4]);
  91. $parts[2] = istr_replace_word($keyword, $keyword."_", $parts[2]);
  92. }
  93. }
  94. if ($has_params) {
  95. $name = $this->ConvertObjcMethodName($source);
  96. // merge default protected keywords for the class/category
  97. if ($this->default_protected["*"]) $protected_keywords = array_merge($this->default_protected["*"], $protected_keywords);
  98. if ($this->default_protected[$class]) $protected_keywords = array_merge($this->default_protected[$class], $protected_keywords);
  99. $param_array = $this->ConvertObjcParamsToPascal($parts[4], $protected_keywords, $variable_arguments);
  100. $params = "(".$param_array["string"].")";
  101. $params_with_modifiers = "(".$param_array["string_with_modifiers"].")";
  102. } else {
  103. $params = "";
  104. $params_with_modifiers = "";
  105. $name = $parts[3];
  106. $param_array = null;
  107. $variable_arguments = false;
  108. }
  109. // protect method name from keywords
  110. if ($this->IsKeywordReserved($name)) $name .= "_";
  111. // replace objc type
  112. $return_type = $this->ConvertReturnType($return_type_clean);
  113. $virtual = "";
  114. $class_prefix = "";
  115. // determine the type based on return value
  116. if (ereg($this->regex_procedure_type, $return_type_clean)) {
  117. $kind = "procedure";
  118. } else {
  119. $kind = "function";
  120. }
  121. // determine if this is a class method
  122. if ($parts[1] == "+") {
  123. $class_prefix = "class ";
  124. // These methods probably return the an allocated instance of the class, a typical convenience method.
  125. // ??? Ack! $class may be the category or protocol name
  126. //if ($return_type == $this->objc_id) $return_type = $class;
  127. }
  128. // Replace SEL with the string equivalent
  129. if ($this->register_selectors) {
  130. $params_with_modifiers = str_replace_word("SEL", $this->sel_string, $params_with_modifiers);
  131. }
  132. // make method templates
  133. if ($kind != "function") {
  134. if ($variable_arguments) $modifier .= " varargs;";
  135. $method = "$class_prefix$kind $name$params_with_modifiers;$modifier$virtual";
  136. $method_template = "[KIND] [PREFIX]$name"."[PARAMS];$modifier";
  137. } else {
  138. if ($variable_arguments) $return_type = "$return_type; varargs";
  139. $method = $class_prefix."function $name$params_with_modifiers: $return_type;$modifier$virtual";
  140. $method_template = "[KIND] [PREFIX]$name"."[PARAMS]: [RETURN];$modifier";
  141. $method_template_function = "function [PREFIX]$name"."[PARAMS]: [RETURN];$modifier";
  142. }
  143. $method_template_procedure = "procedure [PREFIX]$name"."[PARAMS];$modifier";
  144. $method_template_function = "function [PREFIX]$name"."[PARAMS]: [RETURN];$modifier";
  145. // ??? DEBUGGING
  146. //print("$method\n");
  147. // build structure
  148. $struct["def"] = $method;
  149. $struct["template"] = $method_template;
  150. $struct["template_function"] = $method_template_function;
  151. $struct["template_procedure"] = $method_template_procedure;
  152. $struct["objc_method"] = $this->CopyObjcMethodName($source);
  153. $struct["class_prefix"] = $class_prefix;
  154. //$struct["def_objc"] = eregi("(.*);", $source, $captures[1]);
  155. if ($return_type == "void") $return_type = "";
  156. $struct["return"] = $return_type;
  157. if (in_array($return_type, $this->cocoa_classes)) $struct["returns_wrapper"] = true;
  158. $struct["param_string_clean"] = trim($params, "()");
  159. $struct["param_string_clean_with_modifiers"] = trim($params_with_modifiers, "()");
  160. $struct["param_string"] = $params;
  161. $struct["param_string_with_modifiers"] = $params_with_modifiers;
  162. $struct["param_array"] = $param_array["pairs"];
  163. $struct["param_list"] = $param_array["list"];
  164. $struct["class"] = $class;
  165. $struct["name"] = $name;
  166. $struct["kind"] = $kind;
  167. if ($struct["param_array"] != null) $struct["has_params"] = true;
  168. // FPC bug work around
  169. if (strlen($name) > $this->maximum_method_length) {
  170. $struct["can_override"] = false;
  171. print(" # WARNING: method $name can't override because the name is too long\n");
  172. $this->warning_count ++;
  173. }
  174. return $struct;
  175. }
  176. function InsertPatches ($header) {
  177. $path = "$this->root/patches/".$header["name_clean"].".patch";
  178. if ($handle = @fopen($path, "r")) {
  179. $text = ReadTextFile($path);
  180. $this->PrintOutput(0, $text);
  181. fclose($handle);
  182. }
  183. }
  184. function HeaderContainsPatch ($header) {
  185. if ($handle = @fopen("$this->root/patches/".$header["name_clean"].".patch", "r")) {
  186. fclose($handle);
  187. return true;
  188. }
  189. }
  190. // Prints all classes from the header in Objective-P FPC format
  191. function PrintHeader ($header) {
  192. global $version;
  193. $this->output = fopen($header["path"], "w+");
  194. $this->PrintOutput(0, "{ Parsed from ".ucfirst($header["framework"]).".framework ".$header["name"]." }");
  195. $date = date("D M j G:i:s T Y");
  196. $this->PrintOutput(0, "{ Version $version - $date }");
  197. $this->PrintOutput(0, "");
  198. $macro = strtoupper(substr($header["name"], 0, (strripos($header["name"], "."))));
  199. /*
  200. if ($header["classes"]) {
  201. $this->PrintOutput(0, "{\$ifdef HEADER}");
  202. $this->PrintOutput(0, "{\$ifndef $macro"."_PAS_H}");
  203. $this->PrintOutput(0, "{\$define $macro"."_PAS_H}");
  204. $this->PrintOutput(0, "type");
  205. foreach ($header["classes"] as $class) {
  206. // Make a pointer to each class
  207. $this->PrintOutput(1, $class["name"]."Pointer = Pointer;");
  208. }
  209. $this->PrintOutput(0, "");
  210. $this->PrintOutput(0, "{\$endif}");
  211. $this->PrintOutput(0, "{\$endif}");
  212. }
  213. */
  214. $this->PrintOutput(0, "");
  215. $this->PrintOutput(0, "{\$ifdef TYPES}");
  216. $this->PrintOutput(0, "{\$ifndef $macro"."_PAS_T}");
  217. $this->PrintOutput(0, "{\$define $macro"."_PAS_T}");
  218. $this->PrintTypes($header);
  219. $this->PrintOutput(0, "");
  220. $this->PrintOutput(0, "{\$endif}");
  221. $this->PrintOutput(0, "{\$endif}");
  222. $this->PrintOutput(0, "");
  223. $this->PrintOutput(0, "{\$ifdef RECORDS}");
  224. $this->PrintOutput(0, "{\$ifndef $macro"."_PAS_R}");
  225. $this->PrintOutput(0, "{\$define $macro"."_PAS_R}");
  226. // Records from types
  227. $this->PrintRecords($header);
  228. $this->PrintOutput(0, "");
  229. $this->PrintOutput(0, "{\$endif}");
  230. $this->PrintOutput(0, "{\$endif}");
  231. $this->PrintOutput(0, "");
  232. $this->PrintOutput(0, "{\$ifdef FUNCTIONS}");
  233. $this->PrintOutput(0, "{\$ifndef $macro"."_PAS_F}");
  234. $this->PrintOutput(0, "{\$define $macro"."_PAS_F}");
  235. $this->PrintFunctions($header);
  236. $this->PrintOutput(0, "");
  237. $this->PrintOutput(0, "{\$endif}");
  238. $this->PrintOutput(0, "{\$endif}");
  239. $this->PrintOutput(0, "");
  240. $this->PrintOutput(0, "{\$ifdef EXTERNAL_SYMBOLS}");
  241. $this->PrintOutput(0, "{\$ifndef $macro"."_PAS_S}");
  242. $this->PrintOutput(0, "{\$define $macro"."_PAS_S}");
  243. $this->PrintExternalSymbols($header);
  244. $this->PrintOutput(0, "");
  245. $this->PrintOutput(0, "{\$endif}");
  246. $this->PrintOutput(0, "{\$endif}");
  247. // insert user patches
  248. if ($this->HeaderContainsPatch($header)) {
  249. $this->PrintOutput(0, "");
  250. $this->PrintOutput(0, "{\$ifdef USER_PATCHES}");
  251. //$this->PrintOutput(0, "{\$ifndef $macro"."_PAS_PATCH}");
  252. //$this->PrintOutput(0, "{\$define $macro"."_PAS_PATCH}");
  253. $this->InsertPatches($header);
  254. $this->PrintOutput(0, "");
  255. //$this->PrintOutput(0, "{\$endif}");
  256. $this->PrintOutput(0, "{\$endif}");
  257. }
  258. if (($header["classes"]) || ($header["protocols"])) {
  259. $this->PrintOutput(0, "");
  260. $this->PrintOutput(0, "{\$ifdef FORWARD}");
  261. if ($header["protocols"]) {
  262. foreach ($header["protocols"] as $protocol) $this->PrintOutput(1, $protocol["name"]."$this->protocol_suffix = objcprotocol;");
  263. }
  264. if ($header["classes"]) {
  265. foreach ($header["classes"] as $class) {
  266. $this->PrintOutput(1, $class["name"]." = objcclass;");
  267. $this->PrintOutput(1, $class["name"]."Pointer = ^".$class["name"].";");
  268. }
  269. }
  270. $this->PrintOutput(0, "");
  271. $this->PrintOutput(0, "{\$endif}");
  272. }
  273. if ($header["classes"]) {
  274. $this->PrintOutput(0, "");
  275. $this->PrintOutput(0, "{\$ifdef CLASSES}");
  276. $this->PrintOutput(0, "{\$ifndef $macro"."_PAS_C}");
  277. $this->PrintOutput(0, "{\$define $macro"."_PAS_C}");
  278. foreach ($header["classes"] as $class) {
  279. //if (in_array($class["name"], $this->cocoa_classes))
  280. $this->PrintClass($class);
  281. }
  282. $this->PrintOutput(0, "");
  283. $this->PrintOutput(0, "{\$endif}");
  284. $this->PrintOutput(0, "{\$endif}");
  285. }
  286. if ($header["protocols"]) {
  287. $this->PrintOutput(0, "{\$ifdef PROTOCOLS}");
  288. $this->PrintOutput(0, "{\$ifndef $macro"."_PAS_P}");
  289. $this->PrintOutput(0, "{\$define $macro"."_PAS_P}");
  290. foreach ($header["protocols"] as $protocol) {
  291. $this->PrintOutput(1, "");
  292. $this->PrintOutput(0, "{ ".$protocol["name"]." Protocol }");
  293. $this->PrintOutput(1, $protocol["name"]."$this->protocol_suffix = objcprotocol");
  294. // print methods
  295. if ($protocol["methods"]) {
  296. foreach ($protocol["methods"] as $name => $method) {
  297. $this->PrintOutput(2, $method["def"]." message '".$method["objc_method"]."';");
  298. }
  299. }
  300. $this->PrintOutput(1, "end; external name '".$protocol["name"]."';");
  301. }
  302. $this->PrintOutput(0, "{\$endif}");
  303. $this->PrintOutput(0, "{\$endif}");
  304. }
  305. }
  306. function PrintClass ($class) {
  307. $this->PrintOutput(0, "");
  308. $this->PrintOutput(0, "{ ".$class["name"]." }");
  309. //print_r($class["methods"]);
  310. // print super class or protocol which the class conforms to
  311. if ($class["conforms"]) {
  312. $this->PrintOutput(1, $class["name"]." = objcclass(".$class["super"].", ".$class["conforms"].")");
  313. } elseif ($class["super"]) {
  314. $this->PrintOutput(1, $class["name"]." = objcclass(".$class["super"].")");
  315. }
  316. // print instance variables
  317. if ($class["ivars"]) {
  318. $this->PrintOutput(1, "private");
  319. foreach ($class["ivars"] as $ivar) {
  320. $this->PrintOutput(2, $ivar);
  321. }
  322. }
  323. // print alloc method for the class
  324. $this->PrintOutput(2, "");
  325. $this->PrintOutput(1, "public");
  326. $this->PrintOutput(2, "class function alloc: ".$class["name"]."; message 'alloc';");
  327. // print class-level methods
  328. if ($class["methods"]) {
  329. $this->PrintOutput(0, "");
  330. foreach ($class["methods"] as $method) {
  331. $this->PrintOutput(2, $method["def"]." message '".$method["objc_method"]."';");
  332. }
  333. }
  334. // print category-level methods
  335. if (count($class["categories"]) > 0) {
  336. foreach ($class["categories"] as $name => $category) {
  337. $this->PrintOutput(0, "");
  338. $this->PrintOutput(2, "{ Category: $name }");
  339. if ($category["methods"]) {
  340. foreach ($category["methods"] as $method) {
  341. $this->PrintOutput(2, $method["def"]." message '".$method["objc_method"]."';");
  342. }
  343. }
  344. }
  345. }
  346. $this->PrintOutput(1, "end; external;");
  347. }
  348. function PrintDelegateReference ($valid_categories) {
  349. global $version;
  350. $date = date("D M j G:i:s T Y");
  351. $this->PrintOutput(0, "{ Version $version - $date }");
  352. $this->PrintOutput(0, "");
  353. ksort($this->delegate_methods);
  354. $this->PrintOutput(0, "unit $this->master_delegate_file;");
  355. $this->PrintOutput(0, "interface");
  356. $this->PrintOutput(0, "");
  357. $this->PrintOutput(0, "{ Copy and paste these delegate methods into your real classes. }");
  358. // implemented methods
  359. foreach ($this->delegate_methods as $category => $selectors) {
  360. if (in_array($category, $this->ignore_categories)) continue;
  361. // make sure the category is valid
  362. $valid = false;
  363. foreach ($valid_categories as $pattern) {
  364. if (eregi($pattern, $category)) {
  365. $valid = true;
  366. break;
  367. }
  368. }
  369. if (!$valid) continue;
  370. $this->PrintOutput(0, "");
  371. $this->PrintOutput(0, "type");
  372. $this->PrintOutput(1, "$category = objccategory (NSObject)");
  373. //$this->PrintOutput(1, "public");
  374. foreach ($selectors as $selector) {
  375. // FPC long name bug work-around
  376. if (strlen($selector["name_pascal"]) > $this->maximum_method_length) continue;
  377. if ($selector["kind"] == "procedure") {
  378. $this->PrintOutput(2, $selector["kind"]." ".$selector["name_pascal"].$selector["param_string"].";"." message '".$selector["name"]."';");
  379. } else {
  380. $this->PrintOutput(2, $selector["kind"]." ".$selector["name_pascal"].$selector["param_string"].": ".$selector["method"]["return"].";"." message '".$selector["name"]."';");
  381. }
  382. }
  383. $this->PrintOutput(1, "end;");
  384. }
  385. }
  386. function PrintIvarSizeComparison ($path) {
  387. $count = 0;
  388. $block = true;
  389. $block_count = 1;
  390. $limit = 2000;
  391. $handle = fopen($path, "w+");
  392. if (!$handle) die("Bad path to size comparison output program!");
  393. fwrite($handle, "{\$mode objfpc}\n");
  394. fwrite($handle, "{\$modeswitch objectivec1}\n");
  395. fwrite($handle, "program IvarSize;\n");
  396. fwrite($handle, "uses\n");
  397. fwrite($handle, " objp,objcrtl,objcrtlmacosx;\n");
  398. // print derived classes
  399. foreach ($this->cocoa_classes as $class) {
  400. if (in_array($class, $this->ignore_class_ivar_comparison)) continue;
  401. if ($previous == $class) continue;
  402. fwrite($handle, "type\n");
  403. fwrite($handle, " TDerived$class = objcclass ($class)\n");
  404. fwrite($handle, " extrabyte: byte;\n");
  405. fwrite($handle, "end;\n");
  406. $previous = $class;
  407. }
  408. // print procedures
  409. foreach ($this->cocoa_classes as $class) {
  410. if (in_array($class, $this->ignore_class_ivar_comparison)) continue;
  411. if ($previous == $class) continue;
  412. if ($count == 0) {
  413. fwrite($handle, "\n");
  414. fwrite($handle, "procedure PrintGlue$block_count;\n");
  415. fwrite($handle, "begin\n");
  416. $block_count ++;
  417. }
  418. $count ++;
  419. fwrite($handle, " if class_getInstanceSize(TDerived$class) <> (class_getInstanceSize($class)+1) then\n");
  420. fwrite($handle, " writeln('size of $class is wrong: ',class_getInstanceSize(TDerived$class),' <> ',class_getInstanceSize($class)+1);\n");
  421. if ($count == $limit) {
  422. fwrite($handle, "end;\n");
  423. $count = 0;
  424. }
  425. $previous = $class;
  426. }
  427. if ($count < $limit) {
  428. fwrite($handle, "end;\n");
  429. $block_count --;
  430. }
  431. fwrite($handle, "begin\n");
  432. for ($i=1; $i < $block_count + 1; $i++) {
  433. fwrite($handle, " PrintGlue$i;\n");
  434. }
  435. fwrite($handle, "end.\n");
  436. }
  437. }
  438. ?>