// $Id: gencode.java,v 1.14 2004/02/17 12:58:45 ericb Exp $ // // This software is subject to the terms of the IBM Jikes Compiler // License Agreement available at the following URL: // http://www.ibm.com/research/jikes. // Copyright (C) 1999, 2004 IBM Corporation and others. All Rights Reserved. // You must accept the terms of that agreement to use this software. // import java.io.*; import java.util.*; /** * This helper class generates code.h and code.cpp, the lookup table used * by Jikes in deciding how to categorize Unicode input characters. This * is written in Java in order to accurately track the Unicode rules as * implemented by the latest version of java.lang.Character. */ class gencode { static final int SPACE_CODE = 0; // Character.isWhitespace static final int BAD_CODE = 1; // everything else ... static final int DIGIT_CODE = 2; // Character.isDigit static final int ID_PART_CODE = 3; // Character.isJavaIdentifierPart static final int LOWER_CODE = 4; // Character.isLowerCase static final int UPPER_CODE = 5; // Character.isUpperCase static final int ID_START_CODE = 6; // Character.isJavaIdentifierStart static final int MAX_CODEPOINT = 0x10ffff; // Maximum Unicode. static final String[] CODE_NAMES = { "SPACE_CODE", "BAD_CODE", "DIGIT_CODE", "ID_PART_CODE", "LOWER_CODE", "UPPER_CODE", "ID_START_CODE" }; static public void main(String args[]) throws FileNotFoundException, IOException { System.out.println("Gathering data..."); // Use char[] for ease in building strings, despite only using 8 bits. int numElements = MAX_CODEPOINT + 1; char[] info = new char[numElements]; for (int i = 0; i < info.length; i++) { if (Character.isWhitespace(i)) info[i] = SPACE_CODE; else if (Character.isLowerCase(i)) { assert Character.isJavaIdentifierStart(i); info[i] = LOWER_CODE; } else if (Character.isUpperCase(i)) { assert Character.isJavaIdentifierStart(i); info[i] = UPPER_CODE; } else if (Character.isDigit(i)) { assert Character.isJavaIdentifierPart(i); info[i] = DIGIT_CODE; } else if (Character.isJavaIdentifierStart(i)) info[i] = ID_START_CODE; else if (Character.isJavaIdentifierPart(i)) info[i] = ID_PART_CODE; else { info[i] = BAD_CODE; numElements--; } } System.out.println("Compressing tables..."); int bestShift = 0; int bestEst = info.length; String bestBlkStr = null; for (int i = 3; i < 12; i++) { int blkSize = 1 << i; Map blocks = new HashMap(); List blkArray = new ArrayList(); System.out.print("shift: " + i); for (int j = 0; j < info.length; j += blkSize) { String key = new String(info, j, blkSize); if (blocks.get(key) == null) { blkArray.add(key); blocks.put(key, new Integer(blkArray.size())); } } int blkNum = blkArray.size(); int blockLen = blkNum * blkSize; System.out.print(" before " + blockLen); // // Try to pack blkArray, by finding successively smaller matches // between heads and tails of blocks. // for (int j = blkSize - 1; j > 0; j--) { Map tails = new HashMap(); for (int k = 0; k < blkArray.size(); k++) { String str = (String) blkArray.get(k); if (str == null) continue; String tail = str.substring(str.length() - j); List l = (List) tails.get(tail); if (l == null) tails.put(tail, new LinkedList(Collections .singleton(new Integer(k)))); else l.add(new Integer(k)); } // // Now calculate the heads, and merge overlapping blocks // block: for (int k = 0; k < blkArray.size(); k++) { String tomerge = (String) blkArray.get(k); if (tomerge == null) continue; while (true) { String head = tomerge.substring(0, j); LinkedList entry = (LinkedList) tails.get(head); if (entry == null) continue block; Integer other = (Integer) entry.removeFirst(); if (other.intValue() == k) { if (entry.size() > 0) { entry.add(other); other = (Integer) entry.removeFirst(); } else { entry.add(other); continue block; } } if (entry.size() == 0) tails.remove(head); // // A match was found. // String merge = blkArray.get(other.intValue()) + tomerge.substring(j); blockLen -= j; blkNum--; if (other.intValue() < k) { blkArray.set(k, null); blkArray.set(other.intValue(), merge); String tail = merge.substring(merge.length() - j); List l = (List) tails.get(tail); Collections.replaceAll(l, new Integer(k), other); continue block; } blkArray.set(k, merge); blkArray.set(other.intValue(), null); tomerge = merge; } } } StringBuffer blockStr = new StringBuffer(blockLen); for (int k = 0; k < blkArray.size(); k++) { String str = (String) blkArray.get(k); if (str != null) blockStr.append(str); } assert blockStr.length() == blockLen : "Unexpected blockLen " + blockLen; int estimate = blockLen + (info.length >> (i - 1)); System.out.println(" after merge " + blockLen + ": " + estimate + " bytes"); if (estimate < bestEst) { bestEst = estimate; bestShift = i; bestBlkStr = blockStr.toString(); } } System.out.println("Generating code.h with shift of " + bestShift); int blkSize = 1 << bestShift; char[] blocks = new char[info.length >> bestShift]; for (int j = 0; j < info.length; j += blkSize) { String key = new String(info, j, blkSize); int index = bestBlkStr.indexOf(key); assert index != -1 : "Unexpected index for " + j; blocks[j >> bestShift] = (char) (index - j); } // // Process the code.h file // PrintStream hfile = new PrintStream(new FileOutputStream("code.h")); printHeader(hfile, new String[] {"\"platform.h\""}); hfile.println("#ifndef code_INCLUDED"); hfile.println("#define code_INCLUDED"); hfile.println(); hfile.println("class Code"); hfile.println("{"); hfile.println(" //"); hfile.println(" // To facilitate the scanning, the character set is partitioned into"); hfile.println(" // categories using the array CODE. These are described below together"); hfile.println(" // with some self-explanatory functions defined on CODE."); hfile.println(" //"); hfile.println(" enum {"); hfile.println(" SHIFT = " + bestShift + ","); hfile.println(" SPACE_CODE = " + SPACE_CODE + ','); hfile.println(" BAD_CODE = " + BAD_CODE + ','); hfile.println(" DIGIT_CODE = " + DIGIT_CODE + ','); hfile.println(" ID_PART_CODE = " + ID_PART_CODE + ','); hfile.println(" LOWER_CODE = " + LOWER_CODE + ','); hfile.println(" UPPER_CODE = " + UPPER_CODE + ','); hfile.println(" ID_START_CODE = " + ID_START_CODE); hfile.println(" };"); hfile.println(); hfile.println(" static char codes[" + bestBlkStr.length() + "];"); hfile.println(" static u2 blocks[" + blocks.length + "];"); hfile.println(); hfile.println(); hfile.println("public:"); hfile.println("#ifdef JIKES_DEBUG"); hfile.println(" static inline void CodeCheck(u4 c)"); hfile.println(" {"); hfile.println(" assert((u2) (blocks[c >> SHIFT] + c) < " + bestBlkStr.length() + ");"); hfile.println(" }"); hfile.println(); hfile.println(" static inline bool CodeCheck(void)"); hfile.println(" {"); hfile.println(" for (u4 c = 0; c <= " + MAX_CODEPOINT + "; c++)"); hfile.println(" CodeCheck(c);"); hfile.println(" return true;"); hfile.println(" }"); hfile.println("#endif // JIKES_DEBUG"); hfile.println(); hfile.println("//"); hfile.println("// These methods test for Unicode surrogate pairs."); hfile.println("//"); hfile.println(" static inline bool IsHighSurrogate(wchar_t c)"); hfile.println(" {"); hfile.println(" return c >= 0xd800 && c <= 0xdbff;"); hfile.println(" }"); hfile.println(" static inline bool IsLowSurrogate(wchar_t c)"); hfile.println(" {"); hfile.println(" return c >= 0xdc00 && c <= 0xdfff;"); hfile.println(" }"); hfile.println(); hfile.println(" static inline u4 Codepoint(wchar_t hi, wchar_t lo)"); hfile.println(" {"); hfile.println(" assert(IsHighSurrogate(hi) && IsLowSurrogate(lo));"); hfile.println(" return (hi << 10) + lo + (0x10000 - (0xd800 << 10) - 0xdc00);"); hfile.println(" }"); hfile.println(" static inline u4 Codepoint(const wchar_t* p)"); hfile.println(" {"); hfile.println(" u4 result = (u4) *p;"); hfile.println(" if (IsHighSurrogate(result) && IsLowSurrogate(p[1]))"); hfile.println(" result = Codepoint(result, p[1]);"); hfile.println(" return result;"); hfile.println(" }"); hfile.println(" static inline int Codelength(const wchar_t* p)"); hfile.println(" {"); hfile.println(" return (IsHighSurrogate(*p) && IsLowSurrogate(p[1])) ? 2 : 1;"); hfile.println(" }"); hfile.println(); hfile.println("//"); hfile.println("// These methods test for ASCII characteristics. Since it is strictly ASCII,"); hfile.println("// there is no need to check for Unicode surrogate pairs."); hfile.println("//"); hfile.println(" static inline bool IsNewline(wchar_t c)"); hfile.println(" {"); hfile.println(" return c == U_LF || c == U_CR;"); hfile.println(" }"); hfile.println(" static inline bool IsSpaceButNotNewline(wchar_t c)"); hfile.println(" {"); hfile.println(" return c == U_SP || c == U_FF || c == U_HT;"); hfile.println(" }"); hfile.println(" static inline bool IsSpace(wchar_t c)"); hfile.println(" {"); hfile.println(" return c == U_SP || c == U_CR || c == U_LF ||"); hfile.println(" c == U_HT || c == U_FF;"); hfile.println(" }"); hfile.println(); hfile.println(" static inline bool IsDecimalDigit(wchar_t c)"); hfile.println(" {"); hfile.println(" return c <= U_9 && c >= U_0;"); hfile.println(" }"); hfile.println(" static inline bool IsOctalDigit(wchar_t c)"); hfile.println(" {"); hfile.println(" return c <= U_7 && c >= U_0;"); hfile.println(" }"); hfile.println(" static inline bool IsHexDigit(wchar_t c)"); hfile.println(" {"); hfile.println(" return c <= U_f && (c >= U_a ||"); hfile.println(" (c >= U_A && c <= U_F) ||"); hfile.println(" (c >= U_0 && c <= U_9));"); hfile.println(" }"); hfile.println(" static inline int Value(wchar_t c)"); hfile.println(" {"); hfile.println(" assert(IsHexDigit(c));"); hfile.println(" return c - (c <= U_9 ? U_0 : c < U_a ? U_A - 10 : U_a - 10);"); hfile.println(" }"); hfile.println(" static inline bool IsSign(wchar_t c)"); hfile.println(" {"); hfile.println(" return c == U_MINUS || c == U_PLUS;"); hfile.println(" }"); hfile.println(); hfile.println(" static inline bool IsAsciiUpper(wchar_t c)"); hfile.println(" {"); hfile.println(" return c <= U_Z && c >= U_A;"); hfile.println(" }"); hfile.println(" static inline bool IsAsciiLower(wchar_t c)"); hfile.println(" {"); hfile.println(" return c <= U_z && c >= U_a;"); hfile.println(" }"); hfile.println(); hfile.println("//"); hfile.println("// The following methods recognize Unicode surrogate pairs, hence the need to"); hfile.println("// pass a pointer. Use Codelength() to determine if one or two characters"); hfile.println("// were used in the formation of a character."); hfile.println("//"); printMethod(hfile, "IsWhitespace", "SPACE_CODE", "=="); printMethod(hfile, "IsDigit", "DIGIT_CODE", "=="); printMethod(hfile, "IsUpper", "UPPER_CODE", "=="); printMethod(hfile, "IsLower", "LOWER_CODE", "=="); printMethod(hfile, "IsAlpha", "LOWER_CODE", ">="); printMethod(hfile, "IsAlnum", "DIGIT_CODE", ">="); hfile.println("};"); hfile.println(); hfile.println("#endif // code_INCLUDED"); printFooter(hfile); hfile.close(); // // Process the code.cpp file // System.out.println("Generating code.cpp"); PrintStream cfile = new PrintStream(new FileOutputStream("code.cpp")); printHeader(cfile, new String[] {"\"code.h\""}); cfile.println("char Code::codes[" + bestBlkStr.length() + "] ="); cfile.println("{"); for (int j = 0; j < bestBlkStr.length(); j += 5) { for (int k = 0; k < 5; k++) { if (k + j >= bestBlkStr.length()) break; if (k == 0) cfile.print(" "); cfile.print(" " + CODE_NAMES[bestBlkStr.charAt(k + j)] + ","); } cfile.println(); } cfile.println("};"); cfile.println(); cfile.println(); cfile.println("//"); cfile.println("// The Blocks vector:"); cfile.println("//"); cfile.println("u2 Code::blocks[" + blocks.length + "] ="); cfile.println("{"); for (int k = 0; k < blocks.length; k += 9) { for (int i = 0; i < 9; i++) { if (k + i >= blocks.length) break; if (i == 0) cfile.print(" "); cfile.print(" 0x" + Integer.toHexString(blocks[k + i]) + ","); } cfile.println(); } cfile.println("};"); printFooter(cfile); cfile.close(); // // Print statistics. // System.out.println("Total static storage utilization is " + blocks.length * 2 + " bytes for block lookup"); System.out.println(" plus " + bestBlkStr.length() + " bytes for the encodings"); System.out.println("Number of legal unicode codepoints:" + numElements); } static void printHeader(PrintStream file, String[] includes) { file.println("// $I" + /* CVS hack */ "d$ -*- c++ -*-"); file.println("// DO NOT MODIFY THIS FILE - it is generated using gencode.java."); file.println("//"); file.println("// This software is subject to the terms of the IBM Jikes Compiler"); file.println("// License Agreement available at the following URL:"); file.println("// http://www.ibm.com/research/jikes."); file.println("// Copyright (C) 1999, 2004 IBM Corporation and others. All Rights Reserved."); file.println("// You must accept the terms of that agreement to use this software."); file.println("//"); file.println(); for (int i = 0; i < includes.length; i++) { file.println("#include " + includes[i]); } file.println(); file.println("#ifdef HAVE_JIKES_NAMESPACE"); file.println("namespace Jikes { // Open namespace Jikes block"); file.println("#endif"); file.println(); } static void printMethod(PrintStream file, String name, String value, String relation) { file.println(" static inline bool " + name + "(const wchar_t* p)"); file.println(" {"); file.println(" u4 c = Codepoint(p);"); file.println(" return codes[(u2) (blocks[c >> SHIFT] + c)] " + relation + ' ' + value + ";"); file.println(" }"); } static void printFooter(PrintStream file) { file.println(); file.println("#ifdef HAVE_JIKES_NAMESPACE"); file.println("} // Close namespace Jikes block"); file.println("#endif"); file.println(); } }