1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qshaderrewriter_p.h"
5#include <QDebug>
6
7QT_BEGIN_NAMESPACE
8
9namespace QShaderRewriter {
10
11struct Tokenizer {
12
13 enum Token {
14 Token_Void,
15 Token_OpenBrace,
16 Token_CloseBrace,
17 Token_SemiColon,
18 Token_Identifier,
19 Token_Unspecified,
20
21 Token_EOF
22 };
23
24 static const char *NAMES[];
25
26 void initialize(const QByteArray &input);
27 Token next();
28
29 const char *pos;
30 const char *identifier;
31};
32
33const char *Tokenizer::NAMES[] = {
34 "Void",
35 "OpenBrace",
36 "CloseBrace",
37 "SemiColon",
38 "Identifier",
39 "Unspecified",
40 "EOF"
41};
42
43void Tokenizer::initialize(const QByteArray &input)
44{
45 pos = input.constData();
46 identifier = pos;
47}
48
49Tokenizer::Token Tokenizer::next()
50{
51 while (*pos != 0) {
52 char c = *pos++;
53 switch (c) {
54 case 0:
55 return Token_EOF;
56
57 case '/':
58 if (*pos == '/') {
59 // '//' comment
60 ++pos;
61 while (*pos != 0 && *pos != '\n') ++pos;
62 if (*pos != 0) ++pos; // skip the newline
63
64 } else if (*pos == '*') {
65 // /* */ comment
66 ++pos;
67 while (*pos != 0 && *pos != '*' && pos[1] != '/') ++pos;
68 if (*pos != 0) pos += 2;
69 }
70 break;
71
72 case '#': {
73 while (*pos != 0) {
74 if (*pos == '\n') {
75 ++pos;
76 break;
77 } else if (*pos == '\\') {
78 ++pos;
79 while (*pos != 0 && (*pos == ' ' || *pos == '\t'))
80 ++pos;
81 if (*pos != 0 && (*pos == '\n' || (*pos == '\r' && pos[1] == '\n')))
82 pos+=2;
83 } else {
84 ++pos;
85 }
86 }
87 break;
88 }
89
90 case ';': return Token_SemiColon;
91 case '{': return Token_OpenBrace;
92 case '}': return Token_CloseBrace;
93
94 case ' ':
95 case '\n':
96 case '\r': break;
97
98 case 'v': {
99 if (*pos == 'o' && pos[1] == 'i' && pos[2] == 'd') {
100 pos += 3;
101 return Token_Void;
102 }
103 Q_FALLTHROUGH();
104 }
105 default:
106 // Identifier...
107 if ((c >= 'a' && c <= 'z' ) || (c >= 'A' && c <= 'Z' ) || c == '_') {
108 identifier = pos - 1;
109 while (*pos != 0 && ((*pos >= 'a' && *pos <= 'z')
110 || (*pos >= 'A' && *pos <= 'Z')
111 || *pos == '_'
112 || (*pos >= '0' && *pos <= '9'))) {
113 ++pos;
114 }
115 return Token_Identifier;
116 } else {
117 return Token_Unspecified;
118 }
119 }
120 }
121
122 return Token_EOF;
123}
124
125void debugTokenizer(const QByteArray &input)
126{
127 Tokenizer tok;
128 tok.initialize(input);
129 Tokenizer::Token t = tok.next();
130 while (t != Tokenizer::Token_EOF) {
131 if (t == Tokenizer::Token_Identifier)
132 qDebug() << Tokenizer::NAMES[t] << QByteArray::fromRawData(data: tok.identifier, size: tok.pos - tok.identifier);
133 else
134 qDebug() << Tokenizer::NAMES[t];
135
136 t = tok.next();
137 }
138}
139
140QByteArray addZAdjustment(const QByteArray &input, int vertexInputLocation)
141{
142 Tokenizer tok;
143 tok.initialize(input);
144
145 Tokenizer::Token lt = tok.next();
146 Tokenizer::Token t = tok.next();
147
148 // First find "void main() { ... "
149 const char* voidPos = input.constData();
150 while (t != Tokenizer::Token_EOF) {
151 if (lt == Tokenizer::Token_Void && t == Tokenizer::Token_Identifier) {
152 if (qstrncmp(str1: "main", str2: tok.identifier, len: 4) == 0)
153 break;
154 }
155 voidPos = tok.pos - 4;
156 lt = t;
157 t = tok.next();
158 }
159
160 QByteArray result;
161 result.reserve(asize: 1024);
162 result += QByteArray::fromRawData(data: input.constData(), size: voidPos - input.constData());
163
164 result += QByteArrayLiteral("layout(location = ");
165 result += QByteArray::number(vertexInputLocation);
166 result += QByteArrayLiteral(") in float _qt_order;\n");
167
168 // Find first brace '{'
169 while (t != Tokenizer::Token_EOF && t != Tokenizer::Token_OpenBrace) t = tok.next();
170 int braceDepth = 1;
171 t = tok.next();
172
173 // Find matching brace and insert our code there...
174 while (t != Tokenizer::Token_EOF) {
175 switch (t) {
176 case Tokenizer::Token_CloseBrace:
177 braceDepth--;
178 if (braceDepth == 0) {
179 result += QByteArray::fromRawData(data: voidPos, size: tok.pos - 1 - voidPos);
180 result += QByteArrayLiteral(" gl_Position.z = _qt_order * gl_Position.w;\n");
181 result += QByteArray(tok.pos - 1);
182 return result;
183 }
184 break;
185 case Tokenizer::Token_OpenBrace:
186 ++braceDepth;
187 break;
188 default:
189 break;
190 }
191 t = tok.next();
192 }
193 return QByteArray();
194}
195
196} // namespace
197
198QT_END_NAMESPACE
199

source code of qtshadertools/src/shadertools/qshaderrewriter.cpp