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