From 97129cece5642e2821a06534e9d407b2be1e405d Mon Sep 17 00:00:00 2001 From: couriersud Date: Mon, 28 Oct 2019 13:44:26 +0100 Subject: [PATCH] netlist: Preprocessor enhancements. [Couriersud] The builtin preprocessor now behaves closer to cpp: - supports macro parameters, i.e. define x(a) a - supports stringification, i.e. define x(a) #a - supports concatenation, i.e. define x(a) a ## _ext In addition, error reporting now provides a source context including the include history. --- src/lib/netlist/.gitignore | 2 + src/lib/netlist/nl_parser.cpp | 5 +- src/lib/netlist/nl_parser.h | 2 +- src/lib/netlist/plib/pparser.cpp | 464 +++++++++++++++++++++------ src/lib/netlist/plib/pparser.h | 47 ++- src/lib/netlist/plib/pstream.h | 6 +- src/lib/netlist/plib/putil.h | 51 ++- src/lib/netlist/prg/nltool.cpp | 16 +- src/lib/netlist/tools/nl_convert.cpp | 8 +- src/lib/netlist/tools/nl_convert.h | 4 +- 10 files changed, 478 insertions(+), 127 deletions(-) diff --git a/src/lib/netlist/.gitignore b/src/lib/netlist/.gitignore index 6021502dade..ed125b65632 100644 --- a/src/lib/netlist/.gitignore +++ b/src/lib/netlist/.gitignore @@ -3,3 +3,5 @@ build/obj/* buildVS/Release/* buildVS/x64/* buildVS/.vs/* +nltool +nlwav diff --git a/src/lib/netlist/nl_parser.cpp b/src/lib/netlist/nl_parser.cpp index c0cf595119c..f9fc102abb8 100644 --- a/src/lib/netlist/nl_parser.cpp +++ b/src/lib/netlist/nl_parser.cpp @@ -16,10 +16,9 @@ namespace netlist // A netlist parser // ---------------------------------------------------------------------------------------- -void parser_t::verror(const pstring &msg, int line_num, const pstring &line) +void parser_t::verror(const pstring &msg) { - m_setup.log().fatal("line {1}: error: {2}\n\t\t{3}\n", line_num, - msg, line); + m_setup.log().fatal("{1}", msg); //throw error; } diff --git a/src/lib/netlist/nl_parser.h b/src/lib/netlist/nl_parser.h index 92459c4b87b..310a00a5fb2 100644 --- a/src/lib/netlist/nl_parser.h +++ b/src/lib/netlist/nl_parser.h @@ -45,7 +45,7 @@ namespace netlist /* for debugging messages */ //netlist_state_t &netlist() { return m_setup.netlist(); } - void verror(const pstring &msg, int line_num, const pstring &line) override; + void verror(const pstring &msg) override; private: nl_double eval_param(const token_t &tok); diff --git a/src/lib/netlist/plib/pparser.cpp b/src/lib/netlist/plib/pparser.cpp index abfa524d332..3480a8e3bea 100644 --- a/src/lib/netlist/plib/pparser.cpp +++ b/src/lib/netlist/plib/pparser.cpp @@ -9,7 +9,6 @@ #include "palloc.h" #include "putil.h" -//#include namespace plib { // ---------------------------------------------------------------------------------------- @@ -21,7 +20,6 @@ pstring ptokenizer::currentline_str() return m_cur_line; } - void ptokenizer::skipeol() { pstring::value_type c = getc(); @@ -38,7 +36,6 @@ void ptokenizer::skipeol() } } - pstring::value_type ptokenizer::getc() { if (m_unget != 0) @@ -49,7 +46,7 @@ pstring::value_type ptokenizer::getc() } if (m_px == m_cur_line.end()) { - m_lineno++; + ++m_source_location.back(); if (m_strm.readline(m_cur_line)) m_px = m_cur_line.begin(); else @@ -143,45 +140,54 @@ long ptokenizer::get_number_long() ptokenizer::token_t ptokenizer::get_token() { + token_t ret = get_token_internal(); while (true) { - token_t ret = get_token_internal(); if (ret.is_type(ENDOFFILE)) return ret; - - if (m_support_line_markers && ret.is_type(LINEMARKER)) + else if (m_support_line_markers && ret.is_type(LINEMARKER)) { - // FIXME: do something with information gathered + bool benter(false); + bool bexit(false); + pstring file; + unsigned lineno; + ret = get_token_internal(); if (!ret.is_type(NUMBER)) error(pfmt("Expected line number after line marker but got <{1}>")(ret.str()) ); + lineno = pstonum(ret.str()); ret = get_token_internal(); if (!ret.is_type(STRING)) error(pfmt("Expected file name after line marker but got <{1}>")(ret.str()) ); + file = ret.str(); ret = get_token_internal(); while (ret.is_type(NUMBER)) { + if (ret.str() == "1") + benter = true; + if (ret.str() == "2") + bexit = false; // FIXME: process flags; actually only 1 (file enter) and 2 (after file exit) ret = get_token_internal(); } + if (bexit) // pop the last location + m_source_location.pop_back(); + if (!benter) // new location! + m_source_location.pop_back(); + m_source_location.emplace_back(plib::source_location(file, lineno)); } - - if (ret.is(m_tok_comment_start)) + else if (ret.is(m_tok_comment_start)) { do { ret = get_token_internal(); } while (ret.is_not(m_tok_comment_end)); + ret = get_token_internal(); } else if (ret.is(m_tok_line_comment)) { skipeol(); + ret = get_token_internal(); } -#if 0 - else if (ret.str() == "#") - { - skipeol(); - } -#endif else { return ret; @@ -202,7 +208,7 @@ ptokenizer::token_t ptokenizer::get_token_internal() } } if (m_support_line_markers && c == '#') - return token_t(LINEMARKER); + return token_t(LINEMARKER, "#"); else if (m_number_chars_start.find(c) != pstring::npos) { /* read number while we receive number or identifier chars @@ -274,6 +280,25 @@ ptokenizer::token_t ptokenizer::get_token_internal() } } +void ptokenizer::error(const pstring &errs) +{ + pstring s(""); + pstring trail (" from "); + pstring trail_first("In file included from "); + pstring e = plib::pfmt("{1}:{2}:0: error: {3}\n") + (m_source_location.back().file_name(), m_source_location.back().line(), errs); + m_source_location.pop_back(); + while (m_source_location.size() > 0) + { + if (m_source_location.size() == 1) + trail = trail_first; + s = trail + plib::pfmt("{1}:{2}:0\n")(m_source_location.back().file_name(), m_source_location.back().line()) + s; + m_source_location.pop_back(); + } + verror("\n" + s + e + " " + currentline_str() + "\n"); +} + + // ---------------------------------------------------------------------------------------- // A simple preprocessor // ---------------------------------------------------------------------------------------- @@ -298,12 +323,20 @@ ppreprocessor::ppreprocessor(psource_collection_t<> &sources, defines_map_type * m_expr_sep.emplace_back("||"); m_expr_sep.emplace_back("=="); m_expr_sep.emplace_back(","); + m_expr_sep.emplace_back(";"); + m_expr_sep.emplace_back("."); + m_expr_sep.emplace_back("##"); + m_expr_sep.emplace_back("#"); m_expr_sep.emplace_back(" "); m_expr_sep.emplace_back("\t"); + m_expr_sep.emplace_back("\""); if (defines != nullptr) m_defines = *defines; m_defines.insert({"__PLIB_PREPROCESSOR__", define_t("__PLIB_PREPROCESSOR__", "1")}); + auto idx = m_defines.find("__PREPROCESSOR_DEBUG__"); + m_debug_out = idx != m_defines.end(); + } void ppreprocessor::error(const pstring &err) @@ -311,58 +344,127 @@ void ppreprocessor::error(const pstring &err) pstring s(""); pstring trail (" from "); pstring trail_first("In file included from "); - pstring e = plib::pfmt("PREPRO ERROR: {1}:{2}:0: error: {3}\n") - (m_stack.top().m_name, m_stack.top().m_lineno, err); - m_stack.pop(); + pstring e = plib::pfmt("{1}:{2}:0: error: {3}\n") + (m_stack.back().m_name, m_stack.back().m_lineno, err); + m_stack.pop_back(); while (m_stack.size() > 0) { if (m_stack.size() == 1) trail = trail_first; - s = trail + plib::pfmt("{1}:{2}:0\n")(m_stack.top().m_name, m_stack.top().m_lineno) + s; - m_stack.pop(); + s = trail + plib::pfmt("{1}:{2}:0\n")(m_stack.back().m_name, m_stack.back().m_lineno) + s; + m_stack.pop_back(); } - throw pexception("\n" + s + e); + throw pexception("\n" + s + e + " " + m_line + "\n"); } + + +template +struct simple_iter +{ + simple_iter(PP *parent, const L &tokens) + : m_tokens(tokens), m_parent(parent), m_pos(0) + {} + + /**! skip white space in token list + * + */ + void skip_ws() + { + while (m_pos < m_tokens.size() && (m_tokens[m_pos] == " " || m_tokens[m_pos] == "\t")) + m_pos++; + } + + /**! return next token skipping white space + * + * @return next token + */ + pstring next() + { + skip_ws(); + if (m_pos >= m_tokens.size()) + error("unexpected end of line"); + return m_tokens[m_pos++]; + } + + /**! return next token including white space + * + * @return next token + */ + pstring next_ws() + { + if (m_pos >= m_tokens.size()) + error("unexpected end of line"); + return m_tokens[m_pos++]; + } + + pstring peek_ws() + { + if (m_pos >= m_tokens.size()) + error("unexpected end of line"); + return m_tokens[m_pos]; + } + + pstring last() + { + if (m_pos == 0) + error("no last token at beginning of line"); + if (m_pos > m_tokens.size()) + error("unexpected end of line"); + return m_tokens[m_pos-1]; + } + + bool eod() + { + return (m_pos >= m_tokens.size()); + } + + void error(pstring err) + { + m_parent->error(err); + } +private: + L m_tokens; + PP *m_parent; + std::size_t m_pos; +}; + #define CHECKTOK2(p_op, p_prio) \ else if (tok == # p_op) \ { \ - if (!has_val) \ - { error("parsing error!"); return 1;} \ + if (!has_val) \ + { sexpr.error("parsing error!"); return 1;} \ if (prio < (p_prio)) \ return val; \ - start++; \ - const auto v2 = expr(sexpr, start, (p_prio)); \ + sexpr.next(); \ + const auto v2 = prepro_expr(sexpr, (p_prio)); \ val = (val p_op v2); \ } \ // Operator precedence see https://en.cppreference.com/w/cpp/language/operator_precedence -int ppreprocessor::expr(const std::vector &sexpr, std::size_t &start, int prio) +template +static int prepro_expr(simple_iter &sexpr, int prio) { int val(0); bool has_val(false); - pstring tok=sexpr[start]; + pstring tok=sexpr.peek_ws(); if (tok == "(") { - start++; - val = expr(sexpr, start, /*prio*/ 255); - if (sexpr[start] != ")") - error("parsing error!"); + sexpr.next(); + val = prepro_expr(sexpr, 255); + if (sexpr.next() != ")") + sexpr.error("expected ')'"); has_val = true; - start++; } - while (start < sexpr.size()) + while (!sexpr.eod()) { - tok=sexpr[start]; + tok = sexpr.peek_ws(); if (tok == ")") { if (!has_val) - { - error("parsing error!"); - return 1; // tease compiler - } + sexpr.error("Found ')' but have no value computed"); else return val; } @@ -371,15 +473,12 @@ int ppreprocessor::expr(const std::vector &sexpr, std::size_t &start, i if (prio < 3) { if (!has_val) - { - error("parsing error!"); - return 1; // tease compiler - } + sexpr.error("parsing error!"); else return val; } - start++; - val = !expr(sexpr, start, 3); + sexpr.next(); + val = !prepro_expr(sexpr, 3); has_val = true; } CHECKTOK2(*, 5) @@ -393,16 +492,12 @@ int ppreprocessor::expr(const std::vector &sexpr, std::size_t &start, i { val = plib::pstonum(tok); has_val = true; - start++; + sexpr.next(); } } if (!has_val) - { - error("parsing error!"); - return 1; // tease compiler - } - else - return val; + sexpr.error("No value computed. Empty expression ?"); + return val; } ppreprocessor::define_t *ppreprocessor::get_define(const pstring &name) @@ -411,16 +506,158 @@ ppreprocessor::define_t *ppreprocessor::get_define(const pstring &name) return (idx != m_defines.end()) ? &idx->second : nullptr; } +ppreprocessor::string_list ppreprocessor::tokenize(const pstring &str, + const string_list &sep, bool remove_ws, bool concat) +{ + const pstring STR = "\""; + string_list tmpret; + string_list tmp(psplit(str, sep)); + std::size_t pi(0); + + while (pi < tmp.size()) + { + if (tmp[pi] == STR) + { + pstring s(STR); + pi++; + // FIXME : \" + while (pi < tmp.size() && tmp[pi] != STR) + { + s += tmp[pi]; + pi++; + } + s += STR; + tmpret.push_back(s); + } + else + if (!remove_ws || (tmp[pi] != " " && tmp[pi] != "\t")) + tmpret.push_back(tmp[pi]); + pi++; + } + if (!concat) + return tmpret; + else + { + // FIXME: error if concat at beginning or end + string_list ret; + pi = 0; + while (pi='a' && c<='z') || (c>='A' && c<='Z') || c == '_'); +} + pstring ppreprocessor::replace_macros(const pstring &line) { - std::vector elems(psplit(line, m_expr_sep)); - pstring ret(""); - for (auto & elem : elems) + //std::vector elems(psplit(line, m_expr_sep)); + bool repeat(false); + pstring tmpret(line); + do { - define_t *def = get_define(elem); - ret += (def != nullptr) ? def->m_replace : elem; - } - return ret; + repeat = false; + auto elems(simple_iter(this, tokenize(tmpret, m_expr_sep, false, true))); + tmpret = ""; + while (!elems.eod()) + { + auto token(elems.next_ws()); + define_t *def = get_define(token); + if (def == nullptr) + tmpret += token; + else if (!def->m_has_params) + { + tmpret += def->m_replace; + repeat = true; + } + else + { + token = elems.next(); + if (token != "(") + error("expected '(' in macro expansion of " + def->m_name); + string_list rep; + token = elems.next(); + while (token != ")") + { + pstring par(""); + int pcnt(1); + while (true) + { + if (pcnt==1 && token == ",") + { + token = elems.next(); + break; + } + if (token == "(") + pcnt++; + if (token == ")") + if (--pcnt == 0) + break; + par += token; + token = elems.next(); + } + rep.push_back(par); + } + repeat = true; + if (def->m_params.size() != rep.size()) + error(pfmt("Expected {1} parameters, got {2}")(def->m_params.size(), rep.size())); + auto r(simple_iter(this, tokenize(def->m_replace, m_expr_sep, false, false))); + bool stringify_next = false; + while (!r.eod()) + { + pstring token(r.next()); + if (token == "#") + stringify_next = true; + else if (token != " " && token != "\t") + { + for (std::size_t i=0; im_params.size(); i++) + if (def->m_params[i] == token) + { + if (stringify_next) + { + stringify_next = false; + token = "\"" + rep[i] + "\""; + } + else + token = rep[i]; + break; + } + if (stringify_next) + error("'#' is not followed by a macro parameter"); + tmpret += token; + tmpret += " "; // make sure this is not concatenated with next token + } + else + tmpret += token; + } + } + } + } while (repeat); + + return tmpret; } static pstring catremainder(const std::vector &elems, std::size_t start, const pstring &sep) @@ -476,7 +713,7 @@ pstring ppreprocessor::process_comments(pstring line) return ret; } -pstring ppreprocessor::process_line(pstring line) +std::pair ppreprocessor::process_line(pstring line) { bool line_cont = plib::right(line, 1) == "\\"; if (line_cont) @@ -490,7 +727,7 @@ pstring ppreprocessor::process_line(pstring line) if (line_cont) { m_state = LINE_CONTINUATION; - return ""; + return {"", false}; } else m_state = PROCESS; @@ -498,18 +735,20 @@ pstring ppreprocessor::process_line(pstring line) line = process_comments(m_line); pstring lt = plib::trim(plib::replace_all(line, "\t", " ")); - pstring ret; // FIXME ... revise and extend macro handling if (plib::startsWith(lt, "#")) { - std::vector lti(psplit(lt, " ", true)); + string_list lti(psplit(lt, " ", true)); if (lti[0] == "#if") { m_if_level++; - std::size_t start = 0; lt = replace_macros(lt); - std::vector t(psplit(replace_all(lt.substr(3), " ", ""), m_expr_sep)); - auto val = static_cast(expr(t, start, 255)); + //std::vector t(psplit(replace_all(lt.substr(3), " ", ""), m_expr_sep)); + auto t(simple_iter(this, tokenize(lt.substr(3), m_expr_sep, true, true))); + auto val = static_cast(prepro_expr(t, 255)); + t.skip_ws(); + if (!t.eod()) + error("found unprocessed content at end of line"); if (val == 0) m_if_flag |= (1 << m_if_level); } @@ -548,18 +787,18 @@ pstring ppreprocessor::process_line(pstring line) { arg = arg.substr(1, arg.length() - 2); /* first try local context */ - auto l(plib::util::buildpath({m_stack.top().m_local_path, arg})); + auto l(plib::util::buildpath({m_stack.back().m_local_path, arg})); auto lstrm(m_sources.get_stream<>(l)); if (lstrm) { - m_stack.emplace(input_context(std::move(lstrm), plib::util::path(l), l)); + m_stack.emplace_back(input_context(std::move(lstrm), plib::util::path(l), l)); } else { auto strm(m_sources.get_stream<>(arg)); if (strm) { - m_stack.emplace(input_context(std::move(strm), plib::util::path(arg), arg)); + m_stack.emplace_back(input_context(std::move(strm), plib::util::path(arg), arg)); } else error("include not found:" + arg); @@ -567,6 +806,8 @@ pstring ppreprocessor::process_line(pstring line) } else error("include misspelled:" + arg); + pstring linemarker = pfmt("# {1} \"{2}\" 1\n")(m_stack.back().m_lineno, m_stack.back().m_name); + push_out(linemarker); } } else if (lti[0] == "#pragma") @@ -582,52 +823,93 @@ pstring ppreprocessor::process_line(pstring line) if (m_if_flag == 0) { if (lti.size() < 2) - error("define needs at least one argument: " + line); - else if (lti.size() == 2) - m_defines.insert({lti[1], define_t(lti[1], "")}); + error("define needs at least one argument"); + auto args(simple_iter(this, tokenize(lt.substr(8), m_expr_sep, false, false))); + pstring n = args.next(); + if (!is_valid_token(n)) + error("define expected identifier"); + if (args.next_ws() == "(") + { + define_t def(n); + def.m_has_params = true; + auto token(args.next()); + while (true) + { + if (token == ")") + break; + def.m_params.push_back(token); + token = args.next(); + if (token != "," && token != ")") + error(pfmt("expected , or ), found <{1}>")(token)); + if (token == ",") + token = args.next(); + } + pstring r; + while (!args.eod()) + r += args.next_ws(); + def.m_replace = r; + m_defines.insert({n, def}); + } else { - pstring arg(""); - for (std::size_t i=2; i 0) { pstring line; - pstring linemarker = pfmt("# {1} \"{2}\" 1\n")(m_stack.top().m_lineno, m_stack.top().m_name); - m_outbuf += decltype(m_outbuf)(linemarker.c_str()); - while (m_stack.top().m_reader.readline(line)) + pstring linemarker = pfmt("# {1} \"{2}\"\n")(m_stack.back().m_lineno, m_stack.back().m_name); + push_out(linemarker); + bool last_skipped=false; + while (m_stack.back().m_reader.readline(line)) { - m_stack.top().m_lineno++; - line = process_line(line); - m_outbuf += decltype(m_outbuf)(line.c_str()) + static_cast(10); + m_stack.back().m_lineno++; + auto r(process_line(line)); + if (r.second) + { + if (last_skipped) + push_out(pfmt("# {1} \"{2}\"\n")(m_stack.back().m_lineno, m_stack.back().m_name)); + push_out(r.first + "\n"); + last_skipped = false; + } + else + last_skipped = true; } - m_stack.pop(); + m_stack.pop_back(); if (m_stack.size() > 0) { - linemarker = pfmt("# {1} \"{2}\" 2\n")(m_stack.top().m_lineno, m_stack.top().m_name); - m_outbuf += decltype(m_outbuf)(linemarker.c_str()); + linemarker = pfmt("# {1} \"{2}\" 2\n")(m_stack.back().m_lineno, m_stack.back().m_name); + push_out(linemarker); } } } diff --git a/src/lib/netlist/plib/pparser.h b/src/lib/netlist/plib/pparser.h index ced7e500cad..ed67289dea8 100644 --- a/src/lib/netlist/plib/pparser.h +++ b/src/lib/netlist/plib/pparser.h @@ -16,7 +16,7 @@ //#include #include -#include +#include namespace plib { class ptokenizer @@ -25,13 +25,14 @@ public: template ptokenizer(T &&strm) // NOLINT(misc-forwarding-reference-overload, bugprone-forwarding-reference-overload) : m_strm(std::forward(strm)) - , m_lineno(0) , m_cur_line("") , m_px(m_cur_line.begin()) , m_unget(0) , m_string('"') , m_support_line_markers(true) // FIXME { + // add a first entry to the stack + m_source_location.emplace_back(plib::source_location("Unknown", 0)); } COPYASSIGNMOVE(ptokenizer, delete) @@ -91,8 +92,6 @@ public: pstring m_token; }; - - int currentline_no() { return m_lineno; } pstring currentline_str(); /* tokenizer stuff follows ... */ @@ -127,11 +126,11 @@ public: } token_t get_token_internal(); - void error(const pstring &errs) { verror(errs, currentline_no(), currentline_str()); } + void error(const pstring &errs); putf8_reader &stream() { return m_strm; } protected: - virtual void verror(const pstring &msg, int line_num, const pstring &line) = 0; + virtual void verror(const pstring &msg) = 0; private: void skipeol(); @@ -143,7 +142,6 @@ private: putf8_reader m_strm; - int m_lineno; pstring m_cur_line; pstring::const_iterator m_px; pstring::value_type m_unget; @@ -160,7 +158,10 @@ private: token_id_t m_tok_comment_start; token_id_t m_tok_comment_end; token_id_t m_tok_line_comment; + + /* source locations, vector used as stack because we need to loop through stack */ bool m_support_line_markers; + std::vector m_source_location; }; @@ -168,13 +169,20 @@ class ppreprocessor : public std::istream { public: + using string_list = std::vector; + struct define_t { define_t(const pstring &name, const pstring &replace) - : m_name(name), m_replace(replace) + : m_name(name), m_replace(replace), m_has_params(false) + {} + define_t(const pstring &name) + : m_name(name), m_replace(""), m_has_params(false) {} pstring m_name; pstring m_replace; + bool m_has_params; + string_list m_params; }; using defines_map_type = std::unordered_map; @@ -195,6 +203,7 @@ public: , m_pos(s.m_pos) , m_state(s.m_state) , m_comment(s.m_comment) + , m_debug_out(s.m_debug_out) { } @@ -206,11 +215,13 @@ public: template ppreprocessor & process(T &&istrm) { - m_stack.emplace(input_context(std::forward(istrm),"","")); + m_stack.emplace_back(input_context(std::forward(istrm),"","")); process_stack(); return *this; } + void error(const pstring &err); + protected: class readbuffer : public std::streambuf @@ -237,6 +248,7 @@ protected: m_strm->m_pos += static_cast(bytes); } + return this->gptr() == this->egptr() ? std::char_traits::eof() : std::char_traits::to_int_type(*this->gptr()); @@ -246,12 +258,8 @@ protected: ppreprocessor *m_strm; std::array m_buf; }; - //friend class st; - - int expr(const std::vector &sexpr, std::size_t &start, int prio); define_t *get_define(const pstring &name); pstring replace_macros(const pstring &line); - virtual void error(const pstring &err); private: @@ -261,14 +269,19 @@ private: LINE_CONTINUATION }; + void push_out(pstring s); + void process_stack(); - pstring process_line(pstring line); + string_list tokenize(const pstring &str, const string_list &sep, bool remove_ws, bool concat); + bool is_valid_token(const pstring &str); + + std::pair process_line(pstring line); pstring process_comments(pstring line); defines_map_type m_defines; psource_collection_t<> &m_sources; - std::vector m_expr_sep; + string_list m_expr_sep; std::uint_least64_t m_if_flag; // 31 if levels int m_if_level; @@ -289,12 +302,14 @@ private: pstring m_name; }; - std::stack m_stack; + /* vector used as stack because we need to loop through stack */ + std::vector m_stack; pstring_t m_outbuf; std::istream::pos_type m_pos; state_e m_state; pstring m_line; bool m_comment; + bool m_debug_out; }; diff --git a/src/lib/netlist/plib/pstream.h b/src/lib/netlist/plib/pstream.h index 77d4c82b87f..13903caa9b4 100644 --- a/src/lib/netlist/plib/pstream.h +++ b/src/lib/netlist/plib/pstream.h @@ -84,6 +84,8 @@ public: if (m_strm->eof()) return false; m_strm->read(&b, 1); + if (m_strm->eof()) + return false; return true; } @@ -93,12 +95,14 @@ public: if (m_strm->eof()) return false; m_strm->read(&b[0], 1); + if (m_strm->eof()) + return false; const std::size_t l = putf8string::traits_type::codelen(reinterpret_cast(&b)); for (std::size_t i = 1; i < l; i++) { + m_strm->read(&b[i], 1); if (m_strm->eof()) return false; - m_strm->read(&b[i], 1); } c = putf8string::traits_type::code(reinterpret_cast(&b)); return true; diff --git a/src/lib/netlist/plib/putil.h b/src/lib/netlist/plib/putil.h index 227648b172b..9b1d8ec7f84 100644 --- a/src/lib/netlist/plib/putil.h +++ b/src/lib/netlist/plib/putil.h @@ -22,14 +22,59 @@ #define PSTRINGIFY_HELP(y) # y #define PSTRINGIFY(x) PSTRINGIFY_HELP(x) +// FIXME:: __FUNCTION__ may be not be supported by all compilers. + +#define PSOURCELOC() plib::source_location(__FILE__, __LINE__) namespace plib { - // ---------------------------------------------------------------------------------------- - // A Generic netlist sources implementation - // ---------------------------------------------------------------------------------------- + /**! Source code locations + * + * The c++20 draft for source locations is based on const char * strings. + * It is thus only suitable for c++ source code and not for programmatic + * parsing of files. This class is a replacement dynamic use cases. + * + */ + struct source_location + { + source_location() noexcept + : m_file("unknown"), m_func(m_file), m_line(0), m_col(0) + { } + source_location(pstring file, unsigned line) noexcept + : m_file(file), m_func("unknown"), m_line(line), m_col(0) + { } + + source_location(pstring file, pstring func, unsigned line) noexcept + : m_file(file), m_func(func), m_line(line), m_col(0) + { } + + unsigned line() const noexcept { return m_line; } + unsigned column() const noexcept { return m_col; } + pstring file_name() const noexcept { return m_file; } + pstring function_name() const noexcept { return m_func; } + + source_location &operator ++() + { + ++m_line; + return *this; + } + + private: + pstring m_file; + pstring m_func; + unsigned m_line; + unsigned m_col; + }; + + /**! Base source class + * + * Pure virtual class all other source implementations are based on. + * Sources provide an abstraction to read input from a variety of + * sources, e.g. files, memory, remote locations. + * + */ class psource_t { public: diff --git a/src/lib/netlist/prg/nltool.cpp b/src/lib/netlist/prg/nltool.cpp index f34c83d5382..c94d02a77b6 100644 --- a/src/lib/netlist/prg/nltool.cpp +++ b/src/lib/netlist/prg/nltool.cpp @@ -36,6 +36,7 @@ public: opt_rfolders(*this, "r", "rom", "where to look for data files"), opt_verb(*this, "v", "verbose", "be verbose - this produces lots of output"), opt_quiet(*this, "q", "quiet", "be quiet - no warnings"), + opt_prepro(*this, "", "prepro", "output preprocessor output to stderr"), opt_version(*this, "", "version", "display version and exit"), opt_help(*this, "h", "help", "display help and exit"), @@ -82,6 +83,7 @@ public: plib::option_vec opt_rfolders; plib::option_bool opt_verb; plib::option_bool opt_quiet; + plib::option_bool opt_prepro; plib::option_bool opt_version; plib::option_bool opt_help; plib::option_group opt_grp2; @@ -126,7 +128,7 @@ private: void listdevices(); - std::vector m_options; + std::vector m_defines; }; @@ -385,7 +387,7 @@ void tool_app_t::run() nt.read_netlist(opt_file(), opt_name(), opt_logs(), - m_options, opt_rfolders(), opt_includes()); + m_defines, opt_rfolders(), opt_includes()); nt.reset(); @@ -481,7 +483,7 @@ void tool_app_t::validate() nt.read_netlist(opt_file(), opt_name(), opt_logs(), - m_options, opt_rfolders(), opt_includes()); + m_defines, opt_rfolders(), opt_includes()); } catch (netlist::nl_exception &e) { @@ -515,7 +517,7 @@ void tool_app_t::static_compile() nt.read_netlist(opt_file(), opt_name(), opt_logs(), - m_options, opt_rfolders(), opt_includes()); + m_defines, opt_rfolders(), opt_includes()); // need to reset ... @@ -857,8 +859,10 @@ int tool_app_t::execute() return 0; } - m_options = opt_defines(); - m_options.emplace_back("NLTOOL_VERSION=" PSTRINGIFY(NLTOOL_VERSION)); + m_defines = opt_defines(); + m_defines.emplace_back("NLTOOL_VERSION=" PSTRINGIFY(NLTOOL_VERSION)); + if (opt_prepro()) + m_defines.emplace_back("__PREPROCESSOR_DEBUG__=1"); try { diff --git a/src/lib/netlist/tools/nl_convert.cpp b/src/lib/netlist/tools/nl_convert.cpp index 155bcdb0be0..92142b60904 100644 --- a/src/lib/netlist/tools/nl_convert.cpp +++ b/src/lib/netlist/tools/nl_convert.cpp @@ -477,9 +477,9 @@ nl_convert_eagle_t::tokenizer::tokenizer(nl_convert_eagle_t &convert, plib::putf register_token("("); } -void nl_convert_eagle_t::tokenizer::verror(const pstring &msg, int line_num, const pstring &line) +void nl_convert_eagle_t::tokenizer::verror(const pstring &msg) { - m_convert.out("{} (line {}): {}\n", msg.c_str(), line_num, line.c_str()); + m_convert.out("{}\n", msg); } //FIXME: should accept a stream as well @@ -616,9 +616,9 @@ nl_convert_rinf_t::tokenizer::tokenizer(nl_convert_rinf_t &convert, plib::putf8_ m_tok_END = register_token(".END"); } -void nl_convert_rinf_t::tokenizer::verror(const pstring &msg, int line_num, const pstring &line) +void nl_convert_rinf_t::tokenizer::verror(const pstring &msg) { - m_convert.out("{} (line {}): {}\n", msg.c_str(), line_num, line.c_str()); + m_convert.out("{}\n", msg); } /* token_id_t m_tok_HFA; diff --git a/src/lib/netlist/tools/nl_convert.h b/src/lib/netlist/tools/nl_convert.h index b47c5a82ce1..3b30dc6094e 100644 --- a/src/lib/netlist/tools/nl_convert.h +++ b/src/lib/netlist/tools/nl_convert.h @@ -185,7 +185,7 @@ public: protected: - void verror(const pstring &msg, int line_num, const pstring &line) override; + void verror(const pstring &msg) override; private: nl_convert_eagle_t &m_convert; @@ -223,7 +223,7 @@ public: protected: - void verror(const pstring &msg, int line_num, const pstring &line) override; + void verror(const pstring &msg) override; private: nl_convert_rinf_t &m_convert;