@lokascript/framework 4.6.0 → 4.8.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/CHANGELOG.md +121 -1
- package/dist/api/create-dsl.d.ts +18 -2
- package/dist/api/create-dsl.d.ts.map +1 -1
- package/dist/api/index.d.ts +1 -0
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/index.js +1799 -713
- package/dist/api/index.js.map +1 -1
- package/dist/api/translation-loss.d.ts +84 -0
- package/dist/api/translation-loss.d.ts.map +1 -0
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +1057 -684
- package/dist/core/index.js.map +1 -1
- package/dist/core/matching/confidence-model.d.ts +54 -0
- package/dist/core/matching/confidence-model.d.ts.map +1 -0
- package/dist/core/matching/index.d.ts +12 -0
- package/dist/core/matching/index.d.ts.map +1 -0
- package/dist/core/matching/match-skeleton.d.ts +114 -0
- package/dist/core/matching/match-skeleton.d.ts.map +1 -0
- package/dist/core/matching/stock-confidence.d.ts +15 -0
- package/dist/core/matching/stock-confidence.d.ts.map +1 -0
- package/dist/core/matching/stock-reader.d.ts +63 -0
- package/dist/core/matching/stock-reader.d.ts.map +1 -0
- package/dist/core/matching/stock-reading.d.ts +44 -0
- package/dist/core/matching/stock-reading.d.ts.map +1 -0
- package/dist/core/matching/value-reader.d.ts +62 -0
- package/dist/core/matching/value-reader.d.ts.map +1 -0
- package/dist/core/pattern-matching/index.js +561 -660
- package/dist/core/pattern-matching/index.js.map +1 -1
- package/dist/core/pattern-matching/pattern-matcher.d.ts +0 -117
- package/dist/core/pattern-matching/pattern-matcher.d.ts.map +1 -1
- package/dist/core/types.d.ts +53 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/generation/index.d.ts +2 -0
- package/dist/generation/index.d.ts.map +1 -1
- package/dist/generation/index.js +593 -1
- package/dist/generation/index.js.map +1 -1
- package/dist/generation/pattern-render.d.ts +72 -0
- package/dist/generation/pattern-render.d.ts.map +1 -0
- package/dist/generation/schema-patterns.d.ts +152 -0
- package/dist/generation/schema-patterns.d.ts.map +1 -0
- package/dist/index.cjs +2016 -904
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1998 -903
- package/dist/index.js.map +1 -1
- package/dist/ir/index.js.map +1 -1
- package/dist/parsing/index.js +561 -660
- package/dist/parsing/index.js.map +1 -1
- package/dist/testing/gate/index.d.ts +8 -0
- package/dist/testing/gate/index.d.ts.map +1 -0
- package/dist/testing/gate/index.js +664 -0
- package/dist/testing/gate/index.js.map +1 -0
- package/dist/testing/gate/lint/format.d.ts +7 -0
- package/dist/testing/gate/lint/format.d.ts.map +1 -0
- package/dist/testing/gate/lint/index.d.ts +30 -0
- package/dist/testing/gate/lint/index.d.ts.map +1 -0
- package/dist/testing/gate/lint/rules/extractor-coverage.d.ts +18 -0
- package/dist/testing/gate/lint/rules/extractor-coverage.d.ts.map +1 -0
- package/dist/testing/gate/lint/rules/index.d.ts +14 -0
- package/dist/testing/gate/lint/rules/index.d.ts.map +1 -0
- package/dist/testing/gate/lint/rules/keyword-classification.d.ts +19 -0
- package/dist/testing/gate/lint/rules/keyword-classification.d.ts.map +1 -0
- package/dist/testing/gate/lint/rules/keyword-coverage.d.ts +13 -0
- package/dist/testing/gate/lint/rules/keyword-coverage.d.ts.map +1 -0
- package/dist/testing/gate/lint/rules/marker-tokenization.d.ts +20 -0
- package/dist/testing/gate/lint/rules/marker-tokenization.d.ts.map +1 -0
- package/dist/testing/gate/lint/rules/position-ordering.d.ts +25 -0
- package/dist/testing/gate/lint/rules/position-ordering.d.ts.map +1 -0
- package/dist/testing/gate/lint/rules/renderer-coherence.d.ts +24 -0
- package/dist/testing/gate/lint/rules/renderer-coherence.d.ts.map +1 -0
- package/dist/testing/gate/lint/rules/schema-rules.d.ts +10 -0
- package/dist/testing/gate/lint/rules/schema-rules.d.ts.map +1 -0
- package/dist/testing/gate/lint/types.d.ts +74 -0
- package/dist/testing/gate/lint/types.d.ts.map +1 -0
- package/dist/testing/gate/round-trip.d.ts +184 -0
- package/dist/testing/gate/round-trip.d.ts.map +1 -0
- package/package.json +6 -2
- package/src/__test__/fixtures/sql-dsl.ts +8 -2
- package/src/api/create-dsl-engine.test.ts +150 -0
- package/src/api/create-dsl.test.ts +7 -0
- package/src/api/create-dsl.ts +173 -22
- package/src/api/index.ts +1 -0
- package/src/api/translation-loss.test.ts +89 -0
- package/src/api/translation-loss.ts +128 -0
- package/src/core/index.ts +1 -0
- package/src/core/matching/confidence-model.ts +58 -0
- package/src/core/matching/index.ts +12 -0
- package/src/core/matching/match-skeleton.test.ts +179 -0
- package/src/core/matching/match-skeleton.ts +621 -0
- package/src/core/matching/stock-confidence.ts +238 -0
- package/src/core/matching/stock-reader.test.ts +265 -0
- package/src/core/matching/stock-reader.ts +205 -0
- package/src/core/matching/stock-reading.ts +681 -0
- package/src/core/matching/value-reader.ts +85 -0
- package/src/core/pattern-matching/pattern-matcher.ts +11 -876
- package/src/core/types.ts +53 -0
- package/src/generation/index.ts +2 -0
- package/src/generation/pattern-render.ts +378 -0
- package/src/generation/schema-patterns.test.ts +139 -0
- package/src/generation/schema-patterns.ts +974 -0
- package/src/testing/gate/index.ts +8 -0
- package/src/testing/gate/lint/format.ts +23 -0
- package/src/testing/gate/lint/index.ts +73 -0
- package/src/testing/gate/lint/lint.test.ts +131 -0
- package/src/testing/gate/lint/rules/extractor-coverage.ts +63 -0
- package/src/testing/gate/lint/rules/index.ts +37 -0
- package/src/testing/gate/lint/rules/keyword-classification.ts +52 -0
- package/src/testing/gate/lint/rules/keyword-coverage.ts +38 -0
- package/src/testing/gate/lint/rules/marker-tokenization.ts +129 -0
- package/src/testing/gate/lint/rules/position-ordering.ts +87 -0
- package/src/testing/gate/lint/rules/renderer-coherence.ts +100 -0
- package/src/testing/gate/lint/rules/schema-rules.ts +35 -0
- package/src/testing/gate/lint/types.ts +83 -0
- package/src/testing/gate/round-trip.test.ts +208 -0
- package/src/testing/gate/round-trip.ts +503 -0
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The stock value reader: what a domain grammar's matcher reads at each role
|
|
3
|
+
* slot, on the matcher's skeleton (domains rethink Phase 4.7,
|
|
4
|
+
* `docs-internal/DOMAINS_RETHINK.md`). Hyperscript's reader is semantic's
|
|
5
|
+
* `HyperscriptValueReader`; a domain takes this one.
|
|
6
|
+
*
|
|
7
|
+
* - A **greedy** slot (`RoleSpec.greedy`: a SQL condition, a column list) reads
|
|
8
|
+
* every token up to the next marker the pattern still owes, or the end, as
|
|
9
|
+
* one expression (`readGreedy`).
|
|
10
|
+
* - A slot with a **word list** (`RoleSpec.words`: behaviorspec's interaction,
|
|
11
|
+
* voice's thing to close) reads only one of its words, compared lower-cased
|
|
12
|
+
* by value or normalized form; any other word leaves the slot unread.
|
|
13
|
+
* - Any other slot reads one value (`readRoleValue`, the old matcher's reading):
|
|
14
|
+
* a selector, a quoted string, a number, a boolean, a duration, a URL, an
|
|
15
|
+
* identifier, or one of the multi-token forms it knows (`user.name`,
|
|
16
|
+
* `#dialog.showModal()`), checked against the slot's expected types.
|
|
17
|
+
* - A **required** slot reads nothing where the stream is at the marker the
|
|
18
|
+
* pattern wants next (the old matcher's guard against an SOV marker taken as
|
|
19
|
+
* a value). An **optional** one reads nothing at any marker the rest of the
|
|
20
|
+
* pattern owes, through its optional groups (behaviorspec `when user types
|
|
21
|
+
* into #search`, whose typed text is optional and has no marker). Where a required slot finds nothing to read, and the optional,
|
|
22
|
+
* marker-less slot just before it read a value with nothing read since, that
|
|
23
|
+
* value is given back and the required slot reads again (the old matcher's
|
|
24
|
+
* backtrack: behaviorspec `when clicks`, where the optional actor took
|
|
25
|
+
* `clicks`; flow `orders として 取得変数`, where the optional patient took
|
|
26
|
+
* `orders`).
|
|
27
|
+
* - An **optional** slot may be skipped for a token the pattern wants next, the
|
|
28
|
+
* skip kept only when the rest of the pattern then reads to the end.
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
import type {
|
|
32
|
+
StreamMark,
|
|
33
|
+
LanguagePattern,
|
|
34
|
+
LanguageToken,
|
|
35
|
+
PatternToken,
|
|
36
|
+
SemanticRole,
|
|
37
|
+
SemanticValue,
|
|
38
|
+
TokenStream,
|
|
39
|
+
} from '../types';
|
|
40
|
+
import type { MatchView, RoleSlot, ValueReader } from './value-reader';
|
|
41
|
+
import { readRoleValue, type ReadingProfile } from './stock-reading';
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* The markers a greedy value stops at, from the pattern tokens still owed after
|
|
45
|
+
* it: the next literal (with its alternatives), and each optional group's
|
|
46
|
+
* leading literal up to and including the first required token.
|
|
47
|
+
*/
|
|
48
|
+
export function greedyStopMarkers(rest: readonly PatternToken[]): Set<string> {
|
|
49
|
+
const markers = new Set<string>();
|
|
50
|
+
const add = (pt: PatternToken & { type: 'literal' }): void => {
|
|
51
|
+
markers.add(pt.value.toLowerCase());
|
|
52
|
+
for (const alt of pt.alternatives ?? []) markers.add(alt.toLowerCase());
|
|
53
|
+
};
|
|
54
|
+
for (const pt of rest) {
|
|
55
|
+
if (pt.type === 'literal') {
|
|
56
|
+
add(pt);
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
if (pt.type === 'group') {
|
|
60
|
+
const lead = pt.tokens.find(t => t.type === 'literal');
|
|
61
|
+
if (lead?.type === 'literal') add(lead);
|
|
62
|
+
// For optional groups, continue scanning past them so the greedy capture
|
|
63
|
+
// also sees subsequent required literals (the SOV verb after an optional
|
|
64
|
+
// WHERE group).
|
|
65
|
+
if (!pt.optional) break;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return markers;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Whether `token` is one of the stop markers, by its value or its normalized form. */
|
|
72
|
+
function isStopMarker(token: LanguageToken, stopMarkers: ReadonlySet<string>): boolean {
|
|
73
|
+
if (stopMarkers.size === 0) return false;
|
|
74
|
+
if (stopMarkers.has(token.value.toLowerCase())) return true;
|
|
75
|
+
return !!token.normalized && stopMarkers.has(token.normalized.toLowerCase());
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Read every token up to a stop marker (or the end) as one expression value,
|
|
80
|
+
* advancing past them; null (consuming nothing) when the first token stops it.
|
|
81
|
+
*/
|
|
82
|
+
export function readGreedy(
|
|
83
|
+
tokens: TokenStream,
|
|
84
|
+
stopMarkers: ReadonlySet<string>
|
|
85
|
+
): SemanticValue | null {
|
|
86
|
+
const values: string[] = [];
|
|
87
|
+
while (!tokens.isAtEnd()) {
|
|
88
|
+
const next = tokens.peek();
|
|
89
|
+
if (!next || isStopMarker(next, stopMarkers)) break;
|
|
90
|
+
values.push(next.value);
|
|
91
|
+
tokens.advance();
|
|
92
|
+
}
|
|
93
|
+
return values.length > 0 ? { type: 'expression', raw: values.join(' ') } : null;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** The stock reader, for a matcher whose profiles it may consult (possessive words). */
|
|
97
|
+
/** Whether `token` is one of a slot's `words`, by value or normalized form, lower-cased. */
|
|
98
|
+
export function isListedWord(token: LanguageToken | null, words: readonly string[]): boolean {
|
|
99
|
+
if (!token) return false;
|
|
100
|
+
const forms = [token.value, token.normalized].filter((f): f is string => typeof f === 'string');
|
|
101
|
+
return forms.some(form => words.some(word => word.toLowerCase() === form.toLowerCase()));
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export class StockValueReader implements ValueReader {
|
|
105
|
+
private profile: ReadingProfile | undefined;
|
|
106
|
+
/** The last optional, marker-less slot that read a value: what a failing required slot may take back. */
|
|
107
|
+
private lastOptional: { role: SemanticRole; start: StreamMark; end: number } | undefined;
|
|
108
|
+
|
|
109
|
+
constructor(
|
|
110
|
+
private readonly view: MatchView,
|
|
111
|
+
private readonly profiles: Readonly<Record<string, ReadingProfile>> = {}
|
|
112
|
+
) {}
|
|
113
|
+
|
|
114
|
+
beginPattern(pattern: LanguagePattern): void {
|
|
115
|
+
this.profile = this.profiles[pattern.language] ?? { code: pattern.language };
|
|
116
|
+
this.lastOptional = undefined;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
readRole(
|
|
120
|
+
tokens: TokenStream,
|
|
121
|
+
slot: RoleSlot,
|
|
122
|
+
captured: Map<SemanticRole, SemanticValue>,
|
|
123
|
+
next: PatternToken | undefined
|
|
124
|
+
): boolean {
|
|
125
|
+
const start = tokens.mark();
|
|
126
|
+
if (this.readOnce(tokens, slot, captured, next, true)) {
|
|
127
|
+
if (!slot.optional) {
|
|
128
|
+
this.lastOptional = undefined;
|
|
129
|
+
} else if (
|
|
130
|
+
captured.has(slot.role) &&
|
|
131
|
+
tokens.position() > start.position &&
|
|
132
|
+
!this.view.slotAfterLiteral() &&
|
|
133
|
+
next?.type !== 'literal'
|
|
134
|
+
) {
|
|
135
|
+
this.lastOptional = { role: slot.role, start, end: tokens.position() };
|
|
136
|
+
}
|
|
137
|
+
return true;
|
|
138
|
+
}
|
|
139
|
+
const before = this.lastOptional;
|
|
140
|
+
if (slot.optional || !before || before.end !== tokens.position()) return false;
|
|
141
|
+
// Give the optional slot's value back and read this one from there.
|
|
142
|
+
this.lastOptional = undefined;
|
|
143
|
+
tokens.reset(before.start);
|
|
144
|
+
captured.delete(before.role);
|
|
145
|
+
return this.readOnce(tokens, slot, captured, next, false);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/** One read of `slot`; `guard` refuses a required slot the marker the pattern wants next. */
|
|
149
|
+
private readOnce(
|
|
150
|
+
tokens: TokenStream,
|
|
151
|
+
slot: RoleSlot,
|
|
152
|
+
captured: Map<SemanticRole, SemanticValue>,
|
|
153
|
+
next: PatternToken | undefined,
|
|
154
|
+
guard: boolean
|
|
155
|
+
): boolean {
|
|
156
|
+
// A required slot never takes the marker the pattern wants next as its
|
|
157
|
+
// value (`v0x を 実行`: `を` is the marker, not the patient).
|
|
158
|
+
if (guard && !slot.optional && next?.type === 'literal') {
|
|
159
|
+
const token = tokens.peek();
|
|
160
|
+
if (token && this.view.wouldMatch(next, token)) return false;
|
|
161
|
+
}
|
|
162
|
+
if (slot.words && !isListedWord(tokens.peek(), slot.words)) return slot.optional ?? false;
|
|
163
|
+
// Nor does an optional slot read a marker the rest of the pattern owes, through its optional
|
|
164
|
+
// groups (`when user types into #search`: `into` is the destination's, not the typed text).
|
|
165
|
+
if (slot.optional && !slot.greedy) {
|
|
166
|
+
const token = tokens.peek();
|
|
167
|
+
if (token && isStopMarker(token, greedyStopMarkers(this.view.patternRest()))) return true;
|
|
168
|
+
}
|
|
169
|
+
if (slot.greedy) {
|
|
170
|
+
const value = readGreedy(tokens, greedyStopMarkers(this.view.patternRest()));
|
|
171
|
+
if (value) {
|
|
172
|
+
captured.set(slot.role, value);
|
|
173
|
+
return true;
|
|
174
|
+
}
|
|
175
|
+
return slot.optional ?? false;
|
|
176
|
+
}
|
|
177
|
+
return readRoleValue(tokens, slot, captured, this.profile);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
maySkipSlot(
|
|
181
|
+
patternToken: PatternToken,
|
|
182
|
+
token: LanguageToken | null,
|
|
183
|
+
next: PatternToken | undefined
|
|
184
|
+
): boolean {
|
|
185
|
+
const slot =
|
|
186
|
+
patternToken.type === 'group' && patternToken.optional
|
|
187
|
+
? patternToken.tokens.every(t => t.type === 'role')
|
|
188
|
+
? patternToken.tokens[0]
|
|
189
|
+
: undefined
|
|
190
|
+
: patternToken;
|
|
191
|
+
if (!slot || slot.type !== 'role' || !slot.optional) return false;
|
|
192
|
+
return !!token && this.view.wouldMatch(next, token);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
atClauseEnd(tokens: TokenStream): boolean {
|
|
196
|
+
return tokens.isAtEnd();
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/** A reader factory for `MatchSkeleton`, reading with the given profiles. */
|
|
201
|
+
export function createStockReader(
|
|
202
|
+
profiles: Readonly<Record<string, ReadingProfile>> = {}
|
|
203
|
+
): (view: MatchView) => ValueReader {
|
|
204
|
+
return view => new StockValueReader(view, profiles);
|
|
205
|
+
}
|