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

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# 

24 

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 

34 

35NOTE: imports neither KLayout nor generated protobuf code, so that format-only paths can use it 

36""" 

37 

38from __future__ import annotations 

39import math 

40from typing import * 

41 

42from ...util.unit_formatter import parse_spice_number 

43from .nodes import BinaryOperator, ExpressionFunction, UnaryOperator 

44from .syntax import Associativity, BinaryOperatorSyntax, ExpressionSyntax 

45 

46 

47def _sign(x: float) -> float: 

48 return float((x > 0) - (x < 0)) 

49 

50 

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] 

82 

83 

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()} 

88 

89 

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)