blob: 61df620b94f4eb9404ac35eb6b68f87dc6184fb9 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006, 2015 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.internal.core.dom.lrparser.c99.action.deprecated;
import static org.eclipse.cdt.core.parser.util.CollectionUtils.reverseIterable;
import static org.eclipse.cdt.internal.core.dom.lrparser.symboltable.CNamespace.GOTO_LABEL;
import static org.eclipse.cdt.internal.core.dom.lrparser.symboltable.CNamespace.IDENTIFIER;
import static org.eclipse.cdt.internal.core.dom.lrparser.symboltable.CNamespace.STRUCT_TAG;
import java.util.LinkedList;
import java.util.List;
import org.eclipse.cdt.core.dom.ast.EScopeKind;
import org.eclipse.cdt.core.dom.ast.IASTBinaryExpression;
import org.eclipse.cdt.core.dom.ast.IASTElaboratedTypeSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTLiteralExpression;
import org.eclipse.cdt.core.dom.ast.IArrayType;
import org.eclipse.cdt.core.dom.ast.IBasicType;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.ICompositeType;
import org.eclipse.cdt.core.dom.ast.IField;
import org.eclipse.cdt.core.dom.ast.IFunctionType;
import org.eclipse.cdt.core.dom.ast.IParameter;
import org.eclipse.cdt.core.dom.ast.IPointerType;
import org.eclipse.cdt.core.dom.ast.IType;
import org.eclipse.cdt.core.dom.ast.ITypedef;
import org.eclipse.cdt.core.dom.lrparser.action.ITokenStream;
import org.eclipse.cdt.core.dom.lrparser.action.ScopedStack;
import org.eclipse.cdt.core.parser.util.DebugUtil;
import org.eclipse.cdt.internal.core.dom.lrparser.c99.C99Parsersym;
import org.eclipse.cdt.internal.core.dom.lrparser.c99.bindings.C99ArrayType;
import org.eclipse.cdt.internal.core.dom.lrparser.c99.bindings.C99BasicType;
import org.eclipse.cdt.internal.core.dom.lrparser.c99.bindings.C99Enumeration;
import org.eclipse.cdt.internal.core.dom.lrparser.c99.bindings.C99Enumerator;
import org.eclipse.cdt.internal.core.dom.lrparser.c99.bindings.C99Field;
import org.eclipse.cdt.internal.core.dom.lrparser.c99.bindings.C99Function;
import org.eclipse.cdt.internal.core.dom.lrparser.c99.bindings.C99FunctionScope;
import org.eclipse.cdt.internal.core.dom.lrparser.c99.bindings.C99FunctionType;
import org.eclipse.cdt.internal.core.dom.lrparser.c99.bindings.C99Label;
import org.eclipse.cdt.internal.core.dom.lrparser.c99.bindings.C99Parameter;
import org.eclipse.cdt.internal.core.dom.lrparser.c99.bindings.C99PointerType;
import org.eclipse.cdt.internal.core.dom.lrparser.c99.bindings.C99ProblemBinding;
import org.eclipse.cdt.internal.core.dom.lrparser.c99.bindings.C99Scope;
import org.eclipse.cdt.internal.core.dom.lrparser.c99.bindings.C99Structure;
import org.eclipse.cdt.internal.core.dom.lrparser.c99.bindings.C99Typedef;
import org.eclipse.cdt.internal.core.dom.lrparser.c99.bindings.C99Variable;
import org.eclipse.cdt.internal.core.dom.lrparser.c99.bindings.IC99Binding;
import org.eclipse.cdt.internal.core.dom.lrparser.c99.bindings.IC99Scope;
import org.eclipse.cdt.internal.core.dom.lrparser.c99.bindings.ITypeable;
import org.eclipse.cdt.internal.core.dom.lrparser.symboltable.C99SymbolTable;
import org.eclipse.cdt.internal.core.dom.parser.ITypeContainer;
import lpg.lpgjavaruntime.IToken;
/**
* This class was an attempt at doing full binding resolution during the parse
* as opposed to doing it after the parse as is normally done with the DOM parser.
*
*
* TODO: token mapping so that this will work with UPC
* TODO: what about function definitions, don't they count as declarations?
*
* Try to resolve bindings without using the ASTStack, that way I can resolve bindings
* without generating an AST. In the future I can remove this as a subclass of C99ParserAction.
*
* TODO: if I'm calculating scopes then those scopes need to be linked to AST nodes
*
* TODO: some language constructs are not handled yet: typeIds (casts), field designators
*
* @author Mike Kucera
*
* @deprecated Binding resolution is too hard, replacing with simpler C99TypedefTrackerParserAction
*/
@SuppressWarnings("restriction")
@Deprecated
public class C99ResolveParserAction {
private static final boolean DEBUG = true;
private static final String NO_IDENT = ""; //$NON-NLS-1$
// provides limited access to the token stream
private final ITokenStream parser;
// The symbolTable currently in use
private C99SymbolTable symbolTable = C99SymbolTable.EMPTY_TABLE;
// A stack that keeps track of scopes in the symbol table, used to "close" scopes and to undo the opening of scopes
private final LinkedList<C99SymbolTable> symbolTableScopeStack = new LinkedList<>();
// A stack that keeps track of scopes that are set on bindings
private final LinkedList<IC99Scope> bindingScopeStack = new LinkedList<>();
// keeps track of nested declarations
private final LinkedList<DeclaratorFrame> declarationStack = new LinkedList<>();
// keeps track of expression types
private final ScopedStack<IType> exprTypeStack = new ScopedStack<>();
private TypeQualifiers typeQualifiers; // TODO: can this go in the declaration stack?
private static class TypeQualifiers {
boolean isConst, isRestrict, isVolatile;
}
// "For every action there is an equal and opposite reaction." - Newton's third law
private final LinkedList<IUndoAction> undoStack = new LinkedList<>();
private interface IUndoAction {
void undo();
}
public void undo() {
undoStack.removeLast().undo();
}
public void undo(int steps) {
for (int i = 0; i < steps; i++) {
undo();
}
}
public IC99Scope getCurrentScope() {
return bindingScopeStack.getLast();
}
public C99ResolveParserAction(ITokenStream parser) {
this.parser = parser;
bindingScopeStack.add(new C99Scope(EScopeKind.eGlobal)); // the global scope
System.out.println();
}
private static IType rawType(IType type) {
while (type instanceof ITypedef) {
type = ((C99Typedef) type).getType();
}
return type;
}
/**
* Lexer feedback hack, used by the parser to identify typedefname tokens.
*/
public boolean isTypedef(String ident) {
boolean result = symbolTable.lookup(IDENTIFIER, ident) instanceof ITypedef;
return result;
}
/**
* Methods used by tests, package local access.
*/
C99SymbolTable getSymbolTable() {
return symbolTable;
}
int undoStackSize() {
return undoStack.size();
}
LinkedList<DeclaratorFrame> getDeclarationStack() {
return declarationStack;
}
/**
* Called from the grammar file in places where a scope is created.
*
* Scopes are created by compound statements, however special care
* must also be taken with for loops because they may contain
* declarations.
*
* TODO: scope object now need to be handled explicitly
*/
public void openSymbolScope() {
if (DEBUG)
DebugUtil.printMethodTrace();
symbolTableScopeStack.add(symbolTable);
bindingScopeStack.add(new C99Scope(EScopeKind.eLocal));
undoStack.add(new IUndoAction() {
@Override
public void undo() {
bindingScopeStack.removeLast();
symbolTable = symbolTableScopeStack.removeLast();
}
});
}
public IC99Scope closeSymbolScope() {
if (DEBUG)
DebugUtil.printMethodTrace();
final C99SymbolTable undoTable = symbolTable;
symbolTable = symbolTableScopeStack.removeLast(); // close the scope
final IC99Scope undoScope = bindingScopeStack.removeLast();
if (!bindingScopeStack.isEmpty())
undoScope.setParent(bindingScopeStack.getLast());
undoStack.add(new IUndoAction() {
@Override
public void undo() {
if (DEBUG)
DebugUtil.printMethodTrace();
undoScope.setParent(null);
bindingScopeStack.add(undoScope);
symbolTableScopeStack.add(symbolTable);
symbolTable = undoTable;
}
});
return undoScope;
}
// TODO, this needs an undo action
public void openPointerScope() {
if (DEBUG)
DebugUtil.printMethodTrace();
final DeclaratorFrame frame = declarationStack.getLast();
frame.openPointerModifierScope();
undoStack.add(new IUndoAction() {
@Override
public void undo() {
if (DEBUG)
DebugUtil.printMethodTrace();
frame.closePointerModifierScope();
}
});
}
/**
* Called from the grammar before a declaration is about to be reduced.
*/
public void openDeclarationScope() {
if (DEBUG)
DebugUtil.printMethodTrace();
declarationStack.add(new DeclaratorFrame());
undoStack.add(new IUndoAction() {
@Override
public void undo() {
if (DEBUG)
DebugUtil.printMethodTrace();
declarationStack.removeLast();
}
});
}
public void closeDeclarationScope() {
if (DEBUG)
DebugUtil.printMethodTrace();
final DeclaratorFrame undoFrame = declarationStack.removeLast();
undoStack.add(new IUndoAction() {
@Override
public void undo() {
if (DEBUG)
DebugUtil.printMethodTrace();
declarationStack.add(undoFrame);
}
});
}
public void consumeFunctionDefinition() {
if (DEBUG)
DebugUtil.printMethodTrace();
// final IC99Scope undoScope = bindingScope;
//
// C99FunctionScope functionScope = new C99FunctionScope();
// functionScope.setBodyScope(undoScope);
// undoScope.setParent(functionScope);
// bindingScope = bindingScopeStack.removeLast();
// functionScope.setParent(bindingScope);
final IC99Scope undoScope = bindingScopeStack.removeLast();
C99FunctionScope functionScope = new C99FunctionScope();
functionScope.setBodyScope(undoScope);
undoScope.setParent(functionScope);
functionScope.setParent(bindingScopeStack.getLast());
final DeclaratorFrame frame = declarationStack.removeLast();
// the function binding needs to be available outside of the function's scope
String functionName = frame.getDeclaratorName().toString();
C99Function functionBinding = (C99Function) symbolTable.lookup(IDENTIFIER, functionName);
functionBinding.setFunctionScope(functionScope);
final C99SymbolTable undoTable = symbolTable;
final C99SymbolTable outerTable = symbolTableScopeStack.removeLast();
symbolTable = outerTable.insert(IDENTIFIER, functionName, functionBinding);
undoStack.add(new IUndoAction() {
@Override
public void undo() {
if (DEBUG)
DebugUtil.printMethodTrace();
symbolTableScopeStack.add(outerTable);
symbolTable = undoTable;
declarationStack.add(frame);
bindingScopeStack.add(undoScope);
}
});
}
public void consumeAbstractDeclaratorFunctionDeclarator() {
if (DEBUG)
DebugUtil.printMethodTrace();
final DeclaratorFrame frame = declarationStack.getLast();
frame.setFunctionDeclarator(true);
undoStack.add(new IUndoAction() {
@Override
public void undo() {
if (DEBUG)
DebugUtil.printMethodTrace();
frame.setFunctionDeclarator(false);
}
});
}
public void consumeDirectDeclaratorFunctionDeclarator() {
if (DEBUG)
DebugUtil.printMethodTrace();
final DeclaratorFrame frame = declarationStack.getLast();
frame.setFunctionDeclarator(true);
undoStack.add(new IUndoAction() {
@Override
public void undo() {
if (DEBUG)
DebugUtil.printMethodTrace();
frame.setFunctionDeclarator(false);
}
});
}
public void consumeDeclSpecToken() {
if (DEBUG)
DebugUtil.printMethodTrace();
IToken token = parser.getRightIToken();
final int kind = token.getKind();
// creates a DeclSpec if there isn't one already
DeclaratorFrame frame = declarationStack.getLast();
final DeclSpec declSpec = frame.getDeclSpec();
declSpec.add(kind);
undoStack.add(new IUndoAction() {
@Override
public void undo() {
if (DEBUG)
DebugUtil.printMethodTrace();
declSpec.remove(kind);
}
});
}
/**
* A labeled statement is creates an implicit declaration of the label identifier.
*
* TODO: a label has function scope, meaning the same label cannot be declared twice
* in a function regardless of block scope.
* TODO: labels can be implicitly declared, that is a goto can exist above the label
*/
public IBinding consumeStatementLabeled() {
if (DEBUG)
DebugUtil.printMethodTrace();
IToken token = parser.getLeftIToken();
String ident = token.toString();
IC99Binding labelBinding = new C99Label(ident);
// TODO: strictly speaking the same label cannot be declared twice,
// but we aren't checking that here
final C99SymbolTable oldTable = symbolTable;
symbolTable = symbolTable.insert(GOTO_LABEL, ident, labelBinding);
undoStack.add(new IUndoAction() {
@Override
public void undo() {
if (DEBUG)
DebugUtil.printMethodTrace();
symbolTable = oldTable;
}
});
return labelBinding;
}
public IBinding consumeStatementGoto() {
if (DEBUG)
DebugUtil.printMethodTrace();
List<IToken> ruleTokens = parser.getRuleTokens();
assert ruleTokens.size() == 3 : "a goto statement must always consist of 3 tokens"; //$NON-NLS-1$
String ident = ruleTokens.get(1).toString();
final C99SymbolTable oldTable = symbolTable;
IC99Binding labelBinding = symbolTable.lookup(GOTO_LABEL, ident);
if (labelBinding == null) {
labelBinding = new C99Label(ident);
symbolTable = symbolTable.insert(GOTO_LABEL, ident, labelBinding);
}
undoStack.add(new IUndoAction() {
@Override
public void undo() {
if (DEBUG)
DebugUtil.printMethodTrace();
symbolTable = oldTable;
}
});
return labelBinding;
}
public IBinding consumeDeclarationSpecifiersTypedefName() {
if (DEBUG)
DebugUtil.printMethodTrace();
// find the typedef token
String typedefName = null;
for (IToken token : parser.getRuleTokens()) {
// the token kind will still be TK_identifier, but there can only be one
if (token.getKind() == C99Parsersym.TK_identifier) {
typedefName = token.toString();
break;
}
}
assert typedefName != null : "a typedef token must have been parsed for this action to fire"; //$NON-NLS-1$
// we know that the binding is a typedef because the lexer feedback hack worked and got us here
ITypedef binding = (ITypedef) symbolTable.lookup(IDENTIFIER, typedefName);
// TODO: do I need to clone the typedef in case it is further modified like with const?
DeclaratorFrame frame = declarationStack.getLast();
final DeclSpec declSpec = frame.getDeclSpec();
declSpec.setType(binding);
undoStack.add(new IUndoAction() {
@Override
public void undo() {
if (DEBUG)
DebugUtil.printMethodTrace();
declSpec.setType(null);
}
});
return binding;
}
public void consumeDirectDeclaratorIdentifier() {
if (DEBUG)
DebugUtil.printMethodTrace();
final DeclaratorFrame frame = declarationStack.getLast();
DebugUtil.printMethodTrace();
frame.setDeclaratorName(parser.getRightIToken());
undoStack.add(new IUndoAction() {
@Override
public void undo() {
if (DEBUG)
DebugUtil.printMethodTrace();
DebugUtil.printMethodTrace();
frame.setDeclaratorName(null);
}
});
}
// TODO need to be careful, this is called in a lot of places in the grammar
public void consumeDeclaratorWithPointer() {
if (DEBUG)
DebugUtil.printMethodTrace();
final DeclaratorFrame frame = declarationStack.getLast();
final LinkedList<C99PointerType> scope = frame.closePointerModifierScope();
final int scopeSize = scope.size();
for (C99PointerType pt : reverseIterable(scope)) {
frame.addTypeModifier(pt);
}
undoStack.add(new IUndoAction() {
@Override
public void undo() {
if (DEBUG)
DebugUtil.printMethodTrace();
for (int i = 0; i < scopeSize; i++) {
frame.removeLastTypeModifier();
}
frame.openPointerModifierScope(scope);
}
});
}
public void consumeDirectDeclaratorBracketed() {
if (DEBUG)
DebugUtil.printMethodTrace();
// Used to tell the difference between function prototype declarations and function pointer declarations
final DeclaratorFrame frame = declarationStack.getLast();
frame.setDeclaratorBracketed(true);
undoStack.add(new IUndoAction() {
@Override
public void undo() {
if (DEBUG)
DebugUtil.printMethodTrace();
frame.setDeclaratorBracketed(false);
}
});
}
public IBinding consumeDeclaratorComplete() {
if (DEBUG)
DebugUtil.printMethodTrace();
final DeclaratorFrame frame = declarationStack.getLast();
IToken token = frame.getDeclaratorName();
IType type = frame.getDeclaratorType();
DeclSpec declSpec = frame.getDeclSpec();
boolean isFunction = frame.isFunctionDeclarator();
List<IBinding> nestedDeclarators = frame.getNestedDeclarations();
if (isFunction)
type = createFunctionType(type, nestedDeclarators);
String ident = (token == null) ? null : token.toString();
// compute the binding
IC99Binding binding;
if (declSpec.isTypedef())
binding = createTypedefBinding(ident, type);
else if (isFunction && !frame.isDeclaratorBracketed())
binding = createFunctionBinding(ident, (IFunctionType) type, declSpec, nestedDeclarators);
else
binding = createVariableBinding(ident, type, declSpec);
binding.setScope(bindingScopeStack.getLast());
// insert into symbol table
final C99SymbolTable oldTable = symbolTable;
if (ident != null)
symbolTable = symbolTable.insert(IDENTIFIER, ident, binding);
declarationStack.removeLast();
declarationStack.add(new DeclaratorFrame(frame.getDeclSpec())); // reset the declarator
exprTypeStack.push(type);
undoStack.add(new IUndoAction() {
@Override
public void undo() {
if (DEBUG)
DebugUtil.printMethodTrace();
exprTypeStack.pop();
declarationStack.removeLast();
declarationStack.add(frame);
symbolTable = oldTable;
}
});
return binding;
}
/**
* Just gets rid of the type that was on the type stack.
*/
public void consumeInitDeclarator() {
if (DEBUG)
DebugUtil.printMethodTrace();
final IType type = exprTypeStack.pop();
undoStack.add(new IUndoAction() {
@Override
public void undo() {
if (DEBUG)
DebugUtil.printMethodTrace();
exprTypeStack.push(type);
}
});
}
public IBinding consumeFunctionDefinitionHeader() {
if (DEBUG)
DebugUtil.printMethodTrace();
DeclaratorFrame frame = declarationStack.getLast();
DeclSpec declSpec = frame.getDeclSpec();
String functionName = frame.getDeclaratorName().toString();
final C99SymbolTable oldTable = symbolTable;
// there may have been a function prototype, hence there may already be a binding for the function
IC99Binding binding = symbolTable.lookup(IDENTIFIER, functionName);
if (binding == null) {
IType type = frame.getDeclaratorType();
List<IBinding> nestedDeclarators = frame.getNestedDeclarations();
IFunctionType functionType = createFunctionType(type, nestedDeclarators);
binding = createFunctionBinding(functionName, functionType, declSpec, nestedDeclarators);
// a scope has already been opened for the function body, use the outer scope
IC99Scope topScope = bindingScopeStack.removeLast();
binding.setScope(bindingScopeStack.getLast());
bindingScopeStack.add(topScope);
symbolTable = symbolTable.insert(IDENTIFIER, functionName, binding);
}
undoStack.add(new IUndoAction() {
@Override
public void undo() {
if (DEBUG)
DebugUtil.printMethodTrace();
symbolTable = oldTable;
}
});
return binding;
}
public IBinding consumeDeclaratorCompleteParameter() {
if (DEBUG)
DebugUtil.printMethodTrace();
final DeclaratorFrame frame = declarationStack.removeLast();
IToken token = frame.getDeclaratorName();
IType type = frame.getDeclaratorType();
DeclSpec declSpec = frame.getDeclSpec();
boolean isFunction = frame.isFunctionDeclarator();
// its a function pointer
if (isFunction)
type = createFunctionType(type, frame.getNestedDeclarations());
String ident = (token == null) ? NO_IDENT : token.toString();
IC99Binding parameterBinding = createParameterBinding(ident, type, declSpec);
parameterBinding.setScope(bindingScopeStack.getLast());
// check to see if there is already a parameter binding
String functionName = declarationStack.getLast().getDeclaratorName().toString();
C99Function function = (C99Function) symbolTable.lookup(IDENTIFIER, functionName);
if (function != null) {
// there is already a function binding for this function, that means there
// is a function prototype and there is already a binding for this parameter
int position = declarationStack.getLast().getNestedDeclarations().size();
IParameter[] parameters = function.getParameters();
if (parameters != null && position < parameters.length) {
parameterBinding = (IC99Binding) parameters[position];
}
}
// even if the binding is reused it still might be under a different name
final C99SymbolTable oldTable = symbolTable;
if (ident != null) {
symbolTable = symbolTable.insert(IDENTIFIER, ident, parameterBinding);
}
declarationStack.getLast().addNestedDeclaration(parameterBinding);
// parameter declarations can only have one declarator, so don't reset
//declarationStack.add(new DeclaratorFrame()); // reset
undoStack.add(new IUndoAction() {
@Override
public void undo() {
if (DEBUG)
DebugUtil.printMethodTrace();
//declarationStack.removeLast();
declarationStack.getLast().removeLastNestedDeclaration();
declarationStack.add(frame);
symbolTable = oldTable;
}
});
return parameterBinding;
}
/**
* This is a special case for the rule:
* parameter_declaration ::= declaration_specifiers
*
* In this case there is no declarator at all
*
* TODO: creating bindings that have no identifier seems really dumb,
* why does it need to be done? Why not just have a null binding or
* for that matter don't even have a name node
*
*/
public IBinding consumeParameterDeclarationWithoutDeclarator() {
if (DEBUG)
DebugUtil.printMethodTrace();
final DeclaratorFrame frame = declarationStack.removeLast();
DeclSpec declSpec = frame.getDeclSpec();
C99Parameter param = createParameterBinding(null, declSpec.getType(), declSpec);
declarationStack.getLast().addNestedDeclaration(param);
undoStack.add(new IUndoAction() {
@Override
public void undo() {
if (DEBUG)
DebugUtil.printMethodTrace();
declarationStack.getLast().removeLastNestedDeclaration();
declarationStack.add(frame);
}
});
return param;
}
public IBinding consumeDeclaratorCompleteField() {
if (DEBUG)
DebugUtil.printMethodTrace();
final DeclaratorFrame frame = declarationStack.getLast();
IToken token = frame.getDeclaratorName();
IType type = frame.getDeclaratorType();
// its a function pointer
if (frame.isFunctionDeclarator())
type = createFunctionType(type, frame.getNestedDeclarations());
IBinding binding = createFieldBinding(token.toString(), type, frame.getDeclSpec());
declarationStack.removeLast();
declarationStack.getLast().addNestedDeclaration(binding);
declarationStack.add(new DeclaratorFrame(frame.getDeclSpec())); // reset the declarator
exprTypeStack.push(type);
undoStack.add(new IUndoAction() {
@Override
public void undo() {
if (DEBUG)
DebugUtil.printMethodTrace();
exprTypeStack.pop();
declarationStack.removeLast();
declarationStack.getLast().removeLastNestedDeclaration();
declarationStack.add(frame);
}
});
return binding;
}
/**
* An abstract declarator used as part of an expression, eg) a cast.
* Only need the type.
*
* TODO: this isn't enough, I need a binding for the abstract declarator
* what I really need is a consumeDeclaratorCompleteTypeId similar to above
*/
public void consumeTypeId() {
if (DEBUG)
DebugUtil.printMethodTrace();
final DeclaratorFrame frame = declarationStack.removeLast();
IType type = frame.getDeclaratorType();
if (frame.isFunctionDeclarator()) // its a function pointer
type = createFunctionType(type, frame.getNestedDeclarations());
exprTypeStack.push(type);
undoStack.add(new IUndoAction() {
@Override
public void undo() {
if (DEBUG)
DebugUtil.printMethodTrace();
exprTypeStack.pop();
declarationStack.add(frame);
}
});
}
public void consumeDirectDeclaratorArrayModifier() {
if (DEBUG)
DebugUtil.printMethodTrace();
final DeclaratorFrame frame = declarationStack.getLast();
frame.addTypeModifier(new C99ArrayType());
undoStack.add(new IUndoAction() {
@Override
public void undo() {
if (DEBUG)
DebugUtil.printMethodTrace();
frame.removeLastTypeModifier();
}
});
}
public void consumeAbstractDeclaratorArrayModifier() {
if (DEBUG)
DebugUtil.printMethodTrace();
final DeclaratorFrame frame = declarationStack.getLast();
frame.addTypeModifier(new C99ArrayType());
undoStack.add(new IUndoAction() {
@Override
public void undo() {
if (DEBUG)
DebugUtil.printMethodTrace();
frame.removeLastTypeModifier();
}
});
}
public void consumeDirectDeclaratorModifiedArrayModifier(boolean isStatic, boolean isVarSized,
boolean hasTypeQualifierList) {
if (DEBUG)
DebugUtil.printMethodTrace();
C99ArrayType arrayType = new C99ArrayType();
arrayType.setStatic(isStatic);
arrayType.setVariableLength(isVarSized);
if (hasTypeQualifierList) {
arrayType.setConst(typeQualifiers.isConst);
arrayType.setRestrict(typeQualifiers.isRestrict);
arrayType.setVolatile(typeQualifiers.isVolatile);
}
final DeclaratorFrame frame = declarationStack.getLast();
frame.addTypeModifier(arrayType);
undoStack.add(new IUndoAction() {
@Override
public void undo() {
if (DEBUG)
DebugUtil.printMethodTrace();
frame.removeLastTypeModifier();
}
});
}
public void consumePointer() {
if (DEBUG)
DebugUtil.printMethodTrace();
final DeclaratorFrame frame = declarationStack.getLast();
frame.addPointerModifier(new C99PointerType());
undoStack.add(new IUndoAction() {
@Override
public void undo() {
if (DEBUG)
DebugUtil.printMethodTrace();
frame.removeLastPointerModifier();
}
});
}
public void consumePointerTypeQualifierList() {
if (DEBUG)
DebugUtil.printMethodTrace();
C99PointerType pointerType = new C99PointerType();
pointerType.setConst(typeQualifiers.isConst);
pointerType.setRestrict(typeQualifiers.isRestrict);
pointerType.setVolatile(typeQualifiers.isVolatile);
final DeclaratorFrame frame = declarationStack.getLast();
frame.addPointerModifier(pointerType);
undoStack.add(new IUndoAction() {
@Override
public void undo() {
if (DEBUG)
DebugUtil.printMethodTrace();
frame.removeLastPointerModifier();
}
});
}
public void consumeTypeQualifiers() {
if (DEBUG)
DebugUtil.printMethodTrace();
typeQualifiers = new TypeQualifiers();
for (IToken token : parser.getRuleTokens()) {
switch (token.getKind()) {
case C99Parsersym.TK_const:
typeQualifiers.isConst = true;
break;
case C99Parsersym.TK_restrict:
typeQualifiers.isRestrict = true;
break;
case C99Parsersym.TK_volatile:
typeQualifiers.isVolatile = true;
break;
}
}
// I don't think this is really necessary but I need an undo action anyway
undoStack.add(new IUndoAction() {
@Override
public void undo() {
if (DEBUG)
DebugUtil.printMethodTrace();
typeQualifiers = null;
}
});
}
/**
* Works for structs, unions and enums.
* If the struct tag is not in the symbol table then it is treated
* as a declaration.
*/
public IBinding consumeTypeSpecifierElaborated(int kind) {
if (DEBUG)
DebugUtil.printMethodTrace();
String tag = parser.getRightIToken().toString();
IC99Binding structBinding = symbolTable.lookup(STRUCT_TAG, tag);
final C99SymbolTable undoTable;
final boolean isDeclaration = (structBinding == null);
if (isDeclaration) { // declaration of an incomplete type
if (kind == IASTElaboratedTypeSpecifier.k_enum)
structBinding = new C99Enumeration();
else
structBinding = new C99Structure(kind);
undoTable = symbolTable;
symbolTable = symbolTable.insert(STRUCT_TAG, tag, structBinding);
} else {
undoTable = null; // final variable must be initialized
}
final DeclSpec declSpec = declarationStack.getLast().getDeclSpec();
declSpec.setType((IType) structBinding); // upcast
undoStack.add(new IUndoAction() {
@Override
public void undo() {
if (DEBUG)
DebugUtil.printMethodTrace();
declSpec.setType(null);
if (isDeclaration) {
assert undoTable != null;
symbolTable = undoTable;
}
}
});
return structBinding;
}
/**
* @param key A field in IASTCompositeTypeSpecifier.
*/
public IBinding consumeTypeSpecifierComposite(final boolean hasName, int key) {
if (DEBUG)
DebugUtil.printMethodTrace();
// If the symbol table isn't updated then its still ok to undo
// because setting symbolTable to oldTable will effectively be a no-op.
final C99SymbolTable oldTable = symbolTable;
C99Structure struct;
if (hasName) {
String ident = parser.getRuleTokens().get(1).toString();
struct = (C99Structure) symbolTable.lookup(STRUCT_TAG, ident); // structure may have already been declared
if (struct == null) {
struct = new C99Structure(ident, key);
symbolTable = symbolTable.insert(STRUCT_TAG, ident, struct);
}
} else {
struct = new C99Structure(key);
}
final DeclaratorFrame frame = declarationStack.getLast();
for (IBinding binding : frame.getNestedDeclarations()) {
// the parser may allow invalid declarations like typedefs inside of structures, ignore those
if (binding instanceof C99Field) {
C99Field field = (C99Field) binding;
struct.addField(field);
field.setCompositeTypeOwner(struct);
}
}
frame.getDeclSpec().setType(struct);
undoStack.add(new IUndoAction() {
@Override
public void undo() {
if (DEBUG)
DebugUtil.printMethodTrace();
frame.getDeclSpec().setType(null);
symbolTable = oldTable;
}
});
return struct;
}
public IBinding consumeTypeSpecifierEnumeration(final boolean hasName) {
if (DEBUG)
DebugUtil.printMethodTrace();
C99Enumeration enumeration = new C99Enumeration();
final C99SymbolTable oldTable = symbolTable;
if (hasName) {
String ident = parser.getRuleTokens().get(1).toString();
enumeration.setName(ident);
symbolTable = symbolTable.insert(STRUCT_TAG, ident, enumeration);
}
final DeclaratorFrame frame = declarationStack.getLast();
for (IBinding binding : frame.getNestedDeclarations()) {
C99Enumerator enumerator = (C99Enumerator) binding;
enumeration.addEnumerator(enumerator);
enumerator.setType(enumeration);
}
frame.getDeclSpec().setType(enumeration);
undoStack.add(new IUndoAction() {
@Override
public void undo() {
if (DEBUG)
DebugUtil.printMethodTrace();
frame.getDeclSpec().setType(null);
if (hasName)
symbolTable = oldTable;
}
});
return enumeration;
}
public IBinding consumeEnumerator() {
if (DEBUG)
DebugUtil.printMethodTrace();
IToken token = parser.getLeftIToken();
String ident = token.toString();
C99Enumerator enumerator = new C99Enumerator(ident);
final C99SymbolTable oldTable = symbolTable;
symbolTable = symbolTable.insert(IDENTIFIER, ident, enumerator);
// enumerators are not declarations in the standard sense, so a scope won't be opened for them
declarationStack.getLast().addNestedDeclaration(enumerator);
undoStack.add(new IUndoAction() {
@Override
public void undo() {
if (DEBUG)
DebugUtil.printMethodTrace();
declarationStack.getLast().removeLastNestedDeclaration();
symbolTable = oldTable;
}
});
return enumerator;
}
public IField consumeDesignatorBaseField() {
if (DEBUG)
DebugUtil.printMethodTrace();
IType baseType = getInitializerType();
String fieldName = parser.getRightIToken().toString();
C99Field fieldBinding = computeFieldBinding(baseType, fieldName, false);
IType type = fieldBinding == null ? C99ProblemBinding.badType() : fieldBinding.getType();
exprTypeStack.push(type);
undoStack.add(new IUndoAction() {
@Override
public void undo() {
if (DEBUG)
DebugUtil.printMethodTrace();
exprTypeStack.pop();
}
});
return fieldBinding;
}
private IType getInitializerType() {
List<IType> outerScope = exprTypeStack.outerScope();
return outerScope.get(outerScope.size() - 1);
}
public void consumeDesignatorBaseArray() {
if (DEBUG)
DebugUtil.printMethodTrace();
IType baseType = getInitializerType();
IType type = C99ProblemBinding.badType();
if (baseType instanceof C99ArrayType) {
type = ((C99ArrayType) baseType).getType();
}
exprTypeStack.push(type);
undoStack.add(new IUndoAction() {
@Override
public void undo() {
if (DEBUG)
DebugUtil.printMethodTrace();
exprTypeStack.pop();
}
});
}
public void consumeInitializerStartPositional() {
if (DEBUG)
DebugUtil.printMethodTrace();
DebugUtil.printMethodTrace();
IType type = getInitializerType();
type = rawType(type);
IType positionType;
if (type instanceof C99Structure) {
int position = exprTypeStack.topScope().size();
C99Field field = (C99Field) ((C99Structure) type).getFields()[position];
positionType = field.getType();
} else if (type instanceof IArrayType) {
positionType = ((C99ArrayType) type).getType();
} else {
positionType = C99ProblemBinding.badType();
}
exprTypeStack.push(positionType);
undoStack.add(new IUndoAction() {
@Override
public void undo() {
if (DEBUG)
DebugUtil.printMethodTrace();
exprTypeStack.pop();
}
});
}
private static C99FunctionType createFunctionType(IType returnType, List<IBinding> parameterDeclarations) {
C99FunctionType functionType = new C99FunctionType();
functionType.setReturnType(returnType);
for (IBinding b : parameterDeclarations) {
C99Variable parameter = (C99Variable) b;
functionType.addParameterType(parameter.getType());
}
return functionType;
}
// helper functions for creating binding objects
private static C99Function createFunctionBinding(String ident, IFunctionType type, DeclSpec declSpec) {
C99Function func = new C99Function(ident == null ? NO_IDENT : ident, type);
declSpec.modifyBinding(func);
return func;
}
private static C99Function createFunctionBinding(String ident, IFunctionType type, DeclSpec declSpec,
List<IBinding> params) {
C99Function func = createFunctionBinding(ident, type, declSpec);
for (IBinding b : params) {
func.addParameter((IParameter) b);
}
return func;
}
private static C99Field createFieldBinding(String ident, IType type, DeclSpec declSpec) {
C99Field var = new C99Field(ident == null ? NO_IDENT : ident);
var.setType(type);
declSpec.modifyBinding(var);
return var;
}
private static C99Parameter createParameterBinding(String ident, IType type, DeclSpec declSpec) {
C99Parameter param = new C99Parameter(ident == null ? NO_IDENT : ident);
param.setType(type);
declSpec.modifyBinding(param);
return param;
}
private static C99Variable createVariableBinding(String ident, IType type, DeclSpec declSpec) {
C99Variable var = new C99Variable(ident == null ? NO_IDENT : ident);
var.setType(type);
declSpec.modifyBinding(var);
return var;
}
private static C99Typedef createTypedefBinding(String ident, IType type) {
return new C99Typedef(type, ident == null ? NO_IDENT : ident);
}
public void openTypeScope() {
if (DEBUG)
DebugUtil.printMethodTrace();
exprTypeStack.openScope();
undoStack.add(new IUndoAction() {
@Override
public void undo() {
if (DEBUG)
DebugUtil.printMethodTrace();
exprTypeStack.closeScope();
}
});
}
@SuppressWarnings("nls")
public void consumeExpressionConstant(int kind) {
if (DEBUG)
DebugUtil.printMethodTrace();
//super.consumeExpressionConstant(kind);
// TODO: this is incomplete, what about double constants, int constants with long suffix etc
String constant = parser.getRightIToken().toString();
IType type = null;
switch (kind) {
case IASTLiteralExpression.lk_char_constant:
if (constant.startsWith("L")) {//unsigned short int
C99BasicType charType = new C99BasicType(IBasicType.t_int);
charType.setShort(true);
charType.setUnsigned(true);
type = charType;
} else
type = new C99BasicType(IBasicType.t_char);
break;
case IASTLiteralExpression.lk_float_constant:
C99BasicType floatType;
if (constant.contains("f") || constant.contains("F"))
floatType = new C99BasicType(IBasicType.t_float);
else
floatType = new C99BasicType(IBasicType.t_double);
if (constant.contains("l") || constant.contains("L"))
floatType.setLong(true);
type = floatType;
break;
case IASTLiteralExpression.lk_integer_constant:
C99BasicType intType = new C99BasicType(IBasicType.t_int);
if (constant.contains("l") || constant.contains("L"))
intType.setLong(true);
if (constant.contains("ll") || constant.contains("LL")) {
intType.setLongLong(true);
intType.setLong(false);
}
if (constant.contains("u") || constant.contains("U"))
intType.setUnsigned(true);
type = intType;
break;
case IASTLiteralExpression.lk_string_literal:
type = new C99PointerType(new C99BasicType(IBasicType.t_char));
break;
default:
assert false : "can't get here"; //$NON-NLS-1$
}
exprTypeStack.push(type);
undoStack.add(new IUndoAction() {
@Override
public void undo() {
if (DEBUG)
DebugUtil.printMethodTrace();
exprTypeStack.pop();
}
});
}
public IBinding consumeExpressionID() {
if (DEBUG)
DebugUtil.printMethodTrace();
IToken token = parser.getRightIToken();
String ident = token.toString();
IBinding binding = symbolTable.lookup(IDENTIFIER, ident);
IType type = C99ProblemBinding.badType();
if (binding instanceof ITypeable)
type = ((ITypeable) binding).getType();
exprTypeStack.push(type);
undoStack.add(new IUndoAction() {
@Override
public void undo() {
if (DEBUG)
DebugUtil.printMethodTrace();
exprTypeStack.pop();
}
});
return binding;
}
public IField consumeExpressionFieldReference(boolean isPointerDereference) {
if (DEBUG)
DebugUtil.printMethodTrace();
String memberName = parser.getRightIToken().toString();
final IType identType = exprTypeStack.pop();
C99Field field = computeFieldBinding(identType, memberName, isPointerDereference);
IType resultType = field == null ? C99ProblemBinding.badType() : field.getType();
exprTypeStack.push(resultType);
undoStack.add(new IUndoAction() {
@Override
public void undo() {
if (DEBUG)
DebugUtil.printMethodTrace();
exprTypeStack.pop();
exprTypeStack.push(identType);
}
});
return field;
}
/**
* Computes the type of a field member access in an expression.
* eg) x.a; computes the type of a
*/
private C99Field computeFieldBinding(IType identType, String memberName, boolean isPointerDereference) {
IType type = identType;
if (isPointerDereference) {
if (type instanceof IPointerType)
type = ((ITypeContainer) type).getType(); // do the dereference
else
return null;
}
type = rawType(type);
if (type instanceof ICompositeType) {
ICompositeType struct = (ICompositeType) type;
return (C99Field) struct.findField(memberName);
}
return null;
}
// TODO In C a function name can be used without parenthesis, the result is the address of
// the function (which is an int I think). This means an identifier that happens to be
// a function name should probably evaluate to an int, and then if it subsequently gets parsed
// as a function call we can look up its function type from the symbol table.
public void consumeExpressionFunctionCall(final boolean hasArgs) {
if (DEBUG)
DebugUtil.printMethodTrace();
final List<IType> scope = hasArgs ? exprTypeStack.closeScope() : null;
final IType identifierType = exprTypeStack.pop();
IType resultType = C99ProblemBinding.badType();
if (identifierType instanceof IFunctionType) {
// TODO: check the parameter types
IFunctionType functionType = (IFunctionType) identifierType;
resultType = functionType.getReturnType();
}
exprTypeStack.push(resultType);
undoStack.add(new IUndoAction() {
@Override
public void undo() {
if (DEBUG)
DebugUtil.printMethodTrace();
exprTypeStack.pop();
exprTypeStack.push(identifierType);
if (hasArgs)
exprTypeStack.openScope(scope);
}
});
}
public void consumeExpressionArraySubscript() {
if (DEBUG)
DebugUtil.printMethodTrace();
// Not doing type checking so it doesn't matter that this is integral type
final IType subscriptType = exprTypeStack.pop();
final IType exprType = exprTypeStack.pop();
IType resultType = C99ProblemBinding.badType();
if (exprType instanceof IArrayType) {
IArrayType arrType = (IArrayType) exprType;
resultType = arrType.getType(); // strip off the array type
}
exprTypeStack.push(resultType);
undoStack.add(new IUndoAction() {
@Override
public void undo() {
if (DEBUG)
DebugUtil.printMethodTrace();
exprTypeStack.pop();
exprTypeStack.push(exprType);
exprTypeStack.push(subscriptType);
}
});
}
public void consumeExpressionUnaryOperator() {
if (DEBUG)
DebugUtil.printMethodTrace();
// TODO: this is lazy, need to check the actual rules for types and operators
final IType expressionType = exprTypeStack.pop();
IType resultType = new C99BasicType(IBasicType.t_int);
exprTypeStack.push(resultType);
undoStack.add(new IUndoAction() {
@Override
public void undo() {
if (DEBUG)
DebugUtil.printMethodTrace();
exprTypeStack.pop();
exprTypeStack.push(expressionType);
}
});
}
public void consumeExpressionUnarySizeofTypeName() {
if (DEBUG)
DebugUtil.printMethodTrace();
final IType typeNameType = exprTypeStack.pop();
IType resultType = new C99BasicType(IBasicType.t_int);
exprTypeStack.push(resultType);
undoStack.add(new IUndoAction() {
@Override
public void undo() {
if (DEBUG)
DebugUtil.printMethodTrace();
exprTypeStack.pop();
exprTypeStack.push(typeNameType);
}
});
}
public void consumeExpressionCast() {
if (DEBUG)
DebugUtil.printMethodTrace();
final IType exprType = exprTypeStack.pop();
// pop then push is no-op
//IType castType = exprTypeStack.pop();
//exprTypeStack.push(castType);
undoStack.add(new IUndoAction() {
@Override
public void undo() {
if (DEBUG)
DebugUtil.printMethodTrace();
exprTypeStack.push(exprType);
}
});
}
public void consumeExpressionTypeIdInitializer() {
if (DEBUG)
DebugUtil.printMethodTrace();
// Throw away the types of the initializer list
final List<IType> scope = exprTypeStack.closeScope();
undoStack.add(new IUndoAction() {
@Override
public void undo() {
if (DEBUG)
DebugUtil.printMethodTrace();
exprTypeStack.openScope(scope);
}
});
}
public void consumeExpressionInitializer() {
if (DEBUG)
DebugUtil.printMethodTrace();
final IType type = exprTypeStack.pop();
undoStack.add(new IUndoAction() {
@Override
public void undo() {
if (DEBUG)
DebugUtil.printMethodTrace();
exprTypeStack.push(type);
}
});
}
public void consumeExpressionBinaryOperator(int op) {
if (DEBUG)
DebugUtil.printMethodTrace();
final IType op2type = exprTypeStack.pop();
final IType op1type = exprTypeStack.pop();
switch (op) {
case IASTBinaryExpression.op_assign:
case IASTBinaryExpression.op_multiplyAssign:
case IASTBinaryExpression.op_divideAssign:
case IASTBinaryExpression.op_moduloAssign:
case IASTBinaryExpression.op_plusAssign:
case IASTBinaryExpression.op_minusAssign:
case IASTBinaryExpression.op_shiftLeftAssign:
case IASTBinaryExpression.op_shiftRightAssign:
case IASTBinaryExpression.op_binaryAndAssign:
case IASTBinaryExpression.op_binaryXorAssign:
case IASTBinaryExpression.op_binaryOrAssign:
exprTypeStack.push(op1type);
break;
default:
IType resultType = new C99BasicType(IBasicType.t_int);
exprTypeStack.push(resultType);
}
undoStack.add(new IUndoAction() {
@Override
public void undo() {
if (DEBUG)
DebugUtil.printMethodTrace();
exprTypeStack.pop();
exprTypeStack.push(op1type);
exprTypeStack.push(op2type);
}
});
}
public void consumeExpressionConditional() {
if (DEBUG)
DebugUtil.printMethodTrace();
final IType expr2Type = exprTypeStack.pop();
final IType expr1Type = exprTypeStack.pop();
final IType condType = exprTypeStack.pop();
exprTypeStack.push(expr1Type);
undoStack.add(new IUndoAction() {
@Override
public void undo() {
if (DEBUG)
DebugUtil.printMethodTrace();
exprTypeStack.pop();
exprTypeStack.push(condType);
exprTypeStack.push(expr1Type);
exprTypeStack.push(expr2Type);
}
});
}
// // TODO: expression lists are changing
// public void consumeExpressionList(boolean baseCase) {
// // This is a hack, the type of an expression
// // list will be the first expression in the list.
// if(!baseCase) {
// exprTypeStack.pop();
// }
// }
}