mirror of
				https://github.com/YosysHQ/yosys
				synced 2025-11-04 05:19:11 +00:00 
			
		
		
		
	Merge pull request #5221 from rocallahan/typed-stringf
Introduce variadic template implementation of `stringf` that supports `std::string` parameters
This commit is contained in:
		
						commit
						1d229ae254
					
				
					 4 changed files with 551 additions and 19 deletions
				
			
		| 
						 | 
					@ -1163,9 +1163,9 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
 | 
				
			||||||
		dump_sigspec(f, cell->getPort(ID::Y));
 | 
							dump_sigspec(f, cell->getPort(ID::Y));
 | 
				
			||||||
		f << stringf(" = ~((");
 | 
							f << stringf(" = ~((");
 | 
				
			||||||
		dump_cell_expr_port(f, cell, "A", false);
 | 
							dump_cell_expr_port(f, cell, "A", false);
 | 
				
			||||||
		f << stringf(cell->type == ID($_AOI3_) ? " & " : " | ");
 | 
							f << (cell->type == ID($_AOI3_) ? " & " : " | ");
 | 
				
			||||||
		dump_cell_expr_port(f, cell, "B", false);
 | 
							dump_cell_expr_port(f, cell, "B", false);
 | 
				
			||||||
		f << stringf(cell->type == ID($_AOI3_) ? ") |" : ") &");
 | 
							f << (cell->type == ID($_AOI3_) ? ") |" : ") &");
 | 
				
			||||||
		dump_attributes(f, "", cell->attributes, " ");
 | 
							dump_attributes(f, "", cell->attributes, " ");
 | 
				
			||||||
		f << stringf(" ");
 | 
							f << stringf(" ");
 | 
				
			||||||
		dump_cell_expr_port(f, cell, "C", false);
 | 
							dump_cell_expr_port(f, cell, "C", false);
 | 
				
			||||||
| 
						 | 
					@ -1178,13 +1178,13 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
 | 
				
			||||||
		dump_sigspec(f, cell->getPort(ID::Y));
 | 
							dump_sigspec(f, cell->getPort(ID::Y));
 | 
				
			||||||
		f << stringf(" = ~((");
 | 
							f << stringf(" = ~((");
 | 
				
			||||||
		dump_cell_expr_port(f, cell, "A", false);
 | 
							dump_cell_expr_port(f, cell, "A", false);
 | 
				
			||||||
		f << stringf(cell->type == ID($_AOI4_) ? " & " : " | ");
 | 
							f << (cell->type == ID($_AOI4_) ? " & " : " | ");
 | 
				
			||||||
		dump_cell_expr_port(f, cell, "B", false);
 | 
							dump_cell_expr_port(f, cell, "B", false);
 | 
				
			||||||
		f << stringf(cell->type == ID($_AOI4_) ? ") |" : ") &");
 | 
							f << (cell->type == ID($_AOI4_) ? ") |" : ") &");
 | 
				
			||||||
		dump_attributes(f, "", cell->attributes, " ");
 | 
							dump_attributes(f, "", cell->attributes, " ");
 | 
				
			||||||
		f << stringf(" (");
 | 
							f << stringf(" (");
 | 
				
			||||||
		dump_cell_expr_port(f, cell, "C", false);
 | 
							dump_cell_expr_port(f, cell, "C", false);
 | 
				
			||||||
		f << stringf(cell->type == ID($_AOI4_) ? " & " : " | ");
 | 
							f << (cell->type == ID($_AOI4_) ? " & " : " | ");
 | 
				
			||||||
		dump_cell_expr_port(f, cell, "D", false);
 | 
							dump_cell_expr_port(f, cell, "D", false);
 | 
				
			||||||
		f << stringf("));\n");
 | 
							f << stringf("));\n");
 | 
				
			||||||
		return true;
 | 
							return true;
 | 
				
			||||||
| 
						 | 
					@ -1395,10 +1395,10 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
 | 
				
			||||||
		int s_width = cell->getPort(ID::S).size();
 | 
							int s_width = cell->getPort(ID::S).size();
 | 
				
			||||||
		std::string func_name = cellname(cell);
 | 
							std::string func_name = cellname(cell);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		f << stringf("%s" "function [%d:0] %s;\n", indent.c_str(), width-1, func_name.c_str());
 | 
							f << stringf("%s" "function [%d:0] %s;\n", indent, width-1, func_name);
 | 
				
			||||||
		f << stringf("%s" "  input [%d:0] a;\n", indent.c_str(), width-1);
 | 
							f << stringf("%s" "  input [%d:0] a;\n", indent, width-1);
 | 
				
			||||||
		f << stringf("%s" "  input [%d:0] b;\n", indent.c_str(), s_width*width-1);
 | 
							f << stringf("%s" "  input [%d:0] b;\n", indent, s_width*width-1);
 | 
				
			||||||
		f << stringf("%s" "  input [%d:0] s;\n", indent.c_str(), s_width-1);
 | 
							f << stringf("%s" "  input [%d:0] s;\n", indent, s_width-1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		dump_attributes(f, indent + "  ", cell->attributes);
 | 
							dump_attributes(f, indent + "  ", cell->attributes);
 | 
				
			||||||
		if (noparallelcase)
 | 
							if (noparallelcase)
 | 
				
			||||||
| 
						 | 
					@ -1407,7 +1407,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
 | 
				
			||||||
			if (!noattr)
 | 
								if (!noattr)
 | 
				
			||||||
				f << stringf("%s" "  (* parallel_case *)\n", indent.c_str());
 | 
									f << stringf("%s" "  (* parallel_case *)\n", indent.c_str());
 | 
				
			||||||
			f << stringf("%s" "  casez (s)", indent.c_str());
 | 
								f << stringf("%s" "  casez (s)", indent.c_str());
 | 
				
			||||||
			f << stringf(noattr ? " // synopsys parallel_case\n" : "\n");
 | 
								f << (noattr ? " // synopsys parallel_case\n" : "\n");
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		for (int i = 0; i < s_width; i++)
 | 
							for (int i = 0; i < s_width; i++)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										149
									
								
								kernel/io.cc
									
										
									
									
									
								
							
							
						
						
									
										149
									
								
								kernel/io.cc
									
										
									
									
									
								
							| 
						 | 
					@ -384,4 +384,153 @@ std::string escape_filename_spaces(const std::string& filename)
 | 
				
			||||||
	return out;
 | 
						return out;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void format_emit_unescaped(std::string &result, std::string_view fmt)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						result.reserve(result.size() + fmt.size());
 | 
				
			||||||
 | 
						for (size_t i = 0; i < fmt.size(); ++i) {
 | 
				
			||||||
 | 
							char ch = fmt[i];
 | 
				
			||||||
 | 
							result.push_back(ch);
 | 
				
			||||||
 | 
							if (ch == '%' && i + 1 < fmt.size() && fmt[i + 1] == '%') {
 | 
				
			||||||
 | 
								++i;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					std::string unescape_format_string(std::string_view fmt)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						std::string result;
 | 
				
			||||||
 | 
						format_emit_unescaped(result, fmt);
 | 
				
			||||||
 | 
						return result;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static std::string string_view_stringf(std::string_view spec, ...)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						std::string fmt(spec);
 | 
				
			||||||
 | 
						char format_specifier = fmt[fmt.size() - 1];
 | 
				
			||||||
 | 
						switch (format_specifier) {
 | 
				
			||||||
 | 
						case 'd':
 | 
				
			||||||
 | 
						case 'i':
 | 
				
			||||||
 | 
						case 'o':
 | 
				
			||||||
 | 
						case 'u':
 | 
				
			||||||
 | 
						case 'x':
 | 
				
			||||||
 | 
						case 'X': {
 | 
				
			||||||
 | 
							// Strip any length modifier off `fmt`
 | 
				
			||||||
 | 
							std::string long_fmt;
 | 
				
			||||||
 | 
							for (size_t i = 0; i + 1 < fmt.size(); ++i) {
 | 
				
			||||||
 | 
								char ch = fmt[i];
 | 
				
			||||||
 | 
								if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) {
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								long_fmt.push_back(ch);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							// Add `lld` or whatever
 | 
				
			||||||
 | 
							long_fmt += "ll";
 | 
				
			||||||
 | 
							long_fmt.push_back(format_specifier);
 | 
				
			||||||
 | 
							fmt = long_fmt;
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						va_list ap;
 | 
				
			||||||
 | 
						va_start(ap, spec);
 | 
				
			||||||
 | 
						std::string result = vstringf(fmt.c_str(), ap);
 | 
				
			||||||
 | 
						va_end(ap);
 | 
				
			||||||
 | 
						return result;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					template <typename Arg>
 | 
				
			||||||
 | 
					static void format_emit_stringf(std::string &result, std::string_view spec, int *dynamic_ints,
 | 
				
			||||||
 | 
						DynamicIntCount num_dynamic_ints, Arg arg)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						// Delegate nontrivial formats to the C library.
 | 
				
			||||||
 | 
						switch (num_dynamic_ints) {
 | 
				
			||||||
 | 
						case DynamicIntCount::NONE:
 | 
				
			||||||
 | 
							result += string_view_stringf(spec, arg);
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						case DynamicIntCount::ONE:
 | 
				
			||||||
 | 
							result += string_view_stringf(spec, dynamic_ints[0], arg);
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						case DynamicIntCount::TWO:
 | 
				
			||||||
 | 
							result += string_view_stringf(spec, dynamic_ints[0], dynamic_ints[1], arg);
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						YOSYS_ABORT("Internal error");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void format_emit_long_long(std::string &result, std::string_view spec, int *dynamic_ints,
 | 
				
			||||||
 | 
						DynamicIntCount num_dynamic_ints, long long arg)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (spec == "%d") {
 | 
				
			||||||
 | 
							// Format checking will have guaranteed num_dynamic_ints == 0.
 | 
				
			||||||
 | 
							result += std::to_string(arg);
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						format_emit_stringf(result, spec, dynamic_ints, num_dynamic_ints, arg);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void format_emit_unsigned_long_long(std::string &result, std::string_view spec, int *dynamic_ints,
 | 
				
			||||||
 | 
						DynamicIntCount num_dynamic_ints, unsigned long long arg)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (spec == "%u") {
 | 
				
			||||||
 | 
							// Format checking will have guaranteed num_dynamic_ints == 0.
 | 
				
			||||||
 | 
							result += std::to_string(arg);
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (spec == "%c") {
 | 
				
			||||||
 | 
							result += static_cast<char>(arg);
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						format_emit_stringf(result, spec, dynamic_ints, num_dynamic_ints, arg);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void format_emit_double(std::string &result, std::string_view spec, int *dynamic_ints,
 | 
				
			||||||
 | 
						DynamicIntCount num_dynamic_ints, double arg)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						format_emit_stringf(result, spec, dynamic_ints, num_dynamic_ints, arg);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void format_emit_char_ptr(std::string &result, std::string_view spec, int *dynamic_ints,
 | 
				
			||||||
 | 
						DynamicIntCount num_dynamic_ints, const char *arg)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (spec == "%s") {
 | 
				
			||||||
 | 
							// Format checking will have guaranteed num_dynamic_ints == 0.
 | 
				
			||||||
 | 
							result += arg;
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						format_emit_stringf(result, spec, dynamic_ints, num_dynamic_ints, arg);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void format_emit_string(std::string &result, std::string_view spec, int *dynamic_ints,
 | 
				
			||||||
 | 
						DynamicIntCount num_dynamic_ints, const std::string &arg)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (spec == "%s") {
 | 
				
			||||||
 | 
							// Format checking will have guaranteed num_dynamic_ints == 0.
 | 
				
			||||||
 | 
							result += arg;
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						format_emit_stringf(result, spec, dynamic_ints, num_dynamic_ints, arg.c_str());
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void format_emit_string_view(std::string &result, std::string_view spec, int *dynamic_ints,
 | 
				
			||||||
 | 
						DynamicIntCount num_dynamic_ints, std::string_view arg)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (spec == "%s") {
 | 
				
			||||||
 | 
							// Format checking will have guaranteed num_dynamic_ints == 0.
 | 
				
			||||||
 | 
							// We can output the string without creating a temporary copy.
 | 
				
			||||||
 | 
							result += arg;
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// Delegate nontrivial formats to the C library. We need to construct
 | 
				
			||||||
 | 
						// a temporary string to ensure null termination.
 | 
				
			||||||
 | 
						format_emit_stringf(result, spec, dynamic_ints, num_dynamic_ints, std::string(arg).c_str());
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void format_emit_void_ptr(std::string &result, std::string_view spec, int *dynamic_ints,
 | 
				
			||||||
 | 
						DynamicIntCount num_dynamic_ints, const void *arg)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						format_emit_stringf(result, spec, dynamic_ints, num_dynamic_ints, arg);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
YOSYS_NAMESPACE_END
 | 
					YOSYS_NAMESPACE_END
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										392
									
								
								kernel/io.h
									
										
									
									
									
								
							
							
						
						
									
										392
									
								
								kernel/io.h
									
										
									
									
									
								
							| 
						 | 
					@ -1,5 +1,6 @@
 | 
				
			||||||
#include <string>
 | 
					#include <string>
 | 
				
			||||||
#include <stdarg.h>
 | 
					#include <stdarg.h>
 | 
				
			||||||
 | 
					#include <type_traits>
 | 
				
			||||||
#include "kernel/yosys_common.h"
 | 
					#include "kernel/yosys_common.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifndef YOSYS_IO_H
 | 
					#ifndef YOSYS_IO_H
 | 
				
			||||||
| 
						 | 
					@ -50,18 +51,391 @@ inline std::string vstringf(const char *fmt, va_list ap)
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
std::string stringf(const char *fmt, ...) YS_ATTRIBUTE(format(printf, 1, 2));
 | 
					enum ConversionSpecifier : uint8_t
 | 
				
			||||||
 | 
					 | 
				
			||||||
inline std::string stringf(const char *fmt, ...)
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	std::string string;
 | 
						CONVSPEC_NONE,
 | 
				
			||||||
	va_list ap;
 | 
						// Specifier not understood/supported
 | 
				
			||||||
 | 
						CONVSPEC_ERROR,
 | 
				
			||||||
 | 
						// Consumes a "long long"
 | 
				
			||||||
 | 
						CONVSPEC_SIGNED_INT,
 | 
				
			||||||
 | 
						// Consumes a "unsigned long long"
 | 
				
			||||||
 | 
						CONVSPEC_UNSIGNED_INT,
 | 
				
			||||||
 | 
						// Consumes a "double"
 | 
				
			||||||
 | 
						CONVSPEC_DOUBLE,
 | 
				
			||||||
 | 
						// Consumes a "const char*"
 | 
				
			||||||
 | 
						CONVSPEC_CHAR_PTR,
 | 
				
			||||||
 | 
						// Consumes a "void*"
 | 
				
			||||||
 | 
						CONVSPEC_VOID_PTR,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	va_start(ap, fmt);
 | 
					constexpr ConversionSpecifier parse_conversion_specifier(char ch, char prev_ch)
 | 
				
			||||||
	string = vstringf(fmt, ap);
 | 
					{
 | 
				
			||||||
	va_end(ap);
 | 
						switch (ch) {
 | 
				
			||||||
 | 
						case 'd':
 | 
				
			||||||
 | 
						case 'i':
 | 
				
			||||||
 | 
							return CONVSPEC_SIGNED_INT;
 | 
				
			||||||
 | 
						case 'o':
 | 
				
			||||||
 | 
						case 'u':
 | 
				
			||||||
 | 
						case 'x':
 | 
				
			||||||
 | 
						case 'X':
 | 
				
			||||||
 | 
						case 'm':
 | 
				
			||||||
 | 
							return CONVSPEC_UNSIGNED_INT;
 | 
				
			||||||
 | 
						case 'c':
 | 
				
			||||||
 | 
							if (prev_ch == 'l' || prev_ch == 'q' || prev_ch == 'L') {
 | 
				
			||||||
 | 
								// wchar not supported
 | 
				
			||||||
 | 
								return CONVSPEC_ERROR;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return CONVSPEC_UNSIGNED_INT;
 | 
				
			||||||
 | 
						case 'e':
 | 
				
			||||||
 | 
						case 'E':
 | 
				
			||||||
 | 
						case 'f':
 | 
				
			||||||
 | 
						case 'F':
 | 
				
			||||||
 | 
						case 'g':
 | 
				
			||||||
 | 
						case 'G':
 | 
				
			||||||
 | 
						case 'a':
 | 
				
			||||||
 | 
						case 'A':
 | 
				
			||||||
 | 
							return CONVSPEC_DOUBLE;
 | 
				
			||||||
 | 
						case 's':
 | 
				
			||||||
 | 
							if (prev_ch == 'l' || prev_ch == 'q' || prev_ch == 'L') {
 | 
				
			||||||
 | 
								// wchar not supported
 | 
				
			||||||
 | 
								return CONVSPEC_ERROR;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return CONVSPEC_CHAR_PTR;
 | 
				
			||||||
 | 
						case 'p':
 | 
				
			||||||
 | 
							return CONVSPEC_VOID_PTR;
 | 
				
			||||||
 | 
						case '$': // positional parameters
 | 
				
			||||||
 | 
						case 'n':
 | 
				
			||||||
 | 
						case 'S':
 | 
				
			||||||
 | 
							return CONVSPEC_ERROR;
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							return CONVSPEC_NONE;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return string;
 | 
					enum class DynamicIntCount : uint8_t {
 | 
				
			||||||
 | 
						NONE = 0,
 | 
				
			||||||
 | 
						ONE = 1,
 | 
				
			||||||
 | 
						TWO = 2,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Describes a printf-style format conversion specifier found in a format string.
 | 
				
			||||||
 | 
					struct FoundFormatSpec
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						// The start offset of the conversion spec in the format string.
 | 
				
			||||||
 | 
						int start;
 | 
				
			||||||
 | 
						// The end offset of the conversion spec in the format string.
 | 
				
			||||||
 | 
						int end;
 | 
				
			||||||
 | 
						ConversionSpecifier spec;
 | 
				
			||||||
 | 
						// Number of int args consumed by '*' dynamic width/precision args.
 | 
				
			||||||
 | 
						DynamicIntCount num_dynamic_ints;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Ensure there is no format spec.
 | 
				
			||||||
 | 
					constexpr void ensure_no_format_spec(std::string_view fmt, int index, bool *has_escapes)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int fmt_size = static_cast<int>(fmt.size());
 | 
				
			||||||
 | 
						// A trailing '%' is not a format spec.
 | 
				
			||||||
 | 
						while (index + 1 < fmt_size) {
 | 
				
			||||||
 | 
							if (fmt[index] != '%') {
 | 
				
			||||||
 | 
								++index;
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if (fmt[index + 1] != '%') {
 | 
				
			||||||
 | 
								YOSYS_ABORT("More format conversion specifiers than arguments");
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							*has_escapes = true;
 | 
				
			||||||
 | 
							index += 2;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Returns the next format conversion specifier (starting with '%').
 | 
				
			||||||
 | 
					// Returns CONVSPEC_NONE if there isn't a format conversion specifier.
 | 
				
			||||||
 | 
					constexpr FoundFormatSpec find_next_format_spec(std::string_view fmt, int fmt_start, bool *has_escapes)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int index = fmt_start;
 | 
				
			||||||
 | 
						int fmt_size = static_cast<int>(fmt.size());
 | 
				
			||||||
 | 
						while (index < fmt_size) {
 | 
				
			||||||
 | 
							if (fmt[index] != '%') {
 | 
				
			||||||
 | 
								++index;
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							int p = index + 1;
 | 
				
			||||||
 | 
							uint8_t num_dynamic_ints = 0;
 | 
				
			||||||
 | 
							while (true) {
 | 
				
			||||||
 | 
								if (p == fmt_size) {
 | 
				
			||||||
 | 
									return {0, 0, CONVSPEC_NONE, DynamicIntCount::NONE};
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if (fmt[p] == '%') {
 | 
				
			||||||
 | 
									*has_escapes = true;
 | 
				
			||||||
 | 
									index = p + 1;
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if (fmt[p] == '*') {
 | 
				
			||||||
 | 
									if (num_dynamic_ints >= 2) {
 | 
				
			||||||
 | 
										return {0, 0, CONVSPEC_ERROR, DynamicIntCount::NONE};
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									++num_dynamic_ints;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								ConversionSpecifier spec = parse_conversion_specifier(fmt[p], fmt[p - 1]);
 | 
				
			||||||
 | 
								if (spec != CONVSPEC_NONE) {
 | 
				
			||||||
 | 
									return {index, p + 1, spec, static_cast<DynamicIntCount>(num_dynamic_ints)};
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								++p;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return {0, 0, CONVSPEC_NONE, DynamicIntCount::NONE};
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					template <typename... Args>
 | 
				
			||||||
 | 
					constexpr typename std::enable_if<sizeof...(Args) == 0>::type
 | 
				
			||||||
 | 
					check_format(std::string_view fmt, int fmt_start, bool *has_escapes, FoundFormatSpec*, DynamicIntCount)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						ensure_no_format_spec(fmt, fmt_start, has_escapes);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Check that the format string `fmt.substr(fmt_start)` is valid for the given type arguments.
 | 
				
			||||||
 | 
					// Fills `specs` with the FoundFormatSpecs found in the format string.
 | 
				
			||||||
 | 
					// `int_args_consumed` is the number of int arguments already consumed to satisfy the
 | 
				
			||||||
 | 
					// dynamic width/precision args for the next format conversion specifier.
 | 
				
			||||||
 | 
					template <typename Arg, typename... Args>
 | 
				
			||||||
 | 
					constexpr void check_format(std::string_view fmt, int fmt_start, bool *has_escapes, FoundFormatSpec* specs,
 | 
				
			||||||
 | 
					        DynamicIntCount int_args_consumed)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						FoundFormatSpec found = find_next_format_spec(fmt, fmt_start, has_escapes);
 | 
				
			||||||
 | 
						if (found.num_dynamic_ints > int_args_consumed) {
 | 
				
			||||||
 | 
							// We need to consume at least one more int for the dynamic
 | 
				
			||||||
 | 
							// width/precision of this format conversion specifier.
 | 
				
			||||||
 | 
							if constexpr (!std::is_convertible_v<Arg, int>) {
 | 
				
			||||||
 | 
								YOSYS_ABORT("Expected dynamic int argument");
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							check_format<Args...>(fmt, fmt_start, has_escapes, specs,
 | 
				
			||||||
 | 
								static_cast<DynamicIntCount>(static_cast<uint8_t>(int_args_consumed) + 1));
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						switch (found.spec) {
 | 
				
			||||||
 | 
						case CONVSPEC_NONE:
 | 
				
			||||||
 | 
							YOSYS_ABORT("Expected format conversion specifier for argument");
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case CONVSPEC_ERROR:
 | 
				
			||||||
 | 
							YOSYS_ABORT("Found unsupported format conversion specifier");
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case CONVSPEC_SIGNED_INT:
 | 
				
			||||||
 | 
							if constexpr (!std::is_convertible_v<Arg, long long>) {
 | 
				
			||||||
 | 
								YOSYS_ABORT("Expected type convertible to signed integer");
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							*specs = found;
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case CONVSPEC_UNSIGNED_INT:
 | 
				
			||||||
 | 
							if constexpr (!std::is_convertible_v<Arg, unsigned long long>) {
 | 
				
			||||||
 | 
								YOSYS_ABORT("Expected type convertible to unsigned integer");
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							*specs = found;
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case CONVSPEC_DOUBLE:
 | 
				
			||||||
 | 
							if constexpr (!std::is_convertible_v<Arg, double>) {
 | 
				
			||||||
 | 
								YOSYS_ABORT("Expected type convertible to double");
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							*specs = found;
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case CONVSPEC_CHAR_PTR:
 | 
				
			||||||
 | 
							if constexpr (!std::is_convertible_v<Arg, const char *> &&
 | 
				
			||||||
 | 
							        !std::is_convertible_v<Arg, const std::string &> &&
 | 
				
			||||||
 | 
								!std::is_convertible_v<Arg, const std::string_view &>) {
 | 
				
			||||||
 | 
								YOSYS_ABORT("Expected type convertible to char *");
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							*specs = found;
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case CONVSPEC_VOID_PTR:
 | 
				
			||||||
 | 
							if constexpr (!std::is_convertible_v<Arg, const void *>) {
 | 
				
			||||||
 | 
								YOSYS_ABORT("Expected pointer type");
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							*specs = found;
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						check_format<Args...>(fmt, found.end, has_escapes, specs + 1, DynamicIntCount::NONE);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Emit the string representation of `arg` that has been converted to a `long long'.
 | 
				
			||||||
 | 
					void format_emit_long_long(std::string &result, std::string_view spec, int *dynamic_ints,
 | 
				
			||||||
 | 
						DynamicIntCount num_dynamic_ints, long long arg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Emit the string representation of `arg` that has been converted to a `unsigned long long'.
 | 
				
			||||||
 | 
					void format_emit_unsigned_long_long(std::string &result, std::string_view spec, int *dynamic_ints,
 | 
				
			||||||
 | 
						DynamicIntCount num_dynamic_ints, unsigned long long arg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Emit the string representation of `arg` that has been converted to a `double'.
 | 
				
			||||||
 | 
					void format_emit_double(std::string &result, std::string_view spec, int *dynamic_ints,
 | 
				
			||||||
 | 
						DynamicIntCount num_dynamic_ints, double arg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Emit the string representation of `arg` that has been converted to a `const char*'.
 | 
				
			||||||
 | 
					void format_emit_char_ptr(std::string &result, std::string_view spec, int *dynamic_ints,
 | 
				
			||||||
 | 
						DynamicIntCount num_dynamic_ints, const char *arg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Emit the string representation of `arg` that has been converted to a `std::string'.
 | 
				
			||||||
 | 
					void format_emit_string(std::string &result, std::string_view spec, int *dynamic_ints,
 | 
				
			||||||
 | 
						DynamicIntCount num_dynamic_ints, const std::string &arg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Emit the string representation of `arg` that has been converted to a `std::string_view'.
 | 
				
			||||||
 | 
					void format_emit_string_view(std::string &result, std::string_view spec, int *dynamic_ints,
 | 
				
			||||||
 | 
						DynamicIntCount num_dynamic_ints, std::string_view arg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Emit the string representation of `arg` that has been converted to a `double'.
 | 
				
			||||||
 | 
					void format_emit_void_ptr(std::string &result, std::string_view spec, int *dynamic_ints,
 | 
				
			||||||
 | 
						DynamicIntCount num_dynamic_ints, const void *arg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Emit the string representation of `arg` according to the given `FoundFormatSpec`,
 | 
				
			||||||
 | 
					// appending it to `result`.
 | 
				
			||||||
 | 
					template <typename Arg>
 | 
				
			||||||
 | 
					inline void format_emit_one(std::string &result, std::string_view fmt, const FoundFormatSpec &ffspec,
 | 
				
			||||||
 | 
						int *dynamic_ints, const Arg& arg)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						std::string_view spec = fmt.substr(ffspec.start, ffspec.end - ffspec.start);
 | 
				
			||||||
 | 
						DynamicIntCount num_dynamic_ints = ffspec.num_dynamic_ints;
 | 
				
			||||||
 | 
						switch (ffspec.spec) {
 | 
				
			||||||
 | 
						case CONVSPEC_SIGNED_INT:
 | 
				
			||||||
 | 
							if constexpr (std::is_convertible_v<Arg, long long>) {
 | 
				
			||||||
 | 
								long long s = arg;
 | 
				
			||||||
 | 
								format_emit_long_long(result, spec, dynamic_ints, num_dynamic_ints, s);
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case CONVSPEC_UNSIGNED_INT:
 | 
				
			||||||
 | 
							if constexpr (std::is_convertible_v<Arg, unsigned long long>) {
 | 
				
			||||||
 | 
								unsigned long long s = arg;
 | 
				
			||||||
 | 
								format_emit_unsigned_long_long(result, spec, dynamic_ints, num_dynamic_ints, s);
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case CONVSPEC_DOUBLE:
 | 
				
			||||||
 | 
							if constexpr (std::is_convertible_v<Arg, double>) {
 | 
				
			||||||
 | 
								double s = arg;
 | 
				
			||||||
 | 
								format_emit_double(result, spec, dynamic_ints, num_dynamic_ints, s);
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case CONVSPEC_CHAR_PTR:
 | 
				
			||||||
 | 
							if constexpr (std::is_convertible_v<Arg, const char *>) {
 | 
				
			||||||
 | 
								const char *s = arg;
 | 
				
			||||||
 | 
								format_emit_char_ptr(result, spec, dynamic_ints, num_dynamic_ints, s);
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if constexpr (std::is_convertible_v<Arg, const std::string &>) {
 | 
				
			||||||
 | 
								const std::string &s = arg;
 | 
				
			||||||
 | 
								format_emit_string(result, spec, dynamic_ints, num_dynamic_ints, s);
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if constexpr (std::is_convertible_v<Arg, const std::string_view &>) {
 | 
				
			||||||
 | 
								const std::string_view &s = arg;
 | 
				
			||||||
 | 
								format_emit_string_view(result, spec, dynamic_ints, num_dynamic_ints, s);
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case CONVSPEC_VOID_PTR:
 | 
				
			||||||
 | 
							if constexpr (std::is_convertible_v<Arg, const void *>) {
 | 
				
			||||||
 | 
								const void *s = arg;
 | 
				
			||||||
 | 
								format_emit_void_ptr(result, spec, dynamic_ints, num_dynamic_ints, s);
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						YOSYS_ABORT("Internal error");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Append the format string `fmt` to `result`, assuming there are no format conversion
 | 
				
			||||||
 | 
					// specifiers other than "%%" and therefore no arguments. Unescape "%%".
 | 
				
			||||||
 | 
					void format_emit_unescaped(std::string &result, std::string_view fmt);
 | 
				
			||||||
 | 
					std::string unescape_format_string(std::string_view fmt);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					inline void format_emit(std::string &result, std::string_view fmt, int fmt_start,
 | 
				
			||||||
 | 
						bool has_escapes, const FoundFormatSpec*, int*, DynamicIntCount)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						fmt = fmt.substr(fmt_start);
 | 
				
			||||||
 | 
						if (has_escapes) {
 | 
				
			||||||
 | 
							format_emit_unescaped(result, fmt);
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							result += fmt;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					// Format `args` according to `fmt` (starting at `fmt_start`) and `specs` and append to `result`.
 | 
				
			||||||
 | 
					// `num_dynamic_ints` in `dynamic_ints[]` have already been collected to provide as
 | 
				
			||||||
 | 
					// dynamic width/precision args for the next format conversion specifier.
 | 
				
			||||||
 | 
					template <typename Arg, typename... Args>
 | 
				
			||||||
 | 
					inline void format_emit(std::string &result, std::string_view fmt, int fmt_start, bool has_escapes,
 | 
				
			||||||
 | 
						const FoundFormatSpec* specs, int *dynamic_ints, DynamicIntCount num_dynamic_ints,
 | 
				
			||||||
 | 
						const Arg &arg, const Args &... args)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (specs->num_dynamic_ints > num_dynamic_ints) {
 | 
				
			||||||
 | 
							// Collect another int for the dynamic width precision/args
 | 
				
			||||||
 | 
							// for the next format conversion specifier.
 | 
				
			||||||
 | 
							if constexpr (std::is_convertible_v<Arg, int>) {
 | 
				
			||||||
 | 
								dynamic_ints[static_cast<uint8_t>(num_dynamic_ints)] = arg;
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								YOSYS_ABORT("Internal error");
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							format_emit(result, fmt, fmt_start, has_escapes, specs, dynamic_ints,
 | 
				
			||||||
 | 
							            static_cast<DynamicIntCount>(static_cast<uint8_t>(num_dynamic_ints) + 1), args...);
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						std::string_view str = fmt.substr(fmt_start, specs->start - fmt_start);
 | 
				
			||||||
 | 
						if (has_escapes) {
 | 
				
			||||||
 | 
							format_emit_unescaped(result, str);
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							result += str;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						format_emit_one(result, fmt, *specs, dynamic_ints, arg);
 | 
				
			||||||
 | 
						format_emit(result, fmt, specs->end, has_escapes, specs + 1, dynamic_ints, DynamicIntCount::NONE, args...);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					template <typename... Args>
 | 
				
			||||||
 | 
					inline std::string format_emit_toplevel(std::string_view fmt, bool has_escapes, const FoundFormatSpec* specs, const Args &... args)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						std::string result;
 | 
				
			||||||
 | 
						int dynamic_ints[2] = { 0, 0 };
 | 
				
			||||||
 | 
						format_emit(result, fmt, 0, has_escapes, specs, dynamic_ints, DynamicIntCount::NONE, args...);
 | 
				
			||||||
 | 
						return result;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					template <>
 | 
				
			||||||
 | 
					inline std::string format_emit_toplevel(std::string_view fmt, bool has_escapes, const FoundFormatSpec*)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (!has_escapes) {
 | 
				
			||||||
 | 
							return std::string(fmt);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return unescape_format_string(fmt);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// This class parses format strings to build a list of `FoundFormatSpecs` in `specs`.
 | 
				
			||||||
 | 
					// When the compiler supports `consteval` (C++20), this parsing happens at compile time and
 | 
				
			||||||
 | 
					// type errors will be reported at compile time. Otherwise the parsing happens at
 | 
				
			||||||
 | 
					// runtime and type errors will trigger an `abort()` at runtime.
 | 
				
			||||||
 | 
					template <typename... Args>
 | 
				
			||||||
 | 
					class FmtString
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						// Implicit conversion from const char * means that users can pass
 | 
				
			||||||
 | 
						// C string constants which are automatically parsed.
 | 
				
			||||||
 | 
						YOSYS_CONSTEVAL FmtString(const char *p) : fmt(p)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							check_format<Args...>(fmt, 0, &has_escapes, specs, DynamicIntCount::NONE);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						std::string format(const Args &... args)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return format_emit_toplevel(fmt, has_escapes, specs, args...);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
						std::string_view fmt;
 | 
				
			||||||
 | 
						bool has_escapes = false;
 | 
				
			||||||
 | 
						FoundFormatSpec specs[sizeof...(Args)] = {};
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					template <typename T> struct WrapType { using type = T; };
 | 
				
			||||||
 | 
					template <typename T> using TypeIdentity = typename WrapType<T>::type;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					template <typename... Args>
 | 
				
			||||||
 | 
					inline std::string stringf(FmtString<TypeIdentity<Args>...> fmt, Args... args)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return fmt.format(args...);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int readsome(std::istream &f, char *s, int n);
 | 
					int readsome(std::istream &f, char *s, int n);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -134,6 +134,15 @@
 | 
				
			||||||
#  define YS_COLD
 | 
					#  define YS_COLD
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef __cpp_consteval
 | 
				
			||||||
 | 
					#define YOSYS_CONSTEVAL consteval
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					// If we can't use consteval we can at least make it constexpr.
 | 
				
			||||||
 | 
					#define YOSYS_CONSTEVAL constexpr
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define YOSYS_ABORT(s) abort()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "kernel/io.h"
 | 
					#include "kernel/io.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
YOSYS_NAMESPACE_BEGIN
 | 
					YOSYS_NAMESPACE_BEGIN
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue