package sizzle.functions; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Special Functions * * These functions have special properties, such as variable types, variable * numbers of parameters, or parameters that are types rather than values. Some * of the syntax used to describe them, e.g. "...", default arguments and * overloading, is not part of the Sawzall language. * * @author anthonyu * */ public class SizzleSpecialIntrinsics { private static MessageDigest md; private static Map<String, String> regexMap; static { try { SizzleSpecialIntrinsics.md = MessageDigest.getInstance("SHA"); } catch (final NoSuchAlgorithmException e) { throw new RuntimeException(e.getClass().getSimpleName() + " caught", e); } SizzleSpecialIntrinsics.regexMap = new HashMap<String, String>(); SizzleSpecialIntrinsics.regexMap.put("int,16", "(0x)?[A-Fa-f0-9]+h?"); SizzleSpecialIntrinsics.regexMap.put("int,10", "[+-]?[0-9]+"); SizzleSpecialIntrinsics.regexMap.put("int,8", "0[0-7]+"); SizzleSpecialIntrinsics.regexMap.put("string", "\\S+"); SizzleSpecialIntrinsics.regexMap.put("time", "[0-9]+"); SizzleSpecialIntrinsics.regexMap.put("fingerprint", "[0-9]+"); SizzleSpecialIntrinsics.regexMap.put("float", "[-+]?[0-9]*\\.?[0-9]+(e[-+]?[0-9]+)?"); } /** * If <em>condition</em> is false, print the <em>message</em> to standard * error, with the prefix assertion failed:, and exit. The message may be * empty or absent altogether. * * @param condition * The condition to be checked * * @param message * A {@link String} containing the message to be printed upon * failure * * @return True iff <em>condition</em> is true */ @FunctionSpec(name = "assert", formalParameters = { "bool", "string" }) public void azzert(final boolean condition, final String message) { if (!condition) throw new RuntimeException("assertion failed: " + message); } /** * If <em>condition</em> is false, print the <em>message</em> to standard * error, with the prefix assertion failed:, and exit. The message may be * empty or absent altogether. * * @param condition * The condition to be checked * * @return True iff <em>condition</em> is true */ @FunctionSpec(name = "assert", formalParameters = { "bool" }) public void azzert(final boolean condition) { if (!condition) throw new RuntimeException("assertion failed"); } private static byte[] longToByteArray(final long l) { return new byte[] { (byte) (l >> 56 & 0xff), (byte) (l >> 48 & 0xff), (byte) (l >> 40 & 0xff), (byte) (l >> 32 & 0xff), (byte) (l >> 24 & 0xff), (byte) (l >> 16 & 0xff), (byte) (l >> 8 & 0xff), (byte) (l >> 0 & 0xff), }; } private static long byteArrayToLong(final byte[] bs) { return (long) (0xff & bs[0]) << 56 | (long) (0xff & bs[1]) << 48 | (long) (0xff & bs[2]) << 40 | (long) (0xff & bs[3]) << 32 | (long) (0xff & bs[4]) << 24 | (long) (0xff & bs[5]) << 16 | (long) (0xff & bs[6]) << 8 | (long) (0xff & bs[7]) << 0; } /** * The fingerprintof function returns the 64-bit fingerprint of the * argument, which may be of any type. * * @param d * A double to be fingerprinted * * @return The fingerprint of d */ @FunctionSpec(name = "fingerprintof", formalParameters = { "float" }) public long fingerprintOf(final double d) { return SizzleSpecialIntrinsics .byteArrayToLong(SizzleSpecialIntrinsics.md.digest(SizzleSpecialIntrinsics.longToByteArray(Double.doubleToRawLongBits(d)))); } /** * The fingerprintof function returns the 64-bit fingerprint of the * argument, which may be of any type. * * @param s * A {@link String} to be fingerprinted * * @return The fingerprint of s */ @FunctionSpec(name = "fingerprintof", formalParameters = { "string" }) public long fingerprintOf(final String s) { return SizzleSpecialIntrinsics.byteArrayToLong(SizzleSpecialIntrinsics.md.digest(s.getBytes())); } /** * The fingerprintof function returns the 64-bit fingerprint of the * argument, which may be of any type. * * @param bs * An array of byte to be fingerprinted * * @return The fingerprint of bs */ @FunctionSpec(name = "fingerprintof", formalParameters = { "bytes" }) public long fingerprintOf(final byte[] bs) { return SizzleSpecialIntrinsics.byteArrayToLong(SizzleSpecialIntrinsics.md.digest(bs)); } /** * The fingerprintof function returns the 64-bit fingerprint of the * argument, which may be of any type. * * @param b * A boolean to be fingerprinted * * @return The fingerprint of b */ @FunctionSpec(name = "fingerprintof", formalParameters = { "bool" }) public long fingerprintOf(final boolean b) { if (b) return 1; return 0; } /** * The fingerprintof function returns the 64-bit fingerprint of the * argument, which may be of any type. * * @param l * A long to be fingerprinted * * @return The fingerprint of l */ @FunctionSpec(name = "fingerprintof", formalParameters = { "fingerprint" }) public long fingerprintOf(final long l) { return SizzleSpecialIntrinsics.byteArrayToLong(SizzleSpecialIntrinsics.md.digest(SizzleSpecialIntrinsics.longToByteArray(l))); } // TODO: implement new() public static String regex(final String type, final long base) { if (SizzleSpecialIntrinsics.regexMap.containsKey(type + "," + base)) return SizzleSpecialIntrinsics.regexMap.get(type + "," + base); else throw new RuntimeException("unimplemented"); } public static String regex(final String type) { if (SizzleSpecialIntrinsics.regexMap.containsKey(type)) return SizzleSpecialIntrinsics.regexMap.get(type); else throw new RuntimeException("unimplemented"); } private static SawyerReturn sawyer(final String string, final String[] regexes) { final List<String> result = new ArrayList<String>(); final Pattern p = Pattern.compile(regexes[0]); final Matcher matcher = p.matcher(string); if (matcher.find()) { if (regexes.length > 1) { final SawyerReturn sawyerReturn = SizzleSpecialIntrinsics.sawyer(string.substring(matcher.end()), Arrays.copyOfRange(regexes, 1, regexes.length)); result.add(string.substring(matcher.start(), matcher.end())); result.addAll(sawyerReturn.getResult()); return new SawyerReturn(result, sawyerReturn.getLeftover()); } else { result.add(string.substring(matcher.start(), matcher.end())); return new SawyerReturn(result, string.substring(matcher.end())); } } else { return new SawyerReturn(result, string); } } private static String[] saw(final int n, final String string, final String[] regexes) { final List<String> result = new ArrayList<String>(); String todo = string; for (int i = 0; i < n; i++) { final SawyerReturn sawyerReturn = SizzleSpecialIntrinsics.sawyer(todo, Arrays.copyOf(regexes, regexes.length)); result.addAll(sawyerReturn.getResult()); if (sawyerReturn.getLeftover().equals("") || sawyerReturn.getLeftover().equals(todo)) break; todo = sawyerReturn.getLeftover(); } return result.toArray(new String[result.size()]); } @FunctionSpec(name = "saw", returnType = "array of string", formalParameters = { "string", "string..." }) public static String[] saw(final String string, final String... regexes) { return SizzleSpecialIntrinsics.saw(1, string, regexes); } @FunctionSpec(name = "sawn", returnType = "array of string", formalParameters = { "int", "string", "string..." }) public static String[] sawn(final int n, final String string, final String... regexes) { return SizzleSpecialIntrinsics.saw(n, string, regexes); } @FunctionSpec(name = "sawzall", returnType = "array of string", formalParameters = { "string", "string..." }) public static String[] sawzall(final String string, final String... regexes) { // will someone ever trigger this obscure bug? return SizzleSpecialIntrinsics.saw(Integer.MAX_VALUE, string, regexes); } } class SawyerReturn { private final List<String> result; private final String leftover; public SawyerReturn(final List<String> result, final String leftover) { this.result = result; this.leftover = leftover; } public List<String> getResult() { return this.result; } public String getLeftover() { return this.leftover; } }