Correctly store sub-function identifiers for rules with the same name and identical rules

This commit is contained in:
Riccardo Azzolini 2019-01-31 19:23:37 +01:00
parent 2a347e6d19
commit 8eba0cd568
3 changed files with 114 additions and 12 deletions

View File

@ -19,7 +19,7 @@ public class RulesDsl {
for (final PatternRule rule : rules) {
undefinedSubFunctions(rule).stream()
.flatMap(subFunc -> parser.getSubFunctionIdentifiers(rule.getRuleName(), subFunc).stream())
.flatMap(subFunc -> parser.getSubFunctionIdentifiers(rule, subFunc).stream())
.map(UndefinedSubFunction::new)
.forEach(errors::add);
}

View File

@ -31,8 +31,10 @@ public class Parser {
private int current = 0;
// For error reporting
private String currentRuleName;
private final Map<String, Map<SubFunctionPattern, List<Token>>> subFunctionIdentifiers = new HashMap<>();
private Map<SubFunctionPattern, List<Token>> ruleSubFunctionIdentifiers;
// An IdentityHashMap is used to distinguish rules even if they're identical (equal)
private final IdentityHashMap<PatternRule, Map<SubFunctionPattern, List<Token>>> subFunctionIdentifiers =
new IdentityHashMap<>();
public Parser(final List<Token> tokens, final Consumer<? super DslError> errorReporter) {
this.tokens = tokens;
@ -43,8 +45,8 @@ public class Parser {
return rules();
}
public List<Token> getSubFunctionIdentifiers(final String ruleName, final SubFunctionPattern subFunction) {
return subFunctionIdentifiers.get(ruleName).get(subFunction);
public List<Token> getSubFunctionIdentifiers(final PatternRule rule, final SubFunctionPattern subFunction) {
return subFunctionIdentifiers.get(rule).get(subFunction);
}
// rules = { rule } , EOF ;
@ -67,12 +69,14 @@ public class Parser {
private PatternRule rule() throws SyntaxException {
final RuleType type = ruleType();
final String name = matchOrFail(IDENTIFIER).lexeme;
currentRuleName = name; // This field must be set before calling pattern() and replacements()
ruleSubFunctionIdentifiers = new HashMap<>(); // Must be initialized before calling pattern() and replacements()
matchOrFail(COLON);
final Pattern target = pattern();
matchOrFail(ARROW);
final List<Pattern> replacements = replacements();
return new PatternRule(name, type, target, replacements);
final PatternRule rule = new PatternRule(name, type, target, replacements);
subFunctionIdentifiers.put(rule, ruleSubFunctionIdentifiers);
return rule;
}
// rule type = REDUCTION | EXPANSION | CALCULATION | EXISTENCE ;
@ -235,11 +239,7 @@ public class Parser {
}
private void saveSubFunctionIdentifier(final SubFunctionPattern subFunction, final Token curToken) {
final Map<SubFunctionPattern, List<Token>> ruleMap = subFunctionIdentifiers.computeIfAbsent(
currentRuleName,
key -> new HashMap<>()
);
final List<Token> subFunctionList = ruleMap.computeIfAbsent(
final List<Token> subFunctionList = ruleSubFunctionIdentifiers.computeIfAbsent(
subFunction,
key -> new ArrayList<>()
);

View File

@ -14,6 +14,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import static it.cavallium.warppi.math.rules.dsl.frontend.TokenType.*;
import static org.junit.Assert.*;
@ -287,6 +288,107 @@ public class ParserTest {
assertEquals(expected, parser.parse());
}
@Test
public void subFunctionIdentifiers() {
final List<ReferenceEqualityToken> rule0x = new ArrayList<>();
final List<ReferenceEqualityToken> rule1x = new ArrayList<>();
final List<ReferenceEqualityToken> rule1y = new ArrayList<>();
final List<ReferenceEqualityToken> rule2x = new ArrayList<>();
final List<ReferenceEqualityToken> rule3x = new ArrayList<>();
final List<Token> tokens = Arrays.asList(
new Token(REDUCTION, "reduction", 0),
new Token(IDENTIFIER, "test1", 0),
new Token(COLON, ":", 0),
new Token(PLUS, "+", 0),
addIdentifierToken(rule0x, "x"),
new Token(ARROW, "->", 0),
addIdentifierToken(rule0x, "x"),
new Token(EXPANSION, "expansion", 0),
new Token(IDENTIFIER, "test2", 0),
new Token(COLON, ":", 0),
addIdentifierToken(rule1x, "x"),
new Token(POWER, "^", 0),
new Token(MINUS, "-", 0),
addIdentifierToken(rule1y, "y"),
new Token(ARROW, "->", 0),
new Token(NUMBER, "1", 0),
new Token(DIVIDE, "/", 0),
addIdentifierToken(rule1x, "x"),
new Token(POWER, "^", 0),
addIdentifierToken(rule1y, "y"),
// Rule with the same name (and type)
new Token(CALCULATION, "expansion", 0),
new Token(IDENTIFIER, "test2", 0),
new Token(COLON, ":", 0),
addIdentifierToken(rule2x, "x"),
new Token(ARROW, "->", 0),
addIdentifierToken(rule2x, "x"),
new Token(PLUS, "+", 0),
new Token(NUMBER, "0", 0),
// Identical rule
new Token(CALCULATION, "expansion", 0),
new Token(IDENTIFIER, "test2", 0),
new Token(COLON, ":", 0),
addIdentifierToken(rule3x, "x"),
new Token(ARROW, "->", 0),
addIdentifierToken(rule3x, "x"),
new Token(PLUS, "+", 0),
new Token(NUMBER, "0", 0),
new Token(EOF, "", 0)
);
final Parser parser = new Parser(tokens, errors::add);
final List<PatternRule> rules = parser.parse();
assertEquals(rule0x, toReferenceEquality(
parser.getSubFunctionIdentifiers(rules.get(0), new SubFunctionPattern("x"))
));
assertEquals(rule1x, toReferenceEquality(
parser.getSubFunctionIdentifiers(rules.get(1), new SubFunctionPattern("x"))
));
assertEquals(rule1y, toReferenceEquality(
parser.getSubFunctionIdentifiers(rules.get(1), new SubFunctionPattern("y"))
));
assertEquals(rule2x, toReferenceEquality(
parser.getSubFunctionIdentifiers(rules.get(2), new SubFunctionPattern("x"))
));
assertEquals(rule3x, toReferenceEquality(
parser.getSubFunctionIdentifiers(rules.get(3), new SubFunctionPattern("x"))
));
}
private static Token addIdentifierToken(final List<ReferenceEqualityToken> list, final String identifier) {
final Token token = new Token(IDENTIFIER, identifier, 0);
list.add(new ReferenceEqualityToken(token));
return token;
}
private static List<ReferenceEqualityToken> toReferenceEquality(final List<Token> tokens) {
return tokens.stream()
.map(ReferenceEqualityToken::new)
.collect(Collectors.toList());
}
private static class ReferenceEqualityToken {
private final Token token;
ReferenceEqualityToken(final Token token) {
this.token = token;
}
@Override
public boolean equals(final Object o) {
if (!(o instanceof ReferenceEqualityToken)) {
return false;
}
return this.token == ((ReferenceEqualityToken) o).token;
}
}
// The EOF token is inserted by the lexer, therefore it can only be missing
// in case of programming errors, and not directly because of user input.
@Test(expected = RuntimeException.class)