/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* See LICENSE.txt included in this distribution for the specific
* language governing permissions and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at LICENSE.txt.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Cross reference a PHP file
*/
%%
%int
%{
// TODO move this into an include file when bug #16053 is fixed
static {
"string", "integer", "int", "boolean", "bool", "float", "double",
"object", "mixed", "array", "resource", "void", "NULL", "callback",
"FALSE", "TRUE", "self", "callable"
}
));
}
}
}
}
%}
WhiteSpace = [ \t]+
EOL = \r|\n|\r\n
URIChar = [\?\+\%\&\:\/\.\@\_\;\=\$\,\-\!\~\*\\]
BinaryNumber = 0[b|B][01]+
OctalNumber = 0[0-7]+
ClosingTag = "?>"
|"object"|"bool"|"boolean"|"unset"
SingleQuoteEscapeSequences = \\ [\\\']
DocPreviousChar = "*" | {WhiteSpace}
//does not supported nested type expressions like ((array|integer)[]|boolean)[]
//that would require additional states
DocType = {IndividualDocType} (\| {IndividualDocType})*
IndividualDocType = ({SimpleDocType} "[]"? | ( \( {SimpleDocType} "[]"? ( \| {SimpleDocType} "[]"? )* \)\[\] ))
SimpleDocType = {Identifier}
DocParamWithType = "return" | "throws" | "throw" | "var" | "see" //"see" can take a URL
DocParamWithTypeAndName = "param" | "global" | "property" | "property-read"
| "property-write"
DocParamWithName = "uses"
DocInlineTags = "internal" | "inheritDoc" | "link" | "example"
//method needs special treatment
HtmlNameStart = [a-zA-Z_\u00C0-\u10FFFFFF]
HtmlName = {HtmlNameStart} ({HtmlNameStart} | [\-.0-9\u00B7])*
%state TAG_NAME AFTER_TAG_NAME ATTRIBUTE_NOQUOTE ATTRIBUTE_SINGLE ATTRIBUTE_DOUBLE HTMLCOMMENT
%state IN_SCRIPT STRING SCOMMENT HEREDOC NOWDOC COMMENT QSTRING BACKQUOTE STRINGEXPR STRINGVAR
%state DOCCOMMENT DOCCOM_TYPE_THEN_NAME DOCCOM_NAME DOCCOM_TYPE
%%
<YYINITIAL> { //HTML
"<" | "</" { out.write(Util.htmlize(yytext())); yypush(TAG_NAME, ""); }
"<!--" {
out.write("<span class=\"c\"><!--"); spans.push("c");
yybegin(HTMLCOMMENT);
}
}
<TAG_NAME> {
{HtmlName} {
out.write("<span class=\"n\">");
out.write(yytext());
out.write("</span>");
yybegin(AFTER_TAG_NAME);
}
{HtmlName}:{HtmlName} {
out.write("<span class=\"n\">");
int i = 0;
while (yycharat(i) != ':') i++;
out.write(yytext().substring(0,i));
out.write("</span>:<span class=\"n\">");
out.write(yytext().substring(i + 1));
out.write("</span>");
yybegin(AFTER_TAG_NAME);
}
}
<AFTER_TAG_NAME> {
{HtmlName} {
out.write("<strong>");
out.write(yytext()); //attribute
out.write("</strong>");
}
"=" {WhiteSpace}* (\" | \')? {
char attributeDelim = yycharat(yylength()-1);
out.write("=<span class=\"s\">"); spans.push("s");
out.write(yytext().substring(1));
if (attributeDelim == '\'') {
} else if (attributeDelim == '"') {
} else {
}
}
}
<TAG_NAME, AFTER_TAG_NAME> {
}
{OpeningTag} {
}
{WhiteSpace}* {EOL} {
startNewLine();
yypop();
}
{WhiteSpace} {
yypop();
}
}
<ATTRIBUTE_DOUBLE, ATTRIBUTE_SINGLE> {
{WhiteSpace}* {EOL} {
startNewLine();
}
}
{OpeningTag} {
}
}
<HTMLCOMMENT> {
"-->" {
}
{WhiteSpace}* {EOL} {
startNewLine();
}
{OpeningTag} {
}
}
<IN_SCRIPT> {
"$" {Identifier} {
//we ignore keywords if the identifier starts with one of variable chars
}
{Identifier} {
}
int i = 1, j;
j = i + 1;
}
b? \" {
yypush(STRING, "");
if (yycharat(0) == 'b') { out.write('b'); }
}
b? \' {
yypush(QSTRING, "");
if (yycharat(0) == 'b') { out.write('b'); }
out.write("<span class=\"s\">\'"); spans.push("s");
}
` { yypush(BACKQUOTE, ""); out.write("<span class=\"s\">`"); spans.push("s"); }
b? "<<<" {WhiteSpace}* ({Identifier} | (\'{Identifier}\') | (\"{Identifier}\")){EOL} {
if (yycharat(0) == 'b') { out.write('b'); }
out.write("<<<");
int i = yycharat(0) == 'b' ? 4 : 3, j = yylength()-1;
while (isTabOrSpace(i)) {
out.write(yycharat(i++));
}
while (yycharat(j) == '\n' || yycharat(j) == '\r') { j--; }
} else {
}
startNewLine();
}
"/**" { yypush(DOCCOMMENT, ""); out.write("<span class=\"c\">/*"); yypushback(1); spans.push("c"); }
\} {
yypop(); //may pop STRINGEXPR/HEREDOC/BACKQUOTE
/* we don't pop unconditionally because we can exit a <?php block with
* with open braces and we discard the information about the number of
* open braces when exiting the block (see the action for {ClosingTag}
* below. An alternative would be keeping two stacks -- one for HTML
* and another for PHP. The PHP scanner only needs one stack because
* it doesn't need to keep state about the HTML */
}
{ClosingTag} {
while (!isHtmlState(yystate()))
yypop();
}
} //end of IN_SCRIPT
<STRING> {
}
<BACKQUOTE> {
}
"\\{" {
}
}
"$" {
}
"${" {
}
/* ${ is different from {$ -- for instance {$foo->bar[1]} is valid
* but ${foo->bar[1]} is not. ${ only enters the full blown scripting state
* when {Identifer}[ is found (see the PHP scanner). Tthe parser seems to
* put more restrictions on the {$ scripting mode than on the
* "${" {Identifer} "[" scripting mode, but that's not relevant here */
"{$" {
yypushback(1);
}
}
<QSTRING> {
}
\' { out.write("\"</span>"); spans.pop(); yypop(); }
}
<HEREDOC, NOWDOC>^{Identifier} ";"? {EOL} {
int i = yylength() - 1;
boolean hasSemi = false;
while (yycharat(i) == '\n' || yycharat(i) == '\r') { i--; }
if (yycharat(i) == ';') { hasSemi = true; i--; }
if (yytext().substring(0, i+1).equals(docLabels.peek())) {
String text = docLabels.pop();
yypop();
out.write("</span><span class=\"b\">"); spans.pop();
out.write(text);
out.write("</span>");
if (hasSemi) out.write(";");
startNewLine();
} else {
out.write(yytext().substring(0,i+1));
if (hasSemi) out.write(";");
startNewLine();
}
}
<STRING, QSTRING, BACKQUOTE, HEREDOC, NOWDOC>{WhiteSpace}* {EOL} {
out.write("</span>"); spans.pop();
startNewLine();
out.write("<span class=\"s\">"); spans.push("s");
}
<STRINGVAR> {
{Identifier} { writeSymbol(yytext(), null, yyline, false); }
\[ {Number} \] {
out.write("[<span class=\"n\">");
out.write(yytext().substring(1, yylength()-1));
out.write("</span>]");
yypop(); //because "$arr[0][1]" is the same as $arr[0] . "[1]"
}
\[ {Identifier} \] {
//then the identifier is actually a string!
out.write("[<span class=\"s\">");
out.write(yytext().substring(1, yylength()-1));
out.write("</span>]");
yypop();
}
\[ "$" {Identifier} \] {
out.write("[$");
writeSymbol(yytext().substring(2, yylength()-1), null, yyline, false);
out.write("]");
yypop();
}
"->" {Identifier} {
out.write("->");
writeSymbol(yytext().substring(2), null, yyline, false);
yypop(); //because "$arr->a[0]" is the same as $arr->a . "[0]"
}
. | \n { yypushback(1); yypop(); }
}
<STRINGEXPR> {
{Identifier} {
writeSymbol(yytext(), null, yyline, false);
}
\} { out.write('}'); yypop(); }
* STRINGEXPR */
}
<SCOMMENT> {
{ClosingTag} {
while (!isHtmlState(yystate()))
yypop();
}
{WhiteSpace}* {EOL} {
startNewLine();
yypop();
}
}
<DOCCOMMENT> {
}
}
}
writeDocTag();
}
}
{WhiteSpace}+ {DocType} {
int i = 0;
int j = i;
while (i < yylength()) {
//skip over [], |, ( and )
char c;
|| c == '|' || c == '(' || c == ')')) {
i++;
}
j = i;
&& c != '[') { j++; }
i = j;
}
}
}
<DOCCOM_NAME> {
int i = 0;
}
}
<COMMENT, DOCCOMMENT> {
{WhiteSpace}* {EOL} {
startNewLine();
}
}
<YYINITIAL, TAG_NAME, AFTER_TAG_NAME, ATTRIBUTE_NOQUOTE, ATTRIBUTE_DOUBLE, ATTRIBUTE_SINGLE, HTMLCOMMENT, IN_SCRIPT, STRING, QSTRING, BACKQUOTE, HEREDOC, NOWDOC, SCOMMENT, COMMENT, DOCCOMMENT, STRINGEXPR, STRINGVAR> {
{WhiteSpace}* {EOL} {
startNewLine();
}
{WhiteSpace} {
}
. { writeUnicodeChar(yycharat(0)); }
}
<YYINITIAL, HTMLCOMMENT, SCOMMENT, COMMENT, DOCCOMMENT, STRING, QSTRING, BACKQUOTE, HEREDOC, NOWDOC> {
{Path}
{File}
{
{
{
}
}