bare-script 3.5.4 → 3.6.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/lib/bare.js +6 -10
- package/lib/include/args.bare +8 -8
- package/lib/include/dataTable.bare +211 -0
- package/lib/include/diff.bare +1 -1
- package/lib/include/markdownUp.bare +8 -117
- package/lib/include/pager.bare +3 -3
- package/lib/include/unittest.bare +365 -89
- package/lib/library.js +58 -0
- package/lib/model.js +70 -12
- package/lib/options.js +45 -2
- package/lib/parser.js +96 -68
- package/lib/runtime.js +87 -28
- package/lib/runtimeAsync.js +68 -49
- package/lib/value.js +39 -2
- package/package.json +2 -2
package/lib/parser.js
CHANGED
|
@@ -9,14 +9,18 @@
|
|
|
9
9
|
*
|
|
10
10
|
* @param {string|string[]} scriptText - The [script text](./language/)
|
|
11
11
|
* @param {number} [startLineNumber = 1] - The script's starting line number
|
|
12
|
+
* @param {?string} [scriptName = null] - The script name
|
|
12
13
|
* @returns {Object} The [BareScript model](./model/#var.vName='BareScript')
|
|
13
14
|
* @throws [BareScriptParserError]{@link module:lib/parser.BareScriptParserError}
|
|
14
15
|
*/
|
|
15
|
-
export function parseScript(scriptText, startLineNumber = 1) {
|
|
16
|
-
const
|
|
16
|
+
export function parseScript(scriptText, startLineNumber = 1, scriptName = null) {
|
|
17
|
+
const lines = [];
|
|
18
|
+
const script = {'statements': [], 'scriptLines': lines};
|
|
19
|
+
if (scriptName !== null) {
|
|
20
|
+
script.scriptName = scriptName;
|
|
21
|
+
}
|
|
17
22
|
|
|
18
23
|
// Line-split all script text
|
|
19
|
-
const lines = [];
|
|
20
24
|
if (typeof scriptText === 'string') {
|
|
21
25
|
lines.push(...scriptText.split(rScriptLineSplit));
|
|
22
26
|
} else {
|
|
@@ -64,6 +68,13 @@ export function parseScript(scriptText, startLineNumber = 1) {
|
|
|
64
68
|
line = linePart;
|
|
65
69
|
}
|
|
66
70
|
|
|
71
|
+
// Base statement members
|
|
72
|
+
const lineNumber = ixLine + 1;
|
|
73
|
+
const statementBase = {lineNumber};
|
|
74
|
+
if (ixLine !== ixLinePart) {
|
|
75
|
+
statementBase.lineCount = (ixLinePart - ixLine) + 1;
|
|
76
|
+
}
|
|
77
|
+
|
|
67
78
|
// Assignment?
|
|
68
79
|
const matchAssignment = line.match(rScriptAssignment);
|
|
69
80
|
if (matchAssignment !== null) {
|
|
@@ -71,14 +82,15 @@ export function parseScript(scriptText, startLineNumber = 1) {
|
|
|
71
82
|
const exprStatement = {
|
|
72
83
|
'expr': {
|
|
73
84
|
'name': matchAssignment.groups.name,
|
|
74
|
-
'expr': parseExpression(matchAssignment.groups.expr)
|
|
85
|
+
'expr': parseExpression(matchAssignment.groups.expr, lineNumber, scriptName),
|
|
86
|
+
...statementBase
|
|
75
87
|
}
|
|
76
88
|
};
|
|
77
89
|
statements.push(exprStatement);
|
|
78
90
|
continue;
|
|
79
91
|
} catch (error) {
|
|
80
92
|
const columnNumber = line.length - matchAssignment.groups.expr.length + error.columnNumber;
|
|
81
|
-
throw new BareScriptParserError(error.error, line, columnNumber, startLineNumber + ixLine);
|
|
93
|
+
throw new BareScriptParserError(error.error, line, columnNumber, startLineNumber + ixLine, scriptName);
|
|
82
94
|
}
|
|
83
95
|
}
|
|
84
96
|
|
|
@@ -87,7 +99,7 @@ export function parseScript(scriptText, startLineNumber = 1) {
|
|
|
87
99
|
if (matchFunctionBegin !== null) {
|
|
88
100
|
// Nested function definitions are not allowed
|
|
89
101
|
if (functionDef !== null) {
|
|
90
|
-
throw new BareScriptParserError('Nested function definition', line, 1, startLineNumber + ixLine);
|
|
102
|
+
throw new BareScriptParserError('Nested function definition', line, 1, startLineNumber + ixLine, scriptName);
|
|
91
103
|
}
|
|
92
104
|
|
|
93
105
|
// Add the function definition statement
|
|
@@ -95,7 +107,8 @@ export function parseScript(scriptText, startLineNumber = 1) {
|
|
|
95
107
|
functionDef = {
|
|
96
108
|
'function': {
|
|
97
109
|
'name': matchFunctionBegin.groups.name,
|
|
98
|
-
'statements': []
|
|
110
|
+
'statements': [],
|
|
111
|
+
...statementBase
|
|
99
112
|
}
|
|
100
113
|
};
|
|
101
114
|
if (typeof matchFunctionBegin.groups.args !== 'undefined') {
|
|
@@ -115,7 +128,7 @@ export function parseScript(scriptText, startLineNumber = 1) {
|
|
|
115
128
|
const matchFunctionEnd = line.match(rScriptFunctionEnd);
|
|
116
129
|
if (matchFunctionEnd !== null) {
|
|
117
130
|
if (functionDef === null) {
|
|
118
|
-
throw new BareScriptParserError('No matching function definition', line, 1, startLineNumber + ixLine);
|
|
131
|
+
throw new BareScriptParserError('No matching function definition', line, 1, startLineNumber + ixLine, scriptName);
|
|
119
132
|
}
|
|
120
133
|
|
|
121
134
|
// Check for un-matched label definitions
|
|
@@ -123,7 +136,7 @@ export function parseScript(scriptText, startLineNumber = 1) {
|
|
|
123
136
|
const labelDef = labelDefs.pop();
|
|
124
137
|
const [defKey] = Object.keys(labelDef);
|
|
125
138
|
const def = labelDef[defKey];
|
|
126
|
-
throw new BareScriptParserError(`Missing end${defKey} statement`, def.line, 1, def.lineNumber);
|
|
139
|
+
throw new BareScriptParserError(`Missing end${defKey} statement`, def.line, 1, def.lineNumber, scriptName);
|
|
127
140
|
}
|
|
128
141
|
|
|
129
142
|
functionDef = null;
|
|
@@ -138,7 +151,8 @@ export function parseScript(scriptText, startLineNumber = 1) {
|
|
|
138
151
|
const ifthen = {
|
|
139
152
|
'jump': {
|
|
140
153
|
'label': `__bareScriptIf${labelIndex}`,
|
|
141
|
-
'expr': {'unary': {'op': '!', 'expr': parseExpression(matchIfBegin.groups.expr)}}
|
|
154
|
+
'expr': {'unary': {'op': '!', 'expr': parseExpression(matchIfBegin.groups.expr, lineNumber, scriptName)}},
|
|
155
|
+
...statementBase
|
|
142
156
|
},
|
|
143
157
|
'done': `__bareScriptDone${labelIndex}`,
|
|
144
158
|
'hasElse': false,
|
|
@@ -160,26 +174,27 @@ export function parseScript(scriptText, startLineNumber = 1) {
|
|
|
160
174
|
const labelDefDepth = (functionDef !== null ? functionLabelDefDepth : 0);
|
|
161
175
|
const ifthen = (labelDefs.length > labelDefDepth ? (labelDefs[labelDefs.length - 1].if ?? null) : null);
|
|
162
176
|
if (ifthen === null) {
|
|
163
|
-
throw new BareScriptParserError('No matching if statement', line, 1, startLineNumber + ixLine);
|
|
177
|
+
throw new BareScriptParserError('No matching if statement', line, 1, startLineNumber + ixLine, scriptName);
|
|
164
178
|
}
|
|
165
179
|
|
|
166
180
|
// Cannot come after the else-then statement
|
|
167
181
|
if (ifthen.hasElse) {
|
|
168
|
-
throw new BareScriptParserError('Elif statement following else statement', line, 1, startLineNumber + ixLine);
|
|
182
|
+
throw new BareScriptParserError('Elif statement following else statement', line, 1, startLineNumber + ixLine, scriptName);
|
|
169
183
|
}
|
|
170
184
|
|
|
171
185
|
// Generate the next if-then jump statement
|
|
172
186
|
const prevLabel = ifthen.jump.label;
|
|
173
187
|
ifthen.jump = {
|
|
174
188
|
'label': `__bareScriptIf${labelIndex}`,
|
|
175
|
-
'expr': {'unary': {'op': '!', 'expr': parseExpression(matchIfElseIf.groups.expr)}}
|
|
189
|
+
'expr': {'unary': {'op': '!', 'expr': parseExpression(matchIfElseIf.groups.expr, lineNumber, scriptName)}},
|
|
190
|
+
...statementBase
|
|
176
191
|
};
|
|
177
192
|
labelIndex += 1;
|
|
178
193
|
|
|
179
194
|
// Add the if-then else statements
|
|
180
195
|
statements.push(
|
|
181
|
-
{'jump': {'label': ifthen.done}},
|
|
182
|
-
{'label': prevLabel},
|
|
196
|
+
{'jump': {'label': ifthen.done, ...statementBase}},
|
|
197
|
+
{'label': {'name': prevLabel, ...statementBase}},
|
|
183
198
|
{'jump': ifthen.jump}
|
|
184
199
|
);
|
|
185
200
|
continue;
|
|
@@ -192,19 +207,19 @@ export function parseScript(scriptText, startLineNumber = 1) {
|
|
|
192
207
|
const labelDefDepth = (functionDef !== null ? functionLabelDefDepth : 0);
|
|
193
208
|
const ifthen = (labelDefs.length > labelDefDepth ? (labelDefs[labelDefs.length - 1].if ?? null) : null);
|
|
194
209
|
if (ifthen === null) {
|
|
195
|
-
throw new BareScriptParserError('No matching if statement', line, 1, startLineNumber + ixLine);
|
|
210
|
+
throw new BareScriptParserError('No matching if statement', line, 1, startLineNumber + ixLine, scriptName);
|
|
196
211
|
}
|
|
197
212
|
|
|
198
213
|
// Cannot have multiple else-then statements
|
|
199
214
|
if (ifthen.hasElse) {
|
|
200
|
-
throw new BareScriptParserError('Multiple else statements', line, 1, startLineNumber + ixLine);
|
|
215
|
+
throw new BareScriptParserError('Multiple else statements', line, 1, startLineNumber + ixLine, scriptName);
|
|
201
216
|
}
|
|
202
217
|
ifthen.hasElse = true;
|
|
203
218
|
|
|
204
219
|
// Add the if-then else statements
|
|
205
220
|
statements.push(
|
|
206
|
-
{'jump': {'label': ifthen.done}},
|
|
207
|
-
{'label': ifthen.jump.label}
|
|
221
|
+
{'jump': {'label': ifthen.done, ...statementBase}},
|
|
222
|
+
{'label': {'name': ifthen.jump.label, ...statementBase}}
|
|
208
223
|
);
|
|
209
224
|
continue;
|
|
210
225
|
}
|
|
@@ -216,7 +231,7 @@ export function parseScript(scriptText, startLineNumber = 1) {
|
|
|
216
231
|
const labelDefDepth = (functionDef !== null ? functionLabelDefDepth : 0);
|
|
217
232
|
const ifthen = (labelDefs.length > labelDefDepth ? (labelDefs.pop().if ?? null) : null);
|
|
218
233
|
if (ifthen === null) {
|
|
219
|
-
throw new BareScriptParserError('No matching if statement', line, 1, startLineNumber + ixLine);
|
|
234
|
+
throw new BareScriptParserError('No matching if statement', line, 1, startLineNumber + ixLine, scriptName);
|
|
220
235
|
}
|
|
221
236
|
|
|
222
237
|
// Update the previous jump statement's label, if necessary
|
|
@@ -225,7 +240,7 @@ export function parseScript(scriptText, startLineNumber = 1) {
|
|
|
225
240
|
}
|
|
226
241
|
|
|
227
242
|
// Add the if-then footer statement
|
|
228
|
-
statements.push({'label': ifthen.done});
|
|
243
|
+
statements.push({'label': {'name': ifthen.done, ...statementBase}});
|
|
229
244
|
continue;
|
|
230
245
|
}
|
|
231
246
|
|
|
@@ -237,7 +252,7 @@ export function parseScript(scriptText, startLineNumber = 1) {
|
|
|
237
252
|
'loop': `__bareScriptLoop${labelIndex}`,
|
|
238
253
|
'continue': `__bareScriptLoop${labelIndex}`,
|
|
239
254
|
'done': `__bareScriptDone${labelIndex}`,
|
|
240
|
-
'expr': parseExpression(matchWhileBegin.groups.expr),
|
|
255
|
+
'expr': parseExpression(matchWhileBegin.groups.expr, lineNumber, scriptName),
|
|
241
256
|
line,
|
|
242
257
|
'lineNumber': startLineNumber + ixLine
|
|
243
258
|
};
|
|
@@ -246,8 +261,8 @@ export function parseScript(scriptText, startLineNumber = 1) {
|
|
|
246
261
|
|
|
247
262
|
// Add the while-do header statements
|
|
248
263
|
statements.push(
|
|
249
|
-
{'jump': {'label': whiledo.done, 'expr': {'unary': {'op': '!', 'expr': whiledo.expr}}}},
|
|
250
|
-
{'label': whiledo.loop}
|
|
264
|
+
{'jump': {'label': whiledo.done, 'expr': {'unary': {'op': '!', 'expr': whiledo.expr}}, ...statementBase}},
|
|
265
|
+
{'label': {'name': whiledo.loop, ...statementBase}}
|
|
251
266
|
);
|
|
252
267
|
continue;
|
|
253
268
|
}
|
|
@@ -259,13 +274,13 @@ export function parseScript(scriptText, startLineNumber = 1) {
|
|
|
259
274
|
const labelDefDepth = (functionDef !== null ? functionLabelDefDepth : 0);
|
|
260
275
|
const whiledo = (labelDefs.length > labelDefDepth ? (labelDefs.pop().while ?? null) : null);
|
|
261
276
|
if (whiledo === null) {
|
|
262
|
-
throw new BareScriptParserError('No matching while statement', line, 1, startLineNumber + ixLine);
|
|
277
|
+
throw new BareScriptParserError('No matching while statement', line, 1, startLineNumber + ixLine, scriptName);
|
|
263
278
|
}
|
|
264
279
|
|
|
265
280
|
// Add the while-do footer statements
|
|
266
281
|
statements.push(
|
|
267
|
-
{'jump': {'label': whiledo.loop, 'expr': whiledo.expr}},
|
|
268
|
-
{'label': whiledo.done}
|
|
282
|
+
{'jump': {'label': whiledo.loop, 'expr': whiledo.expr, ...statementBase}},
|
|
283
|
+
{'label': {'name': whiledo.done, ...statementBase}}
|
|
269
284
|
);
|
|
270
285
|
continue;
|
|
271
286
|
}
|
|
@@ -290,17 +305,23 @@ export function parseScript(scriptText, startLineNumber = 1) {
|
|
|
290
305
|
|
|
291
306
|
// Add the for-each header statements
|
|
292
307
|
statements.push(
|
|
293
|
-
{'expr': {
|
|
308
|
+
{'expr': {
|
|
309
|
+
'name': foreach.values,
|
|
310
|
+
'expr': parseExpression(matchForBegin.groups.values, lineNumber, scriptName),
|
|
311
|
+
...statementBase
|
|
312
|
+
}},
|
|
294
313
|
{'expr': {
|
|
295
314
|
'name': foreach.length,
|
|
296
|
-
'expr': {'function': {'name': 'arrayLength', 'args': [{'variable': foreach.values}]}}
|
|
315
|
+
'expr': {'function': {'name': 'arrayLength', 'args': [{'variable': foreach.values}]}},
|
|
316
|
+
...statementBase
|
|
297
317
|
}},
|
|
298
|
-
{'jump': {'label': foreach.done, 'expr': {'unary': {'op': '!', 'expr': {'variable': foreach.length}}}}},
|
|
299
|
-
{'expr': {'name': foreach.index, 'expr': {'number': 0}}},
|
|
300
|
-
{'label': foreach.loop},
|
|
318
|
+
{'jump': {'label': foreach.done, 'expr': {'unary': {'op': '!', 'expr': {'variable': foreach.length}}}, ...statementBase}},
|
|
319
|
+
{'expr': {'name': foreach.index, 'expr': {'number': 0}, ...statementBase}},
|
|
320
|
+
{'label': {'name': foreach.loop, ...statementBase}},
|
|
301
321
|
{'expr': {
|
|
302
322
|
'name': foreach.value,
|
|
303
|
-
'expr': {'function': {'name': 'arrayGet', 'args': [{'variable': foreach.values}, {'variable': foreach.index}]}}
|
|
323
|
+
'expr': {'function': {'name': 'arrayGet', 'args': [{'variable': foreach.values}, {'variable': foreach.index}]}},
|
|
324
|
+
...statementBase
|
|
304
325
|
}}
|
|
305
326
|
);
|
|
306
327
|
continue;
|
|
@@ -313,23 +334,25 @@ export function parseScript(scriptText, startLineNumber = 1) {
|
|
|
313
334
|
const labelDefDepth = (functionDef !== null ? functionLabelDefDepth : 0);
|
|
314
335
|
const foreach = (labelDefs.length > labelDefDepth ? (labelDefs.pop().for ?? null) : null);
|
|
315
336
|
if (foreach === null) {
|
|
316
|
-
throw new BareScriptParserError('No matching for statement', line, 1, startLineNumber + ixLine);
|
|
337
|
+
throw new BareScriptParserError('No matching for statement', line, 1, startLineNumber + ixLine, scriptName);
|
|
317
338
|
}
|
|
318
339
|
|
|
319
340
|
// Add the for-each footer statements
|
|
320
341
|
if (foreach.hasContinue) {
|
|
321
|
-
statements.push({'label': foreach.continue});
|
|
342
|
+
statements.push({'label': {'name': foreach.continue, ...statementBase}});
|
|
322
343
|
}
|
|
323
344
|
statements.push(
|
|
324
345
|
{'expr': {
|
|
325
346
|
'name': foreach.index,
|
|
326
|
-
'expr': {'binary': {'op': '+', 'left': {'variable': foreach.index}, 'right': {'number': 1}}}
|
|
347
|
+
'expr': {'binary': {'op': '+', 'left': {'variable': foreach.index}, 'right': {'number': 1}}},
|
|
348
|
+
...statementBase
|
|
327
349
|
}},
|
|
328
350
|
{'jump': {
|
|
329
351
|
'label': foreach.loop,
|
|
330
|
-
'expr': {'binary': {'op': '<', 'left': {'variable': foreach.index}, 'right': {'variable': foreach.length}}}
|
|
352
|
+
'expr': {'binary': {'op': '<', 'left': {'variable': foreach.index}, 'right': {'variable': foreach.length}}},
|
|
353
|
+
...statementBase
|
|
331
354
|
}},
|
|
332
|
-
{'label': foreach.done}
|
|
355
|
+
{'label': {'name': foreach.done, ...statementBase}}
|
|
333
356
|
);
|
|
334
357
|
continue;
|
|
335
358
|
}
|
|
@@ -342,13 +365,13 @@ export function parseScript(scriptText, startLineNumber = 1) {
|
|
|
342
365
|
const ixLabelDef = labelDefs.findLastIndex((def) => !('if' in def));
|
|
343
366
|
const labelDef = (ixLabelDef >= labelDefDepth ? labelDefs[ixLabelDef] : null);
|
|
344
367
|
if (labelDef === null) {
|
|
345
|
-
throw new BareScriptParserError('Break statement outside of loop', line, 1, startLineNumber + ixLine);
|
|
368
|
+
throw new BareScriptParserError('Break statement outside of loop', line, 1, startLineNumber + ixLine, scriptName);
|
|
346
369
|
}
|
|
347
370
|
const [labelKey] = Object.keys(labelDef);
|
|
348
371
|
const loopDef = labelDef[labelKey];
|
|
349
372
|
|
|
350
373
|
// Add the break jump statement
|
|
351
|
-
statements.push({'jump': {'label': loopDef.done}});
|
|
374
|
+
statements.push({'jump': {'label': loopDef.done, ...statementBase}});
|
|
352
375
|
continue;
|
|
353
376
|
}
|
|
354
377
|
|
|
@@ -360,34 +383,34 @@ export function parseScript(scriptText, startLineNumber = 1) {
|
|
|
360
383
|
const ixLabelDef = labelDefs.findLastIndex((def) => !('if' in def));
|
|
361
384
|
const labelDef = (ixLabelDef >= labelDefDepth ? labelDefs[ixLabelDef] : null);
|
|
362
385
|
if (labelDef === null) {
|
|
363
|
-
throw new BareScriptParserError('Continue statement outside of loop', line, 1, startLineNumber + ixLine);
|
|
386
|
+
throw new BareScriptParserError('Continue statement outside of loop', line, 1, startLineNumber + ixLine, scriptName);
|
|
364
387
|
}
|
|
365
388
|
const [labelKey] = Object.keys(labelDef);
|
|
366
389
|
const loopDef = labelDef[labelKey];
|
|
367
390
|
|
|
368
391
|
// Add the continue jump statement
|
|
369
392
|
loopDef.hasContinue = true;
|
|
370
|
-
statements.push({'jump': {'label': loopDef.continue}});
|
|
393
|
+
statements.push({'jump': {'label': loopDef.continue, ...statementBase}});
|
|
371
394
|
continue;
|
|
372
395
|
}
|
|
373
396
|
|
|
374
397
|
// Label definition?
|
|
375
398
|
const matchLabel = line.match(rScriptLabel);
|
|
376
399
|
if (matchLabel !== null) {
|
|
377
|
-
statements.push({'label': matchLabel.groups.name});
|
|
400
|
+
statements.push({'label': {'name': matchLabel.groups.name, ...statementBase}});
|
|
378
401
|
continue;
|
|
379
402
|
}
|
|
380
403
|
|
|
381
404
|
// Jump definition?
|
|
382
405
|
const matchJump = line.match(rScriptJump);
|
|
383
406
|
if (matchJump !== null) {
|
|
384
|
-
const jumpStatement = {'jump': {'label': matchJump.groups.name}};
|
|
407
|
+
const jumpStatement = {'jump': {'label': matchJump.groups.name, ...statementBase}};
|
|
385
408
|
if (typeof matchJump.groups.expr !== 'undefined') {
|
|
386
409
|
try {
|
|
387
|
-
jumpStatement.jump.expr = parseExpression(matchJump.groups.expr);
|
|
410
|
+
jumpStatement.jump.expr = parseExpression(matchJump.groups.expr, lineNumber, scriptName);
|
|
388
411
|
} catch (error) {
|
|
389
412
|
const columnNumber = matchJump.groups.jump.length - matchJump.groups.expr.length - 1 + error.columnNumber;
|
|
390
|
-
throw new BareScriptParserError(error.error, line, columnNumber, startLineNumber + ixLine);
|
|
413
|
+
throw new BareScriptParserError(error.error, line, columnNumber, startLineNumber + ixLine, scriptName);
|
|
391
414
|
}
|
|
392
415
|
}
|
|
393
416
|
statements.push(jumpStatement);
|
|
@@ -397,13 +420,13 @@ export function parseScript(scriptText, startLineNumber = 1) {
|
|
|
397
420
|
// Return definition?
|
|
398
421
|
const matchReturn = line.match(rScriptReturn);
|
|
399
422
|
if (matchReturn !== null) {
|
|
400
|
-
const returnStatement = {'return': {}};
|
|
423
|
+
const returnStatement = {'return': {...statementBase}};
|
|
401
424
|
if (typeof matchReturn.groups.expr !== 'undefined') {
|
|
402
425
|
try {
|
|
403
|
-
returnStatement.return.expr = parseExpression(matchReturn.groups.expr);
|
|
426
|
+
returnStatement.return.expr = parseExpression(matchReturn.groups.expr, lineNumber, scriptName);
|
|
404
427
|
} catch (error) {
|
|
405
428
|
const columnNumber = matchReturn.groups.return.length - matchReturn.groups.expr.length + error.columnNumber;
|
|
406
|
-
throw new BareScriptParserError(error.error, line, columnNumber, startLineNumber + ixLine);
|
|
429
|
+
throw new BareScriptParserError(error.error, line, columnNumber, startLineNumber + ixLine, scriptName);
|
|
407
430
|
}
|
|
408
431
|
}
|
|
409
432
|
statements.push(returnStatement);
|
|
@@ -417,8 +440,10 @@ export function parseScript(scriptText, startLineNumber = 1) {
|
|
|
417
440
|
const url = (delim === '<' ? matchInclude.groups.url : matchInclude.groups.url.replace(rExprStringEscape, '$1'));
|
|
418
441
|
let includeStatement = (statements.length ? statements[statements.length - 1] : null);
|
|
419
442
|
if (includeStatement === null || !('include' in includeStatement)) {
|
|
420
|
-
includeStatement = {'include': {'includes': []}};
|
|
443
|
+
includeStatement = {'include': {'includes': [], ...statementBase}};
|
|
421
444
|
statements.push(includeStatement);
|
|
445
|
+
} else {
|
|
446
|
+
includeStatement.include.lineCount = (ixLinePart - includeStatement.include.lineNumber) + 2;
|
|
422
447
|
}
|
|
423
448
|
includeStatement.include.includes.push(delim === '<' ? {url, 'system': true} : {url});
|
|
424
449
|
continue;
|
|
@@ -426,10 +451,10 @@ export function parseScript(scriptText, startLineNumber = 1) {
|
|
|
426
451
|
|
|
427
452
|
// Expression
|
|
428
453
|
try {
|
|
429
|
-
const exprStatement = {'expr': {'expr': parseExpression(line)}};
|
|
454
|
+
const exprStatement = {'expr': {'expr': parseExpression(line, lineNumber, scriptName), ...statementBase}};
|
|
430
455
|
statements.push(exprStatement);
|
|
431
456
|
} catch (error) {
|
|
432
|
-
throw new BareScriptParserError(error.error, line, error.columnNumber, startLineNumber + ixLine);
|
|
457
|
+
throw new BareScriptParserError(error.error, line, error.columnNumber, startLineNumber + ixLine, scriptName);
|
|
433
458
|
}
|
|
434
459
|
}
|
|
435
460
|
|
|
@@ -438,7 +463,7 @@ export function parseScript(scriptText, startLineNumber = 1) {
|
|
|
438
463
|
const labelDef = labelDefs.pop();
|
|
439
464
|
const [defKey] = Object.keys(labelDef);
|
|
440
465
|
const def = labelDef[defKey];
|
|
441
|
-
throw new BareScriptParserError(`Missing end${defKey} statement`, def.line, 1, def.lineNumber);
|
|
466
|
+
throw new BareScriptParserError(`Missing end${defKey} statement`, def.line, 1, def.lineNumber, scriptName);
|
|
442
467
|
}
|
|
443
468
|
|
|
444
469
|
return script;
|
|
@@ -483,22 +508,22 @@ const rScriptContinue = new RegExp(`^\\s*continue${rPartComment}`);
|
|
|
483
508
|
* @returns {Object} The [expression model](./model/#var.vName='Expression')
|
|
484
509
|
* @throws [BareScriptParserError]{@link module:lib/parser.BareScriptParserError}
|
|
485
510
|
*/
|
|
486
|
-
export function parseExpression(exprText) {
|
|
511
|
+
export function parseExpression(exprText, lineNumber = null, scriptName = null) {
|
|
487
512
|
try {
|
|
488
|
-
const [expr, nextText] = parseBinaryExpression(exprText);
|
|
513
|
+
const [expr, nextText] = parseBinaryExpression(exprText, null);
|
|
489
514
|
if (nextText.trim() !== '') {
|
|
490
|
-
throw new BareScriptParserError('Syntax error', nextText);
|
|
515
|
+
throw new BareScriptParserError('Syntax error', nextText, 1, lineNumber, scriptName);
|
|
491
516
|
}
|
|
492
517
|
return expr;
|
|
493
518
|
} catch (error) {
|
|
494
519
|
const columnNumber = exprText.length - error.line.length + 1;
|
|
495
|
-
throw new BareScriptParserError(error.error, exprText, columnNumber);
|
|
520
|
+
throw new BareScriptParserError(error.error, exprText, columnNumber, lineNumber, scriptName);
|
|
496
521
|
}
|
|
497
522
|
}
|
|
498
523
|
|
|
499
524
|
|
|
500
525
|
// Helper function to parse a binary operator expression chain
|
|
501
|
-
function parseBinaryExpression(exprText, binLeftExpr
|
|
526
|
+
function parseBinaryExpression(exprText, binLeftExpr) {
|
|
502
527
|
// Parse the binary operator's left unary expression if none was passed
|
|
503
528
|
let leftExpr;
|
|
504
529
|
let binText;
|
|
@@ -575,10 +600,10 @@ function parseUnaryExpression(exprText) {
|
|
|
575
600
|
const matchGroupOpen = exprText.match(rExprGroupOpen);
|
|
576
601
|
if (matchGroupOpen !== null) {
|
|
577
602
|
const groupText = exprText.slice(matchGroupOpen[0].length);
|
|
578
|
-
const [expr, nextText] = parseBinaryExpression(groupText);
|
|
603
|
+
const [expr, nextText] = parseBinaryExpression(groupText, null);
|
|
579
604
|
const matchGroupClose = nextText.match(rExprGroupClose);
|
|
580
605
|
if (matchGroupClose === null) {
|
|
581
|
-
throw new BareScriptParserError('Unmatched parenthesis', exprText);
|
|
606
|
+
throw new BareScriptParserError('Unmatched parenthesis', exprText, 1, null, null);
|
|
582
607
|
}
|
|
583
608
|
return [{'group': expr}, nextText.slice(matchGroupClose[0].length)];
|
|
584
609
|
}
|
|
@@ -639,13 +664,13 @@ function parseUnaryExpression(exprText) {
|
|
|
639
664
|
if (args.length !== 0) {
|
|
640
665
|
const matchFunctionSeparator = argText.match(rExprFunctionSeparator);
|
|
641
666
|
if (matchFunctionSeparator === null) {
|
|
642
|
-
throw new BareScriptParserError('Syntax error', argText);
|
|
667
|
+
throw new BareScriptParserError('Syntax error', argText, 1, null, null);
|
|
643
668
|
}
|
|
644
669
|
argText = argText.slice(matchFunctionSeparator[0].length);
|
|
645
670
|
}
|
|
646
671
|
|
|
647
672
|
// Get the argument
|
|
648
|
-
const [argExpr, nextArgText] = parseBinaryExpression(argText);
|
|
673
|
+
const [argExpr, nextArgText] = parseBinaryExpression(argText, null);
|
|
649
674
|
args.push(argExpr);
|
|
650
675
|
argText = nextArgText;
|
|
651
676
|
}
|
|
@@ -674,7 +699,7 @@ function parseUnaryExpression(exprText) {
|
|
|
674
699
|
return [expr, exprText.slice(matchVariableEx[0].length)];
|
|
675
700
|
}
|
|
676
701
|
|
|
677
|
-
throw new BareScriptParserError('Syntax error', exprText);
|
|
702
|
+
throw new BareScriptParserError('Syntax error', exprText, 1, null, null);
|
|
678
703
|
}
|
|
679
704
|
|
|
680
705
|
|
|
@@ -705,6 +730,7 @@ const rExprVariableExEscape = /\\([\\\]])/g;
|
|
|
705
730
|
* @property {string} line - The line text
|
|
706
731
|
* @property {number} columnNumber - The error column number
|
|
707
732
|
* @property {?number} lineNumber - The error line number
|
|
733
|
+
* @property {?string} scriptName - The error line number
|
|
708
734
|
*/
|
|
709
735
|
export class BareScriptParserError extends Error {
|
|
710
736
|
/**
|
|
@@ -712,11 +738,11 @@ export class BareScriptParserError extends Error {
|
|
|
712
738
|
*
|
|
713
739
|
* @param {string} error - The error description
|
|
714
740
|
* @param {string} line - The line text
|
|
715
|
-
* @param {number} [columnNumber
|
|
716
|
-
* @param {?number} [lineNumber
|
|
717
|
-
* @param {?string} [
|
|
741
|
+
* @param {number} [columnNumber] - The error column number
|
|
742
|
+
* @param {?number} [lineNumber] - The error line number
|
|
743
|
+
* @param {?string} [scriptName] - The script name
|
|
718
744
|
*/
|
|
719
|
-
constructor(error, line, columnNumber
|
|
745
|
+
constructor(error, line, columnNumber, lineNumber, scriptName) {
|
|
720
746
|
// Parser error constants
|
|
721
747
|
const lineLengthMax = 120;
|
|
722
748
|
const lineSuffix = ' ...';
|
|
@@ -740,8 +766,9 @@ export class BareScriptParserError extends Error {
|
|
|
740
766
|
}
|
|
741
767
|
|
|
742
768
|
// Format the message
|
|
769
|
+
const errorPrefix = (lineNumber ? `${scriptName || ''}:${lineNumber}: ` : '');
|
|
743
770
|
const message = `\
|
|
744
|
-
${
|
|
771
|
+
${errorPrefix}${error}
|
|
745
772
|
${lineError}
|
|
746
773
|
${' '.repeat(lineColumn - 1)}^
|
|
747
774
|
`;
|
|
@@ -751,5 +778,6 @@ ${' '.repeat(lineColumn - 1)}^
|
|
|
751
778
|
this.line = line;
|
|
752
779
|
this.columnNumber = columnNumber;
|
|
753
780
|
this.lineNumber = lineNumber;
|
|
781
|
+
this.scriptName = scriptName;
|
|
754
782
|
}
|
|
755
783
|
}
|