1 #include "CppBase.h"
2 #include "Internal.h"
3
4 namespace Upp {
5
6 #define LTIMING(x) // DTIMING(x)
7 #define LLOG(x) // DLOG(x)
8
ParamAdd(Vector<String> & param,const char * s,const char * e)9 void Cpp::ParamAdd(Vector<String>& param, const char *s, const char *e)
10 {
11 while(s < e && (byte)*s <= ' ') s++;
12 while(e > s && (byte)*(e - 1) <= ' ') e--;
13 String h;
14 while(s < e) {
15 if((byte)*s <= ' ') {
16 h.Cat(' ');
17 s++;
18 while(s < e && (byte)*s <= ' ')
19 s++;
20 }
21 else
22 if(*s == '\"' || *s == '\'') {
23 const char *q = SkipString(s);
24 h.Cat(String(s, q));
25 s = q;
26 }
27 else
28 h.Cat(*s++);
29 }
30 param.Add(h);
31 }
32
Expand(const char * s)33 String Cpp::Expand(const char *s)
34 {
35 StringBuffer r;
36 const char *l0 = s;
37 while(*s) {
38 if(incomment) {
39 if(s[0] == '*' && s[1] == '/') {
40 incomment = false;
41 s += 2;
42 r.Cat("*/");
43 }
44 else
45 r.Cat(*s++);
46 }
47 else
48 if(iscib(*s)) {
49 LTIMING("Expand ID");
50 const char *bid = s;
51 s++;
52 while(iscid(*s))
53 s++;
54 String id(bid, s);
55 LTIMING("Expand ID2");
56 if(notmacro.Find(id) < 0) {
57 const PPMacro *pp = macro.FindLastPtr(id);
58 int segmenti = pp ? segment_id.Find(pp->segment_id) : -1;
59 const CppMacro *m = FindMacro(id, segment_id, segmenti);
60 if(!m && pp)
61 m = &pp->macro;
62 if(m && m->IsUndef())
63 m = NULL;
64 if(m) {
65 LTIMING("Expand macro");
66 Vector<String> param;
67 bool function_like = false;
68 const char *s0 = s;
69 if(m->param.GetCount()) {
70 while(*s && (byte)*s <= ' ')
71 s++;
72 if(*s == '(') {
73 function_like = true;
74 s++;
75 const char *b = s;
76 int level = 0;
77 for(;;)
78 if(*s == ',' && level == 0) {
79 ParamAdd(param, b, s);
80 s++;
81 b = s;
82 }
83 else
84 if(*s == ')') {
85 s++;
86 if(level == 0) {
87 ParamAdd(param, b, s - 1);
88 break;
89 }
90 level--;
91 }
92 else
93 if(*s == '(') {
94 s++;
95 level++;
96 }
97 else
98 if(*s == '\0') { // macro use spread into more lines
99 if(bid == l0) // begin of line
100 prefix_macro = bid;
101 else
102 prefix_macro = String(' ', 1) + bid; // do not want to emit grounding in body
103 return String(r);
104 }
105 else
106 if(*s == '\"' || *s == '\'')
107 s = SkipString(s);
108 else
109 s++;
110 }
111 }
112 if(!!m->param.GetCount() == function_like) {
113 int ti = notmacro.GetCount();
114 Vector<String> eparam;
115 eparam.SetCount(param.GetCount());
116 for(int i = 0; i < param.GetCount(); i++)
117 eparam[i] = Expand(param[i]);
118 notmacro.Add(id);
119 id = '\x1f' + Expand(m->Expand(param, eparam)); // \x1f is info for Pre that there was a macro expansion
120 notmacro.Trim(ti);
121 }
122 else
123 s = s0;
124 }
125 else
126 notmacro.Add(id);
127 }
128 r.Cat(id);
129 }
130 else
131 if(*s == '\"') {
132 const char *e = SkipString(s);
133 r.Cat(s, e);
134 s = e;
135 }
136 else
137 if(s[0] == '/' && s[1] == '*') {
138 incomment = true;
139 s += 2;
140 r.Cat("/*");
141 }
142 else
143 if(s[0] == '/' && s[1] == '/') {
144 r.Cat(s);
145 break;
146 }
147 else
148 r.Cat(*s++);
149 }
150 return String(r);
151 }
152
DoFlatInclude(const String & header_path)153 void Cpp::DoFlatInclude(const String& header_path)
154 {
155 LTIMING("DoFlatInclude");
156 LLOG("Flat include " << header_path);
157 if(header_path.GetCount()) {
158 md5.Put(header_path);
159 const FlatPP& pp = GetFlatPPFile(header_path);
160 LLOG("DoFlatInclude " << header_path << ", " << pp.segment_id.GetCount() << " segments");
161 for(int i = 0; i < pp.segment_id.GetCount(); i++)
162 segment_id.FindAdd(pp.segment_id[i]);
163 for(int i = 0; i < pp.usings.GetCount(); i++) {
164 namespace_using.FindAdd(pp.usings[i]);
165 LLOG(" Flat usings " << pp.usings[i]);
166 md5.Put('$');
167 md5.Put(pp.usings[i]);
168 }
169 }
170 }
171
172 #define IGNORE_ELSE
173
Do(const String & sourcefile,Stream & in,const String & currentfile,bool get_macros)174 void Cpp::Do(const String& sourcefile, Stream& in, const String& currentfile, bool get_macros)
175 {
176 LLOG("Cpp::Do " << sourcefile << ", current: " << currentfile);
177 if(visited.Find(currentfile) >= 0 || visited.GetCount() > 20000)
178 return;
179 visited.Add(currentfile);
180 String current_folder = GetFileFolder(currentfile);
181 bool notthefile = sourcefile != currentfile;
182 if(notthefile || get_macros) {
183 LTIMING("DO2");
184 const PPFile& pp = GetPPFile(currentfile);
185 for(int i = 0; i < pp.item.GetCount() && !done; i++) {
186 const PPItem& m = pp.item[i];
187 if(m.type == PP_DEFINES) {
188 LTIMING("PP_DEFINES");
189 if(notthefile) // if getting macros, we are interested in included macros only
190 segment_id.FindAdd(m.segment_id);
191 }
192 else
193 if(m.type == PP_INCLUDE) {
194 String s = GetIncludePath(m.text, current_folder);
195 if(s.GetCount()) {
196 if(notthefile && (s == sourcefile || IncludesFile(s, sourcefile))) {
197 LLOG("Include IN " << s);
198 md5.Put(s);
199 Do(sourcefile, in, s, get_macros);
200 }
201 else {
202 LLOG("Include FLAT " << s);
203 DoFlatInclude(s);
204 }
205 }
206 }
207 else
208 if(m.type == PP_NAMESPACE) {
209 namespace_stack.Add(m.text);
210 LLOG("pp namespace " << m.text << " " << namespace_stack);
211 }
212 else
213 if(m.type == PP_NAMESPACE_END && namespace_stack.GetCount()) {
214 namespace_stack.Drop();
215 LLOG("pp end namespace " << namespace_stack);
216 }
217 else
218 if(m.type == PP_USING) {
219 namespace_using.FindAdd(m.text);
220 md5.Put('$');
221 md5.Put(m.text);
222 }
223 }
224 if(sourcefile != currentfile)
225 return;
226 }
227
228 md5.Put('@');
229 md5.Put(Join(namespace_stack, ";"));
230
231 done = true;
232
233 if(get_macros)
234 return;
235
236 if(in.Peek() == 0xef) { // Skip UTF-8 BOM
237 int64 pos = in.GetPos();
238 in.Get();
239 if(in.Get() != 0xbb || in.Get() != 0xbf)
240 in.Seek(pos); // Was not UTF-8 BOM after all
241 }
242
243 LTIMING("Expand");
244 incomment = false;
245 prefix_macro.Clear();
246 StringBuffer result;
247 result.Clear();
248 result.Reserve(16384);
249 int lineno = 0;
250 bool incomment = false;
251 bool do_pp = true;
252 int segment_serial = 0;
253 segment_id.Add(--segment_serial);
254 #ifdef IGNORE_ELSE
255 int ignore_else = 0;
256 #endif
257 while(!in.IsEof()) {
258 String l = prefix_macro + in.GetLine();
259 String ll = TrimLeft(l);
260 if(ll.StartsWith("//$")) { // Do not remove assist++ parser directives
261 result.Cat(l + "\n");
262 do_pp = decode(ll[3], '+', true, '-', false, do_pp);
263 continue;
264 }
265 prefix_macro.Clear();
266 lineno++;
267 int el = 0;
268 while(*l.Last() == '\\' && !in.IsEof()) {
269 el++;
270 l.Trim(l.GetLength() - 1);
271 l.Cat(in.GetLine());
272 }
273 RemoveComments(l, incomment);
274 CParser p(l);
275 if(p.Char('#')) {
276 if(do_pp) {
277 if(p.Id("define")) {
278 result.Cat(l + "\n");
279 CppMacro m;
280 String id = m.Define(p.GetPtr());
281 if(id.GetCount()) {
282 PPMacro& pp = macro.Add(id);
283 pp.macro = m;
284 pp.segment_id = segment_serial;
285 notmacro.Trim(kw.GetCount());
286 }
287 }
288 else
289 if(p.Id("undef")) {
290 result.Cat(l + "\n");
291 if(p.IsId()) {
292 segment_id.Add(--segment_serial);
293 PPMacro& m = macro.Add(p.ReadId());
294 m.segment_id = segment_serial;
295 m.macro.SetUndef();
296 notmacro.Trim(kw.GetCount());
297 segment_id.Add(--segment_serial);
298 }
299 }
300 else {
301 result.Cat('\n');
302 #ifdef IGNORE_ELSE
303 if(ignore_else) {
304 if(p.Id("if") || p.Id("ifdef") || p.Id("ifndef"))
305 ignore_else++;
306 else
307 if(p.Id("endif"))
308 ignore_else--;
309 }
310 else {
311 if(p.Id("else") || p.Id("elif"))
312 ignore_else = 1;
313 }
314 #endif
315 if(p.Id("include")) {
316 LTIMING("Expand include");
317 String s = GetIncludePath(p.GetPtr(), current_folder);
318 DoFlatInclude(s);
319 segment_id.Add(--segment_serial);
320 }
321 }
322 }
323 else
324 result.Cat('\n');
325 }
326 else {
327 LTIMING("Expand expand");
328 #ifdef IGNORE_ELSE
329 if(ignore_else)
330 result.Cat('\n');
331 else
332 #endif
333 result.Cat(Expand(l) + "\n");
334 }
335 while(el--)
336 result.Cat("\n");
337 }
338 output = result;
339 }
340
341 Index<String> Cpp::kw;
342
Preprocess(const String & sourcefile,Stream & in,const String & currentfile,bool get_macros)343 bool Cpp::Preprocess(const String& sourcefile, Stream& in, const String& currentfile,
344 bool get_macros)
345 {
346 LLOG("===== Preprocess " << sourcefile << " <- " << currentfile);
347 LTIMING("Cpp::Preprocess");
348 macro.Clear();
349 macro.Reserve(1000);
350 segment_id.Clear();
351 segment_id.Add(0);
352 segment_id.Reserve(100);
353
354 ONCELOCK {
355 const char **h = CppKeyword();
356 while(*h) {
357 kw.Add(*h);
358 h++;
359 }
360 }
361 notmacro = clone(kw);
362
363 done = false;
364 incomment = false;
365 output.Clear();
366 visited.Clear();
367 Do(NormalizePath(sourcefile), in, NormalizePath(currentfile), get_macros);
368 if(!done)
369 output.Clear();
370 return done;
371 }
372
373 void GetAllMacros(Md5Stream& md5, const String& id, Index<int>& segment_id);
374
GetDependeciesMd5(const Vector<String> & m)375 String Cpp::GetDependeciesMd5(const Vector<String>& m)
376 {
377 String r;
378 Md5Stream md5;
379 md5.Put(Join(namespace_stack, ";"));
380 md5.Put('@');
381 md5.Put(Join(namespace_using.GetKeys(), ";"));
382 md5.Put('@');
383 for(int i = 0; i < m.GetCount(); i++)
384 GetAllMacros(md5, m[i], segment_id);
385 return md5.FinishString();
386 }
387
388 }
389