将状态数字放入语法中
Put state numbers into grammar
我能否以某种方式将 ATN 状态编号插入出现它们的语法中?
我正在尝试制作一个工具,自动将所有不可避免的文字值添加到文档中。例如给定以下规则:
statement
: block
| 'assert' expression (':' expression)? ';'
| 'if' '(' expression ')' statement ('else' statement)?
;
如果用户写 assert
我会添加 ;
或者如果用户输入 if
我想添加括号 (
)
.
我在想,如果我有状态编号,那么我可以解析语法以找到文字值,然后将它们与适当的状态编号一起存储,这样当用户 "enters" 特定状态时,解析器可以检查是否有任何可以为用户自动插入的文本。
给定的语法不可能做到这一点,因为语法总是描述有效输入。
因此,当您尝试解析用户尚未完成语句的输入时(例如,他刚刚键入 assert
),您将遇到错误。当然,您随后可以尝试依靠 ANTLR 的错误恢复系统来为您处理该错误,但我认为这是一个很好的 "dirty" 解决方案。
你的选择(在我看来)是
- 你写了一个语法来匹配各自不完整的语句,并根据那个解析器决定是否插入一个特定的字符
- 您完全独立地处理插入过程(我会推荐),因为它与解析无关。如果您希望在更改语法时自动更新完成,我会说您需要编写一个程序,将语法中的相应信息写入一个文件,然后您可以使用该文件输入 插入器
好吧,我试了一下 API 并不太难。下面是将所有状态编号插入到语法文件副本中的代码,该副本位于进入状态时已识别的语法区域之前或之后。老实说,我不确定间隔为空时的含义。大约三分之一的州似乎属于这种情况。
用于插入文件的代码是从 xor_eq's answer 中逐字提取的。
此代码的结果如下所示:
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;
}
}
我能否以某种方式将 ATN 状态编号插入出现它们的语法中?
我正在尝试制作一个工具,自动将所有不可避免的文字值添加到文档中。例如给定以下规则:
statement
: block
| 'assert' expression (':' expression)? ';'
| 'if' '(' expression ')' statement ('else' statement)?
;
如果用户写 assert
我会添加 ;
或者如果用户输入 if
我想添加括号 (
)
.
我在想,如果我有状态编号,那么我可以解析语法以找到文字值,然后将它们与适当的状态编号一起存储,这样当用户 "enters" 特定状态时,解析器可以检查是否有任何可以为用户自动插入的文本。
给定的语法不可能做到这一点,因为语法总是描述有效输入。
因此,当您尝试解析用户尚未完成语句的输入时(例如,他刚刚键入 assert
),您将遇到错误。当然,您随后可以尝试依靠 ANTLR 的错误恢复系统来为您处理该错误,但我认为这是一个很好的 "dirty" 解决方案。
你的选择(在我看来)是
- 你写了一个语法来匹配各自不完整的语句,并根据那个解析器决定是否插入一个特定的字符
- 您完全独立地处理插入过程(我会推荐),因为它与解析无关。如果您希望在更改语法时自动更新完成,我会说您需要编写一个程序,将语法中的相应信息写入一个文件,然后您可以使用该文件输入 插入器
好吧,我试了一下 API 并不太难。下面是将所有状态编号插入到语法文件副本中的代码,该副本位于进入状态时已识别的语法区域之前或之后。老实说,我不确定间隔为空时的含义。大约三分之一的州似乎属于这种情况。
用于插入文件的代码是从 xor_eq's answer 中逐字提取的。
此代码的结果如下所示:
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;
}
}