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)
}
}
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. –