1 // Copyright (c) 2014-2020 The Khronos Group Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and/or associated documentation files (the "Materials"), 5 // to deal in the Materials without restriction, including without limitation 6 // the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 // and/or sell copies of the Materials, and to permit persons to whom the 8 // Materials are furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Materials. 12 // 13 // MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS 14 // STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND 15 // HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ 16 // 17 // THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 22 // FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS 23 // IN THE MATERIALS. 24 25 // 26 // Print headers for SPIR-V in several languages. 27 // 28 // To change the header information, change the C++-built database in doc.*. 29 // 30 // Then, use "spriv -h <language>" - e.g, spriv.{h,hpp,lua,py,etc}: 31 // replace the auto-generated header, or "spirv -H" to generate all 32 // supported language headers to predefined names in the current directory. 33 // 34 35 #include <string> 36 #include <sstream> 37 #include <fstream> 38 #include <cstring> 39 #include <cstdio> 40 #include <algorithm> 41 #include <memory> 42 #include <cctype> 43 #include <vector> 44 #include <utility> 45 #include <set> 46 47 #include "jsoncpp/dist/json/json.h" 48 49 #include "header.h" 50 #include "jsonToSpirv.h" 51 52 // snprintf and _snprintf are not quite the same, but close enough 53 // for our use. 54 #ifdef _MSC_VER 55 #pragma warning(disable:4996) 56 #define snprintf _snprintf 57 #endif 58 59 // This file converts SPIR-V definitions to an internal JSON 60 // representation, and then generates language specific 61 // data from that single internal form. 62 63 // Initially, the internal form is created from C++ data, 64 // though this can be changed to a JSON master in time. 65 66 namespace { 67 class TPrinter { 68 protected: 69 TPrinter(); 70 71 static const int DocMagicNumber = 0x07230203; 72 static const int DocVersion = 0x00010600; 73 static const int DocRevision = 1; 74 #define DocRevisionString "1" 75 static const std::string DocCopyright; 76 static const std::string DocComment1; 77 static const std::string DocComment2; 78 79 enum enumStyle_t { 80 enumNoMask, 81 enumCount, 82 enumShift, 83 enumMask, 84 enumHex, 85 }; 86 styleStr(enumStyle_t s)87 static std::string styleStr(enumStyle_t s) { 88 return s == enumShift ? "Shift" : 89 s == enumMask ? "Mask" : ""; 90 } 91 92 friend std::ostream& operator<<(std::ostream&, const TPrinter&); 93 94 virtual void printAll(std::ostream&) const; 95 virtual void printComments(std::ostream&) const; printPrologue(std::ostream &) const96 virtual void printPrologue(std::ostream&) const { } 97 virtual void printDefs(std::ostream&) const; printEpilogue(std::ostream &) const98 virtual void printEpilogue(std::ostream&) const { } 99 virtual void printMeta(std::ostream&) const; printTypes(std::ostream &) const100 virtual void printTypes(std::ostream&) const { } printHasResultType(std::ostream &) const101 virtual void printHasResultType(std::ostream&) const { }; 102 103 virtual std::string escapeComment(const std::string& s) const; 104 105 // Default printComments() uses these comment strings commentBeg() const106 virtual std::string commentBeg() const { return ""; } commentEnd(bool isLast) const107 virtual std::string commentEnd(bool isLast) const { return ""; } commentBOL() const108 virtual std::string commentBOL() const { return ""; } commentEOL(bool isLast) const109 virtual std::string commentEOL(bool isLast) const { return ""; } 110 111 typedef std::pair<unsigned, std::string> valpair_t; 112 113 // for printing enum values enumBeg(const std::string &,enumStyle_t) const114 virtual std::string enumBeg(const std::string&, enumStyle_t) const { return ""; } enumEnd(const std::string &,enumStyle_t,bool isLast=false) const115 virtual std::string enumEnd(const std::string&, enumStyle_t, bool isLast = false) const { 116 return ""; 117 } enumFmt(const std::string &,const valpair_t &,enumStyle_t,bool isLast=false) const118 virtual std::string enumFmt(const std::string&, const valpair_t&, 119 enumStyle_t, bool isLast = false) const { 120 return ""; 121 } maxEnumFmt(const std::string &,const valpair_t &,enumStyle_t) const122 virtual std::string maxEnumFmt(const std::string&, const valpair_t&, 123 enumStyle_t) const { 124 return ""; 125 } 126 fmtConstInt(unsigned val,const std::string & name,const char * fmt,bool isLast=false) const127 virtual std::string fmtConstInt(unsigned val, const std::string& name, 128 const char* fmt, bool isLast = false) const { 129 return ""; 130 } 131 132 std::vector<valpair_t> getSortedVals(const Json::Value&) const; 133 indent(int count=1) const134 virtual std::string indent(int count = 1) const { 135 return std::string(count * 4, ' '); // default indent level = 4 136 } 137 fmtNum(const char * fmt,unsigned val)138 static std::string fmtNum(const char* fmt, unsigned val) { 139 char buff[16]; // ample for 8 hex digits + 0x 140 snprintf(buff, sizeof(buff), fmt, val); 141 buff[sizeof(buff)-1] = '\0'; // MSVC doesn't promise null termination 142 return buff; 143 } 144 145 static std::string fmtStyleVal(unsigned v, enumStyle_t style); 146 147 // If the enum value name would start with a sigit, prepend the enum name. 148 // E.g, "3D" -> "Dim3D". prependIfDigit(const std::string & ename,const std::string & vname)149 static std::string prependIfDigit(const std::string& ename, const std::string& vname) { 150 return (std::isdigit(vname[0]) ? ename : std::string("")) + vname; 151 } 152 153 void addComment(Json::Value& node, const std::string& str); 154 155 Json::Value spvRoot; // JSON SPIR-V data 156 }; 157 158 // Format value as mask or value fmtStyleVal(unsigned v,enumStyle_t style)159 std::string TPrinter::fmtStyleVal(unsigned v, enumStyle_t style) 160 { 161 switch (style) { 162 case enumMask: 163 return fmtNum("0x%08x", 1<<v); 164 case enumHex: 165 return fmtNum("0x%08x", v); 166 default: 167 return std::to_string(v); 168 } 169 } 170 171 const std::string TPrinter::DocCopyright = 172 "Copyright (c) 2014-2020 The Khronos Group Inc.\n" 173 "\n" 174 "Permission is hereby granted, free of charge, to any person obtaining a copy\n" 175 "of this software and/or associated documentation files (the \"Materials\"),\n" 176 "to deal in the Materials without restriction, including without limitation\n" 177 "the rights to use, copy, modify, merge, publish, distribute, sublicense,\n" 178 "and/or sell copies of the Materials, and to permit persons to whom the\n" 179 "Materials are furnished to do so, subject to the following conditions:\n" 180 "\n" 181 "The above copyright notice and this permission notice shall be included in\n" 182 "all copies or substantial portions of the Materials.\n" 183 "\n" 184 "MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS\n" 185 "STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND\n" 186 "HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ \n" 187 "\n" 188 "THE MATERIALS ARE PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\n" 189 "OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n" 190 "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n" 191 "THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n" 192 "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n" 193 "FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS\n" 194 "IN THE MATERIALS.\n"; 195 196 const std::string TPrinter::DocComment1 = 197 "This header is automatically generated by the same tool that creates\n" 198 "the Binary Section of the SPIR-V specification.\n"; 199 200 const std::string TPrinter::DocComment2 = 201 "Enumeration tokens for SPIR-V, in various styles:\n" 202 " C, C++, C++11, JSON, Lua, Python, C#, D, Beef\n" 203 "\n" 204 "- C will have tokens with a \"Spv\" prefix, e.g.: SpvSourceLanguageGLSL\n" 205 "- C++ will have tokens in the \"spv\" name space, e.g.: spv::SourceLanguageGLSL\n" 206 "- C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL\n" 207 "- Lua will use tables, e.g.: spv.SourceLanguage.GLSL\n" 208 "- Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL']\n" 209 "- C# will use enum classes in the Specification class located in the \"Spv\" namespace,\n" 210 " e.g.: Spv.Specification.SourceLanguage.GLSL\n" 211 "- D will have tokens under the \"spv\" module, e.g: spv.SourceLanguage.GLSL\n" 212 "- Beef will use enum classes in the Specification class located in the \"Spv\" namespace,\n" 213 " e.g.: Spv.Specification.SourceLanguage.GLSL\n" 214 "\n" 215 "Some tokens act like mask values, which can be OR'd together,\n" 216 "while others are mutually exclusive. The mask-like ones have\n" 217 "\"Mask\" in their name, and a parallel enum that has the shift\n" 218 "amount (1 << x) for each corresponding enumerant.\n"; 219 220 // Construct TPrinter()221 TPrinter::TPrinter() 222 { 223 Json::Value& meta = spvRoot["spv"]["meta"]; 224 Json::Value& enums = spvRoot["spv"]["enum"]; 225 226 meta["MagicNumber"] = DocMagicNumber; 227 meta["Version"] = DocVersion; 228 meta["Revision"] = DocRevision; 229 meta["OpCodeMask"] = 0xffff; 230 meta["WordCountShift"] = 16; 231 232 int commentId = 0; 233 addComment(meta["Comment"][commentId++], DocCopyright); 234 addComment(meta["Comment"][commentId++], DocComment1); 235 addComment(meta["Comment"][commentId++], DocComment2); 236 237 for (int e = spv::OperandSource; e < spv::OperandOpcode; ++e) { 238 auto& enumSet = spv::OperandClassParams[e]; 239 const bool mask = enumSet.bitmask; 240 const std::string enumName = enumSet.codeName; 241 242 for (auto& enumRow : enumSet) { 243 std::string name = enumRow.name; 244 enums[e - spv::OperandSource]["Values"][name] = enumRow.value; 245 } 246 247 enums[e - spv::OperandSource]["Type"] = mask ? "Bit" : "Value"; 248 enums[e - spv::OperandSource]["Name"] = enumName; 249 } 250 251 // Instructions are in their own different table 252 { 253 auto& entry = enums[spv::OperandOpcode - spv::OperandSource]; 254 for (auto& enumRow : spv::InstructionDesc) { 255 std::string name = enumRow.name; 256 entry["Values"][name] = enumRow.value; 257 } 258 entry["Type"] = "Value"; 259 entry["Name"] = "Op"; 260 } 261 } 262 263 // Create comment addComment(Json::Value & node,const std::string & str)264 void TPrinter::addComment(Json::Value& node, const std::string& str) 265 { 266 std::istringstream cstream(str); 267 std::string cline; 268 269 int line = 0; 270 while (std::getline(cstream, cline)) // fmt each line 271 node[line++] = cline; 272 } 273 274 275 // Return a list of values sorted by enum value. The std::vector 276 // returned by value is okay in c++11 due to move semantics. 277 std::vector<TPrinter::valpair_t> getSortedVals(const Json::Value & p) const278 TPrinter::getSortedVals(const Json::Value& p) const 279 { 280 std::vector<valpair_t> values; 281 282 for (auto e = p.begin(); e != p.end(); ++e) 283 values.push_back(valpair_t(e->asUInt(), e.name())); 284 285 // Use a stable sort because we might have aliases, e.g. 286 // SubgropuBallot (might be in future core) vs. SubgroupBallotKHR. 287 std::stable_sort(values.begin(), values.end()); 288 289 return values; 290 } 291 292 // Escape comment characters if needed escapeComment(const std::string & s) const293 std::string TPrinter::escapeComment(const std::string& s) const { return s; } 294 295 // Format comments in language specific way printComments(std::ostream & out) const296 void TPrinter::printComments(std::ostream& out) const 297 { 298 const int commentCount = spvRoot["spv"]["meta"]["Comment"].size(); 299 int commentNum = 0; 300 301 for (const auto& comment : spvRoot["spv"]["meta"]["Comment"]) { 302 out << commentBeg(); 303 304 for (int line = 0; line < int(comment.size()); ++line) 305 out << commentBOL() << escapeComment(comment[line].asString()) << 306 commentEOL((line+1) == comment.size()) << std::endl; 307 308 out << commentEnd(++commentNum == commentCount) << std::endl; 309 } 310 } 311 312 // Format header metadata printMeta(std::ostream & out) const313 void TPrinter::printMeta(std::ostream& out) const 314 { 315 const Json::Value& meta = spvRoot["spv"]["meta"]; 316 317 const auto print = [&](const char* name, const char* fmt, bool isLast) { 318 out << fmtConstInt(meta[name].asUInt(), name, fmt, isLast); 319 }; 320 321 print("MagicNumber", "0x%08lx", false); 322 print("Version", "0x%08lx", false); 323 print("Revision", "%d", false); 324 print("OpCodeMask", "0x%04x", false); 325 print("WordCountShift", "%d", true); 326 } 327 328 // Format value definitions in language specific way printDefs(std::ostream & out) const329 void TPrinter::printDefs(std::ostream& out) const 330 { 331 const Json::Value& enums = spvRoot["spv"]["enum"]; 332 333 for (auto opClass = enums.begin(); opClass != enums.end(); ++opClass) { 334 const bool isMask = (*opClass)["Type"].asString() == "Bit"; 335 const auto opName = (*opClass)["Name"].asString(); 336 const auto opPrefix = opName == "Op" ? "" : opName; 337 338 for (enumStyle_t style = (isMask ? enumShift : enumCount); 339 style <= (isMask ? enumMask : enumCount); style = enumStyle_t(int(style)+1)) { 340 341 out << enumBeg(opName, style); 342 343 if (style == enumMask) 344 out << enumFmt(opPrefix, valpair_t(0, "MaskNone"), enumNoMask); 345 346 const auto sorted = getSortedVals((*opClass)["Values"]); 347 348 std::string maxEnum = maxEnumFmt(opName, valpair_t(0x7FFFFFFF, "Max"), enumHex); 349 350 bool printMax = (style != enumMask && maxEnum.size() > 0); 351 352 for (const auto& v : sorted) 353 out << enumFmt(opPrefix, v, style, !printMax && v.second == sorted.back().second); 354 355 if (printMax) 356 out << maxEnum; 357 358 auto nextOpClass = opClass; 359 out << enumEnd(opName, style, ++nextOpClass == enums.end()); 360 } 361 } 362 } 363 printAll(std::ostream & out) const364 void TPrinter::printAll(std::ostream& out) const 365 { 366 printComments(out); 367 printPrologue(out); 368 printTypes(out); 369 printMeta(out); 370 printDefs(out); 371 printHasResultType(out); 372 printEpilogue(out); 373 } 374 375 // Stream entire header to output operator <<(std::ostream & out,const TPrinter & p)376 std::ostream& operator<<(std::ostream& out, const TPrinter &p) 377 { 378 p.printAll(out); 379 return out; 380 } 381 382 // JSON printer. Rather than use the default printer, we supply our own so 383 // we can control the printing order within various containers. 384 class TPrinterJSON final : public TPrinter { 385 private: printPrologue(std::ostream & out) const386 void printPrologue(std::ostream& out) const override { out << "{\n" + indent() + "\"spv\":\n" + indent() + "{\n"; } printEpilogue(std::ostream & out) const387 void printEpilogue(std::ostream& out) const override { out << indent() + "}\n}\n"; } 388 escapeComment(const std::string & s) const389 std::string escapeComment(const std::string& s) const override { 390 std::string newStr; 391 for (auto c : s) { 392 if (c == '"') { 393 newStr += '\\'; 394 newStr += c; 395 } else { 396 newStr += c; 397 } 398 } 399 return newStr; 400 } 401 fmtConstInt(unsigned val,const std::string & name,const char * fmt,bool isLast) const402 std::string fmtConstInt(unsigned val, const std::string& name, 403 const char* fmt, bool isLast) const override { 404 return indent(3) + '"' + name + "\": " + fmtNum("%d", val) + (isLast ? "\n" : ",\n"); 405 } 406 printMeta(std::ostream & out) const407 void printMeta(std::ostream& out) const override 408 { 409 out << indent(2) + "\"meta\":\n" + indent(2) + "{\n"; 410 printComments(out); 411 TPrinter::printMeta(out); 412 out << indent(2) + "},\n"; 413 } 414 commentBeg() const415 std::string commentBeg() const override { return indent(4) + "[\n"; } commentEnd(bool isLast) const416 std::string commentEnd(bool isLast) const override { return indent(4) + (isLast ? "]" : "],"); } commentBOL() const417 std::string commentBOL() const override { return indent(5) + '"'; } commentEOL(bool isLast) const418 std::string commentEOL(bool isLast) const override { return (isLast ? "\"" : "\","); } 419 printComments(std::ostream & out) const420 void printComments(std::ostream& out) const override 421 { 422 out << indent(3) + "\"Comment\":\n" + indent(3) + "[\n"; 423 TPrinter::printComments(out); 424 out << indent(3) + "],\n"; 425 } 426 printDefs(std::ostream & out) const427 void printDefs(std::ostream& out) const override 428 { 429 out << indent(2) + "\"enum\":\n" + indent(2) + "[\n"; 430 TPrinter::printDefs(out); 431 out << indent(2) + "]\n"; 432 } 433 printAll(std::ostream & out) const434 void printAll(std::ostream& out) const override 435 { 436 printPrologue(out); 437 printMeta(out); 438 printDefs(out); 439 printEpilogue(out); 440 } 441 enumBeg(const std::string & s,enumStyle_t style) const442 std::string enumBeg(const std::string& s, enumStyle_t style) const override { 443 if (style == enumMask) 444 return ""; 445 return indent(3) + "{\n" + 446 indent(4) + "\"Name\": \"" + s + "\",\n" + 447 indent(4) + "\"Type\": " + (style == enumShift ? "\"Bit\"" : "\"Value\"") + ",\n" + 448 indent(4) + "\"Values\":\n" + 449 indent(4) + "{\n"; 450 } 451 enumEnd(const std::string & s,enumStyle_t style,bool isLast) const452 std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override { 453 if (style == enumMask) 454 return ""; 455 return indent(4) + "}\n" + 456 indent(3) + "}" + (isLast ? "" : ",") + "\n"; 457 } 458 enumFmt(const std::string & s,const valpair_t & v,enumStyle_t style,bool isLast) const459 std::string enumFmt(const std::string& s, const valpair_t& v, 460 enumStyle_t style, bool isLast) const override { 461 if (style == enumMask || style == enumNoMask) 462 return ""; 463 return indent(5) + '"' + prependIfDigit(s, v.second) + "\": " + fmtNum("%d", v.first) + 464 (isLast ? "\n" : ",\n"); 465 } 466 }; 467 468 // base for C and C++ 469 class TPrinterCBase : public TPrinter { 470 protected: printPrologue(std::ostream & out) const471 virtual void printPrologue(std::ostream& out) const override { 472 out << "#ifndef spirv_" << headerGuardSuffix() << std::endl 473 << "#define spirv_" << headerGuardSuffix() << std::endl 474 << std::endl; 475 } 476 printMeta(std::ostream & out) const477 void printMeta(std::ostream& out) const override { 478 out << "#define SPV_VERSION 0x" << std::hex << DocVersion << std::dec << "\n"; 479 out << "#define SPV_REVISION " << DocRevision << "\n"; 480 out << "\n"; 481 482 return TPrinter::printMeta(out); 483 } 484 printEpilogue(std::ostream & out) const485 virtual void printEpilogue(std::ostream& out) const override { 486 out << "#endif" << std::endl; 487 } 488 printTypes(std::ostream & out) const489 virtual void printTypes(std::ostream& out) const override { 490 out << "typedef unsigned int " << pre() << "Id;\n\n"; 491 } 492 fmtConstInt(unsigned val,const std::string & name,const char * fmt,bool isLast) const493 virtual std::string fmtConstInt(unsigned val, const std::string& name, 494 const char* fmt, bool isLast) const override 495 { 496 return std::string("static const unsigned int ") + pre() + name + 497 " = " + fmtNum(fmt, val) + (isLast ? ";\n\n" : ";\n"); 498 } 499 pre() const500 virtual std::string pre() const { return ""; } // C name prefix 501 virtual std::string headerGuardSuffix() const = 0; 502 fmtEnumUse(const std::string & opPrefix,const std::string & name) const503 virtual std::string fmtEnumUse(const std::string& opPrefix, const std::string& name) const { return pre() + name; } 504 printHasResultType(std::ostream & out) const505 virtual void printHasResultType(std::ostream& out) const override 506 { 507 const Json::Value& enums = spvRoot["spv"]["enum"]; 508 509 std::set<unsigned> seenValues; 510 511 for (auto opClass = enums.begin(); opClass != enums.end(); ++opClass) { 512 const auto opName = (*opClass)["Name"].asString(); 513 if (opName != "Op") { 514 continue; 515 } 516 517 out << "#ifdef SPV_ENABLE_UTILITY_CODE" << std::endl; 518 out << "#ifndef __cplusplus" << std::endl; 519 out << "#include <stdbool.h>" << std::endl; 520 out << "#endif" << std::endl; 521 out << "inline void " << pre() << "HasResultAndType(" << pre() << opName << " opcode, bool *hasResult, bool *hasResultType) {" << std::endl; 522 out << " *hasResult = *hasResultType = false;" << std::endl; 523 out << " switch (opcode) {" << std::endl; 524 out << " default: /* unknown opcode */ break;" << std::endl; 525 526 for (auto& inst : spv::InstructionDesc) { 527 528 // Filter out duplicate enum values, which would break the switch statement. 529 // These are probably just extension enums promoted to core. 530 if (seenValues.find(inst.value) != seenValues.end()) { 531 continue; 532 } 533 seenValues.insert(inst.value); 534 535 std::string name = inst.name; 536 out << " case " << fmtEnumUse("Op", name) << ": *hasResult = " << (inst.hasResult() ? "true" : "false") << "; *hasResultType = " << (inst.hasType() ? "true" : "false") << "; break;" << std::endl; 537 } 538 539 out << " }" << std::endl; 540 out << "}" << std::endl; 541 out << "#endif /* SPV_ENABLE_UTILITY_CODE */" << std::endl << std::endl; 542 } 543 } 544 }; 545 546 // C printer 547 class TPrinterC final : public TPrinterCBase { 548 private: commentBeg() const549 std::string commentBeg() const override { return "/*\n"; } commentEnd(bool isLast) const550 std::string commentEnd(bool isLast) const override { return "*/\n"; } commentBOL() const551 std::string commentBOL() const override { return "** "; } 552 enumBeg(const std::string & s,enumStyle_t style) const553 std::string enumBeg(const std::string& s, enumStyle_t style) const override { 554 return std::string("typedef enum ") + pre() + s + styleStr(style) + "_ {\n"; 555 } 556 enumEnd(const std::string & s,enumStyle_t style,bool isLast) const557 std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override { 558 return "} " + pre() + s + styleStr(style) + ";\n\n"; 559 } 560 enumFmt(const std::string & s,const valpair_t & v,enumStyle_t style,bool isLast) const561 std::string enumFmt(const std::string& s, const valpair_t& v, 562 enumStyle_t style, bool isLast) const override { 563 return indent() + pre() + s + v.second + styleStr(style) + " = " + fmtStyleVal(v.first, style) + ",\n"; 564 } 565 maxEnumFmt(const std::string & s,const valpair_t & v,enumStyle_t style) const566 std::string maxEnumFmt(const std::string& s, const valpair_t& v, 567 enumStyle_t style) const override { 568 return enumFmt(s, v, style, true); 569 } 570 pre() const571 std::string pre() const override { return "Spv"; } // C name prefix headerGuardSuffix() const572 std::string headerGuardSuffix() const override { return "H"; } 573 }; 574 575 // C++ printer 576 class TPrinterCPP : public TPrinterCBase { 577 private: printPrologue(std::ostream & out) const578 void printPrologue(std::ostream& out) const override { 579 TPrinterCBase::printPrologue(out); 580 out << "namespace spv {\n\n"; 581 } 582 printEpilogue(std::ostream & out) const583 void printEpilogue(std::ostream& out) const override { 584 const Json::Value& enums = spvRoot["spv"]["enum"]; 585 586 out << "// Overload bitwise operators for mask bit combining\n\n"; 587 588 for (auto opClass = enums.begin(); opClass != enums.end(); ++opClass) { 589 const bool isMask = (*opClass)["Type"].asString() == "Bit"; 590 const auto opName = (*opClass)["Name"].asString(); 591 592 if (isMask) { 593 const auto typeName = opName + styleStr(enumMask); 594 595 // Overload operator| 596 out << "inline " << typeName << " operator|(" << typeName << " a, " << typeName << " b) { return " << 597 typeName << "(unsigned(a) | unsigned(b)); }\n"; 598 // Overload operator& 599 out << "inline " << typeName << " operator&(" << typeName << " a, " << typeName << " b) { return " << 600 typeName << "(unsigned(a) & unsigned(b)); }\n"; 601 // Overload operator^ 602 out << "inline " << typeName << " operator^(" << typeName << " a, " << typeName << " b) { return " << 603 typeName << "(unsigned(a) ^ unsigned(b)); }\n"; 604 // Overload operator~ 605 out << "inline " << typeName << " operator~(" << typeName << " a) { return " << 606 typeName << "(~unsigned(a)); }\n"; 607 } 608 } 609 610 out << "\n} // end namespace spv\n\n"; 611 out << "#endif // #ifndef spirv_" << headerGuardSuffix() << std::endl; 612 } 613 commentBOL() const614 std::string commentBOL() const override { return "// "; } 615 616 enumBeg(const std::string & s,enumStyle_t style) const617 virtual std::string enumBeg(const std::string& s, enumStyle_t style) const override { 618 return std::string("enum ") + s + styleStr(style) + " {\n"; 619 } 620 enumEnd(const std::string & s,enumStyle_t style,bool isLast) const621 std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override { 622 return "};\n\n"; 623 } 624 enumFmt(const std::string & s,const valpair_t & v,enumStyle_t style,bool isLast) const625 virtual std::string enumFmt(const std::string& s, const valpair_t& v, 626 enumStyle_t style, bool isLast) const override { 627 return indent() + s + v.second + styleStr(style) + " = " + fmtStyleVal(v.first, style) + ",\n"; 628 } 629 maxEnumFmt(const std::string & s,const valpair_t & v,enumStyle_t style) const630 virtual std::string maxEnumFmt(const std::string& s, const valpair_t& v, 631 enumStyle_t style) const override { 632 return enumFmt(s, v, style, true); 633 } 634 635 // The C++ and C++11 headers define types with the same name. So they 636 // should use the same header guard. headerGuardSuffix() const637 std::string headerGuardSuffix() const override { return "HPP"; } 638 639 std::string operators; 640 }; 641 642 // C++11 printer (uses enum classes) 643 class TPrinterCPP11 final : public TPrinterCPP { 644 private: enumBeg(const std::string & s,enumStyle_t style) const645 std::string enumBeg(const std::string& s, enumStyle_t style) const override { 646 return std::string("enum class ") + s + styleStr(style) + " : unsigned {\n"; 647 } 648 enumFmt(const std::string & s,const valpair_t & v,enumStyle_t style,bool isLast) const649 std::string enumFmt(const std::string& s, const valpair_t& v, 650 enumStyle_t style, bool isLast) const override { 651 return indent() + prependIfDigit(s, v.second) + " = " + fmtStyleVal(v.first, style) + ",\n"; 652 } 653 maxEnumFmt(const std::string & s,const valpair_t & v,enumStyle_t style) const654 std::string maxEnumFmt(const std::string& s, const valpair_t& v, 655 enumStyle_t style) const override { 656 return enumFmt(s, v, style, true); 657 } 658 659 // Add type prefix for scoped enum fmtEnumUse(const std::string & opPrefix,const std::string & name) const660 virtual std::string fmtEnumUse(const std::string& opPrefix, const std::string& name) const override { return opPrefix + "::" + name; } 661 headerGuardSuffix() const662 std::string headerGuardSuffix() const override { return "HPP"; } 663 }; 664 665 // LUA printer 666 class TPrinterLua final : public TPrinter { 667 private: printPrologue(std::ostream & out) const668 void printPrologue(std::ostream& out) const override { out << "spv = {\n"; } 669 printEpilogue(std::ostream & out) const670 void printEpilogue(std::ostream& out) const override { out << "}\n"; } 671 commentBOL() const672 std::string commentBOL() const override { return "-- "; } 673 enumBeg(const std::string & s,enumStyle_t style) const674 std::string enumBeg(const std::string& s, enumStyle_t style) const override { 675 return indent() + s + styleStr(style) + " = {\n"; 676 } 677 enumEnd(const std::string & s,enumStyle_t style,bool isLast) const678 std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override { 679 return indent() + "},\n\n"; 680 } 681 enumFmt(const std::string & s,const valpair_t & v,enumStyle_t style,bool isLast) const682 std::string enumFmt(const std::string& s, const valpair_t& v, 683 enumStyle_t style, bool isLast) const override { 684 return indent(2) + prependIfDigit(s, v.second) + " = " + fmtStyleVal(v.first, style) + ",\n"; 685 } 686 fmtConstInt(unsigned val,const std::string & name,const char * fmt,bool isLast) const687 virtual std::string fmtConstInt(unsigned val, const std::string& name, 688 const char* fmt, bool isLast) const override 689 { 690 return indent() + name + " = " + fmtNum(fmt, val) + (isLast ? ",\n\n" : ",\n"); 691 } 692 }; 693 694 // Python printer 695 class TPrinterPython final : public TPrinter { 696 private: printPrologue(std::ostream & out) const697 void printPrologue(std::ostream& out) const override { out << "spv = {\n"; } 698 printEpilogue(std::ostream & out) const699 void printEpilogue(std::ostream& out) const override { out << "}\n"; } 700 commentBOL() const701 std::string commentBOL() const override { return "# "; } 702 enumBeg(const std::string & s,enumStyle_t style) const703 std::string enumBeg(const std::string& s, enumStyle_t style) const override { 704 return indent() + "'" + s + styleStr(style) + "'" + " : {\n"; 705 } 706 enumEnd(const std::string & s,enumStyle_t style,bool isLast) const707 std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override { 708 return indent() + "},\n\n"; 709 } 710 enumFmt(const std::string & s,const valpair_t & v,enumStyle_t style,bool isLast) const711 std::string enumFmt(const std::string& s, const valpair_t& v, 712 enumStyle_t style, bool isLast) const override { 713 return indent(2) + "'" + prependIfDigit(s, v.second) + "'" + " : " + fmtStyleVal(v.first, style) + ",\n"; 714 } 715 fmtConstInt(unsigned val,const std::string & name,const char * fmt,bool isLast) const716 std::string fmtConstInt(unsigned val, const std::string& name, 717 const char* fmt, bool isLast) const override 718 { 719 return indent() + "'" + name + "'" + " : " + fmtNum(fmt, val) + (isLast ? ",\n\n" : ",\n"); 720 } 721 }; 722 723 // C# printer 724 class TPrinterCSharp final : public TPrinter { 725 private: commentBOL() const726 std::string commentBOL() const override { return "// "; } 727 printPrologue(std::ostream & out) const728 void printPrologue(std::ostream& out) const override { 729 out << "namespace Spv\n{\n\n"; 730 out << indent() << "public static class Specification\n"; 731 out << indent() << "{\n"; 732 } 733 printEpilogue(std::ostream & out) const734 void printEpilogue(std::ostream& out) const override { 735 out << indent() << "}\n"; 736 out << "}\n"; 737 } 738 enumBeg(const std::string & s,enumStyle_t style) const739 std::string enumBeg(const std::string& s, enumStyle_t style) const override { 740 return indent(2) + "public enum " + s + styleStr(style) + "\n" + indent(2) + "{\n"; 741 } 742 enumEnd(const std::string & s,enumStyle_t style,bool isLast) const743 std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override { 744 return indent(2) + "}" + + (isLast ? "\n" : "\n\n"); 745 } 746 enumFmt(const std::string & s,const valpair_t & v,enumStyle_t style,bool isLast) const747 std::string enumFmt(const std::string& s, const valpair_t& v, 748 enumStyle_t style, bool isLast) const override { 749 return indent(3) + prependIfDigit(s, v.second) + " = " + fmtStyleVal(v.first, style) + ",\n"; 750 } 751 fmtConstInt(unsigned val,const std::string & name,const char * fmt,bool isLast) const752 std::string fmtConstInt(unsigned val, const std::string& name, 753 const char* fmt, bool isLast) const override { 754 return indent(2) + std::string("public const uint ") + name + 755 " = " + fmtNum(fmt, val) + (isLast ? ";\n\n" : ";\n"); 756 } 757 }; 758 759 // D printer 760 class TPrinterD final : public TPrinter { 761 private: commentBeg() const762 std::string commentBeg() const override { return "/+\n"; } commentBOL() const763 std::string commentBOL() const override { return " + "; } commentEnd(bool isLast) const764 std::string commentEnd(bool isLast) const override { return " +/\n"; } 765 printPrologue(std::ostream & out) const766 void printPrologue(std::ostream& out) const override { 767 out << "module spv;\n\n"; 768 } 769 printEpilogue(std::ostream & out) const770 void printEpilogue(std::ostream& out) const override { 771 } 772 enumBeg(const std::string & s,enumStyle_t style) const773 std::string enumBeg(const std::string& s, enumStyle_t style) const override { 774 return "enum " + s + styleStr(style) + " : uint\n{\n"; 775 } 776 enumEnd(const std::string & s,enumStyle_t style,bool isLast) const777 std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override { 778 return std::string("}\n\n"); 779 } 780 enumFmt(const std::string & s,const valpair_t & v,enumStyle_t style,bool isLast) const781 std::string enumFmt(const std::string& s, const valpair_t& v, 782 enumStyle_t style, bool isLast) const override { 783 return indent() + prependIfDigit("_", v.second) + " = " + fmtStyleVal(v.first, style) + ",\n"; 784 } 785 fmtConstInt(unsigned val,const std::string & name,const char * fmt,bool isLast) const786 std::string fmtConstInt(unsigned val, const std::string& name, 787 const char* fmt, bool isLast) const override { 788 return std::string("enum uint ") + name + 789 " = " + fmtNum(fmt, val) + (isLast ? ";\n\n" : ";\n"); 790 } 791 }; 792 793 // Beef printer 794 class TPrinterBeef final : public TPrinter { 795 private: commentBOL() const796 std::string commentBOL() const override { return "// "; } 797 printPrologue(std::ostream & out) const798 void printPrologue(std::ostream& out) const override { 799 out << "namespace Spv\n{\n"; 800 out << indent() << "using System;\n\n"; 801 out << indent() << "public static class Specification\n"; 802 out << indent() << "{\n"; 803 } 804 printEpilogue(std::ostream & out) const805 void printEpilogue(std::ostream& out) const override { 806 out << indent() << "}\n"; 807 out << "}\n"; 808 } 809 enumBeg(const std::string & s,enumStyle_t style) const810 std::string enumBeg(const std::string& s, enumStyle_t style) const override { 811 return indent(2) + "[AllowDuplicates, CRepr] public enum " + s + styleStr(style) + "\n" + indent(2) + "{\n"; 812 } 813 enumEnd(const std::string & s,enumStyle_t style,bool isLast) const814 std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override { 815 return indent(2) + "}" + +(isLast ? "\n" : "\n\n"); 816 } 817 enumFmt(const std::string & s,const valpair_t & v,enumStyle_t style,bool isLast) const818 std::string enumFmt(const std::string& s, const valpair_t& v, 819 enumStyle_t style, bool isLast) const override { 820 return indent(3) + prependIfDigit(s, v.second) + " = " + fmtStyleVal(v.first, style) + ",\n"; 821 } 822 fmtConstInt(unsigned val,const std::string & name,const char * fmt,bool isLast) const823 std::string fmtConstInt(unsigned val, const std::string& name, 824 const char* fmt, bool isLast) const override { 825 return indent(2) + std::string("public const uint32 ") + name + 826 " = " + fmtNum(fmt, val) + (isLast ? ";\n\n" : ";\n"); 827 } 828 }; 829 830 } // namespace 831 832 namespace spv { PrintAllHeaders()833 void PrintAllHeaders() 834 { 835 // TODO: Once MSVC 2012 is no longer a factor, use brace initializers here 836 std::vector<std::pair<TLanguage, std::string>> langInfo; 837 838 langInfo.push_back(std::make_pair(ELangC, "spirv.h")); 839 langInfo.push_back(std::make_pair(ELangCPP, "spirv.hpp")); 840 langInfo.push_back(std::make_pair(ELangCPP11, "spirv.hpp11")); 841 langInfo.push_back(std::make_pair(ELangJSON, "spirv.json")); 842 langInfo.push_back(std::make_pair(ELangLua, "spirv.lua")); 843 langInfo.push_back(std::make_pair(ELangPython, "spirv.py")); 844 langInfo.push_back(std::make_pair(ELangCSharp, "spirv.cs")); 845 langInfo.push_back(std::make_pair(ELangD, "spv.d")); 846 langInfo.push_back(std::make_pair(ELangBeef, "spirv.bf")); 847 848 for (const auto& lang : langInfo) { 849 std::ofstream out(lang.second, std::ios::out); 850 851 if ((out.rdstate() & std::ifstream::failbit)) { 852 std::cerr << "Unable to open file: " << lang.second << std::endl; 853 } else { 854 PrintHeader(lang.first, out); 855 } 856 } 857 } 858 859 // Print header for given language to given output stream PrintHeader(TLanguage lang,std::ostream & out)860 void PrintHeader(TLanguage lang, std::ostream& out) 861 { 862 typedef std::unique_ptr<TPrinter> TPrinterPtr; 863 TPrinterPtr p; 864 865 switch (lang) { 866 case ELangC: p = TPrinterPtr(new TPrinterC); break; 867 case ELangCPP: p = TPrinterPtr(new TPrinterCPP); break; 868 case ELangCPP11: p = TPrinterPtr(new TPrinterCPP11); break; 869 case ELangJSON: p = TPrinterPtr(new TPrinterJSON); break; 870 case ELangLua: p = TPrinterPtr(new TPrinterLua); break; 871 case ELangPython: p = TPrinterPtr(new TPrinterPython); break; 872 case ELangCSharp: p = TPrinterPtr(new TPrinterCSharp); break; 873 case ELangD: p = TPrinterPtr(new TPrinterD); break; 874 case ELangBeef: p = TPrinterPtr(new TPrinterBeef); break; 875 case ELangAll: PrintAllHeaders(); break; 876 default: 877 std::cerr << "Unknown language." << std::endl; 878 return; 879 } 880 881 // Print the data in the requested format 882 if (p) 883 out << *p << std::endl; 884 885 // object is auto-deleted 886 } 887 888 } // namespace spv 889