home *** CD-ROM | disk | FTP | other *** search
/ Nebula 2 / Nebula Two.iso / SourceCode / MiscKit1.7.1 / MiscKit / Examples / AutoDoc / lib / perl5 / Autodoc / ReadSource.pm < prev    next >
Encoding:
Perl POD Document  |  1995-11-02  |  29.1 KB  |  1,133 lines

  1. package Autodoc::ReadSource;
  2.  
  3. ###############################################################################
  4. ###############################################################################
  5. ##
  6. ##    Written by Adam Swift (c) 1995 by Friday Software and Consulting
  7. ##                           All rights reserved.
  8. ##
  9. ##      This notice may not be removed from this source code.
  10. ##
  11. ##    This program is included in the MiscKit by permission from the author
  12. ##    and its use is governed by the MiscKit license, found in the file
  13. ##    "LICENSE.rtf" in the MiscKit distribution.  Please refer to that file
  14. ##    for a list of all applicable permissions and restrictions.
  15. ##
  16. ##    Because AutoDoc is licensed free of charge, there is no warranty 
  17. ##    for the program.  Copyright holder, Friday Software and Consulting, 
  18. ##    is providing this program "as is" and this program is distributed in 
  19. ##    the hope that it will be useful, but WITHOUT ANY WARRANTY; without 
  20. ##    even the implied warranty of MERCHANTABILITY or FITNESS FOR A 
  21. ##    PARTICULAR PURPOSE.
  22. ##
  23. ###############################################################################
  24. ###############################################################################
  25.  
  26.  
  27. require 5.000;
  28.  
  29. ##########################
  30. # load required packages #
  31. ##########################
  32. use Exporter;
  33. use Autodoc::LogDebug;
  34. use Autodoc::ParseComments;
  35. use Autodoc::ScanFile;
  36.  
  37.  
  38. @ISA      = qw(Exporter);
  39. @EXPORT   = qw(read_objectinfo
  40.                object_version
  41.                object_year
  42.                object_name
  43.                object_inherits
  44.                object_conformsto
  45.                object_category
  46.                object_protocol
  47.                object_ivars
  48.                object_ivartypes
  49.                object_ivardocs
  50.                object_declaredin
  51.                object_classdocs
  52.                object_methodnames
  53.                object_methodblocks
  54.                object_methoddocs
  55.                object_classmethods
  56.                object_instmethods
  57.                object_defines
  58.                object_definevals
  59.                object_definedocs
  60.                object_typedefs
  61.                object_typedefvals
  62.                object_typedefdocs
  63.                object_macros
  64.                object_macrodocs
  65.                object_functions
  66.                object_functiondocs
  67.                object_globals
  68.                object_globaldocs
  69.                object_noobjectdefined
  70.                extract_methodname);
  71.  
  72.  
  73. ############################################################################
  74. # Purpose: Module that encapsulates reading in objective c source files to
  75. #          parse out language elements and source code comments to construct
  76. #          information from which documentation can be generated. 
  77. #
  78. # HISTORY: START
  79. # $Log: ReadSource.pm,v $
  80. # Revision 1.2  1995/10/20  22:16:28  aswift
  81. # Added DevMan style changes Log support
  82. #
  83. #
  84. # HISTORY: END
  85. ############################################################################
  86. #
  87. # GOBAL VARIABLES THAT HOLD OBJECT INFORMATION:
  88. #
  89. #  $obj_version        The object copyright version 
  90. #  $obj_year        The object copyright year 
  91. #  $obj_name        The object name 
  92. #  $obj_inherits    The object inheritance information
  93. #  $obj_category    The object category information
  94. #  $obj_protocol    The object protocol name
  95. #  @obj_ivars        The object instance variable list (in order of 
  96. #               declaration in the header file
  97. #  %obj_ivartypes    The instance variables' types, associated with the 
  98. #             instance variables names (from @obj_ivars)
  99. #  %obj_ivardocs    The instance variables' descriptions, associated with
  100. #             the instance variables names (from @obj_ivars)
  101. #  $obj_declaredin    The object's declaration (header file) location
  102. #  @obj_conformsto    The protocols the object conforms to
  103. #  $obj_classdocs    The object class description
  104. #  @obj_methodnames     The object method names list (short names, suitable for
  105. #             hash table lookups - similar to @selector style)
  106. #  %obj_methodblocks    The method block names, associated with the method
  107. #             that starts the method block (from @obj_methodnames)
  108. #  %obj_methoddocs    The method descriptions, associated with the method
  109. #             name (from @obj_methodnames)
  110. #  @obj_classmethods    The object class methods, full declaration style
  111. #  @obj_instmethods    The object instance methods, full declaration style
  112. #  @obj_defines         The definitions found in the header file
  113. #  %obj_definevals      The values assigned to the definitions
  114. #  %obj_definedocs      The documentation for the definitions
  115. #  @obj_typedefs        The typedef's found in the header file
  116. #  %obj_typedefvals     The values assigned to the typedefs
  117. #  %obj_typedefdocs     The documentation for the typedefs
  118. #  @obj_macros          The macro definitions found in the header file
  119. #  %obj_macrodocs       The documentation for the macros
  120. #  @obj_functions       The function declarations found in the header file
  121. #  %obj_functiondocs    The documentation for the functions
  122. #  @obj_globals         The global variable declarations found in the header
  123. #  %obj_globaldocs      The documentation for the globals
  124. #
  125. #############################################################################
  126.  
  127.  
  128. $module_version = '$Revision: 1.2 $';
  129. $module_version =~ s!(\$\w+: | \$)!!g;
  130. $module_id    = '$Id: ReadSource.pm,v 1.2 1995/10/20 22:16:28 aswift Exp $';
  131. $module_id      =~ s!(\$\w+: | \$)!!g;
  132. $module_name    = $module_id;
  133. $module_name    =~ s!^([^\,]+).*$!$1!;
  134.  
  135. #############################################################################
  136. #
  137. # NAME:       module_version
  138. #
  139. # ACTION:     returns the version number of this module
  140. #
  141. # RETURN:     the module version
  142. #
  143. #############################################################################
  144. sub module_version
  145. {
  146.     return $module_version;
  147. }
  148.  
  149. sub module_versionstamp
  150. {
  151.     return "$module_name (rev-$module_version)";
  152. }
  153.  
  154. #############################################################################
  155. #
  156. # Object info accessor methods
  157. #
  158. #############################################################################
  159. sub object_version
  160. {
  161.     return $obj_version if (defined($obj_version));
  162.     return "";
  163. }
  164.  
  165. sub object_year
  166. {
  167.     return $obj_year if (defined($obj_year));
  168.     return "";
  169. }
  170.  
  171. sub object_name
  172. {
  173.     return $obj_name if (defined($obj_name));
  174.     return "";
  175. }
  176.  
  177. sub object_inherits
  178. {
  179.     return $obj_inherits if (defined($obj_inherits));
  180.     return "";
  181. }
  182.  
  183. sub object_category
  184. {
  185.     return $obj_category if (defined($obj_category));
  186.     return "";
  187. }
  188.  
  189. sub object_protocol
  190. {
  191.     return $obj_protocol if (defined($obj_protocol));
  192.     return "";
  193. }
  194.  
  195. sub object_ivars
  196. {
  197.     return @obj_ivars if (defined(@obj_ivars));
  198.     return ();
  199. }
  200.  
  201. sub object_ivartypes
  202. {
  203.     return %obj_ivartypes if (defined(%obj_ivartypes));
  204.     return {};
  205. }
  206.  
  207. sub object_ivardocs
  208. {
  209.     return %obj_ivardocs if (defined(%obj_ivardocs));
  210.     return {};
  211. }
  212.  
  213. sub object_declaredin
  214. {
  215.     return $obj_declaredin if (defined($obj_declaredin));
  216.     return "";
  217. }
  218.  
  219. sub object_conformsto
  220. {
  221.     return @obj_conformsto if (defined(@obj_conformsto));
  222.     return ();
  223. }
  224.  
  225. sub object_classdocs
  226. {
  227.     return $obj_classdocs if (defined($obj_classdocs));
  228.     return "";
  229. }
  230.  
  231. sub object_methodnames
  232. {
  233.     return @obj_methodnames if (defined(@obj_methodnames));
  234.     return ();
  235. }
  236.  
  237. sub object_methodblocks
  238. {
  239.     return %obj_methodblocks if (defined(%obj_methodblocks));
  240.     return {};
  241. }
  242.  
  243. sub object_methoddocs
  244. {
  245.     return %obj_methoddocs if (defined(%obj_methoddocs));
  246.     return {};
  247. }
  248.  
  249. sub object_classmethods
  250. {
  251.     return @obj_classmethods if (defined(@obj_classmethods));
  252.     return ();
  253. }
  254.  
  255. sub object_instmethods
  256. {
  257.     return @obj_instmethods if (defined(@obj_instmethods));
  258.     return ();
  259. }
  260.  
  261. sub object_defines
  262. {
  263.     return @obj_defines if (defined(@obj_defines));
  264.     return ();
  265. }
  266.  
  267. sub object_definevals
  268. {
  269.     return %obj_definevals if (defined(%obj_definevals));
  270.     return {};
  271. }
  272.  
  273. sub object_definedocs
  274. {
  275.     return %obj_definedocs if (defined(%obj_definedocs));
  276.     return {};
  277. }
  278.  
  279. sub object_typedefs
  280. {
  281.     return @obj_typedefs if (defined(@obj_typedefs));
  282.     return ();
  283. }
  284.  
  285. sub object_typedefvals
  286. {
  287.     return %obj_typedefvals if (defined(%obj_typedefvals));
  288.     return {};
  289. }
  290.  
  291. sub object_typedefdocs
  292. {
  293.     return %obj_typedefdocs if (defined(%obj_typedefdocs));
  294.     return {};
  295. }
  296.  
  297. sub object_macros
  298. {
  299.     return @obj_macros if (defined(@obj_macros));
  300.     return ();
  301. }
  302.  
  303. sub object_macrodocs
  304. {
  305.     return %obj_macrodocs if (defined(%obj_macrodocs));
  306.     return {};
  307. }
  308.  
  309. sub object_globals
  310. {
  311.     return @obj_globals if (defined(@obj_globals));
  312.     return ();
  313. }
  314.  
  315. sub object_globaldocs
  316. {
  317.     return %obj_globaldocs if (defined(%obj_globaldocs));
  318.     return {};
  319. }
  320.  
  321. sub object_functions
  322. {
  323.     return @obj_functions if (defined(@obj_functions));
  324.     return ();
  325. }
  326.  
  327. sub object_functiondocs
  328. {
  329.     return %obj_functiondocs if (defined(%obj_functiondocs));
  330.     return {};
  331. }
  332.  
  333. sub object_noobjectdefined
  334. {
  335.     return 0
  336.       if (object_inherits() || object_category() || object_protocol());
  337.     return 1;
  338. }
  339.  
  340. sub init_globals
  341. {
  342.     local (@timearray, $obj_root);
  343.  
  344.     # remove any extra file path goo from object root
  345.     $obj_root = $_[0];
  346.     $obj_root =~ m!([^/]+)$!;    
  347.     $obj_root = $1;
  348.  
  349.     @timearray = localtime(time);
  350.  
  351.     $obj_version      = "";
  352.     $obj_year          = "19$timearray[5]";
  353.     $obj_name          = $1;
  354.     $obj_inherits      = "";
  355.     $obj_category      = "";
  356.     $obj_protocol     = "";
  357.     @obj_ivars          = ();
  358.     %obj_ivartypes      = {};
  359.     %obj_ivardocs      = {};
  360.     $obj_declaredin      = "$obj_root.h";
  361.     @obj_conformsto      = ();
  362.     $obj_classdocs      = "";
  363.     @obj_methodnames  = ();
  364.     %obj_methodblocks = {};    
  365.     %obj_methoddocs      = {};
  366.     @obj_classmethods = ();
  367.     @obj_instmethods  = (); 
  368.     @obj_defines      = ();
  369.     %obj_definevals      = {};
  370.     %obj_definedocs      = {};
  371.     @obj_typedefs      = ();
  372.     %obj_typedefvals  = {};
  373.     %obj_typedefdocs  = {};
  374.     @obj_macros          = ();
  375.     %obj_macrodocs      = {};
  376.     @obj_functions      = ();
  377.     %obj_functiondocs = {};
  378.     @obj_globals      = ();
  379.     %obj_globaldocs      = {};
  380. }
  381.  
  382. ###############################################################################
  383. # DATA EXTRACTION FROM OBJECT SOURCE FILES 
  384. #
  385. #    Fill the object data global variables with information read from the
  386. #  object source files.  Return 1 if enough information is read in order to 
  387. #  produce the documentation file
  388. #
  389.  
  390.  
  391. #
  392. # Locate and read from the source files for the object passed as the one
  393. # argument to this subroutine.  If any of the read_... subroutines fail
  394. # this read_objectinfo subroutine fails and returns 0.
  395. #
  396. sub read_objectinfo
  397. {
  398.     local ($obj_root, $obj_h, $obj_m) = @_;
  399.  
  400.     &dblog (0, "Reading object info for:\t$obj_root");
  401.  
  402.     init_globals($obj_root);
  403.  
  404.     # read from the header file
  405.     return 0 
  406.     if (!scan_openfile($obj_h));
  407.  
  408.     read_copyrightinfo();
  409.     read_defines_typedefs_functions();
  410.  
  411.     scan_keepADCNewlines(0);
  412.     read_objectname_inheritance();
  413.     read_instancevariables();
  414.     read_methodnames();
  415.     scan_closefile();
  416.     return 0
  417.     if (scan_eoffound());
  418.  
  419.     # return success if the .m file is "", and we found functions, etc
  420.     if (($obj_m eq "") || (object_noobjectdefined())) {  
  421.     return 1
  422.         if (scalar(@obj_typedefs) || scalar(@obj_defines) ||
  423.         scalar(@obj_macros) || scalar(@obj_functions) ||
  424.         scalar(@obj_globals));
  425.     return 0;
  426.     }
  427.  
  428.     # read from the implementation file
  429.     return 0
  430.     if (!scan_openfile($obj_m));
  431.     scan_keepADCNewlines(1);
  432.  
  433.     read_declarationlocation($obj_root);
  434.     read_classdescription();
  435.     read_methoddescriptions();
  436.     scan_closefile();
  437.     return 0
  438.     if (scan_eoffound());
  439.  
  440.     return 1;
  441. }
  442.  
  443.  
  444. #############################################################################
  445. #
  446. # NAME:       read_copyrightinfo
  447. #
  448. # ACTION:     Read from the header file until we find the object version and
  449. #           copyright year, or fail if we find "@interface" first.  The 
  450. #             argument globs are assigned scalar values for both 
  451. #
  452. # GLOBALS:    The object version and year variable globs
  453. #
  454. # RETURN:     1 if we succeded in finding the copyright info, 0 if we failed
  455. #
  456. #############################################################################
  457. sub read_copyrightinfo
  458. {
  459.     local ($in);
  460.  
  461.     return 0 if (scan_eoffound()); # don't bother if scanning won't work
  462.  
  463.     scan_skipCStyleComments(0);    # copyright info is inside c style comments
  464.  
  465.     # scan lines from the source file until an end condition is reached
  466.     while ($in = scan_line()) {
  467.  
  468.     # check for our data line
  469.     if ($in =~ m/^\$Id\:\s+\S+\s+(\S+)\s+(\d+)\//) {
  470.         $obj_version = "Version $1";
  471.         $obj_year    = $2;
  472.         dblog (2, "Found copyrightinfo:\t(", $obj_version,
  473.            ") (", $obj_year, ")");
  474.         scan_skipCStyleComments(1);
  475.         return 1;
  476.     }
  477.  
  478.     # check for our fail conditions: interface declaration
  479.     if ($in =~ m/^\@\s*interface\s+/) {
  480.         scan_rescanline(1); # this is the line needed for the next sub
  481.         dblog (2, "Can't find copyrightinfo, using defaults.");
  482.         scan_skipCStyleComments(1);
  483.         return 0;
  484.     }
  485.  
  486.     # check for our fail condition: protocol declaration
  487.     if ($in =~ m/^\@\s*protocol\s+/) {
  488.         scan_rescanline(1); # this is the line needed for the next sub
  489.         dblog (2, "Can't find copyrightinfo, using defaults.");
  490.         scan_skipCStyleComments(1);
  491.         return 0;
  492.     }
  493.  
  494.     # check for our fail conditions: defines, typedefs, ad comments...
  495.     if (parse_containsADComment($in) || 
  496.         ($in =~ m!^#\s*define|^typedef|^extern!)) {
  497.         scan_rescanline(1); # this is the line needed for the next sub
  498.         dblog (2, "Can't find copyrightinfo, using defaults.");
  499.         scan_skipCStyleComments(1);
  500.         return 0;
  501.     }
  502.     }
  503.     scan_skipCStyleComments(1);
  504.     dblog (2, "Can't find copyrightinfo, using defaults.");
  505.     return 0;
  506. }
  507.  
  508.  
  509. #############################################################################
  510. #
  511. # NAME:       read_defines_typedefs_functions
  512. #
  513. # ACTION:     Read from the header file to document defines, functions and 
  514. #           typedef's fail if we find "@interface".
  515. #
  516. # GLOBALS:    obj_functions array, obj_defines, obj_typedefs 
  517. #
  518. # RETURN:     1 if we find any defines, typedefs, or functions, 0 otherwise
  519. #
  520. #############################################################################
  521. sub read_defines_typedefs_functions
  522. {
  523.     local ($ADcomment, $findADCTarget, $targetName, $targetValue, $in);
  524.     local ($dblevel) = 2;
  525.  
  526.     return 0 if (scan_eoffound()); # don't bother if scanning won't work
  527.  
  528.     $findADCTarget  = 0;
  529.     $ADcomment      = "";
  530.     $targetName     = "";
  531.     $targetValue     = "";
  532.  
  533.   READDTF:
  534.     while ($in = scan_line()) {
  535.  
  536.     # check for our fail conditions
  537.     if (($in =~ m!^\@\s*interface\s+!)) {
  538.         scan_rescanline(1); # this is the line needed for the next sub
  539.         return 1;
  540.     }
  541.     if (($in =~ m!^\@\s*protocol\s+!)) {
  542.         scan_rescanline(1); # this is the line needed for the next sub
  543.         return 1;
  544.     }
  545.  
  546.     # check for global functions and variables (extern's)
  547.     if ($in =~ m!^extern\s+!) {
  548.         $in = $';
  549.         $in =~ s!\s+! !g;    # remove unnecessary whitespace
  550.         $in .= scan_line()    # read in 'till we find the end ';'
  551.         while (!($in =~ m!\;!) && !scan_eoffound());
  552.  
  553.         $in =~ s!\;.*!\;!;
  554.  
  555.         # determine if we have a variable or a function
  556.         if ($in =~ m!\)\s*\;!) {
  557.         # a function
  558.         $targetName = $in;
  559.         $targetName =~ s!\;!!;
  560.  
  561.         push (@obj_functions, $targetName);
  562.         if ($ADcomment ne "") {
  563.             $obj_functiondocs{$targetName} = $ADcomment;
  564.             $ADcomment = "";
  565.         }
  566.         dblog($dblevel, "Found function ($targetName)");
  567.         } else {
  568.         # a global variable
  569.         $targetName = $in;
  570.  
  571.         push (@obj_globals, $targetName);
  572.         if ($ADcomment ne "") {
  573.             $obj_globaldocs{$targetName} = $ADcomment;
  574.             $ADcomment = "";
  575.         }
  576.         dblog($dblevel, "Found global ($targetName)");
  577.         }
  578.     }
  579.  
  580.     # check for typedef's
  581.     if ($in =~ m!^typedef\s+!) {
  582.         local ($curlies, $buf) = 0;
  583.  
  584.         $targetValue = $in;
  585.         $buf = $in;
  586.         $curlies = ($buf =~ s!\{!x!g) - ($buf =~ s!\}!x!g);
  587.         while (!(($curlies == 0) && ($in =~ m!\;!)) && !scan_eoffound()) {
  588.         $in = scan_line();
  589.         $targetValue .= "\n$in";
  590.         $buf = $in;
  591.         $curlies += ($buf =~ s!\{!x!g) - ($buf =~ s!\}!x!g);
  592.         }
  593.         
  594.         $in =~ m!(\S+)\s*;$!;
  595.         $targetName = $1;
  596.         if ($targetName =~ m!\)$!) {
  597.         # Ewww.. A typedef function thing.
  598.         $targetValue =~ m!(\S+)\([^\(]*\)\;$!;
  599.         $targetName = $1;
  600.         while ($targetName =~ m!([^\(\*]+)\)$!) {
  601.             $targetName = $1;
  602.         }
  603.         }
  604.         
  605.         push (@obj_typedefs, $targetName);
  606.         $obj_typedefvals{$targetName} = $targetValue;
  607.         if ($ADcomment ne "") {
  608.         $obj_typedefdocs{$targetName} = $ADcomment;
  609.         $ADcomment = "";
  610.         }
  611.         # remove the newlines for debugging...
  612.         $targetValue =~ s!\n! !g;
  613.         dblog($dblevel, 
  614.           "Found typedef ($targetName) = \"$targetValue\"");
  615.     }
  616.  
  617.     # check for #define's
  618.     if ($in =~ m!^\#\s*define\s+([^\s\(]+)!) {
  619.         $targetName = $1;
  620.         $in = $';
  621.  
  622.         # determine if the define has parens or not
  623.         if ($in =~ m!^\(!) {
  624.         # it's a macro, get the portion between the parens
  625.         $in =~ m!^\([^\)]*\)!;
  626.         $targetName .= $&;
  627.  
  628.         while ($in =~ m!\\$!) {
  629.             # throw away the contents of a multi line macro definition
  630.             $in = scan_line();
  631.         }
  632.         push (@obj_macros, $targetName);
  633.         if ($ADcomment ne "") {
  634.             $obj_macrodocs{$targetName} = $ADcomment;
  635.             $ADcomment = "";
  636.         }
  637.         dblog($dblevel, "Found macro ($targetName)");
  638.         } else {
  639.         # it's a plain define
  640.         $in =~ m!^\s+!;
  641.         $targetValue = $';
  642.  
  643.         while ($in =~ m!\\$!) {
  644.             $in = scan_line();
  645.             $targetValue =~ s!\\!$in!;
  646.         }
  647.         $obj_definevals{$targetName} = $targetValue;
  648.         push (@obj_defines, $targetName);
  649.         if ($ADcomment ne "") {
  650.             $obj_definedocs{$targetName} = $ADcomment;
  651.             $ADcomment = "";
  652.         }
  653.         dblog($dblevel, 
  654.               "Found define ($targetName) = \"$targetValue\"");
  655.         }
  656.         next READDTF;
  657.     } 
  658.  
  659.     # check for our data line
  660.     if (($ADcomment = parse_extractADComment($in))) {
  661.         # need to determine what we attach the comment to
  662.         $findADCTarget = 1;
  663.         next READDTF;
  664.     }
  665.  
  666.     }
  667.     dblog(1, "Did not find object interface declaration.");
  668.     return 0;
  669. }
  670.  
  671.  
  672. #############################################################################
  673. #
  674. # NAME:       read_objectname_inheritance
  675. #
  676. # ACTION:     Read from the header file until we find the interface and 
  677. #             inheritance info
  678. #
  679. # GLOBALS:    obj_name, obj_inherits, obj_category scalars
  680. #
  681. # RETURN:     1 if we succeed in reading , 0 if we fail
  682. #
  683. #############################################################################
  684. sub read_objectname_inheritance
  685. {
  686.     local ($ADcomments, $in, $after);
  687.  
  688.     return 0 if (scan_eoffound()); # don't bother if scanning won't work
  689.  
  690.     $ADcomments = "";
  691.  
  692.     while ($in = scan_line()) {
  693.  
  694.     # check for our data line
  695.     if ($in =~ m/^\@\s*interface\s+([^\s\:]+)\s*\:\s*([^\s\/]+)\s*/) {
  696.       $obj_name = $1;
  697.       $obj_inherits = $2;
  698.       $after = $';
  699.         
  700.       @obj_conformsto = extract_protocols($after);
  701.       if ($extra = parse_extractADComment ($after)) {
  702.         $obj_inherits .= " : $extra";
  703.       }
  704.       dblog (2, "Found objectname_inheritance:",
  705.          "\t($obj_name) ($obj_inherits) (@obj_conformsto)");
  706.       return 1;
  707.     }
  708.  
  709.     if ($in =~ m/^\@\s*interface\s+([^\s\:]+)\s*\(\s*([^\s\)]+)\)/) {
  710.         $obj_name = $1;
  711.         $obj_category = $2;
  712.         $after = $';
  713.  
  714.         @obj_conformsto = extract_protocols($after);
  715.         dblog (2, "Found objectname_inheritance (category):",
  716.            "\t($obj_name) ($obj_category) (@obj_conformsto)");
  717.         return 1;
  718.     }
  719.  
  720.     if ($in =~ m/^\@\s*protocol\s+([^\s\{]+)\s*/) {
  721.         $obj_name = $1;
  722.         $obj_protocol = $1;
  723.         $after = $';
  724.  
  725.         @obj_conformsto = extract_protocols($after);
  726.         dblog (2, "Found objectname_inheritance protocol:",
  727.            "\t($obj_protocol)");
  728.         return 1;
  729.     }
  730.  
  731.     if ($in =~ m/^\@\s*interface\s+([^\s\{]+)\s*/) {
  732.         $obj_name = $1;
  733.         $obj_inherits = "none";
  734.         $after = $';
  735.  
  736.         @obj_conformsto = extract_protocols($after);
  737.         dblog (2, "Found objectname_inheritance:",
  738.            "\t($obj_name) ($obj_inherits) (obj_conformsto)");
  739.         return 1;
  740.     }
  741.     }
  742.     dblog (2, "Could not find objectname_inheritance.");
  743.     return 0;
  744. }
  745.  
  746.  
  747.  
  748. #############################################################################
  749. #
  750. # NAME:       read_instancevariables
  751. #
  752. # ACTION:     Reads the instance variables out of the header file and stores
  753. #             them, their types and docs
  754. #
  755. # GLOBALS:    @obj_ivars, %obj_ivartypes, %obj_ivardocs
  756. #
  757. # RETURN:     1 if we succeed in reading instance vars, 0 if we fail
  758. #
  759. #############################################################################
  760. sub read_instancevariables
  761.   {
  762.     local ($in, $clevel, $openc, $closec, $foundivars, $ivar, $ivarline, 
  763.            $docs, $type);
  764.  
  765.     return 0 
  766.       if (scan_eoffound()); # don't bother if scanning won't work
  767.     
  768.     $clevel = 0;        # keep track of the curly braces!
  769.     $foundivars = 0;
  770.     $ivarline = "";        # initialize the instance variable line buffer
  771.     
  772.   READIVARS:
  773.     while ($in = scan_line()) {
  774.       
  775.       if ($foundivars == 0) {    # Check if we have entered the ivar region
  776.         if (!($in =~ m/\{/)) {
  777.           scan_rescanline(1);
  778.           dblog (2, "No instance variables found.");
  779.           return 0;
  780.         } 
  781.         $foundivars = 1;
  782.         $clevel += 1;
  783.         
  784.         if ($in =~ m/\w/) {    
  785.           # remove the start brace and scan the line for ivars.
  786.           $in =~ s/\{\s*//;
  787.         } else {
  788.           next READIVARS;    # skip this line if no alpha on it
  789.         }
  790.       }
  791.       
  792.       # Discard any line marked w/INTERNAL
  793.       if ($in =~ m/INTERNAL/) {
  794.         dblog (2, "Discarding \"$in\" in instance variable declaration.");
  795.         next READIVARS;
  796.       }
  797.       
  798.       # preprocess to extract the autodoc comment, if any
  799.       if (($docs = parse_extractADComment($in))) {
  800.         $in = parse_extract_preADComment($in);
  801.       }
  802.       
  803.       $openc = ($in =~ m/\{/g);      # The number of open curly braces
  804.       $closec = ($in =~ m/\}/g);     # The number of close curly braces
  805.       $clevel += ($openc - $closec); # The current curly level
  806.       
  807.       if (($clevel > 1) && ($openc > 0)) {
  808.         warn ("Unable to parse struct encountered in ivars.\n");
  809.         dblog (2, "Found struct on line: ($in)");
  810.         next READIVARS;
  811.       }
  812.       if (($clevel > 0) && ($closec > 0)) {
  813.         dblog (2, "Found struct end on line: ($in)");
  814.         next READIVARS;
  815.       }
  816.       if ($clevel == 0) {    # found the end of the ivars
  817.         dblog (2, "Found ", scalar(@obj_ivars), " instance variables.");
  818.         return 1;
  819.       }
  820.       
  821.       $ivarline .= " " if ($ivarline);
  822.       $ivarline .= $in;
  823.       
  824.       # Discard @public, @private and @protected modifiers...
  825.       if ($in =~ m/\@\s*p/) {
  826.         dblog (2, "Discarding \"$in\" in instance variable declaration.");
  827.         next READIVARS;
  828.       }
  829.       
  830.       if (!($in =~ m/\;/)) {
  831.         dblog (2, "Multiline ivar declaration: $in");
  832.         next READIVARS;
  833.       }
  834.       
  835.       # partition the ivar type and name, from the documentation
  836.       $ivarline =~ m/\;/;
  837.       $ivarline = $`;
  838.       
  839.       # extract the type and name, *'s go in the type string
  840.       if ($ivarline =~ m/(^.*\s+\**)(\S+)$/) {
  841.         $type = $1;
  842.         $ivar = $2;
  843.         
  844.         if (!defined($docs) || ($docs eq "")) {
  845.           $docs = "No description.";
  846.         }
  847.         
  848.         dblog (3, "Adding ivar ($type)$ivar, \"$docs\""); 
  849.         push (@obj_ivars, $ivar);
  850.         $obj_ivartypes{$ivar} = $type;
  851.         $obj_ivardocs{$ivar} = $docs;
  852.       } else {
  853.         warn ("Unable to extract ivar name and type.\n");
  854.       }
  855.       
  856.       $ivarline = "";
  857.     }
  858.     return 0;
  859.   }
  860.  
  861.  
  862. #############################################################################
  863. #
  864. # NAME:       read_methodnames
  865. #
  866. # ACTION:     Read the method declarations and block names into the globals
  867. #
  868. # GLOBALS:    @obj_methodnames, @obj_classmethods, @obj_isntmethods, 
  869. #             %obj_methodblocks
  870. #
  871. # RETURN:     1 if we succeed in reading methods and block names, 0 if we fail
  872. #
  873. #############################################################################
  874. sub read_methodnames
  875. {
  876.     local ($meth, $methname, $methblock, $methline, $in);
  877.  
  878.     return 0 if (scan_eoffound()); # don't bother if scanning won't work
  879.  
  880.     $methline = "";        # initialize the method line buffer
  881.  
  882.   READMETHODS:
  883.     while ($in = scan_line()) {
  884.     # detect the end of the methods list by finding an @end
  885.     if ($in =~ m/\@end\s*$/) {
  886.         # end of methods list reached
  887.         dblog (2, "Found ", scalar(@obj_classmethods), " class methods, ", 
  888.            scalar (@obj_instmethods), " instance methods, and ",
  889.            scalar(keys(%obj_methodblocks)), " method blocks.");
  890.         return 1;
  891.     }
  892.  
  893.     if (parse_containsADComment($in)) {
  894.         $methblock = parse_extractADComment ($in);
  895.         next READMETHODS;
  896.     }
  897.  
  898.     $methline .= $in;
  899.     if (!($in =~ m/\;/)) {
  900.         dblog (3, "Multiline method declaration: $methline");
  901.         $methline .= " ";
  902.         next READMETHODS;
  903.     }
  904.  
  905.     if ($methline =~ m/([\-\+])\s*(.*);/) {
  906.         $meth = $2;
  907.         $methname = $1;        # put the + or - in the methname
  908.         $methname .= &extract_methodname($meth);
  909.         
  910.         if ($1 eq "-") {
  911.         push (@obj_instmethods, $meth);
  912.         } else {
  913.         push (@obj_classmethods, $meth);
  914.         }
  915.  
  916.         push (@obj_methodnames, $methname);
  917.         dblog (3, "Adding method ($methname)\t($meth)");
  918.         
  919.         # if the preceeding line had a method block name comment
  920.         if (defined($methblock) && ($methblock ne "")) {
  921.         $obj_methodblocks{$methname} = $methblock;
  922.         dblog (3, "Assigned ($methname) to block: \"$methblock\"");
  923.         $methblock = "";
  924.         }
  925.     } else {
  926.         warn ("Unable to parse method declaration: $methline");
  927.     }
  928.  
  929.     $methline = "";
  930.     }
  931.     return 0;
  932. }
  933.  
  934.  
  935.  
  936. #
  937. # Search for methods, look them up and associate their descriptions with 
  938. # the method name
  939. #
  940. sub read_methoddescriptions
  941. {
  942.     local ($meth, $methname, $in, $foundmethod);
  943.  
  944.     return 0 if (scan_eoffound()); # don't bother if scanning won't work
  945.  
  946.     $foundmethod = 0;
  947.  
  948.   READMETHDOCS:
  949.     while ($in = scan_line()) {
  950.  
  951.     # look for the end marker
  952.     if ($in =~ m/\@end\s*$/) {    
  953.         dblog (2, "Found ", scalar (keys (%obj_methoddocs)), 
  954.            " method descriptions.");
  955.         return 1;
  956.     }
  957.  
  958.     # If we have documentation for the current method, assign it
  959.     if ($foundmethod) {
  960.  
  961.         if (parse_containsADComment($in)) {
  962.         $methname .= extract_methodname($meth);
  963.         $obj_methoddocs{$methname} = parse_extractADComment($in);
  964.         dblog (3, "Adding documentation for ($methname): \"",
  965.                $obj_methoddocs{$methname}, "\"");
  966.         } else {
  967.         # if we didn't find a description, maybe it's multi-line
  968.         if (!($in =~ m/\{/)) {
  969.             dblog (2, "Multiline method name: $meth");
  970.             $meth .= " $in";
  971.             next READMETHDOCS;
  972.         } else {
  973.             # nope, it's missing a description...
  974.             $methname .= extract_methodname($meth);
  975.             $obj_methoddocs{$methname} = "No method description.";
  976.             dblog (3, "Adding documentation for ($methname): \"",
  977.                $obj_methoddocs{$methname}, "\"");
  978.         }
  979.         }
  980.         $foundmethod = 0;
  981.         $meth = "";
  982.         $methname = "";
  983.         next READMETHDOCS;
  984.     }
  985.     
  986.     # look for a method to assign documentation to 
  987.     if ($in =~ m/^([\+\-])\s*(.*)/) {
  988.         $meth = $2;        # the meth may be multiline, so don't
  989.                 # generate the methname until we're sure
  990.         $methname = $1;    # grab the + or - for the methname
  991.         $foundmethod = 1;
  992.     }
  993.     }
  994. }
  995.  
  996.  
  997. #
  998. # Read in the class description, found immmediately after the @implementation
  999. #
  1000. sub read_classdescription
  1001. {
  1002.     local ($iscategory);
  1003.     local ($in, $foundimp);
  1004.  
  1005.     return 0 if (scan_eoffound()); # don't bother if scanning won't work
  1006.  
  1007.     $iscategory = ($obj_category ne "");
  1008.  
  1009.     # Default description values for classes or categories
  1010.     $obj_classdocs = "No class description." if (!$iscategory);
  1011.     $obj_classdocs = "No category description." if ($iscategory);
  1012.     $foundimp = 0;
  1013.  
  1014.     while ($in = scan_line()) {
  1015.  
  1016.     if ($foundimp) {
  1017.         unless (parse_containsADComment($in)) {
  1018.         scan_rescanline(1);
  1019.         dblog (2, "Could not find class description.");
  1020.         return 0;
  1021.         }
  1022.         $obj_classdocs = parse_extractADComment ($in);
  1023.         dblog (2, "Found class description.");
  1024.         dblog (3, "\t\t\"$obj_classdocs\"");
  1025.         return 1;
  1026.     }
  1027.  
  1028.     # check for our data line
  1029.     if ($iscategory) {
  1030.         if ($in =~ m/^\@implementation/) {
  1031.         $foundimp = 1;
  1032.         }
  1033.     } else {
  1034.         if ($in =~ m/^\@implementation\s+$obj_name/) {
  1035.         $foundimp = 1;
  1036.         }
  1037.     }
  1038.     }
  1039.     return 0;
  1040. }
  1041.  
  1042.  
  1043. #
  1044. # Read in the method declaration location
  1045. #
  1046. sub read_declarationlocation
  1047. {
  1048.     local ($obj_root) = @_;
  1049.     local ($in, $header);
  1050.     
  1051.     return 0 if (scan_eoffound()); # don't bother if scanning won't work
  1052.  
  1053.     $header = "${obj_name}.h";
  1054.  
  1055.     while ($in = scan_line()) {
  1056.  
  1057.     # check for our data line
  1058.     if (($in =~ m/$header/) && (($in =~ m/\#\s*imp/) || 
  1059.                     ($in =~ m/\#\s*incl/))) {
  1060.         $in =~ m/[\<\"](.*$header)[\>\"]/;
  1061.         $obj_declaredin = $1;
  1062.         dblog (2, "Found header declaration location: ($obj_declaredin)");
  1063.         return 1;
  1064.     }
  1065.  
  1066.     # check for our fail condition
  1067.     if ($in =~ m/^\@implementation/) {
  1068.         scan_rescanline(1); # this is the line needed for the next sub
  1069.         dblog (2, "Can't find declaration location, using defaults.");
  1070.         return 0;
  1071.     }
  1072.     }
  1073.     dblog (2, "Can't find declaration location, using defaults.");
  1074.     return 0;
  1075. }
  1076.  
  1077. #
  1078. # turn the full method declaration to a method name suitable for hash table
  1079. # lookups for associated information
  1080. #
  1081. sub extract_methodname
  1082. {
  1083.     local ($methodname) = $_[0];
  1084.     
  1085.     # remove any semi-colons that might have snuck onto the end
  1086.     $methodname =~ s/;//g; 
  1087.     
  1088.     # remove (x) delimited type-casting blocks
  1089.     $methodname =~ s/\([^\)]*\)//g; 
  1090.     
  1091.     # remove :x argument names (leave the :, kill the name and whitespace)
  1092.     $methodname =~ s/\:\s*\S*\s*/:/g;
  1093.     
  1094.     # remove ALL whitespace from the methname
  1095.     $methodname =~ s/\s+//g;
  1096.     
  1097.     return $methodname;
  1098. }
  1099.  
  1100.  
  1101. #
  1102. # parse out protocols which appear as '<proto1, proto2>' in the line passed to
  1103. # this function, and return them as an array.  If no protocol's are referenced
  1104. # this sub returns an empty array.
  1105. #
  1106. sub extract_protocols
  1107. {
  1108.   local ($input) = $_[0];
  1109.   local (@protocols) = ();
  1110.   local ($match);
  1111.  
  1112.   # check for the protocol delimeters: <>
  1113.   if ($input =~ m!<([^>]+)>!) {
  1114.     $match = $1;
  1115.     $match =~ s!\s+!!g;
  1116.    
  1117.     # see if we have a single protocol, or a list
  1118.     if ($match =~ m!,!) {
  1119.       @protocols = split (m!,!, $match);
  1120.     } else {
  1121.       $protocols[0] = $match;
  1122.     }
  1123.   }
  1124.  
  1125.   # return the (possible empty) array
  1126.   return @protocols;
  1127. }
  1128.  
  1129.  
  1130. 1;
  1131.