/******************************************************************************* * Copyright (c) 2009 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: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.cdt.core.dom.lrparser.action; import java.util.Collections; import java.util.List; import java.util.Map; import lpg.lpgjavaruntime.IToken; import org.eclipse.cdt.core.dom.ast.IASTCompletionNode; import org.eclipse.cdt.core.dom.ast.IASTName; import org.eclipse.cdt.core.dom.ast.IASTNode; import org.eclipse.cdt.core.dom.lrparser.ISecondaryParser; import org.eclipse.cdt.internal.core.dom.parser.ASTNode; @SuppressWarnings("restriction") public abstract class AbstractParserAction { /** * Used with very simple optional rules that just say * that some particular token or keyword is optional. * The presence of the PLACE_HOLDER on the stack means that the keyword * was parsed, the presence of null means the keyword wasn't parsed. * * @see BuildASTParserAction#consumePlaceHolder() * @see BuildASTParserAction#consumeEmpty() */ protected static final Object PLACE_HOLDER = Boolean.TRUE; // any object will do /** Provides an interface to the token stream */ protected final ITokenStream stream; /** Stack that holds the intermediate nodes as the AST is being built */ protected final ScopedStack<Object> astStack; /** The completion node, only generated during a completion parse */ protected ASTCompletionNode completionNode; /** Options that change the behavior of the parser actions */ protected Map<String,String> properties = Collections.emptyMap(); /** * Completion tokens are represented by different kinds by different parsers. */ protected abstract boolean isCompletionToken(IToken token); protected abstract IASTName createName(char[] image); /** * Create a new parser action. * @param tu Root node of the AST, its list of declarations should be empty. * @throws NullPointerException if any of the parameters are null */ public AbstractParserAction(ITokenStream parser, ScopedStack<Object> astStack) { if(parser == null) throw new NullPointerException("parser is null"); //$NON-NLS-1$ if(astStack == null) throw new NullPointerException("astStack is null"); //$NON-NLS-1$ this.stream = parser; this.astStack = astStack; } protected void setOffsetAndLength(IASTNode node) { int ruleOffset = stream.getLeftIToken().getStartOffset(); int ruleLength = stream.getRightIToken().getEndOffset() - ruleOffset; ((ASTNode)node).setOffsetAndLength(ruleOffset, ruleLength < 0 ? 0 : ruleLength); } /** * Creates a IASTName node from an identifier token. * If the token is a completion token then it is added to the completion node. */ protected IASTName createName(IToken token) { IASTName name = createName(token.toString().toCharArray()); // TODO, token.toCharArray(); ParserUtil.setOffsetAndLength(name, token); if(isCompletionToken(token)) addNameToCompletionNode(name, token.toString()); return name; } public void setParserProperties(Map<String,String> properties) { this.properties = properties == null ? Collections.<String,String>emptyMap() : properties; } /** * Creates a completion node if one does not yet exist and adds the * given name to it. */ protected void addNameToCompletionNode(IASTName name, String prefix) { if(completionNode == null) { completionNode = newCompletionNode(prefix); } completionNode.addName(name); } public ASTCompletionNode newCompletionNode(String prefix) { return new ASTCompletionNode(prefix); } /** * Returns the completion node if this is a completion parse, null otherwise. */ public IASTCompletionNode getASTCompletionNode() { return completionNode; } /** * Returns the parse result. * @return */ public IASTNode getParseResult() { return (IASTNode) astStack.peek(); } /** * Runs the given parser on the given token list. * */ protected <N extends IASTNode> N runSecondaryParser(ISecondaryParser<N> secondaryParser) { return runSecondaryParser(secondaryParser, stream.getRuleTokens()); } /** * Runs the given parser on the tokens that make up the current rule. */ protected <N extends IASTNode> N runSecondaryParser(ISecondaryParser<N> secondaryParser, List<IToken> tokens) { // the secondary parser will alter the token kinds, which will need to be undone int[] savedKinds = new int[tokens.size()]; int i = 0; for(IToken token : tokens) savedKinds[i++] = token.getKind(); secondaryParser.setTokens(tokens); N result = secondaryParser.parse(); IASTCompletionNode compNode = secondaryParser.getCompletionNode(); if(compNode != null) { for(IASTName name : compNode.getNames()) addNameToCompletionNode(name, compNode.getPrefix()); } // restore the token kinds i = 0; for(IToken token : tokens) token.setKind(savedKinds[i++]); return result; } /************************************************************************************************************* * Basic Actions ************************************************************************************************************/ /** * Method that is called by the special <openscope> production * in order to create a new scope in the AST stack. */ public void openASTScope() { astStack.openScope(); } /** * Place null on the stack. * Usually called for optional element to indicate the element * was not parsed. */ public void consumeEmpty() { astStack.push(null); } /** * Place a marker on the stack. * Usually used for very simple optional elements to indicate * the element was parsed. Usually the existence of an AST node * on the stack is used instead of the marker, but for simple * cases like an optional keyword this action is useful. */ public void consumePlaceHolder() { astStack.push(PLACE_HOLDER); } /** * Just pops the stack, useful if you have a rule that generates * a node but you don't need the node. */ public void consumeIgnore() { astStack.pop(); } /** * Gets the current token and places it on the stack for later consumption. */ public void consumeToken() { astStack.push(stream.getRightIToken()); } }