Coverage for klayout_pex/netlist/expression/ngspice.py: 100%
12 statements
« prev ^ index » next coverage.py v7.17.2, created at 2026-11-06 13:14 +0000
« prev ^ index » next coverage.py v7.17.2, created at 2026-11-06 13:14 +0000
1#
2# --------------------------------------------------------------------------------
3# SPDX-FileCopyrightText: 2024-2026 Martin Jan Köhler and Harald Pretl
4# Johannes Kepler University, Institute for Integrated Circuits.
5#
6# This file is part of KPEX
7# (see https://github.com/iic-jku/klayout-pex).
8#
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation, either version 3 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program. If not, see <http://www.gnu.org/licenses/>.
21# SPDX-License-Identifier: GPL-3.0-or-later
22# --------------------------------------------------------------------------------
23#
25"""
26The expressions of ngspice (as of ngspice-46), e.g. of .param, {...} and '...':
27 - operators, from the weakest: c ? a : b, ||, &&, == !=, < <= > >=, + -, * / %, the unary - + !, ^ **
28 (e.g. -2^2 is -4, 2*-3 is -6)
29 - ^ and ** bind from the left (2^3^2 is 64), % is the remainder with the sign of the dividend
30 - numbers with SI suffix (e.g. 1.5u, 2meg), letters after it are ignored (10pF)
31 - functions as in ngspice: e.g. log is the natural logarithm, pwr(x, y) is |x|^y,
32 nint rounds half to even, int towards zero
33 - names (of parameters and functions) in any case
35NOTE: imports neither KLayout nor generated protobuf code, so that format-only paths can use it
36"""
38from __future__ import annotations
39import math
40from typing import *
42from ...util.unit_formatter import parse_spice_number
43from .nodes import BinaryOperator, ExpressionFunction, UnaryOperator
44from .syntax import Associativity, BinaryOperatorSyntax, ExpressionSyntax
47def _sign(x: float) -> float:
48 return float((x > 0) - (x < 0))
51_FUNCTIONS = [
52 ExpressionFunction('abs', 1, abs),
53 ExpressionFunction('sqrt', 1, math.sqrt),
54 ExpressionFunction('exp', 1, math.exp),
55 ExpressionFunction('ln', 1, math.log),
56 ExpressionFunction('log', 1, math.log),
57 ExpressionFunction('log10', 1, math.log10),
58 ExpressionFunction('pow', 2, math.pow),
59 ExpressionFunction('pwr', 2, lambda x, y: math.pow(abs(x), y)),
60 ExpressionFunction('sgn', 1, _sign),
61 ExpressionFunction('min', 2, min),
62 ExpressionFunction('max', 2, max),
63 ExpressionFunction('int', 1, lambda x: float(math.trunc(x))),
64 ExpressionFunction('nint', 1, lambda x: float(round(x))),
65 ExpressionFunction('floor', 1, lambda x: float(math.floor(x))),
66 ExpressionFunction('ceil', 1, lambda x: float(math.ceil(x))),
67 ExpressionFunction('sin', 1, math.sin),
68 ExpressionFunction('cos', 1, math.cos),
69 ExpressionFunction('tan', 1, math.tan),
70 ExpressionFunction('asin', 1, math.asin),
71 ExpressionFunction('acos', 1, math.acos),
72 ExpressionFunction('atan', 1, math.atan),
73 ExpressionFunction('arctan', 1, math.atan),
74 ExpressionFunction('sinh', 1, math.sinh),
75 ExpressionFunction('cosh', 1, math.cosh),
76 ExpressionFunction('tanh', 1, math.tanh),
77 ExpressionFunction('asinh', 1, math.asinh),
78 ExpressionFunction('acosh', 1, math.acosh),
79 ExpressionFunction('atanh', 1, math.atanh),
80 ExpressionFunction('ternary_fcn', 3, lambda c, a, b: a if c else b),
81]
84def _operators(precedence: int,
85 spellings: Dict[str, BinaryOperator],
86 associativity: Associativity = Associativity.LEFT) -> Dict[str, BinaryOperatorSyntax]:
87 return {s: BinaryOperatorSyntax(o, precedence, associativity) for s, o in spellings.items()}
90NGSPICE_SYNTAX = ExpressionSyntax(
91 binary_operators={
92 **_operators(1, {'||': BinaryOperator.OR}),
93 **_operators(2, {'&&': BinaryOperator.AND}),
94 **_operators(3, {'==': BinaryOperator.EQUAL, '!=': BinaryOperator.NOT_EQUAL}),
95 **_operators(4, {'<': BinaryOperator.LESS, '<=': BinaryOperator.LESS_EQUAL,
96 '>': BinaryOperator.GREATER, '>=': BinaryOperator.GREATER_EQUAL}),
97 **_operators(5, {'+': BinaryOperator.ADD, '-': BinaryOperator.SUBTRACT}),
98 **_operators(6, {'*': BinaryOperator.MULTIPLY, '/': BinaryOperator.DIVIDE, '%': BinaryOperator.REMAINDER}),
99 **_operators(8, {'^': BinaryOperator.POWER, '**': BinaryOperator.POWER}),
100 },
101 unary_operators={'-': UnaryOperator.NEGATIVE, '+': UnaryOperator.POSITIVE, '!': UnaryOperator.NOT},
102 unary_precedence=7,
103 functions={f.name: f for f in _FUNCTIONS},
104 number_pattern=r'(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?[A-Za-z]*',
105 parse_number=parse_spice_number,
106 ignores_case=True,
107)