String serialization for netlists.

This commit is contained in:
Matthias Koefferlein 2019-03-18 02:00:33 +01:00
parent e361a528a9
commit e4078ca750
7 changed files with 426 additions and 3 deletions

View File

@ -246,6 +246,11 @@ Circuit::const_child_circuit_iterator Circuit::end_parents () const
return reinterpret_cast<const tl::vector<const Circuit *> &> (mp_netlist->parent_circuits (const_cast <Circuit *> (this))).end ();
}
void Circuit::clear_pins ()
{
m_pins.clear ();
}
const Pin &Circuit::add_pin (const std::string &name)
{
m_pins.push_back (Pin (name));

View File

@ -235,10 +235,14 @@ public:
}
/**
* @brief Adds a pin to this circuit
* The circuit takes over ownership of the object.
* @brief Clears the pins
*/
const Pin &add_pin(const std::string &name);
void clear_pins ();
/**
* @brief Adds a pin to this circuit
*/
const Pin &add_pin (const std::string &name);
/**
* @brief Begin iterator for the pins of the circuit (non-const version)

View File

@ -44,6 +44,7 @@ DeviceClass &DeviceClass::operator= (const DeviceClass &other)
{
if (this != &other) {
m_terminal_definitions = other.m_terminal_definitions;
m_parameter_definitions = other.m_parameter_definitions;
m_name = other.m_name;
m_description = other.m_description;
}

View File

@ -546,4 +546,373 @@ std::string Netlist::to_string () const
return res;
}
std::string Netlist::to_parsable_string () const
{
std::string res;
for (db::Netlist::const_circuit_iterator c = begin_circuits (); c != end_circuits (); ++c) {
std::string ps;
for (db::Circuit::const_pin_iterator p = c->begin_pins (); p != c->end_pins (); ++p) {
if (! ps.empty ()) {
ps += ",";
}
ps += pin2string (*p) + "=" + net2string (c->net_for_pin (p->id ()));
}
res += std::string ("circuit ") + c->name () + " (" + ps + ");\n";
for (db::Circuit::const_device_iterator d = c->begin_devices (); d != c->end_devices (); ++d) {
std::string ts;
const std::vector<db::DeviceTerminalDefinition> &td = d->device_class ()->terminal_definitions ();
for (std::vector<db::DeviceTerminalDefinition>::const_iterator t = td.begin (); t != td.end (); ++t) {
if (t != td.begin ()) {
ts += ", ";
}
ts += t->name () + "=" + net2string (d->net_for_terminal (t->id ()));
}
std::string ps;
const std::vector<db::DeviceParameterDefinition> &pd = d->device_class ()->parameter_definitions ();
for (std::vector<db::DeviceParameterDefinition>::const_iterator p = pd.begin (); p != pd.end (); ++p) {
if (p != pd.begin ()) {
ps += ",";
}
ps += p->name () + "=" + tl::to_string (d->parameter_value (p->id ()));
}
res += std::string (" device ") + tl::to_word_or_quoted_string (d->device_class ()->name ()) + " " + device2string (*d) + " (" + ts + ") (" + ps + ");\n";
}
for (db::Circuit::const_subcircuit_iterator sc = c->begin_subcircuits (); sc != c->end_subcircuits (); ++sc) {
std::string ps;
const db::SubCircuit &subcircuit = *sc;
const db::Circuit *circuit = sc->circuit_ref ();
for (db::Circuit::const_pin_iterator p = circuit->begin_pins (); p != circuit->end_pins (); ++p) {
if (p != circuit->begin_pins ()) {
ps += ", ";
}
ps += net2string (subcircuit.net_for_pin (p->id ()));
}
res += std::string (" subcircuit ") + tl::to_word_or_quoted_string (circuit->name ()) + " " + subcircuit2string (*sc) + " (" + ps + ");\n";
}
res += std::string ("end;\n");
}
return res;
}
static db::Net *read_net (tl::Extractor &ex, db::Circuit *circuit, std::map<std::string, db::Net *> &n2n)
{
std::string nn;
bool has_name = false;
size_t cluster_id = 0;
if (ex.test ("(")) {
ex.expect ("null");
ex.expect (")");
return 0;
} else if (ex.test ("$")) {
bool has_i = ex.test ("I");
ex.read (cluster_id);
nn = (has_i ? "$I" : "$") + tl::to_string (cluster_id);
if (has_i) {
cluster_id = (std::numeric_limits<size_t>::max () - cluster_id) + 1;
}
} else {
ex.read_word_or_quoted (nn);
has_name = true;
}
std::map<std::string, db::Net *>::const_iterator i = n2n.find (nn);
if (i == n2n.end ()) {
db::Net *net = new db::Net ();
circuit->add_net (net);
if (has_name) {
net->set_name (nn);
} else {
net->set_cluster_id (cluster_id);
}
n2n.insert (std::make_pair (nn, net));
return net;
} else {
return i->second;
}
}
static void read_pins (tl::Extractor &ex, db::Circuit *circuit, std::map<std::string, db::Net *> &n2n)
{
size_t npins = circuit->pin_count ();
circuit->clear_pins ();
ex.expect ("(");
while (! ex.test (")")) {
ex.expect_more ();
std::string pn;
if (ex.test ("$")) {
size_t i;
ex.read (i);
} else {
ex.read_word_or_quoted (pn);
}
ex.expect ("=");
db::Net *net = read_net (ex, circuit, n2n);
const db::Pin &pin = circuit->add_pin (pn);
if (net) {
net->add_pin (db::NetPinRef (pin.id ()));
}
ex.test (",");
}
if (circuit->pin_count () < npins) {
ex.error (tl::to_string (tr ("Circuit defines less pins that subcircuit")));
}
}
static void read_device_terminals (tl::Extractor &ex, db::Device *device, std::map<std::string, db::Net *> &n2n)
{
ex.expect ("(");
while (! ex.test (")")) {
ex.expect_more ();
std::string tn;
ex.read_word_or_quoted (tn);
size_t tid = std::numeric_limits<size_t>::max ();
const std::vector<DeviceTerminalDefinition> &td = device->device_class ()->terminal_definitions ();
for (std::vector<DeviceTerminalDefinition>::const_iterator i = td.begin (); i != td.end (); ++i) {
if (i->name () == tn) {
tid = i->id ();
break;
}
}
if (tid == std::numeric_limits<size_t>::max ()) {
ex.error (tl::to_string (tr ("Not a valid terminal name: ")) + tn);
}
ex.expect ("=");
db::Net *net = read_net (ex, device->circuit (), n2n);
if (net) {
device->connect_terminal (tid, net);
}
ex.test (",");
}
}
static void read_device_parameters (tl::Extractor &ex, db::Device *device)
{
ex.expect ("(");
while (! ex.test (")")) {
ex.expect_more ();
std::string pn;
ex.read_word_or_quoted (pn);
size_t pid = std::numeric_limits<size_t>::max ();
const std::vector<DeviceParameterDefinition> &pd = device->device_class ()->parameter_definitions ();
for (std::vector<DeviceParameterDefinition>::const_iterator i = pd.begin (); i != pd.end (); ++i) {
if (i->name () == pn) {
pid = i->id ();
break;
}
}
if (pid == std::numeric_limits<size_t>::max ()) {
ex.error (tl::to_string (tr ("Not a valid parameter name: ")) + pn);
}
ex.expect ("=");
double value = 0;
ex.read (value);
device->set_parameter_value (pid, value);
ex.test (",");
}
}
static void read_device (tl::Extractor &ex, db::Circuit *circuit, std::map<std::string, db::Net *> &n2n)
{
db::Netlist *netlist = circuit->netlist ();
std::string dcn;
ex.read_word_or_quoted (dcn);
db::DeviceClass *dc = 0;
for (db::Netlist::device_class_iterator i = netlist->begin_device_classes (); i != netlist->end_device_classes (); ++i) {
if (i->name () == dcn) {
dc = i.operator-> ();
}
}
if (! dc) {
ex.error (tl::to_string (tr ("Not a valid device class name: ")) + dcn);
}
std::string dn;
if (ex.test ("$")) {
size_t i;
ex.read (i);
} else {
ex.read_word_or_quoted (dn);
}
db::Device *device = new db::Device (dc, dn);
circuit->add_device (device);
read_device_terminals (ex, device, n2n);
read_device_parameters (ex, device);
}
static void read_subcircuit_pins (tl::Extractor &ex, db::SubCircuit *subcircuit, std::map<std::string, db::Net *> &n2n)
{
db::Circuit *circuit = subcircuit->circuit_ref ();
db::Circuit::pin_iterator pi = circuit->begin_pins ();
ex.expect ("(");
while (! ex.test (")")) {
if (pi == circuit->end_pins ()) {
// add a dummy pin
circuit->add_pin (std::string ());
pi = circuit->end_pins ();
--pi;
}
ex.expect_more ();
db::Net *net = read_net (ex, circuit, n2n);
if (net) {
subcircuit->connect_pin (pi->id (), net);
}
ex.test (",");
++pi;
}
if (pi != circuit->end_pins ()) {
// @@@ ex.error (tl::to_string (tr ("Too few pins in subcircuit call")));
}
}
static void read_subcircuit (tl::Extractor &ex, db::Circuit *circuit, std::map<std::string, db::Net *> &n2n, std::map<std::string, db::Circuit *> &c2n)
{
std::string cn;
ex.read_word_or_quoted (cn);
db::Circuit *cc = 0;
std::map<std::string, db::Circuit *>::const_iterator ic = c2n.find (cn);
if (ic == c2n.end ()) {
cc = new db::Circuit ();
circuit->netlist ()->add_circuit (cc);
cc->set_name (cn);
c2n.insert (std::make_pair (cn, cc));
} else {
cc = ic->second;
}
std::string scn;
if (ex.test ("$")) {
size_t i;
ex.read (i);
} else {
ex.read_word_or_quoted (scn);
}
db::SubCircuit *subcircuit = new db::SubCircuit (cc, scn);
circuit->add_subcircuit (subcircuit);
read_subcircuit_pins (ex, subcircuit, n2n);
}
void Netlist::from_string (const std::string &s)
{
tl::Extractor ex (s.c_str ());
std::map<std::string, db::Circuit *> c2n;
while (ex.test ("circuit")) {
std::string n;
ex.read_word_or_quoted (n);
db::Circuit *circuit = 0;
std::map<std::string, db::Circuit *>::const_iterator ic = c2n.find (n);
if (ic == c2n.end ()) {
circuit = new db::Circuit ();
add_circuit (circuit);
circuit->set_name (n);
c2n.insert (std::make_pair (n, circuit));
} else {
circuit = ic->second;
}
std::map<std::string, db::Net *> n2n;
read_pins (ex, circuit, n2n);
ex.expect (";");
while (! ex.test ("end")) {
ex.expect_more ();
if (ex.test ("device")) {
read_device (ex, circuit, n2n);
ex.expect (";");
} else if (ex.test ("subcircuit")) {
read_subcircuit (ex, circuit, n2n, c2n);
ex.expect (";");
} else {
ex.error (tl::to_string (tr ("device or subcircuit expected")));
}
}
ex.expect (";");
}
ex.expect_end ();
}
}

View File

@ -94,6 +94,21 @@ public:
*/
std::string to_string () const;
/**
* @brief Returns a parsable string representation of the netlist
*
* This method returns a string suitable for being put into from_string.
*/
std::string to_parsable_string () const;
/**
* @brief Reads a netlist from the string generated by to_parsable_string
*
* The device classes have to be installed so it's possible to identify the devices
* by their class.
*/
void from_string (const std::string &s);
/**
* @brief Starts a sequence of operations during which topology updates are not desired
*

View File

@ -304,6 +304,14 @@ TEST(1_DeviceAndNetExtraction)
"Circuit TRANS ($1=$1,$2=$2,$3=$3):\n"
);
// use this opportunity to test serialization to and from string
db::Netlist nldup;
for (db::Netlist::device_class_iterator i = nl.begin_device_classes (); i != nl.end_device_classes (); ++i) {
nldup.add_device_class (i->clone ());
}
nldup.from_string (nl.to_parsable_string ());
EXPECT_EQ (nldup.to_string (), nl.to_string ());
// doesn't do anything here, but we test that this does not destroy anything:
nl.combine_devices ();

View File

@ -501,6 +501,9 @@ TEST(4_NetlistSubcircuits)
dc->add_terminal_definition (db::DeviceTerminalDefinition ("B", ""));
nl->add_device_class (dc);
std::auto_ptr<db::Netlist> nldup (new db::Netlist ());
nldup->add_device_class (dc->clone ());
db::DeviceAbstract *dm = new db::DeviceAbstract ();
dm->set_device_class (dc);
EXPECT_EQ (dm->device_class () == dc, true);
@ -609,6 +612,24 @@ TEST(4_NetlistSubcircuits)
"D:B,+c2p2\n"
);
EXPECT_EQ (nl->to_string (),
"Circuit c1 (c1p1=n1a,c1p2=n1c):\n"
" Xc2 sc1 (c2p1=n1a,c2p2=n1b)\n"
" Xc2 sc2 (c2p1=n1b,c2p2=n1c)\n"
"Circuit c2 (c2p1=n2a,c2p2=n2b):\n"
" Ddc2 D (A=n2a,B=n2b) []\n"
);
nldup->from_string (nl->to_parsable_string ());
EXPECT_EQ (nldup->to_string (),
"Circuit c1 (c1p1=n1a,c1p2=n1c):\n"
" Xc2 sc1 (c2p1=n1a,c2p2=n1b)\n"
" Xc2 sc2 (c2p1=n1b,c2p2=n1c)\n"
"Circuit c2 (c2p1=n2a,c2p2=n2b):\n"
" Ddc2 D (A=n2a,B=n2b) []\n"
);
EXPECT_EQ (netlist2 (*nl),
"c1:c1p1=n1a,c1p2=n1c\n"
" Xsc1:c2p1=n1a,c2p2=n1b\n"