Overview
Based on Crafting Interpreters. Library provides lexers, parsers, and formatters for DAX and Power Query (M) languages. Designed to support code introspection and analysis, not execution. This enables development of ruff-equivalent tools for DAX and Power Query. It also enables extracting metadata from DAX and Power Query code, such PQ source types (Excel, SQL, etc.) and DAX lineage dependencies.
This library is used in pbi_ruff to provide DAX and Power Query (M) linting.
Installation
Functionality
Rust Implementation
Although the library is primarily implemented in Python, there are plans to implement a Rust version for performance and additional type-safety.
- DAX
- Lexer
- Parser
- Formatter
- Testing
- Rust Implementation
- Power Query (M)
- Lexer
- Parser
- Formatter
- Testing
- Rust Implementation
Examples
Formatting DAX Expressions
Like ruff
for Python, this library can format DAX expressions to improve readability and maintainability.
Creating AST Trees from DAX Expressions
The library can parse DAX expressions into Abstract Syntax Trees (ASTs) for further analysis or manipulation.
from pbi_parsers.dax import to_ast
input_dax = """
func.name(arg1 + 1 + 2 + 3, func(), func(10000000000000), arg2)
"""
ast = to_ast(input_dax)
print(ast)
# Output:
# Function (
# name: func.name,
# args: Add (
# left: Identifier (arg1),
# right: Add (
# left: Number (1),
# right: Add (
# left: Number (2),
# right: Number (3)
# )
# )
# ),
# Function (
# name: func,
# args:
# ),
# Function (
# name: func,
# args: Number (10000000000000)
# ),
# Identifier (arg2)
# )
Highlighting DAX Sections
The library can highlight sections of DAX code, making it easier to identify and analyze specific parts of the code.
Note: in the console, the caret (^
) will be yellow and the line number will be cyan.
from pbi_parsers.dax import highlight_section, to_ast
input_dax = """
func.name(
arg1 +
1 +
2 + 3,
func(),
func(10000000000000),
arg2
)
"""
ast = to_ast(input_dax)
assert ast is not None, "AST should not be None"
section = ast.args[0].right.left # the "1" in "arg1 + 1 + 2 + 3"
highlighted = highlight_section(section)
print(highlighted.to_console())
# Output:
# 1 | func.name(
# 2 | arg1 +
# 3 | 1 +
# ^
# 4 | 2 + 3,
# 5 | func(),
Highlighting a larger section:
from pbi_parsers.dax import highlight_section, to_ast
input_dax = """
func.name(
arg1 +
1 +
2 + 3,
func(),
func(10000000000000),
arg2
)
"""
ast = to_ast(input_dax)
assert ast is not None, "AST should not be None"
section = ast.args[0].right # The "1 + 2" in "arg1 + 1 + 2 + 3"
highlighted = highlight_section(section)
print(highlighted.to_console())
# Output:
# 1 | func.name(
# 2 | arg1 +
# 3 | 1 +
# ^^^
# 4 | 2 + 3,
# ^^^^^^^^^^^^^^^^^
# 5 | func(),
# 6 | func(10000000000000),