@creationix/rex 0.4.1 → 0.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/rex.js CHANGED
@@ -32,10 +32,9 @@ var OPCODE_IDS = {
32
32
  object: "ob",
33
33
  mod: "md",
34
34
  neg: "ng",
35
- range: "rn",
36
- size: "sz"
35
+ range: "rn"
37
36
  };
38
- var KEYWORD_OPCODES = new Set(["boolean", "number", "string", "array", "object", "size"]);
37
+ var KEYWORD_OPCODES = new Set(["boolean", "number", "string", "array", "object"]);
39
38
  var BINARY_TO_OPCODE = {
40
39
  add: "add",
41
40
  sub: "sub",
@@ -491,11 +490,30 @@ function collectLogicalChain(node, op) {
491
490
  return [node];
492
491
  return [...collectLogicalChain(node.left, op), ...collectLogicalChain(node.right, op)];
493
492
  }
493
+ function formatParseError(source, match) {
494
+ const message = match.message ?? "Parse failed";
495
+ const pos = match.getRightmostFailurePosition?.();
496
+ if (typeof pos !== "number" || !Number.isFinite(pos))
497
+ return message;
498
+ const safePos = Math.max(0, Math.min(source.length, pos));
499
+ const lineStart = source.lastIndexOf(`
500
+ `, safePos - 1) + 1;
501
+ const lineEndIndex = source.indexOf(`
502
+ `, safePos);
503
+ const lineEnd = lineEndIndex === -1 ? source.length : lineEndIndex;
504
+ const lineText = source.slice(lineStart, lineEnd);
505
+ const lineNumber = source.slice(0, lineStart).split(`
506
+ `).length;
507
+ const columnNumber = safePos - lineStart + 1;
508
+ const caret = `${" ".repeat(Math.max(0, columnNumber - 1))}^`;
509
+ return `${message}
510
+ ${lineText}
511
+ ${caret}`;
512
+ }
494
513
  function parseToIR(source) {
495
514
  const match = grammar.match(source);
496
515
  if (!match.succeeded()) {
497
- const failure = match;
498
- throw new Error(failure.message ?? "Parse failed");
516
+ throw new Error(formatParseError(source, match));
499
517
  }
500
518
  return semantics(match).toIR();
501
519
  }
@@ -841,20 +859,11 @@ function gatherEncodedValueSpans(text) {
841
859
  }
842
860
  return spans;
843
861
  }
844
- function buildPointerToken(pointerStart, targetStart) {
845
- let offset = targetStart - (pointerStart + 1);
862
+ function buildPointerToken(pointerStart, targetStart, occurrenceSize) {
863
+ const offset = targetStart - pointerStart - occurrenceSize;
846
864
  if (offset < 0)
847
865
  return;
848
- for (let guard = 0;guard < 8; guard += 1) {
849
- const prefix = encodeUint(offset);
850
- const recalculated = targetStart - (pointerStart + prefix.length + 1);
851
- if (recalculated === offset)
852
- return `${prefix}^`;
853
- offset = recalculated;
854
- if (offset < 0)
855
- return;
856
- }
857
- return;
866
+ return `${encodeUint(offset)}^`;
858
867
  }
859
868
  function buildDedupeCandidateTable(encoded, minBytes) {
860
869
  const spans = gatherEncodedValueSpans(encoded);
@@ -896,7 +905,7 @@ function dedupeLargeEncodedValues(encoded, minBytes = 4) {
896
905
  if (current.slice(occurrence.span.start, occurrence.span.end) !== value)
897
906
  continue;
898
907
  const canonicalCurrentStart = current.length - canonical.offsetFromEnd - canonical.sizeBytes;
899
- const pointerToken = buildPointerToken(occurrence.span.start, canonicalCurrentStart);
908
+ const pointerToken = buildPointerToken(occurrence.span.start, canonicalCurrentStart, occurrence.sizeBytes);
900
909
  if (!pointerToken)
901
910
  continue;
902
911
  if (pointerToken.length >= occurrence.sizeBytes)
@@ -925,831 +934,8 @@ function encodeIR(node, options) {
925
934
  activeEncodeOptions = previous;
926
935
  }
927
936
  }
928
- function cloneNode(node) {
929
- return structuredClone(node);
930
- }
931
- function emptyOptimizeEnv() {
932
- return { constants: {}, selfCaptures: {} };
933
- }
934
- function cloneOptimizeEnv(env) {
935
- return {
936
- constants: { ...env.constants },
937
- selfCaptures: { ...env.selfCaptures }
938
- };
939
- }
940
- function clearOptimizeEnv(env) {
941
- for (const key of Object.keys(env.constants))
942
- delete env.constants[key];
943
- for (const key of Object.keys(env.selfCaptures))
944
- delete env.selfCaptures[key];
945
- }
946
- function clearBinding(env, name) {
947
- delete env.constants[name];
948
- delete env.selfCaptures[name];
949
- }
950
- function selfTargetFromNode(node, currentDepth) {
951
- if (node.type === "self")
952
- return currentDepth;
953
- if (node.type === "selfDepth") {
954
- const target = currentDepth - (node.depth - 1);
955
- if (target >= 1)
956
- return target;
957
- }
958
- return;
959
- }
960
- function selfNodeFromTarget(targetDepth, currentDepth) {
961
- const relDepth = currentDepth - targetDepth + 1;
962
- if (!Number.isInteger(relDepth) || relDepth < 1)
963
- return;
964
- if (relDepth === 1)
965
- return { type: "self" };
966
- return { type: "selfDepth", depth: relDepth };
967
- }
968
- function dropBindingNames(env, binding) {
969
- if (binding.type === "binding:valueIn") {
970
- clearBinding(env, binding.value);
971
- return;
972
- }
973
- if (binding.type === "binding:keyValueIn") {
974
- clearBinding(env, binding.key);
975
- clearBinding(env, binding.value);
976
- return;
977
- }
978
- if (binding.type === "binding:keyOf") {
979
- clearBinding(env, binding.key);
980
- }
981
- }
982
- function optimizeBinding(binding, sourceEnv, currentDepth) {
983
- const source = optimizeNode(binding.source, sourceEnv, currentDepth);
984
- switch (binding.type) {
985
- case "binding:bareIn":
986
- return { type: "binding:bareIn", source };
987
- case "binding:bareOf":
988
- return { type: "binding:bareOf", source };
989
- case "binding:valueIn":
990
- return { type: "binding:valueIn", value: binding.value, source };
991
- case "binding:keyValueIn":
992
- return { type: "binding:keyValueIn", key: binding.key, value: binding.value, source };
993
- case "binding:keyOf":
994
- return { type: "binding:keyOf", key: binding.key, source };
995
- }
996
- }
997
- function collectReads(node, out) {
998
- switch (node.type) {
999
- case "identifier":
1000
- out.add(node.name);
1001
- return;
1002
- case "group":
1003
- collectReads(node.expression, out);
1004
- return;
1005
- case "array":
1006
- for (const item of node.items)
1007
- collectReads(item, out);
1008
- return;
1009
- case "object":
1010
- for (const entry of node.entries) {
1011
- collectReads(entry.key, out);
1012
- collectReads(entry.value, out);
1013
- }
1014
- return;
1015
- case "arrayComprehension":
1016
- collectReads(node.binding.source, out);
1017
- collectReads(node.body, out);
1018
- return;
1019
- case "whileArrayComprehension":
1020
- collectReads(node.condition, out);
1021
- collectReads(node.body, out);
1022
- return;
1023
- case "objectComprehension":
1024
- collectReads(node.binding.source, out);
1025
- collectReads(node.key, out);
1026
- collectReads(node.value, out);
1027
- return;
1028
- case "whileObjectComprehension":
1029
- collectReads(node.condition, out);
1030
- collectReads(node.key, out);
1031
- collectReads(node.value, out);
1032
- return;
1033
- case "unary":
1034
- collectReads(node.value, out);
1035
- return;
1036
- case "binary":
1037
- collectReads(node.left, out);
1038
- collectReads(node.right, out);
1039
- return;
1040
- case "assign":
1041
- if (!(node.op === "=" && node.place.type === "identifier"))
1042
- collectReads(node.place, out);
1043
- collectReads(node.value, out);
1044
- return;
1045
- case "navigation":
1046
- collectReads(node.target, out);
1047
- for (const segment of node.segments) {
1048
- if (segment.type === "dynamic")
1049
- collectReads(segment.key, out);
1050
- }
1051
- return;
1052
- case "call":
1053
- collectReads(node.callee, out);
1054
- for (const arg of node.args)
1055
- collectReads(arg, out);
1056
- return;
1057
- case "conditional":
1058
- collectReads(node.condition, out);
1059
- for (const part of node.thenBlock)
1060
- collectReads(part, out);
1061
- if (node.elseBranch)
1062
- collectReadsElse(node.elseBranch, out);
1063
- return;
1064
- case "for":
1065
- collectReads(node.binding.source, out);
1066
- for (const part of node.body)
1067
- collectReads(part, out);
1068
- return;
1069
- case "range":
1070
- collectReads(node.from, out);
1071
- collectReads(node.to, out);
1072
- return;
1073
- case "program":
1074
- for (const part of node.body)
1075
- collectReads(part, out);
1076
- return;
1077
- default:
1078
- return;
1079
- }
1080
- }
1081
- function collectReadsElse(elseBranch, out) {
1082
- if (elseBranch.type === "else") {
1083
- for (const part of elseBranch.block)
1084
- collectReads(part, out);
1085
- return;
1086
- }
1087
- collectReads(elseBranch.condition, out);
1088
- for (const part of elseBranch.thenBlock)
1089
- collectReads(part, out);
1090
- if (elseBranch.elseBranch)
1091
- collectReadsElse(elseBranch.elseBranch, out);
1092
- }
1093
- function isPureNode(node) {
1094
- switch (node.type) {
1095
- case "identifier":
1096
- case "self":
1097
- case "selfDepth":
1098
- case "boolean":
1099
- case "null":
1100
- case "undefined":
1101
- case "number":
1102
- case "string":
1103
- case "key":
1104
- return true;
1105
- case "group":
1106
- return isPureNode(node.expression);
1107
- case "array":
1108
- return node.items.every((item) => isPureNode(item));
1109
- case "object":
1110
- return node.entries.every((entry) => isPureNode(entry.key) && isPureNode(entry.value));
1111
- case "navigation":
1112
- return isPureNode(node.target) && node.segments.every((segment) => segment.type === "static" || isPureNode(segment.key));
1113
- case "unary":
1114
- return node.op !== "delete" && isPureNode(node.value);
1115
- case "binary":
1116
- return isPureNode(node.left) && isPureNode(node.right);
1117
- case "range":
1118
- return isPureNode(node.from) && isPureNode(node.to);
1119
- default:
1120
- return false;
1121
- }
1122
- }
1123
- function eliminateDeadAssignments(block) {
1124
- const needed = new Set;
1125
- const out = [];
1126
- for (let index = block.length - 1;index >= 0; index -= 1) {
1127
- const node = block[index];
1128
- if (node.type === "conditional") {
1129
- let rewritten = node;
1130
- if (node.condition.type === "assign" && node.condition.op === "=" && node.condition.place.type === "identifier") {
1131
- const name = node.condition.place.name;
1132
- const branchReads = new Set;
1133
- for (const part of node.thenBlock)
1134
- collectReads(part, branchReads);
1135
- if (node.elseBranch)
1136
- collectReadsElse(node.elseBranch, branchReads);
1137
- if (!needed.has(name) && !branchReads.has(name)) {
1138
- rewritten = {
1139
- type: "conditional",
1140
- head: node.head,
1141
- condition: node.condition.value,
1142
- thenBlock: node.thenBlock,
1143
- elseBranch: node.elseBranch
1144
- };
1145
- }
1146
- }
1147
- collectReads(rewritten, needed);
1148
- out.push(rewritten);
1149
- continue;
1150
- }
1151
- if (node.type === "assign" && node.op === "=" && node.place.type === "identifier") {
1152
- collectReads(node.value, needed);
1153
- const name = node.place.name;
1154
- const canDrop = !needed.has(name) && isPureNode(node.value);
1155
- needed.delete(name);
1156
- if (canDrop)
1157
- continue;
1158
- out.push(node);
1159
- continue;
1160
- }
1161
- collectReads(node, needed);
1162
- out.push(node);
1163
- }
1164
- out.reverse();
1165
- return out;
1166
- }
1167
- function hasIdentifierRead(node, name, asPlace = false) {
1168
- if (node.type === "identifier")
1169
- return !asPlace && node.name === name;
1170
- switch (node.type) {
1171
- case "group":
1172
- return hasIdentifierRead(node.expression, name);
1173
- case "array":
1174
- return node.items.some((item) => hasIdentifierRead(item, name));
1175
- case "object":
1176
- return node.entries.some((entry) => hasIdentifierRead(entry.key, name) || hasIdentifierRead(entry.value, name));
1177
- case "navigation":
1178
- return hasIdentifierRead(node.target, name) || node.segments.some((segment) => segment.type === "dynamic" && hasIdentifierRead(segment.key, name));
1179
- case "unary":
1180
- return hasIdentifierRead(node.value, name, node.op === "delete");
1181
- case "binary":
1182
- return hasIdentifierRead(node.left, name) || hasIdentifierRead(node.right, name);
1183
- case "range":
1184
- return hasIdentifierRead(node.from, name) || hasIdentifierRead(node.to, name);
1185
- case "assign":
1186
- return hasIdentifierRead(node.place, name, true) || hasIdentifierRead(node.value, name);
1187
- default:
1188
- return false;
1189
- }
1190
- }
1191
- function countIdentifierReads(node, name, asPlace = false) {
1192
- if (node.type === "identifier")
1193
- return !asPlace && node.name === name ? 1 : 0;
1194
- switch (node.type) {
1195
- case "group":
1196
- return countIdentifierReads(node.expression, name);
1197
- case "array":
1198
- return node.items.reduce((sum, item) => sum + countIdentifierReads(item, name), 0);
1199
- case "object":
1200
- return node.entries.reduce((sum, entry) => sum + countIdentifierReads(entry.key, name) + countIdentifierReads(entry.value, name), 0);
1201
- case "navigation":
1202
- return countIdentifierReads(node.target, name) + node.segments.reduce((sum, segment) => sum + (segment.type === "dynamic" ? countIdentifierReads(segment.key, name) : 0), 0);
1203
- case "unary":
1204
- return countIdentifierReads(node.value, name, node.op === "delete");
1205
- case "binary":
1206
- return countIdentifierReads(node.left, name) + countIdentifierReads(node.right, name);
1207
- case "range":
1208
- return countIdentifierReads(node.from, name) + countIdentifierReads(node.to, name);
1209
- case "assign":
1210
- return countIdentifierReads(node.place, name, true) + countIdentifierReads(node.value, name);
1211
- default:
1212
- return 0;
1213
- }
1214
- }
1215
- function replaceIdentifier(node, name, replacement, asPlace = false) {
1216
- if (node.type === "identifier") {
1217
- if (!asPlace && node.name === name)
1218
- return cloneNode(replacement);
1219
- return node;
1220
- }
1221
- switch (node.type) {
1222
- case "group":
1223
- return {
1224
- type: "group",
1225
- expression: replaceIdentifier(node.expression, name, replacement)
1226
- };
1227
- case "array":
1228
- return { type: "array", items: node.items.map((item) => replaceIdentifier(item, name, replacement)) };
1229
- case "object":
1230
- return {
1231
- type: "object",
1232
- entries: node.entries.map((entry) => ({
1233
- key: replaceIdentifier(entry.key, name, replacement),
1234
- value: replaceIdentifier(entry.value, name, replacement)
1235
- }))
1236
- };
1237
- case "navigation":
1238
- return {
1239
- type: "navigation",
1240
- target: replaceIdentifier(node.target, name, replacement),
1241
- segments: node.segments.map((segment) => segment.type === "static" ? segment : { type: "dynamic", key: replaceIdentifier(segment.key, name, replacement) })
1242
- };
1243
- case "unary":
1244
- return {
1245
- type: "unary",
1246
- op: node.op,
1247
- value: replaceIdentifier(node.value, name, replacement, node.op === "delete")
1248
- };
1249
- case "binary":
1250
- return {
1251
- type: "binary",
1252
- op: node.op,
1253
- left: replaceIdentifier(node.left, name, replacement),
1254
- right: replaceIdentifier(node.right, name, replacement)
1255
- };
1256
- case "assign":
1257
- return {
1258
- type: "assign",
1259
- op: node.op,
1260
- place: replaceIdentifier(node.place, name, replacement, true),
1261
- value: replaceIdentifier(node.value, name, replacement)
1262
- };
1263
- case "range":
1264
- return {
1265
- type: "range",
1266
- from: replaceIdentifier(node.from, name, replacement),
1267
- to: replaceIdentifier(node.to, name, replacement)
1268
- };
1269
- default:
1270
- return node;
1271
- }
1272
- }
1273
- function isSafeInlineTargetNode(node) {
1274
- if (isPureNode(node))
1275
- return true;
1276
- if (node.type === "assign" && node.op === "=") {
1277
- return isPureNode(node.place) && isPureNode(node.value);
1278
- }
1279
- return false;
1280
- }
1281
- function inlineAdjacentPureAssignments(block) {
1282
- const out = [...block];
1283
- let changed = true;
1284
- while (changed) {
1285
- changed = false;
1286
- for (let index = 0;index < out.length - 1; index += 1) {
1287
- const current = out[index];
1288
- if (current.type !== "assign" || current.op !== "=" || current.place.type !== "identifier")
1289
- continue;
1290
- if (!isPureNode(current.value))
1291
- continue;
1292
- const name = current.place.name;
1293
- if (hasIdentifierRead(current.value, name))
1294
- continue;
1295
- const next = out[index + 1];
1296
- if (!isSafeInlineTargetNode(next))
1297
- continue;
1298
- if (countIdentifierReads(next, name) !== 1)
1299
- continue;
1300
- out[index + 1] = replaceIdentifier(next, name, current.value);
1301
- out.splice(index, 1);
1302
- changed = true;
1303
- break;
1304
- }
1305
- }
1306
- return out;
1307
- }
1308
- function toNumberNode(value) {
1309
- let raw;
1310
- if (Number.isNaN(value))
1311
- raw = "nan";
1312
- else if (value === Infinity)
1313
- raw = "inf";
1314
- else if (value === -Infinity)
1315
- raw = "-inf";
1316
- else
1317
- raw = String(value);
1318
- return { type: "number", raw, value };
1319
- }
1320
- function toStringNode(value) {
1321
- return { type: "string", raw: JSON.stringify(value) };
1322
- }
1323
- function toLiteralNode(value) {
1324
- if (value === undefined)
1325
- return { type: "undefined" };
1326
- if (value === null)
1327
- return { type: "null" };
1328
- if (typeof value === "boolean")
1329
- return { type: "boolean", value };
1330
- if (typeof value === "number")
1331
- return toNumberNode(value);
1332
- if (typeof value === "string")
1333
- return toStringNode(value);
1334
- if (Array.isArray(value)) {
1335
- const items = [];
1336
- for (const item of value) {
1337
- const lowered = toLiteralNode(item);
1338
- if (!lowered)
1339
- return;
1340
- items.push(lowered);
1341
- }
1342
- return { type: "array", items };
1343
- }
1344
- if (value && typeof value === "object") {
1345
- const entries = [];
1346
- for (const [key, entryValue] of Object.entries(value)) {
1347
- const loweredValue = toLiteralNode(entryValue);
1348
- if (!loweredValue)
1349
- return;
1350
- entries.push({ key: { type: "key", name: key }, value: loweredValue });
1351
- }
1352
- return { type: "object", entries };
1353
- }
1354
- return;
1355
- }
1356
- function constValue(node) {
1357
- switch (node.type) {
1358
- case "undefined":
1359
- return;
1360
- case "null":
1361
- return null;
1362
- case "boolean":
1363
- return node.value;
1364
- case "number":
1365
- return node.value;
1366
- case "string":
1367
- return decodeStringLiteral(node.raw);
1368
- case "key":
1369
- return node.name;
1370
- case "array": {
1371
- const out = [];
1372
- for (const item of node.items) {
1373
- const value = constValue(item);
1374
- if (value === undefined && item.type !== "undefined")
1375
- return;
1376
- out.push(value);
1377
- }
1378
- return out;
1379
- }
1380
- case "object": {
1381
- const out = {};
1382
- for (const entry of node.entries) {
1383
- const key = constValue(entry.key);
1384
- if (key === undefined && entry.key.type !== "undefined")
1385
- return;
1386
- const value = constValue(entry.value);
1387
- if (value === undefined && entry.value.type !== "undefined")
1388
- return;
1389
- out[String(key)] = value;
1390
- }
1391
- return out;
1392
- }
1393
- default:
1394
- return;
1395
- }
1396
- }
1397
- function isDefinedValue(value) {
1398
- return value !== undefined;
1399
- }
1400
- function foldUnary(op, value) {
1401
- if (op === "neg") {
1402
- if (typeof value !== "number")
1403
- return;
1404
- return -value;
1405
- }
1406
- if (op === "not") {
1407
- if (typeof value === "boolean")
1408
- return !value;
1409
- if (typeof value === "number")
1410
- return ~value;
1411
- return;
1412
- }
1413
- if (op === "logicalNot") {
1414
- return value === undefined ? true : undefined;
1415
- }
1416
- return;
1417
- }
1418
- function foldBinary(op, left, right) {
1419
- if (op === "add" || op === "sub" || op === "mul" || op === "div" || op === "mod") {
1420
- if (typeof left !== "number" || typeof right !== "number")
1421
- return;
1422
- if (op === "add")
1423
- return left + right;
1424
- if (op === "sub")
1425
- return left - right;
1426
- if (op === "mul")
1427
- return left * right;
1428
- if (op === "div")
1429
- return left / right;
1430
- return left % right;
1431
- }
1432
- if (op === "bitAnd" || op === "bitOr" || op === "bitXor") {
1433
- if (typeof left !== "number" || typeof right !== "number")
1434
- return;
1435
- if (op === "bitAnd")
1436
- return left & right;
1437
- if (op === "bitOr")
1438
- return left | right;
1439
- return left ^ right;
1440
- }
1441
- if (op === "eq")
1442
- return left === right ? left : undefined;
1443
- if (op === "neq")
1444
- return left !== right ? left : undefined;
1445
- if (op === "gt" || op === "gte" || op === "lt" || op === "lte") {
1446
- if (typeof left !== "number" || typeof right !== "number")
1447
- return;
1448
- if (op === "gt")
1449
- return left > right ? left : undefined;
1450
- if (op === "gte")
1451
- return left >= right ? left : undefined;
1452
- if (op === "lt")
1453
- return left < right ? left : undefined;
1454
- return left <= right ? left : undefined;
1455
- }
1456
- if (op === "and")
1457
- return isDefinedValue(left) ? right : undefined;
1458
- if (op === "or")
1459
- return isDefinedValue(left) ? left : right;
1460
- return;
1461
- }
1462
- function optimizeElse(elseBranch, env, currentDepth) {
1463
- if (!elseBranch)
1464
- return;
1465
- if (elseBranch.type === "else") {
1466
- return { type: "else", block: optimizeBlock(elseBranch.block, cloneOptimizeEnv(env), currentDepth) };
1467
- }
1468
- const optimizedCondition = optimizeNode(elseBranch.condition, env, currentDepth);
1469
- const foldedCondition = constValue(optimizedCondition);
1470
- if (foldedCondition !== undefined || optimizedCondition.type === "undefined") {
1471
- const passes = elseBranch.head === "when" ? isDefinedValue(foldedCondition) : !isDefinedValue(foldedCondition);
1472
- if (passes) {
1473
- return {
1474
- type: "else",
1475
- block: optimizeBlock(elseBranch.thenBlock, cloneOptimizeEnv(env), currentDepth)
1476
- };
1477
- }
1478
- return optimizeElse(elseBranch.elseBranch, env, currentDepth);
1479
- }
1480
- return {
1481
- type: "elseChain",
1482
- head: elseBranch.head,
1483
- condition: optimizedCondition,
1484
- thenBlock: optimizeBlock(elseBranch.thenBlock, cloneOptimizeEnv(env), currentDepth),
1485
- elseBranch: optimizeElse(elseBranch.elseBranch, cloneOptimizeEnv(env), currentDepth)
1486
- };
1487
- }
1488
- function optimizeBlock(block, env, currentDepth) {
1489
- const out = [];
1490
- for (const node of block) {
1491
- const optimized = optimizeNode(node, env, currentDepth);
1492
- out.push(optimized);
1493
- if (optimized.type === "break" || optimized.type === "continue")
1494
- break;
1495
- if (optimized.type === "assign" && optimized.op === "=" && optimized.place.type === "identifier") {
1496
- const selfTarget = selfTargetFromNode(optimized.value, currentDepth);
1497
- if (selfTarget !== undefined) {
1498
- env.selfCaptures[optimized.place.name] = selfTarget;
1499
- delete env.constants[optimized.place.name];
1500
- continue;
1501
- }
1502
- const folded = constValue(optimized.value);
1503
- if (folded !== undefined || optimized.value.type === "undefined") {
1504
- env.constants[optimized.place.name] = cloneNode(optimized.value);
1505
- delete env.selfCaptures[optimized.place.name];
1506
- } else {
1507
- clearBinding(env, optimized.place.name);
1508
- }
1509
- continue;
1510
- }
1511
- if (optimized.type === "unary" && optimized.op === "delete" && optimized.value.type === "identifier") {
1512
- clearBinding(env, optimized.value.name);
1513
- continue;
1514
- }
1515
- if (optimized.type === "assign" && optimized.place.type === "identifier") {
1516
- clearBinding(env, optimized.place.name);
1517
- continue;
1518
- }
1519
- if (optimized.type === "assign" || optimized.type === "for" || optimized.type === "call") {
1520
- clearOptimizeEnv(env);
1521
- }
1522
- }
1523
- return inlineAdjacentPureAssignments(eliminateDeadAssignments(out));
1524
- }
1525
- function optimizeNode(node, env, currentDepth, asPlace = false) {
1526
- switch (node.type) {
1527
- case "program": {
1528
- const body = optimizeBlock(node.body, cloneOptimizeEnv(env), currentDepth);
1529
- if (body.length === 0)
1530
- return { type: "undefined" };
1531
- if (body.length === 1)
1532
- return body[0];
1533
- return { type: "program", body };
1534
- }
1535
- case "identifier": {
1536
- if (asPlace)
1537
- return node;
1538
- const selfTarget = env.selfCaptures[node.name];
1539
- if (selfTarget !== undefined) {
1540
- const rewritten = selfNodeFromTarget(selfTarget, currentDepth);
1541
- if (rewritten)
1542
- return rewritten;
1543
- }
1544
- const replacement = env.constants[node.name];
1545
- return replacement ? cloneNode(replacement) : node;
1546
- }
1547
- case "group": {
1548
- return optimizeNode(node.expression, env, currentDepth);
1549
- }
1550
- case "array": {
1551
- return { type: "array", items: node.items.map((item) => optimizeNode(item, env, currentDepth)) };
1552
- }
1553
- case "object": {
1554
- return {
1555
- type: "object",
1556
- entries: node.entries.map((entry) => ({
1557
- key: optimizeNode(entry.key, env, currentDepth),
1558
- value: optimizeNode(entry.value, env, currentDepth)
1559
- }))
1560
- };
1561
- }
1562
- case "unary": {
1563
- const value = optimizeNode(node.value, env, currentDepth, node.op === "delete");
1564
- const foldedValue = constValue(value);
1565
- if (foldedValue !== undefined || value.type === "undefined") {
1566
- const folded = foldUnary(node.op, foldedValue);
1567
- const literal = folded === undefined ? undefined : toLiteralNode(folded);
1568
- if (literal)
1569
- return literal;
1570
- }
1571
- return { type: "unary", op: node.op, value };
1572
- }
1573
- case "binary": {
1574
- const left = optimizeNode(node.left, env, currentDepth);
1575
- const right = optimizeNode(node.right, env, currentDepth);
1576
- const leftValue = constValue(left);
1577
- const rightValue = constValue(right);
1578
- if ((leftValue !== undefined || left.type === "undefined") && (rightValue !== undefined || right.type === "undefined")) {
1579
- const folded = foldBinary(node.op, leftValue, rightValue);
1580
- const literal = folded === undefined ? undefined : toLiteralNode(folded);
1581
- if (literal)
1582
- return literal;
1583
- }
1584
- return { type: "binary", op: node.op, left, right };
1585
- }
1586
- case "range":
1587
- return {
1588
- type: "range",
1589
- from: optimizeNode(node.from, env, currentDepth),
1590
- to: optimizeNode(node.to, env, currentDepth)
1591
- };
1592
- case "navigation": {
1593
- const target = optimizeNode(node.target, env, currentDepth);
1594
- const segments = node.segments.map((segment) => segment.type === "static" ? segment : { type: "dynamic", key: optimizeNode(segment.key, env, currentDepth) });
1595
- const targetValue = constValue(target);
1596
- if (targetValue !== undefined || target.type === "undefined") {
1597
- let current = targetValue;
1598
- let foldable = true;
1599
- for (const segment of segments) {
1600
- if (!foldable)
1601
- break;
1602
- const key = segment.type === "static" ? segment.key : constValue(segment.key);
1603
- if (segment.type === "dynamic" && key === undefined && segment.key.type !== "undefined") {
1604
- foldable = false;
1605
- break;
1606
- }
1607
- if (current === null || current === undefined) {
1608
- current = undefined;
1609
- continue;
1610
- }
1611
- current = current[String(key)];
1612
- }
1613
- if (foldable) {
1614
- const literal = toLiteralNode(current);
1615
- if (literal)
1616
- return literal;
1617
- }
1618
- }
1619
- return {
1620
- type: "navigation",
1621
- target,
1622
- segments
1623
- };
1624
- }
1625
- case "call": {
1626
- return {
1627
- type: "call",
1628
- callee: optimizeNode(node.callee, env, currentDepth),
1629
- args: node.args.map((arg) => optimizeNode(arg, env, currentDepth))
1630
- };
1631
- }
1632
- case "assign": {
1633
- return {
1634
- type: "assign",
1635
- op: node.op,
1636
- place: optimizeNode(node.place, env, currentDepth, true),
1637
- value: optimizeNode(node.value, env, currentDepth)
1638
- };
1639
- }
1640
- case "conditional": {
1641
- const condition = optimizeNode(node.condition, env, currentDepth);
1642
- const thenEnv = cloneOptimizeEnv(env);
1643
- if (condition.type === "assign" && condition.op === "=" && condition.place.type === "identifier") {
1644
- thenEnv.selfCaptures[condition.place.name] = currentDepth;
1645
- delete thenEnv.constants[condition.place.name];
1646
- }
1647
- const conditionValue = constValue(condition);
1648
- if (conditionValue !== undefined || condition.type === "undefined") {
1649
- const passes = node.head === "when" ? isDefinedValue(conditionValue) : !isDefinedValue(conditionValue);
1650
- if (passes) {
1651
- const thenBlock2 = optimizeBlock(node.thenBlock, thenEnv, currentDepth);
1652
- if (thenBlock2.length === 0)
1653
- return { type: "undefined" };
1654
- if (thenBlock2.length === 1)
1655
- return thenBlock2[0];
1656
- return { type: "program", body: thenBlock2 };
1657
- }
1658
- if (!node.elseBranch)
1659
- return { type: "undefined" };
1660
- const loweredElse = optimizeElse(node.elseBranch, cloneOptimizeEnv(env), currentDepth);
1661
- if (!loweredElse)
1662
- return { type: "undefined" };
1663
- if (loweredElse.type === "else") {
1664
- if (loweredElse.block.length === 0)
1665
- return { type: "undefined" };
1666
- if (loweredElse.block.length === 1)
1667
- return loweredElse.block[0];
1668
- return { type: "program", body: loweredElse.block };
1669
- }
1670
- return {
1671
- type: "conditional",
1672
- head: loweredElse.head,
1673
- condition: loweredElse.condition,
1674
- thenBlock: loweredElse.thenBlock,
1675
- elseBranch: loweredElse.elseBranch
1676
- };
1677
- }
1678
- const thenBlock = optimizeBlock(node.thenBlock, thenEnv, currentDepth);
1679
- const elseBranch = optimizeElse(node.elseBranch, cloneOptimizeEnv(env), currentDepth);
1680
- let finalCondition = condition;
1681
- if (condition.type === "assign" && condition.op === "=" && condition.place.type === "identifier") {
1682
- const name = condition.place.name;
1683
- const reads = new Set;
1684
- for (const part of thenBlock)
1685
- collectReads(part, reads);
1686
- if (elseBranch)
1687
- collectReadsElse(elseBranch, reads);
1688
- if (!reads.has(name)) {
1689
- finalCondition = condition.value;
1690
- }
1691
- }
1692
- return {
1693
- type: "conditional",
1694
- head: node.head,
1695
- condition: finalCondition,
1696
- thenBlock,
1697
- elseBranch
1698
- };
1699
- }
1700
- case "for": {
1701
- const sourceEnv = cloneOptimizeEnv(env);
1702
- const binding = optimizeBinding(node.binding, sourceEnv, currentDepth);
1703
- const bodyEnv = cloneOptimizeEnv(env);
1704
- dropBindingNames(bodyEnv, binding);
1705
- return {
1706
- type: "for",
1707
- binding,
1708
- body: optimizeBlock(node.body, bodyEnv, currentDepth + 1)
1709
- };
1710
- }
1711
- case "arrayComprehension": {
1712
- const sourceEnv = cloneOptimizeEnv(env);
1713
- const binding = optimizeBinding(node.binding, sourceEnv, currentDepth);
1714
- const bodyEnv = cloneOptimizeEnv(env);
1715
- dropBindingNames(bodyEnv, binding);
1716
- return {
1717
- type: "arrayComprehension",
1718
- binding,
1719
- body: optimizeNode(node.body, bodyEnv, currentDepth + 1)
1720
- };
1721
- }
1722
- case "whileArrayComprehension":
1723
- return {
1724
- type: "whileArrayComprehension",
1725
- condition: optimizeNode(node.condition, env, currentDepth),
1726
- body: optimizeNode(node.body, env, currentDepth + 1)
1727
- };
1728
- case "objectComprehension": {
1729
- const sourceEnv = cloneOptimizeEnv(env);
1730
- const binding = optimizeBinding(node.binding, sourceEnv, currentDepth);
1731
- const bodyEnv = cloneOptimizeEnv(env);
1732
- dropBindingNames(bodyEnv, binding);
1733
- return {
1734
- type: "objectComprehension",
1735
- binding,
1736
- key: optimizeNode(node.key, bodyEnv, currentDepth + 1),
1737
- value: optimizeNode(node.value, bodyEnv, currentDepth + 1)
1738
- };
1739
- }
1740
- case "whileObjectComprehension":
1741
- return {
1742
- type: "whileObjectComprehension",
1743
- condition: optimizeNode(node.condition, env, currentDepth),
1744
- key: optimizeNode(node.key, env, currentDepth + 1),
1745
- value: optimizeNode(node.value, env, currentDepth + 1)
1746
- };
1747
- default:
1748
- return node;
1749
- }
1750
- }
1751
937
  function optimizeIR(node) {
1752
- return optimizeNode(node, emptyOptimizeEnv(), 1);
938
+ return node;
1753
939
  }
1754
940
  function collectLocalBindings(node, locals) {
1755
941
  switch (node.type) {
@@ -2509,6 +1695,28 @@ semantics.addOperation("toIR", {
2509
1695
  source: source.toIR()
2510
1696
  };
2511
1697
  },
1698
+ IterBindingComprehension_keyValueIn(key, _comma, value, _in, source) {
1699
+ return {
1700
+ type: "binding:keyValueIn",
1701
+ key: key.sourceString,
1702
+ value: value.sourceString,
1703
+ source: source.toIR()
1704
+ };
1705
+ },
1706
+ IterBindingComprehension_valueIn(value, _in, source) {
1707
+ return {
1708
+ type: "binding:valueIn",
1709
+ value: value.sourceString,
1710
+ source: source.toIR()
1711
+ };
1712
+ },
1713
+ IterBindingComprehension_keyOf(key, _of, source) {
1714
+ return {
1715
+ type: "binding:keyOf",
1716
+ key: key.sourceString,
1717
+ source: source.toIR()
1718
+ };
1719
+ },
2512
1720
  Pair(key, _colon, value) {
2513
1721
  return { key: key.toIR(), value: value.toIR() };
2514
1722
  },
@@ -2572,9 +1780,6 @@ semantics.addOperation("toIR", {
2572
1780
  BooleanKw(_kw) {
2573
1781
  return { type: "identifier", name: "boolean" };
2574
1782
  },
2575
- SizeKw(_kw) {
2576
- return { type: "identifier", name: "size" };
2577
- },
2578
1783
  identifier(_a, _b) {
2579
1784
  return { type: "identifier", name: this.sourceString };
2580
1785
  },
@@ -2597,6 +1802,7 @@ export {
2597
1802
  optimizeIR,
2598
1803
  minifyLocalNamesIR,
2599
1804
  grammar,
1805
+ formatParseError,
2600
1806
  encodeIR,
2601
1807
  domainRefsFromConfig,
2602
1808
  rex_default as default,