首页 > 解决方案 > ANTLR4:令牌重写器——压缩删除周围的空间

问题描述

我有以下示例语法:

start: (comments | removes)*
comments: COMMENT;
removes: REMOVE_ME;


COMMENT: ';'~('\n'|'\r')*;
REMOVE_ME: 'remove_me';
fragment NEW_LINE: (('\n')
                  |('\r')
                  |('\r\n'));
NEW_LINES: NEW_LINE+ -> channel(HIDDEN);
OTHER: . -> channel(HIDDEN);

我有以下示例文本

; comments here
; please come closer comment!


remove_me


remove_me

; comment

当我访问每个规则时,我可以使用令牌重写器来删除令牌

    rewritter.delete(ctx.REMOVE_ME);

但是我想做的是,在 REMOVE_ME 之前清理空的新行,并最终得到如下文本。

; comments here
; please come closer comment!
; comment

我如何要求重写器删除以前的新行,直到找到另一个构造或文件的开头?

标签: antlr4

解决方案


更新 我第一次尝试解决这个问题是改变语法。事实是语法倾向于忽略回车符和空格字符。

我用你的语法:

grammar Toek;

start: (comments|removes)*;
comments: COMMENT;
removes: REMOVE_ME;

COMMENT: ';'~('\n'|'\r')*;
REMOVE_ME: 'remove_me';

fragment NEW_LINE: (('\n')
                  |('\r')
                  |('\r\n'));
NEW_LINES: NEW_LINE+ -> channel(HIDDEN);
OTHER: . -> channel(HIDDEN);

然后我写了一个简单的 JUNIT 测试来解析一个字符串(你写的那个)并应用我的解决方案。解决方案基于函数式编程(只是为了简化代码,没有必要)。当 ANTLR 完成替换您指定的规则时,我获取获得的字符串,将其拆分为行并将所有行删除为空。

测试(JUNIT)和一些需要的类:

@Test
public void testOK() throws Throwable {
        final String text = "; comments here\n" +
                "; please come closer comment!" + 
                "\n" + 
                "\n" + 
                "remove_me" + 
                "\n"+
                "\n" + 
                "remove_me" + 
                "\n" + 
                "; comment";
        ParseTreeWalker walker = new ParseTreeWalker();
        List<Triple<Token, Token, String>> replace = new ArrayList<>();     
        ToekBaseListener listener = new ToekBaseListener() {

            @Override
            public void enterRemoves(RemovesContext ctx) {
                System.out.println("-: [" + ctx.getText() + "]");
                replace.add(new Pair<Token, Token,>(ctx.start, ctx.stop));
            }
        };

        ToekLexer lexer = new ToekLexer(CharStreams.fromString(text));
        CommonTokenStream tokens = new CommonTokenStream(lexer);
        ToekParser parser = new ToekParser(tokens);

        parser.removeErrorListeners();
        parser.addErrorListener(new JQLBaseErrorListener() {
            @Override
            public void syntaxError(Recognizer<?, ?> recognizer, Object offendingSymbol, int line,
                    int charPositionInLine, String msg, RecognitionException e) {
                System.out.println(String.format("unespected char at pos %s of text '%s'", charPositionInLine, text));
            }
        });
        ParserRuleContext context = parser.start();

        walker.walk(listener, context);
        TokenStreamRewriter rewriter = new TokenStreamRewriter(tokens);

        for (Triple<Token, Token, String> item : replace) {
            rewriter.replace(item.value0, item.value1, "");
        }

        String solution=split(rewriter.getText());
        System.out.println(solution);
    }

...
public class Pair {
    public Pair(double k ,double v) {
       key=k;
       value=v;
    }

    private String key;
    private String value;  

    public String getKey() { return key; }
    public String getValue() { return value; }
}

以及负责解决的方法。一些解释:获取字符串,在流中转换(由'\n'分割),仅过滤大小> 0的元素,重新压缩在一起。

public static String removeBlankLines(String str) {
  return Stream.of(str.split("\n"))
         .filter(elem -> elem!=null && elem.trim().length()>0)              
         .collect(Collectors.joining("\n"));
}

输出就像你想要的:

; comments here
; please come closer comment!
; comment

推荐阅读