RegexTranslator.java
package com.github.dakusui.jcunitx.regex;
import java.util.*;
import static java.lang.String.format;
import static java.util.Collections.singletonList;
import static java.util.stream.Collectors.toList;
public abstract class RegexTranslator implements Expr.Visitor {
/**
* A mapping from factor names to terms held by composite (alt/cat) expressions.
*/
public final Map<String, List<Value>> terms;
protected final Expr topLevelExpression;
private final String prefix;
protected Context context;
public RegexTranslator(Expr topLevelExpression, String prefix) {
this.topLevelExpression = topLevelExpression;
this.prefix = prefix;
this.context = new Context.Impl(this.prefix, null);
this.terms = new LinkedHashMap<>();
}
public static String composeKey(String prefix, String id) {
return format("REGEX:%s:%s", prefix, id);
}
@Override
public void visit(Expr.Alt expr) {
Context original = this.context;
original.add(expr);
this.context = createContext(expr.id());
try {
for (Expr each : expr.getChildren()) {
each.accept(this);
}
} finally {
this.terms.put(composeKey(this.prefix, this.context.name()), this.context.values());
this.context = original;
}
}
@Override
public void visit(Expr.Cat expr) {
Context original = this.context;
original.add(expr);
this.context = createContext(expr.id());
try {
for (Expr each : expr.getChildren()) {
each.accept(this);
}
} finally {
this.terms.put(composeKey(this.prefix, this.context.name()), this.context.values());
this.context = original;
}
}
@Override
public void visit(Expr.Leaf leaf) {
this.context.add(leaf);
}
@Override
public void visit(Expr.Empty empty) {
this.context.add(empty);
}
protected boolean isTopLevel(String eachKey) {
return composeKey(this.prefix, this.topLevelExpression.id()).equals(eachKey);
}
private Context createContext(String factorName) {
return new Context.Impl(this.prefix, factorName);
}
protected boolean isAlt(String key) {
return key.startsWith("REGEX:" + this.prefix + ":alt-") ||
key.startsWith("REGEX:" + this.prefix + ":rep-");
}
protected boolean isReferencedByAltDirectlyOrIndirectly(final String key) {
for (Map.Entry<String, List<Value>> each : this.terms.entrySet()) {
if (isAlt(each.getKey())) {
if (each.getValue().stream().anyMatch(in -> in instanceof Reference && ((Reference) in).key.equals(key))) {
return true;
}
}
}
return false;
}
protected List<Object> resolveIfImmediate(Value value) {
if (value instanceof Immediate)
return resolveImmediate(value);
return singletonList(value);
}
protected List<Object> resolve(Value value) {
return resolve(new LinkedList<>(), value);
}
private List<Object> resolveImmediate(Value value) {
return Arrays.stream(((Immediate) value).value.toString().split(" +"))
.filter(each -> !"".equals(each))
.collect(toList());
}
private List<Object> resolve(List<Object> values, Value value) {
if (value instanceof Immediate) {
values.add(resolveImmediate(value));
} else {
String key = ((Reference) value).key;
if (isCat(key)) {
for (Value each : this.terms.get(key)) {
resolve(values, each);
}
} else {
values.add(value);
}
}
return values;
}
private boolean isCat(String key) {
return key.startsWith("REGEX:" + this.prefix + ":cat-");
}
interface Context {
void add(Expr value);
List<Value> values();
String name();
class Impl implements Context {
final List<Value> seq;
final String name;
private final String prefix;
Impl(String prefix, String name) {
this.prefix = prefix;
this.name = name;
this.seq = new LinkedList<>();
}
Value toValue(Expr expr) {
Value value;
if (expr instanceof Expr.Leaf) {
value = new Immediate(((Expr.Leaf) expr).value());
} else {
value = new Reference(composeKey(this.prefix, expr.id()));
}
return value;
}
@Override
public List<Value> values() {
return this.seq;
}
public String name() {
return this.name;
}
public void add(Expr expr) {
this.seq.add(toValue(expr));
}
}
}
}