156 lines
6.2 KiB
Markdown
156 lines
6.2 KiB
Markdown
# Übungsblatt 4
|
|
|
|
## Vorwort
|
|
|
|
Wir haben uns in der vergangenen Vorlesung Parser näher angeschaut. In den praktischen Übungen soll es weniger um das "erfinden" eines neuen Parser-Kombinators gehen, als um die Anwendung.
|
|
|
|
## Set-Up
|
|
|
|
Da wir nun zum ersten mal mit externen dependencies arbeiten, müssen wir diese zunächst installieren. Dies ist ein sehr wichtiger Schritt, da nahezu alle weiteren Übungszettel dieses Vorgehen voraussetzen.
|
|
|
|
Als erstes sollten Sie die Quellen von `stack` mit einem `stack update` aktualisieren. Anschließend erstellen sie ein neues Stack-Projekt mittels `stack new <Projektname>`. Sie erhalten ein Verzeichnis mit dem Projektnamen, in dem ein kleines "Hello World" liegt. Als erstes sollte man immer über die `projektname.cabal` drüber schauen. Hier werden nachher auch alle dependencies eingetragen.
|
|
|
|
Bei mir sieht das z.B. so aus (Projekt heisst "parser"):
|
|
|
|
```
|
|
name: parser
|
|
version: 0.1.0.0
|
|
synopsis: Initial project template from stack
|
|
description: Please see README.md
|
|
homepage: https://github.com/githubuser/parser#readme
|
|
license: BSD3
|
|
license-file: LICENSE
|
|
author: Author name here
|
|
maintainer: example@example.com
|
|
copyright: 2016 Author name here
|
|
category: Web
|
|
build-type: Simple
|
|
-- extra-source-files:
|
|
cabal-version: >=1.10
|
|
|
|
library
|
|
hs-source-dirs: src
|
|
exposed-modules: Lib
|
|
build-depends: base >= 4.7 && < 5
|
|
default-language: Haskell2010
|
|
|
|
executable parser-exe
|
|
hs-source-dirs: app
|
|
main-is: Main.hs
|
|
ghc-options: -threaded -rtsopts -with-rtsopts=-N
|
|
build-depends: base
|
|
, parser
|
|
default-language: Haskell2010
|
|
|
|
test-suite parser-test
|
|
type: exitcode-stdio-1.0
|
|
hs-source-dirs: test
|
|
main-is: Spec.hs
|
|
build-depends: base
|
|
, parser
|
|
ghc-options: -threaded -rtsopts -with-rtsopts=-N
|
|
default-language: Haskell2010
|
|
|
|
source-repository head
|
|
type: git
|
|
location: https://github.com/githubuser/parser
|
|
```
|
|
|
|
Generell sollte man hier ordentliche Angaben machen, da viele tools dieses automatisch lesen und überall eintragen. Insbesondere sollte man nicht seine private Email-Adresse nehmen, sondern z.b. die Techfak-Adresse, die Studiumsbezogen ist.
|
|
|
|
Wir haben hier 3 Bereiche: `library`, `parsers-exe` und `test-suite`. Library ist das, was wir meistens schreiben - eine Sammlung von Funktionen, die das eigentlich tun. Dann haben wir die executable; diese enthält die `main` und ruft meist in wenig Code unsere library auf, nachdem sie z.b. Parameter/Dateien/... gelesen hat und kümmert sich um die Ausgabe. Somit können wir für spätere Zwecke (Projekte) die Library 1:1 wiederverwenden.
|
|
Die test-suite ignorieren wir für den Moment. Wir kommen in einer separaten Vorlesung noch einmal auf Tests zu sprechen.
|
|
|
|
Eine editierte Variante könnte etwa so aussehen:
|
|
|
|
```
|
|
name: parser
|
|
version: 0.1.0.0
|
|
synopsis: A little parser for generic CSV-Files
|
|
description: Please see README.md
|
|
homepage: https://github.com/Drezil/FFPiHaskell_parser#readme
|
|
license: BSD3
|
|
license-file: LICENSE
|
|
author: Stefan Dresselhaus
|
|
maintainer: sdressel@techfak.uni-bielefeld.de
|
|
copyright: 2016 Stefan Dresselhaus
|
|
category: Tool
|
|
build-type: Simple
|
|
-- extra-source-files:
|
|
cabal-version: >=1.10
|
|
|
|
library
|
|
hs-source-dirs: src
|
|
exposed-modules: Lib
|
|
build-depends: base >= 4.7 && < 5
|
|
, attoparsec
|
|
default-language: Haskell2010
|
|
|
|
executable parser-exe
|
|
hs-source-dirs: app
|
|
main-is: Main.hs
|
|
ghc-options: -threaded -rtsopts -with-rtsopts=-N
|
|
build-depends: base
|
|
, parser
|
|
default-language: Haskell2010
|
|
|
|
test-suite parser-test
|
|
type: exitcode-stdio-1.0
|
|
hs-source-dirs: test
|
|
main-is: Spec.hs
|
|
build-depends: base
|
|
, parser
|
|
ghc-options: -threaded -rtsopts -with-rtsopts=-N
|
|
default-language: Haskell2010
|
|
|
|
source-repository head
|
|
type: git
|
|
location: https://github.com/Drezil/FFPiHaskell_parser
|
|
```
|
|
|
|
Änderungen die gemacht wurden:
|
|
|
|
- Daten ausgefüllt
|
|
- attoparsec als dependency der library hinzugefügt
|
|
- github-links angepasst (sofern man github verwendet)
|
|
|
|
Nachdem man das ganze nun gespeichert hat, reicht ein `stack build` um alle dependencies herunterzuladen, kompilieren und installieren. Anschließend kann man mit `stack exec parser-exe` das Programm ausführen.
|
|
|
|
## Ein simpler CSV-Parser
|
|
|
|
Sie sollten aus ihrem Studium bereits die EBNF kennen. Eine (simple) CSV-Datei besitzt folgende EBNF:
|
|
|
|
```
|
|
csv-file = { row }
|
|
row = field-list, eol
|
|
field-list = field, [ ",", field-list ]
|
|
field = [ whitespace ], field-value, [ whitespace ]
|
|
field-value = quoted-string | bare-string
|
|
quoted-string = '"', quoted-content, '"'
|
|
quoted-content = { quoted-char }
|
|
quoted-char = (any char except '"' or eol)
|
|
bare-string = { bare-char }
|
|
bare-char = (any char except ',' or eol without whitespace at beginning/end)
|
|
whitespace = space-char, { space-char }
|
|
space-char = " " | "\t"
|
|
eol = "\n"
|
|
```
|
|
|
|
Kurzes recap: `{ .. }` bedeutet 1 oder mehr, `[ .. ]` sind optional, `A | B` heißt, entweder A oder B, `A, B, C` bedeutet zunächst A, dann B, dann C.
|
|
|
|
### Datenstrukturen
|
|
|
|
Überlegen sie sich zunächst, wie eine Datenstruktur aussehen könnte und definieren sie diese. Inhalt sind vorerst nur Strings. Sie brauchen keine Zahlen/Daten/... zu erkennen.
|
|
|
|
### Parser
|
|
|
|
Schreiben sie einen Parser, der einen CSV-String in diese Datenstruktur parsed und geben sie diese aus (`deriving Show` auf der Datenstruktur reicht). Ein paar Testbeispiele für CSV-Dateien finden sie auf github/im Lernraum.
|
|
|
|
### Bonus
|
|
|
|
Natürlich ist das nur ein simpler CSV-Parser. Folgende Features wären für einen echten Einsatz noch Wünschenswert:
|
|
|
|
- sicherstellen, dass alle "rows" gleich lang sind und ggf. mit Fehlermeldung abbrechen
|
|
- einen "Header" mit einlesen, der die einzelnen Spalten beschreibt
|
|
- Quotation nicht nur als "blabla'bla", sondern auch als 'blabla"bla' zulassen, "bla \" bla" auch entsprechend parsen.
|