2014-01-04 4 views
3

У меня есть рабочий синтаксический анализатор, но я только что понял, что не комментирую комментарии. В DSL я разбираюсь, комментарии начинаются с символа ;. Если встречается ;, то остатка строки игнорируется (но не все из этого, если только первый символ не равен ;).Как игнорировать комментарии одиночной строки в синтаксическом анализаторе

Я продолжаю RegexParsers для моего анализатора и игнорирую пробел (по умолчанию), поэтому я все равно теряю новые символы строки. Я не хочу изменять каждый парсер, который я должен учитывать для возможности комментариев, потому что операторы могут охватывать несколько строк (таким образом, каждая часть каждого утверждения может заканчиваться комментарием). Есть ли какой-либо чистый способ добиться этого?

ответ

4

Одна вещь, которая может повлиять на ваш выбор, заключается в том, можно ли найти комментарии в действительных синтаксических анализаторах. Например, скажем, у вас есть что-то вроде:

val p = "(" ~> "[a-z]*".r <~ ")" 

, который будет анализировать что-то вроде (abc), но из-за комментариев, которые могли бы на самом деле сталкиваются с что-то вроде:

(; comment goes here 
    abc 
) 

Тогда я бы рекомендовал использовать TokenParser или один из его подкласс. Это больше работает, потому что вы должны предоставить лексический парсер, который сделает первый проход, чтобы отменить комментарии. Но это также более гибким, если у вас есть вложенные комментарии или если ; можно избежать или, если ; может быть внутри строкового литерала, как:

abc = "; don't ignore this" ; ignore this 

С другой стороны, вы можете также попытаться переопределить значение из whitespace быть что-то вроде

override protected val whiteSpace = """(\s|;.*)+""".r 

Или что-то вдоль этих линий. Например, используя пример из scaladoc RegexParsers:

import scala.util.parsing.combinator.RegexParsers 

object so1 { 
    Calculator("""(1 + ; foo 
    (1 + 2)) 
    ; bar""") 
} 

object Calculator extends RegexParsers { 
    override protected val whiteSpace = """(\s|;.*)+""".r 
    def number: Parser[Double] = """\d+(\.\d*)?""".r ^^ { _.toDouble } 
    def factor: Parser[Double] = number | "(" ~> expr <~ ")" 
    def term: Parser[Double] = factor ~ rep("*" ~ factor | "/" ~ factor) ^^ { 
    case number ~ list => (number /: list) { 
     case (x, "*" ~ y) => x * y 
     case (x, "/" ~ y) => x/y 
    } 
    } 
    def expr: Parser[Double] = term ~ rep("+" ~ log(term)("Plus term") | "-" ~ log(term)("Minus term")) ^^ { 
    case number ~ list => list.foldLeft(number) { // same as before, using alternate name for /: 
     case (x, "+" ~ y) => x + y 
     case (x, "-" ~ y) => x - y 
    } 
    } 
    def apply(input: String): Double = parseAll(expr, input) match { 
    case Success(result, _) => result 
    case failure: NoSuccess => scala.sys.error(failure.msg) 
    } 
} 

Это печатает:

Plus term --> [2.9] parsed: 2.0 
Plus term --> [2.10] parsed: 3.0 
res0: Double = 4.0 
+0

Я немного играл с библиотекой комбинаторов синтаксиса в Scala, и я бы рекомендовал использовать «TokenParser» для чего угодно, кроме самых тривиальных парсеров. Во-первых, токенизация сначала делает парсер более быстрым и простым. Во-вторых, без второй фазы токенизации очень трудно получить парсер правильно. У меня были всевозможные проблемы с попытками отличить ключевые слова и идентификаторы. Например, я обнаружил, что мой синтаксический анализатор будет обрабатывать 'if x then y else z' и' ifxthenyelsez' одинаково, если я не добавлю кучу негативных результатов в мои регулярные выражения. – DaoWen

+0

Спасибо за ваши комментарии. Перекрытие белого пространства кажется интересным. Теперь, когда я закончил свой парсер с помощью 'RegexParsers', я больше не хочу менять все, чтобы использовать« ToeknParser », хотя я еще не знаю разницы. – jbx

+0

@ DaoWen Причина, по которой вы получаете 'ifxthenyelsez', вероятно, объясняется тем, как структурируются ваши регулярные выражения. У меня была аналогичная проблема, вы можете проверить это: http://stackoverflow.com/questions/20793058/how-to-skip-whitespace-but-use-it-as-a-token-delimeter-in-a -parser-combinator/20794142? noredirect = 1 # comment31174010_20794142 – jbx

0

Просто отфильтровывать все комментарии с регулярным выражением, прежде чем передать код в ваш синтаксический анализатор.

def removeComments(input: String): String = { 
    """(?ms)\".*?\"|;.*?$|.+?""".r.findAllIn(input).map(str => if(str.startsWith(";")) "" else str).mkString 
} 

val code = 
"""abc "def; ghij" 
abc ;this is a comment 
def""" 

println(removeComments(code)) 
+0

Да, но это не сработает, если я использую 'Читателя', а также будет мешать позиционированию номера строки, если я использую вывод' Positional', чтобы узнать, где не удалось выполнить синтаксический анализ. – jbx

+0

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

Смежные вопросы