]> git.saurik.com Git - bison.git/blame - src/location.c
parser: no longer use the "braceless" non-terminal
[bison.git] / src / location.c
CommitLineData
8efe435c 1/* Locations for Bison
7d424de1 2
7d6bad19 3 Copyright (C) 2002, 2005-2013 Free Software Foundation, Inc.
8efe435c
AD
4
5 This file is part of Bison, the GNU Compiler Compiler.
6
f16b0819 7 This program is free software: you can redistribute it and/or modify
8efe435c 8 it under the terms of the GNU General Public License as published by
f16b0819
PE
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
8efe435c 11
f16b0819 12 This program is distributed in the hope that it will be useful,
8efe435c
AD
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
f16b0819 18 along with this program. If not, see <http://www.gnu.org/licenses/>. */
8efe435c 19
2cec9080 20#include <config.h>
1a715ef2 21#include "system.h"
81ebdef9 22
0c8e079f 23#include <mbswidth.h>
b17a1fc5 24#include <quotearg.h>
8efe435c 25
0c8e079f 26#include "complain.h"
81ebdef9
PE
27#include "location.h"
28
28e52c0d 29location const empty_location = EMPTY_LOCATION_INIT;
b17a1fc5 30
e9071366
AD
31/* If BUF is null, add BUFSIZE (which in this case must be less than
32 INT_MAX) to COLUMN; otherwise, add mbsnwidth (BUF, BUFSIZE, 0) to
33 COLUMN. If an overflow occurs, or might occur but is undetectable,
34 return INT_MAX. Assume COLUMN is nonnegative. */
35
36static inline int
37add_column_width (int column, char const *buf, size_t bufsize)
38{
39 size_t width;
40 unsigned int remaining_columns = INT_MAX - column;
41
42 if (buf)
43 {
44 if (INT_MAX / 2 <= bufsize)
e9690142 45 return INT_MAX;
e9071366
AD
46 width = mbsnwidth (buf, bufsize, 0);
47 }
48 else
49 width = bufsize;
50
51 return width <= remaining_columns ? column + width : INT_MAX;
52}
53
54/* Set *LOC and adjust scanner cursor to account for token TOKEN of
55 size SIZE. */
56
57void
58location_compute (location *loc, boundary *cur, char const *token, size_t size)
59{
60 int line = cur->line;
61 int column = cur->column;
62 char const *p0 = token;
63 char const *p = token;
64 char const *lim = token + size;
65
66 loc->start = *cur;
67
68 for (p = token; p < lim; p++)
69 switch (*p)
70 {
71 case '\n':
e9690142
JD
72 line += line < INT_MAX;
73 column = 1;
74 p0 = p + 1;
75 break;
e9071366
AD
76
77 case '\t':
e9690142
JD
78 column = add_column_width (column, p0, p - p0);
79 column = add_column_width (column, NULL, 8 - ((column - 1) & 7));
80 p0 = p + 1;
81 break;
e9071366
AD
82
83 default:
e9690142 84 break;
e9071366
AD
85 }
86
87 cur->line = line;
88 cur->column = column = add_column_width (column, p0, p - p0);
89
90 loc->end = *cur;
91
92 if (line == INT_MAX && loc->start.line != INT_MAX)
bb8e56ff 93 complain (loc, Wother, _("line number overflow"));
e9071366 94 if (column == INT_MAX && loc->start.column != INT_MAX)
bb8e56ff 95 complain (loc, Wother, _("column number overflow"));
e9071366
AD
96}
97
98
66381412 99unsigned
b805eca7 100location_print (location loc, FILE *out)
b17a1fc5 101{
66381412 102 unsigned res = 0;
92822aff 103 int end_col = 0 != loc.end.column ? loc.end.column - 1 : 0;
66381412
AR
104 res += fprintf (out, "%s",
105 quotearg_n_style (3, escape_quoting_style, loc.start.file));
92822aff
JD
106 if (0 <= loc.start.line)
107 {
4c36bc2b 108 res += fprintf (out, ":%d", loc.start.line);
92822aff 109 if (0 <= loc.start.column)
66381412 110 res += fprintf (out, ".%d", loc.start.column);
92822aff 111 }
b17a1fc5 112 if (loc.start.file != loc.end.file)
92822aff 113 {
66381412
AR
114 res += fprintf (out, "-%s",
115 quotearg_n_style (3, escape_quoting_style,
116 loc.end.file));
92822aff
JD
117 if (0 <= loc.end.line)
118 {
4c36bc2b 119 res += fprintf (out, ":%d", loc.end.line);
92822aff 120 if (0 <= end_col)
66381412 121 res += fprintf (out, ".%d", end_col);
92822aff
JD
122 }
123 }
124 else if (0 <= loc.end.line)
125 {
126 if (loc.start.line < loc.end.line)
127 {
66381412 128 res += fprintf (out, "-%d", loc.end.line);
92822aff 129 if (0 <= end_col)
66381412 130 res += fprintf (out, ".%d", end_col);
92822aff
JD
131 }
132 else if (0 <= end_col && loc.start.column < end_col)
66381412 133 res += fprintf (out, "-%d", end_col);
92822aff 134 }
66381412
AR
135
136 return res;
b17a1fc5 137}
3fc65ead 138
3f5d1b2c
TR
139
140/* Persistant data used by location_caret to avoid reopening and rereading the
141 same file all over for each error. */
142struct caret_info
143{
dbda5604 144 FILE *source;
3f5d1b2c
TR
145 size_t line;
146 size_t offset;
147};
148
149static struct caret_info caret_info = { NULL, 1, 0 };
150
3f5d1b2c
TR
151void
152cleanup_caret ()
153{
154 if (caret_info.source)
155 fclose (caret_info.source);
432a008d
TR
156 caret_info.source = NULL;
157 caret_info.line = 1;
158 caret_info.offset = 0;
3f5d1b2c
TR
159}
160
3f5d1b2c 161void
b805eca7 162location_caret (location loc, FILE *out)
3f5d1b2c 163{
dbda5604 164 /* FIXME: find a way to support multifile locations, and only open once each
3f5d1b2c
TR
165 file. That would make the procedure future-proof. */
166 if (! (caret_info.source
167 || (caret_info.source = fopen (loc.start.file, "r")))
168 || loc.start.column == -1 || loc.start.line == -1)
169 return;
170
171 /* If the line we want to quote is seekable (the same line as the previous
432a008d
TR
172 location), just seek it. If it was a previous line, we lost track of it,
173 so return to the start of file. */
3f5d1b2c
TR
174 if (caret_info.line <= loc.start.line)
175 fseek (caret_info.source, caret_info.offset, SEEK_SET);
176 else
177 {
178 caret_info.line = 1;
179 caret_info.offset = 0;
180 fseek (caret_info.source, caret_info.offset, SEEK_SET);
181 }
182
183 /* Advance to the line's position, keeping track of the offset. */
dbda5604 184 while (caret_info.line < loc.start.line)
432a008d 185 caret_info.line += getc (caret_info.source) == '\n';
dbda5604 186 caret_info.offset = ftell (caret_info.source);
3f5d1b2c
TR
187
188 /* Read the actual line. Don't update the offset, so that we keep a pointer
189 to the start of the line. */
190 {
432a008d
TR
191 char c = getc (caret_info.source);
192 if (c != EOF)
3f5d1b2c 193 {
dbda5604 194 /* Quote the file, indent by a single column. */
432a008d
TR
195 putc (' ', out);
196 do
197 putc (c, out);
198 while ((c = getc (caret_info.source)) != EOF && c != '\n');
199 putc ('\n', out);
dbda5604 200
dbda5604 201 {
432a008d
TR
202 /* The caret of a multiline location ends with the first line. */
203 size_t len = loc.start.line != loc.end.line
204 ? ftell (caret_info.source) - caret_info.offset
205 : loc.end.column;
206 int i;
207
208 /* Print the carets (at least one), with the same indent as above.*/
209 fprintf (out, " %*s", loc.start.column - 1, "");
210 for (i = loc.start.column; i == loc.start.column || i < len; ++i)
211 putc ('^', out);
212 }
213 putc ('\n', out);
3f5d1b2c
TR
214 }
215 }
216}
217
3fc65ead
JD
218void
219boundary_set_from_string (boundary *bound, char *loc_str)
220{
221 /* Must search in reverse since the file name field may
45eebca4 222 * contain '.' or ':'. */
84526bf3 223 char *delim = strrchr (loc_str, '.');
3fc65ead
JD
224 aver (delim);
225 *delim = '\0';
226 bound->column = atoi (delim+1);
84526bf3 227 delim = strrchr (loc_str, ':');
3fc65ead
JD
228 aver (delim);
229 *delim = '\0';
230 bound->line = atoi (delim+1);
231 bound->file = uniqstr_new (loc_str);
232}