Skip to content

Commit e4c0060

Browse files
Andries-Smitclaude
andcommitted
docs(lint): document xpath_expressions() and parse_xpath() builtins in skill
Adds to write-lint-rules.md: - xpath_expressions() in the query functions table - parse_xpath(s) in the helper functions table - xpath_expression object (13 fields) - expr AST node schema (14 kind variants + type-specific fields) - Leaf-kind list and tree-walking example Closes the skill-docs gap flagged in PR #688 review. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R1yYeYn9rosf8UdAimdatt
1 parent b863d5b commit e4c0060

1 file changed

Lines changed: 72 additions & 0 deletions

File tree

.claude/skills/mendix/write-lint-rules.md

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ def check():
4141
| `module_roles()` | list of module_role | All module roles (deduplicated from role mappings) |
4242
| `role_mappings()` | list of role_mapping | User role to module role assignments |
4343
| `project_security()` | project_security or None | Project-level security settings (requires MPR reader) |
44+
| `xpath_expressions()` | list of xpath_expression | All XPath constraint expressions in the catalog (access rules, retrieve actions, widgets) |
4445

4546
### Graph-analysis functions (architecture rules)
4647

@@ -193,6 +194,76 @@ def check():
193194
| `interval_seconds` | int | `86400``0` for unrecognised interval type |
194195
| `enabled` | bool | `True` if the event is active |
195196

197+
### xpath_expression
198+
199+
Returned by `xpath_expressions()`. Each row represents one XPath constraint used in a retrieve action, access rule, or widget data source.
200+
201+
| Property | Type | Example |
202+
|----------|------|---------|
203+
| `id` | string | Row UUID |
204+
| `document_type` | string | `"MICROFLOW"`, `"NANOFLOW"`, `"DOMAIN_MODEL"`, `"PAGE"`, `"SNIPPET"` |
205+
| `document_id` | string | Owning document UUID |
206+
| `document_qualified_name` | string | `"MyApp.GetActiveItems"` |
207+
| `component_type` | string | `"RETRIEVE_ACTION"`, `"ACCESS_RULE"`, `"WIDGET"` |
208+
| `component_id` | string | Component UUID |
209+
| `component_name` | string | Activity/rule name (may be empty) |
210+
| `xpath_expression` | string | Raw XPath string, may include outer `[ ]` |
211+
| `target_entity` | string | Qualified name of entity being queried, e.g. `"MyApp.Order"` |
212+
| `referenced_entities` | string | Comma-separated qualified names of entities referenced by the XPath |
213+
| `is_parameterized` | bool | True when the XPath contains `$variable` references |
214+
| `usage_type` | string | `"RETRIEVE"`, `"SECURITY"`, `"DATASOURCE"` |
215+
| `module_name` | string | `"MyApp"` |
216+
217+
### expr
218+
219+
Returned by `parse_xpath(s)`. Every node has a `kind` field; additional fields depend on the kind.
220+
221+
| `kind` | Additional fields | Description |
222+
|--------|-------------------|-------------|
223+
| `"bin"` | `op` (string), `left` (expr), `right` (expr) | Binary operator: `=`, `!=`, `<`, `>`, `<=`, `>=`, `and`, `or` |
224+
| `"unary"` | `op` (string), `operand` (expr) | Unary operator: `not`, `-` |
225+
| `"call"` | `name` (string), `args` (list of expr) | Function call, e.g. `contains(…)`, `length(…)` |
226+
| `"string"` | `value` (string) | String literal |
227+
| `"number"` | `value` (string) | Numeric literal (kept as string to preserve precision) |
228+
| `"bool"` | `value` (bool) | `true` or `false` |
229+
| `"empty"` || Mendix `empty` keyword |
230+
| `"variable"` | `name` (string) | `$ParameterName` |
231+
| `"attr_path"` | `variable` (string), `path` (list of string) | `$Obj/Association/Attribute` |
232+
| `"qname"` | `module` (string), `name` (string), `sub` (string) | Qualified name, e.g. `MyApp.Status.Active` |
233+
| `"paren"` | `inner` (expr) | Parenthesised expression |
234+
| `"if"` | `cond` (expr), `then` (expr), `else_` (expr) | If-then-else expression |
235+
| `"constant"` | `qname` (string) | Mendix constant reference, e.g. `[%MyConst%]` |
236+
| `"token"` | `token` (string), `arg` (string) | Mendix token expression, e.g. `[%CurrentUser%]` |
237+
| `"recovered"` | `source` (string), `reason` (string) | Parse failure — node carries the raw source fragment |
238+
| `"null"` || Nil / missing node |
239+
| `"unknown"` || Unrecognised AST node type |
240+
241+
**Walking an expr tree:** check `node.kind` and recurse into child fields. Leaf kinds (no child nodes) are: `string`, `number`, `bool`, `empty`, `variable`, `qname`, `constant`, `token`, `recovered`, `null`, `unknown`.
242+
243+
Example — count `not(…)` calls in an XPath (using `parse_xpath`):
244+
245+
```python
246+
def count_not(node):
247+
if node.kind in ("null", "unknown", "recovered", "string", "number",
248+
"bool", "empty", "variable", "qname", "constant", "token"):
249+
return 0
250+
if node.kind == "call" and node.name == "not":
251+
return 1 + sum([count_not(a) for a in node.args])
252+
if node.kind == "call":
253+
return sum([count_not(a) for a in node.args])
254+
if node.kind == "bin":
255+
return count_not(node.left) + count_not(node.right)
256+
if node.kind == "unary":
257+
return count_not(node.operand)
258+
if node.kind == "paren":
259+
return count_not(node.inner)
260+
if node.kind == "if":
261+
return count_not(node.cond) + count_not(node.then) + count_not(node.else_)
262+
if node.kind == "attr_path":
263+
return 0
264+
return 0
265+
```
266+
196267
### attribute
197268
| Property | Type | Example |
198269
|----------|------|---------|
@@ -298,6 +369,7 @@ Returned by `project_security()`. Returns `none` if no MPR reader is available.
298369
|----------|-------------|
299370
| `violation(message, location?, suggestion?)` | Create a violation to return |
300371
| `location(module, document_type, document_name, document_id?)` | Create a location for a violation |
372+
| `parse_xpath(s)` | Parse a raw XPath/expression string and return its AST as an `expr` struct tree. Outer `[ ]` are stripped automatically. Parse failures produce a `recovered` root node rather than raising. |
301373
| `is_pascal_case(s)` | Returns True if string is PascalCase |
302374
| `is_camel_case(s)` | Returns True if string is camelCase |
303375
| `matches(s, pattern)` | Returns True if string matches regex |

0 commit comments

Comments
 (0)