/** * Copyright 2015 JogAmp Community. All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. */ package com.jogamp.gluegen; import java.math.BigInteger; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import com.jogamp.gluegen.ASTLocusTag.ASTLocusTagProvider; import com.jogamp.gluegen.cgram.types.AliasedSymbol.AliasedSymbolImpl; import com.jogamp.gluegen.cgram.types.TypeComparator.AliasedSemanticSymbol; import com.jogamp.gluegen.cgram.types.TypeComparator.SemanticEqualityOp; /** * Represents a [native] constant expression, * comprises the [native] expression, see {@link #getNativeExpr()} * and the optional {@link CNumber} representation, see {@link #getNumber()}. *

* The representation of the equivalent java expression including * the result type is covered by {@link JavaExpr}, * which can be computed via {@link #computeJavaExpr(Map)}. *

*

* This class and its sub-classes define and convert all native expressions * to Java space. *

*/ public class ConstantDefinition extends AliasedSymbolImpl implements AliasedSemanticSymbol, ASTLocusTagProvider { public static final long UNSIGNED_INT_MAX_VALUE = 0xffffffffL; public static final BigInteger UNSIGNED_LONG_MAX_VALUE = new BigInteger("ffffffffffffffff", 16); /** * A Number, either integer, optionally [long, unsigned], * or floating point, optionally [double]. */ public static class CNumber { /** * {@code true} if number is integer and value stored in {@link #i}, * otherwise {@code false} for floating point and value stored in {@link #f}. */ public final boolean isInteger; /** {@code true} if number is a {@code long} {@link #isInteger}. */ public final boolean isLong; /** {@code true} if number is an {@code unsigned} {@link #isInteger}. */ public final boolean isUnsigned; /** The value if {@link #isInteger} */ public final long i; /** {@code true} if number is a {@code double precision} {@code floating point}, i.e. !{@link #isInteger}. */ public final boolean isDouble; /** The value if !{@link #isInteger} */ public final double f; /** ctor for integer number */ public CNumber(final boolean isLong, final boolean isUnsigned, final long value) { this.isInteger = true; this.isLong = isLong; this.isUnsigned = isUnsigned; this.i = value; this.isDouble = false; this.f = 0.0; } /** ctor for floating point number */ public CNumber(final boolean isDouble, final double value) { this.isInteger = false; this.isLong = false; this.isUnsigned = false; this.i = 0; this.isDouble = isDouble; this.f = value; } @Override public int hashCode() { return isInteger ? Long.valueOf(i).hashCode() : Double.valueOf(f).hashCode(); } @Override public boolean equals(final Object arg) { if (arg == this) { return true; } else if ( !(arg instanceof CNumber) ) { return false; } final CNumber t = (CNumber) arg; return isInteger == t.isInteger && ( isInteger ? i == t.i : f == t.f ); } public final String toJavaString() { if( isInteger ) { if( i >= 0 || isUnsigned ) { if( isLong ) { return "0x"+Long.toHexString(i)+"L"; } else { return "0x"+Integer.toHexString((int)i); } } else { if( isLong ) { return String.valueOf(i)+"L"; } else { return String.valueOf((int)i); } } } else { return String.valueOf(f) + ( !isDouble ? "f" : ""); } } @Override public final String toString() { final StringBuilder sb = new StringBuilder(); sb.append("["); if( isInteger ) { if( isUnsigned ) { sb.append("unsigned "); } if( isLong) { sb.append("long: "); } else { sb.append("int: "); } sb.append(i); } else { if( isDouble ) { sb.append("double: "); } else { sb.append("float: "); } sb.append(f); } sb.append("]"); return sb.toString(); } } /** * A valid java expression, including its result type, * usually generated from a native [C] expression, * see {@link JavaExpr#create(ConstantDefinition)}. */ public static class JavaExpr { public final String javaExpression; public final CNumber resultType; public final Number resultJavaType; public final String resultJavaTypeName; public JavaExpr(final String javaExpression, final CNumber resultType) { this.javaExpression = javaExpression; this.resultType = resultType; if( resultType.isDouble ) { resultJavaTypeName = "double"; resultJavaType = Double.valueOf(resultType.f); } else if( !resultType.isInteger ) { resultJavaTypeName = "float"; resultJavaType = Double.valueOf(resultType.f).floatValue(); } else if( resultType.isLong ) { resultJavaTypeName = "long"; resultJavaType = Long.valueOf(resultType.i); } else /* if( resultType.isInteger ) */ { resultJavaTypeName = "int"; resultJavaType = Long.valueOf(resultType.i).intValue(); } } /** * Computes a valid {@link JavaExpr java expression} based on the given {@link ConstantDefinition}, * which may either be a single {@link CNumber}, see {@link ConstantDefinition#getNumber()}, * or represents a native expression, see {@link ConstantDefinition#getExpr()}. */ public static JavaExpr compute(final ConstantDefinition constDef, final Map constMap) { final boolean debug = GlueGen.debug(); if( debug ) { System.err.println("ConstJavaExpr.create: "+constDef); } if( constDef.hasNumber() ) { // Already parsed as CNumber completely! if( debug ) { System.err.printf("V %s (isCNumber)%n", constDef); } return new JavaExpr(constDef.getNumber().toJavaString(), constDef.getNumber()); } final StringBuilder javaExpr = new StringBuilder(); final String nativeExpr = constDef.getNativeExpr(); // "calculates" the result type of a simple expression // example: (2+3)-(2.0f-3.0) -> Double // example: (1 << 2) -> Integer CNumber resultType = null; final Matcher matcher = patternCPPOperand.matcher(nativeExpr); int preStartIdx = 0; int opEndIdx = 0; while ( matcher.find() ) { final int opStartIdx = matcher.start(); if( opStartIdx > preStartIdx ) { final String sValue = nativeExpr.substring(preStartIdx, opStartIdx).trim(); if( sValue.length() > 0 ) { if( debug ) { System.err.printf("V %03d-%03d: %s%n", preStartIdx, opStartIdx, sValue); } resultType = processValue(constDef, sValue, constMap, resultType, javaExpr); javaExpr.append(" "); } } opEndIdx = matcher.end(); final String op = nativeExpr.substring(opStartIdx, opEndIdx); if( debug ) { System.err.printf("O %03d-%03d: %s%n", opStartIdx, opEndIdx, op); } javaExpr.append(op).append(" "); preStartIdx = opEndIdx; } if( opEndIdx < nativeExpr.length() ) { // tail .. final String sValue = nativeExpr.substring(opEndIdx).trim(); if( sValue.length() > 0 ) { if( debug ) { System.err.printf("V %03d %03d-%03d: %s (tail)%n", preStartIdx, opEndIdx, nativeExpr.length(), sValue); } resultType = processValue(constDef, sValue, constMap, resultType, javaExpr); } } final String javaExprS = javaExpr.toString().trim(); if( null == resultType ) { throw new GlueGenException("Cannot emit const \""+constDef.getName()+"\": value \""+nativeExpr+ "\", parsed \""+javaExprS+"\" does not contain a constant number", constDef.getASTLocusTag()); } return new JavaExpr(javaExprS, resultType); } private static CNumber processValue(final ConstantDefinition constDef, final String sValue, final Map constMap, CNumber resultType, final StringBuilder javaExpr) { final CNumber nValue = getANumber(constDef, sValue); if( null != nValue ) { resultType = evalType(resultType , nValue); javaExpr.append(nValue.toJavaString()); } else { // Lookup CNumber type in const-map, to evaluate this result type final JavaExpr cje = constMap.get(sValue); if( null != cje ) { resultType = evalType(resultType , cje.resultType); } javaExpr.append(sValue); } return resultType; } private static CNumber getANumber(final ConstantDefinition constDef, final String value) { try { final CNumber number = decodeANumber(value); if( null != number ) { return number; } } catch( final Throwable _t ) { final String msg = "Cannot emit const \""+constDef.getName()+"\": value \""+value+ "\" cannot be assigned to a int, long, float, or double"; throw new GlueGenException(msg, constDef.getASTLocusTag(), _t); } return null; } private static CNumber evalType(final CNumber resultType, final CNumber type) { //fast path if( type.isDouble ) { return type; } if( null != resultType ) { if( resultType.isInteger ) { if( resultType.isLong ) { /* resultType is Long */ if( !type.isInteger ) { /* resultType: Long -> [ Float || Double ] */ return type; } } else if( type.isLong || !type.isInteger ) { /* resultType: Integer -> [ Long || Float || Double ] */ return type; } } else if( !resultType.isInteger && !resultType.isDouble ) { if( type.isDouble ) { /* resultType: Float -> Double */ return type; } } } else { return type; } return resultType; } } private final boolean relaxedEqSem; private final String nativeExpr; private final CNumber number; private final boolean isEnum; private final String enumName; private final ASTLocusTag astLocus; /** * Constructor for plain const-values, non-enumerates. * @param name unique name of this constant expression * @param nativeExpr original [native] expression * @param number optional {@link CNumber} representing this constant. * If {@code null}, implementation attempts to derive a {@link CNumber} * of the given {@code nativeExpr}. * @param astLocus AST location of the represented constant. */ public ConstantDefinition(final String name, final String nativeExpr, final CNumber number, final ASTLocusTag astLocus) { this(name, nativeExpr, number, false, null, astLocus); } /** * Constructor for enumerates * @param name unique name of this constant expression * @param nativeExpr original [native] expression * @param number optional {@link CNumber} representing this constant. * If {@code null}, implementation attempts to derive a {@link CNumber} * of the given {@code nativeExpr}. * @param enumName optional name of the represented enumeration * @param astLocus AST location of the represented constant. */ public ConstantDefinition(final String name, final String nativeExpr, final CNumber number, final String enumName, final ASTLocusTag astLocus) { this(name, nativeExpr, number, true, enumName, astLocus); } /** * @param name unique name of this constant expression * @param nativeExpr original [native] expression * @param number optional {@link CNumber} representing this constant. * If {@code null}, implementation attempts to derive a {@link CNumber} * of the given {@code nativeExpr}. * @param isEnum {@code true} if this constant is an enumerate, otherwise {@code false}. * @param enumName optional name of the represented enumeration * @param astLocus AST location of the represented constant. */ private ConstantDefinition(final String name, final String nativeExpr, final CNumber number, final boolean isEnum, final String enumName, final ASTLocusTag astLocus) { super(name); this.nativeExpr = nativeExpr; this.relaxedEqSem = TypeConfig.relaxedEqualSemanticsTest(); if( null != number ) { this.number = number; } else { // Attempt to parse define string as number final CNumber iNum = decodeIntegerNumber(nativeExpr); if( null != iNum ) { this.number = iNum; } else { final CNumber fNum = decodeDecimalNumber(nativeExpr); if( null != fNum ) { this.number = fNum; } else { this.number = null; } } } this.isEnum = isEnum; this.enumName = enumName; this.astLocus = astLocus; } @Override public ASTLocusTag getASTLocusTag() { return astLocus; } /** * Hash by its given {@link #getName() name}. */ @Override public final int hashCode() { return getName().hashCode(); } /** * Equality test by its given {@link #getName() name}. */ @Override public final boolean equals(final Object arg) { if (arg == this) { return true; } else if ( !(arg instanceof ConstantDefinition) ) { return false; } else { final ConstantDefinition t = (ConstantDefinition)arg; return equals(getName(), t.getName()); } } @Override public final int hashCodeSemantics() { // 31 * x == (x << 5) - x int hash = 31 + ( null != getName() ? getName().hashCode() : 0 ); hash = ((hash << 5) - hash) + ( isEnum ? 1 : 0 ); hash = ((hash << 5) - hash) + ( null != enumName ? enumName.hashCode() : 0 ); hash = ((hash << 5) - hash) + ( null != number ? number.hashCode() : 0 ); return ((hash << 5) - hash) + ( !relaxedEqSem && null != nativeExpr ? nativeExpr.hashCode() : 0 ); } @Override public final boolean equalSemantics(final SemanticEqualityOp arg) { if (arg == this) { return true; } else if ( !(arg instanceof ConstantDefinition) ) { return false; } else { final ConstantDefinition t = (ConstantDefinition) arg; if( !equals(getName(), t.getName()) || isEnum != t.isEnum || !equals(enumName, t.enumName) ) { return false; } if( null != number ) { if( number.isInteger ) { return number.i == t.number.i; } else { return number.f == t.number.f; } } else { // define's string value may be semantical equal .. but formatted differently! return relaxedEqSem || equals(nativeExpr, t.nativeExpr); } } } /** Returns the original [native] expression. */ public String getNativeExpr() { return nativeExpr; } /** * Returns the parsed {@link CNumber} of the {@link #getNativeExpr() native expression}, * or {@code null} if the latter does not comprise a single number, * i.e. is a complex expression. */ public CNumber getNumber() { return number; } /** * Returns {@code true} if this instance represents has a {@link #getNumber() number}, * otherwise {@code false}. */ public boolean hasNumber() { return null != number; } /** Returns {@code null} if this definition was not part of an enumeration, or if the enumeration is anonymous. */ public String getEnumName() { return enumName; } public boolean isEnum() { return isEnum; } @Override public String toString() { return "ConstantDefinition [name \"" + getName() + "\", expression \"" + nativeExpr + "\", number "+number + "], enum[is " + isEnum + ", name \"" + enumName + "\"]]"; } private static boolean equals(final String s1, final String s2) { if (s1 == null || s2 == null) { if (s1 == null && s2 == null) { return true; } return false; } return s1.equals(s2); } /** * Computes the {@link JavaExpr java expression} based on this instance, * see {@link JavaExpr#create(ConstantDefinition)}. */ public final JavaExpr computeJavaExpr(final Map constMap) { return JavaExpr.compute(this, constMap); } // // Static utility functions for type detection // public static boolean isConstantExpression(final String value) { if( null != value && value.length() > 0 ) { // Single numeric value if ( isNumber(value) ) { return true; } // Find constant expressions like (1 << 3) // if found just pass them through, they will most likely work in java too // expressions containing identifiers are currently ignored (casts too). // The pattern includes 'patternCPPOperand', i.e. all supported operands used for constant-expression of define macros final String[] values = value.split("[\\s\\+\\-\\*\\/\\|\\&\\(\\)]|(\\<\\<)|(\\>\\>)|(\\~)"); // [ whitespace '(' ')' ] int numberCount = 0; for (final String s : values) { if( s.length() > 0 ) { if( isCPPOperand(s) ) { // OK } else if ( isNumber(s) ) { // OK numberCount++; } else { return false; } } } final boolean res = numberCount > 0; return res; } return false; } public static boolean isIdentifier(final String value) { boolean identifier = false; final char[] chars = value.toCharArray(); for (int i = 0; i < chars.length; i++) { final char c = chars[i]; if (i == 0) { if (Character.isJavaIdentifierStart(c)) { identifier = true; } } else { if (!Character.isJavaIdentifierPart(c)) { identifier = false; break; } } } return identifier; } /** * Returns either {@link #decodeIntegerNumber(String)}, * {@link #decodeDecimalNumber(String)} or {@code null}. * @param v */ public static CNumber decodeANumber(final String v) { final CNumber iNumber = ConstantDefinition.decodeIntegerNumber(v); if( null != iNumber ) { return iNumber; } return ConstantDefinition.decodeDecimalNumber(v); } /** * If the given string {@link #isIntegerNumber(String)}, * return the decoded integer value, represented as a {@code ANumber}, * otherwise returns {@code null}. *

* Method strips off sign prefix {@code +} * and integer modifier suffixes {@code [uUlL]} * before utilizing {@link Long#decode(String)}. *

* @param v */ public static CNumber decodeIntegerNumber(final String v) { if( null == v || !isIntegerNumber(v) ) { return null; } String s0 = v.trim(); if( 0 == s0.length() ) { return null; } if (s0.startsWith("+")) { s0 = s0.substring(1, s0.length()).trim(); if( 0 == s0.length() ) { return null; } } final boolean neg; if (s0.startsWith("-")) { s0 = s0.substring(1, s0.length()).trim(); if( 0 == s0.length() ) { return null; } neg = true; } else { neg = false; } // Test last two chars for [lL] and [uU] modifiers! boolean isUnsigned = false; boolean isLong = false; final int j = s0.length() - 2; for(int i = s0.length() - 1; i >= 0 && i >= j; i--) { final char lastChar = s0.charAt(s0.length()-1); if( lastChar == 'u' || lastChar == 'U' ) { s0 = s0.substring(0, s0.length()-1); isUnsigned = true; } else if( lastChar == 'l' || lastChar == 'L' ) { s0 = s0.substring(0, s0.length()-1); isLong = true; } else { // early out, no modifier match! break; } } if( 0 == s0.length() ) { return null; } final long res; if( isLong && isUnsigned ) { res = decodeULong(s0, neg); } else { if( neg ) { s0 = "-" + s0; } res = Long.decode(s0).longValue(); } final boolean isLong2 = isLong || ( !isUnsigned && ( Integer.MIN_VALUE > res || res > Integer.MAX_VALUE ) ) || ( isUnsigned && res > UNSIGNED_INT_MAX_VALUE ); return new CNumber(isLong2, isUnsigned, res); } private static long decodeULong(final String v, final boolean neg) throws NumberFormatException { final int radix; final int idx; if (v.startsWith("0x") || v.startsWith("0X")) { idx = 2; radix = 16; } else if (v.startsWith("#")) { idx = 1; radix = 16; } else if (v.startsWith("0") && v.length() > 1) { idx = 1; radix = 8; } else { idx = 0; radix = 10; } final String s0 = ( neg ? "-" : "" ) + v.substring(idx); final BigInteger res = new BigInteger(s0, radix); if( res.compareTo(UNSIGNED_LONG_MAX_VALUE) > 0 ) { throw new NumberFormatException("Value \""+v+"\" is > UNSIGNED_LONG_MAX"); } return res.longValue(); } /** * If the given string {@link #isDecimalNumber(String)}, * return the decoded floating-point value, represented as a {@code ANumber} object, * otherwise returns {@code null}. *

* Method utilizes {@link Double#valueOf(String)}. *

* @param v * @param isDouble return value for {@code double} flag */ public static CNumber decodeDecimalNumber(final String v) { if( null == v || !isDecimalNumber(v) ) { return null; } final String s0 = v.trim(); if( 0 == s0.length() ) { return null; } boolean _isDouble = false; final char lastChar = s0.charAt(s0.length()-1); if( lastChar == 'd' || lastChar == 'D' ) { _isDouble = true; } final double res = Double.valueOf(s0).doubleValue(); final double ares = Math.abs(res); return new CNumber(_isDouble || Float.MIN_VALUE > ares || ares > Float.MAX_VALUE, res); } /** * Matches {@link #isHexNumber(String)} or {@link #isDecimalOrIntNumber(String)}. */ public static boolean isNumber(final String s) { if( isHexNumber(s) ) { return true; } else { return isDecimalOrIntNumber(s); } } /** * Matches {@link #isHexNumber(String)} or {@link #patternIntegerNumber}. */ public static boolean isIntegerNumber(final String s) { if( isHexNumber(s) ) { return true; } else { return patternIntegerNumber.matcher(s).matches(); } } /** * Matches {@link #patternHexNumber}. */ public static boolean isHexNumber(final String s) { return patternHexNumber.matcher(s).matches(); } /** * Matches pattern for floating point number, * compatible and described in {@link Double#valueOf(String)}. */ public static boolean isDecimalNumber(final String s) { return patternDecimalNumber.matcher(s).matches(); } /** * Complete pattern for floating point and integer number, * covering {@link #patternDecimalNumber} and {@link #patternIntegerNumber}. */ public static boolean isDecimalOrIntNumber(final String s) { return patternDecimalOrIntNumber.matcher(s).matches(); } /** * Matches pattern for valid CPP operands, see {@link #patternCPPOperand}. */ public static boolean isCPPOperand(final String s) { return patternCPPOperand.matcher(s).matches(); } /** * Complete pattern for hexadecimal number, * including an optional sign {@code [+-]} and optional suffixes {@code [uUlL]}. */ public static Pattern patternHexNumber; /** * Complete pattern for floating point number, * compatible and described in {@link Double#valueOf(String)}. */ public final static Pattern patternDecimalNumber; /** * Complete pattern for floating point and integer number, * covering {@link #patternDecimalNumber} and {@link #patternIntegerNumber}. */ public final static Pattern patternDecimalOrIntNumber; /** * Complete pattern for integer number, * including an optional sign {@code [+-]} and optional suffixes {@code [uUlL]}. */ public final static Pattern patternIntegerNumber; /** * One of: {@code +} {@code -} {@code *} {@code /} {@code |} {@code &} {@code (} {@code )} {@code <<} {@code >>} {@code ~} *

* Expression excludes {@link #patternDecimalOrIntNumber}. *

*/ public static Pattern patternCPPOperand; static { final String WhiteSpace = "[\\x00-\\x20]*"; final String Digits = "(\\p{Digit}+)"; final String HexDigits = "(\\p{XDigit}+)"; final String IntTypeSuffix = "(" + "[uU]|" + "([uU][lL])|" + "[lL]|" + "([lL][uU])" + ")"; final String hexRegex = WhiteSpace + // Optional leading "whitespace" "[+-]?" + // Optional sign character // HexDigits IntTypeSuffix_opt "0[xX]" + HexDigits + IntTypeSuffix + "?" + WhiteSpace // Optional trailing "whitespace" ; patternHexNumber = Pattern.compile(hexRegex); final String intRegex = WhiteSpace + // Optional leading "whitespace" "[+-]?" + // Optional sign character // Digits IntTypeSuffix_opt Digits + IntTypeSuffix + "?" + WhiteSpace // Optional trailing "whitespace" ; patternIntegerNumber = Pattern.compile(intRegex); // an exponent is 'e' or 'E' followed by an optionally // signed decimal integer. final String Exp = "[eE][+-]?"+Digits; final String fpRegex = WhiteSpace + // Optional leading "whitespace" "[+-]?" + // Optional sign character "("+ "NaN|" + // "NaN" string "Infinity|" + // "Infinity" string // A decimal floating-point string representing a finite positive // number without a leading sign has at most five basic pieces: // Digits . Digits ExponentPart FloatTypeSuffix // // Since this method allows integer-only strings as input // in addition to strings of floating-point literals, the // two sub-patterns below are simplifications of the grammar // productions from the Java Language Specification, 2nd // edition, section 3.10.2. "("+ "("+ // Digits ._opt Digits_opt ExponentPart_opt FloatTypeSuffix_opt "("+Digits+"(\\.)?("+Digits+"?)("+Exp+")?)|"+ // . Digits ExponentPart_opt FloatTypeSuffix_opt "(\\.("+Digits+")("+Exp+")?)|"+ // Hexadecimal w/ binary exponent "(" + "(" + // Hexadecimal strings // 0[xX] HexDigits ._opt BinaryExponent FloatTypeSuffix_opt "(0[xX]" + HexDigits + "(\\.)?)|" + // 0[xX] HexDigits_opt . HexDigits BinaryExponent FloatTypeSuffix_opt "(0[xX]" + HexDigits + "?(\\.)" + HexDigits + ")" + ")" + // binary exponent "[pP][+-]?" + Digits + ")" + ")" + "[fFdD]?"+ ")"+ ")" + WhiteSpace // Optional trailing "whitespace" ; patternDecimalNumber = Pattern.compile(fpRegex); final String fpOrIntRegex = WhiteSpace + // Optional leading "whitespace" "[+-]?" + // Optional sign character "("+ "NaN|" + // "NaN" string "Infinity|" + // "Infinity" string // Matching integers w/ IntTypeSuffix, // which are otherwise not matched by the below floating point matcher! // Digits IntTypeSuffix "(" + Digits + IntTypeSuffix +")|" + // A decimal floating-point string representing a finite positive // number without a leading sign has at most five basic pieces: // Digits . Digits ExponentPart FloatTypeSuffix // // Since this method allows integer-only strings as input // in addition to strings of floating-point literals, the // two sub-patterns below are simplifications of the grammar // productions from the Java Language Specification, 2nd // edition, section 3.10.2. "("+ "("+ // Digits ._opt Digits_opt ExponentPart_opt FloatTypeSuffix_opt "(" + Digits + "(\\.)?(" + Digits + "?)(" + Exp + ")?)|" + // . Digits ExponentPart_opt FloatTypeSuffix_opt "(\\.(" + Digits + ")(" + Exp + ")?)|" + // Hexadecimal w/ binary exponent "(" + "(" + // Hexadecimal strings // 0[xX] HexDigits ._opt BinaryExponent FloatTypeSuffix_opt "(0[xX]" + HexDigits + "(\\.)?)|" + // 0[xX] HexDigits_opt . HexDigits BinaryExponent FloatTypeSuffix_opt "(0[xX]" + HexDigits + "?(\\.)" + HexDigits + ")" + ")" + // binary exponent "[pP][+-]?" + Digits + ")" + ")" + "[fFdD]?"+ ")"+ ")" + WhiteSpace // Optional trailing "whitespace" ; patternDecimalOrIntNumber = Pattern.compile(fpOrIntRegex); final String fpOrIntRegex2 = WhiteSpace + // Optional leading "whitespace" // "[+-]?" + // Optional sign character "("+ "NaN|" + // "NaN" string "Infinity|" + // "Infinity" string // Matching integers w/ IntTypeSuffix, // which are otherwise not matched by the below floating point matcher! // Digits IntTypeSuffix "(" + Digits + IntTypeSuffix +")|" + // A decimal floating-point string representing a finite positive // number without a leading sign has at most five basic pieces: // Digits . Digits ExponentPart FloatTypeSuffix // // Since this method allows integer-only strings as input // in addition to strings of floating-point literals, the // two sub-patterns below are simplifications of the grammar // productions from the Java Language Specification, 2nd // edition, section 3.10.2. "("+ "("+ // Digits ._opt Digits_opt ExponentPart_opt FloatTypeSuffix_opt "(" + Digits + "(\\.)?(" + Digits + "?)(" + Exp + ")?)|" + // . Digits ExponentPart_opt FloatTypeSuffix_opt "(\\.(" + Digits + ")(" + Exp + ")?)|" + // Hexadecimal w/ binary exponent "(" + "(" + // Hexadecimal strings // 0[xX] HexDigits ._opt BinaryExponent FloatTypeSuffix_opt "(0[xX]" + HexDigits + "(\\.)?)|" + // 0[xX] HexDigits_opt . HexDigits BinaryExponent FloatTypeSuffix_opt "(0[xX]" + HexDigits + "?(\\.)" + HexDigits + ")" + ")" + // binary exponent "[pP][+-]?" + Digits + ")" + ")" + "[fFdD]?"+ ")"+ ")" + WhiteSpace // Optional trailing "whitespace" ; patternCPPOperand = Pattern.compile("(?!"+fpOrIntRegex2+")[\\+\\-\\*\\/\\|\\&\\(\\)]|(\\<\\<)|(\\>\\>)|(\\~)"); } }