Can't really call this full language support, since this is more a hack and it might not work for all kinds of c++ code, but here you go: http://roughael.net/Cpp.pm
to add it, change the cpp part in the language.txt to:
Language: C/C++
Extensions: c cpp h hpp cxx hxx
Ignore Function Prefix in Index: ~
Full Language Support: NaturalDocs::Languages::Cpp
and in languages.pm add this to the list:
use NaturalDocs::Languages::Cpp;
Not sure if I'm going to continue work on it, I haven't done anything with Perl before and I'm not sure I'll have the time to get into it.
Honestly, I just wanted to get some documentation tool, and now I'm spending hours on something I dont even know if it will work out >-<
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Can't really call this full language support, since this is more a hack and it might not work for all kinds of c++ code, but here you go:
http://roughael.net/Cpp.pm
to add it, change the cpp part in the language.txt to:
Language: C/C++
Extensions: c cpp h hpp cxx hxx
Ignore Function Prefix in Index: ~
Full Language Support: NaturalDocs::Languages::Cpp
and in languages.pm add this to the list:
use NaturalDocs::Languages::Cpp;
Not sure if I'm going to continue work on it, I haven't done anything with Perl before and I'm not sure I'll have the time to get into it.
Honestly, I just wanted to get some documentation tool, and now I'm spending hours on something I dont even know if it will work out >-<
I have updated this to be more complete. Some complicated types involving
function pointers might not work, but everything else seems to work OK.
Class: NaturalDocs::Languages::Cpp
A subclass to handle the language variations of C++.
Topic: Language Support
Supported:
- Classes, Structs, Unions
- Namespaces (no topic generated)
- Functions
- Constructors and Destructors
- Variables
- Constants
- Enums
Not supported yet:
- Autodocumenting enum members
- Using alias
Notes: Complicated types involving function pointers might not be recognized
properly.
This file is part of Natural Docs, which is Copyright (C) 2003-2008 Greg
Valure
Natural Docs is licensed under the GPL
use strict;
use integer;
use List::Util 'max';
use Scalar::Util 'looks_like_number';
package NaturalDocs::Languages::Cpp;
use base 'NaturalDocs::Languages::Advanced';
Group: Package Variables
hash: classKeywords
An existence hash of all the acceptable class keywords. The keys are in all
lowercase.
my %classKeywords = ( 'class' => 1,
'struct' => 1,
'union' => 1);
hash: classModifiers
An existence hash of all the acceptable class modifiers. The keys are in all
lowercase.
my %classModifiers = ( 'public' => 1,
'protected' => 1,
'private' => 1,
'virtual' => 1);
hash: classSectionModifiers
An existence hash of all the acceptable class section modifiers. The keys
are in all lowercase.
my %classSectionModifiers = ( 'public' => 1,
'protected' => 1,
'private' => 1);
hash: functionModifiers
An existence hash of all the acceptable function modifiers. The keys are in
all lowercase.
my %functionModifiers = ( 'static' => 1,
'virtual' => 1,
'explicit' => 1);
hash: impossibleTypeWords
An existence hash of all the reserved words that cannot be in a type. This
includes 'enum' and all modifiers. The keys are in
all lowercase.
my %impossibleTypeWords = ( 'break' => 1, 'case' => 1, 'catch' => 1,
'continue' => 1, 'default' => 1,
'do' => 1, 'else' => 1, 'explicit' => 1,
'false' => 1, 'for' => 1, 'friend' => 1, 'goto' => 1, 'if' =>
1,
'namespace' => 1, 'new' => 1, 'operator' => 1,
'private' => 1, 'protected' => 1, 'public' => 1,
'return' => 1,'sizeof' => 1, 'static' => 1,
'switch' => 1, 'this' => 1, 'throw' => 1, 'true' => 1, 'try' =>
1, 'typeof' => 1,
'using' => 1, 'while' => 1, '=' => 1);
Deleted from the list: object, string, bool, decimal, sbyte, byte, short,
ushort, int, uint, long, ulong, char, float, double, void
Group: Interface Functions
Function: PackageSeparator
Returns the package separator symbol.
sub PackageSeparator
{ return '::'; };
Function: EnumValues
Returns the <EnumValuesType> that describes how the language handles
enums.
sub EnumValues
{ return ::ENUM_UNDER_PARENT(); }; #true for now... changes for C++0x (where
you can have class enums that would be ENUM_UNDER_TYPE)
Function: ParseFile
Parses the passed source file, sending comments acceptable for documentation
to <NaturalDocs::Parser->OnComment()>.
Parameters:
sourceFile - The <FileName> to parse.
topicList - A reference to the list of
<NaturalDocs::Parser::ParsedTopics> being built by the file.
Returns:
The array ( autoTopics, scopeRecord ).
autoTopics - An arrayref of automatically generated topics from the file, or
undef if none.
scopeRecord - An arrayref of
<NaturalDocs::Languages::Advanced::ScopeChanges>, or undef if none.
sub ParseFile #(sourceFile, topicsList)
{
my ($self, $sourceFile, $topicsList) = @_;
$self->ParseForCommentsAndTokens($sourceFile, , , , );
my $tokens = $self->Tokens();
my $index = 0;
my $lineNumber = 1;
my @currentTypedef;
my @currentVisibility;
push(@currentVisibility, 0);
while ($index < scalar @$tokens)
{
if ($self->TryToSkipWhitespace(\$index, \$lineNumber) ||
$self->TryToGetTypeDef(\$index, \$lineNumber, \@currentVisibility) ||
$self->TryToGetEnum(\$index, \$lineNumber, \@currentVisibility) ||
$self->TryToGetNamespace(\$index, \$lineNumber, \@currentVisibility,
\@currentTypedef) ||
$self->TryToGetUsing(\$index, \$lineNumber) ||
$self->TryToGetClassSectionModifier(\$index, \$lineNumber,
\@currentVisibility) ||
$self->TryToGetClass(\$index, \$lineNumber, \@currentVisibility,
\@currentTypedef) ||
$self->TryToGetFunction(\$index, \$lineNumber, \@currentVisibility) ||
$self->TryToGetVariable(\$index, \$lineNumber, \@currentVisibility) )
{
The functions above will handle everything.
}
elsif ($tokens-> eq '{')
{
$self->StartScope('}', $lineNumber, undef, undef, undef);
push(@currentVisibility, $currentVisibility);
push(@currentTypedef, undef);
$index++;
}
elsif ($tokens-> eq '}')
{
if ($self->ClosingScopeSymbol() eq '}')
{
$self->EndScope($lineNumber);
pop(@currentVisibility);
my $typedef = pop(@currentTypedef);
if(defined $typedef)
{
$index++;
$self->TryToSkipWhitespace(\$index, \$lineNumber);
my $name;
my $startIndex = $index;
while($tokens-> ne ';' && $index < scalar @$tokens)
{
$name = $tokens->;
$index++;
$self->TryToSkipWhitespace(\$index, \$lineNumber);
}
if($tokens-> eq ';')
{
my $endIndex = $index;
my $prototype = "typedef $typedef {...} " .
$self->CreateString($startIndex, $endIndex);
if($currentVisibility == 1)
{
$prototype = "protected: " . $prototype;
}
if(::max(@currentVisibility) < 2)
{
$self->AddAutoTopic(NaturalDocs::Parser::ParsedTopic->New(::TOPIC_TYPE()
, $name,
$self->CurrentScope(), $self->CurrentUsing(),
$prototype,
undef, undef, $lineNumber)); #fixme line number is probably wrong
}
}
}
};
$index++;
}
else
{
$self->SkipRestOfStatement(\$index, \$lineNumber);
};
};
Don't need to keep these around.
$self->ClearTokens();
my $autoTopics = $self->AutoTopics();
my $scopeRecord = $self->ScopeRecord();
if (defined $scopeRecord && !scalar @$scopeRecord)
{ $scopeRecord = undef; };
return ( $autoTopics, $scopeRecord );
};
Group: Statement Parsing Functions
All functions here assume that the current position is at the beginning of a
statement.
Note for developers: I am well aware that the code in these functions do not
check if we're past the end of the tokens as
often as it should. We're making use of the fact that Perl will always
return undef in these cases to keep the code simpler.
Function: TryToGetNamespace
Determines whether the position is at a namespace declaration statement, and
if so, adjusts the scope, skips it, and returns
true.
Why no topic?:
The main reason we don't create a Natural Docs topic for a namespace is
because in order to declare class A.B.C in C#,
you must do this:
> namespace A.B
> {
> class C
> { ... }
> }
That would result in a namespace topic whose only purpose is really to
qualify C. It would take the default page title, and
thus the default menu title. So if you have files for A.B.X, A.B.Y, and
A.B.Z, they all will appear as A.B on the menu.
If something actually appears in the namespace besides a class, it will be
handled by
<NaturalDocs::Parser->AddPackageDelineators()>. That function will
add a package topic to correct the scope.
If the user actually documented it, it will still appear because of the
manual topic.
sub TryToGetNamespace #(indexRef, lineNumberRef, visibilityRef, typedefRef)
{
my ($self, $indexRef, $lineNumberRef, $visibilityRef, $typedefRef) = @_;
my $tokens = $self->Tokens();
if (lc($tokens->) ne 'namespace')
{ return undef; };
my $index = $$indexRef + 1;
my $lineNumber = $$lineNumberRef;
if (!$self->TryToSkipWhitespace(\$index, \$lineNumber))
{ return undef; };
my $name;
while ($tokens-> =~ /^/i)
{
$name .= $tokens->;
$index++;
};
if (!defined $name)
{ return undef; };
$self->TryToSkipWhitespace(\$index, \$lineNumber);
if ($tokens-> ne '{')
{ return undef; };
$index++;
my @scopeIdentifiers =
NaturalDocs::SymbolString->IdentifiersOf($self->CurrentScope());
$name = join('::', @scopeIdentifiers, $name);
We found a valid one if we made it this far.
my $autoTopic = NaturalDocs::Parser::ParsedTopic->New(::TOPIC_CLASS(),
$name,
undef, $self->CurrentUsing(),
undef,
undef, undef, $$lineNumberRef);
$self->AddAutoTopic($autoTopic);
$self->StartScope('}', $lineNumber, $autoTopic->Package());
push(@$visibilityRef, $$visibilityRef);
push(@$typedefRef, undef);
$$indexRef = $index;
$$lineNumberRef = $lineNumber;
return 1;
};
sub ExtractTemplate #(indexRef, lineNumberRef)
{
my ($self, $indexRef, $lineNumberRef) = @_;
my $tokens = $self->Tokens();
my $result = 'template <';
$self->TryToSkipWhitespace($indexRef, $lineNumberRef);
if ($tokens-> ne 'template')
{
return undef;
}
$$indexRef++;
$self->TryToSkipWhitespace($indexRef, $lineNumberRef);
if ($tokens-> ne '<')
{
return undef;
};
$$indexRef++;
$self->TryToSkipWhitespace($indexRef, $lineNumberRef);
while ($$indexRef < scalar @$tokens)
{
if ($tokens-> eq 'template')
{
$result .= $self->ExtractTemplate($indexRef, $lineNumberRef);
$self->TryToSkipWhitespace($indexRef, $lineNumberRef);
$result .= ' ';
}
elsif ($tokens-> =~ /^[a-z0-9_:*&]/i)
{
$result .= $tokens->;
$$indexRef++;
$self->TryToSkipWhitespace($indexRef, $lineNumberRef);
if ($tokens-> eq ',')
{
$$indexRef++;
$self->TryToSkipWhitespace($indexRef, $lineNumberRef);
$result .= ', ';
}
elsif ($tokens-> eq ">")
{
$$indexRef++;
$result .= '>';
return $result;
}
else
{
$result .= ' ';
};
}
elsif ($tokens-> eq ">")
{
$$indexRef++;
$result .= '>';
return $result;
}
else
{
return undef;
};
};
return undef;
};
sub ExtractSpecialization #(indexRef, lineNumberRef)
{
my ($self, $indexRef, $lineNumberRef) = @_;
my $tokens = $self->Tokens();
my $result = '<';
$self->TryToSkipWhitespace($indexRef, $lineNumberRef);
if ($tokens-> ne '<')
{
return undef;
}
$$indexRef++;
$self->TryToSkipWhitespace($indexRef, $lineNumberRef);
while ($$indexRef < scalar @$tokens)
{
if ($tokens-> eq '<')
{
$result .= $self->ExtractSpecialization($indexRef, $lineNumberRef);
$self->TryToSkipWhitespace($indexRef, $lineNumberRef);
$result .= ' ';
}
elsif ($tokens-> =~ /^[a-z0-9_:*&\()]/i)
{
$result .= $tokens->;
$$indexRef++;
$self->TryToSkipWhitespace($indexRef, $lineNumberRef);
if ($tokens-> eq ',')
{
$$indexRef++;
$self->TryToSkipWhitespace($indexRef, $lineNumberRef);
$result .= ', ';
}
elsif ($tokens-> eq ">")
{
$$indexRef++;
$result .= '>';
return $result;
}
else
{
$result .= ' ';
};
}
elsif ($tokens-> eq ">")
{
$$indexRef++;
$result .= '>';
return $result;
}
else
{
return undef;
};
};
return undef;
};
Function: TryToGetClass
Determines whether the position is at a class declaration statement, and if
so, generates a topic for it, skips it, and
returns true.
Supported Syntaxes:
- Classes
- Structs
- Interfaces
sub TryToGetClass #(indexRef, lineNumberRef, visibilityRef, typedefRef)
{
my ($self, $indexRef, $lineNumberRef, $visibilityRef, $typedefRef) = @_;
my $tokens = $self->Tokens();
my $index = $$indexRef;
my $lineNumber = $$lineNumberRef;
my $startIndex = $index;
my $startLine = $lineNumber;
my $isTypeDef = 0;
my $isTemplate = 0;
my $templateName;
if (lc($tokens->) eq 'typedef')
{
$isTypeDef = 1;
$index++;
$self->TryToSkipWhitespace(\$index, \$lineNumber);
$startIndex = $index;
$startLine = $lineNumber;
if (!exists $classKeywords{lc($tokens->)}) { return undef; };
}
elsif (lc($tokens->) eq 'template')
{
$isTemplate = 1;
$templateName = $self->ExtractTemplate(\$index, \$lineNumber);
if (!defined $templateName)
{
return undef;
}
$self->TryToSkipWhitespace(\$index, \$lineNumber);
if (!exists $classKeywords{lc($tokens->)}) { return undef; };
}
elsif (!exists $classKeywords{lc($tokens->)})
{
return undef;
};
my $lcClassKeyword = lc($tokens->);
$index++;
if (!$self->TryToSkipWhitespace(\$index, \$lineNumber))
{ return undef; };
my $name;
my @scopes;
while ($tokens-> =~ /^/i)
{
$name .= $tokens->;
$index++;
$self->TryToSkipWhitespace(\$index, \$lineNumber);
if ($tokens-> eq ':' && $tokens-> eq ':')
{
push(@scopes, $name);
$name = '';
$index += 2;
$self->TryToSkipWhitespace(\$index, \$lineNumber);
}
};
if (!defined $name)
{
return undef;
};
my $typedefName = "$lcClassKeyword " . join('::', @scopes, $name);
$self->TryToSkipWhitespace(\$index, \$lineNumber);
if ($tokens-> eq '<')
{
my $specialization = $self->ExtractSpecialization(\$index, \$lineNumber);
if (!defined $specialization)
{
return undef;
}
$name .= $specialization;
$self->TryToSkipWhitespace(\$index, \$lineNumber);
}
no class body.. can be ignored.
if ($tokens-> eq ';') {
$index++;
$self->TryToSkipWhitespace(\$index, \$lineNumber);
$$indexRef = $index;
$$lineNumberRef = $lineNumber;
return 1;
}
my @parents;
if ($tokens-> eq ':')
{
do
{
$index++;
$self->TryToSkipWhitespace(\$index, \$lineNumber);
my @modifiers;
while ($tokens-> =~ /^/i && exists
$classModifiers{lc($tokens->)} )
{
fixme: dunno if this would work, probably not..
push @modifiers, lc($tokens->);
$index++;
$self->TryToSkipWhitespace(\$index, \$lineNumber);
};
$self->TryToSkipWhitespace(\$index, \$lineNumber);
my $parentName;
while ($tokens-> =~ /^/i)
{
$parentName .= $tokens->;
$index++;
};
if ($tokens-> eq '<')
{
my $specialization = $self->ExtractSpecialization(\$index, \$lineNumber);
if (!defined $specialization)
{
return undef;
}
$self->TryToSkipWhitespace(\$index, \$lineNumber);
}
if (!defined $parentName)
{ return undef; };
push @parents, NaturalDocs::SymbolString->FromText($parentName);
$self->TryToSkipWhitespace(\$index, \$lineNumber);
}
while ($tokens-> eq ',')
};
if ($tokens-> ne '{')
{
return undef;
};
If we made it this far, we have a valid class declaration.
my @scopeIdentifiers =
NaturalDocs::SymbolString->IdentifiersOf($self->CurrentScope());
$name = join('::', @scopeIdentifiers, @scopes, $name);
my $prototype = $self->CreateString($startIndex, $index);
if ($$visibilityRef == 1)
{
$prototype = "protected: " . $prototype;
}
my $autoTopic = NaturalDocs::Parser::ParsedTopic->New(::TOPIC_CLASS(),
$name,
undef, $self->CurrentUsing(),
$prototype,
undef, undef, $$lineNumberRef);
if (::max(@$visibilityRef) < 2)
{ # only add classes that aren't private
$self->AddAutoTopic($autoTopic);
NaturalDocs::Parser->OnClass($autoTopic->Package());
foreach my $parent (@parents)
{
NaturalDocs::Parser->OnClassParent($autoTopic->Package(), $parent,
$self->CurrentScope(), undef,
::RESOLVE_RELATIVE());
};
}
$self->StartScope('}', $lineNumber, $autoTopic->Package());
if($isTypeDef)
{
push(@$typedefRef, $typedefName);
}
else
{
push(@$typedefRef, undef);
}
my $newVisibility = 0;
if($lcClassKeyword eq "class")
{
$newVisibility = 2;
}
push(@$visibilityRef, $newVisibility);
$index++;
$$indexRef = $index;
$$lineNumberRef = $lineNumber;
return 1;
};
Function: TryToGetUsing
Determines whether the position is at a using statement, and if so, adds it
to the current scope, skips it, and returns
true.
Supported:
- Using
Unsupported:
- Using with alias
sub TryToGetUsing #(indexRef, lineNumberRef)
{
my ($self, $indexRef, $lineNumberRef) = @_;
my $tokens = $self->Tokens();
my $index = $$indexRef;
my $lineNumber = $$lineNumberRef;
if (lc($tokens->) ne 'using')
{ return undef; };
$index++;
$self->TryToSkipWhitespace(\$index, \$lineNumber);
my $name;
while ($tokens-> =~ /^/i)
{ # not sure how this 'using' thing works exactly
if ($tokens-> ne 'namespace')
{
$name .= $tokens->;
}
$index++;
};
namespace aliases are not supported.
if ($tokens-> ne ';' ||
!defined $name)
{ return undef; };
$index++;
$self->AddUsing( NaturalDocs::SymbolString->FromText($name) );
$$indexRef = $index;
$$lineNumberRef = $lineNumber;
return 1;
};
Function: TryToGetUsing
Get class section (public, private, ..)
sub TryToGetClassSectionModifier #(indexRef, lineNumberRef, visibilityRef)
{
my ($self, $indexRef, $lineNumberRef, $visibilityRef) = @_;
my $tokens = $self->Tokens();
my $index = $$indexRef;
my $lineNumber = $$lineNumberRef;
if (!exists $classSectionModifiers{ lc($tokens->) })
{ return undef; };
my $modifier = lc($tokens->);
$index++;
$self->TryToSkipWhitespace(\$index, \$lineNumber);
if ($tokens-> ne ':' )
{ return undef; };
$index++;
$self->TryToSkipWhitespace(\$index, \$lineNumber);
if ($modifier eq 'public')
{
$$visibilityRef = 0;
}
elsif ($modifier eq 'protected')
{
$$visibilityRef = 1;
}
elsif ($modifier eq 'private')
{
$$visibilityRef = 2;
}
$$indexRef = $index;
$$lineNumberRef = $lineNumber;
return 1;
};
Function: TryToGetFunction
Determines if the position is on a function declaration, and if so,
generates a topic for it, skips it, and returns true.
Supported Syntaxes:
- Functions
- Constructors
- Destructors
- Properties
- Indexers
- Delegates
- Events
sub TryToGetFunction #(indexRef, lineNumberRef, visibilityRef)
{
my ($self, $indexRef, $lineNumberRef, $visibilityRef) = @_;
my $tokens = $self->Tokens();
my $index = $$indexRef;
my $lineNumber = $$lineNumberRef;
if ($self->TryToSkipAttributes(\$index, \$lineNumber))
{ $self->TryToSkipWhitespace(\$index, \$lineNumber); };
my $startIndex = $index;
my $startLine = $lineNumber;
my $isOperator = 0;
my $isTemplate = 0;
my $templateName;
if ($tokens-> eq 'template')
{
$isTemplate = 1;
$templateName = $self->ExtractTemplate(\$index, \$lineNumber);
if (!defined $templateName)
{
return undef;
}
$self->TryToSkipWhitespace(\$index, \$lineNumber);
}
while ($tokens-> =~ /^/i && exists
$functionModifiers{lc($tokens->)} )
{
$index++;
$self->TryToSkipWhitespace(\$index, \$lineNumber);
};
my $isFunctionPtr = 0;
my $returnType = $self->TryToGetType(\$index, \$lineNumber,
\$isFunctionPtr);
fixme: need to handle cases where the return type is a function ptr
$self->TryToSkipWhitespace(\$index, \$lineNumber);
my $name;
my $lastNameWord;
if ( $tokens-> eq 'operator' )
{
$isOperator = 1;
my $startOp = $index;
$index++;
$self->TryToSkipWhitespace(\$index, \$lineNumber);
if($tokens-> eq '(')
{
$index++;
$self->TryToSkipWhitespace(\$index, \$lineNumber);
if($tokens-> ne ')')
{
return undef;
}
$index++;
$self->TryToSkipWhitespace(\$index, \$lineNumber);
}
else
{
while ($index < scalar @$tokens && $tokens-> ne '(')
{
$index++;
}
}
if ( $tokens-> ne '(' )
{
return undef;
}
$name = $self->NormalizePrototype( $self->CreateString($startOp, $index)
);
}
elsif ( $tokens-> ne '(' )
{
$name = '';
if ($tokens-> eq '~')
{
$name = '~';
$index++;
$self->TryToSkipWhitespace(\$index, \$lineNumber);
}
if (!defined $returnType)
{
$lastNameWord = $tokens->;
}
$name .= $tokens->;
$index++;
$self->TryToSkipWhitespace(\$index, \$lineNumber);
if ( $tokens-> eq '<' )
{
my $specialization = $self->ExtractSpecialization(\$index, \$lineNumber);
if (!defined $specialization)
{
return undef;
}
$name .= $specialization;
$self->TryToSkipWhitespace(\$index, \$lineNumber);
}
}
else
{
Constructors and destructors don't have return types. It's possible their
names were mistaken for the return type.
if (defined $returnType)
{
$name = $returnType;
$returnType = undef;
$name =~ /(+)$/i;
$lastNameWord = $1;
}
else
{
return undef;
};
};
If there's no return type, make sure it's a constructor or destructor.
if (!defined $returnType && !$isOperator)
{
my @identifiers = NaturalDocs::SymbolString->IdentifiersOf(
$self->CurrentScope() );
if ($lastNameWord ne $identifiers)
{
return undef;
};
};
$self->TryToSkipWhitespace(\$index, \$lineNumber);
Functions, constructors, destructors, delegates.
if ($tokens-> eq '(')
{
This should jump the parenthesis completely.
my $argStart = $index;
$self->GenericSkip(\$index, \$lineNumber);
my $prototype = $self->NormalizePrototype(
$self->CreateString($startIndex, $index) );
my $addMore = 0;
$self->TryToSkipWhitespace(\$index, \$lineNumber);
while ($tokens-> eq 'const' || $tokens-> eq 'volatile' || $tokens->
eq 'throw')
{
$addMore = 1;
if($tokens-> eq 'throw')
{
$index++;
$self->TryToSkipWhitespace(\$index, \$lineNumber);
if ($tokens-> ne '(') { return undef };
This should jump the parenthesis completely.
$self->GenericSkip(\$index, \$lineNumber);
}
else
{
$index++;
}
$self->TryToSkipWhitespace(\$index, \$lineNumber);
}
if ($tokens-> eq '=')
{
$addMore = 1;
$index++;
$self->TryToSkipWhitespace(\$index, \$lineNumber);
if ($tokens-> ne '0') { return undef };
$index++;
$self->TryToSkipWhitespace(\$index, \$lineNumber);
}
if ($addMore)
{
$prototype = $self->NormalizePrototype( $self->CreateString($startIndex,
$index) );
}
if($$visibilityRef == 1)
{
$prototype = "protected: " . $prototype;
}
if(::max(@$visibilityRef) < 2)
{
$self->AddAutoTopic(NaturalDocs::Parser::ParsedTopic->New(::TOPIC_FUNCTI
ON(), $name,
$self->CurrentScope(), $self->CurrentUsing(),
$prototype,
undef, undef, $startLine));
}
$self->SkipRestOfStatement(\$index, \$lineNumber);
}
else
{ return undef; };
We succeeded if we got this far.
$$indexRef = $index;
$$lineNumberRef = $lineNumber;
return 1;
};
Function: TryToGetVariable
Determines if the position is on a variable declaration statement, and if
so, generates a topic for each variable, skips the
statement, and returns true.
Supported Syntaxes:
- Variables
- Constants
sub TryToGetVariable #(indexRef, lineNumberRef, visibilityRef)
{
my ($self, $indexRef, $lineNumberRef, $visibilityRef) = @_;
my $tokens = $self->Tokens();
my $index = $$indexRef;
my $lineNumber = $$lineNumberRef;
if ($self->TryToSkipAttributes(\$index, \$lineNumber))
{ $self->TryToSkipWhitespace(\$index, \$lineNumber); };
my $startIndex = $index;
my $startLine = $lineNumber;
my @modifiers;
if (lc($tokens->) eq 'static')
{
push @modifiers, lc('static');
$index++;
$self->TryToSkipWhitespace(\$index, \$lineNumber);
};
my $type;
if (lc($tokens->) eq 'const')
{
$type = ::TOPIC_CONSTANT();
$index++;
$self->TryToSkipWhitespace(\$index, \$lineNumber);
}
else
{
$type = ::TOPIC_VARIABLE();
};
my $isFunctionPtr = 0;
if (!$self->TryToGetType(\$index, \$lineNumber, \$isFunctionPtr) &&
!$isFunctionPtr)
{
return undef;
};
my $endTypeIndex = $index;
$self->TryToSkipWhitespace(\$index, \$lineNumber);
my $valueIndex;
my $valueStr;
my @names;
my @values;
my $paramStr;
for (;;)
{
my $name;
while ($tokens-> =~ /^/i)
{
$name .= $tokens->;
$index++;
};
$self->TryToSkipWhitespace(\$index, \$lineNumber);
if($isFunctionPtr)
{
if($tokens-> ne ')')
{
return undef;
}
$index++;
$self->TryToSkipWhitespace(\$index, \$lineNumber);
$paramStr .= ' ) ';
my $paramStart = $index;
if($tokens-> ne '(')
{
return undef;
}
should skip to after parens
$self->GenericSkip(\$index, \$lineNumber);
$paramStr .= $self->CreateString($paramStart, $index);
}
if ($tokens-> eq ';
$index++;
} while( $tokens-> ne ']');
$name .= ']';
$index++;
$self->TryToSkipWhitespace(\$index, \$lineNumber);
}
if ($tokens-> eq '=')
{
$valueIndex = $index;
do
{
$self->GenericSkip(\$index, \$lineNumber);
}
while ($tokens-> ne ',' && $tokens-> ne ';');
my $valueStr = $self->CreateString($valueIndex, $index);
push @values, $valueStr;
}
else {
push @values, '';
}
push @names, $name;
if ($tokens-> eq ';')
{
$index++;
last;
}
elsif ($tokens-> eq ',')
{
$index++;
$self->TryToSkipWhitespace(\$index, \$lineNumber);
}
else
{ return undef; };
};
We succeeded if we got this far.
@values = reverse(@values);
my $prototypePrefix = $self->CreateString($startIndex, $endTypeIndex);
foreach my $name (@names)
{
my $prototype = $self->NormalizePrototype( $prototypePrefix . ' ' . $name .
$paramStr . ' ' . pop(@values) );
if($$visibilityRef == 1)
{
$prototype = "protected: " . $prototype;
}
if(::max(@$visibilityRef) < 2)
{
$self->AddAutoTopic(NaturalDocs::Parser::ParsedTopic->New($type, $name,
$self->CurrentScope(), $self->CurrentUsing(),
$prototype,
undef, undef, $startLine));
}
};
$$indexRef = $index;
$$lineNumberRef = $lineNumber;
return 1;
};
Function: TryToGetTypeDef
sub TryToGetTypeDef #(indexRef, lineNumberRef, visibilityRef)
{
my ($self, $indexRef, $lineNumberRef, $visibilityRef) = @_;
my $tokens = $self->Tokens();
my $index = $$indexRef;
my $lineNumber = $$lineNumberRef;
my $startIndex = $index;
my $startLine = $lineNumber;
if ($tokens-> ne 'typedef')
{ return undef; }
$index++;
$self->TryToSkipWhitespace(\$index, \$lineNumber);
enum and struct/class/union are handled differently..
if ( $tokens-> eq 'enum' || (exists $classKeywords{lc($tokens->)}))
{ return undef; }
my $name;
my $isFunctionPtr = 0;
while($tokens-> ne ';' && $index < scalar @$tokens)
{
if(!$isFunctionPtr && $tokens-> eq '(')
{
$index++;
$self->TryToSkipWhitespace(\$index, \$lineNumber);
if($tokens-> eq '*')
{
$isFunctionPtr = 1;
$index++;
$self->TryToSkipWhitespace(\$index, \$lineNumber);
$name = $tokens->;
}
}
if(!$isFunctionPtr)
{
$name = $tokens->;
}
$index++;
$self->TryToSkipWhitespace(\$index, \$lineNumber);
}
if($tokens-> ne ';')
{
return undef;
}
my $endIndex = $index;
$index++;
$self->TryToSkipWhitespace(\$index, \$lineNumber);
my $prototype = $self->CreateString($startIndex, $endIndex);
if($$visibilityRef == 1)
{
$prototype = "protected: " . $prototype;
}
if(::max(@$visibilityRef) < 2)
{
$self->AddAutoTopic(NaturalDocs::Parser::ParsedTopic->New(::TOPIC_TYPE()
, $name,
$self->CurrentScope(), $self->CurrentUsing(),
$prototype,
undef, undef, $startLine));
}
$$indexRef = $index;
$$lineNumberRef = $lineNumber;
return 1;
};
Function: TryToGetEnum
Determines if the position is on an enum declaration statement, and if so,
generates a topic for it.
Supported Syntaxes:
- Enums
Unsupported:
- Documenting the members automatically
sub TryToGetEnum #(indexRef, lineNumberRef, visibilityRef)
{
my ($self, $indexRef, $lineNumberRef, $visibilityRef) = @_;
my $tokens = $self->Tokens();
my $index = $$indexRef;
my $lineNumber = $$lineNumberRef;
my $istypedef = 0;
my $startIndex = $index;
my $startLine = $lineNumber;
if ($tokens-> eq 'enum')
{ $index++; }
elsif ($tokens-> eq 'typedef' )
{
$index++;
$self->TryToSkipWhitespace(\$index, \$lineNumber);
if ( $tokens-> ne 'enum')
{ return undef; }
$index++;
$self->TryToSkipWhitespace(\$index, \$lineNumber);
$istypedef = 1;
}
else
{ return undef; }
$self->TryToSkipWhitespace(\$index, \$lineNumber);
my $name = '';
if ($tokens-> eq '{')
{
$index++;
$self->TryToSkipWhitespace(\$index, \$lineNumber);
} else {
$name = $tokens->;
$index++;
$self->TryToSkipWhitespace(\$index, \$lineNumber);
if ($tokens-> ne '{')
{
return undef;
}
$index++;
$self->TryToSkipWhitespace(\$index, \$lineNumber);
}
We succeeded if we got this far.
my @names;
my @values;
my $currentValue = 0;
my $valueHelper = 0;
while($tokens-> ne '}' && $index < scalar @$tokens)
{
$self->TryToSkipWhitespace(\$index, \$lineNumber);
if($tokens-> =~ /^/i)
{
my $ename = $tokens->;
$index++;
$self->TryToSkipWhitespace(\$index, \$lineNumber);
if($tokens-> eq '=')
{
$index++;
$self->TryToSkipWhitespace(\$index, \$lineNumber);
my $value = '';
while($tokens-> ne ',' && $tokens-> ne '}' && $index
< scalar @$tokens)
{
$value .= $tokens->;
$index++;
}
$currentValue = $value;
$valueHelper = 0;
}
push(@names, $ename);
if($valueHelper > 0)
{
push(@values, $currentValue . " + " . $valueHelper);
}
else
{
push(@values, $currentValue);
}
if(::looks_like_number($currentValue))
{
$currentValue++;
}
else
{
$valueHelper++;
}
}
elsif($tokens-> eq ',')
{
$index++;
$self->TryToSkipWhitespace(\$index, \$lineNumber);
}
else
{
print "failed to get enum value\n";
return undef;
}
}
if($tokens-> ne '}')
{
return undef;
}
if ( $istypedef ) {
$self->GenericSkipUntilAfter(\$index, \$lineNumber, '}');
$self->TryToSkipWhitespace(\$index, \$lineNumber);
$name = $tokens->;
}
$self->GenericSkipUntilAfter(\$index, \$lineNumber, ';');
my $prototype = $self->CreateString($startIndex, $index);
$prototype = $self->NormalizePrototype( $prototype );
if($$visibilityRef == 1)
{
$prototype = "protected: " . $prototype;
}
if(::max(@$visibilityRef) < 2)
{
$self->AddAutoTopic(NaturalDocs::Parser::ParsedTopic->New(::TOPIC_ENUMER
ATION(), $name,
$self->CurrentScope(), $self->CurrentUsing(),
$prototype,
undef, undef, $startLine));
fixme: Not sure how to add the values properly
foreach my $ename (@names)
{
my $eprototype = $self->NormalizePrototype( $ename . ' = ' .
shift(@values) );
$self->AddAutoTopic(NaturalDocs::Parser::ParsedTopic->New(::TOPIC_CONS
TANT(), $ename,
$self->CurrentScope(), $self->CurrentUsing(),
$eprototype,
undef, undef, $startLine));
}
}
$$indexRef = $index;
$$lineNumberRef = $lineNumber;
return 1;
};
Function: TryToGetType
Determines if the position is on a type identifier, and if so, skips it and
returns it as a string. This function does not allow
modifiers. You must take care of those beforehand.
sub TryToGetType #(indexRef, lineNumberRef, isFunctionPtrRef)
{
my ($self, $indexRef, $lineNumberRef, $isFunctionPtrRef) = @_;
my $tokens = $self->Tokens();
my $index = $$indexRef;
my $lineNumber = $$lineNumberRef;
my $startIndex = $index;
my $endIndex = $index;
my $endLine = $lineNumber;
my $hasTemplate = 0;
while (($tokens-> =~ /^/i) && !(exists $impossibleTypeWords{
lc($tokens->) }))
{
my $didTemplate = 0;
if ($tokens-> eq '<')
{
if($hasTemplate)
{
last;
}
my $specialization = $self->ExtractSpecialization(\$index, \$lineNumber);
if (!defined $specialization)
{
return undef;
}
$hasTemplate = 1;
$didTemplate = 1;
}
$endIndex = $index;
$endLine = $lineNumber;
if (!$didTemplate)
{
$index++;
}
$self->TryToSkipWhitespace(\$index, \$lineNumber);
};
my $testIndex = $index;
if($tokens-> eq '(')
{
$testIndex++;
$self->TryToSkipWhitespace(\$testIndex, \$lineNumber);
if($tokens-> eq '*')
{
$$isFunctionPtrRef = 1;
$testIndex++;
$self->TryToSkipWhitespace(\$testIndex, \$lineNumber);
$$indexRef = $testIndex;
$$lineNumberRef = $lineNumber;
return undef;
}
}
if (($index-$startIndex) < 2) { return undef; };
if ((exists $impossibleTypeWords{ lc($tokens->) }))
{
$endIndex = $index;
}
my $name = $self->CreateString($startIndex, $endIndex);
if (!defined $name)
{ return undef; };
$$indexRef = $endIndex;
$$lineNumberRef = $endLine;
return $name;
};
Function: PreprocessFile
Overridden in a hideous hack to deal with this module's inability to handle
extern "c". TODO - replace with something better.
sub PreprocessFile #(lines)
{
my ($self, $lines) = @_;
for (my $i = 0; $i < scalar @$lines; $i++)
{
if ($lines-> =~ /^extern ""$/i)
{
$lines-> = '';
}
};
};
Group: Low Level Parsing Functions
Function: GenericSkip
Advances the position one place through general code.
- If the position is on a string, it will skip it completely.
- If the position is on an opening symbol, it will skip until the past the
closing symbol.
- If the position is on whitespace (including comments and preprocessing
directives), it will skip it completely.
- Otherwise it skips one token.
Parameters:
indexRef - A reference to the current index.
lineNumberRef - A reference to the current line number.
sub GenericSkip #(indexRef, lineNumberRef)
{
my ($self, $indexRef, $lineNumberRef) = @_;
my $tokens = $self->Tokens();
We can ignore the scope stack because we're just skipping everything without
parsing, and we need recursion anyway.
if ($tokens-> eq '{')
{
$$indexRef++;
$self->GenericSkipUntilAfter($indexRef, $lineNumberRef, '}');
}
elsif ($tokens-> eq '(')
{
$$indexRef++;
$self->GenericSkipUntilAfter($indexRef, $lineNumberRef, ')');
}
elsif ($tokens-> eq '');
}
elsif ($self->TryToSkipWhitespace($indexRef, $lineNumberRef) ||
$self->TryToSkipString($indexRef, $lineNumberRef))
{
}
else
{ $$indexRef++; };
};
Function: GenericSkipUntilAfter
Advances the position via <GenericSkip()> until a specific token is
reached and passed.
sub GenericSkipUntilAfter #(indexRef, lineNumberRef, token)
{
my ($self, $indexRef, $lineNumberRef, $token) = @_;
my $tokens = $self->Tokens();
while ($$indexRef < scalar @$tokens && $tokens-> ne $token)
{ $self->GenericSkip($indexRef, $lineNumberRef); };
if ($tokens-> eq "\n")
{ $$lineNumberRef++; };
$$indexRef++;
};
Function: SkipRestOfStatement
Advances the position via <GenericSkip()> until after the end of the
current statement, which is defined as a semicolon or
a brace group. Of course, either of those appearing inside parenthesis, a
nested brace group, etc. don't count.
sub SkipRestOfStatement #(indexRef, lineNumberRef)
{
my ($self, $indexRef, $lineNumberRef) = @_;
my $tokens = $self->Tokens();
while ($$indexRef < scalar @$tokens &&
$tokens-> ne ';' &&
$tokens-> ne '{')
{
$self->GenericSkip($indexRef, $lineNumberRef);
};
if ($tokens-> eq ';')
{ $$indexRef++; }
elsif ($tokens-> eq '{')
{ $self->GenericSkip($indexRef, $lineNumberRef); };
};
Function: TryToSkipString
If the current position is on a string delimiter, skip past the string and
return true.
Parameters:
indexRef - A reference to the index of the position to start at.
lineNumberRef - A reference to the line number of the position.
Returns:
Whether the position was at a string.
Syntax Support:
- Supports quotes, apostrophes, and at-quotes.
sub TryToSkipString #(indexRef, lineNumberRef)
{
my ($self, $indexRef, $lineNumberRef) = @_;
my $tokens = $self->Tokens();
The three string delimiters. All three are Perl variables when preceded by a
dollar sign.
if ($self->SUPER::TryToSkipString($indexRef, $lineNumberRef, '\'') ||
$self->SUPER::TryToSkipString($indexRef, $lineNumberRef, '"') )
{
return 1;
}
elsif ($tokens-> eq '@' && $tokens-> eq '"')
{
$$indexRef += 2;
We need to do at-strings manually because backslash characters are accepted
as regular characters, and two consecutive
quotes are accepted as well.
while ($$indexRef < scalar @$tokens && !($tokens-> eq '"'
&& $tokens-> ne '"') )
{
if ($tokens-> eq '"')
{
This is safe because the while condition will only let through quote pairs.
$$indexRef += 2;
}
elsif ($tokens-> eq "\n")
{
$$indexRef++;
$$lineNumberRef++;
}
else
{
$$indexRef++;
};
};
Skip the closing quote.
if ($$indexRef < scalar @$tokens)
{ $$indexRef++; };
return 1;
}
else
{ return undef; };
};
Function: TryToSkipAttributes
If the current position is on an attribute section, skip it and return true.
Skips multiple attribute sections if they appear
consecutively.
fixme: needed ?
sub TryToSkipAttributes #(indexRef, lineNumberRef)
{
my ($self, $indexRef, $lineNumberRef) = @_;
my $tokens = $self->Tokens();
my $success;
while ($tokens-> eq '');
$self->TryToSkipWhitespace($indexRef, $lineNumberRef);
};
return $success;
};
Function: TryToSkipWhitespace
If the current position is on a whitespace token, a line break token, a
comment, or a preprocessing directive, it skips them
and returns true. If there are a number of these in a row, it skips them
all.
sub TryToSkipWhitespace #(indexRef, lineNumberRef)
{
my ($self, $indexRef, $lineNumberRef) = @_;
my $tokens = $self->Tokens();
my $result;
while ($$indexRef < scalar @$tokens)
{
if ($tokens-> =~ /^/)
{
$$indexRef++;
$result = 1;
}
elsif ($tokens-> eq "\n")
{
$$indexRef++;
$$lineNumberRef++;
$result = 1;
}
elsif ($self->TryToSkipComment($indexRef, $lineNumberRef) ||
$self->TryToSkipPreprocessingDirective($indexRef, $lineNumberRef))
{
$result = 1;
}
else
{ last; };
};
return $result;
};
Function: TryToSkipComment
If the current position is on a comment, skip past it and return true.
sub TryToSkipComment #(indexRef, lineNumberRef)
{
my ($self, $indexRef, $lineNumberRef) = @_;
return ( $self->TryToSkipLineComment($indexRef, $lineNumberRef) ||
$self->TryToSkipMultilineComment($indexRef, $lineNumberRef) );
};
Function: TryToSkipLineComment
If the current position is on a line comment symbol, skip past it and return
true.
sub TryToSkipLineComment #(indexRef, lineNumberRef)
{
my ($self, $indexRef, $lineNumberRef) = @_;
my $tokens = $self->Tokens();
if ($tokens-> eq '/' && $tokens-> eq '/')
{
$self->SkipRestOfLine($indexRef, $lineNumberRef);
return 1;
}
else
{ return undef; };
};
Function: TryToSkipMultilineComment
If the current position is on an opening comment symbol, skip past it and
return true.
sub TryToSkipMultilineComment #(indexRef, lineNumberRef)
{
my ($self, $indexRef, $lineNumberRef) = @_;
my $tokens = $self->Tokens();
if ($tokens-> eq '/' && $tokens-> eq '*')
{
$self->SkipUntilAfter($indexRef, $lineNumberRef, '*', '/');
return 1;
}
else
{ return undef; };
};
Function: TryToSkipPreprocessingDirective
If the current position is on a preprocessing directive, skip past it and
return true.
sub TryToSkipPreprocessingDirective #(indexRef, lineNumberRef)
{
my ($self, $indexRef, $lineNumberRef) = @_;
my $tokens = $self->Tokens();
if ($tokens-> eq '#' && $self->IsFirstLineToken($$indexRef))
{
$self->SkipRestOfLine($indexRef, $lineNumberRef);
return 1;
}
else
{ return undef; };
};
1;