GLSLDecoder.js 19 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028
  1. import { Program, FunctionDeclaration, For, AccessorElements, Ternary, Varying, DynamicElement, StaticElement, FunctionParameter, Unary, Conditional, VariableDeclaration, Operator, Number, String, FunctionCall, Return, Accessor, Uniform, Discard } from './AST.js';
  2. const unaryOperators = [
  3. '+', '-', '~', '!', '++', '--'
  4. ];
  5. const precedenceOperators = [
  6. '*', '/', '%',
  7. '-', '+',
  8. '<<', '>>',
  9. '<', '>', '<=', '>=',
  10. '==', '!=',
  11. '&',
  12. '^',
  13. '|',
  14. '&&',
  15. '^^',
  16. '||',
  17. '?',
  18. '=',
  19. '+=', '-=', '*=', '/=', '%=', '^=', '&=', '|=', '<<=', '>>=',
  20. ','
  21. ].reverse();
  22. const associativityRightToLeft = [
  23. '=',
  24. '+=', '-=', '*=', '/=', '%=', '^=', '&=', '|=', '<<=', '>>=',
  25. ',',
  26. '?',
  27. ':'
  28. ];
  29. const glslToTSL = {
  30. inversesqrt: 'inverseSqrt'
  31. };
  32. const samplers = [ 'sampler1D', 'sampler2D', 'sampler2DArray', 'sampler2DShadow', 'sampler2DArrayShadow', 'isampler2D', 'isampler2DArray', 'usampler2D', 'usampler2DArray' ];
  33. const samplersCube = [ 'samplerCube', 'samplerCubeShadow', 'usamplerCube', 'isamplerCube' ];
  34. const samplers3D = [ 'sampler3D', 'isampler3D', 'usampler3D' ];
  35. const spaceRegExp = /^((\t| )\n*)+/;
  36. const lineRegExp = /^\n+/;
  37. const commentRegExp = /^\/\*[\s\S]*?\*\//;
  38. const inlineCommentRegExp = /^\/\/.*?(\n|$)/;
  39. const numberRegExp = /^((0x\w+)|(\.?\d+\.?\d*((e-?\d+)|\w)?))/;
  40. const stringDoubleRegExp = /^(\"((?:[^"\\]|\\.)*)\")/;
  41. const stringSingleRegExp = /^(\'((?:[^'\\]|\\.)*)\')/;
  42. const literalRegExp = /^[A-Za-z](\w|\.)*/;
  43. const operatorsRegExp = new RegExp( '^(\\' + [
  44. '<<=', '>>=', '++', '--', '<<', '>>', '+=', '-=', '*=', '/=', '%=', '&=', '^^', '^=', '|=',
  45. '<=', '>=', '==', '!=', '&&', '||',
  46. '(', ')', '[', ']', '{', '}',
  47. '.', ',', ';', '!', '=', '~', '*', '/', '%', '+', '-', '<', '>', '&', '^', '|', '?', ':', '#'
  48. ].join( '$' ).split( '' ).join( '\\' ).replace( /\\\$/g, '|' ) + ')' );
  49. function getFunctionName( str ) {
  50. return glslToTSL[ str ] || str;
  51. }
  52. function getGroupDelta( str ) {
  53. if ( str === '(' || str === '[' || str === '{' ) return 1;
  54. if ( str === ')' || str === ']' || str === '}' ) return - 1;
  55. return 0;
  56. }
  57. class Token {
  58. constructor( tokenizer, type, str, pos ) {
  59. this.tokenizer = tokenizer;
  60. this.type = type;
  61. this.str = str;
  62. this.pos = pos;
  63. this.tag = null;
  64. }
  65. get endPos() {
  66. return this.pos + this.str.length;
  67. }
  68. get isNumber() {
  69. return this.type === Token.NUMBER;
  70. }
  71. get isString() {
  72. return this.type === Token.STRING;
  73. }
  74. get isLiteral() {
  75. return this.type === Token.LITERAL;
  76. }
  77. get isOperator() {
  78. return this.type === Token.OPERATOR;
  79. }
  80. }
  81. Token.LINE = 'line';
  82. Token.COMMENT = 'comment';
  83. Token.NUMBER = 'number';
  84. Token.STRING = 'string';
  85. Token.LITERAL = 'literal';
  86. Token.OPERATOR = 'operator';
  87. const TokenParserList = [
  88. { type: Token.LINE, regexp: lineRegExp, isTag: true },
  89. { type: Token.COMMENT, regexp: commentRegExp, isTag: true },
  90. { type: Token.COMMENT, regexp: inlineCommentRegExp, isTag: true },
  91. { type: Token.NUMBER, regexp: numberRegExp },
  92. { type: Token.STRING, regexp: stringDoubleRegExp, group: 2 },
  93. { type: Token.STRING, regexp: stringSingleRegExp, group: 2 },
  94. { type: Token.LITERAL, regexp: literalRegExp },
  95. { type: Token.OPERATOR, regexp: operatorsRegExp }
  96. ];
  97. class Tokenizer {
  98. constructor( source ) {
  99. this.source = source;
  100. this.position = 0;
  101. this.tokens = [];
  102. }
  103. tokenize() {
  104. let token = this.readToken();
  105. while ( token ) {
  106. this.tokens.push( token );
  107. token = this.readToken();
  108. }
  109. return this;
  110. }
  111. skip( ...params ) {
  112. let remainingCode = this.source.substr( this.position );
  113. let i = params.length;
  114. while ( i -- ) {
  115. const skip = params[ i ].exec( remainingCode );
  116. const skipLength = skip ? skip[ 0 ].length : 0;
  117. if ( skipLength > 0 ) {
  118. this.position += skipLength;
  119. remainingCode = this.source.substr( this.position );
  120. // re-skip, new remainingCode is generated
  121. // maybe exist previous regexp non detected
  122. i = params.length;
  123. }
  124. }
  125. return remainingCode;
  126. }
  127. readToken() {
  128. const remainingCode = this.skip( spaceRegExp );
  129. for ( var i = 0; i < TokenParserList.length; i ++ ) {
  130. const parser = TokenParserList[ i ];
  131. const result = parser.regexp.exec( remainingCode );
  132. if ( result ) {
  133. const token = new Token( this, parser.type, result[ parser.group || 0 ], this.position );
  134. this.position += result[ 0 ].length;
  135. if ( parser.isTag ) {
  136. const nextToken = this.readToken();
  137. if ( nextToken ) {
  138. nextToken.tag = token;
  139. }
  140. return nextToken;
  141. }
  142. return token;
  143. }
  144. }
  145. }
  146. }
  147. const isType = ( str ) => /void|bool|float|u?int|mat[234]|mat[234]x[234]|(u|i|b)?vec[234]/.test( str );
  148. class GLSLDecoder {
  149. constructor() {
  150. this.index = 0;
  151. this.tokenizer = null;
  152. this.keywords = [];
  153. this._currentFunction = null;
  154. this.addPolyfill( 'gl_FragCoord', 'vec3 gl_FragCoord = vec3( screenCoordinate.x, screenCoordinate.y.oneMinus(), screenCoordinate.z );' );
  155. }
  156. addPolyfill( name, polyfill ) {
  157. this.keywords.push( { name, polyfill } );
  158. return this;
  159. }
  160. get tokens() {
  161. return this.tokenizer.tokens;
  162. }
  163. readToken() {
  164. return this.tokens[ this.index ++ ];
  165. }
  166. getToken( offset = 0 ) {
  167. return this.tokens[ this.index + offset ];
  168. }
  169. getTokensUntil( str, tokens, offset = 0 ) {
  170. const output = [];
  171. let groupIndex = 0;
  172. for ( let i = offset; i < tokens.length; i ++ ) {
  173. const token = tokens[ i ];
  174. groupIndex += getGroupDelta( token.str );
  175. output.push( token );
  176. if ( groupIndex === 0 && token.str === str ) {
  177. break;
  178. }
  179. }
  180. return output;
  181. }
  182. readTokensUntil( str ) {
  183. const tokens = this.getTokensUntil( str, this.tokens, this.index );
  184. this.index += tokens.length;
  185. return tokens;
  186. }
  187. parseExpressionFromTokens( tokens ) {
  188. if ( tokens.length === 0 ) return null;
  189. const firstToken = tokens[ 0 ];
  190. const lastToken = tokens[ tokens.length - 1 ];
  191. // precedence operators
  192. let groupIndex = 0;
  193. for ( const operator of precedenceOperators ) {
  194. const parseToken = ( i, inverse = false ) => {
  195. const token = tokens[ i ];
  196. groupIndex += getGroupDelta( token.str );
  197. if ( ! token.isOperator || i === 0 || i === tokens.length - 1 ) return;
  198. if ( groupIndex === 0 && token.str === operator ) {
  199. if ( operator === '?' ) {
  200. const conditionTokens = tokens.slice( 0, i );
  201. const leftTokens = this.getTokensUntil( ':', tokens, i + 1 ).slice( 0, - 1 );
  202. const rightTokens = tokens.slice( i + leftTokens.length + 2 );
  203. const condition = this.parseExpressionFromTokens( conditionTokens );
  204. const left = this.parseExpressionFromTokens( leftTokens );
  205. const right = this.parseExpressionFromTokens( rightTokens );
  206. return new Ternary( condition, left, right );
  207. } else {
  208. const left = this.parseExpressionFromTokens( tokens.slice( 0, i ) );
  209. const right = this.parseExpressionFromTokens( tokens.slice( i + 1, tokens.length ) );
  210. return this._evalOperator( new Operator( operator, left, right ) );
  211. }
  212. }
  213. if ( inverse ) {
  214. if ( groupIndex > 0 ) {
  215. return this.parseExpressionFromTokens( tokens.slice( i ) );
  216. }
  217. } else {
  218. if ( groupIndex < 0 ) {
  219. return this.parseExpressionFromTokens( tokens.slice( 0, i ) );
  220. }
  221. }
  222. };
  223. if ( associativityRightToLeft.includes( operator ) ) {
  224. for ( let i = 0; i < tokens.length; i ++ ) {
  225. const result = parseToken( i );
  226. if ( result ) return result;
  227. }
  228. } else {
  229. for ( let i = tokens.length - 1; i >= 0; i -- ) {
  230. const result = parseToken( i, true );
  231. if ( result ) return result;
  232. }
  233. }
  234. }
  235. // unary operators (before)
  236. if ( firstToken.isOperator ) {
  237. for ( const operator of unaryOperators ) {
  238. if ( firstToken.str === operator ) {
  239. const right = this.parseExpressionFromTokens( tokens.slice( 1 ) );
  240. return new Unary( operator, right );
  241. }
  242. }
  243. }
  244. // unary operators (after)
  245. if ( lastToken.isOperator ) {
  246. for ( const operator of unaryOperators ) {
  247. if ( lastToken.str === operator ) {
  248. const left = this.parseExpressionFromTokens( tokens.slice( 0, tokens.length - 1 ) );
  249. return new Unary( operator, left, true );
  250. }
  251. }
  252. }
  253. // groups
  254. if ( firstToken.str === '(' ) {
  255. const leftTokens = this.getTokensUntil( ')', tokens );
  256. const left = this.parseExpressionFromTokens( leftTokens.slice( 1, leftTokens.length - 1 ) );
  257. const operator = tokens[ leftTokens.length ];
  258. if ( operator ) {
  259. const rightTokens = tokens.slice( leftTokens.length + 1 );
  260. const right = this.parseExpressionFromTokens( rightTokens );
  261. return this._evalOperator( new Operator( operator.str, left, right ) );
  262. }
  263. return left;
  264. }
  265. // primitives and accessors
  266. if ( firstToken.isNumber ) {
  267. let type;
  268. const isHex = /^(0x)/.test( firstToken.str );
  269. if ( isHex ) type = 'int';
  270. else if ( /u$|U$/.test( firstToken.str ) ) type = 'uint';
  271. else if ( /f|e|\./.test( firstToken.str ) ) type = 'float';
  272. else type = 'int';
  273. let str = firstToken.str.replace( /u|U|i$/, '' );
  274. if ( isHex === false ) {
  275. str = str.replace( /f$/, '' );
  276. }
  277. return new Number( str, type );
  278. } else if ( firstToken.isString ) {
  279. return new String( firstToken.str );
  280. } else if ( firstToken.isLiteral ) {
  281. if ( firstToken.str === 'return' ) {
  282. return new Return( this.parseExpressionFromTokens( tokens.slice( 1 ) ) );
  283. } else if ( firstToken.str === 'discard' ) {
  284. return new Discard();
  285. }
  286. const secondToken = tokens[ 1 ];
  287. if ( secondToken ) {
  288. if ( secondToken.str === '(' ) {
  289. // function call
  290. const internalTokens = this.getTokensUntil( ')', tokens, 1 ).slice( 1, - 1 );
  291. const paramsTokens = this.parseFunctionParametersFromTokens( internalTokens );
  292. const functionCall = new FunctionCall( getFunctionName( firstToken.str ), paramsTokens );
  293. const accessTokens = tokens.slice( 3 + internalTokens.length );
  294. if ( accessTokens.length > 0 ) {
  295. const elements = this.parseAccessorElementsFromTokens( accessTokens );
  296. return new AccessorElements( functionCall, elements );
  297. }
  298. return functionCall;
  299. } else if ( secondToken.str === '[' ) {
  300. // array accessor
  301. const elements = this.parseAccessorElementsFromTokens( tokens.slice( 1 ) );
  302. return new AccessorElements( new Accessor( firstToken.str ), elements );
  303. }
  304. }
  305. return new Accessor( firstToken.str );
  306. }
  307. }
  308. parseAccessorElementsFromTokens( tokens ) {
  309. const elements = [];
  310. let currentTokens = tokens;
  311. while ( currentTokens.length > 0 ) {
  312. const token = currentTokens[ 0 ];
  313. if ( token.str === '[' ) {
  314. const accessorTokens = this.getTokensUntil( ']', currentTokens );
  315. const element = this.parseExpressionFromTokens( accessorTokens.slice( 1, accessorTokens.length - 1 ) );
  316. currentTokens = currentTokens.slice( accessorTokens.length );
  317. elements.push( new DynamicElement( element ) );
  318. } else if ( token.str === '.' ) {
  319. const accessorTokens = currentTokens.slice( 1, 2 );
  320. const element = this.parseExpressionFromTokens( accessorTokens );
  321. currentTokens = currentTokens.slice( 2 );
  322. elements.push( new StaticElement( element ) );
  323. } else {
  324. console.error( 'Unknown accessor expression', token );
  325. break;
  326. }
  327. }
  328. return elements;
  329. }
  330. parseFunctionParametersFromTokens( tokens ) {
  331. if ( tokens.length === 0 ) return [];
  332. const expression = this.parseExpressionFromTokens( tokens );
  333. const params = [];
  334. let current = expression;
  335. while ( current.type === ',' ) {
  336. params.push( current.left );
  337. current = current.right;
  338. }
  339. params.push( current );
  340. return params;
  341. }
  342. parseExpression() {
  343. const tokens = this.readTokensUntil( ';' );
  344. const exp = this.parseExpressionFromTokens( tokens.slice( 0, tokens.length - 1 ) );
  345. return exp;
  346. }
  347. parseFunctionParams( tokens ) {
  348. const params = [];
  349. for ( let i = 0; i < tokens.length; i ++ ) {
  350. const immutable = tokens[ i ].str === 'const';
  351. if ( immutable ) i ++;
  352. let qualifier = tokens[ i ].str;
  353. if ( /^(in|out|inout)$/.test( qualifier ) ) {
  354. i ++;
  355. } else {
  356. qualifier = null;
  357. }
  358. const type = tokens[ i ++ ].str;
  359. const name = tokens[ i ++ ].str;
  360. params.push( new FunctionParameter( type, name, qualifier, immutable ) );
  361. if ( tokens[ i ] && tokens[ i ].str !== ',' ) throw new Error( 'Expected ","' );
  362. }
  363. return params;
  364. }
  365. parseFunction() {
  366. const type = this.readToken().str;
  367. const name = this.readToken().str;
  368. const paramsTokens = this.readTokensUntil( ')' );
  369. const params = this.parseFunctionParams( paramsTokens.slice( 1, paramsTokens.length - 1 ) );
  370. const func = new FunctionDeclaration( type, name, params );
  371. this._currentFunction = func;
  372. this.parseBlock( func );
  373. this._currentFunction = null;
  374. return func;
  375. }
  376. parseVariablesFromToken( tokens, type ) {
  377. let index = 0;
  378. const immutable = tokens[ 0 ].str === 'const';
  379. if ( immutable ) index ++;
  380. type = type || tokens[ index ++ ].str;
  381. const name = tokens[ index ++ ].str;
  382. const token = tokens[ index ];
  383. let init = null;
  384. let next = null;
  385. if ( token ) {
  386. const initTokens = this.getTokensUntil( ',', tokens, index );
  387. if ( initTokens[ 0 ].str === '=' ) {
  388. const expressionTokens = initTokens.slice( 1 );
  389. if ( expressionTokens[ expressionTokens.length - 1 ].str === ',' ) expressionTokens.pop();
  390. init = this.parseExpressionFromTokens( expressionTokens );
  391. }
  392. const nextTokens = tokens.slice( initTokens.length + ( index - 1 ) );
  393. if ( nextTokens[ 0 ] && nextTokens[ 0 ].str === ',' ) {
  394. next = this.parseVariablesFromToken( nextTokens.slice( 1 ), type );
  395. }
  396. }
  397. const variable = new VariableDeclaration( type, name, init, next, immutable );
  398. return variable;
  399. }
  400. parseVariables() {
  401. const tokens = this.readTokensUntil( ';' );
  402. return this.parseVariablesFromToken( tokens.slice( 0, tokens.length - 1 ) );
  403. }
  404. parseUniform() {
  405. const tokens = this.readTokensUntil( ';' );
  406. let type = tokens[ 1 ].str;
  407. const name = tokens[ 2 ].str;
  408. // GLSL to TSL types
  409. if ( samplers.includes( type ) ) type = 'texture';
  410. else if ( samplersCube.includes( type ) ) type = 'cubeTexture';
  411. else if ( samplers3D.includes( type ) ) type = 'texture3D';
  412. return new Uniform( type, name );
  413. }
  414. parseVarying() {
  415. const tokens = this.readTokensUntil( ';' );
  416. const type = tokens[ 1 ].str;
  417. const name = tokens[ 2 ].str;
  418. return new Varying( type, name );
  419. }
  420. parseReturn() {
  421. this.readToken(); // skip 'return'
  422. const expression = this.parseExpression();
  423. return new Return( expression );
  424. }
  425. parseFor() {
  426. this.readToken(); // skip 'for'
  427. const forTokens = this.readTokensUntil( ')' ).slice( 1, - 1 );
  428. const initializationTokens = this.getTokensUntil( ';', forTokens, 0 ).slice( 0, - 1 );
  429. const conditionTokens = this.getTokensUntil( ';', forTokens, initializationTokens.length + 1 ).slice( 0, - 1 );
  430. const afterthoughtTokens = forTokens.slice( initializationTokens.length + conditionTokens.length + 2 );
  431. let initialization;
  432. if ( initializationTokens[ 0 ] && isType( initializationTokens[ 0 ].str ) ) {
  433. initialization = this.parseVariablesFromToken( initializationTokens );
  434. } else {
  435. initialization = this.parseExpressionFromTokens( initializationTokens );
  436. }
  437. const condition = this.parseExpressionFromTokens( conditionTokens );
  438. const afterthought = this.parseExpressionFromTokens( afterthoughtTokens );
  439. const statement = new For( initialization, condition, afterthought );
  440. if ( this.getToken().str === '{' ) {
  441. this.parseBlock( statement );
  442. } else {
  443. statement.body.push( this.parseExpression() );
  444. }
  445. return statement;
  446. }
  447. parseIf() {
  448. const parseIfExpression = () => {
  449. this.readToken(); // skip 'if'
  450. const condTokens = this.readTokensUntil( ')' );
  451. return this.parseExpressionFromTokens( condTokens.slice( 1, condTokens.length - 1 ) );
  452. };
  453. const parseIfBlock = ( cond ) => {
  454. if ( this.getToken().str === '{' ) {
  455. this.parseBlock( cond );
  456. } else {
  457. cond.body.push( this.parseExpression() );
  458. }
  459. };
  460. //
  461. const conditional = new Conditional( parseIfExpression() );
  462. parseIfBlock( conditional );
  463. //
  464. let current = conditional;
  465. while ( this.getToken() && this.getToken().str === 'else' ) {
  466. this.readToken(); // skip 'else'
  467. const previous = current;
  468. if ( this.getToken().str === 'if' ) {
  469. current = new Conditional( parseIfExpression() );
  470. } else {
  471. current = new Conditional();
  472. }
  473. previous.elseConditional = current;
  474. parseIfBlock( current );
  475. }
  476. return conditional;
  477. }
  478. parseBlock( scope ) {
  479. const firstToken = this.getToken();
  480. if ( firstToken.str === '{' ) {
  481. this.readToken(); // skip '{'
  482. }
  483. let groupIndex = 0;
  484. while ( this.index < this.tokens.length ) {
  485. const token = this.getToken();
  486. let statement = null;
  487. groupIndex += getGroupDelta( token.str );
  488. if ( groupIndex < 0 ) {
  489. this.readToken(); // skip '}'
  490. break;
  491. }
  492. //
  493. if ( token.isLiteral || token.isOperator ) {
  494. if ( token.str === 'const' ) {
  495. statement = this.parseVariables();
  496. } else if ( token.str === 'uniform' ) {
  497. statement = this.parseUniform();
  498. } else if ( token.str === 'varying' ) {
  499. statement = this.parseVarying();
  500. } else if ( isType( token.str ) ) {
  501. if ( this.getToken( 2 ).str === '(' ) {
  502. statement = this.parseFunction();
  503. } else {
  504. statement = this.parseVariables();
  505. }
  506. } else if ( token.str === 'return' ) {
  507. statement = this.parseReturn();
  508. } else if ( token.str === 'if' ) {
  509. statement = this.parseIf();
  510. } else if ( token.str === 'for' ) {
  511. statement = this.parseFor();
  512. } else {
  513. statement = this.parseExpression();
  514. }
  515. }
  516. if ( statement ) {
  517. scope.body.push( statement );
  518. } else {
  519. this.index ++;
  520. }
  521. }
  522. }
  523. _evalOperator( operator ) {
  524. if ( operator.type.includes( '=' ) ) {
  525. const parameter = this._getFunctionParameter( operator.left.property );
  526. if ( parameter !== undefined ) {
  527. // Parameters are immutable in WGSL
  528. parameter.immutable = false;
  529. }
  530. }
  531. return operator;
  532. }
  533. _getFunctionParameter( name ) {
  534. if ( this._currentFunction ) {
  535. for ( const param of this._currentFunction.params ) {
  536. if ( param.name === name ) {
  537. return param;
  538. }
  539. }
  540. }
  541. }
  542. parse( source ) {
  543. let polyfill = '';
  544. for ( const keyword of this.keywords ) {
  545. if ( new RegExp( `(^|\\b)${ keyword.name }($|\\b)`, 'gm' ).test( source ) ) {
  546. polyfill += keyword.polyfill + '\n';
  547. }
  548. }
  549. if ( polyfill ) {
  550. polyfill = '// Polyfills\n\n' + polyfill + '\n';
  551. }
  552. this.index = 0;
  553. this.tokenizer = new Tokenizer( polyfill + source ).tokenize();
  554. const program = new Program();
  555. this.parseBlock( program );
  556. return program;
  557. }
  558. }
  559. export default GLSLDecoder;