~ubuntu-branches/debian/sid/kdevelop/sid

« back to all changes in this revision

Viewing changes to projectmanagers/cmake/parser/cmakelistsparser.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Jeremy Lainé
  • Date: 2010-05-05 07:21:55 UTC
  • mfrom: (1.2.3 upstream) (5.1.2 squeeze)
  • Revision ID: james.westby@ubuntu.com-20100505072155-h78lx19pu04sbhtn
Tags: 4:4.0.0-2
* Upload to unstable (Closes: #579947, #481832).
* Acknowledge obsolete NMU fixes (Closes: #562410, #546961).

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* KDevelop CMake Support
 
2
 *
 
3
 * Copyright 2006 Matt Rogers <mattr@kde.org>
 
4
 * Copyright 2008 Aleix Pol <aleixpol@gmail.com>
 
5
 *
 
6
 * This program is free software; you can redistribute it and/or
 
7
 * modify it under the terms of the GNU General Public License
 
8
 * as published by the Free Software Foundation; either version 2
 
9
 * of the License, or (at your option) any later version.
 
10
 *
 
11
 * This program is distributed in the hope that it will be useful,
 
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
14
 * GNU General Public License for more details.
 
15
 *
 
16
 * You should have received a copy of the GNU General Public License
 
17
 * along with this program; if not, write to the Free Software
 
18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 
19
 * 02110-1301, USA.
 
20
 */
 
21
 
 
22
#include "cmakelistsparser.h"
 
23
#include "cmakeast.h"
 
24
// #include "cmakeprojectvisitor.h"
 
25
#include "astfactory.h"
 
26
 
 
27
#include <QStack>
 
28
#include <KDebug>
 
29
 
 
30
QMap<QChar, QChar> whatToScape()
 
31
{
 
32
    //Only add those where we're not scaping the next character
 
33
    QMap<QChar, QChar> ret;
 
34
    ret['n']='\n';
 
35
    ret['r']='\r';
 
36
    ret['t']='\t';
 
37
    return ret;
 
38
}
 
39
 
 
40
QMap<QChar, QChar> CMakeFunctionArgument::scapings=whatToScape();
 
41
 
 
42
static QChar scapingChar='\\';
 
43
QString CMakeFunctionArgument::unescapeValue(const QString& value)
 
44
{
 
45
    int firstScape=value.indexOf(scapingChar);
 
46
    if (firstScape<0)
 
47
    {
 
48
        return value;
 
49
    }
 
50
    
 
51
    QString newValue;
 
52
    int last=0;
 
53
    for(int i=firstScape; i<value.size() && i>=0; i=value.indexOf(scapingChar, i+2))
 
54
    {
 
55
        newValue+=value.mid(last, i-last);
 
56
        QChar current=value[i+1];
 
57
        if(scapings.contains(current))
 
58
            newValue += scapings[current];
 
59
        else
 
60
            newValue += current;
 
61
 
 
62
        last=i+2;
 
63
    }
 
64
    newValue+=value.mid(last, value.size());
 
65
//     qDebug() << "escapiiiiiiiiing" << value << newValue;
 
66
    return newValue;
 
67
}
 
68
 
 
69
void CMakeFunctionDesc::addArguments( const QStringList& args )
 
70
{
 
71
    if(args.isEmpty())
 
72
    {
 
73
        CMakeFunctionArgument cmakeArg;
 
74
        arguments.append( cmakeArg );
 
75
    }
 
76
    else
 
77
    {
 
78
        foreach( const QString& arg, args )
 
79
        {
 
80
            CMakeFunctionArgument cmakeArg( arg );
 
81
            arguments.append( cmakeArg );
 
82
        }
 
83
    }
 
84
}
 
85
 
 
86
QString CMakeFunctionDesc::writeBack() const
 
87
{
 
88
    QString output=name+"( ";
 
89
    foreach(const CMakeFunctionArgument& arg, arguments)
 
90
    {
 
91
        QString o = arg.value;
 
92
        if(arg.quoted)
 
93
            o='"'+o+'"';
 
94
        output += o+' ';
 
95
    }
 
96
    output += ')';
 
97
    return output;
 
98
}
 
99
 
 
100
#if 0
 
101
enum RecursivityType { No, Yes, ElseIf, End };
 
102
 
 
103
RecursivityType recursivity(const QString& functionName)
 
104
{
 
105
    if(functionName.toUpper()=="IF" || functionName.toUpper()=="WHILE" ||
 
106
           functionName.toUpper()=="FOREACH" || functionName.toUpper()=="MACRO")
 
107
        return Yes;
 
108
    else if(functionName.toUpper()=="ELSE" || functionName.toUpper()=="ELSEIF")
 
109
        return ElseIf;
 
110
    else if(functionName.toUpper().startsWith("END"))
 
111
        return End;
 
112
    return No;
 
113
}
 
114
 
 
115
bool CMakeListsParser::parseCMakeFile( CMakeAst* root, const QString& fileName )
 
116
{
 
117
    if ( root == 0 )
 
118
        return false;
 
119
    cmListFileLexer* lexer = cmListFileLexer_New();
 
120
    if ( !lexer )
 
121
        return false;
 
122
    if ( !cmListFileLexer_SetFileName( lexer, qPrintable( fileName ) ) )
 
123
        return false;
 
124
 
 
125
    bool parseError = false;
 
126
    bool haveNewline = true;
 
127
    cmListFileLexer_Token* token;
 
128
    QStack<CMakeAst*> parent;
 
129
    QStack<IfAst*> parentIf;
 
130
    parent.push(root);
 
131
 
 
132
    while(!parseError && (token = cmListFileLexer_Scan(lexer)))
 
133
    {
 
134
        parseError=false;
 
135
        if(token->type == cmListFileLexer_Token_Newline)
 
136
        {
 
137
            parseError=false;
 
138
            haveNewline = true;
 
139
        }
 
140
        else if(token->type == cmListFileLexer_Token_Identifier)
 
141
        {
 
142
            if(haveNewline)
 
143
            {
 
144
                CMakeAst* currentParent=parent.top();
 
145
                haveNewline = false;
 
146
                CMakeFunctionDesc function;
 
147
                function.name = token->text;
 
148
                function.filePath = fileName;
 
149
                function.line = token->line;
 
150
 
 
151
                RecursivityType s=recursivity(function.name);
 
152
                if(s==End) { //NOTE: We're not checking if ENDIF(<<this>>) is ok.
 
153
                    parent.pop();
 
154
                    if(function.name.toUpper()=="ENDIF") {
 
155
                        parentIf.pop();
 
156
                    }
 
157
                    parseError = false;
 
158
                } else {
 
159
                    QString ifWorkaround; //FIXME: Please check it!
 
160
                    if(function.name.toUpper().startsWith("ELSE")) {
 
161
                        ifWorkaround = function.name;
 
162
                        function.name = "IF";
 
163
                    }
 
164
                    parseError = !parseCMakeFunction( lexer, function, fileName, currentParent );
 
165
 
 
166
                    if(parseError) {
 
167
                        kDebug(9032) << "Error while parsing:" << function.name;
 
168
                    }
 
169
 
 
170
                    if(!ifWorkaround.isEmpty())
 
171
                        function.name = ifWorkaround;
 
172
 
 
173
                    if(!parseError && s!=No) {
 
174
                        CMakeAst* lastCreated = currentParent->children().last();
 
175
//                         kDebug(9032) << "Lol:" << function.name << s;
 
176
 
 
177
                        if(function.name.toUpper()=="IF") {
 
178
                            parentIf.append(dynamic_cast<IfAst*>(lastCreated));
 
179
                        } else if(function.name.toUpper()=="ELSEIF") {
 
180
                            IfAst* ifast = parentIf.top(), *elseif=dynamic_cast<IfAst*>(lastCreated);
 
181
                            ifast->conditions().append(elseif->conditions()[0]);
 
182
                            lastCreated = ifast;
 
183
                        } else if(function.name.toUpper()=="ELSE") {
 
184
                            lastCreated = parentIf.top();
 
185
                        }
 
186
 
 
187
                        CMakeAst* child = new CMakeAst;
 
188
                        lastCreated->addChildAst(child);
 
189
                        switch(s) {
 
190
                            case Yes:
 
191
                                parent.push(child);
 
192
                                break;
 
193
                            case ElseIf: //For else and ifelse
 
194
                                parent.pop();
 
195
                                parent.push(child);
 
196
                                break;
 
197
                            case End:
 
198
                            case No:
 
199
                            default:
 
200
                                break;
 
201
                        }
 
202
                    }
 
203
                }
 
204
//                 kDebug(9032) << "Parsing:" << function.name << "depth:" << parent.count();
 
205
            }
 
206
        }
 
207
    }
 
208
 
 
209
    return parseError;
 
210
}
 
211
 
 
212
bool CMakeListsParser::parseCMakeFunction( cmListFileLexer* lexer,
 
213
                                           CMakeFunctionDesc& func,
 
214
                                           const QString& fileName, CMakeAst *parent )
 
215
{
 
216
    // Command name has already been parsed.  Read the left paren.
 
217
    cmListFileLexer_Token* token;
 
218
    if(!(token = cmListFileLexer_Scan(lexer)))
 
219
    {
 
220
        return false;
 
221
    }
 
222
    if(token->type != cmListFileLexer_Token_ParenLeft)
 
223
    {
 
224
        return false;
 
225
    }
 
226
 
 
227
    // Arguments.
 
228
    unsigned long lastLine = cmListFileLexer_GetCurrentLine(lexer);
 
229
    while((token = cmListFileLexer_Scan(lexer)))
 
230
    {
 
231
        if(token->type == cmListFileLexer_Token_ParenRight)
 
232
        {
 
233
            CMakeAst* newElement = AstFactory::self()->createAst(func.name);
 
234
            bool asMacro=false;
 
235
 
 
236
            if(!newElement)
 
237
            {
 
238
                asMacro=true;
 
239
                newElement = new MacroCallAst;
 
240
            }
 
241
            bool err = newElement->parseFunctionInfo(func);
 
242
            if(!err)
 
243
                kDebug(9032) << "error! found an error while reading" << func.name << "at" << func.line;
 
244
            parent->addChildAst(newElement);
 
245
//             kDebug(9032) << "Adding:" << func.name << newElement << "as macro :" << asMacro;
 
246
            
 
247
            return true;
 
248
            //FIXME: should return the parseFunctionInfo boolean return value, err
 
249
        }
 
250
        else if(token->type == cmListFileLexer_Token_Identifier ||
 
251
                token->type == cmListFileLexer_Token_ArgumentUnquoted)
 
252
        {
 
253
            CMakeFunctionArgument a( token->text, false, fileName, token->line );
 
254
            func.arguments << a;
 
255
        }
 
256
        else if(token->type == cmListFileLexer_Token_ArgumentQuoted)
 
257
        {
 
258
            CMakeFunctionArgument a( token->text, true, fileName, token->line );
 
259
            func.arguments << a;
 
260
        }
 
261
        else if(token->type != cmListFileLexer_Token_Newline)
 
262
        {
 
263
            return false;
 
264
        }
 
265
        lastLine = cmListFileLexer_GetCurrentLine(lexer);
 
266
    }
 
267
 
 
268
    return false;
 
269
 
 
270
}
 
271
#endif
 
272
 
 
273
CMakeFileContent CMakeListsParser::readCMakeFile(const QString & fileName)
 
274
{
 
275
    cmListFileLexer* lexer = cmListFileLexer_New();
 
276
    if ( !lexer )
 
277
        return CMakeFileContent();
 
278
    if ( !cmListFileLexer_SetFileName( lexer, qPrintable( fileName ) ) ) {
 
279
        kDebug(9042) << "cmake read error. could not read " << fileName;
 
280
        return CMakeFileContent();
 
281
    }
 
282
 
 
283
    CMakeFileContent ret;
 
284
    bool readError = false, haveNewline = true;
 
285
    cmListFileLexer_Token* token;
 
286
 
 
287
    while(!readError && (token = cmListFileLexer_Scan(lexer)))
 
288
    {
 
289
        readError=false;
 
290
        if(token->type == cmListFileLexer_Token_Newline)
 
291
        {
 
292
            readError=false;
 
293
            haveNewline = true;
 
294
        }
 
295
        else if(token->type == cmListFileLexer_Token_Identifier)
 
296
        {
 
297
            if(haveNewline)
 
298
            {
 
299
                haveNewline = false;
 
300
                CMakeFunctionDesc function;
 
301
                function.name = token->text;
 
302
                function.filePath = fileName;
 
303
                function.line = token->line;
 
304
                function.column = token->column;
 
305
 
 
306
                readError = !readCMakeFunction( lexer, function, fileName );
 
307
                ret.append(function);
 
308
 
 
309
                if(readError)
 
310
                {
 
311
                    kDebug(9032) << "Error while parsing:" << function.name << "at" << function.line;
 
312
                }
 
313
            }
 
314
        }
 
315
    }
 
316
 
 
317
    return ret;
 
318
}
 
319
 
 
320
bool CMakeListsParser::readCMakeFunction(cmListFileLexer *lexer, CMakeFunctionDesc &func, const QString & fileName)
 
321
{
 
322
        // Command name has already been parsed.  Read the left paren.
 
323
    cmListFileLexer_Token* token;
 
324
    if(!(token = cmListFileLexer_Scan(lexer)))
 
325
    {
 
326
        return false;
 
327
    }
 
328
    if(token->type != cmListFileLexer_Token_ParenLeft)
 
329
    {
 
330
        return false;
 
331
    }
 
332
 
 
333
    // Arguments.
 
334
    unsigned long lastLine = cmListFileLexer_GetCurrentLine(lexer);
 
335
    int parenthesis=1;
 
336
    while((token = cmListFileLexer_Scan(lexer)))
 
337
    {
 
338
        if(token->type == cmListFileLexer_Token_ParenRight)
 
339
        {
 
340
            parenthesis--;
 
341
            if(parenthesis==0) {
 
342
                func.endLine=token->line;
 
343
                func.endColumn=token->column;
 
344
                return true;
 
345
            } else if(parenthesis<0)
 
346
                 return false;
 
347
            else
 
348
            {
 
349
                CMakeFunctionArgument a( token->text, false, fileName, token->line, token->column );
 
350
                func.arguments << a;
 
351
            }
 
352
        }
 
353
        else if(token->type == cmListFileLexer_Token_ParenLeft)
 
354
        {
 
355
            parenthesis++;
 
356
            CMakeFunctionArgument a( token->text, false, fileName, token->line, token->column );
 
357
            func.arguments << a;
 
358
        }
 
359
        else if(token->type == cmListFileLexer_Token_Identifier ||
 
360
                token->type == cmListFileLexer_Token_ArgumentUnquoted)
 
361
        {
 
362
            CMakeFunctionArgument a( token->text, false, fileName, token->line, token->column );
 
363
            func.arguments << a;
 
364
        }
 
365
        else if(token->type == cmListFileLexer_Token_ArgumentQuoted)
 
366
        {
 
367
            CMakeFunctionArgument a( token->text, true, fileName, token->line, token->column+1 );
 
368
            func.arguments << a;
 
369
        }
 
370
        else if(token->type != cmListFileLexer_Token_Newline)
 
371
        {
 
372
            return false;
 
373
        }
 
374
        lastLine = cmListFileLexer_GetCurrentLine(lexer);
 
375
    }
 
376
 
 
377
    return false;
 
378
 
 
379
}
 
380
 
 
381
bool CMakeFunctionDesc::operator==(const CMakeFunctionDesc & other) const
 
382
{
 
383
    if(other.arguments.count()!=arguments.count() || name!=other.name)
 
384
        return false;
 
385
    
 
386
    QList<CMakeFunctionArgument>::const_iterator it=arguments.constBegin();
 
387
    QList<CMakeFunctionArgument>::const_iterator itOther=other.arguments.constBegin();
 
388
    for(;it!=arguments.constEnd(); ++it, ++itOther)
 
389
    {
 
390
        if(*it!=*itOther)
 
391
            return false;
 
392
    }
 
393
    return true;
 
394
}
 
395
 
 
396
 
 
397
/*CMakeFunctionArgument::CMakeFunctionArgument(const CMakeFunctionArgument & r)
 
398
    : value(r.value), quoted(r.quoted), filePath(r.filePath), line(r.line), column(r.column)
 
399
{
 
400
    value=unescapeValue(value);
 
401
}*/
 
402
 
 
403
CMakeFunctionArgument::CMakeFunctionArgument(const QString & v, bool q, const QString &, quint32 l, quint32 c)
 
404
    : value(v), quoted(q)/*, filePath(file)*/, line(l), column(c)
 
405
{
 
406
    value=unescapeValue(value);
 
407
}
 
408
 
 
409