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 +1 -1
- pbi_parsers/base/lexer.py +3 -0
- pbi_parsers/base/tokens.py +130 -0
- pbi_parsers/dax/exprs/_base.py +36 -1
- {pbi_parsers-0.7.21.dist-info → pbi_parsers-0.8.1.dist-info}/METADATA +1 -1
- {pbi_parsers-0.7.21.dist-info → pbi_parsers-0.8.1.dist-info}/RECORD +8 -8
- {pbi_parsers-0.7.21.dist-info → pbi_parsers-0.8.1.dist-info}/WHEEL +0 -0
- {pbi_parsers-0.7.21.dist-info → pbi_parsers-0.8.1.dist-info}/licenses/LICENSE +0 -0
pbi_parsers/__init__.py
CHANGED
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:
|
pbi_parsers/base/tokens.py
CHANGED
@@ -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
|
pbi_parsers/dax/exprs/_base.py
CHANGED
@@ -1,4 +1,5 @@
|
|
1
|
-
from
|
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.
|
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=
|
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=
|
4
|
-
pbi_parsers/base/tokens.py,sha256=
|
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=
|
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.
|
77
|
-
pbi_parsers-0.
|
78
|
-
pbi_parsers-0.
|
79
|
-
pbi_parsers-0.
|
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,,
|
File without changes
|
File without changes
|