segment: New library for low-level phase of lexical syntax analysis.
[pspp-builds.git] / 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 = 1;
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
447   if (new_cmd)
448     {
449       s->state = S_GENERAL;
450       s->substate = SS_START_OF_LINE | SS_START_OF_COMMAND;
451     }
452   else
453     s->state = S_COMMENT_1;
454   return ofs;
455 }
456
457 static int
458 segmenter_parse_document_1__ (struct segmenter *s, const char *input, size_t n,
459                               enum segment_type *type)
460 {
461   bool end_cmd;
462   int ofs;
463
464   end_cmd = false;
465   ofs = 0;
466   while (ofs < n)
467     {
468       ucs4_t uc;
469       int mblen;
470
471       mblen = segmenter_u8_to_uc__ (&uc, input + ofs, n - ofs);
472       if (mblen < 0)
473         return -1;
474
475       switch (uc)
476         {
477         case '.':
478           end_cmd = true;
479           break;
480
481         case '\n':
482           if (ofs > 1 && input[ofs - 1] == '\r')
483             ofs--;
484
485           *type = SEG_DOCUMENT;
486           s->state = end_cmd ? S_DOCUMENT_3 : S_DOCUMENT_2;
487           return ofs;
488
489         default:
490           if (!lex_uc_is_space (uc))
491             end_cmd = false;
492           break;
493         }
494
495       ofs += mblen;
496     }
497   return -1;
498 }
499
500 static int
501 segmenter_parse_document_2__ (struct segmenter *s, const char *input, size_t n,
502                               enum segment_type *type)
503 {
504   int ofs;
505
506   ofs = segmenter_parse_newline__ (input, n, type);
507   if (ofs < 0)
508     return -1;
509
510   s->state = S_DOCUMENT_1;
511   return ofs;
512 }
513
514 static int
515 segmenter_parse_document_3__ (struct segmenter *s, enum segment_type *type)
516 {
517   *type = SEG_END_COMMAND;
518   s->state = S_GENERAL;
519   s->substate = SS_START_OF_COMMAND | SS_START_OF_LINE;
520   return 0;
521 }
522
523 static int
524 segmenter_unquoted (const char *input, size_t n, int ofs)
525
526 {
527   char c;
528
529   ofs = skip_spaces_and_comments (input, n, ofs);
530   if (ofs < 0)
531     return -1;
532
533   c = input[ofs];
534   return c != '\'' && c != '"' && c != '\n' && c != '\0';
535 }
536
537 static int
538 next_id_in_command (const struct segmenter *s, const char *input, size_t n,
539                     int ofs, char id[], size_t id_size)
540 {
541   struct segmenter sub;
542
543   assert (id_size > 0);
544
545   sub.mode = s->mode;
546   sub.state = S_GENERAL;
547   sub.substate = 0;
548   for (;;)
549     {
550       enum segment_type type;
551       int retval;
552
553       retval = segmenter_push (&sub, input + ofs, n - ofs, &type);
554       if (retval < 0)
555         {
556           id[0] = '\0';
557           return -1;
558         }
559
560       switch (type)
561         {
562         case SEG_SHBANG:
563         case SEG_SPACES:
564         case SEG_COMMENT:
565         case SEG_NEWLINE:
566           break;
567
568         case SEG_IDENTIFIER:
569           if (retval < id_size)
570             {
571               memcpy (id, input + ofs, retval);
572               id[retval] = '\0';
573               return ofs + retval;
574             }
575           /* fall through */
576
577         case SEG_NUMBER:
578         case SEG_QUOTED_STRING:
579         case SEG_HEX_STRING:
580         case SEG_UNICODE_STRING:
581         case SEG_UNQUOTED_STRING:
582         case SEG_RESERVED_WORD:
583         case SEG_PUNCT:
584         case SEG_COMMENT_COMMAND:
585         case SEG_DO_REPEAT_COMMAND:
586         case SEG_INLINE_DATA:
587         case SEG_START_DOCUMENT:
588         case SEG_DOCUMENT:
589         case SEG_START_COMMAND:
590         case SEG_SEPARATE_COMMANDS:
591         case SEG_END_COMMAND:
592         case SEG_END:
593         case SEG_EXPECTED_QUOTE:
594         case SEG_EXPECTED_EXPONENT:
595         case SEG_UNEXPECTED_DOT:
596         case SEG_UNEXPECTED_CHAR:
597           id[0] = '\0';
598           return ofs + retval;
599
600         case SEG_N_TYPES:
601           NOT_REACHED ();
602         }
603       ofs += retval;
604     }
605 }
606
607 static int
608 segmenter_parse_id__ (struct segmenter *s, const char *input, size_t n,
609                       enum segment_type *type)
610 {
611   ucs4_t uc;
612   int ofs;
613
614   assert (s->state == S_GENERAL);
615
616   ofs = u8_mbtouc (&uc, CHAR_CAST (const uint8_t *, input), n);
617   for (;;)
618     {
619       int mblen;
620
621       if (ofs >= n)
622         return -1;
623
624       mblen = segmenter_u8_to_uc__ (&uc, input + ofs, n - ofs);
625       if (mblen < 0)
626         return -1;
627       else if (!lex_uc_is_idn (uc))
628         break;
629
630       ofs += mblen;
631     }
632
633   if (input[ofs - 1] == '.')
634     {
635       int eol = at_end_of_line (input, n, ofs);
636       if (eol < 0)
637         return -1;
638       else if (eol)
639         ofs--;
640     }
641
642   if (is_reserved_word (input, ofs))
643     *type = SEG_RESERVED_WORD;
644   else
645     *type = SEG_IDENTIFIER;
646
647   if (s->substate & SS_START_OF_COMMAND)
648     {
649       struct substring word = ss_buffer (input, ofs);
650
651       if (lex_id_match_n (ss_cstr ("COMMENT"), word, 4))
652         {
653           s->state = S_COMMENT_1;
654           return segmenter_parse_comment_1__ (s, input, n, type);
655         }
656       else if (lex_id_match (ss_cstr ("DOCUMENT"), word))
657         {
658           s->state = S_DOCUMENT_1;
659           *type = SEG_START_DOCUMENT;
660           return 0;
661         }
662       else if (lex_id_match (ss_cstr ("TITLE"), word)
663                || lex_id_match (ss_cstr ("SUBTITLE"), word))
664         {
665           int result = segmenter_unquoted (input, n, ofs);
666           if (result < 0)
667             return -1;
668           else if (result)
669             {
670               s->state = S_TITLE_1;
671               return ofs;
672             }
673         }
674       else if (lex_id_match (ss_cstr ("FILE"), word))
675         {
676           char id[16];
677
678           if (next_id_in_command (s, input, n, ofs, id, sizeof id) < 0)
679             return -1;
680           else if (lex_id_match (ss_cstr ("LABEL"), ss_cstr (id)))
681             {
682               s->state = S_FILE_LABEL;
683               s->substate = 0;
684               return ofs;
685             }
686         }
687       else if (lex_id_match (ss_cstr ("DO"), word))
688         {
689           char id[16];
690
691           if (next_id_in_command (s, input, n, ofs, id, sizeof id) < 0)
692             return -1;
693           else if (lex_id_match (ss_cstr ("REPEAT"), ss_cstr (id)))
694             {
695               s->state = S_DO_REPEAT_1;
696               s->substate = 0;
697               return ofs;
698             }
699         }
700       else if (lex_id_match (ss_cstr ("BEGIN"), word))
701         {
702           char id[16];
703           int ofs2;
704
705           ofs2 = next_id_in_command (s, input, n, ofs, id, sizeof id);
706           if (ofs2 < 0)
707             return -1;
708           else if (lex_id_match (ss_cstr ("DATA"), ss_cstr (id)))
709             {
710               int eol;
711
712               ofs2 = skip_spaces_and_comments (input, n, ofs2);
713               if (ofs2 < 0)
714                 return -1;
715
716               if (input[ofs2] == '.')
717                 {
718                   ofs2 = skip_spaces_and_comments (input, n, ofs2 + 1);
719                   if (ofs2 < 0)
720                     return -1;
721                 }
722
723               eol = is_end_of_line (input, n, ofs2);
724               if (eol < 0)
725                 return -1;
726               else if (eol)
727                 {
728                   if (memchr (input, '\n', ofs2))
729                     s->state = S_BEGIN_DATA_1;
730                   else
731                     s->state = S_BEGIN_DATA_2;
732                   s->substate = 0;
733                   return ofs;
734                 }
735             }
736         }
737     }
738
739   s->substate = 0;
740   return ofs;
741 }
742
743 static int
744 segmenter_parse_string__ (enum segment_type string_type,
745                           int ofs, struct segmenter *s,
746                           const char *input, size_t n, enum segment_type *type)
747 {
748   int quote = input[ofs];
749
750   ofs++;
751   while (ofs < n)
752     if (input[ofs] == quote)
753       {
754         ofs++;
755         if (ofs >= n)
756           return -1;
757         else if (input[ofs] == quote)
758           ofs++;
759         else
760           {
761             *type = string_type;
762             s->substate = 0;
763             return ofs;
764           }
765       }
766     else if (input[ofs] == '\n' || input[ofs] == '\0')
767       {
768         *type = SEG_EXPECTED_QUOTE;
769         s->substate = 0;
770         return ofs;
771       }
772     else
773       ofs++;
774
775   return -1;
776 }
777
778 static int
779 segmenter_maybe_parse_string__ (enum segment_type string_type,
780                                 struct segmenter *s,
781                                 const char *input, size_t n,
782                                 enum segment_type *type)
783 {
784   if (n < 2)
785     return -1;
786   else if (input[1] == '\'' || input[1] == '"')
787     return segmenter_parse_string__ (string_type, 1, s, input, n, type);
788   else
789     return segmenter_parse_id__ (s, input, n, type);
790 }
791
792 static int
793 segmenter_parse_mid_command__ (struct segmenter *s,
794                                const char *input, size_t n,
795                                enum segment_type *type)
796 {
797   ucs4_t uc;
798   int mblen;
799   int ofs;
800
801   assert (s->state == S_GENERAL);
802   assert (!(s->substate & SS_START_OF_LINE));
803
804   mblen = segmenter_u8_to_uc__ (&uc, input, n);
805   if (mblen < 0)
806     return -1;
807
808   switch (uc)
809     {
810     case '\n':
811       s->substate |= SS_START_OF_LINE;
812       *type = SEG_NEWLINE;
813       return 1;
814
815     case '/':
816       if (n == 1)
817         return -1;
818       else if (input[1] == '*')
819         {
820           ofs = skip_comment (input, n, 2);
821           if (ofs < 0)
822             return -1;
823
824           *type = SEG_COMMENT;
825           return ofs;
826         }
827       else
828         {
829           s->substate = 0;
830           *type = SEG_PUNCT;
831           return 1;
832         }
833
834     case '(': case ')': case ',': case '=': case '-':
835     case '[': case ']': case '&': case '|': case '+':
836       *type = SEG_PUNCT;
837       s->substate = 0;
838       return 1;
839
840     case '*':
841       if (s->substate & SS_START_OF_COMMAND)
842         {
843           /* '*' at the beginning of a command begins a comment. */
844           s->state = S_COMMENT_1;
845           return segmenter_parse_comment_1__ (s, input, n, type);
846         }
847       else
848         return segmenter_parse_digraph__ ("*", s, input, n, type);
849
850     case '<':
851       return segmenter_parse_digraph__ ("=>", s, input, n, type);
852
853     case '>':
854       return segmenter_parse_digraph__ ("=", s, input, n, type);
855
856     case '~':
857       return segmenter_parse_digraph__ ("=", s, input, n, type);
858
859     case '.':
860       if (n < 2)
861         return -1;
862       else if (c_isdigit (input[1]))
863         return segmenter_parse_number__ (s, input, n, type);
864       else
865         {
866           int eol = at_end_of_line (input, n, 1);
867           if (eol < 0)
868             return -1;
869
870           if (eol)
871             {
872               *type = SEG_END_COMMAND;
873               s->substate = SS_START_OF_COMMAND;
874             }
875           else
876             *type = SEG_UNEXPECTED_DOT;
877           return 1;
878         }
879       NOT_REACHED ();
880
881     case '0': case '1': case '2': case '3': case '4':
882     case '5': case '6': case '7': case '8': case '9':
883       return segmenter_parse_number__ (s, input, n, type);
884
885     case 'u': case 'U':
886       return segmenter_maybe_parse_string__ (SEG_UNICODE_STRING,
887                                            s, input, n, type);
888
889     case 'x': case 'X':
890       return segmenter_maybe_parse_string__ (SEG_HEX_STRING,
891                                              s, input, n, type);
892
893     case '\'': case '"':
894       return segmenter_parse_string__ (SEG_QUOTED_STRING, 0,
895                                        s, input, n, type);
896
897     default:
898       if (lex_uc_is_space (uc))
899         {
900           ofs = skip_spaces (input, n, mblen);
901           if (ofs < 0)
902             return -1;
903
904           if (input[ofs - 1] == '\r' && input[ofs] == '\n')
905             {
906               if (ofs == 1)
907                 {
908                   s->substate |= SS_START_OF_LINE;
909                   *type = SEG_NEWLINE;
910                   return 2;
911                 }
912               else
913                 ofs--;
914             }
915           *type = SEG_SPACES;
916           return ofs;
917         }
918       else if (lex_uc_is_id1 (uc))
919         return segmenter_parse_id__ (s, input, n, type);
920       else
921         {
922           *type = SEG_UNEXPECTED_CHAR;
923           s->substate = 0;
924           return mblen;
925         }
926     }
927 }
928
929 static int
930 compare_commands (const void *a_, const void *b_)
931 {
932   const char *const *ap = a_;
933   const char *const *bp = b_;
934   const char *a = *ap;
935   const char *b = *bp;
936
937   return c_strcasecmp (a, b);
938 }
939
940 static const char **
941 segmenter_get_command_name_candidates (unsigned char first)
942 {
943 #define DEF_CMD(STATES, FLAGS, NAME, FUNCTION) NAME,
944 #define UNIMPL_CMD(NAME, DESCRIPTION) NAME,
945   static const char *commands[] =
946     {
947 #include "language/command.def"
948       ""
949     };
950   static size_t n_commands = (sizeof commands / sizeof *commands) - 1;
951 #undef DEF_CMD
952 #undef UNIMPL_CMD
953
954   static bool inited;
955
956   static const char **cindex[UCHAR_MAX + 1];
957
958   if (!inited)
959     {
960       size_t i;
961
962       inited = true;
963
964       qsort (commands, n_commands, sizeof *commands, compare_commands);
965       for (i = 0; i < n_commands; i++)
966         {
967           unsigned char c = c_toupper (commands[i][0]);
968           if (cindex[c] == NULL)
969             cindex[c] = &commands[i];
970         }
971       for (i = 0; i <= UCHAR_MAX; i++)
972         if (cindex[i] == NULL)
973           cindex[i] = &commands[n_commands];
974     }
975
976   return cindex[c_toupper (first)];
977 }
978
979 static int
980 segmenter_detect_command_name__ (const char *input, size_t n, int ofs)
981 {
982   const char **commands;
983
984   input += ofs;
985   n -= ofs;
986   ofs = 0;
987   for (;;)
988     {
989       ucs4_t uc;
990       int mblen;
991
992       if (ofs >= n)
993         return -1;
994
995       mblen = segmenter_u8_to_uc__ (&uc, input + ofs, n - ofs);
996       if (mblen < 0)
997         return -1;
998
999       if (uc == '\n'
1000           || !(lex_uc_is_space (uc) || lex_uc_is_idn (uc) || uc == '-'))
1001         break;
1002
1003       ofs += mblen;
1004     }
1005   if (input[ofs - 1] == '.')
1006     ofs--;
1007
1008   for (commands = segmenter_get_command_name_candidates (input[0]);
1009        c_toupper (input[0]) == c_toupper ((*commands)[0]);
1010        commands++)
1011     {
1012       int missing_words;
1013       bool exact;
1014
1015       if (command_match (ss_cstr (*commands), ss_buffer (input, ofs),
1016                          &exact, &missing_words)
1017           && missing_words <= 0)
1018         return 1;
1019     }
1020
1021   return 0;
1022 }
1023
1024 static int
1025 is_start_of_string__ (const char *input, size_t n, int ofs)
1026 {
1027   int c;
1028
1029   c = input[ofs];
1030   if (c == 'x' || c == 'X' || c == 'u' || c == 'U')
1031     {
1032       if (ofs + 1 >= n)
1033         return -1;
1034
1035       return input[ofs + 1] == '\'' || input[ofs + 1] == '"';
1036     }
1037   else
1038     return c == '\'' || c == '"' || c == '\n';
1039 }
1040
1041 static int
1042 segmenter_parse_start_of_line__ (struct segmenter *s,
1043                                  const char *input, size_t n,
1044                                  enum segment_type *type)
1045 {
1046   ucs4_t uc;
1047   int mblen;
1048   int ofs;
1049
1050   assert (s->state == S_GENERAL);
1051   assert (s->substate & SS_START_OF_LINE);
1052
1053   mblen = segmenter_u8_to_uc__ (&uc, input, n);
1054   if (mblen < 0)
1055     return -1;
1056
1057   switch (uc)
1058     {
1059     case '+':
1060       ofs = skip_spaces_and_comments (input, n, 1);
1061       if (ofs < 0)
1062         return -1;
1063       else
1064         {
1065           int is_string = is_start_of_string__ (input, n, ofs);
1066           if (is_string < 0)
1067             return -1;
1068           else if (is_string)
1069             {
1070               /* This is punctuation that may separate pieces of a string. */
1071               *type = SEG_PUNCT;
1072               s->substate = 0;
1073               return 1;
1074             }
1075         }
1076       /* Fall through. */
1077
1078     case '-':
1079     case '.':
1080       *type = SEG_START_COMMAND;
1081       s->substate = SS_START_OF_COMMAND;
1082       return 1;
1083
1084     default:
1085       if (lex_uc_is_space (uc))
1086         {
1087           int eol = at_end_of_line (input, n, 0);
1088           if (eol < 0)
1089             return -1;
1090           else if (eol)
1091             {
1092               s->substate = SS_START_OF_COMMAND;
1093               *type = SEG_SEPARATE_COMMANDS;
1094               return 0;
1095             }
1096           break;
1097         }
1098
1099       if (s->mode == SEG_MODE_INTERACTIVE || s->substate & SS_START_OF_COMMAND)
1100         break;
1101       else if (s->mode == SEG_MODE_AUTO)
1102         {
1103           int cmd = segmenter_detect_command_name__ (input, n, 0);
1104           if (cmd < 0)
1105             return -1;
1106           else if (cmd == 0)
1107             break;
1108         }
1109       else
1110         assert (s->mode == SEG_MODE_BATCH);
1111
1112       s->substate = SS_START_OF_COMMAND;
1113       *type = SEG_START_COMMAND;
1114       return 0;
1115     }
1116
1117   s->substate = SS_START_OF_COMMAND;
1118   return segmenter_parse_mid_command__ (s, input, n, type);
1119 }
1120
1121 static int
1122 segmenter_parse_file_label__ (struct segmenter *s,
1123                               const char *input, size_t n,
1124                               enum segment_type *type)
1125 {
1126   struct segmenter sub;
1127   int ofs;
1128
1129   sub = *s;
1130   sub.state = S_GENERAL;
1131   ofs = segmenter_push (&sub, input, n, type);
1132
1133   if (ofs < 0)
1134     return -1;
1135   else if (*type == SEG_IDENTIFIER)
1136     {
1137       int result;
1138
1139       assert (lex_id_match (ss_cstr ("LABEL"),
1140                             ss_buffer ((char *) input, ofs)));
1141       result = segmenter_unquoted (input, n, ofs);
1142       if (result < 0)
1143         return -1;
1144       else
1145         {
1146           if (result)
1147             s->state = S_TITLE_1;
1148           else
1149             *s = sub;
1150           return ofs;
1151         }
1152     }
1153   else
1154     {
1155       s->substate = sub.substate;
1156       return ofs;
1157     }
1158 }
1159
1160 static int
1161 segmenter_subparse (struct segmenter *s,
1162                     const char *input, size_t n, enum segment_type *type)
1163 {
1164   struct segmenter sub;
1165   int ofs;
1166
1167   sub.mode = s->mode;
1168   sub.state = S_GENERAL;
1169   sub.substate = s->substate;
1170   ofs = segmenter_push (&sub, input, n, type);
1171   s->substate = sub.substate;
1172   return ofs;
1173 }
1174
1175 static int
1176 segmenter_parse_do_repeat_1__ (struct segmenter *s,
1177                                const char *input, size_t n,
1178                                enum segment_type *type)
1179 {
1180   int ofs = segmenter_subparse (s, input, n, type);
1181   if (ofs < 0)
1182     return -1;
1183
1184   if (*type == SEG_START_COMMAND || *type == SEG_SEPARATE_COMMANDS)
1185     s->state = S_DO_REPEAT_2;
1186   else if (*type == SEG_END_COMMAND)
1187     {
1188       s->state = S_DO_REPEAT_3;
1189       s->substate = 1;
1190     }
1191
1192   return ofs;
1193 }
1194
1195 static int
1196 segmenter_parse_do_repeat_2__ (struct segmenter *s,
1197                                const char *input, size_t n,
1198                                enum segment_type *type)
1199 {
1200   int ofs = segmenter_subparse (s, input, n, type);
1201   if (ofs < 0)
1202     return -1;
1203
1204   if (*type == SEG_NEWLINE)
1205     {
1206       s->state = S_DO_REPEAT_3;
1207       s->substate = 1;
1208     }
1209
1210   return ofs;
1211 }
1212
1213 static bool
1214 check_repeat_command (struct segmenter *s,
1215                       const char *input, size_t n)
1216 {
1217   int direction;
1218   char id[16];
1219   int ofs;
1220
1221   ofs = 0;
1222   if (input[ofs] == '+' || input[ofs] == '-')
1223     ofs++;
1224
1225   ofs = next_id_in_command (s, input, n, ofs, id, sizeof id);
1226   if (ofs < 0)
1227     return false;
1228   else if (lex_id_match (ss_cstr ("DO"), ss_cstr (id)))
1229     direction = 1;
1230   else if (lex_id_match (ss_cstr ("END"), ss_cstr (id)))
1231     direction = -1;
1232   else
1233     return true;
1234
1235   ofs = next_id_in_command (s, input, n, ofs, id, sizeof id);
1236   if (ofs < 0)
1237     return false;
1238
1239   if (lex_id_match (ss_cstr ("REPEAT"), ss_cstr (id)))
1240     s->substate += direction;
1241   return true;
1242 }
1243
1244 static int
1245 segmenter_parse_full_line__ (const char *input, size_t n,
1246                              enum segment_type *type)
1247 {
1248   const char *newline = memchr (input, '\n', n);
1249
1250   if (newline == NULL)
1251     return -1;
1252   else
1253     {
1254       int ofs = newline - input;
1255       if (ofs == 0 || (ofs == 1 && input[0] == '\r'))
1256         {
1257           *type = SEG_NEWLINE;
1258           return ofs + 1;
1259         }
1260       else
1261         return ofs - (input[ofs - 1] == '\r');
1262     }
1263 }
1264
1265 static int
1266 segmenter_parse_do_repeat_3__ (struct segmenter *s,
1267                                const char *input, size_t n,
1268                                enum segment_type *type)
1269 {
1270   int ofs;
1271
1272   ofs = segmenter_parse_full_line__ (input, n, type);
1273   if (ofs < 0 || input[ofs - 1] == '\n')
1274     return ofs;
1275   else if (!check_repeat_command (s, input, n))
1276     return -1;
1277   else if (s->substate == 0)
1278     {
1279       s->state = S_GENERAL;
1280       s->substate = SS_START_OF_COMMAND | SS_START_OF_LINE;
1281       return segmenter_push (s, input, n, type);
1282     }
1283   else
1284     {
1285       *type = SEG_DO_REPEAT_COMMAND;
1286       return ofs;
1287     }
1288 }
1289
1290 static int
1291 segmenter_parse_begin_data_1__ (struct segmenter *s,
1292                                 const char *input, size_t n,
1293                                 enum segment_type *type)
1294 {
1295   int ofs = segmenter_subparse (s, input, n, type);
1296   if (ofs < 0)
1297     return -1;
1298
1299   if (*type == SEG_NEWLINE)
1300     s->state = S_BEGIN_DATA_2;
1301
1302   return ofs;
1303 }
1304
1305 static int
1306 segmenter_parse_begin_data_2__ (struct segmenter *s,
1307                                 const char *input, size_t n,
1308                                 enum segment_type *type)
1309 {
1310   int ofs = segmenter_subparse (s, input, n, type);
1311   if (ofs < 0)
1312     return -1;
1313
1314   if (*type == SEG_NEWLINE)
1315     s->state = S_BEGIN_DATA_3;
1316
1317   return ofs;
1318 }
1319
1320 static bool
1321 is_end_data (const char *input, size_t n)
1322 {
1323   const uint8_t *u_input = CHAR_CAST (const uint8_t *, input);
1324   bool endcmd;
1325   ucs4_t uc;
1326   int mblen;
1327   int ofs;
1328
1329   if (n < 3 || c_strncasecmp (input, "END", 3))
1330     return false;
1331
1332   ofs = 3;
1333   mblen = u8_mbtouc (&uc, u_input + ofs, n - ofs);
1334   if (!lex_uc_is_space (uc))
1335     return false;
1336   ofs += mblen;
1337
1338   if (n - ofs < 4 || c_strncasecmp (input + ofs, "DATA", 4))
1339     return false;
1340   ofs += 4;
1341
1342   endcmd = false;
1343   while (ofs < n)
1344     {
1345       mblen = u8_mbtouc (&uc, u_input + ofs, n - ofs);
1346       if (uc == '.')
1347         {
1348           if (endcmd)
1349             return false;
1350           endcmd = true;
1351         }
1352       else if (!lex_uc_is_space (uc))
1353         return false;
1354       ofs += mblen;
1355     }
1356
1357   return true;
1358 }
1359
1360 static int
1361 segmenter_parse_begin_data_3__ (struct segmenter *s,
1362                                 const char *input, size_t n,
1363                                 enum segment_type *type)
1364 {
1365   int ofs;
1366
1367   ofs = segmenter_parse_full_line__ (input, n, type);
1368   if (ofs < 0)
1369     return -1;
1370   else if (is_end_data (input, ofs))
1371     {
1372       s->state = S_GENERAL;
1373       s->substate = SS_START_OF_COMMAND | SS_START_OF_LINE;
1374       return segmenter_push (s, input, n, type);
1375     }
1376   else
1377     {
1378       *type = SEG_INLINE_DATA;
1379       s->state = S_BEGIN_DATA_4;
1380       return input[ofs - 1] == '\n' ? 0 : ofs;
1381     }
1382 }
1383
1384 static int
1385 segmenter_parse_begin_data_4__ (struct segmenter *s,
1386                                 const char *input, size_t n,
1387                                 enum segment_type *type)
1388 {
1389   int ofs;
1390
1391   ofs = segmenter_parse_newline__ (input, n, type);
1392   if (ofs < 0)
1393     return -1;
1394
1395   s->state = S_BEGIN_DATA_3;
1396   return ofs;
1397 }
1398
1399 static int
1400 segmenter_parse_title_1__ (struct segmenter *s,
1401                            const char *input, size_t n,
1402                            enum segment_type *type)
1403 {
1404   int ofs;
1405
1406   ofs = skip_spaces (input, n, 0);
1407   if (ofs < 0)
1408     return -1;
1409   s->state = S_TITLE_2;
1410   *type = SEG_SPACES;
1411   return ofs;
1412 }
1413
1414 static int
1415 segmenter_parse_title_2__ (struct segmenter *s,
1416                            const char *input, size_t n,
1417                            enum segment_type *type)
1418 {
1419   int endcmd;
1420   int ofs;
1421
1422   endcmd = -1;
1423   ofs = 0;
1424   while (ofs < n)
1425     {
1426       ucs4_t uc;
1427       int mblen;
1428
1429       mblen = segmenter_u8_to_uc__ (&uc, input + ofs, n - ofs);
1430       if (mblen < 0)
1431         return -1;
1432
1433       switch (uc)
1434         {
1435         case '\n':
1436           s->state = S_GENERAL;
1437           s->substate = 0;
1438           *type = SEG_UNQUOTED_STRING;
1439           return endcmd >= 0 ? endcmd : ofs;
1440
1441         case '.':
1442           endcmd = ofs;
1443           break;
1444
1445         default:
1446           if (!lex_uc_is_space (uc))
1447             endcmd = -1;
1448           break;
1449         }
1450
1451       ofs += mblen;
1452     }
1453
1454   return -1;
1455 }
1456
1457 /* Returns the name of segment TYPE as a string.  The caller must not modify
1458    or free the returned string.
1459
1460    This is useful only for debugging and testing. */
1461 const char *
1462 segment_type_to_string (enum segment_type type)
1463 {
1464   switch (type)
1465     {
1466 #define SEG_TYPE(NAME) case SEG_##NAME: return #NAME;
1467       SEG_TYPES
1468 #undef SEG_TYPE
1469     default:
1470       return "unknown segment type";
1471     }
1472 }
1473
1474 /* Initializes S as a segmenter with the given syntax MODE.
1475
1476    A segmenter does not contain any external references, so nothing needs to be
1477    done to destroy one.  For the same reason, segmenters may be copied with
1478    plain struct assignment (or memcpy). */
1479 void
1480 segmenter_init (struct segmenter *s, enum segmenter_mode mode)
1481 {
1482   s->state = S_SHBANG;
1483   s->substate = 0;
1484   s->mode = mode;
1485 }
1486
1487 /* Returns the mode passed to segmenter_init() for S. */
1488 enum segmenter_mode
1489 segmenter_get_mode (const struct segmenter *s)
1490 {
1491   return s->mode;
1492 }
1493
1494 /* Attempts to label a prefix of S's remaining input with a segment type.  The
1495    caller supplies the first N bytes of the remaining input as INPUT, which
1496    must be a UTF-8 encoded string.  The end of the input stream must be
1497    indicated by a null byte at the beginning of a line, that is, immediately
1498    following a new-line (or as the first byte of the input stream).
1499
1500    The input may contain '\n' or '\r\n' line ends in any combination.
1501
1502    If successful, returns the number of bytes in the segment at the beginning
1503    of INPUT (between 0 and N, inclusive) and stores the type of that segment
1504    into *TYPE.  The next call to segmenter_push() should not include those
1505    bytes as part of INPUT, because they have (figuratively) been consumed by
1506    the segmenter.
1507
1508    Failure occurs only if the segment type of the N bytes in INPUT cannot yet
1509    be determined.  In this case segmenter_push() returns -1.  The caller should
1510    obtain more input and then call segmenter_push() again with a larger N and
1511    repeat until the input is exhausted (which must be indicated as described
1512    above) or until a valid segment is returned.  segmenter_push() will never
1513    return -1 when the end of input is visible within INPUT.
1514
1515    The caller must not, in a sequence of calls, supply contradictory input.
1516    That is, bytes provided as part of INPUT in one call, but not consumed, must
1517    not be provided with *different* values on subsequent calls.  This is
1518    because segmenter_push() must often make decisions based on looking ahead
1519    beyond the bytes that it consumes. */
1520 int
1521 segmenter_push (struct segmenter *s, const char *input, size_t n,
1522                 enum segment_type *type)
1523 {
1524   if (n == 0)
1525     return -1;
1526
1527   if (input[0] == '\0')
1528     {
1529       *type = SEG_END;
1530       return 1;
1531     }
1532
1533   switch (s->state)
1534     {
1535     case S_SHBANG:
1536       return segmenter_parse_shbang__ (s, input, n, type);
1537
1538     case S_GENERAL:
1539       return (s->substate & SS_START_OF_LINE
1540               ? segmenter_parse_start_of_line__ (s, input, n, type)
1541               : segmenter_parse_mid_command__ (s, input, n, type));
1542
1543     case S_COMMENT_1:
1544       return segmenter_parse_comment_1__ (s, input, n, type);
1545     case S_COMMENT_2:
1546       return segmenter_parse_comment_2__ (s, input, n, type);
1547
1548     case S_DOCUMENT_1:
1549       return segmenter_parse_document_1__ (s, input, n, type);
1550     case S_DOCUMENT_2:
1551       return segmenter_parse_document_2__ (s, input, n, type);
1552     case S_DOCUMENT_3:
1553       return segmenter_parse_document_3__ (s, type);
1554
1555     case S_FILE_LABEL:
1556       return segmenter_parse_file_label__ (s, input, n, type);
1557
1558     case S_DO_REPEAT_1:
1559       return segmenter_parse_do_repeat_1__ (s, input, n, type);
1560     case S_DO_REPEAT_2:
1561       return segmenter_parse_do_repeat_2__ (s, input, n, type);
1562     case S_DO_REPEAT_3:
1563       return segmenter_parse_do_repeat_3__ (s, input, n, type);
1564
1565     case S_BEGIN_DATA_1:
1566       return segmenter_parse_begin_data_1__ (s, input, n, type);
1567     case S_BEGIN_DATA_2:
1568       return segmenter_parse_begin_data_2__ (s, input, n, type);
1569     case S_BEGIN_DATA_3:
1570       return segmenter_parse_begin_data_3__ (s, input, n, type);
1571     case S_BEGIN_DATA_4:
1572       return segmenter_parse_begin_data_4__ (s, input, n, type);
1573
1574     case S_TITLE_1:
1575       return segmenter_parse_title_1__ (s, input, n, type);
1576     case S_TITLE_2:
1577       return segmenter_parse_title_2__ (s, input, n, type);
1578     }
1579
1580   NOT_REACHED ();
1581 }
1582
1583 /* Returns the style of command prompt to display to an interactive user for
1584    input in S.  The return value is most accurate in mode SEG_MODE_INTERACTIVE
1585    and at the beginning of a line (that is, if segmenter_push() consumed as
1586    much as possible of the input up to a new-line).  */
1587 enum prompt_style
1588 segmenter_get_prompt (const struct segmenter *s)
1589 {
1590   switch (s->state)
1591     {
1592     case S_SHBANG:
1593       return PROMPT_FIRST;
1594
1595     case S_GENERAL:
1596       return s->substate & SS_START_OF_COMMAND ? PROMPT_FIRST : PROMPT_LATER;
1597
1598     case S_COMMENT_1:
1599     case S_COMMENT_2:
1600       return PROMPT_COMMENT;
1601
1602     case S_DOCUMENT_1:
1603     case S_DOCUMENT_2:
1604       return PROMPT_DOCUMENT;
1605     case S_DOCUMENT_3:
1606       return PROMPT_FIRST;
1607
1608     case S_FILE_LABEL:
1609       return PROMPT_LATER;
1610
1611     case S_DO_REPEAT_1:
1612     case S_DO_REPEAT_2:
1613       return s->substate & SS_START_OF_COMMAND ? PROMPT_FIRST : PROMPT_LATER;
1614     case S_DO_REPEAT_3:
1615       return PROMPT_DO_REPEAT;
1616
1617     case S_BEGIN_DATA_1:
1618       return PROMPT_FIRST;
1619     case S_BEGIN_DATA_2:
1620       return PROMPT_LATER;
1621     case S_BEGIN_DATA_3:
1622     case S_BEGIN_DATA_4:
1623       return PROMPT_DATA;
1624
1625     case S_TITLE_1:
1626     case S_TITLE_2:
1627       return PROMPT_FIRST;
1628     }
1629
1630   NOT_REACHED ();
1631 }