diff options
| author | Matthias Andreas Benkard <code@mail.matthias.benkard.de> | 2026-08-25 22:49:04 +0200 |
|---|---|---|
| committer | Matthias Andreas Benkard <code@mail.matthias.benkard.de> | 2026-08-25 22:49:04 +0200 |
| commit | 20d8b08492bea77f42d019af5db66ef184f105b3 (patch) | |
| tree | b91b5dfbd39711a5f9d4e0a261869457aafdeacd /src/main | |
| parent | ff8e9837f11a1488dbcdf95fa47457ef74f864bb (diff) | |
Rheinland-Pfalz kennt den Strichpunkt und die Einleitung
Von den drei benannten Idiom-Grenzen des rheinland-pfälzischen Falles fallen zwei.
Die erste: „In Satz 2 wird der Punkt durch einen Strichpunkt ersetzt und
folgender Halbsatz angefügt: „…““. Zwei Neuerungen in einem Satz. „Strichpunkt“
ist die deutsche Nebenform des Semikolons und tritt zu den Satzzeichen-Mustern
hinzu — als Subjekt wie als Ziel, ohne eine einzige neue Gruppe, damit die
hartkodierten Gruppennummern des Erkenners unberührt bleiben. Der Halbsatz wird
eine eigene Ebene der Anfügung. Er beginnt keinen neuen Satz, sondern setzt den
bestehenden hinter dem Strichpunkt fort; angefügt wird er deshalb nur, wenn der
Zieltext tatsächlich auf einen Strichpunkt endet. Sonst stünde er hinter einem
Punkt und wäre gerade kein Halbsatz. Als Ziel einer Struktur-Einfügung oder
-Ersetzung bleibt er ausgeschlossen: Welche Hälfte eines Satzes gemeint wäre,
sagt keine Bezeichnung. Die Verbundmechanik selbst brauchte keinen Eingriff.
Die zweite: „In der Einleitung werden die Worte „…“ durch die Angabe „…“
ersetzt.“ Das ist ein Chapeau-Lokator wie „im Satzteil vor Nummer 1“, nur ohne
Bezugspunkt; er tritt als zweite Alternative zum vorhandenen Muster. Eine echte
Verengung auf den Satzteil vor der ersten Marke wäre eine eigene
Stellenkomponente und ist es nicht: Kommt der Zieltext außer im Chapeau auch in
einer Nummer vor, so meldet die Auflösung Mehrdeutigkeit — der richtige Ausgang.
Dass beide Befehle am rheinland-pfälzischen Stamm gleichwohl nicht greifen, liegt
an der Quelle und nicht am Werkzeug: Das Portal führt keine früheren
Gesamtausgaben, der Stamm ist hier die Nachfassung, und ihr Wortlaut trägt die
Änderung bereits.
Offen bleibt die dritte Grenze, die Verweisung auf einen anderen Punkt desselben
Artikels.
Geprüft: 386 Tests (zuvor 381).
Change-Id: I993d8645fba1c933ad8497ca175cef265df2c0b0
Diffstat (limited to 'src/main')
4 files changed, 67 insertions, 14 deletions
diff --git a/src/main/java/eu/mulk/aendggner/aenderung/Aenderungsbefehl.java b/src/main/java/eu/mulk/aendggner/aenderung/Aenderungsbefehl.java index a80e09a..47bca25 100644 --- a/src/main/java/eu/mulk/aendggner/aenderung/Aenderungsbefehl.java +++ b/src/main/java/eu/mulk/aendggner/aenderung/Aenderungsbefehl.java @@ -22,6 +22,8 @@ public sealed interface Aenderungsbefehl { PARAGRAPH, ABSATZ, SATZ, + /** Die Hälfte eines am Strichpunkt geteilten Satzes; sie beginnt keinen neuen Satz. */ + HALBSATZ, NUMMER, BUCHSTABE } diff --git a/src/main/java/eu/mulk/aendggner/aenderung/parse/BefehlErkenner.java b/src/main/java/eu/mulk/aendggner/aenderung/parse/BefehlErkenner.java index c825a64..ef49b7b 100644 --- a/src/main/java/eu/mulk/aendggner/aenderung/parse/BefehlErkenner.java +++ b/src/main/java/eu/mulk/aendggner/aenderung/parse/BefehlErkenner.java @@ -180,7 +180,7 @@ final class BefehlErkenner { // ersetzt: „…““ — der Ersatz steht als Zitatblock hinter dem Doppelpunkt. private static final Pattern PUNKT_DURCH_WORTLAUT = Pattern.compile( - "^(?:In )?(.+?) (?:wird|werden) (der Punkt|das Komma|das Semikolon)" + "^(?:In )?(.+?) (?:wird|werden) (der Punkt|das Komma|das Semikolon|der Strichpunkt)" + " am Ende(?: des Satzes)? durch " + "(?:die |den )?folgende[n]? (?:Wörter|Wortlaut) ersetzt: " + Z @@ -194,12 +194,12 @@ final class BefehlErkenner { // prüft, statt es an das Einheitsende zu heften. private static final Pattern SATZZEICHEN_ERSETZUNG_OHNE_STELLE = Pattern.compile( - "^(Der Punkt|Das Komma|Das Semikolon)(?: am Ende(?: des Satzes)?)? wird durch " + "^(Der Punkt|Das Komma|Das Semikolon|Der Strichpunkt)(?: am Ende(?: des Satzes)?)? wird durch " + "(ein Komma und " + WOERTER + " " + Z - + "|ein Komma|einen Punkt|ein Semikolon|" + + "|ein Komma|einen Punkt|ein Semikolon|einen Strichpunkt|" + WOERTER + " " + Z @@ -209,13 +209,13 @@ final class BefehlErkenner { // „am Ende“ ist wie oben entbehrlich. private static final Pattern SATZZEICHEN_ERSETZUNG = Pattern.compile( - "^(?:In )?(.+?) (?:wird|werden) (der Punkt|das Komma|das Semikolon)" + "^(?:In )?(.+?) (?:wird|werden) (der Punkt|das Komma|das Semikolon|der Strichpunkt)" + "(?: am Ende(?: des Satzes)?)? durch " + "(ein Komma und " + WOERTER + " " + Z - + "|ein Komma|einen Punkt|ein Semikolon|" + + "|ein Komma|einen Punkt|ein Semikolon|einen Strichpunkt|" + WOERTER + " " + Z @@ -473,7 +473,7 @@ final class BefehlErkenner { + WOERTER + " " + Z - + "(?: am Ende)? durch (ein Komma|ein Semikolon|einen Punkt) ersetzt\\.$"); + + "(?: am Ende)? durch (ein Komma|ein Semikolon|einen Strichpunkt|einen Punkt) ersetzt\\.$"); // „In Satz 2 wird nach dem Wort «1» ein Komma und werden die Wörter «2» eingefügt.“ private static final Pattern KOMMA_UND_WOERTER_EINFUEGUNG = @@ -518,6 +518,8 @@ final class BefehlErkenner { + Z + "\\.?$"); + private static final Pattern HALBSATZ = Pattern.compile("Halbsatz(?: \\d+[a-z]?)?"); + private static final Pattern EBENE_BEZEICHNUNG = Pattern.compile( "^(?:(?:§|Art\\.) (\\d+[a-z]?)|(?:Absatz|Abs\\.) (\\d+[a-z]?)|Satz(?: (\\d+[a-z]?))?|Sätze" @@ -583,7 +585,7 @@ final class BefehlErkenner { + WOERTER + " " + Z - + "|(ein Komma|ein Semikolon))$"); + + "|(ein Komma|ein Semikolon|einen Strichpunkt))$"); // „… ein Komma eingefügt und werden …“: Trennstellen eines Verbundbefehls sind „ und “ (ggf. mit // Komma) bzw. „, “ direkt vor „wird/werden“. Innerhalb von Zitaten steht „ und “ als «n» @@ -597,7 +599,7 @@ final class BefehlErkenner { + "|, (?=(?:die (?:Wörter|Worte)|das Wort|die Angabe|die Zahl) «)" // Ebenso vor einer Satzzeichen-Klausel („… ersetzt, der Punkt am Ende durch ein // Semikolon ersetzt und …“). - + "|, (?=(?:der Punkt|das Komma|das Semikolon) am Ende)"); + + "|, (?=(?:der Punkt|das Komma|das Semikolon|der Strichpunkt) am Ende)"); private static final Pattern WIRD_WERDEN = Pattern.compile(" (?:wird|werden) "); // Rechte Verbundklausel, die ihre Fundstelle als Struktureinheit nennt („in Satz 1 wird …“, @@ -622,7 +624,7 @@ final class BefehlErkenner { "^(?:In )?(.+?) (?:wird|werden) (?:jeweils )?(nach|vor) " + "(?:dem Wort|den (?:Wörtern|Worten)|der Angabe|der Zahl|der Verweisung) " + Z - + " (ein Komma|ein Semikolon|einen Punkt) eingefügt\\.$"); + + " (ein Komma|ein Semikolon|einen Strichpunkt|einen Punkt) eingefügt\\.$"); // „Nach der Angabe «1» wird die Angabe «2» eingefügt.“ — Anker zuerst, ohne eigene Stelle (nutzt // den Kontext). Tritt vor allem als rechte Klausel eines Verbundbefehls auf. @@ -634,7 +636,7 @@ final class BefehlErkenner { + WOERTER + " " + Z - + "|(ein Komma|ein Semikolon)) eingefügt\\.$"); + + "|(ein Komma|ein Semikolon|einen Strichpunkt)) eingefügt\\.$"); private BefehlErkenner() {} @@ -1646,7 +1648,7 @@ final class BefehlErkenner { // Klausel, deren Subjekt ein Satzzeichen ist („das Komma wird durch … ersetzt“). private static final Pattern SATZZEICHEN_SUBJEKT = - Pattern.compile("(?i)(?:der Punkt|das Komma|das Semikolon)\\b"); + Pattern.compile("(?i)(?:der Punkt|das Komma|das Semikolon|der Strichpunkt)\\b"); private static Optional<Aenderungsbefehl> erkenneVerbund( String text, Stelle kontext, ZitatExtraktor.Ergebnis zitate, Provenienz provenienz) { @@ -2032,6 +2034,11 @@ final class BefehlErkenner { private record EbeneBezeichnung(Ebene ebene, String bezeichnung) {} private static Optional<EbeneBezeichnung> ebeneUndBezeichnung(String phrase) { + // Der Halbsatz steht außerhalb des Musters, damit dessen Gruppennummern unberührt bleiben; + // eine Bezeichnung führt er nicht, denn angefügt wird stets hinter dem Strichpunkt. + if (HALBSATZ.matcher(phrase.strip()).matches()) { + return Optional.of(new EbeneBezeichnung(Ebene.HALBSATZ, null)); + } var m = EBENE_BEZEICHNUNG.matcher(phrase.strip()); if (!m.matches()) { return Optional.empty(); @@ -2280,6 +2287,7 @@ final class BefehlErkenner { case PARAGRAPH -> new Stelle.Paragraph(nummer); case ABSATZ -> new Stelle.AbsatzNr(nummer); case SATZ -> new Stelle.SatzNr(nummer); + case HALBSATZ -> new Stelle.HalbsatzNr(nummer); case NUMMER -> new Stelle.NummerNr(nummer); case BUCHSTABE -> new Stelle.BuchstabeNr(nummer); }; @@ -2366,7 +2374,8 @@ final class BefehlErkenner { return switch (Character.toLowerCase(phrase.charAt(0)) + phrase.substring(1)) { case "der Punkt", "einen Punkt" -> "."; case "das Komma", "ein Komma" -> ","; - case "das Semikolon", "ein Semikolon" -> ";"; + // „Strichpunkt“ ist die deutsche Nebenform des Semikolons; Rheinland-Pfalz schreibt sie. + case "das Semikolon", "ein Semikolon", "der Strichpunkt", "einen Strichpunkt" -> ";"; default -> throw new IllegalArgumentException("Unbekanntes Satzzeichen: " + phrase); }; } diff --git a/src/main/java/eu/mulk/aendggner/aenderung/parse/StellenParser.java b/src/main/java/eu/mulk/aendggner/aenderung/parse/StellenParser.java index 9176f89..466adfe 100644 --- a/src/main/java/eu/mulk/aendggner/aenderung/parse/StellenParser.java +++ b/src/main/java/eu/mulk/aendggner/aenderung/parse/StellenParser.java @@ -54,10 +54,18 @@ public final class StellenParser { // „in dem Satzteil vor Nummer 1“, „in der Angabe vor Nummer 1“, bayerisch auch „Satzteil nach // Nr. 3“ — verfeinernde Chapeau-Angaben ohne eigene Stelle-Komponente; die Ersetzung sucht // ohnehin im Text der umgebenden Stelle. + // + // Die zweite Alternative nennt keinen Bezugspunkt: „In der Einleitung“ (Rheinland-Pfalz) meint + // dasselbe wie „im Satzteil vor Nummer 1“, nämlich den Chapeau des Satzes, den der Rahmen + // bezeichnet. Auch sie trägt keine eigene Komponente. Kommt der Zieltext außer im Chapeau auch + // in einer Nummer vor, so meldet die Auflösung Mehrdeutigkeit — der richtige Ausgang; eine + // echte Verengung auf den Satzteil vor der ersten Marke wäre eine eigene Komponente und gehört + // nicht hierher. private static final Pattern CHAPEAU_QUALIFIER = Pattern.compile( - "(?i)(?:im |in dem |in der |dem |der )?(?:Satzteil|Angabe) (?:vor|nach) " - + "(?:Nummer|Nr\\.|Buchstabe|Buchst\\.|Satz|Absatz|Abs\\.) \\S+"); + "(?i)(?:im |in dem |in der |dem |der )?(?:(?:Satzteil|Angabe) (?:vor|nach) " + + "(?:Nummer|Nr\\.|Buchstabe|Buchst\\.|Satz|Absatz|Abs\\.) \\S+" + + "|(?:Einleitungssatz|Eingangssatz|Einleitung)(?!\\p{L}))"); /** * Wahr, wenn die Phrase ausschließlich aus einem Chapeau-Qualifier besteht (z.B. „im Satzteil vor diff --git a/src/main/java/eu/mulk/aendggner/anwendung/BefehlAnwender.java b/src/main/java/eu/mulk/aendggner/anwendung/BefehlAnwender.java index 56b610e..64c6564 100644 --- a/src/main/java/eu/mulk/aendggner/anwendung/BefehlAnwender.java +++ b/src/main/java/eu/mulk/aendggner/anwendung/BefehlAnwender.java @@ -591,6 +591,7 @@ public final class BefehlAnwender { bezeichnung, s.stelle().paragraph().map(Stelle.Paragraph::sigel).orElse("§")); case ABSATZ -> new Stelle.AbsatzNr(bezeichnung); case SATZ -> new Stelle.SatzNr(bezeichnung); + case HALBSATZ -> new Stelle.HalbsatzNr(bezeichnung); case NUMMER -> new Stelle.NummerNr(bezeichnung); case BUCHSTABE -> new Stelle.BuchstabeNr(bezeichnung); }; @@ -1276,6 +1277,15 @@ public final class BefehlAnwender { normen.addAll(vonIndex, neue); yield angewandt(befehl, neue.stream().map(Norm::enbez).toList()); } + // Ein Halbsatz ist keine selbständig bezeichnete Einheit: Er lässt sich anfügen + // (siehe wendeAnfuegungAn), aber nicht als Ziel ersetzen oder einfügen — welche Hälfte + // eines Satzes gemeint wäre, sagt keine Bezeichnung. + case HALBSATZ -> + manuell( + befehl, + Grund.NICHT_UNTERSTUETZT, + "Der Halbsatz ist keine selbständig bezeichnete Einheit; nur seine Anfügung ist" + + " umgesetzt."); }; } @@ -1474,6 +1484,15 @@ public final class BefehlAnwender { } return TextErgebnis.ok(neuerText); }); + // Ein Halbsatz ist keine selbständig bezeichnete Einheit: Er lässt sich anfügen + // (siehe wendeAnfuegungAn), aber nicht als Ziel ersetzen oder einfügen — welche Hälfte + // eines Satzes gemeint wäre, sagt keine Bezeichnung. + case HALBSATZ -> + manuell( + befehl, + Grund.NICHT_UNTERSTUETZT, + "Der Halbsatz ist keine selbständig bezeichnete Einheit; nur seine Anfügung ist" + + " umgesetzt."); }; } @@ -1636,6 +1655,21 @@ public final class BefehlAnwender { normen, befehl, text -> TextErgebnis.ok(text.stripTrailing() + " " + befehl.text().strip())); + // Ein Halbsatz beginnt keinen neuen Satz, sondern setzt den bestehenden hinter dem + // Strichpunkt fort. Angehängt wird deshalb wie beim Satz — aber nur, wenn der Zieltext + // tatsächlich auf einen Strichpunkt endet: Sonst stünde der Halbsatz hinter einem Punkt und + // wäre gerade kein Halbsatz. Regelmäßig setzt ihn der Verbund unmittelbar zuvor. + case HALBSATZ -> + bearbeiteText( + normen, + befehl, + text -> + text.stripTrailing().endsWith(";") + ? TextErgebnis.ok(text.stripTrailing() + " " + befehl.text().strip()) + : TextErgebnis.fehler( + "Der Halbsatz setzt einen Strichpunkt voraus; der Zieltext endet nicht" + + " darauf.", + Grund.BESTAND_WIDERSPRICHT)); case NUMMER, BUCHSTABE -> bearbeiteText( normen, |
