Scala 解析器组合器:解析整数或浮点数

Scala parser combinators: parse either integer or float

编辑:已解决,请参阅下面的 "FIX"

我正在尝试设置一个 Scala 解析器组合器来根据数字的复杂性解析浮点数或整数。这是我目前拥有的:

import scala.util.parsing.combinator.JavaTokenParsers

trait NumberLiteral
case class IntegerLiteral(i:Int) extends NumberLiteral
case class FloatLiteral(f:Float) extends NumberLiteral

class Parser extends JavaTokenParsers {

  def integer:Parser[IntegerLiteral] = wholeNumber ^^ {i => new IntegerLiteral(i.toInt)}
  def float:Parser[FloatLiteral] = floatingPointNumber ^^ {f => new FloatLiteral(f.toFloat)}
  //FIX: def float:Parser[FloatLiteral] = """[+-]?[0-9]*((\.[0-9]+([eE][+-]?[0-9]+)?[fF]?)|([fF])|([eE][+-]?[0-9]+))\b""".r ^^ {f => new FloatLiteral(f.toFloat)} 

  def number:Parser[NumberLiteral] = integer | float;
  //FIX: def number:Parser[NumberLiteral] = float | integer;

}

我设置了 scalatest 来测试整数和浮点解析器,它们都可以工作。这是我的测试 class 的样子:

import org.scalatest._

class ParserSpec extends FlatSpec with Matchers {

  val parser = new Parser()

  "Parser" should "parse IntegerLiteral" in {
    parser.parseAll(parser.integer, "0").get should equal (new IntegerLiteral(0))
    parser.parseAll(parser.integer, "4").get should equal (new IntegerLiteral(4))
    parser.parseAll(parser.integer, "4448338").get should equal (new IntegerLiteral(4448338))
    parser.parseAll(parser.integer, "-33").get should equal (new IntegerLiteral(-33))
    parser.parseAll(parser.integer, "-10101010").get should equal (new IntegerLiteral(-10101010))
    parser.parseAll(parser.integer, "004").get should equal (new IntegerLiteral(4))
  }
  it should "parse FloatLiteral" in {
    parser.parseAll(parser.float, "1.0").get should equal (new FloatLiteral(1.0f))
    parser.parseAll(parser.float, "0").get should equal (new FloatLiteral(0))
    parser.parseAll(parser.float, "32.3").get should equal (new FloatLiteral(32.3f))
    parser.parseAll(parser.float, "3.4e3").get should equal (new FloatLiteral(3400))
    parser.parseAll(parser.float, "-10").get should equal (new FloatLiteral(-10))
    parser.parseAll(parser.float, "-4e-4").get should equal (new FloatLiteral(-0.0004f))
    parser.parseAll(parser.float, "003.4").get should equal (new FloatLiteral(3.4f))
    parser.parseAll(parser.float, "4f").get should equal (new FloatLiteral(4))
  }
  it should "parse NumberLiteral" in {
    parser.parseAll(parser.number, "32").get should equal (new IntegerLiteral(32))
    parser.parseAll(parser.number, "32.3").get should equal (new FloatLiteral(32.3f))
    parser.parseAll(parser.number, "32f").get should equal (new FloatLiteral(32))
    parser.parseAll(parser.number, "0.33").get should equal (new FloatLiteral(0.33f))
    parser.parseAll(parser.number, "32e2").get should equal (new IntegerLiteral(3200))
    parser.parseAll(parser.number, "0").get should equal (new IntegerLiteral(32))
    parser.parseAll(parser.number, "32.3e1").get should equal (new IntegerLiteral(323))
  }

}

IntegerLiteralFloatLiteral 测试都运行良好。如您所见,我想将数字解析为 IntegerLiteral 或 FloatLiteral,具体取决于它是否可以解析为 int 或 float。 NumberLiteral 测试中的第一行有效,但我在第二行收到以下错误:java.lang.RuntimeException: no result when parsing failed。我不明白为什么解析器会抛出这个错误,因为浮点解析器可以解析 32.3。我在使用 integer | float 的数字解析器中做错了什么吗?

只需交换它们:

...
def number:Parser[NumberLiteral] = float | integer //float first
...

示例:

scala> parser.parseAll(parser.number, "32.3").get
res0: NumberLiteral = FloatLiteral(32.3)

首先它不起作用的原因是解析器确实将“32”从“32.3”解析为整数——而未解析的尾部“.3”确实导致了错误。您可以使用 parse:

轻松查看它
...
def number:Parser[NumberLiteral] = integer | float //integer first
...

scala> parser.parse(parser.number, "32.3")
res3: parser.ParseResult[NumberLiteral] = [1.3] parsed: IntegerLiteral(32)

//And here is how to get unparsed tail (".3"): 

scala> val pointer = parser.parse(parser.number, "32.3").next
pointer: parser.Input = scala.util.parsing.input.CharSequenceReader@34a2d29d

scala> pointer.source.toString.drop(pointer.pos.column - 1)
res15: String = .3