source: trunk/tools/porting/src/rpptreeevaluator.cpp@ 315

Last change on this file since 315 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 21.6 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information ([email protected])
5** Copyright (C) 2001-2004 Roberto Raggi
6**
7** This file is part of the qt3to4 porting application of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain
25** additional rights. These rights are described in the Nokia Qt LGPL
26** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
27** package.
28**
29** GNU General Public License Usage
30** Alternatively, this file may be used under the terms of the GNU
31** General Public License version 3.0 as published by the Free Software
32** Foundation and appearing in the file LICENSE.GPL included in the
33** packaging of this file. Please review the following information to
34** ensure the GNU General Public License version 3.0 requirements will be
35** met: http://www.gnu.org/copyleft/gpl.html.
36**
37** If you are unsure which license is appropriate for your use, please
38** contact the sales department at [email protected].
39** $QT_END_LICENSE$
40**
41****************************************************************************/
42
43#include "rpptreeevaluator.h"
44#include <QChar>
45#include <QtDebug>
46
47QT_BEGIN_NAMESPACE
48
49using namespace TokenEngine;
50namespace Rpp {
51
52RppTreeEvaluator::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
64RppTreeEvaluator::~RppTreeEvaluator()
65{
66 delete newlineSection;
67}
68
69TokenSectionSequence 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
78void 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*/
152void 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*/
178void 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
189void RppTreeEvaluator::evaluateDefineDirective(const DefineDirective *directive)
190{
191 m_tokenSections.append(*newlineSection);
192 m_activeDefinitions->insert(directive->identifier().fullText(), directive);
193}
194
195void 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*/
205bool 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*/
222int 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*/
299TokenContainer 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*/
337TokenEngine::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
408TokenContainer 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*/
430Source *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*/
444RppTreeEvaluator::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*/
459MacroFunctionParser::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*/
521bool 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*/
530int MacroFunctionParser::tokenCount()
531{
532 return m_numTokens;
533}
534
535/*
536 Returns the number of arguments for the macro function.
537*/
538int MacroFunctionParser::argumentCount()
539{
540 return m_arguments.count();
541}
542
543/*
544 Returns the tokens for the argument given by argumentIndex.
545*/
546TokenSection MacroFunctionParser::argument(int argumentIndex)
547{
548 Q_ASSERT(argumentIndex < m_arguments.count());
549 return m_arguments.at(argumentIndex);
550}
551
552} //namespace Rpp
553
554QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.