@thigasdevelopment/luam 0.2.1 → 0.4.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/lua/async.lua +118 -118
- package/luam.mjs +239 -29
- package/package.json +1 -1
- package/template/README.md +0 -120
- package/template/config.lua +0 -14
- package/template/env +0 -11
- package/template/framework/bootstrap-client.luam +0 -9
- package/template/framework/bootstrap-server.luam +0 -9
- package/template/framework/command.luam +0 -54
- package/template/framework/core.luam +0 -58
- package/template/framework/event.luam +0 -39
- package/template/framework/listener.luam +0 -40
- package/template/framework/loader.luam +0 -73
- package/template/framework/thread-pool.luam +0 -27
- package/template/gitignore +0 -7
- package/template/src/client/commands/ping-command.luam +0 -8
- package/template/src/client/main.luam +0 -5
- package/template/src/server/commands/hello-command.luam +0 -9
- package/template/src/server/handlers/player-join-listener.luam +0 -9
- package/template/src/server/main.luam +0 -11
- package/template/src/shared/config.luam +0 -5
- package/template/src/shared/framework/command.luam +0 -54
- package/template/src/shared/framework/core.luam +0 -58
- package/template/src/shared/framework/event.luam +0 -39
- package/template/src/shared/framework/listener.luam +0 -40
- package/template/src/shared/framework/loader.luam +0 -73
- package/template/src/shared/framework/thread-pool.luam +0 -27
package/luam.mjs
CHANGED
|
@@ -212,7 +212,7 @@ var EXIT_DIAGNOSTICS = 1;
|
|
|
212
212
|
var EXIT_USAGE = 2;
|
|
213
213
|
|
|
214
214
|
// src/cli/usage.ts
|
|
215
|
-
var VERSION = true ? "0.
|
|
215
|
+
var VERSION = true ? "0.4.0" : "0.0.0-dev";
|
|
216
216
|
var USAGE = [
|
|
217
217
|
"luam \u2014 the Luam compiler for Multi Theft Auto resources.",
|
|
218
218
|
"",
|
|
@@ -916,6 +916,15 @@ function createNamed(name) {
|
|
|
916
916
|
function createRecord(name, members, origin = null) {
|
|
917
917
|
return { kind: "record", name, origin, members };
|
|
918
918
|
}
|
|
919
|
+
function renameRecord(type, name) {
|
|
920
|
+
return type.kind === "record" ? createRecord(name, type.members, type.origin) : type;
|
|
921
|
+
}
|
|
922
|
+
function createObjectType(members) {
|
|
923
|
+
const keys = [...members].map(
|
|
924
|
+
([name, member]) => member.kind === "optional" ? `${name}?: ${typeToString(member.element)}` : `${name}: ${typeToString(member)}`
|
|
925
|
+
);
|
|
926
|
+
return createRecord(keys.length === 0 ? "{}" : `{ ${keys.join(", ")} }`, members);
|
|
927
|
+
}
|
|
919
928
|
function createFunction(parameters, returnType, minimumArguments2, isVariadic = false) {
|
|
920
929
|
return { kind: "function", parameters, returnType, minimumArguments: minimumArguments2 ?? parameters.length, isVariadic };
|
|
921
930
|
}
|
|
@@ -1022,6 +1031,21 @@ function isFunctionAssignable(source, target, options) {
|
|
|
1022
1031
|
}
|
|
1023
1032
|
return target.returnType.kind === "void" || isAssignable(source.returnType, target.returnType, options);
|
|
1024
1033
|
}
|
|
1034
|
+
function isRecordAssignable(source, target, options) {
|
|
1035
|
+
for (const [name, member] of target.members) {
|
|
1036
|
+
const value = source.members.get(name);
|
|
1037
|
+
if (value === void 0) {
|
|
1038
|
+
if (member.kind !== "optional") {
|
|
1039
|
+
return false;
|
|
1040
|
+
}
|
|
1041
|
+
continue;
|
|
1042
|
+
}
|
|
1043
|
+
if (!isAssignable(value, member, options)) {
|
|
1044
|
+
return false;
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
return true;
|
|
1048
|
+
}
|
|
1025
1049
|
function isAssignable(source, target, options = { allowNil: false }) {
|
|
1026
1050
|
if (source.kind === "any" || target.kind === "any" || target.kind === "unknown") {
|
|
1027
1051
|
return true;
|
|
@@ -1053,6 +1077,9 @@ function isAssignable(source, target, options = { allowNil: false }) {
|
|
|
1053
1077
|
if (target.kind === "table") {
|
|
1054
1078
|
return isTableLike(source) || source.kind === "record";
|
|
1055
1079
|
}
|
|
1080
|
+
if (target.kind === "record") {
|
|
1081
|
+
return source.kind === "record" ? isRecordAssignable(source, target, options) : source.kind === "table";
|
|
1082
|
+
}
|
|
1056
1083
|
if (target.kind === "array") {
|
|
1057
1084
|
return source.kind === "table" || source.kind === "array" && isAssignable(source.element, target.element, options);
|
|
1058
1085
|
}
|
|
@@ -4831,6 +4858,14 @@ var DeclarationRegistry = class {
|
|
|
4831
4858
|
lookupInterface(name) {
|
|
4832
4859
|
return this.interfaces.get(name) ?? null;
|
|
4833
4860
|
}
|
|
4861
|
+
interfaceExtends(name, target, visited = /* @__PURE__ */ new Set()) {
|
|
4862
|
+
const current = this.lookupInterface(name);
|
|
4863
|
+
if (current === null || visited.has(name)) {
|
|
4864
|
+
return false;
|
|
4865
|
+
}
|
|
4866
|
+
visited.add(name);
|
|
4867
|
+
return current.superInterfaces.some((parent) => parent === target || this.interfaceExtends(parent, target, visited));
|
|
4868
|
+
}
|
|
4834
4869
|
declareEnum(info) {
|
|
4835
4870
|
this.enums.set(info.name, info);
|
|
4836
4871
|
}
|
|
@@ -4846,24 +4881,42 @@ var DeclarationRegistry = class {
|
|
|
4846
4881
|
allEnums() {
|
|
4847
4882
|
return [...this.enums.values()];
|
|
4848
4883
|
}
|
|
4849
|
-
|
|
4884
|
+
collectInterfaceMembers(name, collected, visited) {
|
|
4885
|
+
const current = this.lookupInterface(name);
|
|
4886
|
+
if (current === null || visited.has(current.name)) {
|
|
4887
|
+
return;
|
|
4888
|
+
}
|
|
4889
|
+
visited.add(current.name);
|
|
4890
|
+
for (const [memberName, member] of current.members) {
|
|
4891
|
+
if (!collected.has(memberName)) {
|
|
4892
|
+
collected.set(memberName, member);
|
|
4893
|
+
}
|
|
4894
|
+
}
|
|
4895
|
+
for (const parent of current.superInterfaces) {
|
|
4896
|
+
this.collectInterfaceMembers(parent, collected, visited);
|
|
4897
|
+
}
|
|
4898
|
+
}
|
|
4899
|
+
collectMembers(name) {
|
|
4850
4900
|
const collected = /* @__PURE__ */ new Map();
|
|
4851
4901
|
const visited = /* @__PURE__ */ new Set();
|
|
4852
|
-
let current = this.lookupClass(
|
|
4902
|
+
let current = this.lookupClass(name);
|
|
4853
4903
|
while (current !== null && !visited.has(current.name)) {
|
|
4854
|
-
for (const [
|
|
4855
|
-
if (!collected.has(
|
|
4856
|
-
collected.set(
|
|
4904
|
+
for (const [name2, member] of current.members) {
|
|
4905
|
+
if (!collected.has(name2)) {
|
|
4906
|
+
collected.set(name2, member);
|
|
4857
4907
|
}
|
|
4858
4908
|
}
|
|
4859
4909
|
visited.add(current.name);
|
|
4860
4910
|
current = current.superClass === null ? null : this.lookupClass(current.superClass);
|
|
4861
4911
|
}
|
|
4912
|
+
if (current === null && this.lookupClass(name) === null) {
|
|
4913
|
+
this.collectInterfaceMembers(name, collected, visited);
|
|
4914
|
+
}
|
|
4862
4915
|
return [...collected.values()];
|
|
4863
4916
|
}
|
|
4864
|
-
lookupMember(
|
|
4917
|
+
lookupMember(name, member) {
|
|
4865
4918
|
const visited = /* @__PURE__ */ new Set();
|
|
4866
|
-
let current = this.lookupClass(
|
|
4919
|
+
let current = this.lookupClass(name);
|
|
4867
4920
|
while (current !== null && !visited.has(current.name)) {
|
|
4868
4921
|
const found = current.members.get(member);
|
|
4869
4922
|
if (found !== void 0) {
|
|
@@ -4872,7 +4925,9 @@ var DeclarationRegistry = class {
|
|
|
4872
4925
|
visited.add(current.name);
|
|
4873
4926
|
current = current.superClass === null ? null : this.lookupClass(current.superClass);
|
|
4874
4927
|
}
|
|
4875
|
-
|
|
4928
|
+
const collected = /* @__PURE__ */ new Map();
|
|
4929
|
+
this.collectInterfaceMembers(name, collected, visited);
|
|
4930
|
+
return collected.get(member) ?? null;
|
|
4876
4931
|
}
|
|
4877
4932
|
};
|
|
4878
4933
|
|
|
@@ -4987,6 +5042,7 @@ var CheckContext = class {
|
|
|
4987
5042
|
methodStack = [];
|
|
4988
5043
|
projectEnvironments = /* @__PURE__ */ new Map();
|
|
4989
5044
|
ambientClasses = /* @__PURE__ */ new Set();
|
|
5045
|
+
ambientInterfaces = /* @__PURE__ */ new Set();
|
|
4990
5046
|
constructor(mode, environment, ambient = EMPTY_AMBIENT, project = EMPTY_PROJECT_DECLARATIONS, oop = false) {
|
|
4991
5047
|
this.mode = mode;
|
|
4992
5048
|
this.environment = environment;
|
|
@@ -5008,6 +5064,9 @@ var CheckContext = class {
|
|
|
5008
5064
|
isAmbientClass(name) {
|
|
5009
5065
|
return this.ambientClasses.has(name);
|
|
5010
5066
|
}
|
|
5067
|
+
isAmbientInterface(name) {
|
|
5068
|
+
return this.ambientInterfaces.has(name);
|
|
5069
|
+
}
|
|
5011
5070
|
noteExternalReference(name, position2) {
|
|
5012
5071
|
if (!this.externalReferences.has(name)) {
|
|
5013
5072
|
this.externalReferences.set(name, position2);
|
|
@@ -5057,6 +5116,16 @@ var CheckContext = class {
|
|
|
5057
5116
|
if (annotation.kind === "type-union") {
|
|
5058
5117
|
return createUnion(annotation.options.map((option) => this.resolveAnnotation(option)));
|
|
5059
5118
|
}
|
|
5119
|
+
if (annotation.kind === "type-object") {
|
|
5120
|
+
const members = /* @__PURE__ */ new Map();
|
|
5121
|
+
for (const member of annotation.members) {
|
|
5122
|
+
members.set(member.name, this.resolveAnnotation(member.annotation));
|
|
5123
|
+
}
|
|
5124
|
+
return createObjectType(members);
|
|
5125
|
+
}
|
|
5126
|
+
if (annotation.kind === "type-tuple") {
|
|
5127
|
+
return createTuple(annotation.elements.map((element) => this.resolveAnnotation(element)));
|
|
5128
|
+
}
|
|
5060
5129
|
if (annotation.kind === "type-function") {
|
|
5061
5130
|
const parameters = annotation.parameters.map((parameter) => this.resolveAnnotation(parameter));
|
|
5062
5131
|
const optional = parameters.findIndex((parameter) => parameter.kind === "optional");
|
|
@@ -5088,6 +5157,7 @@ var CheckContext = class {
|
|
|
5088
5157
|
this.binder.declareGlobal({ name: info.name, type: createNamed(info.name), isLocal: false, position: info.position });
|
|
5089
5158
|
}
|
|
5090
5159
|
for (const info of ambient.interfaces) {
|
|
5160
|
+
this.ambientInterfaces.add(info.name);
|
|
5091
5161
|
this.declarations.declareInterface(info);
|
|
5092
5162
|
}
|
|
5093
5163
|
for (const info of ambient.enums) {
|
|
@@ -5387,7 +5457,7 @@ function expandClassDecorators(context, statement, fieldTypes) {
|
|
|
5387
5457
|
const occupied = new Map(statement.members.filter((member) => member.kind === "class-method").map((member) => [member.name, member.name]));
|
|
5388
5458
|
const classDecorators = validateDecorators(context, statement.decorators, "class");
|
|
5389
5459
|
for (const member of statement.members) {
|
|
5390
|
-
if (member.kind === "class-method") {
|
|
5460
|
+
if (member.kind === "class-method" || member.name === "constructor") {
|
|
5391
5461
|
validateDecorators(context, member.decorators, "method");
|
|
5392
5462
|
continue;
|
|
5393
5463
|
}
|
|
@@ -5969,7 +6039,11 @@ function resolveNamedMember(context, name, expression) {
|
|
|
5969
6039
|
return resolveMtaMember(context, name, expression.property, expression.position)?.type ?? ANY_TYPE;
|
|
5970
6040
|
}
|
|
5971
6041
|
const contract = context.declarations.lookupClass(name) === null ? context.declarations.lookupInterface(name) : null;
|
|
5972
|
-
|
|
6042
|
+
if (contract === null) {
|
|
6043
|
+
return null;
|
|
6044
|
+
}
|
|
6045
|
+
const members = new Map(context.declarations.collectMembers(name).map((member2) => [member2.name, member2]));
|
|
6046
|
+
return checkInterfaceMember(context, name, members, expression);
|
|
5973
6047
|
}
|
|
5974
6048
|
var NATIVE_CONSTRUCTOR = "new";
|
|
5975
6049
|
function nativeConstructor(context, name) {
|
|
@@ -6334,6 +6408,10 @@ function registerMembers(context, info, statement) {
|
|
|
6334
6408
|
return generated;
|
|
6335
6409
|
}
|
|
6336
6410
|
function checkMethodBody(context, info, member) {
|
|
6411
|
+
const explicitSelf = member.parameters.find((parameter) => parameter.name === "self");
|
|
6412
|
+
if (explicitSelf !== void 0) {
|
|
6413
|
+
context.report("check-explicit-self-parameter", 'Class methods receive "self" automatically; remove it from the parameter list.', explicitSelf.position);
|
|
6414
|
+
}
|
|
6337
6415
|
context.pushClassMethod({ className: info.name, methodName: member.name });
|
|
6338
6416
|
checkFunctionBody(context, member.parameters, member.returnAnnotation, member.body, createNamed(info.name));
|
|
6339
6417
|
context.popClassMethod();
|
|
@@ -6360,7 +6438,7 @@ function checkInterfaces(context, info) {
|
|
|
6360
6438
|
context.report("check-unknown-interface", `Class "${info.name}" implements "${name}", which is not defined.`, info.position);
|
|
6361
6439
|
continue;
|
|
6362
6440
|
}
|
|
6363
|
-
for (const member of
|
|
6441
|
+
for (const member of context.declarations.collectMembers(contract.name)) {
|
|
6364
6442
|
checkContract(context, info, name, member);
|
|
6365
6443
|
}
|
|
6366
6444
|
}
|
|
@@ -6418,11 +6496,46 @@ function checkInterfaceDeclaration(context, statement) {
|
|
|
6418
6496
|
return;
|
|
6419
6497
|
}
|
|
6420
6498
|
const members = /* @__PURE__ */ new Map();
|
|
6499
|
+
for (const name of new Set(statement.superInterfaces)) {
|
|
6500
|
+
const parent = context.declarations.lookupInterface(name);
|
|
6501
|
+
if (name === statement.name || context.declarations.interfaceExtends(name, statement.name)) {
|
|
6502
|
+
context.report("check-interface-cycle", `Interface "${statement.name}" creates an inheritance cycle through "${name}".`, statement.position);
|
|
6503
|
+
} else if (parent === null) {
|
|
6504
|
+
context.noteExternalReference(name, statement.position);
|
|
6505
|
+
context.report("check-unknown-interface", `Interface "${statement.name}" extends "${name}", which is not defined.`, statement.position);
|
|
6506
|
+
} else if (context.isAmbientInterface(name)) {
|
|
6507
|
+
context.noteExternalReference(name, statement.position);
|
|
6508
|
+
}
|
|
6509
|
+
}
|
|
6510
|
+
if (new Set(statement.superInterfaces).size !== statement.superInterfaces.length) {
|
|
6511
|
+
context.report("check-duplicate-interface-parent", `Interface "${statement.name}" extends the same interface more than once.`, statement.position);
|
|
6512
|
+
}
|
|
6421
6513
|
for (const member of statement.members) {
|
|
6422
6514
|
const type = member.kind === "interface-field" ? context.resolveAnnotation(member.annotation) : buildFunctionType(context, member.parameters, member.returnAnnotation);
|
|
6423
|
-
|
|
6515
|
+
const info = { name: member.name, type, isMethod: member.kind === "interface-method", position: member.position };
|
|
6516
|
+
if (members.has(member.name)) {
|
|
6517
|
+
context.report("check-duplicate-interface-member", `Interface "${statement.name}" declares member "${member.name}" more than once.`, member.position);
|
|
6518
|
+
} else {
|
|
6519
|
+
members.set(member.name, info);
|
|
6520
|
+
}
|
|
6521
|
+
}
|
|
6522
|
+
const inherited = /* @__PURE__ */ new Map();
|
|
6523
|
+
for (const parent of statement.superInterfaces) {
|
|
6524
|
+
for (const member of context.declarations.collectMembers(parent)) {
|
|
6525
|
+
const existing = members.get(member.name) ?? inherited.get(member.name);
|
|
6526
|
+
if (existing !== void 0 && (existing.isMethod !== member.isMethod || typeToString(existing.type) !== typeToString(member.type))) {
|
|
6527
|
+
context.report("check-conflicting-interface-member", `Interface "${statement.name}" inherits conflicting declarations of "${member.name}".`, statement.position);
|
|
6528
|
+
} else if (existing === void 0) {
|
|
6529
|
+
inherited.set(member.name, member);
|
|
6530
|
+
}
|
|
6531
|
+
}
|
|
6424
6532
|
}
|
|
6425
|
-
context.declarations.declareInterface({
|
|
6533
|
+
context.declarations.declareInterface({
|
|
6534
|
+
name: statement.name,
|
|
6535
|
+
superInterfaces: statement.superInterfaces,
|
|
6536
|
+
members,
|
|
6537
|
+
position: statement.position
|
|
6538
|
+
});
|
|
6426
6539
|
}
|
|
6427
6540
|
function checkEnumDeclaration(context, statement) {
|
|
6428
6541
|
if (context.declarations.lookupEnum(statement.name) !== null) {
|
|
@@ -6582,6 +6695,23 @@ function checkReturn(context, statement) {
|
|
|
6582
6695
|
}
|
|
6583
6696
|
return;
|
|
6584
6697
|
}
|
|
6698
|
+
if (expected.kind === "tuple") {
|
|
6699
|
+
if (valueTypes.length !== expected.elements.length) {
|
|
6700
|
+
const message = `This function declares ${expected.elements.length} return values but returns ${valueTypes.length}.`;
|
|
6701
|
+
context.report("check-return-mismatch", message, statement.position);
|
|
6702
|
+
return;
|
|
6703
|
+
}
|
|
6704
|
+
valueTypes.forEach((type, index) => {
|
|
6705
|
+
const value2 = statement.values[Math.min(index, statement.values.length - 1)];
|
|
6706
|
+
context.expectAssignable(type, expected.elements[index] ?? ANY_TYPE, value2?.position ?? statement.position, `Return value ${index + 1}`);
|
|
6707
|
+
});
|
|
6708
|
+
return;
|
|
6709
|
+
}
|
|
6710
|
+
if (valueTypes.length > 1) {
|
|
6711
|
+
const message = `This function declares one return value of type "${typeToString(expected)}" but returns ${valueTypes.length} values.`;
|
|
6712
|
+
context.report("check-return-mismatch", message, statement.position);
|
|
6713
|
+
return;
|
|
6714
|
+
}
|
|
6585
6715
|
const first = valueTypes[0];
|
|
6586
6716
|
const value = statement.values[0];
|
|
6587
6717
|
if (first === void 0 || value === void 0) {
|
|
@@ -6628,9 +6758,12 @@ function checkStatement(context, statement) {
|
|
|
6628
6758
|
return checkNumericFor(context, statement);
|
|
6629
6759
|
case "generic-for-statement":
|
|
6630
6760
|
return checkGenericFor(context, statement);
|
|
6631
|
-
case "type-alias-statement":
|
|
6632
|
-
|
|
6761
|
+
case "type-alias-statement": {
|
|
6762
|
+
const resolved = context.resolveAnnotation(statement.annotation);
|
|
6763
|
+
const named2 = statement.annotation.kind === "type-object" ? renameRecord(resolved, statement.name) : resolved;
|
|
6764
|
+
context.binder.declareAlias(statement.name, named2);
|
|
6633
6765
|
return;
|
|
6766
|
+
}
|
|
6634
6767
|
case "declare-statement":
|
|
6635
6768
|
return checkDeclareStatement(context, statement);
|
|
6636
6769
|
case "class-declaration":
|
|
@@ -7781,13 +7914,45 @@ function parseFunctionType(stream) {
|
|
|
7781
7914
|
return { kind: "type-function", parameters, isVariadic, returnType, position: position2 };
|
|
7782
7915
|
}
|
|
7783
7916
|
function parseGroupedType(stream) {
|
|
7784
|
-
stream.expect("punctuation", "(");
|
|
7917
|
+
const position2 = stream.expect("punctuation", "(").position;
|
|
7785
7918
|
const inner = parseTypeAnnotation(stream);
|
|
7786
|
-
if (stream.
|
|
7787
|
-
|
|
7919
|
+
if (!stream.match("punctuation", ",")) {
|
|
7920
|
+
stream.expect("punctuation", ")");
|
|
7921
|
+
return inner;
|
|
7788
7922
|
}
|
|
7923
|
+
const elements = [inner];
|
|
7924
|
+
do {
|
|
7925
|
+
elements.push(parseTypeAnnotation(stream));
|
|
7926
|
+
} while (stream.match("punctuation", ","));
|
|
7789
7927
|
stream.expect("punctuation", ")");
|
|
7790
|
-
return
|
|
7928
|
+
return { kind: "type-tuple", elements, position: position2 };
|
|
7929
|
+
}
|
|
7930
|
+
function parseObjectMember(stream) {
|
|
7931
|
+
const token = stream.expectName();
|
|
7932
|
+
const annotation = parseNamedAnnotation(stream, "field");
|
|
7933
|
+
if (annotation === null) {
|
|
7934
|
+
throw stream.error(`Key "${token.value}" of an object type requires a type annotation.`, "parse-invalid-type");
|
|
7935
|
+
}
|
|
7936
|
+
return { name: token.value, annotation, position: token.position };
|
|
7937
|
+
}
|
|
7938
|
+
function parseObjectType(stream) {
|
|
7939
|
+
const position2 = stream.expect("punctuation", "{").position;
|
|
7940
|
+
const members = [];
|
|
7941
|
+
const declared = /* @__PURE__ */ new Set();
|
|
7942
|
+
while (!stream.check("punctuation", "}") && !stream.isEof()) {
|
|
7943
|
+
if (stream.match("punctuation", ";") || stream.match("punctuation", ",")) {
|
|
7944
|
+
continue;
|
|
7945
|
+
}
|
|
7946
|
+
const member = parseObjectMember(stream);
|
|
7947
|
+
if (declared.has(member.name)) {
|
|
7948
|
+
stream.report("parse-duplicate-key", `Object type key "${member.name}" is declared more than once.`, member.position);
|
|
7949
|
+
continue;
|
|
7950
|
+
}
|
|
7951
|
+
declared.add(member.name);
|
|
7952
|
+
members.push(member);
|
|
7953
|
+
}
|
|
7954
|
+
stream.expect("punctuation", "}");
|
|
7955
|
+
return { kind: "type-object", members, position: position2 };
|
|
7791
7956
|
}
|
|
7792
7957
|
function parsePrimaryType(stream) {
|
|
7793
7958
|
if (stream.check("keyword", "function")) {
|
|
@@ -7796,6 +7961,9 @@ function parsePrimaryType(stream) {
|
|
|
7796
7961
|
if (stream.check("identifier", FUNCTION_TYPE)) {
|
|
7797
7962
|
return parseFunctionType(stream);
|
|
7798
7963
|
}
|
|
7964
|
+
if (stream.check("punctuation", "{")) {
|
|
7965
|
+
return parseObjectType(stream);
|
|
7966
|
+
}
|
|
7799
7967
|
return stream.check("punctuation", "(") ? parseGroupedType(stream) : parseTypeName(stream);
|
|
7800
7968
|
}
|
|
7801
7969
|
function parsePostfixType(stream) {
|
|
@@ -8125,16 +8293,24 @@ function parseForStatement(stream) {
|
|
|
8125
8293
|
}
|
|
8126
8294
|
|
|
8127
8295
|
// ../compiler/src/parser/recovery.ts
|
|
8128
|
-
|
|
8296
|
+
var BRACE_TERMINATORS = /* @__PURE__ */ new Set(["}"]);
|
|
8297
|
+
function isTerminator(stream, terminators) {
|
|
8298
|
+
const token = stream.current();
|
|
8299
|
+
if (token.kind !== "punctuation" && token.kind !== "keyword") {
|
|
8300
|
+
return false;
|
|
8301
|
+
}
|
|
8302
|
+
return terminators.has(token.value);
|
|
8303
|
+
}
|
|
8304
|
+
function recoverInBlock(stream, error, terminators = BRACE_TERMINATORS) {
|
|
8129
8305
|
stream.diagnostics.push(error.diagnostic);
|
|
8130
8306
|
if (stream.isEof()) {
|
|
8131
8307
|
return;
|
|
8132
8308
|
}
|
|
8133
8309
|
const start = stream.current();
|
|
8134
|
-
while (!stream.isEof() && !stream
|
|
8310
|
+
while (!stream.isEof() && !isTerminator(stream, terminators) && stream.current().position.line === start.position.line) {
|
|
8135
8311
|
stream.next();
|
|
8136
8312
|
}
|
|
8137
|
-
if (!stream.isEof() && !stream
|
|
8313
|
+
if (!stream.isEof() && !isTerminator(stream, terminators) && stream.current().position.offset === start.position.offset) {
|
|
8138
8314
|
stream.next();
|
|
8139
8315
|
}
|
|
8140
8316
|
}
|
|
@@ -8244,7 +8420,10 @@ function isDeclarationStart(stream) {
|
|
|
8244
8420
|
if (offset > 0 && token.value !== "class") {
|
|
8245
8421
|
return false;
|
|
8246
8422
|
}
|
|
8247
|
-
|
|
8423
|
+
if (token.value === "class") {
|
|
8424
|
+
return isClassHeader(stream, offset);
|
|
8425
|
+
}
|
|
8426
|
+
return token.value === "interface" ? stream.checkAhead(offset + 2, "punctuation", "{") || stream.checkAhead(offset + 2, "keyword", "extends") : stream.checkAhead(offset + 2, "punctuation", "{");
|
|
8248
8427
|
}
|
|
8249
8428
|
function skipDecoratorArguments(stream) {
|
|
8250
8429
|
let depth = 0;
|
|
@@ -8271,6 +8450,21 @@ function parseDecorators(stream) {
|
|
|
8271
8450
|
return decorators;
|
|
8272
8451
|
}
|
|
8273
8452
|
function parseClassMethod(stream, token, decorators) {
|
|
8453
|
+
stream.expect("operator", "=");
|
|
8454
|
+
const expression = parseFunctionExpression(stream);
|
|
8455
|
+
return {
|
|
8456
|
+
kind: "class-method",
|
|
8457
|
+
name: token.value,
|
|
8458
|
+
isConstructor: token.value === "constructor",
|
|
8459
|
+
isSynthetic: false,
|
|
8460
|
+
parameters: expression.parameters,
|
|
8461
|
+
returnAnnotation: expression.returnAnnotation,
|
|
8462
|
+
body: expression.body,
|
|
8463
|
+
decorators,
|
|
8464
|
+
position: token.position
|
|
8465
|
+
};
|
|
8466
|
+
}
|
|
8467
|
+
function parseLegacyClassMethod(stream, token, decorators) {
|
|
8274
8468
|
const parameters = parseParameters(stream);
|
|
8275
8469
|
const returnAnnotation = parseOptionalAnnotation(stream);
|
|
8276
8470
|
const body = parseBraceBlock(stream);
|
|
@@ -8293,6 +8487,9 @@ function parseClassMember(stream) {
|
|
|
8293
8487
|
const decorators = parseDecorators(stream);
|
|
8294
8488
|
const token = stream.expectName();
|
|
8295
8489
|
if (stream.check("punctuation", "(")) {
|
|
8490
|
+
return parseLegacyClassMethod(stream, token, decorators);
|
|
8491
|
+
}
|
|
8492
|
+
if (stream.check("operator", "=") && stream.checkAhead(1, "keyword", "function")) {
|
|
8296
8493
|
return parseClassMethod(stream, token, decorators);
|
|
8297
8494
|
}
|
|
8298
8495
|
const annotation = parseFieldAnnotation(stream);
|
|
@@ -8348,7 +8545,13 @@ function parseInterfaceMember(stream) {
|
|
|
8348
8545
|
function parseInterfaceDeclaration(stream) {
|
|
8349
8546
|
const position2 = stream.next().position;
|
|
8350
8547
|
const name = stream.expect("identifier").value;
|
|
8548
|
+
const superInterfaces = [];
|
|
8351
8549
|
const members = [];
|
|
8550
|
+
if (stream.match("keyword", "extends")) {
|
|
8551
|
+
do {
|
|
8552
|
+
superInterfaces.push(stream.expect("identifier").value);
|
|
8553
|
+
} while (stream.match("punctuation", ","));
|
|
8554
|
+
}
|
|
8352
8555
|
stream.expect("punctuation", "{");
|
|
8353
8556
|
while (!stream.check("punctuation", "}") && !stream.isEof()) {
|
|
8354
8557
|
if (stream.match("punctuation", ";") || stream.match("punctuation", ",")) {
|
|
@@ -8364,7 +8567,7 @@ function parseInterfaceDeclaration(stream) {
|
|
|
8364
8567
|
}
|
|
8365
8568
|
}
|
|
8366
8569
|
stream.expect("punctuation", "}");
|
|
8367
|
-
return { kind: "interface-declaration", name, members, position: position2 };
|
|
8570
|
+
return { kind: "interface-declaration", name, superInterfaces, members, position: position2 };
|
|
8368
8571
|
}
|
|
8369
8572
|
function parseEnumDeclaration(stream) {
|
|
8370
8573
|
const position2 = stream.next().position;
|
|
@@ -8564,14 +8767,14 @@ function parseStatement(stream) {
|
|
|
8564
8767
|
}
|
|
8565
8768
|
return parseExpressionStatement(stream);
|
|
8566
8769
|
}
|
|
8567
|
-
function parseBlockStatement(stream) {
|
|
8770
|
+
function parseBlockStatement(stream, terminators = BRACE_TERMINATORS) {
|
|
8568
8771
|
try {
|
|
8569
8772
|
return parseStatement(stream);
|
|
8570
8773
|
} catch (error) {
|
|
8571
8774
|
if (!(error instanceof ParserError)) {
|
|
8572
8775
|
throw error;
|
|
8573
8776
|
}
|
|
8574
|
-
recoverInBlock(stream, error);
|
|
8777
|
+
recoverInBlock(stream, error, terminators);
|
|
8575
8778
|
return null;
|
|
8576
8779
|
}
|
|
8577
8780
|
}
|
|
@@ -8604,7 +8807,14 @@ function parseBlock(stream, terminators) {
|
|
|
8604
8807
|
if (stream.current().kind === "keyword" && stop.has(stream.current().value)) {
|
|
8605
8808
|
return body;
|
|
8606
8809
|
}
|
|
8607
|
-
const
|
|
8810
|
+
const offset = stream.current().position.offset;
|
|
8811
|
+
const statement = parseBlockStatement(stream, stop);
|
|
8812
|
+
if (statement === null) {
|
|
8813
|
+
if (stream.current().position.offset === offset) {
|
|
8814
|
+
return body;
|
|
8815
|
+
}
|
|
8816
|
+
continue;
|
|
8817
|
+
}
|
|
8608
8818
|
body.push(statement);
|
|
8609
8819
|
if (statement.kind === "return-statement") {
|
|
8610
8820
|
return body;
|
|
@@ -8751,7 +8961,7 @@ function classText(info) {
|
|
|
8751
8961
|
return `class ${info.name}:${info.superClass ?? ""}:${[...info.interfaces].sort(compareText).join(",")}{${membersText(info.members)}}`;
|
|
8752
8962
|
}
|
|
8753
8963
|
function interfaceText(info) {
|
|
8754
|
-
return `interface ${info.name}{${membersText(info.members)}}`;
|
|
8964
|
+
return `interface ${info.name}:${[...info.superInterfaces].sort(compareText).join(",")}{${membersText(info.members)}}`;
|
|
8755
8965
|
}
|
|
8756
8966
|
function enumText(info) {
|
|
8757
8967
|
return `enum ${info.name}{${info.members.join(",")}}`;
|
package/package.json
CHANGED
package/template/README.md
DELETED
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
# luam-resource
|
|
2
|
-
|
|
3
|
-
A Luam resource scaffolded by `luam init`. Write `.luam` sources under `src`, run
|
|
4
|
-
`luam build`, and MTA gets a plain Lua 5.1 resource in `build/luam-resource`.
|
|
5
|
-
|
|
6
|
-
## Commands
|
|
7
|
-
|
|
8
|
-
| Command | Behavior |
|
|
9
|
-
| ------- | -------- |
|
|
10
|
-
| `luam check` | Compiles and reports diagnostics. Writes nothing. |
|
|
11
|
-
| `luam build` | Writes the resource into `build/luam-resource`. |
|
|
12
|
-
| `luam ensure` | Builds, syncs into the MTA server, restarts, and watches sources. |
|
|
13
|
-
|
|
14
|
-
Set `serverPath` in `luam.json` before running `luam ensure`.
|
|
15
|
-
|
|
16
|
-
## Layout
|
|
17
|
-
|
|
18
|
-
```
|
|
19
|
-
.env Declares deployment keys and safe defaults
|
|
20
|
-
config.lua Structural settings the resource owns
|
|
21
|
-
assets/ Files copied verbatim and downloaded by clients
|
|
22
|
-
src/
|
|
23
|
-
shared/
|
|
24
|
-
config.luam Constants and helpers both sides use
|
|
25
|
-
framework/ Core, Event, Listener, Command, Loader, ThreadPool
|
|
26
|
-
server/
|
|
27
|
-
main.luam Creates the server Core and starts it
|
|
28
|
-
handlers/ One file per server listener
|
|
29
|
-
commands/ One file per server command
|
|
30
|
-
client/
|
|
31
|
-
main.luam Creates the client Core and starts it
|
|
32
|
-
commands/ One file per client command
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
The generated resource mirrors this tree. `src/server/main.luam` becomes
|
|
36
|
-
`src/server/main.lua`, and runtime helpers land in `src/<environment>/lib`, so a
|
|
37
|
-
server-only helper is never downloaded by a client.
|
|
38
|
-
|
|
39
|
-
## Configuration
|
|
40
|
-
|
|
41
|
-
Two files hold settings, and they have different owners.
|
|
42
|
-
|
|
43
|
-
`config.lua` belongs to the resource author. It is plain Lua, copied verbatim,
|
|
44
|
-
listed in `meta.xml` as a shared script, and readable by clients. Anything a
|
|
45
|
-
player may see belongs here.
|
|
46
|
-
|
|
47
|
-
`.env` belongs to the deployment. It declares the keys and safe defaults, and it
|
|
48
|
-
is the source of truth for their types: an unquoted number is a number, `true`
|
|
49
|
-
and `false` are booleans, and quoting forces a string. Override values locally in
|
|
50
|
-
`.env.local`, which is never committed.
|
|
51
|
-
|
|
52
|
-
The first `luam build` writes `build/<resource>/.env` from those keys, leaving
|
|
53
|
-
sensitive values blank, and never overwrites it again — that file belongs to the
|
|
54
|
-
server administrator.
|
|
55
|
-
|
|
56
|
-
Values reach Luam through `process.env`, which exists only on the server:
|
|
57
|
-
|
|
58
|
-
```lua
|
|
59
|
-
outputServerLog(tostring(process.env.SERVER_NAME))
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
A key that `.env` does not declare is a compile error, and `process` in a client
|
|
63
|
-
or shared file is a compile error too.
|
|
64
|
-
|
|
65
|
-
## Framework
|
|
66
|
-
|
|
67
|
-
`Core` owns the resource lifecycle. `main.luam` creates it with the side it runs
|
|
68
|
-
on and calls `start()` once the resource has finished loading. `start()` scans
|
|
69
|
-
the class registry, instantiates every `Listener` and `Command` subclass with the
|
|
70
|
-
`Core` instance, and registers them.
|
|
71
|
-
|
|
72
|
-
### Listeners
|
|
73
|
-
|
|
74
|
-
A listener declares the MTA event it handles and overrides `handle`. Nothing
|
|
75
|
-
registers it by hand — being a `Listener` subclass is enough.
|
|
76
|
-
|
|
77
|
-
```lua
|
|
78
|
-
class PlayerQuitListener extends Listener {
|
|
79
|
-
event: string = 'onPlayerQuit'
|
|
80
|
-
|
|
81
|
-
handle(...): void {
|
|
82
|
-
outputDebugString(getPlayerName(source) .. ' left.')
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
`element` defaults to `root`. Set it to narrow the handler to one element.
|
|
88
|
-
|
|
89
|
-
### Commands
|
|
90
|
-
|
|
91
|
-
A command declares its name, optional aliases, and overrides `execute`. Every
|
|
92
|
-
name and alias is registered.
|
|
93
|
-
|
|
94
|
-
```lua
|
|
95
|
-
class KickCommand extends Command {
|
|
96
|
-
name: string = 'kick'
|
|
97
|
-
aliases: string[] = { 'k' }
|
|
98
|
-
description: string = 'Kicks a player by name.'
|
|
99
|
-
|
|
100
|
-
execute(player: any, ...): void {
|
|
101
|
-
local args: any = { ... }
|
|
102
|
-
|
|
103
|
-
kickPlayer(getPlayerFromName(args[1]), player, 'kicked')
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
On the server `execute` receives the calling player; on the client the first
|
|
109
|
-
parameter is `nil`.
|
|
110
|
-
|
|
111
|
-
### Threads
|
|
112
|
-
|
|
113
|
-
`ThreadPool` wraps the `threads.lua` runtime helper, which is opt-in. Add it to
|
|
114
|
-
`luam.json` before using the class:
|
|
115
|
-
|
|
116
|
-
```json
|
|
117
|
-
{
|
|
118
|
-
"helpers": ["threads"]
|
|
119
|
-
}
|
|
120
|
-
```
|
package/template/config.lua
DELETED
package/template/env
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
# Deployment values. This file is versioned and declares the keys and safe
|
|
2
|
-
# defaults. Types come from this file: an unquoted number is a number, "true"
|
|
3
|
-
# and "false" are booleans, and quoting forces a string.
|
|
4
|
-
#
|
|
5
|
-
# Override values on your machine in ".env.local", which is never committed.
|
|
6
|
-
# The server administrator edits the generated "build/.env" instead.
|
|
7
|
-
|
|
8
|
-
# Server
|
|
9
|
-
SERVER_NAME="Luam Server"
|
|
10
|
-
MAX_PLAYERS=32
|
|
11
|
-
DEBUG=false
|