From 739a40dd3359d02c779773732aad925df07f9467 Mon Sep 17 00:00:00 2001 From: Ronald Holshausen Date: Thu, 27 Oct 2022 13:56:37 +1100 Subject: [PATCH] refactor: extract common lexer functions from version parser #1615 --- .../au/com/dius/pact/core/support/Version.kt | 71 ++++++++----------- .../{expressions.kt => Expressions.kt} | 0 .../pact/core/support/parsers/StringLexer.kt | 56 +++++++++++++++ .../core/support/VersionParserSpec.groovy | 9 +-- 4 files changed, 89 insertions(+), 47 deletions(-) rename core/support/src/main/kotlin/au/com/dius/pact/core/support/generators/expressions/{expressions.kt => Expressions.kt} (100%) create mode 100644 core/support/src/main/kotlin/au/com/dius/pact/core/support/parsers/StringLexer.kt diff --git a/core/support/src/main/kotlin/au/com/dius/pact/core/support/Version.kt b/core/support/src/main/kotlin/au/com/dius/pact/core/support/Version.kt index 4be483d409..846651edfb 100644 --- a/core/support/src/main/kotlin/au/com/dius/pact/core/support/Version.kt +++ b/core/support/src/main/kotlin/au/com/dius/pact/core/support/Version.kt @@ -1,5 +1,6 @@ package au.com.dius.pact.core.support +import au.com.dius.pact.core.support.parsers.StringLexer import com.github.michaelbull.result.Err import com.github.michaelbull.result.Ok import com.github.michaelbull.result.Result @@ -22,68 +23,52 @@ data class Version( @JvmStatic fun parse(version: String): Result { - var buffer = version - var index = 0 + val lexer = StringLexer(version) - val major = when (val result = parseInt(buffer, index)) { - is Ok -> { - buffer = result.value.second - index = result.value.third - result.value.first - } + val major = when (val result = parseInt(lexer)) { + is Ok -> result.value is Err -> return result } - when (val dot = parseChar('.', buffer, index)) { - is Ok -> { - buffer = dot.value.first - index = dot.value.second - } - is Err -> { - return dot - } + val err = parseChar('.', lexer) + if (err != null) { + return Err(err) } - val minor = when (val result = parseInt(buffer, index)) { - is Ok -> { - buffer = result.value.second - index = result.value.third - result.value.first - } + val minor = when (val result = parseInt(lexer)) { + is Ok -> result.value is Err -> return result } - val dot = parseChar('.', buffer, index) return when { - dot is Ok -> { - buffer = dot.value.first - index = dot.value.second - when (val result = parseInt(buffer, index)) { - is Ok -> Ok(Version(major, minor, result.value.first)) + lexer.peekNextChar() == '.' -> { + lexer.advance() + when (val result = parseInt(lexer)) { + is Ok -> if (lexer.empty) { + Ok(Version(major, minor, result.value)) + } else { + Err("Unexpected characters '${lexer.remainder}' at index ${lexer.index}") + } is Err -> result } } - buffer.isEmpty() -> Ok(Version(major, minor)) - else -> Err("Unexpected character '${buffer[0]}' at index $index") + lexer.empty -> Ok(Version(major, minor)) + else -> Err("Unexpected characters '${lexer.remainder}' at index ${lexer.index}") } } - private fun parseChar(c: Char, buffer: String, index: Int): Result, String> { - return when { - buffer.isNotEmpty() && buffer[0] == c -> { - Ok(buffer.substring(1) to (index + 1)) - } - else -> Err("Was expecting a $c at index $index") + private fun parseChar(c: Char, lexer: StringLexer): String? { + return when (val ch = lexer.nextChar()) { + null -> "Was expecting a '$c' at index ${lexer.index} but got the end of the input" + c -> null + else -> "Was expecting a '$c' at index ${lexer.index - 1} but got '$ch'" } } - private fun parseInt(buffer: String, index: Int): Result, String> { - return when (val result = INT.find(buffer)) { - null -> Err("Was expecting an integer at index $index") - else -> { - val i = result.value.toInt() - Ok(Triple(i, buffer.substring(result.value.length), index + result.value.length)) - } + private fun parseInt(lexer: StringLexer): Result { + return when (val result = lexer.matchRegex(INT)) { + null -> Err("Was expecting an integer at index ${lexer.index}") + else -> Ok(result.toInt()) } } } diff --git a/core/support/src/main/kotlin/au/com/dius/pact/core/support/generators/expressions/expressions.kt b/core/support/src/main/kotlin/au/com/dius/pact/core/support/generators/expressions/Expressions.kt similarity index 100% rename from core/support/src/main/kotlin/au/com/dius/pact/core/support/generators/expressions/expressions.kt rename to core/support/src/main/kotlin/au/com/dius/pact/core/support/generators/expressions/Expressions.kt diff --git a/core/support/src/main/kotlin/au/com/dius/pact/core/support/parsers/StringLexer.kt b/core/support/src/main/kotlin/au/com/dius/pact/core/support/parsers/StringLexer.kt new file mode 100644 index 0000000000..518dad3ee8 --- /dev/null +++ b/core/support/src/main/kotlin/au/com/dius/pact/core/support/parsers/StringLexer.kt @@ -0,0 +1,56 @@ +package au.com.dius.pact.core.support.parsers + +class StringLexer(private val buffer: String) { + var index = 0 + private set + + val empty: Boolean + get() = index >= buffer.length + + val remainder: String + get() = buffer.substring(index) + + fun nextChar(): Char? { + val c = peekNextChar() + if (c != null) { + index++ + } + return c + } + + fun peekNextChar(): Char? { + return if (empty) { + null + } else { + buffer[index] + } + } + + fun advance() { + advance(1) + } + + fun advance(count: Int) { + for (i in 0 until count) { + index++ + } + } + + fun skipWhitespace() { + var next = peekNextChar() + while (next != null && Character.isWhitespace(next)) { + advance() + next = peekNextChar() + } + } + + fun matchRegex(regex: Regex): String? { + return when (val result = regex.find(buffer.substring(index))) { + null -> null + else -> { + index += result.value.length + result.value + } + } + } +} diff --git a/core/support/src/test/groovy/au/com/dius/pact/core/support/VersionParserSpec.groovy b/core/support/src/test/groovy/au/com/dius/pact/core/support/VersionParserSpec.groovy index 43a1915f63..a038ee25f5 100644 --- a/core/support/src/test/groovy/au/com/dius/pact/core/support/VersionParserSpec.groovy +++ b/core/support/src/test/groovy/au/com/dius/pact/core/support/VersionParserSpec.groovy @@ -31,12 +31,13 @@ class VersionParserSpec extends Specification { version | error '' | 'Was expecting an integer at index 0' 'sdsd' | 'Was expecting an integer at index 0' - '0' | 'Was expecting a . at index 1' - '0sass' | 'Was expecting a . at index 1' - '100' | 'Was expecting a . at index 3' + '0' | "Was expecting a '.' at index 1 but got the end of the input" + '0sass' | "Was expecting a '.' at index 1 but got 's'" + '100' | "Was expecting a '.' at index 3 but got the end of the input" '100.' | 'Was expecting an integer at index 4' '100.10.' | 'Was expecting an integer at index 7' - '100.10x' | "Unexpected character 'x' at index 6" + '100.10x' | "Unexpected characters 'x' at index 6" '100.10.sss' | 'Was expecting an integer at index 7' + '100.10.1ss' | "Unexpected characters 'ss' at index 8" } }