Added a script that parses a SPICE file and prints the parameters that
are on the .subckt line, and the parameters that are internal to the
subcircuit in a .param statement. Also, corrected spectre_to_spice.py
to not add braces around an expression that is already encapsulated by
single quotes, as that causes grief to ngspice.
diff --git a/common/print_subckt_params.py b/common/print_subckt_params.py
new file mode 100755
index 0000000..c8f75eb
--- /dev/null
+++ b/common/print_subckt_params.py
@@ -0,0 +1,243 @@
+#!/bin/env python3
+#
+# print_subckt_params.py --
+#
+# Print a list of subcircuit parameters, dividing them into those which
+# are declared on the subcircuit line, and those that are declared inside
+# the scope of the subcircuit.
+#
+# The single argument is <path_to_input>
+# <path_to_input> should be the path to a single file.
+
+import os
+import sys
+import re
+import glob
+
+def usage():
+ print('print_subckt_params.py <path_to_input>')
+ print('where:')
+ print(' <path_to_input> is the path to the input file to parse')
+
+def parse_pins(line, debug):
+ # Regexp patterns
+ subrex = re.compile('\.subckt[ \t]+[^ \t]+[ \t]+(.*)')
+ parm1rex = re.compile('([^= \t]+)[ \t]*=[ \t]*[^ \t]+[ \t]*(.*)')
+ parm2rex = re.compile('([^= \t]+)[ \t]*(.*)')
+
+ params = []
+
+ pmatch = subrex.match(line)
+ if pmatch:
+ rest = pmatch.group(1)
+ else:
+ # Could not parse
+ return []
+
+ while rest != '':
+ if rest.startswith('$ '):
+ break
+ pmatch = parm1rex.match(rest)
+ if pmatch:
+ rest = pmatch.group(2)
+ params.append(pmatch.group(1))
+ else:
+ pmatch = parm2rex.match(rest)
+ if pmatch:
+ # This is a pin, so don't list it
+ rest = pmatch.group(2)
+
+ return params
+
+# Parse a parameter line for parameters, and divide into two parts,
+# returned as a list. If a parameter name matches an entry in 'params',
+# it goes in the second list. Otherwise, it goes in the first list.
+# The first list is returned as-is minus any parameters that were split
+# into the second list. The second list must begin with '+', as it will
+# be output as a continuation line for the subcircuit.
+
+def param_parse(line, debug):
+ # Regexp patterns
+ parm1rex = re.compile('\.param[ \t]+(.*)')
+ parm2rex = re.compile('\+[ \t]*(.*)')
+ parm3rex = re.compile('([^= \t]+)[ \t]*=[ \t]*[^ \t]+[ \t]*(.*)')
+ parm4rex = re.compile('([^= \t]+)[ \t]*(.*)')
+
+ if debug:
+ print('Diagnostic: param line in = "' + line + '"')
+
+ params = []
+
+ pmatch = parm1rex.match(line)
+ if pmatch:
+ rest = pmatch.group(1)
+ else:
+ pmatch = parm2rex.match(line)
+ if pmatch:
+ rest = pmatch.group(1)
+ else:
+ # Could not parse; return list with line and empty string
+ return []
+
+ while rest != '':
+ if rest.startswith('$ '):
+ break
+ pmatch = parm3rex.match(rest)
+ if pmatch:
+ rest = pmatch.group(2)
+ params.append(pmatch.group(1))
+ else:
+ pmatch = parm4rex.match(rest)
+ if pmatch:
+ rest = pmatch.group(2)
+ params.append(pmatch.group(1))
+
+ return params
+
+def parse_file(in_file, debug):
+
+ # Regexp patterns
+ paramrex = re.compile('\.param[ \t]+(.*)')
+ subrex = re.compile('\.subckt[ \t]+([^ \t]+)[ \t]+([^ \t]*)')
+ modelrex = re.compile('\.model[ \t]+([^ \t]+)[ \t]+([^ \t]+)[ \t]+(.*)')
+ endsubrex = re.compile('\.ends[ \t]+(.+)')
+ increx = re.compile('\.include[ \t]+')
+
+ with open(in_file, 'r') as ifile:
+ inplines = ifile.read().splitlines()
+
+ insubckt = False
+ inparam = False
+ inmodel = False
+ inpinlist = False
+
+ pinparams = []
+ paramlist = []
+ subname = ''
+
+ for line in inplines:
+
+ # Item 1. Handle comment lines
+ if line.startswith('*'):
+ continue
+
+ # Item 2. Flag continuation lines.
+ if line.startswith('+'):
+ contline = True
+ else:
+ contline = False
+ if line.strip() != '':
+ if inpinlist:
+ inpinlist = False
+ if inparam:
+ inparam = False
+
+ # Item 3. Handle blank lines like comment lines
+ if line.strip() == '':
+ continue
+
+ # Item 4. Handle continuation lines
+ # Remove lines that have a continuation mark and nothing else.
+ if contline:
+ if inparam:
+ # Continue handling parameters
+ if insubckt:
+ # Find subcircuit parameters and record what line they were found on
+ if inpinlist:
+ pinparams.extend(param_parse(line, debug))
+ else:
+ paramlist.extend(param_parse(line, debug))
+
+ continue
+
+ # Item 5. Regexp matching
+
+ # parameters
+ pmatch = paramrex.match(line)
+ if pmatch:
+ inparam = True
+ if insubckt:
+ if inpinlist:
+ inpinlist = False
+ # Find subcircuit parameters and record what line they were found on
+ paramlist.extend(param_parse(line, debug))
+ continue
+
+ # model
+ mmatch = modelrex.match(line)
+ if mmatch:
+ inmodel = 2
+ continue
+
+ if not insubckt:
+ # Things to parse if not in a subcircuit
+
+ imatch = subrex.match(line)
+ if imatch:
+ insubckt = True
+ inpinlist = True
+ subname = imatch.group(1)
+ pinparams = parse_pins(line, debug)
+ paramlist = []
+ continue
+
+ else:
+ ematch = endsubrex.match(line)
+ if ematch:
+ insubckt = False
+ inmodel = False
+ # Print out results
+ if debug:
+ print('File: ', end='')
+ print(in_file)
+ if debug:
+ print('Subcircuit: ', end='')
+ print(subname)
+ if debug:
+ print('Callable parameters: ', end='')
+ if len(pinparams) > 0:
+ print(' '.join(pinparams))
+ else:
+ print('----')
+ if debug:
+ print('Internal parameters: ', end='')
+ if len(paramlist) > 0:
+ print(' '.join(paramlist))
+ else:
+ print('----')
+ print()
+ continue
+
+if __name__ == '__main__':
+ debug = False
+
+ if len(sys.argv) == 1:
+ print("No options given to print_subckt_params.py.")
+ usage()
+ sys.exit(0)
+
+ optionlist = []
+ arguments = []
+
+ for option in sys.argv[1:]:
+ if option.find('-', 0) == 0:
+ optionlist.append(option)
+ else:
+ arguments.append(option)
+
+ if len(arguments) < 1:
+ print("Wrong number of arguments given to print_subckt_params.py.")
+ usage()
+ sys.exit(0)
+
+ if '-debug' in optionlist:
+ debug = True
+
+ inpath = arguments[0]
+
+ if not os.path.exists(inpath):
+ print('No such source file ' + inpath)
+ sys.exit(1)
+
+ parse_file(inpath, debug)
+ exit(0)