Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 973e5de5cd1fd755bea2918867904c0eb6812401 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
/*******************************************************************************
 * 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.EnumSet;
import java.util.List;
import java.util.Set;

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.IParser;
import org.eclipse.cdt.core.dom.lrparser.IParserActionTokenProvider;
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 IParserActionTokenProvider parser;
	
	/** 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 Set<IParser.Options> options = EnumSet.noneOf(IParser.Options.class);
	
	
	
	
	/**
	 * 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(IParserActionTokenProvider 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.parser = parser;
		this.astStack = astStack;
	}
	
	

	protected void setOffsetAndLength(IASTNode node) {
		int ruleOffset = parser.getLeftIToken().getStartOffset();
		int ruleLength = parser.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 setParserOptions(Set<IParser.Options> options) {
		this.options = options == null ? EnumSet.noneOf(IParser.Options.class) : options;
	}
	
	/**
	 * 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) {
			prefix = (prefix == null || prefix.length() == 0) ? null : prefix;
			completionNode = newCompletionNode(prefix);
		}
		
		completionNode.addName(name);
	}
	
	public ASTCompletionNode newCompletionNode(String prefix) {
		return new ASTCompletionNode((prefix == null || prefix.length() == 0) ? null : 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(IParser<N> secondaryParser) {
		return runSecondaryParser(secondaryParser, parser.getRuleTokens());
	}
	
	
	/**
	 * Runs the given parser on the tokens that make up the current rule.
	 */
	protected <N extends IASTNode> N runSecondaryParser(IParser<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(options);
		
		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(parser.getRightIToken());
	}
}

Back to the top