Потоки дополнительного состояния через парсер в Scala
я пытаюсь использовать государственный монадный трансформатор вСкалаз 7 пропустить дополнительное состояние через парсер, и яу меня возникли проблемы с чем-то полезныммного изt m a -> t m b
версииm a -> m b
методы.
Предположим, у меня есть строка, содержащая вложенные скобки с цифрами внутри:
val input = "((617)((0)(32)))"
У меня также есть поток свежих имен переменных (в данном случае символов):
val names = Stream('a' to 'z': _*)
Я хочу вытащить имя из верхней части потока и назначить его каждому выражению в скобках при его разборе, а затем сопоставить это имя со строкой, представляющей содержимое скобок, с заменой вложенных выражений в скобках (если они есть) на их имена.
Чтобы сделать это более конкретным, здесьчто яЯ хочу, чтобы вывод был похож на пример ввода выше:
val target = Map(
'a' -> "617",
'b' -> "0",
'c' -> "32",
'd' -> "bc",
'e' -> "ad"
)
На данном уровне может быть либо строка цифр, либо произвольно много подвыражений, но эти два вида контента выиграли »не может быть смешано в одном выражении в скобках.
Для простоты мыПредположим, что поток имен никогда не будет содержать ни дубликатов, ни цифр, и что он всегда будет содержать достаточно имен для нашего ввода.
Использование комбинаторов парсера с немного изменяемым состояниемПриведенный выше пример является несколько упрощенной версией проблемы синтаксического анализа вэтот вопрос переполнения стека, яответил на этот вопрос с решением, которое выглядело примерно так:
import scala.util.parsing.combinator._
class ParenParser(names: Iterator[Char]) extends RegexParsers {
def paren: Parser[List[(Char, String)]] = "(" ~> contents (names.next -> s) :: m
}
def contents: Parser[(String, List[(Char, String)])] =
"\\d+".r ^^ (_ -> Nil) | rep1(paren) ^^ (
ps => ps.map(_.head._1).mkString -> ps.flatten
)
def parse(s: String) = parseAll(paren, s).map(_.toMap)
}
Это'не так уж плохо, но яЯ предпочитаю избегать изменчивого состояния.
Что я хочуХаскеляпарсек библиотека упрощает добавление пользовательского состояния в анализатор:
import Control.Applicative ((*>), (), ( put(names.tail).map(_ => (names.head -> s) :: m)
)
}
def contents: ESP[(String, List[(Char, String)])] =
lift("\\d+".r ^^ (_ -> Nil)) | rep1(paren).map(
ps => ps.map(_.head._1).mkString -> ps.flatten
)
def parse(s: String, names: Stream[Char]) =
parseAll(paren.eval(names), s).map(_.toMap)
}
Это работает, и это 'не намного менее краткий, чем версия изменяемого состояния или версия Parsec.
Но мойExtraStateParsers
безобразен как грехЯ нене хочу испытывать ваше терпение больше, чем у меня уже есть, поэтому я выигралне могу включить это здесь (хотяВот'ссылка, если вы действительно этого хотите). Я'мы должны были написать новые версии каждогоParser
а такжеParsers
метод, который я использую выше для моегоExtraStateParsers
а такжеESP
типы (,,rep1
~>