pbi-parsers 0.7.21__py3-none-any.whl → 0.8.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
pbi_parsers/__init__.py CHANGED
@@ -1,6 +1,6 @@
1
1
  from . import dax, pq
2
2
 
3
- __version__ = "0.7.21"
3
+ __version__ = "0.8.1"
4
4
 
5
5
 
6
6
  __all__ = [
pbi_parsers/base/lexer.py CHANGED
@@ -118,6 +118,9 @@ class BaseLexer:
118
118
  """
119
119
  while not self.at_end():
120
120
  self.tokens.append(self.scan_helper())
121
+ for a_tok, b_tok in zip(self.tokens, self.tokens[1:], strict=False):
122
+ a_tok.next_token = b_tok
123
+ b_tok.prior_token = a_tok
121
124
  return tuple(self.tokens)
122
125
 
123
126
  def scan_helper(self) -> BaseToken:
@@ -31,6 +31,8 @@ class TextSlice:
31
31
  class BaseToken:
32
32
  tok_type: Any
33
33
  text_slice: TextSlice = field(default_factory=TextSlice)
34
+ prior_token: "BaseToken | None" = field(repr=False, default=None)
35
+ next_token: "BaseToken | None" = field(repr=False, default=None)
34
36
 
35
37
  def __eq__(self, other: object) -> bool:
36
38
  """Checks equality based on token type and text slice."""
@@ -64,3 +66,131 @@ class BaseToken:
64
66
 
65
67
  """
66
68
  return self.text_slice.get_text()
69
+
70
+ def add_token_before(self, text: str, tok_type: Any) -> None:
71
+ """Adds a token before the current token in the linked list.
72
+
73
+ Args:
74
+ text (str): The text to add before the current token.
75
+ tok_type (Any): The type of the token to add.
76
+
77
+ """
78
+ new_global_text = (
79
+ self.text_slice.full_text[: self.text_slice.start]
80
+ + text
81
+ + self.text_slice.full_text[self.text_slice.start :]
82
+ )
83
+ self._update_full_text(new_global_text)
84
+
85
+ length = len(text)
86
+ tok = BaseToken(
87
+ tok_type=tok_type,
88
+ text_slice=TextSlice(
89
+ full_text=new_global_text,
90
+ start=self.text_slice.start,
91
+ end=self.text_slice.start + length,
92
+ ),
93
+ prior_token=self.prior_token,
94
+ next_token=self,
95
+ )
96
+ if self.prior_token:
97
+ self.prior_token.next_token = tok
98
+ self.prior_token = tok
99
+
100
+ # prior_token because we need to update the current token's position as well
101
+ curr_tok = self.prior_token
102
+ while curr_tok := curr_tok.next_token:
103
+ curr_tok.text_slice.start += length
104
+ curr_tok.text_slice.end += length
105
+
106
+ def add_token_after(self, text: str, tok_type: Any) -> None:
107
+ """Adds a token after the current token in the linked list.
108
+
109
+ Args:
110
+ text (str): The text to add before the current token.
111
+ tok_type (Any): The type of the token to add.
112
+
113
+ """
114
+ new_global_text = (
115
+ self.text_slice.full_text[: self.text_slice.end] + text + self.text_slice.full_text[self.text_slice.end :]
116
+ )
117
+ self._update_full_text(new_global_text)
118
+
119
+ length = len(text)
120
+ tok = BaseToken(
121
+ tok_type=tok_type,
122
+ text_slice=TextSlice(
123
+ full_text=new_global_text,
124
+ start=self.text_slice.end,
125
+ end=self.text_slice.end + length,
126
+ ),
127
+ prior_token=self,
128
+ next_token=self.next_token,
129
+ )
130
+ if self.next_token:
131
+ self.next_token.prior_token = tok
132
+ self.next_token = tok
133
+
134
+ # prior_token because we need to update the current token's position as well
135
+ curr_tok = self
136
+ while curr_tok := curr_tok.next_token:
137
+ curr_tok.text_slice.start += length
138
+ curr_tok.text_slice.end += length
139
+
140
+ def remove(self) -> None:
141
+ """Removes the current token from the linked list."""
142
+ new_global_text = (
143
+ self.text_slice.full_text[: self.text_slice.start] + self.text_slice.full_text[self.text_slice.end :]
144
+ )
145
+ self._update_full_text(new_global_text)
146
+
147
+ curr_tok = self
148
+ length = len(self.text)
149
+ while curr_tok := curr_tok.next_token:
150
+ curr_tok.text_slice.start -= length
151
+ curr_tok.text_slice.end -= length
152
+
153
+ if self.prior_token:
154
+ self.prior_token.next_token = self.next_token
155
+ if self.next_token:
156
+ self.next_token.prior_token = self.prior_token
157
+ self.prior_token = None
158
+ self.next_token = None
159
+
160
+ def replace(self, new_text: str) -> None:
161
+ """Replaces the text of the current token with new text.
162
+
163
+ Args:
164
+ new_text (str): The new text to replace the current token's text.
165
+
166
+ """
167
+ new_global_text = (
168
+ self.text_slice.full_text[: self.text_slice.start]
169
+ + new_text
170
+ + self.text_slice.full_text[self.text_slice.end :]
171
+ )
172
+ self._update_full_text(new_global_text)
173
+
174
+ len_diff = len(new_text) - len(self.text)
175
+ # Adjust the positions of subsequent tokens
176
+ self.text_slice.end += len_diff
177
+ current = self.next_token
178
+ while current:
179
+ current.text_slice.start += len_diff
180
+ current.text_slice.end += len_diff
181
+ current = current.next_token
182
+
183
+ def _update_full_text(self, new_full_text: str) -> None:
184
+ """Updates the full text of the token and adjusts the text slice accordingly.
185
+
186
+ Args:
187
+ new_full_text (str): The new full text to update the token's text slice.
188
+
189
+ """
190
+ self.text_slice.full_text = new_full_text
191
+ curr_tok = self
192
+ while curr_tok := curr_tok.next_token:
193
+ curr_tok.text_slice.full_text = new_full_text
194
+ curr_tok = self
195
+ while curr_tok := curr_tok.prior_token:
196
+ curr_tok.text_slice.full_text = new_full_text
@@ -1,4 +1,5 @@
1
- from typing import TYPE_CHECKING, Any
1
+ from collections.abc import Callable
2
+ from typing import TYPE_CHECKING, Any, TypeVar
2
3
 
3
4
  from pbi_parsers.dax.tokens import TokenType
4
5
 
@@ -6,6 +7,9 @@ if TYPE_CHECKING:
6
7
  from pbi_parsers.dax.parser import Parser
7
8
 
8
9
 
10
+ T = TypeVar("T", bound="Expression")
11
+
12
+
9
13
  class Expression:
10
14
  pre_comments: list[Any] = []
11
15
  post_comments: list[Any] = []
@@ -23,6 +27,37 @@ class Expression:
23
27
  msg = "This method should be implemented by subclasses."
24
28
  raise NotImplementedError(msg)
25
29
 
30
+ def find(self, cls_type: type[T] | tuple[type[T], ...], attributes: Callable[[T], bool] | None = None) -> T:
31
+ attributes = attributes or (lambda _x: True)
32
+
33
+ if isinstance(self, cls_type) and attributes(self):
34
+ return self
35
+
36
+ for child in self.children():
37
+ try:
38
+ return child.find(cls_type, attributes)
39
+ except ValueError:
40
+ continue
41
+
42
+ msg = f"Matching {cls_type} not found in expression tree."
43
+ raise ValueError(msg)
44
+
45
+ def find_all(
46
+ self,
47
+ cls_type: type[T] | tuple[type[T], ...],
48
+ attributes: Callable[[T], bool] | None = None,
49
+ ) -> list[T]:
50
+ attributes = attributes or (lambda _x: True)
51
+ results: list[T] = []
52
+
53
+ if isinstance(self, cls_type) and attributes(self):
54
+ results.append(self)
55
+
56
+ for child in self.children():
57
+ results.extend(child.find_all(cls_type, attributes))
58
+
59
+ return results
60
+
26
61
  @classmethod
27
62
  def match(cls, parser: "Parser") -> "Expression | None":
28
63
  """Attempt to match the current tokens to this expression type.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pbi_parsers
3
- Version: 0.7.21
3
+ Version: 0.8.1
4
4
  Summary: Power BI lexer, parsers, and formatters for DAX and M (Power Query) languages
5
5
  Project-URL: Homepage, https://github.com/douglassimonsen/pbi_parsers
6
6
  Project-URL: Documentation, https://douglassimonsen.github.io/pbi_parsers/
@@ -1,7 +1,7 @@
1
- pbi_parsers/__init__.py,sha256=XYRPT6QSy184iWUMvcvZjM8YxFQb7VBbETk4M3-OF3M,92
1
+ pbi_parsers/__init__.py,sha256=ljbk7gxiajzYkx2vu3YTjMDWJjYoFMUhfrwtK2iJbI4,91
2
2
  pbi_parsers/base/__init__.py,sha256=U7QpzFFD9A4wK3ZHin6xg5fPgTca0y5KC-O7nrZ-flM,115
3
- pbi_parsers/base/lexer.py,sha256=5iOkdYzJ9wGwz7I4rDlc7slrRNUPzi0oFFkDxN1d62M,4180
4
- pbi_parsers/base/tokens.py,sha256=slIVl4673xXomqHMgrn1ApHs8YRvcC2yQgs1zfSpe1U,2220
3
+ pbi_parsers/base/lexer.py,sha256=Rl2cWlySJblFHvwW8oMVAjnCh83Zjo4N7jW3pkRZZO0,4335
4
+ pbi_parsers/base/tokens.py,sha256=c1PIAU5oRxZbI74SY5KT3AZ3WrcukzXtuiLPrZU4f2o,7017
5
5
  pbi_parsers/dax/__init__.py,sha256=w8tfYFRwfjndq-QNnYQO3cu8fri4-OlG-edxUAKunF4,479
6
6
  pbi_parsers/dax/formatter.py,sha256=jOFnwcgQGIzsmi5sfkKoB_pFEGjDPd8E_pwMPwudmy4,7674
7
7
  pbi_parsers/dax/lexer.py,sha256=2_pERJSrSYd8VujOe9TxJa9R2Ex8mvP-bCotH7uVBZY,8025
@@ -10,7 +10,7 @@ pbi_parsers/dax/parser.py,sha256=QLKrIBcxZ26TGhTHpeKcTGEHEHUDLC6IgpxxrdJzdek,182
10
10
  pbi_parsers/dax/tokens.py,sha256=nY1laCbL8vwALpJ4jcd8k4lAscqOwqdw3dFuj4_KKVk,1234
11
11
  pbi_parsers/dax/utils.py,sha256=OURPa-b6Ldn0_KKXPdLIPA3Zdc12OfbbFd2X5SocCek,4402
12
12
  pbi_parsers/dax/exprs/__init__.py,sha256=OUfiXzZYp5HkTPE9x595MMxpsgG1IvsED8p8spAKZuk,3432
13
- pbi_parsers/dax/exprs/_base.py,sha256=KIlP2AxtZVJGy49ANZfotYnBKt9-EDKOuTx2k48k9cc,1634
13
+ pbi_parsers/dax/exprs/_base.py,sha256=bMHLICgAUOqAKl_S9d6V8kk62cqM1jynUY2-gBJlEcs,2732
14
14
  pbi_parsers/dax/exprs/_utils.py,sha256=BxxRCtsqpL9t330ZfBnkm1lq7B_ejAoG89Ot2bLYJig,2123
15
15
  pbi_parsers/dax/exprs/add_sub.py,sha256=O-1DGXr4F4WWG1PyL1xra_pFR0ZxFo-_y-e6SioHrQc,2241
16
16
  pbi_parsers/dax/exprs/add_sub_unary.py,sha256=OhLF_AFxatvbI7X_Tj3_wQ0gjsnmuwkMkmcCzsECsK4,2003
@@ -73,7 +73,7 @@ pbi_parsers/pq/exprs/statement.py,sha256=JSg48pGAU3Ka2pt4lzVsYlVOqGeF_ARGm8Ajf0l
73
73
  pbi_parsers/pq/exprs/try_expr.py,sha256=UcnqfA-t9S1LVrKqeNUT8n4JJcO-ZQZoJrxAdjJ-GMA,1692
74
74
  pbi_parsers/pq/exprs/type_expr.py,sha256=hH5ubrIJaxwQsopNJHUZ4ByS1rHEgv2Tf8ocYqSukXM,2570
75
75
  pbi_parsers/pq/exprs/variable.py,sha256=wp4t0QHIGA264sXnWp7XVe1H8MJzMIOaoLNBQe-dfNk,1602
76
- pbi_parsers-0.7.21.dist-info/METADATA,sha256=EbXAfjLz-llL3r1V_cmYmX2aLZ-3Djh77b2Dsa96v50,2907
77
- pbi_parsers-0.7.21.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
78
- pbi_parsers-0.7.21.dist-info/licenses/LICENSE,sha256=Sn0IfXOE4B0iL9lZXmGmRuTGyJeCtefxcfws0bLjp2g,1072
79
- pbi_parsers-0.7.21.dist-info/RECORD,,
76
+ pbi_parsers-0.8.1.dist-info/METADATA,sha256=-B23BQU7BCehF7DUTBcDeD9gH8ndy730y41H3HxUicU,2906
77
+ pbi_parsers-0.8.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
78
+ pbi_parsers-0.8.1.dist-info/licenses/LICENSE,sha256=Sn0IfXOE4B0iL9lZXmGmRuTGyJeCtefxcfws0bLjp2g,1072
79
+ pbi_parsers-0.8.1.dist-info/RECORD,,