Je devais analyser les espaces réservés en texte comme abc $$FOO$$ cba
. J'ai piraté ensemble quelque chose avec les combinateurs d'analyseurs de Scala, mais je ne suis pas vraiment content de la solution.Comment analyser les espaces réservés à partir du texte sans jeter votre épée afin de pouvoir combattre les maraudeurs avec un abat-jour
En particulier, j'ai utilisé un matcher de largeur nulle dans l'expression régulière (?=(\$\$|\z))
pour arrêter l'analyse du texte et commencer à analyser les espaces réservés. Cela semble dangereusement proche des manigances discutées et colorées rejeté sur le scala mailing list (qui a inspiré le titre de cette question.)
Donc, le défi: réparer mon analyseur pour travailler sans ce hack. J'aimerais voir une progression claire du problème à votre solution, afin de pouvoir remplacer ma stratégie d'assemblage aléatoire de combinateurs jusqu'à la réussite des tests.
import scala.util.parsing.combinator.RegexParsers
object PlaceholderParser extends RegexParsers {
sealed abstract class Element
case class Text(text: String) extends Element
case class Placeholder(key: String) extends Element
override def skipWhitespace = false
def parseElements(text: String): List[Element] = parseAll(elements, text) match {
case Success(es, _) => es
case NoSuccess(msg, _) => error("Could not parse: [%s]. Error: %s".format(text, msg))
}
def parseElementsOpt(text: String): ParseResult[List[Element]] = parseAll(elements, text)
lazy val elements: Parser[List[Element]] = rep(element)
lazy val element: Parser[Element] = placeholder ||| text
lazy val text: Parser[Text] = """(?ims).+?(?=(\$\$|\z))""".r ^^ Text.apply
lazy val placeholder: Parser[Placeholder] = delimiter ~> """[\w. ]+""".r <~ delimiter ^^ Placeholder.apply
lazy val delimiter: Parser[String] = literal("$$")
}
import org.junit.{Assert, Test}
class PlaceholderParserTest {
@Test
def parse1 = check("a quick brown $$FOX$$ jumped over the lazy $$DOG$$")(Text("a quick brown "), Placeholder("FOX"), Text(" jumped over the lazy "), Placeholder("DOG"))
@Test
def parse2 = check("a quick brown $$FOX$$!")(Text("a quick brown "), Placeholder("FOX"), Text("!"))
@Test
def parse3 = check("a quick brown $$FOX$$!\n!")(Text("a quick brown "), Placeholder("FOX"), Text("!\n!"))
@Test
def parse4 = check("a quick brown $$F.O X$$")(Text("a quick brown "), Placeholder("F.O X"))
def check(text: String)(expected: Element*) = Assert.assertEquals(expected.toList, parseElements(text))
}
Il peut être plus simple d'exécuter d'abord une lexer basée sur une expression rationnelle, en divisant l'entrée en jetons qui sont "$$" ou "une chaîne ne contenant pas $$". D'ailleurs, puisque vos délimiteurs ne correspondent pas à des paires imbriquées, n'est-ce pas simplement une langue régulière? Ce que vous faites ressemble plus à "couvrir une lampe" qu'à "combattre des maraudeurs". –
Sonne comme un pas dans la bonne direction. Comment devrais-je choisir comment diviser le travail entre le lexer et le scanner? – retronym
erm, lexer et parser. – retronym