From 5dda034be4c0222ae7d252766e9a23520b905c9d Mon Sep 17 00:00:00 2001 From: hama Date: Wed, 27 Jul 2016 00:43:11 +0200 Subject: [PATCH] Scanner: number with suffix, tests completed --- src/main/java/de/republib/expr/Scanner.java | 148 +++++++++++++++--- .../java/de/republib/util/StringUtils.java | 33 ---- .../java/de/republib/expr/ScannerTest.java | 55 ++++++- 3 files changed, 179 insertions(+), 57 deletions(-) diff --git a/src/main/java/de/republib/expr/Scanner.java b/src/main/java/de/republib/expr/Scanner.java index 6d3d947..5fb54ec 100644 --- a/src/main/java/de/republib/expr/Scanner.java +++ b/src/main/java/de/republib/expr/Scanner.java @@ -12,12 +12,56 @@ import de.republib.util.I18N; * */ public class Scanner { + static boolean defaultAllowNumberSuffix = false; + + /** + * @return the defaultAllowNumberSuffix + */ + public static boolean getDefaultAllowNumberSuffix() { + return Scanner.defaultAllowNumberSuffix; + } + + /** + * @param defaultAllowNumberSuffix + * the defaultAllowNumberSuffix to set + */ + public static void setDefaultAllowNumberSuffix(boolean defaultAllowNumberSuffix) { + Scanner.defaultAllowNumberSuffix = defaultAllowNumberSuffix; + } + + /** + * Scans a number at the begin of a string. + * + * @param text + * text starting (or not) with a number. White spaces will be + * ignored + * @param defaultValue + * the return value if the text does not start with a number + * @return defaultValue: no number found at the begin of the text
+ * otherwise: the value of the number + */ + public static long stringToLong(String text, long defaultValue) { + final Scanner scanner = new Scanner(text); + Token token; + long rc = defaultValue; + try { + token = scanner.nextNotSpaceToken(); + if (token.isType(TokenType.INTEGER)) { + rc = token.getLongValue(); + } + } catch (final ParserException e) { + } + return rc; + } + int position = 0; int lineNo = 1; String text; Token[] tokens = new Token[3]; int indexToken = 0; + boolean allowNumberSuffix = Scanner.defaultAllowNumberSuffix; Token lastToken = null; + StringBuilder string = new StringBuilder(8096); /** @@ -152,6 +196,13 @@ public class Scanner { return rc; } + /** + * @return the allowNumberSuffix + */ + public boolean isAllowNumberSuffix() { + return this.allowNumberSuffix; + } + /** * Returns the next token which is not a SPACE token. * @@ -197,25 +248,7 @@ public class Scanner { } token.setSpace(this.text.substring(tokenStart, this.position)); } else if (Character.isDigit(cc)) { - long value = cc - '0'; - long digit; - if (this.position < this.text.length() && (cc = this.text.charAt(this.position)) == 'x' || cc == 'X') { - ++this.position; - while (this.position < this.text.length() - && (Character.isDigit(cc = this.text.charAt(this.position)) || cc >= 'A' && cc <= 'F' - || cc >= 'a' && cc <= 'f')) { - this.position++; - digit = cc <= '9' ? cc - '0' : cc <= 'F' ? cc - 'A' + 10 : cc - 'a' + 10; - value = 16 * value + digit; - } - } else { - while (this.position < this.text.length() - && Character.isDigit(cc = this.text.charAt(this.position))) { - this.position++; - value = 10 * value + cc - '0'; - } - } - token.setLong(value); + token.setLong(scanNumber(cc)); } else if (cc == '\'' || cc == '"') { this.string.setLength(0); final char delimiter = cc; @@ -254,4 +287,81 @@ public class Scanner { this.lineNo = 1; return this; } + + /** + * Handles the scan of a number. + * + *
+	 * Syntax:
+	 *  ::= 
+	 *  }+ | {  }+
+	 *  ::=  |  'i'
+	 *  ::= '0' | .. | '9'
+	 *  ::=  | 'a' | .. | 'f'
+	 *  ::= 'k' | 'm' | 'g' | 't'
+	 * 
+ * + * @param cc + * first digit + * @return the number + */ + private long scanNumber(char cc) { + long value = cc - '0'; + long digit; + if (this.position < this.text.length() && (cc = this.text.charAt(this.position)) == 'x' || cc == 'X') { + ++this.position; + while (this.position < this.text.length() && (Character.isDigit(cc = this.text.charAt(this.position)) + || cc >= 'A' && cc <= 'F' || cc >= 'a' && cc <= 'f')) { + this.position++; + digit = cc <= '9' ? cc - '0' : cc <= 'F' ? cc - 'A' + 10 : cc - 'a' + 10; + value = 16 * value + digit; + } + } else { + while (this.position < this.text.length() && Character.isDigit(cc = this.text.charAt(this.position))) { + this.position++; + value = 10 * value + cc - '0'; + } + } + if (this.allowNumberSuffix && (cc == 'k' || cc == 'K' || cc == 'm' || cc == 'M' || cc == 'g' || cc == 'G' + || cc == 't' || cc == 'T') && this.position < this.text.length()) { + ++this.position; + final int len = this.text.length(); + char cc2 = '\0'; + if (this.position < len) { + cc2 = this.text.charAt(this.position); + } + final boolean binary = cc2 == 'i' || cc2 == 'I'; + if (binary) { + ++this.position; + } + switch (cc) { + case 'k': + case 'K': + value *= binary ? 1024L : 1000; + break; + case 'm': + case 'M': + value *= binary ? 1024L * 1024 : 1000L * 1000; + break; + case 'g': + case 'G': + value *= binary ? 1024L * 1024 * 1024 : 1000L * 1000 * 1000; + break; + case 't': + case 'T': + value *= binary ? 1024L * 1024 * 1024 * 1024 : 1000L * 1000 * 1000 * 1000; + break; + } + } + + return value; + } + + /** + * @param allowNumberSuffix + * the allowNumberSuffix to set + */ + public void setAllowNumberSuffix(boolean allowNumberSuffix) { + this.allowNumberSuffix = allowNumberSuffix; + } } diff --git a/src/main/java/de/republib/util/StringUtils.java b/src/main/java/de/republib/util/StringUtils.java index e9c0c6e..e233c00 100644 --- a/src/main/java/de/republib/util/StringUtils.java +++ b/src/main/java/de/republib/util/StringUtils.java @@ -27,21 +27,17 @@ public class StringUtils { } double upperLimit = 1E6; double lowerLimit = 100; - double lowerLimit2 = 1e-4; int prec = 0; - final int prec2 = 5; switch (precision) { case 1: prec = 1; upperLimit = 1E4; lowerLimit = 10.0; - lowerLimit2 = 1e-2; break; case 2: prec = 2; upperLimit = 1E5; lowerLimit = 100.0; - lowerLimit2 = 1e-3; break; case 3: prec = 3; @@ -62,33 +58,4 @@ public class StringUtils { } return rc; } - - /** - * Evaluate a arithmetic expression. - * - * @param expr - * a formula with '+', '-', '*', '/', "**" and numbers but no - * parentheses - * @return the result - */ - long numberOf(final String expr) throws Exception { - long rc = 0; - final String[] terms = expr.split("[-+]"); - if (terms.length == 1) { - rc = Long.parseLong(expr); - } else { - for (final String term : terms) { - final String[] factors = term.split("[*/]"); - if (factors.length == 1) { - - } else { - for (final String factor : factors) { - final long rc2 = 1; - final String[] parts = factor.split("\\*\\*"); - } - } - } - } - return rc; - } } diff --git a/src/test/java/de/republib/expr/ScannerTest.java b/src/test/java/de/republib/expr/ScannerTest.java index 6ebab9f..b2ab884 100644 --- a/src/test/java/de/republib/expr/ScannerTest.java +++ b/src/test/java/de/republib/expr/ScannerTest.java @@ -3,11 +3,6 @@ package de.republib.expr; import org.testng.Assert; import org.testng.annotations.Test; -import de.republib.expr.ParserException; -import de.republib.expr.Scanner; -import de.republib.expr.Token; -import de.republib.expr.TokenType; - public class ScannerTest { @Test public void shouldConstruct() { @@ -127,6 +122,44 @@ public class ScannerTest { Assert.assertEquals(token.getLongValue(), 0xABCDEF0L); } + @Test + public void shouldScanNumberSuffix() throws ParserException { + // decimal suffixes: + final Scanner scanner = new Scanner("12k ix"); + scanner.setAllowNumberSuffix(true); + Assert.assertEquals(scanner.nextToken().getLongValue(), 12 * 1000L); + Assert.assertEquals(scanner.reset("123457890K").nextToken().getLongValue(), 123457890L * 1000); + + Assert.assertEquals(scanner.reset("9876m").nextToken().getLongValue(), 9876L * 1000 * 1000); + Assert.assertEquals(scanner.reset("04711M").nextToken().getLongValue(), 4711L * 1000 * 1000); + + Assert.assertEquals(scanner.reset("3g").nextToken().getLongValue(), 3L * 1000 * 1000 * 1000); + Assert.assertEquals(scanner.reset("9G").nextToken().getLongValue(), 9L * 1000 * 1000 * 1000); + + Assert.assertEquals(scanner.reset("17t").nextToken().getLongValue(), 17L * 1000 * 1000 * 1000 * 1000); + Assert.assertEquals(scanner.reset("73T").nextToken().getLongValue(), 73L * 1000 * 1000 * 1000 * 1000); + + // binary suffixes: + Assert.assertEquals(scanner.reset("0x23ki").nextToken().getLongValue(), 0x23L * 1024L); + Assert.assertEquals(scanner.reset("0x123457890KI").nextToken().getLongValue(), 0x123457890L * 1024); + + Assert.assertEquals(scanner.reset("0x9876mi").nextToken().getLongValue(), 0x9876L * 1024 * 1024); + Assert.assertEquals(scanner.reset("0x04711MI").nextToken().getLongValue(), 0x4711L * 1024 * 1024); + + Assert.assertEquals(scanner.reset("0x30gi").nextToken().getLongValue(), 0x30L * 1024 * 1024 * 1024); + Assert.assertEquals(scanner.reset("0x90GI").nextToken().getLongValue(), 0x90L * 1024 * 1024 * 1024); + + Assert.assertEquals(scanner.reset("0x17ti").nextToken().getLongValue(), 0x17L * 1024 * 1024 * 1024 * 1024); + Assert.assertEquals(scanner.reset("0x73TI").nextToken().getLongValue(), 0x73L * 1024 * 1024 * 1024 * 1024); + + // token behind the suffix: + Assert.assertEquals(scanner.reset("123k+").nextToken().getLongValue(), 123L * 1000); + Assert.assertTrue(scanner.nextToken().isOp(OpCode.PLUS)); + + Assert.assertEquals(scanner.reset("123kI*").nextToken().getLongValue(), 123L * 1024); + Assert.assertTrue(scanner.nextToken().isOp(OpCode.TIMES)); + } + @Test public void shouldScanOp() throws ParserException { final Scanner scanner = new Scanner("+ - * / % ** ( )"); @@ -271,4 +304,16 @@ public class ScannerTest { Assert.assertEquals(token.getDelimiter(), '\''); } + @Test + public void shouldWorkStringToLong() { + Assert.assertEquals(Scanner.stringToLong(" \t123abc", -1), 123L); + final boolean oldValue = Scanner.getDefaultAllowNumberSuffix(); + // suffix recognition is not switched on + Scanner.setDefaultAllowNumberSuffix(false); + Assert.assertEquals(Scanner.stringToLong("0x44a2k", -1), 0x44a2L); + // suffix recognition is switched on + Scanner.setDefaultAllowNumberSuffix(true); + Assert.assertEquals(Scanner.stringToLong("0x44a2k", -1), 0x44a2L * 1000); + Scanner.setDefaultAllowNumberSuffix(oldValue); + } } -- 2.39.5