diff --git a/core/src/main/java/it/cavallium/warppi/math/rules/dsl/RulesDsl.java b/core/src/main/java/it/cavallium/warppi/math/rules/dsl/RulesDsl.java index c6fbfae9..176799d7 100644 --- a/core/src/main/java/it/cavallium/warppi/math/rules/dsl/RulesDsl.java +++ b/core/src/main/java/it/cavallium/warppi/math/rules/dsl/RulesDsl.java @@ -5,10 +5,7 @@ import it.cavallium.warppi.math.rules.dsl.frontend.Lexer; import it.cavallium.warppi.math.rules.dsl.frontend.Parser; import it.cavallium.warppi.math.rules.dsl.patterns.SubFunctionPattern; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Set; +import java.util.*; public class RulesDsl { private RulesDsl() {} @@ -21,7 +18,10 @@ public class RulesDsl { final List rules = parser.parse(); for (final PatternRule rule : rules) { - checkSubFunctionsDefined(rule); + undefinedSubFunctions(rule).stream() + .flatMap(subFunc -> parser.getSubFunctionIdentifiers(rule.getRuleName(), subFunc).stream()) + .map(UndefinedSubFunctionException::new) + .forEach(errors::add); } if (!errors.isEmpty()) { @@ -32,18 +32,21 @@ public class RulesDsl { } /** - * Verifies that all sub-functions in the replacement patterns of a PatternRule - * are defined (captured) in the target pattern. + * Finds any sub-functions that are used in the replacement patterns of a PatternRule + * without being defined (captured) in the target pattern. * - * @param rule The rule to check. - * @throws RuntimeException if any replacement pattern uses undefined sub-functions. + * @param rule The rule to analyze. + * @return The (possibly empty) set of undefined sub-functions. */ - private static void checkSubFunctionsDefined(final PatternRule rule) { + private static Set undefinedSubFunctions(final PatternRule rule) { final Set defined = rule.getTarget().getSubFunctions(); + final Set undefined = new HashSet<>(); for (final Pattern replacement : rule.getReplacements()) { - if (!defined.containsAll(replacement.getSubFunctions())) { - throw new RuntimeException("Undefined sub-function(s) in replacements for " + rule.getRuleName()); - } + undefined.addAll(replacement.getSubFunctions()); } + undefined.removeAll(defined); + return undefined; } + + } diff --git a/core/src/main/java/it/cavallium/warppi/math/rules/dsl/UndefinedSubFunctionException.java b/core/src/main/java/it/cavallium/warppi/math/rules/dsl/UndefinedSubFunctionException.java new file mode 100644 index 00000000..a4d79f54 --- /dev/null +++ b/core/src/main/java/it/cavallium/warppi/math/rules/dsl/UndefinedSubFunctionException.java @@ -0,0 +1,32 @@ +package it.cavallium.warppi.math.rules.dsl; + +import it.cavallium.warppi.math.rules.dsl.frontend.Token; + +/** + * Thrown when a sub-function is used in one of the replacement pattern of a PatternRule, + * but not defined (captured) in the target pattern. + */ +public class UndefinedSubFunctionException extends DslException { + private final Token identifier; + + public UndefinedSubFunctionException(final Token identifier) { + this.identifier = identifier; + } + + @Override + public int getPosition() { + return identifier.position; + } + + @Override + public int getLength() { + return identifier.lexeme.length(); + } + + /** + * @return The name of the undefined sub-function. + */ + public String getName() { + return identifier.lexeme; + } +}