fix(parser): Fix compare ops parsing (#748)

* Fix order of compare ops parse

* Add tests
This commit is contained in:
InversionSpaces 2023-06-14 14:52:30 +02:00 committed by GitHub
parent 45c44b3046
commit 739854a20b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 40 additions and 13 deletions

View File

@ -153,8 +153,10 @@ object InfixToken {
case Lt extends Op("<") case Lt extends Op("<")
case Lte extends Op("<=") case Lte extends Op("<=")
def p: P[Unit] = P.string(symbol)
private def opsParser(ops: List[Op]): P[(Span, Op)] = private def opsParser(ops: List[Op]): P[(Span, Op)] =
P.oneOf(ops.map(op => P.string(op.symbol).lift.map(s => s.as(op)))) P.oneOf(ops.map(op => op.p.lift.map(s => s.as(op))))
// Parse left-associative operations `basic (OP basic)*`. // Parse left-associative operations `basic (OP basic)*`.
// We use this form to avoid left recursion. // We use this form to avoid left recursion.
@ -214,7 +216,10 @@ object InfixToken {
private val compare: P[ValueToken[Span.S]] = private val compare: P[ValueToken[Span.S]] =
infixParserNone( infixParserNone(
add, add,
Op.Gt :: Op.Gte :: Op.Lt :: Op.Lte :: Nil // Here order is important as
// `Op.Gt` is prefix of `Op.Gte`
// and `Op.Lt` is prefix of `Op.Lte`
Op.Gte :: Op.Lte :: Op.Gt :: Op.Lt :: Nil
) )
/** /**

View File

@ -50,9 +50,19 @@ class InfixTokenSpec extends AnyFlatSpec with Matchers with AquaSpec {
private def gt(left: ValueToken[Id], right: ValueToken[Id]): ValueToken[Id] = private def gt(left: ValueToken[Id], right: ValueToken[Id]): ValueToken[Id] =
infixToken(left, right, Gt) infixToken(left, right, Gt)
"primitive math expression" should "be parfvfsed" in { private def gte(left: ValueToken[Id], right: ValueToken[Id]): ValueToken[Id] =
infixToken(left, right, Gte)
private def lt(left: ValueToken[Id], right: ValueToken[Id]): ValueToken[Id] =
infixToken(left, right, Lt)
private def lte(left: ValueToken[Id], right: ValueToken[Id]): ValueToken[Id] =
infixToken(left, right, Lte)
"primitive math expression" should "be parsed" in {
val vt = ValueToken.`value`.parseAll("3").right.get.mapK(spanToId) val vt = ValueToken.`value`.parseAll("3").right.get.mapK(spanToId)
val vt1 = ValueToken.`value`.parseAll("2 - 3").right.get.mapK(spanToId)
val vt2 = ValueToken.`value`.parseAll("3 * 2 * 5").right.get.mapK(spanToId) val vt2 = ValueToken.`value`.parseAll("3 * 2 * 5").right.get.mapK(spanToId)
val vt3 = ValueToken.`value`.parseAll("3 + 1 * 2 - 2").right.get.mapK(spanToId) val vt3 = ValueToken.`value`.parseAll("3 + 1 * 2 - 2").right.get.mapK(spanToId)
@ -82,6 +92,7 @@ class InfixTokenSpec extends AnyFlatSpec with Matchers with AquaSpec {
.mapK(spanToId) .mapK(spanToId)
vt shouldBe literal(3) vt shouldBe literal(3)
vt1 shouldBe sub(literal(2), literal(3))
vt2 shouldBe mul(mul(3, 2), 5) vt2 shouldBe mul(mul(3, 2), 5)
vt3 shouldBe sub(add(3, mul(1, 2)), 2) vt3 shouldBe sub(add(3, mul(1, 2)), 2)
vt4 shouldBe add(add(add(add(add(add(5, 6), 10), 20), 1), 2), 4) vt4 shouldBe add(add(add(add(add(add(5, 6), 10), 20), 1), 2), 4)
@ -93,15 +104,6 @@ class InfixTokenSpec extends AnyFlatSpec with Matchers with AquaSpec {
vt11 shouldBe rem(2, 4) vt11 shouldBe rem(2, 4)
} }
"primitive math expression" should "be parsed" in {
val vt = ValueToken.`value`.parseAll("3 - 2").right.get.mapK(spanToId)
vt shouldBe
InfixToken[Id](literal(3), literal(2), Sub)
}
"primitive math expression with multiplication" should "be parsed" in { "primitive math expression with multiplication" should "be parsed" in {
val res = ValueToken.`value`.parseAll("(3 - 2) * 4").right.get.mapK(spanToId) val res = ValueToken.`value`.parseAll("(3 - 2) * 4").right.get.mapK(spanToId)
@ -244,7 +246,27 @@ class InfixTokenSpec extends AnyFlatSpec with Matchers with AquaSpec {
"simple cmp math expression " should "be parsed" in { "simple cmp math expression " should "be parsed" in {
val vt = ValueToken.`value`.parseAll("1 > 3").right.get.mapK(spanToId) val vt = ValueToken.`value`.parseAll("1 > 3").right.get.mapK(spanToId)
vt shouldBe InfixToken(literal(1), literal(3), Gt) val vt1 = ValueToken.`value`.parseAll("1 < 3").right.get.mapK(spanToId)
val vt2 = ValueToken.`value`.parseAll("1 >= 3").right.get.mapK(spanToId)
val vt3 = ValueToken.`value`.parseAll("1 <= 3").right.get.mapK(spanToId)
vt shouldBe gt(literal(1), literal(3))
vt1 shouldBe lt(literal(1), literal(3))
vt2 shouldBe gte(literal(1), literal(3))
vt3 shouldBe lte(literal(1), literal(3))
}
"complex cmp math expression " should "be parsed" in {
val test = (op: Op) => {
val vt =
ValueToken.`value`.parseAll(s"(1 + 2) ** 3 ${op.symbol} 4 - 5 * 6").right.get.mapK(spanToId)
val left = pow(add(literal(1), literal(2)), literal(3))
val right = sub(literal(4), mul(literal(5), literal(6)))
val exp = infixToken(left, right, op)
vt shouldBe exp
}
List(Gt, Lt, Gte, Lte).foreach(test)
} }
"simple cmp math expression in brackets " should "be parsed" in { "simple cmp math expression in brackets " should "be parsed" in {