mirror of https://github.com/KLayout/klayout.git
Fixed #858 (+ line continuation after blanks in Spice reader)
This commit is contained in:
parent
fd5efe9f92
commit
79c552b300
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 ();
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
|
||||
.include "nreader14a.cir"
|
||||
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
.subckt INVX1 1 2 3 4 5 6
|
||||
.include nreader14x.cir
|
||||
.ends
|
||||
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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
|
||||
Loading…
Reference in New Issue