mirror of
https://github.com/holub/mame
synced 2025-04-19 15:11:37 +03:00
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:
parent
88aaa3cafa
commit
97129cece5
2
src/lib/netlist/.gitignore
vendored
2
src/lib/netlist/.gitignore
vendored
@ -3,3 +3,5 @@ build/obj/*
|
||||
buildVS/Release/*
|
||||
buildVS/x64/*
|
||||
buildVS/.vs/*
|
||||
nltool
|
||||
nlwav
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user