Skip to content

Commit 6d193d1

Browse files
committed
feat(azure-policy): add compiler skeleton with core types and stubs
Add the compiler module structure with: - core.rs: Compiler struct, CountBinding, new(), compile() pipeline, register allocation, span/emit helpers - mod.rs: module declarations, public entry points (compile_policy_rule, compile_policy_definition, etc.) - utils.rs: pure helper functions (path splitting, JSON conversion) - Stub files for conditions, expressions, fields, template dispatch, count, effects, and metadata — real implementations follow in subsequent commits.
1 parent 3d34021 commit 6d193d1

File tree

15 files changed

+803
-0
lines changed

15 files changed

+803
-0
lines changed
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
#![allow(clippy::pattern_type_mismatch, clippy::unused_self)]
4+
5+
//! Constraint / condition / LHS compilation.
6+
//!
7+
//! Stub — real implementation added in a later commit.
8+
9+
use anyhow::{bail, Result};
10+
11+
use crate::languages::azure_policy::ast::Constraint;
12+
13+
use super::core::Compiler;
14+
15+
impl Compiler {
16+
pub(super) fn compile_constraint(&mut self, _constraint: &Constraint) -> Result<u8> {
17+
bail!("condition compilation not yet implemented")
18+
}
19+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
//! Implicit allOf for unbound `[*]` wildcard fields.
5+
//!
6+
//! Stub — real implementation added in a later commit.
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
#![allow(dead_code)]
4+
#![allow(
5+
clippy::arithmetic_side_effects,
6+
clippy::as_conversions,
7+
clippy::indexing_slicing,
8+
clippy::pattern_type_mismatch
9+
)]
10+
11+
//! Core `Compiler` struct, main compilation pipeline, and register/emit
12+
//! infrastructure.
13+
14+
use alloc::collections::{BTreeMap, BTreeSet};
15+
use alloc::string::{String, ToString as _};
16+
use alloc::vec::Vec;
17+
18+
use anyhow::{anyhow, bail, Result};
19+
20+
use crate::rvm::program::{Program, SpanInfo};
21+
use crate::rvm::Instruction;
22+
use crate::{Rc, Value};
23+
24+
use crate::languages::azure_policy::ast::PolicyRule;
25+
26+
// ---------------------------------------------------------------------------
27+
// Types
28+
// ---------------------------------------------------------------------------
29+
30+
#[derive(Debug, Clone)]
31+
pub(super) struct CountBinding {
32+
pub(super) name: Option<String>,
33+
pub(super) field_wildcard_prefix: Option<String>,
34+
pub(super) current_reg: u8,
35+
}
36+
37+
#[derive(Debug, Default)]
38+
pub(super) struct Compiler {
39+
pub(super) program: Program,
40+
pub(super) register_counter: u8,
41+
/// High-water mark of `register_counter`.
42+
pub(super) register_high_water: u8,
43+
pub(super) source_to_index: BTreeMap<String, usize>,
44+
pub(super) builtin_index: BTreeMap<String, u16>,
45+
pub(super) count_bindings: Vec<CountBinding>,
46+
/// Cached register for `LoadInput` — allocated once on first use.
47+
pub(super) cached_input_reg: Option<u8>,
48+
/// Cached register for `LoadContext` — allocated once on first use.
49+
pub(super) cached_context_reg: Option<u8>,
50+
/// Map from lowercase fully-qualified alias name → short name.
51+
pub(super) alias_map: BTreeMap<String, String>,
52+
/// Map from lowercase fully-qualified alias name → modifiable flag.
53+
pub(super) alias_modifiable: BTreeMap<String, bool>,
54+
/// Default values for policy parameters.
55+
pub(super) parameter_defaults: Option<Value>,
56+
/// When set, field conditions resolve against this register instead of
57+
/// `input.resource`. Used for `existenceCondition`.
58+
pub(super) resource_override_reg: Option<u8>,
59+
60+
// -- Metadata accumulators ---------------------------------------------
61+
pub(super) observed_field_kinds: BTreeSet<String>,
62+
pub(super) observed_aliases: BTreeSet<String>,
63+
pub(super) observed_tag_names: BTreeSet<String>,
64+
pub(super) observed_operators: BTreeSet<String>,
65+
pub(super) observed_resource_types: BTreeSet<String>,
66+
pub(super) observed_uses_count: bool,
67+
pub(super) observed_has_dynamic_fields: bool,
68+
pub(super) observed_has_wildcard_aliases: bool,
69+
70+
/// When `true`, unknown aliases are silently treated as raw property paths.
71+
pub(super) alias_fallback_to_raw: bool,
72+
}
73+
74+
// ---------------------------------------------------------------------------
75+
// Core infrastructure
76+
// ---------------------------------------------------------------------------
77+
78+
impl Compiler {
79+
pub(super) fn new() -> Self {
80+
Self {
81+
register_counter: 0,
82+
..Self::default()
83+
}
84+
}
85+
86+
pub(super) fn compile(mut self, rule: &PolicyRule) -> Result<Rc<Program>> {
87+
let cond_reg = self.compile_constraint(&rule.condition)?;
88+
self.emit(
89+
Instruction::ReturnUndefinedIfNotTrue {
90+
condition: cond_reg,
91+
},
92+
&rule.span,
93+
);
94+
95+
let effect_reg = self.compile_effect(rule)?;
96+
self.emit(
97+
Instruction::Return { value: effect_reg },
98+
&rule.then_block.span,
99+
);
100+
101+
self.program.main_entry_point = 0;
102+
self.program.entry_points.insert("main".to_string(), 0);
103+
self.program.dispatch_window_size = self.register_high_water.max(2);
104+
self.program.max_rule_window_size = 0;
105+
106+
if !self.program.builtin_info_table.is_empty() {
107+
self.program.initialize_resolved_builtins()?;
108+
}
109+
110+
self.program
111+
.validate_limits()
112+
.map_err(|message| anyhow!(message))?;
113+
114+
self.populate_compiled_annotations();
115+
116+
Ok(Rc::new(self.program))
117+
}
118+
119+
// -- register / span / emit helpers ------------------------------------
120+
121+
/// Restore `register_counter` to `saved` while protecting cached registers.
122+
pub(super) fn restore_register_counter(&mut self, saved: u8) {
123+
let mut floor = saved;
124+
if let Some(r) = self.cached_input_reg {
125+
floor = floor.max(r.saturating_add(1));
126+
}
127+
if let Some(r) = self.cached_context_reg {
128+
floor = floor.max(r.saturating_add(1));
129+
}
130+
self.register_counter = floor;
131+
}
132+
133+
pub(super) fn alloc_register(&mut self) -> Result<u8> {
134+
if self.register_counter == u8::MAX {
135+
bail!("azure-policy compiler exhausted RVM registers");
136+
}
137+
let reg = self.register_counter;
138+
self.register_counter = self.register_counter.saturating_add(1);
139+
if self.register_counter > self.register_high_water {
140+
self.register_high_water = self.register_counter;
141+
}
142+
Ok(reg)
143+
}
144+
145+
pub(super) fn span_info(&mut self, span: &crate::lexer::Span) -> SpanInfo {
146+
let path = span.source.get_path().to_string();
147+
let source_index = if let Some(index) = self.source_to_index.get(path.as_str()) {
148+
*index
149+
} else {
150+
let index = self
151+
.program
152+
.add_source(path.clone(), span.source.get_contents().to_string());
153+
self.source_to_index.insert(path, index);
154+
index
155+
};
156+
157+
SpanInfo::from_lexer_span(span, source_index)
158+
}
159+
160+
pub(super) fn emit(&mut self, instruction: Instruction, span: &crate::lexer::Span) {
161+
let span_info = self.span_info(span);
162+
self.program.add_instruction(instruction, Some(span_info));
163+
}
164+
165+
/// Return the PC (instruction index) that the *next* emitted instruction
166+
/// will occupy.
167+
pub(super) fn current_pc(&self) -> Result<u16> {
168+
u16::try_from(self.program.instructions.len())
169+
.map_err(|_| anyhow!("instruction index overflow"))
170+
}
171+
172+
/// Patch tracked instruction indices, setting their `end_pc` field.
173+
pub(super) fn patch_end_pc(&mut self, pcs: &[u16], end_pc: u16) {
174+
for &pc in pcs {
175+
match &mut self.program.instructions[pc as usize] {
176+
Instruction::LogicalBlockStart {
177+
end_pc: ref mut ep, ..
178+
}
179+
| Instruction::AllOfNext {
180+
end_pc: ref mut ep, ..
181+
}
182+
| Instruction::AnyOfNext {
183+
end_pc: ref mut ep, ..
184+
} => {
185+
*ep = end_pc;
186+
}
187+
_ => {}
188+
}
189+
}
190+
}
191+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
#![allow(dead_code, clippy::unused_self, clippy::missing_const_for_fn)]
4+
5+
//! `count` / `count.where` loop compilation.
6+
//!
7+
//! Stub — real implementation added in a later commit.
8+
9+
use anyhow::{bail, Result};
10+
11+
use crate::languages::azure_policy::ast::{Condition, CountNode};
12+
13+
use super::core::Compiler;
14+
15+
impl Compiler {
16+
pub(super) fn compile_count(&mut self, _count_node: &CountNode) -> Result<u8> {
17+
bail!("count compilation not yet implemented")
18+
}
19+
20+
pub(super) fn try_compile_count_as_any(
21+
&mut self,
22+
_count_node: &CountNode,
23+
_condition: &Condition,
24+
) -> Result<Option<u8>> {
25+
Ok(None)
26+
}
27+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
//! Existence-pattern optimization (count → Any loop).
5+
//!
6+
//! Stub — real implementation added in a later commit.
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
#![allow(dead_code, clippy::unused_self, clippy::missing_const_for_fn)]
4+
5+
//! Count-binding resolution and `current()` references.
6+
//!
7+
//! Stub — real implementation added in a later commit.
8+
9+
use anyhow::{bail, Result};
10+
11+
use super::core::{Compiler, CountBinding};
12+
13+
impl Compiler {
14+
pub(super) fn resolve_count_binding(&self, _field_path: &str) -> Result<Option<CountBinding>> {
15+
Ok(None)
16+
}
17+
18+
pub(super) fn compile_from_binding(
19+
&mut self,
20+
_binding: CountBinding,
21+
_field_path: &str,
22+
_span: &crate::lexer::Span,
23+
) -> Result<u8> {
24+
bail!("count binding compilation not yet implemented")
25+
}
26+
27+
pub(super) fn compile_current_reference(
28+
&mut self,
29+
_key: &str,
30+
_span: &crate::lexer::Span,
31+
) -> Result<u8> {
32+
bail!("current() reference not yet implemented")
33+
}
34+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
#![allow(dead_code, clippy::unused_self)]
4+
5+
//! Effect compilation (dispatch + cross-resource).
6+
//!
7+
//! Stub — real implementation added in a later commit.
8+
9+
use anyhow::{bail, Result};
10+
11+
use crate::languages::azure_policy::ast::PolicyRule;
12+
13+
use super::core::Compiler;
14+
15+
impl Compiler {
16+
pub(super) fn compile_effect(&mut self, _rule: &PolicyRule) -> Result<u8> {
17+
bail!("effect compilation not yet implemented")
18+
}
19+
20+
pub(super) fn wrap_effect_result(
21+
&mut self,
22+
_effect_name_reg: u8,
23+
_details_reg: Option<u8>,
24+
_span: &crate::lexer::Span,
25+
) -> Result<u8> {
26+
bail!("wrap_effect_result not yet implemented")
27+
}
28+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
//! Modify / Append effect detail compilation.
5+
//!
6+
//! Stub — real implementation added in a later commit.
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
#![allow(dead_code, clippy::unused_self)]
4+
5+
//! Template-expression and call-expression compilation.
6+
//!
7+
//! Stub — real implementation added in a later commit.
8+
9+
use anyhow::{bail, Result};
10+
11+
use crate::languages::azure_policy::ast::{Expr, JsonValue, ValueOrExpr};
12+
13+
use super::core::Compiler;
14+
15+
impl Compiler {
16+
pub(super) fn compile_value_or_expr(
17+
&mut self,
18+
_voe: &ValueOrExpr,
19+
_span: &crate::lexer::Span,
20+
) -> Result<u8> {
21+
bail!("expression compilation not yet implemented")
22+
}
23+
24+
pub(super) fn compile_json_value(
25+
&mut self,
26+
_value: &JsonValue,
27+
_span: &crate::lexer::Span,
28+
) -> Result<u8> {
29+
bail!("JSON value compilation not yet implemented")
30+
}
31+
32+
pub(super) fn compile_expr(&mut self, _expr: &Expr) -> Result<u8> {
33+
bail!("expression compilation not yet implemented")
34+
}
35+
36+
pub(super) fn compile_call_expr(
37+
&mut self,
38+
_span: &crate::lexer::Span,
39+
_func: &str,
40+
_args: &[Expr],
41+
) -> Result<u8> {
42+
bail!("call expression compilation not yet implemented")
43+
}
44+
45+
pub(super) fn compile_call_args(&mut self, _args: &[Expr]) -> Result<alloc::vec::Vec<u8>> {
46+
bail!("call args compilation not yet implemented")
47+
}
48+
}

0 commit comments

Comments
 (0)