@lokascript/framework 2.1.0 → 2.2.1
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/dist/api/index.js +111 -24
- package/dist/api/index.js.map +1 -1
- package/dist/core/index.js +133 -0
- package/dist/core/index.js.map +1 -1
- package/dist/core/pattern-matching/index.js.map +1 -1
- package/dist/core/tokenization/index.js +133 -0
- package/dist/core/tokenization/index.js.map +1 -1
- package/dist/core/tokenization/morphology/base-normalizer.d.ts +94 -0
- package/dist/core/tokenization/morphology/base-normalizer.d.ts.map +1 -0
- package/dist/core/tokenization/morphology/index.d.ts +3 -1
- package/dist/core/tokenization/morphology/index.d.ts.map +1 -1
- package/dist/core/types.d.ts +3 -2
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/generation/index.js.map +1 -1
- package/dist/index.cjs +564 -46
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +556 -46
- package/dist/index.js.map +1 -1
- package/dist/ir/explicit-parser.d.ts +62 -7
- package/dist/ir/explicit-parser.d.ts.map +1 -1
- package/dist/ir/explicit-renderer.d.ts +18 -1
- package/dist/ir/explicit-renderer.d.ts.map +1 -1
- package/dist/ir/index.d.ts +4 -2
- package/dist/ir/index.d.ts.map +1 -1
- package/dist/ir/index.js +423 -46
- package/dist/ir/index.js.map +1 -1
- package/dist/ir/protocol-json.d.ts.map +1 -1
- package/dist/ir/to-runtime-ast.d.ts +53 -0
- package/dist/ir/to-runtime-ast.d.ts.map +1 -0
- package/dist/ir/types.d.ts +14 -4
- package/dist/ir/types.d.ts.map +1 -1
- package/dist/parsing/index.js.map +1 -1
- package/dist/testing/index.js +8671 -8261
- package/dist/testing/index.js.map +1 -1
- package/package.json +1 -1
- package/src/core/tokenization/morphology/base-normalizer.ts +249 -0
- package/src/core/tokenization/morphology/index.ts +3 -1
- package/src/core/types.ts +4 -2
- package/src/index.ts +7 -0
- package/src/ir/conformance.test.ts +120 -0
- package/src/ir/explicit-parser.test.ts +307 -1
- package/src/ir/explicit-parser.ts +403 -29
- package/src/ir/explicit-renderer.test.ts +87 -2
- package/src/ir/explicit-renderer.ts +57 -5
- package/src/ir/index.ts +13 -2
- package/src/ir/protocol-json.test.ts +4 -2
- package/src/ir/protocol-json.ts +30 -24
- package/src/ir/to-runtime-ast.ts +203 -0
- package/src/ir/types.ts +14 -4
package/dist/index.js
CHANGED
|
@@ -2048,6 +2048,8 @@ function isNestedCommand(value) {
|
|
|
2048
2048
|
}
|
|
2049
2049
|
function parseExplicit(input, options = {}) {
|
|
2050
2050
|
const trimmed = input.trim();
|
|
2051
|
+
const collect = options.collectDiagnostics === true;
|
|
2052
|
+
const diagnostics = [];
|
|
2051
2053
|
if (!trimmed.startsWith("[") || !trimmed.endsWith("]")) {
|
|
2052
2054
|
throw new Error("Explicit syntax must be wrapped in brackets: [command role:value ...]");
|
|
2053
2055
|
}
|
|
@@ -2070,10 +2072,30 @@ function parseExplicit(input, options = {}) {
|
|
|
2070
2072
|
const enabled = token.startsWith("+");
|
|
2071
2073
|
const flagName = token.slice(1);
|
|
2072
2074
|
if (!flagName) {
|
|
2075
|
+
if (collect) {
|
|
2076
|
+
diagnostics.push({
|
|
2077
|
+
severity: "error",
|
|
2078
|
+
code: "EMPTY_FLAG",
|
|
2079
|
+
message: `Empty flag name: "${token}"`,
|
|
2080
|
+
source: "schema"
|
|
2081
|
+
});
|
|
2082
|
+
continue;
|
|
2083
|
+
}
|
|
2073
2084
|
throw new Error(`Empty flag name: "${token}"`);
|
|
2074
2085
|
}
|
|
2075
2086
|
if (validRoleNames && !validRoleNames.has(flagName)) {
|
|
2076
2087
|
const roleList = [...validRoleNames].join(", ");
|
|
2088
|
+
if (collect) {
|
|
2089
|
+
diagnostics.push({
|
|
2090
|
+
severity: "error",
|
|
2091
|
+
code: "UNKNOWN_FLAG",
|
|
2092
|
+
message: `Unknown flag "${flagName}" for command "${command}"`,
|
|
2093
|
+
source: "schema",
|
|
2094
|
+
suggestions: [...validRoleNames].map((r) => `+${r}`)
|
|
2095
|
+
});
|
|
2096
|
+
roles.set(flagName, createFlag(flagName, enabled));
|
|
2097
|
+
continue;
|
|
2098
|
+
}
|
|
2077
2099
|
throw new Error(
|
|
2078
2100
|
`Unknown flag "${flagName}" for command "${command}". Valid roles: ${roleList}`
|
|
2079
2101
|
);
|
|
@@ -2083,14 +2105,33 @@ function parseExplicit(input, options = {}) {
|
|
|
2083
2105
|
}
|
|
2084
2106
|
const colonIndex = token.indexOf(":");
|
|
2085
2107
|
if (colonIndex === -1) {
|
|
2108
|
+
if (collect) {
|
|
2109
|
+
diagnostics.push({
|
|
2110
|
+
severity: "error",
|
|
2111
|
+
code: "INVALID_ROLE_FORMAT",
|
|
2112
|
+
message: `Invalid role format: "${token}". Expected role:value or +flag`,
|
|
2113
|
+
source: "schema"
|
|
2114
|
+
});
|
|
2115
|
+
continue;
|
|
2116
|
+
}
|
|
2086
2117
|
throw new Error(`Invalid role format: "${token}". Expected role:value or +flag`);
|
|
2087
2118
|
}
|
|
2088
2119
|
const roleName = token.slice(0, colonIndex);
|
|
2089
2120
|
if (validRoleNames && !STRUCTURAL_ROLES.has(roleName) && !validRoleNames.has(roleName)) {
|
|
2090
2121
|
const roleList = [...validRoleNames].join(", ");
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2122
|
+
if (collect) {
|
|
2123
|
+
diagnostics.push({
|
|
2124
|
+
severity: "error",
|
|
2125
|
+
code: "UNKNOWN_ROLE",
|
|
2126
|
+
message: `Unknown role "${roleName}" for command "${command}"`,
|
|
2127
|
+
source: "schema",
|
|
2128
|
+
suggestions: [...validRoleNames]
|
|
2129
|
+
});
|
|
2130
|
+
} else {
|
|
2131
|
+
throw new Error(
|
|
2132
|
+
`Unknown role "${roleName}" for command "${command}". Valid roles: ${roleList}`
|
|
2133
|
+
);
|
|
2134
|
+
}
|
|
2094
2135
|
}
|
|
2095
2136
|
const role = roleName;
|
|
2096
2137
|
const valueStr = token.slice(colonIndex + 1);
|
|
@@ -2106,37 +2147,67 @@ function parseExplicit(input, options = {}) {
|
|
|
2106
2147
|
if (schema && command !== "on") {
|
|
2107
2148
|
for (const roleSpec of schema.roles) {
|
|
2108
2149
|
if (roleSpec.required && !roles.has(roleSpec.role) && !roleSpec.default) {
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2150
|
+
if (collect) {
|
|
2151
|
+
diagnostics.push({
|
|
2152
|
+
severity: "error",
|
|
2153
|
+
code: "MISSING_REQUIRED_ROLE",
|
|
2154
|
+
message: `Missing required role "${roleSpec.role}" for command "${command}": ${roleSpec.description}`,
|
|
2155
|
+
source: "schema"
|
|
2156
|
+
});
|
|
2157
|
+
} else {
|
|
2158
|
+
throw new Error(
|
|
2159
|
+
`Missing required role "${roleSpec.role}" for command "${command}": ${roleSpec.description}`
|
|
2160
|
+
);
|
|
2161
|
+
}
|
|
2112
2162
|
}
|
|
2113
2163
|
}
|
|
2114
2164
|
}
|
|
2165
|
+
const attachDiagnostics = (node) => {
|
|
2166
|
+
if (diagnostics.length === 0) return node;
|
|
2167
|
+
return { ...node, diagnostics };
|
|
2168
|
+
};
|
|
2115
2169
|
switch (command) {
|
|
2116
2170
|
case "on": {
|
|
2117
2171
|
const eventValue = roles.get("event");
|
|
2118
2172
|
if (!eventValue) {
|
|
2173
|
+
if (collect) {
|
|
2174
|
+
diagnostics.push({
|
|
2175
|
+
severity: "error",
|
|
2176
|
+
code: "MISSING_EVENT_ROLE",
|
|
2177
|
+
message: "Event handler requires event role: [on event:click ...]",
|
|
2178
|
+
source: "schema"
|
|
2179
|
+
});
|
|
2180
|
+
return attachDiagnostics(
|
|
2181
|
+
createEventHandlerNode("on", roles, [], {
|
|
2182
|
+
sourceLanguage: "explicit"
|
|
2183
|
+
})
|
|
2184
|
+
);
|
|
2185
|
+
}
|
|
2119
2186
|
throw new Error("Event handler requires event role: [on event:click ...]");
|
|
2120
2187
|
}
|
|
2121
2188
|
const body = extractStructuralBody(roles, "body", options);
|
|
2122
2189
|
roles.delete("body");
|
|
2123
|
-
return
|
|
2124
|
-
|
|
2125
|
-
|
|
2190
|
+
return attachDiagnostics(
|
|
2191
|
+
createEventHandlerNode("on", roles, body, {
|
|
2192
|
+
sourceLanguage: "explicit"
|
|
2193
|
+
})
|
|
2194
|
+
);
|
|
2126
2195
|
}
|
|
2127
2196
|
case "if": {
|
|
2128
2197
|
const thenBranch = extractStructuralBody(roles, "then", options);
|
|
2129
2198
|
const elseBranch = extractStructuralBody(roles, "else", options);
|
|
2130
2199
|
roles.delete("then");
|
|
2131
2200
|
roles.delete("else");
|
|
2132
|
-
return
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2201
|
+
return attachDiagnostics(
|
|
2202
|
+
createConditionalNode(
|
|
2203
|
+
command,
|
|
2204
|
+
roles,
|
|
2205
|
+
thenBranch,
|
|
2206
|
+
elseBranch.length > 0 ? elseBranch : void 0,
|
|
2207
|
+
{
|
|
2208
|
+
sourceLanguage: "explicit"
|
|
2209
|
+
}
|
|
2210
|
+
)
|
|
2140
2211
|
);
|
|
2141
2212
|
}
|
|
2142
2213
|
case "repeat": {
|
|
@@ -2162,20 +2233,182 @@ function parseExplicit(input, options = {}) {
|
|
|
2162
2233
|
}
|
|
2163
2234
|
const loopVariable = variableValue?.type === "literal" && typeof variableValue.value === "string" ? variableValue.value : variableValue?.type === "expression" ? variableValue.raw : void 0;
|
|
2164
2235
|
const indexVariable = indexVariableValue?.type === "literal" && typeof indexVariableValue.value === "string" ? indexVariableValue.value : void 0;
|
|
2165
|
-
return
|
|
2166
|
-
|
|
2167
|
-
|
|
2236
|
+
return attachDiagnostics(
|
|
2237
|
+
createLoopNode(command, roles, variant, loopBody, loopVariable, indexVariable, {
|
|
2238
|
+
sourceLanguage: "explicit"
|
|
2239
|
+
})
|
|
2240
|
+
);
|
|
2168
2241
|
}
|
|
2169
2242
|
default:
|
|
2170
|
-
return
|
|
2171
|
-
|
|
2172
|
-
|
|
2243
|
+
return attachDiagnostics(
|
|
2244
|
+
createCommandNode(command, roles, {
|
|
2245
|
+
sourceLanguage: "explicit"
|
|
2246
|
+
})
|
|
2247
|
+
);
|
|
2173
2248
|
}
|
|
2174
2249
|
}
|
|
2175
2250
|
function isExplicitSyntax(input) {
|
|
2176
2251
|
const trimmed = input.trim();
|
|
2177
2252
|
return trimmed.startsWith("[") && trimmed.endsWith("]");
|
|
2178
2253
|
}
|
|
2254
|
+
var CHAIN_KEYWORDS = /* @__PURE__ */ new Set(["then", "and", "async", "sequential"]);
|
|
2255
|
+
function splitCompoundSegments(input) {
|
|
2256
|
+
const segments = [];
|
|
2257
|
+
let chainType = null;
|
|
2258
|
+
let current = "";
|
|
2259
|
+
let bracketDepth = 0;
|
|
2260
|
+
let inString = false;
|
|
2261
|
+
let stringChar = "";
|
|
2262
|
+
for (let i = 0; i < input.length; i++) {
|
|
2263
|
+
const char = input[i];
|
|
2264
|
+
if (inString) {
|
|
2265
|
+
current += char;
|
|
2266
|
+
if (char === stringChar && input[i - 1] !== "\\") {
|
|
2267
|
+
inString = false;
|
|
2268
|
+
}
|
|
2269
|
+
continue;
|
|
2270
|
+
}
|
|
2271
|
+
if (char === '"' || char === "'") {
|
|
2272
|
+
inString = true;
|
|
2273
|
+
stringChar = char;
|
|
2274
|
+
current += char;
|
|
2275
|
+
continue;
|
|
2276
|
+
}
|
|
2277
|
+
if (char === "[") {
|
|
2278
|
+
bracketDepth++;
|
|
2279
|
+
current += char;
|
|
2280
|
+
continue;
|
|
2281
|
+
}
|
|
2282
|
+
if (char === "]") {
|
|
2283
|
+
bracketDepth--;
|
|
2284
|
+
current += char;
|
|
2285
|
+
if (bracketDepth === 0) {
|
|
2286
|
+
const rest = input.slice(i + 1);
|
|
2287
|
+
const pipeMatch = rest.match(/^\s*\|>\s*/);
|
|
2288
|
+
if (pipeMatch) {
|
|
2289
|
+
segments.push(current.trim());
|
|
2290
|
+
current = "";
|
|
2291
|
+
chainType ?? (chainType = "pipe");
|
|
2292
|
+
i += pipeMatch[0].length;
|
|
2293
|
+
continue;
|
|
2294
|
+
}
|
|
2295
|
+
const keywordMatch = rest.match(/^\s+(then|and|async|sequential)\s+/);
|
|
2296
|
+
if (keywordMatch && CHAIN_KEYWORDS.has(keywordMatch[1])) {
|
|
2297
|
+
segments.push(current.trim());
|
|
2298
|
+
current = "";
|
|
2299
|
+
chainType ?? (chainType = keywordMatch[1]);
|
|
2300
|
+
i += keywordMatch[0].length;
|
|
2301
|
+
continue;
|
|
2302
|
+
}
|
|
2303
|
+
}
|
|
2304
|
+
continue;
|
|
2305
|
+
}
|
|
2306
|
+
current += char;
|
|
2307
|
+
}
|
|
2308
|
+
if (current.trim()) {
|
|
2309
|
+
segments.push(current.trim());
|
|
2310
|
+
}
|
|
2311
|
+
return { segments, chainType };
|
|
2312
|
+
}
|
|
2313
|
+
function parseCompound(input, options) {
|
|
2314
|
+
const trimmed = input.trim();
|
|
2315
|
+
const { annotations, remainder } = extractAnnotations(trimmed);
|
|
2316
|
+
const { segments, chainType } = splitCompoundSegments(remainder);
|
|
2317
|
+
let node;
|
|
2318
|
+
if (segments.length <= 1) {
|
|
2319
|
+
node = parseExplicit(segments[0] || remainder, options);
|
|
2320
|
+
} else {
|
|
2321
|
+
const statements = segments.map((seg) => parseExplicit(seg, options));
|
|
2322
|
+
node = createCompoundNode(statements, chainType ?? "sequential");
|
|
2323
|
+
}
|
|
2324
|
+
if (annotations.length > 0) {
|
|
2325
|
+
return { ...node, annotations };
|
|
2326
|
+
}
|
|
2327
|
+
return node;
|
|
2328
|
+
}
|
|
2329
|
+
function extractAnnotations(input) {
|
|
2330
|
+
const annotations = [];
|
|
2331
|
+
let pos = 0;
|
|
2332
|
+
while (pos < input.length) {
|
|
2333
|
+
while (pos < input.length && (input[pos] === " " || input[pos] === " ")) {
|
|
2334
|
+
pos++;
|
|
2335
|
+
}
|
|
2336
|
+
if (pos >= input.length || input[pos] !== "@") {
|
|
2337
|
+
break;
|
|
2338
|
+
}
|
|
2339
|
+
pos++;
|
|
2340
|
+
const nameStart = pos;
|
|
2341
|
+
while (pos < input.length && /[A-Za-z0-9\-_]/.test(input[pos])) {
|
|
2342
|
+
pos++;
|
|
2343
|
+
}
|
|
2344
|
+
const name = input.slice(nameStart, pos);
|
|
2345
|
+
if (!name) break;
|
|
2346
|
+
let value;
|
|
2347
|
+
if (pos < input.length && input[pos] === "(") {
|
|
2348
|
+
pos++;
|
|
2349
|
+
const valueStart = pos;
|
|
2350
|
+
if (pos < input.length && (input[pos] === '"' || input[pos] === "'")) {
|
|
2351
|
+
const quote = input[pos];
|
|
2352
|
+
pos++;
|
|
2353
|
+
while (pos < input.length && input[pos] !== quote) {
|
|
2354
|
+
if (input[pos] === "\\") pos++;
|
|
2355
|
+
pos++;
|
|
2356
|
+
}
|
|
2357
|
+
pos++;
|
|
2358
|
+
value = input.slice(valueStart + 1, pos - 1);
|
|
2359
|
+
} else {
|
|
2360
|
+
while (pos < input.length && input[pos] !== ")") {
|
|
2361
|
+
pos++;
|
|
2362
|
+
}
|
|
2363
|
+
value = input.slice(valueStart, pos);
|
|
2364
|
+
}
|
|
2365
|
+
if (pos < input.length && input[pos] === ")") {
|
|
2366
|
+
pos++;
|
|
2367
|
+
}
|
|
2368
|
+
}
|
|
2369
|
+
annotations.push(value !== void 0 ? { name, value } : { name });
|
|
2370
|
+
}
|
|
2371
|
+
return { annotations, remainder: input.slice(pos).trim() };
|
|
2372
|
+
}
|
|
2373
|
+
var VERSION_HEADER_RE = /^#!lse\s+(\d+\.\d+(?:\.\d+)?)\s*$/;
|
|
2374
|
+
function parseDocument(input, options) {
|
|
2375
|
+
const lines = input.split("\n");
|
|
2376
|
+
let lseVersion = "1.0";
|
|
2377
|
+
const nodes = [];
|
|
2378
|
+
let startLine = 0;
|
|
2379
|
+
for (let i = 0; i < lines.length; i++) {
|
|
2380
|
+
const trimmed = lines[i].trim();
|
|
2381
|
+
if (!trimmed) continue;
|
|
2382
|
+
const versionMatch = trimmed.match(VERSION_HEADER_RE);
|
|
2383
|
+
if (versionMatch) {
|
|
2384
|
+
lseVersion = versionMatch[1];
|
|
2385
|
+
startLine = i + 1;
|
|
2386
|
+
}
|
|
2387
|
+
break;
|
|
2388
|
+
}
|
|
2389
|
+
for (let i = startLine; i < lines.length; i++) {
|
|
2390
|
+
const trimmed = lines[i].trim();
|
|
2391
|
+
if (!trimmed) continue;
|
|
2392
|
+
if (trimmed.startsWith("//") || trimmed.startsWith("#") && !trimmed.startsWith("#!")) {
|
|
2393
|
+
continue;
|
|
2394
|
+
}
|
|
2395
|
+
nodes.push(parseCompound(trimmed, options));
|
|
2396
|
+
}
|
|
2397
|
+
return { lseVersion, nodes };
|
|
2398
|
+
}
|
|
2399
|
+
function isCompoundSyntax(input) {
|
|
2400
|
+
const { segments } = splitCompoundSegments(input.trim());
|
|
2401
|
+
return segments.length > 1;
|
|
2402
|
+
}
|
|
2403
|
+
function isDocumentSyntax(input) {
|
|
2404
|
+
const trimmed = input.trim();
|
|
2405
|
+
if (trimmed.includes("\n")) return true;
|
|
2406
|
+
if (VERSION_HEADER_RE.test(trimmed)) return true;
|
|
2407
|
+
if (trimmed.startsWith("//") || trimmed.startsWith("#") && !trimmed.startsWith("#!")) {
|
|
2408
|
+
return true;
|
|
2409
|
+
}
|
|
2410
|
+
return false;
|
|
2411
|
+
}
|
|
2179
2412
|
function extractStructuralBody(roles, roleName, options) {
|
|
2180
2413
|
const value = roles.get(roleName);
|
|
2181
2414
|
if (!value) return [];
|
|
@@ -2277,11 +2510,33 @@ function findMatchingBracket(str, start) {
|
|
|
2277
2510
|
|
|
2278
2511
|
// src/ir/explicit-renderer.ts
|
|
2279
2512
|
function renderExplicit(node) {
|
|
2513
|
+
let result;
|
|
2280
2514
|
if (node.kind === "compound") {
|
|
2281
2515
|
const compoundNode = node;
|
|
2282
2516
|
const renderedStatements = compoundNode.statements.map((stmt) => renderExplicit(stmt));
|
|
2283
|
-
|
|
2517
|
+
result = renderedStatements.join(` ${compoundNode.chainType} `);
|
|
2518
|
+
} else {
|
|
2519
|
+
result = renderBracketCommand(node);
|
|
2284
2520
|
}
|
|
2521
|
+
if (node.annotations && node.annotations.length > 0) {
|
|
2522
|
+
const annParts = node.annotations.map(
|
|
2523
|
+
(ann) => ann.value !== void 0 ? `@${ann.name}(${ann.value})` : `@${ann.name}`
|
|
2524
|
+
);
|
|
2525
|
+
return annParts.join(" ") + " " + result;
|
|
2526
|
+
}
|
|
2527
|
+
return result;
|
|
2528
|
+
}
|
|
2529
|
+
function renderDocument(envelope) {
|
|
2530
|
+
const lines = [];
|
|
2531
|
+
if (envelope.lseVersion && envelope.lseVersion !== "1.0") {
|
|
2532
|
+
lines.push(`#!lse ${envelope.lseVersion}`);
|
|
2533
|
+
}
|
|
2534
|
+
for (const node of envelope.nodes) {
|
|
2535
|
+
lines.push(renderExplicit(node));
|
|
2536
|
+
}
|
|
2537
|
+
return lines.join("\n");
|
|
2538
|
+
}
|
|
2539
|
+
function renderBracketCommand(node) {
|
|
2285
2540
|
const parts = [node.action];
|
|
2286
2541
|
for (const [role, value] of node.roles) {
|
|
2287
2542
|
if (value.type === "flag") {
|
|
@@ -2619,12 +2874,16 @@ function toProtocolJSON(node) {
|
|
|
2619
2874
|
);
|
|
2620
2875
|
}
|
|
2621
2876
|
if (node.diagnostics && node.diagnostics.length > 0) {
|
|
2622
|
-
result.diagnostics = node.diagnostics.map((d) =>
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2877
|
+
result.diagnostics = node.diagnostics.map((d) => {
|
|
2878
|
+
const json = {
|
|
2879
|
+
level: d.severity,
|
|
2880
|
+
message: d.message
|
|
2881
|
+
};
|
|
2882
|
+
if (d.code) json.code = d.code;
|
|
2883
|
+
if (d.source) json.source = d.source;
|
|
2884
|
+
if (d.suggestions && d.suggestions.length > 0) json.suggestions = [...d.suggestions];
|
|
2885
|
+
return json;
|
|
2886
|
+
});
|
|
2628
2887
|
}
|
|
2629
2888
|
return result;
|
|
2630
2889
|
}
|
|
@@ -2672,10 +2931,11 @@ function fromProtocolJSON(json) {
|
|
|
2672
2931
|
(a) => a.value !== void 0 ? { name: a.name, value: a.value } : { name: a.name }
|
|
2673
2932
|
);
|
|
2674
2933
|
const diagnostics = json.diagnostics?.map((d) => ({
|
|
2675
|
-
|
|
2676
|
-
role: d.role,
|
|
2934
|
+
severity: d.level,
|
|
2677
2935
|
message: d.message,
|
|
2678
|
-
code: d.code
|
|
2936
|
+
...d.code ? { code: d.code } : {},
|
|
2937
|
+
...d.source ? { source: d.source } : {},
|
|
2938
|
+
...d.suggestions && d.suggestions.length > 0 ? { suggestions: d.suggestions } : {}
|
|
2679
2939
|
}));
|
|
2680
2940
|
if (json.trigger) {
|
|
2681
2941
|
const eventName = json.trigger.event;
|
|
@@ -2816,7 +3076,7 @@ var VALID_VALUE_TYPES2 = /* @__PURE__ */ new Set([
|
|
|
2816
3076
|
]);
|
|
2817
3077
|
var VALID_CHAIN_TYPES = /* @__PURE__ */ new Set(["then", "and", "async", "sequential", "pipe"]);
|
|
2818
3078
|
var VALID_ASYNC_VARIANTS = /* @__PURE__ */ new Set(["all", "race"]);
|
|
2819
|
-
var VALID_DIAGNOSTIC_LEVELS = /* @__PURE__ */ new Set(["error", "warning"]);
|
|
3079
|
+
var VALID_DIAGNOSTIC_LEVELS = /* @__PURE__ */ new Set(["error", "warning", "info"]);
|
|
2820
3080
|
function validateProtocolJSON(json) {
|
|
2821
3081
|
const diagnostics = [];
|
|
2822
3082
|
if (typeof json !== "object" || json === null) {
|
|
@@ -2965,14 +3225,7 @@ function validateProtocolJSON(json) {
|
|
|
2965
3225
|
diagnostics.push({
|
|
2966
3226
|
severity: "error",
|
|
2967
3227
|
code: "INVALID_DIAGNOSTIC_LEVEL",
|
|
2968
|
-
message: `diagnostics[${i}].level must be "error" or "
|
|
2969
|
-
});
|
|
2970
|
-
}
|
|
2971
|
-
if (typeof d.role !== "string") {
|
|
2972
|
-
diagnostics.push({
|
|
2973
|
-
severity: "error",
|
|
2974
|
-
code: "MISSING_DIAGNOSTIC_ROLE",
|
|
2975
|
-
message: `diagnostics[${i}] missing required field: role`
|
|
3228
|
+
message: `diagnostics[${i}].level must be "error", "warning", or "info"`
|
|
2976
3229
|
});
|
|
2977
3230
|
}
|
|
2978
3231
|
if (typeof d.message !== "string") {
|
|
@@ -2982,11 +3235,11 @@ function validateProtocolJSON(json) {
|
|
|
2982
3235
|
message: `diagnostics[${i}] missing required field: message`
|
|
2983
3236
|
});
|
|
2984
3237
|
}
|
|
2985
|
-
if (typeof d.code !== "string") {
|
|
3238
|
+
if ("code" in d && typeof d.code !== "string") {
|
|
2986
3239
|
diagnostics.push({
|
|
2987
|
-
severity: "
|
|
2988
|
-
code: "
|
|
2989
|
-
message: `diagnostics[${i}]
|
|
3240
|
+
severity: "warning",
|
|
3241
|
+
code: "INVALID_DIAGNOSTIC_CODE",
|
|
3242
|
+
message: `diagnostics[${i}].code must be a string if present`
|
|
2990
3243
|
});
|
|
2991
3244
|
}
|
|
2992
3245
|
}
|
|
@@ -3422,6 +3675,123 @@ function bridgeEventModifiers(mods) {
|
|
|
3422
3675
|
};
|
|
3423
3676
|
}
|
|
3424
3677
|
|
|
3678
|
+
// src/ir/to-runtime-ast.ts
|
|
3679
|
+
function semanticNodeToRuntimeAST(node) {
|
|
3680
|
+
switch (node.kind) {
|
|
3681
|
+
case "event-handler":
|
|
3682
|
+
return convertEventHandler(node);
|
|
3683
|
+
case "conditional":
|
|
3684
|
+
return convertConditional(node);
|
|
3685
|
+
case "loop":
|
|
3686
|
+
return convertLoop(node);
|
|
3687
|
+
case "compound": {
|
|
3688
|
+
const compound = node;
|
|
3689
|
+
const commands = (compound.statements ?? []).map(semanticNodeToRuntimeAST);
|
|
3690
|
+
return {
|
|
3691
|
+
type: "CommandSequence",
|
|
3692
|
+
commands
|
|
3693
|
+
};
|
|
3694
|
+
}
|
|
3695
|
+
case "command":
|
|
3696
|
+
default:
|
|
3697
|
+
return convertCommand2(node);
|
|
3698
|
+
}
|
|
3699
|
+
}
|
|
3700
|
+
function convertEventHandler(eh) {
|
|
3701
|
+
const eventValue = eh.roles.get("event");
|
|
3702
|
+
const eventName = eventValue && "value" in eventValue ? String(eventValue.value) : "click";
|
|
3703
|
+
const modifiers = {};
|
|
3704
|
+
if (eh.eventModifiers) {
|
|
3705
|
+
if (eh.eventModifiers.once) modifiers.once = true;
|
|
3706
|
+
if (eh.eventModifiers.debounce) modifiers.debounce = eh.eventModifiers.debounce;
|
|
3707
|
+
if (eh.eventModifiers.throttle) modifiers.throttle = eh.eventModifiers.throttle;
|
|
3708
|
+
if (eh.eventModifiers.queue) modifiers.queue = eh.eventModifiers.queue;
|
|
3709
|
+
}
|
|
3710
|
+
return {
|
|
3711
|
+
type: "event",
|
|
3712
|
+
event: eventName,
|
|
3713
|
+
modifiers,
|
|
3714
|
+
body: (eh.body ?? []).map(semanticNodeToRuntimeAST)
|
|
3715
|
+
};
|
|
3716
|
+
}
|
|
3717
|
+
function convertCommand2(node) {
|
|
3718
|
+
const roles = {};
|
|
3719
|
+
const args = [];
|
|
3720
|
+
for (const [roleName, value] of node.roles) {
|
|
3721
|
+
const astValue = semanticValueToAST(value);
|
|
3722
|
+
roles[roleName] = astValue;
|
|
3723
|
+
args.push(astValue);
|
|
3724
|
+
}
|
|
3725
|
+
return {
|
|
3726
|
+
type: "command",
|
|
3727
|
+
name: node.action,
|
|
3728
|
+
args,
|
|
3729
|
+
roles
|
|
3730
|
+
};
|
|
3731
|
+
}
|
|
3732
|
+
function convertConditional(node) {
|
|
3733
|
+
const conditionValue = node.roles.get("condition");
|
|
3734
|
+
return {
|
|
3735
|
+
type: "command",
|
|
3736
|
+
name: "if",
|
|
3737
|
+
args: conditionValue ? [semanticValueToAST(conditionValue)] : [],
|
|
3738
|
+
condition: conditionValue ? semanticValueToAST(conditionValue) : void 0,
|
|
3739
|
+
thenBranch: (node.thenBranch ?? []).map(semanticNodeToRuntimeAST),
|
|
3740
|
+
elseBranch: (node.elseBranch ?? []).map(semanticNodeToRuntimeAST)
|
|
3741
|
+
};
|
|
3742
|
+
}
|
|
3743
|
+
function convertLoop(node) {
|
|
3744
|
+
const countValue = node.roles.get("patient") || node.roles.get("count");
|
|
3745
|
+
return {
|
|
3746
|
+
type: "command",
|
|
3747
|
+
name: "repeat",
|
|
3748
|
+
args: countValue ? [semanticValueToAST(countValue)] : [],
|
|
3749
|
+
loopVariant: node.loopVariant ?? "forever",
|
|
3750
|
+
body: (node.body ?? []).map(semanticNodeToRuntimeAST),
|
|
3751
|
+
loopVariable: node.loopVariable,
|
|
3752
|
+
indexVariable: node.indexVariable
|
|
3753
|
+
};
|
|
3754
|
+
}
|
|
3755
|
+
function semanticValueToAST(value) {
|
|
3756
|
+
switch (value.type) {
|
|
3757
|
+
case "selector":
|
|
3758
|
+
return { type: "selector", value: value.value };
|
|
3759
|
+
case "reference":
|
|
3760
|
+
return { type: "identifier", value: value.value };
|
|
3761
|
+
case "literal": {
|
|
3762
|
+
const lit = value;
|
|
3763
|
+
if (lit.dataType === "duration") {
|
|
3764
|
+
const str = String(lit.value);
|
|
3765
|
+
const match = /^(\d+(?:\.\d+)?)(ms|s)$/.exec(str);
|
|
3766
|
+
if (match) {
|
|
3767
|
+
const ms = match[2] === "s" ? parseFloat(match[1]) * 1e3 : parseFloat(match[1]);
|
|
3768
|
+
return { type: "literal", value: ms };
|
|
3769
|
+
}
|
|
3770
|
+
}
|
|
3771
|
+
return { type: "literal", value: lit.value };
|
|
3772
|
+
}
|
|
3773
|
+
case "property-path": {
|
|
3774
|
+
const pp = value;
|
|
3775
|
+
const objectAST = semanticValueToAST(pp.object);
|
|
3776
|
+
return {
|
|
3777
|
+
type: "member",
|
|
3778
|
+
object: objectAST,
|
|
3779
|
+
property: pp.property
|
|
3780
|
+
};
|
|
3781
|
+
}
|
|
3782
|
+
case "expression": {
|
|
3783
|
+
const expr = value;
|
|
3784
|
+
return { type: "expression", value: expr.raw };
|
|
3785
|
+
}
|
|
3786
|
+
case "flag": {
|
|
3787
|
+
const flag = value;
|
|
3788
|
+
return { type: "literal", value: flag.enabled };
|
|
3789
|
+
}
|
|
3790
|
+
default:
|
|
3791
|
+
return { type: "literal", value: String(value.value ?? "") };
|
|
3792
|
+
}
|
|
3793
|
+
}
|
|
3794
|
+
|
|
3425
3795
|
// src/api/create-dsl.ts
|
|
3426
3796
|
var DSLRegistry = class {
|
|
3427
3797
|
constructor(config) {
|
|
@@ -6393,6 +6763,138 @@ function normalized(stem, confidence, metadata) {
|
|
|
6393
6763
|
return { stem, confidence };
|
|
6394
6764
|
}
|
|
6395
6765
|
|
|
6766
|
+
// src/core/tokenization/morphology/base-normalizer.ts
|
|
6767
|
+
var BaseMorphologicalNormalizer = class {
|
|
6768
|
+
constructor(config) {
|
|
6769
|
+
this.language = config.language;
|
|
6770
|
+
this.config = {
|
|
6771
|
+
minWordLength: 3,
|
|
6772
|
+
minStemLength: 2,
|
|
6773
|
+
...config
|
|
6774
|
+
};
|
|
6775
|
+
}
|
|
6776
|
+
/**
|
|
6777
|
+
* Standard normalization pipeline. Override for custom behavior.
|
|
6778
|
+
*/
|
|
6779
|
+
normalize(word) {
|
|
6780
|
+
const lower = word.toLowerCase();
|
|
6781
|
+
if (this.isAlreadyNormalized(lower)) {
|
|
6782
|
+
return noChange(word);
|
|
6783
|
+
}
|
|
6784
|
+
if (this.config.reflexiveSuffixes) {
|
|
6785
|
+
const reflexive = this.tryReflexiveNormalization(lower);
|
|
6786
|
+
if (reflexive) return reflexive;
|
|
6787
|
+
}
|
|
6788
|
+
if (this.config.endings) {
|
|
6789
|
+
const conjugation = this.tryConjugationEndings(lower);
|
|
6790
|
+
if (conjugation) return conjugation;
|
|
6791
|
+
}
|
|
6792
|
+
if (this.config.suffixRules) {
|
|
6793
|
+
const suffix = this.trySuffixRules(lower);
|
|
6794
|
+
if (suffix) return suffix;
|
|
6795
|
+
}
|
|
6796
|
+
if (this.config.prefixRules) {
|
|
6797
|
+
const prefix = this.tryPrefixRules(lower);
|
|
6798
|
+
if (prefix) return prefix;
|
|
6799
|
+
}
|
|
6800
|
+
return noChange(word);
|
|
6801
|
+
}
|
|
6802
|
+
/**
|
|
6803
|
+
* Check if word is already in dictionary form (e.g., ends in -ar/-er/-ir).
|
|
6804
|
+
* Override for language-specific checks.
|
|
6805
|
+
*/
|
|
6806
|
+
isAlreadyNormalized(word) {
|
|
6807
|
+
if (this.config.infinitiveEndings) {
|
|
6808
|
+
return this.config.infinitiveEndings.some((e) => word.endsWith(e));
|
|
6809
|
+
}
|
|
6810
|
+
return false;
|
|
6811
|
+
}
|
|
6812
|
+
/**
|
|
6813
|
+
* Try to strip reflexive suffixes and normalize the remainder.
|
|
6814
|
+
* Common in Romance languages (Spanish, Portuguese, French).
|
|
6815
|
+
*/
|
|
6816
|
+
tryReflexiveNormalization(word) {
|
|
6817
|
+
const suffixes = this.config.reflexiveSuffixes;
|
|
6818
|
+
if (!suffixes) return null;
|
|
6819
|
+
for (const suffix of suffixes) {
|
|
6820
|
+
if (!word.endsWith(suffix)) continue;
|
|
6821
|
+
const remainder = word.slice(0, -suffix.length);
|
|
6822
|
+
if (this.isAlreadyNormalized(remainder)) {
|
|
6823
|
+
return normalized(remainder, 0.88, {
|
|
6824
|
+
removedSuffixes: [suffix],
|
|
6825
|
+
conjugationType: "reflexive"
|
|
6826
|
+
});
|
|
6827
|
+
}
|
|
6828
|
+
const inner = this.tryConjugationEndings(remainder) || this.trySuffixRules(remainder);
|
|
6829
|
+
if (inner && inner.stem !== remainder) {
|
|
6830
|
+
return normalized(inner.stem, inner.confidence * 0.95, {
|
|
6831
|
+
removedSuffixes: [suffix, ...inner.metadata?.removedSuffixes || []],
|
|
6832
|
+
conjugationType: "reflexive"
|
|
6833
|
+
});
|
|
6834
|
+
}
|
|
6835
|
+
}
|
|
6836
|
+
return null;
|
|
6837
|
+
}
|
|
6838
|
+
/**
|
|
6839
|
+
* Try conjugation endings (verb class endings like -ar/-er/-ir patterns).
|
|
6840
|
+
* Endings must be pre-sorted longest-first.
|
|
6841
|
+
*/
|
|
6842
|
+
tryConjugationEndings(word) {
|
|
6843
|
+
const endings = this.config.endings;
|
|
6844
|
+
if (!endings) return null;
|
|
6845
|
+
const minStem = this.config.minStemLength ?? 2;
|
|
6846
|
+
for (const rule of endings) {
|
|
6847
|
+
if (!word.endsWith(rule.ending)) continue;
|
|
6848
|
+
const stemBase = word.slice(0, -rule.ending.length);
|
|
6849
|
+
if (stemBase.length < minStem) continue;
|
|
6850
|
+
const infinitive = stemBase + rule.stem;
|
|
6851
|
+
return normalized(infinitive, rule.confidence, {
|
|
6852
|
+
removedSuffixes: [rule.ending],
|
|
6853
|
+
conjugationType: rule.type
|
|
6854
|
+
});
|
|
6855
|
+
}
|
|
6856
|
+
return null;
|
|
6857
|
+
}
|
|
6858
|
+
/**
|
|
6859
|
+
* Try SuffixRule-style normalization.
|
|
6860
|
+
* Rules must be pre-sorted longest-first.
|
|
6861
|
+
*/
|
|
6862
|
+
trySuffixRules(word) {
|
|
6863
|
+
const rules = this.config.suffixRules;
|
|
6864
|
+
if (!rules) return null;
|
|
6865
|
+
const defaultMinStem = this.config.minStemLength ?? 2;
|
|
6866
|
+
for (const rule of rules) {
|
|
6867
|
+
if (!word.endsWith(rule.pattern)) continue;
|
|
6868
|
+
const stem = word.slice(0, -rule.pattern.length);
|
|
6869
|
+
const minStem = rule.minStemLength ?? defaultMinStem;
|
|
6870
|
+
if (stem.length < minStem) continue;
|
|
6871
|
+
const result = stem + (rule.replacement || "");
|
|
6872
|
+
return normalized(result, rule.confidence, {
|
|
6873
|
+
removedSuffixes: [rule.pattern],
|
|
6874
|
+
...rule.conjugationType && { conjugationType: rule.conjugationType }
|
|
6875
|
+
});
|
|
6876
|
+
}
|
|
6877
|
+
return null;
|
|
6878
|
+
}
|
|
6879
|
+
/**
|
|
6880
|
+
* Try PrefixRule-style normalization.
|
|
6881
|
+
*/
|
|
6882
|
+
tryPrefixRules(word) {
|
|
6883
|
+
const rules = this.config.prefixRules;
|
|
6884
|
+
if (!rules) return null;
|
|
6885
|
+
for (const rule of rules) {
|
|
6886
|
+
if (!word.startsWith(rule.pattern)) continue;
|
|
6887
|
+
const remainder = word.slice(rule.pattern.length);
|
|
6888
|
+
const minRemaining = rule.minRemaining ?? this.config.minStemLength ?? 2;
|
|
6889
|
+
if (remainder.length < minRemaining) continue;
|
|
6890
|
+
return normalized(remainder, 1 - rule.confidencePenalty, {
|
|
6891
|
+
removedPrefixes: [rule.pattern]
|
|
6892
|
+
});
|
|
6893
|
+
}
|
|
6894
|
+
return null;
|
|
6895
|
+
}
|
|
6896
|
+
};
|
|
6897
|
+
|
|
6396
6898
|
// src/training/jsonl-writer.ts
|
|
6397
6899
|
function toJSONL(pairs) {
|
|
6398
6900
|
return pairs.map((pair) => JSON.stringify(toJSONLRow(pair))).join("\n");
|
|
@@ -6789,6 +7291,7 @@ function accumulateIndented(statements, config) {
|
|
|
6789
7291
|
}
|
|
6790
7292
|
export {
|
|
6791
7293
|
AOTOrchestrator,
|
|
7294
|
+
BaseMorphologicalNormalizer,
|
|
6792
7295
|
BaseTokenizer,
|
|
6793
7296
|
CrossDomainDispatcher,
|
|
6794
7297
|
DEFAULT_OPERATORS,
|
|
@@ -6871,8 +7374,10 @@ export {
|
|
|
6871
7374
|
isCSSPropertyRef,
|
|
6872
7375
|
isCSSSelector,
|
|
6873
7376
|
isClassName,
|
|
7377
|
+
isCompoundSyntax,
|
|
6874
7378
|
isContextAwareExtractor,
|
|
6875
7379
|
isDigit,
|
|
7380
|
+
isDocumentSyntax,
|
|
6876
7381
|
isEnvelope,
|
|
6877
7382
|
isExplicitSyntax,
|
|
6878
7383
|
isIdSelector,
|
|
@@ -6896,14 +7401,19 @@ export {
|
|
|
6896
7401
|
needsDisambiguation,
|
|
6897
7402
|
noChange,
|
|
6898
7403
|
normalized,
|
|
7404
|
+
parseCompound,
|
|
7405
|
+
parseDocument,
|
|
6899
7406
|
parseExplicit,
|
|
6900
7407
|
parseJSONL,
|
|
6901
7408
|
patternMatcher,
|
|
6902
7409
|
registryToAOTBackends,
|
|
7410
|
+
renderDocument,
|
|
6903
7411
|
renderExplicit,
|
|
6904
7412
|
renderExpr,
|
|
6905
7413
|
reorderRoles,
|
|
6906
7414
|
semanticNodeToJSON,
|
|
7415
|
+
semanticNodeToRuntimeAST,
|
|
7416
|
+
semanticValueToAST,
|
|
6907
7417
|
synthesizeFromSchemas,
|
|
6908
7418
|
toEnvelopeJSON,
|
|
6909
7419
|
toJSONL,
|