1
/*
2
 * This file is part of the API Extractor project.
3
 *
4
 * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
5
 *
6
 * Contact: PySide team <contact@pyside.org>
7
 *
8
 * This program is free software; you can redistribute it and/or
9
 * modify it under the terms of the GNU General Public License
10
 * version 2 as published by the Free Software Foundation.
11
 *
12
 * This program is distributed in the hope that it will be useful, but
13
 * WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15
 * General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU General Public License
18
 * along with this program; if not, write to the Free Software
19
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20
 * 02110-1301 USA
21
 *
22
 */
23
24
#include "typeparser.h"
25
26
#include <QtCore/QDebug>
27
#include <QtCore/QStack>
28
29
class Scanner
30
{
31
public:
32
    enum Token {
33
        StarToken,
34
        AmpersandToken,
35
        LessThanToken,
36
        ColonToken,
37
        CommaToken,
38
        OpenParenToken,
39
        CloseParenToken,
40
        SquareBegin,
41
        SquareEnd,
42
        GreaterThanToken,
43
44
        ConstToken,
45
        Identifier,
46
        NoToken
47
    };
48
49
    Scanner(const QString &s)
50
            : m_pos(0), m_length(s.length()), m_chars(s.constData())
51
    {
52
    }
53
54
    Token nextToken();
55
    QString identifier() const;
56
57
private:
58
    int m_pos;
59
    int m_length;
60
    int m_tokenStart;
61
    const QChar *m_chars;
62
};
63
64
QString Scanner::identifier() const
65
{
66
    return QString(m_chars + m_tokenStart, m_pos - m_tokenStart);
67
}
68
69
Scanner::Token Scanner::nextToken()
70
{
71
    Token tok = NoToken;
72
73
    // remove whitespace
74
    while (m_pos < m_length && m_chars[m_pos] == ' ')
75
        ++m_pos;
76
77
    m_tokenStart = m_pos;
78
79
    while (m_pos < m_length) {
80
81
        const QChar &c = m_chars[m_pos];
82
83
        if (tok == NoToken) {
84
            switch (c.toLatin1()) {
85
            case '*': tok = StarToken; break;
86
            case '&': tok = AmpersandToken; break;
87
            case '<': tok = LessThanToken; break;
88
            case '>': tok = GreaterThanToken; break;
89
            case ',': tok = CommaToken; break;
90
            case '(': tok = OpenParenToken; break;
91
            case ')': tok = CloseParenToken; break;
92
            case '[': tok = SquareBegin; break;
93
            case ']' : tok = SquareEnd; break;
94
            case ':':
95
                tok = ColonToken;
96
                Q_ASSERT(m_pos + 1 < m_length);
97
                ++m_pos;
98
                break;
99
            default:
100
                if (c.isLetterOrNumber() || c == '_')
101
                    tok = Identifier;
102
                else
103
                    qFatal("Unrecognized character in lexer: %c", c.toLatin1());
104
                break;
105
            }
106
        }
107
108
        if (tok <= GreaterThanToken) {
109
            ++m_pos;
110
            break;
111
        }
112
113
        if (tok == Identifier) {
114
            if (c.isLetterOrNumber() || c == '_')
115
                ++m_pos;
116
            else
117
                break;
118
        }
119
    }
120
121
    if (tok == Identifier && m_pos - m_tokenStart == 5) {
122
        if (m_chars[m_tokenStart] == 'c'
123
            && m_chars[m_tokenStart + 1] == 'o'
124
            && m_chars[m_tokenStart + 2] == 'n'
125
            && m_chars[m_tokenStart + 3] == 's'
126
            && m_chars[m_tokenStart + 4] == 't')
127
            tok = ConstToken;
128
    }
129
130
    return tok;
131
132
}
133
134
TypeParser::Info TypeParser::parse(const QString &str)
135
{
136
    Scanner scanner(str);
137
138
    Info info;
139
    QStack<Info *> stack;
140
    stack.push(&info);
141
142
    bool colon_prefix = false;
143
    bool in_array = false;
144
    QString array;
145
146
    Scanner::Token tok = scanner.nextToken();
147
    while (tok != Scanner::NoToken) {
148
149
//         switch (tok) {
150
//         case Scanner::StarToken: printf(" - *\n"); break;
151
//         case Scanner::AmpersandToken: printf(" - &\n"); break;
152
//         case Scanner::LessThanToken: printf(" - <\n"); break;
153
//         case Scanner::GreaterThanToken: printf(" - >\n"); break;
154
//         case Scanner::ColonToken: printf(" - ::\n"); break;
155
//         case Scanner::CommaToken: printf(" - ,\n"); break;
156
//         case Scanner::ConstToken: printf(" - const\n"); break;
157
//         case Scanner::SquareBegin: printf(" - [\n"); break;
158
//         case Scanner::SquareEnd: printf(" - ]\n"); break;
159
//         case Scanner::Identifier: printf(" - '%s'\n", qPrintable(scanner.identifier())); break;
160
//         default:
161
//             break;
162
//         }
163
164
        switch (tok) {
165
166
        case Scanner::StarToken:
167
            ++stack.top()->indirections;
168
            break;
169
170
        case Scanner::AmpersandToken:
171
            stack.top()->is_reference = true;
172
            break;
173
174
        case Scanner::LessThanToken:
175
            stack.top()->template_instantiations << Info();
176
            stack.push(&stack.top()->template_instantiations.last());
177
            break;
178
179
        case Scanner::CommaToken:
180
            stack.pop();
181
            stack.top()->template_instantiations << Info();
182
            stack.push(&stack.top()->template_instantiations.last());
183
            break;
184
185
        case Scanner::GreaterThanToken:
186
            stack.pop();
187
            break;
188
189
        case Scanner::ColonToken:
190
            colon_prefix = true;
191
            break;
192
193
        case Scanner::ConstToken:
194
            stack.top()->is_constant = true;
195
            break;
196
197
        case Scanner::OpenParenToken: // function pointers not supported
198
        case Scanner::CloseParenToken: {
199
            Info i;
200
            i.is_busted = true;
201
            return i;
202
        }
203
204
205
        case Scanner::Identifier:
206
            if (in_array) {
207
                array = scanner.identifier();
208
            } else if (colon_prefix || stack.top()->qualified_name.isEmpty()) {
209
                stack.top()->qualified_name << scanner.identifier();
210
                colon_prefix = false;
211
            } else {
212
                stack.top()->qualified_name.last().append(" " + scanner.identifier());
213
            }
214
            break;
215
216
        case Scanner::SquareBegin:
217
            in_array = true;
218
            break;
219
220
        case Scanner::SquareEnd:
221
            in_array = false;
222
            stack.top()->arrays += array;
223
            break;
224
225
226
        default:
227
            break;
228
        }
229
230
        tok = scanner.nextToken();
231
    }
232
233
    return info;
234
}
235
236
QString TypeParser::Info::instantiationName() const
237
{
238
    QString s(qualified_name.join("::"));
239
    if (!template_instantiations.isEmpty()) {
240
        QStringList insts;
241
        foreach (Info info, template_instantiations)
242
            insts << info.toString();
243
        s += QString("< %1 >").arg(insts.join(", "));
244
    }
245
246
    return s;
247
}
248
249
QString TypeParser::Info::toString() const
250
{
251
    QString s;
252
253
    if (is_constant) s += "const ";
254
    s += instantiationName();
255
    for (int i = 0; i < arrays.size(); ++i)
256
        s += "[" + arrays.at(i) + "]";
257
    s += QString(indirections, '*');
258
    if (is_reference)  s += '&';
259
260
    return s;
261
}