1 module pry.atoms;
2 
3 import pry.stream, pry.traits;
4 import std.conv, std.range.primitives;
5 
6 template parsers(Stream)
7 {
8 	struct Tk(alias c)
9 	if(is(typeof(c) : ElementType!Stream)) {
10 		static immutable msg = "expected '" ~ to!string(c)~"'";
11 
12 		bool parse(ref Stream stream, ref ElementType!Stream value, ref Stream.Error err){
13 			if(stream.empty) {
14 				err.location = stream.location;
15 				err.reason = "unexpected end of stream";
16 				return false;
17 			}
18 			if(stream.front == c){
19 				value = c;
20 				stream.popFront();
21 				return true;
22 			}
23 			else {
24 				err.location = stream.location;
25 				err.reason = msg;
26 				return false;
27 			}
28 		}
29 	}
30 
31 	/// Single element token.
32 	auto tk(alias c)(){ return Tk!c(); }
33 
34 	struct Range(alias low, alias high)
35 	if(is(typeof(low): ElementType!Stream) && is(typeof(high) : ElementType!Stream)){
36 		static immutable msg = "expected in a range of " ~ to!string(low) ~ ".." ~ to!string(high);
37 			
38 		bool parse(ref Stream stream, ref ElementType!Stream value, ref Stream.Error err){
39 			if(stream.empty) {
40 				err.location = stream.location;
41 				err.reason = "unexpected end of stream";
42 				return false;
43 			}
44 			auto v = stream.front;
45 			if(v >= low && v <= high) {
46 				value = v;
47 				stream.popFront();
48 				return true;
49 			}
50 			else {
51 				err.location = stream.location;
52 				err.reason = msg;
53 				return false;
54 			}
55 		}
56 	}
57 	
58 	// In a range of elements.
59 	auto range(alias low, alias high)(){ return Range!(low, high)(); }
60 
61 	interface DynamicParser(V) {
62 		bool parse(ref Stream stream, ref V value, ref Stream.Error err);
63 	}
64 
65 	// Use LINE & FILE to provide unique types of dynamic.
66 	auto dynamic(V, size_t line=__LINE__, string file=__FILE__)(){
67 		static class Dynamic : DynamicParser!V {
68 			DynamicParser!V wrapped;
69 		final:
70 			void opAssign(P)(P parser)
71 			if(isParser!P && !is(P : Dynamic)){
72 				wrapped = wrap(parser);
73 			}
74 	
75 			bool parse(ref Stream stream, ref V value, ref Stream.Error err){
76 				return wrapped.parse(stream, value, err); 
77 			}
78 		}	
79 		return new Dynamic();
80 	}
81 
82 	auto wrap(Parser)(Parser parser)
83 	if(isParser!Parser){
84 		alias V = ParserValue!Parser;
85 		static class Wrapped: DynamicParser!V {
86 			Parser p;
87 			
88 			this(Parser p){
89 				this.p = p;
90 			}
91 
92 			bool parse(ref Stream stream, ref V value, ref Stream.Error err){
93 				return p.parse(stream, value, err); 
94 			}
95 		}
96 		return new Wrapped(parser);
97 	}
98 }
99 
100 unittest
101 {
102 	alias S = SimpleStream!string;
103 	with(parsers!S)
104 	{
105 		auto parser = dynamic!dchar;
106 		parser = tk!'a';
107 		S s = S("a");
108 		dchar c;
109 		S.Error e;
110 		assert(parser.parse(s, c, e));
111 		assert(c == 'a');
112 		assert(s.empty);
113 	}
114 }