Фильтрация токенов из Scala Parser Combinators - PullRequest
2 голосов
/ 21 июля 2010

Как отфильтровать последовательность токенов, идущих от моего Lexer к моему парсеру при использовании комбинаторов парсера Scala?

Позвольте мне объяснить - предположим, у меня есть довольно стандартный шаблон Lexer (расширение StdLexical) и Parser (расширение StdTokenParsers). Лексер превращает последовательность символов в последовательность токенов, затем анализатор превращает последовательность токенов в абстрактное синтаксическое дерево (типа Expr).

Я решаю, что для некоторых токенов, которые могут возникнуть в любом месте потока, я бы хотел иметь возможность отфильтровывать их, поэтому мне нужна функция, которая бы помещалась между Lexer и Parser для удаления этих токенов. Например, я бы хотел, чтобы лексер токенизировал комментарии, а затем отфильтровал их позже.

Как лучше написать этот фильтр? Это может использовать идиому синтаксического анализатора, но не обязательно.

Пример текущего кода:

 val reader = new PagedSeqReader(PagedSeq.fromReader(reader))
 val tokens = new MyParser.lexical.Scanner(reader)
 val parse = MyParser.phrase(parser)(tokens)

Я бы хотел написать что-то вроде этого:

 val reader = new PagedSeqReader(PagedSeq.fromReader(reader))
 val tokens = new MyParser.lexical.Scanner(reader)
 val parse = MyParser.phrase(parser)(filter(tokens))

Ответы [ 2 ]

2 голосов
/ 24 июля 2010

Я сделал это сейчас, вот результаты. Основная идея заключается в том, что Parser из комбинатора синтаксического анализа использует scala.util.parsing.input.Reader в качестве входных данных. Таким образом, нам нужен класс, который обёртывает Reader, а сам по себе является Reader, который отфильтровывает записи по некоторым условиям.

Я пишу Reader, поэтому при создании он пропускает все нежелательные записи и останавливается либо на первой удачной записи, либо в конце. Затем каждый вызов делегируется первоначальному читателю, за исключением rest, который по очереди создает другой TokenFilter.

import scala.util.parsing.input._

class Filter[T](parent : Reader[T], exclude : T=>Boolean) extends Reader[T] {
  private val start = nextOk(parent)
  def nextOk(r : Reader[T]) : Reader[T] =
    if(r.atEnd) r else (if (exclude(r.first)) nextOk(r.rest) else r)

  override def source = start.source
  override def offset: Int = start.offset
  override def first: T = start.first
  override def rest: Reader[T] = new Filter(start.rest, exclude)
  override def pos: Position = start.pos
  override def atEnd = start.atEnd
}
1 голос
/ 21 июля 2010

Рассматривали ли вы использование RegexParsers для удаления пробелов и комментариев?

EDIT

Вы можете сделать простой фильтр

import scala.util.parsing.input._

object ReaderFilter {
  def filter[T](reader: Reader[T], check: T => Boolean): Reader[T] = {
    new Reader[T] {
      var orig = reader
      def first = { trim; orig.first }
      def atEnd = { trim; orig.atEnd }
      def rest: Reader[T] = { trim; ReaderFilter.filter(orig.rest, check) }
      def pos = orig.pos
      private def trim = {
        while (!orig.atEnd && !check(orig.first))
          orig = orig.rest
      }
    }
  }
}

и используйте его таким образом (чтобы удалить токены, которые # #):

val tokens = ReaderFilter.filter(new MyParser.lexical.Scanner(reader), 
          {t:ExprParser.lexical.Token => t.chars != "#"})
...