1 /**
2 	Parses DMD JSON output and builds up a documentation syntax tree (JSON format from DMD 2.063.2).
3 
4 	Copyright: © 2012-2015 RejectedSoftware e.K.
5 	License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file.
6 	Authors: Sönke Ludwig
7 */
8 module ddox.parsers.jsonparser;
9 
10 import ddox.ddox;
11 import ddox.entities;
12 
13 import std.algorithm;
14 import std.conv;
15 import std.exception;
16 import std.range;
17 import std.stdio;
18 import std..string;
19 import std.typecons;
20 import core.demangle;
21 import vibe.core.log;
22 import vibe.data.json;
23 
24 
25 Package parseJsonDocs(Json json, Package root = null)
26 {
27 	if( !root ) root = new Package(null, null);
28 	Parser p;
29 	foreach( mod; json ){
30 		p.parseModule(mod, root);
31 	}
32 	p.resolveTypes(root);
33 	return root;
34 }
35 
36 private struct Parser
37 {
38 	private Tuple!(Type, Entity)[] m_primTypes;
39 	private Declaration[string] m_typeMap;
40 
41 	void resolveTypes(Package root)
42 	{
43 		bool isTypeDecl(Declaration a)
44 		{
45 			switch(a.kind){
46 				default: return false;
47 				case DeclarationKind.Struct:
48 				case DeclarationKind.Union:
49 				case DeclarationKind.Class:
50 				case DeclarationKind.Interface:
51 				case DeclarationKind.Enum:
52 					return true;
53 				case DeclarationKind.Alias:
54 					return (cast(AliasDeclaration)a).targetType !is null;
55 				case DeclarationKind.TemplateParameter:
56 					return true;
57 				case DeclarationKind.Template:
58 					// support eponymous template types
59 					auto td = cast(TemplateDeclaration)a;
60 					// be optimistic for templates without content that they are in fact types
61 					if (!td.members.length) return true;
62 					// otherwise require an actual eponymous type member
63 					auto mi = td.members.countUntil!(m => m.name == a.name);
64 					return mi >= 0 && isTypeDecl(td.members[mi]);
65 			}
66 		}
67 
68 		foreach (t; m_primTypes) {
69 			Declaration decl;
70 			if (t[0].typeName.length) decl = t[1].lookup!Declaration(t[0].typeName);
71 			if (!decl || !isTypeDecl(decl)) {
72 				auto pd = t[0].typeName in m_typeMap;
73 				if (pd) decl = *pd;
74 			}
75 			if (decl && isTypeDecl(decl))
76 				t[0].typeDecl = decl;
77 		}
78 
79 		// fixup class bases
80 		root.visit!ClassDeclaration((decl){
81 			if( decl.baseClass && decl.baseClass.typeDecl && !cast(ClassDeclaration)decl.baseClass.typeDecl )
82 				decl.baseClass = null;
83 			foreach( i; decl.derivedInterfaces )
84 				if( i.typeDecl && !cast(InterfaceDeclaration)i.typeDecl )
85 					i.typeDecl = null;
86 			assert(decl);
87 		});
88 
89 		// fixup interface bases
90 		root.visit!InterfaceDeclaration((decl){
91 			foreach( i; decl.derivedInterfaces )
92 				if( i.typeDecl && !cast(InterfaceDeclaration)i.typeDecl )
93 					i.typeDecl = null;
94 			assert(decl);
95 		});
96 	}
97 
98 	void parseModule(Json json, Package root_package)
99 	{
100 		Module mod;
101 		if( "name" !in json ){
102 			logError("No name attribute in module %s - ignoring", json.filename.opt!string);
103 			return;
104 		}
105 		auto path = json.name.get!string.split(".");
106 		Package p = root_package;
107 		foreach( i, pe; path ){
108 			if( i+1 < path.length ) p = p.getOrAddPackage(pe);
109 			else mod = p.createModule(pe);
110 		}
111 
112 		mod.file = json.file.get!string;
113 		mod.docGroup = new DocGroup(mod, json.comment.opt!string());
114 		mod.members = parseDeclList(json.members, mod);
115 	}
116 
117 	Declaration[] parseDeclList(Json json, Entity parent)
118 	{
119 		if( json.type == Json.Type.Undefined ) return null;
120 		DocGroup lastdoc;
121 		Declaration[] ret;
122 		foreach( mem; json ){
123 			auto decl = parseDecl(mem, parent);
124 			if( !decl ) continue;
125 			auto doc = decl.docGroup;
126 			if( lastdoc && (doc.text == lastdoc.text && doc.text.length || doc.comment.isDitto) ){
127 				lastdoc.members ~= decl;
128 				decl.docGroup = lastdoc;
129 			} else if( doc.comment.isPrivate ){
130 				decl.protection = Protection.Private;
131 				lastdoc = null;
132 			} else lastdoc = decl.docGroup;
133 			ret ~= decl;
134 		}
135 		return ret;
136 	}
137 
138 	Declaration parseDecl(Json json, Entity parent)
139 	{
140 		Declaration ret;
141 
142 		// DMD outputs templates with the wrong kind sometimes
143 		if (json.name.get!string().canFind('(') && json.kind != "mixin") {
144 			ret = parseTemplateDecl(json, parent);
145 		} else {
146 			switch( json.kind.get!string ){
147 				default:
148 					logWarn("Unknown declaration kind: %s", json.kind.get!string);
149 					return null;
150 				case "import":
151 				case "static import":
152 					// TODO: use for symbol resolving
153 					return null;
154 				case "destructor": return null;
155 				case "mixin": return null;
156 				case "alias":
157 					ret = parseAliasDecl(json, parent);
158 					break;
159 				case "function":
160 				case "allocator":
161 				case "deallocator":
162 				case "constructor":
163 					ret = parseFunctionDecl(json, parent);
164 					break;
165 				case "enum":
166 					ret = parseEnumDecl(json, parent);
167 					break;
168 				case "enum member":
169 					ret = parseEnumMemberDecl(json, parent);
170 					break;
171 				case "struct":
172 				case "union":
173 				case "class":
174 				case "interface":
175 					ret = parseCompositeDecl(json, parent);
176 					break;
177 				case "variable":
178 					ret = parseVariableDecl(json, parent);
179 					break;
180 				case "template":
181 					ret = parseTemplateDecl(json, parent);
182 					break;
183 			}
184 		}
185 
186 		ret.protection = parseProtection(json.protection);
187 		ret.line = json["line"].opt!int;
188 		ret.docGroup = new DocGroup(ret, json.comment.opt!string());
189 
190 		return ret;
191 	}
192 
193 	auto parseAliasDecl(Json json, Entity parent)
194 	{
195 		auto ret = new AliasDeclaration(parent, json.name.get!string);
196 		ret.attributes = json.storageClass.opt!(Json[]).map!(j => j.get!string).array;
197 		ret.targetType = parseType(json, ret, null);
198 		if( ret.targetType && ret.targetType.kind == TypeKind.Primitive && ret.targetType.typeName.length == 0 )
199 			ret.targetType = null;
200 		insertIntoTypeMap(ret);
201 		return ret;
202 	}
203 
204 	auto parseFunctionDecl(Json json, Entity parent)
205 	{
206 		auto ret = new FunctionDeclaration(parent, json.name.opt!string);
207 		ret.type = parseType(json, ret, "void()");
208 		assert(ret.type !is null);
209 		// TODO: use "storageClass" and "parameters" fields
210 		if( ret.type.kind == TypeKind.Function ){
211 			ret.returnType = ret.type.returnType;
212 			assert(ret.returnType !is null);
213 			ret.attributes = ret.type.attributes ~ ret.type.modifiers;
214 			if (auto psc = "storageClass" in json)
215 				foreach (sc; *psc)
216 					if (!ret.attributes.canFind(sc.get!string))
217 						ret.attributes ~= sc.get!string;
218 
219 			auto params = json.parameters.opt!(Json[]);
220 			if (!params) {
221 				params.length = ret.type.parameterTypes.length;
222 				foreach (i, pt; ret.type.parameterTypes) {
223 					auto jp = Json.emptyObject;
224 					jp.name = ret.type._parameterNames[i];
225 					jp["type"] = pt.text;
226 					if (ret.type._parameterDefaultValues[i])
227 						jp["default"] = ret.type._parameterDefaultValues[i].valueString;
228 					params[i] = jp;
229 				}
230 			}
231 
232 			foreach (i, p; params) {
233 				auto pname = p.name.opt!string;
234 				auto decl = new VariableDeclaration(ret, pname);
235 				foreach (sc; p["storageClass"].opt!(Json[]))
236 					if (!decl.attributes.canFind(sc.get!string))
237 						decl.attributes ~= sc.get!string;
238 				decl.type = parseType(p, ret);
239 				if (auto pdv = "default" in p)
240 					decl.initializer = parseValue(pdv.opt!string);
241 				ret.parameters ~= decl;
242 			}
243 		} else {
244 			logError("Expected function type for '%s'/'%s', got %s %s", json["type"].opt!string, demangleType(json["deco"].opt!string), ret.type.kind, ret.type.typeName);
245 		}
246 		return ret;
247 	}
248 
249 	auto parseEnumDecl(Json json, Entity parent)
250 	{
251 		auto ret = new EnumDeclaration(parent, json.name.get!string);
252 		insertIntoTypeMap(ret);
253 		if( "base" !in json ){ // FIXME: parse deco instead
254 			if( auto pd = "baseDeco" in json )
255 				json.base = demanglePrettyType(pd.get!string());
256 		}
257 		ret.baseType = parseType(json.base, ret);
258 		auto mems = parseDeclList(json.members, ret);
259 		foreach( m; mems ){
260 			auto em = cast(EnumMemberDeclaration)m;
261 			assert(em !is null, "Enum containing non-enum-members?");
262 			ret.members ~= em;
263 		}
264 		return ret;
265 	}
266 
267 	auto parseEnumMemberDecl(Json json, Entity parent)
268 	{
269 		auto ret = new EnumMemberDeclaration(parent, json.name.get!string);
270 		if (json.value.opt!string.length)
271 			ret.value = parseValue(json.value.opt!string);
272 		return ret;
273 	}
274 
275 	auto parseCompositeDecl(Json json, Entity parent)
276 	{
277 		CompositeTypeDeclaration ret;
278 		switch(json.kind.get!string){
279 			default:
280 				logWarn("Invalid composite decl kind: %s", json.kind.get!string);
281 				return new StructDeclaration(parent, json.name.get!string);
282 			case "struct":
283 				ret = new StructDeclaration(parent, json.name.get!string);
284 				break;
285 			case "union":
286 				ret = new UnionDeclaration(parent, json.name.get!string);
287 				break;
288 			case "class":
289 				auto clsdecl = new ClassDeclaration(parent, json.name.get!string);
290 				if( clsdecl.qualifiedName != "object.Object" )
291 					clsdecl.baseClass = parseType(json.base, clsdecl, "Object", false);
292 				foreach( intf; json.interfaces.opt!(Json[]) )
293 					clsdecl.derivedInterfaces ~= parseType(intf, clsdecl);
294 				ret = clsdecl;
295 				break;
296 			case "interface":
297 				auto intfdecl = new InterfaceDeclaration(parent, json.name.get!string);
298 				foreach( intf; json.interfaces.opt!(Json[]) )
299 					intfdecl.derivedInterfaces ~= parseType(intf, intfdecl);
300 				ret = intfdecl;
301 				break;
302 		}
303 
304 		insertIntoTypeMap(ret);
305 
306 		ret.members = parseDeclList(json.members, ret);
307 
308 		return ret;
309 	}
310 
311 	Declaration parseVariableDecl(Json json, Entity parent)
312 	{
313 		if (json.storageClass.opt!(Json[]).canFind!(j => j.opt!string == "enum")) {
314 			auto ret = new EnumMemberDeclaration(parent, json.name.get!string);
315 			if (json["init"].opt!string.length)
316 				ret.value = parseValue(json["init"].opt!string);
317 			return ret;
318 		} else {
319 			auto ret = new VariableDeclaration(parent, json.name.get!string);
320 			ret.type = parseType(json, ret);
321 			if (json["init"].opt!string.length)
322 				ret.initializer = parseValue(json["init"].opt!string);
323 			return ret;
324 		}
325 	}
326 
327 	auto parseTemplateDecl(Json json, Entity parent)
328 	{
329 		auto ret = new TemplateDeclaration(parent, json.name.get!string);
330 		foreach (arg; json.parameters.opt!(Json[])) {
331 			string argstr;
332 			switch (arg.kind.get!string) {
333 				case "value":
334 					if (auto pt = "type" in arg) argstr = pt.get!string ~ ' ';
335 					else argstr = demanglePrettyType(arg.deco.get!string) ~ ' ';
336 					goto default;
337 				case "alias":
338 					argstr = "alias ";
339 					goto default;
340 				case "tuple":
341 					argstr ~= arg.name.get!string ~ "...";
342 					break;
343 				default:
344 					argstr ~= arg.name.get!string;
345 			}
346 			ret.templateArgs ~= new TemplateParameterDeclaration(ret, argstr);
347 			ret.templateConstraint = json.constraint.opt!string;
348 		}
349 		ret.members = parseDeclList(json.members, ret);
350 		return ret;
351 	}
352 
353 	Type parseType(Json json, Entity sc, string def_type = "void", bool warn_if_not_exists = true)
354 		out(ret) { assert(!def_type.length || ret !is null); }
355 	body {
356 		string str;
357 		if( json.type == Json.Type.Undefined ){
358 			if (warn_if_not_exists) logWarn("No type found for %s.", sc.qualifiedName);
359 			str = def_type;
360 		} else if (json.type == Json.Type.String) str = json.get!string();
361 		else if (auto pv = "deco" in json) str = demanglePrettyType(pv.get!string());
362 		else if (auto pv = "type" in json) str = fixFunctionType(pv.get!string(), def_type);
363 		else if (auto pv = "originalType" in json) str = fixFunctionType(pv.get!string(), def_type);
364 
365 		if( str.length == 0 ) str = def_type;
366 
367 		if( !str.length ) return null;
368 
369 		return parseType(str, sc);
370 	}
371 
372 	Type parseType(string str, Entity sc)
373 		out(ret) { assert(ret !is null); }
374 	body {
375 		auto tokens = tokenizeDSource(str);
376 		
377 		logDebug("parse type '%s'", str);
378 		try {
379 			auto type = parseTypeDecl(tokens, sc);
380 			type.text = str;
381 			return type;
382 		} catch( Exception e ){
383 			logError("Error parsing type '%s': %s", str, e.msg);
384 			auto type = new Type;
385 			type.text = str;
386 			type.typeName = str;
387 			type.kind = TypeKind.Primitive;
388 			return type;
389 		}
390 	}
391 
392 	Value parseValue(string str)
393 	{
394 		auto ret = new Value;
395 		//ret.type = ;
396 		ret.valueString = str;
397 		return ret;
398 	}
399 
400 	Protection parseProtection(Json prot)
401 	{
402 		switch( prot.opt!string ){
403 			default: return Protection.Public;
404 			case "package": return Protection.Package;
405 			case "protected": return Protection.Protected;
406 			case "private": return Protection.Private;
407 		}
408 	}
409 
410 	Declaration lookupDecl(string qualified_name, Entity sc)
411 	{
412 		while(sc){
413 			auto ent = cast(Declaration)sc.lookup(qualified_name);
414 			if( ent ) return ent;
415 			sc = sc.parent;
416 		}
417 		return null;
418 	}
419 
420 	Type parseTypeDecl(ref string[] tokens, Entity sc)
421 	{
422 
423 		auto ret = parseType(tokens, sc);
424 		return ret;
425 	}
426 
427 	Type parseType(ref string[] tokens, Entity sc)
428 	{
429 		string[] attributes;
430 		auto basic_type = parseBasicType(tokens, sc, attributes);
431 		basic_type.attributes ~= attributes;
432 		return basic_type;	
433 	}
434 
435 	Type parseBasicType(ref string[] tokens, Entity sc, out string[] attributes)
436 		out(ret) { assert(ret !is null); }
437 	body {
438 		static immutable global_attribute_keywords = ["abstract", "auto", "const", "deprecated", "enum",
439 			"extern", "final", "immutable", "inout", "shared", "nothrow", "override", "pure",
440 			"__gshared", "scope", "static", "synchronize"];
441 
442 		static immutable parameter_attribute_keywords = ["auto", "const", "final", "immutable", "in", "inout",
443 			"lazy", "out", "ref", "scope", "shared"];
444 
445 		static immutable member_function_attribute_keywords = ["const", "immutable", "inout", "shared", "pure", "nothrow"];
446 		
447 			
448 		if( tokens.length > 0 && tokens[0] == "extern" ){
449 			enforce(tokens[1] == "(");
450 			enforce(tokens[3] == ")");
451 			attributes ~= join(tokens[0 .. 4]);
452 			tokens = tokens[4 .. $];
453 		}
454 		
455 		immutable string[] attribute_keywords = global_attribute_keywords ~ parameter_attribute_keywords ~ member_function_attribute_keywords;
456 		/*final switch( sc ){
457 			case DeclScope.Global: attribute_keywords = global_attribute_keywords; break;
458 			case DeclScope.Parameter: attribute_keywords = parameter_attribute_keywords; break;
459 			case DeclScope.Class: attribute_keywords = member_function_attribute_keywords; break;
460 		}*/
461 
462 		void parseAttributes(ref string[] dst, const(string)[] keywords)
463 		{
464 			while( tokens.length > 0 ){
465 				if( tokens.front == "@" ){
466 					tokens.popFront();
467 					dst ~= "@"~tokens.front;
468 					tokens.popFront();
469 				} else if( keywords.countUntil(tokens[0]) >= 0 && tokens[1] != "(" ){
470 					dst ~= tokens.front;
471 					tokens.popFront();
472 				} else break;
473 			}
474 		}
475 
476 		parseAttributes(attributes, attribute_keywords);
477 
478 
479 		Type type;
480 		static immutable const_modifiers = ["const", "immutable", "shared", "inout"];
481 		if (tokens.length > 2 && tokens[1] == "(" && const_modifiers.countUntil(tokens[0]) >= 0) {
482 			auto mod = tokens.front;
483 			tokens.popFrontN(2);
484 			string[] subattrs;
485 			type = parseBasicType(tokens, sc, subattrs);
486 			type.modifiers ~= mod;
487 			type.attributes ~= subattrs;
488 			enforce(!tokens.empty && tokens.front == ")", format("Missing ')' for '%s('", mod));
489 			tokens.popFront();
490 		} else if (!tokens.empty && !tokens.front.among("function", "delegate")) {
491 			type = new Type;
492 			type.kind = TypeKind.Primitive;
493 			m_primTypes ~= tuple(type, sc);
494 
495 			size_t start = 0, end;
496 			if( tokens[start] == "." ) start++;
497 			for( end = start; end < tokens.length && isIdent(tokens[end]); ){
498 				end++;
499 				if( end >= tokens.length || tokens[end] != "." )
500 					break;
501 				end++;
502 			}
503 
504 			size_t i = end;
505 
506 			string type_name, nested_name;
507 			if( i == 0 && tokens[0] == "..." ){
508 				type_name = "...";
509 				nested_name = null;
510 			} else if( i == 0 && tokens[0] == "(" ){
511 				type_name = "constructor";
512 				nested_name = null;
513 			} else {
514 				enforce(i > 0, "Expected identifier but got "~tokens.front);
515 				type.typeName = join(tokens[start .. end]);
516 				//type.typeDecl = cast(Declaration)sc.lookup(type.typeName);
517 				tokens.popFrontN(i);
518 				
519 				if (type.typeName == "typeof" && !tokens.empty && tokens.front == "(") {
520 					type.typeName ~= "(";
521 					tokens.popFront();
522 					int level = 1;
523 					while (!tokens.empty && level > 0) {
524 						if (tokens.front == "(") level++;
525 						else if( tokens.front == ")") level--;
526 						type.typeName ~= tokens.front;
527 						tokens.popFront();
528 					}
529 				} else if( !tokens.empty && tokens.front == "!" ){
530 					tokens.popFront();
531 					if( tokens.front == "(" ){
532 						size_t j = 1;
533 						int cc = 1;
534 						while( cc > 0 ){
535 							assert(j < tokens.length);
536 							if( tokens[j] == "(" ) cc++;
537 							else if( tokens[j] == ")") cc--;
538 							j++;
539 						}
540 						type.templateArgs = join(tokens[0 .. j]);
541 						tokens.popFrontN(j);
542 						logDebug("templargs: %s", type.templateArgs);
543 					} else {
544 						type.templateArgs = tokens[0];
545 						tokens.popFront();
546 					}
547 					
548 					// HACK: dropping the actual type name here!
549 					while (!tokens.empty && tokens.front == ".") {
550 						tokens.popFront();
551 						if (!tokens.empty()) tokens.popFront();
552 					}
553 				}
554 			}
555 		}
556 		
557 		while( !tokens.empty ){
558 			if( tokens.front == "*" ){
559 				auto ptr = new Type;
560 				ptr.kind = TypeKind.Pointer;
561 				ptr.elementType = type;
562 				type = ptr;
563 				tokens.popFront();
564 			} else if( tokens.front == "[" ){
565 				tokens.popFront();
566 				if( tokens.front == "]" ){
567 					auto arr = new Type;
568 					arr.kind = TypeKind.Array;
569 					arr.elementType = type;
570 					type = arr;
571 				} else {
572 					string[] tokens_copy = tokens;
573 					Type keytp;
574 					if (!isDigit(tokens.front[0]) && tokens.front != "!") keytp = parseType(tokens_copy, sc);
575 					if (keytp && !tokens_copy.empty && tokens_copy.front == "]") {
576 						tokens = tokens_copy;
577 						logDebug("GOT TYPE: %s", keytp.toString());
578 						auto aa = new Type;
579 						aa.kind = TypeKind.AssociativeArray;
580 						aa.elementType = type;
581 						aa.keyType = keytp;
582 						type = aa;
583 					} else {
584 						auto arr = new Type;
585 						arr.kind = TypeKind.StaticArray;
586 						arr.elementType = type;
587 						arr.arrayLength = tokens.front;
588 						tokens.popFront();
589 						while (!tokens.empty && tokens.front != "]") {
590 							arr.arrayLength ~= tokens.front;
591 							tokens.popFront();
592 						}
593 						type = arr;
594 					}
595 				}
596 				enforce(tokens.front == "]", "Expected ']', got '"~tokens.front~"'.");
597 				tokens.popFront();
598 			} else break;
599 		}
600 		
601 		if (!type) {
602 			type = new Type;
603 			type.kind = TypeKind.Primitive;
604 		}
605 
606 		while (!tokens.empty && (tokens.front == "function" || tokens.front == "delegate" || tokens.front == "(")) {
607 			Type ftype = new Type;
608 			ftype.kind = tokens.front == "(" || tokens.front == "function" ? TypeKind.Function : TypeKind.Delegate;
609 			ftype.returnType = type;
610 			if (tokens.front != "(") tokens.popFront();
611 			enforce(tokens.front == "(");
612 			tokens.popFront();
613 			if (!tokens.empty && tokens.front == ",") tokens.popFront(); // sometimes demangleType() returns something like "void(, ...)"
614 			while (true) {
615 				if (tokens.front == ")") break;
616 				enforce(!tokens.empty);
617 				ftype.parameterTypes ~= parseTypeDecl(tokens, sc);
618 				if (tokens.front != "," && tokens.front != ")") {
619 					ftype._parameterNames ~= tokens.front;
620 					tokens.popFront();
621 				} else ftype._parameterNames ~= null;
622 				if (tokens.front == "...") {
623 					ftype._parameterNames[$-1] ~= tokens.front;
624 					tokens.popFront();
625 				}
626 				if (tokens.front == "=") {
627 					tokens.popFront();
628 					string defval;
629 					int ccount = 0;
630 					while (!tokens.empty) {
631 						if (ccount == 0 && (tokens.front == "," || tokens.front == ")"))
632 							break;
633 						if (tokens.front == "(") ccount++;
634 						else if (tokens.front == ")") ccount--;
635 						defval ~= tokens.front;
636 						tokens.popFront();
637 					}
638 					ftype._parameterDefaultValues ~= parseValue(defval);
639 					logDebug("got defval %s", defval);
640 				} else ftype._parameterDefaultValues ~= null;
641 				if (tokens.front == ")") break;
642 				enforce(tokens.front == ",", "Expecting ',', got "~tokens.front);
643 				tokens.popFront();
644 			}
645 			tokens.popFront();
646 
647 			parseAttributes(ftype.attributes, member_function_attribute_keywords);
648 
649 			type = ftype;
650 		}
651 
652 		return type;
653 	}
654 
655 	/* special function that looks at the default type to see if a function type
656 		is expected and if that's the case, fixes up the type string to read
657 		as a valid D type declaration (DMD omits "function"/"delegate", which
658 		results in an ambiguous meaning)
659 	*/
660 	private string fixFunctionType(string type, string deftype)
661 	{
662 		Type dt = parseType(deftype, new Module(null, "dummy"));
663 		if (deftype == "void()" || dt && dt.kind.among(TypeKind.Function, TypeKind.Delegate)) {
664 			auto last_clamp = type.lastIndexOf(')');
665 			auto idx = last_clamp-1;
666 			int l = 1;
667 			while (idx >= 0) {
668 				if (type[idx] == ')') l++;
669 				else if (type[idx] == '(') l--;
670 				if (l == 0) break;
671 				idx--;
672 			}
673 			if (idx <= 0 || l > 0) return type;
674 			return type[0 .. idx] ~ " function" ~ type[idx .. $];
675 		}
676 		return type;
677 	}
678 	
679 	string[] tokenizeDSource(string dsource_)
680 	{
681 		static import std.uni;
682 
683 		static immutable dstring[] tokens = [
684 			"/", "/=", ".", "..", "...", "&", "&=", "&&", "|", "|=", "||",
685 			"-", "-=", "--", "+", "+=", "++", "<", "<=", "<<", "<<=",
686 			"<>", "<>=", ">", ">=", ">>=", ">>>=", ">>", ">>>", "!", "!=",
687 			"!<>", "!<>=", "!<", "!<=", "!>", "!>=", "(", ")", "[", "]",
688 			"{", "}", "?", ",", ";", ":", "$", "=", "==", "*", "*=",
689 			"%", "%=", "^", "^=", "~", "~=", "@", "=>", "#"
690 		];
691 		static bool[dstring] token_map;
692 		
693 		if( !token_map.length ){
694 			foreach( t; tokens )
695 				token_map[t] = true;
696 			token_map.rehash;
697 		}
698 		
699 		dstring dsource = to!dstring(dsource_);
700 		
701 		dstring[] ret;
702 		outer:
703 		while(true){
704 			dsource = stripLeft(dsource);
705 			if( dsource.length == 0 ) break;
706 			
707 			// special token?
708 			foreach_reverse( i; 1 .. min(5, dsource.length+1) )
709 				if( dsource[0 .. i] in token_map ){
710 					ret ~= dsource[0 .. i];
711 					dsource.popFrontN(i);
712 					continue outer;
713 				}
714 			
715 			// identifier?
716 			if( dsource[0] == '_' || std.uni.isAlpha(dsource[0]) ){
717 				size_t i = 1;
718 				while( i < dsource.length && (dsource[i] == '_' || std.uni.isAlpha(dsource[i]) || isDigit(dsource[i])) ) i++;
719 				ret ~= dsource[0 .. i];
720 				dsource.popFrontN(i);
721 				continue;
722 			}
723 			
724 			// character literal?
725 			if( dsource[0] == '\'' ){
726 				size_t i = 1;
727 				while( dsource[i] != '\'' ){
728 					if( dsource[i] == '\\' ) i++;
729 					i++;
730 					enforce(i < dsource.length);
731 				}
732 				ret ~= dsource[0 .. i+1];
733 				dsource.popFrontN(i+1);
734 				continue;
735 			}
736 			
737 			// string? (incomplete!)
738 			if( dsource[0] == '"' ){
739 				size_t i = 1;
740 				while( dsource[i] != '"' ){
741 					if( dsource[i] == '\\' ) i++;
742 					i++;
743 					enforce(i < dsource.length);
744 				}
745 				ret ~= dsource[0 .. i+1];
746 				dsource.popFrontN(i+1);
747 				continue;
748 			}
749 			
750 			// number?
751 			if( isDigit(dsource[0]) || dsource[0] == '.' ){
752 				auto dscopy = dsource;
753 				parse!double(dscopy);
754 				ret ~= dsource[0 .. dsource.length-dscopy.length];
755 				dsource.popFrontN(dsource.length-dscopy.length);
756 				if( dsource.startsWith("u") ) dsource.popFront();
757 				else if( dsource.startsWith("f") ) dsource.popFront();
758 				continue;
759 			}
760 			
761 			ret ~= dsource[0 .. 1];
762 			dsource.popFront();
763 		}
764 		
765 		auto ret_ = new string[ret.length];
766 		foreach( i; 0 .. ret.length ) ret_[i] = to!string(ret[i]);
767 		return ret_;
768 	}
769 
770 	bool isDigit(dchar ch)
771 	{
772 		return ch >= '0' && ch <= '9';
773 	}
774 
775 	bool isIdent(string str)
776 	{
777 		static import std.uni;
778 
779 		if( str.length < 1 ) return false;
780 		foreach( i, dchar ch; str ){
781 			if( ch == '_' || std.uni.isAlpha(ch) ) continue;
782 			if( i > 0 && isDigit(ch) ) continue;
783 			return false;
784 		}
785 		return true;	
786 	}
787 
788 	string fullStrip(string s)
789 	{
790 		string chars = " \t\r\n";
791 		while( s.length > 0 && chars.countUntil(s[0]) >= 0 ) s.popFront();
792 		while( s.length > 0 && chars.countUntil(s[$-1]) >= 0 ) s.popBack();
793 		return s;
794 	}
795 
796 	void insertIntoTypeMap(Declaration decl)
797 	{
798 		string[] parts = split(decl.qualifiedName, ".");
799 		foreach( i; 0 .. parts.length ){
800 			auto partial_name = join(parts[i .. $], ".");
801 			m_typeMap[partial_name] = decl;
802 		}
803 	}
804 }
805 
806 string demanglePrettyType(string mangled_type)
807 {
808 	if (mangled_type == "n") return "typeof(null)"; // Workaround D issue 14410
809 	auto str = assumeUnique(demangleType(mangled_type));
810 	str = str.replace("immutable(char)[]", "string");
811 	str = str.replace("immutable(wchar)[]", "wstring");
812 	str = str.replace("immutable(dchar)[]", "dstring");
813 	return str;
814 }