836 lines
33 KiB
C++
836 lines
33 KiB
C++
// Copyright (c) 2014-2019 The Khronos Group Inc.
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and/or associated documentation files (the "Materials"),
|
|
// to deal in the Materials without restriction, including without limitation
|
|
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
// and/or sell copies of the Materials, and to permit persons to whom the
|
|
// Materials are furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Materials.
|
|
//
|
|
// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS
|
|
// STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND
|
|
// HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/
|
|
//
|
|
// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
// FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS
|
|
// IN THE MATERIALS.
|
|
|
|
//
|
|
// Print headers for SPIR-V in several languages.
|
|
//
|
|
// To change the header information, change the C++-built database in doc.*.
|
|
//
|
|
// Then, use "spriv -h <language>" - e.g, spriv.{h,hpp,lua,py,etc}:
|
|
// replace the auto-generated header, or "spirv -H" to generate all
|
|
// supported language headers to predefined names in the current directory.
|
|
//
|
|
|
|
#include <string>
|
|
#include <sstream>
|
|
#include <fstream>
|
|
#include <cstring>
|
|
#include <cstdio>
|
|
#include <algorithm>
|
|
#include <memory>
|
|
#include <cctype>
|
|
#include <vector>
|
|
#include <utility>
|
|
#include <set>
|
|
|
|
#include "jsoncpp/dist/json/json.h"
|
|
|
|
#include "header.h"
|
|
#include "jsonToSpirv.h"
|
|
|
|
// snprintf and _snprintf are not quite the same, but close enough
|
|
// for our use.
|
|
#ifdef _MSC_VER
|
|
#pragma warning(disable:4996)
|
|
#define snprintf _snprintf
|
|
#endif
|
|
|
|
// This file converts SPIR-V definitions to an internal JSON
|
|
// representation, and then generates language specific
|
|
// data from that single internal form.
|
|
|
|
// Initially, the internal form is created from C++ data,
|
|
// though this can be changed to a JSON master in time.
|
|
|
|
namespace {
|
|
class TPrinter {
|
|
protected:
|
|
TPrinter();
|
|
|
|
static const int DocMagicNumber = 0x07230203;
|
|
static const int DocVersion = 0x00010400;
|
|
static const int DocRevision = 1;
|
|
#define DocRevisionString "1"
|
|
static const std::string DocCopyright;
|
|
static const std::string DocComment1;
|
|
static const std::string DocComment2;
|
|
|
|
enum enumStyle_t {
|
|
enumNoMask,
|
|
enumCount,
|
|
enumShift,
|
|
enumMask,
|
|
enumHex,
|
|
};
|
|
|
|
static std::string styleStr(enumStyle_t s) {
|
|
return s == enumShift ? "Shift" :
|
|
s == enumMask ? "Mask" : "";
|
|
}
|
|
|
|
friend std::ostream& operator<<(std::ostream&, const TPrinter&);
|
|
|
|
virtual void printAll(std::ostream&) const;
|
|
virtual void printComments(std::ostream&) const;
|
|
virtual void printPrologue(std::ostream&) const { }
|
|
virtual void printDefs(std::ostream&) const;
|
|
virtual void printEpilogue(std::ostream&) const { }
|
|
virtual void printMeta(std::ostream&) const;
|
|
virtual void printTypes(std::ostream&) const { }
|
|
virtual void printHasResultType(std::ostream&) const { };
|
|
|
|
virtual std::string escapeComment(const std::string& s) const;
|
|
|
|
// Default printComments() uses these comment strings
|
|
virtual std::string commentBeg() const { return ""; }
|
|
virtual std::string commentEnd(bool isLast) const { return ""; }
|
|
virtual std::string commentBOL() const { return ""; }
|
|
virtual std::string commentEOL(bool isLast) const { return ""; }
|
|
|
|
typedef std::pair<unsigned, std::string> valpair_t;
|
|
|
|
// for printing enum values
|
|
virtual std::string enumBeg(const std::string&, enumStyle_t) const { return ""; }
|
|
virtual std::string enumEnd(const std::string&, enumStyle_t, bool isLast = false) const {
|
|
return "";
|
|
}
|
|
virtual std::string enumFmt(const std::string&, const valpair_t&,
|
|
enumStyle_t, bool isLast = false) const {
|
|
return "";
|
|
}
|
|
virtual std::string maxEnumFmt(const std::string&, const valpair_t&,
|
|
enumStyle_t) const {
|
|
return "";
|
|
}
|
|
|
|
virtual std::string fmtConstInt(unsigned val, const std::string& name,
|
|
const char* fmt, bool isLast = false) const {
|
|
return "";
|
|
}
|
|
|
|
std::vector<valpair_t> getSortedVals(const Json::Value&) const;
|
|
|
|
virtual std::string indent(int count = 1) const {
|
|
return std::string(count * 4, ' '); // default indent level = 4
|
|
}
|
|
|
|
static std::string fmtNum(const char* fmt, unsigned val) {
|
|
char buff[16]; // ample for 8 hex digits + 0x
|
|
snprintf(buff, sizeof(buff), fmt, val);
|
|
buff[sizeof(buff)-1] = '\0'; // MSVC doesn't promise null termination
|
|
return buff;
|
|
}
|
|
|
|
static std::string fmtStyleVal(unsigned v, enumStyle_t style);
|
|
|
|
// If the enum value name would start with a sigit, prepend the enum name.
|
|
// E.g, "3D" -> "Dim3D".
|
|
static std::string prependIfDigit(const std::string& ename, const std::string& vname) {
|
|
return (std::isdigit(vname[0]) ? ename : std::string("")) + vname;
|
|
}
|
|
|
|
void addComment(Json::Value& node, const std::string& str);
|
|
|
|
Json::Value spvRoot; // JSON SPIR-V data
|
|
};
|
|
|
|
// Format value as mask or value
|
|
std::string TPrinter::fmtStyleVal(unsigned v, enumStyle_t style)
|
|
{
|
|
switch (style) {
|
|
case enumMask:
|
|
return fmtNum("0x%08x", 1<<v);
|
|
case enumHex:
|
|
return fmtNum("0x%08x", v);
|
|
default:
|
|
return std::to_string(v);
|
|
}
|
|
}
|
|
|
|
const std::string TPrinter::DocCopyright =
|
|
"Copyright (c) 2014-2019 The Khronos Group Inc.\n"
|
|
"\n"
|
|
"Permission is hereby granted, free of charge, to any person obtaining a copy\n"
|
|
"of this software and/or associated documentation files (the \"Materials\"),\n"
|
|
"to deal in the Materials without restriction, including without limitation\n"
|
|
"the rights to use, copy, modify, merge, publish, distribute, sublicense,\n"
|
|
"and/or sell copies of the Materials, and to permit persons to whom the\n"
|
|
"Materials are furnished to do so, subject to the following conditions:\n"
|
|
"\n"
|
|
"The above copyright notice and this permission notice shall be included in\n"
|
|
"all copies or substantial portions of the Materials.\n"
|
|
"\n"
|
|
"MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS\n"
|
|
"STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND\n"
|
|
"HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ \n"
|
|
"\n"
|
|
"THE MATERIALS ARE PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\n"
|
|
"OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n"
|
|
"FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n"
|
|
"THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n"
|
|
"LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n"
|
|
"FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS\n"
|
|
"IN THE MATERIALS.\n";
|
|
|
|
const std::string TPrinter::DocComment1 =
|
|
"This header is automatically generated by the same tool that creates\n"
|
|
"the Binary Section of the SPIR-V specification.\n";
|
|
|
|
const std::string TPrinter::DocComment2 =
|
|
"Enumeration tokens for SPIR-V, in various styles:\n"
|
|
" C, C++, C++11, JSON, Lua, Python, C#, D\n"
|
|
"\n"
|
|
"- C will have tokens with a \"Spv\" prefix, e.g.: SpvSourceLanguageGLSL\n"
|
|
"- C++ will have tokens in the \"spv\" name space, e.g.: spv::SourceLanguageGLSL\n"
|
|
"- C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL\n"
|
|
"- Lua will use tables, e.g.: spv.SourceLanguage.GLSL\n"
|
|
"- Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL']\n"
|
|
"- C# will use enum classes in the Specification class located in the \"Spv\" namespace,\n"
|
|
" e.g.: Spv.Specification.SourceLanguage.GLSL\n"
|
|
"- D will have tokens under the \"spv\" module, e.g: spv.SourceLanguage.GLSL\n"
|
|
"\n"
|
|
"Some tokens act like mask values, which can be OR'd together,\n"
|
|
"while others are mutually exclusive. The mask-like ones have\n"
|
|
"\"Mask\" in their name, and a parallel enum that has the shift\n"
|
|
"amount (1 << x) for each corresponding enumerant.\n";
|
|
|
|
// Construct
|
|
TPrinter::TPrinter()
|
|
{
|
|
Json::Value& meta = spvRoot["spv"]["meta"];
|
|
Json::Value& enums = spvRoot["spv"]["enum"];
|
|
|
|
meta["MagicNumber"] = DocMagicNumber;
|
|
meta["Version"] = DocVersion;
|
|
meta["Revision"] = DocRevision;
|
|
meta["OpCodeMask"] = 0xffff;
|
|
meta["WordCountShift"] = 16;
|
|
|
|
int commentId = 0;
|
|
addComment(meta["Comment"][commentId++], DocCopyright);
|
|
addComment(meta["Comment"][commentId++], DocComment1);
|
|
addComment(meta["Comment"][commentId++], DocComment2);
|
|
|
|
for (int e = spv::OperandSource; e < spv::OperandOpcode; ++e) {
|
|
auto& enumSet = spv::OperandClassParams[e];
|
|
const bool mask = enumSet.bitmask;
|
|
const std::string enumName = enumSet.codeName;
|
|
|
|
for (auto& enumRow : enumSet) {
|
|
std::string name = enumRow.name;
|
|
enums[e - spv::OperandSource]["Values"][name] = enumRow.value;
|
|
}
|
|
|
|
enums[e - spv::OperandSource]["Type"] = mask ? "Bit" : "Value";
|
|
enums[e - spv::OperandSource]["Name"] = enumName;
|
|
}
|
|
|
|
// Instructions are in their own different table
|
|
{
|
|
auto& entry = enums[spv::OperandOpcode - spv::OperandSource];
|
|
for (auto& enumRow : spv::InstructionDesc) {
|
|
std::string name = enumRow.name;
|
|
entry["Values"][name] = enumRow.value;
|
|
}
|
|
entry["Type"] = "Value";
|
|
entry["Name"] = "Op";
|
|
}
|
|
}
|
|
|
|
// Create comment
|
|
void TPrinter::addComment(Json::Value& node, const std::string& str)
|
|
{
|
|
std::istringstream cstream(str);
|
|
std::string cline;
|
|
|
|
int line = 0;
|
|
while (std::getline(cstream, cline)) // fmt each line
|
|
node[line++] = cline;
|
|
}
|
|
|
|
|
|
// Return a list of values sorted by enum value. The std::vector
|
|
// returned by value is okay in c++11 due to move semantics.
|
|
std::vector<TPrinter::valpair_t>
|
|
TPrinter::getSortedVals(const Json::Value& p) const
|
|
{
|
|
std::vector<valpair_t> values;
|
|
|
|
for (auto e = p.begin(); e != p.end(); ++e)
|
|
values.push_back(valpair_t(e->asUInt(), e.name()));
|
|
|
|
// Use a stable sort because we might have aliases, e.g.
|
|
// SubgropuBallot (might be in future core) vs. SubgroupBallotKHR.
|
|
std::stable_sort(values.begin(), values.end());
|
|
|
|
return values;
|
|
}
|
|
|
|
// Escape comment characters if needed
|
|
std::string TPrinter::escapeComment(const std::string& s) const { return s; }
|
|
|
|
// Format comments in language specific way
|
|
void TPrinter::printComments(std::ostream& out) const
|
|
{
|
|
const int commentCount = spvRoot["spv"]["meta"]["Comment"].size();
|
|
int commentNum = 0;
|
|
|
|
for (const auto& comment : spvRoot["spv"]["meta"]["Comment"]) {
|
|
out << commentBeg();
|
|
|
|
for (int line = 0; line < int(comment.size()); ++line)
|
|
out << commentBOL() << escapeComment(comment[line].asString()) <<
|
|
commentEOL((line+1) == comment.size()) << std::endl;
|
|
|
|
out << commentEnd(++commentNum == commentCount) << std::endl;
|
|
}
|
|
}
|
|
|
|
// Format header metadata
|
|
void TPrinter::printMeta(std::ostream& out) const
|
|
{
|
|
const Json::Value& meta = spvRoot["spv"]["meta"];
|
|
|
|
const auto print = [&](const char* name, const char* fmt, bool isLast) {
|
|
out << fmtConstInt(meta[name].asUInt(), name, fmt, isLast);
|
|
};
|
|
|
|
print("MagicNumber", "0x%08lx", false);
|
|
print("Version", "0x%08lx", false);
|
|
print("Revision", "%d", false);
|
|
print("OpCodeMask", "0x%04x", false);
|
|
print("WordCountShift", "%d", true);
|
|
}
|
|
|
|
// Format value definitions in language specific way
|
|
void TPrinter::printDefs(std::ostream& out) const
|
|
{
|
|
const Json::Value& enums = spvRoot["spv"]["enum"];
|
|
|
|
for (auto opClass = enums.begin(); opClass != enums.end(); ++opClass) {
|
|
const bool isMask = (*opClass)["Type"].asString() == "Bit";
|
|
const auto opName = (*opClass)["Name"].asString();
|
|
const auto opPrefix = opName == "Op" ? "" : opName;
|
|
|
|
for (enumStyle_t style = (isMask ? enumShift : enumCount);
|
|
style <= (isMask ? enumMask : enumCount); style = enumStyle_t(int(style)+1)) {
|
|
|
|
out << enumBeg(opName, style);
|
|
|
|
if (style == enumMask)
|
|
out << enumFmt(opPrefix, valpair_t(0, "MaskNone"), enumNoMask);
|
|
|
|
const auto sorted = getSortedVals((*opClass)["Values"]);
|
|
|
|
std::string maxEnum = maxEnumFmt(opName, valpair_t(0x7FFFFFFF, "Max"), enumHex);
|
|
|
|
bool printMax = (style != enumMask && maxEnum.size() > 0);
|
|
|
|
for (const auto& v : sorted)
|
|
out << enumFmt(opPrefix, v, style, !printMax && v.second == sorted.back().second);
|
|
|
|
if (printMax)
|
|
out << maxEnum;
|
|
|
|
auto nextOpClass = opClass;
|
|
out << enumEnd(opName, style, ++nextOpClass == enums.end());
|
|
}
|
|
}
|
|
}
|
|
|
|
void TPrinter::printAll(std::ostream& out) const
|
|
{
|
|
printComments(out);
|
|
printPrologue(out);
|
|
printTypes(out);
|
|
printMeta(out);
|
|
printDefs(out);
|
|
printHasResultType(out);
|
|
printEpilogue(out);
|
|
}
|
|
|
|
// Stream entire header to output
|
|
std::ostream& operator<<(std::ostream& out, const TPrinter &p)
|
|
{
|
|
p.printAll(out);
|
|
return out;
|
|
}
|
|
|
|
// JSON printer. Rather than use the default printer, we supply our own so
|
|
// we can control the printing order within various containers.
|
|
class TPrinterJSON final : public TPrinter {
|
|
private:
|
|
void printPrologue(std::ostream& out) const override { out << "{\n" + indent() + "\"spv\":\n" + indent() + "{\n"; }
|
|
void printEpilogue(std::ostream& out) const override { out << indent() + "}\n}\n"; }
|
|
|
|
std::string escapeComment(const std::string& s) const override {
|
|
std::string newStr;
|
|
for (auto c : s) {
|
|
if (c == '"') {
|
|
newStr += '\\';
|
|
newStr += c;
|
|
} else {
|
|
newStr += c;
|
|
}
|
|
}
|
|
return newStr;
|
|
}
|
|
|
|
std::string fmtConstInt(unsigned val, const std::string& name,
|
|
const char* fmt, bool isLast) const override {
|
|
return indent(3) + '"' + name + "\": " + fmtNum("%d", val) + (isLast ? "\n" : ",\n");
|
|
}
|
|
|
|
void printMeta(std::ostream& out) const override
|
|
{
|
|
out << indent(2) + "\"meta\":\n" + indent(2) + "{\n";
|
|
printComments(out);
|
|
TPrinter::printMeta(out);
|
|
out << indent(2) + "},\n";
|
|
}
|
|
|
|
std::string commentBeg() const override { return indent(4) + "[\n"; }
|
|
std::string commentEnd(bool isLast) const override { return indent(4) + (isLast ? "]" : "],"); }
|
|
std::string commentBOL() const override { return indent(5) + '"'; }
|
|
std::string commentEOL(bool isLast) const override { return (isLast ? "\"" : "\","); }
|
|
|
|
void printComments(std::ostream& out) const override
|
|
{
|
|
out << indent(3) + "\"Comment\":\n" + indent(3) + "[\n";
|
|
TPrinter::printComments(out);
|
|
out << indent(3) + "],\n";
|
|
}
|
|
|
|
void printDefs(std::ostream& out) const override
|
|
{
|
|
out << indent(2) + "\"enum\":\n" + indent(2) + "[\n";
|
|
TPrinter::printDefs(out);
|
|
out << indent(2) + "]\n";
|
|
}
|
|
|
|
void printAll(std::ostream& out) const override
|
|
{
|
|
printPrologue(out);
|
|
printMeta(out);
|
|
printDefs(out);
|
|
printEpilogue(out);
|
|
}
|
|
|
|
std::string enumBeg(const std::string& s, enumStyle_t style) const override {
|
|
if (style == enumMask)
|
|
return "";
|
|
return indent(3) + "{\n" +
|
|
indent(4) + "\"Name\": \"" + s + "\",\n" +
|
|
indent(4) + "\"Type\": " + (style == enumShift ? "\"Bit\"" : "\"Value\"") + ",\n" +
|
|
indent(4) + "\"Values\":\n" +
|
|
indent(4) + "{\n";
|
|
}
|
|
|
|
std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override {
|
|
if (style == enumMask)
|
|
return "";
|
|
return indent(4) + "}\n" +
|
|
indent(3) + "}" + (isLast ? "" : ",") + "\n";
|
|
}
|
|
|
|
std::string enumFmt(const std::string& s, const valpair_t& v,
|
|
enumStyle_t style, bool isLast) const override {
|
|
if (style == enumMask || style == enumNoMask)
|
|
return "";
|
|
return indent(5) + '"' + prependIfDigit(s, v.second) + "\": " + fmtNum("%d", v.first) +
|
|
(isLast ? "\n" : ",\n");
|
|
}
|
|
};
|
|
|
|
// base for C and C++
|
|
class TPrinterCBase : public TPrinter {
|
|
protected:
|
|
virtual void printPrologue(std::ostream& out) const override {
|
|
out << "#ifndef spirv_" << headerGuardSuffix() << std::endl
|
|
<< "#define spirv_" << headerGuardSuffix() << std::endl
|
|
<< std::endl;
|
|
}
|
|
|
|
void printMeta(std::ostream& out) const override {
|
|
out << "#define SPV_VERSION 0x" << std::hex << DocVersion << std::dec << "\n";
|
|
out << "#define SPV_REVISION " << DocRevision << "\n";
|
|
out << "\n";
|
|
|
|
return TPrinter::printMeta(out);
|
|
}
|
|
|
|
virtual void printEpilogue(std::ostream& out) const override {
|
|
out << "#endif" << std::endl;
|
|
}
|
|
|
|
virtual void printTypes(std::ostream& out) const override {
|
|
out << "typedef unsigned int " << pre() << "Id;\n\n";
|
|
}
|
|
|
|
virtual std::string fmtConstInt(unsigned val, const std::string& name,
|
|
const char* fmt, bool isLast) const override
|
|
{
|
|
return std::string("static const unsigned int ") + pre() + name +
|
|
" = " + fmtNum(fmt, val) + (isLast ? ";\n\n" : ";\n");
|
|
}
|
|
|
|
virtual std::string pre() const { return ""; } // C name prefix
|
|
virtual std::string headerGuardSuffix() const = 0;
|
|
|
|
virtual std::string fmtEnumUse(const std::string& opPrefix, const std::string& name) const { return pre() + name; }
|
|
|
|
virtual void printHasResultType(std::ostream& out) const
|
|
{
|
|
const Json::Value& enums = spvRoot["spv"]["enum"];
|
|
|
|
std::set<unsigned> seenValues;
|
|
|
|
for (auto opClass = enums.begin(); opClass != enums.end(); ++opClass) {
|
|
const auto opName = (*opClass)["Name"].asString();
|
|
if (opName != "Op") {
|
|
continue;
|
|
}
|
|
|
|
out << "#ifdef SPV_ENABLE_UTILITY_CODE" << std::endl;
|
|
out << "inline void " << pre() << "HasResultAndType(" << pre() << opName << " opcode, bool *hasResult, bool *hasResultType) {" << std::endl;
|
|
out << " *hasResult = *hasResultType = false;" << std::endl;
|
|
out << " switch (opcode) {" << std::endl;
|
|
out << " default: /* unknown opcode */ break;" << std::endl;
|
|
|
|
for (auto& inst : spv::InstructionDesc) {
|
|
|
|
// Filter out duplicate enum values, which would break the switch statement.
|
|
// These are probably just extension enums promoted to core.
|
|
if (seenValues.find(inst.value) != seenValues.end()) {
|
|
continue;
|
|
}
|
|
seenValues.insert(inst.value);
|
|
|
|
std::string name = inst.name;
|
|
out << " case " << fmtEnumUse("Op", name) << ": *hasResult = " << (inst.hasResult() ? "true" : "false") << "; *hasResultType = " << (inst.hasType() ? "true" : "false") << "; break;" << std::endl;
|
|
}
|
|
|
|
out << " }" << std::endl;
|
|
out << "}" << std::endl;
|
|
out << "#endif /* SPV_ENABLE_UTILITY_CODE */" << std::endl << std::endl;
|
|
}
|
|
}
|
|
};
|
|
|
|
// C printer
|
|
class TPrinterC final : public TPrinterCBase {
|
|
private:
|
|
std::string commentBeg() const override { return "/*\n"; }
|
|
std::string commentEnd(bool isLast) const override { return "*/\n"; }
|
|
std::string commentBOL() const override { return "** "; }
|
|
|
|
std::string enumBeg(const std::string& s, enumStyle_t style) const override {
|
|
return std::string("typedef enum ") + pre() + s + styleStr(style) + "_ {\n";
|
|
}
|
|
|
|
std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override {
|
|
return "} " + pre() + s + styleStr(style) + ";\n\n";
|
|
}
|
|
|
|
std::string enumFmt(const std::string& s, const valpair_t& v,
|
|
enumStyle_t style, bool isLast) const override {
|
|
return indent() + pre() + s + v.second + styleStr(style) + " = " + fmtStyleVal(v.first, style) + ",\n";
|
|
}
|
|
|
|
std::string maxEnumFmt(const std::string& s, const valpair_t& v,
|
|
enumStyle_t style) const override {
|
|
return enumFmt(s, v, style, true);
|
|
}
|
|
|
|
std::string pre() const override { return "Spv"; } // C name prefix
|
|
std::string headerGuardSuffix() const override { return "H"; }
|
|
};
|
|
|
|
// C++ printer
|
|
class TPrinterCPP : public TPrinterCBase {
|
|
private:
|
|
void printPrologue(std::ostream& out) const override {
|
|
TPrinterCBase::printPrologue(out);
|
|
out << "namespace spv {\n\n";
|
|
}
|
|
|
|
void printEpilogue(std::ostream& out) const override {
|
|
const Json::Value& enums = spvRoot["spv"]["enum"];
|
|
|
|
// Create overloaded operator| for mask types
|
|
out << "// Overload operator| for mask bit combining\n\n";
|
|
|
|
for (auto opClass = enums.begin(); opClass != enums.end(); ++opClass) {
|
|
const bool isMask = (*opClass)["Type"].asString() == "Bit";
|
|
const auto opName = (*opClass)["Name"].asString();
|
|
|
|
if (isMask) {
|
|
const auto typeName = opName + styleStr(enumMask);
|
|
|
|
out << "inline " + typeName + " operator|(" + typeName + " a, " + typeName + " b) { return " +
|
|
typeName + "(unsigned(a) | unsigned(b)); }\n";
|
|
}
|
|
}
|
|
|
|
out << "\n} // end namespace spv\n\n";
|
|
out << "#endif // #ifndef spirv_" << headerGuardSuffix() << std::endl;
|
|
}
|
|
|
|
std::string commentBOL() const override { return "// "; }
|
|
|
|
|
|
virtual std::string enumBeg(const std::string& s, enumStyle_t style) const override {
|
|
return std::string("enum ") + s + styleStr(style) + " {\n";
|
|
}
|
|
|
|
std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override {
|
|
return "};\n\n";
|
|
}
|
|
|
|
virtual std::string enumFmt(const std::string& s, const valpair_t& v,
|
|
enumStyle_t style, bool isLast) const override {
|
|
return indent() + s + v.second + styleStr(style) + " = " + fmtStyleVal(v.first, style) + ",\n";
|
|
}
|
|
|
|
virtual std::string maxEnumFmt(const std::string& s, const valpair_t& v,
|
|
enumStyle_t style) const override {
|
|
return enumFmt(s, v, style, true);
|
|
}
|
|
|
|
// The C++ and C++11 headers define types with the same name. So they
|
|
// should use the same header guard.
|
|
std::string headerGuardSuffix() const override { return "HPP"; }
|
|
|
|
std::string operators;
|
|
};
|
|
|
|
// C++11 printer (uses enum classes)
|
|
class TPrinterCPP11 final : public TPrinterCPP {
|
|
private:
|
|
std::string enumBeg(const std::string& s, enumStyle_t style) const override {
|
|
return std::string("enum class ") + s + styleStr(style) + " : unsigned {\n";
|
|
}
|
|
|
|
std::string enumFmt(const std::string& s, const valpair_t& v,
|
|
enumStyle_t style, bool isLast) const override {
|
|
return indent() + prependIfDigit(s, v.second) + " = " + fmtStyleVal(v.first, style) + ",\n";
|
|
}
|
|
|
|
std::string maxEnumFmt(const std::string& s, const valpair_t& v,
|
|
enumStyle_t style) const override {
|
|
return enumFmt(s, v, style, true);
|
|
}
|
|
|
|
// Add type prefix for scoped enum
|
|
virtual std::string fmtEnumUse(const std::string& opPrefix, const std::string& name) const { return opPrefix + "::" + name; }
|
|
|
|
std::string headerGuardSuffix() const override { return "HPP"; }
|
|
};
|
|
|
|
// LUA printer
|
|
class TPrinterLua final : public TPrinter {
|
|
private:
|
|
void printPrologue(std::ostream& out) const override { out << "spv = {\n"; }
|
|
|
|
void printEpilogue(std::ostream& out) const override { out << "}\n"; }
|
|
|
|
std::string commentBOL() const override { return "-- "; }
|
|
|
|
std::string enumBeg(const std::string& s, enumStyle_t style) const override {
|
|
return indent() + s + styleStr(style) + " = {\n";
|
|
}
|
|
|
|
std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override {
|
|
return indent() + "},\n\n";
|
|
}
|
|
|
|
std::string enumFmt(const std::string& s, const valpair_t& v,
|
|
enumStyle_t style, bool isLast) const override {
|
|
return indent(2) + prependIfDigit(s, v.second) + " = " + fmtStyleVal(v.first, style) + ",\n";
|
|
}
|
|
|
|
virtual std::string fmtConstInt(unsigned val, const std::string& name,
|
|
const char* fmt, bool isLast) const override
|
|
{
|
|
return indent() + name + " = " + fmtNum(fmt, val) + (isLast ? ",\n\n" : ",\n");
|
|
}
|
|
};
|
|
|
|
// Python printer
|
|
class TPrinterPython final : public TPrinter {
|
|
private:
|
|
void printPrologue(std::ostream& out) const override { out << "spv = {\n"; }
|
|
|
|
void printEpilogue(std::ostream& out) const override { out << "}\n"; }
|
|
|
|
std::string commentBOL() const override { return "# "; }
|
|
|
|
std::string enumBeg(const std::string& s, enumStyle_t style) const override {
|
|
return indent() + "'" + s + styleStr(style) + "'" + " : {\n";
|
|
}
|
|
|
|
std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override {
|
|
return indent() + "},\n\n";
|
|
}
|
|
|
|
std::string enumFmt(const std::string& s, const valpair_t& v,
|
|
enumStyle_t style, bool isLast) const override {
|
|
return indent(2) + "'" + prependIfDigit(s, v.second) + "'" + " : " + fmtStyleVal(v.first, style) + ",\n";
|
|
}
|
|
|
|
std::string fmtConstInt(unsigned val, const std::string& name,
|
|
const char* fmt, bool isLast) const override
|
|
{
|
|
return indent() + "'" + name + "'" + " : " + fmtNum(fmt, val) + (isLast ? ",\n\n" : ",\n");
|
|
}
|
|
};
|
|
|
|
// C# printer
|
|
class TPrinterCSharp final : public TPrinter {
|
|
private:
|
|
std::string commentBOL() const override { return "// "; }
|
|
|
|
void printPrologue(std::ostream& out) const override {
|
|
out << "namespace Spv\n{\n\n";
|
|
out << indent() << "public static class Specification\n";
|
|
out << indent() << "{\n";
|
|
}
|
|
|
|
void printEpilogue(std::ostream& out) const override {
|
|
out << indent() << "}\n";
|
|
out << "}\n";
|
|
}
|
|
|
|
std::string enumBeg(const std::string& s, enumStyle_t style) const override {
|
|
return indent(2) + "public enum " + s + styleStr(style) + "\n" + indent(2) + "{\n";
|
|
}
|
|
|
|
std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override {
|
|
return indent(2) + "}" + + (isLast ? "\n" : "\n\n");
|
|
}
|
|
|
|
std::string enumFmt(const std::string& s, const valpair_t& v,
|
|
enumStyle_t style, bool isLast) const override {
|
|
return indent(3) + prependIfDigit(s, v.second) + " = " + fmtStyleVal(v.first, style) + ",\n";
|
|
}
|
|
|
|
std::string fmtConstInt(unsigned val, const std::string& name,
|
|
const char* fmt, bool isLast) const override {
|
|
return indent(2) + std::string("public const uint ") + name +
|
|
" = " + fmtNum(fmt, val) + (isLast ? ";\n\n" : ";\n");
|
|
}
|
|
};
|
|
|
|
// D printer
|
|
class TPrinterD final : public TPrinter {
|
|
private:
|
|
std::string commentBeg() const override { return "/+\n"; }
|
|
std::string commentBOL() const override { return " + "; }
|
|
std::string commentEnd(bool isLast) const override { return " +/\n"; }
|
|
|
|
void printPrologue(std::ostream& out) const override {
|
|
out << "module spv;\n\n";
|
|
}
|
|
|
|
void printEpilogue(std::ostream& out) const override {
|
|
}
|
|
|
|
std::string enumBeg(const std::string& s, enumStyle_t style) const override {
|
|
return "enum " + s + styleStr(style) + " : uint\n{\n";
|
|
}
|
|
|
|
std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override {
|
|
return std::string("}\n\n");
|
|
}
|
|
|
|
std::string enumFmt(const std::string& s, const valpair_t& v,
|
|
enumStyle_t style, bool isLast) const override {
|
|
return indent() + prependIfDigit("_", v.second) + " = " + fmtStyleVal(v.first, style) + ",\n";
|
|
}
|
|
|
|
std::string fmtConstInt(unsigned val, const std::string& name,
|
|
const char* fmt, bool isLast) const override {
|
|
return std::string("enum uint ") + name +
|
|
" = " + fmtNum(fmt, val) + (isLast ? ";\n\n" : ";\n");
|
|
}
|
|
};
|
|
|
|
} // namespace
|
|
|
|
namespace spv {
|
|
void PrintAllHeaders()
|
|
{
|
|
// TODO: Once MSVC 2012 is no longer a factor, use brace initializers here
|
|
std::vector<std::pair<TLanguage, std::string>> langInfo;
|
|
|
|
langInfo.push_back(std::make_pair(ELangC, "spirv.h"));
|
|
langInfo.push_back(std::make_pair(ELangCPP, "spirv.hpp"));
|
|
langInfo.push_back(std::make_pair(ELangCPP11, "spirv.hpp11"));
|
|
langInfo.push_back(std::make_pair(ELangJSON, "spirv.json"));
|
|
langInfo.push_back(std::make_pair(ELangLua, "spirv.lua"));
|
|
langInfo.push_back(std::make_pair(ELangPython, "spirv.py"));
|
|
langInfo.push_back(std::make_pair(ELangCSharp, "spirv.cs"));
|
|
langInfo.push_back(std::make_pair(ELangD, "spv.d"));
|
|
|
|
for (const auto& lang : langInfo) {
|
|
std::ofstream out(lang.second, std::ios::out);
|
|
|
|
if ((out.rdstate() & std::ifstream::failbit)) {
|
|
std::cerr << "Unable to open file: " << lang.second << std::endl;
|
|
} else {
|
|
PrintHeader(lang.first, out);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Print header for given language to given output stream
|
|
void PrintHeader(TLanguage lang, std::ostream& out)
|
|
{
|
|
typedef std::unique_ptr<TPrinter> TPrinterPtr;
|
|
TPrinterPtr p;
|
|
|
|
switch (lang) {
|
|
case ELangC: p = TPrinterPtr(new TPrinterC); break;
|
|
case ELangCPP: p = TPrinterPtr(new TPrinterCPP); break;
|
|
case ELangCPP11: p = TPrinterPtr(new TPrinterCPP11); break;
|
|
case ELangJSON: p = TPrinterPtr(new TPrinterJSON); break;
|
|
case ELangLua: p = TPrinterPtr(new TPrinterLua); break;
|
|
case ELangPython: p = TPrinterPtr(new TPrinterPython); break;
|
|
case ELangCSharp: p = TPrinterPtr(new TPrinterCSharp); break;
|
|
case ELangD: p = TPrinterPtr(new TPrinterD); break;
|
|
case ELangAll: PrintAllHeaders(); break;
|
|
default:
|
|
std::cerr << "Unknown language." << std::endl;
|
|
return;
|
|
}
|
|
|
|
// Print the data in the requested format
|
|
if (p)
|
|
out << *p << std::endl;
|
|
|
|
// object is auto-deleted
|
|
}
|
|
|
|
} // namespace spv
|