feat(lexer): Scan alphanumeric
This commit is contained in:
parent
3e80aee0db
commit
e0754650bc
4 changed files with 77 additions and 16 deletions
|
@ -9,25 +9,25 @@ class Lexer(private val source: String) {
|
||||||
|
|
||||||
fun scan(): List<Token> {
|
fun scan(): List<Token> {
|
||||||
while (hasNext()) {
|
while (hasNext()) {
|
||||||
val token = scanToken()
|
tokens += scanToken()
|
||||||
tokens += token
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
position.length = 0
|
||||||
tokens += Token(TokenType.EOF, position)
|
tokens += Token(TokenType.EOF, position)
|
||||||
|
|
||||||
return tokens
|
return tokens
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun scanToken(): Token {
|
private fun scanToken(): Token {
|
||||||
val c = peek()
|
val char: Char = peek()
|
||||||
val token = when (c) {
|
|
||||||
'.' -> Token(TokenType.DOT, position)
|
|
||||||
else -> throw Error("Unknown symbol: $c", position)
|
|
||||||
}
|
|
||||||
|
|
||||||
offset++
|
position.length = 1
|
||||||
position.column++
|
|
||||||
return token
|
return when {
|
||||||
|
char == '.' -> scanDot()
|
||||||
|
char.isLetterOrDigit() -> scanAlphanumeric()
|
||||||
|
else -> throw Error("Unknown symbol: $char", position)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun hasNext(): Boolean {
|
private fun hasNext(): Boolean {
|
||||||
|
@ -41,4 +41,27 @@ class Lexer(private val source: String) {
|
||||||
|
|
||||||
return source[offset]
|
return source[offset]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Scanners
|
||||||
|
|
||||||
|
private fun scanDot(): Token {
|
||||||
|
val token = Token(TokenType.DOT, position)
|
||||||
|
offset++
|
||||||
|
position.column++
|
||||||
|
return token
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun scanAlphanumeric(): Token {
|
||||||
|
val token = Token(TokenType.ALPHANUMERIC, position)
|
||||||
|
offset++
|
||||||
|
position.column++
|
||||||
|
|
||||||
|
while (hasNext() && peek().isLetterOrDigit()) {
|
||||||
|
offset++
|
||||||
|
position.column++
|
||||||
|
position.length++
|
||||||
|
}
|
||||||
|
|
||||||
|
return token
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
package lexer
|
package lexer
|
||||||
|
|
||||||
class LexerPosition(val line: Int, var column: Int, val length: Int) {
|
class LexerPosition(val line: Int, var column: Int, var length: Int) {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package lexer
|
package lexer
|
||||||
|
|
||||||
enum class TokenType {
|
enum class TokenType {
|
||||||
|
ALPHANUMERIC,
|
||||||
|
|
||||||
DOT,
|
DOT,
|
||||||
|
|
||||||
EOF
|
EOF
|
||||||
|
|
|
@ -9,15 +9,15 @@ import kotlin.test.assertEquals
|
||||||
|
|
||||||
class LexerScanTest {
|
class LexerScanTest {
|
||||||
@Test
|
@Test
|
||||||
fun scan_emptyString_returnsEOF() {
|
fun scan_emptyString_returns_EOF() {
|
||||||
val lexer = Lexer("")
|
val lexer = Lexer("")
|
||||||
val tokens = lexer.scan()
|
val tokens = lexer.scan()
|
||||||
assertEquals(1, tokens.size)
|
assertEquals(1, tokens.size, "Expected 1 token, got ${tokens.size}")
|
||||||
assertEquals(TokenType.EOF, tokens[0].type)
|
assertEquals(TokenType.EOF, tokens[0].type, "Expected EOF token, got ${tokens[0].type}")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun scan_unknownSymbol_returnsError() {
|
fun scan_unknownSymbol_returns_Error() {
|
||||||
val lexer = Lexer("€")
|
val lexer = Lexer("€")
|
||||||
assertThrows<Error>({
|
assertThrows<Error>({
|
||||||
val tokens = lexer.scan()
|
val tokens = lexer.scan()
|
||||||
|
@ -25,11 +25,47 @@ class LexerScanTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun scan_dot_returnsDot() {
|
fun scan_dot_returns_Dot() {
|
||||||
val lexer = Lexer(".")
|
val lexer = Lexer(".")
|
||||||
val tokens = lexer.scan()
|
val tokens = lexer.scan()
|
||||||
assertEquals(2, tokens.size)
|
assertEquals(2, tokens.size)
|
||||||
assertEquals(TokenType.DOT, tokens[0].type, "Expected DOT token, got ${tokens[0].type}")
|
assertEquals(TokenType.DOT, tokens[0].type, "Expected DOT token, got ${tokens[0].type}")
|
||||||
assertEquals(TokenType.EOF, tokens[1].type, "Expected EOF token, got ${tokens[1].type}")
|
assertEquals(TokenType.EOF, tokens[1].type, "Expected EOF token, got ${tokens[1].type}")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun scan_two_dots_returns_two_dots() {
|
||||||
|
val lexer = Lexer("..")
|
||||||
|
val tokens = lexer.scan()
|
||||||
|
assertEquals(3, tokens.size)
|
||||||
|
assertEquals(TokenType.DOT, tokens[0].type, "Expected DOT token, got ${tokens[0].type}")
|
||||||
|
assertEquals(TokenType.DOT, tokens[1].type, "Expected DOT token, got ${tokens[1].type}")
|
||||||
|
assertEquals(TokenType.EOF, tokens[2].type, "Expected EOF token, got ${tokens[2].type}")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun scan_letter_returns_letter() {
|
||||||
|
val lexer = Lexer("a")
|
||||||
|
val tokens = lexer.scan()
|
||||||
|
|
||||||
|
assertEquals(2, tokens.size)
|
||||||
|
|
||||||
|
assertEquals(TokenType.ALPHANUMERIC, tokens[0].type, "Expected ALPHANUMERIC token, got ${tokens[0].type}")
|
||||||
|
assertEquals(TokenType.EOF, tokens[1].type, "Expected EOF token, got ${tokens[1].type}")
|
||||||
|
|
||||||
|
assertEquals(1, tokens[0].position.length, "Expected length 1, got ${tokens[0].position.length}")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun scan_word_returns_alphanumerics() {
|
||||||
|
val lexer = Lexer("word")
|
||||||
|
val tokens = lexer.scan()
|
||||||
|
|
||||||
|
assertEquals(2, tokens.size)
|
||||||
|
|
||||||
|
assertEquals(TokenType.ALPHANUMERIC, tokens[0].type, "Expected ALPHANUMERIC token, got ${tokens[0].type}")
|
||||||
|
assertEquals(TokenType.EOF, tokens[1].type, "Expected EOF token, got ${tokens[1].type}")
|
||||||
|
|
||||||
|
assertEquals(4, tokens[0].position.length, "Expected length 4, got ${tokens[0].position.length}")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue