@traqula/core 1.1.0 → 1.1.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.
Files changed (40) hide show
  1. package/README.md +1 -1
  2. package/dist/cjs/lib/generator-builder/generatorBuilder.js +38 -0
  3. package/dist/cjs/lib/generator-builder/generatorBuilder.js.map +1 -1
  4. package/dist/cjs/lib/indirection-builder/IndirBuilder.js +72 -0
  5. package/dist/cjs/lib/indirection-builder/IndirBuilder.js.map +1 -1
  6. package/dist/cjs/lib/indirection-builder/helpers.js.map +1 -1
  7. package/dist/cjs/lib/lexer-builder/LexerBuilder.js +56 -2
  8. package/dist/cjs/lib/lexer-builder/LexerBuilder.js.map +1 -1
  9. package/dist/cjs/lib/parser-builder/parserBuilder.js +32 -0
  10. package/dist/cjs/lib/parser-builder/parserBuilder.js.map +1 -1
  11. package/dist/cjs/lib/transformers/TransformerObject.js +11 -0
  12. package/dist/cjs/lib/transformers/TransformerObject.js.map +1 -1
  13. package/dist/cjs/lib/transformers/TransformerSubTyped.js +13 -0
  14. package/dist/cjs/lib/transformers/TransformerSubTyped.js.map +1 -1
  15. package/dist/cjs/lib/transformers/TransformerTyped.js +13 -0
  16. package/dist/cjs/lib/transformers/TransformerTyped.js.map +1 -1
  17. package/dist/esm/lib/generator-builder/generatorBuilder.d.ts +38 -0
  18. package/dist/esm/lib/generator-builder/generatorBuilder.js +38 -0
  19. package/dist/esm/lib/generator-builder/generatorBuilder.js.map +1 -1
  20. package/dist/esm/lib/indirection-builder/IndirBuilder.d.ts +53 -0
  21. package/dist/esm/lib/indirection-builder/IndirBuilder.js +72 -0
  22. package/dist/esm/lib/indirection-builder/IndirBuilder.js.map +1 -1
  23. package/dist/esm/lib/indirection-builder/helpers.d.ts +24 -0
  24. package/dist/esm/lib/indirection-builder/helpers.js.map +1 -1
  25. package/dist/esm/lib/lexer-builder/LexerBuilder.d.ts +56 -2
  26. package/dist/esm/lib/lexer-builder/LexerBuilder.js +56 -2
  27. package/dist/esm/lib/lexer-builder/LexerBuilder.js.map +1 -1
  28. package/dist/esm/lib/parser-builder/parserBuilder.d.ts +42 -0
  29. package/dist/esm/lib/parser-builder/parserBuilder.js +32 -0
  30. package/dist/esm/lib/parser-builder/parserBuilder.js.map +1 -1
  31. package/dist/esm/lib/transformers/TransformerObject.d.ts +11 -0
  32. package/dist/esm/lib/transformers/TransformerObject.js +11 -0
  33. package/dist/esm/lib/transformers/TransformerObject.js.map +1 -1
  34. package/dist/esm/lib/transformers/TransformerSubTyped.d.ts +13 -0
  35. package/dist/esm/lib/transformers/TransformerSubTyped.js +13 -0
  36. package/dist/esm/lib/transformers/TransformerSubTyped.js.map +1 -1
  37. package/dist/esm/lib/transformers/TransformerTyped.d.ts +26 -0
  38. package/dist/esm/lib/transformers/TransformerTyped.js +13 -0
  39. package/dist/esm/lib/transformers/TransformerTyped.js.map +1 -1
  40. package/package.json +2 -2
package/README.md CHANGED
@@ -168,7 +168,7 @@ C[traqulaIndentation] += 2;
168
168
  The generator builder can significantly help you with creating a round tripping parser.
169
169
  Basically what that allows you to do is to keep information that the AST finds 'unimportant' within the generated string.
170
170
  Take for example capitalization and spaces in the sparql spec.
171
- Both are ignored in the AST, but if you want to generate the same string out of your AST, yuo need to store them somewhere.
171
+ Both are ignored in the AST, but if you want to generate the same string out of your AST, you need to store them somewhere.
172
172
  Traqula helps you store this information using it's `Node` `Localization`.
173
173
 
174
174
  Localization basically allows you to remember what _portion of the original string_ a node represents.
@@ -12,6 +12,14 @@ function listToRuleDefMap(rules) {
12
12
  }
13
13
  return newRules;
14
14
  }
15
+ /**
16
+ * Builder for composing modular code generators from rule definitions.
17
+ * Mirrors the {@link ParserBuilder} API but targets code generation (AST → string)
18
+ * instead of parsing (string → AST).
19
+ *
20
+ * Builders mutate internal state and return `this`.
21
+ * Always start by copying an existing builder with `GeneratorBuilder.create(existingBuilder)`.
22
+ */
15
23
  class GeneratorBuilder {
16
24
  static create(start) {
17
25
  if (Array.isArray(start)) {
@@ -23,9 +31,20 @@ class GeneratorBuilder {
23
31
  constructor(startRules) {
24
32
  this.rules = startRules;
25
33
  }
34
+ /**
35
+ * Narrow the builder's context type parameter to a more specific subtype.
36
+ * This is a zero-cost type-level operation — the builder instance is returned as-is
37
+ * but with updated type parameters.
38
+ */
26
39
  widenContext() {
27
40
  return this;
28
41
  }
42
+ /**
43
+ * Update the type signatures (return types and/or parameter types) of existing rules
44
+ * without changing their implementations. Use this when a patched rule changes the types
45
+ * flowing through downstream rules that don't need new implementations.
46
+ * This is a zero-cost type-level operation.
47
+ */
29
48
  typePatch() {
30
49
  return this;
31
50
  }
@@ -55,6 +74,12 @@ class GeneratorBuilder {
55
74
  addRule(rule) {
56
75
  return this.addRuleRedundant(rule);
57
76
  }
77
+ /**
78
+ * Add multiple rules at once using rest parameters.
79
+ * Provides better TypeScript type inference than calling {@link addRule} in a loop,
80
+ * but avoid passing too many rules at once as this can cause TypeScript compilation slowdowns.
81
+ * TypeScript errors if any rule name conflicts with an existing one.
82
+ */
58
83
  addMany(...rules) {
59
84
  this.rules = { ...this.rules, ...listToRuleDefMap(rules) };
60
85
  return this;
@@ -66,12 +91,21 @@ class GeneratorBuilder {
66
91
  delete this.rules[ruleName];
67
92
  return this;
68
93
  }
94
+ /**
95
+ * Delete multiple rules by name in a single call.
96
+ * @param ruleNames - Names of the rules to delete.
97
+ */
69
98
  deleteMany(...ruleNames) {
70
99
  for (const name of ruleNames) {
71
100
  delete this.rules[name];
72
101
  }
73
102
  return this;
74
103
  }
104
+ /**
105
+ * Retrieve a generator rule definition by its name.
106
+ * Useful for inspecting or wrapping existing rules when extending a generator.
107
+ * @param ruleName - The name of the rule, type-checked against the builder's known rule names.
108
+ */
75
109
  getRule(ruleName) {
76
110
  return this.rules[ruleName];
77
111
  }
@@ -110,6 +144,10 @@ class GeneratorBuilder {
110
144
  this.rules = otherRules;
111
145
  return this;
112
146
  }
147
+ /**
148
+ * Construct a generator from the registered rules.
149
+ * @returns An object with a method for each registered rule name.
150
+ */
113
151
  build() {
114
152
  return new dynamicGenerator_js_1.DynamicGenerator(this.rules);
115
153
  }
@@ -1 +1 @@
1
- {"version":3,"file":"generatorBuilder.js","sourceRoot":"","sources":["../../../../lib/generator-builder/generatorBuilder.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ParseNamesFromList } from '../parser-builder/builderTypes.js';\nimport type { CheckOverlap } from '../utils.js';\nimport type { GeneratorFromRules, GenRuleMap, GenRulesToObject } from './builderTypes.js';\nimport { DynamicGenerator } from './dynamicGenerator.js';\nimport type { GeneratorRule } from './generatorTypes.js';\n\n/**\n * Converts a list of ruledefs to a record mapping a name to the corresponding ruledef.\n */\nfunction listToRuleDefMap<T extends readonly GeneratorRule[]>(rules: T): GenRulesToObject<T> {\n const newRules: Record<string, GeneratorRule> = {};\n for (const rule of rules) {\n newRules[rule.name] = rule;\n }\n return <GenRulesToObject<T>>newRules;\n}\n\nexport class GeneratorBuilder<Context, Names extends string, RuleDefs extends GenRuleMap<Names>> {\n /**\n * Create a GeneratorBuilder from some initial grammar rules or an existing GeneratorBuilder.\n * If a GeneratorBuilder is provided, a new copy will be created.\n */\n public static create<Context, Names extends string, RuleDefs extends GenRuleMap<Names>>(\n args: GeneratorBuilder<Context, Names, RuleDefs>\n ): GeneratorBuilder<Context, Names, RuleDefs>;\n public static create<\n Rules extends readonly GeneratorRule[] = readonly GeneratorRule[],\n Context = Rules[0] extends GeneratorRule<infer context> ? context : never,\n Names extends string = ParseNamesFromList<Rules>,\n RuleDefs extends GenRuleMap<Names> = GenRulesToObject<Rules>,\n >(rules: Rules): GeneratorBuilder<Context, Names, RuleDefs>;\n public static create<\n Rules extends readonly GeneratorRule[] = readonly GeneratorRule[],\n Context = Rules[0] extends GeneratorRule<infer context> ? context : never,\n Names extends string = ParseNamesFromList<Rules>,\n RuleDefs extends GenRuleMap<Names> = GenRulesToObject<Rules>,\n >(\n start: Rules | GeneratorBuilder<Context, Names, RuleDefs>,\n ): GeneratorBuilder<Context, Names, RuleDefs> {\n if (Array.isArray(start)) {\n return <GeneratorBuilder<Context, Names, RuleDefs>> <unknown> new GeneratorBuilder(listToRuleDefMap(start));\n }\n return new GeneratorBuilder({ ...(<GeneratorBuilder<any, any, any>>start).rules });\n }\n\n private rules: RuleDefs;\n\n private constructor(startRules: RuleDefs) {\n this.rules = startRules;\n }\n\n public widenContext<NewContext extends Context>(): GeneratorBuilder<\n NewContext,\n Names,\n {[Key in keyof RuleDefs]: Key extends Names ?\n (RuleDefs[Key] extends GeneratorRule<any, any, infer RT, infer PT> ?\n GeneratorRule<NewContext, Key, RT, PT> : never)\n : never }\n > {\n return <any> this;\n }\n\n public typePatch<Patch extends {[Key in Names]?: [any] | [any, any[]]}>():\n GeneratorBuilder<Context, Names, {[Key in Names]: Key extends keyof Patch ? (\n Patch[Key] extends [any, any[]] ? GeneratorRule<Context, Key, Patch[Key][0], Patch[Key][1]> : (\n // Only one - infer arg yourself\n Patch[Key] extends [ any ] ?\n RuleDefs[Key] extends GeneratorRule<any, any, any, infer Par> ?\n GeneratorRule<Context, Key, Patch[Key][0], Par> : never\n : never\n )) : RuleDefs[Key] extends GeneratorRule<any, Key> ? RuleDefs[Key] : never\n }> {\n return <any> this;\n }\n\n /**\n * Change the implementation of an existing generator rule.\n */\n public patchRule<U extends Names, RET, ARGS extends any[]>(patch: GeneratorRule<Context, U, RET, ARGS>):\n GeneratorBuilder<Context, Names, {[Key in Names]: Key extends U ?\n GeneratorRule<Context, Key, RET, ARGS> :\n (RuleDefs[Key] extends GeneratorRule<Context, Key> ? RuleDefs[Key] : never)\n } > {\n const self = <GeneratorBuilder<Context, Names, {[Key in Names]: Key extends U ?\n GeneratorRule<Context, Key, RET, ARGS> :\n (RuleDefs[Key] extends GeneratorRule<Context, Key> ? RuleDefs[Key] : never) }>>\n <unknown> this;\n self.rules[patch.name] = <any> patch;\n return self;\n }\n\n /**\n * Add a rule to the grammar. If the rule already exists, but the implementation differs, an error will be thrown.\n */\n public addRuleRedundant<U extends string, RET, ARGS extends any[]>(rule: GeneratorRule<Context, U, RET, ARGS>):\n GeneratorBuilder<Context, Names | U, {[K in Names | U]: K extends Names ?\n (RuleDefs[K] extends GeneratorRule<Context, K> ? RuleDefs[K] : never)\n : (K extends U ? GeneratorRule<Context, K, RET, ARGS> : never)\n }> {\n const self = <GeneratorBuilder<Context, Names | U, {[K in Names | U]: K extends Names ?\n (RuleDefs[K] extends GeneratorRule<Context, K> ? RuleDefs[K] : never)\n : (K extends U ? GeneratorRule<Context, K, RET, ARGS> : never) }>>\n <unknown> this;\n const rules = <Record<string, GeneratorRule<Context>>> self.rules;\n if (rules[rule.name] !== undefined && rules[rule.name] !== rule) {\n throw new Error(`Rule ${rule.name} already exists in the GeneratorBuilder`);\n }\n rules[rule.name] = rule;\n return self;\n }\n\n /**\n * Add a rule to the grammar. Will raise a typescript error if the rule already exists in the grammar.\n */\n public addRule<U extends string, RET, ARGS extends any[]>(\n rule: CheckOverlap<U, Names, GeneratorRule<Context, U, RET, ARGS>>,\n ): GeneratorBuilder<Context, Names | U, {[K in Names | U]: K extends Names ?\n (RuleDefs[K] extends GeneratorRule<Context, K> ? RuleDefs[K] : never)\n : (K extends U ? GeneratorRule<Context, K, RET, ARGS> : never)\n }> {\n return this.addRuleRedundant(rule);\n }\n\n public addMany<U extends readonly GeneratorRule<Context>[]>(\n ...rules: CheckOverlap<ParseNamesFromList<U>, Names, U>\n ): GeneratorBuilder<\n Context,\n Names | ParseNamesFromList<U>,\n {[K in Names | ParseNamesFromList<U>]:\n K extends keyof GenRulesToObject<typeof rules> ? (\n GenRulesToObject<typeof rules>[K] extends GeneratorRule<Context, K> ? GenRulesToObject<typeof rules>[K] : never\n ) : (\n K extends Names ? (RuleDefs[K] extends GeneratorRule<Context, K> ? RuleDefs[K] : never) : never\n )\n }\n > {\n this.rules = { ...this.rules, ...listToRuleDefMap(rules) };\n return <any> <unknown> this;\n }\n\n /**\n * Delete a grammar rule by its name.\n */\n public deleteRule<U extends Names>(ruleName: U):\n GeneratorBuilder<Context, Exclude<Names, U>, {[K in Exclude<Names, U>]:\n RuleDefs[K] extends GeneratorRule<Context, K> ? RuleDefs[K] : never }> {\n delete this.rules[ruleName];\n return <GeneratorBuilder<Context, Exclude<Names, U>, {[K in Exclude<Names, U>]:\n RuleDefs[K] extends GeneratorRule<Context, K> ? RuleDefs[K] : never }>>\n <unknown> this;\n }\n\n public deleteMany<U extends Names>(...ruleNames: U[]):\n GeneratorBuilder<Context, Exclude<Names, U>, {[K in Exclude<Names, U>]:\n RuleDefs[K] extends GeneratorRule<Context, K> ? RuleDefs[K] : never }> {\n for (const name of ruleNames) {\n delete this.rules[name];\n }\n return <GeneratorBuilder<Context, Exclude<Names, U>, {[K in Exclude<Names, U>]:\n RuleDefs[K] extends GeneratorRule<Context, K> ? RuleDefs[K] : never }>>\n <unknown> this;\n }\n\n public getRule<U extends Names>(ruleName: U): RuleDefs[U] extends GeneratorRule<any, U, infer RT, infer PT> ?\n GeneratorRule<Context, U, RT, PT> : never {\n return <any> this.rules[ruleName];\n }\n\n /**\n * Merge this grammar GeneratorBuilder with another.\n * It is best to merge the bigger grammar with the smaller one.\n * If the two builders both have a grammar rule with the same name,\n * no error will be thrown case they map to the same ruledef object.\n * If they map to a different object, an error will be thrown.\n * To fix this problem, the overridingRules array should contain a rule with the same conflicting name,\n * this rule implementation will be used.\n */\n public merge<\n OtherNames extends string,\n OtherRules extends GenRuleMap<OtherNames>,\n OW extends readonly GeneratorRule<Context>[],\n >(\n GeneratorBuilder: GeneratorBuilder<Context, OtherNames, OtherRules>,\n overridingRules: OW,\n ):\n GeneratorBuilder<\n Context,\n Names | OtherNames | ParseNamesFromList<OW>,\n {[K in Names | OtherNames | ParseNamesFromList<OW>]:\n K extends keyof GenRulesToObject<OW> ? (\n GenRulesToObject<OW>[K] extends GeneratorRule<Context, K> ? GenRulesToObject<OW>[K] : never\n )\n : (\n K extends Names ? (RuleDefs[K] extends GeneratorRule<Context, K> ? RuleDefs[K] : never)\n : K extends OtherNames ? (OtherRules[K] extends GeneratorRule<Context, K> ? OtherRules[K] : never)\n : never\n ) }\n > {\n // Assume the other grammar is bigger than yours. So start from that one and add this one\n const otherRules: Record<string, GeneratorRule<Context>> = { ...GeneratorBuilder.rules };\n const myRules: Record<string, GeneratorRule<Context>> = this.rules;\n\n for (const rule of Object.values(myRules)) {\n if (otherRules[rule.name] === undefined) {\n otherRules[rule.name] = rule;\n } else {\n const existingRule = otherRules[rule.name];\n // If same rule, no issue, move on. Else\n if (existingRule !== rule) {\n const override = overridingRules.find(x => x.name === rule.name);\n // If override specified, take override, else, inform user that there is a conflict\n if (override) {\n otherRules[rule.name] = override;\n } else {\n throw new Error(`Rule with name \"${rule.name}\" already exists in the GeneratorBuilder, specify an override to resolve conflict`);\n }\n }\n }\n }\n\n this.rules = <any> <unknown> otherRules;\n return <any> <unknown> this;\n }\n\n public build(): GeneratorFromRules<Context, Names, RuleDefs> {\n return <GeneratorFromRules<Context, Names, RuleDefs>> new DynamicGenerator(this.rules);\n }\n}\n"]}
1
+ {"version":3,"file":"generatorBuilder.js","sourceRoot":"","sources":["../../../../lib/generator-builder/generatorBuilder.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ParseNamesFromList } from '../parser-builder/builderTypes.js';\nimport type { CheckOverlap } from '../utils.js';\nimport type { GeneratorFromRules, GenRuleMap, GenRulesToObject } from './builderTypes.js';\nimport { DynamicGenerator } from './dynamicGenerator.js';\nimport type { GeneratorRule } from './generatorTypes.js';\n\n/**\n * Converts a list of ruledefs to a record mapping a name to the corresponding ruledef.\n */\nfunction listToRuleDefMap<T extends readonly GeneratorRule[]>(rules: T): GenRulesToObject<T> {\n const newRules: Record<string, GeneratorRule> = {};\n for (const rule of rules) {\n newRules[rule.name] = rule;\n }\n return <GenRulesToObject<T>>newRules;\n}\n\n/**\n * Builder for composing modular code generators from rule definitions.\n * Mirrors the {@link ParserBuilder} API but targets code generation (AST → string)\n * instead of parsing (string → AST).\n *\n * Builders mutate internal state and return `this`.\n * Always start by copying an existing builder with `GeneratorBuilder.create(existingBuilder)`.\n */\nexport class GeneratorBuilder<Context, Names extends string, RuleDefs extends GenRuleMap<Names>> {\n /**\n * Create a GeneratorBuilder from some initial grammar rules or an existing GeneratorBuilder.\n * If a GeneratorBuilder is provided, a new copy will be created.\n */\n public static create<Context, Names extends string, RuleDefs extends GenRuleMap<Names>>(\n args: GeneratorBuilder<Context, Names, RuleDefs>\n ): GeneratorBuilder<Context, Names, RuleDefs>;\n public static create<\n Rules extends readonly GeneratorRule[] = readonly GeneratorRule[],\n Context = Rules[0] extends GeneratorRule<infer context> ? context : never,\n Names extends string = ParseNamesFromList<Rules>,\n RuleDefs extends GenRuleMap<Names> = GenRulesToObject<Rules>,\n >(rules: Rules): GeneratorBuilder<Context, Names, RuleDefs>;\n public static create<\n Rules extends readonly GeneratorRule[] = readonly GeneratorRule[],\n Context = Rules[0] extends GeneratorRule<infer context> ? context : never,\n Names extends string = ParseNamesFromList<Rules>,\n RuleDefs extends GenRuleMap<Names> = GenRulesToObject<Rules>,\n >(\n start: Rules | GeneratorBuilder<Context, Names, RuleDefs>,\n ): GeneratorBuilder<Context, Names, RuleDefs> {\n if (Array.isArray(start)) {\n return <GeneratorBuilder<Context, Names, RuleDefs>> <unknown> new GeneratorBuilder(listToRuleDefMap(start));\n }\n return new GeneratorBuilder({ ...(<GeneratorBuilder<any, any, any>>start).rules });\n }\n\n private rules: RuleDefs;\n\n private constructor(startRules: RuleDefs) {\n this.rules = startRules;\n }\n\n /**\n * Narrow the builder's context type parameter to a more specific subtype.\n * This is a zero-cost type-level operation — the builder instance is returned as-is\n * but with updated type parameters.\n */\n public widenContext<NewContext extends Context>(): GeneratorBuilder<\n NewContext,\n Names,\n {[Key in keyof RuleDefs]: Key extends Names ?\n (RuleDefs[Key] extends GeneratorRule<any, any, infer RT, infer PT> ?\n GeneratorRule<NewContext, Key, RT, PT> : never)\n : never }\n > {\n return <any> this;\n }\n\n /**\n * Update the type signatures (return types and/or parameter types) of existing rules\n * without changing their implementations. Use this when a patched rule changes the types\n * flowing through downstream rules that don't need new implementations.\n * This is a zero-cost type-level operation.\n */\n public typePatch<Patch extends {[Key in Names]?: [any] | [any, any[]]}>():\n GeneratorBuilder<Context, Names, {[Key in Names]: Key extends keyof Patch ? (\n Patch[Key] extends [any, any[]] ? GeneratorRule<Context, Key, Patch[Key][0], Patch[Key][1]> : (\n // Only one - infer arg yourself\n Patch[Key] extends [ any ] ?\n RuleDefs[Key] extends GeneratorRule<any, any, any, infer Par> ?\n GeneratorRule<Context, Key, Patch[Key][0], Par> : never\n : never\n )) : RuleDefs[Key] extends GeneratorRule<any, Key> ? RuleDefs[Key] : never\n }> {\n return <any> this;\n }\n\n /**\n * Change the implementation of an existing generator rule.\n */\n public patchRule<U extends Names, RET, ARGS extends any[]>(patch: GeneratorRule<Context, U, RET, ARGS>):\n GeneratorBuilder<Context, Names, {[Key in Names]: Key extends U ?\n GeneratorRule<Context, Key, RET, ARGS> :\n (RuleDefs[Key] extends GeneratorRule<Context, Key> ? RuleDefs[Key] : never)\n } > {\n const self = <GeneratorBuilder<Context, Names, {[Key in Names]: Key extends U ?\n GeneratorRule<Context, Key, RET, ARGS> :\n (RuleDefs[Key] extends GeneratorRule<Context, Key> ? RuleDefs[Key] : never) }>>\n <unknown> this;\n self.rules[patch.name] = <any> patch;\n return self;\n }\n\n /**\n * Add a rule to the grammar. If the rule already exists, but the implementation differs, an error will be thrown.\n */\n public addRuleRedundant<U extends string, RET, ARGS extends any[]>(rule: GeneratorRule<Context, U, RET, ARGS>):\n GeneratorBuilder<Context, Names | U, {[K in Names | U]: K extends Names ?\n (RuleDefs[K] extends GeneratorRule<Context, K> ? RuleDefs[K] : never)\n : (K extends U ? GeneratorRule<Context, K, RET, ARGS> : never)\n }> {\n const self = <GeneratorBuilder<Context, Names | U, {[K in Names | U]: K extends Names ?\n (RuleDefs[K] extends GeneratorRule<Context, K> ? RuleDefs[K] : never)\n : (K extends U ? GeneratorRule<Context, K, RET, ARGS> : never) }>>\n <unknown> this;\n const rules = <Record<string, GeneratorRule<Context>>> self.rules;\n if (rules[rule.name] !== undefined && rules[rule.name] !== rule) {\n throw new Error(`Rule ${rule.name} already exists in the GeneratorBuilder`);\n }\n rules[rule.name] = rule;\n return self;\n }\n\n /**\n * Add a rule to the grammar. Will raise a typescript error if the rule already exists in the grammar.\n */\n public addRule<U extends string, RET, ARGS extends any[]>(\n rule: CheckOverlap<U, Names, GeneratorRule<Context, U, RET, ARGS>>,\n ): GeneratorBuilder<Context, Names | U, {[K in Names | U]: K extends Names ?\n (RuleDefs[K] extends GeneratorRule<Context, K> ? RuleDefs[K] : never)\n : (K extends U ? GeneratorRule<Context, K, RET, ARGS> : never)\n }> {\n return this.addRuleRedundant(rule);\n }\n\n /**\n * Add multiple rules at once using rest parameters.\n * Provides better TypeScript type inference than calling {@link addRule} in a loop,\n * but avoid passing too many rules at once as this can cause TypeScript compilation slowdowns.\n * TypeScript errors if any rule name conflicts with an existing one.\n */\n public addMany<U extends readonly GeneratorRule<Context>[]>(\n ...rules: CheckOverlap<ParseNamesFromList<U>, Names, U>\n ): GeneratorBuilder<\n Context,\n Names | ParseNamesFromList<U>,\n {[K in Names | ParseNamesFromList<U>]:\n K extends keyof GenRulesToObject<typeof rules> ? (\n GenRulesToObject<typeof rules>[K] extends GeneratorRule<Context, K> ? GenRulesToObject<typeof rules>[K] : never\n ) : (\n K extends Names ? (RuleDefs[K] extends GeneratorRule<Context, K> ? RuleDefs[K] : never) : never\n )\n }\n > {\n this.rules = { ...this.rules, ...listToRuleDefMap(rules) };\n return <any> <unknown> this;\n }\n\n /**\n * Delete a grammar rule by its name.\n */\n public deleteRule<U extends Names>(ruleName: U):\n GeneratorBuilder<Context, Exclude<Names, U>, {[K in Exclude<Names, U>]:\n RuleDefs[K] extends GeneratorRule<Context, K> ? RuleDefs[K] : never }> {\n delete this.rules[ruleName];\n return <GeneratorBuilder<Context, Exclude<Names, U>, {[K in Exclude<Names, U>]:\n RuleDefs[K] extends GeneratorRule<Context, K> ? RuleDefs[K] : never }>>\n <unknown> this;\n }\n\n /**\n * Delete multiple rules by name in a single call.\n * @param ruleNames - Names of the rules to delete.\n */\n public deleteMany<U extends Names>(...ruleNames: U[]):\n GeneratorBuilder<Context, Exclude<Names, U>, {[K in Exclude<Names, U>]:\n RuleDefs[K] extends GeneratorRule<Context, K> ? RuleDefs[K] : never }> {\n for (const name of ruleNames) {\n delete this.rules[name];\n }\n return <GeneratorBuilder<Context, Exclude<Names, U>, {[K in Exclude<Names, U>]:\n RuleDefs[K] extends GeneratorRule<Context, K> ? RuleDefs[K] : never }>>\n <unknown> this;\n }\n\n /**\n * Retrieve a generator rule definition by its name.\n * Useful for inspecting or wrapping existing rules when extending a generator.\n * @param ruleName - The name of the rule, type-checked against the builder's known rule names.\n */\n public getRule<U extends Names>(ruleName: U): RuleDefs[U] extends GeneratorRule<any, U, infer RT, infer PT> ?\n GeneratorRule<Context, U, RT, PT> : never {\n return <any> this.rules[ruleName];\n }\n\n /**\n * Merge this grammar GeneratorBuilder with another.\n * It is best to merge the bigger grammar with the smaller one.\n * If the two builders both have a grammar rule with the same name,\n * no error will be thrown case they map to the same ruledef object.\n * If they map to a different object, an error will be thrown.\n * To fix this problem, the overridingRules array should contain a rule with the same conflicting name,\n * this rule implementation will be used.\n */\n public merge<\n OtherNames extends string,\n OtherRules extends GenRuleMap<OtherNames>,\n OW extends readonly GeneratorRule<Context>[],\n >(\n GeneratorBuilder: GeneratorBuilder<Context, OtherNames, OtherRules>,\n overridingRules: OW,\n ):\n GeneratorBuilder<\n Context,\n Names | OtherNames | ParseNamesFromList<OW>,\n {[K in Names | OtherNames | ParseNamesFromList<OW>]:\n K extends keyof GenRulesToObject<OW> ? (\n GenRulesToObject<OW>[K] extends GeneratorRule<Context, K> ? GenRulesToObject<OW>[K] : never\n )\n : (\n K extends Names ? (RuleDefs[K] extends GeneratorRule<Context, K> ? RuleDefs[K] : never)\n : K extends OtherNames ? (OtherRules[K] extends GeneratorRule<Context, K> ? OtherRules[K] : never)\n : never\n ) }\n > {\n // Assume the other grammar is bigger than yours. So start from that one and add this one\n const otherRules: Record<string, GeneratorRule<Context>> = { ...GeneratorBuilder.rules };\n const myRules: Record<string, GeneratorRule<Context>> = this.rules;\n\n for (const rule of Object.values(myRules)) {\n if (otherRules[rule.name] === undefined) {\n otherRules[rule.name] = rule;\n } else {\n const existingRule = otherRules[rule.name];\n // If same rule, no issue, move on. Else\n if (existingRule !== rule) {\n const override = overridingRules.find(x => x.name === rule.name);\n // If override specified, take override, else, inform user that there is a conflict\n if (override) {\n otherRules[rule.name] = override;\n } else {\n throw new Error(`Rule with name \"${rule.name}\" already exists in the GeneratorBuilder, specify an override to resolve conflict`);\n }\n }\n }\n }\n\n this.rules = <any> <unknown> otherRules;\n return <any> <unknown> this;\n }\n\n /**\n * Construct a generator from the registered rules.\n * @returns An object with a method for each registered rule name.\n */\n public build(): GeneratorFromRules<Context, Names, RuleDefs> {\n return <GeneratorFromRules<Context, Names, RuleDefs>> new DynamicGenerator(this.rules);\n }\n}\n"]}
@@ -3,6 +3,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.IndirBuilder = void 0;
4
4
  const dynamicIndirected_js_1 = require("./dynamicIndirected.js");
5
5
  const helpers_js_1 = require("./helpers.js");
6
+ /**
7
+ * Builder for composing modular transformation pipelines using indirection definitions.
8
+ * Functions registered through this builder call each other via SUBRULE, enabling the same
9
+ * modularity and extensibility as the parser and generator builders.
10
+ *
11
+ * Builders mutate internal state and return `this`.
12
+ * Always start by copying an existing builder with `IndirBuilder.create(existingBuilder)`.
13
+ */
6
14
  class IndirBuilder {
7
15
  static create(start) {
8
16
  if (Array.isArray(start)) {
@@ -14,9 +22,20 @@ class IndirBuilder {
14
22
  constructor(startRules) {
15
23
  this.rules = startRules;
16
24
  }
25
+ /**
26
+ * Narrow the builder's context type parameter to a more specific subtype.
27
+ * This is a zero-cost type-level operation — the builder instance is returned as-is
28
+ * but with updated type parameters.
29
+ */
17
30
  widenContext() {
18
31
  return this;
19
32
  }
33
+ /**
34
+ * Update the type signatures (return types and/or parameter types) of existing indirections
35
+ * without changing their implementations. Use this when a patched indirection changes the types
36
+ * flowing through downstream indirections that don't need new implementations.
37
+ * This is a zero-cost type-level operation.
38
+ */
20
39
  typePatch() {
21
40
  return this;
22
41
  }
@@ -46,6 +65,12 @@ class IndirBuilder {
46
65
  addRule(rule) {
47
66
  return this.addRuleRedundant(rule);
48
67
  }
68
+ /**
69
+ * Add multiple indirection definitions at once using rest parameters.
70
+ * Provides better TypeScript type inference than calling {@link addRule} in a loop,
71
+ * but avoid passing too many at once as this can cause TypeScript compilation slowdowns.
72
+ * TypeScript errors if any name conflicts with an existing one.
73
+ */
49
74
  addMany(...rules) {
50
75
  this.rules = { ...this.rules, ...(0, helpers_js_1.listToIndirectionMap)(rules) };
51
76
  return this;
@@ -57,15 +82,62 @@ class IndirBuilder {
57
82
  delete this.rules[ruleName];
58
83
  return this;
59
84
  }
85
+ /**
86
+ * Delete multiple indirection definitions by name in a single call.
87
+ * @param ruleNames - Names of the indirections to delete.
88
+ */
60
89
  deleteMany(...ruleNames) {
61
90
  for (const name of ruleNames) {
62
91
  delete this.rules[name];
63
92
  }
64
93
  return this;
65
94
  }
95
+ /**
96
+ * Retrieve an indirection definition by its name.
97
+ * Useful for inspecting or wrapping existing definitions when extending a pipeline.
98
+ * @param ruleName - The name of the indirection, type-checked against the builder's known names.
99
+ */
66
100
  getRule(ruleName) {
67
101
  return this.rules[ruleName];
68
102
  }
103
+ /**
104
+ * Merge this indirection builder with another.
105
+ * If the two builders both have a definition with the same name,
106
+ * no error will be thrown in case they map to the same object (by reference).
107
+ * If they map to a different object, an error will be thrown.
108
+ * To fix this problem, the overridingRules array should contain a definition with the same conflicting name,
109
+ * whose implementation will be used.
110
+ */
111
+ merge(builder, overridingRules) {
112
+ // Assume the other set is bigger than yours. So start from that one and add this one
113
+ const otherRules = { ...builder.rules };
114
+ const myRules = this.rules;
115
+ for (const rule of Object.values(myRules)) {
116
+ if (otherRules[rule.name] === undefined) {
117
+ otherRules[rule.name] = rule;
118
+ }
119
+ else {
120
+ const existingRule = otherRules[rule.name];
121
+ // If same rule, no issue, move on. Else
122
+ if (existingRule !== rule) {
123
+ const override = overridingRules.find(x => x.name === rule.name);
124
+ // If override specified, take override, else, inform user that there is a conflict
125
+ if (override) {
126
+ otherRules[rule.name] = override;
127
+ }
128
+ else {
129
+ throw new Error(`Function with name "${rule.name}" already exists in the builder, specify an override to resolve conflict`);
130
+ }
131
+ }
132
+ }
133
+ }
134
+ this.rules = otherRules;
135
+ return this;
136
+ }
137
+ /**
138
+ * Construct an indirection object from the registered definitions.
139
+ * @returns An object with a method for each registered indirection name.
140
+ */
69
141
  build() {
70
142
  return new dynamicIndirected_js_1.DynamicIndirect(this.rules);
71
143
  }
@@ -1 +1 @@
1
- {"version":3,"file":"IndirBuilder.js","sourceRoot":"","sources":["../../../../lib/indirection-builder/IndirBuilder.ts"],"names":[],"mappings":";;;AAEA,iEAAyD;AAEzD,6CAAoD;AAEpD,MAAa,YAAY;IAUhB,MAAM,CAAC,MAAM,CAMlB,KAAqD;QAErD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YACzB,OAA0D,IAAI,YAAY,CAAC,IAAA,iCAAoB,EAAC,KAAK,CAAC,CAAC,CAAC;QAC1G,CAAC;QACD,OAAO,IAAI,YAAY,CAAC,EAAE,GAAiC,KAAM,CAAC,KAAK,EAAE,CAAC,CAAC;IAC7E,CAAC;IAEO,KAAK,CAAW;IAExB,YAAoB,UAAoB;QACtC,IAAI,CAAC,KAAK,GAAG,UAAU,CAAC;IAC1B,CAAC;IAEM,YAAY;QAOjB,OAAa,IAAI,CAAC;IACpB,CAAC;IAEM,SAAS;QASd,OAAa,IAAI,CAAC;IACpB,CAAC;IAED;;OAEG;IACI,SAAS,CAA2C,KAAsC;QAK/F,MAAM,IAAI,GAEE,IAAI,CAAC;QACjB,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,GAAS,KAAK,CAAC;QACrC,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;OAEG;IACI,gBAAgB,CAA4C,IAAqC;QAKtG,MAAM,IAAI,GAGE,IAAI,CAAC;QACjB,MAAM,KAAK,GAAuC,IAAI,CAAC,KAAK,CAAC;QAC7D,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,SAAS,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC;YAChE,MAAM,IAAI,KAAK,CAAC,YAAY,IAAI,CAAC,IAAI,gCAAgC,CAAC,CAAC;QACzE,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;QACxB,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;OAEG;IACI,OAAO,CACZ,IAA6D;QAI7D,OAAO,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC;IACrC,CAAC;IAEM,OAAO,CACZ,GAAG,KAAoD;QAYvD,IAAI,CAAC,KAAK,GAAG,EAAE,GAAG,IAAI,CAAC,KAAK,EAAE,GAAG,IAAA,iCAAoB,EAAC,KAAK,CAAC,EAAE,CAAC;QAC/D,OAAuB,IAAI,CAAC;IAC9B,CAAC;IAED;;OAEG;IACI,UAAU,CAAkB,QAAW;QAG5C,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QAC5B,OAEY,IAAI,CAAC;IACnB,CAAC;IAEM,UAAU,CAAkB,GAAG,SAAc;QAGlD,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;YAC7B,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC1B,CAAC;QACD,OAEY,IAAI,CAAC;IACnB,CAAC;IAEM,OAAO,CAAkB,QAAW;QAEzC,OAAa,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IACpC,CAAC;IAEM,KAAK;QACV,OAA4D,IAAI,sCAAe,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC9F,CAAC;CACF;AAlJD,oCAkJC","sourcesContent":["import type { ParseNamesFromList } from '../parser-builder/builderTypes.js';\nimport type { CheckOverlap } from '../utils.js';\nimport { DynamicIndirect } from './dynamicIndirected.js';\nimport type { IndirDef, IndirectionMap, IndirectObjFromIndirDefs, ParseIndirsToObject } from './helpers.js';\nimport { listToIndirectionMap } from './helpers.js';\n\nexport class IndirBuilder<Context, Names extends string, RuleDefs extends IndirectionMap<Names>> {\n public static create<Context, Names extends string, RuleDefs extends IndirectionMap<Names>>(\n args: IndirBuilder<Context, Names, RuleDefs>\n ): IndirBuilder<Context, Names, RuleDefs>;\n public static create<\n Rules extends readonly IndirDef[] = readonly IndirDef[],\n Context = Rules[0] extends IndirDef<infer context> ? context : never,\n Names extends string = ParseNamesFromList<Rules>,\n RuleDefs extends IndirectionMap<Names> = ParseIndirsToObject<Rules>,\n >(rules: Rules): IndirBuilder<Context, Names, RuleDefs>;\n public static create<\n Rules extends readonly IndirDef[] = readonly IndirDef[],\n Context = Rules[0] extends IndirDef<infer context> ? context : never,\n Names extends string = ParseNamesFromList<Rules>,\n RuleDefs extends IndirectionMap<Names> = ParseIndirsToObject<Rules>,\n >(\n start: Rules | IndirBuilder<Context, Names, RuleDefs>,\n ): IndirBuilder<Context, Names, RuleDefs> {\n if (Array.isArray(start)) {\n return <IndirBuilder<Context, Names, RuleDefs>> <unknown> new IndirBuilder(listToIndirectionMap(start));\n }\n return new IndirBuilder({ ...(<IndirBuilder<any, any, any>>start).rules });\n }\n\n private rules: RuleDefs;\n\n private constructor(startRules: RuleDefs) {\n this.rules = startRules;\n }\n\n public widenContext<NewContext extends Context>(): IndirBuilder<\n NewContext,\n Names,\n {[Key in keyof RuleDefs]: Key extends Names ?\n (RuleDefs[Key] extends IndirDef<any, any, infer RT, infer PT> ? IndirDef<NewContext, Key, RT, PT> : never)\n : never }\n > {\n return <any> this;\n }\n\n public typePatch<Patch extends {[Key in Names]?: [any] | [any, any[]]}>():\n IndirBuilder<Context, Names, {[Key in Names]: Key extends keyof Patch ? (\n Patch[Key] extends [any, any[]] ? IndirDef<Context, Key, Patch[Key][0], Patch[Key][1]> : (\n // Only one - infer arg yourself\n Patch[Key] extends [ any ] ? (\n RuleDefs[Key] extends IndirDef<any, any, any, infer Par> ? IndirDef<Context, Key, Patch[Key][0], Par> : never\n ) : never\n )\n ) : (RuleDefs[Key] extends IndirDef<Context, Key> ? RuleDefs[Key] : never) }> {\n return <any> this;\n }\n\n /**\n * Change the implementation of an existing indirection.\n */\n public patchRule<U extends Names, RET, ARGS extends any[]>(patch: IndirDef<Context, U, RET, ARGS>):\n IndirBuilder<Context, Names, {[Key in Names]: Key extends U ?\n IndirDef<Context, Key, RET, ARGS> :\n (RuleDefs[Key] extends IndirDef<Context, Key> ? RuleDefs[Key] : never)\n } > {\n const self = <IndirBuilder<Context, Names, {[Key in Names]: Key extends U ?\n IndirDef<Context, Key, RET, ARGS> : (RuleDefs[Key] extends IndirDef<Context, Key> ? RuleDefs[Key] : never) }>>\n <unknown> this;\n self.rules[patch.name] = <any> patch;\n return self;\n }\n\n /**\n * Add an indirection function. If the rule already exists, but the implementation differs, an error will be thrown.\n */\n public addRuleRedundant<U extends string, RET, ARGS extends any[]>(rule: IndirDef<Context, U, RET, ARGS>):\n IndirBuilder<Context, Names | U, {[K in Names | U]: K extends U ?\n IndirDef<Context, K, RET, ARGS> :\n (K extends Names ? (RuleDefs[K] extends IndirDef<Context, K> ? RuleDefs[K] : never) : never)\n }> {\n const self = <IndirBuilder<Context, Names | U, {[K in Names | U]: K extends U ?\n IndirDef<Context, K, RET, ARGS> :\n (K extends Names ? (RuleDefs[K] extends IndirDef<Context, K> ? RuleDefs[K] : never) : never) }>>\n <unknown> this;\n const rules = <Record<string, IndirDef<Context>>> self.rules;\n if (rules[rule.name] !== undefined && rules[rule.name] !== rule) {\n throw new Error(`Function ${rule.name} already exists in the builder`);\n }\n rules[rule.name] = rule;\n return self;\n }\n\n /**\n * Add a rule to the grammar. Will raise a typescript error if the rule already exists in the grammar.\n */\n public addRule<U extends string, RET, ARGS extends any[]>(\n rule: CheckOverlap<U, Names, IndirDef<Context, U, RET, ARGS>>,\n ): IndirBuilder<Context, Names | U, {[K in Names | U]: K extends U ?\n IndirDef<Context, K, RET, ARGS> :\n (K extends Names ? (RuleDefs[K] extends IndirDef<Context, K> ? RuleDefs[K] : never) : never) }> {\n return this.addRuleRedundant(rule);\n }\n\n public addMany<U extends readonly IndirDef<Context>[]>(\n ...rules: CheckOverlap<ParseNamesFromList<U>, Names, U>\n ): IndirBuilder<\n Context,\n Names | ParseNamesFromList<U>,\n {[K in Names | ParseNamesFromList<U>]:\n K extends keyof ParseIndirsToObject<typeof rules> ? (\n ParseIndirsToObject<typeof rules>[K] extends IndirDef<Context, K> ? ParseIndirsToObject<typeof rules>[K] : never\n ) : (\n K extends Names ? (RuleDefs[K] extends IndirDef<Context, K> ? RuleDefs[K] : never) : never\n )\n }\n > {\n this.rules = { ...this.rules, ...listToIndirectionMap(rules) };\n return <any> <unknown> this;\n }\n\n /**\n * Delete a grammar rule by its name.\n */\n public deleteRule<U extends Names>(ruleName: U):\n IndirBuilder<Context, Exclude<Names, U>, {[K in Exclude<Names, U>]:\n RuleDefs[K] extends IndirDef<Context, K> ? RuleDefs[K] : never }> {\n delete this.rules[ruleName];\n return <IndirBuilder<Context, Exclude<Names, U>, {[K in Exclude<Names, U>]:\n RuleDefs[K] extends IndirDef<Context, K> ? RuleDefs[K] : never }>>\n <unknown> this;\n }\n\n public deleteMany<U extends Names>(...ruleNames: U[]):\n IndirBuilder<Context, Exclude<Names, U>, {[K in Exclude<Names, U>]:\n RuleDefs[K] extends IndirDef<Context, K> ? RuleDefs[K] : never }> {\n for (const name of ruleNames) {\n delete this.rules[name];\n }\n return <IndirBuilder<Context, Exclude<Names, U>, {[K in Exclude<Names, U>]:\n RuleDefs[K] extends IndirDef<Context, K> ? RuleDefs[K] : never }>>\n <unknown> this;\n }\n\n public getRule<U extends Names>(ruleName: U): RuleDefs[U] extends IndirDef<any, U, infer RT, infer PT> ?\n IndirDef<Context, U, RT, PT> : never {\n return <any> this.rules[ruleName];\n }\n\n public build(): IndirectObjFromIndirDefs<Context, Names, RuleDefs> {\n return <IndirectObjFromIndirDefs<Context, Names, RuleDefs>> new DynamicIndirect(this.rules);\n }\n}\n"]}
1
+ {"version":3,"file":"IndirBuilder.js","sourceRoot":"","sources":["../../../../lib/indirection-builder/IndirBuilder.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ParseNamesFromList } from '../parser-builder/builderTypes.js';\nimport type { CheckOverlap } from '../utils.js';\nimport { DynamicIndirect } from './dynamicIndirected.js';\nimport type { IndirDef, IndirectionMap, IndirectObjFromIndirDefs, ParseIndirsToObject } from './helpers.js';\nimport { listToIndirectionMap } from './helpers.js';\n\n/**\n * Builder for composing modular transformation pipelines using indirection definitions.\n * Functions registered through this builder call each other via SUBRULE, enabling the same\n * modularity and extensibility as the parser and generator builders.\n *\n * Builders mutate internal state and return `this`.\n * Always start by copying an existing builder with `IndirBuilder.create(existingBuilder)`.\n */\nexport class IndirBuilder<Context, Names extends string, RuleDefs extends IndirectionMap<Names>> {\n /**\n * Create an IndirBuilder from initial indirection definitions or an existing builder.\n * If a builder is provided, a new copy will be created.\n */\n public static create<Context, Names extends string, RuleDefs extends IndirectionMap<Names>>(\n args: IndirBuilder<Context, Names, RuleDefs>\n ): IndirBuilder<Context, Names, RuleDefs>;\n public static create<\n Rules extends readonly IndirDef[] = readonly IndirDef[],\n Context = Rules[0] extends IndirDef<infer context> ? context : never,\n Names extends string = ParseNamesFromList<Rules>,\n RuleDefs extends IndirectionMap<Names> = ParseIndirsToObject<Rules>,\n >(rules: Rules): IndirBuilder<Context, Names, RuleDefs>;\n public static create<\n Rules extends readonly IndirDef[] = readonly IndirDef[],\n Context = Rules[0] extends IndirDef<infer context> ? context : never,\n Names extends string = ParseNamesFromList<Rules>,\n RuleDefs extends IndirectionMap<Names> = ParseIndirsToObject<Rules>,\n >(\n start: Rules | IndirBuilder<Context, Names, RuleDefs>,\n ): IndirBuilder<Context, Names, RuleDefs> {\n if (Array.isArray(start)) {\n return <IndirBuilder<Context, Names, RuleDefs>> <unknown> new IndirBuilder(listToIndirectionMap(start));\n }\n return new IndirBuilder({ ...(<IndirBuilder<any, any, any>>start).rules });\n }\n\n private rules: RuleDefs;\n\n private constructor(startRules: RuleDefs) {\n this.rules = startRules;\n }\n\n /**\n * Narrow the builder's context type parameter to a more specific subtype.\n * This is a zero-cost type-level operation — the builder instance is returned as-is\n * but with updated type parameters.\n */\n public widenContext<NewContext extends Context>(): IndirBuilder<\n NewContext,\n Names,\n {[Key in keyof RuleDefs]: Key extends Names ?\n (RuleDefs[Key] extends IndirDef<any, any, infer RT, infer PT> ? IndirDef<NewContext, Key, RT, PT> : never)\n : never }\n > {\n return <any> this;\n }\n\n /**\n * Update the type signatures (return types and/or parameter types) of existing indirections\n * without changing their implementations. Use this when a patched indirection changes the types\n * flowing through downstream indirections that don't need new implementations.\n * This is a zero-cost type-level operation.\n */\n public typePatch<Patch extends {[Key in Names]?: [any] | [any, any[]]}>():\n IndirBuilder<Context, Names, {[Key in Names]: Key extends keyof Patch ? (\n Patch[Key] extends [any, any[]] ? IndirDef<Context, Key, Patch[Key][0], Patch[Key][1]> : (\n // Only one - infer arg yourself\n Patch[Key] extends [ any ] ? (\n RuleDefs[Key] extends IndirDef<any, any, any, infer Par> ? IndirDef<Context, Key, Patch[Key][0], Par> : never\n ) : never\n )\n ) : (RuleDefs[Key] extends IndirDef<Context, Key> ? RuleDefs[Key] : never) }> {\n return <any> this;\n }\n\n /**\n * Change the implementation of an existing indirection.\n */\n public patchRule<U extends Names, RET, ARGS extends any[]>(patch: IndirDef<Context, U, RET, ARGS>):\n IndirBuilder<Context, Names, {[Key in Names]: Key extends U ?\n IndirDef<Context, Key, RET, ARGS> :\n (RuleDefs[Key] extends IndirDef<Context, Key> ? RuleDefs[Key] : never)\n } > {\n const self = <IndirBuilder<Context, Names, {[Key in Names]: Key extends U ?\n IndirDef<Context, Key, RET, ARGS> : (RuleDefs[Key] extends IndirDef<Context, Key> ? RuleDefs[Key] : never) }>>\n <unknown> this;\n self.rules[patch.name] = <any> patch;\n return self;\n }\n\n /**\n * Add an indirection function. If the rule already exists, but the implementation differs, an error will be thrown.\n */\n public addRuleRedundant<U extends string, RET, ARGS extends any[]>(rule: IndirDef<Context, U, RET, ARGS>):\n IndirBuilder<Context, Names | U, {[K in Names | U]: K extends U ?\n IndirDef<Context, K, RET, ARGS> :\n (K extends Names ? (RuleDefs[K] extends IndirDef<Context, K> ? RuleDefs[K] : never) : never)\n }> {\n const self = <IndirBuilder<Context, Names | U, {[K in Names | U]: K extends U ?\n IndirDef<Context, K, RET, ARGS> :\n (K extends Names ? (RuleDefs[K] extends IndirDef<Context, K> ? RuleDefs[K] : never) : never) }>>\n <unknown> this;\n const rules = <Record<string, IndirDef<Context>>> self.rules;\n if (rules[rule.name] !== undefined && rules[rule.name] !== rule) {\n throw new Error(`Function ${rule.name} already exists in the builder`);\n }\n rules[rule.name] = rule;\n return self;\n }\n\n /**\n * Add a rule to the grammar. Will raise a typescript error if the rule already exists in the grammar.\n */\n public addRule<U extends string, RET, ARGS extends any[]>(\n rule: CheckOverlap<U, Names, IndirDef<Context, U, RET, ARGS>>,\n ): IndirBuilder<Context, Names | U, {[K in Names | U]: K extends U ?\n IndirDef<Context, K, RET, ARGS> :\n (K extends Names ? (RuleDefs[K] extends IndirDef<Context, K> ? RuleDefs[K] : never) : never) }> {\n return this.addRuleRedundant(rule);\n }\n\n /**\n * Add multiple indirection definitions at once using rest parameters.\n * Provides better TypeScript type inference than calling {@link addRule} in a loop,\n * but avoid passing too many at once as this can cause TypeScript compilation slowdowns.\n * TypeScript errors if any name conflicts with an existing one.\n */\n public addMany<U extends readonly IndirDef<Context>[]>(\n ...rules: CheckOverlap<ParseNamesFromList<U>, Names, U>\n ): IndirBuilder<\n Context,\n Names | ParseNamesFromList<U>,\n {[K in Names | ParseNamesFromList<U>]:\n K extends keyof ParseIndirsToObject<typeof rules> ? (\n ParseIndirsToObject<typeof rules>[K] extends IndirDef<Context, K> ? ParseIndirsToObject<typeof rules>[K] : never\n ) : (\n K extends Names ? (RuleDefs[K] extends IndirDef<Context, K> ? RuleDefs[K] : never) : never\n )\n }\n > {\n this.rules = { ...this.rules, ...listToIndirectionMap(rules) };\n return <any> <unknown> this;\n }\n\n /**\n * Delete a grammar rule by its name.\n */\n public deleteRule<U extends Names>(ruleName: U):\n IndirBuilder<Context, Exclude<Names, U>, {[K in Exclude<Names, U>]:\n RuleDefs[K] extends IndirDef<Context, K> ? RuleDefs[K] : never }> {\n delete this.rules[ruleName];\n return <IndirBuilder<Context, Exclude<Names, U>, {[K in Exclude<Names, U>]:\n RuleDefs[K] extends IndirDef<Context, K> ? RuleDefs[K] : never }>>\n <unknown> this;\n }\n\n /**\n * Delete multiple indirection definitions by name in a single call.\n * @param ruleNames - Names of the indirections to delete.\n */\n public deleteMany<U extends Names>(...ruleNames: U[]):\n IndirBuilder<Context, Exclude<Names, U>, {[K in Exclude<Names, U>]:\n RuleDefs[K] extends IndirDef<Context, K> ? RuleDefs[K] : never }> {\n for (const name of ruleNames) {\n delete this.rules[name];\n }\n return <IndirBuilder<Context, Exclude<Names, U>, {[K in Exclude<Names, U>]:\n RuleDefs[K] extends IndirDef<Context, K> ? RuleDefs[K] : never }>>\n <unknown> this;\n }\n\n /**\n * Retrieve an indirection definition by its name.\n * Useful for inspecting or wrapping existing definitions when extending a pipeline.\n * @param ruleName - The name of the indirection, type-checked against the builder's known names.\n */\n public getRule<U extends Names>(ruleName: U): RuleDefs[U] extends IndirDef<any, U, infer RT, infer PT> ?\n IndirDef<Context, U, RT, PT> : never {\n return <any> this.rules[ruleName];\n }\n\n /**\n * Merge this indirection builder with another.\n * If the two builders both have a definition with the same name,\n * no error will be thrown in case they map to the same object (by reference).\n * If they map to a different object, an error will be thrown.\n * To fix this problem, the overridingRules array should contain a definition with the same conflicting name,\n * whose implementation will be used.\n */\n public merge<\n OtherNames extends string,\n OtherRules extends IndirectionMap<OtherNames>,\n OW extends readonly IndirDef<Context>[],\n >(\n builder: IndirBuilder<Context, OtherNames, OtherRules>,\n overridingRules: OW,\n ):\n IndirBuilder<\n Context,\n Names | OtherNames | ParseNamesFromList<OW>,\n {[K in Names | OtherNames | ParseNamesFromList<OW>]:\n K extends keyof ParseIndirsToObject<OW> ? (\n ParseIndirsToObject<OW>[K] extends IndirDef<Context, K> ? ParseIndirsToObject<OW>[K] : never\n )\n : (\n K extends Names ? (RuleDefs[K] extends IndirDef<Context, K> ? RuleDefs[K] : never)\n : K extends OtherNames ? (OtherRules[K] extends IndirDef<Context, K> ? OtherRules[K] : never) : never\n ) }\n > {\n // Assume the other set is bigger than yours. So start from that one and add this one\n const otherRules: Record<string, IndirDef<Context>> = { ...builder.rules };\n const myRules: Record<string, IndirDef<Context>> = this.rules;\n\n for (const rule of Object.values(myRules)) {\n if (otherRules[rule.name] === undefined) {\n otherRules[rule.name] = rule;\n } else {\n const existingRule = otherRules[rule.name];\n // If same rule, no issue, move on. Else\n if (existingRule !== rule) {\n const override = overridingRules.find(x => x.name === rule.name);\n // If override specified, take override, else, inform user that there is a conflict\n if (override) {\n otherRules[rule.name] = override;\n } else {\n throw new Error(`Function with name \"${rule.name}\" already exists in the builder, specify an override to resolve conflict`);\n }\n }\n }\n }\n\n this.rules = <any> <unknown> otherRules;\n return <any> <unknown> this;\n }\n\n /**\n * Construct an indirection object from the registered definitions.\n * @returns An object with a method for each registered indirection name.\n */\n public build(): IndirectObjFromIndirDefs<Context, Names, RuleDefs> {\n return <IndirectObjFromIndirDefs<Context, Names, RuleDefs>> new DynamicIndirect(this.rules);\n }\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"helpers.js","sourceRoot":"","sources":["../../../../lib/indirection-builder/helpers.ts"],"names":[],"mappings":";;AA2BA,oDAMC;AATD;;GAEG;AACH,SAAgB,oBAAoB,CAAgC,KAAQ;IAC1E,MAAM,QAAQ,GAA6B,EAAE,CAAC;IAC9C,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAC7B,CAAC;IACD,OAA+B,QAAQ,CAAC;AAC1C,CAAC","sourcesContent":["import type { ParseNamesFromList } from '../parser-builder/builderTypes.js';\n\nexport type IndirDef<\n Context = any,\n NameType extends string = string,\n ReturnType = unknown,\n ParamType extends any[] = any[],\n> = {\n name: NameType;\n fun: (def: IndirDefArg) => (c: Context, ...params: ParamType) => ReturnType;\n};\n\nexport type IndirDefArg = {\n /**\n * Calls another rule using the provided arguments.\n * @param rule\n * @param arg\n * @constructor\n */\n SUBRULE: <T, U extends any[]>(rule: IndirDef<any, any, T, U>, ...arg: U) => T;\n};\n\nexport type IndirectionMap<RuleNames extends string> = {[Key in RuleNames]: IndirDef<any, Key> };\n\n/**\n * Converts a list of ruledefs to a record mapping a name to the corresponding ruledef.\n */\nexport function listToIndirectionMap<T extends readonly IndirDef[]>(rules: T): ParseIndirsToObject<T> {\n const newRules: Record<string, IndirDef> = {};\n for (const rule of rules) {\n newRules[rule.name] = rule;\n }\n return <ParseIndirsToObject<T>>newRules;\n}\n\n/**\n * Convert a list of IndirDefs to a Record with the name of the IndirDef as the key, matching the IndirectionMap type.\n */\nexport type ParseIndirsToObject<\n T extends readonly IndirDef[],\n Names extends string = ParseNamesFromList<T>,\n Agg extends Record<string, IndirDef> = Record<never, never>,\n> = T extends readonly [infer First, ...infer Rest] ? (\n First extends IndirDef ? (\n Rest extends readonly IndirDef[] ? (\n ParseIndirsToObject<Rest, Names, {[K in keyof Agg | First['name']]: K extends First['name'] ? First : Agg[K] }>\n ) : never\n ) : never\n) : IndirectionMap<Names> & Agg;\n\nexport type IndirectObjFromIndirDefs<Context, Names extends string, RuleDefs extends IndirectionMap<Names>> = {\n [K in Names]: RuleDefs[K] extends IndirDef<Context, K, infer RET, infer ARGS> ?\n (context: Context, ...args: ARGS) => RET : never\n};\n"]}
1
+ {"version":3,"file":"helpers.js","sourceRoot":"","sources":["../../../../lib/indirection-builder/helpers.ts"],"names":[],"mappings":";;AA+CA,oDAMC;AATD;;GAEG;AACH,SAAgB,oBAAoB,CAAgC,KAAQ;IAC1E,MAAM,QAAQ,GAA6B,EAAE,CAAC;IAC9C,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAC7B,CAAC;IACD,OAA+B,QAAQ,CAAC;AAC1C,CAAC","sourcesContent":["import type { ParseNamesFromList } from '../parser-builder/builderTypes.js';\n\n/**\n * Definition of an indirection function, analogous to {@link ParserRule} for parsers\n * and {@link GeneratorRule} for generators.\n *\n * An indirection definition has a `name` and a `fun` function.\n * The `fun` function receives helper utilities (currently just `SUBRULE`) and returns\n * the actual implementation function that receives a context and optional parameters.\n *\n * @typeParam Context - Context object available in the function implementation.\n * @typeParam NameType - Name of the function, should be a string literal type (e.g., `'myFunction'`).\n * @typeParam ReturnType - Type returned by the function.\n * @typeParam ParamType - Tuple of additional parameter types beyond the context.\n */\nexport type IndirDef<\n Context = any,\n NameType extends string = string,\n ReturnType = unknown,\n ParamType extends any[] = any[],\n> = {\n name: NameType;\n fun: (def: IndirDefArg) => (c: Context, ...params: ParamType) => ReturnType;\n};\n\n/**\n * Helper utilities provided to {@link IndirDef.fun} implementations.\n * Currently exposes only `SUBRULE` for calling other indirection definitions.\n */\nexport type IndirDefArg = {\n /**\n * Calls another rule using the provided arguments.\n * @param rule\n * @param arg\n * @constructor\n */\n SUBRULE: <T, U extends any[]>(rule: IndirDef<any, any, T, U>, ...arg: U) => T;\n};\n\n/**\n * Record type mapping rule names to their corresponding {@link IndirDef} definitions.\n */\nexport type IndirectionMap<RuleNames extends string> = {[Key in RuleNames]: IndirDef<any, Key> };\n\n/**\n * Converts a list of ruledefs to a record mapping a name to the corresponding ruledef.\n */\nexport function listToIndirectionMap<T extends readonly IndirDef[]>(rules: T): ParseIndirsToObject<T> {\n const newRules: Record<string, IndirDef> = {};\n for (const rule of rules) {\n newRules[rule.name] = rule;\n }\n return <ParseIndirsToObject<T>>newRules;\n}\n\n/**\n * Convert a list of IndirDefs to a Record with the name of the IndirDef as the key, matching the IndirectionMap type.\n */\nexport type ParseIndirsToObject<\n T extends readonly IndirDef[],\n Names extends string = ParseNamesFromList<T>,\n Agg extends Record<string, IndirDef> = Record<never, never>,\n> = T extends readonly [infer First, ...infer Rest] ? (\n First extends IndirDef ? (\n Rest extends readonly IndirDef[] ? (\n ParseIndirsToObject<Rest, Names, {[K in keyof Agg | First['name']]: K extends First['name'] ? First : Agg[K] }>\n ) : never\n ) : never\n) : IndirectionMap<Names> & Agg;\n\n/**\n * The callable object type produced by {@link IndirBuilder.build}.\n * Maps each rule name to a function with the appropriate context and parameter types.\n */\nexport type IndirectObjFromIndirDefs<Context, Names extends string, RuleDefs extends IndirectionMap<Names>> = {\n [K in Names]: RuleDefs[K] extends IndirDef<Context, K, infer RET, infer ARGS> ?\n (context: Context, ...args: ARGS) => RET : never\n};\n"]}
@@ -2,14 +2,33 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.LexerBuilder = void 0;
4
4
  const chevrotain_1 = require("@traqula/chevrotain");
5
+ /**
6
+ * Builder for constructing Chevrotain lexers with type-safe token management.
7
+ * Token ordering matters — the lexer matches the first token that fits, so more specific
8
+ * tokens (e.g., keywords) must be positioned before more general ones (e.g., identifiers).
9
+ *
10
+ * Builders mutate internal state and return `this`.
11
+ * Always start by copying an existing builder with `LexerBuilder.create(existingBuilder)`.
12
+ */
5
13
  class LexerBuilder {
6
14
  tokens;
15
+ /**
16
+ * Create a new LexerBuilder, optionally copying from an existing one.
17
+ * @param starter - An existing builder to copy tokens from. If omitted, starts empty.
18
+ */
7
19
  static create(starter) {
8
20
  return new LexerBuilder(starter);
9
21
  }
10
22
  constructor(starter) {
11
23
  this.tokens = starter?.tokens ? [...starter.tokens] : [];
12
24
  }
25
+ /**
26
+ * Merge tokens from another LexerBuilder into this one.
27
+ * Duplicate tokens (by reference) are skipped. Different tokens with the same name
28
+ * cause an error unless an override is provided.
29
+ * @param merge - The other builder whose tokens to merge.
30
+ * @param overwrite - Tokens that take precedence when names conflict.
31
+ */
13
32
  merge(merge, overwrite = []) {
14
33
  const extraTokens = merge.tokens.filter((token) => {
15
34
  const overwriteToken = overwrite.find(t => t.name === token.name);
@@ -28,10 +47,20 @@ class LexerBuilder {
28
47
  this.tokens.push(...extraTokens);
29
48
  return this;
30
49
  }
50
+ /**
51
+ * Append tokens to the end of the builder's token list.
52
+ * TypeScript errors if a token name already exists.
53
+ * @param token - One or more tokens to add.
54
+ */
31
55
  add(...token) {
32
56
  this.tokens.push(...token);
33
57
  return this;
34
58
  }
59
+ /**
60
+ * Insert tokens before a specified reference token in the ordering.
61
+ * @param before - The existing token to insert before.
62
+ * @param token - One or more tokens to insert.
63
+ */
35
64
  addBefore(before, ...token) {
36
65
  const index = this.tokens.indexOf(before);
37
66
  if (index === -1) {
@@ -56,15 +85,28 @@ class LexerBuilder {
56
85
  return this;
57
86
  }
58
87
  /**
59
- * @param before token to move rest before
60
- * @param tokens tokens to move before the first token
88
+ * Move existing tokens so they appear before a specified reference token.
89
+ * The tokens must already exist in the builder.
90
+ * @param before - The reference token to move before.
91
+ * @param tokens - The tokens to reposition.
61
92
  */
62
93
  moveBefore(before, ...tokens) {
63
94
  return this.moveBeforeOrAfter('before', before, ...tokens);
64
95
  }
96
+ /**
97
+ * Move existing tokens so they appear after a specified reference token.
98
+ * The tokens must already exist in the builder.
99
+ * @param after - The reference token to move after.
100
+ * @param tokens - The tokens to reposition.
101
+ */
65
102
  moveAfter(after, ...tokens) {
66
103
  return this.moveBeforeOrAfter('after', after, ...tokens);
67
104
  }
105
+ /**
106
+ * Insert tokens after a specified reference token in the ordering.
107
+ * @param after - The existing token to insert after.
108
+ * @param token - One or more tokens to insert.
109
+ */
68
110
  addAfter(after, ...token) {
69
111
  const index = this.tokens.indexOf(after);
70
112
  if (index === -1) {
@@ -73,6 +115,10 @@ class LexerBuilder {
73
115
  this.tokens.splice(index + 1, 0, ...token);
74
116
  return this;
75
117
  }
118
+ /**
119
+ * Remove tokens from the builder by reference.
120
+ * @param token - One or more tokens to remove. Throws if a token is not found.
121
+ */
76
122
  delete(...token) {
77
123
  for (const t of token) {
78
124
  const index = this.tokens.indexOf(t);
@@ -83,6 +129,10 @@ class LexerBuilder {
83
129
  }
84
130
  return this;
85
131
  }
132
+ /**
133
+ * Construct a Chevrotain {@link Lexer} from the current token ordering.
134
+ * @param lexerConfig - Optional Chevrotain lexer configuration overrides.
135
+ */
86
136
  build(lexerConfig) {
87
137
  return new chevrotain_1.Lexer(this.tokens, {
88
138
  positionTracking: 'onlyStart',
@@ -93,6 +143,10 @@ class LexerBuilder {
93
143
  ...lexerConfig,
94
144
  });
95
145
  }
146
+ /**
147
+ * Get the current token list (readonly).
148
+ * Useful for passing to {@link ParserBuilder.build} as the `tokenVocabulary` argument.
149
+ */
96
150
  get tokenVocabulary() {
97
151
  return this.tokens;
98
152
  }
@@ -1 +1 @@
1
- {"version":3,"file":"LexerBuilder.js","sourceRoot":"","sources":["../../../../lib/lexer-builder/LexerBuilder.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ILexerConfig, TokenType } from '@traqula/chevrotain';\nimport { Lexer } from '@traqula/chevrotain';\nimport type { CheckOverlap, NamedToken } from '../utils.js';\n\nexport class LexerBuilder<NAMES extends string = string> {\n private readonly tokens: TokenType[];\n\n public static create<U extends LexerBuilder<T>, T extends string = never>(starter?: U): U {\n return <U> new LexerBuilder(starter);\n }\n\n private constructor(starter?: LexerBuilder<NAMES>) {\n this.tokens = starter?.tokens ? [ ...starter.tokens ] : [];\n }\n\n public merge<OtherNames extends string, OW extends string>(\n merge: LexerBuilder<OtherNames>,\n overwrite: NamedToken<OW>[] = [],\n ):\n LexerBuilder<NAMES | OtherNames> {\n const extraTokens = merge.tokens.filter((token) => {\n const overwriteToken = overwrite.find(t => t.name === token.name);\n if (overwriteToken) {\n return false;\n }\n const match = this.tokens.find(t => t.name === token.name);\n if (match) {\n if (match !== token) {\n throw new Error(`Token with name ${token.name} already exists. Implementation is different and no overwrite was provided.`);\n }\n return false;\n }\n return true;\n });\n this.tokens.push(...extraTokens);\n return this;\n }\n\n public add<Name extends string>(...token: CheckOverlap<Name, NAMES, NamedToken<Name>[]>):\n LexerBuilder<Name | NAMES> {\n this.tokens.push(...token);\n return this;\n }\n\n public addBefore<Name extends string>(\n before: NamedToken<NAMES>,\n ...token: CheckOverlap<Name, NAMES, NamedToken<Name>[]>\n ): LexerBuilder<NAMES | Name> {\n const index = this.tokens.indexOf(before);\n if (index === -1) {\n throw new Error('Token not found');\n }\n this.tokens.splice(index, 0, ...token);\n return this;\n }\n\n private moveBeforeOrAfter<Name extends string>(\n beforeOrAfter: 'before' | 'after',\n before: NamedToken<NAMES>,\n ...tokens: CheckOverlap<Name, NAMES, never, NamedToken<Name>[]>\n ): LexerBuilder<NAMES> {\n const beforeIndex = this.tokens.indexOf(before) + (beforeOrAfter === 'before' ? 0 : 1);\n if (beforeIndex === -1) {\n throw new Error('BeforeToken not found');\n }\n for (const token of tokens) {\n const tokenIndex = this.tokens.indexOf(token);\n if (tokenIndex === -1) {\n throw new Error('Token not found');\n }\n this.tokens.splice(tokenIndex, 1);\n this.tokens.splice(beforeIndex, 0, token);\n }\n return this;\n }\n\n /**\n * @param before token to move rest before\n * @param tokens tokens to move before the first token\n */\n public moveBefore<Name extends string>(\n before: NamedToken<NAMES>,\n ...tokens: CheckOverlap<Name, NAMES, never, NamedToken<Name>[]>\n ): LexerBuilder<NAMES> {\n return this.moveBeforeOrAfter('before', before, ...tokens);\n }\n\n public moveAfter<Name extends string>(\n after: NamedToken<NAMES>,\n ...tokens: CheckOverlap<Name, NAMES, never, NamedToken<Name>[]>\n ): LexerBuilder<NAMES> {\n return this.moveBeforeOrAfter('after', after, ...tokens);\n }\n\n public addAfter<Name extends string>(\n after: NamedToken<NAMES>,\n ...token: CheckOverlap<Name, NAMES, NamedToken<Name>[]>\n ): LexerBuilder<NAMES | Name> {\n const index = this.tokens.indexOf(after);\n if (index === -1) {\n throw new Error('Token not found');\n }\n this.tokens.splice(index + 1, 0, ...token);\n return this;\n }\n\n public delete<Name extends NAMES>(...token: NamedToken<Name>[]): LexerBuilder<Exclude<NAMES, Name>> {\n for (const t of token) {\n const index = this.tokens.indexOf(t);\n if (index === -1) {\n throw new Error('Token not found');\n }\n this.tokens.splice(index, 1);\n }\n return this;\n }\n\n public build(lexerConfig?: ILexerConfig): Lexer {\n return new Lexer(this.tokens, {\n positionTracking: 'onlyStart',\n recoveryEnabled: false,\n ensureOptimizations: true,\n // SafeMode: true,\n // SkipValidations: true,\n ...lexerConfig,\n });\n }\n\n public get tokenVocabulary(): readonly TokenType[] {\n return this.tokens;\n }\n}\n"]}
1
+ {"version":3,"file":"LexerBuilder.js","sourceRoot":"","sources":["../../../../lib/lexer-builder/LexerBuilder.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ILexerConfig, TokenType } from '@traqula/chevrotain';\nimport { Lexer } from '@traqula/chevrotain';\nimport type { CheckOverlap, NamedToken } from '../utils.js';\n\n/**\n * Builder for constructing Chevrotain lexers with type-safe token management.\n * Token ordering matters — the lexer matches the first token that fits, so more specific\n * tokens (e.g., keywords) must be positioned before more general ones (e.g., identifiers).\n *\n * Builders mutate internal state and return `this`.\n * Always start by copying an existing builder with `LexerBuilder.create(existingBuilder)`.\n */\nexport class LexerBuilder<NAMES extends string = string> {\n private readonly tokens: TokenType[];\n\n /**\n * Create a new LexerBuilder, optionally copying from an existing one.\n * @param starter - An existing builder to copy tokens from. If omitted, starts empty.\n */\n public static create<U extends LexerBuilder<T>, T extends string = never>(starter?: U): U {\n return <U> new LexerBuilder(starter);\n }\n\n private constructor(starter?: LexerBuilder<NAMES>) {\n this.tokens = starter?.tokens ? [ ...starter.tokens ] : [];\n }\n\n /**\n * Merge tokens from another LexerBuilder into this one.\n * Duplicate tokens (by reference) are skipped. Different tokens with the same name\n * cause an error unless an override is provided.\n * @param merge - The other builder whose tokens to merge.\n * @param overwrite - Tokens that take precedence when names conflict.\n */\n public merge<OtherNames extends string, OW extends string>(\n merge: LexerBuilder<OtherNames>,\n overwrite: NamedToken<OW>[] = [],\n ):\n LexerBuilder<NAMES | OtherNames> {\n const extraTokens = merge.tokens.filter((token) => {\n const overwriteToken = overwrite.find(t => t.name === token.name);\n if (overwriteToken) {\n return false;\n }\n const match = this.tokens.find(t => t.name === token.name);\n if (match) {\n if (match !== token) {\n throw new Error(`Token with name ${token.name} already exists. Implementation is different and no overwrite was provided.`);\n }\n return false;\n }\n return true;\n });\n this.tokens.push(...extraTokens);\n return this;\n }\n\n /**\n * Append tokens to the end of the builder's token list.\n * TypeScript errors if a token name already exists.\n * @param token - One or more tokens to add.\n */\n public add<Name extends string>(...token: CheckOverlap<Name, NAMES, NamedToken<Name>[]>):\n LexerBuilder<Name | NAMES> {\n this.tokens.push(...token);\n return this;\n }\n\n /**\n * Insert tokens before a specified reference token in the ordering.\n * @param before - The existing token to insert before.\n * @param token - One or more tokens to insert.\n */\n public addBefore<Name extends string>(\n before: NamedToken<NAMES>,\n ...token: CheckOverlap<Name, NAMES, NamedToken<Name>[]>\n ): LexerBuilder<NAMES | Name> {\n const index = this.tokens.indexOf(before);\n if (index === -1) {\n throw new Error('Token not found');\n }\n this.tokens.splice(index, 0, ...token);\n return this;\n }\n\n private moveBeforeOrAfter<Name extends string>(\n beforeOrAfter: 'before' | 'after',\n before: NamedToken<NAMES>,\n ...tokens: CheckOverlap<Name, NAMES, never, NamedToken<Name>[]>\n ): LexerBuilder<NAMES> {\n const beforeIndex = this.tokens.indexOf(before) + (beforeOrAfter === 'before' ? 0 : 1);\n if (beforeIndex === -1) {\n throw new Error('BeforeToken not found');\n }\n for (const token of tokens) {\n const tokenIndex = this.tokens.indexOf(token);\n if (tokenIndex === -1) {\n throw new Error('Token not found');\n }\n this.tokens.splice(tokenIndex, 1);\n this.tokens.splice(beforeIndex, 0, token);\n }\n return this;\n }\n\n /**\n * Move existing tokens so they appear before a specified reference token.\n * The tokens must already exist in the builder.\n * @param before - The reference token to move before.\n * @param tokens - The tokens to reposition.\n */\n public moveBefore<Name extends string>(\n before: NamedToken<NAMES>,\n ...tokens: CheckOverlap<Name, NAMES, never, NamedToken<Name>[]>\n ): LexerBuilder<NAMES> {\n return this.moveBeforeOrAfter('before', before, ...tokens);\n }\n\n /**\n * Move existing tokens so they appear after a specified reference token.\n * The tokens must already exist in the builder.\n * @param after - The reference token to move after.\n * @param tokens - The tokens to reposition.\n */\n public moveAfter<Name extends string>(\n after: NamedToken<NAMES>,\n ...tokens: CheckOverlap<Name, NAMES, never, NamedToken<Name>[]>\n ): LexerBuilder<NAMES> {\n return this.moveBeforeOrAfter('after', after, ...tokens);\n }\n\n /**\n * Insert tokens after a specified reference token in the ordering.\n * @param after - The existing token to insert after.\n * @param token - One or more tokens to insert.\n */\n public addAfter<Name extends string>(\n after: NamedToken<NAMES>,\n ...token: CheckOverlap<Name, NAMES, NamedToken<Name>[]>\n ): LexerBuilder<NAMES | Name> {\n const index = this.tokens.indexOf(after);\n if (index === -1) {\n throw new Error('Token not found');\n }\n this.tokens.splice(index + 1, 0, ...token);\n return this;\n }\n\n /**\n * Remove tokens from the builder by reference.\n * @param token - One or more tokens to remove. Throws if a token is not found.\n */\n public delete<Name extends NAMES>(...token: NamedToken<Name>[]): LexerBuilder<Exclude<NAMES, Name>> {\n for (const t of token) {\n const index = this.tokens.indexOf(t);\n if (index === -1) {\n throw new Error('Token not found');\n }\n this.tokens.splice(index, 1);\n }\n return this;\n }\n\n /**\n * Construct a Chevrotain {@link Lexer} from the current token ordering.\n * @param lexerConfig - Optional Chevrotain lexer configuration overrides.\n */\n public build(lexerConfig?: ILexerConfig): Lexer {\n return new Lexer(this.tokens, {\n positionTracking: 'onlyStart',\n recoveryEnabled: false,\n ensureOptimizations: true,\n // SafeMode: true,\n // SkipValidations: true,\n ...lexerConfig,\n });\n }\n\n /**\n * Get the current token list (readonly).\n * Useful for passing to {@link ParserBuilder.build} as the `tokenVocabulary` argument.\n */\n public get tokenVocabulary(): readonly TokenType[] {\n return this.tokens;\n }\n}\n"]}
@@ -35,9 +35,20 @@ class ParserBuilder {
35
35
  constructor(startRules) {
36
36
  this.rules = startRules;
37
37
  }
38
+ /**
39
+ * Narrow the builder's context type parameter to a more specific subtype.
40
+ * This is a zero-cost type-level operation — the builder instance is returned as-is
41
+ * but with updated type parameters.
42
+ */
38
43
  widenContext() {
39
44
  return this;
40
45
  }
46
+ /**
47
+ * Update the type signatures (return types and/or parameter types) of existing rules
48
+ * without changing their implementations. Use this when a patched rule changes the types
49
+ * flowing through downstream rules that don't need new implementations.
50
+ * This is a zero-cost type-level operation.
51
+ */
41
52
  typePatch() {
42
53
  return this;
43
54
  }
@@ -67,6 +78,12 @@ class ParserBuilder {
67
78
  addRule(rule) {
68
79
  return this.addRuleRedundant(rule);
69
80
  }
81
+ /**
82
+ * Add multiple rules at once using rest parameters.
83
+ * Provides better TypeScript type inference than calling {@link addRule} in a loop,
84
+ * but avoid passing too many rules at once as this can cause TypeScript compilation slowdowns.
85
+ * TypeScript errors if any rule name conflicts with an existing one.
86
+ */
70
87
  addMany(...rules) {
71
88
  this.rules = { ...this.rules, ...listToRuleDefMap(rules) };
72
89
  return this;
@@ -78,12 +95,21 @@ class ParserBuilder {
78
95
  delete this.rules[ruleName];
79
96
  return this;
80
97
  }
98
+ /**
99
+ * Delete multiple rules by name in a single call.
100
+ * @param ruleNames - Names of the rules to delete.
101
+ */
81
102
  deleteMany(...ruleNames) {
82
103
  for (const ruleName of ruleNames) {
83
104
  delete this.rules[ruleName];
84
105
  }
85
106
  return this;
86
107
  }
108
+ /**
109
+ * Retrieve a grammar rule definition by its name.
110
+ * Useful for inspecting or wrapping existing rules when extending a parser.
111
+ * @param ruleName - The name of the rule, type-checked against the builder's known rule names.
112
+ */
87
113
  getRule(ruleName) {
88
114
  return this.rules[ruleName];
89
115
  }
@@ -138,6 +164,12 @@ ${errorLine}`);
138
164
  messageBuilder.push(`\n${firstError.message}`);
139
165
  throw new Error(messageBuilder.join(''));
140
166
  }
167
+ /**
168
+ * Construct a self-sufficient parser from the registered rules and token vocabulary.
169
+ * Building a parser is expensive (Chevrotain performs grammar recording), so the result
170
+ * should be reused across parse calls.
171
+ * @returns An object with a method for each registered rule name.
172
+ */
141
173
  build({ tokenVocabulary, parserConfig = {}, lexerConfig = {}, queryPreProcessor = s => s, errorHandler, }) {
142
174
  const lexer = LexerBuilder_js_1.LexerBuilder.create().add(...tokenVocabulary).build({
143
175
  positionTracking: 'onlyOffset',