Skip to content

Commit b751e19

Browse files
committed
Integration of Lionweb nodes into TraceNodes (work in progress)
1 parent c3cdf3e commit b751e19

File tree

8 files changed

+280
-217
lines changed

8 files changed

+280
-217
lines changed

CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22
All notable changes to this project from version 1.2.0 upwards are documented in this file.
33
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
44

5+
## [1.6.4] – 2024-03-05
6+
7+
### Changed
8+
- Decoupled trace nodes from ECore so it may also possible to use them with Lionweb.
9+
510
## [1.6.3] – 2024-02-29
611

712
### Fixed

src/interop/ecore.ts

+130-5
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
Node,
1010
NODE_TYPES,
1111
PackageDescription,
12+
PropertyDefinition,
1213
registerNodeDefinition,
1314
registerNodeProperty
1415
} from "../model/model";
@@ -18,19 +19,29 @@ import {Issue, IssueSeverity, IssueType} from "../validation";
1819
import {addLiteral, getEPackage} from "./ecore-basic";
1920
import {
2021
STARLASU_URI_V2,
22+
THE_ENTITY_DECLARATION_INTERFACE,
23+
THE_EXPRESSION_INTERFACE,
2124
THE_ISSUE_ECLASS,
2225
THE_ISSUE_SEVERITY_EENUM,
2326
THE_ISSUE_TYPE_EENUM,
2427
THE_LOCAL_DATE_ECLASS,
2528
THE_LOCAL_DATE_TIME_ECLASS,
2629
THE_LOCAL_TIME_ECLASS,
27-
THE_NODE_ECLASS, THE_NODE_ORIGIN_ECLASS,
30+
THE_NODE_ECLASS as THE_NODE_ECLASS_V2,
31+
THE_NODE_ECLASS,
32+
THE_NODE_ORIGIN_ECLASS,
2833
THE_POINT_ECLASS,
29-
THE_POSITION_ECLASS, THE_REFERENCE_BY_NAME_ECLASS,
30-
THE_RESULT_ECLASS, THE_SIMPLE_ORIGIN_ECLASS, THE_TEXT_FILE_DESTINATION_ECLASS
34+
THE_POSITION_ECLASS,
35+
THE_REFERENCE_BY_NAME_ECLASS,
36+
THE_RESULT_ECLASS,
37+
THE_SIMPLE_ORIGIN_ECLASS,
38+
THE_STATEMENT_INTERFACE,
39+
THE_TEXT_FILE_DESTINATION_ECLASS
3140
} from "./starlasu-v2-metamodel";
32-
import {KOLASU_URI_V1} from "./kolasu-v1-metamodel";
41+
import {KOLASU_URI_V1, THE_NODE_ECLASS as THE_NODE_ECLASS_V1} from "./kolasu-v1-metamodel";
3342
import {EBigDecimal, EBigInteger} from "./ecore-patching";
43+
import {ExternalNode} from "../trace/trace-node";
44+
3445
export * as starlasu_v2 from "./starlasu-v2-metamodel";
3546
export * as kolasu_v1 from "./kolasu-v1-metamodel";
3647

@@ -316,7 +327,7 @@ export function fromEObject(obj: ECore.EObject | any, parent?: Node): ASTElement
316327
const ePackage = eClass.eContainer as ECore.EPackage;
317328
const constructor = NODE_TYPES[ePackage.get("name")]?.nodes[eClass.get("name")];
318329
if(constructor) {
319-
const node = new constructor();
330+
const node = new constructor().withParent(parent);
320331
node.parent = parent;
321332
eClass.get("eAllStructuralFeatures").forEach(ft => {
322333
const name = ft.get("name");
@@ -804,3 +815,117 @@ export interface EcoreMetamodelSupport {
804815
generateMetamodel(resource: ECore.Resource, includingKolasuMetamodel: boolean): void;
805816
}
806817

818+
export class ECoreNode extends ExternalNode {
819+
820+
parent?: ECoreNode;
821+
822+
constructor(public eo: ECore.EObject) {
823+
super();
824+
const container = this.eo.eContainer;
825+
if (container?.isKindOf(THE_NODE_ECLASS_V2) || container?.isKindOf(THE_NODE_ECLASS_V1)) {
826+
this.parent = new ECoreNode(container);
827+
}
828+
}
829+
830+
get nodeDefinition() {
831+
return {
832+
package: this.eo.eClass.eContainer.get("name") as string,
833+
name: this.eo.eClass.get("name") as string,
834+
properties: this.getProperties()
835+
};
836+
}
837+
838+
get(...path: string[]): ExternalNode | undefined {
839+
let eo: ECore.EObject = this.eo;
840+
for (const component of path) {
841+
eo = eo?.get(component);
842+
}
843+
if (eo) {
844+
return new ECoreNode(eo);
845+
} else {
846+
return undefined;
847+
}
848+
}
849+
850+
getAttribute(name: string): any {
851+
return this.eo.get(name);
852+
}
853+
854+
getAttributes(): { [p: string]: any } {
855+
const result: any = {};
856+
for (const attr of this.eo.eClass.get("eAllAttributes")) {
857+
const name = attr.get("name");
858+
result[name] = this.eo.get(name);
859+
}
860+
return result;
861+
}
862+
863+
getChildren(role?: string): ExternalNode[] {
864+
return this.getChildrenEObjects(role).map(c => new ECoreNode(c));
865+
}
866+
867+
getId(): string {
868+
return this.eo.fragment();
869+
}
870+
871+
getIssues(property = "issues"): Issue[] | undefined {
872+
const raw = this.eo.get(property);
873+
if (raw) {
874+
return fromEObject(raw) as Issue[];
875+
} else {
876+
return undefined;
877+
}
878+
}
879+
880+
getPosition(property = "position"): Position | undefined {
881+
const raw = this.eo.get(property);
882+
if (raw) {
883+
return fromEObject(raw) as Position;
884+
} else {
885+
return undefined;
886+
}
887+
}
888+
889+
getRole(): string | undefined {
890+
return this.eo.eContainingFeature?.get("name");
891+
}
892+
893+
getProperties(): { [name: string | symbol]: PropertyDefinition } {
894+
const result: { [name: string | symbol]: PropertyDefinition } = {};
895+
for (const attr of this.eo.eClass.get("eAllAttributes")) {
896+
const name = attr.get("name");
897+
result[name] = {name: name, child: false};
898+
}
899+
this.eo.eContents()
900+
.filter((c) => c.eContainingFeature.get("name") != "position")
901+
.forEach((c) => {
902+
const name = c.eContainingFeature.get("name");
903+
result[name] = {name, child: true, multiple: c.eContainingFeature.get("many")};
904+
});
905+
return result;
906+
}
907+
908+
protected getChildrenEObjects(role: string | undefined) {
909+
return this.eo.eContents()
910+
.filter((c) => c.isKindOf(THE_NODE_ECLASS_V2) || c.isKindOf(THE_NODE_ECLASS_V1))
911+
.filter((c) => c.eContainingFeature.get("name") != "origin")
912+
.filter((c) => c.eContainingFeature.get("name") != "destination")
913+
.filter((c) => role == null || role == c.eContainingFeature.get("name"));
914+
}
915+
916+
isDeclaration(): boolean {
917+
return this.eo.isKindOf(THE_ENTITY_DECLARATION_INTERFACE);
918+
}
919+
920+
isExpression(): boolean {
921+
return this.eo.isKindOf(THE_EXPRESSION_INTERFACE);
922+
}
923+
924+
isStatement(): boolean {
925+
return this.eo.isKindOf(THE_STATEMENT_INTERFACE);
926+
}
927+
928+
equals(other: ExternalNode): boolean {
929+
return super.equals(other) || (other instanceof ECoreNode && other.eo == this.eo);
930+
}
931+
}

src/interop/lionweb.ts

+73-30
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,15 @@ import {
88
Feature,
99
Id,
1010
InstantiationFacade, Language,
11-
Node as LionwebNode
11+
Node as LionwebNodeInterface
1212
} from "@lionweb/core";
13-
import {Node, NodeDefinition, PropertyDefinition} from "..";
13+
import {ExternalNode, Issue, Node, NodeDefinition, Position, PropertyDefinition} from "..";
1414

15-
export class TylasuNodeWrapper implements LionwebNode {
15+
export class TylasuNodeWrapper implements LionwebNodeInterface {
1616
id: Id;
1717
node: Node;
18-
parent?: LionwebNode;
19-
annotations: LionwebNode[];
18+
parent?: LionwebNodeInterface;
19+
annotations: LionwebNodeInterface[];
2020
}
2121

2222
export class LanguageMapping {
@@ -47,28 +47,6 @@ function featureToProperty(feature: Feature): PropertyDefinition {
4747
return def
4848
}
4949

50-
export class DynamicNode extends Node {
51-
52-
protected readonly _nodeDefinition: NodeDefinition;
53-
54-
constructor(public readonly classifier: Classifier) {
55-
super();
56-
const properties = {};
57-
classifier.features.forEach(f => {
58-
properties[f.name] = featureToProperty(f);
59-
});
60-
this._nodeDefinition = {
61-
name: classifier.name,
62-
properties: properties,
63-
resolved: true
64-
};
65-
}
66-
67-
public get nodeDefinition(): NodeDefinition {
68-
return this._nodeDefinition;
69-
}
70-
}
71-
7250
export class TylasuInstantiationFacade implements InstantiationFacade<TylasuNodeWrapper> {
7351

7452
constructor(public languageMappings: LanguageMapping[] = [STARLASU_LANGUAGE_MAPPING]) {}
@@ -87,12 +65,16 @@ export class TylasuInstantiationFacade implements InstantiationFacade<TylasuNode
8765
}
8866
}
8967
if (!node) {
90-
node = new DynamicNode(classifier);
68+
node = new LionwebNode(classifier, {
69+
id,
70+
parent,
71+
annotations: []
72+
});
9173
}
9274
return {
9375
id,
9476
parent,
95-
node: this.setupNode(node, parent?.node, propertySettings),
77+
node: node.withParent(parent?.node),
9678
annotations: []
9779
};
9880
}
@@ -104,7 +86,7 @@ export class TylasuInstantiationFacade implements InstantiationFacade<TylasuNode
10486
node.node.setChild(feature.name, (value as TylasuNodeWrapper)?.node);
10587
}
10688
} else {
107-
node.node[feature.name] = value;
89+
node.node.setAttribute(feature.name, value);
10890
}
10991
}
11092

@@ -304,3 +286,64 @@ export function findClassifier(language: Language, id: string) {
304286

305287
export const AST_NODE_CLASSIFIER = findClassifier(STARLASU_LANGUAGE, "com_strumenta_starlasu_ASTNode");
306288
STARLASU_LANGUAGE_MAPPING.register(Node, AST_NODE_CLASSIFIER)
289+
290+
export class LionwebNode extends ExternalNode {
291+
292+
parent: LionwebNode;
293+
public readonly nodeDefinition: NodeDefinition;
294+
295+
constructor(
296+
public readonly classifier: Classifier,
297+
protected lwnode: LionwebNodeInterface
298+
) {
299+
super();
300+
const properties = {};
301+
classifier.features.forEach(f => {
302+
properties[f.name] = featureToProperty(f);
303+
});
304+
this.nodeDefinition = {
305+
name: classifier.name,
306+
properties: properties,
307+
resolved: true
308+
};
309+
}
310+
311+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
312+
get(...path: string[]): ExternalNode | undefined {
313+
return undefined; // TODO
314+
}
315+
316+
getAttributes(): { [p: string]: any } {
317+
return {}; // TODO
318+
}
319+
320+
getId(): string {
321+
return "";
322+
}
323+
324+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
325+
getIssues(property?: string): Issue[] | undefined {
326+
return undefined;
327+
}
328+
329+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
330+
getPosition(property?: string): Position | undefined {
331+
return undefined;
332+
}
333+
334+
getRole(): string | undefined {
335+
return undefined;
336+
}
337+
338+
isDeclaration(): boolean {
339+
return false;
340+
}
341+
342+
isExpression(): boolean {
343+
return false;
344+
}
345+
346+
isStatement(): boolean {
347+
return false;
348+
}
349+
}

0 commit comments

Comments
 (0)