aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarvin Borner2019-08-15 15:40:53 +0200
committerMarvin Borner2019-08-15 15:40:53 +0200
commit5707ecbabfe3b9b580fcc7b2ebdfef6a60786c40 (patch)
tree577f41b1acf49c28f4facbf0143d28bddfbe4361
parent5f7cbd46cf7153b18f57fb8aaa59cd3136639208 (diff)
Began basic syntax checking
-rw-r--r--src/runMain/kotlin/Lexical.kt42
-rw-r--r--src/runMain/kotlin/Syntax.kt68
-rw-r--r--src/runMain/kotlin/exceptions/SyntaxError.kt6
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