nl2time 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +43 -2
- package/dist/corpus/runner.d.ts +3 -0
- package/dist/corpus/runner.js +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.js +5 -1
- package/dist/packs/index.d.ts +39 -0
- package/dist/packs/index.js +156 -0
- package/dist/parse/de.d.ts +9 -0
- package/dist/parse/de.js +1301 -0
- package/dist/parse/en.d.ts +9 -0
- package/dist/parse/en.js +80 -34
- package/dist/parse/es.d.ts +9 -0
- package/dist/parse/es.js +3286 -0
- package/dist/parse/fr.d.ts +9 -0
- package/dist/parse/fr.js +2358 -0
- package/dist/parse/index.d.ts +16 -1
- package/dist/parse/index.js +42 -11
- package/dist/parse/ja.d.ts +7 -0
- package/dist/parse/ja.js +1207 -0
- package/dist/parse/languages.d.ts +17 -0
- package/dist/parse/languages.js +25 -0
- package/dist/parse/latin.d.ts +97 -0
- package/dist/parse/latin.js +692 -0
- package/dist/parse/tokenizer.js +30 -1
- package/dist/parse/zh.d.ts +6 -0
- package/dist/parse/zh.js +1360 -0
- package/package.json +7 -3
package/dist/parse/en.d.ts
CHANGED
|
@@ -15,4 +15,13 @@ export interface RuleMatch {
|
|
|
15
15
|
}
|
|
16
16
|
export type Rule = (tokens: Token[], i: number, ctx: TimeContext) => RuleMatch | undefined;
|
|
17
17
|
/** Rules in priority order (first match at a position wins ties by length then order). */
|
|
18
|
+
/**
|
|
19
|
+
* Built-in English rules with stable public names (docs/extending.md).
|
|
20
|
+
* Order matters: the scanner prefers longer matches, then higher confidence,
|
|
21
|
+
* then earlier list position — so names are also the disable/override handle.
|
|
22
|
+
*/
|
|
23
|
+
export declare const EN_RULE_ENTRIES: readonly {
|
|
24
|
+
name: string;
|
|
25
|
+
rule: Rule;
|
|
26
|
+
}[];
|
|
18
27
|
export declare const EN_RULES: readonly Rule[];
|
package/dist/parse/en.js
CHANGED
|
@@ -125,12 +125,17 @@ function yearAt(tokens, i) {
|
|
|
125
125
|
? t.value
|
|
126
126
|
: undefined;
|
|
127
127
|
}
|
|
128
|
-
/** "in the morning" / "at night" / "tonight" lookahead.
|
|
129
|
-
|
|
128
|
+
/** "in the morning" / "at night" / "this morning" / "tonight" lookahead.
|
|
129
|
+
* Returns period + consumed. "this <period>" binds a preceding bare hour
|
|
130
|
+
* exactly like "in the <period>" ("8 this morning" → 8am): the period
|
|
131
|
+
* supplies the meridiem, and the reference day is the anchor either way.
|
|
132
|
+
* Deliberately NOT "last night": that also shifts the day, which a
|
|
133
|
+
* meridiem-only suffix can't express (#9). */
|
|
134
|
+
function readPeriodSuffix(tokens, i, allowThis = true) {
|
|
130
135
|
let at = i;
|
|
131
136
|
if (word(tokens[at]) === 'in' || word(tokens[at]) === 'at')
|
|
132
137
|
at += 1;
|
|
133
|
-
if (word(tokens[at]) === 'the')
|
|
138
|
+
if (word(tokens[at]) === 'the' || (allowThis && word(tokens[at]) === 'this'))
|
|
134
139
|
at += 1;
|
|
135
140
|
const w = word(tokens[at]);
|
|
136
141
|
if (w === 'tonight')
|
|
@@ -143,6 +148,12 @@ function readPeriodSuffix(tokens, i) {
|
|
|
143
148
|
function meridiemFor(period) {
|
|
144
149
|
return period === 'morning' ? 'am' : 'pm';
|
|
145
150
|
}
|
|
151
|
+
/** Words that license a following bare number as a clock time
|
|
152
|
+
* ("at 8 this morning", "by 5 this afternoon"). */
|
|
153
|
+
const TIME_PREPOSITIONS = new Set([
|
|
154
|
+
'at', 'around', 'about', 'by', 'from', 'until', 'till', 'to',
|
|
155
|
+
'before', 'after', 'since',
|
|
156
|
+
]);
|
|
146
157
|
// --- rules -----------------------------------------------------------------
|
|
147
158
|
/** today / yesterday / tomorrow / tonight / day before yesterday / day after tomorrow */
|
|
148
159
|
const ruleDeicticDay = (tokens, i) => {
|
|
@@ -2353,7 +2364,17 @@ const ruleClockTime = (tokens, i, ctx) => {
|
|
|
2353
2364
|
if (after === "o'clock" || after === 'oclock') {
|
|
2354
2365
|
return withPeriodSuffix({ hour: hn.value, meridiem: 'unknown' }, hn.consumed + 1);
|
|
2355
2366
|
}
|
|
2356
|
-
|
|
2367
|
+
// A BARE number before "this <period>" is only a clock time when the
|
|
2368
|
+
// number isn't already bound by a preceding noun — "8 this morning"
|
|
2369
|
+
// is 8am, but in "building 4 this afternoon" the 4 is a building
|
|
2370
|
+
// number and the period keeps its range reading (Recognizers
|
|
2371
|
+
// rt-dtm-0523). Utterance start or a time-preposition before the
|
|
2372
|
+
// number licenses the binding; an arbitrary preceding word does not.
|
|
2373
|
+
// The in/at forms ("4 in the afternoon") are explicit enough to bind
|
|
2374
|
+
// regardless, as before.
|
|
2375
|
+
const prevWord = word(tokens[i - 1]);
|
|
2376
|
+
const allowThis = i === 0 || prevWord === undefined || TIME_PREPOSITIONS.has(prevWord);
|
|
2377
|
+
const ps = readPeriodSuffix(tokens, i + hn.consumed + mc, allowThis);
|
|
2357
2378
|
if (ps) {
|
|
2358
2379
|
const time = { hour: hn.value, meridiem: meridiemFor(ps.period) };
|
|
2359
2380
|
if (minute !== undefined)
|
|
@@ -2383,6 +2404,25 @@ const ruleClockTime = (tokens, i, ctx) => {
|
|
|
2383
2404
|
role: 'time',
|
|
2384
2405
|
};
|
|
2385
2406
|
}
|
|
2407
|
+
// Day-shifting period phrases: "10 last night", "8 yesterday morning",
|
|
2408
|
+
// "5 tomorrow afternoon" (#10). Emit the bare hour with meridiem
|
|
2409
|
+
// 'unknown' and stop — the following day-part matches separately and the
|
|
2410
|
+
// refiner merges them, with the period supplying the meridiem and the
|
|
2411
|
+
// deictic supplying the day shift. Same noun-guard as "this <period>":
|
|
2412
|
+
// "the count was 4 last night" keeps the period's range reading.
|
|
2413
|
+
const relNext = word(tokens[i + hn.consumed]);
|
|
2414
|
+
const isDayShifter = relNext !== undefined &&
|
|
2415
|
+
(REL_SYNONYMS[relNext] !== undefined ||
|
|
2416
|
+
relNext === 'yesterday' || relNext === 'tomorrow' || relNext === 'today');
|
|
2417
|
+
const relPeriodWord = isDayShifter ? word(tokens[i + hn.consumed + 1]) : undefined;
|
|
2418
|
+
if (allowThis && relPeriodWord !== undefined && PERIOD_WORDS[relPeriodWord]) {
|
|
2419
|
+
return {
|
|
2420
|
+
expr: { op: 'literal', time: { hour: hn.value, meridiem: 'unknown' } },
|
|
2421
|
+
consumed: hn.consumed,
|
|
2422
|
+
confidence: 0.95,
|
|
2423
|
+
role: 'time',
|
|
2424
|
+
};
|
|
2425
|
+
}
|
|
2386
2426
|
if (['at', 'from', 'around', 'about'].includes(word(tokens[i - 1]) ?? '')) {
|
|
2387
2427
|
return {
|
|
2388
2428
|
expr: { op: 'literal', time: { hour: hn.value, meridiem: 'unknown' } },
|
|
@@ -2675,34 +2715,40 @@ const ruleAllUnit = (tokens, i) => {
|
|
|
2675
2715
|
return { expr: { op: 'amount', amount: amountFor(unit, 1) }, consumed: 2, confidence: 0.9, role: 'duration' };
|
|
2676
2716
|
};
|
|
2677
2717
|
/** Rules in priority order (first match at a position wins ties by length then order). */
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2718
|
+
/**
|
|
2719
|
+
* Built-in English rules with stable public names (docs/extending.md).
|
|
2720
|
+
* Order matters: the scanner prefers longer matches, then higher confidence,
|
|
2721
|
+
* then earlier list position — so names are also the disable/override handle.
|
|
2722
|
+
*/
|
|
2723
|
+
export const EN_RULE_ENTRIES = [
|
|
2724
|
+
{ name: 'range', rule: ruleRange },
|
|
2725
|
+
{ name: 'week-of', rule: ruleWeekOf },
|
|
2726
|
+
{ name: 'nth-weekday-of', rule: ruleNthWeekdayOf },
|
|
2727
|
+
{ name: 'day-of-scope', rule: ruleDayOfScope },
|
|
2728
|
+
{ name: 'weekend-of', rule: ruleWeekendOf },
|
|
2729
|
+
{ name: 'holiday', rule: ruleHoliday },
|
|
2730
|
+
{ name: 'early-late', rule: ruleEarlyLate },
|
|
2731
|
+
{ name: 'end-of', rule: ruleEndOf },
|
|
2732
|
+
{ name: 'deictic-day', rule: ruleDeicticDay },
|
|
2733
|
+
{ name: 'within', rule: ruleWithin },
|
|
2734
|
+
{ name: 'n-after-date', rule: ruleNAfterDate },
|
|
2735
|
+
{ name: 'unit-after-next', rule: ruleUnitAfterNext },
|
|
2736
|
+
{ name: 'past-n', rule: rulePastN },
|
|
2737
|
+
{ name: 'next-n', rule: ruleNextN },
|
|
2738
|
+
{ name: 'the-n-past-next', rule: ruleTheNPastNext },
|
|
2739
|
+
{ name: 'the-unit', rule: ruleTheUnit },
|
|
2740
|
+
{ name: 'last-this-next', rule: ruleLastThisNext },
|
|
2741
|
+
{ name: 'ago-in', rule: ruleAgoIn },
|
|
2742
|
+
{ name: 'duration', rule: ruleDuration },
|
|
2743
|
+
{ name: 'fraction-duration', rule: ruleFractionDuration },
|
|
2744
|
+
{ name: 'all-unit', rule: ruleAllUnit },
|
|
2745
|
+
{ name: 'decade-century', rule: ruleDecadeCentury },
|
|
2746
|
+
{ name: 'bare-year', rule: ruleBareYear },
|
|
2747
|
+
{ name: 'calendar-date', rule: ruleCalendarDate },
|
|
2748
|
+
{ name: 'clock-time', rule: ruleClockTime },
|
|
2749
|
+
{ name: 'weekday-day', rule: ruleWeekdayDay },
|
|
2750
|
+
{ name: 'weekday-alone', rule: ruleWeekdayAlone },
|
|
2751
|
+
{ name: 'period-alone', rule: rulePeriodAlone },
|
|
2752
|
+
{ name: 'now', rule: ruleNow },
|
|
2708
2753
|
];
|
|
2754
|
+
export const EN_RULES = EN_RULE_ENTRIES.map((e) => e.rule);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Rule } from './en.js';
|
|
2
|
+
import { type LatinLexicon } from './latin.js';
|
|
3
|
+
export declare const ES_LEXICON: LatinLexicon;
|
|
4
|
+
export declare const ES_RULE_ENTRIES: {
|
|
5
|
+
name: string;
|
|
6
|
+
rule: Rule;
|
|
7
|
+
}[];
|
|
8
|
+
/** Connector words allowed between merged date & time parts. */
|
|
9
|
+
export declare const ES_CONNECTORS: string[];
|