1 /**
2 Copyright: Copyright (c) 2020, Joakim Brännström. All rights reserved.
3 License: MPL-2
4 Author: Joakim Brännström (joakim.brannstrom@gmx.com)
5 
6 This Source Code Form is subject to the terms of the Mozilla Public License,
7 v.2.0. If a copy of the MPL was not distributed with this file, You can obtain
8 one at http://mozilla.org/MPL/2.0/.
9 
10 A language independent AST specific for generating mutants both via the plain
11 source code manipulation but also mutant schematas.
12 */
13 module dextool.plugin.mutate.backend.analyze.ast;
14 
15 import logger = std.experimental.logger;
16 import std.algorithm : map, filter, among;
17 import std.array : Appender, appender, empty;
18 import std.exception : collectException;
19 import std.format : formattedWrite, format;
20 import std.meta : AliasSeq;
21 import std.range : isOutputRange;
22 
23 import my.optional;
24 import sumtype;
25 
26 import dextool.type : AbsolutePath, Path;
27 
28 static import dextool.plugin.mutate.backend.type;
29 
30 @safe:
31 
32 struct Ast {
33     import std.experimental.allocator.mallocator : Mallocator;
34     import my.alloc.class_;
35 
36     /// The language the mutation AST is based on.
37     dextool.plugin.mutate.backend.type.Language lang;
38 
39     Location[Node] locs;
40 
41     // a node can have a type
42     TypeId[Node] nodeTypes;
43     Types types;
44 
45     // a node can have been resolved to a symbolic value.
46     SymbolId[Node] nodeSymbols;
47     Symbols symbols;
48 
49     Node root;
50 
51     // Change the path and thus string in the saved locations to reduce the used memory.
52     Dedup!AbsolutePath paths;
53 
54     private {
55         Bundle!Mallocator bundle;
56     }
57 
58     ~this() nothrow {
59         release;
60     }
61 
62     T make(T, Args...)(auto ref Args args) {
63         import std.functional : forward;
64 
65         return bundle.make!T(forward!args);
66     }
67 
68     /// Release all nodes by destroying them and releasing the memory
69     void release() nothrow @trusted {
70         if (!bundle.empty) {
71             if (auto v = root in locs)
72                 logger.tracef("released AST for %s with objects %s", v.file,
73                         bundle.length).collectException;
74             else
75                 logger.tracef("released AST with %s objects", bundle.length).collectException;
76         }
77         bundle.release;
78 
79         paths = typeof(paths).init;
80         nodeTypes = null;
81         nodeSymbols = null;
82         locs = null;
83         root = null;
84     }
85 
86     void releaseCache() {
87         paths.release;
88     }
89 
90     void accept(VisitorT)(VisitorT v) {
91         mixin(mixinSwitch("root"));
92     }
93 
94     void put(Node n, Location l) {
95         // TODO: deduplicate the path because it will otherwise take up so much
96         // memory.....
97         l.file = paths.dedup(l.file);
98         locs[n] = l;
99     }
100 
101     void put(Node n, TypeId id) {
102         nodeTypes[n] = id;
103     }
104 
105     void put(Node n, SymbolId id) {
106         nodeSymbols[n] = id;
107     }
108 
109     Location location(Node n) {
110         if (auto v = n in locs) {
111             return *v;
112         }
113         return Location.init;
114     }
115 
116     Type type(Node n) {
117         if (auto v = n in nodeTypes) {
118             return types.get(*v);
119         }
120         return null;
121     }
122 
123     Optional!TypeId typeId(Node n) {
124         if (auto v = n in nodeTypes) {
125             return some(*v);
126         }
127         return none!TypeId;
128     }
129 
130     Symbol symbol(Node n) {
131         if (auto v = n in nodeSymbols) {
132             return symbols.get(*v);
133         }
134         return null;
135     }
136 
137     string toString() @safe {
138         auto buf = appender!string;
139         toString(buf);
140         return buf.data;
141     }
142 
143     void toString(Writer)(ref Writer w) if (isOutputRange!(Writer, char)) {
144         import std.range : put;
145 
146         formattedWrite(w, "Source language: %s\n", lang);
147 
148         auto res = () @trusted {
149             auto dump = new AstPrintVisitor(&this);
150             this.accept(dump);
151             return dump.buf.data;
152         }();
153         put(w, res);
154 
155         put(w, "Types:");
156         put(w, "\n");
157         types.toString(w);
158         put(w, "Symbols:");
159         put(w, "\n");
160         symbols.toString(w);
161     }
162 }
163 
164 class AstPrintVisitor : DepthFirstVisitor {
165     import std.range : put, repeat;
166 
167     Appender!string buf;
168     int depth;
169     int prevDepth;
170     char[] indent;
171     Ast* ast;
172 
173     this(Ast* ast) {
174         this.ast = ast;
175     }
176 
177     void toBuf(Node n) {
178         import std.conv : to;
179         import colorlog : color, Color;
180 
181         if (depth == 0) {
182             indent = null;
183         } else if (depth == prevDepth) {
184         } else if (depth > prevDepth) {
185             const diff = (depth - prevDepth - 1) * 2;
186             if (indent.length == 0) {
187             } else if (indent[$ - 2] == '`') {
188                 indent[$ - 1] = ' ';
189                 indent[$ - 2] = ' ';
190             } else {
191                 indent[$ - 1] = ' ';
192             }
193 
194             foreach (_; 0 .. diff)
195                 indent ~= "  ";
196 
197             if (n.children.length <= 1)
198                 indent ~= "`-";
199             else
200                 indent ~= "|-";
201         } else {
202             const diff = (prevDepth - depth) * 2;
203             indent = indent[0 .. $ - diff];
204 
205             if (indent.length != 0 && indent[$ - 2 .. $] == "| ") {
206                 indent[$ - 1] = '-';
207             }
208         }
209         put(buf, indent);
210 
211         void printNode() {
212             auto bl = () {
213                 if (n.blacklist)
214                     return " blacklist".color(Color.magenta).toString;
215                 return "";
216             }();
217             auto schemaBl = () {
218                 if (n.schemaBlacklist)
219                     return " !schema".color(Color.magenta).toString;
220                 return "";
221             }();
222             auto covBl = () {
223                 if (n.covBlacklist)
224                     return " !cov".color(Color.magenta).toString;
225                 return "";
226             }();
227             formattedWrite(buf, "%s %s%s%s%s",
228                     n.kind.to!string.color(Color.lightGreen),
229                     n.id.to!string.color(Color.lightYellow), bl, schemaBl, covBl);
230         }
231 
232         void printTypeSymbol(Node n) {
233             if (auto tyId = n in ast.nodeTypes) {
234                 auto ty = ast.types.get(*tyId);
235                 formattedWrite(buf, " %X", tyId.value);
236             }
237             if (auto syId = n in ast.nodeSymbols) {
238                 auto sy = ast.symbols.get(*syId);
239                 formattedWrite(buf, " %X:%s", syId.value, sy.value);
240             }
241         }
242 
243         switch (n.kind) {
244         case Kind.Function:
245             printNode;
246             printTypeSymbol((cast(Function) n).return_);
247             break;
248         case Kind.VarDecl:
249             printNode;
250             if ((cast(VarDecl) n).isConst) {
251                 put(buf, " const");
252             }
253             break;
254         default:
255             printNode;
256             if (isExpression(n.kind)) {
257                 printTypeSymbol(n);
258             }
259         }
260 
261         if (auto tmp = cast(BinaryOp) n) {
262             if (tmp.lhs !is null)
263                 formattedWrite(buf, " l:%s", tmp.lhs.id);
264             if (tmp.rhs !is null)
265                 formattedWrite(buf, " r:%s", tmp.rhs.id);
266         }
267 
268         if (auto l = ast.location(n)) {
269             formattedWrite(buf, " <%s:%s>", l.file.color(Color.cyan), l.posToString);
270         }
271         put(buf, "\n");
272 
273         prevDepth = depth;
274     }
275 
276     static foreach (N; Nodes) {
277         override void visit(N n) {
278             toBuf(n);
279             ++depth;
280 
281             auto op = () @trusted {
282                 if (auto v = cast(BinaryOp) n) {
283                     return v.operator;
284                 } else if (auto v = cast(UnaryOp) n) {
285                     return v.operator;
286                 }
287                 return null;
288             }();
289             if (op !is null) {
290                 visit(op);
291             }
292 
293             accept(n, this);
294             --depth;
295         }
296     }
297 }
298 
299 /// The interval in bytes that the node occupy. It is a closed->open set.
300 alias Interval = dextool.plugin.mutate.backend.type.Offset;
301 alias SourceLoc = dextool.plugin.mutate.backend.type.SourceLoc;
302 alias SourceLocRange = dextool.plugin.mutate.backend.type.SourceLocRange;
303 
304 struct Location {
305     import std.range : isOutputRange;
306 
307     AbsolutePath file;
308     Interval interval;
309     SourceLocRange sloc;
310 
311     this(Path f, Interval i, SourceLocRange s) {
312         file = f;
313         interval = i;
314         sloc = s;
315     }
316 
317     T opCast(T : bool)() @safe pure const nothrow {
318         return !file.empty;
319     }
320 
321     // Convert only the position in the file to a string.
322     string posToString() @safe const {
323         return format!"[%s:%s-%s:%s]:[%s:%s]"(sloc.begin.line, sloc.begin.column,
324                 sloc.end.line, sloc.end.column, interval.begin, interval.end);
325     }
326 
327     string toString() @safe const {
328         auto buf = appender!string;
329         toString(buf);
330         return buf.data;
331     }
332 
333     void toString(Writer)(ref Writer w) const if (isOutputRange!(Writer, char)) {
334         import std.format : formattedWrite;
335 
336         formattedWrite!"%s:%s"(w, file, posToString);
337     }
338 }
339 
340 private ulong uniqueNodeId() {
341     static ulong next;
342     return next++;
343 }
344 
345 abstract class Node {
346     private enum Property {
347         blacklist = 0x1,
348         schemaBlacklist = 0x2,
349         covBlacklist = 0x4,
350         context = 0x8
351     }
352 
353     private ubyte prop;
354 
355     Kind kind() const;
356     ulong id() @safe pure nothrow const @nogc scope;
357 
358     Node[] children;
359 
360     /** If the node is blacklisted from being mutated. This is for example when
361      * the node covers a C macro.
362      */
363     bool blacklist() {
364         return Property.blacklist & prop;
365     }
366 
367     void blacklist(bool x) {
368         if (x)
369             prop |= Property.blacklist;
370         else
371             prop &= ~Property.blacklist;
372     }
373 
374     /** If the node should not be part of mutant schemata because it is highly
375      * likely to introduce compilation errors. It is for example likely when
376      * operators are overloaded.
377      */
378     bool schemaBlacklist() {
379         return (Property.schemaBlacklist & prop) != 0;
380     }
381 
382     void schemaBlacklist(bool x) {
383         if (x)
384             prop |= Property.schemaBlacklist;
385         else
386             prop &= ~Property.schemaBlacklist;
387     }
388 
389     /** Block nodes that have a high probability of failing from being coverage instrumented.
390      *
391      */
392     bool covBlacklist() {
393         return (Property.covBlacklist & prop) != 0;
394     }
395 
396     void covBlacklist(bool x) {
397         if (x)
398             prop |= Property.covBlacklist;
399         else
400             prop &= ~Property.covBlacklist;
401     }
402 
403     bool context() {
404         return (Property.context & prop) != 0;
405     }
406 
407     void context(bool x) {
408         if (x)
409             prop |= Property.context;
410         else
411             prop &= ~Property.context;
412     }
413 
414     bool opEquals(Kind k) {
415         return kind == k;
416     }
417 
418     override bool opEquals(Object o) {
419         auto rhs = cast(const Node) o;
420         return (rhs && (id == rhs.id));
421     }
422 
423     override size_t toHash() @safe pure nothrow const @nogc scope {
424         return id.hashOf();
425     }
426 }
427 
428 private string mixinSwitch(string nodeName) {
429     import std.conv : text;
430     import std.traits : EnumMembers;
431 
432     string s;
433     s ~= "final switch(" ~ nodeName ~ ".kind) {\n";
434     foreach (kind; [EnumMembers!Kind]) {
435         const k = text(kind);
436         s ~= format!"case Kind." ~ k ~ ": v.visit(cast(" ~ k ~ ") " ~ nodeName ~ "); break;\n";
437     }
438     s ~= "}";
439     return s;
440 }
441 
442 /**
443  * It is optional to add the members visitPush/visitPop to push/pop the nodes that are visited.
444  * The parent will always have been the last pushed.
445  */
446 void accept(VisitorT)(Node n, VisitorT v) {
447     static if (__traits(hasMember, VisitorT, "visitPush"))
448         v.visitPush(n);
449     foreach (c; n.children) {
450         mixin(mixinSwitch("c"));
451     }
452 
453     static if (__traits(hasMember, VisitorT, "visitPop"))
454         v.visitPop(n);
455 }
456 
457 /// All nodes that a visitor must be able to handle.
458 // must be sorted such that the leaf nodes are at the top.
459 // dfmt off
460 alias Nodes = AliasSeq!(
461     BinaryOp,
462     Block,
463     Branch,
464     BranchBundle,
465     Call,
466     Condition,
467     Constructor,
468     Expr,
469     Function,
470     Loop,
471     Node,
472     OpAdd,
473     OpAnd,
474     OpAndBitwise,
475     OpAssign,
476     OpAssignAdd,
477     OpAssignAndBitwise,
478     OpAssignDiv,
479     OpAssignMod,
480     OpAssignMul,
481     OpAssignOrBitwise,
482     OpAssignSub,
483     OpDiv,
484     OpEqual,
485     OpGreater,
486     OpGreaterEq,
487     OpLess,
488     OpLessEq,
489     OpMod,
490     OpMul,
491     OpNegate,
492     OpNotEqual,
493     OpOr,
494     OpOrBitwise,
495     OpSub,
496     Operator,
497     Poison,
498     Return,
499     Statement,
500     TranslationUnit,
501     UnaryOp,
502     VarDecl,
503     VarRef,
504     Literal,
505 );
506 
507 // It should be possible to generate the enum from Nodes. How?
508 enum Kind {
509     BinaryOp,
510     Block,
511     Branch,
512     BranchBundle,
513     Call,
514     Condition,
515     Constructor,
516     Expr,
517     Function,
518     Literal,
519     Loop,
520     Node,
521     OpAdd,
522     OpAnd,
523     OpAndBitwise,
524     OpAssign,
525     OpAssignAdd,
526     OpAssignAndBitwise,
527     OpAssignDiv,
528     OpAssignMod,
529     OpAssignMul,
530     OpAssignOrBitwise,
531     OpAssignSub,
532     OpDiv,
533     OpEqual,
534     OpGreater,
535     OpGreaterEq,
536     OpLess,
537     OpLessEq,
538     OpMod,
539     OpMul,
540     OpNegate,
541     OpNotEqual,
542     OpOr,
543     OpOrBitwise,
544     OpSub,
545     Operator,
546     Poison,
547     Return,
548     Statement,
549     TranslationUnit,
550     UnaryOp,
551     VarDecl,
552     VarRef,
553 }
554 
555 alias ExpressionKind = AliasSeq!(
556     Kind.BinaryOp,
557     Kind.Call,
558     Kind.Condition,
559     Kind.Constructor,
560     Kind.Expr,
561     Kind.OpAdd,
562     Kind.OpAnd,
563     Kind.OpAndBitwise,
564     Kind.OpAssign,
565     Kind.OpDiv,
566     Kind.OpEqual,
567     Kind.OpGreater,
568     Kind.OpGreaterEq,
569     Kind.OpLess,
570     Kind.OpLessEq,
571     Kind.OpMod,
572     Kind.OpMul,
573     Kind.OpNegate,
574     Kind.OpNotEqual,
575     Kind.OpOr,
576     Kind.OpOrBitwise,
577     Kind.OpSub,
578     Kind.Return,
579     Kind.UnaryOp,
580     Kind.VarDecl,
581     Kind.VarRef,
582     Kind.Literal,
583 );
584 // dfmt on
585 
586 bool isExpression(Kind k) @safe pure nothrow @nogc {
587     return k.among(ExpressionKind) != 0;
588 }
589 
590 interface Visitor {
591     static foreach (N; Nodes) {
592         void visit(N);
593     }
594 }
595 
596 // TODO: implement a breath first.
597 class DepthFirstVisitor : Visitor {
598     int visitDepth;
599 
600     void visitPush(Node n) {
601     }
602 
603     void visitPop(Node n) {
604     }
605 
606     static foreach (N; Nodes) {
607         override void visit(N n) {
608             ++visitDepth;
609             accept(n, this);
610             --visitDepth;
611         }
612     }
613 }
614 
615 /** A phantom node that carry semantic information about its children. It
616  * "poisons" all children.
617  */
618 class Poison : Node {
619     mixin(nodeImpl!(typeof(this)));
620 }
621 
622 class TranslationUnit : Node {
623     mixin(nodeImpl!(typeof(this)));
624 }
625 
626 class Statement : Node {
627     mixin(nodeImpl!(typeof(this)));
628 }
629 
630 class Loop : Node {
631     mixin(nodeImpl!(typeof(this)));
632 }
633 
634 class Expr : Node {
635     mixin(nodeImpl!(typeof(this)));
636 }
637 
638 /// A function definition.
639 class Function : Node {
640     mixin(nodeImpl!(typeof(this)));
641 
642     /// If the function has a return type it is associated with this expression.
643     Return return_;
644 }
645 
646 /// A constructor for a variable.
647 class Constructor : Expr {
648     mixin(nodeImpl!(typeof(this)));
649 }
650 
651 /// A function call.
652 class Call : Expr {
653     mixin(nodeImpl!(typeof(this)));
654 }
655 
656 /// A constant.
657 class Literal : Expr {
658     mixin(nodeImpl!(typeof(this)));
659 }
660 
661 /// The operator itself in a binary operator expression.
662 class Operator : Node {
663     mixin(nodeImpl!(typeof(this)));
664 }
665 
666 /** A block of code such such as a local scope enclosed by brackets, `{}`.
667  *
668  * It is intended to be possible to delete it. But it may need to be further
669  * analyzed for e.g. `Return` nodes.
670  */
671 class Block : Node {
672     mixin(nodeImpl!(typeof(this)));
673 }
674 
675 /** Multiple branches are contained in the bundle that can be e.g. deleted.
676  *
677  * This can, in C/C++, be either a whole if-statement or switch.
678  */
679 class BranchBundle : Node {
680     mixin(nodeImpl!(typeof(this)));
681 }
682 
683 /** The code for one of the branches resulting from a condition.
684  *
685  * It can be the branches in a if-else statement or a case branch for languages
686  * such as C/C++.
687  *
688  * The important aspect is that the branch is not an expression. It can't be
689  * evaluated to a value of a type.
690  */
691 class Branch : Node {
692     mixin(nodeImpl!(typeof(this)));
693 
694     // The inside of a branch node wherein code can be injected.
695     Block inside;
696 }
697 
698 /// Results in the bottom type or up.
699 class Return : Expr {
700     mixin(nodeImpl!(typeof(this)));
701 }
702 
703 /// A condition wraps "something" which always evaluates to a boolean.
704 class Condition : Expr {
705     mixin(nodeImpl!(typeof(this)));
706 }
707 
708 class VarDecl : Expr {
709     mixin(nodeImpl!(typeof(this)));
710     bool isConst;
711 }
712 
713 class VarRef : Expr {
714     mixin(nodeImpl!(typeof(this)));
715     // should always refer to something
716     VarDecl to;
717 
718     this(VarDecl to) {
719         this();
720         this.to = to;
721     }
722 
723     invariant {
724         assert(to !is null);
725     }
726 }
727 
728 class UnaryOp : Expr {
729     mixin(nodeImpl!(typeof(this)));
730 
731     Operator operator;
732     Expr expr;
733 
734     this(Operator op, Expr expr) {
735         this();
736         this.operator = op;
737         this.expr = expr;
738     }
739 }
740 
741 class OpNegate : UnaryOp {
742     mixin(nodeImpl!(typeof(this)));
743 }
744 
745 class BinaryOp : Expr {
746     mixin(nodeImpl!(typeof(this)));
747 
748     Operator operator;
749     Expr lhs;
750     Expr rhs;
751 
752     this(Operator op, Expr lhs, Expr rhs) {
753         this();
754         this.operator = op;
755         this.lhs = lhs;
756         this.rhs = rhs;
757     }
758 }
759 
760 class OpAssign : BinaryOp {
761     mixin(nodeImpl!(typeof(this)));
762 }
763 
764 class OpAssignAdd : BinaryOp {
765     mixin(nodeImpl!(typeof(this)));
766 }
767 
768 class OpAssignSub : BinaryOp {
769     mixin(nodeImpl!(typeof(this)));
770 }
771 
772 class OpAssignMul : BinaryOp {
773     mixin(nodeImpl!(typeof(this)));
774 }
775 
776 class OpAssignDiv : BinaryOp {
777     mixin(nodeImpl!(typeof(this)));
778 }
779 
780 class OpAssignMod : BinaryOp {
781     mixin(nodeImpl!(typeof(this)));
782 }
783 
784 class OpAssignAndBitwise : BinaryOp {
785     mixin(nodeImpl!(typeof(this)));
786 }
787 
788 class OpAssignOrBitwise : BinaryOp {
789     mixin(nodeImpl!(typeof(this)));
790 }
791 
792 class OpAdd : BinaryOp {
793     mixin(nodeImpl!(typeof(this)));
794 }
795 
796 class OpSub : BinaryOp {
797     mixin(nodeImpl!(typeof(this)));
798 }
799 
800 class OpMul : BinaryOp {
801     mixin(nodeImpl!(typeof(this)));
802 }
803 
804 class OpDiv : BinaryOp {
805     mixin(nodeImpl!(typeof(this)));
806 }
807 
808 class OpMod : BinaryOp {
809     mixin(nodeImpl!(typeof(this)));
810 }
811 
812 class OpAnd : BinaryOp {
813     mixin(nodeImpl!(typeof(this)));
814 }
815 
816 class OpAndBitwise : BinaryOp {
817     mixin(nodeImpl!(typeof(this)));
818 }
819 
820 class OpOr : BinaryOp {
821     mixin(nodeImpl!(typeof(this)));
822 }
823 
824 class OpOrBitwise : BinaryOp {
825     mixin(nodeImpl!(typeof(this)));
826 }
827 
828 class OpEqual : BinaryOp {
829     mixin(nodeImpl!(typeof(this)));
830 }
831 
832 class OpLess : BinaryOp {
833     mixin(nodeImpl!(typeof(this)));
834 }
835 
836 class OpGreater : BinaryOp {
837     mixin(nodeImpl!(typeof(this)));
838 }
839 
840 class OpLessEq : BinaryOp {
841     mixin(nodeImpl!(typeof(this)));
842 }
843 
844 class OpGreaterEq : BinaryOp {
845     mixin(nodeImpl!(typeof(this)));
846 }
847 
848 class OpNotEqual : BinaryOp {
849     mixin(nodeImpl!(typeof(this)));
850 }
851 
852 RetT makeId(RetT, T)(T data) {
853     import my.hash : makeCrc64Iso;
854 
855     auto a = makeCrc64Iso(cast(const(ubyte)[]) data);
856     return RetT(a.c0);
857 }
858 
859 RetT makeUniqueId(RetT)() {
860     import std.random : uniform;
861 
862     return RetT(uniform(long.min, long.max));
863 }
864 
865 class Type {
866     private const ulong id_;
867 
868     Range range;
869 
870     this() {
871         this(Range.makeInf);
872     }
873 
874     this(Range r) {
875         this.range = r;
876         id_ = uniqueNodeId;
877     }
878 
879     TypeKind kind() const {
880         return TypeKind.bottom;
881     }
882 
883     ulong id() @safe pure nothrow const @nogc scope {
884         return id_;
885     }
886 
887     override bool opEquals(Object o) {
888         auto rhs = cast(const Type) o;
889         return (rhs && (id == rhs.id));
890     }
891 
892     override size_t toHash() @safe pure nothrow const @nogc scope {
893         return id.hashOf();
894     }
895 }
896 
897 final class DiscreteType : Type {
898     this(Range r) {
899         super(r);
900     }
901 
902     override TypeKind kind() const {
903         return TypeKind.discrete;
904     }
905 }
906 
907 final class ContinuesType : Type {
908     this(Range r) {
909         super(r);
910     }
911 
912     override TypeKind kind() const {
913         return TypeKind.continues;
914     }
915 }
916 
917 final class UnorderedType : Type {
918     this(Range r) {
919         super(r);
920     }
921 
922     override TypeKind kind() const {
923         return TypeKind.unordered;
924     }
925 }
926 
927 final class BooleanType : Type {
928     this(Range r) {
929         super(r);
930     }
931 
932     override TypeKind kind() const {
933         return TypeKind.boolean;
934     }
935 }
936 
937 final class VoidType : Type {
938     this() {
939         super();
940     }
941 
942     override TypeKind kind() const {
943         return TypeKind.top;
944     }
945 }
946 
947 enum TypeKind {
948     // It can be anything, practically useless for mutation testing because it
949     // doesn't provide any logic that can be used to e.g. generate
950     // "intelligent" ROR mutants.
951     bottom,
952     /// integers, enums
953     discrete,
954     /// floating point values
955     continues,
956     /// no order exists between values in the type thus unable to do ROR
957     unordered,
958     ///
959     boolean,
960     /// a top type is nothing
961     top,
962 }
963 
964 struct Value {
965     import std.traits : TemplateArgsOf;
966 
967     static struct NegInf {
968     }
969 
970     static struct PosInf {
971     }
972 
973     static struct Int {
974         // TODO: change to BigInt?
975         long value;
976     }
977 
978     static struct Bool {
979         bool value;
980     }
981 
982     alias Value = SumType!(NegInf, PosInf, Int, Bool);
983     Value value;
984 
985     static foreach (T; TemplateArgsOf!Value) {
986         this(T a) {
987             value = Value(a);
988         }
989     }
990 
991     string toString() @safe pure const {
992         auto buf = appender!string;
993         toString(buf);
994         return buf.data;
995     }
996 
997     void toString(Writer)(ref Writer w) const if (isOutputRange!(Writer, char)) {
998         import std.conv : to;
999         import std.range : put;
1000 
1001         value.match!((const NegInf a) => put(w, "-inf"), (const PosInf a) => put(w,
1002                 "+inf"), (const Int a) => put(w, a.value.to!string),
1003                 (const Bool a) => put(w, a.value.to!string));
1004     }
1005 }
1006 
1007 struct Range {
1008     static makeInf() {
1009         return Range(Value(Value.NegInf.init), Value(Value.PosInf.init));
1010     }
1011 
1012     static makeBoolean() {
1013         return Range(Value(Value.Bool(false)), Value(Value.Bool(true)));
1014     }
1015 
1016     Value low;
1017     Value up;
1018 
1019     this(Value low, Value up) {
1020         this.low = low;
1021         this.up = up;
1022     }
1023 
1024     enum CompareResult {
1025         onLowerBound,
1026         onUpperBound,
1027         // the value and the range fully overlap each other. This happens when
1028         // the range is only one value.
1029         overlap,
1030         inside,
1031         outside
1032     }
1033 
1034     CompareResult compare(Value v) {
1035         CompareResult negInf() {
1036             return low.value.match!((Value.NegInf a) => CompareResult.onLowerBound,
1037                     (Value.PosInf a) => CompareResult.outside,
1038                     (Value.Int a) => CompareResult.outside, (Value.Bool a) => CompareResult.outside);
1039         }
1040 
1041         CompareResult posInf() {
1042             return up.value.match!((Value.NegInf a) => CompareResult.onUpperBound,
1043                     (Value.PosInf a) => CompareResult.outside,
1044                     (Value.Int a) => CompareResult.outside, (Value.Bool a) => CompareResult.outside);
1045         }
1046 
1047         CompareResult value(long v) {
1048             const l = low.value.match!((Value.NegInf a) => CompareResult.inside,
1049                     (Value.PosInf a) => CompareResult.outside, (Value.Int a) {
1050                 if (a.value < v)
1051                     return CompareResult.inside;
1052                 if (a.value == v)
1053                     return CompareResult.onLowerBound;
1054                 return CompareResult.outside;
1055             }, (Value.Bool a) => CompareResult.outside);
1056 
1057             const u = up.value.match!((Value.NegInf a) => CompareResult.outside,
1058                     (Value.PosInf a) => CompareResult.inside, (Value.Int a) {
1059                 if (a.value > v)
1060                     return CompareResult.inside;
1061                 if (a.value == v)
1062                     return CompareResult.onUpperBound;
1063                 return CompareResult.outside;
1064             }, (Value.Bool a) => CompareResult.outside);
1065 
1066             if (l == CompareResult.inside && u == CompareResult.inside)
1067                 return CompareResult.inside;
1068             if (l == CompareResult.onLowerBound && u == CompareResult.onUpperBound)
1069                 return CompareResult.overlap;
1070             if (l == CompareResult.onLowerBound)
1071                 return l;
1072             if (u == CompareResult.onUpperBound)
1073                 return u;
1074             assert(l == CompareResult.outside || u == CompareResult.outside);
1075             return CompareResult.outside;
1076         }
1077 
1078         CompareResult boolean(bool v) {
1079             // TODO: fix this
1080             return CompareResult.outside;
1081         }
1082 
1083         return v.value.match!((Value.NegInf a) => negInf,
1084                 (Value.PosInf a) => posInf, (Value.Int a) => value(a.value),
1085                 (Value.Bool a) => boolean(a.value));
1086     }
1087 
1088     string toString() @safe pure const {
1089         auto buf = appender!string;
1090         toString(buf);
1091         return buf.data;
1092     }
1093 
1094     void toString(Writer)(ref Writer w) const if (isOutputRange!(Writer, char)) {
1095         import std.range : put;
1096 
1097         put(w, "[");
1098         low.toString(w);
1099         put(w, ":");
1100         up.toString(w);
1101         put(w, "]");
1102     }
1103 }
1104 
1105 struct TypeId {
1106     ulong value;
1107 
1108     size_t toHash() @safe pure nothrow const @nogc {
1109         return value.hashOf;
1110     }
1111 }
1112 
1113 TypeId makeTypeId(T)(T data) {
1114     return makeId!TypeId(data);
1115 }
1116 
1117 TypeId makeUniqueTypeId() {
1118     return makeUniqueId!TypeId;
1119 }
1120 
1121 struct Types {
1122     Type[TypeId] types;
1123 
1124     void require(TypeId id, Type t) {
1125         if (id !in types) {
1126             types[id] = t;
1127         }
1128     }
1129 
1130     void set(TypeId id, Type t) {
1131         types[id] = t;
1132     }
1133 
1134     Type get(TypeId id) {
1135         if (auto v = id in types) {
1136             return *v;
1137         }
1138         return null;
1139     }
1140 
1141     bool hasId(TypeId id) {
1142         return (id in types) !is null;
1143     }
1144 
1145     string toString() @safe const {
1146         auto buf = appender!string;
1147         toString(buf);
1148         return buf.data;
1149     }
1150 
1151     void toString(Writer)(ref Writer w) const if (isOutputRange!(Writer, char)) {
1152         import std.format : formattedWrite;
1153         import std.range : put;
1154 
1155         foreach (kv; types.byKeyValue) {
1156             formattedWrite(w, "%X:%s:%s", kv.key.value, kv.value.kind, kv.value.range);
1157             put(w, "\n");
1158         }
1159     }
1160 }
1161 
1162 class Symbol {
1163     Value value;
1164 
1165     this() {
1166         this(Value(Value.PosInf.init));
1167     }
1168 
1169     this(Value v) {
1170         this.value = v;
1171     }
1172 
1173     SymbolKind kind() const {
1174         return SymbolKind.unknown;
1175     }
1176 }
1177 
1178 final class DiscretSymbol : Symbol {
1179     this(Value r) {
1180         super(r);
1181     }
1182 
1183     override SymbolKind kind() const {
1184         return SymbolKind.discret;
1185     }
1186 }
1187 
1188 final class ContinuesSymbol : Symbol {
1189     this(Value r) {
1190         super(r);
1191     }
1192 
1193     override SymbolKind kind() const {
1194         return SymbolKind.continues;
1195     }
1196 }
1197 
1198 final class BooleanSymbol : Symbol {
1199     this(Value r) {
1200         super(r);
1201     }
1202 
1203     override SymbolKind kind() const {
1204         return SymbolKind.boolean;
1205     }
1206 }
1207 
1208 enum SymbolKind {
1209     /// the symbol wasn't able to evaluate to something useful
1210     unknown,
1211     /// integers, enums
1212     discret,
1213     /// floating point values
1214     continues,
1215     ///
1216     boolean,
1217 }
1218 
1219 struct SymbolId {
1220     ulong value;
1221 
1222     size_t toHash() @safe pure nothrow const @nogc {
1223         return value.hashOf;
1224     }
1225 }
1226 
1227 SymbolId makeSymbolId(T)(T data) {
1228     return makeId!SymbolId(data);
1229 }
1230 
1231 SymbolId makeUniqueSymbolId() {
1232     return makeUniqueId!SymbolId;
1233 }
1234 
1235 struct Symbols {
1236     Symbol[SymbolId] symbols;
1237 
1238     void require(SymbolId id, Symbol s) {
1239         if (id !in symbols) {
1240             symbols[id] = s;
1241         }
1242     }
1243 
1244     void set(SymbolId id, Symbol s) {
1245         symbols[id] = s;
1246     }
1247 
1248     Symbol get(SymbolId id) {
1249         if (auto v = id in symbols) {
1250             return *v;
1251         }
1252         return null;
1253     }
1254 
1255     bool hasId(SymbolId id) {
1256         return (id in symbols) !is null;
1257     }
1258 
1259     string toString() @safe const {
1260         auto buf = appender!string;
1261         toString(buf);
1262         return buf.data;
1263     }
1264 
1265     void toString(Writer)(ref Writer w) const if (isOutputRange!(Writer, char)) {
1266         foreach (kv; symbols.byKeyValue) {
1267             formattedWrite(w, "%X:%s:%s\n", kv.key.value, kv.value.kind, kv.value.value);
1268         }
1269     }
1270 }
1271 
1272 /// Breath first visit of nodes.
1273 struct BreathFirstRange {
1274     import my.container.vector;
1275 
1276     Vector!Node nodes;
1277 
1278     this(Node n) {
1279         nodes.put(n);
1280     }
1281 
1282     Node front() @safe pure nothrow {
1283         assert(!empty, "Can't get front of an empty range");
1284         return nodes.front;
1285     }
1286 
1287     void popFront() @safe pure nothrow {
1288         assert(!empty, "Can't pop front of an empty range");
1289         nodes.put(nodes.front.children);
1290         nodes.popFront;
1291     }
1292 
1293     bool empty() @safe pure nothrow const @nogc {
1294         return nodes.empty;
1295     }
1296 }
1297 
1298 private:
1299 
1300 string nodeImpl(T)() {
1301     return `
1302     private const ulong id_;
1303 
1304     override ulong id() @safe pure nothrow const @nogc scope { return id_; }
1305 
1306     override Kind kind() const {
1307         return Kind.` ~ T.stringof ~ `;
1308     }
1309 
1310     this() {
1311         id_ = uniqueNodeId;
1312     }`;
1313 }
1314 
1315 string typeImpl() {
1316     return `
1317     private const ulong id_;
1318     override ulong id() @safe pure nothrow const @nogc scope { return id_; }
1319     this() {
1320         id_ = uniqueNodeId;
1321     }`;
1322 }
1323 
1324 /// Dedup the paths to reduce the required memory.
1325 struct Dedup(T) {
1326     T[ulong] cache;
1327     /// Number of deduplications.
1328     long count;
1329     /// ".length" accumulated of items deduplicated.
1330     long lengthAccum;
1331 
1332     T dedup(T p) {
1333         import std.traits : hasMember;
1334 
1335         const cs = p.toHash;
1336         if (auto v = cs in cache) {
1337             ++count;
1338             static if (hasMember!(T, "length"))
1339                 lengthAccum += p.length;
1340             return *v;
1341         }
1342 
1343         cache[cs] = p;
1344         return p;
1345     }
1346 
1347     void release() {
1348         cache = typeof(cache).init;
1349     }
1350 }