diff --git a/CHANGELOG.md b/CHANGELOG.md index 02c82a1..98e722a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,14 @@ All notable changes to this project from version 1.2.0 upwards are documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). +## [1.6.17] – 2024-03-21 + +### Changed +- TraceNode.get renamed to getDescendant + +### Fixed +- TraceNode.get to access nodes by path + ## [1.6.16] – 2024-03-21 ### Changed diff --git a/package.json b/package.json index 0f19926..269c855 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "description": "AST building blocks for TypeScript/JavaScript, part of the *lasu family, with optional integrations with ANTLR4 and Ecore.", "author": "Strumenta s.r.l.", "publisher": "strumenta", - "version": "1.6.16", + "version": "1.6.17", "license": "Apache-2.0", "keywords": [ "antlr", diff --git a/src/interop/ecore.ts b/src/interop/ecore.ts index fe06ad1..da5f07e 100644 --- a/src/interop/ecore.ts +++ b/src/interop/ecore.ts @@ -471,7 +471,7 @@ export class ${className} extends ${superClassName} {`; defineProperty(classDef, name); const prop = registerNodeAttribute(classDef as any, name); prop.child = a.isTypeOf('EReference'); - const annotation = prop.child ? (prop.multiple ? "@Children()" : "@Child()") : "@Property()" + const annotation = prop.child ? (prop.multiple ? "@Children()" : "@Child()") : "@Attribute()" classDef[SYMBOL_CLASS_DEFINITION] += `\n\t${annotation}\n\t${name};`; }); classDef[SYMBOL_CLASS_DEFINITION] += "\n}"; diff --git a/src/model/model.ts b/src/model/model.ts index e124edf..5e6d458 100644 --- a/src/model/model.ts +++ b/src/model/model.ts @@ -267,8 +267,10 @@ export abstract class Node extends Origin implements Destination { const raw = this.doGetChildOrChildren(name); if (containment.multiple) { return (raw as Node[]) || []; + } else if (raw) { + return [raw as Node]; } else { - throw new Error(name.toString() + " is not a collection"); + return []; } } diff --git a/src/trace/trace-node.ts b/src/trace/trace-node.ts index 2d331f1..5187431 100644 --- a/src/trace/trace-node.ts +++ b/src/trace/trace-node.ts @@ -209,7 +209,7 @@ export class TraceNode extends Node implements PossiblyNamed { refTarget = refTarget.parent; } if (tempParent) { - const newParent = this.getRoot().get(tempParent.getPathFromRoot()); + const newParent = this.getRoot().getDescendant(tempParent.getPathFromRoot()); if (newParent instanceof Node) { return newParent; } @@ -283,20 +283,19 @@ export class TraceNode extends Node implements PossiblyNamed { return this.nodeAdapter.isStatement(); } - get(path: (string | number)[]) { + getDescendant(path: (string | number)[]) { // eslint-disable-next-line @typescript-eslint/no-this-alias - let node: Node | Node[] | undefined = this; - for (const elem of path) { - if (typeof elem == "string") { - if (node instanceof Node) { - node = node.getChild(elem); - } else { - throw new Error("Invalid path at " + elem + ", expected node, got " + node); - } - } else if (Array.isArray(node)) { - node = node[elem]; + let node: Node | undefined = this; + for (let i = 0; i < path.length; i++) { + const elem = path[i]; + if (typeof elem !== "string") { + throw new Error("Invalid path at " + elem + ", expected node, got " + node); + } + if (i < path.length - 1 && typeof path[i + 1] === "number") { + node = node?.getChild(elem, path[i + 1] as number); + i++; } else { - throw new Error("Invalid path at " + elem + ", expected children, got " + node); + node = node?.getChild(elem); } } return node; diff --git a/tests/ecore.test.ts b/tests/ecore.test.ts index 05b81da..3772e93 100644 --- a/tests/ecore.test.ts +++ b/tests/ecore.test.ts @@ -257,7 +257,7 @@ export class CompilationUnit extends Node { expect(NODE_TYPES["SimpleMM"].nodes["StringLiteral"][SYMBOL_CLASS_DEFINITION]).to.equal( `@ASTNode("SimpleMM", "StringLiteral") export class StringLiteral extends Expression { -\t@Property() +\t@Attribute() \tvalue; }`); node = new NODE_TYPES["SimpleMM"].nodes["StringLiteral"]() as any; diff --git a/tests/interop/lionweb.test.ts b/tests/interop/lionweb.test.ts index a9be909..ee88981 100644 --- a/tests/interop/lionweb.test.ts +++ b/tests/interop/lionweb.test.ts @@ -2,7 +2,7 @@ import FS_LANGUAGE_JSON from "./fs-language.json"; import FS_MODEL from "./fs-model.json"; import {expect} from "chai"; import {deserializeChunk, deserializeLanguages, SerializationChunk} from "@lionweb/core"; -import {Children, Node, Property, TraceNode, walk} from "../../src"; +import {Attribute, Children, Node, TraceNode, walk} from "../../src"; import { findClassifier, LanguageMapping, LionwebNode, @@ -13,7 +13,7 @@ import {map, pipe, reduce} from "iter-ops"; import {STARLASU_LANGUAGE} from "../../src/interop/lionweb-starlasu-language"; abstract class File extends Node { - @Property() + @Attribute() name: string; } @@ -23,7 +23,7 @@ class Directory extends File { } class TextFile extends File { - @Property() + @Attribute() contents: string; } diff --git a/tests/interop/workspace-transpilation-trace.test.ts b/tests/interop/workspace-transpilation-trace.test.ts index 80434bd..a76c0a3 100644 --- a/tests/interop/workspace-transpilation-trace.test.ts +++ b/tests/interop/workspace-transpilation-trace.test.ts @@ -111,17 +111,17 @@ describe('Workspace Transpilation traces', function() { expect(sourceRoot.getSimpleType()).to.eql("CompilationUnit") expect(sourceRoot.getPosition()).to.eql(new Position(new Point(1, 0), new Point(82, 18))) - const refExpr = pipe(sourceRoot.walkDescendants(), + let refExpr = pipe(sourceRoot.walkDescendants(), filter((node: TraceNode) => node.getType() == "com.strumenta.rpgparser.model.ReferenceExpr"), first()).first as TraceNode; expect(refExpr).not.to.be.undefined; expect(refExpr.getPathFromRoot()).to.eql(["mainStatements", 0, "expression", "target"]); - const refDef = refExpr.nodeDefinition.features["dataDefinition"]; + let refDef = refExpr.nodeDefinition.features["dataDefinition"]; expect(refDef?.reference).to.be.true; - const reference = refExpr.getReference("dataDefinition"); + let reference = refExpr.getReference("dataDefinition"); expect(reference).to.be.instanceof(ReferenceByName); expect(reference?.name).to.equal("CNT"); - const refTarget = reference?.referred; + let refTarget = reference?.referred; expect(refTarget).to.be.instanceof(TraceNode); expect(refTarget?.getType()).to.equal("com.strumenta.rpgparser.model.StandaloneField"); expect(refTarget?.name).to.equal("CNT"); @@ -130,6 +130,21 @@ describe('Workspace Transpilation traces', function() { expect(refTarget?.getRoot()).to.equal(sourceRoot); expect(refExpr.getAttributes()["dataDefinition"]).to.be.undefined; + refExpr = sourceRoot.getDescendant(["mainStatements", 4, "name"]) as TraceNode; + refDef = refExpr.nodeDefinition.features["dataDefinition"]; + expect(refDef?.reference).to.be.true; + reference = refExpr.getReference("dataDefinition"); + expect(reference).to.be.instanceof(ReferenceByName); + expect(reference?.name).to.equal("CUSTOMER"); + refTarget = reference?.referred; + expect(refTarget).to.be.instanceof(TraceNode); + expect(refTarget?.getType()).to.equal("com.strumenta.rpgparser.model.ExternalFileDefinition"); + expect(refTarget?.name).to.equal("CUSTOMER"); + expect(refTarget?.getRole()).to.equal("file"); + expect(refTarget?.getPathFromRoot()).to.eql(["externalDefinitions", 17, "record", "file"]); + expect(refTarget?.getRoot()).to.equal(sourceRoot); + expect(refExpr.getAttributes()["dataDefinition"]).to.be.undefined; + // TODO broken expect(cus300File.node.getChildren("dataDefinition").length).to.eql(4) expect(sourceRoot.getChildren("mainStatements").length).to.eql(9) const firstStatement = sourceRoot.getChildren("mainStatements")[0]; diff --git a/tests/testing/testing.test.ts b/tests/testing/testing.test.ts index 159f737..084876e 100644 --- a/tests/testing/testing.test.ts +++ b/tests/testing/testing.test.ts @@ -1,6 +1,6 @@ import {expect} from "chai"; import {assertASTsAreEqual} from "../../src/testing/testing"; -import {ASTNode, Children, Node, Point, Position, PossiblyNamed, Property} from "../../src"; +import {ASTNode, Attribute, Children, Node, Point, Position, PossiblyNamed} from "../../src"; describe('AssertASTsAreEqual', function() { it("the very same node instance compared with itself must pass", function () { @@ -99,7 +99,7 @@ describe('AssertASTsAreEqual', function() { @ASTNode("", "SimpleNode") class SimpleNode extends Node implements PossiblyNamed { - @Property() public name?: string; + @Attribute() public name?: string; @Children() public subTree: Node[]; constructor(name?: string, subTree: Node[] = []) { super(); @@ -110,7 +110,7 @@ class SimpleNode extends Node implements PossiblyNamed { @ASTNode("", "AnotherSimpleNode") class AnotherSimpleNode extends Node implements PossiblyNamed { - @Property() public name?: string; + @Attribute() public name?: string; @Children() public subTree: Node[]; constructor(name?: string, subTree: Node[] = []) { super(); @@ -121,7 +121,7 @@ class AnotherSimpleNode extends Node implements PossiblyNamed { @ASTNode("", "NodeWithStringSubTree") class NodeWithStringSubTree extends Node implements PossiblyNamed { - @Property() public name?: string; + @Attribute() public name?: string; @Children() public subTree?: string; constructor(name?: string, subTree?: string) { super(); diff --git a/tests/transformation/transformation.test.ts b/tests/transformation/transformation.test.ts index fb8420a..5c3d085 100644 --- a/tests/transformation/transformation.test.ts +++ b/tests/transformation/transformation.test.ts @@ -2,13 +2,12 @@ import {expect} from "chai"; import { ASTNode, - ASTTransformer, + ASTTransformer, Attribute, Child, ErrorNode, GenericErrorNode, IssueSeverity, Node, pos, - Property, } from "../../src"; @ASTNode("", "A") @@ -31,7 +30,7 @@ class A extends Node { class B extends Node { @Child() aChild: Node; - @Property() + @Attribute() property: number; other2: string; notThere: any;