eyeleng 1.0.8 → 1.0.10
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/dist/browser/eyeleng.browser.js +112 -78
- package/examples/gps.srl +289 -0
- package/examples/output/gps.trig +86 -0
- package/eyeleng.js +112 -78
- package/package.json +5 -2
- package/src/analyze.js +8 -10
- package/src/builtins.js +3 -1
- package/src/engine.js +36 -33
- package/src/parser.js +1 -1
- package/src/store.js +2 -2
- package/src/term.js +4 -2
- package/src/tokenizer.js +58 -29
- package/test/perf-baseline.json +39 -0
- package/tools/perf-bench.js +234 -0
|
@@ -906,7 +906,7 @@
|
|
|
906
906
|
peekN(n) { return this.tokens[this.pos + n] || this.tokens[this.tokens.length - 1]; }
|
|
907
907
|
previous() { return this.tokens[this.pos - 1]; }
|
|
908
908
|
strictGrammar() { return !!this.options.strictGrammar; }
|
|
909
|
-
error(message, token = this.peek()) { return new SyntaxErrorWithLocation(message, token); }
|
|
909
|
+
error(message, token = this.peek()) { return new SyntaxErrorWithLocation(message, token && token.filename ? token : { ...token, filename: this.options.filename || '<input>' }); }
|
|
910
910
|
}
|
|
911
911
|
|
|
912
912
|
|
|
@@ -1055,7 +1055,7 @@
|
|
|
1055
1055
|
|
|
1056
1056
|
function current() { return source[i]; }
|
|
1057
1057
|
function peek(n = 1) { return source[i + n]; }
|
|
1058
|
-
function startsWith(text) { return source.
|
|
1058
|
+
function startsWith(text) { return source.startsWith(text, i); }
|
|
1059
1059
|
function advance() {
|
|
1060
1060
|
const ch = source[i++];
|
|
1061
1061
|
if (ch === '\n') { line += 1; column = 1; }
|
|
@@ -1063,7 +1063,7 @@
|
|
|
1063
1063
|
return ch;
|
|
1064
1064
|
}
|
|
1065
1065
|
function token(type, value, startLine, startColumn, extra = {}) {
|
|
1066
|
-
tokens.push({ type, value, line: startLine, column: startColumn,
|
|
1066
|
+
tokens.push({ type, value, line: startLine, column: startColumn, ...extra });
|
|
1067
1067
|
}
|
|
1068
1068
|
function syntax(message, startLine, startColumn) {
|
|
1069
1069
|
throw new SyntaxErrorWithLocation(message, { line: startLine, column: startColumn, filename });
|
|
@@ -1071,19 +1071,21 @@
|
|
|
1071
1071
|
|
|
1072
1072
|
function readNumericLiteral() {
|
|
1073
1073
|
let value = '';
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1074
|
+
const start = i;
|
|
1075
|
+
while (i < source.length && isDigitCode(source.charCodeAt(i))) { i += 1; column += 1; }
|
|
1076
|
+
if (source[i] === '.' && isDigitCode(source.charCodeAt(i + 1))) {
|
|
1077
|
+
i += 1; column += 1;
|
|
1078
|
+
while (i < source.length && isDigitCode(source.charCodeAt(i))) { i += 1; column += 1; }
|
|
1078
1079
|
}
|
|
1080
|
+
value = source.slice(start, i);
|
|
1079
1081
|
if (current() === 'e' || current() === 'E') {
|
|
1080
1082
|
const saveI = i;
|
|
1081
1083
|
const saveLine = line;
|
|
1082
1084
|
const saveColumn = column;
|
|
1083
1085
|
let exponent = advance();
|
|
1084
1086
|
if (current() === '+' || current() === '-') exponent += advance();
|
|
1085
|
-
if (
|
|
1086
|
-
while (i < source.length &&
|
|
1087
|
+
if (isDigitCode(source.charCodeAt(i))) {
|
|
1088
|
+
while (i < source.length && isDigitCode(source.charCodeAt(i))) exponent += advance();
|
|
1087
1089
|
value += exponent;
|
|
1088
1090
|
} else {
|
|
1089
1091
|
i = saveI;
|
|
@@ -1101,7 +1103,7 @@
|
|
|
1101
1103
|
const length = esc === 'u' ? 4 : 8;
|
|
1102
1104
|
let hex = '';
|
|
1103
1105
|
for (let j = 0; j < length; j += 1) {
|
|
1104
|
-
if (
|
|
1106
|
+
if (!isHexCode(source.charCodeAt(i))) syntax(`Invalid \\${esc} escape`, startLine, startColumn);
|
|
1105
1107
|
hex += advance();
|
|
1106
1108
|
}
|
|
1107
1109
|
const codePoint = Number.parseInt(hex, 16);
|
|
@@ -1120,7 +1122,7 @@
|
|
|
1120
1122
|
const length = esc === 'u' ? 4 : 8;
|
|
1121
1123
|
let hex = '';
|
|
1122
1124
|
for (let j = 0; j < length; j += 1) {
|
|
1123
|
-
if (
|
|
1125
|
+
if (!isHexCode(source.charCodeAt(i))) syntax(`Invalid \\${esc} escape`, startLine, startColumn);
|
|
1124
1126
|
hex += advance();
|
|
1125
1127
|
}
|
|
1126
1128
|
const codePoint = Number.parseInt(hex, 16);
|
|
@@ -1134,7 +1136,7 @@
|
|
|
1134
1136
|
|
|
1135
1137
|
while (i < source.length) {
|
|
1136
1138
|
const ch = current();
|
|
1137
|
-
if (
|
|
1139
|
+
if (isWhitespaceCode(source.charCodeAt(i))) { advance(); continue; }
|
|
1138
1140
|
if (ch === '#') {
|
|
1139
1141
|
while (i < source.length && current() !== '\n') advance();
|
|
1140
1142
|
continue;
|
|
@@ -1220,18 +1222,21 @@
|
|
|
1220
1222
|
}
|
|
1221
1223
|
|
|
1222
1224
|
if (ch === '@') {
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
+
const wordStart = i;
|
|
1226
|
+
advance();
|
|
1227
|
+
while (i < source.length && isLangTagCode(source.charCodeAt(i))) { i += 1; column += 1; }
|
|
1228
|
+
const value = source.slice(wordStart, i);
|
|
1225
1229
|
if (!/^@[A-Za-z]+(?:-[A-Za-z0-9]+)*(?:--[A-Za-z]+)?$/.test(value)) syntax(`Invalid language tag ${value}`, startLine, startColumn);
|
|
1226
1230
|
token('word', value, startLine, startColumn);
|
|
1227
1231
|
continue;
|
|
1228
1232
|
}
|
|
1229
1233
|
|
|
1230
1234
|
if (ch === '?' || ch === '$') {
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
+
const varStart = i;
|
|
1236
|
+
advance();
|
|
1237
|
+
while (i < source.length && isVarNameCode(source.charCodeAt(i))) { i += 1; column += 1; }
|
|
1238
|
+
if (i - varStart === 1) syntax('Expected variable name', startLine, startColumn);
|
|
1239
|
+
token('variable', source.slice(varStart + 1, i), startLine, startColumn);
|
|
1235
1240
|
continue;
|
|
1236
1241
|
}
|
|
1237
1242
|
|
|
@@ -1258,24 +1263,27 @@
|
|
|
1258
1263
|
continue;
|
|
1259
1264
|
}
|
|
1260
1265
|
|
|
1261
|
-
|
|
1266
|
+
const wordStart = i;
|
|
1262
1267
|
while (i < source.length) {
|
|
1263
|
-
const c =
|
|
1264
|
-
if (c === '\\' &&
|
|
1265
|
-
|
|
1266
|
-
|
|
1268
|
+
const c = source[i];
|
|
1269
|
+
if (c === '\\' && source[i + 1] !== undefined) {
|
|
1270
|
+
i += 2;
|
|
1271
|
+
column += 2;
|
|
1267
1272
|
continue;
|
|
1268
1273
|
}
|
|
1269
|
-
|
|
1274
|
+
const code = source.charCodeAt(i);
|
|
1275
|
+
if (isWhitespaceCode(code) || '{}()[],;|'.includes(c) || '=<>+-*/!^~'.includes(c)) break;
|
|
1270
1276
|
if (c === '.') {
|
|
1271
|
-
const n =
|
|
1272
|
-
if (n === undefined ||
|
|
1277
|
+
const n = source[i + 1];
|
|
1278
|
+
if (n === undefined || isWhitespaceCode(n.charCodeAt(0)) || '{}()[],;|'.includes(n) || '=<>+-*/!^~'.includes(n)) break;
|
|
1273
1279
|
}
|
|
1274
1280
|
if (c === '#') break;
|
|
1275
|
-
|
|
1281
|
+
i += 1;
|
|
1282
|
+
column += 1;
|
|
1276
1283
|
}
|
|
1277
|
-
if (
|
|
1284
|
+
if (i === wordStart) syntax(`Unexpected character ${JSON.stringify(ch)}`, startLine, startColumn);
|
|
1278
1285
|
|
|
1286
|
+
const value = source.slice(wordStart, i);
|
|
1279
1287
|
if (/^[+-]?(?:(?:\d+\.\d*|\.\d+)(?:[eE][+-]?\d+)?|\d+[eE][+-]?\d+|\d+)$/.test(value)) token('number', Number(value), startLine, startColumn);
|
|
1280
1288
|
else token('word', value, startLine, startColumn);
|
|
1281
1289
|
}
|
|
@@ -1284,11 +1292,32 @@
|
|
|
1284
1292
|
return tokens;
|
|
1285
1293
|
}
|
|
1286
1294
|
|
|
1295
|
+
|
|
1296
|
+
function isDigitCode(code) {
|
|
1297
|
+
return code >= 48 && code <= 57;
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
function isHexCode(code) {
|
|
1301
|
+
return (code >= 48 && code <= 57) || (code >= 65 && code <= 70) || (code >= 97 && code <= 102);
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1304
|
+
function isWhitespaceCode(code) {
|
|
1305
|
+
return code === 32 || code === 9 || code === 10 || code === 13 || code === 12;
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1308
|
+
function isLangTagCode(code) {
|
|
1309
|
+
return (code >= 65 && code <= 90) || (code >= 97 && code <= 122) || (code >= 48 && code <= 57) || code === 45;
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
function isVarNameCode(code) {
|
|
1313
|
+
return (code >= 65 && code <= 90) || (code >= 97 && code <= 122) || (code >= 48 && code <= 57) || code === 95 || code === 45;
|
|
1314
|
+
}
|
|
1315
|
+
|
|
1287
1316
|
function startsNumericLiteral(source, i) {
|
|
1288
1317
|
const ch = source[i];
|
|
1289
1318
|
const next = source[i + 1];
|
|
1290
|
-
if (
|
|
1291
|
-
if (ch === '.' &&
|
|
1319
|
+
if (isDigitCode(ch.charCodeAt(0))) return true;
|
|
1320
|
+
if (ch === '.' && next !== undefined && isDigitCode(next.charCodeAt(0))) return true;
|
|
1292
1321
|
return false;
|
|
1293
1322
|
}
|
|
1294
1323
|
|
|
@@ -2918,11 +2947,13 @@
|
|
|
2918
2947
|
}
|
|
2919
2948
|
|
|
2920
2949
|
function variable(name) {
|
|
2921
|
-
|
|
2950
|
+
const value = String(name);
|
|
2951
|
+
return { type: 'var', value: value[0] === '?' || value[0] === '$' ? value.slice(1) : value };
|
|
2922
2952
|
}
|
|
2923
2953
|
|
|
2924
2954
|
function blankNode(value) {
|
|
2925
|
-
|
|
2955
|
+
const label = String(value);
|
|
2956
|
+
return { type: 'blank', value: label.startsWith('_:') ? label.slice(2) : label };
|
|
2926
2957
|
}
|
|
2927
2958
|
|
|
2928
2959
|
function literal(value, datatype = null, lang = null, langDir = null) {
|
|
@@ -3177,6 +3208,8 @@
|
|
|
3177
3208
|
const RDF_LANGSTRING = `${RDF_NS}langString`;
|
|
3178
3209
|
const RDF_DIRLANGSTRING = `${RDF_NS}dirLangString`;
|
|
3179
3210
|
const NUMERIC_DATATYPES = new Set([XSD_INTEGER, XSD_DECIMAL, XSD_DOUBLE]);
|
|
3211
|
+
const MAX_SAFE_INTEGER_BIGINT = BigInt(Number.MAX_SAFE_INTEGER);
|
|
3212
|
+
const MIN_SAFE_INTEGER_BIGINT = BigInt(Number.MIN_SAFE_INTEGER);
|
|
3180
3213
|
|
|
3181
3214
|
// This table is intentionally shaped by the SHACL 1.2 Rules grammar production BuiltInCall.
|
|
3182
3215
|
// Keys are the canonical spellings used by the draft; lookup is case-insensitive so examples
|
|
@@ -3336,7 +3369,7 @@
|
|
|
3336
3369
|
}
|
|
3337
3370
|
|
|
3338
3371
|
function fromIntegerResult(value) {
|
|
3339
|
-
if (value <=
|
|
3372
|
+
if (value <= MAX_SAFE_INTEGER_BIGINT && value >= MIN_SAFE_INTEGER_BIGINT) return Number(value);
|
|
3340
3373
|
return value;
|
|
3341
3374
|
}
|
|
3342
3375
|
|
|
@@ -4047,6 +4080,18 @@
|
|
|
4047
4080
|
layerIndexes,
|
|
4048
4081
|
analysis.dependency ? analysis.dependency.edges : [],
|
|
4049
4082
|
);
|
|
4083
|
+
const baseContext = {
|
|
4084
|
+
...evalOptions,
|
|
4085
|
+
maxIterations,
|
|
4086
|
+
inputKeys,
|
|
4087
|
+
inferred,
|
|
4088
|
+
trace,
|
|
4089
|
+
perRule,
|
|
4090
|
+
layer: 0,
|
|
4091
|
+
iteration: 0,
|
|
4092
|
+
startingIterations: 0,
|
|
4093
|
+
recursiveLayer: false,
|
|
4094
|
+
};
|
|
4050
4095
|
|
|
4051
4096
|
for (let layerIndex = 0; layerIndex < layerIndexes.length; layerIndex += 1) {
|
|
4052
4097
|
const layer = layerIndexes[layerIndex];
|
|
@@ -4056,30 +4101,17 @@
|
|
|
4056
4101
|
if (runOnce.length > 0) {
|
|
4057
4102
|
iterations += 1;
|
|
4058
4103
|
for (const ruleIndex of runOnce) {
|
|
4059
|
-
|
|
4060
|
-
|
|
4061
|
-
|
|
4062
|
-
inferred,
|
|
4063
|
-
trace,
|
|
4064
|
-
perRule,
|
|
4065
|
-
layer: layerIndex + 1,
|
|
4066
|
-
iteration: iterations,
|
|
4067
|
-
});
|
|
4104
|
+
baseContext.layer = layerIndex + 1;
|
|
4105
|
+
baseContext.iteration = iterations;
|
|
4106
|
+
const added = applyRuleOnce(program, store, ruleIndex, baseContext);
|
|
4068
4107
|
ruleApplications += added.applications;
|
|
4069
4108
|
}
|
|
4070
4109
|
}
|
|
4071
4110
|
|
|
4072
|
-
|
|
4073
|
-
|
|
4074
|
-
|
|
4075
|
-
|
|
4076
|
-
inferred,
|
|
4077
|
-
trace,
|
|
4078
|
-
perRule,
|
|
4079
|
-
layer: layerIndex + 1,
|
|
4080
|
-
startingIterations: iterations,
|
|
4081
|
-
recursiveLayer: recursiveLayerFlags[layerIndex],
|
|
4082
|
-
});
|
|
4111
|
+
baseContext.layer = layerIndex + 1;
|
|
4112
|
+
baseContext.startingIterations = iterations;
|
|
4113
|
+
baseContext.recursiveLayer = recursiveLayerFlags[layerIndex];
|
|
4114
|
+
const ordinaryResult = runRulesToFixpoint(program, store, ordinary, baseContext);
|
|
4083
4115
|
iterations = ordinaryResult.iterations;
|
|
4084
4116
|
ruleApplications += ordinaryResult.ruleApplications;
|
|
4085
4117
|
}
|
|
@@ -4110,10 +4142,8 @@
|
|
|
4110
4142
|
const iteration = context.startingIterations + 1;
|
|
4111
4143
|
let ruleApplications = 0;
|
|
4112
4144
|
for (const ruleIndex of ruleIndexes) {
|
|
4113
|
-
|
|
4114
|
-
|
|
4115
|
-
iteration,
|
|
4116
|
-
});
|
|
4145
|
+
context.iteration = iteration;
|
|
4146
|
+
const applied = applyRuleOnce(program, store, ruleIndex, context);
|
|
4117
4147
|
ruleApplications += applied.applications;
|
|
4118
4148
|
}
|
|
4119
4149
|
return { iterations: iteration, ruleApplications };
|
|
@@ -4129,10 +4159,8 @@
|
|
|
4129
4159
|
let addedInIteration = 0;
|
|
4130
4160
|
|
|
4131
4161
|
for (const ruleIndex of ruleIndexes) {
|
|
4132
|
-
|
|
4133
|
-
|
|
4134
|
-
iteration: iterations,
|
|
4135
|
-
});
|
|
4162
|
+
context.iteration = iterations;
|
|
4163
|
+
const applied = applyRuleOnce(program, store, ruleIndex, context);
|
|
4136
4164
|
addedInIteration += applied.added;
|
|
4137
4165
|
ruleApplications += applied.applications;
|
|
4138
4166
|
}
|
|
@@ -4161,16 +4189,24 @@
|
|
|
4161
4189
|
return flags;
|
|
4162
4190
|
}
|
|
4163
4191
|
|
|
4192
|
+
|
|
4164
4193
|
function applyRuleOnce(program, store, ruleIndex, context) {
|
|
4165
4194
|
const rule = program.rules[ruleIndex];
|
|
4166
4195
|
let applications = 0;
|
|
4167
4196
|
let added = 0;
|
|
4168
|
-
const
|
|
4197
|
+
const dedupeBindings = rule.body.some((clause) => clause.type === 'path');
|
|
4198
|
+
const seenBindings = dedupeBindings ? new Set() : null;
|
|
4169
4199
|
|
|
4170
|
-
|
|
4171
|
-
|
|
4172
|
-
|
|
4173
|
-
|
|
4200
|
+
const bodyBindings = rule.body.length === 1 && rule.body[0].type === 'triple'
|
|
4201
|
+
? store.match(rule.body[0].triple, {})
|
|
4202
|
+
: evaluateBodyStream(rule.body, store, {}, context);
|
|
4203
|
+
|
|
4204
|
+
for (const binding of bodyBindings) {
|
|
4205
|
+
if (seenBindings) {
|
|
4206
|
+
const key = bindingKey(binding);
|
|
4207
|
+
if (seenBindings.has(key)) continue;
|
|
4208
|
+
seenBindings.add(key);
|
|
4209
|
+
}
|
|
4174
4210
|
applications += 1;
|
|
4175
4211
|
context.perRule[ruleIndex].applications += 1;
|
|
4176
4212
|
|
|
@@ -4289,7 +4325,7 @@
|
|
|
4289
4325
|
"src/store.js": function (require, module, exports) {
|
|
4290
4326
|
'use strict';
|
|
4291
4327
|
|
|
4292
|
-
const { tripleKey, termKey, termEquals
|
|
4328
|
+
const { tripleKey, termKey, termEquals } = require('./term.js');
|
|
4293
4329
|
|
|
4294
4330
|
class TripleStore {
|
|
4295
4331
|
constructor(triples = []) {
|
|
@@ -4398,7 +4434,7 @@
|
|
|
4398
4434
|
}
|
|
4399
4435
|
|
|
4400
4436
|
function normalizeTriple(triple) {
|
|
4401
|
-
return { s:
|
|
4437
|
+
return { s: triple.s, p: triple.p, o: triple.o };
|
|
4402
4438
|
}
|
|
4403
4439
|
|
|
4404
4440
|
function bindingKey(binding) {
|
|
@@ -4558,9 +4594,7 @@
|
|
|
4558
4594
|
|
|
4559
4595
|
program.rules.forEach((rule, index) => {
|
|
4560
4596
|
const name = ruleName(rule, index);
|
|
4561
|
-
const label = displayRuleName(name, program.prefixes || {});
|
|
4562
4597
|
const bound = boundVariables(rule.body);
|
|
4563
|
-
const positive = positiveVariables(rule.body);
|
|
4564
4598
|
const head = new Set();
|
|
4565
4599
|
for (const triple of rule.head) collectTripleVars(triple, head);
|
|
4566
4600
|
|
|
@@ -4570,7 +4604,7 @@
|
|
|
4570
4604
|
code: 'unsafe-head-variable',
|
|
4571
4605
|
severity: 'error',
|
|
4572
4606
|
rule: name,
|
|
4573
|
-
message: `${
|
|
4607
|
+
message: `${displayRuleName(name, program.prefixes || {})} has unbound head variable ?${variable}`,
|
|
4574
4608
|
});
|
|
4575
4609
|
}
|
|
4576
4610
|
}
|
|
@@ -4581,19 +4615,19 @@
|
|
|
4581
4615
|
code: 'invalid-head-predicate',
|
|
4582
4616
|
severity: 'error',
|
|
4583
4617
|
rule: name,
|
|
4584
|
-
message: `${
|
|
4618
|
+
message: `${displayRuleName(name, program.prefixes || {})} has a non-IRI/non-variable predicate in the head`,
|
|
4585
4619
|
});
|
|
4586
4620
|
}
|
|
4587
4621
|
}
|
|
4588
4622
|
|
|
4589
|
-
diagnostics.push(...sequentialWellFormednessDiagnostics(rule.body, name,
|
|
4623
|
+
diagnostics.push(...sequentialWellFormednessDiagnostics(rule.body, name, program.prefixes || {}));
|
|
4590
4624
|
|
|
4591
4625
|
if (rule.runOnce && recursiveIndexes.has(index)) {
|
|
4592
4626
|
diagnostics.push({
|
|
4593
4627
|
code: 'recursive-assignment-rule',
|
|
4594
4628
|
severity: 'warning',
|
|
4595
4629
|
rule: name,
|
|
4596
|
-
message: `${
|
|
4630
|
+
message: `${displayRuleName(name, program.prefixes || {})} is a run-once rule in a recursive dependency cycle`,
|
|
4597
4631
|
});
|
|
4598
4632
|
}
|
|
4599
4633
|
|
|
@@ -4949,7 +4983,7 @@
|
|
|
4949
4983
|
return components;
|
|
4950
4984
|
}
|
|
4951
4985
|
|
|
4952
|
-
function sequentialWellFormednessDiagnostics(clauses, ruleNameValue,
|
|
4986
|
+
function sequentialWellFormednessDiagnostics(clauses, ruleNameValue, prefixes = {}) {
|
|
4953
4987
|
const diagnostics = [];
|
|
4954
4988
|
|
|
4955
4989
|
function visit(items, initialBound, scopeLabel) {
|
|
@@ -4964,7 +4998,7 @@
|
|
|
4964
4998
|
code: 'unbound-filter-variable',
|
|
4965
4999
|
severity: 'error',
|
|
4966
5000
|
rule: ruleNameValue,
|
|
4967
|
-
message: `${
|
|
5001
|
+
message: `${displayRuleName(ruleNameValue, prefixes)} FILTER uses ?${variable} before it is bound${scopeLabel}`,
|
|
4968
5002
|
});
|
|
4969
5003
|
}
|
|
4970
5004
|
}
|
|
@@ -4974,7 +5008,7 @@
|
|
|
4974
5008
|
code: 'assignment-variable-already-bound',
|
|
4975
5009
|
severity: 'error',
|
|
4976
5010
|
rule: ruleNameValue,
|
|
4977
|
-
message: `${
|
|
5011
|
+
message: `${displayRuleName(ruleNameValue, prefixes)} SET assigns ?${clause.variable}, but that variable is already bound${scopeLabel}`,
|
|
4978
5012
|
});
|
|
4979
5013
|
}
|
|
4980
5014
|
for (const variable of expressionVariables(clause.expr)) {
|
|
@@ -4983,7 +5017,7 @@
|
|
|
4983
5017
|
code: 'unbound-assignment-variable',
|
|
4984
5018
|
severity: 'error',
|
|
4985
5019
|
rule: ruleNameValue,
|
|
4986
|
-
message: `${
|
|
5020
|
+
message: `${displayRuleName(ruleNameValue, prefixes)} SET expression uses ?${variable} before it is bound${scopeLabel}`,
|
|
4987
5021
|
});
|
|
4988
5022
|
}
|
|
4989
5023
|
}
|