1 /++
2  + Module defining the base for all parser combinators.
3  +
4  + Copyright: Copyright © 2015, Christian Köstlin
5  + License: MIT
6  + Authors: Christian Koestlin, Christian Köstlin
7  +/
8 module pc4d.parser;
9 
10 public import std.variant;
11 
12 import pc4d.parsers;
13 import std.array;
14 import std.ascii;
15 import std.conv;
16 import std.functional;
17 import std.stdio;
18 import std..string;
19 import std..string;
20 
21 /**
22  * Result of a parsing step.
23  * use fResults to get to the results.
24  * use fRest to get to the not consumed part of the input.
25  */
26 class ParseResult(T)
27 {
28     T[] fRest;
29     Variant[] fResults;
30     string fMessage;
31     bool fSuccess;
32 
33     private this(bool success)
34     {
35         fRest = null;
36         fResults = null;
37         fMessage = null;
38         fSuccess = success;
39     }
40 
41     public static ParseResult!(T) ok(T[] rest, Variant[] results)
42     {
43         auto res = new ParseResult!(T)(true);
44         res.fRest = rest;
45         res.fResults = results;
46         return res;
47     }
48 
49     public static ParseResult!(T) error(string message)
50     {
51         auto res = new ParseResult!(T)(false);
52         res.fMessage = message;
53         return res;
54     }
55 
56     /// errormessage
57     @property string message()
58     {
59         return fMessage;
60     }
61 
62     /// true if parsing was successfull
63     @property bool success()
64     {
65         return fSuccess;
66     }
67 
68     /// unconsumed input
69     @property T[] rest()
70     {
71         return fRest;
72     }
73 
74     @property T[] rest(T[] rest)
75     {
76         return fRest = rest;
77     }
78 
79     /// the results
80     @property Variant[] results()
81     {
82         if (!success)
83         {
84             throw new Exception("no results available");
85         }
86         return fResults;
87     }
88 }
89 
90 /**
91  * interface for all parser combinators
92  * parse must be implemented.
93  */
94 class Parser(T)
95 {
96     Variant[]delegate(Variant[]) fCallable = null;
97 
98     /// helper to create a successfull result
99     static success(U...)(T[] rest, U args)
100     {
101         return ParseResult!(T).ok(rest, variantArray(args));
102     }
103 
104     ParseResult!(T) parseAll(T[] s)
105     {
106         auto res = parse(s);
107         if (res.success)
108         {
109             if ((res.rest is null) || (res.rest.length == 0))
110             {
111                 return res;
112             }
113             else
114             {
115                 return ParseResult!(T).error("string not completely consumed" /*, res.rest*/ );
116             }
117         }
118         else
119         {
120             return res;
121         }
122     }
123 
124     /// trying to parse all of the input
125     @("parseAll") unittest
126     {
127         import unit_threaded;
128 
129         auto parser = match("test");
130         auto res = parser.parseAll("test");
131 
132         res.success.shouldBeTrue;
133         res.rest.length.shouldEqual(0);
134 
135         res = parser.parseAll("test1");
136         res.success.shouldBeFalse;
137     }
138 
139     /++
140    + this must be implemented by subclasses
141    + Params:
142    +   input = the data to process
143    + Returns: ParseResult with (success, result and rest) or (not success and optional error message)
144    +/
145     ParseResult!(T) parse(T[] input)
146     {
147         throw new Exception("must be implemented in childs");
148     }
149 
150     /// trying to parse part of the input
151     @("parse") unittest
152     {
153         import unit_threaded;
154 
155         auto parser = match("test");
156         auto res = parser.parse("test");
157 
158         res.success.shouldBeTrue;
159         res.rest.length.shouldEqual(0);
160 
161         res = parser.parse("test1");
162         res.success.shouldBeTrue;
163 
164         res.rest.shouldEqual("1");
165     }
166 
167     /// dsl for repetition of a parser e.g. (*match("a")) matches sequences of a
168     Parser opUnary(string op)() if (op == "*")
169     {
170         return new Repetition!(T)(this);
171     }
172 
173     /// dsl for optional parser e.g. (-match("abc")) matches "abc" and "efg"
174     Parser opUnary(string op)() if (op == "-")
175     {
176         return new Optional!(T)(this);
177     }
178 
179     /// dsl for transforming results of a parser
180     Parser opBinary(string op)(Variant[]function(Variant[] objects) toCall)
181             if (op == "^^")
182     {
183         return setCallback(toCall);
184     }
185 
186     /// transforming from regexp string to integer
187     @("regexp to integer") unittest
188     {
189         import unit_threaded;
190         import std.conv;
191 
192         auto res = (regex("\\d+") ^^ (input) {
193             return variantArray(input[0].get!string.to!int);
194         }).parse("123");
195         res.success.shouldBeTrue;
196         res.results[0].shouldEqual(123);
197     }
198 
199     /// dsl for alternatives e.g. match("abc") | match("def") matches "abc" or "def"
200     Parser opBinary(string op)(Parser rhs) if (op == "|")
201     {
202         return or(this, rhs);
203     }
204     /// the pc4d.alternative parser and its dsl '|'
205     @("alternative dsl") unittest
206     {
207         import unit_threaded;
208 
209         auto parser = match("abc") | match("def");
210         auto res = parser.parse("abc");
211         res.success.shouldBeTrue;
212 
213         res = parser.parse("def");
214         res.success.shouldBeTrue;
215 
216         res = parser.parse("ghi");
217         res.success.shouldBeFalse;
218     }
219 
220     /// dsl for sequences e.g. match("a") ~ match("b") matches "ab"
221     Parser opBinary(string op)(Parser rhs) if (op == "~")
222     {
223         return sequence(this, rhs);
224     }
225 
226     Parser setCallback(Variant[]function(Variant[] objects) tocall)
227     {
228         fCallable = toDelegate(tocall);
229         return this;
230     }
231 
232     Parser setCallback(Variant[]delegate(Variant[] objects) tocall)
233     {
234         fCallable = tocall;
235         return this;
236     }
237 
238     ParseResult!(T) transform(ParseResult!(T) result)
239     {
240         if (result.success)
241         {
242             return fCallable ? ParseResult!(T).ok(result.rest, fCallable(result.results)) : result;
243         }
244         else
245         {
246             return result;
247         }
248     }
249 
250 }