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

Last change on this file since 166 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;