aboutsummaryrefslogtreecommitdiff
path: root/src/runMain/kotlin/Syntax.kt
blob: 8ba809c4eb7c4f6a76ec7ffd017d852d6e751ff9 (plain) (blame)
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
import TokenType.*
import exceptions.*

class Syntax {
    /**
     * Checks and validates whether the code complies with the syntax/grammar rules
     */
    fun check(statements: MutableList<MutableList<Token>>): Boolean {
        statements.forEach { statement ->
            removePadding(statement)
            mergeStrings(statement)
            mergeTokens(statement)
            statement.forEach { println("${it.content} ${it.type}") }
            statement.forEachIndexed { j, token ->
                if (statement.count { it.content == "(" } != statement.count { it.content == ")" })
                    throw SyntaxError("Bracket", "Brackets are not even", token.lineNumber)

                when (token.type) {
                    Keyword -> allowNext(statement, j, listOf(Bracket))
                    Function -> allowNext(statement, j, listOf(Bracket))
                    Constant -> allowNext(statement, j, listOf(Comparison, Arithmetic, Logical, Bracket))
                    Assignment -> allowNext(statement, j, listOf(Constant, Function, Variable))
                    Arithmetic -> allowNext(statement, j, listOf(Constant, Function, Variable))
                    Comparison -> allowNext(statement, j, listOf(Constant, Function, Variable))
                    Logical -> allowNext(statement, j, listOf(Constant, Function, Variable))
                    Variable -> allowNext(statement, j, listOf(Assignment, Bracket, Arithmetic, Logical, Comparison))
                    Punctuation -> allowNext(statement, j, listOf(Constant))
                    Bracket -> allowNext(statement, j, listOf(Constant, Keyword, Logical, Assignment, Variable))
                    Classifier -> Unit
                    Empty -> Unit
                    Skip -> throw UnknownType(token.content)
                }
            }
        }
        return true
    }

    /**
     * Removes empty characters from the start and end of statements
     */
    private fun removePadding(statement: MutableList<Token>) {
        while (statement[0].type == Empty)
            statement.removeAt(0)

        while (statement[statement.size - 1].type == Empty)
            statement.removeAt(statement.size - 1)
    }

    /**
     * Merges classified strings to constants
     */
    private fun mergeStrings(statement: MutableList<Token>) {
        var stringing = false
        statement.forEachIndexed { i, token ->
            if (token.type == Classifier)
                stringing = !stringing
            if (stringing || token.type == Classifier) {
                token.type = Constant
                statement[i] = token
            }
        }
    }

    /**
     * Merges tokens by same type
     */
    private fun mergeTokens(statement: MutableList<Token>) {
        var i = 0
        while (statement.size > i + 1) {
            if (statement[i].type == statement[i + 1].type) { // TODO: Differentiate int and string
                statement[i].content = statement[i].content + statement[i + 1].content
                statement.removeAt(i + 1)
                i--
            }
            i++
        }
    }

    /**
     * Throws [SyntaxError] if the next token is not in [allowed]
     */
    private fun allowNext(statement: MutableList<Token>, index: kotlin.Int, allowed: List<TokenType>) {
        if (statement.size > index + 1 && nextNonEmpty(statement, index).type !in allowed) {
            val token = nextNonEmpty(statement, index)
            throw SyntaxError(token.type.toString(), token.content, token.lineNumber)
        }
    }

    /**
     * Finds the next non empty token by [index]
     */
    private fun nextNonEmpty(statement: MutableList<Token>, index: kotlin.Int): Token {
        var i = index + 1
        while (statement[i].type == Empty) i++
        return statement[i]
    }
}