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.
This commit is contained in:
couriersud 2019-10-28 13:44:26 +01:00
parent 88aaa3cafa
commit 97129cece5
10 changed files with 478 additions and 127 deletions

View File

@ -3,3 +3,5 @@ build/obj/*
buildVS/Release/*
buildVS/x64/*
buildVS/.vs/*
nltool
nlwav

View File

@ -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;
}

View File

@ -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);

View File

@ -9,7 +9,6 @@
#include "palloc.h"
#include "putil.h"
//#include <cstdarg>
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<unsigned>(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 <typename PP, typename L = ppreprocessor::string_list>
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<pstring> &sexpr, std::size_t &start, int prio)
template <typename PP>
static int prepro_expr(simple_iter<PP> &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<pstring> &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<pstring> &sexpr, std::size_t &start, i
{
val = plib::pstonum<decltype(val)>(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<tmpret.size())
{
if (tmpret[pi] == "##")
{
while (ret.back() == " " || ret.back() == "\t")
ret.pop_back();
pstring cc = ret.back();
ret.pop_back();
pi++;
while (pi < tmpret.size() && (tmpret[pi] == " " || tmpret[pi] == "\t"))
pi++;
if (pi == tmpret.size())
error("## found at end of sequence");
ret.push_back(cc + tmpret[pi]);
}
else
ret.push_back(tmpret[pi]);
pi++;
}
return ret;
}
}
bool ppreprocessor::is_valid_token(const pstring &str)
{
if (str.length() == 0)
return false;
char c = str.at(0);
return ((c>='a' && c<='z') || (c>='A' && c<='Z') || c == '_');
}
pstring ppreprocessor::replace_macros(const pstring &line)
{
std::vector<pstring> elems(psplit(line, m_expr_sep));
pstring ret("");
for (auto & elem : elems)
//std::vector<pstring> 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<ppreprocessor>(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<ppreprocessor>(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; i<def->m_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<pstring> &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<pstring,bool> 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<pstring> 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<pstring> t(psplit(replace_all(lt.substr(3), " ", ""), m_expr_sep));
auto val = static_cast<int>(expr(t, start, 255));
//std::vector<pstring> t(psplit(replace_all(lt.substr(3), " ", ""), m_expr_sep));
auto t(simple_iter<ppreprocessor>(this, tokenize(lt.substr(3), m_expr_sep, true, true)));
auto val = static_cast<int>(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<ppreprocessor>(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<lti.size() - 1; i++)
arg += lti[i] + " ";
arg += lti[lti.size()-1];
m_defines.insert({lti[1], define_t(lti[1], arg)});
pstring r;
while (!args.eod())
r += args.next_ws();
m_defines.insert({n, define_t(n, r)});
}
}
}
else
{
if (m_if_flag == 0)
error(pfmt("unknown directive on line {1}: {2}")(m_stack.top().m_lineno)(replace_macros(line)));
error("unknown directive");
}
return { "", false };
}
else
{
lt = replace_macros(lt);
if (m_if_flag == 0)
ret += lt;
return { replace_macros(lt), true };
else
return { "", false };
}
return ret;
}
void ppreprocessor::push_out(pstring s)
{
m_outbuf += decltype(m_outbuf)(s.c_str());
if (m_debug_out)
std::cerr << s;
}
void ppreprocessor::process_stack()
{
while (m_stack.size() > 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<char>(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);
}
}
}

View File

@ -16,7 +16,7 @@
//#include <cstdint>
#include <unordered_map>
#include <stack>
#include <vector>
namespace plib {
class ptokenizer
@ -25,13 +25,14 @@ public:
template <typename T>
ptokenizer(T &&strm) // NOLINT(misc-forwarding-reference-overload, bugprone-forwarding-reference-overload)
: m_strm(std::forward<T>(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<plib::source_location> m_source_location;
};
@ -168,13 +169,20 @@ class ppreprocessor : public std::istream
{
public:
using string_list = std::vector<pstring>;
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<pstring, define_t>;
@ -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 <typename T>
ppreprocessor & process(T &&istrm)
{
m_stack.emplace(input_context(std::forward<T>(istrm),"","<stream>"));
m_stack.emplace_back(input_context(std::forward<T>(istrm),"","<stream>"));
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</*pos_type*/long>(bytes);
}
return this->gptr() == this->egptr()
? std::char_traits<char>::eof()
: std::char_traits<char>::to_int_type(*this->gptr());
@ -246,12 +258,8 @@ protected:
ppreprocessor *m_strm;
std::array<char_type, 1024> m_buf;
};
//friend class st;
int expr(const std::vector<pstring> &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<pstring,bool> process_line(pstring line);
pstring process_comments(pstring line);
defines_map_type m_defines;
psource_collection_t<> &m_sources;
std::vector<pstring> 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<input_context> m_stack;
/* vector used as stack because we need to loop through stack */
std::vector<input_context> m_stack;
pstring_t<pu8_traits> m_outbuf;
std::istream::pos_type m_pos;
state_e m_state;
pstring m_line;
bool m_comment;
bool m_debug_out;
};

View File

@ -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<putf8string::traits_type::mem_t *>(&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<putf8string::traits_type::mem_t *>(&b));
return true;

View File

@ -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:

View File

@ -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<pstring> m_options;
std::vector<pstring> 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
{

View File

@ -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;

View File

@ -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;