@microtronics/studio-cli 0.46.0 → 0.48.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 CHANGED
@@ -1,9 +1,22 @@
1
1
  # Changelog
2
2
 
3
- ## 0.46.0 (2026-02-04)
3
+ ## 0.48.0 (2026-02-10)
4
4
 
5
- ### Bug Fixes
5
+ ### Features
6
6
 
7
+ * **cli:** support multiline statements in DLO precompiler ([b2aef4d](https://bitbucket.org/microtronics-core/vscode-studio/commits/b2aef4d924bdd328d5f8bb90485d02516cd35d3b))
8
+
9
+ ## 0.47.0 (2026-02-06)
10
+
11
+ ### Bug Fixes
12
+
13
+ * **cli:** add missing 'tar' dev package ([707f755](https://bitbucket.org/microtronics-core/vscode-studio/commits/707f75561af14ff302b8c780e55cd5a156a9187a))
14
+ * **cli:** add missing 'uuid' package ([48c2fec](https://bitbucket.org/microtronics-core/vscode-studio/commits/48c2fec8fa052326706df2b2e2c7a0a9b71ece24))
15
+
16
+ ## 0.46.0 (2026-02-04)
17
+
18
+ ### Bug Fixes
19
+
7
20
  * limit development site name to 50 characters maximum ([6dd6772](https://bitbucket.org/microtronics-core/vscode-studio/commits/6dd6772eb0d0c84ccf3f68b4a3fcbb751a46f7f8)), closes [#bugfix-ready](https://bitbucket.org/microtronics-core/vscode-studio/issues/bugfix-ready)
8
21
 
9
22
  ## 0.45.0 (2026-02-04)
@@ -74,6 +74,102 @@ var LogLevel;
74
74
  const rxpDDE = /^(.*)(^|\s*)DDE_(state|result|aloha|volatile|setting|command)_/;
75
75
  const rxpUplinkRestore = /^(.*)(^|\s*)onUplink(Restore|Apply)_(state|result|aloha|volatile|setting|command)/;
76
76
  const rxpUplinkEvent = /^(.*)(^|\s*)onUplinkEvent/;
77
+ // Regex to detect start of function-like statements that may span multiple lines
78
+ const rxpMultilineFuncStart = /(?:^|\s)(?:assert|catch|applog(?:_ok|_warning|_alarm|_debug|_fatal)?|log(?:_debug|_info|_warn|_error)?)\s*\(/;
79
+ const rxpMultilineCallbackStart = /^\s*#callback\s+\w+\s*\(/;
80
+ /**
81
+ * Count unbalanced parentheses depth in text, respecting string literals.
82
+ * Returns a positive number if there are more opening than closing parens.
83
+ */
84
+ function getParenthesisDepth(text) {
85
+ let depth = 0;
86
+ let inString = false;
87
+ let stringChar = '';
88
+ for (let i = 0; i < text.length; i++) {
89
+ const ch = text[i];
90
+ if (inString) {
91
+ if (ch === '\\') {
92
+ i++;
93
+ continue;
94
+ }
95
+ if (ch === stringChar) {
96
+ inString = false;
97
+ }
98
+ continue;
99
+ }
100
+ if (ch === '"' || ch === "'") {
101
+ inString = true;
102
+ stringChar = ch;
103
+ continue;
104
+ }
105
+ if (ch === '(') {
106
+ depth++;
107
+ }
108
+ else if (ch === ')') {
109
+ depth--;
110
+ }
111
+ }
112
+ return depth;
113
+ }
114
+ /**
115
+ * Check if a line matches any of the multiline-capable replacement patterns.
116
+ */
117
+ function matchesAnyMultilineCapablePattern(line) {
118
+ return (rxpCallback.test(line) ||
119
+ rxpAssertF.test(line) ||
120
+ rxpAssert.test(line) ||
121
+ rxpAssert0.test(line) ||
122
+ rxpCatch.test(line) ||
123
+ rxpApplog.test(line) ||
124
+ rxpApplog0.test(line) ||
125
+ rxpLogBackend.test(line));
126
+ }
127
+ /**
128
+ * Try to accumulate a multiline statement starting at the given line index.
129
+ * Returns null if the line is not a multiline statement start, or if the
130
+ * accumulated joined line doesn't match any replacement pattern.
131
+ */
132
+ function tryAccumulateMultilineStatement(fileBlob, startIndex, startStripped) {
133
+ // Check if this line could start a multiline statement
134
+ const isCandidate = rxpMultilineFuncStart.test(startStripped) || rxpMultilineCallbackStart.test(startStripped);
135
+ if (!isCandidate) {
136
+ return null;
137
+ }
138
+ // Check if parentheses are unbalanced (indicating continuation on next lines)
139
+ const depth = getParenthesisDepth(startStripped);
140
+ if (depth <= 0) {
141
+ return null;
142
+ }
143
+ // Accumulate lines until parentheses are balanced
144
+ let totalDepth = depth;
145
+ const originalLines = [fileBlob[startIndex]];
146
+ const strippedLines = [startStripped];
147
+ let nextIndex = startIndex + 1;
148
+ const maxAccumulation = 100; // Safety limit
149
+ while (totalDepth > 0 && nextIndex < fileBlob.length && nextIndex - startIndex < maxAccumulation) {
150
+ const nextOriginal = fileBlob[nextIndex];
151
+ const nextStripped = nextOriginal.split('//', 1)[0];
152
+ originalLines.push(nextOriginal);
153
+ strippedLines.push(nextStripped);
154
+ totalDepth += getParenthesisDepth(nextStripped);
155
+ nextIndex++;
156
+ }
157
+ // If we couldn't balance the parens or only have one line, abort
158
+ if (originalLines.length <= 1 || totalDepth > 0) {
159
+ return null;
160
+ }
161
+ const joinedOriginal = originalLines.map((l, i) => (i === 0 ? l : l.trim())).join(' ');
162
+ const joinedStripped = strippedLines.map((l, i) => (i === 0 ? l : l.trim())).join(' ');
163
+ // Only use multiline if the joined line actually matches a replacement pattern
164
+ if (!matchesAnyMultilineCapablePattern(joinedStripped)) {
165
+ return null;
166
+ }
167
+ return {
168
+ joinedOriginal,
169
+ joinedStripped,
170
+ extraLineCount: originalLines.length - 1
171
+ };
172
+ }
77
173
  class DloPreCompiler {
78
174
  pragmaDynamic = null;
79
175
  dloFileSequencer = new fileSequencer_1.DloFileSequencer();
@@ -137,11 +233,24 @@ class DloPreCompiler {
137
233
  let outLineNumber = 0;
138
234
  // !!!
139
235
  // !!! output is always single-line to keep line numbers equal to original source!
236
+ // !!! multiline statements are joined into a single line, with empty lines emitted
237
+ // !!! for consumed continuation lines to preserve line number alignment.
140
238
  // !!!
141
239
  for (let lineNumber = 0; lineNumber < fileBlob.length; lineNumber++) {
142
240
  let newLine = fileBlob[lineNumber];
143
- const currentLine = newLine.split('//', 1)[0]; // todo dirty solution! strip-off "end of line" comments
241
+ let currentLine = newLine.split('//', 1)[0]; // todo dirty solution! strip-off "end of line" comments
144
242
  let h = null;
243
+ let consumedExtraLines = 0;
244
+ // Detect multiline statements: if the current single line doesn't match any
245
+ // multiline-capable pattern, try accumulating continuation lines.
246
+ if (!matchesAnyMultilineCapablePattern(currentLine)) {
247
+ const multiline = tryAccumulateMultilineStatement(fileBlob, lineNumber, currentLine);
248
+ if (multiline) {
249
+ newLine = multiline.joinedOriginal;
250
+ currentLine = multiline.joinedStripped;
251
+ consumedExtraLines = multiline.extraLineCount;
252
+ }
253
+ }
145
254
  // #options
146
255
  if ((h = rxpOptions.exec(currentLine))) {
147
256
  newLine = '// ' + newLine;
@@ -303,6 +412,12 @@ class DloPreCompiler {
303
412
  }
304
413
  precompiledData.push(newLine);
305
414
  outLineNumber++;
415
+ // Emit empty lines for consumed multiline continuation lines
416
+ for (let i = 0; i < consumedExtraLines; i++) {
417
+ precompiledData.push('');
418
+ outLineNumber++;
419
+ lineNumber++;
420
+ }
306
421
  }
307
422
  // add undef of file include
308
423
  if (baseName !== 'main.dlo') {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@microtronics/studio-cli",
3
- "version": "0.46.0",
3
+ "version": "0.48.0",
4
4
  "description": "Microtronics Studio CLI Tool",
5
5
  "main": "./out/api.js",
6
6
  "typings": "./dist/studio-cli.d.ts",
@@ -25,6 +25,7 @@
25
25
  "author": "Microtronics",
26
26
  "license": "ISC",
27
27
  "dependencies": {
28
+ "uuid": "^9.0.1",
28
29
  "@gera2ld/tarjs": "^0.3.1",
29
30
  "ajv": "^8.17.1",
30
31
  "ajv-formats": "^3.0.1",
@@ -70,9 +71,9 @@
70
71
  "mocha": "^10.8.2",
71
72
  "mocha-junit-reporter": "^2.2.1",
72
73
  "openapi-typescript": "^7.6.0",
73
- "tar": "^7.4.3",
74
74
  "ts-node": "^10.9.2",
75
- "typescript": "^5.6.3"
75
+ "typescript": "^5.6.3",
76
+ "tar": "^7.5.7"
76
77
  },
77
78
  "mocha": {
78
79
  "require": [