package com.babel17.interpreter.parser; import java.math.*; import com.babel17.syntaxtree.Location; import com.babel17.syntaxtree.Source; final class ConstParser { static BigInteger str2int(String s) { return new java.math.BigInteger(s); } static BigInteger signedStr2int(String s) { int sign = 1; int first = 0; if (s.charAt(0) == '+') { first = 1; } else if (s.charAt(0) == '-') { first = 1; sign = -1; } BigInteger x = str2int(s.substring(first)); if (sign < 0) x = x.negate(); return x; } static BigInteger fstr2int(String s, int base) { BigInteger num = BigInteger.ZERO; for (char c : s.toCharArray()) { int digit; if (c >= '0' && c <= '9') { digit = c - '0'; } else if (c >= 'A' && c <= 'F') { digit = (c - 'A') + 10; } else if (c >= 'a' && c <= 'f') { digit = (c - 'a') + 10; } else { throw new RuntimeException("unknown digit '" + c + "'"); } if (digit >= base) { throw new RuntimeException("parsing " + s + ": digit " + digit + " is invalid for base " + base); } num = num.multiply(BigInteger.valueOf(base)).add(BigInteger.valueOf(digit)); } return num; } static BigInteger num(String s) { if (s.startsWith("0x")) { return fstr2int(s.substring(2), 16); } else if (s.startsWith("0o")) { return fstr2int(s.substring(2), 8); } else if (s.startsWith("0b")) { return fstr2int(s.substring(2), 2); } else { return fstr2int(s, 10); } } static BigInteger[] decimalFloat(String s) { int dot = s.indexOf("."); int exp = s.indexOf("e"); if (exp < 0) exp = s.indexOf("E"); String mantissa = null; String exponent = "0"; if (dot < 0 && exp < 0) { mantissa = s; } else if (dot >= 0 && exp < 0) { mantissa = s.substring(0, dot) + s.substring(dot + 1); } else if (dot < 0 && exp >= 0) { mantissa = s.substring(0, exp); exponent = s.substring(exp+1); } else { // dot >= 0 && exp >= 0 mantissa = s.substring(0, dot) + s.substring(dot + 1, exp); exponent = s.substring(exp+1); } BigInteger e = signedStr2int(exponent); if (dot >= 0) e = e.subtract(BigInteger.valueOf(mantissa.length() - dot)); return new BigInteger[]{str2int(mantissa), e}; } private static class StringParser { String v; boolean empty; int index; int pos; Source source; public StringParser(Source source, String s) { v = new String(s); // do this to protect against the Java 1.5 offsetByCodePoints bug empty = v.length() <= 2; index = 1; pos = 1; this.source = source; } int nextCodePoint() { int code = v.codePointAt(index); index = v.offsetByCodePoints(index, 1); empty = index + 1 >= v.length(); pos++; return code; } String readString(int len) { StringBuffer buf = new StringBuffer(); for (int i = 0; i < len; i++) { buf.appendCodePoint(nextCodePoint()); } return buf.toString(); } StringBuffer buffer = new StringBuffer(); void addCodePoint(BigInteger _codePoint, int cpos) { long codePoint = _codePoint.longValue(); if (codePoint < 0 || codePoint > 0x10FFFF) { throw new ParseException(new Location(source, 1, cpos + 1). add(new Location(source, 1, pos)), "invalid code point " + codePoint); } else if (codePoint >= 0xD800 && codePoint < 0xE000) { throw new ParseException(new Location(source, 1, cpos + 1). add(new Location(source, 1, pos)), "invalid code point (surrogate char) " + codePoint); } else { buffer.appendCodePoint((int) codePoint); } } void addCodePoint(int codePoint, int cpos) { addCodePoint(BigInteger.valueOf(codePoint), cpos); } String parse() { while (!empty) { int cpos = pos; int codePoint = nextCodePoint(); if (codePoint == '\\') { switch (codePoint = nextCodePoint()) { case 'u': addCodePoint(fstr2int(readString(4), 16), cpos); break; case 'U': addCodePoint(fstr2int(readString(8), 16), cpos); break; case '\\': addCodePoint(0x5C, cpos); break; case '"': addCodePoint(0x22, cpos); break; case 'n': addCodePoint(10, cpos); break; case 'r': addCodePoint(13, cpos); break; default: throw new ParseException(new Location(source, 1, pos - 1). add(new Location(source, 1, pos + 1)), "unknown escape character (codepoint = " + codePoint + ")"); } } else { addCodePoint(codePoint, cpos); } } return buffer.toString(); } } public static String string(Source source, String s) { StringParser p = new StringParser(source, s); return p.parse(); } }