Skip to content

Commit a4e50fb

Browse files
refactored and added triemap
1 parent 40a68b0 commit a4e50fb

File tree

3 files changed

+94
-29
lines changed

3 files changed

+94
-29
lines changed

core/src/main/kotlin/io/github/cybercodernaj/parkour/lexer/Lexer.kt

Lines changed: 56 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package io.github.cybercodernaj.parkour.lexer
22

3-
import arrow.core.None
4-
import arrow.core.Option
3+
import arrow.core.*
54
import arrow.core.raise.OptionRaise
65
import arrow.core.raise.option
76
import io.github.cybercodernaj.parkour.datasource.TextSource
@@ -72,9 +71,16 @@ class Lexer(
7271

7372
private var insideMultilineComment = false
7473

75-
private val _hardKeywords = hardKeywords.sortedByDescending(String::length)
76-
private val _separators = separators.sortedByDescending(String::length)
77-
private val _operators = operators.sortedByDescending(String::length)
74+
private val trieMap = StringTrieMap<Kind>()
75+
private enum class Kind {
76+
KEYWORD, OPERATOR, SEPARATOR
77+
}
78+
79+
init {
80+
hardKeywords.forEach {
81+
trieMap[it] = Kind.KEYWORD
82+
}
83+
}
7884

7985
/**
8086
* Fetches the next [Token] from the source
@@ -83,39 +89,25 @@ class Lexer(
8389
* @since 0.1.0
8490
*/
8591
internal fun nextToken(): Token {
92+
currentLine.onNone { fetchLine() }
93+
8694
currentLine.fold(
87-
ifEmpty = { fetchLine() },
95+
ifEmpty = { return Token.EOF },
8896
ifSome = {
89-
if (position.col >= it.length) {
90-
position = position.nextLine() // This will implicitly call fetchCurrentLine() on the new line
97+
if (it.isBlank()) {
98+
position = position.nextLine()
99+
return nextToken()
91100
}
92101
}
93102
)
94103

95-
return fetchToken()
96-
}
97-
98-
private fun fetchToken(): Token {
99-
if (currentLine.isNone())
100-
return Token.EOF
101-
102-
if (currentLine.isSome { it.isBlank() }) {
103-
position = position.nextLine()
104-
return nextToken()
105-
}
106-
107104
fastForwardToContent()
108105

109-
val contenders = listOf(identifiers())
106+
val winner = listOf(identifiers())
110107
.mapNotNull { it.getOrNull() }
111-
112-
return if (contenders.isEmpty())
113-
return nextToken()
114-
else {
115-
val winner = contenders.maxBy { it.end - it.start }
116-
position = position.copy(col = winner.end.col + 1)
117-
winner
118-
}
108+
.getWinnerOrNullOrThrow() ?: nextToken()
109+
position = position.copy(col = winner.end.col + 1)
110+
return winner
119111
}
120112

121113
/**
@@ -160,6 +152,41 @@ private fun <A> Option<A>.getOrThrow(cause: () -> Exception): A {
160152
return getOrNull() ?: throw cause()
161153
}
162154

155+
private fun List<Token>.getWinnerOrNullOrThrow(): Token? {
156+
if (this.isEmpty())
157+
return null
158+
159+
var maxSize = 0
160+
val winners = mutableSetOf<Token>()
161+
for (token in this) {
162+
if (token.size > maxSize) {
163+
winners.clear()
164+
winners.add(token)
165+
maxSize = token.size
166+
} else if (token.size == maxSize) {
167+
winners.add(token)
168+
}
169+
}
170+
171+
when (winners.size) {
172+
1 -> return winners.first()
173+
2 -> {
174+
val keywordIdentifier = winners.separateEither {
175+
when (it) {
176+
is Token.Keyword -> it.left()
177+
is Token.Identifier -> it.right()
178+
else -> throw LexicalException("Ambiguity error")
179+
}
180+
}
181+
// first is the lefts containing keyword.
182+
// keywords triumph over identifiers.
183+
return keywordIdentifier.first[0]
184+
}
185+
186+
else -> throw LexicalException("Ambiguity error")
187+
}
188+
}
189+
163190
// private fun updateTokenStream() {
164191
// fetchNextLine()
165192
// tokenIndex = 0 // reset the counter
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package io.github.cybercodernaj.parkour.lexer
2+
3+
internal class StringTrieMap<V> {
4+
private val root = TrieNode<Char, V>()
5+
6+
operator fun set(key: String, value: V) {
7+
var node = root
8+
for (elem in key) {
9+
if (elem !in node.children) {
10+
node.children[elem] = TrieNode()
11+
}
12+
node = node.children[elem]!!
13+
}
14+
node.value = value
15+
node.terminal = true
16+
}
17+
18+
operator fun get(key: String): V? {
19+
var node = root
20+
for (elem in key) {
21+
if (elem !in node.children)
22+
return null
23+
node = node.children[elem]!!
24+
}
25+
return if (node.terminal) node.value else null
26+
}
27+
}
28+
29+
private data class TrieNode<T, U>(
30+
val children: MutableMap<T, TrieNode<T, U>>,
31+
var value: U?,
32+
var terminal: Boolean
33+
) {
34+
constructor(): this(mutableMapOf(), null, false)
35+
}

core/src/main/kotlin/io/github/cybercodernaj/parkour/lexer/Token.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ internal sealed class Token(val value: Any, val start: Position, val end: Positi
2222

2323
data object EOF : Token(Any(), Position(-1, -1), Position(-1, -1))
2424

25+
val size: Int
26+
get() = this.end - this.start
27+
2528
override fun equals(other: Any?): Boolean {
2629
if (this === other) return true
2730
if (other !is Token) return false

0 commit comments

Comments
 (0)