1 // Copyright (c) 2014-2018 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 
46 #include "jsoncpp/dist/json/json.h"
47 
48 #include "header.h"
49 #include "jsonToSpirv.h"
50 
51 // snprintf and _snprintf are not quite the same, but close enough
52 // for our use.
53 #ifdef _MSC_VER
54 #pragma warning(disable:4996)
55 #define snprintf _snprintf
56 #endif
57 
58 // This file converts SPIR-V definitions to an internal JSON
59 // representation, and then generates language specific
60 // data from that single internal form.
61 
62 // Initially, the internal form is created from C++ data,
63 // though this can be changed to a JSON master in time.
64 
65 namespace {
66     class TPrinter {
67     protected:
68         TPrinter();
69 
70         static const int         DocMagicNumber = 0x07230203;
71         static const int         DocVersion     = 0x00010300;
72         static const int         DocRevision    = 1;
73         #define DocRevisionString                "1"
74         static const std::string DocCopyright;
75         static const std::string DocComment1;
76         static const std::string DocComment2;
77 
78         enum enumStyle_t {
79             enumNoMask,
80             enumCount,
81             enumShift,
82             enumMask,
83             enumHex,
84         };
85 
styleStr(enumStyle_t s)86         static std::string styleStr(enumStyle_t s) {
87             return s == enumShift ? "Shift" :
88                    s == enumMask  ? "Mask"  : "";
89         }
90 
91         friend std::ostream& operator<<(std::ostream&, const TPrinter&);
92 
93         virtual void printAll(std::ostream&)      const;
94         virtual void printComments(std::ostream&) const;
printPrologue(std::ostream &) const95         virtual void printPrologue(std::ostream&) const { }
96         virtual void printDefs(std::ostream&)     const;
printEpilogue(std::ostream &) const97         virtual void printEpilogue(std::ostream&) const { }
98         virtual void printMeta(std::ostream&)     const;
printTypes(std::ostream &) const99         virtual void printTypes(std::ostream&)    const { }
100 
101         virtual std::string escapeComment(const std::string& s) const;
102 
103         // Default printComments() uses these comment strings
commentBeg() const104         virtual std::string commentBeg() const            { return ""; }
commentEnd(bool isLast) const105         virtual std::string commentEnd(bool isLast) const { return ""; }
commentBOL() const106         virtual std::string commentBOL() const            { return ""; }
commentEOL(bool isLast) const107         virtual std::string commentEOL(bool isLast) const { return ""; }
108 
109         typedef std::pair<unsigned, std::string> valpair_t;
110 
111         // for printing enum values
enumBeg(const std::string &,enumStyle_t) const112         virtual std::string enumBeg(const std::string&, enumStyle_t) const { return ""; }
enumEnd(const std::string &,enumStyle_t,bool isLast=false) const113         virtual std::string enumEnd(const std::string&, enumStyle_t, bool isLast = false) const {
114             return "";
115         }
enumFmt(const std::string &,const valpair_t &,enumStyle_t,bool isLast=false) const116         virtual std::string enumFmt(const std::string&, const valpair_t&,
117                                     enumStyle_t, bool isLast = false) const {
118             return "";
119         }
maxEnumFmt(const std::string &,const valpair_t &,enumStyle_t) const120         virtual std::string maxEnumFmt(const std::string&, const valpair_t&,
121                                        enumStyle_t) const {
122             return "";
123         }
124 
fmtConstInt(unsigned val,const std::string & name,const char * fmt,bool isLast=false) const125         virtual std::string fmtConstInt(unsigned val, const std::string& name,
126                                         const char* fmt, bool isLast = false) const {
127             return "";
128         }
129 
130         std::vector<valpair_t> getSortedVals(const Json::Value&) const;
131 
indent(int count=1) const132         virtual std::string indent(int count = 1) const {
133             return std::string(count * 4, ' ');   // default indent level = 4
134         }
135 
fmtNum(const char * fmt,unsigned val)136         static std::string fmtNum(const char* fmt, unsigned val) {
137             char buff[16]; // ample for 8 hex digits + 0x
138             snprintf(buff, sizeof(buff), fmt, val);
139             buff[sizeof(buff)-1] = '\0';  // MSVC doesn't promise null termination
140             return buff;
141         }
142 
143         static std::string fmtStyleVal(unsigned v, enumStyle_t style);
144 
145         // If the enum value name would start with a sigit, prepend the enum name.
146         // E.g, "3D" -> "Dim3D".
prependIfDigit(const std::string & ename,const std::string & vname)147         static std::string prependIfDigit(const std::string& ename, const std::string& vname) {
148             return (std::isdigit(vname[0]) ? ename : std::string("")) + vname;
149         }
150 
151         void addComment(Json::Value& node, const std::string& str);
152 
153         Json::Value spvRoot; // JSON SPIR-V data
154     };
155 
156     // Format value as mask or value
fmtStyleVal(unsigned v,enumStyle_t style)157     std::string TPrinter::fmtStyleVal(unsigned v, enumStyle_t style)
158     {
159         switch (style) {
160         case enumMask:
161             return fmtNum("0x%08x", 1<<v);
162         case enumHex:
163             return fmtNum("0x%08x", v);
164         default:
165             return std::to_string(v);
166         }
167     }
168 
169     const std::string TPrinter::DocCopyright =
170         "Copyright (c) 2014-2018 The Khronos Group Inc.\n"
171         "\n"
172         "Permission is hereby granted, free of charge, to any person obtaining a copy\n"
173         "of this software and/or associated documentation files (the \"Materials\"),\n"
174         "to deal in the Materials without restriction, including without limitation\n"
175         "the rights to use, copy, modify, merge, publish, distribute, sublicense,\n"
176         "and/or sell copies of the Materials, and to permit persons to whom the\n"
177         "Materials are furnished to do so, subject to the following conditions:\n"
178         "\n"
179         "The above copyright notice and this permission notice shall be included in\n"
180         "all copies or substantial portions of the Materials.\n"
181         "\n"
182         "MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS\n"
183         "STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND\n"
184         "HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ \n"
185         "\n"
186         "THE MATERIALS ARE PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\n"
187         "OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n"
188         "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n"
189         "THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n"
190         "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n"
191         "FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS\n"
192         "IN THE MATERIALS.\n";
193 
194     const std::string TPrinter::DocComment1 =
195         "This header is automatically generated by the same tool that creates\n"
196         "the Binary Section of the SPIR-V specification.\n";
197 
198     const std::string TPrinter::DocComment2 =
199         "Enumeration tokens for SPIR-V, in various styles:\n"
200         "  C, C++, C++11, JSON, Lua, Python\n"
201         "\n"
202         "- C will have tokens with a \"Spv\" prefix, e.g.: SpvSourceLanguageGLSL\n"
203         "- C++ will have tokens in the \"spv\" name space, e.g.: spv::SourceLanguageGLSL\n"
204         "- C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL\n"
205         "- Lua will use tables, e.g.: spv.SourceLanguage.GLSL\n"
206         "- Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL']\n"
207         "\n"
208         "Some tokens act like mask values, which can be OR'd together,\n"
209         "while others are mutually exclusive.  The mask-like ones have\n"
210         "\"Mask\" in their name, and a parallel enum that has the shift\n"
211         "amount (1 << x) for each corresponding enumerant.\n";
212 
213     // Construct
TPrinter()214     TPrinter::TPrinter()
215     {
216         Json::Value& meta            = spvRoot["spv"]["meta"];
217         Json::Value& enums           = spvRoot["spv"]["enum"];
218 
219         meta["MagicNumber"]          = DocMagicNumber;
220         meta["Version"]              = DocVersion;
221         meta["Revision"]             = DocRevision;
222         meta["OpCodeMask"]           = 0xffff;
223         meta["WordCountShift"]       = 16;
224 
225         int commentId = 0;
226         addComment(meta["Comment"][commentId++], DocCopyright);
227         addComment(meta["Comment"][commentId++], DocComment1);
228         addComment(meta["Comment"][commentId++], DocComment2);
229 
230         for (int e = spv::OperandSource; e < spv::OperandOpcode; ++e) {
231             auto& enumSet =  spv::OperandClassParams[e];
232             const bool        mask     = enumSet.bitmask;
233             const std::string enumName = enumSet.codeName;
234 
235             for (auto& enumRow : enumSet) {
236                 std::string name = enumRow.name;
237                 enums[e - spv::OperandSource]["Values"][name] = enumRow.value;
238             }
239 
240             enums[e - spv::OperandSource]["Type"] = mask ? "Bit" : "Value";
241             enums[e - spv::OperandSource]["Name"] = enumName;
242         }
243 
244           // Instructions are in their own different table
245         {
246             auto& entry = enums[spv::OperandOpcode - spv::OperandSource];
247             for (auto& enumRow : spv::InstructionDesc) {
248                 std::string name = enumRow.name;
249                 entry["Values"][name] = enumRow.value;
250             }
251             entry["Type"] = "Value";
252             entry["Name"] = "Op";
253         }
254     }
255 
256     // Create comment
addComment(Json::Value & node,const std::string & str)257     void TPrinter::addComment(Json::Value& node, const std::string& str)
258     {
259         std::istringstream cstream(str);
260         std::string        cline;
261 
262         int line = 0;
263         while (std::getline(cstream, cline))  // fmt each line
264             node[line++] = cline;
265     }
266 
267 
268     // Return a list of values sorted by enum value.  The std::vector
269     // returned by value is okay in c++11 due to move semantics.
270     std::vector<TPrinter::valpair_t>
getSortedVals(const Json::Value & p) const271     TPrinter::getSortedVals(const Json::Value& p) const
272     {
273         std::vector<valpair_t> values;
274 
275         for (auto e = p.begin(); e != p.end(); ++e)
276             values.push_back(valpair_t(e->asUInt(), e.name()));
277 
278         // Use a stable sort because we might have aliases, e.g.
279         // SubgropuBallot (might be in future core) vs. SubgroupBallotKHR.
280         std::stable_sort(values.begin(), values.end());
281 
282         return values;
283     }
284 
285     // Escape comment characters if needed
escapeComment(const std::string & s) const286     std::string TPrinter::escapeComment(const std::string& s) const { return s; }
287 
288     // Format comments in language specific way
printComments(std::ostream & out) const289     void TPrinter::printComments(std::ostream& out) const
290     {
291         const int commentCount = spvRoot["spv"]["meta"]["Comment"].size();
292         int commentNum = 0;
293 
294         for (const auto& comment : spvRoot["spv"]["meta"]["Comment"]) {
295             out << commentBeg();
296 
297             for (int line = 0; line < int(comment.size()); ++line)
298                 out << commentBOL() << escapeComment(comment[line].asString()) <<
299                     commentEOL((line+1) == comment.size()) << std::endl;
300 
301             out << commentEnd(++commentNum == commentCount) << std::endl;
302         }
303     }
304 
305     // Format header metadata
printMeta(std::ostream & out) const306     void TPrinter::printMeta(std::ostream& out) const
307     {
308         const Json::Value& meta = spvRoot["spv"]["meta"];
309 
310         const auto print = [&](const char* name, const char* fmt, bool isLast) {
311             out << fmtConstInt(meta[name].asUInt(), name, fmt, isLast);
312         };
313 
314         print("MagicNumber",    "0x%08lx", false);
315         print("Version",        "0x%08lx", false);
316         print("Revision",       "%d",      false);
317         print("OpCodeMask",     "0x%04x",  false);
318         print("WordCountShift", "%d",      true);
319     }
320 
321     // Format value definitions in language specific way
printDefs(std::ostream & out) const322     void TPrinter::printDefs(std::ostream& out) const
323     {
324         const Json::Value& enums = spvRoot["spv"]["enum"];
325 
326         for (auto opClass = enums.begin(); opClass != enums.end(); ++opClass) {
327             const bool isMask   = (*opClass)["Type"].asString() == "Bit";
328             const auto opName   = (*opClass)["Name"].asString();
329             const auto opPrefix = opName == "Op" ? "" : opName;
330 
331             for (enumStyle_t style = (isMask ? enumShift : enumCount);
332                  style <= (isMask ? enumMask : enumCount); style = enumStyle_t(int(style)+1)) {
333 
334                 out << enumBeg(opName, style);
335 
336                 if (style == enumMask)
337                     out << enumFmt(opPrefix, valpair_t(0, "MaskNone"), enumNoMask);
338 
339                 const auto sorted = getSortedVals((*opClass)["Values"]);
340 
341                 std::string maxEnum = maxEnumFmt(opName, valpair_t(0x7FFFFFFF, "Max"), enumHex);
342 
343                 bool printMax = (style != enumMask && maxEnum.size() > 0);
344 
345                 for (const auto& v : sorted)
346                     out << enumFmt(opPrefix, v, style, !printMax && v.first == sorted.back().first);
347 
348                 if (printMax)
349                     out << maxEnum;
350 
351                 auto nextOpClass = opClass;
352                 out << enumEnd(opName, style, ++nextOpClass == enums.end());
353             }
354         }
355     }
356 
printAll(std::ostream & out) const357     void TPrinter::printAll(std::ostream& out) const
358     {
359         printComments(out);
360         printPrologue(out);
361         printTypes(out);
362         printMeta(out);
363         printDefs(out);
364         printEpilogue(out);
365     }
366 
367     // Stream entire header to output
operator <<(std::ostream & out,const TPrinter & p)368     std::ostream& operator<<(std::ostream& out, const TPrinter &p)
369     {
370         p.printAll(out);
371         return out;
372     }
373 
374     // JSON printer.  Rather than use the default printer, we supply our own so
375     // we can control the printing order within various containers.
376     class TPrinterJSON final : public TPrinter {
377     private:
printPrologue(std::ostream & out) const378         void printPrologue(std::ostream& out) const override { out << "{\n" + indent() + "\"spv\":\n" + indent() + "{\n"; }
printEpilogue(std::ostream & out) const379         void printEpilogue(std::ostream& out) const override { out << indent() + "}\n}\n"; }
380 
escapeComment(const std::string & s) const381         std::string escapeComment(const std::string& s) const override {
382             std::string newStr;
383             for (auto c : s) {
384                 if (c == '"') {
385                     newStr += '\\';
386                     newStr += c;
387                 } else {
388                     newStr += c;
389                 }
390             }
391             return newStr;
392         }
393 
fmtConstInt(unsigned val,const std::string & name,const char * fmt,bool isLast) const394         std::string fmtConstInt(unsigned val, const std::string& name,
395                                 const char* fmt, bool isLast) const override {
396             return indent(3) + '"' + name + "\": " + fmtNum("%d", val) + (isLast ? "\n" : ",\n");
397         }
398 
printMeta(std::ostream & out) const399         void printMeta(std::ostream& out) const override
400         {
401             out << indent(2) + "\"meta\":\n" + indent(2) + "{\n";
402             printComments(out);
403             TPrinter::printMeta(out);
404             out << indent(2) + "},\n";
405         }
406 
commentBeg() const407         std::string commentBeg() const override            { return indent(4) + "[\n"; }
commentEnd(bool isLast) const408         std::string commentEnd(bool isLast) const override { return indent(4) + (isLast ? "]" : "],"); }
commentBOL() const409         std::string commentBOL() const override            { return indent(5) + '"'; }
commentEOL(bool isLast) const410         std::string commentEOL(bool isLast) const override { return (isLast ? "\"" : "\","); }
411 
printComments(std::ostream & out) const412         void printComments(std::ostream& out) const override
413         {
414             out << indent(3) + "\"Comment\":\n" + indent(3) + "[\n";
415             TPrinter::printComments(out);
416             out << indent(3) + "],\n";
417         }
418 
printDefs(std::ostream & out) const419         void printDefs(std::ostream& out) const override
420         {
421             out << indent(2) + "\"enum\":\n" + indent(2) + "[\n";
422             TPrinter::printDefs(out);
423             out << indent(2) + "]\n";
424         }
425 
printAll(std::ostream & out) const426         void printAll(std::ostream& out) const override
427         {
428             printPrologue(out);
429             printMeta(out);
430             printDefs(out);
431             printEpilogue(out);
432         }
433 
enumBeg(const std::string & s,enumStyle_t style) const434         std::string enumBeg(const std::string& s, enumStyle_t style) const override {
435             if (style == enumMask)
436                 return "";
437             return indent(3) + "{\n" +
438                 indent(4) + "\"Name\": \"" + s + "\",\n" +
439                 indent(4) + "\"Type\": " + (style == enumShift ? "\"Bit\"" : "\"Value\"") + ",\n" +
440                 indent(4) + "\"Values\":\n" +
441                 indent(4) + "{\n";
442         }
443 
enumEnd(const std::string & s,enumStyle_t style,bool isLast) const444         std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override {
445             if (style == enumMask)
446                 return "";
447             return indent(4) + "}\n" +
448                    indent(3) + "}" + (isLast ? "" : ",") + "\n";
449         }
450 
enumFmt(const std::string & s,const valpair_t & v,enumStyle_t style,bool isLast) const451         std::string enumFmt(const std::string& s, const valpair_t& v,
452                             enumStyle_t style, bool isLast) const override {
453             if (style == enumMask || style == enumNoMask)
454                 return "";
455             return indent(5) + '"' + prependIfDigit(s, v.second) + "\": " + fmtNum("%d", v.first) +
456                 (isLast ? "\n" : ",\n");
457         }
458     };
459 
460     // base for C and C++
461     class TPrinterCBase : public TPrinter {
462     protected:
printPrologue(std::ostream & out) const463         virtual void printPrologue(std::ostream& out) const override {
464             out << "#ifndef spirv_" << headerGuardSuffix() << std::endl
465                 << "#define spirv_" << headerGuardSuffix() << std::endl
466                 << std::endl;
467         }
468 
printMeta(std::ostream & out) const469         void printMeta(std::ostream& out) const override {
470             out << "#define SPV_VERSION 0x" << std::hex << DocVersion << std::dec << "\n";
471             out << "#define SPV_REVISION " << DocRevision << "\n";
472             out << "\n";
473 
474             return TPrinter::printMeta(out);
475         }
476 
printEpilogue(std::ostream & out) const477         virtual void printEpilogue(std::ostream& out) const override {
478             out << "#endif  // #ifndef spirv_" << headerGuardSuffix() << std::endl;
479         }
480 
printTypes(std::ostream & out) const481         virtual void printTypes(std::ostream& out) const override {
482             out << "typedef unsigned int " << pre() << "Id;\n\n";
483         }
484 
fmtConstInt(unsigned val,const std::string & name,const char * fmt,bool isLast) const485         virtual std::string fmtConstInt(unsigned val, const std::string& name,
486                                         const char* fmt, bool isLast) const override
487         {
488             return std::string("static const unsigned int ") + pre() + name +
489                 " = " + fmtNum(fmt, val) + (isLast ? ";\n\n" : ";\n");
490         }
491 
pre() const492         virtual std::string pre() const { return ""; } // C name prefix
493         virtual std::string headerGuardSuffix() const = 0;
494     };
495 
496     // C printer
497     class TPrinterC final : public TPrinterCBase {
498     private:
commentBeg() const499         std::string commentBeg() const override            { return "/*\n"; }
commentEnd(bool isLast) const500         std::string commentEnd(bool isLast) const override { return "*/\n"; }
commentBOL() const501         std::string commentBOL() const override            { return "** ";  }
502 
enumBeg(const std::string & s,enumStyle_t style) const503         std::string enumBeg(const std::string& s, enumStyle_t style) const override {
504             return std::string("typedef enum ") + pre() + s + styleStr(style) + "_ {\n";
505         }
506 
enumEnd(const std::string & s,enumStyle_t style,bool isLast) const507         std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override {
508             return "} " + pre() + s + styleStr(style) + ";\n\n";
509         }
510 
enumFmt(const std::string & s,const valpair_t & v,enumStyle_t style,bool isLast) const511         std::string enumFmt(const std::string& s, const valpair_t& v,
512                             enumStyle_t style, bool isLast) const override {
513             return indent() + pre() + s + v.second + styleStr(style) + " = " + fmtStyleVal(v.first, style) + ",\n";
514         }
515 
maxEnumFmt(const std::string & s,const valpair_t & v,enumStyle_t style) const516         std::string maxEnumFmt(const std::string& s, const valpair_t& v,
517                                enumStyle_t style) const override {
518             return enumFmt(s, v, style, true);
519         }
520 
pre() const521         std::string pre() const override { return "Spv"; } // C name prefix
headerGuardSuffix() const522         std::string headerGuardSuffix() const override { return "H"; }
523     };
524 
525     // C++ printer
526     class TPrinterCPP : public TPrinterCBase {
527     private:
printPrologue(std::ostream & out) const528         void printPrologue(std::ostream& out) const override {
529             TPrinterCBase::printPrologue(out);
530             out << "namespace spv {\n\n";
531         }
532 
printEpilogue(std::ostream & out) const533         void printEpilogue(std::ostream& out) const override {
534             const Json::Value& enums = spvRoot["spv"]["enum"];
535 
536             // Create overloaded operator| for mask types
537             out << "// Overload operator| for mask bit combining\n\n";
538 
539             for (auto opClass = enums.begin(); opClass != enums.end(); ++opClass) {
540                 const bool isMask   = (*opClass)["Type"].asString() == "Bit";
541                 const auto opName   = (*opClass)["Name"].asString();
542 
543                 if (isMask) {
544                     const auto typeName = opName + styleStr(enumMask);
545 
546                     out << "inline " + typeName + " operator|(" + typeName + " a, " + typeName + " b) { return " +
547                         typeName + "(unsigned(a) | unsigned(b)); }\n";
548                 }
549             }
550 
551             out << "\n}  // end namespace spv\n\n";
552             TPrinterCBase::printEpilogue(out);
553         }
554 
commentBOL() const555         std::string commentBOL() const override { return "// "; }
556 
557 
enumBeg(const std::string & s,enumStyle_t style) const558         virtual std::string enumBeg(const std::string& s, enumStyle_t style) const override {
559             return std::string("enum ") + s + styleStr(style) + " {\n";
560         }
561 
enumEnd(const std::string & s,enumStyle_t style,bool isLast) const562         std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override {
563             return "};\n\n";
564         }
565 
enumFmt(const std::string & s,const valpair_t & v,enumStyle_t style,bool isLast) const566         virtual std::string enumFmt(const std::string& s, const valpair_t& v,
567                                     enumStyle_t style, bool isLast) const override {
568             return indent() + s + v.second + styleStr(style) + " = " + fmtStyleVal(v.first, style) + ",\n";
569         }
570 
maxEnumFmt(const std::string & s,const valpair_t & v,enumStyle_t style) const571         virtual std::string maxEnumFmt(const std::string& s, const valpair_t& v,
572                                        enumStyle_t style) const override {
573             return enumFmt(s, v, style, true);
574         }
575 
576         // The C++ and C++11 headers define types with the same name. So they
577         // should use the same header guard.
headerGuardSuffix() const578         std::string headerGuardSuffix() const override { return "HPP"; }
579 
580         std::string operators;
581     };
582 
583     // C++11 printer (uses enum classes)
584     class TPrinterCPP11 final : public TPrinterCPP {
585     private:
enumBeg(const std::string & s,enumStyle_t style) const586         std::string enumBeg(const std::string& s, enumStyle_t style) const override {
587             return std::string("enum class ") + s + styleStr(style) + " : unsigned {\n";
588         }
589 
enumFmt(const std::string & s,const valpair_t & v,enumStyle_t style,bool isLast) const590         std::string enumFmt(const std::string& s, const valpair_t& v,
591                             enumStyle_t style, bool isLast) const override {
592             return indent() + prependIfDigit(s, v.second) + " = " + fmtStyleVal(v.first, style) + ",\n";
593         }
594 
maxEnumFmt(const std::string & s,const valpair_t & v,enumStyle_t style) const595         std::string maxEnumFmt(const std::string& s, const valpair_t& v,
596                                enumStyle_t style) const override {
597             return enumFmt(s, v, style, true);
598         }
599 
headerGuardSuffix() const600         std::string headerGuardSuffix() const override { return "HPP"; }
601     };
602 
603     // LUA printer
604     class TPrinterLua final : public TPrinter {
605     private:
printPrologue(std::ostream & out) const606         void printPrologue(std::ostream& out) const override { out << "spv = {\n"; }
607 
printEpilogue(std::ostream & out) const608         void printEpilogue(std::ostream& out) const override { out << "}\n"; }
609 
commentBOL() const610         std::string commentBOL() const override { return "-- "; }
611 
enumBeg(const std::string & s,enumStyle_t style) const612         std::string enumBeg(const std::string& s, enumStyle_t style) const override {
613             return indent() + s + styleStr(style) + " = {\n";
614         }
615 
enumEnd(const std::string & s,enumStyle_t style,bool isLast) const616         std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override {
617             return indent() + "},\n\n";
618         }
619 
enumFmt(const std::string & s,const valpair_t & v,enumStyle_t style,bool isLast) const620         std::string enumFmt(const std::string& s, const valpair_t& v,
621                             enumStyle_t style, bool isLast) const override {
622             return indent(2) + prependIfDigit(s, v.second) + " = " + fmtStyleVal(v.first, style) + ",\n";
623         }
624 
fmtConstInt(unsigned val,const std::string & name,const char * fmt,bool isLast) const625         virtual std::string fmtConstInt(unsigned val, const std::string& name,
626                                         const char* fmt, bool isLast) const override
627         {
628             return indent() + name + " = " + fmtNum(fmt, val) + (isLast ? ",\n\n" : ",\n");
629         }
630     };
631 
632     // Python printer
633     class TPrinterPython final : public TPrinter {
634     private:
printPrologue(std::ostream & out) const635         void printPrologue(std::ostream& out) const override { out << "spv = {\n"; }
636 
printEpilogue(std::ostream & out) const637         void printEpilogue(std::ostream& out) const override { out << "}\n"; }
638 
commentBOL() const639         std::string commentBOL() const override { return "# "; }
640 
enumBeg(const std::string & s,enumStyle_t style) const641         std::string enumBeg(const std::string& s, enumStyle_t style) const override {
642             return indent() + "'" + s + styleStr(style) + "'" + " : {\n";
643         }
644 
enumEnd(const std::string & s,enumStyle_t style,bool isLast) const645         std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override {
646             return indent() + "},\n\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(2) + "'" + prependIfDigit(s, v.second) + "'" + " : " + fmtStyleVal(v.first, style) + ",\n";
652         }
653 
fmtConstInt(unsigned val,const std::string & name,const char * fmt,bool isLast) const654         std::string fmtConstInt(unsigned val, const std::string& name,
655                                 const char* fmt, bool isLast) const override
656         {
657             return indent() + "'" + name + "'" + " : " + fmtNum(fmt, val) + (isLast ? ",\n\n" : ",\n");
658         }
659     };
660 
661 } // namespace
662 
663 namespace spv {
PrintAllHeaders()664     void PrintAllHeaders()
665     {
666         // TODO: Once MSVC 2012 is no longer a factor, use brace initializers here
667         std::vector<std::pair<TLanguage, std::string>> langInfo;
668 
669         langInfo.push_back(std::make_pair(ELangC,       "spirv.h"));
670         langInfo.push_back(std::make_pair(ELangCPP,     "spirv.hpp"));
671         langInfo.push_back(std::make_pair(ELangCPP11,   "spirv.hpp11"));
672         langInfo.push_back(std::make_pair(ELangJSON,    "spirv.json"));
673         langInfo.push_back(std::make_pair(ELangLua,     "spirv.lua"));
674         langInfo.push_back(std::make_pair(ELangPython,  "spirv.py"));
675 
676         for (const auto& lang : langInfo) {
677             std::ofstream out(lang.second, std::ios::out);
678 
679             if ((out.rdstate() & std::ifstream::failbit)) {
680                 std::cerr << "Unable to open file: " << lang.second << std::endl;
681             } else {
682                 PrintHeader(lang.first, out);
683             }
684         }
685     }
686 
687     // Print header for given language to given output stream
PrintHeader(TLanguage lang,std::ostream & out)688     void PrintHeader(TLanguage lang, std::ostream& out)
689     {
690         typedef std::unique_ptr<TPrinter> TPrinterPtr;
691         TPrinterPtr p;
692 
693         switch (lang) {
694             case ELangC:       p = TPrinterPtr(new TPrinterC);       break;
695             case ELangCPP:     p = TPrinterPtr(new TPrinterCPP);     break;
696             case ELangCPP11:   p = TPrinterPtr(new TPrinterCPP11);   break;
697             case ELangJSON:    p = TPrinterPtr(new TPrinterJSON);    break;
698             case ELangLua:     p = TPrinterPtr(new TPrinterLua);     break;
699             case ELangPython:  p = TPrinterPtr(new TPrinterPython);  break;
700             case ELangAll:     PrintAllHeaders();                    break;
701             default:
702                 std::cerr << "Unknown language." << std::endl;
703                 return;
704         }
705 
706         // Print the data in the requested format
707         if (p)
708             out << *p << std::endl;
709 
710         // object is auto-deleted
711     }
712 
713 } // namespace spv
714