2010-07-21 6 views
2

Comment filtrer la séquence de jetons provenant de mon Lexer vers mon Parser lorsque j'utilise des combinateurs d'analyseurs Scala? Permettez-moi d'expliquer - supposons que j'ai le modèle assez standard d'un Lexer (extension StdLexical) et un analyseur (extension StdTokenParsers). Le lexeur transforme une séquence de caractères en une séquence de jetons, puis l'analyseur transforme la séquence de jetons en un arbre de syntaxe abstraite (de type Expr).Filtrage des jetons depuis les Combinators Scala Parser

Je décide que certains jetons, qui pourraient se produire n'importe où dans le flux, je voudrais avoir la possibilité de filtrer, donc je voudrais une fonction qui conviendrait entre Lexer et Parser pour supprimer ces jetons. Par exemple, je pourrais vouloir que le lexer tokenise les commentaires, puis filtre ces commentaires plus tard.

Quelle est la meilleure façon d'écrire ce filtre? Cela pourrait utiliser l'idiome du combinateur d'analyseur, mais n'a pas à le faire.

code actuel Exemple:

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

Je voudrais pouvoir écrire quelque chose comme ceci:

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

Répondre

1

Avez-vous envisagé d'utiliser un RegexParsers pour supprimer les espaces et les commentaires?

EDIT

Vous pouvez faire un simple filtre

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 
     } 
    } 
    } 
} 

et de l'utiliser de cette manière (pour enlever les jetons qui sont "#"):

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

Je le fais déjà de manière appropriée. Je veux écrire le filtre comme décrit dans la question. L'application ne supprime pas les commentaires, c'est simplement la façon la plus simple d'expliquer le problème. –

+0

Je l'ai écrit moi-même, puis je suis revenu et j'ai réalisé que vous aviez édité! Vous pouvez avoir la réponse acceptée, intéressant comment le code est si semblable. J'ai utilisé la récursivité et vous avez utilisé une boucle while, mais à part ça, ils sont presque les mêmes :-) –

2

Je Je l'ai fait maintenant, voici les résultats. L'idée clé est qu'un analyseur du combinateur d'analyseurs utilise une entrée scala.util.parsing.input.Reader. Nous avons donc besoin d'une classe qui enveloppe un Reader, et elle-même est un Reader qui filtre les entrées sous certaines conditions.

J'écris le Reader donc lors de la construction il saute toutes les entrées non désirées et s'arrête à la première bonne entrée ou à la fin. Ensuite, chaque appel est délégué au lecteur d'origine à l'exception de rest qui construit un autre TokenFilter à son tour.

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 
} 
Questions connexes