用于嵌入 html 或文本(如 php)的语言的 Scala 解析器组合器

标签 scala embedded-language parser-combinators

我使用 Scala 解析器组合器已经有一段时间了,并且学习了一些方法来让它表现得很好,并使用内置函数做我想做的大部分事情。

但是如何制作嵌入式语言(如 php 或 ruby​​ 的 erb)? 它要求在嵌入真实代码之外的空白不能被忽略。

我设法制作了一个简单的解析器,可以将所有文本匹配到给定的正则表达式匹配项,但我正在寻找一种更好、更漂亮的方法来执行此操作。可能有一些已经定义的函数可以完成所需的工作。

测试语言解析文本如下:

now: [[ millis; ]]
and now: [[; millis; ]]

由以下代码生成:

package test

import scala.util.parsing.combinator.RegexParsers
import scala.util.matching.Regex

sealed abstract class Statement
case class Print(s: String) extends Statement
case class Millis() extends Statement

object SimpleLang extends RegexParsers {

  def until(r: Regex): Parser[String] = new Parser[String]{
    def apply(in: Input) = {
      val source = in.source
      val offset = in.offset
      val start = offset
      (r.findFirstMatchIn( source.subSequence(offset, source.length) )) match {
        case Some(matched) => 
          Success(source.subSequence(offset, offset + matched.start).toString, in.drop(matched.start))
        case None => 
          Failure("string matching regex `"+ r +"' expected but `"+ in.first +"' found", in.drop(0))
      }
    }
  }

  def until(s: String): Parser[String] = until(java.util.regex.Pattern.quote(s).r)

  def interpret(stats: List[Statement]): Unit = stats match {
    case Print(s) :: rest => {
      print(s)
      interpret(rest)
    }
    case Millis() :: rest => {
      print(System.currentTimeMillis)
      interpret(rest)
    }
    case Nil => ()
  }

  def apply(input: String) : List[Statement] = parseAll(beginning, input) match {
    case Success(tree,_) => tree
    case e: NoSuccess => throw new RuntimeException("Syntax error: " + e)
  }

  /** GRAMMAR **/

  def beginning = (
    "[[" ~> stats |
    until("[[") ~ "[[" ~ stats ^^ { 
      case s ~ _ ~ ss => Print(s) :: ss
    }
  )

  def stats = rep1sep(stat, ";")

  def stat = (
    "millis" ^^^ { Millis() } |
    "]]" ~> ( (until("[[") <~ "[[") | until("\\z".r)) ^^ {
      case s => Print(s)
    }
  )

  def main(args: Array[String]){
    val tree = SimpleLang("now: [[ millis; ]]\nand now: [[; millis; ]]")
    println(tree)
    interpret(tree)
  }

}

最佳答案

Scala 的 RegexParsers 特性提供了从 Regex 到 Parser[Char] 的隐式转换,它在检查正则表达式匹配之前跳过任何前导空格。你可以使用

override val skipWhitespace = false

关闭此行为,或覆盖 whiteSpace 成员(它是另一个正则表达式)以提供您自己的自定义字符串。

这些选项在全局范围内有效,关闭空白跳过意味着所有正则表达式产品都会看到空白。

另一种选择是避免在需要空格的少数情况下使用正则表达式转换。我这样做了here在 CSS 的解析器中,它忽略了大多数地方的注释,但就在规则之前,它需要读取它们以提取一些 javadoc 样式的元数据。

关于用于嵌入 html 或文本(如 php)的语言的 Scala 解析器组合器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3347552/

相关文章:

parsing - F# 中是否有任何已知的解析器组合库可以解析二进制(非文本)文件?

java - 是否可以使用支持传输的 netty 和 arterr 来运行 akka 系统?

scala - 避免 Scala 中的全局状态

java - 如何在 java 或 scala 中获取本地主机网络接口(interface)

java - 如何从 java 的类路径外部动态加载 Clojure 脚本?

php - 使用 PHP 创建新的嵌入式语言

xml - 无法选择正确的组合器进行解析并在 Scala 中处理它

scala - 何时使用 scala 三重插入符 (^^^) 与双重插入符 (^^) 和 into 方法 (>>)

python - 是否有 Python 的 "safe"子集用作嵌入式脚本语言?