• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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