From 5d78360b2a6bdd177705a7daf2e6b2934373b226 Mon Sep 17 00:00:00 2001 From: Ronald Holshausen Date: Thu, 27 Oct 2022 17:04:00 +1100 Subject: [PATCH] refactor: Convert ANTLR DateExpression parser to a recursive decent parser #1615 --- .../core/model/generators/DateExpression.kt | 18 +- .../generators/DateExpressionSpec.groovy | 7 +- .../generators/DateTimeExpressionSpec.groovy | 11 +- .../generators/expressions/DateExpression.g4 | 99 ------- .../au/com/dius/pact/core/support/Version.kt | 15 +- .../generators/expressions/DateExpression.kt | 276 ++++++++++++++++++ .../pact/core/support/parsers/StringLexer.kt | 37 ++- 7 files changed, 327 insertions(+), 136 deletions(-) delete mode 100644 core/support/src/main/antlr/au/com/dius/pact/core/support/generators/expressions/DateExpression.g4 create mode 100644 core/support/src/main/kotlin/au/com/dius/pact/core/support/generators/expressions/DateExpression.kt diff --git a/core/model/src/main/kotlin/au/com/dius/pact/core/model/generators/DateExpression.kt b/core/model/src/main/kotlin/au/com/dius/pact/core/model/generators/DateExpression.kt index 82e270204a..9563ba644c 100644 --- a/core/model/src/main/kotlin/au/com/dius/pact/core/model/generators/DateExpression.kt +++ b/core/model/src/main/kotlin/au/com/dius/pact/core/model/generators/DateExpression.kt @@ -10,8 +10,6 @@ import au.com.dius.pact.core.support.generators.expressions.DateExpressionParser import au.com.dius.pact.core.support.generators.expressions.DateOffsetType import au.com.dius.pact.core.support.generators.expressions.Operation import mu.KLogging -import org.antlr.v4.runtime.CharStreams -import org.antlr.v4.runtime.CommonTokenStream import java.time.DayOfWeek import java.time.Month import java.time.OffsetDateTime @@ -142,17 +140,11 @@ object DateExpression : KLogging() { } private fun parseDateExpression(expression: String): Result { - val charStream = CharStreams.fromString(expression) - val lexer = DateExpressionLexer(charStream) - val tokens = CommonTokenStream(lexer) - val parser = DateExpressionParser(tokens) - val errorListener = ErrorListener() - parser.addErrorListener(errorListener) - val result = parser.expression() - return if (errorListener.errors.isNotEmpty()) { - Err("Error parsing expression: ${errorListener.errors.joinToString(", ")}") - } else { - Ok(ParsedDateExpression(result.dateBase, result.adj)) + val lexer = DateExpressionLexer(expression) + val parser = DateExpressionParser(lexer) + return when (val result = parser.expression()) { + is Err -> Err("Error parsing expression: ${result.error}") + is Ok -> Ok(ParsedDateExpression(result.value.first, result.value.second.toMutableList())) } } } diff --git a/core/model/src/test/groovy/au/com/dius/pact/core/model/generators/DateExpressionSpec.groovy b/core/model/src/test/groovy/au/com/dius/pact/core/model/generators/DateExpressionSpec.groovy index 90c84530f0..ab1cb84adb 100644 --- a/core/model/src/test/groovy/au/com/dius/pact/core/model/generators/DateExpressionSpec.groovy +++ b/core/model/src/test/groovy/au/com/dius/pact/core/model/generators/DateExpressionSpec.groovy @@ -1,6 +1,5 @@ -package au.com.dius.pact.model.generators +package au.com.dius.pact.core.model.generators -import au.com.dius.pact.core.model.generators.DateExpression import spock.lang.Specification import spock.lang.Unroll @@ -55,8 +54,8 @@ class DateExpressionSpec extends Specification { where: expression | expected - '+' | 'Error parsing expression: line 1:1 mismatched input \'\' expecting INT' - 'now +' | 'Error parsing expression: line 1:5 mismatched input \'\' expecting INT' + '+' | 'Error parsing expression: Was expecting an integer at index 1' + 'now +' | 'Error parsing expression: Was expecting an integer at index 5' 'tomorr' | /^Error parsing expression.*/ } } diff --git a/core/model/src/test/groovy/au/com/dius/pact/core/model/generators/DateTimeExpressionSpec.groovy b/core/model/src/test/groovy/au/com/dius/pact/core/model/generators/DateTimeExpressionSpec.groovy index fb66bc4af4..ab0e2d42eb 100644 --- a/core/model/src/test/groovy/au/com/dius/pact/core/model/generators/DateTimeExpressionSpec.groovy +++ b/core/model/src/test/groovy/au/com/dius/pact/core/model/generators/DateTimeExpressionSpec.groovy @@ -1,6 +1,5 @@ -package au.com.dius.pact.model.generators +package au.com.dius.pact.core.model.generators -import au.com.dius.pact.core.model.generators.DateTimeExpression import spock.lang.Specification import spock.lang.Unroll @@ -102,12 +101,12 @@ class DateTimeExpressionSpec extends Specification { where: expression | expected - '+' | 'Error parsing expression: line 1:1 mismatched input \'\' expecting INT' - 'now +' | 'Error parsing expression: line 1:5 mismatched input \'\' expecting INT' + '+' | 'Error parsing expression: Was expecting an integer at index 1' + 'now +' | 'Error parsing expression: Was expecting an integer at index 5' 'tomorr' | /^Error parsing expression.*/ 'now @ +' | 'Error parsing expression: line 1:7 mismatched input \'\' expecting INT' - '+ @ +' | 'Error parsing expression: line 1:2 mismatched input \'\' expecting INT, Error parsing expression: line 1:5 mismatched input \'\' expecting INT' - 'now+ @ now +' | 'Error parsing expression: line 1:5 mismatched input \'\' expecting INT, Error parsing expression: line 1:12 mismatched input \'\' expecting INT' + '+ @ +' | 'Error parsing expression: Was expecting an integer at index 2, Error parsing expression: line 1:5 mismatched input \'\' expecting INT' + 'now+ @ now +' | 'Error parsing expression: Was expecting an integer at index 5, Error parsing expression: line 1:12 mismatched input \'\' expecting INT' 'now @ now +' | 'Error parsing expression: line 1:11 mismatched input \'\' expecting INT' 'now @ noo' | /^Error parsing expression.*/ } diff --git a/core/support/src/main/antlr/au/com/dius/pact/core/support/generators/expressions/DateExpression.g4 b/core/support/src/main/antlr/au/com/dius/pact/core/support/generators/expressions/DateExpression.g4 deleted file mode 100644 index cbb218bc23..0000000000 --- a/core/support/src/main/antlr/au/com/dius/pact/core/support/generators/expressions/DateExpression.g4 +++ /dev/null @@ -1,99 +0,0 @@ -grammar DateExpression; - -@header { - package au.com.dius.pact.core.support.generators.expressions; -} - -expression returns [ DateBase dateBase = DateBase.NOW, List> adj = new ArrayList<>() ] : ( base { $dateBase = $base.t; } - | op duration { if ($duration.d != null) $adj.add($duration.d.withOperation($op.o)); } ( op duration { if ($duration.d != null) $adj.add($duration.d.withOperation($op.o)); } )* - | base { $dateBase = $base.t; } ( op duration { if ($duration.d != null) $adj.add($duration.d.withOperation($op.o)); } )* - | 'next' offset { $adj.add(new Adjustment($offset.type, $offset.val, Operation.PLUS)); } - | 'next' offset { $adj.add(new Adjustment($offset.type, $offset.val, Operation.PLUS)); } ( op duration { - if ($duration.d != null) $adj.add($duration.d.withOperation($op.o)); - } )* - | 'last' offset { $adj.add(new Adjustment($offset.type, $offset.val, Operation.MINUS)); } - | 'last' offset { $adj.add(new Adjustment($offset.type, $offset.val, Operation.MINUS)); } ( op duration { - if ($duration.d != null) $adj.add($duration.d.withOperation($op.o)); - } )* - ) EOF - ; - -base returns [ DateBase t ] : 'now' { $t = DateBase.NOW; } - | 'today' { $t = DateBase.TODAY; } - | 'yesterday' { $t = DateBase.YESTERDAY; } - | 'tomorrow' { $t = DateBase.TOMORROW; } - ; - -duration returns [ Adjustment d ] : INT durationType { $d = new Adjustment($durationType.type, $INT.int); } ; - -durationType returns [ DateOffsetType type ] : 'day' { $type = DateOffsetType.DAY; } - | DAYS { $type = DateOffsetType.DAY; } - | 'week' { $type = DateOffsetType.WEEK; } - | WEEKS { $type = DateOffsetType.WEEK; } - | 'month' { $type = DateOffsetType.MONTH; } - | MONTHS { $type = DateOffsetType.MONTH; } - | 'year' { $type = DateOffsetType.YEAR; } - | YEARS { $type = DateOffsetType.YEAR; } - ; - -op returns [ Operation o ] : '+' { $o = Operation.PLUS; } - | '-' { $o = Operation.MINUS; } - ; - -offset returns [ DateOffsetType type, int val = 1 ] : 'day' { $type = DateOffsetType.DAY; } - | 'week' { $type = DateOffsetType.WEEK; } - | 'month' { $type = DateOffsetType.MONTH; } - | 'year' { $type = DateOffsetType.YEAR; } - | 'fortnight' { $type = DateOffsetType.WEEK; $val = 2; } - | 'monday' { $type = DateOffsetType.MONDAY; } - | 'mon' { $type = DateOffsetType.MONDAY; } - | 'tuesday' { $type = DateOffsetType.TUESDAY; } - | 'tues' { $type = DateOffsetType.TUESDAY; } - | 'wednesday' { $type = DateOffsetType.WEDNESDAY; } - | 'wed' { $type = DateOffsetType.WEDNESDAY; } - | 'thursday' { $type = DateOffsetType.THURSDAY; } - | 'thurs' { $type = DateOffsetType.THURSDAY; } - | 'friday' { $type = DateOffsetType.FRIDAY; } - | 'fri' { $type = DateOffsetType.FRIDAY; } - | 'saturday' { $type = DateOffsetType.SATURDAY; } - | 'sat' { $type = DateOffsetType.SATURDAY; } - | 'sunday' { $type = DateOffsetType.SUNDAY; } - | 'sun' { $type = DateOffsetType.SUNDAY; } - | 'january' { $type = DateOffsetType.JAN; } - | 'jan' { $type = DateOffsetType.JAN; } - | 'february' { $type = DateOffsetType.FEB; } - | 'feb' { $type = DateOffsetType.FEB; } - | 'march' { $type = DateOffsetType.MAR; } - | 'mar' { $type = DateOffsetType.MAR; } - | 'april' { $type = DateOffsetType.APR; } - | 'apr' { $type = DateOffsetType.APR; } - | 'may' { $type = DateOffsetType.MAY; } - | 'june' { $type = DateOffsetType.JUNE; } - | 'jun' { $type = DateOffsetType.JUNE; } - | 'july' { $type = DateOffsetType.JULY; } - | 'jul' { $type = DateOffsetType.JULY; } - | 'august' { $type = DateOffsetType.AUG; } - | 'aug' { $type = DateOffsetType.AUG; } - | 'september' { $type = DateOffsetType.SEP; } - | 'sep' { $type = DateOffsetType.SEP; } - | 'october' { $type = DateOffsetType.OCT; } - | 'oct' { $type = DateOffsetType.OCT; } - | 'november' { $type = DateOffsetType.NOV; } - | 'nov' { $type = DateOffsetType.NOV; } - | 'december' { $type = DateOffsetType.DEC; } - | 'dec' { $type = DateOffsetType.DEC; } - ; - -INT : DIGIT+ ; -fragment DIGIT : [0-9] ; - -WS : [ \t\n\r] + -> skip ; - -DAYS : DAY 's'? ; -fragment DAY : 'day' ; -WEEKS : WEEK 's'? ; -fragment WEEK : 'week'; -MONTHS : MONTH 's'? ; -fragment MONTH : 'month'; -YEARS : YEAR 's'? ; -fragment YEAR : 'year'; 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 846651edfb..c9fc7fe39d 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 @@ -19,13 +19,11 @@ data class Version( } companion object { - val INT = Regex("^\\d+") - @JvmStatic fun parse(version: String): Result { val lexer = StringLexer(version) - val major = when (val result = parseInt(lexer)) { + val major = when (val result = lexer.parseInt()) { is Ok -> result.value is Err -> return result } @@ -35,7 +33,7 @@ data class Version( return Err(err) } - val minor = when (val result = parseInt(lexer)) { + val minor = when (val result = lexer.parseInt()) { is Ok -> result.value is Err -> return result } @@ -43,7 +41,7 @@ data class Version( return when { lexer.peekNextChar() == '.' -> { lexer.advance() - when (val result = parseInt(lexer)) { + when (val result = lexer.parseInt()) { is Ok -> if (lexer.empty) { Ok(Version(major, minor, result.value)) } else { @@ -64,12 +62,5 @@ data class Version( else -> "Was expecting a '$c' at index ${lexer.index - 1} but got '$ch'" } } - - 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/DateExpression.kt b/core/support/src/main/kotlin/au/com/dius/pact/core/support/generators/expressions/DateExpression.kt new file mode 100644 index 0000000000..457439b963 --- /dev/null +++ b/core/support/src/main/kotlin/au/com/dius/pact/core/support/generators/expressions/DateExpression.kt @@ -0,0 +1,276 @@ +package au.com.dius.pact.core.support.generators.expressions + +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 + +class DateExpressionLexer(expression: String): StringLexer(expression) { + fun parseDateBase(): DateBase? { + return when { + matchString("now") -> DateBase.NOW + matchString("today") -> DateBase.TODAY + matchString("yesterday") -> DateBase.YESTERDAY + matchString("tomorrow") -> DateBase.TOMORROW + else -> null + } + } + + fun parseOperation(): Operation? { + return when { + matchChar('+') -> Operation.PLUS + matchChar('-') -> Operation.MINUS + else -> null + } + } + + fun parseDateOffsetType(): DateOffsetType? { + return when { + matchRegex(DAYS) != null -> DateOffsetType.DAY + matchRegex(WEEKS) != null -> DateOffsetType.WEEK + matchRegex(MONTHS) != null -> DateOffsetType.MONTH + matchRegex(YEARS) != null -> DateOffsetType.YEAR + else -> null + } + } + + companion object { + val DAYS = Regex("^days?") + val WEEKS = Regex("^weeks?") + val MONTHS = Regex("^months?") + val YEARS = Regex("^years?") + } +} + +@Suppress("MaxLineLength") +class DateExpressionParser(private val lexer: DateExpressionLexer) { + //expression returns [ DateBase dateBase = DateBase.NOW, List> adj = new ArrayList<>() ] : ( base { $dateBase = $base.t; } + // | op duration { if ($duration.d != null) $adj.add($duration.d.withOperation($op.o)); } ( op duration { if ($duration.d != null) $adj.add($duration.d.withOperation($op.o)); } )* + // | base { $dateBase = $base.t; } ( op duration { if ($duration.d != null) $adj.add($duration.d.withOperation($op.o)); } )* + // | 'next' offset { $adj.add(new Adjustment($offset.type, $offset.val, Operation.PLUS)); } + // | 'next' offset { $adj.add(new Adjustment($offset.type, $offset.val, Operation.PLUS)); } ( op duration { + // if ($duration.d != null) $adj.add($duration.d.withOperation($op.o)); + // } )* + // | 'last' offset { $adj.add(new Adjustment($offset.type, $offset.val, Operation.MINUS)); } + // | 'last' offset { $adj.add(new Adjustment($offset.type, $offset.val, Operation.MINUS)); } ( op duration { + // if ($duration.d != null) $adj.add($duration.d.withOperation($op.o)); + // } )* + // ) EOF + // ; + @Suppress("ComplexMethod", "ReturnCount") + fun expression(): Result>>, String> { + val dateBase = DateBase.NOW + + val baseResult = base() + if (baseResult != null) { + return when (val opResult = parseOp()) { + is Ok -> if (opResult.value != null) { + Ok(baseResult to opResult.value!!) + } else { + Ok(baseResult to emptyList()) + } + is Err -> opResult + } + } + + when (val opResult = parseOp()) { + is Ok -> if (opResult.value != null) { + return Ok(dateBase to opResult.value!!) + } + is Err -> return opResult + } + + val nextOrLastResult = parseNextOrLast() + if (nextOrLastResult != null) { + return when (val offsetResult = offset()) { + is Ok -> { + val adj = mutableListOf>() + adj.add(Adjustment(offsetResult.value.first, offsetResult.value.second, nextOrLastResult)) + when (val opResult = parseOp()) { + is Ok -> if (opResult.value != null) { + adj.addAll(opResult.value!!) + Ok(dateBase to adj) + } else { + Ok(dateBase to adj) + } + is Err -> opResult + } + } + is Err -> offsetResult + } + } + + return if (lexer.empty) { + Ok(dateBase to emptyList()) + } else { + Err("Error parsing expression: Unexpected characters '${lexer.remainder}' at ${lexer.index}") + } + } + + private fun parseNextOrLast(): Operation? { + lexer.skipWhitespace() + return when { + lexer.matchString("next") -> Operation.PLUS + lexer.matchString("last") -> Operation.MINUS + else -> null + } + } + + @Suppress("ReturnCount") + private fun parseOp(): Result>?, String> { + val adj = mutableListOf>() + var opResult = op() + if (opResult != null) { + while (opResult != null) { + when (val durationResult = duration()) { + is Ok -> adj.add(durationResult.value.withOperation(opResult)) + is Err -> return durationResult + } + opResult = op() + } + return Ok(adj) + } + return Ok(null) + } + + //base returns [ DateBase t ] : 'now' { $t = DateBase.NOW; } + // | 'today' { $t = DateBase.TODAY; } + // | 'yesterday' { $t = DateBase.YESTERDAY; } + // | 'tomorrow' { $t = DateBase.TOMORROW; } + // ; + fun base(): DateBase? { + lexer.skipWhitespace() + return lexer.parseDateBase() + } + + //duration returns [ Adjustment d ] : INT durationType { $d = new Adjustment($durationType.type, $INT.int); } ; + fun duration(): Result, String> { + lexer.skipWhitespace() + + val intResult = when (val result = lexer.parseInt()) { + is Ok -> result.value + is Err -> return result + } + + val durationTypeResult = durationType() + return if (durationTypeResult != null) { + Ok(Adjustment(durationTypeResult, intResult)) + } else { + Err("Was expecting a duration type at index ${lexer.index}") + } + } + + //durationType returns [ DateOffsetType type ] : 'day' { $type = DateOffsetType.DAY; } + // | DAYS { $type = DateOffsetType.DAY; } + // | 'week' { $type = DateOffsetType.WEEK; } + // | WEEKS { $type = DateOffsetType.WEEK; } + // | 'month' { $type = DateOffsetType.MONTH; } + // | MONTHS { $type = DateOffsetType.MONTH; } + // | 'year' { $type = DateOffsetType.YEAR; } + // | YEARS { $type = DateOffsetType.YEAR; } + // ; + fun durationType(): DateOffsetType? { + lexer.skipWhitespace() + return lexer.parseDateOffsetType() + } + + //op returns [ Operation o ] : '+' { $o = Operation.PLUS; } + // | '-' { $o = Operation.MINUS; } + fun op(): Operation? { + lexer.skipWhitespace() + return lexer.parseOperation() + } + + //offset returns [ DateOffsetType type, int val = 1 ] : 'day' { $type = DateOffsetType.DAY; } + // | 'week' { $type = DateOffsetType.WEEK; } + // | 'month' { $type = DateOffsetType.MONTH; } + // | 'year' { $type = DateOffsetType.YEAR; } + // | 'fortnight' { $type = DateOffsetType.WEEK; $val = 2; } + // | 'monday' { $type = DateOffsetType.MONDAY; } + // | 'mon' { $type = DateOffsetType.MONDAY; } + // | 'tuesday' { $type = DateOffsetType.TUESDAY; } + // | 'tues' { $type = DateOffsetType.TUESDAY; } + // | 'wednesday' { $type = DateOffsetType.WEDNESDAY; } + // | 'wed' { $type = DateOffsetType.WEDNESDAY; } + // | 'thursday' { $type = DateOffsetType.THURSDAY; } + // | 'thurs' { $type = DateOffsetType.THURSDAY; } + // | 'friday' { $type = DateOffsetType.FRIDAY; } + // | 'fri' { $type = DateOffsetType.FRIDAY; } + // | 'saturday' { $type = DateOffsetType.SATURDAY; } + // | 'sat' { $type = DateOffsetType.SATURDAY; } + // | 'sunday' { $type = DateOffsetType.SUNDAY; } + // | 'sun' { $type = DateOffsetType.SUNDAY; } + // | 'january' { $type = DateOffsetType.JAN; } + // | 'jan' { $type = DateOffsetType.JAN; } + // | 'february' { $type = DateOffsetType.FEB; } + // | 'feb' { $type = DateOffsetType.FEB; } + // | 'march' { $type = DateOffsetType.MAR; } + // | 'mar' { $type = DateOffsetType.MAR; } + // | 'april' { $type = DateOffsetType.APR; } + // | 'apr' { $type = DateOffsetType.APR; } + // | 'may' { $type = DateOffsetType.MAY; } + // | 'june' { $type = DateOffsetType.JUNE; } + // | 'jun' { $type = DateOffsetType.JUNE; } + // | 'july' { $type = DateOffsetType.JULY; } + // | 'jul' { $type = DateOffsetType.JULY; } + // | 'august' { $type = DateOffsetType.AUG; } + // | 'aug' { $type = DateOffsetType.AUG; } + // | 'september' { $type = DateOffsetType.SEP; } + // | 'sep' { $type = DateOffsetType.SEP; } + // | 'october' { $type = DateOffsetType.OCT; } + // | 'oct' { $type = DateOffsetType.OCT; } + // | 'november' { $type = DateOffsetType.NOV; } + // | 'nov' { $type = DateOffsetType.NOV; } + // | 'december' { $type = DateOffsetType.DEC; } + // | 'dec' { $type = DateOffsetType.DEC; } + // ; + @Suppress("ComplexMethod") + fun offset(): Result, String> { + lexer.skipWhitespace() + return when { + lexer.matchString("day") -> Ok(DateOffsetType.DAY to 1) + lexer.matchString("week") -> Ok(DateOffsetType.WEEK to 1) + lexer.matchString("month") -> Ok(DateOffsetType.MONTH to 1) + lexer.matchString("year") -> Ok(DateOffsetType.YEAR to 1) + lexer.matchString("fortnight") -> Ok(DateOffsetType.WEEK to 2) + lexer.matchString("monday") -> Ok(DateOffsetType.MONDAY to 1) + lexer.matchString("mon") -> Ok(DateOffsetType.MONDAY to 1) + lexer.matchString("tuesday") -> Ok(DateOffsetType.TUESDAY to 1) + lexer.matchString("tues") -> Ok(DateOffsetType.TUESDAY to 1) + lexer.matchString("wednesday") -> Ok(DateOffsetType.WEDNESDAY to 1) + lexer.matchString("wed") -> Ok(DateOffsetType.WEDNESDAY to 1) + lexer.matchString("thursday") -> Ok(DateOffsetType.THURSDAY to 1) + lexer.matchString("thurs") -> Ok(DateOffsetType.THURSDAY to 1) + lexer.matchString("friday") -> Ok(DateOffsetType.FRIDAY to 1) + lexer.matchString("fri") -> Ok(DateOffsetType.FRIDAY to 1) + lexer.matchString("saturday") -> Ok(DateOffsetType.SATURDAY to 1) + lexer.matchString("sat") -> Ok(DateOffsetType.SATURDAY to 1) + lexer.matchString("sunday") -> Ok(DateOffsetType.SUNDAY to 1) + lexer.matchString("sun") -> Ok(DateOffsetType.SUNDAY to 1) + lexer.matchString("january") -> Ok(DateOffsetType.JAN to 1) + lexer.matchString("jan") -> Ok(DateOffsetType.JAN to 1) + lexer.matchString("february") -> Ok(DateOffsetType.FEB to 1) + lexer.matchString("feb") -> Ok(DateOffsetType.FEB to 1) + lexer.matchString("march") -> Ok(DateOffsetType.MAR to 1) + lexer.matchString("mar") -> Ok(DateOffsetType.MAR to 1) + lexer.matchString("april") -> Ok(DateOffsetType.APR to 1) + lexer.matchString("apr") -> Ok(DateOffsetType.APR to 1) + lexer.matchString("may") -> Ok(DateOffsetType.MAY to 1) + lexer.matchString("june") -> Ok(DateOffsetType.JUNE to 1) + lexer.matchString("jun") -> Ok(DateOffsetType.JUNE to 1) + lexer.matchString("july") -> Ok(DateOffsetType.JULY to 1) + lexer.matchString("jul") -> Ok(DateOffsetType.JULY to 1) + lexer.matchString("august") -> Ok(DateOffsetType.AUG to 1) + lexer.matchString("aug") -> Ok(DateOffsetType.AUG to 1) + lexer.matchString("september") -> Ok(DateOffsetType.SEP to 1) + lexer.matchString("sep") -> Ok(DateOffsetType.SEP to 1) + lexer.matchString("october") -> Ok(DateOffsetType.OCT to 1) + lexer.matchString("oct") -> Ok(DateOffsetType.OCT to 1) + lexer.matchString("november") -> Ok(DateOffsetType.NOV to 1) + lexer.matchString("nov") -> Ok(DateOffsetType.NOV to 1) + lexer.matchString("december") -> Ok(DateOffsetType.DEC to 1) + lexer.matchString("dec") -> Ok(DateOffsetType.DEC to 1) + else -> Err("Was expecting an offset type at index ${lexer.index}") + } + } +} 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 index 518dad3ee8..f08fb48d4a 100644 --- 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 @@ -1,6 +1,10 @@ package au.com.dius.pact.core.support.parsers -class StringLexer(private val buffer: String) { +import com.github.michaelbull.result.Err +import com.github.michaelbull.result.Ok +import com.github.michaelbull.result.Result + +open class StringLexer(private val buffer: String) { var index = 0 private set @@ -31,7 +35,7 @@ class StringLexer(private val buffer: String) { } fun advance(count: Int) { - for (i in 0 until count) { + for (_i in 0 until count) { index++ } } @@ -53,4 +57,33 @@ class StringLexer(private val buffer: String) { } } } + + fun matchString(s: String): Boolean { + return if (buffer.startsWith(s, index)) { + index += s.length + true + } else { + false + } + } + + fun matchChar(c: Char): Boolean { + return if (peekNextChar() == c) { + index++ + true + } else { + false + } + } + + fun parseInt(): Result { + return when (val result = matchRegex(INT)) { + null -> Err("Was expecting an integer at index $index") + else -> Ok(result.toInt()) + } + } + + companion object { + val INT = Regex("^\\d+") + } }