scan: New library for high-level PSPP syntax lexical analysis.
[pspp-builds.git] / 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
29 #include "gl/ftoastr.h"
30 #include "gl/xalloc.h"
31
32 /* Initializes TOKEN with an arbitrary type, number 0, and a null string. */
33 void
34 token_init (struct token *token)
35 {
36   token->type = 0;
37   token->number = 0.0;
38   token->string = ss_empty ();
39 }
40
41 /* Frees the string that TOKEN contains. */
42 void
43 token_destroy (struct token *token)
44 {
45   if (token != NULL)
46     ss_dealloc (&token->string);
47 }
48
49 static char *
50 number_token_to_string (const struct token *token)
51 {
52   char buffer[DBL_BUFSIZE_BOUND];
53
54   dtoastr (buffer, sizeof buffer, 0, 0, fabs (token->number));
55   return (token->type == T_POS_NUM
56           ? xstrdup (buffer)
57           : xasprintf ("-%s", buffer));
58 }
59
60 static char *
61 quoted_string_representation (struct substring ss, size_t n_quotes)
62 {
63   char *rep;
64   size_t i;
65   char *p;
66
67   p = rep = xmalloc (1 + ss.length + n_quotes + 1 + 1);
68   *p++ = '\'';
69   for (i = 0; i < ss.length; i++)
70     {
71       uint8_t c = ss.string[i];
72       if (c == '\'')
73         *p++ = c;
74       *p++ = c;
75     }
76   *p++ = '\'';
77   *p = '\0';
78
79   return rep;
80 }
81
82 static char *
83 hex_string_representation (struct substring ss)
84 {
85   char *rep;
86   size_t i;
87   char *p;
88
89   p = rep = xmalloc (2 + 2 * ss.length + 1 + 1);
90   *p++ = 'X';
91   *p++ = '\'';
92   for (i = 0; i < ss.length; i++)
93     {
94       static const char hex_digits[] = "0123456789abcdef";
95       uint8_t c = ss.string[i];
96       *p++ = hex_digits[c >> 4];
97       *p++ = hex_digits[c & 15];
98     }
99   *p++ = '\'';
100   *p = '\0';
101
102   return rep;
103 }
104
105 static char *
106 string_representation (struct substring ss)
107 {
108   size_t n_quotes;
109   size_t ofs;
110   int mblen;
111
112   n_quotes = 0;
113   for (ofs = 0; ofs < ss.length; ofs += mblen)
114     {
115       ucs4_t uc;
116
117       mblen = u8_mbtoucr (&uc,
118                           CHAR_CAST (const uint8_t *, ss.string + ofs),
119                           ss.length - ofs);
120       if (mblen < 0 || !uc_is_print (uc))
121         return hex_string_representation (ss);
122       else if (uc == '\'')
123         n_quotes++;
124     }
125   return quoted_string_representation (ss, n_quotes);
126 }
127
128 /* Returns a UTF-8 string that would yield TOKEN if it appeared in a syntax
129    file.  The caller should free the returned string, with free(), when it is
130    no longer needed.
131
132    The T_STOP token has no representation, so this function returns NULL. */
133 char *
134 token_to_string (const struct token *token)
135 {
136   const char *name;
137
138   switch (token->type)
139     {
140     case T_POS_NUM:
141     case T_NEG_NUM:
142       return number_token_to_string (token);
143
144     case T_ID:
145       return ss_xstrdup (token->string);
146
147     case T_STRING:
148       return string_representation (token->string);
149
150     default:
151       name = token_type_to_name (token->type);
152       return name != NULL ? xstrdup (name) : NULL;
153     }
154 }
155
156 /* Prints TOKEN on STREAM, for debugging. */
157 void
158 token_print (const struct token *token, FILE *stream)
159 {
160   fputs (token_type_to_name (token->type), stream);
161   if (token->type == T_POS_NUM || token->type == T_NEG_NUM
162       || token->number != 0.0)
163     {
164       char s[DBL_BUFSIZE_BOUND];
165
166       dtoastr (s, sizeof s, 0, 0, token->number);
167       fprintf (stream, "\t%s", s);
168     }
169   if (token->type == T_ID || token->type == T_STRING || token->string.length)
170     fprintf (stream, "\t\"%.*s\"",
171              (int) token->string.length, token->string.string);
172   putc ('\n', stream);
173 }