UNPKG

18.9 kBJavaScriptView Raw
1"use strict";
2
3Object.defineProperty(exports, "__esModule", {
4 value: true
5});
6exports.typeInfos = undefined;
7
8var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
9
10var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();
11
12exports.default = transformSource;
13
14var _string = require("glsl-tokenizer/string");
15
16var _string2 = _interopRequireDefault(_string);
17
18var _direct = require("glsl-parser/direct");
19
20var _direct2 = _interopRequireDefault(_direct);
21
22var _acceptedLicenses = require("./acceptedLicenses");
23
24var _acceptedLicenses2 = _interopRequireDefault(_acceptedLicenses);
25
26function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
27
28function extraPositionFromToken(token) {
29 var line = token.line,
30 column = token.column;
31
32 var out = {};
33 if (typeof line === "number") out.line = line;
34 if (typeof column === "number") out.column = column;
35 return out;
36}
37
38var whitelistMeta = ["author", "license"];
39
40var blacklistScope = ["main", "getToColor", "getFromColor", "ratio", "uv", "progress", "from", "to"];
41
42var reservedTransitionNames = ["new"];
43
44var typeInfos = exports.typeInfos = {
45 float: {
46 primitiveType: "float",
47 exampleValue: "0.7",
48 defaultValue: 0,
49 arity: 1
50 },
51 int: {
52 primitiveType: "int",
53 exampleValue: "42",
54 defaultValue: 0,
55 arity: 1
56 },
57 bool: {
58 primitiveType: "bool",
59 exampleValue: "true",
60 defaultValue: false,
61 arity: 1
62 },
63 sampler2D: {
64 primitiveType: "sampler2D",
65 defaultValue: null,
66 arity: 1
67 },
68 vec2: {
69 primitiveType: "float",
70 exampleValue: "vec2(1.1, 2.2)",
71 defaultValue: Array(2).fill(0),
72 arity: 2
73 },
74 vec3: {
75 primitiveType: "float",
76 exampleValue: "vec3(1.0, 0.7, 0.3)",
77 defaultValue: Array(3).fill(0),
78 arity: 3
79 },
80 vec4: {
81 primitiveType: "float",
82 exampleValue: "vec4(1.0, 0.7, 0.3, 0.9)",
83 defaultValue: Array(4).fill(0),
84 arity: 4
85 },
86 ivec2: {
87 primitiveType: "int",
88 exampleValue: "ivec2(1, 2)",
89 defaultValue: Array(2).fill(0),
90 arity: 2
91 },
92 ivec3: {
93 primitiveType: "int",
94 exampleValue: "ivec3(1, 2, 3)",
95 defaultValue: Array(3).fill(0),
96 arity: 3
97 },
98 ivec4: {
99 primitiveType: "int",
100 exampleValue: "ivec4(1, 2, 3, 4)",
101 defaultValue: Array(4).fill(0),
102 arity: 4
103 },
104 bvec2: {
105 primitiveType: "bool",
106 exampleValue: "bvec2(true, false)",
107 defaultValue: Array(2).fill(false),
108 arity: 2
109 },
110 bvec3: {
111 primitiveType: "bool",
112 exampleValue: "bvec3(true, false, true)",
113 defaultValue: Array(3).fill(false),
114 arity: 3
115 },
116 bvec4: {
117 primitiveType: "bool",
118 exampleValue: "bvec4(true, false, true, false)",
119 defaultValue: Array(4).fill(false),
120 arity: 4
121 },
122 mat2: {
123 primitiveType: "float",
124 exampleValue: "mat2(1.0)",
125 defaultValue: Array(4).fill(0),
126 arity: 4
127 },
128 mat3: {
129 primitiveType: "float",
130 exampleValue: "mat3(1.0)",
131 defaultValue: Array(9).fill(0),
132 arity: 9
133 },
134 mat4: {
135 primitiveType: "float",
136 exampleValue: "mat4(1.0)",
137 defaultValue: Array(16).fill(0),
138 arity: 16
139 }
140};
141
142function extractCommentNode(token) {
143 switch (token.type) {
144 case "line-comment":
145 return token.data.slice(2);
146 case "block-comment":
147 return token.data.slice(2, token.data.length - 2);
148 default:
149 return "";
150 }
151}
152
153function typeCheckTransitionFunction(node) {
154 node = node.parent;
155 if (node.type !== "function") {
156 return false;
157 }
158 // check return type
159 if (node.parent.token.data !== "vec4") {
160 return false;
161 }
162 // back to the function node, we'll check the args
163 node = node.children.find(function (n) {
164 return n.type === "functionargs";
165 });
166 if (!node) {
167 return false;
168 }
169 if (node.children.length !== 1) {
170 return false;
171 }
172 node = node.children[0];
173 if (node.type !== "decl") {
174 return false;
175 }
176 var args = node.children.filter(function (n) {
177 return n.type !== "placeholder";
178 });
179 if (args.length !== 2) {
180 return false;
181 }
182
183 var _args = _slicedToArray(args, 2),
184 keywordNode = _args[0],
185 decllist = _args[1];
186
187 if (keywordNode.type !== "keyword" || keywordNode.token.data !== "vec2") {
188 return false;
189 }
190 if (decllist.type !== "decllist" && decllist.children.length !== 1) {
191 return false;
192 }
193
194 var _decllist$children = _slicedToArray(decllist.children, 1),
195 identNode = _decllist$children[0];
196
197 if (identNode.type !== "ident") {
198 return false;
199 }
200 return true;
201}
202
203function transformSource(filename, glsl) {
204 var data = {
205 name: "",
206 author: null,
207 license: null,
208 paramsTypes: {},
209 defaultParams: {},
210 glsl: glsl
211 };
212 var errors = [];
213
214 var tokens = (0, _string2.default)(glsl);
215
216 var ast = void 0;
217 try {
218 ast = (0, _direct2.default)(tokens);
219 } catch (e) {
220 var _message = e.message;
221
222 var r = _message.split(" at line ");
223 var _line = 0;
224 if (r.length === 2) {
225 _line = parseInt(r[1], 10);
226 }
227 errors.push({
228 type: "error",
229 code: "GLT_GLSL_error",
230 message: "GLSL code error: " + e.message,
231 line: _line
232 });
233 }
234
235 if (ast) {
236 var forbiddenScopes = Object.keys(ast.scope).filter(function (key) {
237 return blacklistScope.includes(key);
238 });
239 forbiddenScopes.forEach(function (id) {
240 // $FlowFixMe
241 var token = ast.scope[id].token;
242 errors.push(_extends({
243 type: "error",
244 code: "GLT_reserved_variable_used",
245 message: "You have defined these forbidden variables in the scope: " + id + ". They are reserved for the wrapping code."
246 }, extraPositionFromToken(token)));
247 });
248
249 if (!ast.scope.transition) {
250 errors.push({
251 type: "error",
252 code: "GLT_transition_no_impl",
253 message: "'vec4 transition(vec2 uv)' function is not implemented"
254 });
255 } else {
256 if (!typeCheckTransitionFunction(ast.scope.transition)) {
257 errors.push(_extends({
258 type: "error",
259 code: "GLT_transition_wrong_type",
260 message: "transition must be a function with following signature: 'vec4 transition(vec2 uv)'"
261 }, extraPositionFromToken(ast.scope.transition.token)));
262 }
263 }
264 }
265
266 function parseUniformCommentDefault(comment, type, uniformId, uniformToken) {
267 comment = comment.trim();
268 if (comment.indexOf("=") !== 0) {
269 return;
270 }
271 var tokens = (0, _string2.default)(uniformId + " " + comment + ";");
272 // wrap with a more "valid" glsl
273
274 var ast = void 0;
275 try {
276 ast = (0, _direct2.default)(tokens);
277 } catch (e) {
278 errors.push(_extends({
279 type: "error",
280 code: "GLT_invalid_default_value",
281 message: "uniform '" + uniformId + "' default value '" + comment + "' does not parse as GLSL code: " + e.message
282 }, extraPositionFromToken(uniformToken)));
283 return;
284 }
285
286 var node = void 0;
287
288 (node = ast) && (node = node.type === "stmtlist" && node.children[0]) && (node = node.type === "stmt" && node.children[0]) && (node = node.type === "expr" && node.children[0]) && (node = node.type === "assign" && node.children[1]);
289
290 var valueNode = node;
291 if (!valueNode) {
292 errors.push(_extends({
293 type: "error",
294 code: "GLT_invalid_default_value",
295 message: "uniform '" + uniformId + "' has invalid format for default value. Got: '" + comment + "'. It should be an assignment in a comment.\nExample: uniform " + type + " " + uniformId + "; // = " + typeInfos[type].exampleValue
296 }, extraPositionFromToken(uniformToken)));
297 return;
298 }
299 var _typeInfos$type = typeInfos[type],
300 arity = _typeInfos$type.arity,
301 primitiveType = _typeInfos$type.primitiveType;
302
303
304 function literalToJSValue(literalNode) {
305 switch (primitiveType) {
306 case "float":
307 {
308 var f = parseFloat(literalNode.data, 10);
309 if (isNaN(f)) {
310 errors.push(_extends({
311 type: "error",
312 code: "GLT_invalid_default_value",
313 message: "uniform '" + uniformId + "' has invalid default value type. Expected a float but could not parseFloat it! Got: '" + literalNode.data + "'"
314 }, extraPositionFromToken(uniformToken)));
315 return;
316 }
317 return f;
318 }
319 case "int":
320 {
321 var i = parseInt(literalNode.data, 10);
322 if (isNaN(i)) {
323 errors.push(_extends({
324 type: "error",
325 code: "GLT_invalid_default_value",
326 message: "uniform '" + uniformId + "' has invalid default value type. Expected an int but could not parseInt it! Got: '" + literalNode.data + "'"
327 }, extraPositionFromToken(uniformToken)));
328 return;
329 }
330 return i;
331 }
332 case "bool":
333 {
334 switch (literalNode.data) {
335 case "1":
336 case "true":
337 return true;
338 case "0":
339 case "false":
340 return false;
341 default:
342 errors.push(_extends({
343 type: "error",
344 code: "GLT_invalid_default_value",
345 message: "uniform '" + uniformId + "' has invalid default value type. Expected a bool but could not parse it! Got: '" + literalNode.data + "'"
346 }, extraPositionFromToken(uniformToken)));
347 return;
348 }
349 }
350 default:
351 return;
352 }
353 }
354
355 if (valueNode.type === "call") {
356 var values = [];
357 for (var c = 0; c < valueNode.children.length; c++) {
358 var _node = valueNode.children[c];
359 switch (_node.type) {
360 case "keyword":
361 if (_node.data !== type) {
362 errors.push(_extends({
363 type: "error",
364 code: "GLT_invalid_default_value",
365 message: "uniform '" + uniformId + "' has invalid format for default value: the value type '" + _node.data + "' does not match the uniform type '" + type + "'. Got: '" + comment + "'.\nExample: uniform " + type + " " + uniformId + "; // = " + typeInfos[type].exampleValue
366 }, extraPositionFromToken(uniformToken)));
367 return;
368 }
369 break;
370 case "literal":
371 var v = literalToJSValue(_node);
372 if (v === undefined) return;
373 values.push(v);
374 break;
375 default:
376 errors.push(_extends({
377 type: "error",
378 code: "GLT_invalid_default_value",
379 message: "uniform '" + uniformId + "' has invalid format for default value: unsupported synthax. Got: '" + comment + "'.\nExample: uniform " + type + " " + uniformId + "; // = " + typeInfos[type].exampleValue
380 }, extraPositionFromToken(uniformToken)));
381 return;
382 }
383 }
384 if (arity === values.length) {
385 return values;
386 }
387 if (values.length === 1) {
388 return Array(arity).fill(values[0]);
389 } else {
390 errors.push(_extends({
391 type: "error",
392 code: "GLT_invalid_default_value",
393 message: "uniform '" + uniformId + "' has invalid format for default value: invalid arity of " + type + ". Got: '" + comment + "'.\nExample: uniform " + type + " " + uniformId + "; // = " + typeInfos[type].exampleValue
394 }, extraPositionFromToken(uniformToken)));
395 return;
396 }
397 } else if (valueNode.type === "literal" || valueNode.type === "keyword") {
398 if (arity !== 1) {
399 errors.push(_extends({
400 type: "error",
401 code: "GLT_invalid_default_value",
402 message: "uniform '" + uniformId + "' has invalid format for default value: you can't assign a literal value to a " + type + " type. Got: '" + comment + "'.\nExample: uniform " + type + " " + uniformId + "; // = " + typeInfos[type].exampleValue
403 }, extraPositionFromToken(uniformToken)));
404 } else {
405 return literalToJSValue(valueNode);
406 }
407 } else {
408 errors.push(_extends({
409 type: "error",
410 code: "GLT_invalid_default_value",
411 message: "uniform '" + uniformId + "' has invalid format for default value. Got: '" + comment + "'.\nExample: uniform " + type + " " + uniformId + "; // = " + typeInfos[type].exampleValue
412 }, extraPositionFromToken(uniformToken)));
413 }
414 }
415
416 for (var i = 0; i < tokens.length; i++) {
417 var token = tokens[i];
418 if (token.type === "keyword" && token.data === "uniform") {
419 var _ret = function () {
420 var uniformToken = token;
421 var idents = [],
422 ident = void 0,
423 typeTokens = [],
424 parsingType = true,
425 commentsPerIdent = {},
426 commentsAll = [];
427 // eat all the tokens until ";"
428 while (++i < tokens.length) {
429 var _token = tokens[i];
430 if (_token.type === "operator" && _token.data === ";") {
431 break;
432 }
433 if (_token.type === "block-comment" || _token.type === "line-comment") {
434 if (ident) {
435 commentsPerIdent[ident] = (commentsPerIdent[ident] || []).concat([extractCommentNode(_token)]);
436 }
437 continue;
438 }
439 if (_token.type === "ident") {
440 parsingType = false;
441 ident = _token.data;
442 idents.push(ident);
443 continue;
444 }
445 if (parsingType && _token.type !== "whitespace") {
446 typeTokens.push(_token);
447 continue;
448 }
449 }
450 // eat all the comments following the uniform
451 for (var j = i + 1; j < tokens.length; j++) {
452 var _token2 = tokens[j];
453 if (_token2.type === "whitespace") {
454 continue;
455 }
456 if (_token2.type === "block-comment" || _token2.type === "line-comment") {
457 commentsAll.push(extractCommentNode(_token2));
458 }
459 break;
460 }
461
462 idents.forEach(function (ident) {
463 var type = typeTokens.map(function (n) {
464 return n.data;
465 }).join("");
466 if (typeof type !== "string" || !(type in typeInfos)) {
467 errors.push(_extends({
468 type: "error",
469 code: "GLT_unsupported_param_value_type",
470 message: "uniform '" + ident + "' type '" + String(type) + "' is not supported"
471 }, extraPositionFromToken(uniformToken)));
472 } else {
473 // Extracting out uniform info
474 var defaultParam = void 0;
475
476 if (type === "sampler2D") {
477 // for sampler2D, we use null by convention, we can't parse anything.
478 defaultParam = null;
479 } else {
480 // try to parse one of the comment
481 var comments = (commentsPerIdent[ident] || []).concat(commentsAll);
482 for (var _j = 0; _j < comments.length && defaultParam === undefined; _j++) {
483 defaultParam = parseUniformCommentDefault(comments[_j], type, ident, uniformToken);
484 }
485 // fallback on a default value
486 if (defaultParam === undefined) {
487 errors.push(_extends({
488 type: "warn",
489 code: "GLT_no_default_param_value",
490 message: "uniform '" + ident + "' has not declared any commented default value.\nExample: uniform " + type + " " + ident + "; // = " + typeInfos[type].exampleValue + ";"
491 }, extraPositionFromToken(uniformToken)));
492 defaultParam = typeInfos[type].defaultValue;
493 }
494 }
495 data.defaultParams[ident] = defaultParam;
496 data.paramsTypes[ident] = type;
497 }
498 });
499 return "continue";
500 }();
501
502 if (_ret === "continue") continue;
503 }
504
505 if (token.type === "line-comment") {
506 // Extracting out meta
507 var com = token.data.slice(2);
508 var _m = com.match(/^(.*):(.*)$/);
509 if (_m && _m.length === 3) {
510 var _m2 = _slicedToArray(_m, 3),
511 _2 = _m2[0],
512 key = _m2[1],
513 value = _m2[2];
514
515 key = key.trim().toLowerCase();
516 value = value.trim();
517 if (whitelistMeta.indexOf(key) !== -1) {
518 data[key] = value;
519 }
520 }
521 continue;
522 }
523 }
524
525 whitelistMeta.forEach(function (key) {
526 if (!data[key]) {
527 errors.push({
528 type: "error",
529 code: "GLT_meta_missing",
530 message: "'" + key + "' is missing. Please define it in a '// " + key + ": ...' comment"
531 });
532 }
533 });
534
535 if (data.license && !(data.license in _acceptedLicenses2.default)) {
536 errors.push({
537 type: "error",
538 code: "GLT_unknown_license",
539 message: "'" + data.license + "' not found in supported licenses: " + Object.keys(_acceptedLicenses2.default).join(", ")
540 });
541 }
542
543 var m = filename.match(/^(.*).glsl$/);
544 if (m) {
545 var _name = m[1];
546 data.name = _name;
547 if (!_name) {
548 errors.push({
549 type: "error",
550 code: "GLT_invalid_filename",
551 message: "A transition filename is required!"
552 });
553 } else if (_name.length > 40) {
554 errors.push({
555 type: "error",
556 code: "GLT_invalid_filename",
557 message: "filename is too long"
558 });
559 } else if (!_name.match(/^[a-zA-Z0-9-_]+$/)) {
560 errors.push({
561 type: "error",
562 code: "GLT_invalid_filename",
563 message: "filename can only contains letters, numbers or - and _ characters. Got '" + filename + "'"
564 });
565 } else if (reservedTransitionNames.includes(_name)) {
566 errors.push({
567 type: "error",
568 code: "GLT_invalid_filename",
569 message: "filename cannot be called '" + _name + "'."
570 });
571 }
572 } else {
573 data.name = filename;
574 errors.push({
575 type: "error",
576 code: "GLT_invalid_filename",
577 message: "filename needs to ends with '.glsl'. Got '" + filename + "'"
578 });
579 }
580
581 return {
582 data: data,
583 errors: errors
584 };
585}
\No newline at end of file