Как передать 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)
}
}