From 2e6a750c1a4fbd69a26bb6c5007c52182f359983 Mon Sep 17 00:00:00 2001 From: aarneranta Date: Fri, 10 Jul 2026 11:28:00 +0200 Subject: [PATCH 01/16] some fixes in ExtendFin to make it compile again --- src/finnish/ExtendFin.gf | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/finnish/ExtendFin.gf b/src/finnish/ExtendFin.gf index 47d0c18f..b5309396 100644 --- a/src/finnish/ExtendFin.gf +++ b/src/finnish/ExtendFin.gf @@ -446,8 +446,9 @@ oper vp.s.s ! vform ++ vp.ext ; + rnp2np : Agr -> RNP -> NP ; rnp2np agr rnp = { - s = \\npf => rnp.s ! agr ! npf ; + s = rnp.s ! agr ; a = agr ; isPron = rnp.isPron ; isNeg = rnp.isNeg From b14c0f849279e59d67be38f687e1964f9516fce5 Mon Sep 17 00:00:00 2001 From: aarneranta Date: Fri, 10 Jul 2026 12:29:30 +0200 Subject: [PATCH 02/16] complete the Czech Symbol module MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement the remaining Symbol functions, which previously fell back to GF's default linearization: IntPN, FloatPN, NumPN, CNNumNP, CNIntNP, CNSymbNP, SymbS, SymbNum, SymbOrd, and the [Symb] list. Symbols and numbers are indeclinable neuters, but a cardinal used as a name still declines (NumPN). CNNumNP treats the numeral as an invariable label, so the noun carries the case: "město / městu / městem pět". CNSymbNP goes through numSizeForm and numSizeAgr, as DetCN does, so SymbNum's Num5 size gives "n měst x a y je staré". SymbOrd is only approximate: CatCze has no lincat Ord, so it defaults to {s : Str} and the masculine "n-tý" cannot agree. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/czech/SymbolCze.gf | 43 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/src/czech/SymbolCze.gf b/src/czech/SymbolCze.gf index 7cef10b4..a64c3a76 100644 --- a/src/czech/SymbolCze.gf +++ b/src/czech/SymbolCze.gf @@ -3,9 +3,48 @@ concrete SymbolCze of Symbol = CatCze ** open Prelude, ResCze in { lincat - Symb = {s : Str} ; + Symb, [Symb] = SS ; + lin MkSymb s = s ; - SymbPN s = lin PN {s = \\_ => s.s ; g = Neutr} ; + + BaseSymb = infixSS "a" ; + ConsSymb = infixSS bindComma ; + + SymbPN s = symbolPN s.s ; + IntPN i = symbolPN i.s ; + FloatPN f = symbolPN f.s ; + + -- unlike a bare symbol, a cardinal used as a name still declines + NumPN card = lin PN {s = \\c => card.s ! Neutr ! c ; g = Neutr} ; + + -- the numeral is an invariable label: "úroveň pět", "na úrovni pět" + CNNumNP cn card = { + s,clit,prep = \\c => cn.s ! Sg ! c ++ card.s ! cn.g ! Nom ; + a = Ag cn.g Sg P3 ; + hasClit = False ; + } ; + + CNIntNP cn i = { + s,clit,prep = \\c => cn.s ! Sg ! c ++ i.s ; + a = Ag cn.g Sg P3 ; + hasClit = False ; + } ; + + -- as DetCN in NounCze, with the symbols in apposition + CNSymbNP det cn xs = { + s,clit,prep = \\c => det.s ! cn.g ! c ++ numSizeForm cn.s det.size c ++ xs.s ; + a = numSizeAgr cn.g det.size P3 ; + hasClit = False ; + } ; + + SymbS sy = sy ; + + SymbNum sy = {s = \\_,_ => sy.s ; size = Num5} ; -- "n čísel", like numerals from 5 up + SymbOrd sy = {s = glue sy.s "-tý"} ; ---- Ord is still an uninflected string + +oper + symbolPN : Str -> PN + = \s -> lin PN {s = \\_ => s ; g = Neutr} ; } From 8f1b3eeece8f8ebbc5d513dd57e04d97ed5e0a43 Mon Sep 17 00:00:00 2001 From: aarneranta Date: Fri, 10 Jul 2026 14:08:24 +0200 Subject: [PATCH 03/16] compiling SymbolicCze by default --- languages.csv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/languages.csv b/languages.csv index e5adb275..e4c52598 100644 --- a/languages.csv +++ b/languages.csv @@ -7,7 +7,7 @@ Bul,Bulgarian,bulgarian,,,y,,,,,y,n Cat,Catalan,catalan,Romance,,y,,,,y,y,n Cgg,Rukiga,rukiga,,,y,y,n,n,y,y,n Chi,Chinese (simplified),chinese,,,,,,,,y,y -Cze,Czech,czech,,,,,,n,,y,n +Cze,Czech,czech,,,,,,y,,y,n Dan,Danish,danish,Scand,,y,,,,,y,n Dut,Dutch,dutch,,,y,,,,,y,n Eng,English,english,,,y,,,,y,y,y From e866940d65847afe7cea36d87a71feb1b07cab71 Mon Sep 17 00:00:00 2001 From: aarneranta Date: Fri, 10 Jul 2026 14:16:34 +0200 Subject: [PATCH 04/16] add the Czech Markup module MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MarkupNP marks s, clit and prep, since in Czech these are alternative surface forms of a pronoun chosen by context, as in MarkupRomance. The nominative clitic is left alone: it is the pro-drop subject, empty for every personal pronoun, so marking it up gave " je starý". Inherit MarkupCze in LangCze, as LangFin does; stringMark is excluded because abstract Lang already excludes it. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/czech/LangCze.gf | 3 ++- src/czech/MarkupCze.gf | 27 +++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 src/czech/MarkupCze.gf diff --git a/src/czech/LangCze.gf b/src/czech/LangCze.gf index 513fa955..dcd92da6 100644 --- a/src/czech/LangCze.gf +++ b/src/czech/LangCze.gf @@ -1,10 +1,11 @@ --# -path=.:../abstract:../common:../api -concrete LangCze of Lang = +concrete LangCze of Lang = GrammarCze, LexiconCze -- ,ConstructionCze -- ,DocumentationCze --# notpresent + ,MarkupCze - [stringMark] ** { } diff --git a/src/czech/MarkupCze.gf b/src/czech/MarkupCze.gf new file mode 100644 index 00000000..93843cd9 --- /dev/null +++ b/src/czech/MarkupCze.gf @@ -0,0 +1,27 @@ +--# -path=.:../abstract:../common + +concrete MarkupCze of Markup = CatCze, MarkHTMLX ** open ResCze in { + +lin + MarkupCN m cn = cn ** {s = \\n,c => appMark m (cn.s ! n ! c)} ; + + -- s, clit and prep are alternative surface forms, so each is marked; + -- but clit ! Nom is the pro-drop subject, empty for every pronoun, + -- and marking it up would leave the tags around nothing + MarkupNP m np = np ** { + s = \\c => appMark m (np.s ! c) ; + clit = \\c => case c of { + Nom => np.clit ! Nom ; + _ => appMark m (np.clit ! c) + } ; + prep = \\c => appMark m (np.prep ! c) + } ; + + MarkupAP m ap = ap ** {s = \\g,n,c => appMark m (ap.s ! g ! n ! c)} ; + MarkupAdv m adv = {s = appMark m adv.s} ; + MarkupS m s = {s = appMark m s.s} ; + MarkupUtt m utt = {s = appMark m utt.s} ; + MarkupPhr m phr = {s = appMark m phr.s} ; + MarkupText m txt = {s = appMark m txt.s} ; + +} From 8a82c2ca538e7dfed4b3eff741bf2e345229be99 Mon Sep 17 00:00:00 2001 From: aarneranta Date: Fri, 10 Jul 2026 14:56:08 +0200 Subject: [PATCH 05/16] added Cze.UttVP --- src/czech/PhraseCze.gf | 1 + 1 file changed, 1 insertion(+) diff --git a/src/czech/PhraseCze.gf b/src/czech/PhraseCze.gf index cc82fefb..c1a0fda6 100644 --- a/src/czech/PhraseCze.gf +++ b/src/czech/PhraseCze.gf @@ -6,6 +6,7 @@ lin UttCN cn = {s = cn.s ! Sg ! Nom} ; UttAP ap = {s = ap.s ! Masc Anim ! Sg ! Nom} ; UttNP np = {s = np.s ! Nom} ; + UttVP vp = let agr = Ag Neutr Sg P3 in {s = vp.clit ! agr ++ vp.verb.inf ++ vp.compl ! agr} ; PhrUtt pconj utt voc = {s = pconj.s ++ utt.s ++ voc.s} ; From a79202f667d6b76838356328536f6c0ba10dd79e Mon Sep 17 00:00:00 2001 From: aarneranta Date: Fri, 10 Jul 2026 15:29:55 +0200 Subject: [PATCH 06/16] czech: fill in the lins needed to build an application grammar MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Czech resource was too incomplete to compile a Syntax/Grammar client: Sentence, Idiom and Question defined almost nothing, and many Structural words were absent, so MissingCze's notYet stubs were reached at PMCFG generation time. Added: CatCze lincat VV SentenceCze AdvS, ExtAdvS, SSubjS, EmbedS, EmbedQS, ImpVP IdiomCze ImpP3, ExistNP, ImpersCl, GenericCl VerbCze CompCN, ComplVS, ComplVV, PassV2 NounCze SentCN, PredetNP AdverbCze SubjS QuestionCze QuestIAdv PhraseCze UttImpSg, UttImpPl, UttImpPol StructuralCze all_Predet, both7and_DConj, between_Prep, by8means_Prep, can_VV, either7or_DConj, every_Det, if_Subj, no_Quant, on_Prep, someSg_Det, that_Subj, under_Prep, where_IAdv ExtendCze ExistsNP no longer excluded Three of these approximate, because VerbForms lacks the required forms: ImpVP uses the 1st person plural present ("předpokládáme, že ...") as there is no imperative; PassV2 uses the reflexive passive ("číslo se dělí") as passpart is commented out in ResCze; ImpP3 uses "nechť", which suits mathematical text more than the "let John walk" of the RGL example. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/czech/AdverbCze.gf | 4 ++++ src/czech/CatCze.gf | 2 +- src/czech/ExtendCze.gf | 1 - src/czech/IdiomCze.gf | 27 +++++++++++++++++++++++++++ src/czech/MissingCze.gf | 2 -- src/czech/NounCze.gf | 7 +++++++ src/czech/PhraseCze.gf | 4 ++++ src/czech/QuestionCze.gf | 2 ++ src/czech/SentenceCze.gf | 21 ++++++++++++++++++++- src/czech/StructuralCze.gf | 31 +++++++++++++++++++++++++++++++ src/czech/VerbCze.gf | 26 ++++++++++++++++++++++++++ 11 files changed, 122 insertions(+), 5 deletions(-) diff --git a/src/czech/AdverbCze.gf b/src/czech/AdverbCze.gf index a441d2cf..b6b18631 100644 --- a/src/czech/AdverbCze.gf +++ b/src/czech/AdverbCze.gf @@ -6,4 +6,8 @@ lin s = prep.s ++ np.prep ! prep.c } ; + SubjS subj s = { + s = subj.s ++ s.s + } ; + } diff --git a/src/czech/CatCze.gf b/src/czech/CatCze.gf index 04166930..d7d42239 100644 --- a/src/czech/CatCze.gf +++ b/src/czech/CatCze.gf @@ -24,7 +24,7 @@ concrete CatCze of Cat = VPSlash = {verb : VerbForms ; clit,compl : Agr => Str ; c : ComplementCase} ; ---- V = ResCze.VerbForms ; V2 = ResCze.VerbForms ** {c : ComplementCase} ; - VS,VQ = ResCze.VerbForms ; + VS,VQ,VV = ResCze.VerbForms ; A = ResCze.AdjForms ; AP = ResCze.Adjective ** {isPost : Bool} ; -- {s : Gender => Number => Case => Str} diff --git a/src/czech/ExtendCze.gf b/src/czech/ExtendCze.gf index 9d094ebc..97b9db43 100644 --- a/src/czech/ExtendCze.gf +++ b/src/czech/ExtendCze.gf @@ -12,7 +12,6 @@ concrete ExtendCze of Extend = CatCze ** ,SlashBareV2S ,PredIAdvVP ,PredAPVP - ,ExistsNP ,ExistS ,ExistPluralCN ,ExistNPQS diff --git a/src/czech/IdiomCze.gf b/src/czech/IdiomCze.gf index d4314dcc..b7d61c65 100644 --- a/src/czech/IdiomCze.gf +++ b/src/czech/IdiomCze.gf @@ -1,5 +1,32 @@ concrete IdiomCze of Idiom = CatCze ** open Prelude, ResCze in { +lin + ImpP3 np vp = { + s = "nechť" ++ np.s ! Nom ++ vp.clit ! np.a ++ + verbAgr vp.verb np.a True ++ vp.compl ! np.a + } ; + ImpersCl vp = let agr = Ag Neutr Sg P3 in { + subj = [] ; + clit = vp.clit ! agr ; + compl = vp.compl ! agr ; + verb = vp.verb ; + a = agr + } ; + + GenericCl vp = let agr = Ag (Masc Anim) Pl P3 in { + subj = [] ; + clit = vp.clit ! agr ; + compl = vp.compl ! agr ; + verb = vp.verb ; + a = agr + } ; + + ExistNP np = { + subj, clit = [] ; + compl = np.s ! Nom ; + verb = iii_kupovatVerbForms "existovat" ; + a = np.a + } ; } diff --git a/src/czech/MissingCze.gf b/src/czech/MissingCze.gf index de1fb188..02ce581c 100644 --- a/src/czech/MissingCze.gf +++ b/src/czech/MissingCze.gf @@ -9,12 +9,10 @@ oper AdjOrd : Ord -> AP = notYet "AdjOrd" ; oper AdnCAdv : CAdv -> AdN = notYet "AdnCAdv" ; oper AdvIAdv : IAdv -> Adv -> IAdv = notYet "AdvIAdv" ; oper AdvIP : IP -> Adv -> IP = notYet "AdvIP" ; -oper AdvS : Adv -> S -> S = notYet "AdvS" ; oper AdvSlash : ClSlash -> Adv -> ClSlash = notYet "AdvSlash" ; oper CAdvAP : CAdv -> AP -> NP -> AP = notYet "CAdvAP" ; oper CleftAdv : Adv -> S -> Cl = notYet "CleftAdv" ; oper CleftNP : NP -> RS -> Cl = notYet "CleftNP" ; -oper CompCN : CN -> Comp = notYet "CompCN" ; oper CompIAdv : IAdv -> IComp = notYet "CompIAdv" ; oper CompIP : IP -> IComp = notYet "CompIP" ; oper ComparA : A -> NP -> AP = notYet "ComparA" ; diff --git a/src/czech/NounCze.gf b/src/czech/NounCze.gf index 6b31f8f7..59372f68 100644 --- a/src/czech/NounCze.gf +++ b/src/czech/NounCze.gf @@ -99,5 +99,12 @@ lin NumDecimal ds = ds ** {s = \\_,_ => ds.s} ; NumNumeral nu = nu ; + SentCN cn sc = cn ** {s = \\n,c => cn.s ! n ! c ++ sc.s} ; + + PredetNP pred np = np ** { + s = \\c => pred.s ++ np.s ! c ; + clit = \\c => pred.s ++ np.clit ! c ; + prep = \\c => pred.s ++ np.prep ! c + } ; } diff --git a/src/czech/PhraseCze.gf b/src/czech/PhraseCze.gf index c1a0fda6..50bd9dc9 100644 --- a/src/czech/PhraseCze.gf +++ b/src/czech/PhraseCze.gf @@ -8,6 +8,10 @@ lin UttNP np = {s = np.s ! Nom} ; UttVP vp = let agr = Ag Neutr Sg P3 in {s = vp.clit ! agr ++ vp.verb.inf ++ vp.compl ! agr} ; + UttImpSg pol imp = {s = pol.s ++ imp.s} ; + UttImpPl pol imp = {s = pol.s ++ imp.s} ; + UttImpPol pol imp = {s = pol.s ++ imp.s} ; + PhrUtt pconj utt voc = {s = pconj.s ++ utt.s ++ voc.s} ; diff --git a/src/czech/QuestionCze.gf b/src/czech/QuestionCze.gf index d5390caa..d0bea0ee 100644 --- a/src/czech/QuestionCze.gf +++ b/src/czech/QuestionCze.gf @@ -4,4 +4,6 @@ concrete QuestionCze of Question = CatCze ** lin QuestCl cl = cl ; ---- + QuestIAdv iadv cl = cl ** {clit = iadv.s ++ cl.clit} ; + } diff --git a/src/czech/SentenceCze.gf b/src/czech/SentenceCze.gf index 2793ec35..661df8e6 100644 --- a/src/czech/SentenceCze.gf +++ b/src/czech/SentenceCze.gf @@ -28,5 +28,24 @@ lin pol.s ++ verbAgr rcl.verb a pol.p ++ rcl.compl ! a ; } ; - + +-- no imperative in VerbForms yet; the 1st person plural present is used +-- instead, which is the normal register in mathematical Czech +-- ("předpokládáme, že ..." = "we assume that ...") + ImpVP vp = let agr = Ag (Masc Anim) Pl P1 in + {s = vp.clit ! agr ++ verbAgr vp.verb agr True ++ vp.compl ! agr} ; + + EmbedS s = {s = "že" ++ s.s} ; + + EmbedQS qs = {s = qs.s} ; + + EmbedVP vp = let agr = Ag Neutr Sg P3 in + {s = vp.clit ! agr ++ vp.verb.inf ++ vp.compl ! agr} ; + + AdvS a s = {s = a.s ++ s.s} ; + + ExtAdvS a s = {s = a.s ++ SOFT_BIND ++ "," ++ s.s} ; + + SSubjS a subj b = {s = a.s ++ SOFT_BIND ++ "," ++ subj.s ++ b.s} ; + } diff --git a/src/czech/StructuralCze.gf b/src/czech/StructuralCze.gf index ef14b1a2..b56c7ffa 100644 --- a/src/czech/StructuralCze.gf +++ b/src/czech/StructuralCze.gf @@ -1,11 +1,42 @@ concrete StructuralCze of Structural = CatCze ** open ParadigmsCze, ResCze, Prelude in { +-- a singular determiner inflecting like an adjective, e.g. "každý", "nějaký" +oper + adjDet : AdjForms -> Determiner = \afs -> { + s = \\g,c => (adjFormsAdjective afs).s ! g ! Sg ! c ; + size = Num1 + } ; + lin + all_Predet = {s = "všechny"} ; and_Conj = mkConj "a" ; + both7and_DConj = {s1 = "jak" ; s2 = "tak"} ; + between_Prep = mkPrep "mezi" Ins ; by8agent_Prep = mkPrep "od" Gen ; ---- TODO this means "from", there might be no good translation + by8means_Prep = mkPrep "pomocí" Gen ; + can_VV = { + inf = "moci" ; + pressg1 = "mohu" ; + pressg2 = "můžeš" ; + pressg3, negpressg3 = "může" ; + prespl1 = "můžeme" ; + prespl2 = "můžete" ; + prespl3 = "mohou" ; + pastpartsg = "mohl" ; + pastpartpl = "mohli" ; + } ; + either7or_DConj = {s1 = "buď" ; s2 = "nebo"} ; + every_Det = adjDet (mladyAdjForms "každý") ; few_Det = invarNumeral "málo" ; -- CEG 6.8 --- TODO genitive mála for_Prep = mkPrep "pro" accusative ; + if_Subj = {s = "jestliže"} ; + no_Quant = adjFormsAdjective (mladyAdjForms "žádný") ; + on_Prep = mkPrep "na" Loc ; + someSg_Det = adjDet (mladyAdjForms "nějaký") ; + that_Subj = {s = "že"} ; + under_Prep = mkPrep "pod" Ins ; + where_IAdv = {s = "kde"} ; from_Prep = mkPrep (pre {"s"|"z" => "ze" ; _ => "z"}) Gen ; ---- consonant clusters have_V2 = mkV2 haveVerbForms ; in_Prep = mkPrep (pre {"v"|"m" => "ve" ; _ => "v"}) Loc ; ---- diff --git a/src/czech/VerbCze.gf b/src/czech/VerbCze.gf index e80c25c7..4cc9c8e2 100644 --- a/src/czech/VerbCze.gf +++ b/src/czech/VerbCze.gf @@ -36,6 +36,12 @@ lin CompNP np = { s = \\a_ => np.s ! Nom ; ---- InstrC in Pol } ; + + CompCN cn = { + s = \\a => case a of { + Ag _ n _ => cn.s ! n ! Nom ---- InstrC also possible + } + } ; CompAdv adv = { s = \\a_ => adv.s @@ -45,4 +51,24 @@ lin compl = \\a => vp.compl ! a ++ adv.s } ; +-- VerbForms has no passive participle yet, so the reflexive passive is used: +-- "číslo se dělí" = "the number is divided" + PassV2 v = { + verb = v ; + clit = \\_ => "se" ; + compl = \\_ => [] + } ; + + ComplVV vv vp = { + verb = vv ; + clit = vp.clit ; + compl = \\a => vp.verb.inf ++ vp.compl ! a + } ; + + ComplVS vs s = { + verb = vs ; + clit = \\_ => [] ; + compl = \\_ => SOFT_BIND ++ "," ++ "že" ++ s.s + } ; + } From 344342012d0bfdfa80ff11c98d0aba1e951efd82 Mon Sep 17 00:00:00 2001 From: aarneranta Date: Fri, 10 Jul 2026 16:32:25 +0200 Subject: [PATCH 07/16] czech: fix noun paradigm selection, add loanword and adjectival types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit declensionNounForms only tested hardConsonant (d t g h k n r), so the neutral consonants (b f l m p s v), which take the hard endings, matched nothing and fell through to the soft declSTROJ fallback: graf, atom, profil, přístup were all declined as if soft. Same gap in guessNounForms. Add hardishConsonant (hard + neutral + the foreign z, x) and use it in both. The genitive passed to the three-argument mkN was only used to pick a paradigm, then thrown away, so the oblique stem was re-derived from the nominative by dropFleetingE. That guesses wrong in both directions: it fires on člen -> člnu, and fails to fire on uzel -> uzelu, doplněk -> doplněku, výpočet -> výpočetu. Give declHRAD, declHRADA and declMUZ stem-taking variants and let declensionNounForms pass the stem it was given. The one-argument guessNounForms keeps the old dropFleetingE behaviour. New paradigms, all previously falling back to declSTROJ: declLATINUS algoritmus - algoritmu, kosmos - kosmu (also -os) declLATINUSA the same, masculine animate declLATINUM kontinuum - kontinua declGREEKMA schéma - schématu declHRADA les - lesa, zákon - zákona declADJF proměnná - proměnné declADJM nultý - nultého declINVAR indeclinable loans: bombé, tamari, software Also match feminine consonant stems by genitive (-i -> kost, -e/-ě -> píseň), masculine -e/-ě genitives (kužel - kužele, král - krále), and neuter -ě (těžiště), none of which were reachable before. Measured on the 1177 nouns of informath's WikidataWordsCze: 225 hit the fallback before, 1 after (the acronym ADE). Co-Authored-By: Claude Opus 4.8 (1M context) --- src/czech/ResCze.gf | 141 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 122 insertions(+), 19 deletions(-) diff --git a/src/czech/ResCze.gf b/src/czech/ResCze.gf index 4e009982..620d0739 100644 --- a/src/czech/ResCze.gf +++ b/src/czech/ResCze.gf @@ -28,6 +28,11 @@ oper softConsonant : pattern Str = #("ť"|"ď"|"j"|"ň"|"ř"|"š"|"c"|"č"|"ž") ; neutralConsonant : pattern Str = #("b"|"f"|"l"|"m"|"p"|"s"|"v") ; +-- neutral consonants take the hard endings by default (hrad, pán), and so do +-- the foreign "z" and "x"; this is the class to test when choosing a paradigm + hardishConsonant : pattern Str = + #("d"|"t"|"g"|"h"|"k"|"n"|"r" | "b"|"f"|"l"|"m"|"p"|"s"|"v" | "z"|"x") ; + consonant : pattern Str = #( "d" | "t" | "g" | "h" | "k" | "n" | "r" | @@ -139,22 +144,33 @@ oper declensionNounForms : (nom,gen : Str) -> Gender -> NounForms = \nom,gen,g -> - let decl : DeclensionType = case of { - => declPAN ; - => declPREDSEDA ; - => declHRAD ; - => declZENA ; - => declMESTO ; - => declMUZ ; - => declMUZ ; +-- the oblique stem, for the paradigms that cannot derive it from the nominative + let stem : Str = Predef.tk 1 gen ; + decl : DeclensionType = case of { + => declMUZstem stem ; => declSOUDCE ; - => declSTROJ ; + => declLATINUSA ; + => declPREDSEDA ; + => declMUZstem stem ; + => declPAN ; + => declLATINUS ; + => declADJM ; + => declSTROJ ; + => declHRADstem stem ; + => declHRADAstem stem ; + => declZENA ; + => declADJF ; => declRUZE ; - => declPISEN ; - => declKOST ; --- also many other "st" 3.6.3 + => declKOST ; --- also many other "st" 3.6.3 + => declPISEN ; + => declLATINUM ; + => declGREEKMA ; + => declMESTO ; => declKURE ; - => declMORE ; => declSTAVENI ; + => declMORE ; + => declINVAR (Masc Inanim) ; + => declINVAR Neutr ; _ => (\s -> declSTROJ ("" + s)) -- Predef.error ("cannot infer declension type for" ++ nom ++ gen) } in decl nom ; @@ -165,12 +181,14 @@ oper = \s -> case s of { _ + "ost" => declKOST s ; _ + "tel" => declMUZ s ; - _ + #hardConsonant => declHRAD s ; + _ + "us" => declLATINUS s ; + _ + "um" => declLATINUM s ; + _ + #hardishConsonant => declHRAD s ; _ + #softConsonant => declSTROJ s ; _ + "a" => declZENA s ; _ + "o" => declMESTO s ; _ + "ce" => declSOUDCE s ; - _ + "e" => declMORE s ; + _ + ("e"|"ě") => declMORE s ; _ + "í" => declSTAVENI s ; _ => declSTROJ ("" + s) -- Predef.error ("cannot guess declension type for" ++ s) } ; @@ -216,9 +234,9 @@ oper g = Masc Anim } ; - declHRAD : DeclensionType = \hrad -> --- 3.5.2: sloc u/ě/e extra arg, sport-u, hrad-ě ; sgen u/a - let hrd = dropFleetingE hrad - in +-- the oblique stem is a separate argument, because it cannot always be +-- derived from the nominative: uzel-uzlu but člen-členu + declHRADstem : Str -> DeclensionType = \hrd,hrad -> { snom,sacc = hrad ; sgen,sdat = hrd + "u" ; --- Berlín-a @@ -233,6 +251,9 @@ oper g = Masc Inanim } ; + declHRAD : DeclensionType = \hrad -> --- 3.5.2: sloc u/ě/e extra arg, sport-u, hrad-ě ; sgen u/a + declHRADstem (dropFleetingE hrad) hrad ; + declZENA : DeclensionType = \zena -> --- 3.6.1 sge y/i ; pgen sometimes shortening let zen = init zena in @@ -270,9 +291,91 @@ oper g = Neutr } ; +-- Latin masculines in -us: the ending is dropped outside the nominative +-- (algoritmus - algoritmu), otherwise they follow hrad + declLATINUS : DeclensionType = \algoritmus -> + let algoritm = Predef.tk 2 algoritmus + in declHRAD algoritm ** { + snom, sacc = algoritmus ; + svoc = algoritm + "e" + } ; + + declLATINUSA : DeclensionType = \genius -> + declLATINUS genius ** {g = Masc Anim} ; + +-- Latin neuters in -um: the ending is dropped outside the nominative +-- (kontinuum - kontinua), otherwise they follow město + declLATINUM : DeclensionType = \kompaktum -> + let kompakt = Predef.tk 2 kompaktum + in declMESTO (kompakt + "o") ** { + snom, sacc, svoc = kompaktum + } ; + +-- Greek neuters in -ma, with the stem extended by -t- (schéma - schématu) + declGREEKMA : DeclensionType = \schema -> + let schemat = schema + "t" + in { + snom,sacc,svoc = schema ; + sgen,sdat,sloc = schemat + "u" ; + sins = schemat + "em" ; + + pnom,pacc = schemat + "a" ; + pgen = schemat ; + pdat = schemat + "ům" ; + ploc = schemat + "ech" ; + pins = schemat + "y" ; + g = Neutr + } ; + +-- the hrad type with genitive -a instead of -u (les - lesa, zákon - zákona) + declHRADAstem : Str -> DeclensionType = \les_,les -> + declHRADstem les_ les ** {sgen = les_ + "a"} ; + + declHRADA : DeclensionType = \les -> + declHRADAstem (dropFleetingE les) les ; + +-- nouns that are adjectives in form: proměnná - proměnné, nultý - nultého + declADJF : DeclensionType = \promenna -> + let a = mladyAdjForms (init promenna + "ý") + in { + snom,svoc = a.fsnom ; + sgen = a.fsgen ; + sdat,sloc = a.fsdat ; + sacc = a.fsacc ; + sins = a.fsins ; + pnom,pacc = a.fpnom ; + pgen,ploc = a.pgen ; + pdat = a.msins ; + pins = a.pins ; + g = Fem + } ; + + declADJM : DeclensionType = \nulty -> + let a = mladyAdjForms nulty + in { + snom,sacc,svoc = a.msnom ; + sgen = a.msgen ; + sdat = a.msdat ; + sloc = a.msloc ; + sins = a.msins ; + pnom,pacc = a.fpnom ; + pgen,ploc = a.pgen ; + pdat = a.msins ; + pins = a.pins ; + g = Masc Inanim + } ; + +-- indeclinable loans: bombé, tamari, software + declINVAR : Gender -> DeclensionType = \g,s -> { + snom,sgen,sdat,sacc,svoc,sloc,sins = s ; + pnom,pgen,pdat,pacc,ploc,pins = s ; + g = g + } ; + declMUZ : DeclensionType = \muz_ -> --- 3.5.3 : sdat,sloc ; pnom - let muz = dropFleetingE muz_ - in + declMUZstem (dropFleetingE muz_) muz_ ; + + declMUZstem : Str -> DeclensionType = \muz,muz_ -> { snom = muz_ ; sgen,sacc = muz + "e" ; --- pacc From 11f73acc2832091e36b6670fb20649ec9c207046 Mon Sep 17 00:00:00 2001 From: aarneranta Date: Thu, 16 Jul 2026 17:51:41 +0200 Subject: [PATCH 08/16] czech: implement three-place verbs (V3) Add a proper V3 category to the Czech RGL, previously absent (V3 fell through to the {s:Str} default and Slash2V3/Slash3V3 were notYet stubs). - CatCze: lincat V3 = VerbForms ** {c, c2 : ComplementCase}; extend VPSlash with a trailing `ind` field for the incorporated indirect object (mirrors the English VPSlash c2 field). - VerbCze: implement Slash2V3 and Slash3V3; ComplSlash now renders the `ind` string after the object slot, and SlashV2a sets ind = []. This keeps the object before the indirect object under the shared ComplV3 v o d = ComplSlash (Slash3V3 v d) o. - ParadigmsCze: add the mkV3 paradigm (default acc/dat, plus an explicit two-complement-case form). - MissingCze: drop the now-implemented Slash2V3/Slash3V3 stubs. V2 predication is unchanged (ind is empty for V2-derived VPSlash). Co-Authored-By: Claude Opus 4.8 (1M context) --- src/czech/CatCze.gf | 3 ++- src/czech/MissingCze.gf | 2 -- src/czech/ParadigmsCze.gf | 8 ++++++++ src/czech/VerbCze.gf | 24 +++++++++++++++++++++--- 4 files changed, 31 insertions(+), 6 deletions(-) diff --git a/src/czech/CatCze.gf b/src/czech/CatCze.gf index d7d42239..20c560e3 100644 --- a/src/czech/CatCze.gf +++ b/src/czech/CatCze.gf @@ -21,9 +21,10 @@ concrete CatCze of Cat = RP = AdjForms ; VP = {verb : VerbForms ; clit,compl : Agr => Str} ; ---- more fields probably needed - VPSlash = {verb : VerbForms ; clit,compl : Agr => Str ; c : ComplementCase} ; ---- + VPSlash = {verb : VerbForms ; clit,compl : Agr => Str ; c : ComplementCase ; ind : Agr => Str} ; -- ind : incorporated indirect object, rendered after the object slot V = ResCze.VerbForms ; V2 = ResCze.VerbForms ** {c : ComplementCase} ; + V3 = ResCze.VerbForms ** {c,c2 : ComplementCase} ; -- c : direct object, c2 : indirect object VS,VQ,VV = ResCze.VerbForms ; A = ResCze.AdjForms ; diff --git a/src/czech/MissingCze.gf b/src/czech/MissingCze.gf index 02ce581c..be4b9cb2 100644 --- a/src/czech/MissingCze.gf +++ b/src/czech/MissingCze.gf @@ -61,8 +61,6 @@ oper RelNP : NP -> RS -> NP = notYet "RelNP" ; oper RelSlash : RP -> ClSlash -> RCl = notYet "RelSlash" ; oper SentAP : AP -> SC -> AP = notYet "SentAP" ; oper SentCN : CN -> SC -> CN = notYet "SentCN" ; -oper Slash2V3 : V3 -> NP -> VPSlash = notYet "Slash2V3" ; -oper Slash3V3 : V3 -> NP -> VPSlash = notYet "Slash3V3" ; oper SlashPrep : Cl -> Prep -> ClSlash = notYet "SlashPrep" ; oper SlashV2A : V2A -> AP -> VPSlash = notYet "SlashV2A" ; oper SlashV2Q : V2Q -> QS -> VPSlash = notYet "SlashV2Q" ; diff --git a/src/czech/ParadigmsCze.gf b/src/czech/ParadigmsCze.gf index e6e60582..63d84236 100644 --- a/src/czech/ParadigmsCze.gf +++ b/src/czech/ParadigmsCze.gf @@ -122,6 +122,14 @@ oper = \vf,c -> vf ** {c = c} ; } ; + mkV3 = overload { + mkV3 : VerbForms -> VerbForms ** {c,c2 : ComplementCase} + = \vf -> vf ** {c = {s = [] ; c = Acc ; hasPrep = False} ; + c2 = {s = [] ; c = Dat ; hasPrep = False}} ; + mkV3 : VerbForms -> ComplementCase -> ComplementCase -> VerbForms ** {c,c2 : ComplementCase} + = \vf,c,c2 -> vf ** {c = c ; c2 = c2} ; + } ; + ------------------------ -- Adverbs, prepositions, conjunctions, ... diff --git a/src/czech/VerbCze.gf b/src/czech/VerbCze.gf index 4cc9c8e2..cc98d4a3 100644 --- a/src/czech/VerbCze.gf +++ b/src/czech/VerbCze.gf @@ -8,17 +8,35 @@ lin ComplSlash vps np = case of { => vps ** { - clit = \\a => vps.clit ! a ++ np.clit ! vps.c.c + clit = \\a => vps.clit ! a ++ np.clit ! vps.c.c ; + compl = \\a => vps.compl ! a ++ vps.ind ! a } ; _ => vps ** { - compl = \\a => vps.compl ! a ++ vps.c.s ++ np.s ! vps.c.c + compl = \\a => vps.compl ! a ++ vps.c.s ++ np.s ! vps.c.c ++ vps.ind ! a } } ; SlashV2a v = { verb = v ; clit,compl = \\_ => [] ; - c = v.c + c = v.c ; + ind = \\_ => [] + } ; + + -- three-place verbs: c = direct object case, c2 = indirect object case + Slash2V3 v np = { -- fill the direct object, leave the indirect open + verb = v ; + clit = \\_ => [] ; + compl = \\_ => v.c.s ++ np.s ! v.c.c ; + c = v.c2 ; + ind = \\_ => [] + } ; + Slash3V3 v np = { -- fill the indirect object (rendered after the object slot) + verb = v ; + clit = \\_ => [] ; + compl = \\_ => [] ; + c = v.c ; + ind = \\_ => v.c2.s ++ np.s ! v.c2.c } ; UseComp comp = { From 8c4cd58bd19abe4239482a44b3d280675ef718ee Mon Sep 17 00:00:00 2001 From: aarneranta Date: Thu, 16 Jul 2026 19:22:24 +0200 Subject: [PATCH 09/16] polish: complete the RGL enough to build an application grammar MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adding Polish to informath (grammars/next) exposed a set of gaps. Unlike Czech, StructuralPol was already complete; what was missing was a handful of lins, a way to govern the nominative, and some outright mistakes. Missing lins, all reached as qualified Grammar.X by the application's functors and so not shimmable from the application side: AdjectivePol AdvAP SentencePol SSubjS IdiomPol ImpP3 StructuralPol that_Subj MarkupPol new module (Markup is not part of the RGL build script, so it is compiled on demand, as MarkupCze is) ParadigmsPol mkAdv (every other ParadigmsX has it; the application interfaces reach it as Paradigms.mkAdv) ImpP3 has no third-person imperative to use, so it takes the standard "niech" periphrasis. The copula takes the future ("niech x będzie grupą") and every other verb the present ("niech x należy do A"): być is the only Polish verb with a synthetic future, and imienne marks exactly the copular VPs. NomPrep: ComplCase had no nominative at all, so "jako", "niż" and "zdefiniowany jako" -- all of which govern the nominative -- came out locative ("mniejszy niż liczbie"). mkCompl now maps Nom to a new NomPrep, and the dep tables of the pronouns, nounPN, mkPN and the structural NPs cover it. LexiconNounPol is marked DO NOT EDIT, but paris_PN and john_PN inline their own dep tables and so had to be extended too. Two corrections: VerbPol.CompCN used the nominative for a predicative noun, giving "x jest grupa"; Polish uses the instrumental, as CompNP right below it already does. ExtendPol.ExistsNP inherited ExistNP from ExtendFunctor, giving "jest macierz". Polish distinguishes the two: "there exists" is istnieć, which is what mathematical prose uses. ParadigmsPol.guess_paradigm_basic never matched -ość, the productive feminine abstract suffix, so sprzeczność took a masculine declension (*sprzeczności/a, *sprzecznościowi) despite being tagged feminine. It now routes to the kość paradigm. Also documents, without changing, why the 2-string mkN throws its genitive away: guess_paradigm's 2-string table is unsound (its first branch matches every noun in -a, and its branches disagree about whether mkNTable* takes the nominative or the bare stem), so passing sggen to it turns "liczba"/"liczby" into *liczbaa. The 1-string guesser is the sound path until that table is repaired. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/polish/AdjectivePol.gf | 3 +++ src/polish/ExtendPol.gf | 15 +++++++++++++-- src/polish/IdiomPol.gf | 14 ++++++++++++++ src/polish/LexiconNounPol.gf | 4 ++-- src/polish/MarkupPol.gf | 23 +++++++++++++++++++++++ src/polish/MorphoPol.gf | 8 ++++++++ src/polish/NounMorphoPol.gf | 1 + src/polish/ParadigmsPol.gf | 15 +++++++++++++++ src/polish/PronounMorphoPol.gf | 13 +++++++++++++ src/polish/ResPol.gf | 14 +++++++------- src/polish/SentencePol.gf | 3 +++ src/polish/StructuralPol.gf | 3 +++ src/polish/VerbPol.gf | 4 +++- 13 files changed, 108 insertions(+), 12 deletions(-) create mode 100644 src/polish/MarkupPol.gf diff --git a/src/polish/AdjectivePol.gf b/src/polish/AdjectivePol.gf index f9157dee..989ba5ef 100644 --- a/src/polish/AdjectivePol.gf +++ b/src/polish/AdjectivePol.gf @@ -16,6 +16,9 @@ flags coding=utf8 ; AdjOrd o = {s=o.s; adv="["++o.s!AF MascPersSg Nom ++ [": the adverb form does not exist (fun AdjOrd)]"] ; isPost = False}; AdAP ada ap = { s = \\af => ada.s ++ ap.s ! af; adv = ada.s ++ ap.adv ; isPost = ap.isPost}; + +-- AdvAP : AP -> Adv -> AP ; -- warm by nature + AdvAP ap adv = { s = \\af => ap.s ! af ++ adv.s; adv = ap.adv ++ adv.s ; isPost = True}; -- CAdvAP : CAdv -> AP -> NP -> AP ; -- as cool as John CAdvAP c a n = { diff --git a/src/polish/ExtendPol.gf b/src/polish/ExtendPol.gf index afa4f84c..a1f13458 100644 --- a/src/polish/ExtendPol.gf +++ b/src/polish/ExtendPol.gf @@ -2,11 +2,22 @@ concrete ExtendPol of Extend = CatPol ** ExtendFunctor - [ - iFem_Pron, youFem_Pron, theyFem_Pron, ProDrop, PassVPSlash + iFem_Pron, youFem_Pron, theyFem_Pron, ProDrop, PassVPSlash, ExistsNP ] with (Grammar = GrammarPol) ** - open PronounMorphoPol, Prelude in { + open PronounMorphoPol, ResPol, VerbMorphoPol, Prelude in { + +-- ExtendFunctor defaults ExistsNP to ExistNP, which gives "jest macierz". +-- Polish distinguishes the two: "there is" is jest/są, but "there exists" is +-- istnieć, which is the form mathematical prose uses. +oper + istniec_V : Verb = mkMonoVerb "istnieć" conj52 Imperfective ; + +lin ExistsNP np = { + s = \\pol,anter,tense => + (indicative_form istniec_V False pol) ! ++ np.nom + } ; lin iFem_Pron = pronJa FemSg ; lin youFem_Pron = pronTy FemSg ; diff --git a/src/polish/IdiomPol.gf b/src/polish/IdiomPol.gf index 0f69333d..b94e462f 100644 --- a/src/polish/IdiomPol.gf +++ b/src/polish/IdiomPol.gf @@ -16,6 +16,20 @@ concrete IdiomPol of Idiom = CatPol ** open Prelude, ResPol, VerbMorphoPol in { vp.sufix !pol !NeutSg }; +-- ImpP3 : NP -> VP -> Utt ; -- let John walk +-- Polish has no third-person imperative; "niech" + a finite form is the +-- standard equivalent. The copula takes the future ("niech x będzie grupą"), +-- every other verb the present ("niech x należy do A") -- być is the only +-- Polish verb with a synthetic future, and imienne marks exactly the +-- copular VPs. + ImpP3 np vp = let + tense : Tense = case vp.imienne of { True => Fut ; False => Pres } + in { + s = "niech" ++ np.nom ++ vp.prefix ++ + ((indicative_form vp.verb vp.imienne Pos) !) ++ + vp.sufix !Pos !np.gn + }; + -- ImpPl1 : VP -> Utt ; -- let's go ImpPl1 vp = { s = vp.prefix ++ diff --git a/src/polish/LexiconNounPol.gf b/src/polish/LexiconNounPol.gf index 6740afd4..5909fcaf 100644 --- a/src/polish/LexiconNounPol.gf +++ b/src/polish/LexiconNounPol.gf @@ -205,7 +205,7 @@ lin paris_PN = { nom = (mkNTable0045 "Paryż")!SF Sg Nom; voc = (mkNTable0045 "Paryż")!SF Sg VocP; dep = let forms = (mkNTable0045 "Paryż") in table { - GenPrep|GenNoPrep=>forms!SF Sg Gen; AccPrep|AccNoPrep=>forms!SF Sg Acc; + NomPrep=>forms!SF Sg Nom; GenPrep|GenNoPrep=>forms!SF Sg Gen; AccPrep|AccNoPrep=>forms!SF Sg Acc; DatPrep|DatNoPrep=>forms!SF Sg Dat; InstrC=>forms!SF Sg Instr; LocPrep=>forms!SF Sg Loc}; gn= MascInaniSg ; p=P3 @@ -213,7 +213,7 @@ lin john_PN = { nom = (mkNTable0002 "Jan")!SF Sg Nom; voc = (mkNTable0002 "Jan")!SF Sg VocP; dep = let forms = (mkNTable0002 "Jan") in table { - GenPrep|GenNoPrep=>forms!SF Sg Gen; AccPrep|AccNoPrep=>forms!SF Sg Acc; + NomPrep=>forms!SF Sg Nom; GenPrep|GenNoPrep=>forms!SF Sg Gen; AccPrep|AccNoPrep=>forms!SF Sg Acc; DatPrep|DatNoPrep=>forms!SF Sg Dat; InstrC=>forms!SF Sg Instr; LocPrep=>forms!SF Sg Loc}; gn= MascPersSg ; p=P3 diff --git a/src/polish/MarkupPol.gf b/src/polish/MarkupPol.gf new file mode 100644 index 00000000..4bc41d98 --- /dev/null +++ b/src/polish/MarkupPol.gf @@ -0,0 +1,23 @@ +--# -path=.:../abstract:../common + +concrete MarkupPol of Markup = CatPol, MarkHTMLX ** open ResPol in { + +lin + MarkupCN m cn = cn ** {s = \\n,c => appMark m (cn.s ! n ! c)} ; + + -- NP has three alternative surface forms: nom, voc and the dependent + -- cases; each is marked up separately. + MarkupNP m np = np ** { + nom = appMark m np.nom ; + voc = appMark m np.voc ; + dep = \\c => appMark m (np.dep ! c) + } ; + + MarkupAP m ap = ap ** {s = \\af => appMark m (ap.s ! af)} ; + MarkupAdv m adv = {s = appMark m adv.s} ; + MarkupS m s = {s = appMark m s.s} ; + MarkupUtt m utt = {s = appMark m utt.s} ; + MarkupPhr m phr = {s = appMark m phr.s} ; + MarkupText m txt = {s = appMark m txt.s} ; + +} diff --git a/src/polish/MorphoPol.gf b/src/polish/MorphoPol.gf index 447ebacc..d239e762 100644 --- a/src/polish/MorphoPol.gf +++ b/src/polish/MorphoPol.gf @@ -60,6 +60,7 @@ oper nom = "wszyscy" ; voc = "wszyscy" ; dep = table { + NomPrep => "wszyscy"; (GenNoPrep|GenPrep) => "wszystkich"; (DatNoPrep|DatPrep) => "wszystkim"; (AccNoPrep|AccPrep) => "wszystkich"; @@ -74,6 +75,7 @@ oper nom = "wszystko" ; voc = "wszystko" ; dep = table { + NomPrep => "wszystko"; (GenNoPrep|GenPrep) => "wszystkiego"; (DatNoPrep|DatPrep) => "wszystkiemu"; (AccNoPrep|AccPrep) => "wszystko"; @@ -88,6 +90,7 @@ oper nom = "ktoś" ; voc = "ktosiu" ; dep = table { + NomPrep => "ktoś"; (GenNoPrep|GenPrep) => "kogoś"; (DatNoPrep|DatPrep) => "komuś"; (AccNoPrep|AccPrep) => "kogoś"; @@ -102,6 +105,7 @@ oper nom = "coś" ; voc = "coś" ; dep = table { + NomPrep => "coś"; (GenNoPrep|GenPrep) => "czegoś"; (DatNoPrep|DatPrep) => "czemuś"; (AccNoPrep|AccPrep) => "coś"; @@ -117,6 +121,7 @@ oper nom = "kto" ; voc = "kto" ; dep = table { + NomPrep => "kto"; (GenNoPrep|GenPrep) => "kogo"; (DatNoPrep|DatPrep) => "komu"; (AccNoPrep|AccPrep) => "kogo"; @@ -131,6 +136,7 @@ oper nom = "co" ; voc = "co" ; dep = table { + NomPrep => "co"; (GenNoPrep|GenPrep) => "czego"; (DatNoPrep|DatPrep) => "czemu"; (AccNoPrep|AccPrep) => "co"; @@ -216,6 +222,7 @@ oper oper niktNP : NounPhrase = { voc,nom="nikt"; dep = table { + NomPrep => "nikt"; (GenNoPrep|GenPrep) => "nikogo"; (DatNoPrep|DatPrep) => "nikomu"; (AccNoPrep|AccPrep) => "nikogo"; @@ -230,6 +237,7 @@ oper oper nicNP : NounPhrase = { voc,nom="nic"; dep = table { + NomPrep => "nic"; (GenNoPrep|GenPrep) => "niczego"; (DatNoPrep|DatPrep) => "niczemu"; (AccNoPrep|AccPrep) => "nic"; diff --git a/src/polish/NounMorphoPol.gf b/src/polish/NounMorphoPol.gf index 152ff5e5..73f27a31 100644 --- a/src/polish/NounMorphoPol.gf +++ b/src/polish/NounMorphoPol.gf @@ -27,6 +27,7 @@ resource NounMorphoPol = open CatPol, ResPol, Prelude, (Predef=Predef) in { { nom = forms!SF Sg Nom; voc = forms!SF Sg VocP; dep = table { + NomPrep =>forms!SF Sg Nom; GenPrep|GenNoPrep=>forms!SF Sg Gen; AccPrep|AccNoPrep=>forms!SF Sg Acc; DatPrep|DatNoPrep=>forms!SF Sg Dat; diff --git a/src/polish/ParadigmsPol.gf b/src/polish/ParadigmsPol.gf index 062ff07d..6131f8e5 100644 --- a/src/polish/ParadigmsPol.gf +++ b/src/polish/ParadigmsPol.gf @@ -26,10 +26,14 @@ mkA2 : A -> Str -> ComplCase -> A2 ; + mkAdv : Str -> Adv ; -- an adverb from a string + --. -- Definitions hidden from the public API + mkAdv s = lin Adv {s = s} ; + ComplCase = ResPol.ComplCase ; genPrep = GenPrep ; genNoPrep = GenNoPrep ; @@ -41,6 +45,7 @@ { nom = (tab form)!SF Sg Nom; voc = (tab form)!SF Sg VocP; dep = let forms = (tab form) in table { + NomPrep =>forms!SF Sg Nom; GenPrep|GenNoPrep=>forms!SF Sg Gen; AccPrep|AccNoPrep=>forms!SF Sg Acc; DatPrep|DatNoPrep=>forms!SF Sg Dat; @@ -101,6 +106,13 @@ let ntable : SubstForm => Str = guess_paradigm_basic sgnom in NM.mkN ntable gender ; -- 2 string + -- NB: sggen is deliberately NOT passed to guess_paradigm. The 2-string + -- guess_paradigm table is unsound: its first branch <_ + "a", _ + ""> + -- matches every noun in -a (the suffix "" matches anything), and its + -- branches disagree about whether mkNTable* takes the nominative + -- (mkNTable0021 does Predef.tk 1) or the bare stem (mkNTable0308 does + -- not). Routing sggen there turns "liczba"/"liczby" into "liczbaa". + -- Until that table is repaired, the 1-string guesser is the sound path. mkNGender : Str -> Str -> Gender -> N = \sgnom,sggen,gender -> let ntable : SubstForm => Str = guess_paradigm sgnom in NM.mkN ntable gender ; @@ -373,6 +385,9 @@ _ + "ń" => NM.mkNTable0142 sgnom ; -- Alternatives: mkNTable0268,mkNTable0290,mkNTable0297,mkNTable0468,mkNTable0592,mkNTable0612,mkNTable0674,mkNTable0676,mkNTable0775,mkNTable0815,mkNTable0935,mkNTable1004 _ + "ł" => NM.mkNTable0151 sgnom ; -- Alternatives: mkNTable0192,mkNTable0280,mkNTable0533,mkNTable0601 _ + "ę" => NM.mkNTable0379 sgnom ; -- Alternatives: mkNTable0604 + -- -ość is the productive feminine abstract-noun suffix (sprzeczność, + -- własność, równość); it always takes the kość declension. + _ + "ość" => NM.mkNTable0475 sgnom ; _ + "ć" => NM.mkNTable0069 sgnom ; -- Alternatives: mkNTable0475,mkNTable0567,mkNTable0573,mkNTable0649,mkNTable0734,mkNTable0792,mkNTable0793,mkNTable0794,mkNTable0814,mkNTable0838,mkNTable0922,mkNTable0923,mkNTable1014 _ + "ź" => NM.mkNTable0316 sgnom ; -- Alternatives: mkNTable0633,mkNTable0661,mkNTable0722,mkNTable0732,mkNTable0771 _ + "y" => NM.mkNTable0012 sgnom ; -- Alternatives: mkNTable0050,mkNTable0058,mkNTable0123,mkNTable0203,mkNTable0635,mkNTable0665,mkNTable0777,mkNTable0886,mkNTable1020 diff --git a/src/polish/PronounMorphoPol.gf b/src/polish/PronounMorphoPol.gf index 804db772..946ce9cd 100644 --- a/src/polish/PronounMorphoPol.gf +++ b/src/polish/PronounMorphoPol.gf @@ -23,6 +23,7 @@ resource PronounMorphoPol = ResPol ** open Prelude, (Predef=Predef) in { { nom = "ja"; voc = "ja"; dep = table { + NomPrep => "ja"; (GenNoPrep|GenPrep) => "mnie"; DatNoPrep => "mi"; DatPrep => "mnie"; @@ -114,6 +115,7 @@ resource PronounMorphoPol = ResPol ** open Prelude, (Predef=Predef) in { nom = "ty" ; voc = "ty" ; dep = table { -- it is simplyfied to avoid variants + NomPrep => "ty"; GenNoPrep => "cię"; GenPrep => "ciebie"; DatNoPrep => "tobie"; @@ -133,6 +135,7 @@ resource PronounMorphoPol = ResPol ** open Prelude, (Predef=Predef) in { { nom = "pan" ; voc = "panie" ; dep = table { + NomPrep => "pan"; GenNoPrep => "pana"; --"go"}; GenPrep => "pana"; DatNoPrep => "panu"; --"mu"}; @@ -152,6 +155,7 @@ resource PronounMorphoPol = ResPol ** open Prelude, (Predef=Predef) in { { nom = "pani" ; voc = "pani" ; dep = table { + NomPrep => "pani"; GenNoPrep => "pani"; --"go"}; GenPrep => "pani"; DatNoPrep => "pani"; --"mu"}; @@ -171,6 +175,7 @@ resource PronounMorphoPol = ResPol ** open Prelude, (Predef=Predef) in { { nom = "on" ; voc = "on" ; dep = table { + NomPrep => "on"; GenNoPrep => "jego"; --"go"}; GenPrep => "niego"; DatNoPrep => "jemu"; --"mu"}; @@ -191,6 +196,7 @@ resource PronounMorphoPol = ResPol ** open Prelude, (Predef=Predef) in { { nom = "ona" ; voc = "ona" ; dep = table { + NomPrep => "ona"; GenNoPrep => "jej"; GenPrep => "niej"; DatNoPrep => "jej"; @@ -247,6 +253,7 @@ resource PronounMorphoPol = ResPol ** open Prelude, (Predef=Predef) in { nom = "ono" ; -- The true nom. and voc. forms will be that of the subject voc = "ono" ; -- Here, we use the neuter pronoun as a shortcut dep = table { + NomPrep => "ono"; GenNoPrep => "się"; GenPrep => "siebie"; DatNoPrep => "sobie"; @@ -265,6 +272,7 @@ resource PronounMorphoPol = ResPol ** open Prelude, (Predef=Predef) in { { nom = "ono" ; voc = "ono" ; dep= table { + NomPrep => "ono"; GenNoPrep => "jego"; --"go"; GenPrep => "niego"; DatNoPrep => "jemu"; --"mu"; @@ -285,6 +293,7 @@ resource PronounMorphoPol = ResPol ** open Prelude, (Predef=Predef) in { { nom = "my"; voc = "my"; dep = table { + NomPrep => "my"; (GenNoPrep|GenPrep) => "nas"; (DatNoPrep|DatPrep) => "nam"; (AccNoPrep|AccPrep) => "nas"; @@ -338,6 +347,7 @@ resource PronounMorphoPol = ResPol ** open Prelude, (Predef=Predef) in { { nom = "wy" ; voc = "wy" ; dep = table { + NomPrep => "wy"; (GenNoPrep|GenPrep) => "was"; (DatNoPrep|DatPrep) => "wam"; (AccNoPrep|AccPrep) => "was"; @@ -391,6 +401,7 @@ resource PronounMorphoPol = ResPol ** open Prelude, (Predef=Predef) in { { nom = "oni" ; voc = "oni" ; dep = table { + NomPrep => "oni"; GenNoPrep => "ich"; GenPrep => "nich"; DatNoPrep => "im"; @@ -411,6 +422,7 @@ resource PronounMorphoPol = ResPol ** open Prelude, (Predef=Predef) in { { nom = "one" ; voc = "one" ; dep = table { + NomPrep => "one"; GenNoPrep => "ich"; GenPrep => "nich"; DatNoPrep => "im"; @@ -429,6 +441,7 @@ resource PronounMorphoPol = ResPol ** open Prelude, (Predef=Predef) in { { nom = "one" ; voc = "one" ; dep = table { + NomPrep => "one"; GenNoPrep => "ich"; GenPrep => "nich"; DatNoPrep => "im"; diff --git a/src/polish/ResPol.gf b/src/polish/ResPol.gf index 422b2662..dda7c57f 100644 --- a/src/polish/ResPol.gf +++ b/src/polish/ResPol.gf @@ -189,23 +189,23 @@ --6 Complement definition param ComplCase = GenPrep | GenNoPrep | DatPrep | DatNoPrep | - AccPrep | AccNoPrep | InstrC | LocPrep ; + AccPrep | AccNoPrep | InstrC | LocPrep | NomPrep ; oper Complement : Type = {s : Str; c : ComplCase} ; mkCompl : Str -> Case -> Complement; - mkCompl s c = { - s=s; - c = case s of { - "" => case c of { Gen => GenNoPrep; Dat => DatNoPrep; Instr => InstrC; _ => AccNoPrep }; - _ => case c of { Gen => GenPrep; Dat => DatPrep; Acc => AccPrep; Instr => InstrC; _ => LocPrep } + mkCompl s c = { + s=s; + c = case s of { + "" => case c of { Gen => GenNoPrep; Dat => DatNoPrep; Instr => InstrC; Nom => NomPrep; _ => AccNoPrep }; + _ => case c of { Gen => GenPrep; Dat => DatPrep; Acc => AccPrep; Instr => InstrC; Nom => NomPrep; _ => LocPrep } } }; extract_case = table {GenPrep => Gen; GenNoPrep => Gen; DatPrep => Dat; DatNoPrep => Dat; AccPrep => Acc; AccNoPrep => Acc; InstrC => Instr; - LocPrep => Loc}; + LocPrep => Loc; NomPrep => Nom}; --7 Various types -- possible problem: dzieci ,ktorych piecioro bawilo sie... / okna, ktorych piec stalo opartych o sciane... diff --git a/src/polish/SentencePol.gf b/src/polish/SentencePol.gf index 5a231376..ee8ed4e6 100644 --- a/src/polish/SentencePol.gf +++ b/src/polish/SentencePol.gf @@ -75,6 +75,9 @@ lin -- AdvS : Adv -> S -> S ; -- today, I will go home AdvS adv s = { s = adv.s ++ s.s }; ExtAdvS adv s = { s = adv.s ++ "," ++ s.s }; + +-- SSubjS : S -> Subj -> S -> S ; -- I go home, if she comes + SSubjS a subj b = { s = a.s ++ "," ++ subj.s ++ b.s }; -- SlashPrep : Cl -> Prep -> ClSlash ; -- (with whom) he walks SlashPrep c p = { s=c.s; c=p }; diff --git a/src/polish/StructuralPol.gf b/src/polish/StructuralPol.gf index a8ee06b0..d4b80eda 100644 --- a/src/polish/StructuralPol.gf +++ b/src/polish/StructuralPol.gf @@ -87,6 +87,9 @@ lin something_NP = cos ; somewhere_Adv = ss "gdzieś"; that_Quant = demPronTen "tamten"; + -- Polish always sets off a "że" clause with a comma, so the subordinator + -- carries it, as IdiomPol/SentencePol already do for EmbedS and SlashVS + that_Subj = ss [", że"]; there_Adv = ss "tam"; there7to_Adv = ss "tam"; there7from_Adv = ss "stamtąd"; diff --git a/src/polish/VerbPol.gf b/src/polish/VerbPol.gf index 0f9e41c7..57c05539 100644 --- a/src/polish/VerbPol.gf +++ b/src/polish/VerbPol.gf @@ -40,7 +40,9 @@ lin -- CompAP : AP -> Comp ; -- (be) small CompAP ap = { s = \\gn => ap.s ! AF gn Nom }; - CompCN cn = { s = \\gn => cn.s ! numGenNum gn ! Nom }; --- AR 7/12/2010 + -- a predicative noun goes in the instrumental in Polish, exactly as in + -- CompNP below: "x jest grupą", not *"x jest grupa" + CompCN cn = { s = \\gn => cn.s ! numGenNum gn ! Instr }; --- AR 7/12/2010 -- CompNP : NP -> Comp ; -- (be) a man CompNP np = { s = \\gn => np.dep !InstrC }; From 363219730e66e0995e52cbd88c014bb619913f88 Mon Sep 17 00:00:00 2001 From: aarneranta Date: Thu, 16 Jul 2026 19:50:20 +0200 Subject: [PATCH 10/16] polish: put the AdvVP adverbial after the verb MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AdvVP built its result with setPrefix, so the adverbial landed before the verb. Polish is neutrally SVO, and this matters beyond adverbs proper: an object reached through PrepNP arrives at AdvVP as an Adv, so a two-place verb came out as *"π zbiór pusty przecina" rather than "π przecina zbiór pusty". Both orders are grammatical, but the preverbal one topicalizes. "śpi tutaj" is as good as "tutaj śpi", so the RGL's own example is unaffected. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/polish/VerbPol.gf | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/polish/VerbPol.gf b/src/polish/VerbPol.gf index 57c05539..5479ce11 100644 --- a/src/polish/VerbPol.gf +++ b/src/polish/VerbPol.gf @@ -28,7 +28,11 @@ lin vps.sufix!p!gn ++ vps.c.s ++ np.dep !(npcase !) ++ vps.postfix!p!gn); -- AdvVP : VP -> Adv -> VP ; -- sleep here - AdvVP vp adv = setPrefix vp (vp.prefix ++ adv.s); + -- the adverbial follows the verb: Polish is neutrally SVO ("śpi tutaj", + -- "przecina zbiór pusty"), and putting it in the prefix topicalizes it. + -- This matters because an object reached through PrepNP arrives here as + -- an Adv, and *"π zbiór pusty przecina" is marked at best. + AdvVP vp adv = setSufix vp (\\p,gn => vp.sufix ! p ! gn ++ adv.s); -- AdVVP : AdV -> VP -> VP ; -- always sleep AdVVP adV vp = setPrefix vp (vp.prefix ++ adV.s); From 83cfc496a407e3c8fbbcb3622700accf433c0d3a Mon Sep 17 00:00:00 2001 From: adelon <22380201+adelon@users.noreply.github.com> Date: Tue, 21 Jul 2026 11:10:31 +0200 Subject: [PATCH 11/16] Fin: fix elative of `ne` --- src/finnish/MorphoFin.gf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/finnish/MorphoFin.gf b/src/finnish/MorphoFin.gf index c2c1b6c9..e54293a8 100644 --- a/src/finnish/MorphoFin.gf +++ b/src/finnish/MorphoFin.gf @@ -1064,7 +1064,7 @@ oper Transl => "niiksi" ; Ess => "niinä" ; Iness => "niissä" ; - Elat => "niitä" ; + Elat => "niistä" ; Illat => "niihin" ; Adess => "niillä" ; Ablat => "niiltä" ; From 00790d98faf5197a1485b88db282726c263eeadb Mon Sep 17 00:00:00 2001 From: adelon <22380201+adelon@users.noreply.github.com> Date: Tue, 21 Jul 2026 11:45:15 +0200 Subject: [PATCH 12/16] Eng: fix simple past of stride and bestride --- src/english/IrregEng.gf | 2 +- src/morphodict/MorphoDictEng.gf | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/english/IrregEng.gf b/src/english/IrregEng.gf index 7b9c48dd..71f3b5cf 100644 --- a/src/english/IrregEng.gf +++ b/src/english/IrregEng.gf @@ -149,7 +149,7 @@ flags optimize=values ; stick_V = irregV "stick" "stuck" "stuck" ; sting_V = irregV "sting" "stung" "stung" ; stink_V = irregV "stink" "stank" "stunk" ; - stride_V = irregV "stride" "strod" "stridden" ; + stride_V = irregV "stride" "strode" "stridden" ; strike_V = irregV "strike" "struck" "struck" ; string_V = irregV "string" "strung" "strung" ; strive_V = irregV "strive" "strove" "striven" ; diff --git a/src/morphodict/MorphoDictEng.gf b/src/morphodict/MorphoDictEng.gf index 7ad0670e..000fa227 100644 --- a/src/morphodict/MorphoDictEng.gf +++ b/src/morphodict/MorphoDictEng.gf @@ -5345,7 +5345,7 @@ lin bestir_V = mkV "bestir" "bestirred" "bestirred" ; lin bestow_V = mkV "bestow" "bestowed" "bestowed" ; lin bestowal_N = mkN "bestowal" "bestowals" ; lin bestrew_V = mkV "bestrew" "bestrewed" "bestrewed" ; -lin bestride_V = mkV "bestride" "bestrod" "bestridden" ; +lin bestride_V = mkV "bestride" "bestrode" "bestridden" ; lin bestubbled_A = mkAMost "bestubbled" "bestubbledly" ; lin bet_N = mkN "bet" "bets" ; lin bet_V = mkV "bet" "betted" "betted" ; @@ -48049,7 +48049,7 @@ lin strict_A = mkA "strict" "stricter" "strictest" "strictly" ; lin strictness_N = mkN "strictness" "strictnesses" ; lin stricture_N = mkN "stricture" "strictures" ; lin stride_N = mkN "stride" "strides" ; -lin stride_V = mkV "stride" "strod" "stridden" ; +lin stride_V = mkV "stride" "strode" "stridden" ; lin strident_A = mkAMost "strident" "stridently" ; lin strider_N = mkN "strider" "striders" ; lin stridor_N = mkN "stridor" "stridors" ; From d947e7e2e2aef8def75b91ee78917f92b939f488 Mon Sep 17 00:00:00 2001 From: adelon <22380201+adelon@users.noreply.github.com> Date: Tue, 21 Jul 2026 12:47:47 +0200 Subject: [PATCH 13/16] Fin: fix comparative and superlative adverbs --- src/finnish/StemFin.gf | 2 +- src/finnish/stemmed/StemFin.gf | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/finnish/StemFin.gf b/src/finnish/StemFin.gf index 5e02a9ad..52efc354 100644 --- a/src/finnish/StemFin.gf +++ b/src/finnish/StemFin.gf @@ -76,7 +76,7 @@ oper let tuoree = init (tuore.s ! NCase Sg Gen) ; tuoreesti = tuoree + "sti" ; - tuoreemmin = init tuoree ; + tuoreemmin = init tuoree + "in" ; in {s = table { AN f => tuore.s ! f ; AAdv => if_then_Str isPos tuoreesti tuoreemmin diff --git a/src/finnish/stemmed/StemFin.gf b/src/finnish/stemmed/StemFin.gf index 466bb797..3759e7f2 100644 --- a/src/finnish/stemmed/StemFin.gf +++ b/src/finnish/stemmed/StemFin.gf @@ -148,7 +148,7 @@ oper let tuoree = tuore.s ! 1 ; tuoreesti = tuoree + "sti" ; - tuoreemmin = init tuoree ; + tuoreemmin = init tuoree + "in" ; in {s = table { SAN f => tuore.s ! f ; SAAdv => if_then_Str isPos tuoreesti tuoreemmin From 498acd0b865480f7f3433bb3dc91095104f1674f Mon Sep 17 00:00:00 2001 From: adelon <22380201+adelon@users.noreply.github.com> Date: Tue, 21 Jul 2026 12:47:47 +0200 Subject: [PATCH 14/16] Fin: fix `probable_AS` inflection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Model `todennäköinen` as a compound so its final component controls vowel harmony. Keep `compoundA.p` empty unless the wrapped adjective has prefix-only modifying use. --- src/finnish/LexiconFin.gf | 3 +-- src/finnish/ParadigmsFin.gf | 10 +++++++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/finnish/LexiconFin.gf b/src/finnish/LexiconFin.gf index c132c799..bf20cf4f 100644 --- a/src/finnish/LexiconFin.gf +++ b/src/finnish/LexiconFin.gf @@ -148,8 +148,7 @@ lin play_V2 = mkV2 (mkV "pelata") cpartitive ; --- leikkiä, soittaa policeman_N = mkN "poliisi" ; priest_N = mkN "pappi" ; - probable_AS = mkAS --- for vowel harmony - (mkA (mkN "todennäköinen") "tonennäköisempi" "todennäköisin") ; ---- sta + probable_AS = mkAS (compoundA "toden" (mkA "näköinen")) ; queen_N = mkN "kuningatar" ; radio_N = mk2N "radio" "radioita" ; rain_V0 = mkV0 (mk2V "sataa" "satoi") ; diff --git a/src/finnish/ParadigmsFin.gf b/src/finnish/ParadigmsFin.gf index d44d9de2..1e8e63a4 100644 --- a/src/finnish/ParadigmsFin.gf +++ b/src/finnish/ParadigmsFin.gf @@ -221,7 +221,15 @@ oper = \s -> lin A {s = \\_,_ => s ; h = Back ; p = [] ; hasPrefix = False} ; ----- stemming adds bogus endings compoundA : Str -> A -> A -- prefix glued to adjective, e.g. "hevos"+"vetoinen" - = \s,a -> lin A {s = \\d,c => s + a.s ! d ! c ; h = a.h ; p = s + a.p ; hasPrefix = a.hasPrefix} ; + = \s,a -> lin A { + s = \\d,c => s + a.s ! d ! c ; + h = a.h ; + p = case a.hasPrefix of { + True => s + a.p ; + False => [] + } ; + hasPrefix = a.hasPrefix + } ; prefixA : Str -> A -> A -- in modifying use, an uninflected glued prefix, e.g. "sähkö" for "sähköinen" = \pr,a -> a ** { From 110266306d5cbaff3d9de8775ac5d898cddb44ed Mon Sep 17 00:00:00 2001 From: adelon <22380201+adelon@users.noreply.github.com> Date: Tue, 21 Jul 2026 12:49:34 +0200 Subject: [PATCH 15/16] Fin: fix singular essive of `tuhatN` --- src/finnish/NumeralFin.gf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/finnish/NumeralFin.gf b/src/finnish/NumeralFin.gf index 17d1ee49..18a4ce83 100644 --- a/src/finnish/NumeralFin.gf +++ b/src/finnish/NumeralFin.gf @@ -142,7 +142,7 @@ oper (ordN "a" "sadas") ; tuhatN = co - (snoun2nounBind (mkN "tuhat" "tuhannen" "tuhatta" "ruhantena" "tuhanteen" + (snoun2nounBind (mkN "tuhat" "tuhannen" "tuhatta" "tuhantena" "tuhanteen" "tuhansien" "tuhansia" "tuhansina" "tuhansissa" "tuhansiin")) (ordN "a" "tuhannes") ; From 827a0296c99d441ed643f9e7eafd4d7d4024d709 Mon Sep 17 00:00:00 2001 From: adelon <22380201+adelon@users.noreply.github.com> Date: Tue, 21 Jul 2026 12:52:55 +0200 Subject: [PATCH 16/16] Fin: fix vowel harmony of millionth and billionth --- src/finnish/NumeralFin.gf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/finnish/NumeralFin.gf b/src/finnish/NumeralFin.gf index 18a4ce83..2c7c2a8a 100644 --- a/src/finnish/NumeralFin.gf +++ b/src/finnish/NumeralFin.gf @@ -173,7 +173,7 @@ oper miljoonaN = co (snoun2nounBind (mkN "miljoona")) - (ordN "s" "miljoonas") ; + (ordN "a" "miljoonas") ; miljoonaaN = {s = table { Sg => miljoonaN.s ; @@ -186,7 +186,7 @@ oper miljardiN = co (snoun2nounBind (mkN "miljardi")) - (ordN "s" "miljardis") ; + (ordN "a" "miljardis") ; miljardiaN = {s = table { Sg => miljardiN.s ;