1212from frequenz .channels import Receiver
1313from frequenz .client .common .microgrid .components import ComponentId
1414from frequenz .quantities import Quantity
15+ from more_itertools import peekable
1516
1617from frequenz .sdk .timeseries import Sample
1718from frequenz .sdk .timeseries ._base_types import QuantityT
2223from ._formula import Formula
2324from ._functions import FunCall , Function
2425from ._lexer import Lexer
25- from ._peekable import Peekable
2626from ._resampled_stream_fetcher import ResampledStreamFetcher
2727
2828_logger = logging .getLogger (__name__ )
@@ -67,10 +67,17 @@ def __init__(
6767 """Initialize the parser."""
6868 self ._name : str = name
6969 self ._formula : str = formula
70- self ._lexer : Peekable [_token .Token ] = Peekable (Lexer (formula ))
70+ self ._lexer : peekable [_token .Token ] = peekable (Lexer (formula ))
7171 self ._telemetry_fetcher : ResampledStreamFetcher = telemetry_fetcher
7272 self ._create_method : Callable [[float ], QuantityT ] = create_method
7373
74+ def _peek_next_token (self ) -> _token .Token | None :
75+ """Get the next token from the lexer, or None if there is None."""
76+ try :
77+ return self ._lexer .peek ()
78+ except StopIteration :
79+ return None
80+
7481 def _parse_term (self ) -> AstNode [QuantityT ] | None :
7582 """Parse a term.
7683
@@ -81,7 +88,7 @@ def _parse_term(self) -> AstNode[QuantityT] | None:
8188 if factor is None :
8289 return None
8390
84- token : _token .Token | None = self ._lexer . peek ()
91+ token : _token .Token | None = self ._peek_next_token ()
8592 while token is not None and isinstance (token , (_token .Plus , _token .Minus )):
8693 token = next (self ._lexer )
8794 next_factor = self ._parse_factor ()
@@ -98,7 +105,7 @@ def _parse_term(self) -> AstNode[QuantityT] | None:
98105 elif isinstance (token , _token .Minus ):
99106 factor = _ast .Sub (left = factor , right = next_factor )
100107
101- token = self ._lexer . peek ()
108+ token = self ._peek_next_token ()
102109
103110 return factor
104111
@@ -113,7 +120,7 @@ def _parse_factor(self) -> AstNode[QuantityT] | None:
113120 if unary is None :
114121 return None
115122
116- token : _token .Token | None = self ._lexer . peek ()
123+ token : _token .Token | None = self ._peek_next_token ()
117124 while token is not None and isinstance (token , (_token .Mul , _token .Div )):
118125 token = next (self ._lexer )
119126 next_unary = self ._parse_unary ()
@@ -129,7 +136,7 @@ def _parse_factor(self) -> AstNode[QuantityT] | None:
129136 elif isinstance (token , _token .Div ):
130137 unary = _ast .Div (left = unary , right = next_unary )
131138
132- token = self ._lexer . peek ()
139+ token = self ._peek_next_token ()
133140
134141 return unary
135142
@@ -139,7 +146,7 @@ def _parse_unary(self) -> AstNode[QuantityT] | None:
139146 A unary is any expression that does not contain any binary
140147 operators outside of parentheses.
141148 """
142- token : _token .Token | None = self ._lexer . peek ()
149+ token : _token .Token | None = self ._peek_next_token ()
143150 if token is not None and isinstance (token , _token .Minus ):
144151 token = next (self ._lexer )
145152 primary : AstNode [QuantityT ] | None = self ._parse_primary ()
@@ -167,7 +174,7 @@ def _parse_bracketed(self) -> AstNode[QuantityT] | None:
167174 message = "Expected expression" ,
168175 )
169176
170- token : _token .Token | None = self ._lexer . peek ()
177+ token : _token .Token | None = self ._peek_next_token ()
171178 if token is None or not isinstance (token , _token .CloseParen ):
172179 raise FormulaSyntaxError (
173180 formula = self ._formula ,
@@ -194,7 +201,7 @@ def _parse_function_call(self) -> AstNode[QuantityT] | None:
194201
195202 params : list [AstNode [QuantityT ]] = []
196203
197- token : _token .Token | None = self ._lexer . peek ()
204+ token : _token .Token | None = self ._peek_next_token ()
198205 if token is None or not isinstance (token , _token .OpenParen ):
199206 raise FormulaSyntaxError (
200207 formula = self ._formula ,
@@ -213,7 +220,7 @@ def _parse_function_call(self) -> AstNode[QuantityT] | None:
213220 )
214221 params .append (param )
215222
216- token = self ._lexer . peek ()
223+ token = self ._peek_next_token ()
217224 if token is None :
218225 raise FormulaSyntaxError (
219226 formula = self ._formula ,
@@ -245,7 +252,7 @@ def _parse_primary(self) -> AstNode[QuantityT] | None:
245252 - A function call
246253 - A bracketed expression
247254 """
248- token : _token .Token | None = self ._lexer . peek ()
255+ token : _token .Token | None = self ._peek_next_token ()
249256 if token is None :
250257 return None
251258
@@ -286,7 +293,7 @@ def parse(self) -> Formula[QuantityT]:
286293 message = "Empty formula" ,
287294 )
288295 # There should not be any tokens left
289- token = self ._lexer . peek ()
296+ token = self ._peek_next_token ()
290297 if token is not None :
291298 raise FormulaSyntaxError (
292299 formula = self ._formula ,
0 commit comments