Использование регулярного выражения в StandardTokenParsers

Я пытаюсь использовать регулярное выражение в моем синтаксическом анализаторе на основе StandardTokenParsers. Для этого я подклассифицировал StdLexical следующим образом:

class CustomLexical extends StdLexical{
  def regex(r: Regex): Parser[String] = new Parser[String] {
    def apply(in:Input) = r.findPrefixMatchOf(in.source.subSequence(in.offset, in.source.length)) match {
      case Some(matched) => Success(in.source.subSequence(in.offset, in.offset + matched.end).toString,
        in.drop(matched.end))
      case None => Failure("string matching regex `" + r + "' expected but " + in.first + " found", in)
    }
  }

  override def token: Parser[Token] =
    (   regex("[a-zA-Z]:\\\\[\\w\\\\?]* | /[\\w/]*".r)     ^^ { StringLit(_) }
      | identChar ~ rep( identChar | digit )               ^^ { case first ~ rest => processIdent(first :: rest mkString "") }
      | ...

Но я немного смущен тем, как я определяю Parser, который использует это. У меня есть парсер, определяемый как:

def mTargetFolder: Parser[String] = "TargetFolder" ~> "=" ~> mFilePath

который должен использоваться для идентификации допустимых путей к файлам. Я попробовал тогда:

def mFilePath: Parser[String] = "[a-zA-Z]:\\\\[\\w\\\\?]* | /[\\w/]*".r

Но это, очевидно, не так. Я получаю сообщение об ошибке:

scala: type mismatch;
 found   : scala.util.matching.Regex
 required: McfpDSL.this.Parser[String]
    def mFilePath: Parser[String] = "[a-zA-Z]:\\\\[\\w\\\\?]* | /[\\w/]*".r
                                                                          ^

Каков правильный способ использования расширения, сделанного в моем подклассе StdLexical?

2
nl ja de

2 ответы

Если вы действительно хотите использовать синтаксический анализ на основе токенов и повторно использовать StdLexical, я бы посоветовал обновить синтаксис для «TargetFolder», чтобы значение после знака равенства было правильным строковым литералом. Или, другими словами, сделайте так, чтобы путь был заключен в кавычки. С этого момента вам больше не нужно расширять StdLexical.

Затем возникает проблема преобразования регулярного выражения в парсер. Scala уже имеет RegexParsers для этого (что неявно преобразует регулярное выражение в Parser [String] ), но, к сожалению, здесь вы не хотите, потому что он работает с потоками Char ( type Elem = Char в RegexParsers )), пока вы работаете над строкой токенов. Поэтому нам действительно нужно определить наше собственное преобразование из Regex в Parser [String] (но на синтаксическом уровне, а не на лексическом уровне, или, другими словами, в парсере маркера).

import scala.util.parsing.combinator.syntactical._
import scala.util.matching.Regex
import scala.util.parsing.input._

object MyParser extends StandardTokenParsers {
  import lexical.StringLit
  def regexStringLit(r: Regex): Parser[String] = acceptMatch( 
    "string literal matching regex " + r, 
    { case StringLit( s ) if r.unapplySeq(s).isDefined => s }
  )
  lexical.delimiters += "="
  lexical.reserved += "TargetFolder"
  lazy val mTargetFolder: Parser[String] = "TargetFolder" ~> "=" ~> mFilePath
  lazy val mFilePath: Parser[String] = regexStringLit("([a-zA-Z]:\\\\[\\w\\\\?]*)|(/[\\w/]*)".r)  
  def parseTargetFolder( s: String ) = { mTargetFolder( new lexical.Scanner( s ) ) }
}

Пример:

scala> MyParser.parseTargetFolder("""TargetFolder = "c:\Dir1\Dir2" """)
res12: MyParser.ParseResult[String] = [1.31] parsed: c:\Dir1\Dir2

scala> MyParser.parseTargetFolder("""TargetFolder = "/Dir1/Dir2" """)
res13: MyParser.ParseResult[String] = [1.29] parsed: /Dir1/Dir2

scala> MyParser.parseTargetFolder("""TargetFolder = "Hello world" """)
res14: MyParser.ParseResult[String] =
[1.16] failure: identifier matching regex ([a-zA-Z]:\\[\w\\?]*)|(/[\w/]*) expected
TargetFolder = "Hello world"
           ^

Обратите внимание, что здесь также исправлено ваше регулярное выражение «целевая папка», у вас отсутствовали парсеры вокруг двух альтернатив, а также ненужные пробелы.

5
добавлено
Отличная дискуссия, у меня есть одна и та же проблема, но у меня не может быть как StandardTokenParses, так и RegexParser рядом друг с другом. пожалуйста, помогите мне в этом: stackoverflow.com/questions/30836294/…
добавлено автор Rubbic, источник
@ RégisJean-Gilles благодарю вас, я пробовал, но, имея некоторые ошибки, если я задам это как вопрос, пожалуйста, помогите мне в этом?
добавлено автор Rubbic, источник
@ RégisJean-Gilles Я обновил свой предыдущий вопрос с ошибкой. Спасибо stackoverflow.com/q/30836294/3827280
добавлено автор Rubbic, источник
Как я понял, я позволил немного изменить синтаксис: путь к dir должен быть правильным строковым литералом (с кавычками). Без этого изменения все намного сложнее. Вы можете сделать маркер маршрута Идентификатором (поэтому нет необходимости в кавычках), но тогда вам нужно будет разрешить «/», «:» и «\» в идентификаторах (это то, что вы, по-видимому, пытались сделать), а затем это влияет на каждый идентификатор (тем самым нарушая синтаксический анализ). Основная проблема заключается в том, что лексер и синтаксический анализатор являются отдельными, лексер не знает, когда он анализирует поток символов, если вам нужен путь или идентификатор (он не имеет контекста).
добавлено автор Régis Jean-Gilles, источник
Еще одно решение, позволяющее избежать этих проблем, - это просто не использовать синтаксический анализ на основе токенов (там, где есть две отдельные части: лексер и синтаксический синтаксический анализатор), и вместо этого делать все на одном уровне. В примере define object MyParser расширяет StdLexical с помощью RegexParsers и делает все. Поскольку нет разделения, вы знаете контекст везде, а в качестве бонуса RegexParsers автоматически превращает каждое регулярное выражение в подходящие синтаксические анализаторы.
добавлено автор Régis Jean-Gilles, источник
Переход на RegexParsers, безусловно, к лучшему. Что касается того, почему для расширения из StdLexical , ваша интуиция хороша: вы не должны. Я просто вставил (не слишком много думая) декларацию из парсера, который я написал. Оказывается, в моей библиотеке я был довольно отчаянным, чтобы уменьшить размер (байт-код). Расширение от StdLexical не предоставило мне никакой дополнительной функции, но позволило уменьшить размер байт-кода. Это связано с тем, что смешивание признака (например, RegexParsers ) приводит к очень значительным ударам по размеру кода, а расширение класса (например, Stdlexical )). Печально, но верно.
добавлено автор Régis Jean-Gilles, источник
Что касается хороших ресурсов, я не думаю, что могу порекомендовать что-нибудь еще, чем то, что откроет Google. Я сам всегда немного проигрываю каждый раз, когда я возвращаюсь к парсеру комбинаторной, поскольку документации недостаточно, и большинство вводных текстов довольно неуловимы в конкретных деталях.
добавлено автор Régis Jean-Gilles, источник
Цитируя себя: «Scala уже имеет RegexParsers для этого (что неявно преобразует регулярное выражение в Parser [String]), но, к сожалению, это не то, что вы хотите здесь, потому что оно работает с потоками Char (тип Elem = Char в RegexParsers), в то время как вы работая над sttream токенов, поэтому нам действительно нужно определить наше собственное преобразование из Regex в Parser [String] (но на синтаксическом уровне, а не на лексическом уровне или, другими словами, в парсере). regexStringLit в моем вышеприведенном фрагменте - это метод, который вы хотите создать парсер (на синтаксическом уровне) из Regex.
добавлено автор Régis Jean-Gilles, источник
Обратите внимание, что это будет соответствовать регулярному выражению как правильные строковые литералы (другими словами, «цитируется»).
добавлено автор Régis Jean-Gilles, источник
Обновите исходный вопрос с полным и минимальным фрагментом того, что вы пытаетесь достичь, вместе с ошибкой, которую вы получите, и я посмотрю, что я могу сделать.
добавлено автор Régis Jean-Gilles, источник
Спасибо за ваш ответ. Я предположил, что StdLexical сначала запускает и обрабатывает «Z: \ ...», поэтому он обрабатывает «Z» как идентификатор -> «идентификатор Z», и я получаю эту ошибку: «string буквально соответствующее регулярное выражение ([a-zA-Z]: \ [\ w \\?] *) | (/ [\ w /] *), ожидаемое в позиции 2,26. Идентификатор токена Z найден. ". Это исчезло после того, как я заменил лексику своей собственной реализацией, которая обрабатывает регулярное выражение до того, как будет достигнут идентификатор. Не совсем уверен, почему вы не получите эту ошибку.
добавлено автор Dan, источник
Да. Я столкнулся с первым - я сделал путь идентификатором, а теперь другие сломаны. Если я решила использовать RegexParsers, зачем мне требовать расширения от StdLexical? Не могли бы вы рассказать о том, какая логика потребуется там?
добавлено автор Dan, источник
Я сделал изменения и переключился на RegexParsers, не увеличивая StdLexical. Парсер работает, и это позволило мне устранить большую часть дополнительного кода, который у меня был. Я все еще чувствую, что не совсем понимаю, как взаимодействуют различные комбинации комбинаторов Scala (парсеры, лексики, токены и т. Д.). Какой хороший ресурс там, где это хорошо объяснено?
добавлено автор Dan, источник

Просто вызовите функцию regex , когда вы хотите получить Parser [String] из Regex :

def p: Parser[String] = regex("".r)

Или сделайте regex неявным, чтобы компилятор вызывал это автоматически для вас:

implicit def regex(r: Regex): Parser[String] = ...
// =>
def p: Parser[String] = "".r
0
добавлено
p определяется в терминах regex и regex определяется в терминах p . Это нормально?
добавлено автор Valentin Tihomirov, источник
regex не определяется в терминах p , почему вы так думаете?
добавлено автор kiritsuku, источник
pro.jvm
pro.jvm
3 503 участник(ов)

Сообщество разработчиков Java Scala Kotlin Groovy Clojure Чат для нач-их: @javastart Наш сайт: projvm.com projvm.ru Наш канал: @proJVM Вакансии: @jvmjobs Конфы: @jvmconf

Scala User Group
Scala User Group
1 486 участник(ов)

[RU] Scala Chat. Rules, additional links, FAQ: https://telegra.ph/Russian-Speaking-Scala-User-Group-08-27

Scala Jobs
Scala Jobs
852 участник(ов)

Rules: http://telegra.ph/My-lyudi-i-ehto-znachit-chto-nam-nuzhna-organizaciya-02-07 Main Scala Channel: https://t.me/scala_jobs_feed Flood Room: https://t.me/scala_ponv