Skip to content

Add secondary longest-suffix exemplar guessing for Serbian#197

Open
nciric wants to merge 4 commits into
unicode-org:mainfrom
nciric:serbian-suffix-guessing
Open

Add secondary longest-suffix exemplar guessing for Serbian#197
nciric wants to merge 4 commits into
unicode-org:mainfrom
nciric:serbian-suffix-guessing

Conversation

@nciric

@nciric nciric commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

I added secondary longest-suffix exemplar guessing for Serbian nouns when rule-based guessing doesn't inflect a word missing from the dictionary.

Key changes:

  • Created suffix_sr.csv mapping common Serbian Cyrillic noun endings (-ак, -њак, -ац, -ник, -ач, -ар, -бус, -ан, -ош, -иша, -ица, -ија, -ка, , -бло, , -ње, -ме) to Cyrillic dictionary exemplars.
  • Updated SrGrammarSynthesizer_SrDisplayFunction to fall back to suffixToExemplar + inflectExemplar if inflectWithRule returns the uninflected word.
  • Added 20 Cyrillic test cases in sr.xml covering unknown words with diverse endings.

@nciric
nciric force-pushed the serbian-suffix-guessing branch from 4fba7f7 to cc389e5 Compare July 20, 2026 21:21
@nciric
nciric requested a review from grhoten July 20, 2026 21:22

@grhoten grhoten left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you're going this route, you might want to enable ExemplarTest#testExemplarsInDictionary to ensure that your new table is valid. The TODO can be to enable it for the other languages.

The other suffix tables are a work in progress. Either the missing words need to be added, or the table needs to be regenerated.

@@ -0,0 +1,19 @@
ак,=,изузетак

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was this file manually generated or generated from genExemplars? It seems a little short.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI the other tables are mostly generated with some small manual tweaking.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was manually generated - I'll look into genExemplars, thanks for the pointer.

Comment on lines +167 to +172
// Primary guess: Rule based inflection for nouns.
::std::u16string ruleResult = inflectWithRule(constraints, displayString);
if (ruleResult != displayString) {
displayString = ruleResult;
} else {
// Secondary guess: Longest suffix exemplar guessing.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an interesting combination. Why not go all in for one or the other? What is the advantage of one over the other?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rule based inflection is going to give the correct answer for words not in the dictionary (with minor exceptions).
Some of the groups are hard to implement, or require more grammatical information to do properly - the longest suffix match is replacement for those for now. I expect the results to be occasionally incorrect, so it's the least preferred approach of the 3 (dictionary, rules, suffix guess) wrt correctness, but easy to implement :).

@nciric nciric Jul 21, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I actually went and implemented the other 3 cases and it wasn't that bad.
I hit a snag on this one as the suffix matching would need to be hand curated to avoid some of the special cases and with the PR #198 this PR is not needed.

Let's discuss it tomorrow.

Comment on lines +173 to +179
auto countString = GrammarSynthesizerUtil::getFeatureValue(constraints, numberFeature);
auto caseString = GrammarSynthesizerUtil::getFeatureValue(constraints, caseFeature);
auto genderString = GrammarSynthesizerUtil::getFeatureValue(constraints, genderFeature);
std::vector<::std::u16string> constraintsVec;
if (!countString.empty()) constraintsVec.push_back(countString);
if (!caseString.empty() && caseString != GrammemeConstants::CASE_NOMINATIVE) constraintsVec.push_back(caseString);
if (!genderString.empty()) constraintsVec.push_back(genderString);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would have expected these values to already be derived. Why are they being queried here?

Also please don't inline if statements. It makes it hard to debug, and get decent code coverage metrics.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants