diff --git a/CHANGES.txt b/CHANGES.txt index 4878986eb..0bb867b98 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -3,6 +3,7 @@ ANTLR v4 Honey Badger November 17, 2012 * .tokens files goes in output dir like parser file. +* added check: action in lexer rules must be last element of outermost alt November 11, 2012 diff --git a/tool/playground/T.g b/tool/playground/T.g index 86969ed2e..ba5a58d69 100644 --- a/tool/playground/T.g +++ b/tool/playground/T.g @@ -1,8 +1,8 @@ grammar T; -s : ~(ID|X)* X ; -b : ~(ID|X)*? X ; -A : 'a' ; -B : 'b' ; -X : 'x' ; -ID : 'id' ; +s : A ; + +A : {} 'a' ; + +B : ('x' {}|{}'y') {} ; + WS : [ \t\r\n]+ -> skip ; diff --git a/tool/src/org/antlr/v4/semantics/BasicSemanticChecks.java b/tool/src/org/antlr/v4/semantics/BasicSemanticChecks.java index 6a7641bf8..9efec92ec 100644 --- a/tool/src/org/antlr/v4/semantics/BasicSemanticChecks.java +++ b/tool/src/org/antlr/v4/semantics/BasicSemanticChecks.java @@ -30,6 +30,8 @@ package org.antlr.v4.semantics; import org.antlr.runtime.Token; +import org.antlr.runtime.tree.CommonTree; +import org.antlr.runtime.tree.Tree; import org.antlr.v4.misc.Utils; import org.antlr.v4.parse.ANTLRParser; import org.antlr.v4.parse.GrammarTreeVisitor; @@ -322,6 +324,28 @@ public class BasicSemanticChecks extends GrammarTreeVisitor { } } + @Override + protected void enterLexerElement(GrammarAST tree) { + if ( tree.getType() == ACTION ) { + // Make sure that action is last element in outer alt; here action + // and a2 are bad, but a3 is ok. + // (RULE A (BLOCK (ALT {action} 'a'))) + // (RULE B (BLOCK (ALT (BLOCK (ALT {a2} 'x') (ALT 'y')) {a3}))) + CommonTree alt = tree.parent; + CommonTree blk = alt.parent; + boolean outerMostAlt = blk.parent.getType() != RULE; + Tree rule = tree.getAncestor(RULE); + String fileName = tree.getToken().getInputStream().getSourceName(); + if ( !outerMostAlt || tree.getChildIndex() != alt.getChildCount()-1 ) { + g.tool.errMgr.grammarError(ErrorType.LEXER_ACTION_PLACEMENT_ISSUE, + fileName, + tree.getToken(), + rule.getChild(0).getText()); + + } + } + } + @Override public void label(GrammarAST op, GrammarAST ID, GrammarAST element) { switch (element.getType()) { diff --git a/tool/src/org/antlr/v4/tool/ErrorType.java b/tool/src/org/antlr/v4/tool/ErrorType.java index 2988023a4..4f1aa2bd9 100644 --- a/tool/src/org/antlr/v4/tool/ErrorType.java +++ b/tool/src/org/antlr/v4/tool/ErrorType.java @@ -153,6 +153,7 @@ public enum ErrorType { // WILDCARD_IN_PARSER(129, "wildcard '.' not allowed in parsers", ErrorSeverity.ERROR), LABEL_BLOCK_NOT_A_SET(130, "label assigned to a block which is not a set", ErrorSeverity.ERROR), EXPECTED_NON_GREEDY_WILDCARD_BLOCK(131, "greedy block () contains wildcard; the non-greedy syntax ()? may be preferred", ErrorSeverity.WARNING), + LEXER_ACTION_PLACEMENT_ISSUE(132, "action in lexer rule must be last element of outermost alt", ErrorSeverity.ERROR), /** Documentation comment is unterminated */ //UNTERMINATED_DOC_COMMENT(, "", ErrorSeverity.ERROR),