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 = 0x00010500; 73 static const int DocRevision = 4; 74 #define DocRevisionString "4" 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\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 "\n" 213 "Some tokens act like mask values, which can be OR'd together,\n" 214 "while others are mutually exclusive. The mask-like ones have\n" 215 "\"Mask\" in their name, and a parallel enum that has the shift\n" 216 "amount (1 << x) for each corresponding enumerant.\n"; 217 218 // Construct TPrinter()219 TPrinter::TPrinter() 220 { 221 Json::Value& meta = spvRoot["spv"]["meta"]; 222 Json::Value& enums = spvRoot["spv"]["enum"]; 223 224 meta["MagicNumber"] = DocMagicNumber; 225 meta["Version"] = DocVersion; 226 meta["Revision"] = DocRevision; 227 meta["OpCodeMask"] = 0xffff; 228 meta["WordCountShift"] = 16; 229 230 int commentId = 0; 231 addComment(meta["Comment"][commentId++], DocCopyright); 232 addComment(meta["Comment"][commentId++], DocComment1); 233 addComment(meta["Comment"][commentId++], DocComment2); 234 235 for (int e = spv::OperandSource; e < spv::OperandOpcode; ++e) { 236 auto& enumSet = spv::OperandClassParams[e]; 237 const bool mask = enumSet.bitmask; 238 const std::string enumName = enumSet.codeName; 239 240 for (auto& enumRow : enumSet) { 241 std::string name = enumRow.name; 242 enums[e - spv::OperandSource]["Values"][name] = enumRow.value; 243 } 244 245 enums[e - spv::OperandSource]["Type"] = mask ? "Bit" : "Value"; 246 enums[e - spv::OperandSource]["Name"] = enumName; 247 } 248 249 // Instructions are in their own different table 250 { 251 auto& entry = enums[spv::OperandOpcode - spv::OperandSource]; 252 for (auto& enumRow : spv::InstructionDesc) { 253 std::string name = enumRow.name; 254 entry["Values"][name] = enumRow.value; 255 } 256 entry["Type"] = "Value"; 257 entry["Name"] = "Op"; 258 } 259 } 260 261 // Create comment addComment(Json::Value & node,const std::string & str)262 void TPrinter::addComment(Json::Value& node, const std::string& str) 263 { 264 std::istringstream cstream(str); 265 std::string cline; 266 267 int line = 0; 268 while (std::getline(cstream, cline)) // fmt each line 269 node[line++] = cline; 270 } 271 272 273 // Return a list of values sorted by enum value. The std::vector 274 // returned by value is okay in c++11 due to move semantics. 275 std::vector<TPrinter::valpair_t> getSortedVals(const Json::Value & p) const276 TPrinter::getSortedVals(const Json::Value& p) const 277 { 278 std::vector<valpair_t> values; 279 280 for (auto e = p.begin(); e != p.end(); ++e) 281 values.push_back(valpair_t(e->asUInt(), e.name())); 282 283 // Use a stable sort because we might have aliases, e.g. 284 // SubgropuBallot (might be in future core) vs. SubgroupBallotKHR. 285 std::stable_sort(values.begin(), values.end()); 286 287 return values; 288 } 289 290 // Escape comment characters if needed escapeComment(const std::string & s) const291 std::string TPrinter::escapeComment(const std::string& s) const { return s; } 292 293 // Format comments in language specific way printComments(std::ostream & out) const294 void TPrinter::printComments(std::ostream& out) const 295 { 296 const int commentCount = spvRoot["spv"]["meta"]["Comment"].size(); 297 int commentNum = 0; 298 299 for (const auto& comment : spvRoot["spv"]["meta"]["Comment"]) { 300 out << commentBeg(); 301 302 for (int line = 0; line < int(comment.size()); ++line) 303 out << commentBOL() << escapeComment(comment[line].asString()) << 304 commentEOL((line+1) == comment.size()) << std::endl; 305 306 out << commentEnd(++commentNum == commentCount) << std::endl; 307 } 308 } 309 310 // Format header metadata printMeta(std::ostream & out) const311 void TPrinter::printMeta(std::ostream& out) const 312 { 313 const Json::Value& meta = spvRoot["spv"]["meta"]; 314 315 const auto print = [&](const char* name, const char* fmt, bool isLast) { 316 out << fmtConstInt(meta[name].asUInt(), name, fmt, isLast); 317 }; 318 319 print("MagicNumber", "0x%08lx", false); 320 print("Version", "0x%08lx", false); 321 print("Revision", "%d", false); 322 print("OpCodeMask", "0x%04x", false); 323 print("WordCountShift", "%d", true); 324 } 325 326 // Format value definitions in language specific way printDefs(std::ostream & out) const327 void TPrinter::printDefs(std::ostream& out) const 328 { 329 const Json::Value& enums = spvRoot["spv"]["enum"]; 330 331 for (auto opClass = enums.begin(); opClass != enums.end(); ++opClass) { 332 const bool isMask = (*opClass)["Type"].asString() == "Bit"; 333 const auto opName = (*opClass)["Name"].asString(); 334 const auto opPrefix = opName == "Op" ? "" : opName; 335 336 for (enumStyle_t style = (isMask ? enumShift : enumCount); 337 style <= (isMask ? enumMask : enumCount); style = enumStyle_t(int(style)+1)) { 338 339 out << enumBeg(opName, style); 340 341 if (style == enumMask) 342 out << enumFmt(opPrefix, valpair_t(0, "MaskNone"), enumNoMask); 343 344 const auto sorted = getSortedVals((*opClass)["Values"]); 345 346 std::string maxEnum = maxEnumFmt(opName, valpair_t(0x7FFFFFFF, "Max"), enumHex); 347 348 bool printMax = (style != enumMask && maxEnum.size() > 0); 349 350 for (const auto& v : sorted) 351 out << enumFmt(opPrefix, v, style, !printMax && v.second == sorted.back().second); 352 353 if (printMax) 354 out << maxEnum; 355 356 auto nextOpClass = opClass; 357 out << enumEnd(opName, style, ++nextOpClass == enums.end()); 358 } 359 } 360 } 361 printAll(std::ostream & out) const362 void TPrinter::printAll(std::ostream& out) const 363 { 364 printComments(out); 365 printPrologue(out); 366 printTypes(out); 367 printMeta(out); 368 printDefs(out); 369 printHasResultType(out); 370 printEpilogue(out); 371 } 372 373 // Stream entire header to output operator <<(std::ostream & out,const TPrinter & p)374 std::ostream& operator<<(std::ostream& out, const TPrinter &p) 375 { 376 p.printAll(out); 377 return out; 378 } 379 380 // JSON printer. Rather than use the default printer, we supply our own so 381 // we can control the printing order within various containers. 382 class TPrinterJSON final : public TPrinter { 383 private: printPrologue(std::ostream & out) const384 void printPrologue(std::ostream& out) const override { out << "{\n" + indent() + "\"spv\":\n" + indent() + "{\n"; } printEpilogue(std::ostream & out) const385 void printEpilogue(std::ostream& out) const override { out << indent() + "}\n}\n"; } 386 escapeComment(const std::string & s) const387 std::string escapeComment(const std::string& s) const override { 388 std::string newStr; 389 for (auto c : s) { 390 if (c == '"') { 391 newStr += '\\'; 392 newStr += c; 393 } else { 394 newStr += c; 395 } 396 } 397 return newStr; 398 } 399 fmtConstInt(unsigned val,const std::string & name,const char * fmt,bool isLast) const400 std::string fmtConstInt(unsigned val, const std::string& name, 401 const char* fmt, bool isLast) const override { 402 return indent(3) + '"' + name + "\": " + fmtNum("%d", val) + (isLast ? "\n" : ",\n"); 403 } 404 printMeta(std::ostream & out) const405 void printMeta(std::ostream& out) const override 406 { 407 out << indent(2) + "\"meta\":\n" + indent(2) + "{\n"; 408 printComments(out); 409 TPrinter::printMeta(out); 410 out << indent(2) + "},\n"; 411 } 412 commentBeg() const413 std::string commentBeg() const override { return indent(4) + "[\n"; } commentEnd(bool isLast) const414 std::string commentEnd(bool isLast) const override { return indent(4) + (isLast ? "]" : "],"); } commentBOL() const415 std::string commentBOL() const override { return indent(5) + '"'; } commentEOL(bool isLast) const416 std::string commentEOL(bool isLast) const override { return (isLast ? "\"" : "\","); } 417 printComments(std::ostream & out) const418 void printComments(std::ostream& out) const override 419 { 420 out << indent(3) + "\"Comment\":\n" + indent(3) + "[\n"; 421 TPrinter::printComments(out); 422 out << indent(3) + "],\n"; 423 } 424 printDefs(std::ostream & out) const425 void printDefs(std::ostream& out) const override 426 { 427 out << indent(2) + "\"enum\":\n" + indent(2) + "[\n"; 428 TPrinter::printDefs(out); 429 out << indent(2) + "]\n"; 430 } 431 printAll(std::ostream & out) const432 void printAll(std::ostream& out) const override 433 { 434 printPrologue(out); 435 printMeta(out); 436 printDefs(out); 437 printEpilogue(out); 438 } 439 enumBeg(const std::string & s,enumStyle_t style) const440 std::string enumBeg(const std::string& s, enumStyle_t style) const override { 441 if (style == enumMask) 442 return ""; 443 return indent(3) + "{\n" + 444 indent(4) + "\"Name\": \"" + s + "\",\n" + 445 indent(4) + "\"Type\": " + (style == enumShift ? "\"Bit\"" : "\"Value\"") + ",\n" + 446 indent(4) + "\"Values\":\n" + 447 indent(4) + "{\n"; 448 } 449 enumEnd(const std::string & s,enumStyle_t style,bool isLast) const450 std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override { 451 if (style == enumMask) 452 return ""; 453 return indent(4) + "}\n" + 454 indent(3) + "}" + (isLast ? "" : ",") + "\n"; 455 } 456 enumFmt(const std::string & s,const valpair_t & v,enumStyle_t style,bool isLast) const457 std::string enumFmt(const std::string& s, const valpair_t& v, 458 enumStyle_t style, bool isLast) const override { 459 if (style == enumMask || style == enumNoMask) 460 return ""; 461 return indent(5) + '"' + prependIfDigit(s, v.second) + "\": " + fmtNum("%d", v.first) + 462 (isLast ? "\n" : ",\n"); 463 } 464 }; 465 466 // base for C and C++ 467 class TPrinterCBase : public TPrinter { 468 protected: printPrologue(std::ostream & out) const469 virtual void printPrologue(std::ostream& out) const override { 470 out << "#ifndef spirv_" << headerGuardSuffix() << std::endl 471 << "#define spirv_" << headerGuardSuffix() << std::endl 472 << std::endl; 473 } 474 printMeta(std::ostream & out) const475 void printMeta(std::ostream& out) const override { 476 out << "#define SPV_VERSION 0x" << std::hex << DocVersion << std::dec << "\n"; 477 out << "#define SPV_REVISION " << DocRevision << "\n"; 478 out << "\n"; 479 480 return TPrinter::printMeta(out); 481 } 482 printEpilogue(std::ostream & out) const483 virtual void printEpilogue(std::ostream& out) const override { 484 out << "#endif" << std::endl; 485 } 486 printTypes(std::ostream & out) const487 virtual void printTypes(std::ostream& out) const override { 488 out << "typedef unsigned int " << pre() << "Id;\n\n"; 489 } 490 fmtConstInt(unsigned val,const std::string & name,const char * fmt,bool isLast) const491 virtual std::string fmtConstInt(unsigned val, const std::string& name, 492 const char* fmt, bool isLast) const override 493 { 494 return std::string("static const unsigned int ") + pre() + name + 495 " = " + fmtNum(fmt, val) + (isLast ? ";\n\n" : ";\n"); 496 } 497 pre() const498 virtual std::string pre() const { return ""; } // C name prefix 499 virtual std::string headerGuardSuffix() const = 0; 500 fmtEnumUse(const std::string & opPrefix,const std::string & name) const501 virtual std::string fmtEnumUse(const std::string& opPrefix, const std::string& name) const { return pre() + name; } 502 printHasResultType(std::ostream & out) const503 virtual void printHasResultType(std::ostream& out) const 504 { 505 const Json::Value& enums = spvRoot["spv"]["enum"]; 506 507 std::set<unsigned> seenValues; 508 509 for (auto opClass = enums.begin(); opClass != enums.end(); ++opClass) { 510 const auto opName = (*opClass)["Name"].asString(); 511 if (opName != "Op") { 512 continue; 513 } 514 515 out << "#ifdef SPV_ENABLE_UTILITY_CODE" << std::endl; 516 out << "inline void " << pre() << "HasResultAndType(" << pre() << opName << " opcode, bool *hasResult, bool *hasResultType) {" << std::endl; 517 out << " *hasResult = *hasResultType = false;" << std::endl; 518 out << " switch (opcode) {" << std::endl; 519 out << " default: /* unknown opcode */ break;" << std::endl; 520 521 for (auto& inst : spv::InstructionDesc) { 522 523 // Filter out duplicate enum values, which would break the switch statement. 524 // These are probably just extension enums promoted to core. 525 if (seenValues.find(inst.value) != seenValues.end()) { 526 continue; 527 } 528 seenValues.insert(inst.value); 529 530 std::string name = inst.name; 531 out << " case " << fmtEnumUse("Op", name) << ": *hasResult = " << (inst.hasResult() ? "true" : "false") << "; *hasResultType = " << (inst.hasType() ? "true" : "false") << "; break;" << std::endl; 532 } 533 534 out << " }" << std::endl; 535 out << "}" << std::endl; 536 out << "#endif /* SPV_ENABLE_UTILITY_CODE */" << std::endl << std::endl; 537 } 538 } 539 }; 540 541 // C printer 542 class TPrinterC final : public TPrinterCBase { 543 private: commentBeg() const544 std::string commentBeg() const override { return "/*\n"; } commentEnd(bool isLast) const545 std::string commentEnd(bool isLast) const override { return "*/\n"; } commentBOL() const546 std::string commentBOL() const override { return "** "; } 547 enumBeg(const std::string & s,enumStyle_t style) const548 std::string enumBeg(const std::string& s, enumStyle_t style) const override { 549 return std::string("typedef enum ") + pre() + s + styleStr(style) + "_ {\n"; 550 } 551 enumEnd(const std::string & s,enumStyle_t style,bool isLast) const552 std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override { 553 return "} " + pre() + s + styleStr(style) + ";\n\n"; 554 } 555 enumFmt(const std::string & s,const valpair_t & v,enumStyle_t style,bool isLast) const556 std::string enumFmt(const std::string& s, const valpair_t& v, 557 enumStyle_t style, bool isLast) const override { 558 return indent() + pre() + s + v.second + styleStr(style) + " = " + fmtStyleVal(v.first, style) + ",\n"; 559 } 560 maxEnumFmt(const std::string & s,const valpair_t & v,enumStyle_t style) const561 std::string maxEnumFmt(const std::string& s, const valpair_t& v, 562 enumStyle_t style) const override { 563 return enumFmt(s, v, style, true); 564 } 565 pre() const566 std::string pre() const override { return "Spv"; } // C name prefix headerGuardSuffix() const567 std::string headerGuardSuffix() const override { return "H"; } 568 }; 569 570 // C++ printer 571 class TPrinterCPP : public TPrinterCBase { 572 private: printPrologue(std::ostream & out) const573 void printPrologue(std::ostream& out) const override { 574 TPrinterCBase::printPrologue(out); 575 out << "namespace spv {\n\n"; 576 } 577 printEpilogue(std::ostream & out) const578 void printEpilogue(std::ostream& out) const override { 579 const Json::Value& enums = spvRoot["spv"]["enum"]; 580 581 // Create overloaded operator| for mask types 582 out << "// Overload operator| for mask bit combining\n\n"; 583 584 for (auto opClass = enums.begin(); opClass != enums.end(); ++opClass) { 585 const bool isMask = (*opClass)["Type"].asString() == "Bit"; 586 const auto opName = (*opClass)["Name"].asString(); 587 588 if (isMask) { 589 const auto typeName = opName + styleStr(enumMask); 590 591 out << "inline " + typeName + " operator|(" + typeName + " a, " + typeName + " b) { return " + 592 typeName + "(unsigned(a) | unsigned(b)); }\n"; 593 } 594 } 595 596 out << "\n} // end namespace spv\n\n"; 597 out << "#endif // #ifndef spirv_" << headerGuardSuffix() << std::endl; 598 } 599 commentBOL() const600 std::string commentBOL() const override { return "// "; } 601 602 enumBeg(const std::string & s,enumStyle_t style) const603 virtual std::string enumBeg(const std::string& s, enumStyle_t style) const override { 604 return std::string("enum ") + s + styleStr(style) + " {\n"; 605 } 606 enumEnd(const std::string & s,enumStyle_t style,bool isLast) const607 std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override { 608 return "};\n\n"; 609 } 610 enumFmt(const std::string & s,const valpair_t & v,enumStyle_t style,bool isLast) const611 virtual std::string enumFmt(const std::string& s, const valpair_t& v, 612 enumStyle_t style, bool isLast) const override { 613 return indent() + s + v.second + styleStr(style) + " = " + fmtStyleVal(v.first, style) + ",\n"; 614 } 615 maxEnumFmt(const std::string & s,const valpair_t & v,enumStyle_t style) const616 virtual std::string maxEnumFmt(const std::string& s, const valpair_t& v, 617 enumStyle_t style) const override { 618 return enumFmt(s, v, style, true); 619 } 620 621 // The C++ and C++11 headers define types with the same name. So they 622 // should use the same header guard. headerGuardSuffix() const623 std::string headerGuardSuffix() const override { return "HPP"; } 624 625 std::string operators; 626 }; 627 628 // C++11 printer (uses enum classes) 629 class TPrinterCPP11 final : public TPrinterCPP { 630 private: enumBeg(const std::string & s,enumStyle_t style) const631 std::string enumBeg(const std::string& s, enumStyle_t style) const override { 632 return std::string("enum class ") + s + styleStr(style) + " : unsigned {\n"; 633 } 634 enumFmt(const std::string & s,const valpair_t & v,enumStyle_t style,bool isLast) const635 std::string enumFmt(const std::string& s, const valpair_t& v, 636 enumStyle_t style, bool isLast) const override { 637 return indent() + prependIfDigit(s, v.second) + " = " + fmtStyleVal(v.first, style) + ",\n"; 638 } 639 maxEnumFmt(const std::string & s,const valpair_t & v,enumStyle_t style) const640 std::string maxEnumFmt(const std::string& s, const valpair_t& v, 641 enumStyle_t style) const override { 642 return enumFmt(s, v, style, true); 643 } 644 645 // Add type prefix for scoped enum fmtEnumUse(const std::string & opPrefix,const std::string & name) const646 virtual std::string fmtEnumUse(const std::string& opPrefix, const std::string& name) const { return opPrefix + "::" + name; } 647 headerGuardSuffix() const648 std::string headerGuardSuffix() const override { return "HPP"; } 649 }; 650 651 // LUA printer 652 class TPrinterLua final : public TPrinter { 653 private: printPrologue(std::ostream & out) const654 void printPrologue(std::ostream& out) const override { out << "spv = {\n"; } 655 printEpilogue(std::ostream & out) const656 void printEpilogue(std::ostream& out) const override { out << "}\n"; } 657 commentBOL() const658 std::string commentBOL() const override { return "-- "; } 659 enumBeg(const std::string & s,enumStyle_t style) const660 std::string enumBeg(const std::string& s, enumStyle_t style) const override { 661 return indent() + s + styleStr(style) + " = {\n"; 662 } 663 enumEnd(const std::string & s,enumStyle_t style,bool isLast) const664 std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override { 665 return indent() + "},\n\n"; 666 } 667 enumFmt(const std::string & s,const valpair_t & v,enumStyle_t style,bool isLast) const668 std::string enumFmt(const std::string& s, const valpair_t& v, 669 enumStyle_t style, bool isLast) const override { 670 return indent(2) + prependIfDigit(s, v.second) + " = " + fmtStyleVal(v.first, style) + ",\n"; 671 } 672 fmtConstInt(unsigned val,const std::string & name,const char * fmt,bool isLast) const673 virtual std::string fmtConstInt(unsigned val, const std::string& name, 674 const char* fmt, bool isLast) const override 675 { 676 return indent() + name + " = " + fmtNum(fmt, val) + (isLast ? ",\n\n" : ",\n"); 677 } 678 }; 679 680 // Python printer 681 class TPrinterPython final : public TPrinter { 682 private: printPrologue(std::ostream & out) const683 void printPrologue(std::ostream& out) const override { out << "spv = {\n"; } 684 printEpilogue(std::ostream & out) const685 void printEpilogue(std::ostream& out) const override { out << "}\n"; } 686 commentBOL() const687 std::string commentBOL() const override { return "# "; } 688 enumBeg(const std::string & s,enumStyle_t style) const689 std::string enumBeg(const std::string& s, enumStyle_t style) const override { 690 return indent() + "'" + s + styleStr(style) + "'" + " : {\n"; 691 } 692 enumEnd(const std::string & s,enumStyle_t style,bool isLast) const693 std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override { 694 return indent() + "},\n\n"; 695 } 696 enumFmt(const std::string & s,const valpair_t & v,enumStyle_t style,bool isLast) const697 std::string enumFmt(const std::string& s, const valpair_t& v, 698 enumStyle_t style, bool isLast) const override { 699 return indent(2) + "'" + prependIfDigit(s, v.second) + "'" + " : " + fmtStyleVal(v.first, style) + ",\n"; 700 } 701 fmtConstInt(unsigned val,const std::string & name,const char * fmt,bool isLast) const702 std::string fmtConstInt(unsigned val, const std::string& name, 703 const char* fmt, bool isLast) const override 704 { 705 return indent() + "'" + name + "'" + " : " + fmtNum(fmt, val) + (isLast ? ",\n\n" : ",\n"); 706 } 707 }; 708 709 // C# printer 710 class TPrinterCSharp final : public TPrinter { 711 private: commentBOL() const712 std::string commentBOL() const override { return "// "; } 713 printPrologue(std::ostream & out) const714 void printPrologue(std::ostream& out) const override { 715 out << "namespace Spv\n{\n\n"; 716 out << indent() << "public static class Specification\n"; 717 out << indent() << "{\n"; 718 } 719 printEpilogue(std::ostream & out) const720 void printEpilogue(std::ostream& out) const override { 721 out << indent() << "}\n"; 722 out << "}\n"; 723 } 724 enumBeg(const std::string & s,enumStyle_t style) const725 std::string enumBeg(const std::string& s, enumStyle_t style) const override { 726 return indent(2) + "public enum " + s + styleStr(style) + "\n" + indent(2) + "{\n"; 727 } 728 enumEnd(const std::string & s,enumStyle_t style,bool isLast) const729 std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override { 730 return indent(2) + "}" + + (isLast ? "\n" : "\n\n"); 731 } 732 enumFmt(const std::string & s,const valpair_t & v,enumStyle_t style,bool isLast) const733 std::string enumFmt(const std::string& s, const valpair_t& v, 734 enumStyle_t style, bool isLast) const override { 735 return indent(3) + prependIfDigit(s, v.second) + " = " + fmtStyleVal(v.first, style) + ",\n"; 736 } 737 fmtConstInt(unsigned val,const std::string & name,const char * fmt,bool isLast) const738 std::string fmtConstInt(unsigned val, const std::string& name, 739 const char* fmt, bool isLast) const override { 740 return indent(2) + std::string("public const uint ") + name + 741 " = " + fmtNum(fmt, val) + (isLast ? ";\n\n" : ";\n"); 742 } 743 }; 744 745 // D printer 746 class TPrinterD final : public TPrinter { 747 private: commentBeg() const748 std::string commentBeg() const override { return "/+\n"; } commentBOL() const749 std::string commentBOL() const override { return " + "; } commentEnd(bool isLast) const750 std::string commentEnd(bool isLast) const override { return " +/\n"; } 751 printPrologue(std::ostream & out) const752 void printPrologue(std::ostream& out) const override { 753 out << "module spv;\n\n"; 754 } 755 printEpilogue(std::ostream & out) const756 void printEpilogue(std::ostream& out) const override { 757 } 758 enumBeg(const std::string & s,enumStyle_t style) const759 std::string enumBeg(const std::string& s, enumStyle_t style) const override { 760 return "enum " + s + styleStr(style) + " : uint\n{\n"; 761 } 762 enumEnd(const std::string & s,enumStyle_t style,bool isLast) const763 std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override { 764 return std::string("}\n\n"); 765 } 766 enumFmt(const std::string & s,const valpair_t & v,enumStyle_t style,bool isLast) const767 std::string enumFmt(const std::string& s, const valpair_t& v, 768 enumStyle_t style, bool isLast) const override { 769 return indent() + prependIfDigit("_", v.second) + " = " + fmtStyleVal(v.first, style) + ",\n"; 770 } 771 fmtConstInt(unsigned val,const std::string & name,const char * fmt,bool isLast) const772 std::string fmtConstInt(unsigned val, const std::string& name, 773 const char* fmt, bool isLast) const override { 774 return std::string("enum uint ") + name + 775 " = " + fmtNum(fmt, val) + (isLast ? ";\n\n" : ";\n"); 776 } 777 }; 778 779 } // namespace 780 781 namespace spv { PrintAllHeaders()782 void PrintAllHeaders() 783 { 784 // TODO: Once MSVC 2012 is no longer a factor, use brace initializers here 785 std::vector<std::pair<TLanguage, std::string>> langInfo; 786 787 langInfo.push_back(std::make_pair(ELangC, "spirv.h")); 788 langInfo.push_back(std::make_pair(ELangCPP, "spirv.hpp")); 789 langInfo.push_back(std::make_pair(ELangCPP11, "spirv.hpp11")); 790 langInfo.push_back(std::make_pair(ELangJSON, "spirv.json")); 791 langInfo.push_back(std::make_pair(ELangLua, "spirv.lua")); 792 langInfo.push_back(std::make_pair(ELangPython, "spirv.py")); 793 langInfo.push_back(std::make_pair(ELangCSharp, "spirv.cs")); 794 langInfo.push_back(std::make_pair(ELangD, "spv.d")); 795 796 for (const auto& lang : langInfo) { 797 std::ofstream out(lang.second, std::ios::out); 798 799 if ((out.rdstate() & std::ifstream::failbit)) { 800 std::cerr << "Unable to open file: " << lang.second << std::endl; 801 } else { 802 PrintHeader(lang.first, out); 803 } 804 } 805 } 806 807 // Print header for given language to given output stream PrintHeader(TLanguage lang,std::ostream & out)808 void PrintHeader(TLanguage lang, std::ostream& out) 809 { 810 typedef std::unique_ptr<TPrinter> TPrinterPtr; 811 TPrinterPtr p; 812 813 switch (lang) { 814 case ELangC: p = TPrinterPtr(new TPrinterC); break; 815 case ELangCPP: p = TPrinterPtr(new TPrinterCPP); break; 816 case ELangCPP11: p = TPrinterPtr(new TPrinterCPP11); break; 817 case ELangJSON: p = TPrinterPtr(new TPrinterJSON); break; 818 case ELangLua: p = TPrinterPtr(new TPrinterLua); break; 819 case ELangPython: p = TPrinterPtr(new TPrinterPython); break; 820 case ELangCSharp: p = TPrinterPtr(new TPrinterCSharp); break; 821 case ELangD: p = TPrinterPtr(new TPrinterD); break; 822 case ELangAll: PrintAllHeaders(); break; 823 default: 824 std::cerr << "Unknown language." << std::endl; 825 return; 826 } 827 828 // Print the data in the requested format 829 if (p) 830 out << *p << std::endl; 831 832 // object is auto-deleted 833 } 834 835 } // namespace spv 836