improve numeric parsers, parser tests

This commit is contained in:
Bixilon 2022-05-18 23:16:31 +02:00
parent 3ab9a67a4c
commit 5f2b5fd847
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
11 changed files with 570 additions and 29 deletions

View File

@ -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 <https://www.gnu.org/licenses/>.
*
* 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)

View File

@ -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<Double> {
override val examples: List<Double> = 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()
}
}
}

View File

@ -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<Float> {
override val examples: List<Float> = 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()
}
}
}

View File

@ -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()
}
}
}

View File

@ -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()
}
}
}

View File

@ -23,8 +23,8 @@ import de.bixilon.minosoft.data.registries.factory.DefaultFactory
object ArgumentParserFactories : DefaultFactory<ArgumentParserFactory<*>>(
BooleanParser,
DoubleParser,
FloatParser,
DoubleParser,
IntParser,
LongParser,
StringParser,

View File

@ -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 <T> readResult(reader: CommandReader.() -> T): ReadResult<T> {
val start = pointer
val result = reader(this)

View File

@ -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 <https://www.gnu.org/licenses/>.
*
* 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<DoubleParseError> { 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<DoubleOutOfRangeError> { 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<DoubleOutOfRangeError> { 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<DoubleOutOfRangeError> { parser.parse(reader) }
}
}

View File

@ -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 <https://www.gnu.org/licenses/>.
*
* 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<FloatParseError> { 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<FloatOutOfRangeError> { 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<FloatOutOfRangeError> { 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<FloatOutOfRangeError> { parser.parse(reader) }
}
}

View File

@ -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 <https://www.gnu.org/licenses/>.
*
* 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<IntParseError> { 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<IntOutOfRangeError> { 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<IntOutOfRangeError> { 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<IntOutOfRangeError> { parser.parse(reader) }
}
}

View File

@ -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 <https://www.gnu.org/licenses/>.
*
* 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<LongParseError> { 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<LongOutOfRangeError> { 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<LongOutOfRangeError> { 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<LongOutOfRangeError> { parser.parse(reader) }
}
}