You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
328 lines
4.9 KiB
JavaScript
328 lines
4.9 KiB
JavaScript
class Block {
|
|
|
|
constructor( node, parent = null ) {
|
|
|
|
this.node = node;
|
|
this.parent = parent;
|
|
|
|
this.properties = {};
|
|
|
|
}
|
|
|
|
setProperty( name, value ) {
|
|
|
|
this.properties[ name ] = value;
|
|
|
|
}
|
|
|
|
getProperty( name ) {
|
|
|
|
let value = this.properties[ name ];
|
|
|
|
if ( value === undefined && this.parent !== null ) {
|
|
|
|
value = this.parent.getProperty( name );
|
|
|
|
}
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
class Linker {
|
|
|
|
constructor() {
|
|
|
|
this.block = null;
|
|
|
|
}
|
|
|
|
addBlock( node ) {
|
|
|
|
this.block = new Block( node, this.block );
|
|
|
|
}
|
|
|
|
removeBlock( node ) {
|
|
|
|
if ( this.block === null || this.block.node !== node ) {
|
|
|
|
throw new Error( 'No block to remove or block mismatch.' );
|
|
|
|
}
|
|
|
|
this.block = this.block.parent;
|
|
|
|
}
|
|
|
|
processVariables( node ) {
|
|
|
|
this.block.setProperty( node.name, node );
|
|
|
|
if ( node.value ) {
|
|
|
|
this.processExpression( node.value );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
processUniform( node ) {
|
|
|
|
this.block.setProperty( node.name, node );
|
|
|
|
}
|
|
|
|
processVarying( node ) {
|
|
|
|
this.block.setProperty( node.name, node );
|
|
|
|
}
|
|
|
|
evalProperty( node ) {
|
|
|
|
let property = '';
|
|
|
|
if ( node.isAccessor ) {
|
|
|
|
property += node.property;
|
|
|
|
}
|
|
|
|
return property;
|
|
|
|
}
|
|
|
|
processExpression( node ) {
|
|
|
|
if ( node.isAccessor ) {
|
|
|
|
const property = this.block.getProperty( this.evalProperty( node ) );
|
|
|
|
if ( property ) {
|
|
|
|
node.linker.reference = property;
|
|
|
|
property.linker.accesses.push( node );
|
|
|
|
}
|
|
|
|
} else if ( node.isNumber || node.isString ) {
|
|
|
|
// Process primitive values
|
|
|
|
} else if ( node.isOperator ) {
|
|
|
|
this.processExpression( node.left );
|
|
this.processExpression( node.right );
|
|
|
|
if ( node.isAssignment ) {
|
|
|
|
const property = this.block.getProperty( this.evalProperty( node.left ) );
|
|
|
|
if ( property ) {
|
|
|
|
property.linker.assignments.push( node );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if ( node.isFunctionCall ) {
|
|
|
|
for ( const param of node.params ) {
|
|
|
|
this.processExpression( param );
|
|
|
|
}
|
|
|
|
} else if ( node.isReturn ) {
|
|
|
|
if ( node.value ) this.processExpression( node.value );
|
|
|
|
} else if ( node.isDiscard || node.isBreak || node.isContinue ) {
|
|
|
|
// Process control flow
|
|
|
|
} else if ( node.isAccessorElements ) {
|
|
|
|
this.processExpression( node.object );
|
|
|
|
for ( const element of node.elements ) {
|
|
|
|
this.processExpression( element.value );
|
|
|
|
}
|
|
|
|
} else if ( node.isDynamicElement || node.isStaticElement ) {
|
|
|
|
this.processExpression( node.value );
|
|
|
|
} else if ( node.isFor || node.isWhile ) {
|
|
|
|
this.processForWhile( node );
|
|
|
|
} else if ( node.isSwitch ) {
|
|
|
|
this.processSwitch( node );
|
|
|
|
} else if ( node.isVariableDeclaration ) {
|
|
|
|
this.processVariables( node );
|
|
|
|
} else if ( node.isUniform ) {
|
|
|
|
this.processUniform( node );
|
|
|
|
} else if ( node.isVarying ) {
|
|
|
|
this.processVarying( node );
|
|
|
|
} else if ( node.isTernary ) {
|
|
|
|
this.processExpression( node.cond );
|
|
this.processExpression( node.left );
|
|
this.processExpression( node.right );
|
|
|
|
} else if ( node.isConditional ) {
|
|
|
|
this.processConditional( node );
|
|
|
|
} else if ( node.isUnary ) {
|
|
|
|
this.processExpression( node.expression );
|
|
|
|
if ( node.isAssignment ) {
|
|
|
|
if ( node.parent.hasAssignment !== true ) {
|
|
|
|
// optimize increment/decrement operator
|
|
// to avoid creating a new variable
|
|
|
|
node.after = false;
|
|
|
|
}
|
|
|
|
const property = this.block.getProperty( this.evalProperty( node.expression ) );
|
|
|
|
if ( property ) {
|
|
|
|
property.linker.assignments.push( node );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
processBody( body ) {
|
|
|
|
for ( const statement of body ) {
|
|
|
|
this.processExpression( statement );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
processConditional( node ) {
|
|
|
|
this.processExpression( node.cond );
|
|
this.processBody( node.body );
|
|
|
|
let current = node;
|
|
|
|
while ( current.elseConditional ) {
|
|
|
|
if ( current.elseConditional.cond ) {
|
|
|
|
this.processExpression( current.elseConditional.cond );
|
|
|
|
}
|
|
|
|
this.processBody( current.elseConditional.body );
|
|
|
|
current = current.elseConditional;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
processForWhile( node ) {
|
|
|
|
if ( node.initialization ) this.processExpression( node.initialization );
|
|
if ( node.condition ) this.processExpression( node.condition );
|
|
if ( node.afterthought ) this.processExpression( node.afterthought );
|
|
|
|
this.processBody( node.body );
|
|
|
|
}
|
|
|
|
processSwitch( switchNode ) {
|
|
|
|
this.processExpression( switchNode.discriminant );
|
|
|
|
for ( const switchCase of switchNode.cases ) {
|
|
|
|
if ( switchCase.isDefault !== true ) {
|
|
|
|
for ( const condition of switchCase.conditions ) {
|
|
|
|
this.processExpression( condition );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
this.processBody( switchCase.body );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
processFunction( node ) {
|
|
|
|
this.addBlock( node );
|
|
|
|
for ( const param of node.params ) {
|
|
|
|
this.block.setProperty( param.name, param );
|
|
|
|
}
|
|
|
|
this.processBody( node.body );
|
|
|
|
this.removeBlock( node );
|
|
|
|
}
|
|
|
|
process( ast ) {
|
|
|
|
this.addBlock( ast );
|
|
|
|
for ( const statement of ast.body ) {
|
|
|
|
if ( statement.isFunctionDeclaration ) {
|
|
|
|
this.processFunction( statement );
|
|
|
|
} else {
|
|
|
|
this.processExpression( statement );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
this.removeBlock( ast );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
export default Linker;
|