【问题标题】:Parsing Scheme using Scala parser combinators使用 Scala 解析器组合器的解析方案
【发布时间】:2010-12-06 06:34:22
【问题描述】:

我正在用 Scala 编写一个小型方案解释器,但在方案中解析列表时遇到了问题。我的代码解析包含多个数字、标识符和布尔值的列表,但如果我尝试解析包含多个字符串或列表的列表,它就会窒息。我错过了什么?

这是我的解析器:

class SchemeParsers extends RegexParsers {

// Scheme boolean #t and #f translate to Scala's true and false
def bool : Parser[Boolean] = 
    ("#t" | "#f") ^^ {case "#t" => true; case "#f" => false}

// A Scheme identifier allows alphanumeric chars, some symbols, and 
// can't start with a digit
def id : Parser[String] = 
    """[a-zA-Z=*+/<>!\?][a-zA-Z0-9=*+/<>!\?]*""".r ^^ {case s => s}

// This interpreter only accepts numbers as integers
def num : Parser[Int] = """-?\d+""".r ^^ {case s => s toInt}

// A string can have any character except ", and is wrapped in "
def str : Parser[String] = '"' ~> """[^""]*""".r <~ '"' ^^ {case s => s}

// A Scheme list is a series of expressions wrapped in ()
def list : Parser[List[Any]] = 
    '(' ~> rep(expr) <~ ')' ^^ {s: List[Any] => s}

// A Scheme expression contains any of the other constructions
def expr : Parser[Any] = id | str | num | bool | list ^^ {case s => s}
} 

【问题讨论】:

  • 你如何处理空白?
  • 为什么需要^^ {case s =&gt; s}
  • @MJP +1 , ^^ {case s =&gt; s} 可以删除
  • @Gabe One 会假定RegexParsers 对空白的默认处理是有效的。
  • 失败的测试用例会很有用。

标签: parsing scala


【解决方案1】:

正如@Gabe 正确指出的那样,您留下了一些未处理的空白:

scala> object SchemeParsers extends RegexParsers {
     |
     | private def space  = regex("[ \\n]*".r)
     |
     | // Scheme boolean #t and #f translate to Scala's true and false
     | private def bool : Parser[Boolean] =
     |     ("#t" | "#f") ^^ {case "#t" => true; case "#f" => false}
     |
     | // A Scheme identifier allows alphanumeric chars, some symbols, and
     | // can't start with a digit
     | private def id : Parser[String] =
     |     """[a-zA-Z=*+/<>!\?][a-zA-Z0-9=*+/<>!\?]*""".r
     |
     | // This interpreter only accepts numbers as integers
     | private def num : Parser[Int] = """-?\d+""".r ^^ {case s => s toInt}
     |
     | // A string can have any character except ", and is wrapped in "
     | private def str : Parser[String] = '"' ~> """[^""]*""".r <~ '"' <~ space ^^ {case s => s}
     |
     | // A Scheme list is a series of expressions wrapped in ()
     | private def list : Parser[List[Any]] =
     |     '(' ~> space  ~> rep(expr) <~ ')' <~ space ^^ {s: List[Any] => s}
     |
     | // A Scheme expression contains any of the other constructions
     | private def expr : Parser[Any] = id | str | num | bool | list ^^ {case s => s}
     |
     | def parseExpr(str: String) = parse(expr, str)
     | }
defined module SchemeParsers   

scala> SchemeParsers.parseExpr("""(("a" "b") ("a" "b"))""")
res12: SchemeParsers.ParseResult[Any] = [1.22] parsed: List(List(a, b), List(a, b))

scala> SchemeParsers.parseExpr("""("a" "b" "c")""")
res13: SchemeParsers.ParseResult[Any] = [1.14] parsed: List(a, b, c)

scala> SchemeParsers.parseExpr("""((1) (1 2) (1 2 3))""")
res14: SchemeParsers.ParseResult[Any] = [1.20] parsed: List(List(1), List(1, 2), List(1, 2, 3))

【讨论】:

  • 太棒了!谢谢!我以为 Regex 解析器会为我处理空格,但我发现我错了。
  • RegexParsers 在任何文字字符串或正则表达式的开头自动跳过空格。我想知道问题是否与正在使用的Char有关...
  • @stomcavage RegexParsers 确实为您处理空间,只是不适用于 Char
【解决方案2】:

代码的唯一问题是您使用字符而不是字符串。下面,我删除了多余的^^ { case s =&gt; s } 并将所有字符替换为字符串。我将在下面进一步讨论这个问题。

class SchemeParsers extends RegexParsers {

// Scheme boolean #t and #f translate to Scala's true and false
def bool : Parser[Boolean] = 
    ("#t" | "#f") ^^ {case "#t" => true; case "#f" => false}

// A Scheme identifier allows alphanumeric chars, some symbols, and 
// can't start with a digit
def id : Parser[String] = 
    """[a-zA-Z=*+/<>!\?][a-zA-Z0-9=*+/<>!\?]*""".r ^^ {case s => s}

// This interpreter only accepts numbers as integers
def num : Parser[Int] = """-?\d+""".r ^^ {case s => s toInt}

// A string can have any character except ", and is wrapped in "
def str : Parser[String] = "\"" ~> """[^""]*""".r <~ "\""

// A Scheme list is a series of expressions wrapped in ()
def list : Parser[List[Any]] = 
    "(" ~> rep(expr) <~ ")" ^^ {s: List[Any] => s}

// A Scheme expression contains any of the other constructions
def expr : Parser[Any] = id | str | num | bool | list
}

所有ParsersElem 类型都有一个隐含的accept。所以,如果基本元素是Char,例如RegexParsers,那么它们有一个隐含的接受动作,这就是符号()"的情况,它们是代码中的字符。

RegexParsers 自动执行的是在任何StringRegex 的开头自动跳过空格(定义为protected val whiteSpace = """\s+""".r,因此您可以覆盖它)。它还负责在出现错误消息时将定位光标移过空白区域。

您似乎没有意识到的一个后果是" a string beginning with a space" 将从解析的输出中删除其前缀空间,这不太可能是您想要的。 :-)

此外,由于\s 包含新行,因此可以在任何标识符之前使用新行,这可能是您想要的,也可能不是。

您可以通过覆盖skipWhiteSpace 来禁用整个正则表达式中的空格跳过。另一方面,默认的skipWhiteSpace 测试whiteSpace 的长度,因此您可以通过在整个解析过程中操作whiteSpace 的值来打开和关闭它。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-07-01
    • 2020-01-17
    • 1970-01-01
    • 1970-01-01
    • 2023-03-12
    • 2010-10-06
    • 1970-01-01
    相关资源
    最近更新 更多