@lionweb/core 0.7.0-beta.8 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/dist/deserializer.d.ts +2 -3
- package/dist/deserializer.d.ts.map +1 -1
- package/dist/deserializer.js +9 -12
- package/dist/deserializer.js.map +1 -1
- package/dist/extraction.d.ts.map +1 -1
- package/dist/functions.d.ts.map +1 -1
- package/dist/handler.d.ts +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -1
- package/dist/index.js.map +1 -1
- package/dist/m1/reference-utils.d.ts +1 -1
- package/dist/m1/reference-utils.d.ts.map +1 -1
- package/dist/m3/builtins.d.ts +16 -0
- package/dist/m3/builtins.d.ts.map +1 -1
- package/dist/m3/builtins.js +24 -1
- package/dist/m3/builtins.js.map +1 -1
- package/dist/m3/constraints.d.ts.map +1 -1
- package/dist/m3/deserializer.d.ts.map +1 -1
- package/dist/m3/deserializer.js +2 -2
- package/dist/m3/deserializer.js.map +1 -1
- package/dist/m3/feature-resolvers.d.ts +23 -0
- package/dist/m3/feature-resolvers.d.ts.map +1 -0
- package/dist/m3/feature-resolvers.js +48 -0
- package/dist/m3/feature-resolvers.js.map +1 -0
- package/dist/m3/functions.d.ts.map +1 -1
- package/dist/m3/index.d.ts +2 -0
- package/dist/m3/index.d.ts.map +1 -1
- package/dist/m3/index.js +2 -0
- package/dist/m3/index.js.map +1 -1
- package/dist/m3/reference-checker.d.ts.map +1 -1
- package/dist/m3/serializer.d.ts.map +1 -1
- package/dist/{symbol-table.d.ts → m3/symbol-table.d.ts} +14 -14
- package/dist/m3/symbol-table.d.ts.map +1 -0
- package/dist/m3/symbol-table.js +43 -0
- package/dist/m3/symbol-table.js.map +1 -0
- package/dist/m3/types.d.ts.map +1 -1
- package/dist/references.d.ts +7 -2
- package/dist/references.d.ts.map +1 -1
- package/dist/references.js +5 -1
- package/dist/references.js.map +1 -1
- package/dist/serializer.d.ts +1 -1
- package/dist/serializer.d.ts.map +1 -1
- package/dist/serializer.js +22 -12
- package/dist/serializer.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/writing.js +1 -1
- package/package.json +31 -31
- package/src/deserializer.ts +228 -0
- package/src/dynamic-facade.ts +63 -0
- package/src/extraction.ts +31 -0
- package/src/functions.ts +28 -0
- package/src/handler.ts +57 -0
- package/src/index.ts +13 -0
- package/src/m1/reference-utils.ts +106 -0
- package/src/m3/README.md +16 -0
- package/src/m3/builtins.ts +196 -0
- package/src/m3/constraints.ts +109 -0
- package/src/m3/deserializer.ts +38 -0
- package/src/m3/facade.ts +130 -0
- package/src/m3/factory.ts +98 -0
- package/src/m3/feature-resolvers.ts +72 -0
- package/src/m3/functions.ts +379 -0
- package/src/m3/index.ts +12 -0
- package/src/m3/lioncore.ts +139 -0
- package/src/m3/reference-checker.ts +38 -0
- package/src/m3/serializer.ts +13 -0
- package/src/m3/symbol-table.ts +125 -0
- package/src/m3/types.ts +325 -0
- package/src/reading.ts +55 -0
- package/src/references.ts +39 -0
- package/src/serializer.ts +244 -0
- package/src/types.ts +11 -0
- package/src/version.ts +5 -0
- package/src/writing.ts +79 -0
- package/dist/symbol-table.d.ts.map +0 -1
- package/dist/symbol-table.js +0 -59
- package/dist/symbol-table.js.map +0 -1
package/src/m3/facade.ts
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { builtinFeatures } from "./builtins.js"
|
|
2
|
+
import { metaTypedBasedClassifierDeducerFor, qualifiedNameOf } from "./functions.js"
|
|
3
|
+
import { lioncore, metaConcepts, metaFeatures } from "./lioncore.js"
|
|
4
|
+
import { Reader } from "../reading.js"
|
|
5
|
+
import {
|
|
6
|
+
Annotation,
|
|
7
|
+
Classifier,
|
|
8
|
+
Concept,
|
|
9
|
+
Containment,
|
|
10
|
+
Enumeration,
|
|
11
|
+
EnumerationLiteral,
|
|
12
|
+
Interface,
|
|
13
|
+
Language,
|
|
14
|
+
M3Concept,
|
|
15
|
+
PrimitiveType,
|
|
16
|
+
Property,
|
|
17
|
+
Reference
|
|
18
|
+
} from "./types.js"
|
|
19
|
+
import { updateSettingsNameBased, Writer } from "../writing.js"
|
|
20
|
+
|
|
21
|
+
const { inamed_name } = builtinFeatures
|
|
22
|
+
const { ikeyed_key } = metaFeatures
|
|
23
|
+
|
|
24
|
+
export const lioncoreReader: Reader<M3Concept> = {
|
|
25
|
+
classifierOf: metaTypedBasedClassifierDeducerFor(lioncore),
|
|
26
|
+
getFeatureValue: (node, feature) =>
|
|
27
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
28
|
+
(node as any)[feature.name], // (mirrors name-based update of settings)
|
|
29
|
+
enumerationLiteralFrom: (value, _) => value as EnumerationLiteral | null
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Alias for {@link lioncoreReader}, kept for backward compatibility, and to be deprecated and removed later.
|
|
34
|
+
*/
|
|
35
|
+
export const lioncoreExtractionFacade = lioncoreReader
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* @return An implementation of {@link Writer} for instances of the LionCore M3 (so M2s).
|
|
39
|
+
*/
|
|
40
|
+
export const lioncoreWriter: Writer<M3Concept> = {
|
|
41
|
+
nodeFor: (parent, classifier, id, propertySettings) => {
|
|
42
|
+
switch (classifier.key) {
|
|
43
|
+
case metaConcepts.annotation.key:
|
|
44
|
+
return new Annotation(
|
|
45
|
+
parent as Language,
|
|
46
|
+
propertySettings[inamed_name.key] as string,
|
|
47
|
+
propertySettings[ikeyed_key.key] as string,
|
|
48
|
+
id
|
|
49
|
+
)
|
|
50
|
+
case metaConcepts.concept.key:
|
|
51
|
+
return new Concept(
|
|
52
|
+
parent as Language,
|
|
53
|
+
propertySettings[inamed_name.key] as string,
|
|
54
|
+
propertySettings[ikeyed_key.key] as string,
|
|
55
|
+
id,
|
|
56
|
+
propertySettings[metaFeatures.concept_abstract.key] as boolean
|
|
57
|
+
)
|
|
58
|
+
case metaConcepts.interface.key:
|
|
59
|
+
return new Interface(
|
|
60
|
+
parent as Language,
|
|
61
|
+
propertySettings[inamed_name.key] as string,
|
|
62
|
+
propertySettings[ikeyed_key.key] as string,
|
|
63
|
+
id
|
|
64
|
+
)
|
|
65
|
+
case metaConcepts.containment.key:
|
|
66
|
+
return new Containment(
|
|
67
|
+
parent as Classifier,
|
|
68
|
+
propertySettings[inamed_name.key] as string,
|
|
69
|
+
propertySettings[ikeyed_key.key] as string,
|
|
70
|
+
id
|
|
71
|
+
)
|
|
72
|
+
case metaConcepts.enumeration.key:
|
|
73
|
+
return new Enumeration(
|
|
74
|
+
parent as Language,
|
|
75
|
+
propertySettings[inamed_name.key] as string,
|
|
76
|
+
propertySettings[ikeyed_key.key] as string,
|
|
77
|
+
id
|
|
78
|
+
)
|
|
79
|
+
case metaConcepts.enumerationLiteral.key:
|
|
80
|
+
return new EnumerationLiteral(
|
|
81
|
+
parent as Enumeration,
|
|
82
|
+
propertySettings[inamed_name.key] as string,
|
|
83
|
+
propertySettings[ikeyed_key.key] as string,
|
|
84
|
+
id
|
|
85
|
+
)
|
|
86
|
+
case metaConcepts.language.key:
|
|
87
|
+
return new Language(
|
|
88
|
+
propertySettings[inamed_name.key] as string,
|
|
89
|
+
propertySettings[metaFeatures.language_version.key] as string,
|
|
90
|
+
id,
|
|
91
|
+
propertySettings[metaFeatures.ikeyed_key.key] as string
|
|
92
|
+
)
|
|
93
|
+
case metaConcepts.primitiveType.key:
|
|
94
|
+
return new PrimitiveType(
|
|
95
|
+
parent as Language,
|
|
96
|
+
propertySettings[inamed_name.key] as string,
|
|
97
|
+
propertySettings[ikeyed_key.key] as string,
|
|
98
|
+
id
|
|
99
|
+
)
|
|
100
|
+
case metaConcepts.property.key:
|
|
101
|
+
return new Property(
|
|
102
|
+
parent as Classifier,
|
|
103
|
+
propertySettings[inamed_name.key] as string,
|
|
104
|
+
propertySettings[ikeyed_key.key] as string,
|
|
105
|
+
id
|
|
106
|
+
)
|
|
107
|
+
case metaConcepts.reference.key:
|
|
108
|
+
return new Reference(
|
|
109
|
+
parent as Classifier,
|
|
110
|
+
propertySettings[inamed_name.key] as string,
|
|
111
|
+
propertySettings[ikeyed_key.key] as string,
|
|
112
|
+
id
|
|
113
|
+
)
|
|
114
|
+
default:
|
|
115
|
+
throw new Error(
|
|
116
|
+
`don't know a node of concept ${qualifiedNameOf(classifier)} with key ${classifier.key} that's not in LionCore M3`
|
|
117
|
+
)
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
setFeatureValue: (node, feature, value) => {
|
|
121
|
+
updateSettingsNameBased(node as unknown as Record<string, unknown>, feature, value)
|
|
122
|
+
},
|
|
123
|
+
encodingOf: literal => literal
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Alias for {@link lioncoreWriter}, kept for backward compatibility, and to be deprecated and removed later.
|
|
128
|
+
*/
|
|
129
|
+
export const lioncoreInstantationFacade = lioncoreWriter
|
|
130
|
+
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { StringsMapper } from "@lionweb/ts-utils"
|
|
2
|
+
import { SingleRef } from "../references.js"
|
|
3
|
+
import {
|
|
4
|
+
Annotation,
|
|
5
|
+
Classifier,
|
|
6
|
+
Concept,
|
|
7
|
+
Containment,
|
|
8
|
+
Enumeration,
|
|
9
|
+
EnumerationLiteral,
|
|
10
|
+
Interface,
|
|
11
|
+
Language,
|
|
12
|
+
PrimitiveType,
|
|
13
|
+
Property,
|
|
14
|
+
Reference
|
|
15
|
+
} from "./types.js"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* A factory that produces a {@link Language} instance,
|
|
20
|
+
* as well as {@link LanguageEntity entities} contained by that instance
|
|
21
|
+
* and {@link Feature features} of {@link Classifier classifiers}
|
|
22
|
+
* and {@link EnumerationLiteral enumeration literals} of {@link Enumeration enumerations}.
|
|
23
|
+
*
|
|
24
|
+
* The factory methods take care of proper containment.
|
|
25
|
+
* *Note:* also calling `havingEntities`, `havingFeatures`, and `havingLiterals` doesn't produce duplicates.
|
|
26
|
+
* (This is to stay backward compatible.)
|
|
27
|
+
*/
|
|
28
|
+
export class LanguageFactory {
|
|
29
|
+
|
|
30
|
+
readonly id: StringsMapper
|
|
31
|
+
readonly key: StringsMapper
|
|
32
|
+
readonly language: Language
|
|
33
|
+
|
|
34
|
+
constructor(name: string, version: string, id: StringsMapper, key: StringsMapper) {
|
|
35
|
+
this.id = id
|
|
36
|
+
this.key = key
|
|
37
|
+
this.language = new Language(name, version, this.id(name), this.key(name))
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
annotation(name: string, extends_?: SingleRef<Annotation>): Annotation {
|
|
42
|
+
const annotation = new Annotation(this.language, name, this.key(this.language.name, name), this.id(this.language.name, name), extends_)
|
|
43
|
+
this.language.havingEntities(annotation)
|
|
44
|
+
return annotation
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
concept(name: string, abstract: boolean, extends_?: SingleRef<Concept>): Concept {
|
|
48
|
+
const concept = new Concept(this.language, name, this.key(this.language.name, name), this.id(this.language.name, name), abstract, extends_)
|
|
49
|
+
this.language.havingEntities(concept)
|
|
50
|
+
return concept
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
interface(name: string): Interface {
|
|
54
|
+
const intface = new Interface(this.language, name, this.key(this.language.name, name), this.id(this.language.name, name))
|
|
55
|
+
this.language.havingEntities(intface)
|
|
56
|
+
return intface
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
enumeration(name: string): Enumeration {
|
|
60
|
+
const enumeration = new Enumeration(this.language, name, this.key(this.language.name, name), this.id(this.language.name, name))
|
|
61
|
+
this.language.havingEntities(enumeration)
|
|
62
|
+
return enumeration
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
primitiveType(name: string): PrimitiveType {
|
|
66
|
+
const primitiveType = new PrimitiveType(this.language, name, this.key(this.language.name, name), this.id(this.language.name, name))
|
|
67
|
+
this.language.havingEntities(primitiveType)
|
|
68
|
+
return primitiveType
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
containment(classifier: Classifier, name: string): Containment {
|
|
73
|
+
const containment = new Containment(classifier, name, this.key(this.language.name, classifier.name, name), this.id(this.language.name, classifier.name, name))
|
|
74
|
+
classifier.havingFeatures(containment)
|
|
75
|
+
return containment
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
property(classifier: Classifier, name: string): Property {
|
|
79
|
+
const property = new Property(classifier, name, this.key(this.language.name, classifier.name, name), this.id(this.language.name, classifier.name, name))
|
|
80
|
+
classifier.havingFeatures(property)
|
|
81
|
+
return property
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
reference(classifier: Classifier, name: string): Reference {
|
|
85
|
+
const reference = new Reference(classifier, name, this.key(this.language.name, classifier.name, name), this.id(this.language.name, classifier.name, name))
|
|
86
|
+
classifier.havingFeatures(reference)
|
|
87
|
+
return reference
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
enumerationLiteral(enumeration: Enumeration, name: string): EnumerationLiteral {
|
|
92
|
+
const enumerationLiteral = new EnumerationLiteral(enumeration, name, this.key(this.language.name, enumeration.name, name), this.id(this.language.name, enumeration.name, name))
|
|
93
|
+
enumeration.havingLiterals(enumerationLiteral)
|
|
94
|
+
return enumerationLiteral
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
}
|
|
98
|
+
|
|
@@ -0,0 +1,72 @@
|
|
|
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 { LionWebJsonMetaPointer } from "@lionweb/json"
|
|
19
|
+
|
|
20
|
+
import { Classifier, Containment, Feature, Language, Property, Reference } from "./types.js"
|
|
21
|
+
import { MemoisingSymbolTable } from "./symbol-table.js"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Type def. for functions that resolve a {@link Feature feature} of the indicated sub-type
|
|
26
|
+
* from a feature's and a classifier's meta-pointers,
|
|
27
|
+
* throwing an {@link Error error} when the feature couldn't be resolved,
|
|
28
|
+
* or it isn't of the expected sub-type.
|
|
29
|
+
*/
|
|
30
|
+
export type FeatureResolver<FT extends Feature> = (metaPointer: LionWebJsonMetaPointer, classifier: Classifier) => FT
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Type def. for an object containing {@link FeatureResolver resolvers} for
|
|
34
|
+
* {@link Property properties}, {@link Containmnent containments}, and {@link Reference references}.
|
|
35
|
+
*/
|
|
36
|
+
export type FeatureResolvers = {
|
|
37
|
+
resolvedPropertyFrom: FeatureResolver<Property>
|
|
38
|
+
resolvedContainmentFrom: FeatureResolver<Containment>
|
|
39
|
+
resolvedReferenceFrom: FeatureResolver<Reference>
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* @return an {@link FeatureResolvers object} for the given {@link Language languages}.
|
|
44
|
+
*/
|
|
45
|
+
export const featureResolversFor = (languages: Language[]): FeatureResolvers => {
|
|
46
|
+
const symbolTable = new MemoisingSymbolTable(languages)
|
|
47
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
48
|
+
const featureResolverFor = <FT extends Feature>(featureClassConstructor: new (...args: any[]) => FT): FeatureResolver<FT> =>
|
|
49
|
+
(featureMetaPointer, classifier: Classifier) => {
|
|
50
|
+
const classifierMetaPointer = classifier.metaPointer()
|
|
51
|
+
const featureLocationMessage = () => `feature with meta-pointer ${JSON.stringify(featureMetaPointer)} on classifier with meta-pointer ${JSON.stringify(classifierMetaPointer)}`
|
|
52
|
+
const feature = symbolTable.featureMatching(classifierMetaPointer, featureMetaPointer)
|
|
53
|
+
if (feature === undefined) {
|
|
54
|
+
throw new Error(`couldn't resolve ${featureLocationMessage()}`) // fail early <== unrecoverable
|
|
55
|
+
}
|
|
56
|
+
if (feature.constructor !== featureClassConstructor) { // feature's type must match desired type *exactly* — cheaper to evaluate than "feature instanceof featureClassConstructor"
|
|
57
|
+
throw new Error(`${featureLocationMessage()} is not a ${featureClassConstructor.name} but a ${feature.constructor.name}`) // fail early <== unrecoverable
|
|
58
|
+
}
|
|
59
|
+
/*
|
|
60
|
+
* We could make this function memoising as well, to avoid having to perform these checks every resolution.
|
|
61
|
+
* That memoisation would involve a 6-deep lookup, in the feature's meta-pointer + container's classifier meta-pointer.
|
|
62
|
+
* The checks seem cheap enough to not be problematic performance-wise, though.
|
|
63
|
+
*/
|
|
64
|
+
return feature as FT // valid <== feature.constructor === featureClassConstructor
|
|
65
|
+
}
|
|
66
|
+
return {
|
|
67
|
+
resolvedPropertyFrom: featureResolverFor(Property),
|
|
68
|
+
resolvedContainmentFrom: featureResolverFor(Containment),
|
|
69
|
+
resolvedReferenceFrom: featureResolverFor(Reference)
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Various functions on M3 models.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
import { LionWebId, LionWebKey } from "@lionweb/json"
|
|
7
|
+
import { cycleWith, flatMapNonCyclingFollowing, sortByStringKey } from "@lionweb/ts-utils"
|
|
8
|
+
import { containmentChain } from "../functions.js"
|
|
9
|
+
import { ClassifierDeducer } from "../reading.js"
|
|
10
|
+
import { isRef, unresolved } from "../references.js"
|
|
11
|
+
import { Node } from "../types.js"
|
|
12
|
+
import {
|
|
13
|
+
Annotation,
|
|
14
|
+
Classifier,
|
|
15
|
+
Concept,
|
|
16
|
+
Containment,
|
|
17
|
+
DataType,
|
|
18
|
+
Enumeration,
|
|
19
|
+
Feature,
|
|
20
|
+
IKeyed,
|
|
21
|
+
IMetaTyped,
|
|
22
|
+
INamed,
|
|
23
|
+
Interface,
|
|
24
|
+
isINamed,
|
|
25
|
+
Language,
|
|
26
|
+
LanguageEntity,
|
|
27
|
+
Link,
|
|
28
|
+
M3Concept,
|
|
29
|
+
Property,
|
|
30
|
+
Reference
|
|
31
|
+
} from "./types.js"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @return The type of the given {@link Feature}
|
|
36
|
+
*/
|
|
37
|
+
const type = (feature: Feature): Classifier | DataType | typeof unresolved =>
|
|
38
|
+
(feature as (Link | Property)).type
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
const isProperty = (feature: Feature): feature is Property =>
|
|
42
|
+
feature instanceof Property
|
|
43
|
+
|
|
44
|
+
const isContainment = (feature: Feature): feature is Containment =>
|
|
45
|
+
feature instanceof Containment
|
|
46
|
+
|
|
47
|
+
const isReference = (feature: Feature): feature is Reference =>
|
|
48
|
+
feature instanceof Reference
|
|
49
|
+
|
|
50
|
+
const isMultiple = (feature: Feature): feature is Link =>
|
|
51
|
+
feature instanceof Link && feature.multiple
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* The (names of the) metatypes of a feature.
|
|
56
|
+
*/
|
|
57
|
+
type FeatureMetaType =
|
|
58
|
+
| "Containment"
|
|
59
|
+
| "Property"
|
|
60
|
+
| "Reference"
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* @return the (name of the) metatype of the given {@link Feature feature}.
|
|
64
|
+
*/
|
|
65
|
+
const featureMetaType = (feature: Feature): FeatureMetaType => {
|
|
66
|
+
if (feature instanceof Containment) {
|
|
67
|
+
return "Containment"
|
|
68
|
+
}
|
|
69
|
+
if (feature instanceof Property) {
|
|
70
|
+
return "Property"
|
|
71
|
+
}
|
|
72
|
+
if (feature instanceof Reference) {
|
|
73
|
+
return "Reference"
|
|
74
|
+
}
|
|
75
|
+
throw new Error(`unhandled Feature sub type ${feature.constructor.name}`)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Determines whether a {@link Feature feature} is "relational",
|
|
81
|
+
* i.e. it's a {@link Link containment or reference}.
|
|
82
|
+
*/
|
|
83
|
+
const isRelational = (feature: Feature): feature is Link =>
|
|
84
|
+
feature instanceof Link
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* @return the relations among the given {@link Feature features}.
|
|
88
|
+
*/
|
|
89
|
+
const relations = (features: Feature[]): Link[] =>
|
|
90
|
+
features.filter(isRelational)
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* @return the non-relations among the given {@link Feature features}.
|
|
94
|
+
*/
|
|
95
|
+
const nonRelationalFeatures = (features: Feature[]): Feature[] =>
|
|
96
|
+
features.filter((feature) => !isRelational(feature))
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* @return the relations of the given {@link LanguageEntity language element}.
|
|
101
|
+
*/
|
|
102
|
+
const relationsOf = (element: LanguageEntity): Link[] =>
|
|
103
|
+
element instanceof Classifier
|
|
104
|
+
? relations(element.features)
|
|
105
|
+
: []
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* @return The "things", i.e. {@link M3Concept}s, directly contained by the given "thing".
|
|
110
|
+
* These can be:
|
|
111
|
+
* {@link LanguageEntity language entities}, {@link Feature features}, and {@link EnumerationLiteral enumeration literals}
|
|
112
|
+
* (and all their sub types).
|
|
113
|
+
*/
|
|
114
|
+
const directlyContaineds = (thing: M3Concept): M3Concept[] => {
|
|
115
|
+
if (thing instanceof Language) {
|
|
116
|
+
return thing.entities
|
|
117
|
+
}
|
|
118
|
+
if (thing instanceof Classifier) {
|
|
119
|
+
return thing.features as M3Concept[] // (cast is necessary because of presence of Feature#classifier getter...?)
|
|
120
|
+
}
|
|
121
|
+
if (thing instanceof Enumeration) {
|
|
122
|
+
return thing.literals
|
|
123
|
+
}
|
|
124
|
+
return []
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* @return All {@link M3Concept nodes} contained in this {@link Language language},
|
|
130
|
+
* including the language itself.
|
|
131
|
+
*/
|
|
132
|
+
const allContaineds = (language: Language): M3Concept[] =>
|
|
133
|
+
[
|
|
134
|
+
language,
|
|
135
|
+
...directlyContaineds(language),
|
|
136
|
+
...directlyContaineds(language).flatMap(directlyContaineds)
|
|
137
|
+
]
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Performs a depth-first tree traversal of a language, "flatMapping" the `map` function on every node.
|
|
142
|
+
* It avoids visiting nodes twice (to avoid potential infinite loops), but doesn't report cycles.
|
|
143
|
+
*/
|
|
144
|
+
const flatMap = <T>(language: Language, map: (t: M3Concept) => T[]): T[] =>
|
|
145
|
+
flatMapNonCyclingFollowing(map, directlyContaineds)(language)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* @return string the name of the given {@link INamed named thing}.
|
|
150
|
+
*/
|
|
151
|
+
const nameOf = <T extends INamed>({name}: T): string =>
|
|
152
|
+
name
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* @return the given named things sorted by name
|
|
157
|
+
*/
|
|
158
|
+
export const nameSorted = <T extends INamed>(ts: T[]): T[] =>
|
|
159
|
+
sortByStringKey(ts, nameOf)
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* @return the concatenation of the names of the given nodes using the given separator.
|
|
164
|
+
*/
|
|
165
|
+
const concatenateNamesOf = (separator: string, nodes: M3Concept[]): string =>
|
|
166
|
+
nodes
|
|
167
|
+
.filter(isINamed)
|
|
168
|
+
.map(nameOf)
|
|
169
|
+
.join(separator)
|
|
170
|
+
// !! slight overkill: every node in an M2 is an M3Concept, so IKeyed and INamed
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* @return the qualified name of the given {@link INamed named thing}.
|
|
174
|
+
*/
|
|
175
|
+
const qualifiedNameOf = <T extends INamed & Node>(node: T, separator = "."): string =>
|
|
176
|
+
concatenateNamesOf(separator, containmentChain(node).reverse() as M3Concept[])
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* @return the {@link INamed named things} in this {@link Language language}
|
|
181
|
+
* (excluding the language itself)
|
|
182
|
+
*/
|
|
183
|
+
const namedsOf = (language: Language): M3Concept[] =>
|
|
184
|
+
allContaineds(language).filter(isINamed)
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* @return the key of the given {@link INamed named thing}.
|
|
189
|
+
*/
|
|
190
|
+
const keyOf = <T extends IKeyed>({key}: T): LionWebKey =>
|
|
191
|
+
key
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
type ConcreteClassifier = Concept | Annotation
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Determines whether the given {@link LanguageEntity metamodel element} is
|
|
199
|
+
* *concrete*, i.e. is instantiable.
|
|
200
|
+
*/
|
|
201
|
+
const isConcrete = (thing: LanguageEntity): thing is ConcreteClassifier =>
|
|
202
|
+
(thing instanceof Concept && !thing.abstract) || (thing instanceof Annotation)
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Determines whether the given {@link LanguageEntity metamodel element} is a {@link Concept concept}
|
|
206
|
+
* which is a partition.
|
|
207
|
+
*/
|
|
208
|
+
const isPartition = (thing: LanguageEntity): thing is Concept =>
|
|
209
|
+
thing instanceof Concept && thing.partition
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* @return an array of {@link Classifier classifiers} that it **directly** inherits from.
|
|
213
|
+
*/
|
|
214
|
+
const inheritsDirectlyFrom = (classifier: Classifier): Classifier[] => {
|
|
215
|
+
if (classifier instanceof Concept || classifier instanceof Annotation) {
|
|
216
|
+
return [
|
|
217
|
+
...(
|
|
218
|
+
isRef(classifier.extends)
|
|
219
|
+
? [classifier.extends as Classifier]
|
|
220
|
+
: []
|
|
221
|
+
),
|
|
222
|
+
...classifier.implements
|
|
223
|
+
]
|
|
224
|
+
}
|
|
225
|
+
if (classifier instanceof Interface) {
|
|
226
|
+
return classifier.extends
|
|
227
|
+
}
|
|
228
|
+
throw new Error(`classifier type ${typeof classifier} not handled`)
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Alias for {@link inheritsDirectlyFrom}, kept for backward compatibility, and to be deprecated and removed later.
|
|
233
|
+
*/
|
|
234
|
+
const inheritsFrom = inheritsDirectlyFrom;
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* @return an array that's either an inheritance cycle, or empty (meaning: no inheritance cycle).
|
|
238
|
+
*/
|
|
239
|
+
const inheritanceCycleWith = (classifier: Classifier) =>
|
|
240
|
+
cycleWith(classifier, inheritsDirectlyFrom)
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Alias for {@link inheritanceCycleWith}, kept for backward compatibility, and to be deprecated and removed later.
|
|
244
|
+
*/
|
|
245
|
+
const inheritedCycleWith = inheritanceCycleWith;
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* @return *all* super types (through `extends` or `implements`) of the given {@link Classifier classifier}.
|
|
249
|
+
*/
|
|
250
|
+
const allSuperTypesOf = (classifier: Classifier): Classifier[] =>
|
|
251
|
+
flatMapNonCyclingFollowing(inheritsDirectlyFrom, inheritsDirectlyFrom)(classifier)
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* @return *all* {@link Feature features} of the given {@link Classifier classifier},
|
|
256
|
+
* including the inherited ones.
|
|
257
|
+
*/
|
|
258
|
+
const allFeaturesOf = (classifier: Classifier): Feature[] =>
|
|
259
|
+
flatMapNonCyclingFollowing((ci) => ci.features, inheritsDirectlyFrom)(classifier)
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Determines whether the given {@link LanguageEntity language element} is an {@link Enumeration enumeration}.
|
|
264
|
+
*/
|
|
265
|
+
const isEnumeration = (element: LanguageEntity): element is Enumeration =>
|
|
266
|
+
element instanceof Enumeration
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* @return a function that looks up a classifier from the given {@link Language language} by its ID.
|
|
271
|
+
*/
|
|
272
|
+
const idBasedClassifierDeducerFor = (language: Language) =>
|
|
273
|
+
(id: LionWebId) =>
|
|
274
|
+
language.entities.find((element) => element instanceof Classifier && element.id === id) as Classifier
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* @return a function that looks up a classifier from the given {@link Language language} by its name.
|
|
278
|
+
*/
|
|
279
|
+
const nameBasedClassifierDeducerFor = (language: Language) =>
|
|
280
|
+
(name: string) =>
|
|
281
|
+
language.entities.find((element) => element instanceof Classifier && element.name === name) as Classifier
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* @return a {@link ClassifierDeducer classifier deducer} that deduces the classifier of nodes by looking up
|
|
286
|
+
* the classifier in the given {@link Language language} by matching the node object's class name to classifiers' names.
|
|
287
|
+
* **Note** that this is not reliable when using bundlers who might minimize class names, and such.
|
|
288
|
+
*/
|
|
289
|
+
const classBasedClassifierDeducerFor = <NT extends Node>(language: Language): ClassifierDeducer<NT> =>
|
|
290
|
+
(node: NT) => nameBasedClassifierDeducerFor(language)(node.constructor.name)
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* @return a {@link ClassifierDeducer classifier deducer} that deduces the classifier of nodes that implement {@link IMetaTyped}
|
|
295
|
+
* by looking up the classifier in the given {@link Language language} by matching the result of {@link IMetaTyped#metaType}
|
|
296
|
+
* to classifiers' names.
|
|
297
|
+
*/
|
|
298
|
+
const metaTypedBasedClassifierDeducerFor = <NT extends Node & IMetaTyped>(language: Language): ClassifierDeducer<NT> =>
|
|
299
|
+
(node: NT) => nameBasedClassifierDeducerFor(language)(node.metaType())
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* @return all {@link Concept concepts} defined in the given {@link Language language}.
|
|
304
|
+
*/
|
|
305
|
+
const conceptsOf = (language: Language): Concept[] =>
|
|
306
|
+
language.entities.filter((entity) => entity instanceof Concept) as Concept[]
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
const isInstantiableClassifier = (entity: LanguageEntity): boolean =>
|
|
310
|
+
entity instanceof Annotation
|
|
311
|
+
|| (entity instanceof Concept && !entity.abstract)
|
|
312
|
+
// leaves out Interface and Concept { abstract: true }
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* @return an array of all instantiable {@link Classifier classifiers} of the given {@link Language language}.
|
|
316
|
+
*/
|
|
317
|
+
const instantiableClassifiersOf = (language: Language): Classifier[] =>
|
|
318
|
+
language.entities.filter(isInstantiableClassifier) as Classifier[]
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* @return whether the two given {@link Classifiers classifiers} are the same (/identical by meta-pointer).
|
|
323
|
+
*/
|
|
324
|
+
const areSameClassifiers = (left: Classifier, right: Classifier) =>
|
|
325
|
+
(left === right) || (
|
|
326
|
+
areSameLanguages(left.language, right.language) && left.key === right.key
|
|
327
|
+
)
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* @return whether the two given {@link Language languages} are the same (/identical by meta-pointer).
|
|
331
|
+
*/
|
|
332
|
+
const areSameLanguages = (left: Language, right: Language) =>
|
|
333
|
+
(left === right) || (
|
|
334
|
+
left.key === right.key && left.version === right.version
|
|
335
|
+
)
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
export {
|
|
339
|
+
allContaineds,
|
|
340
|
+
allFeaturesOf,
|
|
341
|
+
allSuperTypesOf,
|
|
342
|
+
areSameClassifiers,
|
|
343
|
+
areSameLanguages,
|
|
344
|
+
classBasedClassifierDeducerFor,
|
|
345
|
+
concatenateNamesOf,
|
|
346
|
+
conceptsOf,
|
|
347
|
+
containmentChain,
|
|
348
|
+
directlyContaineds,
|
|
349
|
+
featureMetaType,
|
|
350
|
+
flatMap,
|
|
351
|
+
idBasedClassifierDeducerFor,
|
|
352
|
+
inheritanceCycleWith,
|
|
353
|
+
inheritedCycleWith,
|
|
354
|
+
inheritsFrom,
|
|
355
|
+
inheritsDirectlyFrom,
|
|
356
|
+
instantiableClassifiersOf,
|
|
357
|
+
isConcrete,
|
|
358
|
+
isContainment,
|
|
359
|
+
isEnumeration,
|
|
360
|
+
isMultiple,
|
|
361
|
+
isPartition,
|
|
362
|
+
isProperty,
|
|
363
|
+
isReference,
|
|
364
|
+
keyOf,
|
|
365
|
+
metaTypedBasedClassifierDeducerFor,
|
|
366
|
+
nameBasedClassifierDeducerFor,
|
|
367
|
+
nameOf,
|
|
368
|
+
namedsOf,
|
|
369
|
+
nonRelationalFeatures,
|
|
370
|
+
relations,
|
|
371
|
+
relationsOf,
|
|
372
|
+
type,
|
|
373
|
+
qualifiedNameOf
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
export type {
|
|
377
|
+
FeatureMetaType
|
|
378
|
+
}
|
|
379
|
+
|
package/src/m3/index.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export * from "./builtins.js"
|
|
2
|
+
export * from "./constraints.js"
|
|
3
|
+
export * from "./deserializer.js"
|
|
4
|
+
export * from "./facade.js"
|
|
5
|
+
export * from "./factory.js"
|
|
6
|
+
export * from "./feature-resolvers.js"
|
|
7
|
+
export * from "./functions.js"
|
|
8
|
+
export * from "./lioncore.js"
|
|
9
|
+
export * from "./reference-checker.js"
|
|
10
|
+
export * from "./serializer.js"
|
|
11
|
+
export * from "./symbol-table.js"
|
|
12
|
+
export * from "./types.js"
|