package edu.stanford.nlp.sempre.interactive.test;
import static org.testng.AssertJUnit.assertEquals;
import java.util.*;
import java.util.function.Predicate;
import fig.basic.*;
import edu.stanford.nlp.sempre.*;
import edu.stanford.nlp.sempre.Parser.Spec;
import edu.stanford.nlp.sempre.interactive.InteractiveBeamParser;
import edu.stanford.nlp.sempre.interactive.DALExecutor;
import org.testng.Assert;
import org.testng.annotations.Test;
import org.testng.collections.Lists;
/**
* Test the parser, and some floating examples
* @author sidaw
*/
@Test(groups = { "InteractiveLearning" })
public class FloatingParsingTest {
Predicate<Example> contains(String formula) {
Formula answer = Formulas.fromLispTree(LispTree.proto.parseFromString(formula));
return e -> e.predDerivations.stream().anyMatch(d -> d.formula.equals(answer));
}
Predicate<Example> moreThan(int count) {
return e -> e.predDerivations.size() > count;
}
Predicate<Example> hasAll(String...substrings) {
return new Predicate<Example>() {
List<String> required = Lists.newArrayList(substrings);
@Override
public boolean test(Example e) {
int match = 0;
for (Derivation deriv : e.predDerivations) {
String formula = deriv.formula.toString();
if (required.stream().anyMatch(s -> formula.indexOf(s)!=-1)) {
match ++;
LogInfo.log("Got a match: " + formula);
}
}
if (match == 0)
throw new RuntimeException("Failed to match " + required.toString() + " : " + e.utterance);
return true;
}
};
}
private static Spec defaultSpec() {
FloatingParser.opts.defaultIsFloating = true;
DALExecutor.opts.convertNumberValues = true;
DALExecutor.opts.printStackTrace = true;
DALExecutor.opts.worldType = "VoxelWorld";
Grammar.opts.inPaths = Lists.newArrayList("./interactive/voxelurn.grammar");
Grammar.opts.useApplyFn = "interactive.ApplyFn";
Grammar.opts.binarizeRules = false;
DALExecutor executor = new DALExecutor();
DALExecutor.opts.worldType = "BlocksWorld";
FeatureExtractor extractor = new FeatureExtractor(executor);
FeatureExtractor.opts.featureDomains.add("rule");
ValueEvaluator valueEvaluator = new ExactValueEvaluator();
Grammar grammar = new Grammar();
grammar.read();
grammar.write();
return new Parser.Spec(grammar, extractor, executor, valueEvaluator);
}
protected static void parse(String beamUtt, String floatUtt, ContextValue context, Predicate<Example> checker) {
LogInfo.begin_track("Cannonical: %s\t Float: %s", beamUtt, floatUtt);
Example.Builder b = new Example.Builder();
b.setId("session:test");
b.setUtterance(floatUtt);
b.setContext(context);
Example ex = b.createExample();
ex.preprocess();
Spec defSpec = defaultSpec();
Parser parser = new InteractiveBeamParser(defSpec);
ParserState state = parser.parse(new Params(), ex, false);
LogInfo.end_track();
// Add the floating parser and check?
if (checker != null) {
if (!checker.test(ex)) {
Assert.fail(floatUtt);
}
}
}
private static ContextValue getContext(String blocks) {
// a hack to pass in the world state without much change to the code
String strigify2 = Json.writeValueAsStringHard(blocks); // some parsing issue inside lisptree parser
return ContextValue.fromString(String.format("(context (graph NaiveKnowledgeGraph ((string \"%s\") (name b) (name c))))", strigify2));
}
public void basicTest() {
String defaultBlocks = "[[1,1,1,\"Green\",[]],[1,2,1,\"Blue\",[]],[2,2,1,\"Red\",[]],[3,2,2,\"Yellow\",[]]]";
ContextValue context = getContext(defaultBlocks);
LogInfo.begin_track("testJoin");
parse("select all", "select all", context, contains("(: select *)"));
// parse("select has color red", "red blocks", context, contains("(:for (color red) (: select))"));
parse("select has color red", "red blocks", context, hasAll("(color red)", "(: select)", ":foreach"));
parse("add red top", "add some to top of red blocks", context, contains("(: add red top)"));
parse("for has color red [ remove ]", "remove red blocks", context, contains("(:foreach (color red) (: remove))"));
parse("repeat 3 [add red]", "add red 3 times", context, contains("(:loop (number 3) (: add red))"));
parse("for has color red [ add yellow top ]", "add red to top of yellow", context, moreThan(0));
// parse("select has row 3", "select row 3", context, moreThan(0));
// parse("select has color red or has color green", "select red and green", context, contains("(:for (or (color red) (color green)) (: select))"));
// parse("select has color red or has color green", "select red or green", context, contains("(:for (or (color red) (color green)) (: select))"));
parse("remove has color red ; remove has color blue", "remove red then remove blue", context, moreThan(0));
LogInfo.end_track();
}
public void advanced() {
String defaultBlocks = "[[1,1,1,\"Green\",[]],[1,2,1,\"Blue\",[]],[2,2,1,\"Red\",[]],[3,2,2,\"Yellow\",[]]]";
ContextValue context = getContext(defaultBlocks);
LogInfo.begin_track("testJoin");
parse("repeat 4 [add yellow]", "add 4 yellow blocks", context, hasAll("(:loop", "(number 4)", "(color yellow)"));
parse("repeat 4 [for has color red [ add yellow left ] ]", "put 4 yellow left of red", context, hasAll(":for", "red", "left"));
parse("", "put 4 yellow to the left of red", context, hasAll(":foreach", "red", "left"));
parse("", "select has color red or has color green", context, hasAll("(color red)", "(color green)", "(: select)"));
parse("", "add red then add green and then add yellow", context, hasAll("(color red)", "(color green)", "(color yellow)"));
parse("", "add red then add green and then add yellow", context, hasAll("(: add", "(color red)", "(color green)", "(color yellow)"));
parse("", "remove the very left yellow block", context, moreThan(0));
parse("", "add red top then add yellow then add green", context, moreThan(0));
parse("", "add 4 yellow to red or green", context, moreThan(0));
// might be manageable with projectivity
parse("", "add 4 yellow to the left of red or green", context, moreThan(0));
parse("", "repeat 3 [delete top of all]", context, moreThan(0));
parse("", "repeat 3 [delete top of all]", context, moreThan(0));
// parse("", "add 3 red to left", context, hasAll("(:loop (number 3) (: add red left))"));
// parse("repeat 5 [ add red left ]", "add 5 red left", context, hasAll("(:loop (number 5) (: add red left))"));
LogInfo.end_track();
}
// things we won't handle
public void outOfScope() {
String defaultBlocks = "[[1,1,1,\"Green\",[]],[1,2,1,\"Blue\",[]],[2,2,1,\"Red\",[]],[3,2,2,\"Yellow\",[]]]";
ContextValue context = getContext(defaultBlocks);
LogInfo.begin_track("testJoin");
parse("", "repeat 3 [ repeat 3 [delete very top of all] ]", context, moreThan(0));
LogInfo.end_track();
}
}