mirror of https://github.com/KLayout/klayout.git
Proposal for fixing issue #1784 (Spice throws error at midline semicolon comments)
This commit is contained in:
parent
d3921844d6
commit
7978a2bb1d
|
|
@ -54,7 +54,7 @@ read_param_card (tl::Extractor &ex, const db::Netlist *netlist, std::map<std::st
|
||||||
// taken from:
|
// taken from:
|
||||||
// https://nmg.gitlab.io/ngspice-manual/circuitdescription/paramparametricnetlists/paramline.html
|
// https://nmg.gitlab.io/ngspice-manual/circuitdescription/paramparametricnetlists/paramline.html
|
||||||
|
|
||||||
while (! ex.at_end ()) {
|
while (! NetlistSpiceReader::at_eol (ex)) {
|
||||||
|
|
||||||
std::string name;
|
std::string name;
|
||||||
ex.read_word (name);
|
ex.read_word (name);
|
||||||
|
|
@ -333,7 +333,7 @@ read_name (tl::Extractor &ex, const db::Netlist *netlist)
|
||||||
{
|
{
|
||||||
std::string n;
|
std::string n;
|
||||||
ex.read_word_or_quoted (n, allowed_name_chars);
|
ex.read_word_or_quoted (n, allowed_name_chars);
|
||||||
return netlist->normalize_name (n);
|
return netlist->normalize_name (NetlistSpiceReader::unescape_name (n));
|
||||||
}
|
}
|
||||||
|
|
||||||
class SpiceCircuitDict
|
class SpiceCircuitDict
|
||||||
|
|
@ -610,7 +610,7 @@ SpiceCircuitDict::get_line ()
|
||||||
std::string path_or_libname;
|
std::string path_or_libname;
|
||||||
|
|
||||||
ex.read_word_or_quoted (path_or_libname, allowed_name_chars);
|
ex.read_word_or_quoted (path_or_libname, allowed_name_chars);
|
||||||
if (! ex.at_end ()) {
|
if (! NetlistSpiceReader::at_eol (ex)) {
|
||||||
|
|
||||||
std::string libname;
|
std::string libname;
|
||||||
ex.read_word_or_quoted (libname, allowed_name_chars);
|
ex.read_word_or_quoted (libname, allowed_name_chars);
|
||||||
|
|
@ -648,7 +648,7 @@ SpiceCircuitDict::get_line ()
|
||||||
|
|
||||||
ex.expect_end ();
|
ex.expect_end ();
|
||||||
|
|
||||||
} else if (ex.at_end () || ex.test ("*")) {
|
} else if (NetlistSpiceReader::at_eol (ex)) {
|
||||||
|
|
||||||
// skip empty and comment lines
|
// skip empty and comment lines
|
||||||
|
|
||||||
|
|
@ -680,7 +680,7 @@ SpiceCircuitDict::read_card ()
|
||||||
|
|
||||||
} else if (ex.test_without_case (".global")) {
|
} else if (ex.test_without_case (".global")) {
|
||||||
|
|
||||||
while (! ex.at_end ()) {
|
while (! NetlistSpiceReader::at_eol (ex)) {
|
||||||
std::string n = mp_delegate->translate_net_name (read_name (ex, mp_netlist));
|
std::string n = mp_delegate->translate_net_name (read_name (ex, mp_netlist));
|
||||||
if (m_global_net_names.find (n) == m_global_net_names.end ()) {
|
if (m_global_net_names.find (n) == m_global_net_names.end ()) {
|
||||||
m_global_nets.push_back (n);
|
m_global_nets.push_back (n);
|
||||||
|
|
@ -766,7 +766,7 @@ SpiceCircuitDict::read_card ()
|
||||||
void
|
void
|
||||||
SpiceCircuitDict::read_options (tl::Extractor &ex)
|
SpiceCircuitDict::read_options (tl::Extractor &ex)
|
||||||
{
|
{
|
||||||
while (! ex.at_end ()) {
|
while (! NetlistSpiceReader::at_eol (ex)) {
|
||||||
|
|
||||||
std::string n;
|
std::string n;
|
||||||
ex.read_word_or_quoted (n, allowed_name_chars);
|
ex.read_word_or_quoted (n, allowed_name_chars);
|
||||||
|
|
@ -780,7 +780,7 @@ SpiceCircuitDict::read_options (tl::Extractor &ex)
|
||||||
} else {
|
} else {
|
||||||
// skip until end or next space
|
// skip until end or next space
|
||||||
ex.skip ();
|
ex.skip ();
|
||||||
while (! ex.at_end () && ! isspace (*ex)) {
|
while (! NetlistSpiceReader::at_eol (ex) && ! isspace (*ex)) {
|
||||||
++ex;
|
++ex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1345,4 +1345,106 @@ void NetlistSpiceReader::read (tl::InputStream &stream, db::Netlist &netlist)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool NetlistSpiceReader::at_eol (tl::Extractor &ex)
|
||||||
|
{
|
||||||
|
// terminate at end or midline comment
|
||||||
|
return ex.at_end () || ex.test ("*") || ex.test (";");
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string NetlistSpiceReader::unescape_name (const std::string &n)
|
||||||
|
{
|
||||||
|
std::string nn;
|
||||||
|
nn.reserve (n.size ());
|
||||||
|
|
||||||
|
char quote = 0;
|
||||||
|
|
||||||
|
const char *cp = n.c_str ();
|
||||||
|
while (*cp) {
|
||||||
|
|
||||||
|
if (*cp == quote) {
|
||||||
|
|
||||||
|
quote = 0;
|
||||||
|
++cp;
|
||||||
|
|
||||||
|
} else if (! quote && (*cp == '"' || *cp == '\'')) {
|
||||||
|
|
||||||
|
quote = *cp++;
|
||||||
|
|
||||||
|
} else 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string NetlistSpiceReader::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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@
|
||||||
#include "tlStream.h"
|
#include "tlStream.h"
|
||||||
#include "tlObject.h"
|
#include "tlObject.h"
|
||||||
#include "tlVariant.h"
|
#include "tlVariant.h"
|
||||||
|
#include "tlString.h"
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
@ -63,6 +64,29 @@ public:
|
||||||
m_strict = s;
|
m_strict = s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns true, if the extractor is at the end of the line
|
||||||
|
* "at_eol" is true at the line end or when a midline comment starts.
|
||||||
|
*/
|
||||||
|
static bool at_eol (tl::Extractor &ex);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Unescapes a name
|
||||||
|
* Replaces backslash sequences with the true character and removes quotes.
|
||||||
|
*/
|
||||||
|
static std::string unescape_name (const std::string &n);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Parses a netlist component (net name, expression etc.)
|
||||||
|
* Scans over the expression or net name and returns a string representing the latter.
|
||||||
|
*/
|
||||||
|
static std::string parse_component (tl::Extractor &ex);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Reads a component name
|
||||||
|
* Scans over a component name and returns the
|
||||||
|
*/
|
||||||
|
|
||||||
private:
|
private:
|
||||||
tl::weak_ptr<NetlistSpiceReaderDelegate> mp_delegate;
|
tl::weak_ptr<NetlistSpiceReaderDelegate> mp_delegate;
|
||||||
std::unique_ptr<NetlistSpiceReaderDelegate> mp_default_delegate;
|
std::unique_ptr<NetlistSpiceReaderDelegate> mp_default_delegate;
|
||||||
|
|
|
||||||
|
|
@ -32,60 +32,6 @@ 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
NetlistSpiceReaderOptions::NetlistSpiceReaderOptions ()
|
NetlistSpiceReaderOptions::NetlistSpiceReaderOptions ()
|
||||||
{
|
{
|
||||||
scale = 1.0;
|
scale = 1.0;
|
||||||
|
|
@ -147,7 +93,7 @@ bool NetlistSpiceReaderDelegate::wants_subcircuit (const std::string & /*circuit
|
||||||
|
|
||||||
std::string NetlistSpiceReaderDelegate::translate_net_name (const std::string &nn)
|
std::string NetlistSpiceReaderDelegate::translate_net_name (const std::string &nn)
|
||||||
{
|
{
|
||||||
return unescape_name (nn);
|
return NetlistSpiceReader::unescape_name (nn);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetlistSpiceReaderDelegate::error (const std::string &msg)
|
void NetlistSpiceReaderDelegate::error (const std::string &msg)
|
||||||
|
|
@ -172,45 +118,12 @@ static db::DeviceClass *make_device_class (db::Circuit *circuit, const std::stri
|
||||||
return 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, tl::Variant> &pv, const std::map<std::string, tl::Variant> &variables)
|
void NetlistSpiceReaderDelegate::parse_element_components (const std::string &s, std::vector<std::string> &strings, std::map<std::string, tl::Variant> &pv, const std::map<std::string, tl::Variant> &variables)
|
||||||
{
|
{
|
||||||
tl::Extractor ex (s.c_str ());
|
tl::Extractor ex (s.c_str ());
|
||||||
bool in_params = false;
|
bool in_params = false;
|
||||||
|
|
||||||
while (! ex.at_end ()) {
|
while (! NetlistSpiceReader::at_eol (ex)) {
|
||||||
|
|
||||||
if (ex.test_without_case ("params:")) {
|
if (ex.test_without_case ("params:")) {
|
||||||
|
|
||||||
|
|
@ -235,7 +148,7 @@ void NetlistSpiceReaderDelegate::parse_element_components (const std::string &s,
|
||||||
ex.error (tl::to_string (tr ("Invalid syntax for parameter assignment - needs keyword followed by '='")));
|
ex.error (tl::to_string (tr ("Invalid syntax for parameter assignment - needs keyword followed by '='")));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string comp_name = parse_component (ex);
|
std::string comp_name = NetlistSpiceReader::parse_component (ex);
|
||||||
comp_name = mp_netlist ? mp_netlist->normalize_name (comp_name) : tl::to_upper_case (comp_name);
|
comp_name = mp_netlist ? mp_netlist->normalize_name (comp_name) : tl::to_upper_case (comp_name);
|
||||||
|
|
||||||
// resolve variables if string type
|
// resolve variables if string type
|
||||||
|
|
|
||||||
|
|
@ -914,6 +914,25 @@ TEST(25_dismiss_top_level)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(26_comments)
|
||||||
|
{
|
||||||
|
db::Netlist nl;
|
||||||
|
|
||||||
|
std::string path = tl::combine_path (tl::combine_path (tl::testdata (), "algo"), "nreader26.cir");
|
||||||
|
|
||||||
|
db::NetlistSpiceReader reader;
|
||||||
|
tl::InputStream is (path);
|
||||||
|
reader.read (is, nl);
|
||||||
|
|
||||||
|
EXPECT_EQ (nl.to_string (),
|
||||||
|
"circuit TOP (A=A,B=B);\n"
|
||||||
|
" device NMOS '1' (S=A,G=B,D=A,B=B) (L=100,W=100,AS=0,AD=0,PS=0,PD=0);\n"
|
||||||
|
" device NMOS '2' (S='\\\\A',G='NET\"Q\"',D=A,B='NET[0]') (L=100,W=100,AS=0,AD=0,PS=0,PD=0);\n"
|
||||||
|
" device NMOS 'FET[1]' (S='\\\\A',G='NET\"Q\"',D=AAA,B='NET[0]') (L=100,W=1.7,AS=0,AD=0,PS=0,PD=0);\n"
|
||||||
|
"end;\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
TEST(100_ExpressionParser)
|
TEST(100_ExpressionParser)
|
||||||
{
|
{
|
||||||
std::map<std::string, tl::Variant> vars;
|
std::map<std::string, tl::Variant> vars;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
* Test: dismiss empty top level circuit
|
||||||
|
; a comment
|
||||||
|
|
||||||
|
.subckt top a b
|
||||||
|
m1 a b a b nmos * a comment
|
||||||
|
m2 a 'net"q"' \\a net[0] nmos * a comment
|
||||||
|
mfet\[1\] \a\x41a "net\"q\"" "\\a" net\[0\] nmos w=1.7u ; a comment
|
||||||
|
.ends
|
||||||
|
|
||||||
|
* this triggered generation of a top level circuit
|
||||||
|
.param p1 17 ; a comment
|
||||||
|
|
||||||
|
.end ; a comment
|
||||||
|
|
||||||
Loading…
Reference in New Issue