blob: 0c9c1fd70ff18d1f1fd7b6fc2e42967105957908 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2004, 2008 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:
* UIUC - Photran modifications
* IBM Corporation - initial implementation
* Markus Schorn (Wind River Systems)
* Bryan Wilkinson (QNX) - https://bugs.eclipse.org/bugs/show_bug.cgi?id=151207
* Anton Leherbauer (Wind River Systems)
*******************************************************************************/
package org.eclipse.photran.internal.core.preprocessor.c;
import java.util.ArrayList;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.parser.IProblem;
import org.eclipse.cdt.core.parser.util.CharArrayMap;
/**
* Used to evaluate expressions in preprocessor directives.
*/
class ExpressionEvaluator {
@SuppressWarnings("serial")
static class EvalException extends Exception {
private int fProblemID;
private char[] fProblemArg;
public EvalException(int problemID, char[] problemArg) {
fProblemID= problemID;
fProblemArg= problemArg;
}
public int getProblemID() {
return fProblemID;
}
public char[] getProblemArg() {
return fProblemArg;
}
}
private Token fTokens;
private CharArrayMap<PreprocessorMacro> fDictionary;
private ArrayList<IASTName> fMacrosInDefinedExpressions= new ArrayList<IASTName>();
private LocationMap fLocationMap;
public boolean evaluate(TokenList condition, CharArrayMap<PreprocessorMacro> macroDictionary, LocationMap map) throws EvalException {
fTokens= condition.first();
fDictionary= macroDictionary;
fLocationMap= map;
fMacrosInDefinedExpressions.clear();
return expression() != 0;
}
public IASTName[] clearMacrosInDefinedExpression() {
IASTName[] result= fMacrosInDefinedExpressions.toArray(new IASTName[fMacrosInDefinedExpressions.size()]);
fMacrosInDefinedExpressions.clear();
return result;
}
private long expression() throws EvalException {
return conditionalExpression();
}
private long conditionalExpression() throws EvalException {
long r1 = logicalOrExpression();
if (LA() == IToken.tQUESTION) {
consume();
long r2 = expression();
if (LA() == IToken.tCOLON)
consume();
else {
throw new EvalException(IProblem.SCANNER_BAD_CONDITIONAL_EXPRESSION, null);
}
long r3 = conditionalExpression();
return r1 != 0 ? r2 : r3;
}
return r1;
}
private long logicalOrExpression() throws EvalException {
long r1 = logicalAndExpression();
while (LA() == IToken.tOR) {
consume();
long r2 = logicalAndExpression();
r1 = ((r1 != 0) || (r2 != 0)) ? 1 : 0;
}
return r1;
}
private long logicalAndExpression() throws EvalException {
long r1 = inclusiveOrExpression();
while (LA() == IToken.tAND) {
consume();
long r2 = inclusiveOrExpression();
r1 = ((r1 != 0) && (r2 != 0)) ? 1 : 0;
}
return r1;
}
private long inclusiveOrExpression() throws EvalException {
long r1 = exclusiveOrExpression();
while (LA() == IToken.tBITOR) {
consume();
long r2 = exclusiveOrExpression();
r1 = r1 | r2;
}
return r1;
}
private long exclusiveOrExpression() throws EvalException {
long r1 = andExpression();
while (LA() == IToken.tXOR) {
consume();
long r2 = andExpression();
r1 = r1 ^ r2;
}
return r1;
}
private long andExpression() throws EvalException {
long r1 = equalityExpression();
while (LA() == IToken.tAMPER) {
consume();
long r2 = equalityExpression();
r1 = r1 & r2;
}
return r1;
}
private long equalityExpression() throws EvalException {
long r1 = relationalExpression();
for (int t = LA(); t == IToken.tEQUAL || t == IToken.tNOTEQUAL; t = LA()) {
consume();
long r2 = relationalExpression();
if (t == IToken.tEQUAL)
r1 = (r1 == r2) ? 1 : 0;
else
// t == tNOTEQUAL
r1 = (r1 != r2) ? 1 : 0;
}
return r1;
}
private long relationalExpression() throws EvalException {
long r1 = shiftExpression();
for (int t = LA(); t == IToken.tLT || t == IToken.tLTEQUAL || t == IToken.tGT
|| t == IToken.tGTEQUAL || t == IToken.tASSIGN; t = LA()) {
consume();
long r2 = shiftExpression();
switch (t) {
case IToken.tLT:
r1 = (r1 < r2) ? 1 : 0;
break;
case IToken.tLTEQUAL:
r1 = (r1 <= r2) ? 1 : 0;
break;
case IToken.tGT:
r1 = (r1 > r2) ? 1 : 0;
break;
case IToken.tGTEQUAL:
r1 = (r1 >= r2) ? 1 : 0;
break;
case IToken.tASSIGN:
throw new EvalException(IProblem.SCANNER_ASSIGNMENT_NOT_ALLOWED, null);
}
}
return r1;
}
private long shiftExpression() throws EvalException {
long r1 = additiveExpression();
for (int t = LA(); t == IToken.tSHIFTL || t == IToken.tSHIFTR; t = LA()) {
consume();
long r2 = additiveExpression();
if (t == IToken.tSHIFTL)
r1 = r1 << r2;
else
// t == tSHIFTR
r1 = r1 >> r2;
}
return r1;
}
private long additiveExpression() throws EvalException {
long r1 = multiplicativeExpression();
for (int t = LA(); t == IToken.tPLUS || t == IToken.tMINUS; t = LA()) {
consume();
long r2 = multiplicativeExpression();
if (t == IToken.tPLUS)
r1 = r1 + r2;
else
// t == tMINUS
r1 = r1 - r2;
}
return r1;
}
private long multiplicativeExpression() throws EvalException {
long r1 = unaryExpression();
for (int t = LA(); t == IToken.tSTAR || t == IToken.tDIV || t == IToken.tMOD; t = LA()) {
consume();
long r2 = unaryExpression();
if (t == IToken.tSTAR)
r1 = r1 * r2;
else if (r2 != 0) {
if (t == IToken.tDIV)
r1 = r1 / r2;
else
r1 = r1 % r2; //tMOD
} else {
throw new EvalException(IProblem.SCANNER_DIVIDE_BY_ZERO, null);
}
}
return r1;
}
private long unaryExpression() throws EvalException {
switch (LA()) {
case IToken.tPLUS:
consume();
return unaryExpression();
case IToken.tMINUS:
consume();
return -unaryExpression();
case IToken.tNOT:
consume();
return unaryExpression() == 0 ? 1 : 0;
case IToken.tBITCOMPLEMENT:
consume();
return ~unaryExpression();
case IToken.tCHAR:
case IToken.tLCHAR:
case IToken.tINTEGER:
long val= getValue(fTokens);
consume();
return val;
case IToken.t_true:
consume();
return 1;
case IToken.t_false:
consume();
return 0;
case CPreprocessor.tDEFINED:
return handleDefined();
case IToken.tLPAREN:
consume();
long r1 = expression();
if (LA() == IToken.tRPAREN) {
consume();
return r1;
}
throw new EvalException(IProblem.SCANNER_MISSING_R_PAREN, null);
case IToken.tIDENTIFIER:
consume();
return 0;
// 16.1.4 alternate keywords are not replaced by a 0
case IToken.tAND:
case IToken.tOR:
case IToken.tBITOR:
case IToken.tBITORASSIGN:
case IToken.tXOR:
case IToken.tXORASSIGN:
case IToken.tAMPER:
case IToken.tAMPERASSIGN:
case IToken.tSTRING:
case IToken.tLSTRING:
throw new EvalException(IProblem.SCANNER_EXPRESSION_SYNTAX_ERROR, null);
default:
// 16.1.4 keywords are replaced by 0
final char[] image= fTokens.getCharImage();
if (image.length > 0) {
final char c= image[0];
if ((c>='a' && c<='z') || (c>='A' && c<='Z') || c == '_' || c=='$' || c=='@')
return 0;
}
throw new EvalException(IProblem.SCANNER_EXPRESSION_SYNTAX_ERROR, null);
}
}
private long handleDefined() throws EvalException {
boolean parenthesis= false;
consume();
if (LA() == IToken.tLPAREN) {
parenthesis= true;
consume();
}
if (LA() != IToken.tIDENTIFIER) {
throw new EvalException(IProblem.SCANNER_ILLEGAL_IDENTIFIER, null);
}
PreprocessorMacro macro= fDictionary.get(fTokens.getCharImage());
if (macro != null) {
fMacrosInDefinedExpressions.add(fLocationMap.encounterDefinedExpression(macro, fTokens.getOffset(), fTokens.getEndOffset()));
}
int result= macro != null ? 1 : 0;
consume();
if (parenthesis) {
if (LA() != IToken.tRPAREN) {
throw new EvalException(IProblem.SCANNER_MISSING_R_PAREN, null);
}
consume();
}
return result;
}
private int LA() {
return fTokens.getType();
}
private void consume() {
fTokens= (Token) fTokens.getNext();
if (fTokens == null) {
fTokens= new Token(IToken.tEND_OF_INPUT, null, 0, 0);
}
}
long getValue(Token t) throws EvalException {
switch(t.getType()) {
case IToken.tCHAR:
return getChar(t.getCharImage(), 1);
case IToken.tLCHAR:
return getChar(t.getCharImage(), 2);
case IToken.tINTEGER:
return getNumber(t.getCharImage());
}
assert false;
return 1;
}
private long getNumber(char[] image) throws EvalException {
boolean isHex = false;
boolean isOctal = false;
int pos= 0;
if (image.length > 1) {
if (image[0] == '0') {
switch (image[++pos]) {
case 'x':
case 'X':
isHex = true;
++pos;
break;
case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
isOctal = true;
++pos;
break;
}
}
}
if (isHex) {
return getNumber(image, 2, image.length, 16, IProblem.SCANNER_BAD_HEX_FORMAT);
}
if (isOctal) {
return getNumber(image, 1, image.length, 8, IProblem.SCANNER_BAD_OCTAL_FORMAT);
}
return getNumber(image, 0, image.length, 10, IProblem.SCANNER_BAD_DECIMAL_FORMAT);
}
private long getChar(char[] tokenImage, int i) throws EvalException {
if (i>=tokenImage.length) {
throw new EvalException(IProblem.SCANNER_BAD_CHARACTER, tokenImage);
}
final char c= tokenImage[i];
if (c != '\\') {
return c;
}
if (++i>=tokenImage.length) {
throw new EvalException(IProblem.SCANNER_BAD_CHARACTER, tokenImage);
}
final char d= tokenImage[i];
switch(d) {
case '\\': case '"': case '\'':
return d;
case 'a': return 7;
case 'b': return '\b';
case 'f': return '\f';
case 'n': return '\n';
case 'r': return '\r';
case 't': return '\t';
case 'v': return 0xb;
case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7':
return getNumber(tokenImage, i+1, tokenImage.length-1, 8, IProblem.SCANNER_BAD_OCTAL_FORMAT);
case 'x': case 'u': case 'U':
return getNumber(tokenImage, i+1, tokenImage.length-1, 16, IProblem.SCANNER_BAD_HEX_FORMAT);
default:
throw new EvalException(IProblem.SCANNER_BAD_CHARACTER, tokenImage);
}
}
private long getNumber(char[] tokenImage, int from, int to, int base, int problemID) throws EvalException {
if (from == to) {
throw new EvalException(problemID, tokenImage);
}
long result= 0;
int i= from;
for (; i < to; i++) {
int digit= getDigit(tokenImage[i]);
if (digit >= base) {
break;
}
result= result*base + digit;
}
for (; i < to; i++) {
switch(tokenImage[i]) {
case 'u' : case 'l': case 'U': case 'L':
break;
default:
throw new EvalException(problemID, tokenImage);
}
}
return result;
}
private int getDigit(char c) {
switch(c) {
case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
return c-'0';
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
return c-'a' + 10;
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
return c-'A'+10;
}
return Integer.MAX_VALUE;
}
}