@thigasdevelopment/luam 0.9.0 → 0.10.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 +199 -5
- package/package.json +1 -1
package/luam.mjs
CHANGED
|
@@ -3534,6 +3534,7 @@ var SYMBOLS = [
|
|
|
3534
3534
|
">",
|
|
3535
3535
|
"=",
|
|
3536
3536
|
"|",
|
|
3537
|
+
"&",
|
|
3537
3538
|
"?",
|
|
3538
3539
|
"@",
|
|
3539
3540
|
"(",
|
|
@@ -3809,14 +3810,25 @@ function parsePostfixType(stream) {
|
|
|
3809
3810
|
return annotation;
|
|
3810
3811
|
}
|
|
3811
3812
|
}
|
|
3812
|
-
function
|
|
3813
|
+
function parseIntersectionType(stream) {
|
|
3813
3814
|
const first = parsePostfixType(stream);
|
|
3815
|
+
if (!stream.check("operator", "&")) {
|
|
3816
|
+
return first;
|
|
3817
|
+
}
|
|
3818
|
+
const parts = [first];
|
|
3819
|
+
while (stream.match("operator", "&")) {
|
|
3820
|
+
parts.push(parsePostfixType(stream));
|
|
3821
|
+
}
|
|
3822
|
+
return { kind: "type-intersection", parts, position: first.position };
|
|
3823
|
+
}
|
|
3824
|
+
function parseTypeAnnotation(stream) {
|
|
3825
|
+
const first = parseIntersectionType(stream);
|
|
3814
3826
|
if (!stream.check("operator", "|")) {
|
|
3815
3827
|
return first;
|
|
3816
3828
|
}
|
|
3817
3829
|
const options = [first];
|
|
3818
3830
|
while (stream.match("operator", "|")) {
|
|
3819
|
-
options.push(
|
|
3831
|
+
options.push(parseIntersectionType(stream));
|
|
3820
3832
|
}
|
|
3821
3833
|
return { kind: "type-union", options, position: first.position };
|
|
3822
3834
|
}
|
|
@@ -5987,7 +5999,7 @@ import { tmpdir } from "node:os";
|
|
|
5987
5999
|
import { join as join2 } from "node:path";
|
|
5988
6000
|
|
|
5989
6001
|
// src/cli/version.ts
|
|
5990
|
-
var VERSION = true ? "0.
|
|
6002
|
+
var VERSION = true ? "0.10.0" : "0.0.0-dev";
|
|
5991
6003
|
var PROGRAM_NAME = "luam";
|
|
5992
6004
|
var PROGRAM_DESCRIPTION = "luam \u2014 the Luam compiler for Multi Theft Auto resources.";
|
|
5993
6005
|
|
|
@@ -11021,6 +11033,41 @@ function mtaMember(className, member) {
|
|
|
11021
11033
|
return mtaClassRegistry().lookupMember(className, member);
|
|
11022
11034
|
}
|
|
11023
11035
|
|
|
11036
|
+
// ../compiler/src/checker/type-intersection.ts
|
|
11037
|
+
function namedMembers(context, name) {
|
|
11038
|
+
if (context.declarations.lookupInterface(name) === null && context.declarations.lookupClass(name) === null) {
|
|
11039
|
+
return null;
|
|
11040
|
+
}
|
|
11041
|
+
return new Map(context.declarations.collectMembers(name).map((member) => [member.name, member.type]));
|
|
11042
|
+
}
|
|
11043
|
+
function partMembers(context, part) {
|
|
11044
|
+
if (part.kind === "record") {
|
|
11045
|
+
return part.members;
|
|
11046
|
+
}
|
|
11047
|
+
return part.kind === "named" ? namedMembers(context, part.name) : null;
|
|
11048
|
+
}
|
|
11049
|
+
function mergeIntersection(context, parts, position2) {
|
|
11050
|
+
const members = /* @__PURE__ */ new Map();
|
|
11051
|
+
for (const part of parts) {
|
|
11052
|
+
const collected = partMembers(context, part);
|
|
11053
|
+
if (collected === null) {
|
|
11054
|
+
const message = `"${typeToString(part)}" is not an object type, so it cannot take part in an intersection.`;
|
|
11055
|
+
context.report("check-invalid-intersection", message, position2);
|
|
11056
|
+
continue;
|
|
11057
|
+
}
|
|
11058
|
+
for (const [name, type] of collected) {
|
|
11059
|
+
const existing = members.get(name);
|
|
11060
|
+
if (existing !== void 0 && typeToString(existing) !== typeToString(type)) {
|
|
11061
|
+
const message = `Intersection declares "${name}" as both "${typeToString(existing)}" and "${typeToString(type)}".`;
|
|
11062
|
+
context.report("check-conflicting-intersection-member", message, position2);
|
|
11063
|
+
continue;
|
|
11064
|
+
}
|
|
11065
|
+
members.set(name, type);
|
|
11066
|
+
}
|
|
11067
|
+
}
|
|
11068
|
+
return createObjectType(members);
|
|
11069
|
+
}
|
|
11070
|
+
|
|
11024
11071
|
// ../compiler/src/checker/type-substitution.ts
|
|
11025
11072
|
function substituteType(type, substitutions) {
|
|
11026
11073
|
if (type.kind === "named") {
|
|
@@ -11085,6 +11132,8 @@ var CheckContext = class {
|
|
|
11085
11132
|
references = /* @__PURE__ */ new Set();
|
|
11086
11133
|
declaredGlobals = /* @__PURE__ */ new Map();
|
|
11087
11134
|
externalReferences = /* @__PURE__ */ new Map();
|
|
11135
|
+
unknownTypes = /* @__PURE__ */ new Map();
|
|
11136
|
+
typeParameters = /* @__PURE__ */ new Set();
|
|
11088
11137
|
generatedMembers = /* @__PURE__ */ new Map();
|
|
11089
11138
|
mode;
|
|
11090
11139
|
environment;
|
|
@@ -11145,6 +11194,12 @@ var CheckContext = class {
|
|
|
11145
11194
|
}
|
|
11146
11195
|
}
|
|
11147
11196
|
report(code, message, position2) {
|
|
11197
|
+
this.push(code, message, position2, "error");
|
|
11198
|
+
}
|
|
11199
|
+
warn(code, message, position2) {
|
|
11200
|
+
this.push(code, message, position2, "warning");
|
|
11201
|
+
}
|
|
11202
|
+
push(code, message, position2, severity) {
|
|
11148
11203
|
if (this.mode === "nocheck") {
|
|
11149
11204
|
return;
|
|
11150
11205
|
}
|
|
@@ -11153,7 +11208,20 @@ var CheckContext = class {
|
|
|
11153
11208
|
return;
|
|
11154
11209
|
}
|
|
11155
11210
|
this.reported.add(key);
|
|
11156
|
-
this.diagnostics.push(createDiagnostic("checker", code, message, position2));
|
|
11211
|
+
this.diagnostics.push(createDiagnostic("checker", code, message, position2, severity));
|
|
11212
|
+
}
|
|
11213
|
+
noteTypeParameters(names) {
|
|
11214
|
+
for (const name of names) {
|
|
11215
|
+
this.typeParameters.add(name);
|
|
11216
|
+
}
|
|
11217
|
+
}
|
|
11218
|
+
isTypeParameter(name) {
|
|
11219
|
+
return this.typeParameters.has(name);
|
|
11220
|
+
}
|
|
11221
|
+
noteUnknownType(name, position2) {
|
|
11222
|
+
if (!this.unknownTypes.has(name)) {
|
|
11223
|
+
this.unknownTypes.set(name, position2);
|
|
11224
|
+
}
|
|
11157
11225
|
}
|
|
11158
11226
|
record(expression, type) {
|
|
11159
11227
|
this.types.set(expression, type);
|
|
@@ -11212,6 +11280,10 @@ var CheckContext = class {
|
|
|
11212
11280
|
if (annotation.kind === "type-union") {
|
|
11213
11281
|
return createUnion(annotation.options.map((option) => this.resolveAnnotation(option)));
|
|
11214
11282
|
}
|
|
11283
|
+
if (annotation.kind === "type-intersection") {
|
|
11284
|
+
const parts = annotation.parts.map((part) => this.resolveAnnotation(part));
|
|
11285
|
+
return mergeIntersection(this, parts, annotation.position);
|
|
11286
|
+
}
|
|
11215
11287
|
if (annotation.kind === "type-string-literal") {
|
|
11216
11288
|
return createStringLiteral(annotation.value);
|
|
11217
11289
|
}
|
|
@@ -11293,6 +11365,7 @@ var CheckContext = class {
|
|
|
11293
11365
|
}
|
|
11294
11366
|
const alias = this.binder.lookupAlias(name);
|
|
11295
11367
|
if (alias === null) {
|
|
11368
|
+
this.noteUnknownType(name, position2);
|
|
11296
11369
|
return createNamed(name);
|
|
11297
11370
|
}
|
|
11298
11371
|
if (typeArguments.length !== alias.parameters.length) {
|
|
@@ -12304,6 +12377,47 @@ function templateLiteralText(segments) {
|
|
|
12304
12377
|
return segments.map((segment) => segment.kind === "text" ? segment.value : `\${${segment.value}}`).join("");
|
|
12305
12378
|
}
|
|
12306
12379
|
|
|
12380
|
+
// ../compiler/src/checker/union-members.ts
|
|
12381
|
+
function isShapeOption(context, option) {
|
|
12382
|
+
if (option.kind === "record") {
|
|
12383
|
+
return true;
|
|
12384
|
+
}
|
|
12385
|
+
if (option.kind !== "named") {
|
|
12386
|
+
return false;
|
|
12387
|
+
}
|
|
12388
|
+
return context.declarations.lookupInterface(option.name) !== null || context.declarations.lookupClass(option.name) !== null;
|
|
12389
|
+
}
|
|
12390
|
+
function memberOf(context, option, property) {
|
|
12391
|
+
if (option.kind === "record") {
|
|
12392
|
+
return option.members.get(property) ?? null;
|
|
12393
|
+
}
|
|
12394
|
+
return option.kind === "named" ? context.declarations.lookupMember(option.name, property)?.type ?? null : null;
|
|
12395
|
+
}
|
|
12396
|
+
function optionMemberType(context, option, property) {
|
|
12397
|
+
return isShapeOption(context, option) ? memberOf(context, option, property) : null;
|
|
12398
|
+
}
|
|
12399
|
+
function resolveUnionMember(context, type, expression) {
|
|
12400
|
+
if (!type.options.every((option) => isShapeOption(context, option))) {
|
|
12401
|
+
return null;
|
|
12402
|
+
}
|
|
12403
|
+
const resolved2 = [];
|
|
12404
|
+
const missing = [];
|
|
12405
|
+
for (const option of type.options) {
|
|
12406
|
+
const member = memberOf(context, option, expression.property);
|
|
12407
|
+
if (member === null) {
|
|
12408
|
+
missing.push(`"${typeToString(option)}"`);
|
|
12409
|
+
} else {
|
|
12410
|
+
resolved2.push(member);
|
|
12411
|
+
}
|
|
12412
|
+
}
|
|
12413
|
+
if (missing.length === 0) {
|
|
12414
|
+
return createUnion(resolved2);
|
|
12415
|
+
}
|
|
12416
|
+
const message = `"${expression.property}" is not a key of every member of "${typeToString(type)}". It is missing from ${missing.join(", ")}.`;
|
|
12417
|
+
context.report("check-unknown-union-key", message, expression.position);
|
|
12418
|
+
return ANY_TYPE;
|
|
12419
|
+
}
|
|
12420
|
+
|
|
12307
12421
|
// ../compiler/src/checker/expressions.ts
|
|
12308
12422
|
var EXTENSION_RESULTS = {
|
|
12309
12423
|
boolean: BOOLEAN_TYPE,
|
|
@@ -12346,6 +12460,12 @@ function checkMember(context, expression) {
|
|
|
12346
12460
|
if (objectType.kind === "record") {
|
|
12347
12461
|
return context.record(expression, resolveRecordMember(context, objectType, expression));
|
|
12348
12462
|
}
|
|
12463
|
+
if (objectType.kind === "union") {
|
|
12464
|
+
const option = resolveUnionMember(context, objectType, expression);
|
|
12465
|
+
if (option !== null) {
|
|
12466
|
+
return context.record(expression, option);
|
|
12467
|
+
}
|
|
12468
|
+
}
|
|
12349
12469
|
if (objectType.kind === "map" && isAssignable(STRING_TYPE, objectType.key)) {
|
|
12350
12470
|
return context.record(expression, objectType.value);
|
|
12351
12471
|
}
|
|
@@ -12702,6 +12822,43 @@ function checkEnumDeclaration(context, statement) {
|
|
|
12702
12822
|
context.declareModuleGlobal({ name: statement.name, type: createNamed(statement.name), isLocal: false, position: statement.position });
|
|
12703
12823
|
}
|
|
12704
12824
|
|
|
12825
|
+
// ../compiler/src/checker/discriminant.ts
|
|
12826
|
+
function discriminantTest(left, right) {
|
|
12827
|
+
if (left.kind !== "member-expression" || left.object.kind !== "identifier" || right.kind !== "string-literal") {
|
|
12828
|
+
return null;
|
|
12829
|
+
}
|
|
12830
|
+
return { name: left.object.name, property: left.property, value: createStringLiteral(right.value) };
|
|
12831
|
+
}
|
|
12832
|
+
function subjectType(context, name) {
|
|
12833
|
+
const type = context.narrowedType(name) ?? context.binder.lookup(name)?.type ?? null;
|
|
12834
|
+
return type === null ? null : withoutNil(type);
|
|
12835
|
+
}
|
|
12836
|
+
function filterOptions(context, test, keep) {
|
|
12837
|
+
const subject = subjectType(context, test.name);
|
|
12838
|
+
if (subject === null || subject.kind !== "union") {
|
|
12839
|
+
return null;
|
|
12840
|
+
}
|
|
12841
|
+
const matched = [];
|
|
12842
|
+
for (const option of subject.options) {
|
|
12843
|
+
const member = optionMemberType(context, option, test.property);
|
|
12844
|
+
if (member === null) {
|
|
12845
|
+
return null;
|
|
12846
|
+
}
|
|
12847
|
+
if (isAssignable(test.value, member) === keep) {
|
|
12848
|
+
matched.push(option);
|
|
12849
|
+
}
|
|
12850
|
+
}
|
|
12851
|
+
return matched.length === 0 || matched.length === subject.options.length ? null : createUnion(matched);
|
|
12852
|
+
}
|
|
12853
|
+
function discriminantFact(context, left, right, keep) {
|
|
12854
|
+
const test = discriminantTest(left, right) ?? discriminantTest(right, left);
|
|
12855
|
+
if (test === null) {
|
|
12856
|
+
return null;
|
|
12857
|
+
}
|
|
12858
|
+
const narrowed = filterOptions(context, test, keep);
|
|
12859
|
+
return narrowed === null ? null : [test.name, narrowed];
|
|
12860
|
+
}
|
|
12861
|
+
|
|
12705
12862
|
// ../compiler/src/checker/narrowing.ts
|
|
12706
12863
|
var GUARD_FUNCTION = "type";
|
|
12707
12864
|
var GUARD_TYPES = {
|
|
@@ -12749,6 +12906,12 @@ function mergeAlternatives(left, right, facts) {
|
|
|
12749
12906
|
}
|
|
12750
12907
|
}
|
|
12751
12908
|
}
|
|
12909
|
+
function applyDiscriminant(context, left, right, keep, facts) {
|
|
12910
|
+
const fact = discriminantFact(context, left, right, keep);
|
|
12911
|
+
if (fact !== null) {
|
|
12912
|
+
facts.set(fact[0], fact[1]);
|
|
12913
|
+
}
|
|
12914
|
+
}
|
|
12752
12915
|
function collectFacts(context, expression, facts) {
|
|
12753
12916
|
if (expression.kind === "group-expression") {
|
|
12754
12917
|
collectFacts(context, expression.expression, facts);
|
|
@@ -12777,6 +12940,8 @@ function collectFacts(context, expression, facts) {
|
|
|
12777
12940
|
facts.set(guard[0], guard[1]);
|
|
12778
12941
|
} else if (missing !== null) {
|
|
12779
12942
|
facts.set(missing, NIL_TYPE);
|
|
12943
|
+
} else {
|
|
12944
|
+
applyDiscriminant(context, expression.left, expression.right, true, facts);
|
|
12780
12945
|
}
|
|
12781
12946
|
return;
|
|
12782
12947
|
}
|
|
@@ -12784,6 +12949,8 @@ function collectFacts(context, expression, facts) {
|
|
|
12784
12949
|
const name = nilComparison(expression.left, expression.right);
|
|
12785
12950
|
if (name !== null) {
|
|
12786
12951
|
present2(context, name, facts);
|
|
12952
|
+
} else {
|
|
12953
|
+
applyDiscriminant(context, expression.left, expression.right, false, facts);
|
|
12787
12954
|
}
|
|
12788
12955
|
}
|
|
12789
12956
|
}
|
|
@@ -12824,8 +12991,13 @@ function negatedFacts(context, condition) {
|
|
|
12824
12991
|
const name = nilComparison(condition.left, condition.right);
|
|
12825
12992
|
if (name !== null) {
|
|
12826
12993
|
present2(context, name, facts);
|
|
12994
|
+
} else {
|
|
12995
|
+
applyDiscriminant(context, condition.left, condition.right, false, facts);
|
|
12827
12996
|
}
|
|
12828
12997
|
}
|
|
12998
|
+
if (condition.kind === "binary-expression" && condition.operator === "~=") {
|
|
12999
|
+
applyDiscriminant(context, condition.left, condition.right, true, facts);
|
|
13000
|
+
}
|
|
12829
13001
|
if (condition.kind === "unary-expression" && condition.operator === "not" && condition.operand.kind === "identifier") {
|
|
12830
13002
|
present2(context, condition.operand.name, facts);
|
|
12831
13003
|
}
|
|
@@ -13082,7 +13254,9 @@ function checkStatement(context, statement) {
|
|
|
13082
13254
|
return checkGenericFor(context, statement);
|
|
13083
13255
|
case "type-alias-statement": {
|
|
13084
13256
|
const resolved2 = context.resolveAnnotation(statement.annotation);
|
|
13085
|
-
const
|
|
13257
|
+
const shape = statement.annotation.kind === "type-object" || statement.annotation.kind === "type-intersection";
|
|
13258
|
+
const named2 = shape ? renameRecord(resolved2, statement.name) : resolved2;
|
|
13259
|
+
context.noteTypeParameters(statement.typeParameters);
|
|
13086
13260
|
context.binder.declareAlias(statement.name, named2, statement.typeParameters);
|
|
13087
13261
|
return;
|
|
13088
13262
|
}
|
|
@@ -13114,6 +13288,25 @@ function checkStatements(context, statements) {
|
|
|
13114
13288
|
}
|
|
13115
13289
|
}
|
|
13116
13290
|
|
|
13291
|
+
// ../compiler/src/checker/type-names.ts
|
|
13292
|
+
function isDeclaredType(context, name) {
|
|
13293
|
+
const declarations = context.declarations;
|
|
13294
|
+
return declarations.lookupClass(name) !== null || declarations.lookupInterface(name) !== null || declarations.lookupEnum(name) !== null;
|
|
13295
|
+
}
|
|
13296
|
+
function isKnownTypeName(context, name) {
|
|
13297
|
+
if (context.binder.lookupAlias(name) !== null || context.isTypeParameter(name)) {
|
|
13298
|
+
return true;
|
|
13299
|
+
}
|
|
13300
|
+
return isDeclaredType(context, name) || isMtaClass(name);
|
|
13301
|
+
}
|
|
13302
|
+
function reportUnknownTypes(context) {
|
|
13303
|
+
for (const [name, position2] of context.unknownTypes) {
|
|
13304
|
+
if (!isKnownTypeName(context, name)) {
|
|
13305
|
+
context.warn("check-unknown-type", `Type "${name}" is not defined.`, position2);
|
|
13306
|
+
}
|
|
13307
|
+
}
|
|
13308
|
+
}
|
|
13309
|
+
|
|
13117
13310
|
// ../compiler/src/checker/checker.ts
|
|
13118
13311
|
function externalReferences(context) {
|
|
13119
13312
|
const external = /* @__PURE__ */ new Map();
|
|
@@ -13132,6 +13325,7 @@ function check(program2, mode, environment = DEFAULT_ENVIRONMENT, options = {})
|
|
|
13132
13325
|
const structure = context.isDeclarationFile ? checkDeclarationFile(program2.body) : checkJumps(program2.body);
|
|
13133
13326
|
const directives = collectDirectives(program2.body, { isDeclarationFile: context.isDeclarationFile });
|
|
13134
13327
|
checkStatements(context, program2.body);
|
|
13328
|
+
reportUnknownTypes(context);
|
|
13135
13329
|
return {
|
|
13136
13330
|
diagnostics: sortDiagnostics([...structure, ...directives.diagnostics, ...context.diagnostics]),
|
|
13137
13331
|
types: context.types,
|