1 module pry.traits; 2 3 import std.range.primitives, std.traits; 4 5 /// Test if Stream is some stream. 6 enum isStream(Stream) = is(typeof((Stream stream){ 7 auto error = Stream.Error(stream.location, "description"); 8 auto c = error.location; 9 string s = error.reason; 10 size_t k = stream.mark; 11 stream.restore(k); 12 Stream.Range slice = stream.slice(k); 13 })) && isInputRange!Stream; 14 15 /// Test if p is some parser. 16 enum isParser(Parser) = is(typeof((ref Parser parser){ 17 static assert(isCallable!(Parser.parse)); 18 alias Stream = ParserStream!Parser; 19 alias Value = ParserValue!Parser; 20 Stream stream; 21 Value value; 22 Stream.Error error; 23 bool r = parser.parse(stream, value, error); 24 })); 25 26 /// Extract value type of a given Parser. 27 alias ParserValue(Parser) = Parameters!(Parser.parse)[1]; 28 29 /// Extract stream type of a given parser. 30 alias ParserStream(Parser) = Parameters!(Parser.parse)[0]; 31 32 /// Parsers that have `ParserValue` of Nothing produce no value on parse. 33 struct Nothing{} 34 35 /// ParseFailure!string is shortcut for ParseFailure!(SimpleStream!string). 36 template ParseFailure(S) 37 if(is(S == string)) 38 { 39 import pry.stream; 40 alias ParseFailure = ParseFailure!(SimpleStream!string); 41 } 42 43 /// Generic parse failure exception. 44 class ParseFailure(S) : Exception 45 if(isStream!S){ 46 S.Error err; 47 48 this(S.Error err, string file = __FILE__, size_t line = __LINE__, 49 Throwable next = null){ 50 super(err.reason, file, line, next); 51 this.err = err; 52 } 53 54 override string toString(){ 55 import std.format; 56 return format("Parse failure at %s: %s", err.location, err.reason); 57 } 58 } 59 60 /// Convenience wrapper for Parser interface - parse a string, throw on failure. 61 auto parse(Parser, S)(S str, Parser parser) 62 if(isParser!Parser && isSomeString!S){ 63 import pry.stream; 64 alias Stream = SimpleStream!S; 65 auto stream = str.stream; 66 ParserValue!Parser value; 67 Stream.Error err; 68 if(!parser.parse(stream, value, err)){ 69 throw new ParseFailure!Stream(err); 70 } 71 return value; 72 } 73 74 unittest{ 75 import pry.atoms, pry.stream; 76 import std.exception; 77 alias S = SimpleStream!string; 78 with(parsers!S){ 79 auto p = tk!'a'; 80 assert("a".parse(p) == 'a'); 81 assertThrown("".parse(p)); 82 } 83 }