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