merging KvanTTT changes.

This commit is contained in:
Terence Parr 2015-12-08 11:38:08 -08:00
commit 99563b116e
9 changed files with 202 additions and 6 deletions

View File

@ -83,3 +83,4 @@ YYYY/MM/DD, github id, Full name, email
2015/10/21, martin-probst, Martin Probst, martin-probst@web.de
2015/10/21, hkff, Walid Benghabrit, walid.benghabrit@mines-nantes.fr
2015/11/25, abego, Udo Borkowski, ub@abego.org
2015/10/12, KvanTTT, Ivan Kochurkin, ivan.kochurkin@gmail.com

View File

@ -632,7 +632,7 @@ public class TestATNSerialization extends BaseTest {
"lexer grammar L;\n"+
"A : 'a'\n ;\n" +
"B : 'b';\n" +
"mode A;\n" +
"mode M;\n" +
"C : 'c';\n"+
"D : 'd';\n");
String expecting =

View File

@ -116,10 +116,25 @@ public class TestSymbolIssues extends BaseTest {
"warning(" + ErrorType.TOKEN_NAME_REASSIGNMENT.code + "): E.g4:3:4: token name A is already defined\n"
};
static String[] F = {
// INPUT
"lexer grammar F;\n" +
"A: 'a';\n" +
"mode M1;\n" +
"A1: 'a';\n" +
"mode M2;\n" +
"A2: 'a';\n" +
"M1: 'b';\n",
// YIELDS
"error(" + ErrorType.MODE_CONFLICTS_WITH_TOKEN.code + "): F.g4:3:0: mode M1 conflicts with token with same name\n"
};
@Test public void testA() { super.testErrors(A, false); }
@Test public void testB() { super.testErrors(B, false); }
@Test public void testD() { super.testErrors(D, false); }
@Test public void testE() { super.testErrors(E, false); }
@Test public void testF() { super.testErrors(F, false); }
@Test public void testStringLiteralRedefs() throws Exception {
String grammar =
@ -169,4 +184,43 @@ public class TestSymbolIssues extends BaseTest {
testErrors(test, false);
}
@Test public void testTokensModesChannelsDeclarationConflictsWithReserved() throws Exception {
String[] test = {
"lexer grammar L;\n" +
"channels { SKIP, HIDDEN, channel0 }\n" +
"A: 'a';\n" +
"mode MAX_CHAR_VALUE;\n" +
"MIN_CHAR_VALUE: 'a';\n" +
"mode DEFAULT_MODE;\n" +
"B: 'b';\n" +
"mode M;\n" +
"C: 'c';",
"error(" + ErrorType.RESERVED_RULE_NAME.code + "): L.g4:5:0: cannot declare a rule with reserved name MIN_CHAR_VALUE\n" +
"error(" + ErrorType.MODE_CONFLICTS_WITH_COMMON_CONSTANTS.code + "): L.g4:4:0: cannot use or declare mode with reserved name MAX_CHAR_VALUE\n" +
"error(" + ErrorType.CHANNEL_CONFLICTS_WITH_COMMON_CONSTANTS.code + "): L.g4:2:11: cannot use or declare channel with reserved name SKIP\n" +
"error(" + ErrorType.CHANNEL_CONFLICTS_WITH_COMMON_CONSTANTS.code + "): L.g4:2:17: cannot use or declare channel with reserved name HIDDEN\n"
};
testErrors(test, false);
}
@Test public void testTokensModesChannelsUsingConflictsWithReserved() throws Exception {
String[] test = {
"lexer grammar L;\n" +
"A: 'a' -> channel(SKIP);\n" +
"B: 'b' -> type(MORE);\n" +
"C: 'c' -> mode(SKIP);\n" +
"D: 'd' -> channel(HIDDEN);\n" +
"E: 'e' -> type(EOF);\n" +
"F: 'f' -> pushMode(DEFAULT_MODE);",
"error(" + ErrorType.CHANNEL_CONFLICTS_WITH_COMMON_CONSTANTS.code + "): L.g4:2:18: cannot use or declare channel with reserved name SKIP\n" +
"error(" + ErrorType.TOKEN_CONFLICTS_WITH_COMMON_CONSTANTS.code + "): L.g4:3:15: cannot use or declare token with reserved name MORE\n" +
"error(" + ErrorType.MODE_CONFLICTS_WITH_COMMON_CONSTANTS.code + "): L.g4:4:15: cannot use or declare mode with reserved name SKIP\n"
};
testErrors(test, false);
}
}

View File

@ -454,6 +454,33 @@ public class TestToolSyntaxErrors extends BaseTest {
super.testErrors(pair, true);
}
/**
* This is a regression test for antlr/antlr4#959 "NullPointerException".
* https://github.com/antlr/antlr4/issues/959
*/
@Test public void testNotAllowedEmptyStrings() {
String grammar =
"lexer grammar T;\n" +
"Error0: '''test''';\n" +
"Error1: '' 'test';\n" +
"Error2: 'test' '';\n" +
"Error3: '';\n" +
"NotError: ' ';";
String expected =
"error(" + ErrorType.EMPTY_STRINGS_NOT_ALLOWED.code + "): T.g4:2:8: string literals cannot be empty\n" +
"error(" + ErrorType.EMPTY_STRINGS_NOT_ALLOWED.code + "): T.g4:2:16: string literals cannot be empty\n" +
"error(" + ErrorType.EMPTY_STRINGS_NOT_ALLOWED.code + "): T.g4:3:8: string literals cannot be empty\n" +
"error(" + ErrorType.EMPTY_STRINGS_NOT_ALLOWED.code + "): T.g4:4:15: string literals cannot be empty\n" +
"error(" + ErrorType.EMPTY_STRINGS_NOT_ALLOWED.code + "): T.g4:5:8: string literals cannot be empty\n";
String[] pair = new String[] {
grammar,
expected
};
super.testErrors(pair, true);
}
/**
* This test ensures the {@link ErrorType#UNRECOGNIZED_ASSOC_OPTION} warning
* is produced as described in the documentation.

View File

@ -83,7 +83,7 @@ public class LexerATNFactory extends ParserATNFactory {
* actions, but are required during code generation for creating
* {@link LexerAction} instances that are usable by a lexer interpreter.
*/
protected static final Map<String, Integer> COMMON_CONSTANTS = new HashMap<String, Integer>();
public static final Map<String, Integer> COMMON_CONSTANTS = new HashMap<String, Integer>();
static {
COMMON_CONSTANTS.put("HIDDEN", Lexer.HIDDEN);
COMMON_CONSTANTS.put("DEFAULT_TOKEN_CHANNEL", Lexer.DEFAULT_TOKEN_CHANNEL);
@ -112,6 +112,10 @@ public class LexerATNFactory extends ParserATNFactory {
codegenTemplates = gen.getTemplates();
}
public static Set<String> getCommonConstants() {
return COMMON_CONSTANTS.keySet();
}
@Override
public ATN createATN() {
// BUILD ALL START STATES (ONE PER MODE)
@ -407,6 +411,7 @@ public class LexerATNFactory extends ParserATNFactory {
}
else if ("mode".equals(command) && arg != null) {
String modeName = arg.getText();
checkMode(modeName, arg.token);
Integer mode = getConstantValue(modeName, arg.getToken());
if (mode == null) {
return null;
@ -416,6 +421,7 @@ public class LexerATNFactory extends ParserATNFactory {
}
else if ("pushMode".equals(command) && arg != null) {
String modeName = arg.getText();
checkMode(modeName, arg.token);
Integer mode = getConstantValue(modeName, arg.getToken());
if (mode == null) {
return null;
@ -425,6 +431,7 @@ public class LexerATNFactory extends ParserATNFactory {
}
else if ("type".equals(command) && arg != null) {
String typeName = arg.getText();
checkToken(typeName, arg.token);
Integer type = getConstantValue(typeName, arg.getToken());
if (type == null) {
return null;
@ -434,6 +441,7 @@ public class LexerATNFactory extends ParserATNFactory {
}
else if ("channel".equals(command) && arg != null) {
String channelName = arg.getText();
checkChannel(channelName, arg.token);
Integer channel = getConstantValue(channelName, arg.getToken());
if (channel == null) {
return null;
@ -446,6 +454,23 @@ public class LexerATNFactory extends ParserATNFactory {
}
}
protected void checkMode(String modeName, Token token) {
if (!modeName.equals("DEFAULT_MODE") && COMMON_CONSTANTS.containsKey(modeName)) {
g.tool.errMgr.grammarError(ErrorType.MODE_CONFLICTS_WITH_COMMON_CONSTANTS, g.fileName, token, token.getText());
}
}
protected void checkToken(String tokenName, Token token) {
if (!tokenName.equals("EOF") && COMMON_CONSTANTS.containsKey(tokenName)) {
g.tool.errMgr.grammarError(ErrorType.TOKEN_CONFLICTS_WITH_COMMON_CONSTANTS, g.fileName, token, token.getText());
}
}
protected void checkChannel(String channelName, Token token) {
if (!channelName.equals("HIDDEN") && !channelName.equals("DEFAULT_TOKEN_CHANNEL") && COMMON_CONSTANTS.containsKey(channelName)) {
g.tool.errMgr.grammarError(ErrorType.CHANNEL_CONFLICTS_WITH_COMMON_CONSTANTS, g.fileName, token, token.getText());
}
}
protected Integer getConstantValue(String name, Token token) {
if (name == null) {

View File

@ -491,6 +491,14 @@ public class BasicSemanticChecks extends GrammarTreeVisitor {
label.getText());
}
@Override
protected void enterTerminal(GrammarAST tree) {
String text = tree.getText();
if (text.equals("''")) {
g.tool.errMgr.grammarError(ErrorType.EMPTY_STRINGS_NOT_ALLOWED, g.fileName, tree.token);
}
}
/** Check option is appropriate for grammar, rule, subrule */
boolean checkOptions(GrammarAST parent,
Token optionID,

View File

@ -31,6 +31,7 @@
package org.antlr.v4.semantics;
import org.antlr.v4.analysis.LeftRecursiveRuleTransformer;
import org.antlr.v4.automata.LexerATNFactory;
import org.antlr.v4.parse.ANTLRParser;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.misc.Pair;
@ -128,6 +129,8 @@ public class SemanticPipeline {
collector.tokenIDRefs, collector.terminals);
}
symcheck.checkForModeConflicts(g);
assignChannelTypes(g, collector.channelDefs);
// CHECK RULE REFS NOW (that we've defined rules in grammar)
@ -287,6 +290,10 @@ public class SemanticPipeline {
g.tool.errMgr.grammarError(ErrorType.CHANNEL_CONFLICTS_WITH_TOKEN, g.fileName, channel.token, channelName);
}
if (LexerATNFactory.COMMON_CONSTANTS.containsKey(channelName)) {
g.tool.errMgr.grammarError(ErrorType.CHANNEL_CONFLICTS_WITH_COMMON_CONSTANTS, g.fileName, channel.token, channelName);
}
if (outermost instanceof LexerGrammar) {
LexerGrammar lexerGrammar = (LexerGrammar)outermost;
if (lexerGrammar.modes.containsKey(channelName)) {

View File

@ -30,7 +30,9 @@
package org.antlr.v4.semantics;
import org.antlr.v4.automata.LexerATNFactory;
import org.antlr.v4.parse.ANTLRParser;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.tool.Alternative;
import org.antlr.v4.tool.Attribute;
import org.antlr.v4.tool.AttributeDict;
@ -39,6 +41,7 @@ import org.antlr.v4.tool.ErrorType;
import org.antlr.v4.tool.Grammar;
import org.antlr.v4.tool.LabelElementPair;
import org.antlr.v4.tool.Rule;
import org.antlr.v4.tool.LexerGrammar;
import org.antlr.v4.tool.ast.GrammarAST;
import java.util.Collection;
@ -67,7 +70,7 @@ public class SymbolChecks {
protected final Set<String> reservedNames = new HashSet<String>();
{
reservedNames.add("EOF");
reservedNames.addAll(LexerATNFactory.getCommonConstants());
}
public SymbolChecks(Grammar g, SymbolCollector collector) {
@ -269,6 +272,23 @@ public class SymbolChecks {
}
}
public void checkForModeConflicts(Grammar g) {
if (g.isLexer()) {
LexerGrammar lexerGrammar = (LexerGrammar)g;
for (String modeName : lexerGrammar.modes.keySet()) {
if (!modeName.equals("DEFAULT_MODE") && reservedNames.contains(modeName)) {
Rule rule = ((Collection<Rule>)lexerGrammar.modes.get(modeName)).iterator().next();
g.tool.errMgr.grammarError(ErrorType.MODE_CONFLICTS_WITH_COMMON_CONSTANTS, g.fileName, rule.ast.parent.getToken(), modeName);
}
if (g.getTokenType(modeName) != Token.INVALID_TYPE) {
Rule rule = ((Collection<Rule>)lexerGrammar.modes.get(modeName)).iterator().next();
g.tool.errMgr.grammarError(ErrorType.MODE_CONFLICTS_WITH_TOKEN, g.fileName, rule.ast.parent.getToken(), modeName);
}
}
}
}
// CAN ONLY CALL THE TWO NEXT METHODS AFTER GRAMMAR HAS RULE DEFS (see semanticpipeline)
public void checkRuleArgs(Grammar g, List<GrammarAST> rulerefs) {

View File

@ -957,6 +957,60 @@ public enum ErrorType {
CHANNELS_BLOCK_IN_COMBINED_GRAMMAR(164, "custom channels are not supported in combined grammars", ErrorSeverity.ERROR),
NONCONFORMING_LR_RULE(169, "rule <arg> is left recursive but doesn't conform to a pattern ANTLR can handle", ErrorSeverity.ERROR),
/**
* Compiler Error 170.
*
* <pre>
* mode M1;
* A1: 'a'; // ok
* mode M2;
* A2: 'a'; // ok
* M1: 'b'; // error 170
* </pre>
*
* <p>mode <em>name</em> conflicts with token with same name</p>
*/
MODE_CONFLICTS_WITH_TOKEN(170, "mode <arg> conflicts with token with same name", ErrorSeverity.ERROR),
/**
* Compiler Error 171.
*
* <p>can not use or declare token with reserved name</p>
*
* <p>Reserved names: HIDDEN, DEFAULT_TOKEN_CHANNEL, SKIP, MORE, MAX_CHAR_VALUE, MIN_CHAR_VALUE.
*
* <p>Can be used but cannot be declared: EOF</p>
*/
TOKEN_CONFLICTS_WITH_COMMON_CONSTANTS(171, "cannot use or declare token with reserved name <arg>", ErrorSeverity.ERROR),
/**
* Compiler Error 172.
*
* <p>can not use or declare channel with reserved name</p>
*
* <p>Reserved names: DEFAULT_MODE, SKIP, MORE, EOF, MAX_CHAR_VALUE, MIN_CHAR_VALUE.
*
* <p>Can be used but cannot be declared: HIDDEN, DEFAULT_TOKEN_CHANNEL</p>
*/
CHANNEL_CONFLICTS_WITH_COMMON_CONSTANTS(172, "cannot use or declare channel with reserved name <arg>", ErrorSeverity.ERROR),
/**
* Compiler Error 173.
*
* <p>can not use or declare mode with reserved name</p>
*
* <p>Reserved names: HIDDEN, DEFAULT_TOKEN_CHANNEL, SKIP, MORE, MAX_CHAR_VALUE, MIN_CHAR_VALUE.
*
* <p>Can be used and cannot declared: DEFAULT_MODE</p>
*/
MODE_CONFLICTS_WITH_COMMON_CONSTANTS(173, "cannot use or declare mode with reserved name <arg>", ErrorSeverity.ERROR),
/**
* Compiler Error 174.
*
* <p>empty strings not allowed</p>
*
* <pre>A: '''test''';</pre>
* <pre>B: '';</pre>
* <pre>C: 'test' '';</pre>
*/
EMPTY_STRINGS_NOT_ALLOWED(174, "string literals cannot be empty", ErrorSeverity.ERROR),
/*
* Backward incompatibility errors