Refactor exceptions

and maybe other things lol
This commit is contained in:
Chipmunk 2023-08-30 12:43:05 -04:00
parent 03377d0734
commit 1d7d923001
18 changed files with 131 additions and 194 deletions

View file

@ -1,4 +1,4 @@
const { CommandSyntaxError } = require('./errors')
const { CommandSyntaxException } = require('./exceptions')
const SYNTAX_ESCAPE = '\\'
const SYNTAX_DOUBLE_QUOTE = '"'
@ -49,9 +49,9 @@ class StringReader {
this.skip()
}
const number = this.string.substring(start, this.cursor)
if (number === '') throw CommandSyntaxError.BUILT_IN_ERRORS.readerExpectedInt.createWithContext(this)
if (number === '') throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.readerExpectedInt.createWithContext(this)
const _number = Number(number)
if (Number.isNaN(_number) || !Number.isInteger(_number)) throw CommandSyntaxError.BUILT_IN_ERRORS.readerInvalidInt.createWithContext(this, number)
if (Number.isNaN(_number) || !Number.isInteger(_number)) throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.readerInvalidInt.createWithContext(this, number)
return _number
}
@ -61,11 +61,11 @@ class StringReader {
this.skip()
}
const number = this.string.substring(start, this.cursor)
if (number === '') throw CommandSyntaxError.BUILT_IN_ERRORS.readerExpectedLong.createWithContext(this)
if (number === '') throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.readerExpectedLong.createWithContext(this)
try {
return BigInt(number)
} catch (error) {
throw CommandSyntaxError.BUILT_IN_ERRORS.readerInvalidLong.createWithContext(this, number)
throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.readerInvalidLong.createWithContext(this, number)
}
}
@ -75,9 +75,9 @@ class StringReader {
this.skip()
}
const number = this.string.substring(start, this.cursor)
if (number === '') throw CommandSyntaxError.BUILT_IN_ERRORS.readerExpectedDouble.createWithContext(this)
if (number === '') throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.readerExpectedDouble.createWithContext(this)
const _number = Number(number)
if (Number.isNaN(_number)) throw CommandSyntaxError.BUILT_IN_ERRORS.readerInvalidDouble.createWithContext(this, number)
if (Number.isNaN(_number)) throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.readerInvalidDouble.createWithContext(this, number)
return _number
}
@ -87,9 +87,9 @@ class StringReader {
this.skip()
}
const number = this.string.substring(start, this.cursor)
if (number === '') throw CommandSyntaxError.BUILT_IN_ERRORS.readerExpectedFloat.createWithContext(this)
if (number === '') throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.readerExpectedFloat.createWithContext(this)
const _number = Number(number)
if (Number.isNaN(_number)) throw CommandSyntaxError.BUILT_IN_ERRORS.readerInvalidFloat.createWithContext(this, number)
if (Number.isNaN(_number)) throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.readerInvalidFloat.createWithContext(this, number)
return _number
}
@ -112,7 +112,7 @@ class StringReader {
readQuotedString () {
if (!this.canRead) return ''
const next = this.peek()
if (!this.isQuotedString(next)) throw CommandSyntaxError.BUILT_IN_ERRORS.readerExpectedStartOfQuote.createWithContext(this)
if (!this.isQuotedString(next)) throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.readerExpectedStartOfQuote.createWithContext(this)
this.skip()
return this.readStringUntil(next)
}
@ -128,7 +128,7 @@ class StringReader {
escaped = false
} else {
--this.cursor
throw CommandSyntaxError.BUILT_IN_ERRORS.readerInvalidEscape.createWithContext(this)
throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.readerInvalidEscape.createWithContext(this)
}
} else if (c === SYNTAX_ESCAPE) {
escaped = true
@ -139,7 +139,7 @@ class StringReader {
}
}
throw CommandSyntaxError.BUILT_IN_ERRORS.readerExpectedEndOfQuote.createWithContext(this)
throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.readerExpectedEndOfQuote.createWithContext(this)
}
readString () {
@ -155,17 +155,17 @@ class StringReader {
readBoolean () {
const start = this.cursor
const value = this.readString()
if (value === '') throw CommandSyntaxError.BUILT_IN_ERRORS.readerExpectedBool.createWithContext(this)
if (value === '') throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.readerExpectedBool.createWithContext(this)
if (value === 'true') return true
if (value === 'false') return false
this.cursor = start
throw CommandSyntaxError.BUILT_IN_ERRORS.readerInvalidBool.createWithContext(this, value)
throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.readerInvalidBool.createWithContext(this, value)
}
expect (c) {
if (!this.canRead || this.peek() !== c) throw CommandSyntaxError.BUILT_IN_ERRORS.readerExpectedSymbol.createWithContext(this, c)
if (!this.canRead || this.peek() !== c) throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.readerExpectedSymbol.createWithContext(this, c)
this.skip()
}
}

View file

@ -1,17 +0,0 @@
const CommandSyntaxError = require('./CommandSyntaxError.js')
class Dynamic2CommandErrorType {
constructor (_function) {
this.function = _function
}
create (a, b) {
return new CommandSyntaxError(this, this.function(a, b))
}
createWithContext (reader, a, b) {
return new CommandSyntaxError(this, this.function(a, b), reader.string, reader.cursor)
}
}
module.exports = Dynamic2CommandErrorType

View file

@ -1,17 +0,0 @@
const CommandSyntaxError = require('./CommandSyntaxError.js')
class Dynamic3CommandErrorType {
constructor (_function) {
this.function = _function
}
create (a, b, c) {
return new CommandSyntaxError(this, this.function(a, b, c))
}
createWithContext (reader, a, b, c) {
return new CommandSyntaxError(this, this.function(a, b, c), reader.string, reader.cursor)
}
}
module.exports = Dynamic3CommandErrorType

View file

@ -1,17 +0,0 @@
const CommandSyntaxError = require('./CommandSyntaxError.js')
class Dynamic4CommandErrorType {
constructor (_function) {
this.function = _function
}
create (a, b, c, d) {
return new CommandSyntaxError(this, this.function(a, b, c, d))
}
createWithContext (reader, a, b, c, d) {
return new CommandSyntaxError(this, this.function(a, b, c, d), reader.string, reader.cursor)
}
}
module.exports = Dynamic4CommandErrorType

View file

@ -1,17 +0,0 @@
const CommandSyntaxError = require('./CommandSyntaxError.js')
class DynamicCommandErrorType {
constructor (_function) {
this.function = _function
}
create (arg) {
return new CommandSyntaxError(this, this.function(arg))
}
createWithContext (reader, arg) {
return new CommandSyntaxError(this, this.function(arg), reader.string, reader.cursor)
}
}
module.exports = DynamicCommandErrorType

View file

@ -1,17 +0,0 @@
const CommandSyntaxError = require('./CommandSyntaxError.js')
class DynamicNCommandErrorType {
constructor (_function) {
this.function = _function
}
create (...args) {
return new CommandSyntaxError(this, this.function(...args))
}
createWithContext (reader, ...args) {
return new CommandSyntaxError(this, this.function(...args), reader.string, reader.cursor)
}
}
module.exports = DynamicNCommandErrorType

View file

@ -1,19 +0,0 @@
const CommandSyntaxError = require('./CommandSyntaxError.js')
class SimpleCommandErrorType {
constructor (message) {
this.message = message
}
create () {
return new CommandSyntaxError(this, this.message)
}
createWithContext (reader) {
return new CommandSyntaxError(this, this.message, reader.string, reader.cursor)
}
toString () { return this.message.getString() }
}
module.exports = SimpleCommandErrorType

View file

@ -1,40 +0,0 @@
const SimpleCommandErrorType = require('./SimpleCommandErrorType.js')
const DynamicCommandErrorType = require('./DynamicCommandErrorType.js')
const Dynamic2CommandErrorType = require('./Dynamic2CommandErrorType.js')
const LiteralMessage = require('../LiteralMessage.js')
module.exports = {
doubleTooLow: new Dynamic2CommandErrorType((found, min) => new LiteralMessage(`Double must not be less than ${min}, found ${found}`)),
doubleTooHigh: new Dynamic2CommandErrorType((found, max) => new LiteralMessage(`Double must not be more than ${max}, found ${found}`)),
floatTooLow: new Dynamic2CommandErrorType((found, min) => new LiteralMessage(`Float must not be less than ${min}, found ${found}`)),
floatTooHigh: new Dynamic2CommandErrorType((found, max) => new LiteralMessage(`Float must not be more than ${max}, found ${found}`)),
integerTooLow: new Dynamic2CommandErrorType((found, min) => new LiteralMessage(`Integer must not be less than ${min}, found ${found}`)),
integerTooHigh: new Dynamic2CommandErrorType((found, max) => new LiteralMessage(`Integer must not be more than ${max}, found ${found}`)),
longTooLow: new Dynamic2CommandErrorType((found, min) => new LiteralMessage(`Long must not be less than ${min}, found ${found}`)),
longTooHigh: new Dynamic2CommandErrorType((found, max) => new LiteralMessage(`Long must not be more than ${max}, found ${found}`)),
literalIncorrect: new DynamicCommandErrorType(expected => new LiteralMessage(`Expected literal: ${expected}`)),
readerExpectedStartOfQuote: new SimpleCommandErrorType(new LiteralMessage('Expected quote to start a string')),
readerExpectedEndOfQuote: new SimpleCommandErrorType(new LiteralMessage('Unclosed quoted string')),
readerInvalidEscape: new DynamicCommandErrorType(character => new LiteralMessage(`Invalid escape sequence '${character}' in quoted string`)),
readerInvalidBool: new DynamicCommandErrorType(value => new LiteralMessage(`Invalid bool, expected true or false but found '${value}'`)),
readerInvalidInt: new DynamicCommandErrorType(value => new LiteralMessage(`Invalid integer '${value}'`)),
readerExpectedInt: new SimpleCommandErrorType(new LiteralMessage('Expected integer')),
readerInvalidLong: new DynamicCommandErrorType(value => new LiteralMessage(`Invalid long '${value}'`)),
readerExpectedLong: new SimpleCommandErrorType(new LiteralMessage('Expected long')),
readerInvalidDouble: new DynamicCommandErrorType(value => new LiteralMessage(`Invalid double '${value}'`)),
readerExpectedDouble: new SimpleCommandErrorType(new LiteralMessage('Expected double')),
readerInvalidFloat: new DynamicCommandErrorType(value => new LiteralMessage(`Invalid float '${value}'`)),
readerExpectedFloat: new SimpleCommandErrorType(new LiteralMessage('Expected float')),
readerExpectedBool: new SimpleCommandErrorType(new LiteralMessage('Expected bool')),
readerExpectedSymbol: new DynamicCommandErrorType(symbol => new LiteralMessage(`Expected '${symbol}'`)),
dispatcherUnknownCommand: new SimpleCommandErrorType(new LiteralMessage('Unknown command')),
dispatcherUnknownArgument: new SimpleCommandErrorType(new LiteralMessage('Incorrect argument for command')),
dispatcherExpectedArgumentSeperator: new SimpleCommandErrorType(new LiteralMessage('Expected whitespace to end one argument, but found trailing data')),
dispatcherParseException: new DynamicCommandErrorType(message => new LiteralMessage(`Could not parse command: ${message}`))
}

View file

@ -1,12 +0,0 @@
const BUILT_IN_ERRORS = require('./built_in_errors.js')
const CommandSyntaxError = require('./CommandSyntaxError.js')
const Dynamic2CommandErrorType = require('./Dynamic2CommandErrorType.js')
const Dynamic3CommandErrorType = require('./Dynamic3CommandErrorType.js')
const Dynamic4CommandErrorType = require('./Dynamic4CommandErrorType.js')
const DynamicCommandErrorType = require('./DynamicCommandErrorType.js')
const DynamicNCommandErrorType = require('./DynamicNCommandErrorType.js')
const SimpleCommandErrorType = require('./SimpleCommandErrorType.js')
CommandSyntaxError.BUILT_IN_ERRORS = BUILT_IN_ERRORS
module.exports = { CommandSyntaxError, Dynamic2CommandErrorType, Dynamic3CommandErrorType, Dynamic4CommandErrorType, DynamicCommandErrorType, DynamicNCommandErrorType, SimpleCommandErrorType }

View file

@ -1,11 +1,11 @@
class CommandSyntaxError extends Error {
class CommandSyntaxException extends Error {
static CONTEXT_AMOUNT = 10
static ENABLE_COMMAND_STACK_TRACES = true
constructor (type, message, input = null, cursor = -1) {
super()
if (!CommandSyntaxError.ENABLE_COMMAND_STACK_TRACES) delete this.stack
this.name = 'CommandSyntaxError'
if (!CommandSyntaxException.ENABLE_COMMAND_STACK_TRACES) delete this.stack
this.name = 'CommandSyntaxException'
this.type = type
this._message = message
@ -25,13 +25,13 @@ class CommandSyntaxError extends Error {
let context = ''
const cursor = Math.min(this.input.length, this.cursor)
if (cursor > CommandSyntaxError.CONTEXT_AMOUNT) context += '...'
if (cursor > CommandSyntaxException.CONTEXT_AMOUNT) context += '...'
context += this.input.substring(Math.max(0, cursor - CommandSyntaxError.CONTEXT_AMOUNT), cursor)
context += this.input.substring(Math.max(0, cursor - CommandSyntaxException.CONTEXT_AMOUNT), cursor)
context += '<--[HERE]'
return context
}
}
module.exports = CommandSyntaxError
module.exports = CommandSyntaxException

View file

@ -0,0 +1,17 @@
const CommandSyntaxException = require('./CommandSyntaxException.js')
class DynamicCommandExceptionType {
constructor (_function) {
this.function = _function
}
create (...args) {
return new CommandSyntaxException(this, this.function(...args))
}
createWithContext (reader, ...args) {
return new CommandSyntaxException(this, this.function(...args), reader.string, reader.cursor)
}
}
module.exports = DynamicCommandExceptionType

View file

@ -0,0 +1,19 @@
const CommandSyntaxException = require('./CommandSyntaxException.js')
class SimpleCommandExceptionType {
constructor (message) {
this.message = message
}
create () {
return new CommandSyntaxException(this, this.message)
}
createWithContext (reader) {
return new CommandSyntaxException(this, this.message, reader.string, reader.cursor)
}
toString () { return this.message.getString() }
}
module.exports = SimpleCommandExceptionType

View file

@ -0,0 +1,39 @@
const SimpleCommandExceptionType = require('./SimpleCommandExceptionType.js')
const DynamicCommandExceptionType = require('./DynamicCommandExceptionType.js')
const LiteralMessage = require('../LiteralMessage.js')
module.exports = {
doubleTooLow: new DynamicCommandExceptionType((found, min) => new LiteralMessage(`Double must not be less than ${min}, found ${found}`)),
doubleTooHigh: new DynamicCommandExceptionType((found, max) => new LiteralMessage(`Double must not be more than ${max}, found ${found}`)),
floatTooLow: new DynamicCommandExceptionType((found, min) => new LiteralMessage(`Float must not be less than ${min}, found ${found}`)),
floatTooHigh: new DynamicCommandExceptionType((found, max) => new LiteralMessage(`Float must not be more than ${max}, found ${found}`)),
integerTooLow: new DynamicCommandExceptionType((found, min) => new LiteralMessage(`Integer must not be less than ${min}, found ${found}`)),
integerTooHigh: new DynamicCommandExceptionType((found, max) => new LiteralMessage(`Integer must not be more than ${max}, found ${found}`)),
longTooLow: new DynamicCommandExceptionType((found, min) => new LiteralMessage(`Long must not be less than ${min}, found ${found}`)),
longTooHigh: new DynamicCommandExceptionType((found, max) => new LiteralMessage(`Long must not be more than ${max}, found ${found}`)),
literalIncorrect: new DynamicCommandExceptionType(expected => new LiteralMessage(`Expected literal: ${expected}`)),
readerExpectedStartOfQuote: new SimpleCommandExceptionType(new LiteralMessage('Expected quote to start a string')),
readerExpectedEndOfQuote: new SimpleCommandExceptionType(new LiteralMessage('Unclosed quoted string')),
readerInvalidEscape: new DynamicCommandExceptionType(character => new LiteralMessage(`Invalid escape sequence '${character}' in quoted string`)),
readerInvalidBool: new DynamicCommandExceptionType(value => new LiteralMessage(`Invalid bool, expected true or false but found '${value}'`)),
readerInvalidInt: new DynamicCommandExceptionType(value => new LiteralMessage(`Invalid integer '${value}'`)),
readerExpectedInt: new SimpleCommandExceptionType(new LiteralMessage('Expected integer')),
readerInvalidLong: new DynamicCommandExceptionType(value => new LiteralMessage(`Invalid long '${value}'`)),
readerExpectedLong: new SimpleCommandExceptionType(new LiteralMessage('Expected long')),
readerInvalidDouble: new DynamicCommandExceptionType(value => new LiteralMessage(`Invalid double '${value}'`)),
readerExpectedDouble: new SimpleCommandExceptionType(new LiteralMessage('Expected double')),
readerInvalidFloat: new DynamicCommandExceptionType(value => new LiteralMessage(`Invalid float '${value}'`)),
readerExpectedFloat: new SimpleCommandExceptionType(new LiteralMessage('Expected float')),
readerExpectedBool: new SimpleCommandExceptionType(new LiteralMessage('Expected bool')),
readerExpectedSymbol: new DynamicCommandExceptionType(symbol => new LiteralMessage(`Expected '${symbol}'`)),
dispatcherUnknownCommand: new SimpleCommandExceptionType(new LiteralMessage('Unknown command')),
dispatcherUnknownArgument: new SimpleCommandExceptionType(new LiteralMessage('Incorrect argument for command')),
dispatcherExpectedArgumentSeperator: new SimpleCommandExceptionType(new LiteralMessage('Expected whitespace to end one argument, but found trailing data')),
dispatcherParseException: new DynamicCommandExceptionType(message => new LiteralMessage(`Could not parse command: ${message}`))
}

8
lib/exceptions/index.js Normal file
View file

@ -0,0 +1,8 @@
const BUILT_IN_EXCEPTIONS = require('./built_in_exceptions.js')
const CommandSyntaxException = require('./CommandSyntaxException.js')
const DynamicCommandExceptionType = require('./DynamicCommandExceptionType.js')
const SimpleCommandExceptionType = require('./SimpleCommandExceptionType.js')
CommandSyntaxException.BUILT_IN_EXCEPTIONS = BUILT_IN_EXCEPTIONS
module.exports = { CommandSyntaxException, DynamicCommandExceptionType, SimpleCommandExceptionType }

View file

@ -1,7 +1,9 @@
const errors = require('./errors')
const exceptions = require('./exceptions')
const tree = require('./tree')
const StringReader = require('./StringReader.js')
module.exports = {
errors,
StringReader
StringReader,
exceptions,
tree
}

View file

@ -9,7 +9,7 @@ class CommandNode {
this.command = command
this.requirement = requirement
this.redirect = redirect
this.requirement = requirement
this.redirectModifier = redirectModifier
this.forks = forks
}
@ -24,7 +24,7 @@ class CommandNode {
if (child !== undefined) {
// We've found something to merge onto
if (node.command !== undefined) child.command = command
if (node.command !== undefined) child.command = this.command
for (const grandchild of node.getChildren()) child.addChild(grandchild)
} else {
this.children[node.name] = node
@ -34,7 +34,7 @@ class CommandNode {
}
findAmbiguities (consumer) {
const matches = new Set()
let matches = new Set()
for (const child of Object.values(this.children)) {
for (const sibling of Object.values(this.children)) {
@ -60,8 +60,15 @@ class CommandNode {
if (this === that) return true
if (!(that instanceof CommandNode)) return false
if (this.children !== that.children) return false // ?: Am I checking this correctly?
if (this.command !== undefined ? this.command !== that.command : that.command !== undefined) return false
const childrenEntries = Object.entries(this.children)
if (childrenEntries.length !== Object.entries(that.children).length) return false
for (const entry of childrenEntries) {
if (!Object.hasOwn(that, entry.key) || !entry.value.equals(that[entry.key])) {
return false
}
}
if (this.command !== that.command) return false
return true
}
@ -69,12 +76,12 @@ class CommandNode {
// TODO: hashCode
getRelaventNodes (input) {
if (Object.keys(literals).length > 0) {
if (Object.keys(this.literals).length > 0) {
const cursor = input.cursor
while (input.canRead() && input.peek() !== ' ') input.skip()
const text = input.string.substring(cursor, input.cursor)
input.cursor = cursor
const literal = literals[text]
const literal = this.literals[text]
if (literal !== undefined) return [literal]
return Object.values(this.arguments)
}
@ -90,9 +97,9 @@ class CommandNode {
return (object instanceof LiteralCommandNode) ? 1 : -1
}
static _setChildClasses (c) { // ? Is there a better way to do this?
static _setSubclasses (c) { // ? Is there a better way to do this?
({ ArgumentCommandNode, LiteralCommandNode, RootCommandNode } = c)
delete CommandNode._setChildClasses
delete CommandNode._setSubclasses
}
}

View file

@ -1,9 +1,10 @@
const CommandNode = require('./CommandNode.js')
const StringReader = require('../StringReader.js')
const { CommandSyntaxError } = require('../errors')
const { CommandSyntaxException } = require('../exceptions')
const StringRange = require('../context/StringRange.js')
class LiteralCommandNode extends CommandNode {
constructor ({ command, requirement, redirect, redirectModifier, forks }) {
constructor ({ literal, command, requirement, redirect, redirectModifier, forks }) {
super({ command, requirement, redirect, redirectModifier, forks })
this.literal = literal
this.literalLowerCase = literal.toLowerCase()
@ -15,11 +16,11 @@ class LiteralCommandNode extends CommandNode {
const start = reader.cursor
const end = this.#parse(reader)
if (end > -1) {
contextBuilder.withNode(this /* StringRange.between(start, end) */)
contextBuilder.withNode(this, StringRange.between(start, end))
return
}
throw CommandSyntaxError.BUILT_IN_ERRORS.literalIncorrect.createWithContext(reader, literal)
throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.literalIncorrect.createWithContext(reader, this.literal)
}
#parse (reader) {

View file

@ -5,4 +5,4 @@ const RootCommandNode = require('./RootCommandNode.js')
module.exports = { CommandNode, ArgumentCommandNode, LiteralCommandNode, RootCommandNode }
CommandNode._setChildClasses(module.exports)
CommandNode._setSubclasses(module.exports)