@lionweb/class-core-generator 0.6.13-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. package/LICENSE +201 -0
  2. package/dist/api/entity-types.templates.d.ts +4 -0
  3. package/dist/api/entity-types.templates.d.ts.map +1 -0
  4. package/dist/api/entity-types.templates.js +171 -0
  5. package/dist/api/entity-types.templates.js.map +1 -0
  6. package/dist/api/generation-headers.d.ts +9 -0
  7. package/dist/api/generation-headers.d.ts.map +1 -0
  8. package/dist/api/generation-headers.js +40 -0
  9. package/dist/api/generation-headers.js.map +1 -0
  10. package/dist/api/generator.d.ts +11 -0
  11. package/dist/api/generator.d.ts.map +1 -0
  12. package/dist/api/generator.js +59 -0
  13. package/dist/api/generator.js.map +1 -0
  14. package/dist/api/helpers/classifiers.d.ts +6 -0
  15. package/dist/api/helpers/classifiers.d.ts.map +1 -0
  16. package/dist/api/helpers/classifiers.js +49 -0
  17. package/dist/api/helpers/classifiers.js.map +1 -0
  18. package/dist/api/helpers/dependencies.d.ts +11 -0
  19. package/dist/api/helpers/dependencies.d.ts.map +1 -0
  20. package/dist/api/helpers/dependencies.js +52 -0
  21. package/dist/api/helpers/dependencies.js.map +1 -0
  22. package/dist/api/helpers/entities.d.ts +5 -0
  23. package/dist/api/helpers/entities.d.ts.map +1 -0
  24. package/dist/api/helpers/entities.js +33 -0
  25. package/dist/api/helpers/entities.js.map +1 -0
  26. package/dist/api/helpers/features.d.ts +9 -0
  27. package/dist/api/helpers/features.d.ts.map +1 -0
  28. package/dist/api/helpers/features.js +96 -0
  29. package/dist/api/helpers/features.js.map +1 -0
  30. package/dist/api/helpers/imports-tracking.d.ts +22 -0
  31. package/dist/api/helpers/imports-tracking.d.ts.map +1 -0
  32. package/dist/api/helpers/imports-tracking.js +80 -0
  33. package/dist/api/helpers/imports-tracking.js.map +1 -0
  34. package/dist/api/helpers/index.d.ts +8 -0
  35. package/dist/api/helpers/index.d.ts.map +1 -0
  36. package/dist/api/helpers/index.js +24 -0
  37. package/dist/api/helpers/index.js.map +1 -0
  38. package/dist/api/helpers/mps-annotations.d.ts +19 -0
  39. package/dist/api/helpers/mps-annotations.d.ts.map +1 -0
  40. package/dist/api/helpers/mps-annotations.js +65 -0
  41. package/dist/api/helpers/mps-annotations.js.map +1 -0
  42. package/dist/api/helpers/types.d.ts +3 -0
  43. package/dist/api/helpers/types.d.ts.map +1 -0
  44. package/dist/api/helpers/types.js +28 -0
  45. package/dist/api/helpers/types.js.map +1 -0
  46. package/dist/api/index-ts.d.ts +4 -0
  47. package/dist/api/index-ts.d.ts.map +1 -0
  48. package/dist/api/index-ts.js +52 -0
  49. package/dist/api/index-ts.js.map +1 -0
  50. package/dist/api/index.d.ts +5 -0
  51. package/dist/api/index.d.ts.map +1 -0
  52. package/dist/api/index.js +21 -0
  53. package/dist/api/index.js.map +1 -0
  54. package/dist/api/language-file.templates.d.ts +4 -0
  55. package/dist/api/language-file.templates.d.ts.map +1 -0
  56. package/dist/api/language-file.templates.js +65 -0
  57. package/dist/api/language-file.templates.js.map +1 -0
  58. package/dist/api/mega-factory.templates.d.ts +4 -0
  59. package/dist/api/mega-factory.templates.d.ts.map +1 -0
  60. package/dist/api/mega-factory.templates.js +61 -0
  61. package/dist/api/mega-factory.templates.js.map +1 -0
  62. package/dist/api/reflective-layer.templates.d.ts +4 -0
  63. package/dist/api/reflective-layer.templates.d.ts.map +1 -0
  64. package/dist/api/reflective-layer.templates.js +211 -0
  65. package/dist/api/reflective-layer.templates.js.map +1 -0
  66. package/dist/index.d.ts +3 -0
  67. package/dist/index.d.ts.map +1 -0
  68. package/dist/index.js +19 -0
  69. package/dist/index.js.map +1 -0
  70. package/dist/utils/index.d.ts +4 -0
  71. package/dist/utils/index.d.ts.map +1 -0
  72. package/dist/utils/index.js +20 -0
  73. package/dist/utils/index.js.map +1 -0
  74. package/dist/utils/json-as-ts.d.ts +2 -0
  75. package/dist/utils/json-as-ts.d.ts.map +1 -0
  76. package/dist/utils/json-as-ts.js +18 -0
  77. package/dist/utils/json-as-ts.js.map +1 -0
  78. package/dist/utils/string-sorting.d.ts +4 -0
  79. package/dist/utils/string-sorting.d.ts.map +1 -0
  80. package/dist/utils/string-sorting.js +23 -0
  81. package/dist/utils/string-sorting.js.map +1 -0
  82. package/dist/utils/textgen.d.ts +21 -0
  83. package/dist/utils/textgen.d.ts.map +1 -0
  84. package/dist/utils/textgen.js +67 -0
  85. package/dist/utils/textgen.js.map +1 -0
  86. package/dist/utils/toposort.d.ts +6 -0
  87. package/dist/utils/toposort.d.ts.map +1 -0
  88. package/dist/utils/toposort.js +41 -0
  89. package/dist/utils/toposort.js.map +1 -0
  90. package/package.json +34 -0
  91. package/src/api/entity-types.templates.ts +257 -0
  92. package/src/api/generation-headers.ts +47 -0
  93. package/src/api/generator.ts +84 -0
  94. package/src/api/helpers/classifiers.ts +67 -0
  95. package/src/api/helpers/dependencies.ts +62 -0
  96. package/src/api/helpers/entities.ts +43 -0
  97. package/src/api/helpers/features.ts +121 -0
  98. package/src/api/helpers/imports-tracking.ts +96 -0
  99. package/src/api/helpers/index.ts +24 -0
  100. package/src/api/helpers/mps-annotations.ts +69 -0
  101. package/src/api/helpers/types.ts +31 -0
  102. package/src/api/index-ts.ts +59 -0
  103. package/src/api/index.ts +21 -0
  104. package/src/api/language-file.templates.ts +79 -0
  105. package/src/api/mega-factory.templates.ts +79 -0
  106. package/src/api/reflective-layer.templates.ts +273 -0
  107. package/src/index.ts +19 -0
  108. package/src/utils/index.ts +21 -0
  109. package/src/utils/json-as-ts.ts +20 -0
  110. package/src/utils/string-sorting.ts +29 -0
  111. package/src/utils/textgen.ts +89 -0
  112. package/src/utils/toposort.ts +49 -0
  113. package/tsconfig.json +7 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"textgen.js","sourceRoot":"","sources":["../../src/utils/textgen.ts"],"names":[],"mappings":"AAAA,wDAAwD;AACxD,EAAE;AACF,iEAAiE;AACjE,mEAAmE;AACnE,0CAA0C;AAC1C,EAAE;AACF,iDAAiD;AACjD,EAAE;AACF,sEAAsE;AACtE,oEAAoE;AACpE,2EAA2E;AAC3E,sEAAsE;AACtE,iCAAiC;AACjC,EAAE;AACF,sEAAsE;AACtE,sCAAsC;AAEtC;;;GAGG;AAGH,OAAO,EAAC,QAAQ,EAAE,UAAU,EAAW,MAAM,oBAAoB,CAAA;AAGjE;;GAEG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAA;AAG3C,MAAM,cAAc,GAAG,CAAC,QAAgC,EAAE,EAAE,CACxD,CAAC,GAAW,EAAE,EAAE;IACZ,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACnB,OAAO,GAAG,CAAA;IACd,CAAC;IACD,OAAO,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;UACxB,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;AAClD,CAAC,CAAA;AAEL;;GAEG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,cAAc,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,WAAW,EAAE,CAAC,CAAA;AAEtE;;GAEG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,cAAc,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,WAAW,EAAE,CAAC,CAAA;AAGtE;;GAEG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,GAAW,EAAU,EAAE,CAClD,GAAG;KACE,UAAU,CAAC,GAAG,EAAE,GAAG,CAAC;KACpB,UAAU,CAAC,GAAG,EAAE,GAAG,CAAC,CAAA;AAG7B,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,SAAkB,EAAE,IAAkB,EAAE,KAAa,EAAE,EAAE,CAC9E,CAAC,IAAY,EAAE,EAAE,CACb,SAAS;IACL,CAAC,CAAC,GAAG,IAAI,EAAE,GAAG,IAAI,GAAG,KAAK,EAAE;IAC5B,CAAC,CAAC,IAAI,CAAA;AAKlB,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,UAAkB,EAAE,KAAkB,EAAE,YAAsB,EAAE,EAAE,CACzF,KAAK,CAAC,MAAM,GAAG,CAAC;IACZ,CAAC,CAAC;QACE,WAAW,UAAU,KAAK;QAC1B,MAAM,CAAC;YACH,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,cAAc,EAAE,WAAW,CAAC,EAAE,EAAE,CAAC,QAAQ,cAAc,YAAY,WAAW,GAAG,CAAC;YAC9F,YAAY,OAAO,YAAY,KAAK,QAAQ,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,GAAG,EAAE,MAAM,CAAC,YAAY,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE;SAC7G,CAAC;QACF,GAAG;KACN;IACD,CAAC,CAAC;QACE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,cAAc,EAAE,WAAW,CAAC,EAAE,EAAE,CAAC;YACzC,OAAO,UAAU,QAAQ,cAAc,KAAK;YAC5C,MAAM,CAAC,UAAU,WAAW,GAAG,CAAC;YAChC,GAAG;SACN,CAAC;QACF,YAAY;KACf,CAAA"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Computes the topological order of the transitive closure of the graph with the given vertices and edges given by the edge function,
3
+ * or returns {@code false} if there's a cycle.
4
+ */
5
+ export declare const dependencyOrderOf: <T>(vertices: T[], edgesOf: (vertex: T) => T[]) => false | T[];
6
+ //# sourceMappingURL=toposort.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toposort.d.ts","sourceRoot":"","sources":["../../src/utils/toposort.ts"],"names":[],"mappings":"AAiBA;;;GAGG;AACH,eAAO,MAAM,iBAAiB,gEA0B7B,CAAA"}
@@ -0,0 +1,41 @@
1
+ // Copyright 2025 TRUMPF Laser SE and other contributors
2
+ //
3
+ // Licensed under the Apache License, Version 2.0 (the "License")
4
+ // you may not use this file except in compliance with the License.
5
+ // You may obtain a copy of the License at
6
+ //
7
+ // http://www.apache.org/licenses/LICENSE-2.0
8
+ //
9
+ // Unless required by applicable law or agreed to in writing, software
10
+ // distributed under the License is distributed on an "AS IS" BASIS,
11
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ // See the License for the specific language governing permissions and
13
+ // limitations under the License.
14
+ //
15
+ // SPDX-FileCopyrightText: 2025 TRUMPF Laser SE and other contributors
16
+ // SPDX-License-Identifier: Apache-2.0
17
+ /**
18
+ * Computes the topological order of the transitive closure of the graph with the given vertices and edges given by the edge function,
19
+ * or returns {@code false} if there's a cycle.
20
+ */
21
+ export const dependencyOrderOf = (vertices, edgesOf) => {
22
+ const ordered = [];
23
+ const visit = (current, chain) => {
24
+ if (ordered.indexOf(current) > -1) {
25
+ return false;
26
+ }
27
+ if (chain.indexOf(current) > -1) {
28
+ return true;
29
+ }
30
+ const extendedChain = [...chain, current];
31
+ const hasCycle = edgesOf(current).some((edge) => visit(edge, extendedChain));
32
+ ordered.push(current);
33
+ if (hasCycle) {
34
+ console.dir(ordered);
35
+ }
36
+ return hasCycle;
37
+ };
38
+ const hasCycle = vertices.some((vertex) => visit(vertex, []));
39
+ return hasCycle ? false : ordered;
40
+ };
41
+ //# sourceMappingURL=toposort.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toposort.js","sourceRoot":"","sources":["../../src/utils/toposort.ts"],"names":[],"mappings":"AAAA,wDAAwD;AACxD,EAAE;AACF,iEAAiE;AACjE,mEAAmE;AACnE,0CAA0C;AAC1C,EAAE;AACF,iDAAiD;AACjD,EAAE;AACF,sEAAsE;AACtE,oEAAoE;AACpE,2EAA2E;AAC3E,sEAAsE;AACtE,iCAAiC;AACjC,EAAE;AACF,sEAAsE;AACtE,sCAAsC;AAEtC;;;GAGG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAI,QAAa,EAAE,OAA2B,EAAe,EAAE;IAC5F,MAAM,OAAO,GAAQ,EAAE,CAAA;IAEvB,MAAM,KAAK,GAAG,CAAC,OAAU,EAAE,KAAU,EAAE,EAAE;QACrC,IAAI,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;YAChC,OAAO,KAAK,CAAA;QAChB,CAAC;QACD,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;YAC9B,OAAO,IAAI,CAAA;QACf,CAAC;QACD,MAAM,aAAa,GAAG,CAAE,GAAG,KAAK,EAAE,OAAO,CAAE,CAAA;QAC3C,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,IAAI,CAClC,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,aAAa,CAAC,CACvC,CAAA;QACD,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QACrB,IAAI,QAAQ,EAAE,CAAC;YACX,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;QACxB,CAAC;QACD,OAAO,QAAQ,CAAA;IACnB,CAAC,CAAA;IAED,MAAM,QAAQ,GAAG,QAAQ,CAAC,IAAI,CAC1B,CAAC,MAAM,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC,CAChC,CAAA;IAED,OAAO,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAA;AACrC,CAAC,CAAA"}
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "@lionweb/class-core-generator",
3
+ "version": "0.6.13-beta.0",
4
+ "description": "Generator for implementations of languages based on the class-core package",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "typings": "dist/index.d.ts",
8
+ "type": "module",
9
+ "license": "Apache-2.0",
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "git+https://github.com/LionWeb-io/lionweb-typescript.git"
13
+ },
14
+ "bugs": {
15
+ "url": "https://github.com/LionWeb-io/lionweb-typescript/issues"
16
+ },
17
+ "scripts": {
18
+ "clean": "rm -rf dist/ node_modules/ lionweb-class-core-generator-*.tgz",
19
+ "build": "tsc",
20
+ "lint": "eslint src",
21
+ "pre-release-either": "npm run clean && npm install && npm run build",
22
+ "prerelease": "npm run pre-release-either",
23
+ "release": "npm publish",
24
+ "prerelease-beta": "npm run pre-release-either",
25
+ "release-beta": "npm publish --tag beta"
26
+ },
27
+ "devDependencies": {
28
+ "@lionweb/core": "0.6.13-beta.0",
29
+ "@lionweb/utilities": "0.6.13-beta.0",
30
+ "@types/node": "20.17.30",
31
+ "littoral-templates": "0.3.0",
32
+ "typescript": "5.3.3"
33
+ }
34
+ }
@@ -0,0 +1,257 @@
1
+ // Copyright 2025 TRUMPF Laser SE and other contributors
2
+ //
3
+ // Licensed under the Apache License, Version 2.0 (the "License")
4
+ // you may not use this file except in compliance with the License.
5
+ // You may obtain a copy of the License at
6
+ //
7
+ // http://www.apache.org/licenses/LICENSE-2.0
8
+ //
9
+ // Unless required by applicable law or agreed to in writing, software
10
+ // distributed under the License is distributed on an "AS IS" BASIS,
11
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ // See the License for the specific language governing permissions and
13
+ // limitations under the License.
14
+ //
15
+ // SPDX-FileCopyrightText: 2025 TRUMPF Laser SE and other contributors
16
+ // SPDX-License-Identifier: Apache-2.0
17
+
18
+ import {
19
+ builtinClassifiers,
20
+ Classifier,
21
+ Enumeration,
22
+ Feature,
23
+ featureMetaType,
24
+ Id,
25
+ Interface,
26
+ isContainment,
27
+ isProperty,
28
+ isReference,
29
+ LanguageEntity,
30
+ Link,
31
+ nameSorted,
32
+ PrimitiveType,
33
+ Property,
34
+ Reference,
35
+ SingleRef
36
+ } from "@lionweb/core"
37
+ import {commaSeparated, when, withNewlineAppended} from "littoral-templates"
38
+
39
+ import {indent, switchOrIf, withFirstUpper, wrapInIf} from "../utils/textgen.js"
40
+ import {
41
+ ConceptDescription,
42
+ Deprecated,
43
+ entityMetaType,
44
+ extendsFrom,
45
+ featuresToConcretelyImplementOf,
46
+ implementsFrom,
47
+ Imports,
48
+ isAbstract,
49
+ MpsAnnotation,
50
+ optionalityPostfix,
51
+ tsFieldTypeForFeature,
52
+ tsTypeForClassifier,
53
+ tsTypeForDatatype,
54
+ tsTypeForPrimitiveType,
55
+ tsTypeForValueManager
56
+ } from "./helpers/index.js"
57
+
58
+
59
+ const cardinalityPrefix = (feature: Feature) => {
60
+ if (feature instanceof Property) {
61
+ return feature.optional ? "Optional" : "Required"
62
+ }
63
+ if (feature instanceof Link) {
64
+ return `${feature.optional ? "Optional" : "Required"}${feature.multiple ? "Multi" : "Single"}`
65
+ }
66
+ }
67
+
68
+ const valueManagerFor = (feature: Feature) =>
69
+ `${cardinalityPrefix(feature)}${featureMetaType(feature)}ValueManager`
70
+
71
+ export const typeForLanguageEntity = (imports: Imports, mpsAnnotationsPerId: Record<Id, MpsAnnotation[]>) => {
72
+
73
+ const sortedSuperTypesCond = <T extends Classifier>(ts: T[], prefix: string): string =>
74
+ ts.length === 0 ? `` : `${prefix}${nameSorted(ts).map((t) => imports.entity(t)).join(", ")}`
75
+
76
+ const extendsCond = (ref: SingleRef<Classifier>): string =>
77
+ ` extends ${ref === builtinClassifiers.node ? imports.generic("NodeBase") : imports.entity(ref!)}`
78
+
79
+ const classMembersForProperty = (property: Property) => {
80
+ const {name, type} = property
81
+ return [
82
+ `private readonly _${name}: ${imports.generic(valueManagerFor(property))}<${tsTypeForDatatype(type, imports)}>;`,
83
+ `get ${name}(): ${tsTypeForDatatype(type, imports)}${optionalityPostfix(property)} {`,
84
+ indent(`return this._${name}.get();`),
85
+ `}`,
86
+ `set ${name}(newValue: ${tsTypeForDatatype(type, imports)}${optionalityPostfix(property)}) {`,
87
+ indent(`this._${name}.set(newValue);`),
88
+ `}`
89
+ ]
90
+ }
91
+
92
+ const tsTypeForLink = (link: Link) =>
93
+ wrapInIf(link instanceof Reference, () => `${imports.core("SingleRef")}<`, ">")(tsTypeForClassifier(link.type, imports))
94
+ + (link.multiple ? "[]" : optionalityPostfix(link))
95
+
96
+ const classMembersForLink = (link: Link) => {
97
+ const {name, type, multiple} = link
98
+ return [
99
+ `private readonly _${name}: ${imports.generic(valueManagerFor(link))}<${tsTypeForClassifier(type, imports)}>;`,
100
+ `get ${name}(): ${tsTypeForLink(link)} {`,
101
+ indent(`return this._${name}.get();`),
102
+ `}`,
103
+ when(!multiple)([
104
+ `set ${name}(newValue: ${tsTypeForLink(link)}) {`,
105
+ indent(`this._${name}.set(newValue);`),
106
+ `}`
107
+ ]),
108
+ when(multiple)([
109
+ `add${withFirstUpper(name)}(newValue: ${tsTypeForClassifier(type, imports)}) {`,
110
+ indent(`this._${name}.add(newValue);`),
111
+ `}`,
112
+ `remove${withFirstUpper(name)}(valueToRemove: ${tsTypeForClassifier(type, imports)}) {`,
113
+ indent(`this._${name}.remove(valueToRemove);`),
114
+ `}`,
115
+ `add${withFirstUpper(name)}AtIndex(newValue: ${tsTypeForClassifier(type, imports)}, index: number) {`,
116
+ indent(`this._${name}.insertAtIndex(newValue, index);`),
117
+ `}`,
118
+ `move${withFirstUpper(name)}(oldIndex: number, newIndex: number) {`,
119
+ indent(`this._${name}.move(oldIndex, newIndex);`),
120
+ `}`
121
+ ])
122
+ ]
123
+ }
124
+
125
+ const classMembersForFeature = (feature: Feature) => {
126
+ if (isProperty(feature)) {
127
+ return classMembersForProperty(feature)
128
+ }
129
+ if (isContainment(feature) || isReference(feature)) {
130
+ return classMembersForLink(feature)
131
+ }
132
+ return `// unhandled feature <${feature.constructor.name}>"${feature.name}" on classifier "${feature.classifier.name}" in language "${feature.classifier.language.name}"`
133
+ }
134
+
135
+ const classForConcreteClassifier = (classifier: Classifier) => {
136
+ const features = featuresToConcretelyImplementOf(classifier)
137
+
138
+ const getValueManagers = <FT extends Feature>(features: FT[]) => {
139
+ if (features.length === 0) {
140
+ return []
141
+ }
142
+
143
+ const featureMetaType_ = featureMetaType(features[0])
144
+ const argumentName = featureMetaType_.toLowerCase()
145
+ return [
146
+ ``,
147
+ `get${featureMetaType_}ValueManager(${argumentName}: ${imports.core(featureMetaType_)}): ${imports.generic(featureMetaType_ + "ValueManager")}<${featureMetaType_ === "Property" ? "unknown" : imports.generic("INodeBase")}> {`,
148
+ indent(
149
+ switchOrIf(
150
+ `${argumentName}.key`,
151
+ features.map((feature) => [`${imports.language(feature.classifier.language)}.INSTANCE.${feature.classifier.name}_${feature.name}.key`, `this._${feature.name}`]),
152
+ `return super.get${featureMetaType_}ValueManager(${argumentName});`
153
+ )
154
+ ),
155
+ `}`
156
+ ]
157
+ }
158
+
159
+ return [
160
+ // | index (or generic for LionCore-builtins) | | index (or generic for LionCore-builtins) |
161
+ `export ${isAbstract(classifier) ? "abstract " : ""}class ${classifier.name}${extendsCond(extendsFrom(classifier) ?? builtinClassifiers.node)}${sortedSuperTypesCond(implementsFrom(classifier), " implements ")} {`,
162
+ indent([
163
+ when(!isAbstract(classifier))(
164
+ () =>
165
+ [
166
+ `static create(id: Id, handleDelta?: ${imports.generic("DeltaHandler")}, parentInfo?: ${imports.generic("Parentage")}): ${classifier.name} {`,
167
+ indent([
168
+ `return new ${classifier.name}(${imports.language(classifier.language)}.INSTANCE.${classifier.name}, id, handleDelta, parentInfo);`
169
+ ]),
170
+ `}`,
171
+ ]
172
+ ),
173
+ when(features.length > 0)(
174
+ () =>
175
+ [
176
+ ``,
177
+ features.map(withNewlineAppended(classMembersForFeature)),
178
+ `public constructor(classifier: ${imports.core("Classifier")}, id: ${imports.core("Id")}, handleDelta?: ${imports.generic("DeltaHandler")}, parentInfo?: ${imports.generic("Parentage")}) {`,
179
+ indent([
180
+ `super(classifier, id, handleDelta, parentInfo);`,
181
+ features.map((feature) => `this._${feature.name} = new ${imports.generic(valueManagerFor(feature))}<${tsTypeForValueManager(feature, imports)}>(${imports.language(feature.classifier.language)}.INSTANCE.${feature.classifier.name}_${feature.name}, this);`)
182
+ ]),
183
+ `}`,
184
+ getValueManagers(features.filter(isProperty)),
185
+ getValueManagers(features.filter(isContainment)),
186
+ getValueManagers(features.filter(isReference))
187
+ ]
188
+ ),
189
+ ]),
190
+ `}`
191
+ ]
192
+ }
193
+
194
+
195
+ const typeForEnumeration = (enumeration: Enumeration) =>
196
+ [
197
+ `export enum ${enumeration.name} {`,
198
+ indent(
199
+ commaSeparated(enumeration.literals.map(({name, key}) => `${name} = "${key}"`))
200
+ // Using TS string enums means that the key _is_ the literal's runtime TS representation.
201
+ ),
202
+ `}`
203
+ ]
204
+
205
+
206
+ const interfaceFor = (interface_: Interface) =>
207
+ [
208
+ `export interface ${interface_.name} extends ${interface_.extends.length > 0 ? interface_.extends.map((superInterface) => imports.entity(superInterface)).join(", ") : imports.generic("INodeBase")} {`,
209
+ indent(
210
+ interface_.features.map((feature) => `${feature.name}: ${tsFieldTypeForFeature(feature, imports)};`)
211
+ ),
212
+ `}`
213
+ ]
214
+
215
+
216
+ const typeForPrimitiveType = (primitiveType: PrimitiveType) =>
217
+ `export type ${primitiveType.name} = ${tsTypeForPrimitiveType(primitiveType)};`
218
+
219
+ const jsDocFor = (entity: LanguageEntity) => {
220
+ const mpsAnnotationsForEntity = mpsAnnotationsPerId[entity.id] ?? []
221
+ const conceptDescription = mpsAnnotationsForEntity.find((mpsAnnotation) => mpsAnnotation instanceof ConceptDescription) as ConceptDescription
222
+ const hasShortConceptDescription = !!(conceptDescription?.shortDescription)
223
+ const deprecated = mpsAnnotationsForEntity.find((mpsAnnotation) => mpsAnnotation instanceof Deprecated) as Deprecated
224
+ return when(hasShortConceptDescription || deprecated !== undefined)([
225
+ `/**`,
226
+ when(hasShortConceptDescription)(
227
+ () => ` * ${conceptDescription.shortDescription}`
228
+ ),
229
+ when(deprecated !== undefined)(
230
+ () => ` * @deprecated ${deprecated.comment ?? ""}${deprecated.build === null ? "" : ` (build: ${deprecated.build})`}`
231
+ ),
232
+ ` */`
233
+ ])
234
+ }
235
+
236
+ return (entity: LanguageEntity) =>
237
+ [
238
+ jsDocFor(entity),
239
+ (() => {
240
+ if (entity instanceof Interface) {
241
+ return interfaceFor(entity)
242
+ }
243
+ if (entity instanceof Classifier) {
244
+ return classForConcreteClassifier(entity)
245
+ }
246
+ if (entity instanceof Enumeration) {
247
+ return typeForEnumeration(entity)
248
+ }
249
+ if (entity instanceof PrimitiveType) {
250
+ return typeForPrimitiveType(entity)
251
+ }
252
+ return `// unhandled language entity <${entityMetaType(entity)}>"${entity.name}"`
253
+ })()
254
+ ]
255
+
256
+ }
257
+
@@ -0,0 +1,47 @@
1
+ // SPDX-FileCopyrightText: 2025 TRUMPF Laser SE
2
+ //
3
+ // SPDX-License-Identifier: LicenseRef-TRUMPF
4
+
5
+ import {asString} from "littoral-templates"
6
+
7
+ /**
8
+ * @return the given string, reversed — "abc" -> "cba"
9
+ * Note: only used to hack around the REUSE compliance checker!
10
+ */
11
+ const reverseString = (str: string) =>
12
+ str.split("").reverse().join("")
13
+
14
+ /**
15
+ * The license header (as //-styled comments) for code originating from Trumpf Laser SE, licensed under Apache-2.0.
16
+ */
17
+ const trumpfOriginatingApache2_0LicenseHeaderComments = `// Copyright 2025 TRUMPF Laser SE and other contributors
18
+ //
19
+ // Licensed under the Apache License, Version 2.0 (the "License")
20
+ // you may not use this file except in compliance with the License.
21
+ // You may obtain a copy of the License at
22
+ //
23
+ // http://www.apache.org/licenses/LICENSE-2.0
24
+ //
25
+ // Unless required by applicable law or agreed to in writing, software
26
+ // distributed under the License is distributed on an "AS IS" BASIS,
27
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
28
+ // See the License for the specific language governing permissions and
29
+ // limitations under the License.
30
+ //
31
+ // ${reverseString("txeTthgirypoCeliF-XDPS")}: 2025 TRUMPF Laser SE and other contributors
32
+ // ${reverseString("reifitnedI-esneciL-XDPS")}: Apache-2.0
33
+ `
34
+
35
+ /**
36
+ * Generic (//-styled) comments warning about code being generated.
37
+ */
38
+ export const generatedCodeWarningComments = `// Warning: this file is generated!
39
+ // Modifying it by hand is useless at best, and sabotage at worst.
40
+ `
41
+
42
+ /**
43
+ * The empty line-separation concatenation of the Trumpf-flavored Apache-2.0 license header and the generation warning.
44
+ */
45
+ export const defaultTrumpfOriginatingApache2_0LicensedHeader =
46
+ asString([trumpfOriginatingApache2_0LicenseHeaderComments, ``, generatedCodeWarningComments, ``])
47
+
@@ -0,0 +1,84 @@
1
+ // Copyright 2025 TRUMPF Laser SE and other contributors
2
+ //
3
+ // Licensed under the Apache License, Version 2.0 (the "License")
4
+ // you may not use this file except in compliance with the License.
5
+ // You may obtain a copy of the License at
6
+ //
7
+ // http://www.apache.org/licenses/LICENSE-2.0
8
+ //
9
+ // Unless required by applicable law or agreed to in writing, software
10
+ // distributed under the License is distributed on an "AS IS" BASIS,
11
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ // See the License for the specific language governing permissions and
13
+ // limitations under the License.
14
+ //
15
+ // SPDX-FileCopyrightText: 2025 TRUMPF Laser SE and other contributors
16
+ // SPDX-License-Identifier: Apache-2.0
17
+
18
+ import {deserializeLanguages, Language, lioncore, SerializationChunk} from "@lionweb/core"
19
+ import {readFileAsJson} from "@lionweb/utilities"
20
+ import {writeFileSync} from "fs"
21
+ import {join} from "path"
22
+ import {cwd} from "process"
23
+
24
+ import {indexTsFor} from "./index-ts.js"
25
+ import {languageFileFor} from "./language-file.templates.js"
26
+ import {MpsAnnotation} from "./helpers/index.js"
27
+
28
+
29
+ const properGenericImportLocation = "@lionweb/class-core"
30
+
31
+
32
+ export type GeneratorOptions = {
33
+ mpsAnnotations: MpsAnnotation[]
34
+ genericImportLocation: string
35
+ header?: string
36
+ }
37
+
38
+
39
+ const withDefaults = (options?: Partial<GeneratorOptions>): GeneratorOptions => ({
40
+ mpsAnnotations: options?.mpsAnnotations ?? [],
41
+ genericImportLocation: options?.genericImportLocation ?? properGenericImportLocation,
42
+ header: options?.header
43
+ })
44
+
45
+
46
+ export const generateLanguage = (language: Language, generationPath: string, options?: Partial<GeneratorOptions>): string => {
47
+ const {name} = language
48
+ const fileName = `${name}.g.ts`
49
+ writeFileSync(join(generationPath, fileName), languageFileFor(language, withDefaults(options)))
50
+ return fileName
51
+ }
52
+
53
+
54
+ export const generateApiFromLanguagesJson = (languagesJsonPath: string, generationPath: string, options?: Partial<GeneratorOptions>) => {
55
+ console.log(`Running API generator with cwd: ${cwd()}`)
56
+ console.log(` Path to languages: ${languagesJsonPath}`)
57
+ console.log(` Generation path: ${generationPath}`)
58
+
59
+ const languagesJson = readFileAsJson(languagesJsonPath) as SerializationChunk
60
+ const languages = deserializeLanguages(languagesJson, lioncore)
61
+ generateApiFromLanguages(languages, generationPath, options)
62
+ }
63
+
64
+
65
+ export const generateApiFromLanguages = (languages: Language[], generationPath: string, maybeOptions?: Partial<GeneratorOptions>) => {
66
+ console.log(` Generated:`)
67
+
68
+ const options = withDefaults(maybeOptions)
69
+
70
+ if (languages.length > 1) {
71
+ writeFileSync(join(generationPath, "index.g.ts"), indexTsFor(languages, options))
72
+ console.log(` index.g.ts`)
73
+ }
74
+
75
+ languages.forEach((language) => {
76
+ const {name, version, key, id} = language
77
+ const fileName = generateLanguage(language, generationPath, options)
78
+ console.log(` ${fileName} -> language: ${name} (version=${version}, key=${key}, id=${id})`)
79
+ })
80
+
81
+ console.log(`[done]`)
82
+ console.log()
83
+ }
84
+
@@ -0,0 +1,67 @@
1
+ // Copyright 2025 TRUMPF Laser SE and other contributors
2
+ //
3
+ // Licensed under the Apache License, Version 2.0 (the "License")
4
+ // you may not use this file except in compliance with the License.
5
+ // You may obtain a copy of the License at
6
+ //
7
+ // http://www.apache.org/licenses/LICENSE-2.0
8
+ //
9
+ // Unless required by applicable law or agreed to in writing, software
10
+ // distributed under the License is distributed on an "AS IS" BASIS,
11
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ // See the License for the specific language governing permissions and
13
+ // limitations under the License.
14
+ //
15
+ // SPDX-FileCopyrightText: 2025 TRUMPF Laser SE and other contributors
16
+ // SPDX-License-Identifier: Apache-2.0
17
+
18
+ import {
19
+ allFeaturesOf,
20
+ allSuperTypesOf,
21
+ Annotation,
22
+ Classifier,
23
+ Concept,
24
+ Feature,
25
+ Interface,
26
+ MultiRef,
27
+ SingleRef
28
+ } from "@lionweb/core"
29
+ import {uniquesAmong} from "@lionweb/utilities/dist/utils/array.js"
30
+
31
+
32
+ export const isAbstract = (classifier: Classifier): boolean =>
33
+ classifier instanceof Concept && classifier.abstract
34
+ /*
35
+ * The logical inverse is: classifier instanceof Annotation || (classifier instanceof Concept && !classifier.abstract)
36
+ */
37
+
38
+
39
+ export const extendsFrom = (classifier: Classifier): SingleRef<Classifier> | undefined => {
40
+ if (classifier instanceof Annotation) {
41
+ return classifier.extends
42
+ }
43
+ if (classifier instanceof Concept) {
44
+ return classifier.extends
45
+ }
46
+ return undefined
47
+ }
48
+
49
+ export const implementsFrom = (classifier: Classifier): MultiRef<Classifier> => {
50
+ if (classifier instanceof Annotation) {
51
+ return classifier.implements
52
+ }
53
+ if (classifier instanceof Concept) {
54
+ return classifier.implements
55
+ }
56
+ return []
57
+ }
58
+
59
+ export const featuresToConcretelyImplementOf = (classifier: Classifier): Feature[] => {
60
+ if (classifier instanceof Interface) {
61
+ return []
62
+ }
63
+ const implementedFeatures = uniquesAmong(allSuperTypesOf(classifier).flatMap(featuresToConcretelyImplementOf))
64
+ return allFeaturesOf(classifier)
65
+ .filter((feature) => implementedFeatures.indexOf(feature) === -1)
66
+ }
67
+
@@ -0,0 +1,62 @@
1
+ // Copyright 2025 TRUMPF Laser SE and other contributors
2
+ //
3
+ // Licensed under the Apache License, Version 2.0 (the "License")
4
+ // you may not use this file except in compliance with the License.
5
+ // You may obtain a copy of the License at
6
+ //
7
+ // http://www.apache.org/licenses/LICENSE-2.0
8
+ //
9
+ // Unless required by applicable law or agreed to in writing, software
10
+ // distributed under the License is distributed on an "AS IS" BASIS,
11
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ // See the License for the specific language governing permissions and
13
+ // limitations under the License.
14
+ //
15
+ // SPDX-FileCopyrightText: 2025 TRUMPF Laser SE and other contributors
16
+ // SPDX-License-Identifier: Apache-2.0
17
+
18
+ import {Classifier, cycleWith, inheritsFrom, Language, nameOf, nameSorted} from "@lionweb/core"
19
+ import {uniquesAmong} from "@lionweb/utilities/dist/utils/array.js"
20
+ import {asString, when} from "littoral-templates"
21
+ import {indent} from "../../utils/index.js"
22
+
23
+
24
+ /**
25
+ * @return all languages that any {@link Classifier classifier} of the given {@link Language language} depends on through direct inheritance.
26
+ */
27
+ export const dependenciesThroughDirectInheritanceOf = (language: Language) =>
28
+ uniquesAmong(
29
+ language.entities
30
+ .filter((entity) => entity instanceof Classifier)
31
+ .flatMap((entity) => inheritsFrom(entity as Classifier))
32
+ .map((classifier) => classifier.language)
33
+ .filter((depLanguage) => depLanguage !== language)
34
+ )
35
+
36
+
37
+ /**
38
+ * @return the – currently only those arising from direct inheritance on classifier-level – dependencies of the given {@link Language languages},
39
+ * as a human-readable text
40
+ */
41
+ export const verboseDependencies = (languages: Language[]): string => {
42
+ const nameSortedLanguages = nameSorted(languages)
43
+ return asString([
44
+ nameSortedLanguages.map((language) => {
45
+ const deps = dependenciesThroughDirectInheritanceOf(language)
46
+ const what = `direct type-wise dependencies (through inheritance)`
47
+ const cycle = cycleWith(language, dependenciesThroughDirectInheritanceOf)
48
+ return deps.length === 0
49
+ ? `language ${language.name} has no ${what}`
50
+ : [
51
+ `language ${language.name} has the following ${what}:`,
52
+ indent([
53
+ nameSorted(dependenciesThroughDirectInheritanceOf(language)).map(nameOf),
54
+ when(cycle.length > 0)(
55
+ `⚠ language "${language.name}" is part of the following cycle through type-wise (through direct inheritance): ${cycle.map(nameOf).join(" -> ")}`
56
+ )
57
+ ])
58
+ ]
59
+ })
60
+ ])
61
+ }
62
+
@@ -0,0 +1,43 @@
1
+ // Copyright 2025 TRUMPF Laser SE and other contributors
2
+ //
3
+ // Licensed under the Apache License, Version 2.0 (the "License")
4
+ // you may not use this file except in compliance with the License.
5
+ // You may obtain a copy of the License at
6
+ //
7
+ // http://www.apache.org/licenses/LICENSE-2.0
8
+ //
9
+ // Unless required by applicable law or agreed to in writing, software
10
+ // distributed under the License is distributed on an "AS IS" BASIS,
11
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ // See the License for the specific language governing permissions and
13
+ // limitations under the License.
14
+ //
15
+ // SPDX-FileCopyrightText: 2025 TRUMPF Laser SE and other contributors
16
+ // SPDX-License-Identifier: Apache-2.0
17
+
18
+ import {Annotation, Concept, Interface, LanguageEntity, PrimitiveType} from "@lionweb/core"
19
+
20
+
21
+ type EntityMetaType =
22
+ | "Annotation"
23
+ | "Concept"
24
+ | "Enumeration"
25
+ | "Interface"
26
+ | "PrimitiveType"
27
+
28
+ export const entityMetaType = (entity: LanguageEntity): EntityMetaType => {
29
+ if (entity instanceof Annotation) {
30
+ return "Annotation"
31
+ }
32
+ if (entity instanceof Concept) {
33
+ return "Concept"
34
+ }
35
+ if (entity instanceof Interface) {
36
+ return "Interface"
37
+ }
38
+ if (entity instanceof PrimitiveType) {
39
+ return "PrimitiveType"
40
+ }
41
+ throw new Error(`unhandled LanguageEntity sub type ${entity.constructor.name}`)
42
+ }
43
+