package edu.stanford.nlp.sempre;
import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
import fig.basic.LispTree;
import fig.basic.Pair;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* A rule specifies how to take a right hand of terminals and non-terminals.
*
* @author Percy Liang
*/
public class Rule {
public static Rule nullRule = new Rule(null, null, null);
// Categories begin with $.
public static boolean isCat(String item) { return item.charAt(0) == '$'; }
public static final String rootCat = "$ROOT";
public static final String tokenCat = "$TOKEN";
public static final String phraseCat = "$PHRASE"; // Sequence of tokens
public static final String lemmaTokenCat = "$LEMMA_TOKEN"; // Lemmatized version
public static final String lemmaPhraseCat = "$LEMMA_PHRASE"; // Lemmatized version
public static final List<String> specialCats = Lists.newArrayList(rootCat, tokenCat, phraseCat, lemmaTokenCat, lemmaPhraseCat);
public final String lhs; // Left-hand side: category.
public final List<String> rhs; // Right-hand side: sequence of categories (have $ prefix) and tokens.
public final SemanticFn sem; // Takes derivations corresponding to RHS categories and produces a set of derivations corresponding to LHS.
public List<Pair<String, Double>> info; // Extra info
public RuleSource source = null; // for tracking where the rule comes from when they are induced
// Cache the semanticRepn
public String getSemRepn() {
if (semRepn == null) semRepn = sem.getClass().getSimpleName();
return semRepn;
}
private String semRepn = null;
public Rule(String lhs,
List<String> rhs,
SemanticFn sem) {
this.lhs = lhs;
this.rhs = rhs;
this.sem = sem;
}
@Override
public String toString() {
if (stringRepn == null) {
String semStr = sem == null? "NullSemanticFn" : sem.toString();
int maxLength = 100;
if (semStr.length() > maxLength)
semStr = String.format("%s...(%d total)", semStr.substring(0,maxLength), semStr.length());
stringRepn = lhs + " -> " + (rhs == null ? "" : Joiner.on(' ').join(rhs)) + " " + semStr;
}
return stringRepn;
}
private String stringRepn; // Cache toString()
// Get/set info
public void addInfo(String key, double value) {
if (info == null) info = Lists.newArrayList();
info.add(Pair.newPair(key, value));
}
public Rule setInfo(Rule rule) { this.info = rule.info; return this; }
// Accessors
public SemanticFn getSem() { return sem; }
public String getLhs() { return lhs; }
// Return whether rule has form A -> B (both LHS and RHS contain one category).
public boolean isCatUnary() { return rhs.size() == 1 && isCat(rhs.get(0)); }
// Return if all RHS tokens are terminals
public boolean isRhsTerminals() {
for (int i = 0; i < rhs.size(); ++i) {
if (isCat(rhs.get(i)))
return false;
}
return true;
}
// Return the number of categories on the RHS
public int numRhsCats() {
int ret = 0;
for (int i = 0; i < rhs.size(); ++i) {
if (isCat(rhs.get(i)))
ret++;
}
return ret;
}
public LispTree toLispTree() {
LispTree tree = LispTree.proto.newList();
tree.addChild("rule");
tree.addChild(lhs);
tree.addChild(LispTree.proto.newList(rhs));
tree.addChild(sem.toLispTree());
if (info != null) {
for (Pair<String, Double> p : info)
tree.addChild(LispTree.proto.newList(p.getFirst(), "" + p.getSecond()));
}
if (source != null)
tree.addChild(source.toJson());
return tree;
}
/* Extract tag info */
public double getInfoTag(String infoTag) {
if (info != null) {
for (Pair<String, Double> p : info) {
if (p.getFirst().equals(infoTag)) return p.getSecond();
}
}
return -1.0;
}
public boolean isFloating() {
double f = getInfoTag("floating");
double a = getInfoTag("anchored");
if (f == 1.0)
return true;
else if (f == 0.0)
return false;
else
return a == 1.0 ? false : FloatingParser.opts.defaultIsFloating;
}
public boolean isAnchored() {
double f = getInfoTag("floating");
double a = getInfoTag("anchored");
if (a == 1.0)
return true;
else if (a == 0.0)
return false;
else
return f == 1.0 ? false : !FloatingParser.opts.defaultIsFloating;
}
public boolean isInduced() {
double a = getInfoTag("induced");
if (a == 1.0) return true;
return false;
}
@Override
public boolean equals(Object o) {
if (!(o instanceof Rule)) return false;
return ((Rule)o).toString().equals(this.toString());
}
@Override
public int hashCode() {
return this.toString().hashCode();
}
public String toJson() {
Map<String, Object> jsonMap = new LinkedHashMap<>();
jsonMap.put("lhs", lhs);
jsonMap.put("rhs", rhs);
if (source != null) {
jsonMap.put("source", source);
}
if (info != null) {
for (Pair<String, Double> p : info)
jsonMap.put(p.getFirst(), p.getSecond());
}
jsonMap.put("sem", sem.toString());
return Json.writeValueAsStringHard(jsonMap);
}
}