0c4a6bd8007201e1ce685d21f229237f9e46b9f9
[pspp] / src / language / lexer / segment.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2010, 2011, 2013 Free Software Foundation, Inc.
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
16
17 #include <config.h>
18
19 #include "language/lexer/segment.h"
20
21 #include <limits.h>
22 #include <unistr.h>
23
24 #include "data/identifier.h"
25 #include "language/lexer/command-name.h"
26 #include "libpspp/assertion.h"
27 #include "libpspp/cast.h"
28
29 #include "gl/c-ctype.h"
30 #include "gl/c-strcase.h"
31 #include "gl/memchr2.h"
32
33 enum segmenter_state
34   {
35     S_SHBANG,
36     S_GENERAL,
37     S_COMMENT_1,
38     S_COMMENT_2,
39     S_DOCUMENT_1,
40     S_DOCUMENT_2,
41     S_DOCUMENT_3,
42     S_FILE_LABEL,
43     S_DO_REPEAT_1,
44     S_DO_REPEAT_2,
45     S_DO_REPEAT_3,
46     S_BEGIN_DATA_1,
47     S_BEGIN_DATA_2,
48     S_BEGIN_DATA_3,
49     S_BEGIN_DATA_4,
50     S_TITLE_1,
51     S_TITLE_2
52   };
53
54 #define SS_START_OF_LINE (1u << 0)
55 #define SS_START_OF_COMMAND (1u << 1)
56
57 static int segmenter_detect_command_name__ (const char *input,
58                                             size_t n, int ofs);
59
60 static int
61 segmenter_u8_to_uc__ (ucs4_t *puc, const char *input_, size_t n)
62 {
63   const uint8_t *input = CHAR_CAST (const uint8_t *, input_);
64   int mblen;
65
66   assert (n > 0);
67
68   mblen = u8_mbtoucr (puc, input, n);
69   return (mblen >= 0 ? mblen
70           : mblen == -2 ? -1
71           : u8_mbtouc (puc, input, n));
72 }
73
74 static int
75 segmenter_parse_shbang__ (struct segmenter *s, const char *input, size_t n,
76                           enum segment_type *type)
77 {
78   if (input[0] == '#')
79     {
80       if (n < 2)
81         return -1;
82       else if (input[1] == '!')
83         {
84           int ofs;
85
86           for (ofs = 2; ofs < n; ofs++)
87             if (input[ofs] == '\n' || input[ofs] == '\0')
88               {
89                 if (input[ofs] == '\n' && input[ofs - 1] == '\r')
90                   ofs--;
91
92                 s->state = S_GENERAL;
93                 s->substate = SS_START_OF_COMMAND;
94                 *type = SEG_SHBANG;
95                 return ofs;
96               }
97
98           return -1;
99         }
100     }
101
102   s->state = S_GENERAL;
103   s->substate = SS_START_OF_LINE | SS_START_OF_COMMAND;
104   return segmenter_push (s, input, n, type);
105 }
106
107 static int
108 segmenter_parse_digraph__ (const char *seconds, struct segmenter *s,
109                            const char *input, size_t n,
110                            enum segment_type *type)
111 {
112   assert (s->state == S_GENERAL);
113
114   if (n < 2)
115     return -1;
116
117   *type = SEG_PUNCT;
118   s->substate = 0;
119   return input[1] != '\0' && strchr (seconds, input[1]) != NULL ? 2 : 1;
120 }
121
122 static int
123 skip_comment (const char *input, size_t n, size_t ofs)
124 {
125   for (; ofs < n; ofs++)
126     {
127       if (input[ofs] == '\n' || input[ofs] == '\0')
128         return ofs;
129       else if (input[ofs] == '*')
130         {
131           if (ofs + 1 >= n)
132             return -1;
133           else if (input[ofs + 1] == '/')
134             return ofs + 2;
135         }
136     }
137   return -1;
138 }
139
140 static int
141 skip_spaces_and_comments (const char *input, size_t n, int ofs)
142 {
143   while (ofs < n)
144     {
145       ucs4_t uc;
146       int mblen;
147
148       mblen = segmenter_u8_to_uc__ (&uc, input + ofs, n - ofs);
149       if (mblen < 0)
150         return -1;
151
152       if (uc == '/')
153         {
154           if (ofs + 1 >= n)
155             return -1;
156           else if (input[ofs + 1] != '*')
157             return ofs;
158
159           ofs = skip_comment (input, n, ofs + 2);
160           if (ofs < 0)
161             return -1;
162         }
163       else if (lex_uc_is_space (uc) && uc != '\n')
164         ofs += mblen;
165       else
166         return ofs;
167     }
168
169   return -1;
170 }
171
172 static int
173 is_end_of_line (const char *input, size_t n, int ofs)
174 {
175   if (input[ofs] == '\n' || input[ofs] == '\0')
176     return 1;
177   else if (input[ofs] == '\r')
178     {
179       if (ofs + 1 >= n)
180         return -1;
181       return input[ofs + 1] == '\n';
182     }
183   else
184     return 0;
185 }
186
187 static int
188 at_end_of_line (const char *input, size_t n, int ofs)
189 {
190   ofs = skip_spaces_and_comments (input, n, ofs);
191   if (ofs < 0)
192     return -1;
193
194   return is_end_of_line (input, n, ofs);
195 }
196
197 static int
198 segmenter_parse_newline__ (const char *input, size_t n,
199                            enum segment_type *type)
200 {
201   int ofs;
202
203   if (input[0] == '\n')
204     ofs = 1;
205   else
206     {
207       if (n < 2)
208         return -1;
209
210       assert (input[0] == '\r');
211       assert (input[1] == '\n');
212       ofs = 2;
213     }
214
215   *type = SEG_NEWLINE;
216   return ofs;
217 }
218
219 static int
220 skip_spaces (const char *input, size_t n, size_t ofs)
221 {
222   while (ofs < n)
223     {
224       ucs4_t uc;
225       int mblen;
226
227       mblen = segmenter_u8_to_uc__ (&uc, input + ofs, n - ofs);
228       if (mblen < 0)
229         return -1;
230
231       if (!lex_uc_is_space (uc) || uc == '\n' || uc == '\0')
232         return ofs;
233
234       ofs += mblen;
235     }
236
237   return -1;
238 }
239
240 static int
241 skip_digits (const char *input, size_t n, int ofs)
242 {
243   for (; ofs < n; ofs++)
244     if (!c_isdigit (input[ofs]))
245       return ofs;
246   return -1;
247 }
248
249 static int
250 segmenter_parse_number__ (struct segmenter *s, const char *input, size_t n,
251                           enum segment_type *type)
252 {
253   int ofs;
254
255   assert (s->state == S_GENERAL);
256
257   ofs = skip_digits (input, n, 0);
258   if (ofs < 0)
259     return -1;
260
261   if (input[ofs] == '.')
262     {
263       ofs = skip_digits (input, n, ofs + 1);
264       if (ofs < 0)
265         return -1;
266     }
267
268   if (ofs >= n)
269     return -1;
270   if (input[ofs] == 'e' || input[ofs] == 'E')
271     {
272       ofs++;
273       if (ofs >= n)
274         return -1;
275
276       if (input[ofs] == '+' || input[ofs] == '-')
277         {
278           ofs++;
279           if (ofs >= n)
280             return -1;
281         }
282
283       if (!c_isdigit (input[ofs]))
284         {
285           *type = SEG_EXPECTED_EXPONENT;
286           s->substate = 0;
287           return ofs;
288         }
289
290       ofs = skip_digits (input, n, ofs);
291       if (ofs < 0)
292         return -1;
293     }
294
295   if (input[ofs - 1] == '.')
296     {
297       int eol = at_end_of_line (input, n, ofs);
298       if (eol < 0)
299         return -1;
300       else if (eol)
301         ofs--;
302     }
303
304   *type = SEG_NUMBER;
305   s->substate = 0;
306   return ofs;
307 }
308
309 static bool
310 is_reserved_word (const char *s, int n)
311 {
312   char s0, s1, s2, s3;
313
314   s0 = c_toupper (s[0]);
315   switch (n)
316     {
317     case 2:
318       s1 = c_toupper (s[1]);
319       return ((s0 == 'B' && s1 == 'Y')
320               || (s0 == 'E' && s1 == 'Q')
321               || (s0 == 'G' && (s1 == 'E' || s1 == 'T'))
322               || (s0 == 'L' && (s1 == 'E' || s1 == 'T'))
323               || (s0 == 'N' && s1 == 'E')
324               || (s0 == 'O' && s1 == 'R')
325               || (s0 == 'T' && s1 == 'O'));
326
327     case 3:
328       s1 = c_toupper (s[1]);
329       s2 = c_toupper (s[2]);
330       return ((s0 == 'A' && ((s1 == 'L' && s2 == 'L')
331                              || (s1 == 'N' && s2 == 'D')))
332               || (s0 == 'N' && s1 == 'O' && s2 == 'T'));
333
334     case 4:
335       s1 = c_toupper (s[1]);
336       s2 = c_toupper (s[2]);
337       s3 = c_toupper (s[3]);
338       return s0 == 'W' && s1 == 'I' && s2 == 'T' && s3 == 'H';
339
340     default:
341       return false;
342     }
343 }
344
345 static int
346 segmenter_parse_comment_1__ (struct segmenter *s,
347                              const char *input, size_t n,
348                              enum segment_type *type)
349 {
350   int endcmd;
351   int ofs;
352
353   endcmd = -2;
354   ofs = 0;
355   while (ofs < n)
356     {
357       ucs4_t uc;
358       int mblen;
359
360       mblen = segmenter_u8_to_uc__ (&uc, input + ofs, n - ofs);
361       if (mblen < 0)
362         return -1;
363
364       switch (uc)
365         {
366         case '.':
367           endcmd = ofs;
368           break;
369
370         case '\n':
371           if (ofs > 1 && input[ofs - 1] == '\r')
372             ofs--;
373           /* Fall through. */
374         case '\0':
375           if (endcmd == -2 || uc == '\0')
376             {
377               /* Blank line ends comment command. */
378               s->state = S_GENERAL;
379               s->substate = SS_START_OF_COMMAND;
380               *type = SEG_SEPARATE_COMMANDS;
381               return ofs;
382             }
383           else if (endcmd >= 0)
384             {
385               /* '.' at end of line ends comment command. */
386               s->state = S_GENERAL;
387               s->substate = 0;
388               *type = SEG_COMMENT_COMMAND;
389               return endcmd;
390             }
391           else
392             {
393               /* Comment continues onto next line. */
394               *type = SEG_COMMENT_COMMAND;
395               s->state = S_COMMENT_2;
396               return ofs;
397             }
398           NOT_REACHED ();
399
400         default:
401           if (!lex_uc_is_space (uc))
402             endcmd = -1;
403           break;
404         }
405
406       ofs += mblen;
407     }
408   return -1;
409 }
410
411 static int
412 segmenter_parse_comment_2__ (struct segmenter *s, const char *input, size_t n,
413                              enum segment_type *type)
414 {
415   int new_cmd;
416   ucs4_t uc;
417   int mblen;
418   int ofs;
419
420   ofs = segmenter_parse_newline__ (input, n, type);
421   if (ofs < 0 || ofs >= n)
422     return -1;
423
424   mblen = segmenter_u8_to_uc__ (&uc, input + ofs, n - ofs);
425   if (mblen < 0)
426     return -1;
427
428   if (uc == '+' || uc == '-' || uc == '.')
429     new_cmd = true;
430   else if (!lex_uc_is_space (uc))
431     switch (s->mode)
432       {
433       case SEG_MODE_INTERACTIVE:
434         new_cmd = false;
435         break;
436
437       case SEG_MODE_BATCH:
438         new_cmd = true;
439         break;
440
441       case SEG_MODE_AUTO:
442         new_cmd = segmenter_detect_command_name__ (input, n, ofs);
443         if (new_cmd < 0)
444           return -1;
445         break;
446
447       default:
448         NOT_REACHED ();
449       }
450   else
451     new_cmd = false;
452
453   if (new_cmd)
454     {
455       s->state = S_GENERAL;
456       s->substate = SS_START_OF_LINE | SS_START_OF_COMMAND;
457     }
458   else
459     s->state = S_COMMENT_1;
460   return ofs;
461 }
462
463 static int
464 segmenter_parse_document_1__ (struct segmenter *s, const char *input, size_t n,
465                               enum segment_type *type)
466 {
467   bool end_cmd;
468   int ofs;
469
470   end_cmd = false;
471   ofs = 0;
472   while (ofs < n)
473     {
474       ucs4_t uc;
475       int mblen;
476
477       mblen = segmenter_u8_to_uc__ (&uc, input + ofs, n - ofs);
478       if (mblen < 0)
479         return -1;
480
481       switch (uc)
482         {
483         case '.':
484           end_cmd = true;
485           break;
486
487         case '\n':
488           if (ofs > 1 && input[ofs - 1] == '\r')
489             ofs--;
490
491           *type = SEG_DOCUMENT;
492           s->state = end_cmd ? S_DOCUMENT_3 : S_DOCUMENT_2;
493           return ofs;
494
495         case '\0':
496           *type = SEG_DOCUMENT;
497           s->state = S_DOCUMENT_3;
498           return ofs;
499
500         default:
501           if (!lex_uc_is_space (uc))
502             end_cmd = false;
503           break;
504         }
505
506       ofs += mblen;
507     }
508   return -1;
509 }
510
511 static int
512 segmenter_parse_document_2__ (struct segmenter *s, const char *input, size_t n,
513                               enum segment_type *type)
514 {
515   int ofs;
516
517   ofs = segmenter_parse_newline__ (input, n, type);
518   if (ofs < 0)
519     return -1;
520
521   s->state = S_DOCUMENT_1;
522   return ofs;
523 }
524
525 static int
526 segmenter_parse_document_3__ (struct segmenter *s, enum segment_type *type)
527 {
528   *type = SEG_END_COMMAND;
529   s->state = S_GENERAL;
530   s->substate = SS_START_OF_COMMAND | SS_START_OF_LINE;
531   return 0;
532 }
533
534 static int
535 segmenter_unquoted (const char *input, size_t n, int ofs)
536
537 {
538   char c;
539
540   ofs = skip_spaces_and_comments (input, n, ofs);
541   if (ofs < 0)
542     return -1;
543
544   c = input[ofs];
545   return c != '\'' && c != '"' && c != '\n' && c != '\0';
546 }
547
548 static int
549 next_id_in_command (const struct segmenter *s, const char *input, size_t n,
550                     int ofs, char id[], size_t id_size)
551 {
552   struct segmenter sub;
553
554   assert (id_size > 0);
555
556   sub.mode = s->mode;
557   sub.state = S_GENERAL;
558   sub.substate = 0;
559   for (;;)
560     {
561       enum segment_type type;
562       int retval;
563
564       retval = segmenter_push (&sub, input + ofs, n - ofs, &type);
565       if (retval < 0)
566         {
567           id[0] = '\0';
568           return -1;
569         }
570
571       switch (type)
572         {
573         case SEG_SHBANG:
574         case SEG_SPACES:
575         case SEG_COMMENT:
576         case SEG_NEWLINE:
577           break;
578
579         case SEG_IDENTIFIER:
580           if (retval < id_size)
581             {
582               memcpy (id, input + ofs, retval);
583               id[retval] = '\0';
584               return ofs + retval;
585             }
586           /* fall through */
587
588         case SEG_NUMBER:
589         case SEG_QUOTED_STRING:
590         case SEG_HEX_STRING:
591         case SEG_UNICODE_STRING:
592         case SEG_UNQUOTED_STRING:
593         case SEG_RESERVED_WORD:
594         case SEG_PUNCT:
595         case SEG_COMMENT_COMMAND:
596         case SEG_DO_REPEAT_COMMAND:
597         case SEG_INLINE_DATA:
598         case SEG_START_DOCUMENT:
599         case SEG_DOCUMENT:
600         case SEG_START_COMMAND:
601         case SEG_SEPARATE_COMMANDS:
602         case SEG_END_COMMAND:
603         case SEG_END:
604         case SEG_EXPECTED_QUOTE:
605         case SEG_EXPECTED_EXPONENT:
606         case SEG_UNEXPECTED_DOT:
607         case SEG_UNEXPECTED_CHAR:
608           id[0] = '\0';
609           return ofs + retval;
610         }
611       ofs += retval;
612     }
613 }
614
615 static int
616 segmenter_parse_id__ (struct segmenter *s, const char *input, size_t n,
617                       enum segment_type *type)
618 {
619   ucs4_t uc;
620   int ofs;
621
622   assert (s->state == S_GENERAL);
623
624   ofs = u8_mbtouc (&uc, CHAR_CAST (const uint8_t *, input), n);
625   for (;;)
626     {
627       int mblen;
628
629       if (ofs >= n)
630         return -1;
631
632       mblen = segmenter_u8_to_uc__ (&uc, input + ofs, n - ofs);
633       if (mblen < 0)
634         return -1;
635       else if (!lex_uc_is_idn (uc))
636         break;
637
638       ofs += mblen;
639     }
640
641   if (input[ofs - 1] == '.')
642     {
643       int eol = at_end_of_line (input, n, ofs);
644       if (eol < 0)
645         return -1;
646       else if (eol)
647         ofs--;
648     }
649
650   if (is_reserved_word (input, ofs))
651     *type = SEG_RESERVED_WORD;
652   else
653     *type = SEG_IDENTIFIER;
654
655   if (s->substate & SS_START_OF_COMMAND)
656     {
657       struct substring word = ss_buffer (input, ofs);
658
659       if (lex_id_match_n (ss_cstr ("COMMENT"), word, 4))
660         {
661           s->state = S_COMMENT_1;
662           return segmenter_parse_comment_1__ (s, input, n, type);
663         }
664       else if (lex_id_match (ss_cstr ("DOCUMENT"), word))
665         {
666           s->state = S_DOCUMENT_1;
667           *type = SEG_START_DOCUMENT;
668           return 0;
669         }
670       else if (lex_id_match (ss_cstr ("TITLE"), word)
671                || lex_id_match (ss_cstr ("SUBTITLE"), word))
672         {
673           int result = segmenter_unquoted (input, n, ofs);
674           if (result < 0)
675             return -1;
676           else if (result)
677             {
678               s->state = S_TITLE_1;
679               return ofs;
680             }
681         }
682       else if (lex_id_match (ss_cstr ("FILE"), word))
683         {
684           char id[16];
685
686           if (next_id_in_command (s, input, n, ofs, id, sizeof id) < 0)
687             return -1;
688           else if (lex_id_match (ss_cstr ("LABEL"), ss_cstr (id)))
689             {
690               s->state = S_FILE_LABEL;
691               s->substate = 0;
692               return ofs;
693             }
694         }
695       else if (lex_id_match (ss_cstr ("DO"), word))
696         {
697           char id[16];
698
699           if (next_id_in_command (s, input, n, ofs, id, sizeof id) < 0)
700             return -1;
701           else if (lex_id_match (ss_cstr ("REPEAT"), ss_cstr (id)))
702             {
703               s->state = S_DO_REPEAT_1;
704               s->substate = 0;
705               return ofs;
706             }
707         }
708       else if (lex_id_match (ss_cstr ("BEGIN"), word))
709         {
710           char id[16];
711           int ofs2;
712
713           ofs2 = next_id_in_command (s, input, n, ofs, id, sizeof id);
714           if (ofs2 < 0)
715             return -1;
716           else if (lex_id_match (ss_cstr ("DATA"), ss_cstr (id)))
717             {
718               int eol;
719
720               ofs2 = skip_spaces_and_comments (input, n, ofs2);
721               if (ofs2 < 0)
722                 return -1;
723
724               if (input[ofs2] == '.')
725                 {
726                   ofs2 = skip_spaces_and_comments (input, n, ofs2 + 1);
727                   if (ofs2 < 0)
728                     return -1;
729                 }
730
731               eol = is_end_of_line (input, n, ofs2);
732               if (eol < 0)
733                 return -1;
734               else if (eol)
735                 {
736                   if (memchr (input, '\n', ofs2))
737                     s->state = S_BEGIN_DATA_1;
738                   else
739                     s->state = S_BEGIN_DATA_2;
740                   s->substate = 0;
741                   return ofs;
742                 }
743             }
744         }
745     }
746
747   s->substate = 0;
748   return ofs;
749 }
750
751 static int
752 segmenter_parse_string__ (enum segment_type string_type,
753                           int ofs, struct segmenter *s,
754                           const char *input, size_t n, enum segment_type *type)
755 {
756   int quote = input[ofs];
757
758   ofs++;
759   while (ofs < n)
760     if (input[ofs] == quote)
761       {
762         ofs++;
763         if (ofs >= n)
764           return -1;
765         else if (input[ofs] == quote)
766           ofs++;
767         else
768           {
769             *type = string_type;
770             s->substate = 0;
771             return ofs;
772           }
773       }
774     else if (input[ofs] == '\n' || input[ofs] == '\0')
775       {
776         *type = SEG_EXPECTED_QUOTE;
777         s->substate = 0;
778         return ofs;
779       }
780     else
781       ofs++;
782
783   return -1;
784 }
785
786 static int
787 segmenter_maybe_parse_string__ (enum segment_type string_type,
788                                 struct segmenter *s,
789                                 const char *input, size_t n,
790                                 enum segment_type *type)
791 {
792   if (n < 2)
793     return -1;
794   else if (input[1] == '\'' || input[1] == '"')
795     return segmenter_parse_string__ (string_type, 1, s, input, n, type);
796   else
797     return segmenter_parse_id__ (s, input, n, type);
798 }
799
800 static int
801 segmenter_parse_mid_command__ (struct segmenter *s,
802                                const char *input, size_t n,
803                                enum segment_type *type)
804 {
805   ucs4_t uc;
806   int mblen;
807   int ofs;
808
809   assert (s->state == S_GENERAL);
810   assert (!(s->substate & SS_START_OF_LINE));
811
812   mblen = segmenter_u8_to_uc__ (&uc, input, n);
813   if (mblen < 0)
814     return -1;
815
816   switch (uc)
817     {
818     case '\n':
819       s->substate |= SS_START_OF_LINE;
820       *type = SEG_NEWLINE;
821       return 1;
822
823     case '/':
824       if (n == 1)
825         return -1;
826       else if (input[1] == '*')
827         {
828           ofs = skip_comment (input, n, 2);
829           if (ofs < 0)
830             return -1;
831
832           *type = SEG_COMMENT;
833           return ofs;
834         }
835       else
836         {
837           s->substate = 0;
838           *type = SEG_PUNCT;
839           return 1;
840         }
841
842     case '(': case ')': case ',': case '=': case '-':
843     case '[': case ']': case '&': case '|': case '+':
844       *type = SEG_PUNCT;
845       s->substate = 0;
846       return 1;
847
848     case '*':
849       if (s->substate & SS_START_OF_COMMAND)
850         {
851           /* '*' at the beginning of a command begins a comment. */
852           s->state = S_COMMENT_1;
853           return segmenter_parse_comment_1__ (s, input, n, type);
854         }
855       else
856         return segmenter_parse_digraph__ ("*", s, input, n, type);
857
858     case '<':
859       return segmenter_parse_digraph__ ("=>", s, input, n, type);
860
861     case '>':
862       return segmenter_parse_digraph__ ("=", s, input, n, type);
863
864     case '~':
865       return segmenter_parse_digraph__ ("=", s, input, n, type);
866
867     case '.':
868       if (n < 2)
869         return -1;
870       else if (c_isdigit (input[1]))
871         return segmenter_parse_number__ (s, input, n, type);
872       else
873         {
874           int eol = at_end_of_line (input, n, 1);
875           if (eol < 0)
876             return -1;
877
878           if (eol)
879             {
880               *type = SEG_END_COMMAND;
881               s->substate = SS_START_OF_COMMAND;
882             }
883           else
884             *type = SEG_UNEXPECTED_DOT;
885           return 1;
886         }
887       NOT_REACHED ();
888
889     case '0': case '1': case '2': case '3': case '4':
890     case '5': case '6': case '7': case '8': case '9':
891       return segmenter_parse_number__ (s, input, n, type);
892
893     case 'u': case 'U':
894       return segmenter_maybe_parse_string__ (SEG_UNICODE_STRING,
895                                            s, input, n, type);
896
897     case 'x': case 'X':
898       return segmenter_maybe_parse_string__ (SEG_HEX_STRING,
899                                              s, input, n, type);
900
901     case '\'': case '"':
902       return segmenter_parse_string__ (SEG_QUOTED_STRING, 0,
903                                        s, input, n, type);
904
905     default:
906       if (lex_uc_is_space (uc))
907         {
908           ofs = skip_spaces (input, n, mblen);
909           if (ofs < 0)
910             return -1;
911
912           if (input[ofs - 1] == '\r' && input[ofs] == '\n')
913             {
914               if (ofs == 1)
915                 {
916                   s->substate |= SS_START_OF_LINE;
917                   *type = SEG_NEWLINE;
918                   return 2;
919                 }
920               else
921                 ofs--;
922             }
923           *type = SEG_SPACES;
924           return ofs;
925         }
926       else if (lex_uc_is_id1 (uc))
927         return segmenter_parse_id__ (s, input, n, type);
928       else
929         {
930           *type = SEG_UNEXPECTED_CHAR;
931           s->substate = 0;
932           return mblen;
933         }
934     }
935 }
936
937 static int
938 compare_commands (const void *a_, const void *b_)
939 {
940   const char *const *ap = a_;
941   const char *const *bp = b_;
942   const char *a = *ap;
943   const char *b = *bp;
944
945   return c_strcasecmp (a, b);
946 }
947
948 static const char **
949 segmenter_get_command_name_candidates (unsigned char first)
950 {
951 #define DEF_CMD(STATES, FLAGS, NAME, FUNCTION) NAME,
952 #define UNIMPL_CMD(NAME, DESCRIPTION) NAME,
953   static const char *commands[] =
954     {
955 #include "language/command.def"
956       ""
957     };
958   static size_t n_commands = (sizeof commands / sizeof *commands) - 1;
959 #undef DEF_CMD
960 #undef UNIMPL_CMD
961
962   static bool inited;
963
964   static const char **cindex[UCHAR_MAX + 1];
965
966   if (!inited)
967     {
968       size_t i;
969
970       inited = true;
971
972       qsort (commands, n_commands, sizeof *commands, compare_commands);
973       for (i = 0; i < n_commands; i++)
974         {
975           unsigned char c = c_toupper (commands[i][0]);
976           if (cindex[c] == NULL)
977             cindex[c] = &commands[i];
978         }
979       for (i = 0; i <= UCHAR_MAX; i++)
980         if (cindex[i] == NULL)
981           cindex[i] = &commands[n_commands];
982     }
983
984   return cindex[c_toupper (first)];
985 }
986
987 static int
988 segmenter_detect_command_name__ (const char *input, size_t n, int ofs)
989 {
990   const char **commands;
991
992   input += ofs;
993   n -= ofs;
994   ofs = 0;
995   for (;;)
996     {
997       ucs4_t uc;
998       int mblen;
999
1000       if (ofs >= n)
1001         return -1;
1002
1003       mblen = segmenter_u8_to_uc__ (&uc, input + ofs, n - ofs);
1004       if (mblen < 0)
1005         return -1;
1006
1007       if (uc == '\n' || uc == '\0'
1008           || !(lex_uc_is_space (uc) || lex_uc_is_idn (uc) || uc == '-'))
1009         break;
1010
1011       ofs += mblen;
1012     }
1013   if (input[ofs - 1] == '.')
1014     ofs--;
1015
1016   for (commands = segmenter_get_command_name_candidates (input[0]);
1017        c_toupper (input[0]) == c_toupper ((*commands)[0]);
1018        commands++)
1019     {
1020       int missing_words;
1021       bool exact;
1022
1023       if (command_match (ss_cstr (*commands), ss_buffer (input, ofs),
1024                          &exact, &missing_words)
1025           && missing_words <= 0)
1026         return 1;
1027     }
1028
1029   return 0;
1030 }
1031
1032 static int
1033 is_start_of_string__ (const char *input, size_t n, int ofs)
1034 {
1035   int c;
1036
1037   c = input[ofs];
1038   if (c == 'x' || c == 'X' || c == 'u' || c == 'U')
1039     {
1040       if (ofs + 1 >= n)
1041         return -1;
1042
1043       return input[ofs + 1] == '\'' || input[ofs + 1] == '"';
1044     }
1045   else
1046     return c == '\'' || c == '"' || c == '\n';
1047 }
1048
1049 static int
1050 segmenter_parse_start_of_line__ (struct segmenter *s,
1051                                  const char *input, size_t n,
1052                                  enum segment_type *type)
1053 {
1054   ucs4_t uc;
1055   int mblen;
1056   int ofs;
1057
1058   assert (s->state == S_GENERAL);
1059   assert (s->substate & SS_START_OF_LINE);
1060
1061   mblen = segmenter_u8_to_uc__ (&uc, input, n);
1062   if (mblen < 0)
1063     return -1;
1064
1065   switch (uc)
1066     {
1067     case '+':
1068       ofs = skip_spaces_and_comments (input, n, 1);
1069       if (ofs < 0)
1070         return -1;
1071       else
1072         {
1073           int is_string = is_start_of_string__ (input, n, ofs);
1074           if (is_string < 0)
1075             return -1;
1076           else if (is_string)
1077             {
1078               /* This is punctuation that may separate pieces of a string. */
1079               *type = SEG_PUNCT;
1080               s->substate = 0;
1081               return 1;
1082             }
1083         }
1084       /* Fall through. */
1085
1086     case '-':
1087     case '.':
1088       *type = SEG_START_COMMAND;
1089       s->substate = SS_START_OF_COMMAND;
1090       return 1;
1091
1092     default:
1093       if (lex_uc_is_space (uc))
1094         {
1095           int eol = at_end_of_line (input, n, 0);
1096           if (eol < 0)
1097             return -1;
1098           else if (eol)
1099             {
1100               s->substate = SS_START_OF_COMMAND;
1101               *type = SEG_SEPARATE_COMMANDS;
1102               return 0;
1103             }
1104           break;
1105         }
1106
1107       if (s->mode == SEG_MODE_INTERACTIVE || s->substate & SS_START_OF_COMMAND)
1108         break;
1109       else if (s->mode == SEG_MODE_AUTO)
1110         {
1111           int cmd = segmenter_detect_command_name__ (input, n, 0);
1112           if (cmd < 0)
1113             return -1;
1114           else if (cmd == 0)
1115             break;
1116         }
1117       else
1118         assert (s->mode == SEG_MODE_BATCH);
1119
1120       s->substate = SS_START_OF_COMMAND;
1121       *type = SEG_START_COMMAND;
1122       return 0;
1123     }
1124
1125   s->substate = SS_START_OF_COMMAND;
1126   return segmenter_parse_mid_command__ (s, input, n, type);
1127 }
1128
1129 static int
1130 segmenter_parse_file_label__ (struct segmenter *s,
1131                               const char *input, size_t n,
1132                               enum segment_type *type)
1133 {
1134   struct segmenter sub;
1135   int ofs;
1136
1137   sub = *s;
1138   sub.state = S_GENERAL;
1139   ofs = segmenter_push (&sub, input, n, type);
1140
1141   if (ofs < 0)
1142     return -1;
1143   else if (*type == SEG_IDENTIFIER)
1144     {
1145       int result;
1146
1147       assert (lex_id_match (ss_cstr ("LABEL"),
1148                             ss_buffer ((char *) input, ofs)));
1149       result = segmenter_unquoted (input, n, ofs);
1150       if (result < 0)
1151         return -1;
1152       else
1153         {
1154           if (result)
1155             s->state = S_TITLE_1;
1156           else
1157             *s = sub;
1158           return ofs;
1159         }
1160     }
1161   else
1162     {
1163       s->substate = sub.substate;
1164       return ofs;
1165     }
1166 }
1167
1168 static int
1169 segmenter_subparse (struct segmenter *s,
1170                     const char *input, size_t n, enum segment_type *type)
1171 {
1172   struct segmenter sub;
1173   int ofs;
1174
1175   sub.mode = s->mode;
1176   sub.state = S_GENERAL;
1177   sub.substate = s->substate;
1178   ofs = segmenter_push (&sub, input, n, type);
1179   s->substate = sub.substate;
1180   return ofs;
1181 }
1182
1183 static int
1184 segmenter_parse_do_repeat_1__ (struct segmenter *s,
1185                                const char *input, size_t n,
1186                                enum segment_type *type)
1187 {
1188   int ofs = segmenter_subparse (s, input, n, type);
1189   if (ofs < 0)
1190     return -1;
1191
1192   if (*type == SEG_START_COMMAND || *type == SEG_SEPARATE_COMMANDS)
1193     s->state = S_DO_REPEAT_2;
1194   else if (*type == SEG_END_COMMAND)
1195     {
1196       s->state = S_DO_REPEAT_3;
1197       s->substate = 1;
1198     }
1199
1200   return ofs;
1201 }
1202
1203 static int
1204 segmenter_parse_do_repeat_2__ (struct segmenter *s,
1205                                const char *input, size_t n,
1206                                enum segment_type *type)
1207 {
1208   int ofs = segmenter_subparse (s, input, n, type);
1209   if (ofs < 0)
1210     return -1;
1211
1212   if (*type == SEG_NEWLINE)
1213     {
1214       s->state = S_DO_REPEAT_3;
1215       s->substate = 1;
1216     }
1217
1218   return ofs;
1219 }
1220
1221 static bool
1222 check_repeat_command (struct segmenter *s,
1223                       const char *input, size_t n)
1224 {
1225   int direction;
1226   char id[16];
1227   int ofs;
1228
1229   ofs = 0;
1230   if (input[ofs] == '+' || input[ofs] == '-')
1231     ofs++;
1232
1233   ofs = next_id_in_command (s, input, n, ofs, id, sizeof id);
1234   if (ofs < 0)
1235     return false;
1236   else if (lex_id_match (ss_cstr ("DO"), ss_cstr (id)))
1237     direction = 1;
1238   else if (lex_id_match (ss_cstr ("END"), ss_cstr (id)))
1239     direction = -1;
1240   else
1241     return true;
1242
1243   ofs = next_id_in_command (s, input, n, ofs, id, sizeof id);
1244   if (ofs < 0)
1245     return false;
1246
1247   if (lex_id_match (ss_cstr ("REPEAT"), ss_cstr (id)))
1248     s->substate += direction;
1249   return true;
1250 }
1251
1252 static int
1253 segmenter_parse_full_line__ (const char *input, size_t n,
1254                              enum segment_type *type)
1255 {
1256   const char *newline = memchr2 (input, '\n', '\0', n);
1257
1258   if (newline == NULL)
1259     return -1;
1260   else
1261     {
1262       int ofs = newline - input;
1263       if (*newline == '\0')
1264         {
1265           assert (ofs > 0);
1266           return ofs;
1267         }
1268       else if (ofs == 0 || (ofs == 1 && input[0] == '\r'))
1269         {
1270           *type = SEG_NEWLINE;
1271           return ofs + 1;
1272         }
1273       else
1274         return ofs - (input[ofs - 1] == '\r');
1275     }
1276 }
1277
1278 static int
1279 segmenter_parse_do_repeat_3__ (struct segmenter *s,
1280                                const char *input, size_t n,
1281                                enum segment_type *type)
1282 {
1283   int ofs;
1284
1285   ofs = segmenter_parse_full_line__ (input, n, type);
1286   if (ofs < 0 || input[ofs - 1] == '\n')
1287     return ofs;
1288   else if (!check_repeat_command (s, input, n))
1289     return -1;
1290   else if (s->substate == 0)
1291     {
1292       s->state = S_GENERAL;
1293       s->substate = SS_START_OF_COMMAND | SS_START_OF_LINE;
1294       return segmenter_push (s, input, n, type);
1295     }
1296   else
1297     {
1298       *type = SEG_DO_REPEAT_COMMAND;
1299       return ofs;
1300     }
1301 }
1302
1303 static int
1304 segmenter_parse_begin_data_1__ (struct segmenter *s,
1305                                 const char *input, size_t n,
1306                                 enum segment_type *type)
1307 {
1308   int ofs = segmenter_subparse (s, input, n, type);
1309   if (ofs < 0)
1310     return -1;
1311
1312   if (*type == SEG_NEWLINE)
1313     s->state = S_BEGIN_DATA_2;
1314
1315   return ofs;
1316 }
1317
1318 static int
1319 segmenter_parse_begin_data_2__ (struct segmenter *s,
1320                                 const char *input, size_t n,
1321                                 enum segment_type *type)
1322 {
1323   int ofs = segmenter_subparse (s, input, n, type);
1324   if (ofs < 0)
1325     return -1;
1326
1327   if (*type == SEG_NEWLINE)
1328     s->state = S_BEGIN_DATA_3;
1329
1330   return ofs;
1331 }
1332
1333 static bool
1334 is_end_data (const char *input, size_t n)
1335 {
1336   const uint8_t *u_input = CHAR_CAST (const uint8_t *, input);
1337   bool endcmd;
1338   ucs4_t uc;
1339   int mblen;
1340   int ofs;
1341
1342   if (n < 3 || c_strncasecmp (input, "END", 3))
1343     return false;
1344
1345   ofs = 3;
1346   mblen = u8_mbtouc (&uc, u_input + ofs, n - ofs);
1347   if (!lex_uc_is_space (uc))
1348     return false;
1349   ofs += mblen;
1350
1351   if (n - ofs < 4 || c_strncasecmp (input + ofs, "DATA", 4))
1352     return false;
1353   ofs += 4;
1354
1355   endcmd = false;
1356   while (ofs < n)
1357     {
1358       mblen = u8_mbtouc (&uc, u_input + ofs, n - ofs);
1359       if (uc == '.')
1360         {
1361           if (endcmd)
1362             return false;
1363           endcmd = true;
1364         }
1365       else if (!lex_uc_is_space (uc))
1366         return false;
1367       ofs += mblen;
1368     }
1369
1370   return true;
1371 }
1372
1373 static int
1374 segmenter_parse_begin_data_3__ (struct segmenter *s,
1375                                 const char *input, size_t n,
1376                                 enum segment_type *type)
1377 {
1378   int ofs;
1379
1380   ofs = segmenter_parse_full_line__ (input, n, type);
1381   if (ofs < 0)
1382     return -1;
1383   else if (is_end_data (input, ofs))
1384     {
1385       s->state = S_GENERAL;
1386       s->substate = SS_START_OF_COMMAND | SS_START_OF_LINE;
1387       return segmenter_push (s, input, n, type);
1388     }
1389   else
1390     {
1391       *type = SEG_INLINE_DATA;
1392       s->state = S_BEGIN_DATA_4;
1393       return input[ofs - 1] == '\n' ? 0 : ofs;
1394     }
1395 }
1396
1397 static int
1398 segmenter_parse_begin_data_4__ (struct segmenter *s,
1399                                 const char *input, size_t n,
1400                                 enum segment_type *type)
1401 {
1402   int ofs;
1403
1404   ofs = segmenter_parse_newline__ (input, n, type);
1405   if (ofs < 0)
1406     return -1;
1407
1408   s->state = S_BEGIN_DATA_3;
1409   return ofs;
1410 }
1411
1412 static int
1413 segmenter_parse_title_1__ (struct segmenter *s,
1414                            const char *input, size_t n,
1415                            enum segment_type *type)
1416 {
1417   int ofs;
1418
1419   ofs = skip_spaces (input, n, 0);
1420   if (ofs < 0)
1421     return -1;
1422   s->state = S_TITLE_2;
1423   *type = SEG_SPACES;
1424   return ofs;
1425 }
1426
1427 static int
1428 segmenter_parse_title_2__ (struct segmenter *s,
1429                            const char *input, size_t n,
1430                            enum segment_type *type)
1431 {
1432   int endcmd;
1433   int ofs;
1434
1435   endcmd = -1;
1436   ofs = 0;
1437   while (ofs < n)
1438     {
1439       ucs4_t uc;
1440       int mblen;
1441
1442       mblen = segmenter_u8_to_uc__ (&uc, input + ofs, n - ofs);
1443       if (mblen < 0)
1444         return -1;
1445
1446       switch (uc)
1447         {
1448         case '\n':
1449         case '\0':
1450           s->state = S_GENERAL;
1451           s->substate = 0;
1452           *type = SEG_UNQUOTED_STRING;
1453           return endcmd >= 0 ? endcmd : ofs;
1454
1455         case '.':
1456           endcmd = ofs;
1457           break;
1458
1459         default:
1460           if (!lex_uc_is_space (uc))
1461             endcmd = -1;
1462           break;
1463         }
1464
1465       ofs += mblen;
1466     }
1467
1468   return -1;
1469 }
1470
1471 /* Returns the name of segment TYPE as a string.  The caller must not modify
1472    or free the returned string.
1473
1474    This is useful only for debugging and testing. */
1475 const char *
1476 segment_type_to_string (enum segment_type type)
1477 {
1478   switch (type)
1479     {
1480 #define SEG_TYPE(NAME) case SEG_##NAME: return #NAME;
1481       SEG_TYPES
1482 #undef SEG_TYPE
1483     default:
1484       return "unknown segment type";
1485     }
1486 }
1487
1488 /* Initializes S as a segmenter with the given syntax MODE.
1489
1490    A segmenter does not contain any external references, so nothing needs to be
1491    done to destroy one.  For the same reason, segmenters may be copied with
1492    plain struct assignment (or memcpy). */
1493 void
1494 segmenter_init (struct segmenter *s, enum segmenter_mode mode)
1495 {
1496   s->state = S_SHBANG;
1497   s->substate = 0;
1498   s->mode = mode;
1499 }
1500
1501 /* Returns the mode passed to segmenter_init() for S. */
1502 enum segmenter_mode
1503 segmenter_get_mode (const struct segmenter *s)
1504 {
1505   return s->mode;
1506 }
1507
1508 /* Attempts to label a prefix of S's remaining input with a segment type.  The
1509    caller supplies the first N bytes of the remaining input as INPUT, which
1510    must be a UTF-8 encoded string.  The end of the input stream must be
1511    indicated by a null byte at the beginning of a line, that is, immediately
1512    following a new-line (or as the first byte of the input stream).
1513
1514    The input may contain '\n' or '\r\n' line ends in any combination.
1515
1516    If successful, returns the number of bytes in the segment at the beginning
1517    of INPUT (between 0 and N, inclusive) and stores the type of that segment
1518    into *TYPE.  The next call to segmenter_push() should not include those
1519    bytes as part of INPUT, because they have (figuratively) been consumed by
1520    the segmenter.
1521
1522    Failure occurs only if the segment type of the N bytes in INPUT cannot yet
1523    be determined.  In this case segmenter_push() returns -1.  The caller should
1524    obtain more input and then call segmenter_push() again with a larger N and
1525    repeat until the input is exhausted (which must be indicated as described
1526    above) or until a valid segment is returned.  segmenter_push() will never
1527    return -1 when the end of input is visible within INPUT.
1528
1529    The caller must not, in a sequence of calls, supply contradictory input.
1530    That is, bytes provided as part of INPUT in one call, but not consumed, must
1531    not be provided with *different* values on subsequent calls.  This is
1532    because segmenter_push() must often make decisions based on looking ahead
1533    beyond the bytes that it consumes. */
1534 int
1535 segmenter_push (struct segmenter *s, const char *input, size_t n,
1536                 enum segment_type *type)
1537 {
1538   if (n == 0)
1539     return -1;
1540
1541   if (input[0] == '\0')
1542     {
1543       *type = SEG_END;
1544       return 1;
1545     }
1546
1547   switch (s->state)
1548     {
1549     case S_SHBANG:
1550       return segmenter_parse_shbang__ (s, input, n, type);
1551
1552     case S_GENERAL:
1553       return (s->substate & SS_START_OF_LINE
1554               ? segmenter_parse_start_of_line__ (s, input, n, type)
1555               : segmenter_parse_mid_command__ (s, input, n, type));
1556
1557     case S_COMMENT_1:
1558       return segmenter_parse_comment_1__ (s, input, n, type);
1559     case S_COMMENT_2:
1560       return segmenter_parse_comment_2__ (s, input, n, type);
1561
1562     case S_DOCUMENT_1:
1563       return segmenter_parse_document_1__ (s, input, n, type);
1564     case S_DOCUMENT_2:
1565       return segmenter_parse_document_2__ (s, input, n, type);
1566     case S_DOCUMENT_3:
1567       return segmenter_parse_document_3__ (s, type);
1568
1569     case S_FILE_LABEL:
1570       return segmenter_parse_file_label__ (s, input, n, type);
1571
1572     case S_DO_REPEAT_1:
1573       return segmenter_parse_do_repeat_1__ (s, input, n, type);
1574     case S_DO_REPEAT_2:
1575       return segmenter_parse_do_repeat_2__ (s, input, n, type);
1576     case S_DO_REPEAT_3:
1577       return segmenter_parse_do_repeat_3__ (s, input, n, type);
1578
1579     case S_BEGIN_DATA_1:
1580       return segmenter_parse_begin_data_1__ (s, input, n, type);
1581     case S_BEGIN_DATA_2:
1582       return segmenter_parse_begin_data_2__ (s, input, n, type);
1583     case S_BEGIN_DATA_3:
1584       return segmenter_parse_begin_data_3__ (s, input, n, type);
1585     case S_BEGIN_DATA_4:
1586       return segmenter_parse_begin_data_4__ (s, input, n, type);
1587
1588     case S_TITLE_1:
1589       return segmenter_parse_title_1__ (s, input, n, type);
1590     case S_TITLE_2:
1591       return segmenter_parse_title_2__ (s, input, n, type);
1592     }
1593
1594   NOT_REACHED ();
1595 }
1596
1597 /* Returns the style of command prompt to display to an interactive user for
1598    input in S.  The return value is most accurate in mode SEG_MODE_INTERACTIVE
1599    and at the beginning of a line (that is, if segmenter_push() consumed as
1600    much as possible of the input up to a new-line).  */
1601 enum prompt_style
1602 segmenter_get_prompt (const struct segmenter *s)
1603 {
1604   switch (s->state)
1605     {
1606     case S_SHBANG:
1607       return PROMPT_FIRST;
1608
1609     case S_GENERAL:
1610       return s->substate & SS_START_OF_COMMAND ? PROMPT_FIRST : PROMPT_LATER;
1611
1612     case S_COMMENT_1:
1613     case S_COMMENT_2:
1614       return PROMPT_COMMENT;
1615
1616     case S_DOCUMENT_1:
1617     case S_DOCUMENT_2:
1618       return PROMPT_DOCUMENT;
1619     case S_DOCUMENT_3:
1620       return PROMPT_FIRST;
1621
1622     case S_FILE_LABEL:
1623       return PROMPT_LATER;
1624
1625     case S_DO_REPEAT_1:
1626     case S_DO_REPEAT_2:
1627       return s->substate & SS_START_OF_COMMAND ? PROMPT_FIRST : PROMPT_LATER;
1628     case S_DO_REPEAT_3:
1629       return PROMPT_DO_REPEAT;
1630
1631     case S_BEGIN_DATA_1:
1632       return PROMPT_FIRST;
1633     case S_BEGIN_DATA_2:
1634       return PROMPT_LATER;
1635     case S_BEGIN_DATA_3:
1636     case S_BEGIN_DATA_4:
1637       return PROMPT_DATA;
1638
1639     case S_TITLE_1:
1640     case S_TITLE_2:
1641       return PROMPT_FIRST;
1642     }
1643
1644   NOT_REACHED ();
1645 }