Merge branch 'blender2.7'
[blender.git] / source / blender / editors / space_text / text_format_osl.c
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_text_types.h"
25 #include "DNA_space_types.h"
26
27 #include "BKE_text.h"
28
29 #include "text_format.h"
30
31 /* *** Local Functions (for format_line) *** */
32
33 static int txtfmt_osl_find_builtinfunc(const char *string)
34 {
35         int i, len;
36         /* list is from
37          * https://github.com/imageworks/OpenShadingLanguage/raw/master/src/doc/osl-languagespec.pdf
38          */
39         if      (STR_LITERAL_STARTSWITH(string, "break",        len)) i = len;
40         else if (STR_LITERAL_STARTSWITH(string, "closure",      len)) i = len;
41         else if (STR_LITERAL_STARTSWITH(string, "color",        len)) i = len;
42         else if (STR_LITERAL_STARTSWITH(string, "continue",     len)) i = len;
43         else if (STR_LITERAL_STARTSWITH(string, "do",           len)) i = len;
44         else if (STR_LITERAL_STARTSWITH(string, "else",         len)) i = len;
45         else if (STR_LITERAL_STARTSWITH(string, "emit",         len)) i = len;
46         else if (STR_LITERAL_STARTSWITH(string, "float",        len)) i = len;
47         else if (STR_LITERAL_STARTSWITH(string, "for",          len)) i = len;
48         else if (STR_LITERAL_STARTSWITH(string, "if",           len)) i = len;
49         else if (STR_LITERAL_STARTSWITH(string, "illuminance",  len)) i = len;
50         else if (STR_LITERAL_STARTSWITH(string, "illuminate",   len)) i = len;
51         else if (STR_LITERAL_STARTSWITH(string, "int",          len)) i = len;
52         else if (STR_LITERAL_STARTSWITH(string, "matrix",       len)) i = len;
53         else if (STR_LITERAL_STARTSWITH(string, "normal",       len)) i = len;
54         else if (STR_LITERAL_STARTSWITH(string, "output",       len)) i = len;
55         else if (STR_LITERAL_STARTSWITH(string, "point",        len)) i = len;
56         else if (STR_LITERAL_STARTSWITH(string, "public",       len)) i = len;
57         else if (STR_LITERAL_STARTSWITH(string, "return",       len)) i = len;
58         else if (STR_LITERAL_STARTSWITH(string, "string",       len)) i = len;
59         else if (STR_LITERAL_STARTSWITH(string, "struct",       len)) i = len;
60         else if (STR_LITERAL_STARTSWITH(string, "vector",       len)) i = len;
61         else if (STR_LITERAL_STARTSWITH(string, "void",         len)) i = len;
62         else if (STR_LITERAL_STARTSWITH(string, "while",        len)) i = len;
63         else                                                          i = 0;
64
65         /* If next source char is an identifier (eg. 'i' in "definite") no match */
66         if (i == 0 || text_check_identifier(string[i]))
67                 return -1;
68         return i;
69 }
70
71 static int txtfmt_osl_find_reserved(const char *string)
72 {
73         int i, len;
74         /* list is from...
75          * https://github.com/imageworks/OpenShadingLanguage/raw/master/src/doc/osl-languagespec.pdf
76          */
77         if      (STR_LITERAL_STARTSWITH(string, "bool",         len)) i = len;
78         else if (STR_LITERAL_STARTSWITH(string, "case",         len)) i = len;
79         else if (STR_LITERAL_STARTSWITH(string, "catch",        len)) i = len;
80         else if (STR_LITERAL_STARTSWITH(string, "char",         len)) i = len;
81         else if (STR_LITERAL_STARTSWITH(string, "const",        len)) i = len;
82         else if (STR_LITERAL_STARTSWITH(string, "delete",       len)) i = len;
83         else if (STR_LITERAL_STARTSWITH(string, "default",      len)) i = len;
84         else if (STR_LITERAL_STARTSWITH(string, "double",       len)) i = len;
85         else if (STR_LITERAL_STARTSWITH(string, "enum",         len)) i = len;
86         else if (STR_LITERAL_STARTSWITH(string, "extern",       len)) i = len;
87         else if (STR_LITERAL_STARTSWITH(string, "false",        len)) i = len;
88         else if (STR_LITERAL_STARTSWITH(string, "friend",       len)) i = len;
89         else if (STR_LITERAL_STARTSWITH(string, "goto",         len)) i = len;
90         else if (STR_LITERAL_STARTSWITH(string, "inline",       len)) i = len;
91         else if (STR_LITERAL_STARTSWITH(string, "long",         len)) i = len;
92         else if (STR_LITERAL_STARTSWITH(string, "new",          len)) i = len;
93         else if (STR_LITERAL_STARTSWITH(string, "operator",     len)) i = len;
94         else if (STR_LITERAL_STARTSWITH(string, "private",      len)) i = len;
95         else if (STR_LITERAL_STARTSWITH(string, "protected",    len)) i = len;
96         else if (STR_LITERAL_STARTSWITH(string, "short",        len)) i = len;
97         else if (STR_LITERAL_STARTSWITH(string, "signed",       len)) i = len;
98         else if (STR_LITERAL_STARTSWITH(string, "sizeof",       len)) i = len;
99         else if (STR_LITERAL_STARTSWITH(string, "static",       len)) i = len;
100         else if (STR_LITERAL_STARTSWITH(string, "switch",       len)) i = len;
101         else if (STR_LITERAL_STARTSWITH(string, "template",     len)) i = len;
102         else if (STR_LITERAL_STARTSWITH(string, "this",         len)) i = len;
103         else if (STR_LITERAL_STARTSWITH(string, "throw",        len)) i = len;
104         else if (STR_LITERAL_STARTSWITH(string, "true",         len)) i = len;
105         else if (STR_LITERAL_STARTSWITH(string, "try",          len)) i = len;
106         else if (STR_LITERAL_STARTSWITH(string, "typedef",      len)) i = len;
107         else if (STR_LITERAL_STARTSWITH(string, "uniform",      len)) i = len;
108         else if (STR_LITERAL_STARTSWITH(string, "union",        len)) i = len;
109         else if (STR_LITERAL_STARTSWITH(string, "unsigned",     len)) i = len;
110         else if (STR_LITERAL_STARTSWITH(string, "varying",      len)) i = len;
111         else if (STR_LITERAL_STARTSWITH(string, "virtual",      len)) i = len;
112         else if (STR_LITERAL_STARTSWITH(string, "volatile",     len)) i = len;
113         else                                                          i = 0;
114
115         /* If next source char is an identifier (eg. 'i' in "definite") no match */
116         if (i == 0 || text_check_identifier(string[i]))
117                 return -1;
118         return i;
119 }
120
121 /* Checks the specified source string for a OSL special name. This name must
122  * start at the beginning of the source string and must be followed by a non-
123  * identifier (see text_check_identifier(char)) or null character.
124  *
125  * If a special name is found, the length of the matching name is returned.
126  * Otherwise, -1 is returned. */
127
128 static int txtfmt_osl_find_specialvar(const char *string)
129 {
130         int i, len;
131
132         /* OSL shader types */
133         if      (STR_LITERAL_STARTSWITH(string, "shader",       len)) i = len;
134         else if (STR_LITERAL_STARTSWITH(string, "surface",      len)) i = len;
135         else if (STR_LITERAL_STARTSWITH(string, "volume",       len)) i = len;
136         else if (STR_LITERAL_STARTSWITH(string, "displacement", len)) i = len;
137         else                                                    i = 0;
138
139         /* If next source char is an identifier (eg. 'i' in "definite") no match */
140         if (i == 0 || text_check_identifier(string[i]))
141                 return -1;
142         return i;
143 }
144
145 /* matches py 'txtfmt_osl_find_decorator' */
146 static int txtfmt_osl_find_preprocessor(const char *string)
147 {
148         if (string[0] == '#') {
149                 int i = 1;
150                 /* Whitespace is ok '#  foo' */
151                 while (text_check_whitespace(string[i])) {
152                         i++;
153                 }
154                 while (text_check_identifier(string[i])) {
155                         i++;
156                 }
157                 return i;
158         }
159         return -1;
160 }
161
162 static char txtfmt_osl_format_identifier(const char *str)
163 {
164         char fmt;
165         if      ((txtfmt_osl_find_specialvar(str))   != -1) fmt = FMT_TYPE_SPECIAL;
166         else if ((txtfmt_osl_find_builtinfunc(str))  != -1) fmt = FMT_TYPE_KEYWORD;
167         else if ((txtfmt_osl_find_reserved(str))     != -1) fmt = FMT_TYPE_RESERVED;
168         else if ((txtfmt_osl_find_preprocessor(str)) != -1) fmt = FMT_TYPE_DIRECTIVE;
169         else                                                fmt = FMT_TYPE_DEFAULT;
170         return fmt;
171 }
172
173 static void txtfmt_osl_format_line(SpaceText *st, TextLine *line, const bool do_next)
174 {
175         FlattenString fs;
176         const char *str;
177         char *fmt;
178         char cont_orig, cont, find, prev = ' ';
179         int len, i;
180
181         /* Get continuation from previous line */
182         if (line->prev && line->prev->format != NULL) {
183                 fmt = line->prev->format;
184                 cont = fmt[strlen(fmt) + 1]; /* Just after the null-terminator */
185                 BLI_assert((FMT_CONT_ALL & cont) == cont);
186         }
187         else {
188                 cont = FMT_CONT_NOP;
189         }
190
191         /* Get original continuation from this line */
192         if (line->format != NULL) {
193                 fmt = line->format;
194                 cont_orig = fmt[strlen(fmt) + 1]; /* Just after the null-terminator */
195                 BLI_assert((FMT_CONT_ALL & cont_orig) == cont_orig);
196         }
197         else {
198                 cont_orig = 0xFF;
199         }
200
201         len = flatten_string(st, &fs, line->line);
202         str = fs.buf;
203         if (!text_check_format_len(line, len)) {
204                 flatten_string_free(&fs);
205                 return;
206         }
207         fmt = line->format;
208
209         while (*str) {
210                 /* Handle escape sequences by skipping both \ and next char */
211                 if (*str == '\\') {
212                         *fmt = prev; fmt++; str++;
213                         if (*str == '\0') break;
214                         *fmt = prev; fmt++; str += BLI_str_utf8_size_safe(str);
215                         continue;
216                 }
217                 /* Handle continuations */
218                 else if (cont) {
219                         /* C-Style comments */
220                         if (cont & FMT_CONT_COMMENT_C) {
221                                 if (*str == '*' && *(str + 1) == '/') {
222                                         *fmt = FMT_TYPE_COMMENT; fmt++; str++;
223                                         *fmt = FMT_TYPE_COMMENT;
224                                         cont = FMT_CONT_NOP;
225                                 }
226                                 else {
227                                         *fmt = FMT_TYPE_COMMENT;
228                                 }
229                                 /* Handle other comments */
230                         }
231                         else {
232                                 find = (cont & FMT_CONT_QUOTEDOUBLE) ? '"' : '\'';
233                                 if (*str == find) cont = 0;
234                                 *fmt = FMT_TYPE_STRING;
235                         }
236
237                         str += BLI_str_utf8_size_safe(str) - 1;
238                 }
239                 /* Not in a string... */
240                 else {
241                         /* Deal with comments first */
242                         if (*str == '/' && *(str + 1) == '/') {
243                                 /* fill the remaining line */
244                                 text_format_fill(&str, &fmt, FMT_TYPE_COMMENT, len - (int)(fmt - line->format));
245                         }
246                         /* C-Style (multi-line) comments */
247                         else if (*str == '/' && *(str + 1) == '*') {
248                                 cont = FMT_CONT_COMMENT_C;
249                                 *fmt = FMT_TYPE_COMMENT; fmt++; str++;
250                                 *fmt = FMT_TYPE_COMMENT;
251                         }
252                         else if (*str == '"' || *str == '\'') {
253                                 /* Strings */
254                                 find = *str;
255                                 cont = (*str == '"') ? FMT_CONT_QUOTEDOUBLE : FMT_CONT_QUOTESINGLE;
256                                 *fmt = FMT_TYPE_STRING;
257                         }
258                         /* Whitespace (all ws. has been converted to spaces) */
259                         else if (*str == ' ') {
260                                 *fmt = FMT_TYPE_WHITESPACE;
261                         }
262                         /* Numbers (digits not part of an identifier and periods followed by digits) */
263                         else if ((prev != FMT_TYPE_DEFAULT && text_check_digit(*str)) ||
264                                  (*str == '.' && text_check_digit(*(str + 1))))
265                         {
266                                 *fmt = FMT_TYPE_NUMERAL;
267                         }
268                         /* Punctuation */
269                         else if ((*str != '#') && text_check_delim(*str)) {
270                                 *fmt = FMT_TYPE_SYMBOL;
271                         }
272                         /* Identifiers and other text (no previous ws. or delims. so text continues) */
273                         else if (prev == FMT_TYPE_DEFAULT) {
274                                 str += BLI_str_utf8_size_safe(str) - 1;
275                                 *fmt = FMT_TYPE_DEFAULT;
276                         }
277                         /* Not ws, a digit, punct, or continuing text. Must be new, check for special words */
278                         else {
279                                 /* Special vars(v) or built-in keywords(b) */
280                                 /* keep in sync with 'txtfmt_osl_format_identifier()' */
281                                 if      ((i = txtfmt_osl_find_specialvar(str))   != -1) prev = FMT_TYPE_SPECIAL;
282                                 else if ((i = txtfmt_osl_find_builtinfunc(str))  != -1) prev = FMT_TYPE_KEYWORD;
283                                 else if ((i = txtfmt_osl_find_reserved(str))     != -1) prev = FMT_TYPE_RESERVED;
284                                 else if ((i = txtfmt_osl_find_preprocessor(str)) != -1) prev = FMT_TYPE_DIRECTIVE;
285
286                                 if (i > 0) {
287                                         if (prev == FMT_TYPE_DIRECTIVE) {  /* can contain utf8 */
288                                                 text_format_fill(&str, &fmt, prev, i);
289                                         }
290                                         else {
291                                                 text_format_fill_ascii(&str, &fmt, prev, i);
292                                         }
293                                 }
294                                 else {
295                                         str += BLI_str_utf8_size_safe(str) - 1;
296                                         *fmt = FMT_TYPE_DEFAULT;
297                                 }
298                         }
299                 }
300                 prev = *fmt; fmt++; str++;
301         }
302
303         /* Terminate and add continuation char */
304         *fmt = '\0'; fmt++;
305         *fmt = cont;
306
307         /* If continuation has changed and we're allowed, process the next line */
308         if (cont != cont_orig && do_next && line->next) {
309                 txtfmt_osl_format_line(st, line->next, do_next);
310         }
311
312         flatten_string_free(&fs);
313 }
314
315 void ED_text_format_register_osl(void)
316 {
317         static TextFormatType tft = {NULL};
318         static const char *ext[] = {"osl", NULL};
319
320         tft.format_identifier = txtfmt_osl_format_identifier;
321         tft.format_line       = txtfmt_osl_format_line;
322         tft.ext = ext;
323
324         ED_text_format_register(&tft);
325 }