Есть ли способ передать контекстную информацию парсерам?

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

Объявление переменной будет выглядеть так:

?varname
?varname1 ?varname2 - type1
?varname3 ?varname4 ?varname5 - type2

Если тип не указан, тип по умолчанию должен быть object, как и в первом случае. Поэтому для этого у меня есть специальный синтаксический анализатор, который возвращает список моего собственного доменного объекта с именем LiftedTerm (вы можете просто предположить, что это кортеж с именем переменной и типом переменной, на самом деле в нем есть еще кое-что, но не имеет отношения к этой проблеме):

def typed_list_variables : Parser[List[LiftedTerm]]= typed_variables.+ ^^ { case list => list.flatten.map(variable =>
        LiftedTerm(variable._1, variable._2 match {
          case "object" => ObjectType
          case _ => TermType(variable._2)
        })) }

def typed_variables = ((variable+) ~ (("-" ~> primitive_type)?)) ^^ {
    case variables ~ primitive_type => 
         for (variable <- variables) yield variable -> primitive_type.getOrElse("object")
}

def variable = """\?[a-zA-Z][a-zA-Z0-9_-]*""".r
def primitive_type = """[a-zA-Z][a-zA-Z0-9_-]*""".r

Все это прекрасно работает.

Теперь дальше в той же «области действия» я должен разобрать части, где есть ссылка на эти переменные. Переменная, очевидно, не будет снова объявлена ​​полностью. Таким образом, в приведенном выше примере места, где используется ?varname1, не будут включать type1. Однако, когда я анализирую остальную часть ввода, я хочу получить ссылку на правильный объект LiftedTerm, а не просто строку.

У меня есть некоторые рекурсивные структуры, поэтому я не хочу делать это сопоставление на синтаксическом анализаторе верхнего уровня. Я не хочу делать их "глобальное сопоставление" в моем объекте RegexParsers, потому что большинство из них имеют область действия и относятся только к небольшому фрагменту ввода.

Есть ли способ передать контекстную информацию анализатору? В идеале я передаю список LiftedTerm (или еще лучше карту из имен переменных String -> LiftedTerm) в вызовы рекурсивного парсера.

(Извините, если это что-то очевидное, я все еще новичок в Scala и еще новичок в комбинаторах парсеров).


person jbx    schedule 02.01.2014    source источник
comment
Я считаю, что scala.util.parsing.ast.Binders позволил бы вам сделать что-то в этом роде, но он уже давно устарел и находится на грани исчезновения.   -  person Philippe    schedule 02.01.2014
comment
@Филипп Спасибо. Итак, каков стандартный способ достижения этого? Я предполагаю, что это обычное дело при разборе чего-то, что ссылается на что-то еще, что было определено ранее.   -  person jbx    schedule 02.01.2014
comment
Насчет стандарта не знаю, извините. Мое личное мнение заключается в том, что потребность в связывателях (или в системе специального типа) является явным признаком перехода к многопроходному процессору; анализировать, анализировать имена, анализировать типы. Это будет значительно проще в обслуживании. Это только мое мнение.   -  person Philippe    schedule 02.01.2014
comment
Хорошо, спасибо за ваш вклад. Все примеры, которые я нахожу с помощью Scala Parser - Combinators, практически одинаковы... простые экземпляры парсеров математических выражений или простые структуры. :(   -  person jbx    schedule 02.01.2014
comment
Мой вопрос здесь, вероятно, связан. К сожалению, ответы не очень удовлетворительны.   -  person Travis Brown    schedule 02.01.2014
comment
@TravisBrown Да, несколько похоже. Я думаю, что мне нужно поместить мои нетипизированные переменные во временную структуру без типов, а затем найти их соответствующее объявление типа при объединении их вместе в мой объект домена. Не впечатляюще чистый, но это единственное решение, о котором я могу думать.   -  person jbx    schedule 02.01.2014


Ответы (2)


AFAIK, библиотека синтаксического анализатора комбинатора scala ограничена грамматиками без контекста. Следовательно, ваш вариант использования не поддерживается.

Правильным путем было бы расширить scala.util.parsing.combinator.Parsers и предоставить собственный класс Parser, который переносит ваш контекст. Затем вам нужно определить все комбинаторы, чтобы также иметь дело с контекстом.

редактировать: как было указано ниже, парсеры имеют методы into и flatMap, поэтому, когда у вас есть парсер, который дает ваш контекст, вы можете комбинировать его с другим парсером, который требует контекста в монадическом стиле.

person choeger    schedule 16.02.2014
comment
С комбинаторами flatMap/in он явно не ограничивается контекстно-свободными грамматиками. - person Didier Dupont; 17.02.2014
comment
Где вы видите эти комбинаторы? Я не могу найти их здесь: www.scala-lang.org/api/2.10.2/index.html#scala.util.parsing.combinator.Parser - person choeger; 17.02.2014
comment
scala-lang.org/api/current / - person Didier Dupont; 17.02.2014

«Есть ли способ передать контекстную информацию синтаксическому анализатору?» да. Например, вот игрушечная реализация лямбда-исчисления, которая стирает имена переменных.

case class Lam(bod: LambdaExp) extends LambdaExp
case class Var(v: Int) extends LambdaExp {
  require(v >= 0)
}
case class App(f: LambdaExp, a: LambdaExp) extends LambdaExp

sealed trait LambdaExp

и может быть проанализирован с помощью

class LambdaParser extends RegexParsers {

  def varName: Parser[String] = """[a-z|A-Z]+""".r

  def variable(ctx: List[String]): Parser[Var] = varName ^? ({ case name if ctx.contains(name) => Var(ctx.indexOf(name)) }, { name => s"no var in scope with name $name" })

  def scope(v: String, ctx: List[String]): Parser[LambdaExp] = (("." ~> exp(v :: ctx))) ^^ { case bod => bod }

  def lam(ctx: List[String]): Parser[Lam] = (("λ" ~> varName) >> { v => scope(v, ctx) }) ^^ { scope => Lam(scope) }

  def app(ctx: List[String]): Parser[App] = (exp(ctx) ~ exp(ctx)) ^^ { case f ~ a => App(f, a) }

  def exp(ctx: List[String]): Parser[LambdaExp] = ("(" ~> exp(ctx) <~ ")") | ("(" ~> app(ctx) <~ ")") |
    variable(ctx) | lam(ctx)

}

Обратите внимание на контекстную информацию, проходящую через синтаксический анализ.

person user833970    schedule 16.03.2018