AGGREGATE: Bring up to speed.
[pspp] / src / language / lexer / token.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/token.h"
20
21 #include <math.h>
22 #include <unictype.h>
23 #include <unistr.h>
24
25 #include "data/identifier.h"
26 #include "libpspp/assertion.h"
27 #include "libpspp/cast.h"
28 #include "libpspp/misc.h"
29
30 #include "gl/ftoastr.h"
31 #include "gl/xalloc.h"
32
33 /* Initializes DST as a copy of SRC. */
34 void
35 token_copy (struct token *dst, const struct token *src)
36 {
37   *dst = (struct token) {
38     .type = src->type,
39     .number = src->number,
40     .string = ss_clone (src->string),
41   };
42 }
43
44 /* Frees the string that TOKEN contains. */
45 void
46 token_uninit (struct token *token)
47 {
48   if (token != NULL)
49     {
50       ss_dealloc (&token->string);
51       *token = (struct token) { .type = T_STOP };
52     }
53 }
54
55 /* Returns true if A and B are the same token, false otherwise. */
56 bool
57 token_equal (const struct token *a, const struct token *b)
58 {
59   if (a->type != b->type)
60     return false;
61
62   switch (a->type)
63     {
64     case T_POS_NUM:
65     case T_NEG_NUM:
66       return a->number == b->number;
67
68     case T_ID:
69     case T_MACRO_ID:
70     case T_MACRO_PUNCT:
71     case T_STRING:
72       return ss_equals (a->string, b->string);
73
74     default:
75       return true;
76     }
77 }
78
79 static char *
80 number_token_to_string (const struct token *token)
81 {
82   char buffer[DBL_BUFSIZE_BOUND];
83
84   c_dtoastr (buffer, sizeof buffer, 0, 0, fabs (token->number));
85   return (token->type == T_POS_NUM
86           ? xstrdup (buffer)
87           : xasprintf ("-%s", buffer));
88 }
89
90 static char *
91 quoted_string_representation (struct substring ss, size_t n_quotes)
92 {
93   char *rep;
94   size_t i;
95   char *p;
96
97   p = rep = xmalloc (1 + ss.length + n_quotes + 1 + 1);
98   *p++ = '\'';
99   for (i = 0; i < ss.length; i++)
100     {
101       uint8_t c = ss.string[i];
102       if (c == '\'')
103         *p++ = c;
104       *p++ = c;
105     }
106   *p++ = '\'';
107   *p = '\0';
108
109   return rep;
110 }
111
112 static char *
113 hex_string_representation (struct substring ss)
114 {
115   char *rep;
116   size_t i;
117   char *p;
118
119   p = rep = xmalloc (2 + 2 * ss.length + 1 + 1);
120   *p++ = 'X';
121   *p++ = '\'';
122   for (i = 0; i < ss.length; i++)
123     {
124       static const char hex_digits[] = "0123456789abcdef";
125       uint8_t c = ss.string[i];
126       *p++ = hex_digits[c >> 4];
127       *p++ = hex_digits[c & 15];
128     }
129   *p++ = '\'';
130   *p = '\0';
131
132   return rep;
133 }
134
135 static char *
136 string_representation (struct substring ss)
137 {
138   size_t n_quotes;
139   size_t ofs;
140   int mblen;
141
142   n_quotes = 0;
143   for (ofs = 0; ofs < ss.length; ofs += mblen)
144     {
145       ucs4_t uc;
146
147       mblen = u8_mbtoucr (&uc,
148                           CHAR_CAST (const uint8_t *, ss.string + ofs),
149                           ss.length - ofs);
150       if (mblen < 0 || !uc_is_print (uc))
151         return hex_string_representation (ss);
152       else if (uc == '\'')
153         n_quotes++;
154     }
155   return quoted_string_representation (ss, n_quotes);
156 }
157
158 /* Returns a UTF-8 string that would yield TOKEN if it appeared in a syntax
159    file.  The caller should free the returned string, with free(), when it is
160    no longer needed.
161
162    The T_STOP token has no representation, so this function returns NULL. */
163 char *
164 token_to_string (const struct token *token)
165 {
166   switch (token->type)
167     {
168     case T_POS_NUM:
169     case T_NEG_NUM:
170       return number_token_to_string (token);
171
172     case T_ID:
173     case T_MACRO_ID:
174     case T_MACRO_PUNCT:
175       return ss_xstrdup (token->string);
176
177     case T_STRING:
178       return string_representation (token->string);
179
180     default:
181       return xstrdup_if_nonnull (token_type_to_string (token->type));
182     }
183 }
184
185 /* Prints TOKEN on STREAM, for debugging. */
186 void
187 token_print (const struct token *token, FILE *stream)
188 {
189   fputs (token_type_to_name (token->type), stream);
190   if (token->type == T_POS_NUM || token->type == T_NEG_NUM
191       || token->number != 0.0)
192     {
193       char s[DBL_BUFSIZE_BOUND];
194
195       c_dtoastr (s, sizeof s, 0, 0, token->number);
196       fprintf (stream, "\t%s", s);
197     }
198   if (token->type == T_ID || token->type == T_STRING || token->string.length)
199     fprintf (stream, "\t\"%.*s\"",
200              (int) token->string.length, token->string.string);
201   putc ('\n', stream);
202 }
203
204 /* Returns true if T is a numeric token for an integer in the range of "long",
205    except that LONG_MIN is excluded. */
206 bool
207 token_is_integer (const struct token *t)
208 {
209   return (token_is_number (t)
210           && t->number > LONG_MIN
211           && t->number <= LONG_MAX
212           && floor (t->number) == t->number);
213 }
214
215 /* Returns the "long int" value of T, which must satisfy token_is_integer(T). */
216 long
217 token_integer (const struct token *t)
218 {
219   assert (token_is_integer (t));
220   return t->number;
221 }