#! /usr/bin/perl -w # # Module name: BlockParse # Synopsis: Block parser code # # Last Updated: $Date: 2012/02/28 15:37:59 $ # # Copyright (c) 1999-2004 Apple Computer, Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # # This file contains Original Code and/or Modifications of Original Code # as defined in and that are subject to the Apple Public Source License # Version 2.0 (the 'License'). You may not use this file except in # compliance with the License. Please obtain a copy of the License at # http://www.opensource.apple.com/apsl/ and read it before using this # file. # # The Original Code and all software distributed under the License are # distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER # EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, # INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. # Please see the License for the specific language governing rights and # limitations under the License. # # @APPLE_LICENSE_HEADER_END@ # ###################################################################### # /*! @header # @abstract # PythonParse package file. # @discussion # This file contains the PythonParse package, a collection # of functions for parsing Python declarations. # # For details, see the package documentation below. # @indexgroup HeaderDoc Parser Pieces # */ # /*! # @abstract # Internal parser routines for parsing Python code. # @discussion # The PythonParse package parses a Python declaration. It is # essentially a language-specific replacement for parts of the # {@link //apple_ref/perl/cl/HeaderDoc::BlockParse BlockParse} # class. # # In general, you should not call routines in this package # directly. Instead, call the appropriate routines in the # {@link //apple_ref/perl/cl/HeaderDoc::BlockParse BlockParse} # package with the appropriate language values and let them # call these routines for you. # */ package HeaderDoc::PythonParse; BEGIN { foreach (qw(Mac::Files)) { $MOD_AVAIL{$_} = eval "use $_; 1"; } } use Carp qw(cluck); use HeaderDoc::TypeHelper; use Exporter; foreach (qw(Mac::Files Mac::MoreFiles)) { eval "use $_"; } # $HeaderDoc::disable_parms = 0; @ISA = qw(Exporter); @EXPORT = qw(pythonParse); use HeaderDoc::Utilities qw(findRelativePath safeName printArray printHash parseTokens isKeyword classTypeFromFieldAndBPinfo casecmp addAvailabilityMacro printFields); use strict; use vars qw($VERSION @ISA); use File::Basename qw(basename); # /*! # @abstract # The revision control revision number for this module. # @discussion # In the git repository, contains the number of seconds since # January 1, 1970. # */ $HeaderDoc::PythonParse::VERSION = '$Revision: 1330472279 $'; ################ Portability ################################### my $isMacOS; my $pathSeparator; if ($^O =~ /MacOS/io) { $pathSeparator = ":"; $isMacOS = 1; } else { $pathSeparator = "/"; $isMacOS = 0; } $HeaderDoc::inputCounterDebug = 0; # /*! @abstract # The number of spaces that a tab should be replaced with # when parsing Python. # @discussion # This value can be changed with the -w flag on # the command line. # */ $HeaderDoc::python_tab_spaces = 8; my $test_pyspace = 0; ################ Code ################################### # /*! # @abstract # Converts tabs to spaces and calculates indentation. # @param string # The string of spaces. # @discussion # Converts tabs to spaces using the variable # */ sub pylength { my $str = shift; # print "SPACES: $HeaderDoc::python_tab_spaces\n"; if ($str =~ /\t/) { my $pos = 0; my $newstr; my @parts = split(/(\t)/, $str); foreach my $part (@parts) { if ($part =~ /\t/) { my $nspaces = $HeaderDoc::python_tab_spaces - ($pos % $HeaderDoc::python_tab_spaces); my $pyspace = " " x $nspaces; $newstr .= $pyspace; $pos += $nspaces; } else { $newstr .= $part; $pos += length($part); } } $str = $newstr; } return length($str); } # /*! # @abstract # Runs a single python space handling test. # @param testnum # The test number (string). # @param test # The string of spaces and tabs to test. # @param testresult # The expected test result. # @result # Returns 1 for a successful test, 0 for a failed test. # */ sub pySpaceTest { my $testnum = shift; my $test = shift; my $testresult = shift; my $temp = pylength($test); if ($temp != $testresult) { print STDERR "Python space test $testnum: \e[31mFAILED\e[39m\nGot $temp, expected $testresult.\n"; return 0; } else { print STDERR "Python space test $testnum: \e[32mOK\e[39m\n"; return 1; } } # /*! # @abstract # Runs a series of tests on Python tab-to-space conversion. # @result # Returns an array containing the number of successes and failures. # */ sub runPythonSpaceTests { my $tmp = $HeaderDoc::python_tab_spaces; print STDERR "\n-= Running Python space tests =-\n\n"; $HeaderDoc::python_tab_spaces = 8; my $pass_count = 0; $pass_count += pySpaceTest(1, " \t", 8); # 8 $pass_count += pySpaceTest(2, " \t", 8); # 8 $pass_count += pySpaceTest(3, "\t", 8); # 8 $pass_count += pySpaceTest(4, "\t ", 9); # 9 $pass_count += pySpaceTest(5, " \t \t", 16); # 16 $HeaderDoc::python_tab_spaces = 4; $pass_count += pySpaceTest("1a", " \t", 4); # 8 $pass_count += pySpaceTest("2a", " \t", 8); # 8 $pass_count += pySpaceTest("3a", "\t", 4); # 8 $pass_count += pySpaceTest("4a", "\t ", 5); # 9 $pass_count += pySpaceTest("5a", " \t \t", 8); # 16 $HeaderDoc::python_tab_spaces = $tmp; print STDERR "\n"; return ($pass_count, 10-$pass_count); # Change if we add more tests. } # /*! # @abstract # Parses a Python declaration. # @discussion # Python is a painful language to parse because it has no # tokens to end most block structures (except, thankfully, # for multi-line comments, without which supporting it # would be almost impossible). Merging this into the main # parser would be way too messy, so its parser lives in a # separate block of code. # # This function is called at the start of {@link blockParse} and # replaces its functionality. Upon completion, it passes its # state to {@link blockParseReturnState} just like the main parser # does, then returns the result. # # @param fullpath # The path to the file being parsed. # @param fileoffset # The line number where the current block begins. The line number # printed is (fileoffset + inputCounter). # @param inputLinesRef # A reference to an array of code lines. # @param inputCounter # The offset within the array. This is added to fileoffset when # printing the line number. # @param argparse # Disable warnings when parsing arguments to avoid seeing them twice. # @param ignoreref # A reference to a hash of tokens to ignore on all headers. # @param perheaderignoreref # A reference to a hash of tokens, generated from \@ignore # headerdoc comments. # @param perheaderignorefuncmacrosref # A reference to a hash of tokens, generated from # \@ignorefunmacro headerdoc comments. # @param keywordhashref # A reference to a hash of keywords. # @param case_sensitive # Boolean value that controls whether keywords should be processed # in a case-sensitive fashion. # @result # Returns the array ($inputCounter, $declaration, $typelist, $namelist, $posstypes, $value, \@pplStack, $returntype, $privateDeclaration, $treeTop, $simpleTDcontents, $availability) to the caller. # # @vargroup State variables # # @var continue # Indicates that parsing should continue. Upon receiving a terminating token, # this gets set to zero, and parsing ends at the end of the line. # @var parserState # The {@link //apple_ref/perl/cl/HeaderDoc::ParserState ParserState} # object used for storing most of the parser state variables. # # @vargroup Token and line variables # # @var line # The (input) line being parsed. # @var part # The current token being processed (from curline). # @var nextpart # The token after the token being processed (from line). # @var treepart # In some cases, it is necessary to drop a token for formatting purposes but keep it in # the parse tree. When this is needed, the treepart variable contains # the original token, and the part variable contains a placeholder value # (generally a space). # # @vargroup Parse tree nodes # # @var treeTop # The top of the current parse tree. # @var treeCur # The current position in the parse tree. # */ sub pythonParse { my $fullpath = shift; my $fileoffset = shift; my $inputLinesRef = shift; my $inputCounter = shift; my $argparse = shift; my $ignoreref = shift; my $perheaderignoreref = shift; my $perheaderignorefuncmacrosref = shift; my $keywordhashref = shift; my $case_sensitive = shift; my $lang = shift; my $sublang = shift; my $parseDebug = 0; my $liteDebug = 0; my $parmDebug = 0; my $nameDebug = 0; my $stateDebug = 0; my $spaceDebug = 0; my $stackDebug = 0; my $inputCounterDebug = 0; my $anyDebug = ($parseDebug || $liteDebug || $parmDebug || $nameDebug || $stateDebug || $spaceDebug || $stackDebug || $inputCounterDebug); # print STDERR "Test\n"; my $treeTop = HeaderDoc::ParseTree->new(); my $treeCur = $treeTop; my @parserStack = (); my $parserState = HeaderDoc::ParserState->new( "FULLPATH" => $fullpath, "lang" => $lang, "sublang" => $sublang ); # $parserState->{hollow} = $treeTop; # $parserState->setHollowWithLineNumbers($treeCur, $fileoffset, $inputCounter); $parserState->{lang} = $lang; $parserState->{inputCounter} = $inputCounter; $parserState->{initbsCount} = 0; # included for consistency.... my $retDebug = 0; my @inputLines = @{$inputLinesRef}; # Get the parse tokens from Utilities.pm. # my ($sotemplate, $eotemplate, $operator, $soc, $eoc, $ilc, $ilc_b, $sofunction, # $soprocedure, $sopreproc, $lbrace, $rbrace, $unionname, $structname, # $enumname, # $typedefname, $varname, $constname, $structisbrace, $macronameref, # $classregexp, $classbraceregexp, $classclosebraceregexp, $accessregexp, # $requiredregexp, $propname, $objcdynamicname, $objcsynthesizename, $moduleregexp, $definename, # $functionisbrace, $classisbrace, $lbraceconditionalre, $lbraceunconditionalre, $assignmentwithcolon, # $labelregexp, $parmswithcurlybraces, $superclasseswithcurlybraces, $soconstructor) = parseTokens($lang, $sublang); my %parseTokens = %{parseTokens($lang, $sublang)}; # $parserState->{sodname} = "foo"; # $parserState->{sodtype} = ""; # $parserState->{sodclass} = "function"; # $treeTop->token("test"); my $continue = 1; my $nlines = $#inputLines; while ($continue && ($inputCounter <= $nlines)) { my $line = $inputLines[$inputCounter]; print STDERR "LINE IS $line\n" if ($parseDebug); # The tokenizer my @parts = split(/("|'|\n|\r|[ \t]+|\=\=|\!headerdoc\!|\!\=|\<\=|\>\=|\+\=|\-\=|\<\<|\>\>|\W)/, $line); push(@parts, "BOGUSBOGUSBOGUS"); $parserState->{lastpart} = ""; my $part = ""; foreach my $nextpart (@parts) { REDO: if ($stackDebug) { print STDERR "BEGIN STACK DUMP\n"; foreach my $token (@{$parserState->{braceStack}}) { print "ITEM: $token\n"; } print STDERR "END STACK DUMP\n"; } if (!length($part)) { # print STDERR "SKIP TO \"$nextpart\"\n"; $part = $nextpart; next; } print STDERR "PART IS \"$part\"\n" if ($parseDebug || $liteDebug); # The version of the part to insert into the tree (if different); my $treepart = $part; if ($part =~ /\s/ && $part !~ /[\r\n]/) { print STDERR "WSATTOP\n" if ($parseDebug || $spaceDebug); if ($parserState->{lastpart} =~ /[\n\r]/ || $parserState->{lastpart} eq "") { if (!$parserState->isContinuationLine()) { # print STDERR "CMP ".pylength($part)." TO ".$parserState->{leadspace}."\n"; if ($parserState->{leadspace} == -1) { if ($nextpart !~ /[\n\r]/) { $parserState->{leadspace} = pylength($part); $parserState->{setleading} = 1; print STDERR "SETLEADING -> 1[1]\n" if ($parseDebug || $stateDebug); print STDERR "leadspace -> ".$parserState->{leadspace}."\n" if ($parseDebug || $stateDebug || $spaceDebug); $treepart = ""; } else { print STDERR "Ignoring leading space for blank line.\n" if ($spaceDebug); } } else { print STDERR "Not setting leading space because it is already set (".$parserState->{leadspace}.").\n" if ($spaceDebug); } if ((!$parserState->{seenToken}) && (!$parserState->{seenLeading})) { $parserState->{seenLeading} = pylength($part); } } else { print STDERR "Ignoring leading whitespace for continuation line.\n" if ($parseDebug || $spaceDebug); print STDERR "LEADSPACE: ".$parserState->{leadspace}."\n" if ($spaceDebug); } } else { print STDERR "Not setting leading whitespace because this is not the first whitespace\n"."on the line (lastpart is ".$parserState->{lastpart}.").\n" if ($spaceDebug); } } elsif ($part =~ /[\r\n]/) { print STDERR "NLATTOP\n" if ($parseDebug); $parserState->{seenLeading} = 0; if ($parserState->{seenToken}) { $parserState->{setleading} = 0; print STDERR "SETLEADING -> 0\n" if ($parseDebug || $stateDebug); } $parserState->{seenToken} = 0; } else { print STDERR "TEXTATTOP\n" if ($parseDebug); if ($parserState->{leadspace} == -1) { $parserState->{leadspace} = 0; # pylength($part); $parserState->{setleading} = 1; print STDERR "SETLEADING -> 1[1]\n" if ($parseDebug || $stateDebug); print STDERR "leadspace -> ".$parserState->{leadspace}."\n" if ($parseDebug || $stateDebug); } if (!$parserState->{seenLeading}) { $parserState->{seenLeading} = 0; }; print STDERR "CMP: ".$parserState->{seenLeading}." TO ".$parserState->{leadspace}."\n" if ($parseDebug || $spaceDebug); if ((!$parserState->isContinuationLine()) && (!$parserState->{setleading}) && ($parserState->{seenLeading} <= $parserState->{parentLeading}) && (!$parserState->{onlyComments})) { if (!$parserState->{seenToken}) { $parserState->{endgame} = 3; print STDERR "ENDGAME -> 3[TAT-LTPARENTLEAD]\n" if ($parseDebug || $stateDebug); } else { print STDERR "ENDGAME NOT SET (ST: ".$parserState->{seenToken}.", OC: ".$parserState->{onlyComments}.") [TAT-LTPARENTLEAD]\n" if ($parseDebug); } } elsif ((!$parserState->isContinuationLine()) && (!$parserState->{setleading}) && ($parserState->{seenLeading} <= $parserState->{leadspace}) && (!$parserState->{onlyComments})) { if (!$parserState->{seenToken}) { $parserState->{endgame} = 2; print STDERR "ENDGAME -> 2[TAT-LTLEAD]\n" if ($parseDebug || $stateDebug); } else { print STDERR "ENDGAME NOT SET (ST: ".$parserState->{seenToken}.", OC: ".$parserState->{onlyComments}.") [TAT-LTPARENTLEAD]\n" if ($parseDebug); } } elsif (!$parserState->{seenToken}) { if ($parserState->{leadspace}) { my $temp = ' ' x $parserState->{leadspace}; $treeCur = $treeCur->addSibling($temp, 0); } } if (($parserState->{leadspace} == -1) && (!$parserState->{seenLeading})) { $parserState->{leadspace} = 0; $parserState->{setleading} = 1; print STDERR "SETLEADING -> 1[2]\n" if ($parseDebug || $stateDebug); print STDERR "leadspace -> ".$parserState->{leadspace}."\n" if ($parseDebug || $stateDebug); } $parserState->{seenToken} = 1; } print STDERR "POSTCMP: ".$parserState->{seenLeading}." TO ".$parserState->{leadspace}."\n" if ($parseDebug); if (($parserState->{endgame} != 2) && ($parserState->{endgame} != 3)) { SWITCH: { ($parserState->{endgame} == 1 && $part =~ /\S/) && do { if ($parserState->{onlyComments}) { print STDERR "Only comments, so not setting endgame\n" if ($parseDebug); $parserState->{endgame} = 0; print STDERR "ENDGAME -> 0[ONLYCOMMENTS]\n" if ($parseDebug || $stateDebug); } else { $parserState->{endgame} = 2; print STDERR "ENDGAME -> 2[WAS1]\n" if ($parseDebug || $stateDebug); $parserState->{popAfter} = 1; last SWITCH; } # Fall through to next matching case; }; ($part eq "=") && do { if (!($parserState->{inComment} || $parserState->{inChar} || $parserState->{inInlineComment} || $parserState->{inString})) { $parserState->{valuepending} = 1; } }; ($part eq ":") && do { if ($parserState->{bracePending}) { print STDERR "BRACE IS PENDING\n" if ($parseDebug); $parserState->{nestAfter} = 1; $parserState->{popAtEnd} = 1; if (($parserState->{pushParserStateOnBrace} == 1)) { $parserState->{pushParserStateOnBrace} = 2; } if ($parserState->{autoContinue} == 1) { $parserState->{seenBraces} = 1; } } }; ($part =~ /[\n\r]/) && do { print STDERR "newline\n" if ($parseDebug); if ($parserState->isQuoted($lang, $sublang)) { $parserState->{lastNLWasQuoted} = 1; print STDERR "lastNLWasQuoted -> 1\n" if ($spaceDebug || $stateDebug); } else { print STDERR "MAYBE ENDGAME: AC IS ".$parserState->{autoContinue}."\n" if ($parseDebug); $parserState->{lastNLWasQuoted} = 0; print STDERR "lastNLWasQuoted -> 0\n" if ($spaceDebug || $stateDebug); if ((!$parserState->{autoContinue}) && (!$parserState->{onlyComments})) { print STDERR "ENDGAME\n" if ($parseDebug); $parserState->{endgame} = 1; print STDERR "ENDGAME -> 1[NEWLINE]\n" if ($parseDebug || $stateDebug); } } last SWITCH; }; ($part =~ /\s/) && do { print STDERR "whitespace\n" if ($parseDebug); last SWITCH; }; (($part eq "[") || ($part eq "(")) && do { print STDERR "OPAREN OR LEFT BRACKET: $part\n" if ($parseDebug); if (!($parserState->{inComment} || $parserState->{inChar} || $parserState->{inInlineComment} || $parserState->{inString})) { $parserState->pushBrace($part); $parserState->{nestAfter} = 1; if ($part eq "(") { $parserState->{parsedParamParse} = 1; } } last SWITCH; }; (($part eq ")") || ($part eq "]")) && do { print STDERR "CPAREN OR RIGHT BRACKET: $part\n" if ($parseDebug); if (!($parserState->{inComment} || $parserState->{inChar} || $parserState->{inInlineComment} || $parserState->{inString})) { if ($part ne $parserState->peekBraceMatch()) { warn("Braces do not match. We may have a problem.\n"); } else { my $junk = $parserState->popBrace(); } $parserState->{popAfter} = 1; if ($part eq ")") { $parserState->{parsedParamParse} = 3; } } last SWITCH; }; ($part eq "\"" && (!$parserState->isQuoted($lang, $sublang))) && do { print STDERR "DOUBLE QUOTE\n" if ($parseDebug); if (!($parserState->{inComment} || $parserState->{inChar} || $parserState->{inInlineComment})) { print STDERR "IS: ".$parserState->{inString}."\n" if ($parseDebug); if ($parserState->{inString} == 1) { if ($parserState->{lastpart} eq "\"") { print STDERR "SET JLST: $treeCur\n" if ($parseDebug); $parserState->{justLeftStringToken} = 1; } $parserState->{inString} = 0; $parserState->{popAfter} = 1; } elsif ($parserState->{inString} == 2) { print STDERR "EOTQ: ".$parserState->{endOfTripleQuote}." EG: ".$parserState->{endgame}."\n" if ($parseDebug); if ($parserState->{endOfTripleQuote} == 2) { $parserState->{endOfTripleQuote} = 0; $parserState->{inString} = 0; print STDERR "$parserState"."->{endOfTripleQuoteToken} WAS ".$parserState->{endOfTripleQuoteToken}."\n" if ($parseDebug); # wipe out the previous two quote marks and merge them. my $firstquote = $parserState->{endOfTripleQuoteToken}; bless($firstquote, "HeaderDoc::ParseTree"); $firstquote->token(""); $firstquote->next()->token(""); $treepart = "\"\"\""; if ($parserState->popBrace() ne "\"\"\"") { die("Top of brace stack not \"\"\" as expected.\n"); } $parserState->{popAfter} = 1; } else { if ($parserState->{endOfTripleQuote} == 1) { # treeCur contains the first of a possible # triple while leaving. Store it for later. $parserState->{endOfTripleQuoteToken} = $treeCur; print STDERR "SET $parserState"."->{endOfTripleQuoteToken} = $treeCur\n" if ($parseDebug); } $parserState->{endOfTripleQuote} = $parserState->{endOfTripleQuote} + 1; } } elsif ($parserState->{justLeftStringToken}) { $parserState->{inString} = 2; # triple quoted string # wipe out the previous two quote marks and merge them. print STDERR "IN TQUO\n" if ($parseDebug); $treepart = "\"\"\""; $treeCur->dbprint() if ($parseDebug); $treeCur->token($treepart); $treeCur->firstchild()->next()->token(""); $treeCur->firstchild()->next(undef); $parserState->pushBrace($treepart); $parserState->{nestAfter} = 2; $treepart = ""; print STDERR "NA: ".$parserState->{nestAfter}."\n" if ($parseDebug); print STDERR "CHECK: ".$parserState->{endgame}."\n" if ($parseDebug); } else { $parserState->{nestAfter} = 1; $parserState->{inString} = 1; } } last SWITCH; }; ($part eq "\\") && do { $parserState->addBackslash(); }; ($part eq "'" && (!$parserState->isQuoted($lang, $sublang))) && do { print STDERR "SINGLE QUOTE\n" if ($parseDebug); if (!($parserState->{inComment} || $parserState->{inString} || $parserState->{inInlineComment})) { if ($parserState->{inChar}) { $parserState->{inChar} = 0; $parserState->{popAfter} = 1; } else { $parserState->{inChar} = 1; $parserState->{nestAfter} = 1; } } last SWITCH; }; ($part =~ /\s/) && do { print STDERR "whitespace\n" if ($parseDebug); last SWITCH; }; ($part =~ $parseTokens{classregexp}) && do { if (!($parserState->{inComment} || $parserState->{inString} || $parserState->{inChar} || $parserState->{inInlineComment})) { $parserState->{sodclass} = "class"; $parserState->{inClass} = 1; $parserState->{classtype} = "class"; $parserState->{namepending} = 1; $parserState->{bracePending} = 1; # $parserState->pushBrace($part); $parserState->{autoContinue}++; $parserState->{pushParserStateOnBrace} = 1; } last SWITCH; }; ($part eq $parseTokens{sofunction}) && do { if (!($parserState->{inComment} || $parserState->{inString} || $parserState->{inChar} || $parserState->{inInlineComment})) { $parserState->{sodclass} = "function"; $parserState->{namepending} = 1; $parserState->{bracePending} = 1; # $parserState->pushBrace($part); $parserState->{autoContinue}++; } last SWITCH; }; { print STDERR "NP: ".$parserState->{namepending}." PART \"$part\"\n" if ($parseDebug || $nameDebug); if (($parserState->{namepending}) && ((!($parserState->{inString} || $parserState->{inComment} || $parserState->{inChar} || $parserState->{inInlineComment})) && ($part =~ /\w/))) { print STDERR "NAME CHANGED TO $part\n" if ($parseDebug || $nameDebug); $parserState->{sodname} = $part; $parserState->{namepending} = 0; if (!$parserState->{sodclass}) { $parserState->{sodclass} = "variable"; } } print STDERR "default\n" if ($parseDebug); }; } } if ($parserState->{parsedParamParse} == 1) { print "PPL -> 2\n" if ($parmDebug); $parserState->{parsedParamParse} = 2; } elsif ($parserState->{parsedParamParse}) { if (($part eq ",") || ($parserState->{parsedParamParse} == 3)) { if ($parserState->{parsedParamParse} == 3) { $parserState->{parsedParamParse} = 0; } print STDERR "Pushing ".$parserState->{parsedParam}." onto parsed parameters list.\n" if ($parmDebug); push(@{$parserState->{parsedParamList}}, $parserState->{parsedParam}); $parserState->{parsedParam} = ""; } else { $parserState->{parsedParam} .= $part; } } if ((!($parserState->{inString} || $parserState->{inComment} || $parserState->{inChar} || $parserState->{inInlineComment})) && ($part =~ /\w/)) { if ($parserState->{onlyComments}) { $parserState->{setHollowAfter} = 1; # $parserState->setHollowWithLineNumbers($treeCur, $fileoffset, $inputCounter); print STDERR "ONLYCOMMENTS -> 0\n" if ($parseDebug || $stateDebug); $parserState->{onlyComments} = 0; } } if (($parserState->{endgame} != 2) && ($parserState->{endgame} != 3)) { if ($parserState->{valuepending} == 1) { # Skip the "=" part. $parserState->{valuepending} = 2; $parserState->{preEqualsSymbol} = $parserState->{sodname}; } elsif ($parserState->{valuepending} == 2) { $parserState->{value} .= $part; } if ($part ne "\"") { $parserState->{justLeftStringToken} = 0; $parserState->{endOfTripleQuote} = 0; } if ($part ne "\\" && $part =~ /\S/) { $parserState->resetBackslash(); } if ($treepart ne "") { print STDERR "ADDED PART AS SIBLING OF $treeCur: " if ($parseDebug); $treeCur = $treeCur->addSibling($treepart, $parserState->{seenBraces}); print STDERR "$treeCur\n" if ($parseDebug); } else { print STDERR "TREEPART EMPTY. NOT ADDING\n" if ($parseDebug); } } else { print STDERR "ENDGAME == ".$parserState->{endgame}.". NOT ADDING\n" if ($parseDebug); } if (($parserState->{nestAfter}) && (!$parserState->{endgame})) { print STDERR "TREEPUSH\n" if ($parseDebug); $parserState->treePush($treeCur); if ($parserState->{nestAfter} == 2) { print STDERR "TREENESTLITE\n" if ($parseDebug); $treeCur = $treeCur->firstchild(); } else { print STDERR "TREENEST\n" if ($parseDebug); $treeCur = $treeCur->addChild("", $parserState->{seenBraces}); } $parserState->{nestAfter} = 0; } elsif ($parserState->{popAfter}) { $parserState->{popAfter} = 0; print STDERR "TREEPOP\n" if ($parseDebug); $treeCur = $parserState->treePop() || $treeCur; print STDERR "TREECUR: \"$treeCur\"\n" if ($parseDebug); } if ($parserState->{pushParserStateOnBrace} == 2) { $parserState->{pushParserStateOnBrace} = 3; print STDERR "ENDCMP: ".$parserState->{seenLeading}." TO ".$parserState->{leadspace}."\n" if ($parseDebug); push(@parserStack, $parserState); my $leading = $parserState->{seenLeading}; my $parentLeading = $parserState->{leadspace}; print STDERR "NEW PARSER STATE (PPSOB)\n" if ($parseDebug || $stateDebug); $parserState = HeaderDoc::ParserState->new( "FULLPATH" => $fullpath, "lang" => $lang, "sublang" => $sublang ); # $parserState->setHollowWithLineNumbers($treeCur, $fileoffset, $inputCounter); $parserState->{lang} = $lang; $parserState->{inputCounter} = $inputCounter; $parserState->{initbsCount} = 0; # included for consistency.... $parserState->{parentLeading} = $parentLeading || 0; $parserState->{leadspace} = -1; print STDERR "PARENTLEADING -> ".$parserState->{parentLeading}."\n" if ($parseDebug || $stateDebug); # In the brace case, we haven't gotten the leading space yet. # if ($leading ne "" && $leading != -1) { # print STDERR "SETTING LEAD SPACE TO \"$leading\"\n" if ($parseDebug); # $parserState->{seenLeading} = $leading; # $parserState->{leadspace} = $leading; # } } if ($parserState->{setHollowAfter}) { $parserState->{setHollowAfter} = 0; $parserState->setHollowWithLineNumbers($treeCur, $fileoffset, $inputCounter); print STDERR "SET HOLLOW TO $treeCur\n" if ($parseDebug); } if ($parserState->{endgame} == 2 || $parserState->{endgame} == 3) { # Either the part has to be reprocessed with the new parser # state (so redo it) or it's time to quit entirely. if (!scalar(@parserStack)) { $continue = 0; $inputCounter--; print STDERR "Decremented inputCounter from ".($inputCounter+1)." to $inputCounter [1]\n" if ($inputCounterDebug); last; } elsif (!$parserState->{onlyComments}) { # @@@ FIXME? @@@ my $treeRef = $parserState->{hollow}; print STDERR "NEW PARSER STATE (ENDGAME)\n" if ($parseDebug || $stateDebug); my $leading = $parserState->{seenLeading}; $parserState->{lastTreeNode} = $treeCur; if ($parserState->{popAtEnd} == 1) { print STDERR "POP AT END\n" if ($parseDebug); $parserState->{popAtEnd} = 0; $parserState->{popAfter} = 0; print STDERR "TREEPOP\n" if ($parseDebug); $treeCur = $parserState->treePop() || $treeCur; print STDERR "TREECUR: \"$treeCur\"\n" if ($parseDebug); } # if ($treeRef) { $treeRef->addRawParsedParams(\@{$parserState->{parsedParamList}}); $treeRef->parserState($parserState); print STDERR "HOLLOW: ADDED $parserState to $treeRef\n" if ($parseDebug); # } else { # warn("Can't find hollow. This might be a bug.\n"); # } my $parentLeading = $parserState->{parentLeading}; if ($parserState->{endgame} == 3) { # leaving a class. $continue = 0; $inputCounter--; print STDERR "Decremented inputCounter from ".($inputCounter+1)." to $inputCounter [2]\n" if ($inputCounterDebug); last; } else { $parserState = HeaderDoc::ParserState->new( "FULLPATH" => $fullpath, "lang" => $lang, "sublang" => $sublang ); if ($leading ne "" && $leading != -1) { print STDERR "SETTING LEAD SPACE TO \"$leading\"\n" if ($parseDebug); $parserState->{seenLeading} = $leading; $parserState->{leadspace} = $leading; } $parserState->{parentLeading} = $parentLeading } } if ($anyDebug) { print STDERR "REDO\n"; } goto REDO; } else { # This part has been handled. Move on to the # next one. $parserState->{lastpart} = $part; $part = $nextpart; } } print STDERR "Incremented inputCounter from $inputCounter to ".($inputCounter+1)."\n" if ($inputCounterDebug); $inputCounter++; } while (scalar(@parserStack)) { $parserState = pop(@parserStack); } my $inPrivateParamTypes = 0; my $declaration = $treeTop->textTree(); my $publicDeclaration = ""; my $lastACS = ""; # $retDebug = 1; print STDERR "LEAVING PARSER\n" if ($parseDebug || $stateDebug); return HeaderDoc::BlockParse::blockParseReturnState($parserState, $treeTop, $argparse, $declaration, $inPrivateParamTypes, $publicDeclaration, $lastACS, $retDebug, $fileoffset, 0, $parseTokens{definename}, $inputCounter, $lang, $sublang); } 1;