2 static char *RCSid() { return RCSid("$Id: scanner.c,v 1.23 2005/11/23 23:33:37 mikulik Exp $"); }
5 /* GNUPLOT - scanner.c */
8 * Copyright 1986 - 1993, 1998, 2004 Thomas Williams, Colin Kelley
10 * Permission to use, copy, and distribute this software and its
11 * documentation for any purpose with or without fee is hereby granted,
12 * provided that the above copyright notice appear in all copies and
13 * that both that copyright notice and this permission notice appear
14 * in supporting documentation.
16 * Permission to modify the software is granted, but not the right to
17 * distribute the complete modified source code. Modifications are to
18 * be distributed as patches to the released version. Permission to
19 * distribute binaries produced by compiling modified sources is granted,
21 * 1. distribute the corresponding source modifications from the
22 * released version in the form of a patch file along with the binaries,
23 * 2. add special version identification to distinguish your version
24 * in addition to the base release version number,
25 * 3. provide your name and address as the primary contact for the
26 * support of your modified version, and
27 * 4. retain our contact information in regard to use of the base
29 * Permission to distribute the released version of the source code along
30 * with corresponding source modifications in the form of a patch file is
31 * granted with same provisions 2 through 4 for binary distributions.
33 * This software is provided "as is" without express or implied warranty
34 * to the extent permitted by applicable law.
43 static int get_num __PROTO((char str[]));
44 static void substitute __PROTO((char **strp, size_t *str_lenp, int current));
48 int open(const char *_name, int _mode,...);
50 #endif /* AMIGA_AC_5 */
54 #define MAILBOX "PLOT$MAILBOX"
55 #define pclose(f) fclose(f)
57 #include <lib$routines.h> /* avoid some IMPLICITFNC warnings */
63 #define isident(c) (isalnum((unsigned char)c) || (c) == '_')
68 #define APPEND_TOKEN {token[t_num].length++; current++;}
70 #define SCAN_IDENTIFIER while (isident(expression[current + 1]))\
73 static int t_num; /* number of token I'm working on */
76 * scanner() breaks expression[] into lexical units, storing them in token[].
77 * The total number of tokens found is returned as the function
78 * value. Scanning will stop when '\0' is found in expression[], or
79 * when token[] is full. extend_input_line() is called to extend
80 * expression array if needed.
82 * Scanning is performed by following rules:
84 * Current char token should contain
85 * ------------- -----------------------
86 * 1. alpha,_ all following alpha-numerics
87 * 2. digit 0 or more following digits, 0 or 1 decimal point,
88 * 0 or more digits, 0 or 1 'e' or 'E',
90 * 3. ^,+,-,/ only current char
94 * $ for using patch (div)
95 * 4. &,|,=,* current char; also next if next is same
96 * 5. !,<,> current char; also next if next is =
97 * 6. ", ' all chars up until matching quote
98 * 7. # this token cuts off scanning of the line (DFK).
99 * 8. ` (command substitution: all characters through the
100 * matching backtic are replaced by the output of
101 * the contained command, then scanning is restarted.)
103 * white space between tokens is ignored
106 scanner(char **expressionp, size_t *expressionlenp)
108 int current; /* index of current char in expression[] */
109 char *expression = *expressionp;
113 for (current = t_num = 0; expression[current] != NUL; current++) {
114 if (t_num + 1 >= token_table_size) {
115 /* leave space for dummy end token */
116 extend_token_table();
118 if (isspace((unsigned char) expression[current]))
119 continue; /* skip the whitespace */
120 token[t_num].start_index = current;
121 token[t_num].length = 1;
122 token[t_num].is_token = TRUE; /* to start with... */
124 if (expression[current] == '`') {
125 substitute(expressionp, expressionlenp, current);
126 expression = *expressionp; /* expression might have moved */
130 /* allow _ to be the first character of an identifier */
131 if (isalpha((unsigned char) expression[current])
132 || expression[current] == '_') {
134 } else if (isdigit((unsigned char) expression[current])
135 || expression[current] == '.') {
136 token[t_num].is_token = FALSE;
137 token[t_num].length = get_num(&expression[current]);
138 current += (token[t_num].length - 1);
139 #ifdef GP_STRING_VARS
140 if (token[t_num].length == 1 && expression[current] == '.')
141 token[t_num].is_token = TRUE;
143 } else if (expression[current] == LBRACE) {
144 token[t_num].is_token = FALSE;
145 token[t_num].l_val.type = CMPLX;
149 if ((sscanf(&expression[++current], "%lf,%lf%[ }]s",
150 &token[t_num].l_val.v.cmplx_val.real,
151 &token[t_num].l_val.v.cmplx_val.imag,
152 &l) != 3) || (!strchr(l, RBRACE)))
153 int_error(t_num, "invalid complex constant");
156 if ((sscanf(&expression[++current], "%lf , %lf %c",
157 &token[t_num].l_val.v.cmplx_val.real,
158 &token[t_num].l_val.v.cmplx_val.imag,
159 &brace) != 3) || (brace != RBRACE))
160 int_error(t_num, "invalid complex constant");
162 token[t_num].length += 2;
163 while (expression[++current] != RBRACE) {
164 token[t_num].length++;
165 if (expression[current] == NUL) /* { for vi % */
166 int_error(t_num, "no matching '}'");
168 } else if (expression[current] == '\'' ||
169 expression[current] == '\"') {
170 token[t_num].length++;
171 quote = expression[current];
172 while (expression[++current] != quote) {
173 if (!expression[current]) {
174 expression[current] = quote;
175 expression[current + 1] = NUL;
177 } else if (quote == '\"'
178 && expression[current] == '\\'
179 && expression[current + 1]) {
181 token[t_num].length += 2;
182 } else if (quote == '\"' && expression[current] == '`') {
183 substitute(expressionp, expressionlenp, current);
184 expression = *expressionp; /* it might have moved */
186 } else if (quote == '\''
187 && expression[current+1] == '\''
188 && expression[current+2] == '\'') {
189 /* look ahead: two subsequent single quotes
193 token[t_num].length += 3;
195 token[t_num].length++;
198 switch (expression[current]) {
199 case '#': /* DFK: add comments to gnuplot */
200 goto endline; /* ignore the rest of the line */
221 if (expression[current] == expression[current + 1])
227 if (expression[current + 1] == '=')
231 int_error(t_num, "invalid character %c",expression[current]);
233 ++t_num; /* next token if not white space */
236 endline: /* comments jump here to ignore line */
238 /* Now kludge an extra token which points to '\0' at end of expression[].
239 This is useful so printerror() looks nice even if we've fallen off the
242 token[t_num].start_index = current;
243 token[t_num].length = 0;
244 /* print 3+4 then print 3+ is accepted without
245 * this, since string is ignored if it is not
248 token[t_num].is_token = TRUE;
259 token[t_num].is_token = FALSE;
260 token[t_num].l_val.type = INTGR; /* assume unless . or E found */
261 while (isdigit((unsigned char) str[count]))
263 if (str[count] == '.') {
264 token[t_num].l_val.type = CMPLX;
265 /* swallow up digits until non-digit */
266 while (isdigit((unsigned char) str[++count]));
267 /* now str[count] is other than a digit */
269 if (str[count] == 'e' || str[count] == 'E') {
270 token[t_num].l_val.type = CMPLX;
271 /* modified if statement to allow + sign in exponent
274 if (str[count] == '-' || str[count] == '+')
276 if (!isdigit((unsigned char) str[count])) {
277 token[t_num].start_index += count;
278 int_error(t_num, "expecting exponent");
280 while (isdigit((unsigned char) str[++count]));
282 if (token[t_num].l_val.type == INTGR) {
284 if ((token[t_num].l_val.v.int_val = lval) != lval)
285 int_error(t_num, "integer overflow; change to floating point");
287 token[t_num].l_val.v.cmplx_val.imag = 0.0;
289 /* Always read numbers on command line as C locale */
290 if (strcmp(localeconv()->decimal_point,".")) {
291 char *save_locale = gp_strdup(setlocale(LC_NUMERIC,NULL));
292 setlocale(LC_NUMERIC,"C");
293 token[t_num].l_val.v.cmplx_val.real = atof(str);
294 setlocale(LC_NUMERIC,save_locale);
298 token[t_num].l_val.v.cmplx_val.real = atof(str);
303 /* substitute output from ` `
304 * *strp points to the input string. (*strp)[current] is expected to
305 * be the initial back tic. Characters through the following back tic
306 * are replaced by the output of the command. extend_input_line()
307 * is called to extend *strp array if needed.
310 substitute(char **strp, size_t *str_lenp, int current)
314 char *str, *pgm, *rest = NULL;
316 size_t pgm_len, rest_len = 0;
319 /* forgive missing closing backquote at end of line */
320 str = *strp + current;
326 pgm_len = last - str;
327 pgm = gp_alloc(pgm_len, "command string");
328 safe_strncpy(pgm, str + 1, pgm_len); /* omit ` to leave room for NUL */
330 /* save rest of line, if any */
332 last++; /* advance past ` */
333 rest_len = strlen(last) + 1;
335 rest = gp_alloc(rest_len, "input line copy");
341 (void) do_system_func(pgm, &output);
344 /* now replace ` ` with output */
346 while ((c = output[output_pos++])) {
347 if (c != '\n' && c != '\r')
348 (*strp)[current++] = c;
349 if (current == *str_lenp)
352 (*strp)[current] = 0;
355 /* tack on rest of line to output */
357 while (current + rest_len > *str_lenp)
359 strcpy(*strp + current, rest);