1 /**
2 	Parses DMD JSON output and builds up a socumentation syntax tree (JSON format up to DMD 2.061).
3 
4 	Copyright: © 2012 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.jsonparser_old;
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 vibe.core.log;
21 import vibe.data.json;
22 
23 
24 Package parseJsonDocsOld(Json json, Package root = null)
25 {
26 	if( !root ) root = new Package(null, null);
27 	Parser p;
28 	foreach( mod; json ){
29 		p.parseModule(mod, root);
30 	}
31 	p.resolveTypes(root);
32 	return root;
33 }
34 
35 private struct Parser
36 {
37 	private Tuple!(Type, Entity)[] m_primTypes;
38 	private Declaration[string] m_typeMap;
39 
40 	void resolveTypes(Package root)
41 	{
42 		bool isTypeDecl(Declaration a)
43 		{
44 			switch(a.kind){
45 				default: return false;
46 				case DeclarationKind.Struct:
47 				case DeclarationKind.Union:
48 				case DeclarationKind.Class:
49 				case DeclarationKind.Interface:
50 				case DeclarationKind.Enum:
51 					return true;
52 				case DeclarationKind.Alias:
53 					return (cast(AliasDeclaration)a).targetType !is null;
54 			}
55 		}
56 
57 		foreach( t; m_primTypes ){
58 			auto decl = cast(Declaration)t[1].lookup(t[0].typeName);
59 			if( !decl || !isTypeDecl(decl) ){
60 				auto pd = t[0].typeName in m_typeMap;
61 				if( pd ) decl = *pd;
62 			}
63 			if( decl && isTypeDecl(decl) )
64 				t[0].typeDecl = decl;
65 		}
66 
67 		// fixup class bases
68 		root.visit!ClassDeclaration((decl){
69 			if( decl.baseClass && decl.baseClass.typeDecl && !cast(ClassDeclaration)decl.baseClass.typeDecl )
70 				decl.baseClass = null;
71 			foreach( i; decl.derivedInterfaces )
72 				if( i.typeDecl && !cast(InterfaceDeclaration)i.typeDecl )
73 					i.typeDecl = null;
74 			assert(decl);
75 		});
76 
77 		// fixup interface bases
78 		root.visit!InterfaceDeclaration((decl){
79 			foreach( i; decl.derivedInterfaces )
80 				if( i.typeDecl && !cast(InterfaceDeclaration)i.typeDecl )
81 					i.typeDecl = null;
82 			assert(decl);
83 		});
84 	}
85 
86 	void parseModule(Json json, Package root_package)
87 	{
88 		Module mod;
89 		if( "name" !in json ){
90 			logError("No name attribute in module %s - ignoring", json.filename.opt!string);
91 			return;
92 		}
93 		auto path = json.name.get!string.split(".");
94 		Package p = root_package;
95 		foreach( i, pe; path ){
96 			if( i+1 < path.length ) p = p.getOrAddPackage(pe);
97 			else mod = p.createModule(pe);
98 		}
99 
100 		mod.file = json.file.get!string;
101 		mod.docGroup = new DocGroup(mod, json.comment.opt!string());
102 		mod.members = parseDeclList(json.members, mod);
103 	}
104 
105 	Declaration[] parseDeclList(Json json, Entity parent)
106 	{
107 		if( json.type == Json.Type.Undefined ) return null;
108 		DocGroup lastdoc;
109 		Declaration[] ret;
110 		foreach( mem; json ){
111 			auto decl = parseDecl(mem, parent);
112 			auto doc = decl.docGroup;
113 			if( lastdoc && (doc.text == lastdoc.text && doc.text.length || doc.comment.isDitto) ){
114 				lastdoc.members ~= decl;
115 				decl.docGroup = lastdoc;
116 			} else if( doc.comment.isPrivate ){
117 				decl.protection = Protection.Private;
118 				lastdoc = null;
119 			} else lastdoc = decl.docGroup;
120 			ret ~= decl;
121 		}
122 		return ret;
123 	}
124 
125 	Declaration parseDecl(Json json, Entity parent)
126 	{
127 		Declaration ret;
128 
129 		// DMD outputs templates with the wrong kind sometimes
130 		if( json.name.get!string().canFind('(') ){
131 			ret = parseTemplateDecl(json, parent);
132 		} else {
133 			switch( json.kind.get!string ){
134 				default: enforce(false, "Unknown declaration kind: "~json.kind.get!string); assert(false);
135 				case "alias":
136 					ret = parseAliasDecl(json, parent);
137 					break;
138 				case "function":
139 				case "allocator":
140 				case "deallocator":
141 				case "constructor":
142 					ret = parseFunctionDecl(json, parent);
143 					break;
144 				case "enum":
145 					ret = parseEnumDecl(json, parent);
146 					break;
147 				case "enum member":
148 					ret = parseEnumMemberDecl(json, parent);
149 					break;
150 				case "struct":
151 				case "union":
152 				case "class":
153 				case "interface":
154 					ret = parseCompositeDecl(json, parent);
155 					break;
156 				case "variable":
157 					ret = parseVariableDecl(json, parent);
158 					break;
159 				case "template":
160 					ret = parseTemplateDecl(json, parent);
161 					break;
162 			}
163 		}
164 
165 		ret.protection = parseProtection(json.protection);
166 		ret.line = json["line"].opt!int;
167 		ret.docGroup = new DocGroup(ret, json.comment.opt!string());
168 
169 		return ret;
170 	}
171 
172 	auto parseAliasDecl(Json json, Entity parent)
173 	{
174 		auto ret = new AliasDeclaration(parent, json.name.get!string);
175 		if( auto pt = "type" in json ){
176 			ret.targetType = parseType(*pt, parent, null);
177 			if( ret.targetType.kind == TypeKind.Primitive && ret.targetType.typeName.length == 0 )
178 				ret.targetType = null;
179 		}
180 		insertIntoTypeMap(ret);
181 		return ret;
182 	}
183 
184 	auto parseFunctionDecl(Json json, Entity parent)
185 	{
186 		auto ret = new FunctionDeclaration(parent, json.name.get!string);
187 		ret.type = parseType(json["type"], parent);
188 		if( ret.type.kind == TypeKind.Function ){
189 			ret.returnType = ret.type.returnType;
190 			ret.attributes = ret.type.attributes;
191 			foreach( i, pt; ret.type.parameterTypes ){
192 				auto decl = new VariableDeclaration(ret, ret.type._parameterNames[i]);
193 				decl.type = pt;
194 				decl.initializer = ret.type._parameterDefaultValues[i];
195 				ret.parameters ~= decl;
196 			}
197 		} else {
198 			logError("Expected function type for '%s', got %s", json["type"].opt!string, ret.type.kind);
199 		}
200 		return ret;
201 	}
202 
203 	auto parseEnumDecl(Json json, Entity parent)
204 	{
205 		auto ret = new EnumDeclaration(parent, json.name.get!string);
206 		insertIntoTypeMap(ret);
207 		ret.baseType = parseType(json.base, parent);
208 		auto mems = parseDeclList(json.members, ret);
209 		foreach( m; mems ){
210 			auto em = cast(EnumMemberDeclaration)m;
211 			assert(em !is null, "Enum containing non-enum-members?");
212 			ret.members ~= em;
213 		}
214 		return ret;
215 	}
216 
217 	auto parseEnumMemberDecl(Json json, Entity parent)
218 	{
219 		auto ret = new EnumMemberDeclaration(parent, json.name.get!string);
220 		//ret.value = parseValue(json.value);
221 		return ret;
222 	}
223 
224 	auto parseCompositeDecl(Json json, Entity parent)
225 	{
226 		CompositeTypeDeclaration ret;
227 		switch(json.kind.get!string){
228 			default:
229 				logWarn("Invalid composite decl kind: %s", json.kind.get!string);
230 				return new StructDeclaration(parent, json.name.get!string);
231 			case "struct":
232 				ret = new StructDeclaration(parent, json.name.get!string);
233 				break;
234 			case "union":
235 				ret = new UnionDeclaration(parent, json.name.get!string);
236 				break;
237 			case "class":
238 				auto clsdecl = new ClassDeclaration(parent, json.name.get!string);
239 				if( clsdecl.qualifiedName != "object.Object" )
240 					clsdecl.baseClass = parseType(json.base, parent, "Object");
241 				foreach( intf; json.interfaces.opt!(Json[]) )
242 					clsdecl.derivedInterfaces ~= parseType(intf, parent);
243 				ret = clsdecl;
244 				break;
245 			case "interface":
246 				auto intfdecl = new InterfaceDeclaration(parent, json.name.get!string);
247 				foreach( intf; json.interfaces.opt!(Json[]) )
248 					intfdecl.derivedInterfaces ~= parseType(intf, parent);
249 				ret = intfdecl;
250 				break;
251 		}
252 
253 		insertIntoTypeMap(ret);
254 
255 		ret.members = parseDeclList(json.members, ret);
256 
257 		return ret;
258 	}
259 
260 	auto parseVariableDecl(Json json, Entity parent)
261 	{
262 		auto ret = new VariableDeclaration(parent, json.name.get!string);
263 		ret.type = parseType(json["type"], parent);
264 		return ret;
265 	}
266 
267 	auto parseTemplateDecl(Json json, Entity parent)
268 	{
269 		auto name = json.name.get!string().strip();
270 		auto cidx = name.countUntil('(');
271 		assert(cidx > 0 && name.endsWith(')'), "Template name must be of the form name(args)");
272 
273 		auto ret = new TemplateDeclaration(parent, name[0 .. cidx]);
274 		ret.templateArgs = name[cidx+1 .. $-1].split(",").map!(a => new TemplateParameterDeclaration(ret, a.strip)).array;
275 		ret.members = parseDeclList(json.members, ret);
276 		return ret;
277 	}
278 
279 	Type parseType(Json tp, Entity sc, string def_type = "void")
280 	{
281 		auto str = tp.opt!string;
282 		if( str.length == 0 ) str = def_type;
283 		auto tokens = tokenizeDSource(str);
284 		
285 		logDebug("parse type '%s'", str);
286 		try {
287 			auto type = parseTypeDecl(tokens, sc);
288 			type.text = str;
289 			return type;
290 		} catch( Exception e ){
291 			logError("Error parsing type '%s': %s", str, e.msg);
292 			auto type = new Type;
293 			type.text = str;
294 			type.typeName = str;
295 			type.kind = TypeKind.Primitive;
296 			return type;
297 		}
298 	}
299 
300 	Value parseValue(string str)
301 	{
302 		auto ret = new Value;
303 		//ret.type = ;
304 		ret.valueString = str;
305 		return ret;
306 	}
307 
308 	Protection parseProtection(Json prot)
309 	{
310 		switch( prot.opt!string ){
311 			default: return Protection.Public;
312 			case "package": return Protection.Package;
313 			case "protected": return Protection.Protected;
314 			case "private": return Protection.Private;
315 		}
316 	}
317 
318 	Declaration lookupDecl(string qualified_name, Entity sc)
319 	{
320 		while(sc){
321 			auto ent = cast(Declaration)sc.lookup(qualified_name);
322 			if( ent ) return ent;
323 			sc = sc.parent;
324 		}
325 		return null;
326 	}
327 
328 	Type parseTypeDecl(ref string[] tokens, Entity sc)
329 	{
330 		static immutable global_attribute_keywords = ["abstract", "auto", "const", "deprecated", "enum",
331 			"extern", "final", "immutable", "inout", "shared", "nothrow", "override", "pure",
332 			"__gshared", "scope", "static", "synchronize"];
333 
334 		static immutable parameter_attribute_keywords = ["auto", "const", "final", "immutable", "in", "inout",
335 			"lazy", "out", "ref", "scope", "shared"];
336 
337 		static immutable member_function_attribute_keywords = ["const", "immutable", "inout", "shared", "pure", "nothrow"];
338 		
339 			
340 		string[] attributes;	
341 		if( tokens.length > 0 && tokens[0] == "extern" ){
342 			enforce(tokens[1] == "(");
343 			enforce(tokens[3] == ")");
344 			attributes ~= join(tokens[0 .. 4]);
345 			tokens = tokens[4 .. $];
346 		}
347 		
348 		immutable string[] attribute_keywords = global_attribute_keywords ~ parameter_attribute_keywords ~ member_function_attribute_keywords;
349 		/*final switch( sc ){
350 			case DeclScope.Global: attribute_keywords = global_attribute_keywords; break;
351 			case DeclScope.Parameter: attribute_keywords = parameter_attribute_keywords; break;
352 			case DeclScope.Class: attribute_keywords = member_function_attribute_keywords; break;
353 		}*/
354 
355 		while( tokens.length > 0 ){
356 			if( tokens.front == "@" ){
357 				tokens.popFront();
358 				attributes ~= "@"~tokens.front;
359 				tokens.popFront();
360 			} else if( attribute_keywords.countUntil(tokens[0]) >= 0 && tokens[1] != "(" ){
361 				attributes ~= tokens.front;
362 				tokens.popFront();
363 			} else break;
364 		}
365 
366 		auto ret = parseType(tokens, sc);
367 		ret.attributes = attributes;
368 		return ret;
369 	}
370 
371 	Type parseType(ref string[] tokens, Entity sc)
372 	{
373 		auto basic_type = parseBasicType(tokens, sc);
374 		
375 		while( tokens.length > 0 && (tokens[0] == "function" || tokens[0] == "delegate" || tokens[0] == "(") ){
376 			Type ret = new Type;
377 			ret.kind = tokens.front == "(" || tokens.front == "function" ? TypeKind.Function : TypeKind.Delegate;
378 			ret.returnType = basic_type;
379 			if( tokens.front != "(" ) tokens.popFront();
380 			enforce(tokens.front == "(");
381 			tokens.popFront();
382 			while(true){
383 				if( tokens.front == ")" ) break;
384 				enforce(!tokens.empty);
385 				ret.parameterTypes ~= parseTypeDecl(tokens, sc);
386 				if( tokens.front != "," && tokens.front != ")" ){
387 					ret._parameterNames ~= tokens.front;
388 					tokens.popFront();
389 				} else ret._parameterNames ~= null;
390 				if( tokens.front == "..." ){
391 					ret._parameterNames[$-1] ~= tokens.front;
392 					tokens.popFront();
393 				}
394 				if( tokens.front == "=" ){
395 					tokens.popFront();
396 					string defval;
397 					int ccount = 0;
398 					while( !tokens.empty ){
399 						if( ccount == 0 && (tokens.front == "," || tokens.front == ")") )
400 							break;
401 						if( tokens.front == "(" ) ccount++;
402 						else if( tokens.front == ")" ) ccount--;
403 						defval ~= tokens.front;
404 						tokens.popFront();
405 					}
406 					ret._parameterDefaultValues ~= parseValue(defval);
407 					logDebug("got defval %s", defval);
408 				} else ret._parameterDefaultValues ~= null;
409 				if( tokens.front == ")" ) break;
410 				enforce(tokens.front == ",", "Expecting ',', got "~tokens.front);
411 				tokens.popFront();
412 			}
413 			tokens.popFront();
414 			basic_type = ret;
415 		}
416 		
417 		return basic_type;	
418 	}
419 
420 	Type parseBasicType(ref string[] tokens, Entity sc)
421 	{
422 		Type type;
423 		{
424 			static immutable const_modifiers = ["const", "immutable", "shared", "inout"];
425 			string[] modifiers;
426 			while( tokens.length > 2 ){
427 				if( tokens[1] == "(" && const_modifiers.countUntil(tokens[0]) >= 0 ){
428 					modifiers ~= tokens[0];
429 					tokens.popFrontN(2);
430 				} else break;
431 			}
432 			
433 			
434 			if( modifiers.length > 0 ){
435 				type = parseBasicType(tokens, sc);
436 				type.modifiers = modifiers;
437 				foreach( i; 0 .. modifiers.length ){
438 					//enforce(tokens[i] == ")", "expected ')', got '"~tokens[i]~"'");
439 					if( tokens[i] == ")" ) // FIXME: this is a hack to make parsing(const(immutable(char)[][]) somehow "work"
440 						tokens.popFront();
441 				}
442 				//tokens.popFrontN(modifiers.length);
443 			} else {
444 				type = new Type;
445 				type.kind = TypeKind.Primitive;
446 				m_primTypes ~= tuple(type, sc);
447 
448 				size_t start = 0, end;
449 				if( tokens[start] == "." ) start++;
450 				for( end = start; end < tokens.length && isIdent(tokens[end]); ){
451 					end++;
452 					if( end >= tokens.length || tokens[end] != "." )
453 						break;
454 					end++;
455 				}
456 
457 				size_t i = end;
458 
459 				string type_name, nested_name;
460 				if( i == 0 && tokens[0] == "..." ){
461 					type_name = "...";
462 					nested_name = null;
463 				} else if( i == 0 && tokens[0] == "(" ){
464 					type_name = "constructor";
465 					nested_name = null;
466 				} else {
467 					enforce(i > 0, "Expected identifier but got "~tokens.front);
468 					type.typeName = join(tokens[start .. end]);
469 					//type.typeDecl = cast(Declaration)sc.lookup(type.typeName);
470 					tokens.popFrontN(i);
471 
472 					if( !tokens.empty && tokens.front == "!" ){
473 						tokens.popFront();
474 						if( tokens.front == "(" ){
475 							size_t j = 1;
476 							int cc = 1;
477 							while( cc > 0 ){
478 								assert(j < tokens.length);
479 								if( tokens[j] == "(" ) cc++;
480 								else if( tokens[j] == ")") cc--;
481 								j++;
482 							}
483 							type.templateArgs = join(tokens[0 .. j]);
484 							tokens.popFrontN(j);
485 							logDebug("templargs: %s", type.templateArgs);
486 						} else {
487 							type.templateArgs = tokens[0];
488 							tokens.popFront();
489 						}
490 					}
491 				}
492 			}
493 		}
494 		
495 		while( !tokens.empty ){
496 			if( tokens.front == "*" ){
497 				auto ptr = new Type;
498 				ptr.kind = TypeKind.Pointer;
499 				ptr.elementType = type;
500 				type = ptr;
501 				tokens.popFront();
502 			} else if( tokens.front == "[" ){
503 				tokens.popFront();
504 				if( tokens.front == "]" ){
505 					auto arr = new Type;
506 					arr.kind = TypeKind.Array;
507 					arr.elementType = type;
508 					type = arr;
509 				} else if( isDigit(tokens.front[0]) ){
510 					auto arr = new Type;
511 					arr.kind = TypeKind.StaticArray;
512 					arr.elementType = type;
513 					arr.arrayLength = tokens.front;//to!int(tokens.front);
514 					tokens.popFront();
515 					type = arr;
516 				} else {
517 					auto keytp = parseType(tokens, sc);
518 					logDebug("GOT TYPE: %s", keytp.toString());
519 					auto aa = new Type;
520 					aa.kind = TypeKind.AssociativeArray;
521 					aa.elementType = type;
522 					aa.keyType = keytp;
523 					type = aa;
524 				}
525 				enforce(tokens.front == "]", "Expected ']', got '"~tokens.front~"'.");
526 				tokens.popFront();
527 			} else break;
528 		}
529 		
530 		return type;
531 	}
532 	
533 	string[] tokenizeDSource(string dsource_)
534 	{
535 		static immutable dstring[] tokens = [
536 			"/", "/=", ".", "..", "...", "&", "&=", "&&", "|", "|=", "||",
537 			"-", "-=", "--", "+", "+=", "++", "<", "<=", "<<", "<<=",
538 			"<>", "<>=", ">", ">=", ">>=", ">>>=", ">>", ">>>", "!", "!=",
539 			"!<>", "!<>=", "!<", "!<=", "!>", "!>=", "(", ")", "[", "]",
540 			"{", "}", "?", ",", ";", ":", "$", "=", "==", "*", "*=",
541 			"%", "%=", "^", "^=", "~", "~=", "@", "=>", "#"
542 		];
543 		static bool[dstring] token_map;
544 		
545 		if( !token_map.length ){
546 			foreach( t; tokens )
547 				token_map[t] = true;
548 			token_map.rehash;
549 		}
550 		
551 		dstring dsource = to!dstring(dsource_);
552 		
553 		dstring[] ret;
554 		outer:
555 		while(true){
556 			dsource = stripLeft(dsource);
557 			if( dsource.length == 0 ) break;
558 			
559 			// special token?
560 			foreach_reverse( i; 1 .. min(5, dsource.length+1) )
561 				if( dsource[0 .. i] in token_map ){
562 					ret ~= dsource[0 .. i];
563 					dsource.popFrontN(i);
564 					continue outer;
565 				}
566 			
567 			// identifier?
568 			if( dsource[0] == '_' || std.uni.isAlpha(dsource[0]) ){
569 				size_t i = 1;
570 				while( i < dsource.length && (dsource[i] == '_' || std.uni.isAlpha(dsource[i]) || isDigit(dsource[i])) ) i++;
571 				ret ~= dsource[0 .. i];
572 				dsource.popFrontN(i);
573 				continue;
574 			}
575 			
576 			// character literal?
577 			if( dsource[0] == '\'' ){
578 				size_t i = 1;
579 				while( dsource[i] != '\'' ){
580 					if( dsource[i] == '\\' ) i++;
581 					i++;
582 					enforce(i < dsource.length);
583 				}
584 				ret ~= dsource[0 .. i+1];
585 				dsource.popFrontN(i+1);
586 				continue;
587 			}
588 			
589 			// string? (incomplete!)
590 			if( dsource[0] == '"' ){
591 				size_t i = 1;
592 				while( dsource[i] != '"' ){
593 					if( dsource[i] == '\\' ) i++;
594 					i++;
595 					enforce(i < dsource.length);
596 				}
597 				ret ~= dsource[0 .. i+1];
598 				dsource.popFrontN(i+1);
599 				continue;
600 			}
601 			
602 			// number?
603 			if( isDigit(dsource[0]) || dsource[0] == '.' ){
604 				auto dscopy = dsource;
605 				parse!double(dscopy);
606 				ret ~= dsource[0 .. dsource.length-dscopy.length];
607 				dsource.popFrontN(dsource.length-dscopy.length);
608 				if( dsource.startsWith("u") ) dsource.popFront();
609 				else if( dsource.startsWith("f") ) dsource.popFront();
610 				continue;
611 			}
612 			
613 			ret ~= dsource[0 .. 1];
614 			dsource.popFront();
615 		}
616 		
617 		auto ret_ = new string[ret.length];
618 		foreach( i; 0 .. ret.length ) ret_[i] = to!string(ret[i]);
619 		return ret_;
620 	}
621 
622 	bool isDigit(dchar ch)
623 	{
624 		return ch >= '0' && ch <= '9';
625 	}
626 
627 	bool isIdent(string str)
628 	{
629 		if( str.length < 1 ) return false;
630 		foreach( i, dchar ch; str ){
631 			if( ch == '_' || std.uni.isAlpha(ch) ) continue;
632 			if( i > 0 && isDigit(ch) ) continue;
633 			return false;
634 		}
635 		return true;	
636 	}
637 
638 	string fullStrip(string s)
639 	{
640 		string chars = " \t\r\n";
641 		while( s.length > 0 && chars.countUntil(s[0]) >= 0 ) s.popFront();
642 		while( s.length > 0 && chars.countUntil(s[$-1]) >= 0 ) s.popBack();
643 		return s;
644 	}
645 
646 	void insertIntoTypeMap(Declaration decl)
647 	{
648 		string[] parts = split(decl.qualifiedName, ".");
649 		foreach( i; 0 .. parts.length ){
650 			auto partial_name = join(parts[i .. $], ".");
651 			m_typeMap[partial_name] = decl;
652 		}
653 	}
654 }
655