package org.example data class State( val matched: String, val remaining: String, ) data class AvailableState(val seq: Sequence = emptySequence()) : Sequence by seq { val isEmpty: Boolean get() = seq.none() } class MatchResult(val available: AvailableState) { val isSuccess: Boolean get() = !this.available.isEmpty val isFailure: Boolean get() = this.available.isEmpty override fun toString(): String { return if (isSuccess) { "MatchResult(success)" } else { "MatchResult(failure)" } } } // 재귀 하향 분석기. interface RegexItem { override fun toString(): String fun findMatch(item: String): AvailableState } fun RegexItem.match(item: String): MatchResult { // 기본 매칭 함수. AvailableState를 MatchResult로 변환 return MatchResult(this.findMatch(item)) } class AndThenItem(val left: RegexItem, val right: RegexItem) : RegexItem { override fun toString(): String = "${left}${right}" override fun findMatch(item: String): AvailableState { val leftMatch = left.findMatch(item) if (leftMatch.isEmpty) { return AvailableState() // If left match fails, return empty sequence } // If left match is successful, try to match the right item with the remaining string // from the left match. return AvailableState( leftMatch.flatMap { state -> val rightMatch = right.findMatch(state.remaining) if (!rightMatch.isEmpty) { // If right match is successful, combine the matched parts rightMatch.map { rightState -> State(state.matched + rightState.matched, rightState.remaining) } } else { // If right match fails, return an empty sequence sequenceOf() } } ) } } class CharItem(val value: String) : RegexItem { override fun toString(): String = value override fun findMatch(item: String): AvailableState { return if (item.isNotEmpty() && item[0].toString() == value) { // If the first character matches, return a successful match AvailableState(sequenceOf(State(value, item.substring(1)))) } else { // If it doesn't match, return an empty sequence AvailableState() } } } class PlusItem(val item: RegexItem) : RegexItem { override fun toString(): String = "${item}+" override fun findMatch(item: String): AvailableState { // 욕심쟁이 매칭해야 함. 그래서 가능한 한 많이 매칭해야 함. val results = mutableListOf() var remaining = item while (true) { val matchResult = this.item.findMatch(remaining) if (matchResult.isEmpty) { break // No more matches possible } matchResult.forEach { state -> results.add(State(state.matched, state.remaining)) } remaining = matchResult.first().remaining // Update remaining string } return AvailableState(results.reversed().asSequence()) // Return all successful matches } } class StarItem(val item: RegexItem) : RegexItem { override fun toString(): String = "${item}*" override fun findMatch(item: String): AvailableState { // 욕심쟁이 매칭해야 함. 그래서 가능한 한 많이 매칭해야 함. val results = mutableListOf(State("", item)) // Start with an empty match var remaining = item while (true) { val matchResult = this.item.findMatch(remaining) if (matchResult.isEmpty) { break // No more matches possible } matchResult.forEach { state -> results.add(State(state.matched, state.remaining)) } remaining = matchResult.first().remaining // Update remaining string if (remaining.isEmpty()) { break // If remaining string is empty, stop matching } } // 반드시 reverse해서 가장 긴 매칭부터 시작해야 함. return AvailableState(results.reversed().asSequence()) // Return all successful matches } } class QuestionItem(val item: RegexItem) : RegexItem { override fun toString(): String = "${item}?" override fun findMatch(item: String): AvailableState { // ?는 0개 또는 1개 매칭을 의미하므로, 먼저 시도해보고 실패하면 빈 시퀀스를 반환 val matchResult = this.item.findMatch(item) if (matchResult.isEmpty) { // If the item does not match, return an empty sequence return AvailableState(sequenceOf(State("", item))) } // If it matches, return the successful match return AvailableState(matchResult.map { State(it.matched, it.remaining) }) } } class DotItem : RegexItem { override fun toString(): String = "." override fun findMatch(item: String): AvailableState { // .은 임의의 한 문자와 매칭되므로, 첫 문자가 존재하면 매칭 성공 return if (item.isNotEmpty()) { AvailableState(sequenceOf(State(item[0].toString(), item.substring(1)))) } else { AvailableState() // 빈 문자열에 대해서는 매칭 실패 } } } class AlternationItem(val left: RegexItem, val right: RegexItem) : RegexItem { override fun toString(): String = "(${left}|${right})" override fun findMatch(item: String): AvailableState { // Alternation은 왼쪽 또는 오른쪽 항목 중 하나와 매칭되므로, 각각 시도해보고 성공하는 경우를 반환 val leftMatch = left.findMatch(item) val rightMatch = right.findMatch(item) return AvailableState( (leftMatch + rightMatch).asSequence() // 두 매칭 결과를 합쳐서 반환 ) } }