@trioforge/brigadier 0.0.0-stage → 0.1.1
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/LICENSE +22 -0
- package/README.md +73 -2
- package/out/arguments.d.ts +91 -0
- package/out/arguments.luau +475 -0
- package/out/command-dispatcher.d.ts +38 -0
- package/out/command-dispatcher.luau +394 -0
- package/out/command.d.ts +13 -0
- package/out/command.luau +8 -0
- package/out/context.d.ts +121 -0
- package/out/context.luau +576 -0
- package/out/exceptions.d.ts +118 -0
- package/out/exceptions.luau +415 -0
- package/out/index.d.ts +10 -0
- package/out/init.luau +44 -0
- package/out/java.d.ts +40 -0
- package/out/java.luau +424 -0
- package/out/literal-message.d.ts +9 -0
- package/out/literal-message.luau +29 -0
- package/out/string-reader.d.ts +41 -0
- package/out/string-reader.luau +271 -0
- package/out/suggestion.d.ts +60 -0
- package/out/suggestion.luau +340 -0
- package/out/tree.d.ts +126 -0
- package/out/tree.luau +579 -0
- package/package.json +33 -5
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) Microsoft Corporation. All rights reserved.
|
|
4
|
+
Copyright (c) 2026 Trio Forge Studio
|
|
5
|
+
|
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
in the Software without restriction, including without limitation the rights
|
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,74 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @trioforge/brigadier
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A roblox-ts port of [Mojang's Brigadier](https://github.com/Mojang/brigadier), the command parser and dispatcher behind Minecraft's commands. It is a class-for-class port of Brigadier 1.3, and it passes all of Brigadier's unit tests.
|
|
4
|
+
|
|
5
|
+
Read the documentation at <https://website.trio-forge.workers.dev/brigadier/>.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
pnpm add @trioforge/brigadier
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
roblox-ts only looks for packages in the scopes your project lists, so add the `@trioforge` scope in two places.
|
|
14
|
+
|
|
15
|
+
1. In `tsconfig.json`, add it to `typeRoots`:
|
|
16
|
+
|
|
17
|
+
```json
|
|
18
|
+
"typeRoots": ["node_modules/@rbxts", "node_modules/@trioforge"]
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
2. In your Rojo project, map it beside `@rbxts` under `rbxts_include/node_modules`:
|
|
22
|
+
|
|
23
|
+
```json
|
|
24
|
+
"node_modules": {
|
|
25
|
+
"$className": "Folder",
|
|
26
|
+
"@rbxts": { "$path": "node_modules/@rbxts" },
|
|
27
|
+
"@trioforge": { "$path": "node_modules/@trioforge" }
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Example
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
import { CommandDispatcher, argument, getInteger, getString, integer, literal, word } from "@trioforge/brigadier";
|
|
35
|
+
|
|
36
|
+
const dispatcher = new CommandDispatcher<Player>();
|
|
37
|
+
|
|
38
|
+
dispatcher.register(
|
|
39
|
+
literal<Player>("give").then(
|
|
40
|
+
argument<Player, string>("target", word()).then(
|
|
41
|
+
argument<Player, number>("amount", integer(1, 100)).executes((context) => {
|
|
42
|
+
const target = getString(context, "target");
|
|
43
|
+
const amount = getInteger(context, "amount");
|
|
44
|
+
print(`${context.getSource().Name} gives ${amount} to ${target}`);
|
|
45
|
+
return 1;
|
|
46
|
+
}),
|
|
47
|
+
),
|
|
48
|
+
),
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
dispatcher.execute("give Bob 50", player);
|
|
52
|
+
// → Ann gives 50 to Bob
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
A command that fails to parse throws a `CommandSyntaxException`. Its `getMessage()` names the problem and points at the character, such as `Integer must not be more than 100, found 500 at position 9: give Bob <--[HERE]`.
|
|
56
|
+
|
|
57
|
+
## Differences from the Java
|
|
58
|
+
|
|
59
|
+
The names are Brigadier's. [docs/porting.md](docs/porting.md) lists every place the port differs, such as Promises instead of `CompletableFuture` and `stringArg()` instead of `string()`.
|
|
60
|
+
|
|
61
|
+
## Development
|
|
62
|
+
|
|
63
|
+
Install the Node tools with `pnpm install`, and Lune and StyLua with `rokit install`.
|
|
64
|
+
|
|
65
|
+
```sh
|
|
66
|
+
pnpm run check # oxfmt, rbxtsc, StyLua, the tests and the README example
|
|
67
|
+
pnpm run test # build, then run the tests
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
The tests in `test/` are Brigadier's JUnit tests, ported to Luau one to one. `test/lib/runtime.luau` loads the compiled package from `out/` through roblox-ts's runtime, as a place does, and Lune runs them. [AGENTS.md](AGENTS.md) has the rules for changing the port.
|
|
71
|
+
|
|
72
|
+
## License
|
|
73
|
+
|
|
74
|
+
MIT, like Brigadier. See [LICENSE](LICENSE).
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import type { CommandContext } from "./context";
|
|
2
|
+
import { StringReader } from "./string-reader";
|
|
3
|
+
import type { Suggestions, SuggestionsBuilder } from "./suggestion";
|
|
4
|
+
export interface ArgumentType<T> {
|
|
5
|
+
parse(reader: StringReader, source?: unknown): T;
|
|
6
|
+
listSuggestions?<S>(context: CommandContext<S>, builder: SuggestionsBuilder): Promise<Suggestions>;
|
|
7
|
+
getExamples?(): readonly string[];
|
|
8
|
+
}
|
|
9
|
+
export declare class BoolArgumentType implements ArgumentType<boolean> {
|
|
10
|
+
parse(reader: StringReader): boolean;
|
|
11
|
+
listSuggestions<S>(_context: CommandContext<S>, builder: SuggestionsBuilder): Promise<Suggestions>;
|
|
12
|
+
getExamples(): readonly string[];
|
|
13
|
+
}
|
|
14
|
+
export declare function bool(): BoolArgumentType;
|
|
15
|
+
export declare function getBool<S>(context: CommandContext<S>, name: string): boolean;
|
|
16
|
+
export declare class IntegerArgumentType implements ArgumentType<number> {
|
|
17
|
+
private readonly minimum;
|
|
18
|
+
private readonly maximum;
|
|
19
|
+
constructor(minimum: number, maximum: number);
|
|
20
|
+
getMinimum(): number;
|
|
21
|
+
getMaximum(): number;
|
|
22
|
+
parse(reader: StringReader): number;
|
|
23
|
+
equals(other: unknown): boolean;
|
|
24
|
+
toString(): string;
|
|
25
|
+
getExamples(): readonly string[];
|
|
26
|
+
}
|
|
27
|
+
export declare function integer(min?: number, max?: number): IntegerArgumentType;
|
|
28
|
+
export declare function getInteger<S>(context: CommandContext<S>, name: string): number;
|
|
29
|
+
export declare class LongArgumentType implements ArgumentType<number> {
|
|
30
|
+
private readonly minimum;
|
|
31
|
+
private readonly maximum;
|
|
32
|
+
constructor(minimum: number, maximum: number);
|
|
33
|
+
getMinimum(): number;
|
|
34
|
+
getMaximum(): number;
|
|
35
|
+
parse(reader: StringReader): number;
|
|
36
|
+
equals(other: unknown): boolean;
|
|
37
|
+
toString(): string;
|
|
38
|
+
getExamples(): readonly string[];
|
|
39
|
+
}
|
|
40
|
+
export declare function longArg(min?: number, max?: number): LongArgumentType;
|
|
41
|
+
export declare function getLong<S>(context: CommandContext<S>, name: string): number;
|
|
42
|
+
export declare class DoubleArgumentType implements ArgumentType<number> {
|
|
43
|
+
private readonly minimum;
|
|
44
|
+
private readonly maximum;
|
|
45
|
+
constructor(minimum: number, maximum: number);
|
|
46
|
+
getMinimum(): number;
|
|
47
|
+
getMaximum(): number;
|
|
48
|
+
parse(reader: StringReader): number;
|
|
49
|
+
equals(other: unknown): boolean;
|
|
50
|
+
toString(): string;
|
|
51
|
+
getExamples(): readonly string[];
|
|
52
|
+
}
|
|
53
|
+
export declare function doubleArg(min?: number, max?: number): DoubleArgumentType;
|
|
54
|
+
export declare function getDouble<S>(context: CommandContext<S>, name: string): number;
|
|
55
|
+
export declare class FloatArgumentType implements ArgumentType<number> {
|
|
56
|
+
private readonly minimum;
|
|
57
|
+
private readonly maximum;
|
|
58
|
+
constructor(minimum: number, maximum: number);
|
|
59
|
+
getMinimum(): number;
|
|
60
|
+
getMaximum(): number;
|
|
61
|
+
parse(reader: StringReader): number;
|
|
62
|
+
equals(other: unknown): boolean;
|
|
63
|
+
toString(): string;
|
|
64
|
+
getExamples(): readonly string[];
|
|
65
|
+
}
|
|
66
|
+
export declare function floatArg(min?: number, max?: number): FloatArgumentType;
|
|
67
|
+
export declare function getFloat<S>(context: CommandContext<S>, name: string): number;
|
|
68
|
+
export declare class StringType {
|
|
69
|
+
private readonly constantName;
|
|
70
|
+
private readonly examples;
|
|
71
|
+
static readonly SINGLE_WORD: StringType;
|
|
72
|
+
static readonly QUOTABLE_PHRASE: StringType;
|
|
73
|
+
static readonly GREEDY_PHRASE: StringType;
|
|
74
|
+
private constructor();
|
|
75
|
+
name(): string;
|
|
76
|
+
getExamples(): readonly string[];
|
|
77
|
+
toString(): string;
|
|
78
|
+
}
|
|
79
|
+
export declare class StringArgumentType implements ArgumentType<string> {
|
|
80
|
+
private readonly stringType;
|
|
81
|
+
constructor(stringType: StringType);
|
|
82
|
+
getType(): StringType;
|
|
83
|
+
parse(reader: StringReader): string;
|
|
84
|
+
toString(): string;
|
|
85
|
+
getExamples(): readonly string[];
|
|
86
|
+
}
|
|
87
|
+
export declare function word(): StringArgumentType;
|
|
88
|
+
export declare function stringArg(): StringArgumentType;
|
|
89
|
+
export declare function greedyString(): StringArgumentType;
|
|
90
|
+
export declare function getString<S>(context: CommandContext<S>, name: string): string;
|
|
91
|
+
export declare function escapeIfRequired(input: string): string;
|
|
@@ -0,0 +1,475 @@
|
|
|
1
|
+
-- Compiled with roblox-ts v3.0.0
|
|
2
|
+
local TS = _G[script]
|
|
3
|
+
local CommandSyntaxException = TS.import(script, script.Parent, "exceptions").CommandSyntaxException
|
|
4
|
+
local _java = TS.import(script, script.Parent, "java")
|
|
5
|
+
local formatDouble = _java.formatDouble
|
|
6
|
+
local formatFloat = _java.formatFloat
|
|
7
|
+
local formatInteger = _java.formatInteger
|
|
8
|
+
local startsWith = _java.startsWith
|
|
9
|
+
local toFloat = _java.toFloat
|
|
10
|
+
local StringReader = TS.import(script, script.Parent, "string-reader").StringReader
|
|
11
|
+
-- Java's default methods are optional: a type without listSuggestions suggests nothing, and one without
|
|
12
|
+
-- getExamples has none.
|
|
13
|
+
local BOOL_EXAMPLES = { "true", "false" }
|
|
14
|
+
local BoolArgumentType
|
|
15
|
+
do
|
|
16
|
+
BoolArgumentType = setmetatable({}, {
|
|
17
|
+
__tostring = function()
|
|
18
|
+
return "BoolArgumentType"
|
|
19
|
+
end,
|
|
20
|
+
})
|
|
21
|
+
BoolArgumentType.__index = BoolArgumentType
|
|
22
|
+
function BoolArgumentType.new(...)
|
|
23
|
+
local self = setmetatable({}, BoolArgumentType)
|
|
24
|
+
return self:constructor(...) or self
|
|
25
|
+
end
|
|
26
|
+
function BoolArgumentType:constructor()
|
|
27
|
+
end
|
|
28
|
+
function BoolArgumentType:parse(reader)
|
|
29
|
+
return reader:readBoolean()
|
|
30
|
+
end
|
|
31
|
+
function BoolArgumentType:listSuggestions(_context, builder)
|
|
32
|
+
if startsWith("true", builder:getRemainingLowerCase()) then
|
|
33
|
+
builder:suggest("true")
|
|
34
|
+
end
|
|
35
|
+
if startsWith("false", builder:getRemainingLowerCase()) then
|
|
36
|
+
builder:suggest("false")
|
|
37
|
+
end
|
|
38
|
+
return builder:buildFuture()
|
|
39
|
+
end
|
|
40
|
+
function BoolArgumentType:getExamples()
|
|
41
|
+
return BOOL_EXAMPLES
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
local function bool()
|
|
45
|
+
return BoolArgumentType.new()
|
|
46
|
+
end
|
|
47
|
+
local function getBool(context, name)
|
|
48
|
+
return context:getArgument(name, "boolean")
|
|
49
|
+
end
|
|
50
|
+
local INTEGER_EXAMPLES = { "0", "123", "-123" }
|
|
51
|
+
local INTEGER_MIN = -2147483648
|
|
52
|
+
local INTEGER_MAX = 2147483647
|
|
53
|
+
local IntegerArgumentType
|
|
54
|
+
do
|
|
55
|
+
IntegerArgumentType = setmetatable({}, {
|
|
56
|
+
__tostring = function()
|
|
57
|
+
return "IntegerArgumentType"
|
|
58
|
+
end,
|
|
59
|
+
})
|
|
60
|
+
IntegerArgumentType.__index = IntegerArgumentType
|
|
61
|
+
function IntegerArgumentType.new(...)
|
|
62
|
+
local self = setmetatable({}, IntegerArgumentType)
|
|
63
|
+
return self:constructor(...) or self
|
|
64
|
+
end
|
|
65
|
+
function IntegerArgumentType:constructor(minimum, maximum)
|
|
66
|
+
self.minimum = minimum
|
|
67
|
+
self.maximum = maximum
|
|
68
|
+
end
|
|
69
|
+
function IntegerArgumentType:getMinimum()
|
|
70
|
+
return self.minimum
|
|
71
|
+
end
|
|
72
|
+
function IntegerArgumentType:getMaximum()
|
|
73
|
+
return self.maximum
|
|
74
|
+
end
|
|
75
|
+
function IntegerArgumentType:parse(reader)
|
|
76
|
+
local start = reader:getCursor()
|
|
77
|
+
local result = reader:readInt()
|
|
78
|
+
if result < self.minimum then
|
|
79
|
+
reader:setCursor(start)
|
|
80
|
+
error(CommandSyntaxException.BUILT_IN_EXCEPTIONS:integerTooLow():createWithContext(reader, result, self.minimum))
|
|
81
|
+
end
|
|
82
|
+
if result > self.maximum then
|
|
83
|
+
reader:setCursor(start)
|
|
84
|
+
error(CommandSyntaxException.BUILT_IN_EXCEPTIONS:integerTooHigh():createWithContext(reader, result, self.maximum))
|
|
85
|
+
end
|
|
86
|
+
return result
|
|
87
|
+
end
|
|
88
|
+
function IntegerArgumentType:equals(other)
|
|
89
|
+
if rawequal(self, other) then
|
|
90
|
+
return true
|
|
91
|
+
end
|
|
92
|
+
if not (TS.instanceof(other, IntegerArgumentType)) then
|
|
93
|
+
return false
|
|
94
|
+
end
|
|
95
|
+
return self.maximum == other.maximum and self.minimum == other.minimum
|
|
96
|
+
end
|
|
97
|
+
function IntegerArgumentType:toString()
|
|
98
|
+
if self.minimum == INTEGER_MIN and self.maximum == INTEGER_MAX then
|
|
99
|
+
return "integer()"
|
|
100
|
+
elseif self.maximum == INTEGER_MAX then
|
|
101
|
+
return `integer({formatInteger(self.minimum)})`
|
|
102
|
+
end
|
|
103
|
+
return `integer({formatInteger(self.minimum)}, {formatInteger(self.maximum)})`
|
|
104
|
+
end
|
|
105
|
+
function IntegerArgumentType:getExamples()
|
|
106
|
+
return INTEGER_EXAMPLES
|
|
107
|
+
end
|
|
108
|
+
function IntegerArgumentType:__tostring()
|
|
109
|
+
return self:toString()
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
local function integer(min, max)
|
|
113
|
+
if min == nil then
|
|
114
|
+
min = INTEGER_MIN
|
|
115
|
+
end
|
|
116
|
+
if max == nil then
|
|
117
|
+
max = INTEGER_MAX
|
|
118
|
+
end
|
|
119
|
+
return IntegerArgumentType.new(min, max)
|
|
120
|
+
end
|
|
121
|
+
local function getInteger(context, name)
|
|
122
|
+
return context:getArgument(name, "number")
|
|
123
|
+
end
|
|
124
|
+
local LONG_MIN = -(2 ^ 63)
|
|
125
|
+
local LONG_MAX = 2 ^ 63
|
|
126
|
+
local LongArgumentType
|
|
127
|
+
do
|
|
128
|
+
LongArgumentType = setmetatable({}, {
|
|
129
|
+
__tostring = function()
|
|
130
|
+
return "LongArgumentType"
|
|
131
|
+
end,
|
|
132
|
+
})
|
|
133
|
+
LongArgumentType.__index = LongArgumentType
|
|
134
|
+
function LongArgumentType.new(...)
|
|
135
|
+
local self = setmetatable({}, LongArgumentType)
|
|
136
|
+
return self:constructor(...) or self
|
|
137
|
+
end
|
|
138
|
+
function LongArgumentType:constructor(minimum, maximum)
|
|
139
|
+
self.minimum = minimum
|
|
140
|
+
self.maximum = maximum
|
|
141
|
+
end
|
|
142
|
+
function LongArgumentType:getMinimum()
|
|
143
|
+
return self.minimum
|
|
144
|
+
end
|
|
145
|
+
function LongArgumentType:getMaximum()
|
|
146
|
+
return self.maximum
|
|
147
|
+
end
|
|
148
|
+
function LongArgumentType:parse(reader)
|
|
149
|
+
local start = reader:getCursor()
|
|
150
|
+
local result = reader:readLong()
|
|
151
|
+
if result < self.minimum then
|
|
152
|
+
reader:setCursor(start)
|
|
153
|
+
error(CommandSyntaxException.BUILT_IN_EXCEPTIONS:longTooLow():createWithContext(reader, result, self.minimum))
|
|
154
|
+
end
|
|
155
|
+
if result > self.maximum then
|
|
156
|
+
reader:setCursor(start)
|
|
157
|
+
error(CommandSyntaxException.BUILT_IN_EXCEPTIONS:longTooHigh():createWithContext(reader, result, self.maximum))
|
|
158
|
+
end
|
|
159
|
+
return result
|
|
160
|
+
end
|
|
161
|
+
function LongArgumentType:equals(other)
|
|
162
|
+
if rawequal(self, other) then
|
|
163
|
+
return true
|
|
164
|
+
end
|
|
165
|
+
if not (TS.instanceof(other, LongArgumentType)) then
|
|
166
|
+
return false
|
|
167
|
+
end
|
|
168
|
+
return self.maximum == other.maximum and self.minimum == other.minimum
|
|
169
|
+
end
|
|
170
|
+
function LongArgumentType:toString()
|
|
171
|
+
if self.minimum == LONG_MIN and self.maximum == LONG_MAX then
|
|
172
|
+
return "longArg()"
|
|
173
|
+
elseif self.maximum == LONG_MAX then
|
|
174
|
+
return `longArg({formatInteger(self.minimum)})`
|
|
175
|
+
end
|
|
176
|
+
return `longArg({formatInteger(self.minimum)}, {formatInteger(self.maximum)})`
|
|
177
|
+
end
|
|
178
|
+
function LongArgumentType:getExamples()
|
|
179
|
+
return INTEGER_EXAMPLES
|
|
180
|
+
end
|
|
181
|
+
function LongArgumentType:__tostring()
|
|
182
|
+
return self:toString()
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
local function longArg(min, max)
|
|
186
|
+
if min == nil then
|
|
187
|
+
min = LONG_MIN
|
|
188
|
+
end
|
|
189
|
+
if max == nil then
|
|
190
|
+
max = LONG_MAX
|
|
191
|
+
end
|
|
192
|
+
return LongArgumentType.new(min, max)
|
|
193
|
+
end
|
|
194
|
+
local function getLong(context, name)
|
|
195
|
+
return context:getArgument(name, "number")
|
|
196
|
+
end
|
|
197
|
+
local DECIMAL_EXAMPLES = { "0", "1.2", ".5", "-1", "-.5", "-1234.56" }
|
|
198
|
+
local DOUBLE_MIN = -1.7976931348623157e308
|
|
199
|
+
local DOUBLE_MAX = 1.7976931348623157e308
|
|
200
|
+
local DoubleArgumentType
|
|
201
|
+
do
|
|
202
|
+
DoubleArgumentType = setmetatable({}, {
|
|
203
|
+
__tostring = function()
|
|
204
|
+
return "DoubleArgumentType"
|
|
205
|
+
end,
|
|
206
|
+
})
|
|
207
|
+
DoubleArgumentType.__index = DoubleArgumentType
|
|
208
|
+
function DoubleArgumentType.new(...)
|
|
209
|
+
local self = setmetatable({}, DoubleArgumentType)
|
|
210
|
+
return self:constructor(...) or self
|
|
211
|
+
end
|
|
212
|
+
function DoubleArgumentType:constructor(minimum, maximum)
|
|
213
|
+
self.minimum = minimum
|
|
214
|
+
self.maximum = maximum
|
|
215
|
+
end
|
|
216
|
+
function DoubleArgumentType:getMinimum()
|
|
217
|
+
return self.minimum
|
|
218
|
+
end
|
|
219
|
+
function DoubleArgumentType:getMaximum()
|
|
220
|
+
return self.maximum
|
|
221
|
+
end
|
|
222
|
+
function DoubleArgumentType:parse(reader)
|
|
223
|
+
local start = reader:getCursor()
|
|
224
|
+
local result = reader:readDouble()
|
|
225
|
+
if result < self.minimum then
|
|
226
|
+
reader:setCursor(start)
|
|
227
|
+
error(CommandSyntaxException.BUILT_IN_EXCEPTIONS:doubleTooLow():createWithContext(reader, result, self.minimum))
|
|
228
|
+
end
|
|
229
|
+
if result > self.maximum then
|
|
230
|
+
reader:setCursor(start)
|
|
231
|
+
error(CommandSyntaxException.BUILT_IN_EXCEPTIONS:doubleTooHigh():createWithContext(reader, result, self.maximum))
|
|
232
|
+
end
|
|
233
|
+
return result
|
|
234
|
+
end
|
|
235
|
+
function DoubleArgumentType:equals(other)
|
|
236
|
+
if rawequal(self, other) then
|
|
237
|
+
return true
|
|
238
|
+
end
|
|
239
|
+
if not (TS.instanceof(other, DoubleArgumentType)) then
|
|
240
|
+
return false
|
|
241
|
+
end
|
|
242
|
+
return self.maximum == other.maximum and self.minimum == other.minimum
|
|
243
|
+
end
|
|
244
|
+
function DoubleArgumentType:toString()
|
|
245
|
+
if self.minimum == DOUBLE_MIN and self.maximum == DOUBLE_MAX then
|
|
246
|
+
return "double()"
|
|
247
|
+
elseif self.maximum == DOUBLE_MAX then
|
|
248
|
+
return `double({formatDouble(self.minimum)})`
|
|
249
|
+
end
|
|
250
|
+
return `double({formatDouble(self.minimum)}, {formatDouble(self.maximum)})`
|
|
251
|
+
end
|
|
252
|
+
function DoubleArgumentType:getExamples()
|
|
253
|
+
return DECIMAL_EXAMPLES
|
|
254
|
+
end
|
|
255
|
+
function DoubleArgumentType:__tostring()
|
|
256
|
+
return self:toString()
|
|
257
|
+
end
|
|
258
|
+
end
|
|
259
|
+
local function doubleArg(min, max)
|
|
260
|
+
if min == nil then
|
|
261
|
+
min = DOUBLE_MIN
|
|
262
|
+
end
|
|
263
|
+
if max == nil then
|
|
264
|
+
max = DOUBLE_MAX
|
|
265
|
+
end
|
|
266
|
+
return DoubleArgumentType.new(min, max)
|
|
267
|
+
end
|
|
268
|
+
local function getDouble(context, name)
|
|
269
|
+
return context:getArgument(name, "number")
|
|
270
|
+
end
|
|
271
|
+
local FLOAT_MIN = -3.4028234663852886e38
|
|
272
|
+
local FLOAT_MAX = 3.4028234663852886e38
|
|
273
|
+
local FloatArgumentType
|
|
274
|
+
do
|
|
275
|
+
FloatArgumentType = setmetatable({}, {
|
|
276
|
+
__tostring = function()
|
|
277
|
+
return "FloatArgumentType"
|
|
278
|
+
end,
|
|
279
|
+
})
|
|
280
|
+
FloatArgumentType.__index = FloatArgumentType
|
|
281
|
+
function FloatArgumentType.new(...)
|
|
282
|
+
local self = setmetatable({}, FloatArgumentType)
|
|
283
|
+
return self:constructor(...) or self
|
|
284
|
+
end
|
|
285
|
+
function FloatArgumentType:constructor(minimum, maximum)
|
|
286
|
+
self.minimum = toFloat(minimum)
|
|
287
|
+
self.maximum = toFloat(maximum)
|
|
288
|
+
end
|
|
289
|
+
function FloatArgumentType:getMinimum()
|
|
290
|
+
return self.minimum
|
|
291
|
+
end
|
|
292
|
+
function FloatArgumentType:getMaximum()
|
|
293
|
+
return self.maximum
|
|
294
|
+
end
|
|
295
|
+
function FloatArgumentType:parse(reader)
|
|
296
|
+
local start = reader:getCursor()
|
|
297
|
+
local result = reader:readFloat()
|
|
298
|
+
if result < self.minimum then
|
|
299
|
+
reader:setCursor(start)
|
|
300
|
+
error(CommandSyntaxException.BUILT_IN_EXCEPTIONS:floatTooLow():createWithContext(reader, result, self.minimum))
|
|
301
|
+
end
|
|
302
|
+
if result > self.maximum then
|
|
303
|
+
reader:setCursor(start)
|
|
304
|
+
error(CommandSyntaxException.BUILT_IN_EXCEPTIONS:floatTooHigh():createWithContext(reader, result, self.maximum))
|
|
305
|
+
end
|
|
306
|
+
return result
|
|
307
|
+
end
|
|
308
|
+
function FloatArgumentType:equals(other)
|
|
309
|
+
if rawequal(self, other) then
|
|
310
|
+
return true
|
|
311
|
+
end
|
|
312
|
+
if not (TS.instanceof(other, FloatArgumentType)) then
|
|
313
|
+
return false
|
|
314
|
+
end
|
|
315
|
+
return self.maximum == other.maximum and self.minimum == other.minimum
|
|
316
|
+
end
|
|
317
|
+
function FloatArgumentType:toString()
|
|
318
|
+
if self.minimum == FLOAT_MIN and self.maximum == FLOAT_MAX then
|
|
319
|
+
return "float()"
|
|
320
|
+
elseif self.maximum == FLOAT_MAX then
|
|
321
|
+
return `float({formatFloat(self.minimum)})`
|
|
322
|
+
end
|
|
323
|
+
return `float({formatFloat(self.minimum)}, {formatFloat(self.maximum)})`
|
|
324
|
+
end
|
|
325
|
+
function FloatArgumentType:getExamples()
|
|
326
|
+
return DECIMAL_EXAMPLES
|
|
327
|
+
end
|
|
328
|
+
function FloatArgumentType:__tostring()
|
|
329
|
+
return self:toString()
|
|
330
|
+
end
|
|
331
|
+
end
|
|
332
|
+
local function floatArg(min, max)
|
|
333
|
+
if min == nil then
|
|
334
|
+
min = FLOAT_MIN
|
|
335
|
+
end
|
|
336
|
+
if max == nil then
|
|
337
|
+
max = FLOAT_MAX
|
|
338
|
+
end
|
|
339
|
+
return FloatArgumentType.new(min, max)
|
|
340
|
+
end
|
|
341
|
+
local function getFloat(context, name)
|
|
342
|
+
return context:getArgument(name, "number")
|
|
343
|
+
end
|
|
344
|
+
local StringType
|
|
345
|
+
do
|
|
346
|
+
StringType = setmetatable({}, {
|
|
347
|
+
__tostring = function()
|
|
348
|
+
return "StringType"
|
|
349
|
+
end,
|
|
350
|
+
})
|
|
351
|
+
StringType.__index = StringType
|
|
352
|
+
function StringType.new(...)
|
|
353
|
+
local self = setmetatable({}, StringType)
|
|
354
|
+
return self:constructor(...) or self
|
|
355
|
+
end
|
|
356
|
+
function StringType:constructor(constantName, examples)
|
|
357
|
+
self.constantName = constantName
|
|
358
|
+
self.examples = examples
|
|
359
|
+
end
|
|
360
|
+
function StringType:name()
|
|
361
|
+
return self.constantName
|
|
362
|
+
end
|
|
363
|
+
function StringType:getExamples()
|
|
364
|
+
return self.examples
|
|
365
|
+
end
|
|
366
|
+
function StringType:toString()
|
|
367
|
+
return self.constantName
|
|
368
|
+
end
|
|
369
|
+
function StringType:__tostring()
|
|
370
|
+
return self:toString()
|
|
371
|
+
end
|
|
372
|
+
StringType.SINGLE_WORD = StringType.new("SINGLE_WORD", { "word", "words_with_underscores" })
|
|
373
|
+
StringType.QUOTABLE_PHRASE = StringType.new("QUOTABLE_PHRASE", { '"quoted phrase"', "word", '""' })
|
|
374
|
+
StringType.GREEDY_PHRASE = StringType.new("GREEDY_PHRASE", { "word", "words with spaces", '"and symbols"' })
|
|
375
|
+
end
|
|
376
|
+
local StringArgumentType
|
|
377
|
+
do
|
|
378
|
+
StringArgumentType = setmetatable({}, {
|
|
379
|
+
__tostring = function()
|
|
380
|
+
return "StringArgumentType"
|
|
381
|
+
end,
|
|
382
|
+
})
|
|
383
|
+
StringArgumentType.__index = StringArgumentType
|
|
384
|
+
function StringArgumentType.new(...)
|
|
385
|
+
local self = setmetatable({}, StringArgumentType)
|
|
386
|
+
return self:constructor(...) or self
|
|
387
|
+
end
|
|
388
|
+
function StringArgumentType:constructor(stringType)
|
|
389
|
+
self.stringType = stringType
|
|
390
|
+
end
|
|
391
|
+
function StringArgumentType:getType()
|
|
392
|
+
return self.stringType
|
|
393
|
+
end
|
|
394
|
+
function StringArgumentType:parse(reader)
|
|
395
|
+
if self.stringType == StringType.GREEDY_PHRASE then
|
|
396
|
+
local text = reader:getRemaining()
|
|
397
|
+
reader:setCursor(reader:getTotalLength())
|
|
398
|
+
return text
|
|
399
|
+
elseif self.stringType == StringType.SINGLE_WORD then
|
|
400
|
+
return reader:readUnquotedString()
|
|
401
|
+
end
|
|
402
|
+
return reader:readString()
|
|
403
|
+
end
|
|
404
|
+
function StringArgumentType:toString()
|
|
405
|
+
return "string()"
|
|
406
|
+
end
|
|
407
|
+
function StringArgumentType:getExamples()
|
|
408
|
+
return self.stringType:getExamples()
|
|
409
|
+
end
|
|
410
|
+
function StringArgumentType:__tostring()
|
|
411
|
+
return self:toString()
|
|
412
|
+
end
|
|
413
|
+
end
|
|
414
|
+
local function word()
|
|
415
|
+
return StringArgumentType.new(StringType.SINGLE_WORD)
|
|
416
|
+
end
|
|
417
|
+
local function stringArg()
|
|
418
|
+
return StringArgumentType.new(StringType.QUOTABLE_PHRASE)
|
|
419
|
+
end
|
|
420
|
+
local function greedyString()
|
|
421
|
+
return StringArgumentType.new(StringType.GREEDY_PHRASE)
|
|
422
|
+
end
|
|
423
|
+
local function getString(context, name)
|
|
424
|
+
return context:getArgument(name, "string")
|
|
425
|
+
end
|
|
426
|
+
local function escape(input)
|
|
427
|
+
local result = { '"' }
|
|
428
|
+
for index = 1, #input do
|
|
429
|
+
local _input = input
|
|
430
|
+
local _index = index
|
|
431
|
+
local _index_1 = index
|
|
432
|
+
local c = string.sub(_input, _index, _index_1)
|
|
433
|
+
if c == "\\" or c == '"' then
|
|
434
|
+
table.insert(result, "\\")
|
|
435
|
+
end
|
|
436
|
+
table.insert(result, c)
|
|
437
|
+
end
|
|
438
|
+
table.insert(result, '"')
|
|
439
|
+
return table.concat(result, "")
|
|
440
|
+
end
|
|
441
|
+
local function escapeIfRequired(input)
|
|
442
|
+
for index = 1, #input do
|
|
443
|
+
local _input = input
|
|
444
|
+
local _index = index
|
|
445
|
+
local _index_1 = index
|
|
446
|
+
if not StringReader:isAllowedInUnquotedString(string.sub(_input, _index, _index_1)) then
|
|
447
|
+
return escape(input)
|
|
448
|
+
end
|
|
449
|
+
end
|
|
450
|
+
return input
|
|
451
|
+
end
|
|
452
|
+
return {
|
|
453
|
+
bool = bool,
|
|
454
|
+
getBool = getBool,
|
|
455
|
+
integer = integer,
|
|
456
|
+
getInteger = getInteger,
|
|
457
|
+
longArg = longArg,
|
|
458
|
+
getLong = getLong,
|
|
459
|
+
doubleArg = doubleArg,
|
|
460
|
+
getDouble = getDouble,
|
|
461
|
+
floatArg = floatArg,
|
|
462
|
+
getFloat = getFloat,
|
|
463
|
+
word = word,
|
|
464
|
+
stringArg = stringArg,
|
|
465
|
+
greedyString = greedyString,
|
|
466
|
+
getString = getString,
|
|
467
|
+
escapeIfRequired = escapeIfRequired,
|
|
468
|
+
BoolArgumentType = BoolArgumentType,
|
|
469
|
+
IntegerArgumentType = IntegerArgumentType,
|
|
470
|
+
LongArgumentType = LongArgumentType,
|
|
471
|
+
DoubleArgumentType = DoubleArgumentType,
|
|
472
|
+
FloatArgumentType = FloatArgumentType,
|
|
473
|
+
StringType = StringType,
|
|
474
|
+
StringArgumentType = StringArgumentType,
|
|
475
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { AmbiguityConsumer, ResultConsumer } from "./command";
|
|
2
|
+
import { CommandContextBuilder } from "./context";
|
|
3
|
+
import { CommandSyntaxException } from "./exceptions";
|
|
4
|
+
import { type ImmutableStringReader, StringReader } from "./string-reader";
|
|
5
|
+
import { Suggestions } from "./suggestion";
|
|
6
|
+
import { type CommandNode, type LiteralArgumentBuilder, type LiteralCommandNode, RootCommandNode } from "./tree";
|
|
7
|
+
export declare class ParseResults<S> {
|
|
8
|
+
private readonly context;
|
|
9
|
+
private readonly reader;
|
|
10
|
+
private readonly exceptions;
|
|
11
|
+
constructor(context: CommandContextBuilder<S>, reader?: ImmutableStringReader, exceptions?: ReadonlyMap<CommandNode<S>, CommandSyntaxException>);
|
|
12
|
+
getContext(): CommandContextBuilder<S>;
|
|
13
|
+
getReader(): ImmutableStringReader;
|
|
14
|
+
getExceptions(): ReadonlyMap<CommandNode<S>, CommandSyntaxException>;
|
|
15
|
+
}
|
|
16
|
+
export declare class CommandDispatcher<S> {
|
|
17
|
+
static readonly ARGUMENT_SEPARATOR = " ";
|
|
18
|
+
static readonly ARGUMENT_SEPARATOR_CHAR = " ";
|
|
19
|
+
private readonly root;
|
|
20
|
+
private consumer;
|
|
21
|
+
constructor(root?: RootCommandNode<S>);
|
|
22
|
+
register(command: LiteralArgumentBuilder<S>): LiteralCommandNode<S>;
|
|
23
|
+
setConsumer(consumer: ResultConsumer<S>): void;
|
|
24
|
+
execute(input: string | StringReader, source: S): number;
|
|
25
|
+
execute(parse: ParseResults<S>): number;
|
|
26
|
+
private executeParse;
|
|
27
|
+
parse(command: string | StringReader, source: S): ParseResults<S>;
|
|
28
|
+
private parseNodes;
|
|
29
|
+
getAllUsage(node: CommandNode<S>, source: S, restricted: boolean): string[];
|
|
30
|
+
private getAllUsageRecursive;
|
|
31
|
+
getSmartUsage(node: CommandNode<S>, source: S): Map<CommandNode<S>, string>;
|
|
32
|
+
private getSmartUsageRecursive;
|
|
33
|
+
getCompletionSuggestions(parse: ParseResults<S>, cursor?: number): Promise<Suggestions>;
|
|
34
|
+
getRoot(): RootCommandNode<S>;
|
|
35
|
+
getPath(target: CommandNode<S>): string[];
|
|
36
|
+
findNode(path: readonly string[]): CommandNode<S> | undefined;
|
|
37
|
+
findAmbiguities(consumer: AmbiguityConsumer<S>): void;
|
|
38
|
+
}
|