aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/org/shedlang/compiler/parser/Tokeniser.java
blob: 8139e254db6a1cdc415a775e2a99017b3306cc54 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
package org.shedlang.compiler.parser;

import org.shedlang.compiler.tokeniser.RegexTokeniser;
import org.shedlang.compiler.tokeniser.Token;
import org.shedlang.compiler.tokeniser.TokenIterator;
import org.zwobble.sourcetext.SourceText;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static org.shedlang.compiler.tokeniser.Token.token;

public class Tokeniser {
    private static final Map<String, TokenType> KEYWORDS = Map.ofEntries(
        Map.entry("break", TokenType.KEYWORD_BREAK),
        Map.entry("effect", TokenType.KEYWORD_EFFECT),
        Map.entry("else", TokenType.KEYWORD_ELSE),
        Map.entry("extend", TokenType.KEYWORD_EXTEND),
        Map.entry("false", TokenType.KEYWORD_FALSE),
        Map.entry("fn", TokenType.KEYWORD_FN),
        Map.entry("fun", TokenType.KEYWORD_FUN),
        Map.entry("handle", TokenType.KEYWORD_HANDLE),
        Map.entry("if", TokenType.KEYWORD_IF),
        Map.entry("import", TokenType.KEYWORD_IMPORT),
        Map.entry("is", TokenType.KEYWORD_IS),
        Map.entry("native", TokenType.KEYWORD_NATIVE),
        Map.entry("pub", TokenType.KEYWORD_PUB),
        Map.entry("resume", TokenType.KEYWORD_RESUME),
        Map.entry("return", TokenType.KEYWORD_RETURN),
        Map.entry("set", TokenType.KEYWORD_SET),
        Map.entry("struct", TokenType.KEYWORD_STRUCT),
        Map.entry("sumtype", TokenType.KEYWORD_SUMTYPE),
        Map.entry("true", TokenType.KEYWORD_TRUE),
        Map.entry("try", TokenType.KEYWORD_TRY),
        Map.entry("type", TokenType.KEYWORD_TYPE),
        Map.entry("unit", TokenType.KEYWORD_UNIT),
        Map.entry("val", TokenType.KEYWORD_VAL),
        Map.entry("var", TokenType.KEYWORD_VAR),
        Map.entry("when", TokenType.KEYWORD_WHEN),
        Map.entry("while", TokenType.KEYWORD_WHILE)
    );

    private static final RegexTokeniser<TokenType> tokeniser = new RegexTokeniser<>(
        TokenType.UNKNOWN,
        List.of(
            RegexTokeniser.rule(TokenType.IDENTIFIER, "[A-Za-z][A-Za-z0-9]*"),
            RegexTokeniser.rule(TokenType.INT, "-?[0-9]+"),
            RegexTokeniser.rule(TokenType.STRING, "\"(?:[^\\\\\"\n\r]|\\\\.)*\""),
            RegexTokeniser.rule(TokenType.WHITESPACE, "[\r\n\t ]+"),
            RegexTokeniser.rule(TokenType.COMMENT_SINGLE_LINE, "//[^\r\n]*[\r\n]"),

            RegexTokeniser.rule(TokenType.SYMBOL_DOT_DOT_DOT, "\\.\\.\\."),

            RegexTokeniser.rule(TokenType.SYMBOL_BANG_EQUALS, "!="),
            RegexTokeniser.rule(TokenType.SYMBOL_COLON_EQUALS, ":="),
            RegexTokeniser.rule(TokenType.SYMBOL_EQUALS_EQUALS, "=="),
            RegexTokeniser.rule(TokenType.SYMBOL_GREATER_THAN_OR_EQUAL, ">="),
            RegexTokeniser.rule(TokenType.SYMBOL_LESS_THAN_OR_EQUAL, "<="),
            RegexTokeniser.rule(TokenType.SYMBOL_THIN_ARROW, "->"),
            RegexTokeniser.rule(TokenType.SYMBOL_HASH_PAREN_OPEN, "#\\("),

            RegexTokeniser.rule(TokenType.SYMBOL_ASTERISK, "\\*"),
            RegexTokeniser.rule(TokenType.SYMBOL_BANG, "!"),
            RegexTokeniser.rule(TokenType.SYMBOL_BAR, "\\|"),
            RegexTokeniser.rule(TokenType.SYMBOL_BRACE_OPEN, "\\{"),
            RegexTokeniser.rule(TokenType.SYMBOL_BRACE_CLOSE, "\\}"),
            RegexTokeniser.rule(TokenType.SYMBOL_COLON, ":"),
            RegexTokeniser.rule(TokenType.SYMBOL_COMMA, ","),
            RegexTokeniser.rule(TokenType.SYMBOL_DOT, "\\."),
            RegexTokeniser.rule(TokenType.SYMBOL_EQUALS, "="),
            RegexTokeniser.rule(TokenType.SYMBOL_FORWARD_SLASH, "/"),
            RegexTokeniser.rule(TokenType.SYMBOL_GREATER_THAN, ">"),
            RegexTokeniser.rule(TokenType.SYMBOL_LESS_THAN, "<"),
            RegexTokeniser.rule(TokenType.SYMBOL_MINUS, "-"),
            RegexTokeniser.rule(TokenType.SYMBOL_PAREN_OPEN, "\\("),
            RegexTokeniser.rule(TokenType.SYMBOL_PAREN_CLOSE, "\\)"),
            RegexTokeniser.rule(TokenType.SYMBOL_PLUS, "\\+"),
            RegexTokeniser.rule(TokenType.SYMBOL_SEMICOLON, ";"),
            RegexTokeniser.rule(TokenType.SYMBOL_SQUARE_OPEN, "\\["),
            RegexTokeniser.rule(TokenType.SYMBOL_SQUARE_CLOSE, "\\]"),
            RegexTokeniser.rule(TokenType.SYMBOL_UNDERSCORE, "_")
        )
    );

    public static TokenIterator<TokenType> tokenise(SourceText sourceText) {
        var tokens = tokeniser.tokenise(sourceText).stream()
            .filter(token ->
                token.tokenType() != TokenType.WHITESPACE &&
                    token.tokenType() != TokenType.COMMENT_SINGLE_LINE
            )
            .map(token ->
                token.tokenType() == TokenType.IDENTIFIER
                    ? new Token<>(token.sourceRange(), KEYWORDS.getOrDefault(token.value().toString(), token.tokenType()))
                    : token
            )
            .collect(Collectors.toList());

        var endPosition = sourceText.characterPosition(sourceText.characterLength());
        var endSourceRange = endPosition.to(endPosition);
        var endToken = token(endSourceRange, TokenType.END);

        return new TokenIterator<>(tokens, endToken);
    }
}