fix: support Hunspell flag aliases and lazy affix expansion - #1160
fix: support Hunspell flag aliases and lazy affix expansion#1160fmagnucz wants to merge 2 commits into
Conversation
63b2143 to
4c0077c
Compare
|
Thanks for the work here. Two Hunspell compatibility gaps remain at 1. A zero affix with continuation flags keeps a literal
|
|
Hi @clemlesne, First of all, thank you for your comment. I have fixed your issue, and I have updated the PR description as well. But these problems already exists in the v3.19.0. I hope I didn't mess anything else up with this. :-) |
|
Hi @jdkato, What are your plans for this PR? Are you going to reject it, or merge it into the active branch? If you're going to merge it, please send me a message in case I need to resolve the merge conflicts. Thank you for your answer. Best regards, |
Problem
Vale previously expanded every Hunspell dictionary entry eagerly while loading
the dictionary. It generated all reachable prefixed, suffixed, cross-product,
and continuation forms, then retained every generated surface form in memory.
This approach becomes impractical for highly inflected languages. With the
Hungarian
hu_HUdictionary:1.7 GiB of memory.
Correctly parsing Hunspell flag aliases made this problem more visible because
the aliases enabled many affix rules that Vale had previously ignored.
The affected dictionaries also exposed several compatibility issues:
AFalias indices from.dicentries were interpreted as literal flags;AFalias indices in affix-rule continuation fields were also interpreted asliteral flags, so later suffixes in a chain were never reached;
.affand.dicfiles;expressions interpreted it as a range;
entry flags, causing valid Italian words such as
dito be rejected;so Unicode words could be skipped before dictionary lookup;
0/L, retained a literal0because zero normalization happened before the continuation was splitfrom the affix text;
Stripfield in the forward path, while the lazyreverse path removed the added prefix without restoring the stripped text.
The final two issues predated this change and were also reproducible with
v3.19.0, but the new lazy implementation needed to preserve the corrected
semantics in both its forward and reverse paths.
Solution
AFflag alias tables and resolve aliases in both.dicentries and affix continuation fields;
.affand.dicfiles;
0representation ofan empty affix;
Stripwhen generating prefixed forms and restore it when recoveringcandidate roots in the lazy reverse path;
derived surface form;
exact forward validation;
reverse-reachable candidates, avoiding the combinatorial expansion exposed by
real Hungarian continuation aliases;
homographs, and suggestions;
and uppercase checks while retaining the ASCII fast path;
cross-products, compounds, suggestions, the Italian
dicase, zero affixeswith continuation flags, and prefix stripping.
Follow-up regressions
A Hungarian AsciiDoc reproducer exposed that affix continuation values also use
the
AFalias table. Native Hunspell accepts the inflected formsszoftvertervezőt,képeslapot,lekérdezéseket,kigondolása,készítése,and
dokumentálása. Vale now accepts the same forms, while the intentional typoszoftvvvvertervezőtis still reported.A minimal
AF 2fixture verifies that a rule such asSFX A 0 ed/2 .resolvescontinuation alias
2instead of treating it as a literal flag.The default spelling filters now pass Unicode words to the dictionary checker
and still skip words ending in Unicode uppercase letters. The spelling
end-to-end expectation was updated to confirm that the Japanese token
オプションis no longer silently skipped.A follow-up review against
dictionary-fr@3.0.0exposed two additional,pre-existing Hunspell compatibility gaps.
First, a rule such as:
must produce an unchanged form carrying continuation flag
L. The parserpreviously checked for an exact
0before splitting the continuation, so0/Lbecame affix text
"0"with continuation"L". As a result, Vale rejectedl'ordinateurand accepted the invalid formsordinateur0andl'ordinateur0. Continuation flags are now split first, after which theremaining
0is normalized to an empty affix.Second, a prefix rule such as:
must strip the initial
abefore addingl'A, producingl'Amifromami.The forward path previously produced
l'Aami, and the lazy reverse path didnot restore the stripped
awhen recovering the dictionary root. Prefixstripping is now applied in the forward path and reversed when constructing
lazy lookup candidates.
The reviewer's minimal fixtures are covered by
TestZeroAffixWithContinuationFlagsandTestPrefixStrip. They verify thatVale accepts
l'ordinateurandl'Amiwhile rejectingordinateur0,l'ordinateur0, andl'Aami.Performance
Measurements used the system Hungarian
hu_HUdictionary.The eager full-dictionary figure is not presented as a completed benchmark:
the approximately 370-million-form value is an extrapolation from the
controlled 1,000-entry sample.
On the supplied Hungarian AsciiDoc reproducer, the pruned continuation lookup
completed in approximately 0.46–0.60 s. The earlier lookup took about 7.17 s,
while resolving continuation aliases without reverse-reachable pruning did not
complete within 90 s.
Automated testing
The following checks passed on the final implementation:
go test ./internal/spellgo test -race ./internal/spellgo vet ./internal/spellgo test ./internal/checkgo test ./internal/e2e -run 'TestScenarios/checks/spelling'go test ./internal/spell -run 'TestZeroAffixWithContinuationFlags|TestPrefixStrip' -count=1gofmton all changed Go filesgit diff --checkA full
go test ./...run was attempted after the French compatibilityfollow-up. The affected packages passed, but the complete run could not finish
successfully in the restricted environment because package synchronization
required unavailable network access and some core tests attempted to write to
the read-only global Vale styles directory. The generated Tree-sitter Lua
NUL-character compiler warning was non-fatal.
golangci-lintwas not availablelocally.
Cross-language smoke tests
Five languages with different flag formats and morphological characteristics
were tested:
en_USde_DEfromhunspell-de-de20161207-12it_ITfromhunspell-it1:24.2.1-1frfromhunspell-fr-classical1:7.0-1dictionary-fr@3.0.0ru_RUfromhunspell-ru1:24.2.1-1Each sentence was checked first with native Hunspell and then with the final
Vale CLI. In every language, the correct sentence produced no alerts and the
misspelled sentence produced exactly the same three alerts in native Hunspell
and Vale.
English —
en_USCorrect:
Misspelled:
Detected:
carefull,revievs,documentaton.German —
de_DECorrect:
Misspelled:
Detected:
Entwikler,Dokumentazion,zuverlassige.Italian —
it_ITCorrect:
Misspelled:
Detected:
svilupatrice,documentazzione,aggiornatto.The correct sentence deliberately includes
di, covering the Italiansingle-byte flag regression.
French —
dictionary-fr@3.0.0follow-upCorrect:
Misspelled:
Detected:
dévelopeuse,documentattion,programne.The correct sentence deliberately includes both
L'Amiandl'ordinateur.The former covers prefix stripping, while the latter covers a zero affix
carrying continuation flags. With v3.19.0, Vale reported both expressions as
misspellings. The final implementation produces no alerts, matching native
Hunspell.
Russian —
ru_RUCorrect:
Misspelled:
Detected:
разработтчик,внимателно,докуминтацию.