@aryacode/dsh-engineering 0.0.0-stage → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +115 -0
- package/LICENSE +21 -0
- package/README.md +228 -2
- package/cordis.patch.yml +122 -0
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +77 -3
- package/vendor/ftview/README.md +55 -0
- package/vendor/ftview/SKILL.md +74 -0
- package/vendor/ftview/lib/checks.d.ts +27 -0
- package/vendor/ftview/lib/checks.js +39 -0
- package/vendor/ftview/lib/core.d.ts +208 -0
- package/vendor/ftview/lib/core.js +160 -0
- package/vendor/ftview/lib/edit/drafts.d.ts +49 -0
- package/vendor/ftview/lib/edit/drafts.js +84 -0
- package/vendor/ftview/lib/edit/operations.d.ts +62 -0
- package/vendor/ftview/lib/edit/operations.js +126 -0
- package/vendor/ftview/lib/edit/prepare.d.ts +38 -0
- package/vendor/ftview/lib/edit/prepare.js +133 -0
- package/vendor/ftview/lib/edit/tools.d.ts +25 -0
- package/vendor/ftview/lib/edit/tools.js +198 -0
- package/vendor/ftview/lib/edit.d.ts +30 -0
- package/vendor/ftview/lib/edit.js +38 -0
- package/vendor/ftview/lib/index.d.ts +31 -0
- package/vendor/ftview/lib/index.js +36 -0
- package/vendor/ftview/lib/model.d.ts +80 -0
- package/vendor/ftview/lib/model.js +267 -0
- package/vendor/ftview/lib/session.d.ts +48 -0
- package/vendor/ftview/lib/session.js +98 -0
- package/vendor/ftview/lib/skill.d.ts +29 -0
- package/vendor/ftview/lib/skill.js +43 -0
- package/vendor/ftview/lib/studio/discover.d.ts +36 -0
- package/vendor/ftview/lib/studio/discover.js +87 -0
- package/vendor/ftview/lib/studio/preflight.d.ts +51 -0
- package/vendor/ftview/lib/studio/preflight.js +64 -0
- package/vendor/ftview/lib/studio/tools.d.ts +15 -0
- package/vendor/ftview/lib/studio/tools.js +83 -0
- package/vendor/ftview/lib/studio.d.ts +30 -0
- package/vendor/ftview/lib/studio.js +37 -0
- package/vendor/ftview/lib/tools.d.ts +14 -0
- package/vendor/ftview/lib/tools.js +202 -0
- package/vendor/l5x/README.md +62 -0
- package/vendor/l5x/SKILL.md +81 -0
- package/vendor/l5x/lib/approvals.d.ts +50 -0
- package/vendor/l5x/lib/approvals.js +93 -0
- package/vendor/l5x/lib/client.js +412 -0
- package/vendor/l5x/lib/client.js.map +1 -0
- package/vendor/l5x/lib/core/errors.d.ts +38 -0
- package/vendor/l5x/lib/core/errors.js +48 -0
- package/vendor/l5x/lib/core/params.d.ts +49 -0
- package/vendor/l5x/lib/core/params.js +39 -0
- package/vendor/l5x/lib/core/result.d.ts +46 -0
- package/vendor/l5x/lib/core/result.js +21 -0
- package/vendor/l5x/lib/core/tool.d.ts +102 -0
- package/vendor/l5x/lib/core/tool.js +19 -0
- package/vendor/l5x/lib/drafts.d.ts +66 -0
- package/vendor/l5x/lib/drafts.js +98 -0
- package/vendor/l5x/lib/editing.d.ts +30 -0
- package/vendor/l5x/lib/editing.js +42 -0
- package/vendor/l5x/lib/engine/acd-edit.d.ts +13 -0
- package/vendor/l5x/lib/engine/acd-edit.js +115 -0
- package/vendor/l5x/lib/engine/analysis.d.ts +22 -0
- package/vendor/l5x/lib/engine/analysis.js +258 -0
- package/vendor/l5x/lib/engine/capabilities.d.ts +9 -0
- package/vendor/l5x/lib/engine/capabilities.js +40 -0
- package/vendor/l5x/lib/engine/change-set.d.ts +122 -0
- package/vendor/l5x/lib/engine/change-set.js +446 -0
- package/vendor/l5x/lib/engine/configuration.d.ts +21 -0
- package/vendor/l5x/lib/engine/configuration.js +42 -0
- package/vendor/l5x/lib/engine/details.d.ts +47 -0
- package/vendor/l5x/lib/engine/details.js +173 -0
- package/vendor/l5x/lib/engine/direct-polling.d.ts +91 -0
- package/vendor/l5x/lib/engine/direct-polling.js +193 -0
- package/vendor/l5x/lib/engine/download-review.d.ts +15 -0
- package/vendor/l5x/lib/engine/download-review.js +63 -0
- package/vendor/l5x/lib/engine/logic-syntax.d.ts +60 -0
- package/vendor/l5x/lib/engine/logic-syntax.js +237 -0
- package/vendor/l5x/lib/engine/observations.d.ts +41 -0
- package/vendor/l5x/lib/engine/observations.js +67 -0
- package/vendor/l5x/lib/engine/project-analysis.d.ts +131 -0
- package/vendor/l5x/lib/engine/project-analysis.js +314 -0
- package/vendor/l5x/lib/engine/review-evidence.d.ts +8 -0
- package/vendor/l5x/lib/engine/review-evidence.js +49 -0
- package/vendor/l5x/lib/engine/review.d.ts +18 -0
- package/vendor/l5x/lib/engine/review.js +123 -0
- package/vendor/l5x/lib/engine/studio-sdk.d.ts +101 -0
- package/vendor/l5x/lib/engine/studio-sdk.js +571 -0
- package/vendor/l5x/lib/engine/tag-resolution.d.ts +20 -0
- package/vendor/l5x/lib/engine/tag-resolution.js +169 -0
- package/vendor/l5x/lib/engine/usage.d.ts +16 -0
- package/vendor/l5x/lib/engine/usage.js +92 -0
- package/vendor/l5x/lib/engine/xml-regions.d.ts +23 -0
- package/vendor/l5x/lib/engine/xml-regions.js +77 -0
- package/vendor/l5x/lib/fs.d.ts +17 -0
- package/vendor/l5x/lib/fs.js +36 -0
- package/vendor/l5x/lib/index.d.ts +36 -0
- package/vendor/l5x/lib/index.js +42 -0
- package/vendor/l5x/lib/live.d.ts +48 -0
- package/vendor/l5x/lib/live.js +48 -0
- package/vendor/l5x/lib/model/aois.d.ts +39 -0
- package/vendor/l5x/lib/model/aois.js +112 -0
- package/vendor/l5x/lib/model/errors.d.ts +15 -0
- package/vendor/l5x/lib/model/errors.js +18 -0
- package/vendor/l5x/lib/model/index.d.ts +20 -0
- package/vendor/l5x/lib/model/index.js +19 -0
- package/vendor/l5x/lib/model/module.d.ts +37 -0
- package/vendor/l5x/lib/model/module.js +54 -0
- package/vendor/l5x/lib/model/modules.d.ts +24 -0
- package/vendor/l5x/lib/model/modules.js +55 -0
- package/vendor/l5x/lib/model/parse.d.ts +32 -0
- package/vendor/l5x/lib/model/parse.js +116 -0
- package/vendor/l5x/lib/model/project.d.ts +15 -0
- package/vendor/l5x/lib/model/project.js +31 -0
- package/vendor/l5x/lib/model/routines.d.ts +71 -0
- package/vendor/l5x/lib/model/routines.js +183 -0
- package/vendor/l5x/lib/model/tags.d.ts +51 -0
- package/vendor/l5x/lib/model/tags.js +119 -0
- package/vendor/l5x/lib/model/types.d.ts +304 -0
- package/vendor/l5x/lib/model/types.js +12 -0
- package/vendor/l5x/lib/model/udts.d.ts +34 -0
- package/vendor/l5x/lib/model/udts.js +81 -0
- package/vendor/l5x/lib/model/xml.d.ts +139 -0
- package/vendor/l5x/lib/model/xml.js +291 -0
- package/vendor/l5x/lib/model/xref.d.ts +28 -0
- package/vendor/l5x/lib/model/xref.js +154 -0
- package/vendor/l5x/lib/session.d.ts +119 -0
- package/vendor/l5x/lib/session.js +236 -0
- package/vendor/l5x/lib/skill.d.ts +30 -0
- package/vendor/l5x/lib/skill.js +44 -0
- package/vendor/l5x/lib/studio-state.d.ts +77 -0
- package/vendor/l5x/lib/studio-state.js +79 -0
- package/vendor/l5x/lib/studio.d.ts +29 -0
- package/vendor/l5x/lib/studio.js +40 -0
- package/vendor/l5x/lib/tools/checks.d.ts +19 -0
- package/vendor/l5x/lib/tools/checks.js +140 -0
- package/vendor/l5x/lib/tools/common.d.ts +41 -0
- package/vendor/l5x/lib/tools/common.js +84 -0
- package/vendor/l5x/lib/tools/controller.d.ts +21 -0
- package/vendor/l5x/lib/tools/controller.js +170 -0
- package/vendor/l5x/lib/tools/drafts.d.ts +23 -0
- package/vendor/l5x/lib/tools/drafts.js +270 -0
- package/vendor/l5x/lib/tools/live.d.ts +21 -0
- package/vendor/l5x/lib/tools/live.js +175 -0
- package/vendor/l5x/lib/tools/read.d.ts +19 -0
- package/vendor/l5x/lib/tools/read.js +245 -0
- package/vendor/l5x/lib/tools/references.d.ts +20 -0
- package/vendor/l5x/lib/tools/references.js +269 -0
- package/vendor/l5x/lib/tools/studio.d.ts +21 -0
- package/vendor/l5x/lib/tools/studio.js +193 -0
- package/vendor/l5x/poll-worker.mjs +77 -0
- package/vendor/l5x/studio-bridge/NuGet.Config +1 -0
- package/vendor/l5x/studio-bridge/Program.cs +304 -0
- package/vendor/l5x/studio-bridge/StudioBridge.csproj +11 -0
- package/vendor/mwi/README.md +60 -0
- package/vendor/mwi/SKILL.md +87 -0
- package/vendor/mwi/bridge/README.md +8 -0
- package/vendor/mwi/bridge/mw_bridge.ps1 +2763 -0
- package/vendor/mwi/bridge/sdk-interop-importer.cs +30 -0
- package/vendor/mwi/bridge/sdk-variable-reader.cs +22 -0
- package/vendor/mwi/bridge/start_bridge.cmd +21 -0
- package/vendor/mwi/bridge/watch_dialogs.cmd +13 -0
- package/vendor/mwi/bridge/watch_dialogs.ps1 +153 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/__init__.py +14 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/__init__.cpython-312.pyc +0 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/block_interfaces.cpython-312.pyc +0 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/cfb.cpython-312.pyc +0 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/datatypes.cpython-312.pyc +0 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/declarations.cpython-312.pyc +0 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/errors.cpython-312.pyc +0 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/graphical_listing.cpython-312.pyc +0 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/grid.cpython-312.pyc +0 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/knowledge.cpython-312.pyc +0 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/mwt_bind.cpython-312.pyc +0 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/navigation.cpython-312.pyc +0 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/program_checks.cpython-312.pyc +0 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/project.cpython-312.pyc +0 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/staging.cpython-312.pyc +0 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/structure.cpython-312.pyc +0 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/task_bindings.cpython-312.pyc +0 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/tasks.cpython-312.pyc +0 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/tree.cpython-312.pyc +0 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/validation.cpython-312.pyc +0 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/variable_edit.cpython-312.pyc +0 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/variables.cpython-312.pyc +0 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/workflow.cpython-312.pyc +0 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/block_interfaces.py +275 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/build.py +339 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/capture.py +448 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/cfb.py +765 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/com.py +268 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/datatypes.py +204 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/declarations.py +399 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/descriptions.py +30 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/eip.py +292 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/errors.py +36 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/graphical_listing.py +208 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/grid.py +227 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/ide.py +317 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/iec.py +317 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/installed_help.py +151 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/knowledge.py +218 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/libraries.py +250 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/manuals.py +520 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/mwt_bind.py +133 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/navigation.py +44 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/pou_writer.py +1547 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/program_checks.py +360 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/project.py +704 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/reference_data/catalog.json +672 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/reference_data/patterns.json +86 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/restore.py +208 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/server.py +1913 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/snapshot.py +138 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/st_comments.py +25 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/staging.py +179 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/stlint.py +676 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/structure.py +98 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/task_bindings.py +68 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/tasks.py +162 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/transaction.py +157 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/tree.py +563 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/tree_writer.py +790 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/uia.py +465 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/validation.py +41 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/variable_edit.py +183 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/variables.py +472 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/workflow.py +130 -0
- package/vendor/mwi/code/engine/motionworks_iec_mcp/writer.py +666 -0
- package/vendor/mwi/code/mw_code.py +1258 -0
- package/vendor/mwi/legacy/edit-session.js +50 -0
- package/vendor/mwi/legacy/fb-insertion.js +74 -0
- package/vendor/mwi/legacy/native-code.js +83 -0
- package/vendor/mwi/legacy/native-groups.js +75 -0
- package/vendor/mwi/legacy/native-structure.js +143 -0
- package/vendor/mwi/legacy/native-variables.js +77 -0
- package/vendor/mwi/legacy/pou-conversion.js +47 -0
- package/vendor/mwi/legacy/pou-package.js +66 -0
- package/vendor/mwi/lib/core/bridge.d.ts +71 -0
- package/vendor/mwi/lib/core/bridge.js +53 -0
- package/vendor/mwi/lib/core/code-engine.d.ts +47 -0
- package/vendor/mwi/lib/core/code-engine.js +109 -0
- package/vendor/mwi/lib/core/errors.d.ts +37 -0
- package/vendor/mwi/lib/core/errors.js +47 -0
- package/vendor/mwi/lib/core/params.d.ts +52 -0
- package/vendor/mwi/lib/core/params.js +46 -0
- package/vendor/mwi/lib/core/ps-bridge.d.ts +62 -0
- package/vendor/mwi/lib/core/ps-bridge.js +127 -0
- package/vendor/mwi/lib/core/result.d.ts +45 -0
- package/vendor/mwi/lib/core/result.js +20 -0
- package/vendor/mwi/lib/core/tool.d.ts +80 -0
- package/vendor/mwi/lib/core/tool.js +55 -0
- package/vendor/mwi/lib/edit/compare.d.ts +33 -0
- package/vendor/mwi/lib/edit/compare.js +70 -0
- package/vendor/mwi/lib/edit.d.ts +20 -0
- package/vendor/mwi/lib/edit.js +32 -0
- package/vendor/mwi/lib/index.d.ts +21 -0
- package/vendor/mwi/lib/index.js +33 -0
- package/vendor/mwi/lib/read/service.d.ts +117 -0
- package/vendor/mwi/lib/read/service.js +229 -0
- package/vendor/mwi/lib/read.d.ts +21 -0
- package/vendor/mwi/lib/read.js +33 -0
- package/vendor/mwi/lib/session/project.d.ts +34 -0
- package/vendor/mwi/lib/session/project.js +54 -0
- package/vendor/mwi/lib/session/service.d.ts +167 -0
- package/vendor/mwi/lib/session/service.js +566 -0
- package/vendor/mwi/lib/session/stage.d.ts +84 -0
- package/vendor/mwi/lib/session/stage.js +187 -0
- package/vendor/mwi/lib/session/workspace.d.ts +52 -0
- package/vendor/mwi/lib/session/workspace.js +90 -0
- package/vendor/mwi/lib/skill.d.ts +36 -0
- package/vendor/mwi/lib/skill.js +56 -0
- package/vendor/mwi/lib/tools/edit.d.ts +20 -0
- package/vendor/mwi/lib/tools/edit.js +286 -0
- package/vendor/mwi/lib/tools/read.d.ts +19 -0
- package/vendor/mwi/lib/tools/read.js +161 -0
- package/vendor/mwi/lib/tools/session.d.ts +20 -0
- package/vendor/mwi/lib/tools/session.js +166 -0
- package/vendor/mwi/lib/tools/verify.d.ts +19 -0
- package/vendor/mwi/lib/tools/verify.js +85 -0
- package/vendor/mwi/lib/verify.d.ts +17 -0
- package/vendor/mwi/lib/verify.js +29 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* User-defined-type reader.
|
|
3
|
+
*
|
|
4
|
+
* Only types the export keeps in the `NoFamily` family are user-defined; the
|
|
5
|
+
* remaining entries in `DataTypes` are the built-in types Studio 5000 writes
|
|
6
|
+
* out alongside them, and reporting those as UDTs would bury the real ones.
|
|
7
|
+
* Hidden members are omitted because the export marks them as preprocessor
|
|
8
|
+
* bookkeeping rather than part of the interface.
|
|
9
|
+
*/
|
|
10
|
+
import type { L5xDocument, L5xUdt } from './types.ts';
|
|
11
|
+
import { type XmlElement } from './xml.ts';
|
|
12
|
+
/**
|
|
13
|
+
* List every user-defined type's name.
|
|
14
|
+
*
|
|
15
|
+
* @param document - The parsed project.
|
|
16
|
+
* @returns Type names, in written order.
|
|
17
|
+
*/
|
|
18
|
+
export declare function listUdtNames(document: L5xDocument): readonly string[];
|
|
19
|
+
/**
|
|
20
|
+
* Read user-defined types with their visible members.
|
|
21
|
+
*
|
|
22
|
+
* @param document - The parsed project.
|
|
23
|
+
* @param name - Type name to restrict to; empty or omitted returns every type.
|
|
24
|
+
* @returns Matching types, in written order. An unknown name yields an empty
|
|
25
|
+
* list rather than an error, matching the reference reader.
|
|
26
|
+
*/
|
|
27
|
+
export declare function getUdt(document: L5xDocument, name?: string): readonly L5xUdt[];
|
|
28
|
+
/**
|
|
29
|
+
* Read every user-defined type of a controller.
|
|
30
|
+
*
|
|
31
|
+
* @param controller - The controller element.
|
|
32
|
+
* @returns The types, in written order, each with its visible members.
|
|
33
|
+
*/
|
|
34
|
+
export declare function buildUdts(controller: XmlElement): readonly L5xUdt[];
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* User-defined-type reader.
|
|
3
|
+
*
|
|
4
|
+
* Only types the export keeps in the `NoFamily` family are user-defined; the
|
|
5
|
+
* remaining entries in `DataTypes` are the built-in types Studio 5000 writes
|
|
6
|
+
* out alongside them, and reporting those as UDTs would bury the real ones.
|
|
7
|
+
* Hidden members are omitted because the export marks them as preprocessor
|
|
8
|
+
* bookkeeping rather than part of the interface.
|
|
9
|
+
*/
|
|
10
|
+
import { descriptionOf } from "./module.js";
|
|
11
|
+
import { elementChildren, firstChild, optionalAttribute, presentAttribute } from "./xml.js";
|
|
12
|
+
/** The family value that marks a data type as user-defined. */
|
|
13
|
+
const NO_FAMILY = 'NoFamily';
|
|
14
|
+
/**
|
|
15
|
+
* List every user-defined type's name.
|
|
16
|
+
*
|
|
17
|
+
* @param document - The parsed project.
|
|
18
|
+
* @returns Type names, in written order.
|
|
19
|
+
*/
|
|
20
|
+
export function listUdtNames(document) {
|
|
21
|
+
return document.udts.map(udt => udt.name);
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Read user-defined types with their visible members.
|
|
25
|
+
*
|
|
26
|
+
* @param document - The parsed project.
|
|
27
|
+
* @param name - Type name to restrict to; empty or omitted returns every type.
|
|
28
|
+
* @returns Matching types, in written order. An unknown name yields an empty
|
|
29
|
+
* list rather than an error, matching the reference reader.
|
|
30
|
+
*/
|
|
31
|
+
export function getUdt(document, name = '') {
|
|
32
|
+
if (name === '')
|
|
33
|
+
return document.udts;
|
|
34
|
+
return document.udts.filter(udt => udt.name === name);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Read every user-defined type of a controller.
|
|
38
|
+
*
|
|
39
|
+
* @param controller - The controller element.
|
|
40
|
+
* @returns The types, in written order, each with its visible members.
|
|
41
|
+
*/
|
|
42
|
+
export function buildUdts(controller) {
|
|
43
|
+
return udtElements(controller).map(udt => ({
|
|
44
|
+
name: optionalAttribute(udt, 'Name', ''),
|
|
45
|
+
description: descriptionOf(udt),
|
|
46
|
+
members: udtMembers(udt),
|
|
47
|
+
}));
|
|
48
|
+
}
|
|
49
|
+
/** The `<DataType>` elements the export keeps in the user-defined family. */
|
|
50
|
+
function udtElements(controller) {
|
|
51
|
+
const dataTypes = firstChild(controller, 'DataTypes');
|
|
52
|
+
if (dataTypes === undefined)
|
|
53
|
+
return [];
|
|
54
|
+
return elementChildren(dataTypes, 'DataType').filter(udt => udt.attrs['Family'] === NO_FAMILY);
|
|
55
|
+
}
|
|
56
|
+
/** One type's visible members, in written order. */
|
|
57
|
+
function udtMembers(udt) {
|
|
58
|
+
const members = firstChild(udt, 'Members');
|
|
59
|
+
if (members === undefined)
|
|
60
|
+
return [];
|
|
61
|
+
const results = [];
|
|
62
|
+
for (const member of elementChildren(members, 'Member')) {
|
|
63
|
+
if (member.attrs['Hidden'] === 'true')
|
|
64
|
+
continue;
|
|
65
|
+
const entry = {
|
|
66
|
+
name: optionalAttribute(member, 'Name', ''),
|
|
67
|
+
dataType: optionalAttribute(member, 'DataType', ''),
|
|
68
|
+
};
|
|
69
|
+
const dimension = optionalAttribute(member, 'Dimension', '0');
|
|
70
|
+
if (dimension !== '0')
|
|
71
|
+
entry.dimension = Number.parseInt(dimension, 10);
|
|
72
|
+
const description = descriptionOf(member);
|
|
73
|
+
if (description !== '')
|
|
74
|
+
entry.description = description;
|
|
75
|
+
const radix = presentAttribute(member, 'Radix');
|
|
76
|
+
if (radix !== undefined)
|
|
77
|
+
entry.radix = radix;
|
|
78
|
+
results.push(entry);
|
|
79
|
+
}
|
|
80
|
+
return results;
|
|
81
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The XML access layer the L5X readers navigate through.
|
|
3
|
+
*
|
|
4
|
+
* `fast-xml-parser` runs in preserved order, so a document is an array of
|
|
5
|
+
* name-keyed nodes and the export's element order survives parsing. Nothing
|
|
6
|
+
* outside this module touches that array: readers ask for children, an
|
|
7
|
+
* attribute, or an element's text and get plain values back.
|
|
8
|
+
*
|
|
9
|
+
* Text follows the reference parser's ElementTree semantics, which is the
|
|
10
|
+
* behavior the readers reproduce. An element's text is the concatenated
|
|
11
|
+
* character data directly inside it — text nodes and CDATA sections, in order —
|
|
12
|
+
* and stops at the first child element; a comment contributes nothing. The
|
|
13
|
+
* readers then decide whether to strip, because the reference strips
|
|
14
|
+
* descriptions and ladder text but keeps Structured Text lines verbatim.
|
|
15
|
+
*/
|
|
16
|
+
/** An element's attributes, exactly as the export wrote them (always strings). */
|
|
17
|
+
interface XmlAttributes {
|
|
18
|
+
readonly [name: string]: string;
|
|
19
|
+
}
|
|
20
|
+
/** One parsed element, in whatever position the export wrote it. */
|
|
21
|
+
export interface XmlElement {
|
|
22
|
+
/** The element's tag name; a non-element node has a reserved name instead. */
|
|
23
|
+
readonly name: string;
|
|
24
|
+
/** The element's direct children, in written order; empty for an empty element. */
|
|
25
|
+
readonly children: readonly XmlNode[];
|
|
26
|
+
/** The element's attributes; empty for an element with none. */
|
|
27
|
+
readonly attrs: XmlAttributes;
|
|
28
|
+
}
|
|
29
|
+
/** A node inside an element: a child element, character data, or a comment. */
|
|
30
|
+
export type XmlNode =
|
|
31
|
+
/** A child element, with its own children and attributes. */
|
|
32
|
+
XmlElement
|
|
33
|
+
/** Character data between elements, or inside an empty element. */
|
|
34
|
+
| {
|
|
35
|
+
readonly name: '#text';
|
|
36
|
+
readonly text: string;
|
|
37
|
+
}
|
|
38
|
+
/** A CDATA section, whose character data is reported as this element's text. */
|
|
39
|
+
| {
|
|
40
|
+
readonly name: '#cdata';
|
|
41
|
+
readonly text: string;
|
|
42
|
+
}
|
|
43
|
+
/** A comment, which contributes no text. */
|
|
44
|
+
| {
|
|
45
|
+
readonly name: '#comment';
|
|
46
|
+
readonly text: string;
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* Parse XML text into preserved-order nodes, one per top-level node.
|
|
50
|
+
*
|
|
51
|
+
* The document is validated first so a malformed export is refused with the
|
|
52
|
+
* parser's own description of what is wrong — an unclosed tag, a stray
|
|
53
|
+
* character — rather than with the partial tree a permissive parse would leave
|
|
54
|
+
* behind. This is a parse failure, not a recoverable one: a reader that went on
|
|
55
|
+
* would report an export that is not there.
|
|
56
|
+
*
|
|
57
|
+
* @param xml - The document text.
|
|
58
|
+
* @returns Every top-level node, in written order.
|
|
59
|
+
* @throws L5xParseError When the text is not well-formed XML.
|
|
60
|
+
*/
|
|
61
|
+
export declare function parseXml(xml: string): readonly XmlNode[];
|
|
62
|
+
/**
|
|
63
|
+
* The document element.
|
|
64
|
+
*
|
|
65
|
+
* An export begins with an XML declaration, which parses as a top-level node
|
|
66
|
+
* named `?xml`, and may carry processing instructions or a DOCTYPE before the
|
|
67
|
+
* root; the first node that is neither is the document element.
|
|
68
|
+
*
|
|
69
|
+
* @param nodes - Top-level nodes from {@link parseXml}.
|
|
70
|
+
* @returns The root element.
|
|
71
|
+
* @throws L5xParseError When the document has no root element.
|
|
72
|
+
*/
|
|
73
|
+
export declare function documentRoot(nodes: readonly XmlNode[]): XmlElement;
|
|
74
|
+
/**
|
|
75
|
+
* Direct children of an element that carry a tag name.
|
|
76
|
+
*
|
|
77
|
+
* @param element - Element to read.
|
|
78
|
+
* @param name - Tag name to match, case-sensitively.
|
|
79
|
+
* @returns Matching children, in written order.
|
|
80
|
+
*/
|
|
81
|
+
export declare function elementChildren(element: XmlElement, name: string): readonly XmlElement[];
|
|
82
|
+
/**
|
|
83
|
+
* The first direct child with a tag name.
|
|
84
|
+
*
|
|
85
|
+
* @param element - Element to read.
|
|
86
|
+
* @param name - Tag name to match, case-sensitively.
|
|
87
|
+
* @returns The first matching child, or undefined when there is none.
|
|
88
|
+
*/
|
|
89
|
+
export declare function firstChild(element: XmlElement, name: string): XmlElement | undefined;
|
|
90
|
+
/**
|
|
91
|
+
* An attribute value.
|
|
92
|
+
*
|
|
93
|
+
* @param element - Element to read.
|
|
94
|
+
* @param name - Attribute name.
|
|
95
|
+
* @returns The value, exactly as written.
|
|
96
|
+
* @throws L5xParseError When the element does not carry the attribute.
|
|
97
|
+
*/
|
|
98
|
+
export declare function attribute(element: XmlElement, name: string): string;
|
|
99
|
+
/**
|
|
100
|
+
* An attribute value, or a caller-chosen substitute when it is absent.
|
|
101
|
+
*
|
|
102
|
+
* @param element - Element to read.
|
|
103
|
+
* @param name - Attribute name.
|
|
104
|
+
* @param fallback - Value returned when the attribute is absent.
|
|
105
|
+
* @returns The value, exactly as written, or the fallback.
|
|
106
|
+
*/
|
|
107
|
+
export declare function optionalAttribute(element: XmlElement, name: string, fallback: string): string;
|
|
108
|
+
/**
|
|
109
|
+
* An attribute value, or undefined when it is absent or empty.
|
|
110
|
+
*
|
|
111
|
+
* The reference readers emit a field only when the export declares something to
|
|
112
|
+
* put in it, so an attribute written as `Radix=""` is reported as undeclared.
|
|
113
|
+
*
|
|
114
|
+
* @param element - Element to read.
|
|
115
|
+
* @param name - Attribute name.
|
|
116
|
+
* @returns The value, or undefined when it is absent or empty.
|
|
117
|
+
*/
|
|
118
|
+
export declare function presentAttribute(element: XmlElement, name: string): string | undefined;
|
|
119
|
+
/**
|
|
120
|
+
* An element's text: its character data, stopping at the first child element.
|
|
121
|
+
*
|
|
122
|
+
* A comment contributes nothing, which is what the reference parser reports.
|
|
123
|
+
* The loop keeps its element arm as the break condition even though `isElement`
|
|
124
|
+
* already answered it, because that answer is what narrows `child` to a node
|
|
125
|
+
* that carries text.
|
|
126
|
+
*
|
|
127
|
+
* @param element - Element to read.
|
|
128
|
+
* @returns The text, or undefined when the element holds none. The caller
|
|
129
|
+
* decides whether to strip it.
|
|
130
|
+
*/
|
|
131
|
+
export declare function nodeText(element: XmlElement): string | undefined;
|
|
132
|
+
/**
|
|
133
|
+
* Whether a node is an element rather than character data or a comment.
|
|
134
|
+
*
|
|
135
|
+
* @param node - Node to classify.
|
|
136
|
+
* @returns True when the node carries children rather than character data.
|
|
137
|
+
*/
|
|
138
|
+
export declare function isElement(node: XmlNode): node is XmlElement;
|
|
139
|
+
export {};
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The XML access layer the L5X readers navigate through.
|
|
3
|
+
*
|
|
4
|
+
* `fast-xml-parser` runs in preserved order, so a document is an array of
|
|
5
|
+
* name-keyed nodes and the export's element order survives parsing. Nothing
|
|
6
|
+
* outside this module touches that array: readers ask for children, an
|
|
7
|
+
* attribute, or an element's text and get plain values back.
|
|
8
|
+
*
|
|
9
|
+
* Text follows the reference parser's ElementTree semantics, which is the
|
|
10
|
+
* behavior the readers reproduce. An element's text is the concatenated
|
|
11
|
+
* character data directly inside it — text nodes and CDATA sections, in order —
|
|
12
|
+
* and stops at the first child element; a comment contributes nothing. The
|
|
13
|
+
* readers then decide whether to strip, because the reference strips
|
|
14
|
+
* descriptions and ladder text but keeps Structured Text lines verbatim.
|
|
15
|
+
*/
|
|
16
|
+
import { XMLParser } from 'fast-xml-parser';
|
|
17
|
+
import { SyntaxValidator } from 'fast-xml-validator';
|
|
18
|
+
import { L5xParseError } from "./errors.js";
|
|
19
|
+
const TEXT_NODE = '#text';
|
|
20
|
+
const COMMENT_NODE = '#comment';
|
|
21
|
+
const parser = new XMLParser({
|
|
22
|
+
preserveOrder: true,
|
|
23
|
+
ignoreAttributes: false,
|
|
24
|
+
attributeNamePrefix: '',
|
|
25
|
+
attributesGroupName: ':@',
|
|
26
|
+
// The reference parser yields attribute values as text and never coerces
|
|
27
|
+
// them, so `Number="0"` stays `"0"` and `parseInt` decides what it means.
|
|
28
|
+
parseAttributeValue: false,
|
|
29
|
+
parseTagValue: false,
|
|
30
|
+
trimValues: false,
|
|
31
|
+
processEntities: true,
|
|
32
|
+
// CDATA is written in real exports. Naming the key keeps CDATA structurally
|
|
33
|
+
// distinct from text at THIS boundary; `nodeText` merges the two, which is
|
|
34
|
+
// what ElementTree reports, while comment nodes stay distinguishable.
|
|
35
|
+
cdataPropName: '#cdata',
|
|
36
|
+
// The reference parser reports a comment as its own node, so the readers can
|
|
37
|
+
// tell one from character data. Left unnamed, the parser drops comments
|
|
38
|
+
// entirely.
|
|
39
|
+
commentPropName: '#comment',
|
|
40
|
+
});
|
|
41
|
+
const CDATA_NODE = '#cdata';
|
|
42
|
+
/**
|
|
43
|
+
* Parse XML text into preserved-order nodes, one per top-level node.
|
|
44
|
+
*
|
|
45
|
+
* The document is validated first so a malformed export is refused with the
|
|
46
|
+
* parser's own description of what is wrong — an unclosed tag, a stray
|
|
47
|
+
* character — rather than with the partial tree a permissive parse would leave
|
|
48
|
+
* behind. This is a parse failure, not a recoverable one: a reader that went on
|
|
49
|
+
* would report an export that is not there.
|
|
50
|
+
*
|
|
51
|
+
* @param xml - The document text.
|
|
52
|
+
* @returns Every top-level node, in written order.
|
|
53
|
+
* @throws L5xParseError When the text is not well-formed XML.
|
|
54
|
+
*/
|
|
55
|
+
export function parseXml(xml) {
|
|
56
|
+
assertWellFormed(xml);
|
|
57
|
+
try {
|
|
58
|
+
const parsed = parser.parse(xml);
|
|
59
|
+
return normalise(asSequence(parsed));
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
/* v8 ignore next -- nesting past the parser's limit is a bare Error; the fallback keeps the message readable. */
|
|
63
|
+
throw new L5xParseError(`Malformed XML: ${error instanceof Error ? error.message : String(error)}`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/** The parser's top-level value, which preservation makes a sequence of nodes. */
|
|
67
|
+
function asSequence(parsed) {
|
|
68
|
+
/* v8 ignore next -- preservation always returns an array; this narrows the parser's any-typed result for the adapter below. */
|
|
69
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Refuse text that is not well-formed XML.
|
|
73
|
+
*
|
|
74
|
+
* The validator reports what is wrong — an unclosed tag, a repeated attribute,
|
|
75
|
+
* a stray character — where a permissive parse would return a partial tree that
|
|
76
|
+
* a reader would then report as a project. This is a parse failure, not a
|
|
77
|
+
* recoverable one.
|
|
78
|
+
*
|
|
79
|
+
* @param xml - The document text.
|
|
80
|
+
* @throws L5xParseError When the text is not well-formed XML.
|
|
81
|
+
*/
|
|
82
|
+
function assertWellFormed(xml) {
|
|
83
|
+
try {
|
|
84
|
+
SyntaxValidator.validate(xml);
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
/* v8 ignore next -- the validator reports refusal as a ValidationError; the literal fallback only covers a non-Error throw. */
|
|
88
|
+
throw new L5xParseError(`Malformed XML: ${error instanceof Error ? error.message : String(error)}`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* The document element.
|
|
93
|
+
*
|
|
94
|
+
* An export begins with an XML declaration, which parses as a top-level node
|
|
95
|
+
* named `?xml`, and may carry processing instructions or a DOCTYPE before the
|
|
96
|
+
* root; the first node that is neither is the document element.
|
|
97
|
+
*
|
|
98
|
+
* @param nodes - Top-level nodes from {@link parseXml}.
|
|
99
|
+
* @returns The root element.
|
|
100
|
+
* @throws L5xParseError When the document has no root element.
|
|
101
|
+
*/
|
|
102
|
+
export function documentRoot(nodes) {
|
|
103
|
+
for (const node of nodes) {
|
|
104
|
+
if (isElement(node) && !node.name.startsWith('?'))
|
|
105
|
+
return node;
|
|
106
|
+
}
|
|
107
|
+
throw new L5xParseError('Malformed XML: no root element');
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Direct children of an element that carry a tag name.
|
|
111
|
+
*
|
|
112
|
+
* @param element - Element to read.
|
|
113
|
+
* @param name - Tag name to match, case-sensitively.
|
|
114
|
+
* @returns Matching children, in written order.
|
|
115
|
+
*/
|
|
116
|
+
export function elementChildren(element, name) {
|
|
117
|
+
const matches = [];
|
|
118
|
+
for (const child of element.children) {
|
|
119
|
+
if (isElement(child) && child.name === name)
|
|
120
|
+
matches.push(child);
|
|
121
|
+
}
|
|
122
|
+
return matches;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* The first direct child with a tag name.
|
|
126
|
+
*
|
|
127
|
+
* @param element - Element to read.
|
|
128
|
+
* @param name - Tag name to match, case-sensitively.
|
|
129
|
+
* @returns The first matching child, or undefined when there is none.
|
|
130
|
+
*/
|
|
131
|
+
export function firstChild(element, name) {
|
|
132
|
+
for (const child of element.children) {
|
|
133
|
+
if (isElement(child) && child.name === name)
|
|
134
|
+
return child;
|
|
135
|
+
}
|
|
136
|
+
return undefined;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* An attribute value.
|
|
140
|
+
*
|
|
141
|
+
* @param element - Element to read.
|
|
142
|
+
* @param name - Attribute name.
|
|
143
|
+
* @returns The value, exactly as written.
|
|
144
|
+
* @throws L5xParseError When the element does not carry the attribute.
|
|
145
|
+
*/
|
|
146
|
+
export function attribute(element, name) {
|
|
147
|
+
const value = element.attrs[name];
|
|
148
|
+
if (value === undefined) {
|
|
149
|
+
throw new L5xParseError(`Malformed XML: <${element.name}> has no ${name} attribute`);
|
|
150
|
+
}
|
|
151
|
+
return value;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* An attribute value, or a caller-chosen substitute when it is absent.
|
|
155
|
+
*
|
|
156
|
+
* @param element - Element to read.
|
|
157
|
+
* @param name - Attribute name.
|
|
158
|
+
* @param fallback - Value returned when the attribute is absent.
|
|
159
|
+
* @returns The value, exactly as written, or the fallback.
|
|
160
|
+
*/
|
|
161
|
+
export function optionalAttribute(element, name, fallback) {
|
|
162
|
+
return element.attrs[name] ?? fallback;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* An attribute value, or undefined when it is absent or empty.
|
|
166
|
+
*
|
|
167
|
+
* The reference readers emit a field only when the export declares something to
|
|
168
|
+
* put in it, so an attribute written as `Radix=""` is reported as undeclared.
|
|
169
|
+
*
|
|
170
|
+
* @param element - Element to read.
|
|
171
|
+
* @param name - Attribute name.
|
|
172
|
+
* @returns The value, or undefined when it is absent or empty.
|
|
173
|
+
*/
|
|
174
|
+
export function presentAttribute(element, name) {
|
|
175
|
+
return element.attrs[name] || undefined;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* An element's text: its character data, stopping at the first child element.
|
|
179
|
+
*
|
|
180
|
+
* A comment contributes nothing, which is what the reference parser reports.
|
|
181
|
+
* The loop keeps its element arm as the break condition even though `isElement`
|
|
182
|
+
* already answered it, because that answer is what narrows `child` to a node
|
|
183
|
+
* that carries text.
|
|
184
|
+
*
|
|
185
|
+
* @param element - Element to read.
|
|
186
|
+
* @returns The text, or undefined when the element holds none. The caller
|
|
187
|
+
* decides whether to strip it.
|
|
188
|
+
*/
|
|
189
|
+
export function nodeText(element) {
|
|
190
|
+
let text = '';
|
|
191
|
+
for (const child of element.children) {
|
|
192
|
+
if (isElement(child))
|
|
193
|
+
break;
|
|
194
|
+
/* v8 ignore next -- a node named as an element is already excluded above, so the comment test is the only remaining arm. */
|
|
195
|
+
if (child.name !== COMMENT_NODE)
|
|
196
|
+
text += child.text;
|
|
197
|
+
}
|
|
198
|
+
return text === '' ? undefined : text;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Whether a node is an element rather than character data or a comment.
|
|
202
|
+
*
|
|
203
|
+
* @param node - Node to classify.
|
|
204
|
+
* @returns True when the node carries children rather than character data.
|
|
205
|
+
*/
|
|
206
|
+
export function isElement(node) {
|
|
207
|
+
return node.name !== TEXT_NODE && node.name !== COMMENT_NODE && node.name !== CDATA_NODE;
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Convert the parser's own object layout into {@link XmlNode} values.
|
|
211
|
+
*
|
|
212
|
+
* `fast-xml-parser` keys a preserved-order node by its tag name and carries
|
|
213
|
+
* children and attributes under reserved keys. The reserved text and comment
|
|
214
|
+
* names carry their character data directly, an element name carries the array
|
|
215
|
+
* of its children, and an attribute group appears only on an element that has
|
|
216
|
+
* attributes.
|
|
217
|
+
*
|
|
218
|
+
* @param value - The children at one position of a parsed document.
|
|
219
|
+
* @returns The equivalent nodes, in written order.
|
|
220
|
+
*/
|
|
221
|
+
/* v8 ignore start -- adapter for fast-xml-parser's preserved-order output; every arm in
|
|
222
|
+
this region is one that layout does not produce, and the suite holds every arm a real
|
|
223
|
+
export does reach. */
|
|
224
|
+
function normalise(value) {
|
|
225
|
+
const nodes = [];
|
|
226
|
+
for (const entry of value) {
|
|
227
|
+
if (isRecord(entry))
|
|
228
|
+
nodes.push(...nodesOf(entry));
|
|
229
|
+
}
|
|
230
|
+
return nodes;
|
|
231
|
+
}
|
|
232
|
+
/** Every node of one parsed entry, in written order. */
|
|
233
|
+
function nodesOf(entry) {
|
|
234
|
+
const nodes = [];
|
|
235
|
+
for (const name of Object.keys(entry)) {
|
|
236
|
+
if (name === ':@')
|
|
237
|
+
continue;
|
|
238
|
+
const children = entry[name];
|
|
239
|
+
if (name === TEXT_NODE)
|
|
240
|
+
nodes.push(characterData(entry));
|
|
241
|
+
else if (name === COMMENT_NODE)
|
|
242
|
+
nodes.push({ name: COMMENT_NODE, text: '' });
|
|
243
|
+
else if (name === CDATA_NODE)
|
|
244
|
+
nodes.push(cdataNode(children));
|
|
245
|
+
else if (Array.isArray(children))
|
|
246
|
+
nodes.push(elementNode(name, children, entry));
|
|
247
|
+
}
|
|
248
|
+
return nodes;
|
|
249
|
+
}
|
|
250
|
+
/** Character data carried by an entry that declares the text node name. */
|
|
251
|
+
function characterData(entry) {
|
|
252
|
+
const text = entry[TEXT_NODE];
|
|
253
|
+
return { name: TEXT_NODE, text: typeof text === 'string' ? text : '' };
|
|
254
|
+
}
|
|
255
|
+
/** A CDATA section, which nests the character data it carries. */
|
|
256
|
+
function cdataNode(children) {
|
|
257
|
+
let text = '';
|
|
258
|
+
if (Array.isArray(children)) {
|
|
259
|
+
for (const child of children) {
|
|
260
|
+
if (isRecord(child))
|
|
261
|
+
text += cdataTextOf(child);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
return { name: CDATA_NODE, text };
|
|
265
|
+
}
|
|
266
|
+
/** The character data inside one entry of a CDATA section. */
|
|
267
|
+
function cdataTextOf(child) {
|
|
268
|
+
const text = child[TEXT_NODE];
|
|
269
|
+
return typeof text === 'string' ? text : '';
|
|
270
|
+
}
|
|
271
|
+
/** One element entry, with its children and attributes. */
|
|
272
|
+
function elementNode(name, children, entry) {
|
|
273
|
+
return { name, children: normalise(children), attrs: attributesOf(entry) };
|
|
274
|
+
}
|
|
275
|
+
/** The attribute group of an element entry, or an empty group when it has none. */
|
|
276
|
+
function attributesOf(entry) {
|
|
277
|
+
const raw = entry[':@'];
|
|
278
|
+
const attrs = {};
|
|
279
|
+
if (isRecord(raw)) {
|
|
280
|
+
for (const key of Object.keys(raw)) {
|
|
281
|
+
const value = raw[key];
|
|
282
|
+
if (typeof value === 'string')
|
|
283
|
+
attrs[key] = value;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
return attrs;
|
|
287
|
+
}
|
|
288
|
+
/** Whether a parsed value is a keyed record rather than a primitive. */
|
|
289
|
+
function isRecord(value) {
|
|
290
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
291
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-reference search — which symbols a project's logic references, and where.
|
|
3
|
+
*
|
|
4
|
+
* The index is built from decoded ladder NeutralText and Structured Text lines,
|
|
5
|
+
* not from the raw XML, so a symbol is reported the way a reader of the logic
|
|
6
|
+
* would write it: `Motor_1.Running` is one symbol and `Motor_1` is also its own
|
|
7
|
+
* entry, because a caller asking "where is Motor_1 used" expects the dotted
|
|
8
|
+
* references too.
|
|
9
|
+
*
|
|
10
|
+
* Instruction mnemonics and language keywords are excluded — `XIC`, `OTE`, `IF`,
|
|
11
|
+
* and `AND` are not tags, and reporting them would bury every real answer. The
|
|
12
|
+
* index is cached per parsed document, so asking one project several questions
|
|
13
|
+
* scans its logic once.
|
|
14
|
+
*/
|
|
15
|
+
import type { L5xDocument, L5xSymbolUsage } from './types.ts';
|
|
16
|
+
/**
|
|
17
|
+
* Search the project's logic for symbols matching a pattern.
|
|
18
|
+
*
|
|
19
|
+
* The pattern is a regular expression, evaluated case-insensitively. A pattern
|
|
20
|
+
* that is not a valid expression is searched for literally instead, so a tag
|
|
21
|
+
* whose name contains regex punctuation still finds its uses.
|
|
22
|
+
*
|
|
23
|
+
* @param document - The parsed project to search.
|
|
24
|
+
* @param pattern - Regular expression, or literal text when it does not compile.
|
|
25
|
+
* @returns One entry per matching symbol, in the order the symbols were first
|
|
26
|
+
* seen, each with every reference to it and an empty result when nothing matched.
|
|
27
|
+
*/
|
|
28
|
+
export declare function searchLogic(document: L5xDocument, pattern: string): readonly L5xSymbolUsage[];
|