### Eclipse Workspace Patch 1.0 #P api_cvs Index: parser.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/api/parser.inc,v retrieving revision 1.41.2.4 diff -u -r1.41.2.4 parser.inc --- parser.inc 10 Jul 2009 07:06:44 -0000 1.41.2.4 +++ parser.inc 14 Jul 2009 02:09:53 -0000 @@ -6,6 +6,11 @@ * The PHP documentation parser that generates content for api.module. */ +module_load_include('inc', 'pgp', 'pgp.parser'); +module_load_include('inc', 'pgp', 'pgp.reader'); +module_load_include('inc', 'pgp', 'pgp.writer'); +module_load_include('inc', 'pgp', 'pgp.editor'); + /** * Parse out function definitions from the PHP manual. */ @@ -102,288 +107,332 @@ * Read in the file at the given path and parse its documentation. */ function api_parse_php_file($file_path, $branch_name, $file_name) { + api_parse_php_file_with_pgp($file_path, $branch_name, $file_name); +} + +/** + * Read in the file at the given path and parse its documentation. + * + * @param string $file_path + * @param string $branch_name + * @param string $file_name + * @param array + * An array of documentation block items. + */ +function api_parse_php_file_with_pgp($file_path, $branch_name, $file_name) { $source = file_get_contents($file_path); // Convert Mac/Win line breaks to Unix format. $source = str_replace("\r\n", "\n", $source); $source = str_replace("\r", "\n", $source); + // Build grammar statements. + $reader = new PGPReader($source); + $reader->addTokenNames(); + $reader->buildGrammar(); + $reader->setSnippet(); // Free up memory. + + // Retrieve items of interest. + $statements = $reader->getStatements(); + + // Edit grammar statements. + global $editor; + $editor = new PGPEditor(); + + $default_block = api_default_block($file_path, $branch_name, $file_name); + + // Reserve the first array slot for the file documentation block. + global $docblocks; $docblocks = array(); + $docblocks[] = api_documentation_file($source, $default_block); + $source = ''; // Free up memory. - // Set up documentation block for file, in case it is not explicitly defined. - $docblocks[0] = array( - 'object_name' => $file_name, + global $nested_groups; + $nested_groups = array(); + + api_documentation_loop($statements, $default_block); + + $reader->setStatements(); // Free up memory. + + api_save_documentation($docblocks, $branch_name, $file_name); + + $docblocks = array(); // Free up memory. +} + +/** + * Build a list of documentation items. + * + * @param array $statements + * An array of body statements. + * @param array $default_block + * The default documentation block item. + */ +function api_documentation_loop($statements, $default_block) { + global $docblocks; + global $is_file_block; + + // Traverse statement array to gather documentation items. + foreach ($statements as $statement) { + if (!isset($statement['type'])) { + continue; + } + // Common processing. + switch ($statement['type']) { +// case T_INTERFACE: +// case T_CLASS: + case T_FUNCTION: + case T_DEFINE: + case T_GLOBAL: + $docblock = api_documentation_item($statement, $default_block); + break; + + case T_DOC_COMMENT: + $docblock = api_documentation_comment($statement, $default_block/*, $branch_name, $file_name*/); + if ($is_file_block) { + $is_file_block = FALSE; + $docblocks[0]['documentation'] = $docblock['documentation']; + $docblocks[0]['summary'] = $docblock['summary']; + // Reset the docblock so we do not add it again to the list. + $docblock = array(); + } + break; + + default: + $docblock = array(); + continue; + +// case T_CONST: +// case T_VAR: +// $docblock = api_documentation_global($statement, $branch_name, $file_name); +// break; + } + if ($docblock && $docblock['object_type'] != '') { + if ($docblock['object_name'] == '') { + dpm("empty name\n"); + dpm($docblock); + } + $docblocks[] = $docblock; + } + // Additional processing. + switch ($statement['type']) { +// case T_INTERFACE: +// case T_CLASS: + case T_FUNCTION: + api_documentation_loop($statement['body'], $default_block); + break; + } + } +} + +/** + * Return default documentation block item. + * + * @param string $branch_name + * @param string $file_name + * @return array + */ +function api_default_block($file_path, $branch_name, $file_name) { +// static $default; + $default = array( + 'object_name' => '', 'branch_name' => $branch_name, - 'object_type' => 'file', + 'object_type' => '', 'file_name' => $file_name, - 'title' => strpos($file_name, '/') ? substr($file_name, strrpos($file_name, '/') + 1) : $file_name, + 'title' => '', 'summary' => '', 'documentation' => '', - 'code' => api_format_php($source), - 'version' => '', - 'modified' => filemtime($file_path), + 'code' => '', + 'modified' => filemtime($file_path), // Only needed for 'file' item, but it simplifies parameters in other functions. ); - $version_match = array(); - if (preg_match('!\$'.'Id: .*?,v (.*?) (.*?) (.*?) (.*?) Exp \$!', $source, $version_match)) { - $docblocks[0]['version'] = $version_match[1] .' (checked in on '. $version_match[2] .' at '. $version_match[3] .' by '. $version_match[4] .')'; - } + return $default; +} - $nested_groups = array(); +/** + * Add items to documentation block for a statement. + * + * @param array $statement + * An array of the statement block. + * @param array $docblock + * An array of the documentation block. + * @return array + * An array of the documentation block. + */ +function api_documentation_item($statement, $docblock) { + global $editor; + + $docblock['object_type'] = $editor->statementTypeToString($statement); + $docblock['object_name'] = $editor->statementOperandToText($statement); + $docblock['title'] = $editor->statementOperandToText($statement); + $docblock['code'] = api_format_php("statementToText($statement) ."?>"); + $docblock['start_line'] = 0; // TODO - $docblock_matches = array(); - preg_match_all('!/\*\*(.*?)\*/!s', $source, $docblock_matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE); + $docblock['content'] = $editor->commentToString($statement['comment']); - foreach ($docblock_matches as $docblock_match) { - $docblock = array( - 'object_name' => '', - 'branch_name' => $branch_name, - 'object_type' => '', - 'file_name' => $file_name, - 'title' => '', - 'summary' => '', - 'documentation' => '', - 'code' => '', - ); - $docblock['content'] = str_replace(array("\n *", "\n "), array("\n", "\n"), $docblock_match[1][0]); - $docblock['start'] = $docblock_match[0][1]; - $docblock['length'] = strlen($docblock_match[0][0]); - $code_start = $docblock['start'] + $docblock['length'] + 1; - $docblock['start_line'] = substr_count(substr($source, 0, $code_start), "\n"); - - // Determine what kind of documentation block this is. - if (substr($source, $code_start, 8) == 'function') { - $function_matches = array(); - - $docblock['object_type'] = 'function'; - preg_match('!^function (&?([a-zA-Z0-9_]+)\(.*?)\s*\{!', substr($source, $code_start), $function_matches); - $docblock['object_name'] = $function_matches[2]; - $docblock['title'] = $function_matches[2]; - $docblock['signature'] = $function_matches[1]; - - // We rely on the Drupal coding convention that functions are closed in column 1. - $code_end = strpos($source, "\n}", $code_start) + 2; - $docblock['code'] = substr($source, $code_start, $code_end - $code_start); - $docblock['code'] = api_format_php(""); - - // Find parameter definitions. - $param_match = array(); - $offset = 0; - $docblock['parameters'] = ''; - while (preg_match('!@param(.*?)(?=\n@|\n\n|$)!s', substr($docblock['content'], $offset), $param_match, PREG_OFFSET_CAPTURE)) { - $docblock['content'] = str_replace($param_match[0][0], '', $docblock['content']); - $docblock['parameters'] .= "\n\n". $param_match[1][0]; - $offset = $param_match[0][1]; - } - $docblock['parameters'] = api_format_documentation($docblock['parameters'], $branch_name); - - // Find return value definitions. - $return_matches = array(); - $docblock['return_value'] = ''; - preg_match_all('!@return(.*?)(\n@|\n\n|$)!s', $docblock['content'], $return_matches, PREG_SET_ORDER); - foreach ($return_matches as $return_match) { - $docblock['content'] = str_replace($return_match[0], '', $docblock['content']); - $docblock['return_value'] .= "\n\n". $return_match[1]; - } - $docblock['return_value'] = api_format_documentation($docblock['return_value'], $branch_name); + if ($statement['type'] == T_FUNCTION) { + $docblock['signature'] = $editor->functionGetSignature($statement); - $docblock['function calls'] = api_parse_function_calls($docblock['code']); + // Find parameter definitions. + $matches = array(); + $offset = 0; + $docblock['parameters'] = ''; + // TODO This regex appears to be opportunistic about contents of next line. + while (preg_match('!@param(.*?)(?=\n@|\n\n|$)!s', substr($docblock['content'], $offset), $matches, PREG_OFFSET_CAPTURE)) { + $docblock['content'] = str_replace($matches[0][0], '', $docblock['content']); + $docblock['parameters'] .= "\n\n". $matches[1][0]; + $offset = $matches[0][1]; + } + $docblock['parameters'] = api_format_documentation($docblock['parameters'], $docblock['branch_name']); // TODO The branch_name parameter is unused. - // Determine group membership. - $group_matches = array(); - preg_match_all('!@(ingroup|addtogroup) ([a-zA-Z0-9_]+)!', $docblock['content'], $group_matches); - $docblock['groups'] = $group_matches[2]; - $docblock['content'] = preg_replace('!@ingroup.*?\n!', '', $docblock['content']); - - foreach ($nested_groups as $group_id) { - if (!empty($group_id)) { - $docblock['groups'][] = $group_id; - } - } + // Find return value definitions. + $matches = array(); + $docblock['return_value'] = ''; + preg_match_all('!@return(.*?)(\n@|\n\n|$)!s', $docblock['content'], $matches, PREG_SET_ORDER); + foreach ($matches as $match) { + $docblock['content'] = str_replace($match[0], '', $docblock['content']); + $docblock['return_value'] .= "\n\n". $match[1]; } - else if (substr($source, $code_start, 6) == 'define') { - $constant_matches = array(); + $docblock['return_value'] = api_format_documentation($docblock['return_value'], $docblock['branch_name']); // TODO The branch_name parameter is unused. - $docblock['object_type'] = 'constant'; - preg_match('!^define\([\'"]([a-zA-Z0-9_]+)[\'"]!', substr($source, $code_start), $constant_matches); - $docblock['object_name'] = $constant_matches[1]; - $docblock['title'] = $constant_matches[1]; - - $code_end = strpos($source, ';', $code_start) + 1; - $docblock['code'] = substr($source, $code_start, $code_end - $code_start); - $docblock['code'] = api_format_php(""); + // Find function calls. + $docblock['function calls'] = api_parse_function_calls($docblock['code']); // Add this helper routine!!! + } - // Determine group membership. - $group_matches = array(); - preg_match_all('!@(ingroup|addtogroup) ([a-zA-Z0-9_]+)!', $docblock['content'], $group_matches); - $docblock['groups'] = $group_matches[2]; - $docblock['content'] = preg_replace('!@ingroup.*?\n!', '', $docblock['content']); - - foreach ($nested_groups as $group_id) { - if (!empty($group_id)) { - $docblock['groups'][] = $group_id; - } - } - } - else if (substr($source, $code_start, 6) == 'global') { - $global_matches = array(); - $docblock['object_type'] = 'global'; - preg_match('!^global (\$[a-zA-Z0-9_]+)!', substr($source, $code_start), $global_matches); + $docblock['documentation'] = api_format_documentation($docblock['content'], $docblock['branch_name']); // TODO The branch_name parameter is unused. + $docblock['summary'] = api_documentation_summary($docblock['documentation']); + + // Determine group membership. + api_documentation_group($docblock); + api_documentation_nested_group($docblock); + + return $docblock; +} + +/** + * Add items to documentation block for a statement. + * + * @param array $statement + * An array of the statement block. + * @param array $docblock + * An array of the documentation block. + * @return array + * An array of the documentation block. + */ +function api_documentation_file($source, $docblock) { + $filename = $docblock['file_name']; - $docblock['object_name'] = substr($global_matches[1], 1); - $docblock['title'] = $global_matches[1]; + $docblock['object_name'] = $filename; + $docblock['object_type'] = 'file'; + $docblock['title'] = strpos($filename, '/') ? substr($filename, strrpos($filename, '/') + 1) : $filename; + $docblock['code'] = api_format_php($source); - $code_end = strpos($source, ';', $code_start) + 1; - $docblock['code'] = substr($source, $code_start, $code_end - $code_start); - $docblock['code'] = api_format_php(""); + $matches = array(); + if (preg_match('!\$'.'Id: .*?,v (.*?) (.*?) (.*?) (.*?) Exp \$!', $source, $matches)) { + $docblock['version'] = $matches[1] . ' (checked in on ' . $matches[2] . ' at ' . $matches[3] . ' by ' . $matches[4] . ')'; + } - $docblock['start_line'] = substr_count(substr($source, 0, $code_start), "\n"); + return $docblock; +} - // Determine group membership. - $group_matches = array(); - preg_match_all('!@(ingroup|addtogroup) ([a-zA-Z0-9_]+)!', $docblock['content'], $group_matches); - $docblock['groups'] = $group_matches[2]; - $docblock['content'] = preg_replace('!@ingroup.*?\n!', '', $docblock['content']); - - foreach ($nested_groups as $group_id) { - if (!empty($group_id)) { - $docblock['groups'][] = $group_id; - } - } +/** + * Determine group membership. + * + * @param array $docblock + * An array of the documentation block. + */ +function api_documentation_group(&$docblock) { + global $nested_groups; + + $group_matches = array(); + preg_match_all('!@(ingroup|addtogroup) ([a-zA-Z0-9_]+)!', $docblock['content'], $group_matches); + $docblock['groups'] = $group_matches[2]; + $docblock['content'] = preg_replace('!@ingroup.*?\n!', '', $docblock['content']); + + foreach ($nested_groups as $group_id) { + if (!empty($group_id)) { + $docblock['groups'][] = $group_id; } - else if (strpos($docblock['content'], '@mainpage') !== FALSE) { - $mainpage_matches = array(); - preg_match('!@mainpage (.*?)\n!', $docblock['content'], $mainpage_matches); - $docblock['title'] = $mainpage_matches[1]; - $docblock['content'] = preg_replace('!@mainpage.*?\n!', '', $docblock['content']); - $docblock['object_type'] = 'mainpage'; - $docblock['object_name'] = $branch_name; - } - else if (strpos($docblock['content'], '@file') !== FALSE) { - $docblocks[0]['content'] = str_replace('@file', '', $docblock['content']); - $docblocks[0]['documentation'] = api_format_documentation($docblocks[0]['content'], $branch_name); - $docblocks[0]['summary'] = api_documentation_summary($docblocks[0]['documentation']); + } +} + +/** + * Handle nested function groups. + * + * @param array $docblock + * An array of the documentation block. + */ +function api_documentation_nested_group($docblock) { + global $nested_groups; + + if (strpos($docblock['content'], '@{') !== FALSE) { + if ($docblock['object_type'] == 'group') { + array_push($nested_groups, $docblock['object_name']); } - else if (strpos($docblock['content'], '@defgroup') !== FALSE) { + else { $group_matches = array(); - if (preg_match('!@defgroup ([a-zA-Z0-9_.-]+) +(.*?)\n!', $docblock['content'], $group_matches)) { - $docblock['object_name'] = $group_matches[1]; - $docblock['title'] = $group_matches[2]; - $docblock['content'] = preg_replace('!@defgroup.*?\n!', '', $docblock['content']); - $docblock['object_type'] = 'group'; + if (preg_match('!@(ingroup|addtogroup) ([a-zA-Z0-9_]+)!', $docblock['content'], $group_matches)) { + array_push($nested_groups, $group_matches[2]); } else { - watchdog('api', 'Malformed @defgroup in %file at line %line.', array('%file' => $file_path, '%line' => $docblock['start_line']), WATCHDOG_NOTICE); + array_push($nested_groups, ''); } } + } + if (strpos($docblock['content'], '@}') !== FALSE) { + array_pop($nested_groups); + } +} - // Handle nested function groups. - if (strpos($docblock['content'], '@{') !== FALSE) { - if ($docblock['object_type'] == 'group') { - array_push($nested_groups, $docblock['object_name']); - } - else { - $group_matches = array(); - if (preg_match('!@(ingroup|addtogroup) ([a-zA-Z0-9_]+)!', $docblock['content'], $group_matches)) { - array_push($nested_groups, $group_matches[2]); - } - else { - array_push($nested_groups, ''); - } - } - } - if (strpos($docblock['content'], '@}') !== FALSE) { - array_pop($nested_groups); +/** + * Add items to documentation block for a comment. + * + * @param array $comment + * An array of the comment. + * @param array $docblock + * An array of the documentation block. + * @return array + * An array of the documentation block. + */ +function api_documentation_comment($comment, $docblock) { + global $editor; + global $is_file_block; + + $is_file_block = FALSE; + $docblock['content'] = $editor->commentToString($comment); + + $matches = array(); + if (strpos($docblock['content'], '@mainpage') !== FALSE) { + preg_match('!@mainpage (.*?)\n!', $docblock['content'], $matches); + $docblock['object_type'] = 'mainpage'; + $docblock['object_name'] = $docblock['branch_name']; + $docblock['title'] = $matches[1]; + $docblock['content'] = preg_replace('!@mainpage.*?\n!', '', $docblock['content']); + } + elseif (strpos($docblock['content'], '@file') !== FALSE) { + $is_file_block = TRUE; + $docblock['object_type'] = 'file'; // Redundant? + $docblock['content'] = str_replace('@file', '', $docblock['content']); + } + elseif (strpos($docblock['content'], '@defgroup') !== FALSE) { + if (preg_match('!@defgroup ([a-zA-Z0-9_.-]+) +(.*?)\n!', $docblock['content'], $matches)) { + $docblock['object_type'] = 'group'; + $docblock['object_name'] = $matches[1]; + $docblock['title'] = $matches[2]; + $docblock['content'] = preg_replace('!@defgroup.*?\n!', '', $docblock['content']); } - - if ($docblock['object_type'] != '') { - $docblock['documentation'] = api_format_documentation($docblock['content'], $branch_name); - $docblock['summary'] = api_documentation_summary($docblock['documentation']); - $docblocks[] = $docblock; + else { + watchdog('api', 'Malformed @defgroup in %file at line %line.', array('%file' => $file_path, '%line' => $docblock['start_line']), WATCHDOG_NOTICE); } } - // Find undocumented functions. - $function_matches = array(); - preg_match_all('%(? $function_match[2][0], - 'branch_name' => $branch_name, - 'object_type' => 'function', - 'file_name' => $file_name, - 'title' => $function_match[2][0], - 'summary' => '', - 'documentation' => '', - 'code' => ''); - $docblock['signature'] = $function_match[1][0]; - $docblock['parameters'] = ''; - $docblock['return_value'] = ''; - $docblock['groups'] = array(); - - $code_start = $function_match[0][1]; - $code_end = strpos($source, "\n}", $code_start) + 2; - $docblock['code'] = substr($source, $code_start, $code_end - $code_start); - $docblock['code'] = api_format_php(""); - - $docblock['function calls'] = api_parse_function_calls($docblock['code']); - - $docblock['start_line'] = substr_count(substr($source, 0, $code_start), "\n"); - - $docblocks[] = $docblock; - } - - // Find undocumented constants. - $constant_matches = array(); - preg_match_all('%(? $constant_match[1][0], - 'branch_name' => $branch_name, - 'object_type' => 'constant', - 'file_name' => $file_name, - 'title' => $constant_match[1][0], - 'summary' => '', - 'documentation' => '', - 'code' => ''); - $docblock['groups'] = array(); - - $code_start = $constant_match[0][1]; - $code_end = strpos($source, ';', $code_start) + 1; - $docblock['code'] = substr($source, $code_start, $code_end - $code_start); - $docblock['code'] = api_format_php(""); - - $docblock['start_line'] = substr_count(substr($source, 0, $code_start), "\n"); - - $docblocks[] = $docblock; - } - - // Find undocumented globals. - $global_matches = array(); - preg_match_all('%(? $global_match[1][0], - 'branch_name' => $branch_name, - 'object_type' => 'global', - 'file_name' => $file_name, - 'title' => $global_match[1][0], - 'summary' => '', - 'documentation' => '', - 'code' => '', - ); - $docblock['groups'] = array(); - - $code_start = $global_match[0][1]; - $code_end = strpos($source, ';', $code_start) + 1; - $docblock['code'] = substr($source, $code_start, $code_end - $code_start); - $docblock['code'] = api_format_php(""); - - $docblock['start_line'] = substr_count(substr($source, 0, $code_start), "\n"); - - $docblocks[] = $docblock; + if ($docblock['object_type'] != '') { + $docblock['documentation'] = api_format_documentation($docblock['content'], $docblock['branch_name']); // TODO The branch_name parameter is unused. + $docblock['summary'] = api_documentation_summary($docblock['documentation']); } - api_save_documentation($docblocks, $branch_name, $file_name); + // Update groups. + api_documentation_nested_group($docblock); + + return $docblock; } /** Index: api.info =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/api/api.info,v retrieving revision 1.6 diff -u -r1.6 api.info --- api.info 15 Aug 2008 08:01:10 -0000 1.6 +++ api.info 14 Jul 2009 02:09:53 -0000 @@ -4,3 +4,4 @@ package = Development core = 6.x dependencies[] = job_queue +dependencies[] = pgp