diff options
author | Marvin Borner | 2019-08-15 15:40:53 +0200 |
---|---|---|
committer | Marvin Borner | 2019-08-15 15:40:53 +0200 |
commit | 5707ecbabfe3b9b580fcc7b2ebdfef6a60786c40 (patch) | |
tree | 577f41b1acf49c28f4facbf0143d28bddfbe4361 | |
parent | 5f7cbd46cf7153b18f57fb8aaa59cd3136639208 (diff) |
Began basic syntax checking
-rw-r--r-- | src/runMain/kotlin/Lexical.kt | 42 | ||||
-rw-r--r-- | src/runMain/kotlin/Syntax.kt | 68 | ||||
-rw-r--r-- | src/runMain/kotlin/exceptions/SyntaxError.kt | 6 |
3 files changed, 88 insertions, 28 deletions
diff --git a/src/runMain/kotlin/Lexical.kt b/src/runMain/kotlin/Lexical.kt index 2910bca..dbf2bad 100644 --- a/src/runMain/kotlin/Lexical.kt +++ b/src/runMain/kotlin/Lexical.kt @@ -1,8 +1,10 @@ +import TokenType.* import exceptions.* class Lexical { /** * Analyzes the given [source] and returns the tokens divided into an array of statements + * TODO: Add line number to token (abstract to class?) */ fun analyze(source: String): MutableList<MutableList<Pair<String, TokenType>>> { var buffer = "" @@ -15,7 +17,7 @@ class Lexical { if (source[i] == '"') skipStatementEnd = !skipStatementEnd statementEnd = source[i] == ';' && !skipStatementEnd val tokenType = getTokenType(buffer, if (source.length > i + 1) source[i + 1] else ' ') - if (tokenType != TokenType.Skip && !statementEnd) { + if (tokenType != Skip && !statementEnd) { currentStatement.add(buffer to tokenType) buffer = "" } else if (statementEnd) { @@ -32,37 +34,37 @@ class Lexical { */ private fun getTokenType(token: String, next: Char): TokenType { return when { - token + next in keyword -> TokenType.Skip - token in keyword -> TokenType.Keyword + token + next in keyword -> Skip + token in keyword -> Keyword - token + next in comparison -> TokenType.Skip - token in assignment -> TokenType.Assignment + token + next in comparison -> Skip + token in assignment -> Assignment - token + next in assignment -> TokenType.Skip - token in arithmetic -> TokenType.Arithmetic + token + next in assignment -> Skip + token in arithmetic -> Arithmetic - token + next in comparison -> TokenType.Skip - token in comparison -> TokenType.Comparison + token + next in comparison -> Skip + token in comparison -> Comparison - token + next in comparison -> TokenType.Skip - token in logical -> TokenType.Logical + token + next in comparison -> Skip + token in logical -> Logical - (token + next).matches(Regex("[a-zA-Z]*")) -> TokenType.Skip - token.matches(Regex("[a-zA-Z]*")) -> TokenType.Identifier + (token + next).matches(Regex("[a-zA-Z]*")) -> Skip + token.matches(Regex("[a-zA-Z]*")) -> Identifier - (token + next).matches(Regex("[0-9]*")) -> TokenType.Skip - token.matches(Regex("[0-9]*")) -> TokenType.Constant + (token + next).matches(Regex("[0-9]*")) -> Skip + token.matches(Regex("[0-9]*")) -> Constant token in emptiness && token.length > 1 -> throw UnknownType(token) - token in emptiness -> TokenType.Empty + token in emptiness -> Empty - token in punctuation -> TokenType.Punctuation + token in punctuation -> Punctuation - token in brackets -> TokenType.Bracket + token in brackets -> Bracket - token in classifier -> TokenType.Classifier + token in classifier -> Classifier - else -> TokenType.Skip + else -> Skip } } diff --git a/src/runMain/kotlin/Syntax.kt b/src/runMain/kotlin/Syntax.kt index 16304cc..a950e77 100644 --- a/src/runMain/kotlin/Syntax.kt +++ b/src/runMain/kotlin/Syntax.kt @@ -1,26 +1,47 @@ +import TokenType.* +import exceptions.* + class Syntax { /** * Checks and validates whether the code complies with the syntax/grammar rules */ fun check(statements: MutableList<MutableList<Pair<String, TokenType>>>): Boolean { - statements.forEachIndexed { i, statement -> + statements.forEach { statement -> removePadding(statement) mergeStrings(statement) + mergeTokens(statement) + print(statement) + statement.forEachIndexed { j, token -> + when (token.second) { + Keyword -> allowNext(statement, j, listOf(Assignment, Constant)) + Constant -> allowNext(statement, j, listOf(Comparison, Arithmetic, Logical, Bracket)) + Assignment -> allowNext(statement, j, listOf(Constant)) + Arithmetic -> allowNext(statement, j, listOf(Constant)) + Comparison -> allowNext(statement, j, listOf(Constant)) + Logical -> allowNext(statement, j, listOf(Constant)) + Identifier -> allowNext(statement, j, listOf(Keyword, Assignment)) + Punctuation -> allowNext(statement, j, listOf(Constant)) + Bracket -> allowNext(statement, j, listOf(Constant, Keyword, Logical, Assignment)) + Classifier -> { + } + Empty -> { + } + else -> throw UnknownType(token.first) + } + } } return true } /** - * Removed empty characters from the start and end of statements + * Removes empty characters from the start and end of statements */ private fun removePadding(statement: MutableList<Pair<String, TokenType>>) { - while (statement[0].second == TokenType.Empty) { + while (statement[0].second == Empty) statement.removeAt(0) - } - while (statement[statement.size - 1].second == TokenType.Empty) { + while (statement[statement.size - 1].second == Empty) statement.removeAt(statement.size - 1) - } } /** @@ -29,9 +50,40 @@ class Syntax { private fun mergeStrings(statement: MutableList<Pair<String, TokenType>>) { var stringing = false statement.forEachIndexed { i, token -> - if (token.second == TokenType.Classifier) + if (token.second == Classifier) stringing = !stringing - if (stringing) statement[i] = token.first to TokenType.Constant + if (stringing || token.second == Classifier) statement[i] = token.first to Constant + } + } + + /** + * Merges tokens by same type + */ + private fun mergeTokens(statement: MutableList<Pair<String, TokenType>>) { + statement.forEachIndexed { i, token -> + if (statement.size > i + 1 && statement[i + 1].second == token.second) { + statement[i] = statement[i].first + statement[i + 1].first to token.second + statement.removeAt(i + 1) + } } } + + /** + * Throws [SyntaxError] if the next token is not in [allowed] + */ + private fun allowNext(statement: MutableList<Pair<String, TokenType>>, index: Int, allowed: List<TokenType>) { + if (nextNonEmpty(statement, index).second !in allowed) { + throw SyntaxError(nextNonEmpty(statement, index).first) + } + } + + /** + * Finds the next non empty token by [index] + */ + private fun nextNonEmpty(statement: MutableList<Pair<String, TokenType>>, index: Int): Pair<String, TokenType> { + var i = index + 1 + while (statement[i].second == Empty) + i++ + return statement[i] + } }
\ No newline at end of file diff --git a/src/runMain/kotlin/exceptions/SyntaxError.kt b/src/runMain/kotlin/exceptions/SyntaxError.kt new file mode 100644 index 0000000..3fc2a69 --- /dev/null +++ b/src/runMain/kotlin/exceptions/SyntaxError.kt @@ -0,0 +1,6 @@ +package exceptions + +/** + * Gets thrown if the syntax is wrong + */ +class SyntaxError(context: String = "") : Exception("Syntax error: $context")
\ No newline at end of file |