diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index dbec8e46e..e339c9501 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -43,7 +43,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
- uses: github/codeql-action/init@v1
+ uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@@ -54,7 +54,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
- uses: github/codeql-action/autobuild@v1
+ uses: github/codeql-action/autobuild@v2
# ℹ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
@@ -68,4 +68,4 @@ jobs:
# make release
- name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@v1
+ uses: github/codeql-action/analyze@v2
diff --git a/.gitignore b/.gitignore
index d60b61d06..2a8bc5f90 100644
--- a/.gitignore
+++ b/.gitignore
@@ -112,6 +112,25 @@ examples/c-examples/ngspice-shared/sharedspice.h
resources
+rsync-vps.sh
+
+spice-examples/eg6.dat
+spice-examples/gnucap.cir
+spice-examples/mosfet-characterization.cir
+spice-examples/ring-modulator.cir
+spice-examples/xyce-error.cir
+spice-examples/xyce-error.cir.FD.prn
+spice-examples/xyce-raw.data
+spice-examples/xyce-test.cir
+spice-examples/xyce-test2.cir
+spice-examples/xyce-test2.csv
+
+tools/run-tox
+tools/upload-www
+
+trash/
+.idea/
+
unit-test/test.py
anaconda-recipe/conda-bld
@@ -127,4 +146,10 @@ examples/c-examples/ngspice_cb/ng_shared_test_sl/ng_shared_test_sl.layout
examples/c-examples/ngspice_cb/ng_shared_test_sl_v/
examples/c-examples/ngspice_cb/ng_shared_test_v/
-Spice64_dll/
\ No newline at end of file
+Spice64_dll/
+unit-test/Spice/input.cir
+unit-test/Spice/input.cir.ic
+unit-test/Spice/output.raw
+input.cir
+input.cir.ic
+output.raw
diff --git a/.idea/PySpice.iml b/.idea/PySpice.iml
new file mode 100644
index 000000000..df194f43d
--- /dev/null
+++ b/.idea/PySpice.iml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/dataSources.local.xml b/.idea/dataSources.local.xml
new file mode 100644
index 000000000..d85b9629d
--- /dev/null
+++ b/.idea/dataSources.local.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml
new file mode 100644
index 000000000..105ce2da2
--- /dev/null
+++ b/.idea/inspectionProfiles/profiles_settings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 000000000..d9f89f9e4
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 000000000..42c5abcc5
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/other.xml b/.idea/other.xml
new file mode 100644
index 000000000..a708ec781
--- /dev/null
+++ b/.idea/other.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 000000000..94a25f7f4
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/workspace.xml b/.idea/workspace.xml
new file mode 100644
index 000000000..4ae60f64e
--- /dev/null
+++ b/.idea/workspace.xml
@@ -0,0 +1,864 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1585652882548
+
+
+ 1585652882548
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1621316590920
+
+
+
+ 1621316590920
+
+
+ 1621316929764
+
+
+
+ 1621316929764
+
+
+ 1621317272814
+
+
+
+ 1621317272814
+
+
+ 1621317474494
+
+
+
+ 1621317474494
+
+
+ 1621529024027
+
+
+
+ 1621529024027
+
+
+ 1621601315502
+
+
+
+ 1621601315502
+
+
+ 1621854642253
+
+
+
+ 1621854642253
+
+
+ 1621922186970
+
+
+
+ 1621922186970
+
+
+ 1621923696258
+
+
+
+ 1621923696258
+
+
+ 1622109826837
+
+
+
+ 1622109826837
+
+
+ 1622124559026
+
+
+
+ 1622124559026
+
+
+ 1622361004647
+
+
+
+ 1622361004647
+
+
+ 1622365436460
+
+
+
+ 1622365436460
+
+
+ 1622367433876
+
+
+
+ 1622367433876
+
+
+ 1622385838407
+
+
+
+ 1622385838407
+
+
+ 1622397395406
+
+
+
+ 1622397395406
+
+
+ 1622402434932
+
+
+
+ 1622402434932
+
+
+ 1622466526664
+
+
+
+ 1622466526664
+
+
+ 1622487755158
+
+
+
+ 1622487755158
+
+
+ 1622489707241
+
+
+
+ 1622489707241
+
+
+ 1622489751289
+
+
+
+ 1622489751289
+
+
+ 1622490875363
+
+
+
+ 1622490875363
+
+
+ 1622539484037
+
+
+
+ 1622539484037
+
+
+ 1622545223544
+
+
+
+ 1622545223544
+
+
+ 1622560988342
+
+
+
+ 1622560988342
+
+
+ 1622562746484
+
+
+
+ 1622562746484
+
+
+ 1622565898932
+
+
+
+ 1622565898932
+
+
+ 1622566122148
+
+
+
+ 1622566122148
+
+
+ 1622568968775
+
+
+
+ 1622568968775
+
+
+ 1622642980193
+
+
+
+ 1622642980193
+
+
+ 1622986018906
+
+
+
+ 1622986018906
+
+
+ 1623048518199
+
+
+
+ 1623048518199
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ file://$PROJECT_DIR$/PySpice/Unit/Unit.py
+ 1335
+
+
+
+ file://$PROJECT_DIR$/PySpice/Spice/Netlist.py
+ 451
+
+
+
+ file://$PROJECT_DIR$/PySpice/Spice/BasicElement.py
+ 923
+
+
+
+ file://$PROJECT_DIR$/unit-test/Unit/test_Units.py
+ 22
+
+
+
+ file://$PROJECT_DIR$/PySpice/Spice/Library.py
+ 106
+
+
+
+ file://$PROJECT_DIR$/examples/run-examples
+ 35
+
+
+
+ file://$PROJECT_DIR$/setup.py
+ 24
+
+
+
+ file://$PROJECT_DIR$/venv/lib/python3.10/site-packages/PySpice/Spice/Netlist.py
+ 382
+
+
+
+ file://$PROJECT_DIR$/PySpice/Spice/RawFile.py
+ 242
+
+
+
+ file://$PROJECT_DIR$/PySpice/Spice/BasicElement.py
+ 892
+
+
+
+ file://$PROJECT_DIR$/PySpice/Spice/ElementParameter.py
+ 401
+
+
+
+ file://$PROJECT_DIR$/PySpice/Spice/BasicElement.py
+ 178
+
+
+
+ file://$PROJECT_DIR$/PySpice/Spice/Simulation.py
+ 672
+
+
+
+ file://$PROJECT_DIR$/PySpice/Spice/EBNFSpiceParser.py
+ 1322
+
+
+
+ file://$PROJECT_DIR$/PySpice/Spice/EBNFSpiceParser.py
+ 2036
+
+
+
+ file://$PROJECT_DIR$/PySpice/Spice/EBNFSpiceParser.py
+ 1781
+
+
+
+ file://$PROJECT_DIR$/unit-test-todo/test_netlist.py
+ 5
+
+
+
+ file://$PROJECT_DIR$/unit-test/Spice/test_BasicElement.py
+ 59
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/PySpice/Math/__init__.py b/PySpice/Math/__init__.py
index 45367217f..945cc91d0 100644
--- a/PySpice/Math/__init__.py
+++ b/PySpice/Math/__init__.py
@@ -38,9 +38,9 @@ def even(x):
####################################################################################################
def rms_to_amplitude(x):
- """Return :math:`x \sqrt{2}`"""
+ """Return :math:`x \\sqrt{2}`"""
return x * math.sqrt(2)
def amplitude_to_rms(x):
- """Return :math:`x / \sqrt{2}`"""
+ """Return :math:`x / \\sqrt{2}`"""
return x / math.sqrt(2)
diff --git a/PySpice/Probe/WaveForm.py b/PySpice/Probe/WaveForm.py
index ca92d15f2..9b88baf16 100644
--- a/PySpice/Probe/WaveForm.py
+++ b/PySpice/Probe/WaveForm.py
@@ -31,7 +31,7 @@
import logging
import os
-# import numpy as np
+import numpy as np
####################################################################################################
@@ -39,7 +39,7 @@
####################################################################################################
-from PySpice.Unit.Unit import UnitValues
+from ..Unit.Unit import UnitValues, UnitValue
####################################################################################################
@@ -64,9 +64,11 @@ class WaveForm(UnitValues):
##############################################
- @classmethod
- def from_unit_values(cls, name, array, title=None, abscissa=None):
- obj = cls(
+ @staticmethod
+ def from_unit_values(name, array, title=None, abscissa=None):
+
+ shape = array.shape
+ obj = WaveForm(
name,
array.prefixed_unit,
array.shape,
@@ -82,7 +84,7 @@ def from_unit_values(cls, name, array, title=None, abscissa=None):
@classmethod
def from_array(cls, name, array, title=None, abscissa=None):
# Fixme: ok ???
- obj = cls(name, None, array.shape, title=title, abscissa=abscissa)
+ obj = WaveForm(name, None, array.shape, title=title, abscissa=abscissa)
obj[...] = array[...]
return obj
@@ -137,7 +139,10 @@ def __array_ufunc__(self, ufunc, method, *inputs, **kwargs):
result = super().__array_ufunc__(ufunc, method, *inputs, **kwargs)
# self._logger.info("result\n{}".format(result))
if isinstance(result, UnitValues):
- return self.from_unit_values(name='', array=result, title='', abscissa=self._abscissa)
+ if len(result.shape) == 0:
+ return UnitValue(result.prefixed_unit, result)
+ else:
+ return self.from_unit_values(name='', array=result, title='', abscissa=self._abscissa)
else:
return result # e.g. foo <= 0
@@ -250,10 +255,10 @@ def __init__(self, simulation, nodes=(), branches=(), elements=(), internal_para
self._simulation = simulation
# Fixme: to func?
- self._nodes = {waveform.name:waveform for waveform in nodes}
- self._branches = {waveform.name:waveform for waveform in branches}
- self._elements = {waveform.name:waveform for waveform in elements}
- self._internal_parameters = {waveform.name:waveform for waveform in internal_parameters}
+ self._nodes = {waveform.name.lower():waveform for waveform in nodes}
+ self._branches = {waveform.name.lower():waveform for waveform in branches}
+ self._elements = {waveform.name.lower():waveform for waveform in elements}
+ self._internal_parameters = {waveform.name.lower():waveform for waveform in internal_parameters}
##############################################
@@ -297,10 +302,7 @@ def _get_item(self, name):
##############################################
def __getitem__(self, name):
- try:
- return self._get_item(name)
- except IndexError:
- return self._get_item(name.lower())
+ return self._get_item(name.lower())
##############################################
diff --git a/PySpice/Spice/BasicElement.py b/PySpice/Spice/BasicElement.py
index f4f943a60..04a27fde4 100644
--- a/PySpice/Spice/BasicElement.py
+++ b/PySpice/Spice/BasicElement.py
@@ -100,8 +100,9 @@
import logging
from ..Tools.StringTools import str_spice, join_list, join_dict
-from ..Unit import U_m, U_s, U_A, U_V, U_Degree, U_Ω, U_F, U_H, U_Hz
-from .Netlist import (Element, AnyPinElement, FixedPinElement, NPinElement, OptionalPin)
+from ..Unit import U_m, U_s, U_a, U_v, U_c, U_Ω, U_F, U_h, U_Hz
+from .Netlist import (Element, AnyPinElement, FixedPinElement, NPinElement,
+ OptionalPin, Pin, PinDefinition)
from .ElementParameter import (
# KeyValueParameter,
BoolKeyParameter,
@@ -117,8 +118,7 @@
IntKeyParameter,
ModelPositionalParameter,
)
-
-####################################################################################################
+from .Expressions import Expression
_module_logger = logging.getLogger(__name__)
@@ -159,12 +159,11 @@ class SubCircuitElement(NPinElement):
##############################################
- def __init__(self, netlist, name, subcircuit_name, *nodes, **parameters):
-
- super().__init__(netlist, name, nodes, subcircuit_name)
+ def __init__(self, netlist, name, subcircuit_name, *nodes, **kwargs):
# Fixme: match parameters to subcircuit
- self.parameters = parameters
+ self.parameters = kwargs
+ self.parent = netlist
# Fixme: investigate
# for key, value in parameters.items():
@@ -173,6 +172,24 @@ def __init__(self, netlist, name, subcircuit_name, *nodes, **parameters):
# self.optional_parameters[key] = parameter
# setattr(self, key, parameter)
+ subcircuit_name = subcircuit_name.lower()
+ if netlist is None:
+ raise ValueError("Unexpected None value")
+ subcircuit = netlist._find_subcircuit(subcircuit_name)
+
+ if subcircuit is None:
+ raise ValueError("Non existing subcircuit: {}".format(subcircuit_name))
+ if len(nodes) != len(subcircuit.PINS):
+ raise ValueError("Incorrect number of nodes for subcircuit {}".format(subcircuit_name))
+
+ if not hasattr(self, '_pins'):
+ self._pins = []
+ if len(nodes) > 0:
+ self._pins = [Pin(self, pin_definition, netlist.get_node(node, True))
+ for pin_definition, node in zip(subcircuit.PINS, nodes)]
+
+ super().__init__(netlist, name, subcircuit_name)
+
##############################################
def copy_to(self, netlist):
@@ -188,7 +205,9 @@ def format_spice_parameters(self):
spice_parameters = super().format_spice_parameters()
if self.parameters:
- spice_parameters += ' ' + join_dict(self.parameters)
+ parameters = {key: ('{%s}' % str_spice(value)) if isinstance(value, Expression) else str_spice(value)
+ for key, value in self.parameters.items()}
+ spice_parameters += ' params: ' + join_dict(parameters)
return spice_parameters
@@ -246,13 +265,17 @@ class Resistor(DipoleElement):
ALIAS = 'R'
PREFIX = 'R'
- resistance = FloatPositionalParameter(position=0, key_parameter=False, unit=U_Ω)
+ resistance = FloatPositionalParameter(position=-1, key_parameter=False, unit=U_Ω)
+ model = ModelPositionalParameter(position=0, key_parameter=True)
ac = FloatKeyParameter('ac', unit=U_Ω)
multiplier = IntKeyParameter('m')
scale = FloatKeyParameter('scale')
- temperature = FloatKeyParameter('temp', unit=U_Degree)
- device_temperature = FloatKeyParameter('dtemp', unit=U_Degree)
+ temperature = FloatKeyParameter('temp', unit=U_c)
+ device_temperature = FloatKeyParameter('dtemp', unit=U_c)
noisy = BoolKeyParameter('noisy')
+ tc = FloatPairKeyParameter('tc')
+ tc1 = FloatPairKeyParameter('tc1')
+ tc2 = FloatKeyParameter('tc2')
####################################################################################################
@@ -318,12 +341,12 @@ class SemiconductorResistor(DipoleElement):
ALIAS = 'SemiconductorResistor'
PREFIX = 'R'
- resistance = FloatPositionalParameter(position=0, key_parameter=False, unit=U_Ω)
- model = ModelPositionalParameter(position=1, key_parameter=True)
+ resistance = FloatPositionalParameter(position=-1, key_parameter=False, unit=U_Ω)
+ model = ModelPositionalParameter(position=0, key_parameter=True)
length = FloatKeyParameter('l', unit=U_m)
width = FloatKeyParameter('w', unit=U_m)
- temperature = FloatKeyParameter('temp', unit=U_Degree)
- device_temperature = FloatKeyParameter('dtemp', unit=U_Degree)
+ temperature = FloatKeyParameter('temp', unit=U_c)
+ device_temperature = FloatKeyParameter('dtemp', unit=U_c)
multiplier = IntKeyParameter('m')
ac = FloatKeyParameter('ac', unit=U_Ω)
scale = FloatKeyParameter('scale')
@@ -364,6 +387,7 @@ class BehavioralResistor(DipoleElement):
PREFIX = 'R'
resistance_expression = ExpressionPositionalParameter(position=0, key_parameter=False)
+ tc = FloatPairKeyParameter('tc')
tc1 = FloatKeyParameter('tc1')
tc2 = FloatKeyParameter('tc2')
@@ -418,13 +442,16 @@ class Capacitor(DipoleElement):
ALIAS = 'C'
PREFIX = 'C'
- capacitance = FloatPositionalParameter(position=0, key_parameter=False, unit=U_F)
- model = ModelPositionalParameter(position=1, key_parameter=True)
+ capacitance = FloatPositionalParameter(position=-1, key_parameter=False, unit=U_F)
+ model = ModelPositionalParameter(position=0, key_parameter=True)
multiplier = IntKeyParameter('m')
scale = FloatKeyParameter('scale')
- temperature = FloatKeyParameter('temp', unit=U_Degree)
- device_temperature = FloatKeyParameter('dtemp', unit=U_Degree)
+ temperature = FloatKeyParameter('temp', unit=U_c)
+ device_temperature = FloatKeyParameter('dtemp', unit=U_c)
initial_condition = FloatKeyParameter('ic')
+ tc = FloatPairKeyParameter('tc')
+ tc1 = FloatPairKeyParameter('tc1')
+ tc2 = FloatKeyParameter('tc2')
####################################################################################################
@@ -487,14 +514,14 @@ class SemiconductorCapacitor(DipoleElement):
ALIAS = 'SemiconductorCapacitor'
PREFIX = 'C'
- capacitance = FloatPositionalParameter(position=0, key_parameter=False, unit=U_F)
- model = ModelPositionalParameter(position=1, key_parameter=True)
+ capacitance = FloatPositionalParameter(position=-1, key_parameter=False, unit=U_F)
+ model = ModelPositionalParameter(position=0, key_parameter=True)
length = FloatKeyParameter('l', unit=U_m)
width = FloatKeyParameter('w', unit=U_m)
multiplier = IntKeyParameter('m')
scale = FloatKeyParameter('scale')
- temperature = FloatKeyParameter('temp', unit=U_Degree)
- device_temperature = FloatKeyParameter('dtemp', unit=U_Degree)
+ temperature = FloatKeyParameter('temp', unit=U_c)
+ device_temperature = FloatKeyParameter('dtemp', unit=U_c)
initial_condition = FloatKeyParameter('ic')
####################################################################################################
@@ -530,6 +557,7 @@ class BehavioralCapacitor(DipoleElement):
PREFIX = 'C'
capacitance_expression = ExpressionPositionalParameter(position=0, key_parameter=False)
+ tc = FloatPairKeyParameter('tc')
tc1 = FloatKeyParameter('tc1')
tc2 = FloatKeyParameter('tc2')
@@ -586,14 +614,17 @@ class Inductor(DipoleElement):
ALIAS = 'L'
PREFIX = 'L'
- inductance = FloatPositionalParameter(position=0, key_parameter=False, unit=U_H)
- model = ModelPositionalParameter(position=1, key_parameter=True)
+ inductance = FloatPositionalParameter(position=-1, key_parameter=False, unit=U_h)
+ model = ModelPositionalParameter(position=0, key_parameter=True)
nt = FloatKeyParameter('nt')
multiplier = IntKeyParameter('m')
scale = FloatKeyParameter('scale')
- temperature = FloatKeyParameter('temp', unit=U_Degree)
- device_temperature = FloatKeyParameter('dtemp', unit=U_Degree)
+ temperature = FloatKeyParameter('temp', unit=U_c)
+ device_temperature = FloatKeyParameter('dtemp', unit=U_c)
initial_condition = FloatKeyParameter('ic')
+ tc = FloatPairKeyParameter('tc')
+ tc1 = FloatPairKeyParameter('tc1')
+ tc2 = FloatKeyParameter('tc2')
####################################################################################################
@@ -628,7 +659,8 @@ class BehavioralInductor(DipoleElement):
PREFIX = 'L'
inductance_expression = ExpressionPositionalParameter(position=0, key_parameter=False)
- tc1 = FloatKeyParameter('tc1')
+ tc = FloatPairKeyParameter('tc')
+ tc1 = FloatPairKeyParameter('tc1')
tc2 = FloatKeyParameter('tc2')
####################################################################################################
@@ -658,6 +690,10 @@ class CoupledInductor(AnyPinElement):
ALIAS = 'K'
PREFIX = 'K'
+ # Adding the variable as it is not used by the coupling inductor
+
+ _pins = tuple()
+
inductor1 = ElementNamePositionalParameter(position=0, key_parameter=False)
inductor2 = ElementNamePositionalParameter(position=1, key_parameter=False)
coupling_factor = FloatPositionalParameter(position=2, key_parameter=False)
@@ -670,20 +706,7 @@ def __init__(self, name, *args, **kwargs):
super().__init__(name, *args, **kwargs)
- self._inductors = []
- for inductor in (self.inductor1, self.inductor2):
- try:
- self.netlist.element(inductor)
- except KeyError:
- try:
- inductor = 'L' + inductor
- self.netlist.element(inductor)
- self._logger.info('Prefixed element {}'.format(inductor))
- except KeyError:
- raise ValueError('Element with name {} not found'.format(inductor))
- # Fixme: str or Element instance ?
- self._inductors.append(inductor)
- self.inductor1, self.inductor2 = self._inductors
+ self._inductors = (self.inductor1, self.inductor2)
####################################################################################################
@@ -784,7 +807,36 @@ class VoltageSource(DipoleElement):
PREFIX = 'V'
# Fixme: ngspice manual doesn't describe well the syntax
- dc_value = FloatPositionalParameter(position=0, key_parameter=False, unit=U_V)
+ dc_value = FloatPositionalParameter(position=0, key_parameter=False, unit=U_v)
+ ac_magnitude = FloatPositionalParameter(position=1, key_parameter=False, unit=U_v)
+ ac_phase = ExpressionPositionalParameter(position=2, key_parameter=False)
+ transient = ExpressionPositionalParameter(position=3, key_parameter=False)
+
+ def format_spice_parameters(self):
+ parameters = []
+ if self.dc_value is not None:
+ dc_value = str_spice(self.dc_value)
+ if isinstance(self.dc_value, Expression):
+ dc_value = "{%s}" % dc_value
+ parameters.append('dc {}'.format(dc_value))
+ if self.ac_magnitude is not None:
+ ac_magnitude = str_spice(self.ac_magnitude)
+ if isinstance(self.ac_magnitude, Expression):
+ ac_magnitude = "{%s}" % ac_magnitude
+ parameters.append('ac {}'.format(ac_magnitude))
+ if self.ac_phase is not None:
+ ac_phase = str_spice(self.ac_phase)
+ if self.ac_magnitude is None:
+ parameters.append('ac 0')
+ if isinstance(self.ac_phase, Expression):
+ ac_phase = "{%s}" % ac_phase
+ parameters.append(ac_phase)
+ if self.transient is not None:
+ transient = str_spice(self.transient)
+ if isinstance(self.transient, Expression):
+ transient = "{%s}" % transient
+ parameters.append(transient)
+ return join_list(parameters)
####################################################################################################
@@ -810,7 +862,36 @@ class CurrentSource(DipoleElement):
PREFIX = 'I'
# Fixme: ngspice manual doesn't describe well the syntax
- dc_value = FloatPositionalParameter(position=0, key_parameter=False, unit=U_A)
+ dc_value = FloatPositionalParameter(position=0, key_parameter=False, unit=U_a)
+ ac_magnitude = FloatPositionalParameter(position=1, key_parameter=False, unit=U_a)
+ ac_phase = ExpressionPositionalParameter(position=2, key_parameter=False)
+ transient = ExpressionPositionalParameter(position=3, key_parameter=False)
+
+ def format_spice_parameters(self):
+ parameters = []
+ if self.dc_value is not None:
+ dc_value = str_spice(self.dc_value)
+ if isinstance(self.dc_value, Expression):
+ dc_value = "{%s}" % dc_value
+ parameters.append('dc {}'.format(dc_value))
+ if self.ac_magnitude is not None:
+ ac_magnitude = str_spice(self.ac_magnitude)
+ if isinstance(self.ac_magnitude, Expression):
+ ac_magnitude = "{%s}" % ac_magnitude
+ parameters.append('ac {}'.format(ac_magnitude))
+ if self.ac_phase is not None:
+ ac_phase = str_spice(self.ac_phase)
+ if self.ac_magnitude is None:
+ parameters.append('ac 0')
+ if isinstance(self.ac_phase, Expression):
+ ac_phase = "{%s}" % ac_phase
+ parameters.append(ac_phase)
+ if self.transient is not None:
+ transient = str_spice(self.transient)
+ if isinstance(self.transient, Expression):
+ transient = "{%s}" % transient
+ parameters.append(transient)
+ return join_list(parameters)
####################################################################################################
@@ -983,10 +1064,76 @@ class BehavioralSource(DipoleElement):
current_expression = ExpressionKeyParameter('i')
voltage_expression = ExpressionKeyParameter('v')
+ tc = FloatPairKeyParameter('tc')
tc1 = FloatKeyParameter('tc1')
tc2 = FloatKeyParameter('tc2')
- temperature = FloatKeyParameter('temp', unit=U_Degree)
- device_temperature = FloatKeyParameter('dtemp', unit=U_Degree)
+ temperature = FloatKeyParameter('temp', unit=U_c)
+ device_temperature = FloatKeyParameter('dtemp', unit=U_c)
+ smoothbsrc = IntKeyParameter('smoothbsrc')
+
+ ##############################################
+
+ def __str__(self):
+ self._logger = _module_logger.getChild('BehaviouralSource')
+
+ from .Expressions import Symbol
+ spice_element = self.format_node_names()
+ # Fixme: expression
+ temp_expression = None
+ if self.netlist.spice_sim == 'xyce':
+ temp = 'temp'
+ if self.temperature is not None:
+ temp = self.temperature
+ elif self.device_temperature is not None:
+ temp = self.device_temperature
+ if self.tc is not None:
+ temp_expression = (1+Symbol(self.tc[0]) * (Symbol(temp) - Symbol(27)) +
+ Symbol(self.tc[1]) * (Symbol(temp) - Symbol(27))**2)
+ else:
+ if self.tc1 is not None:
+ temp_expression = (1 + Symbol(self.tc1) * (Symbol(temp) - Symbol(27)))
+ if self.tc2 is not None:
+ temp_expression = (1 + Symbol(self.tc2) * (Symbol(temp) - Symbol(27))**2)
+ expression = ''
+ if self.current_expression is not None:
+ if temp_expression is not None:
+ if isinstance(self.current_expression, Expression):
+ expression = ' i={%s}' % (self.current_expression * temp_expression)
+ else:
+ self._logger.warning('Unable to include associated temperature behaviour')
+ else:
+ if isinstance(self.current_expression, Expression) or type(self.current_expression) in (int, float):
+ expression = ' i={%s}' % self.current_expression
+ else:
+ expression = ' i=%s' % self.current_expression
+ elif self.voltage_expression is not None:
+ if temp_expression is not None:
+ if isinstance(self.voltage_expression, Expression) or type(self.voltage_expression) in (int, float):
+ expression = ' v={%s}' % (self.voltage_expression * temp_expression)
+ else:
+ self._logger.warning('Unable to include associated temperature behaviour')
+ else:
+ if isinstance(self.voltage_expression, Expression):
+ expression = ' v={%s}' % self.voltage_expression
+ else:
+ expression = ' v=%s' % self.voltage_expression
+ spice_element += expression
+ if self.netlist.spice_sim != 'xyce':
+ if self.tc is not None:
+ spice_element += ' tc1=%f,%f' % self.tc
+ else:
+ if self.tc1 is not None:
+ spice_element += ' tc1=%f' % self.tc1
+ if self.tc2 is not None:
+ spice_element += ' tc2=%f' % self.tc2
+ if self.temperature is not None:
+ spice_element += ' temp=%f' % self.temperature
+ if self.device_temperature is not None:
+ spice_element += ' dtemp=%f' % self.device_temperature
+ else:
+ if self.smoothbsrc is not None:
+ spice_element += ' smoothbsrc=%s' % self.smoothbsrc
+ return spice_element
####################################################################################################
@@ -1015,8 +1162,9 @@ class NonLinearVoltageSource(DipoleElement):
ALIAS = 'NonLinearVoltageSource'
PREFIX = 'E'
- # Fixme:
- VALID_KWARGS = ('expression', 'table')
+ value = ExpressionKeyParameter('value')
+ table = ExpressionKeyParameter('table')
+ smoothbsrc = ExpressionKeyParameter('smoothbsrc')
##############################################
@@ -1136,8 +1284,8 @@ class Diode(FixedPinElement):
pj = FloatKeyParameter('pj')
off = FlagParameter('off')
ic = FloatPairKeyParameter('ic')
- temperature = FloatKeyParameter('temp', unit=U_Degree)
- device_temperature = FloatKeyParameter('dtemp', unit=U_Degree)
+ temperature = FloatKeyParameter('temp', unit=U_c)
+ device_temperature = FloatKeyParameter('dtemp', unit=U_c)
####################################################################################################
#
@@ -1205,7 +1353,7 @@ class BipolarJunctionTransistor(FixedPinElement):
ALIAS = 'Q'
LONG_ALIAS = 'BJT'
PREFIX = 'Q'
- PINS = ('collector', 'base', 'emitter', OptionalPin('substrate'))
+ PINS = ('collector', 'base', 'emitter', OptionalPin('substrate'), OptionalPin('thermal'))
model = ModelPositionalParameter(position=0, key_parameter=True)
area = FloatKeyParameter('area')
@@ -1214,8 +1362,20 @@ class BipolarJunctionTransistor(FixedPinElement):
multiplier = IntKeyParameter('m')
off = FlagParameter('off')
ic = FloatPairKeyParameter('ic')
- temperature = FloatKeyParameter('temp', unit=U_Degree)
- device_temperature = FloatKeyParameter('dtemp', unit=U_Degree)
+ temperature = FloatKeyParameter('temp', unit=U_c)
+ device_temperature = FloatKeyParameter('dtemp', unit=U_c)
+
+ def format_node_names(self):
+ fixed_pins = len(self.PINS) - self._number_of_optional_pins_
+ nodes = [pin.node for pin in self._pins[:fixed_pins]]
+ for pin in self._pins[fixed_pins:]:
+ if str(pin.node).lower() != 'dt':
+ nodes.append('[{}]'.format(pin.node))
+ else:
+ nodes.append(pin.node)
+
+ """ Return the formatted list of nodes. """
+ return join_list((self.name, join_list(nodes)))
####################################################################################################
#
@@ -1274,7 +1434,7 @@ class JunctionFieldEffectTransistor(JfetElement):
multiplier = IntKeyParameter('m')
off = FlagParameter('off')
ic = FloatPairKeyParameter('ic')
- temperature = FloatKeyParameter('temp', unit=U_Degree)
+ temperature = FloatKeyParameter('temp', unit=U_c)
####################################################################################################
#
@@ -1437,7 +1597,7 @@ class Mosfet(FixedPinElement):
source_number_square = FloatKeyParameter('nrs')
off = FlagParameter('off')
ic = FloatTripletKeyParameter('ic')
- temperature = FloatKeyParameter('temp', unit=U_Degree)
+ temperature = FloatKeyParameter('temp', unit=U_c)
# only for Xyce
nfin = IntKeyParameter('nfin')
@@ -1509,12 +1669,13 @@ class LosslessTransmissionLine(TwoPortElement):
def __init__(self, name, *args, **kwargs):
- super().__init__(name, *args, **kwargs)
-
- if not (self.has_parameter('time_delay') or
- (self.has_parameter('frequency') and self.has_parameter('normalized_length'))):
+ # check: ^ xor, & bitwise and
+ if not (('time_delay' in kwargs) ^
+ (('frequency' in kwargs) & ('normalized_length' in kwargs))):
raise NameError('Either TD or F, NL must be specified')
+ super().__init__(name, *args, **kwargs)
+
####################################################################################################
class LossyTransmission(TwoPortElement):
@@ -1690,7 +1851,7 @@ def __init__(self, netlist, name, *nodes, **parameters):
# Fixme: ok ???
- super().__init__(netlist, name, nodes, **parameters)
+ super().__init__(netlist, name, *nodes, **parameters)
####################################################################################################
#
diff --git a/PySpice/Spice/EBNFExpressionParser.py b/PySpice/Spice/EBNFExpressionParser.py
new file mode 100644
index 000000000..b3f923dba
--- /dev/null
+++ b/PySpice/Spice/EBNFExpressionParser.py
@@ -0,0 +1,470 @@
+import logging
+import os
+import csv
+
+from unicodedata import normalize
+from PySpice.Unit.Unit import UnitValue, ZeroPower, PrefixedUnit, UnitMetaclass, UnitPrefixMetaclass
+from PySpice.Unit.SiUnits import Tera, Giga, Mega, Kilo, Milli, Micro, Nano, Pico, Femto
+from PySpice.Tools.StringTools import join_lines
+from .Expressions import *
+
+from .ExpressionGrammar import ExpressionParser as parser
+from .ExpressionModel import ExpressionModelBuilderSemantics
+from tatsu import to_python_sourcecode, to_python_model, compile
+from tatsu.model import NodeWalker
+
+_module_logger = logging.getLogger(__name__)
+
+
+class ParseError(NameError):
+ pass
+
+
+class Statement:
+ """ This class implements a statement, in fact a line in a Spice netlist. """
+
+ @staticmethod
+ def arg_to_python(x):
+
+ if x:
+ if str(x)[0].isdigit():
+ return str(x)
+ else:
+ return "'{}'".format(x)
+ else:
+ return ''
+
+ @staticmethod
+ def args_to_python(*args):
+
+ return [Statement.arg_to_python(x) for x in args]
+
+ @staticmethod
+ def kwargs_to_python(self, **kwargs):
+ return Statement.join_args(*['{}={}'.format(key, self.value_to_python(value))
+ for key, value in kwargs.items()])
+
+ @staticmethod
+ def join_args(self, *args):
+ return ', '.join(args)
+
+
+class ExpressionModelWalker(NodeWalker):
+
+ def __init__(self):
+ self._scales = (Tera(), Giga(), Mega(), Kilo(), Milli(), Micro(), Nano(), Pico(), Femto())
+ self._suffix = dict([(normalize("NFKD", unit.prefix).lower(), PrefixedUnit(power=unit))
+ for unit in self._scales] +
+ [(normalize("NFKD", unit.spice_prefix).lower(), PrefixedUnit(power=unit))
+ for unit in self._scales
+ if unit.spice_prefix is not None]
+ )
+ self._functions = {"abs": Abs,
+ "agauss": AGauss,
+ "acos": ACos,
+ "acosh": ACosh,
+ "arctan": ATan,
+ "asin": ASin,
+ "asinh": ASinh,
+ "atan": ATan,
+ "atan2": ATan2,
+ "atanh": ATanh,
+ "aunif": AUnif,
+ "ceil": Ceil,
+ "cos": Cos,
+ "cosh": Cosh,
+ "db": Db,
+ "ddt": Ddt,
+ "ddx": Ddx,
+ "exp": Exp,
+ "ln": Ln,
+ "log": Ln,
+ "log10": Log10,
+ "floor": Floor,
+ "gauss": Gauss,
+ "i": I,
+ "if": If,
+ "img": Img,
+ "int": Int,
+ "limit": Limit,
+ "m": M,
+ "max": Max,
+ "min": Min,
+ "nint": NInt,
+ "ph": Ph,
+ "pow": Pow,
+ "pwr": Pow,
+ "pwrs": Pwrs,
+ "r": Re,
+ "rand": Rand,
+ "re": Re,
+ "sdt": Sdt,
+ "sgn": Sgn,
+ "sign": Sign,
+ "sin": Sin,
+ "sinh": Sinh,
+ "sqrt": Sqrt,
+ "stp": Stp,
+ "tan": Tan,
+ "tanh": Tanh,
+ "unif": Unif,
+ "uramp": URamp,
+ "v": V
+ }
+ self._relational = {
+ "<": LT,
+ "<=": LE,
+ "==": EQ,
+ "!=": NE,
+ ">=": GE,
+ ">": GT
+ }
+
+ def walk_SpiceExpression(self, node, data):
+ return self.walk(node.ast, data)
+
+ def walk_GenericExpression(self, node, data):
+ if node.value is None:
+ return self.walk(node.braced, data)
+ else:
+ return self.walk(node.value, data)
+
+ def walk_BracedExpression(self, node, data):
+ return self.walk(node.ast, data)
+
+ def walk_Ternary(self, node, data):
+ t = self.walk(node.t, data)
+ x = self.walk(node.x, data)
+ y = self.walk(node.y, data)
+ return self._functions["if"](t, x, y)
+
+ def walk_Conditional(self, node, data):
+ return self.walk(node.expr, data)
+
+ def walk_And(self, node, data):
+ left = self.walk(node.left, data)
+ if node.right is None:
+ return left
+ else:
+ right = self.walk(node.right, data)
+ return And(left, right)
+
+ def walk_Not(self, node, data):
+ operator = self.walk(node.operator, data)
+ if node.op is None:
+ return operator
+ else:
+ return Not(operator)
+
+ def walk_Or(self, node, data):
+ left = self.walk(node.left, data)
+ if node.right is None:
+ return left
+ else:
+ right = self.walk(node.right, data)
+ return Or(left, right)
+
+ def walk_Xor(self, node, data):
+ left = self.walk(node.left, data)
+ if node.right is None:
+ return left
+ else:
+ right = self.walk(node.right, data)
+ return Xor(left, right)
+
+ def walk_Relational(self, node, data):
+ if node.factor is None:
+ left = self.walk(node.left, data)
+ right = self.walk(node.right, data)
+ return self._relational[node.op](left, right)
+ else:
+ return self.walk(node.factor, data)
+
+ def walk_ConditionalFactor(self, node, data):
+ if node.boolean is None:
+ return self.walk(node.expr, data)
+ else:
+ return node.boolean.lower() == "true"
+
+ def walk_Expression(self, node, data):
+ if node.term is None:
+ return self.walk(node.ternary, data)
+ else:
+ return self.walk(node.term, data)
+
+ def walk_Functional(self, node, data):
+ return self.walk(node.ast, data)
+
+ def walk_Functions(self, node, data):
+ l_func = node.func.lower()
+ function = self._functions[l_func]
+ if function.nargs == 0:
+ return function()
+ elif l_func == 'v':
+ nodes = self.walk(node.node, data)
+ if isinstance(nodes, list):
+ return function(*nodes)
+ else:
+ return function(nodes)
+ elif l_func == 'i':
+ device = self.walk(node.device, data)
+ return function(device)
+ elif function.nargs == 1:
+ x = self.walk(node.x, data)
+ return function(x)
+ elif l_func == 'limit':
+ x = self.walk(node.x, data)
+ y = self.walk(node.y, data)
+ z = self.walk(node.z, data)
+ return function(x, y, z)
+ elif l_func == 'atan2':
+ x = self.walk(node.x, data)
+ y = self.walk(node.y, data)
+ return function(y, x)
+ elif l_func in ('aunif', 'unif'):
+ mu = self.walk(node.mu, data)
+ alpha = self.walk(node.alpha, data)
+ return function(mu, alpha)
+ elif l_func == "ddx":
+ f = node.f
+ x = self.walk(node.x, data)
+ return function(Symbol(f), x)
+ elif function.nargs == 2:
+ x = self.walk(node.x, data)
+ y = self.walk(node.y, data)
+ return function(x, y)
+ elif l_func == "if":
+ t = self.walk(node.t, data)
+ x = self.walk(node.x, data)
+ y = self.walk(node.y, data)
+ return function(t, x, y)
+ elif l_func == "limit":
+ x = self.walk(node.x, data)
+ y = self.walk(node.y, data)
+ z = self.walk(node.z, data)
+ return function(x, y, z)
+ elif l_func in ('agauss', 'gauss'):
+ mu = self.walk(node.mu, data)
+ alpha = self.walk(node.alpha, data)
+ n = self.walk(node.n, data)
+ return function(mu, alpha, n)
+ else:
+ raise NotImplementedError("Function: {}".format(node.func));
+
+ def walk_Term(self, node, data):
+ return self.walk(node.ast, data)
+
+ def walk_AddSub(self, node, data):
+ lhs = self.walk(node.left, data)
+ if node.right is not None:
+ rhs = self.walk(node.right, data)
+ if node.op == "+":
+ return Add(lhs, rhs)
+ else:
+ return Sub(lhs, rhs)
+ else:
+ return lhs
+
+ def walk_ProdDivMod(self, node, data):
+ lhs = self.walk(node.left, data)
+ if node.right is not None:
+ rhs = self.walk(node.right, data)
+ if node.op == "*":
+ return Mul(lhs, rhs)
+ elif node.op == "/":
+ return Div(lhs, rhs)
+ else:
+ return Mod(lhs, rhs)
+ else:
+ return lhs
+
+ def walk_Sign(self, node, data):
+ operator = self.walk(node.operator, data)
+ if node.op is not None:
+ if node.op == "-":
+ if isinstance(operator, (int, float)):
+ return -operator
+ else:
+ return Neg(operator)
+ else:
+ if not isinstance(operator, (int, float)):
+ return Pos(operator)
+ return operator
+
+ def walk_Exponential(self, node, data):
+ lhs = self.walk(node.left, data)
+ if node.right is not None:
+ rhs = self.walk(node.right, data)
+ return Power(lhs, rhs)
+ else:
+ return lhs
+
+ def walk_Factor(self, node, data):
+ return self.walk(node.ast, data)
+
+ def walk_Variable(self, node, data):
+ if node.variable is None:
+ return self.walk(node.factor, data)
+ else:
+ return Symbol(node.variable)
+
+ def walk_Value(self, node, data):
+ real = 0.0
+ if node.real is not None:
+ real = self.walk(node.real, data)
+ imag = None
+ if node.imag is not None:
+ imag = self.walk(node.imag, data)
+ value = 0.0
+ if imag is None:
+ value = real
+ else:
+ value = complex(float(real), float(imag))
+ if node.unit is not None:
+ unit = self.walk(node.unit, data)
+ if type(unit) is not str:
+ if not isinstance(value, UnitValue):
+ value = PrefixedUnit.from_si_unit(unit.si_unit).new_value(value)
+ else:
+ value = PrefixedUnit.from_prefixed_unit(unit, value.power).new_value(value.value)
+ return value
+
+ def walk_ImagValue(self, node, data):
+ return self.walk(node.value, data)
+
+ def walk_RealValue(self, node, data):
+ return self.walk(node.value, data)
+
+ def walk_NumberScale(self, node, data):
+ value = ExpressionModelWalker._to_number(self.walk(node.value, data))
+ scale = node.scale
+ if scale is not None:
+ scale = normalize("NFKD", scale).lower()
+ value = PrefixedUnit(power=UnitPrefixMetaclass.get(scale)).new_value(value)
+ return value
+
+ def walk_Float(self, node, data):
+ value = ExpressionModelWalker._to_number(node.ast)
+ return value
+
+ def walk_Int(self, node, data):
+ value = int(node.ast)
+ return value
+
+ def walk_Unit(self, node, data):
+ unit = UnitMetaclass.from_prefix(node.ast.lower())
+ if unit is None:
+ unit = node.ast
+ return unit
+
+ def walk_Hz(self, node, data):
+ return UnitMetaclass.from_prefix(node.ast.lower())
+
+ def walk_Comment(self, node, data):
+ # TODO implement comments on devices
+ return node.ast
+
+ def walk_Separator(self, node, data):
+ if node.comment is not None:
+ return self.walk(node.comment, data)
+
+ def walk_NetNode(self, node, data):
+ return node.node
+
+ def walk_Filename(self, node, data):
+ return node.ast
+
+ def walk_BinaryPattern(self, node, data):
+ return ''.join(node.pattern)
+
+ def walk_closure(self, node, data):
+ return ''.join(node)
+
+ def walk_list(self, node, data):
+ return [self.walk(e, data) for e in iter(node)]
+
+ def walk_object(self, node, data):
+ raise ParseError("No walker defined for the node: {}".format(node))
+
+ @staticmethod
+ def _to_number(value):
+ if type(value) is tuple:
+ value = value[0]
+ try:
+ int_value = int(value)
+ float_value = float(value)
+ if int_value == float_value:
+ return int_value
+ else:
+ return float_value
+ except ValueError:
+ return float(value)
+
+
+class ParsingData:
+ def __init__(self):
+ self._root = None
+ self._present = None
+ self._context = []
+
+
+class ExpressionParser:
+ """ This class parse a Spice netlist file and build a syntax tree.
+
+ Public Attributes:
+
+ :attr:`circuit`
+
+ :attr:`models`
+
+ :attr:`subcircuits`
+
+ """
+
+ _logger = _module_logger.getChild('ExpressionParser')
+
+ ##############################################
+
+ _parser = parser(whitespace='', semantics=ExpressionModelBuilderSemantics())
+ _walker = ExpressionModelWalker()
+
+ def __init__(self):
+ pass
+
+ @staticmethod
+ def parse(source=None):
+ # Fixme: empty source
+
+ if source is not None:
+ raw_code = source
+ else:
+ raise ValueError("No path or source")
+
+ try:
+ model = ExpressionParser._parser.parse(raw_code)
+ except Exception as e:
+ raise ParseError(str(e)) from e
+
+ data = ParsingData()
+ expr = ExpressionParser._walker.walk(model, data)
+ return expr
+
+ @staticmethod
+ def _regenerate():
+ from PySpice.Spice import __file__ as spice_file
+ location = os.path.realpath(
+ os.path.join(os.getcwd(), os.path.dirname(spice_file)))
+ grammar_file = os.path.join(location, "expressiongrammar.ebnf")
+ with open(grammar_file, "r") as grammar_ifile:
+ grammar = grammar_ifile.read();
+ with open(grammar_file, "w") as grammar_ofile:
+ model = compile(str(grammar))
+ grammar_ofile.write(str(model))
+ python_file = os.path.join(location, "ExpressionGrammar.py")
+ python_grammar = to_python_sourcecode(grammar)
+ with open(python_file, 'w') as grammar_ofile:
+ grammar_ofile.write(python_grammar)
+ python_model = to_python_model(grammar)
+ model_file = os.path.join(location, "ExpressionModel.py")
+ with open(model_file, 'w') as model_ofile:
+ model_ofile.write(python_model)
diff --git a/PySpice/Spice/EBNFSpiceParser.py b/PySpice/Spice/EBNFSpiceParser.py
new file mode 100644
index 000000000..d312e3a79
--- /dev/null
+++ b/PySpice/Spice/EBNFSpiceParser.py
@@ -0,0 +1,2016 @@
+import logging
+import os
+import csv
+import sys
+
+from unicodedata import normalize
+from collections import OrderedDict
+from .EBNFExpressionParser import ExpressionModelWalker
+from ..Unit.Unit import UnitValue, ZeroPower, PrefixedUnit
+from ..Tools.StringTools import join_lines
+from .Expressions import *
+from .Netlist import (Circuit,
+ SubCircuit)
+from .BasicElement import (BehavioralSource,
+ BipolarJunctionTransistor,
+ Capacitor,
+ CoupledInductor,
+ CurrentSource,
+ Diode,
+ Inductor,
+ JunctionFieldEffectTransistor,
+ Mosfet,
+ Resistor,
+ SubCircuitElement,
+ VoltageControlledSwitch,
+ VoltageSource)
+from .HighLevelElement import (ExponentialCurrentSource,
+ ExponentialMixin,
+ ExponentialVoltageSource,
+ PatternCurrentSource,
+ PatternMixin,
+ PatternVoltageSource,
+ PieceWiseLinearCurrentSource,
+ PieceWiseLinearMixin,
+ PieceWiseLinearVoltageSource,
+ PulseCurrentSource,
+ PulseMixin,
+ PulseVoltageSource,
+ SinusoidalCurrentSource,
+ SinusoidalMixin,
+ SinusoidalVoltageSource,
+ SingleFrequencyFMCurrentSource,
+ SingleFrequencyFMMixin,
+ SingleFrequencyFMVoltageSource)
+
+from .SpiceGrammar import SpiceParser as parser
+from .SpiceModel import SpiceModelBuilderSemantics
+from tatsu import to_python_sourcecode, to_python_model, compile
+from tatsu.model import NodeWalker
+
+_module_logger = logging.getLogger(__name__)
+
+
+class ParseError(NameError):
+ pass
+
+
+class Statement:
+ """ This class implements a statement, in fact a line in a Spice netlist. """
+
+ @staticmethod
+ def arg_to_python(x):
+
+ if x:
+ if str(x)[0].isdigit():
+ return str(x)
+ else:
+ return "'{}'".format(x)
+ else:
+ return ''
+
+ @staticmethod
+ def args_to_python(*args):
+
+ return [Statement.arg_to_python(x) for x in args]
+
+ @staticmethod
+ def kwargs_to_python(self, **kwargs):
+ return Statement.join_args(*['{}={}'.format(key, self.value_to_python(value))
+ for key, value in kwargs.items()])
+
+ @staticmethod
+ def join_args(self, *args):
+ return ', '.join(args)
+
+
+class DataStatement(Statement):
+ """ This class implements a data definition.
+
+ Spice syntax::
+
+ .data name, name, ..., value, value, value, value...
+
+ """
+
+ ##############################################
+
+ def __init__(self, table, **parameters):
+ self._table = table
+ self._parameters = parameters
+
+ @property
+ def table(self):
+ """ Name of the model """
+ return self._table
+
+ ##############################################
+
+ @property
+ def names(self):
+ """ Name of the model """
+ return self._parameters.keys()
+
+ ##############################################
+
+ def __repr__(self):
+ return 'Data {}'.format(Statement.kwargs_to_python(**self._parameters))
+
+ ##############################################
+
+ def to_python(self, netlist_name):
+ kwargs = "{{{}}}".format(", ".join(["{} = ({})".format(param, ", ".join(values))
+ for param, values in self._parameters.items]))
+ return '{}.data({}, {})'.format(netlist_name, self._table, kwargs) + os.linesep
+
+ ##############################################
+
+ def build(self, circuit):
+ circuit.data(self._table, self._parameters)
+
+
+class IncludeStatement(Statement):
+ """ This class implements a include definition. """
+
+ ##############################################
+
+ def __init__(self, parent, filename):
+ self._include = filename
+ root, _ = os.path.split(parent.path)
+ file_name = os.path.abspath(os.path.join(root,
+ self._include.replace('"', '')))
+ if not (os.path.exists(file_name) and os.path.isfile(file_name)):
+ raise ParseError("{}: File not found: {}".format(parent.path, file_name))
+ try:
+ self._contents = SpiceParser.parse(path=file_name)
+ except Exception as e:
+ raise ParseError("{}: {:s}".format(parent.path, e))
+
+ ##############################################
+
+ def __str__(self):
+ return self._include
+
+ ##############################################
+
+ def __repr__(self):
+ return 'Include {}'.format(self._include)
+
+ ##############################################
+
+ def to_python(self, netlist_name):
+ return '{}.include({})'.format(netlist_name, self._include) + os.linesep
+
+ def contents(self):
+ return self._contents
+
+
+class ModelStatement(Statement):
+ """ This class implements a model definition.
+
+ Spice syntax::
+
+ .model mname type (pname1=pval1 pname2=pval2)
+
+ """
+
+ def __eq__(self, other):
+ return isinstance(other, ModelStatement) and self._name == other._name
+
+ def __hash__(self):
+ return hash(self._name)
+
+ ##############################################
+
+ def __init__(self, name, device, **parameters):
+ self._name = str(name).lower()
+ self._model_type = device
+ self._parameters = parameters
+
+ ##############################################
+
+ @property
+ def name(self):
+ """ Name of the model """
+ return self._name
+
+ ##############################################
+
+ def __repr__(self):
+ return 'Model {} {} {}'.format(self._name, self._model_type, self._parameters)
+
+ ##############################################
+
+ def to_python(self, netlist_name):
+ args = self.values_to_python((self._name, self._model_type))
+ kwargs = self.kwargs_to_python(self._parameters)
+ return '{}.model({})'.format(netlist_name, self.join_args(args + kwargs)) + os.linesep
+
+ ##############################################
+
+ def build(self, circuit):
+ return circuit.model(self._name, self._model_type, **self._parameters)
+
+
+####################################################################################################
+
+class ParamStatement(Statement):
+ """ This class implements a param definition.
+
+ Spice syntax::
+
+ .param name=expr
+
+ """
+
+ ##############################################
+
+ def __init__(self, **parameters):
+ self._parameters = parameters
+
+ ##############################################
+
+ @property
+ def names(self):
+ """ Name of the model """
+ return self._parameters.keys()
+
+ ##############################################
+
+ def __repr__(self):
+ return 'Param {}'.format(Statement.kwargs_to_python(**self._parameters))
+
+ ##############################################
+
+ def to_python(self, netlist_name):
+ args = self.values_to_python((self._name, self._value))
+ return '{}.param({})'.format(netlist_name, self.join_args(args)) + os.linesep
+
+ ##############################################
+
+ def build(self, circuit):
+ for key, value in self._parameters.items():
+ circuit.parameter(key, value)
+
+
+class ElementStatement(Statement):
+ """ This class implements an element definition.
+
+ "{ expression }" are allowed in device line.
+
+ """
+
+ _logger = _module_logger.getChild('Element')
+
+ ##############################################
+
+ def __init__(self, statement, name, *nodes, **params):
+ self._statement = statement
+ self._prefix = name[0]
+ self._name = name[1:]
+ self._nodes = nodes
+ self._params = params
+
+ ##############################################
+
+ @property
+ def name(self):
+ """ Name of the element """
+ return self._name
+
+ ##############################################
+
+ def __repr__(self):
+ return 'Element {0._prefix} {0._name} {0._nodes} {0._params}'.format(self)
+
+ ##############################################
+
+ def translate_ground_node(self, ground):
+
+ nodes = []
+ for idx, node in enumerate(self._nodes):
+ if str(node) == str(ground):
+ self._node[idx] = 0
+
+ return nodes
+
+ ##############################################
+
+ def to_python(self, netlist_name, ground=0):
+
+ args = self.translate_ground_node(ground)
+ args = self.values_to_python(args)
+ kwargs = self.kwargs_to_python(self._dict_parameters)
+ return '{}.{}({})'.format(netlist_name,
+ self._prefix, self.join_args(args + kwargs)) + os.linesep
+
+ def build(self, circuit, ground=0):
+ return self._statement(circuit, self._name, *self._nodes, **self._params)
+
+
+class LibraryStatement(Statement):
+ """ This class implements a library definition.
+
+ Spice syntax::
+
+ .LIB entry
+ .ENDL [entry]
+
+ """
+
+ ##############################################
+
+ def __init__(self, entry):
+ self._entry = entry
+
+ self._statements = []
+ self._subcircuits = []
+ self._models = []
+ self._params = []
+
+ ##############################################
+
+ @property
+ def entry(self):
+ """ Name of the sub-circuit. """
+ return self._entry
+
+ @property
+ def models(self):
+ """ Models of the sub-circuit. """
+ return self._models
+
+ @property
+ def params(self):
+ """ Params of the sub-circuit. """
+ return self._params
+
+ @property
+ def subcircuits(self):
+ """ Subcircuits of the sub-circuit. """
+ return self._subcircuits
+
+ ##############################################
+
+ def __repr__(self):
+ text = 'LIB {}'.format(self._entry) + os.linesep
+ text += os.linesep.join([repr(model) for model in self._models]) + os.linesep
+ text += os.linesep.join([repr(subcircuit) for subcircuit in self._subcircuits]) + os.linesep
+ text += os.linesep.join([' ' + repr(statement) for statement in self._statements])
+ return text
+
+ ##############################################
+
+ def __iter__(self):
+ """ Return an iterator on the statements. """
+ return iter(self._models + self._subcircuits + self._statements)
+
+ ##############################################
+
+ def append(self, statement):
+ """ Append a statement to the statement's list. """
+ self._statements.append(statement)
+
+ def appendModel(self, statement):
+
+ """ Append a model to the statement's list. """
+
+ self._models.append(statement)
+
+ def appendParam(self, statement):
+
+ """ Append a param to the statement's list. """
+
+ self._params.append(statement)
+
+ def appendSubCircuit(self, statement):
+
+ """ Append a model to the statement's list. """
+
+ self._subcircuits.append(statement)
+
+ ##############################################
+
+ def to_python(self, ground=0):
+
+ lib_name = 'lib_' + self._entry
+ source_code = ''
+ source_code += '{} = Lib({})'.format(lib_name, self._entry) + os.linesep
+ source_code += SpiceParser.netlist_to_python(lib_name, self, ground)
+ return source_code
+
+
+class LibCallStatement(Statement):
+ """ This class implements a library call statement.
+
+ Spice syntax::
+
+ .lib library entry
+
+ """
+
+ ##############################################
+
+ def __init__(self, library, entry):
+ self._library = library
+ self._entry = entry
+
+ ##############################################
+
+ @property
+ def name(self):
+ """ Name of the library """
+ return self._library
+
+ ##############################################
+
+ @property
+ def entry(self):
+ """ Entry in the library """
+ return self._entry
+
+ ##############################################
+
+ def __repr__(self):
+ return 'Library {} {}'.format(self._library, self._entry)
+
+ ##############################################
+
+ def to_python(self, netlist_name):
+ args = self.values_to_python((self._name, self._model_type))
+ kwargs = self.kwargs_to_python(self._parameters)
+ return '{}.include({}, {})'.format(netlist_name, self._library, self._entry) + os.linesep
+
+ ##############################################
+
+ def build(self, circuit, libraries):
+ library = libraries[self._entry]
+ for statement in library._params:
+ statement.build(circuit)
+ for statement in library._models:
+ statement.build(circuit)
+ for statement in library._subcircuits:
+ statement.build(circuit, parent=circuit)
+ return circuit
+
+
+class SubCircuitStatement(Statement):
+ """ This class implements a sub-circuit definition.
+
+ Spice syntax::
+
+ .SUBCKT name node1 ... param1=value1 ...
+
+ """
+
+ ##############################################
+ def __eq__(self, other):
+ return isinstance(other, SubCircuitStatement) and self._name == other._name
+
+ def __hash__(self):
+ return hash(self._name)
+
+ def __init__(self, name, *nodes, **params):
+
+ self._name = str(name).lower()
+ self._nodes = nodes
+ self._params = params
+
+ self._statements = []
+ self._subcircuits = OrderedDict()
+ self._models = OrderedDict()
+ self._required_subcircuits = OrderedDict()
+ self._required_models = set()
+ self._parameters = []
+ self._parent = None
+
+ ##############################################
+
+ @property
+ def name(self):
+ """ Name of the sub-circuit. """
+ return self._name
+
+ @property
+ def nodes(self):
+ """ Nodes of the sub-circuit. """
+ return self._nodes
+
+ @property
+ def models(self):
+ """ Models of the sub-circuit. """
+ return self._models
+
+ @property
+ def params(self):
+ """ Params of the sub-circuit. """
+ return self._params
+
+ @property
+ def subcircuits(self):
+ """ Subcircuits of the sub-circuit. """
+ return self._subcircuits
+
+ ##############################################
+
+ def __repr__(self):
+ if self._params:
+ text = 'SubCircuit {} {} Params: {}'.format(self._name, self._nodes, self._params) + os.linesep
+ else:
+ text = 'SubCircuit {} {}'.format(self._name, self._nodes) + os.linesep
+ text += os.linesep.join([repr(model) for model in self._models]) + os.linesep
+ text += os.linesep.join([repr(subcircuit) for subcircuit in self._subcircuits]) + os.linesep
+ text += os.linesep.join([' ' + repr(statement) for statement in self._statements])
+ return text
+
+ ##############################################
+
+ def __iter__(self):
+ """ Return an iterator on the statements. """
+ return iter(self._parameters + self._models + self._subcircuits + self._statements)
+
+ ##############################################
+
+ def append(self, statement):
+ """ Append a statement to the statement's list. """
+ self._statements.append(statement)
+ if len(statement.name) > 0 and statement.name[0] in "xX":
+ self._required_subcircuits[statement.model] = None
+
+ def appendModel(self, statement):
+
+ """ Append a model to the models list. """
+
+ self._models[statement._name] = statement
+
+ def appendParam(self, statement):
+
+ """ Append a param to the parameters list. """
+
+ self._parameters.append(statement)
+
+ def appendSubCircuit(self, statement):
+
+ """ Append a subcircuit to the subcircuits list. """
+ statement._parent = self
+ self._subcircuits[statement._name] = statement
+
+ ##############################################
+
+ def to_python(self, ground=0):
+
+ subcircuit_name = 'subcircuit_' + self._name
+ args = self.values_to_python([subcircuit_name] + self._nodes)
+ source_code = ''
+ source_code += '{} = SubCircuit({})'.format(subcircuit_name, self.join_args(args)) + os.linesep
+ source_code += SpiceParser.netlist_to_python(subcircuit_name, self, ground)
+ return source_code
+
+ ##############################################
+
+ def _build(self, ground, netlist):
+ for statement in self._parameters:
+ statement.build(netlist)
+ for name, statement in self._models.items():
+ statement.build(netlist)
+ # Check subcircuits
+ names = list(self._required_subcircuits.keys())
+ for name in names:
+ req_subcircuit = self._required_subcircuits[name]
+ if req_subcircuit is None:
+ # Search for the subcircuit
+ valid_subcircuit = self._search_build_subcircuit(name, ground, netlist)
+ if valid_subcircuit is not None:
+ self._required_subcircuits[name] = id(valid_subcircuit)
+ else:
+ self._required_subcircuits[name] = None
+ for name in names:
+ if self._required_subcircuits[name] is None:
+ raise ValueError("Subcircuit not found: {}", name)
+ for statement in self._statements:
+ if isinstance(statement, ElementStatement):
+ statement.build(netlist, ground)
+ return netlist
+
+ def build(self, ground=0, parent=None):
+ subcircuit = SubCircuit(str(self._name).lower(), *self._nodes, **self._params)
+ subcircuit.parent = parent
+ return self._build(ground, subcircuit)
+
+ def _search_build_subcircuit(self, subcircuit_name, ground=0, netlist=None):
+ # If the subcircuit is in the netlist, return the subcircuit
+ subcircuit = None
+ if netlist is not None:
+ subcircuit = netlist._search_subcircuit(subcircuit_name)
+ if subcircuit is not None:
+ return subcircuit
+ # If it is not, search for it in subcircuits
+ if subcircuit_name in self._subcircuits:
+ subcircuit = self._subcircuits[subcircuit_name]
+ # Or in a possible library
+ elif hasattr(self, '_library'):
+ if subcircuit_name in self._library._subcircuits:
+ subcircuit = self._library._subcircuits[subcircuit_name]
+ self._subcircuits[subcircuit_name] = subcircuit
+ # If a netlist has been received and the subcircuit exists
+ if netlist is not None and subcircuit is not None:
+ # Build the subcircuit
+ result = self._subcircuits[subcircuit_name].build(ground, netlist)
+ # Add the subcircuit to the netlist
+ netlist.subcircuit(result)
+ # Return the subcircuit
+ return result
+ # If no subcircuit has been fount, try the parent
+ if self._parent is None or netlist.parent is None:
+ return None
+ else:
+ return self._parent._search_build_subcircuit(subcircuit_name, ground, netlist.parent)
+
+
+class CircuitStatement(SubCircuitStatement):
+ """ This class implements a circuit definition.
+
+ Spice syntax::
+
+ Title ...
+
+ """
+
+ ##############################################
+
+ def __init__(self, title, path):
+ super(CircuitStatement, self).__init__("")
+
+ if path is not None:
+ self._path = str(path)
+ else:
+ self._path = os.getcwd()
+
+ self._title = str(title)
+
+ self._library_calls = []
+ self._libraries = {}
+ self._data = {}
+ self._library = None
+
+ ##############################################
+
+ @property
+ def path(self):
+ """ Path of the circuit. """
+ return self._path
+
+ @property
+ def title(self):
+ """ Title of the circuit. """
+ return self._title
+
+ @property
+ def name(self):
+ """ Name of the circuit. """
+ return self._title
+
+ @property
+ def libraries(self):
+ """ Libraries. """
+ return self._libraries
+
+ @property
+ def models(self):
+ """ Models of the circuit. """
+ return self._models
+
+ @property
+ def subcircuits(self):
+ """ Subcircuits of the circuit. """
+ return self._subcircuits
+
+ @property
+ def parameters(self):
+ """ Parameters of the circuit. """
+ return self._parameters
+
+ ##############################################
+
+ def __repr__(self):
+
+ text = 'Circuit {}'.format(self._title) + os.linesep
+ text += os.linesep.join([repr(library) for library in self._libraries]) + os.linesep
+ text += os.linesep.join([repr(parameter) for parameter in self._parameters]) + os.linesep
+ text += os.linesep.join([repr(model) for model in self._models]) + os.linesep
+ text += os.linesep.join([repr(subcircuit) for subcircuit in self._subcircuits]) + os.linesep
+ text += os.linesep.join([' ' + repr(statement) for statement in self._statements])
+ return text
+
+ ##############################################
+
+ def __iter__(self):
+
+ """ Return an iterator on the statements. """
+
+ return iter(self._libraries, self._parameters + self._models + self._subcircuits + self._statements)
+
+ ##############################################
+
+ def appendData(self, statement):
+
+ """ Append a model to the statement's list. """
+
+ self._data[statement.table] = statement
+
+ def appendLibrary(self, statement):
+
+ """ Append a library to the statement's list. """
+
+ self._libraries[statement.entry] = statement
+
+ def appendLibraryCall(self, statement):
+
+ """ Append a library to the statement's list. """
+
+ self._library_calls.append(statement)
+
+ ##############################################
+
+ def to_python(self, ground=0):
+
+ circuit_title = self._title
+ source_code = ''
+ source_code += '{} = Circuit({})'.format(circuit_title) + os.linesep
+ source_code += SpiceParser.netlist_to_python(circuit_title, self, ground)
+ return source_code
+
+ ##############################################
+
+ def build(self, ground=0, library=None):
+ circuit = Circuit(self._title)
+ if library is not None:
+ if self._library is None:
+ self._library = library
+ else:
+ raise ValueError("Library already assigned.")
+ if self._library is not None:
+ circuit.include(self._library)
+ return self._build(ground, circuit)
+
+
+####################################################################################################
+
+class SpiceModelWalker(ExpressionModelWalker):
+
+ def __init__(self):
+ self._functions = {"abs": Abs,
+ "agauss": AGauss,
+ "acos": ACos,
+ "acosh": ACosh,
+ "arctan": ATan,
+ "asin": ASin,
+ "asinh": ASinh,
+ "atan": ATan,
+ "atan2": ATan2,
+ "atanh": ATanh,
+ "aunif": AUnif,
+ "ceil": Ceil,
+ "cos": Cos,
+ "cosh": Cosh,
+ "db": Db,
+ "ddt": Ddt,
+ "ddx": Ddx,
+ "exp": Exp,
+ "ln": Ln,
+ "log": Ln,
+ "log10": Log10,
+ "floor": Floor,
+ "gauss": Gauss,
+ "i": I,
+ "if": If,
+ "img": Img,
+ "int": Int,
+ "limit": Limit,
+ "m": M,
+ "max": Max,
+ "min": Min,
+ "nint": NInt,
+ "ph": Ph,
+ "pow": Pow,
+ "pwr": Pow,
+ "pwrs": Pwrs,
+ "r": Re,
+ "rand": Rand,
+ "re": Re,
+ "sdt": Sdt,
+ "sgn": Sgn,
+ "sign": Sign,
+ "sin": Sin,
+ "sinh": Sinh,
+ "sqrt": Sqrt,
+ "stp": Stp,
+ "tan": Tan,
+ "tanh": Tanh,
+ "unif": Unif,
+ "uramp": URamp,
+ "v": V
+ }
+ self._relational = {
+ "<": LT,
+ "<=": LE,
+ "==": EQ,
+ "!=": NE,
+ ">=": GE,
+ ">": GT
+ }
+
+ def walk_Circuit(self, node, data):
+ if data._root is None:
+ title = self.walk(node.title, data)
+ title = join_lines(title)
+ data._root = CircuitStatement(
+ title,
+ data._path
+ )
+ data._present = data._root
+ else:
+ raise ValueError('Circuit already created: {}'.format(data._path))
+
+ self.walk(node.lines, data)
+ if len(data._context) != 0:
+ raise ParseError("Not closed hierarchy: {}".format(data._path))
+ return data._root
+
+ def walk_BJT(self, node, data):
+ device = self.walk(node.dev, data)
+ kwargs = {}
+ collector = self.walk(node.collector, data)
+ base = self.walk(node.base, data)
+ emitter = self.walk(node.emitter, data)
+ nodes = [
+ collector,
+ base,
+ emitter
+ ]
+ if node.substrate is not None:
+ substrate = self.walk(node.substrate, data)
+ nodes.append(substrate)
+ if node.thermal is not None:
+ thermal = node.thermal
+ nodes.append(thermal)
+ if node.area is not None:
+ area = self.walk(node.area, data)
+ kwargs["area"] = SpiceModelWalker._to_number(area)
+
+ model_name = self.walk(node.model, data)
+ kwargs["model"] = model_name
+ data._present._required_models.add(model_name.lower())
+
+ if node.parameters is not None:
+ parameters = self.walk(node.parameters, data)
+ kwargs.update(parameters)
+
+ data._present.append(
+ ElementStatement(
+ BipolarJunctionTransistor,
+ device,
+ *nodes,
+ **kwargs
+ )
+ )
+
+ def walk_SubstrateNode(self, node, data):
+ return node.text[1:-1]
+
+ def walk_Capacitor(self, node, data):
+ device = self.walk(node.dev, data)
+ kwargs = {}
+ if node.model is not None:
+ model_name = self.walk(node.model, data)
+ kwargs['model'] = model_name
+ data._present._required_models.add(model_name.lower())
+ value = None
+ if node.value is not None:
+ value = self.walk(node.value, data)
+ kwargs['capacitance'] = value
+ if node.parameters is not None:
+ parameters = self.walk(node.parameters, data)
+ kwargs.update(parameters)
+ if value is None:
+ if 'c' in kwargs:
+ value = kwargs.pop('c')
+ kwargs['capacitance'] = value
+ elif 'C' in kwargs:
+ value = kwargs.pop('C')
+ kwargs['capacitance'] = value
+
+
+ positive = self.walk(node.positive, data)
+ negative = self.walk(node.negative, data)
+ nodes = (
+ positive,
+ negative
+ )
+ data._present.append(
+ ElementStatement(
+ Capacitor,
+ device,
+ *nodes,
+ **kwargs
+ )
+ )
+
+ def walk_CurrentControlledCurrentSource(self, node, data):
+ device = self.walk(node.dev, data)
+ if (node.controller is None and node.dev is None and
+ node.gain is None):
+ raise ValueError("Device {} not properly defined".format(node.dev))
+ if node.controller is not None:
+ controller = self.walk(node.controller, data)
+ kwargs = {"I": controller}
+ else:
+ value = self.walk(node.gain, data)
+ kwargs = {"I": I(self.walk(node.device, data).lower())*value}
+
+ positive = self.walk(node.positive, data)
+ negative = self.walk(node.negative, data)
+ nodes = (
+ positive,
+ negative
+ )
+ data._present.append(
+ ElementStatement(
+ BehavioralSource,
+ device,
+ *nodes,
+ **kwargs
+ )
+ )
+
+ def walk_CurrentControlledVoltageSource(self, node, data):
+ device = self.walk(node.dev, data)
+ if (node.controller is None and node.dev is None and
+ node.gain is None):
+ raise ValueError("Device {} not properly defined".format(node.dev))
+ if node.controller is not None:
+ controller = self.walk(node.controller, data)
+ kwargs = {"V": controller}
+ else:
+ value = self.walk(node.transresistance, data)
+ kwargs = {"V": I(self.walk(node.device, data).lower())*value}
+
+ positive = self.walk(node.positive, data)
+ negative = self.walk(node.negative, data)
+ nodes = (
+ positive,
+ negative
+ )
+ data._present.append(
+ ElementStatement(
+ BehavioralSource,
+ device,
+ *nodes,
+ **kwargs
+ )
+ )
+
+ def walk_CurrentSource(self, node, data):
+ device = self.walk(node.dev, data)
+ kwargs = {}
+ element = CurrentSource
+ if node.dc_value is not None:
+ kwargs['dc_value'] = self.walk(node.dc_value, data)
+ if node.ac_magnitude is not None:
+ kwargs['ac_magnitude'] = self.walk(node.ac_magnitude, data)
+ if node.ac_phase is not None:
+ kwargs['ac_phase'] = self.walk(node.ac_phase, data)
+ if node.transient is not None:
+ transient = self.walk(node.transient, data)
+ if transient[0] == ExponentialMixin:
+ element = ExponentialCurrentSource
+ elif transient[0] == PatternMixin:
+ element = PatternCurrentSource
+ elif transient[0] == PieceWiseLinearMixin:
+ element = PieceWiseLinearCurrentSource
+ elif transient[0] == PulseMixin:
+ element = PulseCurrentSource
+ elif transient[0] == SingleFrequencyFMMixin:
+ element = SingleFrequencyFMCurrentSource
+ elif transient[0] == SinusoidalMixin:
+ element = SinusoidalCurrentSource
+ else:
+ raise ParseError("Unknown transient: {}".format(transient[0]))
+ kwargs.update(transient[1])
+
+ positive = self.walk(node.positive, data)
+ negative = self.walk(node.negative, data)
+ nodes = (
+ positive,
+ negative
+ )
+ data._present.append(
+ ElementStatement(
+ element,
+ device,
+ *nodes,
+ **kwargs
+ )
+ )
+
+ def walk_Diode(self, node, data):
+ device = self.walk(node.dev, data)
+ kwargs = {}
+ if node.model is None:
+ raise ValueError("The device {} has no model".format(node.dev))
+ else:
+ model_name = self.walk(node.model, data)
+ kwargs['model'] = model_name
+ data._present._required_models.add(model_name.lower())
+ if node.area is not None:
+ area = self.walk(node.area, data)
+ kwargs['area'] = area
+
+ positive = self.walk(node.positive, data)
+ negative = self.walk(node.negative, data)
+ nodes = (
+ positive,
+ negative
+ )
+ data._present.append(
+ ElementStatement(
+ Diode,
+ device,
+ *nodes,
+ **kwargs
+ )
+ )
+
+ def walk_Inductor(self, node, data):
+ device = self.walk(node.dev, data)
+ kwargs = {}
+ if node.model is not None:
+ model_name = self.walk(node.model, data)
+ kwargs['model'] = model_name
+ data._present._required_models.add(model_name.lower())
+ value = None
+ if node.value is not None:
+ value = self.walk(node.value, data)
+ kwargs['inductance'] = value
+ if node.parameters is not None:
+ parameters = self.walk(node.parameters, data)
+ kwargs.update(parameters)
+ if value is None:
+ if 'l' in kwargs:
+ value = kwargs.pop('l')
+ kwargs['inductance'] = value
+ elif 'L' in kwargs:
+ value = kwargs.pop('L')
+ kwargs['inductance'] = value
+
+ positive = self.walk(node.positive, data)
+ negative = self.walk(node.negative, data)
+ nodes = (
+ positive,
+ negative
+ )
+ data._present.append(
+ ElementStatement(
+ Inductor,
+ device,
+ *nodes,
+ **kwargs
+ )
+ )
+
+ def walk_JFET(self, node, data):
+ device = self.walk(node.dev, data)
+ kwargs = {}
+ if node.model is None:
+ raise ValueError("The device {} has no model".format(node.dev))
+ else:
+ model_name = self.walk(node.model, data)
+ kwargs["model"] = model_name
+ data._present._required_models.add(model_name.lower())
+ if node.area is not None:
+ area = self.walk(node.area, data)
+ kwargs["area"] = area
+ if node.parameters is not None:
+ parameters = self.walk(node.parameters, data)
+ kwargs.update(parameters)
+
+ drain = self.walk(node.drain, data)
+ gate = self.walk(node.gate, data)
+ source = self.walk(node.source, data)
+ nodes = [
+ drain,
+ gate,
+ source
+ ]
+ data._present.append(
+ ElementStatement(
+ JunctionFieldEffectTransistor,
+ device,
+ *nodes,
+ **kwargs
+ )
+ )
+
+ def walk_MOSFET(self, node, data):
+ device = self.walk(node.dev, data)
+ kwargs = {}
+ if node.model is None:
+ raise ValueError("The device {} has no model".format(node.dev))
+ else:
+ model_name = self.walk(node.model, data)
+ kwargs["model"] = model_name
+ data._present._required_models.add(model_name.lower())
+ if node.param is not None:
+ if isinstance(node.param, list):
+ # The separators are not taken into account
+ for parameter in node.param:
+ if isinstance(parameter, list):
+ kwargs[parameter[0]] = self.walk(parameter[2][::2], data)
+ else:
+ kwargs.update(self.walk(parameter, data))
+ else:
+ kwargs.update(self.walk(node.param, data))
+ drain = self.walk(node.drain, data)
+ gate = self.walk(node.gate, data)
+ source = self.walk(node.source, data)
+ bulk = self.walk(node.bulk, data)
+ nodes = [
+ drain,
+ gate,
+ source,
+ bulk
+ ]
+ data._present.append(
+ ElementStatement(
+ Mosfet,
+ device,
+ *nodes,
+ **kwargs
+ )
+ )
+
+ def walk_MutualInductor(self, node, data):
+ device = self.walk(node.dev, data)
+ kwargs = {}
+ if node.model is not None:
+ model_name = self.walk(node.model, data)
+ kwargs['model'] = model_name
+ data._present._required_models.add(model_name.lower())
+ inductors = self.walk(node.inductor, data)
+ if len(inductors) != 2:
+ raise ParseError("Presently, only two inductors are allowed.")
+ inductor1 = inductors[0]
+ inductor2 = inductors[1]
+ coupling_factor = self.walk(node.value, data)
+ kwargs["inductor1"] = inductor1
+ kwargs["inductor2"] = inductor2
+ kwargs["coupling_factor"] = coupling_factor
+
+ data._present.append(
+ ElementStatement(
+ CoupledInductor,
+ device,
+ **kwargs
+ )
+ )
+
+ def walk_NonLinearDependentSource(self, node, data):
+ device = self.walk(node.dev, data)
+ positive = self.walk(node.positive, data)
+ negative = self.walk(node.negative, data)
+ nodes = (
+ positive,
+ negative
+ )
+ expr = self.walk(node.expr, data)
+ kwargs = {}
+ if node.parameters is not None:
+ parameters = self.walk(node.parameters, data)
+ kwargs.update(parameters)
+ if node.magnitude == "V":
+ kwargs["voltage_expression"] = expr
+ else:
+ kwargs["current_expression"] = expr
+
+ data._present.append(
+ ElementStatement(
+ BehavioralSource,
+ device,
+ *nodes,
+ **kwargs
+ )
+ )
+
+ def walk_Resistor(self, node, data):
+ device = self.walk(node.dev, data)
+ kwargs = {}
+ if node.model is not None:
+ model_name = self.walk(node.model, data)
+ kwargs['model'] = model_name
+ data._present._required_models.add(model_name.lower())
+ value = None
+ if node.value is not None:
+ value = self.walk(node.value, data)
+ kwargs['resistance'] = value
+ if node.parameters is not None:
+ parameters = self.walk(node.parameters, data)
+ kwargs.update(parameters)
+ if value is None:
+ if 'r' in kwargs:
+ value = kwargs.pop('r')
+ kwargs['resistance'] = value
+ elif 'R' in kwargs:
+ value = kwargs.pop('R')
+ kwargs['resistance'] = value
+
+ positive = self.walk(node.positive, data)
+ negative = self.walk(node.negative, data)
+ nodes = (
+ positive,
+ negative
+ )
+ data._present.append(
+ ElementStatement(
+ Resistor,
+ device,
+ *nodes,
+ **kwargs
+ )
+ )
+
+ def walk_Subcircuit(self, node, data):
+ device = self.walk(node.dev, data)
+ node_node = self.walk(node.node, data)
+ if node.params is not None:
+ subcircuit_name = node_node[-2]
+ nodes = node_node[:-2]
+ else:
+ subcircuit_name = node_node[-1]
+ nodes = node_node[:-1]
+ kwargs = {}
+ if node.parameters is not None:
+ parameters = self.walk(node.parameters, data)
+ kwargs.update(parameters)
+ data._present._required_subcircuits[subcircuit_name.lower()] = None
+ data._present.append(
+ ElementStatement(
+ SubCircuitElement,
+ device,
+ subcircuit_name,
+ *nodes,
+ **kwargs
+ )
+ )
+
+ def walk_Switch(self, node, data):
+ device = self.walk(node.dev, data)
+ kwargs = {}
+ if node.model is not None:
+ model_name = self.walk(node.model, data)
+ kwargs['model'] = model_name
+ data._present._required_models.add(model_name.lower())
+ if node.initial_state is not None:
+ kwargs['initial_state'] = node.initial_state
+
+ positive = self.walk(node.positive, data)
+ negative = self.walk(node.negative, data)
+ if node.control_p is not None:
+ if node.control_n is not None:
+ control_p = self.walk(node.control_p, data)
+ control_n = self.walk(node.control_n, data)
+ nodes = (
+ positive,
+ negative,
+ control_p,
+ control_n
+ )
+ else:
+ raise ValueError("Only one control node defined")
+ else:
+ if node.control_n is None:
+ nodes = (
+ positive,
+ negative
+ )
+ data._present.append(
+ ElementStatement(
+ VoltageControlledSwitch,
+ device,
+ *nodes,
+ **kwargs
+ )
+ )
+
+ def walk_VoltageControlledCurrentSource(self, node, data):
+ device = self.walk(node.dev, data)
+ if node.controller is None and node.nodes is None:
+ raise ValueError("Device {} not properly defined".format(node.dev))
+ if node.controller is not None:
+ controller = self.walk(node.controller, data)
+ kwargs = {"I": controller}
+ else:
+ value = self.walk(node.transconductance, data)
+ nodes = self.walk(node.nodes, data)
+ if len(nodes) != 2:
+ raise ValueError("Device {} not properly defined".format(node.dev))
+ ctrl_p = nodes[0]
+ ctrl_n = nodes[1]
+ kwargs = {"I": V(ctrl_p, ctrl_n) * value}
+
+ positive = self.walk(node.positive, data)
+ negative = self.walk(node.negative, data)
+ nodes = (
+ positive,
+ negative
+ )
+ data._present.append(
+ ElementStatement(
+ BehavioralSource,
+ device,
+ *nodes,
+ **kwargs
+ )
+ )
+
+ def walk_VoltageControlledVoltageSource(self, node, data):
+ device = self.walk(node.dev, data)
+ if node.controller is not None:
+ controller = self.walk(node.controller, data)
+ kwargs = {"V": controller}
+ else:
+ value = self.walk(node.transconductance, data)
+ nodes = self.walk(node.nodes, data)
+ if len(nodes) != 2:
+ raise ValueError("Device {} not properly defined".format(node.dev))
+ ctrl_p = nodes[0]
+ ctrl_n = nodes[1]
+ kwargs = {"V": V(ctrl_p, ctrl_n) * value}
+
+ positive = self.walk(node.positive, data)
+ negative = self.walk(node.negative, data)
+ nodes = (
+ positive,
+ negative
+ )
+ data._present.append(
+ ElementStatement(
+ BehavioralSource,
+ device,
+ *nodes,
+ **kwargs
+ )
+ )
+
+ def walk_VoltageSource(self, node, data):
+ device = self.walk(node.dev, data)
+ kwargs = {}
+ element = VoltageSource
+ if node.dc_value is not None:
+ kwargs['dc_value'] = self.walk(node.dc_value, data)
+ if node.ac_magnitude is not None:
+ kwargs['ac_magnitude'] = self.walk(node.ac_magnitude, data)
+ if node.ac_phase is not None:
+ kwargs['ac_phase'] = self.walk(node.ac_phase, data)
+ if node.transient is not None:
+ transient = self.walk(node.transient, data)
+ if transient[0] == ExponentialMixin:
+ element = ExponentialVoltageSource
+ elif transient[0] == PatternMixin:
+ element = PatternVoltageSource
+ elif transient[0] == PieceWiseLinearMixin:
+ element = PieceWiseLinearVoltageSource
+ elif transient[0] == PulseMixin:
+ element = PulseVoltageSource
+ elif transient[0] == SingleFrequencyFMMixin:
+ element = SingleFrequencyFMVoltageSource
+ elif transient[0] == SinusoidalMixin:
+ element = SinusoidalVoltageSource
+ else:
+ raise ParseError("Unknown transient: {}".format(transient[0]))
+ kwargs.update(transient[1])
+
+ positive = self.walk(node.positive, data)
+ negative = self.walk(node.negative, data)
+ nodes = (
+ positive,
+ negative
+ )
+ data._present.append(
+ ElementStatement(
+ element,
+ device,
+ *nodes,
+ **kwargs
+ )
+ )
+
+
+ def walk_ControlVoltagePoly(self, node, data):
+ controllers = self.walk(node.value, data)
+ positive = self.walk(node.positive, data)
+ negative = self.walk(node.negative, data)
+ if len(positive) < controllers or len(negative) < controllers:
+ raise ValueError(
+ "The number of control nodes is smaller than the expected controllers: {}".format(controllers))
+
+ ctrl_pos = positive[:controllers]
+ ctrl_neg = negative[:controllers]
+
+ values = []
+ if len(positive) > controllers:
+ if isinstance(positive, list):
+ values_pos = positive[controllers:]
+ else:
+ values_pos = [positive]
+ if isinstance(negative, list):
+ values_neg = negative[controllers:]
+ else:
+ values_neg = [negative]
+ values += [SpiceModelWalker._to_number(val)
+ for pair in zip(values_pos, values_neg)
+ for val in pair]
+ if node.coefficient:
+ coefficients = self.walk(node.coefficient, data)
+ if isinstance(coefficients, list):
+ values.extend(coefficients)
+ else:
+ values.append(coefficients)
+ controllers = [V(*nodes)
+ for nodes in zip(ctrl_pos,
+ ctrl_neg)]
+ return Poly(controllers, values)
+
+ def walk_ControlCurrentPoly(self, node, data):
+ controllers = self.walk(node.value, data)
+ if len(node.device) < controllers:
+ raise ValueError(
+ "The number of control nodes is smaller than the expected controllers: {}".format(controllers))
+
+ ctrl_dev = [self.walk(dev, data)
+ for dev in node.device[:controllers]]
+
+ values = []
+ if controllers > len(node.device):
+ values = [SpiceModelWalker._to_number(self.walk(value, data))
+ for value in node.device[controllers:]]
+ if node.coefficient:
+ coefficients = self.walk(node.coefficient, data)
+ if isinstance(coefficients, list):
+ values.extend(coefficients)
+ else:
+ values.append(coefficients)
+ controllers = [I(dev) for dev in ctrl_dev]
+ return Poly(controllers, values)
+
+ def walk_ControlTable(self, node, data):
+ return Table(self.walk(node.expr, data),
+ list(zip(self.walk(node.input, data),
+ self.walk(node.output, data))))
+
+ def walk_ControlValue(self, node, data):
+ return self.walk(node.expression, data)
+
+ def walk_TransientSpecification(self, node, data):
+ return self.walk(node.ast, data)
+
+ def walk_TransientPulse(self, node, data):
+ parameters = dict([(key, value)
+ for value, key in zip(self.walk(node.ast, data),
+ ("initial_value",
+ "pulse_value",
+ "delay_time",
+ "rise_time",
+ "fall_time",
+ "pulse_width",
+ "period",
+ "phase"))])
+ return PulseMixin, parameters
+
+ def walk_PulseArguments(self, node, data):
+ v1 = self.walk(node.v1, data)
+ value = []
+ if node.value is not None:
+ value = self.walk(node.value, data)
+ if isinstance(value, list):
+ return [v1] + value
+ else:
+ return [v1, value]
+
+ def walk_TransientPWL(self, node, data):
+ data, parameters = self.walk(node.ast, data)
+ keys = list(parameters.keys())
+ low_keys = [key.lower() for key in keys]
+ if 'r' in low_keys:
+ idx = low_keys.index('r')
+ key = keys[idx]
+ repeat_time = parameters.pop(key)
+ parameters['repeat_time'] = repeat_time
+ if 'td' in low_keys:
+ idx = low_keys.index('td')
+ key = keys[idx]
+ time_delay = parameters.pop(key)
+ parameters['time_delay'] = time_delay
+ if isinstance(data, list):
+ parameters.update({"values": data})
+ else:
+ curdir = os.path.abspath(os.curdir)
+ datapath, filename = os.path.split(data)
+ if curdir.endswith(datapath):
+ curdir += os.sep + filename
+ else:
+ curdir = os.path.abspath(data)
+
+ with open(curdir) as ifile:
+ ext = os.path.splitext(curdir)[1]
+ reader = csv.reader(ifile, delimiter=',' if ext.lower() == ".csv" else ' ')
+ data = [(SpiceModelWalker._to_number(t),
+ SpiceModelWalker._to_number(value))
+ for t, value in reader]
+ parameters.update({"values": data})
+ return PieceWiseLinearMixin, parameters
+
+ def walk_PWLArguments(self, node, data):
+ t = self.walk(node.t, data)
+ value = self.walk(node.value, data)
+ parameters = {}
+ if node.parameters is not None:
+ parameters = self.walk(node.parameters, data)
+ return list(zip(t, value)), parameters
+
+ def walk_PWLFileArguments(self, node, data):
+ filename = self.walk(node.filename, data)
+ parameters = {}
+ if node.parameters is not None:
+ parameters = self.walk(node.parameters, data)
+ return filename, parameters
+
+ def walk_TransientSin(self, node, data):
+ parameters = dict([(key, value)
+ for value, key in zip(self.walk(node.ast, data),
+ ("offset",
+ "amplitude",
+ "frequency",
+ "delay",
+ "damping_factor"))])
+ return SinusoidalMixin, parameters
+
+ def walk_SinArguments(self, node, data):
+ v0 = self.walk(node.v0, data)
+ va = self.walk(node.va, data)
+ freq = self.walk(node.freq, data)
+ value = []
+ if node.value is not None:
+ value = self.walk(node.value, data)
+ if isinstance(value, list):
+ return [v0, va, freq] + value
+ else:
+ return [v0, va, freq, value]
+
+ def walk_TransientPat(self, node, data):
+ parameters = dict([(key, value)
+ for value, key in zip(self.walk(node.ast, data),
+ ("high_value",
+ "low_value",
+ "delay_time",
+ "rise_time",
+ "fall_time",
+ "bit_period",
+ "bit_pattern",
+ "repeat"))])
+ return PatternMixin, parameters
+
+ def walk_PatArguments(self, node, data):
+ vhi = self.walk(node.vhi, data)
+ vlo = self.walk(node.vlo, data)
+ td = self.walk(node.td, data)
+ tr = self.walk(node.tr, data)
+ tf = self.walk(node.tf, data)
+ tsample = self.walk(node.tsample, data)
+ data = self.walk(node.data, data)
+ repeat = False
+ if node.repeat is not None:
+ repeat = (node.repeat == '1')
+ return [vhi, vlo, td, tr, tf, tsample, data, repeat]
+
+ def walk_TransientExp(self, node, data):
+ parameters = dict([(key, value)
+ for value, key in zip(self.walk(node.ast, data),
+ ("initial_amplitude",
+ "amplitude",
+ "rise_delay_time",
+ "rise_time_constant",
+ "delay_fall_time",
+ "fall_time_constant"))])
+ return ExponentialMixin, parameters
+
+ def walk_ExpArguments(self, node, data):
+ v1 = self.walk(node.v1, data)
+ v2 = self.walk(node.v2, data)
+ if node.value is not None:
+ value = self.walk(node.value, data)
+ if isinstance(value, list):
+ return [v1, v2] + value
+ else:
+ return [v1, v2, value]
+
+ def walk_TransientSFFM(self, node, data):
+ parameters = dict([(key, value)
+ for value, key in zip(self.walk(node.ast, data),
+ ("offset",
+ "amplitude",
+ "carrier_frequency",
+ "modulation_index",
+ "signal_frequency"))])
+ return SingleFrequencyFMMixin, parameters
+
+ def walk_SFFMArguments(self, node, data):
+ v0 = self.walk(node.v0, data)
+ va = self.walk(node.va, data)
+ if node.value is not None:
+ value = self.walk(node.value, data)
+ if isinstance(value, list):
+ return [v0, va] + value
+ else:
+ return [v0, va, value]
+
+ def walk_ACCmd(self, node, data):
+ return node.text
+
+ def walk_DataCmd(self, node, data):
+ table = node.table
+ names = node.name
+ values = self.walk(node.value, data)
+ if len(values) % len(names) != 0:
+ raise ValueError("The number of elements per parameter do not match (line: {})".format(node.line))
+ parameters = dict([(name, [value for value in values[idx::len(names)]])
+ for idx, name in enumerate(names)])
+ data._root.appendData(
+ DataStatement(
+ table,
+ **parameters
+ )
+ )
+
+ def walk_DCCmd(self, node, data):
+ return node.text
+
+ def walk_IncludeCmd(self, node, data):
+ filename = self.walk(node.filename, data)
+ include = IncludeStatement(
+ data._root,
+ filename
+ )
+ # The include statement makes available all the parameters, models and
+ # subcircuits in the file.
+ for inc_parameters in include._contents._parameters:
+ for parameters in data._present._parameters:
+ for name in inc_parameters.names:
+ if name in parameters.names:
+ raise ValueError("Duplicated parameter name {} in include file: {}".format(name, filename))
+ data._present._parameters.append(inc_parameters)
+
+ for model in include._contents._models:
+ if model not in data._present._models:
+ data._present._models[model] = include._contents._models[model]
+ else:
+ raise ValueError("Duplicated model name {} in include file: {}".format(model.name, filename))
+ for name, subcircuit in include._contents._subcircuits.items():
+ if name not in data._present._subcircuits:
+ data._present._subcircuits[name] = subcircuit
+ else:
+ raise ValueError("Duplicated subcircuit name {} in include file: {}".format(subcircuit.name, filename))
+
+ def walk_ICCmd(self, node, data):
+ return node.text
+
+ def walk_ModelCmd(self, node, data):
+ name = self.walk(node.name, data)
+ device = node.type
+ if node.parameters is not None:
+ parameters = self.walk(node.parameters, data)
+ else:
+ parameters = {}
+
+ data._present.appendModel(
+ ModelStatement(
+ name,
+ device,
+ **parameters
+ )
+ )
+
+ def walk_ModelName(self, node, data):
+ return node.name.lower()
+
+ def walk_ParamCmd(self, node, data):
+ if node.parameters is not None:
+ parameters = self.walk(node.parameters, data)
+ else:
+ parameters = {}
+
+ data._present.appendParam(
+ ParamStatement(**parameters)
+ )
+
+ def walk_LibCmd(self, node, data):
+ if node.block is not None:
+ self.walk(node.block, data)
+ else:
+ self.walk(node.call, data)
+
+ def walk_LibBlock(self, node, data):
+ entries = node.entry
+ if len(entries) == 2:
+ if entries[0] != entries[1]:
+ raise NameError(
+ 'Begin and end library entries differ: {} != {}'.format(*entries))
+ entries = entries[0]
+ library = LibraryStatement(entries)
+ data._context.append(data._present)
+ data._present = library
+ self.walk(node.lines, data)
+ tmp = data._context.pop()
+ tmp.appendLibrary(data._present)
+ data._present = tmp
+
+ def walk_LibCall(self, node, data):
+ entries = node.entry
+ if len(entries) == 2:
+ if entries[0] != entries[1]:
+ raise NameError(
+ 'Begin and end library entries differ: {} != {}'.format(*entries))
+ entries = entries[0]
+ filename = self.walk(node.filename, data)
+ data._present.appendLibraryCall(
+ LibCallStatement(filename, entries)
+ )
+
+ def walk_SimulatorCmd(self, node, data):
+ return node.simulator
+
+ def walk_SubcktCmd(self, node, data):
+ name = self.walk(node.name, data)
+ if isinstance(name, list) and len(name) == 2:
+ if name[0] != name[1]:
+ raise NameError(
+ 'Begin and end library entries differ (file:{}, line:{}): {} != {}'.format(self._path, node.parseinfo.line,
+ *name))
+ name = name[0]
+ nodes = self.walk(node.node, data)
+ parameters = None
+ if node.parameters is not None:
+ parameters = self.walk(node.parameters, data)
+
+ if nodes is None:
+ if parameters is None:
+ subckt = SubCircuitStatement(name)
+ else:
+ subckt = SubCircuitStatement(name, **parameters)
+ else:
+ if parameters is None:
+ subckt = SubCircuitStatement(name, *nodes)
+ else:
+ subckt = SubCircuitStatement(name, *nodes, **parameters)
+ data._context.append(data._present)
+ data._present = subckt
+ self.walk(node.lines, data)
+ tmp = data._context.pop()
+ tmp.appendSubCircuit(data._present)
+ data._present = tmp
+
+ def walk_TitleCmd(self, node, data):
+ if id(data._root) == id(data._present):
+ data._root._title = self.walk(node.title)
+ else:
+ raise SyntaxError(".Title command can only be used in the root circuit.")
+ return data._root
+
+ def walk_Lines(self, node, data):
+ return self.walk(node.ast, data)
+
+ def walk_CircuitLine(self, node, data):
+ return self.walk(node.ast, data)
+
+ def walk_NetlistLines(self, node, data):
+ return self.walk(node.ast, data)
+
+ def walk_NetlistLine(self, node, data):
+ return self.walk(node.ast, data)
+
+ def walk_Parameters(self, node, data):
+ result = {}
+ # The separators are not taken into account
+ for parameter in self.walk(node.ast, data):
+ result.update(parameter)
+ return result
+
+ def walk_Parameter(self, node, data):
+ value = self.walk(node.value, data)
+ return {node.name.lower(): value}
+
+ def walk_ParenthesisNodes(self, node, data):
+ return self.walk(node.ast, data)
+
+ def walk_CircuitNodes(self, node, data):
+ return self.walk_list(node.ast, data)
+ def walk_Comment(self, node, data):
+ # TODO implement comments on devices
+ return node.ast
+
+ def walk_Separator(self, node, data):
+ if node.comment is not None:
+ return self.walk(node.comment, data)
+
+ def walk_Device(self, node, data):
+ # Conversion of controlled devices to the B device names
+ if node.ast[0] in ("E", "F", "G", "H"):
+ return "B" + node.ast
+ else:
+ return node.ast
+
+ def walk_Command(self, node, data):
+ return self.walk(node.ast, data)
+
+ def walk_NetlistCmds(self, node, data):
+ return self.walk(node.ast, data)
+
+ def walk_TableFile(self, node, data):
+ filename = self.walk(node.filename, data)
+ return TableFile(filename)
+
+
+
+class ParsingData:
+ def __init__(self, filename):
+ self._path = filename
+ self._root = None
+ self._present = None
+ self._context = []
+
+
+class SpiceParser:
+ """ This class parse a Spice netlist file and build a syntax tree.
+
+ Public Attributes:
+
+ :attr:`circuit`
+
+ :attr:`models`
+
+ :attr:`subcircuits`
+
+ """
+
+ _logger = _module_logger.getChild('SpiceParser')
+
+ ##############################################
+
+ _parser = parser(whitespace='', semantics=SpiceModelBuilderSemantics())
+ _walker = SpiceModelWalker()
+
+ def __init__(self):
+ pass
+
+ @staticmethod
+ def parse(path=None, source=None, library=None):
+ # Fixme: empty source
+
+ if path is not None:
+ with open(str(path), 'rb') as f:
+ raw_code = f.read().decode('utf-8')
+ elif source is not None:
+ raw_code = source
+ else:
+ raise ValueError("No path or source")
+
+ try:
+ model = SpiceParser._parser.parse(raw_code)
+ except Exception as e:
+ if path is not None:
+ raise ParseError("{}: ".format(path) + str(e)) from e
+ else:
+ raise ParseError(str(e)) from e
+
+ if path is None:
+ path = os.getcwd()
+ data = ParsingData(path)
+ circuit = SpiceParser._walker.walk(model, data)
+ if library is not None:
+ circuit._library = library
+
+ return circuit
+
+ @staticmethod
+ def _regenerate():
+ from PySpice.Spice import __file__ as spice_file
+ location = os.path.realpath(
+ os.path.join(os.getcwd(), os.path.dirname(spice_file)))
+ grammar_file = os.path.join(location, "spicegrammar.ebnf")
+ with open(grammar_file, "r") as grammar_ifile:
+ grammar = grammar_ifile.read()
+ with open(grammar_file, "w") as grammar_ofile:
+ model = compile(str(grammar))
+ grammar_ofile.write(str(model))
+ python_file = os.path.join(location, "SpiceGrammar.py")
+ python_grammar = to_python_sourcecode(grammar)
+ with open(python_file, 'w') as grammar_ofile:
+ grammar_ofile.write(python_grammar)
+ python_model = to_python_model(grammar)
+ model_file = os.path.join(location, "SpiceModel.py")
+ with open(model_file, 'w') as model_ofile:
+ model_ofile.write(python_model)
+
+ @property
+ def circuit(self):
+ """ Circuit statements. """
+ return self._circuit
+
+ @property
+ def models(self):
+ """ Models of the sub-circuit. """
+ return self._circuit.models
+
+ @property
+ def subcircuits(self):
+ """ Subcircuits of the sub-circuit. """
+ return self._circuit.subcircuits
+
+ @property
+ def parameters(self):
+ """ Subcircuits of the sub-circuit. """
+ return self._circuit.params
+
+ ##############################################
+
+ def is_only_subcircuit(self):
+ return bool(not self._circuit and self.subcircuits)
+
+ ##############################################
+
+ def is_only_model(self):
+ return bool(not self.circuit and not self.subcircuits and self.models)
+
+ ##############################################
+
+ @staticmethod
+ def _build_circuit(circuit, statements, ground):
+
+ for statement in statements:
+ if isinstance(statement, IncludeStatement):
+ circuit.include(str(statement))
+
+ for statement in statements:
+ if isinstance(statement, ElementStatement):
+ statement.build(circuit, ground)
+ elif isinstance(statement, ModelStatement):
+ statement.build(circuit)
+ elif isinstance(statement, SubCircuitStatement):
+ subcircuit = statement.build(ground) # Fixme: ok ???
+ circuit.subcircuit(subcircuit)
+
+ ##############################################
+
+ def build_circuit(self, ground=0):
+
+ """Build a :class:`Circuit` instance.
+
+ Use the *ground* parameter to specify the node which must be translated to 0 (SPICE ground node).
+
+ """
+
+ # circuit = Circuit(str(self._title))
+ circuit = self._circuit.build(str(ground))
+ return circuit
+
+ ##############################################
+
+ @staticmethod
+ def netlist_to_python(netlist_name, statements, ground=0):
+
+ source_code = ''
+ for statement in statements:
+ if isinstance(statement, ElementStatement):
+ source_code += statement.to_python(netlist_name, ground)
+ elif isinstance(statement, LibraryStatement):
+ source_code += statement.to_python(netlist_name)
+ elif isinstance(statement, ModelStatement):
+ source_code += statement.to_python(netlist_name)
+ elif isinstance(statement, SubCircuitStatement):
+ source_code += statement.to_python(netlist_name)
+ elif isinstance(statement, IncludeStatement):
+ source_code += statement.to_python(netlist_name)
+ return source_code
+
+ ##############################################
+
+ def to_python_code(self, ground=0):
+
+ ground = str(ground)
+
+ source_code = ''
+
+ if self.circuit:
+ source_code += "circuit = Circuit('{}')".format(self._title) + os.linesep
+ source_code += self.netlist_to_python('circuit', self._statements, ground)
+
+ return source_code
diff --git a/PySpice/Spice/ElementParameter.py b/PySpice/Spice/ElementParameter.py
index 19e70d578..7be06aa26 100644
--- a/PySpice/Spice/ElementParameter.py
+++ b/PySpice/Spice/ElementParameter.py
@@ -24,8 +24,10 @@
####################################################################################################
-from ..Unit import Unit
+from ..Unit.Unit import UnitValue, PrefixedUnit
from ..Tools.StringTools import str_spice
+from .Expressions import Expression, Symbol
+from .EBNFExpressionParser import ExpressionParser
####################################################################################################
@@ -49,6 +51,7 @@ def __init__(self, default=None):
self._default_value = default
self._attribute_name = None
+ self._unit = None
##############################################
@@ -76,7 +79,7 @@ def __get__(self, instance, owner=None):
##############################################
def __set__(self, instance, value):
- setattr(instance, '_' + self._attribute_name, value)
+ setattr(instance, '_' + self._attribute_name, self.validate(value))
##############################################
@@ -109,6 +112,27 @@ def to_str(self, instance):
def __lt__(self, other):
return self._attribute_name < other.attribute_name
+ def _validate_float(self, value):
+ if self._unit is None:
+ return float(value)
+ if isinstance(value, type(self._unit)):
+ return value
+ if type(value) is str:
+ value = ExpressionParser.parse(value)
+ if isinstance(value, Symbol):
+ if type(value.value) is str:
+ return value
+ value = value.value
+ if isinstance(value, Expression):
+ return value
+ if isinstance(value, UnitValue):
+ if value.prefixed_unit.is_unit_less:
+ unit = PrefixedUnit(self._unit.unit, value.prefixed_unit.power)
+ return unit.new_value(value.value)
+ elif value.prefixed_unit.unit == self._unit.unit:
+ return value
+ return self._unit.new_value(value)
+
####################################################################################################
class PositionalElementParameter(ParameterDescriptor):
@@ -147,7 +171,14 @@ def key_parameter(self):
##############################################
def to_str(self, instance):
- return str_spice(self.__get__(instance))
+
+ if bool(self):
+ value = self.__get__(instance)
+ if isinstance(value, Expression):
+ return '{%s}' % value
+ return str_spice(value)
+ else:
+ return ''
##############################################
@@ -174,7 +205,9 @@ class ExpressionPositionalParameter(PositionalElementParameter):
##############################################
def validate(self, value):
- return str(value)
+ if isinstance(value, Expression):
+ return value
+ return ExpressionParser.parse(value)
####################################################################################################
@@ -192,11 +225,7 @@ def __init__(self, position, unit=None, **kwargs):
##############################################
def validate(self, value):
-
- if isinstance(value, Unit):
- return value
- else:
- return Unit(value)
+ return self._validate_float(value)
####################################################################################################
@@ -295,7 +324,10 @@ def str_value(self, instance):
def to_str(self, instance):
if bool(self):
- return '{}={}'.format(self.spice_name, self.str_value(instance))
+ value = self.str_value(instance)
+ if isinstance(value, Expression):
+ value = '{%s}' % value
+ return '{}={}'.format(self.spice_name, value)
else:
return ''
@@ -328,7 +360,9 @@ class ExpressionKeyParameter(KeyValueParameter):
##############################################
def validate(self, value):
- return str(value)
+ if isinstance(value, Expression):
+ return value
+ return ExpressionParser.parse(value)
####################################################################################################
@@ -346,7 +380,7 @@ def __init__(self, spice_name, unit=None, **kwargs):
##############################################
def validate(self, value):
- return float(value)
+ return self._validate_float(value)
####################################################################################################
@@ -359,7 +393,7 @@ class FloatPairKeyParameter(KeyValueParameter):
def validate(self, pair):
if len(pair) == 2:
- return (float(pair[0]), float(pair[1]))
+ return (self._validate_float(pair[0]), self._validate_float(pair[1]))
else:
raise ValueError()
@@ -379,7 +413,7 @@ class FloatTripletKeyParameter(FloatPairKeyParameter):
def validate(self, uplet):
if len(uplet) == 3:
- return (float(uplet[0]), float(uplet[1]), float(uplet[2]))
+ return (self._validate_float(uplet[0]), self._validate_float(uplet[1]), self._validate_float(uplet[2]))
else:
raise ValueError()
diff --git a/PySpice/Spice/ExpressionGrammar.py b/PySpice/Spice/ExpressionGrammar.py
new file mode 100644
index 000000000..8c3fd3f73
--- /dev/null
+++ b/PySpice/Spice/ExpressionGrammar.py
@@ -0,0 +1,1760 @@
+#!/usr/bin/env python
+
+# CAVEAT UTILITOR
+#
+# This file was automatically generated by TatSu.
+#
+# https://pypi.python.org/pypi/tatsu/
+#
+# Any changes you make to it will be overwritten the next time
+# the file is generated.
+
+from __future__ import annotations
+
+import sys
+
+from tatsu.buffering import Buffer
+from tatsu.parsing import Parser
+from tatsu.parsing import tatsumasu
+from tatsu.parsing import leftrec, nomemo, isname # noqa
+from tatsu.infos import ParserConfig
+from tatsu.util import re, generic_main # noqa
+
+
+KEYWORDS = {} # type: ignore
+
+
+class ExpressionBuffer(Buffer):
+ def __init__(self, text, /, config: ParserConfig = None, **settings):
+ config = ParserConfig.new(
+ config,
+ owner=self,
+ whitespace=None,
+ nameguard=None,
+ comments_re=None,
+ eol_comments_re=None,
+ ignorecase=True,
+ namechars='',
+ parseinfo=True,
+ )
+ config = config.replace(**settings)
+ super().__init__(text, config=config)
+
+
+class ExpressionParser(Parser):
+ def __init__(self, /, config: ParserConfig = None, **settings):
+ config = ParserConfig.new(
+ config,
+ owner=self,
+ whitespace=None,
+ nameguard=None,
+ comments_re=None,
+ eol_comments_re=None,
+ ignorecase=True,
+ namechars='',
+ parseinfo=True,
+ keywords=KEYWORDS,
+ start='start',
+ )
+ config = config.replace(**settings)
+ super().__init__(config=config)
+
+ @tatsumasu('SpiceExpression')
+ @nomemo
+ def _start_(self): # noqa
+ self._gen_expr_()
+
+ @tatsumasu('GenericExpression')
+ @nomemo
+ def _gen_expr_(self): # noqa
+ with self._choice():
+ with self._option():
+ self._braced_expression_()
+ self.name_last_node('braced')
+ with self._option():
+ self._value_()
+ self.name_last_node('value')
+ self._error(
+ 'expecting one of: '
+ ' '
+ ' '
+ )
+
+ @tatsumasu('BracedExpression')
+ @nomemo
+ def _braced_expression_(self): # noqa
+ with self._choice():
+ with self._option():
+ self._lc_()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._expression_()
+ self.name_last_node('@')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._rc_()
+
+ self._define(
+ ['sep'],
+ []
+ )
+ with self._option():
+ self._expression_()
+ self.name_last_node('@')
+ self._error(
+ 'expecting one of: '
+ "'{' "
+ )
+
+ @tatsumasu('Expression')
+ @leftrec
+ def _expression_(self): # noqa
+ with self._choice():
+ with self._option():
+ self._ternary_()
+ self.name_last_node('ternary')
+ with self._option():
+ self._term_()
+ self.name_last_node('term')
+ self._error(
+ 'expecting one of: '
+ ' '
+ ' '
+ )
+
+ @tatsumasu('Ternary')
+ @nomemo
+ def _ternary_(self): # noqa
+ self._conditional_expression_()
+ self.name_last_node('t')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._token('?')
+ self.name_last_node('op')
+ self._cut()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._expression_()
+ self.name_last_node('x')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._token(':')
+ self._cut()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._expression_()
+ self.name_last_node('y')
+
+ self._define(
+ ['op', 'sep', 't', 'x', 'y'],
+ []
+ )
+
+ @tatsumasu('Conditional')
+ @nomemo
+ def _conditional_expression_(self): # noqa
+ self._boolean_or_()
+ self.name_last_node('expr')
+
+ @tatsumasu('Or')
+ @nomemo
+ def _boolean_or_(self): # noqa
+ self._boolean_xor_()
+ self.name_last_node('left')
+ with self._optional():
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._token('|')
+ self.name_last_node('op')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._boolean_or_()
+ self.name_last_node('right')
+
+ self._define(
+ ['op', 'right', 'sep'],
+ []
+ )
+
+ self._define(
+ ['left', 'op', 'right', 'sep'],
+ []
+ )
+
+ @tatsumasu('Xor')
+ @nomemo
+ def _boolean_xor_(self): # noqa
+ self._boolean_and_()
+ self.name_last_node('left')
+ with self._optional():
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._token('^')
+ self.name_last_node('op')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._boolean_xor_()
+ self.name_last_node('right')
+
+ self._define(
+ ['op', 'right', 'sep'],
+ []
+ )
+
+ self._define(
+ ['left', 'op', 'right', 'sep'],
+ []
+ )
+
+ @tatsumasu('And')
+ @nomemo
+ def _boolean_and_(self): # noqa
+ self._boolean_not_()
+ self.name_last_node('left')
+ with self._optional():
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._token('&')
+ self.name_last_node('op')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._boolean_and_()
+ self.name_last_node('right')
+
+ self._define(
+ ['op', 'right', 'sep'],
+ []
+ )
+
+ self._define(
+ ['left', 'op', 'right', 'sep'],
+ []
+ )
+
+ @tatsumasu('Not')
+ @nomemo
+ def _boolean_not_(self): # noqa
+ with self._optional():
+ self._token('~')
+ self.name_last_node('op')
+ self._relational_()
+ self.name_last_node('operator')
+
+ self._define(
+ ['op', 'operator'],
+ []
+ )
+
+ @tatsumasu('Relational')
+ @nomemo
+ def _relational_(self): # noqa
+ with self._choice():
+ with self._option():
+ self._expression_()
+ self.name_last_node('left')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ with self._group():
+ with self._choice():
+ with self._option():
+ self._token('==')
+ with self._option():
+ self._token('!=')
+ with self._option():
+ self._token('>=')
+ with self._option():
+ self._token('<=')
+ with self._option():
+ self._token('>')
+ with self._option():
+ self._token('<')
+ self._error(
+ 'expecting one of: '
+ "'!=' '<' '<=' '==' '>' '>='"
+ )
+ self.name_last_node('op')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._expression_()
+ self.name_last_node('right')
+
+ self._define(
+ ['left', 'op', 'right', 'sep'],
+ []
+ )
+ with self._option():
+ self._conditional_factor_()
+ self.name_last_node('factor')
+ self._error(
+ 'expecting one of: '
+ ' '
+ ''
+ ' '
+ ' '
+ )
+
+ @tatsumasu('ConditionalFactor')
+ def _conditional_factor_(self): # noqa
+ with self._choice():
+ with self._option():
+ self._lp_()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._conditional_expression_()
+ self.name_last_node('expr')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._rp_()
+
+ self._define(
+ ['expr', 'sep'],
+ []
+ )
+ with self._option():
+ self._boolean_()
+ self.name_last_node('boolean')
+ self._error(
+ 'expecting one of: '
+ "'(' 'FALSE' 'TRUE' "
+ )
+
+ @tatsumasu('Term')
+ def _term_(self): # noqa
+ self._add_sub_()
+ self.name_last_node('@')
+
+ @tatsumasu('AddSub')
+ def _add_sub_(self): # noqa
+ self._prod_()
+ self.name_last_node('left')
+ with self._optional():
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ with self._group():
+ with self._choice():
+ with self._option():
+ self._token('+')
+ with self._option():
+ self._token('-')
+ self._error(
+ 'expecting one of: '
+ "'+' '-'"
+ )
+ self.name_last_node('op')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._add_sub_()
+ self.name_last_node('right')
+
+ self._define(
+ ['op', 'right', 'sep'],
+ []
+ )
+
+ self._define(
+ ['left', 'op', 'right', 'sep'],
+ []
+ )
+
+ @tatsumasu('ProdDivMod')
+ def _prod_(self): # noqa
+ self._unary_()
+ self.name_last_node('left')
+ with self._optional():
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ with self._group():
+ with self._choice():
+ with self._option():
+ self._token('*')
+ with self._option():
+ self._token('/')
+ with self._option():
+ self._token('%')
+ self._error(
+ 'expecting one of: '
+ "'%' '*' '/'"
+ )
+ self.name_last_node('op')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._prod_()
+ self.name_last_node('right')
+
+ self._define(
+ ['op', 'right', 'sep'],
+ []
+ )
+
+ self._define(
+ ['left', 'op', 'right', 'sep'],
+ []
+ )
+
+ @tatsumasu('Sign')
+ def _unary_(self): # noqa
+ with self._optional():
+ with self._group():
+ with self._choice():
+ with self._option():
+ self._token('+')
+ with self._option():
+ self._token('-')
+ self._error(
+ 'expecting one of: '
+ "'+' '-'"
+ )
+ self.name_last_node('op')
+ self._exp_()
+ self.name_last_node('operator')
+
+ self._define(
+ ['op', 'operator'],
+ []
+ )
+
+ @tatsumasu('Exponential')
+ def _exp_(self): # noqa
+ self._functional_()
+ self.name_last_node('left')
+ with self._optional():
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._token('**')
+ self.name_last_node('op')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._exp_()
+ self.name_last_node('right')
+
+ self._define(
+ ['op', 'right', 'sep'],
+ []
+ )
+
+ self._define(
+ ['left', 'op', 'right', 'sep'],
+ []
+ )
+
+ @tatsumasu('Functional')
+ def _functional_(self): # noqa
+ with self._choice():
+ with self._option():
+ self._functions_()
+ self.name_last_node('@')
+ with self._option():
+ self._variable_()
+ self.name_last_node('@')
+ self._error(
+ 'expecting one of: '
+ ' '
+ ' '
+ ' '
+ ' '
+ )
+
+ @tatsumasu('Variable')
+ def _variable_(self): # noqa
+ with self._choice():
+ with self._option():
+ self._lc_()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._var_id_()
+ self.name_last_node('variable')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._rc_()
+
+ self._define(
+ ['sep', 'variable'],
+ []
+ )
+ with self._option():
+ self._var_id_()
+ self.name_last_node('variable')
+ with self._option():
+ self._factor_()
+ self.name_last_node('factor')
+ self._error(
+ 'expecting one of: '
+ "'{' "
+ '[a-zA-Z] [a-zA-Z_`@#\\$][a-zA-Z0-'
+ '9_:`@#\\.\\$]*[a-zA-Z0-9_`@#\\.\\$]'
+ )
+
+ @tatsumasu('Factor')
+ def _factor_(self): # noqa
+ with self._choice():
+ with self._option():
+ self._lp_()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._expression_()
+ self.name_last_node('@')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._rp_()
+
+ self._define(
+ ['sep'],
+ []
+ )
+ with self._option():
+ self._value_()
+ self.name_last_node('@')
+ self._error(
+ 'expecting one of: '
+ "'(' "
+ ''
+ )
+
+ @tatsumasu('Functions')
+ def _functions_(self): # noqa
+ with self._choice():
+ with self._option():
+ self._functions_1_()
+ with self._option():
+ self._atan2_()
+ with self._option():
+ self._ddx_()
+ with self._option():
+ self._gauss_()
+ with self._option():
+ self._if_func_()
+ with self._option():
+ self._limit_()
+ with self._option():
+ self._functions_2_()
+ with self._option():
+ self._rand_()
+ with self._option():
+ self._unif_()
+ with self._option():
+ self._i_func_()
+ with self._option():
+ self._v_func_()
+ self._error(
+ 'expecting one of: '
+ "'Img' 'Ph' 'R' 'Re' 'abs' 'acos' 'acosh'"
+ "'agauss' 'arctan' 'asin' 'asinh' 'atan'"
+ "'atan2' 'atanh' 'aunif' 'ceil' 'cos'"
+ "'cosh' 'ddt' 'ddx' 'exp' 'floor' 'gauss'"
+ "'i' 'if' 'int' 'limit' 'ln' 'log'"
+ "'log10' 'm' 'max' 'min' 'nint' 'pow'"
+ "'pwr' 'pwrs' 'rand' 'sdt' 'sgn' 'sign'"
+ "'sin' 'sinh' 'sqrt' 'stp' 'tan' 'tanh'"
+ "'unif' 'uramp' 'v' "
+ ' '
+ ''
+ )
+
+ @tatsumasu()
+ def _functions_1_(self): # noqa
+ with self._group():
+ with self._choice():
+ with self._option():
+ self._token('abs')
+ with self._option():
+ self._token('ceil')
+ with self._option():
+ self._token('ddt')
+ with self._option():
+ self._token('floor')
+ with self._option():
+ self._token('int')
+ with self._option():
+ self._token('m')
+ with self._option():
+ self._token('nint')
+ with self._option():
+ self._token('sdt')
+ with self._option():
+ self._token('sgn')
+ with self._option():
+ self._token('stp')
+ with self._option():
+ self._token('sqrt')
+ with self._option():
+ self._token('uramp')
+ with self._option():
+ self._token('Ph')
+ with self._option():
+ self._token('Re')
+ with self._option():
+ self._token('R')
+ with self._option():
+ self._token('Img')
+ with self._option():
+ self._token('acosh')
+ with self._option():
+ self._token('acos')
+ with self._option():
+ self._token('asinh')
+ with self._option():
+ self._token('asin')
+ with self._option():
+ self._token('arctan')
+ with self._option():
+ self._token('atanh')
+ with self._option():
+ self._token('atan')
+ with self._option():
+ self._token('cosh')
+ with self._option():
+ self._token('cos')
+ with self._option():
+ self._token('exp')
+ with self._option():
+ self._token('ln')
+ with self._option():
+ self._token('log')
+ with self._option():
+ self._token('log10')
+ with self._option():
+ self._token('sinh')
+ with self._option():
+ self._token('sin')
+ with self._option():
+ self._token('tanh')
+ with self._option():
+ self._token('tan')
+ self._error(
+ 'expecting one of: '
+ "'Img' 'Ph' 'R' 'Re' 'abs' 'acos' 'acosh'"
+ "'arctan' 'asin' 'asinh' 'atan' 'atanh'"
+ "'ceil' 'cos' 'cosh' 'ddt' 'exp' 'floor'"
+ "'int' 'ln' 'log' 'log10' 'm' 'nint'"
+ "'sdt' 'sgn' 'sin' 'sinh' 'sqrt' 'stp'"
+ "'tan' 'tanh' 'uramp'"
+ )
+ self.name_last_node('func')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._lp_()
+ self._cut()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._expression_()
+ self.name_last_node('x')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._rp_()
+
+ self._define(
+ ['func', 'sep', 'x'],
+ []
+ )
+
+ @tatsumasu()
+ def _atan2_(self): # noqa
+ self._token('atan2')
+ self.name_last_node('func')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._lp_()
+ self._cut()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._expression_()
+ self.name_last_node('y')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._comma_()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._expression_()
+ self.name_last_node('x')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._rp_()
+
+ self._define(
+ ['func', 'sep', 'x', 'y'],
+ []
+ )
+
+ @tatsumasu()
+ def _ddx_(self): # noqa
+ self._token('ddx')
+ self.name_last_node('func')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._lp_()
+ self._cut()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._id_()
+ self.name_last_node('f')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._comma_()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._expression_()
+ self.name_last_node('x')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._rp_()
+
+ self._define(
+ ['f', 'func', 'sep', 'x'],
+ []
+ )
+
+ @tatsumasu()
+ def _gauss_(self): # noqa
+ with self._group():
+ with self._choice():
+ with self._option():
+ self._token('agauss')
+ with self._option():
+ self._token('gauss')
+ self._error(
+ 'expecting one of: '
+ "'agauss' 'gauss'"
+ )
+ self.name_last_node('func')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._lp_()
+ self._cut()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._expression_()
+ self.name_last_node('mu')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._comma_()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._expression_()
+ self.name_last_node('alpha')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._comma_()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._expression_()
+ self.name_last_node('n')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._rp_()
+
+ self._define(
+ ['alpha', 'func', 'mu', 'n', 'sep'],
+ []
+ )
+
+ @tatsumasu()
+ def _i_func_(self): # noqa
+ self._token('i')
+ self.name_last_node('func')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._lp_()
+ self._cut()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ with self._if():
+ self._token('V')
+ self._dev_()
+ self.name_last_node('device')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._rp_()
+
+ self._define(
+ ['device', 'func', 'sep'],
+ []
+ )
+
+ @tatsumasu()
+ def _if_func_(self): # noqa
+ self._token('if')
+ self.name_last_node('func')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._lp_()
+ self._cut()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._conditional_expression_()
+ self.name_last_node('t')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._comma_()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._expression_()
+ self.name_last_node('x')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._comma_()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._expression_()
+ self.name_last_node('y')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._rp_()
+
+ self._define(
+ ['func', 'sep', 't', 'x', 'y'],
+ []
+ )
+
+ @tatsumasu()
+ def _limit_(self): # noqa
+ self._token('limit')
+ self.name_last_node('func')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._lp_()
+ self._cut()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._expression_()
+ self.name_last_node('x')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._comma_()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._expression_()
+ self.name_last_node('y')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._comma_()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._expression_()
+ self.name_last_node('z')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._rp_()
+
+ self._define(
+ ['func', 'sep', 'x', 'y', 'z'],
+ []
+ )
+
+ @tatsumasu()
+ def _functions_2_(self): # noqa
+ with self._group():
+ with self._choice():
+ with self._option():
+ self._token('min')
+ with self._option():
+ self._token('max')
+ with self._option():
+ self._token('pwrs')
+ with self._option():
+ self._token('pow')
+ with self._option():
+ self._token('pwr')
+ with self._option():
+ self._token('sign')
+ self._error(
+ 'expecting one of: '
+ "'max' 'min' 'pow' 'pwr' 'pwrs' 'sign'"
+ )
+ self.name_last_node('func')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._lp_()
+ self._cut()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._expression_()
+ self.name_last_node('x')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._comma_()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._expression_()
+ self.name_last_node('y')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._rp_()
+
+ self._define(
+ ['func', 'sep', 'x', 'y'],
+ []
+ )
+
+ @tatsumasu()
+ def _rand_(self): # noqa
+ self._token('rand')
+ self.name_last_node('func')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._lp_()
+ self._cut()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._rp_()
+
+ self._define(
+ ['func', 'sep'],
+ []
+ )
+
+ @tatsumasu()
+ def _unif_(self): # noqa
+ with self._group():
+ with self._choice():
+ with self._option():
+ self._token('aunif')
+ with self._option():
+ self._token('unif')
+ self._error(
+ 'expecting one of: '
+ "'aunif' 'unif'"
+ )
+ self.name_last_node('func')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._lp_()
+ self._cut()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._expression_()
+ self.name_last_node('mu')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._comma_()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._expression_()
+ self.name_last_node('alpha')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._rp_()
+
+ self._define(
+ ['alpha', 'func', 'mu', 'sep'],
+ []
+ )
+
+ @tatsumasu()
+ def _v_func_(self): # noqa
+ self._token('v')
+ self.name_last_node('func')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._lp_()
+ self._cut()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._node_()
+ self.name_last_node('node')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ with self._optional():
+ self._comma_()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._node_()
+ self.name_last_node('node')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+
+ self._define(
+ ['node', 'sep'],
+ []
+ )
+ self._rp_()
+
+ self._define(
+ ['func', 'node', 'sep'],
+ []
+ )
+
+ @tatsumasu()
+ def _special_variables_(self): # noqa
+ with self._choice():
+ with self._option():
+ self._token('time')
+ with self._option():
+ self._token('temper')
+ with self._option():
+ self._token('temp')
+ with self._option():
+ self._token('freq')
+ with self._option():
+ self._token('vt')
+ with self._option():
+ self._token('pi')
+ self._error(
+ 'expecting one of: '
+ "'freq' 'pi' 'temp' 'temper' 'time' 'vt'"
+ )
+
+ @tatsumasu('Value')
+ def _value_(self): # noqa
+ with self._group():
+ with self._choice():
+ with self._option():
+ with self._group():
+ self._real_value_()
+ self.name_last_node('real')
+ self._token('+')
+ self._imag_value_()
+ self.name_last_node('imag')
+
+ self._define(
+ ['imag', 'real'],
+ []
+ )
+ with self._option():
+ self._imag_value_()
+ self.name_last_node('imag')
+ with self._option():
+ self._real_value_()
+ self.name_last_node('real')
+ self._error(
+ 'expecting one of: '
+ ' '
+ )
+ with self._optional():
+ with self._choice():
+ with self._option():
+ self._hz_()
+ with self._option():
+ self._unit_()
+ self._error(
+ 'expecting one of: '
+ ' '
+ )
+ self.name_last_node('unit')
+
+ self._define(
+ ['imag', 'real', 'unit'],
+ []
+ )
+
+ @tatsumasu('ImagValue')
+ def _imag_value_(self): # noqa
+ self._number_scale_()
+ self.name_last_node('value')
+ self._token('J')
+
+ self._define(
+ ['value'],
+ []
+ )
+
+ @tatsumasu('RealValue')
+ def _real_value_(self): # noqa
+ self._number_scale_()
+ self.name_last_node('value')
+
+ @tatsumasu()
+ def _freq_value_(self): # noqa
+ self._number_scale_()
+ self.name_last_node('value')
+ with self._optional():
+ self._hz_()
+ self.name_last_node('unit')
+
+ self._define(
+ ['unit', 'value'],
+ []
+ )
+
+ @tatsumasu('NumberScale')
+ def _number_scale_(self): # noqa
+ with self._choice():
+ with self._option():
+ self._floating_point_()
+ self.name_last_node('value')
+ with self._group():
+ with self._choice():
+ with self._option():
+ self._meg_()
+ with self._option():
+ with self._optional():
+ self._suffix_()
+ self._error(
+ 'expecting one of: '
+ ' '
+ )
+ self.name_last_node('scale')
+
+ self._define(
+ ['scale', 'value'],
+ []
+ )
+ with self._option():
+ self._integer_()
+ self.name_last_node('value')
+ with self._group():
+ with self._choice():
+ with self._option():
+ self._meg_()
+ with self._option():
+ with self._optional():
+ self._suffix_()
+ self._error(
+ 'expecting one of: '
+ ' '
+ )
+ self.name_last_node('scale')
+
+ self._define(
+ ['scale', 'value'],
+ []
+ )
+ self._error(
+ 'expecting one of: '
+ '([\\+\\-]?(([0-9]+(\\.[0-9]*)?)|(\\.[0-'
+ '9]+))([eE][\\-\\+]?[0-9]{1,3})?)'
+ '([\\+\\-]?[0-9]+) '
+ ''
+ )
+
+ @tatsumasu()
+ def _suffix_(self): # noqa
+ self._pattern('[tTgGkKmMxXuUnNpPfFµ]')
+
+ @tatsumasu()
+ def _meg_(self): # noqa
+ self._pattern('[mM][eE][gG]')
+
+ @tatsumasu('Unit')
+ def _unit_(self): # noqa
+ self._pattern('[a-zA-Z%]+')
+
+ @tatsumasu('Hz')
+ def _hz_(self): # noqa
+ self._pattern('[Hh][Zz]')
+
+ @tatsumasu()
+ def _lead_name_(self): # noqa
+ self._pattern('I[SDGBEC1-9]')
+
+ @tatsumasu('Float')
+ def _floating_point_(self): # noqa
+ self._pattern('([\\+\\-]?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))([eE][\\-\\+]?[0-9]{1,3})?)')
+
+ @tatsumasu('Int')
+ def _integer_(self): # noqa
+ self._pattern('([\\+\\-]?[0-9]+)')
+
+ @tatsumasu()
+ def _digit_(self): # noqa
+ self._pattern('[0-9]')
+
+ @tatsumasu()
+ def _boolean_(self): # noqa
+ with self._choice():
+ with self._option():
+ self._token('TRUE')
+ with self._option():
+ self._token('FALSE')
+ self._error(
+ 'expecting one of: '
+ "'FALSE' 'TRUE'"
+ )
+
+ @tatsumasu('BinaryPattern')
+ def _binary_pattern_(self): # noqa
+ self._pattern('[Bb]')
+
+ def block1():
+ self._binary_()
+ self._positive_closure(block1)
+ self.name_last_node('pattern')
+
+ self._define(
+ ['pattern'],
+ []
+ )
+
+ @tatsumasu()
+ def _binary_(self): # noqa
+ self._pattern('[01]')
+
+ @tatsumasu('Device')
+ def _dev_(self): # noqa
+ self._pattern('[a-zA-Z\\$][a-zA-Z0-9_:!`@#\\.\\+\\-\\$]*')
+
+ @tatsumasu('NetNode')
+ def _node_(self): # noqa
+ with self._group():
+ with self._choice():
+ with self._option():
+ self._pattern('[a-zA-Z0-9_\\[\\$\\/\\+\\-][a-zA-Z0-9_:\\$\\-`~!@#%&_\\+|<>\\?\\.\\\\|\\^\\*\\/]*[a-zA-Z0-9_\\$\\-`~!@#%&_\\+|<>\\?\\.\\\\|\\^\\*\\]\\/]')
+ with self._option():
+ self._pattern('[a-zA-Z0-9_]')
+ self._error(
+ 'expecting one of: '
+ '[a-zA-Z0-9_\\[\\$\\/\\+\\-][a-zA-Z0-9_:\\$\\-'
+ '`~!@#%&_\\+|<>\\?\\.\\|\\^\\*\\/]*[a-zA-Z0-'
+ '9_\\$\\-`~!@#%&_\\+|<>\\?\\.\\|\\^\\*\\]\\/]'
+ '[a-zA-Z0-9_]'
+ )
+ self.name_last_node('node')
+ with self._ifnot():
+ with self._group():
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._token('=')
+
+ self._define(
+ ['sep'],
+ []
+ )
+
+ self._define(
+ ['node', 'sep'],
+ []
+ )
+
+ @tatsumasu()
+ def _id_(self): # noqa
+ with self._choice():
+ with self._option():
+ self._pattern('[a-zA-Z_`@#\\$][a-zA-Z0-9_:`@#\\.\\$\\/]*[a-zA-Z0-9_`@#\\.\\$]')
+ with self._option():
+ self._pattern('[a-zA-Z_`@#\\$]')
+ self._error(
+ 'expecting one of: '
+ '[a-zA-Z_`@#\\$] [a-zA-Z_`@#\\$][a-zA-Z0-'
+ '9_:`@#\\.\\$\\/]*[a-zA-Z0-9_`@#\\.\\$]'
+ )
+
+ @tatsumasu()
+ def _var_id_(self): # noqa
+ with self._choice():
+ with self._option():
+ self._pattern('[a-zA-Z_`@#\\$][a-zA-Z0-9_:`@#\\.\\$]*[a-zA-Z0-9_`@#\\.\\$]')
+ with self._option():
+ self._pattern('[a-zA-Z]')
+ self._error(
+ 'expecting one of: '
+ '[a-zA-Z] [a-zA-Z_`@#\\$][a-zA-Z0-'
+ '9_:`@#\\.\\$]*[a-zA-Z0-9_`@#\\.\\$]'
+ )
+
+ @tatsumasu()
+ def _end_sep_(self): # noqa
+ with self._choice():
+ with self._option():
+ self._cmd_net_sep_()
+ self.name_last_node('@')
+
+ def block2():
+ self._st_()
+ self._closure(block2)
+ with self._option():
+
+ def block3():
+ self._st_()
+ self._positive_closure(block3)
+ self._error(
+ 'expecting one of: '
+ ' '
+ ' [ \\t]'
+ )
+
+ @tatsumasu()
+ def _sep_(self): # noqa
+ with self._choice():
+ with self._option():
+
+ def block1():
+ self._cmd_net_sep_()
+ self.name_last_node('@')
+
+ def block3():
+ self._st_()
+ self._closure(block3)
+ self._token('+')
+
+ def block4():
+ self._st_()
+ self._closure(block4)
+ self._positive_closure(block1)
+ with self._option():
+
+ def block5():
+ self._st_()
+ self._positive_closure(block5)
+ self._error(
+ 'expecting one of: '
+ ' '
+ ' [ \\t]'
+ )
+
+ @tatsumasu('Separator')
+ def _cmd_net_sep_(self): # noqa
+
+ def block0():
+ self._st_()
+ self._closure(block0)
+ with self._optional():
+ self._inline_comment_()
+ self.name_last_node('@')
+ self.name_last_node('comment')
+ self._newline_()
+
+ def block3():
+
+ def block4():
+ self._st_()
+ self._closure(block4)
+ with self._optional():
+ with self._choice():
+ with self._option():
+ self._line_comment_()
+ self.name_last_node('@')
+ with self._option():
+ self._inline_comment_()
+ self.name_last_node('@')
+ self._error(
+ 'expecting one of: '
+ ' '
+ )
+ self.name_last_node('comment')
+ self._newline_()
+
+ self._define(
+ ['comment'],
+ []
+ )
+ self._closure(block3)
+
+ self._define(
+ ['comment'],
+ []
+ )
+
+ @tatsumasu()
+ def _inline_comment_(self): # noqa
+ self._semicolon_()
+
+ def block0():
+ self._st_()
+ self._closure(block0)
+ self._text_()
+ self.name_last_node('@')
+
+ @tatsumasu()
+ def _line_comment_(self): # noqa
+ self._asterisk_()
+
+ def block0():
+ self._st_()
+ self._closure(block0)
+ self._text_()
+ self.name_last_node('@')
+
+ @tatsumasu('Comment')
+ def _text_(self): # noqa
+ self._pattern('[^\\r\\n]*')
+
+ @tatsumasu()
+ def _asterisk_(self): # noqa
+ self._token('*')
+
+ @tatsumasu()
+ def _question_mark_(self): # noqa
+ self._token('?')
+
+ @tatsumasu()
+ def _colon_(self): # noqa
+ self._token(':')
+
+ @tatsumasu()
+ def _semicolon_(self): # noqa
+ self._token(';')
+
+ @tatsumasu()
+ def _comma_(self): # noqa
+ self._token(',')
+
+ @tatsumasu()
+ def _dot_(self): # noqa
+ self._token('.')
+
+ @tatsumasu()
+ def _dollar_(self): # noqa
+ self._token('\\$')
+
+ @tatsumasu()
+ def _double_bar_(self): # noqa
+ self._token('//')
+
+ @tatsumasu()
+ def _single_quote_(self): # noqa
+ self._token("'")
+
+ @tatsumasu()
+ def _double_quote_(self): # noqa
+ self._token('"')
+
+ @tatsumasu()
+ def _lc_(self): # noqa
+ self._token('{')
+
+ @tatsumasu()
+ def _rc_(self): # noqa
+ self._token('}')
+
+ @tatsumasu()
+ def _lp_(self): # noqa
+ self._token('(')
+
+ @tatsumasu()
+ def _rp_(self): # noqa
+ self._token(')')
+
+ @tatsumasu()
+ def _newline_(self): # noqa
+ self._pattern('[\\r\\n]')
+
+ @tatsumasu()
+ def _st_(self): # noqa
+ self._pattern('[ \\t]')
+
+ @tatsumasu()
+ def _ws_(self): # noqa
+ self._pattern('[^\\S\\r\\n]*')
+
+
+class ExpressionSemantics:
+ def start(self, ast): # noqa
+ return ast
+
+ def gen_expr(self, ast): # noqa
+ return ast
+
+ def braced_expression(self, ast): # noqa
+ return ast
+
+ def expression(self, ast): # noqa
+ return ast
+
+ def ternary(self, ast): # noqa
+ return ast
+
+ def conditional_expression(self, ast): # noqa
+ return ast
+
+ def boolean_or(self, ast): # noqa
+ return ast
+
+ def boolean_xor(self, ast): # noqa
+ return ast
+
+ def boolean_and(self, ast): # noqa
+ return ast
+
+ def boolean_not(self, ast): # noqa
+ return ast
+
+ def relational(self, ast): # noqa
+ return ast
+
+ def conditional_factor(self, ast): # noqa
+ return ast
+
+ def term(self, ast): # noqa
+ return ast
+
+ def add_sub(self, ast): # noqa
+ return ast
+
+ def prod(self, ast): # noqa
+ return ast
+
+ def unary(self, ast): # noqa
+ return ast
+
+ def exp(self, ast): # noqa
+ return ast
+
+ def functional(self, ast): # noqa
+ return ast
+
+ def variable(self, ast): # noqa
+ return ast
+
+ def factor(self, ast): # noqa
+ return ast
+
+ def functions(self, ast): # noqa
+ return ast
+
+ def functions_1(self, ast): # noqa
+ return ast
+
+ def atan2(self, ast): # noqa
+ return ast
+
+ def ddx(self, ast): # noqa
+ return ast
+
+ def gauss(self, ast): # noqa
+ return ast
+
+ def i_func(self, ast): # noqa
+ return ast
+
+ def if_func(self, ast): # noqa
+ return ast
+
+ def limit(self, ast): # noqa
+ return ast
+
+ def functions_2(self, ast): # noqa
+ return ast
+
+ def rand(self, ast): # noqa
+ return ast
+
+ def unif(self, ast): # noqa
+ return ast
+
+ def v_func(self, ast): # noqa
+ return ast
+
+ def special_variables(self, ast): # noqa
+ return ast
+
+ def value(self, ast): # noqa
+ return ast
+
+ def imag_value(self, ast): # noqa
+ return ast
+
+ def real_value(self, ast): # noqa
+ return ast
+
+ def freq_value(self, ast): # noqa
+ return ast
+
+ def number_scale(self, ast): # noqa
+ return ast
+
+ def suffix(self, ast): # noqa
+ return ast
+
+ def meg(self, ast): # noqa
+ return ast
+
+ def unit(self, ast): # noqa
+ return ast
+
+ def hz(self, ast): # noqa
+ return ast
+
+ def lead_name(self, ast): # noqa
+ return ast
+
+ def floating_point(self, ast): # noqa
+ return ast
+
+ def integer(self, ast): # noqa
+ return ast
+
+ def digit(self, ast): # noqa
+ return ast
+
+ def boolean(self, ast): # noqa
+ return ast
+
+ def binary_pattern(self, ast): # noqa
+ return ast
+
+ def binary(self, ast): # noqa
+ return ast
+
+ def dev(self, ast): # noqa
+ return ast
+
+ def node(self, ast): # noqa
+ return ast
+
+ def id(self, ast): # noqa
+ return ast
+
+ def var_id(self, ast): # noqa
+ return ast
+
+ def end_sep(self, ast): # noqa
+ return ast
+
+ def sep(self, ast): # noqa
+ return ast
+
+ def cmd_net_sep(self, ast): # noqa
+ return ast
+
+ def inline_comment(self, ast): # noqa
+ return ast
+
+ def line_comment(self, ast): # noqa
+ return ast
+
+ def text(self, ast): # noqa
+ return ast
+
+ def asterisk(self, ast): # noqa
+ return ast
+
+ def question_mark(self, ast): # noqa
+ return ast
+
+ def colon(self, ast): # noqa
+ return ast
+
+ def semicolon(self, ast): # noqa
+ return ast
+
+ def comma(self, ast): # noqa
+ return ast
+
+ def dot(self, ast): # noqa
+ return ast
+
+ def dollar(self, ast): # noqa
+ return ast
+
+ def double_bar(self, ast): # noqa
+ return ast
+
+ def single_quote(self, ast): # noqa
+ return ast
+
+ def double_quote(self, ast): # noqa
+ return ast
+
+ def lc(self, ast): # noqa
+ return ast
+
+ def rc(self, ast): # noqa
+ return ast
+
+ def lp(self, ast): # noqa
+ return ast
+
+ def rp(self, ast): # noqa
+ return ast
+
+ def newline(self, ast): # noqa
+ return ast
+
+ def st(self, ast): # noqa
+ return ast
+
+ def ws(self, ast): # noqa
+ return ast
+
+
+def main(filename, **kwargs):
+ if not filename or filename == '-':
+ text = sys.stdin.read()
+ else:
+ with open(filename) as f:
+ text = f.read()
+ parser = ExpressionParser()
+ return parser.parse(
+ text,
+ filename=filename,
+ **kwargs
+ )
+
+
+if __name__ == '__main__':
+ import json
+ from tatsu.util import asjson
+
+ ast = generic_main(main, ExpressionParser, name='Expression')
+ data = asjson(ast)
+ print(json.dumps(data, indent=2))
diff --git a/PySpice/Spice/ExpressionModel.py b/PySpice/Spice/ExpressionModel.py
new file mode 100644
index 000000000..9afd94599
--- /dev/null
+++ b/PySpice/Spice/ExpressionModel.py
@@ -0,0 +1,240 @@
+#!/usr/bin/env python
+
+# CAVEAT UTILITOR
+#
+# This file was automatically generated by TatSu.
+#
+# https://pypi.python.org/pypi/tatsu/
+#
+# Any changes you make to it will be overwritten the next time
+# the file is generated.
+
+from __future__ import annotations
+
+from typing import Any
+from dataclasses import dataclass
+
+from tatsu.objectmodel import Node
+from tatsu.semantics import ModelBuilderSemantics
+
+
+@dataclass(eq=False)
+class ModelBase(Node):
+ pass
+
+
+class ExpressionModelBuilderSemantics(ModelBuilderSemantics):
+ def __init__(self, context=None, types=None):
+ types = [
+ t for t in globals().values()
+ if type(t) is type and issubclass(t, ModelBase)
+ ] + (types or [])
+ super().__init__(context=context, types=types)
+
+
+@dataclass(eq=False)
+class SpiceExpression(ModelBase):
+ pass
+
+
+@dataclass(eq=False)
+class GenericExpression(ModelBase):
+ braced: Any = None
+ value: Any = None
+
+
+@dataclass(eq=False)
+class BracedExpression(ModelBase):
+ sep: Any = None
+
+
+@dataclass(eq=False)
+class Expression(ModelBase):
+ term: Any = None
+ ternary: Any = None
+
+
+@dataclass(eq=False)
+class Ternary(ModelBase):
+ op: Any = None
+ sep: Any = None
+ t: Any = None
+ x: Any = None
+ y: Any = None
+
+
+@dataclass(eq=False)
+class Conditional(ModelBase):
+ expr: Any = None
+
+
+@dataclass(eq=False)
+class Or(ModelBase):
+ left: Any = None
+ op: Any = None
+ right: Any = None
+ sep: Any = None
+
+
+@dataclass(eq=False)
+class Xor(ModelBase):
+ left: Any = None
+ op: Any = None
+ right: Any = None
+ sep: Any = None
+
+
+@dataclass(eq=False)
+class And(ModelBase):
+ left: Any = None
+ op: Any = None
+ right: Any = None
+ sep: Any = None
+
+
+@dataclass(eq=False)
+class Not(ModelBase):
+ op: Any = None
+ operator: Any = None
+
+
+@dataclass(eq=False)
+class Relational(ModelBase):
+ factor: Any = None
+ left: Any = None
+ op: Any = None
+ right: Any = None
+ sep: Any = None
+
+
+@dataclass(eq=False)
+class ConditionalFactor(ModelBase):
+ boolean: Any = None
+ expr: Any = None
+ sep: Any = None
+
+
+@dataclass(eq=False)
+class Term(ModelBase):
+ pass
+
+
+@dataclass(eq=False)
+class AddSub(ModelBase):
+ left: Any = None
+ op: Any = None
+ right: Any = None
+ sep: Any = None
+
+
+@dataclass(eq=False)
+class ProdDivMod(ModelBase):
+ left: Any = None
+ op: Any = None
+ right: Any = None
+ sep: Any = None
+
+
+@dataclass(eq=False)
+class Sign(ModelBase):
+ op: Any = None
+ operator: Any = None
+
+
+@dataclass(eq=False)
+class Exponential(ModelBase):
+ left: Any = None
+ op: Any = None
+ right: Any = None
+ sep: Any = None
+
+
+@dataclass(eq=False)
+class Functional(ModelBase):
+ pass
+
+
+@dataclass(eq=False)
+class Variable(ModelBase):
+ factor: Any = None
+ sep: Any = None
+ variable: Any = None
+
+
+@dataclass(eq=False)
+class Factor(ModelBase):
+ sep: Any = None
+
+
+@dataclass(eq=False)
+class Functions(ModelBase):
+ pass
+
+
+@dataclass(eq=False)
+class Value(ModelBase):
+ imag: Any = None
+ real: Any = None
+ unit: Any = None
+
+
+@dataclass(eq=False)
+class ImagValue(ModelBase):
+ value: Any = None
+
+
+@dataclass(eq=False)
+class RealValue(ModelBase):
+ value: Any = None
+
+
+@dataclass(eq=False)
+class NumberScale(ModelBase):
+ scale: Any = None
+ value: Any = None
+
+
+@dataclass(eq=False)
+class Unit(ModelBase):
+ pass
+
+
+@dataclass(eq=False)
+class Hz(ModelBase):
+ pass
+
+
+@dataclass(eq=False)
+class Float(ModelBase):
+ pass
+
+
+@dataclass(eq=False)
+class Int(ModelBase):
+ pass
+
+
+@dataclass(eq=False)
+class BinaryPattern(ModelBase):
+ pattern: Any = None
+
+
+@dataclass(eq=False)
+class Device(ModelBase):
+ pass
+
+
+@dataclass(eq=False)
+class NetNode(ModelBase):
+ node: Any = None
+ sep: Any = None
+
+
+@dataclass(eq=False)
+class Separator(ModelBase):
+ comment: Any = None
+
+
+@dataclass(eq=False)
+class Comment(ModelBase):
+ pass
diff --git a/PySpice/Spice/Expressions.py b/PySpice/Spice/Expressions.py
new file mode 100644
index 000000000..f2b4b1dfd
--- /dev/null
+++ b/PySpice/Spice/Expressions.py
@@ -0,0 +1,721 @@
+import operator
+
+import numpy as np
+import operator as op
+from ..Tools.StringTools import str_spice
+
+class Expression:
+ """Base class for all expressions.
+ """
+ def __call__(self, **kwargs):
+ raise NotImplementedError("The call function is not implemented in class: {}".format(type(self)))
+
+ def __str__(self):
+ raise NotImplementedError("The str function is not implemented in class: {}".format(type(self)))
+
+ def __abs__(self, symbol):
+ return Abs(symbol)
+
+ def __add__(self, other):
+ return Add(self, other)
+
+ def __radd__(self, other):
+ return Add(other, self)
+
+ def __sub__(self, other):
+ return Sub(self, other)
+
+ def __rsub__(self, other):
+ return Sub(other, self)
+
+ def __mul__(self, other):
+ return Mul(self, other)
+
+ def __rmul__(self, other):
+ return Mul(other, self)
+
+ def __truediv__(self, other):
+ return Div(self, other)
+
+ def __rtruediv__(self, other):
+ return Div(other, self)
+
+ def __mod__(self, other):
+ return Mod(self, other)
+
+ def __rmod__(self, other):
+ return Mod(other, self)
+
+ def __pow__(self, other):
+ return Power(self, other)
+
+ def __rpow__(self, other):
+ return Power(other, self)
+
+ def __neg__(self):
+ return Neg(self)
+
+ def __pos__(self):
+ return Pos(self)
+
+ def __lt__(self, other):
+ return LT(self, other)
+
+ def __le__(self, other):
+ return LE(self, other)
+
+ def __eq__(self, other):
+ return EQ(self, other)
+
+ def __ne__(self, other):
+ return NE(self, other)
+
+ def __ge__(self, other):
+ return GE(self, other)
+
+ def __gt__(self, other):
+ return GT(self, other)
+
+
+class Function(Expression):
+ """Base class for all functions.
+
+ Args:
+ Expression (arguments): The arguments of the different functions.
+
+ Raises:
+ ValueError: If the number of arguments is not one of the expected.
+
+ Returns:
+ Expression or basic type: The result of the calculation of the expression
+ if all the arguments are basic types.
+ """
+ nargs = 0
+
+ def __init__(self, func, *symbols):
+ self._func = func
+ self._symbols = symbols
+ if isinstance(self.__class__.nargs, tuple):
+ if len(symbols) not in self.__class__.nargs:
+ raise ValueError("The number of arguments is not correct: {} != {}".format(
+ len(self._symbols),
+ self.__class__.nargs
+ ))
+ else:
+ if len(symbols) != self.__class__.nargs:
+ raise ValueError("The number of arguments is not correct: {} != {}".format(
+ len(self._symbols),
+ self.__class__.nargs
+ ))
+
+ def __str__(self):
+ arguments = ", ".join([str_spice(symbol) for symbol in self._symbols])
+ return "{:s}({:s})".format(self.__class__.__name__.lower(), arguments)
+
+ def __call__(self, **kwargs):
+ result = [symbol if isinstance(symbol, (bool, int, float, complex)) else symbol.subs(**kwargs)
+ for symbol in self._symbols]
+ expr = [True for element in result
+ if isinstance(element, Expression)]
+ if len(expr) > 0:
+ return self.__class__(*result)
+ else:
+ return self._func(*result)
+
+
+class BinaryOperator(Expression):
+ def __init__(self, op, string, lhs, rhs):
+ self._lhs = lhs
+ self._op = op
+ self._rhs = rhs
+ self._string = string
+
+ def __str__(self):
+ lhs = str_spice(self._lhs)
+ if isinstance(self._lhs, BinaryOperator):
+ lhs = "({:s})".format(lhs)
+ rhs = str_spice(self._rhs)
+ if isinstance(self._rhs, BinaryOperator):
+ rhs = "({:s})".format(rhs)
+ return "{:s} {:s} {:s}".format(lhs, str(self._string), rhs)
+
+ def __call__(self, **kwargs):
+ lhs = self._lhs
+ rhs = self._rhs
+ if kwargs:
+ lhs = lhs.subs(**kwargs)
+ rhs = rhs.subs(**kwargs)
+ if isinstance(lhs, Expression) or isinstance(rhs, Expression):
+ return self.__class__(lhs, rhs)
+ else:
+ return self._op(lhs, rhs)
+ else:
+ try:
+ return self._op(lhs, rhs)
+ except:
+ return self.__class__(lhs, rhs)
+
+class UnaryOperator(Expression):
+ def __init__(self, op, operator, operand):
+ self._op = op
+ self._operator = operator
+ self._operand = operand
+
+ def __str__(self):
+ operand = str_spice(self._operand)
+ if isinstance(self._operand, BinaryOperator):
+ operand = "({:s})".format(operand)
+ return "{:s}{:s}".format(str(self._operator), operand)
+
+ def __call__(self, **kwargs):
+ operator = self._operator
+ if kwargs:
+ operator.subs(**kwargs)
+ if isinstance(operator, Expression):
+ return self.__class__(operator)
+ else:
+ return self._op(operator)
+ else:
+ try:
+ return self._op(operator)
+ except:
+ return self.__class__(operator)
+
+
+class Add(BinaryOperator):
+ def __init__(self, lhs, rhs):
+ super(Add, self).__init__(op.add, "+", lhs, rhs)
+
+
+class Sub(BinaryOperator):
+ def __init__(self, lhs, rhs):
+ super(Sub, self).__init__(op.sub, "-", lhs, rhs)
+
+
+class Mul(BinaryOperator):
+ def __init__(self, lhs, rhs):
+ super(Mul, self).__init__(op.mul, "*", lhs, rhs)
+
+
+class Div(BinaryOperator):
+ def __init__(self, lhs, rhs):
+ super(Div, self).__init__(op.truediv, "/", lhs, rhs)
+
+
+class Mod(BinaryOperator):
+ def __init__(self, lhs, rhs):
+ super(Mod, self).__init__(op.mod, "%", lhs, rhs)
+
+
+class Power(BinaryOperator):
+ def __init__(self, lhs, rhs):
+ super(Power, self).__init__(op.pow, "**", lhs, rhs)
+
+
+class Neg(UnaryOperator):
+ def __init__(self, operator):
+ super(Neg, self).__init__(op.neg, "-", operator)
+
+
+class Pos(UnaryOperator):
+ def __init__(self, operator):
+ super(Pos, self).__init__(op.pos, "+", operator)
+
+
+class LT(BinaryOperator):
+ def __init__(self, lhs, rhs):
+ super(LT, self).__init__(op.lt, "<", lhs, rhs)
+
+
+class LE(BinaryOperator):
+ def __init__(self, lhs, rhs):
+ super(LE, self).__init__(op.le, "<=", lhs, rhs)
+
+
+class EQ(BinaryOperator):
+ def __init__(self, lhs, rhs):
+ super(EQ, self).__init__(op.eq, "==", lhs, rhs)
+
+
+class NE(BinaryOperator):
+ def __init__(self, lhs, rhs):
+ super(NE, self).__init__(op.ne, "!=", lhs, rhs)
+
+
+class GE(BinaryOperator):
+ def __init__(self, lhs, rhs):
+ super(GE, self).__init__(op.ge, ">=", lhs, rhs)
+
+
+class GT(BinaryOperator):
+ def __init__(self, lhs, rhs):
+ super(GT, self).__init__(op.gt, ">", lhs, rhs)
+
+
+class Not(UnaryOperator):
+ def __init__(self, value):
+ super(Not, self).__init__(operator.not_, "~", value)
+
+
+class And(BinaryOperator):
+ def __init__(self, lhs, rhs):
+ super(And, self).__init__(operator.and_, "&", lhs, rhs)
+
+
+class Or(BinaryOperator):
+ def __init__(self, lhs, rhs):
+ super(Or, self).__init__(operator.or_, "|", lhs, rhs)
+
+
+class Xor(BinaryOperator):
+ @staticmethod
+ def _xor(lhs, rhs):
+ return (lhs and not rhs) or (rhs and not lhs)
+
+ def __init__(self, lhs, rhs):
+ super(Xor, self).__init__(Xor._xor, "^", lhs, rhs)
+
+
+class Abs(Function):
+ nargs = 1
+
+ def __init__(self, *symbol):
+ super(Abs, self).__init__(np.abs, *symbol)
+
+
+class ACos(Function):
+ nargs = 1
+
+ def __init__(self, *symbol):
+ super(ACos, self).__init__(np.arccos, *symbol)
+
+
+class ACosh(Function):
+ nargs = 1
+
+ def __init__(self, *symbol):
+ super(ACosh, self).__init__(np.arccosh, *symbol)
+
+
+class AGauss(Function):
+ nargs = 3
+
+ @staticmethod
+ def _agauss(mu, alpha, n):
+ return np.normal(mu,
+ alpha / n,
+ 1)
+
+ def __init__(self, *symbol):
+ super(AGauss, self).__init__(AGauss._agauss, *symbol)
+
+
+class ASin(Function):
+ nargs = 1
+
+ def __init__(self, *symbol):
+ super(ASin, self).__init__(np.arcsin, *symbol)
+
+
+class ASinh(Function):
+ nargs = 1
+
+ def __init__(self, *symbol):
+ super(ASinh, self).__init__(np.arcsinh, *symbol)
+
+
+class ATan(Function):
+ nargs = 1
+
+ def __init__(self, *symbol):
+ super(ATan, self).__init__(np.arctan, *symbol)
+
+
+class ATan2(Function):
+ nargs = 2
+
+ def __init__(self, *symbol):
+ super(ATan2, self).__init__(np.arctan2, *symbol)
+
+
+class ATanh(Function):
+ nargs = 1
+
+ def __init__(self, *symbol):
+ super(ATanh, self).__init__(np.arctanh, *symbol)
+
+
+class AUnif(Function):
+ nargs = 2
+
+ @staticmethod
+ def _aunif(mu, alpha):
+ return np.uniform(mu - alpha,
+ mu + alpha,
+ 1)
+
+ def __init__(self, *symbol):
+ super(AUnif, self).__init__(AUnif._aunif, *symbol)
+
+
+class Ceil(Function):
+ nargs = 1
+
+ def __init__(self, *symbol):
+ super(Ceil, self).__init__(np.ceil, *symbol)
+
+
+class Cos(Function):
+ nargs = 1
+
+ def __init__(self, *symbol):
+ super(Cos, self).__init__(np.cos, *symbol)
+
+
+class Cosh(Function):
+ nargs = 1
+
+ def __init__(self, *symbol):
+ super(Cosh, self).__init__(np.cosh, *symbol)
+
+
+class Db(Function):
+ nargs = 1
+
+ def __init__(self, *symbol):
+ super(Db, self).__init__(10*np.log10, *symbol)
+
+
+class Ddt(Function):
+ nargs = 1
+
+ def __init__(self, *symbol):
+ super(Ddt, self).__init__(np.diff, *symbol)
+
+
+class Ddx(Function):
+ nargs = 2
+
+ def __init__(self, *symbol):
+ super(Ddx, self).__init__(np.diff, *symbol)
+
+
+class Exp(Function):
+ nargs = 1
+
+ def __init__(self, *symbol):
+ super(Exp, self).__init__(np.exp, *symbol)
+
+
+class Floor(Function):
+ nargs = 1
+
+ def __init__(self, *symbol):
+ super(Floor, self).__init__(np.floor, *symbol)
+
+
+class Gauss(Function):
+ nargs = 3
+
+ @staticmethod
+ def _gauss(mu, alpha, n):
+ return np.normal(mu,
+ alpha * mu / n,
+ 1)
+
+ def __init__(self, *symbol):
+ def __init__(self, *symbol):
+ super(Gauss, self).__init__(Gauss._gauss, *symbol)
+
+
+class If(Function):
+ nargs = 3
+
+ @staticmethod
+ def _if(t, x, y):
+ return x if t else y
+
+ def __init__(self, *symbol):
+ super(If, self).__init__(If._if, *symbol)
+
+
+class Img(Function):
+ nargs = 1
+
+ def __init__(self, *symbol):
+ super(Img, self).__init__(np.imag, *symbol)
+
+
+class Int(Function):
+ nargs = 1
+
+ def __init__(self, *symbol):
+ super(Int, self).__init__(int, *symbol)
+
+
+class Limit(Function):
+ nargs = 3
+
+ @staticmethod
+ def _limit(x, y, z):
+ return y if x < y else z if x > z else x
+
+ def __init__(self, *symbol):
+ super(Limit, self).__init__(Limit._limit, *symbol)
+
+
+class Ln(Function):
+ nargs = 1
+
+ def __init__(self, *symbol):
+ super(Ln, self).__init__(np.log, *symbol)
+
+
+class Log10(Function):
+ nargs = 1
+
+ def __init__(self, *symbol):
+ super(Log10, self).__init__(np.log10, *symbol)
+
+
+class M(Function):
+ nargs = 1
+
+ def __init__(self, *symbol):
+ super(M, self).__init__(np.abs, *symbol)
+
+
+class Max(Function):
+ nargs = 2
+
+ def __init__(self, *symbol):
+ super(Max, self).__init__(np.max, *symbol)
+
+
+class Min(Function):
+ nargs = 2
+
+ def __init__(self, *symbol):
+ super(Min, self).__init__(np.min, *symbol)
+
+
+class NInt(Function):
+ nargs = 1
+
+ def __init__(self, *symbol):
+ super(NInt, self).__init__(np.round, *symbol)
+
+
+class Ph(Function):
+ nargs = 1
+
+ def __init__(self, *symbol):
+ super(Ph, self).__init__(np.angle, *symbol)
+
+
+class Pow(Function):
+ nargs = 2
+
+ def __init__(self, *symbol):
+ super(Pow, self).__init__(np.power, *symbol)
+
+
+class Pwr(Function):
+ nargs = 2
+
+ def __init__(self, *symbol):
+ super(Pwr, self).__init__(np.power, *symbol)
+
+
+class Pwrs(Function):
+ nargs = 2
+
+ @staticmethod
+ def _pwrs(x, y):
+ return np.copysign(np.power(np.abs(x), y), x)
+
+ def __init__(self, *symbol):
+ super(Pwrs, self).__init__(Pwrs._pwrs, *symbol)
+
+
+class Rand(Function):
+ nargs = 0
+
+ @staticmethod
+ def _rand():
+ return np.random.rand(1)
+
+ def __init__(self, *symbol):
+ super(Rand, self).__init__(Rand._rand, *symbol)
+
+
+class Re(Function):
+ nargs = 1
+
+ def __init__(self, *symbol):
+ super(Re, self).__init__(np.real, *symbol)
+
+
+class Sdt(Function):
+ nargs = 1
+
+ def __init__(self, *symbol):
+ super(Sdt, self).__init__(np.trapz, *symbol)
+
+
+class Sgn(Function):
+ nargs = 1
+
+ def __init__(self, *symbol):
+ super(Sgn, self).__init__(np.sign, *symbol)
+
+
+class Sign(Function):
+ nargs = 2
+
+ def __init__(self, *symbol):
+ super(Sign, self).__init__(np.copysign, *symbol)
+
+
+class Sin(Function):
+ nargs = 1
+
+ def __init__(self, *symbol):
+ super(Sin, self).__init__(np.sin, *symbol)
+
+
+class Sinh(Function):
+ nargs = 1
+
+ def __init__(self, *symbol):
+ super(Sinh, self).__init__(np.sinh, *symbol)
+
+
+class Sqrt(Function):
+ nargs = 1
+
+ def __init__(self, *symbol):
+ super(Sqrt, self).__init__(np.sqrt, *symbol)
+
+
+class Stp(Function):
+ nargs = 1
+
+ @staticmethod
+ def _stp(x):
+ return x * (x > 0)
+
+ def __init__(self, *symbol):
+ super(Stp, self).__init__(Stp._stp, *symbol)
+
+
+class Tan(Function):
+ nargs = 1
+
+ def __init__(self, *symbol):
+ super(Tan, self).__init__(np.arctan, *symbol)
+
+
+class Tanh(Function):
+ nargs = 1
+
+ def __init__(self, *symbol):
+ super(Tanh, self).__init__(np.cosh, *symbol)
+
+
+class Unif(Function):
+ nargs = 2
+
+ @staticmethod
+ def _unif(mu, alpha):
+ return np.uniform(mu * (1. - alpha),
+ mu * (1. + alpha),
+ 1)
+
+ def __init__(self, *symbol):
+ super(Unif, self).__init__(Unif._unif, *symbol)
+
+
+class URamp(Function):
+ nargs = 1
+
+ @staticmethod
+ def _uramp(x):
+ return x * (x > 0)
+
+ def __init__(self, *symbol):
+ super(URamp, self).__init__(URamp._uramp, *symbol)
+
+
+class Symbol(Expression):
+ def __init__(self, value):
+ self._value = value
+
+ @property
+ def value(self):
+ return self._value
+
+ def __str__(self):
+ return str_spice(self._value)
+
+ def subs(self, **kwargs):
+ name = str(self._value)
+ if name in kwargs:
+ return kwargs[name]
+ else:
+ return self
+
+ def __int__(self):
+ return int(self._value)
+
+ def __float__(self):
+ return float(self._value)
+
+
+class I(Symbol):
+ nargs = 1
+
+ def __init__(self, device):
+ super(I, self).__init__("i({:s})".format(device))
+
+
+class V(Symbol):
+ nargs = (1, 2)
+
+ def __init__(self, *nodes):
+ if len(nodes) not in self.__class__.nargs:
+ ValueError("Only 1 or two nodes allowed.")
+ string = ""
+ if len(nodes) == 1:
+ string = "v({:s})".format(str(nodes[0]))
+ else:
+ string = "v({:s}, {:s})".format(str(nodes[0]),
+ str(nodes[1]))
+ super(V, self).__init__(string)
+
+class Poly(Symbol):
+ def __init__(self, controllers, coefficient):
+ self._controllers = controllers
+ self._coefficient = coefficient
+ string = "poly({}) {} {}".format(len(self._controllers),
+ " ".join([str(controller)
+ for controller in self._controllers]),
+ " ".join([str(coeff)
+ for coeff in self._coefficient]))
+ super(Poly, self).__init__(string)
+
+class Table(Symbol):
+ def __init__(self, expression, points):
+ self._expression = expression
+ self._points = points
+ string = "table {{{}}} = {}".format(self._expression,
+ " ".join(["({}, {})".format(*point)
+ for point in self._points]))
+ super(Table, self).__init__(string)
+
+
+class TableFile(Symbol):
+ def __init__(self, filename):
+ self._filename = filename
+ string = "tablefile({})".format(self._filename)
+ super(TableFile, self).__init__(string)
diff --git a/PySpice/Spice/HighLevelElement.py b/PySpice/Spice/HighLevelElement.py
index dbce6238a..4810f773f 100644
--- a/PySpice/Spice/HighLevelElement.py
+++ b/PySpice/Spice/HighLevelElement.py
@@ -28,7 +28,7 @@
from ..Math import rms_to_amplitude, amplitude_to_rms
from ..Tools.StringTools import join_list, join_dict, str_spice, str_spice_list
-from ..Unit import as_s, as_V, as_A, as_Hz
+from ..Unit import as_s, as_v, as_a, as_Hz
from .BasicElement import VoltageSource, CurrentSource
####################################################################################################
@@ -39,12 +39,12 @@ class SourceMixinAbc:
####################################################################################################
class VoltageSourceMixinAbc:
- AS_UNIT = as_V
+ AS_UNIT = as_v
####################################################################################################
class CurrentSourceMixinAbc:
- AS_UNIT = as_A
+ AS_UNIT = as_a
####################################################################################################
@@ -81,8 +81,6 @@ class SinusoidalMixin(SourceMixinAbc):
Public Attributes:
- :attr:`ac_magnitude`
-
:attr:`amplitude`
:attr:`damping_factor`
@@ -148,7 +146,7 @@ class PulseMixin(SourceMixinAbc):
+--------+---------------+---------------+-------+
| V1 + initial value + + V, A |
+--------+---------------+---------------+-------+
- | V2 + pulsed value + + V, A |
+ | V2 + pulse value + + V, A |
+--------+---------------+---------------+-------+
| Td + delay time + 0.0 + sec |
+--------+---------------+---------------+-------+
@@ -204,7 +202,7 @@ class PulseMixin(SourceMixinAbc):
:attr:`pulse_width`
- :attr:`pulsed_value`
+ :attr:`pulse_value`
:attr:`rise_time`
@@ -213,30 +211,25 @@ class PulseMixin(SourceMixinAbc):
##############################################
def __init__(self,
- initial_value, pulsed_value,
- pulse_width, period,
+ initial_value, pulse_value=0,
+ pulse_width=None, period=None,
delay_time=0, rise_time=0, fall_time=0,
- phase=None,
- dc_offset=0):
+ phase=None):
# Fixme: default
# rise_time, fall_time = Tstep
# pulse_width, period = Tstop
- self.dc_offset = self.AS_UNIT(dc_offset) # Fixme: -> SourceMixinAbc
self.initial_value = self.AS_UNIT(initial_value)
- self.pulsed_value = self.AS_UNIT(pulsed_value)
+ self.pulse_value = self.AS_UNIT(pulse_value)
self.delay_time = as_s(delay_time)
self.rise_time = as_s(rise_time)
self.fall_time = as_s(fall_time)
- self.pulse_width = as_s(pulse_width)
- self.period = as_s(period) # Fixme: protect by setter?
+ self.pulse_width = as_s(pulse_width, none=True)
+ self.period = as_s(period, none=True) # Fixme: protect by setter?
# XSPICE
- if phase is not None:
- self.phase = as_s(phase)
- else:
- self.phase = None
+ self.phase = as_s(phase, none=True)
# # Fixme: to func?
# # Check parameters
@@ -258,18 +251,25 @@ def frequency(self):
##############################################
def format_spice_parameters(self):
-
- # if DC is not provided, ngspice complains
- # Warning: vpulse: no DC value, transient time 0 value used
-
+ values = [self.initial_value]
+ if self.pulse_value is not None:
+ values.append(self.pulse_value)
+ if self.delay_time is not None:
+ values.append(self.delay_time)
+ if self.rise_time is not None:
+ values.append(self.rise_time)
+ if self.fall_time is not None:
+ values.append(self.fall_time)
+ if self.pulse_width is not None:
+ values.append(self.pulse_width)
+ if self.period is not None:
+ values.append(self.period)
+ if self.phase is not None:
+ values.append(self.phase)
# Fixme: to func?
- return join_list((
- 'DC {}'.format(str_spice(self.dc_offset)),
- 'PULSE(' +
- join_list((self.initial_value, self.pulsed_value, self.delay_time,
- self.rise_time, self.fall_time, self.pulse_width, self.period,
- self.phase)) +
- ')'))
+ return ('PULSE(' +
+ join_list(values) +
+ ')')
####################################################################################################
@@ -282,15 +282,15 @@ class ExponentialMixin(SourceMixinAbc):
+------+--------------------+---------------+-------+
| Name + Parameter + Default Value + Units |
+------+--------------------+---------------+-------+
- | V1 + Initial value + + V, A |
+ | V1 + Initial amplitude + + V, A |
+------+--------------------+---------------+-------+
- | V2 + pulsed value + + V, A |
+ | V2 + amplitude + + V, A |
+------+--------------------+---------------+-------+
| Td1 + rise delay time + 0.0 + sec |
+------+--------------------+---------------+-------+
| tau1 + rise time constant + Tstep + sec |
+------+--------------------+---------------+-------+
- | Td2 + fall delay time + Td1+Tstep + sec |
+ | Td2 + delay fall time + Td1+Tstep + sec |
+------+--------------------+---------------+-------+
| tau2 + fall time constant + Tstep + sec |
+------+--------------------+---------------+-------+
@@ -318,28 +318,34 @@ class ExponentialMixin(SourceMixinAbc):
##############################################
def __init__(self,
- initial_value, pulsed_value,
+ initial_amplitude, amplitude,
rise_delay_time=.0, rise_time_constant=None,
- fall_delay_time=None, fall_time_constant=None):
+ delay_fall_time=None, fall_time_constant=None):
# Fixme: default
- self.initial_value = self.AS_UNIT(initial_value)
- self.pulsed_value = self.AS_UNIT(pulsed_value)
+ self.initial_amplitude = self.AS_UNIT(initial_amplitude)
+ self.amplitude = self.AS_UNIT(amplitude)
self.rise_delay_time = as_s(rise_delay_time)
- self.rise_time_constant = as_s(rise_time_constant)
- self.fall_delay_time = as_s(fall_delay_time)
- self.fall_time_constant = as_s(fall_time_constant)
+ self.rise_time_constant = as_s(rise_time_constant, none=True)
+ self.delay_fall_time = as_s(delay_fall_time, none=True)
+ self.fall_time_constant = as_s(fall_time_constant, none=True)
##############################################
def format_spice_parameters(self):
# Fixme: to func?
+ values = [self.initial_amplitude, self.amplitude,
+ self.rise_delay_time]
+ if self.rise_time_constant is not None:
+ values.append(self.rise_time_constant)
+ if self.delay_fall_time is not None:
+ values.append(self.delay_fall_time)
+ if self.fall_time_constant is not None:
+ values.append(self.fall_time_constant)
+
return ('EXP(' +
- join_list((self.initial_value, self.pulsed_value,
- self.rise_delay_time, self.rise_time_constant,
- self.fall_delay_time, self.fall_time_constant,
- )) +
+ join_list(values) +
')')
####################################################################################################
@@ -374,10 +380,13 @@ class PieceWiseLinearMixin(SourceMixinAbc):
##############################################
- def __init__(self, values, repeat_time=None, delay_time=None, dc=None):
+ def __init__(self, values, repeat_time=0, time_delay=.0, dc=None):
+
+ # Fixme: default
+
self.values = sum(([as_s(t), self.AS_UNIT(x)] for (t, x) in values), [])
- self.repeat_time = as_s(repeat_time, none=True)
- self.delay_time = as_s(delay_time, none=True)
+ self.repeat_time = as_s(repeat_time)
+ self.time_delay = as_s(time_delay)
self.dc = self.AS_UNIT(dc, none=True)
##############################################
@@ -385,22 +394,85 @@ def __init__(self, values, repeat_time=None, delay_time=None, dc=None):
def format_spice_parameters(self):
# Fixme: to func?
+ result = ''
+ if self.dc is not None:
+ result = 'dc {} '.format(str_spice(self.dc))
+ return (result + 'pwl(' +
+ join_list(self.values) +
+ ' ' +
+ join_dict({'r':self.repeat_time, 'td':self.time_delay}) + # OrderedDict(
+ ')')
- d = {}
- if self.repeat_time is not None:
- d["r"] = self.repeat_time
- if self.delay_time is not None:
- d["td"] = self.delay_time
+####################################################################################################
- _ = ""
- if self.dc is not None:
- _ += "DC {} ".format(str_spice(self.dc))
- _ += "PWL(" + join_list(self.values)
- if d:
- _ += " " + join_dict(d) # OrderedDict(
- _ += ")"
+class PatternMixin(SourceMixinAbc):
+
+ r"""This class implements a Piece-Wise Linear waveform.
+
+ Spice Syntax::
+
+ PAT( VHI VLO TD TR RF TSAMPLE DATA )
+
+ Generates a pattern based on the bit pattern indicated in the DATA field.
+
+ `values` should be given as a list of (`Time`, `Value`)-tuples, e.g.::
+
+ PatternVoltageSource(
+ circuit,
+ 'pat1', '1', '0',
+ high_value,
+ low_value,
+ delay_time,
+ rise_time,
+ fall_time,
+ bit_period,
+ bit_pattern,
+ repeat
+ )
+
+ """
+
+ ##############################################
+
+ def __init__(self,
+ high_value,
+ low_value,
+ delay_time,
+ rise_time,
+ fall_time,
+ bit_period,
+ bit_pattern,
+ repeat=False):
+
+ # Fixme: default
+
+ self.high_value = self.AS_UNIT(high_value)
+ self.low_value = self.AS_UNIT(low_value)
+ self.delay_time = as_s(delay_time)
+ self.rise_time = as_s(rise_time)
+ self.fall_time = as_s(fall_time)
+ self.bit_period = as_s(bit_period)
+ self.bit_pattern = bit_pattern
+ self.repeat = repeat
- return _
+ ##############################################
+
+ def format_spice_parameters(self):
+
+ # Fixme: to func?
+ return ('PAT(' +
+ join_list((self.high_value,
+ self.low_value,
+ self.delay_time,
+ self.rise_time,
+ self.fall_time,
+ self.bit_period,
+ "b" + self.bit_pattern,
+ 1 if self.repeat else 0
+ )
+ )
+ + ")"
+ )
####################################################################################################
@@ -436,20 +508,27 @@ class SingleFrequencyFMMixin(SourceMixinAbc):
##############################################
- def __init__(self, offset, amplitude, carrier_frequency, modulation_index, signal_frequency):
+ def __init__(self, offset, amplitude, carrier_frequency=None, modulation_index=None, signal_frequency=None):
self.offset = self.AS_UNIT(offset)
self.amplitude = self.AS_UNIT(amplitude)
- self.carrier_frequency = as_Hz(carrier_frequency)
+ self.carrier_frequency = as_Hz(carrier_frequency, none=True)
self.modulation_index = modulation_index
- self.signal_frequency = as_Hz(signal_frequency)
+ self.signal_frequency = as_Hz(signal_frequency, none=True)
##############################################
def format_spice_parameters(self):
+ values = [self.offset, self.amplitude]
+ if self.carrier_frequency is not None:
+ values.append(self.carrier_frequency)
+ if self.modulation_index is not None:
+ values.append(self.modulation_index)
+ if self.signal_frequency is not None:
+ values.append(self.signal_frequency)
+
# Fixme: to func?
return ('SFFM(' +
- join_list((self.offset, self.amplitude, self.carrier_frequency,
- self.modulation_index, self.signal_frequency)) +
+ join_list(values) +
')')
####################################################################################################
@@ -745,6 +824,48 @@ def __init__(self, netlist, name, node_plus, node_minus, *args, **kwargs):
####################################################################################################
+class PatternVoltageSource(VoltageSource, VoltageSourceMixinAbc, PatternMixin):
+
+ r"""This class implements a pattern voltage source.
+
+ See :class:`PatternMixin` for documentation.
+
+ """
+
+ ##############################################
+
+ def __init__(self, netlist, name, node_plus, node_minus, *args, **kwargs):
+
+ VoltageSource.__init__(self, netlist, name, node_plus, node_minus)
+ PatternMixin.__init__(self, *args, **kwargs)
+
+ ##############################################
+
+ format_spice_parameters = PatternMixin.format_spice_parameters
+
+####################################################################################################
+
+class PatternCurrentSource(CurrentSource, CurrentSourceMixinAbc, PatternMixin):
+
+ r"""This class implements a pattern current source.
+
+ See :class:`PatternMixin` for documentation.
+
+ """
+
+ ##############################################
+
+ def __init__(self, netlist, name, node_plus, node_minus, *args, **kwargs):
+
+ CurrentSource.__init__(self, netlist, name, node_plus, node_minus)
+ PatternMixin.__init__(self, *args, **kwargs)
+
+ ##############################################
+
+ format_spice_parameters = PatternMixin.format_spice_parameters
+
+####################################################################################################
+
class SingleFrequencyFMVoltageSource(VoltageSource, VoltageSourceMixinAbc, SingleFrequencyFMMixin):
r"""This class implements a single frequency FM waveform voltage source.
diff --git a/PySpice/Spice/Library.py b/PySpice/Spice/Library.py
index 4518d30b4..fbe78d984 100644
--- a/PySpice/Spice/Library.py
+++ b/PySpice/Spice/Library.py
@@ -22,11 +22,13 @@
import logging
import re
+import sys
+from collections import OrderedDict
####################################################################################################
from ..Tools.File import Directory
-from .Parser import SpiceParser
+from .EBNFSpiceParser import SpiceParser
####################################################################################################
@@ -62,36 +64,36 @@ class SpiceLibrary:
'.mod@xyce',
)
+ def _add_parsed(self, parsed):
+ for name, subcircuit in parsed.subcircuits.items():
+ self._subcircuits[name] = subcircuit
+ for name, model in parsed.models.items():
+ self._models[name] = model
+
##############################################
- def __init__(self, root_path, recurse=False, section=None):
+ def __init__(self, root_path=None, recurse=False, section=None):
self._directory = Directory(root_path).expand_vars_and_user()
- self._subcircuits = {}
- self._models = {}
+ self._subcircuits = OrderedDict()
+ self._models = OrderedDict()
+
+ if root_path is None:
+ self._directory=None
+ return
for path in self._directory.iter_file():
extension = path.extension.lower()
if extension in self.EXTENSIONS:
self._logger.debug("Parse {}".format(path))
try:
- spice_parser = SpiceParser(path=path, recurse=recurse, section=section)
- for lib in spice_parser.incl_libs:
- self._subcircuits.update(lib._subcircuits)
- self._models.update(lib._models)
+ parsed = SpiceParser.parse(path=path)
+ self._add_parsed(parsed)
except Exception as e:
+ tb = sys.exc_info()[2]
# Parse problem with this file, so skip it and keep going.
- self._logger.warn("Problem parsing {path} - {e}".format(**locals()))
- continue
- if spice_parser.is_only_subcircuit():
- for subcircuit in spice_parser.subcircuits:
- name = self._suffix_name(subcircuit.name, extension)
- self._subcircuits[name] = path
- elif spice_parser.is_only_model():
- for model in spice_parser.models:
- name = self._suffix_name(model.name, extension)
- self._models[name] = path
+ raise RuntimeError("Problem parsing {}".format(e)).with_traceback(tb)
##############################################
@@ -148,3 +150,7 @@ def search(self, s):
if re.search(s, name):
matches[name] = mdl_subckt
return matches
+
+ def insert(self, raw):
+ parsed = SpiceParser.parse(source=raw)
+ self._add_parsed(parsed)
diff --git a/PySpice/Spice/Netlist.py b/PySpice/Spice/Netlist.py
index 96fbc7334..ae0f45eb1 100644
--- a/PySpice/Spice/Netlist.py
+++ b/PySpice/Spice/Netlist.py
@@ -87,13 +87,14 @@ def __init__(self, **kwargs):
####################################################################################################
-from ..Tools.StringTools import join_lines, join_list, join_dict
+from ..Tools.StringTools import join_lines, join_list, join_dict, str_spice
from .ElementParameter import (
ParameterDescriptor,
PositionalElementParameter,
FlagParameter, KeyValueParameter,
)
from .Simulation import CircuitSimulator
+from .Expressions import Expression
####################################################################################################
@@ -102,7 +103,6 @@ def __init__(self, **kwargs):
####################################################################################################
class DeviceModel:
-
"""This class implements a device model.
Ngspice model types:
@@ -147,13 +147,14 @@ class DeviceModel:
##############################################
- def __init__(self, name, modele_type, **parameters):
+ def __init__(self, name, model_type, **parameters):
- self._name = str(name)
- self._model_type = str(modele_type)
+ self._name = str(name).lower()
+ self._model_type = str(model_type)
self._parameters = {}
for key, value in parameters.items():
+ # For parameters like is that are also python keywords
if key.endswith('_'):
key = key[:-1]
self._parameters[key] = value
@@ -178,20 +179,31 @@ def model_type(self):
def parameters(self):
return self._parameters.keys()
- ##############################################
+ @property
+ def include(self):
+ """Include file"""
+ return self._include
- def __getitem__(self, name):
- return self._parameters[name]
+ @property
+ def is_included(self):
+ """is_included"""
+ return self._include is None
##############################################
+ def __getitem__(self, name):
+ if name in self._parameters:
+ return self._parameters[name]
+ elif name.endswith('_'):
+ return self._parameters[name[:-1]]
+ else:
+ raise IndexError(name)
+
def __getattr__(self, name):
try:
- return self._parameters[name]
- except KeyError:
- if name.endswith('_'):
- return self._parameters[name[:-1]]
- # Fixme: else
+ return self.__getitem__(name)
+ except IndexError:
+ raise AttributeError(name)
##############################################
@@ -206,7 +218,6 @@ def __str__(self):
####################################################################################################
class PinDefinition:
-
"""This class defines a pin of an element."""
##############################################
@@ -255,7 +266,6 @@ def name(self):
####################################################################################################
class Pin(PinDefinition):
-
"""This class implements a pin of an element. It stores a reference to the element, the name of the
pin and the node.
@@ -266,7 +276,6 @@ class Pin(PinDefinition):
##############################################
def __init__(self, element, pin_definition, node):
-
super().__init__(pin_definition.position, pin_definition.name, pin_definition.alias)
self._element = element
@@ -298,7 +307,6 @@ def disconnect(self):
##############################################
def add_current_probe(self, circuit):
-
"""Add a current probe between the node and the pin.
The ammeter is named *ElementName_PinName*.
@@ -315,7 +323,6 @@ def add_current_probe(self, circuit):
####################################################################################################
class ElementParameterMetaClass(type):
-
# Metaclass to implements the element node and parameter machinery.
"""Metaclass to customise the element classes when they are created and to register SPICE prefix.
@@ -380,7 +387,7 @@ def __new__(meta_cls, class_name, base_classes, namespace):
for parameter in namespace['_spice_to_parameters'].values():
if (parameter.spice_name in namespace
and parameter.spice_name != parameter.attribute_name):
- _module_logger.error("Spice parameter '{}' clash with namespace".format(parameter.spice_name))
+ _module_logger.error("Spice parameter '{}' clash with namespace, attribute name: '{}'".format(parameter.spice_name, parameter.attribute_name))
# Initialise pins
@@ -419,7 +426,7 @@ def getter(self):
pin = PinDefinition(position, *pin_definition, optional=optional)
pins.append(pin)
namespace['PINS'] = pins
- namespace['__number_of_optional_pins__'] = number_of_optional_pins
+ namespace['_number_of_optional_pins_'] = number_of_optional_pins
else:
_module_logger.debug("{} don't define a PINS attribute".format(class_name))
@@ -452,8 +459,8 @@ def __init__(meta_cls, class_name, base_classes, namespace):
def number_of_pins(cls):
#! Fixme: many pins ???
number_of_pins = len(cls.PINS)
- if cls.__number_of_optional_pins__:
- return slice(number_of_pins - cls.__number_of_optional_pins__, number_of_pins +1)
+ if cls._number_of_optional_pins_:
+ return slice(number_of_pins - cls._number_of_optional_pins_, number_of_pins +1)
else:
return number_of_pins
@@ -480,7 +487,6 @@ def spice_to_parameters(cls):
####################################################################################################
class Element(metaclass=ElementParameterMetaClass):
-
"""This class implements a base class for an element.
It use a metaclass machinery for the declaration of the parameters.
@@ -502,30 +508,54 @@ class Element(metaclass=ElementParameterMetaClass):
def __init__(self, netlist, name, *args, **kwargs):
self._netlist = netlist
- self._name = str(name)
+ self._name = str(name).lower()
self.raw_spice = ''
self.enabled = True
+ parent = netlist
+ self._parameters = kwargs
+ # self._pins = kwargs.pop('pins',())
# Process remaining args
- if len(self._parameters_from_args) < len(args):
- raise NameError("Number of args mismatch")
- for parameter, value in zip(self._parameters_from_args, args):
- setattr(self, parameter.attribute_name, value)
-
+ if len(self._parameters_from_args) + self._number_of_optional_pins_ + len(self._positional_parameters) < len(args):
+ raise NameError("Number of args mismatch for device: {}".format(self.name))
+ # TODO: Modify the selection of arguments to take into account the RLC model cases.
+ if len(args) > 0:
+ optional_pins = 0
+ if len(self._positional_parameters) < len(args):
+ optional_pins = len(args) - len(self._positional_parameters)
+ for parameter in self._positional_parameters.values():
+ if parameter.attribute_name in kwargs:
+ optional_pins += 1
+ continue
+ if optional_pins <= self._number_of_optional_pins_ and len(args) >= optional_pins:
+ self._pins += [Pin(self, pin_definition, netlist.get_node(node, True))
+ for pin_definition, node in zip(self.PINS[len(self._pins):], args[:optional_pins])]
+ args = args[optional_pins:]
+ else:
+ raise IndexError("Incongruent number of optional pins on device: {}{}".format(self.PREFIX, self._name))
+ if len(args) > 0:
+ read = [False] * len(args)
+ for parameter in self._positional_parameters.values():
+ if parameter.position < len(read) and not read[parameter.position]:
+ setattr(self, parameter.attribute_name, args[parameter.position])
+ read[parameter.position] = True
# Process kwargs
for key, value in kwargs.items():
if key == 'raw_spice':
self.raw_spice = value
- elif (key in self._positional_parameters or
- key in self._optional_parameters or
- key in self._spice_to_parameters):
- setattr(self, key, value)
- elif hasattr(self, 'VALID_KWARGS') and key in self.VALID_KWARGS:
- pass # cf. NonLinearVoltageSource
else:
- raise ValueError('Unknown argument {}={}'.format(key, value))
+ if (key in self._positional_parameters or
+ key in self._optional_parameters or
+ key in self._spice_to_parameters):
+ setattr(self, key, value)
+ else:
+ for parameter in self._optional_parameters:
+ if key.lower() == self._optional_parameters[parameter].spice_name.lower():
+ setattr(self, parameter, value)
+ break
+ else:
+ raise ValueError('Unknown argument for {}: {}={}'.format(self.name, key, value))
- self._pins = ()
netlist._add_element(self)
##############################################
@@ -614,8 +644,19 @@ def format_node_names(self):
def parameter_iterator(self):
""" This iterator returns the parameter in the right order. """
+ positional_parameters = OrderedDict()
# Fixme: .parameters ???
- for parameter_dict in self._positional_parameters, self._optional_parameters:
+ if len(self._positional_parameters) > 0:
+ read = [False] * len(self._positional_parameters)
+ for parameter in self._positional_parameters.values():
+ if parameter.position < len(read) and read[parameter.position] is False:
+ if parameter.nonzero(self):
+ read[parameter.position] = parameter
+ for parameter in read:
+ if not (parameter is False):
+ positional_parameters[parameter.attribute_name] = parameter
+
+ for parameter_dict in positional_parameters, self._optional_parameters:
for parameter in parameter_dict.values():
if parameter.nonzero(self):
yield parameter
@@ -688,11 +729,11 @@ def __init__(self, netlist, name, *args, **kwargs):
raise NameError("Node '{}' is missing for element {}".format(pin_definition.name, self.name))
pin_definition_nodes.append((pin_definition, node))
- super().__init__(netlist, name, *args, **kwargs)
-
self._pins = [Pin(self, pin_definition, netlist.get_node(node, True))
for pin_definition, node in pin_definition_nodes]
+ super().__init__(netlist, name, *args, **kwargs)
+
##############################################
def copy_to(self, netlist):
@@ -708,10 +749,25 @@ class NPinElement(Element):
##############################################
- def __init__(self, netlist, name, nodes, *args, **kwargs):
+ def __init__(self, netlist, name, *args, **kwargs):
+ nodes = []
+ positional = len(self._positional_parameters)
+ for key, parameter in self._positional_parameters.items():
+ if parameter.key_parameter:
+ if key in kwargs:
+ positional -= 1
+ if positional > 0:
+ if positional < len(args):
+ nodes = args[:-positional]
+ args = args[-positional:]
+ else:
+ nodes = args
+ args = []
+
+ if len(nodes) > 0:
+ self._pins = [Pin(self, self.PINS[0], netlist.get_node(node, True))
+ for node in nodes]
super().__init__(netlist, name, *args, **kwargs)
- self._pins = [Pin(self, PinDefinition(position), netlist.get_node(node, True))
- for position, node in enumerate(nodes)]
##############################################
@@ -724,7 +780,6 @@ def copy_to(self, netlist):
####################################################################################################
class Node:
-
"""This class implements a node in the circuit. It stores a reference to the pins connected to
the node.
@@ -740,7 +795,7 @@ def __init__(self, netlist, name):
self._logger.warning("Node name '{}' is a Python keyword".format(name))
self._netlist = netlist
- self._name = str(name)
+ self._name = str(name).lower()
self._pins = set()
@@ -803,7 +858,6 @@ def disconnect(self, pin):
####################################################################################################
class Netlist:
-
"""This class implements a base class for a netlist.
.. note:: This class is completed with element shortcuts when the module is loaded.
@@ -820,13 +874,19 @@ def __init__(self):
self._nodes = {}
self._ground_node = self._add_node(self._ground_name)
- self._subcircuits = OrderedDict() # to keep the declaration order
- self._elements = OrderedDict() # to keep the declaration order
- self._models = {}
+ self._subcircuits = OrderedDict() # to keep the declaration order
+ self._elements = OrderedDict() # to keep the declaration order
+ self._models = OrderedDict()
+ self._includes = [] # .include
+ self._used_models = {}
+ self._used_subcircuits = {}
+ self._parameters = OrderedDict()
self.raw_spice = ''
- # self._graph = networkx.Graph()
+ self._spice_sim = ''
+
+ self._parent = None
##############################################
@@ -894,22 +954,30 @@ def model(self, name):
def node(self, name):
return self._nodes[name]
+ def _get_spice_sim(self):
+ return self._spice_sim
+
+ def _set_spice_sim(self, spice_sim):
+ for subcircuit in self._subcircuits:
+ self._subcircuits[subcircuit].spice_sim = spice_sim
+ self._spice_sim = spice_sim
+
+ spice_sim = property(_get_spice_sim, _set_spice_sim)
+
##############################################
def __getitem__(self, attribute_name):
-
- if attribute_name in self._elements:
- return self.element(attribute_name)
- elif attribute_name in self._models:
- return self.model(attribute_name)
+ attr = str(attribute_name).lower()
+ if attr in self._elements:
+ return self.element(attr)
+ elif attr in self._models:
+ return self.model(attr)
# Fixme: subcircuits
- elif attribute_name in self._nodes:
- return self.node(attribute_name)
+ elif attr in self._nodes:
+ return self.node(attr)
else:
raise IndexError(attribute_name) # KeyError
- ##############################################
-
def __getattr__(self, attribute_name):
try:
return self.__getitem__(attribute_name)
@@ -918,8 +986,18 @@ def __getattr__(self, attribute_name):
##############################################
+ def _find_subcircuit(self, name):
+ name_low = name.lower()
+ if name_low not in self._subcircuits:
+ if hasattr(self, 'parent') and self.parent is not None:
+ return self.parent._find_subcircuit(name_low)
+ else:
+ return None
+ else:
+ return self._subcircuits[name_low]
+
def _add_node(self, node_name):
- node_name = str(node_name)
+ node_name = str(node_name).lower()
if node_name not in self._nodes:
node = Node(self, node_name)
self._nodes[node_name] = node
@@ -942,7 +1020,7 @@ def get_node(self, node, create=False):
if isinstance(node, Node):
return node
else:
- str_node = str(node)
+ str_node = str(node).lower()
if str_node in self._nodes:
return self._nodes[str_node]
elif create:
@@ -957,26 +1035,111 @@ def has_ground_node(self):
##############################################
+ # def _update_used_models_subcircuits(self, subcircuit):
+ # for mod in subcircuit._used_models:
+ # if mod not in subcircuit._models:
+ # self._used_models.add(mod)
+ # for sub in subcircuit._used_subcircuits:
+ # if sub in subcircuit._subcircuits:
+ # if sub in self._subcircuits:
+ # raise ValueError("Repeated subcircuit label: {}".format(sub))
+ # self._update_used_models_subcircuits(subcircuit._subcircuits[sub])
+ # if sub not in self._subcircuits:
+ # self._subcircuits[sub] = subcircuit._subcircuits[sub]
+ # self._used_subcircuits.add(subcircuit.name)
+
+ def _revise_required_models_subcircuits(self, subcircuits):
+ # Check the subcircuits required in the present object
+ # and confirm there are no duplicated subcircuit object with the same name
+ for subcircuit_name, subid in self._used_subcircuits.items():
+ if subcircuit_name in self._subcircuits:
+ subcktid = id(self._subcircuits[subcircuit_name])
+ if subid is None:
+ self._used_subcircuits[subcircuit_name] = subcktid
+ elif subid != subcktid:
+ raise ValueError("Differing subcircuits: {}".format(subcircuit_name))
+
+ # Check the subcircuits required in the present object with respect to parent
+ # and confirm there are no duplicated subcircuit object with the same name
+ for subcircuit_name in self._used_subcircuits:
+ if subcircuit_name in subcircuits:
+ subcktid = id(subcircuits[subcircuit_name])
+ if self._used_subcircuits[subcircuit_name] is not None and self._used_subcircuits[subcircuit_name] != subcktid:
+ raise ValueError("Differing subcircuits: {}".format(subcircuit_name))
+ else:
+ self._used_subcircuits[subcircuit_name] = subcktid
+
+ # Check the subcircuits in the present object with respect to parent
+ # and confirm there are no duplicated subcircuit object with the same name
+ for subcircuit_name in self._subcircuits:
+ if subcircuit_name in subcircuits:
+ if id(self._subcircuits[subcircuit_name]) != id(subcircuits[subcircuit_name]):
+ raise ValueError("Differing subcircuits: {}".format(subcircuit_name))
+ else:
+ _, subcircuits[subcircuit_name] = self._subcircuits.popitem(subcircuit_name)
+
+ # Check the subcircuits in the child objects
+ for subcircuit_name in self._subcircuits:
+ required_subs, required_mods = self._subcircuits[subcircuit_name]._revise_required_models_subcircuits(subcircuits)
+ for subckt, subid in required_subs.items():
+ if subckt in self._used_subcircuits:
+ if self._used_subcircuits[subckt] is None:
+ self._used_subcircuits[subckt] = subid
+ elif subid is not None and subid != self._used_subcircuits[subckt]:
+ raise ValueError("Differing subcircuits: {}".format(subckt))
+ else:
+ self._used_subcircuits[subckt] = subid
+ for mod_name, modid in required_mods.items():
+ if mod_name in self._used_models:
+ if self._used_models[mod_name] is None:
+ self._used_models[mod_name] = modid
+ elif modid is not None and modid != self._used_models[mod_name]:
+ raise ValueError("Differing subcircuits: {}".format(mod_name))
+ else:
+ self._used_models[mod_name] = modid
+ return self._used_subcircuits, self._used_models
+
def _add_element(self, element):
+ from .BasicElement import SubCircuitElement
"""Add an element."""
- if element.name not in self._elements:
- self._elements[element.name] = element
+ element_name = str(element.name).lower()
+ if element_name not in self._elements:
+ self._elements[element_name] = element
+ if hasattr(element, 'model'):
+ model = str(element.model).lower()
+ if model is not None:
+ self._used_models[model] = None
+
+ if isinstance(element, SubCircuitElement):
+ subcircuit_name = str(element.subcircuit_name).lower()
+ if subcircuit_name is not None:
+ self._used_subcircuits[subcircuit_name] = None
+ else:
+ ValueError("Subcircuit not provided for element: {}".format(element_name))
else:
- raise NameError("Element name {} is already defined".format(element.name))
+ raise NameError("Element name {} is already defined".format(element_name))
##############################################
def _remove_element(self, element):
try:
- del self._elements[element.name]
+ del self._elements[str(element.name).lower()]
except KeyError:
raise NameError("Cannot remove undefined element {}".format(element))
##############################################
- def model(self, name, modele_type, **parameters):
+ def parameter(self, name, expression):
+ """Set a parameter."""
+ self._parameters[str(name).lower()] = expression
+
+ ##############################################
+
+ def model(self, name, model_type, **parameters):
+
"""Add a model."""
- model = DeviceModel(name, modele_type, **parameters)
+
+ model = DeviceModel(str(name).lower(), model_type, **parameters)
if model.name not in self._models:
self._models[model.name] = model
else:
@@ -989,7 +1152,10 @@ def model(self, name, modele_type, **parameters):
def subcircuit(self, subcircuit):
"""Add a sub-circuit."""
# Fixme: subcircuit is a class
- self._subcircuits[str(subcircuit.name)] = subcircuit
+ assert isinstance(subcircuit, SubCircuit)
+ self._subcircuits[str(subcircuit.name).lower()] = subcircuit
+ subcircuit._parent = self
+ self._revise_required_models_subcircuits(self._subcircuits)
##############################################
@@ -997,30 +1163,53 @@ def __str__(self):
""" Return the formatted list of element and model definitions. """
# Fixme: order ???
netlist = self._str_raw_spice()
- netlist += self._str_subcircuits() # before elements
- netlist += self._str_elements()
- netlist += self._str_models()
+ if self._parameters:
+ parameters = self._str_parameters()
+ netlist += parameters
+ netlist += os.linesep
+ if self._models:
+ models = self._str_models()
+ netlist += models
+ netlist += os.linesep
+ if self._subcircuits:
+ subcircuits = self._str_subcircuits()
+ netlist += subcircuits # before elements
+ netlist += os.linesep
+ netlist += self._str_elements() + os.linesep
return netlist
##############################################
+ def _str_parameters(self):
+ parameters = [".param {}={}".format(key, ('{%s}' % str_spice(value)) if isinstance(value, Expression) else str_spice(value))
+ for key, value in self._parameters.items()]
+ return join_lines(parameters)
+
+ ##############################################
+
def _str_elements(self):
elements = [element for element in self.elements if element.enabled]
- return join_lines(elements) + os.linesep
+ return join_lines(elements)
##############################################
def _str_models(self):
- if self._models:
- return join_lines(self.models) + os.linesep
+ if self._used_models:
+ models = [self._models[model]
+ for model in self._models
+ if model in self._used_models]
+ return join_lines(models)
else:
return ''
##############################################
def _str_subcircuits(self):
- if self._subcircuits:
- return join_lines(self.subcircuits)
+ if self._used_subcircuits:
+ subcircuits = [self._subcircuits[subcircuit]
+ for subcircuit in self._subcircuits
+ if subcircuit in self._used_subcircuits]
+ return join_lines(subcircuits)
else:
return ''
@@ -1032,6 +1221,34 @@ def _str_raw_spice(self):
netlist += os.linesep
return netlist
+ def include(self, library):
+ from .Library import SpiceLibrary
+
+ """Include a file."""
+ if isinstance(library, SpiceLibrary):
+ spice_library = library
+ else:
+ spice_library = SpiceLibrary(library)
+ models = spice_library.models
+ for model_name in models:
+ self.model(model_name,
+ spice_library[model_name]._model_type,
+ **spice_library[model_name]._parameters)
+ subcircuits = spice_library.subcircuits
+ for subcircuit_name in subcircuits:
+ if self._search_subcircuit(subcircuit_name) is not None:
+ continue
+ subcircuit = spice_library[subcircuit_name]
+ assert subcircuit is not None
+ subcircuit_def = subcircuit.build(parent=self)
+ self.subcircuit(subcircuit_def)
+
+ def _search_subcircuit(self, name):
+ if name in self._subcircuits:
+ return self._subcircuits[name]
+ if self._parent is not None:
+ return self._parent._search_subcircuit(name)
+ return None
####################################################################################################
class SubCircuit(Netlist):
@@ -1041,21 +1258,25 @@ class SubCircuit(Netlist):
##############################################
def __init__(self, name, *nodes, **kwargs):
+ self._included = None
- if len(set(nodes)) != len(nodes):
+ nodes_set = set(nodes)
+ if len(nodes_set) != len(nodes):
raise ValueError("Duplicated nodes in {}".format(nodes))
super().__init__()
- self._name = str(name)
- self._external_nodes = nodes
-
+ self._name = str(name).lower()
+ self._external_nodes = tuple([Node(self, str(node)) for node in nodes])
+ self.PINS = [PinDefinition(position, pin_definition)
+ for position, pin_definition in enumerate(nodes)]
# Fixme: ok ?
- self._ground = kwargs.get('ground', 0)
- if 'ground' in kwargs:
- del kwargs['ground']
+ ground = 'ground'
+ self._ground = kwargs.get(ground, 0)
+ if ground in kwargs:
+ kwargs.pop(ground)
- self._parameters = kwargs
+ self._params = kwargs
##############################################
@@ -1073,6 +1294,14 @@ def clone(self, name=None):
##############################################
+ def parameter(self, name, expression):
+
+ """Set a parameter."""
+
+ self._parameters[str(name)] = expression
+
+ ##############################################
+
@property
def name(self):
return self._name
@@ -1086,6 +1315,16 @@ def parameters(self):
"""Parameters"""
return self._parameters
+ @property
+ def included(self):
+ """Include file"""
+ return self._included
+
+ @property
+ def is_included(self):
+ """is_included"""
+ return self._included is None
+
##############################################
def check_nodes(self):
@@ -1093,31 +1332,93 @@ def check_nodes(self):
"""Check for dangling nodes in the subcircuit."""
nodes = self._external_nodes
- connected_nodes = set()
+ connected_nodes = dict()
+ connected_nodes.update([(node.name, False) for node in nodes])
for element in self.elements:
- connected_nodes.add(nodes & element.nodes)
- not_connected_nodes = nodes - connected_nodes
+ for node in element.nodes:
+ node_name = node.name
+ if node_name in connected_nodes:
+ connected_nodes[node_name] = True
+ else:
+ connected_nodes[node_name] = False
+ not_connected_nodes = [node for node in connected_nodes
+ if not connected_nodes[node]]
if not_connected_nodes:
raise NameError("SubCircuit Nodes {} are not connected".format(not_connected_nodes))
##############################################
def __str__(self):
+ netlist = self._str_raw_spice()
+
"""Return the formatted subcircuit definition."""
nodes = join_list(self._external_nodes)
- parameters = join_list(['{}={}'.format(key, value)
- for key, value in self._parameters.items()])
- netlist = '.subckt ' + join_list((self._name, nodes, parameters)) + os.linesep
- netlist += super().__str__()
+
+ netlist += '.subckt ' + join_list((self._name, nodes))
+ if self._params:
+ parameters = {key: ('{%s}' % str_spice(value)) if isinstance(value, Expression) else str_spice(value)
+ for key, value in self._params.items()}
+ netlist += ' params: ' + join_dict(parameters)
+ netlist += os.linesep
+ if self._parameters:
+ parameters = self._str_parameters()
+ netlist += parameters
+ netlist += os.linesep
+ if self._models:
+ models = self._str_models()
+ netlist += models
+ netlist += os.linesep
+ if self._subcircuits:
+ subcircuits = self._str_subcircuits()
+ netlist += subcircuits # before elements
+ netlist += os.linesep
+ netlist += self._str_elements() + os.linesep
netlist += '.ends ' + self._name + os.linesep
return netlist
####################################################################################################
+class Library(Netlist):
+ """This class implements a library netlist."""
+
+ ##############################################
+
+ def __init__(self, entry):
+ self._entry = entry
+
+ ##############################################
+
+ def clone(self, entry=None):
+ if entry is None:
+ entry = self._entry
+
+ library = self.__class__(entry)
+ self.copy_to(library)
+
+ ##############################################
+
+ @property
+ def entry(self):
+ return self._entry
+
+ ##############################################
+
+ def __str__(self):
+ """Return the formatted library definition."""
+
+ netlist = '.lib ' + self._entry + os.linesep
+ netlist += super().__str__()
+ netlist += '.endl ' + self._entry + os.linesep
+ return netlist
+
+
+####################################################################################################
+
class SubCircuitFactory(SubCircuit):
NAME = None
NODES = None
+ PINS = None
##############################################
@@ -1149,17 +1450,24 @@ def __init__(self, title,
super().__init__()
+ if title is None:
+ title = ""
self.title = str(title)
self._ground = ground
- self._global_nodes = set(global_nodes) # .global
- self._includes = [] # .include
- self._libs = [] # .lib, contains a (name, section) tuple
- self._parameters = {} # .param
+ self._global_nodes = set(global_nodes) # .global
+ self._data = {} # .data
# Fixme: not implemented
# .csparam
# .func
# .if
+ # .lib
+
+ ##############################################
+
+ @property
+ def name(self):
+ return self.title
##############################################
@@ -1180,47 +1488,42 @@ def clone(self, title=None):
##############################################
- def include(self, path):
- """Include a file."""
- if path not in self._includes:
- self._includes.append(path)
- else:
- self._logger.warn("Duplicated include")
-
- ##############################################
-
- def lib(self, name, section=None):
- """Load a library."""
- v = (name, section)
- if v not in self._libs:
- self._libs.append(v)
- else:
- self._logger.warn(f"Duplicated lib {v}")
-
##############################################
- def parameter(self, name, expression):
- """Set a parameter."""
- self._parameters[str(name)] = str(expression)
+ def data(self, table, **kwargs):
+ self._data.update[table] = kwargs
##############################################
- def str(self, simulator=None):
+ def str(self, spice_sim=None):
+ if spice_sim is not None:
+ self.spice_sim = spice_sim
"""Return the formatted desk."""
# if not self.has_ground_node():
# raise NameError("Circuit don't have ground node")
netlist = self._str_title()
- netlist += self._str_includes(simulator)
- netlist += self._str_libs(simulator)
- netlist += self._str_globals()
- netlist += self._str_parameters()
+ netlist += os.linesep
+ # netlist += self._str_includes(simulator)
+ for sub_circuit in self._subcircuits:
+ self._subcircuits[sub_circuit].spice_sim = self.spice_sim
+
+ if self._global_nodes:
+ netlist += self._str_globals() + os.linesep
netlist += super().__str__()
return netlist
##############################################
def _str_title(self):
- return '.title {}'.format(self.title) + os.linesep
+ if self.title:
+ lines = self.title.splitlines()
+ title = '.title {}'.format(lines[0]) + os.linesep
+ if len(lines) > 1:
+ for line in lines[1:]:
+ title += '* {}'.format(line) + os.linesep
+ return title
+ else:
+ return '.title' + os.linesep
##############################################
@@ -1242,44 +1545,18 @@ def _str_includes(self, simulator=None):
##############################################
- def _str_libs(self, simulator=None):
- if self._libs:
- libs = []
- for lib, section in self._libs:
- lib = Path(str(lib)).resolve()
- if simulator:
- lib_flavour = Path(f"{lib}@{simulator}")
- if lib_flavour.exists():
- lib = lib_flavour
- s = f".lib {lib}"
- if section:
- s += f" {section}"
- libs.append(s)
- return os.linesep.join(libs) + os.linesep
- else:
- return ''
-
- ##############################################
-
def _str_globals(self):
- if self._global_nodes:
- return '.global ' + join_list(self._global_nodes) + os.linesep
- else:
- return ''
-
- ##############################################
- def _str_parameters(self):
- if self._parameters:
- return ''.join([f'.param {key}={value}' + os.linesep
- for key, value in self._parameters.items()])
+ if self._global_nodes:
+ return join_lines(['.global {}'.format(str_spice(node))
+ for node in self._global_nodes])
else:
return ''
##############################################
def __str__(self):
- return self.str(simulator=None)
+ return self.str(spice_sim=None)
##############################################
@@ -1290,3 +1567,17 @@ def str_end(self):
def simulator(self, *args, **kwargs):
return CircuitSimulator.factory(self, *args, **kwargs)
+
+
+####################################################################################################
+
+class Comment:
+
+ def __init__(self, txt=''):
+ self._txt = txt
+
+ def __str__(self):
+ return self._txt
+
+ def __repr__(self):
+ return "Comment({})".format(repr(self._txt))
diff --git a/PySpice/Spice/NgSpice/Shared.py b/PySpice/Spice/NgSpice/Shared.py
index 183c08a4b..dd2777e77 100644
--- a/PySpice/Spice/NgSpice/Shared.py
+++ b/PySpice/Spice/NgSpice/Shared.py
@@ -101,7 +101,7 @@
WaveForm,
)
from PySpice.Tools.EnumFactory import EnumFactory
-from PySpice.Unit import u_V, u_A, u_s, u_Hz, u_F, u_Degree
+from PySpice.Unit import u_V, u_A, u_s, u_Hz, u_F, u_celsius
from .SimulationType import SIMULATION_TYPE
diff --git a/PySpice/Spice/NgSpice/Simulation.py b/PySpice/Spice/NgSpice/Simulation.py
index 92b62ec24..b7ee69362 100644
--- a/PySpice/Spice/NgSpice/Simulation.py
+++ b/PySpice/Spice/NgSpice/Simulation.py
@@ -24,12 +24,14 @@
####################################################################################################
import logging
+import os
####################################################################################################
from ..Simulation import CircuitSimulator
from .Server import SpiceServer
from .Shared import NgSpiceShared
+from PySpice.Tools.StringTools import join_list
####################################################################################################
@@ -69,6 +71,24 @@ def __init__(self, circuit, **kwargs):
##############################################
+ def save_str(self):
+ result = ""
+ if self._saved_nodes:
+ # Place 'all' first
+ saved_nodes = set(self._saved_nodes)
+ if 'all' in saved_nodes:
+ all_str = ' all'
+ saved_nodes.remove('all')
+ else:
+ all_str = ''
+ result += '.save' + all_str
+ if saved_nodes:
+ result += ' ' + join_list(saved_nodes)
+ result += os.linesep
+ return result
+
+ ##############################################
+
def _run(self, analysis_method, *args, **kwargs):
super()._run(analysis_method, *args, **kwargs)
diff --git a/PySpice/Spice/Parser_jmgc.py b/PySpice/Spice/Parser_jmgc.py
deleted file mode 100644
index c28e04d72..000000000
--- a/PySpice/Spice/Parser_jmgc.py
+++ /dev/null
@@ -1,1486 +0,0 @@
-####################################################################################################
-#
-# PySpice - A Spice Package for Python
-# Copyright (C) 2020 jmgc / Fabrice Salvaire
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-#
-####################################################################################################
-
-####################################################################################################
-
-"""This module implements a partial SPICE netlist parser.
-
-See the :command:`cir2py` tool for an example of usage of the parser.
-
-It would be difficult to implement a full parser for Ngspice since the syntax is mainly contextual.
-
-SPICE is case insensitive.
-
-"""
-
-####################################################################################################
-
-from collections import OrderedDict
-import logging
-import os
-import regex
-
-####################################################################################################
-
-from .ElementParameter import FlagParameter
-from .Netlist import ElementParameterMetaClass, Circuit, SubCircuit
-
-####################################################################################################
-
-_module_logger = logging.getLogger(__name__)
-
-####################################################################################################
-
-class ParseError(NameError):
- pass
-
-####################################################################################################
-
-class PrefixData:
-
- """This class represents a device prefix."""
-
- ##############################################
-
- def __init__(self, prefix, classes):
-
- self.prefix = prefix
- self.classes = classes
-
- number_of_positionals_min = 1000
- number_of_positionals_max = 0
- has_optionals = False
- for element_class in classes:
- number_of_positionals = element_class.number_of_positional_parameters
- number_of_positionals_min = min(number_of_positionals_min, number_of_positionals)
- number_of_positionals_max = max(number_of_positionals_max, number_of_positionals)
- has_optionals = max(has_optionals, bool(element_class.optional_parameters))
-
- self.number_of_positionals_min = number_of_positionals_min
- self.number_of_positionals_max = number_of_positionals_max
- self.has_optionals = has_optionals
-
- self.multi_devices = len(classes) > 1
- self.has_variable_number_of_pins = prefix in ('Q', 'X') # NPinElement, Q has 3 to 4 pins
- if self.has_variable_number_of_pins:
- self.number_of_pins = None
- else:
- # Q and X are single
- self.number_of_pins = classes[0].number_of_pins
-
- self.has_flag = False
- for element_class in classes:
- for parameter in element_class.optional_parameters.values():
- if isinstance(parameter, FlagParameter):
- self.has_flag = True
-
- ##############################################
-
- def __len__(self):
- return len(self.classes)
-
- ##############################################
-
- def __iter__(self):
- return iter(self.classes)
-
- ##############################################
-
- @property
- def single(self):
- if not self.multi_devices:
- return self.classes[0]
- else:
- raise NameError()
-
-####################################################################################################
-
-_prefix_cache = {}
-for prefix, classes in ElementParameterMetaClass._classes.items():
- prefix_data = PrefixData(prefix, classes)
- _prefix_cache[prefix] = prefix_data
- _prefix_cache[prefix.lower()] = prefix_data
-
-# for prefix_data in sorted(_prefix_cache.values(), key=lambda x: len(x)):
-# print(prefix_data.prefix,
-# len(prefix_data),
-# prefix_data.number_of_positionals_min, prefix_data.number_of_positionals_max,
-# prefix_data.has_optionals)
-
-# Single:
-# B 0 True
-# D 1 True
-# F 2 False
-# G 1 False
-# H 2 False
-# I 1 False
-# J 1 True
-# K 3 False
-# M 1 True
-# S 2 False
-# V 1 False
-# W 3 False
-# Z 1 True
-
-# Two:
-# E 0 1 False
-# L 1 2 True
-
-# Three:
-# C 1 2 True
-# R 1 2 True
-
-# NPinElement:
-# Q 1 1 True
-# X 1 1 False
-
-####################################################################################################
-
-class Statement:
-
- """This base class implements a statement, in fact a line in a Spice netlist."""
-
- ##############################################
-
- def __init__(self, line, statement=None):
- self._line = line
- if statement is not None:
- self._line.lower_case_statement(statement)
-
- ##############################################
-
- def __repr__(self):
- return '{} {}'.format(self.__class__.__name__, repr(self._line))
-
- ##############################################
-
- def value_to_python(self, x):
- if x:
- if str(x)[0].isdigit():
- return str(x)
- else:
- return "'{}'".format(x)
- else:
- return ''
-
- ##############################################
-
- def values_to_python(self, values):
- return [self.value_to_python(x) for x in values]
-
- ##############################################
-
- def kwargs_to_python(self, kwargs):
- return ['{}={}'.format(key, self.value_to_python(value))
- for key, value in kwargs.items()]
-
- ##############################################
-
- def join_args(self, args):
- return ', '.join(args)
-
-####################################################################################################
-
-class Comment(Statement):
- pass
-
-####################################################################################################
-
-class Title(Statement):
-
- """This class implements a title definition."""
-
- ##############################################
-
- def __init__(self, line):
- super().__init__(line, statement='title')
- self._title = self._line.right_of('.title')
-
- ##############################################
-
- def __str__(self):
- return self._title
-
- ##############################################
-
- def __repr__(self):
- return 'Title {}'.format(self._title)
-
-####################################################################################################
-
-class Include(Statement):
-
- """This class implements a include definition."""
-
- ##############################################
-
- def __init__(self, line):
- super().__init__(line, statement='include')
- self._include = self._line.right_of('.include')
-
- ##############################################
-
- def __str__(self):
- return self._include
-
- ##############################################
-
- def __repr__(self):
- return 'Include {}'.format(self._include)
-
- ##############################################
-
- def to_python(self, netlist_name):
- return '{}.include({})'.format(netlist_name, self._include) + os.linesep
-
-####################################################################################################
-
-class Model(Statement):
-
- """This class implements a model definition.
-
- Spice syntax::
-
- .model mname type(pname1=pval1 pname2=pval2 ... )
-
- """
-
- ##############################################
-
- def __init__(self, line):
- super().__init__(line, statement='model')
-
- base, self._parameters = line.split_keyword('.model')
- self._name, self._model_type = base
- self._name = self._name.lower()
-
- ##############################################
-
- @property
- def name(self):
- """Name of the model"""
- return self._name
-
- ##############################################
-
- def __repr__(self):
- return 'Model {} {} {}'.format(self._name, self._model_type, self._parameters)
-
- ##############################################
-
- def to_python(self, netlist_name):
- args = self.values_to_python((self._name, self._model_type))
- kwargs = self.kwargs_to_python(self._parameters)
- return '{}.model({})'.format(netlist_name, self.join_args(args + kwargs)) + os.linesep
-
- ##############################################
-
- def build(self, circuit):
- return circuit.model(self._name, self._model_type, **self._parameters)
-
-####################################################################################################
-
-class Parameter(Statement):
-
- """This class implements a parameter definition.
-
- Spice syntax::
-
- .param name=expr
-
- """
-
- ##############################################
-
- def __init__(self, line):
- super().__init__(line, statement='param')
-
- text = line.right_of('.param').strip().lower() # Fixme: lower ???
- idx = text.find('=')
- self._name = text[:idx].strip()
- self._value = text[idx + 1:].strip()
-
- ##############################################
-
- @property
- def name(self):
- """Name of the model"""
- return self._name
-
- ##############################################
-
- def __repr__(self):
- return 'Param {}={}'.format(self._name, self._value)
-
- ##############################################
-
- def to_python(self, netlist_name):
- args = self.values_to_python((self._name, self._value))
- # Fixme: linesep here ???
- return '{}.param({})'.format(netlist_name, self.join_args(args)) + os.linesep
-
- ##############################################
-
- def build(self, circuit):
- circuit.parameter(self._name, self._value)
-
-####################################################################################################
-
-# Review: HERE
-
-class CircuitStatement(Statement):
-
- # Review: jmgc
-
- """This class implements a circuit definition.
-
- Spice syntax::
-
- Title ...
-
- """
-
- ##############################################
-
- def __init__(self, title):
-
- super().__init__(title, statement='title')
-
- # Review: Title
- title_statement = '.title '
- self._title = str(title)
- if self._title.startswith(title_statement):
- self._title = self._title[len(title_statement):]
-
- self._statements = []
- self._subcircuits = []
- self._models = []
- self._required_subcircuits = set()
- self._required_models = set()
- self._params = []
-
- ##############################################
-
- @property
- def title(self):
- """Title of the circuit."""
- return self._title
-
- @property
- def name(self):
- """Name of the circuit."""
- return self._title
-
- @property
- def models(self):
- """Models of the circuit."""
- return self._models
-
- @property
- def subcircuits(self):
- """Subcircuits of the circuit."""
- return self._subcircuits
-
- @property
- def params(self):
- """Parameters of the circuit."""
- return self._params
-
- ##############################################
-
- def __repr__(self):
- text = 'Circuit {}'.format(self._title) + os.linesep
- text += os.linesep.join([repr(model) for model in self._models]) + os.linesep
- text += os.linesep.join([repr(subcircuit) for subcircuit in self._subcircuits]) + os.linesep
- text += os.linesep.join([' ' + repr(statement) for statement in self._statements])
- return text
-
- ##############################################
-
- def __iter__(self):
- """Return an iterator on the statements."""
- return iter(self._models + self._subcircuits + self._statements)
-
- ##############################################
-
- def append(self, statement):
- """Append a statement to the statement's list."""
- self._statements.append(statement)
-
- ##############################################
-
- def append_model(self, statement):
- """Append a model to the statement's list."""
- self._models.append(statement)
-
- ##############################################
-
- def append_param(self, statement):
- """Append a param to the statement's list."""
- self._params.append(statement)
-
- ##############################################
-
- def append_subcircuit(self, statement):
- """Append a subcircuit to the statement's list."""
- self._subcircuits.append(statement)
-
- ##############################################
-
- def to_python(self, ground=0):
- subcircuit_name = 'subcircuit_' + self._name
- args = self.values_to_python([subcircuit_name] + self._nodes)
- source_code = ''
- source_code += '{} = SubCircuit({})'.format(subcircuit_name, self.join_args(args)) + os.linesep
- source_code += SpiceParser.netlist_to_python(subcircuit_name, self, ground)
- return source_code
-
- ##############################################
-
- def build(self, ground=0):
- circuit = Circuit(self._title)
- for statement in self._params:
- statement.build(circuit)
- for statement in self._models:
- model = statement.build(circuit)
- for statement in self._subcircuits:
- subckt = statement.build(ground) # Fixme: ok ???
- circuit.subcircuit(subckt)
- for statement in self._statements:
- if isinstance(statement, Element):
- statement.build(circuit, ground)
- return circuit
-
-####################################################################################################
-
-class SubCircuitStatement(Statement):
-
- """This class implements a sub-circuit definition.
-
- Spice syntax::
-
- .SUBCKT name node1 ... param1=value1 ...
-
- """
-
- ##############################################
-
- def __init__(self, line):
-
- super().__init__(line, statement='subckt')
-
- # Fixme
- parameters, dict_parameters = self._line.split_keyword('.subckt')
- # Review: syntax ???
- if parameters[-1].lower() == 'params:':
- parameters = parameters[:-1]
- self._name, self._nodes = parameters[0], parameters[1:]
- self._name = self._name.lower()
- self._parameters = dict_parameters
-
- self._statements = []
- self._subcircuits = []
- self._models = []
- self._required_subcircuits = set()
- self._required_models = set()
- self._params = []
-
- ##############################################
-
- @property
- def name(self):
- """Name of the sub-circuit."""
- return self._name
-
- @property
- def nodes(self):
- """Nodes of the sub-circuit."""
- return self._nodes
-
- @property
- def models(self):
- """Models of the sub-circuit."""
- return self._models
-
- @property
- def params(self):
- """Params of the sub-circuit."""
- return self._params
-
- @property
- def subcircuits(self):
- """Subcircuits of the sub-circuit."""
- return self._subcircuits
-
- ##############################################
-
- def __repr__(self):
- if self._parameters:
- text = 'SubCircuit {} {} Parameters: {}'.format(self._name, self._nodes, self._parameters) + os.linesep
- else:
- text = 'SubCircuit {} {}'.format(self._name, self._nodes) + os.linesep
- text += os.linesep.join([repr(model) for model in self._models]) + os.linesep
- text += os.linesep.join([repr(subcircuit) for subcircuit in self._subcircuits]) + os.linesep
- text += os.linesep.join([' ' + repr(statement) for statement in self._statements])
- return text
-
- ##############################################
-
- def __iter__(self):
- """Return an iterator on the statements."""
- return iter(self._models + self._subcircuits + self._statements)
-
- ##############################################
-
- def append(self, statement):
- """Append a statement to the statement's list."""
- self._statements.append(statement)
-
- ##############################################
-
- def append_model(self, statement):
- """Append a model to the statement's list."""
- self._models.append(statement)
-
- ##############################################
-
- def append_param(self, statement):
- """Append a param to the statement's list."""
- self._params.append(statement)
-
- ##############################################
-
- def append_subcircuit(self, statement):
- """Append a model to the statement's list."""
- self._subcircuits.append(statement)
-
- ##############################################
-
- def to_python(self, ground=0):
- subcircuit_name = 'subcircuit_' + self._name
- args = self.values_to_python([subcircuit_name] + self._nodes)
- source_code = ''
- source_code += '{} = SubCircuit({})'.format(subcircuit_name, self.join_args(args)) + os.linesep
- source_code += SpiceParser.netlist_to_python(subcircuit_name, self, ground)
- return source_code
-
- ##############################################
-
- def build(self, ground=0, parent=None):
- subcircuit = SubCircuit(self._name, *self._nodes, **self._parameters)
- subcircuit.parent = parent
- for statement in self._params:
- statement.build(subcircuit)
- for statement in self._models:
- model = statement.build(subcircuit)
- for statement in self._subcircuits:
- subckt = statement.build(ground, parent=subcircuit) # Fixme: ok ???
- subcircuit.subcircuit(subckt)
- for statement in self._statements:
- if isinstance(statement, Element):
- statement.build(subcircuit, ground)
- return subcircuit
-
-####################################################################################################
-
-class Element(Statement):
-
- """This class implements an element definition.
-
- "{ expression }" are allowed in device line.
-
- """
-
- _logger = _module_logger.getChild('Element')
-
- ##############################################
-
- def __init__(self, line):
-
- super().__init__(line)
-
- line_str = str(line)
- # self._logger.debug(os.linesep + line_str)
-
- # Retrieve device prefix
- prefix = line_str[0]
- if prefix.isalpha():
- self._prefix = prefix
- else:
- raise ParseError("Not an element prefix: " + prefix)
- prefix_data = _prefix_cache[self._prefix]
-
- # Retrieve device name
- args, kwargs = line.split_element(prefix)
- self._name = args.pop(0)
-
- self._nodes = []
- self._parameters = []
- self._dict_parameters = {}
-
- # Read nodes
- if not prefix_data.has_variable_number_of_pins:
- number_of_pins = prefix_data.number_of_pins
- if number_of_pins:
- self._nodes = args[:number_of_pins]
- args = args[number_of_pins:]
- else: # Q or X
- if prefix_data.prefix == 'Q':
- self._nodes = args[:3]
- args = args[3:]
- # Fixme: optional node
- else: # X
- if args[-1].lower() == 'params:':
- args.pop()
- self._parameters.append(args.pop())
- self._nodes = args
- args = []
-
- # Read positionals
- number_of_positionals = prefix_data.number_of_positionals_min
- if number_of_positionals and (len(args) > 0) and (prefix_data.prefix != 'X'): # model is optional
- self._parameters = args[:number_of_positionals]
- args = args[number_of_positionals:]
- if prefix_data.multi_devices and (len(args) > 0):
- remaining = args
- args = []
- self._parameters.extend(remaining)
-
- if prefix_data.prefix in ('V', 'I') and (len(args) > 0):
- # merge remaining
- self._parameters[-1] += " " + " ".join(args)
- self._dict_parameters = kwargs
-
- # Read optionals
- if (prefix_data.has_optionals or (prefix_data.prefix == 'X')) and (len(kwargs) > 0):
- for key in kwargs:
- self._dict_parameters[key] = kwargs[key]
-
- if prefix_data.multi_devices:
- for element_class in prefix_data:
- if len(self._parameters) == element_class.number_of_positional_parameters:
- break
- else:
- element_class = prefix_data.single
- self.factory = element_class
-
- # Move positionals passed as kwarg
- to_delete = []
- for parameter in element_class.positional_parameters.values():
- if parameter.key_parameter:
- idx = parameter.position
- if idx < len(self._parameters):
- self._dict_parameters[parameter.attribute_name] = self._parameters[idx]
- to_delete.append(idx - len(to_delete))
- for idx in to_delete:
- self._parameters.pop(idx)
-
- # self._logger.debug(os.linesep + self.__repr__())
-
- ##############################################
-
- @property
- def name(self):
- """Name of the element"""
- return self._name
-
- ##############################################
-
- def __repr__(self):
- return 'Element {0._prefix} {0._name} {0._nodes} {0._parameters} {0._dict_parameters}'.format(self)
-
- ##############################################
-
- def translate_ground_node(self, ground):
- nodes = []
- for node in self._nodes:
- if str(node) == str(ground):
- node = 0
- nodes.append(node)
- return nodes
-
- ##############################################
-
- def to_python(self, netlist_name, ground=0):
-
- nodes = self.translate_ground_node(ground)
- args = [self._name]
- if self._prefix != 'X':
- args += nodes + self._parameters
- else: # != Spice
- args += self._parameters + nodes
- args = self.values_to_python(args)
- kwargs = self.kwargs_to_python(self._dict_parameters)
- return '{}.{}({})'.format(netlist_name, self._prefix, self.join_args(args + kwargs)) + os.linesep
-
- ##############################################
-
- def _check_params(self, elements=1):
- params = []
- for param in self._parameters:
- values = param.replace(',', ' ')
- if values[0] == '(' and values[-1] == ')':
- values = values[1: -1].split()
- if len(values) > elements:
- raise IndexError('Incorrect number of elements for (%r): %s' % (self, param))
- params.extend(values)
- else:
- params.extend(values.split())
- self._parameters = params
-
- ##############################################
-
- def _voltage_controlled_nodes(self, poly_arg):
- result = ['v(%s,%s)' % nodes
- for nodes in zip(self._parameters[:(2 * poly_arg):2],
- self._parameters[1:(2 * poly_arg):2])]
- result += self._parameters[2 * poly_arg:]
- return ' '.join(result)
-
- ##############################################
-
- def _current_controlled_nodes(self, poly_arg):
- result = ['i(%s)' % node
- for node in self._parameters[:poly_arg]]
- result += self._parameters[poly_arg:]
- return ' '.join(result)
-
- ##############################################
-
- def _manage_controlled_sources(self, nodes):
- try:
- idx = self._nodes.index('POLY')
- if idx == 2:
- poly_arg = self._nodes[3]
- if poly_arg[0] == '(' and poly_arg[-1] == ')':
- poly_arg = poly_arg[1:-1]
- try:
- poly_arg = int(poly_arg)
- except TypeError as te:
- raise TypeError('Not valid poly argument: %s' % poly_arg, te)
- self._nodes = self._nodes[:2]
- nodes = nodes[:2]
- if self._prefix in 'EG':
- self._check_params(2)
- values = self._voltage_controlled_nodes(poly_arg)
- if self._prefix == 'E':
- key = 'v'
- else:
- key = 'i'
- else:
- self._check_params(1)
- values = self._current_controlled_nodes(poly_arg)
- if self._prefix == 'F':
- key = 'v'
- else:
- key = 'i'
- poly_str = '{ POLY (%d) %s }' % (poly_arg, values)
-
- self._dict_parameters[key] = poly_str
- self._parameters.clear()
- self._name = self._prefix + self._name
- self._prefix = 'B'
- prefix_data = _prefix_cache[self._prefix]
- self.factory = prefix_data.single
- return nodes
- raise IndexError('Incorrect position of POLY: %r' % self)
- except ValueError:
- pass
- _correction = []
- correction = []
- for _node, node in zip(self._nodes, nodes):
- _values = _node.replace(',', ' ')
- try:
- values = node.replace(',', ' ')
- except AttributeError:
- values = str(node)
- if _values[0] == '(' and _values[-1] == ')':
- _values = _values[1: -1]
- if values[0] == '(' and values[-1] == ')':
- values = values[1: -1]
- _correction.extend(_values.split())
- correction.extend(values.split())
- self._parameters = correction[len(self._nodes):] + self._parameters
- self._nodes = _correction[:len(self._nodes)]
- parameters = self._parameters
- correction = correction[:len(self._nodes)]
- if self._prefix in 'EG':
- if len(correction) + len(parameters) == 5:
- parameters = correction[2:] + parameters
- self._nodes = _correction[:2]
- value = '{v(%s, %s) * %s}' % tuple(parameters)
- if self._prefix == 'E':
- key = 'v'
- else:
- key = 'i'
- self._dict_parameters[key] = value
- self._parameters.clear()
- self._name = self._prefix + self._name
- self._prefix = 'B'
- prefix_data = _prefix_cache[self._prefix]
- self.factory = prefix_data.single
- else:
- if len(correction) + len(parameters) == 4:
- parameters = correction[2:] + parameters
- self._nodes = _correction[:2]
- value = '{i(%s) * %s}' % tuple(parameters)
- if self._prefix == 'F':
- key = 'v'
- else:
- key = 'i'
- self._dict_parameters[key] = value
- self._parameters.clear()
- self._name = self._prefix + self._name
- self._prefix = 'B'
- prefix_data = _prefix_cache[self._prefix]
- self.factory = prefix_data.single
- return correction[:len(self._nodes)]
-
- ##############################################
-
- def build(self, circuit, ground=0):
-
- nodes = self.translate_ground_node(ground)
- if self._prefix != 'X':
- if self._prefix in ('EFGH'):
- nodes = self._manage_controlled_sources(nodes)
- args = nodes + self._parameters
- else: # != Spice
- args = self._parameters + nodes
- factory = getattr(circuit, self.factory.__alias__)
- kwargs = self._dict_parameters
- message = ' '.join([str(x) for x in (self._prefix, self._name, args,
- self._dict_parameters)])
- self._logger.debug(message)
- return factory(self._name, *args, **kwargs)
-
-
-####################################################################################################
-
-class Line:
-
- """This class implements a line in the netlist."""
-
- _logger = _module_logger.getChild('Line')
-
- ##############################################
-
- def __init__(self, line, line_range, end_of_line_comment):
-
- self._end_of_line_comment = end_of_line_comment
-
- text, comment, self._is_comment = self._split_comment(line)
-
- self._text = text
- self._comment = comment
- self._line_range = line_range
-
- ##############################################
-
- def __repr__(self):
- return '{0._line_range}: {0._text} // {0._comment}'.format(self)
-
- ##############################################
-
- def __str__(self):
- return self._text
-
- ##############################################
-
- @property
- def comment(self):
- return self._comment
-
- @property
- def is_comment(self):
- return self._is_comment
-
- ##############################################
-
- def _split_comment(self, line):
-
- line = str(line)
-
- if line.startswith('*'):
- is_comment = True
- text = ''
- comment = line[1:].strip()
- else:
- is_comment = False
- # remove end of line comment
- location = -1
- for marker in self._end_of_line_comment:
- _location = line.find(marker)
- if _location != -1:
- if location == -1:
- location = _location
- else:
- location = min(_location, location)
- if location != -1:
- text = line[:location].strip()
- comment = line[location:].strip()
- else:
- text = line
- comment = ''
-
- return text, comment, is_comment
-
- ##############################################
-
- def append(self, line):
-
- text, comment, is_comment = self._split_comment(line)
-
- if text:
- if not self._text.endswith(' ') or text.startswith(' '):
- self._text += ' '
- self._text += text
- if comment:
- self._comment += ' // ' + comment
-
- _slice = self._line_range
- self._line_range = slice(_slice.start, _slice.stop + 1)
-
- ##############################################
-
- def lower_case_statement(self, statement):
-
- """Lower case the statement"""
-
- # statement without . prefix
-
- if self._text:
- lower_statement = statement.lower()
- _slice = slice(1, len(statement) + 1)
- _statement = self._text[_slice]
- if _statement.lower() == lower_statement:
- self._text = '.' + lower_statement + self._text[_slice.stop:]
-
- ##############################################
-
- def right_of(self, text):
- return self._text[len(text):].strip()
-
- ##############################################
-
- def read_words(self, start_location, number_of_words):
-
- """Read a fixed number of words separated by space."""
-
- words = []
- stop_location = None
-
- line_str = self._text
- number_of_words_read = 0
- while number_of_words_read < number_of_words: # and start_location < len(line_str)
- if line_str[start_location] == '{':
- stop_location = line_str.find('}', start_location)
- if stop_location > start_location:
- stop_location += 1
- else:
- stop_location = line_str.find(' ', start_location)
- if stop_location == -1:
- stop_location = None # read until end
- word = line_str[start_location:stop_location].strip()
- if word:
- number_of_words_read += 1
- words.append(word)
- if stop_location is None: # we should stop
- if number_of_words_read != number_of_words:
- template = 'Bad element line, looking for word {}/{}:' + os.linesep
- message = (template.format(number_of_words_read, number_of_words) +
- line_str + os.linesep +
- ' ' * start_location + '^')
- self._logger.warning(message)
- raise ParseError(message)
- else:
- if start_location < stop_location:
- start_location = stop_location
- else: # we have read a space
- start_location += 1
-
- return words, stop_location
-
- ##############################################
-
- def split_words(self, start_location, until=None):
-
- stop_location = None
-
- line_str = self._text
- if until is not None:
- location = line_str.find(until, start_location)
- if location != -1:
- stop_location = location
- location = line_str.rfind(' ', start_location, stop_location)
- if location != -1:
- stop_location = location
- else:
- raise NameError('Bad element line, missing key? ' + line_str)
-
- line_str = line_str[start_location:stop_location]
- words = [x for x in line_str.split(' ') if x]
- result = []
- expression = 0
- begin_idx = 0
- for idx, word in enumerate(words):
- if expression == 0:
- begin_idx = idx
- expression += word.count('{') - word.count('}')
- if expression == 0:
- if begin_idx < idx:
- result.append(' '.join(words[begin_idx:idx + 1]))
- else:
- result.append(word)
- return result, stop_location
-
- ##############################################
-
- @staticmethod
- def get_kwarg(text):
-
- dict_parameters = {}
-
- parts = []
- for part in text.split():
- if '=' in part and part != '=':
- left, right = [x for x in part.split('=')]
- parts.append(left)
- parts.append('=')
- if right:
- parts.append(right)
- else:
- parts.append(part)
-
- i = 0
- i_stop = len(parts)
- while i < i_stop:
- if i + 1 < i_stop and parts[i + 1] == '=':
- key, value = parts[i], parts[i + 2]
- dict_parameters[key] = value
- i += 3
- else:
- raise ParseError("Bad kwarg: {}".format(text))
-
- return dict_parameters
-
- ##############################################
-
- @staticmethod
- def _partition(text):
- parts = []
- values = text.replace(',', ' ')
- for part in values.split():
- if '=' in part and part != '=':
- left, right = [x for x in part.split('=')]
- parts.append(left)
- parts.append('=')
- if right:
- parts.append(right)
- else:
- parts.append(part)
- return parts
-
- ##############################################
-
- @staticmethod
- def _partition_parentheses(text):
- p = regex.compile(r'\(([^\(\)]|(?R))*?\)')
- parts = []
- previous_start = 0
- for m in regex.finditer(p, text):
- parts.extend(Line._partition(text[previous_start:m.start()]))
- parts.append(m.group())
- previous_start = m.end()
- parts.extend(Line._partition(text[previous_start:]))
- return parts
-
- ##############################################
-
- @staticmethod
- def _partition_braces(text):
- p = regex.compile(r'\{([^\{\}]|(?R))*?\}')
- parts = []
- previous_start = 0
- for m in regex.finditer(p, text):
- parts.extend(Line._partition_parentheses(text[previous_start:m.start()]))
- parts.append(m.group())
- previous_start = m.end()
- parts.extend(Line._partition_parentheses(text[previous_start:]))
- return parts
-
- ##############################################
-
- @staticmethod
- def _check_parameters(parts):
- parameters = []
- dict_parameters = {}
-
- i = 0
- i_stop = len(parts)
- while i < i_stop:
- if i + 1 < i_stop and parts[i + 1] == '=':
- key, value = parts[i], parts[i + 2]
- dict_parameters[key] = value
- i += 3
- else:
- parameters.append(parts[i])
- i += 1
-
- return parameters, dict_parameters
-
- ##############################################
-
- def split_keyword(self, keyword):
-
- """Split the line according to the following pattern::
-
- keyword parameter1 parameter2 ( key1=value1 key2=value2 )
-
- Return the list of parameters and the dictionary.
- The parenthesis can be omitted.
-
- """
-
- text = self.right_of(keyword)
-
- p = regex.compile(r'\(([^\(\)]|(?R))*?\)')
- b = regex.compile(r'\{([^\{\}]|(?R))*?\}')
- parts = []
-
- mp = regex.search(p, text)
- mb = regex.search(b, text)
- if mb is not None:
- if mp is not None:
- if (mb.start() > mp.start()) and (mb.end() < mp.end()):
- parts.extend(Line._partition(text[:mp.start()]))
- parts.extend(Line._partition_braces(mp.group()[1:-1]))
- elif (mb.start() < mp.start()) and (mb.end() > mp.end()):
- parts.extend(Line._partition_braces(text))
- else:
- raise ValueError("Incorrect format {}".format(text))
- else:
- parts.extend(Line._partition_braces(text))
- else:
- if mp is not None:
- parts.extend(Line._partition(text[:mp.start()]))
- parts.extend(Line._partition(mp.group()[1:-1]))
- else:
- parts.extend(Line._partition(text))
- return Line._check_parameters(parts)
-
- ##############################################
-
- def split_element(self, prefix):
-
- """Split the line according to the following pattern::
-
- keyword parameter1 parameter2 ... key1=value1 key2=value2 ...
-
- Return the list of parameters and the dictionary.
-
- """
-
- # Fixme: cf. get_kwarg
-
- parameters = []
- dict_parameters = {}
-
- text = self.right_of(prefix)
-
- parts = Line._partition_braces(text)
-
- return Line._check_parameters(parts)
-
-####################################################################################################
-
-class SpiceParser:
-
- """This class parse a Spice netlist file and build a syntax tree.
-
- Public Attributes:
-
- :attr:`circuit`
-
- :attr:`models`
-
- :attr:`subcircuits`
-
- """
-
- _logger = _module_logger.getChild('SpiceParser')
-
- ##############################################
-
- def __init__(self, path=None, source=None, end_of_line_comment=('$', '//', ';')):
-
- # Fixme: empty source
-
- if path is not None:
- with open(str(path), 'r') as fh:
- raw_lines = fh.readlines() # Fixme: cf. jmgc
- elif source is not None:
- raw_lines = source.split(os.linesep)
- else:
- raise ValueError
-
- self._end_of_line_comment = end_of_line_comment
-
- lines = self._merge_lines(raw_lines)
- self._title = None
- self._statements = self._parse(lines)
-
- ##############################################
-
- def _merge_lines(self, raw_lines):
-
- """Merge broken lines and return a new list of lines.
-
- A line starting with "+" continues the preceding line.
-
- """
-
- lines = []
- current_line = None
- for line_index, line_string in enumerate(raw_lines):
- if line_string.startswith('+'):
- current_line.append(line_string[1:].strip('\r\n'))
- else:
- line_string = line_string.strip(' \t\r\n')
- if line_string:
- _slice = slice(line_index, line_index + 1)
- line = Line(line_string, _slice, self._end_of_line_comment)
- lines.append(line)
- # handle case with comment before line continuation
- if not line_string.startswith('*'):
- current_line = line
-
- return lines
-
- ##############################################
-
- @staticmethod
- def _check_models(circuit, available_models=set()):
- p_available_models = available_models.copy()
- p_available_models.update([model.name for model in circuit._models])
- for subcircuit in circuit._subcircuits:
- SpiceParser._check_models(subcircuit, p_available_models)
- for model in circuit._required_models:
- if model not in p_available_models:
- raise ValueError("model (%s) not available in (%s)" % (model, circuit.name))
-
- ##############################################
-
- @staticmethod
- def _sort_subcircuits(circuit, available_subcircuits=set()):
- p_available_subcircuits = available_subcircuits.copy()
- names = [subcircuit.name for subcircuit in circuit._subcircuits]
- p_available_subcircuits.update(names)
- dependencies = dict()
- for subcircuit in circuit._subcircuits:
- required = SpiceParser._sort_subcircuits(subcircuit, p_available_subcircuits)
- dependencies[subcircuit] = required
- for subcircuit in circuit._required_subcircuits:
- if subcircuit not in p_available_subcircuits:
- raise ValueError("subcircuit (%s) not available in (%s)" % (subcircuit, circuit.name))
- items = sorted(dependencies.items(), key=lambda item: len(item[1]))
- result = list()
- result_names = list()
- previous = len(items) + 1
- while 0 < len(items) < previous:
- previous = len(items)
- remove = list()
- for item in items:
- subckt, depends = item
- for name in depends:
- if name not in result_names:
- break
- else:
- result.append(subckt)
- result_names.append(subckt.name)
- remove.append(item)
- for item in remove:
- items.remove(item)
- if len(items) > 0:
- raise ValueError("Crossed dependencies (%s)" % [(key.name, value) for key, value in items])
- circuit._subcircuits = result
- return circuit._required_subcircuits - set(names)
-
- ##############################################
-
- def _parse(self, lines):
-
- """Parse the lines and return a list of statements."""
-
- # The first line in the input file must be the title, which is the only comment line that does
- # not need any special character in the first place.
- #
- # The last line must be .end
-
- if len(lines) <= 1:
- raise NameError('Netlist is empty')
- # if lines[-1] != '.end':
- # raise NameError('".end" is expected at the end of the netlist')
-
- circuit = CircuitStatement(lines[0])
- stack = []
- scope = circuit
- for line in lines[1:]:
- # print('>', repr(line))
- text = str(line)
- lower_case_text = text.lower() # !
- if line.is_comment:
- scope.append(Comment(line))
- elif lower_case_text.startswith('.'):
- lower_case_text = lower_case_text[1:]
- if lower_case_text.startswith('subckt'):
- stack.append(scope)
- scope = SubCircuitStatement(line)
- elif lower_case_text.startswith('ends'):
- parent = stack.pop()
- parent.append_subcircuit(scope)
- scope = parent
- elif lower_case_text.startswith('title'):
- # override fist line
- self._title = Title(line)
- scope.append(self._title)
- elif lower_case_text.startswith('end'):
- pass
- elif lower_case_text.startswith('model'):
- model = Model(line)
- scope.append_model(model)
- elif lower_case_text.startswith('include'):
- include = Include(line)
- scope.append(include)
- elif lower_case_text.startswith('param'):
- param = Parameter(line)
- scope.append_param(param)
- else:
- # options param ...
- # .global
- # .lib filename libname
- # .func .csparam .temp .if
- # { expr } are allowed in .model lines and in device lines.
- self._logger.warn('Parser ignored: {}'.format(line))
- else:
- try:
- element = Element(line)
- scope.append(element)
- if hasattr(element, '_prefix') and (element._prefix == "X"):
- name = element._parameters[0].lower()
- scope._required_subcircuits.add(name)
- elif hasattr(element, '_dict_parameters') and 'model' in element._dict_parameters:
- name = element._dict_parameters['model'].lower()
- scope._required_models.add(name)
- except ParseError:
- pass
- SpiceParser._check_models(circuit)
- SpiceParser._sort_subcircuits(circuit)
- return circuit
-
- ##############################################
-
- @property
- def circuit(self):
- """Circuit statements."""
- return self._statements
-
- @property
- def models(self):
- """Models of the sub-circuit."""
- return self._statements.models
-
- @property
- def subcircuits(self):
- """Subcircuits of the sub-circuit."""
- return self._statements.subcircuits
-
- ##############################################
-
- def is_only_subcircuit(self):
- return bool(not self.circuit and self.subcircuits)
-
- ##############################################
-
- def is_only_model(self):
- return bool(not self.circuit and not self.subcircuits and self.models)
-
- ##############################################
-
- @staticmethod
- def _build_circuit(circuit, statements, ground):
-
- for statement in statements:
- if isinstance(statement, Include):
- circuit.include(str(statement))
-
- for statement in statements:
- if isinstance(statement, Element):
- statement.build(circuit, ground)
- elif isinstance(statement, Model):
- statement.build(circuit)
- elif isinstance(statement, SubCircuit):
- subcircuit = statement.build(ground) # Fixme: ok ???
- circuit.subcircuit(subcircuit)
-
- ##############################################
-
- def build_circuit(self, ground=0):
- """Build a :class:`Circuit` instance.
-
- Use the *ground* parameter to specify the node which must be translated to 0 (SPICE ground node).
-
- """
- # circuit = Circuit(str(self._title))
- circuit = self.circuit.build(str(ground))
- return circuit
-
- ##############################################
-
- @staticmethod
- def netlist_to_python(netlist_name, statements, ground=0):
-
- source_code = ''
- for statement in statements:
- if isinstance(statement, Element):
- source_code += statement.to_python(netlist_name, ground)
- elif isinstance(statement, Include):
- pass
- elif isinstance(statement, Model):
- source_code += statement.to_python(netlist_name)
- elif isinstance(statement, SubCircuitStatement):
- source_code += statement.to_python(netlist_name)
- elif isinstance(statement, Include):
- source_code += statement.to_python(netlist_name)
- return source_code
-
- ##############################################
-
- def to_python_code(self, ground=0):
-
- ground = str(ground)
-
- source_code = ''
-
- if self.circuit:
- source_code += "circuit = Circuit('{}')".format(self._title) + os.linesep
- source_code += self.netlist_to_python('circuit', self._statements, ground)
-
- return source_code
diff --git a/PySpice/Spice/RawFile.py b/PySpice/Spice/RawFile.py
index eb7d04c6e..afcd7759f 100644
--- a/PySpice/Spice/RawFile.py
+++ b/PySpice/Spice/RawFile.py
@@ -29,7 +29,7 @@
####################################################################################################
-from PySpice.Unit import u_Degree, u_V, u_A, u_s, u_Hz
+from PySpice.Unit import u_c, u_v, u_a, u_s, u_hz
####################################################################################################
@@ -191,9 +191,9 @@ def circuit(self):
_name_to_unit = {
'time': u_s,
- 'voltage': u_V,
- 'current': u_A,
- 'frequency': u_Hz,
+ 'voltage': u_v,
+ 'current': u_a,
+ 'frequency': u_hz,
}
##############################################
@@ -232,6 +232,8 @@ def _read_header_field_line(self, header_line_iterator, expected_label, has_valu
"""
line = self._read_line(header_line_iterator)
+ while not line.startswith(expected_label):
+ line = self._read_line(header_line_iterator)
self._logger.debug(line)
if has_value:
# a title can have ': ' after 'title: '
@@ -258,8 +260,8 @@ def _read_temperature_line(self, header_line_iterator):
if pos1 != -1 and pos2 != -1:
part1 = line[pos1+len(pattern1):pos2]
part2 = line[pos2+len(pattern2):].strip()
- temperature = u_Degree(float(part1))
- nominal_temperature = u_Degree(float(part2))
+ temperature = u_c(float(part1))
+ nominal_temperature = u_c(float(part2))
else:
temperature = None
nominal_temperature = None
@@ -294,7 +296,10 @@ def _read_variable_data(self, raw_data):
else:
raise NotImplementedError
- input_data = np.fromstring(raw_data, count=number_of_columns*self.number_of_points, dtype='f8')
+ input_data = np.frombuffer(raw_data, count=number_of_columns*self.number_of_points, dtype='f8')
+ if len(input_data) != number_of_columns*self.number_of_points:
+ self.number_of_points = len(input_data)//number_of_columns
+ input_data = input_data[:number_of_columns*self.number_of_points]
input_data = input_data.reshape((self.number_of_points, number_of_columns))
input_data = input_data.transpose()
# np.savetxt('raw.txt', input_data)
diff --git a/PySpice/Spice/Simulation.py b/PySpice/Spice/Simulation.py
index 54488bc5e..97432f0e9 100644
--- a/PySpice/Spice/Simulation.py
+++ b/PySpice/Spice/Simulation.py
@@ -30,7 +30,7 @@
from ..Config import ConfigInstall
from ..Tools.StringTools import join_list, join_dict, str_spice
-from ..Unit import Unit, as_V, as_A, as_s, as_Hz, as_Degree, u_Degree
+from ..Unit import Unit, as_v, as_a, as_s, as_Hz, as_c, u_c
####################################################################################################
@@ -159,10 +159,16 @@ class DCAnalysisParameters(AnalysisParameters):
def __init__(self, **kwargs):
self._parameters = []
- for variable, value_slice in kwargs.items():
+ for variable, value in kwargs.items():
variable_lower = variable.lower()
+
if variable_lower[0] in ('v', 'i', 'r') or variable_lower == 'temp':
- self._parameters += [variable, value_slice.start, value_slice.stop, value_slice.step]
+ try:
+ iter(value)
+ self._parameters += [variable, 'LIST'] + [val for val in value]
+ except TypeError:
+ self._parameters += [variable, value.start, value.stop, value.step]
+
else:
raise NameError('Sweep variable must be a voltage/current source, '
'a resistor or the circuit temperature')
@@ -238,13 +244,19 @@ class TransientAnalysisParameters(AnalysisParameters):
##############################################
- def __init__(self, step_time, end_time, start_time=0, max_time=None, use_initial_condition=False):
+ def __init__(self, step_time, end_time, start_time=0, max_time=None,
+ use_initial_condition=False):
+
+ if use_initial_condition:
+ uic = 'uic'
+ else:
+ uic = None
self._step_time = as_s(step_time)
self._end_time = as_s(end_time)
self._start_time = as_s(start_time)
self._max_time = as_s(max_time, none=True)
- self._use_initial_condition = use_initial_condition
+ self._use_initial_condition = uic
##############################################
@@ -276,7 +288,7 @@ def to_list(self):
self._end_time,
self._start_time,
self._max_time,
- 'uic' if self._use_initial_condition else None,
+ self._use_initial_condition,
)
####################################################################################################
@@ -536,8 +548,8 @@ def __init__(self, circuit, **kwargs):
self._saved_nodes = set()
self._analyses = {}
- self.temperature = kwargs.get('temperature', u_Degree(27))
- self.nominal_temperature = kwargs.get('nominal_temperature', u_Degree(27))
+ self.temperature = kwargs.get('temperature', u_c(27))
+ self.nominal_temperature = kwargs.get('nominal_temperature', u_c(27))
##############################################
@@ -561,7 +573,7 @@ def temperature(self):
@temperature.setter
def temperature(self, value):
- self._options['TEMP'] = as_Degree(value)
+ self._options['TEMP'] = as_c(value)
##############################################
@@ -571,7 +583,7 @@ def nominal_temperature(self):
@nominal_temperature.setter
def nominal_temperature(self, value):
- self._options['TNOM'] = as_Degree(value)
+ self._options['TNOM'] = as_c(value)
##############################################
@@ -664,7 +676,7 @@ def save(self, *args):
"""
- self._saved_nodes |= set(*args)
+ self._saved_nodes.update(args)
##############################################
@@ -1085,24 +1097,35 @@ def str_options(self, unit=True):
##############################################
+ def save_str(self):
+ result = ""
+ if self._saved_nodes:
+ # Place 'all' first
+ saved_nodes = set(self._saved_nodes)
+ if 'all' in saved_nodes:
+ all_str = ' all'
+ saved_nodes.remove('all')
+ else:
+ all_str = ''
+ result += '.save' + all_str
+ if saved_nodes:
+ result += ' ' + join_list(saved_nodes)
+ result += os.linesep
+ return result
+
+ ##############################################
+
def __str__(self):
- netlist = self._circuit.str(simulator=self.SIMULATOR)
+ netlist = self._circuit.str(spice_sim=self.SIMULATOR)
netlist += self.str_options()
if self._initial_condition:
netlist += '.ic ' + join_dict(self._initial_condition) + os.linesep
if self._node_set:
netlist += '.nodeset ' + join_dict(self._node_set) + os.linesep
- if self._saved_nodes:
- # Place 'all' first
- saved_nodes = self._saved_nodes
- if 'all' in saved_nodes:
- all_str = 'all '
- saved_nodes.remove('all')
- else:
- all_str = ''
- netlist += '.save ' + all_str + join_list(saved_nodes) + os.linesep
+ netlist += self.save_str()
+
for measure_parameters in self._measures:
netlist += str(measure_parameters) + os.linesep
for analysis_parameters in self._analyses.values():
diff --git a/PySpice/Spice/SpiceGrammar.py b/PySpice/Spice/SpiceGrammar.py
new file mode 100644
index 000000000..da9f82bee
--- /dev/null
+++ b/PySpice/Spice/SpiceGrammar.py
@@ -0,0 +1,5343 @@
+#!/usr/bin/env python
+
+# CAVEAT UTILITOR
+#
+# This file was automatically generated by TatSu.
+#
+# https://pypi.python.org/pypi/tatsu/
+#
+# Any changes you make to it will be overwritten the next time
+# the file is generated.
+
+from __future__ import annotations
+
+import sys
+
+from tatsu.buffering import Buffer
+from tatsu.parsing import Parser
+from tatsu.parsing import tatsumasu
+from tatsu.parsing import leftrec, nomemo, isname # noqa
+from tatsu.infos import ParserConfig
+from tatsu.util import re, generic_main # noqa
+
+
+KEYWORDS = {} # type: ignore
+
+
+class SpiceBuffer(Buffer):
+ def __init__(self, text, /, config: ParserConfig = None, **settings):
+ config = ParserConfig.new(
+ config,
+ owner=self,
+ whitespace=None,
+ nameguard=None,
+ comments_re=None,
+ eol_comments_re=None,
+ ignorecase=True,
+ namechars='',
+ parseinfo=True,
+ )
+ config = config.replace(**settings)
+ super().__init__(text, config=config)
+
+
+class SpiceParser(Parser):
+ def __init__(self, /, config: ParserConfig = None, **settings):
+ config = ParserConfig.new(
+ config,
+ owner=self,
+ whitespace=None,
+ nameguard=None,
+ comments_re=None,
+ eol_comments_re=None,
+ ignorecase=True,
+ namechars='',
+ parseinfo=True,
+ keywords=KEYWORDS,
+ start='start',
+ )
+ config = config.replace(**settings)
+ super().__init__(config=config)
+
+ @tatsumasu('Circuit')
+ def _start_(self): # noqa
+ with self._optional():
+ self._token('.TITLE')
+
+ def block0():
+ self._st_()
+ self._closure(block0)
+ with self._optional():
+ self._asterisk_()
+
+ def block1():
+ self._st_()
+ self._closure(block1)
+ self._text_()
+ self.add_last_node_to_name('title')
+
+ def block3():
+ self._newline_()
+ self._asterisk_()
+
+ def block4():
+ self._st_()
+ self._closure(block4)
+ self._text_()
+ self.add_last_node_to_name('title')
+
+ self._define(
+ [],
+ ['title']
+ )
+ self._closure(block3)
+
+ def block6():
+
+ def block7():
+ self._st_()
+ self._closure(block7)
+ with self._optional():
+ with self._choice():
+ with self._option():
+ self._line_comment_()
+ with self._option():
+ self._inline_comment_()
+ self._error(
+ 'expecting one of: '
+ ' '
+ )
+ self._newline_()
+ self._closure(block6)
+
+ def block9():
+ self._st_()
+ self._closure(block9)
+ with self._optional():
+ self._lines_()
+ self.name_last_node('lines')
+ with self._optional():
+ self._token('.END')
+ self._end_sep_()
+ self._check_eof()
+
+ self._define(
+ ['lines'],
+ ['title']
+ )
+
+ @tatsumasu('Lines')
+ def _lines_(self): # noqa
+
+ def block0():
+ self._circuit_line_()
+ self.name_last_node('@')
+ self._closure(block0)
+
+ @tatsumasu('CircuitLine')
+ def _circuit_line_(self): # noqa
+ with self._group():
+ with self._choice():
+ with self._option():
+ self._device_()
+ self.name_last_node('@')
+ with self._option():
+ self._command_()
+ self.name_last_node('@')
+ with self._option():
+ self._encrypted_()
+ self._error(
+ 'expecting one of: '
+ ' '
+ )
+ self._end_sep_()
+ self.name_last_node('@')
+
+ @tatsumasu('NetlistLines')
+ def _netlist_lines_(self): # noqa
+
+ def block0():
+ self._netlist_line_()
+ self.name_last_node('@')
+ self._closure(block0)
+
+ @tatsumasu('NetlistLine')
+ def _netlist_line_(self): # noqa
+ with self._group():
+ with self._choice():
+ with self._option():
+ self._device_()
+ self.name_last_node('@')
+ with self._option():
+ self._netlist_cmds_()
+ self.name_last_node('@')
+ with self._option():
+ self._encrypted_()
+ self._error(
+ 'expecting one of: '
+ ' '
+ )
+ self._end_sep_()
+ self.name_last_node('@')
+
+ @tatsumasu()
+ def _encrypted_(self): # noqa
+ self._token('$CDNENCSTART')
+ self._cut()
+ with self._optional():
+ self._id_()
+
+ def block0():
+ self._newline_()
+ self._positive_closure(block0)
+
+ def block1():
+
+ def block2():
+ self._pattern('[0-9a-f]')
+ self._closure(block2)
+
+ def block3():
+ self._newline_()
+ self._positive_closure(block3)
+ self._closure(block1)
+ self._token('$CDNENCFINISH')
+ self._cut()
+ with self._optional():
+ self._id_()
+
+ @tatsumasu()
+ def _device_(self): # noqa
+ with self._choice():
+ with self._option():
+ self._nonlinear_dependent_source_()
+ self.name_last_node('@')
+ with self._option():
+ self._capacitor_()
+ self.name_last_node('@')
+ with self._option():
+ self._diode_()
+ self.name_last_node('@')
+ with self._option():
+ self._voltage_controlled_voltage_source_()
+ self.name_last_node('@')
+ with self._option():
+ self._current_controlled_current_source_()
+ self.name_last_node('@')
+ with self._option():
+ self._voltage_controlled_current_source_()
+ self.name_last_node('@')
+ with self._option():
+ self._current_controlled_voltage_source_()
+ self.name_last_node('@')
+ with self._option():
+ self._current_source_()
+ self.name_last_node('@')
+ with self._option():
+ self._jfet_()
+ self.name_last_node('@')
+ with self._option():
+ self._mutual_inductor_()
+ self.name_last_node('@')
+ with self._option():
+ self._inductor_()
+ self.name_last_node('@')
+ with self._option():
+ self._mosfet_()
+ self.name_last_node('@')
+ with self._option():
+ self._bjt_()
+ self.name_last_node('@')
+ with self._option():
+ self._resistor_()
+ self.name_last_node('@')
+ with self._option():
+ self._subcircuit_()
+ self.name_last_node('@')
+ with self._option():
+ self._switch_()
+ self.name_last_node('@')
+ with self._option():
+ self._voltage_source_()
+ self.name_last_node('@')
+ self._error(
+ 'expecting one of: '
+ "'B' 'C' 'D' 'E' 'F' 'G' 'H' 'I' 'J' 'K'"
+ "'L' 'M' 'Q' 'R' 'S' 'V' 'X' "
+ ''
+ ''
+ ''
+ ' '
+ ' '
+ ' '
+ ' '
+ ''
+ ''
+ ''
+ )
+
+ @tatsumasu('NonLinearDependentSource')
+ def _nonlinear_dependent_source_(self): # noqa
+ with self._if():
+ self._token('B')
+ self._cut()
+ self._dev_()
+ self.name_last_node('dev')
+ self._sep_()
+ self.name_last_node('sep')
+ self._node_()
+ self.name_last_node('positive')
+ self._sep_()
+ self.name_last_node('sep')
+ self._node_()
+ self.name_last_node('negative')
+ self._cut()
+ self._sep_()
+ self.name_last_node('sep')
+ with self._group():
+ with self._choice():
+ with self._option():
+ self._token('V')
+ with self._option():
+ self._token('I')
+ self._error(
+ 'expecting one of: '
+ "'I' 'V'"
+ )
+ self.name_last_node('magnitude')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._token('=')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._abm_expression_()
+ self.name_last_node('expr')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._parameters_()
+ self.name_last_node('parameters')
+
+ self._define(
+ ['parameters', 'sep'],
+ []
+ )
+
+ self._define(
+ ['dev', 'expr', 'magnitude', 'negative', 'parameters', 'positive', 'sep'],
+ []
+ )
+
+ @tatsumasu()
+ def _abm_expression_(self): # noqa
+ with self._choice():
+ with self._option():
+ self._lc_()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._control_table_()
+ self.name_last_node('@')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._rc_()
+
+ self._define(
+ ['sep'],
+ []
+ )
+ with self._option():
+ self._braced_expression_()
+ self.name_last_node('@')
+ with self._option():
+ self._tablefile_()
+ self.name_last_node('@')
+ self._error(
+ 'expecting one of: '
+ "'tablefile' '{' "
+ )
+
+ @tatsumasu('Capacitor')
+ def _capacitor_(self): # noqa
+ with self._if():
+ self._token('C')
+ self._cut()
+ self._dev_()
+ self.name_last_node('dev')
+ self._sep_()
+ self.name_last_node('sep')
+ self._node_()
+ self.name_last_node('positive')
+ self._sep_()
+ self.name_last_node('sep')
+ self._node_()
+ self.name_last_node('negative')
+ self._cut()
+ with self._optional():
+ with self._choice():
+ with self._option():
+ self._sep_()
+ self.name_last_node('sep')
+ self._gen_expr_()
+ self.name_last_node('value')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._model_name_()
+ self.name_last_node('model')
+
+ self._define(
+ ['model', 'sep'],
+ []
+ )
+
+ self._define(
+ ['model', 'sep', 'value'],
+ []
+ )
+ with self._option():
+ self._sep_()
+ self.name_last_node('sep')
+ self._model_name_()
+ self.name_last_node('model')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._gen_expr_()
+ self.name_last_node('value')
+
+ self._define(
+ ['sep', 'value'],
+ []
+ )
+
+ self._define(
+ ['model', 'sep', 'value'],
+ []
+ )
+ self._error(
+ 'expecting one of: '
+ ''
+ )
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._parameters_()
+ self.name_last_node('parameters')
+
+ self._define(
+ ['parameters', 'sep'],
+ []
+ )
+
+ self._define(
+ ['dev', 'model', 'negative', 'parameters', 'positive', 'sep', 'value'],
+ []
+ )
+
+ @tatsumasu('Diode')
+ def _diode_(self): # noqa
+ with self._if():
+ self._token('D')
+ self._cut()
+ self._dev_()
+ self.name_last_node('dev')
+ self._sep_()
+ self.name_last_node('sep')
+ self._node_()
+ self.name_last_node('positive')
+ self._sep_()
+ self.name_last_node('sep')
+ self._node_()
+ self.name_last_node('negative')
+ self._cut()
+ self._sep_()
+ self.name_last_node('sep')
+ self._model_name_()
+ self.name_last_node('model')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._gen_expr_()
+ self.name_last_node('area')
+
+ self._define(
+ ['area', 'sep'],
+ []
+ )
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._parameters_()
+ self.name_last_node('parameters')
+
+ self._define(
+ ['parameters', 'sep'],
+ []
+ )
+
+ self._define(
+ ['area', 'dev', 'model', 'negative', 'parameters', 'positive', 'sep'],
+ []
+ )
+
+ @tatsumasu('VoltageControlledVoltageSource')
+ def _voltage_controlled_voltage_source_(self): # noqa
+ with self._if():
+ self._token('E')
+ self._cut()
+ self._dev_()
+ self.name_last_node('dev')
+ self._sep_()
+ self.name_last_node('sep')
+ self._node_()
+ self.name_last_node('positive')
+ self._sep_()
+ self.name_last_node('sep')
+ self._node_()
+ self.name_last_node('negative')
+ self._cut()
+ self._sep_()
+ self.name_last_node('sep')
+ with self._group():
+ with self._choice():
+ with self._option():
+ with self._group():
+ with self._group():
+ with self._choice():
+ with self._option():
+ self._parenthesis_nodes_()
+ with self._option():
+ self._circuit_nodes_()
+ self._error(
+ 'expecting one of: '
+ ' '
+ )
+ self.name_last_node('nodes')
+ self._sep_()
+ self.name_last_node('sep')
+ self._gen_expr_()
+ self.name_last_node('transconductance')
+
+ self._define(
+ ['nodes', 'sep', 'transconductance'],
+ []
+ )
+ with self._option():
+ with self._group():
+ with self._group():
+ with self._choice():
+ with self._option():
+ self._control_value_()
+ with self._option():
+ self._control_table_()
+ with self._option():
+ self._control_voltage_poly_()
+ self._error(
+ 'expecting one of: '
+ ' '
+ ''
+ )
+ self.name_last_node('controller')
+ self._error(
+ 'expecting one of: '
+ ' '
+ ' '
+ ''
+ )
+
+ self._define(
+ ['controller', 'dev', 'negative', 'nodes', 'positive', 'sep', 'transconductance'],
+ []
+ )
+
+ @tatsumasu('CurrentControlledCurrentSource')
+ def _current_controlled_current_source_(self): # noqa
+ with self._if():
+ self._token('F')
+ self._cut()
+ self._dev_()
+ self.name_last_node('dev')
+ self._sep_()
+ self.name_last_node('sep')
+ self._node_()
+ self.name_last_node('positive')
+ self._sep_()
+ self.name_last_node('sep')
+ self._node_()
+ self.name_last_node('negative')
+ self._cut()
+ self._sep_()
+ self.name_last_node('sep')
+ with self._group():
+ with self._choice():
+ with self._option():
+ with self._group():
+ self._control_current_poly_()
+ self.name_last_node('controller')
+ with self._option():
+ with self._group():
+ self._dev_()
+ self.name_last_node('device')
+ self._sep_()
+ self.name_last_node('sep')
+ self._gen_expr_()
+ self.name_last_node('gain')
+
+ self._define(
+ ['device', 'gain', 'sep'],
+ []
+ )
+ self._error(
+ 'expecting one of: '
+ ' '
+ )
+
+ self._define(
+ ['controller', 'dev', 'device', 'gain', 'negative', 'positive', 'sep'],
+ []
+ )
+
+ @tatsumasu('VoltageControlledCurrentSource')
+ def _voltage_controlled_current_source_(self): # noqa
+ with self._if():
+ self._token('G')
+ self._cut()
+ self._dev_()
+ self.name_last_node('dev')
+ self._sep_()
+ self.name_last_node('sep')
+ self._node_()
+ self.name_last_node('positive')
+ self._sep_()
+ self.name_last_node('sep')
+ self._node_()
+ self.name_last_node('negative')
+ self._cut()
+ self._sep_()
+ self.name_last_node('sep')
+ with self._group():
+ with self._choice():
+ with self._option():
+ with self._group():
+ with self._group():
+ with self._choice():
+ with self._option():
+ self._parenthesis_nodes_()
+ with self._option():
+ self._circuit_nodes_()
+ self._error(
+ 'expecting one of: '
+ ' '
+ )
+ self.name_last_node('nodes')
+ self._sep_()
+ self.name_last_node('sep')
+ self._gen_expr_()
+ self.name_last_node('transconductance')
+
+ self._define(
+ ['nodes', 'sep', 'transconductance'],
+ []
+ )
+ with self._option():
+ with self._group():
+ with self._group():
+ with self._choice():
+ with self._option():
+ self._control_value_()
+ with self._option():
+ self._control_table_()
+ with self._option():
+ self._control_voltage_poly_()
+ self._error(
+ 'expecting one of: '
+ ' '
+ ''
+ )
+ self.name_last_node('controller')
+ self._error(
+ 'expecting one of: '
+ ' '
+ ' '
+ ''
+ )
+
+ self._define(
+ ['controller', 'dev', 'negative', 'nodes', 'positive', 'sep', 'transconductance'],
+ []
+ )
+
+ @tatsumasu('CurrentControlledVoltageSource')
+ def _current_controlled_voltage_source_(self): # noqa
+ with self._if():
+ self._token('H')
+ self._cut()
+ self._dev_()
+ self.name_last_node('dev')
+ self._sep_()
+ self.name_last_node('sep')
+ self._node_()
+ self.name_last_node('positive')
+ self._sep_()
+ self.name_last_node('sep')
+ self._node_()
+ self.name_last_node('negative')
+ self._cut()
+ self._sep_()
+ self.name_last_node('sep')
+ with self._group():
+ with self._choice():
+ with self._option():
+ with self._group():
+ self._control_current_poly_()
+ self.name_last_node('controller')
+ with self._option():
+ with self._group():
+ self._dev_()
+ self.name_last_node('device')
+ self._sep_()
+ self.name_last_node('sep')
+ self._gen_expr_()
+ self.name_last_node('transresistance')
+
+ self._define(
+ ['device', 'sep', 'transresistance'],
+ []
+ )
+ self._error(
+ 'expecting one of: '
+ ' '
+ )
+
+ self._define(
+ ['controller', 'dev', 'device', 'negative', 'positive', 'sep', 'transresistance'],
+ []
+ )
+
+ @tatsumasu('ControlValue')
+ def _control_value_(self): # noqa
+ self._token('VALUE')
+ self.name_last_node('type')
+ self._cut()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._token('=')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._braced_expression_()
+ self.name_last_node('expression')
+
+ self._define(
+ ['expression', 'sep', 'type'],
+ []
+ )
+
+ @tatsumasu('ControlTable')
+ def _control_table_(self): # noqa
+ self._token('TABLE')
+ self.name_last_node('type')
+ self._cut()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._braced_expression_()
+ self.name_last_node('expr')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._token('=')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ with self._group():
+ with self._choice():
+ with self._option():
+ with self._group():
+ self._lp_()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._value_()
+ self.add_last_node_to_name('input')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._comma_()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._value_()
+ self.add_last_node_to_name('output')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._rp_()
+
+ def block12():
+
+ def block13():
+ self._sep_()
+ self.name_last_node('sep')
+ self._positive_closure(block13)
+ self._lp_()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._value_()
+ self.add_last_node_to_name('input')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._comma_()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._value_()
+ self.add_last_node_to_name('output')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._rp_()
+
+ self._define(
+ ['sep'],
+ ['input', 'output']
+ )
+ self._closure(block12)
+
+ self._define(
+ ['sep'],
+ ['input', 'output']
+ )
+ with self._option():
+ with self._group():
+ self._value_()
+ self.add_last_node_to_name('input')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._comma_()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._value_()
+ self.add_last_node_to_name('output')
+
+ def block25():
+
+ def block26():
+ self._sep_()
+ self.name_last_node('sep')
+ self._positive_closure(block26)
+ self._value_()
+ self.add_last_node_to_name('input')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._comma_()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._value_()
+ self.add_last_node_to_name('output')
+
+ self._define(
+ ['sep'],
+ ['input', 'output']
+ )
+ self._closure(block25)
+
+ self._define(
+ ['sep'],
+ ['input', 'output']
+ )
+ self._error(
+ 'expecting one of: '
+ ' '
+ )
+
+ self._define(
+ ['expr', 'sep', 'type'],
+ ['input', 'output']
+ )
+
+ @tatsumasu('ControlVoltagePoly')
+ def _control_voltage_poly_(self): # noqa
+ self._token('POLY')
+ self._cut()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._lp_()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._integer_()
+ self.name_last_node('value')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._rp_()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+
+ def sep5():
+ with self._group():
+ self._sep_()
+ self.name_last_node('sep')
+
+ def block5():
+ with self._choice():
+ with self._option():
+ self._lp_()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._node_()
+ self.add_last_node_to_name('positive')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._comma_()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._node_()
+ self.add_last_node_to_name('negative')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._rp_()
+
+ self._define(
+ ['sep'],
+ ['negative', 'positive']
+ )
+ with self._option():
+ self._node_()
+ self.add_last_node_to_name('positive')
+ self._sep_()
+ self.name_last_node('sep')
+ self._node_()
+ self.add_last_node_to_name('negative')
+
+ self._define(
+ ['sep'],
+ ['negative', 'positive']
+ )
+ with self._option():
+ self._value_()
+ self.name_last_node('coefficient')
+ self._error(
+ 'expecting one of: '
+ ' '
+ )
+ self._join(block5, sep5)
+
+ self._define(
+ ['coefficient', 'sep', 'value'],
+ ['negative', 'positive']
+ )
+
+ @tatsumasu('ControlCurrentPoly')
+ def _control_current_poly_(self): # noqa
+ self._token('POLY')
+ self._cut()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._lp_()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._integer_()
+ self.name_last_node('value')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._rp_()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+
+ def sep5():
+ with self._group():
+ self._sep_()
+ self.name_last_node('sep')
+
+ def block5():
+ with self._choice():
+ with self._option():
+ self._dev_()
+ self.add_last_node_to_name('device')
+ with self._option():
+ self._value_()
+ self.add_last_node_to_name('coefficient')
+ self._error(
+ 'expecting one of: '
+ ' '
+ )
+ self._join(block5, sep5)
+
+ self._define(
+ ['sep', 'value'],
+ ['coefficient', 'device']
+ )
+
+ @tatsumasu('CurrentSource')
+ def _current_source_(self): # noqa
+ with self._if():
+ self._token('I')
+ self._cut()
+ self._dev_()
+ self.name_last_node('dev')
+ self._sep_()
+ self.name_last_node('sep')
+ self._node_()
+ self.name_last_node('positive')
+ self._sep_()
+ self.name_last_node('sep')
+ self._node_()
+ self.name_last_node('negative')
+ self._cut()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ with self._optional():
+ self._dc_()
+ self._cut()
+ self._sep_()
+ self.name_last_node('sep')
+
+ self._define(
+ ['sep'],
+ []
+ )
+ self._gen_expr_()
+ self.name_last_node('dc_value')
+
+ self._define(
+ ['dc_value', 'sep'],
+ []
+ )
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._ac_()
+ self._cut()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._gen_expr_()
+ self.name_last_node('ac_magnitude')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._gen_expr_()
+ self.name_last_node('ac_phase')
+
+ self._define(
+ ['ac_phase', 'sep'],
+ []
+ )
+
+ self._define(
+ ['ac_magnitude', 'ac_phase', 'sep'],
+ []
+ )
+
+ self._define(
+ ['ac_magnitude', 'ac_phase', 'sep'],
+ []
+ )
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._transient_specification_()
+ self.name_last_node('transient')
+
+ self._define(
+ ['sep', 'transient'],
+ []
+ )
+
+ self._define(
+ ['ac_magnitude', 'ac_phase', 'dc_value', 'dev', 'negative', 'positive', 'sep', 'transient'],
+ []
+ )
+
+ @tatsumasu('JFET')
+ def _jfet_(self): # noqa
+ with self._if():
+ self._token('J')
+ self._cut()
+ self._dev_()
+ self.name_last_node('dev')
+ self._sep_()
+ self.name_last_node('sep')
+ self._node_()
+ self.name_last_node('drain')
+ self._sep_()
+ self.name_last_node('sep')
+ self._node_()
+ self.name_last_node('gate')
+ self._sep_()
+ self.name_last_node('sep')
+ self._node_()
+ self.name_last_node('source')
+ self._cut()
+ self._sep_()
+ self.name_last_node('sep')
+ self._model_name_()
+ self.name_last_node('model')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._gen_expr_()
+ self.name_last_node('area')
+
+ self._define(
+ ['area', 'sep'],
+ []
+ )
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._parameters_()
+ self.name_last_node('parameters')
+
+ self._define(
+ ['parameters', 'sep'],
+ []
+ )
+
+ self._define(
+ ['area', 'dev', 'drain', 'gate', 'model', 'parameters', 'sep', 'source'],
+ []
+ )
+
+ @tatsumasu('MutualInductor')
+ def _mutual_inductor_(self): # noqa
+ with self._if():
+ self._token('K')
+ self._cut()
+ self._dev_()
+ self.name_last_node('dev')
+
+ def block1():
+ self._sep_()
+ self.name_last_node('sep')
+ with self._if():
+ self._token('L')
+ self._dev_()
+ self.add_last_node_to_name('inductor')
+
+ self._define(
+ ['sep'],
+ ['inductor']
+ )
+ self._positive_closure(block1)
+ self._sep_()
+ self.name_last_node('sep')
+ self._gen_expr_()
+ self.name_last_node('value')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._model_name_()
+ self.name_last_node('model')
+
+ self._define(
+ ['model', 'sep'],
+ []
+ )
+
+ self._define(
+ ['dev', 'model', 'sep', 'value'],
+ ['inductor']
+ )
+
+ @tatsumasu('Inductor')
+ def _inductor_(self): # noqa
+ with self._if():
+ self._token('L')
+ self._cut()
+ self._dev_()
+ self.name_last_node('dev')
+ self._sep_()
+ self.name_last_node('sep')
+ self._node_()
+ self.name_last_node('positive')
+ self._sep_()
+ self.name_last_node('sep')
+ self._node_()
+ self.name_last_node('negative')
+ self._cut()
+ with self._optional():
+ with self._choice():
+ with self._option():
+ self._sep_()
+ self.name_last_node('sep')
+ self._gen_expr_()
+ self.name_last_node('value')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._model_name_()
+ self.name_last_node('model')
+
+ self._define(
+ ['model', 'sep'],
+ []
+ )
+
+ self._define(
+ ['model', 'sep', 'value'],
+ []
+ )
+ with self._option():
+ self._sep_()
+ self.name_last_node('sep')
+ self._model_name_()
+ self.name_last_node('model')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._gen_expr_()
+ self.name_last_node('value')
+
+ self._define(
+ ['sep', 'value'],
+ []
+ )
+
+ self._define(
+ ['model', 'sep', 'value'],
+ []
+ )
+ self._error(
+ 'expecting one of: '
+ ''
+ )
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._parameters_()
+ self.name_last_node('parameters')
+
+ self._define(
+ ['parameters', 'sep'],
+ []
+ )
+
+ self._define(
+ ['dev', 'model', 'negative', 'parameters', 'positive', 'sep', 'value'],
+ []
+ )
+
+ @tatsumasu('MOSFET')
+ def _mosfet_(self): # noqa
+ with self._if():
+ self._token('M')
+ self._cut()
+ self._dev_()
+ self.name_last_node('dev')
+ self._sep_()
+ self.name_last_node('sep')
+ self._node_()
+ self.name_last_node('drain')
+ self._sep_()
+ self.name_last_node('sep')
+ self._node_()
+ self.name_last_node('gate')
+ self._sep_()
+ self.name_last_node('sep')
+ self._node_()
+ self.name_last_node('source')
+ self._cut()
+ self._sep_()
+ self.name_last_node('sep')
+ self._node_()
+ self.name_last_node('bulk')
+ self._sep_()
+ self.name_last_node('sep')
+ self._model_name_()
+ self.name_last_node('model')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+
+ def sep12():
+ with self._group():
+ self._sep_()
+ self.name_last_node('sep')
+
+ def block12():
+ with self._group():
+ with self._choice():
+ with self._option():
+ self._token('IC')
+ self.name_last_node('name')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._token('=')
+ self._cut()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+
+ def sep19():
+ with self._group():
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._comma_()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+
+ self._define(
+ ['sep'],
+ []
+ )
+
+ def block19():
+ self._value_()
+ self.name_last_node('value')
+ self._join(block19, sep19)
+
+ self._define(
+ ['name', 'sep', 'value'],
+ []
+ )
+ with self._option():
+ self._parameter_()
+ self.name_last_node('parameter')
+ self._error(
+ 'expecting one of: '
+ "'IC' "
+ )
+ self.name_last_node('param')
+ self._join(block12, sep12)
+
+ self._define(
+ ['name', 'param', 'parameter', 'sep', 'value'],
+ []
+ )
+
+ self._define(
+ ['bulk', 'dev', 'drain', 'gate', 'model', 'name', 'param', 'parameter', 'sep', 'source', 'value'],
+ []
+ )
+
+ @tatsumasu('BJT')
+ def _bjt_(self): # noqa
+ with self._if():
+ self._token('Q')
+ self._cut()
+ self._dev_()
+ self.name_last_node('dev')
+ self._sep_()
+ self.name_last_node('sep')
+ self._node_()
+ self.name_last_node('collector')
+ self._sep_()
+ self.name_last_node('sep')
+ self._node_()
+ self.name_last_node('base')
+ self._sep_()
+ self.name_last_node('sep')
+ self._node_()
+ self.name_last_node('emitter')
+ self._cut()
+ self._sep_()
+ self.name_last_node('sep')
+ with self._optional():
+ self._substrate_node_()
+ self.name_last_node('substrate')
+ self._sep_()
+ self.name_last_node('sep')
+
+ self._define(
+ ['sep', 'substrate'],
+ []
+ )
+ with self._optional():
+ self._token('DT')
+ self.name_last_node('thermal')
+ self._sep_()
+ self.name_last_node('sep')
+
+ self._define(
+ ['sep', 'thermal'],
+ []
+ )
+ self._model_name_()
+ self.name_last_node('model')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._gen_expr_()
+ self.name_last_node('area')
+
+ self._define(
+ ['area', 'sep'],
+ []
+ )
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._parameters_()
+ self.name_last_node('parameters')
+
+ self._define(
+ ['parameters', 'sep'],
+ []
+ )
+
+ self._define(
+ ['area', 'base', 'collector', 'dev', 'emitter', 'model', 'parameters', 'sep', 'substrate', 'thermal'],
+ []
+ )
+
+ @tatsumasu('SubstrateNode')
+ def _substrate_node_(self): # noqa
+ with self._group():
+ with self._choice():
+ with self._option():
+ self._pattern('[0-9]+')
+ with self._option():
+ with self._if():
+ self._token('[')
+ self._cut()
+ self._node_()
+ self._error(
+ 'expecting one of: '
+ "'[' [0-9]+"
+ )
+ self.name_last_node('substrate')
+
+ @tatsumasu('Resistor')
+ def _resistor_(self): # noqa
+ with self._if():
+ self._token('R')
+ self._cut()
+ self._dev_()
+ self.name_last_node('dev')
+ self._sep_()
+ self.name_last_node('sep')
+ self._node_()
+ self.name_last_node('positive')
+ self._sep_()
+ self.name_last_node('sep')
+ self._node_()
+ self.name_last_node('negative')
+ self._cut()
+ with self._optional():
+ with self._choice():
+ with self._option():
+ self._sep_()
+ self.name_last_node('sep')
+ self._gen_expr_()
+ self.name_last_node('value')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._model_name_()
+ self.name_last_node('model')
+
+ self._define(
+ ['model', 'sep'],
+ []
+ )
+
+ self._define(
+ ['model', 'sep', 'value'],
+ []
+ )
+ with self._option():
+ self._sep_()
+ self.name_last_node('sep')
+ self._model_name_()
+ self.name_last_node('model')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._gen_expr_()
+ self.name_last_node('value')
+
+ self._define(
+ ['sep', 'value'],
+ []
+ )
+
+ self._define(
+ ['model', 'sep', 'value'],
+ []
+ )
+ self._error(
+ 'expecting one of: '
+ ''
+ )
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._parameters_()
+ self.name_last_node('parameters')
+
+ self._define(
+ ['parameters', 'sep'],
+ []
+ )
+
+ self._define(
+ ['dev', 'model', 'negative', 'parameters', 'positive', 'sep', 'value'],
+ []
+ )
+
+ @tatsumasu('Switch')
+ def _switch_(self): # noqa
+ with self._if():
+ self._token('S')
+ self._cut()
+ self._dev_()
+ self.name_last_node('dev')
+ self._sep_()
+ self.name_last_node('sep')
+ self._node_()
+ self.name_last_node('positive')
+ self._sep_()
+ self.name_last_node('sep')
+ self._node_()
+ self.name_last_node('negative')
+ self._cut()
+ self._sep_()
+ self.name_last_node('sep')
+ with self._group():
+ with self._choice():
+ with self._option():
+ self._model_name_()
+ self.name_last_node('model')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ with self._group():
+ with self._choice():
+ with self._option():
+ self._token('ON')
+ with self._option():
+ self._token('OFF')
+ self._error(
+ 'expecting one of: '
+ "'OFF' 'ON'"
+ )
+ self.name_last_node('initial_state')
+
+ self._define(
+ ['initial_state', 'sep'],
+ []
+ )
+ self._sep_()
+ self.name_last_node('sep')
+ self._token('control')
+ self._cut()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._token('=')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._braced_expression_()
+
+ self._define(
+ ['initial_state', 'model', 'sep'],
+ []
+ )
+ with self._option():
+ self._node_()
+ self.name_last_node('control_p')
+ self._sep_()
+ self.name_last_node('sep')
+ self._node_()
+ self.name_last_node('control_n')
+ self._sep_()
+ self.name_last_node('sep')
+ self._model_name_()
+ self.name_last_node('model')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ with self._group():
+ with self._choice():
+ with self._option():
+ self._token('ON')
+ with self._option():
+ self._token('OFF')
+ self._error(
+ 'expecting one of: '
+ "'OFF' 'ON'"
+ )
+ self.name_last_node('initial_state')
+
+ self._define(
+ ['initial_state', 'sep'],
+ []
+ )
+
+ self._define(
+ ['control_n', 'control_p', 'initial_state', 'model', 'sep'],
+ []
+ )
+ self._error(
+ 'expecting one of: '
+ ' '
+ )
+
+ self._define(
+ ['control_n', 'control_p', 'dev', 'initial_state', 'model', 'negative', 'positive', 'sep'],
+ []
+ )
+
+ @tatsumasu('Subcircuit')
+ def _subcircuit_(self): # noqa
+ with self._if():
+ self._token('X')
+ self._cut()
+ self._dev_()
+ self.name_last_node('dev')
+
+ def block1():
+ self._sep_()
+ self.name_last_node('sep')
+ self._node_()
+ self.add_last_node_to_name('node')
+
+ self._define(
+ ['sep'],
+ ['node']
+ )
+ self._closure(block1)
+ with self._optional():
+ self._token(':')
+ self.name_last_node('params')
+ self._cut()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._parameters_()
+ self.name_last_node('parameters')
+
+ self._define(
+ ['parameters', 'params', 'sep'],
+ []
+ )
+
+ self._define(
+ ['dev', 'parameters', 'params', 'sep'],
+ ['node']
+ )
+
+ @tatsumasu('VoltageSource')
+ def _voltage_source_(self): # noqa
+ with self._if():
+ self._token('V')
+ self._cut()
+ self._dev_()
+ self.name_last_node('dev')
+ self._sep_()
+ self.name_last_node('sep')
+ self._node_()
+ self.name_last_node('positive')
+ self._sep_()
+ self.name_last_node('sep')
+ self._node_()
+ self.name_last_node('negative')
+ self._cut()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ with self._optional():
+ self._dc_()
+ self._cut()
+ self._sep_()
+ self.name_last_node('sep')
+
+ self._define(
+ ['sep'],
+ []
+ )
+ self._gen_expr_()
+ self.name_last_node('dc_value')
+
+ self._define(
+ ['dc_value', 'sep'],
+ []
+ )
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._ac_()
+ self._cut()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._gen_expr_()
+ self.name_last_node('ac_magnitude')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._gen_expr_()
+ self.name_last_node('ac_phase')
+
+ self._define(
+ ['ac_phase', 'sep'],
+ []
+ )
+
+ self._define(
+ ['ac_magnitude', 'ac_phase', 'sep'],
+ []
+ )
+
+ self._define(
+ ['ac_magnitude', 'ac_phase', 'sep'],
+ []
+ )
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._transient_specification_()
+ self.name_last_node('transient')
+
+ self._define(
+ ['sep', 'transient'],
+ []
+ )
+
+ self._define(
+ ['ac_magnitude', 'ac_phase', 'dc_value', 'dev', 'negative', 'positive', 'sep', 'transient'],
+ []
+ )
+
+ @tatsumasu()
+ def _dc_(self): # noqa
+ self._token('DC')
+
+ @tatsumasu()
+ def _ac_(self): # noqa
+ self._token('AC')
+
+ @tatsumasu('TransientSpecification')
+ def _transient_specification_(self): # noqa
+ with self._choice():
+ with self._option():
+ self._transient_pulse_()
+ self.name_last_node('@')
+ with self._option():
+ self._transient_sin_()
+ self.name_last_node('@')
+ with self._option():
+ self._transient_exp_()
+ self.name_last_node('@')
+ with self._option():
+ self._transient_pat_()
+ self.name_last_node('@')
+ with self._option():
+ self._transient_pwl_()
+ self.name_last_node('@')
+ with self._option():
+ self._transient_sffm_()
+ self.name_last_node('@')
+ self._error(
+ 'expecting one of: '
+ "'EXP' 'PAT' 'PULSE' 'PWL' 'SFFM' 'SIN'"
+ ' '
+ ' '
+ ' '
+ )
+
+ @tatsumasu('TransientPulse')
+ def _transient_pulse_(self): # noqa
+ self._token('PULSE')
+ self.name_last_node('type')
+ with self._group():
+ with self._choice():
+ with self._option():
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._lp_()
+ self._cut()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._pulse_arguments_()
+ self.name_last_node('@')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._rp_()
+
+ self._define(
+ ['sep'],
+ []
+ )
+ with self._option():
+ self._sep_()
+ self.name_last_node('sep')
+ self._pulse_arguments_()
+ self.name_last_node('@')
+
+ self._define(
+ ['sep'],
+ []
+ )
+ self._error(
+ 'expecting one of: '
+ ' '
+ )
+
+ self._define(
+ ['sep', 'type'],
+ []
+ )
+
+ @tatsumasu('PulseArguments')
+ def _pulse_arguments_(self): # noqa
+ self._gen_expr_()
+ self.name_last_node('v1')
+ self._sep_()
+ self.name_last_node('sep')
+
+ def sep2():
+ with self._group():
+ self._sep_()
+ self.name_last_node('sep')
+
+ def block2():
+ self._gen_expr_()
+ self.name_last_node('value')
+ self._join(block2, sep2)
+
+ self._define(
+ ['sep', 'v1', 'value'],
+ []
+ )
+
+ @tatsumasu('TransientSin')
+ def _transient_sin_(self): # noqa
+ self._token('SIN')
+ self.name_last_node('type')
+ with self._group():
+ with self._choice():
+ with self._option():
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._lp_()
+ self._cut()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._sin_arguments_()
+ self.name_last_node('@')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._rp_()
+
+ self._define(
+ ['sep'],
+ []
+ )
+ with self._option():
+ self._sep_()
+ self.name_last_node('sep')
+ self._sin_arguments_()
+ self.name_last_node('@')
+
+ self._define(
+ ['sep'],
+ []
+ )
+ self._error(
+ 'expecting one of: '
+ ' '
+ )
+
+ self._define(
+ ['sep', 'type'],
+ []
+ )
+
+ @tatsumasu('SinArguments')
+ def _sin_arguments_(self): # noqa
+ self._gen_expr_()
+ self.name_last_node('v0')
+ self._sep_()
+ self.name_last_node('sep')
+ self._gen_expr_()
+ self.name_last_node('va')
+ self._sep_()
+ self.name_last_node('sep')
+ self._gen_expr_()
+ self.name_last_node('freq')
+ self._sep_()
+ self.name_last_node('sep')
+
+ def sep6():
+ with self._group():
+ self._sep_()
+ self.name_last_node('sep')
+
+ def block6():
+ self._gen_expr_()
+ self.name_last_node('value')
+ self._join(block6, sep6)
+
+ self._define(
+ ['freq', 'sep', 'v0', 'va', 'value'],
+ []
+ )
+
+ @tatsumasu('TransientExp')
+ def _transient_exp_(self): # noqa
+ self._token('EXP')
+ self.name_last_node('type')
+ with self._group():
+ with self._choice():
+ with self._option():
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._lp_()
+ self._cut()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._exp_arguments_()
+ self.name_last_node('@')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._rp_()
+
+ self._define(
+ ['sep'],
+ []
+ )
+ with self._option():
+ self._sep_()
+ self.name_last_node('sep')
+ self._exp_arguments_()
+ self.name_last_node('@')
+
+ self._define(
+ ['sep'],
+ []
+ )
+ self._error(
+ 'expecting one of: '
+ ' '
+ )
+
+ self._define(
+ ['sep', 'type'],
+ []
+ )
+
+ @tatsumasu('ExpArguments')
+ def _exp_arguments_(self): # noqa
+ self._gen_expr_()
+ self.name_last_node('v1')
+ self._sep_()
+ self.name_last_node('sep')
+ self._gen_expr_()
+ self.name_last_node('v2')
+ self._sep_()
+ self.name_last_node('sep')
+
+ def sep4():
+ with self._group():
+ self._sep_()
+ self.name_last_node('sep')
+
+ def block4():
+ self._gen_expr_()
+ self.name_last_node('value')
+ self._join(block4, sep4)
+
+ self._define(
+ ['sep', 'v1', 'v2', 'value'],
+ []
+ )
+
+ @tatsumasu('TransientPat')
+ def _transient_pat_(self): # noqa
+ self._token('PAT')
+ self.name_last_node('type')
+ with self._group():
+ with self._choice():
+ with self._option():
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._lp_()
+ self._cut()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._pat_arguments_()
+ self.name_last_node('@')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._rp_()
+
+ self._define(
+ ['sep'],
+ []
+ )
+ with self._option():
+ self._sep_()
+ self.name_last_node('sep')
+ self._pat_arguments_()
+ self.name_last_node('@')
+
+ self._define(
+ ['sep'],
+ []
+ )
+ self._error(
+ 'expecting one of: '
+ ' '
+ )
+
+ self._define(
+ ['sep', 'type'],
+ []
+ )
+
+ @tatsumasu('PatArguments')
+ def _pat_arguments_(self): # noqa
+ self._gen_expr_()
+ self.name_last_node('vhi')
+ self._sep_()
+ self.name_last_node('sep')
+ self._gen_expr_()
+ self.name_last_node('vlo')
+ self._sep_()
+ self.name_last_node('sep')
+ self._gen_expr_()
+ self.name_last_node('td')
+ self._sep_()
+ self.name_last_node('sep')
+ self._gen_expr_()
+ self.name_last_node('tr')
+ self._sep_()
+ self.name_last_node('sep')
+ self._gen_expr_()
+ self.name_last_node('tf')
+ self._sep_()
+ self.name_last_node('sep')
+ self._gen_expr_()
+ self.name_last_node('tsample')
+ self._sep_()
+ self.name_last_node('sep')
+ self._binary_pattern_()
+ self.name_last_node('data')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._binary_()
+ self.name_last_node('repeat')
+
+ self._define(
+ ['repeat', 'sep'],
+ []
+ )
+
+ self._define(
+ ['data', 'repeat', 'sep', 'td', 'tf', 'tr', 'tsample', 'vhi', 'vlo'],
+ []
+ )
+
+ @tatsumasu('TransientPWL')
+ def _transient_pwl_(self): # noqa
+ self._token('PWL')
+ self.name_last_node('type')
+ self._cut()
+ with self._group():
+ with self._choice():
+ with self._option():
+ self._pwl_file_arguments_()
+ self.name_last_node('@')
+ with self._option():
+ self._pwl_arguments_()
+ self.name_last_node('@')
+ self._error(
+ 'expecting one of: '
+ ' '
+ )
+
+ self._define(
+ ['type'],
+ []
+ )
+
+ @tatsumasu('PWLFileArguments')
+ def _pwl_file_arguments_(self): # noqa
+ self._sep_()
+ self.name_last_node('sep')
+ self._token('FILE')
+ self._cut()
+ self._sep_()
+ self.name_last_node('sep')
+ with self._group():
+ with self._choice():
+ with self._option():
+ self._double_quote_()
+ self._filename_()
+ self.name_last_node('filename')
+ self._double_quote_()
+
+ self._define(
+ ['filename'],
+ []
+ )
+ with self._option():
+ self._filename_()
+ self.name_last_node('filename')
+ self._error(
+ 'expecting one of: '
+ ' '
+ )
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._parameters_()
+ self.name_last_node('parameters')
+
+ self._define(
+ ['parameters', 'sep'],
+ []
+ )
+
+ self._define(
+ ['filename', 'parameters', 'sep'],
+ []
+ )
+
+ @tatsumasu('PWLArguments')
+ def _pwl_arguments_(self): # noqa
+ with self._group():
+ with self._choice():
+ with self._option():
+
+ def block1():
+ self._sep_()
+ self.name_last_node('sep')
+ self._closure(block1)
+ self._lp_()
+ self._cut()
+
+ def block3():
+ self._sep_()
+ self.name_last_node('sep')
+ self._closure(block3)
+ self._value_()
+ self.name_last_node('t')
+ self._sep_()
+ self.name_last_node('sep')
+ self._value_()
+ self.name_last_node('value')
+
+ def block8():
+ self._sep_()
+ self.name_last_node('sep')
+ self._value_()
+ self.name_last_node('t')
+ self._sep_()
+ self.name_last_node('sep')
+ self._value_()
+ self.name_last_node('value')
+
+ self._define(
+ ['sep', 't', 'value'],
+ []
+ )
+ self._closure(block8)
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._parameters_()
+ self.name_last_node('parameters')
+
+ self._define(
+ ['parameters', 'sep'],
+ []
+ )
+
+ def block15():
+ self._sep_()
+ self.name_last_node('sep')
+ self._closure(block15)
+ self._rp_()
+
+ self._define(
+ ['parameters', 'sep', 't', 'value'],
+ []
+ )
+ with self._option():
+
+ def block17():
+ self._sep_()
+ self.name_last_node('sep')
+ self._value_()
+ self.name_last_node('t')
+ self._sep_()
+ self.name_last_node('sep')
+ self._value_()
+ self.name_last_node('value')
+
+ self._define(
+ ['sep', 't', 'value'],
+ []
+ )
+ self._positive_closure(block17)
+ self._error(
+ 'expecting one of: '
+ ' '
+ )
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._parameters_()
+ self.name_last_node('parameters')
+
+ self._define(
+ ['parameters', 'sep'],
+ []
+ )
+
+ self._define(
+ ['parameters', 'sep', 't', 'value'],
+ []
+ )
+
+ @tatsumasu('TransientSFFM')
+ def _transient_sffm_(self): # noqa
+ self._token('SFFM')
+ self.name_last_node('type')
+ with self._group():
+ with self._choice():
+ with self._option():
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._lp_()
+ self._cut()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._sffm_arguments_()
+ self.name_last_node('@')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._rp_()
+
+ self._define(
+ ['sep'],
+ []
+ )
+ with self._option():
+ self._sep_()
+ self.name_last_node('sep')
+ self._sffm_arguments_()
+ self.name_last_node('@')
+
+ self._define(
+ ['sep'],
+ []
+ )
+ self._error(
+ 'expecting one of: '
+ ' '
+ )
+
+ self._define(
+ ['sep', 'type'],
+ []
+ )
+
+ @tatsumasu('SFFMArguments')
+ def _sffm_arguments_(self): # noqa
+ self._gen_expr_()
+ self.name_last_node('v0')
+ self._sep_()
+ self.name_last_node('sep')
+ self._gen_expr_()
+ self.name_last_node('va')
+ self._sep_()
+ self.name_last_node('sep')
+
+ def sep4():
+ with self._group():
+ self._sep_()
+ self.name_last_node('sep')
+
+ def block4():
+ self._gen_expr_()
+ self.name_last_node('value')
+ self._join(block4, sep4)
+
+ self._define(
+ ['sep', 'v0', 'va', 'value'],
+ []
+ )
+
+ @tatsumasu('Command')
+ def _command_(self): # noqa
+ with self._choice():
+ with self._option():
+ self._embedded_sampling_cmd_()
+ self.name_last_node('@')
+ with self._option():
+ self._include_cmd_()
+ self.name_last_node('@')
+ with self._option():
+ self._lib_cmd_()
+ self.name_last_node('@')
+ with self._option():
+ self._netlist_cmds_()
+ self.name_last_node('@')
+ with self._option():
+ self._subckt_cmd_()
+ self.name_last_node('@')
+ with self._option():
+ self._simulator_cmd_()
+ self.name_last_node('@')
+ with self._option():
+ self._title_cmd_()
+ self.name_last_node('@')
+ with self._option():
+ self._ac_cmd_()
+ self.name_last_node('@')
+ with self._option():
+ self._dc_cmd_()
+ self.name_last_node('@')
+ self._error(
+ 'expecting one of: '
+ "'.AC' '.DC' '.EMBEDDEDSAMPLING' '.INC'"
+ "'.INCL' '.INCLUDE' '.LIB' '.SIMULATOR'"
+ "'.SUBCKT' '.TITLE' "
+ ' '
+ ' '
+ ' '
+ ' '
+ )
+
+ @tatsumasu('NetlistCmds')
+ def _netlist_cmds_(self): # noqa
+ with self._choice():
+ with self._option():
+ self._data_cmd_()
+ self.name_last_node('@')
+ with self._option():
+ self._ic_cmd_()
+ self.name_last_node('@')
+ with self._option():
+ self._model_cmd_()
+ self.name_last_node('@')
+ with self._option():
+ self._param_cmd_()
+ self.name_last_node('@')
+ with self._option():
+ self._subckt_cmd_()
+ self.name_last_node('@')
+ self._error(
+ 'expecting one of: '
+ "'.DATA' '.DCVOLT' '.IC' '.MODEL'"
+ "'.PARAM' '.SUBCKT' "
+ ' '
+ )
+
+ @tatsumasu('ACCmd')
+ def _ac_cmd_(self): # noqa
+ self._token('.AC')
+ self.name_last_node('cmd')
+ self._cut()
+ self._sep_()
+ self.name_last_node('sep')
+ with self._group():
+ with self._choice():
+ with self._option():
+ with self._group():
+ self._ac_sweep_type_()
+ self.name_last_node('sweep')
+ self._sep_()
+ self.name_last_node('sep')
+ self._integer_()
+ self.name_last_node('points')
+ self._sep_()
+ self.name_last_node('sep')
+ self._value_()
+ self.name_last_node('start')
+ self._sep_()
+ self.name_last_node('sep')
+ self._value_()
+ self.name_last_node('end')
+
+ self._define(
+ ['end', 'points', 'sep', 'start', 'sweep'],
+ []
+ )
+ with self._option():
+ with self._group():
+ self._token('DATA')
+ self.name_last_node('sweep')
+
+ def block11():
+ self._st_()
+ self._closure(block11)
+ self._token('=')
+
+ def block12():
+ self._st_()
+ self._closure(block12)
+ self._id_()
+ self.name_last_node('table')
+
+ self._define(
+ ['sweep', 'table'],
+ []
+ )
+ self._error(
+ 'expecting one of: '
+ "'DATA' "
+ )
+
+ self._define(
+ ['cmd', 'end', 'points', 'sep', 'start', 'sweep', 'table'],
+ []
+ )
+
+ @tatsumasu()
+ def _ac_sweep_type_(self): # noqa
+ with self._choice():
+ with self._option():
+ self._token('LIN')
+ with self._option():
+ self._token('OCT')
+ with self._option():
+ self._token('DEC')
+ self._error(
+ 'expecting one of: '
+ "'DEC' 'LIN' 'OCT'"
+ )
+
+ @tatsumasu('DataCmd')
+ def _data_cmd_(self): # noqa
+ self._token('.DATA')
+ self.name_last_node('cmd')
+ self._cut()
+ self._sep_()
+ self.name_last_node('sep')
+ self._id_()
+ self.name_last_node('table')
+
+ def block3():
+ self._sep_()
+ self.name_last_node('sep')
+ self._id_()
+ self.name_last_node('name')
+
+ self._define(
+ ['name', 'sep'],
+ []
+ )
+ self._positive_closure(block3)
+
+ def block6():
+ self._sep_()
+ self.name_last_node('sep')
+ self._value_()
+ self.name_last_node('value')
+
+ self._define(
+ ['sep', 'value'],
+ []
+ )
+ self._positive_closure(block6)
+ self._end_sep_()
+ self.name_last_node('sep')
+ self._token('.ENDDATA')
+
+ self._define(
+ ['cmd', 'name', 'sep', 'table', 'value'],
+ []
+ )
+
+ @tatsumasu('DCCmd')
+ def _dc_cmd_(self): # noqa
+ self._token('.DC')
+ self.name_last_node('cmd')
+ with self._group():
+ with self._choice():
+ with self._option():
+ with self._group():
+ self._sep_()
+ self.name_last_node('sep')
+ self._token('DATA')
+ self.name_last_node('sweep')
+
+ def block4():
+ self._st_()
+ self._closure(block4)
+ self._token('=')
+
+ def block5():
+ self._st_()
+ self._closure(block5)
+ self._id_()
+ self.name_last_node('table')
+
+ self._define(
+ ['sep', 'sweep', 'table'],
+ []
+ )
+ with self._option():
+ with self._group():
+
+ def block7():
+ with self._choice():
+ with self._option():
+ with self._group():
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._token('LIN')
+ self.name_last_node('sweep')
+
+ self._define(
+ ['sep', 'sweep'],
+ []
+ )
+ self._sep_()
+ self.name_last_node('sep')
+ self._id_()
+ self.name_last_node('name')
+ self._sep_()
+ self.name_last_node('sep')
+ self._value_()
+ self.name_last_node('start')
+ self._sep_()
+ self.name_last_node('sep')
+ self._value_()
+ self.name_last_node('stop')
+ self._sep_()
+ self.name_last_node('sep')
+ self._value_()
+ self.name_last_node('step')
+
+ self._define(
+ ['name', 'sep', 'start', 'step', 'stop', 'sweep'],
+ []
+ )
+ with self._option():
+ with self._group():
+ self._sep_()
+ self.name_last_node('sep')
+ with self._group():
+ with self._choice():
+ with self._option():
+ self._token('DEC')
+ with self._option():
+ self._token('OCT')
+ self._error(
+ 'expecting one of: '
+ "'DEC' 'OCT'"
+ )
+ self.name_last_node('sweep')
+ self._sep_()
+ self.name_last_node('sep')
+ self._id_()
+ self.name_last_node('name')
+ self._sep_()
+ self.name_last_node('sep')
+ self._value_()
+ self.name_last_node('start')
+ self._sep_()
+ self.name_last_node('sep')
+ self._value_()
+ self.name_last_node('stop')
+ self._sep_()
+ self.name_last_node('sep')
+ self._integer_()
+ self.name_last_node('points')
+
+ self._define(
+ ['name', 'points', 'sep', 'start', 'stop', 'sweep'],
+ []
+ )
+ with self._option():
+ with self._group():
+ self._sep_()
+ self.name_last_node('sep')
+ self._id_()
+ self.name_last_node('name')
+ self._sep_()
+ self.name_last_node('sep')
+ self._token('LIST')
+ self.name_last_node('sweep')
+ self._sep_()
+ self.name_last_node('sep')
+
+ def sep35():
+ with self._group():
+ self._sep_()
+ self.name_last_node('sep')
+
+ def block35():
+ self._value_()
+ self.name_last_node('point')
+ self._positive_join(block35, sep35)
+
+ self._define(
+ ['name', 'point', 'sep', 'sweep'],
+ []
+ )
+ self._error(
+ 'expecting one of: '
+ ''
+ )
+ self._positive_closure(block7)
+ self._error(
+ 'expecting one of: '
+ ''
+ )
+
+ self._define(
+ ['cmd', 'name', 'point', 'points', 'sep', 'start', 'step', 'stop', 'sweep', 'table'],
+ []
+ )
+
+ @tatsumasu('EmbeddedSamplingCmd')
+ def _embedded_sampling_cmd_(self): # noqa
+ self._token('.EMBEDDEDSAMPLING')
+ self.name_last_node('cmd')
+ self._cut()
+ with self._group():
+ with self._choice():
+ with self._option():
+ with self._group():
+ self._sep_()
+ self.name_last_node('sep')
+ self._token('param')
+ self.name_last_node('parameter')
+
+ def block4():
+ self._st_()
+ self._closure(block4)
+ self._token('=')
+
+ def block5():
+ self._st_()
+ self._closure(block5)
+
+ def sep6():
+ with self._group():
+ self._es_sep_()
+
+ def block6():
+ self._id_()
+ self.name_last_node('name')
+ self._positive_join(block6, sep6)
+ self._sep_()
+ self.name_last_node('sep')
+ self._token('type')
+ self.name_last_node('parameter')
+
+ def block10():
+ self._st_()
+ self._closure(block10)
+ self._token('=')
+
+ def block11():
+ self._st_()
+ self._closure(block11)
+
+ def sep12():
+ with self._group():
+ self._es_sep_()
+
+ def block12():
+ self._es_parameter_type_()
+ self.name_last_node('type')
+ self._positive_join(block12, sep12)
+
+ def block14():
+ self._sep_()
+ self.name_last_node('sep')
+ self._es_parameter_name_()
+ self.name_last_node('parameter')
+
+ def block17():
+ self._st_()
+ self._closure(block17)
+ self._token('=')
+
+ def block18():
+ self._st_()
+ self._closure(block18)
+
+ def sep19():
+ with self._group():
+ self._es_sep_()
+
+ def block19():
+ self._gen_expr_()
+ self.name_last_node('value')
+ self._positive_join(block19, sep19)
+
+ self._define(
+ ['parameter', 'sep', 'value'],
+ []
+ )
+ self._closure(block14)
+
+ self._define(
+ ['name', 'parameter', 'sep', 'type', 'value'],
+ []
+ )
+ with self._option():
+ with self._group():
+ self._sep_()
+ self.name_last_node('sep')
+ self._token('useExpr')
+ self.name_last_node('parameter')
+
+ def block23():
+ self._st_()
+ self._closure(block23)
+ self._token('=')
+ self._cut()
+
+ def block24():
+ self._st_()
+ self._closure(block24)
+ self._boolean_()
+ self.name_last_node('value')
+
+ self._define(
+ ['parameter', 'sep', 'value'],
+ []
+ )
+ self._error(
+ 'expecting one of: '
+ ''
+ )
+
+ self._define(
+ ['cmd', 'name', 'parameter', 'sep', 'type', 'value'],
+ []
+ )
+
+ @tatsumasu()
+ def _es_parameter_type_(self): # noqa
+ with self._choice():
+ with self._option():
+ self._token('UNIFORM')
+ with self._option():
+ self._token('NORMAL')
+ with self._option():
+ self._token('GAMMA')
+ self._error(
+ 'expecting one of: '
+ "'GAMMA' 'NORMAL' 'UNIFORM'"
+ )
+
+ @tatsumasu()
+ def _es_parameter_name_(self): # noqa
+ with self._choice():
+ with self._option():
+ self._token('alpha')
+ with self._option():
+ self._token('beta')
+ with self._option():
+ self._token('means')
+ with self._option():
+ self._token('std_deviations')
+ with self._option():
+ self._token('lower_bounds')
+ with self._option():
+ self._token('upper_bounds')
+ self._error(
+ 'expecting one of: '
+ "'alpha' 'beta' 'lower_bounds' 'means'"
+ "'std_deviations' 'upper_bounds'"
+ )
+
+ @tatsumasu()
+ def _es_sep_(self): # noqa
+ self._comma_()
+ self._cut()
+
+ def block0():
+ self._st_()
+ self._closure(block0)
+
+ @tatsumasu('ICCmd')
+ def _ic_cmd_(self): # noqa
+ with self._group():
+ with self._choice():
+ with self._option():
+ self._token('.IC')
+ with self._option():
+ self._token('.DCVOLT')
+ self._error(
+ 'expecting one of: '
+ "'.DCVOLT' '.IC'"
+ )
+ self.name_last_node('cmd')
+ self._cut()
+ with self._group():
+ with self._choice():
+ with self._option():
+
+ def block3():
+ self._sep_()
+ self.name_last_node('sep')
+ self._token('V')
+ self._lp_()
+ self._cut()
+ self._node_()
+ self.name_last_node('node')
+ self._rp_()
+
+ def block6():
+ self._st_()
+ self._closure(block6)
+ self._token('=')
+
+ def block7():
+ self._st_()
+ self._closure(block7)
+ self._gen_expr_()
+ self.name_last_node('value')
+
+ self._define(
+ ['node', 'sep', 'value'],
+ []
+ )
+ self._positive_closure(block3)
+ with self._option():
+
+ def block9():
+ self._sep_()
+ self.name_last_node('sep')
+ self._node_()
+ self.name_last_node('node')
+
+ def block12():
+ self._st_()
+ self._positive_closure(block12)
+ self._gen_expr_()
+ self.name_last_node('value')
+
+ self._define(
+ ['node', 'sep', 'value'],
+ []
+ )
+ self._positive_closure(block9)
+ self._error(
+ 'expecting one of: '
+ ''
+ )
+
+ self._define(
+ ['cmd', 'node', 'sep', 'value'],
+ []
+ )
+
+ @tatsumasu('IncludeCmd')
+ def _include_cmd_(self): # noqa
+ with self._group():
+ with self._choice():
+ with self._option():
+ self._token('.INCLUDE')
+ with self._option():
+ self._token('.INCL')
+ with self._option():
+ self._token('.INC')
+ self._error(
+ 'expecting one of: '
+ "'.INC' '.INCL' '.INCLUDE'"
+ )
+ self.name_last_node('cmd')
+ self._cut()
+ self._sep_()
+ self.name_last_node('sep')
+ with self._group():
+ with self._choice():
+ with self._option():
+ self._double_quote_()
+ self._cut()
+ self._filename_()
+ self.name_last_node('filename')
+ self._double_quote_()
+
+ self._define(
+ ['filename'],
+ []
+ )
+ with self._option():
+ self._single_quote_()
+ self._cut()
+ self._filename_()
+ self.name_last_node('filename')
+ self._single_quote_()
+
+ self._define(
+ ['filename'],
+ []
+ )
+ with self._option():
+ self._filename_()
+ self.name_last_node('filename')
+ self._error(
+ 'expecting one of: '
+ ' '
+ )
+
+ self._define(
+ ['cmd', 'filename', 'sep'],
+ []
+ )
+
+ @tatsumasu('LibCmd')
+ def _lib_cmd_(self): # noqa
+ self._token('.LIB')
+ self.name_last_node('cmd')
+ self._cut()
+ self._sep_()
+ self.name_last_node('sep')
+ with self._group():
+ with self._choice():
+ with self._option():
+ self._lib_call_()
+ self.name_last_node('call')
+ with self._option():
+ self._lib_block_()
+ self.name_last_node('block')
+ self._error(
+ 'expecting one of: '
+ ' '
+ )
+
+ self._define(
+ ['block', 'call', 'cmd', 'sep'],
+ []
+ )
+
+ @tatsumasu('LibCall')
+ def _lib_call_(self): # noqa
+ with self._group():
+ with self._choice():
+ with self._option():
+ self._double_quote_()
+ self._cut()
+ self._filename_()
+ self.name_last_node('filename')
+ self._double_quote_()
+
+ self._define(
+ ['filename'],
+ []
+ )
+ with self._option():
+ self._single_quote_()
+ self._cut()
+ self._filename_()
+ self.name_last_node('filename')
+ self._single_quote_()
+
+ self._define(
+ ['filename'],
+ []
+ )
+ with self._option():
+ self._filename_()
+ self.name_last_node('filename')
+ self._error(
+ 'expecting one of: '
+ ' '
+ )
+ self._sep_()
+ self.name_last_node('sep')
+ self._id_()
+ self.name_last_node('entry')
+
+ self._define(
+ ['entry', 'filename', 'sep'],
+ []
+ )
+
+ @tatsumasu('ModelCmd')
+ def _model_cmd_(self): # noqa
+ self._token('.MODEL')
+ self.name_last_node('cmd')
+ self._cut()
+ self._sep_()
+ self.name_last_node('sep')
+ self._model_name_()
+ self.name_last_node('name')
+ self._sep_()
+ self.name_last_node('sep')
+ self._model_type_()
+ self.name_last_node('type')
+ with self._optional():
+ with self._choice():
+ with self._option():
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._lp_()
+ self._cut()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._parameters_()
+ self.name_last_node('parameters')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._rp_()
+
+ self._define(
+ ['parameters', 'sep'],
+ []
+ )
+ with self._option():
+ self._sep_()
+ self.name_last_node('sep')
+ self._parameters_()
+ self.name_last_node('parameters')
+
+ self._define(
+ ['parameters', 'sep'],
+ []
+ )
+ self._error(
+ 'expecting one of: '
+ ' '
+ )
+
+ self._define(
+ ['cmd', 'name', 'parameters', 'sep', 'type'],
+ []
+ )
+
+ @tatsumasu()
+ def _model_type_(self): # noqa
+ with self._choice():
+ with self._option():
+ self._token('CAP')
+ with self._option():
+ self._token('CORE')
+ with self._option():
+ self._token('C')
+ with self._option():
+ self._token('DIG')
+ with self._option():
+ self._token('D')
+ with self._option():
+ self._token('IND')
+ with self._option():
+ self._token('ISWITCH')
+ with self._option():
+ self._token('LIN')
+ with self._option():
+ self._token('LTRA')
+ with self._option():
+ self._token('L')
+ with self._option():
+ self._token('NJF')
+ with self._option():
+ self._token('NMF')
+ with self._option():
+ self._token('NMOS')
+ with self._option():
+ self._token('NPN')
+ with self._option():
+ self._token('PJF')
+ with self._option():
+ self._token('PMF')
+ with self._option():
+ self._token('PMOS')
+ with self._option():
+ self._token('PNP')
+ with self._option():
+ self._token('RES')
+ with self._option():
+ self._token('R')
+ with self._option():
+ self._token('SWITCH')
+ with self._option():
+ self._token('TRANSLINE')
+ with self._option():
+ self._token('VSWITCH')
+ with self._option():
+ self._token('MEMRISTOR')
+ with self._option():
+ self._token('ZOD')
+ self._error(
+ 'expecting one of: '
+ "'C' 'CAP' 'CORE' 'D' 'DIG' 'IND'"
+ "'ISWITCH' 'L' 'LIN' 'LTRA' 'MEMRISTOR'"
+ "'NJF' 'NMF' 'NMOS' 'NPN' 'PJF' 'PMF'"
+ "'PMOS' 'PNP' 'R' 'RES' 'SWITCH'"
+ "'TRANSLINE' 'VSWITCH' 'ZOD'"
+ )
+
+ @tatsumasu('ParamCmd')
+ def _param_cmd_(self): # noqa
+ self._token('.PARAM')
+ self.name_last_node('cmd')
+ self._cut()
+ self._sep_()
+ self.name_last_node('sep')
+ self._parameters_()
+ self.name_last_node('parameters')
+
+ self._define(
+ ['cmd', 'parameters', 'sep'],
+ []
+ )
+
+ @tatsumasu('SimulatorCmd')
+ def _simulator_cmd_(self): # noqa
+ self._token('.SIMULATOR')
+ self.name_last_node('cmd')
+ self._cut()
+ self._sep_()
+ self.name_last_node('sep')
+ self._id_()
+ self.name_last_node('simulator')
+
+ self._define(
+ ['cmd', 'sep', 'simulator'],
+ []
+ )
+
+ @tatsumasu('SubcktCmd')
+ def _subckt_cmd_(self): # noqa
+ self._token('.SUBCKT')
+ self.name_last_node('cmd')
+ self._cut()
+ self._sep_()
+ self.name_last_node('sep')
+ self._model_name_()
+ self.name_last_node('name')
+
+ def block3():
+ self._sep_()
+ self.name_last_node('sep')
+ with self._group():
+ self._node_()
+ with self._ifnot():
+ self._token(':')
+ self.name_last_node('node')
+
+ self._define(
+ ['node', 'sep'],
+ []
+ )
+ self._closure(block3)
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._token('params:')
+ self._cut()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._parameters_()
+ self.name_last_node('parameters')
+
+ self._define(
+ ['parameters', 'sep'],
+ []
+ )
+ self._cmd_net_sep_()
+ self.name_last_node('sep')
+
+ def block10():
+ self._st_()
+ self._closure(block10)
+ self._netlist_lines_()
+ self.name_last_node('lines')
+
+ def block12():
+ self._st_()
+ self._closure(block12)
+ self._token('.ENDS')
+ self._cut()
+ with self._optional():
+
+ def block13():
+ self._st_()
+ self._positive_closure(block13)
+ self._model_name_()
+ self.name_last_node('name')
+
+ self._define(
+ ['name'],
+ []
+ )
+
+ self._define(
+ ['cmd', 'lines', 'name', 'node', 'parameters', 'sep'],
+ []
+ )
+
+ @tatsumasu('LibBlock')
+ def _lib_block_(self): # noqa
+ self._id_()
+ self.name_last_node('entry')
+ self._cmd_net_sep_()
+ self.name_last_node('sep')
+ self._cut()
+ self._netlist_lines_()
+ self.name_last_node('lines')
+
+ def block3():
+ self._st_()
+ self._closure(block3)
+ self._token('.ENDL')
+ self._cut()
+ with self._optional():
+
+ def block4():
+ self._st_()
+ self._positive_closure(block4)
+ self._id_()
+ self.name_last_node('entry')
+
+ self._define(
+ ['entry'],
+ []
+ )
+
+ self._define(
+ ['entry', 'lines', 'sep'],
+ []
+ )
+
+ @tatsumasu('TitleCmd')
+ def _title_cmd_(self): # noqa
+ self._token('.TITLE')
+ self.name_last_node('cmd')
+ self._cut()
+ self._text_()
+ self.name_last_node('title')
+
+ self._define(
+ ['cmd', 'title'],
+ []
+ )
+
+ @tatsumasu('Parameters')
+ def _parameters_(self): # noqa
+ self._parameter_()
+ self.add_last_node_to_name('@')
+
+ def block1():
+ with self._group():
+ with self._choice():
+ with self._option():
+ with self._group():
+ with self._optional():
+ self._sep_()
+ self._comma_()
+ with self._optional():
+ self._sep_()
+ with self._option():
+ self._sep_()
+ self._error(
+ 'expecting one of: '
+ ' '
+ )
+ self._parameter_()
+ self.add_last_node_to_name('@')
+ self._closure(block1)
+
+ @tatsumasu('Parameter')
+ def _parameter_(self): # noqa
+ self._id_()
+ self.name_last_node('name')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._token('=')
+ self._cut()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._gen_expr_()
+ self.name_last_node('value')
+
+ def block4():
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._comma_()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._gen_expr_()
+ self.name_last_node('value')
+
+ self._define(
+ ['sep', 'value'],
+ []
+ )
+ self._closure(block4)
+
+ self._define(
+ ['name', 'sep', 'value'],
+ []
+ )
+
+ @tatsumasu('GenericExpression')
+ def _gen_expr_(self): # noqa
+ with self._choice():
+ with self._option():
+ self._braced_expression_()
+ self.name_last_node('braced')
+ with self._option():
+ self._value_()
+ self.name_last_node('value')
+ self._error(
+ 'expecting one of: '
+ ' '
+ ' '
+ )
+
+ @tatsumasu('TableFile')
+ def _tablefile_(self): # noqa
+ self._token('tablefile')
+ self.name_last_node('func')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._lp_()
+ self._cut()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ with self._group():
+ with self._choice():
+ with self._option():
+ self._double_quote_()
+ self._cut()
+ self._filename_()
+ self.name_last_node('filename')
+ self._double_quote_()
+
+ self._define(
+ ['filename'],
+ []
+ )
+ with self._option():
+ self._filename_()
+ self.name_last_node('filename')
+ self._error(
+ 'expecting one of: '
+ ' '
+ )
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._rp_()
+
+ self._define(
+ ['filename', 'func', 'sep'],
+ []
+ )
+
+ @tatsumasu('BracedExpression')
+ def _braced_expression_(self): # noqa
+ self._lc_()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._expression_()
+ self.name_last_node('@')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._rc_()
+
+ self._define(
+ ['sep'],
+ []
+ )
+
+ @tatsumasu('ParenthesisNodes')
+ def _parenthesis_nodes_(self): # noqa
+ self._lp_()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._circuit_nodes_()
+ self.name_last_node('@')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._rp_()
+
+ self._define(
+ ['sep'],
+ []
+ )
+
+ @tatsumasu('CircuitNodes')
+ def _circuit_nodes_(self): # noqa
+ self._node_()
+ self.add_last_node_to_name('@')
+ with self._group():
+ with self._choice():
+ with self._option():
+ self._sep_()
+ self.name_last_node('sep')
+ with self._option():
+ with self._group():
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._comma_()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+
+ self._define(
+ ['sep'],
+ []
+ )
+ self._error(
+ 'expecting one of: '
+ ' '
+ )
+ self._node_()
+ self.add_last_node_to_name('@')
+
+ self._define(
+ ['sep'],
+ []
+ )
+
+ @tatsumasu('Expression')
+ @leftrec
+ def _expression_(self): # noqa
+ with self._choice():
+ with self._option():
+ self._ternary_()
+ self.name_last_node('ternary')
+ with self._option():
+ self._term_()
+ self.name_last_node('term')
+ self._error(
+ 'expecting one of: '
+ ' '
+ ' '
+ )
+
+ @tatsumasu('Ternary')
+ @nomemo
+ def _ternary_(self): # noqa
+ self._conditional_expression_()
+ self.name_last_node('t')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._token('?')
+ self.name_last_node('op')
+ self._cut()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._expression_()
+ self.name_last_node('x')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._token(':')
+ self._cut()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._expression_()
+ self.name_last_node('y')
+
+ self._define(
+ ['op', 'sep', 't', 'x', 'y'],
+ []
+ )
+
+ @tatsumasu('Conditional')
+ @nomemo
+ def _conditional_expression_(self): # noqa
+ self._boolean_or_()
+ self.name_last_node('expr')
+
+ @tatsumasu('Or')
+ @nomemo
+ def _boolean_or_(self): # noqa
+ self._boolean_xor_()
+ self.name_last_node('left')
+ with self._optional():
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._token('|')
+ self.name_last_node('op')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._boolean_or_()
+ self.name_last_node('right')
+
+ self._define(
+ ['op', 'right', 'sep'],
+ []
+ )
+
+ self._define(
+ ['left', 'op', 'right', 'sep'],
+ []
+ )
+
+ @tatsumasu('Xor')
+ @nomemo
+ def _boolean_xor_(self): # noqa
+ self._boolean_and_()
+ self.name_last_node('left')
+ with self._optional():
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._token('^')
+ self.name_last_node('op')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._boolean_xor_()
+ self.name_last_node('right')
+
+ self._define(
+ ['op', 'right', 'sep'],
+ []
+ )
+
+ self._define(
+ ['left', 'op', 'right', 'sep'],
+ []
+ )
+
+ @tatsumasu('And')
+ @nomemo
+ def _boolean_and_(self): # noqa
+ self._boolean_not_()
+ self.name_last_node('left')
+ with self._optional():
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._token('&')
+ self.name_last_node('op')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._boolean_and_()
+ self.name_last_node('right')
+
+ self._define(
+ ['op', 'right', 'sep'],
+ []
+ )
+
+ self._define(
+ ['left', 'op', 'right', 'sep'],
+ []
+ )
+
+ @tatsumasu('Not')
+ @nomemo
+ def _boolean_not_(self): # noqa
+ with self._optional():
+ self._token('~')
+ self.name_last_node('op')
+ self._relational_()
+ self.name_last_node('operator')
+
+ self._define(
+ ['op', 'operator'],
+ []
+ )
+
+ @tatsumasu('Relational')
+ @nomemo
+ def _relational_(self): # noqa
+ with self._choice():
+ with self._option():
+ self._expression_()
+ self.name_last_node('left')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ with self._group():
+ with self._choice():
+ with self._option():
+ self._token('==')
+ with self._option():
+ self._token('!=')
+ with self._option():
+ self._token('>=')
+ with self._option():
+ self._token('<=')
+ with self._option():
+ self._token('>')
+ with self._option():
+ self._token('<')
+ self._error(
+ 'expecting one of: '
+ "'!=' '<' '<=' '==' '>' '>='"
+ )
+ self.name_last_node('op')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._expression_()
+ self.name_last_node('right')
+
+ self._define(
+ ['left', 'op', 'right', 'sep'],
+ []
+ )
+ with self._option():
+ self._conditional_factor_()
+ self.name_last_node('factor')
+ self._error(
+ 'expecting one of: '
+ ' '
+ ''
+ ' '
+ ' '
+ )
+
+ @tatsumasu('ConditionalFactor')
+ def _conditional_factor_(self): # noqa
+ with self._choice():
+ with self._option():
+ self._lp_()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._conditional_expression_()
+ self.name_last_node('expr')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._rp_()
+
+ self._define(
+ ['expr', 'sep'],
+ []
+ )
+ with self._option():
+ self._boolean_()
+ self.name_last_node('boolean')
+ self._error(
+ 'expecting one of: '
+ "'(' 'FALSE' 'TRUE' "
+ )
+
+ @tatsumasu('Term')
+ def _term_(self): # noqa
+ self._add_sub_()
+ self.name_last_node('@')
+
+ @tatsumasu('AddSub')
+ def _add_sub_(self): # noqa
+ self._prod_()
+ self.name_last_node('left')
+ with self._optional():
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ with self._group():
+ with self._choice():
+ with self._option():
+ self._token('+')
+ with self._option():
+ self._token('-')
+ self._error(
+ 'expecting one of: '
+ "'+' '-'"
+ )
+ self.name_last_node('op')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._add_sub_()
+ self.name_last_node('right')
+
+ self._define(
+ ['op', 'right', 'sep'],
+ []
+ )
+
+ self._define(
+ ['left', 'op', 'right', 'sep'],
+ []
+ )
+
+ @tatsumasu('ProdDivMod')
+ def _prod_(self): # noqa
+ self._unary_()
+ self.name_last_node('left')
+ with self._optional():
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ with self._group():
+ with self._choice():
+ with self._option():
+ self._token('*')
+ with self._option():
+ self._token('/')
+ with self._option():
+ self._token('%')
+ self._error(
+ 'expecting one of: '
+ "'%' '*' '/'"
+ )
+ self.name_last_node('op')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._prod_()
+ self.name_last_node('right')
+
+ self._define(
+ ['op', 'right', 'sep'],
+ []
+ )
+
+ self._define(
+ ['left', 'op', 'right', 'sep'],
+ []
+ )
+
+ @tatsumasu('Sign')
+ def _unary_(self): # noqa
+ with self._optional():
+ with self._group():
+ with self._choice():
+ with self._option():
+ self._token('+')
+ with self._option():
+ self._token('-')
+ self._error(
+ 'expecting one of: '
+ "'+' '-'"
+ )
+ self.name_last_node('op')
+ self._exp_()
+ self.name_last_node('operator')
+
+ self._define(
+ ['op', 'operator'],
+ []
+ )
+
+ @tatsumasu('Exponential')
+ def _exp_(self): # noqa
+ self._functional_()
+ self.name_last_node('left')
+ with self._optional():
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._token('**')
+ self.name_last_node('op')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._exp_()
+ self.name_last_node('right')
+
+ self._define(
+ ['op', 'right', 'sep'],
+ []
+ )
+
+ self._define(
+ ['left', 'op', 'right', 'sep'],
+ []
+ )
+
+ @tatsumasu('Functional')
+ def _functional_(self): # noqa
+ with self._choice():
+ with self._option():
+ self._functions_()
+ self.name_last_node('@')
+ with self._option():
+ self._variable_()
+ self.name_last_node('@')
+ self._error(
+ 'expecting one of: '
+ ' '
+ ' '
+ ' '
+ ' '
+ )
+
+ @tatsumasu('Variable')
+ def _variable_(self): # noqa
+ with self._choice():
+ with self._option():
+ self._lc_()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._var_id_()
+ self.name_last_node('variable')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._rc_()
+
+ self._define(
+ ['sep', 'variable'],
+ []
+ )
+ with self._option():
+ self._var_id_()
+ self.name_last_node('variable')
+ with self._option():
+ self._factor_()
+ self.name_last_node('factor')
+ self._error(
+ 'expecting one of: '
+ "'{' "
+ '[a-zA-Z] [a-zA-Z_`@#\\$][a-zA-Z0-'
+ '9_:`@#\\.\\$]*[a-zA-Z0-9_`@#\\.\\$]'
+ )
+
+ @tatsumasu('Factor')
+ def _factor_(self): # noqa
+ with self._choice():
+ with self._option():
+ self._lp_()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._expression_()
+ self.name_last_node('@')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._rp_()
+
+ self._define(
+ ['sep'],
+ []
+ )
+ with self._option():
+ self._value_()
+ self.name_last_node('@')
+ self._error(
+ 'expecting one of: '
+ "'(' "
+ ''
+ )
+
+ @tatsumasu('Functions')
+ def _functions_(self): # noqa
+ with self._choice():
+ with self._option():
+ self._functions_1_()
+ with self._option():
+ self._atan2_()
+ with self._option():
+ self._ddx_()
+ with self._option():
+ self._gauss_()
+ with self._option():
+ self._if_func_()
+ with self._option():
+ self._limit_()
+ with self._option():
+ self._functions_2_()
+ with self._option():
+ self._rand_()
+ with self._option():
+ self._unif_()
+ with self._option():
+ self._i_func_()
+ with self._option():
+ self._v_func_()
+ self._error(
+ 'expecting one of: '
+ "'Img' 'Ph' 'R' 'Re' 'abs' 'acos' 'acosh'"
+ "'agauss' 'arctan' 'asin' 'asinh' 'atan'"
+ "'atan2' 'atanh' 'aunif' 'ceil' 'cos'"
+ "'cosh' 'ddt' 'ddx' 'exp' 'floor' 'gauss'"
+ "'i' 'if' 'int' 'limit' 'ln' 'log'"
+ "'log10' 'm' 'max' 'min' 'nint' 'pow'"
+ "'pwr' 'pwrs' 'rand' 'sdt' 'sgn' 'sign'"
+ "'sin' 'sinh' 'sqrt' 'stp' 'tan' 'tanh'"
+ "'unif' 'uramp' 'v' "
+ ' '
+ ''
+ )
+
+ @tatsumasu()
+ def _functions_1_(self): # noqa
+ with self._group():
+ with self._choice():
+ with self._option():
+ self._token('abs')
+ with self._option():
+ self._token('ceil')
+ with self._option():
+ self._token('ddt')
+ with self._option():
+ self._token('floor')
+ with self._option():
+ self._token('int')
+ with self._option():
+ self._token('m')
+ with self._option():
+ self._token('nint')
+ with self._option():
+ self._token('sdt')
+ with self._option():
+ self._token('sgn')
+ with self._option():
+ self._token('stp')
+ with self._option():
+ self._token('sqrt')
+ with self._option():
+ self._token('uramp')
+ with self._option():
+ self._token('Ph')
+ with self._option():
+ self._token('Re')
+ with self._option():
+ self._token('R')
+ with self._option():
+ self._token('Img')
+ with self._option():
+ self._token('acosh')
+ with self._option():
+ self._token('acos')
+ with self._option():
+ self._token('asinh')
+ with self._option():
+ self._token('asin')
+ with self._option():
+ self._token('arctan')
+ with self._option():
+ self._token('atanh')
+ with self._option():
+ self._token('atan')
+ with self._option():
+ self._token('cosh')
+ with self._option():
+ self._token('cos')
+ with self._option():
+ self._token('exp')
+ with self._option():
+ self._token('ln')
+ with self._option():
+ self._token('log')
+ with self._option():
+ self._token('log10')
+ with self._option():
+ self._token('sinh')
+ with self._option():
+ self._token('sin')
+ with self._option():
+ self._token('tanh')
+ with self._option():
+ self._token('tan')
+ self._error(
+ 'expecting one of: '
+ "'Img' 'Ph' 'R' 'Re' 'abs' 'acos' 'acosh'"
+ "'arctan' 'asin' 'asinh' 'atan' 'atanh'"
+ "'ceil' 'cos' 'cosh' 'ddt' 'exp' 'floor'"
+ "'int' 'ln' 'log' 'log10' 'm' 'nint'"
+ "'sdt' 'sgn' 'sin' 'sinh' 'sqrt' 'stp'"
+ "'tan' 'tanh' 'uramp'"
+ )
+ self.name_last_node('func')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._lp_()
+ self._cut()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._expression_()
+ self.name_last_node('x')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._rp_()
+
+ self._define(
+ ['func', 'sep', 'x'],
+ []
+ )
+
+ @tatsumasu()
+ def _atan2_(self): # noqa
+ self._token('atan2')
+ self.name_last_node('func')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._lp_()
+ self._cut()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._expression_()
+ self.name_last_node('y')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._comma_()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._expression_()
+ self.name_last_node('x')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._rp_()
+
+ self._define(
+ ['func', 'sep', 'x', 'y'],
+ []
+ )
+
+ @tatsumasu()
+ def _ddx_(self): # noqa
+ self._token('ddx')
+ self.name_last_node('func')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._lp_()
+ self._cut()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._id_()
+ self.name_last_node('f')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._comma_()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._expression_()
+ self.name_last_node('x')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._rp_()
+
+ self._define(
+ ['f', 'func', 'sep', 'x'],
+ []
+ )
+
+ @tatsumasu()
+ def _gauss_(self): # noqa
+ with self._group():
+ with self._choice():
+ with self._option():
+ self._token('agauss')
+ with self._option():
+ self._token('gauss')
+ self._error(
+ 'expecting one of: '
+ "'agauss' 'gauss'"
+ )
+ self.name_last_node('func')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._lp_()
+ self._cut()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._expression_()
+ self.name_last_node('mu')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._comma_()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._expression_()
+ self.name_last_node('alpha')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._comma_()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._expression_()
+ self.name_last_node('n')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._rp_()
+
+ self._define(
+ ['alpha', 'func', 'mu', 'n', 'sep'],
+ []
+ )
+
+ @tatsumasu()
+ def _i_func_(self): # noqa
+ self._token('i')
+ self.name_last_node('func')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._lp_()
+ self._cut()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ with self._if():
+ self._token('V')
+ self._dev_()
+ self.name_last_node('device')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._rp_()
+
+ self._define(
+ ['device', 'func', 'sep'],
+ []
+ )
+
+ @tatsumasu()
+ def _if_func_(self): # noqa
+ self._token('if')
+ self.name_last_node('func')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._lp_()
+ self._cut()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._conditional_expression_()
+ self.name_last_node('t')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._comma_()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._expression_()
+ self.name_last_node('x')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._comma_()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._expression_()
+ self.name_last_node('y')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._rp_()
+
+ self._define(
+ ['func', 'sep', 't', 'x', 'y'],
+ []
+ )
+
+ @tatsumasu()
+ def _limit_(self): # noqa
+ self._token('limit')
+ self.name_last_node('func')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._lp_()
+ self._cut()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._expression_()
+ self.name_last_node('x')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._comma_()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._expression_()
+ self.name_last_node('y')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._comma_()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._expression_()
+ self.name_last_node('z')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._rp_()
+
+ self._define(
+ ['func', 'sep', 'x', 'y', 'z'],
+ []
+ )
+
+ @tatsumasu()
+ def _functions_2_(self): # noqa
+ with self._group():
+ with self._choice():
+ with self._option():
+ self._token('min')
+ with self._option():
+ self._token('max')
+ with self._option():
+ self._token('pwrs')
+ with self._option():
+ self._token('pow')
+ with self._option():
+ self._token('pwr')
+ with self._option():
+ self._token('sign')
+ self._error(
+ 'expecting one of: '
+ "'max' 'min' 'pow' 'pwr' 'pwrs' 'sign'"
+ )
+ self.name_last_node('func')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._lp_()
+ self._cut()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._expression_()
+ self.name_last_node('x')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._comma_()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._expression_()
+ self.name_last_node('y')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._rp_()
+
+ self._define(
+ ['func', 'sep', 'x', 'y'],
+ []
+ )
+
+ @tatsumasu()
+ def _rand_(self): # noqa
+ self._token('rand')
+ self.name_last_node('func')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._lp_()
+ self._cut()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._rp_()
+
+ self._define(
+ ['func', 'sep'],
+ []
+ )
+
+ @tatsumasu()
+ def _unif_(self): # noqa
+ with self._group():
+ with self._choice():
+ with self._option():
+ self._token('aunif')
+ with self._option():
+ self._token('unif')
+ self._error(
+ 'expecting one of: '
+ "'aunif' 'unif'"
+ )
+ self.name_last_node('func')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._lp_()
+ self._cut()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._expression_()
+ self.name_last_node('mu')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._comma_()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._expression_()
+ self.name_last_node('alpha')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._rp_()
+
+ self._define(
+ ['alpha', 'func', 'mu', 'sep'],
+ []
+ )
+
+ @tatsumasu()
+ def _v_func_(self): # noqa
+ self._token('v')
+ self.name_last_node('func')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._lp_()
+ self._cut()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._node_()
+ self.name_last_node('node')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ with self._optional():
+ self._comma_()
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+ self._node_()
+ self.name_last_node('node')
+ with self._optional():
+ self._sep_()
+ self.name_last_node('sep')
+
+ self._define(
+ ['node', 'sep'],
+ []
+ )
+ self._rp_()
+
+ self._define(
+ ['func', 'node', 'sep'],
+ []
+ )
+
+ @tatsumasu()
+ def _special_variables_(self): # noqa
+ with self._choice():
+ with self._option():
+ self._token('time')
+ with self._option():
+ self._token('temper')
+ with self._option():
+ self._token('temp')
+ with self._option():
+ self._token('freq')
+ with self._option():
+ self._token('vt')
+ with self._option():
+ self._token('pi')
+ self._error(
+ 'expecting one of: '
+ "'freq' 'pi' 'temp' 'temper' 'time' 'vt'"
+ )
+
+ @tatsumasu('Value')
+ def _value_(self): # noqa
+ with self._group():
+ with self._choice():
+ with self._option():
+ with self._group():
+ self._real_value_()
+ self.name_last_node('real')
+ self._token('+')
+ self._imag_value_()
+ self.name_last_node('imag')
+
+ self._define(
+ ['imag', 'real'],
+ []
+ )
+ with self._option():
+ self._imag_value_()
+ self.name_last_node('imag')
+ with self._option():
+ self._real_value_()
+ self.name_last_node('real')
+ self._error(
+ 'expecting one of: '
+ ' '
+ )
+ with self._optional():
+ with self._choice():
+ with self._option():
+ self._hz_()
+ with self._option():
+ self._unit_()
+ self._error(
+ 'expecting one of: '
+ ' '
+ )
+ self.name_last_node('unit')
+
+ self._define(
+ ['imag', 'real', 'unit'],
+ []
+ )
+
+ @tatsumasu('ImagValue')
+ def _imag_value_(self): # noqa
+ self._number_scale_()
+ self.name_last_node('value')
+ self._token('J')
+
+ self._define(
+ ['value'],
+ []
+ )
+
+ @tatsumasu('RealValue')
+ def _real_value_(self): # noqa
+ self._number_scale_()
+ self.name_last_node('value')
+
+ @tatsumasu()
+ def _freq_value_(self): # noqa
+ self._number_scale_()
+ self.name_last_node('value')
+ with self._optional():
+ self._hz_()
+ self.name_last_node('unit')
+
+ self._define(
+ ['unit', 'value'],
+ []
+ )
+
+ @tatsumasu('NumberScale')
+ def _number_scale_(self): # noqa
+ with self._choice():
+ with self._option():
+ self._floating_point_()
+ self.name_last_node('value')
+ with self._group():
+ with self._choice():
+ with self._option():
+ self._meg_()
+ with self._option():
+ with self._optional():
+ self._suffix_()
+ self._error(
+ 'expecting one of: '
+ '