Wichtungskorrekturen.
[wortliste.git] / skripte / lua / helper_records.lua
blob9b7cdce4509b23faa6eb4f55ae961fd4487398dd
1 -- -*- coding: utf-8 -*-
3 --[[
4 Copyright 2012, 2013, 2014, 2015 Stephan Hennig
6 This program is free software: you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published by the
8 Free Software Foundation, either version 3 of the License, or (at your
9 option) any later version.
11 This program is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License along
17 with this program. If not, see <http://www.gnu.org/licenses/>.
18 --]]
22 --- Dieses Modul stellt die folgende Funktionalität zur Manipulation der
23 --- Wortliste bereit:
25 -- <ul>
26 -- <li>Prüfen einer Datei auf Wohlgeformtheit,</li>
27 -- <li>Prüfen von Datensätzen auf Wohlgeformtheit,</li>
28 -- <li>Zerlegen von Datensätzen,</li>
29 -- </ul>
31 -- @class module
32 -- @name helper_records
33 -- @author Stephan Hennig
34 -- @copyright 2012, 2013, 2014, 2015 Stephan Hennig
36 -- Die API-Dokumentation kann mit <pre>
38 -- luadoc -d API *.lua
40 -- </pre> erstellt werden.
44 --[[ just for luadoc 3.0 compatibility
45 module "helper_records"
46 --]]
47 -- lokale Modul-Tabelle
48 local M = {}
52 -- Lade Module.
53 local lpeg = require("lpeg")
54 local unicode = require("unicode")
55 local hwords = require("helper_words")
59 -- Kürzel für LPEG-Funktionen.
60 local P = lpeg.P
61 local R = lpeg.R
62 local C = lpeg.C
63 local Cc = lpeg.Cc
64 local Ct = lpeg.Ct
65 -- Muster für ein beliebiges Zeichen.
66 local any = P(1)
67 -- Kürzel für Unicode-Funktionen.
68 local Ufind = unicode.utf8.find
69 local Ugmatch = unicode.utf8.gmatch
70 local Ugsub = unicode.utf8.gsub
71 local Ulower = unicode.utf8.lower
72 -- Kürzel für Modul helper_words.
73 local validate_word = hwords.validate_word
78 -- Beschreibung des Formats der Wortliste:
80 -- Jeder Datensatz (Zeile) entspricht einer bestimmten Schreibvariante
81 -- eines Wortes. Unterschiedliche Schreibvarianten desselben Wortes
82 -- sind nicht miteinander verknüpft.
84 -- Feldtrenner ist das Semikolon.
85 local sep = P";"
87 -- Kommentare werden durch '#' eingeleitet und erstrecken sich bis zum
88 -- Zeilenende. Vor dem Kommentarzeichen sind beliebige Leerzeichen
89 -- erlaubt.
90 local com = P"#"
91 local spc = P" "
92 -- Muster für ein Kommentar.
93 local opcomment = spc^0 * (com * any^0)^-1 * -1
94 -- Muster für ein Kommentar mit Capture. Die Capture enthält den
95 -- Kommentartext ohne das Kommentarzeichen.
96 local opcommentC = spc^0 * (com * C(any^0))^-1 * -1
98 -- Leere Felder bestehen aus der Feldnummer eingeschlossen in
99 -- Minuszeichen, z. B. steht -2- für ein leeres Feld 2.
101 -- Muster für eine Ziffer.
102 local digit = R"09"
103 -- Muster für ein beliebiges leeres Feld.
104 local leerX = P"-" * digit * P"-"
105 -- Muster für leere Felder mit festem Inhalt.
106 local leer2 = P"-2-"
107 local leer3 = P"-3-"
108 local leer4 = P"-4-"
109 local leer5 = P"-5-"
110 local leer6 = P"-6-"
111 local leer7 = P"-7-"
112 local leer8 = P"-8-"
113 -- Kürzel für leere Felder mit voranstehendem Feldtrenner.
114 local _leer2 = sep * leer2
115 local _leer3 = sep * leer3
116 local _leer4 = sep * leer4
117 local _leer5 = sep * leer5
118 local _leer6 = sep * leer6
119 local _leer7 = sep * leer7
120 local _leer8 = sep * leer8
122 -- Belegte Felder bestehen aus beliebigen Zeichen außer Feldtrennern,
123 -- Leerzeichen oder Kommentarzeichen. Eine präzisere Beschreibung von
124 -- zulässigen Wörtern in Form einer Grammatik ist weiter unten zu
125 -- finden.
127 -- Muster für ein Feld beliebigen Inhalts.
128 local feld = (any - (sep + spc + com))^1
129 -- Muster für ein Feld beliebigen Inhalts mit Capture. Die Capture
130 -- enthält den Feldinhalt.
131 local feldC = C(feld)
132 -- Kürzel für ein Feld beliebigen Inhalts mit voranstehendem
133 -- Feldtrenner.
134 local _feldC = sep * feldC
135 -- Muster für ein belegtes Feld.
136 local bfeld = feld - leerX
137 -- Kürzel für ein belegtes Feld mit voranstehendem Feldtrenner.
138 local _bfeld = sep * bfeld
140 -- Feld 1 enthält ein Wort in ungetrennter Schreibung.
142 -- Die Felder 2, 3, 4 enthalten Wörter, die nicht ausschließlich in
143 -- Versalschreibung existieren. Die Felder 3, 4 treten immer paarweise
144 -- auf. Enthielten sie denselben Inhalt, so wird stattdessen Feld 2
145 -- verwendet. Die Felder 3 und 4 existieren dann nicht.
147 -- Die Felder 5, 6, 7, 8 enthalten Wörter, die nur in expliziter
148 -- Versalschreibung existieren ('ß' durch 'ss' ersetzt). Die Felder
149 -- 6, 7 treten immer zu zweit auf; Feld 8 wird nur angegeben, wenn die
150 -- Schreibweise nach (deutsch)schweizerischer, traditioneller
151 -- Rechtschreibung sich von der traditionellen Versalschreibung nach
152 -- Duden unterscheidet. Enthielten sie alle denselben Inhalt, so wird
153 -- stattdessen Feld 5 verwendet. Die Felder 6, 7, 8 existieren dann
154 -- nicht.
156 -- Feld Beschreibung
158 -- 1 * ungetrennt,
160 -- 2 * keine explizite Versalschreibung,
161 -- * falls in allen Rechtschreibungen gleich,
163 -- 3 * traditionelle Rechtschreibung,
165 -- 4 * reformierte Rechtschreibung,
167 -- 5 * explizite Versalschreibung,
168 -- * falls in allen Rechtschreibungen gleich,
170 -- 6 * traditionelle Rechtschreibung in Deutschland und/oder
171 -- Österreich,
173 -- 7 * reformierte Rechtschreibung,
175 -- 8 * traditionelle Rechtschreibung in der Schweiz,
180 -- Tabelle mit Capture-Ersetzungen. Bilde leere Felder auf den Wert
181 -- 'false' ab.
182 local _replace_empty_fields = {} for i = 1,8 do _replace_empty_fields["-"..i.."-"] = false end
183 -- Muster für ein Feld mit Ersetzung. Die Capture enthält den Feldinhalt
184 -- oder 'false' für leere Felder.
185 local feldRC = leerX / _replace_empty_fields + feldC
186 local _feldRC = sep * feldRC
187 -- Muster für eine Zeile mit bis zu acht Feldern (mit Table-Capture) und
188 -- einem optionalen Kommentar.
189 local split_record = Ct(C(bfeld) * _feldRC^-7) * opcommentC
191 --- Zerlege einen Datensatz in einzelne Felder und speichere diese in
192 --- einer Tabelle.
194 -- @param record Datensatz
196 -- @return Tabelle, die einen Datensatz repräsentiert. Nummerische
197 -- Indizes korrespondieren mit Feldnummern. Leere Felder entsprechen
198 -- dem Wert <code>false</code>. Der Schlüssel 'comment' enthält
199 -- gegebenenfalls einen Kommentar.
200 local function split(record)
201 local t, comment = split_record:match(record)
202 if comment then t.comment = comment end
203 return t
205 M.split = split
210 -- Prüfmuster für Datensatzstruktur
212 -- Im folgenden werden unterschiedliche Typen von zulässigen Datensätzen
213 -- durch Zeichenketten repräsentiert. Zeichenpositionen korrespondieren
214 -- dabei mit Feldernummern. Das Zeichen an einer Position beschreibt,
215 -- ob das jeweilige Feld belegt ist oder leer.
217 -- Leere Felder werden durch '_' oder 'x' repräsentiert, wobei 'x' nur
218 -- aus Gründen der Lesbarkeit für die Felder 2 und 5 verwendet wird.
220 -- Belegte Felder werden durch einen beschreibenden Buchstaben
221 -- gekennzeichnet. Folgende Buchstaben werden verwendet:
223 -- Feld Zeichen Beschreibung
225 -- 1 u ungetrennt
226 -- 2 a alle
227 -- 3 t traditionelle Rechtschreibung
228 -- 4 r reformierte Rechtschreibung
229 -- 5 c Versalschreibung, alle
230 -- 6 t Versalschr., trad. Rechtschr. (D, AT)
231 -- 7 r Versalschr., reform. Rechtschr.
232 -- 8 s Versalschr., trad. Rechtschr. (CH)
234 -- Beispiele:
236 -- * Ein Wort, welches in allen Rechtschreibungen gleich getrennt
237 -- wird: Die Felder 1 und 2 sind belegt, gekennzeichnet durch die
238 -- Zeichen 'u' und 'a'. Die Felder 3 bis 8 existieren nicht. Typ:
239 -- 'ua'.
241 -- * Ein Wort, welches nicht nur in Versalschreibung existiert (also
242 -- ein Wort in normaler Schreibung), welches jedoch in traditioneller
243 -- und reformierter Rechtschreibung unterschiedlich getrennt wird: Die
244 -- Felder 1, 3 und 4 sind belegt ('u', 't', 'r'). Feld 2 ist leer
245 -- ('x'). Die Felder 5 bis 8 existieren nicht. Typ: 'uxtr'.
247 -- * Ein Wort, welches nur in Versalschreibung existiert und dort für
248 -- alle Rechtschreibungen gleich getrennt wird: Die Felder 1 und 5 sind
249 -- belegt ('u' und 'c'). Die Felder 2 bis 4 sind leer ('x' und '_').
250 -- Die Felder 6 bis 8 existieren nicht. Typ: 'ux__c'.
252 -- * Ein Wort, welches nur in (deutsch-)schweizerischer
253 -- Versalschreibung existiert: Die Felder 1 und 8 sind belegt ('u' und
254 -- 's'). Die Felder 2 bis 7 sind leer ('x' und '_'). Typ: 'ux__x__s'.
257 -- Diese Tabelle bildet Positionen (Indizes von 1-8) auf Tabellen
258 -- zulässiger Zeichen ab. Die Untertabellen enthalten die an der
259 -- jeweiligen Position zulässigen Zeichen und bilden diese auf
260 -- Teilmuster ab.
261 local valid_flags = {
262 [1] = { u = bfeld },-- ungetrennt
263 [2] = { a = _bfeld, x = _leer2 },-- alle
264 [3] = { t = _bfeld, _ = _leer3 },-- trad. RS
265 [4] = { r = _bfeld, _ = _leer4 },-- reform. RS
266 [5] = { c = _bfeld, x = _leer5 },-- Versalschr., alle
267 [6] = { t = _bfeld, _ = _leer6 },-- Versalschr., trad. RS (D, AT)
268 [7] = { r = _bfeld, _ = _leer7 },-- Versalschr., reform. RS
269 [8] = { s = _bfeld, _ = _leer8 },-- Versalschr., trad. RS (CH)
272 -- Diese Variable enthält ODER-verknüpft (+) sämtliche Muster, die
273 -- zulässige Datensätze repräsentieren.
274 local valid_records
276 --- <strong>nicht-öffentlich</strong> Füge der Liste aller Muster
277 --- zulässiger Datensätze ein neues Muster hinzu. Die Captures der
278 --- Muster enthalten jeweils eine Zeichenkette, die den Datensatztyp
279 --- repräsentiert.
281 -- @param rec_type Zeichenkette, die ein Muster für einen zulässigen
282 -- Datensatz repräsentiert
283 local function _add_to_valid_records(rec_type)
284 local ch = string.sub(rec_type, 1, 1)
285 local pat = valid_flags[1][ch]
286 for i = 2,#rec_type do
287 local ch = string.sub(rec_type, i, i)
288 pat = pat * valid_flags[i][ch]
290 pat = Cc(rec_type) * pat * opcomment
291 if valid_records then
292 valid_records = valid_records + pat
293 else
294 valid_records = pat
298 -- Erstelle ein Muster, welches sämtliche zulässigen Datensätze
299 -- repräsentiert.
301 -- Muster für Wörter, die nicht nur in Versalschreibung existieren.
302 -- (Die Felder 5 bis 8 existieren nicht.)
304 _add_to_valid_records("ua")
305 -- einfach;ein·fach
307 _add_to_valid_records("uxt_")
308 -- Abfallager;-2-;Ab·fa{ll/ll·l}a·ger;-4-
309 -- Abfluß;-2-;Ab-fluß;-4-
311 _add_to_valid_records("ux_r")
312 -- Abfalllager;-2-;-3-;Ab-fall=la-ger
314 _add_to_valid_records("uxtr")
315 -- abgelöste;-2-;ab-ge-lö-ste;ab-ge-lös-te
318 -- Muster für Wörter, die nur in Versalschreibung existieren ('ß' durch
319 -- 'ss' ersetzt).
321 _add_to_valid_records("ux__c")
322 -- Abstoss;-2-;-3-;-4-;Ab·stoss
324 _add_to_valid_records("ux__xt_")
325 -- Litfasssäulenstilleben;-2-;-3-;-4-;-5-;Lit-fass-säu-len-sti{ll/ll-l}e-ben;-7-
327 _add_to_valid_records("ux__x_r")
328 -- Fussballliga;-2-;-3-;-4-;-5-;-6-;Fuss·ball·li·ga
330 _add_to_valid_records("ux__x__s")
331 -- Litfassäule;-2-;-3-;-4-;-5-;-6-;-7-;Lit·fa{ss/ss·s}äu·le
333 _add_to_valid_records("ux__xtr")
334 -- Fussabdrücke;-2-;-3-;-4-;-5-;Fuss=ab<drü{ck/k-k}e;Fuss=ab<drü-cke
336 _add_to_valid_records("ux__xtr_")
337 -- süsssauer;-2-;-3-;-4-;-5-;süss·sau·er;süss·sau·er;-8-
339 _add_to_valid_records("ux__xt_s")
340 -- Stemmeissel;-2-;-3-;-4-;-5-;Ste{mm/mm=m}ei-ssel;-7-;Ste{mm/mm=m}eis-sel
342 _add_to_valid_records("ux__xtrs")
343 -- Füsse;-2-;-3-;-4-;-5-;Fü·sse;Füs·se;Füs·se
346 -- Muster für Wörter, die in der reformierten Rechtschreibung
347 -- existieren, in der traditionellen Rechtschreibung jedoch nur in
348 -- Versalschreibweise ('ß' durch 'ss' ersetzt).
350 _add_to_valid_records("ux_rc")
351 -- Abfluss;-2-;-3-;Ab-fluss;Ab·fluss
353 _add_to_valid_records("ux_rxtr")
354 -- hässlichste;-2-;-3-;häss>lichs-te;-5-;häss>lich-ste;häss>lichs-te
356 _add_to_valid_records("ux_rxtr_")
357 -- Abflusssystem;-2-;-3-;Ab<fluss=sys-tem;-5-;Ab<fluss=sy-stem;Ab<fluss=sys-tem;-8-
359 _add_to_valid_records("uxtrxtrs")
360 -- Bussystem;-2-;Bus=sy-.stem;Bus=sys-tem;-5-;Bus=sy-.stem;Bus=sys-tem;Bu[s=s/{ss/ss=s}]y-.stem
363 --- Ermittle den Typ eines Datensatzes.
365 -- @param record Datensatz
367 -- @return Zeichenkette, die den Datensatztyp repräsentiert.
368 local function identify_record(record)
369 return valid_records:match(record)
371 M.identify_record = identify_record
375 -- Diese Liste enthält Datensätze mit bekannten "Fehlern". Die
376 -- Datensätze in dieser Liste werden nicht auf Wohlgeformtheit der
377 -- einzelnen Wörter geprüft. Schlüssel ist ein Datensatz. Der Wert
378 -- darf nicht zu logisch `falsch` auswerten.
379 local exceptions_regular = {}
383 --- <strong>nicht-öffentlich</strong> Öffne Ausnahmedatei.
384 -- Die Datei wird im aktuellen Verzeichnis gesucht, im Verzeichnis `lua`
385 -- und im Verzeichnis `skripte/lua`, in der angegebenen Reihenfolge.
387 -- @param fname Dateiname
389 -- @return Dateihandle und Pfad der erfolgreich geöffneten
390 -- Ausnahmedatei.
391 local function _open_exception_file(fname)
392 -- Tabelle möglicher Suchpfade.
393 local search_path = {
395 "lua/",
396 "skripte/lua/",
398 -- Ermittle plattformspezifischen Verzeichnistrenner.
399 local dirsep = string.match(package.config, "(.-)\n")
400 -- Suche Ausnahmedatei.
401 local err = {}
402 local f
403 for i, path in ipairs(search_path) do
404 -- Ersetzte `/` durch geeigneten Verzeichnistrenner.
405 path = Ugsub(path, "/", dirsep)
406 f, err[i] = io.open(path .. fname, 'r')
407 if f then return f, path end
409 error(err[1])
414 --- Lese Ausnahmeliste von Datensätzen aus Datei.
415 -- Die Datensätze (Zeilen) der Datei werden in der übergebenen Tabelle
416 -- als Schlüssel gespeichert.
418 -- @param fname Dateiname (optional mit Pfad)
419 -- @param exceptions Tabelle, in der Ausnahmen gespeichert werden
421 -- @return Dateiname der erfolgreich gelesenen Datei.
422 local function read_exception_file(fname, exceptions)
423 local f, path = _open_exception_file(fname)
424 -- Lese Datei in einem Rutsch.
425 local s = f:read('*all')
426 f:close()
427 -- Füge Datensätze der Ausnahmeliste hinzu.
428 for record in Ugmatch(s, "(.-)\n") do
429 exceptions[record] = true
431 return path .. fname
433 M.read_exception_file = read_exception_file
437 --- Lese Ausnahmeliste für normale Prüfung.
439 -- @param fname Dateiname (optional mit Pfad)
441 -- @return Dateiname der erfolgreich gelesenen Datei.
442 local function read_regular_exceptions(fname)
443 return read_exception_file(fname, exceptions_regular)
445 M.read_regular_exceptions = read_regular_exceptions
449 --- Prüfe einen Datensatz auf Wohlgeformtheit. Geprüft wird das Format
450 --- des Datensatzes und die Zulässigkeit sämtlicher Wörter.
452 -- @param record zu prüfender Datensatz
454 -- @return <code>true, info</code>, falls der Datensatz wohlgeformt
455 -- ist;<br /> <code>false, info</code>, falls der Datensatz nicht
456 -- wohlgeformt ist.<br /> <code>info</code> ist eine Tabelle mit näheren
457 -- Informationen zum Datensatz bzw. zum Fehler. Hat der Datensatz ein
458 -- unzulässiges Format, so ist <code>info = nil</code>. Andernfalls
459 -- enthält <code>info</code> das Wort des ersten Feldes des Datensatzes,
460 -- Informationen zu dessen Eszett-Schreibung sowie den Datensatztyp.<br
461 -- /> Enthält der Datensatz ein unzulässiges Wort, so enthält
462 -- <code>info</code> die Feldnummer und eine Fehlerbeschreibung des
463 -- unzulässigen Wortes.
464 local function validate_record(record)
465 -- Prüfe Gültigkeit des Datensatzes.
466 local rectype = identify_record(record)
467 if not rectype then return false, nil end
468 -- Sichere Typ in Rückgabetabelle.
469 local info = { type = rectype }
470 -- Verzichte auf Prüfung der Wortstruktur bei bestimmten Datensätzen.
471 if exceptions_regular[record] then
472 info.is_exception = true
473 return true, info
475 -- Zerlege Datensatz.
476 local trec = split(record)
477 -- Sichere Datensatztabelle in Rückgabetabelle.
478 info.trec = trec
479 -- Merke Inhalt von Feld 1 für Gleichheitsprüfung der belegten
480 -- Felder.
481 local field1 = trec[1]
482 if not field1 then
483 info.errfield = 1
484 info.errmsg = "leer"
485 return false, info
487 -- Merke Eigenschaften von Feld 1 für Eszett-Prüfung (siehe unten).
488 local props1
489 for i = 1,#trec do
490 local word = trec[i]
491 if word then
492 -- Hat das Wort eine zulässige Struktur?
493 local props, msg = validate_word(word)
494 if not props then
495 info.errfield = i
496 info.errmsg = msg
497 return false, info
499 if i == 1 then props1 = props end
500 -- Stimmt Wort mit Feld 1 überein?
501 word = Ugsub(props.norm_word, "-", "")
502 if word ~= field1 then
503 info.errfield = i
504 info.errmsg = "ungleich Feld 1"
505 return false, info
507 -- Tritt eine Spezialtrennung an unzulässiger Feldnummer auf?
508 if props.has_nonstd and (i==2 or i==4 or i==5 or i==7) then
509 info.errfield = i
510 info.errmsg = "unzulässige Spezialtrennung"
511 return false, info
513 -- Tritt eine Dreikonsonantenregel für 's' an unzulässiger Feldnummer auf?
514 if props.has_nonstd_sss and (i ~= 8) then
515 info.errfield = i
516 info.errmsg = "unzulässige Spezialtrennung"
517 return false, info
519 -- Tritt 'ß' an unzulässiger Feldnummer auf?
520 if props.has_eszett and (i > 4) then
521 info.errfield = i
522 info.errmsg = "unzulässiges Eszett"
523 return false, info
527 info.has_eszett = props1.has_eszett
528 return true, info
530 M.validate_record = validate_record
534 --- Extrahiert den Eintrag für die traditionelle Rechtschreibung.
535 local function extract_trad(trec)
536 return trec[2] or trec[3] or trec[5] or trec[6]
539 --- Extrahiert den Eintrag für die reformierte Rechtschreibung.
540 local function extract_refo(trec)
541 return trec[2] or trec[4] or trec[5] or trec[7]
544 --- Extrahiert den Eintrag für die traditionelle Rechtschreibung in der
545 --- Schweiz.
546 local function extract_swiss(trec)
547 return trec[2] or trec[3] or trec[5] or trec[8] or trec[6]
552 -- Tabelle, die spezifische Daten für die verschiedenen
553 -- Rechtschreibungen enthält.
554 local spellings = {
555 trad = {
556 extract = extract_trad,
558 refo = {
559 extract = extract_refo,
561 swiss = {
562 extract = extract_swiss,
568 -- Tabelle von Wörtern mit Eszett. Schlüssel sind Indizes, Werte sind
569 -- Datensatztabellen.
570 local eszett_forms = {}
571 -- Tabelle von Wörtern ohne Eszett, aber mit Doppel-s. Schlüssel sind
572 -- Doppel-s-Schreibungen, Werte sind Datensatztabellen.
573 local ss_forms = {}
574 -- Sequenz von Zeilennummern unzulässiger Datensätze.
575 local bad_lineno = {}
579 --- Speichere Varianten von Wörtern mit Eszett.
580 -- Speichere alle Wörter i) mit Eszett und ii) ohne Eszett,
581 -- aber mit Doppel-s, in Kleinschreibung für nachgelagerte
582 -- Prüfung auf vorhandene Eszett-Ersatzschreibung.
584 -- @param trec (erweiterte) Datensatztabelle
585 local function prepare_eszett_check(trec)
586 local word_lower = Ulower(trec[1])
587 if trec.has_eszett then
588 trec.eszett_form = word_lower
589 table.insert(eszett_forms, trec)
590 elseif Ufind(word_lower, "ss") then
591 ss_forms[word_lower] = trec
597 --- Prüfe Eszett-Ersatzschreibungen auf Vollständigkeit.
598 local function check_eszett()
599 -- Prüfe, ob zu jeder Eszett-Schreibung eine Doppel-s-Schreibung
600 -- vorhanden ist.
601 for _,trec_eszett in ipairs(eszett_forms) do
602 local ss_form = Ugsub(trec_eszett.eszett_form, "ß", "ss")
603 local trec_ss = ss_forms[ss_form]
604 -- Fehlt Datensatz mit Doppel-s-Schreibung?
605 if not trec_ss then
606 -- Merke fehlerhafte Zeilennummer für Commit-Ermittlung.
607 bad_lineno[trec_eszett.lineno] = true
608 -- Gebe fehlende Doppel-s-Schreibung aus.
609 io.stderr:write("Zeile ", trec_eszett.lineno, " fehlende Doppel-s-Schreibung: ", trec_eszett[1], "\n")
610 else
611 -- Prüfe, ob die Ersatzschreibung in jeder Rechtschreibung
612 -- existiert, in der die Eszett-Schreibung gültig ist.
613 for spelling,data in pairs(spellings) do
614 local hyph_eszett = data.extract(trec_eszett)
615 local hyph_ss = data.extract(trec_ss)
616 -- Fehlende Ersatzschreibung?
617 if hyph_eszett and not hyph_ss then
618 -- Merke fehlerhafte Zeilennummer für Commit-Ermittlung.
619 bad_lineno[trec_ss.lineno] = true
620 -- Gebe fehlende Doppel-s-Schreibung aus.
621 io.stderr:write("Zeile ", trec_ss.lineno, " fehlende Doppel-s-Schreibung (", spelling, "): ", trec_ss[1], "\n")
630 --- Prüfe eine Datei auf Wohlgeformtheit.
631 -- Geprüft werden das Format der Datensätze und die Zulässigkeit
632 -- sämtlicher Wörter. Während der Prüfung werden die Häufigkeiten der
633 -- verschiedenen Datensatztypen erhoben. Die übergebene Datei wird
634 -- nicht geschlossen.
636 -- @param f Dateihandle
638 -- @return Tabelle mit Häufigkeiten der Datensatztypen.
639 local function validate_file(f)
640 -- Gesamtzahl der Datensätze.
641 local cnt_lineno = 0
642 -- Anzahl der identifizierten Datensatztypen.
643 local cnt_rectypes = {
644 ua = 0,
645 uxt_ = 0,
646 ux_r = 0,
647 uxtr = 0,
648 ux__c = 0,
649 ux__xt_ = 0,
650 ux__x_r = 0,
651 ux__x__s = 0,
652 ux__xt_s = 0,
653 ux__xtr = 0,
654 ux__xtr_ = 0,
655 ux__xtrs = 0,
656 ux_rc = 0,
657 ux_rxtr = 0,
658 ux_rxtr_ = 0,
659 uxtrxtrs = 0,
661 -- Iteriere über Zeilen der Eingabe.
662 for record in f:lines() do
663 cnt_lineno = cnt_lineno + 1
664 local is_valid, info = validate_record(record)
665 -- Datensatz zulässig?
666 if is_valid then
667 -- Zähle Vorkommen des Typs.
668 cnt_rectypes[info.type] = cnt_rectypes[info.type] + 1
669 if not info.is_exception then
670 local trec = info.trec
671 trec.has_eszett = info.has_eszett
672 trec.lineno = cnt_lineno
673 prepare_eszett_check(trec)
675 else-- Datensatz unzulässig.
676 -- Zeile ausgeben.
677 io.stderr:write("Zeile ", tostring(cnt_lineno))
678 -- Fehlermeldung ausgeben.
679 if not info then
680 io.stderr:write(" ungültiger Datensatz")
681 else
682 io.stderr:write(" Feld ", tostring(info.errfield), ": ", info.errmsg)
684 -- Datensatz ausgeben.
685 io.stderr:write(": ", record, "\n")
686 -- Merke fehlerhafte Zeilennummer für Commit-Ermittlung.
687 bad_lineno[cnt_lineno] = true
690 -- Nachgeschaltete Eszett-Prüfung.
691 check_eszett()
692 -- Anzahl der ungültigen Datensätze.
693 local cnt_invalid = 0
694 for _,__ in pairs(bad_lineno) do
695 cnt_invalid = cnt_invalid + 1
697 return {
698 cnt_total = cnt_lineno,
699 cnt_invalid = cnt_invalid,
700 cnt_rectypes = cnt_rectypes,
701 bad_lineno = bad_lineno,
704 M.validate_file = validate_file
708 --- Output record statistics.
709 -- This function prints all valid record types' frequency.
711 -- @param count Table containing record statistics.
712 local function output_record_statistics(count)
713 print("ua ", count.ua)
714 print("uxt_ ", count.uxt_)
715 print("ux_r ", count.ux_r)
716 print("uxtr ", count.uxtr)
717 print("ux__c ", count.ux__c)
718 print("ux__xt_ ", count.ux__xt_)
719 print("ux__x_r ", count.ux__x_r)
720 print("ux__x__s ", count.ux__x__s)
721 print("ux__xt_s ", count.ux__xt_s)
722 print("ux__xtr ", count.ux__xtr)
723 print("ux__xtr_ ", count.ux__xtr_)
724 print("ux__xtrs ", count.ux__xtrs)
725 print("ux_rc ", count.ux_rc)
726 print("ux_rxtr ", count.ux_rxtr)
727 print("ux_rxtr_ ", count.ux_rxtr_)
728 print("uxtrxtrs ", count.uxtrxtrs)
730 M.output_record_statistics = output_record_statistics
734 -- Exportiere Modul-Tabelle.
735 return M