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 }