@lokascript/framework 4.7.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 +35 -1
- package/dist/api/create-dsl.d.ts +5 -2
- package/dist/api/create-dsl.d.ts.map +1 -1
- package/dist/api/index.js +45 -5
- package/dist/api/index.js.map +1 -1
- package/dist/core/index.js +10 -0
- package/dist/core/index.js.map +1 -1
- package/dist/core/matching/stock-reader.d.ts +8 -1
- package/dist/core/matching/stock-reader.d.ts.map +1 -1
- package/dist/core/pattern-matching/index.js.map +1 -1
- package/dist/core/types.d.ts +5 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/generation/index.js +12 -2
- package/dist/generation/index.js.map +1 -1
- package/dist/index.cjs +45 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +45 -5
- package/dist/index.js.map +1 -1
- package/dist/ir/index.js.map +1 -1
- package/dist/parsing/index.js.map +1 -1
- package/dist/testing/gate/index.js +44 -12
- package/dist/testing/gate/index.js.map +1 -1
- package/dist/testing/gate/round-trip.d.ts +24 -3
- package/dist/testing/gate/round-trip.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/api/create-dsl-engine.test.ts +65 -1
- package/src/api/create-dsl.test.ts +7 -0
- package/src/api/create-dsl.ts +32 -4
- package/src/core/matching/stock-reader.test.ts +87 -3
- package/src/core/matching/stock-reader.ts +20 -1
- package/src/core/types.ts +5 -0
- package/src/generation/schema-patterns.test.ts +41 -1
- package/src/generation/schema-patterns.ts +12 -0
- package/src/testing/gate/round-trip.test.ts +99 -9
- package/src/testing/gate/round-trip.ts +76 -11
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,39 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [4.8.0] - 2026-10-16
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **`@lokascript/intent` / `@lokascript/framework`: a role's word list, `RoleSpec.words`** (domains
|
|
15
|
+
rethink Phase 5). Per language, with `'*'` for every language; the core engine (`engine: 'core'`)
|
|
16
|
+
reads such a slot only from one of its words, compared lower-cased, so a word the grammar does not
|
|
17
|
+
know is left unread and the input refused, instead of read as the value. It is for a domain's fixed
|
|
18
|
+
words: behaviorspec's `when clicks zzqx` read actor `clicks`, action `zzqx`, and compiled to a
|
|
19
|
+
comment. The schema-pattern generator carries the pattern language's list on the role token
|
|
20
|
+
(`RolePatternToken.words`); a grammar that lists no words generates the same patterns. The classic
|
|
21
|
+
engine and semantic ignore it.
|
|
22
|
+
- **`@lokascript/framework/testing/gate`: `GateFixture.commands`**, commands the gate writes besides
|
|
23
|
+
the generated ones, where one role's value depends on another's (behaviorspec's
|
|
24
|
+
`[given subject:#box value:visible]` beside `[given subject:viewport value:1024x768]`).
|
|
25
|
+
- **`@lokascript/framework`: `UnreadInputError.hint`.** Where the last word a parse read as a value is
|
|
26
|
+
followed by another bare word, the refusal adds `if "buy milk" is one value, quote it` (todo's
|
|
27
|
+
`add buy milk to groceries`, whose item is quoted).
|
|
28
|
+
|
|
29
|
+
### Changed
|
|
30
|
+
|
|
31
|
+
- **`@lokascript/framework/testing/gate`: leftover input read into a role is `pass`.** An unknown word
|
|
32
|
+
after a command used to be `silent` wherever the parse accepted it. It is `pass` when the parse is
|
|
33
|
+
the text's own with the word in one role (an open-text role extended by it, sql's
|
|
34
|
+
`where active zzqx`, or a role the text left empty, todo's `list zzqx`) and the word reaches the
|
|
35
|
+
compiled code; `refused` when the input stops compiling; `silent` otherwise (read as something
|
|
36
|
+
else, or the word goes nowhere). `readsWord` is exported. On the domains (domains 4.1.0, this
|
|
37
|
+
framework packed): 74 leftover cases accepted → read, 0 worse, 0 parses moved.
|
|
38
|
+
- **`@lokascript/framework`: the stock reader's optional slot does not read a marker the rest of the
|
|
39
|
+
pattern owes**, through its optional groups, as a required slot already does not read the marker
|
|
40
|
+
next to it (behaviorspec's `when user types into #search`, whose typed text is optional and has no
|
|
41
|
+
marker). No domain parse moves.
|
|
42
|
+
|
|
10
43
|
## [4.7.0] - 2026-10-16
|
|
11
44
|
|
|
12
45
|
### Added
|
|
@@ -1821,7 +1854,8 @@ _Synchronized version release. See git history for details._
|
|
|
1821
1854
|
- npm access token stored in GitHub Secrets
|
|
1822
1855
|
- 2FA recommended for npm organization
|
|
1823
1856
|
|
|
1824
|
-
[Unreleased]: https://github.com/codetalcott/hyperfixi/compare/v4.
|
|
1857
|
+
[Unreleased]: https://github.com/codetalcott/hyperfixi/compare/v4.8.0...HEAD
|
|
1858
|
+
[4.8.0]: https://github.com/codetalcott/hyperfixi/compare/v4.7.0...v4.8.0
|
|
1825
1859
|
[4.7.0]: https://github.com/codetalcott/hyperfixi/compare/v4.6.0...v4.7.0
|
|
1826
1860
|
[4.6.0]: https://github.com/codetalcott/hyperfixi/compare/v4.5.0...v4.6.0
|
|
1827
1861
|
[4.5.0]: https://github.com/codetalcott/hyperfixi/compare/v4.4.0...v4.5.0
|
package/dist/api/create-dsl.d.ts
CHANGED
|
@@ -178,15 +178,18 @@ export interface CompileResult {
|
|
|
178
178
|
/**
|
|
179
179
|
* Thrown by `parse` (and so `compile`, `validate`, `translate`) when the input reads as a command
|
|
180
180
|
* only by leaving some of it unread: `select name from users banana` is not `select name from users`.
|
|
181
|
-
* `reading` is what the longest such match read, `unread` the text after it.
|
|
181
|
+
* `reading` is what the longest such match read, `unread` the text after it. `hint`, when there is
|
|
182
|
+
* one, says how the input might read: where a bare word read as a value is followed by another, the
|
|
183
|
+
* two may be one value written without quotes (todo `add buy milk to groceries`).
|
|
182
184
|
*/
|
|
183
185
|
export declare class UnreadInputError extends Error {
|
|
184
186
|
readonly input: string;
|
|
185
187
|
readonly language: string;
|
|
186
188
|
readonly reading: SemanticNode;
|
|
187
189
|
readonly unread: string;
|
|
190
|
+
readonly hint?: string | undefined;
|
|
188
191
|
readonly code = "unread-input";
|
|
189
|
-
constructor(input: string, language: string, reading: SemanticNode, unread: string);
|
|
192
|
+
constructor(input: string, language: string, reading: SemanticNode, unread: string, hint?: string | undefined);
|
|
190
193
|
}
|
|
191
194
|
/** Options for `translate`. */
|
|
192
195
|
export interface TranslateOptions {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-dsl.d.ts","sourceRoot":"","sources":["../../src/api/create-dsl.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EACV,YAAY,
|
|
1
|
+
{"version":3,"file":"create-dsl.d.ts","sourceRoot":"","sources":["../../src/api/create-dsl.ts"],"names":[],"mappings":"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"}
|
package/dist/api/index.js
CHANGED
|
@@ -1547,6 +1547,11 @@ function readGreedy(tokens, stopMarkers) {
|
|
|
1547
1547
|
}
|
|
1548
1548
|
return values.length > 0 ? { type: "expression", raw: values.join(" ") } : null;
|
|
1549
1549
|
}
|
|
1550
|
+
function isListedWord(token, words) {
|
|
1551
|
+
if (!token) return false;
|
|
1552
|
+
const forms = [token.value, token.normalized].filter((f) => typeof f === "string");
|
|
1553
|
+
return forms.some((form) => words.some((word) => word.toLowerCase() === form.toLowerCase()));
|
|
1554
|
+
}
|
|
1550
1555
|
var StockValueReader = class {
|
|
1551
1556
|
constructor(view, profiles = {}) {
|
|
1552
1557
|
this.view = view;
|
|
@@ -1579,6 +1584,11 @@ var StockValueReader = class {
|
|
|
1579
1584
|
const token = tokens.peek();
|
|
1580
1585
|
if (token && this.view.wouldMatch(next, token)) return false;
|
|
1581
1586
|
}
|
|
1587
|
+
if (slot.words && !isListedWord(tokens.peek(), slot.words)) return slot.optional ?? false;
|
|
1588
|
+
if (slot.optional && !slot.greedy) {
|
|
1589
|
+
const token = tokens.peek();
|
|
1590
|
+
if (token && isStopMarker(token, greedyStopMarkers(this.view.patternRest()))) return true;
|
|
1591
|
+
}
|
|
1582
1592
|
if (slot.greedy) {
|
|
1583
1593
|
const value = readGreedy(tokens, greedyStopMarkers(this.view.patternRest()));
|
|
1584
1594
|
if (value) {
|
|
@@ -1968,11 +1978,13 @@ function generateVerbFirstPattern(schema, profile, config = defaultConfig2) {
|
|
|
1968
1978
|
const verbToken = keyword.alternatives ? { type: "literal", value: keyword.primary, alternatives: keyword.alternatives } : { type: "literal", value: keyword.primary };
|
|
1969
1979
|
const roleTokens = requiredRoles.flatMap((r) => {
|
|
1970
1980
|
const prefix = r.valuePrefixLiteral?.[profile.code];
|
|
1981
|
+
const words = wordsFor(r, profile.code);
|
|
1971
1982
|
const roleToken = {
|
|
1972
1983
|
type: "role",
|
|
1973
1984
|
role: r.role,
|
|
1974
1985
|
optional: false,
|
|
1975
|
-
expectedTypes: r.expectedTypes
|
|
1986
|
+
expectedTypes: r.expectedTypes,
|
|
1987
|
+
...words ? { words } : {}
|
|
1976
1988
|
};
|
|
1977
1989
|
return prefix ? [{ type: "literal", value: prefix }, roleToken] : [roleToken];
|
|
1978
1990
|
});
|
|
@@ -2142,11 +2154,17 @@ function buildRoleTokens(grammar, schema, profile) {
|
|
|
2142
2154
|
}
|
|
2143
2155
|
return tokens;
|
|
2144
2156
|
}
|
|
2157
|
+
function wordsFor(roleSpec, language) {
|
|
2158
|
+
if (!roleSpec.words) return void 0;
|
|
2159
|
+
const words = [...roleSpec.words["*"] ?? [], ...roleSpec.words[language] ?? []];
|
|
2160
|
+
return words.length > 0 ? words : void 0;
|
|
2161
|
+
}
|
|
2145
2162
|
function buildRoleToken(roleSpec, profile) {
|
|
2146
2163
|
const tokens = [];
|
|
2147
2164
|
const overrideMarker = roleSpec.markerOverride?.[profile.code];
|
|
2148
2165
|
const defaultMarker = profile.roleMarkers[roleSpec.role];
|
|
2149
2166
|
const suppressMarker = roleSpec.renderOverride?.[profile.code] === "";
|
|
2167
|
+
const words = wordsFor(roleSpec, profile.code);
|
|
2150
2168
|
const roleValueToken = {
|
|
2151
2169
|
type: "role",
|
|
2152
2170
|
role: roleSpec.role,
|
|
@@ -2158,7 +2176,9 @@ function buildRoleToken(roleSpec, profile) {
|
|
|
2158
2176
|
...roleSpec.valueShape !== void 0 ? { valueShape: roleSpec.valueShape } : {},
|
|
2159
2177
|
// A greedy role reads up to the next marker the pattern owes (a domain's
|
|
2160
2178
|
// SQL condition); its value reader does the reading.
|
|
2161
|
-
...roleSpec.greedy ? { greedy: true } : {}
|
|
2179
|
+
...roleSpec.greedy ? { greedy: true } : {},
|
|
2180
|
+
// A role with a word list reads only one of its words.
|
|
2181
|
+
...words ? { words } : {}
|
|
2162
2182
|
};
|
|
2163
2183
|
const prefixLiteral = roleSpec.valuePrefixLiteral?.[profile.code];
|
|
2164
2184
|
const pushPrefixed = () => {
|
|
@@ -2683,12 +2703,15 @@ function readingDifference(source, back) {
|
|
|
2683
2703
|
|
|
2684
2704
|
// src/api/create-dsl.ts
|
|
2685
2705
|
var UnreadInputError = class extends Error {
|
|
2686
|
-
constructor(input, language, reading, unread) {
|
|
2687
|
-
super(
|
|
2706
|
+
constructor(input, language, reading, unread, hint) {
|
|
2707
|
+
super(
|
|
2708
|
+
`"${input}" (${language}) reads as ${reading.action} only by leaving "${unread}" unread` + (hint ? `; ${hint}` : "")
|
|
2709
|
+
);
|
|
2688
2710
|
this.input = input;
|
|
2689
2711
|
this.language = language;
|
|
2690
2712
|
this.reading = reading;
|
|
2691
2713
|
this.unread = unread;
|
|
2714
|
+
this.hint = hint;
|
|
2692
2715
|
this.code = "unread-input";
|
|
2693
2716
|
this.name = "UnreadInputError";
|
|
2694
2717
|
}
|
|
@@ -2807,15 +2830,32 @@ var MultilingualDSLImpl = class {
|
|
|
2807
2830
|
}
|
|
2808
2831
|
if (longestPartial) {
|
|
2809
2832
|
const unread = tokens.tokens.slice(longestPartial.consumedTokens).map((t) => t.value).join(" ");
|
|
2833
|
+
const hint = this.quoteHint(longestPartial, tokens.tokens);
|
|
2810
2834
|
throw new UnreadInputError(
|
|
2811
2835
|
input,
|
|
2812
2836
|
language,
|
|
2813
2837
|
this.nodeOf(longestPartial, input, language),
|
|
2814
|
-
unread
|
|
2838
|
+
unread,
|
|
2839
|
+
...hint ? [hint] : []
|
|
2815
2840
|
);
|
|
2816
2841
|
}
|
|
2817
2842
|
throw new Error(`No pattern matched for input: ${input}`);
|
|
2818
2843
|
}
|
|
2844
|
+
/**
|
|
2845
|
+
* `if "buy milk" is one value, quote it`, where the last token a match read is a bare word it took
|
|
2846
|
+
* as a role's value (a role with no word list) and the first it left is a bare word too.
|
|
2847
|
+
*/
|
|
2848
|
+
quoteHint(match, tokens) {
|
|
2849
|
+
const last = tokens[match.consumedTokens - 1];
|
|
2850
|
+
const next = tokens[match.consumedTokens];
|
|
2851
|
+
if (last?.kind !== "identifier" || next?.kind !== "identifier") return void 0;
|
|
2852
|
+
const schema = this.schemaLookup.getSchema(match.pattern.command);
|
|
2853
|
+
const role = [...match.captured].find(
|
|
2854
|
+
([, v]) => v.raw === last.value
|
|
2855
|
+
)?.[0];
|
|
2856
|
+
if (!role || schema?.roles.find((r) => r.role === role)?.words) return void 0;
|
|
2857
|
+
return `if "${last.value} ${next.value}" is one value, quote it`;
|
|
2858
|
+
}
|
|
2819
2859
|
nodeOf(match, input, language) {
|
|
2820
2860
|
return {
|
|
2821
2861
|
kind: "command",
|