]> ocean-lang.org Git - ocean/blob - csrc/scanner.mdc
scanner.mdc: lexical scanner for Ocean.
[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         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                         return tk;
726                 }
727                 // Indents are needed, so check all white space.
728                 while (ch <= ' ' && !at_eon(state)) {
729                         if (is_newline(ch))
730                                 newlines += 1;
731                         ch = get_char(state);
732                 }
733                 if (at_eon(state)) {
734                         newlines += 1;
735                         if (state->node->next &&
736                             state->node->next->indent > state->node->indent)
737                                 state->col = state->node->next->indent;
738                         else
739                                 state->col = state->node->indent;
740                 } else
741                         unget_char(state);
742                 state->delayed_lines = newlines;
743                 state->undent_next = was_son;
744                 state->check_indent = 1;
745                 continue;
746         }
747
748
749 ###### delayed tokens
750
751         if (state->check_indent || state->delayed_lines) {
752                 if (state->col < state->indent_sizes[state->indent_level]) {
753                         if (!state->undent_next &&
754                             !(ignored & (1<<TK_newline))) {
755                                 state->undent_next = 1;
756                                 tk.num = TK_newline;
757                                 return tk;
758                         }
759                         state->indent_level -= 1;
760                         state->undent_next = 0;
761                         tk.num = TK_undent;
762                         return tk;
763                 }
764                 if (state->col > state->indent_sizes[state->indent_level] &&
765                     state->indent_level < sizeof(state->indent_sizes)-1) {
766                         state->indent_level += 1;
767                         state->indent_sizes[state->indent_level] = state->col;
768                         state->delayed_lines -= 1;
769                         tk.num = TK_indent;
770                         return tk;
771                 }
772                 state->check_indent = 0;
773                 if (state->delayed_lines && !(ignored & (1<<TK_newline))) {
774                         tk.num = TK_newline;
775                         state->delayed_lines -= 1;
776                         return tk;
777                 }
778                 state->delayed_lines = 0;
779                 continue;
780         }
781
782 ### End of File
783
784 After the last newline in the file has been processed, a special
785 end-of-file token will be returned.  any further attempts to get more
786 tokens will continue to return the same end-of-file token.
787
788 ###### token types
789         TK_eof,
790
791
792 ###### white space
793         if (ch == WEOF) {
794                 tk.num = TK_eof;
795                 return tk;
796         }
797
798 ### Unknown Marks, or errors.
799
800 We have now handled all the possible known mark-like tokens.
801 If the token we have is not empty and `TK_mark` is allowed,
802 we have an unknown mark, otherwise this must be an error.
803
804 ###### unknown mark
805         /* one unknown character */
806         close_token(state, &tk);
807         tk.num = TK_error;
808         return tk;
809
810 ## Tools For The Task
811
812 You may have noticed that are few gaps we left in the above -
813 functions used without first defining them.  Doing so above would have
814 broken the flow.
815
816 ### Character by character
817
818 As we walk through the various `code_node`s we need to process whole
819 Unicode codepoints, and keep track of which line and column we are on.
820 We will assume for now that any printing character uses one column,
821 though that is not true in general.
822
823 As the text in a `code_node` may include an indent that identifies it as
824 being code, we need to be careful to strip that.  The `code_node` has
825 a flag that tells us whether or not we need to strip.
826
827 ###### includes
828         #include <memory.h>
829
830 ###### state fields
831         struct code_node *node;
832         int    offset;
833         int    line;
834         int    col;
835
836 ###### internal functions
837
838         static void do_strip(struct token_state *state)
839         {
840                 if (state->node->needs_strip) {
841                         int n = 4;
842                         while (n && state->node->code.txt[state->offset] == ' ') {
843                                 state->offset += 1;
844                                 n -= 1;
845                         }
846                         while (n == 4 && state->node->code.txt[0] == '\t') {
847                                 state->offset += 1;
848                                 n -= 4;
849                         }
850                 }
851         }
852
853         static wint_t get_char(struct token_state *state)
854         {
855                 wchar_t next;
856                 size_t n;
857                 mbstate_t mbstate;
858
859                 if (state->node == NULL)
860                         return WEOF;
861                 if (state->node->code.len <= state->offset) {
862                         do
863                                 state->node = state->node->next;
864                         while (state->node && state->node->code.txt == NULL);
865                         state->offset = 0;
866                         if (state->node == NULL)
867                                 return WEOF;
868                         do_strip(state);
869                         state->line = state->node->line_no;
870                         state->col = state->node->indent;
871                 }
872
873                 ## before get_char
874
875                 memset(&mbstate, 0, sizeof(mbstate));
876
877                 n = mbrtowc(&next, state->node->code.txt + state->offset,
878                             state->node->code.len - state->offset,
879                             &mbstate);
880                 if (n == -2 || n == 0) {
881                         /* Not enough bytes - not really possible */
882                         next = '\n';
883                         state->offset = state->node->code.len;
884                 } else if (n == -1) {
885                         /* error */
886                         state->offset += 1;
887                         next = 0x7f; // an illegal character
888                 } else
889                         state->offset += n;
890
891                 if (next >= ' ') {
892                         state->col += 1;
893                 } else if (is_newline(next)) {
894                         state->line += 1;
895                         state->col = state->node->indent;
896                         do_strip(state);
897                 } else if (next == '\t') {
898                         state->col = indent_tab(state->col);
899                 }
900                 return next;
901         }
902
903 We will sometimes want to "unget" the last character as it needs to be
904 considered again as part of the next token.  So we need to store a
905 'previous' version of all metadata.
906
907 ###### state fields
908         int    prev_offset;
909         int    prev_line;
910         int    prev_col;
911
912 ###### before get_char
913         state->prev_offset = state->offset;
914         state->prev_line   = state->line;
915         state->prev_col    = state->col;
916
917 ###### internal functions
918
919         static void unget_char(struct token_state *state)
920         {
921                 if (state->node) {
922                         state->offset = state->prev_offset;
923                         state->line   = state->prev_line;
924                         state->col    = state->prev_col;
925                 }
926         }
927
928 We occasionally need a double-unget, particularly for numbers and
929 block comments.  We don't impose this cost on all scanning, but
930 require those code sections that need it to call `save_unget_state`
931 before each `get_char`, and then `restore_unget_state` when a
932 double-unget is needed.
933
934 ###### state fields
935         int     prev_offset2;
936         int     prev_line2;
937         int     prev_col2;
938
939 ###### internal functions
940         static void save_unget_state(struct token_state *state)
941         {
942                 state->prev_offset2 = state->prev_offset;
943                 state->prev_line2 = state->prev_line;
944                 state->prev_col2 = state->prev_col;
945         }
946
947         static void restore_unget_state(struct token_state *state)
948         {
949                 state->prev_offset = state->prev_offset2;
950                 state->prev_line = state->prev_line2;
951                 state->prev_col = state->prev_col2;
952         }
953
954 At the start of a token we don't want to be at the end of a code block
955 if we can help it.  To avoid this possibility, we 'get' and 'unget' a
956 single character.  This will move into the next non-empty code block
957 and leave the current pointer at the start of it.
958
959 This has to happen _after_ dealing with delayed tokens as some of them
960 must appear in the previous node.  When we do this, we need to reset
961 the data in the token.
962
963 ###### delayed tokens
964         if (at_eon(state)) {
965                 get_char(state);
966                 unget_char(state);
967                 tk.node = state->node;
968                 if (state->node)
969                         tk.txt.txt = state->node->code.txt + state->offset;
970                 tk.line = state->line;
971                 tk.col = state->col;
972                 tk.txt.len = 0;
973         }
974
975 ### Managing tokens
976
977 The current token is initialized to line up with the first character
978 that we 'get' for each token.  When we have, or might have, a full
979 token we can call `close_token` to set the `len` of the token
980 appropriately.  This can safely be called multiple times.
981
982 Finally we occasionally (for single-line strings and block comments)
983 need to reset to the beginning of the current token as we might have
984 parsed too much already.  For that there is `reset_token`.
985
986 ###### one token
987         tk.node = state->node;
988         if (state->node)
989                 tk.txt.txt = state->node->code.txt + state->offset;
990         tk.line = state->line;
991         tk.col = state->col;
992         tk.txt.len = 0;
993
994 ###### internal functions
995
996         static void close_token(struct token_state *state,
997                                 struct token *tk)
998         {
999                 tk->txt.len = (state->node->code.txt + state->offset)
1000                               - tk->txt.txt;
1001         }
1002
1003         static void reset_token(struct token_state *state, struct token *tok)
1004         {
1005                 state->prev_line = tok->line;
1006                 state->prev_col = tok->col;
1007                 state->prev_offset = tok->txt.txt - state->node->code.txt;
1008                 unget_char(state);
1009                 tok->txt.len = 0;
1010         }
1011
1012
1013 Tokens make not cross into the next `code_node`, and some tokens can
1014 include the newline at the and of a `code_node`, we must be able to
1015 easily check if we have reached the end.  Equally we need to know if
1016 we are at the start of a node, as white space is treated a little
1017 differently there.
1018
1019 ###### internal functions
1020
1021         static int at_son(struct token_state *state)
1022         {
1023                 return state->offset == 0;
1024         }
1025
1026         static int at_eon(struct token_state *state)
1027         {
1028                 // at end-of-node ??
1029                 return state->node == NULL ||
1030                        state->offset >= state->node->code.len;
1031         }
1032
1033 ### Find a known word
1034
1035 As the known-word list is sorted we can use a simple binary search.
1036 Following the pattern established in "mdcode", we will use a `struct
1037 text` with start and length to represent the code fragment we are
1038 searching for.
1039
1040 ###### internal functions
1041         static int find_known(struct token_config *conf, struct text txt)
1042         {
1043                 int lo = 0;
1044                 int hi = conf->known_count;
1045
1046                 while (lo + 1 < hi) {
1047                         int mid = (lo + hi) / 2;
1048                         int cmp = strncmp(conf->words_marks[mid],
1049                                           txt.txt, txt.len);
1050                         if (cmp == 0 && conf->words_marks[mid][txt.len])
1051                                 cmp = 1;
1052                         if (cmp <= 0)
1053                                 lo = mid;
1054                         else
1055                                 hi = mid;
1056                 }
1057                 if (strncmp(conf->words_marks[lo],
1058                            txt.txt, txt.len) == 0
1059                     && conf->words_marks[lo][txt.len] == 0)
1060                         return lo;
1061                 else
1062                         return -1;
1063         }
1064
1065 ### Bringing it all together
1066
1067 Now we have all the bits there is just one section missing:  combining
1068 all the token parsing code into one block.
1069
1070 The handling of delayed tokens (newlines, indents, undents) must come
1071 first before we try getting another character.
1072
1073 Then we parse all the test, making sure that we check for known marks
1074 before strings and comments, but unknown marks after strings and comments.
1075
1076 This block of code will either return a token, or will choose to
1077 ignore one, in which case it will `continue` around to the top of the
1078 loop.
1079
1080 ###### one token
1081         ## delayed tokens
1082
1083         ch = get_char(state);
1084
1085         ## white space
1086         ## parse number
1087         ## parse word
1088         ## parse mark
1089         ## parse string
1090         ## parse comment
1091         ## unknown mark
1092
1093 ### Start and stop
1094
1095 As well as getting tokens, we need to be able to create the
1096 `token_state` to start with, and discard it later.
1097
1098 ###### includes
1099         #include <malloc.h>
1100
1101 ###### main functions
1102         struct token_state *token_open(struct code_node *code, struct
1103                                        token_config *conf)
1104         {
1105                 struct token_state *state = malloc(sizeof(*state));
1106                 memset(state, 0, sizeof(*state));
1107                 state->node = code;
1108                 state->line = code->line_no;
1109                 state->conf = conf;
1110                 return state;
1111         }
1112         void token_close(struct token_state *state)
1113         {
1114                 free(state);
1115         }
1116
1117 ###### exported functions
1118         struct token_state *token_open(struct code_node *code, struct
1119                                        token_config *conf);
1120         void token_close(struct token_state *state);
1121
1122 ### Trace tokens
1123
1124 Getting tokens is the main thing but it is also useful to be able to
1125 print out token information, particularly for tracing and testing.
1126
1127 Known tokens are printed verbatim.  Other tokens are printed as
1128 `type(content)` where content is truncated to a given number of characters.
1129
1130 The function for printing a truncated string (`text_dump`) is also exported
1131 so that it can be used to tracing processed strings too.
1132
1133 ###### includes
1134         #include <stdio.h>
1135
1136 ###### exported functions
1137         void token_trace(FILE *f, struct token tok, int max);
1138         void text_dump(FILE *f, struct text t, int max);
1139
1140 ###### main functions
1141
1142         void text_dump(FILE *f, struct text txt, int max)
1143         {
1144                 int i;
1145                 if (txt.len > max)
1146                         max -= 2;
1147                 else
1148                         max = txt.len;
1149                 for (i = 0; i < max; i++) {
1150                         char c = txt.txt[i];
1151                         if (c < ' ' || c > '~')
1152                                 fprintf(f, "\\x%02x", c & 0xff);
1153                         else if (c == '\\')
1154                                 fprintf(f, "\\\\");
1155                         else
1156                                 fprintf(f, "%c", c);
1157                 }
1158                 if (i < txt.len)
1159                         fprintf(f, "..");
1160         }
1161
1162         void token_trace(FILE *f, struct token tok, int max)
1163         {
1164                 static char *types[] = {
1165                         [TK_ident] = "ident",
1166                         [TK_mark] = "mark",
1167                         [TK_number] = "number",
1168                         [TK_string] = "string",
1169                         [TK_multi_string] = "mstring",
1170                         [TK_line_comment] = "lcomment",
1171                         [TK_block_comment] = "bcomment",
1172                         [TK_indent] = "indent",
1173                         [TK_undent] = "undent",
1174                         [TK_newline] = "newline",
1175                         [TK_eof] = "eof",
1176                         [TK_error] = "ERROR",
1177                         };
1178
1179                 switch (tok.num) {
1180                 default: /* known word or mark */
1181                         fprintf(f, "%.*s", tok.txt.len, tok.txt.txt);
1182                         break;
1183                 case TK_indent:
1184                 case TK_undent:
1185                 case TK_newline:
1186                 case TK_eof:
1187                         /* No token text included */
1188                         fprintf(f, "%s()", types[tok.num]);
1189                         break;
1190                 case TK_ident:
1191                 case TK_mark:
1192                 case TK_number:
1193                 case TK_string:
1194                 case TK_multi_string:
1195                 case TK_line_comment:
1196                 case TK_block_comment:
1197                 case TK_error:
1198                         fprintf(f, "%s(", types[tok.num]);
1199                         text_dump(f, tok.txt, max);
1200                         fprintf(f, ")");
1201                         break;
1202                 }
1203         }
1204
1205 ### And there we have it
1206
1207 We now have all the library functions defined for reading and printing
1208 tokens.  Now we just need C files to store them, and a mk file to make them.
1209
1210 ###### File: scanner.h
1211         ## public types
1212         ## exported functions
1213
1214 ###### File: libscanner.c
1215         ## includes
1216         #include "scanner.h"
1217         ## private types
1218         ## internal functions
1219         ## main functions
1220
1221 ###### File: scanner.mk
1222
1223         CFLAGS += -Wall -g
1224         all ::
1225         scanner.mk scanner.h libscanner.c : scanner.mdc
1226                 ./md2c scanner.mdc
1227         all :: libscanner.o
1228         libscanner.o : libscanner.c
1229                 $(CC) $(CFLAGS) -c libscanner.c
1230
1231 ## Processing numbers
1232
1233 Converting a `TK_number` token to a numerical value is a slightly
1234 higher level task than lexical analysis, and slightly lower than
1235 grammar parsing, so put it here - as an index if you like.
1236
1237 Importantly it will be used by the same testing rig that is used for
1238 testing the token scanner.
1239
1240 The numeric value that we will convert all numbers into is the `mpq_t`
1241 from the GNU high precision number library "libgmp".
1242
1243 ###### number includes
1244         #include <gmp.h>
1245         #include "mdcode.h"
1246
1247 Firstly we need to be able to parse a string of digits in a given base
1248 and possibly with a decimal marker.  We store this in an `mpz_t`
1249 integer and report the number of digits after the decimal mark.
1250
1251 On error we return zero and ensure that the 'mpz_t' has been freed, or
1252 had never been initialised.
1253
1254 ###### number functions
1255
1256         static int parse_digits(mpz_t num, struct text tok, int base,
1257                                 int *placesp)
1258         {
1259                 /* Accept digits up to 'base', ignore '_' and
1260                  * ' ' if they appear between two legal digits,
1261                  * and if `placesp` is not NULL, allow a single
1262                  * '.' or ',' and report the number of digits
1263                  * beyond there.
1264                  * Return number of characters processed (p),
1265                  * or 0 if something illegal was found.
1266                  */
1267                 int p;
1268                 int decimal = -1; // digits after marker
1269                 enum {Digit, Space, Other} prev = Other;
1270                 int digits = 0;
1271
1272                 for (p = 0; p < tok.len; p++) {
1273                         int dig;
1274                         char c = tok.txt[p];
1275
1276                         if (c == '_' || c == ' ') {
1277                                 if (prev != Digit)
1278                                         goto bad;
1279                                 prev = Space;
1280                                 continue;
1281                         }
1282                         if (c == '.' || c == ',') {
1283                                 if (prev != Digit)
1284                                         goto bad;
1285                                 if (!placesp || decimal >= 0)
1286                                         return p-1;
1287                                 decimal = 0;
1288                                 prev = Other;
1289                                 continue;
1290                         }
1291                         if (isdigit(c))
1292                                 dig = c - '0';
1293                         else if (isupper(c))
1294                                 dig = 10 + c - 'A';
1295                         else if (islower(c))
1296                                 dig = 10 + c - 'a';
1297                         else
1298                                 dig = base;
1299                         if (dig >= base) {
1300                                 if (prev == Space)
1301                                         p--;
1302                                 break;
1303                         }
1304                         prev = Digit;
1305                         if (digits)
1306                                 mpz_mul_ui(num, num, base);
1307                         else
1308                                 mpz_init(num);
1309                         digits += 1;
1310                         mpz_add_ui(num, num, dig);
1311                         if (decimal >= 0)
1312                                 decimal++;
1313                 }
1314                 if (digits == 0)
1315                         return 0;
1316                 if (placesp) {
1317                         if (decimal >= 0)
1318                                 *placesp = decimal;
1319                         else
1320                                 *placesp = 0;
1321                 }
1322                 return p;
1323         bad:
1324                 if (digits)
1325                         mpz_clear(num);
1326                 return 0;
1327         }
1328
1329 ###### number includes
1330         #include <ctype.h>
1331
1332 To parse a full number we need to consider the optional base, the
1333 mantissa, and the optional exponent.  We will treat these one at a
1334 time.
1335
1336 The base is indicated by a letter after a leading zero, which must be
1337 followed by a base letter or a period.  The base also determines the
1338 character which will mark an exponent.
1339
1340 ###### number vars
1341         int base = 10;
1342         char expc = 'e';
1343
1344 ###### parse base
1345
1346         if (tok.txt[0] == '0' && tok.len > 1) {
1347                 int skip = 0;
1348                 switch(tok.txt[1]) {
1349                 case 'x':
1350                 case 'X':
1351                         base = 16;
1352                         skip = 2;
1353                         expc = 'p';
1354                         break;
1355                 case 'o':
1356                 case 'O':
1357                         base = 8;
1358                         skip = 2;
1359                         expc = 'p';
1360                         break;
1361                 case 'b':
1362                 case 'B':
1363                         base = 2;
1364                         skip = 2;
1365                         expc = 'p';
1366                         break;
1367                 case '0':
1368                 case '1':
1369                 case '2':
1370                 case '3':
1371                 case '4':
1372                 case '5':
1373                 case '6':
1374                 case '7':
1375                 case '8':
1376                 case '9':
1377                 case '_':
1378                 case ' ':
1379                         // another digit is not permitted
1380                         // after a zero.
1381                         return 0;
1382                 default:
1383                         // must be decimal marker or trailing
1384                         // letter, which are OK;
1385                         break;
1386                 }
1387                 tok.txt += skip;
1388                 tok.len -= skip;
1389         }
1390
1391 After the base is the mantissa, which may contain a decimal mark, so
1392 we need to record the number of places.  We won't impose the number of
1393 places until we have the exponent as well.
1394
1395 ###### number vars
1396         int places =0;
1397         mpz_t mant;
1398         int d;
1399
1400 ###### parse mantissa
1401
1402         d = parse_digits(mant, tok, base, &places);
1403         if (d == 0)
1404                 return 0;
1405         tok.txt += d;
1406         tok.len -= d;
1407         mpq_init(num);
1408         mpq_set_z(num, mant);
1409         mpz_clear(mant);
1410
1411 After the mantissa number may come an exponent which may be positive
1412 or negative.  We assume at this point that we have seen the exponent
1413 character `expc`.
1414
1415 ###### number vars
1416         long lexp = 0;
1417         mpz_t exp;
1418         int esign = 1;
1419
1420 ###### parse exponent
1421         if (tok.len > 1) {
1422                 if (tok.txt[0] == '+') {
1423                         tok.txt++;
1424                         tok.len--;
1425                 } else if (tok.txt[0] == '-') {
1426                         esign = -1;
1427                         tok.txt++;
1428                         tok.len--;
1429                 }
1430         }
1431         d = parse_digits(exp, tok, 10, NULL);
1432         if (d == 0) {
1433                 mpq_clear(num);
1434                 return 0;
1435         }
1436         if (!mpz_fits_slong_p(exp)) {
1437                 mpq_clear(num);
1438                 mpz_clear(exp);
1439                 return 0;
1440         }
1441         lexp = mpz_get_si(exp) * esign;
1442         mpz_clear(exp);
1443         tok.txt += d;
1444         tok.len -= d;
1445
1446
1447 Now that we have the mantissa and the exponent we can multiply them
1448 together, also allowing for the number of digits after the decimal
1449 mark.
1450
1451 For base 10, we simply subtract the decimal places from the exponent.
1452 For the other bases, as the exponent is alway based on 2, even for
1453 octal and hex, we need a bit more detail.
1454 We then recover the sign from the exponent, as division is quite
1455 different from multiplication.
1456
1457 ###### calc exponent
1458         switch (base) {
1459         case 10:
1460         case 2:
1461                 lexp -= places;
1462                 break;
1463         case 16:
1464                 lexp -= 4*places;
1465                 break;
1466         case 8:
1467                 lexp -= 3*places;
1468                 break;
1469         }
1470         if (lexp < 0) {
1471                 lexp = -lexp;
1472                 esign = -1;
1473         } else
1474                 esign = 1;
1475
1476 Imposing the exponent on the number is also very different for base 10
1477 than for the others.  For the binary shift `gmp` provides a simple
1478 function.  For base 10 we use something like Russian Peasant
1479 Multiplication.
1480
1481 ###### calc exponent
1482         if (expc == 'e') {
1483                 mpq_t tens;
1484                 mpq_init(tens);
1485                 mpq_set_ui(tens, 10, 1);
1486                 while (1) {
1487                         if (lexp & 1) {
1488                                 if (esign > 1)
1489                                         mpq_mul(num, num, tens);
1490                                 else
1491                                         mpq_div(num, num, tens);
1492                         }
1493                         lexp >>= 1;
1494                         if (lexp == 0)
1495                                 break;
1496                         mpq_mul(tens, tens, tens);
1497                 }
1498                 mpq_clear(tens);
1499         } else {
1500                 if (esign > 0)
1501                         mpq_mul_2exp(num, num, lexp);
1502                 else
1503                         mpq_div_2exp(num, num, lexp);
1504         }
1505
1506 Now we are ready to parse a number: the base, mantissa, and exponent.
1507 If all goes well we check for the possible trailing letters and
1508 return.  Return value is 1 for success and 0 for failure.
1509
1510
1511 ###### number functions
1512         int number_parse(mpq_t num, char tail[3], struct text tok)
1513         {
1514                 ## number vars
1515                 int i;
1516
1517                 ## parse base
1518                 ## parse mantissa
1519                 if (tok.len > 1 && (tok.txt[0] == expc ||
1520                                     tok.txt[0] == toupper(expc))) {
1521                         tok.txt++;
1522                         tok.len--;
1523                         ## parse exponent
1524                 }
1525                 ## calc exponent
1526
1527                 for (i = 0; i < 2; i++) {
1528                         if (tok.len <= i)
1529                                 break;
1530                         if (!isalpha(tok.txt[i]))
1531                                 goto err;
1532                         tail[i] = tok.txt[i];
1533                 }
1534                 tail[i] = 0;
1535                 if (i == tok.len)
1536                         return 1;
1537         err:
1538                 mpq_clear(num);
1539                 return 0;
1540         }
1541
1542 Number parsing goes in `libnumber.c`
1543
1544 ###### File: libnumber.c
1545
1546         #include <unistd.h>
1547         #include <stdlib.h>
1548
1549         ## number includes
1550         ## number functions
1551
1552 ###### File: number.h
1553         int number_parse(mpq_t num, char tail[3], struct text tok);
1554
1555 ###### File: scanner.mk
1556         all :: libnumber.o
1557         libnumber.o : libnumber.c
1558                 $(CC) $(CFLAGS) -c libnumber.c
1559
1560 ## Processing strings
1561
1562 Both `TK_string` and `TK_multi_string` require post-processing which
1563 can be one of two types: literal or with escapes processed.
1564 Even literal processing is non-trivial as the file may contain indents
1565 which need to be stripped.
1566
1567 Errors can only occur when processing escapes.  Any unrecognised
1568 character following the escape character will cause an error.
1569
1570 Processing escapes and striping indents can only make the string
1571 shorter, not longer, so we allocate a buffer which is the same size as
1572 the string and process into that.
1573
1574 To request escape processing, we pass the character we want to use for
1575 quoting, usually '`\`'.  To avoid escape processing we pass a zero.
1576
1577 ###### string main
1578         int string_parse(struct token *tok, char escape,
1579                          struct text *str, char tail[3])
1580         {
1581                 ## string vars
1582                 struct text t = tok->txt;
1583
1584                 str->txt = NULL;
1585                 ## strip tail
1586                 if (tok->num == TK_string) {
1587                         ## strip single
1588                 } else {
1589                         ## strip multi
1590                 }
1591                 str->txt = malloc(t.len);
1592                 str->len = 0;
1593
1594                 ## process string
1595                 return 1;
1596         err:
1597                 free(str->txt);
1598                 str->txt = NULL;
1599                 return 0;
1600         }
1601
1602 ### strip tail
1603
1604 The tail of the string can be 0, 1, or 2 letters
1605
1606         i = t.len;
1607         if (i >= 0 && isalpha(t.txt[i-1]))
1608                 i -= 1;
1609         if (i >= 0 && isalpha(t.txt[i-1]))
1610                 i -= 1;
1611         strncpy(tail, t.txt+i, t.len-i);
1612         tail[t.len-i] = 0;
1613         t.len = i;
1614
1615 ###### string vars
1616         int i;
1617
1618 ### strip single
1619
1620 Stripping the quote of a single-line string is trivial.
1621 The only part that is at all interesting is that quote character must
1622 be remembered.
1623
1624         quote = t.txt[0];
1625         if (t.txt[t.len-1] != quote)
1626                 goto err;
1627         t.txt += 1;
1628         t.len -= 2;
1629
1630 ###### string vars
1631         char quote;
1632
1633 ### strip multi
1634
1635 For a multi-line string we have a little more work to do.  We need to
1636 remove 3 quotes, not 1, and need to count the indent of the close
1637 quote as it will need to be stripped from all lines.
1638
1639         quote = t.txt[0];
1640         if (t.len < 7 ||
1641             t.txt[1] != quote || t.txt[2] != quote ||
1642             !is_newline(t.txt[3]))
1643                 goto err;
1644         t.txt += 4;
1645         t.len -= 4;
1646         i = t.len;
1647         if (i <= 0 || t.txt[i-1] != quote)
1648                 goto err;
1649         i -= 1;
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         t.len = i;
1657         while (i > 0 && !is_newline(t.txt[i-1]))
1658                 i--;
1659         indent = 0;
1660         while (i < t.len) {
1661                 if (t.txt[i] == ' ')
1662                         indent += 1;
1663                 if (t.txt[i] == '\t')
1664                         indent = indent_tab(indent);
1665                 i++;
1666         }
1667
1668 ###### string vars
1669         int indent = 0;
1670
1671 ### process string
1672
1673 Now we just take one byte at a time. trans-ASCII unicode won't look
1674 like anything we are interested in so it will just be copied byte by
1675 byte.
1676
1677         cp = str->txt;
1678         at_sol = 1;
1679         for (i = 0; i < t.len; i++) {
1680                 char c;
1681                 if (at_sol) {
1682                         at_sol = 0;
1683                         ## strip indent
1684                         if (i >= t.len)
1685                                 break;
1686                 }
1687                 c = t.txt[i];
1688                 if (c != escape) {
1689                         *cp = c;
1690                         cp += 1;
1691                         if (is_newline(c))
1692                                 at_sol = 1;
1693                 } else if (i+1 >= t.len) {
1694                         // escape and end of string
1695                         goto err;
1696                 } else {
1697                         i += 1;
1698                         c = t.txt[i];
1699                         ## parse escape
1700                 }
1701         }
1702         str->len = cp - str->txt;
1703
1704 ###### string vars
1705         char *cp;
1706         int at_sol;
1707
1708 ### strip indent
1709
1710 Every time we find a start of line, we strip spaces and tabs until the
1711 required indent is found.
1712
1713         int skipped = 0;
1714         while (i < t.len && skipped < indent) {
1715                 c = t.txt[i];
1716                 if (c == ' ')
1717                         skipped += 1;
1718                 else if (c == '\t')
1719                         skipped = indent_tab(c);
1720                 else
1721                         break;
1722                 i+= 1;
1723         }
1724
1725 ### parse escape
1726         switch (c) {
1727         case 'n':
1728                 *cp++ = '\n'; break;
1729         case 'r':
1730                 *cp++ = '\r'; break;
1731         case 't':
1732                 *cp++ = '\t'; break;
1733         case 'b':
1734                 *cp++ = '\b'; break;
1735         case 'q':
1736                 *cp++ = quote; break;
1737         case 'f':
1738                 *cp++ = '\f'; break;
1739         case 'v':
1740                 *cp++ = '\v'; break;
1741         case 'a':
1742                 *cp++ = '\a'; break;
1743         case '0':
1744         case '1':
1745         case '2':
1746         case '3':
1747                 // 3 digit octal number
1748                 if (i+2 >= t.len)
1749                         goto err;
1750                 if (t.txt[i+1] < '0' || t.txt[i+1] > '7' ||
1751                     t.txt[i+2] < '0' || t.txt[i+1] > '7')
1752                         goto err;
1753                 n = (t.txt[i  ]-'0') * 64 +
1754                     (t.txt[i+1]-'0') *  8 +
1755                     (t.txt[i+2]-'0') *  1;
1756                 *cp++ = n;
1757                 i += 2;
1758                 break;
1759         case 'x':
1760                 // 2 hex digits
1761                 n = take_hex(2, t.txt+i+1, t.len-i-1);
1762                 if (n < 0)
1763                         goto err;
1764                 *cp++ = n;
1765                 i += 2;
1766                 break;
1767         case 'u':
1768         case 'U':
1769                 // 4 or 8 hex digits for unicode
1770                 n = take_hex(c == 'u'?4:8, t.txt+i+1, t.len-i-1);
1771                 if (n < 0)
1772                         goto err;
1773                 memset(&pstate, 0, sizeof(pstate));
1774                 n = wcrtomb(cp, n, &pstate);
1775                 if (n <= 0)
1776                         goto err;
1777                 cp += n;
1778                 i += c == 'u' ? 4 : 8;
1779                 break;
1780         default:
1781                 if (c == escape)
1782                         *cp++ = c;
1783                 else if (is_newline(c))
1784                         at_sol = 1;
1785                 else
1786                         goto err;
1787         }
1788
1789 ###### string vars
1790         long n;
1791         mbstate_t pstate;
1792
1793 For `\x` `\u` and `\U` we need to collect a specific number of
1794 hexadecimal digits
1795
1796 ###### string functions
1797
1798         static long take_hex(int digits, char *cp, int l)
1799         {
1800                 long n = 0;
1801                 if (l < digits)
1802                         return -1;
1803                 while (digits) {
1804                         char  c = *cp;
1805                         int d;
1806                         if (!isxdigit(c))
1807                                 return -1;
1808                         if (isdigit(c))
1809                                 d = c - '0';
1810                         else if (isupper(c))
1811                                 d = 10 + c - 'A';
1812                         else
1813                                 d = 10 + c - 'a';
1814                         n = n * 16 + d;
1815                         digits--;
1816                         cp++;
1817                 }
1818                 return n;
1819         }
1820
1821 #### File: libstring.c
1822
1823 String parsing goes in `libstring.c`
1824
1825         #include <unistd.h>
1826         #include <stdlib.h>
1827         #include <stdio.h>
1828         #include <string.h>
1829         #include <ctype.h>
1830         #include <wchar.h>
1831         #include "mdcode.h"
1832         #include "scanner.h"
1833         ## string functions
1834         ## string main
1835
1836 ###### File: string.h
1837         int string_parse(struct token *tok, char escape,
1838                          struct text *str, char tail[3]);
1839
1840 ###### File: scanner.mk
1841         all :: libstring.o
1842         libstring.o : libstring.c
1843                 $(CC) $(CFLAGS) -c libstring.c
1844
1845
1846 ## Testing
1847
1848 As "untested code is buggy code" we need a program to easily test
1849 the scanner library.  This will simply parse a given file and report
1850 the tokens one per line.
1851
1852 ###### File: scanner.c
1853
1854         #include <unistd.h>
1855         #include <stdlib.h>
1856         #include <fcntl.h>
1857         #include <errno.h>
1858         #include <sys/mman.h>
1859         #include <string.h>
1860         #include <stdio.h>
1861         #include <gmp.h>
1862         #include <locale.h>
1863         #include "mdcode.h"
1864         #include "scanner.h"
1865         #include "number.h"
1866         #include "string.h"
1867
1868         static int errs;
1869         static void pr_err(char *msg)
1870         {
1871                 errs++;
1872                 fprintf(stderr, "%s\n", msg);
1873         }
1874
1875         int main(int argc, char *argv[])
1876         {
1877                 int fd;
1878                 int len;
1879                 char *file;
1880                 struct token_state *state;
1881                 char *known[] = {
1882                         "==",
1883                         "else",
1884                         "if",
1885                         "then",
1886                         "while",
1887                         "{",
1888                         "}",
1889                 };
1890                 struct token_config conf = {
1891                         .word_start = "_$",
1892                         .word_cont = "",
1893                         .words_marks = known,
1894                         .number_chars = "., _+-",
1895                         .known_count = sizeof(known)/sizeof(known[0]),
1896                         .ignored = (0 << TK_line_comment)
1897                                   |(0 << TK_block_comment),
1898                 };
1899                 struct section *table, *s, *prev;
1900                 setlocale(LC_ALL,"");
1901                 if (argc != 2) {
1902                         fprintf(stderr, "Usage: scanner file\n");
1903                         exit(2);
1904                 }
1905                 fd = open(argv[1], O_RDONLY);
1906                 if (fd < 0) {
1907                         fprintf(stderr, "scanner: cannot open %s: %s\n",
1908                                 argv[1], strerror(errno));
1909                         exit(1);
1910                 }
1911                 len = lseek(fd, 0, 2);
1912                 file = mmap(NULL, len, PROT_READ, MAP_SHARED, fd, 0);
1913                 table = code_extract(file, file+len, pr_err);
1914
1915                 for (s = table; s;
1916                         (code_free(s->code), prev = s, s = s->next, free(prev))) {
1917                         printf("Tokenizing: %.*s\n", s->section.len,
1918                                 s->section.txt);
1919                         state = token_open(s->code, &conf);
1920                         while(1) {
1921                                 struct token tk = token_next(state);
1922                                 printf("%d:%d ", tk.line, tk.col);
1923                                 token_trace(stdout, tk, 20);
1924                                 if (tk.num == TK_number) {
1925                                         mpq_t num;
1926                                         char tail[3];
1927                                         if (number_parse(num, tail,tk.txt)) {
1928                                                 printf(" %s ", tail);
1929                                                 mpq_out_str(stdout, 10, num);
1930                                                 mpq_clear(num);
1931                                         } else
1932                                                 printf(" BAD NUMBER");
1933                                 }
1934                                 if (tk.num == TK_string ||
1935                                     tk.num == TK_multi_string) {
1936                                         char esc = '\\';
1937                                         struct text str;
1938                                         char tail[3];
1939                                         if (tk.txt.txt[0] == '`')
1940                                                 esc = 0;
1941                                         if (string_parse(&tk, esc,
1942                                                          &str, tail)) {
1943                                                 printf(" %s ", tail);
1944                                                 text_dump(stdout, str, 20);
1945                                                 free(str.txt);
1946                                         } else
1947                                                 printf(" BAD STRING");
1948                                 }
1949                                 printf("\n");
1950                                 if (tk.num == TK_error)
1951                                         errs = 1;
1952                                 if (tk.num == TK_eof)
1953                                         break;
1954                         }
1955                 }
1956                 exit(!!errs);
1957         }
1958 ###### File: scanner.mk
1959         scanner.c : scanner.mdc
1960                 ./md2c scanner.mdc
1961         all :: scanner
1962         scanner : scanner.o scanner.h libscanner.o libmdcode.o mdcode.h
1963                 $(CC) $(CFLAGS) -o scanner scanner.o libscanner.o \
1964                         libmdcode.o libnumber.o libstring.o -licuuc -lgmp
1965         scanner.o : scanner.c
1966                 $(CC) $(CFLAGS) -c scanner.c
1967