// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. use alloc::format; use alloc::string::{String, ToString as _}; use alloc::vec::Vec; use core::fmt::{self, Write as _}; use crate::rvm::{ instructions::{Instruction, InstructionData, LoopMode}, program::Program, }; // Writing into a String via fmt never fails, so we intentionally ignore writeln! results. fn push_line(buf: &mut String, args: fmt::Arguments) { let _ = buf.write_fmt(args); let _ = buf.write_char('\n'); } /// Configuration for assembly listing output #[derive(Debug, Clone)] pub struct AssemblyListingConfig { /// Show instruction addresses pub show_addresses: bool, /// Show raw instruction bytes (if available) pub show_bytes: bool, /// Indent size for nested loops pub indent_size: usize, /// Maximum width for instruction column pub instruction_width: usize, /// Show literal values inline pub show_literal_values: bool, /// Column position for comments pub comment_column: usize, } impl Default for AssemblyListingConfig { fn default() -> Self { Self { show_addresses: true, show_bytes: false, indent_size: 4, instruction_width: 40, show_literal_values: true, comment_column: 50, } } } /// Generate annotated assembly listing for a compiled program pub fn generate_assembly_listing(program: &Program, config: &AssemblyListingConfig) -> String { let mut output = String::new(); let mut indent_level: usize = 0; let mut current_rule_index: Option = None; // Track active loops and comprehensions by their end addresses let mut active_ends: Vec = Vec::new(); // Add header push_line( &mut output, format_args!( "; RVM Assembly - {} instructions, {} literals, {} builtins", program.instructions.len(), program.literals.len(), program.builtin_info_table.len() ), ); // Add builtins table if !program.builtin_info_table.is_empty() { push_line(&mut output, format_args!(";")); push_line(&mut output, format_args!("; BUILTINS TABLE:")); for (idx, builtin_info) in program.builtin_info_table.iter().enumerate() { push_line( &mut output, format_args!("; B{:2}: {}", idx, builtin_info.name), ); } } // Add literals table if config.show_literal_values && !program.literals.is_empty() { push_line(&mut output, format_args!(";")); push_line(&mut output, format_args!("; LITERALS (JSON values):")); for (idx, literal) in program.literals.iter().enumerate() { let literal_json = serde_json::to_string(literal).unwrap_or_else(|_| "".to_string()); push_line( &mut output, format_args!("; L{:2}: {}", idx, literal_json), ); } } // Add rules table if available if !program.rule_infos.is_empty() { push_line(&mut output, format_args!(";")); push_line(&mut output, format_args!("; RULES TABLE:")); for (idx, rule_info) in program.rule_infos.iter().enumerate() { push_line( &mut output, format_args!("; R{:2}: {}", idx, rule_info.name), ); } } // Add metadata section { push_line(&mut output, format_args!(";")); push_line(&mut output, format_args!("; METADATA:")); push_line( &mut output, format_args!( "; compiler_version: {}", program.metadata.compiler_version ), ); push_line( &mut output, format_args!("; compiled_at: {}", program.metadata.compiled_at), ); if !program.metadata.source_info.is_empty() { push_line( &mut output, format_args!("; source_info: {}", program.metadata.source_info), ); } push_line( &mut output, format_args!( "; optimization_level: {}", program.metadata.optimization_level ), ); if !program.metadata.language.is_empty() { push_line( &mut output, format_args!("; language: {}", program.metadata.language), ); } if !program.metadata.annotations.is_empty() { push_line(&mut output, format_args!("; annotations:")); for (key, value) in &program.metadata.annotations { let json = serde_json::to_string(value).unwrap_or_else(|_| "".to_string()); push_line(&mut output, format_args!("; {}: {}", key, json)); } } } push_line(&mut output, format_args!(";")); for (pc, instruction) in program.instructions.iter().enumerate() { // Handle rule transitions and add gaps if let &Instruction::RuleInit { rule_index, .. } = instruction { // Add gap before new rule (except for the first rule) if current_rule_index.is_some() { push_line(&mut output, format_args!("")); } current_rule_index = Some(rule_index); // Add rule name prefix if let Some(rule_info) = program.rule_infos.get(usize::from(rule_index)) { push_line( &mut output, format_args!("; ===== RULE: {} =====", rule_info.name), ); } else { push_line( &mut output, format_args!("; ===== RULE: rule_{} =====", rule_index), ); } } // Check if current PC matches any active end addresses (loops, comprehensions, rules) let current_pc = u16::try_from(pc).unwrap_or(u16::MAX); while let Some(&end_addr) = active_ends.last() { if current_pc >= end_addr { active_ends.pop(); indent_level = indent_level.saturating_sub(1); } else { break; } } // Handle explicit end instructions match *instruction { Instruction::LoopNext { .. } => { // LoopNext already handled by end address tracking above } Instruction::RuleReturn { .. } => { indent_level = indent_level.saturating_sub(1); } _ => {} } // Special case: Block end instructions should be indented at their block level (one level out) let effective_indent_level = match *instruction { Instruction::ComprehensionEnd {} => indent_level.saturating_sub(1), Instruction::LoopNext { .. } => indent_level.saturating_sub(1), _ => indent_level, }; let indent = " ".repeat(effective_indent_level.saturating_mul(config.indent_size)); // Format address let addr_str = if config.show_addresses { format!("{:03}: ", pc) } else { String::new() }; // Format instruction with proper indentation and aligned comments let inst_str = format_instruction_readable( instruction, &indent, &program.instruction_data, program, config, ); push_line(&mut output, format_args!("{}{}", addr_str, inst_str)); // Increase indentation for loop/rule/comprehension starts and track their end addresses match *instruction { Instruction::LoopStart { params_index } => { if let Some(params) = program.instruction_data.get_loop_params(params_index) { active_ends.push(params.loop_end); indent_level = indent_level.saturating_add(1); } } Instruction::ComprehensionBegin { params_index } => { if let Some(params) = program .instruction_data .get_comprehension_begin_params(params_index) { active_ends.push(params.comprehension_end); indent_level = indent_level.saturating_add(1); } } Instruction::RuleInit { .. } => { indent_level = indent_level.saturating_add(1); // Note: Rules end with RuleReturn, not an address, so we don't track them here } _ => {} } } output } /// Helper function to align comments at a specific column fn align_comment(base_text: &str, comment: &str, target_column: usize) -> String { let current_len = base_text.len(); if current_len >= target_column { format!("{} ; {}", base_text, comment) } else { let padding = " ".repeat(target_column.saturating_sub(current_len)); format!("{}{} ; {}", base_text, padding, comment) } } /// Format a single instruction with proper indentation and mathematical notation fn format_instruction_readable( instruction: &Instruction, indent: &str, instruction_data: &InstructionData, program: &Program, config: &AssemblyListingConfig, ) -> String { match *instruction { Instruction::Load { dest, literal_idx } => { let base = format!("{}Load r{} ← L{}", indent, dest, literal_idx); let comment = program.literals.get(usize::from(literal_idx)).map_or_else( || "Load literal: ".to_string(), |literal| { let literal_json = serde_json::to_string(literal).unwrap_or_else(|_| "".to_string()); format!("Load literal: {}", literal_json) }, ); align_comment(&base, &comment, config.comment_column) } Instruction::LoadTrue { dest } => { let base = format!("{}LoadTrue r{} ← true", indent, dest); align_comment(&base, "Load boolean constant true", config.comment_column) } Instruction::LoadFalse { dest } => { let base = format!("{}LoadFalse r{} ← false", indent, dest); align_comment(&base, "Load boolean constant false", config.comment_column) } Instruction::LoadNull { dest } => { let base = format!("{}LoadNull r{} ← null", indent, dest); align_comment(&base, "Load null value", config.comment_column) } Instruction::LoadBool { dest, value } => { let base = format!("{}LoadBool r{} ← {}", indent, dest, value); let comment = format!("Load boolean constant {}", value); align_comment(&base, &comment, config.comment_column) } Instruction::LoadData { dest } => { let base = format!("{}LoadData r{} ← data", indent, dest); align_comment(&base, "Load global data document", config.comment_column) } Instruction::LoadInput { dest } => { let base = format!("{}LoadInput r{} ← input", indent, dest); align_comment(&base, "Load global input document", config.comment_column) } Instruction::LoadContext { dest } => { let base = format!("{}LoadContext r{} ← context", indent, dest); align_comment(&base, "Load evaluation context", config.comment_column) } Instruction::LoadMetadata { dest } => { let base = format!("{}LoadMetadata r{} ← metadata", indent, dest); align_comment(&base, "Load program metadata", config.comment_column) } Instruction::Move { dest, src } => { let base = format!("{}Move r{} ← r{}", indent, dest, src); let comment = format!("Copy value from r{} to r{}", src, dest); align_comment(&base, &comment, config.comment_column) } Instruction::Add { dest, left, right } => { let base = format!("{}Add r{} ← r{} + r{}", indent, dest, left, right); let comment = format!("Arithmetic addition: r{} + r{}", left, right); align_comment(&base, &comment, config.comment_column) } Instruction::Sub { dest, left, right } => { let base = format!("{}Sub r{} ← r{} - r{}", indent, dest, left, right); let comment = format!("Arithmetic subtraction: r{} - r{}", left, right); align_comment(&base, &comment, config.comment_column) } Instruction::Mul { dest, left, right } => { let base = format!("{}Mul r{} ← r{} × r{}", indent, dest, left, right); let comment = format!("Arithmetic multiplication: r{} × r{}", left, right); align_comment(&base, &comment, config.comment_column) } Instruction::Div { dest, left, right } => { let base = format!("{}Div r{} ← r{} ÷ r{}", indent, dest, left, right); let comment = format!("Arithmetic division: r{} ÷ r{}", left, right); align_comment(&base, &comment, config.comment_column) } Instruction::Mod { dest, left, right } => { let base = format!( "{}Mod r{} ← r{} mod r{}", indent, dest, left, right ); let comment = format!("Modulo operation: r{} mod r{}", left, right); align_comment(&base, &comment, config.comment_column) } Instruction::Eq { dest, left, right } => { let base = format!( "{}Eq r{} ← (r{} = r{})", indent, dest, left, right ); let comment = format!("Equality test: r{} == r{}", left, right); align_comment(&base, &comment, config.comment_column) } Instruction::Ne { dest, left, right } => { let base = format!( "{}Ne r{} ← (r{} ≠ r{})", indent, dest, left, right ); let comment = format!("Inequality test: r{} != r{}", left, right); align_comment(&base, &comment, config.comment_column) } Instruction::Lt { dest, left, right } => { let base = format!( "{}Lt r{} ← (r{} < r{})", indent, dest, left, right ); let comment = format!("Less than comparison: r{} < r{}", left, right); align_comment(&base, &comment, config.comment_column) } Instruction::Le { dest, left, right } => { let base = format!( "{}Le r{} ← (r{} ≤ r{})", indent, dest, left, right ); let comment = format!("Less or equal comparison: r{} <= r{}", left, right); align_comment(&base, &comment, config.comment_column) } Instruction::Gt { dest, left, right } => { let base = format!( "{}Gt r{} ← (r{} > r{})", indent, dest, left, right ); let comment = format!("Greater than comparison: r{} > r{}", left, right); align_comment(&base, &comment, config.comment_column) } Instruction::Ge { dest, left, right } => { let base = format!( "{}Ge r{} ← (r{} ≥ r{})", indent, dest, left, right ); let comment = format!("Greater or equal comparison: r{} >= r{}", left, right); align_comment(&base, &comment, config.comment_column) } Instruction::And { dest, left, right } => { let base = format!("{}And r{} ← r{} ∧ r{}", indent, dest, left, right); let comment = format!("Logical AND: r{} && r{}", left, right); align_comment(&base, &comment, config.comment_column) } Instruction::Or { dest, left, right } => { let base = format!("{}Or r{} ← r{} ∨ r{}", indent, dest, left, right); let comment = format!("Logical OR: r{} || r{}", left, right); align_comment(&base, &comment, config.comment_column) } Instruction::Not { dest, operand } => { let base = format!("{}Not r{} ← ¬r{}", indent, dest, operand); let comment = format!("Logical NOT: !r{}", operand); align_comment(&base, &comment, config.comment_column) } Instruction::BuiltinCall { params_index } => instruction_data .get_builtin_call_params(params_index) .map_or_else( || { let base = format!("{}BuiltinCall [INVALID P({})]", indent, params_index); align_comment( &base, "ERROR: Invalid builtin call parameters", config.comment_column, ) }, |params| { let args_str = params .arg_registers() .iter() .map(|&r| format!("r{}", r)) .collect::>() .join(", "); let builtin_name = program .builtin_info_table .get(usize::from(params.builtin_index)) .map(|info| info.name.as_str()) .unwrap_or(""); let base = format!( "{}BuiltinCall r{} ← {}({})", indent, params.dest, builtin_name, args_str ); let comment = format!( "Call builtin '{}' (B{}) with {} args", builtin_name, params.builtin_index, params.num_args ); align_comment(&base, &comment, config.comment_column) }, ), Instruction::FunctionCall { params_index } => instruction_data .get_function_call_params(params_index) .map_or_else( || { let base = format!("{}FunctionCall [INVALID P({})]", indent, params_index); align_comment( &base, "ERROR: Invalid function call parameters", config.comment_column, ) }, |params| { let args_str = params .arg_registers() .iter() .map(|&r| format!("r{}", r)) .collect::>() .join(", "); let func_name = program .rule_infos .get(usize::from(params.func_rule_index)) .map(|info| info.name.as_str()) .unwrap_or(""); let base = format!( "{}FunctionCall r{} ← {}({})", indent, params.dest, func_name, args_str ); let comment = format!( "Call function '{}' (R{}) with {} args", func_name, params.func_rule_index, params.num_args ); align_comment(&base, &comment, config.comment_column) }, ), Instruction::HostAwait { dest, arg, id } => { let base = format!( "{}HostAwait r{} ← await r{} (id r{})", indent, dest, arg, id ); align_comment( &base, &format!( "Suspend and request host result using r{} with identifier r{}", arg, id ), config.comment_column, ) } Instruction::Return { value } => { let base = format!("{}Return return r{}", indent, value); let comment = format!("Return value from r{}", value); align_comment(&base, &comment, config.comment_column) } Instruction::ObjectSet { obj, key, value } => { let base = format!("{}ObjectSet r{}[r{}] ← r{}", indent, obj, key, value); let comment = format!("Set field r{}[r{}] = r{}", obj, key, value); align_comment(&base, &comment, config.comment_column) } Instruction::ObjectCreate { params_index } => { let params = program .instruction_data .get_object_create_params(params_index); let base = format!( "{}ObjectCreate r{} ← {{...}}", indent, params.map_or(0, |p| p.dest) ); let comment = params.map_or_else( || format!("Create object (P{} - INVALID)", params_index), |p| { format!( "Create object with {} fields (P{})", p.field_count(), params_index, ) }, ); align_comment(&base, &comment, config.comment_column) } Instruction::Index { dest, container, key, } => { let base = format!( "{}Index r{} ← r{}[r{}]", indent, dest, container, key ); let comment = format!("Index operation: get r{}[r{}]", container, key); align_comment(&base, &comment, config.comment_column) } Instruction::IndexLiteral { dest, container, literal_idx, } => { let base = format!( "{}IndexLiteral r{} ← r{}[L{}]", indent, dest, container, literal_idx ); let comment = program.literals.get(usize::from(literal_idx)).map_or_else( || { format!( "Index with literal: r{}[L{}] (invalid index)", container, literal_idx, ) }, |literal| { let literal_json = serde_json::to_string(literal).unwrap_or_else(|_| "".to_string()); format!("Index with literal key: r{}[{}]", container, literal_json) }, ); align_comment(&base, &comment, config.comment_column) } Instruction::ArrayNew { dest } => { let base = format!("{}ArrayNew r{} ← []", indent, dest); align_comment(&base, "Create new empty array", config.comment_column) } Instruction::ArrayPush { arr, value } => { let base = format!("{}ArrayPush r{}.push(r{})", indent, arr, value); let comment = format!("Append r{} to array r{}", value, arr); align_comment(&base, &comment, config.comment_column) } Instruction::ArrayPushDefined { arr, value } => { let base = format!("{}ArrayPushDef r{}.push(r{})", indent, arr, value); let comment = format!("Append r{} to array r{} (skip if undefined)", value, arr); align_comment(&base, &comment, config.comment_column) } Instruction::ArrayCreate { params_index } => instruction_data .get_array_create_params(params_index) .map_or_else( || format!("{}ArrayCreate ", indent, params_index), |params| { let elements = params .element_registers() .iter() .map(|r| format!("r{}", r)) .collect::>() .join(", "); let base = format!("{}ArrayCreate r{} ← [{}]", indent, params.dest, elements); let comment = format!( "Create array from {} elements (undefined if any element is undefined)", params.element_count() ); align_comment(&base, &comment, config.comment_column) }, ), Instruction::SetNew { dest } => { let base = format!("{}SetNew r{} ← set()", indent, dest); align_comment(&base, "Create new empty set", config.comment_column) } Instruction::SetAdd { set, value } => { let base = format!("{}SetAdd r{} ∪= r{}", indent, set, value); let comment = format!("Add r{} to set r{}", value, set); align_comment(&base, &comment, config.comment_column) } Instruction::SetCreate { params_index } => instruction_data .get_set_create_params(params_index) .map_or_else( || format!("{}SetCreate ", indent, params_index), |params| { let elements = params .element_registers() .iter() .map(|r| format!("r{}", r)) .collect::>() .join(", "); let base = format!("{}SetCreate r{} ← {{{}}}", indent, params.dest, elements); let comment = format!( "Create set from {} elements (undefined if any element is undefined)", params.element_count() ); align_comment(&base, &comment, config.comment_column) }, ), Instruction::Contains { dest, collection, value, } => { let base = format!( "{}Contains r{} ← (r{} ∈ r{})", indent, dest, value, collection ); let comment = format!("Membership test: r{} in r{}", value, collection); align_comment(&base, &comment, config.comment_column) } Instruction::Count { dest, collection } => { let base = format!("{}Count r{} ← count(r{})", indent, dest, collection); let comment = format!("Get count/length of collection r{}", collection); align_comment(&base, &comment, config.comment_column) } Instruction::AssertEq { left, right } => { let base = format!("{}AssertEq assert r{} == r{}", indent, left, right); let comment = format!( "Assert r{} equals r{} (exit if either undefined or different)", left, right ); align_comment(&base, &comment, config.comment_column) } Instruction::Guard { register, mode } => { let (keyword, comment) = match mode { crate::rvm::instructions::GuardMode::Not => ( format!("{}AssertNot assert !r{}", indent, register), format!("Assert r{} is false/undefined (exit if true)", register), ), crate::rvm::instructions::GuardMode::Condition => ( format!("{}Assert assert r{}", indent, register), format!("Assert r{} is true (exit if false/undefined)", register), ), crate::rvm::instructions::GuardMode::NotUndefined => ( format!( "{}AssertNotUndefined assert_not_undefined r{}", indent, register ), format!("Assert r{} is not undefined (exit if undefined)", register), ), }; align_comment(&keyword, &comment, config.comment_column) } Instruction::ReturnUndefinedIfNotTrue { condition } => { let base = format!( "{}ReturnUndefinedIfNotTrue if r{} != true return undefined", indent, condition ); let comment = format!( "Return undefined unless r{} is exactly boolean true", condition ); align_comment(&base, &comment, config.comment_column) } Instruction::CoalesceUndefinedToNull { register } => { let base = format!( "{}CoalesceUndefinedToNull r{} = null if undefined", indent, register ); let comment = format!("Azure Policy: absent field → null (r{})", register); align_comment(&base, &comment, config.comment_column) } Instruction::LoopStart { params_index } => { instruction_data.get_loop_params(params_index).map_or_else( || { let base = format!("{}LoopStart [INVALID P({})] {{", indent, params_index); align_comment( &base, "ERROR: Invalid loop parameters", config.comment_column, ) }, |params| { let mode_str = match params.mode { LoopMode::Any => "any", LoopMode::Every => "every", LoopMode::ForEach => "foreach", }; let base = format!( "{}LoopStart {} r{},r{} in r{} → r{} {{", indent, mode_str, params.key_reg, params.value_reg, params.collection, params.result_reg ); let comment = format!( "{} loop over r{}, body: {}-{} (P{})", mode_str, params.collection, params.body_start, params.loop_end, params_index ); align_comment(&base, &comment, config.comment_column) }, ) } Instruction::LoopNext { body_start, loop_end, } => { let base = format!( "{}}} continue → {} or exit → {}", indent, body_start, loop_end ); let comment = format!( "Next iteration or exit loop (body:{}-{})", body_start, loop_end ); align_comment(&base, &comment, config.comment_column) } Instruction::CallRule { dest, rule_index } => { let rule_name = program .rule_infos .get(usize::from(rule_index)) .map(|info| info.name.as_str()) .unwrap_or(""); let base = format!("{}CallRule r{} ← {}", indent, dest, rule_name); let comment = format!("Call rule '{}' (R{}) with caching", rule_name, rule_index); align_comment(&base, &comment, config.comment_column) } Instruction::RuleInit { result_reg, rule_index, } => { let rule_name = program .rule_infos .get(usize::from(rule_index)) .map(|info| info.name.as_str()) .unwrap_or(""); let base = format!("{}RuleInit {} → r{} {{", indent, rule_name, result_reg); let comment = format!( "Initialize rule '{}' (R{}) evaluation", rule_name, rule_index ); align_comment(&base, &comment, config.comment_column) } Instruction::RuleReturn {} => { let base = format!("{}}} return from rule", indent); align_comment(&base, "End of rule evaluation", config.comment_column) } Instruction::ChainedIndex { params_index } => { let (base, comment) = instruction_data .get_chained_index_params(params_index) .map_or_else( || { let base_str = format!("{}ChainedIndex chained_index", indent); let comment_str = "Multi-level chained indexing (invalid params)".to_string(); (base_str, comment_str) }, |params| { let chain_parts: Vec = params .path_components .iter() .map(|component| match *component { crate::rvm::instructions::LiteralOrRegister::Literal(idx) => { program.literals.get(usize::from(idx)).map_or_else( || format!("[L{}?]", idx), |literal| match *literal { crate::Value::String(ref s) => { format!(".{}", s.as_ref()) } ref other => format!( "[{}]", serde_json::to_string(other) .unwrap_or_else(|_| "?".to_string()) ), }, ) } crate::rvm::instructions::LiteralOrRegister::Register(reg) => { format!("[r{}]", reg) } }) .collect(); let chain_display = if chain_parts.is_empty() { String::new() } else { format!(" r{}{}", params.root, chain_parts.join("")) }; let base_str = format!( "{}ChainedIndex r{} ← r{}{}", indent, params.dest, params.root, chain_display ); let comment_str = format!( "Multi-level chained indexing: r{} → r{}", params.root, params.dest ); (base_str, comment_str) }, ); align_comment(&base, &comment, config.comment_column) } Instruction::VirtualDataDocumentLookup { .. } => { let base = format!( "{}VirtualDataDocumentLookup virtual_data_document_lookup", indent ); align_comment( &base, "Lookup in data namespace virtual documents", config.comment_column, ) } Instruction::DestructuringSuccess {} => { let base = format!("{}DestructuringSuccess ✓", indent); align_comment( &base, "Parameter destructuring validated", config.comment_column, ) } Instruction::Halt {} => { let base = format!("{}Halt halt", indent); align_comment(&base, "Stop execution", config.comment_column) } Instruction::ComprehensionBegin { params_index } => instruction_data .get_comprehension_begin_params(params_index) .map_or_else( || { let base = format!("{}CompBegin [INVALID P({})] {{", indent, params_index); align_comment( &base, "ERROR: Invalid comprehension parameters", config.comment_column, ) }, |params| { let mode_str = match params.mode { crate::rvm::instructions::ComprehensionMode::Array => "array", crate::rvm::instructions::ComprehensionMode::Set => "set", crate::rvm::instructions::ComprehensionMode::Object => "object", }; let (source_desc, result_desc) = if params.collection_reg == params.result_reg { ( format!("r{}", params.collection_reg), format!("r{}", params.result_reg), ) } else { ( format!("r{} (src)", params.collection_reg), format!("r{} (dst)", params.result_reg), ) }; let base = format!( "{}CompBegin {} {} → {} k:{} v:{} {{", indent, mode_str, source_desc, result_desc, params.key_reg, params.value_reg ); let comment = format!( "{} comprehension in r{}, body: {}-{} (P{})", mode_str, params.collection_reg, params.body_start, params.comprehension_end, params_index ); align_comment(&base, &comment, config.comment_column) }, ), Instruction::ComprehensionYield { value_reg, key_reg } => { let base = key_reg.map_or_else( || format!("{}CompYield r{}", indent, value_reg), |k| format!("{}CompYield r{} r{}", indent, k, value_reg), ); align_comment(&base, "Yield value to comprehension", config.comment_column) } Instruction::ComprehensionEnd {} => { let base = format!("{}}} CompEnd", indent); align_comment(&base, "End comprehension block", config.comment_column) } // Azure Policy & allOf/anyOf instructions — use Display impl instruction @ Instruction::PolicyCondition { .. } | instruction @ Instruction::LogicalBlockStart { .. } | instruction @ Instruction::AllOfNext { .. } | instruction @ Instruction::AnyOfNext { .. } | instruction @ Instruction::LogicalBlockEnd { .. } => { format!("{}{}", indent, instruction) } } } /// Generate compact tabular assembly listing pub fn generate_tabular_assembly_listing( program: &Program, _config: &AssemblyListingConfig, ) -> String { let mut output = String::new(); let mut indent_level: usize = 0; // Add header push_line(&mut output, format_args!("; RVM Assembly (Tabular Format)")); push_line( &mut output, format_args!( "; {} instructions, {} literals", program.instructions.len(), program.literals.len() ), ); push_line(&mut output, format_args!(";")); push_line( &mut output, format_args!("; PC | Instruction | Operation"), ); push_line( &mut output, format_args!(";-----|--------------|----------"), ); for (pc, instruction) in program.instructions.iter().enumerate() { // Handle loop indentation match *instruction { Instruction::LoopNext { .. } => { indent_level = indent_level.saturating_sub(1); } Instruction::RuleReturn { .. } => { indent_level = indent_level.saturating_sub(1); } _ => {} } let indent = " ".repeat(indent_level.saturating_mul(2)); // Smaller indent for tabular format // Format in tabular style let addr_str = format!("{:03}", pc); let inst_name = get_instruction_name(instruction); let operation = format_operation_compact(instruction, &indent, &program.instruction_data, program); push_line( &mut output, format_args!("{:>4} | {:12} | {}", addr_str, inst_name, operation), ); // Increase indentation for loop/rule starts match *instruction { Instruction::LoopStart { .. } => { indent_level = indent_level.saturating_add(1); } Instruction::RuleInit { .. } => { indent_level = indent_level.saturating_add(1); } _ => {} } } output } const fn get_instruction_name(instruction: &Instruction) -> &'static str { match *instruction { Instruction::Load { .. } => "LOAD", Instruction::LoadTrue { .. } => "LOAD_TRUE", Instruction::LoadFalse { .. } => "LOAD_FALSE", Instruction::LoadNull { .. } => "LOAD_NULL", Instruction::LoadBool { .. } => "LOAD_BOOL", Instruction::LoadData { .. } => "LOAD_DATA", Instruction::LoadInput { .. } => "LOAD_INPUT", Instruction::LoadContext { .. } => "LOAD_CONTEXT", Instruction::LoadMetadata { .. } => "LOAD_METADATA", Instruction::Move { .. } => "MOVE", Instruction::Add { .. } => "ADD", Instruction::Sub { .. } => "SUB", Instruction::Mul { .. } => "MUL", Instruction::Div { .. } => "DIV", Instruction::Mod { .. } => "MOD", Instruction::Eq { .. } => "EQ", Instruction::Ne { .. } => "NE", Instruction::Lt { .. } => "LT", Instruction::Le { .. } => "LE", Instruction::Gt { .. } => "GT", Instruction::Ge { .. } => "GE", Instruction::And { .. } => "AND", Instruction::Or { .. } => "OR", Instruction::Not { .. } => "NOT", Instruction::BuiltinCall { .. } => "BUILTIN_CALL", Instruction::FunctionCall { .. } => "FUNC_CALL", Instruction::HostAwait { .. } => "HOST_AWAIT", Instruction::Return { .. } => "RETURN", Instruction::ObjectSet { .. } => "OBJ_SET", Instruction::ObjectCreate { .. } => "OBJ_CREATE", Instruction::Index { .. } => "INDEX", Instruction::IndexLiteral { .. } => "INDEX_LIT", Instruction::ArrayNew { .. } => "ARRAY_NEW", Instruction::ArrayPush { .. } => "ARRAY_PUSH", Instruction::ArrayPushDefined { .. } => "ARRAY_PUSH_DEF", Instruction::ArrayCreate { .. } => "ARRAY_CREATE", Instruction::SetNew { .. } => "SET_NEW", Instruction::SetAdd { .. } => "SET_ADD", Instruction::SetCreate { .. } => "SET_CREATE", Instruction::Contains { .. } => "CONTAINS", Instruction::Count { .. } => "COUNT", Instruction::AssertEq { .. } => "ASSERT_EQ", Instruction::Guard { mode, .. } => match mode { crate::rvm::instructions::GuardMode::Not => "ASSERT_NOT", crate::rvm::instructions::GuardMode::Condition => "ASSERT", crate::rvm::instructions::GuardMode::NotUndefined => "ASSERT_NOT_UNDEF", }, Instruction::ReturnUndefinedIfNotTrue { .. } => "RET_UNDEF_IF_NOT_TRUE", Instruction::CoalesceUndefinedToNull { .. } => "COALESCE_UNDEF_TO_NULL", Instruction::LoopStart { .. } => "LOOP_START", Instruction::LoopNext { .. } => "LOOP_NEXT", Instruction::CallRule { .. } => "CALL_RULE", Instruction::RuleInit { .. } => "RULE_INIT", Instruction::RuleReturn { .. } => "RULE_RET", Instruction::DestructuringSuccess {} => "DESTRUCT_SUCCESS", Instruction::ChainedIndex { .. } => "CHAINED_INDEX", Instruction::VirtualDataDocumentLookup { .. } => "VIRTUAL_DATA_DOC_LOOKUP", Instruction::Halt {} => "HALT", Instruction::ComprehensionBegin { .. } => "COMP_BEGIN", Instruction::ComprehensionYield { .. } => "COMP_YIELD", Instruction::ComprehensionEnd {} => "COMP_END", // Azure Policy instructions Instruction::PolicyCondition { op, .. } => op.compact_name(), // AllOf / AnyOf Instruction::LogicalBlockStart { mode, .. } => match mode { crate::rvm::instructions::LogicalBlockMode::AllOf => "ALL_OF_START", crate::rvm::instructions::LogicalBlockMode::AnyOf => "ANY_OF_START", }, Instruction::AllOfNext { .. } => "ALL_OF_NEXT", Instruction::AnyOfNext { .. } => "ANY_OF_NEXT", Instruction::LogicalBlockEnd { mode, .. } => match mode { crate::rvm::instructions::LogicalBlockMode::AllOf => "ALL_OF_END", crate::rvm::instructions::LogicalBlockMode::AnyOf => "ANY_OF_END", }, } } fn format_operation_compact( instruction: &Instruction, indent: &str, instruction_data: &InstructionData, _program: &Program, ) -> String { match *instruction { Instruction::Load { dest, literal_idx } => { format!("{}r{} ← L{}", indent, dest, literal_idx) } Instruction::LoadInput { dest } => { format!("{}r{} ← input", indent, dest) } Instruction::LoadContext { dest } => { format!("{}r{} ← context", indent, dest) } Instruction::LoadMetadata { dest } => { format!("{}r{} ← metadata", indent, dest) } Instruction::LoadData { dest } => { format!("{}r{} ← data", indent, dest) } Instruction::Move { dest, src } => { format!("{}r{} ← r{}", indent, dest, src) } Instruction::Add { dest, left, right } => { format!("{}r{} ← r{} + r{}", indent, dest, left, right) } Instruction::Index { dest, container, key, } => { format!("{}r{} ← r{}[r{}]", indent, dest, container, key) } Instruction::IndexLiteral { dest, container, literal_idx, } => { format!("{}r{} ← r{}[L{}]", indent, dest, container, literal_idx) } Instruction::LoopStart { params_index } => { instruction_data.get_loop_params(params_index).map_or_else( || format!("{}loop P({}) {{", indent, params_index), |params| { format!( "{}loop r{} in r{} {{", indent, params.value_reg, params.collection ) }, ) } Instruction::LoopNext { .. } => { format!("{}}}", indent) } Instruction::CallRule { dest, rule_index } => { format!("{}r{} ← rule_{}", indent, dest, rule_index) } Instruction::HostAwait { dest, arg, id } => { format!("{}await r{} → r{} (id r{})", indent, arg, dest, id) } Instruction::RuleInit { result_reg, rule_index, } => { format!("{}rule_{} → r{} {{", indent, rule_index, result_reg) } Instruction::RuleReturn {} => { format!("{}}}", indent) } Instruction::DestructuringSuccess {} => { format!("{}✓ destructuring validated", indent) } _ => { // For other instructions, use a simplified version format!( "{}{}", indent, instruction .to_string() .replace("R(", "r") .replace(")", "") .replace("L(", "L") ) } } }