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

时间:2010-07-27 20:14:36

标签: 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)
  }

}

2 个答案:

答案 0 :(得分:8)

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

override val skipWhitespace = false

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

这些选项全局工作,关闭空格跳过意味着所有正则表达式产生都会看到空格。

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

答案 1 :(得分:1)

您是否考虑过在解析器之前使用词法分析器?