将州号放入语法中

时间:2017-03-25 10:43:10

标签: antlr antlr4

我可以以某种方式将ATN状态号插入它们出现的语法中吗?

我正在尝试制作一个自动将所有不可避免的文字值添加到文档中的工具。例如,给出以下规则:

statement
    :   block
    |   'assert' expression (':' expression)? ';'
    |   'if' '(' expression ')' statement ('else' statement)?
    ;

如果用户写assert我会添加;,或者如果用户输入if,我想添加括号( )

我在想如果我有州号,那么我可以解析语法以找到文字值,然后用适当的状态号存储它们,这样当用户“进入”特定状态时,解析器就可以检查如果有任何文本可以自动为用户插入。

2 个答案:

答案 0 :(得分:0)

对于给定的语法,这是不可能的,因为语法总是描述有效输入。
因此,当您尝试解析用户尚未完成语句的输入时(例如,他刚刚键入assert),您将收到错误。当然,您可以尝试依赖ANTLR的错误恢复系统为您处理,但我认为这是一个非常“脏”的解决方案。

您拥有的替代品(我的意见)是

  1. 您编写的语法与相应的不完整语句匹配,并根据该解析器决定是否插入特定字符
  2. 你处理插入过程完全独立(我建议),因为它与解析无关。如果您希望在更改语法时自动更新完成,我会说您需要编写一个程序,将语法中的相应信息写入文件,然后您可以使用该文件将其输入 Inserter < / em>的

答案 1 :(得分:0)

好吧,我玩过API而且并不太难。这是在输入状态时识别的语法区域之前或之后将所有状态编号插入到语法文件副本中的代码。老实说,我不确定当间隔为空时它意味着什么。大约三分之一的州似乎就是这种情况。

插入文件的代码是从xor_eq's answer逐字记录的。

此代码的结果如下所示:

enter image description here

private static String GRAMMAR_FILE_NAME = "JavaSimple.g4";
private static String EDITED_GRAMMAR_FILE_NAME = "JavaSimple_edited.g4";

private static void insertStateNumbersIntoGrammar() throws IOException, RecognitionException {
    copyGrammarFile();

    // Load tokens
    ANTLRInputStream input = new ANTLRFileStream(GRAMMAR_FILE_NAME);
    ANTLRv4Lexer lexer = new ANTLRv4Lexer(input);
    CommonTokenStream tokens = new CommonTokenStream(lexer);
    tokens.fill();

    // Load Grammar
    String contents = new String(Files.readAllBytes(Paths.get(GRAMMAR_FILE_NAME)));
    Grammar g = new Grammar(contents);

    List<Insert> inserts = new ArrayList<Insert>();
    boolean before = false;
    for (ATNState state : g.atn.states) {
        int stateNr = state.stateNumber;
        Interval interval = g.getStateToGrammarRegion(stateNr);
        if (interval != null) {
            Token token = before ? tokens.get(interval.a) : tokens.get(interval.b);
            int i = before ? token.getStartIndex() : token.getStopIndex() + 1;

            String stateStr = "[" + stateNr + "]";
            long insertSize = calcInsertLengthBefore(inserts, i);
            insert(EDITED_GRAMMAR_FILE_NAME, i + insertSize, stateStr.getBytes());
            inserts.add(new Insert(i, stateStr));
        }
    }
}

private static int calcInsertLengthBefore(List<Insert> inserts, int index) {
    return inserts.stream()
            .filter(insert -> insert.index < index)
            .flatMapToInt(insert -> IntStream.of(insert.state.length()))
            .sum();
}

private static void insert(String filename, long offset, byte[] content) throws IOException {
    RandomAccessFile r = new RandomAccessFile(new File(filename), "rw");
    RandomAccessFile rtemp = new RandomAccessFile(new File(filename + "~"), "rw");
    long fileSize = r.length();
    FileChannel sourceChannel = r.getChannel();
    FileChannel targetChannel = rtemp.getChannel();
    sourceChannel.transferTo(offset, (fileSize - offset), targetChannel);
    sourceChannel.truncate(offset);
    r.seek(offset);
    r.write(content);
    long newOffset = r.getFilePointer();
    targetChannel.position(0L);
    sourceChannel.transferFrom(targetChannel, newOffset, (fileSize - offset));
    sourceChannel.close();
    targetChannel.close();
}

private static void copyGrammarFile() {
    File source = new File(GRAMMAR_FILE_NAME);
    File target = new File(EDITED_GRAMMAR_FILE_NAME);
    try {
        Files.copy(source.toPath(), target.toPath(), StandardCopyOption.REPLACE_EXISTING);
    } catch (IOException e) {
        e.printStackTrace();
    }
}

private static class Insert {
    final Integer index;
    final String state;

    Insert(int index, String state) {
        this.index = index;
        this.state = state;
    }
}