pbi-parsers 0.7.8__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.
Files changed (78) hide show
  1. pbi_parsers/__init__.py +9 -0
  2. pbi_parsers/base/__init__.py +7 -0
  3. pbi_parsers/base/lexer.py +127 -0
  4. pbi_parsers/base/tokens.py +61 -0
  5. pbi_parsers/dax/__init__.py +22 -0
  6. pbi_parsers/dax/exprs/__init__.py +107 -0
  7. pbi_parsers/dax/exprs/_base.py +46 -0
  8. pbi_parsers/dax/exprs/_utils.py +45 -0
  9. pbi_parsers/dax/exprs/add_sub.py +73 -0
  10. pbi_parsers/dax/exprs/add_sub_unary.py +72 -0
  11. pbi_parsers/dax/exprs/array.py +75 -0
  12. pbi_parsers/dax/exprs/column.py +56 -0
  13. pbi_parsers/dax/exprs/comparison.py +76 -0
  14. pbi_parsers/dax/exprs/concatenation.py +73 -0
  15. pbi_parsers/dax/exprs/div_mul.py +75 -0
  16. pbi_parsers/dax/exprs/exponent.py +67 -0
  17. pbi_parsers/dax/exprs/function.py +102 -0
  18. pbi_parsers/dax/exprs/hierarchy.py +68 -0
  19. pbi_parsers/dax/exprs/identifier.py +46 -0
  20. pbi_parsers/dax/exprs/ins.py +67 -0
  21. pbi_parsers/dax/exprs/keyword.py +60 -0
  22. pbi_parsers/dax/exprs/literal_number.py +46 -0
  23. pbi_parsers/dax/exprs/literal_string.py +45 -0
  24. pbi_parsers/dax/exprs/logical.py +76 -0
  25. pbi_parsers/dax/exprs/measure.py +44 -0
  26. pbi_parsers/dax/exprs/none.py +30 -0
  27. pbi_parsers/dax/exprs/parens.py +61 -0
  28. pbi_parsers/dax/exprs/returns.py +76 -0
  29. pbi_parsers/dax/exprs/table.py +51 -0
  30. pbi_parsers/dax/exprs/variable.py +68 -0
  31. pbi_parsers/dax/formatter.py +215 -0
  32. pbi_parsers/dax/lexer.py +222 -0
  33. pbi_parsers/dax/main.py +63 -0
  34. pbi_parsers/dax/parser.py +66 -0
  35. pbi_parsers/dax/tokens.py +54 -0
  36. pbi_parsers/dax/utils.py +120 -0
  37. pbi_parsers/pq/__init__.py +17 -0
  38. pbi_parsers/pq/exprs/__init__.py +98 -0
  39. pbi_parsers/pq/exprs/_base.py +33 -0
  40. pbi_parsers/pq/exprs/_utils.py +31 -0
  41. pbi_parsers/pq/exprs/add_sub.py +59 -0
  42. pbi_parsers/pq/exprs/add_sub_unary.py +57 -0
  43. pbi_parsers/pq/exprs/and_or_expr.py +60 -0
  44. pbi_parsers/pq/exprs/array.py +53 -0
  45. pbi_parsers/pq/exprs/arrow.py +50 -0
  46. pbi_parsers/pq/exprs/column.py +42 -0
  47. pbi_parsers/pq/exprs/comparison.py +62 -0
  48. pbi_parsers/pq/exprs/concatenation.py +61 -0
  49. pbi_parsers/pq/exprs/div_mul.py +59 -0
  50. pbi_parsers/pq/exprs/each.py +41 -0
  51. pbi_parsers/pq/exprs/ellipsis_expr.py +28 -0
  52. pbi_parsers/pq/exprs/function.py +63 -0
  53. pbi_parsers/pq/exprs/identifier.py +77 -0
  54. pbi_parsers/pq/exprs/if_expr.py +70 -0
  55. pbi_parsers/pq/exprs/is_expr.py +54 -0
  56. pbi_parsers/pq/exprs/keyword.py +40 -0
  57. pbi_parsers/pq/exprs/literal_number.py +31 -0
  58. pbi_parsers/pq/exprs/literal_string.py +31 -0
  59. pbi_parsers/pq/exprs/meta.py +54 -0
  60. pbi_parsers/pq/exprs/negation.py +52 -0
  61. pbi_parsers/pq/exprs/none.py +22 -0
  62. pbi_parsers/pq/exprs/not_expr.py +39 -0
  63. pbi_parsers/pq/exprs/parens.py +43 -0
  64. pbi_parsers/pq/exprs/record.py +58 -0
  65. pbi_parsers/pq/exprs/row.py +54 -0
  66. pbi_parsers/pq/exprs/row_index.py +57 -0
  67. pbi_parsers/pq/exprs/statement.py +67 -0
  68. pbi_parsers/pq/exprs/try_expr.py +55 -0
  69. pbi_parsers/pq/exprs/type_expr.py +78 -0
  70. pbi_parsers/pq/exprs/variable.py +52 -0
  71. pbi_parsers/pq/formatter.py +13 -0
  72. pbi_parsers/pq/lexer.py +219 -0
  73. pbi_parsers/pq/main.py +63 -0
  74. pbi_parsers/pq/parser.py +65 -0
  75. pbi_parsers/pq/tokens.py +81 -0
  76. pbi_parsers-0.7.8.dist-info/METADATA +66 -0
  77. pbi_parsers-0.7.8.dist-info/RECORD +78 -0
  78. pbi_parsers-0.7.8.dist-info/WHEEL +4 -0
@@ -0,0 +1,46 @@
1
+ from typing import TYPE_CHECKING
2
+
3
+ from pbi_parsers.dax.tokens import Token, TokenType
4
+
5
+ from ._base import Expression
6
+ from ._utils import lexer_reset
7
+
8
+ if TYPE_CHECKING:
9
+ from pbi_parsers.dax.parser import Parser
10
+
11
+
12
+ class LiteralNumberExpression(Expression):
13
+ """Represents a literal number in DAX.
14
+
15
+ Examples:
16
+ 42
17
+ 3.14
18
+ -1000
19
+
20
+ """
21
+
22
+ value: Token
23
+
24
+ def __init__(self, value: Token) -> None:
25
+ self.value = value
26
+
27
+ def pprint(self) -> str:
28
+ return f"Number ({self.value.text})"
29
+
30
+ @classmethod
31
+ @lexer_reset
32
+ def match(cls, parser: "Parser") -> "LiteralNumberExpression | None":
33
+ if cls.match_tokens(parser, [TokenType.NUMBER_LITERAL]):
34
+ value = parser.consume()
35
+ return LiteralNumberExpression(value=value)
36
+ return None
37
+
38
+ def children(self) -> list[Expression]: # noqa: PLR6301
39
+ """Returns a list of child expressions."""
40
+ return []
41
+
42
+ def position(self) -> tuple[int, int]:
43
+ return self.value.text_slice.start, self.value.text_slice.end
44
+
45
+ def full_text(self) -> str:
46
+ return self.value.text_slice.full_text
@@ -0,0 +1,45 @@
1
+ from typing import TYPE_CHECKING
2
+
3
+ from pbi_parsers.dax.tokens import Token, TokenType
4
+
5
+ from ._base import Expression
6
+ from ._utils import lexer_reset
7
+
8
+ if TYPE_CHECKING:
9
+ from pbi_parsers.dax.parser import Parser
10
+
11
+
12
+ class LiteralStringExpression(Expression):
13
+ """A literal string in DAX.
14
+
15
+ Examples:
16
+ "Hello, World!"
17
+ "Another String"
18
+
19
+ """
20
+
21
+ value: Token
22
+
23
+ def __init__(self, value: Token) -> None:
24
+ self.value = value
25
+
26
+ def pprint(self) -> str:
27
+ return f"String ({self.value.text})"
28
+
29
+ @classmethod
30
+ @lexer_reset
31
+ def match(cls, parser: "Parser") -> "LiteralStringExpression | None":
32
+ if cls.match_tokens(parser, [TokenType.STRING_LITERAL]):
33
+ value = parser.consume()
34
+ return LiteralStringExpression(value=value)
35
+ return None
36
+
37
+ def children(self) -> list[Expression]: # noqa: PLR6301
38
+ """Returns a list of child expressions."""
39
+ return []
40
+
41
+ def position(self) -> tuple[int, int]:
42
+ return self.value.text_slice.start, self.value.text_slice.end
43
+
44
+ def full_text(self) -> str:
45
+ return self.value.text_slice.full_text
@@ -0,0 +1,76 @@
1
+ import textwrap
2
+ from typing import TYPE_CHECKING
3
+
4
+ from pbi_parsers.dax.tokens import Token, TokenType
5
+
6
+ from ._base import Expression
7
+ from ._utils import lexer_reset
8
+
9
+ if TYPE_CHECKING:
10
+ from pbi_parsers.dax.parser import Parser
11
+
12
+
13
+ class LogicalExpression(Expression):
14
+ """AND/OR boolean expression in DAX.
15
+
16
+ Examples:
17
+ 1 || 2
18
+ func() && 3
19
+
20
+ """
21
+
22
+ operator: Token
23
+ left: Expression
24
+ right: Expression
25
+
26
+ def __init__(self, operator: Token, left: Expression, right: Expression) -> None:
27
+ self.operator = operator
28
+ self.left = left
29
+ self.right = right
30
+
31
+ @classmethod
32
+ @lexer_reset
33
+ def match(cls, parser: "Parser") -> "LogicalExpression | None":
34
+ from . import EXPRESSION_HIERARCHY, any_expression_match # noqa: PLC0415
35
+
36
+ skip_index = EXPRESSION_HIERARCHY.index(LogicalExpression)
37
+
38
+ left_term = any_expression_match(parser=parser, skip_first=skip_index + 1)
39
+ operator = parser.consume()
40
+
41
+ if not left_term:
42
+ return None
43
+ if operator.tok_type not in {
44
+ TokenType.DOUBLE_PIPE_OPERATOR,
45
+ TokenType.DOUBLE_AMPERSAND_OPERATOR,
46
+ }:
47
+ return None
48
+
49
+ right_term: Expression | None = any_expression_match(
50
+ parser=parser,
51
+ skip_first=skip_index,
52
+ )
53
+ if right_term is None:
54
+ msg = f"Expected a right term after operator {operator.text}, found: {parser.peek()}"
55
+ raise ValueError(msg)
56
+ return LogicalExpression(operator=operator, left=left_term, right=right_term)
57
+
58
+ def pprint(self) -> str:
59
+ left_str = textwrap.indent(self.left.pprint(), " " * 10).lstrip()
60
+ right_str = textwrap.indent(self.right.pprint(), " " * 10).lstrip()
61
+ return f"""
62
+ Logical (
63
+ operator: {self.operator.text},
64
+ left: {left_str},
65
+ right: {right_str}
66
+ )""".strip()
67
+
68
+ def children(self) -> list[Expression]:
69
+ """Returns a list of child expressions."""
70
+ return [self.left, self.right]
71
+
72
+ def position(self) -> tuple[int, int]:
73
+ return self.left.position()[0], self.right.position()[1]
74
+
75
+ def full_text(self) -> str:
76
+ return self.left.full_text()
@@ -0,0 +1,44 @@
1
+ from typing import TYPE_CHECKING
2
+
3
+ from pbi_parsers.dax.tokens import Token, TokenType
4
+
5
+ from ._base import Expression
6
+ from ._utils import lexer_reset
7
+
8
+ if TYPE_CHECKING:
9
+ from pbi_parsers.dax.parser import Parser
10
+
11
+
12
+ class MeasureExpression(Expression):
13
+ """Represents a measure in DAX.
14
+
15
+ Examples:
16
+ [Measure Name]
17
+
18
+ """
19
+
20
+ name: Token
21
+
22
+ def __init__(self, name: Token) -> None:
23
+ self.name = name
24
+
25
+ def pprint(self) -> str:
26
+ return f"Measure ({self.name.text})"
27
+
28
+ @classmethod
29
+ @lexer_reset
30
+ def match(cls, parser: "Parser") -> "MeasureExpression | None":
31
+ if cls.match_tokens(parser, [TokenType.BRACKETED_IDENTIFIER]):
32
+ name = parser.consume()
33
+ return MeasureExpression(name=name)
34
+ return None
35
+
36
+ def children(self) -> list[Expression]: # noqa: PLR6301
37
+ """Returns a list of child expressions."""
38
+ return []
39
+
40
+ def position(self) -> tuple[int, int]:
41
+ return self.name.text_slice.start, self.name.text_slice.end
42
+
43
+ def full_text(self) -> str:
44
+ return self.name.text_slice.full_text
@@ -0,0 +1,30 @@
1
+ from typing import TYPE_CHECKING
2
+
3
+ from ._base import Expression
4
+
5
+ if TYPE_CHECKING:
6
+ from pbi_parsers.dax.parser import Parser
7
+
8
+
9
+ class NoneExpression(Expression):
10
+ """Used to represent the absence of a value, so far only occurring when a argument is skipped in a function."""
11
+
12
+ def pprint(self) -> str: # noqa: PLR6301 # kept this way to match the interface
13
+ return "None"
14
+
15
+ @classmethod
16
+ def match(cls, parser: "Parser") -> "NoneExpression | None":
17
+ msg = "NoneExpression.match should not be called, this is a placeholder for the absence of an expression."
18
+ raise NotImplementedError(msg)
19
+
20
+ def children(self) -> list[Expression]: # noqa: PLR6301
21
+ """Returns a list of child expressions."""
22
+ return []
23
+
24
+ def position(self) -> tuple[int, int]: # noqa: PLR6301
25
+ """Returns the position of the expression."""
26
+ return -1, -1
27
+
28
+ def full_text(self) -> str: # noqa: PLR6301
29
+ """Returns the full text of the expression."""
30
+ return ""
@@ -0,0 +1,61 @@
1
+ from typing import TYPE_CHECKING
2
+
3
+ from pbi_parsers.dax.tokens import Token, TokenType
4
+
5
+ from ._base import Expression
6
+ from ._utils import lexer_reset
7
+
8
+ if TYPE_CHECKING:
9
+ from pbi_parsers.dax.parser import Parser
10
+
11
+
12
+ class ParenthesesExpression(Expression):
13
+ """Represents a parenthesized expression in DAX.
14
+
15
+ Examples:
16
+ (1 + 2)
17
+ (func())
18
+
19
+ """
20
+
21
+ inner_statement: Expression
22
+ parens: tuple[Token, Token]
23
+
24
+ def __init__(self, inner_statement: Expression, parens: tuple[Token, Token]) -> None:
25
+ self.inner_statement = inner_statement
26
+ self.parens = parens
27
+
28
+ def pprint(self) -> str:
29
+ return f"""
30
+ Parentheses (
31
+ {self.inner_statement}
32
+ )""".strip()
33
+
34
+ @classmethod
35
+ @lexer_reset
36
+ def match(cls, parser: "Parser") -> "ParenthesesExpression | None":
37
+ from . import any_expression_match # noqa: PLC0415
38
+
39
+ if not cls.match_tokens(parser, [TokenType.LEFT_PAREN]):
40
+ return None
41
+
42
+ left_paren = parser.consume()
43
+ value = any_expression_match(parser)
44
+ if value is None:
45
+ msg = "ParenthesesExpression.match called without valid inner expression"
46
+ raise ValueError(msg)
47
+ right_paren = parser.consume()
48
+ assert right_paren.tok_type == TokenType.RIGHT_PAREN # Consume the right parenthesis
49
+ return ParenthesesExpression(inner_statement=value, parens=(left_paren, right_paren))
50
+
51
+ def children(self) -> list[Expression]:
52
+ """Returns a list of child expressions."""
53
+ return [self.inner_statement]
54
+
55
+ def position(self) -> tuple[int, int]:
56
+ """Returns the position of the expression."""
57
+ return self.parens[0].text_slice.start, self.parens[1].text_slice.end
58
+
59
+ def full_text(self) -> str:
60
+ """Returns the full text of the expression."""
61
+ return self.parens[0].text_slice.full_text
@@ -0,0 +1,76 @@
1
+ import textwrap
2
+ from typing import TYPE_CHECKING
3
+
4
+ from pbi_parsers.dax.tokens import TokenType
5
+
6
+ from ._base import Expression
7
+ from ._utils import lexer_reset
8
+ from .variable import VariableExpression
9
+
10
+ if TYPE_CHECKING:
11
+ from pbi_parsers.dax.parser import Parser
12
+
13
+
14
+ class ReturnExpression(Expression):
15
+ """Represents a RETURN statement in DAX.
16
+
17
+ Examples:
18
+ VAR x = 1
19
+ RETURN x + 2
20
+
21
+ VAR x = 1
22
+ RETURN x
23
+
24
+ """
25
+
26
+ ret: Expression
27
+ variable_statements: list[VariableExpression]
28
+
29
+ def __init__(self, ret: Expression, variable_statements: list[VariableExpression]) -> None:
30
+ self.ret = ret
31
+ self.variable_statements = variable_statements
32
+
33
+ def pprint(self) -> str:
34
+ return_val = textwrap.indent(self.ret.pprint(), " " * 12).lstrip()
35
+ statements = textwrap.indent(
36
+ ",\n".join(stmt.pprint() for stmt in self.variable_statements),
37
+ " " * 16,
38
+ ).lstrip()
39
+ return f"""
40
+ Return (
41
+ Return: {return_val},
42
+ Statements: {statements}
43
+ )""".strip()
44
+
45
+ @classmethod
46
+ @lexer_reset
47
+ def match(cls, parser: "Parser") -> "ReturnExpression | None":
48
+ from . import any_expression_match # noqa: PLC0415
49
+
50
+ if not cls.match_tokens(parser, [TokenType.VARIABLE]):
51
+ return None
52
+
53
+ statements: list[VariableExpression] = []
54
+ while not cls.match_tokens(parser, [TokenType.RETURN]):
55
+ statement = VariableExpression.match(parser)
56
+ if statement is None:
57
+ msg = "ReturnExpression.match called without valid inner expression"
58
+ raise ValueError(msg)
59
+ statements.append(statement)
60
+
61
+ assert parser.consume().tok_type == TokenType.RETURN # Consume the return token
62
+ ret = any_expression_match(parser)
63
+ if ret is None:
64
+ msg = "ReturnExpression.match called without valid return expression"
65
+ raise ValueError(msg)
66
+ return ReturnExpression(ret=ret, variable_statements=statements)
67
+
68
+ def children(self) -> list[Expression]:
69
+ """Returns a list of child expressions."""
70
+ return [self.ret, *self.variable_statements]
71
+
72
+ def position(self) -> tuple[int, int]:
73
+ return self.variable_statements[0].position()[0], self.ret.position()[1]
74
+
75
+ def full_text(self) -> str:
76
+ return self.ret.full_text()
@@ -0,0 +1,51 @@
1
+ from typing import TYPE_CHECKING
2
+
3
+ from pbi_parsers.dax.tokens import Token, TokenType
4
+
5
+ from ._base import Expression
6
+ from ._utils import lexer_reset
7
+
8
+ if TYPE_CHECKING:
9
+ from pbi_parsers.dax.parser import Parser
10
+
11
+
12
+ class TableExpression(Expression):
13
+ """Represents a table in DAX.
14
+
15
+ Examples:
16
+ TableName
17
+ 'Table Name'
18
+
19
+ """
20
+
21
+ name: Token
22
+
23
+ def __init__(self, name: Token) -> None:
24
+ self.name = name
25
+
26
+ def pprint(self) -> str:
27
+ return f"""
28
+ Table (
29
+ {self.name.text}
30
+ )""".strip()
31
+
32
+ @classmethod
33
+ @lexer_reset
34
+ def match(cls, parser: "Parser") -> "TableExpression | None":
35
+ name = parser.consume()
36
+ if name.tok_type not in {
37
+ TokenType.SINGLE_QUOTED_IDENTIFIER,
38
+ TokenType.UNQUOTED_IDENTIFIER,
39
+ }:
40
+ return None
41
+ return TableExpression(name=name)
42
+
43
+ def children(self) -> list[Expression]: # noqa: PLR6301
44
+ """Returns a list of child expressions."""
45
+ return []
46
+
47
+ def position(self) -> tuple[int, int]:
48
+ return self.name.text_slice.start, self.name.text_slice.end
49
+
50
+ def full_text(self) -> str:
51
+ return self.name.text_slice.full_text
@@ -0,0 +1,68 @@
1
+ import textwrap
2
+ from typing import TYPE_CHECKING
3
+
4
+ from pbi_parsers.dax.tokens import Token, TokenType
5
+
6
+ from ._base import Expression
7
+ from ._utils import lexer_reset
8
+
9
+ if TYPE_CHECKING:
10
+ from pbi_parsers.dax.parser import Parser
11
+
12
+
13
+ class VariableExpression(Expression):
14
+ """Represents a variable assignment in DAX.
15
+
16
+ Examples:
17
+ VAR x = 1
18
+ VAR y = x + 2
19
+
20
+ VAR z = func()
21
+
22
+ """
23
+
24
+ var_name: Token
25
+ statement: Expression
26
+
27
+ def __init__(self, var_name: Token, statement: Expression) -> None:
28
+ self.var_name = var_name
29
+ self.statement = statement
30
+
31
+ def pprint(self) -> str:
32
+ statement = textwrap.indent(self.statement.pprint(), " " * 15).lstrip()
33
+ return f"""
34
+ Variable (
35
+ name: {self.var_name.text},
36
+ statement: {statement}
37
+ )
38
+ """.strip()
39
+
40
+ @classmethod
41
+ @lexer_reset
42
+ def match(cls, parser: "Parser") -> "VariableExpression | None":
43
+ from . import any_expression_match # noqa: PLC0415
44
+
45
+ if not cls.match_tokens(
46
+ parser,
47
+ [TokenType.VARIABLE, TokenType.UNQUOTED_IDENTIFIER, TokenType.EQUAL_SIGN],
48
+ ):
49
+ return None
50
+
51
+ parser.consume()
52
+ var_name = parser.consume()
53
+ parser.consume()
54
+ statement = any_expression_match(parser)
55
+ if statement is None:
56
+ msg = "VariableExpression.match called without valid inner expression"
57
+ raise ValueError(msg)
58
+ return VariableExpression(var_name=var_name, statement=statement)
59
+
60
+ def children(self) -> list[Expression]:
61
+ """Returns a list of child expressions."""
62
+ return [self.statement]
63
+
64
+ def position(self) -> tuple[int, int]:
65
+ return self.var_name.text_slice.start, self.statement.position()[1]
66
+
67
+ def full_text(self) -> str:
68
+ return self.var_name.text_slice.full_text