Как передать ParseResults другим методам?

void whatever() {
  // ...
  val parser = new MyParser
  val parse = parser.parse(input)
  if (parse successful) {
    semanticAnalysis(parse)
  }
}

void semanticAnalysis(parse: DontKnowTheCorrectType) {
  // ...
}

Какой тип я должен дать формальному параметру parse? Зависший над parse внутри whatever говорится val parse: parser.ParseResult[parsing.Program], но, конечно, это не работает как тип параметра semanticAnalysisпотому что локальная переменная parse там не по объему.

4 ответа

Решение

Результаты разбора являются зависимыми от пути типами, потому что они являются результатами этого конкретного синтаксического анализатора, и нет никакой гарантии, что они совместимы.

Вот почему парсеры обычно не создаются так, как вы их используете (new MyParser), но, как object,

object MyParser extends RegexParsers { 
   def statements : Parser[List[Statement]] = // ...
} 

def handleResult(res: MyParser.ParseResult[List[Statement]]) = { // ... } 

val res = MyParser.parseAll(MyParser.statements, "/* */")

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

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

  def fun(p: scala.util.parsing.combinator.Parsers, res: p.ParseResult[_]) = {}

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

Вы должны быть в состоянии определить semanticAnalysis как:

def semanticAnalysis(parse: MyParser#ParseResult[parsing.Program]) = {
  ...
}

Обратите внимание на использование # вместо . для типа. Здесь больше подробностей о типовых проекциях, но в основном, parser.ParseResult отличается для каждого parser, в то время как MyParser#ParseResult одинаков для всех parser экземпляров.

Но, как говорит themel, вы, вероятно, должны использовать object вместо class,

Вы можете переписать это так:

def whatever() {
  // ...
  val parser = new MyParser
  def semanticAnalysis(parse: parser.ParseResult) {
    // ...
  }
  val parse = parser.parse(input)
  if (parse successful) {
    semanticAnalysis(parse)
  }
}

Если у вас это вызывается из нескольких мест, то, возможно, это:

  class SemanticAnalysis(parser: Parser) {
    def apply(parse: parser.ParseResult) {
      // ...
    }
  }

А потом

  if (parse successful) {
    new SemanticAnalysis(parser)(parse)
  }

Когда мне нужно использовать результат синтаксических анализаторов в других частях программы, я извлекаю результат успеха или сообщение об ошибке из зависимого от пути ParseResult и поместить данные в независимый тип. Это более многословно, чем мне нравится, но я хочу, чтобы экземпляр комбинатора был деталью реализации парсера.

sealed abstract class Result[+T]
case class Success[T](result: T) extends Result[T]
case class Failure(msg: String) extends Result[Nothing]
case class Error(msg: String) extends Result[Nothing]

/** Parse the package declarations in the file. */
def parse(file: String): Result[List[Package]] = {
  val stream = ... // open the file...
  val parser = new MyParser
  val result = parser.parseAll(parser.packages, stream)   
  stream.close()
  result match {
    case parser.Success(packages, _) => Success(packages)
    case parser.Failure(msg, _) => Failure(msg)
    case parser.Error(msg, _) => Error(msg)
  }
}
Другие вопросы по тегам