Skip to content

Commit 9715d9a

Browse files
committed
Renamed "property" to either "feature" or "attribute" according to Kolasu terminology
Correctly record references in node metadata for ECore nodes
1 parent 9d7ae9d commit 9715d9a

14 files changed

+80
-66
lines changed

CHANGELOG.md

+8
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@
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.16] – 2024-03-21
6+
7+
### Changed
8+
- Renamed "property" to either "feature" or "attribute" according to Kolasu terminology
9+
10+
### Fixed
11+
- Correctly record references in node metadata for ECore nodes
12+
513
## [1.6.15] – 2024-03-21
614

715
### Added

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"description": "AST building blocks for TypeScript/JavaScript, part of the *lasu family, with optional integrations with ANTLR4 and Ecore.",
44
"author": "Strumenta s.r.l.",
55
"publisher": "strumenta",
6-
"version": "1.6.15",
6+
"version": "1.6.16",
77
"license": "Apache-2.0",
88
"keywords": [
99
"antlr",

src/interop/ecore.ts

+11-9
Original file line numberDiff line numberDiff line change
@@ -111,8 +111,8 @@ function registerEClass(nodeType: string, packageDef: PackageDescription, ePacka
111111
}
112112
const nodeDef = getNodeDefinition(constructor);
113113
if (nodeDef) {
114-
for (const prop in nodeDef.properties) {
115-
const property = nodeDef.properties[prop];
114+
for (const prop in nodeDef.features) {
115+
const property = nodeDef.features[prop];
116116
if(property.inherited) {
117117
continue;
118118
}
@@ -376,8 +376,8 @@ Node.prototype[TO_EOBJECT_SYMBOL] = function(): ECore.EObject {
376376
throw new Error("Unknown class " + def.name + " in package " + def.package);
377377
}
378378
const eObject = eClass.create();
379-
for (const name in def.properties) {
380-
const p = def.properties[name];
379+
for (const name in def.features) {
380+
const p = def.features[name];
381381
const feature = eClass.get("eAllStructuralFeatures").find(f => f.get("name") == name);
382382
if (!feature) {
383383
throw new Error(`Unknown feature: ${name} of ${eClass.get("name")}`);
@@ -850,7 +850,7 @@ export class ECoreNode extends NodeAdapter implements PossiblyNamed {
850850
this._nodeDefinition = {
851851
package: this.eo.eClass.eContainer.get("name") as string,
852852
name: this.eo.eClass.get("name") as string,
853-
properties: this.getProperties()
853+
features: this.getFeatures()
854854
};
855855
}
856856
return this._nodeDefinition;
@@ -946,7 +946,7 @@ export class ECoreNode extends NodeAdapter implements PossiblyNamed {
946946
return this.eo.eContainingFeature?.get("name");
947947
}
948948

949-
getProperties(): { [name: string | symbol]: PropertyDefinition } {
949+
getFeatures(): { [name: string | symbol]: PropertyDefinition } {
950950
const result: { [name: string | symbol]: PropertyDefinition } = {};
951951
const eClass = this.eo.eClass;
952952
const features = eClass.get("eAllStructuralFeatures");
@@ -956,11 +956,13 @@ export class ECoreNode extends NodeAdapter implements PossiblyNamed {
956956
if (isReference && !isNodeType(ft.get("eType") || ft.get("eGenericType")?.get("eClassifier"))) {
957957
// skip
958958
} else {
959+
const isChild = isReference &&
960+
ft.get('containment') &&
961+
ft.get("eGenericType")?.get("eClassifier") != THE_REFERENCE_BY_NAME_ECLASS;
959962
result[name] = {
960963
name,
961-
child: isReference &&
962-
ft.get('containment') &&
963-
ft.get("eGenericType")?.get("eClassifier") != THE_REFERENCE_BY_NAME_ECLASS,
964+
child: isChild,
965+
reference: isReference && !isChild,
964966
multiple: isReference ? ft.get('many') : undefined,
965967
};
966968
}

src/interop/indexing.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ export class OnlyReferencedIdProvider implements IdProvider {
4040

4141
constructor(private root: Node, private idProvider: IdProvider = new SequentialIdProvider()) {
4242
for (const node of root.walk()) {
43-
node.properties
43+
node.features
4444
.filter(p => p.value instanceof ReferenceByName)
4545
.map(p => (p.value as ReferenceByName<any>).referred as Node)
4646
.forEach(node => this.referencedElements.push(node));

src/interop/lionweb.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ export class LionwebNode extends NodeAdapter {
141141
});
142142
this._nodeDefinition = {
143143
name: classifier.name,
144-
properties: properties,
144+
features: properties,
145145
resolved: true
146146
};
147147
}
@@ -164,8 +164,8 @@ export class LionwebNode extends NodeAdapter {
164164

165165
getAttributes(): { [p: string]: any } {
166166
const attributes = {};
167-
for (const p in this.nodeDefinition.properties) {
168-
if (!this.nodeDefinition.properties[p].child) {
167+
for (const p in this.nodeDefinition.features) {
168+
if (!this.nodeDefinition.features[p].child) {
169169
attributes[p] = this.getAttributeValue(p);
170170
}
171171
}

src/model/model.ts

+24-24
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ export const NODE_TYPES: { [name: string]: PackageDescription } = {
1515
export type NodeDefinition = {
1616
package?: string,
1717
name?: string,
18-
properties: { [name: string | symbol]: PropertyDefinition },
18+
features: { [name: string | symbol]: PropertyDefinition },
1919
resolved?: boolean;
2020
};
2121

@@ -35,29 +35,29 @@ export function getNodeDefinition(node: Node | (new (...args: any[]) => Node)):
3535
if(Object.prototype.hasOwnProperty.call(target, NODE_DEFINITION_SYMBOL)) {
3636
definition = target[NODE_DEFINITION_SYMBOL] as NodeDefinition;
3737
} else {
38-
const inheritedProperties = {...(target[NODE_DEFINITION_SYMBOL]?.properties || {})};
39-
for (const p in inheritedProperties) {
40-
inheritedProperties[p] = { inherited: true, ...inheritedProperties[p] };
38+
const inheritedFeatures = {...(target[NODE_DEFINITION_SYMBOL]?.features || {})};
39+
for (const p in inheritedFeatures) {
40+
inheritedFeatures[p] = { inherited: true, ...inheritedFeatures[p] };
4141
}
4242
target[NODE_DEFINITION_SYMBOL] = definition = {
43-
properties: inheritedProperties,
43+
features: inheritedFeatures,
4444
resolved: false
4545
};
4646
}
47-
if(definition && definition.properties && !definition.resolved) {
47+
if(definition && definition.features && !definition.resolved) {
4848
try {
4949
let metadataHolder;
5050
try {
5151
metadataHolder = new (node as any)();
5252
} catch (_) {
5353
metadataHolder = node;
5454
}
55-
for(const p in definition.properties) {
56-
if (!definition.properties[p].type) {
55+
for(const p in definition.features) {
56+
if (!definition.features[p].type) {
5757
const type = Reflect.getMetadata("design:type", metadataHolder, p);
58-
definition.properties[p].type = type;
58+
definition.features[p].type = type;
5959
if(type === Array) {
60-
definition.properties[p].arrayType =
60+
definition.features[p].arrayType =
6161
Reflect.getMetadata("design:arrayElementType", metadataHolder, p);
6262
}
6363
}
@@ -152,16 +152,16 @@ export abstract class Node extends Origin implements Destination {
152152
}
153153

154154
getChildNames(): string[] {
155-
const props = this.nodeDefinition?.properties || {};
155+
const props = this.nodeDefinition?.features || {};
156156
return Object.getOwnPropertyNames(props).filter(p => props[p].child);
157157
}
158158

159159
get nodeDefinition(): NodeDefinition {
160160
return getNodeDefinition(this);
161161
}
162162

163-
get properties(): PropertyDescription[] {
164-
const props = this.nodeDefinition?.properties || {};
163+
get features(): FeatureDescription[] {
164+
const props = this.nodeDefinition?.features || {};
165165
return Object.getOwnPropertyNames(props).map(p => {
166166
const value = props[p].child ?
167167
(props[p].multiple ? this.getChildren(p) : this.getChild(p)) :
@@ -171,7 +171,7 @@ export abstract class Node extends Origin implements Destination {
171171
}
172172

173173
containment(name: string | symbol): PropertyDefinition | undefined {
174-
const props = this.nodeDefinition?.properties || {};
174+
const props = this.nodeDefinition?.features || {};
175175
return props[name]?.child ? props[name] : undefined;
176176
}
177177

@@ -241,7 +241,7 @@ export abstract class Node extends Origin implements Destination {
241241
}
242242

243243
getAllChildren() {
244-
const props = this.nodeDefinition?.properties || {};
244+
const props = this.nodeDefinition?.features || {};
245245
const result: Node[] = [];
246246
for (const p in props) {
247247
const prop = props[p];
@@ -273,7 +273,7 @@ export abstract class Node extends Origin implements Destination {
273273
}
274274

275275
getAttributeValue(name: string | symbol): any {
276-
const props = this.nodeDefinition?.properties || {};
276+
const props = this.nodeDefinition?.features || {};
277277
const prop = props[name];
278278
if(prop) {
279279
if (prop.child) {
@@ -291,7 +291,7 @@ export abstract class Node extends Origin implements Destination {
291291
}
292292

293293
setAttributeValue(name: string | symbol, value: any) {
294-
const props = this.nodeDefinition?.properties || {};
294+
const props = this.nodeDefinition?.features || {};
295295
const prop = props[name];
296296
if(prop) {
297297
if (prop.child) {
@@ -339,7 +339,7 @@ export abstract class Node extends Origin implements Destination {
339339
}
340340
}
341341

342-
export interface PropertyDescription {
342+
export interface FeatureDescription {
343343
name: string;
344344
value: any;
345345
}
@@ -414,11 +414,11 @@ export function registerNodeDefinition<T extends Node>(
414414
def = {
415415
package: pkg,
416416
name: name,
417-
properties: {}
417+
features: {}
418418
};
419419
if(existingDef) {
420-
for(const prop in existingDef.properties) {
421-
def.properties[prop] = { inherited: true, ...existingDef.properties[prop]};
420+
for(const prop in existingDef.features) {
421+
def.features[prop] = { inherited: true, ...existingDef.features[prop]};
422422
}
423423
}
424424
}
@@ -450,12 +450,12 @@ export function registerNodeAttribute<T extends Node>(
450450
methodName = Symbol(methodName);
451451
}
452452
const definition = ensureNodeDefinition(type);
453-
if (!definition.properties[methodName]) {
454-
definition.properties[methodName] = {
453+
if (!definition.features[methodName]) {
454+
definition.features[methodName] = {
455455
name: methodName
456456
};
457457
}
458-
return definition.properties[methodName];
458+
return definition.features[methodName];
459459
}
460460

461461
export function registerNodeChild<T extends Node>(

src/testing/testing.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ export function assertASTsAreEqual(
1212
if (considerPosition) {
1313
expect(actual.position, `${context}.position`).to.eql(expected.position);
1414
}
15-
expected.properties.forEach(expectedProperty => {
16-
const actualPropValue = actual.properties.find(p => p.name == expectedProperty.name)!.value;
15+
expected.features.forEach(expectedProperty => {
16+
const actualPropValue = actual.features.find(p => p.name == expectedProperty.name)!.value;
1717
const expectedPropValue = expectedProperty.value;
1818

1919
if (actualPropValue instanceof Node && expectedPropValue instanceof Node) {

src/trace/trace-node.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export abstract class NodeAdapter extends Node {
2323

2424
getRole(): string | symbol | undefined {
2525
if (this.parent) { // Inefficient default implementation, searching in the parent's children
26-
const props = this.parent.nodeDefinition?.properties || {};
26+
const props = this.parent.nodeDefinition?.features || {};
2727
for (const p in props) {
2828
const prop = props[p];
2929
if (prop.child) {

src/transformation/transformation.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export class PropertyRef<Obj, Value> {
2323
}
2424

2525
if (nodeDefinition) {
26-
const property = Object.keys(nodeDefinition.properties).find(p => p == name);
26+
const property = Object.keys(nodeDefinition.features).find(p => p == name);
2727
if (!property) {
2828
throw new Error(`${name} is not a feature of ${nodeDefinition}`)
2929
}
@@ -286,7 +286,7 @@ export class ASTTransformer {
286286
if (prefix) {
287287
prefix += "#";
288288
}
289-
const properties = nodeDefinition ? Object.keys(nodeDefinition.properties) : Object.keys(node);
289+
const properties = nodeDefinition ? Object.keys(nodeDefinition.features) : Object.keys(node);
290290
properties.forEach(propertyName => {
291291
const childNodeFactory = factory.getChildNodeFactory(prefix, propertyName);
292292
if (childNodeFactory) {

src/traversing/structurally.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ Node.prototype.walkDescendants = function(walker: typeof walk = walk) {
9393
* @return all direct children of this node.
9494
*/
9595
export function* walkChildren(node: Node): Generator<Node> {
96-
for (const property of node.properties) {
96+
for (const property of node.features) {
9797
const value = property.value;
9898
if (value instanceof Node) {
9999
yield value;

tests/ecore.test.ts

+14-12
Original file line numberDiff line numberDiff line change
@@ -184,20 +184,20 @@ describe("Import/export", function () {
184184

185185
const PlistParameter = ePackages[1].eContents().find(x => x.get("name") == "PlistParameter");
186186
let eo = PlistParameter.create({});
187-
let properties = new ECoreNode(eo).getProperties();
187+
let properties = new ECoreNode(eo).getFeatures();
188188
expect(properties).to.eql({
189-
"name": {"child": true, "multiple": false, "name": "name"},
190-
"sourceField": {"child": true, "multiple": false, "name": "sourceField"},
191-
"targetField": {"child": true, "multiple": false, "name": "targetField"},
192-
"type": {"child": true, "multiple": false, "name": "type"}
189+
"name": {"child": true, "multiple": false, "name": "name", "reference": false},
190+
"sourceField": {"child": true, "multiple": false, "name": "sourceField", "reference": false},
191+
"targetField": {"child": true, "multiple": false, "name": "targetField", "reference": false},
192+
"type": {"child": true, "multiple": false, "name": "type", "reference": false}
193193
});
194194

195195
const InvokeSubroutineStatement = ePackages[1].eContents().find(x => x.get("name") == "InvokeSubroutineStatement");
196196
eo = InvokeSubroutineStatement.create({});
197-
properties = new ECoreNode(eo).getProperties();
197+
properties = new ECoreNode(eo).getFeatures();
198198
expect(properties).to.eql({
199-
"conditionalIndicator": {"child": true, "multiple": false, "name": "conditionalIndicator"},
200-
"subroutine": {"child": false, "multiple": false, "name": "subroutine"}
199+
"conditionalIndicator": {"child": true, "multiple": false, "name": "conditionalIndicator", "reference": false},
200+
"subroutine": {"child": false, "multiple": false, "name": "subroutine", "reference": true}
201201
});
202202
});
203203
it("containments and references - SAS", function () {
@@ -214,13 +214,15 @@ describe("Import/export", function () {
214214
const vd = VariableDeclaration.create({});
215215
sf.get("statementsAndDeclarations").add(vd);
216216
const eCoreNode = new ECoreNode(vd);
217-
const properties = eCoreNode.getProperties();
217+
const properties = eCoreNode.getFeatures();
218218
expect(properties).to.eql({
219-
"name": {"child": false, "multiple": undefined, "name": "name"},
220-
"expression": {"child": true, "multiple": false, "name": "expression"},
219+
"name": {"child": false, "multiple": undefined, "name": "name", "reference": false},
220+
"expression": {"child": true, "multiple": false, "name": "expression", "reference": false},
221221
});
222222
expect(eCoreNode.getRole()).to.equal("statementsAndDeclarations");
223-
expect(eCoreNode.parent!.containment("statementsAndDeclarations")).to.eql({"child": true, "multiple": true, "name": "statementsAndDeclarations"});
223+
expect(eCoreNode.parent!.containment("statementsAndDeclarations")).to.eql({
224+
"child": true, "multiple": true, "name": "statementsAndDeclarations", "reference": false
225+
});
224226
});
225227
it("importing using raw Ecore.js",
226228
function () {

tests/interop/parser-trace.test.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,9 @@ describe('Parser traces – Kolasu metamodel V1', function() {
5454
expect(rootNode.getType()).to.eql("com.strumenta.rpgparser.model.CompilationUnit");
5555
expect(rootNode.getSimpleType()).to.eql("CompilationUnit");
5656
const child = rootNode.getChildren("dataDefinitions")[3];
57-
expect(child.nodeDefinition.properties).to.eql({
58-
name: { name: "name", child: false, multiple: undefined },
59-
keywords: { name: "keywords", child: true, multiple: true }
57+
expect(child.nodeDefinition.features).to.eql({
58+
name: { name: "name", child: false, multiple: undefined, reference: false },
59+
keywords: { name: "keywords", child: true, multiple: true, reference: false }
6060
});
6161
expect(child.getAttributes()).to.eql({ name: 'ENDPOINT' });
6262
});

tests/interop/workspace-transpilation-trace.test.ts

+2
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,8 @@ describe('Workspace Transpilation traces', function() {
116116
first()).first as TraceNode;
117117
expect(refExpr).not.to.be.undefined;
118118
expect(refExpr.getPathFromRoot()).to.eql(["mainStatements", 0, "expression", "target"]);
119+
const refDef = refExpr.nodeDefinition.features["dataDefinition"];
120+
expect(refDef?.reference).to.be.true;
119121
const reference = refExpr.getReference("dataDefinition");
120122
expect(reference).to.be.instanceof(ReferenceByName);
121123
expect(reference?.name).to.equal("CNT");

tests/metamodel.test.ts

+7-7
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,17 @@ describe('Meta model', function () {
2121
expect(def).not.to.be.undefined;
2222
expect(def!.package).to.equal("some.package");
2323
expect(def!.name).to.equal("SomeNodeInPackage");
24-
expect(def!.properties["nonExistent"]).to.be.undefined;
25-
expect(def!.properties["someNode"]).not.to.be.undefined;
26-
expect(def!.properties["someNode"].multiple).to.be.false;
27-
expect(def!.properties["multi"]).not.to.be.undefined;
28-
expect(def!.properties["multi"].multiple).to.be.true;
24+
expect(def!.features["nonExistent"]).to.be.undefined;
25+
expect(def!.features["someNode"]).not.to.be.undefined;
26+
expect(def!.features["someNode"].multiple).to.be.false;
27+
expect(def!.features["multi"]).not.to.be.undefined;
28+
expect(def!.features["multi"].multiple).to.be.true;
2929
});
3030
it("records references",
3131
function () {
3232
const def = getNodeDefinition(SomeNodeWithReferences);
3333
expect(def).not.to.be.undefined;
34-
expect(def!.properties["a"].reference).to.be.undefined;
35-
expect(def!.properties["ref"].reference).to.be.true;
34+
expect(def!.features["a"].reference).to.be.undefined;
35+
expect(def!.features["ref"].reference).to.be.true;
3636
});
3737
});

0 commit comments

Comments
 (0)