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