diff --git a/src/reynir/Greynir.grammar b/src/reynir/Greynir.grammar index f83ec5d3..3286b9d8 100644 --- a/src/reynir/Greynir.grammar +++ b/src/reynir/Greynir.grammar @@ -1662,7 +1662,7 @@ SetningÁnF_et_p3_hk → EinSetningÁnF/tala/pers/kyn → Atviksliður? ÖfugurSagnliður/tala/pers/kyn - LoTengtSögn_nf/tala/kyn? ÖfugtFramhald/tala/pers/kyn* + LoTengtSögn_nf/tala/kyn? SamanburðurNl? ÖfugtFramhald/tala/pers/kyn* # Lýsingarorð á undan sögn: # (Ráðherrar taka þær ákvarðanir sem) nauðsynlegar eru/þykja/teljast til rækslu starfa þeirra | LoTengtSögn_nf/tala/kyn Sögn_0/tala/pers/kyn SagnInnskotAtv? @@ -2796,6 +2796,12 @@ SagnarBotn → | Nl_ef AtvFs? so_subj_nh_ef # (Anna telur) fólki fækka í landinu | Nl_þgf AtvFs? so_subj_nh_þgf + # (Hún segir) mig hafa dreymt (vel) + | Nl/aukafall AtvFs? 'hafa:so'_0_nh so_subj_sagnb/aukafall ÓpSagnarBotn? + # (Hún segir) Spáni hafa mistekist (að vernda landamærin) + # (miðmyndarsögn með frumlagi í þágufalli, sbr. finítu regluna + # 'NlFrumlag_þgf ... HjSögnMM so_mm_sagnb MiðmyndarBotn?') + | Nl_þgf AtvFs? 'hafa:so'_0_nh so_mm_sagnb MiðmyndarBotn? # (Már segir) [í grundvallaratriðum] gæta misskilnings (hjá Hannesi) | FsAtv? so_subj_nh_ef Nl_ef # !!! Ekki virðist þörf fyrir öfuga orðaröð í þgf eða þf @@ -3743,8 +3749,17 @@ Fyrirbæri_ft/fall/kyn → Númer → 'númer:hk' Tala_hk_nf + | NúmerTala + +# Óbeygð tala í hvorugkyni nefnifalls sem auðkenni á eftir nafnorði: +# '(lögreglu)stöð tvö', 'stofa fimm', 'hlið átta', 'rás eitt' +NúmerTala → + Töluorð_et_nf_hk + | Töluorð_ft_nf_hk $score(+8) Númer +# Velja frekar sambeygða tölu (Fjöldi) en auðkennistölu, ef báðar koma til greina +$score(-4) NúmerTala Fjöldi_ft/fall/kyn → to_ft/fall/kyn | töl_ft/fall/kyn @@ -3893,6 +3908,7 @@ NlStak_p3/tala/fall/kyn → NlKjarni/tala/fall/kyn NlStakTagl/tala/fall/kyn? | NlFornafn/tala/fall/kyn | Pfn/kyn/tala/fall to/tala/fall/kyn? 'persónulega:ao'? # 'þið tvær', 'hann einn', 'hán eitt' + | Pfn/kyn/tala/fall LoEftirNl/tala/fall/kyn # '(með) hann fremstan (í fararbroddi)', '(ég hitti) hana glaða' | LoTöluorð_ef 'konar:kk'_ef_et NlKjarni/tala/fall/kyn # 'tvenns/þrenns/ferns konar innstungur' | Raðnr/fall/kyn NlKjarni/tala/fall/kyn Fornafn/tala/fall/kyn* # 1. verðlaunin öll, 1.-3. sætið, 10. og 12. árið | NlStakNema/tala/fall/kyn @@ -5954,6 +5970,7 @@ FsMeðFallstjórn → # Fleiryrtar forsetningar sem eru ekki dregnar saman í Phrases.conf | 'nær:fs'_þgf 'draga:so'_op_gm_et_p3 Nl_þgf # 'þegar nær dró/dregur jólum' | 'beggja:fn'_hk_ef_ft 'vegna:fs'_ef Nl_ef # 'beggja vegna vegarins' + | FsVegnaEftirNl # 'sýningarinnar vegna', 'mín vegna' | 'bera:so'_lhþt_sb_hk_nf_et "saman:ao" 'við:fs'_þf Nl_þf # borið saman við (fyrra ár) | "fyrir:ao" "nú:ao"? 'utan:fs'_þf Nl_þf | "innan:ao" "sem:st" 'utan:fs'_ef Nl_ef @@ -5993,6 +6010,14 @@ FsMeðFallstjórn → $score(+1) FsMeðFallstjórn +# Eignarfallsliður á undan 'vegna': 'sýningarinnar vegna', +# 'veðursins vegna', 'mín vegna' +FsVegnaEftirNl → + Nl_ef 'vegna:fs'_ef + +# Þarf að vinna gegn rangri greiningu á 'vegna' sem lýsingarorði +$score(+4) FsVegnaEftirNl + MiðaðVið → "miðað:so" 'við:fs'_þf | 'miðað_við:fs'_þf # Samtals eigið fé m.v. síðustu tölur ... @@ -7671,6 +7696,30 @@ VillaEndingIR → $tag(error) VillaEndingIR $score(-999) VillaEndingIR +# Regla fyrir villu þar sem lýsingarorð (oft lýsingarháttur þátíðar) +# með fallstýrðu andlagi stendur á eftir nafnlið en sambeygist honum +# ekki í falli, t.d. 'móðurfélag fleiri félaga tengdum rekstri +# Morgunblaðsins' (rétt: 'tengdra'); villan stafar oft af aðlöðun +# að falli andlagsins ('félögum tengdum rekstri') + +LoEftirNl/tala/fall/kyn → + > VillaLoEftirNlMeðAndlagi/fall/tala/kyn + +VillaLoEftirNlMeðAndlagi_nf/tala/kyn → + LoEftirNlMeðAndlagi/fallxnf/tala/kyn + +VillaLoEftirNlMeðAndlagi_þf/tala/kyn → + LoEftirNlMeðAndlagi/fallxþf/tala/kyn + +VillaLoEftirNlMeðAndlagi_þgf/tala/kyn → + LoEftirNlMeðAndlagi/fallxþgf/tala/kyn + +VillaLoEftirNlMeðAndlagi_ef/tala/kyn → + LoEftirNlMeðAndlagi/fallxef/tala/kyn + +$tag(error) VillaLoEftirNlMeðAndlagi/fall/tala/kyn +$score(-999) VillaLoEftirNlMeðAndlagi/fall/tala/kyn + # Orð sem endar á -ana í þolfalli notað í stað eignarfalls: # 'niðurstaða þingflokkana', 'hattar karlana' diff --git a/src/reynir/bintokenizer.py b/src/reynir/bintokenizer.py index 88cd2260..3a9fbcb0 100644 --- a/src/reynir/bintokenizer.py +++ b/src/reynir/bintokenizer.py @@ -1259,6 +1259,24 @@ def _compatible(pn: PersonNameTuple, npn: PersonNameTuple) -> bool: return True +def _add_uninflected_genitive(gn: List[PersonNameTuple]) -> List[PersonNameTuple]: + """If a name appears in the same form in the nominative, accusative + and dative cases, it is a non-inflecting (foreign) name. Such names + are also used unmodified in genitive position ('Samtök Paul Watson', + 'bók Mary Robinson'), so we add the genitive case to the list of + possible interpretations.""" + seen: Dict[Tuple[str, Optional[str]], Set[str]] = defaultdict(set) + for pn in gn: + if pn.case: + seen[(pn.name, pn.gender)].add(pn.case) + additions = [ + PersonNameTuple(name=name, gender=gender, case="ef") + for (name, gender), cases in seen.items() + if "ef" not in cases and cases >= {"nf", "þf", "þgf"} + ] + return gn + additions if additions else gn + + def parse_phrases_2( token_stream: TokenIterator, token_ctor: TokenConstructor, auto_uppercase: bool ) -> TokenIterator: @@ -1604,7 +1622,7 @@ def eat_surnames( if not weak: # Return a person token with the accumulated name # and the intersected set of possible cases - token = token_ctor.Person(w, gn) + token = token_ctor.Person(w, _add_uninflected_genitive(gn)) token.original = namespan # Yield the current token and advance to the lookahead diff --git a/src/reynir/config/Phrases.conf b/src/reynir/config/Phrases.conf index cefaa649..deb91f6e 100644 --- a/src/reynir/config/Phrases.conf +++ b/src/reynir/config/Phrases.conf @@ -1573,7 +1573,7 @@ meaning = fs frasi - "láta* slag standa" so kk so "láta* þess getið" so pfn so "vísa* málinu" so hk -"vera* bent á" so so ao +"vera* bent á" so so fs/ao "um síðustu áramót" fs lo hk "um síðustu mánaðamót" fs lo hk "um sig" fs * diff --git a/test/test_parse.py b/test/test_parse.py index 707d33c9..d726553a 100644 --- a/test/test_parse.py +++ b/test/test_parse.py @@ -2245,6 +2245,178 @@ def test_aukafall(r): assert s and s.tree +def test_impersonal_passive_prep(r): + """The 'vera* bent á' ambiguous phrase must retain the preposition + meaning of 'á', so that 'er bent á' + Nl parses""" + s = r.parse_single("Í skýrslunni er bent á kostina.") + assert s and s.tree + assert ( + s.tree.flat_with_all_variants + == "S0 S-MAIN IP PP P fs_þgf /P NP no_et_gr_kvk_þgf /NP /PP " + "VP VP-AUX so_et_fh_gm_nt_p3 /VP-AUX VP so_gm_sagnb /VP " + "PP P fs_þf /P NP no_ft_gr_kk_þf /NP /PP /VP /IP /S-MAIN p /S0" + ) + s = r.parse_single("Í skýrslunni er bent á kosti og galla upptöku evru.") + assert s and s.tree + # 'bent á' followed by a subordinate clause must still work + s = r.parse_single("Í skýrslunni er bent á að vandinn sé mikill.") + assert s and s.tree + s = r.parse_single( + "Í nýrri ársskýrslu sjóðsins um Ísland er bent á kosti og galla " + "upptöku evru og varað við því að verðtrygging í íslensku " + "efnahagslífi geti viðhaldið verðbólgu." + ) + assert s and s.tree + + +def test_uninflected_genitive_names(r): + """Non-inflecting (foreign) person names, i.e. names having identical + forms in nf/þf/þgf, should also be accepted in genitive contexts""" + s = r.parse_single("Samtök Paul Watson mótmæltu hvalveiðum.") + assert s and s.tree + assert ( + s.tree.flat_with_all_variants + == "S0 S-MAIN IP NP-SUBJ no_ft_hk_nf NP-POSS person_ef_kk person_ef_kk " + "/NP-POSS /NP-SUBJ VP VP so_1_þgf_fh_ft_gm_p3_þt /VP " + "NP-OBJ no_ft_kvk_þgf /NP-OBJ /VP /IP /S-MAIN p /S0" + ) + s = r.parse_single("Ég las bók John Lennon.") + assert s and s.tree + s = r.parse_single( + "Samtök Paul Watson leiða líkur að því að þau hafi með aðgerðum " + "sínum frá því að hvalveiðitímabilið hófst bjargað lífi " + "allt að þrjátíu langreyða." + ) + assert s and s.tree + # Inflecting Icelandic names must not gain a genitive interpretation + s = r.parse_single("Samtök Guðmundur Jónsson eru góð.") + assert s.tree is None + # Token-level check of person name cases + from reynir.bintokenizer import tokenize + + toks = [t for t in tokenize("Samtök Paul Watson mótmæltu hvalveiðum.")] + pw = next(t for t in toks if t.txt == "Paul Watson") + assert set(pn.case for pn in pw.person_names) == {"nf", "þf", "þgf", "ef"} + toks = [t for t in tokenize("Þarna gengur Jón.")] + jon = next(t for t in toks if t.txt == "Jón") + assert "ef" not in set(pn.case for pn in jon.person_names) + + +def test_sem_role_relative_clause(r): + """A 'sem' + Nl_nf role complement ('starfar sem sérfræðingur') + must be allowed inside a subject-gap relative clause""" + s = r.parse_single("Konan, sem starfar sem sérfræðingur hjá bankanum, er glöð.") + assert s and s.tree + assert ( + s.tree.flat_with_all_variants + == "S0 S-MAIN IP NP-SUBJ no_et_gr_kvk_nf p CP-REL C stt /C " + "IP VP so_0_et_fh_gm_nt_p3 /VP CP-ADV-CMP C st /C NP no_et_kk_nf " + "PP P fs_þgf /P NP no_et_gr_kk_þgf /NP /PP /NP /CP-ADV-CMP /IP " + "/CP-REL p /NP-SUBJ VP VP-AUX so_et_fh_gm_nt_p3 /VP-AUX " + "NP-PRD lo_et_kvk_nf_sb /NP-PRD /VP /IP /S-MAIN p /S0" + ) + s = r.parse_single( + "Doktor Stefán Ólafsson, sem starfar sem sérfræðingur hjá Eflingu, " + "gefur ekki mikið fyrir myljandi hagnað bankanna." + ) + assert s and s.tree + + +def test_oblique_subject_infinitive(r): + """Oblique subject + perfect infinitive ('hafa' + supine) in the + complement of segja/telja: 'segir Spáni hafa mistekist'""" + s = r.parse_single("Hún segir Spáni hafa mistekist að vernda landamærin.") + assert s and s.tree + assert ( + s.tree.flat_with_all_variants + == "S0 S-MAIN IP NP-SUBJ pfn_et_kvk_nf_p3 /NP-SUBJ VP VP " + "so_1_þf_et_fh_gm_nt_p3 /VP IP NP no_et_kk_þgf /NP VP so_0_gm_nh /VP " + "VP so_mm_sagnb /VP IP-INF TO nhm /TO VP VP so_1_þf_gm_nh /VP " + "NP-OBJ no_ft_gr_hk_þf /NP-OBJ /VP /IP-INF /IP /VP /IP /S-MAIN p /S0" + ) + s = r.parse_single("Hún segir mig hafa dreymt vel.") + assert s and s.tree + s = r.parse_single("Hún telur Spáni hafa mistekist.") + assert s and s.tree + s = r.parse_single( + "Hún segir Spáni hafa mistekist að vernda ytri landamæri " + "Schengen-svæðisins gegn innrás." + ) + assert s and s.tree + + +def test_postposed_vegna(r): + """A genitive Nl preceding 'vegna' ('sýningarinnar vegna') + should parse as a prepositional phrase""" + s = r.parse_single("Sýningarinnar vegna ákvað hann þetta.") + assert s and s.tree + assert ( + s.tree.flat_with_all_variants + == "S0 S-MAIN IP PP NP no_ef_et_gr_kvk /NP P fs_ef /P /PP " + "VP VP so_1_þf_et_fh_gm_p3_þt /VP NP-SUBJ pfn_et_kk_nf_p3 /NP-SUBJ " + "NP-OBJ fn_et_hk_þf /NP-OBJ /VP /IP /S-MAIN p /S0" + ) + s = r.parse_single("Hann gerði þetta mín vegna.") + assert s and s.tree + s = r.parse_single("Hann gerði þetta veðursins vegna.") + assert s and s.tree + # Normal (preposed) order must be unaffected + s = r.parse_single("Hann gerði þetta vegna veðursins.") + assert s and s.tree + s = r.parse_single("Trén standa beggja vegna vegarins.") + assert s and s.tree + s = r.parse_single( + "Umboðsmaður Boy George sagði að sýningarinnar vegna hefði hann " + "ákveðið að söngvarinn yrði ekki með í henni." + ) + assert s and s.tree + + +def test_pronoun_postposed_adjective(r): + """A personal pronoun followed by an agreeing adjective phrase + should form a noun phrase: '(með) hann fremstan (í fararbroddi)'""" + s = r.parse_single("Með hann fremstan í fararbroddi vann liðið leikinn.") + assert s and s.tree + assert ( + s.tree.flat_with_all_variants + == "S0 S-MAIN IP PP P fs_þf /P NP pfn_et_kk_p3_þf lo_esb_et_kk_þf " + "PP P fs_þgf /P NP no_et_kk_þgf /NP /PP /NP /PP " + "VP VP so_1_þf_et_fh_gm_p3_þt /VP NP-SUBJ no_et_gr_hk_nf /NP-SUBJ " + "NP-OBJ no_et_gr_kk_þf /NP-OBJ /VP /IP /S-MAIN p /S0" + ) + s = r.parse_single("Ég hitti hana glaða.") + assert s and s.tree + s = r.parse_single( + "Með hann fremstan í fararbroddi tókst liði Levante " + "að bjarga sér frá falli." + ) + assert s and s.tree + + +def test_designation_numeral(r) -> None: + """An uninflected neuter nominative numeral used as a designation + after a noun: '(lögreglu)stöð tvö', 'stofa fimm'""" + s = r.parse_single("Hann býr í umdæmi lögreglustöðvar tvö.") + assert s and s.tree + assert ( + s.tree.flat_with_all_variants + == "S0 S-MAIN IP NP-SUBJ pfn_et_kk_nf_p3 /NP-SUBJ VP VP so_0_et_fh_gm_nt_p3 /VP " + "PP P fs_þgf /P NP no_et_hk_þgf NP-POSS no_ef_et_kvk to_ft_hk_nf " + "/NP-POSS /NP /PP /VP /IP /S-MAIN p /S0" + ) + s = r.parse_single("Hann býr í stofu fimm.") + assert s and s.tree + s = r.parse_single( + "Það átti sér stað í umdæmi lögreglustöðvar tvö, " + "sem sér um Hafnarfjörð og Garðabæ." + ) + assert s and s.tree + # An agreeing numeral must still be preferred where possible + s = r.parse_single("Ég sá mennina þrjá.") + assert s and s.tree + assert "to_ft_kk_þf" in s.tree.flat_with_all_variants + + if __name__ == "__main__": # When invoked as a main module, do a verbose test from reynir import Greynir @@ -2296,4 +2468,11 @@ def test_aukafall(r): print(e) test_foreign(g) test_aukafall(g) + test_impersonal_passive_prep(g) + test_uninflected_genitive_names(g) + test_sem_role_relative_clause(g) + test_oblique_subject_infinitive(g) + test_postposed_vegna(g) + test_pronoun_postposed_adjective(g) + test_designation_numeral(g) g.__class__.cleanup()