X-Git-Url: https://mograsim.net/gitweb/?a=blobdiff_plain;f=plugins%2Fnet.mograsim.plugin.core%2Fsrc%2Fnet%2Fmograsim%2Fplugin%2Fasm%2FAsmNumberUtil.java;fp=plugins%2Fnet.mograsim.plugin.core%2Fsrc%2Fnet%2Fmograsim%2Fplugin%2Fasm%2FAsmNumberUtil.java;h=580c32494be7f644b24d8dc5f49447e33924f4f2;hb=7d05144c25daa53e60fc9ed9fd503546a86567f8;hp=0000000000000000000000000000000000000000;hpb=8bed58cd47f4e53a0a83e066d38864aa6875502f;p=Mograsim.git diff --git a/plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/asm/AsmNumberUtil.java b/plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/asm/AsmNumberUtil.java new file mode 100644 index 00000000..580c3249 --- /dev/null +++ b/plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/asm/AsmNumberUtil.java @@ -0,0 +1,211 @@ +package net.mograsim.plugin.asm; + +import static java.lang.String.format; + +import java.math.BigInteger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public final class AsmNumberUtil +{ + + private AsmNumberUtil() + { + + } + + private static final String sgnPat = "([-+]?)"; + private static final String binPat = "((?:[01]+_)*[01]+)"; + private static final String octPat = "((?:[0-7]+_)*[0-7]+)"; + private static final String decPat = "((?:[0-9]+_)*[0-9]+)"; + private static final String hexPat = "((?:[0-9a-f]+_)*[0-9a-f]+)"; + + private static final String preferredBinPat = "%1$s0b%2$s"; + private static final String preferredOctPat = "%s%sq"; + private static final String preferredDecPat = "%s%s"; + private static final String preferredHexPat = "%1$s0x%2$s"; + + static final Pattern numberBin = Pattern.compile(format(preferredBinPat + "|%1$s%2$sb", sgnPat, binPat), Pattern.CASE_INSENSITIVE); + static final Pattern numberOct = Pattern.compile(format(preferredOctPat, sgnPat, octPat), Pattern.CASE_INSENSITIVE); + static final Pattern numberDec = Pattern.compile(format(preferredDecPat, sgnPat, decPat)); + static final Pattern numberHex = Pattern.compile(format(preferredHexPat + "|%1$s%2$sh", sgnPat, hexPat), Pattern.CASE_INSENSITIVE); + static final Pattern numberFloat = Pattern.compile(format("%1$s%2$s(?:\\.%2$s)?(?:e%1$s%2$s)?", sgnPat, decPat), + Pattern.CASE_INSENSITIVE); + + public static boolean isPrefix(CharSequence cs, Pattern p) + { + Matcher m = p.matcher(cs); + return m.matches() || m.hitEnd(); + } + + public static boolean isBinary(CharSequence cs) + { + return numberBin.matcher(cs).matches(); + } + + public static boolean isOctal(CharSequence cs) + { + return numberOct.matcher(cs).matches(); + } + + public static boolean isDecimal(CharSequence cs) + { + return numberDec.matcher(cs).matches(); + } + + public static boolean isHexadecimal(CharSequence cs) + { + return numberHex.matcher(cs).matches(); + } + + public static boolean isFloatingPoint(CharSequence cs) + { + return numberFloat.matcher(cs).matches(); + } + + public static boolean isNumber(CharSequence cs) + { + return getType(cs) != NumberType.NONE; + } + + public static boolean quickCheckForNumber(CharSequence cs) + { + if (cs.length() == 0 || !isStart(cs.charAt(0))) + return false; + return cs.length() == 1 || isPart(cs.charAt(1)); + } + + public static boolean isStart(int c) + { + return isDigit(c) || c == '+' || c == '-'; + } + + public static boolean isDigit(int c) + { + return ('0' <= c && c <= '9') || ('A' <= c && c <= 'F') || ('a' <= c && c <= 'f'); + } + + public static boolean isPart(int c) + { + return isDigit(c) || isMarker(Character.toLowerCase(c)); + } + + public static boolean isMarker(int lowerCase) + { + switch (lowerCase) + { + case '.': + case '_': + case '+': + case '-': + case 'e': + case 'x': + case 'b': + case 'q': + case 'h': + return true; + default: + return false; + } + } + + public static NumberType getType(CharSequence cs) + { + if (!quickCheckForNumber(cs)) + return NumberType.NONE; + if (isDecimal(cs)) + return NumberType.DECIMAL; + if (isHexadecimal(cs)) + return NumberType.HEXADECIMAL; + if (isBinary(cs)) + return NumberType.BINARY; + if (isOctal(cs)) + return NumberType.OCTAL; + if (isFloatingPoint(cs)) + return NumberType.FLOATINGPOINT; + return NumberType.NONE; + } + + /** + * 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 + * prefix of multiple different {@link NumberType}s, the one with the smallest radix is returned. If no valid {@link NumberType} is + * found, {@link NumberType#NONE} is returned. + * + * @param cs The potential prefix + * @return The type the {@link CharSequence} is a prefix of + */ + public static NumberType prefixOfType(CharSequence cs) + { + if (isPrefix(cs, numberBin)) + return NumberType.BINARY; + if (isPrefix(cs, numberOct)) + return NumberType.OCTAL; + if (isPrefix(cs, numberDec)) + return NumberType.DECIMAL; + if (isPrefix(cs, numberHex)) + return NumberType.HEXADECIMAL; + if (isPrefix(cs, numberFloat)) + return NumberType.FLOATINGPOINT; + return NumberType.NONE; + } + + private static CharSequence extractSignAndDigits(NumberType type, CharSequence cs) + { + return type.numberPattern.matcher(cs).replaceAll("$1$2").replaceAll("_", ""); + } + + /** + * Computes the {@link BigInteger} value of a {@link CharSequence}, by determining the {@link NumberType} and parsing the number with + * the determined radix. + * + * @throws NumberFormatException if the {@link CharSequence} does not match any of the expected number patterns + */ + public static BigInteger valueOf(CharSequence cs) + { + NumberType type = getType(cs); + if (NumberType.NONE.equals(type)) + throw new NumberFormatException(); + return new BigInteger(extractSignAndDigits(type, cs).toString(), type.radix); + } + + /** + * Formats a {@link BigInteger} in accordance with the pattern associated with the supplied {@link NumberType} + */ + public static String toString(BigInteger number, NumberType type) + { + String pattern; + switch (type) + { + case BINARY: + pattern = preferredBinPat; + break; + case OCTAL: + pattern = preferredOctPat; + break; + default: + case DECIMAL: + pattern = preferredDecPat; + break; + case HEXADECIMAL: + pattern = preferredHexPat; + } + if (number.signum() < 0) + return String.format(pattern, "-", number.abs().toString(type.radix)); + return String.format(pattern, "", number.toString(type.radix)); + } + + public enum NumberType + { + NONE(-1, null), BINARY(2, AsmNumberUtil.numberBin), OCTAL(8, AsmNumberUtil.numberOct), DECIMAL(10, AsmNumberUtil.numberDec), + HEXADECIMAL(16, AsmNumberUtil.numberHex), FLOATINGPOINT(10, AsmNumberUtil.numberFloat); + + public final int radix; + final Pattern numberPattern; + + NumberType(int radix, Pattern numberPattern) + { + this.radix = radix; + this.numberPattern = numberPattern; + } + } +}