Skip to content

Recognize month ranges fused into a single word - #63

Merged
vthorsteinsson merged 2 commits into
masterfrom
month-range-tokens
Sep 2, 2026
Merged

vthorsteinsson merged 2 commits into
masterfrom
month-range-tokens

Conversation

@vthorsteinsson

Copy link
Copy Markdown
Member

A tight month-month range such as "ágúst-september" — common in phrases like "um mánaðamótin ágúst-september" — is glued into a single WORD token by the hyphenated-compound handling before date recognition can see it, so downstream consumers (e.g. the GreynirEngine parser) never receive the month names.

parse_date_and_time() now splits such a word into DATEREL + hyphen + DATEREL, for both the ASCII hyphen and the en dash ("júlí–ágúst"). This matches what the tokenizer already produces for the spaced and slashed variants ("ágúst - september", "ágúst/september").

Performance: the guard is deliberately cheap — on the common path a WORD token costs only C-level in substring scans; the split, two frozenset checks and two dict lookups run only for the rare hyphen-bearing words whose halves might be months.

Correctly left untouched: ambiguous month abbreviations ("mar-apr"), the capitalized-Ágúst blacklist ("Ágúst-september"), ordinary hyphenated words ("vestur-þýska"), and the "fjármála-og efnahagsráðuneyti" composite path.

Full test suite passes (151 tests, including 5 new cases).

🤖 Generated with Claude Code

A tight month-month range such as "ágúst-september" (common in
phrases like "um mánaðamótin ágúst-september") was glued into a
single WORD token by hyphenated-compound handling before date
recognition could see it, so downstream parsers never received the
month names. parse_date_and_time() now splits such a word into
DATEREL, hyphen and DATEREL, for both the ASCII hyphen and the en
dash. The check is guarded cheaply: only WORD tokens containing a
hyphen character are examined further, and the month lookup only
runs on a two-part split whose halves are not ambiguous month names
("mar-apr" and capitalized "Ágúst-..." are left alone, consistent
with single-month behavior).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@vthorsteinsson vthorsteinsson left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fix performance issues

Comment thread src/tokenizer/tokenizer.py Outdated
a = token.txt.split(hchar)
if (
len(a) == 2
and a[0] not in AMBIGUOUS_MONTH_NAMES

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

more efficient: (a0 := a[0]) not in AMBIGUOUS_MONTH_NAMES

Comment thread src/tokenizer/tokenizer.py Outdated
if (
len(a) == 2
and a[0] not in AMBIGUOUS_MONTH_NAMES
and a[1] not in AMBIGUOUS_MONTH_NAMES

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

more efficient: (a1 := a[1]) not in AMBIGUOUS_MONTH_NAMES

Comment thread src/tokenizer/tokenizer.py Outdated
and a[0] not in AMBIGUOUS_MONTH_NAMES
and a[1] not in AMBIGUOUS_MONTH_NAMES
):
m1 = MONTHS.get(a[0].lower())

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

a0.lower()

Comment thread src/tokenizer/tokenizer.py Outdated
and a[1] not in AMBIGUOUS_MONTH_NAMES
):
m1 = MONTHS.get(a[0].lower())
m2 = MONTHS.get(a[1].lower())

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

a1.lower()

Comment thread src/tokenizer/tokenizer.py Outdated
m1 = MONTHS.get(a[0].lower())
m2 = MONTHS.get(a[1].lower())
if m1 is not None and m2 is not None:
first, rest = token.split(len(a[0]))

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Delete the assignments to first, rest, hyphen, second - they're superfluous

Comment thread src/tokenizer/tokenizer.py Outdated
if m1 is not None and m2 is not None:
first, rest = token.split(len(a[0]))
hyphen, second = rest.split(1)
yield TOK.Daterel(first, y=0, m=m1, d=0)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

a0 instead of first

Comment thread src/tokenizer/tokenizer.py Outdated
first, rest = token.split(len(a[0]))
hyphen, second = rest.split(1)
yield TOK.Daterel(first, y=0, m=m1, d=0)
yield TOK.Punctuation(hyphen)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

hchar instead of hyphen

Comment thread src/tokenizer/tokenizer.py Outdated
hyphen, second = rest.split(1)
yield TOK.Daterel(first, y=0, m=m1, d=0)
yield TOK.Punctuation(hyphen)
token = TOK.Daterel(second, y=0, m=m2, d=0)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

a1 instead of second

Capture the two halves with walrus assignments, reuse them for the
month lookups, and construct the emitted tokens directly from the
strings instead of slicing the original token.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vthorsteinsson
vthorsteinsson merged commit 8fdf4b1 into master Sep 2, 2026
12 checks passed
@vthorsteinsson
vthorsteinsson deleted the month-range-tokens branch September 2, 2026 18:24
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.

1 participant