glcpp: Skip unnecessary line continuations removal
[mesa.git] / src / compiler / glsl / glcpp / pp.c
1 /*
2 * Copyright © 2010 Intel Corporation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 */
23
24 #include <assert.h>
25 #include <string.h>
26 #include <ctype.h>
27 #include "glcpp.h"
28
29 void
30 glcpp_error (YYLTYPE *locp, glcpp_parser_t *parser, const char *fmt, ...)
31 {
32 va_list ap;
33
34 parser->error = 1;
35 ralloc_asprintf_rewrite_tail(&parser->info_log,
36 &parser->info_log_length,
37 "%u:%u(%u): "
38 "preprocessor error: ",
39 locp->source,
40 locp->first_line,
41 locp->first_column);
42 va_start(ap, fmt);
43 ralloc_vasprintf_rewrite_tail(&parser->info_log,
44 &parser->info_log_length,
45 fmt, ap);
46 va_end(ap);
47 ralloc_asprintf_rewrite_tail(&parser->info_log,
48 &parser->info_log_length, "\n");
49 }
50
51 void
52 glcpp_warning (YYLTYPE *locp, glcpp_parser_t *parser, const char *fmt, ...)
53 {
54 va_list ap;
55
56 ralloc_asprintf_rewrite_tail(&parser->info_log,
57 &parser->info_log_length,
58 "%u:%u(%u): "
59 "preprocessor warning: ",
60 locp->source,
61 locp->first_line,
62 locp->first_column);
63 va_start(ap, fmt);
64 ralloc_vasprintf_rewrite_tail(&parser->info_log,
65 &parser->info_log_length,
66 fmt, ap);
67 va_end(ap);
68 ralloc_asprintf_rewrite_tail(&parser->info_log,
69 &parser->info_log_length, "\n");
70 }
71
72 /* Given str, (that's expected to start with a newline terminator of some
73 * sort), return a pointer to the first character in str after the newline.
74 *
75 * A newline terminator can be any of the following sequences:
76 *
77 * "\r\n"
78 * "\n\r"
79 * "\n"
80 * "\r"
81 *
82 * And the longest such sequence will be skipped.
83 */
84 static const char *
85 skip_newline (const char *str)
86 {
87 const char *ret = str;
88
89 if (ret == NULL)
90 return ret;
91
92 if (*ret == '\0')
93 return ret;
94
95 if (*ret == '\r') {
96 ret++;
97 if (*ret && *ret == '\n')
98 ret++;
99 } else if (*ret == '\n') {
100 ret++;
101 if (*ret && *ret == '\r')
102 ret++;
103 }
104
105 return ret;
106 }
107
108 /* Remove any line continuation characters in the shader, (whether in
109 * preprocessing directives or in GLSL code).
110 */
111 static char *
112 remove_line_continuations(glcpp_parser_t *ctx, const char *shader)
113 {
114 char *clean = ralloc_strdup(ctx, "");
115 const char *backslash, *newline, *search_start;
116 const char *cr, *lf;
117 char newline_separator[3];
118 int collapsed_newlines = 0;
119
120 backslash = strchr(shader, '\\');
121
122 /* No line continuations were found in this shader, our job is done */
123 if (backslash == NULL)
124 return (char *) shader;
125
126 search_start = shader;
127
128 /* Determine what flavor of newlines this shader is using. GLSL
129 * provides for 4 different possible ways to separate lines, (using
130 * one or two characters):
131 *
132 * "\n" (line-feed, like Linux, Unix, and new Mac OS)
133 * "\r" (carriage-return, like old Mac files)
134 * "\r\n" (carriage-return + line-feed, like DOS files)
135 * "\n\r" (line-feed + carriage-return, like nothing, really)
136 *
137 * This code explicitly supports a shader that uses a mixture of
138 * newline terminators and will properly handle line continuation
139 * backslashes followed by any of the above.
140 *
141 * But, since we must also insert additional newlines in the output
142 * (for any collapsed lines) we attempt to maintain consistency by
143 * examining the first encountered newline terminator, and using the
144 * same terminator for any newlines we insert.
145 */
146 cr = strchr(search_start, '\r');
147 lf = strchr(search_start, '\n');
148
149 newline_separator[0] = '\n';
150 newline_separator[1] = '\0';
151 newline_separator[2] = '\0';
152
153 if (cr == NULL) {
154 /* Nothing to do. */
155 } else if (lf == NULL) {
156 newline_separator[0] = '\r';
157 } else if (lf == cr + 1) {
158 newline_separator[0] = '\r';
159 newline_separator[1] = '\n';
160 } else if (cr == lf + 1) {
161 newline_separator[0] = '\n';
162 newline_separator[1] = '\r';
163 }
164
165 while (true) {
166 /* If we have previously collapsed any line-continuations,
167 * then we want to insert additional newlines at the next
168 * occurrence of a newline character to avoid changing any
169 * line numbers.
170 */
171 if (collapsed_newlines) {
172 cr = strchr (search_start, '\r');
173 lf = strchr (search_start, '\n');
174 if (cr && lf)
175 newline = cr < lf ? cr : lf;
176 else if (cr)
177 newline = cr;
178 else
179 newline = lf;
180 if (newline &&
181 (backslash == NULL || newline < backslash))
182 {
183 ralloc_strncat(&clean, shader,
184 newline - shader + 1);
185 while (collapsed_newlines) {
186 ralloc_strcat(&clean, newline_separator);
187 collapsed_newlines--;
188 }
189 shader = skip_newline (newline);
190 search_start = shader;
191 }
192 }
193
194 search_start = backslash + 1;
195
196 if (backslash == NULL)
197 break;
198
199 /* At each line continuation, (backslash followed by a
200 * newline), copy all preceding text to the output, then
201 * advance the shader pointer to the character after the
202 * newline.
203 */
204 if (backslash[1] == '\r' || backslash[1] == '\n')
205 {
206 collapsed_newlines++;
207 ralloc_strncat(&clean, shader, backslash - shader);
208 shader = skip_newline (backslash + 1);
209 search_start = shader;
210 }
211
212 backslash = strchr(search_start, '\\');
213 }
214
215 ralloc_strcat(&clean, shader);
216
217 return clean;
218 }
219
220 int
221 glcpp_preprocess(void *ralloc_ctx, const char **shader, char **info_log,
222 glcpp_extension_iterator extensions, void *state,
223 struct gl_context *gl_ctx)
224 {
225 int errors;
226 glcpp_parser_t *parser =
227 glcpp_parser_create(&gl_ctx->Extensions, extensions, state, gl_ctx->API);
228
229 if (! gl_ctx->Const.DisableGLSLLineContinuations)
230 *shader = remove_line_continuations(parser, *shader);
231
232 glcpp_lex_set_source_string (parser, *shader);
233
234 glcpp_parser_parse (parser);
235
236 if (parser->skip_stack)
237 glcpp_error (&parser->skip_stack->loc, parser, "Unterminated #if\n");
238
239 glcpp_parser_resolve_implicit_version(parser);
240
241 ralloc_strcat(info_log, parser->info_log);
242
243 ralloc_steal(ralloc_ctx, parser->output);
244 *shader = parser->output;
245
246 errors = parser->error;
247 glcpp_parser_destroy (parser);
248 return errors;
249 }