Схема парсинга с использованием комбинаторов парсера Scala - PullRequest
4 голосов
/ 06 декабря 2010

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

Вот мой парсер:

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}
} 

Ответы [ 2 ]

3 голосов
/ 06 декабря 2010

Как правильно указал @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))
1 голос
/ 06 декабря 2010

Единственная проблема с кодом - использование символов вместо строк.Ниже я удалил лишний ^^ { case s => 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
}

Все Parsers имеют неявное accept для их Elem типов.Итак, если базовый элемент - это Char, например, в RegexParsers, то для них существует неявное действие принятия, которое происходит здесь для символов (, ) и ", которыесимволов в вашем коде.

Что автоматически делает RegexParsers, так это автоматически пропускает пробелы (определенные как protected val whiteSpace = """\s+""".r, так что вы можете переопределить это) автоматически в начале любого String или Regex.Он также заботится о перемещении позиционирующего курсора за пробелом в случае сообщений об ошибках.

Одним из следствий этого, которое, как вы, похоже, не поняли, является то, что у префикса префикса " a string beginning with a space" будет удалено из проанализированноговывод, который вряд ли будет чем-то, что вы хотите.: -)

Кроме того, поскольку \s включает новые строки, новая строка будет приемлемой перед любым идентификатором, который может быть или не быть тем, что вы хотите.

Вы можете отключить пропуск пробеловв вашем регулярном выражении в целом, переопределив skipWhiteSpace.С другой стороны, по умолчанию skipWhiteSpace проверяет длину whiteSpace, поэтому вы можете потенциально включать и выключать ее, просто манипулируя значением whiteSpace в течение всего процесса синтаксического анализа.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...