diff --git a/src/main/java/net/alloyggp/griddle/validator/AnalyzedGame.java b/src/main/java/net/alloyggp/griddle/validator/AnalyzedGame.java index dcbc76b..c737bfc 100644 --- a/src/main/java/net/alloyggp/griddle/validator/AnalyzedGame.java +++ b/src/main/java/net/alloyggp/griddle/validator/AnalyzedGame.java @@ -71,6 +71,9 @@ private static AnalyzedGame analyze(List rules) { private static Set getNamesMatchingOrDownstreamOf(String target, Map> ancestorGraph) { Set results = new HashSet(); + //Always add target, for cases like "true" or "does" that are not + //made true by sentences or rules within the GDL. (See GitHub issue #18) + results.add(target); for (String name : ancestorGraph.keySet()) { if (name.equalsIgnoreCase(target)) { results.add(name); diff --git a/src/main/java/net/alloyggp/griddle/validator/ValidatorConfiguration.java b/src/main/java/net/alloyggp/griddle/validator/ValidatorConfiguration.java index 5d91f56..a59c581 100644 --- a/src/main/java/net/alloyggp/griddle/validator/ValidatorConfiguration.java +++ b/src/main/java/net/alloyggp/griddle/validator/ValidatorConfiguration.java @@ -11,6 +11,7 @@ import net.alloyggp.griddle.validator.check.DatalogKeywordsNotConstantsCheck; import net.alloyggp.griddle.validator.check.DisjunctionNotNestedCheck; import net.alloyggp.griddle.validator.check.EmptyBodyCheck; +import net.alloyggp.griddle.validator.check.EmptyDisjunctionCheck; import net.alloyggp.griddle.validator.check.ErrorStringCheck; import net.alloyggp.griddle.validator.check.FixedArityCheck; import net.alloyggp.griddle.validator.check.InconsistentCapitalizationCheck; @@ -29,6 +30,7 @@ import net.alloyggp.griddle.validator.check.TrueDoesAreNotStandaloneSentencesCheck; import net.alloyggp.griddle.validator.check.UnproducedSentenceNamesCheck; import net.alloyggp.griddle.validator.check.UnusedSentenceNamesCheck; +import net.alloyggp.griddle.validator.check.VariablesOnlyInRulesCheck; public class ValidatorConfiguration { private final Map checks; @@ -46,6 +48,7 @@ public static ValidatorConfiguration createStandard() { checks.put(DisjunctionNotNestedCheck.INSTANCE, Level.WARNING); checks.put(ErrorStringCheck.INSTANCE, Level.ERROR); checks.put(EmptyBodyCheck.INSTANCE, Level.WARNING); + checks.put(EmptyDisjunctionCheck.INSTANCE, Level.WARNING); checks.put(FixedArityCheck.INSTANCE, Level.WARNING); checks.put(InconsistentCapitalizationCheck.INSTANCE, Level.ERROR); checks.put(InitBaseInputAreConstantCheck.INSTANCE, Level.ERROR); @@ -63,6 +66,7 @@ public static ValidatorConfiguration createStandard() { checks.put(TrueDoesAreNotStandaloneSentencesCheck.INSTANCE, Level.ERROR); checks.put(UnproducedSentenceNamesCheck.INSTANCE, Level.WARNING); checks.put(UnusedSentenceNamesCheck.INSTANCE, Level.WARNING); + checks.put(VariablesOnlyInRulesCheck.INSTANCE, Level.ERROR); return new ValidatorConfiguration(checks); } diff --git a/src/main/java/net/alloyggp/griddle/validator/check/EmptyDisjunctionCheck.java b/src/main/java/net/alloyggp/griddle/validator/check/EmptyDisjunctionCheck.java new file mode 100644 index 0000000..d7773d8 --- /dev/null +++ b/src/main/java/net/alloyggp/griddle/validator/check/EmptyDisjunctionCheck.java @@ -0,0 +1,24 @@ +package net.alloyggp.griddle.validator.check; + +import net.alloyggp.griddle.grammar.GdlVisitor; +import net.alloyggp.griddle.grammar.Literal; +import net.alloyggp.griddle.validator.AnalyzedGame; + +public class EmptyDisjunctionCheck implements Check { + public static final EmptyDisjunctionCheck INSTANCE = new EmptyDisjunctionCheck(); + private EmptyDisjunctionCheck() { + //Singleton + } + + @Override + public void findProblems(AnalyzedGame game, final ProblemReporter reporter) { + game.visitAll(new GdlVisitor() { + @Override + public void visitDisjunction(Literal disjunction) { + if (disjunction.getDisjunction().isEmpty()) { + reporter.report("Disjunctions should not be empty.", disjunction.getPosition()); + } + } + }); + } +} diff --git a/src/main/java/net/alloyggp/griddle/validator/check/VariablesOnlyInRulesCheck.java b/src/main/java/net/alloyggp/griddle/validator/check/VariablesOnlyInRulesCheck.java new file mode 100644 index 0000000..b6c55d4 --- /dev/null +++ b/src/main/java/net/alloyggp/griddle/validator/check/VariablesOnlyInRulesCheck.java @@ -0,0 +1,30 @@ +package net.alloyggp.griddle.validator.check; + +import net.alloyggp.griddle.Position; +import net.alloyggp.griddle.grammar.GdlVisitor; +import net.alloyggp.griddle.grammar.Sentence; +import net.alloyggp.griddle.grammar.TopLevelGdl; +import net.alloyggp.griddle.validator.AnalyzedGame; + +public class VariablesOnlyInRulesCheck implements Check { + public static final VariablesOnlyInRulesCheck INSTANCE = new VariablesOnlyInRulesCheck(); + private VariablesOnlyInRulesCheck() { + //Singleton + } + + @Override + public void findProblems(AnalyzedGame game, final ProblemReporter reporter) { + for (TopLevelGdl gdl : game.getTopLevelComponents()) { + if (gdl.isSentence()) { + Sentence sentence = gdl.getSentence(); + sentence.accept(new GdlVisitor() { + @Override + public void visitVariable(String variable, Position position) { + reporter.report("Variables may only appear within rules.", position); + } + }); + } + } + } + +} diff --git a/src/test/java/net/alloyggp/griddle/ValidatorTest.java b/src/test/java/net/alloyggp/griddle/ValidatorTest.java index 7384718..d6798eb 100644 --- a/src/test/java/net/alloyggp/griddle/ValidatorTest.java +++ b/src/test/java/net/alloyggp/griddle/ValidatorTest.java @@ -5,6 +5,7 @@ import net.alloyggp.griddle.validator.AnalyzedGame; import net.alloyggp.griddle.validator.ParenthesesValidator; +import net.alloyggp.griddle.validator.Validators; import net.alloyggp.griddle.validator.check.Check; import net.alloyggp.griddle.validator.check.DatalogKeywordsNotConstantsCheck; import net.alloyggp.griddle.validator.check.ErrorStringCheck; @@ -117,6 +118,36 @@ public void testMisplacedDatalogKeyword3() throws Exception { assertEquals(9, problem.getPosition().getEnd()); } + @Test + public void testVariableInTopLevelSentence() throws Exception { + String gameString = TestGames.getGameString("varInTopLevelSentence"); + Set problems = Validators.getStandardValidator().findProblems(gameString); + assertEquals(1, problems.size()); + GdlProblem problem = problems.iterator().next(); + assertEquals(10, problem.getPosition().getStart()); + assertEquals(15, problem.getPosition().getEnd()); + } + + @Test + public void testEmptyDisjunction() throws Exception { + String gameString = TestGames.getGameString("emptyDisjunction"); + Set problems = Validators.getStandardValidator().findProblems(gameString); + assertEquals(1, problems.size()); + GdlProblem problem = problems.iterator().next(); + assertEquals(31, problem.getPosition().getStart()); + assertEquals(36, problem.getPosition().getEnd()); + } + + @Test + public void testBaseDependingOnTrue() throws Exception { + String gameString = TestGames.getGameString("baseDependsOnTrue"); + Set problems = Validators.getStandardValidator().findProblems(gameString); + assertEquals(1, problems.size()); + GdlProblem problem = problems.iterator().next(); + assertEquals(15, problem.getPosition().getStart()); + assertEquals(25, problem.getPosition().getEnd()); + } + private Set findProblems(Check check, String gameString) throws Exception { AnalyzedGame game = AnalyzedGame.parseAndAnalyze(gameString); final Set problems = new HashSet();