1 module pushdown.rule;
2 
3 import pushdown.node;
4 
5 /// The rule interface is what all rules much extend from. Note that these don't have to be a template if you don't want them to be
6 interface Rule {
7   Node result(Node[] nodes, Node[] next); /// Returns the result of the rule. If the rule is invalid for the given parameters, null is returned
8 }
9 
10 private bool valid(T)(Node n) {
11   static if(is(T : Node)) {
12     if(cast(T)n is null)
13       return false;
14     return true;
15   } else {
16     return T.valid(n);
17   }
18 }
19 
20 /// For a sequence with a given length
21 class SequenceRule(Node delegate(Node[]) res, bool delegate(Node[], Node[]) extra, T...) : Rule {
22 
23   // static foreach(U; T) {
24   //   static assert(is(U : Node) || is(U == Either));
25   // }
26 
27   Node result(Node[] nodes, Node[] next) {
28     if(nodes.length != T.length)
29       return null;
30     // it's kinda crazy that D just lets me do this
31     string genConditions() {
32       import std.string, std.conv;
33       string ret = "";
34       for(int i = 0; i < T.length; i++) {
35         ret ~= `
36           if(!valid!(T[_])(nodes[_]))
37             return null;
38         `.replace("_", i.to!string);
39       }
40       return ret;
41     }
42     mixin(genConditions);
43     if(!extra(nodes, next))
44       return null;
45     return res(nodes);
46   }
47 }
48 
49 /// For an indefinite amount of things surrounded by two tokens
50 class EdgeRule(Node delegate(Node[]) res, bool delegate(Node[], Node[]) extra, T, U) : Rule {
51   Node result(Node[] nodes, Node[] next) {
52     if(nodes.length < 2)
53       return null;
54     if(valid!(T)(nodes[0]) && valid!(U)(nodes[$-1]) && extra(nodes, next))
55       return res(nodes);
56     return null;
57   }
58 }
59 
60 /// Similar to EdgeRule, except that it checks if the two given types are balanced
61 /// Usually you'd want to use this instead of EdgeRule
62 class BalancedEdgeRule(Node delegate(Node[]) res, bool delegate(Node[], Node[]) extra, T, U) : Rule {
63   Node result(Node[] nodes, Node[] next) {
64     if(nodes.length < 2)
65       return null;
66     if(valid!(T)(nodes[0]) && valid!(U)(nodes[$-1])) {
67       // check balance
68       int balance = 0;
69       for(int i = 0; i < nodes.length; i++) {
70         if(cast(T)nodes[i] !is null)
71           balance++;
72         else if(cast(U)nodes[i] !is null)
73           balance--;
74       }
75       if(balance == 0 && extra(nodes, next)) {
76         return res(nodes);
77       }
78     }
79     return null;
80   }
81 }
82 
83 /// Only checks the first node
84 class StartRule(Node delegate(Node[]) res, bool delegate(Node[], Node[]) extra, T) : Rule {
85   Node result(Node[] nodes, Node[] next) {
86     if(nodes.length < 1)
87       return null;
88     if(valid!(T)(nodes[0]) && extra(nodes, next))
89       return res(nodes);
90   }
91 }
92 
93 /// Only checks the last node
94 class EndRule(Node delegate(Node[]) res, bool delegate(Node[], Node[]) extra, T) : Rule {
95   Node result(Node[] nodes, Node[] next) {
96     if(nodes.length < 1)
97       return null;
98     if(valid!(T)(nodes[$-1]) !is null && extra(nodes, next))
99       return res(nodes);
100   }
101 }
102 
103 /// Checks for a list with T as the item and U as the delimiter
104 /// `T U T U` => valid
105 /// `T U` => valid
106 /// `T U T U T` => valid
107 /// `T T U T` => invalid
108 class ListRule(Node delegate(Node[]) res, bool delegate(Node[], Node[]) extra, T, U) : Rule {
109   Node result(Node[] nodes, Node[] next) {
110     bool t = true;
111     foreach(n; nodes) {
112       if(t) {
113         if(!valid!(T)(n))
114           return null;
115       } else {
116         if(!valid!(U)(n))
117           return null;
118       }
119       t = !t;
120     }
121     if(extra(nodes, next))
122       return res(nodes);
123     return null;
124   }
125 }