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 }