1 /++ 2 + Module defining the base for all parser combinators. 3 + 4 + Copyright: Copyright © 2015, Christian Köstlin 5 + License: MIT 6 + Authors: Christian Koestlin, Christian Köstlin 7 +/ 8 module pc4d.parser; 9 10 version (unittest) import unit_threaded; 11 12 public import std.variant; 13 14 import pc4d.parsers; 15 import std.array; 16 import std.ascii; 17 import std.conv; 18 import std.functional; 19 import std.stdio; 20 import std.string; 21 import std.string; 22 23 /** 24 * Result of a parsing step. 25 * use fResults to get to the results. 26 * use fRest to get to the not consumed part of the input. 27 */ 28 class ParseResult(T) 29 { 30 T[] fRest; 31 Variant[] fResults; 32 string fMessage; 33 bool fSuccess; 34 35 private this(bool success) 36 { 37 fRest = null; 38 fResults = null; 39 fMessage = null; 40 fSuccess = success; 41 } 42 43 public static ParseResult!(T) ok(T[] rest, Variant[] results) 44 { 45 auto res = new ParseResult!(T)(true); 46 res.fRest = rest; 47 res.fResults = results; 48 return res; 49 } 50 51 public static ParseResult!(T) error(string message) 52 { 53 auto res = new ParseResult!(T)(false); 54 res.fMessage = message; 55 return res; 56 } 57 58 /// errormessage 59 @property string message() 60 { 61 return fMessage; 62 } 63 64 /// true if parsing was successfull 65 @property bool success() 66 { 67 return fSuccess; 68 } 69 70 /// unconsumed input 71 @property T[] rest() 72 { 73 return fRest; 74 } 75 76 @property T[] rest(T[] rest) 77 { 78 return fRest = rest; 79 } 80 81 /// the results 82 @property Variant[] results() 83 { 84 if (!success) 85 { 86 throw new Exception("no results available"); 87 } 88 return fResults; 89 } 90 } 91 92 /** 93 * interface for all parser combinators 94 * parse must be implemented. 95 */ 96 class Parser(T) 97 { 98 Variant[]delegate(Variant[]) fCallable = null; 99 100 /// helper to create a successfull result 101 static success(U...)(T[] rest, U args) 102 { 103 return ParseResult!(T).ok(rest, variantArray(args)); 104 } 105 106 ParseResult!(T) parseAll(T[] s) 107 { 108 auto res = parse(s); 109 if (res.success) 110 { 111 if ((res.rest is null) || (res.rest.length == 0)) 112 { 113 return res; 114 } 115 else 116 { 117 return ParseResult!(T).error("string not completely consumed" /*, res.rest*/ ); 118 } 119 } 120 else 121 { 122 return res; 123 } 124 } 125 126 /++ 127 + this must be implemented by subclasses 128 + Params: 129 + input = the data to process 130 + Returns: ParseResult with (success, result and rest) or (not success and optional error message) 131 +/ 132 ParseResult!(T) parse(T[] input) 133 { 134 throw new Exception("must be implemented in childs"); 135 } 136 137 /// dsl for repetition of a parser e.g. (*match("a")) matches sequences of a 138 Parser opUnary(string op)() if (op == "*") 139 { 140 return new Repetition!(T)(this); 141 } 142 143 /// dsl for optional parser e.g. (-match("abc")) matches "abc" and "efg" 144 Parser opUnary(string op)() if (op == "-") 145 { 146 return new Optional!(T)(this); 147 } 148 149 /// dsl for transforming results of a parser 150 Parser opBinary(string op)(Variant[]function(Variant[] objects) toCall) 151 if (op == "^^") 152 { 153 return setCallback(toCall); 154 } 155 156 /// dsl for alternatives e.g. match("abc") | match("def") matches "abc" or "def" 157 Parser opBinary(string op)(Parser rhs) if (op == "|") 158 { 159 return or(this, rhs); 160 } 161 162 /// dsl for sequences e.g. match("a") ~ match("b") matches "ab" 163 Parser opBinary(string op)(Parser rhs) if (op == "~") 164 { 165 return sequence(this, rhs); 166 } 167 168 Parser setCallback(Variant[]function(Variant[] objects) tocall) 169 { 170 fCallable = toDelegate(tocall); 171 return this; 172 } 173 174 Parser setCallback(Variant[]delegate(Variant[] objects) tocall) 175 { 176 fCallable = tocall; 177 return this; 178 } 179 180 ParseResult!(T) transform(ParseResult!(T) result) 181 { 182 if (result.success) 183 { 184 return fCallable ? ParseResult!(T).ok(result.rest, fCallable(result.results)) : result; 185 } 186 else 187 { 188 return result; 189 } 190 } 191 192 } 193 194 /// transforming from regexp string to integer 195 @("regexp to integer") unittest 196 { 197 import std.conv; 198 199 auto res = (regex("\\d+") ^^ (input) { 200 return variantArray(input[0].get!string 201 .to!int); 202 }).parse("123"); 203 res.success.should == true; 204 res.results[0].should == 123; 205 } 206 207 /// the pc4d.alternative parser and its dsl '|' 208 @("alternative dsl") unittest 209 { 210 auto parser = match("abc") | match("def"); 211 auto res = parser.parse("abc"); 212 res.success.should == true; 213 214 res = parser.parse("def"); 215 res.success.should == true; 216 217 res = parser.parse("ghi"); 218 res.success.should == false; 219 } 220 221 /// trying to parse all of the input 222 @("parseAll") unittest 223 { 224 auto parser = match("test"); 225 auto res = parser.parseAll("test"); 226 227 res.success.should == true; 228 res.rest.length.should == 0; 229 230 res = parser.parseAll("test1"); 231 res.success.should == false; 232 } 233 234 /// trying to parse part of the input 235 @("parse") unittest 236 { 237 auto parser = match("test"); 238 auto res = parser.parse("test"); 239 240 res.success.should == true; 241 res.rest.length.should == 0; 242 243 res = parser.parse("test1"); 244 res.success.should == true; 245 246 res.rest.should == "1"; 247 }