c526f37719c308020c737831a1dc8f74716ac35a
[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 search_start = shader;
121
122 /* Determine what flavor of newlines this shader is using. GLSL
123 * provides for 4 different possible ways to separate lines, (using
124 * one or two characters):
125 *
126 * "\n" (line-feed, like Linux, Unix, and new Mac OS)
127 * "\r" (carriage-return, like old Mac files)
128 * "\r\n" (carriage-return + line-feed, like DOS files)
129 * "\n\r" (line-feed + carriage-return, like nothing, really)
130 *
131 * This code explicitly supports a shader that uses a mixture of
132 * newline terminators and will properly handle line continuation
133 * backslashes followed by any of the above.
134 *
135 * But, since we must also insert additional newlines in the output
136 * (for any collapsed lines) we attempt to maintain consistency by
137 * examining the first encountered newline terminator, and using the
138 * same terminator for any newlines we insert.
139 */
140 cr = strchr(search_start, '\r');
141 lf = strchr(search_start, '\n');
142
143 newline_separator[0] = '\n';
144 newline_separator[1] = '\0';
145 newline_separator[2] = '\0';
146
147 if (cr == NULL) {
148 /* Nothing to do. */
149 } else if (lf == NULL) {
150 newline_separator[0] = '\r';
151 } else if (lf == cr + 1) {
152 newline_separator[0] = '\r';
153 newline_separator[1] = '\n';
154 } else if (cr == lf + 1) {
155 newline_separator[0] = '\n';
156 newline_separator[1] = '\r';
157 }
158
159 while (true) {
160 backslash = strchr(search_start, '\\');
161
162 /* If we have previously collapsed any line-continuations,
163 * then we want to insert additional newlines at the next
164 * occurrence of a newline character to avoid changing any
165 * line numbers.
166 */
167 if (collapsed_newlines) {
168 cr = strchr (search_start, '\r');
169 lf = strchr (search_start, '\n');
170 if (cr && lf)
171 newline = cr < lf ? cr : lf;
172 else if (cr)
173 newline = cr;
174 else
175 newline = lf;
176 if (newline &&
177 (backslash == NULL || newline < backslash))
178 {
179 ralloc_strncat(&clean, shader,
180 newline - shader + 1);
181 while (collapsed_newlines) {
182 ralloc_strcat(&clean, newline_separator);
183 collapsed_newlines--;
184 }
185 shader = skip_newline (newline);
186 search_start = shader;
187 }
188 }
189
190 search_start = backslash + 1;
191
192 if (backslash == NULL)
193 break;
194
195 /* At each line continuation, (backslash followed by a
196 * newline), copy all preceding text to the output, then
197 * advance the shader pointer to the character after the
198 * newline.
199 */
200 if (backslash[1] == '\r' || backslash[1] == '\n')
201 {
202 collapsed_newlines++;
203 ralloc_strncat(&clean, shader, backslash - shader);
204 shader = skip_newline (backslash + 1);
205 search_start = shader;
206 }
207 }
208
209 ralloc_strcat(&clean, shader);
210
211 return clean;
212 }
213
214 int
215 glcpp_preprocess(void *ralloc_ctx, const char **shader, char **info_log,
216 glcpp_extension_iterator extensions, void *state,
217 struct gl_context *gl_ctx)
218 {
219 int errors;
220 glcpp_parser_t *parser =
221 glcpp_parser_create(&gl_ctx->Extensions, extensions, state, gl_ctx->API);
222
223 if (! gl_ctx->Const.DisableGLSLLineContinuations)
224 *shader = remove_line_continuations(parser, *shader);
225
226 glcpp_lex_set_source_string (parser, *shader);
227
228 glcpp_parser_parse (parser);
229
230 if (parser->skip_stack)
231 glcpp_error (&parser->skip_stack->loc, parser, "Unterminated #if\n");
232
233 glcpp_parser_resolve_implicit_version(parser);
234
235 ralloc_strcat(info_log, parser->info_log);
236
237 ralloc_steal(ralloc_ctx, parser->output);
238 *shader = parser->output;
239
240 errors = parser->error;
241 glcpp_parser_destroy (parser);
242 return errors;
243 }