package org.zwobble.hobgoblin.compiler.parser; import org.junit.jupiter.api.Test; import org.zwobble.sourcetext.SourceRange; import org.zwobble.sourcetext.SourceText; import static org.zwobble.precisely.AssertThat.assertThat; import static org.zwobble.precisely.Matchers.equalTo; import static org.zwobble.precisely.Matchers.isSequence; public class TokenizerTests { @Test public void emptySourceTextIsTokenizedToEmpty() { var sourceText = SourceText.fromString("", ""); var tokens = Tokenizer.tokenize(sourceText); assertThat(tokens.tokens(), isSequence()); } @Test public void keywordBoxIsTokenized() { var sourceText = SourceText.fromString("", "box"); var tokens = Tokenizer.tokenize(sourceText); assertThat(tokens.tokens(), isSequence( equalTo(new Token(TokenType.KEYWORD_BOX, sourceRange(sourceText, 0, 3))) )); } @Test public void keywordEnumIsTokenized() { var sourceText = SourceText.fromString("", "enum"); var tokens = Tokenizer.tokenize(sourceText); assertThat(tokens.tokens(), isSequence( equalTo(new Token(TokenType.KEYWORD_ENUM, sourceRange(sourceText, 0, 4))) )); } @Test public void keywordFieldIsTokenized() { var sourceText = SourceText.fromString("", "field"); var tokens = Tokenizer.tokenize(sourceText); assertThat(tokens.tokens(), isSequence( equalTo(new Token(TokenType.KEYWORD_FIELD, sourceRange(sourceText, 0, 5))) )); } @Test public void keywordFromIsTokenized() { var sourceText = SourceText.fromString("", "from"); var tokens = Tokenizer.tokenize(sourceText); assertThat(tokens.tokens(), isSequence( equalTo(new Token(TokenType.KEYWORD_FROM, sourceRange(sourceText, 0, 4))) )); } @Test public void keywordImportIsTokenized() { var sourceText = SourceText.fromString("", "import"); var tokens = Tokenizer.tokenize(sourceText); assertThat(tokens.tokens(), isSequence( equalTo(new Token(TokenType.KEYWORD_IMPORT, sourceRange(sourceText, 0, 6))) )); } @Test public void keywordNativeIsTokenized() { var sourceText = SourceText.fromString("", "native"); var tokens = Tokenizer.tokenize(sourceText); assertThat(tokens.tokens(), isSequence( equalTo(new Token(TokenType.KEYWORD_NATIVE, sourceRange(sourceText, 0, 6))) )); } @Test public void keywordStructIsTokenized() { var sourceText = SourceText.fromString("", "struct"); var tokens = Tokenizer.tokenize(sourceText); assertThat(tokens.tokens(), isSequence( equalTo(new Token(TokenType.KEYWORD_STRUCT, sourceRange(sourceText, 0, 6))) )); } @Test public void keywordSumIsTokenized() { var sourceText = SourceText.fromString("", "sum"); var tokens = Tokenizer.tokenize(sourceText); assertThat(tokens.tokens(), isSequence( equalTo(new Token(TokenType.KEYWORD_SUM, sourceRange(sourceText, 0, 3))) )); } @Test public void keywordVariantIsTokenized() { var sourceText = SourceText.fromString("", "variant"); var tokens = Tokenizer.tokenize(sourceText); assertThat(tokens.tokens(), isSequence( equalTo(new Token(TokenType.KEYWORD_VARIANT, sourceRange(sourceText, 0, 7))) )); } @Test public void alphabeticIdentifierIsTokenized() { var sourceText = SourceText.fromString("", "blah"); var tokens = Tokenizer.tokenize(sourceText); assertThat(tokens.tokens(), isSequence( equalTo(new Token(TokenType.IDENTIFIER, sourceRange(sourceText, 0, 4))) )); } @Test public void digitsCanBePartOfIdentifier() { var sourceText = SourceText.fromString("", "blah32"); var tokens = Tokenizer.tokenize(sourceText); assertThat(tokens.tokens(), isSequence( equalTo(new Token(TokenType.IDENTIFIER, sourceRange(sourceText, 0, 6))) )); } @Test public void symbolBraceOpenIsTokenized() { var sourceText = SourceText.fromString("", "{"); var tokens = Tokenizer.tokenize(sourceText); assertThat(tokens.tokens(), isSequence( equalTo(new Token(TokenType.SYMBOL_BRACE_OPEN, sourceRange(sourceText, 0, 1))) )); } @Test public void symbolBraceCloseIsTokenized() { var sourceText = SourceText.fromString("", "}"); var tokens = Tokenizer.tokenize(sourceText); assertThat(tokens.tokens(), isSequence( equalTo(new Token(TokenType.SYMBOL_BRACE_CLOSE, sourceRange(sourceText, 0, 1))) )); } @Test public void symbolColonIsTokenized() { var sourceText = SourceText.fromString("", ":"); var tokens = Tokenizer.tokenize(sourceText); assertThat(tokens.tokens(), isSequence( equalTo(new Token(TokenType.SYMBOL_COLON, sourceRange(sourceText, 0, 1))) )); } @Test public void symbolCommaIsTokenized() { var sourceText = SourceText.fromString("", ","); var tokens = Tokenizer.tokenize(sourceText); assertThat(tokens.tokens(), isSequence( equalTo(new Token(TokenType.SYMBOL_COMMA, sourceRange(sourceText, 0, 1))) )); } @Test public void symbolDotIsTokenized() { var sourceText = SourceText.fromString("", "."); var tokens = Tokenizer.tokenize(sourceText); assertThat(tokens.tokens(), isSequence( equalTo(new Token(TokenType.SYMBOL_DOT, sourceRange(sourceText, 0, 1))) )); } @Test public void symbolDotDotIsTokenized() { var sourceText = SourceText.fromString("", ".."); var tokens = Tokenizer.tokenize(sourceText); assertThat(tokens.tokens(), isSequence( equalTo(new Token(TokenType.SYMBOL_DOT_DOT, sourceRange(sourceText, 0, 2))) )); } @Test public void symbolForwardSlashIsTokenized() { var sourceText = SourceText.fromString("", "/"); var tokens = Tokenizer.tokenize(sourceText); assertThat(tokens.tokens(), isSequence( equalTo(new Token(TokenType.SYMBOL_FORWARD_SLASH, sourceRange(sourceText, 0, 1))) )); } @Test public void symbolSemicolonIsTokenized() { var sourceText = SourceText.fromString("", ";"); var tokens = Tokenizer.tokenize(sourceText); assertThat(tokens.tokens(), isSequence( equalTo(new Token(TokenType.SYMBOL_SEMICOLON, sourceRange(sourceText, 0, 1))) )); } @Test public void symbolSquareOpenIsTokenized() { var sourceText = SourceText.fromString("", "["); var tokens = Tokenizer.tokenize(sourceText); assertThat(tokens.tokens(), isSequence( equalTo(new Token(TokenType.SYMBOL_SQUARE_OPEN, sourceRange(sourceText, 0, 1))) )); } @Test public void symbolSquareCloseIsTokenized() { var sourceText = SourceText.fromString("", "]"); var tokens = Tokenizer.tokenize(sourceText); assertThat(tokens.tokens(), isSequence( equalTo(new Token(TokenType.SYMBOL_SQUARE_CLOSE, sourceRange(sourceText, 0, 1))) )); } @Test public void whitespaceIsIgnored() { var sourceText = SourceText.fromString("", " blah "); var tokens = Tokenizer.tokenize(sourceText); assertThat(tokens.tokens(), isSequence( equalTo(new Token(TokenType.IDENTIFIER, sourceRange(sourceText, 2, 6))) )); } @Test public void whitespaceSeparatesOtherTokens() { var sourceText = SourceText.fromString("", "one two \t three"); var tokens = Tokenizer.tokenize(sourceText); assertThat(tokens.tokens(), isSequence( equalTo(new Token(TokenType.IDENTIFIER, sourceRange(sourceText, 0, 3))), equalTo(new Token(TokenType.IDENTIFIER, sourceRange(sourceText, 4, 7))), equalTo(new Token(TokenType.IDENTIFIER, sourceRange(sourceText, 10, 15))) )); } @Test public void singleLineCommentsAreIgnored() { var sourceText = SourceText.fromString("", "one // two\nthree\n// four"); var tokens = Tokenizer.tokenize(sourceText); assertThat(tokens.tokens(), isSequence( equalTo(new Token(TokenType.IDENTIFIER, sourceRange(sourceText, 0, 3))), equalTo(new Token(TokenType.IDENTIFIER, sourceRange(sourceText, 11, 16))) )); } @Test public void docCommentRunsToEndOfLine() { var sourceText = SourceText.fromString("", "a\n/// One\n /// Two\nb"); var tokens = Tokenizer.tokenize(sourceText); assertThat(tokens.tokens(), isSequence( equalTo(new Token(TokenType.IDENTIFIER, sourceRange(sourceText, 0, 1))), equalTo(new Token(TokenType.DOC_COMMENT, sourceRange(sourceText, 2, 10))), equalTo(new Token(TokenType.DOC_COMMENT, sourceRange(sourceText, 14, 22))), equalTo(new Token(TokenType.IDENTIFIER, sourceRange(sourceText, 22, 23))) )); } private static SourceRange sourceRange(SourceText sourceText, int fromCharacterIndex, int toCharacterIndex) { return sourceText.characterPosition(fromCharacterIndex).to(sourceText.characterPosition(toCharacterIndex)); } }