Repository navigation
Expand file tree
/
Copy pathtutorial.mjs
More file actions
83 lines (73 loc) · 4.5 KB
/
Copy pathtutorial.mjs
File metadata and controls
83 lines (73 loc) · 4.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
// SysML v2 SDK, JavaScript / TypeScript: a first walk through a model.
//
// Run from the repository root:
//
// node examples/typescript/tutorial.mjs reads examples/vehicle.full.json
// node examples/typescript/tutorial.mjs --toolkit reads examples/vehicle.sysml through the SysML Toolkit
//
// The payload is a full-form interchange export any SysML v2 tool can produce; this one came
// from the SysML Toolkit. Its references into the standard library resolve against the library
// as JSON, published with each SDK release. In this repository both are generated by
// `python tools/export_example.py --library <sysml.library>`. The SysML Toolkit path loads the
// WebAssembly build of the binding library, which is the same in Node and in a browser (build it
// first: see abi/README.md).
//
// Everything is spelled as in the OMG specification: ownedMember, ownedFeature,
// qualifiedName, type. Elements are Proxy objects; `is(el, "PartDefinition")` answers the
// metaclass question, because JavaScript has no class hierarchy to lean on here.
import { readFileSync } from "node:fs";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
import { ToolkitBackend, Model, NotImplementedInToolkit, PayloadLibrary, is } from "../../ts/index.mjs"; // `sysml` once published
const root = join(dirname(fileURLToPath(import.meta.url)), "..", "..");
const toolkit = process.argv.includes("--toolkit");
const readJson = (...path) => JSON.parse(readFileSync(join(root, ...path), "utf8"));
const model = toolkit
? new Model(await ToolkitBackend.open({ sources: { "vehicle.sysml": readFileSync(join(root, "examples", "vehicle.sysml"), "utf8") } }))
: Model.fromFullJson(readJson("examples", "vehicle.full.json"),
{ library: new PayloadLibrary(readJson("examples", "sysml.library.full.json")) });
// A read the SysML Toolkit may refuse: where it cannot vouch for a value yet, it says so
// (NotImplementedInToolkit) instead of answering; the tutorial shows the refusal and goes on.
function refusalOr(read) {
try {
return read();
} catch (e) {
if (!(e instanceof NotImplementedInToolkit)) throw e;
return `(${e.message})`;
}
}
// 1. Find an element by its qualified name.
const vehicle = model.resolve("Vehicles::Vehicle");
console.log(`${vehicle.qualifiedName} is a ${vehicle.$metaclass}`);
// 2. Ask about metaclasses with is(); the hierarchy comes from the metamodel table.
const vehicles = model.resolve("Vehicles");
const definitions = vehicles.ownedMember.filter((m) => is(m, "Definition"));
console.log("definitions:", definitions.map((d) => d.declaredName).join(", "));
// 3. Walk a definition's features and their declared types.
console.log(`\nfeatures of ${vehicle.declaredName}:`);
for (const feature of vehicle.ownedFeature) {
const types = refusalOr(() => feature.type.map((t) => t.declaredName).join(", ") || "untyped");
console.log(` ${feature.$metaclass.padEnd(16)} ${(feature.declaredName ?? "(unnamed)").padEnd(10)} : ${types}`);
}
// 4. Documentation is a member like any other.
for (const doc of vehicle.documentation) console.log(`\ndoc: ${doc.body.trim()}`);
// 5. A usage and the definition it is typed by.
const myCar = model.resolve("Vehicles::myCar");
console.log(`\n${myCar.qualifiedName} is typed by ${refusalOr(() => myCar.type.map((t) => t.qualifiedName))}`);
// 6. Inheritance. Through the SysML Toolkit, inherited features are computed. A payload holds what
// the exporting tool wrote: this file was exported at the closure level, so it has them too; an
// export by the SysML Toolkit's command-line tool would list the owned side only. With the
// standard library, every part also inherits the library's features (`self`, `start`, ...);
// `isLibraryElement` tells them apart.
const sports = model.resolve("Vehicles::SportsCar");
const inherited = refusalOr(() => sports.inheritedFeature.filter((f) => !f.isLibraryElement).length);
console.log(`\n${sports.declaredName} owns ${sports.ownedFeature.map((f) => f.declaredName)}; inherited: ${inherited}`);
// 7. Honest refusal. Through the SysML Toolkit, a member it cannot vouch for yet says so instead of
// answering, as the reads above show where it does. Through a payload there is nobody to say so:
// the file holds what the exporting tool wrote.
try {
console.log(`\npartDefinition of myCar: ${myCar.partDefinition.map((d) => d.qualifiedName)}`);
} catch (e) {
if (!(e instanceof NotImplementedInToolkit)) throw e;
console.log(`\nnot answered by the SysML Toolkit, and it says so: ${e.message}`);
}