1 /*
2 * Copyright 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 #include "Parser.h"
17 
18 #include <vector>
19 
20 #define WHITESPACE " \t\n"
21 
22 // Parse the |input| string as a list of type-specific tokens.
23 // This tokenizes the input, using whitespace as separators and '*' as
24 // a single token too. On success, return true and sets |*out| to the
25 // list of tokens. On failure, return false.
26 //
27 // Example: 'const char**foo' -> ['const', 'char', '*', '*', 'foo']
28 //
parseTypeTokens(const std::string & input,std::vector<std::string> * out,std::string * error)29 static bool parseTypeTokens(const std::string& input,
30                             std::vector<std::string>* out,
31                             std::string* error) {
32     out->clear();
33     size_t pos = 0U;
34 
35     // Parse all tokens in the input, treat '*' as a single token.
36     // I.e.
37     for (;;) {
38         // skip leading whitespace.
39         pos = input.find_first_not_of(WHITESPACE, pos);
40         if (pos == std::string::npos) {
41             break;  // end of parse.
42         }
43 
44         // If this is a star, ensure it follows a type name.
45         // otherwise treat it as part of the final type.
46         if (input[pos] == '*') {
47             out->push_back(std::string("*"));
48             pos += 1U;
49             continue;
50         }
51 
52         // find end of type/token.
53         size_t end = input.find_first_of(WHITESPACE "*", pos);
54         if (end == std::string::npos) {
55             end = input.size();
56         }
57 
58         std::string str = input.substr(pos, end - pos);
59         if (str.size() == 0) {
60             // Sanity check: should not happen.
61             if (error != NULL) {
62                 *error = "Unexpected empty token !?";
63             }
64             return false;
65         }
66 
67         out->push_back(str);
68         pos = end;
69     }
70 
71     if (error != NULL) {
72         // Sanity check: require non-empty input
73         if (out->empty()) {
74             *error = "Empty parameter declaration!";
75             return false;
76         }
77 
78         // Sanity check: There must be base type name before any '*'
79         for (size_t n = 0; n < out->size(); ++n) {
80             std::string& token = (*out)[n];
81             if (token == "*") {
82                 *error = "Unexpected '*' before type name";
83                 return false;
84             } else if (token != "const") {
85                 break;
86             }
87         }
88     }
89 
90     return true;
91 }
92 
93 // Given |tokens|, an input vector of strings, join the first |count| items
94 // into a normalized type string, and return it.
buildTypeString(const std::vector<std::string> & tokens,size_t count)95 static std::string buildTypeString(const std::vector<std::string>& tokens,
96                                    size_t count) {
97     std::string result;
98 
99     for (size_t n = 0; n < count; ++n) {
100         const std::string& token = tokens[n];
101         if (n > 0 && token != "*") {
102             result.append(" ");
103         }
104         result.append(token);
105     }
106     return result;
107 }
108 
109 
normalizeTypeDeclaration(const std::string & input)110 std::string normalizeTypeDeclaration(const std::string& input) {
111     std::vector<std::string> tokens;
112     if (!parseTypeTokens(input, &tokens, NULL)) {
113         return "";
114     }
115     return buildTypeString(tokens, tokens.size());
116 }
117 
parseTypeDeclaration(const std::string & input,std::string * typeName,std::string * error)118 bool parseTypeDeclaration(const std::string& input,
119                           std::string* typeName,
120                           std::string* error) {
121     // The type name can be made of several tokens, e.g. 'unsigned int'
122     // use an array to store them, and a count variable. Each item can be
123     // one of '*', 'const' or a type name component (e.g. 'struct', 'unsigned')
124     std::vector<std::string> tokens;
125 
126     if (!parseTypeTokens(input, &tokens, error)) {
127         return false;
128     }
129 
130     // Sanity check, there must be a least one non-special tokens.
131     size_t nonSpecialCount = 0;
132     for (size_t n = 0; n < tokens.size(); ++n) {
133         if (tokens[n] != "*" && tokens[n] != "const") {
134             nonSpecialCount++;
135         }
136     }
137     if (nonSpecialCount == 0) {
138         *error = "Missing type name";
139         return false;
140     }
141     // Build the type name from all tokens before it.
142     *typeName = buildTypeString(tokens, tokens.size());
143     return true;
144 }
145 
146 
parseParameterDeclaration(const std::string & param,std::string * typeName,std::string * variableName,std::string * error)147 bool parseParameterDeclaration(const std::string& param,
148                                std::string* typeName,
149                                std::string* variableName,
150                                std::string* error) {
151     std::vector<std::string> tokens;
152 
153     if (!parseTypeTokens(param, &tokens, error)) {
154         return false;
155     }
156 
157     // Sanity check, there must be a least two non-special tokens.
158     size_t nonSpecialCount = 0;
159     for (size_t n = 0; n < tokens.size(); ++n) {
160         if (tokens[n] != "*" && tokens[n] != "const") {
161             nonSpecialCount++;
162         }
163     }
164     if (nonSpecialCount == 0) {
165         *error = "Missing type name";
166         return false;
167     }
168     if (nonSpecialCount == 1) {
169         *error = "Missing variable name";
170         return false;
171     }
172 
173     // Sanity check: variable name must not be followed by 'const' or '*'
174     const std::string& lastToken = tokens[tokens.size() - 1U];
175     if (lastToken == "*") {
176         *error = "Extra '*' after variable name";
177         return false;
178     }
179     if (lastToken == "const") {
180         *error = "Extra 'const' after variable name";
181         return false;
182     }
183 
184     // Extract the variable name as the last token.
185     if (variableName) {
186         *variableName = lastToken;
187     }
188     // Build the type name from all tokens before it.
189     *typeName = buildTypeString(tokens, tokens.size() - 1U);
190     return true;
191 }
192