2010-07-27 19 views
2

Je joue avec les combinateurs d'analyseurs Scala depuis un certain temps maintenant, et j'ai appris quelques-unes des façons de le faire bien et de faire le maximum voulez, en utilisant la fonction intégrée.Combinateurs d'analyseurs Scala pour langage incorporé en HTML ou texte (comme PHP)

Mais comment faites-vous un langage intégré (comme php ou ruby's erb)? Il faut que les espaces ne soient pas ignorés, en dehors de l'incorporation de code réel.

J'ai réussi à faire un simple analyseur qui correspond à tout le texte jusqu'à un match regex donné, mais je suis à la recherche d'une meilleure façon de le faire. Il y a probablement une fonction déjà définie qui fait le nécessaire.

La langue de test parse texte comme:

now: [[ millis; ]] 
and now: [[; millis; ]] 

et est généré par le code suivant:

package test 

import scala.util.parsing.combinator.RegexParsers 
import scala.util.matching.Regex 

sealed abstract class Statement 
case class Print(s: String) extends Statement 
case class Millis() extends Statement 

object SimpleLang extends RegexParsers { 

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

    def until(s: String): Parser[String] = until(java.util.regex.Pattern.quote(s).r) 

    def interpret(stats: List[Statement]): Unit = stats match { 
    case Print(s) :: rest => { 
     print(s) 
     interpret(rest) 
    } 
    case Millis() :: rest => { 
     print(System.currentTimeMillis) 
     interpret(rest) 
    } 
    case Nil =>() 
    } 

    def apply(input: String) : List[Statement] = parseAll(beginning, input) match { 
    case Success(tree,_) => tree 
    case e: NoSuccess => throw new RuntimeException("Syntax error: " + e) 
    } 

    /** GRAMMAR **/ 

    def beginning = (
    "[[" ~> stats | 
    until("[[") ~ "[[" ~ stats ^^ { 
     case s ~ _ ~ ss => Print(s) :: ss 
    } 
) 

    def stats = rep1sep(stat, ";") 

    def stat = (
    "millis" ^^^ { Millis() } | 
    "]]" ~> ((until("[[") <~ "[[") | until("\\z".r)) ^^ { 
     case s => Print(s) 
    } 
) 

    def main(args: Array[String]){ 
    val tree = SimpleLang("now: [[ millis; ]]\nand now: [[; millis; ]]") 
    println(tree) 
    interpret(tree) 
    } 

} 

Répondre

8

le trait de RegexParsers Scala offre une conversion implicite de Regex à Parser [Char] qui permet de sauter tout diriger les espaces avant de rechercher une correspondance regex. Vous pouvez utiliser

override val skipWhitespace = false 

pour désactiver ce comportement, ou remplacer le membre whiteSpace (il est une autre expression régulière) pour fournir votre propre chaîne personnalisée.

Ces options fonctionnent globalement, la désactivation de l'espacement des espaces signifie que TOUTES les productions regex verront les espaces.

Une autre option serait d'éviter d'utiliser la conversion regex pour quelques cas où vous avez besoin d'espaces. Je l'ai fait here dans un analyseur pour CSS qui ignore les commentaires dans la plupart des endroits, mais juste avant une règle, il doit les lire pour extraire des métadonnées de style javadoc.

+0

Je suis conscient de la possibilité de skipWhitespace, mais j'espérais une solution plus propre (comme une fonction lib je l'ai manqué en quelque sorte). Je vais vérifier cela. Peut-être que la solution la plus propre implique une préparation où le texte est converti en une fonction de texte imprimé de la langue. –

1

Avez-vous envisagé d'utiliser un lexer avant l'analyseur?

+0

Oui. Mais je devrais écrire un moi-même. La lexeur pour les combinateurs d'analyseurs de scala élimine les espaces, de sorte qu'il n'est pas adapté au code incorporé dans le texte. –

+0

@Otey AFAIK, vous pouvez contrôler si elle jette des espaces ou non. –