use super::instruction::InstructionRegistry;
use super::instruction::{CompiledInstruction, InstructionMetadata, ScriptError};
use super::parser::AstNode;
pub fn multi_type_block_pairing(
instructions: &[CompiledInstruction],
block_types: &[(&str, &str)], ) -> Vec<Option<InstructionMetadata>> {
let mut metadata_list = (0..instructions.len()).map(|_| None).collect::<Vec<_>>();
let mut stack: Vec<(usize, String)> = Vec::new();
for (ip, instr) in instructions.iter().enumerate() {
let mut is_start = false;
let mut block_type_name = String::new();
for (start_name, _end_name) in block_types {
if instr.name == *start_name {
is_start = true;
block_type_name = start_name.to_string();
break;
}
}
if is_start {
stack.push((ip, block_type_name));
} else {
let mut matched_end = false;
for (_start_name, end_name) in block_types {
if instr.name == *end_name {
if let Some((start_ip, start_type)) = stack.pop() {
metadata_list[start_ip] = Some(InstructionMetadata::new(GenericBlockMetadata {
end_ip: ip,
block_type: start_type.clone(),
}));
if metadata_list[ip].is_none() {
metadata_list[ip] = Some(InstructionMetadata::new(TerminatorMetadata {
start_ip,
block_type: start_type,
}));
}
matched_end = true;
} else {
eprintln!("Warning: Unmatched '{}' terminator at IP {}", instr.name, ip);
}
break; }
}
if !matched_end && !is_start {
continue;
}
}
}
if !stack.is_empty() {
eprintln!(
"Warning: {} unclosed block(s) detected",
stack.len()
);
for (start_ip, start_type) in &stack {
eprintln!(
" - '{}' block starting at IP {} has no matching 'end'",
start_type, start_ip
);
}
}
metadata_list
}
#[derive(Debug, Clone)]
pub struct GenericBlockMetadata {
pub end_ip: usize,
pub block_type: String,
}
#[derive(Debug, Clone)]
pub struct ConditionalBlockMetadata {
pub else_ip: Option<usize>,
pub end_ip: usize,
pub block_type: String,
}
#[derive(Debug, Clone)]
pub struct TerminatorMetadata {
pub start_ip: usize,
pub block_type: String,
}
pub fn pair_conditional_blocks(
instructions: &[CompiledInstruction],
if_name: &str,
else_name: &str,
end_name: &str,
) -> Vec<Option<InstructionMetadata>> {
let mut metadata_list = (0..instructions.len()).map(|_| None).collect::<Vec<_>>();
let mut stack: Vec<(Option<usize>, usize)> = Vec::new();
for (ip, instr) in instructions.iter().enumerate() {
if instr.name == if_name {
stack.push((None, ip));
} else if instr.name == else_name {
if let Some((_, if_ip)) = stack.last_mut() {
*stack.last_mut().unwrap() = (Some(ip), *if_ip);
} else {
eprintln!("Warning: 'else' without matching 'if' at IP {}", ip);
}
} else if instr.name == end_name {
if let Some((else_ip, if_ip)) = stack.pop() {
metadata_list[if_ip] = Some(InstructionMetadata::new(ConditionalBlockMetadata {
else_ip,
end_ip: ip,
block_type: if_name.to_string(),
}));
if let Some(else_ip_val) = else_ip {
metadata_list[else_ip_val] =
Some(InstructionMetadata::new(TerminatorMetadata {
start_ip: if_ip,
block_type: if_name.to_string(),
}));
}
metadata_list[ip] = Some(InstructionMetadata::new(TerminatorMetadata {
start_ip: if_ip,
block_type: if_name.to_string(),
}));
} else {
eprintln!("Warning: Unmatched '{}' terminator at IP {}", end_name, ip);
}
}
}
if !stack.is_empty() {
eprintln!(
"Warning: {} unclosed '{}' block(s) detected",
stack.len(),
if_name
);
}
metadata_list
}
#[derive(Debug, Clone)]
pub struct CompilerConfig {
pub optimization_level: u8,
}
impl Default for CompilerConfig {
fn default() -> Self {
Self {
optimization_level: 0,
}
}
}
pub struct Compiler<'a> {
#[allow(dead_code)]
config: CompilerConfig,
registry: &'a InstructionRegistry,
}
impl<'a> Compiler<'a> {
pub fn new(config: CompilerConfig, registry: &'a InstructionRegistry) -> Self {
Self { config, registry }
}
pub fn compile(&self, ast_nodes: &[AstNode]) -> Result<CompiledScript, ScriptError> {
let mut instructions = Vec::new();
for node in ast_nodes {
let instr = self.compile_node(node)?;
instructions.push(instr);
}
self.apply_automatic_block_pairing(&mut instructions)?;
Ok(CompiledScript::new(instructions))
}
fn compile_node(&self, node: &AstNode) -> Result<CompiledInstruction, ScriptError> {
let handler = self.registry.get_handler(&node.command).ok_or_else(|| {
ScriptError::CompilationError(format!("Handler not found for '{}'", node.command))
})?;
let args: Vec<&str> = node.args.iter().map(|s| s.as_str()).collect();
let data = handler.parse(&args)?;
Ok(CompiledInstruction {
name: node.command.clone(),
data,
metadata: None, })
}
fn apply_automatic_block_pairing(
&self,
instructions: &mut Vec<CompiledInstruction>,
) -> Result<(), ScriptError> {
use crate::script_engine::instruction::BlockStructure;
let mut simple_pairs: Vec<(String, String)> = Vec::new();
let mut conditional_blocks: Vec<(String, String, String)> = Vec::new();
let mut seen_names = std::collections::HashSet::new();
for instr in instructions.iter() {
if seen_names.insert(&instr.name) {
if let Some(handler) = self.registry.get_handler(&instr.name) {
if let Some(structure) = handler.declare_block_structure() {
match structure {
BlockStructure::SimplePair {
start_name,
end_name,
} => {
simple_pairs.push((start_name.to_string(), end_name.to_string()));
}
BlockStructure::ConditionalTriple {
if_name,
else_name,
end_name,
} => {
conditional_blocks.push((if_name.to_string(), else_name.to_string(), end_name.to_string()));
}
BlockStructure::Custom { .. } => {
eprintln!("Warning: Custom block structure not yet implemented");
}
}
}
}
}
}
if !simple_pairs.is_empty() {
let block_type_refs: Vec<(&str, &str)> = simple_pairs
.iter()
.map(|(s, e)| (s.as_str(), e.as_str()))
.collect();
let metadata = multi_type_block_pairing(instructions, &block_type_refs);
self.merge_metadata(instructions, metadata);
}
for (if_name, else_name, end_name) in conditional_blocks {
let metadata =
pair_conditional_blocks(instructions, &if_name, &else_name, &end_name);
self.merge_metadata(instructions, metadata);
}
Ok(())
}
fn merge_metadata(
&self,
instructions: &mut Vec<CompiledInstruction>,
new_metadata: Vec<Option<InstructionMetadata>>,
) {
for (i, meta_opt) in new_metadata.into_iter().enumerate() {
if i < instructions.len() {
if instructions[i].metadata.is_none() {
instructions[i].metadata = meta_opt;
}
}
}
}
}
#[derive(Debug)]
pub struct CompiledScript {
pub instructions: Vec<CompiledInstruction>,
pub source_lines: Vec<String>,
}
impl CompiledScript {
pub fn new(instructions: Vec<CompiledInstruction>) -> Self {
Self {
instructions,
source_lines: Vec::new(),
}
}
pub fn len(&self) -> usize {
self.instructions.len()
}
pub fn is_empty(&self) -> bool {
self.instructions.is_empty()
}
}