pbi-parsers 0.8.0__tar.gz → 0.8.2__tar.gz
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-0.8.0 → pbi_parsers-0.8.2}/PKG-INFO +1 -1
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/__init__.py +1 -1
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/base/lexer.py +3 -0
- pbi_parsers-0.8.2/pbi_parsers/base/tokens.py +196 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/dax/tokens.py +17 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/pq/tokens.py +17 -0
- pbi_parsers-0.8.0/pbi_parsers/base/tokens.py +0 -66
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/.github/workflows/deploy-docs.yml +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/.github/workflows/deploy.yml +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/.gitignore +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/LICENSE +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/README.md +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/docs/docs/api/dax/formatter.md +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/docs/docs/api/dax/lexer.md +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/docs/docs/api/dax/parser.md +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/docs/docs/api/pq/formatter.md +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/docs/docs/api/pq/lexer.md +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/docs/docs/api/pq/parser.md +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/docs/docs/api/shared/lexer.md +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/docs/docs/api/shared/text_slice.md +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/docs/docs/api/shared/token.md +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/docs/docs/index.md +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/docs/mkdocs.yml +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/base/__init__.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/dax/__init__.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/dax/exprs/__init__.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/dax/exprs/_base.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/dax/exprs/_utils.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/dax/exprs/add_sub.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/dax/exprs/add_sub_unary.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/dax/exprs/array.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/dax/exprs/column.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/dax/exprs/comparison.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/dax/exprs/concatenation.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/dax/exprs/div_mul.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/dax/exprs/exponent.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/dax/exprs/function.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/dax/exprs/hierarchy.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/dax/exprs/identifier.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/dax/exprs/ins.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/dax/exprs/keyword.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/dax/exprs/literal_number.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/dax/exprs/literal_string.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/dax/exprs/logical.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/dax/exprs/measure.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/dax/exprs/none.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/dax/exprs/parens.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/dax/exprs/returns.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/dax/exprs/table.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/dax/exprs/variable.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/dax/formatter.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/dax/lexer.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/dax/main.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/dax/parser.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/dax/utils.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/pq/__init__.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/pq/exprs/__init__.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/pq/exprs/_base.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/pq/exprs/_utils.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/pq/exprs/add_sub.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/pq/exprs/add_sub_unary.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/pq/exprs/and_or_expr.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/pq/exprs/array.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/pq/exprs/arrow.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/pq/exprs/column.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/pq/exprs/comparison.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/pq/exprs/concatenation.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/pq/exprs/div_mul.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/pq/exprs/each.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/pq/exprs/ellipsis_expr.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/pq/exprs/function.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/pq/exprs/identifier.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/pq/exprs/if_expr.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/pq/exprs/is_expr.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/pq/exprs/keyword.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/pq/exprs/literal_number.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/pq/exprs/literal_string.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/pq/exprs/meta.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/pq/exprs/negation.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/pq/exprs/none.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/pq/exprs/not_expr.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/pq/exprs/parens.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/pq/exprs/record.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/pq/exprs/row.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/pq/exprs/row_index.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/pq/exprs/statement.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/pq/exprs/try_expr.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/pq/exprs/type_expr.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/pq/exprs/variable.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/pq/formatter.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/pq/lexer.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/pq/main.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pbi_parsers/pq/parser.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/pyproject.toml +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/tests/__init__.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/tests/test_dax/__init__.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/tests/test_dax/test_exprs/__init__.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/tests/test_dax/test_exprs/test_add_sub.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/tests/test_dax/test_exprs/test_add_sub_unary.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/tests/test_dax/test_exprs/test_array.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/tests/test_dax/test_exprs/test_column.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/tests/test_dax/test_exprs/test_comparison.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/tests/test_dax/test_exprs/test_concatenation.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/tests/test_dax/test_exprs/test_div_mul.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/tests/test_dax/test_exprs/test_exponent.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/tests/test_dax/test_exprs/test_function.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/tests/test_dax/test_exprs/test_hierarchy.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/tests/test_dax/test_exprs/test_identifier.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/tests/test_dax/test_exprs/test_ins.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/tests/test_dax/test_exprs/test_keyword.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/tests/test_dax/test_exprs/test_literal_number.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/tests/test_dax/test_exprs/test_literal_string.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/tests/test_dax/test_exprs/test_logical.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/tests/test_dax/test_exprs/test_measure.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/tests/test_dax/test_exprs/test_parens.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/tests/test_dax/test_exprs/test_returns.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/tests/test_dax/test_exprs/test_table.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/tests/test_dax/test_exprs/test_variable.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/tests/test_dax/test_formatter/__init__.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/tests/test_dax/test_formatter/test_basic.py +0 -0
- {pbi_parsers-0.8.0 → pbi_parsers-0.8.2}/tests/test_dax/test_lexer.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: pbi_parsers
|
3
|
-
Version: 0.8.
|
3
|
+
Version: 0.8.2
|
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/
|
@@ -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:
|
@@ -0,0 +1,196 @@
|
|
1
|
+
from dataclasses import dataclass, field
|
2
|
+
from typing import Any
|
3
|
+
|
4
|
+
|
5
|
+
@dataclass
|
6
|
+
class TextSlice:
|
7
|
+
full_text: str = ""
|
8
|
+
start: int = -1
|
9
|
+
end: int = -1
|
10
|
+
|
11
|
+
def __eq__(self, other: object) -> bool:
|
12
|
+
"""Checks equality based on the text slice."""
|
13
|
+
if not isinstance(other, TextSlice):
|
14
|
+
return NotImplemented
|
15
|
+
return self.full_text == other.full_text and self.start == other.start and self.end == other.end
|
16
|
+
|
17
|
+
def __hash__(self) -> int:
|
18
|
+
"""Returns a hash based on the text slice."""
|
19
|
+
return hash((self.full_text, self.start, self.end))
|
20
|
+
|
21
|
+
def __repr__(self) -> str:
|
22
|
+
"""Returns a string representation of the TextSlice."""
|
23
|
+
return f"TextSlice(text='{self.get_text()}', start={self.start}, end={self.end})"
|
24
|
+
|
25
|
+
def get_text(self) -> str:
|
26
|
+
"""Returns the text slice."""
|
27
|
+
return self.full_text[self.start : self.end]
|
28
|
+
|
29
|
+
|
30
|
+
@dataclass
|
31
|
+
class BaseToken:
|
32
|
+
tok_type: Any
|
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)
|
36
|
+
|
37
|
+
def __eq__(self, other: object) -> bool:
|
38
|
+
"""Checks equality based on token type and text slice."""
|
39
|
+
if not isinstance(other, BaseToken):
|
40
|
+
return NotImplemented
|
41
|
+
return self.tok_type == other.tok_type and self.text_slice == other.text_slice
|
42
|
+
|
43
|
+
def __hash__(self) -> int:
|
44
|
+
"""Returns a hash based on token type and text slice."""
|
45
|
+
return hash((self.tok_type, self.text_slice))
|
46
|
+
|
47
|
+
def __repr__(self) -> str:
|
48
|
+
pretty_text = self.text_slice.get_text().replace("\n", "\\n").replace("\r", "\\r")
|
49
|
+
return f"Token(type={self.tok_type.name}, text='{pretty_text}')"
|
50
|
+
|
51
|
+
def position(self) -> tuple[int, int]:
|
52
|
+
"""Returns the start and end positions of the token.
|
53
|
+
|
54
|
+
Returns:
|
55
|
+
tuple[int, int]: A tuple containing the start and end positions of the token within the source text.
|
56
|
+
|
57
|
+
"""
|
58
|
+
return self.text_slice.start, self.text_slice.end
|
59
|
+
|
60
|
+
@property
|
61
|
+
def text(self) -> str:
|
62
|
+
"""Returns the text underlying the token.
|
63
|
+
|
64
|
+
Returns:
|
65
|
+
str: The text of the token as a string.
|
66
|
+
|
67
|
+
"""
|
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
|
@@ -2,6 +2,7 @@ from dataclasses import dataclass
|
|
2
2
|
from enum import Enum, auto
|
3
3
|
|
4
4
|
from pbi_parsers.base import BaseToken
|
5
|
+
from pbi_parsers.base.tokens import TextSlice
|
5
6
|
|
6
7
|
|
7
8
|
class TokenType(Enum):
|
@@ -39,6 +40,8 @@ class TokenType(Enum):
|
|
39
40
|
UNQUOTED_IDENTIFIER = auto()
|
40
41
|
VARIABLE = auto()
|
41
42
|
WHITESPACE = auto()
|
43
|
+
UNKNOWN = auto()
|
44
|
+
"""unknown is used when someone replaces a token with a str"""
|
42
45
|
|
43
46
|
|
44
47
|
KEYWORD_MAPPING = {
|
@@ -52,3 +55,17 @@ KEYWORD_MAPPING = {
|
|
52
55
|
@dataclass
|
53
56
|
class Token(BaseToken):
|
54
57
|
tok_type: TokenType = TokenType.EOF
|
58
|
+
|
59
|
+
@staticmethod
|
60
|
+
def from_str(value: str, tok_type: TokenType = TokenType.UNKNOWN) -> "Token":
|
61
|
+
tok_type = KEYWORD_MAPPING.get(value, tok_type)
|
62
|
+
return Token(
|
63
|
+
tok_type=tok_type,
|
64
|
+
text_slice=TextSlice(value, 0, len(value)),
|
65
|
+
)
|
66
|
+
|
67
|
+
def add_token_before(self, text: str, tok_type: TokenType) -> None:
|
68
|
+
super().add_token_before(text, tok_type)
|
69
|
+
|
70
|
+
def add_token_after(self, text: str, tok_type: TokenType) -> None:
|
71
|
+
super().add_token_after(text, tok_type)
|
@@ -3,6 +3,8 @@ from enum import Enum
|
|
3
3
|
|
4
4
|
from pbi_parsers.base import BaseToken
|
5
5
|
|
6
|
+
from ..base.tokens import TextSlice
|
7
|
+
|
6
8
|
|
7
9
|
class TokenType(Enum):
|
8
10
|
LET = 1
|
@@ -52,12 +54,27 @@ class TokenType(Enum):
|
|
52
54
|
IS = 46
|
53
55
|
AS = 47
|
54
56
|
EXCLAMATION_POINT = 48
|
57
|
+
UNKNOWN = 99
|
58
|
+
"""unknown is used when someone replaces a token with a str"""
|
55
59
|
|
56
60
|
|
57
61
|
@dataclass
|
58
62
|
class Token(BaseToken):
|
59
63
|
tok_type: TokenType = TokenType.EOF
|
60
64
|
|
65
|
+
@staticmethod
|
66
|
+
def from_str(value: str, tok_type: TokenType = TokenType.UNKNOWN) -> "Token":
|
67
|
+
return Token(
|
68
|
+
tok_type=tok_type,
|
69
|
+
text_slice=TextSlice(value, 0, len(value)),
|
70
|
+
)
|
71
|
+
|
72
|
+
def add_token_before(self, text: str, tok_type: TokenType) -> None:
|
73
|
+
super().add_token_before(text, tok_type)
|
74
|
+
|
75
|
+
def add_token_after(self, text: str, tok_type: TokenType) -> None:
|
76
|
+
super().add_token_after(text, tok_type)
|
77
|
+
|
61
78
|
|
62
79
|
# These are tokens that could also be used as identifiers in expressions.
|
63
80
|
TEXT_TOKENS = (
|
@@ -1,66 +0,0 @@
|
|
1
|
-
from dataclasses import dataclass, field
|
2
|
-
from typing import Any
|
3
|
-
|
4
|
-
|
5
|
-
@dataclass
|
6
|
-
class TextSlice:
|
7
|
-
full_text: str = ""
|
8
|
-
start: int = -1
|
9
|
-
end: int = -1
|
10
|
-
|
11
|
-
def __eq__(self, other: object) -> bool:
|
12
|
-
"""Checks equality based on the text slice."""
|
13
|
-
if not isinstance(other, TextSlice):
|
14
|
-
return NotImplemented
|
15
|
-
return self.full_text == other.full_text and self.start == other.start and self.end == other.end
|
16
|
-
|
17
|
-
def __hash__(self) -> int:
|
18
|
-
"""Returns a hash based on the text slice."""
|
19
|
-
return hash((self.full_text, self.start, self.end))
|
20
|
-
|
21
|
-
def __repr__(self) -> str:
|
22
|
-
"""Returns a string representation of the TextSlice."""
|
23
|
-
return f"TextSlice(text='{self.get_text()}', start={self.start}, end={self.end})"
|
24
|
-
|
25
|
-
def get_text(self) -> str:
|
26
|
-
"""Returns the text slice."""
|
27
|
-
return self.full_text[self.start : self.end]
|
28
|
-
|
29
|
-
|
30
|
-
@dataclass
|
31
|
-
class BaseToken:
|
32
|
-
tok_type: Any
|
33
|
-
text_slice: TextSlice = field(default_factory=TextSlice)
|
34
|
-
|
35
|
-
def __eq__(self, other: object) -> bool:
|
36
|
-
"""Checks equality based on token type and text slice."""
|
37
|
-
if not isinstance(other, BaseToken):
|
38
|
-
return NotImplemented
|
39
|
-
return self.tok_type == other.tok_type and self.text_slice == other.text_slice
|
40
|
-
|
41
|
-
def __hash__(self) -> int:
|
42
|
-
"""Returns a hash based on token type and text slice."""
|
43
|
-
return hash((self.tok_type, self.text_slice))
|
44
|
-
|
45
|
-
def __repr__(self) -> str:
|
46
|
-
pretty_text = self.text_slice.get_text().replace("\n", "\\n").replace("\r", "\\r")
|
47
|
-
return f"Token(type={self.tok_type.name}, text='{pretty_text}')"
|
48
|
-
|
49
|
-
def position(self) -> tuple[int, int]:
|
50
|
-
"""Returns the start and end positions of the token.
|
51
|
-
|
52
|
-
Returns:
|
53
|
-
tuple[int, int]: A tuple containing the start and end positions of the token within the source text.
|
54
|
-
|
55
|
-
"""
|
56
|
-
return self.text_slice.start, self.text_slice.end
|
57
|
-
|
58
|
-
@property
|
59
|
-
def text(self) -> str:
|
60
|
-
"""Returns the text underlying the token.
|
61
|
-
|
62
|
-
Returns:
|
63
|
-
str: The text of the token as a string.
|
64
|
-
|
65
|
-
"""
|
66
|
-
return self.text_slice.get_text()
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|