1 /** 2 Simple parsers 3 4 Here are some commonly used parsers. Also as an example for how 5 to use the combinators. 6 7 Copyright: 2017 Yuxuan Shui 8 */ 9 module sdpc.parsers; 10 import sdpc.combinators, 11 sdpc.primitives; 12 import std.traits, 13 std.string, 14 std.conv, 15 std.range; 16 public @safe : 17 18 ///Consumes nothing, always return OK 19 auto nop(R)(ref R i) if (isForwardRange!R) { 20 return ParseResult!R(i); 21 } 22 23 struct ParseError(R) { 24 dstring msg; 25 R err_range; 26 } 27 28 /// Match a string, return the matched string 29 struct token(string t) { 30 import std.algorithm.comparison; 31 static auto opCall(R)(R i) if (isForwardRange!R && is(typeof(equal(i, t)))) { 32 import std.array; 33 alias RT = ParseResult!(R, string, ParseError!R); 34 auto str = take(i, t.length); 35 auto retr = i.save.drop(t.length); 36 if (equal(str.save, t)) 37 return RT(retr, t); 38 return RT(ParseError!R("expecting \""~t~"\", got \""~str.array.idup~"\"", retr)); 39 } 40 } 41 42 /// Match any character in accept 43 struct ch(alias accept) if (isSomeChar!(ElementType!(typeof(accept)))){ 44 static auto opCall(R)(R i) if (isForwardRange!R) {// && is(typeof(ElementType!R == accept[0]))) { 45 alias RT = ParseResult!(R, ElementType!(typeof(accept)), ParseError!R); 46 if (i.empty || accept.indexOf(i.front) == -1) { 47 if (!i.empty) 48 i.popFront; 49 return RT(ParseError!R("expecting one of \""~accept~"\"", i)); 50 } 51 52 auto ch = i.front; 53 i.popFront; 54 return RT(i, ch); 55 } 56 } 57 58 /// Match any character except those in reject 59 struct not_ch(alias reject) if (isSomeChar!(ElementType!(typeof(reject)))){ 60 static auto opCall(R)(R i) if (isForwardRange!R) { 61 alias RT = ParseResult!(R, ElementType!(typeof(reject)), ParseError!R); 62 if (i.empty || reject.indexOf(i.front) != -1) { 63 if (!i.empty) 64 i.popFront; 65 return RT(ParseError!R("not expecting one of \""~reject~"\"", i)); 66 } 67 68 auto ch = i.front; 69 i.popFront; 70 return RT(i, ch); 71 } 72 } 73 74 /// Parse a sequences of digits, return an array of number 75 alias digit(string _digits) = transform!(ch!_digits, (ch) => cast(int)_digits.indexOf(ch)); 76 77 immutable string lower = "qwertyuiopasdfghjklzxcvbnm"; 78 immutable string upper = "QWERTYUIOPASDFGHJKLZXCVBNM"; 79 immutable string alphabet = lower ~ upper; 80 immutable string digits = "0123456789"; 81 82 /** 83 Parse a number 84 Params: 85 accept = digits allowed in the number, i-th character corresponds to digit i 86 base = base 87 */ 88 template number(string accept = digits, int base = 10) if (accept.length == base) { 89 import std.algorithm.iteration; 90 alias number = transform!(many!(digit!accept), (x) => x.reduce!((a,b) => a*base+b)); 91 } 92 93 /// 94 unittest { 95 auto i = "12354"; 96 auto rx = number!()(i); 97 assert(rx.ok); 98 assert(rx.v == 12354); 99 100 i = "ffabc"; 101 auto rx1 = number!(digits~"abcdef", 16)(i); 102 assert(rx1.ok); 103 assert(rx1.v == 1047228); 104 } 105 106 /** 107 Parse a sequence of characters 108 Params: 109 accept = an array of acceptable characters 110 */ 111 alias word(alias accept = alphabet) = transform!(many!(ch!accept), (x) => x.idup); 112 113 /// Parse an identifier, starts with a letter or _, followed by letter, _, or digits 114 auto identifier(R)(R i) if (isForwardRange!R) { 115 auto ret = ch!(alphabet~"_")(i); 116 alias RT = ParseResult!(R, ElementType!R[], ParseError!R); 117 if (!ret.ok) 118 return RT(ParseError!R("Failed to parse a identifier", i)); 119 auto ret2 = word!(alphabet~"_"~digits)(ret.cont); 120 ElementType!R[] str = [ret.v]; 121 if (ret2.ok) 122 str ~= ret2.v; 123 return RT(ret2.cont, str); 124 } 125 126 /// 127 unittest { 128 auto i = "_asd1234a"; 129 auto rx2 = identifier(i); 130 assert(rx2.ok); 131 assert(!rx2.cont.length); 132 assert(rx2.v == "_asd1234a"); 133 } 134 135 /// Parse escaped character, \n, \r, \b, \" and \\ 136 auto parse_escape1(R)(R i) if (isForwardRange!R) { 137 alias RT = ParseResult!(R, dchar, ParseError!R); 138 auto r = seq!( 139 discard!(token!"\\"), 140 transform_err!(choice!( 141 token!"n", 142 token!"b", 143 token!"r", 144 token!"\"", 145 token!"\\" 146 ), (x) => x[0]) 147 )(i); 148 if (!r.ok) 149 return RT(ParseError!R("Failed to parse escape sequence", i)); 150 dchar res; 151 final switch(r.v.v!1) { 152 case "n": 153 res = '\n'; 154 break; 155 case "b": 156 res = '\b'; 157 break; 158 case "r": 159 res = '\r'; 160 break; 161 case "\"": 162 res = '\"'; 163 break; 164 case "\\": 165 res = '\\'; 166 break; 167 } 168 return RT(r.cont, res); 169 } 170 171 /// Parse a string enclosed by a pair of quotes, and containing escape sequence 172 auto parse_string(R)(R i) if (isForwardRange!R) { 173 alias RT = ParseResult!(R, dchar[], ParseError!R); 174 auto r = between!(token!"\"", 175 transform_err!(many!(choice!( 176 parse_escape1, 177 not_ch!"\"" 178 )), (x) => x[0]), 179 token!"\"")(i); 180 if (!r.ok) 181 return RT(ParseError!R("Failed to parse a string", i)); 182 return r; 183 } 184 185 /// 186 unittest { 187 auto i = "\"asdf\\n\\b\""; 188 auto r = parse_string(i); 189 import std.format; 190 assert(r.ok); 191 assert(r.v == "asdf\n\b", format("%s", r.v)); 192 } 193 194 /// Skip white spaces 195 alias skip_whitespace = skip!(choice!(token!" ", token!"\n", token!"\t")); 196 197 /// 198 unittest { 199 auto i = " \n\t "; 200 auto r = skip_whitespace(i); 201 assert(r.ok); 202 assert(!r.cont.length); 203 }