mirror of
https://github.com/Myzel394/NumberHub.git
synced 2025-06-18 16:25:27 +02:00
Fix #177
This commit is contained in:
parent
32c8f3184d
commit
4a499b8fd3
@ -126,7 +126,7 @@ object Token {
|
||||
}
|
||||
|
||||
val expressionTokens by lazy {
|
||||
Digit.allWithDot + Operator.all + Func.all + Const.all
|
||||
Digit.allWithDot + Operator.all + Func.all + Const.all + DisplayOnly.engineeringE
|
||||
}
|
||||
|
||||
val numberBaseTokens by lazy {
|
||||
|
@ -54,11 +54,6 @@ fun String.formatExpression(
|
||||
): String {
|
||||
var input = this
|
||||
|
||||
// Don't do anything to engineering string.
|
||||
if (input.contains(Token.DisplayOnly.engineeringE)) {
|
||||
return input.replace(Token.Digit.dot, formatterSymbols.fractional)
|
||||
}
|
||||
|
||||
// Fractional
|
||||
if (input.contains(Token.DisplayOnly.fraction)) {
|
||||
// Only format integral part
|
||||
@ -77,8 +72,9 @@ fun String.formatExpression(
|
||||
input = input.replace(it, it.formatNumber(formatterSymbols))
|
||||
}
|
||||
|
||||
Token.sexyToUgly.forEach { (token, ugliness) ->
|
||||
ugliness.forEach { uglySymbol ->
|
||||
// Replace ugly symbols
|
||||
Token.sexyToUgly.forEach { (token, uglySymbols) ->
|
||||
uglySymbols.forEach { uglySymbol ->
|
||||
input = input.replace(uglySymbol, token)
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,8 @@ import org.junit.Test
|
||||
|
||||
private const val ENG_VALUE = "123E+21"
|
||||
private const val ENG_VALUE_FRACTIONAL = "123.3E+21"
|
||||
private const val ENG_VALUE_EXPRESSION = "123E+21+(123456.789)"
|
||||
private const val ENG_VALUE_FRACTIONAL_EXPRESSION = "123.3E+21+(123456.789)"
|
||||
private const val COMPLETE_VALUE = "123456.789"
|
||||
private const val INCOMPLETE_VALUE = "123456."
|
||||
private const val NO_FRACTIONAL_VALUE = "123456"
|
||||
@ -41,6 +43,8 @@ class FormatterExpressionTest {
|
||||
fun String.format(): String = formatExpression(FormatterSymbols.Spaces)
|
||||
assertEquals("123E+21", ENG_VALUE.format())
|
||||
assertEquals("123.3E+21", ENG_VALUE_FRACTIONAL.format())
|
||||
assertEquals("123E+21+(123 456.789)", ENG_VALUE_EXPRESSION.format())
|
||||
assertEquals("123.3E+21+(123 456.789)", ENG_VALUE_FRACTIONAL_EXPRESSION.format())
|
||||
assertEquals("123 456.789", COMPLETE_VALUE.format())
|
||||
assertEquals("123 456.", INCOMPLETE_VALUE.format())
|
||||
assertEquals("123 456", NO_FRACTIONAL_VALUE.format())
|
||||
@ -56,6 +60,8 @@ class FormatterExpressionTest {
|
||||
fun String.format(): String = formatExpression(FormatterSymbols.Comma)
|
||||
assertEquals("123E+21", ENG_VALUE.format())
|
||||
assertEquals("123.3E+21", ENG_VALUE_FRACTIONAL.format())
|
||||
assertEquals("123E+21+(123,456.789)", ENG_VALUE_EXPRESSION.format())
|
||||
assertEquals("123.3E+21+(123,456.789)", ENG_VALUE_FRACTIONAL_EXPRESSION.format())
|
||||
assertEquals("123,456.789", COMPLETE_VALUE.format())
|
||||
assertEquals("123,456.", INCOMPLETE_VALUE.format())
|
||||
assertEquals("123,456", NO_FRACTIONAL_VALUE.format())
|
||||
@ -71,6 +77,8 @@ class FormatterExpressionTest {
|
||||
fun String.format(): String = formatExpression(FormatterSymbols.Period)
|
||||
assertEquals("123E+21", ENG_VALUE.format())
|
||||
assertEquals("123,3E+21", ENG_VALUE_FRACTIONAL.format())
|
||||
assertEquals("123E+21+(123.456,789)", ENG_VALUE_EXPRESSION.format())
|
||||
assertEquals("123,3E+21+(123.456,789)", ENG_VALUE_FRACTIONAL_EXPRESSION.format())
|
||||
assertEquals("123.456,789", COMPLETE_VALUE.format())
|
||||
assertEquals("123.456,", INCOMPLETE_VALUE.format())
|
||||
assertEquals("123.456", NO_FRACTIONAL_VALUE.format())
|
||||
|
@ -20,8 +20,10 @@ package io.github.sadellie.evaluatto
|
||||
|
||||
import com.sadellie.unitto.core.base.Token
|
||||
|
||||
sealed class TokenizerException(override val message: String) : Exception(message) {
|
||||
class BadNumber : TokenizerException("Number has multiple commas in it")
|
||||
sealed class TokenizerException(message: String) : Exception(message) {
|
||||
class TooManyFractionSymbols : TokenizerException("Number has multiple commas in it")
|
||||
class FailedToUnpackNumber : TokenizerException("Unexpected token before percentage")
|
||||
class BadScientificNotation : TokenizerException("Expected plus or minus symbol after \"E\"")
|
||||
}
|
||||
|
||||
class Tokenizer(private val streamOfTokens: String) {
|
||||
@ -60,7 +62,7 @@ class Tokenizer(private val streamOfTokens: String) {
|
||||
.takeWhile { Token.Digit.allWithDot.contains(it.toString()) }
|
||||
|
||||
if (number.count { it.toString() == Token.Digit.dot } > 1) {
|
||||
throw TokenizerException.BadNumber()
|
||||
throw TokenizerException.TooManyFractionSymbols()
|
||||
}
|
||||
|
||||
return number
|
||||
@ -74,13 +76,14 @@ class Tokenizer(private val streamOfTokens: String) {
|
||||
private fun List<String>.repairLexicon(): List<String> {
|
||||
return this
|
||||
.missingClosingBrackets()
|
||||
.unpackNotation()
|
||||
.missingMultiply()
|
||||
.unpackAlPercents()
|
||||
.unpackAllPercents()
|
||||
// input like 80%80% should be treated as 80%*80%.
|
||||
// After unpacking we get (80/100)(80/100), the multiply is missing (!!!)
|
||||
// No, we can't unpack before fixing missing multiply.
|
||||
// Ideally we we need to add missing multiply for 80%80%
|
||||
// In that case unpackAlPercents gets input with all operators 80%*80% in this case
|
||||
// In that case unpackAllPercents gets input with all operators 80%*80% in this case
|
||||
// Can't be done right now since missingMultiply checks for tokens in front only
|
||||
.missingMultiply()
|
||||
}
|
||||
@ -135,7 +138,7 @@ class Tokenizer(private val streamOfTokens: String) {
|
||||
return result
|
||||
}
|
||||
|
||||
private fun List<String>.unpackAlPercents(): List<String> {
|
||||
private fun List<String>.unpackAllPercents(): List<String> {
|
||||
var result = this
|
||||
while (result.contains(Token.Operator.percent)) {
|
||||
val percIndex = result.indexOf(Token.Operator.percent)
|
||||
@ -144,6 +147,38 @@ class Tokenizer(private val streamOfTokens: String) {
|
||||
return result
|
||||
}
|
||||
|
||||
private fun List<String>.unpackNotation(): List<String> {
|
||||
// Transform 1E+7 ==> 1*10^7
|
||||
// Transform 1E-7 ==> 1/10^7
|
||||
val result = this.toMutableList()
|
||||
val listIterator = result.listIterator()
|
||||
|
||||
while (listIterator.hasNext()) {
|
||||
if (listIterator.next() == Token.DisplayOnly.engineeringE) {
|
||||
listIterator.remove()
|
||||
|
||||
val tokenAfterE = try {
|
||||
listIterator.next()
|
||||
} catch (e: Exception) {
|
||||
throw TokenizerException.BadScientificNotation()
|
||||
}
|
||||
|
||||
listIterator.remove()
|
||||
|
||||
when (tokenAfterE) {
|
||||
Token.Operator.minus -> listIterator.add(Token.Operator.divide)
|
||||
Token.Operator.plus -> listIterator.add(Token.Operator.multiply)
|
||||
else -> throw TokenizerException.BadScientificNotation()
|
||||
}
|
||||
|
||||
listIterator.add("10")
|
||||
listIterator.add(Token.Operator.power)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
private fun List<String>.unpackPercentAt(percentIndex: Int): List<String> {
|
||||
var cursor = percentIndex
|
||||
|
||||
@ -206,8 +241,9 @@ class Tokenizer(private val streamOfTokens: String) {
|
||||
// Just number
|
||||
if (tokenInFront.all { it in digits }) return listOf(tokenInFront)
|
||||
|
||||
// Not just a number. Probably expression in brackets.
|
||||
if (tokenInFront != Token.Operator.rightBracket) throw Exception("Unexpected token before percentage")
|
||||
// For cases like "100+(2+5)|%". The check above won't pass, so the next expected thing is
|
||||
// a number in brackets. Anything else is not expected.
|
||||
if (tokenInFront != Token.Operator.rightBracket) throw TokenizerException.FailedToUnpackNumber()
|
||||
|
||||
// Start walking left until we get balanced brackets
|
||||
var cursor = pos - 1
|
||||
|
@ -38,7 +38,16 @@ class ExpressionExceptionsTest {
|
||||
fun `ugly ahh expression`() = assertExprFail(ExpressionException.BadExpression::class.java, "100+cos()")
|
||||
|
||||
@Test
|
||||
fun `ugly ahh expression2`() = assertExprFail(TokenizerException.BadNumber::class.java, "...")
|
||||
fun `ugly ahh expression2`() = assertExprFail(TokenizerException.TooManyFractionSymbols::class.java, "...")
|
||||
|
||||
@Test
|
||||
fun `ugly ahh expression3`() = assertExprFail(TokenizerException.BadScientificNotation::class.java, "2.5E-")
|
||||
|
||||
@Test
|
||||
fun `ugly ahh expression4`() = assertExprFail(TokenizerException.BadScientificNotation::class.java, "2.5E")
|
||||
|
||||
@Test
|
||||
fun `ugly ahh expression5`() = assertExprFail(TokenizerException.BadScientificNotation::class.java, "2.5E÷")
|
||||
|
||||
@Test
|
||||
fun `too big`() = assertExprFail(ExpressionException.TooBig::class.java, "999999!")
|
||||
|
@ -130,4 +130,13 @@ class FixLexiconTest {
|
||||
|
||||
assertLex("10+(2.÷100×(10))", "10+2.%")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `scientific notation`() {
|
||||
assertLex("1.2×10^3", "1.2E+3")
|
||||
|
||||
assertLex("1.2÷10^3", "1.2E−3")
|
||||
|
||||
assertLex("1.2×10^3+4.5×10^6", "1.2E+3+4.5E+6")
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user