【问题标题】:Scala parser combinators: parse either integer or floatScala解析器组合器:解析整数或浮点数
【发布时间】:2015-06-28 06:11:38
【问题描述】:

编辑:已解决,请参阅下面的“修复”

我正在尝试设置一个 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 来测试整数和浮点解析器,它们都可以工作。这是我的测试类的样子:

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 的数字解析器中做错了吗?

【问题讨论】:

    标签: scala parsing parser-combinators alternation


    【解决方案1】:

    只需交换它们:

    ...
    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
    

    【讨论】:

    • 这会导致浮点解析器将所有内容都视为浮点数。 (浮点解析器将成功处理任何数字,包括整数)。在这种情况下,测试在第一行失败,因为FloatLiteral(32.0) does not equal IntegerLiteral(32)。有没有办法导致浮点解析器在整数上失败?
    • 你可以定义你自己的基于正则表达式的浮点解析器,它需要强制.,比如"""[0-9]*.[0-9]*""".r
    • 好吧,我摆弄了一下,想出了这个正则表达式:[+-]?[0-9]*((\.[0-9]+([eE][+-]?[0-9]+)?[fF]?)|([fF])|([eE][+-]?[0-9]+))\b。它将匹配带有小数点和可选“e”参数的数字,或者它将匹配带有“f”或“F”标志的整数。例如。匹配:3.3, -4.12, .43, 3e3, 3.4e-4, 3.3e4f, 1e1F, 4f, -23F 不匹配:0, 1, -3, 443, 3e。按照你说的用这个替换浮点解析器后,它工作得很好。
    • 谢谢@Harrison 这对我来说非常有效。我可以建议您将其保留为答案,以便更多人可以看到它
    猜你喜欢
    • 1970-01-01
    • 2020-01-17
    • 1970-01-01
    • 1970-01-01
    • 2011-07-01
    • 1970-01-01
    • 2010-10-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多