From 52303b90b2c7fc2d367f9154c0a069911adf5c48 Mon Sep 17 00:00:00 2001 From: Gulum Date: Sat, 6 Jun 2026 17:42:30 +0200 Subject: [PATCH] =?UTF-8?q?CR-1a:=20ee[-]=20Parser-Fix=20(Silvain)=20?= =?UTF-8?q?=E2=80=94=20lookbehind=20trennt=20e[-]=20vom=20f=C3=BChrenden?= =?UTF-8?q?=20Allel?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit normalizeToken: lookbehind-Regex /(?<=[A-Za-z])e\[-\]/→'?' greift wenn e[-] von einem Buchstaben (erstes Allel) gefolgt wird (ee[-] → e?); standalone e[-] fällt durch auf die generische \[-\]→? Regel (→ e?). Beide Pfade liefern splitToken 2 Allele [e,?]. Vorher: ee[-]→ee?→ 3 Allele → Fehler. Fixture: Silvain 'aa c[chm]c[chm] Dd ee[-] Gg Pp Spsp' → E=[e,?] ✓. Gate: build ✓ eslint ✓ vitest 92/92 ✓ e2e 126/126 ✓ --- .../src/genetics/__tests__/genetics.test.ts | 17 +++++++++++++---- gerbil-manager-web/src/genetics/genotype.ts | 12 +++++++++--- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/gerbil-manager-web/src/genetics/__tests__/genetics.test.ts b/gerbil-manager-web/src/genetics/__tests__/genetics.test.ts index b7bf3bd..2a2aa22 100644 --- a/gerbil-manager-web/src/genetics/__tests__/genetics.test.ts +++ b/gerbil-manager-web/src/genetics/__tests__/genetics.test.ts @@ -602,12 +602,21 @@ describe('GEN-3h: breeder bracket-notation display + E-locus e-before-ef order', ) }) - it('[-] bracket-unknown parses as wildcard (e[-] → E=[e,?], displays e-)', () => { - // NOTE: oracle for Silvain shows "ee[-]" which contains 3 E-allele tokens - // (e + e + [-]) and cannot be parsed. Flagged to god — see done-report. - // This test documents what [e,?] at E produces: "e-". + it('e[-] standalone: parses as [e,?], displays e-', () => { const g = fromDisplayString('aa CC DD e[-] GG PP spsp rere') expect(g.E).toEqual(['e', '?']) expect(toDisplayString(g)).toBe('aa CC DD e- GG PP spsp rere') }) + + it('CR-1a: Silvain oracle ee[-] parses without crash → [e,?], displays e-', () => { + // Real herdbook notation: ee[-] = fox allele e + unknown e-type second allele. + // The lookbehind rule strips the second e[-] → '?', leaving 'e?' for splitToken. + const input = 'aa c[chm]c[chm] Dd ee[-] Gg Pp Spsp' + const g = fromDisplayString(input) + expect(g.E).toEqual(['e', '?']) + expect(g.C).toEqual(['cchm', 'cchm']) + expect(g.D).toEqual(['D', 'd']) + expect(g.Sp).toEqual(['Sp', 'sp']) + expect(toDisplayString(g)).toBe('aa c[chm]c[chm] Dd e- Gg Pp Spsp rere') + }) }) diff --git a/gerbil-manager-web/src/genetics/genotype.ts b/gerbil-manager-web/src/genetics/genotype.ts index 807c2f8..5a54606 100644 --- a/gerbil-manager-web/src/genetics/genotype.ts +++ b/gerbil-manager-web/src/genetics/genotype.ts @@ -179,11 +179,17 @@ function normalizeToken(tok: string): string | null { t = t.replace(/S\(l\)/g, 'Sl').replace(/s\(l\)/g, 'sl') t = t.replace(/Uw/g, 'G').replace(/uw/g, 'g') // GEN-3h: accept bracket display notation → canonical internal symbols. - // Order matters: [-] must be replaced before the plain-dash rule below. - t = t.replace(/\[-\]/g, '?') // bracket-unknown [-] → internal wildcard - t = t.replace(/e\[f\]/g, 'ef') // Schimmel display form → internal + t = t.replace(/e\[f\]/g, 'ef') // Schimmel allele display form → internal t = t.replace(/c\[chm\]/g, 'cchm') // Colourpoint display form → internal t = t.replace(/c\[h\]/g, 'ch') // Himalayan display form → internal + // CR-1a: allele-prefixed bracket-unknown like ee[-] (Silvain). + // When e[-] is PRECEDED by a letter it is the second unknown allele in a + // 2-allele token (e.g. ee[-] → e + e[-] → e + ?). Lookbehind strips only + // the e[-] part; the leading allele stays. Standalone e[-] falls through to + // the generic [-]→? rule below (which makes the bracket-dash a wildcard, + // leaving the leading allele intact for splitToken). + t = t.replace(/(?<=[A-Za-z])e\[-\]/g, '?') + t = t.replace(/\[-\]/g, '?') // bare/standalone bracket-unknown → wildcard // GEN-3c: plain dash is the breeder's UNKNOWN marker on input; store internally as '?'. t = t.replace(/-/g, '?') return t