1 /**
2  * Copyright: Copyright (c) 2017 Wojciech Szęszoł. All rights reserved.
3  * Authors: Wojciech Szęszoł
4  * Version: Initial created: October 05, 2017
5  * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0)
6  */
7 module dstep.translator.MacroParser;
8 
9 import std.array;
10 import std.meta;
11 import std.traits;
12 import std.variant;
13 import std.stdio;
14 
15 import clang.c.Index;
16 import clang.Cursor;
17 import clang.Token;
18 import clang.Type;
19 import clang.Util;
20 import clang.SourceLocation;
21 import clang.SourceRange;
22 
23 enum bool isStringValue(alias T) =
24     is(typeof(T) : const char[]) &&
25     !isAggregateType!(typeof(T)) &&
26     !isStaticArray!(typeof(T));
27 
28 /**
29  * The accept family of functions parse tokens. In the case of successful parsing,
30  * the function advances the beginning of the tokens to the next token and returns true.
31  *
32  * The parsing is successful, if the first token in tokens is of the specified kind
33  * and its spelling matches one of the strings passed as Args.
34  * It assigns the spelling of the token to the spelling parameter.
35  */
36 bool accept(Args...)(ref Token[] tokens, ref string spelling, TokenKind kind)
37     if (Args.length > 0 && allSatisfy!(isStringValue, Args))
38 {
39     if (!tokens.empty && tokens.front.kind == kind)
40     {
41         foreach (arg; Args)
42         {
43             if (tokens.front.spelling == arg)
44             {
45                 tokens = tokens[1 .. $];
46                 spelling = arg;
47                 return true;
48             }
49         }
50     }
51 
52     return false;
53 }
54 
55 bool accept(ref Token[] tokens, ref string spelling, TokenKind kind)
56 {
57     if (!tokens.empty && tokens.front.kind == kind)
58     {
59         spelling = tokens.front.spelling;
60         tokens = tokens[1 .. $];
61         return true;
62     }
63 
64     return false;
65 }
66 
67 bool accept(Args...)(ref Token[] tokens, TokenKind kind)
68     if (allSatisfy!(isStringValue, Args))
69 {
70     if (!tokens.empty && tokens.front.kind == kind)
71     {
72         foreach (arg; Args)
73         {
74             if (tokens.front.spelling == arg)
75             {
76                 tokens = tokens[1 .. $];
77                 return true;
78             }
79         }
80     }
81 
82     return false;
83 }
84 
85 bool accept(Args...)(ref Token[] tokens, ref string spelling)
86     if (Args.length > 0 && allSatisfy!(isStringValue, Args))
87 {
88     if (!tokens.empty)
89     {
90         foreach (arg; Args)
91         {
92             if (tokens.front.spelling == arg)
93             {
94                 tokens = tokens[1 .. $];
95                 spelling = arg;
96                 return true;
97             }
98         }
99     }
100 
101     return false;
102 }
103 
104 bool acceptPunctuation(Args...)(ref Token[] tokens, ref string spelling)
105     if (allSatisfy!(isStringValue, Args))
106 {
107     return accept!(Args)(tokens, spelling, TokenKind.punctuation);
108 }
109 
110 bool acceptPunctuation(Args...)(ref Token[] tokens)
111     if (allSatisfy!(isStringValue, Args))
112 {
113     return accept!(Args)(tokens, TokenKind.punctuation);
114 }
115 
116 bool acceptIdentifier(ref Token[] tokens, ref string spelling)
117 {
118     return accept(tokens, spelling, TokenKind.identifier);
119 }
120 
121 bool acceptLiteral(ref Token[] tokens, ref string spelling)
122 {
123     return accept(tokens, spelling, TokenKind.literal);
124 }
125 
126 bool acceptKeyword(ref Token[] tokens, ref string spelling)
127 {
128     return accept(tokens, spelling, TokenKind.keyword);
129 }
130 
131 bool acceptStringLiteral(ref Token[] tokens, ref string spelling)
132 {
133     import std..string : startsWith, endsWith;
134 
135     if (!tokens.empty && tokens.front.kind == TokenKind.literal)
136     {
137         spelling = tokens.front.spelling;
138 
139         if (!spelling.startsWith(`"`) || !spelling.endsWith(`"`))
140             return false;
141 
142         tokens = tokens[1 .. $];
143         return true;
144     }
145 
146     return false;
147 }
148 
149 Expression parseLeftAssoc(ResultExpr, alias parseChild, Ops...)(
150     ref Token[] tokens,
151     Cursor[string] table,
152     bool defined) if (allSatisfy!(isStringValue, Ops))
153 {
154     import std.traits;
155     import std.range;
156 
157     auto local = tokens;
158 
159     ReturnType!parseChild[] exprs = [ parseChild(local, table, defined) ];
160     string[] ops = [];
161 
162     if (!exprs[0].hasValue)
163         return Expression.init;
164 
165     string op;
166     while (accept!(Ops)(local, op, TokenKind.punctuation))
167     {
168         exprs ~= parseChild(local, table, defined);
169 
170         if (!exprs[$ - 1].hasValue)
171             return Expression.init;
172 
173         ops ~= op;
174     }
175 
176     tokens = local;
177 
178     if (exprs.length == 1)
179         return exprs[0];
180 
181     ResultExpr result = new ResultExpr;
182     result.left = exprs[0];
183     result.right = exprs[1];
184     result.operator = ops[0];
185 
186     foreach (expr, fop; zip(exprs[2 .. $], ops[1 .. $]))
187     {
188         ResultExpr parent = new ResultExpr;
189         parent.left = result;
190         parent.right = expr;
191         parent.operator = fop;
192         result = parent;
193     }
194 
195     return Expression(result);
196 }
197 
198 struct Identifier
199 {
200     string spelling;
201 
202     string toString()
203     {
204         import std.format : format;
205 
206         return format("Identifier(spelling = %s)", spelling);
207     }
208 }
209 
210 struct TypeIdentifier
211 {
212     Type type;
213 }
214 
215 struct Literal
216 {
217     string spelling;
218 
219     this (string spelling)
220     {
221         this.spelling = spelling;
222     }
223 
224     string toString()
225     {
226         import std.format : format;
227 
228         return format("Literal(spelling = %s)", spelling);
229     }
230 }
231 
232 struct StringLiteral
233 {
234     string spelling;
235 
236     this (string spelling)
237     {
238         this.spelling = spelling;
239     }
240 
241     string toString()
242     {
243         import std.format : format;
244 
245         return format("Literal(spelling = %s)", spelling);
246     }
247 }
248 
249 struct StringifyExpr
250 {
251     string spelling;
252 
253     this (string spelling)
254     {
255         this.spelling = spelling;
256     }
257 
258     string toString()
259     {
260         import std.format : format;
261 
262         return format("StringifyExpr(spelling = %s)", spelling);
263     }
264 }
265 
266 class StringConcat
267 {
268     Expression[] substrings;
269 
270     this (Expression[] substrings)
271     {
272         this.substrings = substrings;
273     }
274 
275     override string toString()
276     {
277         import std.format : format;
278 
279         return format("StringConcat(substrings = %s)", substrings);
280     }
281 }
282 
283 class TokenConcat
284 {
285     Expression[] subexprs;
286 
287     this (Expression[] subexprs)
288     {
289         this.subexprs = subexprs;
290     }
291 
292     override string toString()
293     {
294         import std.format : format;
295 
296         return format("TokenConcat(subexprs = %s)", subexprs);
297     }
298 }
299 
300 class IndexExpr
301 {
302     Expression subexpr;
303     Expression index;
304 
305     override string toString()
306     {
307         import std.format : format;
308 
309         return format("IndexExpr(subexpr = %s, index = %s)", subexpr, index);
310     }
311 }
312 
313 class CallExpr
314 {
315     Expression expr;
316     Expression[] args;
317 
318     override string toString()
319     {
320         import std.format : format;
321 
322         return format("CallExpr(expr = %s, args = %s)", expr, args);
323     }
324 }
325 
326 class DotExpr
327 {
328     Expression subexpr;
329     string identifier;
330 
331     override string toString()
332     {
333         import std.format : format;
334 
335         return format(
336             "DotExpr(subexpr = %s, identifier = %s)",
337             subexpr,
338             identifier);
339     }
340 }
341 
342 class ArrowExpr : DotExpr
343 {
344     override string toString()
345     {
346         import std.format : format;
347 
348         return format(
349             "ArrowExpr(subexpr = %s, identifier = %s)",
350             subexpr,
351             identifier);
352     }
353 }
354 
355 class SubExpr
356 {
357     Expression subexpr;
358 
359     this (Expression subexpr)
360     {
361         this.subexpr = subexpr;
362     }
363 
364     override string toString()
365     {
366         import std.format : format;
367 
368         return format("SubExpr(subexpr = %s)", subexpr);
369     }
370 }
371 
372 class UnaryExpr
373 {
374     Expression subexpr;
375     string operator;
376     bool postfix = false;
377 
378     override string toString()
379     {
380         import std.format : format;
381 
382         return format(
383             "UnaryExpr(subexpr = %s, operator = %s)",
384             subexpr,
385             operator);
386     }
387 }
388 
389 struct DefinedExpr
390 {
391     string identifier;
392 
393     string toString()
394     {
395         import std.format : format;
396 
397         return format("DefinedExpr(identifier = %s)", identifier);
398     }
399 }
400 
401 struct SizeofType
402 {
403     Type type;
404 
405     string toString()
406     {
407         import std.format : format;
408 
409         return format("SizeofType(type = %s)", type);
410     }
411 }
412 
413 class CastExpr
414 {
415     Type type;
416     Expression subexpr;
417 
418     override string toString()
419     {
420         import std.format : format;
421 
422         return format(
423             "CastExpr(typename = %s, subexpr = %s)",
424             type,
425             subexpr);
426     }
427 }
428 
429 class BinaryExpr
430 {
431     Expression left;
432     Expression right;
433     string operator;
434 
435     override string toString()
436     {
437         import std.format : format;
438         import std.range : retro;
439         import std.algorithm.searching : findSplit;
440         import std.array : array;
441 
442         auto rname = findSplit(this.classinfo.name.retro, ".")[0].array;
443 
444         return format(
445             "%s(left = %s, right = %s, operator = %s)",
446             rname.retro,
447             left,
448             right,
449             operator);
450     }
451 }
452 
453 class MulExpr : BinaryExpr
454 { }
455 
456 class AddExpr : BinaryExpr
457 { }
458 
459 class SftExpr : BinaryExpr
460 { }
461 
462 class RelExpr : BinaryExpr
463 { }
464 
465 class EqlExpr : BinaryExpr
466 { }
467 
468 class AndExpr : BinaryExpr
469 { }
470 
471 class XorExpr : BinaryExpr
472 { }
473 
474 class OrExpr : BinaryExpr
475 { }
476 
477 class LogicalAndExpr : BinaryExpr
478 { }
479 
480 class LogicalOrExpr : BinaryExpr
481 { }
482 
483 class CondExpr
484 {
485     Expression expr;
486     Expression left;
487     Expression right;
488 
489     override string toString()
490     {
491         import std.format : format;
492         import std.range : retro;
493         import std.algorithm.searching : findSplit;
494         import std.array : array;
495 
496         auto rname = findSplit(this.classinfo.name.retro, ".")[0].array;
497 
498         return format(
499             "CondExpr(expr = %s, left = %s, right = %s)",
500             expr,
501             left,
502             right);
503     }
504 }
505 
506 alias Expression = Algebraic!(
507     Identifier,
508     TypeIdentifier,
509     Literal,
510     StringLiteral,
511     StringifyExpr,
512     StringConcat,
513     TokenConcat,
514     IndexExpr,
515     CallExpr,
516     DotExpr,
517     ArrowExpr,
518     SubExpr,
519     UnaryExpr,
520     DefinedExpr,
521     SizeofType,
522     CastExpr,
523     MulExpr,
524     AddExpr,
525     SftExpr,
526     RelExpr,
527     EqlExpr,
528     AndExpr,
529     XorExpr,
530     OrExpr,
531     LogicalAndExpr,
532     LogicalOrExpr,
533     CondExpr);
534 
535 Expression parseStringConcat(ref Token[] tokens)
536 {
537     import std.array;
538 
539     auto local = tokens;
540 
541     Expression[] substrings;
542 
543     while (true)
544     {
545         string spelling;
546 
547         if (acceptStringLiteral(local, spelling))
548         {
549             substrings ~= Expression(StringLiteral(spelling));
550         }
551         else if (accept!("#")(local, TokenKind.punctuation))
552         {
553             if (!accept(local, spelling, TokenKind.identifier))
554                 return Expression.init;
555 
556             substrings ~= Expression(StringifyExpr(spelling));
557         }
558         else
559         {
560             break;
561         }
562     }
563 
564     if (substrings.length == 0)
565         return Expression.init;
566 
567     tokens = local;
568 
569     if (substrings.length == 1)
570         return substrings.front;
571 
572     return Expression(new StringConcat(substrings));
573 }
574 
575 Expression parseTokenConcat(ref Token[] tokens)
576 {
577     Expression parseSubexpr(ref Token[] tokens)
578     {
579         string spelling;
580 
581         if (acceptIdentifier(tokens, spelling))
582             return Expression(Identifier(spelling));
583 
584         if (acceptLiteral(tokens, spelling))
585             return Expression(Literal(spelling));
586 
587         return Expression.init;
588     }
589 
590     auto local = tokens;
591 
592     Expression[] subexprs;
593     auto first = parseSubexpr(local);
594 
595     if (first.hasValue)
596     {
597         if (!acceptPunctuation!("##")(local))
598             return Expression.init;
599 
600         auto expr = parseSubexpr(local);
601 
602         if (expr.hasValue)
603         {
604             subexprs ~= first;
605             subexprs ~= expr;
606 
607             tokens = local;
608 
609             while (acceptPunctuation!("##")(local))
610             {
611                 expr = parseSubexpr(local);
612 
613                 if (expr.hasValue)
614                 {
615                     subexprs ~= expr;
616                     tokens = local;
617                 }
618                 else
619                 {
620                     break;
621                 }
622             }
623 
624             return Expression(new TokenConcat(subexprs));
625         }
626     }
627 
628     return Expression.init;
629 }
630 
631 Expression parsePrimaryExpr(ref Token[] tokens, Cursor[string] table, bool defined)
632 {
633     string spelling;
634 
635     auto type = parseTypeName(tokens, table);
636 
637     if (type.isValid)
638         return Expression(TypeIdentifier(type));
639 
640     if (accept(tokens, spelling, TokenKind.identifier))
641         return Expression(Identifier(spelling));
642 
643     auto local = tokens;
644 
645     auto substrings = parseStringConcat(local);
646 
647     if (substrings.hasValue)
648     {
649         tokens = local;
650         return substrings;
651     }
652 
653     if (accept(local, spelling, TokenKind.literal))
654     {
655         tokens = local;
656         return Expression(Literal(spelling));
657     }
658 
659     if (!accept!("(")(local, TokenKind.punctuation))
660         return Expression.init;
661 
662     auto subexpr = parseExpr(local, table, defined);
663 
664     if (!subexpr.hasValue)
665         return Expression.init;
666 
667     if (!accept!(")")(local, TokenKind.punctuation))
668         return Expression.init;
669 
670     tokens = local;
671 
672     return Expression(new SubExpr(subexpr));
673 }
674 
675 Expression parseArg(ref Token[] tokens, Cursor[string] table, bool defined)
676 {
677     auto local = tokens;
678 
679     auto expression = parseSftExpr(local, table, defined);
680 
681     if (expression.hasValue)
682     {
683         tokens = local;
684         return expression;
685     }
686 
687     auto type = parseTypeName(local, table);
688 
689     if (type.isValid)
690     {
691         tokens = local;
692         expression = TypeIdentifier(type);
693     }
694 
695     return expression;
696 }
697 
698 Expression[] parseArgsList(ref Token[] tokens, Cursor[string] table, bool defined)
699 {
700     auto local = tokens;
701 
702     Expression[] exprs = [ parseArg(local, table, defined) ];
703 
704     if (!exprs[0].hasValue)
705         return null;
706 
707     while (true)
708     {
709         if (acceptPunctuation!(",")(local))
710         {
711             Expression expr = parseArg(local, table, defined);
712 
713             if (!expr.hasValue)
714                 break;
715 
716             exprs ~= expr;
717         }
718         else
719         {
720             break;
721         }
722     }
723 
724     tokens = local;
725 
726     return exprs;
727 }
728 
729 Expression parsePostfixExpr(ref Token[] tokens, Cursor[string] table, bool defined)
730 {
731     auto local = tokens;
732 
733     Expression expr = parsePrimaryExpr(local, table, defined);
734 
735     if (!expr.hasValue)
736         return expr;
737 
738     string spelling;
739 
740     while (true)
741     {
742         if (acceptPunctuation!("[")(local))
743         {
744             auto index = parseExpr(local, table, defined);
745 
746             if (!index.hasValue)
747                 break;
748 
749             if (!acceptPunctuation!("]")(local))
750                 break;
751 
752             IndexExpr subexpr = new IndexExpr;
753             subexpr.subexpr = expr;
754             subexpr.index = index;
755             expr = subexpr;
756         }
757         else if (acceptPunctuation!("(")(local))
758         {
759             if (acceptPunctuation!(")")(local))
760             {
761                 CallExpr subexpr = new CallExpr;
762                 subexpr.expr = expr;
763                 subexpr.args = [];
764                 expr = subexpr;
765             }
766             else
767             {
768                 auto args = parseArgsList(local, table, defined);
769 
770                 if (args is null)
771                     break;
772 
773                 if (!acceptPunctuation!(")")(local))
774                     break;
775 
776                 CallExpr subexpr = new CallExpr;
777                 subexpr.expr = expr;
778                 subexpr.args = args;
779                 expr = subexpr;
780             }
781         }
782         else if (acceptPunctuation!(".")(local) && acceptIdentifier(local, spelling))
783         {
784             DotExpr subexpr = new DotExpr;
785             subexpr.subexpr = expr;
786             subexpr.identifier = spelling;
787             expr = subexpr;
788         }
789         else if (acceptPunctuation!("->")(local) && acceptIdentifier(local, spelling))
790         {
791             ArrowExpr subexpr = new ArrowExpr;
792             subexpr.subexpr = expr;
793             subexpr.identifier = spelling;
794             expr = subexpr;
795         }
796         else if (acceptPunctuation!("++", "--")(local, spelling))
797         {
798             UnaryExpr subexpr = new UnaryExpr;
799             subexpr.subexpr = expr;
800             subexpr.operator = spelling;
801             subexpr.postfix = true;
802             expr = subexpr;
803         }
804         else
805         {
806             break;
807         }
808     }
809 
810     tokens = local;
811 
812     return expr;
813 }
814 
815 Expression parseSizeofType(ref Token[] tokens, Cursor[string] table)
816 {
817     auto local = tokens;
818 
819     if (acceptPunctuation!("(")(local))
820     {
821         Type type = parseTypeName(local, table);
822 
823         if (type.isValid && acceptPunctuation!(")")(local))
824         {
825             SizeofType expr = SizeofType();
826             expr.type = type;
827             tokens = local;
828             return Expression(expr);
829         }
830     }
831 
832     return Expression.init;
833 }
834 
835 Expression parseDefinedExpr(ref Token[] tokens)
836 {
837     auto local = tokens;
838 
839     if (accept!("defined")(local, TokenKind.identifier))
840     {
841         string spelling;
842 
843         if (acceptIdentifier(local, spelling))
844         {
845             auto expr = DefinedExpr();
846             expr.identifier = spelling;
847             tokens = local;
848             return Expression(expr);
849         }
850 
851         if (acceptPunctuation!("(")(local) &&
852             acceptIdentifier(local, spelling) &&
853             acceptPunctuation!(")")(local))
854         {
855             auto expr = DefinedExpr();
856             expr.identifier = spelling;
857             tokens = local;
858             return Expression(expr);
859         }
860     }
861 
862     return Expression.init;
863 }
864 
865 Expression parseUnaryExpr(ref Token[] tokens, Cursor[string] table, bool defined)
866 {
867     auto local = tokens;
868 
869     string spelling;
870 
871     if (accept!("++", "--")(local, spelling, TokenKind.punctuation))
872     {
873         Expression subexpr = parseUnaryExpr(local, table, defined);
874 
875         if (subexpr.hasValue)
876         {
877             UnaryExpr expr = new UnaryExpr;
878             expr.subexpr = subexpr;
879             expr.operator = spelling;
880             tokens = local;
881             return Expression(expr);
882         }
883     }
884 
885     if (accept!("&", "*", "+", "-", "~", "!")(local, spelling, TokenKind.punctuation))
886     {
887         Expression subexpr = parseCastExpr(local, table, defined);
888 
889         if (subexpr.hasValue)
890         {
891             UnaryExpr expr = new UnaryExpr;
892             expr.subexpr = subexpr;
893             expr.operator = spelling;
894             tokens = local;
895             return Expression(expr);
896         }
897     }
898 
899     if (accept!("sizeof")(local, spelling, TokenKind.keyword))
900     {
901         auto sizeofExpr = parseSizeofType(local, table);
902 
903         if (sizeofExpr.hasValue)
904         {
905             tokens = local;
906             return sizeofExpr;
907         }
908 
909         Expression subexpr = parseUnaryExpr(local, table, defined);
910 
911         if (subexpr.hasValue)
912         {
913             UnaryExpr expr = new UnaryExpr;
914             expr.subexpr = subexpr;
915             expr.operator = spelling;
916             tokens = local;
917             return Expression(expr);
918         }
919     }
920 
921     if (defined)
922     {
923         auto expr = parseDefinedExpr(local);
924 
925         if (expr.hasValue)
926         {
927             tokens = local;
928             return expr;
929         }
930     }
931 
932     return parsePostfixExpr(tokens, table, defined);
933 }
934 
935 Expression parseCastExpr(ref Token[] tokens, Cursor[string] table, bool defined)
936 {
937     auto local = tokens;
938 
939     if (!accept!("(")(local, TokenKind.punctuation))
940         return parseUnaryExpr(tokens, table, defined);
941 
942     Type type = parseTypeName(local, table);
943 
944     if (!type.isValid)
945         return parseUnaryExpr(tokens, table, defined);
946 
947     if (!accept!(")")(local, TokenKind.punctuation))
948         return parseUnaryExpr(tokens, table, defined);
949 
950     auto subexpr = parseCastExpr(local, table, defined);
951 
952     if (!subexpr.hasValue)
953         return parseUnaryExpr(tokens, table, defined);
954 
955     tokens = local;
956 
957     CastExpr result = new CastExpr;
958     result.type = type;
959     result.subexpr = subexpr;
960 
961     return Expression(result);
962 }
963 
964 alias parseMulExpr = parseLeftAssoc!(MulExpr, parseCastExpr, "*", "/", "%");
965 alias parseAddExpr = parseLeftAssoc!(AddExpr, parseMulExpr, "+", "-");
966 alias parseSftExpr = parseLeftAssoc!(SftExpr, parseAddExpr, "<<", ">>");
967 alias parseRelExpr = parseLeftAssoc!(RelExpr, parseSftExpr, "<", ">", "<=", ">=");
968 alias parseEqlExpr = parseLeftAssoc!(EqlExpr, parseRelExpr, "==", "!=");
969 alias parseAndExpr = parseLeftAssoc!(AndExpr, parseEqlExpr, "&");
970 alias parseXorExpr = parseLeftAssoc!(XorExpr, parseAndExpr, "^");
971 alias parseOrExpr = parseLeftAssoc!(OrExpr, parseXorExpr, "|");
972 alias parseLogicalAndExpr = parseLeftAssoc!(LogicalAndExpr, parseOrExpr, "&&");
973 alias parseLogicalOrExpr = parseLeftAssoc!(LogicalOrExpr, parseLogicalAndExpr, "||");
974 
975 Expression parseCondExpr(ref Token[] tokens, Cursor[string] table, bool defined)
976 {
977     auto local = tokens;
978 
979     Expression expr = parseLogicalOrExpr(local, table, defined);
980 
981     if (!expr.hasValue)
982         return Expression.init;
983 
984     tokens = local;
985 
986     if (acceptPunctuation!("?")(local))
987     {
988         Expression left = parseExpr(local, table, defined);
989 
990         if (left.hasValue && acceptPunctuation!(":")(local))
991         {
992             Expression right = parseCondExpr(local, table, defined);
993 
994             if (right.hasValue)
995             {
996                 CondExpr supexpr = new CondExpr;
997                 supexpr.expr = expr;
998                 supexpr.left = left;
999                 supexpr.right = right;
1000                 expr = supexpr;
1001 
1002                 tokens = local;
1003             }
1004         }
1005     }
1006 
1007     return expr;
1008 }
1009 
1010 bool parseBasicSpecifier(ref Token[] tokens, ref string spelling, Cursor[string] table)
1011 {
1012     import std.meta : AliasSeq;
1013 
1014     alias specifiers = AliasSeq!(
1015         "void",
1016         "char",
1017         "short",
1018         "int",
1019         "long",
1020         "float",
1021         "double",
1022         "signed",
1023         "unsigned",
1024         // "__complex__", TBD
1025         // "_Complex", TBD
1026         "bool",
1027         "_Bool");
1028 
1029     return accept!(specifiers)(tokens, spelling);
1030 }
1031 
1032 bool parseRecordSpecifier(ref Token[] tokens, ref Type type, Cursor[string] table)
1033 {
1034     auto local = tokens;
1035     string spelling;
1036     string keywordType;
1037 
1038     if (accept!("struct", "union")(local, keywordType, TokenKind.keyword) &&
1039         acceptIdentifier(local, spelling))
1040     {
1041         if (auto ptr = (keywordType ~ " " ~ spelling in table))
1042         {
1043             type = ptr.type;
1044             tokens = local;
1045             return true;
1046         }
1047     }
1048 
1049     return false;
1050 }
1051 
1052 bool parseEnumSpecifier(ref Token[] tokens, ref Type type, Cursor[string] table)
1053 {
1054     auto local = tokens;
1055     string spelling;
1056 
1057     if (acceptIdentifier(local, spelling))
1058     {
1059         if (auto ptr = ("enum " ~ spelling in table))
1060         {
1061             type = ptr.type;
1062             tokens = local;
1063             return true;
1064         }
1065     }
1066 
1067     return false;
1068 }
1069 
1070 bool parseTypedefName(ref Token[] tokens, ref Type type, Cursor[string] table)
1071 {
1072     auto local = tokens;
1073     string spelling;
1074 
1075     if (acceptIdentifier(local, spelling))
1076     {
1077         if (auto ptr = (spelling in table))
1078         {
1079             type = Type.makeTypedef(spelling, ptr.type.canonical);
1080 
1081             tokens = local;
1082 
1083             return true;
1084         }
1085     }
1086 
1087     return false;
1088 }
1089 
1090 bool parseComplexSpecifier(ref Token[] tokens, ref Type type, Cursor[string] table)
1091 {
1092     return parseRecordSpecifier(tokens, type, table) ||
1093         parseEnumSpecifier(tokens, type, table) ||
1094         parseTypedefName(tokens, type, table);
1095 }
1096 
1097 bool parseTypeQualifier(ref Token[] tokens, ref string spelling)
1098 {
1099     import std.meta : AliasSeq;
1100 
1101     alias qualifiers = AliasSeq!(
1102         "const",
1103         "volatile",
1104         "_Atomic");
1105 
1106     return accept!(qualifiers)(tokens, spelling);
1107 }
1108 
1109 bool parseSpecifierQualifierList(
1110     ref Token[] tokens,
1111     ref Type type,
1112     Cursor[string] table)
1113 {
1114     auto local = tokens;
1115 
1116     Set!string specifiers;
1117     Set!string qualifiers;
1118 
1119     while (true)
1120     {
1121         string spelling;
1122 
1123         if (parseBasicSpecifier(local, spelling, table))
1124         {
1125             if (type.isValid)
1126                 return false;
1127 
1128             if (specifiers.contains(spelling))
1129             {
1130                 if (spelling == "long")
1131                 {
1132                     if (specifiers.contains("__llong"))
1133                         return false;
1134                     else
1135                         spelling = "__llong";
1136                 }
1137                 else
1138                 {
1139                     return false;
1140                 }
1141             }
1142 
1143             specifiers.add(spelling);
1144         }
1145         else if (parseComplexSpecifier(local, type, table))
1146         {
1147             if (specifiers.length != 0)
1148                 return false;
1149         }
1150         else if (parseTypeQualifier(local, spelling))
1151         {
1152             if (qualifiers.contains(spelling))
1153                 return false;
1154 
1155             qualifiers.add(spelling);
1156         }
1157         else
1158         {
1159             break;
1160         }
1161     }
1162 
1163     if (specifiers.length != 0)
1164     {
1165         if(!basicSpecifierListToType(type, specifiers))
1166             return false;
1167     }
1168 
1169     if (qualifiers.contains("const"))
1170         type.isConst = true;
1171 
1172     if (qualifiers.contains("volatile"))
1173         type.isVolatile = true;
1174 
1175     tokens = local;
1176 
1177     return true;
1178 }
1179 
1180 bool parseQualifierList(
1181     ref Token[] tokens,
1182     ref Type type)
1183 {
1184     auto local = tokens;
1185 
1186     Set!string qualifiers;
1187 
1188     while (true)
1189     {
1190         string spelling;
1191 
1192         if (parseTypeQualifier(local, spelling))
1193         {
1194             if (qualifiers.contains(spelling))
1195                 return false;
1196 
1197             qualifiers.add(spelling);
1198         }
1199         else
1200         {
1201             break;
1202         }
1203     }
1204 
1205     if (qualifiers.contains("const"))
1206         type.isConst = true;
1207 
1208     if (qualifiers.contains("volatile"))
1209         type.isVolatile = true;
1210 
1211     tokens = local;
1212 
1213     return true;
1214 }
1215 
1216 bool basicSpecifierListToType(ref Type type, Set!string specifiers)
1217 {
1218     if (specifiers.contains("void"))
1219     {
1220         if (specifiers.length != 1)
1221             return false;
1222 
1223         type = Type(CXTypeKind.void_, "void");
1224         return true;
1225     }
1226 
1227     if (specifiers.contains("bool") || specifiers.contains("_Bool"))
1228     {
1229         if (specifiers.length != 1)
1230             return false;
1231 
1232         type = Type(CXTypeKind.bool_, "bool");
1233         return true;
1234     }
1235 
1236     if (specifiers.contains("float"))
1237     {
1238         if (specifiers.length != 1)
1239             return false;
1240 
1241         type = Type(CXTypeKind.float_, "float");
1242         return true;
1243     }
1244 
1245     if (specifiers.contains("double"))
1246     {
1247         if (specifiers.contains("long"))
1248         {
1249             if (specifiers.length != 2)
1250                 return false;
1251 
1252             type = Type(CXTypeKind.longDouble, "long double");
1253 
1254             return true;
1255         }
1256 
1257         if (specifiers.length != 1)
1258             return false;
1259 
1260         type = Type(CXTypeKind.double_, "double");
1261 
1262         return true;
1263     }
1264 
1265     if ((specifiers.contains("signed") && specifiers.contains("unsigned")) ||
1266         (specifiers.contains("char") && specifiers.contains("short")) ||
1267         (specifiers.contains("char") && specifiers.contains("long")) ||
1268         (specifiers.contains("char") && specifiers.contains("__llong")) ||
1269         (specifiers.contains("short") && specifiers.contains("long")) ||
1270         (specifiers.contains("short") && specifiers.contains("__llong")))
1271         return false;
1272 
1273     if (specifiers.contains("char"))
1274     {
1275         if (specifiers.contains("signed"))
1276         {
1277             if (specifiers.length != 2)
1278                 return false;
1279 
1280             type = Type(CXTypeKind.sChar, "signed char");
1281         }
1282         else if (specifiers.contains("unsigned"))
1283         {
1284             if (specifiers.length != 2)
1285                 return false;
1286 
1287             type = Type(CXTypeKind.uChar, "unsigned char");
1288         }
1289         else
1290         {
1291             if (specifiers.length != 1)
1292                 return false;
1293 
1294             type = Type(CXTypeKind.charS, "char");
1295         }
1296 
1297         return true;
1298     }
1299 
1300     if (specifiers.contains("short"))
1301     {
1302         if (specifiers.contains("unsigned"))
1303             type = Type(CXTypeKind.uShort, "unsigned short");
1304         else
1305             type = Type(CXTypeKind.short_, "short");
1306 
1307         return true;
1308     }
1309 
1310     if (specifiers.contains("__llong"))
1311     {
1312         if (specifiers.contains("unsigned"))
1313             type = Type(CXTypeKind.uLongLong, "unsigned long long");
1314         else
1315             type = Type(CXTypeKind.longLong, "long long");
1316 
1317         return true;
1318     }
1319 
1320     if (specifiers.contains("long"))
1321     {
1322         if (specifiers.contains("unsigned"))
1323             type = Type(CXTypeKind.uLong, "unsigned long");
1324         else
1325             type = Type(CXTypeKind.long_, "long");
1326 
1327         return true;
1328     }
1329 
1330     if (specifiers.contains("int"))
1331     {
1332         if (specifiers.contains("unsigned"))
1333             type = Type(CXTypeKind.uInt, "unsigned int");
1334         else
1335             type = Type(CXTypeKind.int_, "int");
1336 
1337         return true;
1338     }
1339 
1340     if (specifiers.contains("unsigned"))
1341     {
1342         type = Type(CXTypeKind.uInt, "unsigned int");
1343         return true;
1344     }
1345 
1346     if (specifiers.contains("signed"))
1347     {
1348         type = Type(CXTypeKind.int_, "int");
1349         return true;
1350     }
1351 
1352     return false;
1353 }
1354 
1355 bool parsePointer(ref Token[] tokens, ref Type type)
1356 {
1357     if (acceptPunctuation!("*")(tokens))
1358     {
1359         type = Type.makePointer(type);
1360 
1361         if (!parsePointer(tokens, type))
1362         {
1363             if (parseQualifierList(tokens, type))
1364                 parsePointer(tokens, type);
1365         }
1366 
1367         return true;
1368     }
1369     else
1370     {
1371         return false;
1372     }
1373 }
1374 
1375 bool parseAbstractDeclarator(ref Token[] tokens, ref Type type, Cursor[string] table)
1376 {
1377     return parsePointer(tokens, type);
1378 }
1379 
1380 Type parseTypeName(ref Token[] tokens, Cursor[string] table)
1381 {
1382     auto local = tokens;
1383 
1384     Type type;
1385 
1386     if (!parseSpecifierQualifierList(local, type, table))
1387         return type;
1388 
1389     parseAbstractDeclarator(local, type, table);
1390 
1391     tokens = local;
1392 
1393     return type;
1394 }
1395 
1396 Expression parseExpr(ref Token[] tokens, Cursor[string] table, bool defined)
1397 {
1398     auto concatExpr = parseTokenConcat(tokens);
1399 
1400     if (concatExpr.hasValue)
1401         return concatExpr;
1402 
1403     auto condExpr = parseCondExpr(tokens, table, defined);
1404 
1405     if (condExpr.hasValue)
1406         return condExpr;
1407 
1408     return Expression.init;
1409 }
1410 
1411 Expression parseExpr(ref Token[] tokens, bool defined)
1412 {
1413     Cursor[string] table;
1414 
1415     return parseCondExpr(tokens, table, defined);
1416 }
1417 
1418 Expression parseEnumMember(Token[] tokens, Cursor[string] table)
1419 {
1420     string member;
1421 
1422     if (!acceptIdentifier(tokens, member))
1423         return Expression.init;
1424 
1425     if (!acceptPunctuation!("=")(tokens, member))
1426         return Expression.init;
1427 
1428     auto expression = parseExpr(tokens, table, false);
1429 
1430     if (tokens.empty)
1431         return expression;
1432     else
1433         return Expression.init;
1434 }
1435 
1436 string[] parseMacroParams(ref Token[] tokens)
1437 {
1438     auto local = tokens;
1439 
1440     string[] params;
1441 
1442     string param;
1443 
1444     if (!accept(local, param, TokenKind.identifier))
1445         return [];
1446 
1447     params ~= param;
1448 
1449     while (accept!(",")(local, TokenKind.punctuation))
1450     {
1451         if (!accept(local, param, TokenKind.identifier))
1452             return null;
1453 
1454         params ~= param;
1455     }
1456 
1457     tokens = local;
1458 
1459     return params;
1460 }
1461 
1462 class MacroDefinition
1463 {
1464     Cursor cursor;
1465     string spelling;
1466     string[] params;
1467     bool constant;
1468     Expression expr;
1469 
1470     override string toString()
1471     {
1472         import std.format : format;
1473 
1474         return format(
1475             "MacroDefinition(spelling = %s, params = %s, constant = %s, expr = %s)",
1476             spelling,
1477             params,
1478             constant,
1479             expr);
1480     }
1481 
1482     void dumpAST(ref Appender!string result, size_t indent)
1483     {
1484         import std.format;
1485         import std.array : replicate;
1486         import std..string : join;
1487 
1488         result.put(" ".replicate(indent));
1489 
1490         if (constant)
1491             formattedWrite(result, "MacroDefinition %s", spelling);
1492         else
1493             formattedWrite(result, "MacroDefinition %s(%s)", spelling, join(params, ", "));
1494     }
1495 
1496     string dumpAST()
1497     {
1498         auto result = Appender!string();
1499         dumpAST(result, 0);
1500         return result.data;
1501     }
1502 }
1503 
1504 MacroDefinition parseMacroDefinition(
1505     ref Token[] tokens,
1506     Cursor[string] table,
1507     bool defined = false)
1508 {
1509     auto local = tokens;
1510 
1511     if (!accept!("#")(local, TokenKind.punctuation))
1512         return null;
1513 
1514     if (!accept!("define")(local, TokenKind.identifier))
1515         return null;
1516 
1517     MacroDefinition result = parsePartialMacroDefinition(local, table, defined);
1518 
1519     if (result !is null)
1520         tokens = local;
1521 
1522     return result;
1523 }
1524 
1525 MacroDefinition parsePartialMacroDefinition(
1526     ref Token[] tokens,
1527     Cursor[string] table,
1528     bool defined = false)
1529 {
1530     auto local = tokens;
1531 
1532     MacroDefinition result = new MacroDefinition;
1533 
1534     if (!accept(local, result.spelling, TokenKind.identifier))
1535         return null;
1536 
1537     // Functional macros mustn't contain space before parentheses of parameter list.
1538     bool space =
1539         tokens.length > 2 &&
1540         tokens[0].extent.end.offset == tokens[1].extent.start.offset;
1541 
1542     if (space && accept!("(")(local, TokenKind.punctuation))
1543     {
1544         if (!accept!(")")(local, TokenKind.punctuation))
1545         {
1546             result.params = parseMacroParams(local);
1547 
1548             if (!accept!(")")(local, TokenKind.punctuation))
1549                 return null;
1550         }
1551     }
1552     else
1553     {
1554         result.constant = true;
1555     }
1556 
1557     result.expr = parseExpr(local, table, defined);
1558 
1559     if (!local.empty)
1560     {
1561         return null;
1562     }
1563     else
1564     {
1565         tokens = local;
1566         return result;
1567     }
1568 }
1569 
1570 MacroDefinition[] parseMacroDefinitions(Range)(
1571     Range cursors,
1572     Cursor[string] types)
1573 {
1574     import std.algorithm;
1575     import std.array;
1576 
1577     alias predicate = (Cursor cursor) =>
1578         cursor.kind == CXCursorKind.macroDefinition;
1579     auto definitions = cursors.filter!predicate();
1580 
1581     auto macroDefinitions = appender!(MacroDefinition[]);
1582 
1583     foreach (definition; definitions) {
1584         auto tokens = definition.tokens;
1585         auto parsed = parsePartialMacroDefinition(tokens, types);
1586 
1587         if (parsed !is null && parsed.expr.hasValue)
1588         {
1589             parsed.cursor = definition;
1590             macroDefinitions.put(parsed);
1591         }
1592     }
1593 
1594     return macroDefinitions.data;
1595 }