@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/README.md +24 -0
- package/package.json +9 -6
- package/rex-cli.js +785 -987
- package/rex-cli.ts +236 -16
- package/rex-repl.js +592 -1005
- package/rex-repl.ts +389 -101
- package/rex.js +51 -845
- package/rex.ohm +7 -8
- package/rex.ohm-bundle.cjs +1 -1
- package/rex.ohm-bundle.d.ts +4 -3
- package/rex.ohm-bundle.js +1 -1
- package/rex.ts +52 -23
- package/rexc-interpreter.ts +136 -15
- package/rx-cli.js +2836 -0
- package/rx-cli.ts +298 -0
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"
|
|
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
|
-
|
|
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
|
-
|
|
862
|
+
function buildPointerToken(pointerStart, targetStart, occurrenceSize) {
|
|
863
|
+
const offset = targetStart - pointerStart - occurrenceSize;
|
|
846
864
|
if (offset < 0)
|
|
847
865
|
return;
|
|
848
|
-
|
|
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
|
|
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,
|