• 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     protected:
printMaskOperators(std::ostream & out,const std::string & specifiers) const578         void printMaskOperators(std::ostream& out, const std::string& specifiers) const {
579             const Json::Value& enums = spvRoot["spv"]["enum"];
580 
581             out << "// Overload bitwise operators for mask bit combining\n\n";
582 
583             for (auto opClass = enums.begin(); opClass != enums.end(); ++opClass) {
584                 const bool isMask   = (*opClass)["Type"].asString() == "Bit";
585                 const auto opName   = (*opClass)["Name"].asString();
586 
587                 if (isMask) {
588                     const auto typeName = opName + styleStr(enumMask);
589 
590                     // Overload operator|
591                     out << specifiers << " " << typeName << " operator|(" << typeName << " a, " << typeName << " b) { return " <<
592                         typeName << "(unsigned(a) | unsigned(b)); }\n";
593                     // Overload operator&
594                     out << specifiers << " " << typeName << " operator&(" << typeName << " a, " << typeName << " b) { return " <<
595                         typeName << "(unsigned(a) & unsigned(b)); }\n";
596                     // Overload operator^
597                     out << specifiers << " " << typeName << " operator^(" << typeName << " a, " << typeName << " b) { return " <<
598                         typeName << "(unsigned(a) ^ unsigned(b)); }\n";
599                     // Overload operator~
600                     out << specifiers << " " << typeName << " operator~(" << typeName << " a) { return " <<
601                         typeName << "(~unsigned(a)); }\n";
602                 }
603             }
604         }
605     private:
printPrologue(std::ostream & out) const606         void printPrologue(std::ostream& out) const override {
607             TPrinterCBase::printPrologue(out);
608             out << "namespace spv {\n\n";
609         }
610 
printEpilogue(std::ostream & out) const611         void printEpilogue(std::ostream& out) const override {
612             printMaskOperators(out, "inline");
613             out << "\n}  // end namespace spv\n\n";
614             out << "#endif  // #ifndef spirv_" << headerGuardSuffix() << std::endl;
615         }
616 
commentBOL() const617         std::string commentBOL() const override { return "// "; }
618 
619 
enumBeg(const std::string & s,enumStyle_t style) const620         virtual std::string enumBeg(const std::string& s, enumStyle_t style) const override {
621             return std::string("enum ") + s + styleStr(style) + " {\n";
622         }
623 
enumEnd(const std::string & s,enumStyle_t style,bool isLast) const624         std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override {
625             return "};\n\n";
626         }
627 
enumFmt(const std::string & s,const valpair_t & v,enumStyle_t style,bool isLast) const628         virtual std::string enumFmt(const std::string& s, const valpair_t& v,
629                                     enumStyle_t style, bool isLast) const override {
630             return indent() + s + v.second + styleStr(style) + " = " + fmtStyleVal(v.first, style) + ",\n";
631         }
632 
maxEnumFmt(const std::string & s,const valpair_t & v,enumStyle_t style) const633         virtual std::string maxEnumFmt(const std::string& s, const valpair_t& v,
634                                        enumStyle_t style) const override {
635             return enumFmt(s, v, style, true);
636         }
637 
638         // The C++ and C++11 headers define types with the same name. So they
639         // should use the same header guard.
headerGuardSuffix() const640         std::string headerGuardSuffix() const override { return "HPP"; }
641 
642         std::string operators;
643     };
644 
645     // C++11 printer (uses enum classes)
646     class TPrinterCPP11 final : public TPrinterCPP {
647     private:
printEpilogue(std::ostream & out) const648         void printEpilogue(std::ostream& out) const override {
649             printMaskOperators(out, "constexpr");
650             out << "\n}  // end namespace spv\n\n";
651             out << "#endif  // #ifndef spirv_" << headerGuardSuffix() << std::endl;
652         }
enumBeg(const std::string & s,enumStyle_t style) const653         std::string enumBeg(const std::string& s, enumStyle_t style) const override {
654             return std::string("enum class ") + s + styleStr(style) + " : unsigned {\n";
655         }
656 
enumFmt(const std::string & s,const valpair_t & v,enumStyle_t style,bool isLast) const657         std::string enumFmt(const std::string& s, const valpair_t& v,
658                             enumStyle_t style, bool isLast) const override {
659             return indent() + prependIfDigit(s, v.second) + " = " + fmtStyleVal(v.first, style) + ",\n";
660         }
661 
maxEnumFmt(const std::string & s,const valpair_t & v,enumStyle_t style) const662         std::string maxEnumFmt(const std::string& s, const valpair_t& v,
663                                enumStyle_t style) const override {
664             return enumFmt(s, v, style, true);
665         }
666 
667         // Add type prefix for scoped enum
fmtEnumUse(const std::string & opPrefix,const std::string & name) const668         virtual std::string fmtEnumUse(const std::string& opPrefix, const std::string& name) const override { return opPrefix + "::" + name; }
669 
headerGuardSuffix() const670         std::string headerGuardSuffix() const override { return "HPP"; }
671     };
672 
673     // LUA printer
674     class TPrinterLua final : public TPrinter {
675     private:
printPrologue(std::ostream & out) const676         void printPrologue(std::ostream& out) const override { out << "spv = {\n"; }
677 
printEpilogue(std::ostream & out) const678         void printEpilogue(std::ostream& out) const override { out << "}\n"; }
679 
commentBOL() const680         std::string commentBOL() const override { return "-- "; }
681 
enumBeg(const std::string & s,enumStyle_t style) const682         std::string enumBeg(const std::string& s, enumStyle_t style) const override {
683             return indent() + s + styleStr(style) + " = {\n";
684         }
685 
enumEnd(const std::string & s,enumStyle_t style,bool isLast) const686         std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override {
687             return indent() + "},\n\n";
688         }
689 
enumFmt(const std::string & s,const valpair_t & v,enumStyle_t style,bool isLast) const690         std::string enumFmt(const std::string& s, const valpair_t& v,
691                             enumStyle_t style, bool isLast) const override {
692             return indent(2) + prependIfDigit(s, v.second) + " = " + fmtStyleVal(v.first, style) + ",\n";
693         }
694 
fmtConstInt(unsigned val,const std::string & name,const char * fmt,bool isLast) const695         virtual std::string fmtConstInt(unsigned val, const std::string& name,
696                                         const char* fmt, bool isLast) const override
697         {
698             return indent() + name + " = " + fmtNum(fmt, val) + (isLast ? ",\n\n" : ",\n");
699         }
700     };
701 
702     // Python printer
703     class TPrinterPython final : public TPrinter {
704     private:
printPrologue(std::ostream & out) const705         void printPrologue(std::ostream& out) const override { out << "spv = {\n"; }
706 
printEpilogue(std::ostream & out) const707         void printEpilogue(std::ostream& out) const override { out << "}\n"; }
708 
commentBOL() const709         std::string commentBOL() const override { return "# "; }
710 
enumBeg(const std::string & s,enumStyle_t style) const711         std::string enumBeg(const std::string& s, enumStyle_t style) const override {
712             return indent() + "'" + s + styleStr(style) + "'" + " : {\n";
713         }
714 
enumEnd(const std::string & s,enumStyle_t style,bool isLast) const715         std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override {
716             return indent() + "},\n\n";
717         }
718 
enumFmt(const std::string & s,const valpair_t & v,enumStyle_t style,bool isLast) const719         std::string enumFmt(const std::string& s, const valpair_t& v,
720                             enumStyle_t style, bool isLast) const override {
721             return indent(2) + "'" + prependIfDigit(s, v.second) + "'" + " : " + fmtStyleVal(v.first, style) + ",\n";
722         }
723 
fmtConstInt(unsigned val,const std::string & name,const char * fmt,bool isLast) const724         std::string fmtConstInt(unsigned val, const std::string& name,
725                                 const char* fmt, bool isLast) const override
726         {
727             return indent() + "'" + name + "'" + " : " + fmtNum(fmt, val) + (isLast ? ",\n\n" : ",\n");
728         }
729     };
730 
731     // C# printer
732     class TPrinterCSharp final : public TPrinter {
733     private:
commentBOL() const734         std::string commentBOL() const override { return "// ";  }
735 
printPrologue(std::ostream & out) const736         void printPrologue(std::ostream& out) const override {
737             out << "namespace Spv\n{\n\n";
738             out << indent() << "public static class Specification\n";
739             out << indent() << "{\n";
740         }
741 
printEpilogue(std::ostream & out) const742         void printEpilogue(std::ostream& out) const override {
743             out << indent() << "}\n";
744             out << "}\n";
745         }
746 
enumBeg(const std::string & s,enumStyle_t style) const747         std::string enumBeg(const std::string& s, enumStyle_t style) const override {
748             return indent(2) + "public enum " + s + styleStr(style) + "\n" + indent(2) + "{\n";
749         }
750 
enumEnd(const std::string & s,enumStyle_t style,bool isLast) const751         std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override {
752             return indent(2) + "}" + + (isLast ? "\n" : "\n\n");
753         }
754 
enumFmt(const std::string & s,const valpair_t & v,enumStyle_t style,bool isLast) const755         std::string enumFmt(const std::string& s, const valpair_t& v,
756                             enumStyle_t style, bool isLast) const override {
757             return indent(3) + prependIfDigit(s, v.second) + " = " + fmtStyleVal(v.first, style) + ",\n";
758         }
759 
fmtConstInt(unsigned val,const std::string & name,const char * fmt,bool isLast) const760         std::string fmtConstInt(unsigned val, const std::string& name,
761                                 const char* fmt, bool isLast) const override {
762             return indent(2) + std::string("public const uint ") + name +
763                 " = " + fmtNum(fmt, val) + (isLast ? ";\n\n" : ";\n");
764         }
765     };
766 
767     // D printer
768     class TPrinterD final : public TPrinter {
769     private:
commentBeg() const770         std::string commentBeg() const override            { return "/+\n"; }
commentBOL() const771         std::string commentBOL() const override            { return " + ";  }
commentEnd(bool isLast) const772         std::string commentEnd(bool isLast) const override { return " +/\n"; }
773 
printPrologue(std::ostream & out) const774         void printPrologue(std::ostream& out) const override {
775             out << "module spv;\n\n";
776         }
777 
printEpilogue(std::ostream & out) const778         void printEpilogue(std::ostream& out) const override {
779         }
780 
enumBeg(const std::string & s,enumStyle_t style) const781         std::string enumBeg(const std::string& s, enumStyle_t style) const override {
782             return "enum " + s + styleStr(style) + " : uint\n{\n";
783         }
784 
enumEnd(const std::string & s,enumStyle_t style,bool isLast) const785         std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override {
786             return std::string("}\n\n");
787         }
788 
enumFmt(const std::string & s,const valpair_t & v,enumStyle_t style,bool isLast) const789         std::string enumFmt(const std::string& s, const valpair_t& v,
790                             enumStyle_t style, bool isLast) const override {
791             return indent() + prependIfDigit("_", v.second) + " = " + fmtStyleVal(v.first, style) + ",\n";
792         }
793 
fmtConstInt(unsigned val,const std::string & name,const char * fmt,bool isLast) const794         std::string fmtConstInt(unsigned val, const std::string& name,
795                                 const char* fmt, bool isLast) const override {
796             return std::string("enum uint ") + name +
797                 " = " + fmtNum(fmt, val) + (isLast ? ";\n\n" : ";\n");
798         }
799     };
800 
801     // Beef printer
802     class TPrinterBeef final : public TPrinter {
803     private:
commentBOL() const804         std::string commentBOL() const override { return "// "; }
805 
printPrologue(std::ostream & out) const806         void printPrologue(std::ostream& out) const override {
807             out << "namespace Spv\n{\n";
808             out << indent() << "using System;\n\n";
809             out << indent() << "public static class Specification\n";
810             out << indent() << "{\n";
811         }
812 
printEpilogue(std::ostream & out) const813         void printEpilogue(std::ostream& out) const override {
814             out << indent() << "}\n";
815             out << "}\n";
816         }
817 
enumBeg(const std::string & s,enumStyle_t style) const818         std::string enumBeg(const std::string& s, enumStyle_t style) const override {
819             return indent(2) + "[AllowDuplicates, CRepr] public enum " + s + styleStr(style) + "\n" + indent(2) + "{\n";
820         }
821 
enumEnd(const std::string & s,enumStyle_t style,bool isLast) const822         std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override {
823             return indent(2) + "}" + +(isLast ? "\n" : "\n\n");
824         }
825 
enumFmt(const std::string & s,const valpair_t & v,enumStyle_t style,bool isLast) const826         std::string enumFmt(const std::string& s, const valpair_t& v,
827             enumStyle_t style, bool isLast) const override {
828             return indent(3) + prependIfDigit(s, v.second) + " = " + fmtStyleVal(v.first, style) + ",\n";
829         }
830 
fmtConstInt(unsigned val,const std::string & name,const char * fmt,bool isLast) const831         std::string fmtConstInt(unsigned val, const std::string& name,
832             const char* fmt, bool isLast) const override {
833             return indent(2) + std::string("public const uint32 ") + name +
834                 " = " + fmtNum(fmt, val) + (isLast ? ";\n\n" : ";\n");
835         }
836     };
837 
838 } // namespace
839 
840 namespace spv {
PrintAllHeaders()841     void PrintAllHeaders()
842     {
843         // TODO: Once MSVC 2012 is no longer a factor, use brace initializers here
844         std::vector<std::pair<TLanguage, std::string>> langInfo;
845 
846         langInfo.push_back(std::make_pair(ELangC,       "spirv.h"));
847         langInfo.push_back(std::make_pair(ELangCPP,     "spirv.hpp"));
848         langInfo.push_back(std::make_pair(ELangCPP11,   "spirv.hpp11"));
849         langInfo.push_back(std::make_pair(ELangJSON,    "spirv.json"));
850         langInfo.push_back(std::make_pair(ELangLua,     "spirv.lua"));
851         langInfo.push_back(std::make_pair(ELangPython,  "spirv.py"));
852         langInfo.push_back(std::make_pair(ELangCSharp,  "spirv.cs"));
853         langInfo.push_back(std::make_pair(ELangD,       "spv.d"));
854         langInfo.push_back(std::make_pair(ELangBeef,    "spirv.bf"));
855 
856         for (const auto& lang : langInfo) {
857             std::ofstream out(lang.second, std::ios::out);
858 
859             if ((out.rdstate() & std::ifstream::failbit)) {
860                 std::cerr << "Unable to open file: " << lang.second << std::endl;
861             } else {
862                 PrintHeader(lang.first, out);
863             }
864         }
865     }
866 
867     // Print header for given language to given output stream
PrintHeader(TLanguage lang,std::ostream & out)868     void PrintHeader(TLanguage lang, std::ostream& out)
869     {
870         typedef std::unique_ptr<TPrinter> TPrinterPtr;
871         TPrinterPtr p;
872 
873         switch (lang) {
874             case ELangC:       p = TPrinterPtr(new TPrinterC);       break;
875             case ELangCPP:     p = TPrinterPtr(new TPrinterCPP);     break;
876             case ELangCPP11:   p = TPrinterPtr(new TPrinterCPP11);   break;
877             case ELangJSON:    p = TPrinterPtr(new TPrinterJSON);    break;
878             case ELangLua:     p = TPrinterPtr(new TPrinterLua);     break;
879             case ELangPython:  p = TPrinterPtr(new TPrinterPython);  break;
880             case ELangCSharp:  p = TPrinterPtr(new TPrinterCSharp);  break;
881             case ELangD:       p = TPrinterPtr(new TPrinterD);       break;
882             case ELangBeef:    p = TPrinterPtr(new TPrinterBeef);    break;
883             case ELangAll:     PrintAllHeaders();                    break;
884             default:
885                 std::cerr << "Unknown language." << std::endl;
886                 return;
887         }
888 
889         // Print the data in the requested format
890         if (p)
891             out << *p << std::endl;
892 
893         // object is auto-deleted
894     }
895 
896 } // namespace spv
897