9728e176f4df9ab6070d7169ebe40d00a4ae4684
[pspp] / src / language / lexer / segment.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2010, 2011, 2013, 2016 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/verify.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_DEFINE_1,
47     S_DEFINE_2,
48     S_DEFINE_3,
49     S_DEFINE_4,
50     S_BEGIN_DATA_1,
51     S_BEGIN_DATA_2,
52     S_BEGIN_DATA_3,
53     S_BEGIN_DATA_4,
54     S_TITLE_1,
55     S_TITLE_2
56   };
57
58 #define SS_START_OF_LINE (1u << 0)
59 #define SS_START_OF_COMMAND (1u << 1)
60
61 static int segmenter_detect_command_name__ (const char *input,
62                                             size_t n, bool eof, int ofs);
63
64 static int
65 segmenter_u8_to_uc__ (ucs4_t *puc, const char *input_, size_t n, bool eof,
66                       size_t ofs)
67 {
68   const uint8_t *input = CHAR_CAST (const uint8_t *, input_);
69   int mblen;
70
71   assert (n > ofs);
72
73   input += ofs;
74   n -= ofs;
75
76   mblen = u8_mbtoucr (puc, input, n);
77   if (mblen >= 0)
78     return mblen;
79   else if (mblen != -2)
80     return u8_mbtouc (puc, input, n);
81   else if (eof)
82     {
83       *puc = 0xfffd;
84       return n;
85     }
86   else
87     return -1;
88 }
89
90 static int
91 segmenter_parse_shbang__ (struct segmenter *s, const char *input, size_t n,
92                           bool eof, enum segment_type *type)
93 {
94   if (input[0] == '#')
95     {
96       if (n >= 2)
97         {
98           if (input[1] == '!')
99             {
100               for (int ofs = 2; ; ofs++)
101                 {
102                   if (ofs >= n)
103                     {
104                       if (!eof)
105                         return -1;
106                     }
107                   else if (input[ofs] == '\n')
108                     {
109                       if (input[ofs - 1] == '\r')
110                         ofs--;
111                     }
112                   else
113                     continue;
114
115                   s->state = S_GENERAL;
116                   s->substate = SS_START_OF_COMMAND;
117                   *type = SEG_SHBANG;
118                   return ofs;
119                 }
120             }
121         }
122       else if (!eof)
123         return -1;
124     }
125
126   s->state = S_GENERAL;
127   s->substate = SS_START_OF_LINE | SS_START_OF_COMMAND;
128   return segmenter_push (s, input, n, eof, type);
129 }
130
131 static int
132 segmenter_parse_digraph__ (const char *seconds, struct segmenter *s,
133                            const char *input, size_t n, bool eof,
134                            enum segment_type *type)
135 {
136   assert (s->state == S_GENERAL);
137
138   *type = SEG_PUNCT;
139   s->substate = 0;
140   return (n < 2
141           ? (eof ? 1 : -1)
142           : (strchr (seconds, input[1]) != NULL ? 2 : 1));
143 }
144
145 static int
146 skip_comment (const char *input, size_t n, bool eof, size_t ofs)
147 {
148   for (; ofs < n; ofs++)
149     {
150       if (input[ofs] == '\n')
151         return ofs;
152       else if (input[ofs] == '*')
153         {
154           if (ofs + 1 >= n)
155             return eof ? ofs + 1 : -1;
156           else if (input[ofs + 1] == '/')
157             return ofs + 2;
158         }
159     }
160   return eof ? ofs : -1;
161 }
162
163 static int
164 skip_spaces_and_comments (const char *input, size_t n, bool eof, int ofs)
165 {
166   while (ofs < n)
167     {
168       ucs4_t uc;
169       int mblen;
170
171       mblen = segmenter_u8_to_uc__ (&uc, input, n, eof, ofs);
172       if (mblen < 0)
173         return -1;
174
175       if (uc == '/')
176         {
177           if (ofs + 1 >= n)
178             return eof ? ofs : -1;
179           else if (input[ofs + 1] != '*')
180             return ofs;
181
182           ofs = skip_comment (input, n, eof, ofs + 2);
183           if (ofs < 0)
184             return -1;
185         }
186       else if (lex_uc_is_space (uc) && uc != '\n')
187         ofs += mblen;
188       else
189         return ofs;
190     }
191
192   return eof ? ofs : -1;
193 }
194
195 static int
196 is_end_of_line (const char *input, size_t n, bool eof, int ofs)
197 {
198   if (ofs >= n)
199     return eof ? 1 : -1;
200   else if (input[ofs] == '\n')
201     return 1;
202   else if (input[ofs] == '\r')
203     {
204       if (ofs + 1 >= n)
205         return eof ? 1 : -1;
206       return input[ofs + 1] == '\n';
207     }
208   else
209     return 0;
210 }
211
212 static int
213 at_end_of_line (const char *input, size_t n, bool eof, int ofs)
214 {
215   ofs = skip_spaces_and_comments (input, n, eof, ofs);
216   if (ofs < 0)
217     return -1;
218
219   return is_end_of_line (input, n, eof, ofs);
220 }
221
222 static bool
223 is_all_spaces (const char *input_, size_t n)
224 {
225   const uint8_t *input = CHAR_CAST (const uint8_t *, input_);
226
227   int mblen;
228   for (int ofs = 0; ofs < n; ofs += mblen)
229     {
230       ucs4_t uc;
231       mblen = u8_mbtouc (&uc, input + ofs, n - ofs);
232       if (!lex_uc_is_space (uc))
233         return false;
234     }
235   return true;
236 }
237
238 static int
239 segmenter_parse_newline__ (const char *input, size_t n, bool eof,
240                            enum segment_type *type)
241 {
242   int ofs;
243
244   if (input[0] == '\n')
245     ofs = 1;
246   else
247     {
248       if (n < 2)
249         {
250           assert (!eof);
251           return -1;
252         }
253
254       assert (input[0] == '\r');
255       assert (input[1] == '\n');
256       ofs = 2;
257     }
258
259   *type = SEG_NEWLINE;
260   return ofs;
261 }
262
263 static int
264 skip_spaces (const char *input, size_t n, bool eof, size_t ofs)
265 {
266   while (ofs < n)
267     {
268       ucs4_t uc;
269       int mblen;
270
271       mblen = segmenter_u8_to_uc__ (&uc, input, n, eof, ofs);
272       if (mblen < 0)
273         return -1;
274
275       if (!lex_uc_is_space (uc) || uc == '\n')
276         return ofs;
277
278       ofs += mblen;
279     }
280
281   return eof ? ofs : -1;
282 }
283
284 static int
285 skip_digits (const char *input, size_t n, bool eof, int ofs)
286 {
287   for (; ofs < n; ofs++)
288     if (!c_isdigit (input[ofs]))
289       return ofs;
290   return eof ? ofs : -1;
291 }
292
293 static int
294 segmenter_parse_number__ (struct segmenter *s, const char *input, size_t n,
295                           bool eof, enum segment_type *type)
296 {
297   int ofs;
298
299   assert (s->state == S_GENERAL);
300
301   ofs = skip_digits (input, n, eof, 0);
302   if (ofs < 0)
303     return -1;
304
305   if (ofs >= n)
306     {
307       if (!eof)
308         return -1;
309       goto number;
310     }
311   if (input[ofs] == '.')
312     {
313       if (ofs + 1 >= n)
314         {
315           if (!eof)
316             return -1;
317           goto number;
318         }
319
320       ofs = skip_digits (input, n, eof, ofs + 1);
321       if (ofs < 0)
322         return -1;
323       else if (ofs >= n)
324         goto number;
325     }
326
327   if (input[ofs] == 'e' || input[ofs] == 'E')
328     {
329       ofs++;
330       if (ofs >= n)
331         {
332           if (!eof)
333             return -1;
334           goto expected_exponent;
335         }
336
337       if (input[ofs] == '+' || input[ofs] == '-')
338         {
339           ofs++;
340           if (ofs >= n)
341             {
342               if (!eof)
343                 return -1;
344               goto expected_exponent;
345             }
346         }
347
348       if (!c_isdigit (input[ofs]))
349         goto expected_exponent;
350
351       ofs = skip_digits (input, n, eof, ofs);
352       if (ofs < 0)
353         return -1;
354     }
355
356   if (input[ofs - 1] == '.')
357     {
358       int eol = at_end_of_line (input, n, eof, ofs);
359       if (eol < 0)
360         return -1;
361       else if (eol)
362         ofs--;
363     }
364
365 number:
366   *type = SEG_NUMBER;
367   s->substate = 0;
368   return ofs;
369
370 expected_exponent:
371   *type = SEG_EXPECTED_EXPONENT;
372   s->substate = 0;
373   return ofs;
374 }
375
376 static bool
377 is_reserved_word (const char *s, int n)
378 {
379   char s0, s1, s2, s3;
380
381   s0 = c_toupper (s[0]);
382   switch (n)
383     {
384     case 2:
385       s1 = c_toupper (s[1]);
386       return ((s0 == 'B' && s1 == 'Y')
387               || (s0 == 'E' && s1 == 'Q')
388               || (s0 == 'G' && (s1 == 'E' || s1 == 'T'))
389               || (s0 == 'L' && (s1 == 'E' || s1 == 'T'))
390               || (s0 == 'N' && s1 == 'E')
391               || (s0 == 'O' && s1 == 'R')
392               || (s0 == 'T' && s1 == 'O'));
393
394     case 3:
395       s1 = c_toupper (s[1]);
396       s2 = c_toupper (s[2]);
397       return ((s0 == 'A' && ((s1 == 'L' && s2 == 'L')
398                              || (s1 == 'N' && s2 == 'D')))
399               || (s0 == 'N' && s1 == 'O' && s2 == 'T'));
400
401     case 4:
402       s1 = c_toupper (s[1]);
403       s2 = c_toupper (s[2]);
404       s3 = c_toupper (s[3]);
405       return s0 == 'W' && s1 == 'I' && s2 == 'T' && s3 == 'H';
406
407     default:
408       return false;
409     }
410 }
411
412 static int
413 segmenter_parse_comment_1__ (struct segmenter *s,
414                              const char *input, size_t n, bool eof,
415                              enum segment_type *type)
416 {
417   int endcmd;
418   int ofs;
419
420   endcmd = -2;
421   ofs = 0;
422   while (ofs < n)
423     {
424       ucs4_t uc;
425       int mblen;
426
427       mblen = segmenter_u8_to_uc__ (&uc, input, n, eof, ofs);
428       if (mblen < 0)
429         return -1;
430
431       switch (uc)
432         {
433         case '.':
434           endcmd = ofs;
435           break;
436
437         case '\n':
438           if (ofs > 1 && input[ofs - 1] == '\r')
439             ofs--;
440           if (endcmd == -2)
441             {
442               /* Blank line ends comment command. */
443               s->state = S_GENERAL;
444               s->substate = SS_START_OF_COMMAND;
445               *type = SEG_SEPARATE_COMMANDS;
446               return ofs;
447             }
448           else if (endcmd >= 0)
449             {
450               /* '.' at end of line ends comment command. */
451               s->state = S_GENERAL;
452               s->substate = 0;
453               *type = SEG_COMMENT_COMMAND;
454               return endcmd;
455             }
456           else
457             {
458               /* Comment continues onto next line. */
459               *type = SEG_COMMENT_COMMAND;
460               s->state = S_COMMENT_2;
461               return ofs;
462             }
463           NOT_REACHED ();
464
465         default:
466           if (!lex_uc_is_space (uc))
467             endcmd = -1;
468           break;
469         }
470
471       ofs += mblen;
472     }
473
474   if (eof)
475     {
476       /* End of file. */
477       s->state = S_GENERAL;
478       s->substate = SS_START_OF_COMMAND;
479       *type = SEG_SEPARATE_COMMANDS;
480       return ofs;
481     }
482
483   return -1;
484 }
485
486 static int
487 segmenter_parse_comment_2__ (struct segmenter *s, const char *input,
488                              size_t n, bool eof, enum segment_type *type)
489 {
490   int ofs = segmenter_parse_newline__ (input, n, eof, type);
491   if (ofs < 0)
492     return -1;
493
494   int new_cmd;
495   if (ofs >= n)
496     {
497       if (!eof)
498         return -1;
499       new_cmd = false;
500     }
501   else
502     {
503       ucs4_t uc;
504       int mblen = segmenter_u8_to_uc__ (&uc, input, n, eof, ofs);
505       if (mblen < 0)
506         return -1;
507
508       if (uc == '+' || uc == '-' || uc == '.')
509         new_cmd = true;
510       else if (!lex_uc_is_space (uc))
511         switch (s->mode)
512           {
513           case SEG_MODE_INTERACTIVE:
514             new_cmd = false;
515             break;
516
517           case SEG_MODE_BATCH:
518             new_cmd = true;
519             break;
520
521           case SEG_MODE_AUTO:
522             new_cmd = segmenter_detect_command_name__ (input, n, eof, ofs);
523             if (new_cmd < 0)
524               return -1;
525             break;
526
527           default:
528             NOT_REACHED ();
529           }
530       else
531         new_cmd = false;
532     }
533
534   if (new_cmd)
535     {
536       s->state = S_GENERAL;
537       s->substate = SS_START_OF_LINE | SS_START_OF_COMMAND;
538     }
539   else
540     s->state = S_COMMENT_1;
541   return ofs;
542 }
543
544 static int
545 segmenter_parse_document_1__ (struct segmenter *s, const char *input, size_t n,
546                               bool eof, enum segment_type *type)
547 {
548   bool end_cmd;
549   int ofs;
550
551   end_cmd = false;
552   ofs = 0;
553   while (ofs < n)
554     {
555       ucs4_t uc;
556       int mblen;
557
558       mblen = segmenter_u8_to_uc__ (&uc, input, n, eof, ofs);
559       if (mblen < 0)
560         return -1;
561
562       switch (uc)
563         {
564         case '.':
565           end_cmd = true;
566           break;
567
568         case '\n':
569           if (ofs > 1 && input[ofs - 1] == '\r')
570             ofs--;
571
572           *type = SEG_DOCUMENT;
573           s->state = end_cmd ? S_DOCUMENT_3 : S_DOCUMENT_2;
574           return ofs;
575
576         default:
577           if (!lex_uc_is_space (uc))
578             end_cmd = false;
579           break;
580         }
581
582       ofs += mblen;
583     }
584   if (eof)
585     {
586       *type = SEG_DOCUMENT;
587       s->state = S_DOCUMENT_3;
588       return ofs;
589     }
590   return -1;
591 }
592
593 static int
594 segmenter_parse_document_2__ (struct segmenter *s, const char *input, size_t n,
595                               bool eof, enum segment_type *type)
596 {
597   int ofs;
598
599   ofs = segmenter_parse_newline__ (input, n, eof, type);
600   if (ofs < 0)
601     return -1;
602
603   s->state = S_DOCUMENT_1;
604   return ofs;
605 }
606
607 static int
608 segmenter_parse_document_3__ (struct segmenter *s, enum segment_type *type)
609 {
610   *type = SEG_END_COMMAND;
611   s->state = S_GENERAL;
612   s->substate = SS_START_OF_COMMAND | SS_START_OF_LINE;
613   return 0;
614 }
615
616 static int
617 segmenter_unquoted (const char *input, size_t n, bool eof, int ofs)
618
619 {
620   ofs = skip_spaces_and_comments (input, n, eof, ofs);
621   if (ofs < 0)
622     return -1;
623   else if (ofs < n)
624     {
625       char c = input[ofs];
626       return c != '\'' && c != '"' && c != '\n';
627     }
628   else
629     {
630       assert (eof);
631       return 0;
632     }
633 }
634
635 static int
636 next_id_in_command (const struct segmenter *s, const char *input, size_t n,
637                     bool eof, int ofs, char id[], size_t id_size)
638 {
639   struct segmenter sub;
640
641   assert (id_size > 0);
642
643   sub.mode = s->mode;
644   sub.state = S_GENERAL;
645   sub.substate = 0;
646   for (;;)
647     {
648       enum segment_type type;
649       int retval;
650
651       retval = segmenter_push (&sub, input + ofs, n - ofs, eof, &type);
652       if (retval < 0)
653         {
654           id[0] = '\0';
655           return -1;
656         }
657
658       switch (type)
659         {
660         case SEG_SHBANG:
661         case SEG_SPACES:
662         case SEG_COMMENT:
663         case SEG_NEWLINE:
664           break;
665
666         case SEG_IDENTIFIER:
667           if (retval < id_size)
668             {
669               memcpy (id, input + ofs, retval);
670               id[retval] = '\0';
671               return ofs + retval;
672             }
673           /* fall through */
674
675         case SEG_NUMBER:
676         case SEG_QUOTED_STRING:
677         case SEG_HEX_STRING:
678         case SEG_UNICODE_STRING:
679         case SEG_UNQUOTED_STRING:
680         case SEG_RESERVED_WORD:
681         case SEG_PUNCT:
682         case SEG_COMMENT_COMMAND:
683         case SEG_DO_REPEAT_COMMAND:
684         case SEG_INLINE_DATA:
685         case SEG_MACRO_ID:
686         case SEG_MACRO_BODY:
687         case SEG_START_DOCUMENT:
688         case SEG_DOCUMENT:
689         case SEG_START_COMMAND:
690         case SEG_SEPARATE_COMMANDS:
691         case SEG_END_COMMAND:
692         case SEG_END:
693         case SEG_EXPECTED_QUOTE:
694         case SEG_EXPECTED_EXPONENT:
695         case SEG_UNEXPECTED_CHAR:
696           id[0] = '\0';
697           return ofs + retval;
698         }
699       ofs += retval;
700     }
701 }
702
703 /* Called when INPUT begins with a character that can start off an ID token. */
704 static int
705 segmenter_parse_id__ (struct segmenter *s, const char *input, size_t n,
706                       bool eof, enum segment_type *type)
707 {
708   ucs4_t uc;
709   int ofs;
710
711   assert (n > 0);
712   assert (s->state == S_GENERAL);
713
714   ofs = u8_mbtouc (&uc, CHAR_CAST (const uint8_t *, input), n);
715   for (;;)
716     {
717       int mblen;
718
719       if (ofs >= n)
720         {
721           if (eof)
722             break;
723           return -1;
724         }
725
726       mblen = segmenter_u8_to_uc__ (&uc, input, n, eof, ofs);
727       if (mblen < 0)
728         return -1;
729       else if (!lex_uc_is_idn (uc))
730         break;
731
732       ofs += mblen;
733     }
734
735   if (input[ofs - 1] == '.')
736     {
737       int eol = at_end_of_line (input, n, eof, ofs);
738       if (eol < 0)
739         return -1;
740       else if (eol)
741         ofs--;
742     }
743
744   *type = (is_reserved_word (input, ofs) ? SEG_RESERVED_WORD
745            : input[0] == '!' ? SEG_MACRO_ID
746            : SEG_IDENTIFIER);
747
748   if (s->substate & SS_START_OF_COMMAND)
749     {
750       struct substring word = ss_buffer (input, ofs);
751
752       if (lex_id_match_n (ss_cstr ("COMMENT"), word, 4))
753         {
754           s->state = S_COMMENT_1;
755           return segmenter_parse_comment_1__ (s, input, n, eof, type);
756         }
757       else if (lex_id_match (ss_cstr ("DOCUMENT"), word))
758         {
759           s->state = S_DOCUMENT_1;
760           *type = SEG_START_DOCUMENT;
761           return 0;
762         }
763       else if (lex_id_match (ss_cstr ("TITLE"), word)
764                || lex_id_match (ss_cstr ("SUBTITLE"), word))
765         {
766           int result = segmenter_unquoted (input, n, eof, ofs);
767           if (result < 0)
768             return -1;
769           else if (result)
770             {
771               s->state = S_TITLE_1;
772               return ofs;
773             }
774         }
775       else if (lex_id_match_n (ss_cstr ("DEFINE"), word, 6))
776         {
777           s->state = S_DEFINE_1;
778           return ofs;
779         }
780       else if (lex_id_match (ss_cstr ("FILE"), word))
781         {
782           char id[16];
783
784           if (next_id_in_command (s, input, n, eof, ofs, id, sizeof id) < 0)
785             return -1;
786           else if (lex_id_match (ss_cstr ("LABEL"), ss_cstr (id)))
787             {
788               s->state = S_FILE_LABEL;
789               s->substate = 0;
790               return ofs;
791             }
792         }
793       else if (lex_id_match (ss_cstr ("DO"), word))
794         {
795           char id[16];
796
797           if (next_id_in_command (s, input, n, eof, ofs, id, sizeof id) < 0)
798             return -1;
799           else if (lex_id_match (ss_cstr ("REPEAT"), ss_cstr (id)))
800             {
801               s->state = S_DO_REPEAT_1;
802               s->substate = 0;
803               return ofs;
804             }
805         }
806       else if (lex_id_match (ss_cstr ("BEGIN"), word))
807         {
808           char id[16];
809           int ofs2;
810
811           ofs2 = next_id_in_command (s, input, n, eof, ofs, id, sizeof id);
812           if (ofs2 < 0)
813             return -1;
814           else if (lex_id_match (ss_cstr ("DATA"), ss_cstr (id)))
815             {
816               int eol;
817
818               ofs2 = skip_spaces_and_comments (input, n, eof, ofs2);
819               if (ofs2 < 0)
820                 return -1;
821
822               if (ofs2 >= n)
823                 assert (eof);
824               else if (input[ofs2] == '.')
825                 {
826                   ofs2 = skip_spaces_and_comments (input, n, eof, ofs2 + 1);
827                   if (ofs2 < 0)
828                     return -1;
829                 }
830
831               eol = is_end_of_line (input, n, eof, ofs2);
832               if (eol < 0)
833                 return -1;
834               else if (eol)
835                 {
836                   if (memchr (input, '\n', ofs2))
837                     s->state = S_BEGIN_DATA_1;
838                   else
839                     s->state = S_BEGIN_DATA_2;
840                   s->substate = 0;
841                   return ofs;
842                 }
843             }
844         }
845     }
846
847   s->substate = 0;
848   return ofs;
849 }
850
851 static int
852 segmenter_parse_string__ (enum segment_type string_type,
853                           int ofs, struct segmenter *s,
854                           const char *input, size_t n, bool eof,
855                           enum segment_type *type)
856 {
857   int quote = input[ofs];
858
859   ofs++;
860   while (ofs < n)
861     if (input[ofs] == quote)
862       {
863         ofs++;
864         if (ofs < n)
865           {
866             if (input[ofs] == quote)
867               {
868                 ofs++;
869                 continue;
870               }
871           }
872         else if (!eof)
873           return -1;
874
875         *type = string_type;
876         s->substate = 0;
877         return ofs;
878       }
879     else if (input[ofs] == '\n')
880       goto expected_quote;
881     else
882       ofs++;
883
884   if (eof)
885     goto expected_quote;
886
887   return -1;
888
889 expected_quote:
890   *type = SEG_EXPECTED_QUOTE;
891   s->substate = 0;
892   return ofs;
893 }
894
895 static int
896 segmenter_maybe_parse_string__ (enum segment_type string_type,
897                                 struct segmenter *s,
898                                 const char *input, size_t n, bool eof,
899                                 enum segment_type *type)
900 {
901   if (n < 2)
902     {
903       if (!eof)
904         return -1;
905     }
906   else if (input[1] == '\'' || input[1] == '"')
907     return segmenter_parse_string__ (string_type, 1, s, input, n, eof, type);
908
909   return segmenter_parse_id__ (s, input, n, eof, type);
910 }
911
912 static int
913 segmenter_parse_mid_command__ (struct segmenter *s,
914                                const char *input, size_t n, bool eof,
915                                enum segment_type *type)
916 {
917   ucs4_t uc;
918   int mblen;
919   int ofs;
920
921   assert (s->state == S_GENERAL);
922   assert (!(s->substate & SS_START_OF_LINE));
923
924   mblen = segmenter_u8_to_uc__ (&uc, input, n, eof, 0);
925   if (mblen < 0)
926     return -1;
927
928   switch (uc)
929     {
930     case '\n':
931       s->substate |= SS_START_OF_LINE;
932       *type = SEG_NEWLINE;
933       return 1;
934
935     case '/':
936       if (n < 2)
937         {
938           if (!eof)
939             return -1;
940         }
941       else if (input[1] == '*')
942         {
943           ofs = skip_comment (input, n, eof, 2);
944           if (ofs < 0)
945             return -1;
946
947           *type = SEG_COMMENT;
948           return ofs;
949         }
950
951       s->substate = 0;
952       *type = SEG_PUNCT;
953       return 1;
954
955     case '(': case ')': case ',': case '=': case '-':
956     case '[': case ']': case '&': case '|': case '+':
957       *type = SEG_PUNCT;
958       s->substate = 0;
959       return 1;
960
961     case '*':
962       if (s->substate & SS_START_OF_COMMAND)
963         {
964           /* '*' at the beginning of a command begins a comment. */
965           s->state = S_COMMENT_1;
966           return segmenter_parse_comment_1__ (s, input, n, eof, type);
967         }
968       else
969         return segmenter_parse_digraph__ ("*", s, input, n, eof, type);
970
971     case '<':
972       return segmenter_parse_digraph__ ("=>", s, input, n, eof, type);
973
974     case '>':
975       return segmenter_parse_digraph__ ("=", s, input, n, eof, type);
976
977     case '~':
978       return segmenter_parse_digraph__ ("=", s, input, n, eof, type);
979
980     case '.':
981       if (n < 2)
982         {
983           if (!eof)
984             return -1;
985         }
986       else if (c_isdigit (input[1]))
987         return segmenter_parse_number__ (s, input, n, eof, type);
988
989       int eol = at_end_of_line (input, n, eof, 1);
990       if (eol < 0)
991         return -1;
992
993       if (eol)
994         {
995           *type = SEG_END_COMMAND;
996           s->substate = SS_START_OF_COMMAND;
997         }
998       else
999         *type = SEG_PUNCT;
1000       return 1;
1001
1002     case '0': case '1': case '2': case '3': case '4':
1003     case '5': case '6': case '7': case '8': case '9':
1004       return segmenter_parse_number__ (s, input, n, eof, type);
1005
1006     case 'u': case 'U':
1007       return segmenter_maybe_parse_string__ (SEG_UNICODE_STRING,
1008                                              s, input, n, eof, type);
1009
1010     case 'x': case 'X':
1011       return segmenter_maybe_parse_string__ (SEG_HEX_STRING,
1012                                              s, input, n, eof, type);
1013
1014     case '\'': case '"':
1015       return segmenter_parse_string__ (SEG_QUOTED_STRING, 0,
1016                                        s, input, n, eof, type);
1017
1018     case '!':
1019       return segmenter_parse_id__ (s, input, n, eof, type);
1020
1021     default:
1022       if (lex_uc_is_space (uc))
1023         {
1024           ofs = skip_spaces (input, n, eof, mblen);
1025           if (ofs < 0)
1026             return -1;
1027
1028           if (input[ofs - 1] == '\r' && input[ofs] == '\n')
1029             {
1030               if (ofs == 1)
1031                 {
1032                   s->substate |= SS_START_OF_LINE;
1033                   *type = SEG_NEWLINE;
1034                   return 2;
1035                 }
1036               else
1037                 ofs--;
1038             }
1039           *type = SEG_SPACES;
1040           return ofs;
1041         }
1042       else if (lex_uc_is_id1 (uc))
1043         return segmenter_parse_id__ (s, input, n, eof, type);
1044       else if (uc > 32 && uc < 127 && uc != '\\' && uc != '^')
1045         {
1046           *type = SEG_PUNCT;
1047           s->substate = 0;
1048           return 1;
1049         }
1050       else
1051         {
1052           *type = SEG_UNEXPECTED_CHAR;
1053           s->substate = 0;
1054           return mblen;
1055         }
1056     }
1057 }
1058
1059 static int
1060 compare_commands (const void *a_, const void *b_)
1061 {
1062   const char *const *ap = a_;
1063   const char *const *bp = b_;
1064   const char *a = *ap;
1065   const char *b = *bp;
1066
1067   return c_strcasecmp (a, b);
1068 }
1069
1070 static const char **
1071 segmenter_get_command_name_candidates (unsigned char first)
1072 {
1073 #define DEF_CMD(STATES, FLAGS, NAME, FUNCTION) NAME,
1074 #define UNIMPL_CMD(NAME, DESCRIPTION) NAME,
1075   static const char *commands[] =
1076     {
1077 #include "language/command.def"
1078       ""
1079     };
1080   static size_t n_commands = (sizeof commands / sizeof *commands) - 1;
1081 #undef DEF_CMD
1082 #undef UNIMPL_CMD
1083
1084   static bool inited;
1085
1086   static const char **cindex[UCHAR_MAX + 1];
1087
1088   if (!inited)
1089     {
1090       size_t i;
1091
1092       inited = true;
1093
1094       qsort (commands, n_commands, sizeof *commands, compare_commands);
1095       for (i = 0; i < n_commands; i++)
1096         {
1097           unsigned char c = c_toupper (commands[i][0]);
1098           if (cindex[c] == NULL)
1099             cindex[c] = &commands[i];
1100         }
1101       for (i = 0; i <= UCHAR_MAX; i++)
1102         if (cindex[i] == NULL)
1103           cindex[i] = &commands[n_commands];
1104     }
1105
1106   return cindex[c_toupper (first)];
1107 }
1108
1109 static int
1110 segmenter_detect_command_name__ (const char *input, size_t n, bool eof,
1111                                  int ofs)
1112 {
1113   const char **commands;
1114
1115   input += ofs;
1116   n -= ofs;
1117   ofs = 0;
1118   for (;;)
1119     {
1120       ucs4_t uc;
1121       int mblen;
1122
1123       if (ofs >= n)
1124         {
1125           if (eof)
1126             break;
1127           return -1;
1128         }
1129
1130       mblen = segmenter_u8_to_uc__ (&uc, input, n, eof, ofs);
1131       if (mblen < 0)
1132         return -1;
1133
1134       if (uc == '\n'
1135           || !(lex_uc_is_space (uc) || lex_uc_is_idn (uc) || uc == '-'))
1136         break;
1137
1138       ofs += mblen;
1139     }
1140   if (!ofs)
1141     return 0;
1142
1143   if (input[ofs - 1] == '.')
1144     ofs--;
1145
1146   for (commands = segmenter_get_command_name_candidates (input[0]);
1147        c_toupper (input[0]) == c_toupper ((*commands)[0]);
1148        commands++)
1149     {
1150       int missing_words;
1151       bool exact;
1152
1153       if (command_match (ss_cstr (*commands), ss_buffer (input, ofs),
1154                          &exact, &missing_words)
1155           && missing_words <= 0)
1156         return 1;
1157     }
1158
1159   return 0;
1160 }
1161
1162 static int
1163 is_start_of_string__ (const char *input, size_t n, bool eof, int ofs)
1164 {
1165   if (ofs >= n)
1166     return eof ? 0 : -1;
1167
1168   int c = input[ofs];
1169   if (c == 'x' || c == 'X' || c == 'u' || c == 'U')
1170     {
1171       if (ofs + 1 >= n)
1172         return eof ? 0 : -1;
1173
1174       return input[ofs + 1] == '\'' || input[ofs + 1] == '"';
1175     }
1176   else
1177     return c == '\'' || c == '"' || c == '\n';
1178 }
1179
1180 static int
1181 segmenter_parse_start_of_line__ (struct segmenter *s,
1182                                  const char *input, size_t n, bool eof,
1183                                  enum segment_type *type)
1184 {
1185   ucs4_t uc;
1186   int mblen;
1187   int ofs;
1188
1189   assert (s->state == S_GENERAL);
1190   assert (s->substate & SS_START_OF_LINE);
1191
1192   mblen = segmenter_u8_to_uc__ (&uc, input, n, eof, 0);
1193   if (mblen < 0)
1194     return -1;
1195
1196   switch (uc)
1197     {
1198     case '+':
1199       ofs = skip_spaces_and_comments (input, n, eof, 1);
1200       if (ofs < 0)
1201         return -1;
1202       else
1203         {
1204           int is_string = is_start_of_string__ (input, n, eof, ofs);
1205           if (is_string < 0)
1206             return -1;
1207           else if (is_string)
1208             {
1209               /* This is punctuation that may separate pieces of a string. */
1210               *type = SEG_PUNCT;
1211               s->substate = 0;
1212               return 1;
1213             }
1214         }
1215       /* Fall through. */
1216
1217     case '-':
1218     case '.':
1219       *type = SEG_START_COMMAND;
1220       s->substate = SS_START_OF_COMMAND;
1221       return 1;
1222
1223     default:
1224       if (lex_uc_is_space (uc))
1225         {
1226           int eol = at_end_of_line (input, n, eof, 0);
1227           if (eol < 0)
1228             return -1;
1229           else if (eol)
1230             {
1231               s->substate = SS_START_OF_COMMAND;
1232               *type = SEG_SEPARATE_COMMANDS;
1233               return 0;
1234             }
1235           break;
1236         }
1237
1238       if (s->mode == SEG_MODE_INTERACTIVE || s->substate & SS_START_OF_COMMAND)
1239         break;
1240       else if (s->mode == SEG_MODE_AUTO)
1241         {
1242           int cmd = segmenter_detect_command_name__ (input, n, eof, 0);
1243           if (cmd < 0)
1244             return -1;
1245           else if (cmd == 0)
1246             break;
1247         }
1248       else
1249         assert (s->mode == SEG_MODE_BATCH);
1250
1251       s->substate = SS_START_OF_COMMAND;
1252       *type = SEG_START_COMMAND;
1253       return 0;
1254     }
1255
1256   s->substate = SS_START_OF_COMMAND;
1257   return segmenter_parse_mid_command__ (s, input, n, eof, type);
1258 }
1259
1260 static int
1261 segmenter_parse_file_label__ (struct segmenter *s,
1262                               const char *input, size_t n, bool eof,
1263                               enum segment_type *type)
1264 {
1265   struct segmenter sub;
1266   int ofs;
1267
1268   sub = *s;
1269   sub.state = S_GENERAL;
1270   ofs = segmenter_push (&sub, input, n, eof, type);
1271
1272   if (ofs < 0)
1273     return -1;
1274   else if (*type == SEG_IDENTIFIER)
1275     {
1276       int result;
1277
1278       assert (lex_id_match (ss_cstr ("LABEL"),
1279                             ss_buffer ((char *) input, ofs)));
1280       result = segmenter_unquoted (input, n, eof, ofs);
1281       if (result < 0)
1282         return -1;
1283       else
1284         {
1285           if (result)
1286             s->state = S_TITLE_1;
1287           else
1288             *s = sub;
1289           return ofs;
1290         }
1291     }
1292   else
1293     {
1294       s->substate = sub.substate;
1295       return ofs;
1296     }
1297 }
1298
1299 static int
1300 segmenter_subparse (struct segmenter *s,
1301                     const char *input, size_t n, bool eof,
1302                     enum segment_type *type)
1303 {
1304   struct segmenter sub;
1305   int ofs;
1306
1307   sub.mode = s->mode;
1308   sub.state = S_GENERAL;
1309   sub.substate = s->substate;
1310   ofs = segmenter_push (&sub, input, n, eof, type);
1311   s->substate = sub.substate;
1312   return ofs;
1313 }
1314
1315 /* We are segmenting a DO REPEAT command, currently reading the syntax that
1316    defines the stand-in variables (the head) before the lines of syntax to be
1317    repeated (the body). */
1318 static int
1319 segmenter_parse_do_repeat_1__ (struct segmenter *s,
1320                                const char *input, size_t n, bool eof,
1321                                enum segment_type *type)
1322 {
1323   int ofs = segmenter_subparse (s, input, n, eof, type);
1324   if (ofs < 0)
1325     return -1;
1326
1327   if (*type == SEG_SEPARATE_COMMANDS)
1328     {
1329       /* We reached a blank line that separates the head from the body. */
1330       s->state = S_DO_REPEAT_2;
1331     }
1332   else if (*type == SEG_END_COMMAND || *type == SEG_START_COMMAND)
1333     {
1334       /* We reached the body. */
1335       s->state = S_DO_REPEAT_3;
1336       s->substate = 1;
1337     }
1338
1339   return ofs;
1340 }
1341
1342 /* We are segmenting a DO REPEAT command, currently reading a blank line that
1343    separates the head from the body. */
1344 static int
1345 segmenter_parse_do_repeat_2__ (struct segmenter *s,
1346                                const char *input, size_t n, bool eof,
1347                                enum segment_type *type)
1348 {
1349   int ofs = segmenter_subparse (s, input, n, eof, type);
1350   if (ofs < 0)
1351     return -1;
1352
1353   if (*type == SEG_NEWLINE)
1354     {
1355       /* We reached the body. */
1356       s->state = S_DO_REPEAT_3;
1357       s->substate = 1;
1358     }
1359
1360   return ofs;
1361 }
1362
1363 static bool
1364 check_repeat_command (struct segmenter *s,
1365                       const char *input, size_t n, bool eof)
1366 {
1367   int direction;
1368   char id[16];
1369   int ofs;
1370
1371   ofs = 0;
1372   if (input[ofs] == '+' || input[ofs] == '-')
1373     ofs++;
1374
1375   ofs = next_id_in_command (s, input, n, eof, ofs, id, sizeof id);
1376   if (ofs < 0)
1377     return false;
1378   else if (lex_id_match (ss_cstr ("DO"), ss_cstr (id)))
1379     direction = 1;
1380   else if (lex_id_match (ss_cstr ("END"), ss_cstr (id)))
1381     direction = -1;
1382   else
1383     return true;
1384
1385   ofs = next_id_in_command (s, input, n, eof, ofs, id, sizeof id);
1386   if (ofs < 0)
1387     return false;
1388
1389   if (lex_id_match (ss_cstr ("REPEAT"), ss_cstr (id)))
1390     s->substate += direction;
1391   return true;
1392 }
1393
1394 static int
1395 segmenter_parse_full_line__ (const char *input, size_t n, bool eof,
1396                              enum segment_type *type)
1397 {
1398   const char *newline = memchr (input, '\n', n);
1399   if (!newline)
1400     return eof ? n : -1;
1401
1402   ptrdiff_t ofs = newline - input;
1403   if (ofs == 0 || (ofs == 1 && input[0] == '\r'))
1404     {
1405       *type = SEG_NEWLINE;
1406       return ofs + 1;
1407     }
1408   else
1409     return ofs - (input[ofs - 1] == '\r');
1410 }
1411
1412 /* We are in the body of DO REPEAT, segmenting the lines of syntax that are to
1413    be repeated.  Report each line of syntax as a single SEG_DO_REPEAT_COMMAND.
1414
1415    DO REPEAT can be nested, so we look for DO REPEAT...END REPEAT blocks inside
1416    the lines we're segmenting.  s->substate counts the nesting level, starting
1417    at 1. */
1418 static int
1419 segmenter_parse_do_repeat_3__ (struct segmenter *s,
1420                                const char *input, size_t n, bool eof,
1421                                enum segment_type *type)
1422 {
1423   int ofs;
1424
1425   ofs = segmenter_parse_full_line__ (input, n, eof, type);
1426   if (ofs < 0 || (ofs > 0 && input[ofs - 1] == '\n'))
1427     return ofs;
1428   else if (!check_repeat_command (s, input, n, eof) && !eof)
1429     return -1;
1430   else if (s->substate == 0)
1431     {
1432       /* Nesting level dropped to 0, so we've finished reading the DO REPEAT
1433          body. */
1434       s->state = S_GENERAL;
1435       s->substate = SS_START_OF_COMMAND | SS_START_OF_LINE;
1436       return segmenter_push (s, input, n, eof, type);
1437     }
1438   else
1439     {
1440       *type = SEG_DO_REPEAT_COMMAND;
1441       return ofs;
1442     }
1443 }
1444
1445 /* We are segmenting a DEFINE command, which consists of:
1446
1447   - The DEFINE keyword.
1448
1449   - Anything but "(".
1450
1451   - "(" followed by a sequence of tokens possibly including balanced parentheses
1452     up to a final ")".
1453
1454   - A sequence of any number of lines, one string per line, ending with
1455     "!ENDDEFINE".  The first line is usually blank (that is, a newline follows
1456     the "(").  The last line usually just has "!ENDDEFINE." on it, but it can
1457     start with other tokens.  The whole DEFINE...!ENDDEFINE can be on a single
1458     line, even.
1459    */
1460 static int
1461 segmenter_parse_define_1__ (struct segmenter *s,
1462                             const char *input, size_t n, bool eof,
1463                             enum segment_type *type)
1464 {
1465   int ofs = segmenter_subparse (s, input, n, eof, type);
1466   if (ofs < 0)
1467     return -1;
1468
1469   if (*type == SEG_SEPARATE_COMMANDS
1470       || *type == SEG_END_COMMAND
1471       || *type == SEG_START_COMMAND)
1472     {
1473       /* The DEFINE command is malformed because we reached its end without
1474          ever hitting a "(" token.  Transition back to general parsing. */
1475       s->state = S_GENERAL;
1476       return ofs;
1477     }
1478   else if (*type == SEG_PUNCT && input[0] == '(')
1479     {
1480       s->state = S_DEFINE_2;
1481       s->nest = 1;
1482       return ofs;
1483     }
1484
1485   return ofs;
1486 }
1487
1488 static int
1489 segmenter_parse_define_2__ (struct segmenter *s,
1490                             const char *input, size_t n, bool eof,
1491                             enum segment_type *type)
1492 {
1493   int ofs = segmenter_subparse (s, input, n, eof, type);
1494   if (ofs < 0)
1495     return -1;
1496
1497   if (*type == SEG_SEPARATE_COMMANDS
1498       || *type == SEG_END_COMMAND
1499       || *type == SEG_START_COMMAND)
1500     {
1501       /* The DEFINE command is malformed because we reached its end before
1502          closing the set of parentheses.  Transition back to general
1503          parsing. */
1504       s->state = S_GENERAL;
1505       return ofs;
1506     }
1507   else if (*type == SEG_PUNCT && input[0] == '(')
1508     {
1509       s->nest++;
1510       return ofs;
1511     }
1512   else if (*type == SEG_PUNCT && input[0] == ')')
1513     {
1514       s->nest--;
1515       if (!s->nest)
1516         {
1517           s->state = S_DEFINE_3;
1518           s->substate = 0;
1519         }
1520       return ofs;
1521     }
1522
1523   return ofs;
1524 }
1525
1526 static size_t
1527 find_enddefine (struct substring input)
1528 {
1529   size_t n = input.length;
1530   const struct substring enddefine = ss_cstr ("!ENDDEFINE");
1531   for (size_t i = 0; i + enddefine.length <= n; i++)
1532     if (input.string[i] == '!'
1533         && ss_equals_case (ss_substr (input, i, enddefine.length), enddefine))
1534       return i;
1535   return SIZE_MAX;
1536 }
1537
1538 /* We are in the body of a macro definition, looking for additional lines of
1539    the body or !ENDDEFINE. */
1540 static int
1541 segmenter_parse_define_3__ (struct segmenter *s,
1542                             const char *input, size_t n, bool eof,
1543                             enum segment_type *type)
1544 {
1545   /* Gather a whole line. */
1546   const char *newline = memchr (input, '\n', n);
1547   int ofs = (newline ? newline - input - (newline > input && newline[-1] == '\r')
1548              : eof ? n
1549              : -1);
1550   if (ofs < 0)
1551     return -1;
1552
1553   /* Does the line contain !ENDDEFINE? */
1554   size_t end = find_enddefine (ss_buffer (input, ofs));
1555   if (end == SIZE_MAX)
1556     {
1557       /* No !ENDDEFINE.  We have a full line of macro body.
1558
1559          The line might be blank, whether completely empty or just spaces and
1560          comments.  That's OK: we need to report blank lines because they can
1561          have significance.
1562
1563          However, if the first line of the macro body (the same line as the
1564          closing parenthesis in the argument definition) is blank, we just
1565          report it as spaces because it's not significant. */
1566       *type = (s->substate == 0 && is_all_spaces (input, ofs)
1567                ? SEG_SPACES : SEG_MACRO_BODY);
1568       s->state = S_DEFINE_4;
1569       s->substate = 1;
1570       return ofs;
1571     }
1572   else
1573     {
1574       /* Macro ends at the !ENDDEFINE on this line. */
1575       s->state = S_GENERAL;
1576       s->substate = 0;
1577       if (!end)
1578         {
1579           /* Line starts with !ENDDEFINE. */
1580           return segmenter_push (s, input, n, eof, type);
1581         }
1582       else
1583         {
1584           if (is_all_spaces (input, end))
1585             {
1586               /* Line starts with spaces followed by !ENDDEFINE. */
1587               *type = SEG_SPACES;
1588             }
1589           else
1590             {
1591               /* Line starts with some content followed by !ENDDEFINE. */
1592               *type = SEG_MACRO_BODY;
1593             }
1594           return end;
1595         }
1596     }
1597 }
1598
1599 static int
1600 segmenter_parse_define_4__ (struct segmenter *s,
1601                             const char *input, size_t n, bool eof,
1602                             enum segment_type *type)
1603 {
1604   int ofs = segmenter_parse_newline__ (input, n, eof, type);
1605   if (ofs < 0)
1606     return -1;
1607
1608   s->state = S_DEFINE_3;
1609   return ofs;
1610 }
1611
1612 static int
1613 segmenter_parse_begin_data_1__ (struct segmenter *s,
1614                                 const char *input, size_t n, bool eof,
1615                                 enum segment_type *type)
1616 {
1617   int ofs = segmenter_subparse (s, input, n, eof, type);
1618   if (ofs < 0)
1619     return -1;
1620
1621   if (*type == SEG_NEWLINE)
1622     s->state = S_BEGIN_DATA_2;
1623
1624   return ofs;
1625 }
1626
1627 static int
1628 segmenter_parse_begin_data_2__ (struct segmenter *s,
1629                                 const char *input, size_t n, bool eof,
1630                                 enum segment_type *type)
1631 {
1632   int ofs = segmenter_subparse (s, input, n, eof, type);
1633   if (ofs < 0)
1634     return -1;
1635
1636   if (*type == SEG_NEWLINE)
1637     s->state = S_BEGIN_DATA_3;
1638
1639   return ofs;
1640 }
1641
1642 static bool
1643 is_end_data (const char *input, size_t n)
1644 {
1645   const uint8_t *u_input = CHAR_CAST (const uint8_t *, input);
1646   bool endcmd;
1647   ucs4_t uc;
1648   int mblen;
1649   int ofs;
1650
1651   if (n < 4 || c_strncasecmp (input, "END", 3))
1652     return false;
1653
1654   ofs = 3;
1655   mblen = u8_mbtouc (&uc, u_input + ofs, n - ofs);
1656   if (!lex_uc_is_space (uc))
1657     return false;
1658   ofs += mblen;
1659
1660   if (n - ofs < 4 || c_strncasecmp (input + ofs, "DATA", 4))
1661     return false;
1662   ofs += 4;
1663
1664   endcmd = false;
1665   while (ofs < n)
1666     {
1667       mblen = u8_mbtouc (&uc, u_input + ofs, n - ofs);
1668       if (uc == '.')
1669         {
1670           if (endcmd)
1671             return false;
1672           endcmd = true;
1673         }
1674       else if (!lex_uc_is_space (uc))
1675         return false;
1676       ofs += mblen;
1677     }
1678
1679   return true;
1680 }
1681
1682 static int
1683 segmenter_parse_begin_data_3__ (struct segmenter *s,
1684                                 const char *input, size_t n, bool eof,
1685                                 enum segment_type *type)
1686 {
1687   int ofs;
1688
1689   ofs = segmenter_parse_full_line__ (input, n, eof, type);
1690   if (ofs < 0)
1691     return -1;
1692   else if (is_end_data (input, ofs))
1693     {
1694       s->state = S_GENERAL;
1695       s->substate = SS_START_OF_COMMAND | SS_START_OF_LINE;
1696       return segmenter_push (s, input, n, eof, type);
1697     }
1698   else
1699     {
1700       *type = SEG_INLINE_DATA;
1701       s->state = S_BEGIN_DATA_4;
1702       return input[ofs - 1] == '\n' ? 0 : ofs;
1703     }
1704 }
1705
1706 static int
1707 segmenter_parse_begin_data_4__ (struct segmenter *s,
1708                                 const char *input, size_t n, bool eof,
1709                                 enum segment_type *type)
1710 {
1711   int ofs;
1712
1713   ofs = segmenter_parse_newline__ (input, n, eof, type);
1714   if (ofs < 0)
1715     return -1;
1716
1717   s->state = S_BEGIN_DATA_3;
1718   return ofs;
1719 }
1720
1721 static int
1722 segmenter_parse_title_1__ (struct segmenter *s,
1723                            const char *input, size_t n, bool eof,
1724                            enum segment_type *type)
1725 {
1726   int ofs;
1727
1728   ofs = skip_spaces (input, n, eof, 0);
1729   if (ofs < 0)
1730     return -1;
1731   s->state = S_TITLE_2;
1732   *type = SEG_SPACES;
1733   return ofs;
1734 }
1735
1736 static int
1737 segmenter_parse_title_2__ (struct segmenter *s,
1738                            const char *input, size_t n, bool eof,
1739                            enum segment_type *type)
1740 {
1741   int endcmd;
1742   int ofs;
1743
1744   endcmd = -1;
1745   ofs = 0;
1746   while (ofs < n)
1747     {
1748       ucs4_t uc;
1749       int mblen;
1750
1751       mblen = segmenter_u8_to_uc__ (&uc, input, n, eof, ofs);
1752       if (mblen < 0)
1753         return -1;
1754
1755       switch (uc)
1756         {
1757         case '\n':
1758           goto end_of_line;
1759
1760         case '.':
1761           endcmd = ofs;
1762           break;
1763
1764         default:
1765           if (!lex_uc_is_space (uc))
1766             endcmd = -1;
1767           break;
1768         }
1769
1770       ofs += mblen;
1771     }
1772
1773   if (eof)
1774     {
1775     end_of_line:
1776       s->state = S_GENERAL;
1777       s->substate = 0;
1778       *type = SEG_UNQUOTED_STRING;
1779       return endcmd >= 0 ? endcmd : ofs;
1780     }
1781
1782   return -1;
1783 }
1784
1785 /* Returns the name of segment TYPE as a string.  The caller must not modify
1786    or free the returned string.
1787
1788    This is useful only for debugging and testing. */
1789 const char *
1790 segment_type_to_string (enum segment_type type)
1791 {
1792   switch (type)
1793     {
1794 #define SEG_TYPE(NAME) case SEG_##NAME: return #NAME;
1795       SEG_TYPES
1796 #undef SEG_TYPE
1797     default:
1798       return "unknown segment type";
1799     }
1800 }
1801
1802 /* Returns a segmenter with the given syntax MODE.
1803
1804    If IS_SNIPPET is false, then the segmenter will parse as if it's being given
1805    a whole file.  This means, for example, that it will interpret - or + at the
1806    beginning of the syntax as a separator between commands (since - or + at the
1807    beginning of a line has this meaning).
1808
1809    If IS_SNIPPET is true, then the segmenter will parse as if it's being given
1810    an isolated piece of syntax.  This means that, for example, that it will
1811    interpret - or + at the beginning of the syntax as an operator token or (if
1812    followed by a digit) as part of a number.
1813
1814    A segmenter does not contain any external references, so nothing needs to be
1815    done to destroy one.  For the same reason, segmenters may be copied with
1816    plain struct assignment (or memcpy). */
1817 struct segmenter
1818 segmenter_init (enum segmenter_mode mode, bool is_snippet)
1819 {
1820   return (struct segmenter) {
1821     .state = is_snippet ? S_GENERAL : S_SHBANG,
1822     .mode = mode,
1823   };
1824 }
1825
1826 /* Returns the mode passed to segmenter_init() for S. */
1827 enum segmenter_mode
1828 segmenter_get_mode (const struct segmenter *s)
1829 {
1830   return s->mode;
1831 }
1832
1833 /* Attempts to label a prefix of S's remaining input with a segment type.  The
1834    caller supplies the first N bytes of the remaining input as INPUT, which
1835    must be a UTF-8 encoded string.  If EOF is true, then the N bytes supplied
1836    are the entire (remainder) of the input; if EOF is false, then further input
1837    is potentially available.
1838
1839    The input may contain '\n' or '\r\n' line ends in any combination.
1840
1841    If successful, returns the number of bytes in the segment at the beginning
1842    of INPUT (between 0 and N, inclusive) and stores the type of that segment
1843    into *TYPE.  The next call to segmenter_push() should not include those
1844    bytes as part of INPUT, because they have (figuratively) been consumed by
1845    the segmenter.
1846
1847    Failure occurs only if the segment type of the N bytes in INPUT cannot yet
1848    be determined.  In this case segmenter_push() returns -1.  If more input is
1849    available, the caller should obtain some more, then call again with a larger
1850    N.  If this is not enough, the process might need to repeat again and agin.
1851    If input is exhausted, then the caller may call again setting EOF to true.
1852    segmenter_push() will never return -1 when EOF is true.
1853
1854    The caller must not, in a sequence of calls, supply contradictory input.
1855    That is, bytes provided as part of INPUT in one call, but not consumed, must
1856    not be provided with *different* values on subsequent calls.  This is
1857    because segmenter_push() must often make decisions based on looking ahead
1858    beyond the bytes that it consumes. */
1859 int
1860 segmenter_push (struct segmenter *s, const char *input, size_t n, bool eof,
1861                 enum segment_type *type)
1862 {
1863   if (!n)
1864     {
1865       if (eof)
1866         {
1867           *type = SEG_END;
1868           return 0;
1869         }
1870       else
1871         return -1;
1872     }
1873
1874   switch (s->state)
1875     {
1876     case S_SHBANG:
1877       return segmenter_parse_shbang__ (s, input, n, eof, type);
1878
1879     case S_GENERAL:
1880       return (s->substate & SS_START_OF_LINE
1881               ? segmenter_parse_start_of_line__ (s, input, n, eof, type)
1882               : segmenter_parse_mid_command__ (s, input, n, eof, type));
1883
1884     case S_COMMENT_1:
1885       return segmenter_parse_comment_1__ (s, input, n, eof, type);
1886     case S_COMMENT_2:
1887       return segmenter_parse_comment_2__ (s, input, n, eof, type);
1888
1889     case S_DOCUMENT_1:
1890       return segmenter_parse_document_1__ (s, input, n, eof, type);
1891     case S_DOCUMENT_2:
1892       return segmenter_parse_document_2__ (s, input, n, eof, type);
1893     case S_DOCUMENT_3:
1894       return segmenter_parse_document_3__ (s, type);
1895
1896     case S_FILE_LABEL:
1897       return segmenter_parse_file_label__ (s, input, n, eof, type);
1898
1899     case S_DO_REPEAT_1:
1900       return segmenter_parse_do_repeat_1__ (s, input, n, eof, type);
1901     case S_DO_REPEAT_2:
1902       return segmenter_parse_do_repeat_2__ (s, input, n, eof, type);
1903     case S_DO_REPEAT_3:
1904       return segmenter_parse_do_repeat_3__ (s, input, n, eof, type);
1905
1906     case S_DEFINE_1:
1907       return segmenter_parse_define_1__ (s, input, n, eof, type);
1908     case S_DEFINE_2:
1909       return segmenter_parse_define_2__ (s, input, n, eof, type);
1910     case S_DEFINE_3:
1911       return segmenter_parse_define_3__ (s, input, n, eof, type);
1912     case S_DEFINE_4:
1913       return segmenter_parse_define_4__ (s, input, n, eof, type);
1914
1915     case S_BEGIN_DATA_1:
1916       return segmenter_parse_begin_data_1__ (s, input, n, eof, type);
1917     case S_BEGIN_DATA_2:
1918       return segmenter_parse_begin_data_2__ (s, input, n, eof, type);
1919     case S_BEGIN_DATA_3:
1920       return segmenter_parse_begin_data_3__ (s, input, n, eof, type);
1921     case S_BEGIN_DATA_4:
1922       return segmenter_parse_begin_data_4__ (s, input, n, eof, type);
1923
1924     case S_TITLE_1:
1925       return segmenter_parse_title_1__ (s, input, n, eof, type);
1926     case S_TITLE_2:
1927       return segmenter_parse_title_2__ (s, input, n, eof, type);
1928     }
1929
1930   NOT_REACHED ();
1931 }
1932
1933 /* Returns the style of command prompt to display to an interactive user for
1934    input in S.  The return value is most accurate in mode SEG_MODE_INTERACTIVE
1935    and at the beginning of a line (that is, if segmenter_push() consumed as
1936    much as possible of the input up to a new-line).  */
1937 enum prompt_style
1938 segmenter_get_prompt (const struct segmenter *s)
1939 {
1940   switch (s->state)
1941     {
1942     case S_SHBANG:
1943       return PROMPT_FIRST;
1944
1945     case S_GENERAL:
1946       return s->substate & SS_START_OF_COMMAND ? PROMPT_FIRST : PROMPT_LATER;
1947
1948     case S_COMMENT_1:
1949     case S_COMMENT_2:
1950       return PROMPT_COMMENT;
1951
1952     case S_DOCUMENT_1:
1953     case S_DOCUMENT_2:
1954       return PROMPT_DOCUMENT;
1955     case S_DOCUMENT_3:
1956       return PROMPT_FIRST;
1957
1958     case S_FILE_LABEL:
1959       return PROMPT_LATER;
1960
1961     case S_DO_REPEAT_1:
1962     case S_DO_REPEAT_2:
1963       return s->substate & SS_START_OF_COMMAND ? PROMPT_FIRST : PROMPT_LATER;
1964     case S_DO_REPEAT_3:
1965       return PROMPT_DO_REPEAT;
1966
1967     case S_DEFINE_1:
1968     case S_DEFINE_2:
1969       return s->substate & SS_START_OF_COMMAND ? PROMPT_FIRST : PROMPT_LATER;
1970     case S_DEFINE_3:
1971     case S_DEFINE_4:
1972       return PROMPT_DEFINE;
1973
1974     case S_BEGIN_DATA_1:
1975       return PROMPT_FIRST;
1976     case S_BEGIN_DATA_2:
1977       return PROMPT_LATER;
1978     case S_BEGIN_DATA_3:
1979     case S_BEGIN_DATA_4:
1980       return PROMPT_DATA;
1981
1982     case S_TITLE_1:
1983     case S_TITLE_2:
1984       return PROMPT_FIRST;
1985     }
1986
1987   NOT_REACHED ();
1988 }