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 }