Skip to content

Commit 4a499b8

Browse files
committed
1 parent 32c8f31 commit 4a499b8

File tree

6 files changed

+75
-17
lines changed

6 files changed

+75
-17
lines changed

core/base/src/main/java/com/sadellie/unitto/core/base/Token.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ object Token {
126126
}
127127

128128
val expressionTokens by lazy {
129-
Digit.allWithDot + Operator.all + Func.all + Const.all
129+
Digit.allWithDot + Operator.all + Func.all + Const.all + DisplayOnly.engineeringE
130130
}
131131

132132
val numberBaseTokens by lazy {

core/ui/src/main/java/com/sadellie/unitto/core/ui/common/textfield/FormatterExtensions.kt

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,6 @@ fun String.formatExpression(
5454
): String {
5555
var input = this
5656

57-
// Don't do anything to engineering string.
58-
if (input.contains(Token.DisplayOnly.engineeringE)) {
59-
return input.replace(Token.Digit.dot, formatterSymbols.fractional)
60-
}
61-
6257
// Fractional
6358
if (input.contains(Token.DisplayOnly.fraction)) {
6459
// Only format integral part
@@ -77,8 +72,9 @@ fun String.formatExpression(
7772
input = input.replace(it, it.formatNumber(formatterSymbols))
7873
}
7974

80-
Token.sexyToUgly.forEach { (token, ugliness) ->
81-
ugliness.forEach { uglySymbol ->
75+
// Replace ugly symbols
76+
Token.sexyToUgly.forEach { (token, uglySymbols) ->
77+
uglySymbols.forEach { uglySymbol ->
8278
input = input.replace(uglySymbol, token)
8379
}
8480
}

core/ui/src/test/java/com/sadellie/unitto/core/ui/FormatterExpressionTest.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ import org.junit.Test
2525

2626
private const val ENG_VALUE = "123E+21"
2727
private const val ENG_VALUE_FRACTIONAL = "123.3E+21"
28+
private const val ENG_VALUE_EXPRESSION = "123E+21+(123456.789)"
29+
private const val ENG_VALUE_FRACTIONAL_EXPRESSION = "123.3E+21+(123456.789)"
2830
private const val COMPLETE_VALUE = "123456.789"
2931
private const val INCOMPLETE_VALUE = "123456."
3032
private const val NO_FRACTIONAL_VALUE = "123456"
@@ -41,6 +43,8 @@ class FormatterExpressionTest {
4143
fun String.format(): String = formatExpression(FormatterSymbols.Spaces)
4244
assertEquals("123E+21", ENG_VALUE.format())
4345
assertEquals("123.3E+21", ENG_VALUE_FRACTIONAL.format())
46+
assertEquals("123E+21+(123 456.789)", ENG_VALUE_EXPRESSION.format())
47+
assertEquals("123.3E+21+(123 456.789)", ENG_VALUE_FRACTIONAL_EXPRESSION.format())
4448
assertEquals("123 456.789", COMPLETE_VALUE.format())
4549
assertEquals("123 456.", INCOMPLETE_VALUE.format())
4650
assertEquals("123 456", NO_FRACTIONAL_VALUE.format())
@@ -56,6 +60,8 @@ class FormatterExpressionTest {
5660
fun String.format(): String = formatExpression(FormatterSymbols.Comma)
5761
assertEquals("123E+21", ENG_VALUE.format())
5862
assertEquals("123.3E+21", ENG_VALUE_FRACTIONAL.format())
63+
assertEquals("123E+21+(123,456.789)", ENG_VALUE_EXPRESSION.format())
64+
assertEquals("123.3E+21+(123,456.789)", ENG_VALUE_FRACTIONAL_EXPRESSION.format())
5965
assertEquals("123,456.789", COMPLETE_VALUE.format())
6066
assertEquals("123,456.", INCOMPLETE_VALUE.format())
6167
assertEquals("123,456", NO_FRACTIONAL_VALUE.format())
@@ -71,6 +77,8 @@ class FormatterExpressionTest {
7177
fun String.format(): String = formatExpression(FormatterSymbols.Period)
7278
assertEquals("123E+21", ENG_VALUE.format())
7379
assertEquals("123,3E+21", ENG_VALUE_FRACTIONAL.format())
80+
assertEquals("123E+21+(123.456,789)", ENG_VALUE_EXPRESSION.format())
81+
assertEquals("123,3E+21+(123.456,789)", ENG_VALUE_FRACTIONAL_EXPRESSION.format())
7482
assertEquals("123.456,789", COMPLETE_VALUE.format())
7583
assertEquals("123.456,", INCOMPLETE_VALUE.format())
7684
assertEquals("123.456", NO_FRACTIONAL_VALUE.format())

data/evaluatto/src/main/java/io/github/sadellie/evaluatto/Tokenizer.kt

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,10 @@ package io.github.sadellie.evaluatto
2020

2121
import com.sadellie.unitto.core.base.Token
2222

23-
sealed class TokenizerException(override val message: String) : Exception(message) {
24-
class BadNumber : TokenizerException("Number has multiple commas in it")
23+
sealed class TokenizerException(message: String) : Exception(message) {
24+
class TooManyFractionSymbols : TokenizerException("Number has multiple commas in it")
25+
class FailedToUnpackNumber : TokenizerException("Unexpected token before percentage")
26+
class BadScientificNotation : TokenizerException("Expected plus or minus symbol after \"E\"")
2527
}
2628

2729
class Tokenizer(private val streamOfTokens: String) {
@@ -60,7 +62,7 @@ class Tokenizer(private val streamOfTokens: String) {
6062
.takeWhile { Token.Digit.allWithDot.contains(it.toString()) }
6163

6264
if (number.count { it.toString() == Token.Digit.dot } > 1) {
63-
throw TokenizerException.BadNumber()
65+
throw TokenizerException.TooManyFractionSymbols()
6466
}
6567

6668
return number
@@ -74,13 +76,14 @@ class Tokenizer(private val streamOfTokens: String) {
7476
private fun List<String>.repairLexicon(): List<String> {
7577
return this
7678
.missingClosingBrackets()
79+
.unpackNotation()
7780
.missingMultiply()
78-
.unpackAlPercents()
81+
.unpackAllPercents()
7982
// input like 80%80% should be treated as 80%*80%.
8083
// After unpacking we get (80/100)(80/100), the multiply is missing (!!!)
8184
// No, we can't unpack before fixing missing multiply.
8285
// Ideally we we need to add missing multiply for 80%80%
83-
// In that case unpackAlPercents gets input with all operators 80%*80% in this case
86+
// In that case unpackAllPercents gets input with all operators 80%*80% in this case
8487
// Can't be done right now since missingMultiply checks for tokens in front only
8588
.missingMultiply()
8689
}
@@ -135,7 +138,7 @@ class Tokenizer(private val streamOfTokens: String) {
135138
return result
136139
}
137140

138-
private fun List<String>.unpackAlPercents(): List<String> {
141+
private fun List<String>.unpackAllPercents(): List<String> {
139142
var result = this
140143
while (result.contains(Token.Operator.percent)) {
141144
val percIndex = result.indexOf(Token.Operator.percent)
@@ -144,6 +147,38 @@ class Tokenizer(private val streamOfTokens: String) {
144147
return result
145148
}
146149

150+
private fun List<String>.unpackNotation(): List<String> {
151+
// Transform 1E+7 ==> 1*10^7
152+
// Transform 1E-7 ==> 1/10^7
153+
val result = this.toMutableList()
154+
val listIterator = result.listIterator()
155+
156+
while (listIterator.hasNext()) {
157+
if (listIterator.next() == Token.DisplayOnly.engineeringE) {
158+
listIterator.remove()
159+
160+
val tokenAfterE = try {
161+
listIterator.next()
162+
} catch (e: Exception) {
163+
throw TokenizerException.BadScientificNotation()
164+
}
165+
166+
listIterator.remove()
167+
168+
when (tokenAfterE) {
169+
Token.Operator.minus -> listIterator.add(Token.Operator.divide)
170+
Token.Operator.plus -> listIterator.add(Token.Operator.multiply)
171+
else -> throw TokenizerException.BadScientificNotation()
172+
}
173+
174+
listIterator.add("10")
175+
listIterator.add(Token.Operator.power)
176+
}
177+
}
178+
179+
return result
180+
}
181+
147182
private fun List<String>.unpackPercentAt(percentIndex: Int): List<String> {
148183
var cursor = percentIndex
149184

@@ -206,8 +241,9 @@ class Tokenizer(private val streamOfTokens: String) {
206241
// Just number
207242
if (tokenInFront.all { it in digits }) return listOf(tokenInFront)
208243

209-
// Not just a number. Probably expression in brackets.
210-
if (tokenInFront != Token.Operator.rightBracket) throw Exception("Unexpected token before percentage")
244+
// For cases like "100+(2+5)|%". The check above won't pass, so the next expected thing is
245+
// a number in brackets. Anything else is not expected.
246+
if (tokenInFront != Token.Operator.rightBracket) throw TokenizerException.FailedToUnpackNumber()
211247

212248
// Start walking left until we get balanced brackets
213249
var cursor = pos - 1

data/evaluatto/src/test/java/io/github/sadellie/evaluatto/ExpressionExceptionsTest.kt

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,16 @@ class ExpressionExceptionsTest {
3838
fun `ugly ahh expression`() = assertExprFail(ExpressionException.BadExpression::class.java, "100+cos()")
3939

4040
@Test
41-
fun `ugly ahh expression2`() = assertExprFail(TokenizerException.BadNumber::class.java, "...")
41+
fun `ugly ahh expression2`() = assertExprFail(TokenizerException.TooManyFractionSymbols::class.java, "...")
42+
43+
@Test
44+
fun `ugly ahh expression3`() = assertExprFail(TokenizerException.BadScientificNotation::class.java, "2.5E-")
45+
46+
@Test
47+
fun `ugly ahh expression4`() = assertExprFail(TokenizerException.BadScientificNotation::class.java, "2.5E")
48+
49+
@Test
50+
fun `ugly ahh expression5`() = assertExprFail(TokenizerException.BadScientificNotation::class.java, "2.5E÷")
4251

4352
@Test
4453
fun `too big`() = assertExprFail(ExpressionException.TooBig::class.java, "999999!")

data/evaluatto/src/test/java/io/github/sadellie/evaluatto/FixLexiconTest.kt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,4 +130,13 @@ class FixLexiconTest {
130130

131131
assertLex("10+(2.÷100×(10))", "10+2.%")
132132
}
133+
134+
@Test
135+
fun `scientific notation`() {
136+
assertLex("1.2×10^3", "1.2E+3")
137+
138+
assertLex("1.2÷10^3", "1.2E−3")
139+
140+
assertLex("1.2×10^3+4.5×10^6", "1.2E+3+4.5E+6")
141+
}
133142
}

0 commit comments

Comments
 (0)