Skip to content

Commit 739a40d

Browse files
committed
refactor: extract common lexer functions from version parser #1615
1 parent 0b1be93 commit 739a40d

File tree

4 files changed

+89
-47
lines changed

4 files changed

+89
-47
lines changed

core/support/src/main/kotlin/au/com/dius/pact/core/support/Version.kt

Lines changed: 28 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package au.com.dius.pact.core.support
22

3+
import au.com.dius.pact.core.support.parsers.StringLexer
34
import com.github.michaelbull.result.Err
45
import com.github.michaelbull.result.Ok
56
import com.github.michaelbull.result.Result
@@ -22,68 +23,52 @@ data class Version(
2223

2324
@JvmStatic
2425
fun parse(version: String): Result<Version, String> {
25-
var buffer = version
26-
var index = 0
26+
val lexer = StringLexer(version)
2727

28-
val major = when (val result = parseInt(buffer, index)) {
29-
is Ok -> {
30-
buffer = result.value.second
31-
index = result.value.third
32-
result.value.first
33-
}
28+
val major = when (val result = parseInt(lexer)) {
29+
is Ok -> result.value
3430
is Err -> return result
3531
}
3632

37-
when (val dot = parseChar('.', buffer, index)) {
38-
is Ok -> {
39-
buffer = dot.value.first
40-
index = dot.value.second
41-
}
42-
is Err -> {
43-
return dot
44-
}
33+
val err = parseChar('.', lexer)
34+
if (err != null) {
35+
return Err(err)
4536
}
4637

47-
val minor = when (val result = parseInt(buffer, index)) {
48-
is Ok -> {
49-
buffer = result.value.second
50-
index = result.value.third
51-
result.value.first
52-
}
38+
val minor = when (val result = parseInt(lexer)) {
39+
is Ok -> result.value
5340
is Err -> return result
5441
}
5542

56-
val dot = parseChar('.', buffer, index)
5743
return when {
58-
dot is Ok -> {
59-
buffer = dot.value.first
60-
index = dot.value.second
61-
when (val result = parseInt(buffer, index)) {
62-
is Ok -> Ok(Version(major, minor, result.value.first))
44+
lexer.peekNextChar() == '.' -> {
45+
lexer.advance()
46+
when (val result = parseInt(lexer)) {
47+
is Ok -> if (lexer.empty) {
48+
Ok(Version(major, minor, result.value))
49+
} else {
50+
Err("Unexpected characters '${lexer.remainder}' at index ${lexer.index}")
51+
}
6352
is Err -> result
6453
}
6554
}
66-
buffer.isEmpty() -> Ok(Version(major, minor))
67-
else -> Err("Unexpected character '${buffer[0]}' at index $index")
55+
lexer.empty -> Ok(Version(major, minor))
56+
else -> Err("Unexpected characters '${lexer.remainder}' at index ${lexer.index}")
6857
}
6958
}
7059

71-
private fun parseChar(c: Char, buffer: String, index: Int): Result<Pair<String, Int>, String> {
72-
return when {
73-
buffer.isNotEmpty() && buffer[0] == c -> {
74-
Ok(buffer.substring(1) to (index + 1))
75-
}
76-
else -> Err("Was expecting a $c at index $index")
60+
private fun parseChar(c: Char, lexer: StringLexer): String? {
61+
return when (val ch = lexer.nextChar()) {
62+
null -> "Was expecting a '$c' at index ${lexer.index} but got the end of the input"
63+
c -> null
64+
else -> "Was expecting a '$c' at index ${lexer.index - 1} but got '$ch'"
7765
}
7866
}
7967

80-
private fun parseInt(buffer: String, index: Int): Result<Triple<Int, String, Int>, String> {
81-
return when (val result = INT.find(buffer)) {
82-
null -> Err("Was expecting an integer at index $index")
83-
else -> {
84-
val i = result.value.toInt()
85-
Ok(Triple(i, buffer.substring(result.value.length), index + result.value.length))
86-
}
68+
private fun parseInt(lexer: StringLexer): Result<Int, String> {
69+
return when (val result = lexer.matchRegex(INT)) {
70+
null -> Err("Was expecting an integer at index ${lexer.index}")
71+
else -> Ok(result.toInt())
8772
}
8873
}
8974
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package au.com.dius.pact.core.support.parsers
2+
3+
class StringLexer(private val buffer: String) {
4+
var index = 0
5+
private set
6+
7+
val empty: Boolean
8+
get() = index >= buffer.length
9+
10+
val remainder: String
11+
get() = buffer.substring(index)
12+
13+
fun nextChar(): Char? {
14+
val c = peekNextChar()
15+
if (c != null) {
16+
index++
17+
}
18+
return c
19+
}
20+
21+
fun peekNextChar(): Char? {
22+
return if (empty) {
23+
null
24+
} else {
25+
buffer[index]
26+
}
27+
}
28+
29+
fun advance() {
30+
advance(1)
31+
}
32+
33+
fun advance(count: Int) {
34+
for (i in 0 until count) {
35+
index++
36+
}
37+
}
38+
39+
fun skipWhitespace() {
40+
var next = peekNextChar()
41+
while (next != null && Character.isWhitespace(next)) {
42+
advance()
43+
next = peekNextChar()
44+
}
45+
}
46+
47+
fun matchRegex(regex: Regex): String? {
48+
return when (val result = regex.find(buffer.substring(index))) {
49+
null -> null
50+
else -> {
51+
index += result.value.length
52+
result.value
53+
}
54+
}
55+
}
56+
}

core/support/src/test/groovy/au/com/dius/pact/core/support/VersionParserSpec.groovy

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,13 @@ class VersionParserSpec extends Specification {
3131
version | error
3232
'' | 'Was expecting an integer at index 0'
3333
'sdsd' | 'Was expecting an integer at index 0'
34-
'0' | 'Was expecting a . at index 1'
35-
'0sass' | 'Was expecting a . at index 1'
36-
'100' | 'Was expecting a . at index 3'
34+
'0' | "Was expecting a '.' at index 1 but got the end of the input"
35+
'0sass' | "Was expecting a '.' at index 1 but got 's'"
36+
'100' | "Was expecting a '.' at index 3 but got the end of the input"
3737
'100.' | 'Was expecting an integer at index 4'
3838
'100.10.' | 'Was expecting an integer at index 7'
39-
'100.10x' | "Unexpected character 'x' at index 6"
39+
'100.10x' | "Unexpected characters 'x' at index 6"
4040
'100.10.sss' | 'Was expecting an integer at index 7'
41+
'100.10.1ss' | "Unexpected characters 'ss' at index 8"
4142
}
4243
}

0 commit comments

Comments
 (0)