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