aboutsummaryrefslogtreecommitdiffstats
path: root/src/java/com/jogamp/gluegen/ConstantDefinition.java
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2015-04-01 15:51:04 +0200
committerSven Gothel <[email protected]>2015-04-01 15:51:04 +0200
commitc6d5ee30e023d030697f14ae2c444ce7a5542e94 (patch)
tree0013fef29a228522b6791630385932d38a36a399 /src/java/com/jogamp/gluegen/ConstantDefinition.java
parentc156343fec33ceea7f238b9766a9f4985fb92687 (diff)
Bug 1153 - GlueGen: Support [const] [native] expressions and conversion to java space, incl. [native] numbers
Rewrite ConstantDefinition: Add sub-class CNumber: - containing integer/float values and their original qualifiers [long, double, unsigned] - conversion to java number ConstantDefinition: - holds native expression - optionally holds CNumber representing native expression, if [only] a number - can compute equivalent java expression with result type (JavaExpr) Add static native number reg-expression for number detection and parsing. Add static native number to CNumber conversion methods. +++ Retrieve full LISP tree and convert to serialized expression to be utilized for expressions used in enumerates. Parse enumerates, allowing const native expressions: - Utilize ConstantDefinition either for definite CNumber or expression - Simply add "+1" for new default values, if previous is an expression
Diffstat (limited to 'src/java/com/jogamp/gluegen/ConstantDefinition.java')
-rw-r--r--src/java/com/jogamp/gluegen/ConstantDefinition.java831
1 files changed, 737 insertions, 94 deletions
diff --git a/src/java/com/jogamp/gluegen/ConstantDefinition.java b/src/java/com/jogamp/gluegen/ConstantDefinition.java
index f8f4973..675c6d7 100644
--- a/src/java/com/jogamp/gluegen/ConstantDefinition.java
+++ b/src/java/com/jogamp/gluegen/ConstantDefinition.java
@@ -1,38 +1,35 @@
-/*
- * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved.
+/**
+ * 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:
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
*
- * - Redistribution of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
*
- * - Redistribution 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.
+ * 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.
*
- * Neither the name of Sun Microsystems, Inc. or the names of
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * This software is provided "AS IS," without a warranty of any kind. ALL
- * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
- * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
- * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
- * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
- * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
- * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR
- * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR
- * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
- * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
- * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
- * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ * 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;
@@ -40,55 +37,351 @@ 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 the definition of a constant which was provided either
- via a #define statement or through an enum definition. */
+/**
+ * Represents a [native] constant expression,
+ * comprises the [native] expression, see {@link #getNativeExpr()}
+ * and the optional {@link CNumber} representation, see {@link #getNumber()}.
+ * <p>
+ * The representation of the equivalent java expression including
+ * the result type is covered by {@link JavaExpr},
+ * which can be computed via {@link #computeJavaExpr(Map)}.
+ * </p>
+ * <p>
+ * This class and its sub-classes define and convert all native expressions
+ * to Java space.
+ * </p>
+ */
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" : "");
+ }
+ }
+ 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<String, ConstantDefinition.JavaExpr> 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<String, ConstantDefinition.JavaExpr> 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 sValue;
- private final long iValue;
- private final boolean hasIntValue;
+ private final String nativeExpr;
+ private final CNumber number;
private final boolean isEnum;
private final String enumName;
private final ASTLocusTag astLocus;
- /** Covering enums */
+ /**
+ * 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 long value,
- final String enumName,
+ final String nativeExpr,
+ final CNumber number,
final ASTLocusTag astLocus) {
- super(name);
- this.relaxedEqSem = TypeConfig.relaxedEqualSemanticsTest();
- this.sValue = String.valueOf(value);
- this.iValue = value;
- this.hasIntValue = true;
- this.isEnum = true;
- this.enumName = enumName;
- this.astLocus = astLocus;
+ this(name, nativeExpr, number, false, null, astLocus);
}
-
- /** Covering defines */
+ /**
+ * 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 value,
- final ASTLocusTag astLocus) {
+ 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();
- this.sValue = value;
- {
+ if( null != number ) {
+ this.number = number;
+ } else {
// Attempt to parse define string as number
- long v;
- boolean b;
- try {
- v = Long.decode(value).longValue();
- b = true;
- } catch (final NumberFormatException e) {
- v = 0;
- b = false;
+ 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.iValue = v;
- this.hasIntValue = b;
}
- this.isEnum = false;
- this.enumName = null;
+ this.isEnum = isEnum;
+ this.enumName = enumName;
this.astLocus = astLocus;
}
@@ -122,8 +415,10 @@ public class ConstantDefinition extends AliasedSymbolImpl implements AliasedSema
public final int hashCodeSemantics() {
// 31 * x == (x << 5) - x
int hash = 31 + ( null != getName() ? getName().hashCode() : 0 );
- hash = ((hash << 5) - hash) + ( null != sValue ? sValue.hashCode() : 0 );
- return ((hash << 5) - hash) + ( null != enumName ? enumName.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
@@ -135,30 +430,49 @@ public class ConstantDefinition extends AliasedSymbolImpl implements AliasedSema
} else {
final ConstantDefinition t = (ConstantDefinition) arg;
if( !equals(getName(), t.getName()) ||
+ isEnum != t.isEnum ||
!equals(enumName, t.enumName) ) {
return false;
}
- if( hasIntValue ) {
- return iValue == t.iValue;
+ 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(sValue, t.sValue);
+ return relaxedEqSem || equals(nativeExpr, t.nativeExpr);
}
}
}
- public String getValue() { return sValue; }
- /** Returns null if this definition was not part of an
- enumeration, or if the enum was anonymous. */
+ /** 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()
- + ", value " + sValue + " (isInt " + hasIntValue
- + "), enumName " + enumName + ", isEnum " + isEnum + "]";
+ return "ConstantDefinition [name \"" + getName()
+ + "\", expression \"" + nativeExpr
+ + "\", number "+number
+ + "], enum[is " + isEnum + ", name \"" + enumName + "\"]]";
}
private static boolean equals(final String s1, final String s2) {
@@ -172,6 +486,18 @@ public class ConstantDefinition extends AliasedSymbolImpl implements AliasedSema
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<String, ConstantDefinition.JavaExpr> 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
@@ -222,32 +548,260 @@ public class ConstantDefinition extends AliasedSymbolImpl implements AliasedSema
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}.
+ * <p>
+ * Method strips off sign prefix {@code +}
+ * and integer modifier suffixes {@code [uUlL]}
+ * before utilizing {@link Long#decode(String)}.
+ * </p>
+ * @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}.
+ * <p>
+ * Method utilizes {@link Double#valueOf(String)}.
+ * </p>
+ * @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 isDecimalNumber(s);
+ 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();
}
- public static Pattern patternHexNumber = Pattern.compile("0[xX][0-9a-fA-F]+[lLfFuU]?");
+
+ /**
+ * Matches pattern for <code>floating point</code> number,
+ * compatible and described in {@link Double#valueOf(String)}.
+ */
+ public static boolean isDecimalNumber(final String s) {
+ return patternDecimalNumber.matcher(s).matches();
+ }
+
+ /**
+ * Complete pattern for <code>floating point</code> <i>and</i> <code>integer</code> number,
+ * covering {@link #patternDecimalNumber} <i>and</i> {@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 <code>hexadecimal</code> number,
+ * including an optional sign {@code [+-]} and optional suffixes {@code [uUlL]}.
+ */
+ public static Pattern patternHexNumber;
/**
* Complete pattern for <code>floating point</code> number,
* compatible and described in {@link Double#valueOf(String)}.
*/
- public static Pattern patternDecimalNumber;
- private static String fpRegex;
+ public final static Pattern patternDecimalNumber;
+
+ /**
+ * Complete pattern for <code>floating point</code> <i>and</i> <code>integer</code> number,
+ * covering {@link #patternDecimalNumber} <i>and</i> {@link #patternIntegerNumber}.
+ */
+ public final static Pattern patternDecimalOrIntNumber;
+
+ /**
+ * Complete pattern for <code>integer</code> 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 >>}
+ * <p>
+ * Expression excludes {@link #patternDecimalOrIntNumber}.
+ * </p>
+ */
+ 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;
- fpRegex =
- ("[\\x00-\\x20]*"+ // Optional leading "whitespace"
+ final String fpRegex =
+ WhiteSpace + // Optional leading "whitespace"
"[+-]?" + // Optional sign character
"("+
"NaN|" + // "NaN" string
@@ -289,23 +843,112 @@ public class ConstantDefinition extends AliasedSymbolImpl implements AliasedSema
"[fFdD]?"+
")"+
")" +
- "[\\x00-\\x20]*"// Optional trailing "whitespace"
- );
+ WhiteSpace // Optional trailing "whitespace"
+ ;
patternDecimalNumber = Pattern.compile(fpRegex);
- }
- public static boolean isDecimalNumber(final String s) {
- return patternDecimalNumber.matcher(s).matches();
- }
- public static boolean isCPPOperand(final String s) {
- return patternCPPOperand.matcher(s).matches();
- }
- /**
- * One of: {@code +} {@code -} {@code *} {@code /} {@code |} {@code &} {@code (} {@code )} {@code <<} {@code >>}
- * <p>
- * Expression excludes {@link #patternDecimalNumber}.
- * </p>
- */
- public static Pattern patternCPPOperand = 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+")[\\+\\-\\*\\/\\|\\&\\(\\)]|(\\<\\<)|(\\>\\>)");
+ }
}