@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.
- package/README.md +1 -1
- package/dist/cjs/lib/generator-builder/generatorBuilder.js +38 -0
- package/dist/cjs/lib/generator-builder/generatorBuilder.js.map +1 -1
- package/dist/cjs/lib/indirection-builder/IndirBuilder.js +72 -0
- package/dist/cjs/lib/indirection-builder/IndirBuilder.js.map +1 -1
- package/dist/cjs/lib/indirection-builder/helpers.js.map +1 -1
- package/dist/cjs/lib/lexer-builder/LexerBuilder.js +56 -2
- package/dist/cjs/lib/lexer-builder/LexerBuilder.js.map +1 -1
- package/dist/cjs/lib/parser-builder/parserBuilder.js +32 -0
- package/dist/cjs/lib/parser-builder/parserBuilder.js.map +1 -1
- package/dist/cjs/lib/transformers/TransformerObject.js +11 -0
- package/dist/cjs/lib/transformers/TransformerObject.js.map +1 -1
- package/dist/cjs/lib/transformers/TransformerSubTyped.js +13 -0
- package/dist/cjs/lib/transformers/TransformerSubTyped.js.map +1 -1
- package/dist/cjs/lib/transformers/TransformerTyped.js +13 -0
- package/dist/cjs/lib/transformers/TransformerTyped.js.map +1 -1
- package/dist/esm/lib/generator-builder/generatorBuilder.d.ts +38 -0
- package/dist/esm/lib/generator-builder/generatorBuilder.js +38 -0
- package/dist/esm/lib/generator-builder/generatorBuilder.js.map +1 -1
- package/dist/esm/lib/indirection-builder/IndirBuilder.d.ts +53 -0
- package/dist/esm/lib/indirection-builder/IndirBuilder.js +72 -0
- package/dist/esm/lib/indirection-builder/IndirBuilder.js.map +1 -1
- package/dist/esm/lib/indirection-builder/helpers.d.ts +24 -0
- package/dist/esm/lib/indirection-builder/helpers.js.map +1 -1
- package/dist/esm/lib/lexer-builder/LexerBuilder.d.ts +56 -2
- package/dist/esm/lib/lexer-builder/LexerBuilder.js +56 -2
- package/dist/esm/lib/lexer-builder/LexerBuilder.js.map +1 -1
- package/dist/esm/lib/parser-builder/parserBuilder.d.ts +42 -0
- package/dist/esm/lib/parser-builder/parserBuilder.js +32 -0
- package/dist/esm/lib/parser-builder/parserBuilder.js.map +1 -1
- package/dist/esm/lib/transformers/TransformerObject.d.ts +11 -0
- package/dist/esm/lib/transformers/TransformerObject.js +11 -0
- package/dist/esm/lib/transformers/TransformerObject.js.map +1 -1
- package/dist/esm/lib/transformers/TransformerSubTyped.d.ts +13 -0
- package/dist/esm/lib/transformers/TransformerSubTyped.js +13 -0
- package/dist/esm/lib/transformers/TransformerSubTyped.js.map +1 -1
- package/dist/esm/lib/transformers/TransformerTyped.d.ts +26 -0
- package/dist/esm/lib/transformers/TransformerTyped.js +13 -0
- package/dist/esm/lib/transformers/TransformerTyped.js.map +1 -1
- package/package.json +2 -2
|
@@ -1,14 +1,37 @@
|
|
|
1
1
|
import type { ParseNamesFromList } from '../parser-builder/builderTypes.js';
|
|
2
2
|
import type { CheckOverlap } from '../utils.js';
|
|
3
3
|
import type { IndirDef, IndirectionMap, IndirectObjFromIndirDefs, ParseIndirsToObject } from './helpers.js';
|
|
4
|
+
/**
|
|
5
|
+
* Builder for composing modular transformation pipelines using indirection definitions.
|
|
6
|
+
* Functions registered through this builder call each other via SUBRULE, enabling the same
|
|
7
|
+
* modularity and extensibility as the parser and generator builders.
|
|
8
|
+
*
|
|
9
|
+
* Builders mutate internal state and return `this`.
|
|
10
|
+
* Always start by copying an existing builder with `IndirBuilder.create(existingBuilder)`.
|
|
11
|
+
*/
|
|
4
12
|
export declare class IndirBuilder<Context, Names extends string, RuleDefs extends IndirectionMap<Names>> {
|
|
13
|
+
/**
|
|
14
|
+
* Create an IndirBuilder from initial indirection definitions or an existing builder.
|
|
15
|
+
* If a builder is provided, a new copy will be created.
|
|
16
|
+
*/
|
|
5
17
|
static create<Context, Names extends string, RuleDefs extends IndirectionMap<Names>>(args: IndirBuilder<Context, Names, RuleDefs>): IndirBuilder<Context, Names, RuleDefs>;
|
|
6
18
|
static create<Rules extends readonly IndirDef[] = readonly IndirDef[], Context = Rules[0] extends IndirDef<infer context> ? context : never, Names extends string = ParseNamesFromList<Rules>, RuleDefs extends IndirectionMap<Names> = ParseIndirsToObject<Rules>>(rules: Rules): IndirBuilder<Context, Names, RuleDefs>;
|
|
7
19
|
private rules;
|
|
8
20
|
private constructor();
|
|
21
|
+
/**
|
|
22
|
+
* Narrow the builder's context type parameter to a more specific subtype.
|
|
23
|
+
* This is a zero-cost type-level operation — the builder instance is returned as-is
|
|
24
|
+
* but with updated type parameters.
|
|
25
|
+
*/
|
|
9
26
|
widenContext<NewContext extends Context>(): IndirBuilder<NewContext, Names, {
|
|
10
27
|
[Key in keyof RuleDefs]: Key extends Names ? (RuleDefs[Key] extends IndirDef<any, any, infer RT, infer PT> ? IndirDef<NewContext, Key, RT, PT> : never) : never;
|
|
11
28
|
}>;
|
|
29
|
+
/**
|
|
30
|
+
* Update the type signatures (return types and/or parameter types) of existing indirections
|
|
31
|
+
* without changing their implementations. Use this when a patched indirection changes the types
|
|
32
|
+
* flowing through downstream indirections that don't need new implementations.
|
|
33
|
+
* This is a zero-cost type-level operation.
|
|
34
|
+
*/
|
|
12
35
|
typePatch<Patch extends {
|
|
13
36
|
[Key in Names]?: [any] | [any, any[]];
|
|
14
37
|
}>(): IndirBuilder<Context, Names, {
|
|
@@ -32,6 +55,12 @@ export declare class IndirBuilder<Context, Names extends string, RuleDefs extend
|
|
|
32
55
|
addRule<U extends string, RET, ARGS extends any[]>(rule: CheckOverlap<U, Names, IndirDef<Context, U, RET, ARGS>>): IndirBuilder<Context, Names | U, {
|
|
33
56
|
[K in Names | U]: K extends U ? IndirDef<Context, K, RET, ARGS> : (K extends Names ? (RuleDefs[K] extends IndirDef<Context, K> ? RuleDefs[K] : never) : never);
|
|
34
57
|
}>;
|
|
58
|
+
/**
|
|
59
|
+
* Add multiple indirection definitions at once using rest parameters.
|
|
60
|
+
* Provides better TypeScript type inference than calling {@link addRule} in a loop,
|
|
61
|
+
* but avoid passing too many at once as this can cause TypeScript compilation slowdowns.
|
|
62
|
+
* TypeScript errors if any name conflicts with an existing one.
|
|
63
|
+
*/
|
|
35
64
|
addMany<U extends readonly IndirDef<Context>[]>(...rules: CheckOverlap<ParseNamesFromList<U>, Names, U>): IndirBuilder<Context, Names | ParseNamesFromList<U>, {
|
|
36
65
|
[K in Names | ParseNamesFromList<U>]: K extends keyof ParseIndirsToObject<typeof rules> ? (ParseIndirsToObject<typeof rules>[K] extends IndirDef<Context, K> ? ParseIndirsToObject<typeof rules>[K] : never) : (K extends Names ? (RuleDefs[K] extends IndirDef<Context, K> ? RuleDefs[K] : never) : never);
|
|
37
66
|
}>;
|
|
@@ -41,9 +70,33 @@ export declare class IndirBuilder<Context, Names extends string, RuleDefs extend
|
|
|
41
70
|
deleteRule<U extends Names>(ruleName: U): IndirBuilder<Context, Exclude<Names, U>, {
|
|
42
71
|
[K in Exclude<Names, U>]: RuleDefs[K] extends IndirDef<Context, K> ? RuleDefs[K] : never;
|
|
43
72
|
}>;
|
|
73
|
+
/**
|
|
74
|
+
* Delete multiple indirection definitions by name in a single call.
|
|
75
|
+
* @param ruleNames - Names of the indirections to delete.
|
|
76
|
+
*/
|
|
44
77
|
deleteMany<U extends Names>(...ruleNames: U[]): IndirBuilder<Context, Exclude<Names, U>, {
|
|
45
78
|
[K in Exclude<Names, U>]: RuleDefs[K] extends IndirDef<Context, K> ? RuleDefs[K] : never;
|
|
46
79
|
}>;
|
|
80
|
+
/**
|
|
81
|
+
* Retrieve an indirection definition by its name.
|
|
82
|
+
* Useful for inspecting or wrapping existing definitions when extending a pipeline.
|
|
83
|
+
* @param ruleName - The name of the indirection, type-checked against the builder's known names.
|
|
84
|
+
*/
|
|
47
85
|
getRule<U extends Names>(ruleName: U): RuleDefs[U] extends IndirDef<any, U, infer RT, infer PT> ? IndirDef<Context, U, RT, PT> : never;
|
|
86
|
+
/**
|
|
87
|
+
* Merge this indirection builder with another.
|
|
88
|
+
* If the two builders both have a definition with the same name,
|
|
89
|
+
* no error will be thrown in case they map to the same object (by reference).
|
|
90
|
+
* If they map to a different object, an error will be thrown.
|
|
91
|
+
* To fix this problem, the overridingRules array should contain a definition with the same conflicting name,
|
|
92
|
+
* whose implementation will be used.
|
|
93
|
+
*/
|
|
94
|
+
merge<OtherNames extends string, OtherRules extends IndirectionMap<OtherNames>, OW extends readonly IndirDef<Context>[]>(builder: IndirBuilder<Context, OtherNames, OtherRules>, overridingRules: OW): IndirBuilder<Context, Names | OtherNames | ParseNamesFromList<OW>, {
|
|
95
|
+
[K in Names | OtherNames | ParseNamesFromList<OW>]: K extends keyof ParseIndirsToObject<OW> ? (ParseIndirsToObject<OW>[K] extends IndirDef<Context, K> ? ParseIndirsToObject<OW>[K] : never) : (K extends Names ? (RuleDefs[K] extends IndirDef<Context, K> ? RuleDefs[K] : never) : K extends OtherNames ? (OtherRules[K] extends IndirDef<Context, K> ? OtherRules[K] : never) : never);
|
|
96
|
+
}>;
|
|
97
|
+
/**
|
|
98
|
+
* Construct an indirection object from the registered definitions.
|
|
99
|
+
* @returns An object with a method for each registered indirection name.
|
|
100
|
+
*/
|
|
48
101
|
build(): IndirectObjFromIndirDefs<Context, Names, RuleDefs>;
|
|
49
102
|
}
|
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
import { DynamicIndirect } from './dynamicIndirected.js';
|
|
2
2
|
import { listToIndirectionMap } from './helpers.js';
|
|
3
|
+
/**
|
|
4
|
+
* Builder for composing modular transformation pipelines using indirection definitions.
|
|
5
|
+
* Functions registered through this builder call each other via SUBRULE, enabling the same
|
|
6
|
+
* modularity and extensibility as the parser and generator builders.
|
|
7
|
+
*
|
|
8
|
+
* Builders mutate internal state and return `this`.
|
|
9
|
+
* Always start by copying an existing builder with `IndirBuilder.create(existingBuilder)`.
|
|
10
|
+
*/
|
|
3
11
|
export class IndirBuilder {
|
|
4
12
|
static create(start) {
|
|
5
13
|
if (Array.isArray(start)) {
|
|
@@ -11,9 +19,20 @@ export class IndirBuilder {
|
|
|
11
19
|
constructor(startRules) {
|
|
12
20
|
this.rules = startRules;
|
|
13
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* Narrow the builder's context type parameter to a more specific subtype.
|
|
24
|
+
* This is a zero-cost type-level operation — the builder instance is returned as-is
|
|
25
|
+
* but with updated type parameters.
|
|
26
|
+
*/
|
|
14
27
|
widenContext() {
|
|
15
28
|
return this;
|
|
16
29
|
}
|
|
30
|
+
/**
|
|
31
|
+
* Update the type signatures (return types and/or parameter types) of existing indirections
|
|
32
|
+
* without changing their implementations. Use this when a patched indirection changes the types
|
|
33
|
+
* flowing through downstream indirections that don't need new implementations.
|
|
34
|
+
* This is a zero-cost type-level operation.
|
|
35
|
+
*/
|
|
17
36
|
typePatch() {
|
|
18
37
|
return this;
|
|
19
38
|
}
|
|
@@ -43,6 +62,12 @@ export class IndirBuilder {
|
|
|
43
62
|
addRule(rule) {
|
|
44
63
|
return this.addRuleRedundant(rule);
|
|
45
64
|
}
|
|
65
|
+
/**
|
|
66
|
+
* Add multiple indirection definitions at once using rest parameters.
|
|
67
|
+
* Provides better TypeScript type inference than calling {@link addRule} in a loop,
|
|
68
|
+
* but avoid passing too many at once as this can cause TypeScript compilation slowdowns.
|
|
69
|
+
* TypeScript errors if any name conflicts with an existing one.
|
|
70
|
+
*/
|
|
46
71
|
addMany(...rules) {
|
|
47
72
|
this.rules = { ...this.rules, ...listToIndirectionMap(rules) };
|
|
48
73
|
return this;
|
|
@@ -54,15 +79,62 @@ export class IndirBuilder {
|
|
|
54
79
|
delete this.rules[ruleName];
|
|
55
80
|
return this;
|
|
56
81
|
}
|
|
82
|
+
/**
|
|
83
|
+
* Delete multiple indirection definitions by name in a single call.
|
|
84
|
+
* @param ruleNames - Names of the indirections to delete.
|
|
85
|
+
*/
|
|
57
86
|
deleteMany(...ruleNames) {
|
|
58
87
|
for (const name of ruleNames) {
|
|
59
88
|
delete this.rules[name];
|
|
60
89
|
}
|
|
61
90
|
return this;
|
|
62
91
|
}
|
|
92
|
+
/**
|
|
93
|
+
* Retrieve an indirection definition by its name.
|
|
94
|
+
* Useful for inspecting or wrapping existing definitions when extending a pipeline.
|
|
95
|
+
* @param ruleName - The name of the indirection, type-checked against the builder's known names.
|
|
96
|
+
*/
|
|
63
97
|
getRule(ruleName) {
|
|
64
98
|
return this.rules[ruleName];
|
|
65
99
|
}
|
|
100
|
+
/**
|
|
101
|
+
* Merge this indirection builder with another.
|
|
102
|
+
* If the two builders both have a definition with the same name,
|
|
103
|
+
* no error will be thrown in case they map to the same object (by reference).
|
|
104
|
+
* If they map to a different object, an error will be thrown.
|
|
105
|
+
* To fix this problem, the overridingRules array should contain a definition with the same conflicting name,
|
|
106
|
+
* whose implementation will be used.
|
|
107
|
+
*/
|
|
108
|
+
merge(builder, overridingRules) {
|
|
109
|
+
// Assume the other set is bigger than yours. So start from that one and add this one
|
|
110
|
+
const otherRules = { ...builder.rules };
|
|
111
|
+
const myRules = this.rules;
|
|
112
|
+
for (const rule of Object.values(myRules)) {
|
|
113
|
+
if (otherRules[rule.name] === undefined) {
|
|
114
|
+
otherRules[rule.name] = rule;
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
const existingRule = otherRules[rule.name];
|
|
118
|
+
// If same rule, no issue, move on. Else
|
|
119
|
+
if (existingRule !== rule) {
|
|
120
|
+
const override = overridingRules.find(x => x.name === rule.name);
|
|
121
|
+
// If override specified, take override, else, inform user that there is a conflict
|
|
122
|
+
if (override) {
|
|
123
|
+
otherRules[rule.name] = override;
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
throw new Error(`Function with name "${rule.name}" already exists in the builder, specify an override to resolve conflict`);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
this.rules = otherRules;
|
|
132
|
+
return this;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Construct an indirection object from the registered definitions.
|
|
136
|
+
* @returns An object with a method for each registered indirection name.
|
|
137
|
+
*/
|
|
66
138
|
build() {
|
|
67
139
|
return new DynamicIndirect(this.rules);
|
|
68
140
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"IndirBuilder.js","sourceRoot":"","sources":["../../../../lib/indirection-builder/IndirBuilder.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAEzD,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAEpD,MAAM,OAAO,YAAY;IAUhB,MAAM,CAAC,MAAM,CAMlB,KAAqD;QAErD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YACzB,OAA0D,IAAI,YAAY,CAAC,oBAAoB,CAAC,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,oBAAoB,CAAC,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,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC9F,CAAC;CACF","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":"AAEA,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAEzD,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAEpD;;;;;;;GAOG;AACH,MAAM,OAAO,YAAY;IAchB,MAAM,CAAC,MAAM,CAMlB,KAAqD;QAErD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YACzB,OAA0D,IAAI,YAAY,CAAC,oBAAoB,CAAC,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;IAED;;;;OAIG;IACI,YAAY;QAOjB,OAAa,IAAI,CAAC;IACpB,CAAC;IAED;;;;;OAKG;IACI,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;IAED;;;;;OAKG;IACI,OAAO,CACZ,GAAG,KAAoD;QAYvD,IAAI,CAAC,KAAK,GAAG,EAAE,GAAG,IAAI,CAAC,KAAK,EAAE,GAAG,oBAAoB,CAAC,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;IAED;;;OAGG;IACI,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;IAED;;;;OAIG;IACI,OAAO,CAAkB,QAAW;QAEzC,OAAa,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IACpC,CAAC;IAED;;;;;;;OAOG;IACI,KAAK,CAKV,OAAsD,EACtD,eAAmB;QAcnB,qFAAqF;QACrF,MAAM,UAAU,GAAsC,EAAE,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC;QAC3E,MAAM,OAAO,GAAsC,IAAI,CAAC,KAAK,CAAC;QAE9D,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;YAC1C,IAAI,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,SAAS,EAAE,CAAC;gBACxC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;YAC/B,CAAC;iBAAM,CAAC;gBACN,MAAM,YAAY,GAAG,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBAC3C,wCAAwC;gBACxC,IAAI,YAAY,KAAK,IAAI,EAAE,CAAC;oBAC1B,MAAM,QAAQ,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,CAAC,CAAC;oBACjE,mFAAmF;oBACnF,IAAI,QAAQ,EAAE,CAAC;wBACb,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC;oBACnC,CAAC;yBAAM,CAAC;wBACN,MAAM,IAAI,KAAK,CAAC,uBAAuB,IAAI,CAAC,IAAI,0EAA0E,CAAC,CAAC;oBAC9H,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;QAED,IAAI,CAAC,KAAK,GAAmB,UAAU,CAAC;QACxC,OAAuB,IAAI,CAAC;IAC9B,CAAC;IAED;;;OAGG;IACI,KAAK;QACV,OAA4D,IAAI,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC9F,CAAC;CACF","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,8 +1,25 @@
|
|
|
1
1
|
import type { ParseNamesFromList } from '../parser-builder/builderTypes.js';
|
|
2
|
+
/**
|
|
3
|
+
* Definition of an indirection function, analogous to {@link ParserRule} for parsers
|
|
4
|
+
* and {@link GeneratorRule} for generators.
|
|
5
|
+
*
|
|
6
|
+
* An indirection definition has a `name` and a `fun` function.
|
|
7
|
+
* The `fun` function receives helper utilities (currently just `SUBRULE`) and returns
|
|
8
|
+
* the actual implementation function that receives a context and optional parameters.
|
|
9
|
+
*
|
|
10
|
+
* @typeParam Context - Context object available in the function implementation.
|
|
11
|
+
* @typeParam NameType - Name of the function, should be a string literal type (e.g., `'myFunction'`).
|
|
12
|
+
* @typeParam ReturnType - Type returned by the function.
|
|
13
|
+
* @typeParam ParamType - Tuple of additional parameter types beyond the context.
|
|
14
|
+
*/
|
|
2
15
|
export type IndirDef<Context = any, NameType extends string = string, ReturnType = unknown, ParamType extends any[] = any[]> = {
|
|
3
16
|
name: NameType;
|
|
4
17
|
fun: (def: IndirDefArg) => (c: Context, ...params: ParamType) => ReturnType;
|
|
5
18
|
};
|
|
19
|
+
/**
|
|
20
|
+
* Helper utilities provided to {@link IndirDef.fun} implementations.
|
|
21
|
+
* Currently exposes only `SUBRULE` for calling other indirection definitions.
|
|
22
|
+
*/
|
|
6
23
|
export type IndirDefArg = {
|
|
7
24
|
/**
|
|
8
25
|
* Calls another rule using the provided arguments.
|
|
@@ -12,6 +29,9 @@ export type IndirDefArg = {
|
|
|
12
29
|
*/
|
|
13
30
|
SUBRULE: <T, U extends any[]>(rule: IndirDef<any, any, T, U>, ...arg: U) => T;
|
|
14
31
|
};
|
|
32
|
+
/**
|
|
33
|
+
* Record type mapping rule names to their corresponding {@link IndirDef} definitions.
|
|
34
|
+
*/
|
|
15
35
|
export type IndirectionMap<RuleNames extends string> = {
|
|
16
36
|
[Key in RuleNames]: IndirDef<any, Key>;
|
|
17
37
|
};
|
|
@@ -25,6 +45,10 @@ export declare function listToIndirectionMap<T extends readonly IndirDef[]>(rule
|
|
|
25
45
|
export type ParseIndirsToObject<T extends readonly IndirDef[], Names extends string = ParseNamesFromList<T>, Agg extends Record<string, IndirDef> = Record<never, never>> = T extends readonly [infer First, ...infer Rest] ? (First extends IndirDef ? (Rest extends readonly IndirDef[] ? (ParseIndirsToObject<Rest, Names, {
|
|
26
46
|
[K in keyof Agg | First['name']]: K extends First['name'] ? First : Agg[K];
|
|
27
47
|
}>) : never) : never) : IndirectionMap<Names> & Agg;
|
|
48
|
+
/**
|
|
49
|
+
* The callable object type produced by {@link IndirBuilder.build}.
|
|
50
|
+
* Maps each rule name to a function with the appropriate context and parameter types.
|
|
51
|
+
*/
|
|
28
52
|
export type IndirectObjFromIndirDefs<Context, Names extends string, RuleDefs extends IndirectionMap<Names>> = {
|
|
29
53
|
[K in Names]: RuleDefs[K] extends IndirDef<Context, K, infer RET, infer ARGS> ? (context: Context, ...args: ARGS) => RET : never;
|
|
30
54
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"helpers.js","sourceRoot":"","sources":["../../../../lib/indirection-builder/helpers.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"helpers.js","sourceRoot":"","sources":["../../../../lib/indirection-builder/helpers.ts"],"names":[],"mappings":"AA4CA;;GAEG;AACH,MAAM,UAAU,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"]}
|
|
@@ -1,22 +1,76 @@
|
|
|
1
1
|
import type { ILexerConfig, TokenType } from '@traqula/chevrotain';
|
|
2
2
|
import { Lexer } from '@traqula/chevrotain';
|
|
3
3
|
import type { CheckOverlap, NamedToken } from '../utils.js';
|
|
4
|
+
/**
|
|
5
|
+
* Builder for constructing Chevrotain lexers with type-safe token management.
|
|
6
|
+
* Token ordering matters — the lexer matches the first token that fits, so more specific
|
|
7
|
+
* tokens (e.g., keywords) must be positioned before more general ones (e.g., identifiers).
|
|
8
|
+
*
|
|
9
|
+
* Builders mutate internal state and return `this`.
|
|
10
|
+
* Always start by copying an existing builder with `LexerBuilder.create(existingBuilder)`.
|
|
11
|
+
*/
|
|
4
12
|
export declare class LexerBuilder<NAMES extends string = string> {
|
|
5
13
|
private readonly tokens;
|
|
14
|
+
/**
|
|
15
|
+
* Create a new LexerBuilder, optionally copying from an existing one.
|
|
16
|
+
* @param starter - An existing builder to copy tokens from. If omitted, starts empty.
|
|
17
|
+
*/
|
|
6
18
|
static create<U extends LexerBuilder<T>, T extends string = never>(starter?: U): U;
|
|
7
19
|
private constructor();
|
|
20
|
+
/**
|
|
21
|
+
* Merge tokens from another LexerBuilder into this one.
|
|
22
|
+
* Duplicate tokens (by reference) are skipped. Different tokens with the same name
|
|
23
|
+
* cause an error unless an override is provided.
|
|
24
|
+
* @param merge - The other builder whose tokens to merge.
|
|
25
|
+
* @param overwrite - Tokens that take precedence when names conflict.
|
|
26
|
+
*/
|
|
8
27
|
merge<OtherNames extends string, OW extends string>(merge: LexerBuilder<OtherNames>, overwrite?: NamedToken<OW>[]): LexerBuilder<NAMES | OtherNames>;
|
|
28
|
+
/**
|
|
29
|
+
* Append tokens to the end of the builder's token list.
|
|
30
|
+
* TypeScript errors if a token name already exists.
|
|
31
|
+
* @param token - One or more tokens to add.
|
|
32
|
+
*/
|
|
9
33
|
add<Name extends string>(...token: CheckOverlap<Name, NAMES, NamedToken<Name>[]>): LexerBuilder<Name | NAMES>;
|
|
34
|
+
/**
|
|
35
|
+
* Insert tokens before a specified reference token in the ordering.
|
|
36
|
+
* @param before - The existing token to insert before.
|
|
37
|
+
* @param token - One or more tokens to insert.
|
|
38
|
+
*/
|
|
10
39
|
addBefore<Name extends string>(before: NamedToken<NAMES>, ...token: CheckOverlap<Name, NAMES, NamedToken<Name>[]>): LexerBuilder<NAMES | Name>;
|
|
11
40
|
private moveBeforeOrAfter;
|
|
12
41
|
/**
|
|
13
|
-
*
|
|
14
|
-
*
|
|
42
|
+
* Move existing tokens so they appear before a specified reference token.
|
|
43
|
+
* The tokens must already exist in the builder.
|
|
44
|
+
* @param before - The reference token to move before.
|
|
45
|
+
* @param tokens - The tokens to reposition.
|
|
15
46
|
*/
|
|
16
47
|
moveBefore<Name extends string>(before: NamedToken<NAMES>, ...tokens: CheckOverlap<Name, NAMES, never, NamedToken<Name>[]>): LexerBuilder<NAMES>;
|
|
48
|
+
/**
|
|
49
|
+
* Move existing tokens so they appear after a specified reference token.
|
|
50
|
+
* The tokens must already exist in the builder.
|
|
51
|
+
* @param after - The reference token to move after.
|
|
52
|
+
* @param tokens - The tokens to reposition.
|
|
53
|
+
*/
|
|
17
54
|
moveAfter<Name extends string>(after: NamedToken<NAMES>, ...tokens: CheckOverlap<Name, NAMES, never, NamedToken<Name>[]>): LexerBuilder<NAMES>;
|
|
55
|
+
/**
|
|
56
|
+
* Insert tokens after a specified reference token in the ordering.
|
|
57
|
+
* @param after - The existing token to insert after.
|
|
58
|
+
* @param token - One or more tokens to insert.
|
|
59
|
+
*/
|
|
18
60
|
addAfter<Name extends string>(after: NamedToken<NAMES>, ...token: CheckOverlap<Name, NAMES, NamedToken<Name>[]>): LexerBuilder<NAMES | Name>;
|
|
61
|
+
/**
|
|
62
|
+
* Remove tokens from the builder by reference.
|
|
63
|
+
* @param token - One or more tokens to remove. Throws if a token is not found.
|
|
64
|
+
*/
|
|
19
65
|
delete<Name extends NAMES>(...token: NamedToken<Name>[]): LexerBuilder<Exclude<NAMES, Name>>;
|
|
66
|
+
/**
|
|
67
|
+
* Construct a Chevrotain {@link Lexer} from the current token ordering.
|
|
68
|
+
* @param lexerConfig - Optional Chevrotain lexer configuration overrides.
|
|
69
|
+
*/
|
|
20
70
|
build(lexerConfig?: ILexerConfig): Lexer;
|
|
71
|
+
/**
|
|
72
|
+
* Get the current token list (readonly).
|
|
73
|
+
* Useful for passing to {@link ParserBuilder.build} as the `tokenVocabulary` argument.
|
|
74
|
+
*/
|
|
21
75
|
get tokenVocabulary(): readonly TokenType[];
|
|
22
76
|
}
|
|
@@ -1,12 +1,31 @@
|
|
|
1
1
|
import { Lexer } from '@traqula/chevrotain';
|
|
2
|
+
/**
|
|
3
|
+
* Builder for constructing Chevrotain lexers with type-safe token management.
|
|
4
|
+
* Token ordering matters — the lexer matches the first token that fits, so more specific
|
|
5
|
+
* tokens (e.g., keywords) must be positioned before more general ones (e.g., identifiers).
|
|
6
|
+
*
|
|
7
|
+
* Builders mutate internal state and return `this`.
|
|
8
|
+
* Always start by copying an existing builder with `LexerBuilder.create(existingBuilder)`.
|
|
9
|
+
*/
|
|
2
10
|
export class LexerBuilder {
|
|
3
11
|
tokens;
|
|
12
|
+
/**
|
|
13
|
+
* Create a new LexerBuilder, optionally copying from an existing one.
|
|
14
|
+
* @param starter - An existing builder to copy tokens from. If omitted, starts empty.
|
|
15
|
+
*/
|
|
4
16
|
static create(starter) {
|
|
5
17
|
return new LexerBuilder(starter);
|
|
6
18
|
}
|
|
7
19
|
constructor(starter) {
|
|
8
20
|
this.tokens = starter?.tokens ? [...starter.tokens] : [];
|
|
9
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* Merge tokens from another LexerBuilder into this one.
|
|
24
|
+
* Duplicate tokens (by reference) are skipped. Different tokens with the same name
|
|
25
|
+
* cause an error unless an override is provided.
|
|
26
|
+
* @param merge - The other builder whose tokens to merge.
|
|
27
|
+
* @param overwrite - Tokens that take precedence when names conflict.
|
|
28
|
+
*/
|
|
10
29
|
merge(merge, overwrite = []) {
|
|
11
30
|
const extraTokens = merge.tokens.filter((token) => {
|
|
12
31
|
const overwriteToken = overwrite.find(t => t.name === token.name);
|
|
@@ -25,10 +44,20 @@ export class LexerBuilder {
|
|
|
25
44
|
this.tokens.push(...extraTokens);
|
|
26
45
|
return this;
|
|
27
46
|
}
|
|
47
|
+
/**
|
|
48
|
+
* Append tokens to the end of the builder's token list.
|
|
49
|
+
* TypeScript errors if a token name already exists.
|
|
50
|
+
* @param token - One or more tokens to add.
|
|
51
|
+
*/
|
|
28
52
|
add(...token) {
|
|
29
53
|
this.tokens.push(...token);
|
|
30
54
|
return this;
|
|
31
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* Insert tokens before a specified reference token in the ordering.
|
|
58
|
+
* @param before - The existing token to insert before.
|
|
59
|
+
* @param token - One or more tokens to insert.
|
|
60
|
+
*/
|
|
32
61
|
addBefore(before, ...token) {
|
|
33
62
|
const index = this.tokens.indexOf(before);
|
|
34
63
|
if (index === -1) {
|
|
@@ -53,15 +82,28 @@ export class LexerBuilder {
|
|
|
53
82
|
return this;
|
|
54
83
|
}
|
|
55
84
|
/**
|
|
56
|
-
*
|
|
57
|
-
*
|
|
85
|
+
* Move existing tokens so they appear before a specified reference token.
|
|
86
|
+
* The tokens must already exist in the builder.
|
|
87
|
+
* @param before - The reference token to move before.
|
|
88
|
+
* @param tokens - The tokens to reposition.
|
|
58
89
|
*/
|
|
59
90
|
moveBefore(before, ...tokens) {
|
|
60
91
|
return this.moveBeforeOrAfter('before', before, ...tokens);
|
|
61
92
|
}
|
|
93
|
+
/**
|
|
94
|
+
* Move existing tokens so they appear after a specified reference token.
|
|
95
|
+
* The tokens must already exist in the builder.
|
|
96
|
+
* @param after - The reference token to move after.
|
|
97
|
+
* @param tokens - The tokens to reposition.
|
|
98
|
+
*/
|
|
62
99
|
moveAfter(after, ...tokens) {
|
|
63
100
|
return this.moveBeforeOrAfter('after', after, ...tokens);
|
|
64
101
|
}
|
|
102
|
+
/**
|
|
103
|
+
* Insert tokens after a specified reference token in the ordering.
|
|
104
|
+
* @param after - The existing token to insert after.
|
|
105
|
+
* @param token - One or more tokens to insert.
|
|
106
|
+
*/
|
|
65
107
|
addAfter(after, ...token) {
|
|
66
108
|
const index = this.tokens.indexOf(after);
|
|
67
109
|
if (index === -1) {
|
|
@@ -70,6 +112,10 @@ export class LexerBuilder {
|
|
|
70
112
|
this.tokens.splice(index + 1, 0, ...token);
|
|
71
113
|
return this;
|
|
72
114
|
}
|
|
115
|
+
/**
|
|
116
|
+
* Remove tokens from the builder by reference.
|
|
117
|
+
* @param token - One or more tokens to remove. Throws if a token is not found.
|
|
118
|
+
*/
|
|
73
119
|
delete(...token) {
|
|
74
120
|
for (const t of token) {
|
|
75
121
|
const index = this.tokens.indexOf(t);
|
|
@@ -80,6 +126,10 @@ export class LexerBuilder {
|
|
|
80
126
|
}
|
|
81
127
|
return this;
|
|
82
128
|
}
|
|
129
|
+
/**
|
|
130
|
+
* Construct a Chevrotain {@link Lexer} from the current token ordering.
|
|
131
|
+
* @param lexerConfig - Optional Chevrotain lexer configuration overrides.
|
|
132
|
+
*/
|
|
83
133
|
build(lexerConfig) {
|
|
84
134
|
return new Lexer(this.tokens, {
|
|
85
135
|
positionTracking: 'onlyStart',
|
|
@@ -90,6 +140,10 @@ export class LexerBuilder {
|
|
|
90
140
|
...lexerConfig,
|
|
91
141
|
});
|
|
92
142
|
}
|
|
143
|
+
/**
|
|
144
|
+
* Get the current token list (readonly).
|
|
145
|
+
* Useful for passing to {@link ParserBuilder.build} as the `tokenVocabulary` argument.
|
|
146
|
+
*/
|
|
93
147
|
get tokenVocabulary() {
|
|
94
148
|
return this.tokens;
|
|
95
149
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LexerBuilder.js","sourceRoot":"","sources":["../../../../lib/lexer-builder/LexerBuilder.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAG5C,MAAM,OAAO,YAAY;IACN,MAAM,CAAc;IAE9B,MAAM,CAAC,MAAM,CAAsD,OAAW;QACnF,OAAW,IAAI,YAAY,CAAC,OAAO,CAAC,CAAC;IACvC,CAAC;IAED,YAAoB,OAA6B;QAC/C,IAAI,CAAC,MAAM,GAAG,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,CAAE,GAAG,OAAO,CAAC,MAAM,CAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IAC7D,CAAC;IAEM,KAAK,CACV,KAA+B,EAC/B,YAA8B,EAAE;QAGhC,MAAM,WAAW,GAAG,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;YAChD,MAAM,cAAc,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,KAAK,CAAC,IAAI,CAAC,CAAC;YAClE,IAAI,cAAc,EAAE,CAAC;gBACnB,OAAO,KAAK,CAAC;YACf,CAAC;YACD,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,KAAK,CAAC,IAAI,CAAC,CAAC;YAC3D,IAAI,KAAK,EAAE,CAAC;gBACV,IAAI,KAAK,KAAK,KAAK,EAAE,CAAC;oBACpB,MAAM,IAAI,KAAK,CAAC,mBAAmB,KAAK,CAAC,IAAI,6EAA6E,CAAC,CAAC;gBAC9H,CAAC;gBACD,OAAO,KAAK,CAAC;YACf,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,WAAW,CAAC,CAAC;QACjC,OAAO,IAAI,CAAC;IACd,CAAC;IAEM,GAAG,CAAsB,GAAG,KAAoD;QAErF,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,CAAC;QAC3B,OAAO,IAAI,CAAC;IACd,CAAC;IAEM,SAAS,CACd,MAAyB,EACzB,GAAG,KAAoD;QAEvD,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QAC1C,IAAI,KAAK,KAAK,CAAC,CAAC,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;QACrC,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,EAAE,GAAG,KAAK,CAAC,CAAC;QACvC,OAAO,IAAI,CAAC;IACd,CAAC;IAEO,iBAAiB,CACvB,aAAiC,EACjC,MAAyB,EACzB,GAAG,MAA4D;QAE/D,MAAM,WAAW,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,aAAa,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACvF,IAAI,WAAW,KAAK,CAAC,CAAC,EAAE,CAAC;YACvB,MAAM,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAC;QAC3C,CAAC;QACD,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAC3B,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;YAC9C,IAAI,UAAU,KAAK,CAAC,CAAC,EAAE,CAAC;gBACtB,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;YACrC,CAAC;YACD,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC;YAClC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC;QAC5C,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;OAGG;IACI,UAAU,CACf,MAAyB,EACzB,GAAG,MAA4D;QAE/D,OAAO,IAAI,CAAC,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC,CAAC;IAC7D,CAAC;IAEM,SAAS,CACd,KAAwB,EACxB,GAAG,MAA4D;QAE/D,OAAO,IAAI,CAAC,iBAAiB,CAAC,OAAO,EAAE,KAAK,EAAE,GAAG,MAAM,CAAC,CAAC;IAC3D,CAAC;IAEM,QAAQ,CACb,KAAwB,EACxB,GAAG,KAAoD;QAEvD,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QACzC,IAAI,KAAK,KAAK,CAAC,CAAC,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;QACrC,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,GAAG,CAAC,EAAE,CAAC,EAAE,GAAG,KAAK,CAAC,CAAC;QAC3C,OAAO,IAAI,CAAC;IACd,CAAC;IAEM,MAAM,CAAqB,GAAG,KAAyB;QAC5D,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;YACtB,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;YACrC,IAAI,KAAK,KAAK,CAAC,CAAC,EAAE,CAAC;gBACjB,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;YACrC,CAAC;YACD,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;QAC/B,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAEM,KAAK,CAAC,WAA0B;QACrC,OAAO,IAAI,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE;YAC5B,gBAAgB,EAAE,WAAW;YAC7B,eAAe,EAAE,KAAK;YACtB,mBAAmB,EAAE,IAAI;YACzB,kBAAkB;YAClB,yBAAyB;YACzB,GAAG,WAAW;SACf,CAAC,CAAC;IACL,CAAC;IAED,IAAW,eAAe;QACxB,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;CACF","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"]}
|