@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.
Files changed (36) hide show
  1. package/CHANGELOG.md +35 -1
  2. package/dist/api/create-dsl.d.ts +5 -2
  3. package/dist/api/create-dsl.d.ts.map +1 -1
  4. package/dist/api/index.js +45 -5
  5. package/dist/api/index.js.map +1 -1
  6. package/dist/core/index.js +10 -0
  7. package/dist/core/index.js.map +1 -1
  8. package/dist/core/matching/stock-reader.d.ts +8 -1
  9. package/dist/core/matching/stock-reader.d.ts.map +1 -1
  10. package/dist/core/pattern-matching/index.js.map +1 -1
  11. package/dist/core/types.d.ts +5 -0
  12. package/dist/core/types.d.ts.map +1 -1
  13. package/dist/core/types.js.map +1 -1
  14. package/dist/generation/index.js +12 -2
  15. package/dist/generation/index.js.map +1 -1
  16. package/dist/index.cjs +45 -5
  17. package/dist/index.cjs.map +1 -1
  18. package/dist/index.js +45 -5
  19. package/dist/index.js.map +1 -1
  20. package/dist/ir/index.js.map +1 -1
  21. package/dist/parsing/index.js.map +1 -1
  22. package/dist/testing/gate/index.js +44 -12
  23. package/dist/testing/gate/index.js.map +1 -1
  24. package/dist/testing/gate/round-trip.d.ts +24 -3
  25. package/dist/testing/gate/round-trip.d.ts.map +1 -1
  26. package/package.json +2 -2
  27. package/src/api/create-dsl-engine.test.ts +65 -1
  28. package/src/api/create-dsl.test.ts +7 -0
  29. package/src/api/create-dsl.ts +32 -4
  30. package/src/core/matching/stock-reader.test.ts +87 -3
  31. package/src/core/matching/stock-reader.ts +20 -1
  32. package/src/core/types.ts +5 -0
  33. package/src/generation/schema-patterns.test.ts +41 -1
  34. package/src/generation/schema-patterns.ts +12 -0
  35. package/src/testing/gate/round-trip.test.ts +99 -9
  36. 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.7.0...HEAD
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
@@ -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":"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"}
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(`"${input}" (${language}) reads as ${reading.action} only by leaving "${unread}" unread`);
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",