fix: VF-Linienzug Journal-Reset, Spec-Grammatik (auf-ab/Linie-GF) und hz-Bug in HundM05
- vfl-journal-reset-lauf ans Funktionsanfang verschoben (Modus 1/2/3,
Lisp/vf_linienzug.lsp): stand bisher nach der Startpunkt/Hoehe/AS-
Praeambel und loeschte diese beim Reset selbst wieder - Journal begann
mit dem ersten STEP-Marker statt PT, betraf jede Kette.
- Spec-Grammatik erweitert (Lisp/vf_spec.lsp, lib/vf_linienzug_grammatik.py,
lib/vf_spec_export.py): "auf-ab" bei bereits flach ankommender Kette
(Richtungswahl statt Zielhoehe) und neue Linie-GF-Fragereihenfolge
(Kettenende direkt nach laenge_modus, inkl. vorgezogenem ES). Tests in
tests/test_vf_spec.lsp/.py angepasst.
- lib/vf_linienzug_grammatik.py: _linie("kettenende_ziel") erwartete
faelschlich ein hz-REAL nach der Distanz - der reale Bauablauf
(vfl-body-abschluss) leitet die Richtung dort aus dem Frame ab und
fragt sie nie. Fuehrte zu einem Token-Desync beim Dekodieren echter
Kettenende-Journale.
- tests/testdata/hundm05.json (VF_hundm05_LZ_01): 5 ueberfluessige
hz-Felder entfernt. vsp-linie injiziert bei JEDEM vorhandenen hz-Feld
einen Richtungs-Token, unabhaengig vom tatsaechlichen Bauablauf - die
Felder desyncten den Replay und liessen die Kette stumm mit der
falschen (verkuerzten) Geometrie fertig bauen.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
6f12cab86f
commit
4a057862a8
+139
-11
@@ -72,6 +72,9 @@ GF_LAENGE_MODUS = {"1": "endpunkt", "2": "hoehe", "3": "steigend"}
|
||||
VERTEILUNG = {"1": "haelfte", "2": "einlauf"}
|
||||
IM_VF = {"1": "horizontal", "2": "vario-kurve", "3": "auf-ab"}
|
||||
ENDPUNKT = {"1": "motorstation", "2": "weiter", "3": "kettenende"}
|
||||
# Richtungswahl fuer "auf-ab", wenn die Kette bereits FLACH ankommt
|
||||
# (vfl-frage-auf-ab-richtung: "1"=Auf, "2"=Ab) - siehe Kern.vf_einheit.
|
||||
RICHTUNG = {"1": "auf", "2": "ab"}
|
||||
ENDE3 = {"1": "ja-mit-es", "2": "ja-ohne-es", "3": "nein"}
|
||||
ENDE2 = {"1": "ja-mit-es", "2": "nein"}
|
||||
# Ja/Nein-Antworten des Dialogs. Als Tabelle gefuehrt (und nicht als
|
||||
@@ -147,13 +150,33 @@ class Grammatik(object):
|
||||
fragt. Der heutige Ablauf fragt sie dort NICHT mehr: die Aufrufstelle
|
||||
in vf-linienzug-modus gibt "2" (alles am Einlauf) fest vor
|
||||
(vfl-vf-einheit-abschluss, Argument gf-verteilung-fest).
|
||||
linie_gf_laenge_modus
|
||||
Ob eine "Linie-GF"-Sektion die Vorwahl "laenge_modus" (endpunkt/
|
||||
hoehe/steigend) kennt und "Ist das Kettenende?" SOFORT danach fragt
|
||||
(NEU 2026-09-12, siehe vsp-sektion in Lisp/vf_spec.lsp). Die
|
||||
eingefrorene Aufzeichnung stammt von VOR dieser Erweiterung: dort
|
||||
folgen auf Laenge/Gefaelle direkt Winkel/Hoehe, und "Ist das
|
||||
Kettenende?" steht ganz am Schluss der Sektion.
|
||||
linie_hz_je_segment
|
||||
Ob vfl-in-abstand die gesnappte Fahrtrichtung (hz) bei JEDEM Segment
|
||||
journalisiert (linienzug_tests.json/-stufe2.json, 2026-09-12: auch
|
||||
nach einer Vario-Kurve oder einem neuen Glied) oder nur beim
|
||||
ALLERERSTEN Segment der ganzen Kette (eingefrorene Aufzeichnung,
|
||||
hm_recformat.json). Muss grammatik-gebunden sein, nicht per
|
||||
Lookahead entschieden: ein direkt auf DL folgendes REAL kann auch
|
||||
"eingang_hoehe" einer Horizontal-VF-Sektion sein (siehe
|
||||
Kern._opt_eingang_hoehe) - blinder Lookahead wuerde das faelschlich
|
||||
als hz konsumieren und die Queue verschieben.
|
||||
"""
|
||||
|
||||
def __init__(self, name, menu_start, horizontal_vf_fragt_gf_verteilung):
|
||||
def __init__(self, name, menu_start, horizontal_vf_fragt_gf_verteilung,
|
||||
linie_gf_laenge_modus=True, linie_hz_je_segment=True):
|
||||
self.name = name
|
||||
self.menu_start = menu_start
|
||||
self.horizontal_vf_fragt_gf_verteilung = (
|
||||
horizontal_vf_fragt_gf_verteilung)
|
||||
self.linie_gf_laenge_modus = linie_gf_laenge_modus
|
||||
self.linie_hz_je_segment = linie_hz_je_segment
|
||||
|
||||
def __repr__(self):
|
||||
return "Grammatik(%r)" % self.name
|
||||
@@ -161,9 +184,13 @@ class Grammatik(object):
|
||||
|
||||
# Stand der EINGEFRORENEN Aufzeichnung (hm_recformat.json) - Vorgabe, damit
|
||||
# alle bestehenden Aufrufer unveraendert weiterlaufen.
|
||||
GRAMMATIK_AUFZEICHNUNG = Grammatik("aufzeichnung", MENU_START, True)
|
||||
GRAMMATIK_AUFZEICHNUNG = Grammatik("aufzeichnung", MENU_START, True,
|
||||
linie_gf_laenge_modus=False,
|
||||
linie_hz_je_segment=False)
|
||||
# Stand des LEBENDEN Bau-Ablaufs (vf_linienzug.lsp) - fuer neue Uebersetzungen.
|
||||
GRAMMATIK_AKTUELL = Grammatik("aktuell", MENU_START_AKTUELL, False)
|
||||
GRAMMATIK_AKTUELL = Grammatik("aktuell", MENU_START_AKTUELL, False,
|
||||
linie_gf_laenge_modus=True,
|
||||
linie_hz_je_segment=True)
|
||||
|
||||
|
||||
# ============================================================
|
||||
@@ -400,12 +427,35 @@ class Kern(object):
|
||||
return wert
|
||||
|
||||
def _linie(self, frage):
|
||||
"""Segmentlaenge, und NUR beim allerersten Segment der Kette die
|
||||
gesnappte Fahrtrichtung (vfl-in-abstand journalisiert sie nur bei
|
||||
freier Richtungswahl; jedes spaetere Segment erbt die Richtung des
|
||||
Vorgaengers)."""
|
||||
"""Segmentlaenge, und optional die gesnappte Fahrtrichtung
|
||||
(vfl-in-abstand journalisiert sie bei freier Richtungswahl; ein
|
||||
Segment, dessen Richtung schon feststeht, erbt sie vom Vorgaenger und
|
||||
hat keinen eigenen Journal-Eintrag). Grammatik-gebunden
|
||||
(linie_hz_je_segment), NICHT per blindem Lookahead: ein direkt auf DL
|
||||
folgendes REAL kann auch "eingang_hoehe" einer Horizontal-VF-Sektion
|
||||
sein (siehe _opt_eingang_hoehe, dort IMMER ein zweiter, eigener
|
||||
Lookahead direkt danach) - ein Lookahead hier ohne Grammatik-Wissen
|
||||
wuerde das faelschlich als hz konsumieren und die Queue verschieben.
|
||||
Die eingefrorene Aufzeichnung (hm_recformat.json) journalisiert die
|
||||
Richtung nur beim allerersten Segment der ganzen Kette, der heutige
|
||||
Ablauf (linienzug_tests.json/-stufe2.json, 2026-09-12) dagegen bei
|
||||
JEDEM Segment neu, auch nach einer Vario-Kurve oder einem neuen
|
||||
Glied.
|
||||
|
||||
Ausnahme "kettenende_ziel" (vf_einheit, Option 3 "Kettenende"):
|
||||
Lisp/vf_linienzug.lsp:7102 uebergibt vfl-body-abschluss dort
|
||||
(car (frame->hz-winkel frame)) - die Richtung wird aus dem
|
||||
bestehenden Frame ABGELEITET, nie erfragt. vfl-neue-linie-messen
|
||||
(Zeile 4829) misst danach nur noch die Distanz. Ein REAL direkt nach
|
||||
dem DL ist an dieser Stelle darum IMMER die Zielhoehe, nie ein hz -
|
||||
wurde bisher fälschlich als hz verschluckt (Token-Desync am
|
||||
Kettenende, siehe hundm05.json LZ_01/VF_0-Rekonstruktion)."""
|
||||
dl = float(self.nimm("DL"))
|
||||
hz = self.real() if (self.erster_dl and self.art() == "REAL") else None
|
||||
if frage == "kettenende_ziel":
|
||||
erwartet = False
|
||||
else:
|
||||
erwartet = self.erster_dl or self.grammatik.linie_hz_je_segment
|
||||
hz = self.real() if (erwartet and self.art() == "REAL") else None
|
||||
self.erster_dl = False
|
||||
self.sink.linie(dl, hz, frage)
|
||||
|
||||
@@ -415,6 +465,20 @@ class Kern(object):
|
||||
if self.art() == "INT":
|
||||
self.sink.int_feld("winkel_idx", self.ganz(), "winkel_idx")
|
||||
|
||||
def _opt_gf2_bestaetigung(self):
|
||||
"""Stufe-2-Fallback (vfl-frage-gf2-minimum, Lisp/vf_linienzug.lsp):
|
||||
steht nur im Journal, wenn die am Kettenende automatisch berechnete
|
||||
GF2-Laenge UNTER der ueblichen Mindestlaenge liegt (Stufe 1 -
|
||||
Winkeltabelle - hat dann keinen gueltigen Kandidaten geliefert).
|
||||
"ja" = mit der exakt berechneten (kuerzeren) Laenge bauen (trifft den
|
||||
Zielpunkt exakt), "nein" = auf die Mindestlaenge auffuellen
|
||||
(Zielpunkt verschiebt sich entsprechend). Faellt die Frage nicht an
|
||||
(GF2 lang genug oder Stufe 1 erfolgreich), gibt es keinen
|
||||
Journal-Eintrag - genau wie bei _opt_winkel_idx."""
|
||||
if self.art() == "STR":
|
||||
self._ja("gf2_exakt_bauen", "GF2 unter Mindestlaenge - exakt bauen",
|
||||
"gf2_minimum")
|
||||
|
||||
def _opt_eingang_hoehe(self):
|
||||
"""Ziel-Hoehe fuer den GF1-Einlauf einer mit "Horizontal-VF"
|
||||
eroeffneten VF-Einheit (vfl-prompt-hoehe-eingang-gf1). Sie wird nur
|
||||
@@ -494,7 +558,10 @@ class Kern(object):
|
||||
if label == "GF-Bogen":
|
||||
self.sink.int_feld("winkel", self.ganz(), "gf_bogen_winkel")
|
||||
self._code("seite", SEITE, "GF-Bogen-Seite", "gf_bogen_seite")
|
||||
elif label == "Linie-GF":
|
||||
elif label == "Linie-GF" and not self.grammatik.linie_gf_laenge_modus:
|
||||
# Eingefrorene Aufzeichnung (GRAMMATIK_AUFZEICHNUNG): stammt von
|
||||
# VOR der "laenge_modus"-Erweiterung - Laenge/Gefaelle direkt,
|
||||
# "Ist das Kettenende?" ganz am Schluss. Siehe Grammatik-Doku.
|
||||
self._linie("gf_linie")
|
||||
if self._code("gefaelle", GEFAELLE, "Gefaelle",
|
||||
"gefaelle") == "winkel":
|
||||
@@ -502,6 +569,56 @@ class Kern(object):
|
||||
else:
|
||||
self.sink.real_feld("hoehe", self.real(), "gf_hoehe")
|
||||
self._code("ende", ENDE3, "Kettenende", "ende3")
|
||||
elif label == "Linie-GF":
|
||||
# Reihenfolge seit 2026-09-12 (siehe Lisp/vf_spec.lsp
|
||||
# vsp-sektion, dortiger Kommentar): erst die Vorwahl
|
||||
# "laenge_modus" (endpunkt/hoehe/steigend), DANN sofort "Ist
|
||||
# das Kettenende?" - beide muessen VOR den modus-spezifischen
|
||||
# Feldern gelesen werden, der alte Ablauf (Laenge/Gefaelle
|
||||
# zuerst, Kettenende-Frage zuletzt) ist veraltet.
|
||||
modus = self._code("laenge_modus", GF_LAENGE_MODUS,
|
||||
"GF-Laengenmodus", "gf_laenge_modus")
|
||||
ende = self._code("ende", ENDE3, "Kettenende", "ende3")
|
||||
if ende == "ja-mit-es":
|
||||
# Das ES-Element wird HIER inline erfragt (Winkel/Seite,
|
||||
# OHNE eigenen STEP-Marker) - noch nicht decodierbar, bisher
|
||||
# in keiner Aufzeichnung vorgekommen. Fail fast statt falsch
|
||||
# zu raten (siehe Modul-Kopf, Invariante I3).
|
||||
raise ValueError(
|
||||
"Linie-GF mit ende=\"ja-mit-es\" wird von diesem Parser "
|
||||
"noch nicht unterstuetzt (Token %d)" % (self.pos - 1))
|
||||
if modus == "steigend":
|
||||
smodus = self._code("steigend_modus", GF_LAENGE_MODUS,
|
||||
"Steigend-Modus", "gf_laenge_modus")
|
||||
if smodus == "endpunkt":
|
||||
self._linie("gf_linie")
|
||||
elif self.erster_dl:
|
||||
raise ValueError(
|
||||
"Linie-GF steigend/%s als allererstes Segment der "
|
||||
"Kette wird von diesem Parser noch nicht "
|
||||
"unterstuetzt (Token %d)" % (smodus, self.pos - 1))
|
||||
if smodus == "hoehe":
|
||||
self.sink.real_feld("hoehe", self.real(), "gf_hoehe")
|
||||
self.sink.real_feld("winkel", self.real(), "gf_winkel")
|
||||
elif modus == "hoehe":
|
||||
if self.erster_dl:
|
||||
raise ValueError(
|
||||
"Linie-GF hoehe als allererstes Segment der Kette "
|
||||
"wird von diesem Parser noch nicht unterstuetzt "
|
||||
"(Token %d)" % (self.pos - 1))
|
||||
self._ja("sep_vor", "Separator vor", "sep_vor")
|
||||
self._ja("sep_nach", "Separator nach", "sep_nach")
|
||||
self.sink.real_feld("hoehe", self.real(), "gf_hoehe")
|
||||
self.sink.real_feld("winkel", self.real(), "gf_winkel")
|
||||
else: # "endpunkt"
|
||||
self._linie("gf_linie")
|
||||
self._ja("sep_vor", "Separator vor", "sep_vor")
|
||||
self._ja("sep_nach", "Separator nach", "sep_nach")
|
||||
if self._code("gefaelle", GEFAELLE, "Gefaelle",
|
||||
"gefaelle") == "winkel":
|
||||
self.sink.real_feld("winkel", self.real(), "gf_winkel")
|
||||
else:
|
||||
self.sink.real_feld("hoehe", self.real(), "gf_hoehe")
|
||||
elif label == "Linie-VF":
|
||||
self._linie("vf_linie")
|
||||
self.sink.real_feld("hoehe", self.real(), "vf_zielhoehe")
|
||||
@@ -570,6 +687,7 @@ class Kern(object):
|
||||
self._linie("kettenende_ziel")
|
||||
self.sink.real_feld("hoehe", self.real(), "kettenende_hoehe")
|
||||
self._opt_winkel_idx()
|
||||
self._opt_gf2_bestaetigung()
|
||||
self.sink.sub_ende()
|
||||
ziel_ende = True
|
||||
break
|
||||
@@ -592,8 +710,18 @@ class Kern(object):
|
||||
"vario_variante")
|
||||
else: # auf-ab
|
||||
self._linie("aufab_sub")
|
||||
self.sink.real_feld("hoehe", self.real(), "aufab_hoehe")
|
||||
self._opt_winkel_idx()
|
||||
# Zwei Auspraegungen, am naechsten Token erkennbar (Kern kennt
|
||||
# keinen eigenen "Modus"-Schalter, vf-linienzug-modus
|
||||
# entscheidet selbst anhand des Zeichnungszustands): kommt die
|
||||
# Kette bereits FLACH an, folgt eine STR-Richtungswahl (Auf/
|
||||
# Ab) + Winkeltabelle statt einer direkten Zielhoehe (REAL).
|
||||
if self.art() == "STR":
|
||||
self._code("richtung", RICHTUNG, "Auf/Ab-Richtung",
|
||||
"aufab_richtung")
|
||||
self._opt_winkel_idx()
|
||||
else:
|
||||
self.sink.real_feld("hoehe", self.real(), "aufab_hoehe")
|
||||
self._opt_winkel_idx()
|
||||
self.sink.sub_ende()
|
||||
# --- Abschluss (vfl-vf-einheit-abschluss) ---
|
||||
# Ohne ES-Wunsch am Zielpunkt endet die Kette dort direkt; bei
|
||||
|
||||
+87
-31
@@ -51,8 +51,9 @@ from vf_journal_export import arrays_einzeilig # noqa: E402
|
||||
from vf_linienzug_grammatik import ( # noqa: E402,F401
|
||||
ENDE2, ENDE3, ENDPUNKT, GEFAELLE, GF_LAENGE_MODUS, GLIEDER,
|
||||
GRAMMATIK_AKTUELL, GRAMMATIK_AUFZEICHNUNG, Grammatik, IM_VF, MENU_START,
|
||||
MENU_START_AKTUELL, MENU_WEITER, SEITE, VARIANTE, VERTEILUNG, VF_GLIEDER,
|
||||
Sink, code_aus_ja, code_aus_wert, lies_journal, pruefe_flachen_freitext)
|
||||
MENU_START_AKTUELL, MENU_WEITER, RICHTUNG, SEITE, VARIANTE, VERTEILUNG,
|
||||
VF_GLIEDER, Sink, code_aus_ja, code_aus_wert, lies_journal,
|
||||
pruefe_flachen_freitext)
|
||||
|
||||
PREFIX = "[vf_spec_export]"
|
||||
|
||||
@@ -302,6 +303,13 @@ class SpecSchreiber(object):
|
||||
self.tok = []
|
||||
self.erster_dl = True
|
||||
self.wo = "Spec"
|
||||
# Siehe sektion()/schreib(): bei "Linie-GF" mit ende="ja-mit-es"
|
||||
# zieht vf-linienzug-modus die ES-Winkel/Seite-Frage direkt in
|
||||
# dieselbe Antwortrunde (Fussabdruck muss vor der Laengenberechnung
|
||||
# feststehen) - die naechste Sektion (glied="ES") ist dann bereits
|
||||
# vollstaendig verbraucht und muss beim Weiterzaehlen uebersprungen
|
||||
# werden. Analog zu *vsp-es-verbraucht* in Lisp/vf_spec.lsp.
|
||||
self.es_verbraucht = False
|
||||
|
||||
# --- Ausgabe ---
|
||||
|
||||
@@ -328,28 +336,50 @@ class SpecSchreiber(object):
|
||||
|
||||
def linie(self, quelle):
|
||||
self.zahl("DL", self.feld(quelle, "dl"))
|
||||
if self.erster_dl:
|
||||
# Beim ersten Segment MUSS die Richtung dabeistehen (die Kette hat
|
||||
# noch keine geerbte), bei jedem spaeteren darf sie es NICHT -
|
||||
# sonst verschiebt sich die ganze Queue um einen Eintrag.
|
||||
# Feldpraesenz entscheidet, nicht "erster_dl": die eingefrorene
|
||||
# Aufzeichnung (hm_recformat.json) journalisiert die Richtung nur
|
||||
# beim allerersten Segment der ganzen Kette, der heutige Ablauf
|
||||
# (linienzug_tests.json/-stufe2.json, 2026-09-12) dagegen bei JEDEM
|
||||
# Segment neu, auch nach einer Vario-Kurve oder einem neuen Glied -
|
||||
# siehe Kern._linie in vf_linienzug_grammatik.py (Gegenstueck beim
|
||||
# Lesen, dort per Lookahead statt Feldpraesenz, weil die Spec beim
|
||||
# Lesen noch nicht existiert). Beim ALLERERSTEN Segment MUSS die
|
||||
# Richtung dabeistehen (die Kette hat noch keine geerbte).
|
||||
if self.erster_dl and "hz" not in quelle:
|
||||
raise ValueError("%s: Feld \"hz\" fehlt (Pflicht beim ersten "
|
||||
"Segment der Kette)" % self.wo)
|
||||
if "hz" in quelle:
|
||||
self.zahl("REAL", self.feld(quelle, "hz"))
|
||||
self.erster_dl = False
|
||||
elif "hz" in quelle:
|
||||
raise ValueError("%s: hz steht nur beim ERSTEN Segment der Kette "
|
||||
"im Journal (vfl-in-abstand)" % self.wo)
|
||||
self.erster_dl = False
|
||||
|
||||
def winkel_idx(self, quelle):
|
||||
if "winkel_idx" in quelle:
|
||||
self.raus("INT", str(int(quelle["winkel_idx"])))
|
||||
|
||||
def gf2_bestaetigung(self, quelle):
|
||||
# Stufe-2-Fallback (vfl-frage-gf2-minimum) - siehe
|
||||
# Kern._opt_gf2_bestaetigung in vf_linienzug_grammatik.py fuer die
|
||||
# volle Erklaerung. Nur vorhanden, wenn die berechnete GF2-Laenge
|
||||
# am Kettenende unter der Mindestlaenge lag.
|
||||
if "gf2_exakt_bauen" in quelle:
|
||||
self.raus("STR", code_aus_ja(quelle["gf2_exakt_bauen"]))
|
||||
|
||||
# --- Ablauf ---
|
||||
|
||||
def schreib(self):
|
||||
self.praeambel()
|
||||
sektionen = self.spec.get("sektionen") or []
|
||||
for nr, sek in enumerate(sektionen, 1):
|
||||
self.wo = "Sektion %d (%s)" % (nr, sek.get("glied"))
|
||||
self.sektion(sek, erster=(nr == 1))
|
||||
if not sektionen:
|
||||
raise ValueError("Spec hat keine Sektionen")
|
||||
n = len(sektionen)
|
||||
i = 0
|
||||
while i < n:
|
||||
sek = sektionen[i]
|
||||
naechste = sektionen[i + 1] if i + 1 < n else None
|
||||
self.wo = "Sektion %d (%s)" % (i + 1, sek.get("glied"))
|
||||
self.es_verbraucht = False
|
||||
self.sektion(sek, erster=(i == 0), naechste=naechste)
|
||||
i += 2 if self.es_verbraucht else 1
|
||||
return self.tok
|
||||
|
||||
def praeambel(self):
|
||||
@@ -369,7 +399,7 @@ class SpecSchreiber(object):
|
||||
self.raus("STR", code_aus_wert(SEITE, self.feld(start, "as_seite"),
|
||||
"as_seite"))
|
||||
|
||||
def sektion(self, sek, erster):
|
||||
def sektion(self, sek, erster, naechste=None):
|
||||
label = self.feld(sek, "glied")
|
||||
if label == "ES":
|
||||
self.es_glied(sek)
|
||||
@@ -393,22 +423,37 @@ class SpecSchreiber(object):
|
||||
# DL(+hz)-Ausgabe (self.linie) faellt bei "hoehe"/"steigend"+
|
||||
# "hoehe" NUR beim allerersten Segment der Kette an - siehe
|
||||
# self.linie/self.erster_dl.
|
||||
#
|
||||
# ACHTUNG Rundlauf-Test (TestRundlauf.test_verlustfrei): die 5
|
||||
# echten HundM-Ketten (hm_recformat.json) wurden VOR dieser
|
||||
# Erweiterung aufgezeichnet und enthalten daher fuer ihre
|
||||
# Linie-GF-Sektionen KEINE Separator-Antworten. spec_aus_journal
|
||||
# (Leser, unveraendert) liefert fuer sie weiterhin die alte,
|
||||
# separator-lose Spec-Form; journal_aus_spec (dieser Schreiber)
|
||||
# haengt bei modus="endpunkt" aber IMMER die beiden neuen
|
||||
# sep_vor/sep_nach-Tokens an. Der Rundlauf ist fuer Ketten mit
|
||||
# Linie-GF damit bewusst NICHT mehr verlustfrei - das ist eine
|
||||
# inhaerente Folge der neuen Pflichtfragen im interaktiven Ablauf
|
||||
# (vf-linienzug-modus), kein Fehler in diesem Schreiber. Siehe
|
||||
# doc/vf_linienzug_modus1_anleitung.md / Commit-Historie.
|
||||
modus = self.feld(sek, "laenge_modus", pflicht=False) or "endpunkt"
|
||||
self.raus("STR", code_aus_wert(GF_LAENGE_MODUS, modus,
|
||||
"laenge_modus"))
|
||||
# "Ist das Kettenende?" wird SOFORT nach der Laenge-Modus-Wahl
|
||||
# gefragt (vor Separator/Gefaelle/Laenge) - Gegenstueck zu
|
||||
# vsp-sektion in Lisp/vf_spec.lsp (Kommentar dort: "NEU
|
||||
# 2026-09-12"). Bei "ja-mit-es" fragt vf-linienzug-modus DIREKT im
|
||||
# Anschluss auch ES-Winkel/-Seite (deren Fussabdruck muss schon in
|
||||
# die Laengenberechnung eingehen) - die Spec beschreibt das
|
||||
# ES-Element trotzdem als EIGENE, nachfolgende Sektion
|
||||
# (glied="ES") fuer bessere Lesbarkeit. Die beiden Tokengruppen
|
||||
# werden hier zusammengezogen: "ende" + (bei ja-mit-es) die
|
||||
# Winkel/Seite-Tokens der naechsten Sektion, vorgezogen - die
|
||||
# eigentliche ES-Sektion ist dadurch schon vollstaendig
|
||||
# verbraucht (self.es_verbraucht, von schreib() zum Ueberspringen
|
||||
# ausgewertet), sonst kaemen ihre Tokens ein zweites Mal.
|
||||
ende_wert = self.feld(sek, "ende")
|
||||
self.raus("STR", code_aus_wert(ENDE3, ende_wert, "ende"))
|
||||
if ende_wert == "ja-mit-es":
|
||||
if naechste is None:
|
||||
raise ValueError("%s: ende=\"ja-mit-es\", aber keine "
|
||||
"folgende ES-Sektion vorhanden" % self.wo)
|
||||
if naechste.get("glied") != "ES":
|
||||
raise ValueError("%s: ende=\"ja-mit-es\" erwartet eine "
|
||||
"direkt folgende Sektion mit "
|
||||
"glied=\"ES\"" % self.wo)
|
||||
self.raus("STR", str(self.feld(naechste, "winkel")))
|
||||
self.raus("STR", code_aus_wert(SEITE,
|
||||
self.feld(naechste, "seite"),
|
||||
"seite (ES, vorgezogen)"))
|
||||
self.es_verbraucht = True
|
||||
if modus == "steigend":
|
||||
smodus = (self.feld(sek, "steigend_modus", pflicht=False)
|
||||
or "endpunkt")
|
||||
@@ -440,8 +485,6 @@ class SpecSchreiber(object):
|
||||
self.raus("STR", code_aus_wert(GEFAELLE, art, "gefaelle"))
|
||||
self.zahl("REAL", self.feld(sek, "winkel" if art == "winkel"
|
||||
else "hoehe"))
|
||||
self.raus("STR", code_aus_wert(ENDE3, self.feld(sek, "ende"),
|
||||
"ende"))
|
||||
elif label == "Linie-VF":
|
||||
self.linie(sek)
|
||||
self.zahl("REAL", self.feld(sek, "hoehe"))
|
||||
@@ -529,6 +572,7 @@ class SpecSchreiber(object):
|
||||
self.linie(sub)
|
||||
self.zahl("REAL", self.feld(sub, "hoehe"))
|
||||
self.winkel_idx(sub)
|
||||
self.gf2_bestaetigung(sub)
|
||||
else:
|
||||
if letzter:
|
||||
raise ValueError("%s: VF-Einheit endet mit sub %r - der "
|
||||
@@ -551,8 +595,20 @@ class SpecSchreiber(object):
|
||||
"variante"))
|
||||
else: # auf-ab
|
||||
self.linie(sub)
|
||||
self.zahl("REAL", self.feld(sub, "hoehe"))
|
||||
self.winkel_idx(sub)
|
||||
# Zwei Auspraegungen, per Feldpraesenz unterschieden (kein
|
||||
# separates Modus-Flag - "richtung" IST die Aussage, siehe
|
||||
# vsp-vf-einheit in Lisp/vf_spec.lsp): kommt die Kette
|
||||
# bereits FLACH an, fragt vf-linienzug-modus die Richtung
|
||||
# (Auf/Ab) + waehlt den Winkel aus einer Kandidatentabelle
|
||||
# statt einer direkten Zielhoehe.
|
||||
if "richtung" in sub:
|
||||
self.raus("STR", code_aus_wert(RICHTUNG,
|
||||
self.feld(sub, "richtung"),
|
||||
"richtung"))
|
||||
self.winkel_idx(sub)
|
||||
else:
|
||||
self.zahl("REAL", self.feld(sub, "hoehe"))
|
||||
self.winkel_idx(sub)
|
||||
ende = self.feld(vf, "ende")
|
||||
if ende in ("zielpunkt-ohne-es", "automatisch"):
|
||||
return # keine Frage
|
||||
|
||||
Reference in New Issue
Block a user