diff --git a/mps-cli-ts/src/model/builder/root_node_builder.ts b/mps-cli-ts/src/model/builder/root_node_builder.ts
index b04d11f..ac56f39 100644
--- a/mps-cli-ts/src/model/builder/root_node_builder.ts
+++ b/mps-cli-ts/src/model/builder/root_node_builder.ts
@@ -50,11 +50,12 @@ function populateNodePropertiesAndLinks(nodeElement : Element, node : SNode, imp
let property = child
const propertyRole = property.attributes.getNamedItem("role")!.value
const propertyValue = property.attributes.getNamedItem("value")!.value
- const nodeProperty = registry.getPropertyByIndex(propertyRole)
+ const nodeProperty = registry.getPropertyByRole(propertyRole)
node.addProperty(nodeProperty, propertyValue)
} else if(child.tagName === "ref") {
let ref = child
const refRole = ref.attributes.getNamedItem("role")!.value
+ const refResolveInfo = ref.attributes.getNamedItem("resolve")!.value
var refNodeId : string;
var modelId : string;
@@ -69,7 +70,7 @@ function populateNodePropertiesAndLinks(nodeElement : Element, node : SNode, imp
refNodeId = ref.attributes.getNamedItem("node")!.value
}
const refLink = registry.getLinkByIndex(refRole)
- node.addLink(refLink, new SNodeRef(modelId, refNodeId))
+ node.addLink(refLink, new SNodeRef(modelId, refNodeId, refResolveInfo))
}
}
}
diff --git a/mps-cli-ts/src/model/builder/root_node_builder_fast.ts b/mps-cli-ts/src/model/builder/root_node_builder_fast.ts
index 411b6bb..9bff2e0 100644
--- a/mps-cli-ts/src/model/builder/root_node_builder_fast.ts
+++ b/mps-cli-ts/src/model/builder/root_node_builder_fast.ts
@@ -48,12 +48,13 @@ function populateNodePropertiesAndLinks(node_json_element : any, node : SNode, i
for(var property_json_element of ensure_array(node_json_element["property"])) {
const propertyRole = property_json_element["@_role"] as string
const propertyValue = property_json_element["@_value"] as string
- const nodeProperty = registry.getPropertyByIndex(propertyRole)
+ const nodeProperty = registry.getPropertyByRole(propertyRole)
node.addProperty(nodeProperty, propertyValue)
}
for(var ref_json_element of ensure_array(node_json_element["ref"])) {
const refRole = ref_json_element["@_role"] as string
+ const refResolveInfo = ref_json_element["@_resolve"] as string
var refNodeId : string;
var modelId : string;
@@ -68,7 +69,7 @@ function populateNodePropertiesAndLinks(node_json_element : any, node : SNode, i
refNodeId = ref_json_element["@_node"] as string
}
const refLink = registry.getLinkByIndex(refRole)
- node.addLink(refLink, new SNodeRef(modelId, refNodeId))
+ node.addLink(refLink, new SNodeRef(modelId, refNodeId, refResolveInfo))
}
}
@@ -81,7 +82,7 @@ function buildRootNodeImports(imports_json_element : JsonElement) : SRootNodeImp
const modelRef = model_import_json_element["@_ref"] as string
const splits = modelRef.split("(")
const modelId = splits[0]
- const modelName = splits[1].substring(0, splits[1].length)
+ const modelName = splits[1].substring(0, splits[1].length - 1)
const implicit = model_import_json_element["@_implicit"] as string
imports.imports.push(new SModelImport(importIndex, modelId, modelName, implicit === "true"))
@@ -103,7 +104,7 @@ function buildRootNodeRegistry(registry_json_element : JsonElement) : SRootNodeR
const conceptId = registry_language_concept_json_element["@_id"] as string
const conceptName = registry_language_concept_json_element["@_name"] as string
- const conceptFlag = registry_language_concept_json_element["@_flag"] as string
+ const conceptFlag = registry_language_concept_json_element["@_flags"] as string
const conceptIndex = registry_language_concept_json_element["@_index"] as string
var myConcept = SLanguageBuilder.getConcept(myLanguage, conceptName, conceptId)
diff --git a/mps-cli-ts/src/model/serializer/root_node_serializer.ts b/mps-cli-ts/src/model/serializer/root_node_serializer.ts
new file mode 100644
index 0000000..29603b1
--- /dev/null
+++ b/mps-cli-ts/src/model/serializer/root_node_serializer.ts
@@ -0,0 +1,100 @@
+import { childrenByTagName } from "../builder/utils";
+import { SAbstractConceptLink, SChildLink, SProperty, SReferenceLink } from "../sconcept";
+import { SModel } from "../smodel";
+import { SNode, SNodeRef, SRootNode, SRootNodeImports, SRootNodeRegistry } from "../snode";
+
+
+export function serialize_root_node(model : SModel, root_node : SRootNode) : string {
+ var res = ""
+ res += "" + "\n"
+ res += `` + "\n"
+ var PADDING = " "
+ res += PADDING + "" + "\n"
+
+ res += serialize_imports(root_node, PADDING);
+ res += serialize_registry(root_node, PADDING);
+ res += serialize_nodes(root_node.registry!, root_node.imports!, root_node, undefined, PADDING);
+
+ res += "\n\n"
+ return res;
+}
+
+function serialize_nodes(registry : SRootNodeRegistry, imports : SRootNodeImports, node : SNode, role : string | undefined, padding : string) : string {
+ var res = role === undefined ?
+ `${padding}\n` :
+ `${padding}\n`
+ padding = increase_indent(padding)
+
+ for(const prop2Val of node.properties) {
+ res += `${padding}\n`
+ }
+
+ for(const refLink of node.links.filter(it => it[0] instanceof SReferenceLink)) {
+ const refNode = (refLink[1][0] as SNodeRef)
+ res += `${padding}\n`
+ }
+ for(const childLink of node.links.filter(it => it[0] instanceof SChildLink)) {
+ for(const child of childLink[1]!) {
+ res += serialize_nodes(registry, imports, child as SNode, registry.getIndexForLink(childLink[0]), padding)
+ }
+ }
+ padding = decrease_indent(padding)
+ res += `${padding}\n`
+ return res;
+}
+
+function serialize_registry(root_node : SRootNode, padding : string) : string {
+ var res = `${padding}\n`
+ padding = increase_indent(padding)
+ for(var languageRegistry of root_node.registry?.languages!) {
+ res += `${padding}\n`
+ padding = increase_indent(padding)
+ for(var concept of languageRegistry.usedConcepts) {
+ const noPropertiesAndLinks = concept.propertiesRegistries.length === 0 && concept.linksRegistries.length === 0
+ res += `${padding}\n`
+ padding = increase_indent(padding)
+ for(var prop of concept.propertiesRegistries) {
+ res += `${padding}\n`
+ }
+ padding = decrease_indent(padding)
+ padding = increase_indent(padding)
+ for(var link of concept.linksRegistries) {
+ const linkKind = link.link instanceof SChildLink ? "child" : "reference"
+ res += `${padding}<${linkKind} id="${link.link.id}" name="${link.link.name}" index="${link.index}" />\n`
+ }
+ padding = decrease_indent(padding)
+ if (!noPropertiesAndLinks) {
+ res += `${padding}\n`
+ }
+ }
+ padding = decrease_indent(padding)
+ res += `${padding}\n`
+ }
+ padding = decrease_indent(padding)
+ res += `${padding}\n`
+
+ return res;
+}
+
+function serialize_imports(root_node : SRootNode, padding : string) : string {
+ if(root_node.imports?.imports.length == 0) {
+ return padding + "\n";
+ }
+
+ var res = padding + "\n";
+ padding = increase_indent(padding)
+ for(var crtImport of root_node.imports?.imports!) {
+ res += `${padding}\n`
+ }
+ padding = decrease_indent(padding)
+ res += `${padding}\n`
+ return res;
+}
+
+function increase_indent(padding : string) : string {
+ return padding + " "
+}
+
+function decrease_indent(padding : string) : string {
+ return padding.substring(2)
+}
\ No newline at end of file
diff --git a/mps-cli-ts/src/model/smodel.ts b/mps-cli-ts/src/model/smodel.ts
index a08055d..a9283dd 100644
--- a/mps-cli-ts/src/model/smodel.ts
+++ b/mps-cli-ts/src/model/smodel.ts
@@ -30,4 +30,12 @@ export class SModel {
return undefined
}
+ findRootNodesByName(rootNodeName : string) : SRootNode[] {
+ const res : SRootNode[] = []
+ for(const crtNode of this.rootNodes) {
+ if(crtNode.getProperty("name") === rootNodeName)
+ res.push(crtNode)
+ }
+ return res
+ }
}
\ No newline at end of file
diff --git a/mps-cli-ts/src/model/snode.ts b/mps-cli-ts/src/model/snode.ts
index 4391705..73e4512 100644
--- a/mps-cli-ts/src/model/snode.ts
+++ b/mps-cli-ts/src/model/snode.ts
@@ -5,8 +5,8 @@ import { SRepository } from "./srepository";
export class SNode {
myConcept : SConcept;
id : string;
- links : Map = new Map
- properties : Map = new Map
+ links : [SAbstractConceptLink, (SNode | SNodeRef)[]][] = []
+ properties : [SProperty, string][] = []
myParent : SNode | undefined;
constructor(myConcept : SConcept, id : string, parent : SNode | undefined) {
@@ -16,30 +16,30 @@ export class SNode {
}
addLink(link : SAbstractConceptLink, node : SNode | SNodeRef) {
- var nodesForLink = this.links.get(link);
- if (nodesForLink == null) {
- nodesForLink = [];
- this.links.set(link, nodesForLink)
+ var nodesForLink = this.links.find(it => it[0] === link);
+ if (nodesForLink == undefined) {
+ this.links.push([link, [node]])
+ } else {
+ nodesForLink[1].push(node)
}
- nodesForLink.push(node)
}
allLinkedNodes() : (SNode | SNodeRef)[] {
var res : (SNode | SNodeRef)[] = []
- this.links.forEach((linkedNodes : (SNode | SNodeRef)[], link : SAbstractConceptLink) => {
- res = res.concat(linkedNodes)
+ this.links.forEach(it => {
+ res = res.concat(it[1])
})
return res
}
addProperty(property : SProperty, value : string) {
- this.properties.set(property, value);
+ this.properties.push([property, value]);
}
getProperty(propertyName : string) : string | undefined {
- for(const prop of this.properties.keys())
- if(prop.name === propertyName)
- return this.properties.get(prop);
+ for(const prop of this.properties)
+ if(prop[0].name === propertyName)
+ return prop[1];
return undefined
}
@@ -48,7 +48,7 @@ export class SNode {
if (includeSelf && (concept === undefined || this.myConcept == concept)) { res.push(this) }
const linksToVisit : [SChildLink, SNode[]][] = []
- const myChildren = Array.from(this.links.entries()).filter(it => it[0] instanceof SChildLink)
+ const myChildren = Array.from(this.links).filter(it => it[0] instanceof SChildLink)
myChildren.forEach(it => linksToVisit.push(it as [SChildLink, SNode[]]))
while(linksToVisit.length > 0) {
@@ -56,7 +56,7 @@ export class SNode {
res.push(...crtLink?.[1])
for(const childNode of crtLink?.[1]) {
- const myChildren = Array.from(childNode.links.entries()).filter(it => it[0] instanceof SChildLink)
+ const myChildren = childNode.links.filter(it => it[0] instanceof SChildLink)
myChildren.forEach(it => linksToVisit.push(it as [SChildLink, SNode[]]))
}
}
@@ -65,9 +65,9 @@ export class SNode {
getLinkedNodes(linkName : string) : (SNode | SNodeRef)[] {
const res : (SNode | SNodeRef)[] = []
- this.links.forEach((linkedNodes : (SNode | SNodeRef)[], link : SAbstractConceptLink) => {
- if (link.name === linkName) {
- res.push(...linkedNodes)
+ this.links.forEach(it => {
+ if (it[0].name === linkName) {
+ res.push(...it[1])
}
});
return res
@@ -88,10 +88,12 @@ export class SRootNode extends SNode {
export class SNodeRef {
modelId : string
nodeId : string
+ resolveInfo : string;
- constructor(modelId : string, nodeId : string) {
+ constructor(modelId : string, nodeId : string, resolveInfo : string) {
this.modelId = modelId
this.nodeId = nodeId
+ this.resolveInfo = resolveInfo
}
resolve(repo : SRepository) : SNode | undefined {
@@ -114,6 +116,14 @@ export class SRootNodeImports {
return "undefined";
}
+ getModelIndexByModelId(modelId : string) : string {
+ for(const imp of this.imports) {
+ if (imp.myModelId === modelId)
+ return imp.myModelIndex
+ }
+ return "undefined";
+ }
+
}
export class SRootNodeRegistry {
@@ -127,13 +137,34 @@ export class SRootNodeRegistry {
return this.index2concepts.get(index)!;
}
+ getIndexForConcept(concept : SConcept) : string | undefined {
+ for(const entry of this.index2concepts.entries())
+ if(entry[1] === concept)
+ return entry[0]
+ return undefined;
+ }
+
getLinkByIndex(index : string) : SAbstractConceptLink {
return this.index2links.get(index)!;
}
- getPropertyByIndex(index : string) : SProperty {
+ getIndexForLink(link : SAbstractConceptLink) : string | undefined {
+ for(const entry of this.index2links.entries())
+ if(entry[1] === link)
+ return entry[0]
+ return undefined;
+ }
+
+ getPropertyByRole(index : string) : SProperty {
return this.index2properties.get(index)!;
}
+
+ getRoleForProperty(property : SProperty) : string | undefined {
+ for(const entry of this.index2properties.entries())
+ if(entry[1] === property)
+ return entry[0]
+ return undefined;
+ }
}
export class SModelImport {
diff --git a/mps-cli-ts/src/model/srepository.ts b/mps-cli-ts/src/model/srepository.ts
index 66f876b..385e091 100644
--- a/mps-cli-ts/src/model/srepository.ts
+++ b/mps-cli-ts/src/model/srepository.ts
@@ -1,6 +1,6 @@
import { SModel } from "./smodel";
import { SAbstractModule } from "./smodule";
-import { SNode } from "./snode";
+import { SNode, SRootNode } from "./snode";
export class SRepository {
@@ -60,7 +60,7 @@ export class SRepository {
return undefined;
}
- findModelByName(modelName : string) : SModel[] {
+ findModelsByName(modelName : string) : SModel[] {
const res : SModel[] = []
for(const crtModule of this.modules) {
for(const crtModel of crtModule.models) {
@@ -71,5 +71,18 @@ export class SRepository {
return res;
}
+ findRootNodesByName(rootNodeName : string) : SRootNode[] {
+ const res : SRootNode[] = []
+ for(const crtModule of this.modules) {
+ for(const crtModel of crtModule.models) {
+ for(const crtRoot of crtModel.rootNodes) {
+ if (crtRoot.getProperty("name") === rootNodeName)
+ res.push(crtRoot)
+ }
+ }
+ }
+ return res;
+ }
+
}
diff --git a/mps-cli-ts/tests/node.spec.ts b/mps-cli-ts/tests/node.spec.ts
index 806c0e5..660710b 100644
--- a/mps-cli-ts/tests/node.spec.ts
+++ b/mps-cli-ts/tests/node.spec.ts
@@ -43,13 +43,12 @@ describe('testing building of root nodes', () => {
assert.equal(rootNode.myConcept.name, "mps.cli.landefs.people.structure.PersonsContainer")
assert.equal(rootNode.id, "4Yb5JA31NUu")
assert.equal(rootNode.allLinkedNodes().length, 2)
- assert.equal(rootNode.properties.size, 1)
- const propertiesKeysArray = Array.from(rootNode.properties.keys());
+ assert.equal(rootNode.properties.length, 1)
- const nameProperty = propertiesKeysArray[0]
+ const nameProperty = rootNode.properties[0][0]
assert.equal(nameProperty.name, "name");
assert.equal(nameProperty.id, "1169194664001");
- assert.equal(rootNode.properties.get(nameProperty), "_010_classical_authors");
+ assert.equal(rootNode.getProperty(nameProperty.name), "_010_classical_authors");
assert.equal(rootNode.getProperty("name"), "_010_classical_authors");
const descendants = rootNode.descendants(undefined, false)
diff --git a/mps-cli-ts/tests/project.spec.ts b/mps-cli-ts/tests/project.spec.ts
index 86b45d2..ed3629c 100644
--- a/mps-cli-ts/tests/project.spec.ts
+++ b/mps-cli-ts/tests/project.spec.ts
@@ -14,9 +14,9 @@ describe("testing building of the model from a directory with solutions", () =>
assert.equal(repo.modules.length, 2);
assert.equal(repo.getNodesWithPropertyAndValue("name").length, 11)
- assert.equal(repo.findModelByName("mps.cli.lanuse.library_second.library_top").length, 1)
- assert.equal(repo.findModelByName("mps.cli.lanuse.library_second.library_top")[0].rootNodes.length, 1)
- assert.equal(repo.findModelByName("mps.cli.lanuse.library_second.library_top")[0].rootNodes[0].getProperty("name"), "_library")
+ assert.equal(repo.findModelsByName("mps.cli.lanuse.library_second.library_top").length, 1)
+ assert.equal(repo.findModelsByName("mps.cli.lanuse.library_second.library_top")[0].rootNodes.length, 1)
+ assert.equal(repo.findModelsByName("mps.cli.lanuse.library_second.library_top")[0].rootNodes[0].getProperty("name"), "_library")
assert.equal(repo.getNodesWithPropertyAndValue("name", "_library").length, 1)
assert.equal(repo.getNodesWithPropertyAndValue("name", "Mark Twain").length, 1)
const markTwain = repo.getNodesWithPropertyAndValue("name", "Mark Twain")[0]
diff --git a/mps-cli-ts/tests/root_node_serializer.spec.ts b/mps-cli-ts/tests/root_node_serializer.spec.ts
new file mode 100644
index 0000000..2461515
--- /dev/null
+++ b/mps-cli-ts/tests/root_node_serializer.spec.ts
@@ -0,0 +1,59 @@
+
+import { assert } from 'chai'
+import { readdirSync, readFileSync, lstatSync } from 'fs'
+import { loadSolutions, parseXml } from '../src/file_parser';
+import { buildRootNode } from '../src/model/builder/root_node_builder';
+import { SModel } from '../src/model/smodel';
+import { SRootNode } from '../src/model/snode';
+import { SRepository } from '../src/model/srepository';
+import { serialize_root_node } from '../src/model/serializer/root_node_serializer';
+
+
+describe('testing serializing the root node', () => {
+
+ const projectPath = "../mps_test_projects/mps_cli_lanuse_file_per_root"
+ const repo: SRepository = loadSolutions(projectPath);
+ const library_second_library_top_model = repo.findModelsByName("mps.cli.lanuse.library_second.library_top")[0]
+ const library_top_library_top_model = repo.findModelsByName("mps.cli.lanuse.library_top.library_top")[0]
+ const library_top_authors_top_model = repo.findModelsByName("mps.cli.lanuse.library_top.authors_top")[0]
+
+ it('serialize _library root node', () => {
+ const library = library_second_library_top_model.findRootNodesByName("_library")[0]
+ const serializedLibrary = serialize_root_node(library_second_library_top_model, library)
+ compare(projectPath + "/solutions/mps.cli.lanuse.library_second/models/mps.cli.lanuse.library_second.library_top/_library.mpsr", serializedLibrary)
+ })
+
+ it('serialize munich_library root node', () => {
+ const munich_library = library_top_library_top_model.findRootNodesByName("munich_library")[0]
+ const serializedLibrary = serialize_root_node(library_top_library_top_model, munich_library)
+ compare(projectPath + "/solutions/mps.cli.lanuse.library_top/models/mps.cli.lanuse.library_top.library_top/munich_library.mpsr", serializedLibrary)
+ })
+
+ it('serialize schwabing_library root node', () => {
+ const schwabing_library = library_top_library_top_model.findRootNodesByName("schwabing library")[0]
+ const serializedLibrary = serialize_root_node(library_top_library_top_model, schwabing_library)
+ compare(projectPath + "/solutions/mps.cli.lanuse.library_top/models/mps.cli.lanuse.library_top.library_top/schwabing library.mpsr", serializedLibrary)
+ })
+
+ it('serialize classical_authors root node', () => {
+ const classical_authors = library_top_authors_top_model.findRootNodesByName("_010_classical_authors")[0]
+ const serializedLibrary = serialize_root_node(library_top_authors_top_model, classical_authors)
+ compare(projectPath + "/solutions/mps.cli.lanuse.library_top/models/mps.cli.lanuse.library_top.authors_top/_010_classical_authors.mpsr", serializedLibrary)
+ })
+
+
+
+})
+
+
+
+function compare(filePathWithExpectedResult : string, serializedString : string) {
+ const expectedString = readFileSync(filePathWithExpectedResult, 'utf8')
+ const expectedLines = expectedString.split(/\r?\n/)
+ const actualLines = serializedString.split(/\r?\n/)
+
+ for(let i = 0; i < Math.min(expectedLines.length, actualLines.length); i++) {
+ assert.equal(actualLines.at(i), expectedLines.at(i), `line ${i} is different`)
+ }
+ assert.equal(actualLines.length, expectedLines.length, `different number of lines:\n\texpected: ${expectedLines.length}\n\tactual: ${actualLines.length}`)
+}
\ No newline at end of file
diff --git a/mps-cli-ts/tests/sandbox.ts b/mps-cli-ts/tests/sandbox.ts
index a1cb711..8be54a3 100644
--- a/mps-cli-ts/tests/sandbox.ts
+++ b/mps-cli-ts/tests/sandbox.ts
@@ -1,10 +1,10 @@
import { loadModelsFromSolution, loadSolutions, parseXml } from "../src/file_parser";
//const repo = loadSolutions("c:\\work\\E3_2.0_Solution\\solutions")
-const repo = loadSolutions('C:\\work\\mbeddr.formal\\code\\tutorial-safety')
+//const repo = loadSolutions('C:\\work\\mbeddr.formal\\code\\tutorial-safety')
//console.log("starting..........")
//loadModelsFromSolution("C:\\work\\mps-cli\\mps_test_projects\\mps_cli_lanuse_file_per_root\\solutions\\mps.cli.lanuse.library_second")
-console.log(`number of modules: ${ repo.modules.length }`)
-console.log(`number of models: ${ repo.allModels().length }`)
-console.log(`number of nodes: ${ repo.allNodes().length }`)
\ No newline at end of file
+//console.log(`number of modules: ${ repo.modules.length }`)
+//console.log(`number of models: ${ repo.allModels().length }`)
+//console.log(`number of nodes: ${ repo.allNodes().length }`)
\ No newline at end of file