Потоки дополнительного состояния через парсер в 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~>

Ответы на вопрос(1)

Ваш ответ на вопрос