Skip to content

Commit afd540a

Browse files
committed
Support formatting pre tree
1 parent c5339a1 commit afd540a

2 files changed

Lines changed: 138 additions & 0 deletions

File tree

src/main/java/org/taumc/glsl/ShaderPrinter.java

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@
22

33
import org.antlr.v4.runtime.tree.ParseTree;
44
import org.antlr.v4.runtime.tree.TerminalNode;
5+
import org.taumc.glsl.grammar.GLSLLexer;
56
import org.taumc.glsl.grammar.GLSLParser;
67
import 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
*

src/test/java/org/taumc/glsl/ShaderPrinterTest.java

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ private static String format(String input) {
1010
return new ShaderPrinter(ShaderParser.parseShader(input).full()).getFormattedShader();
1111
}
1212

13+
private static String formatPre(String input) {
14+
return new ShaderPrinter(ShaderParser.parseShader(input).pre()).getFormattedShader();
15+
}
16+
1317
@Test
1418
void formatsDeterministicallyAndReadably() {
1519
String input = "void main(){float x=1.0;for(int i=0;i<2;i++){x+=i;}if(x>0.0){x=x-1.0;}else{x=0.0;}}";
@@ -360,4 +364,39 @@ void interpolationAndStorageQualifiers() {
360364

361365
assertEquals(expected, format(input));
362366
}
367+
368+
@Test
369+
void directiveVersion() {
370+
assertEquals("#version 330 core\n", formatPre("#version 330 core\nvoid main(){}"));
371+
}
372+
373+
@Test
374+
void directiveExtension() {
375+
assertEquals("#extension GL_ARB_gpu_shader5 : require\n",
376+
formatPre("#extension GL_ARB_gpu_shader5 : require\nvoid main(){}"));
377+
}
378+
379+
@Test
380+
void directiveDefine() {
381+
assertEquals("#define MY_CONST 1.0\n", formatPre("#define MY_CONST 1.0\nvoid main(){}"));
382+
}
383+
384+
@Test
385+
void directiveMultiple() {
386+
String input = "#version 330 core\n#extension GL_ARB_gpu_shader5 : require\nvoid main(){}";
387+
String expected = "#version 330 core\n#extension GL_ARB_gpu_shader5 : require\n";
388+
assertEquals(expected, formatPre(input));
389+
}
390+
391+
@Test
392+
void directivePragma() {
393+
assertEquals("#pragma optimize(on)\n", formatPre("#pragma optimize(on)\nvoid main(){}"));
394+
}
395+
396+
@Test
397+
void directiveIfdef() {
398+
String input = "#ifdef MY_FLAG\n#define FOO 1\n#endif\nvoid main(){}";
399+
String expected = "#ifdef MY_FLAG\n#define FOO 1\n#endif\n";
400+
assertEquals(expected, formatPre(input));
401+
}
363402
}

0 commit comments

Comments
 (0)