diff --git a/src/main/java/de/bixilon/minosoft/commands/errors/reader/number/NegativeNumberError.kt b/src/main/java/de/bixilon/minosoft/commands/errors/reader/number/NegativeNumberError.kt new file mode 100644 index 000000000..cf377649d --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/commands/errors/reader/number/NegativeNumberError.kt @@ -0,0 +1,22 @@ +/* + * Minosoft + * Copyright (C) 2020-2022 Moritz Zwerger + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.commands.errors.reader.number + +import de.bixilon.minosoft.commands.errors.ReaderError +import de.bixilon.minosoft.commands.util.CommandReader + +class NegativeNumberError( + reader: CommandReader, + pointer: Int, +) : ReaderError(reader, pointer, pointer + 1) diff --git a/src/main/java/de/bixilon/minosoft/commands/parser/brigadier/_double/DoubleParser.kt b/src/main/java/de/bixilon/minosoft/commands/parser/brigadier/_double/DoubleParser.kt index 88316c774..5e5f840db 100644 --- a/src/main/java/de/bixilon/minosoft/commands/parser/brigadier/_double/DoubleParser.kt +++ b/src/main/java/de/bixilon/minosoft/commands/parser/brigadier/_double/DoubleParser.kt @@ -24,7 +24,7 @@ import de.bixilon.minosoft.util.BitByte.isBitMask import de.bixilon.minosoft.util.KUtil.toResourceLocation class DoubleParser( - val min: Double = Double.MIN_VALUE, + val min: Double = -Double.MAX_VALUE, val max: Double = Double.MAX_VALUE, ) : BrigadierParser { override val examples: List = listOf(1.0, -1.0, 1000.0) @@ -54,18 +54,13 @@ class DoubleParser( override fun read(buffer: PlayInByteBuffer): DoubleParser { val flags = buffer.readUnsignedByte() - val min = if (flags.isBitMask(0x01)) buffer.readDouble() else Double.MIN_VALUE + val min = if (flags.isBitMask(0x01)) buffer.readDouble() else -Double.MAX_VALUE val max = if (flags.isBitMask(0x03)) buffer.readDouble() else Double.MAX_VALUE return DoubleParser(min = min, max = max) } fun CommandReader.readDouble(): Double? { - val string = readUnquotedString() ?: return null - return try { - string.toDouble() - } catch (exception: NumberFormatException) { - null - } + return readNumeric()?.toDoubleOrNull() } } } diff --git a/src/main/java/de/bixilon/minosoft/commands/parser/brigadier/_float/FloatParser.kt b/src/main/java/de/bixilon/minosoft/commands/parser/brigadier/_float/FloatParser.kt index 0805e7f39..b473f2304 100644 --- a/src/main/java/de/bixilon/minosoft/commands/parser/brigadier/_float/FloatParser.kt +++ b/src/main/java/de/bixilon/minosoft/commands/parser/brigadier/_float/FloatParser.kt @@ -24,7 +24,7 @@ import de.bixilon.minosoft.util.BitByte.isBitMask import de.bixilon.minosoft.util.KUtil.toResourceLocation class FloatParser( - val min: Float = Float.MIN_VALUE, + val min: Float = -Float.MAX_VALUE, val max: Float = Float.MAX_VALUE, ) : BrigadierParser { override val examples: List = listOf(1.0f, -1.0f, 1000.0f) @@ -54,18 +54,13 @@ class FloatParser( override fun read(buffer: PlayInByteBuffer): FloatParser { val flags = buffer.readUnsignedByte() - val min = if (flags.isBitMask(0x01)) buffer.readFloat() else Float.MIN_VALUE + val min = if (flags.isBitMask(0x01)) buffer.readFloat() else -Float.MAX_VALUE val max = if (flags.isBitMask(0x03)) buffer.readFloat() else Float.MAX_VALUE return FloatParser(min = min, max = max) } fun CommandReader.readFloat(): Float? { - val string = readUnquotedString() ?: return null - return try { - string.toFloat() - } catch (exception: NumberFormatException) { - null - } + return readNumeric()?.toFloatOrNull() } } } diff --git a/src/main/java/de/bixilon/minosoft/commands/parser/brigadier/_int/IntParser.kt b/src/main/java/de/bixilon/minosoft/commands/parser/brigadier/_int/IntParser.kt index 310b87965..925b98b29 100644 --- a/src/main/java/de/bixilon/minosoft/commands/parser/brigadier/_int/IntParser.kt +++ b/src/main/java/de/bixilon/minosoft/commands/parser/brigadier/_int/IntParser.kt @@ -60,12 +60,7 @@ class IntParser( } fun CommandReader.readInt(): Int? { - val string = readUnquotedString() ?: return null - return try { - string.toInt() - } catch (exception: NumberFormatException) { - null - } + return readNumeric(decimal = false)?.toIntOrNull() } } } diff --git a/src/main/java/de/bixilon/minosoft/commands/parser/brigadier/_long/LongParser.kt b/src/main/java/de/bixilon/minosoft/commands/parser/brigadier/_long/LongParser.kt index cb6d0c20c..8c838db85 100644 --- a/src/main/java/de/bixilon/minosoft/commands/parser/brigadier/_long/LongParser.kt +++ b/src/main/java/de/bixilon/minosoft/commands/parser/brigadier/_long/LongParser.kt @@ -60,12 +60,7 @@ class LongParser( } fun CommandReader.readLong(): Long? { - val string = readUnquotedString() ?: return null - return try { - string.toLong() - } catch (exception: NumberFormatException) { - null - } + return readNumeric(decimal = false)?.toLongOrNull() } } } diff --git a/src/main/java/de/bixilon/minosoft/commands/parser/factory/ArgumentParserFactories.kt b/src/main/java/de/bixilon/minosoft/commands/parser/factory/ArgumentParserFactories.kt index 3c2aad8b9..3f4eefb35 100644 --- a/src/main/java/de/bixilon/minosoft/commands/parser/factory/ArgumentParserFactories.kt +++ b/src/main/java/de/bixilon/minosoft/commands/parser/factory/ArgumentParserFactories.kt @@ -23,8 +23,8 @@ import de.bixilon.minosoft.data.registries.factory.DefaultFactory object ArgumentParserFactories : DefaultFactory>( BooleanParser, - DoubleParser, FloatParser, + DoubleParser, IntParser, LongParser, StringParser, diff --git a/src/main/java/de/bixilon/minosoft/commands/util/CommandReader.kt b/src/main/java/de/bixilon/minosoft/commands/util/CommandReader.kt index e54e83d84..2f6cec19a 100644 --- a/src/main/java/de/bixilon/minosoft/commands/util/CommandReader.kt +++ b/src/main/java/de/bixilon/minosoft/commands/util/CommandReader.kt @@ -14,6 +14,7 @@ package de.bixilon.minosoft.commands.util import de.bixilon.minosoft.commands.errors.reader.* +import de.bixilon.minosoft.commands.errors.reader.number.NegativeNumberError open class CommandReader(val string: String) { var pointer = 0 @@ -205,6 +206,54 @@ open class CommandReader(val string: String) { return string } + fun readUntil(vararg chars: Int, required: Boolean = false): String? { + if (!canPeekNext()) { + return null + } + val builder = StringBuilder() + while (true) { + val peek = peek() + if (peek == null) { + if (required) { + throw OutOfBoundsError(this, length - 1) + } + return builder.toString() + } + if (peek in chars) { + return builder.toString() + } + builder.appendCodePoint(peek) + pointer++ + } + } + + fun readNumeric(decimal: Boolean = true, negative: Boolean = true): String? { + if (!canPeekNext()) { + return null + } + val builder = StringBuilder() + while (true) { + val peek = peek() ?: break + if (peek in '0'.code..'9'.code) { + builder.appendCodePoint(peek) + pointer++ + continue + } else if (peek == '-'.code && builder.isEmpty()) { + if (!negative) { + throw NegativeNumberError(this, pointer) + } + builder.appendCodePoint(peek) + pointer++ + } else if (decimal && peek == '.'.code && '.' !in builder) { + builder.appendCodePoint(peek) + pointer++ + } else { + break + } + } + return builder.toString() + } + fun readResult(reader: CommandReader.() -> T): ReadResult { val start = pointer val result = reader(this) diff --git a/src/test/java/de/bixilon/minosoft/commands/parser/brigadier/_double/DoubleParserTest.kt b/src/test/java/de/bixilon/minosoft/commands/parser/brigadier/_double/DoubleParserTest.kt new file mode 100644 index 000000000..edb441899 --- /dev/null +++ b/src/test/java/de/bixilon/minosoft/commands/parser/brigadier/_double/DoubleParserTest.kt @@ -0,0 +1,130 @@ +/* + * Minosoft + * Copyright (C) 2020-2022 Moritz Zwerger + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.commands.parser.brigadier._double + +import de.bixilon.minosoft.commands.util.CommandReader +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import kotlin.test.assertEquals + +internal class DoubleParserTest { + + @Test + fun readNormal() { + val reader = CommandReader("1") + val parser = DoubleParser() + assertEquals(parser.parse(reader), 1.0) + } + + @Test + fun readNegative() { + val reader = CommandReader("-1") + val parser = DoubleParser() + assertEquals(parser.parse(reader), -1.0) + } + + @Test + fun readTwice() { + val reader = CommandReader("5,1") + val parser = DoubleParser() + assertEquals(parser.parse(reader), 5.0) + reader.read() + assertEquals(parser.parse(reader), 1.0) + } + + @Test + fun readDecimal() { + val reader = CommandReader("1.1") + val parser = DoubleParser() + assertEquals(parser.parse(reader), 1.1) + } + + @Test + fun readNegativeDecimal() { + val reader = CommandReader("-1.1") + val parser = DoubleParser() + assertEquals(parser.parse(reader), -1.1) + } + + + @Test + fun readNoDecimal() { + val reader = CommandReader("-1.0") + val parser = DoubleParser() + assertEquals(parser.parse(reader), -1.0) + } + + @Test + fun readInvalid() { + val reader = CommandReader("abc") + val parser = DoubleParser() + assertThrows { parser.parse(reader) } + } + + @Test + fun readBigNumber() { + val reader = CommandReader("123456.7891") + val parser = DoubleParser() + assertEquals(parser.parse(reader), 123456.7891) + } + + @Test + fun readNegativeBigNumber() { + val reader = CommandReader("-123456.7891") + val parser = DoubleParser() + assertEquals(parser.parse(reader), -123456.7891) + } + + @Test + fun checkMin() { + val reader = CommandReader("5") + val parser = DoubleParser(min = 0.0) + assertEquals(parser.parse(reader), 5.0) + } + + @Test + fun checkMinError() { + val reader = CommandReader("-2") + val parser = DoubleParser(min = 0.0) + assertThrows { parser.parse(reader) } + } + + @Test + fun checkMax() { + val reader = CommandReader("3") + val parser = DoubleParser(max = 5.0) + assertEquals(parser.parse(reader), 3.0) + } + + @Test + fun checkMaxError() { + val reader = CommandReader("10") + val parser = DoubleParser(max = 8.0) + assertThrows { parser.parse(reader) } + } + + @Test + fun checkMinMax() { + val reader = CommandReader("4") + val parser = DoubleParser(min = 3.0, max = 5.0) + assertEquals(parser.parse(reader), 4.0) + } + + @Test + fun checkMinMaxError() { + val reader = CommandReader("1") + val parser = DoubleParser(min = 2.0, max = 8.0) + assertThrows { parser.parse(reader) } + } +} diff --git a/src/test/java/de/bixilon/minosoft/commands/parser/brigadier/_float/FloatParserTest.kt b/src/test/java/de/bixilon/minosoft/commands/parser/brigadier/_float/FloatParserTest.kt new file mode 100644 index 000000000..ad6f330e8 --- /dev/null +++ b/src/test/java/de/bixilon/minosoft/commands/parser/brigadier/_float/FloatParserTest.kt @@ -0,0 +1,130 @@ +/* + * Minosoft + * Copyright (C) 2020-2022 Moritz Zwerger + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.commands.parser.brigadier._float + +import de.bixilon.minosoft.commands.util.CommandReader +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import kotlin.test.assertEquals + +internal class FloatParserTest { + + @Test + fun readNormal() { + val reader = CommandReader("1") + val parser = FloatParser() + assertEquals(parser.parse(reader), 1.0f) + } + + @Test + fun readNegative() { + val reader = CommandReader("-1") + val parser = FloatParser() + assertEquals(parser.parse(reader), -1.0f) + } + + @Test + fun readTwice() { + val reader = CommandReader("5,1") + val parser = FloatParser() + assertEquals(parser.parse(reader), 5.0f) + reader.read() + assertEquals(parser.parse(reader), 1.0f) + } + + @Test + fun readDecimal() { + val reader = CommandReader("1.1") + val parser = FloatParser() + assertEquals(parser.parse(reader), 1.1f) + } + + @Test + fun readNegativeDecimal() { + val reader = CommandReader("-1.1") + val parser = FloatParser() + assertEquals(parser.parse(reader), -1.1f) + } + + + @Test + fun readNoDecimal() { + val reader = CommandReader("-1.0") + val parser = FloatParser() + assertEquals(parser.parse(reader), -1.0f) + } + + @Test + fun readInvalid() { + val reader = CommandReader("abc") + val parser = FloatParser() + assertThrows { parser.parse(reader) } + } + + @Test + fun readBigNumber() { + val reader = CommandReader("123456.7891") + val parser = FloatParser() + assertEquals(parser.parse(reader), 123456.7891f) + } + + @Test + fun readNegativeBigNumber() { + val reader = CommandReader("-123456.7891") + val parser = FloatParser() + assertEquals(parser.parse(reader), -123456.7891f) + } + + @Test + fun checkMin() { + val reader = CommandReader("5") + val parser = FloatParser(min = 0.0f) + assertEquals(parser.parse(reader), 5.0f) + } + + @Test + fun checkMinError() { + val reader = CommandReader("-2") + val parser = FloatParser(min = 0.0f) + assertThrows { parser.parse(reader) } + } + + @Test + fun checkMax() { + val reader = CommandReader("3") + val parser = FloatParser(max = 5.0f) + assertEquals(parser.parse(reader), 3.0f) + } + + @Test + fun checkMaxError() { + val reader = CommandReader("10") + val parser = FloatParser(max = 8.0f) + assertThrows { parser.parse(reader) } + } + + @Test + fun checkMinMax() { + val reader = CommandReader("4") + val parser = FloatParser(min = 3.0f, max = 5.0f) + assertEquals(parser.parse(reader), 4.0f) + } + + @Test + fun checkMinMaxError() { + val reader = CommandReader("1") + val parser = FloatParser(min = 2.0f, max = 8.0f) + assertThrows { parser.parse(reader) } + } +} diff --git a/src/test/java/de/bixilon/minosoft/commands/parser/brigadier/_int/IntParserTest.kt b/src/test/java/de/bixilon/minosoft/commands/parser/brigadier/_int/IntParserTest.kt new file mode 100644 index 000000000..a2599192b --- /dev/null +++ b/src/test/java/de/bixilon/minosoft/commands/parser/brigadier/_int/IntParserTest.kt @@ -0,0 +1,115 @@ +/* + * Minosoft + * Copyright (C) 2020-2022 Moritz Zwerger + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.commands.parser.brigadier._int + +import de.bixilon.minosoft.commands.util.CommandReader +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import kotlin.test.assertEquals + +internal class IntParserTest { + + @Test + fun readNormal() { + val reader = CommandReader("1") + val parser = IntParser() + assertEquals(parser.parse(reader), 1) + } + + @Test + fun readNegative() { + val reader = CommandReader("-1") + val parser = IntParser() + assertEquals(parser.parse(reader), -1) + } + + @Test + fun readTwice() { + val reader = CommandReader("5,1") + val parser = IntParser() + assertEquals(parser.parse(reader), 5) + reader.read() + assertEquals(parser.parse(reader), 1) + } + + @Test + fun readDecimal() { + val reader = CommandReader("-1.1") + val parser = IntParser() + assertEquals(parser.parse(reader), -1) + } + + @Test + fun readInvalid() { + val reader = CommandReader("abc") + val parser = IntParser() + assertThrows { parser.parse(reader) } + } + + @Test + fun readBigNumber() { + val reader = CommandReader("1234567891") + val parser = IntParser() + assertEquals(parser.parse(reader), 1234567891) + } + + @Test + fun readNegativeBigNumber() { + val reader = CommandReader("-1234567891") + val parser = IntParser() + assertEquals(parser.parse(reader), -1234567891) + } + + @Test + fun checkMin() { + val reader = CommandReader("5") + val parser = IntParser(min = 0) + assertEquals(parser.parse(reader), 5) + } + + @Test + fun checkMinError() { + val reader = CommandReader("-2") + val parser = IntParser(min = 0) + assertThrows { parser.parse(reader) } + } + + @Test + fun checkMax() { + val reader = CommandReader("3") + val parser = IntParser(max = 5) + assertEquals(parser.parse(reader), 3) + } + + @Test + fun checkMaxError() { + val reader = CommandReader("10") + val parser = IntParser(max = 8) + assertThrows { parser.parse(reader) } + } + + @Test + fun checkMinMax() { + val reader = CommandReader("4") + val parser = IntParser(min = 3, max = 5) + assertEquals(parser.parse(reader), 4) + } + + @Test + fun checkMinMaxError() { + val reader = CommandReader("1") + val parser = IntParser(min = 2, max = 8) + assertThrows { parser.parse(reader) } + } +} diff --git a/src/test/java/de/bixilon/minosoft/commands/parser/brigadier/_long/LongParserTest.kt b/src/test/java/de/bixilon/minosoft/commands/parser/brigadier/_long/LongParserTest.kt new file mode 100644 index 000000000..b10416243 --- /dev/null +++ b/src/test/java/de/bixilon/minosoft/commands/parser/brigadier/_long/LongParserTest.kt @@ -0,0 +1,115 @@ +/* + * Minosoft + * Copyright (C) 2020-2022 Moritz Zwerger + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.commands.parser.brigadier._long + +import de.bixilon.minosoft.commands.util.CommandReader +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import kotlin.test.assertEquals + +internal class LongParserTest { + + @Test + fun readNormal() { + val reader = CommandReader("1") + val parser = LongParser() + assertEquals(parser.parse(reader), 1) + } + + @Test + fun readNegative() { + val reader = CommandReader("-1") + val parser = LongParser() + assertEquals(parser.parse(reader), -1) + } + + @Test + fun readTwice() { + val reader = CommandReader("5,1") + val parser = LongParser() + assertEquals(parser.parse(reader), 5) + reader.read() + assertEquals(parser.parse(reader), 1) + } + + @Test + fun readDecimal() { + val reader = CommandReader("-1.1") + val parser = LongParser() + assertEquals(parser.parse(reader), -1) + } + + @Test + fun readInvalid() { + val reader = CommandReader("abc") + val parser = LongParser() + assertThrows { parser.parse(reader) } + } + + @Test + fun readBigNumber() { + val reader = CommandReader("123456789234567891") + val parser = LongParser() + assertEquals(parser.parse(reader), 123456789234567891L) + } + + @Test + fun readNegativeBigNumber() { + val reader = CommandReader("-123456789234567891") + val parser = LongParser() + assertEquals(parser.parse(reader), -123456789234567891L) + } + + @Test + fun checkMin() { + val reader = CommandReader("5") + val parser = LongParser(min = 0) + assertEquals(parser.parse(reader), 5) + } + + @Test + fun checkMinError() { + val reader = CommandReader("-2") + val parser = LongParser(min = 0) + assertThrows { parser.parse(reader) } + } + + @Test + fun checkMax() { + val reader = CommandReader("3") + val parser = LongParser(max = 5) + assertEquals(parser.parse(reader), 3) + } + + @Test + fun checkMaxError() { + val reader = CommandReader("10") + val parser = LongParser(max = 8) + assertThrows { parser.parse(reader) } + } + + @Test + fun checkMinMax() { + val reader = CommandReader("4") + val parser = LongParser(min = 3, max = 5) + assertEquals(parser.parse(reader), 4) + } + + @Test + fun checkMinMaxError() { + val reader = CommandReader("1") + val parser = LongParser(min = 2, max = 8) + assertThrows { parser.parse(reader) } + } +}