Skip to content

Commit 9d7ae9d

Browse files
committed
Information about references in node metadata, bump version
1 parent 9dd9b2e commit 9d7ae9d

10 files changed

+106
-31
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.15] – 2024-03-21
6+
7+
### Added
8+
- Information about references in node metadata
9+
510
## [1.6.14] – 2024-03-21
611

712
### Fixed

jest.config.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ const config = {
8888
],
8989
globals: {
9090
"ts-jest": {
91-
tsConfig: `tests/tsconfig.json`
91+
tsconfig: `tests/tsconfig.json`
9292
}
9393
}
9494
};

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.14",
6+
"version": "1.6.15",
77
"license": "Apache-2.0",
88
"keywords": [
99
"antlr",

src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,5 @@ export * from "./traversing/by-position";
1313
export * from "./transformation/transformation";
1414
export * from "./validation";
1515
export * from "./interop/json";
16+
export * from "./interop/indexing";
1617
export * from "./trace/trace-node";

src/interop/ecore.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {
1111
PackageDescription,
1212
PropertyDefinition,
1313
registerNodeDefinition,
14-
registerNodeProperty
14+
registerNodeAttribute
1515
} from "../model/model";
1616
import ECore from "ecore/dist/ecore";
1717
import {Point, Position} from "../model/position";
@@ -469,7 +469,7 @@ export class ${className} extends ${superClassName} {`;
469469
eClass.get("eStructuralFeatures").each(a => {
470470
const name = a.get("name");
471471
defineProperty(classDef, name);
472-
const prop = registerNodeProperty(classDef as any, name);
472+
const prop = registerNodeAttribute(classDef as any, name);
473473
prop.child = a.isTypeOf('EReference');
474474
const annotation = prop.child ? (prop.multiple ? "@Children()" : "@Child()") : "@Property()"
475475
classDef[SYMBOL_CLASS_DEFINITION] += `\n\t${annotation}\n\t${name};`;

src/model/model.ts

+46-9
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import {Position} from "./position";
22
import "reflect-metadata";
3-
import {ReferenceByName} from "./naming";
3+
import {PossiblyNamed, ReferenceByName} from "./naming";
44

55
export const NODE_DEFINITION_SYMBOL = Symbol("nodeDefinition");
66

@@ -24,6 +24,7 @@ export type PropertyDefinition = {
2424
child?: boolean,
2525
multiple?: boolean,
2626
inherited?: boolean,
27+
reference?: boolean,
2728
type?: any,
2829
arrayType?: any
2930
}
@@ -163,8 +164,8 @@ export abstract class Node extends Origin implements Destination {
163164
const props = this.nodeDefinition?.properties || {};
164165
return Object.getOwnPropertyNames(props).map(p => {
165166
const value = props[p].child ?
166-
(props[p].multiple ? this.getChildren(p) : this.getChild(p)) :
167-
this.getAttributeValue(p);
167+
(props[p].multiple ? this.getChildren(p) : this.getChild(p)) :
168+
(props[p].reference ? this.getReference(p) : this.getAttributeValue(p));
168169
return { name: p, value };
169170
});
170171
}
@@ -278,7 +279,11 @@ export abstract class Node extends Origin implements Destination {
278279
if (prop.child) {
279280
throw new Error(name.toString() + " is a containment, please use getChild");
280281
} else {
281-
return this.doGetAttributeValue(name);
282+
const attributeValue = this.doGetAttributeValue(name);
283+
if (attributeValue instanceof ReferenceByName) {
284+
throw new Error(name.toString() + " is a reference, please use getReference");
285+
}
286+
return attributeValue;
282287
}
283288
} else {
284289
throw new Error(`${name.toString()} is not a feature of ${this} (${this.nodeDefinition}).`);
@@ -308,7 +313,11 @@ export abstract class Node extends Origin implements Destination {
308313
}
309314

310315
getReference(name: string | symbol): ReferenceByName<any> | undefined {
311-
return this[name] as ReferenceByName<any>;
316+
return this.doGetReference(name) as ReferenceByName<any>;
317+
}
318+
319+
protected doGetReference(name: string | symbol) {
320+
return this[name];
312321
}
313322

314323
withParent(parent?: Node): this {
@@ -434,7 +443,7 @@ export function ensureNodeDefinition(node: Node | { new (...args: any[]): Node }
434443
return definition;
435444
}
436445

437-
export function registerNodeProperty<T extends Node>(
446+
export function registerNodeAttribute<T extends Node>(
438447
type: { new(...args: any[]): T }, methodName: string | symbol
439448
): PropertyDefinition {
440449
if (methodName == "parent" || methodName == "children" || methodName == "origin") {
@@ -451,12 +460,19 @@ export function registerNodeProperty<T extends Node>(
451460

452461
export function registerNodeChild<T extends Node>(
453462
type: new (...args: any[]) => T, methodName: string, multiple: boolean = false): PropertyDefinition {
454-
const propInfo = registerNodeProperty(type, methodName);
463+
const propInfo = registerNodeAttribute(type, methodName);
455464
propInfo.child = true;
456465
propInfo.multiple = multiple;
457466
return propInfo;
458467
}
459468

469+
export function registerNodeReference<T extends Node & PossiblyNamed>(
470+
type: new (...args: any[]) => T, methodName: string): PropertyDefinition {
471+
const propInfo = registerNodeAttribute(type, methodName);
472+
propInfo.reference = true;
473+
return propInfo;
474+
}
475+
460476
//------------//
461477
// Decorators //
462478
//------------//
@@ -485,9 +501,30 @@ export function Children(): (target, methodName: string) => void {
485501
};
486502
}
487503

488-
504+
/**
505+
* Declares the decorated property as an attribute.
506+
* @deprecated use Attribute instead.
507+
*/
489508
export function Property(): (target, methodName: string) => void {
490509
return function (target, methodName: string) {
491-
registerNodeProperty(target, methodName);
510+
registerNodeAttribute(target, methodName);
511+
};
512+
}
513+
514+
/**
515+
* Declares the decorated property as an attribute.
516+
*/
517+
export function Attribute(): (target, methodName: string) => void {
518+
return function (target, methodName: string) {
519+
registerNodeAttribute(target, methodName);
520+
};
521+
}
522+
523+
/**
524+
* Declares the decorated property as a reference.
525+
*/
526+
export function Reference(): (target, methodName: string) => void {
527+
return function (target, methodName: string) {
528+
registerNodeReference(target, methodName);
492529
};
493530
}

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

+1
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ describe('Workspace Transpilation traces', function() {
126126
expect(refTarget?.getRole()).to.equal("dataDefinitions");
127127
expect(refTarget?.getPathFromRoot()).to.eql(["dataDefinitions", 3]);
128128
expect(refTarget?.getRoot()).to.equal(sourceRoot);
129+
expect(refExpr.getAttributes()["dataDefinition"]).to.be.undefined;
129130

130131
// TODO broken expect(cus300File.node.getChildren("dataDefinition").length).to.eql(4)
131132
expect(sourceRoot.getChildren("mainStatements").length).to.eql(9)

tests/json.test.ts

+6-10
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import {expect} from "chai";
22

3-
import {ASTNode, Child, Children, GenericNode, Node, PossiblyNamed, Property, ReferenceByName} from "../src";
4-
import {JSONGenerator} from "../src";
5-
import {Indexer} from "../src/interop/indexing";
3+
import {ASTNode, Child, Children, GenericNode, Node, PossiblyNamed, Reference, ReferenceByName} from "../src";
4+
import {Indexer, JSONGenerator} from "../src";
65

76
describe('JSON generator', function() {
87
it("Empty AST",
@@ -199,21 +198,18 @@ class DummyNamedNode extends Node implements PossiblyNamed {
199198

200199
@ASTNode("", "NodeWithReference")
201200
class NodeWithReference extends Node implements PossiblyNamed {
202-
constructor(
203-
public name?: string,
204-
public reference?: ReferenceByName<DummyNamedNode>) {
201+
constructor(public name?: string, public reference?: ReferenceByName<DummyNamedNode>) {
205202
super();
206203
}
204+
207205
@Child() namedNode?: DummyNamedNode;
208206
}
209207

210208
@ASTNode("", "NodeWithSelfReference")
211209
class NodeWithSelfReference extends Node implements PossiblyNamed {
212-
@Property() public reference?: ReferenceByName<NodeWithSelfReference>;
210+
@Reference() public reference?: ReferenceByName<NodeWithSelfReference>;
213211

214-
constructor(
215-
public name?: string,
216-
reference?: ReferenceByName<NodeWithSelfReference>) {
212+
constructor(public name?: string, reference?: ReferenceByName<NodeWithSelfReference>) {
217213
super();
218214
this.reference = reference;
219215
}

tests/metamodel.test.ts

+9-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import {expect} from "chai";
22

33
import {getNodeDefinition, NODE_TYPES} from "../src";
4-
import {SomeNode, SomeNodeInPackage} from "./nodes";
4+
import {SomeNode, SomeNodeInPackage, SomeNodeWithReferences} from "./nodes";
55

6-
describe('Meta model', function() {
6+
describe('Meta model', function () {
77
it("info recorded in the default package",
88
function () {
99
expect(NODE_TYPES[""]).not.to.be.undefined;
@@ -27,4 +27,11 @@ describe('Meta model', function() {
2727
expect(def!.properties["multi"]).not.to.be.undefined;
2828
expect(def!.properties["multi"].multiple).to.be.true;
2929
});
30+
it("records references",
31+
function () {
32+
const def = getNodeDefinition(SomeNodeWithReferences);
33+
expect(def).not.to.be.undefined;
34+
expect(def!.properties["a"].reference).to.be.undefined;
35+
expect(def!.properties["ref"].reference).to.be.true;
36+
});
3037
});

tests/nodes.ts

+34-6
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,15 @@
1-
import {ASTNode, Child, Children, ErrorNode, Node, Position, Property} from "../src";
1+
import {
2+
ASTNode,
3+
Attribute,
4+
Child,
5+
Children,
6+
ErrorNode,
7+
Node,
8+
Position,
9+
PossiblyNamed,
10+
Reference,
11+
ReferenceByName
12+
} from "../src";
213

314
export class Box extends Node {
415
@Children()
@@ -25,21 +36,25 @@ export enum Fibo {
2536
}
2637

2738
@ASTNode("", "SomeNode")
28-
export class SomeNode extends Node {
29-
@Property()
39+
export class SomeNode extends Node implements PossiblyNamed {
40+
@Attribute()
3041
a?: string;
31-
@Property()
42+
@Attribute()
3243
fib: Fibo
3344

3445
constructor(a?: string, positionOverride?: Position) {
3546
super(positionOverride);
3647
this.a = a;
3748
}
49+
50+
get name(): string | undefined {
51+
return this.a;
52+
}
3853
}
3954

4055
@ASTNode("some.package", "SomeNodeInPackage")
4156
export class SomeNodeInPackage extends Node {
42-
@Property()
57+
@Attribute()
4358
a?: string;
4459
@Child()
4560
someNode: SomeNode;
@@ -54,7 +69,7 @@ export class SomeNodeInPackage extends Node {
5469

5570
@ASTNode("some.package", "NodeSubclass")
5671
export class NodeSubclass extends SomeNodeInPackage {
57-
@Property()
72+
@Attribute()
5873
b: string;
5974
@Child()
6075
anotherChild: SomeNodeInPackage;
@@ -68,3 +83,16 @@ export class NodeWithError extends SomeNodeInPackage {
6883

6984
@ASTNode("another.package", "SomeNodeInPackage")
7085
export class SomeNodeInAnotherPackage extends Node {}
86+
87+
@ASTNode("", "SomeNodeWithReferences")
88+
export class SomeNodeWithReferences extends Node {
89+
@Attribute()
90+
a?: string;
91+
@Reference()
92+
ref: ReferenceByName<SomeNode> = new ReferenceByName<SomeNode>("a")
93+
94+
constructor(a?: string, positionOverride?: Position) {
95+
super(positionOverride);
96+
this.a = a;
97+
}
98+
}

0 commit comments

Comments
 (0)