プログラミング2(Kotlin+字句解析器)
勉強がてら2パターン試しました。
Tokenを返すメソッドの内容が少し違いますが、基本的な動作は一緒です。
Main.kt : メインループ
Token.kt : トークンタイプとリテラル(読み取った内容)のdata class
TokenType.kt : トークンタイプを示すenum class
Lexer.kt : 字句解析器(入力に対してTokenを返す)
forを使用した場合
Main.kt
fun main() { val input ="1+2*3/4-5" println(input) val l=Lexer(input) input.forEach { val tmp = l.nextToken() println("$it:${tmp.literal},${tmp.tokenType}") } }
Token.kt
data class Token(val tokenType: TokenType, val literal: String)
TokenType.kt
enum class TokenType { ILLEGAL, EOF, INT, //整数 //演算子 PLUS, // + MINUS, // - ASTERISK, // * SLASH, // / }
Lexer.kt
import kotlin.properties.Delegates class Lexer(private val input: String) { private var position: Int = 0 private var currentChar: Char by Delegates.notNull() private var nextChar: Char by Delegates.notNull() init { readChar() } private fun readChar() { currentChar = if (position >= input.length) { 0.toChar() } else { input[position] } nextChar = if (position + 1 >= input.length) { 0.toChar() } else { input[position + 1] } position++ } private fun readNumber(): String { var number: String = currentChar.toString() while (isDigit(nextChar)) { number += nextChar readChar() } return number } private fun skipWhiteSpace() { while ((currentChar == ' ') || (currentChar == '\t') || (currentChar == '\r') || (currentChar == 'n')) { readChar() } } fun nextToken(): Token { skipWhiteSpace() var token: Token by Delegates.notNull() when (currentChar) { '+' -> { token = Token(TokenType.PLUS, currentChar.toString()) } '-' -> { token = Token(TokenType.MINUS, currentChar.toString()) } '*' -> { token = Token(TokenType.ASTERISK, currentChar.toString()) } '/' -> { token = Token(TokenType.SLASH, currentChar.toString()) } else -> { token = if (isDigit(currentChar)) { val number = readNumber() Token(TokenType.INT, number) } else { Token(TokenType.ILLEGAL, currentChar.toString()) } } } readChar() return token } private fun isDigit(c: Char): Boolean { return (c in '0'..'9') } }
Iteratorを使用した場合
Main.kt
fun main() { val input = "+-*/===1234" val l = Lexer(input) val e = l.iterator() println("input : $input") while (e.hasNext()) { val tmp = e.next() println("TokenType: ${tmp.tokenType}, Literal: ${tmp.literal}") } }
Token.kt
data class Token(val tokenType: TokenType, val literal: String)
TokenType.kt
enum class TokenType { ILLEGAL, EOF, INT, //整数 //演算子 PLUS, // + MINUS, // - ASTERISK, // * SLASH, // / }
Lexer.kt
class Lexer(private val input: String) : Iterator<Token> { var position = 0 override fun hasNext(): Boolean { return position < input.length } override fun next(): Token { val token: Token skipWhiteSpace() when (input[position]) { '+' -> { token = Token(TokenType.PLUS, input[position].toString()) } '-' -> { token = Token(TokenType.MINUS, input[position].toString()) } '*' -> { token = Token(TokenType.ASTERISK, input[position].toString()) } '/' -> { token = Token(TokenType.SLASH, input[position].toString()) } '=' -> { if (input[position + 1] == '=') { token = Token(TokenType.EQ, "==") position++ } else { token = Token(TokenType.ASSIGN, input[position].toString()) } } else -> { token = if (readDigit(input[position])) { val number = readNumber() Token(TokenType.INT, number) } else { Token(TokenType.ILLEGAL, input[position].toString()) } } } position++ return token } private fun readDigit(c: Char): Boolean { return (c in '0'..'9') } private fun readNumber(): String { var number: String = input[position].toString() if (lengthCheck()) return number while (readDigit(input[position + 1])) { number += input[position + 1] position++ if (lengthCheck()) break } return number } private fun skipWhiteSpace(){ while(input[position]== ' ' || input[position]=='\t' || input[position]=='\r' || input[position]=='\n') position++ } private fun lengthCheck():Boolean{ return position + 1 >= input.length } }
入力と出力
input : +-*/= = =1234 TokenType: PLUS, Literal: + TokenType: MINUS, Literal: - TokenType: ASTERISK, Literal: * TokenType: SLASH, Literal: / TokenType: ASSIGN, Literal: = TokenType: ASSIGN, Literal: = TokenType: ASSIGN, Literal: = TokenType: INT, Literal: 1234