Gibt es eine bequeme Möglichkeit, die Parser-Kombinatoren von Scala zu verwenden, um Sprachen zu analysieren, bei denen der Einzug wichtig ist? (Zum Beispiel Python)Parsing einer indentation-basierten Sprache mit Hilfe von Scala-Parser-Kombinatoren
Antwort
Nehmen wir an, wir haben eine sehr einfache Sprache, wo diese ein gültiges Programm ist
block
inside
the
block
und wir wollen, dass diese mit jeder Zeile in eine List[String]
analysieren innerhalb des Blocks als ein String
.
Zuerst definieren wir eine Methode, die eine minimale Einrückungsebene annimmt und einen Parser für eine Zeile mit dieser Einrückungsebene zurückgibt.
def line(minIndent:Int):Parser[String] =
repN(minIndent + 1,"\\s".r) ~ ".*".r ^^ {case s ~ r => s.mkString + r}
Dann definieren wir einen Block mit einem minimalen Einrückungslevel, indem wir den Linienparser mit einem geeigneten Trennzeichen zwischen den Zeilen wiederholen.
def lines(minIndent:Int):Parser[List[String]] =
rep1sep(line(minIndent), "[\n\r]|(\n\r)".r)
Jetzt können wir einen Parser für unsere kleine Sprache wie folgt definieren:
val block:Parser[List[String]] =
(("\\s*".r <~ "block\\n".r) ^^ { _.size }) >> lines
Es bestimmt zunächst die aktuelle Ebene Vertiefung und gelangt dann, dass als Minimum auf den Linien-Parser. Lassen Sie uns testen:
val s =
"""block
inside
the
block
outside
the
block"""
println(block(new CharSequenceReader(s)))
Und wir bekommen
[4.10] parsed: List( inside, the, block)
Für all dies zu kompilieren, müssen Sie diese Einfuhren
import scala.util.parsing.combinator.RegexParsers
import scala.util.parsing.input.CharSequenceReader
Und Sie brauchen, um alles in ein Objekt zu setzen, die sich RegexParsers
so
object MyParsers extends RegexParsers {
override def skipWhitespace = false
....
Von dem, was ich weiß, nein, die Scala-Parser-Kombinierer haben keine Unterstützung für diese Art von Ding aus der Box. Sie können dies sicherlich tun, indem Sie Leerraum auf eine sinnvolle Weise analysieren, aber Sie werden einige Probleme haben, da Sie eine Form von Zustandsmaschine benötigen, um den Eindrucksstapel zu verfolgen.
Ich würde einen Vorverarbeitungsschritt empfehlen. Hier ist ein kleiner Präprozessor des Marker fügt gegliederte Blöcke zu trennen:
object Preprocessor {
val BlockStartToken = "{"
val BlockEndToken = "}"
val TabSize = 4 //how many spaces does a tab take
def preProcess(text: String): String = {
val lines = text.split('\n').toList.filterNot(_.forall(isWhiteChar))
val processedLines = BlockStartToken :: insertTokens(lines, List(0))
processedLines.mkString("\n")
}
def insertTokens(lines: List[String], stack: List[Int]): List[String] = lines match {
case List() => List.fill(stack.length) { BlockEndToken } //closing all opened blocks
case line :: rest => {
(computeIndentation(line), stack) match {
case (indentation, top :: stackRest) if indentation > top => {
BlockStartToken :: line :: insertTokens(rest, indentation :: stack)
}
case (indentation, top :: stackRest) if indentation == top =>
line :: insertTokens(rest, stack)
case (indentation, top :: stackRest) if indentation < top => {
BlockEndToken :: insertTokens(lines, stackRest)
}
case _ => throw new IllegalStateException("Invalid algorithm")
}
}
}
private def computeIndentation(line: String): Int = {
val whiteSpace = line takeWhile isWhiteChar
(whiteSpace map {
case ' ' => 1
case '\t' => TabSize
}).sum
}
private def isWhiteChar(ch: Char) = ch == ' ' || ch == '\t'
}
Ausführung für diesen Text gibt:
val text =
"""
|line1
|line2
| line3
| line4
| line5
| line6
| line7
| line8
| line9
|line10
| line11
| line12
| line13
""".stripMargin
println(Preprocessor.preProcess(text))
... folgendes Ergebnis
{
line1
line2
{
line3
line4
line5
{
line6
line7
}
}
{
line8
line9
}
line10
{
line11
line12
line13
}
}
Und anschliessend können Sie Verwenden Sie die Kombinator-Bibliothek, um das Parsen einfacher durchzuführen.
Hoffe, dies hilft
- 1. Hilfe beim Erstellen einer App mit Parsing
- 2. Parsing einer einfachen Sprache für Titelformatierung
- 3. libxml-ruby parsing HILFE
- 4. Stylistic Hilfe - jQuery regex Parsing
- 5. Anzeige Hindi Sprache in der Konsole mit Hilfe von Java
- 6. Swift: Hilfe bezüglich JSON Parsing
- 7. C# - Parsing-Tool für die DOT-Sprache
- 8. Parsing XML von einer API mit Simplexml_load_file
- 9. Benötigen Sie Hilfe bei Android Json Parsing
- 10. Sprache in einer Sprache zu einer anderen Sprache
- 11. Parsing einer Webseite mit Java
- 12. Regex/Javascript Hilfe - Suche URL term Parsing
- 13. Brauchen Sie Hilfe mit PHP-DOM XPath Parsing-Tabelle
- 14. Brauchen Sie Hilfe Parsing Ergebnisse von ldap zu CSV
- 15. Parsing einer Zeichenfolge mit Leerzeichen
- 16. Hilfe mit einer Ausnahme
- 17. Brauchen Sie Hilfe in Gesichtserkennung mit C# Sprache
- 18. Hilfe mit einer Rewrite-Regel
- 19. Parsing einer .htaccess Datei mit PHP
- 20. Parsing-Quelle einer Webseite mit Objective-C
- 21. SAS Mit Hilfe einer Textdatei
- 22. Hilfe mit einer Verkaufsstelle Design
- 23. Hilfe mit einer SQL-Abfrage
- 24. Hilfe mit einer MySQL-Abfrage
- 25. Parsing von Datetime mit Zeitzone
- 26. Parsing von Binärdaten mit Scala
- 27. Parsing einer Lisp-Datei mit Python
- 28. Parsing und Split von einer Textdatei
- 29. Was sind einige gute natürliche Sprache Parsing-Tools für Perl?
- 30. Arbeiten mit einer streng begrenzten interpretierten Sprache
Verwenden Sie 'override val skipWhitespace = false' – senia