@nudojs/cli 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/LICENSE +21 -0
- package/dist/{chunk-JW3RMCNY.js → chunk-LGWVGEMB.js} +363 -13
- package/dist/evaluator-api.d.ts +3 -1
- package/dist/evaluator-api.js +7 -3
- package/dist/index.js +12 -2
- package/package.json +13 -3
- package/src/__tests__/evaluator-loops.test.ts +163 -0
- package/src/__tests__/evaluator-refined.test.ts +121 -0
- package/src/__tests__/evaluator-string-methods.test.ts +151 -0
- package/src/evaluator-api.ts +2 -0
- package/src/evaluator.ts +380 -9
- package/src/index.ts +12 -0
- package/src/narrowing.ts +56 -0
- package/dist/chunk-RBHWWZII.js +0 -1467
- package/dist/chunk-VN5HMBGB.js +0 -1467
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# nudo
|
|
2
2
|
|
|
3
|
+
## 0.2.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 6c38283: docs and ci
|
|
8
|
+
- 9f7f819: fix pkg info
|
|
9
|
+
- c175f71: version
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- Updated dependencies [6c38283]
|
|
14
|
+
- Updated dependencies [9f7f819]
|
|
15
|
+
- Updated dependencies [c175f71]
|
|
16
|
+
- @nudojs/core@0.2.0
|
|
17
|
+
- @nudojs/parser@0.2.0
|
|
18
|
+
|
|
3
19
|
## 0.1.0
|
|
4
20
|
|
|
5
21
|
### Minor Changes
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Nudo Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -36,7 +36,9 @@ import {
|
|
|
36
36
|
narrowType,
|
|
37
37
|
subtractType,
|
|
38
38
|
getPrimitiveTypeOf,
|
|
39
|
-
typeValueEquals
|
|
39
|
+
typeValueEquals,
|
|
40
|
+
isSubtypeOf,
|
|
41
|
+
createRange
|
|
40
42
|
} from "@nudojs/core";
|
|
41
43
|
function narrow(test, env) {
|
|
42
44
|
if (test.type === "BinaryExpression" && test.operator === "===" && isTypeofExpr(test.left) && test.right.type === "StringLiteral") {
|
|
@@ -82,6 +84,19 @@ function narrow(test, env) {
|
|
|
82
84
|
if (test.type === "BinaryExpression" && test.operator === "instanceof" && test.left.type === "Identifier" && test.right.type === "Identifier") {
|
|
83
85
|
return narrowByInstanceof(test.left.name, test.right.name, env);
|
|
84
86
|
}
|
|
87
|
+
if (test.type === "BinaryExpression" && (test.operator === ">=" || test.operator === ">" || test.operator === "<=" || test.operator === "<") && test.left.type === "Identifier" && isLiteralNode(test.right)) {
|
|
88
|
+
const litVal = getLiteralValue(test.right);
|
|
89
|
+
if (litVal.kind === "literal" && typeof litVal.value === "number") {
|
|
90
|
+
return narrowByComparison(test.left.name, test.operator, litVal.value, env);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (test.type === "BinaryExpression" && (test.operator === ">=" || test.operator === ">" || test.operator === "<=" || test.operator === "<") && isLiteralNode(test.left) && test.right.type === "Identifier") {
|
|
94
|
+
const litVal = getLiteralValue(test.left);
|
|
95
|
+
if (litVal.kind === "literal" && typeof litVal.value === "number") {
|
|
96
|
+
const flipped = { ">=": "<=", ">": "<", "<=": ">=", "<": ">" };
|
|
97
|
+
return narrowByComparison(test.right.name, flipped[test.operator], litVal.value, env);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
85
100
|
if (test.type === "UnaryExpression" && test.operator === "!") {
|
|
86
101
|
const [trueEnv, falseEnv] = narrow(test.argument, env);
|
|
87
102
|
return [falseEnv, trueEnv];
|
|
@@ -183,17 +198,32 @@ function narrowByStrictEqual(varName, literalTV, env) {
|
|
|
183
198
|
falseEnv.bind(varName, excluded.kind === "never" ? current : excluded);
|
|
184
199
|
return [trueEnv, falseEnv];
|
|
185
200
|
}
|
|
201
|
+
function narrowByComparison(varName, op, value, env) {
|
|
202
|
+
const current = env.lookup(varName);
|
|
203
|
+
if (!isSubtypeOf(current, T.number) && current.kind !== "union") return [env, env];
|
|
204
|
+
const trueEnv = env.extend({});
|
|
205
|
+
const falseEnv = env.extend({});
|
|
206
|
+
const trueRange = op === ">=" ? createRange({ min: value }) : op === ">" ? createRange({ min: value + 1 }) : op === "<=" ? createRange({ max: value }) : createRange({ max: value - 1 });
|
|
207
|
+
const falseRange = op === ">=" ? createRange({ max: value - 1 }) : op === ">" ? createRange({ max: value }) : op === "<=" ? createRange({ min: value + 1 }) : createRange({ min: value });
|
|
208
|
+
trueEnv.bind(varName, trueRange);
|
|
209
|
+
falseEnv.bind(varName, falseRange);
|
|
210
|
+
return [trueEnv, falseEnv];
|
|
211
|
+
}
|
|
186
212
|
|
|
187
213
|
// src/evaluator.ts
|
|
188
214
|
import {
|
|
189
215
|
T as T2,
|
|
190
216
|
simplifyUnion,
|
|
191
|
-
|
|
217
|
+
dispatchBinaryOp,
|
|
218
|
+
dispatchMethod,
|
|
219
|
+
dispatchProperty,
|
|
192
220
|
Ops,
|
|
193
221
|
createEnvironment,
|
|
194
222
|
typeValueEquals as typeValueEquals2,
|
|
195
223
|
typeValueToString,
|
|
196
|
-
isSubtypeOf
|
|
224
|
+
isSubtypeOf as isSubtypeOf2,
|
|
225
|
+
widenLiteral,
|
|
226
|
+
createTemplate
|
|
197
227
|
} from "@nudojs/core";
|
|
198
228
|
var RETURN_SIGNAL = Symbol("ReturnSignal");
|
|
199
229
|
var BRANCH_SIGNAL = Symbol("BranchSignal");
|
|
@@ -235,6 +265,14 @@ function setModuleResolver(resolver) {
|
|
|
235
265
|
var currentModuleResolver = null;
|
|
236
266
|
var currentFileDir = "";
|
|
237
267
|
var _nodeTypeCollector = null;
|
|
268
|
+
var _sampleCount = 3;
|
|
269
|
+
var _maxConcreteIter = 1e3;
|
|
270
|
+
function setSampleCount(count) {
|
|
271
|
+
_sampleCount = count;
|
|
272
|
+
}
|
|
273
|
+
function setMaxConcreteIter(count) {
|
|
274
|
+
_maxConcreteIter = count;
|
|
275
|
+
}
|
|
238
276
|
function setNodeTypeCollector(collector) {
|
|
239
277
|
_nodeTypeCollector = collector;
|
|
240
278
|
}
|
|
@@ -374,7 +412,7 @@ function evaluateNode(node, env) {
|
|
|
374
412
|
parts.map((p) => p.kind === "literal" ? String(p.value) : "").join("")
|
|
375
413
|
);
|
|
376
414
|
}
|
|
377
|
-
return
|
|
415
|
+
return createTemplate(parts);
|
|
378
416
|
}
|
|
379
417
|
case "BinaryExpression": {
|
|
380
418
|
const leftVal = evaluate(node.left, env);
|
|
@@ -387,7 +425,7 @@ function evaluateNode(node, env) {
|
|
|
387
425
|
return distributeBinaryOverUnion(
|
|
388
426
|
leftVal,
|
|
389
427
|
rightVal,
|
|
390
|
-
(l, r) =>
|
|
428
|
+
(l, r) => dispatchBinaryOp(node.operator, l, r)
|
|
391
429
|
);
|
|
392
430
|
}
|
|
393
431
|
case "UnaryExpression": {
|
|
@@ -592,14 +630,14 @@ function evaluateNode(node, env) {
|
|
|
592
630
|
return evaluateForIn(node, rightVal, env);
|
|
593
631
|
}
|
|
594
632
|
case "ForStatement": {
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
633
|
+
return evaluateForStatement(node, env);
|
|
634
|
+
}
|
|
635
|
+
case "WhileStatement": {
|
|
636
|
+
return evaluateWhileStatement(node, env);
|
|
637
|
+
}
|
|
638
|
+
case "DoWhileStatement": {
|
|
639
|
+
return evaluateDoWhileStatement(node, env);
|
|
600
640
|
}
|
|
601
|
-
case "WhileStatement":
|
|
602
|
-
return T2.undefined;
|
|
603
641
|
case "FunctionDeclaration": {
|
|
604
642
|
if (!node.id) return T2.undefined;
|
|
605
643
|
const paramNames = node.params.map(describeParam);
|
|
@@ -728,6 +766,16 @@ function evaluateNode(node, env) {
|
|
|
728
766
|
if (propName === "length" && (obj.kind === "array" || obj.kind === "tuple")) {
|
|
729
767
|
return obj.kind === "tuple" ? T2.literal(obj.elements.length) : T2.number;
|
|
730
768
|
}
|
|
769
|
+
if (propName === "length" && obj.kind === "literal" && typeof obj.value === "string") {
|
|
770
|
+
return T2.literal(obj.value.length);
|
|
771
|
+
}
|
|
772
|
+
if (propName === "length" && obj.kind === "primitive" && obj.type === "string") {
|
|
773
|
+
return T2.number;
|
|
774
|
+
}
|
|
775
|
+
if (obj.kind === "refined") {
|
|
776
|
+
const result = dispatchProperty(obj, propName);
|
|
777
|
+
if (result !== void 0) return result;
|
|
778
|
+
}
|
|
731
779
|
return T2.unknown;
|
|
732
780
|
});
|
|
733
781
|
}
|
|
@@ -921,8 +969,184 @@ function evaluateMethodCall(callee, args, env) {
|
|
|
921
969
|
if (objVal.kind === "array" || objVal.kind === "tuple") {
|
|
922
970
|
return evaluateArrayMethod(objVal, methodName, args, env);
|
|
923
971
|
}
|
|
972
|
+
if (isStringLike(objVal)) {
|
|
973
|
+
const argVals = evaluateArgs(args, env);
|
|
974
|
+
if (isReturn(argVals) || isBranch(argVals) || isThrow(argVals)) return argVals;
|
|
975
|
+
if (objVal.kind === "refined") {
|
|
976
|
+
const refined = dispatchMethod(objVal, methodName, argVals);
|
|
977
|
+
if (refined !== void 0) return refined;
|
|
978
|
+
}
|
|
979
|
+
return evaluateStringMethod(objVal, methodName, argVals);
|
|
980
|
+
}
|
|
981
|
+
if (objVal.kind === "refined") {
|
|
982
|
+
const argVals = evaluateArgs(args, env);
|
|
983
|
+
if (isReturn(argVals) || isBranch(argVals) || isThrow(argVals)) return argVals;
|
|
984
|
+
const result = dispatchMethod(objVal, methodName, argVals);
|
|
985
|
+
if (result !== void 0) return result;
|
|
986
|
+
}
|
|
924
987
|
return null;
|
|
925
988
|
}
|
|
989
|
+
function isStringLike(tv) {
|
|
990
|
+
if (tv.kind === "literal" && typeof tv.value === "string") return true;
|
|
991
|
+
if (tv.kind === "primitive" && tv.type === "string") return true;
|
|
992
|
+
if (tv.kind === "refined") return isStringLike(tv.base);
|
|
993
|
+
return false;
|
|
994
|
+
}
|
|
995
|
+
function evaluateStringMethod(receiver, method, args) {
|
|
996
|
+
if (receiver.kind === "literal" && typeof receiver.value === "string") {
|
|
997
|
+
return evaluateStringMethodLiteral(receiver.value, method, args);
|
|
998
|
+
}
|
|
999
|
+
return evaluateStringMethodAbstract(method, args);
|
|
1000
|
+
}
|
|
1001
|
+
function evaluateStringMethodLiteral(str, method, args) {
|
|
1002
|
+
const litArg = (i) => {
|
|
1003
|
+
const a = args[i];
|
|
1004
|
+
if (a?.kind === "literal" && (typeof a.value === "string" || typeof a.value === "number")) return a.value;
|
|
1005
|
+
return void 0;
|
|
1006
|
+
};
|
|
1007
|
+
switch (method) {
|
|
1008
|
+
case "toUpperCase":
|
|
1009
|
+
return T2.literal(str.toUpperCase());
|
|
1010
|
+
case "toLowerCase":
|
|
1011
|
+
return T2.literal(str.toLowerCase());
|
|
1012
|
+
case "trim":
|
|
1013
|
+
return T2.literal(str.trim());
|
|
1014
|
+
case "trimStart":
|
|
1015
|
+
return T2.literal(str.trimStart());
|
|
1016
|
+
case "trimEnd":
|
|
1017
|
+
return T2.literal(str.trimEnd());
|
|
1018
|
+
case "charAt": {
|
|
1019
|
+
const idx = litArg(0);
|
|
1020
|
+
return typeof idx === "number" ? T2.literal(str.charAt(idx)) : T2.string;
|
|
1021
|
+
}
|
|
1022
|
+
case "charCodeAt": {
|
|
1023
|
+
const idx = litArg(0);
|
|
1024
|
+
return typeof idx === "number" ? T2.literal(str.charCodeAt(idx)) : T2.number;
|
|
1025
|
+
}
|
|
1026
|
+
case "at": {
|
|
1027
|
+
const idx = litArg(0);
|
|
1028
|
+
if (typeof idx === "number") {
|
|
1029
|
+
const ch = str.at(idx);
|
|
1030
|
+
return ch !== void 0 ? T2.literal(ch) : T2.undefined;
|
|
1031
|
+
}
|
|
1032
|
+
return T2.union(T2.string, T2.undefined);
|
|
1033
|
+
}
|
|
1034
|
+
case "startsWith": {
|
|
1035
|
+
const search = litArg(0);
|
|
1036
|
+
return typeof search === "string" ? T2.literal(str.startsWith(search)) : T2.boolean;
|
|
1037
|
+
}
|
|
1038
|
+
case "endsWith": {
|
|
1039
|
+
const search = litArg(0);
|
|
1040
|
+
return typeof search === "string" ? T2.literal(str.endsWith(search)) : T2.boolean;
|
|
1041
|
+
}
|
|
1042
|
+
case "includes": {
|
|
1043
|
+
const search = litArg(0);
|
|
1044
|
+
return typeof search === "string" ? T2.literal(str.includes(search)) : T2.boolean;
|
|
1045
|
+
}
|
|
1046
|
+
case "indexOf": {
|
|
1047
|
+
const search = litArg(0);
|
|
1048
|
+
return typeof search === "string" ? T2.literal(str.indexOf(search)) : T2.number;
|
|
1049
|
+
}
|
|
1050
|
+
case "lastIndexOf": {
|
|
1051
|
+
const search = litArg(0);
|
|
1052
|
+
return typeof search === "string" ? T2.literal(str.lastIndexOf(search)) : T2.number;
|
|
1053
|
+
}
|
|
1054
|
+
case "slice": {
|
|
1055
|
+
const start = litArg(0);
|
|
1056
|
+
const end = litArg(1);
|
|
1057
|
+
if (typeof start === "number") {
|
|
1058
|
+
return T2.literal(str.slice(start, typeof end === "number" ? end : void 0));
|
|
1059
|
+
}
|
|
1060
|
+
return T2.string;
|
|
1061
|
+
}
|
|
1062
|
+
case "substring": {
|
|
1063
|
+
const start = litArg(0);
|
|
1064
|
+
const end = litArg(1);
|
|
1065
|
+
if (typeof start === "number") {
|
|
1066
|
+
return T2.literal(str.substring(start, typeof end === "number" ? end : void 0));
|
|
1067
|
+
}
|
|
1068
|
+
return T2.string;
|
|
1069
|
+
}
|
|
1070
|
+
case "split": {
|
|
1071
|
+
const sep = litArg(0);
|
|
1072
|
+
if (typeof sep === "string") {
|
|
1073
|
+
const parts = str.split(sep);
|
|
1074
|
+
return T2.tuple(parts.map((p) => T2.literal(p)));
|
|
1075
|
+
}
|
|
1076
|
+
return T2.array(T2.string);
|
|
1077
|
+
}
|
|
1078
|
+
case "replace": {
|
|
1079
|
+
const search = litArg(0);
|
|
1080
|
+
const replacement = litArg(1);
|
|
1081
|
+
if (typeof search === "string" && typeof replacement === "string") {
|
|
1082
|
+
return T2.literal(str.replace(search, replacement));
|
|
1083
|
+
}
|
|
1084
|
+
return T2.string;
|
|
1085
|
+
}
|
|
1086
|
+
case "replaceAll": {
|
|
1087
|
+
const search = litArg(0);
|
|
1088
|
+
const replacement = litArg(1);
|
|
1089
|
+
if (typeof search === "string" && typeof replacement === "string") {
|
|
1090
|
+
return T2.literal(str.replaceAll(search, replacement));
|
|
1091
|
+
}
|
|
1092
|
+
return T2.string;
|
|
1093
|
+
}
|
|
1094
|
+
case "repeat": {
|
|
1095
|
+
const count = litArg(0);
|
|
1096
|
+
return typeof count === "number" ? T2.literal(str.repeat(count)) : T2.string;
|
|
1097
|
+
}
|
|
1098
|
+
case "padStart": {
|
|
1099
|
+
const len = litArg(0);
|
|
1100
|
+
const fill = litArg(1);
|
|
1101
|
+
if (typeof len === "number") {
|
|
1102
|
+
return T2.literal(str.padStart(len, typeof fill === "string" ? fill : void 0));
|
|
1103
|
+
}
|
|
1104
|
+
return T2.string;
|
|
1105
|
+
}
|
|
1106
|
+
case "padEnd": {
|
|
1107
|
+
const len = litArg(0);
|
|
1108
|
+
const fill = litArg(1);
|
|
1109
|
+
if (typeof len === "number") {
|
|
1110
|
+
return T2.literal(str.padEnd(len, typeof fill === "string" ? fill : void 0));
|
|
1111
|
+
}
|
|
1112
|
+
return T2.string;
|
|
1113
|
+
}
|
|
1114
|
+
default:
|
|
1115
|
+
return null;
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
function evaluateStringMethodAbstract(method, _args) {
|
|
1119
|
+
switch (method) {
|
|
1120
|
+
case "toUpperCase":
|
|
1121
|
+
case "toLowerCase":
|
|
1122
|
+
case "trim":
|
|
1123
|
+
case "trimStart":
|
|
1124
|
+
case "trimEnd":
|
|
1125
|
+
case "charAt":
|
|
1126
|
+
case "slice":
|
|
1127
|
+
case "substring":
|
|
1128
|
+
case "replace":
|
|
1129
|
+
case "replaceAll":
|
|
1130
|
+
case "repeat":
|
|
1131
|
+
case "padStart":
|
|
1132
|
+
case "padEnd":
|
|
1133
|
+
return T2.string;
|
|
1134
|
+
case "charCodeAt":
|
|
1135
|
+
case "indexOf":
|
|
1136
|
+
case "lastIndexOf":
|
|
1137
|
+
return T2.number;
|
|
1138
|
+
case "at":
|
|
1139
|
+
return T2.union(T2.string, T2.undefined);
|
|
1140
|
+
case "startsWith":
|
|
1141
|
+
case "endsWith":
|
|
1142
|
+
case "includes":
|
|
1143
|
+
return T2.boolean;
|
|
1144
|
+
case "split":
|
|
1145
|
+
return T2.array(T2.string);
|
|
1146
|
+
default:
|
|
1147
|
+
return null;
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
926
1150
|
function evaluateObjectStaticMethod(method, obj) {
|
|
927
1151
|
if (obj.kind !== "object") {
|
|
928
1152
|
if (method === "keys") return T2.array(T2.string);
|
|
@@ -1153,6 +1377,130 @@ function bindForLoopVar(left, value, env) {
|
|
|
1153
1377
|
env.bind(left.name, value);
|
|
1154
1378
|
}
|
|
1155
1379
|
}
|
|
1380
|
+
function getLoopVarNames(node) {
|
|
1381
|
+
if (node.type === "VariableDeclaration") {
|
|
1382
|
+
return node.declarations.map((d) => d.id?.type === "Identifier" ? d.id.name : null).filter((n) => n !== null);
|
|
1383
|
+
}
|
|
1384
|
+
return [];
|
|
1385
|
+
}
|
|
1386
|
+
function snapshotVars(names, env) {
|
|
1387
|
+
const snap = /* @__PURE__ */ new Map();
|
|
1388
|
+
for (const name of names) {
|
|
1389
|
+
snap.set(name, env.lookup(name));
|
|
1390
|
+
}
|
|
1391
|
+
return snap;
|
|
1392
|
+
}
|
|
1393
|
+
function varsStabilized(prev, curr) {
|
|
1394
|
+
for (const [name, prevVal] of prev) {
|
|
1395
|
+
const currVal = curr.get(name);
|
|
1396
|
+
if (!currVal || !typeValueEquals2(prevVal, currVal)) return false;
|
|
1397
|
+
}
|
|
1398
|
+
return true;
|
|
1399
|
+
}
|
|
1400
|
+
function widenVars(names, env) {
|
|
1401
|
+
for (const name of names) {
|
|
1402
|
+
const val = env.lookup(name);
|
|
1403
|
+
const widened = widenLiteral(val);
|
|
1404
|
+
if (!typeValueEquals2(val, widened)) {
|
|
1405
|
+
if (!env.update(name, widened)) env.bind(name, widened);
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1409
|
+
function evaluateForStatement(node, env) {
|
|
1410
|
+
const loopEnv = env.extend({});
|
|
1411
|
+
if (node.init) {
|
|
1412
|
+
const initResult = evaluate(node.init, loopEnv);
|
|
1413
|
+
if (isReturn(initResult) || isBranch(initResult) || isThrow(initResult)) return initResult;
|
|
1414
|
+
}
|
|
1415
|
+
const varNames = node.init ? getLoopVarNames(node.init) : [];
|
|
1416
|
+
const returnValues = [];
|
|
1417
|
+
let concreteCompleted = false;
|
|
1418
|
+
for (let i = 0; i < _maxConcreteIter; i++) {
|
|
1419
|
+
if (node.test) {
|
|
1420
|
+
const testVal = evaluate(node.test, loopEnv);
|
|
1421
|
+
if (isReturn(testVal) || isBranch(testVal) || isThrow(testVal)) return testVal;
|
|
1422
|
+
if (testVal.kind === "literal" && testVal.value === false) {
|
|
1423
|
+
concreteCompleted = true;
|
|
1424
|
+
break;
|
|
1425
|
+
}
|
|
1426
|
+
if (testVal.kind !== "literal") break;
|
|
1427
|
+
}
|
|
1428
|
+
const bodyResult = evaluate(node.body, loopEnv);
|
|
1429
|
+
if (isReturn(bodyResult)) {
|
|
1430
|
+
returnValues.push(bodyResult.value);
|
|
1431
|
+
concreteCompleted = true;
|
|
1432
|
+
break;
|
|
1433
|
+
}
|
|
1434
|
+
if (isBranch(bodyResult)) {
|
|
1435
|
+
returnValues.push(bodyResult.returnedValue);
|
|
1436
|
+
}
|
|
1437
|
+
if (node.update) {
|
|
1438
|
+
const updateResult = evaluate(node.update, loopEnv);
|
|
1439
|
+
if (isReturn(updateResult) || isBranch(updateResult) || isThrow(updateResult)) return updateResult;
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
if (!concreteCompleted) {
|
|
1443
|
+
widenVars(varNames, loopEnv);
|
|
1444
|
+
const prevSnap = snapshotVars(varNames, loopEnv);
|
|
1445
|
+
for (let i = 0; i < 10; i++) {
|
|
1446
|
+
evaluate(node.body, loopEnv);
|
|
1447
|
+
if (node.update) evaluate(node.update, loopEnv);
|
|
1448
|
+
widenVars(varNames, loopEnv);
|
|
1449
|
+
const currSnap = snapshotVars(varNames, loopEnv);
|
|
1450
|
+
if (varsStabilized(prevSnap, currSnap)) break;
|
|
1451
|
+
}
|
|
1452
|
+
}
|
|
1453
|
+
for (const name of varNames) {
|
|
1454
|
+
const val = loopEnv.lookup(name);
|
|
1455
|
+
if (!env.update(name, val)) env.bind(name, val);
|
|
1456
|
+
}
|
|
1457
|
+
if (returnValues.length > 0) {
|
|
1458
|
+
return makeBranch(simplifyUnion(returnValues), env);
|
|
1459
|
+
}
|
|
1460
|
+
return T2.undefined;
|
|
1461
|
+
}
|
|
1462
|
+
function evaluateWhileStatement(node, env) {
|
|
1463
|
+
const returnValues = [];
|
|
1464
|
+
for (let i = 0; i < _maxConcreteIter; i++) {
|
|
1465
|
+
const tv = evaluate(node.test, env);
|
|
1466
|
+
if (isReturn(tv) || isBranch(tv) || isThrow(tv)) return tv;
|
|
1467
|
+
if (tv.kind === "literal" && tv.value === false) break;
|
|
1468
|
+
if (tv.kind !== "literal") break;
|
|
1469
|
+
const bodyResult = evaluate(node.body, env);
|
|
1470
|
+
if (isReturn(bodyResult)) {
|
|
1471
|
+
returnValues.push(bodyResult.value);
|
|
1472
|
+
break;
|
|
1473
|
+
}
|
|
1474
|
+
if (isBranch(bodyResult)) {
|
|
1475
|
+
returnValues.push(bodyResult.returnedValue);
|
|
1476
|
+
}
|
|
1477
|
+
}
|
|
1478
|
+
if (returnValues.length > 0) {
|
|
1479
|
+
return makeBranch(simplifyUnion(returnValues), env);
|
|
1480
|
+
}
|
|
1481
|
+
return T2.undefined;
|
|
1482
|
+
}
|
|
1483
|
+
function evaluateDoWhileStatement(node, env) {
|
|
1484
|
+
const returnValues = [];
|
|
1485
|
+
for (let i = 0; i < _maxConcreteIter; i++) {
|
|
1486
|
+
const bodyResult = evaluate(node.body, env);
|
|
1487
|
+
if (isReturn(bodyResult)) {
|
|
1488
|
+
returnValues.push(bodyResult.value);
|
|
1489
|
+
break;
|
|
1490
|
+
}
|
|
1491
|
+
if (isBranch(bodyResult)) {
|
|
1492
|
+
returnValues.push(bodyResult.returnedValue);
|
|
1493
|
+
}
|
|
1494
|
+
const tv = evaluate(node.test, env);
|
|
1495
|
+
if (isReturn(tv) || isBranch(tv) || isThrow(tv)) return tv;
|
|
1496
|
+
if (tv.kind === "literal" && tv.value === false) break;
|
|
1497
|
+
if (tv.kind !== "literal") break;
|
|
1498
|
+
}
|
|
1499
|
+
if (returnValues.length > 0) {
|
|
1500
|
+
return makeBranch(simplifyUnion(returnValues), env);
|
|
1501
|
+
}
|
|
1502
|
+
return T2.undefined;
|
|
1503
|
+
}
|
|
1156
1504
|
function evaluateImportDeclaration(node, env) {
|
|
1157
1505
|
const source = node.source.value;
|
|
1158
1506
|
if (!currentModuleResolver) return T2.undefined;
|
|
@@ -1222,7 +1570,7 @@ function evaluateInstanceof(left, _right, rightNode, _env) {
|
|
|
1222
1570
|
if (!className) return T2.boolean;
|
|
1223
1571
|
return distributeOverUnion(left, (lv) => {
|
|
1224
1572
|
if (lv.kind === "instance") {
|
|
1225
|
-
const matches = lv.className === className ||
|
|
1573
|
+
const matches = lv.className === className || isSubtypeOf2(lv, T2.instanceOf(className));
|
|
1226
1574
|
return T2.literal(matches);
|
|
1227
1575
|
}
|
|
1228
1576
|
return T2.boolean;
|
|
@@ -1456,6 +1804,8 @@ export {
|
|
|
1456
1804
|
narrow,
|
|
1457
1805
|
resetMemo,
|
|
1458
1806
|
setModuleResolver,
|
|
1807
|
+
setSampleCount,
|
|
1808
|
+
setMaxConcreteIter,
|
|
1459
1809
|
setNodeTypeCollector,
|
|
1460
1810
|
evaluate,
|
|
1461
1811
|
evaluateFunction,
|
package/dist/evaluator-api.d.ts
CHANGED
|
@@ -34,6 +34,8 @@ declare function setModuleResolver(resolver: ((source: string, fromDir: string)
|
|
|
34
34
|
ast: Node;
|
|
35
35
|
filePath: string;
|
|
36
36
|
} | null) | null): void;
|
|
37
|
+
declare function setSampleCount(count: number): void;
|
|
38
|
+
declare function setMaxConcreteIter(count: number): void;
|
|
37
39
|
declare function setNodeTypeCollector(collector: ((node: Node, tv: TypeValue) => void) | null): void;
|
|
38
40
|
declare function evaluate(node: Node, env: Environment): EvalResult;
|
|
39
41
|
type CallResult = {
|
|
@@ -54,4 +56,4 @@ declare function setCurrentFileDir(dir: string): void;
|
|
|
54
56
|
*/
|
|
55
57
|
declare function narrow(test: Node, env: Environment): [trueEnv: Environment, falseEnv: Environment];
|
|
56
58
|
|
|
57
|
-
export { evaluate, evaluateFunction, evaluateFunctionFull, evaluateProgram, getUnreachableRanges, narrow, resetMemo, resetUnreachableRanges, setCurrentFileDir, setModuleResolver, setNodeTypeCollector };
|
|
59
|
+
export { evaluate, evaluateFunction, evaluateFunctionFull, evaluateProgram, getUnreachableRanges, narrow, resetMemo, resetUnreachableRanges, setCurrentFileDir, setMaxConcreteIter, setModuleResolver, setNodeTypeCollector, setSampleCount };
|
package/dist/evaluator-api.js
CHANGED
|
@@ -8,9 +8,11 @@ import {
|
|
|
8
8
|
resetMemo,
|
|
9
9
|
resetUnreachableRanges,
|
|
10
10
|
setCurrentFileDir,
|
|
11
|
+
setMaxConcreteIter,
|
|
11
12
|
setModuleResolver,
|
|
12
|
-
setNodeTypeCollector
|
|
13
|
-
|
|
13
|
+
setNodeTypeCollector,
|
|
14
|
+
setSampleCount
|
|
15
|
+
} from "./chunk-LGWVGEMB.js";
|
|
14
16
|
export {
|
|
15
17
|
evaluate,
|
|
16
18
|
evaluateFunction,
|
|
@@ -21,6 +23,8 @@ export {
|
|
|
21
23
|
resetMemo,
|
|
22
24
|
resetUnreachableRanges,
|
|
23
25
|
setCurrentFileDir,
|
|
26
|
+
setMaxConcreteIter,
|
|
24
27
|
setModuleResolver,
|
|
25
|
-
setNodeTypeCollector
|
|
28
|
+
setNodeTypeCollector,
|
|
29
|
+
setSampleCount
|
|
26
30
|
};
|
package/dist/index.js
CHANGED
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
resetMemo,
|
|
9
9
|
setCurrentFileDir,
|
|
10
10
|
setModuleResolver
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-LGWVGEMB.js";
|
|
12
12
|
|
|
13
13
|
// ../../node_modules/.pnpm/commander@13.1.0/node_modules/commander/lib/error.js
|
|
14
14
|
var require_error = __commonJS({
|
|
@@ -3336,7 +3336,9 @@ import {
|
|
|
3336
3336
|
typeValueToString,
|
|
3337
3337
|
simplifyUnion,
|
|
3338
3338
|
createEnvironment,
|
|
3339
|
-
isSubtypeOf
|
|
3339
|
+
isSubtypeOf,
|
|
3340
|
+
isTemplate,
|
|
3341
|
+
getTemplateParts
|
|
3340
3342
|
} from "@nudojs/core";
|
|
3341
3343
|
import { parse, extractDirectives, parseTypeValueExpr } from "@nudojs/parser";
|
|
3342
3344
|
var program2 = new Command();
|
|
@@ -3380,6 +3382,14 @@ function typeValueToTSType(tv) {
|
|
|
3380
3382
|
}
|
|
3381
3383
|
case "primitive":
|
|
3382
3384
|
return tv.type;
|
|
3385
|
+
case "refined": {
|
|
3386
|
+
if (isTemplate(tv)) {
|
|
3387
|
+
const parts = getTemplateParts(tv);
|
|
3388
|
+
const inner = parts.map((p) => p.kind === "literal" && typeof p.value === "string" ? p.value : `\${${typeValueToTSType(p)}}`).join("");
|
|
3389
|
+
return `\`${inner}\``;
|
|
3390
|
+
}
|
|
3391
|
+
return typeValueToTSType(tv.base);
|
|
3392
|
+
}
|
|
3383
3393
|
case "object": {
|
|
3384
3394
|
const entries = Object.entries(tv.properties);
|
|
3385
3395
|
if (entries.length === 0) return "{}";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nudojs/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"exports": {
|
|
@@ -10,9 +10,19 @@
|
|
|
10
10
|
"bin": {
|
|
11
11
|
"nudo": "src/index.ts"
|
|
12
12
|
},
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "git+https://github.com/nudojs/nudo.git",
|
|
16
|
+
"directory": "packages/cli"
|
|
17
|
+
},
|
|
18
|
+
"publishConfig": {
|
|
19
|
+
"access": "public",
|
|
20
|
+
"provenance": true,
|
|
21
|
+
"registry": "https://registry.npmjs.org"
|
|
22
|
+
},
|
|
13
23
|
"dependencies": {
|
|
14
|
-
"@nudojs/core": "0.
|
|
15
|
-
"@nudojs/parser": "0.
|
|
24
|
+
"@nudojs/core": "0.2.0",
|
|
25
|
+
"@nudojs/parser": "0.2.0"
|
|
16
26
|
},
|
|
17
27
|
"scripts": {
|
|
18
28
|
"build": "tsup src/index.ts src/evaluator-api.ts --format esm --dts"
|