22
33import org .antlr .v4 .runtime .tree .ParseTree ;
44import org .antlr .v4 .runtime .tree .TerminalNode ;
5+ import org .taumc .glsl .grammar .GLSLLexer ;
56import org .taumc .glsl .grammar .GLSLParser ;
67import org .taumc .glsl .grammar .GLSLParserBaseVisitor ;
8+ import org .taumc .glsl .grammar .GLSLPreParser ;
9+ import org .taumc .glsl .grammar .GLSLPreParserBaseVisitor ;
710
811/**
912 * Formats a GLSL parse tree as a human-readable string with consistent indentation and spacing.
@@ -16,6 +19,11 @@ public ShaderPrinter(ParseTree shaderTree) {
1619 }
1720
1821 public String getFormattedShader () {
22+ if (shaderTree instanceof GLSLPreParser .Translation_unitContext ) {
23+ var v = new PreVisitor ();
24+ v .visit (shaderTree );
25+ return v .sb .toString ();
26+ }
1927 var v = new Visitor ();
2028 v .visit (shaderTree );
2129 return v .sb .toString ();
@@ -25,6 +33,97 @@ public static String getFormattedShader(ParseTree tree) {
2533 return new ShaderPrinter (tree ).getFormattedShader ();
2634 }
2735
36+ /**
37+ * ANTLR visitor for the GLSLPreParser tree (preprocessor directives).
38+ *
39+ * <p>Uses the same {@code nextSep}/{@link #emit}/{@link #newline}/{@link #nosep} pattern as
40+ * {@link Visitor}. Directives are never indented, so {@link #newline} simply sets
41+ * {@link #nextSep} to {@code "\n"} with no leading spaces.
42+ *
43+ * <p>{@link #visitCompiler_directive} appends a newline after each top-level directive.
44+ * Nested directives inside {@code #if}/{@code #ifdef}/{@code #ifndef} blocks fall through to
45+ * {@code visitChildren} and are emitted inline; complex conditional blocks are not
46+ * specially formatted beyond correct spacing.
47+ */
48+ private static class PreVisitor extends GLSLPreParserBaseVisitor <Void > {
49+ final StringBuilder sb = new StringBuilder ();
50+ String nextSep = "" ;
51+
52+ void emit (String s ) {
53+ sb .append (nextSep );
54+ sb .append (s );
55+ nextSep = " " ;
56+ }
57+
58+ void newline () {
59+ nextSep = "\n " ;
60+ }
61+
62+ void nosep () {
63+ nextSep = "" ;
64+ }
65+
66+ @ Override
67+ public Void visitCompiler_directive (GLSLPreParser .Compiler_directiveContext ctx ) {
68+ nosep ();
69+ visitChildren (ctx );
70+ sb .append ('\n' );
71+ nextSep = "" ;
72+ return null ;
73+ }
74+
75+ /**
76+ * Inserts a newline before the content of a {@code #if}/{@code #ifdef}/{@code #ifndef}
77+ * block body, separating the opening directive line from its contents.
78+ */
79+ @ Override
80+ public Void visitGroup_of_lines (GLSLPreParser .Group_of_linesContext ctx ) {
81+ sb .append ('\n' );
82+ nextSep = "" ;
83+ return visitChildren (ctx );
84+ }
85+
86+ @ Override
87+ public Void visitTerminal (TerminalNode node ) {
88+ int type = node .getSymbol ().getType ();
89+ String text = node .getText ();
90+ switch (type ) {
91+ case GLSLLexer .EOF :
92+ break ;
93+ case GLSLLexer .NUMBER_SIGN :
94+ nosep ();
95+ emit ("#" );
96+ nosep ();
97+ break ;
98+ case GLSLLexer .LEFT_PAREN :
99+ case GLSLLexer .LEFT_BRACKET :
100+ nosep ();
101+ emit (text );
102+ nosep ();
103+ break ;
104+ case GLSLLexer .RIGHT_PAREN :
105+ case GLSLLexer .RIGHT_BRACKET :
106+ nosep ();
107+ emit (text );
108+ break ;
109+ // These tokens include their own leading whitespace as lexed;
110+ // suppress the default separator to avoid doubling it.
111+ case GLSLLexer .MACRO_TEXT :
112+ case GLSLLexer .CONSTANT_EXPRESSION :
113+ case GLSLLexer .ERROR_MESSAGE :
114+ case GLSLLexer .LINE_EXPRESSION :
115+ case GLSLLexer .PROGRAM_TEXT :
116+ nosep ();
117+ emit (text );
118+ break ;
119+ default :
120+ emit (text );
121+ break ;
122+ }
123+ return null ;
124+ }
125+ }
126+
28127 /**
29128 * ANTLR visitor that walks the parse tree and writes formatted GLSL to a StringBuilder.
30129 *
0 commit comments