@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.
@@ -0,0 +1,394 @@
1
+ -- Compiled with roblox-ts v3.0.0
2
+ local TS = _G[script]
3
+ local _context = TS.import(script, script.Parent, "context")
4
+ local CommandContextBuilder = _context.CommandContextBuilder
5
+ local ContextChain = _context.ContextChain
6
+ local CommandSyntaxException = TS.import(script, script.Parent, "exceptions").CommandSyntaxException
7
+ local _java = TS.import(script, script.Parent, "java")
8
+ local rethrow = _java.rethrow
9
+ local stableSort = _java.stableSort
10
+ local substring = _java.substring
11
+ local toLowerCase = _java.toLowerCase
12
+ local StringReader = TS.import(script, script.Parent, "string-reader").StringReader
13
+ local _suggestion = TS.import(script, script.Parent, "suggestion")
14
+ local Suggestions = _suggestion.Suggestions
15
+ local SuggestionsBuilder = _suggestion.SuggestionsBuilder
16
+ local RootCommandNode = TS.import(script, script.Parent, "tree").RootCommandNode
17
+ local ParseResults
18
+ do
19
+ ParseResults = setmetatable({}, {
20
+ __tostring = function()
21
+ return "ParseResults"
22
+ end,
23
+ })
24
+ ParseResults.__index = ParseResults
25
+ function ParseResults.new(...)
26
+ local self = setmetatable({}, ParseResults)
27
+ return self:constructor(...) or self
28
+ end
29
+ function ParseResults:constructor(context, reader, exceptions)
30
+ self.context = context
31
+ self.reader = reader or StringReader.new("")
32
+ self.exceptions = exceptions or {}
33
+ end
34
+ function ParseResults:getContext()
35
+ return self.context
36
+ end
37
+ function ParseResults:getReader()
38
+ return self.reader
39
+ end
40
+ function ParseResults:getExceptions()
41
+ return self.exceptions
42
+ end
43
+ end
44
+ local ARGUMENT_SEPARATOR = " "
45
+ local USAGE_OPTIONAL_OPEN = "["
46
+ local USAGE_OPTIONAL_CLOSE = "]"
47
+ local USAGE_REQUIRED_OPEN = "("
48
+ local USAGE_REQUIRED_CLOSE = ")"
49
+ local USAGE_OR = "|"
50
+ local function ignoreResult()
51
+ end
52
+ -- The message of an error that isn't a CommandSyntaxException, as RuntimeException.getMessage gives it.
53
+ local function messageOf(err)
54
+ local _err = err
55
+ local _condition = type(_err) == "table"
56
+ if _condition then
57
+ local _getMessage = err.getMessage
58
+ _condition = type(_getMessage) == "function"
59
+ end
60
+ if _condition then
61
+ return err:getMessage()
62
+ end
63
+ return tostring(err)
64
+ end
65
+ local function hasCommandSyntaxExceptions(parse)
66
+ return not (next(parse:getExceptions()) == nil)
67
+ end
68
+ local function compareParses(a, b)
69
+ if not a:getReader():canRead() and b:getReader():canRead() then
70
+ return -1
71
+ end
72
+ if a:getReader():canRead() and not b:getReader():canRead() then
73
+ return 1
74
+ end
75
+ if not hasCommandSyntaxExceptions(a) and hasCommandSyntaxExceptions(b) then
76
+ return -1
77
+ end
78
+ if hasCommandSyntaxExceptions(a) and not hasCommandSyntaxExceptions(b) then
79
+ return 1
80
+ end
81
+ return 0
82
+ end
83
+ local CommandDispatcher
84
+ do
85
+ CommandDispatcher = setmetatable({}, {
86
+ __tostring = function()
87
+ return "CommandDispatcher"
88
+ end,
89
+ })
90
+ CommandDispatcher.__index = CommandDispatcher
91
+ function CommandDispatcher.new(...)
92
+ local self = setmetatable({}, CommandDispatcher)
93
+ return self:constructor(...) or self
94
+ end
95
+ function CommandDispatcher:constructor(root)
96
+ self.consumer = ignoreResult
97
+ self.root = root or RootCommandNode.new()
98
+ end
99
+ function CommandDispatcher:register(command)
100
+ local build = command:build()
101
+ self.root:addChild(build)
102
+ return build
103
+ end
104
+ function CommandDispatcher:setConsumer(consumer)
105
+ self.consumer = consumer
106
+ end
107
+ function CommandDispatcher:execute(input, source)
108
+ if TS.instanceof(input, ParseResults) then
109
+ return self:executeParse(input)
110
+ end
111
+ return self:executeParse(self:parse(input, source))
112
+ end
113
+ function CommandDispatcher:executeParse(parse)
114
+ if parse:getReader():canRead() then
115
+ local exceptions = parse:getExceptions()
116
+ -- ▼ ReadonlyMap.size ▼
117
+ local _size = 0
118
+ for _ in exceptions do
119
+ _size += 1
120
+ end
121
+ -- ▲ ReadonlyMap.size ▲
122
+ if _size == 1 then
123
+ for _, exception in exceptions do
124
+ error(exception)
125
+ end
126
+ elseif parse:getContext():getRange():isEmpty() then
127
+ error(CommandSyntaxException.BUILT_IN_EXCEPTIONS:dispatcherUnknownCommand():createWithContext(parse:getReader()))
128
+ end
129
+ error(CommandSyntaxException.BUILT_IN_EXCEPTIONS:dispatcherUnknownArgument():createWithContext(parse:getReader()))
130
+ end
131
+ local command = parse:getReader():getString()
132
+ local original = parse:getContext():build(command)
133
+ local flatContext = ContextChain:tryFlatten(original)
134
+ if flatContext == nil then
135
+ self.consumer(original, false, 0)
136
+ error(CommandSyntaxException.BUILT_IN_EXCEPTIONS:dispatcherUnknownCommand():createWithContext(parse:getReader()))
137
+ end
138
+ return flatContext:executeAll(original:getSource(), self.consumer)
139
+ end
140
+ function CommandDispatcher:parse(command, source)
141
+ local _command = command
142
+ local reader = if type(_command) == "string" then StringReader.new(command) else command
143
+ local context = CommandContextBuilder.new(self, source, self.root, reader:getCursor())
144
+ return self:parseNodes(self.root, reader, context)
145
+ end
146
+ function CommandDispatcher:parseNodes(node, originalReader, contextSoFar)
147
+ local source = contextSoFar:getSource()
148
+ local errors
149
+ local potentials
150
+ local cursor = originalReader:getCursor()
151
+ for _, child in node:getRelevantNodes(originalReader) do
152
+ if not child:canUse(source) then
153
+ continue
154
+ end
155
+ local context = contextSoFar:copy()
156
+ local reader = StringReader.new(originalReader)
157
+ local exception
158
+ TS.try(function()
159
+ child:parse(reader, context)
160
+ if reader:canRead() and reader:peek() ~= ARGUMENT_SEPARATOR then
161
+ exception = CommandSyntaxException.BUILT_IN_EXCEPTIONS:dispatcherExpectedArgumentSeparator():createWithContext(reader)
162
+ end
163
+ end, function(err)
164
+ exception = if TS.instanceof(err, CommandSyntaxException) then err else CommandSyntaxException.BUILT_IN_EXCEPTIONS:dispatcherParseException():createWithContext(reader, messageOf(err))
165
+ end)
166
+ if exception ~= nil then
167
+ if errors == nil then
168
+ errors = {}
169
+ end
170
+ local _errors = errors
171
+ local _exception = exception
172
+ _errors[child] = _exception
173
+ reader:setCursor(cursor)
174
+ continue
175
+ end
176
+ context:withCommand(child:getCommand())
177
+ local redirect = child:getRedirect()
178
+ if reader:canRead(if redirect == nil then 2 else 1) then
179
+ reader:skip()
180
+ if redirect ~= nil then
181
+ local childContext = CommandContextBuilder.new(self, source, redirect, reader:getCursor())
182
+ local parse = self:parseNodes(redirect, reader, childContext)
183
+ context:withChild(parse:getContext())
184
+ return ParseResults.new(context, parse:getReader(), parse:getExceptions())
185
+ end
186
+ local parse = self:parseNodes(child, reader, context)
187
+ if potentials == nil then
188
+ potentials = {}
189
+ end
190
+ table.insert(potentials, parse)
191
+ else
192
+ if potentials == nil then
193
+ potentials = {}
194
+ end
195
+ local _potentials = potentials
196
+ local _parseResults = ParseResults.new(context, reader, {})
197
+ table.insert(_potentials, _parseResults)
198
+ end
199
+ end
200
+ if potentials ~= nil then
201
+ if #potentials > 1 then
202
+ stableSort(potentials, compareParses)
203
+ end
204
+ return potentials[1]
205
+ end
206
+ return ParseResults.new(contextSoFar, originalReader, errors or {})
207
+ end
208
+ function CommandDispatcher:getAllUsage(node, source, restricted)
209
+ local result = {}
210
+ self:getAllUsageRecursive(node, source, result, "", restricted)
211
+ return result
212
+ end
213
+ function CommandDispatcher:getAllUsageRecursive(node, source, result, prefix, restricted)
214
+ if restricted and not node:canUse(source) then
215
+ return nil
216
+ end
217
+ if node:getCommand() ~= nil then
218
+ local _result = result
219
+ local _prefix = prefix
220
+ table.insert(_result, _prefix)
221
+ end
222
+ local redirect = node:getRedirect()
223
+ if redirect ~= nil then
224
+ local redirectText = if rawequal(redirect, self.root) then "..." else "-> " .. redirect:getUsageText()
225
+ local _result = result
226
+ local _arg0 = if prefix == "" then node:getUsageText() .. ARGUMENT_SEPARATOR .. redirectText else prefix .. ARGUMENT_SEPARATOR .. redirectText
227
+ table.insert(_result, _arg0)
228
+ else
229
+ for _, child in node:getChildren() do
230
+ self:getAllUsageRecursive(child, source, result, if prefix == "" then child:getUsageText() else prefix .. ARGUMENT_SEPARATOR .. child:getUsageText(), restricted)
231
+ end
232
+ end
233
+ end
234
+ function CommandDispatcher:getSmartUsage(node, source)
235
+ local result = {}
236
+ local optional = node:getCommand() ~= nil
237
+ for _, child in node:getChildren() do
238
+ local usage = self:getSmartUsageRecursive(child, source, optional, false)
239
+ if usage ~= nil then
240
+ result[child] = usage
241
+ end
242
+ end
243
+ return result
244
+ end
245
+ function CommandDispatcher:getSmartUsageRecursive(node, source, optional, deep)
246
+ if not node:canUse(source) then
247
+ return nil
248
+ end
249
+ local selfUsage = if optional then USAGE_OPTIONAL_OPEN .. node:getUsageText() .. USAGE_OPTIONAL_CLOSE else node:getUsageText()
250
+ local childOptional = node:getCommand() ~= nil
251
+ local open = if childOptional then USAGE_OPTIONAL_OPEN else USAGE_REQUIRED_OPEN
252
+ local close = if childOptional then USAGE_OPTIONAL_CLOSE else USAGE_REQUIRED_CLOSE
253
+ if not deep then
254
+ local redirect = node:getRedirect()
255
+ if redirect ~= nil then
256
+ local redirectText = if rawequal(redirect, self.root) then "..." else "-> " .. redirect:getUsageText()
257
+ return selfUsage .. ARGUMENT_SEPARATOR .. redirectText
258
+ end
259
+ local _exp = node:getChildren()
260
+ -- ▼ ReadonlyArray.filter ▼
261
+ local _newValue = {}
262
+ local _callback = function(child)
263
+ return child:canUse(source)
264
+ end
265
+ local _length = 0
266
+ for _k, _v in _exp do
267
+ if _callback(_v, _k - 1, _exp) == true then
268
+ _length += 1
269
+ _newValue[_length] = _v
270
+ end
271
+ end
272
+ -- ▲ ReadonlyArray.filter ▲
273
+ local children = _newValue
274
+ if #children == 1 then
275
+ local usage = self:getSmartUsageRecursive(children[1], source, childOptional, childOptional)
276
+ if usage ~= nil then
277
+ return selfUsage .. ARGUMENT_SEPARATOR .. usage
278
+ end
279
+ elseif #children > 1 then
280
+ -- A LinkedHashSet<String>.
281
+ local childUsage = {}
282
+ for _, child in children do
283
+ local usage = self:getSmartUsageRecursive(child, source, childOptional, true)
284
+ if usage ~= nil and not (table.find(childUsage, usage) ~= nil) then
285
+ table.insert(childUsage, usage)
286
+ end
287
+ end
288
+ if #childUsage == 1 then
289
+ local usage = childUsage[1]
290
+ return selfUsage .. ARGUMENT_SEPARATOR .. (if childOptional then USAGE_OPTIONAL_OPEN .. usage .. USAGE_OPTIONAL_CLOSE else usage)
291
+ elseif #childUsage > 1 then
292
+ local builder = open
293
+ local count = 0
294
+ for _, child in children do
295
+ if count > 0 then
296
+ builder ..= USAGE_OR
297
+ end
298
+ builder ..= child:getUsageText()
299
+ count += 1
300
+ end
301
+ if count > 0 then
302
+ builder ..= close
303
+ return selfUsage .. ARGUMENT_SEPARATOR .. builder
304
+ end
305
+ end
306
+ end
307
+ end
308
+ return selfUsage
309
+ end
310
+ function CommandDispatcher:getCompletionSuggestions(parse, cursor)
311
+ local _condition = cursor
312
+ if _condition == nil then
313
+ _condition = parse:getReader():getTotalLength()
314
+ end
315
+ local cursorPosition = _condition
316
+ local context = parse:getContext()
317
+ local nodeBeforeCursor = context:findSuggestionContext(cursorPosition)
318
+ local parent = nodeBeforeCursor.parent
319
+ local start = math.min(nodeBeforeCursor.startPos, cursorPosition)
320
+ local fullInput = parse:getReader():getString()
321
+ local truncatedInput = substring(fullInput, 0, cursorPosition)
322
+ local truncatedInputLowerCase = toLowerCase(truncatedInput)
323
+ local futures = {}
324
+ for _, node in parent:getChildren() do
325
+ local future = Suggestions:empty()
326
+ TS.try(function()
327
+ future = node:listSuggestions(nodeBeforeCursor.context:build(truncatedInput), SuggestionsBuilder.new(truncatedInput, truncatedInputLowerCase, start))
328
+ end, function(err)
329
+ if not (TS.instanceof(err, CommandSyntaxException)) then
330
+ rethrow(err)
331
+ end
332
+ end)
333
+ local _future = future
334
+ table.insert(futures, _future)
335
+ end
336
+ -- Promise.all's typings lose the element type of an array that isn't a tuple.
337
+ return TS.Promise.all(futures):andThen(function(suggestions)
338
+ return Suggestions:merge(fullInput, suggestions)
339
+ end)
340
+ end
341
+ function CommandDispatcher:getRoot()
342
+ return self.root
343
+ end
344
+ function CommandDispatcher:getPath(target)
345
+ local nodes = {}
346
+ local addPaths
347
+ addPaths = function(node, parents)
348
+ local _array = {}
349
+ local _length = #_array
350
+ local _parentsLength = #parents
351
+ table.move(parents, 1, _parentsLength, _length + 1, _array)
352
+ _length += _parentsLength
353
+ _array[_length + 1] = node
354
+ local current = _array
355
+ table.insert(nodes, current)
356
+ for _, child in node:getChildren() do
357
+ addPaths(child, current)
358
+ end
359
+ end
360
+ addPaths(self.root, {})
361
+ for _, list in nodes do
362
+ if rawequal(list[#list], target) then
363
+ local result = {}
364
+ for _1, node in list do
365
+ if not rawequal(node, self.root) then
366
+ local _arg0 = node:getName()
367
+ table.insert(result, _arg0)
368
+ end
369
+ end
370
+ return result
371
+ end
372
+ end
373
+ return {}
374
+ end
375
+ function CommandDispatcher:findNode(path)
376
+ local node = self.root
377
+ for _, name in path do
378
+ node = node:getChild(name)
379
+ if node == nil then
380
+ return nil
381
+ end
382
+ end
383
+ return node
384
+ end
385
+ function CommandDispatcher:findAmbiguities(consumer)
386
+ self.root:findAmbiguities(consumer)
387
+ end
388
+ CommandDispatcher.ARGUMENT_SEPARATOR = ARGUMENT_SEPARATOR
389
+ CommandDispatcher.ARGUMENT_SEPARATOR_CHAR = ARGUMENT_SEPARATOR
390
+ end
391
+ return {
392
+ ParseResults = ParseResults,
393
+ CommandDispatcher = CommandDispatcher,
394
+ }
@@ -0,0 +1,13 @@
1
+ import type { CommandContext } from "./context";
2
+ import type { Suggestions, SuggestionsBuilder } from "./suggestion";
3
+ import type { CommandNode } from "./tree";
4
+ export type Command<S> = (context: CommandContext<S>) => number;
5
+ export declare const Command: {
6
+ readonly SINGLE_SUCCESS: 1;
7
+ };
8
+ export type Predicate<S> = (source: S) => boolean;
9
+ export type RedirectModifier<S> = (context: CommandContext<S>) => readonly S[];
10
+ export type SingleRedirectModifier<S> = (context: CommandContext<S>) => S;
11
+ export type ResultConsumer<S> = (context: CommandContext<S>, success: boolean, result: number) => void;
12
+ export type AmbiguityConsumer<S> = (parent: CommandNode<S>, child: CommandNode<S>, sibling: CommandNode<S>, inputs: readonly string[]) => void;
13
+ export type SuggestionProvider<S> = (context: CommandContext<S>, builder: SuggestionsBuilder) => Promise<Suggestions>;
@@ -0,0 +1,8 @@
1
+ -- Compiled with roblox-ts v3.0.0
2
+ -- Brigadier's functional interfaces are plain functions. Each one may throw a CommandSyntaxException.
3
+ local Command = {
4
+ SINGLE_SUCCESS = 1,
5
+ }
6
+ return {
7
+ Command = Command,
8
+ }
@@ -0,0 +1,121 @@
1
+ import type { Command, RedirectModifier, ResultConsumer } from "./command";
2
+ import type { CommandDispatcher } from "./command-dispatcher";
3
+ import type { ImmutableStringReader } from "./string-reader";
4
+ import type { CommandNode } from "./tree";
5
+ export declare class StringRange {
6
+ private readonly start;
7
+ private readonly finish;
8
+ constructor(start: number, finish: number);
9
+ static at(pos: number): StringRange;
10
+ static between(start: number, finish: number): StringRange;
11
+ static encompassing(a: StringRange, b: StringRange): StringRange;
12
+ getStart(): number;
13
+ getEnd(): number;
14
+ get(reader: ImmutableStringReader | string): string;
15
+ isEmpty(): boolean;
16
+ getLength(): number;
17
+ equals(other: unknown): boolean;
18
+ toString(): string;
19
+ }
20
+ export declare class ParsedArgument<T> {
21
+ private readonly result;
22
+ private readonly range;
23
+ constructor(start: number, finish: number, result: T);
24
+ getRange(): StringRange;
25
+ getResult(): T;
26
+ equals(other: unknown): boolean;
27
+ }
28
+ export declare class ParsedCommandNode<S> {
29
+ private readonly node;
30
+ private readonly range;
31
+ constructor(node: CommandNode<S>, range: StringRange);
32
+ getNode(): CommandNode<S>;
33
+ getRange(): StringRange;
34
+ toString(): string;
35
+ equals(other: unknown): boolean;
36
+ }
37
+ export declare class SuggestionContext<S> {
38
+ readonly context: CommandContextBuilder<S>;
39
+ readonly parent: CommandNode<S>;
40
+ readonly startPos: number;
41
+ constructor(context: CommandContextBuilder<S>, parent: CommandNode<S>, startPos: number);
42
+ }
43
+ interface Checkable {
44
+ any: unknown;
45
+ }
46
+ export type ArgumentClass = keyof CheckableTypes | keyof Checkable | (new (...args: never[]) => object);
47
+ export declare class CommandContext<S> {
48
+ private readonly source;
49
+ private readonly input;
50
+ private readonly args;
51
+ private readonly command;
52
+ private readonly rootNode;
53
+ private readonly nodes;
54
+ private readonly range;
55
+ private readonly child;
56
+ private readonly modifier;
57
+ private readonly forks;
58
+ constructor(source: S, input: string, args: ReadonlyMap<string, ParsedArgument<unknown>>, command: Command<S> | undefined, rootNode: CommandNode<S>, nodes: readonly ParsedCommandNode<S>[], range: StringRange, child: CommandContext<S> | undefined, modifier: RedirectModifier<S> | undefined, forks: boolean);
59
+ copyFor(source: S): CommandContext<S>;
60
+ getChild(): CommandContext<S> | undefined;
61
+ getLastChild(): CommandContext<S>;
62
+ getCommand(): Command<S> | undefined;
63
+ getSource(): S;
64
+ getArgument<V>(name: string, clazz: ArgumentClass): V;
65
+ equals(other: unknown): boolean;
66
+ getRedirectModifier(): RedirectModifier<S> | undefined;
67
+ getRange(): StringRange;
68
+ getInput(): string;
69
+ getRootNode(): CommandNode<S>;
70
+ getNodes(): readonly ParsedCommandNode<S>[];
71
+ hasNodes(): boolean;
72
+ isForked(): boolean;
73
+ }
74
+ export declare class CommandContextBuilder<S> {
75
+ private readonly dispatcher;
76
+ private source;
77
+ private readonly rootNode;
78
+ private readonly args;
79
+ private readonly nodes;
80
+ private command?;
81
+ private child?;
82
+ private range;
83
+ private modifier?;
84
+ private forks;
85
+ constructor(dispatcher: CommandDispatcher<S>, source: S, rootNode: CommandNode<S>, start: number);
86
+ withSource(source: S): this;
87
+ getSource(): S;
88
+ getRootNode(): CommandNode<S>;
89
+ withArgument(name: string, argument: ParsedArgument<unknown>): this;
90
+ getArguments(): ReadonlyMap<string, ParsedArgument<unknown>>;
91
+ withCommand(command: Command<S> | undefined): this;
92
+ withNode(node: CommandNode<S>, range: StringRange): this;
93
+ copy(): CommandContextBuilder<S>;
94
+ withChild(child: CommandContextBuilder<S>): this;
95
+ getChild(): CommandContextBuilder<S> | undefined;
96
+ getLastChild(): CommandContextBuilder<S>;
97
+ getCommand(): Command<S> | undefined;
98
+ getNodes(): readonly ParsedCommandNode<S>[];
99
+ build(input: string): CommandContext<S>;
100
+ getDispatcher(): CommandDispatcher<S>;
101
+ getRange(): StringRange;
102
+ findSuggestionContext(cursor: number): SuggestionContext<S>;
103
+ }
104
+ export declare class ContextChain<S> {
105
+ private readonly modifiers;
106
+ private readonly executable;
107
+ static readonly Stage: {
108
+ readonly MODIFY: "MODIFY";
109
+ readonly EXECUTE: "EXECUTE";
110
+ };
111
+ private nextStageCache?;
112
+ constructor(modifiers: readonly CommandContext<S>[], executable: CommandContext<S>);
113
+ static tryFlatten<S>(rootContext: CommandContext<S>): ContextChain<S> | undefined;
114
+ static runModifier<S>(modifier: CommandContext<S>, source: S, resultConsumer: ResultConsumer<S>, forkedMode: boolean): readonly S[];
115
+ static runExecutable<S>(executable: CommandContext<S>, source: S, resultConsumer: ResultConsumer<S>, forkedMode: boolean): number;
116
+ executeAll(source: S, resultConsumer: ResultConsumer<S>): number;
117
+ getStage(): (typeof ContextChain.Stage)[keyof typeof ContextChain.Stage];
118
+ getTopContext(): CommandContext<S>;
119
+ nextStage(): ContextChain<S> | undefined;
120
+ }
121
+ export {};