]> ocean-lang.org Git - ocean/blob - csrc/scanner.mdc
scanner: clarify the interaction between parsing marks and comments/strings
[ocean] / csrc / scanner.mdc
1 # Lexical Scanner #
2
3 ## The Task at Hand ##
4
5 The main task of the lexical scanner is to convert a stream of
6 characters into a stream of tokens.  The tokens are then typically
7 used by a parser to extract the syntactic structure.
8
9 The stream of characters are assumed to be in memory identified by a
10 linked list of blocks, such as provided by the "[mdcode][]" literate
11 program extractor.  A single token may never cross a block boundary.
12
13 [mdcode]: mdcode.html
14
15 ###### includes
16         #include "mdcode.h"
17
18 The text is assumed to be UTF-8 though some matching assumes the
19 ASCII subset.  If the text provided does not conform to UTF-8 an error
20 will be reported and some number of bytes will be skipped.
21
22 ###### public types
23         #include <wchar.h>
24         #include <wctype.h>
25         #include <unicode/uchar.h>
26
27 Tokens are returned by successive calls to the main interface
28 function: `token_next()` which has a `state` structure to keep track
29 of where it is up to.  Each token carries not just a numeric
30 identifier but also the code block, the line and character within that
31 block, and the actual start and length using the `struct text` from
32 "mdcode".
33
34 ###### public types
35         struct token {
36                 int               num;
37                 struct code_node *node;
38                 struct text       txt;
39                 int               line, col;
40         };
41         struct token_state;
42
43 ###### private types
44         struct token_state {
45                 ## state fields
46         };
47
48 ###### exported functions
49         struct token token_next(struct token_state *state);
50
51 ###### main functions
52         struct token token_next(struct token_state *state)
53         {
54                 ## token_next init
55                 while (1) {
56                         wint_t ch;
57                         struct token tk;
58
59                         ## one token
60                 }
61         }
62
63 The `line` and `col` offsets are useful for reporting errors.
64 The `txt` provides the content when that is important.
65
66 ### Token types and configuration ##
67
68 The scanner is not completely general, yet not completely specified.
69 There are a fixed set of token types, though particular tokens within
70 those types can be distinguish via configuration.
71
72 Most token types may be explicitly ignored, as typically comments
73 would be.  The exact consequence of ignoring each token type varies
74 from token to token.
75
76 ###### public types
77         struct token_config {
78                 int ignored;    // bit set of ignored tokens.
79                 ## token config parameters
80         };
81
82 ###### state fields
83         struct token_config *conf;
84
85 ###### token_next init
86         int ignored = state->conf->ignored;
87
88
89 The different tokens are numbers, words, marks, strings, comments,
90 newlines, EOF, and indents, each of which is examined in detail below.
91
92 There are various cases where no token can be found in part of the
93 input.  All of these will be reported as an `TK_error` token.
94
95 It is possible to declare a number of strings which form distinct
96 tokens (rather than being grouped as e.g. 'word').  These are given
97 token numbers from `TK_reserved` upwards.
98
99 ###### public types
100         enum token_num {
101                 TK_error,
102                 ## token types
103                 TK_reserved
104         };
105
106 ### Numbers
107
108 Numbers are the messiest tokens to parse, primarily because they can
109 contain characters that also have meaning outside of number and,
110 particularly, immediately after numbers.
111
112 The obvious example is the '`-`' sign.  It can come inside a number for
113 a negative exponent, or after a number as a subtraction operator.  To
114 be sure we have parsed as best as possible we need to only allow the
115 '`-`' inside a number if it is after an exponent character.  This can be
116 `e` or `p` (for hex exponents), but `e` can also be a hexadecimal
117 digit, so we don't allow '`-`' after just any `e`.
118
119 To make matters worse, our language designer has decided to experiment
120 with allowing commas to be used as the decimal indicator, and spaces
121 to be used to separate groups of digits in large numbers.  Both of
122 these can reasonably be restricted to appear between two digits, so we
123 have to add that condition to our tests.
124
125 So we cannot just treat numbers as starting with a digit and being
126 followed by some set of characters.  We need more structure than that.
127
128 So:
129
130 - Numbers must start with a digit.
131 - If the first digit is zero, the next character must be a base
132   signifier (one of `xob`) or a decimal marker (`.` or `,`).
133   In the first case the first `p` or `P` may be followed by a sign.
134 - If the number doesn't start with `0` followed by one of `xob`, the
135   first `e` may be followed by a sign.
136 - Any digit or hex digit may be followed by a space or underscore
137   providing that the subsequence character is also a (hex) digit.
138   This rule will require an extra level of 'unget' to be
139   supported when handling characters.
140 - Otherwise any digits or ASCII letters are allowed.  We do not at
141   this point check that the digits given are permitted by the base.
142   That will happen when the token is converted to a number.
143
144 To allow easy configuration, the various non alphanumeric characters
145 are only permitted if they are listed in a configuration parameter.
146
147 ###### token config parameters
148         char *number_chars;
149
150 Note that numbers may not start with a period, so `.75` is not a
151 number.  This is not the norm, but is not unheard of.  Excluding these
152 numbers simplifies the rule at very little cost.
153
154 ###### token types
155         TK_number,
156
157 If TK_number is ignored, digits will result in an error unless they
158 are declared to be a start character for words.
159
160 ###### includes
161
162         #include <string.h>
163
164 ###### parse number
165
166         if (iswdigit(ch) && !(ignored & (1<<TK_number))) {
167                 int prev_special = 0;
168                 int expect_p = 0;
169                 int decimal_mark = 0;
170                 if (ch == '0') {
171                         wchar_t ch2 = get_char(state);
172                         if (strchr("xobXOB", ch2) != NULL)
173                                 expect_p = 1;
174                         unget_char(state);
175                 }
176                 while (1) {
177                         int sign_ok = 0;
178                         switch(expect_p) {
179                         case 0:
180                                 if (ch == 'e' || ch == 'E')
181                                         sign_ok = 1;
182                                 break;
183                         case 1:
184                                 if (ch == 'p' || ch == 'P')
185                                         sign_ok = 1;
186                                 break;
187                         }
188                         save_unget_state(state);
189                         ch = get_char(state);
190                         if (iswalnum(ch)) {
191                                 prev_special = 0;
192                                 continue;
193                         }
194                         if (ch == '+' || ch == '-') {
195                                 if (!sign_ok)
196                                         break;
197                                 expect_p = -1;
198                         }
199                         if (ch == '.' || ch == ',') {
200                                 if (decimal_mark)
201                                         break;
202                                 decimal_mark = 1;
203                         }
204                         if (prev_special) {
205                                 /* Don't allow that special char,
206                                  * need two 'ungets'
207                                  */
208                                 restore_unget_state(state);
209                                 break;
210                         }
211                         if (strchr(state->conf->number_chars, ch)) {
212                                 prev_special = 1;
213                                 continue;
214                         }
215                         /* non-number char */
216                         break;
217                 }
218                 /* We seem to have a "number" token */
219                 unget_char(state);
220                 close_token(state, &tk);
221                 tk.num = TK_number;
222                 return tk;
223         }
224
225 ### Words
226 Words start with a "start" character followed by the longest
227 sequence of "continue" characters.  The Unicode ID_START and
228 ID_CONTINUE sets are always permitted, but other ASCII characters
229 can be added to these sets.
230
231 ###### token config parameters
232         char *word_start;
233         char *word_cont;
234
235 ###### internal functions
236         static int is_word_start(wchar_t ch, struct token_config *conf)
237         {
238                 return iswalpha(ch) ||
239                        strchr(conf->word_start, ch) != NULL ||
240                        u_hasBinaryProperty(ch, UCHAR_ID_START);
241         }
242
243         static int is_word_continue(wchar_t ch, struct token_config *conf)
244         {
245                 return iswalnum(ch) ||
246                        strchr(conf->word_cont, ch) != NULL ||
247                        u_hasBinaryProperty(ch, UCHAR_ID_CONTINUE);
248         }
249
250 Words can be either known or unknown.  Known words are referred to as
251 "reserved words" and get a unique token number.  Unknown words are
252 "identifiers" and are syntactically a single token.
253
254 ###### token types
255         TK_ident,
256
257 A list of known words must be provided.  This list is shared with the
258 "marks" which are described next.  The list must be lexically sorted
259 and the length of the list must be given (`known_count`).
260 Tokens matching these known words are reported as the index of the
261 list added to `TK_reserved`.
262
263 ###### token config parameters
264         const char **words_marks;
265         int known_count;
266
267 ###### parse word
268
269         if (is_word_start(ch, state->conf)) {
270                 int n;
271                 /* A word: identifier or reserved */
272                 do
273                         ch = get_char(state);
274                 while (is_word_continue(ch, state->conf));
275                 unget_char(state);
276                 close_token(state, &tk);
277                 tk.num = TK_ident;
278                 if (ignored & (1<<TK_ident))
279                         tk.num = TK_error;
280                 n = find_known(state->conf, tk.txt);
281                 if (n >= 0)
282                         tk.num = TK_reserved + n;
283                 return tk;
284         }
285
286 ### Marks
287
288 Marks are generally one or more punctuation marks joined together.  It
289 would be nice to use the term "symbol" for these, but that causes
290 confusion in a subsequent discussion of the grammar, which has terminal
291 symbols and non-terminal symbols which are conceptually quite
292 different.  So strings of punctuation characters will be marks.
293
294 A "mark" consists of ASCII characters that are not white space, are not
295 "start" characters for words, and are not digits.
296 These will collectively be called mark characters.
297
298 ###### internal functions
299         static int is_mark(wchar_t ch, struct token_config *conf)
300         {
301                 return ch > ' ' &&
302                        ch < 0x7f &&
303                        !iswalnum(ch) &&
304                        strchr(conf->word_start, ch) == NULL;
305         }
306
307 As with words, there can be known and unknown marks, though the rules
308 are slightly different.
309
310 Two marks do not need to be separated by a non-mark characters.  This
311 is different from words which do need to be separated by at least one
312 non-continue character.
313
314 The scanner will normally prefer longer sequences of mark characters,
315 but will more strongly prefer known marks over unknown marks.  So if
316 it finds a known mark where adding one more character does not result
317 in a known mark, it will return that first known mark.
318
319 If no known mark is found we will test against strings and comments
320 below before giving up and assuming an unknown mark.
321
322 If an unknown mark contains a quote character or a comment marker, and
323 that token is not being ignored, then we terminate the unknown mark
324 before that quote or comment.  This ensure that an unknown mark
325 immediately before a string is handled correctly.
326
327 If `TK_mark` is ignored, then unknown marks as returned as an error.
328
329 ###### token types
330         TK_mark,
331
332 Known marks are included in the same list as the list of known words.
333
334 ###### parse mark
335         tk.num = TK_error;
336         while (is_mark(ch, state->conf)) {
337                 int n;
338                 wchar_t prev;
339                 close_token(state, &tk);
340                 n = find_known(state->conf, tk.txt);
341                 if (n >= 0)
342                         tk.num = TK_reserved + n;
343                 else if (tk.num != TK_error) {
344                         /* found a longest-known-mark */
345                         unget_char(state);
346                         close_token(state, &tk);
347                         return tk;
348                 }
349                 prev = ch;
350                 save_unget_state(state);
351                 ch = get_char(state);
352                 if (!(ignored && (1<<TK_string)) && is_quote(ch))
353                         break;
354                 if (prev == '#')
355                         break;
356                 if (prev == '/' && ch == '/' && tk.txt.len > 1) {
357                         restore_unget_state(state);
358                         break;
359                 }
360                 if (prev == '/' && ch == '*' && tk.txt.len > 1) {
361                         restore_unget_state(state);
362                         break;
363                 }
364         }
365         unget_char(state);
366         if (tk.num != TK_error) {
367                 close_token(state, &tk);
368                 return tk;
369         }
370
371 If we don't find a known mark, we will check for strings and comments
372 before assuming that we have an unknown mark
373
374 ###### parse mark
375         ## parse string
376         ## parse comment
377         ## unknown mark
378
379 ###### unknown mark
380         if (tk.txt.len) {
381                 if (ignored & (1<<TK_mark))
382                         tk.num = TK_error;
383                 else
384                         tk.num = TK_mark;
385                 return tk;
386         }
387
388 ### Strings
389
390 Strings start with one of single quote, double quote, or back quote
391 and continue until a matching character on the same line.  Any of
392 these characters can be included in the list of known marks and then
393 they will not be used for identifying strings.
394
395 Immediately following the close quote one or two ASCII letters may
396 appear.  These are somewhat like the arbitrary letters allowed in
397 "Numbers" above.  They can be used by the language in various ways.
398
399 If 3 identical quote characters appear in a row and are
400 followed by a newline, then this forms a multi-line string which
401 continues until an identical triple quote appears on a line preceded
402 only by whitespace and followed immediately by 0-2 ASCII letters and a newline.
403
404 Multi-line strings may not extend beyond the end of the `code_node` in
405 which they start.
406
407 Normal strings and multi-line strings are encoded as two different
408 token types.
409
410 ###### token types
411         TK_string,
412         TK_multi_string,
413
414 ###### internal functions
415         static int is_quote(wchar_t ch)
416         {
417                 return ch == '\'' || ch == '"' || ch == '`';
418         }
419
420 #### Multi-line strings
421
422 The multi-line string is checked for first.  If they are being
423 ignored, we fall through and treat a triple quote as an empty string
424 followed by the start of a new string.
425
426 ###### parse string
427         if (tk.txt.len == 3 &&
428             !(ignored & (1 << TK_multi_string)) &&
429             is_quote(tk.txt.txt[0]) &&
430             memcmp(tk.txt.txt, tk.txt.txt+1, 2) == 0 &&
431             is_newline(tk.txt.txt[3])) {
432                 // triple quote
433                 wchar_t first = tk.txt.txt[0];
434                 int qseen = 0;
435                 int at_sol = 1;
436                 while (!at_eon(state) && qseen < 3) {
437                         ch = get_char(state);
438                         if (is_newline(ch)) {
439                                 at_sol = 1;
440                                 qseen = 0;
441                         } else if (at_sol && ch == first) {
442                                 qseen += 1;
443                         } else if (ch != ' ' && ch != '\t') {
444                                 at_sol = 0;
445                                 qseen = 0;
446                         }
447                 }
448                 if (qseen != 3) {
449                         /* Hit end of node - error.
450                          * unget so the newline is seen,
451                          * but return rest of string as an error.
452                          */
453                         unget_char(state);
454                         close_token(state, &tk);
455                         tk.num = TK_error;
456                         return tk;
457                 }
458                 /* 2 letters are allowed */
459                 ch = get_char(state);
460                 if (iswalpha(ch))
461                         ch = get_char(state);
462                 if (iswalpha(ch))
463                         ch = get_char(state);
464                 /* Now we must have a newline, but we don't return it
465                  * whatever it is.*/
466                 unget_char(state);
467                 close_token(state, &tk);
468                 tk.num = TK_multi_string;
469                 if (!is_newline(ch))
470                         tk.num = TK_error;
471                 return tk;
472         }
473
474 #### Single-line strings
475
476 The sequence of marks collected may be more than a single-line
477 string, so we reset to the start and collect characters until
478 we find a close quote or a newline.
479
480 If `TK_string` is ignored, then quote characters will appear as `TK_mark`s.
481
482 ###### parse string
483         if (tk.txt.len && is_quote(tk.txt.txt[0]) &&
484             !(ignored & (1<<TK_string))) {
485                 wchar_t first = tk.txt.txt[0];
486                 reset_token(state, &tk);
487                 get_char(state);
488                 do
489                         ch = get_char(state);
490                 while (ch != first && !is_newline(ch));
491                 tk.num = TK_string;
492                 if (is_newline(ch)) {
493                         unget_char(state);
494                         tk.num = TK_error;
495                 }
496                 close_token(state, &tk);
497                 return tk;
498         }
499
500 ### Comments
501
502 Single line comments may start with '`//`' or '`#`' providing that these
503 are not known marks.  They continue to the end of the line.
504
505 Block comments start with '`/*`' if this is not a known mark.  They
506 continue to the first occurrence of '`*/`' and may not contain any
507 occurrence of '`/*`'.
508
509 Block comments can be wholly within one line or can continue over
510 multiple lines.  The multi-line version should be followed immediately
511 by a newline.  The Linux kernel contains over 285000 multi-line
512 comments are only 34 are followed by characters other than white space
513 (which should be removed) or a backslash (only needed in macros).  So
514 it would not suffer from this rule.
515
516 These two comment types are reported as two separate token types, and
517 consequently can be ignored separately.  When ignored a comment is
518 still parsed, but is discarded.
519
520 ###### token types
521         TK_line_comment,
522         TK_block_comment,
523
524 ###### internal functions
525         static int is_line_comment(struct text txt)
526         {
527                 return (txt.len >= 1 && txt.txt[0] == '#') ||
528                        (txt.len >= 2 && txt.txt[0] == '/' &&
529                                         txt.txt[1] == '/');
530         }
531
532         static int is_block_comment(struct text txt)
533         {
534                 return txt.len >= 2 && txt.txt[0] == '/' &&
535                        txt.txt[1] == '*';
536         }
537
538 #### Single line comments
539
540 A single-line comment continues up to, but not including the newline.
541
542 ###### parse comment
543
544         if (is_line_comment(tk.txt)) {
545                 while (!is_newline(ch))
546                         ch = get_char(state);
547                 unget_char(state);
548                 close_token(state, &tk);
549                 tk.num = TK_line_comment;
550                 if (ignored & (1 << TK_line_comment))
551                         continue;
552                 return tk;
553         }
554
555 #### Block comments
556
557 The token text collected so far could exceed the comment, so we need
558 to reset it first.
559
560 If we find an embedded `/*` we reset to just before the '/' and report
561 an error.  That way the next thing to be parsed will be the rest of
562 the comment.  This requires a double unget, so we need to save/restore
563 the unget state (explained later).
564
565 ###### parse comment
566
567         if (is_block_comment(tk.txt)) {
568                 wchar_t prev;
569                 int newlines = 0;
570                 reset_token(state, &tk);
571                 get_char(state);
572                 get_char(state);
573                 save_unget_state(state);
574                 ch = get_char(state);
575                 prev = 0;
576                 while (!at_eon(state) &&
577                        (prev != '/' || ch != '*') &&
578                        (prev != '*' || ch != '/')) {
579                         if (is_newline(ch))
580                                 newlines = 1;
581                         prev = ch;
582                         save_unget_state(state);
583                         ch = get_char(state);
584                 }
585                 close_token(state, &tk);
586                 if (at_eon(state)) {
587                         tk.num = TK_error;
588                         return tk;
589                 }
590                 if (prev == '/') {
591                         /* embedded.  Need to unget twice! */
592                         restore_unget_state(state);
593                         unget_char(state);
594                         tk.num = TK_error;
595                         return tk;
596                 }
597                 tk.num = TK_block_comment;
598                 if (newlines && !(ignored & (1<<TK_newline))) {
599                         /* next char must be newline */
600                         ch = get_char(state);
601                         unget_char(state);
602                         if (!is_newline(ch))
603                                 tk.num = TK_error;
604                 }
605                 if (tk.num == TK_error ||
606                     !(ignored & (1 << TK_block_comment)))
607                         return tk;
608                 continue;
609         }
610
611 ### Indents, Newlines, and White Space.
612
613 Normally white space is ignored.  However newlines can be important as
614 can indents, which are either after a newline or at the start of a
615 node (detected by `at_son()`);
616
617 ###### exported functions
618         static inline int is_newline(wchar_t ch)
619         {
620                 return ch == '\n' || ch == '\f' || ch == '\v';
621         }
622
623 ###### white space
624         if (ch <= ' ' && !is_newline(ch)
625             && ! at_son(state))
626                 continue;
627
628 If a line starts with more white-space than the previous non-blank
629 line - or if the first non-blank line in the document starts with any
630 white-space - then an "IN" is reported at the start of the line.
631
632 Before the next non-blank line which starts with less white space, or
633 at the latest at the end of the document, a matching "OUT" token
634 is reported.  There will always be an exact match between "IN" and
635 "OUT" tokens.
636
637 It is possible for "OUT" to be followed (almost) immediately by an
638 "IN".  This happens if, for example, the indent of three consecutive
639 lines are 0, 8, 4 spaces.  Before the second line we report an
640 "IN".  Before the third line we must report an "OUT", as 4 is less
641 than 8, then also an Ident as 4 is greater than 0.
642
643 ###### token types
644         TK_in,
645         TK_out,
646
647 For the purpose of measuring the length of white space, a tab adds at
648 least one space, and rounds up to a multiple of 8.
649
650 ###### exported functions
651         static inline int indent_tab(int indent)
652         {
653                 return (indent|7)+1;
654         }
655
656 We need to track the current levels of indent.  This requires some
657 sort of stack as indent levels are pushed on and popped off.  In
658 practice this stack is unlikely to often exceed 5 so we will used a
659 fixed stack of 20 indent levels.  More than this will be silently
660 ignored.
661
662 ###### state fields
663         int     indent_level;
664         int     indent_sizes[20];
665
666 #### Newlines
667
668 Newlines can optionally be reported.  Newlines within a block comment
669 or a multi-line string are not reported separately, but each of these
670 must be followed immediately by a newline so these constructs cannot
671 hide the fact that a newline was present.
672
673 When indents are being reported, the Newline which would normally be
674 reported immediately before the "IN" is delayed until after the
675 matching "OUT".  This makes an indented section act like a
676 continuation of the previous line to some extent.
677
678 A blank line would normally be reported simply as two consecutive Newline
679 tokens.  However if the subsequent line is indented (and indents are being
680 reported) then the right thing to do is less obvious as Newlines should be
681 delayed - but how many Newlines?
682
683 The approach we will take is to report the extra Newlines immediately after
684 the IN token, so the blank line is treated as though it were an indented
685 blank line.
686
687 ###### token types
688         TK_newline,
689
690 If we find a newline or white space at the start of a block, we keep
691 collecting spaces, tabs, and newlines until we find some real text.
692 Then depending on the indent we generate some number of tokens.  These
693 will be a sequence of "Newline OUT" pairs representing a decrease
694 in indent, then either a Newline or an IN depending on whether the
695 next line is indented, then zero or more Newlines representing all the
696 blank lines that have been skipped.
697
698 When a Newline leads to the next block of code there is a question of
699 whether the various Newline and OUT/IN tokens should appear to
700 pbelong to the earlier or later block.  This is addressed by processing
701 the tokens in two stages based on the relative indent levels of the
702 two blocks (each block has a base indent to which the actual indents
703 are added).
704
705 Any "Newline OUT" pairs needed to reduce the current indent to the
706 maximum of the base indents of the old and new blocks are generated
707 against the old block.  Then if the next block does not have an
708 increased indent, one more "Newline" is generated.
709
710 If further "Newline OUT" pairs are needed to get to the indent
711 level of the 'next' block, they are generated against that block,
712 though the first Newline is suppressed (it having already been
713 generated).
714
715 Finally the Newline or IN for the first line of the new block is
716 generated, unless the Newline needs to be suppressed because it
717 appeared at the end of the previous block.
718
719 This means that a block may start with an OUT or an IN, but
720 will only start with a Newline if it actually starts with a blank
721 line.
722
723 We will need to represent in the `token_state` where in this sequence
724 of delayed tokens we are.  As `state.col` records the target indent we
725 don't need to record how many OUTs or INs are needed.  We do
726 need to record the number of blank lines, and which of Newline and
727 OUT is needed next in the initial sequence of pairs.
728
729 For this we store one more than the number of blank lines as
730 `delayed_lines` and a flag for `out_next`.
731
732 ###### state fields
733         int check_indent;
734         int delayed_lines;
735         int out_next;
736
737 Generating these tokens involve two separate pieces of code.
738
739 Firstly we need to recognise white space and count the indents and
740 newlines.  These are recorded in the above state fields.
741
742 Separately we need, on each call to `token_next`, we need to check if
743 there are some delayed tokens and if so we need to advance the state
744 information and return one token.
745
746 ###### white space
747         if (is_newline(ch) || (at_son(state) && ch <= ' ')) {
748                 int newlines = 0;
749                 int was_son = at_son(state);
750                 if (ignored & (1<<TK_in)) {
751                         if (!is_newline(ch))
752                                 continue;
753                         if (ignored & (1<<TK_newline))
754                                 continue;
755                         tk.num = TK_newline;
756                         close_token(state, &tk);
757                         return tk;
758                 }
759                 // Indents are needed, so check all white space.
760                 while (ch <= ' ' && !at_eon(state)) {
761                         if (is_newline(ch))
762                                 newlines += 1;
763                         ch = get_char(state);
764                 }
765                 if (at_eon(state)) {
766                         newlines += 1;
767                         if (state->node->next &&
768                             state->node->next->indent > state->node->indent)
769                                 state->col = state->node->next->indent;
770                         else
771                                 state->col = state->node->indent;
772                 } else
773                         unget_char(state);
774                 state->delayed_lines = newlines;
775                 state->out_next = was_son;
776                 state->check_indent = 1;
777                 continue;
778         }
779
780
781 ###### delayed tokens
782
783         if (state->check_indent || state->delayed_lines) {
784                 if (state->col < state->indent_sizes[state->indent_level]) {
785                         if (!state->out_next &&
786                             !(ignored & (1<<TK_newline))) {
787                                 state->out_next = 1;
788                                 tk.num = TK_newline;
789                                 return tk;
790                         }
791                         state->indent_level -= 1;
792                         state->out_next = 0;
793                         tk.num = TK_out;
794                         return tk;
795                 }
796                 if (state->col > state->indent_sizes[state->indent_level] &&
797                     state->indent_level < sizeof(state->indent_sizes)-1) {
798                         state->indent_level += 1;
799                         state->indent_sizes[state->indent_level] = state->col;
800                         state->delayed_lines -= 1;
801                         tk.num = TK_in;
802                         return tk;
803                 }
804                 state->check_indent = 0;
805                 if (state->delayed_lines && !(ignored & (1<<TK_newline))) {
806                         tk.num = TK_newline;
807                         state->delayed_lines -= 1;
808                         return tk;
809                 }
810                 state->delayed_lines = 0;
811                 continue;
812         }
813
814 ### End of File
815
816 After the last newline in the file has been processed, a special
817 end-of-file token will be returned.  any further attempts to get more
818 tokens will continue to return the same end-of-file token.
819
820 ###### token types
821         TK_eof,
822
823
824 ###### white space
825         if (ch == WEOF) {
826                 tk.num = TK_eof;
827                 return tk;
828         }
829
830 ### Unknown Marks, or errors.
831
832 We have now handled all the possible known mark-like tokens.
833 If the token we have is not empty and `TK_mark` is allowed,
834 we have an unknown mark, otherwise this must be an error.
835
836 ###### unknown mark
837         /* one unknown character */
838         close_token(state, &tk);
839         tk.num = TK_error;
840         return tk;
841
842 ## Tools For The Task
843
844 You may have noticed that are few gaps we left in the above -
845 functions used without first defining them.  Doing so above would have
846 broken the flow.
847
848 ### Character by character
849
850 As we walk through the various `code_node`s we need to process whole
851 Unicode codepoints, and keep track of which line and column we are on.
852 We will assume for now that any printing character uses one column,
853 though that is not true in general.
854
855 As the text in a `code_node` may include an indent that identifies it as
856 being code, we need to be careful to strip that.  The `code_node` has
857 a flag that tells us whether or not we need to strip.
858
859 ###### includes
860         #include <memory.h>
861
862 ###### state fields
863         struct code_node *node;
864         int    offset;
865         int    line;
866         int    col;
867
868 ###### internal functions
869
870         static void do_strip(struct token_state *state)
871         {
872                 if (state->node->needs_strip) {
873                         int n = 4;
874                         while (n && state->node->code.txt[state->offset] == ' ') {
875                                 state->offset += 1;
876                                 n -= 1;
877                         }
878                         while (n == 4 && state->node->code.txt[state->offset] == '\t') {
879                                 state->offset += 1;
880                                 n -= 4;
881                         }
882                 }
883         }
884
885         static wint_t get_char(struct token_state *state)
886         {
887                 wchar_t next;
888                 size_t n;
889                 mbstate_t mbstate;
890
891                 if (state->node == NULL)
892                         return WEOF;
893                 if (state->node->code.len <= state->offset) {
894                         do
895                                 state->node = state->node->next;
896                         while (state->node && state->node->code.txt == NULL);
897                         state->offset = 0;
898                         if (state->node == NULL)
899                                 return WEOF;
900                         do_strip(state);
901                         state->line = state->node->line_no;
902                         state->col = state->node->indent;
903                 }
904
905                 ## before get_char
906
907                 memset(&mbstate, 0, sizeof(mbstate));
908
909                 n = mbrtowc(&next, state->node->code.txt + state->offset,
910                             state->node->code.len - state->offset,
911                             &mbstate);
912                 if (n == -2 || n == 0) {
913                         /* Not enough bytes - not really possible */
914                         next = '\n';
915                         state->offset = state->node->code.len;
916                 } else if (n == -1) {
917                         /* error */
918                         state->offset += 1;
919                         next = 0x7f; // an illegal character
920                 } else
921                         state->offset += n;
922
923                 if (next >= ' ') {
924                         state->col += 1;
925                 } else if (is_newline(next)) {
926                         state->line += 1;
927                         state->col = state->node->indent;
928                         do_strip(state);
929                 } else if (next == '\t') {
930                         state->col = indent_tab(state->col);
931                 }
932                 return next;
933         }
934
935 We will sometimes want to "unget" the last character as it needs to be
936 considered again as part of the next token.  So we need to store a
937 'previous' version of all metadata.
938
939 ###### state fields
940         int    prev_offset;
941         int    prev_line;
942         int    prev_col;
943
944 ###### before get_char
945         state->prev_offset = state->offset;
946         state->prev_line   = state->line;
947         state->prev_col    = state->col;
948
949 ###### internal functions
950
951         static void unget_char(struct token_state *state)
952         {
953                 if (state->node) {
954                         state->offset = state->prev_offset;
955                         state->line   = state->prev_line;
956                         state->col    = state->prev_col;
957                 }
958         }
959
960 We occasionally need a double-unget, particularly for numbers and
961 block comments.  We don't impose this cost on all scanning, but
962 require those code sections that need it to call `save_unget_state`
963 before each `get_char`, and then `restore_unget_state` when a
964 double-unget is needed.
965
966 ###### state fields
967         int     prev_offset2;
968         int     prev_line2;
969         int     prev_col2;
970
971 ###### internal functions
972         static void save_unget_state(struct token_state *state)
973         {
974                 state->prev_offset2 = state->prev_offset;
975                 state->prev_line2 = state->prev_line;
976                 state->prev_col2 = state->prev_col;
977         }
978
979         static void restore_unget_state(struct token_state *state)
980         {
981                 state->prev_offset = state->prev_offset2;
982                 state->prev_line = state->prev_line2;
983                 state->prev_col = state->prev_col2;
984         }
985
986 At the start of a token we don't want to be at the end of a code block
987 if we can help it.  To avoid this possibility, we 'get' and 'unget' a
988 single character.  This will move into the next non-empty code block
989 and leave the current pointer at the start of it.
990
991 This has to happen _after_ dealing with delayed tokens as some of them
992 must appear in the previous node.  When we do this, we need to reset
993 the data in the token.
994
995 ###### delayed tokens
996         if (at_eon(state)) {
997                 get_char(state);
998                 unget_char(state);
999                 tk.node = state->node;
1000                 if (state->node)
1001                         tk.txt.txt = state->node->code.txt + state->offset;
1002                 tk.line = state->line;
1003                 tk.col = state->col;
1004                 tk.txt.len = 0;
1005         }
1006
1007 ### Managing tokens
1008
1009 The current token is initialized to line up with the first character
1010 that we 'get' for each token.  When we have, or might have, a full
1011 token we can call `close_token` to set the `len` of the token
1012 appropriately.  This can safely be called multiple times.
1013
1014 Finally we occasionally (for single-line strings and block comments)
1015 need to reset to the beginning of the current token as we might have
1016 parsed too much already.  For that there is `reset_token`.
1017
1018 ###### one token
1019         tk.node = state->node;
1020         if (state->node)
1021                 tk.txt.txt = state->node->code.txt + state->offset;
1022         tk.line = state->line;
1023         tk.col = state->col;
1024         tk.txt.len = 0;
1025
1026 ###### internal functions
1027
1028         static void close_token(struct token_state *state,
1029                                 struct token *tk)
1030         {
1031                 tk->txt.len = (state->node->code.txt + state->offset)
1032                               - tk->txt.txt;
1033         }
1034
1035         static void reset_token(struct token_state *state, struct token *tok)
1036         {
1037                 state->prev_line = tok->line;
1038                 state->prev_col = tok->col;
1039                 state->prev_offset = tok->txt.txt - state->node->code.txt;
1040                 unget_char(state);
1041                 tok->txt.len = 0;
1042         }
1043
1044
1045 Tokens make not cross into the next `code_node`, and some tokens can
1046 include the newline at the and of a `code_node`, we must be able to
1047 easily check if we have reached the end.  Equally we need to know if
1048 we are at the start of a node, as white space is treated a little
1049 differently there.
1050
1051 ###### internal functions
1052
1053         static int at_son(struct token_state *state)
1054         {
1055                 return state->offset == 0;
1056         }
1057
1058         static int at_eon(struct token_state *state)
1059         {
1060                 // at end-of-node ??
1061                 return state->node == NULL ||
1062                        state->offset >= state->node->code.len;
1063         }
1064
1065 ### Find a known word
1066
1067 As the known-word list is sorted we can use a simple binary search.
1068 Following the pattern established in "mdcode", we will use a `struct
1069 text` with start and length to represent the code fragment we are
1070 searching for.
1071
1072 ###### internal functions
1073         static int find_known(struct token_config *conf, struct text txt)
1074         {
1075                 int lo = 0;
1076                 int hi = conf->known_count;
1077
1078                 while (lo + 1 < hi) {
1079                         int mid = (lo + hi) / 2;
1080                         int cmp = strncmp(conf->words_marks[mid],
1081                                           txt.txt, txt.len);
1082                         if (cmp == 0 && conf->words_marks[mid][txt.len])
1083                                 cmp = 1;
1084                         if (cmp <= 0)
1085                                 lo = mid;
1086                         else
1087                                 hi = mid;
1088                 }
1089                 if (strncmp(conf->words_marks[lo],
1090                            txt.txt, txt.len) == 0
1091                     && conf->words_marks[lo][txt.len] == 0)
1092                         return lo;
1093                 else
1094                         return -1;
1095         }
1096
1097 ### Bringing it all together
1098
1099 Now we have all the bits there is just one section missing:  combining
1100 all the token parsing code into one block.
1101
1102 The handling of delayed tokens (Newlines, INs, OUTs) must come
1103 first before we try getting another character.
1104
1105 Then we parse all the test, making sure that we check for known marks
1106 before strings and comments, but unknown marks after strings and comments.
1107
1108 This block of code will either return a token, or will choose to
1109 ignore one, in which case it will `continue` around to the top of the
1110 loop.
1111
1112 ###### one token
1113         ## delayed tokens
1114
1115         ch = get_char(state);
1116
1117         ## white space
1118         ## parse number
1119         ## parse word
1120         ## parse mark
1121
1122 ### Start and stop
1123
1124 As well as getting tokens, we need to be able to create the
1125 `token_state` to start with, and discard it later.
1126
1127 ###### includes
1128         #include <malloc.h>
1129
1130 ###### main functions
1131         struct token_state *token_open(struct code_node *code, struct
1132                                        token_config *conf)
1133         {
1134                 struct token_state *state = malloc(sizeof(*state));
1135                 memset(state, 0, sizeof(*state));
1136                 state->node = code;
1137                 state->line = code->line_no;
1138                 state->col = code->indent;
1139                 state->conf = conf;
1140                 do_strip(state);
1141                 return state;
1142         }
1143         void token_close(struct token_state *state)
1144         {
1145                 free(state);
1146         }
1147
1148 ###### exported functions
1149         struct token_state *token_open(struct code_node *code, struct
1150                                        token_config *conf);
1151         void token_close(struct token_state *state);
1152
1153 ### Trace tokens
1154
1155 Getting tokens is the main thing but it is also useful to be able to
1156 print out token information, particularly for tracing and testing.
1157
1158 Known tokens are printed verbatim.  Other tokens are printed as
1159 `type(content)` where content is truncated to a given number of characters.
1160
1161 The function for printing a truncated string (`text_dump`) is also exported
1162 so that it can be used to tracing processed strings too.
1163
1164 ###### includes
1165         #include <stdio.h>
1166
1167 ###### exported functions
1168         void token_trace(FILE *f, struct token tok, int max);
1169         void text_dump(FILE *f, struct text t, int max);
1170
1171 ###### main functions
1172
1173         void text_dump(FILE *f, struct text txt, int max)
1174         {
1175                 int i;
1176                 if (txt.len > max)
1177                         max -= 2;
1178                 else
1179                         max = txt.len;
1180                 for (i = 0; i < max; i++) {
1181                         char c = txt.txt[i];
1182                         if (c < ' ' || c > '~')
1183                                 fprintf(f, "\\x%02x", c & 0xff);
1184                         else if (c == '\\')
1185                                 fprintf(f, "\\\\");
1186                         else
1187                                 fprintf(f, "%c", c);
1188                 }
1189                 if (i < txt.len)
1190                         fprintf(f, "..");
1191         }
1192
1193         void token_trace(FILE *f, struct token tok, int max)
1194         {
1195                 static char *types[] = {
1196                         [TK_ident] = "ident",
1197                         [TK_mark] = "mark",
1198                         [TK_number] = "number",
1199                         [TK_string] = "string",
1200                         [TK_multi_string] = "mstring",
1201                         [TK_line_comment] = "lcomment",
1202                         [TK_block_comment] = "bcomment",
1203                         [TK_in] = "in",
1204                         [TK_out] = "out",
1205                         [TK_newline] = "newline",
1206                         [TK_eof] = "eof",
1207                         [TK_error] = "ERROR",
1208                         };
1209
1210                 switch (tok.num) {
1211                 default: /* known word or mark */
1212                         fprintf(f, "%.*s", tok.txt.len, tok.txt.txt);
1213                         break;
1214                 case TK_in:
1215                 case TK_out:
1216                 case TK_newline:
1217                 case TK_eof:
1218                         /* No token text included */
1219                         fprintf(f, "%s()", types[tok.num]);
1220                         break;
1221                 case TK_ident:
1222                 case TK_mark:
1223                 case TK_number:
1224                 case TK_string:
1225                 case TK_multi_string:
1226                 case TK_line_comment:
1227                 case TK_block_comment:
1228                 case TK_error:
1229                         fprintf(f, "%s(", types[tok.num]);
1230                         text_dump(f, tok.txt, max);
1231                         fprintf(f, ")");
1232                         break;
1233                 }
1234         }
1235
1236 ### And there we have it
1237
1238 We now have all the library functions defined for reading and printing
1239 tokens.  Now we just need C files to store them, and a mk file to make them.
1240
1241 ###### File: scanner.h
1242         ## public types
1243         ## exported functions
1244
1245 ###### File: libscanner.c
1246         ## includes
1247         #include "scanner.h"
1248         ## private types
1249         ## internal functions
1250         ## main functions
1251
1252 ###### File: scanner.mk
1253
1254         CFLAGS += -Wall -g
1255         all ::
1256         scanner.mk scanner.h libscanner.c : scanner.mdc
1257                 ./md2c scanner.mdc
1258         all :: libscanner.o
1259         libscanner.o : libscanner.c
1260                 $(CC) $(CFLAGS) -c libscanner.c
1261
1262 ## Processing numbers
1263
1264 Converting a `TK_number` token to a numerical value is a slightly
1265 higher level task than lexical analysis, and slightly lower than
1266 grammar parsing, so put it here - as an index if you like.
1267
1268 Importantly it will be used by the same testing rig that is used for
1269 testing the token scanner.
1270
1271 The numeric value that we will convert all numbers into is the `mpq_t`
1272 from the GNU high precision number library "libgmp".
1273
1274 ###### number includes
1275         #include <gmp.h>
1276         #include "mdcode.h"
1277
1278 Firstly we need to be able to parse a string of digits in a given base
1279 and possibly with a decimal marker.  We store this in an `mpz_t`
1280 integer and report the number of digits after the decimal mark.
1281
1282 On error we return zero and ensure that the 'mpz_t' has been freed, or
1283 had never been initialised.
1284
1285 ###### number functions
1286
1287         static int parse_digits(mpz_t num, struct text tok, int base,
1288                                 int *placesp)
1289         {
1290                 /* Accept digits up to 'base', ignore '_' and
1291                  * ' ' if they appear between two legal digits,
1292                  * and if `placesp` is not NULL, allow a single
1293                  * '.' or ',' and report the number of digits
1294                  * beyond there.
1295                  * Return number of characters processed (p),
1296                  * or 0 if something illegal was found.
1297                  */
1298                 int p;
1299                 int decimal = -1; // digits after marker
1300                 enum {Digit, Space, Other} prev = Other;
1301                 int digits = 0;
1302
1303                 for (p = 0; p < tok.len; p++) {
1304                         int dig;
1305                         char c = tok.txt[p];
1306
1307                         if (c == '_' || c == ' ') {
1308                                 if (prev != Digit)
1309                                         goto bad;
1310                                 prev = Space;
1311                                 continue;
1312                         }
1313                         if (c == '.' || c == ',') {
1314                                 if (prev != Digit)
1315                                         goto bad;
1316                                 if (!placesp || decimal >= 0)
1317                                         return p-1;
1318                                 decimal = 0;
1319                                 prev = Other;
1320                                 continue;
1321                         }
1322                         if (isdigit(c))
1323                                 dig = c - '0';
1324                         else if (isupper(c))
1325                                 dig = 10 + c - 'A';
1326                         else if (islower(c))
1327                                 dig = 10 + c - 'a';
1328                         else
1329                                 dig = base;
1330                         if (dig >= base) {
1331                                 if (prev == Space)
1332                                         p--;
1333                                 break;
1334                         }
1335                         prev = Digit;
1336                         if (digits)
1337                                 mpz_mul_ui(num, num, base);
1338                         else
1339                                 mpz_init(num);
1340                         digits += 1;
1341                         mpz_add_ui(num, num, dig);
1342                         if (decimal >= 0)
1343                                 decimal++;
1344                 }
1345                 if (digits == 0)
1346                         return 0;
1347                 if (placesp) {
1348                         if (decimal >= 0)
1349                                 *placesp = decimal;
1350                         else
1351                                 *placesp = 0;
1352                 }
1353                 return p;
1354         bad:
1355                 if (digits)
1356                         mpz_clear(num);
1357                 return 0;
1358         }
1359
1360 ###### number includes
1361         #include <ctype.h>
1362
1363 To parse a full number we need to consider the optional base, the
1364 mantissa, and the optional exponent.  We will treat these one at a
1365 time.
1366
1367 The base is indicated by a letter after a leading zero, which must be
1368 followed by a base letter or a period.  The base also determines the
1369 character which will mark an exponent.
1370
1371 ###### number vars
1372         int base = 10;
1373         char expc = 'e';
1374
1375 ###### parse base
1376
1377         if (tok.txt[0] == '0' && tok.len > 1) {
1378                 int skip = 0;
1379                 switch(tok.txt[1]) {
1380                 case 'x':
1381                 case 'X':
1382                         base = 16;
1383                         skip = 2;
1384                         expc = 'p';
1385                         break;
1386                 case 'o':
1387                 case 'O':
1388                         base = 8;
1389                         skip = 2;
1390                         expc = 'p';
1391                         break;
1392                 case 'b':
1393                 case 'B':
1394                         base = 2;
1395                         skip = 2;
1396                         expc = 'p';
1397                         break;
1398                 case '0':
1399                 case '1':
1400                 case '2':
1401                 case '3':
1402                 case '4':
1403                 case '5':
1404                 case '6':
1405                 case '7':
1406                 case '8':
1407                 case '9':
1408                 case '_':
1409                 case ' ':
1410                         // another digit is not permitted
1411                         // after a zero.
1412                         return 0;
1413                 default:
1414                         // must be decimal marker or trailing
1415                         // letter, which are OK;
1416                         break;
1417                 }
1418                 tok.txt += skip;
1419                 tok.len -= skip;
1420         }
1421
1422 After the base is the mantissa, which may contain a decimal mark, so
1423 we need to record the number of places.  We won't impose the number of
1424 places until we have the exponent as well.
1425
1426 ###### number vars
1427         int places =0;
1428         mpz_t mant;
1429         int d;
1430
1431 ###### parse mantissa
1432
1433         d = parse_digits(mant, tok, base, &places);
1434         if (d == 0)
1435                 return 0;
1436         tok.txt += d;
1437         tok.len -= d;
1438         mpq_init(num);
1439         mpq_set_z(num, mant);
1440         mpz_clear(mant);
1441
1442 After the mantissa number may come an exponent which may be positive
1443 or negative.  We assume at this point that we have seen the exponent
1444 character `expc`.
1445
1446 ###### number vars
1447         long lexp = 0;
1448         mpz_t exp;
1449         int esign = 1;
1450
1451 ###### parse exponent
1452         if (tok.len > 1) {
1453                 if (tok.txt[0] == '+') {
1454                         tok.txt++;
1455                         tok.len--;
1456                 } else if (tok.txt[0] == '-') {
1457                         esign = -1;
1458                         tok.txt++;
1459                         tok.len--;
1460                 }
1461         }
1462         d = parse_digits(exp, tok, 10, NULL);
1463         if (d == 0) {
1464                 mpq_clear(num);
1465                 return 0;
1466         }
1467         if (!mpz_fits_slong_p(exp)) {
1468                 mpq_clear(num);
1469                 mpz_clear(exp);
1470                 return 0;
1471         }
1472         lexp = mpz_get_si(exp) * esign;
1473         mpz_clear(exp);
1474         tok.txt += d;
1475         tok.len -= d;
1476
1477
1478 Now that we have the mantissa and the exponent we can multiply them
1479 together, also allowing for the number of digits after the decimal
1480 mark.
1481
1482 For base 10, we simply subtract the decimal places from the exponent.
1483 For the other bases, as the exponent is alway based on 2, even for
1484 octal and hex, we need a bit more detail.
1485 We then recover the sign from the exponent, as division is quite
1486 different from multiplication.
1487
1488 ###### calc exponent
1489         switch (base) {
1490         case 10:
1491         case 2:
1492                 lexp -= places;
1493                 break;
1494         case 16:
1495                 lexp -= 4*places;
1496                 break;
1497         case 8:
1498                 lexp -= 3*places;
1499                 break;
1500         }
1501         if (lexp < 0) {
1502                 lexp = -lexp;
1503                 esign = -1;
1504         } else
1505                 esign = 1;
1506
1507 Imposing the exponent on the number is also very different for base 10
1508 than for the others.  For the binary shift `gmp` provides a simple
1509 function.  For base 10 we use something like Russian Peasant
1510 Multiplication.
1511
1512 ###### calc exponent
1513         if (expc == 'e') {
1514                 mpq_t tens;
1515                 mpq_init(tens);
1516                 mpq_set_ui(tens, 10, 1);
1517                 while (1) {
1518                         if (lexp & 1) {
1519                                 if (esign > 0)
1520                                         mpq_mul(num, num, tens);
1521                                 else
1522                                         mpq_div(num, num, tens);
1523                         }
1524                         lexp >>= 1;
1525                         if (lexp == 0)
1526                                 break;
1527                         mpq_mul(tens, tens, tens);
1528                 }
1529                 mpq_clear(tens);
1530         } else {
1531                 if (esign > 0)
1532                         mpq_mul_2exp(num, num, lexp);
1533                 else
1534                         mpq_div_2exp(num, num, lexp);
1535         }
1536
1537 Now we are ready to parse a number: the base, mantissa, and exponent.
1538 If all goes well we check for the possible trailing letters and
1539 return.  Return value is 1 for success and 0 for failure.
1540
1541
1542 ###### number functions
1543         int number_parse(mpq_t num, char tail[3], struct text tok)
1544         {
1545                 ## number vars
1546                 int i;
1547
1548                 ## parse base
1549                 ## parse mantissa
1550                 if (tok.len > 1 && (tok.txt[0] == expc ||
1551                                     tok.txt[0] == toupper(expc))) {
1552                         tok.txt++;
1553                         tok.len--;
1554                         ## parse exponent
1555                 }
1556                 ## calc exponent
1557
1558                 for (i = 0; i < 2; i++) {
1559                         if (tok.len <= i)
1560                                 break;
1561                         if (!isalpha(tok.txt[i]))
1562                                 goto err;
1563                         tail[i] = tok.txt[i];
1564                 }
1565                 tail[i] = 0;
1566                 if (i == tok.len)
1567                         return 1;
1568         err:
1569                 mpq_clear(num);
1570                 return 0;
1571         }
1572
1573 Number parsing goes in `libnumber.c`
1574
1575 ###### File: libnumber.c
1576
1577         #include <unistd.h>
1578         #include <stdlib.h>
1579
1580         ## number includes
1581         ## number functions
1582
1583 ###### File: number.h
1584         int number_parse(mpq_t num, char tail[3], struct text tok);
1585
1586 ###### File: scanner.mk
1587         all :: libnumber.o
1588         libnumber.o : libnumber.c
1589                 $(CC) $(CFLAGS) -c libnumber.c
1590
1591 ## Processing strings
1592
1593 Both `TK_string` and `TK_multi_string` require post-processing which
1594 can be one of two types: literal or with escapes processed.
1595 Even literal processing is non-trivial as the file may contain indents
1596 which need to be stripped.
1597
1598 Errors can only occur when processing escapes.  Any unrecognised
1599 character following the escape character will cause an error.
1600
1601 Processing escapes and striping indents can only make the string
1602 shorter, not longer, so we allocate a buffer which is the same size as
1603 the string and process into that.
1604
1605 To request escape processing, we pass the character we want to use for
1606 quoting, usually '`\`'.  To avoid escape processing we pass a zero.
1607
1608 ###### string main
1609         int string_parse(struct token *tok, char escape,
1610                          struct text *str, char tail[3])
1611         {
1612                 ## string vars
1613                 struct text t = tok->txt;
1614
1615                 str->txt = NULL;
1616                 ## strip tail
1617                 if (tok->num == TK_string) {
1618                         ## strip single
1619                 } else {
1620                         ## strip multi
1621                 }
1622                 str->txt = malloc(t.len);
1623                 str->len = 0;
1624
1625                 ## process string
1626                 return 1;
1627         err:
1628                 free(str->txt);
1629                 str->txt = NULL;
1630                 return 0;
1631         }
1632
1633 ### strip tail
1634
1635 The tail of the string can be 0, 1, or 2 letters
1636
1637         i = t.len;
1638         if (i >= 0 && isalpha(t.txt[i-1]))
1639                 i -= 1;
1640         if (i >= 0 && isalpha(t.txt[i-1]))
1641                 i -= 1;
1642         strncpy(tail, t.txt+i, t.len-i);
1643         tail[t.len-i] = 0;
1644         t.len = i;
1645
1646 ###### string vars
1647         int i;
1648
1649 ### strip single
1650
1651 Stripping the quote of a single-line string is trivial.
1652 The only part that is at all interesting is that quote character must
1653 be remembered.
1654
1655         quote = t.txt[0];
1656         if (t.txt[t.len-1] != quote)
1657                 goto err;
1658         t.txt += 1;
1659         t.len -= 2;
1660
1661 ###### string vars
1662         char quote;
1663
1664 ### strip multi
1665
1666 For a multi-line string we have a little more work to do.  We need to
1667 remove 3 quotes, not 1, and need to count the indent of the close
1668 quote as it will need to be stripped from all lines.
1669
1670         quote = t.txt[0];
1671         if (t.len < 7 ||
1672             t.txt[1] != quote || t.txt[2] != quote ||
1673             !is_newline(t.txt[3]))
1674                 goto err;
1675         t.txt += 4;
1676         t.len -= 4;
1677         i = t.len;
1678         if (i <= 0 || t.txt[i-1] != quote)
1679                 goto err;
1680         i -= 1;
1681         if (i <= 0 || t.txt[i-1] != quote)
1682                 goto err;
1683         i -= 1;
1684         if (i <= 0 || t.txt[i-1] != quote)
1685                 goto err;
1686         i -= 1;
1687         t.len = i;
1688         while (i > 0 && !is_newline(t.txt[i-1]))
1689                 i--;
1690         indent = 0;
1691         while (i < t.len) {
1692                 if (t.txt[i] == ' ')
1693                         indent += 1;
1694                 if (t.txt[i] == '\t')
1695                         indent = indent_tab(indent);
1696                 i++;
1697         }
1698
1699 ###### string vars
1700         int indent = 0;
1701
1702 ### process string
1703
1704 Now we just take one byte at a time. trans-ASCII unicode won't look
1705 like anything we are interested in so it will just be copied byte by
1706 byte.
1707
1708         cp = str->txt;
1709         at_sol = 1;
1710         for (i = 0; i < t.len; i++) {
1711                 char c;
1712                 if (at_sol) {
1713                         at_sol = 0;
1714                         ## strip indent
1715                         if (i >= t.len)
1716                                 break;
1717                 }
1718                 c = t.txt[i];
1719                 if (c != escape) {
1720                         *cp = c;
1721                         cp += 1;
1722                         if (is_newline(c))
1723                                 at_sol = 1;
1724                 } else if (i+1 >= t.len) {
1725                         // escape and end of string
1726                         goto err;
1727                 } else {
1728                         i += 1;
1729                         c = t.txt[i];
1730                         ## parse escape
1731                 }
1732         }
1733         str->len = cp - str->txt;
1734
1735 ###### string vars
1736         char *cp;
1737         int at_sol;
1738
1739 ### strip indent
1740
1741 Every time we find a start of line, we strip spaces and tabs until the
1742 required indent is found.
1743
1744         int skipped = 0;
1745         while (i < t.len && skipped < indent) {
1746                 c = t.txt[i];
1747                 if (c == ' ')
1748                         skipped += 1;
1749                 else if (c == '\t')
1750                         skipped = indent_tab(c);
1751                 else
1752                         break;
1753                 i+= 1;
1754         }
1755
1756 ### parse escape
1757         switch (c) {
1758         case 'n':
1759                 *cp++ = '\n'; break;
1760         case 'r':
1761                 *cp++ = '\r'; break;
1762         case 't':
1763                 *cp++ = '\t'; break;
1764         case 'b':
1765                 *cp++ = '\b'; break;
1766         case 'q':
1767                 *cp++ = quote; break;
1768         case 'f':
1769                 *cp++ = '\f'; break;
1770         case 'v':
1771                 *cp++ = '\v'; break;
1772         case 'a':
1773                 *cp++ = '\a'; break;
1774         case '0':
1775         case '1':
1776         case '2':
1777         case '3':
1778                 // 3 digit octal number
1779                 if (i+2 >= t.len)
1780                         goto err;
1781                 if (t.txt[i+1] < '0' || t.txt[i+1] > '7' ||
1782                     t.txt[i+2] < '0' || t.txt[i+1] > '7')
1783                         goto err;
1784                 n = (t.txt[i  ]-'0') * 64 +
1785                     (t.txt[i+1]-'0') *  8 +
1786                     (t.txt[i+2]-'0') *  1;
1787                 *cp++ = n;
1788                 i += 2;
1789                 break;
1790         case 'x':
1791                 // 2 hex digits
1792                 n = take_hex(2, t.txt+i+1, t.len-i-1);
1793                 if (n < 0)
1794                         goto err;
1795                 *cp++ = n;
1796                 i += 2;
1797                 break;
1798         case 'u':
1799         case 'U':
1800                 // 4 or 8 hex digits for unicode
1801                 n = take_hex(c == 'u'?4:8, t.txt+i+1, t.len-i-1);
1802                 if (n < 0)
1803                         goto err;
1804                 memset(&pstate, 0, sizeof(pstate));
1805                 n = wcrtomb(cp, n, &pstate);
1806                 if (n <= 0)
1807                         goto err;
1808                 cp += n;
1809                 i += c == 'u' ? 4 : 8;
1810                 break;
1811         default:
1812                 if (c == escape)
1813                         *cp++ = c;
1814                 else if (is_newline(c))
1815                         at_sol = 1;
1816                 else
1817                         goto err;
1818         }
1819
1820 ###### string vars
1821         long n;
1822         mbstate_t pstate;
1823
1824 For `\x` `\u` and `\U` we need to collect a specific number of
1825 hexadecimal digits
1826
1827 ###### string functions
1828
1829         static long take_hex(int digits, char *cp, int l)
1830         {
1831                 long n = 0;
1832                 if (l < digits)
1833                         return -1;
1834                 while (digits) {
1835                         char  c = *cp;
1836                         int d;
1837                         if (!isxdigit(c))
1838                                 return -1;
1839                         if (isdigit(c))
1840                                 d = c - '0';
1841                         else if (isupper(c))
1842                                 d = 10 + c - 'A';
1843                         else
1844                                 d = 10 + c - 'a';
1845                         n = n * 16 + d;
1846                         digits--;
1847                         cp++;
1848                 }
1849                 return n;
1850         }
1851
1852 #### File: libstring.c
1853
1854 String parsing goes in `libstring.c`
1855
1856         #include <unistd.h>
1857         #include <stdlib.h>
1858         #include <stdio.h>
1859         #include <string.h>
1860         #include <ctype.h>
1861         #include <wchar.h>
1862         #include "mdcode.h"
1863         #include "scanner.h"
1864         ## string functions
1865         ## string main
1866
1867 ###### File: string.h
1868         int string_parse(struct token *tok, char escape,
1869                          struct text *str, char tail[3]);
1870
1871 ###### File: scanner.mk
1872         all :: libstring.o
1873         libstring.o : libstring.c
1874                 $(CC) $(CFLAGS) -c libstring.c
1875
1876
1877 ## Testing
1878
1879 As "untested code is buggy code" we need a program to easily test
1880 the scanner library.  This will simply parse a given file and report
1881 the tokens one per line.
1882
1883 ###### File: scanner.c
1884
1885         #include <unistd.h>
1886         #include <stdlib.h>
1887         #include <fcntl.h>
1888         #include <errno.h>
1889         #include <sys/mman.h>
1890         #include <string.h>
1891         #include <stdio.h>
1892         #include <gmp.h>
1893         #include <locale.h>
1894         #include "mdcode.h"
1895         #include "scanner.h"
1896         #include "number.h"
1897         #include "string.h"
1898
1899         static int errs;
1900         static void pr_err(char *msg)
1901         {
1902                 errs++;
1903                 fprintf(stderr, "%s\n", msg);
1904         }
1905
1906         int main(int argc, char *argv[])
1907         {
1908                 int fd;
1909                 int len;
1910                 char *file;
1911                 struct token_state *state;
1912                 const char *known[] = {
1913                         "==",
1914                         "else",
1915                         "if",
1916                         "then",
1917                         "while",
1918                         "{",
1919                         "}",
1920                 };
1921                 struct token_config conf = {
1922                         .word_start = "_$",
1923                         .word_cont = "",
1924                         .words_marks = known,
1925                         .number_chars = "., _+-",
1926                         .known_count = sizeof(known)/sizeof(known[0]),
1927                         .ignored = (0 << TK_line_comment)
1928                                   |(0 << TK_block_comment),
1929                 };
1930                 struct section *table, *s, *prev;
1931                 setlocale(LC_ALL,"");
1932                 if (argc != 2) {
1933                         fprintf(stderr, "Usage: scanner file\n");
1934                         exit(2);
1935                 }
1936                 fd = open(argv[1], O_RDONLY);
1937                 if (fd < 0) {
1938                         fprintf(stderr, "scanner: cannot open %s: %s\n",
1939                                 argv[1], strerror(errno));
1940                         exit(1);
1941                 }
1942                 len = lseek(fd, 0, 2);
1943                 file = mmap(NULL, len, PROT_READ, MAP_SHARED, fd, 0);
1944                 table = code_extract(file, file+len, pr_err);
1945
1946                 for (s = table; s;
1947                         (code_free(s->code), prev = s, s = s->next, free(prev))) {
1948                         printf("Tokenizing: %.*s\n", s->section.len,
1949                                 s->section.txt);
1950                         state = token_open(s->code, &conf);
1951                         while(1) {
1952                                 struct token tk = token_next(state);
1953                                 printf("%d:%d ", tk.line, tk.col);
1954                                 token_trace(stdout, tk, 20);
1955                                 if (tk.num == TK_number) {
1956                                         mpq_t num;
1957                                         char tail[3];
1958                                         if (number_parse(num, tail,tk.txt)) {
1959                                                 printf(" %s ", tail);
1960                                                 mpq_out_str(stdout, 10, num);
1961                                                 mpq_clear(num);
1962                                         } else
1963                                                 printf(" BAD NUMBER");
1964                                 }
1965                                 if (tk.num == TK_string ||
1966                                     tk.num == TK_multi_string) {
1967                                         char esc = '\\';
1968                                         struct text str;
1969                                         char tail[3];
1970                                         if (tk.txt.txt[0] == '`')
1971                                                 esc = 0;
1972                                         if (string_parse(&tk, esc,
1973                                                          &str, tail)) {
1974                                                 printf(" %s ", tail);
1975                                                 text_dump(stdout, str, 20);
1976                                                 free(str.txt);
1977                                         } else
1978                                                 printf(" BAD STRING");
1979                                 }
1980                                 printf("\n");
1981                                 if (tk.num == TK_error)
1982                                         errs = 1;
1983                                 if (tk.num == TK_eof)
1984                                         break;
1985                         }
1986                 }
1987                 exit(!!errs);
1988         }
1989 ###### File: scanner.mk
1990         scanner.c : scanner.mdc
1991                 ./md2c scanner.mdc
1992         all :: scanner
1993         scanner : scanner.o scanner.h libscanner.o libmdcode.o mdcode.h
1994                 $(CC) $(CFLAGS) -o scanner scanner.o libscanner.o \
1995                         libmdcode.o libnumber.o libstring.o -licuuc -lgmp
1996         scanner.o : scanner.c
1997                 $(CC) $(CFLAGS) -c scanner.c
1998