/*****************************************************************************
* Copyright (C) Codehaus.org *
* ------------------------------------------------------------------------- *
* Licensed under the Apache License, Version 2.0 (the "License"); *
* you may not use this file except in compliance with the License. *
* You may obtain a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
*****************************************************************************/
package net.ion.rosetta;
import java.util.Collections;
import java.util.List;
import net.ion.rosetta.annotations.Private;
import net.ion.rosetta.functors.Map;
import net.ion.rosetta.functors.Map2;
import net.ion.rosetta.util.Lists;
/**
* Builds {@link Parser} to parse expressions with operator-precedence grammar. The operators and precedences are declared in this table.
*
* <p>
* Operators have precedences. The higher the precedence number, the higher the precedence. For the same precedence, prefix > postfix > left-associative > non-associative > right-asscociative.
*
* @author Ben Yu
*/
public final class OperatorTable<T> {
/** Describes operator associativity, in order of precedence. */
enum Associativity {
PREFIX, POSTFIX, LASSOC, NASSOC, RASSOC
}
private final List<Operator> ops = Lists.arrayList();
static final class Operator implements Comparable<Operator> {
final Parser<?> op;
final int precedence;
final Associativity associativity;
Operator(Parser<?> op, int precedence, Associativity associativity) {
this.op = op;
this.precedence = precedence;
this.associativity = associativity;
}
/** Higher precedence first. For tie, compares associativity. */
public int compareTo(Operator that) {
if (precedence > that.precedence)
return -1;
if (precedence < that.precedence)
return 1;
return associativity.compareTo(that.associativity);
}
}
/**
* Adds a prefix unary operator.
*
* @param parser
* the parser for the operator.
* @param precedence
* the precedence number.
* @return this.
*/
public OperatorTable<T> prefix(Parser<? extends Map<? super T, ? extends T>> parser, int precedence) {
ops.add(new Operator(parser, precedence, Associativity.PREFIX));
return this;
}
/**
* Adds a postfix unary operator.
*
* @param parser
* the parser for the operator.
* @param precedence
* the precedence number.
* @return this.
*/
public OperatorTable<T> postfix(Parser<? extends Map<? super T, ? extends T>> parser, int precedence) {
ops.add(new Operator(parser, precedence, Associativity.POSTFIX));
return this;
}
/**
* Adds an infix left-associative binary operator.
*
* @param parser
* the parser for the operator.
* @param precedence
* the precedence number.
* @return this.
*/
public OperatorTable<T> infixl(Parser<? extends Map2<? super T, ? super T, ? extends T>> parser, int precedence) {
ops.add(new Operator(parser, precedence, Associativity.LASSOC));
return this;
}
/**
* Adds an infix right-associative binary operator.
*
* @param parser
* the parser for the operator.
* @param precedence
* the precedence number.
* @return this.
*/
public OperatorTable<T> infixr(Parser<? extends Map2<? super T, ? super T, ? extends T>> parser, int precedence) {
ops.add(new Operator(parser, precedence, Associativity.RASSOC));
return this;
}
/**
* Adds an infix non-associative binary operator.
*
* @param parser
* the parser for the operator.
* @param precedence
* the precedence number.
* @return this.
*/
public OperatorTable<T> infixn(Parser<? extends Map2<? super T, ? super T, ? extends T>> parser, int precedence) {
ops.add(new Operator(parser, precedence, Associativity.NASSOC));
return this;
}
/**
* Builds a {@link Parser} based on information in this {@link OperatorTable}.
*
* @param operand
* parser for the operands.
* @return the expression parser.
*/
public Parser<T> build(Parser<? extends T> operand) {
return buildExpressionParser(operand, operators());
}
@Private
Operator[] operators() {
Collections.sort(ops);
return ops.toArray(new Operator[ops.size()]);
}
/**
* Builds a {@link Parser} based on information described by {@link OperatorTable}.
*
* @param term
* parser for the terminals.
* @param ops
* the operators.
* @return the expression parser.
*/
static <T> Parser<T> buildExpressionParser(final Parser<? extends T> term, final Operator... ops) {
if (ops.length == 0)
return term.<T> cast();
int begin = 0;
int precedence = ops[0].precedence;
Associativity associativity = ops[0].associativity;
int end = 0;
Parser<T> ret = term.<T> cast();
for (int i = 1; i < ops.length; i++) {
Operator op = ops[i];
end = i;
if (op.precedence == precedence && op.associativity == associativity) {
continue;
}
end = i;
Parser<?> p = slice(ops, begin, end);
ret = build(p, associativity, ret);
begin = i;
precedence = ops[i].precedence;
associativity = ops[i].associativity;
}
if (end != ops.length) {
end = ops.length;
associativity = ops[begin].associativity;
Parser<?> p = slice(ops, begin, end);
ret = build(p, associativity, ret);
}
return ret.<T> cast();
}
private static Parser<?> slice(Operator[] ops, int begin, int end) {
Parser<?>[] ps = new Parser<?>[end - begin];
for (int i = 0; i < ps.length; i++) {
ps[i] = ops[i + begin].op;
}
return Parsers.or(ps);
}
@SuppressWarnings("unchecked")
private static <T> Parser<T> build(Parser op, Associativity associativity, Parser<T> operand) {
switch (associativity) {
case PREFIX:
return operand.prefix(op);
case POSTFIX:
return operand.postfix(op);
case LASSOC:
return operand.infixl(op);
case RASSOC:
return operand.infixr(op);
case NASSOC:
return operand.infixn(op);
default:
throw new AssertionError();
}
}
}