diff --git a/src/V3EmitMkJson.cpp b/src/V3EmitMkJson.cpp index 91b9dcf9b..d42c57ad3 100644 --- a/src/V3EmitMkJson.cpp +++ b/src/V3EmitMkJson.cpp @@ -30,111 +30,6 @@ VL_DEFINE_DEBUG_FUNCTIONS; // Emit statements class V3EmitMkJsonEmitter final { - class Printer final { - // MEMBERS - private: - const std::unique_ptr& m_of; - std::stack - m_scope; // Stack of ']' and '}' to be used to close currently open scopes - std::string m_prefix; // Prefix emitted before each line in the current scope (indent * - // scope depth) - std::string m_indent; // Single indent - bool m_empty = true; // Indicates that the current scope is empty - - // METHODS - public: - explicit Printer(const std::unique_ptr& of, - const std::string& indent = " ") - : m_of(of) - , m_indent(indent) { - begin(); - } - - ~Printer() { end(); } - - Printer& begin(const std::string& name, char type = '{') { - if (!m_empty) *m_of << ",\n"; - *m_of << m_prefix << "\"" << name << "\": " << type << "\n"; - m_prefix += m_indent; - m_scope.push(type == '{' ? '}' : ']'); - m_empty = true; - return *this; - } - - Printer& put(const std::string& name, const std::string& value) { - if (!m_empty) *m_of << ",\n"; - *m_of << m_prefix << "\"" << name << "\": \"" << value << "\""; - m_empty = false; - return *this; - } - - Printer& put(const std::string& name, bool value) { - if (!m_empty) *m_of << ",\n"; - *m_of << m_prefix << "\"" << name << "\": " << (value ? "true" : "false"); - m_empty = false; - return *this; - } - - Printer& put(const std::string& name, int value) { - if (!m_empty) *m_of << ",\n"; - *m_of << m_prefix << "\"" << name << "\": " << value; - m_empty = false; - return *this; - } - - Printer& begin(char type = '{') { - if (!m_empty) *m_of << ",\n"; - *m_of << m_prefix << type << "\n"; - m_prefix += m_indent; - m_scope.push(type == '{' ? '}' : ']'); - m_empty = true; - return *this; - } - - Printer& put(const std::string& value) { - if (!m_empty) *m_of << ",\n"; - *m_of << m_prefix << "\"" << value << "\""; - m_empty = false; - return *this; - } - - Printer& put(bool value) { - if (!m_empty) *m_of << ",\n"; - *m_of << m_prefix << (value ? "true" : "false"); - m_empty = false; - return *this; - } - - Printer& put(int value) { - if (!m_empty) *m_of << ",\n"; - *m_of << m_prefix << value; - m_empty = false; - return *this; - } - - template - Printer& putList(const std::string& name, const T& list) { - if (list.empty()) return *this; - begin(name, '['); - for (auto it = list.begin(); it != list.end(); ++it) put(*it); - return end(); - } - - Printer& end() { - assert(m_prefix.length() >= m_indent.length()); - m_prefix.erase(m_prefix.end() - m_indent.length(), m_prefix.end()); - assert(!m_scope.empty()); - *m_of << "\n" << m_prefix << m_scope.top(); - m_scope.pop(); - return *this; - } - - Printer& operator+=(Printer& cursor) { - // Meaningless syntax sugar, at least for now - return *this; - } - }; - // METHODS // STATIC FUNCTIONS @@ -142,8 +37,7 @@ class V3EmitMkJsonEmitter final { const std::string makeDir = V3Os::filenameSlashPath(V3Os::filenameRealPath(v3Global.opt.makeDir())); - const std::unique_ptr of{ - V3File::new_ofstream(makeDir + "/" + v3Global.opt.prefix() + ".json")}; + V3OutJsonFile of{makeDir + "/" + v3Global.opt.prefix() + ".json"}; std::vector classesFast; std::vector classesSlow; @@ -188,41 +82,40 @@ class V3EmitMkJsonEmitter final { for (const auto& cppFile : v3Global.opt.cppFiles()) cppFiles.emplace_back(V3Os::filenameSlashPath(V3Os::filenameRealPath(cppFile))); - Printer manifest(of); - Printer& cursor = manifest.put("version", 1) - .begin("system") - .put("perl", V3Options::getenvPERL()) - .put("python3", V3Options::getenvPYTHON3()) - .put("verilator_root", verilatorRoot) - .put("verilator_solver", V3Options::getenvVERILATOR_SOLVER()) - .end() - .begin("options") - .putList("cflags", v3Global.opt.cFlags()) - .putList("ldflags", v3Global.opt.ldLibs()) - .put("system_c", v3Global.opt.systemC()) - .put("coverage", v3Global.opt.coverage()) - .put("use_timing", v3Global.usesTiming()) - .put("threads", v3Global.opt.threads()) - .put("trace", v3Global.opt.trace()) - .put("trace_fst", v3Global.opt.traceEnabledFst()) - .put("trace_saif", v3Global.opt.traceEnabledSaif()) - .put("trace_vcd", v3Global.opt.traceEnabledVcd()) - .end() - .begin("sources") - .putList("global", global) - .putList("classes_slow", classesSlow) - .putList("classes_fast", classesFast) - .putList("support_slow", supportSlow) - .putList("support_fast", supportFast) - .putList("deps", deps) - .putList("user_classes", cppFiles) - .end(); + of.put("version", 1) + .begin("system") + .put("perl", V3Options::getenvPERL()) + .put("python3", V3Options::getenvPYTHON3()) + .put("verilator_root", verilatorRoot) + .put("verilator_solver", V3Options::getenvVERILATOR_SOLVER()) + .end() + .begin("options") + .putList("cflags", v3Global.opt.cFlags()) + .putList("ldflags", v3Global.opt.ldLibs()) + .put("system_c", v3Global.opt.systemC()) + .put("coverage", v3Global.opt.coverage()) + .put("use_timing", v3Global.usesTiming()) + .put("threads", v3Global.opt.threads()) + .put("trace", v3Global.opt.trace()) + .put("trace_fst", v3Global.opt.traceEnabledFst()) + .put("trace_saif", v3Global.opt.traceEnabledSaif()) + .put("trace_vcd", v3Global.opt.traceEnabledVcd()) + .end() + .begin("sources") + .putList("global", global) + .putList("classes_slow", classesSlow) + .putList("classes_fast", classesFast) + .putList("support_slow", supportSlow) + .putList("support_fast", supportFast) + .putList("deps", deps) + .putList("user_classes", cppFiles) + .end(); if (const V3HierBlockPlan* const planp = v3Global.hierPlanp()) { // Sorted hierarchical blocks in order of leaf-first. const V3HierBlockPlan::HierVector& hierBlocks = planp->hierBlocksSorted(); - cursor += cursor.begin("submodules", '['); + of.begin("submodules", '['); for (V3HierBlockPlan::HierVector::const_iterator it = hierBlocks.begin(); it != hierBlocks.end(); ++it) { @@ -247,17 +140,16 @@ class V3EmitMkJsonEmitter final { std::vector cflags; cflags.emplace_back("-fPIC"); - cursor += cursor.begin() - .put("prefix", hblockp->hierPrefix()) - .put("top", hblockp->modp()->name()) - .putList("deps", childDeps) - .put("directory", makeDir + "/" + hblockp->hierPrefix()) - .putList("sources", sources) - .putList("cflags", cflags) - .put("verilator_args", - V3Os::filenameSlashPath( - V3Os::filenameRealPath(hblockp->commandArgsFilename(true)))) - .end(); + of.begin() + .put("prefix", hblockp->hierPrefix()) + .put("top", hblockp->modp()->name()) + .putList("deps", childDeps) + .put("directory", makeDir + "/" + hblockp->hierPrefix()) + .putList("sources", sources) + .putList("cflags", cflags) + .put("verilator_args", V3Os::filenameSlashPath(V3Os::filenameRealPath( + hblockp->commandArgsFilename(true)))) + .end(); } std::vector sources; @@ -268,15 +160,15 @@ class V3EmitMkJsonEmitter final { for (const string& i : vFiles) sources.emplace_back(V3Os::filenameSlashPath(V3Os::filenameRealPath(i))); - cursor += cursor.begin() - .put("prefix", v3Global.opt.prefix()) - .put("top", v3Global.rootp()->topModulep()->name()) - .put("directory", makeDir) - .putList("sources", sources) - .put("verilator_args", V3Os::filenameSlashPath(V3Os::filenameRealPath( - planp->topCommandArgsFilename(true)))) - .end() - .end(); + of.begin() + .put("prefix", v3Global.opt.prefix()) + .put("top", v3Global.rootp()->topModulep()->name()) + .put("directory", makeDir) + .putList("sources", sources) + .put("verilator_args", V3Os::filenameSlashPath(V3Os::filenameRealPath( + planp->topCommandArgsFilename(true)))) + .end() + .end(); } } diff --git a/src/V3File.h b/src/V3File.h index b76dad9b1..3a28ef92e 100644 --- a/src/V3File.h +++ b/src/V3File.h @@ -109,12 +109,7 @@ class V3OutFormatter VL_NOT_FINAL { static constexpr int MAXSPACE = 80; // After this indent, stop indenting more public: enum AlignClass : uint8_t { AL_AUTO = 0, AL_STATIC = 1 }; - enum Language : uint8_t { - LA_C = 0, - LA_VERILOG = 1, - LA_MK = 2, - LA_XML = 3, - }; + enum Language : uint8_t { LA_C, LA_JSON, LA_MK, LA_VERILOG, LA_XML }; private: // MEMBERS @@ -286,6 +281,119 @@ public: } }; +class V3OutJsonFile final : public V3OutFile { + // CONSTANTS + static constexpr const char* INDENT = " "; // Single indent (4, per JSON std) + + // MEMBERS +private: + std::stack m_scope; // Stack of ']' and '}' to close currently open scopes + std::string m_prefix; // Prefix emitted before each line in current scope + bool m_empty = true; // Current scope is empty, no comma later + +public: + explicit V3OutJsonFile(const string& filename) + : V3OutFile{filename, V3OutFormatter::LA_JSON} { + begin(); + } + ~V3OutJsonFile() override { end(); } + virtual void putsHeader() {} + void puts(const char* strg) { putsNoTracking(strg); } + void puts(const string& strg) { putsNoTracking(strg); } + + // METHODS + V3OutJsonFile& begin(const std::string& name, char type = '{') { + comma(); + puts(m_prefix + "\"" + name + "\": " + type + "\n"); + m_prefix += INDENT; + m_scope.push(type == '{' ? '}' : ']'); + return *this; + } + V3OutJsonFile& begin(char type = '{') { + comma(); + puts(m_prefix + type + "\n"); + m_prefix += INDENT; + m_scope.push(type == '{' ? '}' : ']'); + return *this; + } + + V3OutJsonFile& put(const std::string& name, const std::string& value) { + comma(); + puts(m_prefix + "\"" + name + "\": \"" + value + "\""); + m_empty = false; + return *this; + } + V3OutJsonFile& put(const std::string& name, bool value) { + comma(); + puts(m_prefix + "\"" + name + "\": " + (value ? "true" : "false")); + m_empty = false; + return *this; + } + V3OutJsonFile& put(const std::string& name, int value) { + comma(); + puts(m_prefix + "\"" + name + "\": " + std::to_string(value)); + m_empty = false; + return *this; + } + V3OutJsonFile& put(const std::string& value) { + comma(); + puts(m_prefix + "\"" + value + "\""); + m_empty = false; + return *this; + } + V3OutJsonFile& put(bool value) { + comma(); + puts(m_prefix + (value ? "true" : "false")); + m_empty = false; + return *this; + } + V3OutJsonFile& put(int value) { + comma(); + puts(m_prefix + std::to_string(value)); + m_empty = false; + return *this; + } + + template + V3OutJsonFile& putList(const std::string& name, const T& list) { + if (list.empty()) return *this; + begin(name, '['); + for (auto it = list.begin(); it != list.end(); ++it) put(*it); + return end(); + } + + V3OutJsonFile& end() { + assert(m_prefix.length() >= strlen(INDENT)); + m_prefix.erase(m_prefix.end() - strlen(INDENT), m_prefix.end()); + assert(!m_scope.empty()); + puts("\n" + m_prefix + m_scope.top()); + m_scope.pop(); + return *this; + } + + V3OutJsonFile& operator+=(V3OutJsonFile& cursor) { + // Meaningless syntax sugar, at least for now + return *this; + } + +private: + void comma() { + if (!m_empty) puts(",\n"); + m_empty = true; + } +}; + +class V3OutMkFile final : public V3OutFile { +public: + explicit V3OutMkFile(const string& filename) + : V3OutFile{filename, V3OutFormatter::LA_MK} {} + ~V3OutMkFile() override = default; + virtual void putsHeader() { puts("# Verilated -*- Makefile -*-\n"); } + // No automatic indentation yet. + void puts(const char* strg) { putsNoTracking(strg); } + void puts(const string& strg) { putsNoTracking(strg); } +}; + class V3OutScFile final : public V3OutCFile { public: explicit V3OutScFile(const string& filename) @@ -317,17 +425,6 @@ public: virtual void putsHeader() { puts("\n"); } }; -class V3OutMkFile final : public V3OutFile { -public: - explicit V3OutMkFile(const string& filename) - : V3OutFile{filename, V3OutFormatter::LA_MK} {} - ~V3OutMkFile() override = default; - virtual void putsHeader() { puts("# Verilated -*- Makefile -*-\n"); } - // No automatic indentation yet. - void puts(const char* strg) { putsNoTracking(strg); } - void puts(const string& strg) { putsNoTracking(strg); } -}; - //============================================================================ // VIdProtect: Hash identifier names in output files to protect them