Antlr не будет показывать никаких предупреждений при компиляции этой грамматики, но при вызове с входным файлом он будет постоянно повторяться в org.antlr.v4.runtime.atn.LexerATNSimulator.closure
. Есть идеи, как создать грамматики парсинга макросов, которые должны работать с лексерами с любыми символами, кроме специальных?
Лексер:
lexer grammar e8pp_lexer;
NEWLINE: '\r' '\n' | '\n' | '\r';
CONTENT: NEWLINE CONTENTF+;
MACRODEF : NEWLINE 'MACRO' -> pushMode(MACRO_DEF);
MACROUSE : NEWLINE '`' -> mode (MACRO_USE);
fragment CONTENTF: .* ;
mode MACRO_DEF;
MACRONAME: LETTERS+ ;
MACROPARAMSTART: '[' ;
MACROPARAM: LETTERS+ ;
MACROSEP: ',';
MACROPARAMEND : ']' ;
MACRODEF_DEF : ':' -> pushMode(MACRO_MODE);
WHITESPACE: WS+ -> skip;
fragment WS: [ \t\r\n\u000C];
fragment LETTERS : [a-zA-Z];
mode MACRO_MODE;
NEWLINE_CONTENT: '\r' '\n' | '\n' | '\r';
ENDMACRO : NEWLINE_CONTENT 'ENDMACRO' -> popMode;
MACRO_CONTENT : NEWLINE_CONTENT MACRO_CONTENTF+;
fragment MACRO_CONTENTF: .* ;
mode MACRO_USE;
MU_ID: LETTERS+ ;
MU_PARAMSTART: '(';
MU_PARAM: LETTERS+;
MU_PARAMSEP : ',';
MU_PARAM_END: ')' -> popMode;
fragment MU_WS: [ \t\r\n\u000C];
fragment MU_LETTERS : [a-zA-Z];
Парсер
grammar e8pp_parser;
options { tokenVocab=e8pp_lexer; }
content:
contentElement+ EOF
;
contentElement:
data | macro
;
data:
contentData | macroUse
;
contentData:
CONTENT
;
macroUse:
MACROUSE macroId MU_PARAMSTART macroUseParams? MU_PARAM_END
;
macroId:
MU_ID
;
macroUseParams:
macroUseParam (MU_PARAMSEP macroUseParam)*
;
macroUseParam:
MU_PARAM
;
macro:
MACRODEF macroName
MACROPARAMSTART macroParams? MACROPARAMEND MACRODEF_DEF
macroBody?
NEWLINE_CONTENT ENDMACRO
;
macroName:
MACRONAME
;
macroParams:
macroParam (MACROSEP macroParam)*
;
macroParam:
MACROPARAM
;
macroBody:
MACRO_CONTENT+
;
Идея состоит в том, что при этом специальные блоки будут анализироваться с помощью макросов, а все остальное будет возвращено в виде данных. Использование Java:
private ByteArrayInputStream parseMacros() throws Exception {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
e8pp_lexer lexer = new e8pp_lexer(new ANTLRFileStream(srcFile.getAbsolutePath(), "utf-8"));
CommonTokenStream tokens = new CommonTokenStream(lexer);
e8pp_parserParser parser = new e8pp_parserParser(tokens);
ContentContext cc = parser.content();
Map<String, MacroDef> macros = new HashMap<String, MacroDef>();
for (ContentElementContext ctx : cc.contentElement()) {
parseMacroElement(ctx, bos, macros);
}
return new ByteArrayInputStream(bos.toByteArray());
}
private void parseMacroElement(ContentElementContext ctx, ByteArrayOutputStream bos, Map<String, MacroDef> macros) throws Exception {
if (ctx.data() != null) {
if (ctx.data().contentData() != null) {
bos.write(ctx.data().contentData().getText().getBytes(Charset.forName("UTF-8")));
} else {
resolveMacro(ctx.data().macroUse(), bos, macros);
}
} else {
defineMacro(ctx.macro(), macros);
}
}
private void defineMacro(MacroContext macro, Map<String, MacroDef> macros) {
String name = macro.macroName().getText();
if (macros.containsKey(name)) {
throw new AssemblyException("Duplicate macro name " + name);
}
MacroDef md = new MacroDef();
md.content = macro.macroBody().getText();
md.argNames = parseMacroArgs(macro.macroParams());
}
private List<String> parseMacroArgs(MacroParamsContext ctx) {
List<String> params = new ArrayList<String>();
for (MacroParamContext p : ctx.macroParam()) {
params.add(p.getText());
}
return params;
}
private void resolveMacro(MacroUseContext ctx, ByteArrayOutputStream bos, Map<String, MacroDef> macros) throws Exception {
String macroName = ctx.macroId().getText();
if (!macros.containsKey(macroName)) {
throw new AssemblyException("Unknown macro " + macroName);
}
MacroDef md = macros.get(macroName);
List<String> values = parseMacroParams(ctx.macroUseParams());
if (values.size() != md.argNames.size()) {
throw new AssemblyException("Bad macro param count for macro " + macroName);
}
String replaced = md.content;
for (int i=0; i<md.argNames.size(); i++) {
String paramName = md.argNames.get(i);
String replaceValue = values.get(i);
replaced = replaced.replaceAll(Pattern.quote(paramName), replaceValue);
}
bos.write(replaced.getBytes(Charset.forName("UTF-8")));
}
private List<String> parseMacroParams(MacroUseParamsContext ctx) {
List<String> params = new ArrayList<String>();
for (MacroUseParamContext p : ctx.macroUseParam()) {
params.add(p.getText());
}
return params;
}
Во-первых, похоже, что вы используете старую версию Antlr4. Лучше всего обновить до последней только потому, что. Во-вторых, похоже, есть проблемы с вашим лексером: фрагмент правила fragment CONTENTF: .* ;
будет потреблять контент для EOF! Выполните отладку лексера независимо, сбросив поток токенов и убедившись, что грамматика лексера правильно идентифицирует токены.
Я не могу помочь тебе с твоей проблемой. Но если вы хотите создать собственный язык + грамматика, вам следует обязательно взглянуть на xtext: eclipse.org/Xtext Он основан на antlr и, на мой взгляд, его проще использовать как простой antlr.