三元算子解析与调车场算法

时间:2016-03-31 11:24:12

标签: java algorithm ternary-operator shunting-yard

有关背景信息,请先阅读this question about Ternary Operators

我正在构建自己的编程语言,允许您定义自定义运算符。因为我希望它拥有尽可能少的编译器内置函数,所以它应该允许自定义三元运算符的定义,最好是以

的形式
a ? b ? c : d : e
(a) ? (b) ? (c) : (d) : (d)
OperatorChain(operators: [?, ?, :, :], operands: [a, b, c, d, e])

我的(手写)Expression Parser将嵌套的三元运算符转换为由运算符分隔的操作数列表。

OperatorChain

// Note: OperatorElement is a class that merely stores an Identifier, an associated source code position and the resolved operator. // IValue is the base interface for all Expression AST nodes final Stack<OperatorElement> operatorStack = new LinkedList<>(); final Stack<IValue> operandStack = new LinkedList<>(); operandStack.push(this.operands[0]); for (int i = 0; i < this.operatorCount; i++) { final OperatorElement element1 = this.operators[i]; OperatorElement element2; while (!operatorStack.isEmpty()) { element2 = operatorStack.peek(); final int comparePrecedence = element1.operator.comparePrecedence(element2.operator); if (comparePrecedence < 0 || element1.operator.getAssociativity() != IOperator.RIGHT && comparePrecedence == 0) { operatorStack.pop(); this.pushCall(operandStack, element2); } else { break; } } operatorStack.push(element1); operandStack.push(this.operands[i + 1]); } while (!operatorStack.isEmpty()) { this.pushCall(operandStack, operatorStack.pop()); } return operandStack.pop().resolve(markers, context); 类现在从范围内的运算符定义中查找运算符,并使用分流码算法的修改版本将列表转换为二进制AST节点,如下所示:

public class A{
  static int a;
  static
  {
    a = 1;
  }
}

我如何修改此算法以使用三元运算符,包括自定义运算符?

1 个答案:

答案 0 :(得分:2)

我在java中实现了一个可以处理三元运算符的数学解析器。其核心是expression方法。输入包含在迭代器it中,其中it.peekNext()方法用于查看下一个标记,it.consume()移动到下一个标记。它调用prefixSuffix()来读取带有可能的前缀和后缀运算符的常量和变量,例如++x

protected void expression() throws ParseException  {

    prefixSuffix(); 

    Token t = it.peekNext();
    while(t!=null) {
        if(t.isBinary()) {
            OperatorToken ot = (OperatorToken)t;
            Operator op = ot.getBinaryOp();
            pushOp(op,ot);
            it.consume();
            prefixSuffix();
        }
        else if(t.isTernary()){
            OperatorToken ot =(OperatorToken)t;
            Operator to = ot.getTernaryOp();
            pushOp(to,ot);
            it.consume();
            prefixSuffix();
        }
        else
            break;
        t=it.peekNext();
    }
    // read all remaining elements off the stack
    while(!sentinel.equals(ops.peek())) {
        popOp();
    }
}

因此,当遇到任何一个令牌时,它会调用pushOp方法将它们推入堆栈。每个令牌都有一个关联的运算符,该运算符也被解析为pushOp

pushOp将new运算符与堆栈顶部进行比较,必要时弹出

protected void pushOp(Operator op,Token tok) throws ParseException 
{
    while(compareOps(ops.peek(),op))
        popOp();
    ops.push(op); 
}

处理tenary运营商的逻辑发生在compareOps

/**
 * Compare operators based on their precedence and associativity.
 * @param op1
 * @param op2
 * @return true if op1 has a lower precedence than op2, or equal precedence and a left assoc op, etc.
 */
protected boolean compareOps(Operator op1,Operator op2)
{
    if(op1.isTernary() && op2.isTernary()) {
        if(op1 instanceof TernaryOperator.RhsTernaryOperator &&
                op2 instanceof TernaryOperator.RhsTernaryOperator )
            return true;
        return false;
    }
    if(op1 == sentinel ) return false;
    if(op2 == sentinel ) return true;
    if(op2.isPrefix() && op1.isBinary()) return false;
    if(op1.getPrecedence() < op2.getPrecedence()) return true;
    if(op1.getPrecedence() == op2.getPrecedence() && op1.isLeftBinding()) return true;
    return false;
}

如果两个运算符都是三元运算符的右手,则compareOps返回true将弹出一个运算符。否则,如果两者都是左手三元运算符,或者一个是左,一个是右,则compareOps返回false,不会弹出运算符。

另一部分处理发生在popOp方法中:

protected void popOp() throws ParseException
{
    Operator op = ops.pop();

    if(op == implicitMul) {
        Node rhs = nodes.pop();
        Node lhs = nodes.pop();
        Node node = nf.buildOperatorNode(
                jep.getOperatorTable().getMultiply(), 
                lhs, rhs);
        nodes.push(node);
    }
    else if(op.isBinary()) {
        Node rhs = nodes.pop();
        Node lhs = nodes.pop();
        Node node = nf.buildOperatorNode(op, lhs, rhs);
        nodes.push(node);
    }
    else if(op.isUnary()) {
        Node lhs = nodes.pop();
        Node node = nf.buildOperatorNode(op, lhs);
        nodes.push(node);
    }
    else if(op.isTernary() && op instanceof TernaryOperator.RhsTernaryOperator ) {
        Operator op2 = ops.pop();
        if(!(op2 instanceof TernaryOperator ) || !((TernaryOperator) op2).getRhsOperator().equals(op)) {
            throw new ParseException(
                    MessageFormat.format(JepMessages.getString("configurableparser.ShuntingYard.NextOperatorShouldHaveBeenMatchingTernaryOp"),op2.getName())); //$NON-NLS-1$

        }

        Node rhs = nodes.pop();
        Node middle = nodes.pop();
        Node lhs = nodes.pop();
        Node node = nf.buildOperatorNode(op2, new Node[]{lhs,middle,rhs});
        nodes.push(node);
    }
    else {
        throw new ParseException(MessageFormat.format(JepMessages.getString("configurableparser.ShuntingYard.InvalidOperatorOnStack"),op.getSymbol())); //$NON-NLS-1$
    }
}

只应遇到三元运算符的右侧。它正下方的操作员应该是相应的左手操作员。 (任何具有较低优先级的运算符都将被弹出,唯一具有较高优先级的运算符是不能在三元运算符内出现的赋值运算符)。

左手和右手操作员现在都已弹出。我构建了一个树,并且使用构造的新的三元运算符节点移除了生成的最后三个节点。