| 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 | |
| 7 | QT_BEGIN_NAMESPACE |
| 8 | |
| 9 | namespace QShaderRewriter { |
| 10 | |
| 11 | struct 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 | |
| 33 | const char *Tokenizer::NAMES[] = { |
| 34 | "Void" , |
| 35 | "OpenBrace" , |
| 36 | "CloseBrace" , |
| 37 | "SemiColon" , |
| 38 | "Identifier" , |
| 39 | "Unspecified" , |
| 40 | "EOF" |
| 41 | }; |
| 42 | |
| 43 | void Tokenizer::initialize(const QByteArray &input) |
| 44 | { |
| 45 | pos = input.constData(); |
| 46 | identifier = pos; |
| 47 | } |
| 48 | |
| 49 | Tokenizer::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 | |
| 125 | void 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 | |
| 140 | QByteArray 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 | |
| 198 | QT_END_NAMESPACE |
| 199 | |