1 // Copyright (c) 2014-2020 The Khronos Group Inc.
2 //
3 // Permission is hereby granted, free of charge, to any person obtaining a copy
4 // of this software and/or associated documentation files (the "Materials"),
5 // to deal in the Materials without restriction, including without limitation
6 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
7 // and/or sell copies of the Materials, and to permit persons to whom the
8 // Materials are furnished to do so, subject to the following conditions:
9 //
10 // The above copyright notice and this permission notice shall be included in
11 // all copies or substantial portions of the Materials.
12 //
13 // MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS
14 // STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND
15 // HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/
16 //
17 // THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 // FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS
23 // IN THE MATERIALS.
24 
25 //
26 // Print headers for SPIR-V in several languages.
27 //
28 // To change the header information, change the C++-built database in doc.*.
29 //
30 // Then, use "spriv -h <language>" - e.g, spriv.{h,hpp,lua,py,etc}:
31 // replace the auto-generated header, or "spirv -H" to generate all
32 // supported language headers to predefined names in the current directory.
33 //
34 
35 #include <string>
36 #include <sstream>
37 #include <fstream>
38 #include <cstring>
39 #include <cstdio>
40 #include <algorithm>
41 #include <memory>
42 #include <cctype>
43 #include <vector>
44 #include <utility>
45 #include <set>
46 
47 #include "jsoncpp/dist/json/json.h"
48 
49 #include "header.h"
50 #include "jsonToSpirv.h"
51 
52 // snprintf and _snprintf are not quite the same, but close enough
53 // for our use.
54 #ifdef _MSC_VER
55 #pragma warning(disable:4996)
56 #define snprintf _snprintf
57 #endif
58 
59 // This file converts SPIR-V definitions to an internal JSON
60 // representation, and then generates language specific
61 // data from that single internal form.
62 
63 // Initially, the internal form is created from C++ data,
64 // though this can be changed to a JSON master in time.
65 
66 namespace {
67     class TPrinter {
68     protected:
69         TPrinter();
70 
71         static const int         DocMagicNumber = 0x07230203;
72         static const int         DocVersion     = 0x00010500;
73         static const int         DocRevision    = 4;
74         #define DocRevisionString                "4"
75         static const std::string DocCopyright;
76         static const std::string DocComment1;
77         static const std::string DocComment2;
78 
79         enum enumStyle_t {
80             enumNoMask,
81             enumCount,
82             enumShift,
83             enumMask,
84             enumHex,
85         };
86 
styleStr(enumStyle_t s)87         static std::string styleStr(enumStyle_t s) {
88             return s == enumShift ? "Shift" :
89                    s == enumMask  ? "Mask"  : "";
90         }
91 
92         friend std::ostream& operator<<(std::ostream&, const TPrinter&);
93 
94         virtual void printAll(std::ostream&)      const;
95         virtual void printComments(std::ostream&) const;
printPrologue(std::ostream &) const96         virtual void printPrologue(std::ostream&) const { }
97         virtual void printDefs(std::ostream&)     const;
printEpilogue(std::ostream &) const98         virtual void printEpilogue(std::ostream&) const { }
99         virtual void printMeta(std::ostream&)     const;
printTypes(std::ostream &) const100         virtual void printTypes(std::ostream&)    const { }
printHasResultType(std::ostream &) const101         virtual void printHasResultType(std::ostream&)     const { };
102 
103         virtual std::string escapeComment(const std::string& s) const;
104 
105         // Default printComments() uses these comment strings
commentBeg() const106         virtual std::string commentBeg() const            { return ""; }
commentEnd(bool isLast) const107         virtual std::string commentEnd(bool isLast) const { return ""; }
commentBOL() const108         virtual std::string commentBOL() const            { return ""; }
commentEOL(bool isLast) const109         virtual std::string commentEOL(bool isLast) const { return ""; }
110 
111         typedef std::pair<unsigned, std::string> valpair_t;
112 
113         // for printing enum values
enumBeg(const std::string &,enumStyle_t) const114         virtual std::string enumBeg(const std::string&, enumStyle_t) const { return ""; }
enumEnd(const std::string &,enumStyle_t,bool isLast=false) const115         virtual std::string enumEnd(const std::string&, enumStyle_t, bool isLast = false) const {
116             return "";
117         }
enumFmt(const std::string &,const valpair_t &,enumStyle_t,bool isLast=false) const118         virtual std::string enumFmt(const std::string&, const valpair_t&,
119                                     enumStyle_t, bool isLast = false) const {
120             return "";
121         }
maxEnumFmt(const std::string &,const valpair_t &,enumStyle_t) const122         virtual std::string maxEnumFmt(const std::string&, const valpair_t&,
123                                        enumStyle_t) const {
124             return "";
125         }
126 
fmtConstInt(unsigned val,const std::string & name,const char * fmt,bool isLast=false) const127         virtual std::string fmtConstInt(unsigned val, const std::string& name,
128                                         const char* fmt, bool isLast = false) const {
129             return "";
130         }
131 
132         std::vector<valpair_t> getSortedVals(const Json::Value&) const;
133 
indent(int count=1) const134         virtual std::string indent(int count = 1) const {
135             return std::string(count * 4, ' ');   // default indent level = 4
136         }
137 
fmtNum(const char * fmt,unsigned val)138         static std::string fmtNum(const char* fmt, unsigned val) {
139             char buff[16]; // ample for 8 hex digits + 0x
140             snprintf(buff, sizeof(buff), fmt, val);
141             buff[sizeof(buff)-1] = '\0';  // MSVC doesn't promise null termination
142             return buff;
143         }
144 
145         static std::string fmtStyleVal(unsigned v, enumStyle_t style);
146 
147         // If the enum value name would start with a sigit, prepend the enum name.
148         // E.g, "3D" -> "Dim3D".
prependIfDigit(const std::string & ename,const std::string & vname)149         static std::string prependIfDigit(const std::string& ename, const std::string& vname) {
150             return (std::isdigit(vname[0]) ? ename : std::string("")) + vname;
151         }
152 
153         void addComment(Json::Value& node, const std::string& str);
154 
155         Json::Value spvRoot; // JSON SPIR-V data
156     };
157 
158     // Format value as mask or value
fmtStyleVal(unsigned v,enumStyle_t style)159     std::string TPrinter::fmtStyleVal(unsigned v, enumStyle_t style)
160     {
161         switch (style) {
162         case enumMask:
163             return fmtNum("0x%08x", 1<<v);
164         case enumHex:
165             return fmtNum("0x%08x", v);
166         default:
167             return std::to_string(v);
168         }
169     }
170 
171     const std::string TPrinter::DocCopyright =
172         "Copyright (c) 2014-2020 The Khronos Group Inc.\n"
173         "\n"
174         "Permission is hereby granted, free of charge, to any person obtaining a copy\n"
175         "of this software and/or associated documentation files (the \"Materials\"),\n"
176         "to deal in the Materials without restriction, including without limitation\n"
177         "the rights to use, copy, modify, merge, publish, distribute, sublicense,\n"
178         "and/or sell copies of the Materials, and to permit persons to whom the\n"
179         "Materials are furnished to do so, subject to the following conditions:\n"
180         "\n"
181         "The above copyright notice and this permission notice shall be included in\n"
182         "all copies or substantial portions of the Materials.\n"
183         "\n"
184         "MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS\n"
185         "STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND\n"
186         "HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ \n"
187         "\n"
188         "THE MATERIALS ARE PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\n"
189         "OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n"
190         "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n"
191         "THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n"
192         "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n"
193         "FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS\n"
194         "IN THE MATERIALS.\n";
195 
196     const std::string TPrinter::DocComment1 =
197         "This header is automatically generated by the same tool that creates\n"
198         "the Binary Section of the SPIR-V specification.\n";
199 
200     const std::string TPrinter::DocComment2 =
201         "Enumeration tokens for SPIR-V, in various styles:\n"
202         "  C, C++, C++11, JSON, Lua, Python, C#, D\n"
203         "\n"
204         "- C will have tokens with a \"Spv\" prefix, e.g.: SpvSourceLanguageGLSL\n"
205         "- C++ will have tokens in the \"spv\" name space, e.g.: spv::SourceLanguageGLSL\n"
206         "- C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL\n"
207         "- Lua will use tables, e.g.: spv.SourceLanguage.GLSL\n"
208         "- Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL']\n"
209         "- C# will use enum classes in the Specification class located in the \"Spv\" namespace,\n"
210         "    e.g.: Spv.Specification.SourceLanguage.GLSL\n"
211         "- D will have tokens under the \"spv\" module, e.g: spv.SourceLanguage.GLSL\n"
212         "\n"
213         "Some tokens act like mask values, which can be OR'd together,\n"
214         "while others are mutually exclusive.  The mask-like ones have\n"
215         "\"Mask\" in their name, and a parallel enum that has the shift\n"
216         "amount (1 << x) for each corresponding enumerant.\n";
217 
218     // Construct
TPrinter()219     TPrinter::TPrinter()
220     {
221         Json::Value& meta            = spvRoot["spv"]["meta"];
222         Json::Value& enums           = spvRoot["spv"]["enum"];
223 
224         meta["MagicNumber"]          = DocMagicNumber;
225         meta["Version"]              = DocVersion;
226         meta["Revision"]             = DocRevision;
227         meta["OpCodeMask"]           = 0xffff;
228         meta["WordCountShift"]       = 16;
229 
230         int commentId = 0;
231         addComment(meta["Comment"][commentId++], DocCopyright);
232         addComment(meta["Comment"][commentId++], DocComment1);
233         addComment(meta["Comment"][commentId++], DocComment2);
234 
235         for (int e = spv::OperandSource; e < spv::OperandOpcode; ++e) {
236             auto& enumSet =  spv::OperandClassParams[e];
237             const bool        mask     = enumSet.bitmask;
238             const std::string enumName = enumSet.codeName;
239 
240             for (auto& enumRow : enumSet) {
241                 std::string name = enumRow.name;
242                 enums[e - spv::OperandSource]["Values"][name] = enumRow.value;
243             }
244 
245             enums[e - spv::OperandSource]["Type"] = mask ? "Bit" : "Value";
246             enums[e - spv::OperandSource]["Name"] = enumName;
247         }
248 
249           // Instructions are in their own different table
250         {
251             auto& entry = enums[spv::OperandOpcode - spv::OperandSource];
252             for (auto& enumRow : spv::InstructionDesc) {
253                 std::string name = enumRow.name;
254                 entry["Values"][name] = enumRow.value;
255             }
256             entry["Type"] = "Value";
257             entry["Name"] = "Op";
258         }
259     }
260 
261     // Create comment
addComment(Json::Value & node,const std::string & str)262     void TPrinter::addComment(Json::Value& node, const std::string& str)
263     {
264         std::istringstream cstream(str);
265         std::string        cline;
266 
267         int line = 0;
268         while (std::getline(cstream, cline))  // fmt each line
269             node[line++] = cline;
270     }
271 
272 
273     // Return a list of values sorted by enum value.  The std::vector
274     // returned by value is okay in c++11 due to move semantics.
275     std::vector<TPrinter::valpair_t>
getSortedVals(const Json::Value & p) const276     TPrinter::getSortedVals(const Json::Value& p) const
277     {
278         std::vector<valpair_t> values;
279 
280         for (auto e = p.begin(); e != p.end(); ++e)
281             values.push_back(valpair_t(e->asUInt(), e.name()));
282 
283         // Use a stable sort because we might have aliases, e.g.
284         // SubgropuBallot (might be in future core) vs. SubgroupBallotKHR.
285         std::stable_sort(values.begin(), values.end());
286 
287         return values;
288     }
289 
290     // Escape comment characters if needed
escapeComment(const std::string & s) const291     std::string TPrinter::escapeComment(const std::string& s) const { return s; }
292 
293     // Format comments in language specific way
printComments(std::ostream & out) const294     void TPrinter::printComments(std::ostream& out) const
295     {
296         const int commentCount = spvRoot["spv"]["meta"]["Comment"].size();
297         int commentNum = 0;
298 
299         for (const auto& comment : spvRoot["spv"]["meta"]["Comment"]) {
300             out << commentBeg();
301 
302             for (int line = 0; line < int(comment.size()); ++line)
303                 out << commentBOL() << escapeComment(comment[line].asString()) <<
304                     commentEOL((line+1) == comment.size()) << std::endl;
305 
306             out << commentEnd(++commentNum == commentCount) << std::endl;
307         }
308     }
309 
310     // Format header metadata
printMeta(std::ostream & out) const311     void TPrinter::printMeta(std::ostream& out) const
312     {
313         const Json::Value& meta = spvRoot["spv"]["meta"];
314 
315         const auto print = [&](const char* name, const char* fmt, bool isLast) {
316             out << fmtConstInt(meta[name].asUInt(), name, fmt, isLast);
317         };
318 
319         print("MagicNumber",    "0x%08lx", false);
320         print("Version",        "0x%08lx", false);
321         print("Revision",       "%d",      false);
322         print("OpCodeMask",     "0x%04x",  false);
323         print("WordCountShift", "%d",      true);
324     }
325 
326     // Format value definitions in language specific way
printDefs(std::ostream & out) const327     void TPrinter::printDefs(std::ostream& out) const
328     {
329         const Json::Value& enums = spvRoot["spv"]["enum"];
330 
331         for (auto opClass = enums.begin(); opClass != enums.end(); ++opClass) {
332             const bool isMask   = (*opClass)["Type"].asString() == "Bit";
333             const auto opName   = (*opClass)["Name"].asString();
334             const auto opPrefix = opName == "Op" ? "" : opName;
335 
336             for (enumStyle_t style = (isMask ? enumShift : enumCount);
337                  style <= (isMask ? enumMask : enumCount); style = enumStyle_t(int(style)+1)) {
338 
339                 out << enumBeg(opName, style);
340 
341                 if (style == enumMask)
342                     out << enumFmt(opPrefix, valpair_t(0, "MaskNone"), enumNoMask);
343 
344                 const auto sorted = getSortedVals((*opClass)["Values"]);
345 
346                 std::string maxEnum = maxEnumFmt(opName, valpair_t(0x7FFFFFFF, "Max"), enumHex);
347 
348                 bool printMax = (style != enumMask && maxEnum.size() > 0);
349 
350                 for (const auto& v : sorted)
351                     out << enumFmt(opPrefix, v, style, !printMax && v.second == sorted.back().second);
352 
353                 if (printMax)
354                     out << maxEnum;
355 
356                 auto nextOpClass = opClass;
357                 out << enumEnd(opName, style, ++nextOpClass == enums.end());
358             }
359         }
360     }
361 
printAll(std::ostream & out) const362     void TPrinter::printAll(std::ostream& out) const
363     {
364         printComments(out);
365         printPrologue(out);
366         printTypes(out);
367         printMeta(out);
368         printDefs(out);
369         printHasResultType(out);
370         printEpilogue(out);
371     }
372 
373     // Stream entire header to output
operator <<(std::ostream & out,const TPrinter & p)374     std::ostream& operator<<(std::ostream& out, const TPrinter &p)
375     {
376         p.printAll(out);
377         return out;
378     }
379 
380     // JSON printer.  Rather than use the default printer, we supply our own so
381     // we can control the printing order within various containers.
382     class TPrinterJSON final : public TPrinter {
383     private:
printPrologue(std::ostream & out) const384         void printPrologue(std::ostream& out) const override { out << "{\n" + indent() + "\"spv\":\n" + indent() + "{\n"; }
printEpilogue(std::ostream & out) const385         void printEpilogue(std::ostream& out) const override { out << indent() + "}\n}\n"; }
386 
escapeComment(const std::string & s) const387         std::string escapeComment(const std::string& s) const override {
388             std::string newStr;
389             for (auto c : s) {
390                 if (c == '"') {
391                     newStr += '\\';
392                     newStr += c;
393                 } else {
394                     newStr += c;
395                 }
396             }
397             return newStr;
398         }
399 
fmtConstInt(unsigned val,const std::string & name,const char * fmt,bool isLast) const400         std::string fmtConstInt(unsigned val, const std::string& name,
401                                 const char* fmt, bool isLast) const override {
402             return indent(3) + '"' + name + "\": " + fmtNum("%d", val) + (isLast ? "\n" : ",\n");
403         }
404 
printMeta(std::ostream & out) const405         void printMeta(std::ostream& out) const override
406         {
407             out << indent(2) + "\"meta\":\n" + indent(2) + "{\n";
408             printComments(out);
409             TPrinter::printMeta(out);
410             out << indent(2) + "},\n";
411         }
412 
commentBeg() const413         std::string commentBeg() const override            { return indent(4) + "[\n"; }
commentEnd(bool isLast) const414         std::string commentEnd(bool isLast) const override { return indent(4) + (isLast ? "]" : "],"); }
commentBOL() const415         std::string commentBOL() const override            { return indent(5) + '"'; }
commentEOL(bool isLast) const416         std::string commentEOL(bool isLast) const override { return (isLast ? "\"" : "\","); }
417 
printComments(std::ostream & out) const418         void printComments(std::ostream& out) const override
419         {
420             out << indent(3) + "\"Comment\":\n" + indent(3) + "[\n";
421             TPrinter::printComments(out);
422             out << indent(3) + "],\n";
423         }
424 
printDefs(std::ostream & out) const425         void printDefs(std::ostream& out) const override
426         {
427             out << indent(2) + "\"enum\":\n" + indent(2) + "[\n";
428             TPrinter::printDefs(out);
429             out << indent(2) + "]\n";
430         }
431 
printAll(std::ostream & out) const432         void printAll(std::ostream& out) const override
433         {
434             printPrologue(out);
435             printMeta(out);
436             printDefs(out);
437             printEpilogue(out);
438         }
439 
enumBeg(const std::string & s,enumStyle_t style) const440         std::string enumBeg(const std::string& s, enumStyle_t style) const override {
441             if (style == enumMask)
442                 return "";
443             return indent(3) + "{\n" +
444                 indent(4) + "\"Name\": \"" + s + "\",\n" +
445                 indent(4) + "\"Type\": " + (style == enumShift ? "\"Bit\"" : "\"Value\"") + ",\n" +
446                 indent(4) + "\"Values\":\n" +
447                 indent(4) + "{\n";
448         }
449 
enumEnd(const std::string & s,enumStyle_t style,bool isLast) const450         std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override {
451             if (style == enumMask)
452                 return "";
453             return indent(4) + "}\n" +
454                    indent(3) + "}" + (isLast ? "" : ",") + "\n";
455         }
456 
enumFmt(const std::string & s,const valpair_t & v,enumStyle_t style,bool isLast) const457         std::string enumFmt(const std::string& s, const valpair_t& v,
458                             enumStyle_t style, bool isLast) const override {
459             if (style == enumMask || style == enumNoMask)
460                 return "";
461             return indent(5) + '"' + prependIfDigit(s, v.second) + "\": " + fmtNum("%d", v.first) +
462                 (isLast ? "\n" : ",\n");
463         }
464     };
465 
466     // base for C and C++
467     class TPrinterCBase : public TPrinter {
468     protected:
printPrologue(std::ostream & out) const469         virtual void printPrologue(std::ostream& out) const override {
470             out << "#ifndef spirv_" << headerGuardSuffix() << std::endl
471                 << "#define spirv_" << headerGuardSuffix() << std::endl
472                 << std::endl;
473         }
474 
printMeta(std::ostream & out) const475         void printMeta(std::ostream& out) const override {
476             out << "#define SPV_VERSION 0x" << std::hex << DocVersion << std::dec << "\n";
477             out << "#define SPV_REVISION " << DocRevision << "\n";
478             out << "\n";
479 
480             return TPrinter::printMeta(out);
481         }
482 
printEpilogue(std::ostream & out) const483         virtual void printEpilogue(std::ostream& out) const override {
484             out << "#endif" << std::endl;
485         }
486 
printTypes(std::ostream & out) const487         virtual void printTypes(std::ostream& out) const override {
488             out << "typedef unsigned int " << pre() << "Id;\n\n";
489         }
490 
fmtConstInt(unsigned val,const std::string & name,const char * fmt,bool isLast) const491         virtual std::string fmtConstInt(unsigned val, const std::string& name,
492                                         const char* fmt, bool isLast) const override
493         {
494             return std::string("static const unsigned int ") + pre() + name +
495                 " = " + fmtNum(fmt, val) + (isLast ? ";\n\n" : ";\n");
496         }
497 
pre() const498         virtual std::string pre() const { return ""; } // C name prefix
499         virtual std::string headerGuardSuffix() const = 0;
500 
fmtEnumUse(const std::string & opPrefix,const std::string & name) const501         virtual std::string fmtEnumUse(const std::string& opPrefix, const std::string& name) const { return pre() + name; }
502 
printHasResultType(std::ostream & out) const503         virtual void printHasResultType(std::ostream& out) const override
504         {
505             const Json::Value& enums = spvRoot["spv"]["enum"];
506 
507             std::set<unsigned> seenValues;
508 
509             for (auto opClass = enums.begin(); opClass != enums.end(); ++opClass) {
510                 const auto opName   = (*opClass)["Name"].asString();
511                 if (opName != "Op") {
512                     continue;
513                 }
514 
515                 out << "#ifdef SPV_ENABLE_UTILITY_CODE" << std::endl;
516                 out << "inline void " << pre() << "HasResultAndType(" << pre() << opName << " opcode, bool *hasResult, bool *hasResultType) {" << std::endl;
517                 out << "    *hasResult = *hasResultType = false;" << std::endl;
518                 out << "    switch (opcode) {" << std::endl;
519                 out << "    default: /* unknown opcode */ break;" << std::endl;
520 
521                 for (auto& inst : spv::InstructionDesc) {
522 
523                     // Filter out duplicate enum values, which would break the switch statement.
524                     // These are probably just extension enums promoted to core.
525                     if (seenValues.find(inst.value) != seenValues.end()) {
526                         continue;
527                     }
528                     seenValues.insert(inst.value);
529 
530                     std::string name = inst.name;
531                     out << "    case " << fmtEnumUse("Op", name) << ": *hasResult = " << (inst.hasResult() ? "true" : "false") << "; *hasResultType = " << (inst.hasType() ? "true" : "false") << "; break;" << std::endl;
532                 }
533 
534                 out << "    }" << std::endl;
535                 out << "}" << std::endl;
536                 out << "#endif /* SPV_ENABLE_UTILITY_CODE */" << std::endl << std::endl;
537             }
538         }
539     };
540 
541     // C printer
542     class TPrinterC final : public TPrinterCBase {
543     private:
commentBeg() const544         std::string commentBeg() const override            { return "/*\n"; }
commentEnd(bool isLast) const545         std::string commentEnd(bool isLast) const override { return "*/\n"; }
commentBOL() const546         std::string commentBOL() const override            { return "** ";  }
547 
enumBeg(const std::string & s,enumStyle_t style) const548         std::string enumBeg(const std::string& s, enumStyle_t style) const override {
549             return std::string("typedef enum ") + pre() + s + styleStr(style) + "_ {\n";
550         }
551 
enumEnd(const std::string & s,enumStyle_t style,bool isLast) const552         std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override {
553             return "} " + pre() + s + styleStr(style) + ";\n\n";
554         }
555 
enumFmt(const std::string & s,const valpair_t & v,enumStyle_t style,bool isLast) const556         std::string enumFmt(const std::string& s, const valpair_t& v,
557                             enumStyle_t style, bool isLast) const override {
558             return indent() + pre() + s + v.second + styleStr(style) + " = " + fmtStyleVal(v.first, style) + ",\n";
559         }
560 
maxEnumFmt(const std::string & s,const valpair_t & v,enumStyle_t style) const561         std::string maxEnumFmt(const std::string& s, const valpair_t& v,
562                                enumStyle_t style) const override {
563             return enumFmt(s, v, style, true);
564         }
565 
pre() const566         std::string pre() const override { return "Spv"; } // C name prefix
headerGuardSuffix() const567         std::string headerGuardSuffix() const override { return "H"; }
568     };
569 
570     // C++ printer
571     class TPrinterCPP : public TPrinterCBase {
572     private:
printPrologue(std::ostream & out) const573         void printPrologue(std::ostream& out) const override {
574             TPrinterCBase::printPrologue(out);
575             out << "namespace spv {\n\n";
576         }
577 
printEpilogue(std::ostream & out) const578         void printEpilogue(std::ostream& out) const override {
579             const Json::Value& enums = spvRoot["spv"]["enum"];
580 
581             // Create overloaded operator| for mask types
582             out << "// Overload operator| for mask bit combining\n\n";
583 
584             for (auto opClass = enums.begin(); opClass != enums.end(); ++opClass) {
585                 const bool isMask   = (*opClass)["Type"].asString() == "Bit";
586                 const auto opName   = (*opClass)["Name"].asString();
587 
588                 if (isMask) {
589                     const auto typeName = opName + styleStr(enumMask);
590 
591                     out << "inline " + typeName + " operator|(" + typeName + " a, " + typeName + " b) { return " +
592                         typeName + "(unsigned(a) | unsigned(b)); }\n";
593                 }
594             }
595 
596             out << "\n}  // end namespace spv\n\n";
597             out << "#endif  // #ifndef spirv_" << headerGuardSuffix() << std::endl;
598         }
599 
commentBOL() const600         std::string commentBOL() const override { return "// "; }
601 
602 
enumBeg(const std::string & s,enumStyle_t style) const603         virtual std::string enumBeg(const std::string& s, enumStyle_t style) const override {
604             return std::string("enum ") + s + styleStr(style) + " {\n";
605         }
606 
enumEnd(const std::string & s,enumStyle_t style,bool isLast) const607         std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override {
608             return "};\n\n";
609         }
610 
enumFmt(const std::string & s,const valpair_t & v,enumStyle_t style,bool isLast) const611         virtual std::string enumFmt(const std::string& s, const valpair_t& v,
612                                     enumStyle_t style, bool isLast) const override {
613             return indent() + s + v.second + styleStr(style) + " = " + fmtStyleVal(v.first, style) + ",\n";
614         }
615 
maxEnumFmt(const std::string & s,const valpair_t & v,enumStyle_t style) const616         virtual std::string maxEnumFmt(const std::string& s, const valpair_t& v,
617                                        enumStyle_t style) const override {
618             return enumFmt(s, v, style, true);
619         }
620 
621         // The C++ and C++11 headers define types with the same name. So they
622         // should use the same header guard.
headerGuardSuffix() const623         std::string headerGuardSuffix() const override { return "HPP"; }
624 
625         std::string operators;
626     };
627 
628     // C++11 printer (uses enum classes)
629     class TPrinterCPP11 final : public TPrinterCPP {
630     private:
enumBeg(const std::string & s,enumStyle_t style) const631         std::string enumBeg(const std::string& s, enumStyle_t style) const override {
632             return std::string("enum class ") + s + styleStr(style) + " : unsigned {\n";
633         }
634 
enumFmt(const std::string & s,const valpair_t & v,enumStyle_t style,bool isLast) const635         std::string enumFmt(const std::string& s, const valpair_t& v,
636                             enumStyle_t style, bool isLast) const override {
637             return indent() + prependIfDigit(s, v.second) + " = " + fmtStyleVal(v.first, style) + ",\n";
638         }
639 
maxEnumFmt(const std::string & s,const valpair_t & v,enumStyle_t style) const640         std::string maxEnumFmt(const std::string& s, const valpair_t& v,
641                                enumStyle_t style) const override {
642             return enumFmt(s, v, style, true);
643         }
644 
645         // Add type prefix for scoped enum
fmtEnumUse(const std::string & opPrefix,const std::string & name) const646         virtual std::string fmtEnumUse(const std::string& opPrefix, const std::string& name) const override { return opPrefix + "::" + name; }
647 
headerGuardSuffix() const648         std::string headerGuardSuffix() const override { return "HPP"; }
649     };
650 
651     // LUA printer
652     class TPrinterLua final : public TPrinter {
653     private:
printPrologue(std::ostream & out) const654         void printPrologue(std::ostream& out) const override { out << "spv = {\n"; }
655 
printEpilogue(std::ostream & out) const656         void printEpilogue(std::ostream& out) const override { out << "}\n"; }
657 
commentBOL() const658         std::string commentBOL() const override { return "-- "; }
659 
enumBeg(const std::string & s,enumStyle_t style) const660         std::string enumBeg(const std::string& s, enumStyle_t style) const override {
661             return indent() + s + styleStr(style) + " = {\n";
662         }
663 
enumEnd(const std::string & s,enumStyle_t style,bool isLast) const664         std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override {
665             return indent() + "},\n\n";
666         }
667 
enumFmt(const std::string & s,const valpair_t & v,enumStyle_t style,bool isLast) const668         std::string enumFmt(const std::string& s, const valpair_t& v,
669                             enumStyle_t style, bool isLast) const override {
670             return indent(2) + prependIfDigit(s, v.second) + " = " + fmtStyleVal(v.first, style) + ",\n";
671         }
672 
fmtConstInt(unsigned val,const std::string & name,const char * fmt,bool isLast) const673         virtual std::string fmtConstInt(unsigned val, const std::string& name,
674                                         const char* fmt, bool isLast) const override
675         {
676             return indent() + name + " = " + fmtNum(fmt, val) + (isLast ? ",\n\n" : ",\n");
677         }
678     };
679 
680     // Python printer
681     class TPrinterPython final : public TPrinter {
682     private:
printPrologue(std::ostream & out) const683         void printPrologue(std::ostream& out) const override { out << "spv = {\n"; }
684 
printEpilogue(std::ostream & out) const685         void printEpilogue(std::ostream& out) const override { out << "}\n"; }
686 
commentBOL() const687         std::string commentBOL() const override { return "# "; }
688 
enumBeg(const std::string & s,enumStyle_t style) const689         std::string enumBeg(const std::string& s, enumStyle_t style) const override {
690             return indent() + "'" + s + styleStr(style) + "'" + " : {\n";
691         }
692 
enumEnd(const std::string & s,enumStyle_t style,bool isLast) const693         std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override {
694             return indent() + "},\n\n";
695         }
696 
enumFmt(const std::string & s,const valpair_t & v,enumStyle_t style,bool isLast) const697         std::string enumFmt(const std::string& s, const valpair_t& v,
698                             enumStyle_t style, bool isLast) const override {
699             return indent(2) + "'" + prependIfDigit(s, v.second) + "'" + " : " + fmtStyleVal(v.first, style) + ",\n";
700         }
701 
fmtConstInt(unsigned val,const std::string & name,const char * fmt,bool isLast) const702         std::string fmtConstInt(unsigned val, const std::string& name,
703                                 const char* fmt, bool isLast) const override
704         {
705             return indent() + "'" + name + "'" + " : " + fmtNum(fmt, val) + (isLast ? ",\n\n" : ",\n");
706         }
707     };
708 
709     // C# printer
710     class TPrinterCSharp final : public TPrinter {
711     private:
commentBOL() const712         std::string commentBOL() const override { return "// ";  }
713 
printPrologue(std::ostream & out) const714         void printPrologue(std::ostream& out) const override {
715             out << "namespace Spv\n{\n\n";
716             out << indent() << "public static class Specification\n";
717             out << indent() << "{\n";
718         }
719 
printEpilogue(std::ostream & out) const720         void printEpilogue(std::ostream& out) const override {
721             out << indent() << "}\n";
722             out << "}\n";
723         }
724 
enumBeg(const std::string & s,enumStyle_t style) const725         std::string enumBeg(const std::string& s, enumStyle_t style) const override {
726             return indent(2) + "public enum " + s + styleStr(style) + "\n" + indent(2) + "{\n";
727         }
728 
enumEnd(const std::string & s,enumStyle_t style,bool isLast) const729         std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override {
730             return indent(2) + "}" + + (isLast ? "\n" : "\n\n");
731         }
732 
enumFmt(const std::string & s,const valpair_t & v,enumStyle_t style,bool isLast) const733         std::string enumFmt(const std::string& s, const valpair_t& v,
734                             enumStyle_t style, bool isLast) const override {
735             return indent(3) + prependIfDigit(s, v.second) + " = " + fmtStyleVal(v.first, style) + ",\n";
736         }
737 
fmtConstInt(unsigned val,const std::string & name,const char * fmt,bool isLast) const738         std::string fmtConstInt(unsigned val, const std::string& name,
739                                 const char* fmt, bool isLast) const override {
740             return indent(2) + std::string("public const uint ") + name +
741                 " = " + fmtNum(fmt, val) + (isLast ? ";\n\n" : ";\n");
742         }
743     };
744 
745     // D printer
746     class TPrinterD final : public TPrinter {
747     private:
commentBeg() const748         std::string commentBeg() const override            { return "/+\n"; }
commentBOL() const749         std::string commentBOL() const override            { return " + ";  }
commentEnd(bool isLast) const750         std::string commentEnd(bool isLast) const override { return " +/\n"; }
751 
printPrologue(std::ostream & out) const752         void printPrologue(std::ostream& out) const override {
753             out << "module spv;\n\n";
754         }
755 
printEpilogue(std::ostream & out) const756         void printEpilogue(std::ostream& out) const override {
757         }
758 
enumBeg(const std::string & s,enumStyle_t style) const759         std::string enumBeg(const std::string& s, enumStyle_t style) const override {
760             return "enum " + s + styleStr(style) + " : uint\n{\n";
761         }
762 
enumEnd(const std::string & s,enumStyle_t style,bool isLast) const763         std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override {
764             return std::string("}\n\n");
765         }
766 
enumFmt(const std::string & s,const valpair_t & v,enumStyle_t style,bool isLast) const767         std::string enumFmt(const std::string& s, const valpair_t& v,
768                             enumStyle_t style, bool isLast) const override {
769             return indent() + prependIfDigit("_", v.second) + " = " + fmtStyleVal(v.first, style) + ",\n";
770         }
771 
fmtConstInt(unsigned val,const std::string & name,const char * fmt,bool isLast) const772         std::string fmtConstInt(unsigned val, const std::string& name,
773                                 const char* fmt, bool isLast) const override {
774             return std::string("enum uint ") + name +
775                 " = " + fmtNum(fmt, val) + (isLast ? ";\n\n" : ";\n");
776         }
777     };
778 
779 } // namespace
780 
781 namespace spv {
PrintAllHeaders()782     void PrintAllHeaders()
783     {
784         // TODO: Once MSVC 2012 is no longer a factor, use brace initializers here
785         std::vector<std::pair<TLanguage, std::string>> langInfo;
786 
787         langInfo.push_back(std::make_pair(ELangC,       "spirv.h"));
788         langInfo.push_back(std::make_pair(ELangCPP,     "spirv.hpp"));
789         langInfo.push_back(std::make_pair(ELangCPP11,   "spirv.hpp11"));
790         langInfo.push_back(std::make_pair(ELangJSON,    "spirv.json"));
791         langInfo.push_back(std::make_pair(ELangLua,     "spirv.lua"));
792         langInfo.push_back(std::make_pair(ELangPython,  "spirv.py"));
793         langInfo.push_back(std::make_pair(ELangCSharp,  "spirv.cs"));
794         langInfo.push_back(std::make_pair(ELangD,       "spv.d"));
795 
796         for (const auto& lang : langInfo) {
797             std::ofstream out(lang.second, std::ios::out);
798 
799             if ((out.rdstate() & std::ifstream::failbit)) {
800                 std::cerr << "Unable to open file: " << lang.second << std::endl;
801             } else {
802                 PrintHeader(lang.first, out);
803             }
804         }
805     }
806 
807     // Print header for given language to given output stream
PrintHeader(TLanguage lang,std::ostream & out)808     void PrintHeader(TLanguage lang, std::ostream& out)
809     {
810         typedef std::unique_ptr<TPrinter> TPrinterPtr;
811         TPrinterPtr p;
812 
813         switch (lang) {
814             case ELangC:       p = TPrinterPtr(new TPrinterC);       break;
815             case ELangCPP:     p = TPrinterPtr(new TPrinterCPP);     break;
816             case ELangCPP11:   p = TPrinterPtr(new TPrinterCPP11);   break;
817             case ELangJSON:    p = TPrinterPtr(new TPrinterJSON);    break;
818             case ELangLua:     p = TPrinterPtr(new TPrinterLua);     break;
819             case ELangPython:  p = TPrinterPtr(new TPrinterPython);  break;
820             case ELangCSharp:  p = TPrinterPtr(new TPrinterCSharp);  break;
821             case ELangD:       p = TPrinterPtr(new TPrinterD);       break;
822             case ELangAll:     PrintAllHeaders();                    break;
823             default:
824                 std::cerr << "Unknown language." << std::endl;
825                 return;
826         }
827 
828         // Print the data in the requested format
829         if (p)
830             out << *p << std::endl;
831 
832         // object is auto-deleted
833     }
834 
835 } // namespace spv
836