Improve type safety on string->numeric conversions. (nw)

Also fixed an issue with 7497.

./nltool -t 5 -f src/mame/machine/nl_tp1983.cpp -v

now runs again.
This commit is contained in:
couriersud 2019-01-10 00:30:51 +01:00
parent f8d5b95e37
commit 4213a396d8
18 changed files with 212 additions and 148 deletions

View File

@ -74,7 +74,7 @@ namespace netlist
ENTRYX(7485, TTL_7485, "+A0,+A1,+A2,+A3,+B0,+B1,+B2,+B3,+LTIN,+EQIN,+GTIN")
ENTRYX(7490, TTL_7490, "+A,+B,+R1,+R2,+R91,+R92")
ENTRYX(7493, TTL_7493, "+CLKA,+CLKB,+R1,+R2")
ENTRYX(7497, TTL_7497, "+CLK,+STRB,+EN,+UNITY,+CLR,+B0,+B1,+B2,+B3,+B4,+B5")
ENTRYX(7497, TTL_7497, "+CLK,+STRBQ,+ENQ,+UNITYQ,+CLR,+B0,+B1,+B2,+B3,+B4,+B5")
ENTRYX(74107, TTL_74107, "+CLK,+J,+K,+CLRQ")
ENTRYX(74107A, TTL_74107A, "+CLK,+J,+K,+CLRQ")
ENTRYX(74123, TTL_74123, "")

View File

@ -136,7 +136,9 @@ namespace netlist
unsigned long total = 0;
for (unsigned i=0; i<m_size; i++)
{
pati[i] = static_cast<unsigned long>(plib::pstol(pat[i]));
// FIXME: use pstonum_ne
//pati[i] = plib::pstonum<decltype(pati[i])>(pat[i]);
pati[i] = plib::pstonum<unsigned long>(pat[i]);
total += pati[i];
}
netlist_time ttotal = netlist_time::zero();

View File

@ -407,7 +407,8 @@ void truthtable_parser::parse(const std::vector<pstring> &truthtable)
val.set(j);
else
nl_assert_always(outs == "0", "Unknown value (not 0 or 1");
netlist_time t = netlist_time::from_nsec(static_cast<unsigned long>(plib::pstol(plib::trim(times[j]))));
// FIXME: error handling
netlist_time t = netlist_time::from_nsec(plib::pstonum<unsigned long>(plib::trim(times[j])));
uint_least8_t k=0;
while (m_timing_nt[k] != netlist_time::zero() && m_timing_nt[k] != t)
k++;

View File

@ -343,8 +343,8 @@ void netlist_t::start()
auto p = setup().m_param_values.find(d->name() + ".HINT_NO_DEACTIVATE");
if (p != setup().m_param_values.end())
{
//FIXME: turn this into a proper function
auto v = plib::pstod(p->second);;
//FIXME: check for errors ...
double v = plib::pstonum<double>(p->second);;
if (std::abs(v - std::floor(v)) > 1e-6 )
log().fatal(MF_1_HND_VAL_NOT_SUPPORTED, p->second);
d->set_hint_deactivate(v == 0.0);

View File

@ -26,7 +26,6 @@
* linear memory pool. This is based of the assumption that
* due to enhanced locality there will be less cache misses.
* Your mileage may vary.
* This will cause crashes on OSX and thus is ignored on OSX.
*
*/
#define USE_MEMPOOL (0)

View File

@ -403,7 +403,6 @@ nl_double parser_t::eval_param(const token_t tok)
int i;
int f=0;
nl_double ret;
pstring val;
for (i=1; i<6;i++)
if (tok.str() == macs[i])
@ -416,9 +415,10 @@ nl_double parser_t::eval_param(const token_t tok)
}
else
{
val = tok.str();
if (!plib::pstod_ne(val, ret))
error(plib::pfmt("Parameter value <{1}> not double \n")(val));
bool err;
ret = plib::pstonum_ne<nl_double>(tok.str(), err);
if (err)
error(plib::pfmt("Parameter value <{1}> not double \n")(tok.str()));
}
return ret * facs[f];

View File

@ -159,8 +159,9 @@ double setup_t::get_initial_param_val(const pstring &name, const double def)
auto i = m_param_values.find(name);
if (i != m_param_values.end())
{
double vald = 0;
if (!plib::pstod_ne(i->second, vald))
bool err = false;
double vald = plib::pstonum_ne<double>(i->second, err);
if (err)
log().fatal(MF_2_INVALID_NUMBER_CONVERSION_1_2, name, i->second);
return vald;
}
@ -173,8 +174,9 @@ int setup_t::get_initial_param_val(const pstring &name, const int def)
auto i = m_param_values.find(name);
if (i != m_param_values.end())
{
long vald = 0;
if (!plib::pstod_ne(i->second, vald))
bool err;
double vald = plib::pstonum_ne<double>(i->second, err);
if (err)
log().fatal(MF_2_INVALID_NUMBER_CONVERSION_1_2, name, i->second);
if (vald - std::floor(vald) != 0.0)
log().fatal(MF_2_INVALID_NUMBER_CONVERSION_1_2, name, i->second);
@ -872,7 +874,8 @@ nl_double setup_t::model_value(detail::model_map_t &map, const pstring &entity)
}
if (factor != NL_FCONST(1.0))
tmp = plib::left(tmp, tmp.size() - 1);
return plib::pstod(tmp) * factor;
// FIXME: check for errors
return plib::pstonum<nl_double>(tmp) * factor;
}
class logic_family_std_proxy_t : public logic_family_desc_t

View File

@ -69,7 +69,9 @@ void pfunction::compile_postfix(const std::vector<pstring> &inputs,
if (rc.m_cmd != PUSH_INPUT)
{
rc.m_cmd = PUSH_CONST;
if (!plib::pstod_ne(cmd, rc.m_param))
bool err;
rc.m_param = plib::pstonum_ne<decltype(rc.m_param)>(cmd, err);
if (err)
throw plib::pexception(plib::pfmt("nld_function: unknown/misformatted token <{1}> in <{2}>")(cmd)(expr));
stk += 1;
}

View File

@ -46,49 +46,13 @@ namespace plib {
return 0;
}
int option_str_limit::parse(const pstring &argument)
{
if (plib::container::contains(m_limit, argument))
{
m_val = argument;
return 0;
}
else
return 1;
}
int option_bool::parse(const pstring &argument)
{
unused_var(argument);
m_val = true;
return 0;
}
int option_double::parse(const pstring &argument)
{
try
{
m_val = plib::pstod(argument);
return 0;
}
catch (...)
{
return 1;
}
}
int option_long::parse(const pstring &argument)
{
try
{
m_val = plib::pstol(argument);
return 0;
}
catch (...)
{
return 1;
}
}
int option_vec::parse(const pstring &argument)
{
bool err = false;
@ -233,7 +197,7 @@ namespace plib {
if (opt->has_argument())
{
line += "=";
option_str_limit *ol = dynamic_cast<option_str_limit *>(opt);
option_str_limit_base *ol = dynamic_cast<option_str_limit_base *>(opt);
if (ol)
{
for (auto &v : ol->limit())

View File

@ -102,24 +102,52 @@ private:
pstring m_val;
};
class option_str_limit : public option
class option_str_limit_base : public option
{
public:
option_str_limit(options &parent, pstring ashort, pstring along, pstring defval, pstring limit, pstring help)
: option(parent, ashort, along, help, true), m_val(defval)
, m_limit(plib::psplit(limit, ":"))
option_str_limit_base(options &parent, pstring ashort, pstring along, std::vector<pstring> &&limit, pstring help)
: option(parent, ashort, along, help, true)
, m_limit(limit)
{
}
const std::vector<pstring> &limit() const { return m_limit; }
protected:
private:
std::vector<pstring> m_limit;
};
template <typename T>
class option_str_limit : public option_str_limit_base
{
public:
option_str_limit(options &parent, pstring ashort, pstring along, const T &defval, std::vector<pstring> &&limit, pstring help)
: option_str_limit_base(parent, ashort, along, std::move(limit), help), m_val(defval)
{
}
pstring operator ()() { return m_val; }
const std::vector<pstring> &limit() { return m_limit; }
T operator ()() { return m_val; }
pstring as_string() const { return limit()[m_val]; }
protected:
virtual int parse(const pstring &argument) override;
virtual int parse(const pstring &argument) override
{
auto raw = plib::container::indexof(limit(), argument);
if (raw != plib::container::npos)
{
m_val = static_cast<T>(raw);
return 0;
}
else
return 1;
}
private:
pstring m_val;
std::vector<pstring> m_limit;
T m_val;
};
class option_bool : public option
@ -138,36 +166,34 @@ private:
bool m_val;
};
class option_double : public option
template <typename T>
class option_num : public option
{
public:
option_double(options &parent, pstring ashort, pstring along, double defval, pstring help)
: option(parent, ashort, along, help, true), m_val(defval)
option_num(options &parent, pstring ashort, pstring along, T defval,
pstring help,
T minval = std::numeric_limits<T>::min(),
T maxval = std::numeric_limits<T>::max() )
: option(parent, ashort, along, help, true)
, m_val(defval)
, m_min(minval)
, m_max(maxval)
{}
double operator ()() { return m_val; }
T operator ()() { return m_val; }
protected:
virtual int parse(const pstring &argument) override;
virtual int parse(const pstring &argument) override
{
bool err;
m_val = pstonum_ne<T>(argument, err);
return (err ? 1 : (m_val < m_min || m_val > m_max));
}
private:
double m_val;
};
class option_long : public option
{
public:
option_long(options &parent, pstring ashort, pstring along, long defval, pstring help)
: option(parent, ashort, along, help, true), m_val(defval)
{}
long operator ()() { return m_val; }
protected:
virtual int parse(const pstring &argument) override;
private:
long m_val;
T m_val;
T m_min;
T m_max;
};
class option_vec : public option
@ -207,6 +233,17 @@ private:
static pstring split_paragraphs(pstring text, unsigned width, unsigned indent,
unsigned firstline_indent);
template <typename T>
T *getopt_type()
{
for (auto & optbase : m_opts )
{
if (auto opt = dynamic_cast<T *>(optbase))
return opt;
}
return nullptr;
}
option *getopt_short(pstring arg);
option *getopt_long(pstring arg);

View File

@ -122,6 +122,7 @@ pstring ptokenizer::get_identifier_or_number()
return tok.str();
}
// FIXME: combine into template
double ptokenizer::get_number_double()
{
token_t tok = get_token();
@ -129,9 +130,9 @@ double ptokenizer::get_number_double()
{
error(pfmt("Expected a number, got <{1}>")(tok.str()) );
}
double ret = 0.0;
if (!plib::pstod_ne(tok.str(), ret))
bool err;
double ret = plib::pstonum_ne<double>(tok.str(), err);
if (err)
error(pfmt("Expected a number, got <{1}>")(tok.str()) );
return ret;
}
@ -143,8 +144,9 @@ long ptokenizer::get_number_long()
{
error(pfmt("Expected a long int, got <{1}>")(tok.str()) );
}
long ret = 0;
if (!plib::pstol_ne(tok.str(), ret))
bool err;
long ret = plib::pstonum_ne<long>(tok.str(), err);
if (err)
error(pfmt("Expected a long int, got <{1}>")(tok.str()) );
return ret;
}
@ -326,7 +328,8 @@ double ppreprocessor::expr(const std::vector<pstring> &sexpr, std::size_t &start
else
{
tok=sexpr[start];
val = plib::pstod(tok);
// FIXME: error handling
val = plib::pstonum<decltype(val)>(tok);
start++;
}
while (start < sexpr.size())

View File

@ -9,8 +9,10 @@
#include <cstring>
#include <exception>
#include <stdexcept>
#include <iterator>
#include <string>
#include <limits>
#include <type_traits>
// ----------------------------------------------------------------------------------------
@ -518,57 +520,72 @@ namespace plib
return pwstring(std::to_wstring(v));
}
#if (PSTRING_USE_STD_STRING)
inline double pstod(const std::string &str, std::size_t *e = nullptr)
{
return std::stod(str, e);
}
inline long pstol(const std::string &str, std::size_t *e = nullptr, int base = 10)
{
return std::stol(str, e, base);
}
#else
template<typename T>
double pstod(const T &str, std::size_t *e = nullptr)
{
return std::stod(str.cpp_string(), e);
}
template <typename T, typename E = void>
struct pstonum_helper;
template<typename T>
long pstol(const T &str, std::size_t *e = nullptr, int base = 10)
struct pstonum_helper<T, typename std::enable_if<std::is_integral<T>::value
&& std::is_signed<T>::value>::type>
{
return std::stol(str.cpp_string(), e, base);
}
#endif
template <typename S>
long long operator()(const S &arg, std::size_t *idx)
{
return std::stoll(arg, idx);
}
};
template<typename T, typename R>
bool pstol_ne(const T &str, R &ret)
template<typename T>
struct pstonum_helper<T, typename std::enable_if<std::is_integral<T>::value
&& !std::is_signed<T>::value>::type>
{
template <typename S>
unsigned long long operator()(const S &arg, std::size_t *idx)
{
return std::stoull(arg, idx);
}
};
template<typename T>
struct pstonum_helper<T, typename std::enable_if<std::is_floating_point<T>::value>::type>
{
template <typename S>
long double operator()(const S &arg, std::size_t *idx)
{
return std::stold(arg, idx);
}
};
template<typename T, typename S>
T pstonum(const S &arg)
{
decltype(arg.c_str()) cstr = arg.c_str();
std::size_t idx(0);
auto ret = pstonum_helper<T>()(cstr, &idx);
if (ret >= std::numeric_limits<T>::lowest() && ret <= std::numeric_limits<T>::max())
//&& (ret == T(0) || std::abs(ret) >= std::numeric_limits<T>::min() ))
{
if (cstr[idx] != 0)
throw std::invalid_argument(std::string("Continuation after numeric value ends: ") + cstr);
}
else
{
throw std::out_of_range(std::string("Out of range: ") + cstr);
}
return static_cast<T>(ret);
}
template<typename R, typename T>
R pstonum_ne(const T &str, bool &err) noexcept
{
try
{
std::size_t e = 0;
ret = pstol(str, &e);
return str.c_str()[e] == 0;
err = false;
return pstonum<R>(str);
}
catch (...)
{
return false;
}
}
template<typename T, typename R>
bool pstod_ne(const T &str, R &ret)
{
try
{
std::size_t e = 0;
ret = pstod(str, &e);
return str.c_str()[e] == 0;
}
catch (...)
{
return false;
err = true;
return R(0);
}
}

View File

@ -64,6 +64,36 @@ namespace plib
return ret;
}
std::vector<std::string> psplit_r(const std::string &stri,
const std::string &token,
const std::size_t maxsplit)
{
std::string str(stri);
std::vector<std::string> result;
std::size_t splits = 0;
while(str.size())
{
std::size_t index = str.rfind(token);
bool found = index!=std::string::npos;
if (found)
splits++;
if ((splits <= maxsplit || maxsplit == 0) && found)
{
result.push_back(str.substr(index+token.size()));
str = str.substr(0, index);
if (str.size()==0)
result.push_back(str);
}
else
{
result.push_back(str);
str = "";
}
}
return result;
}
std::vector<pstring> psplit(const pstring &str, const std::vector<pstring> &onstrl)
{
pstring col = "";

View File

@ -16,6 +16,11 @@
namespace plib
{
// Avoid unused variable warnings
template<typename... Ts>
inline void unused_var(Ts&&...) {}
namespace util
{
const pstring buildpath(std::initializer_list<pstring> list );

View File

@ -23,7 +23,7 @@ public:
tool_app_t() :
plib::app(),
opt_grp1(*this, "General options", "The following options apply to all commands."),
opt_cmd (*this, "c", "cmd", "run", "run:convert:listdevices:static:header:docheader", "run|convert|listdevices|static|header"),
opt_cmd (*this, "c", "cmd", 0, std::vector<pstring>({"run","convert","listdevices","static","header","docheader"}), "run|convert|listdevices|static|header|docheader"),
opt_file(*this, "f", "file", "-", "file to process (default is stdin)"),
opt_defines(*this, "D", "define", "predefine value as macro, e.g. -Dname=value. If '=value' is omitted predefine it as 1. This option may be specified repeatedly."),
opt_rfolders(*this, "r", "rom", "where to look for data files"),
@ -40,7 +40,7 @@ public:
opt_loadstate(*this,"", "loadstate", "", "load state from file and continue from there"),
opt_savestate(*this,"", "savestate", "", "save state to file at end of run"),
opt_grp4(*this, "Options for convert command", "These options are only used by the convert command."),
opt_type(*this, "y", "type", "spice", "spice:eagle:rinf", "type of file to be converted: spice,eagle,rinf"),
opt_type(*this, "y", "type", 0, std::vector<pstring>({"spice","eagle","rinf"}), "type of file to be converted: spice,eagle,rinf"),
opt_ex1(*this, "nltool -c run -t 3.5 -f nl_examples/cdelay.c -n cap_delay",
"Run netlist \"cap_delay\" from file nl_examples/cdelay.c for 3.5 seconds"),
@ -49,7 +49,7 @@ public:
{}
plib::option_group opt_grp1;
plib::option_str_limit opt_cmd;
plib::option_str_limit<unsigned> opt_cmd;
plib::option_str opt_file;
plib::option_vec opt_defines;
plib::option_vec opt_rfolders;
@ -60,13 +60,13 @@ public:
plib::option_group opt_grp2;
plib::option_str opt_name;
plib::option_group opt_grp3;
plib::option_double opt_ttr;
plib::option_num<double> opt_ttr;
plib::option_vec opt_logs;
plib::option_str opt_inp;
plib::option_str opt_loadstate;
plib::option_str opt_savestate;
plib::option_group opt_grp4;
plib::option_str_limit opt_type;
plib::option_str_limit<unsigned> opt_type;
plib::option_example opt_ex1;
plib::option_example opt_ex2;
@ -706,7 +706,7 @@ int tool_app_t::execute()
try
{
pstring cmd = opt_cmd();
pstring cmd = opt_cmd.as_string();
if (cmd == "listdevices")
listdevices();
else if (cmd == "run")
@ -734,19 +734,19 @@ int tool_app_t::execute()
contents = ostrm.str();
pstring result;
if (opt_type() == "spice")
if (opt_type.as_string() == "spice")
{
nl_convert_spice_t c;
c.convert(contents);
result = c.result();
}
else if (opt_type() == "eagle")
else if (opt_type.as_string() == "eagle")
{
nl_convert_eagle_t c;
c.convert(contents);
result = c.result();
}
else if (opt_type() == "rinf")
else if (opt_type.as_string() == "rinf")
{
nl_convert_rinf_t c;
c.convert(contents);

View File

@ -24,8 +24,8 @@ public:
{}
plib::option_str opt_inp;
plib::option_str opt_out;
plib::option_double opt_amp;
plib::option_long opt_rate;
plib::option_num<double> opt_amp;
plib::option_num<long> opt_rate;
plib::option_bool opt_verb;
plib::option_bool opt_quiet;
plib::option_bool opt_version;

View File

@ -268,7 +268,7 @@ void NETLIB_NAME(solver)::post_start()
// Override log statistics
pstring p = plib::util::environment("NL_STATS", "");
if (p != "")
m_params.m_log_stats = plib::pstol(p);
m_params.m_log_stats = plib::pstonum<decltype(m_params.m_log_stats)>(p);
else
m_params.m_log_stats = m_log_stats();

View File

@ -214,7 +214,7 @@ double nl_convert_base_t::get_sp_val(const pstring &sin)
++p;
pstring val = plib::left(sin, p);
pstring unit = sin.substr(p);
double ret = get_sp_unit(unit) * plib::pstod(val);
double ret = get_sp_unit(unit) * plib::pstonum<double>(val);
return ret;
}
@ -304,11 +304,12 @@ void nl_convert_spice_t::process_line(const pstring &line)
/* check for fourth terminal ... should be numeric net
* including "0" or start with "N" (ltspice)
*/
long nval = 0;
pstring model;
pstring pins ="CBE";
bool err;
ATTR_UNUSED long nval = plib::pstonum_ne<long>(tt[4], err);
if ((!plib::pstol_ne(tt[4], nval) || plib::startsWith(tt[4], "N")) && tt.size() > 5)
if ((err || plib::startsWith(tt[4], "N")) && tt.size() > 5)
model = tt[5];
else
model = tt[4];
@ -492,7 +493,7 @@ void nl_convert_eagle_t::convert(const pstring &contents)
else if (plib::ucase(sval) == "LOW")
add_device("TTL_INPUT", name, 0);
else
add_device("ANALOG_INPUT", name, plib::pstod(sval));
add_device("ANALOG_INPUT", name, plib::pstonum<double>(sval));
add_pin_alias(name, "1", "Q");
break;
case 'D':