Files
regorus/tests/rvm/rego/cases/destructuring.yaml
Anand Krishnamoorthi a3a20a1235 feat!: Rego -> RVM Compiler and extensive testsuite (#506)
# RVM compiler test cases

Coverage:
- arithmetic
- arrays
- chained lookups
- comparisons
- comprehensions
- default rules
- destructuring
- function rules
- loops/quantifiers
- multiple entrypoints
- objects/sets
- variables
- negative/edge scenarios such as data/rule conflicts
- virtual data lookups
- etc

 # Modify interpreter and compiled policy for RVM Compilation

- Interpreter::eval_default_rule_for_compiler:
   evaluates a named default rule in isolation - allows compiler to emit a constant value instead of instructions
   for the default value

#  feat: Rego Compiler Scaffolding

- Introduce the rego::compiler module surface and entry point wiring
- Add the core compiler concepts:
  - register allocator
  - scope tracking
  - literal/builtin tables
  - rule worklists
  - instruction emit helpers
  - compiler-specific error types
  - context structs for rules, comprehensions, and loops to support later lowering passes.

# feat: Compile Rules/Queries

- add compiler::compile_from_policy workflow plus rule worklist, entry-point wiring, and recursion checks
- implement query lowering:
  - scheduling-aware statement ordering
  - loop hoisting
  - “every/some” semantics
  - context yields
  -  literal assertions
- finalize Program construction

# feat: Expression Lowering

- add compile_rego_expr and helpers to translate every AST expression into RVM instructions,
- interop with binding plans, comprehensions, and membership checks.
- implement collection literal builders (ArrayCreate, SetCreate, ObjectCreate)
  - dedupe literal keys and handle mixed literal/dynamic fields via instruction data blocks.
- operations:
  - arithmetic/boolean/bin operators
  - membership
  - unary minus
  - set unions/intersections
  - etc
- user-defined and builtin function calls
- reference handling
  - analyse chained refs
  - distinguishe data/input/local roots
  - perform rule dispatch or virtual document lookups
  - emits optimized Index/ChainedIndex instructions.

# feat: Comprehensions & Loops

- shared comprehension emitter
 - wraps array/set/object comprehensions with ComprehensionBegin/End
 - context management
- loop lowering utilities
 - read hoisting metadata
 - emit LoopStart/LoopNext
 - some in lowering
 - every quantifiers
 - index iteration
 - propagate binding plans into stored registers so downstream statements see bound variables.

# feat: Destructuring Lowering

- destructuring planner integration
 - assignment/parameter/loop bindings use hoisted plans instead of re-walking ASTs.
- handle :=, =, wildcard matches, and equality
 - evaluate RHS
 - applying destructuring plans
 - emit assert condition as needed
- support nested array/object destructuring, dynamic keys, and some ... in forms

# test: Shared Testing + RVM Suites

- move YAML test helpers into test_utils.rs and re-export via common.rs for use by interpreter and vm test suites
- comprehensive compiler test suite
  - compiles policies with the new Rego→RVM compiler
  - runs them through RegoVM
  - compares against interpreter behavior
  - supports multiple entry points
  - provides assembly listings
  - filterable YAML suites.

Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
2025-11-24 12:08:37 -06:00

260 lines
7.7 KiB
YAML

# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
# Destructuring Pattern Test Suite
# Tests destructuring patterns in assignments, function parameters, and some-in loops
# Note: Set destructuring is not supported by Rego and should produce compilation errors
cases:
# Basic array destructuring with colon assignment
- note: array_destructuring_basic
data: {}
modules:
- |
package test
main := [a, b] if {
[a, b] := [1, 2]
}
query: data.test.main
want_result: [1, 2]
# Array destructuring with equals assignment
- note: array_destructuring_equals
data: {}
modules:
- |
package test
main := [x, y, z] if {
arr := [10, 20, 30]
[x, y, z] = arr
}
query: data.test.main
want_result: [10, 20, 30]
# Object destructuring with colon assignment
- note: object_destructuring_basic
data: {}
modules:
- |
package test
main := [name, age] if {
{"name": name, "age": age} := {"name": "Alice", "age": 30}
}
query: data.test.main
want_result: ["Alice", 30]
# Object destructuring with equals assignment
- note: object_destructuring_equals
data: {}
modules:
- |
package test
main := [x, y] if {
obj := {"x": 100, "y": 200}
{"x": x, "y": y} = obj
}
query: data.test.main
want_result: [100, 200]
# Nested array destructuring
- note: nested_array_destructuring
data: {}
modules:
- |
package test
main := [a, c, d] if {
[[a, b], [c, d]] := [[1, 2], [3, 4]]
}
query: data.test.main
want_result: [1, 3, 4]
# Array destructuring in function parameters
- note: array_destructuring_function_param
data: {}
modules:
- |
package test
add_first_two([x, y]) := x + y
main := add_first_two([5, 7])
query: data.test.main
want_result: 12
# Object destructuring in function parameters
- note: object_destructuring_function_param
data: {}
modules:
- |
package test
get_name({"name": name}) := name
main := get_name({"name": "Bob", "age": 25})
query: data.test.main
want_result: "Bob"
# Mixed array and object destructuring
- note: mixed_destructuring
data: {}
modules:
- |
package test
main := [name, x, y] if {
[user, {"x": x, "y": y}] := [{"name": "Grace"}, {"x": 1, "y": 2}]
{"name": name} = user
}
query: data.test.main
want_result: ["Grace", 1, 2]
# Destructuring with literal matching
- note: destructuring_with_literals
data: {}
modules:
- |
package test
main := value if {
[1, value, 3] := [1, 42, 3]
}
query: data.test.main
want_result: 42
# SET DESTRUCTURING ERROR CASES - These should fail compilation
# RVM correctly rejects these, but interpreter incorrectly allows them
# Set destructuring in colon assignment should error
- note: set_destructuring_colon_error
data: {}
modules:
- |
package test
main := result if {
{a, b} := {1, 2, 3}
result := [a, b]
}
query: data.test.main
want_error: "assignment operator := requires left-hand side to have bindable variables"
allow_interpreter_success: true
# Set destructuring in function parameters should error
- note: set_destructuring_function_param_error
data: {}
modules:
- |
package test
has_element({x, y}, elem) := elem in {x, y}
main := has_element({10, 20}, 20)
query: data.test.main
want_error: "Undefined variable"
allow_interpreter_success: true
# Set destructuring in equals assignment should error
- note: set_destructuring_equals_error
data: {}
modules:
- |
package test
main := result if {
s := {1, 2}
{x, y} = s
result := [x, y]
}
query: data.test.main
want_error: "Undefined variable"
allow_interpreter_success: true
# Option 2: Function parameter destructuring with multiple definitions and definition-level failure
- note: function_param_destructuring_multiple_definitions
data: {}
modules:
- |
package test
# This function has multiple definitions with different parameter patterns
# Only the matching definition should succeed, others should fail at definition level
process_input([x, y]) := sprintf("array: %v, %v", [x, y])
process_input([x, y, z]) := sprintf("array: %v, %v, %v", [x, y, z])
process_input({"name": name, "age": age}) := sprintf("object: %s is %d", [name, age])
# Test with 2-element array - should match first definition
test_2_elements := process_input([1, 2])
# Test with 3-element array - should match second definition
test_3_elements := process_input([1, 2, 3])
# Test with object - should match third definition
test_object := process_input({"name": "Alice", "age": 30})
# Combined result for testing
main := {
"test_2_elements": test_2_elements,
"test_3_elements": test_3_elements,
"test_object": test_object
}
query: data.test.main
want_result:
test_2_elements: "array: 1, 2"
test_3_elements: "array: 1, 2, 3"
test_object: "object: Alice is 30"
# Option 2: Complex nested destructuring in function parameters
- note: function_param_nested_destructuring
data: {}
modules:
- |
package test
# Function with nested destructuring patterns
extract_info({"user": {"name": name, "details": {"age": age, "city": city}}, "active": active}) := {
"user_name": name,
"user_age": age,
"user_city": city,
"is_active": active
}
main := extract_info({
"user": {
"name": "Bob",
"details": {
"age": 25,
"city": "Seattle"
}
},
"active": true
})
query: data.test.main
want_result:
user_name: "Bob"
user_age: 25
user_city: "Seattle"
is_active: true
# Option 2: Mixed destructuring and non-destructuring definitions
- note: function_mixed_destructuring_and_simple
data: {}
modules:
- |
package test
# Function with mixed parameter styles - some with destructuring, some without
handle_request(method) := sprintf("simple method: %s", [method]) if {
method in ["GET", "POST", "PUT", "DELETE"]
}
handle_request({"method": method, "path": path}) := sprintf("structured request: %s %s", [method, path])
handle_request({"method": method, "headers": {"auth": token}}) := sprintf("authenticated %s with token %s", [method, token])
# Test simple string parameter - should match first definition
test_simple := handle_request("GET")
# Test structured request - should match second definition
test_structured := handle_request({"method": "POST", "path": "/users"})
# Test with auth header - should match third definition
test_auth := handle_request({"method": "PUT", "headers": {"auth": "abc123"}})
# Test that fails all patterns - this should be undefined
test_invalid := handle_request(42)
# Combined result for testing
main := {
"test_simple": test_simple,
"test_structured": test_structured,
"test_auth": test_auth,
"test_invalid": test_invalid
}
query: data.test.main
want_result: "#undefined"