Dois-je utiliser RegexParsers, StandardTokenParsers ou ces outils sont-ils appropriés pour analyser ce type de syntaxe? L'exemple de la syntaxe peut être trouvé à partir de here.Comment écrire un analyseur pour la syntaxe de diff unifiée
Répondre
Voici une solution utilisant RegexParsers
.
import scala.util.parsing.combinator.RegexParsers
object UnifiedDiffParser extends RegexParsers {
// case classes representing the data of the diff
case class UnifiedDiff(oldFile: File, newFile: File, changeChunks: List[ChangeChunk])
case class File(name: String, timeStamp: String)
case class ChangeChunk(rangeInformation: RangeInformation, changeLines: List[String])
case class RangeInformation(oldOffset: Int, oldLength: Int, newOffset: Int, newLength: Int)
override def skipWhitespace = false
def unifiedDiff: Parser[UnifiedDiff] = oldFile ~ newFile ~ rep1(changeChunk) ^^ {
case of ~ nf ~ l => UnifiedDiff(of, nf, l)
}
def oldFile: Parser[File] = ("--- " ~> filename) ~ ("""\s+""".r ~> timestamp <~ newline) ^^ {
case f~t => File(f, t)
}
def newFile: Parser[File] = ("+++ " ~> filename) ~ ("""\s+""".r ~> timestamp <~ newline) ^^ {
case f~t => File(f, t)
}
def filename: Parser[String] = """[\S]+""".r
def timestamp: Parser[String] = """.*""".r
def changeChunk: Parser[ChangeChunk] = rangeInformation ~ (newline ~> rep1(lineChange)) ^^ {
case ri ~ l => ChangeChunk(ri, l)
}
def rangeInformation: Parser[RangeInformation] = ("@@ " ~> "-" ~> number) ~ ("," ~> number) ~ (" +" ~> number) ~ ("," ~> number) <~ " @@" ^^ {
case a ~ b ~ c ~ d => RangeInformation(a, b, c, d)
}
def lineChange: Parser[String] = contextLine | addedLine | deletedLine
def contextLine: Parser[String] = """ .*""".r <~ newline
def addedLine: Parser[String] = """\+.*""".r <~ newline
def deletedLine: Parser[String] = """-.*""".r <~ newline
def newline: Parser[String] = """\n""".r
def number: Parser[Int] = """\d+""".r ^^ {_.toInt}
def main(args: Array[String]) {
val reader = {
if (args.length == 0) {
// read from stdin
Console.in
} else {
new java.io.FileReader(args(0))
}
}
println(parseAll(unifiedDiff, reader))
}
}
Ce format a été conçu pour être facile à analyser, vous pouvez le faire sans expressions régulières et sans que votre entrée soit fragmentée. Allez juste ligne par ligne et regardez les deux premiers caractères. L'en-tête du fichier et les en-têtes des blocs nécessiteront un peu plus d'attention, mais ce n'est pas ce que vous ne pouvez pas faire avec split.
Bien sûr, si vous voulez apprendre à utiliser certaines bibliothèques d'analyse, alors allez-y.
Il y a quelques années, au cours de ma première semaine de programmation, j'ai commencé à créer des correctifs pour Nethack. Ne sachant pas 'diff', j'ai commencé à écrire les foutues choses à la main. Vous pouvez imaginer mon embarras quand quelqu'un dans le groupe de nouvelles m'a poliment informé que je pourrais être hors de mon esprit. Eh bien, de toute façon non seulement les diffs unifiés sont faciles à analyser, mais ils ne sont pas si difficiles à écrire à la main non plus. :) – guns
Je ne voudrais pas garder explicitement l'état moi-même et apprendre à utiliser les combinateurs de l'analyseur est également l'un de mes objectifs. Sur la plupart des exemples, il y a une certaine syntaxe à utiliser pour le langage de programmation, mais je me demandais si les combinateurs de l'analyseur ne pouvaient pas être utilisés pour l'analyse de la syntaxe diff ou même des formats binaires. – JtR
J'utiliserais regex. Cela simplifie un certain nombre de choses et rend le reste standard.
def process(src: scala.io.Source) {
import scala.util.matching.Regex
val FilePattern = """(.*) ''(.*)''"""
val OriginalFile = new Regex("--- "+FilePattern, "path", "timestamp")
val NewFile = new Regex("+++ "+FilePattern, "path", "timestamp")
val Chunk = new Regex("""@@ -(\d+),(\d+) +(\d+),(\d+) @@""", "orgStarting", "orgSize", "newStarting", "newSize")
val AddedLine = """+(.*)""".r
val RemovedLine = """-(.*)""".r
val UnchangedLine = """ (.*)""".r
src.getLines() foreach {
case OriginalFile(path, timestamp) => println("Original file: "+path)
case NewFile(path, timestamp) => println("New file: "+path)
case Chunk(l1, s1, l2, s2) => println("Modifying %d lines at line %d, to %d lines at %d" format (s1, l1, s2, l2))
case AddedLine(line) => println("Adding line "+line)
case RemovedLine(line) => println("Removing line "+line)
case UnchangedLine(line) => println("Keeping line "+line)
}
}
J'espérais pouvoir utiliser des combinateurs d'analyseurs pour me débarrasser de mon état. Je construis un graphique d'objet à partir des détails du correctif quelque chose comme ça que j'ai un correctif qui contient FileModifications qui contiennent des morceaux. Il semble que les combinateurs d'analyseurs pourraient offrir un moyen plus simple de créer des objets à partir de choses analysées au lieu de construire le graphe d'objets sur certaines variables et de suivre l'état d'analyse. – JtR
Btw, je ne savais pas que les regexps peuvent être utilisés dans la correspondance de motifs comme ça, très soigné! – JtR
trébuché sur ce tout en cherchant à construire un analyseur Scala pour une diff git, tel que généré en exécutant git diff-tree
. Ceci est très similaire à diff unifié, mais il a quelques variantes intéressantes. J'ai beaucoup compté sur une réponse ci-dessus, et j'ai fini par écrire l'analyseur inclus ici.
Ce n'est pas strictement ce que l'affiche originale était après, bien sûr, mais j'ai pensé que cela pourrait être utile aux autres.
import util.parsing.combinator._
object GitDiff {
// file names have "a/" or "b/" as prefix, need to drop that to compare
def apply (files: (String,String), op: FileOperation, chunks: List[ChangeChunk]) = {
def strip(s: String) = s.dropWhile(_ != '/').drop(1)
new GitDiff(strip(files._1), strip(files._2), op, chunks)
}
}
case class GitDiff(oldFile: String, newFile: String, op: FileOperation, chunks: List[ChangeChunk]) {
val isRename = oldFile != newFile
}
sealed trait FileOperation
case class NewFile(mode: Int) extends FileOperation
case class DeletedFile(mode: Int) extends FileOperation
case object UpdatedFile extends FileOperation
sealed trait LineChange { def line: String }
case class ContextLine(line: String) extends LineChange
case class LineRemoved(line: String) extends LineChange
case class LineAdded(line: String) extends LineChange
case class RangeInformation(oldOffset: Int, oldLength: Int, newOffset: Int, newLength: Int)
case class ChangeChunk(rangeInformation: RangeInformation, changeLines: List[LineChange])
// Code taken from http://stackoverflow.com/questions/3560073/how-to-write-parser-for-unified-diff-syntax
object GitDiffParser extends RegexParsers {
override def skipWhitespace = false
def allDiffs: Parser[List[GitDiff]] = rep1(gitDiff)
def gitDiff: Parser[GitDiff] = filesChanged ~ fileOperation ~ diffChunks ^^ {
case files ~ op ~ chunks => GitDiff(files, op, chunks)
}
def filesChanged: Parser[(String, String)] =
"diff --git " ~> filename ~ (" " ~> filename) <~ newline ^^ { case f1 ~ f2 => (f1,f2) }
def fileOperation: Parser[FileOperation] =
opt(deletedFileMode | newFileMode) <~ index ^^ { _ getOrElse UpdatedFile }
def index: Parser[Any] = ("index " ~ hash ~ ".." ~ hash) ~> opt(" " ~> mode) <~ newline
def deletedFileMode: Parser[DeletedFile] = "deleted file mode " ~> mode <~ newline ^^ { m => DeletedFile(m) }
def newFileMode: Parser[NewFile] = "new file mode " ~> mode <~ newline ^^ { m => NewFile(m) }
def hash: Parser[String] = """[0-9a-f]{7}""".r
def mode: Parser[Int] = """\d{6}""".r ^^ { _.toInt }
def diffChunks: Parser[List[ChangeChunk]] = (oldFile ~ newFile) ~> rep1(changeChunk)
def oldFile: Parser[String] = "--- " ~> filename <~ newline
def newFile: Parser[String] = "+++ " ~> filename <~ newline
def filename: Parser[String] = """[\S]+""".r
def changeChunk: Parser[ChangeChunk] = rangeInformation ~ opt(contextLine) ~ (opt(newline) ~> rep1(lineChange)) ^^ {
case ri ~ opCtx ~ lines => ChangeChunk(ri, opCtx map (_ :: lines) getOrElse (lines))
}
def rangeInformation: Parser[RangeInformation] =
("@@ " ~> "-" ~> number) ~ opt("," ~> number) ~ (" +" ~> number) ~ opt("," ~> number) <~ " @@" ^^ {
case a ~ b ~ c ~ d => RangeInformation(a, b getOrElse 0, c, d getOrElse 0)
}
def lineChange: Parser[LineChange] = contextLine | addedLine | deletedLine
def contextLine: Parser[ContextLine] = " " ~> """.*""".r <~ newline ^^ { l => ContextLine(l) }
def addedLine: Parser[LineAdded] = "+" ~> """.*""".r <~ newline ^^ { l => LineAdded(l) }
def deletedLine: Parser[LineRemoved] = "-" ~> """.*""".r <~ newline ^^ { l => LineRemoved(l) }
def newline: Parser[String] = """\n""".r
def number: Parser[Int] = """\d+""".r ^^ { _.toInt }
def parse(str: String) = parseAll(allDiffs, str)
def main(args: Array[String]) {
val reader = {
if (args.length == 0) {
// read from stdin
Console.in
} else {
new java.io.FileReader(args(0))
}
}
parseAll(allDiffs, reader) match {
case Success(s,_) => println(s)
case NoSuccess(msg,_) => sys.error("ERROR: " + msg)
}
}
}
- 1. Analyseur pour la syntaxe Mathematica?
- 2. Analyseur de syntaxe Printf pour PHP/Codeigniter?
- 3. Bibliothèque de diff unifiée basée sur les lignes C++
- 4. comment écrire une nouvelle ligne unifiée en PHP?
- 5. Comment écrire son propre analyseur pour (f) lex?
- 6. Un analyseur lexical pour SPDH
- 7. comment créer un analyseur pour TEX?
- 8. Comment tester un analyseur CSS?
- 9. Comment puis-je mieux écrire des cas de test unitaires pour un analyseur?
- 10. Bibliothèque d'authentification unifiée
- 11. Un analyseur pour la trame SPDH
- 12. Écrire un analyseur (pour un langage de balisage): théorie et pratique
- 13. Comment écrire un analyseur ANTLR pour les langages JSP/ASP/PHP?
- 14. Comment gérer l'incapacité de casser les erreurs de syntaxe dans un analyseur de descente récursif
- 15. syntaxe sql comment écrire en java
- 16. analyseur Configurer Xerces SAX pour une erreur de tolérer la syntaxe XML
- 17. Comment écrire un test unitaire pour "InterruptedException"
- 18. Comment écrire cette requête MySql en utilisant la syntaxe Rails3?
- 19. Éditeur de texte natif pour mac qui peut mettre en surbrillance la syntaxe des fichiers diff?
- 20. C# Lua Analyseur/Analyseur
- 21. Unix diff pour imprimer uniquement diff pertinentes
- 22. Comment effectuer un diff de roulement?
- 23. Comment développer un analyseur de requêtes
- 24. Ruby, comment devrais-je concevoir un analyseur?
- 25. Comment utiliser git diff seulement diff fichiers.
- 26. comment faire ressembler svn diff comme diff?
- 27. Comment faire diff ressembler à svn diff?
- 28. composant ou code pour un analyseur d'onde
- 29. Comment écrire l'expression arithmétique pour évaluer la différenciation dans haskell
- 30. Comment faire pour convertir la syntaxe de la requête à la syntaxe de la méthode
Merci, juste ce que je cherchais! – JtR