Added functionality to AsmNumberUtil
[Mograsim.git] / net.mograsim.plugin.core / src / net / mograsim / plugin / asm / AsmNumberUtil.java
1 package net.mograsim.plugin.asm;\r
2 \r
3 import static java.lang.String.format;\r
4 \r
5 import java.math.BigInteger;\r
6 import java.util.regex.Matcher;\r
7 import java.util.regex.Pattern;\r
8 \r
9 public final class AsmNumberUtil\r
10 {\r
11 \r
12         private AsmNumberUtil()\r
13         {\r
14 \r
15         }\r
16 \r
17         private static final String sgnPat = "([-+]?)";\r
18         private static final String binPat = "((?:[01]+_)*[01]+)";\r
19         private static final String octPat = "((?:[0-7]+_)*[0-7]+)";\r
20         private static final String decPat = "((?:[0-9]+_)*[0-9]+)";\r
21         private static final String hexPat = "((?:[0-9a-f]+_)*[0-9a-f]+)";\r
22 \r
23         private static final String preferredBinPat = "%1$s0b%2$s";\r
24         private static final String preferredOctPat = "%s%sq";\r
25         private static final String preferredDecPat = "%s%s";\r
26         private static final String preferredHexPat = "%1$s0x%2$s";\r
27 \r
28         static final Pattern numberBin = Pattern.compile(format(preferredBinPat + "|%1$s%2$sb", sgnPat, binPat), Pattern.CASE_INSENSITIVE);\r
29         static final Pattern numberOct = Pattern.compile(format(preferredOctPat, sgnPat, octPat), Pattern.CASE_INSENSITIVE);\r
30         static final Pattern numberDec = Pattern.compile(format(preferredDecPat, sgnPat, decPat));\r
31         static final Pattern numberHex = Pattern.compile(format(preferredHexPat + "|%1$s%2$sh", sgnPat, hexPat), Pattern.CASE_INSENSITIVE);\r
32         static final Pattern numberFloat = Pattern.compile(format("%1$s%2$s(?:\\.%2$s)?(?:e%1$s%2$s)?", sgnPat, decPat),\r
33                         Pattern.CASE_INSENSITIVE);\r
34 \r
35         public static boolean isPrefix(CharSequence cs, Pattern p)\r
36         {\r
37                 Matcher m = p.matcher(cs);\r
38                 return m.matches() || m.hitEnd();\r
39         }\r
40 \r
41         public static boolean isBinary(CharSequence cs)\r
42         {\r
43                 return numberBin.matcher(cs).matches();\r
44         }\r
45 \r
46         public static boolean isOctal(CharSequence cs)\r
47         {\r
48                 return numberOct.matcher(cs).matches();\r
49         }\r
50 \r
51         public static boolean isDecimal(CharSequence cs)\r
52         {\r
53                 return numberDec.matcher(cs).matches();\r
54         }\r
55 \r
56         public static boolean isHexadecimal(CharSequence cs)\r
57         {\r
58                 return numberHex.matcher(cs).matches();\r
59         }\r
60 \r
61         public static boolean isFloatingPoint(CharSequence cs)\r
62         {\r
63                 return numberFloat.matcher(cs).matches();\r
64         }\r
65 \r
66         public static boolean isNumber(CharSequence cs)\r
67         {\r
68                 return getType(cs) != NumberType.NONE;\r
69         }\r
70 \r
71         public static boolean quickCheckForNumber(CharSequence cs)\r
72         {\r
73                 if (cs.length() == 0 || !isStart(cs.charAt(0)))\r
74                         return false;\r
75                 return cs.length() == 1 || isPart(cs.charAt(1));\r
76         }\r
77 \r
78         public static boolean isStart(int c)\r
79         {\r
80                 return isDigit(c) || c == '+' || c == '-';\r
81         }\r
82 \r
83         public static boolean isDigit(int c)\r
84         {\r
85                 return ('0' <= c && c <= '9') || ('A' <= c && c <= 'F') || ('a' <= c && c <= 'f');\r
86         }\r
87 \r
88         public static boolean isPart(int c)\r
89         {\r
90                 return isDigit(c) || isMarker(Character.toLowerCase(c));\r
91         }\r
92 \r
93         public static boolean isMarker(int lowerCase)\r
94         {\r
95                 switch (lowerCase)\r
96                 {\r
97                 case '.':\r
98                 case '_':\r
99                 case '+':\r
100                 case '-':\r
101                 case 'e':\r
102                 case 'x':\r
103                 case 'b':\r
104                 case 'q':\r
105                 case 'h':\r
106                         return true;\r
107                 default:\r
108                         return false;\r
109                 }\r
110         }\r
111 \r
112         public static NumberType getType(CharSequence cs)\r
113         {\r
114                 if (!quickCheckForNumber(cs))\r
115                         return NumberType.NONE;\r
116                 if (isDecimal(cs))\r
117                         return NumberType.DECIMAL;\r
118                 if (isHexadecimal(cs))\r
119                         return NumberType.HEXADECIMAL;\r
120                 if (isBinary(cs))\r
121                         return NumberType.BINARY;\r
122                 if (isOctal(cs))\r
123                         return NumberType.OCTAL;\r
124                 if (isFloatingPoint(cs))\r
125                         return NumberType.FLOATINGPOINT;\r
126                 return NumberType.NONE;\r
127         }\r
128 \r
129         /**\r
130          * Checks if the {@link CharSequence} is a prefix of a valid number, and returns the NumberType it is a prefix of. If a String is a\r
131          * prefix of multiple different {@link NumberType}s, the one with the smallest radix is returned. If no valid {@link NumberType} is\r
132          * found, {@link NumberType#NONE} is returned.\r
133          * \r
134          * @param cs The potential prefix\r
135          * @return The type the {@link CharSequence} is a prefix of\r
136          */\r
137         public static NumberType prefixOfType(CharSequence cs)\r
138         {\r
139                 if (isPrefix(cs, numberBin))\r
140                         return NumberType.BINARY;\r
141                 if (isPrefix(cs, numberOct))\r
142                         return NumberType.OCTAL;\r
143                 if (isPrefix(cs, numberDec))\r
144                         return NumberType.DECIMAL;\r
145                 if (isPrefix(cs, numberHex))\r
146                         return NumberType.HEXADECIMAL;\r
147                 if (isPrefix(cs, numberFloat))\r
148                         return NumberType.FLOATINGPOINT;\r
149                 return NumberType.NONE;\r
150         }\r
151 \r
152         private static CharSequence extractSignAndDigits(NumberType type, CharSequence cs)\r
153         {\r
154                 return type.numberPattern.matcher(cs).replaceAll("$1$2").replaceAll("_", "");\r
155         }\r
156 \r
157         /**\r
158          * Computes the {@link BigInteger} value of a {@link CharSequence}, by determining the {@link NumberType} and parsing the number with\r
159          * the determined radix.\r
160          * \r
161          * @throws NumberFormatException if the {@link CharSequence} does not match any of the expected number patterns\r
162          */\r
163         public static BigInteger valueOf(CharSequence cs)\r
164         {\r
165                 NumberType type = getType(cs);\r
166                 if (NumberType.NONE.equals(type))\r
167                         throw new NumberFormatException();\r
168                 return new BigInteger(extractSignAndDigits(type, cs).toString(), type.radix);\r
169         }\r
170 \r
171         /**\r
172          * Formats a {@link BigInteger} in accordance with the pattern associated with the supplied {@link NumberType}\r
173          */\r
174         public static String toString(BigInteger number, NumberType type)\r
175         {\r
176                 String pattern;\r
177                 switch (type)\r
178                 {\r
179                 case BINARY:\r
180                         pattern = preferredBinPat;\r
181                         break;\r
182                 case OCTAL:\r
183                         pattern = preferredOctPat;\r
184                         break;\r
185                 default:\r
186                 case DECIMAL:\r
187                         pattern = preferredDecPat;\r
188                         break;\r
189                 case HEXADECIMAL:\r
190                         pattern = preferredHexPat;\r
191                 }\r
192                 if (number.signum() < 0)\r
193                         return String.format(pattern, "-", number.abs().toString(type.radix));\r
194                 return String.format(pattern, "", number.toString(type.radix));\r
195         }\r
196 \r
197         public enum NumberType\r
198         {\r
199                 NONE(-1, null), BINARY(2, AsmNumberUtil.numberBin), OCTAL(8, AsmNumberUtil.numberOct), DECIMAL(10, AsmNumberUtil.numberDec),\r
200                 HEXADECIMAL(16, AsmNumberUtil.numberHex), FLOATINGPOINT(10, AsmNumberUtil.numberFloat);\r
201 \r
202                 public final int radix;\r
203                 final Pattern numberPattern;\r
204 \r
205                 NumberType(int radix, Pattern numberPattern)\r
206                 {\r
207                         this.radix = radix;\r
208                         this.numberPattern = numberPattern;\r
209                 }\r
210         }\r
211 }\r