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