options { USER_CHAR_STREAM = true; NODE_USES_PARSER=true; UNICODE_INPUT=true; FORCE_LA_CHECK = false; IGNORE_CASE = true; STATIC = false; MULTI=true; VISITOR=true; TRACK_TOKENS = true; } PARSER_BEGIN(VfParser) package net.sourceforge.pmd.lang.vf.ast; import net.sourceforge.pmd.lang.ast.CharStream; import net.sourceforge.pmd.lang.ast.TokenMgrError; public class VfParser { /** * Counter used to keep track of unclosed tags */ private OpenTagRegister tagRegister = new OpenTagRegister(); /** * Return the contents of a quote. * @param quote String - starting and ending with " or ' * @return String a substring of quote: quote without the first and list * character. */ private static String quoteContent(String quote) { return quote.substring(1, quote.length()-1); } /** * Return the contents of a EL expression. * @param expression String - starting with ${ or #{ and ending with } * @return String a substring of expression: expression without the first two and list * characters. */ private static String expressionContent(String expression) { return expression.substring(2, expression.length()-1).trim(); } } PARSER_END(VfParser) /** ************************* VF LEXICON **************************** */ <*> TOKEN : { <#ALPHA_CHAR: [ "\u0024", "\u0041"-"\u005a", "\u005f", "\u0061"-"\u007a", "\u00c0"-"\u00d6", "\u00d8"-"\u00f6", "\u00f8"-"\u00ff" ] > | <#NUM_CHAR: [ "\u0030"-"\u0039" ] > | <#ALPHANUM_CHAR: ( | ) > | <#IDENTIFIER_CHAR: ( | [ "_", "-", ".", ":" ] ) > | <#IDENTIFIER: ()* > | <#XMLNAME: ( | "_" | ":") ()* > | <#QUOTED_STRING_NO_BREAKS: ( "'" ( ~["'", "\r", "\n"] )* "'" ) | ( "\"" ( ~["\"", "\r", "\n"] )* "\"" ) > | <#QUOTED_STRING: ( "'" ( ~["'"] )* "'" ) | ( "\"" ( ~["\""] )* "\"" ) > | <#WHITESPACE: ( " " | "\t" | "\n" | "\r" ) > | <#NEWLINE: ( "\r\n" | "\r" | "\n" ) > | <#QUOTE: ( "'" | "\"" )> | <#NO_WHITESPACE_OR_LT_OR_DOLLAR: (~[" ", "\t", "\n", "\r", "<", "$", "#"])> | <#NO_BANG: (~["!"])> | <#OPENBRACE: ("{") > | <#NO_LT_OR_OPENBRACE: (~["<","{"])> | <#NO_ENDTAG_START: (~["<"]~["/"]) > | <#TEXT_IN_EL: (~["}", "'", "\""])+ > } SKIP : { < ()+ > } SPECIAL_TOKEN : { < ()+ > } TOKEN : { : StartTagState | : StartTagState | : CommentState | : StartTagState | : DocTypeState | : CDataState | : InTagState } TOKEN : { | )* "}" ) > | |)+ > } TOKEN : { )+ > } TOKEN: { ) > : DocTypeExternalIdState } TOKEN: { | | " > : AfterTagState | ) > } TOKEN : { | ") > : AfterTagState } TOKEN : { > : InTagState | : DEFAULT } TOKEN : { > | " > : AfterTagState | " | "!>") > : AfterTagState | " > : AfterTagState | : AttrValueBetweenSingleQuotesState | : AttrValueBetweenDoubleQuotesState | { input_stream.backup(1);} : AttrValueNoQuotesState | : InTagState //support for empty attributes } TOKEN: { | )* "}" > } TOKEN : { : InTagState | } TOKEN : { : InTagState | } TOKEN : { : InTagState | } TOKEN : { < COMMENT_END: ("--" (" ")* ">" | "->") > : AfterTagState | < COMMENT_TEXT: (~[]) > } TOKEN : { | " | "" | ""> : AfterTagState } /** ************************* VF GRAMMAR **************************** */ /** * The root of the AST of a VF. */ ASTCompilationUnit CompilationUnit() : {} { Prolog() Content() { return jjtThis; } } /** * The optional prolog of a VF, including (xml) declarations and DTD. */ void Prolog() #void : {} { ( LOOKAHEAD( ( CommentTag() )* Declaration() ) ( CommentTag() )* Declaration() )? ( LOOKAHEAD( ( CommentTag() )* DoctypeDeclaration() ) ( CommentTag() )* DoctypeDeclaration() )? } /** * Everything between a start-tag and the corresponding end-tag of an element (if an end tag exists). */ void Content() #void : {} { ( Text() | ContentElement() )* } /** * A single (non-text) element that can occur between a start-tag and end-tag of an element. * */ void ContentElement() #void : {} { ( CommentTag() | Element() | CData() | HtmlScript() ) } /** * This production groups all characters between two tags, where * tag is an xml-tag "<...>" or CDATA "<![CDATA[...]]>". * Text consists of unparsed text and/or Expression Language expressions. */ void Text() : { StringBuffer content = new StringBuffer(); String tmp; } { ( tmp = UnparsedText() { content.append(tmp); } | tmp = ElExpression() { content.append(tmp); } )+ {jjtThis.setImage(content.toString());} } String UnparsedText() : { Token t; } { t = { jjtThis.setImage(t.image); return t.image; } } String UnparsedTextNoWhitespace() #UnparsedText : { Token t;} { ( t = ) { jjtThis.setImage(t.image); return t.image; } } /** * Text that contains no single quotes, and that does not contain the start * of a EL expression. */ String UnparsedTextNoSingleQuotes() #UnparsedText : { Token t; } { t = { jjtThis.setImage(t.image); return t.image; } } /** * Text that contains no double quotes, and that does not contain the start * of a EL expression. */ String UnparsedTextNoDoubleQuotes() #UnparsedText : { Token t; } { t = { jjtThis.setImage(t.image); return t.image; } } /** * An EL expression, not within an attribute value. */ String ElExpression() : { Token t; } { t = { jjtThis.setImage(expressionContent(t.image)); return t.image; } } String ElExpressionInAttribute() #ElExpression : { Token t; } { t = { jjtThis.setImage(expressionContent(t.image)); return t.image; } } void CData() : { StringBuffer content = new StringBuffer(); Token t; } { ( t = { content.append(t.image); } )* { jjtThis.setImage(content.toString()); } } /** * A XML element, either with a single empty tag, or with a starting and closing tag * with optional contained content. */ void Element() : { Token startTag; Token endTag; String tagName; } { ( ( startTag = { tagName = startTag.image; jjtThis.setName(tagName); tagRegister.openTag(jjtThis); } ) (Attribute())* ( ( { jjtThis.setEmpty(false);} (Content()) ( endTag = {tagRegister.closeTag(endTag.image);} )? ) | ( { jjtThis.setEmpty(true); jjtThis.setUnclosed(false); } ) ) ) } void Attribute() : { Token t; } { t = { jjtThis.setName(t.image); } ( AttributeValue() ) } /** * The value of an attribute of an element. * EL expressions * are parsed as sub-nodes of the AttributeValue node. */ void AttributeValue() : { StringBuffer content = new StringBuffer(); String tmp; Token t = null ; } { ( ( ( ( tmp = UnparsedTextNoDoubleQuotes() | tmp = QuoteIndependentAttributeValueContent() ) { content.append(tmp); } )* ( ) ) | ( ( ( tmp = UnparsedTextNoSingleQuotes() | tmp = QuoteIndependentAttributeValueContent() ) { content.append(tmp); } )* ( ) ) | ( ( ( tmp = UnparsedTextNoWhitespace() | tmp = QuoteIndependentAttributeValueContent() ) { content.append(tmp); } )* ( ) ) | ) { jjtThis.setImage( content.toString() ); } } /** * Partial content of an attribute value that can contain all quotes. * This groups EL expressions. */ String QuoteIndependentAttributeValueContent() #void : { String tmp; } { ( tmp = ElExpressionInAttribute() ) { return tmp; } } void CommentTag() : { StringBuffer content = new StringBuffer(); Token t; } { ( t = { content.append(t.image); } )* { jjtThis.setImage(content.toString().trim()); } } void Declaration() : { Token t; } { t = { jjtThis.setName(t.image); } (Attribute())* } void DoctypeDeclaration() : { Token t; } { t = { jjtThis.setName(t.image); } ()? (DoctypeExternalId() ()?)? } void DoctypeExternalId() : { Token systemLiteral; Token pubIdLiteral; } { ( systemLiteral = { jjtThis.setUri(quoteContent(systemLiteral.image)); } ) | ( pubIdLiteral = { jjtThis.setPublicId(quoteContent(pubIdLiteral.image)); } systemLiteral = { jjtThis.setUri(quoteContent(systemLiteral.image)); } ) } void HtmlScript() : { StringBuffer content = new StringBuffer(); String tagName; Token t; } { {} (Attribute() )* {} ( ( {token_source.SwitchTo(HtmlScriptContentState);} (t = { content.append(t.image); })* { jjtThis.setImage(content.toString().trim());} ) | ( ) ) }