scan: New library for high-level PSPP syntax lexical analysis.
[pspp-builds.git] / tests / language / lexer / scan-test.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 <ctype.h>
20 #include <errno.h>
21 #include <getopt.h>
22 #include <limits.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26
27 #include "libpspp/assertion.h"
28 #include "libpspp/compiler.h"
29 #include "libpspp/misc.h"
30 #include "language/lexer/scan.h"
31 #include "language/lexer/token.h"
32
33 #include "gl/error.h"
34 #include "gl/ftoastr.h"
35 #include "gl/progname.h"
36 #include "gl/read-file.h"
37 #include "gl/xalloc.h"
38
39 /* -a/--auto, -b/--batch, -i/--interactive: syntax mode. */
40 static enum segmenter_mode mode = SEG_MODE_AUTO;
41
42 static const char *parse_options (int argc, char **argv);
43 static void usage (void) NO_RETURN;
44
45 int
46 main (int argc, char *argv[])
47 {
48   struct segment
49     {
50       enum segment_type type;
51       struct substring string;
52     };
53
54   size_t offset;
55   const char *file_name;
56   char *input;
57   struct segmenter s;
58   struct segment *segs;
59   size_t n_segs, allocated_segs;
60   size_t length;
61   size_t i;
62   int n;
63
64   set_program_name (argv[0]);
65   file_name = parse_options (argc, argv);
66
67   /* Read from stdin into 'input'.  Ensure that 'input' ends in a new-line
68      followed by a null byte. */
69   input = (!strcmp (file_name, "-")
70            ? fread_file (stdin, &length)
71            : read_file (file_name, &length));
72   if (input == NULL)
73     error (EXIT_FAILURE, errno, "reading %s failed", file_name);
74   input = xrealloc (input, length + 3);
75   if (length == 0 || input[length - 1] != '\n')
76     input[length++] = '\n';
77   input[length++] = '\0';
78
79   segs = NULL;
80   n_segs = allocated_segs = 0;
81
82   segmenter_init (&s, mode);
83   for (offset = 0; offset < length; offset += n)
84     {
85       enum segment_type type;
86
87       n = segmenter_push (&s, input + offset, length - offset, &type);
88       assert (n >= 0);
89       assert (offset + n <= length);
90
91       if (n_segs >= allocated_segs)
92         segs = x2nrealloc (segs, &allocated_segs, sizeof *segs);
93
94       segs[n_segs].type = type;
95       segs[n_segs].string.string = input + offset;
96       segs[n_segs].string.length = n;
97       n_segs++;
98     }
99
100   for (i = 0; i < n_segs; )
101     {
102       enum scan_result result;
103       struct scanner scanner;
104       struct token token;
105       int saved = -1;
106
107       scanner_init (&scanner, &token);
108       do
109         {
110           struct segment *seg;
111
112           assert (i < n_segs);
113
114           seg = &segs[i++];
115           result = scanner_push (&scanner, seg->type, seg->string, &token);
116           if (result == SCAN_SAVE)
117             saved = i;
118         }
119       while (result == SCAN_MORE || result == SCAN_SAVE);
120
121       if (result == SCAN_BACK)
122         {
123           assert (saved >= 0);
124           i = saved;
125         }
126
127       printf ("%s", scan_type_to_string (token.type));
128       if (token.number != 0.0)
129         {
130           char s[DBL_BUFSIZE_BOUND];
131
132           dtoastr (s, sizeof s, 0, 0, token.number);
133           printf (" %s", s);
134         }
135       if (token.string.string != NULL || token.string.length > 0)
136         printf (" \"%.*s\"", (int) token.string.length, token.string.string);
137       printf ("\n");
138
139       token_destroy (&token);
140     }
141
142   free (input);
143
144   return 0;
145 }
146
147 static const char *
148 parse_options (int argc, char **argv)
149 {
150   for (;;)
151     {
152       static const struct option options[] =
153         {
154           {"auto", no_argument, NULL, 'a'},
155           {"batch", no_argument, NULL, 'b'},
156           {"interactive", no_argument, NULL, 'i'},
157           {"help", no_argument, NULL, 'h'},
158           {NULL, 0, NULL, 0},
159         };
160
161       int c = getopt_long (argc, argv, "abih", options, NULL);
162       if (c == -1)
163         break;
164
165       switch (c)
166         {
167         case 'a':
168           mode = SEG_MODE_AUTO;
169           break;
170
171         case 'b':
172           mode = SEG_MODE_BATCH;
173           break;
174
175         case 'i':
176           mode = SEG_MODE_INTERACTIVE;
177           break;
178
179         case 'h':
180           usage ();
181
182         case 0:
183           break;
184
185         case '?':
186           exit (EXIT_FAILURE);
187           break;
188
189         default:
190           NOT_REACHED ();
191         }
192
193     }
194
195   if (optind + 1 != argc)
196     error (1, 0, "exactly one non-option argument required; "
197            "use --help for help");
198   return argv[optind];
199 }
200
201 static void
202 usage (void)
203 {
204   printf ("\
205 %s, to test breaking PSPP syntax into tokens\n\
206 usage: %s [OPTIONS] INPUT\n\
207 \n\
208 Options:\n\
209   -1, --one-segment   feed one segment at a time\n\
210   -a, --auto          use \"auto\" syntax mode\n\
211   -b, --batch         use \"batch\" syntax mode\n\
212   -i, --interactive   use \"interactive\" syntax mode (default)\n\
213   -v, --verbose       include rows and column numbers in output\n\
214   -h, --help          print this help message\n",
215           program_name, program_name);
216   exit (EXIT_SUCCESS);
217 }