@thigasdevelopment/luam 0.18.1 → 0.19.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/luam.mjs +308 -79
- package/package.json +1 -1
package/luam.mjs
CHANGED
|
@@ -6584,7 +6584,7 @@ import { tmpdir } from "node:os";
|
|
|
6584
6584
|
import { join as join2 } from "node:path";
|
|
6585
6585
|
|
|
6586
6586
|
// src/cli/version.ts
|
|
6587
|
-
var VERSION = true ? "0.
|
|
6587
|
+
var VERSION = true ? "0.19.0" : "0.0.0-dev";
|
|
6588
6588
|
var PROGRAM_NAME = "luam";
|
|
6589
6589
|
var PROGRAM_DESCRIPTION = "luam \u2014 the Luam compiler for Multi Theft Auto resources.";
|
|
6590
6590
|
|
|
@@ -13296,13 +13296,13 @@ function chooseCandidate(source, candidates3) {
|
|
|
13296
13296
|
return matching.length === 1 && best !== void 0 ? best : null;
|
|
13297
13297
|
}
|
|
13298
13298
|
function missingKeys(source, target) {
|
|
13299
|
-
const
|
|
13299
|
+
const missing2 = [];
|
|
13300
13300
|
for (const [name, member] of target.members) {
|
|
13301
13301
|
if (member.kind !== "optional" && !source.members.has(name)) {
|
|
13302
|
-
|
|
13302
|
+
missing2.push(`"${name}"`);
|
|
13303
13303
|
}
|
|
13304
13304
|
}
|
|
13305
|
-
return
|
|
13305
|
+
return missing2;
|
|
13306
13306
|
}
|
|
13307
13307
|
function missingKeyHint(source, target) {
|
|
13308
13308
|
if (source.kind !== "record" || source.isLiteral !== true) {
|
|
@@ -13314,11 +13314,11 @@ function missingKeyHint(source, target) {
|
|
|
13314
13314
|
if (candidate === null) {
|
|
13315
13315
|
return "";
|
|
13316
13316
|
}
|
|
13317
|
-
const
|
|
13318
|
-
if (
|
|
13317
|
+
const missing2 = missingKeys(source, candidate);
|
|
13318
|
+
if (missing2.length === 0) {
|
|
13319
13319
|
return "";
|
|
13320
13320
|
}
|
|
13321
|
-
const label2 =
|
|
13321
|
+
const label2 = missing2.length === 1 ? `Key ${missing2[0]} is` : `Keys ${missing2.join(", ")} are`;
|
|
13322
13322
|
return ` ${label2} missing from "${typeToString(candidate)}".`;
|
|
13323
13323
|
}
|
|
13324
13324
|
|
|
@@ -13458,17 +13458,24 @@ var CheckContext = class {
|
|
|
13458
13458
|
pushNarrowing(facts) {
|
|
13459
13459
|
this.narrowings.push(new Map(facts));
|
|
13460
13460
|
}
|
|
13461
|
+
get isNarrowed() {
|
|
13462
|
+
return this.narrowings.length > 0;
|
|
13463
|
+
}
|
|
13461
13464
|
popNarrowing() {
|
|
13462
13465
|
this.narrowings.pop();
|
|
13463
13466
|
}
|
|
13464
|
-
forgetNarrowing(
|
|
13467
|
+
forgetNarrowing(path) {
|
|
13465
13468
|
for (const frame of this.narrowings) {
|
|
13466
|
-
frame.
|
|
13469
|
+
for (const key of [...frame.keys()]) {
|
|
13470
|
+
if (key === path || key.startsWith(`${path}.`) || path.startsWith(`${key}.`)) {
|
|
13471
|
+
frame.delete(key);
|
|
13472
|
+
}
|
|
13473
|
+
}
|
|
13467
13474
|
}
|
|
13468
13475
|
}
|
|
13469
|
-
narrowedType(
|
|
13476
|
+
narrowedType(path) {
|
|
13470
13477
|
for (let index = this.narrowings.length - 1; index >= 0; index -= 1) {
|
|
13471
|
-
const found = this.narrowings[index]?.get(
|
|
13478
|
+
const found = this.narrowings[index]?.get(path);
|
|
13472
13479
|
if (found !== void 0) {
|
|
13473
13480
|
return found;
|
|
13474
13481
|
}
|
|
@@ -14272,6 +14279,139 @@ function expandClassDecorators(context, statement, fieldTypes) {
|
|
|
14272
14279
|
return generated;
|
|
14273
14280
|
}
|
|
14274
14281
|
|
|
14282
|
+
// ../compiler/src/checker/access-path.ts
|
|
14283
|
+
function pathOf2(expression) {
|
|
14284
|
+
if (expression.kind === "identifier") {
|
|
14285
|
+
return expression.name;
|
|
14286
|
+
}
|
|
14287
|
+
if (expression.kind !== "member-expression") {
|
|
14288
|
+
return null;
|
|
14289
|
+
}
|
|
14290
|
+
const object = pathOf2(expression.object);
|
|
14291
|
+
return object === null ? null : `${object}.${expression.property}`;
|
|
14292
|
+
}
|
|
14293
|
+
function assignedPath(target) {
|
|
14294
|
+
const direct = pathOf2(target);
|
|
14295
|
+
if (direct !== null) {
|
|
14296
|
+
return direct;
|
|
14297
|
+
}
|
|
14298
|
+
return target.kind === "member-expression" || target.kind === "index-expression" ? assignedPath(target.object) : null;
|
|
14299
|
+
}
|
|
14300
|
+
function pathType(context, expression) {
|
|
14301
|
+
if (expression.kind === "identifier") {
|
|
14302
|
+
return context.binder.lookup(expression.name)?.type ?? null;
|
|
14303
|
+
}
|
|
14304
|
+
return expression.kind === "member-expression" ? context.types.get(expression) ?? null : null;
|
|
14305
|
+
}
|
|
14306
|
+
function childExpressions2(expression) {
|
|
14307
|
+
switch (expression.kind) {
|
|
14308
|
+
case "member-expression":
|
|
14309
|
+
return [expression.object];
|
|
14310
|
+
case "index-expression":
|
|
14311
|
+
return [expression.object, expression.index];
|
|
14312
|
+
case "call-expression":
|
|
14313
|
+
return [expression.callee, ...expression.args];
|
|
14314
|
+
case "new-expression":
|
|
14315
|
+
return expression.args;
|
|
14316
|
+
case "table-expression":
|
|
14317
|
+
return expression.fields.flatMap((field2) => field2.key === null ? [field2.value] : [field2.key, field2.value]);
|
|
14318
|
+
case "binary-expression":
|
|
14319
|
+
return [expression.left, expression.right];
|
|
14320
|
+
case "unary-expression":
|
|
14321
|
+
return [expression.operand];
|
|
14322
|
+
case "group-expression":
|
|
14323
|
+
return [expression.expression];
|
|
14324
|
+
default:
|
|
14325
|
+
return [];
|
|
14326
|
+
}
|
|
14327
|
+
}
|
|
14328
|
+
function statementExpressions2(statement) {
|
|
14329
|
+
switch (statement.kind) {
|
|
14330
|
+
case "local-statement":
|
|
14331
|
+
case "return-statement":
|
|
14332
|
+
return statement.values;
|
|
14333
|
+
case "assignment-statement":
|
|
14334
|
+
return [...statement.targets, ...statement.values];
|
|
14335
|
+
case "call-statement":
|
|
14336
|
+
return [statement.expression];
|
|
14337
|
+
case "while-statement":
|
|
14338
|
+
case "repeat-statement":
|
|
14339
|
+
return [statement.condition];
|
|
14340
|
+
case "if-statement":
|
|
14341
|
+
return statement.clauses.map((clause) => clause.condition);
|
|
14342
|
+
case "numeric-for-statement":
|
|
14343
|
+
return statement.step === null ? [statement.start, statement.limit] : [statement.start, statement.limit, statement.step];
|
|
14344
|
+
case "generic-for-statement":
|
|
14345
|
+
return statement.iterators;
|
|
14346
|
+
case "class-declaration":
|
|
14347
|
+
return statement.members.flatMap((member) => member.kind === "class-field" && member.value !== null ? [member.value] : []);
|
|
14348
|
+
default:
|
|
14349
|
+
return [];
|
|
14350
|
+
}
|
|
14351
|
+
}
|
|
14352
|
+
function blockBodies(statement) {
|
|
14353
|
+
switch (statement.kind) {
|
|
14354
|
+
case "do-statement":
|
|
14355
|
+
case "while-statement":
|
|
14356
|
+
case "repeat-statement":
|
|
14357
|
+
case "numeric-for-statement":
|
|
14358
|
+
case "generic-for-statement":
|
|
14359
|
+
case "function-declaration":
|
|
14360
|
+
return [statement.body];
|
|
14361
|
+
case "if-statement":
|
|
14362
|
+
return [...statement.clauses.map((clause) => clause.body), ...statement.alternate === null ? [] : [statement.alternate]];
|
|
14363
|
+
case "class-declaration":
|
|
14364
|
+
return statement.members.filter((member) => member.kind === "class-method").map((member) => member.body);
|
|
14365
|
+
default:
|
|
14366
|
+
return [];
|
|
14367
|
+
}
|
|
14368
|
+
}
|
|
14369
|
+
function collectExpressionPaths(expression, paths) {
|
|
14370
|
+
if (expression.kind === "function-expression") {
|
|
14371
|
+
collectPaths(expression.body, paths);
|
|
14372
|
+
}
|
|
14373
|
+
for (const child of childExpressions2(expression)) {
|
|
14374
|
+
collectExpressionPaths(child, paths);
|
|
14375
|
+
}
|
|
14376
|
+
}
|
|
14377
|
+
function addTargets(statement, paths) {
|
|
14378
|
+
if (statement.kind === "assignment-statement") {
|
|
14379
|
+
for (const target of statement.targets) {
|
|
14380
|
+
const path = assignedPath(target);
|
|
14381
|
+
if (path !== null) {
|
|
14382
|
+
paths.add(path);
|
|
14383
|
+
}
|
|
14384
|
+
}
|
|
14385
|
+
}
|
|
14386
|
+
if (statement.kind === "local-statement") {
|
|
14387
|
+
statement.declarations.forEach((declaration) => paths.add(declaration.name));
|
|
14388
|
+
}
|
|
14389
|
+
}
|
|
14390
|
+
function collectPaths(body, paths) {
|
|
14391
|
+
for (const statement of body) {
|
|
14392
|
+
addTargets(statement, paths);
|
|
14393
|
+
for (const expression of statementExpressions2(statement)) {
|
|
14394
|
+
collectExpressionPaths(expression, paths);
|
|
14395
|
+
}
|
|
14396
|
+
for (const block of blockBodies(statement)) {
|
|
14397
|
+
collectPaths(block, paths);
|
|
14398
|
+
}
|
|
14399
|
+
}
|
|
14400
|
+
}
|
|
14401
|
+
function assignedPaths(body) {
|
|
14402
|
+
const paths = /* @__PURE__ */ new Set();
|
|
14403
|
+
collectPaths(body, paths);
|
|
14404
|
+
return paths;
|
|
14405
|
+
}
|
|
14406
|
+
function forgetAssignedPaths(context, body) {
|
|
14407
|
+
if (!context.isNarrowed) {
|
|
14408
|
+
return;
|
|
14409
|
+
}
|
|
14410
|
+
for (const path of assignedPaths(body)) {
|
|
14411
|
+
context.forgetNarrowing(path);
|
|
14412
|
+
}
|
|
14413
|
+
}
|
|
14414
|
+
|
|
14275
14415
|
// ../compiler/src/extensions/native-extensions.ts
|
|
14276
14416
|
var NATIVE_EXTENSIONS = [
|
|
14277
14417
|
{ receiver: "table", property: "count", target: "table.size", style: "property", result: "number", helper: "table" },
|
|
@@ -17934,19 +18074,19 @@ function resolveUnionMember(context, type, expression) {
|
|
|
17934
18074
|
return null;
|
|
17935
18075
|
}
|
|
17936
18076
|
const resolved2 = [];
|
|
17937
|
-
const
|
|
18077
|
+
const missing2 = [];
|
|
17938
18078
|
for (const option of type.options) {
|
|
17939
18079
|
const member = memberOf(context, option, expression.property);
|
|
17940
18080
|
if (member === null) {
|
|
17941
|
-
|
|
18081
|
+
missing2.push(`"${typeToString(option)}"`);
|
|
17942
18082
|
} else {
|
|
17943
18083
|
resolved2.push(member);
|
|
17944
18084
|
}
|
|
17945
18085
|
}
|
|
17946
|
-
if (
|
|
18086
|
+
if (missing2.length === 0) {
|
|
17947
18087
|
return createUnion(resolved2);
|
|
17948
18088
|
}
|
|
17949
|
-
const message = `"${expression.property}" is not a key of every member of "${typeToString(type)}". It is missing from ${
|
|
18089
|
+
const message = `"${expression.property}" is not a key of every member of "${typeToString(type)}". It is missing from ${missing2.join(", ")}.`;
|
|
17950
18090
|
context.report("check-unknown-union-key", message, expression.position);
|
|
17951
18091
|
return ANY_TYPE;
|
|
17952
18092
|
}
|
|
@@ -17962,18 +18102,19 @@ function literalValue(expression) {
|
|
|
17962
18102
|
return expression.kind === "number-literal" ? createNumberLiteral(expression.value) : null;
|
|
17963
18103
|
}
|
|
17964
18104
|
function discriminantTest(left, right) {
|
|
17965
|
-
if (left.kind !== "member-expression"
|
|
18105
|
+
if (left.kind !== "member-expression") {
|
|
17966
18106
|
return null;
|
|
17967
18107
|
}
|
|
18108
|
+
const path = pathOf2(left.object);
|
|
17968
18109
|
const value = literalValue(right);
|
|
17969
|
-
return value === null ? null : {
|
|
18110
|
+
return path === null || value === null ? null : { path, subject: left.object, property: left.property, value };
|
|
17970
18111
|
}
|
|
17971
|
-
function subjectType(context,
|
|
17972
|
-
const type = context.narrowedType(
|
|
18112
|
+
function subjectType(context, test) {
|
|
18113
|
+
const type = context.narrowedType(test.path) ?? pathType(context, test.subject);
|
|
17973
18114
|
return type === null ? null : withoutNil(type);
|
|
17974
18115
|
}
|
|
17975
18116
|
function filterOptions(context, test, keep) {
|
|
17976
|
-
const subject = subjectType(context, test
|
|
18117
|
+
const subject = subjectType(context, test);
|
|
17977
18118
|
if (subject === null || subject.kind !== "union") {
|
|
17978
18119
|
return null;
|
|
17979
18120
|
}
|
|
@@ -17995,7 +18136,7 @@ function discriminantFact(context, left, right, keep) {
|
|
|
17995
18136
|
return null;
|
|
17996
18137
|
}
|
|
17997
18138
|
const narrowed = filterOptions(context, test, keep);
|
|
17998
|
-
return narrowed === null ? null : [test.
|
|
18139
|
+
return narrowed === null ? null : [test.path, narrowed];
|
|
17999
18140
|
}
|
|
18000
18141
|
|
|
18001
18142
|
// ../compiler/src/checker/narrowing.ts
|
|
@@ -18010,38 +18151,42 @@ var GUARD_TYPES = {
|
|
|
18010
18151
|
thread: THREAD_TYPE,
|
|
18011
18152
|
userdata: USERDATA_TYPE
|
|
18012
18153
|
};
|
|
18013
|
-
function
|
|
18014
|
-
return context.binder.lookup(name)?.type ?? null;
|
|
18015
|
-
}
|
|
18016
|
-
function guardedName(expression) {
|
|
18154
|
+
function guardedPath(expression) {
|
|
18017
18155
|
if (expression.kind !== "call-expression" || expression.callee.kind !== "identifier" || expression.callee.name !== GUARD_FUNCTION) {
|
|
18018
18156
|
return null;
|
|
18019
18157
|
}
|
|
18020
18158
|
const [argument] = expression.args;
|
|
18021
|
-
return argument
|
|
18159
|
+
return argument === void 0 ? null : pathOf2(argument);
|
|
18022
18160
|
}
|
|
18023
18161
|
function guardFact(left, right) {
|
|
18024
|
-
const
|
|
18162
|
+
const path = guardedPath(left);
|
|
18025
18163
|
const value = right.kind === "string-literal" ? GUARD_TYPES[right.value] : void 0;
|
|
18026
|
-
return
|
|
18164
|
+
return path === null || value === void 0 ? null : [path, value];
|
|
18027
18165
|
}
|
|
18028
18166
|
function nilComparison(left, right) {
|
|
18029
|
-
if (
|
|
18030
|
-
return left
|
|
18167
|
+
if (right.kind === "nil-literal" && pathOf2(left) !== null) {
|
|
18168
|
+
return left;
|
|
18031
18169
|
}
|
|
18032
|
-
return
|
|
18170
|
+
return left.kind === "nil-literal" && pathOf2(right) !== null ? right : null;
|
|
18033
18171
|
}
|
|
18034
|
-
function present2(context,
|
|
18035
|
-
const
|
|
18036
|
-
|
|
18037
|
-
|
|
18172
|
+
function present2(context, expression, facts) {
|
|
18173
|
+
const path = pathOf2(expression);
|
|
18174
|
+
const declared = path === null ? null : pathType(context, expression);
|
|
18175
|
+
if (path !== null && declared !== null) {
|
|
18176
|
+
facts.set(path, withoutNil(declared));
|
|
18177
|
+
}
|
|
18178
|
+
}
|
|
18179
|
+
function missing(expression, facts) {
|
|
18180
|
+
const path = pathOf2(expression);
|
|
18181
|
+
if (path !== null) {
|
|
18182
|
+
facts.set(path, NIL_TYPE);
|
|
18038
18183
|
}
|
|
18039
18184
|
}
|
|
18040
18185
|
function mergeAlternatives(left, right, facts) {
|
|
18041
|
-
for (const [
|
|
18042
|
-
const other = right.get(
|
|
18186
|
+
for (const [path, type] of left) {
|
|
18187
|
+
const other = right.get(path);
|
|
18043
18188
|
if (other !== void 0) {
|
|
18044
|
-
facts.set(
|
|
18189
|
+
facts.set(path, createUnion([type, other]));
|
|
18045
18190
|
}
|
|
18046
18191
|
}
|
|
18047
18192
|
}
|
|
@@ -18056,8 +18201,8 @@ function collectFacts(context, expression, facts) {
|
|
|
18056
18201
|
collectFacts(context, expression.expression, facts);
|
|
18057
18202
|
return;
|
|
18058
18203
|
}
|
|
18059
|
-
if (expression.kind === "identifier") {
|
|
18060
|
-
present2(context, expression
|
|
18204
|
+
if (expression.kind === "identifier" || expression.kind === "member-expression") {
|
|
18205
|
+
present2(context, expression, facts);
|
|
18061
18206
|
return;
|
|
18062
18207
|
}
|
|
18063
18208
|
if (expression.kind !== "binary-expression") {
|
|
@@ -18074,20 +18219,20 @@ function collectFacts(context, expression, facts) {
|
|
|
18074
18219
|
}
|
|
18075
18220
|
if (expression.operator === "==") {
|
|
18076
18221
|
const guard = guardFact(expression.left, expression.right) ?? guardFact(expression.right, expression.left);
|
|
18077
|
-
const
|
|
18222
|
+
const absent = nilComparison(expression.left, expression.right);
|
|
18078
18223
|
if (guard !== null) {
|
|
18079
18224
|
facts.set(guard[0], guard[1]);
|
|
18080
|
-
} else if (
|
|
18081
|
-
|
|
18225
|
+
} else if (absent !== null) {
|
|
18226
|
+
missing(absent, facts);
|
|
18082
18227
|
} else {
|
|
18083
18228
|
applyDiscriminant(context, expression.left, expression.right, true, facts);
|
|
18084
18229
|
}
|
|
18085
18230
|
return;
|
|
18086
18231
|
}
|
|
18087
18232
|
if (expression.operator === "~=") {
|
|
18088
|
-
const
|
|
18089
|
-
if (
|
|
18090
|
-
present2(context,
|
|
18233
|
+
const tested = nilComparison(expression.left, expression.right);
|
|
18234
|
+
if (tested !== null) {
|
|
18235
|
+
present2(context, tested, facts);
|
|
18091
18236
|
} else {
|
|
18092
18237
|
applyDiscriminant(context, expression.left, expression.right, false, facts);
|
|
18093
18238
|
}
|
|
@@ -18121,15 +18266,15 @@ function guardFacts(context, statement) {
|
|
|
18121
18266
|
function negatedFacts(context, condition) {
|
|
18122
18267
|
const facts = /* @__PURE__ */ new Map();
|
|
18123
18268
|
if (condition.kind === "binary-expression" && condition.operator === "or") {
|
|
18124
|
-
for (const [
|
|
18125
|
-
facts.set(
|
|
18269
|
+
for (const [path, type] of [...negatedFacts(context, condition.left), ...negatedFacts(context, condition.right)]) {
|
|
18270
|
+
facts.set(path, type);
|
|
18126
18271
|
}
|
|
18127
18272
|
return facts;
|
|
18128
18273
|
}
|
|
18129
18274
|
if (condition.kind === "binary-expression" && condition.operator === "==") {
|
|
18130
|
-
const
|
|
18131
|
-
if (
|
|
18132
|
-
present2(context,
|
|
18275
|
+
const tested = nilComparison(condition.left, condition.right);
|
|
18276
|
+
if (tested !== null) {
|
|
18277
|
+
present2(context, tested, facts);
|
|
18133
18278
|
} else {
|
|
18134
18279
|
applyDiscriminant(context, condition.left, condition.right, false, facts);
|
|
18135
18280
|
}
|
|
@@ -18137,8 +18282,8 @@ function negatedFacts(context, condition) {
|
|
|
18137
18282
|
if (condition.kind === "binary-expression" && condition.operator === "~=") {
|
|
18138
18283
|
applyDiscriminant(context, condition.left, condition.right, true, facts);
|
|
18139
18284
|
}
|
|
18140
|
-
if (condition.kind === "unary-expression" && condition.operator === "not"
|
|
18141
|
-
present2(context, condition.operand
|
|
18285
|
+
if (condition.kind === "unary-expression" && condition.operator === "not") {
|
|
18286
|
+
present2(context, condition.operand, facts);
|
|
18142
18287
|
}
|
|
18143
18288
|
return facts;
|
|
18144
18289
|
}
|
|
@@ -18174,6 +18319,7 @@ function checkNumericFor(context, statement) {
|
|
|
18174
18319
|
context.report("check-invalid-operand", `Numeric "for" expects "number" but received "${typeToString(type)}".`, bound.position);
|
|
18175
18320
|
}
|
|
18176
18321
|
}
|
|
18322
|
+
forgetAssignedPaths(context, statement.body);
|
|
18177
18323
|
context.binder.pushScope();
|
|
18178
18324
|
context.binder.declare({ name: statement.variable.name, type: NUMBER_TYPE, isLocal: true, position: statement.variable.position, origin: "local" });
|
|
18179
18325
|
checkStatements(context, statement.body);
|
|
@@ -18197,6 +18343,7 @@ function iteratedTypes(context, iterators) {
|
|
|
18197
18343
|
function checkGenericFor(context, statement) {
|
|
18198
18344
|
statement.iterators.forEach((iterator) => checkExpression(context, iterator));
|
|
18199
18345
|
const iterated = iteratedTypes(context, statement.iterators);
|
|
18346
|
+
forgetAssignedPaths(context, statement.body);
|
|
18200
18347
|
context.binder.pushScope();
|
|
18201
18348
|
statement.variables.forEach((variable, index) => {
|
|
18202
18349
|
const inferred = iterated[index] ?? ANY_TYPE;
|
|
@@ -18269,6 +18416,7 @@ function buildFunctionType(context, parameters, returnAnnotation, contextual = n
|
|
|
18269
18416
|
return createFunction(types, context.resolveAnnotation(returnAnnotation), minimumArguments(context, parameters), isVariadic);
|
|
18270
18417
|
}
|
|
18271
18418
|
function checkFunctionBody(context, parameters, returnAnnotation, body, signature, selfType) {
|
|
18419
|
+
forgetAssignedPaths(context, body);
|
|
18272
18420
|
context.binder.pushScope();
|
|
18273
18421
|
context.pushReturnType(returnAnnotation === null ? null : context.resolveAnnotation(returnAnnotation));
|
|
18274
18422
|
if (selfType !== null) {
|
|
@@ -18327,6 +18475,10 @@ function checkCompoundOperand(context, operator, type, position2) {
|
|
|
18327
18475
|
function checkAssignment(context, statement) {
|
|
18328
18476
|
const valueTypes = checkValueList(context, statement.values);
|
|
18329
18477
|
statement.targets.forEach((target, index) => {
|
|
18478
|
+
const assigned = assignedPath(target);
|
|
18479
|
+
if (assigned !== null) {
|
|
18480
|
+
context.forgetNarrowing(assigned);
|
|
18481
|
+
}
|
|
18330
18482
|
const targetType = checkExpression(context, target);
|
|
18331
18483
|
const valueType = valueTypes[index] ?? NIL_TYPE;
|
|
18332
18484
|
const value = valueAt(statement.values, valueTypes, index);
|
|
@@ -18355,9 +18507,6 @@ function checkAssignment(context, statement) {
|
|
|
18355
18507
|
if (value !== void 0) {
|
|
18356
18508
|
context.expectAssignable(valueType, declared, value.position, "Assignment");
|
|
18357
18509
|
}
|
|
18358
|
-
if (target.kind === "identifier") {
|
|
18359
|
-
context.forgetNarrowing(target.name);
|
|
18360
|
-
}
|
|
18361
18510
|
});
|
|
18362
18511
|
}
|
|
18363
18512
|
function checkFunctionDeclaration(context, statement) {
|
|
@@ -18441,8 +18590,10 @@ function checkStatement(context, statement) {
|
|
|
18441
18590
|
return checkBlock(context, statement.body);
|
|
18442
18591
|
case "while-statement":
|
|
18443
18592
|
checkExpression(context, statement.condition);
|
|
18593
|
+
forgetAssignedPaths(context, statement.body);
|
|
18444
18594
|
return checkBlock(context, statement.body);
|
|
18445
18595
|
case "repeat-statement":
|
|
18596
|
+
forgetAssignedPaths(context, statement.body);
|
|
18446
18597
|
checkBlock(context, statement.body);
|
|
18447
18598
|
checkExpression(context, statement.condition);
|
|
18448
18599
|
return;
|
|
@@ -18501,20 +18652,12 @@ function parseInterpolation(segment) {
|
|
|
18501
18652
|
function collectInterpolations(segments) {
|
|
18502
18653
|
return segments.filter((segment) => segment.kind === "interpolation").map(parseInterpolation);
|
|
18503
18654
|
}
|
|
18504
|
-
function collectTemplateRoots(segments) {
|
|
18505
|
-
const roots = [];
|
|
18506
|
-
for (const interpolation of collectInterpolations(segments)) {
|
|
18507
|
-
if (interpolation.root.length > 0 && !roots.includes(interpolation.root)) {
|
|
18508
|
-
roots.push(interpolation.root);
|
|
18509
|
-
}
|
|
18510
|
-
}
|
|
18511
|
-
return roots;
|
|
18512
|
-
}
|
|
18513
18655
|
function templateLiteralText(segments) {
|
|
18514
18656
|
return segments.map((segment) => segment.kind === "text" ? segment.value : `\${${segment.value}}`).join("");
|
|
18515
18657
|
}
|
|
18516
18658
|
|
|
18517
18659
|
// ../compiler/src/checker/expressions.ts
|
|
18660
|
+
var NAME_PATH_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)*$/;
|
|
18518
18661
|
var EXTENSION_RESULTS = {
|
|
18519
18662
|
boolean: BOOLEAN_TYPE,
|
|
18520
18663
|
number: NUMBER_TYPE,
|
|
@@ -18528,7 +18671,16 @@ function isNativeConstruction(context, expression) {
|
|
|
18528
18671
|
}
|
|
18529
18672
|
return nativeConstructor(context, object.name) !== null;
|
|
18530
18673
|
}
|
|
18674
|
+
function narrowedMember(context, expression) {
|
|
18675
|
+
const path = pathOf2(expression);
|
|
18676
|
+
return path === null ? null : context.narrowedType(path);
|
|
18677
|
+
}
|
|
18531
18678
|
function checkMember(context, expression) {
|
|
18679
|
+
const narrowed = narrowedMember(context, expression);
|
|
18680
|
+
if (narrowed !== null) {
|
|
18681
|
+
checkExpression(context, expression.object);
|
|
18682
|
+
return context.record(expression, narrowed);
|
|
18683
|
+
}
|
|
18532
18684
|
if (expression.object.kind === "identifier" && isMtaClassReference(context, expression.object.name)) {
|
|
18533
18685
|
context.references.add(expression.object.name);
|
|
18534
18686
|
return context.record(expression, resolveMtaStaticMember(context, expression.object.name, expression.property, expression.position)?.type ?? ANY_TYPE);
|
|
@@ -18691,13 +18843,18 @@ function checkTable(context, expression) {
|
|
|
18691
18843
|
}
|
|
18692
18844
|
function checkTemplate(context, expression) {
|
|
18693
18845
|
for (const interpolation of collectInterpolations(expression.segments)) {
|
|
18694
|
-
if (interpolation.
|
|
18846
|
+
if (interpolation.path.length === 0) {
|
|
18695
18847
|
const message = 'Template interpolation is empty. Write "${name}", "${object.field}", or "${name:fallback}".';
|
|
18696
18848
|
context.report("check-empty-interpolation", message, interpolation.position);
|
|
18697
18849
|
continue;
|
|
18698
18850
|
}
|
|
18699
|
-
if (
|
|
18700
|
-
const message = `Template interpolation "${interpolation.path}"
|
|
18851
|
+
if (!NAME_PATH_PATTERN.test(interpolation.path)) {
|
|
18852
|
+
const message = `Template interpolation "${interpolation.path}" is not a name or a member path. Compute the value first, then interpolate the name.`;
|
|
18853
|
+
context.report("check-unknown-template-root", message, interpolation.position);
|
|
18854
|
+
continue;
|
|
18855
|
+
}
|
|
18856
|
+
if (interpolation.fallback === null && context.binder.lookup(interpolation.root) === null) {
|
|
18857
|
+
const message = `Template interpolation "${interpolation.path}" refers to "${interpolation.root}", which is not in scope. Write "\${${interpolation.path}:fallback}" to accept a value that may be missing.`;
|
|
18701
18858
|
context.report("check-unknown-template-root", message, interpolation.position);
|
|
18702
18859
|
}
|
|
18703
18860
|
}
|
|
@@ -19238,16 +19395,86 @@ function withSymbol(state, symbol, action) {
|
|
|
19238
19395
|
}
|
|
19239
19396
|
|
|
19240
19397
|
// ../compiler/src/emitter/template.ts
|
|
19398
|
+
var SELF_PATH_PATTERN = /^self((?:\.[A-Za-z_][A-Za-z0-9_]*)+)$/;
|
|
19399
|
+
function flattenedName(path) {
|
|
19400
|
+
const fields = SELF_PATH_PATTERN.exec(path)?.[1] ?? null;
|
|
19401
|
+
if (fields === null) {
|
|
19402
|
+
return null;
|
|
19403
|
+
}
|
|
19404
|
+
const name = fields.slice(1).split(".").join("_");
|
|
19405
|
+
return LUA_KEYWORDS.has(name) ? null : name;
|
|
19406
|
+
}
|
|
19407
|
+
function collectFlattened(segments) {
|
|
19408
|
+
const interpolations = collectInterpolations(segments);
|
|
19409
|
+
const reserved = /* @__PURE__ */ new Set();
|
|
19410
|
+
const claims = /* @__PURE__ */ new Map();
|
|
19411
|
+
const conflicting = /* @__PURE__ */ new Set();
|
|
19412
|
+
for (const interpolation of interpolations) {
|
|
19413
|
+
const name = flattenedName(interpolation.path);
|
|
19414
|
+
if (name === null) {
|
|
19415
|
+
reserved.add(interpolation.root);
|
|
19416
|
+
continue;
|
|
19417
|
+
}
|
|
19418
|
+
const claimed = claims.get(name) ?? null;
|
|
19419
|
+
if (claimed !== null && claimed !== interpolation.path) {
|
|
19420
|
+
conflicting.add(name);
|
|
19421
|
+
continue;
|
|
19422
|
+
}
|
|
19423
|
+
claims.set(name, interpolation.path);
|
|
19424
|
+
}
|
|
19425
|
+
const flattened = /* @__PURE__ */ new Map();
|
|
19426
|
+
for (const interpolation of interpolations) {
|
|
19427
|
+
const name = flattenedName(interpolation.path);
|
|
19428
|
+
if (name !== null && !reserved.has(name) && !conflicting.has(name)) {
|
|
19429
|
+
flattened.set(interpolation.raw, name);
|
|
19430
|
+
}
|
|
19431
|
+
}
|
|
19432
|
+
return flattened;
|
|
19433
|
+
}
|
|
19434
|
+
function rewriteInterpolation(raw, name) {
|
|
19435
|
+
const separator = raw.indexOf(":");
|
|
19436
|
+
const key = separator === -1 ? raw : raw.slice(0, separator);
|
|
19437
|
+
const rest = separator === -1 ? "" : raw.slice(separator);
|
|
19438
|
+
const path = key.trim();
|
|
19439
|
+
const start = key.indexOf(path);
|
|
19440
|
+
return `${key.slice(0, start)}${name}${key.slice(start + path.length)}${rest}`;
|
|
19441
|
+
}
|
|
19442
|
+
function flattenedLiteral(segments, flattened) {
|
|
19443
|
+
if (flattened.size === 0) {
|
|
19444
|
+
return templateLiteralText(segments);
|
|
19445
|
+
}
|
|
19446
|
+
return segments.map((segment) => {
|
|
19447
|
+
if (segment.kind === "text") {
|
|
19448
|
+
return segment.value;
|
|
19449
|
+
}
|
|
19450
|
+
const name = flattened.get(segment.value) ?? null;
|
|
19451
|
+
return `\${${name === null ? segment.value : rewriteInterpolation(segment.value, name)}}`;
|
|
19452
|
+
}).join("");
|
|
19453
|
+
}
|
|
19454
|
+
function collectBindings(segments, flattened) {
|
|
19455
|
+
const bindings = [];
|
|
19456
|
+
const seen = /* @__PURE__ */ new Set();
|
|
19457
|
+
for (const interpolation of collectInterpolations(segments)) {
|
|
19458
|
+
const flatName = flattened.get(interpolation.raw) ?? null;
|
|
19459
|
+
const name = flatName ?? interpolation.root;
|
|
19460
|
+
if (name.length === 0 || seen.has(name)) {
|
|
19461
|
+
continue;
|
|
19462
|
+
}
|
|
19463
|
+
seen.add(name);
|
|
19464
|
+
bindings.push({ name, value: flatName === null ? name : interpolation.path });
|
|
19465
|
+
}
|
|
19466
|
+
return bindings;
|
|
19467
|
+
}
|
|
19241
19468
|
function lowerTemplate(segments) {
|
|
19242
|
-
const
|
|
19469
|
+
const flattened = collectFlattened(segments);
|
|
19243
19470
|
const isInterpolated = segments.some((segment) => segment.kind === "interpolation");
|
|
19244
|
-
return { literal:
|
|
19471
|
+
return { literal: flattenedLiteral(segments, flattened), bindings: collectBindings(segments, flattened), isInterpolated };
|
|
19245
19472
|
}
|
|
19246
|
-
function templateContext(
|
|
19247
|
-
if (
|
|
19473
|
+
function templateContext(bindings) {
|
|
19474
|
+
if (bindings.length === 0) {
|
|
19248
19475
|
return "{}";
|
|
19249
19476
|
}
|
|
19250
|
-
return `{ ${
|
|
19477
|
+
return `{ ${bindings.map((binding) => `${binding.name} = ${binding.value}`).join(", ")} }`;
|
|
19251
19478
|
}
|
|
19252
19479
|
|
|
19253
19480
|
// ../compiler/src/emitter/expressions.ts
|
|
@@ -19320,7 +19547,7 @@ function emitTemplate(state, segments) {
|
|
|
19320
19547
|
return emitString(lowered.literal);
|
|
19321
19548
|
}
|
|
19322
19549
|
requireHelper(state, "string");
|
|
19323
|
-
return `string.template(${emitString(lowered.literal)}, ${templateContext(lowered.
|
|
19550
|
+
return `string.template(${emitString(lowered.literal)}, ${templateContext(lowered.bindings)})`;
|
|
19324
19551
|
}
|
|
19325
19552
|
function emitBinary(state, expression, limit) {
|
|
19326
19553
|
const precedence = binaryPrecedence(expression.operator);
|
|
@@ -19457,9 +19684,11 @@ function emitMembers(state, statement) {
|
|
|
19457
19684
|
entries2.push(indentLine(state, `${markSource(state, member.position.line, `${statement.name}:${member.name}`)}${emitMethod(state, statement.name, member)}`));
|
|
19458
19685
|
continue;
|
|
19459
19686
|
}
|
|
19460
|
-
if (member.
|
|
19461
|
-
|
|
19687
|
+
if (member.decorators.some((decorator) => decorator.name === "Lazy")) {
|
|
19688
|
+
continue;
|
|
19462
19689
|
}
|
|
19690
|
+
const value = member.value === null ? "nil" : emitExpression(state, member.value);
|
|
19691
|
+
entries2.push(indentLine(state, `${markSource(state, member.position.line, statement.name)}${member.name} = ${value}`));
|
|
19463
19692
|
}
|
|
19464
19693
|
for (const generated of state.generatedMembers.get(statement) ?? []) {
|
|
19465
19694
|
entries2.push(indentLine(state, `${markSource(state, generated.position.line, `${statement.name}:${generated.name}`)}${emitMethod(state, statement.name, generated)}`));
|
|
@@ -19954,11 +20183,11 @@ function canonicalEdit(input, statement, span) {
|
|
|
19954
20183
|
}
|
|
19955
20184
|
const authored = source.slice(span.start, span.end);
|
|
19956
20185
|
const replacement = reindent(trimmed, indentOf(source, span.start));
|
|
19957
|
-
const
|
|
19958
|
-
if (
|
|
20186
|
+
const missing2 = lineCount2(authored) - lineCount2(replacement);
|
|
20187
|
+
if (missing2 < 0 && input.development) {
|
|
19959
20188
|
return null;
|
|
19960
20189
|
}
|
|
19961
|
-
const padded =
|
|
20190
|
+
const padded = missing2 > 0 && source.slice(span.end).trim().length > 0 ? `${replacement}${"\n".repeat(missing2)}` : replacement;
|
|
19962
20191
|
return { start: span.start, end: span.end, replacement: padded, lines: emitted.lines };
|
|
19963
20192
|
}
|
|
19964
20193
|
|