mirror of https://github.com/KLayout/klayout.git
WIP
This commit is contained in:
parent
715c7ed282
commit
51c4b7ed28
|
|
@ -60,6 +60,8 @@ SOURCES = \
|
|||
dbNetlistCompareCore.cc \
|
||||
dbNetlistCompareGraph.cc \
|
||||
dbNetlistCompareUtils.cc \
|
||||
dbNetlistSpiceReaderDelegate.cc \
|
||||
dbNetlistSpiceReaderExpressionParser.cc \
|
||||
dbObject.cc \
|
||||
dbPath.cc \
|
||||
dbPCellDeclaration.cc \
|
||||
|
|
@ -279,6 +281,8 @@ HEADERS = \
|
|||
dbNetlistCompareCore.h \
|
||||
dbNetlistCompareGraph.h \
|
||||
dbNetlistCompareUtils.h \
|
||||
dbNetlistSpiceReaderDelegate.h \
|
||||
dbNetlistSpiceReaderExpressionParser.h \
|
||||
dbObject.h \
|
||||
dbObjectTag.h \
|
||||
dbObjectWithProperties.h \
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -26,50 +26,13 @@
|
|||
#include "dbCommon.h"
|
||||
#include "dbNetlistReader.h"
|
||||
#include "tlStream.h"
|
||||
|
||||
#include <string>
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <list>
|
||||
#include "tlObject.h"
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
class Netlist;
|
||||
class Net;
|
||||
class Circuit;
|
||||
class DeviceClass;
|
||||
class Device;
|
||||
|
||||
/**
|
||||
* @brief A class implementing the expression parser
|
||||
*
|
||||
* This class is exposed mainly for testing purposes.
|
||||
*/
|
||||
class DB_PUBLIC SpiceExpressionParser
|
||||
{
|
||||
public:
|
||||
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;
|
||||
};
|
||||
class NetlistSpiceReaderDelegate;
|
||||
|
||||
/**
|
||||
* @brief A specialized exception class to handle netlist reader delegate errors
|
||||
|
|
@ -83,103 +46,6 @@ public:
|
|||
{ }
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A delegate to handle various forms of devices and translates them
|
||||
*
|
||||
* The reader delegate can be configured to receive subcircuit elements too.
|
||||
* In this case, parameters are allowed.
|
||||
* For receiving subcircuit elements, the delegate needs to indicate
|
||||
* this by returning true upon "wants_subcircuit".
|
||||
*/
|
||||
class DB_PUBLIC NetlistSpiceReaderDelegate
|
||||
: public tl::Object
|
||||
{
|
||||
public:
|
||||
NetlistSpiceReaderDelegate ();
|
||||
virtual ~NetlistSpiceReaderDelegate ();
|
||||
|
||||
/**
|
||||
* @brief Called when the netlist reading starts
|
||||
*/
|
||||
virtual void start (db::Netlist *netlist);
|
||||
|
||||
/**
|
||||
* @brief Called when the netlist reading ends
|
||||
*/
|
||||
virtual void finish (db::Netlist *netlist);
|
||||
|
||||
/**
|
||||
* @brief Called when an unknown control statement is encountered
|
||||
*
|
||||
* Returns true if the statement is understood.
|
||||
*/
|
||||
virtual bool control_statement (const std::string &line);
|
||||
|
||||
/**
|
||||
* @brief Returns true, if the delegate wants subcircuit elements with this name
|
||||
*
|
||||
* The name is always upper case.
|
||||
*/
|
||||
virtual bool wants_subcircuit (const std::string &circuit_name);
|
||||
|
||||
/**
|
||||
* @brief This method translates a raw net name to a valid net name
|
||||
*
|
||||
* The default implementation will unescape backslash sequences into plain characters.
|
||||
*/
|
||||
virtual std::string translate_net_name (const std::string &nn);
|
||||
|
||||
/**
|
||||
* @brief Makes a device from an element line
|
||||
*
|
||||
* @param circuit The circuit that is currently read.
|
||||
* @param element The upper-case element code ("M", "R", ...).
|
||||
* @param name The element's name.
|
||||
* @param model The upper-case model name (may be empty).
|
||||
* @param value The default value (e.g. resistance for resistors) and may be zero.
|
||||
* @param nets The nets given in the element line.
|
||||
* @param parameters The parameters of the element statement (parameter names are upper case).
|
||||
*
|
||||
* The default implementation will create corresponding devices for
|
||||
* some known elements using the Spice writer's parameter conventions.
|
||||
*
|
||||
* This method returns true, if the element was read.
|
||||
*/
|
||||
virtual bool element (db::Circuit *circuit, const std::string &element, const std::string &name, const std::string &model, double value, const std::vector<db::Net *> &nets, const std::map<std::string, double> ¶ms);
|
||||
|
||||
/**
|
||||
* @brief Parses an element from a line
|
||||
*
|
||||
* @param s The line to parse (the part after the element and name)
|
||||
* @param model Out parameter: the model name if given
|
||||
* @param value Out parameter: the value if given (for R, L, C)
|
||||
* @param nn Out parameter: the net names
|
||||
* @param pv Out parameter: the parameter values (key/value pairs)
|
||||
*/
|
||||
virtual void 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> ¶ms);
|
||||
|
||||
/**
|
||||
* @brief Produces an error with the given message
|
||||
*/
|
||||
virtual void error (const std::string &msg);
|
||||
|
||||
/**
|
||||
* @brief Reads a set of string components and parameters from the string
|
||||
* A special key "param:" is recognized for starting a parameter list.
|
||||
*/
|
||||
static void parse_element_components (const std::string &s, std::vector<std::string> &strings, std::map<std::string, double> &pv, const std::map<std::string, double> &variables);
|
||||
|
||||
/**
|
||||
* @brief Reads a value from the extractor (with formula evaluation)
|
||||
*/
|
||||
static double read_value (tl::Extractor &ex, const std::map<std::string, double> &variables);
|
||||
|
||||
/**
|
||||
* @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);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A SPICE format reader for netlists
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -0,0 +1,561 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2023 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
#include "dbNetlistSpiceReaderDelegate.h"
|
||||
#include "dbNetlistSpiceReader.h"
|
||||
#include "dbNetlistSpiceReaderExpressionParser.h"
|
||||
#include "dbNetlist.h"
|
||||
#include "dbCircuit.h"
|
||||
#include "dbNetlistDeviceClasses.h"
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
// ------------------------------------------------------------------------------------------------------
|
||||
|
||||
inline static int hex_num (char c)
|
||||
{
|
||||
if (c >= '0' && c <= '9') {
|
||||
return (int (c - '0'));
|
||||
} else if (c >= 'a' && c <= 'f') {
|
||||
return (int (c - 'f') + 10);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static std::string unescape_name (const std::string &n)
|
||||
{
|
||||
std::string nn;
|
||||
nn.reserve (n.size ());
|
||||
|
||||
const char *cp = n.c_str ();
|
||||
while (*cp) {
|
||||
|
||||
if (*cp == '\\' && cp[1]) {
|
||||
|
||||
if (tolower (cp[1]) == 'x') {
|
||||
|
||||
cp += 2;
|
||||
|
||||
char c = 0;
|
||||
for (int i = 0; i < 2 && *cp; ++i) {
|
||||
int n = hex_num (*cp);
|
||||
if (n >= 0) {
|
||||
++cp;
|
||||
c = c * 16 + char (n);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
nn += c;
|
||||
|
||||
} else {
|
||||
++cp;
|
||||
nn += *cp++;
|
||||
}
|
||||
|
||||
} else {
|
||||
nn += *cp++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nn;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------------
|
||||
|
||||
NetlistSpiceReaderDelegate::NetlistSpiceReaderDelegate ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
NetlistSpiceReaderDelegate::~NetlistSpiceReaderDelegate ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
void NetlistSpiceReaderDelegate::start (db::Netlist * /*netlist*/)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
void NetlistSpiceReaderDelegate::finish (db::Netlist * /*netlist*/)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
bool NetlistSpiceReaderDelegate::control_statement(const std::string & /*line*/)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NetlistSpiceReaderDelegate::wants_subcircuit (const std::string & /*circuit_name*/)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string NetlistSpiceReaderDelegate::translate_net_name (const std::string &nn)
|
||||
{
|
||||
return unescape_name (nn);
|
||||
}
|
||||
|
||||
void NetlistSpiceReaderDelegate::error (const std::string &msg)
|
||||
{
|
||||
throw NetlistSpiceReaderDelegateError (msg);
|
||||
}
|
||||
|
||||
template <class Cls>
|
||||
static db::DeviceClass *make_device_class (db::Circuit *circuit, const std::string &name)
|
||||
{
|
||||
if (! circuit || ! circuit->netlist ()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
db::DeviceClass *cls = circuit->netlist ()->device_class_by_name (name);
|
||||
if (! cls) {
|
||||
cls = new Cls ();
|
||||
cls->set_name (name);
|
||||
circuit->netlist ()->add_device_class (cls);
|
||||
}
|
||||
|
||||
return cls;
|
||||
}
|
||||
|
||||
static std::string parse_component (tl::Extractor &ex)
|
||||
{
|
||||
const char *cp = ex.skip ();
|
||||
const char *cp0 = cp;
|
||||
|
||||
char quote = 0;
|
||||
unsigned int brackets = 0;
|
||||
|
||||
while (*cp) {
|
||||
if (quote) {
|
||||
if (*cp == quote) {
|
||||
quote = 0;
|
||||
} else if (*cp == '\\' && cp[1]) {
|
||||
++cp;
|
||||
}
|
||||
} else if ((isspace (*cp) || *cp == '=') && ! brackets) {
|
||||
break;
|
||||
} else if (*cp == '"' || *cp == '\'') {
|
||||
quote = *cp;
|
||||
} else if (*cp == '(') {
|
||||
++brackets;
|
||||
} else if (*cp == ')') {
|
||||
if (brackets > 0) {
|
||||
--brackets;
|
||||
}
|
||||
}
|
||||
++cp;
|
||||
}
|
||||
|
||||
ex = tl::Extractor (cp);
|
||||
return std::string (cp0, cp - cp0);
|
||||
}
|
||||
|
||||
void NetlistSpiceReaderDelegate::parse_element_components (const std::string &s, std::vector<std::string> &strings, std::map<std::string, double> &pv, const std::map<std::string, double> &variables)
|
||||
{
|
||||
tl::Extractor ex (s.c_str ());
|
||||
bool in_params = false;
|
||||
|
||||
while (! ex.at_end ()) {
|
||||
|
||||
if (ex.test_without_case ("params:")) {
|
||||
|
||||
in_params = true;
|
||||
|
||||
} else {
|
||||
|
||||
tl::Extractor ex0 = ex;
|
||||
std::string n;
|
||||
|
||||
if (ex.try_read_word (n) && ex.test ("=")) {
|
||||
// a parameter. Note that parameter names are always made upper case.
|
||||
pv.insert (std::make_pair (tl::to_upper_case (n), read_value (ex, variables)));
|
||||
} else {
|
||||
ex = ex0;
|
||||
if (in_params) {
|
||||
ex.error (tl::to_string (tr ("Invalid syntax for parameter assignment - needs keyword followed by '='")));
|
||||
}
|
||||
strings.push_back (parse_component (ex));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
// interpret the parameters according to the code
|
||||
if (element == "X") {
|
||||
|
||||
// subcircuit call:
|
||||
// Xname n1 n2 ... nn circuit [params]
|
||||
|
||||
if (nn.empty ()) {
|
||||
error (tl::to_string (tr ("No circuit name given for subcircuit call")));
|
||||
}
|
||||
|
||||
model = nn.back ();
|
||||
nn.pop_back ();
|
||||
|
||||
} else if (element == "R" || element == "C" || element == "L") {
|
||||
|
||||
// resistor, cap, inductor: two-terminal devices with a value
|
||||
// Rname n1 n2 value
|
||||
// Rname n1 n2 n3 value
|
||||
// Rname n1 n2 value model [params]
|
||||
// Rname n1 n2 n3 value model [params]
|
||||
// Rname n1 n2 [params]
|
||||
// Rname n1 n2 model [params]
|
||||
// Rname n1 n2 n3 model [params]
|
||||
// NOTE: there is no "Rname n1 n2 n3 [params]"!
|
||||
// (same for C, L instead of R)
|
||||
|
||||
if (nn.size () < 2) {
|
||||
error (tl::to_string (tr ("Not enough specs for a R, C or L device")));
|
||||
}
|
||||
|
||||
std::map<std::string, double>::const_iterator rv = pv.find (element);
|
||||
if (rv != pv.end ()) {
|
||||
|
||||
// value given by parameter
|
||||
value = rv->second;
|
||||
|
||||
if (nn.size () >= 3) {
|
||||
// Rname n1 n2 model [params]
|
||||
// Rname n1 n2 n3 model [params]
|
||||
model = nn.back ();
|
||||
nn.pop_back ();
|
||||
}
|
||||
|
||||
} else if (nn.size () >= 3) {
|
||||
|
||||
if (try_read_value (nn.back (), value, variables)) {
|
||||
|
||||
// Rname n1 n2 value
|
||||
// Rname n1 n2 n3 value
|
||||
nn.pop_back ();
|
||||
|
||||
} else {
|
||||
|
||||
// Rname n1 n2 value model [params]
|
||||
// Rname n1 n2 n3 value model [params]
|
||||
model = nn.back ();
|
||||
nn.pop_back ();
|
||||
if (! try_read_value (nn.back (), value, variables)) {
|
||||
error (tl::to_string (tr ("Can't find a value for a R, C or L device")));
|
||||
} else {
|
||||
nn.pop_back ();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// others: n-terminal devices with a model (last node)
|
||||
|
||||
if (nn.empty ()) {
|
||||
error (tl::sprintf (tl::to_string (tr ("No model name given for element '%s'")), element));
|
||||
}
|
||||
|
||||
model = nn.back ();
|
||||
nn.pop_back ();
|
||||
|
||||
if (element == "M") {
|
||||
if (nn.size () != 4) {
|
||||
error (tl::to_string (tr ("'M' element must have four nodes")));
|
||||
}
|
||||
} else if (element == "Q") {
|
||||
if (nn.size () != 3 && nn.size () != 4) {
|
||||
error (tl::to_string (tr ("'Q' element must have three or four nodes")));
|
||||
}
|
||||
} else if (element == "D") {
|
||||
if (nn.size () != 2) {
|
||||
error (tl::to_string (tr ("'D' element must have two nodes")));
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: other devices?
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
bool NetlistSpiceReaderDelegate::element (db::Circuit *circuit, const std::string &element, const std::string &name, const std::string &model, double value, const std::vector<db::Net *> &nets, const std::map<std::string, double> &pv)
|
||||
{
|
||||
std::map<std::string, double> params = pv;
|
||||
|
||||
double mult = 1.0;
|
||||
std::map<std::string, double>::const_iterator mp = params.find ("M");
|
||||
if (mp != params.end ()) {
|
||||
mult = mp->second;
|
||||
}
|
||||
|
||||
if (mult < 1e-10) {
|
||||
error (tl::sprintf (tl::to_string (tr ("Invalid multiplier value (M=%.12g) - must not be zero or negative")), mult));
|
||||
}
|
||||
|
||||
std::string cn = model;
|
||||
db::DeviceClass *cls = circuit->netlist ()->device_class_by_name (cn);
|
||||
|
||||
if (element == "R") {
|
||||
|
||||
if (nets.size () == 2) {
|
||||
if (cls) {
|
||||
if (! dynamic_cast<db::DeviceClassResistor *>(cls)) {
|
||||
error (tl::sprintf (tl::to_string (tr ("Class %s is not a resistor device class as required by 'R' element")), cn));
|
||||
}
|
||||
} else {
|
||||
if (cn.empty ()) {
|
||||
cn = "RES";
|
||||
}
|
||||
cls = make_device_class<db::DeviceClassResistor> (circuit, cn);
|
||||
}
|
||||
} else if (nets.size () == 3) {
|
||||
if (cls) {
|
||||
if (! dynamic_cast<db::DeviceClassResistorWithBulk *>(cls)) {
|
||||
error (tl::sprintf (tl::to_string (tr ("Class %s is not a three-terminal resistor device class as required by 'R' element")), cn));
|
||||
}
|
||||
} else {
|
||||
if (cn.empty ()) {
|
||||
cn = "RES3";
|
||||
}
|
||||
cls = make_device_class<db::DeviceClassResistorWithBulk> (circuit, cn);
|
||||
}
|
||||
} else {
|
||||
error (tl::to_string (tr ("A 'R' element requires two or three nets")));
|
||||
}
|
||||
|
||||
// Apply multiplier
|
||||
value /= mult;
|
||||
|
||||
} else if (element == "L") {
|
||||
|
||||
if (nets.size () == 2) {
|
||||
if (cls) {
|
||||
if (! dynamic_cast<db::DeviceClassInductor *>(cls)) {
|
||||
error (tl::sprintf (tl::to_string (tr ("Class %s is not a inductor device class as required by 'L' element")), cn));
|
||||
}
|
||||
} else {
|
||||
if (cn.empty ()) {
|
||||
cn = "IND";
|
||||
}
|
||||
cls = make_device_class<db::DeviceClassInductor> (circuit, cn);
|
||||
}
|
||||
} else {
|
||||
error (tl::to_string (tr ("A 'L' element requires two nets")));
|
||||
}
|
||||
|
||||
// Apply multiplier
|
||||
value /= mult;
|
||||
|
||||
} else if (element == "C") {
|
||||
|
||||
if (nets.size () == 2) {
|
||||
if (cls) {
|
||||
if (! dynamic_cast<db::DeviceClassCapacitor *>(cls)) {
|
||||
error (tl::sprintf (tl::to_string (tr ("Class %s is not a capacitor device class as required by 'C' element")), cn));
|
||||
}
|
||||
} else {
|
||||
if (cn.empty ()) {
|
||||
cn = "CAP";
|
||||
}
|
||||
cls = make_device_class<db::DeviceClassCapacitor> (circuit, cn);
|
||||
}
|
||||
} else if (nets.size () == 3) {
|
||||
if (cls) {
|
||||
if (! dynamic_cast<db::DeviceClassCapacitorWithBulk *>(cls)) {
|
||||
error (tl::sprintf (tl::to_string (tr ("Class %s is not a three-terminal capacitor device class as required by 'C' element")), cn));
|
||||
}
|
||||
} else {
|
||||
if (cn.empty ()) {
|
||||
cn = "CAP3";
|
||||
}
|
||||
cls = make_device_class<db::DeviceClassCapacitorWithBulk> (circuit, cn);
|
||||
}
|
||||
} else {
|
||||
error (tl::to_string (tr ("A 'C' element requires two or three nets")));
|
||||
}
|
||||
|
||||
// Apply multiplier
|
||||
value *= mult;
|
||||
|
||||
} else if (element == "D") {
|
||||
|
||||
if (cls) {
|
||||
if (! dynamic_cast<db::DeviceClassDiode *>(cls)) {
|
||||
error (tl::sprintf (tl::to_string (tr ("Class %s is not a diode device class as required by 'D' element")), cn));
|
||||
}
|
||||
} else {
|
||||
if (cn.empty ()) {
|
||||
cn = "DIODE";
|
||||
}
|
||||
cls = make_device_class<db::DeviceClassDiode> (circuit, cn);
|
||||
}
|
||||
|
||||
// Apply multiplier to "A"
|
||||
std::map<std::string, double>::iterator p;
|
||||
p = params.find ("A");
|
||||
if (p != params.end ()) {
|
||||
p->second *= mult;
|
||||
}
|
||||
|
||||
} else if (element == "Q") {
|
||||
|
||||
if (nets.size () != 3 && nets.size () != 4) {
|
||||
error (tl::to_string (tr ("'Q' element needs to have 3 or 4 terminals")));
|
||||
} else if (cls) {
|
||||
if (nets.size () == 3) {
|
||||
if (! dynamic_cast<db::DeviceClassBJT3Transistor *>(cls)) {
|
||||
error (tl::sprintf (tl::to_string (tr ("Class %s is not a 3-terminal BJT device class as required by 'Q' element")), cn));
|
||||
}
|
||||
} else {
|
||||
if (! dynamic_cast<db::DeviceClassBJT4Transistor *>(cls)) {
|
||||
error (tl::sprintf (tl::to_string (tr ("Class %s is not a 4-terminal BJT device class as required by 'Q' element")), cn));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (nets.size () == 3) {
|
||||
if (cn.empty ()) {
|
||||
cn = "BJT3";
|
||||
}
|
||||
cls = make_device_class<db::DeviceClassBJT3Transistor> (circuit, cn);
|
||||
} else {
|
||||
if (cn.empty ()) {
|
||||
cn = "BJT4";
|
||||
}
|
||||
cls = make_device_class<db::DeviceClassBJT4Transistor> (circuit, cn);
|
||||
}
|
||||
}
|
||||
|
||||
// Apply multiplier to "AE"
|
||||
std::map<std::string, double>::iterator p;
|
||||
p = params.find ("AE");
|
||||
if (p != params.end ()) {
|
||||
p->second *= mult;
|
||||
}
|
||||
|
||||
} else if (element == "M") {
|
||||
|
||||
if (cls) {
|
||||
if (! dynamic_cast<db::DeviceClassMOS4Transistor *>(cls)) {
|
||||
error (tl::sprintf (tl::to_string (tr ("Class %s is not a 4-terminal MOS device class as required by 'M' element")), cn));
|
||||
}
|
||||
} else {
|
||||
if (nets.size () == 4) {
|
||||
if (cn.empty ()) {
|
||||
cn = "MOS4";
|
||||
}
|
||||
cls = make_device_class<db::DeviceClassMOS4Transistor> (circuit, cn);
|
||||
} else {
|
||||
error (tl::to_string (tr ("'M' element needs to have 4 terminals")));
|
||||
}
|
||||
}
|
||||
|
||||
// Apply multiplier to "W"
|
||||
std::map<std::string, double>::iterator p;
|
||||
p = params.find ("W");
|
||||
if (p != params.end ()) {
|
||||
p->second *= mult;
|
||||
}
|
||||
|
||||
} else {
|
||||
error (tl::sprintf (tl::to_string (tr ("Not a known element type: '%s'")), element));
|
||||
}
|
||||
|
||||
const std::vector<db::DeviceTerminalDefinition> &td = cls->terminal_definitions ();
|
||||
if (td.size () != nets.size ()) {
|
||||
error (tl::sprintf (tl::to_string (tr ("Wrong number of terminals: class '%s' expects %d, but %d are given")), cn, int (td.size ()), int (nets.size ())));
|
||||
}
|
||||
|
||||
db::Device *device = new db::Device (cls, name);
|
||||
circuit->add_device (device);
|
||||
|
||||
for (std::vector<db::DeviceTerminalDefinition>::const_iterator t = td.begin (); t != td.end (); ++t) {
|
||||
device->connect_terminal (t->id (), nets [t - td.begin ()]);
|
||||
}
|
||||
|
||||
size_t defp = std::numeric_limits<size_t>::max ();
|
||||
if (dynamic_cast<db::DeviceClassCapacitor *> (cls)) {
|
||||
defp = db::DeviceClassCapacitor::param_id_C;
|
||||
} else if (dynamic_cast<db::DeviceClassResistor *> (cls)) {
|
||||
defp = db::DeviceClassResistor::param_id_R;
|
||||
} else if (dynamic_cast<db::DeviceClassInductor *> (cls)) {
|
||||
defp = db::DeviceClassInductor::param_id_L;
|
||||
}
|
||||
|
||||
std::vector<db::DeviceParameterDefinition> &pd = cls->parameter_definitions_non_const ();
|
||||
for (std::vector<db::DeviceParameterDefinition>::iterator i = pd.begin (); i != pd.end (); ++i) {
|
||||
std::map<std::string, double>::const_iterator v = params.find (i->name ());
|
||||
if (v != params.end ()) {
|
||||
device->set_parameter_value (i->id (), v->second / i->si_scaling ());
|
||||
} else if (i->id () == defp) {
|
||||
device->set_parameter_value (i->id (), value / i->si_scaling ());
|
||||
}
|
||||
}
|
||||
|
||||
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)));
|
||||
}
|
||||
|
||||
NetlistSpiceReaderExpressionParser 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)));
|
||||
}
|
||||
|
||||
NetlistSpiceReaderExpressionParser 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,142 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2023 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
#ifndef HDR_dbNetlistSpiceReaderDelegate
|
||||
#define HDR_dbNetlistSpiceReaderDelegate
|
||||
|
||||
#include "dbCommon.h"
|
||||
#include "tlStream.h"
|
||||
#include "tlException.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
class Netlist;
|
||||
class Net;
|
||||
class Circuit;
|
||||
class DeviceClass;
|
||||
class Device;
|
||||
|
||||
/**
|
||||
* @brief A delegate to handle various forms of devices and translates them
|
||||
*
|
||||
* The reader delegate can be configured to receive subcircuit elements too.
|
||||
* In this case, parameters are allowed.
|
||||
* For receiving subcircuit elements, the delegate needs to indicate
|
||||
* this by returning true upon "wants_subcircuit".
|
||||
*/
|
||||
class DB_PUBLIC NetlistSpiceReaderDelegate
|
||||
: public tl::Object
|
||||
{
|
||||
public:
|
||||
NetlistSpiceReaderDelegate ();
|
||||
virtual ~NetlistSpiceReaderDelegate ();
|
||||
|
||||
/**
|
||||
* @brief Called when the netlist reading starts
|
||||
*/
|
||||
virtual void start (db::Netlist *netlist);
|
||||
|
||||
/**
|
||||
* @brief Called when the netlist reading ends
|
||||
*/
|
||||
virtual void finish (db::Netlist *netlist);
|
||||
|
||||
/**
|
||||
* @brief Called when an unknown control statement is encountered
|
||||
*
|
||||
* Returns true if the statement is understood.
|
||||
*/
|
||||
virtual bool control_statement (const std::string &line);
|
||||
|
||||
/**
|
||||
* @brief Returns true, if the delegate wants subcircuit elements with this name
|
||||
*
|
||||
* The name is always upper case.
|
||||
*/
|
||||
virtual bool wants_subcircuit (const std::string &circuit_name);
|
||||
|
||||
/**
|
||||
* @brief This method translates a raw net name to a valid net name
|
||||
*
|
||||
* The default implementation will unescape backslash sequences into plain characters.
|
||||
*/
|
||||
virtual std::string translate_net_name (const std::string &nn);
|
||||
|
||||
/**
|
||||
* @brief Makes a device from an element line
|
||||
*
|
||||
* @param circuit The circuit that is currently read.
|
||||
* @param element The upper-case element code ("M", "R", ...).
|
||||
* @param name The element's name.
|
||||
* @param model The upper-case model name (may be empty).
|
||||
* @param value The default value (e.g. resistance for resistors) and may be zero.
|
||||
* @param nets The nets given in the element line.
|
||||
* @param parameters The parameters of the element statement (parameter names are upper case).
|
||||
*
|
||||
* The default implementation will create corresponding devices for
|
||||
* some known elements using the Spice writer's parameter conventions.
|
||||
*
|
||||
* This method returns true, if the element was read.
|
||||
*/
|
||||
virtual bool element (db::Circuit *circuit, const std::string &element, const std::string &name, const std::string &model, double value, const std::vector<db::Net *> &nets, const std::map<std::string, double> ¶ms);
|
||||
|
||||
/**
|
||||
* @brief Parses an element from a line
|
||||
*
|
||||
* @param s The line to parse (the part after the element and name)
|
||||
* @param model Out parameter: the model name if given
|
||||
* @param value Out parameter: the value if given (for R, L, C)
|
||||
* @param nn Out parameter: the net names
|
||||
* @param pv Out parameter: the parameter values (key/value pairs)
|
||||
*/
|
||||
virtual void 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> ¶ms);
|
||||
|
||||
/**
|
||||
* @brief Produces an error with the given message
|
||||
*/
|
||||
virtual void error (const std::string &msg);
|
||||
|
||||
/**
|
||||
* @brief Reads a set of string components and parameters from the string
|
||||
* A special key "param:" is recognized for starting a parameter list.
|
||||
*/
|
||||
static void parse_element_components (const std::string &s, std::vector<std::string> &strings, std::map<std::string, double> &pv, const std::map<std::string, double> &variables);
|
||||
|
||||
/**
|
||||
* @brief Reads a value from the extractor (with formula evaluation)
|
||||
*/
|
||||
static double read_value (tl::Extractor &ex, const std::map<std::string, double> &variables);
|
||||
|
||||
/**
|
||||
* @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);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,534 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2023 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
#include "dbNetlistSpiceReaderExpressionParser.h"
|
||||
#include "dbNetlistSpiceReader.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
// ------------------------------------------------------------------------------------------------------
|
||||
|
||||
NetlistSpiceReaderExpressionParser::NetlistSpiceReaderExpressionParser (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
|
||||
NetlistSpiceReaderExpressionParser::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
|
||||
NetlistSpiceReaderExpressionParser::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 NetlistSpiceReaderExpressionParser::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 NetlistSpiceReaderExpressionParser::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 NetlistSpiceReaderExpressionParser::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 NetlistSpiceReaderExpressionParser::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 NetlistSpiceReaderExpressionParser::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 NetlistSpiceReaderExpressionParser::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 NetlistSpiceReaderExpressionParser::read_tl_expr (tl::Extractor &ex, bool *status) const
|
||||
{
|
||||
return read_ternary_op (ex, status);
|
||||
}
|
||||
|
||||
static const char *start_quote (tl::Extractor &ex)
|
||||
{
|
||||
if (ex.test ("'")) {
|
||||
return "'";
|
||||
} else if (ex.test ("\"")) {
|
||||
return "\"";
|
||||
} else if (ex.test ("{")) {
|
||||
return "}";
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
tl::Variant NetlistSpiceReaderExpressionParser::read (tl::Extractor &ex) const
|
||||
{
|
||||
try {
|
||||
|
||||
tl::Variant res;
|
||||
|
||||
const char *endquote = start_quote (ex);
|
||||
res = read_tl_expr (ex, 0);
|
||||
if (endquote) {
|
||||
ex.test (endquote);
|
||||
}
|
||||
|
||||
return res;
|
||||
|
||||
} catch (tl::Exception &error) {
|
||||
throw NetlistSpiceReaderDelegateError (error.msg ());
|
||||
}
|
||||
}
|
||||
|
||||
bool NetlistSpiceReaderExpressionParser::try_read (tl::Extractor &ex, tl::Variant &value) const
|
||||
{
|
||||
tl::Extractor ex_saved = ex;
|
||||
|
||||
bool status = false;
|
||||
const char *endquote = start_quote (ex);
|
||||
value = read_tl_expr (ex, &status);
|
||||
if (endquote && ! ex.test (endquote)) {
|
||||
status = false;
|
||||
}
|
||||
if (! status) {
|
||||
value = tl::Variant ();
|
||||
ex = ex_saved;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2023 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
#ifndef HDR_dbNetlistSpiceReaderExpressionParser
|
||||
#define HDR_dbNetlistSpiceReaderExpressionParser
|
||||
|
||||
#include "dbCommon.h"
|
||||
#include "tlStream.h"
|
||||
#include "tlVariant.h"
|
||||
#include "tlString.h"
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief A class implementing the expression parser
|
||||
*
|
||||
* This class is exposed mainly for testing purposes.
|
||||
*/
|
||||
class DB_PUBLIC NetlistSpiceReaderExpressionParser
|
||||
{
|
||||
public:
|
||||
typedef std::map<std::string, tl::Variant> variables_type;
|
||||
|
||||
NetlistSpiceReaderExpressionParser (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;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -26,6 +26,7 @@
|
|||
#include "dbNetlistSpiceWriter.h"
|
||||
#include "dbNetlistReader.h"
|
||||
#include "dbNetlistSpiceReader.h"
|
||||
#include "dbNetlistSpiceReaderDelegate.h"
|
||||
#include "tlException.h"
|
||||
#include "tlInternational.h"
|
||||
#include "tlStream.h"
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
*/
|
||||
|
||||
#include "dbNetlistSpiceReader.h"
|
||||
#include "dbNetlistSpiceReaderDelegate.h"
|
||||
#include "dbNetlist.h"
|
||||
#include "dbNetlistDeviceClasses.h"
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue