1 ###############################################################################
3 # Class: NaturalDocs::Languages::PLSQL
5 ###############################################################################
7 # A subclass to handle the language variations of PL/SQL.
9 ###############################################################################
11 # This file is part of Natural Docs, which is Copyright © 2003-2010 Greg Valure
12 # Natural Docs is licensed under version 3 of the GNU Affero General Public License (AGPL)
13 # Refer to License.txt for the complete details
18 package NaturalDocs::Languages::PLSQL;
20 use base 'NaturalDocs::Languages::Simple';
24 # Function: OnPrototypeEnd
26 # Microsoft's SQL specifies parameters as shown below.
28 # > CREATE PROCEDURE Test @as int, @foo int AS ...
30 # Having a parameter @is or @as is perfectly valid even though those words are also used to end the prototype. We need to
31 # ignore text-based enders preceded by an at sign. Also note that it does not have parenthesis for parameter lists. We need to
32 # skip all commas if the prototype doesn't have parenthesis but does have @ characters.
34 # Identifiers such as function names may contain the characters $, #, and _, so if "as" or "is" appears directly after one of them
35 # we need to ignore the ender there as well.
37 # > FUNCTION Something_is_something ...
41 # type - The <TopicType> of the prototype.
42 # prototypeRef - A reference to the prototype so far, minus the ender in dispute.
43 # ender - The ender symbol.
47 # ENDER_ACCEPT - The ender is accepted and the prototype is finished.
48 # ENDER_IGNORE - The ender is rejected and parsing should continue. Note that the prototype will be rejected as a whole
49 # if all enders are ignored before reaching the end of the code.
50 # ENDER_ACCEPT_AND_CONTINUE - The ender is accepted so the prototype may stand as is. However, the prototype might
51 # also continue on so continue parsing. If there is no accepted ender between here and
52 # the end of the code this version will be accepted instead.
53 # ENDER_REVERT_TO_ACCEPTED - The expedition from ENDER_ACCEPT_AND_CONTINUE failed. Use the last accepted
54 # version and end parsing.
56 sub OnPrototypeEnd #(type, prototypeRef, ender)
58 my ($self, $type, $prototypeRef, $ender) = @_;
60 # _ should be handled already.
61 if ($ender =~ /^[a-z]+$/i && substr($$prototypeRef, -1) =~ /^[\@\$\#]$/)
62 { return ::ENDER_IGNORE(); }
64 elsif ($type eq ::TOPIC_FUNCTION() && $ender eq ',')
66 if ($$prototypeRef =~ /^[^\(]*\@/)
67 { return ::ENDER_IGNORE(); }
69 { return ::ENDER_ACCEPT(); };
73 { return ::ENDER_ACCEPT(); };
78 # Function: ParsePrototype
80 # Overridden to handle Microsoft's parenthesisless version. Otherwise just throws to the parent.
84 # type - The <TopicType>.
85 # prototype - The text prototype.
89 # A <NaturalDocs::Languages::Prototype> object.
91 sub ParsePrototype #(type, prototype)
93 my ($self, $type, $prototype) = @_;
95 my $noParenthesisParameters = ($type eq ::TOPIC_FUNCTION() && $prototype =~ /^[^\(]*\@/);
97 if ($prototype !~ /\(.*[^ ].*\)/ && !$noParenthesisParameters)
98 { return $self->SUPER::ParsePrototype($type, $prototype); };
102 my ($beforeParameters, $afterParameters, $isAfterParameters);
104 if ($noParenthesisParameters)
106 ($beforeParameters, $prototype) = split(/\@/, $prototype, 2);
107 $prototype = '@' . $prototype;
110 my @tokens = $prototype =~ /([^\(\)\[\]\{\}\<\>\'\"\,]+|.)/g;
117 foreach my $token (@tokens)
119 if ($isAfterParameters)
120 { $afterParameters .= $token; }
122 elsif ($symbolStack[-1] eq '\'' || $symbolStack[-1] eq '"')
124 if ($noParenthesisParameters || $symbolStack[0] eq '(')
125 { $parameter .= $token; }
127 { $beforeParameters .= $token; };
129 if ($token eq $symbolStack[-1])
130 { pop @symbolStack; };
133 elsif ($token =~ /^[\(\[\{\<\'\"]$/)
135 if ($noParenthesisParameters || $symbolStack[0] eq '(')
136 { $parameter .= $token; }
138 { $beforeParameters .= $token; };
140 push @symbolStack, $token;
143 elsif ( ($token eq ')' && $symbolStack[-1] eq '(') ||
144 ($token eq ']' && $symbolStack[-1] eq '[') ||
145 ($token eq '}' && $symbolStack[-1] eq '{') ||
146 ($token eq '>' && $symbolStack[-1] eq '<') )
148 if (!$noParenthesisParameters && $token eq ')' && scalar @symbolStack == 1 && $symbolStack[0] eq '(')
150 $afterParameters .= $token;
151 $isAfterParameters = 1;
154 { $parameter .= $token; };
159 elsif ($token eq ',')
161 if (!scalar @symbolStack)
163 if ($noParenthesisParameters)
165 push @parameterLines, $parameter . $token;
170 $beforeParameters .= $token;
175 if (scalar @symbolStack == 1 && $symbolStack[0] eq '(' && !$noParenthesisParameters)
177 push @parameterLines, $parameter . $token;
182 $parameter .= $token;
189 if ($noParenthesisParameters || $symbolStack[0] eq '(')
190 { $parameter .= $token; }
192 { $beforeParameters .= $token; };
196 push @parameterLines, $parameter;
198 foreach my $item (\$beforeParameters, \$afterParameters)
204 my $prototypeObject = NaturalDocs::Languages::Prototype->New($beforeParameters, $afterParameters);
207 # Parse the actual parameters.
209 foreach my $parameterLine (@parameterLines)
211 $prototypeObject->AddParameter( $self->ParseParameterLine($parameterLine) );
214 return $prototypeObject;
219 # Function: ParseParameterLine
221 # Parses a prototype parameter line and returns it as a <NaturalDocs::Languages::Prototype::Parameter> object.
223 sub ParseParameterLine #(line)
225 my ($self, $line) = @_;
230 my @tokens = $line =~ /([^\(\)\[\]\{\}\<\>\'\"\:\=\ ]+|\:\=|.)/g;
232 my ($name, $type, $defaultValue, $defaultValuePrefix, $inType, $inDefaultValue);
237 foreach my $token (@tokens)
240 { $defaultValue .= $token; }
242 elsif ($symbolStack[-1] eq '\'' || $symbolStack[-1] eq '"')
247 { $name .= $token; };
249 if ($token eq $symbolStack[-1])
250 { pop @symbolStack; };
253 elsif ($token =~ /^[\(\[\{\<\'\"]$/)
258 { $name .= $token; };
260 push @symbolStack, $token;
263 elsif ( ($token eq ')' && $symbolStack[-1] eq '(') ||
264 ($token eq ']' && $symbolStack[-1] eq '[') ||
265 ($token eq '}' && $symbolStack[-1] eq '{') ||
266 ($token eq '>' && $symbolStack[-1] eq '<') )
271 { $name .= $token; };
276 elsif ($token eq ' ')
280 elsif (!scalar @symbolStack)
283 { $name .= $token; };
286 elsif ($token eq ':=' || $token eq '=')
288 if (!scalar @symbolStack)
290 $defaultValuePrefix = $token;
296 { $name .= $token; };
304 { $name .= $token; };
308 foreach my $part (\$type, \$defaultValue)
313 return NaturalDocs::Languages::Prototype::Parameter->New($type, undef, $name, undef, $defaultValue, $defaultValuePrefix);
317 sub TypeBeforeParameter