├── README.md ├── examples └── editor.html ├── src ├── index.ts ├── node │ ├── index.ts │ ├── main.ts │ ├── context.ts │ └── library.ts ├── browser │ ├── index.ts │ ├── library.ts │ └── context.ts └── common │ ├── diagnostics.ts │ ├── scope.ts │ ├── writer.ts │ ├── util.ts │ ├── dom.ts │ ├── parser.ts │ ├── document.ts │ ├── library.ts │ ├── context.ts │ ├── predefined.ts │ ├── graphics.ts │ └── symbols.ts ├── package.json ├── webpack.config.js ├── .gitignore ├── tsconfig.json └── OSMC-License.txt /README.md: -------------------------------------------------------------------------------- 1 | # OMFrontend.js 2 | 3 | An [open-source](OSMC-License.txt) front-end for Modelica written in TypeScript/JavaScript. 4 | Works together with [tree-sitter-modelica](https://github.com/OpenModelica/tree-sitter-modelica). 5 | -------------------------------------------------------------------------------- /examples/editor.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Modelica Editor 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * OMFrontend.js 3 | * Copyright (C) 2022 Perpetual Labs, Ltd. 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU Affero General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU Affero General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Affero General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | /** 20 | * @author Mohamad Omar Nachawati 21 | */ 22 | 23 | export * from "./browser/index.js"; 24 | export * from "./node/index.js"; 25 | -------------------------------------------------------------------------------- /src/node/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * OMFrontend.js 3 | * Copyright (C) 2022 Perpetual Labs, Ltd. 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU Affero General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU Affero General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Affero General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | /** 20 | * @author Mohamad Omar Nachawati 21 | */ 22 | 23 | export * from "../common/context.js"; 24 | export * from "../common/diagnostics.js"; 25 | export * from "../common/document.js"; 26 | export * from "../common/graphics.js"; 27 | export * from "../common/library.js"; 28 | export * from "../common/parser.js"; 29 | export * from "../common/predefined.js"; 30 | export * from "../common/scope.js"; 31 | export * from "../common/symbols.js"; 32 | export * from "../common/syntax.js"; 33 | export * from "../common/util.js"; 34 | export * from "../common/writer.js"; 35 | 36 | export * from "./context.js"; 37 | export * from "./library.js"; 38 | export * from "./main.js"; -------------------------------------------------------------------------------- /src/browser/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * OMFrontend.js 3 | * Copyright (C) 2022 Perpetual Labs, Ltd. 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU Affero General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU Affero General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Affero General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | /** 20 | * @author Mohamad Omar Nachawati 21 | */ 22 | 23 | export * from "../common/context.js"; 24 | export * from "../common/diagnostics.js"; 25 | export * from "../common/document.js"; 26 | export * from "../common/dom.js"; 27 | export * from "../common/graphics.js"; 28 | export * from "../common/library.js"; 29 | export * from "../common/parser.js"; 30 | export * from "../common/predefined.js"; 31 | export * from "../common/scope.js"; 32 | export * from "../common/symbols.js"; 33 | export * from "../common/syntax.js"; 34 | export * from "../common/util.js"; 35 | export * from "../common/writer.js"; 36 | 37 | export * from "./context.js"; 38 | export * from "./library.js"; -------------------------------------------------------------------------------- /src/common/diagnostics.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * OMFrontend.js 3 | * Copyright (C) 2022 Perpetual Labs, Ltd. 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU Affero General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU Affero General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Affero General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | /** 20 | * @author Mohamad Omar Nachawati 21 | */ 22 | 23 | import { Range } from "./parser.js"; 24 | 25 | export enum ModelicaDiagnosticSeverity { 26 | ERROR = 0, 27 | HINT = 3, 28 | INFORMATION = 2, 29 | WARNING = 1, 30 | } 31 | 32 | export class ModelicaDiagnostic { 33 | 34 | code: number; 35 | message: string; 36 | range?: Range; 37 | severity: ModelicaDiagnosticSeverity; 38 | source?: string; 39 | 40 | constructor(code: number, message: string, severity: ModelicaDiagnosticSeverity, range?: Range | null) { 41 | this.code = code; 42 | this.message = message; 43 | this.severity = severity; 44 | this.range = range ?? undefined; 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/browser/library.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * OMFrontend.js 3 | * Copyright (C) 2022 Perpetual Labs, Ltd. 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU Affero General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU Affero General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Affero General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | /** 20 | * @author Mohamad Omar Nachawati 21 | */ 22 | 23 | import { ModelicaContext } from "../common/context.js"; 24 | import { ModelicaLibrary } from "../common/library.js"; 25 | 26 | export class ModelicaBrowserFileSystemLibrary extends ModelicaLibrary { 27 | 28 | #rootPath: string; 29 | 30 | constructor(context: ModelicaContext, rootPath: string) { 31 | super(context); 32 | this.#rootPath = rootPath; 33 | } 34 | 35 | override list(...path: string[]): AsyncIterableIterator { 36 | throw new Error("Method not implemented."); 37 | } 38 | 39 | override read(...path: string[]): Promise { 40 | throw new Error("Method not implemented."); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "omfrontend", 3 | "version": "0.0.1", 4 | "description": "Modelica frontend in JavaScript", 5 | "type": "module", 6 | "main": "dist/node/index.js", 7 | "browser": "dist/browser/index.js", 8 | "types": "dist/types/index.d.ts", 9 | "files": [ 10 | "dist" 11 | ], 12 | "bin": { 13 | "omf": "./dist/node/main.js" 14 | }, 15 | "scripts": { 16 | "build-cli": "tsc", 17 | "build": "tsc && cd node_modules/tree-sitter-modelica && npx tree-sitter build --wasm . --docker && cd ../.. && webpack", 18 | "serve": "tsc -w & webpack serve & wait" 19 | }, 20 | "devDependencies": { 21 | "@types/d3": "^7.1.0", 22 | "@types/jsdom": "^16.2.14", 23 | "@types/node": "^17.0.10", 24 | "@types/yargs": "^17.0.8", 25 | "copy-webpack-plugin": "^10.2.1", 26 | "file-loader": "^6.2.0", 27 | "resolve-typescript-plugin": "^1.1.5", 28 | "tree-sitter-cli": "^0.22.1", 29 | "ts-loader": "^9.2.6", 30 | "typescript": "^4.5.5", 31 | "webpack": "^5.67.0", 32 | "webpack-cli": "^4.9.2", 33 | "webpack-dev-server": "^4.7.3" 34 | }, 35 | "dependencies": { 36 | "browser-process-hrtime": "^1.0.0", 37 | "jszip": "^3.7.1", 38 | "monaco-editor": "^0.32.1", 39 | "monaco-editor-core": "^0.32.1", 40 | "process": "^0.11.10", 41 | "tree-sitter": "^0.21.1", 42 | "tree-sitter-modelica": "file:../tree-sitter-modelica", 43 | "vscode-languageserver-textdocument": "^1.0.4", 44 | "web-tree-sitter": "^0.22.5", 45 | "yargs": "^17.3.1" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/common/scope.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * OMFrontend.js 3 | * Copyright (C) 2022 Perpetual Labs, Ltd. 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU Affero General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU Affero General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Affero General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | /** 20 | * @author Mohamad Omar Nachawati 21 | */ 22 | 23 | import { ModelicaContext } from "./context.js"; 24 | import { ModelicaClassSymbol, ModelicaNamedElementSymbol } from "./symbols.js"; 25 | import { ModelicaComponentReferenceExpressionSyntax, ModelicaIdentifierSyntax, ModelicaNameSyntax, ModelicaTypeSpecifierSyntax } from "./syntax.js"; 26 | 27 | export interface ModelicaScope { 28 | 29 | get context(): ModelicaContext; 30 | 31 | resolve(reference: ModelicaIdentifierSyntax | ModelicaNameSyntax | ModelicaTypeSpecifierSyntax | string[] | string | null | undefined, global?: boolean): Promise; 32 | 33 | resolveFunction(reference: ModelicaComponentReferenceExpressionSyntax | ModelicaIdentifierSyntax | ModelicaNameSyntax | ModelicaTypeSpecifierSyntax | string[] | string | null | undefined, global?: boolean): Promise; 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/common/writer.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * OMFrontend.js 3 | * Copyright (C) 2022 Perpetual Labs, Ltd. 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU Affero General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU Affero General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Affero General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | /** 20 | * @author Mohamad Omar Nachawati 21 | */ 22 | 23 | export interface Writer { 24 | write(message: string): void; 25 | } 26 | 27 | export abstract class PrintWriter implements Writer { 28 | 29 | print(message: string, indent?: number): void { 30 | 31 | if (indent != null) 32 | this.write(" ".repeat(indent)); 33 | 34 | this.write(message); 35 | 36 | } 37 | 38 | println(message?: string, indent?: number): void { 39 | 40 | if (message != null) { 41 | 42 | if (indent != null) 43 | this.write(" ".repeat(indent)); 44 | 45 | this.write(message); 46 | 47 | } 48 | 49 | this.write("\n"); 50 | 51 | } 52 | 53 | abstract write(message: string): void; 54 | 55 | } 56 | 57 | export class BufferedPrintWriter extends PrintWriter { 58 | 59 | #buffer: string[] = []; 60 | 61 | override toString(): string { 62 | return this.#buffer.join(""); 63 | } 64 | 65 | override write(message: string): void { 66 | this.#buffer.push(message); 67 | } 68 | 69 | } -------------------------------------------------------------------------------- /src/common/util.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * OMFrontend.js 3 | * Copyright (C) 2022 Perpetual Labs, Ltd. 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU Affero General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU Affero General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Affero General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | /** 20 | * @author Mohamad Omar Nachawati 21 | */ 22 | 23 | import { ModelicaIdentifierSyntax, ModelicaNameSyntax, ModelicaTypeSpecifierSyntax } from "./syntax.js"; 24 | 25 | export function getIdentifiers(reference: ModelicaIdentifierSyntax | ModelicaNameSyntax | ModelicaTypeSpecifierSyntax | string | string[] | null | undefined): string[] { 26 | 27 | if (reference == null) 28 | return []; 29 | 30 | if (reference instanceof ModelicaIdentifierSyntax) { 31 | 32 | if (reference.value != null) 33 | return [reference.value]; 34 | 35 | return []; 36 | 37 | } 38 | 39 | if (reference instanceof ModelicaNameSyntax) 40 | return reference.identifiers ?? []; 41 | 42 | if (reference instanceof ModelicaTypeSpecifierSyntax) 43 | return reference.identifiers ?? []; 44 | 45 | if (Array.isArray(reference)) 46 | return reference; 47 | 48 | return reference.split(".").map(item => item.trim()); 49 | 50 | } 51 | 52 | export function toArray(iterator: IterableIterator | null | undefined): T[] { 53 | 54 | let values: T[] = []; 55 | 56 | for (let value of iterator ?? []) 57 | values.push(value); 58 | 59 | return values; 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/node/main.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S node --enable-source-maps 2 | 3 | /* 4 | * OMFrontend.js 5 | * Copyright (C) 2022 Perpetual Labs, Ltd. 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | /** 22 | * @author Mohamad Omar Nachawati 23 | */ 24 | 25 | import yargs from "yargs"; 26 | import { hideBin } from "yargs/helpers"; 27 | import { ModelicaNodeContext } from "./context.js"; 28 | import fs from "fs"; 29 | import { ModelicaStoredDefinitionSyntax } from "../common/syntax.js"; 30 | const yargz = yargs(hideBin(process.argv)); 31 | import { BufferedPrintWriter } from "../common/writer.js"; 32 | import JSZip from "jszip"; 33 | 34 | import util from "util"; 35 | 36 | yargz.scriptName("omf") 37 | .usage("usage: $0 [options]") 38 | .alias("h", "help") 39 | .alias("v", "version") 40 | .option("silent", { 41 | alias: "q", 42 | default: false, 43 | describe: "Turns on silent mode", 44 | type: "boolean" 45 | }) 46 | .option("std", { 47 | choices: ["3.5", "latest"], 48 | default: "latest", 49 | describe: "Sets the language standard that should be used" 50 | }) 51 | .showHelpOnFail(true) 52 | .demandCommand(1) 53 | .wrap(yargz.terminalWidth()) 54 | .command("parse ", "Parse Modelica file", (yargs) => { 55 | yargs.positional("file", { 56 | describe: "file to parse", 57 | type: "string", 58 | }) 59 | }, async (args: any) => { 60 | let context = new ModelicaNodeContext(); 61 | let text = fs.readFileSync(args.file, { encoding: "utf8" }); 62 | let tree = context.parse(text); 63 | console.log(tree.rootNode.toString()); 64 | }) 65 | .argv; 66 | -------------------------------------------------------------------------------- /src/node/context.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * OMFrontend.js 3 | * Copyright (C) 2022 Perpetual Labs, Ltd. 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU Affero General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU Affero General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Affero General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | /** 20 | * @author Mohamad Omar Nachawati 21 | */ 22 | 23 | import Parser from "tree-sitter"; 24 | 25 | // @ts-ignore 26 | import Modelica from "tree-sitter-modelica"; 27 | 28 | import { ModelicaContext } from "../common/context.js"; 29 | import { ModelicaLibrary } from "../common/library.js"; 30 | import { InputReader, Tree } from "../common/parser.js"; 31 | import { ModelicaNodeFileSystemLibrary } from "./library.js"; 32 | 33 | 34 | export class ModelicaNodeContext extends ModelicaContext { 35 | 36 | static #parser: Parser; 37 | 38 | constructor(workspace?: ModelicaLibrary, libraries?: ModelicaLibrary[]) { 39 | 40 | super(workspace, libraries); 41 | 42 | ModelicaNodeContext.initialize(); 43 | 44 | if (ModelicaNodeContext.#parser == null) 45 | throw new Error("ModelicaNodeContext is not initialized."); 46 | 47 | } 48 | 49 | addLibrary(rootPath: string): ModelicaLibrary { 50 | let library = new ModelicaNodeFileSystemLibrary(this, rootPath); 51 | this.libraries.push(library); 52 | return library; 53 | } 54 | 55 | static initialize(): void { 56 | 57 | if (ModelicaNodeContext.#parser != null) 58 | return; 59 | 60 | let parser = new Parser(); 61 | parser.setLanguage(Modelica); 62 | ModelicaNodeContext.#parser = parser; 63 | 64 | } 65 | 66 | override parse(input: string | InputReader, previousTree?: Tree): Tree { 67 | return ModelicaNodeContext.#parser.parse(input, previousTree as Parser.Tree | undefined); 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /src/browser/context.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * OMFrontend.js 3 | * Copyright (C) 2022 Perpetual Labs, Ltd. 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU Affero General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU Affero General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Affero General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | /** 20 | * @author Mohamad Omar Nachawati 21 | */ 22 | 23 | import * as Parser from 'web-tree-sitter'; 24 | 25 | import { ModelicaContext } from "../common/context"; 26 | import { ModelicaLibrary } from "../common/library"; 27 | import { InputReader, Tree } from "../common/parser"; 28 | import { ModelicaBrowserFileSystemLibrary } from "./library"; 29 | 30 | export class ModelicaBrowserContext extends ModelicaContext { 31 | 32 | static #parser: Parser; 33 | 34 | constructor(workspace?: ModelicaLibrary, libraries?: ModelicaLibrary[]) { 35 | 36 | super(workspace, libraries); 37 | 38 | if (ModelicaBrowserContext.#parser == null) 39 | throw new Error("ModelicaBrowserContext is not initialized."); 40 | 41 | } 42 | 43 | addLibrary(rootPath: string): ModelicaLibrary { 44 | let library = new ModelicaBrowserFileSystemLibrary(this, rootPath); 45 | this.libraries.push(library); 46 | return library; 47 | } 48 | 49 | static async initialize(parser?: Parser): Promise { 50 | 51 | if (ModelicaBrowserContext.#parser != null) 52 | return; 53 | 54 | if (parser != null) { 55 | ModelicaBrowserContext.#parser = parser; 56 | return; 57 | } 58 | 59 | await Parser.default.init(); 60 | parser = new Parser.default(); 61 | parser.setLanguage(await Parser.Language.load("tree-sitter-modelica.wasm")); 62 | 63 | ModelicaBrowserContext.#parser = parser; 64 | 65 | } 66 | 67 | override parse(input: string | InputReader, previousTree?: Tree): Tree { 68 | return ModelicaBrowserContext.#parser.parse(input, previousTree); 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /src/node/library.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * OMFrontend.js 3 | * Copyright (C) 2022 Perpetual Labs, Ltd. 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU Affero General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU Affero General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Affero General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | /** 20 | * @author Mohamad Omar Nachawati 21 | */ 22 | 23 | import { ModelicaContext } from "../common/context.js"; 24 | import { ModelicaLibrary } from "../common/library.js"; 25 | import fs from "fs"; 26 | import path from "path"; 27 | 28 | export class ModelicaNodeFileSystemLibrary extends ModelicaLibrary { 29 | 30 | #rootPath: string; 31 | 32 | constructor(context: ModelicaContext, rootPath: string) { 33 | super(context); 34 | this.#rootPath = path.resolve(rootPath); 35 | } 36 | 37 | override async *list(...filePath: string[]): AsyncIterableIterator { 38 | 39 | try { 40 | 41 | for (let fileName of await fs.promises.readdir(path.join(this.#rootPath, ...filePath))) { 42 | 43 | try { 44 | 45 | let absoluteFilePath = path.join(this.#rootPath, ...filePath, fileName); 46 | 47 | if (absoluteFilePath.endsWith("/package.mo")) 48 | continue; 49 | 50 | let stats = await fs.promises.stat(absoluteFilePath); 51 | 52 | if (stats.isDirectory() == true) 53 | yield fileName; 54 | 55 | else if (stats.isFile() && fileName.endsWith(".mo")) 56 | yield fileName; 57 | 58 | } catch (e) { 59 | } 60 | 61 | } 62 | 63 | } catch (e) { 64 | } 65 | 66 | } 67 | 68 | override async read(...filePath: string[]): Promise { 69 | 70 | try { 71 | 72 | let absoluteFilePath = path.join(this.#rootPath, ...filePath); 73 | return await fs.promises.readFile(absoluteFilePath, "utf-8"); 74 | 75 | } catch (e) { 76 | return null; 77 | } 78 | 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | /* 2 | * OMFrontend.js 3 | * Copyright (C) 2022 Perpetual Labs, Ltd. 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU Affero General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU Affero General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Affero General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | import CopyPlugin from "copy-webpack-plugin"; 20 | import ResolveTypeScriptPlugin from "resolve-typescript-plugin"; 21 | import { dirname, join, resolve } from "path"; 22 | import { fileURLToPath } from "url"; 23 | 24 | const __filename = fileURLToPath(import.meta.url); 25 | const __dirname = dirname(__filename); 26 | 27 | export default (env, argv) => { 28 | return { 29 | mode: "development", 30 | entry: { 31 | OMFrontend: "./src/browser/index.ts", 32 | "editor.worker": "monaco-editor-core/esm/vs/editor/editor.worker.js", 33 | "json.worker": "monaco-editor/esm/vs/language/json/json.worker" 34 | }, 35 | target: "web", 36 | output: { 37 | filename: "[name].js", 38 | globalObject: "self", 39 | path: resolve(__dirname, "./dist") 40 | }, 41 | module: { 42 | rules: [ 43 | { 44 | test: /\.tsx?$/, 45 | use: "ts-loader", 46 | exclude: /node_modules/, 47 | } 48 | ] 49 | }, 50 | resolve: { 51 | extensions: [".js", ".ts"], 52 | fallback: { 53 | assert: false, 54 | child_process: false, 55 | fs: false, 56 | http: false, 57 | https: false, 58 | net: false, 59 | os: false, 60 | path: false, 61 | process: false, 62 | stream: false, 63 | tls: false, 64 | url: false, 65 | util: false, 66 | zlib: false 67 | }, 68 | modules: [ 69 | join(__dirname, "./node_modules"), 70 | ], 71 | plugins: [ 72 | new ResolveTypeScriptPlugin() 73 | ] 74 | }, 75 | plugins: [ 76 | new CopyPlugin({ 77 | patterns: [ 78 | { from: "./node_modules/web-tree-sitter/tree-sitter.wasm", to: "./" }, 79 | { from: "./node_modules/tree-sitter-modelica/tree-sitter-modelica.wasm", to: "./" }, 80 | ], 81 | }) 82 | ], 83 | performance: { 84 | hints: false, 85 | maxEntrypointSize: 512000, 86 | maxAssetSize: 512000 87 | } 88 | } 89 | }; 90 | -------------------------------------------------------------------------------- /src/common/dom.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * OMFrontend.js 3 | * Copyright (C) 2022 Perpetual Labs, Ltd. 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU Affero General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU Affero General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Affero General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | /** 20 | * @author Mohamad Omar Nachawati 21 | */ 22 | 23 | import { BufferedPrintWriter, PrintWriter } from './writer.js'; 24 | 25 | export abstract class XmlNode { 26 | 27 | #parent?: XmlElement; 28 | 29 | constructor(parent?: XmlElement) { 30 | this.#parent = parent; 31 | } 32 | 33 | get parent(): XmlElement | undefined { 34 | return this.#parent; 35 | } 36 | 37 | abstract serialize(printWriter: PrintWriter): void; 38 | 39 | } 40 | 41 | export class XmlElement extends XmlNode { 42 | 43 | #attributes: Map = new Map(); 44 | #children: XmlNode[] = []; 45 | #styles: Map = new Map(); 46 | #type: string; 47 | 48 | constructor(type: string, parent?: XmlElement) { 49 | super(parent); 50 | this.#type = type; 51 | } 52 | 53 | append(type: string): XmlElement { 54 | let element = new XmlElement(type, this); 55 | this.#children.push(element); 56 | return element; 57 | } 58 | 59 | appendText(value: any): XmlText { 60 | let text = new XmlText(String(value), this); 61 | this.#children.push(text); 62 | return text; 63 | } 64 | 65 | attr(name: string): string | undefined 66 | attr(name: string, value: any): XmlElement 67 | attr(name: string, value?: any): XmlElement | string | undefined { 68 | if (value == null) 69 | return this.#attributes.get(name); 70 | this.#attributes.set(name, value); 71 | return this; 72 | } 73 | 74 | get attributes(): Map { 75 | return this.#attributes; 76 | } 77 | 78 | get children(): XmlNode[] { 79 | return this.#children; 80 | } 81 | 82 | override serialize(printWriter: PrintWriter): void { 83 | printWriter.print("<" + this.#type); 84 | for (let attribute of this.#attributes.entries()) 85 | printWriter.print(` ${attribute[0]}="${attribute[1]}"`); 86 | if (this.#styles.size > 0) { 87 | printWriter.print(` style="`); 88 | for (let style of this.#styles.entries()) 89 | printWriter.print(` ${style[0]}:${style[1]};`); 90 | printWriter.print(`"`); 91 | } 92 | printWriter.print(">"); 93 | for (let child of this.#children) 94 | child.serialize(printWriter); 95 | printWriter.print(""); 96 | } 97 | 98 | style(name: string): string | undefined 99 | style(name: string, value: any): XmlElement 100 | style(name: string, value?: string): XmlElement | string | undefined { 101 | if (value == null) 102 | return this.#styles.get(name); 103 | this.#styles.set(name, value); 104 | return this; 105 | } 106 | 107 | get styles(): Map { 108 | return this.#styles; 109 | } 110 | 111 | override toString(): string { 112 | let printWriter = new BufferedPrintWriter(); 113 | this.serialize(printWriter); 114 | return printWriter.toString(); 115 | } 116 | 117 | } 118 | 119 | export class XmlText extends XmlNode { 120 | 121 | #value: string; 122 | 123 | constructor(value: string, parent?: XmlElement) { 124 | super(parent); 125 | this.#value = value; 126 | } 127 | 128 | override serialize(printWriter: PrintWriter): void { 129 | printWriter.print(this.#value); 130 | } 131 | 132 | get value(): string { 133 | return this.#value; 134 | } 135 | 136 | set value(value: string) { 137 | this.#value = value; 138 | } 139 | 140 | } 141 | -------------------------------------------------------------------------------- /src/common/parser.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * OMFrontend.js 3 | * Copyright (C) 2022 Perpetual Labs, Ltd. 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU Affero General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU Affero General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Affero General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | /** 20 | * @author Mohamad Omar Nachawati 21 | */ 22 | 23 | export type Edit = { 24 | startIndex: number; 25 | oldEndIndex: number; 26 | newEndIndex: number; 27 | startPosition: Point; 28 | oldEndPosition: Point; 29 | newEndPosition: Point; 30 | }; 31 | 32 | export type InputReader = (index: any, position?: Point) => string; 33 | 34 | export interface Parser { 35 | parse(input: string | InputReader, previousTree?: Tree): Tree; 36 | } 37 | 38 | export type Point = { 39 | row: number; 40 | column: number; 41 | }; 42 | 43 | export type Range = { 44 | startPosition: Point; 45 | endPosition: Point; 46 | startIndex: number; 47 | endIndex: number; 48 | }; 49 | 50 | export interface SyntaxNode { 51 | 52 | childCount: number; 53 | children: Array; 54 | endIndex: number; 55 | endPosition: Point; 56 | firstChild: SyntaxNode | null; 57 | firstNamedChild: SyntaxNode | null; 58 | lastChild: SyntaxNode | null; 59 | lastNamedChild: SyntaxNode | null; 60 | namedChildCount: number; 61 | namedChildren: Array; 62 | nextNamedSibling: SyntaxNode | null; 63 | nextSibling: SyntaxNode | null; 64 | parent: SyntaxNode | null; 65 | previousNamedSibling: SyntaxNode | null; 66 | previousSibling: SyntaxNode | null; 67 | startIndex: number; 68 | startPosition: Point; 69 | text: string; 70 | tree: Tree; 71 | type: string; 72 | hasChanges: boolean; 73 | hasError: boolean; 74 | isMissing: boolean; 75 | 76 | toString(): string; 77 | walk(): TreeCursor; 78 | 79 | } 80 | 81 | export interface Tree { 82 | 83 | readonly rootNode: SyntaxNode; 84 | 85 | edit(delta: Edit): Tree; 86 | getChangedRanges(other: Tree): Range[]; 87 | getEditedRange(other: Tree): Range; 88 | 89 | } 90 | 91 | export interface TreeCursor { 92 | 93 | nodeType: string; 94 | nodeText: string; 95 | nodeIsNamed: boolean; 96 | startPosition: Point; 97 | endPosition: Point; 98 | startIndex: number; 99 | endIndex: number; 100 | 101 | reset(node: SyntaxNode): void 102 | gotoParent(): boolean; 103 | gotoFirstChild(): boolean; 104 | gotoFirstChildForIndex(index: number): boolean; 105 | gotoNextSibling(): boolean; 106 | 107 | } 108 | 109 | export function currentFieldName(cursor: TreeCursor): string { 110 | 111 | let currentFieldName = (cursor).currentFieldName; 112 | 113 | if (typeof currentFieldName === "function") 114 | return currentFieldName.bind(cursor)(); 115 | 116 | else 117 | return currentFieldName; 118 | 119 | } 120 | 121 | export function currentNode(cursor: TreeCursor): SyntaxNode { 122 | 123 | let currentNode = (cursor).currentNode; 124 | 125 | if (typeof currentNode === "function") 126 | return currentNode.bind(cursor)(); 127 | 128 | else 129 | return currentNode; 130 | 131 | } 132 | 133 | 134 | export function childForFieldName(syntaxNode: SyntaxNode | null | undefined, ...fieldNames: string[]): SyntaxNode | undefined { 135 | 136 | if (syntaxNode == null) 137 | return; 138 | 139 | for (let child of childrenForFieldName(syntaxNode, ...fieldNames)) 140 | return child; 141 | 142 | } 143 | 144 | export function* childrenForFieldName(syntaxNode: SyntaxNode | null | undefined, ...fieldNames: string[]): IterableIterator { 145 | 146 | let cursor = syntaxNode?.walk(); 147 | 148 | if (cursor == null || !cursor.gotoFirstChild()) 149 | return; 150 | 151 | do { 152 | 153 | if (!fieldNames.includes(currentFieldName(cursor))) 154 | continue; 155 | 156 | yield currentNode(cursor); 157 | 158 | } while (cursor.gotoNextSibling()); 159 | 160 | } 161 | -------------------------------------------------------------------------------- /src/common/document.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * OMFrontend.js 3 | * Copyright (C) 2022 Perpetual Labs, Ltd. 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU Affero General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU Affero General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Affero General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | /** 20 | * @author Mohamad Omar Nachawati 21 | */ 22 | 23 | import { Position, Range, TextDocument, TextDocumentContentChangeEvent, TextEdit } from "vscode-languageserver-textdocument"; 24 | 25 | import { ModelicaContext } from "./context.js"; 26 | import { Point, SyntaxNode, Tree } from "./parser.js"; 27 | import { ModelicaScope } from "./scope.js"; 28 | import { ModelicaClassSymbol, ModelicaNamedElementSymbol } from "./symbols.js"; 29 | import { ModelicaComponentReferenceExpressionSyntax, ModelicaIdentifierSyntax, ModelicaNameSyntax, ModelicaStoredDefinitionSyntax, ModelicaTypeSpecifierSyntax } from "./syntax.js"; 30 | 31 | export class ModelicaDocument implements ModelicaScope, TextDocument { 32 | 33 | #context: ModelicaContext; 34 | #document: TextDocument; 35 | #tree: Tree; 36 | #symbols: ModelicaClassSymbol[]; 37 | #syntax: ModelicaStoredDefinitionSyntax; 38 | 39 | constructor(context: ModelicaContext, uri: string, version: number, content: string) { 40 | this.#context = context; 41 | this.#document = TextDocument.create(uri, "modelica", version, content); 42 | this.#tree = this.#context.parse(content); 43 | this.#syntax = new ModelicaStoredDefinitionSyntax(this.#tree.rootNode); 44 | this.#symbols = []; 45 | } 46 | 47 | private asPoint(position: Position): Point { 48 | return { 49 | column: position.character, 50 | row: position.line 51 | }; 52 | } 53 | 54 | get context(): ModelicaContext { 55 | return this.#context; 56 | } 57 | 58 | getText(range?: Range): string { 59 | return this.#document.getText(range); 60 | } 61 | 62 | get languageId(): string { 63 | return this.#document.languageId; 64 | } 65 | 66 | get lineCount(): number { 67 | return this.#document.lineCount; 68 | } 69 | 70 | offsetAt(position: Position): number { 71 | return this.#document.offsetAt(position); 72 | } 73 | 74 | positionAt(offset: number): Position { 75 | return this.#document.positionAt(offset); 76 | } 77 | 78 | async resolve(reference: ModelicaIdentifierSyntax | ModelicaNameSyntax | ModelicaTypeSpecifierSyntax | string[] | string | null | undefined, global?: boolean): Promise { 79 | throw new Error("Method not implemented."); 80 | } 81 | 82 | async resolveFunction(reference: ModelicaComponentReferenceExpressionSyntax | ModelicaIdentifierSyntax | ModelicaNameSyntax | ModelicaTypeSpecifierSyntax | string[] | string | null | undefined, global?: boolean): Promise { 83 | return null; 84 | } 85 | 86 | get syntax(): ModelicaStoredDefinitionSyntax { 87 | return this.#syntax; 88 | } 89 | 90 | * syntaxErrors(node?: SyntaxNode): IterableIterator { 91 | 92 | if (node == null) 93 | node = this.tree.rootNode; 94 | 95 | if (!node.hasError) 96 | return; 97 | 98 | if (node.type === "ERROR" || node.isMissing) 99 | yield node; 100 | 101 | for (let child of node.children) 102 | yield* this.syntaxErrors(child); 103 | 104 | } 105 | 106 | get tree(): Tree { 107 | return this.#tree; 108 | } 109 | 110 | update(text: string, range?: Range): void { 111 | 112 | if (range == null) { 113 | 114 | TextDocument.update(this.#document, [{ text: text }], this.#document.version + 1); 115 | this.#tree = this.#context.parse(this.#document.getText()); 116 | 117 | } else { 118 | 119 | let startIndex = this.offsetAt(range.start); 120 | let oldEndIndex = this.offsetAt(range.end); 121 | let startPosition = this.asPoint(this.positionAt(startIndex)); 122 | let oldEndPosition = this.asPoint(this.positionAt(oldEndIndex)); 123 | let newEndIndex = startIndex + text.length; 124 | 125 | TextDocument.update(this.#document, [{ range: range, text: text }], this.#document.version + 1); 126 | let newEndPosition = this.asPoint(this.positionAt(newEndIndex)); 127 | 128 | this.#tree.edit({ 129 | newEndIndex: newEndIndex, 130 | newEndPosition: newEndPosition, 131 | oldEndIndex: oldEndIndex, 132 | oldEndPosition: oldEndPosition, 133 | startIndex: startIndex, 134 | startPosition: startPosition 135 | }); 136 | 137 | this.#tree = this.#context.parse((index: number, position?: Point) => { 138 | 139 | if (position != null) { 140 | return this.getText({ 141 | end: { 142 | character: position.column + 1, 143 | line: position.row, 144 | }, 145 | start: { 146 | character: position.column, 147 | line: position.row, 148 | } 149 | }); 150 | } 151 | 152 | return this.getText({ 153 | end: this.positionAt(index + 1), 154 | start: this.positionAt(index) 155 | }); 156 | 157 | }, this.#tree); 158 | 159 | } 160 | 161 | this.#syntax = new ModelicaStoredDefinitionSyntax(this.#tree.rootNode); 162 | 163 | } 164 | 165 | get uri(): string { 166 | return this.#document.uri; 167 | } 168 | 169 | get version(): number { 170 | return this.#document.version; 171 | } 172 | 173 | } 174 | -------------------------------------------------------------------------------- /src/common/library.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * OMFrontend.js 3 | * Copyright (C) 2022 Perpetual Labs, Ltd. 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU Affero General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU Affero General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Affero General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | /** 20 | * @author Mohamad Omar Nachawati 21 | */ 22 | 23 | import * as JSZip from "jszip"; 24 | 25 | import { ModelicaContext } from "./context.js"; 26 | import { ModelicaScope } from "./scope.js"; 27 | import { ModelicaClassSymbol, ModelicaNamedElementSymbol } from "./symbols.js"; 28 | import { ModelicaComponentReferenceExpressionSyntax, ModelicaIdentifierSyntax, ModelicaNameSyntax, ModelicaStoredDefinitionSyntax, ModelicaTypeSpecifierSyntax } from "./syntax.js"; 29 | import { getIdentifiers } from "./util.js"; 30 | 31 | export abstract class ModelicaLibrary implements ModelicaScope { 32 | 33 | #context: ModelicaContext; 34 | #symbols: Map = new Map(); 35 | 36 | constructor(context: ModelicaContext) { 37 | this.#context = context; 38 | } 39 | 40 | get context(): ModelicaContext { 41 | return this.#context; 42 | } 43 | 44 | abstract list(...filePath: string[]): AsyncIterableIterator; 45 | 46 | async load(...filePath: string[]): Promise { 47 | 48 | // TODO: implement caching... 49 | 50 | if (filePath == null || filePath.length == 0) 51 | return null; 52 | 53 | let text = null; 54 | let fileName = filePath[filePath.length - 1]; 55 | 56 | if (fileName.endsWith(".mo")) 57 | text = await this.read(...filePath); 58 | 59 | else 60 | text = await this.read(...filePath, "package.mo"); 61 | 62 | if (text == null) 63 | return null; 64 | 65 | let tree = this.#context.parse(text); 66 | 67 | if (fileName.endsWith(".mo")) 68 | return ModelicaStoredDefinitionSyntax.new(tree.rootNode) ?? null; 69 | 70 | return ModelicaStoredDefinitionSyntax.new(tree.rootNode, this, filePath) ?? null; 71 | 72 | } 73 | 74 | async resolve(reference: ModelicaIdentifierSyntax | ModelicaNameSyntax | ModelicaTypeSpecifierSyntax | string[] | string | null | undefined, global?: boolean): Promise { 75 | 76 | // TODO: structured entity should not have more than one class definition 77 | 78 | let identifiers = getIdentifiers(reference); 79 | let identifier = identifiers[0]; 80 | 81 | if (identifier == null) 82 | return null; 83 | 84 | let classSymbols = null; 85 | 86 | if (this.#symbols.get(identifier) !== undefined) { 87 | 88 | classSymbols = this.#symbols.get(identifier); 89 | 90 | } else { 91 | 92 | let text = await this.read(identifier + ".mo"); 93 | 94 | if (text != null) { 95 | 96 | let tree = this.#context.parse(text); 97 | let node = ModelicaStoredDefinitionSyntax.new(tree.rootNode); 98 | classSymbols = await node?.instantiate(this.#context); 99 | 100 | } else { 101 | 102 | text = await this.read(identifier, "package.mo"); 103 | 104 | if (text != null) { 105 | let tree = this.#context.parse(text); 106 | let node = ModelicaStoredDefinitionSyntax.new(tree.rootNode, this, [identifier]); 107 | classSymbols = await node?.instantiate(this.#context); 108 | } 109 | 110 | } 111 | 112 | this.#symbols.set(identifier, classSymbols ?? null); 113 | 114 | } 115 | 116 | if (classSymbols != null) { 117 | 118 | identifiers = identifiers.slice(1); 119 | 120 | if (identifiers.length > 0) 121 | return classSymbols[0]?.resolve(identifiers) ?? null; 122 | 123 | return classSymbols[0] ?? null; 124 | 125 | } 126 | 127 | return null; 128 | 129 | } 130 | 131 | async resolveFunction(reference: ModelicaComponentReferenceExpressionSyntax | ModelicaIdentifierSyntax | ModelicaNameSyntax | ModelicaTypeSpecifierSyntax | string[] | string | null | undefined, global?: boolean): Promise { 132 | return null; 133 | } 134 | 135 | abstract read(...filePath: string[]): Promise; 136 | 137 | } 138 | 139 | export abstract class ModelicaFileSystemLibrary extends ModelicaLibrary { 140 | 141 | constructor(context: ModelicaContext) { 142 | super(context); 143 | } 144 | 145 | } 146 | 147 | export class ModelicaWebLibrary extends ModelicaLibrary { 148 | 149 | constructor(context: ModelicaContext) { 150 | super(context); 151 | } 152 | 153 | override list(...filePath: string[]): AsyncIterableIterator { 154 | throw new Error("Method not implemented."); 155 | } 156 | 157 | override read(...filePath: string[]): Promise { 158 | throw new Error("Method not implemented."); 159 | } 160 | 161 | } 162 | 163 | export class ModelicaZipLibrary extends ModelicaLibrary { 164 | 165 | #rootPath?: string; 166 | #zip: JSZip; 167 | 168 | constructor(context: ModelicaContext, zip: JSZip, rootPath?: string | string[]) { 169 | super(context); 170 | this.#zip = zip; 171 | this.#rootPath = Array.isArray(rootPath) ? rootPath.join("/") : rootPath; 172 | } 173 | 174 | override async *list(...filePath: string[]): AsyncIterableIterator { 175 | 176 | if (filePath.length > 0) { 177 | 178 | let firstName = filePath[0]; 179 | 180 | for await (let name of this.list()) { 181 | 182 | if (name == firstName || name.startsWith(firstName + " ")) { 183 | filePath[0] = name; 184 | break; 185 | } 186 | 187 | } 188 | 189 | } 190 | 191 | let name = null; 192 | 193 | if (this.#rootPath == null) 194 | name = filePath.join("/"); 195 | 196 | else 197 | name = this.#rootPath + "/" + filePath.join("/"); 198 | 199 | let list: string[] = []; 200 | 201 | this.#zip.folder(name)?.forEach((relativePath: string, file: JSZip.JSZipObject) => { 202 | 203 | if (file.name.endsWith("/package.mo")) 204 | return; 205 | 206 | let relativePathParts = relativePath.split("/"); 207 | 208 | if (file.dir == true && relativePathParts.length == 2) 209 | list.push(relativePathParts[0]); 210 | 211 | else if (file.name.endsWith(".mo") && relativePathParts.length == 1) 212 | list.push(relativePathParts[0]); 213 | 214 | }); 215 | 216 | yield* list; 217 | 218 | } 219 | 220 | override async read(...filePath: string[]): Promise { 221 | 222 | if (filePath.length == 0) 223 | return null; 224 | 225 | let firstName = filePath[0]; 226 | 227 | for await (let name of this.list()) { 228 | 229 | if (name == firstName || name.startsWith(firstName + " ")) { 230 | filePath[0] = name; 231 | break; 232 | } 233 | 234 | } 235 | 236 | let name = null; 237 | 238 | if (this.#rootPath == null) 239 | name = filePath.join("/"); 240 | 241 | else 242 | name = this.#rootPath + "/" + filePath.join("/"); 243 | 244 | //console.log("READ ZIP FILE: " + name); 245 | 246 | return this.#zip.file(name)?.async("string") ?? null; 247 | 248 | } 249 | 250 | } 251 | -------------------------------------------------------------------------------- /src/common/context.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * OMFrontend.js 3 | * Copyright (C) 2022 Perpetual Labs, Ltd. 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU Affero General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU Affero General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Affero General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | /** 20 | * @author Mohamad Omar Nachawati 21 | */ 22 | 23 | import * as JSZip from "jszip"; 24 | 25 | import { ModelicaDocument } from "./document.js"; 26 | import { ModelicaLibrary, ModelicaZipLibrary } from "./library.js"; 27 | import { InputReader, Tree } from "./parser.js"; 28 | import { MODELICA_PREDEFINED } from "./predefined.js"; 29 | import { ModelicaScope } from "./scope.js"; 30 | import { ModelicaArrayClassSymbol, ModelicaBooleanClassSymbol, ModelicaClassSymbol, ModelicaIntegerClassSymbol, ModelicaNamedElementSymbol, ModelicaNumberObjectSymbol, ModelicaRealClassSymbol, ModelicaStringClassSymbol } from "./symbols.js"; 31 | import { ModelicaComponentReferenceExpressionSyntax, ModelicaIdentifierSyntax, ModelicaNameSyntax, ModelicaStoredDefinitionSyntax, ModelicaTypeSpecifierSyntax } from "./syntax.js"; 32 | 33 | export abstract class ModelicaContext implements ModelicaScope { 34 | 35 | static #annotations?: ModelicaClassSymbol; 36 | #builtins: Map = new Map(); 37 | #documents: Map = new Map(); 38 | #libraries: ModelicaLibrary[]; 39 | #workspace?: ModelicaLibrary; 40 | 41 | constructor(workspace?: ModelicaLibrary, libraries?: ModelicaLibrary[]) { 42 | this.#workspace = workspace; 43 | this.#libraries = libraries ?? []; 44 | this.#builtins = new Map(); 45 | this.#builtins.set("Boolean", new ModelicaBooleanClassSymbol(this)); 46 | this.#builtins.set("Integer", new ModelicaIntegerClassSymbol(this)); 47 | this.#builtins.set("Real", new ModelicaRealClassSymbol(this)); 48 | this.#builtins.set("String", new ModelicaStringClassSymbol(this)); 49 | } 50 | 51 | abstract addLibrary(path: string): ModelicaLibrary; 52 | 53 | addZipLibrary(zip: JSZip, rootPath?: string | string[]): ModelicaZipLibrary { 54 | let library = new ModelicaZipLibrary(this, zip, rootPath); 55 | this.libraries.push(library); 56 | return library; 57 | } 58 | 59 | get annotations(): Promise { 60 | 61 | let context = this; 62 | 63 | return async function () { 64 | 65 | if (ModelicaContext.#annotations != null) 66 | return ModelicaContext.#annotations; 67 | 68 | let tree = context.parse(MODELICA_PREDEFINED); 69 | 70 | let node = ModelicaStoredDefinitionSyntax.new(tree.rootNode); 71 | 72 | ModelicaContext.#annotations = (await node?.instantiate(context))?.[0] ?? new ModelicaClassSymbol(context); 73 | 74 | return ModelicaContext.#annotations; 75 | 76 | }(); 77 | 78 | } 79 | 80 | get builtins(): Map { 81 | return this.#builtins; 82 | } 83 | 84 | get roots(): AsyncIterableIterator { 85 | 86 | let context = this; 87 | 88 | return async function* () { 89 | 90 | for (let library of context.libraries) { 91 | 92 | let root = await library.resolve("Modelica"); 93 | 94 | if (root instanceof ModelicaClassSymbol) 95 | yield root; 96 | 97 | root = await library.resolve("PL_Lib"); 98 | 99 | if (root instanceof ModelicaClassSymbol) 100 | yield root; 101 | 102 | root = await library.resolve("ThermoPower"); 103 | 104 | if (root instanceof ModelicaClassSymbol) 105 | yield root; 106 | 107 | root = await library.resolve("FirstBookExamples"); 108 | 109 | if (root instanceof ModelicaClassSymbol) 110 | yield root; 111 | 112 | } 113 | 114 | }(); 115 | 116 | } 117 | 118 | get context(): ModelicaContext { 119 | return this; 120 | } 121 | 122 | get documents(): Map { 123 | return this.#documents; 124 | } 125 | 126 | get libraries(): ModelicaLibrary[] { 127 | return this.#libraries; 128 | } 129 | 130 | async resolve(reference: ModelicaIdentifierSyntax | ModelicaNameSyntax | ModelicaTypeSpecifierSyntax | string[] | string | null | undefined, global?: boolean): Promise { 131 | 132 | if (reference == null) 133 | return null; 134 | 135 | for (let document of this.documents.values()) { 136 | 137 | let symbol = await document.resolve(reference, global); 138 | 139 | if (symbol != null) 140 | return symbol; 141 | 142 | } 143 | 144 | let symbol = await this.workspace?.resolve(reference, global); 145 | 146 | if (symbol != null) 147 | return symbol; 148 | 149 | for (let library of this.libraries) { 150 | 151 | let symbol = await library.resolve(reference, global); 152 | 153 | if (symbol != null) 154 | return symbol; 155 | 156 | } 157 | 158 | let name: string | null | undefined = null; 159 | 160 | if (reference instanceof ModelicaIdentifierSyntax) 161 | name = reference.toString(); 162 | 163 | else if (reference instanceof ModelicaNameSyntax) 164 | name = reference.toString(); 165 | 166 | else if (reference instanceof ModelicaTypeSpecifierSyntax) 167 | name = reference.name?.toString(); 168 | 169 | else if (Array.isArray(reference)) 170 | name = reference.join("."); 171 | 172 | else 173 | name = reference; 174 | 175 | if (name != null) { 176 | 177 | symbol = (await (await this.annotations).getNamedElement(name)) ?? this.builtins.get(name) ?? null; 178 | 179 | if (reference instanceof ModelicaTypeSpecifierSyntax && reference.subscripts != null && reference.subscripts.length > 0) { 180 | 181 | if (symbol instanceof ModelicaClassSymbol) { 182 | 183 | let shape = []; 184 | 185 | for (let subscript of reference.subscripts) { 186 | 187 | let value = await subscript.expression?.evaluate(this); 188 | 189 | if (value instanceof ModelicaNumberObjectSymbol) 190 | shape.push(value.value); 191 | 192 | else 193 | shape.push(undefined); 194 | 195 | } 196 | 197 | return new ModelicaArrayClassSymbol(symbol.parent, undefined, undefined, symbol, shape); 198 | 199 | } 200 | 201 | return null; 202 | 203 | } 204 | 205 | return symbol; 206 | 207 | } 208 | 209 | return null; 210 | 211 | } 212 | 213 | async resolveFunction(reference: ModelicaComponentReferenceExpressionSyntax | ModelicaIdentifierSyntax | ModelicaNameSyntax | ModelicaTypeSpecifierSyntax | string[] | string | null | undefined, global?: boolean): Promise { 214 | 215 | if (reference == null) 216 | return null; 217 | 218 | for (let document of this.documents.values()) { 219 | 220 | let symbol = await document.resolveFunction(reference, global); 221 | 222 | if (symbol != null) 223 | return symbol; 224 | 225 | } 226 | 227 | let symbol = await this.workspace?.resolveFunction(reference, global); 228 | 229 | if (symbol != null) 230 | return symbol; 231 | 232 | for (let library of this.libraries) { 233 | 234 | let symbol = await library.resolveFunction(reference, global); 235 | 236 | if (symbol != null) 237 | return symbol; 238 | 239 | } 240 | 241 | return null; 242 | 243 | } 244 | 245 | 246 | abstract parse(input: string | InputReader, previousTree?: Tree): Tree; 247 | 248 | get workspace(): ModelicaLibrary | undefined { 249 | return this.#workspace; 250 | } 251 | 252 | } 253 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.toptal.com/developers/gitignore/api/maven,git,eclipse,visualstudiocode,java,python,c++,node,angular 3 | # Edit at https://www.toptal.com/developers/gitignore?templates=maven,git,eclipse,visualstudiocode,java,python,c++,node,angular 4 | 5 | ### Angular ### 6 | ## Angular ## 7 | # compiled output 8 | dist/ 9 | tmp/ 10 | app/**/*.js 11 | app/**/*.js.map 12 | 13 | # dependencies 14 | node_modules/ 15 | bower_components/ 16 | 17 | # IDEs and editors 18 | .idea/ 19 | 20 | # misc 21 | .sass-cache/ 22 | connect.lock/ 23 | coverage/ 24 | libpeerconnection.log/ 25 | npm-debug.log 26 | testem.log 27 | typings/ 28 | 29 | # e2e 30 | e2e/*.js 31 | e2e/*.map 32 | 33 | # System Files 34 | .DS_Store/ 35 | 36 | ### C++ ### 37 | # Prerequisites 38 | *.d 39 | 40 | # Compiled Object files 41 | *.slo 42 | *.lo 43 | *.o 44 | *.obj 45 | 46 | # Precompiled Headers 47 | *.gch 48 | *.pch 49 | 50 | # Linker files 51 | *.ilk 52 | 53 | # Debugger Files 54 | *.pdb 55 | 56 | # Compiled Dynamic libraries 57 | *.so 58 | *.dylib 59 | *.dll 60 | 61 | # Fortran module files 62 | *.mod 63 | *.smod 64 | 65 | # Compiled Static libraries 66 | *.lai 67 | *.la 68 | *.a 69 | *.lib 70 | 71 | # Executables 72 | *.exe 73 | *.out 74 | *.app 75 | 76 | ### Eclipse ### 77 | .metadata 78 | bin/ 79 | *.tmp 80 | *.bak 81 | *.swp 82 | *~.nib 83 | local.properties 84 | .settings/ 85 | .loadpath 86 | .recommenders 87 | 88 | # External tool builders 89 | .externalToolBuilders/ 90 | 91 | # Locally stored "Eclipse launch configurations" 92 | *.launch 93 | 94 | # PyDev specific (Python IDE for Eclipse) 95 | *.pydevproject 96 | 97 | # CDT-specific (C/C++ Development Tooling) 98 | .cproject 99 | 100 | # CDT- autotools 101 | .autotools 102 | 103 | # Java annotation processor (APT) 104 | .factorypath 105 | 106 | # PDT-specific (PHP Development Tools) 107 | .buildpath 108 | 109 | # sbteclipse plugin 110 | .target 111 | 112 | # Tern plugin 113 | .tern-project 114 | 115 | # TeXlipse plugin 116 | .texlipse 117 | 118 | # STS (Spring Tool Suite) 119 | .springBeans 120 | 121 | # Code Recommenders 122 | .recommenders/ 123 | 124 | # Annotation Processing 125 | .apt_generated/ 126 | .apt_generated_test/ 127 | 128 | # Scala IDE specific (Scala & Java development for Eclipse) 129 | .cache-main 130 | .scala_dependencies 131 | .worksheet 132 | 133 | # Uncomment this line if you wish to ignore the project description file. 134 | # Typically, this file would be tracked if it contains build/dependency configurations: 135 | #.project 136 | 137 | ### Eclipse Patch ### 138 | # Spring Boot Tooling 139 | .sts4-cache/ 140 | 141 | ### Git ### 142 | # Created by git for backups. To disable backups in Git: 143 | # $ git config --global mergetool.keepBackup false 144 | *.orig 145 | 146 | # Created by git when using merge tools for conflicts 147 | *.BACKUP.* 148 | *.BASE.* 149 | *.LOCAL.* 150 | *.REMOTE.* 151 | *_BACKUP_*.txt 152 | *_BASE_*.txt 153 | *_LOCAL_*.txt 154 | *_REMOTE_*.txt 155 | 156 | ### Java ### 157 | # Compiled class file 158 | *.class 159 | 160 | # Log file 161 | *.log 162 | 163 | # BlueJ files 164 | *.ctxt 165 | 166 | # Mobile Tools for Java (J2ME) 167 | .mtj.tmp/ 168 | 169 | # Package Files # 170 | *.jar 171 | *.war 172 | *.nar 173 | *.ear 174 | *.zip 175 | *.tar.gz 176 | *.rar 177 | 178 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 179 | hs_err_pid* 180 | 181 | ### Maven ### 182 | target/ 183 | pom.xml.tag 184 | pom.xml.releaseBackup 185 | pom.xml.versionsBackup 186 | pom.xml.next 187 | release.properties 188 | dependency-reduced-pom.xml 189 | buildNumber.properties 190 | .mvn/timing.properties 191 | # https://github.com/takari/maven-wrapper#usage-without-binary-jar 192 | .mvn/wrapper/maven-wrapper.jar 193 | .flattened-pom.xml 194 | 195 | ### Node ### 196 | # Logs 197 | logs 198 | npm-debug.log* 199 | yarn-debug.log* 200 | yarn-error.log* 201 | lerna-debug.log* 202 | 203 | # Diagnostic reports (https://nodejs.org/api/report.html) 204 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 205 | 206 | # Runtime data 207 | pids 208 | *.pid 209 | *.seed 210 | *.pid.lock 211 | 212 | # Directory for instrumented libs generated by jscoverage/JSCover 213 | lib-cov 214 | 215 | # Coverage directory used by tools like istanbul 216 | coverage 217 | *.lcov 218 | 219 | # nyc test coverage 220 | .nyc_output 221 | 222 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 223 | .grunt 224 | 225 | # Bower dependency directory (https://bower.io/) 226 | bower_components 227 | 228 | # node-waf configuration 229 | .lock-wscript 230 | 231 | # Compiled binary addons (https://nodejs.org/api/addons.html) 232 | build/Release 233 | 234 | # Dependency directories 235 | jspm_packages/ 236 | 237 | # TypeScript v1 declaration files 238 | 239 | # TypeScript cache 240 | *.tsbuildinfo 241 | 242 | # Optional npm cache directory 243 | .npm 244 | 245 | # Optional eslint cache 246 | .eslintcache 247 | 248 | # Optional stylelint cache 249 | .stylelintcache 250 | 251 | # Microbundle cache 252 | .rpt2_cache/ 253 | .rts2_cache_cjs/ 254 | .rts2_cache_es/ 255 | .rts2_cache_umd/ 256 | 257 | # Optional REPL history 258 | .node_repl_history 259 | 260 | # Output of 'npm pack' 261 | *.tgz 262 | 263 | # Yarn Integrity file 264 | .yarn-integrity 265 | 266 | # dotenv environment variables file 267 | .env 268 | .env.test 269 | .env*.local 270 | 271 | # parcel-bundler cache (https://parceljs.org/) 272 | .cache 273 | .parcel-cache 274 | 275 | # Next.js build output 276 | .next 277 | 278 | # Nuxt.js build / generate output 279 | .nuxt 280 | dist 281 | 282 | # Storybook build outputs 283 | .out 284 | .storybook-out 285 | storybook-static 286 | 287 | # rollup.js default build output 288 | 289 | # Gatsby files 290 | .cache/ 291 | # Comment in the public line in if your project uses Gatsby and not Next.js 292 | # https://nextjs.org/blog/next-9-1#public-directory-support 293 | # public 294 | 295 | # vuepress build output 296 | .vuepress/dist 297 | 298 | # Serverless directories 299 | .serverless/ 300 | 301 | # FuseBox cache 302 | .fusebox/ 303 | 304 | # DynamoDB Local files 305 | .dynamodb/ 306 | 307 | # TernJS port file 308 | .tern-port 309 | 310 | # Stores VSCode versions used for testing VSCode extensions 311 | .vscode-test 312 | 313 | # Temporary folders 314 | temp/ 315 | 316 | ### Python ### 317 | # Byte-compiled / optimized / DLL files 318 | __pycache__/ 319 | *.py[cod] 320 | *$py.class 321 | 322 | # C extensions 323 | 324 | # Distribution / packaging 325 | .Python 326 | build/ 327 | develop-eggs/ 328 | downloads/ 329 | eggs/ 330 | .eggs/ 331 | parts/ 332 | sdist/ 333 | var/ 334 | wheels/ 335 | pip-wheel-metadata/ 336 | share/python-wheels/ 337 | *.egg-info/ 338 | .installed.cfg 339 | *.egg 340 | MANIFEST 341 | 342 | # PyInstaller 343 | # Usually these files are written by a python script from a template 344 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 345 | *.manifest 346 | *.spec 347 | 348 | # Installer logs 349 | pip-log.txt 350 | pip-delete-this-directory.txt 351 | 352 | # Unit test / coverage reports 353 | htmlcov/ 354 | .tox/ 355 | .nox/ 356 | .coverage 357 | .coverage.* 358 | nosetests.xml 359 | coverage.xml 360 | *.cover 361 | *.py,cover 362 | .hypothesis/ 363 | .pytest_cache/ 364 | pytestdebug.log 365 | 366 | # Translations 367 | *.mo 368 | *.pot 369 | 370 | # Django stuff: 371 | local_settings.py 372 | db.sqlite3 373 | db.sqlite3-journal 374 | 375 | # Flask stuff: 376 | instance/ 377 | .webassets-cache 378 | 379 | # Scrapy stuff: 380 | .scrapy 381 | 382 | # Sphinx documentation 383 | docs/_build/ 384 | doc/_build/ 385 | 386 | # PyBuilder 387 | 388 | # Jupyter Notebook 389 | .ipynb_checkpoints 390 | 391 | # IPython 392 | profile_default/ 393 | ipython_config.py 394 | 395 | # pyenv 396 | .python-version 397 | 398 | # pipenv 399 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 400 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 401 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 402 | # install all needed dependencies. 403 | #Pipfile.lock 404 | 405 | # poetry 406 | #poetry.lock 407 | 408 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 409 | __pypackages__/ 410 | 411 | # Celery stuff 412 | celerybeat-schedule 413 | celerybeat.pid 414 | 415 | # SageMath parsed files 416 | *.sage.py 417 | 418 | # Environments 419 | # .env 420 | .env/ 421 | .venv/ 422 | env/ 423 | venv/ 424 | ENV/ 425 | env.bak/ 426 | venv.bak/ 427 | pythonenv* 428 | 429 | # Spyder project settings 430 | .spyderproject 431 | .spyproject 432 | 433 | # Rope project settings 434 | .ropeproject 435 | 436 | # mkdocs documentation 437 | /site 438 | 439 | # mypy 440 | .mypy_cache/ 441 | .dmypy.json 442 | dmypy.json 443 | 444 | # Pyre type checker 445 | .pyre/ 446 | 447 | # pytype static type analyzer 448 | .pytype/ 449 | 450 | # operating system-related files 451 | # file properties cache/storage on macOS 452 | *.DS_Store 453 | # thumbnail cache on Windows 454 | Thumbs.db 455 | 456 | # profiling data 457 | .prof 458 | 459 | 460 | ### VisualStudioCode ### 461 | .vscode/* 462 | !.vscode/settings.json 463 | !.vscode/tasks.json 464 | !.vscode/launch.json 465 | !.vscode/extensions.json 466 | *.code-workspace 467 | 468 | ### VisualStudioCode Patch ### 469 | # Ignore all local history of files 470 | .history 471 | .ionide 472 | 473 | # End of https://www.toptal.com/developers/gitignore/api/maven,git,eclipse,visualstudiocode,java,python,c++,node,angular 474 | 475 | *.wasm -------------------------------------------------------------------------------- /src/common/predefined.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * OMFrontend.js 3 | * Copyright (C) 2022 Perpetual Labs, Ltd. 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU Affero General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU Affero General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Affero General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | /** 20 | * @author Mohamad Omar Nachawati 21 | */ 22 | 23 | export const MODELICA_PREDEFINED = ` 24 | /* 25 | * Copyright © 1998-2020, Modelica Association (https://www.modelica.org) 26 | * 27 | * All rights reserved. Reproduction or use of editorial or pictorial content is permitted, i.e., this document can 28 | * be freely distributed especially electronically, provided the copyright notice and these conditions are retained. 29 | * No patent liability is assumed with respect to the use of information contained herein. While every precaution 30 | * has been taken in the preparation of this document no responsibility for errors or omissions is assumed. 31 | */ 32 | 33 | package ModelicaAnnotations 34 | 35 | type Arrow = enumeration(None, Open, Filled, Half); 36 | 37 | record Authorization 38 | String licensor = "" "Optional string to show information about the licensor"; 39 | String libraryKey "Matching the key in the class. Must be encrypted and not visible"; 40 | License license[:] "Definition of the license options and of the access rights"; 41 | end Authorization; 42 | 43 | record Axis 44 | Real min "Axis lower bound, in ’unit’"; 45 | Real max "Axis upper bound, in ’unit’"; 46 | String unit = "" "Unit of axis tick labels"; 47 | String label "Axis label"; 48 | end Axis; 49 | 50 | record Bitmap 51 | extends GraphicItem; 52 | Extent extent; 53 | String fileName "Name of bitmap file"; 54 | String imageSource "Base64 representation of bitmap"; 55 | end Bitmap; 56 | 57 | constant Color Black = {0, 0, 0}; 58 | 59 | type BorderPattern = enumeration(None, Raised, Sunken, Engraved); 60 | 61 | type Color = Integer[3](min = 0, max = 255) "RGB representation"; 62 | 63 | record CoordinateSystem 64 | Extent extent; 65 | Boolean preserveAspectRatio = true; 66 | Real initialScale = 0.1; 67 | DrawingUnit grid[2]; 68 | end CoordinateSystem; 69 | 70 | record Curve 71 | expression x = time "X coordinate values"; 72 | expression y "Y coordinate values"; 73 | String legend "Legend"; 74 | end Curve; 75 | 76 | record Diagram "Representation of the diagram layer" 77 | CoordinateSystem coordinateSystem(extent = {{-100, -100}, {100, 100}}); 78 | GraphicItem[:] graphics; 79 | end Diagram; 80 | 81 | record DiagramMap 82 | Extent extent = {{0, 0}, {0, 0}}; 83 | Boolean primitivesVisible = true; 84 | end DiagramMap; 85 | 86 | record Dialog 87 | parameter String tab = "General"; 88 | parameter String group = "Parameters"; 89 | parameter Boolean enable = true; 90 | parameter Boolean showStartAttribute = false; 91 | parameter Boolean colorSelector = false; 92 | parameter Selector loadSelector; 93 | parameter Selector saveSelector; 94 | parameter String groupImage = ""; 95 | parameter Boolean connectorSizing = false; 96 | end Dialog; 97 | 98 | record Documentation 99 | String info = "" "Description of the class"; 100 | String revisions = "" "Revision history"; 101 | Figure[:] figures = {}; "Simulation result figures"; 102 | end Documentation; 103 | 104 | type DrawingUnit = Real(final unit="mm"); 105 | 106 | record Ellipse 107 | extends GraphicItem; 108 | extends FilledShape; 109 | Extent extent; 110 | Real startAngle(quantity = "angle", unit = "deg") = 0; 111 | Real endAngle(quantity = "angle", unit = "deg") = 360; 112 | EllipseClosure closure = if startAngle == 0 and endAngle == 360 113 | then EllipseClosure.Chord 114 | else EllipseClosure.Radial; 115 | end Ellipse; 116 | 117 | type EllipseClosure = enumeration(None, Chord, Radial); 118 | 119 | type Extent = Point[2] "Defines a rectangular area {{x1, y1}, {x2, y2}}"; 120 | 121 | record Figure 122 | String title = "" "Title meant for display"; 123 | String identifier "Identifier meant for programmatic access"; 124 | String group = "" "Name of plot group"; 125 | Boolean preferred = false "Automatically display figure after simulation"; 126 | Plot[:] plots "Plots"; 127 | String caption "Figure caption"; 128 | end Figure; 129 | 130 | record FilledShape "Style attributes for filled shapes" 131 | Color lineColor = Black "Color of border line"; 132 | Color fillColor = Black "Interior fill color"; 133 | LinePattern pattern = LinePattern.Solid "Border line pattern"; 134 | FillPattern fillPattern = FillPattern.None "Interior fill pattern"; 135 | DrawingUnit lineThickness = 0.25 "Line thickness"; 136 | end FilledShape; 137 | 138 | type FillPattern = enumeration(None, Solid, Horizontal, Vertical, 139 | Cross, Forward, Backward, CrossDiag, 140 | HorizontalCylinder, VerticalCylinder, Sphere); 141 | 142 | partial record GraphicItem 143 | Boolean visible = true; 144 | Point origin = {0, 0}; 145 | Real rotation(quantity="angle", unit="deg")=0; 146 | end GraphicItem; 147 | 148 | record Icon "Representation of the icon layer" 149 | CoordinateSystem coordinateSystem(extent = {{-100, -100}, {100, 100}}); 150 | GraphicItem[:] graphics; 151 | end Icon; 152 | 153 | record IconMap 154 | Extent extent = {{0, 0}, {0, 0}}; 155 | Boolean primitivesVisible = true; 156 | end IconMap; 157 | 158 | record License 159 | String licensee = "" "Optional string to show information about the licensee"; 160 | String id[:] "Unique machine identifications, e.g.\ MAC addresses"; 161 | String features[:] = fill("", 0) "Activated library license features"; 162 | String startDate = "" "Optional start date in UTCformat YYYY-MM-DD"; 163 | String expirationDate = "" "Optional expiration date in UTCformat YYYY-MM-DD"; 164 | String operations[:] = fill("", 0) "Library usage conditions"; 165 | end License; 166 | 167 | record Line 168 | extends GraphicItem; 169 | Point points[:]; 170 | Color color = Black; 171 | LinePattern pattern = LinePattern.Solid; 172 | DrawingUnit thickness = 0.25; 173 | Arrow arrow[2] = {Arrow.None, Arrow.None} "{start arrow, end arrow}"; 174 | DrawingUnit arrowSize = 3; 175 | Smooth smooth = Smooth.None "Spline"; 176 | end Line; 177 | 178 | type LinePattern = enumeration(None, Solid, Dash, Dot, DashDot, DashDotDot); 179 | 180 | record OnMouseDownEditInteger 181 | Integer variable "Name of variable to change"; 182 | end OnMouseDownEditInteger; 183 | 184 | record OnMouseDownEditReal 185 | Real variable "Name of variable to change"; 186 | end OnMouseDownEditReal; 187 | 188 | record OnMouseDownEditString 189 | String variable "Name of variable to change"; 190 | end OnMouseDownEditString; 191 | 192 | record OnMouseDownSetBoolean 193 | Boolean variable "Name of variable to change when mouse button pressed"; 194 | Boolean value "Assigned value"; 195 | end OnMouseDownSetBoolean; 196 | 197 | record OnMouseMoveXSetReal 198 | Real xVariable "Name of variable to change when cursor moved in x direction"; 199 | Real minValue; 200 | Real maxValue; 201 | end OnMouseMoveXSetReal; 202 | 203 | record OnMouseMoveYSetReal 204 | Real yVariable "Name of variable to change when cursor moved in y direction"; 205 | Real minValue; 206 | Real maxValue; 207 | end OnMouseMoveYSetReal; 208 | 209 | record OnMouseUpSetBoolean 210 | Boolean variable "Name of variable to change when mouse button released"; 211 | Boolean value "Assigned value"; 212 | end OnMouseUpSetBoolean; 213 | 214 | record Placement 215 | Boolean visible = true; 216 | Transformation transformation "Placement in the diagram layer"; 217 | Boolean iconVisible "Visible in icon layer; for public connector"; 218 | Transformation iconTransformation "Placement in the icon layer; for public connector"; 219 | end Placement; 220 | 221 | record Plot 222 | String title "Title meant for display"; 223 | String identifier "Identifier meant for programmatic access"; 224 | Curve[:] curves "Plot curves"; 225 | Axis x "X axis properties"; 226 | Axis y "Y axis properties"; 227 | end Plot; 228 | 229 | type Point = DrawingUnit[2] "{x, y}"; 230 | 231 | record Polygon 232 | extends GraphicItem; 233 | extends FilledShape; 234 | Point points[:]; 235 | Smooth smooth = Smooth.None "Spline outline"; 236 | end Polygon; 237 | 238 | record Rectangle 239 | extends GraphicItem; 240 | extends FilledShape; 241 | BorderPattern borderPattern = BorderPattern.None; 242 | Extent extent; 243 | DrawingUnit radius = 0 "Corner radius"; 244 | end Rectangle; 245 | 246 | record Selector 247 | parameter String filter = ""; 248 | parameter String caption = ""; 249 | end Selector; 250 | 251 | type Smooth = enumeration(None, Bezier); 252 | 253 | record Text 254 | extends GraphicItem; 255 | extends FilledShape; 256 | Extent extent; 257 | String string; 258 | String textString; 259 | Real fontSize = 0 "unit pt"; 260 | String fontName; 261 | TextStyle textStyle[:]; 262 | Color textColor = lineColor; 263 | TextAlignment horizontalAlignment = TextAlignment.Center; 264 | Integer index; 265 | end Text; 266 | 267 | type TextAlignment = enumeration(Left, Center, Right); 268 | 269 | type TextStyle = enumeration(Bold, Italic, UnderLine); 270 | 271 | record Transformation 272 | Point origin = {0, 0}; 273 | Extent extent; 274 | Real rotation(quantity = "angle", unit = "deg") = 0; 275 | end Transformation; 276 | 277 | end ModelicaAnnotations; 278 | `; -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Visit https://aka.ms/tsconfig.json to read more about this file */ 4 | 5 | /* Projects */ 6 | // "incremental": true, /* Enable incremental compilation */ 7 | // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ 8 | // "tsBuildInfoFile": "./", /* Specify the folder for .tsbuildinfo incremental compilation files. */ 9 | // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects */ 10 | // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ 11 | // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ 12 | 13 | /* Language and Environment */ 14 | "target": "es6", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ 15 | // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ 16 | // "jsx": "preserve", /* Specify what JSX code is generated. */ 17 | // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ 18 | // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ 19 | // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h' */ 20 | // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ 21 | // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using `jsx: react-jsx*`.` */ 22 | // "reactNamespace": "", /* Specify the object invoked for `createElement`. This only applies when targeting `react` JSX emit. */ 23 | // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ 24 | // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ 25 | 26 | /* Modules */ 27 | "module": "es6", /* Specify what module code is generated. */ 28 | // "rootDir": "./", /* Specify the root folder within your source files. */ 29 | "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ 30 | // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ 31 | // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ 32 | // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ 33 | // "typeRoots": [], /* Specify multiple folders that act like `./node_modules/@types`. */ 34 | // "types": [], /* Specify type package names to be included without being referenced in a source file. */ 35 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 36 | // "resolveJsonModule": true, /* Enable importing .json files */ 37 | // "noResolve": true, /* Disallow `import`s, `require`s or ``s from expanding the number of files TypeScript should add to a project. */ 38 | 39 | /* JavaScript Support */ 40 | // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */ 41 | // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ 42 | // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from `node_modules`. Only applicable with `allowJs`. */ 43 | 44 | /* Emit */ 45 | "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ 46 | // "declarationMap": true, /* Create sourcemaps for d.ts files. */ 47 | // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ 48 | "sourceMap": true, /* Create source map files for emitted JavaScript files. */ 49 | // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output. */ 50 | "outDir": "./dist", /* Specify an output folder for all emitted files. */ 51 | // "removeComments": true, /* Disable emitting comments. */ 52 | // "noEmit": true, /* Disable emitting files from a compilation. */ 53 | // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ 54 | // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types */ 55 | // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ 56 | // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ 57 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 58 | // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ 59 | // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ 60 | // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ 61 | // "newLine": "crlf", /* Set the newline character for emitting files. */ 62 | // "stripInternal": true, /* Disable emitting declarations that have `@internal` in their JSDoc comments. */ 63 | // "noEmitHelpers": true, /* Disable generating custom helper functions like `__extends` in compiled output. */ 64 | // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ 65 | // "preserveConstEnums": true, /* Disable erasing `const enum` declarations in generated code. */ 66 | "declarationDir": "./dist/types", /* Specify the output directory for generated declaration files. */ 67 | // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ 68 | 69 | /* Interop Constraints */ 70 | // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ 71 | // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ 72 | "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */ 73 | // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ 74 | "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ 75 | 76 | /* Type Checking */ 77 | "strict": true, /* Enable all strict type-checking options. */ 78 | "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied `any` type.. */ 79 | "strictNullChecks": true, /* When type checking, take into account `null` and `undefined`. */ 80 | // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ 81 | // "strictBindCallApply": true, /* Check that the arguments for `bind`, `call`, and `apply` methods match the original function. */ 82 | // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ 83 | // "noImplicitThis": true, /* Enable error reporting when `this` is given the type `any`. */ 84 | // "useUnknownInCatchVariables": true, /* Type catch clause variables as 'unknown' instead of 'any'. */ 85 | // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ 86 | // "noUnusedLocals": true, /* Enable error reporting when a local variables aren't read. */ 87 | // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read */ 88 | // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ 89 | // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ 90 | // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ 91 | // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ 92 | // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ 93 | // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type */ 94 | // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ 95 | // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ 96 | 97 | /* Completeness */ 98 | // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ 99 | "skipLibCheck": true /* Skip type checking all .d.ts files. */ 100 | }, 101 | "include": [ 102 | "src" 103 | ] 104 | } 105 | -------------------------------------------------------------------------------- /OSMC-License.txt: -------------------------------------------------------------------------------- 1 | --- Start of Definition of OSMC Public License --- 2 | 3 | /* 4 | * This file is part of OpenModelica. 5 | * 6 | * Copyright (c) 1998-CurrentYear, Open Source Modelica Consortium (OSMC), 7 | * c/o Linköpings universitet, Department of Computer and Information Science, 8 | * SE-58183 Linköping, Sweden. 9 | * 10 | * All rights reserved. 11 | * 12 | * THIS PROGRAM IS PROVIDED UNDER THE TERMS OF AGPL VERSION 3 LICENSE OR 13 | * THIS OSMC PUBLIC LICENSE (OSMC-PL) VERSION 1.8. 14 | * ANY USE, REPRODUCTION OR DISTRIBUTION OF THIS PROGRAM CONSTITUTES 15 | * RECIPIENT'S ACCEPTANCE OF THE OSMC PUBLIC LICENSE OR THE GNU AGPL 16 | * VERSION 3, ACCORDING TO RECIPIENTS CHOICE. 17 | * 18 | * The OpenModelica software and the OSMC (Open Source Modelica Consortium) 19 | * Public License (OSMC-PL) are obtained from OSMC, either from the above 20 | * address, from the URLs: 21 | * http://www.openmodelica.org or 22 | * https://github.com/OpenModelica/ or 23 | * http://www.ida.liu.se/projects/OpenModelica, 24 | * and in the OpenModelica distribution. 25 | * 26 | * GNU AGPL version 3 is obtained from: 27 | * https://www.gnu.org/licenses/licenses.html#GPL 28 | * 29 | * This program is distributed WITHOUT ANY WARRANTY; without 30 | * even the implied warranty of MERCHANTABILITY or FITNESS 31 | * FOR A PARTICULAR PURPOSE, EXCEPT AS EXPRESSLY SET FORTH 32 | * IN THE BY RECIPIENT SELECTED SUBSIDIARY LICENSE CONDITIONS OF OSMC-PL. 33 | * 34 | * See the full OSMC Public License conditions for more details. 35 | * 36 | */ 37 | 38 | --- End of OSMC Public License Header --- 39 | 40 | The OSMC-PL is a public license for OpenModelica with three modes/alternatives 41 | (AGPL, OSMC-Internal-EPL, OSMC-External-EPL) for use and redistribution, 42 | in source and/or binary/object-code form: 43 | 44 | * AGPL. Any party (member or non-member of OSMC) may use and redistribute 45 | OpenModelica under GNU AGPL version 3. 46 | 47 | * Level 1 members of OSMC may also use and redistribute OpenModelica under 48 | OSMC-Internal-EPL conditions. 49 | 50 | * Level 2 members of OSMC may also use and redistribute OpenModelica under 51 | OSMC-Internal-EPL or OSMC-External-EPL conditions. 52 | Definitions of OSMC Public license modes: 53 | 54 | * AGPL = GNU AGPL version 3. 55 | 56 | * OSMC-Internal-EPL = These OSMC Public license conditions together with 57 | Internally restricted EPL, i.e., EPL version 1.0 with the Additional 58 | Condition that use and redistribution by an OSMC member is only allowed 59 | within the OSMC member's own organization (i.e., its own legal entity), 60 | or for an OSMC member paying an annual fee corresponding to the size 61 | of the organization including all its affiliates, use and redistribution 62 | is allowed within/between its affiliates. 63 | 64 | * OSMC-External-EPL = These OSMC Public license conditions together with 65 | Externally restricted EPL, i.e., EPL version 1.0 with the Additional 66 | Condition that use and redistribution by an OSMC member, or by a Licensed 67 | Third Party Distributor having a redistribution agreement with that member, 68 | to parties external to the OSMC member’s own organization (i.e., its own 69 | legal entity) is only allowed in binary/object-code form, except the case of 70 | redistribution to other OSMC members to which source is also allowed to be 71 | distributed. 72 | 73 | [This has the consequence that an external party who wishes to use 74 | OpenModelica in source form together with its own proprietary software in all 75 | cases must be a member of OSMC]. 76 | 77 | In all cases of usage and redistribution by recipients, the following 78 | conditions also apply: 79 | 80 | a) Redistributions of source code must retain the above copyright notice, 81 | all definitions, and conditions. It is sufficient if the OSMC-PL Header 82 | is present in each source file, if the full OSMC-PL is available in a 83 | prominent and easily located place in the redistribution. 84 | 85 | b) Redistributions in binary/object-code form must reproduce the above 86 | copyright notice, all definitions, and conditions. It is sufficient if the 87 | OSMC-PL Header and the location in the redistribution of the full OSMC-PL 88 | are present in the documentation and/or other materials provided with the 89 | redistribution, if the full OSMC-PL is available in a prominent and easily 90 | located place in the redistribution. 91 | 92 | c) A recipient must clearly indicate its chosen usage mode of OSMC-PL, 93 | in accompanying documentation and in a text file OSMC-USAGE-MODE.txt, 94 | provided with the distribution. 95 | 96 | d) Contributor(s) making a Contribution to OpenModelica thereby also makes a 97 | Transfer of Contribution Copyright. In return, upon the effective date of 98 | the transfer, OSMC grants the Contributor(s) a Contribution License of the 99 | Contribution. OSMC has the right to accept or refuse Contributions. 100 | 101 | Definitions: 102 | 103 | "Subsidiary license conditions" means: 104 | 105 | The additional license conditions depending on the by the recipient chosen 106 | mode of OSMC-PL, defined by GNU AGPL version 3.0 for AGPL, and by EPL for 107 | OSMC-Internal-EPL and OSMC-External-EPL. 108 | "OSMC-PL" means: 109 | 110 | Open Source Modelica Consortium Public License version 1.8, i.e., the license 111 | defined here (the text between 112 | "--- Start of Definition of OSMC Public License ---" and 113 | "--- End of Definition of OSMC Public License ---", or later versions thereof. 114 | 115 | "OSMC-PL Header" means: 116 | 117 | Open Source Modelica Consortium Public License Header version 1.8, i.e., the 118 | text between "--- Start of Definition of OSMC Public License ---" and 119 | "--- End of OSMC Public License Header ---", or later versions thereof. 120 | 121 | "Contribution" means: 122 | 123 | a) in the case of the initial Contributor, the initial code and documentation 124 | distributed under OSMC-PL, and 125 | 126 | b) in the case of each subsequent Contributor: 127 | i) changes to OpenModelica, and 128 | ii) additions to OpenModelica; 129 | 130 | where such changes and/or additions to OpenModelica originate from and are 131 | distributed by that particular Contributor. A Contribution 'originates' from 132 | a Contributor if it was added to OpenModelica by such Contributor itself or 133 | anyone acting on such Contributor's behalf. 134 | 135 | For Contributors licensing OpenModelica under OSMC-Internal-EPL or 136 | OSMC-External-EPL conditions, the following conditions also hold: 137 | 138 | Contributions do not include additions to the distributed Program which: (i) 139 | are separate modules of software distributed in conjunction with OpenModelica 140 | under their own license agreement, (ii) are separate modules which are not 141 | derivative works of OpenModelica, and (iii) are separate modules of software 142 | distributed in conjunction with OpenModelica under their own license agreement 143 | where these separate modules are merged with (weaved together with) modules of 144 | OpenModelica to form new modules that are distributed as object code or source 145 | code under their own license agreement, as allowed under the Additional 146 | Condition of internal distribution according to OSMC-Internal-EPL and/or 147 | Additional Condition for external distribution according to OSMC-External-EPL. 148 | 149 | "Transfer of Contribution Copyright" means that the Contributors of a 150 | Contribution transfer the ownership and the copyright of the Contribution to 151 | Open Source Modelica Consortium, the OpenModelica Copyright owner, for 152 | inclusion in OpenModelica. The transfer takes place upon the effective date 153 | when the Contribution is made available on the OSMC web site under OSMC-PL, by 154 | such Contributors themselves or anyone acting on such Contributors' behalf. 155 | The transfer is free of charge. If the Contributors or OSMC so wish, 156 | an optional Copyright transfer agreement can be signed between OSMC and the 157 | Contributors, as specified in an Appendix of the OSMC Bylaws. 158 | 159 | "Contribution License" means a license from OSMC to the Contributors of the 160 | Contribution, effective on the date of the Transfer of Contribution Copyright, 161 | where OSMC grants the Contributors a non-exclusive, world-wide, transferable, 162 | free of charge, perpetual license, including sublicensing rights, to use, 163 | have used, modify, have modified, reproduce and or have reproduced the 164 | contributed material, for business and other purposes, including but not 165 | limited to evaluation, development, testing, integration and merging with 166 | other software and distribution. The warranty and liability disclaimers of 167 | OSMC-PL apply to this license. 168 | 169 | "Contributor" means any person or entity that distributes (part of) 170 | OpenModelica. 171 | 172 | "The Program" means the Contributions distributed in accordance with OSMC-PL. 173 | 174 | "OpenModelica" means the Contributions distributed in accordance with OSMC-PL. 175 | 176 | "Recipient" means anyone who receives OpenModelica under OSMC-PL, 177 | including all Contributors. 178 | 179 | "Licensed Third Party Distributor" means a reseller/distributor having signed 180 | a redistribution/resale agreement in accordance with OSMC-PL and OSMC Bylaws, 181 | with an OSMC Level 2 organizational member which is not an Affiliate of the 182 | reseller/distributor, for distributing a product containing part(s) of 183 | OpenModelica. The Licensed Third Party Distributor shall only be allowed 184 | further redistribution to other resellers if the Level 2 member is granting 185 | such a right to it in the redistribution/resale agreement between the 186 | Level 2 member and the Licensed Third Party Distributor. 187 | 188 | "Affiliate" shall mean any legal entity, directly or indirectly, through one 189 | or more intermediaries, controlling or controlled by or under common control 190 | with any other legal entity, as the case may be. For purposes of this 191 | definition, the term "control" (including the terms "controlling", 192 | "controlled by" and "under common control with") means the possession, 193 | direct or indirect, of the power to direct or cause the direction of the 194 | management and policies of a legal entity, whether through the ownership of 195 | voting securities, by contract or otherwise. 196 | 197 | NO WARRANTY 198 | 199 | EXCEPT AS EXPRESSLY SET FORTH IN THE BY RECIPIENT SELECTED SUBSIDIARY 200 | LICENSE CONDITIONS OF OSMC-PL, OPENMODELICA IS PROVIDED ON AN "AS IS" 201 | BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR 202 | IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF 203 | TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR 204 | PURPOSE. Each Recipient is solely responsible for determining the 205 | appropriateness of using and distributing OPENMODELICA and assumes all risks 206 | associated with its exercise of rights under OSMC-PL , including but not 207 | limited to the risks and costs of program errors, compliance with applicable 208 | laws, damage to or loss of data, programs or equipment, and unavailability 209 | or interruption of operations. 210 | 211 | DISCLAIMER OF LIABILITY 212 | 213 | EXCEPT AS EXPRESSLY SET FORTH IN THE BY RECIPIENT SELECTED SUBSIDIARY 214 | LICENSE CONDITIONS OF OSMC-PL, NEITHER RECIPIENT NOR ANY CONTRIBUTORS 215 | SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 216 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION 217 | LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 218 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 219 | ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF OPENMODELICA OR THE 220 | EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE 221 | POSSIBILITY OF SUCH DAMAGES. 222 | 223 | A Contributor licensing OpenModelica under OSMC-Internal-EPL or 224 | OSMC-External-EPL may choose to distribute (parts of) OpenModelica in object 225 | code form under its own license agreement, provided that: 226 | 227 | a) it complies with the terms and conditions of OSMC-PL; or for the case of 228 | redistribution of OpenModelica together with proprietary code it is a dual 229 | license where the OpenModelica parts are distributed under OSMC-PL compatible 230 | conditions and the proprietary code is distributed under proprietary license 231 | conditions; and 232 | 233 | b) its license agreement: 234 | i) effectively disclaims on behalf of all Contributors all warranties and 235 | conditions, express and implied, including warranties or conditions of title 236 | and non-infringement, and implied warranties or conditions of merchantability 237 | and fitness for a particular purpose; 238 | ii) effectively excludes on behalf of all Contributors all liability for 239 | damages, including direct, indirect, special, incidental and consequential 240 | damages, such as lost profits; 241 | iii) states that any provisions which differ from OSMC-PL are offered by that 242 | Contributor alone and not by any other party; and 243 | iv) states from where the source code for OpenModelica is available, and 244 | informs licensees how to obtain it in a reasonable manner on or through a 245 | medium customarily used for software exchange. 246 | 247 | When OPENMODELICA is made available in source code form: 248 | 249 | a) it must be made available under OSMC-PL; and 250 | 251 | b) a copy of OSMC-PL must be included with each copy of OPENMODELICA. 252 | 253 | c) a copy of the subsidiary license associated with the selected mode of 254 | OSMC-PL must be included with each copy of OPENMODELICA. 255 | 256 | Contributors may not remove or alter any copyright notices contained within 257 | OPENMODELICA. 258 | 259 | If there is a conflict between OSMC-PL and the subsidiary license conditions, 260 | OSMC-PL has priority. 261 | 262 | This Agreement is governed by the laws of Sweden. The place of jurisdiction 263 | for all disagreements related to this Agreement, is Linköping, Sweden. 264 | 265 | The EPL 1.0 license definition has been obtained from: 266 | http://www.eclipse.org/legal/epl-v10.html. It is also reproduced in Appendix B 267 | of the OSMC Bylaws, and in the OpenModelica distribution. 268 | 269 | The AGPL Version 3 license definition has been obtained from 270 | https://www.gnu.org/licenses/licenses.html#GPL. It is also reproduced in 271 | Appendix C of the OSMC Bylaws, and in the OpenModelica distribution. 272 | 273 | --- End of Definition of OSMC Public License --- 274 | -------------------------------------------------------------------------------- /src/common/graphics.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * OMFrontend.js 3 | * Copyright (C) 2021-2022 Perpetual Labs, Ltd. 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU Affero General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU Affero General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Affero General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | /** 20 | * @author Mohamad Omar Nachawati 21 | */ 22 | 23 | import { ModelicaClassSymbol, ModelicaComponentSymbol } from "./symbols.js"; 24 | import { ModelicaClassRestriction } from "./syntax.js"; 25 | import { XmlElement } from "./dom.js"; 26 | 27 | function computeExtent(extent?: any, coordinateSystem?: any, origin?: any, rotation?: any): [[number, number], [number, number]] { 28 | 29 | let cextent = coordinateSystem?.extent; 30 | 31 | let xmin = Math.min(cextent?.[0]?.[0] ?? -100, cextent?.[1]?.[0] ?? 100); 32 | let ymax = Math.max(cextent?.[1]?.[1] ?? 100, cextent?.[0]?.[1] ?? -100); 33 | 34 | let x0 = origin?.[0] ?? 0; 35 | let y0 = origin?.[1] ?? 0; 36 | let r = rotation ?? 0; 37 | 38 | return [ 39 | computePoint(extent[0]?.[0] ?? 0, extent[0]?.[1] ?? 0, x0, y0, xmin, ymax, r), 40 | computePoint(extent[1]?.[0] ?? 0, extent[1]?.[1] ?? 0, x0, y0, xmin, ymax, r) 41 | ]; 42 | 43 | } 44 | 45 | function computeFillPattern(defs: XmlElement, graphic: any): string { 46 | 47 | let id = defs.children.length; 48 | 49 | let fillColor = graphic.fillColor; 50 | let fillPattern = graphic.fillPattern; 51 | let lineColor = graphic.lineColor; 52 | let lineThickness = graphic.lineThickness ?? 0.25; 53 | 54 | switch (fillPattern) { 55 | 56 | case 1: // Solid 57 | return `rgb(${fillColor?.[0] ?? 0},${fillColor?.[1] ?? 0},${fillColor?.[2] ?? 0})`; 58 | 59 | case 2: { // Horizontal 60 | 61 | let pattern = defs.append("pattern") 62 | .attr("height", 5) 63 | .attr("id", "Horizontal" + id) 64 | .attr("patternUnits", "userSpaceOnUse") 65 | .attr("width", 5) 66 | .attr("x", 0) 67 | .attr("y", 0); 68 | 69 | pattern.append("rect") 70 | .attr("height", 5) 71 | .attr("fill", `rgb(${fillColor?.[0] ?? 0},${fillColor?.[1] ?? 0},${fillColor?.[2] ?? 0})`) 72 | .attr("width", 5) 73 | .attr("x", 0) 74 | .attr("y", 0); 75 | 76 | pattern.append("path") 77 | .attr("d", "M0,0 L5,0") 78 | .attr("stroke", `rgb(${lineColor?.[0] ?? 0},${lineColor?.[1] ?? 0},${lineColor?.[2] ?? 0})`); 79 | 80 | return `url(#Horizontal${id})`; 81 | 82 | } 83 | 84 | case 3: { // Vertical 85 | 86 | let pattern = defs.append("pattern") 87 | .attr("height", 5) 88 | .attr("id", "Vertical" + id) 89 | .attr("patternUnits", "userSpaceOnUse") 90 | .attr("width", 5) 91 | .attr("x", 0) 92 | .attr("y", 0); 93 | 94 | pattern.append("rect") 95 | .attr("height", 5) 96 | .attr("fill", `rgb(${fillColor?.[0] ?? 0},${fillColor?.[1] ?? 0},${fillColor?.[2] ?? 0})`) 97 | .attr("width", 5) 98 | .attr("x", 0) 99 | .attr("y", 0); 100 | 101 | pattern.append("path") 102 | .attr("d", "M0,0 L0,5") 103 | .attr("stroke", `rgb(${lineColor?.[0] ?? 0},${lineColor?.[1] ?? 0},${lineColor?.[2] ?? 0})`); 104 | 105 | return `url(#Vertical${id})`; 106 | 107 | } 108 | 109 | case 4: { // Cross 110 | 111 | let pattern = defs.append("pattern") 112 | .attr("height", 5) 113 | .attr("id", "Cross" + id) 114 | .attr("patternUnits", "userSpaceOnUse") 115 | .attr("width", 5) 116 | .attr("x", 0) 117 | .attr("y", 0); 118 | 119 | pattern.append("rect") 120 | .attr("height", 5) 121 | .attr("fill", `rgb(${fillColor?.[0] ?? 0},${fillColor?.[1] ?? 0},${fillColor?.[2] ?? 0})`) 122 | .attr("width", 5) 123 | .attr("x", 0) 124 | .attr("y", 0); 125 | 126 | pattern.append("path") 127 | .attr("d", "M0,0 L5,0") 128 | .attr("stroke", `rgb(${lineColor?.[0] ?? 0},${lineColor?.[1] ?? 0},${lineColor?.[2] ?? 0})`); 129 | 130 | pattern.append("path") 131 | .attr("d", "M0,0 L0,5") 132 | .attr("stroke", `rgb(${lineColor?.[0] ?? 0},${lineColor?.[1] ?? 0},${lineColor?.[2] ?? 0})`); 133 | 134 | return `url(#Cross${id})`; 135 | 136 | } 137 | 138 | case 5: { // Forward 139 | 140 | let pattern = defs.append("pattern") 141 | .attr("height", 7) 142 | .attr("id", "Forward" + id) 143 | .attr("patternUnits", "userSpaceOnUse") 144 | .attr("width", 7) 145 | .attr("x", 0) 146 | .attr("y", 0); 147 | 148 | pattern.append("rect") 149 | .attr("height", 7) 150 | .attr("fill", `rgb(${fillColor?.[0] ?? 0},${fillColor?.[1] ?? 0},${fillColor?.[2] ?? 0})`) 151 | .attr("width", 7) 152 | .attr("x", 0) 153 | .attr("y", 0); 154 | 155 | pattern.append("path") 156 | .attr("d", "M0,0 L7,7") 157 | .attr("stroke", `rgb(${lineColor?.[0] ?? 0},${lineColor?.[1] ?? 0},${lineColor?.[2] ?? 0})`); 158 | 159 | pattern.append("path") 160 | .attr("d", "M6,-1 l3,3") 161 | .attr("stroke", `rgb(${lineColor?.[0] ?? 0},${lineColor?.[1] ?? 0},${lineColor?.[2] ?? 0})`); 162 | 163 | pattern.append("path") 164 | .attr("d", "M-1,6 l3,3") 165 | .attr("stroke", `rgb(${lineColor?.[0] ?? 0},${lineColor?.[1] ?? 0},${lineColor?.[2] ?? 0})`); 166 | 167 | return `url(#Forward${id})`; 168 | 169 | } 170 | 171 | case 6: { // Backward 172 | 173 | let pattern = defs.append("pattern") 174 | .attr("height", 7) 175 | .attr("id", "Backward" + id) 176 | .attr("patternUnits", "userSpaceOnUse") 177 | .attr("width", 7) 178 | .attr("x", 0) 179 | .attr("y", 0); 180 | 181 | pattern.append("rect") 182 | .attr("height", 7) 183 | .attr("fill", `rgb(${fillColor?.[0] ?? 0},${fillColor?.[1] ?? 0},${fillColor?.[2] ?? 0})`) 184 | .attr("width", 7) 185 | .attr("x", 0) 186 | .attr("y", 0); 187 | 188 | pattern.append("path") 189 | .attr("d", "M7,0 l-7,7") 190 | .attr("stroke", `rgb(${lineColor?.[0] ?? 0},${lineColor?.[1] ?? 0},${lineColor?.[2] ?? 0})`); 191 | 192 | pattern.append("path") 193 | .attr("d", "M1,-1 l-7,7") 194 | .attr("stroke", `rgb(${lineColor?.[0] ?? 0},${lineColor?.[1] ?? 0},${lineColor?.[2] ?? 0})`); 195 | 196 | pattern.append("path") 197 | .attr("d", "M8,6 l-7,7") 198 | .attr("stroke", `rgb(${lineColor?.[0] ?? 0},${lineColor?.[1] ?? 0},${lineColor?.[2] ?? 0})`); 199 | 200 | return `url(#Backward${id})`; 201 | 202 | } 203 | 204 | case 7: { // CrossDiag 205 | 206 | let pattern = defs.append("pattern") 207 | .attr("height", 8) 208 | .attr("id", "CrossDiag" + id) 209 | .attr("patternUnits", "userSpaceOnUse") 210 | .attr("width", 8) 211 | .attr("x", 0) 212 | .attr("y", 0); 213 | 214 | pattern.append("rect") 215 | .attr("height", 8) 216 | .attr("fill", `rgb(${fillColor?.[0] ?? 0},${fillColor?.[1] ?? 0},${fillColor?.[2] ?? 0})`) 217 | .attr("width", 8) 218 | .attr("x", 0) 219 | .attr("y", 0); 220 | 221 | pattern.append("path") 222 | .attr("d", "M0,0 l8,8") 223 | .attr("stroke", `rgb(${lineColor?.[0] ?? 0},${lineColor?.[1] ?? 0},${lineColor?.[2] ?? 0})`); 224 | 225 | pattern.append("path") 226 | .attr("d", "M8,0 l-8,8") 227 | .attr("stroke", `rgb(${lineColor?.[0] ?? 0},${lineColor?.[1] ?? 0},${lineColor?.[2] ?? 0})`); 228 | 229 | return `url(#CrossDiag${id})`; 230 | 231 | } 232 | 233 | case 8: { // HorizontalCylinder 234 | 235 | let gradient = defs.append("linearGradient") 236 | .attr("id", "HorizontalCylinder" + id) 237 | .attr("x1", "0%") 238 | .attr("x2", "0%") 239 | .attr("y1", "0%") 240 | .attr("y2", "100%"); 241 | 242 | let stops = [ 243 | [0, 0], 244 | [0.3, 1], 245 | [0.7, 1], 246 | [1, 0] 247 | ]; 248 | 249 | for (let stop of stops) { 250 | 251 | let color = getGradientColor(stop[1], lineColor, fillColor, 1); 252 | 253 | gradient.append("stop") 254 | .attr("offset", stop[0]) 255 | .attr("stop-color", `rgb(${color[0]},${color[1]},${color[2]})`) 256 | .attr("stop-opacity", 1); 257 | 258 | } 259 | 260 | return `url(#HorizontalCylinder${id})`; 261 | 262 | } 263 | 264 | case 9: { // VerticalCylinder 265 | 266 | let gradient = defs.append("linearGradient") 267 | .attr("id", "VerticalCylinder" + id) 268 | .attr("x1", "0%") 269 | .attr("x2", "100%") 270 | .attr("y1", "0%") 271 | .attr("y2", "0%"); 272 | 273 | let stops = [ 274 | [0, 0], 275 | [0.3, 1], 276 | [0.7, 1], 277 | [1, 0] 278 | ]; 279 | 280 | for (let stop of stops) { 281 | 282 | let color = getGradientColor(stop[1], lineColor, fillColor, 1); 283 | 284 | gradient.append("stop") 285 | .attr("offset", stop[0]) 286 | .attr("stop-color", `rgb(${color[0]},${color[1]},${color[2]})`) 287 | .attr("stop-opacity", 1); 288 | 289 | } 290 | 291 | return `url(#VerticalCylinder${id})`; 292 | 293 | } 294 | 295 | case 10: { // Sphere 296 | 297 | let gradient; 298 | let stops; 299 | 300 | if (graphic["@type"] == "Ellipse") { 301 | 302 | gradient = defs.append("radialGradient") 303 | .attr("id", "Sphere" + id) 304 | .attr("cx", "50%") 305 | .attr("cy", "50%") 306 | .attr("fx", "50%") 307 | .attr("fy", "50%") 308 | .attr("r", "55%"); 309 | 310 | stops = [ 311 | [0, 10], 312 | [0.45, 8], 313 | [0.7, 6], 314 | [1, 0] 315 | ]; 316 | 317 | } else { 318 | 319 | gradient = defs.append("radialGradient") 320 | .attr("id", "Sphere" + id) 321 | .attr("cx", "50%") 322 | .attr("cy", "50%") 323 | .attr("fx", "50%") 324 | .attr("fy", "50%") 325 | .attr("r", "0.9"); 326 | 327 | stops = [ 328 | [0, 1], 329 | [1, 0] 330 | ]; 331 | 332 | } 333 | 334 | for (let stop of stops) { 335 | 336 | let color = getGradientColor(stop[1], lineColor, fillColor, 1); 337 | 338 | gradient.append("stop") 339 | .attr("offset", stop[0]) 340 | .attr("stop-color", `rgb(${color[0]},${color[1]},${color[2]})`) 341 | .attr("stop-opacity", 1); 342 | 343 | } 344 | 345 | return `url(#Sphere${id})`; 346 | 347 | } 348 | 349 | case 0: // None 350 | default: { 351 | 352 | if (graphic["@type"] == "Text") 353 | return `rgb(${lineColor?.[0] ?? 0},${lineColor?.[1] ?? 0},${lineColor?.[2] ?? 0})`; 354 | 355 | return "none"; 356 | 357 | } 358 | 359 | } 360 | 361 | } 362 | 363 | function computeLinePattern(linePattern: number, lineThickness: number): string | null { 364 | 365 | let dash = 16 * lineThickness; 366 | let dot = 4 * lineThickness; 367 | let space = 8 * lineThickness; 368 | 369 | switch (linePattern) { 370 | 371 | case 1: // Solid 372 | return null; 373 | 374 | case 2: // Dash 375 | return `${dash} ${space}`; 376 | 377 | case 3: // Dot 378 | return `${dot} ${space}`; 379 | 380 | case 4: // DashDot 381 | return `${dash} ${space} ${dot} ${space}`; 382 | 383 | case 5: // DashDotDot 384 | return `${dash} ${space} ${dot} ${space} ${dot} ${space}`; 385 | 386 | case 0: // None 387 | default: 388 | return null; 389 | 390 | } 391 | 392 | } 393 | 394 | function computeArrow(defs: XmlElement, arrow: number, arrowSize: number, lineThickness: number, lineColor?: any[]): string { 395 | 396 | let id = defs.children.length; 397 | 398 | switch (Math.abs(arrow)) { 399 | 400 | case 1: { // Open 401 | 402 | let marker = defs.append("marker") 403 | .attr("id", "Marker" + id) 404 | .attr("orient", "auto") 405 | .attr("overflow", "visible") 406 | .attr("viewBox", "0 0 3 3") 407 | .attr("markerHeight", `${arrowSize}`) 408 | .attr("markerUnits", "userSpaceOnUse") 409 | .attr("markerWidth", `${arrowSize}`) 410 | .attr("refY", 1.5); 411 | 412 | if (arrow > 0) { 413 | marker.attr("refX", 0).append("path") 414 | .attr("d", "M 3 2.5 L 0 1.5 L 3 0.5") 415 | .attr("stroke", `rgb(${lineColor?.[0] ?? 0},${lineColor?.[1] ?? 0},${lineColor?.[2] ?? 0})`) 416 | .attr("stroke-width", `${lineThickness}mm`) 417 | .attr("vector-effect", "non-scaling-stroke") 418 | .attr("fill", "none"); 419 | } else { 420 | marker.attr("refX", 3).append("path") 421 | .attr("d", "M 0 2.5 L 3 1.5 L 0 0.5") 422 | .attr("stroke", `rgb(${lineColor?.[0] ?? 0},${lineColor?.[1] ?? 0},${lineColor?.[2] ?? 0})`) 423 | .attr("stroke-width", `${lineThickness}mm`) 424 | .attr("vector-effect", "non-scaling-stroke") 425 | .attr("fill", "none"); 426 | } 427 | 428 | break; 429 | 430 | } 431 | 432 | case 3: { // Half 433 | 434 | let marker = defs.append("marker") 435 | .attr("id", "Marker" + id) 436 | .attr("orient", "auto") 437 | .attr("overflow", "visible") 438 | .attr("viewBox", "0 0 3 3") 439 | .attr("markerHeight", `${arrowSize}`) 440 | .attr("markerUnits", "userSpaceOnUse") 441 | .attr("markerWidth", `${arrowSize}`) 442 | .attr("refY", 1.5); 443 | 444 | if (arrow > 0) { 445 | marker.attr("refX", 0).append("path") 446 | .attr("d", "M 3 2.5 L 0 1.5 L 3 1.5") 447 | .attr("stroke", `rgb(${lineColor?.[0] ?? 0},${lineColor?.[1] ?? 0},${lineColor?.[2] ?? 0})`) 448 | .attr("stroke-width", `${lineThickness}mm`) 449 | .attr("vector-effect", "non-scaling-stroke") 450 | .attr("fill", "none"); 451 | } else { 452 | marker.attr("refX", 3).append("path") 453 | .attr("d", "M 0 2.5 L 3 1.5 L 0 1.5") 454 | .attr("stroke", `rgb(${lineColor?.[0] ?? 0},${lineColor?.[1] ?? 0},${lineColor?.[2] ?? 0})`) 455 | .attr("stroke-width", `${lineThickness}mm`) 456 | .attr("vector-effect", "non-scaling-stroke") 457 | .attr("fill", "none"); 458 | } 459 | 460 | break; 461 | 462 | } 463 | 464 | case 2: { // Filled 465 | 466 | let marker = defs.append("marker") 467 | .attr("id", "Marker" + id) 468 | .attr("orient", "auto") 469 | .attr("overflow", "visible") 470 | .attr("viewBox", "0 0 3 3") 471 | .attr("markerHeight", `${arrowSize}`) 472 | .attr("markerUnits", "userSpaceOnUse") 473 | .attr("markerWidth", `${arrowSize}`) 474 | .attr("refY", 1.5); 475 | 476 | if (arrow > 0) { 477 | marker.attr("refX", 0).append("path") 478 | .attr("d", "M 3 2.5 L 0 1.5 L 3 0.5 z") 479 | .attr("fill", `rgb(${lineColor?.[0] ?? 0},${lineColor?.[1] ?? 0},${lineColor?.[2] ?? 0})`); 480 | } else { 481 | marker.attr("refX", 3).append("path") 482 | .attr("d", "M 0 2.5 L 3 1.5 L 0 0.5 z") 483 | .attr("fill", `rgb(${lineColor?.[0] ?? 0},${lineColor?.[1] ?? 0},${lineColor?.[2] ?? 0})`); 484 | } 485 | 486 | break; 487 | 488 | } 489 | 490 | } 491 | 492 | return `url(#Marker${id})`; 493 | } 494 | 495 | function computePath(points: any[], coordinateSystem?: any, origin?: any, rotation?: any, closed?: boolean): string { 496 | 497 | let extent = coordinateSystem?.extent; 498 | 499 | let xmin = Math.min(extent?.[0]?.[0] ?? -100, extent?.[1]?.[0] ?? 100); 500 | let ymax = Math.max(extent?.[1]?.[1] ?? 100, extent?.[0]?.[1] ?? -100); 501 | 502 | let x0 = origin?.[0] ?? 0; 503 | let y0 = origin?.[1] ?? 0; 504 | let r = rotation ?? 0; 505 | let n = points.length - 1; 506 | 507 | let p0 = computePoint(points[0]?.[0] ?? 0, points[0]?.[1] ?? 0, x0, y0, xmin, ymax, r); 508 | let p1 = computePoint(points[1]?.[0] ?? 0, points[1]?.[1] ?? 0, x0, y0, xmin, ymax, r); 509 | let p2 = computePoint(points[2]?.[0] ?? 0, points[2]?.[1] ?? 0, x0, y0, xmin, ymax, r); 510 | let pn = computePoint(points[n]?.[0] ?? 0, points[n]?.[1] ?? 0, x0, y0, xmin, ymax, r); 511 | 512 | let result; 513 | 514 | if ((closed ?? false) == false) { 515 | result = `M ${p0[0]} ${p0[1]}`; 516 | result += ` L ${p0[0] + (p1[0] - p0[0]) / 2} ${p0[1] + (p1[1] - p0[1]) / 2}`; 517 | } else { 518 | result = `M ${p0[0]} ${p0[1]}`; 519 | result += ` Q ${p0[0]} ${p0[1]} ${p0[0] + (p1[0] - p0[0]) / 2} ${p0[1] + (p1[1] - p0[1]) / 2}`; 520 | } 521 | 522 | result += ` Q ${p1[0]} ${p1[1]} ${p1[0] + (p2[0] - p1[0]) / 2} ${p1[1] + (p2[1] - p1[1]) / 2}`; 523 | 524 | let pp = p2; 525 | for (let point of points.slice(3)) { 526 | let p = computePoint(point?.[0] ?? 0, point?.[1] ?? 0, x0, y0, xmin, ymax, r); 527 | result += ` Q ${pp[0]} ${pp[1]} ${pp[0] + (p[0] - pp[0]) / 2} ${pp[1] + (p[1] - pp[1]) / 2}`; 528 | pp = p; 529 | } 530 | 531 | if ((closed ?? false) == false) 532 | result += ` L ${pn[0]} ${pn[1]}`; 533 | 534 | else 535 | result += ` Z`; 536 | 537 | return result; 538 | 539 | } 540 | 541 | function computePoint(x: number, y: number, x0: number, y0: number, xmin: number, ymax: number, r: number): [number, number] { 542 | 543 | r = r / 180 * Math.PI; 544 | 545 | return [ 546 | (x * Math.cos(r) - y * Math.sin(r) + x0) - xmin, 547 | ymax - (y * Math.sin(r) + y * Math.cos(r) + y0) 548 | ]; 549 | 550 | } 551 | 552 | function computePoints(points?: any[], coordinateSystem?: any, origin?: any, rotation?: any, closed?: boolean): string { 553 | 554 | let extent = coordinateSystem?.extent; 555 | 556 | let xmin = Math.min(extent?.[0]?.[0] ?? -100, extent?.[1]?.[0] ?? 100); 557 | let ymax = Math.max(extent?.[1]?.[1] ?? 100, extent?.[0]?.[1] ?? -100); 558 | 559 | let x0 = origin?.[0] ?? 0; 560 | let y0 = origin?.[1] ?? 0; 561 | let r = rotation ?? 0; 562 | 563 | let result = ""; 564 | 565 | for (let point of points ?? []) { 566 | let p = computePoint(point?.[0] ?? 0, point?.[1] ?? 0, x0, y0, xmin, ymax, r); 567 | result += `,${p[0]},${p[1]}`; 568 | } 569 | 570 | return result.substring(1) + (closed == true ? " Z" : ""); 571 | 572 | } 573 | 574 | async function computeTextMacros(graphic: any, componentSymbol?: ModelicaComponentSymbol): Promise { 575 | 576 | let textString: string = graphic.textString ?? graphic.string ?? ""; 577 | 578 | textString = textString.replace(/\%\%/, "%"); 579 | 580 | textString = textString.replace(/\%name\b/, componentSymbol?.identifier?.value ?? "%name"); 581 | 582 | textString = textString.replace(/\%class\b/, (await componentSymbol?.classSymbol)?.identifier?.value ?? "%class"); 583 | 584 | //textString = textString.replace(/\%[A-Za-z][A-Za-z0-9]*\b/, componentSymbol?.identifier?.value ?? "?"); 585 | 586 | return textString; 587 | 588 | } 589 | 590 | function computeTransform(coordinateSystem: any, transformation: any): string { 591 | 592 | let cextent = coordinateSystem?.extent; 593 | 594 | let cx1 = cextent?.[0]?.[0] ?? -100; 595 | let cy1 = cextent?.[0]?.[1] ?? -100; 596 | let cx2 = cextent?.[1]?.[0] ?? 100; 597 | let cy2 = cextent?.[1]?.[1] ?? 100; 598 | 599 | let cxmin = Math.min(cx1, cx2); 600 | let cxmax = Math.max(cx2, cx1); 601 | let cymin = Math.min(cy1, cy2); 602 | let cymax = Math.max(cy2, cy1); 603 | 604 | let cwidth = Math.abs(cxmax - cxmin); 605 | let cheight = Math.abs(cymax - cymin); 606 | 607 | let textent = transformation?.extent; 608 | 609 | let tx1 = textent?.[0]?.[0] ?? -100; 610 | let ty1 = textent?.[0]?.[1] ?? -100; 611 | let tx2 = textent?.[1]?.[0] ?? 100; 612 | let ty2 = textent?.[1]?.[1] ?? 100; 613 | 614 | let txmin = Math.min(tx1, tx2); 615 | let txmax = Math.max(tx2, tx1); 616 | let tymin = Math.min(ty1, ty2); 617 | let tymax = Math.max(ty2, ty1); 618 | 619 | let twidth = Math.abs(txmax - txmin); 620 | let theight = Math.abs(tymax - tymin); 621 | 622 | let torigin = transformation?.origin; 623 | 624 | let tox = torigin?.[0] ?? 0; 625 | let toy = torigin?.[1] ?? 0; 626 | 627 | let tx = tox - cxmin - twidth / 2 + txmin + twidth / 2; 628 | let ty = cymax - toy - theight / 2 - tymin - theight / 2; 629 | 630 | let sx = twidth / cwidth; 631 | let sy = theight / cheight; 632 | 633 | let r = transformation?.rotation ?? 0; 634 | let ox = twidth / 2; 635 | let oy = theight / 2; 636 | 637 | return `translate(${tx},${ty}) rotate(${r},${ox},${oy}) scale(${sx},${sy})`; 638 | 639 | } 640 | 641 | function configureLayer(svg: XmlElement, coordinateSystem: any): void { 642 | 643 | let extent = coordinateSystem?.extent; 644 | let preserveAspectRatio = coordinateSystem?.preserveAspectRatio ?? true; 645 | 646 | let x1 = extent?.[0]?.[0] ?? -100; 647 | let y1 = extent?.[0]?.[1] ?? -100; 648 | let x2 = extent?.[1]?.[0] ?? 100; 649 | let y2 = extent?.[1]?.[1] ?? 100; 650 | 651 | let xmin = Math.min(x1, x2); 652 | let xmax = Math.max(x2, x1); 653 | let ymin = Math.min(y1, y2); 654 | let ymax = Math.max(y2, y1); 655 | 656 | let width = Math.abs(xmax - xmin); 657 | let height = Math.abs(ymax - ymin); 658 | 659 | svg.attr("overflow", "visible"); 660 | svg.attr("width", `${width}`); 661 | svg.attr("height", `${height}`); 662 | svg.attr("viewBox", `0 0 ${width} ${height}`); 663 | svg.attr("preserveAspectRatio", preserveAspectRatio ? "xMidYMid" : "none"); 664 | 665 | } 666 | 667 | function getGradientColor(index: number, startColor: number[], stopColor: number[], midpoints: number): number[] { 668 | 669 | index |= 0; 670 | startColor = [(startColor[0] ?? 0) | 0, (startColor[1] ?? 0) | 0, (startColor[2] ?? 0) | 0]; 671 | stopColor = [(stopColor[0] ?? 0) | 0, (stopColor[1] ?? 0) | 0, (stopColor[2] ?? 0) | 0]; 672 | midpoints = midpoints < 0 ? 0 : midpoints | 0; 673 | 674 | if (index <= 0) 675 | return startColor; 676 | 677 | if (index > midpoints) 678 | return stopColor; 679 | 680 | return [ 681 | startColor[0] + index * (stopColor[0] - startColor[0]) / (midpoints + 1), 682 | startColor[1] + index * (stopColor[1] - startColor[1]) / (midpoints + 1), 683 | startColor[2] + index * (stopColor[2] - startColor[2]) / (midpoints + 1) 684 | ]; 685 | } 686 | 687 | export async function renderDiagram(svg: XmlElement, classSymbol: ModelicaClassSymbol, defs?: XmlElement): Promise { 688 | 689 | if (defs == null) 690 | defs = svg.append("defs"); 691 | 692 | for await (let baseClass of classSymbol.baseClasses) 693 | if (baseClass != null) 694 | await renderDiagram(svg, baseClass, defs); 695 | 696 | let diagram: any = null; 697 | let diagramAnnotation = await (await classSymbol.annotation)?.getNamedElement("Diagram"); 698 | 699 | if (diagramAnnotation instanceof ModelicaClassSymbol) 700 | diagram = (await diagramAnnotation.construct())?.toJSON(); 701 | 702 | let coordinateSystem = diagram?.coordinateSystem; 703 | let graphics = diagram?.graphics; 704 | 705 | configureLayer(svg, coordinateSystem); 706 | 707 | for (let graphic of graphics ?? []) 708 | await renderGraphic(svg, defs, graphic, coordinateSystem); 709 | 710 | for await (let connection of classSymbol.connections) { 711 | 712 | let annotation = await connection.annotationClause?.instantiate(classSymbol); 713 | 714 | if (annotation == null) 715 | continue; 716 | 717 | for await (let namedElement of annotation.namedElements) { 718 | 719 | if (!(namedElement instanceof ModelicaClassSymbol)) 720 | continue; 721 | 722 | let graphic = (await namedElement.construct())?.toJSON(); 723 | 724 | if (graphic == null) 725 | continue; 726 | 727 | await renderGraphic(svg, defs, graphic, coordinateSystem); 728 | 729 | } 730 | 731 | } 732 | 733 | for await (let componentSymbol of classSymbol.components) { 734 | 735 | let componentClassSymbol = await componentSymbol.classSymbol; 736 | 737 | if (componentClassSymbol == null) 738 | continue; 739 | 740 | let placement: any = null; 741 | let placementAnnotation = await (await componentSymbol?.annotation)?.getNamedElement("Placement"); 742 | 743 | if (placementAnnotation instanceof ModelicaClassSymbol) 744 | placement = (await placementAnnotation.construct())?.toJSON(); 745 | 746 | if (placement != null && placement["@type"] != "Placement") 747 | placement = null; 748 | 749 | if (placement?.visible == false) 750 | continue; 751 | 752 | let transformation = placement?.transformation; 753 | 754 | let g = svg.append("g"); 755 | 756 | g.attr("transform", computeTransform(coordinateSystem, transformation)); 757 | 758 | await renderIcon(g.append("svg").attr("overflow", "visible"), componentClassSymbol, componentSymbol, defs); 759 | 760 | } 761 | 762 | } 763 | 764 | export async function renderIcon(svg: XmlElement, classSymbol: ModelicaClassSymbol, componentSymbol?: ModelicaComponentSymbol, defs?: XmlElement): Promise { 765 | 766 | if (defs == null) 767 | defs = svg.append("defs"); 768 | 769 | for await (let baseClass of classSymbol.baseClasses) 770 | if (baseClass != null) 771 | await renderIcon(svg, baseClass, componentSymbol, defs); 772 | 773 | let icon: any = null; 774 | let iconAnnotation = await (await classSymbol.annotation)?.getNamedElement("Icon"); 775 | 776 | if (iconAnnotation instanceof ModelicaClassSymbol) 777 | icon = (await iconAnnotation.construct())?.toJSON(); 778 | 779 | if (icon == null || icon["@type"] != "Icon") 780 | return; 781 | 782 | let coordinateSystem = icon.coordinateSystem; 783 | let graphics = icon.graphics; 784 | 785 | configureLayer(svg, coordinateSystem); 786 | 787 | for (let graphic of graphics ?? []) 788 | await renderGraphic(svg, defs, graphic, coordinateSystem, componentSymbol); 789 | 790 | for await (let connectorSymbol of classSymbol.components) { 791 | 792 | let connectorClassSymbol = await connectorSymbol.classSymbol; 793 | 794 | if (connectorClassSymbol?.classRestriction != ModelicaClassRestriction.CONNECTOR) 795 | continue; 796 | 797 | let placement: any = null; 798 | let placementAnnotation = await (await connectorSymbol?.annotation)?.getNamedElement("Placement"); 799 | 800 | if (placementAnnotation instanceof ModelicaClassSymbol) 801 | placement = (await placementAnnotation.construct())?.toJSON(); 802 | 803 | if (placement != null && placement["@type"] != "Placement") 804 | placement = null; 805 | 806 | if (placement?.iconVisible == false || (placement?.iconVisible == null && placement?.visible == false)) 807 | continue; 808 | 809 | let iconTransformation = placement?.iconTransformation; 810 | 811 | if (iconTransformation?.extent?.["@type"] == "Extent") 812 | iconTransformation = placement?.transformation; 813 | 814 | let g = svg.append("g"); 815 | 816 | g.attr("transform", computeTransform(coordinateSystem, iconTransformation)); 817 | 818 | await renderIcon(g.append("svg").attr("overflow", "visible"), connectorClassSymbol, connectorSymbol, defs); 819 | 820 | } 821 | 822 | } 823 | 824 | 825 | export async function renderSimpleIcon(svg: XmlElement, classSymbol: ModelicaClassSymbol, componentSymbol?: ModelicaComponentSymbol, defs?: XmlElement): Promise { 826 | 827 | if (defs == null) 828 | defs = svg.append("defs"); 829 | 830 | let icon: any = null; 831 | let iconAnnotation = await (await classSymbol.annotation)?.getNamedElement("Icon"); 832 | 833 | if (iconAnnotation instanceof ModelicaClassSymbol) 834 | icon = (await iconAnnotation.construct())?.toJSON(); 835 | 836 | if (icon == null || icon["@type"] != "Icon") 837 | return; 838 | 839 | let coordinateSystem = icon.coordinateSystem; 840 | let graphics = icon.graphics; 841 | 842 | configureLayer(svg, coordinateSystem); 843 | 844 | svg.attr("width", "100%"); 845 | svg.attr("height", "100%"); 846 | 847 | for (let graphic of graphics ?? []) 848 | await renderGraphic(svg, defs, graphic, coordinateSystem, componentSymbol); 849 | 850 | } 851 | 852 | async function renderGraphic(svg: XmlElement, defs: XmlElement, graphic: any, coordinateSystem?: any, componentSymbol?: ModelicaComponentSymbol): Promise { 853 | 854 | if (graphic?.visible == false) 855 | return; 856 | 857 | switch (graphic["@type"]) { 858 | 859 | case "Bitmap": 860 | await renderBitmap(svg, defs, graphic, coordinateSystem); 861 | break; 862 | 863 | case "Ellipse": 864 | await renderEllipse(svg, defs, graphic, coordinateSystem); 865 | break; 866 | 867 | case "Line": 868 | await renderLine(svg, defs, graphic, coordinateSystem); 869 | break; 870 | 871 | case "Polygon": 872 | await renderPolygon(svg, defs, graphic, coordinateSystem); 873 | break; 874 | 875 | case "Rectangle": 876 | await renderRectangle(svg, defs, graphic, coordinateSystem); 877 | break; 878 | 879 | case "Text": 880 | await renderText(svg, defs, graphic, coordinateSystem, componentSymbol); 881 | break; 882 | 883 | } 884 | 885 | } 886 | 887 | async function renderBitmap(svg: XmlElement, defs: XmlElement, graphic: any, coordinateSystem?: any): Promise { 888 | } 889 | 890 | async function renderEllipse(svg: XmlElement, defs: XmlElement, graphic: any, coordinateSystem?: any): Promise { 891 | 892 | let element = svg.append("ellipse"); 893 | 894 | let extent = computeExtent(graphic.extent, coordinateSystem, graphic.origin, graphic.rotation); 895 | 896 | element.attr('cx', extent[0][0] + Math.abs(extent[1][0] - extent[0][0]) / 2) 897 | .attr('cy', extent[0][1] - Math.abs(extent[1][1] - extent[0][1]) / 2) 898 | .attr('rx', (extent[1][0] - extent[0][0]) / 2) 899 | .attr('ry', (extent[1][1] - extent[0][1]) / 2); 900 | 901 | 902 | let lineColor = graphic.lineColor; 903 | let lineThickness = graphic.lineThickness ?? 0.25; 904 | let linePattern = computeLinePattern(graphic.pattern, lineThickness); 905 | 906 | if (linePattern != null || (graphic.pattern ?? 0) > 0) { 907 | 908 | if (lineColor != null) 909 | element.attr("stroke", `rgb(${lineColor[0] ?? 0},${lineColor[1] ?? 0},${lineColor[2] ?? 0})`); 910 | 911 | else 912 | element.attr("stroke", "none"); 913 | 914 | element.attr("stroke-width", `${lineThickness}mm`); 915 | 916 | if (linePattern != null) 917 | element.attr("stroke-dasharray", linePattern); 918 | 919 | } 920 | 921 | element.attr('fill', computeFillPattern(defs, graphic)); 922 | 923 | element.attr("vector-effect", "non-scaling-stroke"); 924 | 925 | } 926 | 927 | async function renderLine(svg: XmlElement, defs: XmlElement, graphic: any, coordinateSystem?: any): Promise { 928 | 929 | let element; 930 | 931 | if (graphic.smooth == 1 && graphic.points?.length > 2) { 932 | 933 | element = svg.append("path") 934 | .attr("d", computePath(graphic.points, coordinateSystem, graphic.origin, graphic.rotation)); 935 | 936 | } else { 937 | 938 | element = svg.append("polyline") 939 | .attr("points", computePoints(graphic.points, coordinateSystem, graphic.origin, graphic.rotation)); 940 | 941 | } 942 | 943 | let lineColor = graphic.color; 944 | let lineThickness = graphic.thickness ?? 0.25; 945 | let linePattern = computeLinePattern(graphic.pattern, lineThickness); 946 | 947 | if (linePattern != null || (graphic.pattern ?? 0) > 0) { 948 | 949 | if (lineColor != null) 950 | element.attr("stroke", `rgb(${lineColor[0] ?? 0},${lineColor[1] ?? 0},${lineColor[2] ?? 0})`); 951 | 952 | else 953 | element.attr("stroke", "none"); 954 | 955 | element.attr("stroke-width", `${lineThickness}mm`); 956 | 957 | if (linePattern != null) 958 | element.attr("stroke-dasharray", linePattern); 959 | 960 | } 961 | 962 | let arrowSize = graphic.arrowSize ?? 3; 963 | 964 | if ((graphic.arrow?.[0] ?? 0) > 0) 965 | element.attr("marker-start", computeArrow(defs, graphic.arrow[0], arrowSize, lineThickness, lineColor)); 966 | 967 | if ((graphic.arrow?.[1] ?? 0) > 0) 968 | element.attr("marker-end", computeArrow(defs, -graphic.arrow[1], arrowSize, lineThickness, lineColor)); 969 | 970 | element.attr("fill", "none"); 971 | element.attr("vector-effect", "non-scaling-stroke"); 972 | 973 | } 974 | 975 | async function renderPolygon(svg: XmlElement, defs: XmlElement, graphic: any, coordinateSystem?: any): Promise { 976 | 977 | let element; 978 | 979 | if (graphic.smooth == 1 && graphic.points?.length > 2) { 980 | 981 | element = svg.append("path") 982 | .attr("d", computePath(graphic.points, coordinateSystem, graphic.origin, graphic.rotation, true)); 983 | 984 | } else { 985 | 986 | element = svg.append("polyline") 987 | .attr("points", computePoints(graphic.points, coordinateSystem, graphic.origin, graphic.rotation, true)); 988 | 989 | } 990 | 991 | let lineColor = graphic.lineColor; 992 | let lineThickness = graphic.lineThickness ?? 0.25; 993 | let linePattern = computeLinePattern(graphic.pattern, lineThickness); 994 | 995 | if (linePattern != null || (graphic.pattern ?? 0) > 0) { 996 | 997 | if (lineColor != null) 998 | element.attr("stroke", `rgb(${lineColor[0] ?? 0},${lineColor[1] ?? 0},${lineColor[2] ?? 0})`); 999 | 1000 | else 1001 | element.attr("stroke", "none"); 1002 | 1003 | element.attr("stroke-width", `${lineThickness}mm`); 1004 | 1005 | if (linePattern != null) 1006 | element.attr("stroke-dasharray", linePattern); 1007 | 1008 | } 1009 | 1010 | element.attr('fill', computeFillPattern(defs, graphic)); 1011 | 1012 | element.attr("vector-effect", "non-scaling-stroke"); 1013 | 1014 | } 1015 | 1016 | async function renderRectangle(svg: XmlElement, defs: XmlElement, graphic: any, coordinateSystem?: any): Promise { 1017 | 1018 | let element = svg.append("rect"); 1019 | 1020 | let extent = computeExtent(graphic.extent, coordinateSystem, graphic.origin, graphic.rotation); 1021 | 1022 | element.attr('x', extent[0][0]) 1023 | .attr('y', extent[1][1] - Math.abs(extent[1][1] - extent[0][1])) 1024 | .attr('width', Math.abs(extent[1][0] - extent[0][0])) 1025 | .attr('height', Math.abs(extent[1][1] - extent[0][1])); 1026 | 1027 | let radius = graphic.radius ?? 0; 1028 | element.attr("rx", radius); 1029 | element.attr("ry", radius); 1030 | 1031 | let lineColor = graphic.lineColor; 1032 | let lineThickness = graphic.lineThickness ?? 0.25; 1033 | let linePattern = computeLinePattern(graphic.pattern, lineThickness); 1034 | 1035 | if (linePattern != null || (graphic.pattern ?? 0) > 0) { 1036 | 1037 | if (lineColor != null) 1038 | element.attr("stroke", `rgb(${lineColor[0] ?? 0},${lineColor[1] ?? 0},${lineColor[2] ?? 0})`); 1039 | 1040 | else 1041 | element.attr("stroke", "none"); 1042 | 1043 | element.attr("stroke-width", `${lineThickness}mm`); 1044 | 1045 | if (linePattern != null) 1046 | element.attr("stroke-dasharray", linePattern); 1047 | 1048 | } 1049 | 1050 | element.attr('fill', computeFillPattern(defs, graphic)); 1051 | 1052 | element.attr("vector-effect", "non-scaling-stroke"); 1053 | 1054 | } 1055 | 1056 | async function renderText(svg: XmlElement, defs: XmlElement, graphic: any, coordinateSystem?: any, componentSymbol?: ModelicaComponentSymbol): Promise { 1057 | 1058 | let element = svg.append("text"); 1059 | 1060 | element.appendText(await computeTextMacros(graphic, componentSymbol)); 1061 | 1062 | element.attr('font-family', 'monospace'); 1063 | 1064 | let textColor = graphic.textColor; 1065 | 1066 | if (textColor != null) 1067 | element.attr("fill", `rgb(${textColor[0] ?? 0},${textColor[1] ?? 0},${textColor[2] ?? 0})`); 1068 | 1069 | else 1070 | element.attr("fill", "none"); 1071 | 1072 | let extent = computeExtent(graphic.extent, coordinateSystem); 1073 | 1074 | element.attr('x', extent[0][0] + (extent[1][0] - extent[0][0]) / 2); 1075 | element.attr('y', extent[0][1] + (extent[1][1] - extent[0][1]) / 2); 1076 | 1077 | let fontSize = Number(graphic.fontSize) ?? 0; 1078 | 1079 | element.attr("font-size", fontSize > 0 ? fontSize : Math.abs(extent[1][1] - extent[0][1])); 1080 | element.attr("dominant-baseline", "middle"); 1081 | element.attr("text-anchor", "middle"); 1082 | //element.attr("textLength", extent[1][0] - extent[0][0]); 1083 | 1084 | } 1085 | 1086 | -------------------------------------------------------------------------------- /src/common/symbols.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * OMFrontend.js 3 | * Copyright (C) 2022 Perpetual Labs, Ltd. 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU Affero General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU Affero General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Affero General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | /** 20 | * @author Mohamad Omar Nachawati 21 | */ 22 | 23 | import { ModelicaScope } from "./scope.js"; 24 | import { ModelicaAlgorithmSectionSyntax, ModelicaClassDefinitionSyntax, ModelicaClassRedeclarationSyntax, ModelicaClassRestriction, ModelicaComponentDeclarationSyntax, ModelicaComponentReferenceExpressionSyntax, ModelicaConnectClauseSyntax, ModelicaDescriptionStringSyntax, ModelicaElementModificationSyntax, ModelicaElementSyntax, ModelicaEnumerationLiteralSyntax, ModelicaEquationSectionSyntax, ModelicaExpressionSyntax, ModelicaExtendsClauseSyntax, ModelicaIdentifierSyntax, ModelicaImportClauseSyntax, ModelicaModificationEnvironment, ModelicaNamedArgumentSyntax, ModelicaNamedElementSyntax, ModelicaNameSyntax, ModelicaTypeSpecifierSyntax, ModelicaVisibility } from "./syntax.js"; 25 | import { BufferedPrintWriter, PrintWriter } from "./writer.js"; 26 | import { getIdentifiers, toArray } from "./util.js"; 27 | import { ModelicaContext } from "./context.js"; 28 | import { renderDiagram, renderIcon, renderSimpleIcon } from "./graphics.js"; 29 | import { XmlElement } from "./dom.js"; 30 | 31 | 32 | export abstract class ModelicaSymbol { 33 | 34 | constructor() { 35 | } 36 | 37 | abstract accept(visitor: ModelicaSymbolVisitor, ...args: any[]): Promise; 38 | 39 | abstract print(printWriter: PrintWriter, indent?: number): Promise; 40 | 41 | } 42 | 43 | export class ModelicaObjectSymbol extends ModelicaSymbol { 44 | 45 | #properties: Map = new Map(); 46 | #type: ModelicaClassSymbol; 47 | #value?: Array | boolean | number | string; 48 | 49 | constructor(type: ModelicaClassSymbol, value?: Array | boolean | number | string) { 50 | super(); 51 | this.#type = type; 52 | this.#value = value; 53 | } 54 | 55 | override accept(visitor: ModelicaSymbolVisitor, ...args: any[]): Promise { 56 | return visitor.visitObject(this, ...args); 57 | } 58 | 59 | override async print(printWriter: PrintWriter, indent?: number): Promise { 60 | 61 | printWriter.println("<" + this.type.name + "> {"); 62 | 63 | if (this.value != null) { 64 | 65 | if (this instanceof ModelicaArrayObjectSymbol) { 66 | 67 | printWriter.println("value: [", (indent ?? 0) + 1); 68 | 69 | for (let item of this.value) { 70 | 71 | if (item != null) { 72 | 73 | printWriter.print("", (indent ?? 0) + 2); 74 | await item.print(printWriter, (indent ?? 0) + 2); 75 | 76 | } else { 77 | printWriter.println("null", (indent ?? 0) + 2); 78 | } 79 | 80 | } 81 | 82 | printWriter.println("]", (indent ?? 0) + 1); 83 | 84 | } else if (this instanceof ModelicaEnumerationObjectSymbol) { 85 | 86 | printWriter.println("value: " + this.value, (indent ?? 0) + 1); 87 | 88 | } else { 89 | 90 | switch (this.type.name) { 91 | 92 | case "Boolean": 93 | printWriter.println("value: " + this.value, (indent ?? 0) + 1); 94 | break; 95 | 96 | case "Integer": 97 | printWriter.println("value: " + this.value, (indent ?? 0) + 1); 98 | break; 99 | 100 | case "Real": 101 | printWriter.println("value: " + this.value, (indent ?? 0) + 1); 102 | break; 103 | 104 | case "String": 105 | printWriter.println("value: \"" + this.value + "\"", (indent ?? 0) + 1); 106 | break; 107 | 108 | } 109 | 110 | } 111 | 112 | } 113 | 114 | for (let entry of this.#properties.entries()) { 115 | 116 | printWriter.print(entry[0] + ": ", (indent ?? 0) + 1); 117 | 118 | if (entry[1] != null) 119 | await entry[1].print(printWriter, (indent ?? 0) + 1); 120 | 121 | else 122 | printWriter.println("null"); 123 | 124 | } 125 | 126 | printWriter.println("}", indent); 127 | 128 | } 129 | 130 | get properties(): Map { 131 | return this.#properties; 132 | } 133 | 134 | toJSON(): any { 135 | 136 | let value: any; 137 | 138 | if (this.type instanceof ModelicaBuiltinClassSymbol) { 139 | 140 | if (Array.isArray(this.value)) { 141 | 142 | value = []; 143 | 144 | for (let element of this.value) 145 | value.push(element?.toJSON()); 146 | 147 | } else { 148 | 149 | value = this.value ?? null; 150 | 151 | } 152 | 153 | } else { 154 | 155 | value = {}; 156 | value["@type"] = this.type.toString(); 157 | 158 | for (let key of this.properties.keys()) 159 | value[key] = this.properties.get(key)?.toJSON(); 160 | 161 | } 162 | 163 | return value; 164 | 165 | } 166 | 167 | get type(): ModelicaClassSymbol { 168 | return this.#type; 169 | } 170 | 171 | get value(): Array | boolean | number | string | undefined { 172 | return this.#value; 173 | } 174 | 175 | set value(value: Array | boolean | number | string | undefined) { 176 | this.#value = value; 177 | } 178 | 179 | } 180 | 181 | export class ModelicaArrayObjectSymbol extends ModelicaObjectSymbol { 182 | 183 | constructor(type: ModelicaArrayClassSymbol, value?: Array) { 184 | super(type, value ?? []); 185 | } 186 | 187 | override get type(): ModelicaArrayClassSymbol { 188 | return super.type; 189 | } 190 | 191 | override get value(): Array { 192 | return >super.value ?? []; 193 | } 194 | 195 | override set value(value: Array) { 196 | super.value = value; 197 | } 198 | 199 | } 200 | 201 | export class ModelicaBooleanObjectSymbol extends ModelicaObjectSymbol { 202 | 203 | constructor(type: ModelicaBooleanClassSymbol, value: boolean) { 204 | super(type, value); 205 | } 206 | 207 | override get type(): ModelicaBooleanClassSymbol { 208 | return super.type; 209 | } 210 | 211 | override get value(): boolean { 212 | return super.value === true; 213 | } 214 | 215 | override set value(value: boolean) { 216 | super.value = value; 217 | } 218 | 219 | } 220 | 221 | export class ModelicaEnumerationObjectSymbol extends ModelicaObjectSymbol { 222 | 223 | constructor(type: ModelicaClassSymbol, value: number) { 224 | super(type, value); 225 | } 226 | 227 | override get value(): number { 228 | return Number(super.value); 229 | } 230 | 231 | override set value(value: number) { 232 | super.value = value; 233 | } 234 | 235 | } 236 | 237 | export abstract class ModelicaNumberObjectSymbol extends ModelicaObjectSymbol { 238 | 239 | constructor(type: ModelicaNumberClassSymbol, value: number) { 240 | super(type, value); 241 | } 242 | 243 | override get type(): ModelicaNumberClassSymbol { 244 | return super.type; 245 | } 246 | 247 | override get value(): number { 248 | return Number(super.value); 249 | } 250 | 251 | override set value(value: number) { 252 | super.value = value; 253 | } 254 | 255 | } 256 | 257 | export class ModelicaIntegerObjectSymbol extends ModelicaNumberObjectSymbol { 258 | 259 | constructor(type: ModelicaIntegerClassSymbol, value: number) { 260 | super(type, value | 0); 261 | } 262 | 263 | override get type(): ModelicaIntegerClassSymbol { 264 | return super.type; 265 | } 266 | 267 | override get value(): number { 268 | return super.value | 0; 269 | } 270 | 271 | override set value(value: number) { 272 | super.value = value; 273 | } 274 | 275 | } 276 | 277 | export class ModelicaRealObjectSymbol extends ModelicaNumberObjectSymbol { 278 | 279 | constructor(type: ModelicaRealClassSymbol, value: number) { 280 | super(type, value); 281 | } 282 | 283 | override get type(): ModelicaRealClassSymbol { 284 | return super.type; 285 | } 286 | 287 | override get value(): number { 288 | return super.value; 289 | } 290 | 291 | override set value(value: number) { 292 | super.value = value; 293 | } 294 | 295 | } 296 | 297 | export class ModelicaStringObjectSymbol extends ModelicaObjectSymbol { 298 | 299 | constructor(type: ModelicaStringClassSymbol, value: string) { 300 | super(type, value); 301 | } 302 | 303 | override get type(): ModelicaStringClassSymbol { 304 | return super.type; 305 | } 306 | 307 | override get value(): string { 308 | return String(super.value); 309 | } 310 | 311 | override set value(value: string) { 312 | super.value = value; 313 | } 314 | 315 | } 316 | 317 | export abstract class ModelicaElementSymbol extends ModelicaSymbol { 318 | 319 | #parent: ModelicaScope; 320 | 321 | constructor(parent: ModelicaScope) { 322 | super(); 323 | this.#parent = parent; 324 | } 325 | 326 | abstract get annotation(): Promise; 327 | 328 | get parent() { 329 | return this.#parent; 330 | } 331 | 332 | abstract get syntax(): ModelicaElementSyntax | undefined; 333 | 334 | get visibility(): ModelicaVisibility | undefined { 335 | return this.syntax?.visibility; 336 | } 337 | 338 | } 339 | 340 | export class ModelicaAlgorithmSectionSymbol extends ModelicaElementSymbol { 341 | 342 | #syntax?: ModelicaAlgorithmSectionSyntax; 343 | 344 | constructor(parent: ModelicaScope, syntax?: ModelicaAlgorithmSectionSyntax) { 345 | super(parent); 346 | this.#syntax = syntax; 347 | } 348 | 349 | override async accept(visitor: ModelicaSymbolVisitor, ...args: any[]): Promise { 350 | return await visitor.visitAlgorithmSection(this, ...args); 351 | } 352 | 353 | override get annotation(): Promise { 354 | 355 | return async function () { 356 | return undefined; 357 | }(); 358 | 359 | } 360 | 361 | override async print(printWriter: PrintWriter, indent?: number): Promise { 362 | printWriter.println("algorithm;", indent); 363 | } 364 | 365 | override get syntax(): ModelicaAlgorithmSectionSyntax | undefined { 366 | return this.#syntax; 367 | } 368 | 369 | } 370 | 371 | export class ModelicaEquationSectionSymbol extends ModelicaElementSymbol { 372 | 373 | #syntax?: ModelicaEquationSectionSyntax; 374 | 375 | constructor(parent: ModelicaScope, syntax?: ModelicaEquationSectionSyntax) { 376 | super(parent); 377 | this.#syntax = syntax; 378 | } 379 | 380 | override async accept(visitor: ModelicaSymbolVisitor, ...args: any[]): Promise { 381 | return await visitor.visitEquationSection(this, ...args); 382 | } 383 | 384 | override get annotation(): Promise { 385 | 386 | return async function () { 387 | return undefined; 388 | }(); 389 | 390 | } 391 | 392 | override async print(printWriter: PrintWriter, indent?: number): Promise { 393 | printWriter.println("equation;", indent); 394 | } 395 | 396 | override get syntax(): ModelicaEquationSectionSyntax | undefined { 397 | return this.#syntax; 398 | } 399 | 400 | } 401 | 402 | export class ModelicaExtendsSymbol extends ModelicaElementSymbol { 403 | 404 | #annotation?: ModelicaAnnotationClassSymbol; 405 | #classSymbol?: ModelicaClassSymbol; 406 | #instantiated?: boolean; 407 | #instantiating?: boolean; 408 | #modificationEnvironment: ModelicaModificationEnvironment; 409 | #syntax?: ModelicaExtendsClauseSyntax; 410 | 411 | constructor(parent: ModelicaScope, modificationEnvironment?: ModelicaModificationEnvironment, syntax?: ModelicaExtendsClauseSyntax) { 412 | super(parent); 413 | this.#modificationEnvironment = modificationEnvironment ?? new ModelicaModificationEnvironment(); 414 | this.#syntax = syntax; 415 | } 416 | 417 | override async accept(visitor: ModelicaSymbolVisitor, ...args: any[]): Promise { 418 | return await visitor.visitExtends(this, ...args); 419 | } 420 | 421 | override get annotation(): Promise { 422 | 423 | let node = this; 424 | 425 | return async function () { 426 | 427 | if (node.#annotation == null) 428 | node.#annotation = await node.syntax?.annotationClause?.instantiate(node.parent); 429 | 430 | return node.#annotation; 431 | 432 | }(); 433 | 434 | } 435 | 436 | get classSymbol(): Promise { 437 | 438 | let node = this; 439 | 440 | return async function () { 441 | 442 | await node.instantiate(); 443 | return node.#classSymbol; 444 | 445 | }(); 446 | 447 | } 448 | 449 | async instantiate(): Promise { 450 | 451 | if (this.#instantiated == true) 452 | return; 453 | 454 | if (this.#instantiating == true) { 455 | this.#instantiated = true; 456 | return; 457 | //throw new Error("Inconsistency detected when instantiating extends: " + this.syntax?.source?.text); 458 | } 459 | 460 | //console.log("Start instantiate extends", this.syntax?.source?.text); 461 | 462 | this.#instantiating = true; 463 | 464 | //console.log("GOOD1"); 465 | let classSymbol = await this.parent?.resolve(this.syntax?.typeSpecifier); 466 | //console.log("GOOD2"); 467 | 468 | if (classSymbol != null && classSymbol instanceof ModelicaClassSymbol) { 469 | //console.log("GOOD3"); 470 | this.#classSymbol = await classSymbol.instantiate(classSymbol.parent, this.modificationEnvironment.merge(this.syntax?.classModification)); 471 | 472 | // FIND BETTER WAY TO RECURSIVLEY INSTANTIATE BASE CLASSES 473 | for await (let baseClass of this.#classSymbol.baseClasses) 474 | ; 475 | //console.log("GOOD4"); 476 | } 477 | 478 | 479 | //console.log("End instantiate extends", this.syntax?.source?.text); 480 | 481 | this.#instantiated = true; 482 | this.#instantiating = undefined; 483 | 484 | } 485 | 486 | get modificationEnvironment(): ModelicaModificationEnvironment { 487 | return this.#modificationEnvironment; 488 | } 489 | 490 | override async print(printWriter: PrintWriter, indent?: number): Promise { 491 | 492 | printWriter.print("extends ", indent); 493 | 494 | let typeSpecifier = this.syntax?.typeSpecifier?.toString(); 495 | 496 | if (typeSpecifier != null) 497 | printWriter.print(typeSpecifier); 498 | 499 | printWriter.println(";"); 500 | 501 | } 502 | 503 | override get syntax(): ModelicaExtendsClauseSyntax | undefined { 504 | return this.#syntax; 505 | } 506 | 507 | } 508 | 509 | export class ModelicaImportSymbol extends ModelicaElementSymbol { 510 | 511 | #syntax?: ModelicaImportClauseSyntax; 512 | 513 | constructor(parent: ModelicaScope, syntax?: ModelicaImportClauseSyntax) { 514 | super(parent); 515 | this.#syntax = syntax; 516 | } 517 | 518 | override accept(visitor: ModelicaSymbolVisitor, ...args: any[]): any { 519 | return visitor.visitImport(this, ...args); 520 | } 521 | 522 | get alias(): ModelicaIdentifierSyntax | undefined { 523 | return this.syntax?.alias; 524 | } 525 | 526 | override get annotation(): Promise { 527 | 528 | return async function () { 529 | return undefined; 530 | }(); 531 | 532 | } 533 | 534 | get elements(): AsyncIterableIterator { 535 | 536 | let node = this; 537 | 538 | return async function* () { 539 | 540 | let element = await node.parent.resolve(node.name, true); 541 | 542 | if (element == null) 543 | return; 544 | 545 | if (node.wildcard) { 546 | 547 | if (element instanceof ModelicaClassSymbol) 548 | yield* element.namedElements; 549 | 550 | } else if (node.imports != null && node.imports.length > 0) { 551 | 552 | if (element instanceof ModelicaClassSymbol) { 553 | 554 | for (let importName of node.imports) { 555 | 556 | let importElement = await element.getNamedElement(importName); 557 | 558 | if (importElement != null) 559 | yield importElement; 560 | 561 | } 562 | 563 | } 564 | 565 | } else { 566 | yield element; 567 | } 568 | 569 | }(); 570 | 571 | } 572 | 573 | get imports(): ModelicaIdentifierSyntax[] | undefined { 574 | return this.syntax?.imports; 575 | } 576 | 577 | get name(): ModelicaNameSyntax | undefined { 578 | return this.syntax?.name; 579 | } 580 | 581 | override async print(printWriter: PrintWriter, indent?: number): Promise { 582 | 583 | printWriter.print("import ", indent); 584 | 585 | let alias = this.alias?.toString(); 586 | 587 | if (alias != null) 588 | printWriter.print(alias + " = "); 589 | 590 | let name = this.name?.toString(); 591 | 592 | if (name != null) 593 | printWriter.print(name); 594 | 595 | if (this.imports != null && this.imports.length > 0) { 596 | 597 | printWriter.print(".{"); 598 | 599 | for (let i = 0; i < this.imports.length; i++) { 600 | 601 | let importName = this.imports[i].toString(); 602 | 603 | if (importName != null) 604 | printWriter.print(importName); 605 | 606 | if (i < this.imports.length - 1) 607 | printWriter.print(", "); 608 | 609 | } 610 | 611 | printWriter.print("}"); 612 | 613 | } else if (this.wildcard == true) { 614 | printWriter.print(".*"); 615 | } 616 | 617 | printWriter.println(";"); 618 | 619 | } 620 | 621 | override get syntax(): ModelicaImportClauseSyntax | undefined { 622 | return this.#syntax; 623 | } 624 | 625 | get wildcard(): boolean | undefined { 626 | return this.syntax?.wildcard; 627 | } 628 | 629 | } 630 | 631 | export abstract class ModelicaNamedElementSymbol extends ModelicaElementSymbol { 632 | 633 | #modificationEnvironment: ModelicaModificationEnvironment; 634 | 635 | constructor(parent: ModelicaScope, modificationEnvironment?: ModelicaModificationEnvironment) { 636 | super(parent); 637 | this.#modificationEnvironment = modificationEnvironment ?? new ModelicaModificationEnvironment(); 638 | } 639 | 640 | get descriptionString(): ModelicaDescriptionStringSyntax | undefined { 641 | return this.syntax?.descriptionString; 642 | } 643 | 644 | abstract get diagram(): Promise; 645 | 646 | abstract get icon(): Promise; 647 | 648 | abstract get simpleIcon(): Promise; 649 | 650 | get identifier(): ModelicaIdentifierSyntax | undefined { 651 | return this.syntax?.identifier; 652 | } 653 | 654 | get modificationEnvironment(): ModelicaModificationEnvironment { 655 | return this.#modificationEnvironment; 656 | } 657 | 658 | get name(): string | undefined { 659 | 660 | if (this.identifier == null || this.identifier.value == null) 661 | return undefined; 662 | 663 | if (this.parent instanceof ModelicaClassSymbol) { 664 | 665 | let parentName = this.parent.name; 666 | 667 | if (parentName == null) 668 | return undefined; 669 | 670 | return parentName + "." + this.identifier.value; 671 | 672 | } 673 | 674 | return this.identifier.value; 675 | 676 | } 677 | 678 | abstract get syntax(): ModelicaNamedElementSyntax | undefined; 679 | 680 | override toString(): string | undefined { 681 | return this.name; 682 | } 683 | 684 | } 685 | 686 | export class ModelicaClassSymbol extends ModelicaNamedElementSymbol implements ModelicaScope { 687 | 688 | #annotation?: ModelicaAnnotationClassSymbol; 689 | #diagram?: string; 690 | #elements?: ModelicaElementSymbol[]; 691 | #icon?: string; 692 | #inconsistent?: boolean; 693 | #instantiated?: boolean; 694 | #instantiatedBaseClasses?: boolean; 695 | #instantiatedComponents?: boolean; 696 | #instantiating?: boolean; 697 | #instantiatingBaseClasses?: boolean; 698 | #instantiatingComponents?: boolean; 699 | #syntax?: ModelicaClassDefinitionSyntax; 700 | 701 | constructor(parent: ModelicaScope, modificationEnvironment?: ModelicaModificationEnvironment, syntax?: ModelicaClassDefinitionSyntax) { 702 | super(parent, modificationEnvironment); 703 | this.#syntax = syntax; 704 | } 705 | 706 | override accept(visitor: ModelicaSymbolVisitor, ...args: any[]): any { 707 | return visitor.visitClass(this, ...args); 708 | } 709 | 710 | get algorithmSections(): AsyncIterableIterator { 711 | 712 | let node = this; 713 | 714 | return async function* () { 715 | 716 | await node.#instantiateBaseClasses(); 717 | 718 | for await (let element of node.elements) { 719 | 720 | if (element instanceof ModelicaAlgorithmSectionSymbol) 721 | yield element; 722 | 723 | else if (node.#instantiatedBaseClasses && element instanceof ModelicaExtendsSymbol) { 724 | 725 | let classSymbol = await element.classSymbol; 726 | 727 | if (classSymbol != null) 728 | yield* classSymbol.algorithmSections; 729 | 730 | } 731 | 732 | } 733 | 734 | }(); 735 | 736 | } 737 | 738 | override get annotation(): Promise { 739 | 740 | let node = this; 741 | 742 | return async function () { 743 | 744 | if (node.#annotation == null) 745 | node.#annotation = await node.syntax?.classSpecifier?.annotationClause?.instantiate(node); 746 | 747 | return node.#annotation; 748 | 749 | }(); 750 | 751 | } 752 | 753 | get baseClasses(): AsyncIterableIterator { 754 | 755 | let node = this; 756 | 757 | return async function* () { 758 | 759 | await node.#instantiateBaseClasses(); 760 | 761 | for await (let element of node.elements ?? []) { 762 | 763 | if (element instanceof ModelicaExtendsSymbol) { 764 | 765 | let classSymbol = await element.classSymbol; 766 | 767 | if (classSymbol != null) { 768 | yield classSymbol; 769 | yield* classSymbol.baseClasses; 770 | } 771 | 772 | } 773 | } 774 | 775 | }(); 776 | 777 | } 778 | 779 | get classes(): AsyncIterableIterator { 780 | 781 | let node = this; 782 | 783 | return async function* () { 784 | 785 | for await (let element of node.namedElements) { 786 | 787 | if (element instanceof ModelicaClassSymbol) 788 | yield element; 789 | 790 | } 791 | 792 | }(); 793 | 794 | } 795 | 796 | get classRestriction(): ModelicaClassRestriction | undefined { 797 | return this.syntax?.classRestriction; 798 | } 799 | 800 | get components(): AsyncIterableIterator { 801 | 802 | let node = this; 803 | 804 | return async function* () { 805 | 806 | await node.#instantiateComponents(); 807 | 808 | for await (let element of node.namedElements) { 809 | 810 | if (element instanceof ModelicaComponentSymbol) 811 | yield element; 812 | 813 | } 814 | 815 | }(); 816 | 817 | } 818 | 819 | get connections(): AsyncIterableIterator { 820 | 821 | let node = this; 822 | 823 | return async function* () { 824 | 825 | for await (let element of node.equationSections) { 826 | 827 | for (let equation of element.syntax?.equations ?? []) { 828 | 829 | if (equation instanceof ModelicaConnectClauseSyntax) 830 | yield equation; 831 | 832 | } 833 | 834 | } 835 | 836 | }(); 837 | 838 | } 839 | 840 | async construct(positionalArguments?: ModelicaExpressionSyntax[], namedArguments?: ModelicaNamedArgumentSyntax[]): Promise { 841 | 842 | let object = new ModelicaObjectSymbol(this); 843 | 844 | let position = 0; 845 | 846 | for await (let component of this.components) { 847 | 848 | if (component instanceof ModelicaEnumerationLiteralComponentSymbol) 849 | continue; 850 | 851 | let key = component.identifier?.toString(); 852 | let value = null; 853 | 854 | for (let namedArgument of namedArguments ?? []) { 855 | 856 | if (namedArgument.identifier?.value == key) { 857 | value = await namedArgument.expression?.evaluate(this); 858 | break; 859 | } 860 | 861 | } 862 | 863 | if (value == null && positionalArguments?.[position] != null) 864 | value = await positionalArguments?.[position]?.evaluate(this); 865 | 866 | if (value == null) 867 | value = await component.value; 868 | 869 | if (value == null) 870 | value = await (await component.classSymbol)?.construct(); 871 | 872 | if (key != null) 873 | object.properties.set(key, value ?? null); 874 | 875 | position++; 876 | 877 | } 878 | 879 | return object; 880 | 881 | } 882 | 883 | get context(): ModelicaContext { 884 | return this.parent.context; 885 | } 886 | 887 | override get diagram(): Promise { 888 | 889 | let node = this; 890 | 891 | return async function () { 892 | 893 | if (node.#diagram != null) 894 | return node.#diagram; 895 | 896 | let svg = new XmlElement("svg"); 897 | svg.attributes.set("xmlns", "http://www.w3.org/2000/svg"); 898 | await renderDiagram(svg, node); 899 | node.#diagram = svg.toString(); 900 | 901 | return node.#diagram; 902 | 903 | }(); 904 | 905 | } 906 | 907 | get elements(): AsyncIterableIterator { 908 | 909 | let node = this; 910 | 911 | return async function* () { 912 | 913 | await node.#instantiate(); 914 | 915 | for (let element of node.#elements ?? []) 916 | yield element; 917 | 918 | }(); 919 | 920 | } 921 | 922 | get equationSections(): AsyncIterableIterator { 923 | 924 | let node = this; 925 | 926 | return async function* () { 927 | 928 | await node.#instantiateBaseClasses(); 929 | 930 | for await (let element of node.elements) { 931 | 932 | if (element instanceof ModelicaEquationSectionSymbol) 933 | yield element; 934 | 935 | else if (node.#instantiatedBaseClasses && element instanceof ModelicaExtendsSymbol) { 936 | 937 | let classSymbol = await element.classSymbol; 938 | 939 | if (classSymbol != null) 940 | yield* classSymbol.equationSections; 941 | 942 | } 943 | 944 | } 945 | 946 | }(); 947 | 948 | } 949 | 950 | 951 | async getNamedElement(identifier: string | ModelicaIdentifierSyntax | null | undefined): Promise { 952 | 953 | identifier = identifier?.toString(); 954 | 955 | if (identifier == null) 956 | return null; 957 | 958 | for await (let namedElement of this.namedElements) { 959 | 960 | if (namedElement.identifier?.toString() == identifier) 961 | return namedElement; 962 | 963 | } 964 | 965 | return null; 966 | 967 | } 968 | 969 | override get simpleIcon(): Promise { 970 | 971 | let node = this; 972 | 973 | return async function () { 974 | 975 | if (node.#icon != null) 976 | return node.#icon; 977 | 978 | let svg = new XmlElement("svg"); 979 | svg.attributes.set("xmlns", "http://www.w3.org/2000/svg"); 980 | await renderSimpleIcon(svg, node); 981 | node.#icon = svg.toString(); 982 | 983 | return node.#icon; 984 | 985 | }(); 986 | 987 | } 988 | 989 | override get icon(): Promise { 990 | 991 | let node = this; 992 | 993 | return async function () { 994 | 995 | if (node.#icon != null) 996 | return node.#icon; 997 | 998 | let svg = new XmlElement("svg"); 999 | svg.attributes.set("xmlns", "http://www.w3.org/2000/svg"); 1000 | await renderIcon(svg, node); 1001 | node.#icon = svg.toString(); 1002 | 1003 | return node.#icon; 1004 | 1005 | }(); 1006 | 1007 | } 1008 | 1009 | get imports(): AsyncIterableIterator { 1010 | 1011 | let node = this; 1012 | 1013 | return async function* () { 1014 | 1015 | for await (let element of node.elements ?? []) { 1016 | 1017 | if (element instanceof ModelicaImportSymbol) 1018 | yield element; 1019 | 1020 | } 1021 | 1022 | }(); 1023 | 1024 | } 1025 | 1026 | async instantiate(parent: ModelicaScope, modificationEnvironment?: ModelicaModificationEnvironment): Promise { 1027 | 1028 | if (modificationEnvironment == null || modificationEnvironment.arguments == null || modificationEnvironment.arguments.length == 0) 1029 | //modificationEnvironment = this.modificationEnvironment; 1030 | return this; 1031 | 1032 | 1033 | modificationEnvironment = modificationEnvironment.merge(this.modificationEnvironment); 1034 | return new ModelicaClassSymbol(parent, modificationEnvironment, this.syntax); 1035 | 1036 | } 1037 | 1038 | async #instantiate(): Promise { 1039 | 1040 | if (this.#instantiated == true) 1041 | return; 1042 | 1043 | if (this.#instantiating == true) { 1044 | this.#instantiated = true; 1045 | return; 1046 | //throw new Error("Inconsistency detected when instantiating class: " + this.name); 1047 | } 1048 | 1049 | //console.log("Start Instantiate:", this.name); 1050 | 1051 | this.#instantiating = true; 1052 | 1053 | this.#elements = []; 1054 | 1055 | let enumerationValue = 0; 1056 | 1057 | for (let element of this.syntax?.elements ?? []) { 1058 | 1059 | if (element instanceof ModelicaEnumerationLiteralSyntax) 1060 | this.#elements.push(await element.instantiate(this, this.modificationEnvironment.getModificationEnvironment(element.identifier?.value), enumerationValue++)); 1061 | 1062 | else if (element instanceof ModelicaNamedElementSyntax) 1063 | this.#elements.push(await element.instantiate(this, this.modificationEnvironment.getModificationEnvironment(element.identifier?.value))); 1064 | 1065 | else 1066 | this.#elements.push(await element.instantiate(this, this.modificationEnvironment)); 1067 | 1068 | } 1069 | 1070 | if (this.syntax?.library != null && this.syntax?.filePath != null) { 1071 | 1072 | for await (let fileName of this.syntax.library.list(...this.syntax.filePath)) { 1073 | 1074 | let storedDefinition = await this.syntax.library.load(...this.syntax.filePath, fileName); 1075 | 1076 | if (storedDefinition != null) { 1077 | 1078 | let classSymbols = await storedDefinition.instantiate(this, this.modificationEnvironment); 1079 | 1080 | if (classSymbols.length > 0) 1081 | this.#elements.push(classSymbols[0]); 1082 | 1083 | } 1084 | 1085 | } 1086 | 1087 | } 1088 | 1089 | //console.log("End Instantiate:", this.name); 1090 | 1091 | this.#instantiated = true; 1092 | this.#instantiating = undefined; 1093 | 1094 | } 1095 | 1096 | async #instantiateBaseClasses(): Promise { 1097 | 1098 | if (this.#instantiatedBaseClasses == true) 1099 | return; 1100 | 1101 | if (this.#instantiatingBaseClasses == true) { 1102 | this.#instantiatedBaseClasses = true; 1103 | return; 1104 | //throw new Error("Inconsistency detected when instantiating base classes for class: " + this.name); 1105 | } 1106 | 1107 | //console.log("Start Base Instantiate1:", this.name); 1108 | 1109 | await this.#instantiate(); 1110 | 1111 | //console.log("Start Base Instantiate2:", this.name); 1112 | 1113 | this.#instantiatingBaseClasses = true; 1114 | 1115 | for (let element of this.#elements ?? []) { 1116 | 1117 | if (element instanceof ModelicaExtendsSymbol) 1118 | await element.instantiate(); 1119 | 1120 | } 1121 | 1122 | //console.log("End Base Instantiate:", this.name); 1123 | 1124 | this.#instantiatedBaseClasses = true; 1125 | this.#instantiatingBaseClasses = undefined; 1126 | 1127 | } 1128 | 1129 | async #instantiateComponents(): Promise { 1130 | 1131 | if (this.#instantiatedComponents == true) 1132 | return; 1133 | 1134 | if (this.#instantiatingComponents == true) { 1135 | this.#instantiatedComponents = true; 1136 | return; 1137 | //throw new Error("Inconsistency detected when instantiating components for class: " + this.name); 1138 | } 1139 | 1140 | await this.#instantiateBaseClasses(); 1141 | 1142 | this.#instantiatingComponents = true; 1143 | 1144 | for await (let element of this.#elements ?? []) { 1145 | 1146 | if (element instanceof ModelicaComponentSymbol) 1147 | await element.instantiate(); 1148 | 1149 | } 1150 | 1151 | this.#instantiatedComponents = true; 1152 | this.#instantiatingComponents = undefined; 1153 | 1154 | } 1155 | 1156 | get namedElements(): AsyncIterableIterator { 1157 | 1158 | let node = this; 1159 | 1160 | return async function* () { 1161 | 1162 | for await (let element of node.elements) { 1163 | 1164 | if (element instanceof ModelicaNamedElementSymbol) 1165 | yield element; 1166 | 1167 | else if (node.#instantiatedBaseClasses && element instanceof ModelicaExtendsSymbol) { 1168 | 1169 | let classSymbol = await element.classSymbol; 1170 | 1171 | if (classSymbol != null) 1172 | yield* classSymbol.namedElements; 1173 | 1174 | } 1175 | 1176 | } 1177 | 1178 | }(); 1179 | 1180 | } 1181 | 1182 | override async print(printWriter: PrintWriter, indent?: number): Promise { 1183 | 1184 | await this.#instantiateBaseClasses(); 1185 | await this.#instantiateComponents(); 1186 | 1187 | printWriter.println(this.classRestriction + " " + this.identifier, indent); 1188 | 1189 | for await (let element of this.namedElements) 1190 | await element.print(printWriter, (indent ?? 0) + 1); 1191 | 1192 | let annotation = await (await this.annotation)?.evaluate(); 1193 | 1194 | if (annotation != null) { 1195 | 1196 | printWriter.println("annotation(", (indent ?? 0) + 1); 1197 | 1198 | for (let element of annotation.value) { 1199 | 1200 | printWriter.print(element.type.name + ": ", (indent ?? 0) + 2); 1201 | await element.print(printWriter, (indent ?? 0) + 2); 1202 | } 1203 | 1204 | printWriter.println(");", (indent ?? 0) + 1); 1205 | 1206 | } 1207 | 1208 | printWriter.println("end " + this.identifier + ";", indent); 1209 | 1210 | } 1211 | 1212 | async rdf(printWriter: PrintWriter, indent?: number): Promise { 1213 | 1214 | printWriter.println(":" + this.name + " rdf:type owl:Class .", indent); 1215 | 1216 | for await (let baseClass of this.baseClasses) 1217 | printWriter.println(":" + this.name + " rdfs:subClassOf " + ":" + baseClass.name); 1218 | 1219 | for await (let clazz of this.classes) { 1220 | await clazz.rdf(printWriter, indent); 1221 | } 1222 | 1223 | } 1224 | 1225 | async resolve(reference: ModelicaIdentifierSyntax | ModelicaNameSyntax | ModelicaTypeSpecifierSyntax | string[] | string | null | undefined, global?: boolean): Promise { 1226 | 1227 | //console.log("RESOLVE: ", reference?.toString(), " in scope ", this.name); 1228 | 1229 | if (reference == null) 1230 | return null; 1231 | 1232 | global = global ?? false; 1233 | 1234 | if (reference instanceof ModelicaTypeSpecifierSyntax) 1235 | global = reference.global; 1236 | 1237 | let namedElementSymbol = null; 1238 | 1239 | if (this.parent == null || global != true) { 1240 | 1241 | let identifiers = getIdentifiers(reference); 1242 | 1243 | //console.log("RGOOD1", this.#instantiated, this.#instantiating, identifiers?.[0]); 1244 | namedElementSymbol = await this.getNamedElement(identifiers?.[0]); 1245 | //console.log("RGOOD2", namedElementSymbol); 1246 | 1247 | if (namedElementSymbol != null) { 1248 | 1249 | for (let part of identifiers?.slice(1) ?? []) { 1250 | 1251 | if (namedElementSymbol instanceof ModelicaClassSymbol) 1252 | namedElementSymbol = await namedElementSymbol.getNamedElement(part); 1253 | 1254 | else 1255 | namedElementSymbol = null; 1256 | 1257 | if (namedElementSymbol == null) 1258 | break; 1259 | 1260 | } 1261 | 1262 | } 1263 | 1264 | } 1265 | 1266 | if (namedElementSymbol == null) { 1267 | 1268 | namedElementSymbol = await this.parent?.resolve(reference, global); 1269 | 1270 | } else if (reference instanceof ModelicaTypeSpecifierSyntax && reference.subscripts != null && reference.subscripts.length > 0) { 1271 | 1272 | if (namedElementSymbol instanceof ModelicaClassSymbol) { 1273 | 1274 | let shape = []; 1275 | 1276 | for (let subscript of reference.subscripts) { 1277 | 1278 | let value = await subscript.expression?.evaluate(this); 1279 | 1280 | if (value instanceof ModelicaNumberObjectSymbol) 1281 | shape.push(value.value); 1282 | 1283 | else 1284 | shape.push(undefined); 1285 | 1286 | } 1287 | 1288 | return new ModelicaArrayClassSymbol(namedElementSymbol.parent, undefined, undefined, namedElementSymbol, shape); 1289 | 1290 | } 1291 | 1292 | return null; 1293 | 1294 | } 1295 | 1296 | return namedElementSymbol; 1297 | 1298 | } 1299 | 1300 | async resolveFunction(reference: ModelicaComponentReferenceExpressionSyntax | ModelicaIdentifierSyntax | ModelicaNameSyntax | ModelicaTypeSpecifierSyntax | string[] | string | null | undefined, global?: boolean): Promise { 1301 | 1302 | if (reference == null) 1303 | return null; 1304 | 1305 | if (!(reference instanceof ModelicaComponentReferenceExpressionSyntax)) 1306 | return null; 1307 | 1308 | let components = toArray(reference.components); 1309 | 1310 | if (components.length == 0) 1311 | return null; 1312 | 1313 | let symbol: ModelicaNamedElementSymbol | null = await this.resolve(components[0].identifier, components[0].global); 1314 | 1315 | for (let component of components.slice(1)) { 1316 | 1317 | if (symbol instanceof ModelicaClassSymbol) 1318 | symbol = await symbol.getNamedElement(component.identifier); 1319 | 1320 | else if (symbol instanceof ModelicaComponentSymbol) 1321 | symbol = await (await symbol.classSymbol)?.getNamedElement(component.identifier) ?? null; 1322 | 1323 | else 1324 | symbol = null; 1325 | 1326 | if (symbol == null) 1327 | break; 1328 | 1329 | } 1330 | 1331 | if (symbol instanceof ModelicaClassSymbol && (symbol.classRestriction == ModelicaClassRestriction.FUNCTION || symbol.classRestriction == ModelicaClassRestriction.RECORD)) 1332 | return symbol; 1333 | 1334 | return null; 1335 | 1336 | } 1337 | 1338 | override get syntax(): ModelicaClassDefinitionSyntax | undefined { 1339 | return this.#syntax; 1340 | } 1341 | 1342 | override toString(): string | undefined { 1343 | return this.identifier?.toString(); 1344 | } 1345 | 1346 | } 1347 | 1348 | export abstract class ModelicaBuiltinClassSymbol extends ModelicaClassSymbol { 1349 | 1350 | #identifier?: ModelicaIdentifierSyntax; 1351 | 1352 | constructor(parent: ModelicaScope, modificationEnvironment?: ModelicaModificationEnvironment, identifier?: string) { 1353 | 1354 | super(parent, modificationEnvironment); 1355 | 1356 | if (identifier != null) 1357 | this.#identifier = new ModelicaIdentifierSyntax(null, identifier); 1358 | 1359 | } 1360 | 1361 | abstract override get classRestriction(): ModelicaClassRestriction | undefined; 1362 | 1363 | override get identifier(): ModelicaIdentifierSyntax | undefined { 1364 | return this.#identifier; 1365 | } 1366 | 1367 | override get name(): string | undefined { 1368 | 1369 | if (this.identifier == null || this.identifier.value == null) 1370 | return undefined; 1371 | 1372 | return this.identifier.value; 1373 | 1374 | } 1375 | 1376 | abstract override instantiate(parent: ModelicaScope, modificationEnvironment?: ModelicaModificationEnvironment): Promise; 1377 | 1378 | } 1379 | 1380 | export class ModelicaArrayClassSymbol extends ModelicaBuiltinClassSymbol { 1381 | 1382 | #dtype?: ModelicaClassSymbol; 1383 | #shape?: (number | undefined)[]; 1384 | 1385 | constructor(parent: ModelicaScope, modificationEnvironment?: ModelicaModificationEnvironment, identifier?: string, dtype?: ModelicaClassSymbol, shape?: (number | undefined)[]) { 1386 | super(parent, modificationEnvironment, (identifier ?? "Array<" + (dtype?.name ?? "object") + ">") + "[" + (shape?.join(",") ?? ":") + "]"); 1387 | this.#dtype = dtype; 1388 | this.#shape = shape; 1389 | } 1390 | 1391 | override get classRestriction(): ModelicaClassRestriction { 1392 | return ModelicaClassRestriction.TYPE; 1393 | } 1394 | 1395 | get dtype(): ModelicaClassSymbol | undefined { 1396 | return this.#dtype; 1397 | } 1398 | 1399 | set dtype(dtype: ModelicaClassSymbol | undefined) { 1400 | this.#dtype = dtype; 1401 | } 1402 | 1403 | override async construct(): Promise { 1404 | 1405 | let object = new ModelicaArrayObjectSymbol(this); 1406 | 1407 | for await (let component of this.components) { 1408 | 1409 | let key = component.identifier?.toString(); 1410 | let value = await component.value; 1411 | 1412 | if (value == null) 1413 | value = await (await component.classSymbol)?.construct(); 1414 | 1415 | if (key != null) 1416 | object.properties.set(key, value ?? null); 1417 | 1418 | } 1419 | 1420 | return object; 1421 | 1422 | } 1423 | 1424 | override async instantiate(parent: ModelicaScope, modificationEnvironment?: ModelicaModificationEnvironment): Promise { 1425 | return new ModelicaArrayClassSymbol(parent, modificationEnvironment); 1426 | } 1427 | 1428 | get shape(): (number | undefined)[] | undefined { 1429 | return this.#shape; 1430 | } 1431 | 1432 | set shape(shape: (number | undefined)[] | undefined) { 1433 | this.#shape = shape; 1434 | } 1435 | 1436 | } 1437 | 1438 | export class ModelicaBooleanClassSymbol extends ModelicaBuiltinClassSymbol { 1439 | 1440 | constructor(parent: ModelicaScope, modificationEnvironment?: ModelicaModificationEnvironment, identifier?: string) { 1441 | super(parent, modificationEnvironment, identifier ?? "Boolean"); 1442 | } 1443 | 1444 | override get classRestriction(): ModelicaClassRestriction { 1445 | return ModelicaClassRestriction.TYPE; 1446 | } 1447 | 1448 | override async instantiate(parent: ModelicaScope, modificationEnvironment?: ModelicaModificationEnvironment): Promise { 1449 | return new ModelicaBooleanClassSymbol(parent, modificationEnvironment); 1450 | } 1451 | 1452 | } 1453 | 1454 | export abstract class ModelicaNumberClassSymbol extends ModelicaBuiltinClassSymbol { 1455 | 1456 | constructor(parent: ModelicaScope, modificationEnvironment?: ModelicaModificationEnvironment, identifier?: string) { 1457 | super(parent, modificationEnvironment, identifier); 1458 | } 1459 | 1460 | override get classRestriction(): ModelicaClassRestriction { 1461 | return ModelicaClassRestriction.TYPE; 1462 | } 1463 | 1464 | abstract override instantiate(parent: ModelicaScope, modificationEnvironment?: ModelicaModificationEnvironment): Promise; 1465 | 1466 | } 1467 | 1468 | export class ModelicaIntegerClassSymbol extends ModelicaNumberClassSymbol { 1469 | 1470 | constructor(parent: ModelicaScope, modificationEnvironment?: ModelicaModificationEnvironment, identifier?: string) { 1471 | super(parent, modificationEnvironment, identifier ?? "Integer"); 1472 | } 1473 | 1474 | override async instantiate(parent: ModelicaScope, modificationEnvironment?: ModelicaModificationEnvironment): Promise { 1475 | return new ModelicaIntegerClassSymbol(parent, modificationEnvironment); 1476 | } 1477 | 1478 | } 1479 | 1480 | export class ModelicaRealClassSymbol extends ModelicaNumberClassSymbol { 1481 | 1482 | constructor(parent: ModelicaScope, modificationEnvironment?: ModelicaModificationEnvironment, identifier?: string) { 1483 | super(parent, modificationEnvironment, identifier ?? "Real"); 1484 | } 1485 | 1486 | override async instantiate(parent: ModelicaScope, modificationEnvironment?: ModelicaModificationEnvironment): Promise { 1487 | return new ModelicaRealClassSymbol(parent, modificationEnvironment); 1488 | } 1489 | 1490 | } 1491 | 1492 | export class ModelicaStringClassSymbol extends ModelicaBuiltinClassSymbol { 1493 | 1494 | constructor(parent: ModelicaScope, modificationEnvironment?: ModelicaModificationEnvironment, identifer?: string) { 1495 | super(parent, modificationEnvironment, identifer ?? "String"); 1496 | } 1497 | 1498 | override get classRestriction(): ModelicaClassRestriction { 1499 | return ModelicaClassRestriction.TYPE; 1500 | } 1501 | 1502 | override async instantiate(parent: ModelicaScope, modificationEnvironment?: ModelicaModificationEnvironment): Promise { 1503 | return new ModelicaStringClassSymbol(parent, modificationEnvironment); 1504 | } 1505 | 1506 | } 1507 | 1508 | export class ModelicaAnnotationClassSymbol extends ModelicaClassSymbol { 1509 | 1510 | #elements?: ModelicaElementSymbol[]; 1511 | 1512 | constructor(parent: ModelicaScope, elements?: ModelicaElementSymbol[]) { 1513 | super(parent); 1514 | this.#elements = elements; 1515 | } 1516 | 1517 | override get classRestriction(): undefined { 1518 | return undefined; 1519 | } 1520 | 1521 | override get elements(): AsyncIterableIterator { 1522 | 1523 | let node = this; 1524 | 1525 | return async function* () { 1526 | 1527 | if (node.#elements != null) 1528 | yield* node.#elements; 1529 | 1530 | }(); 1531 | 1532 | } 1533 | 1534 | async evaluate(): Promise { 1535 | 1536 | let values = []; 1537 | 1538 | for await (let annotation of this.classes) { 1539 | 1540 | let value = await annotation.construct(); 1541 | 1542 | if (value != null) 1543 | values.push(value); 1544 | 1545 | } 1546 | 1547 | return new ModelicaArrayObjectSymbol(new ModelicaArrayClassSymbol(this), values); 1548 | 1549 | } 1550 | 1551 | override get identifier(): undefined { 1552 | return undefined; 1553 | } 1554 | 1555 | override async instantiate(parent: ModelicaScope): Promise { 1556 | 1557 | let elements: ModelicaElementSymbol[] = []; 1558 | 1559 | for await (let element of this.elements) 1560 | elements.push(element); 1561 | 1562 | return new ModelicaAnnotationClassSymbol(parent, elements); 1563 | 1564 | } 1565 | 1566 | override async print(printWriter: PrintWriter, indent?: number): Promise { 1567 | 1568 | for await (let element of this.elements) { 1569 | await element.print(printWriter, indent); 1570 | } 1571 | 1572 | } 1573 | 1574 | } 1575 | 1576 | export class ModelicaComponentSymbol extends ModelicaNamedElementSymbol { 1577 | 1578 | #annotation?: ModelicaAnnotationClassSymbol; 1579 | #classSymbol?: ModelicaClassSymbol; 1580 | #instantiated?: boolean; 1581 | #instantiating?: boolean; 1582 | #syntax?: ModelicaComponentDeclarationSyntax; 1583 | #value?: ModelicaObjectSymbol; 1584 | 1585 | constructor(parent: ModelicaScope, modificationEnvironment?: ModelicaModificationEnvironment, syntax?: ModelicaComponentDeclarationSyntax, classSymbol?: ModelicaClassSymbol, value?: ModelicaObjectSymbol) { 1586 | super(parent, modificationEnvironment); 1587 | this.#syntax = syntax; 1588 | this.#classSymbol = classSymbol; 1589 | this.#value = value; 1590 | } 1591 | 1592 | override accept(visitor: ModelicaSymbolVisitor, ...args: any[]): any { 1593 | return visitor.visitComponent(this, ...args); 1594 | } 1595 | 1596 | override get annotation(): Promise { 1597 | 1598 | let node = this; 1599 | 1600 | return async function () { 1601 | 1602 | if (node.#annotation == null) 1603 | node.#annotation = await node.syntax?.annotationClause?.instantiate(node.parent); 1604 | 1605 | return node.#annotation; 1606 | 1607 | }(); 1608 | 1609 | } 1610 | 1611 | get classSymbol(): Promise { 1612 | 1613 | let node = this; 1614 | 1615 | return async function () { 1616 | 1617 | await node.instantiate(); 1618 | return node.#classSymbol; 1619 | 1620 | }(); 1621 | 1622 | } 1623 | 1624 | override get diagram(): Promise { 1625 | 1626 | let node = this; 1627 | 1628 | return async function () { 1629 | return await (await node.classSymbol)?.diagram; 1630 | }(); 1631 | 1632 | } 1633 | 1634 | override get simpleIcon(): Promise { 1635 | 1636 | let node = this; 1637 | 1638 | return async function () { 1639 | return await (await node.classSymbol)?.simpleIcon; 1640 | }(); 1641 | 1642 | } 1643 | 1644 | override get icon(): Promise { 1645 | 1646 | let node = this; 1647 | 1648 | return async function () { 1649 | return await (await node.classSymbol)?.icon; 1650 | }(); 1651 | 1652 | } 1653 | 1654 | async instantiate(): Promise { 1655 | 1656 | if (this.#instantiated == true) 1657 | return; 1658 | 1659 | if (this.#instantiating == true) { 1660 | this.#instantiated = true; 1661 | return; 1662 | //throw new Error("Inconsistency detected"); 1663 | } 1664 | 1665 | //console.log("START INSTANTIATE COMPONENT", this.name) 1666 | 1667 | this.#instantiating = true; 1668 | 1669 | let elementModification = this.modificationEnvironment.getElementModification(this.identifier?.value); 1670 | 1671 | if (this.#value == null) { 1672 | 1673 | if (elementModification?.modification?.expression != null) 1674 | this.#value = await elementModification?.modification?.expression?.evaluate(this.parent); 1675 | 1676 | else 1677 | this.#value = await this.syntax?.modification?.expression?.evaluate(this.parent); 1678 | 1679 | } 1680 | 1681 | if (this.#classSymbol == null) { 1682 | 1683 | let classSymbol = await this.parent?.resolve(this.syntax?.typeSpecifier); 1684 | 1685 | if (classSymbol != null && classSymbol instanceof ModelicaClassSymbol) { 1686 | let modificationEnvironment = new ModelicaModificationEnvironment(...(elementModification?.modification?.classModification?.arguments ?? [])); 1687 | this.#classSymbol = await classSymbol.instantiate(classSymbol.parent, modificationEnvironment.merge(this.syntax?.modification?.classModification)); 1688 | } 1689 | 1690 | if (this.#classSymbol != null && this.#value == null && this.syntax?.modification?.expression == null) 1691 | this.#value = await this.#classSymbol.construct(); 1692 | 1693 | } 1694 | 1695 | //console.log("END INSTANTIATE COMPONENT", this.name) 1696 | 1697 | this.#instantiated = true; 1698 | this.#instantiating = undefined; 1699 | 1700 | } 1701 | 1702 | override async print(printWriter: PrintWriter, indent?: number): Promise { 1703 | 1704 | printWriter.print("", indent); 1705 | 1706 | let classSymbol = await this.classSymbol; 1707 | 1708 | let classSymbolIdentifier = classSymbol?.identifier?.toString(); 1709 | 1710 | if (classSymbolIdentifier != null) 1711 | printWriter.print(classSymbolIdentifier); 1712 | 1713 | printWriter.print(" "); 1714 | 1715 | let identifier = this.identifier?.toString(); 1716 | 1717 | if (identifier != null) 1718 | printWriter.print(identifier); 1719 | 1720 | if (this.syntax?.modification != null && classSymbol != null) { 1721 | //console.log(this.syntax.modification.expression?.evaluate(classSymbol)); 1722 | } 1723 | 1724 | printWriter.println(""); 1725 | 1726 | //printWriter.println(" {"); 1727 | //await classSymbol?.print(printWriter, (indent ?? 0) + 1); 1728 | 1729 | let annotation = await (await this.annotation)?.evaluate(); 1730 | 1731 | if (annotation != null) { 1732 | 1733 | printWriter.println("component-annotation(", (indent ?? 0) + 1); 1734 | 1735 | for (let element of annotation.value) { 1736 | 1737 | printWriter.print(element.type.name + ": ", (indent ?? 0) + 2); 1738 | await element.print(printWriter, (indent ?? 0) + 2); 1739 | } 1740 | 1741 | printWriter.println(")", (indent ?? 0) + 1); 1742 | 1743 | } 1744 | 1745 | annotation = await (await (await this.classSymbol)?.annotation)?.evaluate(); 1746 | 1747 | if (annotation != null) { 1748 | 1749 | printWriter.println("class-annotation(", (indent ?? 0) + 1); 1750 | 1751 | for (let element of annotation.value) { 1752 | 1753 | printWriter.print(element.type.name + ": ", (indent ?? 0) + 2); 1754 | await element.print(printWriter, (indent ?? 0) + 2); 1755 | } 1756 | 1757 | printWriter.println(")", (indent ?? 0) + 1); 1758 | 1759 | } 1760 | 1761 | } 1762 | 1763 | override get syntax(): ModelicaComponentDeclarationSyntax | undefined { 1764 | return this.#syntax; 1765 | } 1766 | 1767 | get value(): Promise { 1768 | 1769 | let node = this; 1770 | 1771 | return async function () { 1772 | await node.instantiate(); 1773 | return node.#value; 1774 | }(); 1775 | 1776 | } 1777 | 1778 | } 1779 | 1780 | export class ModelicaEnumerationLiteralComponentSymbol extends ModelicaComponentSymbol { 1781 | 1782 | constructor(parent: ModelicaScope, modificationEnvironment?: ModelicaModificationEnvironment, syntax?: ModelicaEnumerationLiteralSyntax, classSymbol?: ModelicaClassSymbol, value?: number) { 1783 | super(parent, modificationEnvironment, syntax, classSymbol, value != null ? new ModelicaEnumerationObjectSymbol(new ModelicaIntegerClassSymbol(parent.context, undefined, syntax?.identifier?.value), value) : undefined); 1784 | } 1785 | 1786 | accept(visitor: ModelicaSymbolVisitor, ...args: any[]): Promise { 1787 | return visitor.visitEnumerationLiteralComponent(this, ...args); 1788 | } 1789 | 1790 | override toString(): string | undefined { 1791 | return this.identifier?.value; 1792 | } 1793 | 1794 | } 1795 | 1796 | export abstract class ModelicaSymbolVisitor { 1797 | 1798 | visitAlgorithmSection(node: ModelicaAlgorithmSectionSymbol, ...args: any[]): any { 1799 | throw new Error("Method not implemented."); 1800 | } 1801 | 1802 | visitClass(node: ModelicaClassSymbol, ...args: any[]): any { 1803 | throw new Error("Method not implemented."); 1804 | } 1805 | 1806 | visitComponent(node: ModelicaComponentSymbol, ...args: any[]): any { 1807 | throw new Error("Method not implemented."); 1808 | } 1809 | 1810 | visitEnumerationLiteralComponent(node: ModelicaEnumerationLiteralComponentSymbol, ...args: any[]): any { 1811 | throw new Error("Method not implemented."); 1812 | } 1813 | 1814 | visitEquationSection(node: ModelicaEquationSectionSymbol, ...args: any[]): any { 1815 | throw new Error("Method not implemented."); 1816 | } 1817 | 1818 | visitExtends(node: ModelicaExtendsSymbol, ...args: any[]): any { 1819 | throw new Error("Method not implemented."); 1820 | } 1821 | 1822 | visitImport(node: ModelicaImportSymbol, ...args: any[]): any { 1823 | throw new Error("Method not implemented."); 1824 | } 1825 | 1826 | visitObject(node: ModelicaObjectSymbol, ...args: any[]): any { 1827 | throw new Error("Method not implemented."); 1828 | } 1829 | 1830 | } 1831 | --------------------------------------------------------------------------------