/*******************************************************************************
* Copyright (c) 2004, 2015 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* John Camelon (IBM) - Initial API and implementation
* Markus Schorn (Wind River Systems)
* Sergey Prigogin (Google)
* Richard Eames
*******************************************************************************/
package org.eclipse.cdt.internal.core.dom.parser.cpp;
import static org.eclipse.cdt.core.dom.ast.IASTExpression.ValueCategory.LVALUE;
import static org.eclipse.cdt.core.dom.ast.IASTExpression.ValueCategory.PRVALUE;
import org.eclipse.cdt.core.dom.ast.ASTVisitor;
import org.eclipse.cdt.core.dom.ast.DOMException;
import org.eclipse.cdt.core.dom.ast.IASTImplicitDestructorName;
import org.eclipse.cdt.core.dom.ast.IASTImplicitName;
import org.eclipse.cdt.core.dom.ast.IBasicType;
import org.eclipse.cdt.core.dom.ast.IBasicType.Kind;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.IPointerType;
import org.eclipse.cdt.core.dom.ast.IScope;
import org.eclipse.cdt.core.dom.ast.ISemanticProblem;
import org.eclipse.cdt.core.dom.ast.IType;
import org.eclipse.cdt.core.dom.ast.IValue;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTLiteralExpression;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPFunction;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPParameter;
import org.eclipse.cdt.core.parser.util.CharArrayUtils;
import org.eclipse.cdt.internal.core.dom.parser.ASTNode;
import org.eclipse.cdt.internal.core.dom.parser.CStringValue;
import org.eclipse.cdt.internal.core.dom.parser.FloatingPointValue;
import org.eclipse.cdt.internal.core.dom.parser.IntegralValue;
import org.eclipse.cdt.internal.core.dom.parser.ProblemBinding;
import org.eclipse.cdt.internal.core.dom.parser.ProblemType;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPSemantics;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPVisitor;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.EvalBinding;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.EvalFixed;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.EvalFunctionCall;
import org.eclipse.cdt.internal.core.parser.scanner.ExpressionEvaluator;
import org.eclipse.cdt.internal.core.parser.scanner.ExpressionEvaluator.EvalException;
/**
* Represents a C++ literal.
*/
public class CPPASTLiteralExpression extends ASTNode implements ICPPASTLiteralExpression, ICPPEvaluationOwner {
private static final EvalFixed EVAL_TRUE = new EvalFixed(CPPBasicType.BOOLEAN, PRVALUE, IntegralValue.create(true));
private static final EvalFixed EVAL_FALSE = new EvalFixed(CPPBasicType.BOOLEAN, PRVALUE, IntegralValue.create(false));
private static final EvalFixed EVAL_NULL_PTR = new EvalFixed(CPPBasicType.NULL_PTR, PRVALUE, IntegralValue.create(0));
public static final CPPASTLiteralExpression INT_ZERO =
new CPPASTLiteralExpression(lk_integer_constant, new char[] {'0'});
private int fKind;
private char[] fValue = CharArrayUtils.EMPTY;
private int fStringLiteralSize = -1; // Accounting for escape sequences and the null terminator.
private char[] fSuffix = CharArrayUtils.EMPTY;
private boolean fIsCompilerSuffix = true;
private ICPPEvaluation fEvaluation;
private IBinding fUserDefinedLiteralOperator;
private IASTImplicitName[] fImplicitNames;
public CPPASTLiteralExpression() {
}
public CPPASTLiteralExpression(int kind, char[] value) {
this.fKind = kind;
this.fValue = value;
}
public CPPASTLiteralExpression(int kind, char[] value, char[] suffix, boolean isCompilerSuffix) {
this(kind, value);
this.setSuffix(suffix);
}
@Override
public CPPASTLiteralExpression copy() {
return copy(CopyStyle.withoutLocations);
}
@Override
public CPPASTLiteralExpression copy(CopyStyle style) {
CPPASTLiteralExpression copy = new CPPASTLiteralExpression(fKind,
fValue == null ? null : fValue.clone(),
fSuffix == null ? null : fSuffix.clone(),
fIsCompilerSuffix);
copy.setOffsetAndLength(this);
return copy(copy, style);
}
@Override
public int getKind() {
return fKind;
}
@Override
public void setKind(int value) {
assertNotFrozen();
fKind = value;
}
@Override
public char[] getValue() {
return fValue;
}
@Override
public void setValue(char[] value) {
assertNotFrozen();
this.fValue= value;
}
public char[] getSuffix() {
return fSuffix;
}
public void setSuffix(char[] suffix) {
this.fSuffix = suffix;
}
public void calculateSuffix() {
this.calculateSuffix(CharArrayUtils.EMPTY);
}
/**
* Returns the suffix of a user-defined literal integer or float
* @param compilerSuffixes
*/
public void calculateSuffix(char[] compilerSuffixes) {
try {
switch (fKind) {
case lk_float_constant:
case lk_integer_constant:
int udOffset = (fValue[0] == '.' ? afterDecimalPoint(0) : integerLiteral());
if (udOffset > 0) {
/*
* 2.14.8.1
* "If a token matches both user-defined-literal and another literal kind, it is
* treated as the latter"
*/
setSuffix(CharArrayUtils.subarray(fValue, udOffset, -1));
for (int i = 0; i < fSuffix.length; i++) {
switch (fSuffix[i]) {
case 'l': case 'L':
case 'u': case 'U':
case 'f': case 'F':
continue;
}
for (int j = 0; j < compilerSuffixes.length; j++) {
if (fSuffix[i] == compilerSuffixes[j]) {
continue;
}
}
fIsCompilerSuffix = false;
// Remove the suffix from the value if it's a UDL
setValue(CharArrayUtils.subarray(fValue, 0, udOffset));
break;
}
}
break;
case lk_string_literal:
{
final int offset = CharArrayUtils.lastIndexOf('"', fValue, CharArrayUtils.indexOf('"', fValue) + 1);
if (offset > 0) {
setSuffix(CharArrayUtils.subarray(fValue, offset + 1, -1));
if (fSuffix.length > 0) {
fIsCompilerSuffix = false;
}
}
}
break;
case lk_char_constant:
{
final int offset = CharArrayUtils.lastIndexOf('\'', fValue, CharArrayUtils.indexOf('\'', fValue) + 1);
if (offset > 0) {
setSuffix(CharArrayUtils.subarray(fValue, offset + 1, -1));
if (fSuffix.length > 0) {
fIsCompilerSuffix = false;
}
}
}
break;
}
} catch (ArrayIndexOutOfBoundsException e) {
// pass
}
}
@Override
public String toString() {
return new String(fValue);
}
@Override
public IASTImplicitDestructorName[] getImplicitDestructorNames() {
return IASTImplicitDestructorName.EMPTY_NAME_ARRAY; // Literal expression does not call destructors.
}
@Override
public boolean accept(ASTVisitor action) {
if (action.shouldVisitExpressions) {
switch (action.visit(this)) {
case ASTVisitor.PROCESS_ABORT: return false;
case ASTVisitor.PROCESS_SKIP: return true;
default: break;
}
}
if (action.shouldVisitImplicitNames) {
for (IASTImplicitName name : getImplicitNames()) {
if (!name.accept(action)) return false;
}
}
if (action.shouldVisitExpressions) {
switch (action.leave(this)) {
case ASTVisitor.PROCESS_ABORT: return false;
case ASTVisitor.PROCESS_SKIP: return true;
default: break;
}
}
return true;
}
private int computeStringLiteralSize() {
int start = 0, end = fValue.length - 1 - getSuffix().length;
boolean isRaw = false;
// Skip past a prefix affecting the character type.
if (fValue[0] == 'L' || fValue[0] == 'u' || fValue[0] == 'U') {
++start;
}
// If there is an 'R' prefix, skip past it but take note of it.
if (fValue[start] == 'R') {
++start;
isRaw = true;
}
// Now we should have a quote-enclosed string. Skip past the quotes.
if (!(fValue[start] == '"' && fValue[end] == '"')) {
// Unexpected!
return 0;
}
++start;
--end;
// If we have a raw string, skip past the raw prefix.
if (isRaw) {
while (fValue[start] != '(' && start <= end) {
++start;
--end;
}
// Now we should have a parenthesis-enclosed string.
if (!(fValue[start] == '(' && fValue[end] == ')')) {
// Unexpected!
return 0;
}
// Since the string is raw, we don't need to process
// escape sequences, so the size is just the number
// of remaining characters, plus 1 for the null terminator.
return (end - start + 1) + 1;
}
// Otherwise, we have a non-raw string and we need to
// process escape sequences.
int length = 0;
boolean escaping = false;
for (; start <= end; ++start) {
if (escaping) {
escaping = false;
++length;
} else if (fValue[start] == '\\') {
escaping = true;
} else {
++length;
}
// TODO: Handle fancier things like octal literals.
}
// + 1 for null terminator.
return length + 1;
}
private IValue getStringLiteralSize() {
if (fStringLiteralSize == -1) {
fStringLiteralSize = computeStringLiteralSize();
}
return IntegralValue.create(fStringLiteralSize);
}
private IType getStringType() {
if (fSuffix.length > 0) {
return getUserDefinedLiteralOperatorType();
}
IType type = new CPPBasicType(getBasicCharKind(), 0, this);
type = new CPPQualifierType(type, true, false);
return new CPPArrayType(type, getStringLiteralSize());
}
private IType getCharType() {
return fSuffix.length > 0 ? getUserDefinedLiteralOperatorType() : new CPPBasicType(getBasicCharKind(), 0, this);
}
private IBinding getUserDefinedLiteralOperator() {
if (!fIsCompilerSuffix && fUserDefinedLiteralOperator == null) {
try {
fUserDefinedLiteralOperator = CPPSemantics.findUserDefinedLiteralOperator(this);
} catch (DOMException e) {
}
if (fUserDefinedLiteralOperator == null) {
fUserDefinedLiteralOperator = new ProblemBinding(this, ISemanticProblem.BINDING_NOT_FOUND,
fSuffix);
}
}
return fUserDefinedLiteralOperator;
}
// 13.5.8
private IType getUserDefinedLiteralOperatorType() {
IType ret = new ProblemType(ISemanticProblem.TYPE_UNRESOLVED_NAME);
IBinding func = getUserDefinedLiteralOperator();
if (func != null && func instanceof ICPPFunction) {
ret = ((ICPPFunction) func).getType().getReturnType();
}
return ret;
}
public char[] getOperatorName() {
return CharArrayUtils.concat("operator \"\"".toCharArray(), fSuffix); //$NON-NLS-1$
}
public Kind getBasicCharKind() {
switch (fValue[0]) {
case 'L':
return Kind.eWChar;
case 'u':
return Kind.eChar16;
case 'U':
return Kind.eChar32;
default:
return Kind.eChar;
}
}
private IType classifyTypeOfFloatLiteral() {
final char[] lit= fSuffix;
final int len= lit.length;
Kind kind= Kind.eDouble;
int flags= 0;
if (len > 0) {
if (fIsCompilerSuffix) {
switch (lit[len - 1]) {
case 'f': case 'F':
kind= Kind.eFloat;
break;
case 'l': case 'L':
flags |= IBasicType.IS_LONG;
break;
}
} else {
return getUserDefinedLiteralOperatorType();
}
}
return new CPPBasicType(kind, flags, this);
}
private IType classifyTypeOfIntLiteral() {
int makelong= 0;
boolean unsigned= false;
final char[] lit= fSuffix;
int flags= 0;
if (fIsCompilerSuffix) {
for (int i= lit.length - 1; i >= 0; i--) {
final char c= lit[i];
if (!(c > 'f' && c <= 'z') && !(c > 'F' && c <= 'Z')) {
break;
}
switch (c) {
case 'u':
case 'U':
unsigned = true;
break;
case 'l':
case 'L':
makelong++;
break;
}
}
if (unsigned) {
flags |= IBasicType.IS_UNSIGNED;
}
if (makelong > 1) {
flags |= IBasicType.IS_LONG_LONG;
} else if (makelong == 1) {
flags |= IBasicType.IS_LONG;
}
} else if (lit.length > 0) {
return getUserDefinedLiteralOperatorType();
}
return new CPPBasicType(Kind.eInt, flags, this);
}
private int integerLiteral() {
int i = 0;
char c = fValue[i++];
if (c == '0' && i < fValue.length) {
// Probably octal/hex/binary
c = fValue[i];
switch ((c | 0x20)) {
case 'x':
return probablyHex(i);
case 'b':
return probablyBinary(i);
case '0': case '1': case '2': case '3':
case '4': case '5': case '6': case '7':
/* octal-literal:
* 0
* octal-literal octal-digit
*/
while (isOctal(c) && i < fValue.length) {
c = fValue[++i];
}
break;
case '.':
return afterDecimalPoint(i);
}
/*
* If there is an 8 or 9, then we have a malformed octal
*/
if (c == '8' || c == '9') {
// eat remaining numbers
c = fValue[i];
while (Character.isDigit(c) && i < fValue.length) {
c = fValue[++i];
}
}
/*
* A floating-point constant could also have a leading zero
*/
return handleDecimalOrExponent(c, i);
} else if (Character.isDigit(c)) {
/* decimal-literal :
* nonzero-digit (c has to be this to get into this else)
* decimal-literal digit
*/
c = fValue[i];
while (Character.isDigit(c) && i < fValue.length) {
c = fValue[++i];
}
return handleDecimalOrExponent(c, i);
} else {
// Somehow we got called and there wasn't a digit
// Shouldn't get here
assert false;
}
return i;
}
/*
* Consumes a decimal point or exponent, if present.
*/
private int handleDecimalOrExponent(char c, int i) {
if (c == '.') {
return afterDecimalPoint(i);
} else if ((c | 0x20) == 'e') {
return exponentPart(i);
}
return i;
}
/*
* Called with the expectation that fValue[i] == '.'
*/
private int afterDecimalPoint(int i) {
char c = fValue[++i];
while (Character.isDigit(c) && i < fValue.length) {
c = fValue[++i];
}
if ((c | 0x20) == 'e') {
return exponentPart(i);
}
return i;
}
/*
* Called with the expectation that c == 'e'
*/
private int exponentPart(int i) {
char c = fValue[++i];
// optional '+' or '-'
if (c == '+' || c == '-') {
c = fValue[++i];
}
while (Character.isDigit(c) && i < fValue.length) {
c = fValue[++i];
}
// If there were no digits following the 'e' then we have
// D.De or .De which is a UDL on a double
return i--;
}
// GCC's binary constant notation
private int probablyBinary(int i) {
char c = fValue[++i];
if (c == '1' || c == '0') {
while (c == '1' || c == '0' && i < fValue.length) {
c = fValue[i++];
}
if (Character.isDigit(c)) {
// UDL can't begin with digit, so this is a malformed binary
return -1;
} else if (c == '.') {
// no such thing as binary floating point
c = fValue[++i];
while (Character.isDigit(c) && i < fValue.length) {
c = fValue[i++];
}
}
} else {
// Here we have 0b or 0B
return i - 1;
}
return i;
}
private int probablyHex(int i) {
/* hexadecimal-literal
* 0x hexadecimal-digit
* 0X hexadecimal-digit
* hexadecimal-literal hexadecimal-digit
*/
char c = fValue[++i];
if (isHexDigit(c)) {
while (isHexDigit(c) && i < fValue.length) {
c = fValue[++i];
}
if (c == '.') {
// Could be GCC's hex float
return hexFloatAfterDecimal(i);
} else if ((c | 0x20) == 'p') {
return hexFloatExponent(i);
}
} else {
return i - 1;
}
return i;
}
// Assumes fValue[i] == '.'
private int hexFloatAfterDecimal(int i) {
// 0xHHH.
char c = fValue[++i];
if (isHexDigit(c)) {
while (isHexDigit(c) && i < fValue.length) {
c = fValue[++i];
}
if ((c | 0x20) == 'p') {
return hexFloatExponent(i);
} else {
// The parser is very confused at this point
// as the expression is 0x1.f
return -1;
}
}
// Probably shouldn't be able to get here
// we have 0xHHH.
return -1;
}
// Assumes image[i] == 'p'
private int hexFloatExponent(int i) {
// 0xHH.HH[pP][-+]?DDDD
char c = fValue[++i];
if (c == '-' || c == '+') {
c = fValue[++i];
}
if (Character.isDigit(c)) {
while (Character.isDigit(c) && i < fValue.length) {
c = fValue[++i];
}
} else {
return i - 1;
}
return i;
}
private boolean isHexDigit(char c) {
c |= 0x20;
return ((c <= 'f' && c >= 'a') || (c <= '9' && c >= '0'));
}
private boolean isOctal(final char c) {
return c >= '0' && c <= '7';
}
/**
* @deprecated, use {@link #setValue(char[])}, instead.
*/
@Override
@Deprecated
public void setValue(String value) {
assertNotFrozen();
this.fValue = value.toCharArray();
}
/**
* @deprecated use {@link #CPPASTLiteralExpression(int, char[])}, instead.
*/
@Deprecated
public CPPASTLiteralExpression(int kind, String value) {
this(kind, value.toCharArray());
}
@Override
public ICPPEvaluation getEvaluation() {
if (fEvaluation == null)
fEvaluation= createEvaluation();
return fEvaluation;
}
private ICPPEvaluation createLiteralEvaluation() {
switch (fKind) {
case lk_this: {
IScope scope = CPPVisitor.getContainingScope(this);
IType type= CPPVisitor.getImpliedObjectType(scope);
if (type == null)
return EvalFixed.INCOMPLETE;
return new EvalFixed(new CPPPointerType(type), PRVALUE, IntegralValue.THIS);
}
case lk_true:
return EVAL_TRUE;
case lk_false:
return EVAL_FALSE;
case lk_char_constant:
return new EvalFixed(getCharType(), PRVALUE, createCharValue());
case lk_float_constant:
return new EvalFixed(classifyTypeOfFloatLiteral(), PRVALUE, FloatingPointValue.create(getValue()));
case lk_integer_constant:
return new EvalFixed(classifyTypeOfIntLiteral(), PRVALUE, createIntValue());
case lk_string_literal:
return new EvalFixed(getStringType(), LVALUE, CStringValue.create(getValue()));
case lk_nullptr:
return EVAL_NULL_PTR;
}
return EvalFixed.INCOMPLETE;
}
private ICPPEvaluation createEvaluation() {
ICPPEvaluation literalEval = createLiteralEvaluation();
IBinding udlOperator = getUserDefinedLiteralOperator();
if (udlOperator != null && literalEval != EvalFixed.INCOMPLETE) {
if (udlOperator instanceof ICPPFunction) {
ICPPFunction udlOpFunction = (ICPPFunction) udlOperator;
EvalBinding op = new EvalBinding(udlOpFunction, udlOpFunction.getType(), this);
ICPPEvaluation[] args = null;
ICPPParameter params[] = udlOpFunction.getParameters();
int paramCount = params.length;
if (paramCount == 0) {
// TODO: Support literal operator templates.
args = new ICPPEvaluation[]{op};
} else if (paramCount == 1) {
//this means that we need to fall back to the raw literal operator
if (params[0].getType() instanceof IPointerType) {
char numValue[] = getValue();
int numLen = numValue.length;
char strValue[] = new char[numLen + 2];
strValue[0] = '"';
strValue[numLen + 1] = '"';
System.arraycopy(numValue, 0, strValue, 1, numLen);
IType type = new CPPBasicType(Kind.eChar, 0, this);
type = new CPPQualifierType(type, true, false);
type = new CPPArrayType(type, IntegralValue.create(numLen+1));
EvalFixed strEval = new EvalFixed(type, LVALUE, CStringValue.create(strValue));
args = new ICPPEvaluation[]{op, strEval};
} else {
args = new ICPPEvaluation[]{op, literalEval};
}
} else if (paramCount == 2) {
IValue sizeValue = IntegralValue.create(computeStringLiteralSize() - 1);
EvalFixed literalSizeEval = new EvalFixed(CPPBasicType.INT, PRVALUE, sizeValue);
args = new ICPPEvaluation[]{op, literalEval, literalSizeEval};
}
return new EvalFunctionCall(args, null, this);
}
}
//has a user-defined literal suffix but didn't find a udl operator function => error
if (getSuffix().length > 0 && (getKind() == lk_string_literal || !fIsCompilerSuffix)) {
return EvalFixed.INCOMPLETE;
}
return literalEval;
}
private IValue createCharValue() {
try {
final char[] image= getValue();
if (image.length > 1 && image[0] == 'L')
return IntegralValue.create(ExpressionEvaluator.getChar(image, 2));
return IntegralValue.create(ExpressionEvaluator.getChar(image, 1));
} catch (EvalException e) {
return IntegralValue.UNKNOWN;
}
}
private IValue createIntValue() {
try {
return IntegralValue.create(ExpressionEvaluator.getNumber(getValue()));
} catch (EvalException e) {
return IntegralValue.UNKNOWN;
}
}
@Override
public IType getExpressionType() {
return getEvaluation().getType(this);
}
@Override
public boolean isLValue() {
return getValueCategory() == LVALUE;
}
@Override
public ValueCategory getValueCategory() {
return getKind() == lk_string_literal ? LVALUE : PRVALUE;
}
@Override
public IASTImplicitName[] getImplicitNames() {
if (fImplicitNames == null) {
if (fIsCompilerSuffix) {
fImplicitNames = IASTImplicitName.EMPTY_NAME_ARRAY;
} else {
CPPASTImplicitName operatorName = new CPPASTImplicitName(fSuffix, this);
operatorName.setOperator(true);
operatorName.setBinding(getUserDefinedLiteralOperator());
operatorName.setOffsetAndLength(getOffset() + fValue.length, fSuffix.length);
fImplicitNames = new IASTImplicitName[] { operatorName };
}
}
return fImplicitNames;
}
}