Scala парсер комбинаторы для языка, встроенного в HTML или текст (например, PHP) - PullRequest
2 голосов
/ 28 июля 2010

Я уже некоторое время играю с комбинаторами парсера Scala и изучил некоторые способы заставить его вести себя хорошо и выполнять большинство задач, которые я хочу, используя встроенную функцию.

Но как сделать встроенный язык (например, php или rub's 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 ]

8 голосов
/ 28 июля 2010

Черта Scala RegexParsers обеспечивает неявное преобразование из Regex в Parser [Char], которое пропускает любой начальный пробел перед проверкой на соответствие регулярному выражению.Вы можете использовать

override val skipWhitespace = false

, чтобы отключить это поведение, или переопределить член whiteSpace (это другое регулярное выражение), чтобы предоставить собственную настраиваемую строку.

Эти параметры работают глобально, отключаяпропуск пропусков означает, что ВСЕ произведения регулярных выражений будут видеть пробелы.

Другой вариант - избегать использования преобразования регулярных выражений только в тех случаях, когда вам нужны пробелы.Я сделал это здесь в парсере для CSS, который игнорирует комментарии в большинстве мест, но перед правилом ему нужно прочитать их, чтобы извлечь некоторые метаданные в стиле javadoc.

1 голос
/ 07 августа 2010

Рассматривали ли вы использовать лексер перед парсером?

...