Source

fparsec / Doc / html / users-guide / customizing-error-messages.html

Full commit
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
 <title>Customizing error messages</title>
 <link rel="stylesheet" type="text/css" media="all" href="../css/style.css" />
 <link rel="stylesheet" type="text/css" media="screen" href="../css/screen-sidebar.css" />
 <!--[if lt IE 9]>
 <link rel="stylesheet" type="text/css" media="all" href="../css/style-ie.css" />
 <![endif]-->
 <!--[if IE 6]>
 <link rel="stylesheet" type="text/css" media="all" href="../css/style-ie6.css" />
 <![endif]-->
 <link rel="stylesheet" type="text/css" media="print" href="../css/print.css" />
</head>
<body>
 <div id="fixed-layer">
 <div id="fixed-wrapper">
 <div id="sidebar">
  <div id="top-links"><span><a href="http://bitbucket.org/fparsec/main">FParsec @ BitBucket</a> | <a href="https://bitbucket.org/fparsec/main/issues">Report a bug</a> | <a href="mailto:fparsec [at] quanttec.com?subject=FParsec&amp;body=Hello Stephan,%0A%0A[your feedback]">Feedback</a></span></div>
  <div id="nav-tree">
   <table class="nav n1">
    <tbody class="nav-open n1">
     <tr class="nav-entry n1 _1">
      <td class="nav-number n1"></td>
      <td class="nav-title n1"><a href="../index.html">FParsec Documentation</a></td>
     </tr>
     <tr class="nav-subentries n1 _1">
      <td class="nav-subentries-number n1"></td>
      <td class="nav-subentries n1">
       <table class="nav n2">
        <tbody class="nav-before-open n2">
         <tr class="nav-entry n2 _1">
          <td class="nav-number n2"><a href="../about/index.html"><span class="section-number">1</span><span class="nav-space"></span></a></td>
          <td class="nav-title n2"><a href="../about/index.html">About FParsec</a></td>
         </tr>
         <tr class="nav-entry n2 _2">
          <td class="nav-number n2"><a href="../license.html"><span class="section-number">2</span><span class="nav-space"></span></a></td>
          <td class="nav-title n2"><a href="../license.html">License</a></td>
         </tr>
         <tr class="nav-entry n2 _3">
          <td class="nav-number n2">
           <a href="../download-and-installation.html"><span class="section-number">3</span><span class="nav-space"></span></a>
          </td>
          <td class="nav-title n2"><a href="../download-and-installation.html">Download and installation</a></td>
         </tr>
         <tr class="nav-entry n2 _4">
          <td class="nav-number n2"><a href="../tutorial.html"><span class="section-number">4</span><span class="nav-space"></span></a></td>
          <td class="nav-title n2"><a href="../tutorial.html">Tutorial</a></td>
         </tr>
        </tbody>
        <tbody class="nav-open n2">
         <tr class="nav-entry n2 _5">
          <td class="nav-number n2"><a href="index.html"><span class="section-number">5</span><span class="nav-space"></span></a></td>
          <td class="nav-title n2"><a href="index.html">User’s Guide</a></td>
         </tr>
         <tr class="nav-subentries n2 _5">
          <td class="nav-subentries-number n2"></td>
          <td class="nav-subentries n2">
           <table class="nav n3">
            <tbody class="nav-before-open n3">
             <tr class="nav-entry n3 _1">
              <td class="nav-number n3"><a href="parser-functions.html"><span class="section-number">1</span><span class="nav-space"></span></a></td>
              <td class="nav-title n3"><a href="parser-functions.html">Parser functions</a></td>
             </tr>
             <tr class="nav-entry n3 _2">
              <td class="nav-number n3">
               <a href="running-parsers-on-input.html"><span class="section-number">2</span><span class="nav-space"></span></a>
              </td>
              <td class="nav-title n3"><a href="running-parsers-on-input.html">Running parsers on input</a></td>
             </tr>
             <tr class="nav-entry n3 _3">
              <td class="nav-number n3">
               <a href="internals-of-a-simple-parser-function.html"><span class="section-number">3</span><span class="nav-space"></span></a>
              </td>
              <td class="nav-title n3">
               <a href="internals-of-a-simple-parser-function.html">Internals of a simple <code class="fsharp"><span class="ci">Parser</span></code>
               function</a>
              </td>
             </tr>
             <tr class="nav-entry n3 _4">
              <td class="nav-number n3">
               <a href="applying-parsers-in-sequence.html"><span class="section-number">4</span><span class="nav-space"></span></a>
              </td>
              <td class="nav-title n3"><a href="applying-parsers-in-sequence.html">Applying parsers in sequence</a></td>
             </tr>
             <tr class="nav-entry n3 _5">
              <td class="nav-number n3"><a href="parsing-sequences.html"><span class="section-number">5</span><span class="nav-space"></span></a></td>
              <td class="nav-title n3"><a href="parsing-sequences.html">Parsing sequences</a></td>
             </tr>
             <tr class="nav-entry n3 _6">
              <td class="nav-number n3">
               <a href="parsing-alternatives.html"><span class="section-number">6</span><span class="nav-space"></span></a>
              </td>
              <td class="nav-title n3"><a href="parsing-alternatives.html">Parsing alternatives</a></td>
             </tr>
             <tr class="nav-entry n3 _7">
              <td class="nav-number n3">
               <a href="looking-ahead-and-backtracking.html"><span class="section-number">7</span><span class="nav-space"></span></a>
              </td>
              <td class="nav-title n3"><a href="looking-ahead-and-backtracking.html">Looking ahead and backtracking</a></td>
             </tr>
            </tbody>
            <tbody class="nav-open selected n3">
             <tr class="nav-entry selected n3 _8">
              <td class="nav-number selected n3"><a href="#"><span class="section-number">8</span><span class="nav-space"></span></a></td>
              <td class="nav-title selected n3"><a href="#">Customizing error messages</a></td>
             </tr>
            </tbody>
            <tbody class="nav-after-open n3">
             <tr class="nav-entry n3 _9">
              <td class="nav-number n3">
               <a href="parsing-with-user-state.html"><span class="section-number">9</span><span class="nav-space"></span></a>
              </td>
              <td class="nav-title n3"><a href="parsing-with-user-state.html">Parsing with user state</a></td>
             </tr>
             <tr class="nav-entry n3 _0">
              <td class="nav-number n3">
               <a href="where-is-the-monad.html"><span class="section-number">10</span><span class="nav-space"></span></a>
              </td>
              <td class="nav-title n3"><a href="where-is-the-monad.html">Where is the monad?</a></td>
             </tr>
             <tr class="nav-entry n3 _1">
              <td class="nav-number n3">
               <a href="debugging-a-parser.html"><span class="section-number">11</span><span class="nav-space"></span></a>
              </td>
              <td class="nav-title n3"><a href="debugging-a-parser.html">Debugging a parser</a></td>
             </tr>
             <tr class="nav-entry n3 _2">
              <td class="nav-number n3">
               <a href="performance-optimizations.html"><span class="section-number">12</span><span class="nav-space"></span></a>
              </td>
              <td class="nav-title n3"><a href="performance-optimizations.html">Performance optimizations</a></td>
             </tr>
             <tr class="nav-entry n3 _3">
              <td class="nav-number n3"><a href="tips-and-tricks.html"><span class="section-number">13</span><span class="nav-space"></span></a></td>
              <td class="nav-title n3"><a href="tips-and-tricks.html">Tips and tricks</a></td>
             </tr>
            </tbody>
           </table>
          </td>
         </tr>
        </tbody>
        <tbody class="nav-after-open n2">
         <tr class="nav-entry n2 _6">
          <td class="nav-number n2"><a href="../reference/index.html"><span class="section-number">6</span><span class="nav-space"></span></a></td>
          <td class="nav-title n2"><a href="../reference/index.html">Reference</a></td>
         </tr>
        </tbody>
       </table>
      </td>
     </tr>
    </tbody>
   </table>
  </div>
  <div id="copyright">
    <span>Copyright © 2012 <a href="../about/contact.html">Stephan Tolksdorf</a></span>
  </div>
 </div>
 </div>
 </div>
 <div id="wrapper">
 <div id="main">
 <div id="main-content">
 <div id="breadcrumbs">
  <span class="breadcrumbs">
   <span id="breadcrumbs-parents"><a href="../index.html">FParsec Documentation</a><span class="breadcrumbs-sep"> > </span><a href="index.html">User’s Guide</a></span><span class="breadcrumbs-sep"> > </span>Customizing error messages
  </span>
 </div>
 <div class="section s2">
  <h1 class="title h2"><span><span class="section-number">5.8</span> Customizing error messages</span></h1>
  <div class="intro i2">
   <div class="para _1">
    <p>
     Generating relevant and informative parser error messages is one of FParsec greatest strengths. The top‐down approach of recursive‐descent
     parsing guarantees that there is always enough context to describe the exact cause of a parser error and how it could be avoided. FParsec
     exploits this context to automatically generate descriptive error messages whenever possible. This chapter explains how you can ensure with
     minimal efforts that your parser always produces understandable error messages.
    </p>
   </div>
   <div class="para _2 lcinp">
    <p>
     As we already described in detail in <a href="applying-parsers-in-sequence.html#merging-error-messages">section 5.4.2</a>, error reporting in
     FParsec is based on the following two principles:
    </p>
    <ul class="l1">
     <li class="_1">
      Parsers that fail or could have consumed more input return as part of their <code class="fsharp"><a href="../reference/reply.html"><span
      class="ci">Reply</span></a></code> an <code class="fsharp"><a href="../reference/errormessagelist.html"><span
      class="ci">ErrorMessageList</span></a></code> describing the input they expected or the reason they failed.
     </li>
     <li class="_2">
      Parser combinators aggregate all error messages that apply to the same input position and then propagate these error messages as appropriate.
     </li>
    </ul>
   </div>
   <div class="para _3">
    <p>
     The various error messages in the previous chapters demonstrate that the built‐in error reporting usually works quite well even without any
     intervention by the parser author. However, sometimes FParsec lacks the information necessary to produce an informative error message by itself.
    </p>
   </div>
   <div class="para _4 lcinp">
    <p>
     Consider for example the <code class="fsharp"><a href="../reference/charparsers.html#members.many1Satisfy"><span
     class="ci">many1Satisfy</span></a> <span class="ci">f</span></code> parser, which parses a string consisting of one or more chars satisfying the
     predicate function <code class="fsharp"><span class="ci">f</span></code>. If this parser fails to parse at least one char, the generated error is
     not very helpful:
    </p>
<pre class="code fsharp"><span class="cmd"><span class="cmdp">&gt;</span> <a href="../reference/charparsers.html#members.run"><span class="ci">run</span></a> <span class="cp">(</span><a href="../reference/charparsers.html#members.many1Satisfy"><span class="ci">many1Satisfy</span></a> <a href="../reference/charparsers.html#members.isLetter"><span class="ci">isLetter</span></a><span class="cp">)</span> <span class="cs"><span class="cld">"</span>123<span class="crd">"</span></span><span class="cp">;;</span></span>
<span class="cout">val it : ParserResult&lt;string,unit&gt; = Failure:
Error in Ln: 1 Col: 1
123
^
Unknown Error(s)
</span></pre>
   </div>
   <div class="para _5">
    <p>
     The problem here is that <code class="fsharp"><a href="../reference/charparsers.html#members.many1Satisfy"><span
     class="ci">many1Satisfy</span></a></code> can’t describe what chars the function predicate accepts. Hence, when you don’t use <code
     class="fsharp"><a href="../reference/charparsers.html#members.many1Satisfy"><span class="ci">many1Satisfy</span></a></code> as part of a combined
     parser that takes care of a potential error, you better replace it with <code class="fsharp"><a
     href="../reference/charparsers.html#members.many1SatisfyL"><span class="ci">many1SatisfyL</span></a></code>, which allows you to describe the
     accepted input with a label (hence the &#x201C;L&#x201D;):
    </p>
   </div>
   <div class="para _6 lcinp">
<pre class="code fsharp"><span class="cmd"><span class="cmdp">&gt;</span> <a href="../reference/charparsers.html#members.run"><span class="ci">run</span></a> <span class="cp">(</span><a href="../reference/charparsers.html#members.many1SatisfyL"><span class="ci">many1SatisfyL</span></a> <a href="../reference/charparsers.html#members.isLetter"><span class="ci">isLetter</span></a> <span class="cs"><span class="cld">"</span>identifier<span class="crd">"</span></span><span class="cp">)</span> <span class="cs"><span class="cld">"</span>123<span class="crd">"</span></span><span class="cp">;;</span></span>
<span class="cout">val it : ParserResult&lt;string,unit&gt; = Failure:
Error in Ln: 1 Col: 1
123
^
Expecting: identifier
</span></pre>
   </div>
   <div class="para _7">
    <p>
     There are also labelled variants of other parsers and combinators, for example <code class="fsharp"><a
     href="../reference/primitives.html#members.choiceL"><span class="ci">choiceL</span></a></code> and <code class="fsharp"><a
     href="../reference/primitives.html#members.notFollowedByL"><span class="ci">notFollowedByL</span></a></code>.
    </p>
   </div>
   <div class="para _8 lcinp">
    <p>If there is no labelled parser variant or you want to replace a predefined error message, you can always use the labelling operator</p>
<pre class="code fsharp"><span class="ck">val</span> <span class="cp">(</span><a href="../reference/primitives.html#members.:60::63::62:"><span class="co">&lt;?&gt;</span></a><span class="cp">)</span><span class="cp">:</span> <a href="../reference/primitives.html#members.Parser"><span class="ci">Parser</span></a><span class="cp">&lt;</span><span class="ctv">'a</span><span class="cp">,</span><span class="ctv">'u</span><span class="cp">&gt;</span> <span class="cr">-&gt;</span> <span class="ci">string</span> <span class="cr">-&gt;</span> <a href="../reference/primitives.html#members.Parser"><span class="ci">Parser</span></a><span class="cp">&lt;</span><span class="ctv">'a</span><span class="cp">,</span><span class="ctv">'u</span><span class="cp">&gt;</span>
</pre>
   </div>
   <div class="para _9">
    <p>
     The parser <code class="fsharp"><span class="ci">p</span> <a href="../reference/primitives.html#members.:60::63::62:"><span
     class="co">&lt;?&gt;</span></a> <span class="ci">label</span></code> behaves like <code class="fsharp"><span class="ci">p</span></code>, except
     that the error messages are replaced with <code class="fsharp"><span class="ci">expectedError</span> <span class="ci">label</span></code> if
     <code class="fsharp"><span class="ci">p</span></code> does not change the parser state (usually because <code class="fsharp"><span
     class="ci">p</span></code> failed).
    </p>
   </div>
   <div class="para _0 lcinp">
    <p>
     For example, if FParsec didn’t provide <code class="fsharp"><a href="../reference/charparsers.html#members.many1SatisfyL"><span
     class="ci">many1SatisfyL</span></a></code>, you could define it yourself as
    </p>
<pre class="code fsharp"><span class="ck">let</span> <a href="../reference/charparsers.html#members.many1SatisfyL"><span class="ci">many1SatisfyL</span></a> <span class="ci">f</span> <span class="ci">label</span> <span class="cp">=</span> <a href="../reference/charparsers.html#members.many1Satisfy"><span class="ci">many1Satisfy</span></a> <span class="ci">f</span> <a href="../reference/primitives.html#members.:60::63::62:"><span class="co">&lt;?&gt;</span></a> <span class="ci">label</span>
</pre>
   </div>
   <div class="para _1 lcinp">
    <p>
     The labelling operator is particularly useful for producing error messages in terms of higher‐level grammar productions instead of error messages
     in terms of lower‐level component parsers. Suppose you want to parse a string literal with the following parser
    </p>
<pre class="code fsharp"><span class="ck">let</span> <span class="ci">literal_</span> <span class="cp">=</span> <a href="../reference/primitives.html#members.between"><span class="ci">between</span></a> <span class="cp">(</span><a href="../reference/charparsers.html#members.pstring"><span class="ci">pstring</span></a> <span class="cs"><span class="cld">"</span><span class="ce">\"</span><span class="crd">"</span></span><span class="cp">)</span> <span class="cp">(</span><a href="../reference/charparsers.html#members.pstring"><span class="ci">pstring</span></a> <span class="cs"><span class="cld">"</span><span class="ce">\"</span><span class="crd">"</span></span><span class="cp">)</span>
                       <span class="cp">(</span><a href="../reference/charparsers.html#members.manySatisfy"><span class="ci">manySatisfy</span></a> <span class="cp">(</span><span class="cp">(</span><span class="co">&lt;&gt;</span><span class="cp">)</span> <span class="cc"><span class="cld">'</span>"<span class="crd">'</span></span><span class="cp">)</span><span class="cp">)</span>
</pre>
   </div>
   <div class="para _2 lcinp">
    <p>
     If this parser encounters input that doesn’t start with a double quote it will fail with the error message produced by the parser for the opening
     quote:
    </p>
<pre class="code fsharp"><span class="cmd"><span class="cmdp">&gt;</span> <a href="../reference/charparsers.html#members.run"><span class="ci">run</span></a> <span class="ci">literal_</span> <span class="cs"><span class="cld">"</span>123<span class="crd">"</span></span><span class="cp">;;</span></span>
<span class="cout">val it : ParserResult&lt;string,unit&gt; = Failure:
Error in Ln: 1 Col: 1
123
^
Expecting: '"'
</span></pre>
   </div>
   <div class="para _3 lcinp">
    <p>In situations like these an error message that mentions the aggregate thing you’re trying to parse will often be more helpful:</p>
<pre class="code fsharp"><span class="ck">let</span> <span class="ci">literal</span> <span class="cp">=</span> <span class="ci">literal_</span> <a href="../reference/primitives.html#members.:60::63::62:"><span class="co">&lt;?&gt;</span></a> <span class="cs"><span class="cld">"</span>string literal in double quotes<span class="crd">"</span></span>
</pre>
<pre class="code fsharp"><span class="cmd"><span class="cmdp">&gt;</span> <a href="../reference/charparsers.html#members.run"><span class="ci">run</span></a> <span class="ci">literal</span> <span class="cs"><span class="cld">"</span>123<span class="crd">"</span></span><span class="cp">;;</span></span>
<span class="cout">val it : ParserResult&lt;string,unit&gt; = Failure:
Error in Ln: 1 Col: 1
123
^
Expecting: string literal in double quotes
</span></pre>
   </div>
   <div class="para _4 lcinp">
    <p>
     Note that <code class="fsharp"><a href="../reference/primitives.html#members.:60::63::62:"><span class="co">&lt;?&gt;</span></a></code> only
     replaces the error message if the parser doesn’t consume input. For example, our <code class="fsharp"><span class="ci">literal</span></code>
     parser won’t mention that we’re trying to parse a string literal if it fails after the initial double quote:
    </p>
<pre class="code fsharp"><span class="cmd"><span class="cmdp">&gt;</span> <a href="../reference/charparsers.html#members.run"><span class="ci">run</span></a> <span class="ci">literal</span> <span class="cs"><span class="cld">"</span><span class="ce">\"</span>abc def<span class="crd">"</span></span><span class="cp">;;</span></span>
<span class="cout">val it : ParserResult&lt;string,unit&gt; = Failure:
Error in Ln: 1 Col: 9
"abc def
        ^
Note: The error occurred at the end of the input stream.
Expecting: '"'
</span></pre>
   </div>
   <div class="para _5 lcinp">
    <p>
     With the compound labelling operator <code class="fsharp"><a href="../reference/primitives.html#members.:60::63::63::62:"><span
     class="co">&lt;??&gt;</span></a></code> you can make sure that the compound gets mentioned even if the parser fails after consuming input:
    </p>
<pre class="code fsharp"><span class="ck">let</span> <span class="ci">literal</span> <span class="cp">=</span> <span class="ci">literal_</span> <a href="../reference/primitives.html#members.:60::63::63::62:"><span class="co">&lt;??&gt;</span></a> <span class="cs"><span class="cld">"</span>string literal in double quotes<span class="crd">"</span></span>
</pre>
<pre class="code fsharp"><span class="cmd"><span class="cmdp">&gt;</span> <a href="../reference/charparsers.html#members.run"><span class="ci">run</span></a> <span class="ci">literal</span> <span class="cs"><span class="cld">"</span><span class="ce">\"</span>abc def<span class="crd">"</span></span><span class="cp">;;</span></span>
<span class="cout">val it : ParserResult&lt;string,unit&gt; = Failure:
Error in Ln: 1 Col: 1
"abc def
^
Expecting: string literal in double quotes

string literal in double quotes could not be parsed because:
  Error in Ln: 1 Col: 9
  "abc def
          ^
  Note: The error occurred at the end of the input stream.
  Expecting: '"'
</span></pre>
   </div>
   <div class="para _6 lcinp">
    <div class="admonition">
     <div class="admonition-title">Tip</div>
     <div class="admonition-body">
      <div class="para _1">
       <p>
        If you don’t like the formatting of these error messages, you can write a custom formatter for your application. The data structure in which
        error messages are stored is easy to query and process. See the reference for the <a href="../reference/error.html"><code class="fsharp"><span
        class="ci">Error</span></code> module</a>.
       </p>
      </div>
     </div>
    </div>
   </div>
   <div class="para _7 lcinp">
    <p>
     The parsers we discussed so far in this chapter only generated <code class="fsharp"><a href="../reference/error.html#interface.Expected"><span
     class="ci">Expected</span></a></code> error messages, but FParsec also supports other type of error messages. For example, the <code
     class="fsharp"><a href="../reference/primitives.html#members.notFollowedByL"><span class="ci">notFollowedByL</span></a></code> parser generates
     <code class="fsharp"><a href="../reference/error.html#interface.Unexpected"><span class="ci">Unexpected</span></a></code> error messages:
    </p>
<pre class="code fsharp"><span class="cmd"><span class="cmdp">&gt;</span> <a href="../reference/charparsers.html#members.run"><span class="ci">run</span></a> <span class="cp">(</span><a href="../reference/primitives.html#members.notFollowedByL"><span class="ci">notFollowedByL</span></a> <a href="../reference/charparsers.html#members.spaces"><span class="ci">spaces</span></a> <span class="cs"><span class="cld">"</span>whitespace<span class="crd">"</span></span><span class="cp">)</span> <span class="cs"><span class="cld">"</span> <span class="crd">"</span></span><span class="cp">;;</span></span>
<span class="cout">val it : ParserResult&lt;unit,unit&gt; = Failure:
Error in Ln: 1 Col: 1

^
Unexpected: whitespace
</span></pre>
   </div>
   <div class="para _8">
    <p>
     Error messages that don’t fit into the <code class="fsharp"><a href="../reference/error.html#interface.Expected"><span
     class="ci">Expected</span></a></code> and <code class="fsharp"><a href="../reference/error.html#interface.Unexpected"><span
     class="ci">Unexpected</span></a></code> categories can be produced with the <code class="fsharp"><a
     href="../reference/primitives.html#members.fail"><span class="ci">fail</span></a></code> and <code class="fsharp"><a
     href="../reference/primitives.html#members.failFatally"><span class="ci">failFatally</span></a></code> primitives:
    </p>
   </div>
   <div class="para _9 lcinp">
<pre class="code fsharp"><span class="ck">let</span> <span class="ci">theory</span> <span class="cp">=</span>
    <a href="../reference/charparsers.html#members.charsTillString"><span class="ci">charsTillString</span></a> <span class="cs"><span class="cld">"</span>3) <span class="crd">"</span></span> <span class="cb">true</span> <span class="ci">System</span><span class="cm">.</span><span class="ci">Int32</span><span class="cm">.</span><span class="ci">MaxValue</span>
     <a href="../reference/primitives.html#members.:62::62:.."><span class="co">&gt;&gt;.</span></a> <span class="cp">(</span><a href="../reference/charparsers.html#members.pstring"><span class="ci">pstring</span></a> <span class="cs"><span class="cld">"</span>profit<span class="crd">"</span></span> <a href="../reference/primitives.html#members.:60::124::62:"><span class="co">&lt;|&gt;</span></a> <a href="../reference/primitives.html#members.fail"><span class="ci">fail</span></a> <span class="cs"><span class="cld">"</span>So much about that theory ... ;-)<span class="crd">"</span></span><span class="cp">)</span>

<span class="ck">let</span> <span class="ci">practice</span> <span class="cp">=</span> <span class="cs"><span class="cld">"</span>1) Write open source library 2) ??? 3) lot's of unpaid work<span class="crd">"</span></span>

</pre>
<pre class="code fsharp"><span class="cmd"><span class="cmdp">&gt;</span> <a href="../reference/charparsers.html#members.run"><span class="ci">run</span></a> <span class="ci">theory</span> <span class="ci">practice</span><span class="cp">;;</span></span>
<span class="cout">val it : ParserResult&lt;string,unit&gt; = Failure:
Error in Ln: 1 Col: 40
1) Write open source library 2) ??? 3) lot's of unpaid work
                                       ^
Expecting: 'profit'
Other error messages:
  So much about that theory... ;-)
</span></pre>
   </div>
   <div class="para _0">
    <p>
     If you can’t get the built‐in operators and parsers to produce the error message you need, you can always drop down one API level and write a
     special‐purpose parser combinator.
    </p>
   </div>
   <div class="para _1">
    <p>
     The following example shows how you can define a custom <code class="fsharp"><a href="../reference/primitives.html#members.between"><span
     class="ci">between</span></a></code> combinator that includes the position of the opening delimiter as part of the error message that gets
     generated when the closing delimiter cannot be parsed.
    </p>
   </div>
   <div class="para _2 lcinp">
<pre class="code fsharp"><span class="ck">let</span> <span class="ci">betweenL</span> <span class="cp">(</span><span class="ci">popen</span><span class="cp">:</span> <a href="../reference/primitives.html#members.Parser"><span class="ci">Parser</span></a><span class="cp">&lt;</span><span class="ci">_</span><span class="cp">,</span><span class="ci">_</span><span class="cp">&gt;</span><span class="cp">)</span> <span class="cp">(</span><span class="ci">pclose</span><span class="cp">:</span> <a href="../reference/primitives.html#members.Parser"><span class="ci">Parser</span></a><span class="cp">&lt;</span><span class="ci">_</span><span class="cp">,</span><span class="ci">_</span><span class="cp">&gt;</span><span class="cp">)</span> <span class="cp">(</span><span class="ci">p</span><span class="cp">:</span> <a href="../reference/primitives.html#members.Parser"><span class="ci">Parser</span></a><span class="cp">&lt;</span><span class="ci">_</span><span class="cp">,</span><span class="ci">_</span><span class="cp">&gt;</span><span class="cp">)</span> <span class="ci">label</span> <span class="cp">=</span>
  <span class="ck">let</span> <span class="ci">expectedLabel</span> <span class="cp">=</span> <a href="../reference/error.html#members.expected"><span class="ci">expected</span></a> <span class="ci">label</span>
  <span class="ck">let</span> <span class="ci">notClosedError</span> <span class="cp">(</span><span class="ci">pos</span><span class="cp">:</span> <a href="../reference/charstream.html#CharStream.members.Position"><span class="ci">Position</span></a><span class="cp">)</span> <span class="cp">=</span>
     <a href="../reference/error.html#members.messageError"><span class="ci">messageError</span></a> <span class="cp">(</span><span class="ci">sprintf</span> <span class="cs"><span class="cld">"</span>The %s opened at %s was not closed.<span class="crd">"</span></span>
                           <span class="ci">label</span> <span class="cp">(</span><span class="ci">pos</span><span class="cm">.</span><span class="ci">ToString</span><span class="cp">()</span><span class="cp">)</span><span class="cp">)</span>
  <span class="ck">fun</span> <span class="cp">(</span><span class="ci">stream</span><span class="cp">:</span> <a href="../reference/charstream.html#CharStream"><span class="ci">CharStream</span></a><span class="cp">&lt;</span><span class="ci">_</span><span class="cp">&gt;</span><span class="cp">)</span> <span class="cr">-&gt;</span>
    <span class="clc"><span class="cld">//</span> The following code might look a bit complicated, but that's mainly</span>
    <span class="clc"><span class="cld">//</span> because we manually apply three parsers in sequence and have to merge</span>
    <span class="clc"><span class="cld">//</span> the errors when they refer to the same parser state.</span>
    <span class="ck">let</span> <span class="ci">state0</span> <span class="cp">=</span> <span class="ci">stream</span><span class="cm">.</span><a href="../reference/charstream.html#CharStream_1.members.State"><span class="ci">State</span></a>
    <span class="ck">let</span> <span class="ci">reply1</span> <span class="cp">=</span> <span class="ci">popen</span> <span class="ci">stream</span>
    <span class="ck">if</span> <span class="ci">reply1</span><span class="cm">.</span><a href="../reference/reply.html#members.Status"><span class="ci">Status</span></a> <span class="co">=</span> <a href="../reference/primitives.html#members.Ok"><span class="ci">Ok</span></a> <span class="ck">then</span>
      <span class="ck">let</span> <span class="ci">stateTag1</span> <span class="cp">=</span> <span class="ci">stream</span><span class="cm">.</span><a href="../reference/charstream.html#CharStream.members.StateTag"><span class="ci">StateTag</span></a>
      <span class="ck">let</span> <span class="ci">reply2</span> <span class="cp">=</span> <span class="ci">p</span> <span class="ci">stream</span>
      <span class="ck">let</span> <span class="ci">error2</span> <span class="cp">=</span> <span class="ck">if</span> <span class="ci">stateTag1</span> <span class="co">&lt;&gt;</span> <span class="ci">stream</span><span class="cm">.</span><a href="../reference/charstream.html#CharStream.members.StateTag"><span class="ci">StateTag</span></a> <span class="ck">then</span> <span class="ci">reply2</span><span class="cm">.</span><a href="../reference/reply.html#members.Error"><span class="ci">Error</span></a>
                   <span class="ck">else</span> <a href="../reference/error.html#members.mergeErrors"><span class="ci">mergeErrors</span></a> <span class="ci">reply1</span><span class="cm">.</span><a href="../reference/reply.html#members.Error"><span class="ci">Error</span></a> <span class="ci">reply2</span><span class="cm">.</span><a href="../reference/reply.html#members.Error"><span class="ci">Error</span></a>
      <span class="ck">if</span> <span class="ci">reply2</span><span class="cm">.</span><a href="../reference/reply.html#members.Status"><span class="ci">Status</span></a> <span class="co">=</span> <a href="../reference/primitives.html#members.Ok"><span class="ci">Ok</span></a> <span class="ck">then</span>
        <span class="ck">let</span> <span class="ci">stateTag2</span> <span class="cp">=</span> <span class="ci">stream</span><span class="cm">.</span><a href="../reference/charstream.html#CharStream.members.StateTag"><span class="ci">StateTag</span></a>
        <span class="ck">let</span> <span class="ci">reply3</span> <span class="cp">=</span> <span class="ci">pclose</span> <span class="ci">stream</span>
        <span class="ck">let</span> <span class="ci">error3</span> <span class="cp">=</span> <span class="ck">if</span> <span class="ci">stateTag2</span> <span class="co">&lt;&gt;</span> <span class="ci">stream</span><span class="cm">.</span><a href="../reference/charstream.html#CharStream.members.StateTag"><span class="ci">StateTag</span></a> <span class="ck">then</span> <span class="ci">reply3</span><span class="cm">.</span><a href="../reference/reply.html#members.Error"><span class="ci">Error</span></a>
                     <span class="ck">else</span> <a href="../reference/error.html#members.mergeErrors"><span class="ci">mergeErrors</span></a> <span class="ci">error2</span> <span class="ci">reply3</span><span class="cm">.</span><a href="../reference/reply.html#members.Error"><span class="ci">Error</span></a>
        <span class="ck">if</span> <span class="ci">reply3</span><span class="cm">.</span><a href="../reference/reply.html#members.Status"><span class="ci">Status</span></a> <span class="co">=</span> <a href="../reference/primitives.html#members.Ok"><span class="ci">Ok</span></a> <span class="ck">then</span>
          <a href="../reference/reply.html"><span class="ci">Reply</span></a><span class="cp">(</span><a href="../reference/primitives.html#members.Ok"><span class="ci">Ok</span></a><span class="cp">,</span> <span class="ci">reply2</span><span class="cm">.</span><a href="../reference/reply.html#members.Result"><span class="ci">Result</span></a><span class="cp">,</span> <span class="ci">error3</span><span class="cp">)</span>
        <span class="ck">else</span>
          <a href="../reference/reply.html"><span class="ci">Reply</span></a><span class="cp">(</span><span class="ci">reply3</span><span class="cm">.</span><a href="../reference/reply.html#members.Status"><span class="ci">Status</span></a><span class="cp">,</span>
                <a href="../reference/error.html#members.mergeErrors"><span class="ci">mergeErrors</span></a> <span class="ci">error3</span> <span class="cp">(</span><span class="ci">notClosedError</span> <span class="cp">(</span><span class="ci">state0</span><span class="cm">.</span><a href="../reference/charstream.html#CharStreamState.GetPosition"><span class="ci">GetPosition</span></a><span class="cp">(</span><span class="ci">stream</span><span class="cp">)</span><span class="cp">)</span><span class="cp">)</span><span class="cp">)</span>
      <span class="ck">else</span>
        <a href="../reference/reply.html"><span class="ci">Reply</span></a><span class="cp">(</span><span class="ci">reply2</span><span class="cm">.</span><a href="../reference/reply.html#members.Status"><span class="ci">Status</span></a><span class="cp">,</span> <span class="ci">reply2</span><span class="cm">.</span><a href="../reference/reply.html#members.Error"><span class="ci">Error</span></a><span class="cp">)</span>
    <span class="ck">else</span>
      <span class="ck">let</span> <span class="ci">error</span> <span class="cp">=</span> <span class="ck">if</span> <span class="ci">state0</span><span class="cm">.</span><a href="../reference/charstream.html#CharStreamState.Tag"><span class="ci">Tag</span></a> <span class="co">&lt;&gt;</span> <span class="ci">stream</span><span class="cm">.</span><a href="../reference/charstream.html#CharStream.members.StateTag"><span class="ci">StateTag</span></a> <span class="ck">then</span> <span class="ci">reply1</span><span class="cm">.</span><a href="../reference/reply.html#members.Error"><span class="ci">Error</span></a>
                  <span class="ck">else</span> <span class="ci">expectedLabel</span>
      <a href="../reference/reply.html"><span class="ci">Reply</span></a><span class="cp">(</span><span class="ci">reply1</span><span class="cm">.</span><a href="../reference/reply.html#members.Status"><span class="ci">Status</span></a><span class="cp">,</span> <span class="ci">error</span><span class="cp">)</span>
</pre>
   </div>
   <div class="para _3 lcinp">
    <p>
     The behaviour of the <code class="fsharp"><span class="ci">betweenL</span></code> combinator differs from that of the standard <code
     class="fsharp"><a href="../reference/primitives.html#members.between"><span class="ci">between</span></a></code> combinator in two ways:
    </p>
    <ul class="l1">
     <li class="_1">
      If <code class="fsharp"><span class="ci">popen</span></code> fails without changing the parser state, <code class="fsharp"><span
      class="ci">betweenL</span> <span class="ci">popen</span> <span class="ci">p</span> <span class="ci">pclose</span> <span
      class="ci">label</span></code> fails with <code class="fsharp"><a href="../reference/error.html#members.expected"><span
      class="ci">expected</span></a> <span class="ci">label</span></code>, just like <code class="fsharp"><a
      href="../reference/primitives.html#members.between"><span class="ci">between</span></a> <span class="ci">popen</span> <span
      class="ci">p</span> <span class="ci">pclose</span> <a href="../reference/primitives.html#members.:60::63::62:"><span
      class="co">&lt;?&gt;</span></a> <span class="ci">label</span></code> would have.
     </li>
     <li class="_2">
      If <code class="fsharp"><span class="ci">pclose</span></code> fails without changing the parser state, <code class="fsharp"><span
      class="ci">betweenL</span></code> additionally prints the opening position of the compound.
     </li>
    </ul>
   </div>
   <div class="para _4">
    <p>The following tests demonstrate this behaviour:</p>
   </div>
   <div class="para _5 lcinp">
<pre class="code fsharp"><span class="ck">let</span> <span class="ci">stringLiteral</span> <span class="cp">=</span> <span class="ci">betweenL</span> <span class="cp">(</span><span class="ci">str</span> <span class="cs"><span class="cld">"</span><span class="ce">\"</span><span class="crd">"</span></span><span class="cp">)</span> <span class="cp">(</span><span class="ci">str</span> <span class="cs"><span class="cld">"</span><span class="ce">\"</span><span class="crd">"</span></span><span class="cp">)</span>
                             <span class="cp">(</span><a href="../reference/charparsers.html#members.manySatisfy"><span class="ci">manySatisfy</span></a> <span class="cp">(</span><span class="cp">(</span><span class="co">&lt;&gt;</span><span class="cp">)</span> <span class="cc"><span class="cld">'</span>"<span class="crd">'</span></span><span class="cp">)</span><span class="cp">)</span>
                             <span class="cs"><span class="cld">"</span>string literal in double quotes<span class="crd">"</span></span>
</pre>
<pre class="code fsharp"><span class="cmd"><span class="cmdp">&gt;</span> <a href="../reference/charparsers.html#members.run"><span class="ci">run</span></a> <span class="ci">stringLiteral</span> <span class="cs"><span class="cld">"</span><span class="ce">\"</span>test<span class="ce">\"</span><span class="crd">"</span></span><span class="cp">;;</span></span>
<span class="cout">val it : ParserResult&lt;string,unit&gt; = Success: "test"

</span><span class="cmd"><span class="cmdp">&gt;</span> <a href="../reference/charparsers.html#members.run"><span class="ci">run</span></a> <span class="ci">stringLiteral</span> <span class="cs"><span class="cld">"</span><span class="ce">\"</span>test<span class="crd">"</span></span><span class="cp">;;</span></span>
<span class="cout">val it : ParserResult&lt;string,unit&gt; = Failure:
Error in Ln: 1 Col: 6
"test
     ^
Note: The error occurred at the end of the input stream.
Expecting: '"'
Other messages:
  The string literal in double quotes opened at (Ln: 1, Col: 1) was not closed.

</span><span class="cmd"><span class="cmdp">&gt;</span> <a href="../reference/charparsers.html#members.run"><span class="ci">run</span></a> <span class="ci">stringLiteral</span> <span class="cs"><span class="cld">"</span>test<span class="crd">"</span></span><span class="cp">;;</span></span>
<span class="cout">val it : ParserResult&lt;string,unit&gt; = Failure:
Error in Ln: 1 Col: 1
test
^
Expecting: string literal in double quotes
</span></pre>
   </div>
  </div>
 </div>
 </div>
 </div>
 </div>
</body>
</html>