Skip to content

bg.sor: bracket/parenthesis transposed in ordinal-thousands rule (line 146) #139

Description

@Casara

Summary

data/bg.sor line 146 has a bracket and a closing parenthesis in the wrong
order. Read as a nesting tree, the bracket's ] ends up inside the
call's still-open parentheses, which a strict, tree-based Soros parser
rejects as unparseable - even though the reference implementations handle
this file today without complaint (see "Why the reference implementations
don't notice" below).

Location

data/bg.sor, line 146

Current:

([0-9]{1,3})([0-9]{3}) $(f:|$1) хиляди[ $(and:$(ordinal \2)])

Fixed:

([0-9]{1,3})([0-9]{3}) $(f:|$1) хиляди[ $(and:$(ordinal \2))]

Why this is a typo, not intentional

The current line ends \2)]) - the bracket closes before the call's
second closing parenthesis. Compare it with the singular counterpart one
line above, which already has it right:

1([0-9]{3}) хиляда[ $(and:$(ordinal \1))]

Both rules build the same "N thousand[ and REMAINDER-th]" shape. The
singular rule correctly closes both parentheses before the bracket
())]); the plural rule has those two characters transposed ()])).
That's a plain two-character transposition, verifiable by comparing
against a rule in the same file that already works - not a guess about
intent.

Why the reference implementations don't notice

They never parse [...] as a nesting construct at all. Optional blocks
are rewritten into call syntax by a text substitution, and then every
] is replaced by ), unconditionally, before any paren counting runs:

So a bracket never has to nest correctly relative to the call's own
parentheses: the crossing is absorbed by that substitution, and the
malformed line comes out balanced by accident. No specification says ]
and ) are interchangeable, which is why a parser that builds a real
tree has no principled way to accept the line without either guessing or
replicating that substitution.

The fix changes no output

Verified rather than assumed: running this project's own spellout
against the unpatched bg.sor produces exactly the same values as a
strict parser produces with the fix applied.

Input spellout -l bg -p ordinal (unpatched, upstream) With the fix
1023 хиляда двадесет и трети identical
2023 две хиляди двадесет и трети identical
5023 пет хиляди двадесет и трети identical

The change is therefore backward compatible with the C++/Java/Python
implementations: their pipeline never depended on this line's nesting
being wrong, so the fix only affects a parser that is strict about it.

Context

Found independently by two ports of this project's Soros engine, both of
which build a real syntax tree for templates and so reject the line:

  • numbertext-go - Go port,
    where it was first found. Write-up, including the engine-side
    workarounds considered and rejected before concluding this was a data
    typo rather than a parser gap, in its
    AGENTS.md.
  • numbertext-rs - Rust port
    (numbertext and
    soros on crates.io). Same
    conclusion reached separately; see its
    AGENTS.md.

Both carry the one-line change as a local patch, reapplied on every data
sync, and both have a CI check that fails once upstream no longer needs
it - which is how the eventual merge gets noticed.

Suggested fix

- ([0-9]{1,3})([0-9]{3}) $(f:|$1) хиляди[ $(and:$(ordinal \2)])
+ ([0-9]{1,3})([0-9]{3}) $(f:|$1) хиляди[ $(and:$(ordinal \2))]

Happy to open a PR if that is easier.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions