Java ANTLR4中的动态运算符标记
我正在尝试用ANTLR4制作一个计算器,它可以使用几乎所有可能的符号作为数学运算符。Java ANTLR4中的动态运算符标记,java,parsing,token,antlr4,Java,Parsing,Token,Antlr4,我正在尝试用ANTLR4制作一个计算器,它可以使用几乎所有可能的符号作为数学运算符。 混凝土: -用户定义由运算符和优先级组成的操作。运算符可以是除某些系统符号(括号、逗号等)以外的任何符号组合。优先级是一个正整数。操作存储在java HashMap中。 -有三种不同类型的运算:左侧(一元负数,…)、右侧(阶乘,…)和二进制(加法,…) -应该在运行时请求操作,以便在解析过程中可以(取消)激活操作。如果不可能,则应在创建解析器时请求运算符。 -对于优先级:最好是完全动态优先级(在运行时请求遇到的
混凝土:
-用户定义由运算符和优先级组成的操作。运算符可以是除某些系统符号(括号、逗号等)以外的任何符号组合。优先级是一个正整数。操作存储在java HashMap中。
-有三种不同类型的运算:左侧(一元负数,…)、右侧(阶乘,…)和二进制(加法,…)
-应该在运行时请求操作,以便在解析过程中可以(取消)激活操作。如果不可能,则应在创建解析器时请求运算符。
-对于优先级:最好是完全动态优先级(在运行时请求遇到的操作的优先级),但如果不可能,则应该有不同的优先级预设。(乘法、加法……) 我所拥有的:
-操作员识别的工作代码
-优先级爬升代码,该代码生成正确的解析树,但给出错误:rule expr failed predicate:(getpreference($op)>=$\u p) 更新:修复了操作员识别代码,并找到了优先爬升机制的代码
tokens { PREOP, POSTOP, BINOP, ERROR }
@lexer::members {
private static List<String> binaryOperators;
private static List<String> prefixOperators;
private static List<String> postfixOperators;
{
binaryOperators = new ArrayList<String>();
binaryOperators.add("+");
binaryOperators.add("*");
binaryOperators.add("-");
binaryOperators.add("/");
prefixOperators = new ArrayList<String>();
prefixOperators.add("-");
postfixOperators = new ArrayList<String>();
postfixOperators.add("!");
}
private Deque<Token> deque = new LinkedList<Token>();
private Token previousToken;
private Token nextToken;
@Override
public Token nextToken() {
if (!deque.isEmpty()) {
return previousToken = deque.pollFirst();
}
Token next = super.nextToken();
if (next.getType() != SYMBOL) {
return previousToken = next;
}
StringBuilder builder = new StringBuilder();
while (next.getType() == SYMBOL) {
builder.append(next.getText());
next = super.nextToken();
}
deque.addLast(nextToken = next);
List<Token> tokens = findOperatorCombination(builder.toString(), getOperatorType());
for (int i = tokens.size() - 1; i >= 0; i--) {
deque.addFirst(tokens.get(i));
}
return deque.pollFirst();
}
private static List<Token> findOperatorCombination(String sequence, OperatorType type) {
switch (type) {
case POSTFIX:
return getPostfixCombination(sequence);
case PREFIX:
return getPrefixCombination(sequence);
case BINARY:
return getBinaryCombination(sequence);
default:
break;
}
return null;
}
private static List<Token> getPrefixCombination(String sequence) {
if (isPrefixOperator(sequence)) {
List<Token> seq = new ArrayList<Token>(1);
seq.add(0, new CommonToken(MathParser.PREOP, sequence));
return seq;
}
if (sequence.length() <= 1) {
return null;
}
for (int i = 1; i < sequence.length(); i++) {
List<Token> seq1 = getPrefixCombination(sequence.substring(0, i));
List<Token> seq2 = getPrefixCombination(sequence.substring(i, sequence.length()));
if (seq1 != null & seq2 != null) {
seq1.addAll(seq2);
return seq1;
}
}
return null;
}
private static List<Token> getPostfixCombination(String sequence) {
if (isPostfixOperator(sequence)) {
List<Token> seq = new ArrayList<Token>(1);
seq.add(0, new CommonToken(MathParser.POSTOP, sequence));
return seq;
}
if (sequence.length() <= 1) {
return null;
}
for (int i = 1; i < sequence.length(); i++) {
List<Token> seq1 = getPostfixCombination(sequence.substring(0, i));
List<Token> seq2 = getPostfixCombination(sequence.substring(i, sequence.length()));
if (seq1 != null && seq2 != null) {
seq1.addAll(seq2);
return seq1;
}
}
return null;
}
private static List<Token> getBinaryCombination(String sequence) {
for (int i = 0; i < sequence.length(); i++) { // i is number of postfix spaces
for (int j = 0; j < sequence.length() - i; j++) { // j is number of prefix spaces
String seqPost = sequence.substring(0, i);
List<Token> post = getPostfixCombination(seqPost);
String seqPre = sequence.substring(sequence.length()-j, sequence.length());
List<Token> pre = getPrefixCombination(seqPre);
String seqBin = sequence.substring(i, sequence.length()-j);
if ((post != null || seqPost.isEmpty()) &&
(pre != null || seqPre.isEmpty()) &&
isBinaryOperator(seqBin)) {
List<Token> res = new ArrayList<Token>();
if (post != null)
res.addAll(post);
res.add(new CommonToken(MathParser.BINOP, seqBin));
if (pre != null)
res.addAll(pre);
return res;
}
}
}
return null;
}
/**
* Returns the expected operator type based on the previous and next token
*/
private OperatorType getOperatorType() {
if (isValueEnd(previousToken.getType())) {
if (isValueStart(nextToken.getType())) {
return OperatorType.BINARY;
}
return OperatorType.POSTFIX;
}
return OperatorType.PREFIX;
}
private enum OperatorType { BINARY, PREFIX, POSTFIX };
/**
* Checks whether the given token is a token found at the start of value elements
* @param tokenType
* @return
*/
private static boolean isValueStart(int tokenType) {
return tokenType == MathParser.INT;
}
/**
* Checks whether the given token is a token found at the end of value elements
* @param tokenType
* @return
*/
private static boolean isValueEnd(int tokenType) {
return tokenType == MathParser.INT;
}
private static boolean isBinaryOperator(String operator) {
return binaryOperators.contains(operator);
}
private static boolean isPrefixOperator(String operator) {
return prefixOperators.contains(operator);
}
private static boolean isPostfixOperator(String operator) {
return postfixOperators.contains(operator);
}
}
tokens{PREOP,POSTOP,BINOP,ERROR}
@lexer::成员{
私有静态列表二进制运算符;
专用静态列表前置运算符;
私有静态列表后缀操作符;
{
binaryOperators=newArrayList();
添加(“+”);
binaryOperators.add(“*”);
添加(“-”);
添加(“/”);
prefixOperators=新的ArrayList();
前缀运算符。添加(“-”);
postfixOperators=newArrayList();
postfix操作符。添加(“!”);
}
private Deque Deque=new LinkedList();
私有令牌previousToken;
私人代币nextToken;
@凌驾
公共令牌nextToken(){
如果(!deque.isEmpty()){
return-previousToken=deque.pollFirst();
}
令牌next=super.nextToken();
if(next.getType()!=SYMBOL){
返回previousToken=next;
}
StringBuilder=新的StringBuilder();
while(next.getType()==SYMBOL){
append(next.getText());
next=super.nextToken();
}
deque.addLast(nextToken=next);
列表标记=findOperatorCombination(builder.toString(),getOperatorType());
对于(int i=tokens.size()-1;i>=0;i--){
deque.addFirst(tokens.get(i));
}
返回deque.pollFirst();
}
私有静态列表FindOperator组合(字符串序列、运算符类型){
开关(类型){
大小写后缀:
返回getpostfix组合(序列);
大小写前缀:
返回getPrefixCombination(序列);
大小写二进制:
返回getBinaryComposition(序列);
违约:
打破
}
返回null;
}
私有静态列表getPrefixCombination(字符串序列){
if(isPrefixOperator(序列)){
列表顺序=新阵列列表(1);
seq.add(0,新的CommonToken(MathParser.PREOP,sequence));
返回顺序;
}
if(sequence.length()=$\u p}?POSTOP
)*
;
原子
:INT
|“(“expr[0]”)
|op=PREOP expr[getnextrecepence($op)]
;
所以现在的问题是如何处理这个谓词失败错误您不能在运行时为Antlr定义优先级/关联性规则。但是,您可以将所有运算符(内置于语言或用户定义)解析为一个单链列表(如
ArrayList
)在解析中,然后在访问者中应用您自己的优先级和关联性算法(或者在语法操作中,如果您真的愿意)
如果您多次迭代列表,算法本身并不难。例如,您可以首先获取列表中每个运算符的优先级,然后检查优先级最高的运算符,查看其右关联还是左关联,然后从中构建第一个(最底层)树节点。继续应用,直到列表为空,并且您已经构建了自己的“解析树”,但没有解析(您不再使用抽象输入字符串)
或者,在运行时对Antlr进行外部调用以编译
.g4
,并对javac
编译生成的Antlr代码,然后使用反射来调用它。然而,这可能要慢得多,而且可能更难实现。根据符号的某些运行时定义,这是一条“正确”工作的解析器规则优先是可能的。虽然一开始似乎不是惯用的选择,但将语义分析推迟到解析器之外的标准替代方案将产生一个差别非常小的解析树——这是标准设计规则的合理例外
以(过于简化的)形式,解析器规则将是:
expr : LParen expr RParen # group
| expr Symbol expr # binary
| expr Symbol # postfix
| Symbol expr # prefix
| Int+ # value
;
public abstract class PrecedenceParser extends Parser {
private Map<String, Integer> precedences;
public PrecedenceParser(TokenStream input) {
super(input);
this.precedences = new HashMap<>();
}
public PrecedenceParser putOperator(String op, int p) {
precedences.put(op, p);
return this;
}
public int getPrecedence(Token operator) {
Integer p = precedences.get(operator.getText());
if (p == null) {
return Integer.MAX_VALUE;
} else {
return p;
}
}
}
要消除歧义,请添加内联谓词:
expr : LParen expr RParen # group
| expr s=Symbol { binary($s) }? expr # binary
| expr s=Symbol { postfix($s) }? # postfix
| s=Symbol { prefix($s) }? expr # prefix
| Int+ # value
;
对于任何给定的符号,单个谓词方法的计算结果应为true
扩展到多个符号字符串将增加一点复杂性(例如,区分二进制和后缀加前缀),但机制基本上保持不变。我认为您的方法是正确的。我建议使用以下语法:
grammar Op;
options {
superClass=PrecedenceParser;
}
prog : expr[0] ;
expr[int _p] locals[Token op]: INT ({$op = _input.LT(1);} {getPrecedence($op) >= $_p}? OP expr[getPrecedence($op)])*;
INT : ( '0'..'9' )+ ;
OP : '+' | '*'; // all allowed symbols, should be extended
WS : [ \t\r\n]+ -> skip ; // skip spaces, tabs, newlines
op规则应包含所有允许的运算符符号。我对+
和*
的限制仅为简单起见。解析器超类应为:
expr : LParen expr RParen # group
| expr Symbol expr # binary
| expr Symbol # postfix
| Symbol expr # prefix
| Int+ # value
;
public abstract class PrecedenceParser extends Parser {
private Map<String, Integer> precedences;
public PrecedenceParser(TokenStream input) {
super(input);
this.precedences = new HashMap<>();
}
public PrecedenceParser putOperator(String op, int p) {
precedences.put(op, p);
return this;
}
public int getPrecedence(Token operator) {
Integer p = precedences.get(operator.getText());
if (p == null) {
return Integer.MAX_VALUE;
} else {
return p;
}
}
}
有先例{+:3,*:4}
(prog (expr 1 + (expr 2) * (expr 3 + (expr 4))))
(prog (expr 1 + (expr 2 * (expr 3) + (expr 4))))
从左到右评估这些序列等同于优先评估它们
这种方法应该适用于更大的运算符集。ANTLR4在内部使用这种方法进行优先级爬升