Implement argument types

Also fixes CommandDispatcher not throwing parse exceptions correctly, RequiredArgumentBuilder returning incorrect objects, and CommandContext.getArgument not returning the correct value
This commit is contained in:
Chipmunk 2023-09-01 18:41:31 -04:00
parent 6dc676ef84
commit d302cf8f37
13 changed files with 281 additions and 5 deletions

View file

@ -34,7 +34,7 @@ class CommandDispatcher {
else parse = this.parse(new StringReader(input), source) else parse = this.parse(new StringReader(input), source)
if (parse.reader.canRead()) { if (parse.reader.canRead()) {
if (parse.exceptions.size === 1) throw parse.exceptions.values().next() if (parse.exceptions.size === 1) throw parse.exceptions.values().next().value
if (parse.context.range.isEmpty()) throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownCommand.createWithContext(parse.reader) if (parse.context.range.isEmpty()) throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownCommand.createWithContext(parse.reader)
throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownArgument.createWithContext(parse.reader) throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownArgument.createWithContext(parse.reader)
} }

View file

@ -0,0 +1,11 @@
class ArgumentType {
async listSuggestions (context, builder) {
return Suggestions.empty()
}
getExamples () {
return []
}
}
module.exports = ArgumentType

View file

@ -0,0 +1,25 @@
const ArgumentType = require('./ArgumentType.js')
const EXAMPLES = ['true', 'false']
class BoolArgumentType extends ArgumentType {
static bool () {
return new BoolArgumentType()
}
parse (reader) {
return reader.readBoolean()
}
async listSuggestions (context, builder) {
if ('true'.startsWith(builder.remainingLowerCase)) builder.suggest('true')
if ('false'.startsWith(builder.remainingLowerCase)) builder.suggest('false')
return builder.buildPromise()
}
getExamples () {
return EXAMPLES
}
}
module.exports = BoolArgumentType

View file

@ -0,0 +1,43 @@
const ArgumentType = require('./ArgumentType.js')
const { CommandSyntaxException } = require('../exceptions')
const MAXIMUM_DOUBLE = 1.7976931348623157E308
const EXAMPLES = ['0', '1.2', '.5', '-1', '-.5', '-1234.56']
class DoubleArgumentType extends ArgumentType {
constructor (minimum, maximum) {
super()
this.minumum = +minimum
this.maximum = +maximum
}
static double (min = -MAXIMUM_DOUBLE, max = MAXIMUM_DOUBLE) {
return new DoubleArgumentType(min, max)
}
parse (reader) {
const start = reader.cursor
const result = reader.readDouble()
if (result < this.minumum) {
reader.cursor = start
throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.doubleTooLow.createWithContext(reader, result, this.minimum)
}
if (result > this.maximum) {
reader.cursor = start
throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.doubleTooHigh.createWithContext(reader, result, this.maximum)
}
return result
}
toString () {
if (this.minumum === -MAXIMUM_DOUBLE && this.maximum === MAXIMUM_DOUBLE) return 'double()'
if (this.maximum === MAXIMUM_DOUBLE) return `double(${this.minimum})`
return `double(${this.minimum}, ${this.maximum})`
}
getExamples () {
return EXAMPLES
}
}
module.exports = DoubleArgumentType

View file

@ -0,0 +1,43 @@
const ArgumentType = require('./ArgumentType.js')
const { CommandSyntaxException } = require('../exceptions')
const MAXIMUM_FLOAT = 3.4028235E38
const EXAMPLES = ['0', '1.2', '.5', '-1', '-.5', '-1234.56']
class FloatArgumentType extends ArgumentType {
constructor (minimum, maximum) {
super()
this.minumum = +minimum
this.maximum = +maximum
}
static float (min = -MAXIMUM_FLOAT, max = MAXIMUM_FLOAT) {
return new FloatArgumentType(min, max)
}
parse (reader) {
const start = reader.cursor
const result = reader.readFloat()
if (result < this.minumum) {
reader.cursor = start
throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.floatTooLow.createWithContext(reader, result, this.minimum)
}
if (result > this.maximum) {
reader.cursor = start
throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.floatTooHigh.createWithContext(reader, result, this.maximum)
}
return result
}
toString () {
if (this.minumum === -MAXIMUM_FLOAT && this.maximum === MAXIMUM_FLOAT) return 'float()'
if (this.maximum === MAXIMUM_FLOAT) return `float(${this.minimum})`
return `float(${this.minimum}, ${this.maximum})`
}
getExamples () {
return EXAMPLES
}
}
module.exports = FloatArgumentType

View file

@ -0,0 +1,44 @@
const ArgumentType = require('./ArgumentType.js')
const { CommandSyntaxException } = require('../exceptions')
const MINIMUM_INT = -2147483648
const MAXIMUM_INT = 2147483647
const EXAMPLES = ['0', '123', '-123']
class IntegerArgumentType extends ArgumentType {
constructor (minimum, maximum) {
super()
this.minumum = +minimum
this.maximum = +maximum
}
static integer (min = MINIMUM_INT, max = MAXIMUM_INT) {
return new IntegerArgumentType(min, max)
}
parse (reader) {
const start = reader.cursor
const result = reader.readInt()
if (result < this.minumum) {
reader.cursor = start
throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.integerTooLow.createWithContext(reader, result, this.minimum)
}
if (result > this.maximum) {
reader.cursor = start
throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.integerTooHigh.createWithContext(reader, result, this.maximum)
}
return result
}
toString () {
if (this.minumum === MINIMUM_INT && this.maximum === MAXIMUM_INT) return 'integer()'
if (this.maximum === MAXIMUM_INT) return `integer(${this.minimum})`
return `integer(${this.minimum}, ${this.maximum})`
}
getExamples () {
return EXAMPLES
}
}
module.exports = IntegerArgumentType

View file

@ -0,0 +1,44 @@
const ArgumentType = require('./ArgumentType.js')
const { CommandSyntaxException } = require('../exceptions')
const MINIMUM_LONG = -9223372036854775808n
const MAXIMUM_LONG = 9223372036854775807n
const EXAMPLES = ['0', '123', '-123']
class LongArgumentType extends ArgumentType {
constructor (minimum, maximum) {
super()
this.minumum = BigInt(minimum)
this.maximum = BigInt(maximum)
}
static long (min = MINIMUM_LONG, max = MAXIMUM_LONG) {
return new LongArgumentType(min, max)
}
parse (reader) {
const start = reader.cursor
const result = reader.readLong()
if (result < this.minumum) {
reader.cursor = start
throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.longTooLow.createWithContext(reader, result, this.minimum)
}
if (result > this.maximum) {
reader.cursor = start
throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.longTooHigh.createWithContext(reader, result, this.maximum)
}
return result
}
toString () {
if (this.minumum === MINIMUM_LONG && this.maximum === MAXIMUM_LONG) return 'long()'
if (this.maximum === MAXIMUM_LONG) return `long(${this.minimum})`
return `long(${this.minimum}, ${this.maximum})`
}
getExamples () {
return EXAMPLES
}
}
module.exports = LongArgumentType

View file

@ -0,0 +1,46 @@
const ArgumentType = require('./ArgumentType.js')
class StringArgumentType extends ArgumentType {
constructor (type) {
super()
this.type = type
}
static word () {
return new StringArgumentType(StringArgumentType.StringType.SINGLE_WORD)
}
static string () {
return new StringArgumentType(StringArgumentType.StringType.QUOTABLE_PHRASE)
}
static greedyString () {
return new StringArgumentType(StringArgumentType.StringType.GREEDY_PHRASE)
}
parse (reader) {
if (this.type === StringArgumentType.StringType.GREEDY_PHRASE) {
const text = reader.getRemaining()
reader.cursor = reader.getTotalLength()
return text
}
if (this.type === StringArgumentType.StringType.SINGLE_WORD) {
return reader.readUnquotedString()
}
return reader.readString()
}
getExamples () {
return this.type.examples
}
static StringType = {
SINGLE_WORD: { examples: ['word', 'words_with_underscores'] },
QUOTABLE_PHRASE: { examples: ['"quoted phrase"', 'word', '""'] },
GREEDY_PHRASE: { examples: ['word', 'words with spaces', '"and symbols"'] }
}
}
module.exports = StringArgumentType

17
lib/arguments/index.js Normal file
View file

@ -0,0 +1,17 @@
const ArgumentType = require('./ArgumentType.js')
const BoolArgumentType = require('./BoolArgumentType.js')
const DoubleArgumentType = require('./DoubleArgumentType.js')
const FloatArgumentType = require('./FloatArgumentType.js')
const IntegerArgumentType = require('./IntegerArgumentType.js')
const LongArgumentType = require('./LongArgumentType.js')
const StringArgumentType = require('./StringArgumentType.js')
module.exports = {
ArgumentType,
BoolArgumentType,
DoubleArgumentType,
FloatArgumentType,
IntegerArgumentType,
LongArgumentType,
StringArgumentType
}

View file

@ -11,12 +11,13 @@ class RequiredArgumentBuilder extends ArgumentBuilder {
} }
static argument (name, type) { static argument (name, type) {
return new RequiredArgumentBuilder(name) return new RequiredArgumentBuilder(name, type)
} }
build () { build () {
const result = new ArgumentCommandNode({ const result = new ArgumentCommandNode({
literal: this.literal, name: this.name,
type: this.type,
command: this.command, command: this.command,
requirement: this.requirement, requirement: this.requirement,
redirect: this.target, redirect: this.target,

View file

@ -42,7 +42,7 @@ class CommandContext {
throw new ReferenceError(`No such argument '${name}' exists on this command`) throw new ReferenceError(`No such argument '${name}' exists on this command`)
} }
return argument return argument.result
} }
} }

View file

@ -1,5 +1,6 @@
const exceptions = require('./exceptions') const exceptions = require('./exceptions')
const StringReader = require('./StringReader.js') const StringReader = require('./StringReader.js')
const _arguments = require('./arguments')
const context = require('./context') const context = require('./context')
const suggestion = require('./suggestion') const suggestion = require('./suggestion')
const tree = require('./tree') const tree = require('./tree')
@ -9,6 +10,7 @@ const LiteralMessage = require('./LiteralMessage.js')
const ParseResults = require('./ParseResults.js') const ParseResults = require('./ParseResults.js')
module.exports = { module.exports = {
arguments: _arguments,
builder, builder,
context, context,
exceptions, exceptions,

View file

@ -29,7 +29,7 @@ class CommandNode {
} else { } else {
this.children[node.name] = node this.children[node.name] = node
if (node instanceof LiteralCommandNode) this.literals[node.name] = node if (node instanceof LiteralCommandNode) this.literals[node.name] = node
else if (node instanceof ArgumentCommandNode) this.literals[node.name] = node else if (node instanceof ArgumentCommandNode) this.arguments[node.name] = node
} }
} }