forked from jasder/antlr
This is one implementation addressing issue 160.
Change to support import of lexer grammars containing modes into other lexer grammars. The semantics for this are, * sets of channels from all grammars are merged * rules of modes found in an imported grammar which are in the root grammar are merged into the root grammar mode. * modes which are not in the root grammar are added to the root grammar, excluding modes which become empty due to a re-definition of rules in the root grammar.
This commit is contained in:
parent
e158824ca8
commit
d02844c813
|
@ -61,6 +61,131 @@ public class TestCompositeGrammars extends BaseJavaToolTest {
|
||||||
assertEquals(0, equeue.size());
|
assertEquals(0, equeue.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test public void testImportIntoLexerGrammar() throws Exception {
|
||||||
|
BaseRuntimeTest.mkdir(tmpdir);
|
||||||
|
|
||||||
|
String master =
|
||||||
|
"lexer grammar M;\n" +
|
||||||
|
"import S;\n" +
|
||||||
|
"A : 'a';\n" +
|
||||||
|
"B : 'b';\n";
|
||||||
|
writeFile(tmpdir, "M.g4", master);
|
||||||
|
|
||||||
|
String slave =
|
||||||
|
"lexer grammar S;\n" +
|
||||||
|
"C : 'c';\n";
|
||||||
|
writeFile(tmpdir, "S.g4", slave);
|
||||||
|
|
||||||
|
ErrorQueue equeue = BaseRuntimeTest.antlrOnString(tmpdir, "Java", "M.g4", false, "-lib", tmpdir);
|
||||||
|
assertEquals(0, equeue.errors.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test public void testImportModesIntoLexerGrammar() throws Exception {
|
||||||
|
BaseRuntimeTest.mkdir(tmpdir);
|
||||||
|
|
||||||
|
String master =
|
||||||
|
"lexer grammar M;\n" +
|
||||||
|
"import S;\n" +
|
||||||
|
"A : 'a' -> pushMode(X);\n" +
|
||||||
|
"B : 'b';\n";
|
||||||
|
writeFile(tmpdir, "M.g4", master);
|
||||||
|
|
||||||
|
String slave =
|
||||||
|
"lexer grammar S;\n" +
|
||||||
|
"D : 'd';\n" +
|
||||||
|
"mode X;\n" +
|
||||||
|
"C : 'c' -> popMode;\n";
|
||||||
|
writeFile(tmpdir, "S.g4", slave);
|
||||||
|
|
||||||
|
ErrorQueue equeue = BaseRuntimeTest.antlrOnString(tmpdir, "Java", "M.g4", false, "-lib", tmpdir);
|
||||||
|
assertEquals(0, equeue.errors.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test public void testImportChannelsIntoLexerGrammar() throws Exception {
|
||||||
|
BaseRuntimeTest.mkdir(tmpdir);
|
||||||
|
|
||||||
|
String master =
|
||||||
|
"lexer grammar M;\n" +
|
||||||
|
"import S;\n" +
|
||||||
|
"channels {CH_A, CH_B}\n" +
|
||||||
|
"A : 'a' -> channel(CH_A);\n" +
|
||||||
|
"B : 'b' -> channel(CH_B);\n";
|
||||||
|
writeFile(tmpdir, "M.g4", master);
|
||||||
|
|
||||||
|
String slave =
|
||||||
|
"lexer grammar S;\n" +
|
||||||
|
"C : 'c';\n";
|
||||||
|
writeFile(tmpdir, "S.g4", slave);
|
||||||
|
|
||||||
|
ErrorQueue equeue = BaseRuntimeTest.antlrOnString(tmpdir, "Java", "M.g4", false, "-lib", tmpdir);
|
||||||
|
assertEquals(0, equeue.errors.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test public void testImportMixedChannelsIntoLexerGrammar() throws Exception {
|
||||||
|
BaseRuntimeTest.mkdir(tmpdir);
|
||||||
|
|
||||||
|
String master =
|
||||||
|
"lexer grammar M;\n" +
|
||||||
|
"import S;\n" +
|
||||||
|
"channels {CH_A, CH_B}\n" +
|
||||||
|
"A : 'a' -> channel(CH_A);\n" +
|
||||||
|
"B : 'b' -> channel(CH_B);\n";
|
||||||
|
writeFile(tmpdir, "M.g4", master);
|
||||||
|
|
||||||
|
String slave =
|
||||||
|
"lexer grammar S;\n" +
|
||||||
|
"channels {CH_C}\n" +
|
||||||
|
"C : 'c' -> channel(CH_C);\n";
|
||||||
|
writeFile(tmpdir, "S.g4", slave);
|
||||||
|
|
||||||
|
ErrorQueue equeue = BaseRuntimeTest.antlrOnString(tmpdir, "Java", "M.g4", false, "-lib", tmpdir);
|
||||||
|
assertEquals(0, equeue.errors.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test public void testMergeModesIntoLexerGrammar() throws Exception {
|
||||||
|
BaseRuntimeTest.mkdir(tmpdir);
|
||||||
|
|
||||||
|
String master =
|
||||||
|
"lexer grammar M;\n" +
|
||||||
|
"import S;\n" +
|
||||||
|
"A : 'a' -> pushMode(X);\n" +
|
||||||
|
"mode X;\n" +
|
||||||
|
"B : 'b';\n";
|
||||||
|
writeFile(tmpdir, "M.g4", master);
|
||||||
|
|
||||||
|
String slave =
|
||||||
|
"lexer grammar S;\n" +
|
||||||
|
"D : 'd';\n" +
|
||||||
|
"mode X;\n" +
|
||||||
|
"C : 'c' -> popMode;\n";
|
||||||
|
writeFile(tmpdir, "S.g4", slave);
|
||||||
|
|
||||||
|
ErrorQueue equeue = BaseRuntimeTest.antlrOnString(tmpdir, "Java", "M.g4", false, "-lib", tmpdir);
|
||||||
|
assertEquals(0, equeue.errors.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test public void testEmptyModesInLexerGrammar() throws Exception {
|
||||||
|
BaseRuntimeTest.mkdir(tmpdir);
|
||||||
|
|
||||||
|
String master =
|
||||||
|
"lexer grammar M;\n" +
|
||||||
|
"import S;\n" +
|
||||||
|
"A : 'a';\n" +
|
||||||
|
"C : 'e';\n" +
|
||||||
|
"B : 'b';\n";
|
||||||
|
writeFile(tmpdir, "M.g4", master);
|
||||||
|
|
||||||
|
String slave =
|
||||||
|
"lexer grammar S;\n" +
|
||||||
|
"D : 'd';\n" +
|
||||||
|
"mode X;\n" +
|
||||||
|
"C : 'c' -> popMode;\n";
|
||||||
|
writeFile(tmpdir, "S.g4", slave);
|
||||||
|
|
||||||
|
ErrorQueue equeue = BaseRuntimeTest.antlrOnString(tmpdir, "Java", "M.g4", false, "-lib", tmpdir);
|
||||||
|
assertEquals(0, equeue.errors.size());
|
||||||
|
}
|
||||||
|
|
||||||
@Test public void testDelegatesSeeSameTokenType() throws Exception {
|
@Test public void testDelegatesSeeSameTokenType() throws Exception {
|
||||||
String slaveS =
|
String slaveS =
|
||||||
"parser grammar S;\n"+
|
"parser grammar S;\n"+
|
||||||
|
|
|
@ -161,6 +161,7 @@ public class GrammarTransformPipeline {
|
||||||
GrammarAST id = (GrammarAST) root.getChild(0);
|
GrammarAST id = (GrammarAST) root.getChild(0);
|
||||||
GrammarASTAdaptor adaptor = new GrammarASTAdaptor(id.token.getInputStream());
|
GrammarASTAdaptor adaptor = new GrammarASTAdaptor(id.token.getInputStream());
|
||||||
|
|
||||||
|
GrammarAST channelsRoot = (GrammarAST)root.getFirstChildWithType(ANTLRParser.CHANNELS);
|
||||||
GrammarAST tokensRoot = (GrammarAST)root.getFirstChildWithType(ANTLRParser.TOKENS_SPEC);
|
GrammarAST tokensRoot = (GrammarAST)root.getFirstChildWithType(ANTLRParser.TOKENS_SPEC);
|
||||||
|
|
||||||
List<GrammarAST> actionRoots = root.getNodesWithType(ANTLRParser.AT);
|
List<GrammarAST> actionRoots = root.getNodesWithType(ANTLRParser.AT);
|
||||||
|
@ -172,7 +173,39 @@ public class GrammarTransformPipeline {
|
||||||
List<GrammarAST> rootRules = RULES.getNodesWithType(ANTLRParser.RULE);
|
List<GrammarAST> rootRules = RULES.getNodesWithType(ANTLRParser.RULE);
|
||||||
for (GrammarAST r : rootRules) rootRuleNames.add(r.getChild(0).getText());
|
for (GrammarAST r : rootRules) rootRuleNames.add(r.getChild(0).getText());
|
||||||
|
|
||||||
|
// make list of modes we have in root grammar
|
||||||
|
List<GrammarAST> rootModes = root.getNodesWithType(ANTLRParser.MODE);
|
||||||
|
Set<String> rootModeNames = new HashSet<String>();
|
||||||
|
for (GrammarAST m : rootModes) rootModeNames.add(m.getChild(0).getText());
|
||||||
|
List<GrammarAST> addedModes = new ArrayList<GrammarAST>();
|
||||||
|
|
||||||
for (Grammar imp : imports) {
|
for (Grammar imp : imports) {
|
||||||
|
// COPY CHANNELS
|
||||||
|
GrammarAST imp_channelRoot = (GrammarAST)imp.ast.getFirstChildWithType(ANTLRParser.CHANNELS);
|
||||||
|
if ( imp_channelRoot != null) {
|
||||||
|
rootGrammar.tool.log("grammar", "imported channels: "+imp_channelRoot.getChildren());
|
||||||
|
if (channelsRoot==null) {
|
||||||
|
channelsRoot = imp_channelRoot.dupTree();
|
||||||
|
channelsRoot.g = rootGrammar;
|
||||||
|
root.insertChild(1, channelsRoot); // ^(GRAMMAR ID TOKENS...)
|
||||||
|
} else {
|
||||||
|
for (int c = 0; c < imp_channelRoot.getChildCount(); ++c) {
|
||||||
|
String channel = imp_channelRoot.getChild(c).getText();
|
||||||
|
boolean channelIsInRootGrammar = false;
|
||||||
|
for (int rc = 0; rc < channelsRoot.getChildCount(); ++rc) {
|
||||||
|
String rootChannel = channelsRoot.getChild(rc).getText();
|
||||||
|
if (rootChannel.equals(channel)) {
|
||||||
|
channelIsInRootGrammar = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!channelIsInRootGrammar) {
|
||||||
|
channelsRoot.addChild(imp_channelRoot.getChild(c).dupNode());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// COPY TOKENS
|
// COPY TOKENS
|
||||||
GrammarAST imp_tokensRoot = (GrammarAST)imp.ast.getFirstChildWithType(ANTLRParser.TOKENS_SPEC);
|
GrammarAST imp_tokensRoot = (GrammarAST)imp.ast.getFirstChildWithType(ANTLRParser.TOKENS_SPEC);
|
||||||
if ( imp_tokensRoot!=null ) {
|
if ( imp_tokensRoot!=null ) {
|
||||||
|
@ -242,7 +275,54 @@ public class GrammarTransformPipeline {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// COPY MODES
|
||||||
|
// The strategy is to copy all the mode sections rules across to any
|
||||||
|
// mode section in the new grammar with the same name or a new
|
||||||
|
// mode section if no matching mode is resolved. Rules which are
|
||||||
|
// already in the new grammar are ignored for copy. If the mode
|
||||||
|
// section being added ends up empty it is not added to the merged
|
||||||
|
// grammar.
|
||||||
|
List<GrammarAST> modes = imp.ast.getNodesWithType(ANTLRParser.MODE);
|
||||||
|
if (modes != null) {
|
||||||
|
for (GrammarAST m : modes) {
|
||||||
|
rootGrammar.tool.log("grammar", "imported mode: " + m.toStringTree());
|
||||||
|
String name = m.getChild(0).getText();
|
||||||
|
boolean rootAlreadyHasMode = rootModeNames.contains(name);
|
||||||
|
GrammarAST destinationAST = null;
|
||||||
|
if (rootAlreadyHasMode) {
|
||||||
|
for (GrammarAST m2 : rootModes) {
|
||||||
|
if (m2.getChild(0).getText().equals(name)) {
|
||||||
|
destinationAST = m2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
destinationAST = m.dupNode();
|
||||||
|
destinationAST.addChild(m.getChild(0).dupNode());
|
||||||
|
}
|
||||||
|
|
||||||
|
int addedRules = 0;
|
||||||
|
List<GrammarAST> modeRules = m.getAllChildrenWithType(ANTLRParser.RULE);
|
||||||
|
for (GrammarAST r : modeRules) {
|
||||||
|
rootGrammar.tool.log("grammar", "imported rule: "+r.toStringTree());
|
||||||
|
String ruleName = r.getChild(0).getText();
|
||||||
|
boolean rootAlreadyHasRule = rootRuleNames.contains(ruleName);
|
||||||
|
if (!rootAlreadyHasRule) {
|
||||||
|
destinationAST.addChild(r);
|
||||||
|
addedRules++;
|
||||||
|
rootRuleNames.add(ruleName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!rootAlreadyHasMode && addedRules > 0) {
|
||||||
|
rootGrammar.ast.addChild(destinationAST);
|
||||||
|
rootModeNames.add(name);
|
||||||
|
rootModes.add(destinationAST);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// COPY RULES
|
// COPY RULES
|
||||||
|
// Rules copied in the mode copy phase are not copied again.
|
||||||
List<GrammarAST> rules = imp.ast.getNodesWithType(ANTLRParser.RULE);
|
List<GrammarAST> rules = imp.ast.getNodesWithType(ANTLRParser.RULE);
|
||||||
if ( rules!=null ) {
|
if ( rules!=null ) {
|
||||||
for (GrammarAST r : rules) {
|
for (GrammarAST r : rules) {
|
||||||
|
|
Loading…
Reference in New Issue