Fixed #858 (+ line continuation after blanks in Spice reader)

This commit is contained in:
Matthias Koefferlein 2021-07-02 23:31:54 +02:00
parent fd5efe9f92
commit 79c552b300
7 changed files with 254 additions and 65 deletions

View File

@ -635,8 +635,108 @@ bool NetlistSpiceReaderDelegate::element (db::Circuit *circuit, const std::strin
// ------------------------------------------------------------------------------------------------------
NetlistSpiceReader::SpiceReaderStream::SpiceReaderStream ()
: mp_stream (0), m_owns_stream (false), mp_text_stream (0), m_line_number (0), m_stored_line (), m_has_stored_line (false)
{
// .. nothing yet ..
}
NetlistSpiceReader::SpiceReaderStream::~SpiceReaderStream ()
{
close ();
}
void
NetlistSpiceReader::SpiceReaderStream::close ()
{
delete mp_text_stream;
mp_text_stream = 0;
if (m_owns_stream) {
delete mp_stream;
mp_stream = 0;
m_owns_stream = false;
}
}
std::pair<std::string, bool>
NetlistSpiceReader::SpiceReaderStream::get_line ()
{
if (mp_text_stream->at_end ()) {
return std::make_pair (std::string (), false);
}
++m_line_number;
std::string l = m_has_stored_line ? m_stored_line : mp_text_stream->get_line ();
m_has_stored_line = false;
m_stored_line.clear ();
while (! mp_text_stream->at_end ()) {
std::string ll = mp_text_stream->get_line ();
tl::Extractor ex (ll.c_str ());
if (! ex.test ("+")) {
m_stored_line = ll;
m_has_stored_line = true;
break;
} else {
++m_line_number;
l += " ";
l += ex.get ();
}
}
return std::make_pair (l, true);
}
int
NetlistSpiceReader::SpiceReaderStream::line_number () const
{
return m_line_number;
}
std::string
NetlistSpiceReader::SpiceReaderStream::source () const
{
return mp_stream->source ();
}
bool
NetlistSpiceReader::SpiceReaderStream::at_end () const
{
return mp_text_stream->at_end ();
}
void
NetlistSpiceReader::SpiceReaderStream::set_stream (tl::InputStream &stream)
{
close ();
mp_stream = &stream;
mp_text_stream = new tl::TextInputStream (stream);
m_owns_stream = false;
m_has_stored_line = false;
m_line_number = 0;
}
void
NetlistSpiceReader::SpiceReaderStream::set_stream (tl::InputStream *stream)
{
close ();
mp_stream = stream;
mp_text_stream = new tl::TextInputStream (*stream);
m_owns_stream = true;
m_has_stored_line = false;
m_line_number = 0;
}
// ------------------------------------------------------------------------------------------------------
NetlistSpiceReader::NetlistSpiceReader (NetlistSpiceReaderDelegate *delegate)
: mp_netlist (0), mp_stream (), mp_delegate (delegate)
: mp_netlist (0), mp_delegate (delegate), m_stream ()
{
static NetlistSpiceReaderDelegate std_delegate;
if (! delegate) {
@ -653,7 +753,8 @@ void NetlistSpiceReader::read (tl::InputStream &stream, db::Netlist &netlist)
{
tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (tr ("Reading netlist ")) + stream.source ());
mp_stream.reset (new tl::TextInputStream (stream));
m_stream.set_stream (stream);
mp_netlist = &netlist;
mp_circuit = 0;
mp_anonymous_top_circuit = 0;
@ -681,7 +782,7 @@ void NetlistSpiceReader::read (tl::InputStream &stream, db::Netlist &netlist)
// NOTE: because we do a peek to capture the "+" line continuation character, we're
// one line ahead.
std::string fmt_msg = tl::sprintf ("%s in %s, line %d", ex.msg (), mp_stream->source (), mp_stream->line_number () - 1);
std::string fmt_msg = tl::sprintf ("%s in %s, line %d", ex.msg (), m_stream.source (), m_stream.line_number ());
finish ();
throw tl::Exception (fmt_msg);
@ -737,11 +838,9 @@ void NetlistSpiceReader::build_global_nets ()
void NetlistSpiceReader::finish ()
{
while (! m_streams.empty ()) {
pop_stream ();
}
m_streams.clear ();
m_stream.close ();
mp_stream.reset (0);
mp_netlist = 0;
mp_circuit = 0;
mp_nets_by_name.reset (0);
@ -749,7 +848,7 @@ void NetlistSpiceReader::finish ()
void NetlistSpiceReader::push_stream (const std::string &path)
{
tl::URI current_uri (mp_stream->source ());
tl::URI current_uri (m_stream.source ());
tl::URI new_uri (path);
tl::InputStream *istream;
@ -757,80 +856,68 @@ void NetlistSpiceReader::push_stream (const std::string &path)
if (tl::is_absolute (path)) {
istream = new tl::InputStream (path);
} else {
istream = new tl::InputStream (tl::combine_path (tl::dirname (mp_stream->source ()), path));
istream = new tl::InputStream (tl::combine_path (tl::dirname (m_stream.source ()), path));
}
} else {
istream = new tl::InputStream (current_uri.resolved (new_uri).to_abstract_path ());
}
m_streams.push_back (std::make_pair (istream, mp_stream.release ()));
mp_stream.reset (new tl::TextInputStream (*istream));
m_streams.push_back (SpiceReaderStream ());
m_streams.back ().swap (m_stream);
m_stream.set_stream (istream);
}
void NetlistSpiceReader::pop_stream ()
{
if (! m_streams.empty ()) {
mp_stream.reset (m_streams.back ().second);
delete m_streams.back ().first;
m_stream.swap (m_streams.back ());
m_streams.pop_back ();
}
}
bool NetlistSpiceReader::at_end ()
{
return mp_stream->at_end () && m_streams.empty ();
return m_stream.at_end () && m_streams.empty ();
}
std::string NetlistSpiceReader::get_line ()
{
if (! m_stored_line.empty ()) {
std::string l;
l.swap (m_stored_line);
return l;
std::pair<std::string, bool> lp;
while (true) {
lp = m_stream.get_line ();
if (! lp.second) {
if (m_streams.empty ()) {
break;
} else {
pop_stream ();
}
} else {
tl::Extractor ex (lp.first.c_str ());
if (ex.test_without_case (".include") || ex.test_without_case (".inc")) {
std::string path;
ex.read_word_or_quoted (path, allowed_name_chars);
push_stream (path);
} else if (ex.at_end () || ex.test ("*")) {
// skip empty and comment lines
} else {
break;
}
}
}
std::string l;
do {
while (mp_stream->at_end ()) {
if (m_streams.empty ()) {
return std::string ();
}
pop_stream ();
}
l = mp_stream->get_line ();
while (! mp_stream->at_end () && mp_stream->peek_char () == '+') {
mp_stream->get_char ();
l += mp_stream->get_line ();
}
tl::Extractor ex (l.c_str ());
if (ex.test_without_case (".include") || ex.test_without_case (".inc")) {
std::string path;
ex.read_word_or_quoted (path, allowed_name_chars);
push_stream (path);
l.clear ();
} else if (ex.at_end () || ex.test ("*")) {
l.clear ();
}
} while (l.empty ());
return l;
}
void NetlistSpiceReader::unget_line (const std::string &l)
{
m_stored_line = l;
return lp.first;
}
bool NetlistSpiceReader::subcircuit_captured (const std::string &nc_name)
@ -928,7 +1015,7 @@ void NetlistSpiceReader::error (const std::string &msg)
void NetlistSpiceReader::warn (const std::string &msg)
{
std::string fmt_msg = tl::sprintf ("%s in %s, line %d", msg, mp_stream->source (), mp_stream->line_number () - 1);
std::string fmt_msg = tl::sprintf ("%s in %s, line %d", msg, m_stream.source (), m_stream.line_number ());
tl::warn << fmt_msg;
}

View File

@ -168,14 +168,48 @@ public:
virtual void read (tl::InputStream &stream, db::Netlist &netlist);
private:
class SpiceReaderStream
{
public:
SpiceReaderStream ();
~SpiceReaderStream ();
void set_stream (tl::InputStream &stream);
void set_stream (tl::InputStream *stream);
void close ();
std::pair<std::string, bool> get_line();
int line_number () const;
std::string source () const;
bool at_end () const;
void swap (SpiceReaderStream &other)
{
std::swap (mp_stream, other.mp_stream);
std::swap (m_owns_stream, other.m_owns_stream);
std::swap (mp_text_stream, other.mp_text_stream);
std::swap (m_line_number, other.m_line_number);
std::swap (m_stored_line, other.m_stored_line);
std::swap (m_has_stored_line, other.m_has_stored_line);
}
private:
tl::InputStream *mp_stream;
bool m_owns_stream;
tl::TextInputStream *mp_text_stream;
int m_line_number;
std::string m_stored_line;
bool m_has_stored_line;
};
db::Netlist *mp_netlist;
db::Circuit *mp_circuit;
db::Circuit *mp_anonymous_top_circuit;
std::unique_ptr<tl::TextInputStream> mp_stream;
tl::weak_ptr<NetlistSpiceReaderDelegate> mp_delegate;
std::vector<std::pair<tl::InputStream *, tl::TextInputStream *> > m_streams;
std::list<SpiceReaderStream> m_streams;
SpiceReaderStream m_stream;
std::unique_ptr<std::map<std::string, db::Net *> > mp_nets_by_name;
std::string m_stored_line;
std::map<std::string, bool> m_captured;
std::vector<std::string> m_global_nets;
std::set<std::string> m_global_net_names;
@ -191,7 +225,6 @@ private:
bool read_card ();
std::string read_name (tl::Extractor &ex);
std::string get_line ();
void unget_line (const std::string &l);
void error (const std::string &msg);
void warn (const std::string &msg);
void finish ();

View File

@ -550,3 +550,43 @@ TEST(13_NoGlobalNetsIfNotUsed)
);
}
TEST(14_IncludeWithError)
{
db::Netlist nl;
std::string path = tl::combine_path (tl::combine_path (tl::testdata (), "algo"), "nreader14.cir");
try {
db::NetlistSpiceReader reader;
tl::InputStream is (path);
reader.read (is, nl);
EXPECT_EQ (true, false); // must not happen
} catch (tl::Exception &ex) {
EXPECT_EQ (ex.msg (), "'M' element must have four nodes in " + std::string (tl::combine_path (tl::combine_path (tl::testdata (), "algo"), "nreader14x.cir")) + ", line 3");
}
}
TEST(15_ContinuationWithBlanks)
{
db::Netlist nl;
std::string path = tl::combine_path (tl::combine_path (tl::testdata (), "algo"), "nreader15.cir");
db::NetlistSpiceReader reader;
tl::InputStream is (path);
reader.read (is, nl);
EXPECT_EQ (nl.to_string (),
"circuit SUBCKT ($1=$1,'A[5]<1>'='A[5]<1>','V42(%)'='V42(%)',Z=Z,GND=GND,GND$1=GND$1);\n"
" subcircuit HVPMOS D_$1 ($1='V42(%)',$2=$3,$3=Z,$4=$1);\n"
" subcircuit HVPMOS D_$2 ($1='V42(%)',$2='A[5]<1>',$3=$3,$4=$1);\n"
" subcircuit HVNMOS D_$3 ($1=GND,$2=$3,$3=GND,$4=GND$1);\n"
" subcircuit HVNMOS D_$4 ($1=GND,$2=$3,$3=Z,$4=GND$1);\n"
" subcircuit HVNMOS D_$5 ($1=GND,$2='A[5]<1>',$3=$3,$4=GND$1);\n"
"end;\n"
"circuit HVPMOS ($1=(null),$2=(null),$3=(null),$4=(null));\n"
"end;\n"
"circuit HVNMOS ($1=(null),$2=(null),$3=(null),$4=(null));\n"
"end;\n"
);
}

3
testdata/algo/nreader14.cir vendored Normal file
View File

@ -0,0 +1,3 @@
.include "nreader14a.cir"

4
testdata/algo/nreader14a.cir vendored Normal file
View File

@ -0,0 +1,4 @@
.subckt INVX1 1 2 3 4 5 6
.include nreader14x.cir
.ends

4
testdata/algo/nreader14x.cir vendored Normal file
View File

@ -0,0 +1,4 @@
m$1 1 5 2 4 mlvpmos w=1.5um l=0.25um
m$2 3 5 2 6 mlvnmos w=0.95um l=0.25um
m1 1 *an error

18
testdata/algo/nreader15.cir vendored Normal file
View File

@ -0,0 +1,18 @@
.SUBCKT SUBCKT
+ \$1 A[5]<1> V42\x28\x25\x29 Z gnd gnd$1
* device instance $1 r0 *1 0,0 HVPMOS
XD_$1 V42\x28\x25\x29 \$3 Z \$1
+ HVPMOS PARAMS: L=0.2 W=1 AS=0.18 AD=0.18
+ PS=2.16 PD=2.16
XD_$2
+ V42\x28\x25\x29 A[5]<1> \$3 \$1
+ HVPMOS PARAMS: L=0.2 W=1 AS=0.18 AD=0.18
+ PS=2.16 PD=2.16
XD_$3 gnd \$3 gnd gnd$1 HVNMOS PARAMS: L=1.13 W=2.12 PS=6 PD=6 AS=0 AD=0
+
XD_$4 gnd \$3 Z gnd$1 HVNMOS PARAMS: L=0.4 W=0.4 PS=1.16 PD=1.16 AS=0.19 AD=0.19
XD_$5 gnd A[5]<1> \$3 gnd$1 HVNMOS
+ PARAMS: L=0.4 W=0.4 PS=1.76 PD=1.76 AS=0.19 AD=0.19
.ENDS SUBCKT