| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171 |
- 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 } from './AST.js';
- import { isType } from './TranspilerUtils.js';
- const unaryOperators = [
- '+', '-', '~', '!', '++', '--'
- ];
- const arithmeticOperators = [
- '*', '/', '%', '+', '-', '<<', '>>'
- ];
- const precedenceOperators = [
- [ ',' ],
- [ '=', '+=', '-=', '*=', '/=', '%=', '^=', '&=', '|=', '<<=', '>>=' ],
- [ '?' ],
- [ '||' ],
- [ '^^' ],
- [ '&&' ],
- [ '|' ],
- [ '^' ],
- [ '&' ],
- [ '==', '!=' ],
- [ '<', '>', '<=', '>=' ],
- [ '<<', '>>' ],
- [ '+', '-' ],
- [ '*', '/', '%' ]
- ];
- const associativityRightToLeft = [
- '=',
- '+=', '-=', '*=', '/=', '%=', '^=', '&=', '|=', '<<=', '>>=',
- ',',
- '?',
- ':'
- ];
- const glslToTSL = {
- inversesqrt: 'inverseSqrt'
- };
- const samplers = [ 'sampler1D', 'sampler2D', 'sampler2DArray', 'sampler2DShadow', 'sampler2DArrayShadow', 'isampler2D', 'isampler2DArray', 'usampler2D', 'usampler2DArray' ];
- const samplersCube = [ 'samplerCube', 'samplerCubeShadow', 'usamplerCube', 'isamplerCube' ];
- const samplers3D = [ 'sampler3D', 'isampler3D', 'usampler3D' ];
- const spaceRegExp = /^((\t| )\n*)+/;
- const lineRegExp = /^\n+/;
- const commentRegExp = /^\/\*[\s\S]*?\*\//;
- const inlineCommentRegExp = /^\/\/.*?(?=\n|$)/;
- const numberRegExp = /^((0x\w+)|(\.?\d+\.?\d*((e-?\d+)|\w)?))/;
- const stringDoubleRegExp = /^(\"((?:[^"\\]|\\.)*)\")/;
- const stringSingleRegExp = /^(\'((?:[^'\\]|\\.)*)\')/;
- const literalRegExp = /^[A-Za-z](\w|\.)*/;
- const operatorsRegExp = new RegExp( '^(\\' + [
- '<<=', '>>=', '++', '--', '<<', '>>', '+=', '-=', '*=', '/=', '%=', '&=', '^^', '^=', '|=',
- '<=', '>=', '==', '!=', '&&', '||',
- '(', ')', '[', ']', '{', '}',
- '.', ',', ';', '!', '=', '~', '*', '/', '%', '+', '-', '<', '>', '&', '^', '|', '?', ':', '#'
- ].join( '$' ).split( '' ).join( '\\' ).replace( /\\\$/g, '|' ) + ')' );
- function getFunctionName( str ) {
- return glslToTSL[ str ] || str;
- }
- function getGroupDelta( str ) {
- if ( str === '(' || str === '[' || str === '{' ) return 1;
- if ( str === ')' || str === ']' || str === '}' ) return - 1;
- return 0;
- }
- class Token {
- constructor( tokenizer, type, str, pos ) {
- this.tokenizer = tokenizer;
- this.type = type;
- this.str = str;
- this.pos = pos;
- this.isTag = false;
- this.tags = null;
- }
- get endPos() {
- return this.pos + this.str.length;
- }
- get isNumber() {
- return this.type === Token.NUMBER;
- }
- get isString() {
- return this.type === Token.STRING;
- }
- get isLiteral() {
- return this.type === Token.LITERAL;
- }
- get isOperator() {
- return this.type === Token.OPERATOR;
- }
- }
- Token.LINE = 'line';
- Token.COMMENT = 'comment';
- Token.NUMBER = 'number';
- Token.STRING = 'string';
- Token.LITERAL = 'literal';
- Token.OPERATOR = 'operator';
- const TokenParserList = [
- { type: Token.LINE, regexp: lineRegExp, isTag: true },
- { type: Token.COMMENT, regexp: commentRegExp, isTag: true },
- { type: Token.COMMENT, regexp: inlineCommentRegExp, isTag: true },
- { type: Token.NUMBER, regexp: numberRegExp },
- { type: Token.STRING, regexp: stringDoubleRegExp, group: 2 },
- { type: Token.STRING, regexp: stringSingleRegExp, group: 2 },
- { type: Token.LITERAL, regexp: literalRegExp },
- { type: Token.OPERATOR, regexp: operatorsRegExp }
- ];
- class Tokenizer {
- constructor( source ) {
- this.source = source;
- this.position = 0;
- this.tokens = [];
- }
- tokenize() {
- let token = this.readToken();
- while ( token ) {
- this.tokens.push( token );
- token = this.readToken();
- }
- return this;
- }
- skip( ...params ) {
- let remainingCode = this.source.substr( this.position );
- let i = params.length;
- while ( i -- ) {
- const skip = params[ i ].exec( remainingCode );
- const skipLength = skip ? skip[ 0 ].length : 0;
- if ( skipLength > 0 ) {
- this.position += skipLength;
- remainingCode = this.source.substr( this.position );
- // re-skip, new remainingCode is generated
- // maybe exist previous regexp non detected
- i = params.length;
- }
- }
- return remainingCode;
- }
- nextToken() {
- const remainingCode = this.skip( spaceRegExp );
- for ( var i = 0; i < TokenParserList.length; i ++ ) {
- const parser = TokenParserList[ i ];
- const result = parser.regexp.exec( remainingCode );
- if ( result ) {
- const token = new Token( this, parser.type, result[ parser.group || 0 ], this.position );
- token.isTag = parser.isTag;
- this.position += result[ 0 ].length;
- return token;
- }
- }
- }
- readToken() {
- let token = this.nextToken();
- if ( token && token.isTag ) {
- const tags = [];
- while ( token.isTag ) {
- tags.push( token );
- token = this.nextToken();
- if ( ! token ) return;
- }
- token.tags = tags;
- }
- return token;
- }
- }
- class GLSLDecoder {
- constructor() {
- this.index = 0;
- this.tokenizer = null;
- this.keywords = [];
- this.addPolyfill( 'gl_FragCoord', 'vec3 gl_FragCoord = vec3( screenCoordinate.x, screenCoordinate.y.oneMinus(), screenCoordinate.z );' );
- }
- addPolyfill( name, polyfill ) {
- this.keywords.push( { name, polyfill } );
- return this;
- }
- get tokens() {
- return this.tokenizer.tokens;
- }
- readToken() {
- return this.tokens[ this.index ++ ];
- }
- getToken( offset = 0 ) {
- return this.tokens[ this.index + offset ];
- }
- getTokensUntil( str, tokens, offset = 0 ) {
- const output = [];
- let groupIndex = 0;
- for ( let i = offset; i < tokens.length; i ++ ) {
- const token = tokens[ i ];
- groupIndex += getGroupDelta( token.str );
- output.push( token );
- if ( groupIndex === 0 && token.str === str ) {
- break;
- }
- }
- return output;
- }
- readTokensUntil( str ) {
- const tokens = this.getTokensUntil( str, this.tokens, this.index );
- this.index += tokens.length;
- return tokens;
- }
- parseExpressionFromTokens( tokens ) {
- if ( tokens.length === 0 ) return null;
- const firstToken = tokens[ 0 ];
- const lastToken = tokens[ tokens.length - 1 ];
- // precedence operators
- let groupIndex = 0;
- for ( const operators of precedenceOperators ) {
- const parseToken = ( i, inverse = false ) => {
- const token = tokens[ i ];
- groupIndex += getGroupDelta( token.str );
- if ( ! token.isOperator || i === 0 || i === tokens.length - 1 ) return;
- // important for negate operator after arithmetic operator: a * -1, a * -( b )
- if ( inverse && arithmeticOperators.includes( tokens[ i - 1 ].str ) ) {
- return;
- }
- if ( groupIndex === 0 && operators.includes( token.str ) ) {
- const operator = token.str;
- if ( operator === '?' ) {
- const conditionTokens = tokens.slice( 0, i );
- const leftTokens = this.getTokensUntil( ':', tokens, i + 1 ).slice( 0, - 1 );
- const rightTokens = tokens.slice( i + leftTokens.length + 2 );
- const condition = this.parseExpressionFromTokens( conditionTokens );
- const left = this.parseExpressionFromTokens( leftTokens );
- const right = this.parseExpressionFromTokens( rightTokens );
- return new Ternary( condition, left, right );
- } else {
- const left = this.parseExpressionFromTokens( tokens.slice( 0, i ) );
- const right = this.parseExpressionFromTokens( tokens.slice( i + 1, tokens.length ) );
- return new Operator( operator, left, right );
- }
- }
- if ( inverse ) {
- if ( groupIndex > 0 ) {
- return this.parseExpressionFromTokens( tokens.slice( i ) );
- }
- } else {
- if ( groupIndex < 0 ) {
- return this.parseExpressionFromTokens( tokens.slice( 0, i ) );
- }
- }
- };
- const isRightAssociative = operators.some( op => associativityRightToLeft.includes( op ) );
- if ( isRightAssociative ) {
- for ( let i = 0; i < tokens.length; i ++ ) {
- const result = parseToken( i );
- if ( result ) return result;
- }
- } else {
- for ( let i = tokens.length - 1; i >= 0; i -- ) {
- const result = parseToken( i, true );
- if ( result ) return result;
- }
- }
- }
- // unary operators (before)
- if ( firstToken.isOperator ) {
- for ( const operator of unaryOperators ) {
- if ( firstToken.str === operator ) {
- const right = this.parseExpressionFromTokens( tokens.slice( 1 ) );
- return new Unary( operator, right );
- }
- }
- }
- // unary operators (after)
- if ( lastToken.isOperator ) {
- for ( const operator of unaryOperators ) {
- if ( lastToken.str === operator ) {
- const left = this.parseExpressionFromTokens( tokens.slice( 0, tokens.length - 1 ) );
- return new Unary( operator, left, true );
- }
- }
- }
- // groups
- if ( firstToken.str === '(' ) {
- const leftTokens = this.getTokensUntil( ')', tokens );
- const left = this.parseExpressionFromTokens( leftTokens.slice( 1, leftTokens.length - 1 ) );
- const operator = tokens[ leftTokens.length ];
- if ( operator ) {
- const rightTokens = tokens.slice( leftTokens.length + 1 );
- const right = this.parseExpressionFromTokens( rightTokens );
- return new Operator( operator.str, left, right );
- }
- return left;
- }
- // primitives and accessors
- if ( firstToken.isNumber ) {
- let type;
- const isHex = /^(0x)/.test( firstToken.str );
- if ( isHex ) type = 'int';
- else if ( /u$|U$/.test( firstToken.str ) ) type = 'uint';
- else if ( /f|e|\./.test( firstToken.str ) ) type = 'float';
- else type = 'int';
- let str = firstToken.str.replace( /u|U|i$/, '' );
- if ( isHex === false ) {
- str = str.replace( /f$/, '' );
- }
- return new Number( str, type );
- } else if ( firstToken.isString ) {
- return new String( firstToken.str );
- } else if ( firstToken.isLiteral ) {
- if ( firstToken.str === 'return' ) {
- return new Return( this.parseExpressionFromTokens( tokens.slice( 1 ) ) );
- } else if ( firstToken.str === 'discard' ) {
- return new Discard();
- } else if ( firstToken.str === 'continue' ) {
- return new Continue();
- } else if ( firstToken.str === 'break' ) {
- return new Break();
- }
- const secondToken = tokens[ 1 ];
- if ( secondToken ) {
- if ( secondToken.str === '(' ) {
- // function call
- const internalTokens = this.getTokensUntil( ')', tokens, 1 ).slice( 1, - 1 );
- const paramsTokens = this.parseFunctionParametersFromTokens( internalTokens );
- const functionCall = new FunctionCall( getFunctionName( firstToken.str ), paramsTokens );
- const accessTokens = tokens.slice( 3 + internalTokens.length );
- if ( accessTokens.length > 0 ) {
- const elements = this.parseAccessorElementsFromTokens( accessTokens );
- return new AccessorElements( functionCall, elements );
- }
- return functionCall;
- } else if ( secondToken.str === '[' ) {
- // array accessor
- const elements = this.parseAccessorElementsFromTokens( tokens.slice( 1 ) );
- return new AccessorElements( new Accessor( firstToken.str ), elements );
- }
- }
- return new Accessor( firstToken.str );
- }
- }
- parseAccessorElementsFromTokens( tokens ) {
- const elements = [];
- let currentTokens = tokens;
- while ( currentTokens.length > 0 ) {
- const token = currentTokens[ 0 ];
- if ( token.str === '[' ) {
- const accessorTokens = this.getTokensUntil( ']', currentTokens );
- const element = this.parseExpressionFromTokens( accessorTokens.slice( 1, accessorTokens.length - 1 ) );
- currentTokens = currentTokens.slice( accessorTokens.length );
- elements.push( new DynamicElement( element ) );
- } else if ( token.str === '.' ) {
- const accessorTokens = currentTokens.slice( 1, 2 );
- const element = this.parseExpressionFromTokens( accessorTokens );
- currentTokens = currentTokens.slice( 2 );
- elements.push( new StaticElement( element ) );
- } else {
- console.error( 'Unknown accessor expression', token );
- break;
- }
- }
- return elements;
- }
- parseFunctionParametersFromTokens( tokens ) {
- if ( tokens.length === 0 ) return [];
- const expression = this.parseExpressionFromTokens( tokens );
- const params = [];
- let current = expression;
- while ( current.type === ',' ) {
- params.push( current.left );
- current = current.right;
- }
- params.push( current );
- return params;
- }
- parseExpression() {
- const tokens = this.readTokensUntil( ';' );
- const exp = this.parseExpressionFromTokens( tokens.slice( 0, tokens.length - 1 ) );
- return exp;
- }
- parseFunctionParams( tokens ) {
- const params = [];
- for ( let i = 0; i < tokens.length; i ++ ) {
- const immutable = tokens[ i ].str === 'const';
- if ( immutable ) i ++;
- let qualifier = tokens[ i ].str;
- if ( /^(in|out|inout)$/.test( qualifier ) ) {
- i ++;
- } else {
- qualifier = null;
- }
- const type = tokens[ i ++ ].str;
- const name = tokens[ i ++ ].str;
- params.push( new FunctionParameter( type, name, qualifier, immutable ) );
- if ( tokens[ i ] && tokens[ i ].str !== ',' ) throw new Error( 'Expected ","' );
- }
- return params;
- }
- parseFunction() {
- const type = this.readToken().str;
- const name = this.readToken().str;
- const paramsTokens = this.readTokensUntil( ')' );
- const params = this.parseFunctionParams( paramsTokens.slice( 1, paramsTokens.length - 1 ) );
- const body = this.parseBlock();
- const func = new FunctionDeclaration( type, name, params, body );
- return func;
- }
- parseVariablesFromToken( tokens, type ) {
- let index = 0;
- const immutable = tokens[ 0 ].str === 'const';
- if ( immutable ) index ++;
- type = type || tokens[ index ++ ].str;
- const name = tokens[ index ++ ].str;
- const token = tokens[ index ];
- let init = null;
- let next = null;
- if ( token ) {
- const initTokens = this.getTokensUntil( ',', tokens, index );
- if ( initTokens[ 0 ].str === '=' ) {
- const expressionTokens = initTokens.slice( 1 );
- if ( expressionTokens[ expressionTokens.length - 1 ].str === ',' ) expressionTokens.pop();
- init = this.parseExpressionFromTokens( expressionTokens );
- }
- const nextTokens = tokens.slice( initTokens.length + ( index - 1 ) );
- if ( nextTokens[ 0 ] && nextTokens[ 0 ].str === ',' ) {
- next = this.parseVariablesFromToken( nextTokens.slice( 1 ), type );
- }
- }
- const variable = new VariableDeclaration( type, name, init, next, immutable );
- return variable;
- }
- parseVariables() {
- const tokens = this.readTokensUntil( ';' );
- return this.parseVariablesFromToken( tokens.slice( 0, tokens.length - 1 ) );
- }
- parseUniform() {
- const tokens = this.readTokensUntil( ';' );
- let type = tokens[ 1 ].str;
- const name = tokens[ 2 ].str;
- // GLSL to TSL types
- if ( samplers.includes( type ) ) type = 'texture';
- else if ( samplersCube.includes( type ) ) type = 'cubeTexture';
- else if ( samplers3D.includes( type ) ) type = 'texture3D';
- return new Uniform( type, name );
- }
- parseVarying() {
- const tokens = this.readTokensUntil( ';' );
- const type = tokens[ 1 ].str;
- const name = tokens[ 2 ].str;
- return new Varying( type, name );
- }
- parseReturn() {
- this.readToken(); // skip 'return'
- const expression = this.parseExpression();
- return new Return( expression );
- }
- parseWhile() {
- this.readToken(); // skip 'while'
- const conditionTokens = this.readTokensUntil( ')' ).slice( 1, - 1 );
- const condition = this.parseExpressionFromTokens( conditionTokens );
- let body;
- if ( this.getToken().str === '{' ) {
- body = this.parseBlock();
- } else {
- body = [ this.parseExpression() ];
- }
- const statement = new While( condition, body );
- return statement;
- }
- parseFor() {
- this.readToken(); // skip 'for'
- const forTokens = this.readTokensUntil( ')' ).slice( 1, - 1 );
- const initializationTokens = this.getTokensUntil( ';', forTokens, 0 ).slice( 0, - 1 );
- const conditionTokens = this.getTokensUntil( ';', forTokens, initializationTokens.length + 1 ).slice( 0, - 1 );
- const afterthoughtTokens = forTokens.slice( initializationTokens.length + conditionTokens.length + 2 );
- let initialization;
- if ( initializationTokens[ 0 ] && isType( initializationTokens[ 0 ].str ) ) {
- initialization = this.parseVariablesFromToken( initializationTokens );
- } else {
- initialization = this.parseExpressionFromTokens( initializationTokens );
- }
- const condition = this.parseExpressionFromTokens( conditionTokens );
- const afterthought = this.parseExpressionFromTokens( afterthoughtTokens );
- let body;
- if ( this.getToken().str === '{' ) {
- body = this.parseBlock();
- } else {
- body = [ this.parseExpression() ];
- }
- const statement = new For( initialization, condition, afterthought, body );
- return statement;
- }
- parseSwitch() {
- this.readToken(); // Skip 'switch'
- const switchDeterminantTokens = this.readTokensUntil( ')' );
- // Parse expression between parentheses. Index 1: char after '('. Index -1: char before ')'
- const discriminant = this.parseExpressionFromTokens( switchDeterminantTokens.slice( 1, - 1 ) );
- // Validate curly braces
- if ( this.getToken().str !== '{' ) {
- throw new Error( 'Expected \'{\' after switch(...) ' );
- }
- this.readToken(); // Skip '{'
- const cases = this.parseSwitchCases();
- const switchStatement = new Switch( discriminant, cases );
- return switchStatement;
- }
- parseSwitchCases() {
- const cases = [];
- let token = this.getToken();
- let conditions = null;
- const isCase = ( token ) => token.str === 'case' || token.str === 'default';
- while ( isCase( token ) ) {
- this.readToken(); // Skip 'case' or 'default'
- if ( token.str === 'case' ) {
- const caseTokens = this.readTokensUntil( ':' );
- const caseStatement = this.parseExpressionFromTokens( caseTokens.slice( 0, - 1 ) );
- conditions = conditions || [];
- conditions.push( caseStatement );
- } else {
- this.readTokensUntil( ':' ); // Skip 'default:'
- conditions = null;
- }
- token = this.getToken();
- if ( isCase( token ) ) {
- // If the next token is another case/default, continue parsing
- continue;
- }
- cases.push( new SwitchCase( this.parseBlock(), conditions ) );
- token = this.getToken();
- conditions = null;
- }
- return cases;
- }
- parseIf() {
- const parseIfExpression = () => {
- this.readToken(); // skip 'if'
- const condTokens = this.readTokensUntil( ')' );
- return this.parseExpressionFromTokens( condTokens.slice( 1, condTokens.length - 1 ) );
- };
- const parseIfBlock = () => {
- let body;
- if ( this.getToken().str === '{' ) {
- body = this.parseBlock();
- } else {
- body = [ this.parseExpression() ];
- }
- return body;
- };
- //
- // Parse the first if statement
- const conditional = new Conditional( parseIfExpression(), parseIfBlock() );
- //
- let current = conditional;
- while ( this.getToken() && this.getToken().str === 'else' ) {
- this.readToken(); // skip 'else'
- // Assign the current if/else statement as the previous within the chain of conditionals
- const previous = current;
- let expression = null;
- // If an 'else if' statement, parse the conditional within the if
- if ( this.getToken().str === 'if' ) {
- // Current conditional now equal to next conditional in the chain
- expression = parseIfExpression();
- }
- current = new Conditional( expression, parseIfBlock() );
- current.parent = previous;
- // n - 1 conditional's else statement assigned to new if/else statement
- previous.elseConditional = current;
- }
- return conditional;
- }
- parseBlock() {
- const body = [];
- const firstToken = this.getToken();
- if ( firstToken.str === '{' ) {
- this.readToken(); // skip '{'
- }
- let groupIndex = 0;
- while ( this.index < this.tokens.length ) {
- const token = this.getToken();
- let statement = null;
- groupIndex += getGroupDelta( token.str );
- if ( groupIndex === 0 && ( token.str === 'case' || token.str === 'default' ) ) {
- return body; // switch case or default statement, return body
- } else if ( groupIndex < 0 ) {
- this.readToken(); // skip '}'
- return body;
- }
- //
- if ( token.tags ) {
- let lastStatement = null;
- for ( const tag of token.tags ) {
- if ( tag.type === Token.COMMENT ) {
- const str = tag.str.replace( /\t/g, '' );
- if ( ! lastStatement || lastStatement.isComment !== true ) {
- lastStatement = new Comment( str );
- body.push( lastStatement );
- } else {
- lastStatement.comment += '\n' + str;
- }
- }
- }
- }
- if ( token.isLiteral || token.isOperator ) {
- if ( token.str === 'const' ) {
- statement = this.parseVariables();
- } else if ( token.str === 'uniform' ) {
- statement = this.parseUniform();
- } else if ( token.str === 'varying' ) {
- statement = this.parseVarying();
- } else if ( isType( token.str ) ) {
- if ( this.getToken( 2 ).str === '(' ) {
- statement = this.parseFunction();
- } else {
- statement = this.parseVariables();
- }
- } else if ( token.str === 'return' ) {
- statement = this.parseReturn();
- } else if ( token.str === 'if' ) {
- statement = this.parseIf();
- } else if ( token.str === 'for' ) {
- statement = this.parseFor();
- } else if ( token.str === 'while' ) {
- statement = this.parseWhile();
- } else if ( token.str === 'switch' ) {
- statement = this.parseSwitch();
- } else {
- statement = this.parseExpression();
- }
- }
- if ( statement ) {
- body.push( statement );
- } else {
- this.index ++;
- }
- }
- return body;
- }
- parse( source ) {
- let polyfill = '';
- for ( const keyword of this.keywords ) {
- if ( new RegExp( `(^|\\b)${ keyword.name }($|\\b)`, 'gm' ).test( source ) ) {
- polyfill += keyword.polyfill + '\n';
- }
- }
- if ( polyfill ) {
- polyfill = '// Polyfills\n\n' + polyfill + '\n';
- }
- this.index = 0;
- this.tokenizer = new Tokenizer( polyfill + source ).tokenize();
- const body = this.parseBlock();
- const program = new Program( body );
- return program;
- }
- }
- export default GLSLDecoder;
|