Skip to content

Commit 86f697d

Browse files
committed
e2e
1 parent a4acbcf commit 86f697d

33 files changed

+543
-216
lines changed

ts/packages/actionGrammar/src/grammarMatcher.ts

Lines changed: 71 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ type MatchedValue =
2828
type MatchedValueNode = {
2929
valueId: number;
3030
value: MatchedValue;
31+
wildcard: boolean;
3132
prev: MatchedValueNode | undefined;
3233
};
3334

@@ -64,22 +65,35 @@ type MatchState = {
6465
| undefined;
6566
};
6667

67-
function getMatchedValue(
68-
valueId: ValueIdNode,
68+
function getMatchedValueNode(
69+
valueId: number,
6970
values: MatchedValueNode | undefined,
70-
): MatchedValue | undefined {
71+
): MatchedValueNode {
7172
let v: MatchedValueNode | undefined = values;
72-
while (v !== undefined && v.valueId !== valueId.valueId) {
73+
while (v !== undefined && v.valueId !== valueId) {
7374
v = v.prev;
7475
}
75-
return v?.value;
76+
if (v === undefined) {
77+
throw new Error(`Internal error: Missing value for ${valueId}`);
78+
}
79+
return v;
7680
}
7781

82+
type GrammarMatchStat = {
83+
matchedValueCount: number;
84+
wildcardCharCount: number;
85+
entityWildcardPropertyNames: string[];
86+
};
87+
export type GrammarMatchResult = GrammarMatchStat & {
88+
match: unknown;
89+
};
90+
7891
function createValue(
92+
stat: GrammarMatchStat,
7993
node: ValueNode | undefined,
8094
valueIds: ValueIdNode | undefined,
8195
values: MatchedValueNode | undefined,
82-
): any {
96+
): unknown {
8397
if (node === undefined) {
8498
if (valueIds === undefined) {
8599
throw new Error("Internal error: default matched values");
@@ -89,9 +103,10 @@ function createValue(
89103
`Internal error: No value definitions for multiple values`,
90104
);
91105
}
92-
const value = getMatchedValue(valueIds, values);
106+
const valueNode = getMatchedValueNode(valueIds.valueId, values);
107+
const value = valueNode.value;
93108
if (typeof value === "object") {
94-
return createValue(value.node, value.valueIds, values);
109+
return createValue(stat, value.node, value.valueIds, values);
95110
}
96111
return value;
97112
}
@@ -103,14 +118,14 @@ function createValue(
103118
const obj: Record<string, any> = {};
104119

105120
for (const [k, v] of Object.entries(node.value)) {
106-
obj[k] = createValue(v, valueIds, values);
121+
obj[k] = createValue(stat, v, valueIds, values);
107122
}
108123
return obj;
109124
}
110125
case "array": {
111126
const arr: any[] = [];
112127
for (const v of node.value) {
113-
arr.push(createValue(v, valueIds, values));
128+
arr.push(createValue(stat, v, valueIds, values));
114129
}
115130
return arr;
116131
}
@@ -124,10 +139,26 @@ function createValue(
124139
`Internal error: No value for variable '${node.name}. Values: ${JSON.stringify(valueIds)}'`,
125140
);
126141
}
127-
const value = getMatchedValue(v, values);
142+
const valueNode = getMatchedValueNode(v.valueId, values);
143+
const value = valueNode.value;
128144
if (typeof value === "object") {
129-
return createValue(value.node, value.valueIds, values);
145+
return createValue(stat, value.node, value.valueIds, values);
146+
}
147+
148+
// undefined means optional, don't count
149+
if (value !== undefined) {
150+
stat.matchedValueCount++;
130151
}
152+
153+
if (valueNode.wildcard) {
154+
if (typeof value !== "string") {
155+
throw new Error(
156+
`Internal error: Wildcard has non-string value for variable '${node.name}'`,
157+
);
158+
}
159+
stat.wildcardCharCount += value.length;
160+
}
161+
131162
return value;
132163
}
133164
}
@@ -157,14 +188,14 @@ function createCaptureWildcardState(
157188
newIndex: number,
158189
) {
159190
const { start: wildcardStart, valueId } = state.pendingWildcard!;
160-
const wildcard = captureWildcard(request, wildcardStart, wildcardEnd);
161-
if (wildcard === undefined) {
191+
const wildcardStr = captureWildcard(request, wildcardStart, wildcardEnd);
192+
if (wildcardStr === undefined) {
162193
return undefined;
163194
}
164195
const newState = { ...state };
165196
newState.index = newIndex;
166197
newState.pendingWildcard = undefined;
167-
addValueWithId(newState, valueId, wildcard);
198+
addValueWithId(newState, valueId, wildcardStr, true);
168199
return newState;
169200
}
170201

@@ -178,10 +209,12 @@ function addValueWithId(
178209
state: MatchState,
179210
valueId: number,
180211
matchedValue: MatchedValue,
212+
wildcard: boolean,
181213
) {
182214
state.values = {
183215
valueId,
184216
value: matchedValue,
217+
wildcard,
185218
prev: state.values,
186219
};
187220
}
@@ -192,13 +225,13 @@ function addValue(
192225
matchedValue: MatchedValue,
193226
) {
194227
const valueId = addValueId(state, name);
195-
addValueWithId(state, valueId, matchedValue);
228+
addValueWithId(state, valueId, matchedValue, false);
196229
}
197230

198231
function finalizeRule(
199232
state: MatchState,
200233
request: string,
201-
results: any[],
234+
results: GrammarMatchResult[],
202235
pending: MatchState[],
203236
) {
204237
const nested = state.nested;
@@ -239,7 +272,7 @@ function finalizeRule(
239272
return;
240273
}
241274
state.index = request.length;
242-
addValueWithId(state, state.pendingWildcard.valueId, value);
275+
addValueWithId(state, state.pendingWildcard.valueId, value, true);
243276
}
244277
if (state.index < request.length) {
245278
// Detect trailing separators
@@ -261,18 +294,34 @@ function finalizeRule(
261294
debugMatch(
262295
`Matched at end of input. Matched ids: ${JSON.stringify(state.valueIds)}, values: ${JSON.stringify(state.values)}'`,
263296
);
264-
results.push(createValue(state.rule.value, state.valueIds, state.values));
297+
298+
const matchResult: GrammarMatchResult = {
299+
match: undefined,
300+
matchedValueCount: 0,
301+
wildcardCharCount: 0,
302+
entityWildcardPropertyNames: [],
303+
};
304+
matchResult.match = createValue(
305+
matchResult,
306+
state.rule.value,
307+
state.valueIds,
308+
state.values,
309+
);
310+
results.push(matchResult);
265311
}
266312

267-
type MatchResult = any;
268-
function matchRules(grammar: Grammar, request: string): MatchResult[] {
313+
function matchRules(grammar: Grammar, request: string): GrammarMatchResult[] {
269314
const pending: MatchState[] = grammar.rules.map((r) => ({
270315
rule: r,
271316
partIndex: 0,
272317
index: 0,
273318
nextValueId: 0,
319+
matchedCount: 0,
320+
wildcardCharCount: 0,
321+
nonOptionalCount: 0,
322+
implicitParameterCount: 0,
274323
}));
275-
const results: MatchResult[] = [];
324+
const results: GrammarMatchResult[] = [];
276325
while (pending.length > 0) {
277326
const state = pending.shift()!;
278327
const { rule, partIndex } = state;

ts/packages/actionGrammar/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@ export type { GrammarJson, Grammar } from "./grammarTypes.js";
55
export { grammarFromJson } from "./grammarDeserializer.js";
66
export { grammarToJson } from "./grammarSerializer.js";
77
export { loadGrammarRules } from "./grammarLoader.js";
8-
export { matchGrammar } from "./grammarMatcher.js";
8+
export { matchGrammar, GrammarMatchResult } from "./grammarMatcher.js";

0 commit comments

Comments
 (0)