![JAR search and dependency download from the Maven repository](/logo.png)
org.petitparser.tools.ExpressionBuilder Maven / Gradle / Ivy
package org.petitparser.tools;
import org.petitparser.parser.Parser;
import org.petitparser.parser.combinators.ChoiceParser;
import org.petitparser.parser.combinators.SequenceParser;
import org.petitparser.parser.primitive.FailureParser;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.function.Function;
/**
* A builder that allows the simple definition of expression grammars with prefix, postfix, and
* left- and right-associative infix operators.
*/
public class ExpressionBuilder {
private final List groups = new ArrayList<>();
/**
* Creates a new group of operators that share the same priority.
*/
public ExpressionGroup group() {
ExpressionGroup group = new ExpressionGroup();
groups.add(group);
return group;
}
/**
* Builds the expression parser.
*/
public Parser build() {
Parser parser =
FailureParser.withMessage("Highest priority group should define a primitive parser.");
for (ExpressionGroup group : groups) {
parser = group.build(parser);
}
return parser;
}
/**
* Models a group of operators of the same precedence.
*/
public static class ExpressionGroup {
private final List primitives = new ArrayList<>();
private final List prefix = new ArrayList<>();
private final List postfix = new ArrayList<>();
private final List right = new ArrayList<>();
private final List left = new ArrayList<>();
/**
* Defines a new primitive or literal {@code parser}.
*/
public ExpressionGroup primitive(Parser parser) {
primitives.add(parser);
return this;
}
private Parser buildPrimitive(Parser inner) {
return buildChoice(primitives, inner);
}
/**
* Adds a prefix operator {@code parser}.
*/
public ExpressionGroup prefix(Parser parser) {
addTo(prefix, parser, Function.identity());
return this;
}
/**
* Adds a prefix operator {@code parser}. Evaluates the optional {@code action} with the parsed
* {@code operator} and {@code value}.
*/
public ExpressionGroup prefix(Parser parser, Function action) {
addTo(prefix, parser, action);
return this;
}
private Parser buildPrefix(Parser inner) {
if (prefix.isEmpty()) {
return inner;
} else {
Parser sequence = new SequenceParser(buildChoice(prefix).star(), inner);
return sequence.map((List> tuple) -> {
Object value = tuple.get(1);
List tuples = tuple.get(0);
Collections.reverse(tuples);
for (ExpressionResult result : tuples) {
value = result.action.apply(Arrays.asList(result.operator, value));
}
return value;
});
}
}
/**
* Adds a postfix operator {@code parser}.
*/
public ExpressionGroup postfix(Parser parser) {
addTo(postfix, parser, Function.identity());
return this;
}
/**
* Adds a postfix operator {@code parser}. Evaluates the optional {@code action} with the parsed
* {@code value} and {@code operator}.
*/
public ExpressionGroup postfix(Parser parser, Function action) {
addTo(postfix, parser, action);
return this;
}
private Parser buildPostfix(Parser inner) {
if (postfix.isEmpty()) {
return inner;
} else {
Parser sequence = new SequenceParser(inner, buildChoice(postfix).star());
return sequence.map((List> tuple) -> {
Object value = tuple.get(0);
for (ExpressionResult result : tuple.get(1)) {
value = result.action.apply(Arrays.asList(value, result.operator));
}
return value;
});
}
}
/**
* Adds a right-associative operator {@code parser}.
*/
public ExpressionGroup right(Parser parser) {
addTo(right, parser, Function.identity());
return this;
}
/**
* Adds a right-associative operator {@code parser}. Evaluates the optional {@code action} with
* the parsed {@code left} term, {@code operator}, and {@code right} term.
*/
public ExpressionGroup right(Parser parser, Function action) {
addTo(right, parser, action);
return this;
}
private Parser buildRight(Parser inner) {
if (right.isEmpty()) {
return inner;
} else {
Parser sequence = inner.separatedBy(buildChoice(right));
return sequence.map((List
© 2015 - 2025 Weber Informatics LLC | Privacy Policy