mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-06-18 01:38:46 +02:00

than only record containing nothing but bitpacked fields (although in theory this can also cause errors, it results in correct data layouts for all Cocoa structs while the previous code didn't) * changed the translation of bitpacked structs so that the compiler will properly align them. This requires that all bitpacked fields are wrapped into a separate record though, so all fields are now accessible via recname.data.field instead of via recname.field. Since all fields in the Cocoa classes are private, this should not cause problems (except if exported record types are also used elsewhere) git-svn-id: trunk@18113 -
3807 lines
127 KiB
PHP
3807 lines
127 KiB
PHP
<?php
|
|
|
|
// Includes
|
|
require_once("utilities.php");
|
|
require_once("objp_base.php");
|
|
require_once("docset.php");
|
|
|
|
// Constants
|
|
define("CAST_HANDLE", true);
|
|
define("DONT_CAST_HANDLE", false);
|
|
|
|
define("ACCESS_HANDLE_DIRECT", 1);
|
|
define("ACCESS_HANDLE_FUNCTION", 2);
|
|
|
|
define("REGISTER_SEL", true);
|
|
define("DONT_REGISTER_SEL", false);
|
|
|
|
define("USE_HANDLE", true);
|
|
define("DONT_USE_HANDLE", false);
|
|
|
|
class ObjectivePParser extends ObjectivePParserBase {
|
|
|
|
/**
|
|
* UTILITIES
|
|
*/
|
|
|
|
// Skips blocks in the current file being parsed
|
|
function SkipBlock ($line) {
|
|
|
|
if ($line != "") {
|
|
foreach ($this->skip_blocks as $key => $value) {
|
|
if (@ereg($key, $line)) {
|
|
$this->parser_skipping = true;
|
|
}
|
|
if (@ereg($value, $line)) {
|
|
$this->parser_skipping = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return $this->parser_skipping;
|
|
}
|
|
|
|
function IsKeywordReserved($keyword) {
|
|
$keyword = strtolower($keyword);
|
|
if (in_array($keyword, $this->reserved_keywords)) return true;
|
|
if (($this->current_class != null) &&
|
|
($this->dump["master"][$this->current_class["name"]] ["field_names"] != null) &&
|
|
in_array($keyword, $this->dump["master"][$this->current_class["name"]] ["field_names"])) return true;
|
|
}
|
|
|
|
// Extracts the name from an Objective-C method definition
|
|
function ExtractObjcMethodName ($method) {
|
|
|
|
// cut out comments first
|
|
$method = eregi_replace("(/\*.*\*/)", "", $method);
|
|
$method = eregi_replace("//.*$", "", $method);
|
|
$method = trim($method, " ");
|
|
|
|
$params = explode(":", $method);
|
|
$name = "";
|
|
|
|
if (count($params) > 1) {
|
|
foreach ($params as $value) {
|
|
$value = trim($value, " ");
|
|
|
|
if (eregi("([a-zA-Z0-9_]+)[[:space:]]*$", $value, $captures)) $name .= $captures[1].":";
|
|
|
|
/*
|
|
// paremeter with no label (or first parameter)
|
|
if (eregi("\([a-zA-Z0-9_]+\)[[:space:]]*([a-zA-Z0-9]+)[[:space:]]*$", $value, $captures)) {
|
|
if ($name != "") {
|
|
$name .= ":";
|
|
} else {
|
|
$name .= $captures[1].":";
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
// parameter with label
|
|
if (eregi("\([a-zA-Z0-9_]+\)[[:space:]]*[a-zA-Z0-9_]+[[:space:]]+([a-zA-Z0-9]+)$", $value, $captures)) $name .= $captures[1].":";
|
|
*/
|
|
}
|
|
} else {
|
|
if (eregi("([a-zA-Z0-9_]+)[[:space:]]*(;)*$", $method, $captures)) $name = $captures[1];
|
|
}
|
|
|
|
return $name;
|
|
}
|
|
|
|
|
|
function MaybeRenameMethod(&$name, $isclassmethod) {
|
|
if ($isclassmethod) {
|
|
foreach ($this->replace_class_methods as $org_name => $replace_name) {
|
|
if ($name == $org_name) {
|
|
$name = $replace_name;
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
foreach ($this->replace_instance_methods as $org_name => $replace_name) {
|
|
if ($name == $org_name) {
|
|
$name = $replace_name;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// in case of a non-function pointer and non-pointer type,
|
|
// split a C field expression (e.g. "long afield" or
|
|
// "long int :32") into its type and field name. Necessary
|
|
// to recognise name that "long int :32" means "a field
|
|
// without a name whose type/alignment is "long int" and that is
|
|
// bitpacked
|
|
function ExtractCFieldSimpleTypeAndSingleName($param) {
|
|
// print("Converting field $param\n");
|
|
|
|
// remove all "const" occurrences to simplify matching
|
|
$clean_param = str_replace_word("const", "", $param);
|
|
|
|
$got_type = false;
|
|
// assume not a pointer type
|
|
if (preg_match("!^\s*(unsigned|signed)\b!", $clean_param, $signedunsigned)) {
|
|
$got_type = true;
|
|
$clean_param = preg_replace("!^\s*(unsigned|signed)\b!", "", $clean_param);
|
|
}
|
|
if (preg_match("!^\s*(char|short|int|long\s+long\s+int|long\s+int|long\s+long|long)\b!", $clean_param, $inttype)) {
|
|
$got_type = true;
|
|
$clean_param = preg_replace("!^\s*(char|short|int|long\s+long\s+int|long\s+int|long\s+long|long)\b!", "", $clean_param);
|
|
}
|
|
if ($got_type) {
|
|
// remove bitfield and array, are encoded later
|
|
$result["name"] = trim(preg_replace("!(?::.*)?(?:\[[^]]*\])?!","",$clean_param));
|
|
$result["type"] = trim(preg_replace("!\s+!", " ", $signedunsigned[0] . " " . $inttype[0]));
|
|
} else {
|
|
// remove "struct"
|
|
$clean_param = str_replace_word("struct", "", $clean_param);
|
|
// has to be "type fieldname"
|
|
preg_match("!^\s*(\w+)\b\s*(\w+)(?:\[[0-9]*\])?\s*!", $clean_param, $matches);
|
|
$result["name"] = $matches[2]; // can be empty
|
|
$result["type"] = $matches[1];
|
|
}
|
|
// print("field conversion result: ".$result["name"].": ".$result["type"]."\n");
|
|
return $result;
|
|
}
|
|
|
|
function ExtractCParaNameAndType($param) {
|
|
// print("Converting $param\n");
|
|
|
|
// remove all "const" occurrences to simplify matching
|
|
$clean_param = str_replace_word("const", "", $param);
|
|
// remove all spaces between multiple pointer modifiers
|
|
$clean_param = trim(preg_replace("![*]\s+[*]!", "**", $clean_param));
|
|
// 1) varargs
|
|
if ($clean_param == "...") {
|
|
$result["name"] = "";
|
|
$result["pointermods"] = "";
|
|
$result["type"] = "...";
|
|
// 2) type is a function pointer (does not yet match arrays of function
|
|
// pointers!)
|
|
} elseif (preg_match($this->pregex_function_pointer_c_paratype, $clean_param, $matches)) {
|
|
$result["name"] = $matches[3];
|
|
// not correct; simply assume no "pointer to function pointer" types are
|
|
// used
|
|
$result["pointermods"] = "";
|
|
$funcptrtype = $this->ParseFunctionDeclaration($matches[1], $matches[2], "", $matches[4], false, "");
|
|
$result["type"] = $this->AddCallback($matches[3], $funcptrtype);
|
|
// 3) any other pointer type (including arrays)
|
|
} elseif (preg_match("!^([^*]*)([*]+)\s*(\w+(?:\[[0-9]*\])?)?\s*$!", $clean_param, $matches)){
|
|
$result["name"] = $matches[3]; // can be empty
|
|
$result["pointermods"] = $matches[2];
|
|
$result["type"] = trim($matches[1]);
|
|
// 4) basic C type (including arrays)
|
|
} else {
|
|
// definitely not a pointer type
|
|
$result["pointermods"] = "";
|
|
$got_type = false;
|
|
if (preg_match("!^\s*(unsigned|signed)!", $clean_param, $signedunsigned)) {
|
|
$got_type = true;
|
|
$clean_param = preg_replace("!^\s*(unsigned|signed)!", "", $clean_param);
|
|
}
|
|
if (preg_match("!^\s*(char|short|int|long\s+long\s+int|long\s+int|long\s+long|long)!", $clean_param, $inttype)) {
|
|
$got_type = true;
|
|
$clean_param = preg_replace("!^\s*(char|short|int|long\s+long\s+int|long\s+int|long\s+long|long)!", "", $clean_param);
|
|
}
|
|
if ($got_type) {
|
|
$result["name"] = trim($clean_param);
|
|
$result["type"] = trim($signedunsigned[0] . " " . $inttype[0]);
|
|
} else {
|
|
// remove "struct"
|
|
$clean_param = str_replace_word("struct", "", $clean_param);
|
|
// has to be "type paraname", or just "type"
|
|
preg_match("!^\s*(\w+)\b\s*(\w+(?:\[[0-9]*\])?)?\s*!", $clean_param, $matches);
|
|
$result["name"] = $matches[2]; // can be empty
|
|
$result["type"] = $matches[1];
|
|
}
|
|
}
|
|
// print("param conversion result: ".$result["name"].": ".$result["pointermods"].$result["type"]."\n");
|
|
return $result;
|
|
}
|
|
|
|
// Returns the parameter modifier string for a callback parameter type
|
|
function GetCallbackParameterModifiers ($type, $name) {
|
|
$modifiers = "";
|
|
$type = trim($type, "*");
|
|
|
|
// if the name starts with * it's a pointer
|
|
// don't process framework classes since they're always pointers
|
|
if (ereg("^[[:space:]]*\*", $name)) {
|
|
if (!in_array($type, $this->cocoa_classes)) $modifiers = "var ";
|
|
}
|
|
|
|
// double ** are always var
|
|
if (ereg("^[[:space:]]*\*\*", $name)) {
|
|
$modifiers = "var ";
|
|
}
|
|
|
|
return $modifiers;
|
|
}
|
|
|
|
// Print string to output file
|
|
function PrintOutput ($indent, $string) {
|
|
for ($i=0; $i < $indent; $i++) {
|
|
$indent_string .= " ";
|
|
}
|
|
|
|
if (($this->output) && (!$this->show)) fwrite($this->output, "$indent_string$string\n");
|
|
|
|
if ($this->show) print("$indent_string$string\n");
|
|
}
|
|
|
|
// Returns a class hierarchy array
|
|
function GetClassHierarchy ($class, &$hierarchy) {
|
|
if (!$hierarchy) $hierarchy = array();
|
|
$hierarchy[] = $class["name"];
|
|
|
|
if ($class["super_class"]) {
|
|
$hierarchy[] = $this->GetClassHierarchy($class["super_class"], $hierarchy);
|
|
}
|
|
|
|
return $class["name"];
|
|
}
|
|
|
|
// Returns all protected keywords in a class hierarchy
|
|
function GetProtectedKeywords ($in_class) {
|
|
$this->GetClassHierarchy($in_class, $hierarchy);
|
|
$keywords = array();
|
|
|
|
foreach ($hierarchy as $class) {
|
|
if ($this->dump["master"][$class]["protected_keywords"]) {
|
|
foreach ($this->dump["master"][$class]["protected_keywords"] as $keyword) $keywords[] = strtolower($keyword);
|
|
}
|
|
}
|
|
|
|
return $keywords;
|
|
}
|
|
|
|
// Gets the preferred property name from attributes
|
|
function GetPropertyName ($kind, $params, &$name) {
|
|
foreach ($params as $value) {
|
|
$pair = explode("=", $value);
|
|
|
|
if ($pair[0] == $kind) {
|
|
$name = $pair[1];
|
|
return true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Checks if a line has a property and returns the parts
|
|
function LineHasProperty ($line, &$parts) {
|
|
if (eregi($this->regex_objc_property_attributes, $line, $captures)) {
|
|
$parts = $captures;
|
|
//print_r($parts);
|
|
return true;
|
|
} elseif (eregi($this->regex_objc_property, $line, $captures)) {
|
|
$parts = $captures;
|
|
//print_r($parts);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// Returns header a category should be moved to
|
|
function FindCategoryHeader ($category) {
|
|
|
|
foreach ($this->dump as $name => $header) {
|
|
if ((@array_key_exists($category, $header["classes"])) && ($category != "NSObject")) {
|
|
return $name;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Checks if $class (array) contains $method (array)
|
|
function ClassContainsMethod ($class, $method) {
|
|
if ($class["methods"]) {
|
|
foreach ($class["methods"] as $key) {
|
|
if ($key["name"] == $method["name"]) return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
function GetAnonBitFieldName() {
|
|
$name = "_anonbitfield_".$this->current_header["name_clean"].$this->current_header["anonbitfields"];
|
|
$this->current_header["anonbitfields"]++;
|
|
return $name;
|
|
}
|
|
|
|
// create a variant normal record with a first anonymous field of type
|
|
// first_bitpacked_type so that the compiler may correctly align it
|
|
// the actual bitpacked record is embedded inside
|
|
function BitPackedForceAlignment($first_bitpacked_type, $firstindent, $otherindents) {
|
|
$result = $firstindent . "case byte of\n" . $otherindents ."0: (" . $this->GetAnonBitFieldName() . ": $first_bitpacked_type);\n" . $otherindents . "1: (data: bitpacked record";
|
|
return $result;
|
|
}
|
|
|
|
function EncodePointerModifiers($type, $pointertype) {
|
|
if ($pointertype[0] == "*") $type = $this->ReplacePointerType($type);
|
|
if ($pointertype[1] == "*") {
|
|
if ($type == "Pointer") {
|
|
$type = "PPointer";
|
|
} elseif ($type == "PChar") {
|
|
$type = "PPChar";
|
|
} elseif (in_array($type, $this->cocoa_classes)) {
|
|
$type = $type.$this->class_pointer_suffix;
|
|
} else {
|
|
// to match MacOSAll types
|
|
$type = $type."Ptr";
|
|
}
|
|
}
|
|
return $type;
|
|
}
|
|
|
|
/**
|
|
* DOCSETS UTILITIES
|
|
*/
|
|
|
|
function FindDocumentationForMethod ($class, $name) {
|
|
if ($this->docset) {
|
|
$doc = $this->docset[$class][$name];
|
|
|
|
if ($doc) return "{ $doc }";
|
|
}
|
|
}
|
|
|
|
function FindDocumentationForType ($name) {
|
|
if ($this->docset) {
|
|
foreach ($this->docset as $class) {
|
|
foreach ($class as $type => $text) {
|
|
if ($type == $name) return "{ $text }";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* ADDING LANGUAGE STRUCTURE UTILITIES
|
|
*/
|
|
|
|
// Adds a method structure to a class and performs checks for overloaded methods
|
|
function AddMethodToClass (&$method, &$class) {
|
|
|
|
// ignore methods
|
|
if (in_array($method["name"], $this->ignore_methods)) return false;
|
|
|
|
// add comment to the method
|
|
$method["comment"] = $this->InsertCurrentComment();
|
|
|
|
if (@!in_array($method["name"], $class["declared_methods"])) {
|
|
|
|
$class["all"][$method["name"]] = $method;
|
|
$class["protected_keywords"][] = $method["name"];
|
|
$class["declared_methods"][] = $method["name"];
|
|
$this->dump["all_methods"][$class["name"]][] = $method["objc_method"];
|
|
|
|
if ($this->show_added_messages) print(" @ Added ".$method["name"]." to ".$class["name"]."\n");
|
|
|
|
$this->method_count ++;
|
|
return true;
|
|
} else {
|
|
print(" ! ".$method["def"]." already exists in ".$class["name"]." defined as ".$class["all"][$method["name"]]["def"]."\n");
|
|
}
|
|
}
|
|
|
|
// Adds a typedef to the header and handles organization to prevent order conflicts
|
|
function AddTypeDef (&$header, $typedef) {
|
|
|
|
//$this->AppendCurrentMacro($header["types"]["typedef"]);
|
|
$this->AppendCurrentComment($header["types"]["typedef"]);
|
|
if ($this->comment_terminated) $header["types"]["typedef"][] = $this->InsertCurrentComment();
|
|
|
|
$header["types"]["typedef"][] = $typedef;
|
|
}
|
|
|
|
// adds a function callback type with name $name, and returns
|
|
// the name of the callback type
|
|
function AddCallback($name, $function_pointer) {
|
|
if ($this->current_header) {
|
|
$type = $this->current_header["name_clean"].ucwords($name);
|
|
|
|
if (!@in_array($function_pointer, $this->current_header["types"]["callbacks"])) {
|
|
$count = 0;
|
|
while (@array_key_exists($type, $this->current_header["types"]["callbacks"])) {
|
|
$count ++;
|
|
$type = "$type$count";
|
|
}
|
|
|
|
// append the new type to the the current class
|
|
$this->current_header["types"]["callbacks"][$type] = $function_pointer;
|
|
} else {
|
|
// Use the name of the existing callback of matching type
|
|
$type = array_search($function_pointer, $this->current_header["types"]["callbacks"]);
|
|
}
|
|
return $type;
|
|
}
|
|
return "invalid_callback_type_because_no_current_header";
|
|
}
|
|
|
|
/**
|
|
* OBJC FORMATTING UTILITIES
|
|
*/
|
|
|
|
// Performs additional formatting on Objective-c type i.e. (out NSObject **)
|
|
function FormatObjcType ($type, &$modifiers) {
|
|
$modifiers = "";
|
|
|
|
// toss out all const identifiers
|
|
$type = istr_replace_word("const", "", $type);
|
|
|
|
// replace inout paramaters
|
|
$type = istr_replace_word("inout", "", $type);
|
|
$type = istr_replace_word("out", "", $type);
|
|
|
|
// Translate protocol which type conforms to (id <NSURLHandleClient>)
|
|
$type = preg_replace("!id\s*<([^,>]*)>!", "$1Protocol", $type);
|
|
// Remove other protocol types
|
|
$type = trim(eregi_replace("<[^>]*>", "", $type));
|
|
|
|
// Replace types before cleaning
|
|
$type = $this->ReplaceObjcType($type);
|
|
|
|
// Remove array brackets (NSUInteger[])p
|
|
$type = trim(eregi_replace("\[[0-9]*\]", "", $type));
|
|
|
|
// var params to non-object types (NSRange *)
|
|
if (ereg("([a-zA-Z0-9_]+)[[:space:]]*[*]+$", $type, $captures)) {
|
|
if (!in_array($captures[1], $this->cocoa_classes)) {
|
|
$type = $this->ReplaceReferenceParameterType(trim($type,"* "));
|
|
//$modifiers = "var ";
|
|
}
|
|
}
|
|
|
|
// Handle NS*** pointers (NSError **)
|
|
if (ereg("(NS[a-zA-Z0-9_]+)[[:space:]]*\*\*$", $type, $captures)) {
|
|
if (in_array($captures[1], $this->cocoa_classes)) {
|
|
$type = trim($type,"* ")."$this->class_pointer_suffix";
|
|
}
|
|
}
|
|
|
|
// clean the type
|
|
$type = trim($type, "* ");
|
|
|
|
//print("$type\n");
|
|
return $type;
|
|
}
|
|
|
|
// Performs additional formatting on Objective-c parameter types
|
|
function FormatObjcParams ($string, &$variable_arguments) {
|
|
$params = explode(":", $string);
|
|
$string = "";
|
|
$variable_arguments = false;
|
|
// print_r($params);
|
|
|
|
if (count($params) > 0) {
|
|
foreach ($params as $value) {
|
|
|
|
// parameter is varargs
|
|
if (eregi("(.*),[[:space:]]*\.\.\.", $value)) {
|
|
$string .= ":(id)$this->varargs_param_name";
|
|
$variable_arguments = true;
|
|
//print("$value\n");
|
|
continue;
|
|
}
|
|
|
|
if (preg_match("!^[^(]*$!",$value)) {
|
|
// parameter without type -> id
|
|
$value = "(id)".$value;
|
|
}
|
|
|
|
// normal parameter
|
|
if (ereg("\((.*)\)", $value, $captures)) {
|
|
$new_value = $this->ReplaceObjcType($captures[1]);
|
|
|
|
if ($new_value != $captures[1]) $value = ereg_replace("\((.*)\)", "($new_value)", $value);
|
|
|
|
$string .= ":$value";
|
|
continue;
|
|
}
|
|
|
|
}
|
|
}
|
|
$string = ltrim($string, ":");
|
|
return $string;
|
|
}
|
|
|
|
/**
|
|
* SYNTAX MAKING UTILITIES
|
|
*/
|
|
|
|
// Makes a paramater list string with options to modify
|
|
function MakeParamList ($param_array, $use_handle, $cast_handle, $direct, $register_selector) {
|
|
$params = "";
|
|
foreach ($param_array as $pair) {
|
|
|
|
// register selector parameters
|
|
if (($register_selector) && ($pair["type"] == "SEL")) {
|
|
$params .= "sel_registerName(".$pair["name"]."), ";
|
|
continue;
|
|
}
|
|
|
|
// use the object handle for NSObject descendants
|
|
if ($use_handle) {
|
|
if (in_array($pair["type"], $this->cocoa_classes)) {
|
|
|
|
// cast the param to the original class type
|
|
if ($cast_handle) {
|
|
if ($direct == ACCESS_HANDLE_DIRECT) {
|
|
$params .= $pair["type"]."(".$pair["name"].".Handle), ";
|
|
} else {
|
|
$params .= $pair["type"]."(GetHandle(".$pair["name"].")), ";
|
|
}
|
|
} else {
|
|
if ($direct == ACCESS_HANDLE_DIRECT) {
|
|
$params .= $pair["name"].".Handle, ";
|
|
} else {
|
|
$params .= "GetHandle(".$pair["name"]."), ";
|
|
}
|
|
}
|
|
|
|
} else {
|
|
if (($this->objects_are_wrappers) && ($pair["type"] == $this->objc_id)) { // id is always a wrapper
|
|
if ($direct == ACCESS_HANDLE_DIRECT) {
|
|
$params .= $pair["type"]."(".$pair["name"].".Handle), ";
|
|
} else {
|
|
$params .= $pair["type"]."(GetHandle(".$pair["name"].")), ";
|
|
}
|
|
} else {
|
|
$params .= $pair["name"].", ";
|
|
}
|
|
}
|
|
} else { // append without modification
|
|
$params .= $pair["name"].", ";
|
|
}
|
|
}
|
|
|
|
return trim($params, ", ");
|
|
}
|
|
|
|
// Makes a list of paramameter variables with NS*** class types cast to "id" or the original class
|
|
function MakeObjcTypeParamList ($param_array, $objc_type) {
|
|
$params = "";
|
|
foreach ($param_array as $pair) {
|
|
if (in_array($pair["type"], $this->cocoa_classes)) {
|
|
if ($objc_type) {
|
|
$params .= "$this->objc_id(".$pair["name"]."), ";
|
|
} else {
|
|
$params .= $pair["type"]."(".$pair["name"]."), ";
|
|
}
|
|
} else {
|
|
$params .= $pair["name"].", ";
|
|
}
|
|
}
|
|
return trim($params, ", ");
|
|
}
|
|
|
|
// Makes a struct field into an inline array (or returns field un-changed)
|
|
function MakeFieldInlineArray ($io_field, $line, $name, $type) {
|
|
|
|
if (eregi("\[([^]]+)\];", $line, $array_size)) {
|
|
if ($array_size[1] == "")
|
|
$io_field = "$name: array[0..0] of $type; { dynamically expanding, 0 elements in C }";
|
|
else if ($array_size[1] == "0")
|
|
$io_field = "$name: record end; { array of 0 elements in C, does not allocate space }";
|
|
else
|
|
// array_size[1] may be a symbolic constant rather than a number, so don't calculate in php
|
|
$io_field = "$name: array[0..($array_size[1])-1] of $type;";
|
|
}
|
|
|
|
return $io_field;
|
|
}
|
|
|
|
// Makes a type bitpacked (or returns type un-changed)
|
|
function MakeFieldBitPacked ($ioType, $field, &$bitpacked) {
|
|
$bitpacked = false;
|
|
|
|
if (preg_match("!:([0-9]+)\s*(?:__attribute__\(\([^)]*\)\))?\s*;\s*$!", $field, $bitpack)) {
|
|
$length = (int)$bitpack[1];
|
|
if ($length > 1) {
|
|
$ioType = "0..((1 shl $length)-1)";
|
|
} else {
|
|
$ioType = "0..$length";
|
|
}
|
|
|
|
$bitpacked = true;
|
|
}
|
|
|
|
return $ioType;
|
|
}
|
|
|
|
/**
|
|
* REPLACEMENT UTILITIES
|
|
*/
|
|
|
|
// Replace type with pointer equivalent
|
|
function ReplacePointerType ($type) {
|
|
$found = false;
|
|
|
|
// all classes are implicit pointer types
|
|
if (in_array($type, $this->cocoa_classes)) return $type;
|
|
|
|
// function pointer types are sometimes also implicit
|
|
// pointers
|
|
if (in_array($type, $this->implicit_function_pointer_types)) return $type;
|
|
|
|
// PPointer = ^Pointer
|
|
if ($type == "Pointer") return "PPointer";
|
|
if ($type == "PChar") return "PPChar";
|
|
|
|
// use preferred pointer type
|
|
foreach ($this->pointer_types as $objc_type => $replace_type) {
|
|
if ($objc_type == $type) {
|
|
$found = true;
|
|
$type = $replace_type;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//$type = "Pointer {".$type."}";
|
|
// use generic pointer type
|
|
if (!$found)
|
|
$type = $type."Ptr";
|
|
|
|
return $type;
|
|
}
|
|
|
|
// Replace objc type with preferred type
|
|
function ReplaceObjcType ($type) {
|
|
|
|
foreach ($this->replace_types as $objc_type => $replace_type) {
|
|
if ($objc_type == $type) {
|
|
$type = $replace_type;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return $type;
|
|
}
|
|
|
|
// Replace garbage collector hints
|
|
function ReplaceGarbageCollectorHints ($string, &$io_hint) {
|
|
$io_hint = false;
|
|
|
|
foreach ($this->garbage_collector_hints as $hint) {
|
|
$out_string = str_ireplace($hint, "", $string);
|
|
if ($out_string != $string) {
|
|
$io_hint = $hint;
|
|
$string = $out_string;
|
|
}
|
|
}
|
|
|
|
return $string;
|
|
}
|
|
|
|
// Replace remote message passing modifiers
|
|
function ReplaceRemoteMessagingModifiers ($string, &$io_modifier) {
|
|
$io_hint = false;
|
|
|
|
foreach ($this->remote_messaging_modifiers as $modifier) {
|
|
$out_string = preg_replace("!\b".$modifier."\b!", "", $string);
|
|
if ($out_string != $string) {
|
|
$io_modifier = $modifier;
|
|
$string = $out_string;
|
|
}
|
|
}
|
|
|
|
return trim($string);
|
|
}
|
|
|
|
// Replace type of reference parameter with pointer
|
|
function ReplaceReferenceParameterType ($type) {
|
|
$type = $this->ReplaceObjcType($type);
|
|
return $this->ReplacePointerType($type);
|
|
}
|
|
|
|
// Replace a framework class with generic id and comment hint
|
|
function ReplaceFrameworkClassWithID ($string) {
|
|
foreach ($this->cocoa_classes as $class) {
|
|
$string = istr_replace_word($class, $this->objc_id, $string);
|
|
}
|
|
return $string;
|
|
}
|
|
|
|
|
|
/**
|
|
* MACRO UTILITIES
|
|
*/
|
|
|
|
// Insert macro blocks to replace c-style blocks
|
|
function InsertMacroBlocks ($line, &$in_macro_block) {
|
|
|
|
// only insert if we are in a block already.
|
|
// NOTE: this does not handle nesting!
|
|
if ($in_macro_block) {
|
|
|
|
// macro else statment
|
|
if (eregi("#else", $line)) {
|
|
return "{\$else}";
|
|
}
|
|
|
|
// macro endif statment
|
|
if (eregi("#endif", $line)) {
|
|
$in_macro_block = false;
|
|
return "{\$endif}";
|
|
}
|
|
}
|
|
|
|
foreach ($this->macro_blocks as $key => $value) {
|
|
|
|
if (eregi($key, $line, $captures)) {
|
|
$in_macro_block = true;
|
|
|
|
// replace the c-macro with a Pascal version
|
|
if ($value == "*") {
|
|
$captures[0] = trim($captures[0], "#");
|
|
return "{\$".$captures[0]."}";
|
|
} else {
|
|
return "{".$value."}";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Appends the current macro to a recipient and resets it's reference
|
|
function AppendCurrentMacro (&$recipient) {
|
|
if ($this->in_macro_block) {
|
|
$this->in_macro_block = false;
|
|
|
|
$recipient[] = $this->macro_block;
|
|
//if (is_array($recipient)) $recipient[] = $this->macro_block;
|
|
//if (is_string($recipient)) $recipient .= $this->macro_block;
|
|
}
|
|
}
|
|
|
|
// Returns the current macro and resets it's reference
|
|
function InsertCurrentMacro () {
|
|
if ($this->in_macro_block) {
|
|
$this->in_macro_block = false;
|
|
return $this->macro_block;
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
// Build the current macro block
|
|
function BuildMacroBlocks ($line) {
|
|
|
|
// only insert if we are in a block already.
|
|
// NOTE: this does not handle nesting!
|
|
if ($this->in_macro_block) {
|
|
|
|
// macro else statment
|
|
if (eregi("#else", $line)) {
|
|
$this->macro_block = "{\$else}";
|
|
}
|
|
|
|
// macro endif statment
|
|
if (eregi("#endif", $line)) {
|
|
$this->in_macro_block = false;
|
|
$this->macro_block = "{\$endif}";
|
|
}
|
|
}
|
|
|
|
foreach ($this->macro_blocks as $key => $value) {
|
|
|
|
if (eregi($key, $line, $captures)) {
|
|
$this->in_macro_block = true;
|
|
|
|
// replace the c-macro with a Pascal version
|
|
if ($value == "*") {
|
|
$captures[0] = trim($captures[0], "#");
|
|
$this->macro_block = "{\$".$captures[0]."}";
|
|
} else {
|
|
$this->macro_block = "{".$value."}";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Remove OS X versions macros from a line
|
|
// NOTE: These should be re-inlcuded in Pascal
|
|
function RemoveVersionMacros ($line, &$deprecatedmods) {
|
|
if (preg_match($this->pregex_deprecated_macro,$line)) {
|
|
$deprecatedmods = DeprecatedMacroToDirective($line);
|
|
} else {
|
|
$deprecatedmods = "";
|
|
}
|
|
foreach ($this->version_macros as $macro) {
|
|
$line = eregi_replace($macro, "", $line);
|
|
}
|
|
return $line;
|
|
}
|
|
|
|
/**
|
|
* CONVERTING UTILITIES
|
|
*/
|
|
|
|
// Converts an Objective-c parameter string to Pascal
|
|
function ConvertObjcParamsToPascal ($string, $protected_keywords) {
|
|
$params = explode(":", $string);
|
|
$list = array();
|
|
$list["pairs"] = array();
|
|
$param_list = array();
|
|
//print_r($params);
|
|
//print("$string\n");
|
|
|
|
if (count($params) > 0) {
|
|
|
|
foreach ($params as $value) {
|
|
$value = trim($value);
|
|
$valid = false;
|
|
$modifiers = "";
|
|
|
|
$value = $this->ReplaceRemoteMessagingModifiers($value, $null);
|
|
|
|
// function pointer (callback)
|
|
if (preg_match($this->pregex_function_pointer_objc_paratype, $value, $captures)) {
|
|
$name = $captures[5];
|
|
|
|
$function_pointer = $this->ParseFunctionDeclaration($captures[1], $captures[2], "", $captures[4], false, "");
|
|
$type = $this->AddCallback($name, $function_pointer);
|
|
$valid = true;
|
|
} elseif ((eregi("\(([a-zA-Z_]+).*\)([a-zA-Z_]+).*\.\.\.", $value, $captures)) || (eregi("(.*),[[:space:]]*\.\.\.", $value, $captures))) { // variable arguments
|
|
$name = $captures[2];
|
|
$type = $captures[1];
|
|
$valid = true;
|
|
} elseif (eregi("\((.*)\)[[:space:]]*([a-zA-Z_]+)", $value, $captures)) { // standard parameter
|
|
|
|
$captures[1] = trim($captures[1]);
|
|
$type = $this->FormatObjcType($captures[1], $modifiers);
|
|
$name = $captures[2];
|
|
|
|
$valid = true;
|
|
}
|
|
|
|
if ($valid) {
|
|
|
|
// protect reserved keywords
|
|
if ($this->IsKeywordReserved($name)) $name .= "_";
|
|
|
|
if (!in_array($type, $this->reserved_types)) {
|
|
if ($this->IsKeywordReserved($type)) $type .= "_";
|
|
}
|
|
|
|
// can't have "boolean: boolean;" parameters
|
|
if (preg_match("!\b$name\b!i",$type)) $name .= "_";
|
|
|
|
// print("para name: \"$name\"\n");
|
|
// print_r("protected: \"$protected_keywords\"\n");
|
|
while (@in_array(strtolower($name), $protected_keywords)) $name .= "_";
|
|
|
|
// case-insensitive in_array:
|
|
// if( preg_match("/\b$name\b/i", join(" ", array_values($protected_keywords))) ) $name .= "_";
|
|
// if (@in_array($type, $protected_keywords)) $type .= "_";
|
|
|
|
// replace objc types
|
|
$type = $this->ReplaceObjcType($type);
|
|
|
|
// make sure we label duplicate params, which are allowed in Objective-C
|
|
while (in_array($name, $param_list)) {
|
|
$count ++;
|
|
$name = "$name$count";
|
|
}
|
|
|
|
// id is always a wrapper
|
|
if (($this->objects_are_wrappers) && ($type == $this->objc_id)) {
|
|
$name_list = "$type(GetHandle($name))";
|
|
} else {
|
|
$name_list = $name;
|
|
}
|
|
|
|
// add modifiers to the name if there are any
|
|
$name_with_modifiers = $modifiers.$name;
|
|
|
|
// create pair array
|
|
$pair["name"] = $name;
|
|
$pair["type"] = $type;
|
|
|
|
// append list
|
|
$list["pairs"][] = $pair;
|
|
$list["string_with_modifiers"] .= "$name_with_modifiers: $type; ";
|
|
$list["string"] .= "$name: $type; ";
|
|
$list["list"] .= "$name_list, ";
|
|
$param_list[] = $name;
|
|
}
|
|
}
|
|
}
|
|
// clean up the strings
|
|
$list["string"] = trim($list["string"], "; ");
|
|
$list["string_with_modifiers"] = trim($list["string_with_modifiers"], "; ");
|
|
$list["list"] = trim($list["list"], ", ");
|
|
|
|
return $list;
|
|
}
|
|
|
|
// Converts an Objective-C method to Pascal format
|
|
function ConvertObjcMethodToPascal ($class, $source, $parts, $protected_keywords, $has_params, $deprecatedmods) {
|
|
|
|
//print("$source\n");
|
|
//print_r($parts);
|
|
|
|
// replace "hinted" params comment with hinted type
|
|
if ($this->replace_hinted_params) {
|
|
|
|
// param string
|
|
if (eregi("(/\*[[:space:]]*(.*)[[:space:]]*\*/)", $parts[4], $captures)) {
|
|
// ??? change the parameter to the hinted type
|
|
//$parts[4] = eregi_replace("(/\*.*\*/)", $captures[2], $parts[4]);
|
|
//$parts[4] = trim($parts[4], " ");
|
|
}
|
|
|
|
// return type
|
|
if (eregi("(/\*[[:space:]]*(.*)[[:space:]]*\*/)", $parts[2], $captures)) $parts[2] = $this->ReplaceRemoteMessagingModifiers($captures[2], $null);
|
|
|
|
//print_r($parts);
|
|
|
|
} else { // remove comments from params and return type
|
|
$parts[4] = eregi_replace("(/\*.*\*/)", "", $parts[4]);
|
|
$parts[4] = trim($parts[4]);
|
|
|
|
$parts[2] = eregi_replace("(/\*.*\*/)", "", $parts[2]);
|
|
$parts[2] = $this->ReplaceRemoteMessagingModifiers($parts[2], $null);
|
|
}
|
|
|
|
$return_type_clean = $parts[2];
|
|
$return_type_pointers = preg_replace("![^*]+!e", "", $return_type_clean);
|
|
$return_type_clean = trim($return_type_clean,"* ");
|
|
|
|
// perform preformatting before attempting to protect keywords
|
|
$parts[2] = $this->FormatObjcType($parts[2], $modifiers);
|
|
$parts[4] = $this->FormatObjcParams($parts[4], $variable_arguments);
|
|
//print($parts[4]."\n");
|
|
|
|
if ($has_params) {
|
|
$name = $this->ConvertObjcMethodName($source);
|
|
|
|
// merge default protected keywords for the class/category
|
|
if ($this->default_protected["*"]) $protected_keywords = array_merge($this->default_protected["*"], $protected_keywords);
|
|
if ($this->default_protected[$class]) $protected_keywords = array_merge($this->default_protected[$class], $protected_keywords);
|
|
|
|
|
|
$param_array = $this->ConvertObjcParamsToPascal($parts[4], $protected_keywords);
|
|
$params = "(".$param_array["string"].")";
|
|
$params_with_modifiers = "(".$param_array["string_with_modifiers"].")";
|
|
|
|
} else {
|
|
$params = "";
|
|
$params_with_modifiers = "";
|
|
// no parameters -> definitely no underscore normally, but there are some
|
|
// conflicts...
|
|
$name = $parts[3];
|
|
// clean it up
|
|
if ($this->trailing_underscore) {
|
|
if (in_array($source, $this->trailing_underscore_methods)) $name = $name . "_";
|
|
}
|
|
$param_array = null;
|
|
$variable_arguments = false;
|
|
}
|
|
|
|
// rename method if required
|
|
$this->MaybeRenameMethod($name,$parts[1]=="+");
|
|
|
|
// protect method name from keywords
|
|
if ($this->IsKeywordReserved($name)) $name .= "_";
|
|
|
|
// replace objc type
|
|
$return_type = $this->ConvertReturnType($return_type_clean,$return_type_pointers);
|
|
|
|
$virtual = "";
|
|
$class_prefix = "";
|
|
|
|
// determine the type based on return value
|
|
if (ereg($this->regex_procedure_type, $return_type_clean.$return_type_pointers)) {
|
|
$kind = "procedure";
|
|
} else {
|
|
$kind = "function";
|
|
}
|
|
|
|
// determine if this is a class method
|
|
if ($parts[1] == "+") {
|
|
$class_prefix = "class ";
|
|
|
|
// These methods probably return the an allocated instance of the class, a typical convenience method.
|
|
// ??? Ack! $class may be the category or protocol name
|
|
//if ($return_type == $this->objc_id) $return_type = $class;
|
|
}
|
|
|
|
// Replace SEL with the string equivalent
|
|
if ($this->register_selectors) {
|
|
$params_with_modifiers = str_replace_word("SEL", $this->sel_string, $params_with_modifiers);
|
|
}
|
|
|
|
// detect blocks (not yet supported)
|
|
$has_blocks = strpos("$return_type $params_with_modifiers","^");
|
|
|
|
// make method templates
|
|
if ($kind != "function") {
|
|
if ($variable_arguments) $modifier .= " varargs;";
|
|
|
|
$method = "$class_prefix$kind $name$params_with_modifiers;$modifier$virtual";
|
|
$method_template = "[KIND] [PREFIX]$name"."[PARAMS];$modifier";
|
|
} else {
|
|
if ($variable_arguments) $return_type = "$return_type; varargs";
|
|
|
|
$method = $class_prefix."function $name$params_with_modifiers: $return_type;$modifier$virtual";
|
|
$method_template = "[KIND] [PREFIX]$name"."[PARAMS]: [RETURN];$modifier";
|
|
}
|
|
|
|
$method_template_procedure = "procedure [PREFIX]$name"."[PARAMS];$modifier";
|
|
$method_template_function = "function [PREFIX]$name"."[PARAMS]: [RETURN];$modifier";
|
|
|
|
// build structure
|
|
$struct["def"] = $method;
|
|
$struct["template"] = $method_template;
|
|
$struct["template_function"] = $method_template_function;
|
|
$struct["template_procedure"] = $method_template_procedure;
|
|
$struct["objc_method"] = $this->ExtractObjcMethodName($source);
|
|
$struct["class_prefix"] = $class_prefix;
|
|
if ($deprecatedmods != "") $struct["deprecated"] = $deprecatedmods.";";
|
|
//$struct["def_objc"] = eregi("(.*);", $source, $captures[1]);
|
|
if ($return_type == "void") $return_type = "";
|
|
$struct["return"] = $return_type;
|
|
|
|
if (in_array($return_type, $this->cocoa_classes)) $struct["returns_wrapper"] = true;
|
|
$struct["param_string_clean"] = trim($params, "()");
|
|
$struct["param_string_clean_with_modifiers"] = trim($params_with_modifiers, "()");
|
|
$struct["param_string"] = $params;
|
|
$struct["param_string_with_modifiers"] = $params_with_modifiers;
|
|
$struct["param_array"] = $param_array["pairs"];
|
|
$struct["param_list"] = $param_array["list"];
|
|
$struct["class"] = $class;
|
|
$struct["name"] = $name;
|
|
$struct["kind"] = $kind;
|
|
if ($has_blocks === false) $struct["blocks_disable_comment"] = "";
|
|
else $struct["blocks_disable_comment"] = "// ";
|
|
|
|
if ($struct["param_array"] != null) $struct["has_params"] = true;
|
|
|
|
// FPC bug work around
|
|
if (strlen($name) > $this->maximum_method_length) {
|
|
$struct["can_override"] = false;
|
|
print(" # WARNING: method $name can't override because the name is too long\n");
|
|
$this->warning_count ++;
|
|
}
|
|
|
|
return $struct;
|
|
}
|
|
|
|
// Converts a C parameter string to Pascal
|
|
function ConvertCParamsPascal ($string) {
|
|
|
|
// print("params: $string\n");
|
|
if ((trim($string) == "void") || (trim($string) == "")) return "";
|
|
|
|
$params = explode(",", $string);
|
|
$count = 0;
|
|
$param_string = "";
|
|
|
|
foreach ($params as $param) {
|
|
|
|
$name_type = $this->ExtractCParaNameAndType($param);
|
|
$type = $name_type["type"];
|
|
$name = $name_type["name"];
|
|
$pointertype = $name_type["pointermods"];
|
|
|
|
// create name if none is specified
|
|
if ($name == "") {
|
|
$count ++;
|
|
$name = "param$count";
|
|
}
|
|
|
|
// remove const keywords
|
|
$type = str_replace_word("const", "", $type);
|
|
$type = $this->ReplaceObjcType($type);
|
|
|
|
// Remove array brackets (NSUInteger[])p
|
|
if (eregi("\[[0-9]*\]", $name)) {
|
|
$orgtype = $this->EncodePointerModifiers($type,$pointertype);
|
|
$pointertype .= "*";
|
|
$type = $this->EncodePointerModifiers($type,$pointertype)." {array of $orgtype}";
|
|
$name = eregi_replace("\[[0-9]*\]", "", $name);
|
|
} else {
|
|
$type = $this->EncodePointerModifiers($type,$pointertype);
|
|
}
|
|
$modifier = "";
|
|
|
|
if ($this->IsKeywordReserved($name)) $name .= "_";
|
|
|
|
// multiple parameters
|
|
if ($type == "...") {
|
|
$param_string .= "varargs: array of const";
|
|
break;
|
|
}
|
|
|
|
$param_string .= "$modifier$name: $type; ";
|
|
}
|
|
|
|
$param_string = trim($param_string, "; ");
|
|
//print("$param_string\n");
|
|
return $param_string;
|
|
}
|
|
|
|
// Converts an Objective-c method name to Pascal
|
|
function ConvertObjcMethodName ($method) {
|
|
$params = explode(":", $method);
|
|
$name = "";
|
|
$count = 0;
|
|
|
|
if (count($params) > 1) {
|
|
foreach ($params as $value) {
|
|
if (eregi("([a-zA-Z0-9]+)$", $value, $captures)) $name .= $captures[1]."_";
|
|
}
|
|
} else {
|
|
if (eregi("([a-zA-Z0-9]+)(;)*$", $params[0], $captures)) $name .= $captures[1]."_";
|
|
}
|
|
|
|
// clean it up
|
|
if ($this->trailing_underscore) {
|
|
if (!in_array($method, $this->trailing_underscore_methods)) $name = trim($name, "_");
|
|
}
|
|
|
|
$name = $this->ReplaceObjcType($name);
|
|
|
|
return $name;
|
|
}
|
|
|
|
// Convert a method return type to Pascal
|
|
function ConvertReturnType ($type, $pointertype) {
|
|
$type = $this->ReplaceGarbageCollectorHints($type, $null);
|
|
$type = $this->ReplaceRemoteMessagingModifiers($type, $null);
|
|
|
|
// format the return type to make sure it's clean
|
|
$type = $this->FormatObjcType($type, $null_modifier);
|
|
// translate type to Pascal
|
|
$type = $this->ReplaceObjcType($type);
|
|
// incorportate pointer modifiers
|
|
$type = $this->EncodePointerModifiers($type,$pointertype);
|
|
|
|
return $type;
|
|
}
|
|
|
|
/**
|
|
* USER PATCHES
|
|
*/
|
|
function InsertPatches ($header) {
|
|
$path = "$this->root/patches/".$header["name_clean"].".patch";
|
|
if ($handle = @fopen($path, "r")) {
|
|
$text = file_get_contents($path);
|
|
$this->PrintOutput(0, $text);
|
|
fclose($handle);
|
|
}
|
|
}
|
|
|
|
function HeaderContainsPatch ($header) {
|
|
if ($handle = @fopen("$this->root/patches/".$header["name_clean"].".patch", "r")) {
|
|
fclose($handle);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* COMMENTS
|
|
*/
|
|
|
|
// Trim a comment string
|
|
function TrimComment ($comment, $preserve_line_endings) {
|
|
|
|
// trim line breaks
|
|
if (!$preserve_line_endings) $comment = trim($comment, "\n");
|
|
|
|
// remove remains of C comments
|
|
$comment = eregi_replace("^[!*/ ]+", "", $comment);
|
|
$comment = eregi_replace("[!*/ ]+$", "", $comment);
|
|
|
|
// remove all Pascal comment characters to prevent nesting
|
|
$comment = str_replace(array("{", "}"), "", $comment);
|
|
|
|
//print("$comment\n");
|
|
return $comment;
|
|
|
|
}
|
|
|
|
// Builds comments from $line spaning multiple lines or at the end of lines
|
|
// Returns a terminated comment string or a fragment in $fragment for futher processing
|
|
function BuildComment ($line, $file) {
|
|
|
|
// set the current comment header being parsed
|
|
$this->comment_header = $file;
|
|
|
|
// comment parsing is off, bail!
|
|
if (!$this->parse_comments) return;
|
|
|
|
// single-line comment at start of line
|
|
if (eregi("^[[:space:]]*//(.*)", $line, $captures)) {
|
|
//print("$captures[1]\n");
|
|
// ignore comment
|
|
foreach ($this->ignore_comments as $pattern) {
|
|
if (eregi($pattern, $captures[1])) return;
|
|
}
|
|
|
|
$this->comment_terminated = $this->TrimComment($captures[1], false);
|
|
return;
|
|
} elseif (eregi("[[:space:]]+//(.*)", $line, $captures)) { // single-line comments at end of line
|
|
//print("$captures[1]\n");
|
|
// ignore comment
|
|
foreach ($this->ignore_comments as $pattern) {
|
|
if (eregi($pattern, $captures[1])) return;
|
|
}
|
|
|
|
$this->comment_eol = $captures[1];
|
|
return;
|
|
}
|
|
|
|
// multi-line comments terminated
|
|
if (eregi("/\*(.*)\*/", $line, $captures)) {
|
|
//print("$captures[1]\n");
|
|
|
|
// ignore comment
|
|
foreach ($this->ignore_comments as $pattern) {
|
|
if (eregi($pattern, $captures[1])) return;
|
|
}
|
|
|
|
$this->comment_terminated = $this->TrimComment($captures[1], false);
|
|
return;
|
|
}
|
|
|
|
// terminate comment fragment
|
|
if ($this->comment_fragment_open) {
|
|
if (eregi("(.*)\*/", $line, $captures)) {
|
|
|
|
// append fragment
|
|
$comment = $this->TrimComment($captures[1], false);
|
|
if ($comment) $this->comment_fragment .= $comment;
|
|
|
|
// closed comment block
|
|
if (!$captures[1]) {
|
|
$this->comment_block_closed = true;
|
|
}
|
|
|
|
// set terminated comment with fragment
|
|
$this->comment_terminated = $this->comment_fragment;
|
|
|
|
// add extra line break for comment blocks
|
|
if (($this->comment_block_open) && ($this->comment_block_closed)) {
|
|
// ??? the printing will cut this out if we add line endings!
|
|
$this->comment_terminated = "$this->comment_fragment";
|
|
}
|
|
|
|
$this->comment_fragment = null;
|
|
$this->comment_fragment_open = false;
|
|
|
|
$this->comment_block_open = false;
|
|
$this->comment_block_closed = false;
|
|
|
|
return;
|
|
} else {
|
|
// build the fragment
|
|
$comment = $this->TrimComment($line, true);
|
|
|
|
// ignore comment and stop building fragment
|
|
foreach ($this->ignore_comments as $pattern) {
|
|
if (eregi($pattern, $comment)) {
|
|
$this->comment_fragment = null;
|
|
$this->comment_fragment_open = false;
|
|
|
|
$this->comment_block_open = false;
|
|
$this->comment_block_closed = false;
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (($this->comment_fragment_previous != $line) && ($comment)) $this->comment_fragment .= $comment."\n";
|
|
|
|
$this->comment_fragment_previous = $line;
|
|
return;
|
|
}
|
|
}
|
|
|
|
// start comment fragment
|
|
if (eregi("/\*(.*)", $line, $captures)) {
|
|
|
|
$this->comment_terminated = null;
|
|
|
|
// ignore comment
|
|
foreach ($this->ignore_comments as $pattern) {
|
|
if (eregi($pattern, $captures[1])) return;
|
|
}
|
|
|
|
$this->comment_fragment_open = true;
|
|
$this->comment_fragment = "";
|
|
$this->comment_block_open = true;
|
|
$this->comment_block_closed = false;
|
|
|
|
// prevent against empty comments
|
|
if ((!$captures[1]) || ($captures[1] == "\n")) {
|
|
$this->comment_block_open = true;
|
|
return;
|
|
}
|
|
|
|
// append the line if valid
|
|
$comment = $this->TrimComment($captures[1], false);
|
|
|
|
if ($comment) $this->comment_fragment .= $comment;
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Resets current comment references
|
|
function ResetComment () {
|
|
$this->comment_fragment = null;
|
|
$this->comment_eol = null;
|
|
$this->comment_terminated = null;
|
|
$this->comment_block_open = false;
|
|
$this->comment_block_closed = false;
|
|
}
|
|
|
|
// Checks/sets if a comment is a duplicate in the file
|
|
function CheckDuplicateComment ($comment) {
|
|
if ((@!in_array($comment, $this->dump[$this->comment_header]["comments"])) && ($comment)) {
|
|
$this->dump[$this->comment_header]["comments"][] = $comment;
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
|
|
}
|
|
|
|
// Appends the eol comment to the output and clears the reference
|
|
function AppendEOLComment () {
|
|
if ($this->comment_eol) {
|
|
$comment = " // ".$this->TrimComment($this->comment_eol, false);
|
|
$this->comment_eol = "";
|
|
return $comment;
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
// Inserts the recently terminated comment to the output on a new line and clears the reference
|
|
function InsertCurrentComment () {
|
|
if (($this->comment_terminated) && ($this->CheckDuplicateComment($this->comment_terminated))) {
|
|
|
|
if ($this->comment_terminated != $this->comment_previous) $comment = "$this->comment_break{".$this->comment_padding_left.$this->TrimComment($this->comment_terminated, false).$this->comment_padding_right."}";
|
|
|
|
$this->comment_previous = $this->comment_terminated;
|
|
$this->comment_terminated = "";
|
|
|
|
return $comment;
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
// Appends the recently terminated comment to a recipient and clears the reference
|
|
function AppendCurrentComment (&$recipient) {
|
|
if (($this->comment_terminated) && ($this->CheckDuplicateComment($this->comment_terminated)) && (gettype($recipient) == "array")) {
|
|
|
|
if ($this->comment_terminated != $this->comment_previous) $comment = "$this->comment_break{".$this->comment_padding_left.$this->TrimComment($this->comment_terminated, false).$this->comment_padding_right."}";
|
|
|
|
$recipient[] = $comment;
|
|
//if (is_array($recipient)) $recipient[] = $comment;
|
|
//if (is_string($recipient)) $recipient .= $comment;
|
|
|
|
$this->comment_previous = $this->comment_terminated;
|
|
$this->comment_terminated = "";
|
|
}
|
|
}
|
|
|
|
// Removes all comments from a line
|
|
function RemoveComments ($line) {
|
|
// remove single-line comments
|
|
$line = eregi_replace("[[:space:]]+//(.*)", "", $line);
|
|
|
|
// remove multi-line comments /* ... */
|
|
$line = eregi_replace("/\*.*\*/", "", $line);
|
|
|
|
return $line;
|
|
}
|
|
|
|
/**
|
|
* PRINTING METHODS
|
|
*/
|
|
|
|
function PrintClass ($class) {
|
|
|
|
$this->PrintOutput(0, "");
|
|
$this->PrintOutput(0, "{ ".$class["name"]." }");
|
|
|
|
if ($class["comment"]) $this->PrintOutput(0, $class["comment"]);
|
|
//print_r($class["methods"]);
|
|
|
|
// print super class or protocol which the class conforms to
|
|
if ($class["adopts"]) {
|
|
if ($class["super"]) {
|
|
$this->PrintOutput(1, $class["name"]." = objcclass external (".$class["super"].", ".$class["adopts"].")");
|
|
} else {
|
|
$this->PrintOutput(1, $class["name"]." = objcclass external (".$class["adopts"].")");
|
|
}
|
|
} elseif ($class["super"]) {
|
|
$this->PrintOutput(1, $class["name"]." = objcclass external (".$class["super"].")");
|
|
} else {
|
|
$this->PrintOutput(1, $class["name"]." = objcclass external");
|
|
}
|
|
|
|
// print instance variables
|
|
if ($class["ivars"]) {
|
|
$this->PrintOutput(1, "private");
|
|
foreach ($class["ivars"] as $ivar) {
|
|
$this->PrintOutput(2, $ivar);
|
|
}
|
|
}
|
|
|
|
// print methods (public)
|
|
$this->PrintOutput(2, "");
|
|
$this->PrintOutput(1, "public");
|
|
|
|
// print class-level methods
|
|
if ($class["methods"]) {
|
|
foreach ($class["methods"] as $method) {
|
|
if ($method["comment"]) $this->PrintOutput(2, $method["comment"]);
|
|
if ($method["documentation"]) $this->PrintOutput(2, $method["documentation"]);
|
|
$this->PrintOutput(2, $method["blocks_disable_comment"].$method["def"]." message '".$method["objc_method"]."';".$method["deprecated"]);
|
|
}
|
|
}
|
|
|
|
// print adopted protocol methods
|
|
if (count($class["protocols"]) > 0) {
|
|
$this->PrintOutput(0, "");
|
|
$this->PrintOutput(2, "{ Adopted Protocols }");
|
|
//print_r($this->dump["protocols"]);
|
|
|
|
foreach ($class["protocols"] as $name) {
|
|
if ($this->dump["protocols"][$name]) {
|
|
foreach ($this->dump["protocols"][$name] as $method) {
|
|
if (!$this->ClassContainsMethod($class, $method)) $this->PrintOutput(2, $method["blocks_disable_comment"].$method["def"]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
$this->PrintOutput(1, "end;");
|
|
}
|
|
|
|
function PrintCategory ($class, $category) {
|
|
|
|
// declare real category if external
|
|
if ($category["external"]) {
|
|
$new_name = " name '".$category["external_name"]."'";
|
|
}
|
|
|
|
$category_name = $category["name"].$this->category_suffix;
|
|
|
|
$this->PrintOutput(0, "");
|
|
$this->PrintOutput(0, "{ $category_name }");
|
|
if ($category["comment"]) $this->PrintOutput(0, $category["comment"]);
|
|
|
|
// print super class or protocol which the class conforms to
|
|
$this->PrintOutput(1, "$category_name = objccategory external$new_name (".$category["super"].")");
|
|
|
|
// print methods
|
|
if ($category["methods"]) {
|
|
foreach ($category["methods"] as $method) {
|
|
if ($method["comment"]) $this->PrintOutput(2, $method["comment"]);
|
|
$this->PrintOutput(2, $method["blocks_disable_comment"].$method["def"]." message '".$method["objc_method"]."';".$method["deprecated"]);
|
|
}
|
|
}
|
|
|
|
$this->PrintOutput(1, "end;");
|
|
}
|
|
|
|
function PrintHeader ($header) {
|
|
global $version;
|
|
//print_r($header);
|
|
//print_r($this->dump["categories"]);
|
|
|
|
// open the output file if we not printing to terminal
|
|
if (!$this->show) {
|
|
if ($this->merge_headers) {
|
|
$this->output = fopen($header["path_merge"], "w+");
|
|
} else {
|
|
$this->output = fopen($header["path"], "w+");
|
|
}
|
|
}
|
|
|
|
$this->PrintOutput(0, "{ Parsed from ".ucfirst($header["framework"]).".framework ".$header["name"]." }");
|
|
|
|
//$date = @date("D M j G:i:s T Y");
|
|
//$this->PrintOutput(0, "{ Version: $version - $date }");
|
|
//$this->PrintOutput(0, "");
|
|
|
|
$macro = strtoupper(substr($header["name"], 0, (strripos($header["name"], "."))));
|
|
|
|
$this->PrintOutput(0, "");
|
|
$this->PrintOutput(0, "{\$ifdef TYPES}");
|
|
$this->PrintOutput(0, "{\$ifndef $macro"."_PAS_T}");
|
|
$this->PrintOutput(0, "{\$define $macro"."_PAS_T}");
|
|
$this->PrintTypes($header, false);
|
|
$this->PrintOutput(0, "");
|
|
$this->PrintOutput(0, "{\$endif}");
|
|
$this->PrintOutput(0, "{\$endif}");
|
|
|
|
$this->PrintOutput(0, "");
|
|
$this->PrintOutput(0, "{\$ifdef RECORDS}");
|
|
$this->PrintOutput(0, "{\$ifndef $macro"."_PAS_R}");
|
|
$this->PrintOutput(0, "{\$define $macro"."_PAS_R}");
|
|
|
|
// Records from types
|
|
$this->PrintRecords($header);
|
|
|
|
$this->PrintOutput(0, "");
|
|
$this->PrintOutput(0, "{\$endif}");
|
|
$this->PrintOutput(0, "{\$endif}");
|
|
|
|
$this->PrintOutput(0, "");
|
|
$this->PrintOutput(0, "{\$ifdef FUNCTIONS}");
|
|
$this->PrintOutput(0, "{\$ifndef $macro"."_PAS_F}");
|
|
$this->PrintOutput(0, "{\$define $macro"."_PAS_F}");
|
|
$this->PrintFunctions($header);
|
|
$this->PrintOutput(0, "");
|
|
$this->PrintOutput(0, "{\$endif}");
|
|
$this->PrintOutput(0, "{\$endif}");
|
|
|
|
$this->PrintOutput(0, "");
|
|
$this->PrintOutput(0, "{\$ifdef EXTERNAL_SYMBOLS}");
|
|
$this->PrintOutput(0, "{\$ifndef $macro"."_PAS_S}");
|
|
$this->PrintOutput(0, "{\$define $macro"."_PAS_S}");
|
|
$this->PrintExternalSymbols($header);
|
|
$this->PrintOutput(0, "");
|
|
$this->PrintOutput(0, "{\$endif}");
|
|
$this->PrintOutput(0, "{\$endif}");
|
|
|
|
// insert user patches
|
|
if ($this->HeaderContainsPatch($header)) {
|
|
$this->PrintOutput(0, "");
|
|
$this->PrintOutput(0, "{\$ifdef USER_PATCHES}");
|
|
//$this->PrintOutput(0, "{\$ifndef $macro"."_PAS_PATCH}");
|
|
//$this->PrintOutput(0, "{\$define $macro"."_PAS_PATCH}");
|
|
$this->InsertPatches($header);
|
|
$this->PrintOutput(0, "");
|
|
//$this->PrintOutput(0, "{\$endif}");
|
|
$this->PrintOutput(0, "{\$endif}");
|
|
}
|
|
|
|
// print class/protocol forward declarations
|
|
if (($header["classes"]) || ($header["protocols"])) {
|
|
$this->PrintOutput(0, "");
|
|
$this->PrintOutput(0, "{\$ifdef FORWARD}");
|
|
|
|
if ($header["protocols"]) {
|
|
foreach ($header["protocols"] as $protocol) $this->PrintOutput(1, $protocol["name"]."$this->protocol_suffix = objcprotocol;");
|
|
}
|
|
|
|
if ($header["classes"]) {
|
|
foreach ($header["classes"] as $class) {
|
|
if ($class["name"]) {
|
|
$this->PrintOutput(1, $class["name"]." = objcclass;");
|
|
$this->PrintOutput(1, $class["name"].$this->class_pointer_suffix." = ^".$class["name"].";");
|
|
// for consistency also offer Ptr-name variant
|
|
$this->PrintOutput(1, $class["name"]."Ptr = ".$class["name"].$this->class_pointer_suffix.";");
|
|
}
|
|
}
|
|
}
|
|
$this->PrintOutput(0, "");
|
|
$this->PrintOutput(0, "{\$endif}");
|
|
}
|
|
|
|
// print classes
|
|
if ($header["classes"]) {
|
|
$this->PrintOutput(0, "");
|
|
$this->PrintOutput(0, "{\$ifdef CLASSES}");
|
|
$this->PrintOutput(0, "{\$ifndef $macro"."_PAS_C}");
|
|
$this->PrintOutput(0, "{\$define $macro"."_PAS_C}");
|
|
|
|
foreach ($header["classes"] as $class) {
|
|
if ($class["name"]) $this->PrintClass($class);
|
|
}
|
|
|
|
if (count($header["categories"]) > 0) {
|
|
foreach ($header["categories"] as $category) {
|
|
$this->PrintCategory($class, $category);
|
|
}
|
|
}
|
|
|
|
$this->PrintOutput(0, "");
|
|
$this->PrintOutput(0, "{\$endif}");
|
|
$this->PrintOutput(0, "{\$endif}");
|
|
}
|
|
|
|
// print protocols
|
|
if ($header["protocols"]) {
|
|
$this->PrintOutput(0, "{\$ifdef PROTOCOLS}");
|
|
$this->PrintOutput(0, "{\$ifndef $macro"."_PAS_P}");
|
|
$this->PrintOutput(0, "{\$define $macro"."_PAS_P}");
|
|
|
|
foreach ($header["protocols"] as $protocol) {
|
|
$this->PrintOutput(1, "");
|
|
$this->PrintOutput(0, "{ ".$protocol["name"]." Protocol }");
|
|
if ($protocol["comment"]) $this->PrintOutput(0, $protocol["comment"]);
|
|
$this->PrintOutput(1, $protocol["name"]."$this->protocol_suffix = objcprotocol external name '".$protocol["name"]."'");
|
|
|
|
// print methods
|
|
if ($protocol["methods"]) {
|
|
$section="";
|
|
foreach ($protocol["methods"] as $name => $method) {
|
|
// print the required/optional section
|
|
if ($method["section"] != $section) {
|
|
$section = $method["section"];
|
|
$this->PrintOutput(1, $section);
|
|
}
|
|
if ($method["comment"]) $this->PrintOutput(2, $method["comment"]);
|
|
$this->PrintOutput(2, $method["blocks_disable_comment"].$method["def"]." message '".$method["objc_method"]."';".$method["deprecated"]);
|
|
}
|
|
}
|
|
|
|
$this->PrintOutput(1, "end;");
|
|
}
|
|
|
|
$this->PrintOutput(0, "{\$endif}");
|
|
$this->PrintOutput(0, "{\$endif}");
|
|
}
|
|
}
|
|
|
|
// Prints all externally defined symbols
|
|
function PrintExternalSymbols ($header) {
|
|
if (!$this->dump[$header["name"]]["types"]) return;
|
|
foreach ($this->dump[$header["name"]]["types"] as $key => $type_array) {
|
|
|
|
// External string constants
|
|
if ($key == "string_constant") {
|
|
$this->PrintOutput(0, "");
|
|
$this->PrintOutput(0, "{ External string constants }");
|
|
$this->PrintOutput(0, "var");
|
|
|
|
foreach ($type_array as $type) $this->PrintOutput(1, $type);
|
|
}
|
|
|
|
if ($key == "external_symbol") {
|
|
$this->PrintOutput(0, "");
|
|
$this->PrintOutput(0, "{ External symbols }");
|
|
$this->PrintOutput(0, "var");
|
|
|
|
foreach ($type_array as $type) $this->PrintOutput(1, $type);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Prints all types in the header
|
|
function PrintTypes ($header) {
|
|
if (!$this->dump[$header["name"]]["types"]) return;
|
|
|
|
foreach ($this->dump[$header["name"]]["types"] as $key => $type_array) {
|
|
$section_printed = false;
|
|
|
|
// External defines
|
|
if ($key == "defines") {
|
|
|
|
foreach ($type_array as $type) {
|
|
if (!$section_printed) {
|
|
$this->PrintOutput(0, "");
|
|
$this->PrintOutput(0, "{ Defines }");
|
|
$this->PrintOutput(0, "const");
|
|
$section_printed = true;
|
|
}
|
|
$this->PrintOutput(1, $type);
|
|
}
|
|
}
|
|
|
|
// Enumerations
|
|
if ($key == "enums") {
|
|
$this->PrintOutput(0, "");
|
|
$this->PrintOutput(0, "{ Constants }");
|
|
foreach ($type_array as $block) {
|
|
$section_printed = false;
|
|
|
|
foreach ($block as $type) {
|
|
if (!$section_printed) {
|
|
$this->PrintOutput(0, "");
|
|
$this->PrintOutput(0, "const");
|
|
$section_printed = true;
|
|
}
|
|
|
|
$this->PrintOutput(1, $type);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Typedefs
|
|
if (($key == "typedef") || ($key == "named_enums")) {
|
|
foreach ($type_array as $type) {
|
|
if (!$section_printed) {
|
|
$this->PrintOutput(0, "");
|
|
$this->PrintOutput(0, "{ Types }");
|
|
$this->PrintOutput(0, "type");
|
|
$section_printed = true;
|
|
}
|
|
|
|
$this->PrintOutput(1, $type);
|
|
}
|
|
}
|
|
|
|
// CallBacks
|
|
if ($key == "callbacks") {
|
|
foreach ($type_array as $name => $type) {
|
|
if (!$section_printed) {
|
|
$this->PrintOutput(0, "");
|
|
$this->PrintOutput(0, "{ Callbacks }");
|
|
$this->PrintOutput(0, "type");
|
|
$section_printed = true;
|
|
}
|
|
|
|
$this->PrintOutput(1, "$name = $type");
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
// Prints all records in the header
|
|
function PrintRecords ($header) {
|
|
if (!$this->dump[$header["name"]]["types"]) return;
|
|
|
|
foreach ($this->dump[$header["name"]]["types"] as $key => $type_array) {
|
|
// Structures
|
|
if ($key == "structs") {
|
|
$this->PrintOutput(0, "");
|
|
$this->PrintOutput(0, "{ Records }");
|
|
|
|
foreach ($type_array as $type) {
|
|
$this->PrintOutput(0, "type");
|
|
$this->PrintOutput(1, $type);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Prints all external functions in the header
|
|
function PrintFunctions ($header) {
|
|
if (!$this->dump[$header["name"]]["types"]) return;
|
|
|
|
foreach ($this->dump[$header["name"]]["types"] as $key => $type_array) {
|
|
if ($key == "functions") {
|
|
$this->PrintOutput(0, "");
|
|
$this->PrintOutput(0, "{ Functions }");
|
|
|
|
foreach ($type_array as $type) $this->PrintOutput(0, $type);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Prints all classes from the header in reference format (not for compiling)
|
|
function PrintHeaderReference ($header, $path) {
|
|
|
|
$this->output = fopen($path, "w+");
|
|
|
|
//$this->PrintOutput(0, "{ ".ucfirst($header["framework"]).".framework ".$header["name"]." }");
|
|
$this->PrintOutput(0, "unit ".$header["name_clean"].";");
|
|
$this->PrintOutput(0, "interface");
|
|
$this->PrintOutput(0, "uses");
|
|
$this->PrintOutput(1, "ctypes, objc, MacOSAll, AnonClassDefinitions;");
|
|
|
|
if ($header["classes"]) {
|
|
foreach ($header["classes"] as $class) {
|
|
$this->PrintOutput(0, "");
|
|
$this->PrintOutput(0, "type");
|
|
$this->PrintOutput(1, $class["name"]."Ref = ".$this->objc_id.";");
|
|
$this->PrintOutput(1, $class["name"]."Ptr = Pointer;");
|
|
}
|
|
}
|
|
|
|
// types
|
|
$this->PrintTypes($header);
|
|
$this->PrintRecords($header);
|
|
$this->PrintFunctions($header);
|
|
$this->PrintExternalSymbols($header);
|
|
|
|
if ($header["classes"]) {
|
|
|
|
foreach ($header["classes"] as $class) {
|
|
if (in_array($class["name"], $this->cocoa_classes)) {
|
|
$this->PrintOutput(0, "");
|
|
$this->PrintOutput(0, "type");
|
|
|
|
$this->PrintOutput(1, $class["name"]." = object(".$class["super"].")");
|
|
|
|
// print class-level methods
|
|
if (count($class["methods"]) > 0) {
|
|
$this->PrintOutput(0, "");
|
|
foreach ($class["methods"] as $method) {
|
|
$this->PrintOutput(2, $method["blocks_disable_comment"].$method["def"]);
|
|
}
|
|
}
|
|
|
|
// print category-level methods
|
|
if (count($class["categories"]) > 0) {
|
|
foreach ($class["categories"] as $name => $category) {
|
|
$this->PrintOutput(0, "");
|
|
$this->PrintOutput(2, "{ Category: $name }");
|
|
|
|
if ($category["methods"]) {
|
|
foreach ($category["methods"] as $method) {
|
|
$this->PrintOutput(2, $method["blocks_disable_comment"].$method["def"]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
$this->PrintOutput(1, "end;");
|
|
}
|
|
}
|
|
}
|
|
|
|
// print procedural protocols
|
|
if ($header["protocols"]) {
|
|
foreach ($header["protocols"] as $protocol) {
|
|
if ($protocol["methods"]) {
|
|
$this->PrintOutput(0, "");
|
|
$this->PrintOutput(0, "{ Protocol: ".$protocol["name"]." }");
|
|
|
|
foreach ($protocol["methods"] as $name => $method) {
|
|
if ($method["kind"] != "constructor") {
|
|
$this->PrintProtocolDeclaration($protocol, $method);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
$this->PrintOutput(0, "");
|
|
$this->PrintOutput(0, "implementation");
|
|
$this->PrintOutput(0, "end.");
|
|
}
|
|
|
|
// Merges two headers by difference
|
|
function MergeHeader ($header) {
|
|
/*
|
|
diff QLPreviewPanel_ORIGINAL.inc QLPreviewPanel_UPDATED.inc > diff.patch
|
|
patch QLPreviewPanel_ORIGINAL.inc -i diff.patch -o output.txt
|
|
|
|
patch QLPreviewPanel.inc -i header.patch -o QLPreviewPanel.inc.merged
|
|
|
|
also add -section="types,classes" which only prints into the sections and copies text from
|
|
the previous version if not specified
|
|
|
|
*/
|
|
|
|
// set the paths to use
|
|
$path = $header["path"];
|
|
$patch = "$this->root$this->out/$this->framework/header.patch";
|
|
$merged = $header["path_merge"];
|
|
|
|
// create a patch using diff
|
|
exec("/usr/bin/diff \"$path\" \"$merged\" > \"$patch\"");
|
|
|
|
// parse the diff file by mode
|
|
$lines = file($patch);
|
|
$content = "";
|
|
$section_lines = null;
|
|
$section_part = null;
|
|
$count = 0;
|
|
$section_id = 0;
|
|
//print_r($lines);
|
|
|
|
if ($lines) {
|
|
foreach ($lines as $line) {
|
|
$count++;
|
|
|
|
// remove line endings to aid regex
|
|
$line = trim($line, "\n");
|
|
|
|
// parse section
|
|
if (($section_lines) && (!eregi("^[0-9]+", $line))) {
|
|
|
|
// append line to section
|
|
$section_lines[] = "$line\n";
|
|
|
|
// the section id is adding
|
|
if (eregi("^>", $line)) $section_part[$section_id] = true;
|
|
if (eregi("^<", $line)) $section_part[$section_id] = 0;
|
|
|
|
// section is changing type
|
|
if ($line == "---") {
|
|
$section_id++;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// line is a new section or eof
|
|
if ((eregi("^[0-9]+", $line)) || ($count == count($lines))) {
|
|
|
|
// append the section to the content
|
|
// we only accept sections where the first part (before ---) contains additions ">"
|
|
//print_r($section_part);
|
|
if (($section_part[0]) && (!$section_part[1])) $content .= implode($section_lines);
|
|
|
|
// init the new section
|
|
$section_lines = array();
|
|
$section_lines[] = "$line\n";
|
|
$section_id = 0;
|
|
$section_part = array(null, null);
|
|
|
|
continue;
|
|
}
|
|
|
|
}
|
|
|
|
//print($content);
|
|
|
|
// write the parsed patch back to the file
|
|
if ($content) {
|
|
file_put_contents($patch, $content);
|
|
} else {
|
|
$content = null;
|
|
}
|
|
}
|
|
|
|
// patch the header to the merged file
|
|
if ($content) {
|
|
exec("/usr/bin/patch \"$path\" -i \"$patch\" -o \"$merged\" ");
|
|
|
|
// swap the contents of the merged file with the original
|
|
file_put_contents($path, file_get_contents($merged));
|
|
}
|
|
|
|
// delete the temporary files
|
|
unlink($patch);
|
|
unlink($merged);
|
|
}
|
|
|
|
function PrintGlobalClassInfo($all_classes, $defined_classes, $anon_classes) {
|
|
// add all classes as anonymous external classes to a separate unit.
|
|
// They will be overridden by the actual definitions in the translated
|
|
// headers part of the main unit, but this way they can appear as record
|
|
// field types and as callback parameters
|
|
|
|
// open the output file if we not printing to terminal
|
|
if (!$this->show) {
|
|
$this->output = fopen("$this->root$this->out/AnonClassDefinitions".ucfirst($this->framework).".pas", "w+");
|
|
}
|
|
|
|
$this->PrintOutput(0, "{ Parsed from ".ucfirst($this->framework)." }");
|
|
|
|
$date = @date("D M j G:i:s T Y");
|
|
|
|
$this->PrintOutput(0, "");
|
|
// allows parameter names conflicting with field names
|
|
$this->PrintOutput(0, "{\$mode delphi}");
|
|
$this->PrintOutput(0, "{\$modeswitch objectivec1}");
|
|
// enables "external" after the semi-colon
|
|
$this->PrintOutput(0, "{\$modeswitch cvar}");
|
|
$this->PrintOutput(0, "");
|
|
|
|
$this->PrintOutPut(0,"unit AnonClassDefinitions".ucfirst($this->framework).";");
|
|
$this->PrintOutput(0, "");
|
|
$this->PrintOutput(0, "interface");
|
|
$this->PrintOutput(0, "");
|
|
$this->PrintOutput(0, "type");
|
|
|
|
foreach ($all_classes as $class) {
|
|
$this->PrintOutput(1, $class." = objcclass external;");
|
|
}
|
|
|
|
$this->PrintOutput(0, "");
|
|
$this->PrintOutput(0, "implementation");
|
|
$this->PrintOutput(0, "");
|
|
$this->PrintOutput(0, "end.");
|
|
|
|
|
|
// Now all anonymous external classes that have no real definition to an
|
|
// include file that is added to the main unit. This way it is possible
|
|
// to declare variables of these types in user programs without having to
|
|
// include the unit above will all anonymous classes (should not be used)
|
|
|
|
// open the output file if we not printing to terminal
|
|
if (!$this->show) {
|
|
$this->output = fopen("$this->root$this->out/$this->framework/AnonIncludeClassDefinitions".ucfirst($this->framework).".inc", "w+");
|
|
}
|
|
|
|
$this->PrintOutput(0, "{ Parsed from ".ucfirst($this->framework)." }");
|
|
|
|
$date = @date("D M j G:i:s T Y");
|
|
|
|
|
|
// add all classes as anonymous external classes. They will be overridden
|
|
// by the actual definitions in the translated headers, but this way they
|
|
// can appear as record field types and as callback parameters
|
|
$first = true;
|
|
foreach ($anon_classes as $class) {
|
|
if (!in_array($class,$defined_classes)) {
|
|
if ($first) {
|
|
$this->PrintOutput(0, "type");
|
|
$first = false;
|
|
}
|
|
$this->PrintOutput(1, $class." = objcclass external;");
|
|
}
|
|
}
|
|
}
|
|
|
|
// Prints all headers parsed
|
|
function PrintAllHeaders ($output_path, $ignore_output, $only_files, $print_header_references) {
|
|
|
|
//print("• Printing ".count($this->dump)." headers...\n");
|
|
|
|
foreach ($this->dump as $file => $header) {
|
|
|
|
// the framework is set to not print, ignore the header
|
|
if (!$this->frameworks[$header["framework"]]["print"]) continue;
|
|
|
|
if (eregi("^[a-zA-Z0-9]+\.h", $file)) {
|
|
|
|
// ignore these files
|
|
if (@in_array($header["path_partial"], $ignore_output)) continue;
|
|
|
|
// only parse these files
|
|
if ((@count($only_files) > 0) && (@!in_array($header["name"], $only_files))) continue;
|
|
|
|
$name_clean = substr($file, 0, (strripos($file, ".")));
|
|
|
|
// assign output path
|
|
if ($output_path != "") {
|
|
$header["path"] = $output_path."/".$name_clean.".inc";
|
|
}
|
|
|
|
// print the header
|
|
$this->PrintHeader($header);
|
|
|
|
// merge the headers
|
|
if (($this->merge_headers) && (!$this->show)) {
|
|
$this->MergeHeader($header);
|
|
}
|
|
|
|
if ($print_header_references) $this->PrintHeaderReference($header, $this->root.$this->out."/reference/".$name_clean.".pas");
|
|
|
|
if (!$this->show) print("* Printed $name_clean.h to ".$header["path"]."\n");
|
|
}
|
|
}
|
|
|
|
// print global stuff (for Obj-P: a unit containing all classes as
|
|
// anonymous external classes, so they can be used before they are
|
|
// declared)
|
|
$this->PrintGlobalClassInfo($this->cocoa_classes, $this->defined_cocoa_classes, $this->anon_cocoa_classes);
|
|
}
|
|
|
|
/**
|
|
* PRE-PARSING METHODS
|
|
*/
|
|
|
|
// Parse all "pre-defined" category methods in a header
|
|
function PreparseCategoryMethods ($file) {
|
|
$contents = file_get_contents($file);
|
|
$file_name = substr($file, (strripos($file, "/")) + 1, strlen($file));
|
|
|
|
$lines = explode("\n", $contents);
|
|
foreach ($lines as $line) {
|
|
|
|
// skip blocks
|
|
if ($this->SkipBlock($line)) continue;
|
|
|
|
// parse category
|
|
if ($got_category) {
|
|
|
|
// build method fragment
|
|
if ($method_fragment) $method_fragment .= " ".trim($line, " ");
|
|
|
|
// found method fragment termination
|
|
if (($method_fragment) && (preg_match($this->pregex_objc_method_terminate, $line))) {
|
|
$line = $method_fragment;
|
|
$method_fragment = null;
|
|
}
|
|
|
|
// found method
|
|
$method = null;
|
|
if (preg_match($this->pregex_objc_method_params, $line, $captures)) {
|
|
$method = $this->ConvertObjcMethodToPascal($current_category, $line, $captures, array(), true, "");
|
|
} elseif (preg_match($this->pregex_objc_method_no_params, $line, $captures)) {
|
|
$method = $this->ConvertObjcMethodToPascal($current_category, $line, $captures, array(), false, "");
|
|
} elseif (preg_match($this->pregex_objc_method_partial, $line, $captures)) {
|
|
$method_fragment = $line;
|
|
}
|
|
|
|
// append to classes
|
|
if (($method) && ($current_class)) {
|
|
$this->dump[$category_owner]["category_methods"][] = $method["name"];
|
|
//print($method["name"]."\n");
|
|
}
|
|
|
|
// found the end
|
|
if (ereg("^@end", $line)) $got_category = false;
|
|
}
|
|
|
|
// got category
|
|
if (eregi($this->regex_objc_category, $line, $captures)) {
|
|
$category_owner = $this->FindCategoryHeader($captures[1]);
|
|
if ($category_owner) {
|
|
$got_category = true;
|
|
$current_category = $captures[2];
|
|
$current_class = $captures[1];
|
|
} else {
|
|
$current_class = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
return $this->dump[$category_owner]["category_methods"];
|
|
}
|
|
|
|
// Preparses a class for protected keywords
|
|
function PreparseClass ($lines, $line_count) {
|
|
$protected_keywords = array();
|
|
|
|
for ($i=$line_count; $i < count($lines); $i++) {
|
|
$line = $lines[$i - 1];
|
|
|
|
// skip blocks
|
|
if ($this->SkipBlock($line)) continue;
|
|
|
|
// build method fragment
|
|
if ($method_fragment) $method_fragment .= " ".trim($line, " ");
|
|
|
|
// found method fragment termination
|
|
if (($method_fragment) && (preg_match($this->pregex_objc_method_terminate, $line))) {
|
|
$line = $method_fragment;
|
|
$method_fragment = null;
|
|
}
|
|
|
|
// found method
|
|
if (preg_match($this->pregex_objc_method_params, $line, $captures)) {
|
|
$method = $this->ConvertObjcMethodToPascal($current, $line, $captures, $protected_keywords, true, "");
|
|
$this->current_class["protected_keywords"][] = strtolower($method["name"]);
|
|
} elseif (preg_match($this->pregex_objc_method_no_params, $line, $captures)) {
|
|
$method = $this->ConvertObjcMethodToPascal($current, $line, $captures, $protected_keywords, false, "");
|
|
$this->current_class["protected_keywords"][] = strtolower($method["name"]);
|
|
} elseif (preg_match($this->pregex_objc_method_partial, $line, $captures)) {
|
|
$method_fragment = $line;
|
|
}
|
|
|
|
// class ended
|
|
if (ereg("^@end", $line)) return $protected_keywords;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* PARSING METHODS
|
|
*/
|
|
|
|
function ParseNormalField($line, $protected_keywords, &$field_bitpacked, &$bitpacked_real_type, &$this_class_field_names) {
|
|
if (preg_match("!^\s*([^-{}*@+,<;:]*struct[^-*@+,<;:]+|[^-{}*@+,<;:]+)\b\s*(?:<([^>]*)>)?\s*([*]*)\s*(\w+)((?:\s*,\s*[^:;[]+)*)?\s*(:[0-9]+)?\s*(\[.*\])?\s*(?:__attribute__\(\(([^)]*)\)\))?\s*;!", $line, $captures)) { // regular field
|
|
// captures[1]: type name (may be multiple words, and may not be complete
|
|
// in case of an anonymous bitfield without a name)
|
|
// Curly braces are only allowed in case of structs
|
|
// captures[2]: in case of id<protocol list>, the protocol list, empty
|
|
// otherwise
|
|
// captures[3]: all pointer modifiers (*, **, ...), but only "*" and "**"
|
|
// currently handled
|
|
// captures[4]: the field name (may be last part of type in case of
|
|
// anonymous bit field, e.g. "unsigned long : 32")
|
|
// captures[5]: in case of multiple fields, the other field names (note
|
|
// that things will go wrong if some of the fields in the
|
|
// list are arrays/bitfields, that is only supported for
|
|
// a single field)
|
|
// captures[6]: bitfield specification if any, empty otherwise
|
|
// captures[7]: array specification if any, empty otherwise
|
|
// captures[8]: attributes if any, empty otherwise
|
|
|
|
|
|
if ($captures[3] != "") { // pointer type -> ok (cannot be wrongly interpreted)
|
|
$type = trim($captures[1]);
|
|
$pointertype = $captures[3];
|
|
$name = trim($captures[4]);
|
|
// print("pointer field: $name: $pointertype $type\n");
|
|
} else {
|
|
// make sure we properly interpret stuff like "unsigned int :32"
|
|
// print("regular: type = \"$captures[1]\", name = $captures[3]\n");
|
|
$pair = $this->ExtractCFieldSimpleTypeAndSingleName($captures[1]." ".$captures[4]);
|
|
$type = $pair["type"];
|
|
$name = $pair["name"];
|
|
if ($name == "") $name = $this->GetAnonBitFieldName();
|
|
// print("regular field: \"$name\": $type\n");
|
|
}
|
|
// print("field \"$name\": \"$type\", attr: $captures[8]\n");
|
|
|
|
// if we have id <protocollist> and there's only one protocol in the
|
|
// in the list, we can replace id with the protocol
|
|
if (($type == "id") && ($captures[2] != "") && !strstr(",", $captures[2])) {
|
|
// print("id<protocol>: $type -> $captures[2]Protocol\n");
|
|
$type = $captures[2]."Protocol";
|
|
}
|
|
// in case of "type field1, field2, field3", ", field2, field3" gets
|
|
// stored in othernames
|
|
$othernames = $captures[5];
|
|
$field_prefix = "";
|
|
|
|
// Multiple Objective-C fields cannot have the same name, but they
|
|
// are case-insensitive and some only differ in case (only "reserved"
|
|
// fields until now, but that can change)
|
|
while (in_array(strtolower("$field_prefix$name"),$protected_keywords)) $field_prefix.="_";
|
|
if ($this_class_field_names != null) {
|
|
while (in_array(strtolower("$field_prefix$name"),$this_class_field_names)) $field_prefix.="_";
|
|
}
|
|
if ($this->IsKeywordReserved($field_prefix.$name)) $field_prefix .= "_";
|
|
// protect the name of these fields, in case there are methods with
|
|
// the same name
|
|
$this_class_field_names[] = strtolower($field_prefix.$name);
|
|
if ($othernames != "") {
|
|
$other_lower_case_names = preg_split("/\s*,\s*/",$othernames,-1,PREG_SPLIT_NO_EMPTY);
|
|
array_walk($other_lower_case_names,strtolowerref);
|
|
// should actually also check these for conflicts and add underscores if necessary
|
|
$this_class_field_names = array_merge ($this_class_field_names, $other_lower_case_names);
|
|
}
|
|
|
|
/*
|
|
// we should also add prefixes to the other names if required, but my
|
|
// php-fu is too weak to replace the elements in original string
|
|
// efficiently, and this problem does not occur in the supported headers
|
|
foreach (explode($othernames, ",") as $othername) {
|
|
while (in_array(strtolower("$field_prefix$othername"),$protected_keywords)) $field_prefix.="_";
|
|
if ($this->IsKeywordReserved($field_prefix.$othernamename)) $field_prefix .= "_";
|
|
}
|
|
*/
|
|
// remove "struct" from the type
|
|
$type = preg_replace("!(\b)struct\b!","\1",$type);
|
|
|
|
// clean/convert type
|
|
$type = $this->ReplaceObjcType($type);
|
|
$bitpacked_real_type = $type;
|
|
$type = $this->MakeFieldBitPacked($type, $line, $field_bitpacked);
|
|
|
|
// add pointer modifiers
|
|
$type = $this->EncodePointerModifiers($type,$pointertype);
|
|
|
|
$field = "$field_prefix$name$othernames: $type";
|
|
if ($captures[8] && strstr("deprecated",$captures[8])) $field .= " deprecated";
|
|
$field .= ";";
|
|
$field = $this->MakeFieldInlineArray($field, $line, $name, $type);
|
|
$field = eregi_replace("<.*>", "", $field);
|
|
return $field;
|
|
}
|
|
return "";
|
|
}
|
|
|
|
function ParseInstanceVariables ($line, &$struct, $protected_keywords, &$this_class_field_names) {
|
|
$field = null;
|
|
$field_bitpacked = false;
|
|
// print("$line\n");
|
|
|
|
// insert macros
|
|
if ($macro = $this->InsertMacroBlocks($line, $this->inside_macro_block)) {
|
|
if ($struct["valid"]) {
|
|
$struct["fields"][] = $macro;
|
|
return null;
|
|
} else {
|
|
return $macro;
|
|
}
|
|
}
|
|
|
|
// got inline struct, probably a reference to a private struct
|
|
if (eregi("[[:space:]]*struct[[:space:]]+([a-zA-Z0-9_]+)[[:space:]]+\*([a-zA-Z0-9_]+)", $line, $captures)) {
|
|
if ($struct["valid"]) {
|
|
// ??? These don't compile as inline records which I thought they did...
|
|
//$struct["fields"][] = "$captures[1] = record end;";
|
|
//$struct["fields"][] = "$captures[1]Pointer = ^$captures[1];";
|
|
$struct["fields"][] = "$captures[2]: Pointer;";
|
|
$struct["ignore"][] = "$captures[2]";
|
|
return null;
|
|
} else {
|
|
//$fields = array();
|
|
//$fields[] = "$captures[1] = record end;";
|
|
//$fields[] = "$captures[1]Ptr = ^$captures[1];";
|
|
$field = "_$captures[2]: Pointer;";
|
|
return $field;
|
|
}
|
|
}
|
|
|
|
// got struct
|
|
if (eregi("^[[:space:]]*struct.*{", $line)) {
|
|
$struct["valid"] = true;
|
|
$struct["ispsuedostruct"] = false;
|
|
$struct["fieldnames"] = array();
|
|
return null;
|
|
}
|
|
|
|
// create an anonymous struct in case we have bitpacked fields without a
|
|
// surrounding struct
|
|
if (!$struct["valid"] && preg_match("!.*:[0-9]+\s*;!", $line)) {
|
|
$struct["valid"] = true;
|
|
$struct["ispsuedostruct"] = true;
|
|
$struct["fieldnames"] = array();
|
|
}
|
|
|
|
// end of bunch of bitfields -> go to normal mode again
|
|
if ($struct["ispsuedostruct"] && !preg_match("!.*:[0-9]+\s*;!", $line)) {
|
|
$struct["name"] = "_anoninternstruct_".$this->current_header["name_clean"].$this->current_header["anoninternstrucs"];
|
|
$this->current_header["anoninternstrucs"]++;
|
|
$struct["isfinished"] = true;
|
|
// make sure the current field isn't added anymore
|
|
$struct["valid"] = false;
|
|
}
|
|
|
|
// end of struct
|
|
if (eregi("^[[:space:]]*}[[:space:]]*([a-zA-Z_0-9]+);", $line, $captures)) {
|
|
$struct["name"] = "_".trim($captures[1], " ");
|
|
//print_r($struct);
|
|
$struct["isfinished"] = true;
|
|
return "struct";
|
|
}
|
|
|
|
// set field prefix to protect scope
|
|
if (!$struct["valid"]) $field_prefix = "_";
|
|
|
|
// remove null-defined macros:
|
|
$line = str_ireplace($this->null_macros, "", $line);
|
|
|
|
// replace garbage collector hints in the field
|
|
$line = $this->ReplaceGarbageCollectorHints($line, $garbage_collector_hint);
|
|
|
|
if (preg_match($this->pregex_function_pointer, $line, $captures)) { // function pointer
|
|
// print("function pointer: $line\n");
|
|
$field =$this->ParseFunctionDeclaration($captures[1], $captures[2], $captures[3], $captures[4], false, "");
|
|
} else {
|
|
if (!$struct["valid"])
|
|
$field = $this->ParseNormalField($line,$protected_keywords,$field_bitpacked,$bitpacked_real_type,$this_class_field_names);
|
|
else
|
|
// don't register the names of fields of embedded structs and field names of the current
|
|
// class, but do track them for the current struct as there may be conflicts due to
|
|
// Pascal's case-insensitivity
|
|
$field = $this->ParseNormalField($line,$protected_keywords,$field_bitpacked,$bitpacked_real_type,$struct["fieldnames"]);
|
|
}
|
|
|
|
// mark the field as having a garbage collector field
|
|
if ($garbage_collector_hint) $field = "$field {garbage collector: $garbage_collector_hint }";
|
|
|
|
// return field
|
|
if ($struct["valid"]) {
|
|
if (!$struct["bitpacked"]) $struct["bitpacked_first_type"] = $bitpacked_real_type;
|
|
if ($field_bitpacked) $struct["bitpacked"] = true;
|
|
$struct["fields"][] = $field;
|
|
} else {
|
|
return $field;
|
|
}
|
|
}
|
|
|
|
// Parses a struct field into a list
|
|
function ParseStructList ($line, $input, $name, $type) {
|
|
$field = "";
|
|
|
|
$list = explode(",", $input);
|
|
if (count($list) > 1) {
|
|
$field = " ";
|
|
foreach ($list as $key) {
|
|
$key = trim($key, " ");
|
|
$field .= "$key, ";
|
|
}
|
|
|
|
$field = rtrim($field, ", ");
|
|
$field .= ": $type;\n";
|
|
} else {
|
|
$field = " $name: $type;";
|
|
$field = $this->MakeFieldInlineArray($field, $line, $name, $type)."\n";
|
|
}
|
|
|
|
return $field;
|
|
}
|
|
|
|
// Parses $line into a function declaration string. Handles both
|
|
// function pointers (isexternfunc = false) and external functions
|
|
// (isexternfunc = true)
|
|
function ParseFunctionDeclaration($rettypestr, $retpointertypestr, $funcname, $parastr, $isexternfunc, $deprecatedmods) {
|
|
|
|
if ($deprecatedmods != "") $deprecatedmods .= ";";
|
|
if ($this->IsKeywordReserved($funcname)) $funcname .= "_";
|
|
|
|
$rettype = trim(str_replace_word("const","",$rettypestr));
|
|
$rettype = $this->ReplaceObjcType($rettype);
|
|
$rettype = $this->EncodePointerModifiers($rettype,$retpointertypestr);
|
|
$params = $this->ConvertCParamsPascal($parastr);
|
|
|
|
if ($rettype == "void")
|
|
$result = "procedure ";
|
|
else
|
|
$result = "function ";
|
|
|
|
// if no name specified, the caller will add it
|
|
if ($funcname != "") {
|
|
if (!$isexternfunc)
|
|
$result = "$funcname: " . $result;
|
|
else
|
|
$result .= $funcname;
|
|
}
|
|
|
|
if ($params != "") $params = "(" . $params . ")";
|
|
if ($rettype == "void")
|
|
$result .= $params . "; cdecl;";
|
|
else
|
|
$result .= $params . ": " . $rettype . "; cdecl;";
|
|
|
|
if ($isexternfunc)
|
|
$result .= " external;$deprecatedmods";
|
|
else
|
|
$result .= "\n";
|
|
|
|
return $result;
|
|
}
|
|
|
|
// Parses $line into the combined $struct_fields string
|
|
function ParseStructFields ($line, $protected_keywords, &$struct_fields, &$any_field_parsed, &$found_any_bitpacked, &$all_bitpacked, &$first_bitpacked_type) {
|
|
if (preg_match($this->pregex_function_pointer, $line, $captures)) {
|
|
$struct_fields .= " " . $this->ParseFunctionDeclaration($captures[1], $captures[2], $captures[3], $captures[4], false, "");
|
|
$all_bitpacked = false;
|
|
} else {
|
|
// better: keep for entire struct, so we can escape conflicting names due to
|
|
// case-insensitivity
|
|
$tempnewprotected = array();
|
|
$new_field = $this->ParseNormalField($line,$protected_keywords,$field_bitpacked,$bitpacked_real_type,$tempnewprotected);
|
|
// print("field: '$new_field', bitpacked: $field_bitpacked, any: $found_any_bitpacked, all: $all_bitpacked\n");
|
|
if ($new_field != "") {
|
|
$found_any_bitpacked |= $field_bitpacked;
|
|
if (!$any_field_parsed) {
|
|
$all_bitpacked = $field_bitpacked;
|
|
$first_bitpacked_type = $bitpacked_real_type;
|
|
$any_field_parsed=true;
|
|
}
|
|
else $all_bitpacked &= $field_bitpacked;
|
|
$struct_fields .= " " . $new_field . $this->AppendEOLComment() . "\n";
|
|
}
|
|
// print(" after: any: $found_any_bitpacked, all: $all_bitpacked\n");
|
|
}
|
|
}
|
|
|
|
// Parse a single enum field
|
|
function ParseEnumFields ($line, $file_name, &$block_count, &$auto_increment) {
|
|
|
|
// insert macros
|
|
//if ($macro = $this->InsertMacroBlocks($line, $this->inside_macro_block)) $this->dump[$file_name]["types"]["enums"][$block_count][] = $macro;
|
|
|
|
if (ereg("^[[:space:]]*[,]*[[:space:]]*([a-zA-Z0-9_]+)[[:space:]]*=[[:space:]]*[(]*([a-zA-Z_]+)[)]*[,]*[[:space:]]*$", $line, $captures)) { // string value
|
|
$captures[2] = trim($captures[2], ", ");
|
|
$this->dump[$file_name]["types"]["enums"][$block_count][] = $captures[1]." = ".$captures[2].";".$this->AppendEOLComment();
|
|
} elseif (ereg("^[[:space:]]*[,]*[[:space:]]*([a-zA-Z0-9_]+)[[:space:]]*=[[:space:]]*[(]*([0-9-]+)[)]*[,]*[[:space:]]*$", $line, $captures)) { // integer value
|
|
$captures[2] = trim($captures[2], ", ");
|
|
$this->dump[$file_name]["types"]["enums"][$block_count][] = $captures[1]." = ".$captures[2].";".$this->AppendEOLComment();
|
|
$auto_increment = $captures[2] + 1;
|
|
} elseif (ereg("^[[:space:]]*[,]*[[:space:]]*([a-zA-Z0-9_]+)[[:space:]]*=[[:space:]]*[(]*([0-9]+[xX]+[a-fA-F0-9]+)[)]*", $line, $captures)) { // hexadecimal value
|
|
$captures[2] = trim($captures[2], ", ");
|
|
$auto_increment = $captures[2] + 1;
|
|
$captures[2] = eregi_replace("^0x", "$", $captures[2]);
|
|
$this->dump[$file_name]["types"]["enums"][$block_count][] = $captures[1]." = ".$captures[2].";".$this->AppendEOLComment();
|
|
} elseif (ereg("^[[:space:]]*[,]*[[:space:]]*([a-zA-Z0-9_]+)[[:space:]]*=[[:space:]]*([a-zA-Z0-9]+[[:space:]]*<<[[:space:]]*[a-zA-Z0-9]+)", $line, $captures)) { // << shl value, no ()
|
|
$captures[2] = ereg_replace("[[:space:]]?<<[[:space:]]?", " shl ", $captures[2]);
|
|
|
|
// remove integer type hints
|
|
$captures[2] = ereg_replace("([0-9]+)[UL]+([[:space:]]+)shl([[:space:]])", "\\1\\2shl\\3", $captures[2]);
|
|
$captures[2] = ereg_replace("([[:space:]])shl([[:space:]]+)([0-9]+)[UL]+", "\\1shl\\2\\3", $captures[2]);
|
|
|
|
$this->dump[$file_name]["types"]["enums"][$block_count][] = $captures[1]." = ".$captures[2].";".$this->AppendEOLComment();
|
|
$operands = preg_split("/\s*shl\s*/", $captures[2]);
|
|
$auto_increment = ($operands[0] << $operands[1]) + 1;
|
|
|
|
} elseif (ereg("^[[:space:]]*[,]*[[:space:]]*([a-zA-Z0-9_]+)[[:space:]]*=[[:space:]]*\(([a-zA-Z0-9]+[[:space:]]*<<[[:space:]]*[a-zA-Z0-9]+)\)", $line, $captures)) { // << shl value
|
|
$captures[2] = trim($captures[2], ", ");
|
|
$captures[2] = ereg_replace("[[:space:]]?<<[[:space:]]?", " shl ", $captures[2]);
|
|
|
|
// remove integer type hints
|
|
$captures[2] = ereg_replace("([0-9]+)[UL]+([[:space:]]+)shl([[:space:]])", "\\1\\2shl\\3", $captures[2]);
|
|
$captures[2] = ereg_replace("([[:space:]])shl([[:space:]]+)([0-9]+)[UL]+", "\\1shl\\2\\3", $captures[2]);
|
|
|
|
$this->dump[$file_name]["types"]["enums"][$block_count][] = $captures[1]." = ".$captures[2].";".$this->AppendEOLComment();
|
|
|
|
$operands = preg_split("/\s*shl\s*/", $captures[2]);
|
|
$auto_increment = ($operands[0] << $operands[1]) + 1;
|
|
} elseif (ereg("^[[:space:]]*[,]*[[:space:]]*([a-zA-Z0-9_]+)[[:space:]]*[,}]*[[:space:]]*$", $line, $captures)) { // non-value
|
|
|
|
// omit lines which started nested structures.
|
|
// bad practice but the single-line regex parser can't handle them
|
|
if (!eregi("[=|]+", $line)) {
|
|
$captures[1] = trim($captures[1], ", ");
|
|
$this->dump[$file_name]["types"]["enums"][$block_count][] = $captures[1]." = ".$auto_increment.";";
|
|
$auto_increment ++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Parse external symbols, enums and typedef's from the header
|
|
function ParseHeaderTypes ($file) {
|
|
$contents = file_get_contents($file);
|
|
$file_name = substr($file, (strripos($file, "/")) + 1, strlen($file));
|
|
$any_field_parsed = false;
|
|
$any_field_bitpacked = false;
|
|
$all_fields_bitpacked = false;
|
|
|
|
// reset comments from previous parsing sections
|
|
$this->ResetComment();
|
|
|
|
$lines = explode("\n", $contents);
|
|
foreach ($lines as $line) {
|
|
|
|
// skip blocks
|
|
if ($this->SkipBlock($line)) continue;
|
|
|
|
// ignore lines
|
|
if (in_array($line, $this->ignore_lines)) continue;
|
|
|
|
// build comments
|
|
$this->BuildComment($line, $file_name);
|
|
|
|
// build macro blocks
|
|
$this->BuildMacroBlocks($line);
|
|
|
|
// garbage collector hints
|
|
$line = $this->ReplaceGarbageCollectorHints($line, $garbage_collector_hint);
|
|
|
|
// remove macros
|
|
$line = $this->RemoveVersionMacros($line, $deprecatedmods);
|
|
|
|
// remove comments
|
|
$line = $this->RemoveComments($line);
|
|
$line = trim($line, " ");
|
|
|
|
if ($got_struct) {
|
|
|
|
// insert macros
|
|
if ($macro = $this->InsertMacroBlocks($line, $this->inside_macro_block)) $struct_fields .= "$macro\n";
|
|
|
|
// collect fields
|
|
$this->ParseStructFields($line, array(), $struct_fields, $any_field_parsed, $any_field_bitpacked, $all_fields_bitpacked, $first_bitpacked_type);
|
|
|
|
// got end of struct
|
|
if (ereg("^}[[:space:]]*([a-zA-Z_0-9]+);", $line, $captures)) {
|
|
|
|
if ($struct_name == "") {
|
|
$struct_name = $captures[1];
|
|
} else {
|
|
$struct_type = $captures[1];
|
|
}
|
|
|
|
// ignore this struct
|
|
if (in_array($struct_name, $this->ignore_types)) continue;
|
|
|
|
$struct = "$struct_comment$struct_name = $this->record_keyword\n";
|
|
if ($any_field_bitpacked) {
|
|
$struct .= $this->BitPackedForceAlignment($first_bitpacked_type, " ", " ") . "\n";
|
|
$struct_fields = str_replace(" "," ",$struct_fields);
|
|
$struct_fields .= " end;\n );\n";
|
|
}
|
|
|
|
$struct .= $struct_fields;
|
|
$struct .= " end$deprecatedmods;\n";
|
|
if (($struct_type) && ($struct_name != $struct_type)) {
|
|
$struct .= "$struct_type = $struct_name;\n";
|
|
}
|
|
// pointer type
|
|
$struct .= $struct_name."Ptr = ^".$struct_name.";\n";
|
|
|
|
$this->dump[$file_name]["types"]["structs"][] = $struct;
|
|
$this->dump["global_structs"][] = $struct_name;
|
|
$got_struct = false;
|
|
$any_field_parsed = false;
|
|
$any_field_bitpacked = false;
|
|
$all_fields_bitpacked = false;
|
|
}
|
|
}
|
|
|
|
// got single-line struct
|
|
if (ereg("^typedef[[:space:]]+struct[[:space:]]+{(.*)}[[:space:]]+([a-zA-Z0-9_]+)", $line, $captures)) {
|
|
$struct_name = trim($captures[2], " ");
|
|
if (!in_array($struct_name, $this->ignore_types)) {
|
|
|
|
|
|
// break the struct into lines
|
|
$single_struct_fields = "";
|
|
$fields = explode(";", $captures[1]);
|
|
$comment = $this->InsertCurrentComment();
|
|
$this->ResetComment();
|
|
|
|
// parse each line
|
|
foreach ($fields as $field) {
|
|
$field = trim($field);
|
|
$this->ParseStructFields($field.";", array(), $single_struct_fields, $any_field_parsed, $any_field_bitpacked, $all_fields_bitpacked, $first_bitpacked_type);
|
|
}
|
|
|
|
// merge the fields into the definition
|
|
$struct = "$comment\n"."$struct_name = ";
|
|
$struct .= "$this->record_keyword\n";
|
|
if ($any_field_bitpacked) {
|
|
$struct .= $this->BitPackedForceAlignment($first_bitpacked_type, " ", " ") . "\n";
|
|
$single_struct_fields = str_replace(" "," ",$single_struct_fields);
|
|
$single_struct_fields .= " end;\n );\n";
|
|
} else ;
|
|
$struct .= $single_struct_fields;
|
|
$struct .= " end$deprecatedmods;\n";
|
|
// pointer type
|
|
$struct .= $struct_name."Ptr = ^".$struct_name.";\n";
|
|
|
|
$this->dump[$file_name]["types"]["structs"][] = $struct;
|
|
$this->dump["global_structs"][] = $struct_name;
|
|
$any_field_parsed = false;
|
|
$any_field_bitpacked = false;
|
|
$all_fields_bitpacked = false;
|
|
//print("$single_struct_fields\n");
|
|
}
|
|
// got begin of struct
|
|
} elseif (ereg("^typedef struct(.*){", $line, $captures)) {
|
|
$struct_name = trim($captures[1], " ");
|
|
if (!in_array($struct_name, $this->ignore_types)) {
|
|
$struct_type = null;
|
|
$struct_fields = "";
|
|
$struct_comment = $this->InsertCurrentComment();
|
|
$this->ResetComment();
|
|
if ($struct_comment != "") $struct_comment = "$struct_comment\n";
|
|
$got_struct = true;
|
|
print("Parsing struct $struct_name\n");
|
|
}
|
|
}
|
|
|
|
// got function pointer type
|
|
if (preg_match($this->pregex_function_pointer_typedef, $line, $captures)) {
|
|
|
|
$typestr = $this->ParseFunctionDeclaration($captures[1], $captures[2], "", $captures[6], false, $deprecatedmods);
|
|
$functypename = $captures[7];
|
|
if ($functypename == "") $functypename = $captures[5];
|
|
if ($functypename == "") $functypename = $captures[4];
|
|
$this->dump[$file_name]["types"]["callbacks"][$functypename] = $typestr;
|
|
// record if it is a function type instead of a function pointer type
|
|
if ($captures[3] == "") $this->implicit_function_pointer_types[] = $functypename;
|
|
|
|
continue;
|
|
}
|
|
|
|
// #defines
|
|
$got_define = false;
|
|
if (ereg("#[[:space:]]*define[[:space:]]+([a-zA-Z0-9_]+)[[:space:]]+\(\(.*\)(.*)\)", $line, $captures)) { // named constant with type case
|
|
$got_define = true;
|
|
} elseif (ereg("#[[:space:]]*define[[:space:]]+([a-zA-Z0-9_]+)[[:space:]]+[(]*([0-9.-]+)[)]*", $line, $captures)) { //integer
|
|
$got_define = true;
|
|
}
|
|
|
|
if ($got_define) {
|
|
$define_name = $captures[1];
|
|
if (!in_array($define_name, $this->ignore_types)) {
|
|
$define_name = $this->ReplaceObjcType($define_name);
|
|
//if ($this->comment_terminated) $this->dump[$file_name]["types"]["defines"][] = $this->InsertCurrentComment();
|
|
$this->AppendCurrentComment($this->dump[$file_name]["types"]["defines"]);
|
|
$this->dump[$file_name]["types"]["defines"][] = $define_name." = ".$captures[2].";".$this->AppendEOLComment();
|
|
} else {
|
|
$this->ResetComment();
|
|
}
|
|
}
|
|
|
|
// parse enum fields
|
|
if (($got_enum) || ($got_named_enum)) {
|
|
// print($line.", auto_inc = $auto_increment\n");
|
|
|
|
$this->ParseEnumFields($line, $file_name, &$block_count, &$auto_increment);
|
|
|
|
// found the end
|
|
if (ereg("^};", $line)) $got_enum = false;
|
|
}
|
|
|
|
// ==== got inline named enum ===
|
|
if (ereg("^[[:space:]]*enum[[:space:]]+([a-zA-Z0-9_]+)[[:space:]]*{(.*)};", $line, $captures)) {
|
|
//print("$line\n");
|
|
|
|
$enum_name = trim($captures[1], " ");
|
|
if (!in_array($enum_name, $this->ignore_types)) {
|
|
|
|
$block_count ++;
|
|
$auto_increment = 0;
|
|
|
|
// break the enum into lines
|
|
$fields = explode(",", $captures[2]);
|
|
//$this->AppendCurrentMacro($this->dump[$file_name]["types"]["enums"][$block_count]);
|
|
$this->AppendCurrentComment($this->dump[$file_name]["types"]["enums"][$block_count]);
|
|
if ($this->comment_terminated) $this->dump[$file_name]["types"]["enums"][$block_count][] = $this->InsertCurrentComment();
|
|
$this->ResetComment();
|
|
|
|
// parse each line
|
|
foreach ($fields as $field) {
|
|
$field = trim($field, " ");
|
|
$this->ParseEnumFields($field.",", $file_name, &$block_count, &$auto_increment);
|
|
}
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
// ==== got inline enum ===
|
|
if (ereg("^[[:space:]]*enum[[:space:]]*{(.*)};", $line, $captures)) {
|
|
//print("$line\n");
|
|
|
|
$block_count ++;
|
|
$auto_increment = 0;
|
|
|
|
// break the enum into lines
|
|
$fields = explode(",", $captures[1]);
|
|
$this->AppendCurrentComment($this->dump[$file_name]["types"]["enums"][$block_count]);
|
|
//if ($this->comment_terminated) $this->dump[$file_name]["types"]["enums"][$block_count][] = $this->InsertCurrentComment();
|
|
$this->ResetComment();
|
|
|
|
// parse each line
|
|
foreach ($fields as $field) {
|
|
$field = trim($field, " ");
|
|
$this->ParseEnumFields($field.",", $file_name, &$block_count, &$auto_increment);
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
// ==== got enum ===
|
|
if (ereg("^enum", $line)) {
|
|
$got_enum = true;
|
|
$block_count ++;
|
|
$auto_increment = 0;
|
|
$this->AppendCurrentComment($this->dump[$file_name]["types"]["enums"][$block_count]);
|
|
//if ($this->comment_terminated) $this->dump[$file_name]["types"]["enums"][$block_count][] = $this->InsertCurrentComment();
|
|
}
|
|
|
|
// terminate named enum
|
|
if ($got_named_enum) {
|
|
if (ereg("^}[[:space:]]*([a-zA-Z0-9_]+);", $line, $captures)) {
|
|
$got_named_enum = false;
|
|
|
|
$named_enum = trim($named_enum, ", \n");
|
|
$name = $captures[1];
|
|
|
|
if (!in_array($name, $this->ignore_types)) {
|
|
$this->dump[$file_name]["types"]["named_enums"][] = "$name = culong;";
|
|
$this->dump["global_types"][$name] = $name;
|
|
}
|
|
}
|
|
}
|
|
|
|
// ==== got named enum ===
|
|
if (ereg("^typedef enum {", $line)) {
|
|
$got_named_enum = true;
|
|
$named_enum = "";
|
|
$auto_increment = 0;
|
|
$block_count ++;
|
|
$this->AppendCurrentComment($this->dump[$file_name]["types"]["named_enums"][$block_count]);
|
|
//if ($this->comment_terminated) $this->dump[$file_name]["types"]["named_enums"][] = $this->InsertCurrentComment();
|
|
}
|
|
|
|
// ==== external functions ===
|
|
// doesn't work when $this->external_string_macros is added to
|
|
// the string at initialisation time, because it can still change
|
|
// later (while loading frameworks.xml)
|
|
if (preg_match("!^(?:$this->external_string_macros)+".$this->pregex_external_function_end, $line, $captures)) {
|
|
|
|
// ignore symbols
|
|
if (in_array($captures[3], $this->ignore_types)) continue;
|
|
|
|
$typestr = $this->ParseFunctionDeclaration($captures[1], $captures[2], $captures[3], $captures[4], true, $deprecatedmods);
|
|
|
|
$this->dump[$file_name]["types"]["functions"][] = $typestr;
|
|
continue;
|
|
}
|
|
|
|
// ==== external string constant ===
|
|
if (eregi("^($this->external_string_macros)+[[:space:]]+NSString[[:space:]]*\*[[:space:]]*(const)*[[:space:]]*([a-zA-Z0-9_]+)", $line, $captures)) {
|
|
$name = $captures[3];
|
|
|
|
if (in_array($name, $this->ignore_types)) continue;
|
|
|
|
// insert comments
|
|
$this->AppendCurrentComment($this->dump[$file_name]["types"]["string_constant"]);
|
|
//if ($this->comment_terminated) $this->dump[$file_name]["types"]["string_constant"][] = $this->InsertCurrentComment();
|
|
|
|
$this->dump[$file_name]["types"]["string_constant"][] = "$name: $this->string_macro$deprecatedmods; cvar; external;";
|
|
}
|
|
|
|
// ==== external symbol ===
|
|
if (eregi("^($this->external_string_macros)+[[:space:]]+([a-zA-Z0-9_ ]+)[[:space:]]+([a-zA-Z0-9_]+)", $line, $captures)) {
|
|
$name = $captures[3];
|
|
$type = $captures[2];
|
|
|
|
// ignore symbols
|
|
if (in_array($name, $this->ignore_types)) continue;
|
|
|
|
$type = istr_replace_word("const", "", $type);
|
|
$type = $this->ReplaceObjcType(trim($type, " "));
|
|
|
|
|
|
$this->AppendCurrentComment($this->dump[$file_name]["types"]["external_symbol"]);
|
|
//if ($this->comment_terminated) $this->dump[$file_name]["types"]["external_symbol"][] = $this->InsertCurrentComment();
|
|
$this->dump[$file_name]["types"]["external_symbol"][] = "$name: $type$deprecatedmods; cvar; external;";
|
|
}
|
|
|
|
// ==== got typedef ===
|
|
if (ereg("^typedef[[:space:]]+struct[[:space:]]+([a-zA-Z0-9_]+)[[:space:]]+([a-zA-Z0-9_]+);", $line, $captures)) { // defined struct type
|
|
$real_type = $captures[1];
|
|
$struct_type = $captures[1];
|
|
$new_type = $captures[2];
|
|
|
|
// ignore types
|
|
if (in_array($struct_type, $this->ignore_types)) continue;
|
|
if (in_array($new_type, $this->ignore_types)) continue;
|
|
|
|
$this->AddTypeDef($this->dump[$file_name], "$struct_type = record end$deprecatedmods;");
|
|
|
|
$struct_type = $this->ReplaceObjcType($struct_type);
|
|
if ($new_type != $struct_type) {
|
|
$this->AddTypeDef($this->dump[$file_name], "$new_type = $struct_type$deprecatedmods;");
|
|
$this->dump["global_types"][$new_type] = $real_type;
|
|
}
|
|
$this->opaque_structs[] = $struct_type;
|
|
|
|
// also add pointer type to the opaque struct
|
|
$this->AddTypeDef($this->dump[$file_name], $new_type."Ptr = ^$new_type$deprecatedmods;");
|
|
|
|
$this->dump["global_types"][$struct_type] = "record end";
|
|
$this->dump["global_types"][$new_type."Ptr"] = "^".$new_type;
|
|
} elseif (ereg("^typedef[[:space:]]+struct[[:space:]]+([a-zA-Z0-9_]+)[[:space:]]+([a-zA-Z0-9_*]+);", $line, $captures)) { // pointer to struct
|
|
$real_type = $captures[1];
|
|
$clean_name = trim($captures[2], "*");
|
|
$pointer_type = $captures[1];
|
|
|
|
// ignore types
|
|
if (in_array($clean_name, $this->ignore_types)) continue;
|
|
|
|
$pointer_type = "Pointer";
|
|
$this->AddTypeDef($this->dump[$file_name], "$clean_name = $pointer_type$deprecatedmods;");
|
|
|
|
$this->dump["global_types"][$clean_name] = $real_type;
|
|
|
|
// also add pointer type
|
|
$this->AddTypeDef($this->dump[$file_name], $clean_name."Ptr = ^$clean_name$deprecatedmods;");
|
|
$this->dump["global_types"][$clean_name."Ptr"] = "^".$clean_name;
|
|
|
|
} elseif (ereg("^typedef[[:space:]]+(const)*[[:space:]]*struct[[:space:]]+([a-zA-Z0-9_*]+)[[:space:]]+([a-zA-Z0-9_]+);", $line, $captures)) { // struct type (complex)
|
|
$real_type = $captures[1];
|
|
$typedef_name = $captures[3];
|
|
|
|
// ignore types
|
|
if (in_array($typedef_name, $this->ignore_types)) continue;
|
|
|
|
$captures[2] = $this->FormatObjcType($captures[2], $modifiers);
|
|
$this->AddTypeDef($this->dump[$file_name], $typedef_name." = ".$captures[2].$deprecatedmods.";");
|
|
|
|
$this->dump["global_types"][$typedef_name] = $real_type;
|
|
|
|
// also add pointer type
|
|
$this->AddTypeDef($this->dump[$file_name], $typedef_name."Ptr = ^$typedef_name$deprecatedmods;");
|
|
$this->dump["global_types"][$typedef_name."Ptr"] = "^".$typedef_name;
|
|
} elseif (ereg("^typedef[[:space:]]+([a-zA-Z0-9_]+)[[:space:]]+([a-zA-Z0-9_*]+);", $line, $captures)) { // single-word type
|
|
$real_type = $captures[1];
|
|
|
|
// type is a pointer
|
|
if ($captures[2][0] == "*") {
|
|
$captures[2] = trim($captures[2], "*");
|
|
$captures[1] = $this->ReplaceObjcType($captures[1]);
|
|
|
|
// ignore types
|
|
if (in_array($captures[2], $this->ignore_types)) continue;
|
|
|
|
$this->AddTypeDef($this->dump[$file_name], $captures[2]." = ^".$captures[1]."$deprecatedmods;");
|
|
|
|
$this->dump["global_types"][$captures[2]] = $real_type;
|
|
|
|
} else {
|
|
$captures[2] = trim($captures[2], "*");
|
|
$captures[1] = $this->ReplaceObjcType($captures[1]);
|
|
|
|
// ignore types
|
|
if (in_array($captures[2], $this->ignore_types)) continue;
|
|
|
|
$this->AddTypeDef($this->dump[$file_name],$captures[2]." = ".$captures[1]."$deprecatedmods;");
|
|
}
|
|
// also add pointer type
|
|
$this->AddTypeDef($this->dump[$file_name], $captures[2]."Ptr = ^$captures[2]$deprecatedmods;");
|
|
$this->dump["global_types"][$captures[2]."Ptr"] = "^".$captures[2];
|
|
} elseif (ereg("^typedef[[:space:]]+([a-zA-Z0-9_]+)[[:space:]]+([a-zA-Z0-9_]+)[[:space:]]+([a-zA-Z0-9_*]+);", $line, $captures)) { // double-word type
|
|
$real_type = $captures[1];
|
|
|
|
$typedef_name = trim($captures[3], "*");
|
|
$long_type = $captures[1]." ".$captures[2];
|
|
$long_type = $this->ReplaceObjcType($long_type);
|
|
|
|
// ignore types
|
|
if (in_array($captures[2], $this->ignore_types)) continue;
|
|
|
|
$this->AddTypeDef($this->dump[$file_name], $typedef_name." = $long_type$deprecatedmods;");
|
|
|
|
$this->dump["global_types"][$typedef_name] = $real_type;
|
|
|
|
// also add pointer type
|
|
$this->AddTypeDef($this->dump[$file_name], $typedef_name."Ptr = ^$typedef_name$deprecatedmods;");
|
|
$this->dump["global_types"][$typedef_name."Ptr"] = "^".$typedef_name;
|
|
}
|
|
}
|
|
|
|
//print_r($this->dump[$file_name]["types"]);
|
|
}
|
|
|
|
// Parse all protocols in a header
|
|
function ParseHeaderProtocols ($file) {
|
|
$contents = file_get_contents($file);
|
|
$file_name = substr($file, (strripos($file, "/")) + 1, strlen($file));
|
|
$section = null;
|
|
|
|
// reset comments from previous parsing sections
|
|
$this->ResetComment();
|
|
|
|
$lines = explode("\n", $contents);
|
|
foreach ($lines as $line) {
|
|
|
|
// skip blocks
|
|
if ($this->SkipBlock($line)) continue;
|
|
|
|
// ignore lines
|
|
if (in_array($line, $this->ignore_lines)) continue;
|
|
|
|
// remove macros
|
|
$line = $this->RemoveVersionMacros($line, $deprecatedmods);
|
|
|
|
// build comments
|
|
if (!$got_got_protocol) $this->BuildComment($line, $file_name);
|
|
|
|
// parse protocol
|
|
if ($got_protocol) {
|
|
|
|
// build comments
|
|
$this->BuildComment($line, $file_name);
|
|
|
|
// remove comments
|
|
$line = $this->RemoveComments($line);
|
|
|
|
// found @optional/@required section
|
|
if (eregi("^[[:space:]]*@(optional|required)+", $line, $captures)) {
|
|
$section = $captures[1];
|
|
}
|
|
|
|
// found property
|
|
if ($this->LineHasProperty($line, $captures)) {
|
|
$properties = $this->ParseClassProperty($current_protocol, $captures, $deprecatedmods);
|
|
|
|
foreach ($properties as $property) {
|
|
|
|
if ($property["setter"]) {
|
|
$property["setter"]["comment"] = $this->InsertCurrentComment();
|
|
$property["setter"]["section"] = $section;
|
|
$property["setter"]["documentation"] = $this->FindDocumentationForMethod($current_protocol, $property["setter"]["property"]);
|
|
|
|
$this->current_header["protocols"][$current_protocol]["methods"][$property["setter"]["objc_method"]] = $property["setter"];
|
|
|
|
// append to master list of protocols
|
|
$this->dump["protocols"][$current_protocol][] = $property["setter"];
|
|
}
|
|
|
|
if ($property["getter"]) {
|
|
$property["getter"]["comment"] = $this->InsertCurrentComment();
|
|
$property["getter"]["section"] = $section;
|
|
$property["getter"]["documentation"] = $this->FindDocumentationForMethod($current_protocol, $property["getter"]["property"]);
|
|
|
|
$this->current_header["protocols"][$current_protocol]["methods"][$property["getter"]["objc_method"]] = $property["getter"];
|
|
|
|
// append to master list of protocols
|
|
$this->dump["protocols"][$current_protocol][] = $property["getter"];
|
|
}
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
// found method
|
|
$method = null;
|
|
if (preg_match($this->pregex_objc_method_params, $line, $captures)) {
|
|
$method = $this->ConvertObjcMethodToPascal($current_protocol, $line, $captures, array(), true, $deprecatedmods);
|
|
} elseif (preg_match($this->pregex_objc_method_no_params, $line, $captures)) {
|
|
$method = $this->ConvertObjcMethodToPascal($current_protocol, $line, $captures, array(), false, $deprecatedmods);
|
|
} elseif (preg_match($this->pregex_objc_method_partial, $line, $captures)) {
|
|
$method_fragment = $line;
|
|
}
|
|
|
|
// append to classes
|
|
if (($method) && (!in_array($method["name"], $this->ignore_methods)) ) {
|
|
|
|
// add comment to the method
|
|
$method["comment"] = $this->InsertCurrentComment();
|
|
|
|
// add optional/required section to the method
|
|
$method["section"] = $section;
|
|
|
|
// add documentation for method
|
|
$method["documentation"] = $this->FindDocumentationForMethod($current_protocol, $method["objc_method"]);
|
|
|
|
$this->current_header["protocols"][$current_protocol]["methods"][$method["objc_method"]] = $method;
|
|
|
|
// append to master list of protocols
|
|
$this->dump["protocols"][$current_protocol][] = $method;
|
|
}
|
|
|
|
// found the end
|
|
if (ereg("^@end", $line)) {
|
|
$this->ResetComment();
|
|
$got_protocol = false;
|
|
}
|
|
}
|
|
|
|
// got protocol
|
|
if ((eregi($this->regex_objc_protocol, $line, $captures)) && (!eregi(".*;$", $line))) {
|
|
$got_protocol = true;
|
|
$current_protocol = $captures[1];
|
|
|
|
print("+ Protocol $current_protocol\n");
|
|
|
|
if ($this->comment_terminated) $this->current_header["protocols"][$current_protocol]["comment"] = $this->InsertCurrentComment();
|
|
$this->current_header["protocols"][$current_protocol]["name"] = $captures[1];
|
|
}
|
|
}
|
|
|
|
//print_r($this->current_class);
|
|
}
|
|
|
|
// Parse all categories in a header
|
|
function ParseHeaderCategories ($file) {
|
|
$contents = file_get_contents($file);
|
|
$file_name = substr($file, (strripos($file, "/")) + 1, strlen($file));
|
|
|
|
// reset comments from previous parsing sections
|
|
$this->ResetComment();
|
|
|
|
$lines = explode("\n", $contents);
|
|
foreach ($lines as $line) {
|
|
|
|
// skip blocks
|
|
if ($this->SkipBlock($line)) continue;
|
|
|
|
// ignore lines
|
|
if (in_array($line, $this->ignore_lines)) continue;
|
|
|
|
// remove macros
|
|
$line = $this->RemoveVersionMacros($line, $deprecatedmods);
|
|
|
|
// build comments
|
|
if (!$got_category) $this->BuildComment($line, $file_name);
|
|
|
|
// parse category
|
|
if ($got_category) {
|
|
|
|
// build comments
|
|
$this->BuildComment($line, $file_name);
|
|
|
|
// remove comments
|
|
$line = $this->RemoveComments($line);
|
|
|
|
// found property
|
|
if ($this->LineHasProperty($line, $captures)) {
|
|
$properties = $this->ParseClassProperty($current_category, $captures, $deprecatedmods);
|
|
|
|
if (!in_array($current_category, $this->ignore_categories)) {
|
|
foreach ($properties as $property) {
|
|
if ($property["setter"]) {
|
|
if ($this->AddMethodToClass($property["setter"], $this->current_class)) {
|
|
$this->dump[$category_owner]["classes"][$current_class]["categories"][$current_category]["methods"][] = $property["setter"];
|
|
$this->dump[$category_owner]["categories"][$current_category]["methods"][] = $property["setter"];
|
|
}
|
|
}
|
|
|
|
if ($property["getter"]) {
|
|
if ($this->AddMethodToClass($property["getter"], $this->current_class)) {
|
|
$this->dump[$category_owner]["classes"][$current_class]["categories"][$current_category]["methods"][] = $property["getter"];
|
|
$this->dump[$category_owner]["categories"][$current_category]["methods"][] = $property["getter"];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// build method fragment
|
|
if ($method_fragment) $method_fragment .= " ".trim($line);
|
|
|
|
// found method fragment termination
|
|
if (($method_fragment) && (preg_match($this->pregex_objc_method_terminate, $line))) {
|
|
$line = $method_fragment;
|
|
$method_fragment = null;
|
|
}
|
|
|
|
// found method
|
|
$method = null;
|
|
if (preg_match($this->pregex_objc_method_params, $line, $captures)) {
|
|
$method = $this->ConvertObjcMethodToPascal($current_category, $line, $captures, $this->GetProtectedKeywords($this->current_class), true, $deprecatedmods);
|
|
} elseif (preg_match($this->pregex_objc_method_no_params, $line, $captures)) {
|
|
$method = $this->ConvertObjcMethodToPascal($current_category, $line, $captures, $this->GetProtectedKeywords($this->current_class), false, $deprecatedmods);
|
|
} elseif (preg_match($this->pregex_objc_method_partial, $line, $captures)) {
|
|
$method_fragment = $line;
|
|
}
|
|
|
|
// append to classes
|
|
if (($method) && !in_array($current_category, $this->ignore_categories)) {
|
|
if ($current_class) {
|
|
if ($this->AddMethodToClass($method, $this->current_class)) {
|
|
$this->dump[$category_owner]["classes"][$current_class]["categories"][$current_category]["methods"][] = $method;
|
|
$this->dump[$category_owner]["categories"][$current_category]["methods"][] = $method;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// found the end
|
|
if (ereg("^@end", $line)) {
|
|
$got_category = false;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// got category
|
|
if (eregi($this->regex_objc_category, $line, $captures)) {
|
|
|
|
$got_category = true;
|
|
$category_owner = $file_name;
|
|
$category_name = $captures[2];
|
|
$current_class = $captures[1];
|
|
$current_category = $category_name;
|
|
|
|
if (!in_array($current_category, $this->ignore_categories)) {
|
|
// Protect category names against duplicate identifiers by appending the class it extends to the name
|
|
if ((count($this->dump["categories"][$current_category]) > 0) || (in_array($category_name, $this->cocoa_classes))) {
|
|
$current_category = $category_name."_".$current_class;
|
|
$this->dump[$file_name]["categories"][$current_category]["external"] = true;
|
|
} else {
|
|
$this->dump[$file_name]["categories"][$current_category]["external"] = false;
|
|
}
|
|
|
|
$this->current_class = &$this->dump[$category_owner]["classes"][$current_class];
|
|
|
|
// insert into headers category array
|
|
$this->dump[$file_name]["categories"][$current_category]["name"] = $current_category;
|
|
$this->dump[$file_name]["categories"][$current_category]["super"] = $current_class;
|
|
$this->dump[$file_name]["categories"][$current_category]["comment"] = $this->InsertCurrentComment();
|
|
$this->dump[$file_name]["categories"][$current_category]["external_name"] = $category_name;
|
|
|
|
// append to master list of categories
|
|
$this->dump["categories"][$category_name][] = $current_class;
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
//print_r($this->current_class);
|
|
}
|
|
|
|
// Parse a property into accessor methods
|
|
function ParseClassProperty ($class, $parts, $deprecatedmods) {
|
|
|
|
$method = array();
|
|
//print_r($parts);
|
|
|
|
// property has attributes
|
|
if (count($parts) == 5) {
|
|
//$property["parameters"] = explode(",", $parts[1]);
|
|
$property["parameters"] = preg_split("/\s*,\s*/", $parts[1]);
|
|
$attributes = $parts[1];
|
|
$type = $parts[2];
|
|
$pointertype = $parts[3];
|
|
$content = $parts[4];
|
|
} else {
|
|
$property["parameters"] = array();
|
|
$type = $parts[1];
|
|
$pointertype = $parts[2];
|
|
$content = $parts[3];
|
|
}
|
|
|
|
// unspecified type -> id
|
|
if ($type == "") $type = "id";
|
|
|
|
// get property list
|
|
$list = explode(",", $content);
|
|
if (count($list) > 1) {
|
|
$property_list = array();
|
|
foreach ($list as $key) {
|
|
// clean the name and remove the return type
|
|
$property_list[] = trim($key);
|
|
}
|
|
//print_r($property_list);
|
|
} else {
|
|
$property_list = array($content);
|
|
}
|
|
|
|
$methods = array();
|
|
|
|
foreach ($property_list as $property_name) {
|
|
|
|
// property name
|
|
if (eregi("([a-zA-Z0-9_]+)[[:space:]]*$", $property_name, $captures)) {
|
|
$property["name"] = ucwords($captures[1]);
|
|
$property["name_raw"] = $captures[1];
|
|
}
|
|
|
|
// property type
|
|
$type = $this->ConvertReturnType($type,$pointertype);
|
|
|
|
// prepare for appending
|
|
if ($deprecatedmods != "") $deprecatedmods .= ";";
|
|
|
|
// setter
|
|
if (!in_array("readonly", $property["parameters"])) {
|
|
$method["setter"] = array();
|
|
|
|
$name = $property["name"];
|
|
if (!$this->GetPropertyName("setter", $property["parameters"], $name)) {
|
|
$name = "set$name";
|
|
}
|
|
|
|
// protect method name from keywords
|
|
if ($this->IsKeywordReserved($name)) $name .= "_";
|
|
|
|
$method["setter"]["def"] = "procedure $name (newValue: $type);";
|
|
$method["setter"]["objc_method"] = "$name:";
|
|
$method["setter"]["class"] = $class;
|
|
$method["setter"]["name"] = $name;
|
|
$method["setter"]["kind"] = "procedure";
|
|
$method["setter"]["deprecated"] = $deprecatedmods;
|
|
$method["setter"]["property"] = $property["name_raw"];
|
|
//$method["setter"]["comment"] = $this->InsertCurrentComment();
|
|
}
|
|
|
|
// getter
|
|
$method["getter"] = array();
|
|
|
|
$name = $property["name"];
|
|
if (!$this->GetPropertyName("getter", $property["parameters"], $name)) {
|
|
$name = strtolower(substr($name, 0, 1)) . substr($name, 1);
|
|
}
|
|
|
|
// protect method name from keywords
|
|
if ($this->IsKeywordReserved($name)) $name .= "_";
|
|
|
|
$method["getter"]["def"] = "function $name: $type;";
|
|
$method["getter"]["objc_method"] = $name;
|
|
$method["getter"]["class"] = $class;
|
|
$method["getter"]["name"] = $name;
|
|
$method["getter"]["kind"] = "function";
|
|
$method["getter"]["deprecated"] = $deprecatedmods;
|
|
$method["getter"]["property"] = $property["name_raw"];
|
|
//$method["getter"]["comment"] = $this->InsertCurrentComment();
|
|
|
|
// append to array of methods
|
|
$methods[] = $method;
|
|
|
|
}
|
|
|
|
//print_r($methods);
|
|
return $methods;
|
|
}
|
|
|
|
// Parse header classes and methods
|
|
function ParseHeaderClasses ($file) {
|
|
$contents = file_get_contents($file);
|
|
|
|
$file_name = substr($file, (strripos($file, "/")) + 1, strlen($file));
|
|
$line_count = 0;
|
|
|
|
// reset comments from previous parsing sections
|
|
$this->ResetComment();
|
|
|
|
$lines = explode("\n", $contents);
|
|
foreach ($lines as $line) {
|
|
$line_count++;
|
|
|
|
// skip blocks
|
|
if ($this->SkipBlock($line)) continue;
|
|
|
|
// ignore lines
|
|
if (in_array($line, $this->ignore_lines)) continue;
|
|
|
|
// remove macros
|
|
$line = $this->RemoveVersionMacros($line, $deprecatedmods);
|
|
|
|
// remove external class macros
|
|
$line = eregi_replace("^[A-Z0-9]+_EXTERN_CLASS[[:space:]]+", "", $line);
|
|
|
|
// build comments
|
|
if (!$got_class) $this->BuildComment($line, $file_name);
|
|
|
|
// parse instance vars
|
|
if ($got_instance_vars) {
|
|
|
|
// scope compiler directive
|
|
if (eregi($this->regex_scope_compiler_directive, $line, $captures)) {
|
|
$this->instance_var_scope = $captures[1];
|
|
continue;
|
|
}
|
|
|
|
// remove comments
|
|
$line = $this->RemoveComments($line);
|
|
|
|
// parse instance variables
|
|
$result = $this->ParseInstanceVariables($line, $struct, $this->GetProtectedKeywords($this->current_class),$this->dump["master"][$this->current_class["name"]]["field_names"]);
|
|
|
|
// parse structures
|
|
if ($struct["isfinished"]) {
|
|
//print_r($struct);
|
|
//$this->dump[$file_name]["classes"][$current]["ivars"][] = $struct["name"].": $current"."_".$struct["name"].";";
|
|
$this->dump[$file_name]["classes"][$current]["ivars_structs"][] = $struct;
|
|
|
|
// print inline-record type
|
|
$this->dump[$file_name]["classes"][$current]["ivars"][] = $struct["name"].": ".$this->record_keyword;
|
|
if ($struct["bitpacked"]) {
|
|
$this->dump[$file_name]["classes"][$current]["ivars"][] = $this->BitPackedForceAlignment($struct["bitpacked_first_type"], " ", " ");
|
|
}
|
|
|
|
// print fields
|
|
if ($struct["fields"]) {
|
|
foreach ($struct["fields"] as $field) $this->dump[$file_name]["classes"][$current]["ivars"][] = " ".$field;
|
|
}
|
|
if ($struct["bitpacked"]) {
|
|
$this->dump[$file_name]["classes"][$current]["ivars"][] = " end;";
|
|
$this->dump[$file_name]["classes"][$current]["ivars"][] = " );";
|
|
}
|
|
$this->dump[$file_name]["classes"][$current]["ivars"][] = " end;";
|
|
|
|
$struct = null;
|
|
}
|
|
|
|
if(($result != null) && ($result != "struct")) {
|
|
//print($result);
|
|
|
|
// add a single string or an array of fields to the ivars array
|
|
if (count($result) <= 1) {
|
|
$this->dump[$file_name]["classes"][$current]["ivars"][] = $result;
|
|
} else {
|
|
foreach ($result as $field) {
|
|
$this->dump[$file_name]["classes"][$current]["ivars"][] = $field;
|
|
}
|
|
}
|
|
}
|
|
|
|
// instance var section terminated.
|
|
if (preg_match("!^\s*}\s*[;]*$!", $line)) {
|
|
$struct = null;
|
|
$got_instance_vars = false;
|
|
$this->instance_var_scope = null;
|
|
}
|
|
|
|
} elseif ($got_class) { // parse the class
|
|
|
|
// the instance variable section started after the class line and no other ivar's were parsed yet
|
|
if (!$this->dump[$file_name]["classes"][$current]["ivars"]) {
|
|
if (preg_match("!{\s*$!", $line)) {
|
|
$got_instance_vars = true;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// build comments
|
|
$this->BuildComment($line, $file_name);
|
|
|
|
// remove comments
|
|
$line = $this->RemoveComments($line);
|
|
|
|
// found property
|
|
if ($this->LineHasProperty($line, $captures)) {
|
|
$properties = $this->ParseClassProperty($current, $captures, $deprecatedmods);
|
|
foreach ($properties as $property) {
|
|
if ($property["setter"]) {
|
|
if ($this->AddMethodToClass($property["setter"], $this->dump[$file_name]["classes"][$current])) {
|
|
$this->dump[$file_name]["classes"][$current]["methods"][] = $property["setter"];
|
|
}
|
|
}
|
|
|
|
if ($property["getter"]) {
|
|
if ($this->AddMethodToClass($property["getter"], $this->dump[$file_name]["classes"][$current])) {
|
|
$this->dump[$file_name]["classes"][$current]["methods"][] = $property["getter"];
|
|
}
|
|
}
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
// build method fragment
|
|
if ($method_fragment) $method_fragment .= " ".trim($line, " ");
|
|
|
|
// found method fragment termination
|
|
if (($method_fragment) && (preg_match($this->pregex_objc_method_terminate, $line))) {
|
|
$line = $method_fragment;
|
|
$method_fragment = null;
|
|
}
|
|
|
|
// found method
|
|
if (preg_match($this->pregex_objc_method_params, $line, $captures)) {
|
|
$method = $this->ConvertObjcMethodToPascal($current, $line, $captures, $this->GetProtectedKeywords($this->current_class), true, $deprecatedmods);
|
|
|
|
// add documentation for method
|
|
$method["documentation"] = $this->FindDocumentationForMethod($current, $method["objc_method"]);
|
|
|
|
if ($this->AddMethodToClass($method, $this->dump[$file_name]["classes"][$current])) {
|
|
//if ($this->comment_terminated) $method["comment"] = $this->InsertCurrentComment();
|
|
$this->dump[$file_name]["classes"][$current]["methods"][] = $method;
|
|
}
|
|
|
|
} elseif (preg_match($this->pregex_objc_method_no_params, $line, $captures)) {
|
|
$method = $this->ConvertObjcMethodToPascal($current, $line, $captures, $this->GetProtectedKeywords($this->current_class), false, $deprecatedmods);
|
|
|
|
// add documentation for method
|
|
$method["documentation"] = $this->FindDocumentationForMethod($current, $method["objc_method"]);
|
|
|
|
if ($this->AddMethodToClass($method, $this->dump[$file_name]["classes"][$current])) {
|
|
//if ($this->comment_terminated) $method["comment"] = $this->InsertCurrentComment();
|
|
$this->dump[$file_name]["classes"][$current]["methods"][] = $method;
|
|
}
|
|
} elseif (preg_match($this->pregex_objc_method_partial, $line, $captures)) {
|
|
$method_fragment = $line;
|
|
}
|
|
|
|
// found the end
|
|
if (ereg("^@end", $line)) {
|
|
$got_class = false;
|
|
$this->ResetComment();
|
|
}
|
|
}
|
|
|
|
// ==== got class ====
|
|
if ((eregi($this->regex_objc_class, $line, $captures)) || (eregi($this->regex_objc_class_no_super, $line, $captures))) {
|
|
$current = $captures[1];
|
|
$got_class = true;
|
|
$has_superclass = true;
|
|
|
|
// check for instance variable section
|
|
if (preg_match("!{\s*$!", $line)) $got_instance_vars = true;
|
|
|
|
// get the protocols which the class adopts
|
|
if (eregi($this->regex_objc_class, $line, $captures)) {
|
|
if ($captures[3]) $this->dump[$file_name]["classes"][$current]["adopts"] = $captures[3];
|
|
} else {
|
|
if ($captures[2]) $this->dump[$file_name]["classes"][$current]["adopts"] = $captures[2];
|
|
$has_superclass=false;
|
|
}
|
|
|
|
// clean up the conforms string
|
|
if ($this->dump[$file_name]["classes"][$current]["adopts"]) {
|
|
$conform_protocols = explode(",", $this->dump[$file_name]["classes"][$current]["adopts"]);
|
|
$protocol_list = array();
|
|
|
|
foreach ($conform_protocols as $protocol) {
|
|
$protocol = trim($protocol, "<> ");
|
|
$protocol_clean .= $protocol."$this->protocol_suffix, ";
|
|
$protocol_list[] = $protocol;
|
|
}
|
|
|
|
$protocol_clean = trim($protocol_clean, ", ");
|
|
$this->dump[$file_name]["classes"][$current]["adopts"] = $protocol_clean;
|
|
$this->dump[$file_name]["classes"][$current]["protocols"] = $protocol_list;
|
|
|
|
$protocol_clean = "";
|
|
}
|
|
|
|
$this->dump[$file_name]["classes"][$current]["name"] = $captures[1];
|
|
if ($has_superclass) {
|
|
$this->dump[$file_name]["classes"][$current]["super"] = $captures[2];
|
|
$this->dump[$file_name]["classes"][$current]["super_class"] = &$this->dump["master"][$captures[2]];
|
|
}
|
|
$this->dump[$file_name]["classes"][$current]["file_name"] = $file_name;
|
|
$this->dump[$file_name]["classes"][$current]["file_clean"] = substr($file_name, 0, (strripos($file_name, ".")));
|
|
$this->dump[$file_name]["classes"][$current]["protected_keywords"] = array();
|
|
$this->dump[$file_name]["classes"][$current]["declared_methods"] = array();
|
|
$this->dump[$file_name]["classes"][$current]["comment"] = $this->InsertCurrentComment();
|
|
|
|
$this->dump[$file_name]["category_methods"] = array();
|
|
|
|
$this->current_class = &$this->dump[$file_name]["classes"][$current];
|
|
|
|
// append master class listing
|
|
$this->dump["master"][$current] = &$this->dump[$file_name]["classes"][$current];
|
|
|
|
// preparse for protected keywords
|
|
$this->PreparseClass($lines, $line_count);
|
|
|
|
// preparse for category methods that may present naming conflicts
|
|
$category_methods = $this->PreparseCategoryMethods($file);
|
|
|
|
// add category methods to protected keywords
|
|
if ($category_methods) $this->current_class["protected_keywords"] = array_merge($this->current_class["protected_keywords"], $category_methods);
|
|
|
|
// print class hierarchy
|
|
if ($this->show_class_hierarchy) {
|
|
$this->GetClassHierarchy($this->current_class, $hierarchy);
|
|
$hierarchy_string = "";
|
|
foreach ($hierarchy as $value) {
|
|
$hierarchy_string .= "$value->";
|
|
}
|
|
$hierarchy_string = trim($hierarchy_string, "->");
|
|
print(" - $current: $hierarchy_string\n");
|
|
}
|
|
|
|
$this->class_count ++;
|
|
//print_r($this->dump[$file_name]["classes"][$current]);
|
|
}
|
|
|
|
}
|
|
|
|
//print_r($this->dump[$file_name]["classes"][$current]);
|
|
}
|
|
|
|
// Parse categories which depend on another header
|
|
function ParseHeaderDependents ($file) {
|
|
$file_name = substr($file, (strripos($file, "/")) + 1, strlen($file));
|
|
|
|
$this->ParseHeaderCategories($file);
|
|
|
|
print("+ Parsed $file_name for dependents\n");
|
|
}
|
|
|
|
// Main entry to parse a header
|
|
function ParseHeader ($file) {
|
|
$file_name = substr($file, (strripos($file, "/")) + 1, strlen($file));
|
|
$name_clean = substr($file_name, 0, (strripos($file_name, ".")));
|
|
|
|
// get the framework we're parsing from if it was specified during a batch parse
|
|
if ((!$this->framework) && (eregi("/([a-zA-Z]+)\.framework", $file, $captures))) $this->framework = strtolower($captures[1]);
|
|
|
|
// set the dump for the header
|
|
$this->dump[$file_name]["path"] = "$this->root$this->out/$this->framework/$name_clean.inc";
|
|
$this->dump[$file_name]["path_partial"] = "$this->framework/$name_clean.inc";
|
|
$this->dump[$file_name]["path_merge"] = $this->dump[$file_name]["path"].".merge";
|
|
$this->dump[$file_name]["framework"] = $this->framework;
|
|
$this->dump[$file_name]["name"] = $file_name;
|
|
$this->dump[$file_name]["name_clean"] = $name_clean;
|
|
$this->dump[$file_name]["anoninternstrucs"] = 0;
|
|
$this->dump[$file_name]["anonbitfields"] = 0;
|
|
$this->current_header = &$this->dump[$file_name];
|
|
|
|
// parse each section of the header
|
|
$this->ParseHeaderTypes($file);
|
|
$this->ParseHeaderProtocols($file);
|
|
$this->ParseHeaderClasses($file);
|
|
|
|
print("+ Parsed $file_name\n");
|
|
}
|
|
|
|
// Parses the docset at $path for the current framework
|
|
function ParseFrameworkDocset ($path) {
|
|
$name = basename($path);
|
|
|
|
$parser = new DocSetParser($path);
|
|
if ($parser->parse_directory($this->docset_paths[$name])) {
|
|
$this->docset = $parser->methods;
|
|
print("+ Parsed documentation for $name.\n");
|
|
}
|
|
|
|
}
|
|
|
|
// Parse all headers assigned in $this->frameworks
|
|
function ParseAllFrameworks ($ignore_files, $parse_only) {
|
|
|
|
foreach ($this->frameworks as $framework_name => $framework_info) {
|
|
|
|
// framework is disabled
|
|
if ($framework_info["enabled"] != 1) continue;
|
|
|
|
// set the current framework being parsed
|
|
$this->framework = $framework_name;
|
|
|
|
// get the root file path
|
|
if ($this->out != "/") {
|
|
$path = $this->root.$this->out."/".$framework_info["root"];
|
|
} else {
|
|
$path = $this->root.$framework_info["root"];
|
|
}
|
|
|
|
// Parse the framework docset
|
|
if ($this->parse_docsets) $this->ParseFrameworkDocset($framework_info["docset"]);
|
|
|
|
// Load the header if found
|
|
if (file_exists($path)) {
|
|
$contents = file_get_contents($path);
|
|
$lines = explode("\n", $contents);
|
|
|
|
foreach ($lines as $line) {
|
|
|
|
$header = null;
|
|
$path = null;
|
|
|
|
// parse the header path from the {$include} macro
|
|
if (eregi($framework_info["include_pattern"], $line, $captures)) {
|
|
$header = $captures[1].".h";
|
|
$path = $framework_info["headers"]."/$header";
|
|
}
|
|
|
|
// parse the header path from {-parse} directive
|
|
if (eregi("^\{-parse[[:space:]]+(.*)[[:space:]]*\}", $line, $captures)) {
|
|
$header = $captures[1];
|
|
$path = $framework_info["headers"]."/$header";
|
|
}
|
|
|
|
// parse the header if valid
|
|
if (file_exists($path)) {
|
|
|
|
// main header
|
|
if ($parse_only) {
|
|
if (@in_array($header, $parse_only)) $this->ParseHeader($path);
|
|
} elseif (@!in_array($header, $ignore_files)) {
|
|
$this->ParseHeader($path);
|
|
}
|
|
|
|
// header dependents
|
|
if ($parse_only) {
|
|
if (@in_array($header, $parse_only)) $this->ParseHeaderDependents($path);
|
|
} elseif (@!in_array($header, $ignore_files)) {
|
|
$this->ParseHeaderDependents($path);
|
|
}
|
|
|
|
} elseif ($header) {
|
|
print("*** The header $path could not be found. ***\n");
|
|
$this->warning_count++;
|
|
}
|
|
}
|
|
} else {
|
|
die("FATAL: The master include \"$path\" is missing.\n");
|
|
}
|
|
}
|
|
|
|
// diagnostics
|
|
print("\n• Parsed $this->method_count methods in $this->class_count classes.\n\n");
|
|
|
|
if ($this->warning_count > 0) print("• $this->warning_count warnings were encountered.\n\n");
|
|
}
|
|
|
|
/**
|
|
* MAIN METHODS
|
|
*/
|
|
|
|
// Parse all classes/categories (non-delegate) from the header
|
|
function CollectHeaderClasses ($file) {
|
|
|
|
// can't find the header, bail
|
|
if (!file_exists($file)) return;
|
|
|
|
$contents = file_get_contents($file);
|
|
|
|
$lines = explode("\n", $contents);
|
|
foreach ($lines as $line) {
|
|
|
|
// remove external class macros
|
|
$line = eregi_replace("^[A-Z0-9]+_EXTERN_CLASS[[:space:]]+", "", $line);
|
|
// remove version macro's (some can appear before a class)
|
|
$line = $this->RemoveVersionMacros($line, $dummy_deprecated_mods);
|
|
|
|
// classes
|
|
if (eregi($this->regex_objc_class, $line, $captures)) {
|
|
$this->defined_cocoa_classes[] = $captures[1];
|
|
// may already have been parsed as an anonymous class
|
|
if (!in_array($captures[1], $this->cocoa_classes))
|
|
$this->cocoa_classes[] = $captures[1];
|
|
}
|
|
if (eregi($this->regex_objc_class_no_super, $line, $captures)) {
|
|
$this->defined_cocoa_classes[] = $captures[1];
|
|
// may already have been parsed as an anonymous class
|
|
if (!in_array($captures[1], $this->cocoa_classes))
|
|
$this->cocoa_classes[] = $captures[1];
|
|
}
|
|
// anonymous classes ===
|
|
if (eregi($this->regex_objc_anon_class, $line, $captures)) {
|
|
$anon_classes = explode(",", $captures[1]);
|
|
foreach ($anon_classes as $anon_class) {
|
|
$anon_class=trim($anon_class);
|
|
// may already have been parsed as a regular class
|
|
if (!in_array($anon_class, $this->cocoa_classes)) {
|
|
$this->cocoa_classes[] = $anon_class;
|
|
if (!in_array($anon_class, $this->anon_cocoa_classes)) {
|
|
$this->anon_cocoa_classes[] = $anon_class;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// categories
|
|
if (eregi($this->regex_objc_category, $line, $captures)) {
|
|
$this->cocoa_categories[] = $captures[1];
|
|
}
|
|
}
|
|
}
|
|
|
|
// Build array of all known classes in frameworks
|
|
function BuildFrameworkClasses () {
|
|
|
|
foreach ($this->frameworks as $framework_name => $framework_info) {
|
|
|
|
// framework is disabled
|
|
if ($framework_info["enabled"] != 1) continue;
|
|
|
|
if ($handle = @opendir($framework_info["headers"])) {
|
|
while (($file = readdir($handle)) !== false) {
|
|
if (eregi($framework_info["header_pattern"], $file)) {
|
|
$this->CollectHeaderClasses($framework_info["headers"]."/$file");
|
|
}
|
|
}
|
|
closedir($handle);
|
|
} else {
|
|
die("FATAL: The framework \"$framework_name\" can not be located at ".$framework_info["headers"]);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
// Process a single and print output
|
|
function ProcessFile ($file, $print) {
|
|
|
|
// set the current framework to null so it's parsed from the framework
|
|
$this->framework = null;
|
|
|
|
$this->ParseHeader($file);
|
|
$this->ParseHeaderDependents($file);
|
|
|
|
if ($print) $this->PrintAllHeaders("", null, null, false);
|
|
}
|
|
|
|
// Loads parser settings from the XML file
|
|
function LoadFrameworksXML ($framework_path) {
|
|
$xml = new SimpleXMLElement(file_get_contents("frameworks.xml"));
|
|
|
|
foreach ($xml as $framework) {
|
|
$this->frameworks[(string) $framework->name]["root"] = (string) $framework->root;
|
|
$this->frameworks[(string) $framework->name]["headers"] = (string) $framework->headers;
|
|
if ($framework_path != "")
|
|
$this->frameworks[(string) $framework->name]["headers"] = preg_replace("!^.*/System/Library/Frameworks!", $framework_path, $this->frameworks[(string) $framework->name]["headers"]);
|
|
$this->frameworks[(string) $framework->name]["include_pattern"] = (string) $framework->include_pattern;
|
|
$this->frameworks[(string) $framework->name]["header_pattern"] = (string) $framework->header_pattern;
|
|
$this->frameworks[(string) $framework->name]["external_macro"] = (string) $framework->external_macro;
|
|
$this->frameworks[(string) $framework->name]["ignore_types"] = (string) $framework->ignore_types;
|
|
$this->frameworks[(string) $framework->name]["ignore_methods"] = (string) $framework->ignore_methods;
|
|
$this->frameworks[(string) $framework->name]["replace_types"] = $framework->replace_types;
|
|
$this->frameworks[(string) $framework->name]["ignore_lines"] = $framework->ignore_lines;
|
|
$this->frameworks[(string) $framework->name]["ignore_comments"] = $framework->ignore_comments;
|
|
$this->frameworks[(string) $framework->name]["docset"] = (string)$framework->docset;
|
|
$this->frameworks[(string) $framework->name]["enabled"] = false;
|
|
$this->frameworks[(string) $framework->name]["print"] = true;
|
|
}
|
|
|
|
}
|
|
|
|
function __construct ($directory, $out_directory, $frameworks, $frameworks_path, $show) {
|
|
$this->root = $directory;
|
|
$this->out = $out_directory;
|
|
$this->show = $show;
|
|
|
|
// load defined frameworks from xml
|
|
$this->LoadFrameworksXML($frameworks_path);
|
|
|
|
// enable frameworks requested by the command line options
|
|
if ($frameworks) {
|
|
foreach ($frameworks as $name) {
|
|
$name_clean = trim($name, "^ ");
|
|
$this->frameworks[$name_clean]["enabled"] = true;
|
|
|
|
// apply options from framework definition
|
|
if ($this->frameworks[$name_clean]["external_macro"]) $this->external_string_macros .= "|".$this->frameworks[$name_clean]["external_macro"];
|
|
|
|
if ($this->frameworks[$name_clean]["ignore_types"]) $this->ignore_types = array_merge($this->ignore_types, explode(",", $this->frameworks[$name_clean]["ignore_types"]));
|
|
if ($this->frameworks[$name_clean]["ignore_methods"]) $this->ignore_methods = array_merge($this->ignore_methods, explode(",", $this->frameworks[$name_clean]["ignore_methods"]));
|
|
|
|
if ($this->frameworks[$name_clean]["ignore_lines"]) {
|
|
foreach ($this->frameworks[$name_clean]["ignore_lines"]->line as $line) {
|
|
if (!in_array($line, $this->ignore_lines)) $this->ignore_lines[] = (string)$line;
|
|
}
|
|
}
|
|
|
|
if ($this->frameworks[$name_clean]["ignore_comments"]) {
|
|
foreach ($this->frameworks[$name_clean]["ignore_comments"]->line as $line) {
|
|
if (!in_array($line, $this->ignore_comments)) $this->ignore_comments[] = (string)$line;
|
|
}
|
|
}
|
|
|
|
if ($this->frameworks[$name_clean]["replace_types"]) {
|
|
foreach ($this->frameworks[$name_clean]["replace_types"]->type as $type) {
|
|
$pair = explode("=", (string)$type);
|
|
$this->replace_types[$pair[0]] = $pair[1];
|
|
}
|
|
}
|
|
|
|
// print mode
|
|
if ($name[0] == "^") $this->frameworks[$name_clean]["print"] = false;
|
|
}
|
|
}
|
|
|
|
//print_r($this->ignore_comments);
|
|
//print_r($this->ignore_lines);
|
|
//print_r($this->frameworks);
|
|
//exit;
|
|
$this->BuildFrameworkClasses();
|
|
}
|
|
|
|
}
|
|
|
|
?>
|