From a87d5f76858a58766ff9fae5d42aba4d6baf0aad Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Sat, 16 Sep 2023 20:13:27 +0300 Subject: [PATCH] Added floating number scientific notation --- .../com/annimon/ownlang/parser/Lexer.java | 32 +++++++++++++++++++ .../com/annimon/ownlang/parser/LexerTest.java | 13 ++++++++ .../parser/LexerValidDataProvider.java | 6 ++-- 3 files changed, 48 insertions(+), 3 deletions(-) diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Lexer.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Lexer.java index 48efd15..d4af633 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Lexer.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Lexer.java @@ -164,6 +164,10 @@ public final class Lexer { if (current == '.') { if (hasDot) throw error("Invalid float number " + buffer); hasDot = true; + } else if (current == 'e' || current == 'E') { + int exp = subTokenizeScientificNumber(); + buffer.append(current).append(exp); + break; } else if (!Character.isDigit(current)) { break; } @@ -172,6 +176,34 @@ public final class Lexer { } addToken(TokenType.NUMBER, buffer.toString(), startPos); } + + private int subTokenizeScientificNumber() { + int sign = 1; + switch (next()) { + case '-': sign = -1; + case '+': skip(); break; + } + + boolean hasValue = false; + char current = peek(0); + while (current == '0') { + hasValue = true; + current = next(); + } + int result = 0; + int position = 0; + while (Character.isDigit(current)) { + result = result * 10 + (current - '0'); + current = next(); + position++; + } + if (position == 0 && !hasValue) throw error("Empty floating point exponent"); + if (position >= 4) { + if (sign > 0) throw error("Float number too large"); + else throw error("Float number too small"); + } + return sign * result; + } private void tokenizeHexNumber(int skipChars) { clearBuffer(); diff --git a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerTest.java b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerTest.java index c3e9785..92b39c9 100644 --- a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerTest.java +++ b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerTest.java @@ -26,6 +26,9 @@ public class LexerTest { public static Stream invalidData() { return Stream.builder() .add(Arguments.of("Wrong float point", "3.14.15")) + .add(Arguments.of("Empty float exponent", "3e")) + .add(Arguments.of("Float number too small", "3e-00009000")) + .add(Arguments.of("Float number too large", "3e+00009000")) .add(Arguments.of("Wrong HEX number", "0Xf7_p6_s5")) .add(Arguments.of("HEX number ends with _", "0Xf7_")) .add(Arguments.of("Empty rest of HEX number", "#")) @@ -46,6 +49,16 @@ public class LexerTest { .extracting(Token::text) .containsExactly("0", "3.1415", "CAFEBABE", "f7d6c5", "FFFF"); } + + @Test + public void testFloatNumbersExponent() { + String input = "4e+7 0.3E-19 2e0 5e0000000000000200 5E-000000089"; + List result = Lexer.tokenize(input); + assertThat(result) + .allMatch(t -> t.type().equals(NUMBER)) + .extracting(Token::text) + .containsExactly("4e7", "0.3E-19", "2e0", "5e200", "5E-89"); + } @Test public void testString() { diff --git a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerValidDataProvider.java b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerValidDataProvider.java index 5c0390c..c90cb44 100644 --- a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerValidDataProvider.java +++ b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/LexerValidDataProvider.java @@ -25,6 +25,9 @@ public class LexerValidDataProvider { Arguments.of("Numbers", "12 7.8 90000000 10.03", List.of(NUMBER, NUMBER, NUMBER, NUMBER)), + Arguments.of("Float notation", + "7e8", + List.of(NUMBER)), Arguments.of("Hex numbers", "#FF 0xCA 0x12fb 0xFF", List.of(HEX_NUMBER, HEX_NUMBER, HEX_NUMBER, HEX_NUMBER)) @@ -80,9 +83,6 @@ public class LexerValidDataProvider { private static List notSupported() { return List.of( - Arguments.of("Float notation", - "7e8", - List.of(NUMBER, WORD)), Arguments.of("Float hex numbers", "0Xf7p6", List.of(HEX_NUMBER, WORD))