Class TransformationPhase<T extends JobParameters>

java.lang.Object
io.github.douira.glsl_transformer.GLSLParserBaseListener
io.github.douira.glsl_transformer.transform.TransformationPhase<T>
All Implemented Interfaces:
io.github.douira.glsl_transformer.GLSLParserListener, LifecycleUser<T>, PartialParseTreeListener, ParseTreeListener
Direct Known Subclasses:
HandlerTarget, RunPhase, WalkPhase

public abstract class TransformationPhase<T extends JobParameters> extends io.github.douira.glsl_transformer.GLSLParserBaseListener implements LifecycleUser<T>, PartialParseTreeListener
The transformations phase actually does a specific transformation. It can be added to a transformation which holds multiple transformation phases and their ordering. A phase can also be added to multiple transformations if they should share the functionality. The transformation phase has methods for adding and removing parse tree nodes. It can also inject nodes into the root node's child array with injection points.
  • Nested Class Summary

    Nested Classes
    Modifier and Type
    Class
    Description
    static enum 
    Shader code is expected to be roughly structured as follows: version, extensions, other directives (#define, #pragma etc.), declarations (layout etc.), functions (void main etc.).
  • Constructor Summary

    Constructors
    Constructor
    Description
     
  • Method Summary

    Modifier and Type
    Method
    Description
    protected abstract boolean
    Called during planning in order to determine if this phase does any walking at all or if it just runs some code, like a RunPhase.
    protected boolean
    checkBeforeWalk(io.github.douira.glsl_transformer.GLSLParser.TranslationUnitContext ctx)
    Method called by the execution planner before the walk happens.
    protected XPath
    Compiles the given string as an xpath with the parser.
    compilePattern(String pattern, int rootRule)
    Compiles the given string as a parse tree matching pattern what starts matching at the given parser rule.
    protected <RuleType extends ExtendedContext>
    RuleType
    createLocalRoot(String str, ExtendedContext parent, Function<io.github.douira.glsl_transformer.GLSLParser,RuleType> parseMethod)
    Parses the given string using the given parser method.
    protected List<ParseTreeMatch>
    findAndMatch(ParseTree tree, XPath xpath, ParseTreePattern pattern)
    This method uses a statically constructed xpath, so it doesn't need to be repeatedly constructed.
    Returns the execution planner set on this child.
    protected static List<ParseTree>
    Gets the sibling nodes of a given node.
    protected void
    Injects a new #define statement at the specified location.
    protected void
    Injects the given string parsed as an external declaration.
    protected void
    Injects multiple strings parsed as individual external declarations.
    protected void
    Injects the given node into the translation unit context root node at the given injection point.
    protected void
    Injects a list of nodes into the translation unit context node.
    protected void
    Injects an array of nodes at an injection location.
    protected boolean
    Overwrite this method to add a check of if this phase should be run at all.
    boolean
    Checks if this lifecycle user has been initialized.
    protected int
    removeNode(TreeMember removeNode)
    Removes the given node from its parent's child list.
    protected int
    replaceNode(TreeMember removeNode, TreeMember newNode)
    Replaces the given node in its parent with a new given node.
    protected void
    replaceNode(TreeMember removeNode, String newContent, Function<io.github.douira.glsl_transformer.GLSLParser,ExtendedContext> parseMethod)
    Replaces the given node in its parent with a new node generated by parsing the given string with the given method of the parser.
    protected void
    runAfterWalk(io.github.douira.glsl_transformer.GLSLParser.TranslationUnitContext ctx)
    Method called by the execution planner after the walk happens.
    void
    Marks this lifecycle user as initialized.
    void
    This must be called before executing this phase in the context of a specific parse tree.
    protected void
    Marks this phase as being done walking the tree in the current execution.

    Methods inherited from class io.github.douira.glsl_transformer.GLSLParserBaseListener

    enterAdditiveExpression, enterArrayAccessExpression, enterArraySpecifier, enterArraySpecifierSegment, enterAssignmentExpression, enterAtomicUnitType, enterAttribute, enterBitwiseAndExpression, enterBitwiseExclusiveOrExpression, enterBitwiseInclusiveOrExpression, enterBooleanType, enterBooleanVectorType, enterBreakStatement, enterBuiltinType, enterCallParameterList, enterCompoundStatement, enterConditionalExpression, enterContinueStatement, enterDeclarationMember, enterDeclarationStatement, enterDefaultCaseLabel, enterDemoteStatement, enterDiscardStatement, enterDoWhileStatement, enterEmptyDeclaration, enterEmptyStatement, enterEqualityExpression, enterEveryRule, enterExpressionStatement, enterExtensionStatement, enterExternalDeclaration, enterFloatMatrixType, enterFloatType, enterFloatVectorType, enterForStatement, enterFullySpecifiedType, enterFunctionCall, enterFunctionCallExpression, enterFunctionDeclaration, enterFunctionDefinition, enterFunctionHeader, enterFunctionIdentifier, enterFunctionParameterList, enterFunctionPrototype, enterGroupingExpression, enterImageType, enterInitDeclaratorList, enterInitializer, enterIntegerType, enterIntegerVectorType, enterInterfaceBlockDeclaration, enterInterpolationQualifier, enterInvariantQualifier, enterIterationCondition, enterLayoutDefaults, enterLayoutQualifier, enterLiteralExpression, enterLogicalAndExpression, enterLogicalExclusiveOrExpression, enterLogicalInclusiveOrExpression, enterMemberAccessExpression, enterMethodCall, enterMethodCallExpression, enterMultiplicativeExpression, enterNamedLayoutQualifier, enterParameterDeclaration, enterParameterDeclarator, enterPostfixExpression, enterPragmaStatement, enterPreciseQualifier, enterPrecisionDeclaration, enterPrecisionQualifier, enterPrefixExpression, enterReferencedType, enterReferenceExpression, enterRelationalExpression, enterReturnStatement, enterSamplerType, enterSelectionStatement, enterSequenceExpression, enterSharedLayoutQualifier, enterShiftExpression, enterSingleAttribute, enterStatement, enterStorageQualifier, enterStructBody, enterStructDeclarator, enterStructMember, enterStructSpecifier, enterStructSpecifierType, enterSwitchStatement, enterTranslationUnit, enterTypeAndInitDeclaration, enterTypeNameList, enterTypeQualifier, enterTypeSpecifier, enterValuedCaseLabel, enterVariableDeclaration, enterVariableIdentifier, enterVersionStatement, enterVoidType, enterWhileStatement, exitAdditiveExpression, exitArrayAccessExpression, exitArraySpecifier, exitArraySpecifierSegment, exitAssignmentExpression, exitAtomicUnitType, exitAttribute, exitBitwiseAndExpression, exitBitwiseExclusiveOrExpression, exitBitwiseInclusiveOrExpression, exitBooleanType, exitBooleanVectorType, exitBreakStatement, exitBuiltinType, exitCallParameterList, exitCompoundStatement, exitConditionalExpression, exitContinueStatement, exitDeclarationMember, exitDeclarationStatement, exitDefaultCaseLabel, exitDemoteStatement, exitDiscardStatement, exitDoWhileStatement, exitEmptyDeclaration, exitEmptyStatement, exitEqualityExpression, exitEveryRule, exitExpressionStatement, exitExtensionStatement, exitExternalDeclaration, exitFloatMatrixType, exitFloatType, exitFloatVectorType, exitForStatement, exitFullySpecifiedType, exitFunctionCall, exitFunctionCallExpression, exitFunctionDeclaration, exitFunctionDefinition, exitFunctionHeader, exitFunctionIdentifier, exitFunctionParameterList, exitFunctionPrototype, exitGroupingExpression, exitImageType, exitInitDeclaratorList, exitInitializer, exitIntegerType, exitIntegerVectorType, exitInterfaceBlockDeclaration, exitInterpolationQualifier, exitInvariantQualifier, exitIterationCondition, exitLayoutDefaults, exitLayoutQualifier, exitLiteralExpression, exitLogicalAndExpression, exitLogicalExclusiveOrExpression, exitLogicalInclusiveOrExpression, exitMemberAccessExpression, exitMethodCall, exitMethodCallExpression, exitMultiplicativeExpression, exitNamedLayoutQualifier, exitParameterDeclaration, exitParameterDeclarator, exitPostfixExpression, exitPragmaStatement, exitPreciseQualifier, exitPrecisionDeclaration, exitPrecisionQualifier, exitPrefixExpression, exitReferencedType, exitReferenceExpression, exitRelationalExpression, exitReturnStatement, exitSamplerType, exitSelectionStatement, exitSequenceExpression, exitSharedLayoutQualifier, exitShiftExpression, exitSingleAttribute, exitStatement, exitStorageQualifier, exitStructBody, exitStructDeclarator, exitStructMember, exitStructSpecifier, exitStructSpecifierType, exitSwitchStatement, exitTranslationUnit, exitTypeAndInitDeclaration, exitTypeNameList, exitTypeQualifier, exitTypeSpecifier, exitValuedCaseLabel, exitVariableDeclaration, exitVariableIdentifier, exitVersionStatement, exitVoidType, exitWhileStatement, visitErrorNode, visitTerminal

    Methods inherited from class java.lang.Object

    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait

    Methods inherited from interface io.github.douira.glsl_transformer.transform.LifecycleUser

    getJobParameters, getLexer, getParser, getRootNode, init, initOnce, resetState

    Methods inherited from interface org.antlr.v4.runtime.tree.ParseTreeListener

    enterEveryRule, exitEveryRule, visitErrorNode, visitTerminal

    Methods inherited from interface io.github.douira.glsl_transformer.traversal.PartialParseTreeListener

    isDeepEnough, isFinished
  • Constructor Details

    • TransformationPhase

      public TransformationPhase()
  • Method Details

    • canWalk

      protected abstract boolean canWalk()
      Called during planning in order to determine if this phase does any walking at all or if it just runs some code, like a RunPhase. This doesn't exclude or include this phase from walking but rather helps the execution planner combine walk phases into execution levels. Enabling and disabling a phase should be done with the methods checkBeforeWalk(TranslationUnitContext) and runAfterWalk(TranslationUnitContext).
      Returns:
      If this phase needs to be walked on the tree
      Implementation Note:
      This method should run quickly and will only be called once (or never) during execution planning.
    • checkBeforeWalk

      protected boolean checkBeforeWalk(io.github.douira.glsl_transformer.GLSLParser.TranslationUnitContext ctx)
      Method called by the execution planner before the walk happens. The returned boolean determines if the phase is added to the list of phases that are walked on the tree. Returns false by default and implementing classes should overwrite this.
      Parameters:
      ctx - The root node
      Returns:
      true if the phase should be walked on the tree
    • runAfterWalk

      protected void runAfterWalk(io.github.douira.glsl_transformer.GLSLParser.TranslationUnitContext ctx)
      Method called by the execution planner after the walk happens. Does nothing by default.
      Parameters:
      ctx - The root node
    • getPlanner

      public ExecutionPlanner<T> getPlanner()
      Description copied from interface: LifecycleUser
      Returns the execution planner set on this child.
      Specified by:
      getPlanner in interface LifecycleUser<T extends JobParameters>
      Returns:
      The currently set execution planner
    • setPlanner

      public void setPlanner(ExecutionPlanner<T> parent)
      This must be called before executing this phase in the context of a specific parse tree. Sets the parent planner of this child.
      Specified by:
      setPlanner in interface LifecycleUser<T extends JobParameters>
      Parameters:
      parent - The execution planner to set as the parent
    • isInitialized

      public boolean isInitialized()
      Description copied from interface: LifecycleUser
      Checks if this lifecycle user has been initialized.
      Specified by:
      isInitialized in interface LifecycleUser<T extends JobParameters>
      Returns:
      True if initialized, false otherwise
    • setInitialized

      public void setInitialized()
      Description copied from interface: LifecycleUser
      Marks this lifecycle user as initialized.
      Specified by:
      setInitialized in interface LifecycleUser<T extends JobParameters>
    • walkFinished

      protected void walkFinished()
      Marks this phase as being done walking the tree in the current execution. This removes it from the proxy parse tree listener which in turn can make the dynamic parse tree walker not further walk the parse tree if there are no more listeners that are interested in continuing.
      API Usage Note:
      Calling this method multiple times in the same execution has no effect but is efficient
    • getSiblings

      protected static List<ParseTree> getSiblings(TreeMember node)
      Gets the sibling nodes of a given node. It looks up the parent and then returns the parent's children.
      Parameters:
      node - The node to get the siblings for
      Returns:
      The siblings of the given node. null if the node has no parent.
    • replaceNode

      protected void replaceNode(TreeMember removeNode, String newContent, Function<io.github.douira.glsl_transformer.GLSLParser,ExtendedContext> parseMethod)
      Replaces the given node in its parent with a new node generated by parsing the given string with the given method of the parser. See createLocalRoot(String, ExtendedContext, Function) for details of creating parsed nodes.
      Parameters:
      removeNode - The node to be replaced
      newContent - The string from which a new node is generated
      parseMethod - The method with which the string will be parsed
    • replaceNode

      protected int replaceNode(TreeMember removeNode, TreeMember newNode)
      Replaces the given node in its parent with a new given node. The new node should either be already set up as a local root or be a terminal node tree member.
      Parameters:
      removeNode - The node to be removed
      newNode - The new node to take its place
      Returns:
      The index of the removed and new node
    • removeNode

      protected int removeNode(TreeMember removeNode)
      Removes the given node from its parent's child list.
      Parameters:
      removeNode - The node to remove
      Returns:
      the index of the removed node
      Implementation Note:
      The empty space is filled with an empty terminal node that keeps a reference to the removed node.
    • compilePath

      protected XPath compilePath(String xpath)
      Compiles the given string as an xpath with the parser. This method is meant to be used in LifecycleUser.init() for initializing (effectively) final but phase-specific fields.
      Parameters:
      xpath - The string to compile as an xpath
      Returns:
      The compiled xpath
    • compilePattern

      protected ParseTreePattern compilePattern(String pattern, int rootRule)
      Compiles the given string as a parse tree matching pattern what starts matching at the given parser rule. The pattern will not compile or function correctly if the pattern can't be compiled in the context of the given parser rule. See ANTLR's documentation on how tree matching patterns work. (there is special syntax that should be used for extracting) The resulting pattern will need to be applied to nodes that exactly match the given root rule of the pattern. For finding nodes at any depth and then matching, findAndMatch(ParseTree, XPath, ParseTreePattern) can be used. This method is meant to be used in LifecycleUser.init() for initializing (effectively) final but phase-specific fields.
      Parameters:
      pattern - The string to compile as a tree matching pattern.
      rootRule - The parser rule to compile the pattern as
      Returns:
      The compiled pattern
    • findAndMatch

      protected List<ParseTreeMatch> findAndMatch(ParseTree tree, XPath xpath, ParseTreePattern pattern)
      This method uses a statically constructed xpath, so it doesn't need to be repeatedly constructed. The subtrees yielded by the xpath need to start with the rule that the pattern was constructed with or nothing will match. Adapted from ANTLR's implementation of ParseTreePattern.findAll(ParseTree, String).
      Parameters:
      tree - The parse tree to find and match in
      xpath - The xpath that leads to a subtree for matching
      pattern - The pattern that tests the subtrees for matches
      Returns:
      A list of all matches resulting from the subtrees
    • isActive

      protected boolean isActive()
      Overwrite this method to add a check of if this phase should be run at all. Especially for WalkPhase this is important since it reduces the number of listeners that need to be processed.
      Returns:
      If the phase should run. true by default.
    • createLocalRoot

      protected <RuleType extends ExtendedContext> RuleType createLocalRoot(String str, ExtendedContext parent, Function<io.github.douira.glsl_transformer.GLSLParser,RuleType> parseMethod)
      Parses the given string using the given parser method. Since the parser doesn't know which part of the parse tree any string would be part of, we need to tell it. In many cases multiple methods would produce a correct result. However, this can lead to a truncated parse tree when the resulting node is inserted into a bigger parse tree. The parsing method should be chosen such that when the resulting node is inserted into a parse tree, the tree has the same structure as if it had been parsed as one piece. For example, the code fragment foo() could be parsed as a functionCall, a primaryExpression, an expression or other enclosing parse rules. If it's inserted into an expression, it should be parsed as an expression so that this rule isn't missing from the parse tree. Using the wrong parse method often doesn't matter, but it can cause tree matchers to not find the node if they are, for example, looking for an expression specifically. All nodes inserted into the parse tree must have properly configured parent references or looking up a node's local root won't work. Other things in ANTLR may also break if non-root nodes are missing their parent references.
      Type Parameters:
      RuleType - The type of the resulting parsed node
      Parameters:
      str - The string to be parsed
      parent - The parent to be set on the node. All nodes will eventually end up in the a main tree so some parent will be available. Getting the siblings of the new node will not work if no parent is set.
      parseMethod - The parser method with which the string is parsed
      Returns:
      The resulting parsed node
    • injectNode

      protected void injectNode(TransformationPhase.InjectionPoint location, ParseTree newNode)
      Injects the given node into the translation unit context root node at the given injection point. Note that this may break things if used improperly (if breaking the grammar's rules for example). The addChild method sets the parent on the added node.
      Parameters:
      location - The injection point at which the new node is inserted
      newNode - The new node to be inserted
      Implementation Note:
      Since ANTLR's rule context stores children in an ArrayList, this operation runs in linear time O(n) with respect to the the number n of external declarations in the root node.
    • injectNodes

      protected void injectNodes(TransformationPhase.InjectionPoint location, Deque<ParseTree> newNodes)
      Injects a list of nodes into the translation unit context node. Does the same thing as injectNode(InjectionPoint, ParseTree) but with a list of nodes.
      Parameters:
      location - The injection point at which the new nodes are inserted
      newNodes - The list of nodes to be inserted
    • injectNodes

      protected void injectNodes(TransformationPhase.InjectionPoint location, ParseTree... newNodes)
      Injects an array of nodes at an injection location.
      Parameters:
      location - The injection point at which the new nodes are inserted
      newNodes - The list of nodes to be inserted
      See Also:
    • injectExternalDeclaration

      protected void injectExternalDeclaration(TransformationPhase.InjectionPoint location, String str)
      Injects the given string parsed as an external declaration. This is a convenience method since most of the time injected nodes are external declarations.
      Parameters:
      location - The injection point at which the new node is inserted
      str - The code fragment to be parsed as an external declaration and inserted at the given injection point
      See Also:
    • injectExternalDeclarations

      protected void injectExternalDeclarations(TransformationPhase.InjectionPoint location, String... str)
      Injects multiple strings parsed as individual external declarations.
      Parameters:
      location - The injection point at which the new nodes are inserted
      str - The strings to parse as external declarations and then insert
      See Also:
    • injectDefine

      protected void injectDefine(TransformationPhase.InjectionPoint location, String content)
      Injects a new #define statement at the specified location. This method is for convenience since injecting defines is a common operation. For other directives the Directive class should be used.
      Parameters:
      location - The injection point at which the new node is inserted
      content - The content after the #define prefix
      API Usage Note:
      This method should be avoided if a direct replacement of identifiers using the appropriate core transformations is possible.