mirror of https://github.com/KLayout/klayout.git
WIP: rewriting Spice expression parser to support more functions
This commit is contained in:
parent
b421f1e499
commit
715c7ed282
|
|
@ -38,6 +38,482 @@
|
|||
namespace db
|
||||
{
|
||||
|
||||
// ------------------------------------------------------------------------------------------------------
|
||||
|
||||
SpiceExpressionParser::SpiceExpressionParser (const variables_type *vars)
|
||||
{
|
||||
static variables_type empty_variables;
|
||||
mp_variables = vars ? vars : &empty_variables;
|
||||
}
|
||||
|
||||
// expression syntax taken from ngspice:
|
||||
// https://nmg.gitlab.io/ngspice-manual/circuitdescription/paramparametricnetlists/syntaxofexpressions.html
|
||||
|
||||
static double sqrt_f (double v) { return sqrt (v); }
|
||||
static double sin_f (double v) { return sin (v); }
|
||||
static double cos_f (double v) { return cos (v); }
|
||||
static double tan_f (double v) { return tan (v); }
|
||||
static double sinh_f (double v) { return sinh (v); }
|
||||
static double cosh_f (double v) { return cosh (v); }
|
||||
static double tanh_f (double v) { return tanh (v); }
|
||||
static double asin_f (double v) { return asin (v); }
|
||||
static double acos_f (double v) { return acos (v); }
|
||||
static double atan_f (double v) { return atan (v); }
|
||||
static double asinh_f (double v) { return asinh (v); }
|
||||
static double acosh_f (double v) { return acosh (v); }
|
||||
static double atanh_f (double v) { return atanh (v); }
|
||||
static double exp_f (double v) { return exp (v); }
|
||||
static double ln_f (double v) { return log (v); }
|
||||
static double log_f (double v) { return log10 (v); }
|
||||
static double abs_f (double v) { return abs (v); }
|
||||
static double nint_f (double v) { return round (v); }
|
||||
static double floor_f (double v) { return floor (v); }
|
||||
static double ceil_f (double v) { return ceil (v); }
|
||||
static double sgn_f (double v) { return v == 0.0 ? 0.0 : (v < 0.0 ? -1.0 : 1.0); }
|
||||
static double int_f (double v) { return sgn_f (v) * floor (sgn_f (v) * v); }
|
||||
|
||||
tl::Variant
|
||||
SpiceExpressionParser::eval_func (const std::string &name, const std::vector<tl::Variant> ¶ms, bool * /*status*/) const
|
||||
{
|
||||
double (*f) (double) = 0;
|
||||
|
||||
if (name == "sqrt") { f = sqrt_f; } else
|
||||
if (name == "sin") { f = sin_f; } else
|
||||
if (name == "cos") { f = cos_f; } else
|
||||
if (name == "tan") { f = tan_f; } else
|
||||
if (name == "sinh") { f = sinh_f; } else
|
||||
if (name == "cosh") { f = cosh_f; } else
|
||||
if (name == "tanh") { f = tanh_f; } else
|
||||
if (name == "asin") { f = asin_f; } else
|
||||
if (name == "acos") { f = acos_f; } else
|
||||
if (name == "atan" || name == "arctan") { f = atan_f; } else
|
||||
if (name == "asinh") { f = asinh_f; } else
|
||||
if (name == "acosh") { f = acosh_f; } else
|
||||
if (name == "atanh") { f = atanh_f; } else
|
||||
if (name == "exp") { f = exp_f; } else
|
||||
if (name == "ln") { f = ln_f; } else
|
||||
if (name == "log") { f = log_f; } else
|
||||
if (name == "abs") { f = abs_f; } else
|
||||
if (name == "nint") { f = nint_f; } else
|
||||
if (name == "floor") { f = floor_f; } else
|
||||
if (name == "ceil") { f = ceil_f; } else
|
||||
if (name == "sgn") { f = sgn_f; } else
|
||||
if (name == "int") { f = int_f; }
|
||||
|
||||
if (f != 0) {
|
||||
|
||||
if (params.size () < 1 || ! params.front ().can_convert_to_double ()) {
|
||||
return tl::Variant ();
|
||||
} else {
|
||||
return tl::Variant ((*f) (params.front ().to_double ()));
|
||||
}
|
||||
|
||||
} else if (name == "pwr" || name == "pow") {
|
||||
|
||||
if (params.size () < 2 || ! params [0].can_convert_to_double () || ! params [1].can_convert_to_double ()) {
|
||||
return tl::Variant ();
|
||||
} else {
|
||||
return tl::Variant (pow (params [0].to_double (), params [1].to_double ()));
|
||||
}
|
||||
|
||||
} else if (name == "ternary_fcn") {
|
||||
|
||||
if (params.size () < 3) {
|
||||
return tl::Variant ();
|
||||
} else {
|
||||
return params [0].to_bool () ? params [1] : params [2];
|
||||
}
|
||||
|
||||
} else if (name == "min") {
|
||||
|
||||
if (params.size () < 1) {
|
||||
return tl::Variant ();
|
||||
}
|
||||
|
||||
tl::Variant v = params [0];
|
||||
for (size_t i = 1; i < params.size (); ++i) {
|
||||
if (params [i] < v) {
|
||||
v = params [i];
|
||||
}
|
||||
}
|
||||
return v;
|
||||
|
||||
} else if (name == "max") {
|
||||
|
||||
if (params.size () < 1) {
|
||||
return tl::Variant ();
|
||||
}
|
||||
|
||||
tl::Variant v = params [0];
|
||||
for (size_t i = 1; i < params.size (); ++i) {
|
||||
if (v < params [i]) {
|
||||
v = params [i];
|
||||
}
|
||||
}
|
||||
return v;
|
||||
|
||||
} else {
|
||||
|
||||
return tl::Variant ();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
tl::Variant
|
||||
SpiceExpressionParser::read_atomic_value (tl::Extractor &ex, bool *status) const
|
||||
{
|
||||
double vd = 0.0;
|
||||
std::string var;
|
||||
|
||||
if (ex.test ("-")) {
|
||||
|
||||
tl::Variant v = read_atomic_value (ex, status);
|
||||
if (v.can_convert_to_double ()) {
|
||||
return tl::Variant (-v.to_double ());
|
||||
} else {
|
||||
return tl::Variant ();
|
||||
}
|
||||
|
||||
} else if (ex.test ("!")) {
|
||||
|
||||
tl::Variant v = read_atomic_value (ex, status);
|
||||
return tl::Variant (! v.to_bool ());
|
||||
|
||||
} else if (ex.test ("(")) {
|
||||
|
||||
tl::Variant v = read_tl_expr (ex, status);
|
||||
if (status && !*status) {
|
||||
return tl::Variant ();
|
||||
}
|
||||
if (status) {
|
||||
*status = ex.test (")");
|
||||
} else {
|
||||
ex.expect (")");
|
||||
}
|
||||
return v;
|
||||
|
||||
} else if (ex.try_read (vd)) {
|
||||
|
||||
if (status) {
|
||||
*status = true;
|
||||
}
|
||||
|
||||
double f = 1.0;
|
||||
if (*ex == 't' || *ex == 'T') {
|
||||
f = 1e12;
|
||||
} else if (*ex == 'g' || *ex == 'G') {
|
||||
f = 1e9;
|
||||
} else if (*ex == 'k' || *ex == 'K') {
|
||||
f = 1e3;
|
||||
} else if (*ex == 'm' || *ex == 'M') {
|
||||
f = 1e-3;
|
||||
if (ex.test_without_case ("meg")) {
|
||||
f = 1e6;
|
||||
}
|
||||
} else if (*ex == 'u' || *ex == 'U') {
|
||||
f = 1e-6;
|
||||
} else if (*ex == 'n' || *ex == 'N') {
|
||||
f = 1e-9;
|
||||
} else if (*ex == 'p' || *ex == 'P') {
|
||||
f = 1e-12;
|
||||
} else if (*ex == 'f' || *ex == 'F') {
|
||||
f = 1e-15;
|
||||
} else if (*ex == 'a' || *ex == 'A') {
|
||||
f = 1e-18;
|
||||
}
|
||||
while (*ex && isalpha (*ex)) {
|
||||
++ex;
|
||||
}
|
||||
|
||||
vd *= f;
|
||||
return tl::Variant (vd);
|
||||
|
||||
} else if (ex.try_read_word (var)) {
|
||||
|
||||
if (ex.test ("(")) {
|
||||
|
||||
// a function
|
||||
|
||||
std::vector<tl::Variant> params;
|
||||
if (! ex.test (")")) {
|
||||
while (! ex.at_end ()) {
|
||||
params.push_back (read_tl_expr (ex, status));
|
||||
if (status && !*status) {
|
||||
return tl::Variant ();
|
||||
}
|
||||
if (! ex.test (",")) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (status && ! ex.test (")")) {
|
||||
*status = false;
|
||||
return tl::Variant ();
|
||||
} else {
|
||||
ex.expect (")");
|
||||
}
|
||||
}
|
||||
|
||||
return eval_func (var, params, status);
|
||||
|
||||
} else {
|
||||
|
||||
auto vi = mp_variables->find (tl::to_upper_case (var));
|
||||
if (vi != mp_variables->end ()) {
|
||||
return vi->second;
|
||||
} else {
|
||||
// keep word as string value
|
||||
return tl::Variant (var);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if (status) {
|
||||
*status = false;
|
||||
} else {
|
||||
throw tl::Exception (tl::sprintf (tl::to_string (tr ("Expected number of variable name here: '...%s'")), ex.get ()));
|
||||
}
|
||||
|
||||
return tl::Variant ();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
tl::Variant SpiceExpressionParser::read_pwr_expr (tl::Extractor &ex, bool *status) const
|
||||
{
|
||||
tl::Variant v = read_atomic_value (ex, status);
|
||||
if (status && !*status) {
|
||||
return tl::Variant ();
|
||||
}
|
||||
while (true) {
|
||||
if (ex.test ("**") || ex.test ("^")) {
|
||||
tl::Variant vv = read_atomic_value (ex, status);
|
||||
if (status && !*status) {
|
||||
return tl::Variant ();
|
||||
}
|
||||
if (! v.can_convert_to_double () || ! vv.can_convert_to_double ()) {
|
||||
v = tl::Variant ();
|
||||
} else {
|
||||
v = tl::Variant (pow (v.to_double (), vv.to_double ()));
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
tl::Variant SpiceExpressionParser::read_dot_expr (tl::Extractor &ex, bool *status) const
|
||||
{
|
||||
tl::Variant v = read_pwr_expr (ex, status);
|
||||
if (status && !*status) {
|
||||
return tl::Variant ();
|
||||
}
|
||||
while (true) {
|
||||
if (ex.test ("*")) {
|
||||
tl::Variant vv = read_pwr_expr (ex, status);
|
||||
if (status && !*status) {
|
||||
return tl::Variant ();
|
||||
}
|
||||
if (! v.can_convert_to_double () || ! vv.can_convert_to_double ()) {
|
||||
v = tl::Variant ();
|
||||
} else {
|
||||
v = v.to_double () * vv.to_double ();
|
||||
}
|
||||
} else if (ex.test ("/")) {
|
||||
tl::Variant vv = read_pwr_expr (ex, status);
|
||||
if (status && !*status) {
|
||||
return tl::Variant ();
|
||||
}
|
||||
if (! v.can_convert_to_double () || ! vv.can_convert_to_double ()) {
|
||||
v = tl::Variant ();
|
||||
} else {
|
||||
v = v.to_double () / vv.to_double ();
|
||||
}
|
||||
} else if (ex.test ("%")) {
|
||||
tl::Variant vv = read_pwr_expr (ex, status);
|
||||
if (status && !*status) {
|
||||
return tl::Variant ();
|
||||
}
|
||||
if (! v.can_convert_to_double () || ! vv.can_convert_to_double ()) {
|
||||
v = tl::Variant ();
|
||||
} else {
|
||||
v = tl::Variant ((long int) v.to_double () % (long int) vv.to_double ());
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
tl::Variant SpiceExpressionParser::read_bar_expr (tl::Extractor &ex, bool *status) const
|
||||
{
|
||||
tl::Variant v = read_dot_expr (ex, status);
|
||||
if (status && !*status) {
|
||||
return tl::Variant ();
|
||||
}
|
||||
while (true) {
|
||||
if (ex.test ("+")) {
|
||||
tl::Variant vv = read_dot_expr (ex, status);
|
||||
if (status && !*status) {
|
||||
return tl::Variant ();
|
||||
}
|
||||
if (! v.can_convert_to_double () || ! vv.can_convert_to_double ()) {
|
||||
v = tl::Variant ();
|
||||
} else {
|
||||
v = v.to_double () + vv.to_double ();
|
||||
}
|
||||
} else if (ex.test ("-")) {
|
||||
tl::Variant vv = read_dot_expr (ex, status);
|
||||
if (status && !*status) {
|
||||
return tl::Variant ();
|
||||
}
|
||||
if (! v.can_convert_to_double () || ! vv.can_convert_to_double ()) {
|
||||
v = tl::Variant ();
|
||||
} else {
|
||||
v = v.to_double () - vv.to_double ();
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
tl::Variant SpiceExpressionParser::read_compare_expr (tl::Extractor &ex, bool *status) const
|
||||
{
|
||||
tl::Variant v = read_bar_expr (ex, status);
|
||||
if (status && !*status) {
|
||||
return tl::Variant ();
|
||||
}
|
||||
while (true) {
|
||||
if (ex.test ("==")) {
|
||||
tl::Variant vv = read_bar_expr (ex, status);
|
||||
if (status && !*status) {
|
||||
return tl::Variant ();
|
||||
}
|
||||
v = tl::Variant (v == vv);
|
||||
} else if (ex.test ("!=")) {
|
||||
tl::Variant vv = read_bar_expr (ex, status);
|
||||
if (status && !*status) {
|
||||
return tl::Variant ();
|
||||
}
|
||||
v = tl::Variant (!(v == vv));
|
||||
} else if (ex.test ("<=")) {
|
||||
tl::Variant vv = read_bar_expr (ex, status);
|
||||
if (status && !*status) {
|
||||
return tl::Variant ();
|
||||
}
|
||||
v = tl::Variant (v < vv || v == vv);
|
||||
} else if (ex.test ("<")) {
|
||||
tl::Variant vv = read_bar_expr (ex, status);
|
||||
if (status && !*status) {
|
||||
return tl::Variant ();
|
||||
}
|
||||
v = tl::Variant (v < vv);
|
||||
} else if (ex.test (">=")) {
|
||||
tl::Variant vv = read_bar_expr (ex, status);
|
||||
if (status && !*status) {
|
||||
return tl::Variant ();
|
||||
}
|
||||
v = tl::Variant (vv < v || v == vv);
|
||||
} else if (ex.test (">")) {
|
||||
tl::Variant vv = read_bar_expr (ex, status);
|
||||
if (status && !*status) {
|
||||
return tl::Variant ();
|
||||
}
|
||||
v = tl::Variant (vv < v);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
tl::Variant SpiceExpressionParser::read_logical_op (tl::Extractor &ex, bool *status) const
|
||||
{
|
||||
tl::Variant v = read_compare_expr (ex, status);
|
||||
if (status && !*status) {
|
||||
return tl::Variant ();
|
||||
}
|
||||
while (true) {
|
||||
if (ex.test ("&&")) {
|
||||
tl::Variant vv = read_compare_expr (ex, status);
|
||||
if (status && !*status) {
|
||||
return tl::Variant ();
|
||||
}
|
||||
v = tl::Variant (v.to_bool () && vv.to_bool ());
|
||||
} else if (ex.test ("||")) {
|
||||
tl::Variant vv = read_compare_expr (ex, status);
|
||||
if (status && !*status) {
|
||||
return tl::Variant ();
|
||||
}
|
||||
v = tl::Variant (v.to_bool () && vv.to_bool ());
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
tl::Variant SpiceExpressionParser::read_ternary_op (tl::Extractor &ex, bool *status) const
|
||||
{
|
||||
tl::Variant v = read_logical_op (ex, status);
|
||||
if (status && !*status) {
|
||||
return tl::Variant ();
|
||||
}
|
||||
if (ex.test ("?")) {
|
||||
tl::Variant vv1 = read_logical_op (ex, status);
|
||||
if (status && !*status) {
|
||||
return tl::Variant ();
|
||||
}
|
||||
if (! ex.test (":")) {
|
||||
if (status) {
|
||||
*status = false;
|
||||
} else {
|
||||
ex.expect (":");
|
||||
}
|
||||
}
|
||||
tl::Variant vv2 = read_logical_op (ex, status);
|
||||
if (status && !*status) {
|
||||
return tl::Variant ();
|
||||
}
|
||||
v = v.to_bool () ? vv1 : vv2;
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
tl::Variant SpiceExpressionParser::read_tl_expr (tl::Extractor &ex, bool *status) const
|
||||
{
|
||||
return read_ternary_op (ex, status);
|
||||
}
|
||||
|
||||
tl::Variant SpiceExpressionParser::read (tl::Extractor &ex) const
|
||||
{
|
||||
try {
|
||||
return read_tl_expr (ex, 0);
|
||||
} catch (tl::Exception &error) {
|
||||
throw NetlistSpiceReaderDelegateError (error.msg ());
|
||||
}
|
||||
}
|
||||
|
||||
bool SpiceExpressionParser::try_read (tl::Extractor &ex, tl::Variant &value) const
|
||||
{
|
||||
tl::Extractor ex_saved = ex;
|
||||
|
||||
bool status = false;
|
||||
value = read_tl_expr (ex, &status);
|
||||
if (! status) {
|
||||
ex = ex_saved;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------------------------------
|
||||
|
||||
static const char *allowed_name_chars = "_.:,!+$/&\\#[]|<>";
|
||||
|
|
@ -133,7 +609,7 @@ std::string NetlistSpiceReaderDelegate::translate_net_name (const std::string &n
|
|||
|
||||
void NetlistSpiceReaderDelegate::error (const std::string &msg)
|
||||
{
|
||||
throw SpiceReaderDelegateException (msg);
|
||||
throw NetlistSpiceReaderDelegateError (msg);
|
||||
}
|
||||
|
||||
template <class Cls>
|
||||
|
|
@ -218,156 +694,6 @@ void NetlistSpiceReaderDelegate::parse_element_components (const std::string &s,
|
|||
}
|
||||
}
|
||||
|
||||
double NetlistSpiceReaderDelegate::read_atomic_value (tl::Extractor &ex, const std::map<std::string, double> &variables, bool *status)
|
||||
{
|
||||
double v = 0.0;
|
||||
std::string var;
|
||||
|
||||
if (ex.test ("(")) {
|
||||
|
||||
double v = read_dot_expr (ex, variables, status);
|
||||
if (status && !*status) {
|
||||
return 0.0;
|
||||
}
|
||||
if (status) {
|
||||
*status = ex.test (")");
|
||||
} else {
|
||||
ex.expect (")");
|
||||
}
|
||||
return v;
|
||||
|
||||
} else if (ex.try_read (v)) {
|
||||
|
||||
if (status) {
|
||||
*status = true;
|
||||
}
|
||||
|
||||
double f = 1.0;
|
||||
if (*ex == 't' || *ex == 'T') {
|
||||
f = 1e12;
|
||||
} else if (*ex == 'g' || *ex == 'G') {
|
||||
f = 1e9;
|
||||
} else if (*ex == 'k' || *ex == 'K') {
|
||||
f = 1e3;
|
||||
} else if (*ex == 'm' || *ex == 'M') {
|
||||
f = 1e-3;
|
||||
if (ex.test_without_case ("meg")) {
|
||||
f = 1e6;
|
||||
}
|
||||
} else if (*ex == 'u' || *ex == 'U') {
|
||||
f = 1e-6;
|
||||
} else if (*ex == 'n' || *ex == 'N') {
|
||||
f = 1e-9;
|
||||
} else if (*ex == 'p' || *ex == 'P') {
|
||||
f = 1e-12;
|
||||
} else if (*ex == 'f' || *ex == 'F') {
|
||||
f = 1e-15;
|
||||
} else if (*ex == 'a' || *ex == 'A') {
|
||||
f = 1e-18;
|
||||
}
|
||||
while (*ex && isalpha (*ex)) {
|
||||
++ex;
|
||||
}
|
||||
|
||||
v *= f;
|
||||
return v;
|
||||
|
||||
} else if (ex.try_read_word (var)) {
|
||||
|
||||
auto v = variables.find (tl::to_upper_case (var));
|
||||
if (v != variables.end ()) {
|
||||
if (status) {
|
||||
*status = true;
|
||||
}
|
||||
return v->second;
|
||||
} else if (status) {
|
||||
*status = false;
|
||||
} else {
|
||||
throw tl::Exception (tl::sprintf (tl::to_string (tr ("Undefined parameter '%s'")), var));
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if (status) {
|
||||
*status = false;
|
||||
} else {
|
||||
throw tl::Exception (tl::sprintf (tl::to_string (tr ("Expected number of variable name here: '...%s'")), ex.get ()));
|
||||
}
|
||||
|
||||
return 0.0;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
double NetlistSpiceReaderDelegate::read_bar_expr (tl::Extractor &ex, const std::map<std::string, double> &variables, bool *status)
|
||||
{
|
||||
double v = read_atomic_value (ex, variables, status);
|
||||
if (status && !*status) {
|
||||
return 0.0;
|
||||
}
|
||||
while (true) {
|
||||
if (ex.test ("+")) {
|
||||
double vv = read_atomic_value (ex, variables, status);
|
||||
if (status && !*status) {
|
||||
return 0.0;
|
||||
}
|
||||
v += vv;
|
||||
} else if (ex.test ("-")) {
|
||||
double vv = read_atomic_value (ex, variables, status);
|
||||
if (status && !*status) {
|
||||
return 0.0;
|
||||
}
|
||||
v -= vv;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
double NetlistSpiceReaderDelegate::read_dot_expr (tl::Extractor &ex, const std::map<std::string, double> &variables, bool *status)
|
||||
{
|
||||
double v = read_bar_expr (ex, variables, status);
|
||||
if (status && !*status) {
|
||||
return 0.0;
|
||||
}
|
||||
while (true) {
|
||||
if (ex.test ("*")) {
|
||||
double vv = read_bar_expr (ex, variables, status);
|
||||
if (status && !*status) {
|
||||
return 0.0;
|
||||
}
|
||||
v *= vv;
|
||||
} else if (ex.test ("/")) {
|
||||
double vv = read_bar_expr (ex, variables, status);
|
||||
if (status && !*status) {
|
||||
return 0.0;
|
||||
}
|
||||
v /= vv;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
double NetlistSpiceReaderDelegate::read_value (tl::Extractor &ex, const std::map<std::string, double> &variables)
|
||||
{
|
||||
try {
|
||||
return read_dot_expr (ex, variables, 0);
|
||||
} catch (tl::Exception &error) {
|
||||
throw SpiceReaderDelegateException (error.msg ());
|
||||
}
|
||||
}
|
||||
|
||||
bool NetlistSpiceReaderDelegate::try_read_value (const std::string &s, double &value, const std::map<std::string, double> &variables)
|
||||
{
|
||||
bool status = false;
|
||||
tl::Extractor ex (s.c_str ());
|
||||
value = read_dot_expr (ex, variables, &status);
|
||||
return status;
|
||||
}
|
||||
|
||||
void NetlistSpiceReaderDelegate::parse_element (const std::string &s, const std::string &element, std::string &model, double &value, std::vector<std::string> &nn, std::map<std::string, double> &pv, const std::map<std::string, double> &variables)
|
||||
{
|
||||
parse_element_components (s, nn, pv, variables);
|
||||
|
|
@ -685,6 +1011,39 @@ bool NetlistSpiceReaderDelegate::element (db::Circuit *circuit, const std::strin
|
|||
return true;
|
||||
}
|
||||
|
||||
double
|
||||
NetlistSpiceReaderDelegate::read_value (tl::Extractor &ex, const std::map<std::string, double> &variables)
|
||||
{
|
||||
std::map<std::string, tl::Variant> vvariables;
|
||||
for (auto i = variables.begin (); i != variables.end (); ++i) {
|
||||
vvariables.insert (std::make_pair (i->first, tl::Variant (i->second)));
|
||||
}
|
||||
|
||||
SpiceExpressionParser parser (&vvariables);
|
||||
return parser.read (ex).to_double ();
|
||||
}
|
||||
|
||||
bool
|
||||
NetlistSpiceReaderDelegate::try_read_value (const std::string &s, double &v, const std::map<std::string, double> &variables)
|
||||
{
|
||||
std::map<std::string, tl::Variant> vvariables;
|
||||
for (auto i = variables.begin (); i != variables.end (); ++i) {
|
||||
vvariables.insert (std::make_pair (i->first, tl::Variant (i->second)));
|
||||
}
|
||||
|
||||
SpiceExpressionParser parser (&vvariables);
|
||||
|
||||
tl::Variant vv;
|
||||
tl::Extractor ex (s.c_str ());
|
||||
bool res = parser.try_read (ex, vv);
|
||||
|
||||
if (res) {
|
||||
v = vv.to_double ();
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------------
|
||||
|
||||
class SpiceReaderStream
|
||||
|
|
@ -1121,7 +1480,7 @@ SpiceCircuitDict::read (tl::InputStream &stream)
|
|||
read_card ();
|
||||
}
|
||||
|
||||
} catch (SpiceReaderDelegateException &ex) {
|
||||
} catch (NetlistSpiceReaderDelegateError &ex) {
|
||||
|
||||
// Translate the exception and add a location
|
||||
error (ex.msg ());
|
||||
|
|
@ -1490,7 +1849,7 @@ SpiceNetlistBuilder::build ()
|
|||
build_global_nets ();
|
||||
mp_delegate->finish (mp_netlist);
|
||||
|
||||
} catch (SpiceReaderDelegateException &ex) {
|
||||
} catch (NetlistSpiceReaderDelegateError &ex) {
|
||||
|
||||
// translate the error and add a source location
|
||||
error (ex.msg ());
|
||||
|
|
|
|||
|
|
@ -43,15 +43,32 @@ class DeviceClass;
|
|||
class Device;
|
||||
|
||||
/**
|
||||
* @brief A specific SPICE reader exception that allows attaching location information
|
||||
* @brief A class implementing the expression parser
|
||||
*
|
||||
* This class is exposed mainly for testing purposes.
|
||||
*/
|
||||
class SpiceReaderDelegateException
|
||||
: public tl::Exception
|
||||
class DB_PUBLIC SpiceExpressionParser
|
||||
{
|
||||
public:
|
||||
SpiceReaderDelegateException (const std::string &msg)
|
||||
: tl::Exception (msg)
|
||||
{ }
|
||||
typedef std::map<std::string, tl::Variant> variables_type;
|
||||
|
||||
SpiceExpressionParser (const variables_type *vars);
|
||||
|
||||
tl::Variant read (tl::Extractor &ex) const;
|
||||
bool try_read (tl::Extractor &ex, tl::Variant &v) const;
|
||||
|
||||
private:
|
||||
const variables_type *mp_variables;
|
||||
|
||||
tl::Variant read_atomic_value (tl::Extractor &ex, bool *status) const;
|
||||
tl::Variant read_dot_expr (tl::Extractor &ex, bool *status) const;
|
||||
tl::Variant read_bar_expr (tl::Extractor &ex, bool *status) const;
|
||||
tl::Variant read_pwr_expr (tl::Extractor &ex, bool *status) const;
|
||||
tl::Variant read_compare_expr (tl::Extractor &ex, bool *status) const;
|
||||
tl::Variant read_logical_op (tl::Extractor &ex, bool *status) const;
|
||||
tl::Variant read_ternary_op (tl::Extractor &ex, bool *status) const;
|
||||
tl::Variant read_tl_expr (tl::Extractor &ex, bool *status) const;
|
||||
tl::Variant eval_func (const std::string &name, const std::vector<tl::Variant> ¶ms, bool *status) const;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -161,11 +178,6 @@ public:
|
|||
* @brief Tries to read a value from the extractor (with formula evaluation)
|
||||
*/
|
||||
static bool try_read_value (const std::string &s, double &v, const std::map<std::string, double> &variables);
|
||||
|
||||
private:
|
||||
static double read_atomic_value (tl::Extractor &ex, const std::map<std::string, double> &variables, bool *status);
|
||||
static double read_dot_expr (tl::Extractor &ex, const std::map<std::string, double> &variables, bool *status);
|
||||
static double read_bar_expr (tl::Extractor &ex, const std::map<std::string, double> &variables, bool *status);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Reference in New Issue