GLSLDecoder.js 23 KB

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