0
|
1 |
/****************************************************************************
|
|
2 |
**
|
|
3 |
** Copyright (C) 2001-2004 Roberto Raggi
|
|
4 |
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
|
|
5 |
** All rights reserved.
|
|
6 |
** Contact: Nokia Corporation (qt-info@nokia.com)
|
|
7 |
**
|
|
8 |
** This file is part of the qt3to4 porting application of the Qt Toolkit.
|
|
9 |
**
|
|
10 |
** $QT_BEGIN_LICENSE:LGPL$
|
|
11 |
** No Commercial Usage
|
|
12 |
** This file contains pre-release code and may not be distributed.
|
|
13 |
** You may use this file in accordance with the terms and conditions
|
|
14 |
** contained in the Technology Preview License Agreement accompanying
|
|
15 |
** this package.
|
|
16 |
**
|
|
17 |
** GNU Lesser General Public License Usage
|
|
18 |
** Alternatively, this file may be used under the terms of the GNU Lesser
|
|
19 |
** General Public License version 2.1 as published by the Free Software
|
|
20 |
** Foundation and appearing in the file LICENSE.LGPL included in the
|
|
21 |
** packaging of this file. Please review the following information to
|
|
22 |
** ensure the GNU Lesser General Public License version 2.1 requirements
|
|
23 |
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
|
24 |
**
|
|
25 |
** In addition, as a special exception, Nokia gives you certain additional
|
|
26 |
** rights. These rights are described in the Nokia Qt LGPL Exception
|
|
27 |
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
|
28 |
**
|
|
29 |
** If you have questions regarding the use of this file, please contact
|
|
30 |
** Nokia at qt-info@nokia.com.
|
|
31 |
**
|
|
32 |
**
|
|
33 |
**
|
|
34 |
**
|
|
35 |
**
|
|
36 |
**
|
|
37 |
**
|
|
38 |
**
|
|
39 |
** $QT_END_LICENSE$
|
|
40 |
**
|
|
41 |
****************************************************************************/
|
|
42 |
|
|
43 |
#include "rpptreeevaluator.h"
|
|
44 |
#include <QChar>
|
|
45 |
#include <QtDebug>
|
|
46 |
|
|
47 |
QT_BEGIN_NAMESPACE
|
|
48 |
|
|
49 |
using namespace TokenEngine;
|
|
50 |
namespace Rpp {
|
|
51 |
|
|
52 |
RppTreeEvaluator::RppTreeEvaluator()
|
|
53 |
{
|
|
54 |
QByteArray text(" ");
|
|
55 |
TokenEngine::Token token;
|
|
56 |
token.start = 0;
|
|
57 |
token.length = 1;
|
|
58 |
QVector<TokenEngine::Token> tokenList;
|
|
59 |
tokenList.append(token);
|
|
60 |
TokenContainer newLineContainer(text, tokenList, new TokenEngine::GeneratedInfo());
|
|
61 |
newlineSection= new TokenSection(newLineContainer, 0, 1);
|
|
62 |
}
|
|
63 |
|
|
64 |
RppTreeEvaluator::~RppTreeEvaluator()
|
|
65 |
{
|
|
66 |
delete newlineSection;
|
|
67 |
}
|
|
68 |
|
|
69 |
TokenSectionSequence RppTreeEvaluator::evaluate(const Source *source,
|
|
70 |
DefineMap *activeDefinitions)
|
|
71 |
{
|
|
72 |
m_tokenSections.clear();
|
|
73 |
m_activeDefinitions = activeDefinitions;
|
|
74 |
evaluateSource(source);
|
|
75 |
return TokenSectionSequence(m_tokenSections);
|
|
76 |
}
|
|
77 |
|
|
78 |
void RppTreeEvaluator::evaluateText(const Text *textLine)
|
|
79 |
{
|
|
80 |
const int numTokens = textLine->count();
|
|
81 |
const TokenContainer tokenContainer = textLine->text().tokenContainer(0);
|
|
82 |
|
|
83 |
int t = 0;
|
|
84 |
int startTokenRun = 0;
|
|
85 |
while(t < numTokens) {
|
|
86 |
const Token *currentToken = textLine->token(t);
|
|
87 |
int currentContainerIndex = currentToken->index();
|
|
88 |
//handle macro replacements
|
|
89 |
if(currentToken->toIdToken()) {
|
|
90 |
const int tokenIndex = currentToken->index();
|
|
91 |
const QByteArray tokenText = tokenContainer.tempText(tokenIndex);
|
|
92 |
if(m_activeDefinitions->contains(tokenText)) {
|
|
93 |
//crate section
|
|
94 |
TokenSection section(tokenContainer, textLine->token(startTokenRun)->index(), t - startTokenRun);
|
|
95 |
m_tokenSections.append(section);
|
|
96 |
//evaluate macro
|
|
97 |
const int oldContainerIndex = currentContainerIndex;
|
|
98 |
TokenContainer evaluatedText = evaluateMacro(tokenContainer, currentContainerIndex);
|
|
99 |
TokenSection evalSection(evaluatedText, 0, evaluatedText.count());
|
|
100 |
m_tokenSections.append(evalSection);
|
|
101 |
t += currentContainerIndex - oldContainerIndex;
|
|
102 |
startTokenRun = t;
|
|
103 |
}
|
|
104 |
++t;
|
|
105 |
continue;
|
|
106 |
}
|
|
107 |
|
|
108 |
//handle comments
|
|
109 |
if(currentToken->toLineComment() || currentToken->toMultiLineComment()) {
|
|
110 |
//create section
|
|
111 |
TokenSection section(tokenContainer, textLine->token(startTokenRun)->index(), t - startTokenRun );
|
|
112 |
m_tokenSections.append(section);
|
|
113 |
t++; //skip comment
|
|
114 |
startTokenRun = t;
|
|
115 |
t++;
|
|
116 |
continue;
|
|
117 |
}
|
|
118 |
|
|
119 |
// handle escaped newlines
|
|
120 |
if (currentContainerIndex + 1 < numTokens) {
|
|
121 |
const TokenTempRef tokenRef1 = tokenContainer.tokenTempRef(currentContainerIndex);
|
|
122 |
const TokenTempRef tokenRef2 = tokenContainer.tokenTempRef(currentContainerIndex + 1);
|
|
123 |
// This is i slight hack. We want to check if the next token is a newline token,
|
|
124 |
// but since we don't have any lexical info at this point we just check if it starts
|
|
125 |
// with \r or \n
|
|
126 |
if (tokenRef1.at(0) == '\\' && (tokenRef2.at(0) == '\n' || tokenRef2.at(0) == '\r')) {
|
|
127 |
//create section
|
|
128 |
TokenSection section(tokenContainer, textLine->token(startTokenRun)->index(), t - startTokenRun );
|
|
129 |
m_tokenSections.append(section);
|
|
130 |
t += 2;
|
|
131 |
startTokenRun = t;
|
|
132 |
t++;
|
|
133 |
continue;
|
|
134 |
}
|
|
135 |
}
|
|
136 |
|
|
137 |
t++;
|
|
138 |
}
|
|
139 |
//round up any tokens at the end and put them in a section
|
|
140 |
if(t - startTokenRun > 1) {
|
|
141 |
TokenSection section(tokenContainer, textLine->token(startTokenRun)->index(), t - startTokenRun );
|
|
142 |
m_tokenSections.append(section);
|
|
143 |
}
|
|
144 |
|
|
145 |
m_tokenSections.append(*newlineSection);
|
|
146 |
}
|
|
147 |
|
|
148 |
/*
|
|
149 |
Evaluates and ifsection by selecting which one of the if-elif-else
|
|
150 |
groups and then evaling that.
|
|
151 |
*/
|
|
152 |
void RppTreeEvaluator::evaluateIfSection(const IfSection *ifSection)
|
|
153 |
{
|
|
154 |
ConditionalDirective *ifGroup = ifSection->ifGroup();
|
|
155 |
if(evaluateCondition(ifGroup)) {
|
|
156 |
evaluateConditionalDirective(ifGroup);
|
|
157 |
return;
|
|
158 |
}
|
|
159 |
|
|
160 |
QVector<ConditionalDirective *> elifGroups = ifSection->elifGroups();
|
|
161 |
foreach(ConditionalDirective *elifGroup, elifGroups) {
|
|
162 |
if(evaluateCondition(elifGroup)) {
|
|
163 |
evaluateConditionalDirective(elifGroup);
|
|
164 |
return;
|
|
165 |
}
|
|
166 |
}
|
|
167 |
|
|
168 |
ConditionalDirective *elseGroup = ifSection->elseGroup();
|
|
169 |
if(elseGroup)
|
|
170 |
evaluateConditionalDirective(elseGroup);
|
|
171 |
}
|
|
172 |
|
|
173 |
/*
|
|
174 |
Evaluate an IncludeDirective by evaluating the Source for the included
|
|
175 |
file. The source is found by emitting the includeCallback signal, which
|
|
176 |
must be handled outside RppTreeEvaluator.
|
|
177 |
*/
|
|
178 |
void RppTreeEvaluator::evaluateIncludeDirective(const IncludeDirective *directive)
|
|
179 |
{
|
|
180 |
Source *currentSource = getParentSource(directive);
|
|
181 |
IncludeType includeType = includeTypeFromDirective(directive);
|
|
182 |
Source *newSource = 0;
|
|
183 |
emit includeCallback(newSource, currentSource, QString::fromLatin1(directive->filename().constData()), includeType);
|
|
184 |
Q_ASSERT(newSource); // If you get an assert here you probably
|
|
185 |
// forgot to connect to the includeCallback signal
|
|
186 |
evaluateSource(newSource);
|
|
187 |
}
|
|
188 |
|
|
189 |
void RppTreeEvaluator::evaluateDefineDirective(const DefineDirective *directive)
|
|
190 |
{
|
|
191 |
m_tokenSections.append(*newlineSection);
|
|
192 |
m_activeDefinitions->insert(directive->identifier().fullText(), directive);
|
|
193 |
}
|
|
194 |
|
|
195 |
void RppTreeEvaluator::evaluateUndefDirective(const UndefDirective *directive)
|
|
196 |
{
|
|
197 |
m_tokenSections.append(*newlineSection);
|
|
198 |
const QByteArray text = directive->identifier().fullText();
|
|
199 |
m_activeDefinitions->remove(text);
|
|
200 |
}
|
|
201 |
|
|
202 |
/*
|
|
203 |
Evaluate the truth-value of an conditionalDirective
|
|
204 |
*/
|
|
205 |
bool RppTreeEvaluator::evaluateCondition(const ConditionalDirective *conditionalDirective)
|
|
206 |
{
|
|
207 |
if (IfDirective *ifDirective = conditionalDirective->toIfDirective())
|
|
208 |
return (evaluateExpression(ifDirective->expression()) != 0);
|
|
209 |
if (ElifDirective *elifDirective = conditionalDirective->toElifDirective())
|
|
210 |
return (evaluateExpression(elifDirective->expression()) != 0);
|
|
211 |
if (IfdefDirective *ifdefDirective = conditionalDirective->toIfdefDirective())
|
|
212 |
return m_activeDefinitions->contains(ifdefDirective->identifier().fullText());
|
|
213 |
if (IfndefDirective *ifndefDirective = conditionalDirective->toIfndefDirective())
|
|
214 |
return !m_activeDefinitions->contains(ifndefDirective->identifier().fullText());
|
|
215 |
else
|
|
216 |
return false; //error!
|
|
217 |
}
|
|
218 |
|
|
219 |
/*
|
|
220 |
Recursively evaluates an Expression
|
|
221 |
*/
|
|
222 |
int RppTreeEvaluator::evaluateExpression(Expression *expression)
|
|
223 |
{
|
|
224 |
if (IntLiteral *e = expression->toIntLiteral()) {
|
|
225 |
return e->value();
|
|
226 |
} else if (StringLiteral *e = expression->toStringLiteral()) {
|
|
227 |
return e->value().size();
|
|
228 |
} else if (MacroReference *e = expression->toMacroReference()) {
|
|
229 |
switch(e->type()) {
|
|
230 |
case MacroReference::DefinedRef: {
|
|
231 |
return m_activeDefinitions->contains(e->name().fullText()) ? 1 : 0;
|
|
232 |
} case MacroReference::ValueRef: {
|
|
233 |
const QByteArray identifier = e->name().fullText();
|
|
234 |
if (m_activeDefinitions->contains(identifier)) {
|
|
235 |
int token = e->name().containerIndex(0);
|
|
236 |
TokenContainer value = evaluateMacro(e->name().tokenContainer(token), token);
|
|
237 |
return QString(QLatin1String(value.fullText())).toInt(0, 0);
|
|
238 |
} else {
|
|
239 |
return 0; // error
|
|
240 |
}
|
|
241 |
}
|
|
242 |
default: Q_ASSERT(0);
|
|
243 |
}
|
|
244 |
} else if (MacroFunctionReference *e = expression->toMacroFunctionReference()) {
|
|
245 |
Q_UNUSED(e);
|
|
246 |
//TODO handle MacroFunctionReference
|
|
247 |
// DefineDirective *def = e->findDefinition(e->name());
|
|
248 |
// Q_ASSERT(def->toMacroFunctionDefinition());
|
|
249 |
// qWarning("not implemented yet");
|
|
250 |
return 0;
|
|
251 |
} else if (UnaryExpression *e = expression->toUnaryExpression()) {
|
|
252 |
int result = evaluateExpression(e->expression());
|
|
253 |
switch (e->op()) {
|
|
254 |
case '+': return + result;
|
|
255 |
case '-': return - result;
|
|
256 |
case '!': return ! result;
|
|
257 |
case '~': return ~ result;
|
|
258 |
default: Q_ASSERT(0);
|
|
259 |
}
|
|
260 |
} else if (BinaryExpression *e = expression->toBinaryExpression()) {
|
|
261 |
int v1 = evaluateExpression(e->leftExpression());
|
|
262 |
int v2 = evaluateExpression(e->rightExpression());
|
|
263 |
|
|
264 |
switch (e->op()) {
|
|
265 |
case '/': { return v2 ? v1 / v2 : 0; } //avoid division by zero
|
|
266 |
case '*': return v1 * v2;
|
|
267 |
case '%': { return v2 ? v1 % v2 : 0; } //avoid modulus by zero
|
|
268 |
case '+': return v1 + v2;
|
|
269 |
case '-': return v1 - v2;
|
|
270 |
case '<': return v1 < v2;
|
|
271 |
case '>': return v1 > v2;
|
|
272 |
case '&': return v1 & v2;
|
|
273 |
case '^': return v1 ^ v2;
|
|
274 |
case '|': return v1 | v2;
|
|
275 |
case Expression::LtEqOp: return v1 <= v2;
|
|
276 |
case Expression::GtEqOp: return v1 >= v2;
|
|
277 |
case Expression::EqOp: return v1 == v2;
|
|
278 |
case Expression::NotEqOp: return v1 != v2;
|
|
279 |
case Expression::AndOp: return v1 && v2;
|
|
280 |
case Expression::OrOp: return v1 || v2;
|
|
281 |
case Expression::LShiftOp: return v1 << v2;
|
|
282 |
case Expression::RShiftOp: return v1 >> v2;
|
|
283 |
default: Q_ASSERT(0);
|
|
284 |
}
|
|
285 |
|
|
286 |
} else if ( ConditionalExpression *e = expression->toConditionalExpression()){
|
|
287 |
return e->condition() ? evaluateExpression(e->leftExpression()) : evaluateExpression(e->rightExpression());
|
|
288 |
}
|
|
289 |
return 0;
|
|
290 |
}
|
|
291 |
/*
|
|
292 |
Expands a macro at index identiferTokenIndex in tokenContainer. Returns
|
|
293 |
the expanded macro text, and updates identiferTokenIndex to point after
|
|
294 |
the last token consumed.
|
|
295 |
|
|
296 |
Given the construct 'FN(a)', the '(a)' part will be consumed if FN is
|
|
297 |
defined to be a macro function, but not if it is an ordenary macro.
|
|
298 |
*/
|
|
299 |
TokenContainer RppTreeEvaluator::evaluateMacro(TokenContainer tokenContainer, int &identiferTokenIndex)
|
|
300 |
{
|
|
301 |
QByteArray identifierText = tokenContainer.text(identiferTokenIndex);
|
|
302 |
if(!m_activeDefinitions->contains(identifierText))
|
|
303 |
return TokenContainer();
|
|
304 |
|
|
305 |
const Rpp::DefineDirective *directive = m_activeDefinitions->value(identifierText);
|
|
306 |
Q_ASSERT(directive);
|
|
307 |
|
|
308 |
// To prevent infinite recursive macro expansions, the skip set contains
|
|
309 |
// a set of identifers already seen.
|
|
310 |
QSet<QByteArray> skip;
|
|
311 |
|
|
312 |
if(directive->toMacroDefinition()) {
|
|
313 |
++identiferTokenIndex;
|
|
314 |
QVector<TokenEngine::Token> tokenList;
|
|
315 |
tokenList.append(TokenEngine::Token(0, identifierText.count()));
|
|
316 |
return evaluateMacroInternal(skip, TokenContainer(identifierText, tokenList));
|
|
317 |
} else if (Rpp::MacroFunctionDefinition *macro = directive->toMacroFunctionDefinition()) {
|
|
318 |
MacroFunctionParser macroFunctionParser(tokenContainer, identiferTokenIndex);
|
|
319 |
if (macroFunctionParser.isValid() && macro->parameters().count() == macroFunctionParser.argumentCount()) {
|
|
320 |
TokenContainer macroFunctionContainer =
|
|
321 |
TokenEngine::copy(tokenContainer, identiferTokenIndex, macroFunctionParser.tokenCount());
|
|
322 |
identiferTokenIndex += macroFunctionParser.tokenCount();
|
|
323 |
return evaluateMacroInternal(skip, macroFunctionContainer);
|
|
324 |
} else {
|
|
325 |
// Error case, such as calling a macro function with the wrong number of parameters,
|
|
326 |
// or calling a macro function witout a parameter list.
|
|
327 |
return TokenEngine::copy(tokenContainer, identiferTokenIndex++, 1);
|
|
328 |
}
|
|
329 |
}
|
|
330 |
return TokenContainer();
|
|
331 |
}
|
|
332 |
|
|
333 |
/*
|
|
334 |
Recursively expands all macroes in macroInvokeTokens, returns a
|
|
335 |
TokenContainer with the new tokens.
|
|
336 |
*/
|
|
337 |
TokenEngine::TokenContainer RppTreeEvaluator::evaluateMacroInternal(QSet<QByteArray> skip, TokenEngine::TokenContainer macroInvokeTokens)
|
|
338 |
{
|
|
339 |
bool changed = false;
|
|
340 |
QByteArray tokenText;
|
|
341 |
QVector<TokenEngine::Token> tokenList;
|
|
342 |
const int numTokens = macroInvokeTokens.count();
|
|
343 |
|
|
344 |
for (int t = 0; t < numTokens; ++t) {
|
|
345 |
const QByteArray identifierText = macroInvokeTokens.text(t);
|
|
346 |
|
|
347 |
// if the current token text is not a part of a macro definition we just copy it.
|
|
348 |
if (!m_activeDefinitions->contains(identifierText)) {
|
|
349 |
tokenList.append(TokenEngine::Token(tokenText.count(), identifierText.count()));
|
|
350 |
tokenText.append(identifierText);
|
|
351 |
continue;
|
|
352 |
}
|
|
353 |
|
|
354 |
// If the token text is in the skip list we copy it.
|
|
355 |
if (skip.contains(identifierText)) {
|
|
356 |
tokenList.append(TokenEngine::Token(tokenText.count(), identifierText.count()));
|
|
357 |
tokenText.append(identifierText);
|
|
358 |
continue;
|
|
359 |
}
|
|
360 |
|
|
361 |
skip.insert(identifierText);
|
|
362 |
changed = true;
|
|
363 |
const Rpp::DefineDirective *directive = m_activeDefinitions->value(identifierText);
|
|
364 |
Q_ASSERT(directive);
|
|
365 |
// if it is a macro, we copy in the replacement list.
|
|
366 |
if (Rpp::MacroDefinition *macro = directive->toMacroDefinition()) {
|
|
367 |
TokenList replacementList = macro->replacementList();
|
|
368 |
TokenEngine::copy(tokenText, tokenList, replacementList, 0, replacementList.count());
|
|
369 |
|
|
370 |
// To avoid infinite loops, set changed to false if the replacement
|
|
371 |
// text is identical to the identifier text.
|
|
372 |
if (replacementList.fullText().simplified() == identifierText.simplified())
|
|
373 |
changed = false;
|
|
374 |
} else if (Rpp::MacroFunctionDefinition *macro = directive->toMacroFunctionDefinition()) {
|
|
375 |
TokenList replacementList = macro->replacementList();
|
|
376 |
TokenList paramenterList = macro->parameters();
|
|
377 |
|
|
378 |
MacroFunctionParser macroFunctionParser(macroInvokeTokens, t);
|
|
379 |
if (macroFunctionParser.isValid() && macro->parameters().count() == macroFunctionParser.argumentCount()) {
|
|
380 |
t += macroFunctionParser.tokenCount();
|
|
381 |
// For each token in the replacement list: If the token matches a
|
|
382 |
// token in the parameter list, replace it with the
|
|
383 |
// corresponding argument tokens from the argument list.
|
|
384 |
for (int replacementToken = 0; replacementToken < replacementList.count(); ++replacementToken) {
|
|
385 |
const QByteArray replacementTokenText = replacementList.text(replacementToken);
|
|
386 |
bool replaced = false;
|
|
387 |
for (int parameterToken = 0; parameterToken < paramenterList.count(); ++parameterToken) {
|
|
388 |
const QByteArray parameterTokenText = paramenterList.text(parameterToken);
|
|
389 |
if (parameterTokenText == replacementTokenText) {
|
|
390 |
TokenSection argumentTokenSection = macroFunctionParser.argument(parameterToken);
|
|
391 |
TokenEngine::copy(tokenText, tokenList, argumentTokenSection, 0, argumentTokenSection.count());
|
|
392 |
replaced = true;
|
|
393 |
break;
|
|
394 |
}
|
|
395 |
}
|
|
396 |
if (! replaced) {
|
|
397 |
TokenEngine::copy(tokenText, tokenList, replacementList, replacementToken, 1);
|
|
398 |
}
|
|
399 |
}
|
|
400 |
}
|
|
401 |
}
|
|
402 |
}
|
|
403 |
if (!changed)
|
|
404 |
return macroInvokeTokens;
|
|
405 |
return evaluateMacroInternal(skip, TokenContainer(tokenText, tokenList));
|
|
406 |
}
|
|
407 |
|
|
408 |
TokenContainer RppTreeEvaluator::cloneTokenList(const TokenList &list)
|
|
409 |
{
|
|
410 |
QByteArray text;
|
|
411 |
QVector<TokenEngine::Token> tokens;
|
|
412 |
int index = 0;
|
|
413 |
for (int t = 0; t<list.count(); ++t) {
|
|
414 |
const QByteArray tokenText = list.text(t);
|
|
415 |
const int textLength = tokenText.count();
|
|
416 |
text += tokenText;
|
|
417 |
TokenEngine::Token token;
|
|
418 |
token.start = index;
|
|
419 |
token.length = textLength;
|
|
420 |
tokens.append(token);
|
|
421 |
index += textLength;
|
|
422 |
}
|
|
423 |
TokenContainer container(text, tokens, new GeneratedInfo());
|
|
424 |
return container;
|
|
425 |
}
|
|
426 |
|
|
427 |
/*
|
|
428 |
Returns the parent Source for a given item.
|
|
429 |
*/
|
|
430 |
Source *RppTreeEvaluator::getParentSource(const Item *item) const
|
|
431 |
{
|
|
432 |
Q_ASSERT(item);
|
|
433 |
while(item->toSource() == 0) {
|
|
434 |
item = item->parent();
|
|
435 |
Q_ASSERT(item);
|
|
436 |
}
|
|
437 |
|
|
438 |
return item->toSource();
|
|
439 |
}
|
|
440 |
/*
|
|
441 |
We have two IncludeType enums, one in IncludeDirective and one in
|
|
442 |
RppTreeEvaluator. This function translates between them.
|
|
443 |
*/
|
|
444 |
RppTreeEvaluator::IncludeType RppTreeEvaluator::includeTypeFromDirective(
|
|
445 |
const IncludeDirective *includeDirective) const
|
|
446 |
{
|
|
447 |
if(includeDirective->includeType() == IncludeDirective::QuoteInclude)
|
|
448 |
return QuoteInclude;
|
|
449 |
else
|
|
450 |
return AngleBracketInclude;
|
|
451 |
}
|
|
452 |
|
|
453 |
/*
|
|
454 |
The MacrofunctionParser class is used to parse a macro function call (not
|
|
455 |
a macro function definition.)
|
|
456 |
|
|
457 |
startToken should give the token index for the identifier token for the macro function.
|
|
458 |
*/
|
|
459 |
MacroFunctionParser::MacroFunctionParser(const TokenEngine::TokenContainer &tokenContainer, int startToken)
|
|
460 |
:m_tokenContainer(tokenContainer)
|
|
461 |
,m_startToken(startToken)
|
|
462 |
,m_numTokens(0)
|
|
463 |
,m_valid(false)
|
|
464 |
{
|
|
465 |
int tokenIndex = startToken;
|
|
466 |
++tokenIndex; //skip identifier token
|
|
467 |
int parenthesisCount = 0;
|
|
468 |
int currentArgumentStartToken = tokenIndex;
|
|
469 |
|
|
470 |
// Parse argument tokens, add arguments to the m_arguments list.
|
|
471 |
// Arguments may consist of multiple tokens. Parenthesis in arguments
|
|
472 |
// are allowed, as long as they match. Inside a pair of argument
|
|
473 |
// parenthesis, ',' no longer signals a new argument. For example,
|
|
474 |
// FN((a,b)) is legal and contains one argument.
|
|
475 |
while(tokenIndex < tokenContainer.count()) {
|
|
476 |
QByteArray currentText = tokenContainer.text(tokenIndex);
|
|
477 |
++tokenIndex;
|
|
478 |
if (currentText == "(") {
|
|
479 |
++parenthesisCount;
|
|
480 |
if (parenthesisCount == 1) {
|
|
481 |
// first parenthesis
|
|
482 |
currentArgumentStartToken = tokenIndex;
|
|
483 |
continue;
|
|
484 |
}
|
|
485 |
}
|
|
486 |
if (currentText == ")") {
|
|
487 |
--parenthesisCount;
|
|
488 |
if (parenthesisCount == 0) {
|
|
489 |
//end of argument
|
|
490 |
m_arguments.append(TokenSection(tokenContainer, currentArgumentStartToken, tokenIndex - currentArgumentStartToken - 1));
|
|
491 |
currentArgumentStartToken = tokenIndex;
|
|
492 |
//end of argument list
|
|
493 |
break;
|
|
494 |
}
|
|
495 |
}
|
|
496 |
if (currentText == "," && parenthesisCount == 1) {
|
|
497 |
//end of argument
|
|
498 |
m_arguments.append(TokenSection(tokenContainer, currentArgumentStartToken, tokenIndex - currentArgumentStartToken - 1));
|
|
499 |
currentArgumentStartToken = tokenIndex;
|
|
500 |
continue;
|
|
501 |
}
|
|
502 |
|
|
503 |
if (QChar::fromLatin1(currentText.at(0)).isSpace()) {
|
|
504 |
continue;
|
|
505 |
}
|
|
506 |
|
|
507 |
// If we get here without having seen a paranthesis we have a syntax
|
|
508 |
// error in the macro function call.
|
|
509 |
if (parenthesisCount == 0) {
|
|
510 |
parenthesisCount = -1;
|
|
511 |
break;
|
|
512 |
}
|
|
513 |
}
|
|
514 |
m_numTokens = tokenIndex - startToken;
|
|
515 |
m_valid = (parenthesisCount == 0);
|
|
516 |
}
|
|
517 |
|
|
518 |
/*
|
|
519 |
Returns true if the MacroFunctionParser contains a valid macro function
|
|
520 |
*/
|
|
521 |
bool MacroFunctionParser::isValid()
|
|
522 |
{
|
|
523 |
return m_valid;
|
|
524 |
}
|
|
525 |
|
|
526 |
/*
|
|
527 |
Returns the number of tokens in the tokenContainer that is covered by
|
|
528 |
the macro function.
|
|
529 |
*/
|
|
530 |
int MacroFunctionParser::tokenCount()
|
|
531 |
{
|
|
532 |
return m_numTokens;
|
|
533 |
}
|
|
534 |
|
|
535 |
/*
|
|
536 |
Returns the number of arguments for the macro function.
|
|
537 |
*/
|
|
538 |
int MacroFunctionParser::argumentCount()
|
|
539 |
{
|
|
540 |
return m_arguments.count();
|
|
541 |
}
|
|
542 |
|
|
543 |
/*
|
|
544 |
Returns the tokens for the argument given by argumentIndex.
|
|
545 |
*/
|
|
546 |
TokenSection MacroFunctionParser::argument(int argumentIndex)
|
|
547 |
{
|
|
548 |
Q_ASSERT(argumentIndex < m_arguments.count());
|
|
549 |
return m_arguments.at(argumentIndex);
|
|
550 |
}
|
|
551 |
|
|
552 |
} //namespace Rpp
|
|
553 |
|
|
554 |
QT_END_NAMESPACE
|