Skip to content

Commit 0c1dd36

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 ce23535 commit 0c1dd36

File tree

15 files changed

+856
-0
lines changed

15 files changed

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

0 commit comments

Comments
 (0)