Ensure that lex and parse methods are only called once per instance

This commit is contained in:
Riccardo Azzolini 2019-09-09 18:32:18 +02:00
parent c37f7f52b3
commit 12406c787b
4 changed files with 31 additions and 0 deletions

View File

@ -29,6 +29,7 @@ public class Lexer {
private final String source; private final String source;
private final Consumer<? super DslError> errorReporter; private final Consumer<? super DslError> errorReporter;
private boolean used = false;
private final List<Token> tokens = new ArrayList<>(); private final List<Token> tokens = new ArrayList<>();
private int startOfLexeme = 0; private int startOfLexeme = 0;
private int curPosition = 0; private int curPosition = 0;
@ -55,8 +56,14 @@ public class Lexer {
* If any errors are reported, this list should not be considered to represent a valid set of DSL rules, * If any errors are reported, this list should not be considered to represent a valid set of DSL rules,
* but it can still be parsed to potentially find additional errors (which may allow the user to fix more * but it can still be parsed to potentially find additional errors (which may allow the user to fix more
* errors before having to rerun the <code>Lexer</code>). * errors before having to rerun the <code>Lexer</code>).
* @throws IllegalStateException if called multiple times on the same instance.
*/ */
public List<Token> lex() { public List<Token> lex() {
if (used) {
throw new IllegalStateException("Lexer.lex can only be called once per instance");
}
used = true;
while (!atEnd()) { while (!atEnd()) {
startOfLexeme = curPosition; startOfLexeme = curPosition;
lexAndHandleErrors(); lexAndHandleErrors();

View File

@ -29,6 +29,7 @@ public class Parser {
private final List<Token> tokens; private final List<Token> tokens;
private final Consumer<? super DslError> errorReporter; private final Consumer<? super DslError> errorReporter;
private boolean used = false;
private int currentIndex = 0; private int currentIndex = 0;
// For error reporting // For error reporting
@ -57,8 +58,14 @@ public class Parser {
* but each rule can still be analyzed to look for undefined sub-functions in replacement patterns and * but each rule can still be analyzed to look for undefined sub-functions in replacement patterns and
* report them (which may allow the user to fix more errors before having to rerun the <code>Lexer</code> * report them (which may allow the user to fix more errors before having to rerun the <code>Lexer</code>
* and <code>Parser</code>). * and <code>Parser</code>).
* @throws IllegalStateException if called multiple times on the same instance.
*/ */
public List<PatternRule> parse() { public List<PatternRule> parse() {
if (used) {
throw new IllegalStateException("Parser.parse can only be called once per instance");
}
used = true;
return rules(); return rules();
} }

View File

@ -29,6 +29,13 @@ class LexerTest {
assertEquals(expected, lexer.lex()); assertEquals(expected, lexer.lex());
} }
@Test
void multipleLexCalls() {
final Lexer lexer = new Lexer("", errors::add);
lexer.lex();
assertThrows(IllegalStateException.class, lexer::lex);
}
@Test @Test
void validRule() { void validRule() {
final Lexer lexer = new Lexer( final Lexer lexer = new Lexer(

View File

@ -38,6 +38,16 @@ class ParserTest {
assertEquals(Collections.emptyList(), parser.parse()); assertEquals(Collections.emptyList(), parser.parse());
} }
@Test
void multipleParseCalls() {
final List<Token> tokens = Collections.singletonList(
new Token(EOF, "", 0)
);
final Parser parser = new Parser(tokens, errors::add);
parser.parse();
assertThrows(IllegalStateException.class, parser::parse);
}
@Test @Test
void validRuleMultipleReplacements() { void validRuleMultipleReplacements() {
final List<Token> tokens = Arrays.asList( final List<Token> tokens = Arrays.asList(