SimpleParser { public final char expect(char token) { if (peek() == token) { skip(); return token; } throw syntax(String.format("Expected: '%s', but found '%s'", token, peekSingleCharForMessage())); } SimpleParser(String expression);  SimpleParser(String expression, int maxNestingLevel); final String expression(); final int position(); final int line(); final int column(); final boolean eof(); final char peek(); final char peek(int offset); final char expect(char token); final String peekSingleCharForMessage(); final char expect(char... tokens); RuntimeException syntax(String message); void ws(); void sp(); void br(); void skip(); void consumeRemainingCharactersOnLine(); final String sliceFrom(int start); final int consumeUntilNoLongerMatches(Predicate<Character> predicate); final void increaseNestingLevel(); final void decreaseNestingLevel(); int nestingLevel();  }
@Test public void expectThrowsWhenNotMatching() { RuntimeException e = Assertions.assertThrows(RuntimeException.class, () -> { new SimpleParser("foo").expect('!'); }); assertThat(e.getMessage(), equalTo("Syntax error at line 1 column 1: Expected: '!', but found 'f'")); }
@Test public void expectThrowsWhenNotMatchingOneOfMany() { RuntimeException e = Assertions.assertThrows(RuntimeException.class, () -> { new SimpleParser("foo").expect('!', '?'); }); assertThat(e.getMessage(), equalTo( "Syntax error at line 1 column 1: Found 'f', but expected one of the following tokens: '!' '?'")); }
@Test public void expectThrowsWhenNotMatchingOneOfManyAndDueToEof() { RuntimeException e = Assertions.assertThrows(RuntimeException.class, () -> { new SimpleParser("").expect('!', '?'); }); assertThat(e.getMessage(), equalTo( "Syntax error at line 1 column 1: Found '[EOF]', but expected one of the following tokens: '!' '?'")); }
@Test public void expectConsumesAndDoesNotThrowWhenValid() { SimpleParser p = new SimpleParser("hi"); assertThat(p.expect('h'), equalTo('h')); assertThat(p.expect('i', '!'), equalTo('i')); }