1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License
4 * as published by the Free Software Foundation; either version 2
5 * of the License, or (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * GNU General Public License for more details.
10 *
11 * You should have received a copy of the GNU General Public License
12 * along with this program; if not, write to the Free Software Foundation,
13 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
14 */
15
16 /** \file
17 * \ingroup sptext
18 */
19
20 #include <string.h>
21
22 #include "BLI_blenlib.h"
23
24 #include "DNA_space_types.h"
25 #include "DNA_text_types.h"
26
27 #include "BKE_text.h"
28
29 #include "text_format.h"
30
31 /* *** Local Functions (for format_line) *** */
32
txtfmt_osl_find_builtinfunc(const char * string)33 static int txtfmt_osl_find_builtinfunc(const char *string)
34 {
35 int i, len;
36
37 /* Keep aligned args for readability. */
38 /* clang-format off */
39
40 /* list is from
41 * https://github.com/imageworks/OpenShadingLanguage/raw/master/src/doc/osl-languagespec.pdf
42 */
43 if (STR_LITERAL_STARTSWITH(string, "break", len)) { i = len;
44 } else if (STR_LITERAL_STARTSWITH(string, "closure", len)) { i = len;
45 } else if (STR_LITERAL_STARTSWITH(string, "color", len)) { i = len;
46 } else if (STR_LITERAL_STARTSWITH(string, "continue", len)) { i = len;
47 } else if (STR_LITERAL_STARTSWITH(string, "do", len)) { i = len;
48 } else if (STR_LITERAL_STARTSWITH(string, "else", len)) { i = len;
49 } else if (STR_LITERAL_STARTSWITH(string, "emit", len)) { i = len;
50 } else if (STR_LITERAL_STARTSWITH(string, "float", len)) { i = len;
51 } else if (STR_LITERAL_STARTSWITH(string, "for", len)) { i = len;
52 } else if (STR_LITERAL_STARTSWITH(string, "if", len)) { i = len;
53 } else if (STR_LITERAL_STARTSWITH(string, "illuminance", len)) { i = len;
54 } else if (STR_LITERAL_STARTSWITH(string, "illuminate", len)) { i = len;
55 } else if (STR_LITERAL_STARTSWITH(string, "int", len)) { i = len;
56 } else if (STR_LITERAL_STARTSWITH(string, "matrix", len)) { i = len;
57 } else if (STR_LITERAL_STARTSWITH(string, "normal", len)) { i = len;
58 } else if (STR_LITERAL_STARTSWITH(string, "output", len)) { i = len;
59 } else if (STR_LITERAL_STARTSWITH(string, "point", len)) { i = len;
60 } else if (STR_LITERAL_STARTSWITH(string, "public", len)) { i = len;
61 } else if (STR_LITERAL_STARTSWITH(string, "return", len)) { i = len;
62 } else if (STR_LITERAL_STARTSWITH(string, "string", len)) { i = len;
63 } else if (STR_LITERAL_STARTSWITH(string, "struct", len)) { i = len;
64 } else if (STR_LITERAL_STARTSWITH(string, "vector", len)) { i = len;
65 } else if (STR_LITERAL_STARTSWITH(string, "void", len)) { i = len;
66 } else if (STR_LITERAL_STARTSWITH(string, "while", len)) { i = len;
67 } else { i = 0;
68 }
69
70 /* clang-format on */
71
72 /* If next source char is an identifier (eg. 'i' in "definite") no match */
73 if (i == 0 || text_check_identifier(string[i])) {
74 return -1;
75 }
76 return i;
77 }
78
txtfmt_osl_find_reserved(const char * string)79 static int txtfmt_osl_find_reserved(const char *string)
80 {
81 int i, len;
82
83 /* Keep aligned args for readability. */
84 /* clang-format off */
85
86 /* list is from...
87 * https://github.com/imageworks/OpenShadingLanguage/raw/master/src/doc/osl-languagespec.pdf
88 */
89 if (STR_LITERAL_STARTSWITH(string, "bool", len)) { i = len;
90 } else if (STR_LITERAL_STARTSWITH(string, "case", len)) { i = len;
91 } else if (STR_LITERAL_STARTSWITH(string, "catch", len)) { i = len;
92 } else if (STR_LITERAL_STARTSWITH(string, "char", len)) { i = len;
93 } else if (STR_LITERAL_STARTSWITH(string, "const", len)) { i = len;
94 } else if (STR_LITERAL_STARTSWITH(string, "delete", len)) { i = len;
95 } else if (STR_LITERAL_STARTSWITH(string, "default", len)) { i = len;
96 } else if (STR_LITERAL_STARTSWITH(string, "double", len)) { i = len;
97 } else if (STR_LITERAL_STARTSWITH(string, "enum", len)) { i = len;
98 } else if (STR_LITERAL_STARTSWITH(string, "extern", len)) { i = len;
99 } else if (STR_LITERAL_STARTSWITH(string, "false", len)) { i = len;
100 } else if (STR_LITERAL_STARTSWITH(string, "friend", len)) { i = len;
101 } else if (STR_LITERAL_STARTSWITH(string, "goto", len)) { i = len;
102 } else if (STR_LITERAL_STARTSWITH(string, "inline", len)) { i = len;
103 } else if (STR_LITERAL_STARTSWITH(string, "long", len)) { i = len;
104 } else if (STR_LITERAL_STARTSWITH(string, "new", len)) { i = len;
105 } else if (STR_LITERAL_STARTSWITH(string, "operator", len)) { i = len;
106 } else if (STR_LITERAL_STARTSWITH(string, "private", len)) { i = len;
107 } else if (STR_LITERAL_STARTSWITH(string, "protected", len)) { i = len;
108 } else if (STR_LITERAL_STARTSWITH(string, "short", len)) { i = len;
109 } else if (STR_LITERAL_STARTSWITH(string, "signed", len)) { i = len;
110 } else if (STR_LITERAL_STARTSWITH(string, "sizeof", len)) { i = len;
111 } else if (STR_LITERAL_STARTSWITH(string, "static", len)) { i = len;
112 } else if (STR_LITERAL_STARTSWITH(string, "switch", len)) { i = len;
113 } else if (STR_LITERAL_STARTSWITH(string, "template", len)) { i = len;
114 } else if (STR_LITERAL_STARTSWITH(string, "this", len)) { i = len;
115 } else if (STR_LITERAL_STARTSWITH(string, "throw", len)) { i = len;
116 } else if (STR_LITERAL_STARTSWITH(string, "true", len)) { i = len;
117 } else if (STR_LITERAL_STARTSWITH(string, "try", len)) { i = len;
118 } else if (STR_LITERAL_STARTSWITH(string, "typedef", len)) { i = len;
119 } else if (STR_LITERAL_STARTSWITH(string, "uniform", len)) { i = len;
120 } else if (STR_LITERAL_STARTSWITH(string, "union", len)) { i = len;
121 } else if (STR_LITERAL_STARTSWITH(string, "unsigned", len)) { i = len;
122 } else if (STR_LITERAL_STARTSWITH(string, "varying", len)) { i = len;
123 } else if (STR_LITERAL_STARTSWITH(string, "virtual", len)) { i = len;
124 } else if (STR_LITERAL_STARTSWITH(string, "volatile", len)) { i = len;
125 } else { i = 0;
126 }
127
128 /* clang-format on */
129
130 /* If next source char is an identifier (eg. 'i' in "definite") no match */
131 if (i == 0 || text_check_identifier(string[i])) {
132 return -1;
133 }
134 return i;
135 }
136
137 /* Checks the specified source string for a OSL special name. This name must
138 * start at the beginning of the source string and must be followed by a non-
139 * identifier (see text_check_identifier(char)) or null character.
140 *
141 * If a special name is found, the length of the matching name is returned.
142 * Otherwise, -1 is returned. */
143
txtfmt_osl_find_specialvar(const char * string)144 static int txtfmt_osl_find_specialvar(const char *string)
145 {
146 int i, len;
147
148 /* Keep aligned args for readability. */
149 /* clang-format off */
150
151 /* OSL shader types */
152 if (STR_LITERAL_STARTSWITH(string, "shader", len)) { i = len;
153 } else if (STR_LITERAL_STARTSWITH(string, "surface", len)) { i = len;
154 } else if (STR_LITERAL_STARTSWITH(string, "volume", len)) { i = len;
155 } else if (STR_LITERAL_STARTSWITH(string, "displacement", len)) { i = len;
156 } else { i = 0;
157 }
158
159 /* clang-format on */
160
161 /* If next source char is an identifier (eg. 'i' in "definite") no match */
162 if (i == 0 || text_check_identifier(string[i])) {
163 return -1;
164 }
165 return i;
166 }
167
168 /* matches py 'txtfmt_osl_find_decorator' */
txtfmt_osl_find_preprocessor(const char * string)169 static int txtfmt_osl_find_preprocessor(const char *string)
170 {
171 if (string[0] == '#') {
172 int i = 1;
173 /* Whitespace is ok '# foo' */
174 while (text_check_whitespace(string[i])) {
175 i++;
176 }
177 while (text_check_identifier(string[i])) {
178 i++;
179 }
180 return i;
181 }
182 return -1;
183 }
184
txtfmt_osl_format_identifier(const char * str)185 static char txtfmt_osl_format_identifier(const char *str)
186 {
187 char fmt;
188
189 /* Keep aligned args for readability. */
190 /* clang-format off */
191
192 if ((txtfmt_osl_find_specialvar(str)) != -1) { fmt = FMT_TYPE_SPECIAL;
193 } else if ((txtfmt_osl_find_builtinfunc(str)) != -1) { fmt = FMT_TYPE_KEYWORD;
194 } else if ((txtfmt_osl_find_reserved(str)) != -1) { fmt = FMT_TYPE_RESERVED;
195 } else if ((txtfmt_osl_find_preprocessor(str)) != -1) { fmt = FMT_TYPE_DIRECTIVE;
196 } else { fmt = FMT_TYPE_DEFAULT;
197 }
198
199 /* clang-format on */
200
201 return fmt;
202 }
203
txtfmt_osl_format_line(SpaceText * st,TextLine * line,const bool do_next)204 static void txtfmt_osl_format_line(SpaceText *st, TextLine *line, const bool do_next)
205 {
206 FlattenString fs;
207 const char *str;
208 char *fmt;
209 char cont_orig, cont, find, prev = ' ';
210 int len, i;
211
212 /* Get continuation from previous line */
213 if (line->prev && line->prev->format != NULL) {
214 fmt = line->prev->format;
215 cont = fmt[strlen(fmt) + 1]; /* Just after the null-terminator */
216 BLI_assert((FMT_CONT_ALL & cont) == cont);
217 }
218 else {
219 cont = FMT_CONT_NOP;
220 }
221
222 /* Get original continuation from this line */
223 if (line->format != NULL) {
224 fmt = line->format;
225 cont_orig = fmt[strlen(fmt) + 1]; /* Just after the null-terminator */
226 BLI_assert((FMT_CONT_ALL & cont_orig) == cont_orig);
227 }
228 else {
229 cont_orig = 0xFF;
230 }
231
232 len = flatten_string(st, &fs, line->line);
233 str = fs.buf;
234 if (!text_check_format_len(line, len)) {
235 flatten_string_free(&fs);
236 return;
237 }
238 fmt = line->format;
239
240 while (*str) {
241 /* Handle escape sequences by skipping both \ and next char */
242 if (*str == '\\') {
243 *fmt = prev;
244 fmt++;
245 str++;
246 if (*str == '\0') {
247 break;
248 }
249 *fmt = prev;
250 fmt++;
251 str += BLI_str_utf8_size_safe(str);
252 continue;
253 }
254 /* Handle continuations */
255 if (cont) {
256 /* C-Style comments */
257 if (cont & FMT_CONT_COMMENT_C) {
258 if (*str == '*' && *(str + 1) == '/') {
259 *fmt = FMT_TYPE_COMMENT;
260 fmt++;
261 str++;
262 *fmt = FMT_TYPE_COMMENT;
263 cont = FMT_CONT_NOP;
264 }
265 else {
266 *fmt = FMT_TYPE_COMMENT;
267 }
268 /* Handle other comments */
269 }
270 else {
271 find = (cont & FMT_CONT_QUOTEDOUBLE) ? '"' : '\'';
272 if (*str == find) {
273 cont = 0;
274 }
275 *fmt = FMT_TYPE_STRING;
276 }
277
278 str += BLI_str_utf8_size_safe(str) - 1;
279 }
280 /* Not in a string... */
281 else {
282 /* Deal with comments first */
283 if (*str == '/' && *(str + 1) == '/') {
284 /* fill the remaining line */
285 text_format_fill(&str, &fmt, FMT_TYPE_COMMENT, len - (int)(fmt - line->format));
286 }
287 /* C-Style (multi-line) comments */
288 else if (*str == '/' && *(str + 1) == '*') {
289 cont = FMT_CONT_COMMENT_C;
290 *fmt = FMT_TYPE_COMMENT;
291 fmt++;
292 str++;
293 *fmt = FMT_TYPE_COMMENT;
294 }
295 else if (*str == '"' || *str == '\'') {
296 /* Strings */
297 find = *str;
298 cont = (*str == '"') ? FMT_CONT_QUOTEDOUBLE : FMT_CONT_QUOTESINGLE;
299 *fmt = FMT_TYPE_STRING;
300 }
301 /* Whitespace (all ws. has been converted to spaces) */
302 else if (*str == ' ') {
303 *fmt = FMT_TYPE_WHITESPACE;
304 }
305 /* Numbers (digits not part of an identifier and periods followed by digits) */
306 else if ((prev != FMT_TYPE_DEFAULT && text_check_digit(*str)) ||
307 (*str == '.' && text_check_digit(*(str + 1)))) {
308 *fmt = FMT_TYPE_NUMERAL;
309 }
310 /* Punctuation */
311 else if ((*str != '#') && text_check_delim(*str)) {
312 *fmt = FMT_TYPE_SYMBOL;
313 }
314 /* Identifiers and other text (no previous ws. or delims. so text continues) */
315 else if (prev == FMT_TYPE_DEFAULT) {
316 str += BLI_str_utf8_size_safe(str) - 1;
317 *fmt = FMT_TYPE_DEFAULT;
318 }
319 /* Not ws, a digit, punct, or continuing text. Must be new, check for special words */
320 else {
321 /* Keep aligned args for readability. */
322 /* clang-format off */
323
324 /* Special vars(v) or built-in keywords(b) */
325 /* keep in sync with 'txtfmt_osl_format_identifier()' */
326 if ((i = txtfmt_osl_find_specialvar(str)) != -1) { prev = FMT_TYPE_SPECIAL;
327 } else if ((i = txtfmt_osl_find_builtinfunc(str)) != -1) { prev = FMT_TYPE_KEYWORD;
328 } else if ((i = txtfmt_osl_find_reserved(str)) != -1) { prev = FMT_TYPE_RESERVED;
329 } else if ((i = txtfmt_osl_find_preprocessor(str)) != -1) { prev = FMT_TYPE_DIRECTIVE;
330 }
331
332 /* clang-format on */
333
334 if (i > 0) {
335 if (prev == FMT_TYPE_DIRECTIVE) { /* can contain utf8 */
336 text_format_fill(&str, &fmt, prev, i);
337 }
338 else {
339 text_format_fill_ascii(&str, &fmt, prev, i);
340 }
341 }
342 else {
343 str += BLI_str_utf8_size_safe(str) - 1;
344 *fmt = FMT_TYPE_DEFAULT;
345 }
346 }
347 }
348 prev = *fmt;
349 fmt++;
350 str++;
351 }
352
353 /* Terminate and add continuation char */
354 *fmt = '\0';
355 fmt++;
356 *fmt = cont;
357
358 /* If continuation has changed and we're allowed, process the next line */
359 if (cont != cont_orig && do_next && line->next) {
360 txtfmt_osl_format_line(st, line->next, do_next);
361 }
362
363 flatten_string_free(&fs);
364 }
365
ED_text_format_register_osl(void)366 void ED_text_format_register_osl(void)
367 {
368 static TextFormatType tft = {NULL};
369 static const char *ext[] = {"osl", NULL};
370
371 tft.format_identifier = txtfmt_osl_format_identifier;
372 tft.format_line = txtfmt_osl_format_line;
373 tft.ext = ext;
374
375 ED_text_format_register(&tft);
376 }
377