diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e3fb81..cd43409 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ 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.24] – Not yet released + +### Fixed +- `assertASTsAreEqual` is now properly exported + ## [1.6.23] – 2024-04-24 ### Fixed diff --git a/src/interop/lionweb.ts b/src/interop/lionweb.ts index 2dba692..9f1c153 100644 --- a/src/interop/lionweb.ts +++ b/src/interop/lionweb.ts @@ -5,15 +5,16 @@ import { Classifier, Concept, - Containment, + Containment, deserializeChunk, Feature as LWFeature, Id, InstantiationFacade, Interface, Language, - Node as LWNodeInterface + Node as LWNodeInterface, + SerializationChunk } from "@lionweb/core"; -import {NodeAdapter, Issue, Node, NodeDefinition, Position, Feature, Point, pos} from ".."; +import {NodeAdapter, Issue, Node, NodeDefinition, Position, Feature, Point, pos, TraceNode} from ".."; import {STARLASU_LANGUAGE} from "./lionweb-starlasu-language"; export {STARLASU_LANGUAGE} from "./lionweb-starlasu-language"; @@ -77,25 +78,37 @@ export class LanguageMapping { } } -function isASTNode(classifier: Classifier) { - return (classifier instanceof Concept) && - (classifier.key == ASTNode.key || (classifier.extends && isASTNode(classifier.extends))); +function isSpecialConcept(classifier: Classifier | null) { + return classifier?.key == PositionClassifier.key; } function importFeature(feature: LWFeature): Feature | undefined { const def: Feature = { name: feature.name }; if (feature instanceof Containment) { - if (feature.type && isASTNode(feature.type)) { + if (!isSpecialConcept(feature.type)) { def.child = true; def.multiple = feature.multiple; } else { - // TODO we assume that: - // 1) we're importing a StarLasu AST - // 2) containments in a StarLasu AST are either AST nodes or internal StarLasu objects like the position + // We don't import the containment because we handle it specially return undefined; } } - return def + return def; +} + +function importConcept(concept: NodeDefinition | undefined, classifier: Classifier) { + concept = { + name: classifier.name, + features: {}, + resolved: true + }; + allFeatures(classifier).forEach(f => { + const feature = importFeature(f); + if (feature) { + concept!.features[f.name] = feature; + } + }); + return concept; } export class TylasuInstantiationFacade implements InstantiationFacade { @@ -128,17 +141,7 @@ export class TylasuInstantiationFacade implements InstantiationFacade { - const feature = importFeature(f); - if (feature) { - concept!.features[f.name] = feature; - } - }); + concept = importConcept(concept, classifier); this.concepts.set(classifier, concept); } node = new LionwebNode(concept, { @@ -281,3 +284,25 @@ export class LionwebNode extends NodeAdapter { return other instanceof LionwebNode && other.lwnode == this.lwnode; } } + +export function deserializeToTylasuNodes( + chunk: SerializationChunk, + languages: Language[], + languageMappings: LanguageMapping[] = [STARLASU_LANGUAGE_MAPPING], + dependentNodes: LWNodeInterface[] = [] +): Node[] { + return deserializeChunk( + chunk, new TylasuInstantiationFacade(languageMappings), [STARLASU_LANGUAGE, ...languages], dependentNodes + ) + .filter(n => n instanceof TylasuNodeWrapper) + .map(n => (n as TylasuNodeWrapper).node); +} + +export function deserializeToTraceNodes( + chunk: SerializationChunk, + languages: Language[], + dependentNodes: LWNodeInterface[] = [] +): TraceNode[] { + return deserializeToTylasuNodes(chunk, languages, [], dependentNodes).map( + n => new TraceNode(n as LionwebNode)); +} diff --git a/tests/interop/fs-language.json b/tests/interop/fs-language.json index d7d05fc..bd28897 100644 --- a/tests/interop/fs-language.json +++ b/tests/interop/fs-language.json @@ -147,12 +147,7 @@ "version": "2023.1", "key": "Concept-extends" }, - "targets": [ - { - "resolveInfo": "ASTNode", - "reference": "com-strumenta-StarLasu-ASTNode-id" - } - ] + "targets": [] }, { "reference": { @@ -287,12 +282,7 @@ "version": "2023.1", "key": "Concept-extends" }, - "targets": [ - { - "resolveInfo": "ASTNode", - "reference": "com-strumenta-StarLasu-ASTNode-id" - } - ] + "targets": [] }, { "reference": { @@ -630,12 +620,7 @@ "version": "2023.1", "key": "Concept-extends" }, - "targets": [ - { - "resolveInfo": "ASTNode", - "reference": "com-strumenta-StarLasu-ASTNode-id" - } - ] + "targets": [] }, { "reference": { @@ -766,12 +751,7 @@ "version": "2023.1", "key": "Concept-extends" }, - "targets": [ - { - "resolveInfo": "ASTNode", - "reference": "com-strumenta-StarLasu-ASTNode-id" - } - ] + "targets": [] }, { "reference": { @@ -1096,12 +1076,7 @@ "version": "2023.1", "key": "Concept-extends" }, - "targets": [ - { - "resolveInfo": "ASTNode", - "reference": "com-strumenta-StarLasu-ASTNode-id" - } - ] + "targets": [] }, { "reference": { @@ -1224,12 +1199,7 @@ "version": "2023.1", "key": "Link-type" }, - "targets": [ - { - "resolveInfo": "ASTNode", - "reference": "com-strumenta-StarLasu-ASTNode-id" - } - ] + "targets": [] } ], "annotations": [], @@ -1357,12 +1327,7 @@ "version": "2023.1", "key": "Concept-extends" }, - "targets": [ - { - "resolveInfo": "ASTNode", - "reference": "com-strumenta-StarLasu-ASTNode-id" - } - ] + "targets": [] }, { "reference": { @@ -1604,12 +1569,7 @@ "version": "2023.1", "key": "Concept-extends" }, - "targets": [ - { - "resolveInfo": "ASTNode", - "reference": "com-strumenta-StarLasu-ASTNode-id" - } - ] + "targets": [] }, { "reference": { @@ -1943,12 +1903,7 @@ "version": "2023.1", "key": "Concept-extends" }, - "targets": [ - { - "resolveInfo": "ASTNode", - "reference": "com-strumenta-StarLasu-ASTNode-id" - } - ] + "targets": [] }, { "reference": { @@ -2083,12 +2038,7 @@ "version": "2023.1", "key": "Concept-extends" }, - "targets": [ - { - "resolveInfo": "ASTNode", - "reference": "com-strumenta-StarLasu-ASTNode-id" - } - ] + "targets": [] }, { "reference": { @@ -2275,12 +2225,7 @@ "version": "2023.1", "key": "Concept-extends" }, - "targets": [ - { - "resolveInfo": "ASTNode", - "reference": "com-strumenta-StarLasu-ASTNode-id" - } - ] + "targets": [] }, { "reference": { @@ -2467,12 +2412,7 @@ "version": "2023.1", "key": "Concept-extends" }, - "targets": [ - { - "resolveInfo": "ASTNode", - "reference": "com-strumenta-StarLasu-ASTNode-id" - } - ] + "targets": [] }, { "reference": { @@ -2667,12 +2607,7 @@ "version": "2023.1", "key": "Concept-extends" }, - "targets": [ - { - "resolveInfo": "ASTNode", - "reference": "com-strumenta-StarLasu-ASTNode-id" - } - ] + "targets": [] }, { "reference": { diff --git a/tests/interop/lionweb.test.ts b/tests/interop/lionweb.test.ts index af4e086..21e084c 100644 --- a/tests/interop/lionweb.test.ts +++ b/tests/interop/lionweb.test.ts @@ -6,6 +6,8 @@ import {expect} from "chai"; import {deserializeChunk, deserializeLanguages, SerializationChunk} from "@lionweb/core"; import {Attribute, Children, Node, TraceNode, walk} from "../../src"; import { + deserializeToTraceNodes, + deserializeToTylasuNodes, findClassifier, LanguageMapping, LionwebNode, STARLASU_LANGUAGE_MAPPING, @@ -44,12 +46,12 @@ describe('Lionweb integration', function() { it("can deserialize simple model", function () { - const nodes = deserializeChunk(FS_MODEL, new TylasuInstantiationFacade([FS_LANGUAGE_MAPPING]), [FS_LANGUAGE], []); + const nodes = deserializeToTylasuNodes(FS_MODEL, [FS_LANGUAGE], [FS_LANGUAGE_MAPPING]); expect(nodes).not.to.be.empty; expect(nodes.length).to.equal(1); - const root = nodes[0] as TylasuNodeWrapper; - expect(root.node).to.be.instanceof(Directory); - let dir = root.node as Directory; + const root = nodes[0]; + expect(root).to.be.instanceof(Directory); + let dir = root as Directory; expect(dir.name).to.equal("resources.zip"); expect(dir.files.length).to.equal(1); expect(dir.files[0]).to.be.instanceof(Directory); @@ -61,7 +63,7 @@ describe('Lionweb integration', function() { expect(file.name).to.equal("delegate.egl"); expect(file.contents.substring(0, 10)).to.equal("Delegate F"); - expect(printSequence(walk(root.node))).to.equal( + expect(printSequence(walk(root))).to.equal( "resources.zip, resources, delegate.egl, rosetta-code-count-examples-2.egl, " + "rosetta-code-count-examples-1.egl, sub1, sub2, foreach.egl, SQLDropTable.egl, for.egl, SQLBatch.egl, " + "SQLCreateTable.egl, SQLDropTable.egl, hello.egl, foreach.egl, Calc.egl, SQLBatch.egl, " + @@ -71,12 +73,12 @@ describe('Lionweb integration', function() { it("can deserialize simple model to dynamic nodes", function () { - const nodes = deserializeChunk(FS_MODEL, new TylasuInstantiationFacade(), [FS_LANGUAGE], []); + const nodes = deserializeToTylasuNodes(FS_MODEL, [FS_LANGUAGE]); expect(nodes).not.to.be.empty; expect(nodes.length).to.equal(1); - const root = nodes[0] as TylasuNodeWrapper; - expect(root.node).to.be.instanceof(LionwebNode); - let dir = root.node as LionwebNode & any; + const root = nodes[0]; + expect(root).to.be.instanceof(LionwebNode); + let dir = root as LionwebNode & any; expect(dir.nodeDefinition.name).to.equal("Directory"); expect(dir.getAttributeValue("name")).to.equal("resources.zip"); expect(dir.files.length).to.equal(1); @@ -91,7 +93,7 @@ describe('Lionweb integration', function() { expect(file.name).to.equal("delegate.egl"); expect(file.contents.substring(0, 10)).to.equal("Delegate F"); - expect(printSequence(walk(root.node))).to.equal( + expect(printSequence(walk(root))).to.equal( "resources.zip, resources, delegate.egl, rosetta-code-count-examples-2.egl, " + "rosetta-code-count-examples-1.egl, sub1, sub2, foreach.egl, SQLDropTable.egl, for.egl, SQLBatch.egl, " + "SQLCreateTable.egl, SQLDropTable.egl, hello.egl, foreach.egl, Calc.egl, SQLBatch.egl, " + @@ -101,12 +103,10 @@ describe('Lionweb integration', function() { it("supports trace nodes", function () { - const nodes = deserializeChunk(FS_MODEL, new TylasuInstantiationFacade(), [FS_LANGUAGE], []); + const nodes = deserializeToTraceNodes(FS_MODEL, [FS_LANGUAGE]); expect(nodes).not.to.be.empty; expect(nodes.length).to.equal(1); - const root = nodes[0] as TylasuNodeWrapper; - expect(root.node).to.be.instanceof(LionwebNode); - let dir = new TraceNode(root.node as LionwebNode); + let dir = nodes[0]; expect(dir.nodeDefinition).not.to.be.undefined; expect(dir.getRole()).to.be.undefined; expect(dir.nodeDefinition.name).to.equal("Directory"); @@ -124,7 +124,7 @@ describe('Lionweb integration', function() { expect(file.getRole()).to.equal("files"); expect(file.getPathFromRoot()).to.eql(["files", 0, "files", 1]); - expect(printSequence(walk(root.node))).to.equal( + expect(printSequence(walk(nodes[0]))).to.equal( "resources.zip, resources, delegate.egl, rosetta-code-count-examples-2.egl, " + "rosetta-code-count-examples-1.egl, sub1, sub2, foreach.egl, SQLDropTable.egl, for.egl, SQLBatch.egl, " + "SQLCreateTable.egl, SQLDropTable.egl, hello.egl, foreach.egl, Calc.egl, SQLBatch.egl, " + @@ -133,16 +133,15 @@ describe('Lionweb integration', function() { }); it("trace nodes don't include the position as a child", function () { - const nodes = deserializeChunk(EGL_MODEL, new TylasuInstantiationFacade(), [EGL_LANGUAGE, STARLASU_LANGUAGE], []); + const nodes = deserializeToTraceNodes(EGL_MODEL, [EGL_LANGUAGE]); expect(nodes).not.to.be.empty; - expect(nodes.length).to.equal(4); - const root = nodes[0] as TylasuNodeWrapper; - expect(root.node).to.be.instanceof(LionwebNode); - const dir = new TraceNode(root.node as LionwebNode); + expect(nodes.length).to.equal(1); + const dir = nodes[0]; expect(dir.nodeDefinition).not.to.be.undefined; expect(dir.getRole()).to.be.undefined; expect(dir.nodeDefinition.name).to.equal("EglCompilationUnit"); expect(dir.containment("position")).to.be.undefined; expect(dir.position).not.to.be.undefined; + expect(dir.children.length).to.equal(0); }); });