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