Skip to main content

cqlib_core/ir/qasm2/
load.rs

1// This code is part of Cqlib.
2//
3// (C) Copyright China Telecom Quantum Group 2026
4//
5// This code is licensed under the Apache License, Version 2.0. You may
6// obtain a copy of this license in the LICENSE.txt file in the root directory
7// of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
8//
9// Any modifications or derivative works of this code must retain this
10// copyright notice, and modified files need to carry a notice indicating
11// that they have been altered from the originals.
12
13//! OpenQASM 2.0 Parser Module
14//!
15//! This module provides functionality to parse OpenQASM 2.0 quantum programs
16//! and convert them into the internal `Circuit` representation.
17//!
18//! ## Features
19//!
20//! - **Full OpenQASM 2.0 Support**: Parses all standard statements including quantum registers,
21//!   classical registers, gates, measurements, barriers, resets, and conditional execution
22//! - **Custom Gate Compilation**: Compiles user-defined gates into internal representations
23//! - **Include Processing**: Handles external includes and built-in `qelib1.inc`
24//! - **Expression Evaluation**: Evaluates mathematical expressions in gate parameters
25//! - **Error Handling**: Provides detailed error messages with line numbers and context
26//!
27//! ## Architecture
28//!
29//! The parser works in multiple phases:
30//! 1. **Discovery Pass**: Collects register declarations, gate definitions, and processes includes
31//! 2. **Compilation Phase**: Compiles custom gates into internal `CircuitGate` representations
32//! 3. **Generation Pass**: Converts AST statements into circuit operations
33//!
34//! ## Example
35//!
36//! ```rust
37//! use cqlib_core::ir::qasm2::load::loads;
38//!
39//! let qasm = r#"
40//!     OPENQASM 2.0;
41//!     include "qelib1.inc";
42//!     qreg q[2];
43//!     creg c[2];
44//!     h q[0];
45//!     cx q[0], q[1];
46//!     measure q[0] -> c[0];
47//!     measure q[1] -> c[1];
48//! "#;
49//!
50//! let circuit = loads(qasm).unwrap();
51//! assert_eq!(circuit.num_qubits(), 2);
52//! ```
53//!
54//! ## Gate Compilation
55//!
56//! Custom gates are collected during discovery and compiled before operation
57//! generation so invalid or circular definitions fail deterministically:
58//!
59//! ```rust
60//! use cqlib_core::ir::qasm2::load::loads;
61//!
62//! let qasm = r#"
63//!     OPENQASM 2.0;
64//!     qreg q[1];
65//!     gate my_gate q { h q; }
66//!     my_gate q;
67//! "#;
68//!
69//! let circuit = loads(qasm).unwrap();
70//! ```
71
72use crate::circuit::bit::Qubit;
73use crate::circuit::circuit_param::ParameterValue;
74use crate::circuit::gate::circuit_gate::{CircuitGate, FrozenCircuit};
75use crate::circuit::gate::{Directive, Instruction, StandardGate};
76use crate::circuit::{Circuit, CircuitError, ClassicalExpr, ClassicalType, ClassicalVar};
77
78use crate::circuit::parameter::Parameter;
79use crate::ir::qasm2::ast::{
80    Argument, Expression, OpCode, OpenQASMProgram, Statement, UnaryOpCode,
81};
82use smallvec::{SmallVec, smallvec};
83use std::collections::{HashMap, HashSet};
84use std::io;
85use std::panic::{AssertUnwindSafe, catch_unwind};
86use std::path::{Component, Path, PathBuf};
87
88/// Trait for abstracting file system access during OpenQASM parsing.
89///
90/// This allows the parser to work in different environments:
91/// - Real file system (use `FileSystemResolver`)
92/// - Memory-only mode (use `NullResolver`)
93/// - Custom resolvers for WASM or embedded files
94pub trait QasmSourceResolver {
95    /// Resolve the content of a file given its path.
96    ///
97    /// # Arguments
98    /// * `path` - The file path to resolve
99    ///
100    /// # Returns
101    /// * `Ok(String)` - The file contents
102    /// * `Err(io::Error)` - I/O error if resolution fails
103    fn resolve_source(&self, path: &Path) -> Result<String, io::Error>;
104}
105
106/// Default resolver that uses the real file system.
107pub struct FileSystemResolver;
108
109impl QasmSourceResolver for FileSystemResolver {
110    fn resolve_source(&self, path: &Path) -> Result<String, io::Error> {
111        std::fs::read_to_string(path)
112    }
113}
114
115/// A null resolver that fails on any include. Used when loading raw strings without context.
116pub struct NullResolver;
117
118impl QasmSourceResolver for NullResolver {
119    fn resolve_source(&self, path: &Path) -> Result<String, io::Error> {
120        Err(io::Error::new(
121            io::ErrorKind::Unsupported,
122            format!("Cannot include files in raw string mode (path: {:?})", path),
123        ))
124    }
125}
126
127const QELIB1_DIRECT_GATES: &[&str] = &[
128    "u3", "u2", "u1", "cx", "id", "x", "y", "z", "h", "s", "sdg", "t", "tdg", "rx", "ry", "rz",
129    "cz", "cy", "ccx", "crz",
130];
131
132const QELIB1_CUSTOM_GATES: &[&str] = &["ch", "cu1", "cu3"];
133
134#[allow(dead_code)]
135const CQLIB_BUILTIN_EXTENSION_GATES: &[&str] = &[
136    "p", "sx", "sxdg", "rxx", "ryy", "rzz", "rzx", "fsim", "crx", "cry", "swap", "toffoli",
137];
138
139/// Gate names declared by OpenQASM 2.0 qelib1.inc.
140///
141/// Cqlib treats qelib1 gates as compatibility builtins: user sources may call
142/// them without an explicit include, and may not redefine them. OpenQASM
143/// identifiers are case-sensitive, so this list uses the exact lowercase
144/// spellings present in qelib1.inc.
145const QELIB1_RESERVED_GATE_NAMES: &[&str] = &[
146    "u3", "u2", "u1", "cx", "id", "x", "y", "z", "h", "s", "sdg", "t", "tdg", "rx", "ry", "rz",
147    "cz", "cy", "ch", "ccx", "crz", "cu1", "cu3",
148];
149
150#[rustfmt::skip]
151mod parser {
152    include!(concat!(env!("OUT_DIR"), "/ir/qasm2/parser.rs"));
153}
154
155/// Parse OpenQASM 2.0 file and convert to Circuit
156pub fn load<P: AsRef<Path>>(path: P) -> Result<Circuit, QasmParseError> {
157    let path = path.as_ref();
158
159    // Use FileSystemResolver for file-based loading
160    let resolver = Box::new(FileSystemResolver);
161
162    // Get content using the resolver
163    let content = resolver
164        .resolve_source(path)
165        .map_err(QasmParseError::IoError)?;
166
167    // Pass the parent directory as the base path for includes
168    let base_path = path.parent().map(|p| p.to_path_buf());
169
170    parse_qasm_file_with_context(&content, normalize_path(path), base_path, resolver)
171}
172
173/// Parse an OpenQASM 2.0 file and convert it to a [`Circuit`].
174///
175/// Rust-style alias for [`load`]. The Python-style `load` name is retained for
176/// compatibility with the rest of the IR module API.
177pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Circuit, QasmParseError> {
178    load(path)
179}
180
181/// Parse OpenQASM 2.0 string and convert to Circuit
182pub fn loads(source: &str) -> Result<Circuit, QasmParseError> {
183    // Use NullResolver for string-based loading (no file includes allowed)
184    parse_qasm_with_context(source, None, Box::new(NullResolver))
185}
186
187/// Parse an OpenQASM 2.0 source string and convert it to a [`Circuit`].
188///
189/// Rust-style alias for [`loads`].
190pub fn from_str(source: &str) -> Result<Circuit, QasmParseError> {
191    loads(source)
192}
193
194fn parse_qasm_with_context(
195    source: &str,
196    base_path: Option<PathBuf>,
197    resolver: Box<dyn QasmSourceResolver>,
198) -> Result<Circuit, QasmParseError> {
199    parse_qasm_source_with_context(source, None, base_path, resolver)
200}
201
202fn parse_qasm_file_with_context(
203    source: &str,
204    root_path: PathBuf,
205    base_path: Option<PathBuf>,
206    resolver: Box<dyn QasmSourceResolver>,
207) -> Result<Circuit, QasmParseError> {
208    parse_qasm_source_with_context(source, Some(root_path), base_path, resolver)
209}
210
211fn parse_qasm_source_with_context(
212    source: &str,
213    root_path: Option<PathBuf>,
214    base_path: Option<PathBuf>,
215    resolver: Box<dyn QasmSourceResolver>,
216) -> Result<Circuit, QasmParseError> {
217    if let Some(literal) = first_oversized_integer_literal(source) {
218        return Err(QasmParseError::ParseError(format!(
219            "Integer literal '{literal}' exceeds supported i64 range"
220        )));
221    }
222
223    let parser = parser::MainParser::new();
224    let parsed = catch_unwind(AssertUnwindSafe(|| parser.parse(source))).map_err(|payload| {
225        let message = if let Some(message) = payload.downcast_ref::<String>() {
226            message.clone()
227        } else if let Some(message) = payload.downcast_ref::<&'static str>() {
228            (*message).to_string()
229        } else {
230            "OpenQASM 2.0 parser panicked".to_string()
231        };
232        QasmParseError::ParseError(message)
233    })?;
234    let program = match parsed {
235        Ok(program) => program,
236        Err(e) => return Err(QasmParseError::ParseError(format!("{:?}", e))),
237    };
238    if (program.version - 2.0).abs() > f64::EPSILON {
239        return Err(QasmParseError::UnsupportedVersion(program.version));
240    }
241
242    let mut converter = AstToCircuit::new_with_root(base_path, root_path, resolver);
243    converter.convert(&program)
244}
245
246fn first_oversized_integer_literal(source: &str) -> Option<String> {
247    let bytes = source.as_bytes();
248    let mut index = 0;
249    while index < bytes.len() {
250        if bytes[index] == b'"' {
251            index += 1;
252            while index < bytes.len() && bytes[index] != b'"' {
253                index += 1;
254            }
255            index += usize::from(index < bytes.len());
256            continue;
257        }
258        if bytes[index] == b'/' && bytes.get(index + 1) == Some(&b'/') {
259            index += 2;
260            while index < bytes.len() && bytes[index] != b'\n' && bytes[index] != b'\r' {
261                index += 1;
262            }
263            continue;
264        }
265        if !bytes[index].is_ascii_digit() {
266            index += 1;
267            continue;
268        }
269
270        let start = index;
271        while index < bytes.len() && bytes[index].is_ascii_digit() {
272            index += 1;
273        }
274        let prev = start.checked_sub(1).and_then(|idx| bytes.get(idx)).copied();
275        let next = bytes.get(index).copied();
276        if matches!(prev, Some(b'.' | b'e' | b'E')) || matches!(next, Some(b'.' | b'e' | b'E')) {
277            continue;
278        }
279
280        let literal = &source[start..index];
281        if literal.parse::<i64>().is_err() {
282            return Some(literal.to_string());
283        }
284    }
285    None
286}
287
288/// Errors that can occur during OpenQASM parsing and conversion.
289///
290/// Each variant represents a distinct error category that can occur
291/// when processing OpenQASM 2.0 source code.
292#[derive(Debug)]
293pub enum QasmParseError {
294    /// File system or I/O error (file not found, permission denied, etc.)
295    IoError(io::Error),
296    /// Syntax error during parsing (invalid QASM syntax)
297    ParseError(String),
298    /// Semantic error during AST to Circuit conversion
299    ConversionError(String),
300    /// Reference to undefined quantum register or qubit
301    UndefinedQubit(String),
302    /// Reference to undefined classical register
303    UndefinedRegister(String),
304    /// Reference to undefined gate
305    UndefinedGate(String),
306    /// Attempt to redefine a gate declared by qelib1.inc
307    ReservedGateName(String),
308    /// Invalid argument format or usage
309    InvalidArgument(String),
310    /// Gate applied to wrong number of qubits
311    MismatchedQubitCount { expected: usize, actual: usize },
312    /// Gate called with wrong number of parameters
313    MismatchedParameterCount { expected: usize, actual: usize },
314    /// Gate expansion exceeded maximum recursion depth (circular definition likely)
315    RecursionLimitExceeded(String),
316    /// Failed to evaluate parameter expression (undefined variable, division by zero, etc.)
317    EvaluationError(String),
318    /// Circular gate dependency detected (gate calls itself directly or indirectly)
319    CircularGateDependency { gate: String, dependency: String },
320    /// Unsupported OpenQASM version for this loader
321    UnsupportedVersion(f64),
322    /// Duplicate declaration or namespace conflict
323    DuplicateDeclaration(String),
324    /// Include cycle detected
325    IncludeCycle(Vec<PathBuf>),
326    /// Opaque gate invocation is not representable in the Circuit IR
327    UnsupportedOpaqueGate(String),
328}
329
330impl std::fmt::Display for QasmParseError {
331    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
332        match self {
333            QasmParseError::IoError(s) => write!(f, "IO error: {}", s),
334            QasmParseError::ParseError(s) => write!(f, "Parse error: {}", s),
335            QasmParseError::ConversionError(s) => write!(f, "Conversion error: {}", s),
336            QasmParseError::UndefinedQubit(s) => write!(f, "Undefined qubit: {}", s),
337            QasmParseError::UndefinedRegister(s) => write!(f, "Undefined register: {}", s),
338            QasmParseError::UndefinedGate(s) => write!(f, "Undefined gate: {}", s),
339            QasmParseError::ReservedGateName(s) => write!(
340                f,
341                "Gate name '{}' is reserved by qelib1.inc and cannot be redefined",
342                s
343            ),
344            QasmParseError::InvalidArgument(s) => write!(f, "Invalid argument: {}", s),
345            QasmParseError::MismatchedQubitCount { expected, actual } => {
346                write!(
347                    f,
348                    "Mismatched qubit count: expected {}, got {}",
349                    expected, actual
350                )
351            }
352            QasmParseError::MismatchedParameterCount { expected, actual } => {
353                write!(
354                    f,
355                    "Mismatched parameter count: expected {}, got {}",
356                    expected, actual
357                )
358            }
359            QasmParseError::RecursionLimitExceeded(s) => {
360                write!(f, "Recursion limit exceeded: {}", s)
361            }
362            QasmParseError::EvaluationError(s) => write!(f, "Evaluation error: {}", s),
363            QasmParseError::CircularGateDependency { gate, dependency } => {
364                write!(
365                    f,
366                    "Circular gate dependency detected: '{}' depends on '{}'",
367                    gate, dependency
368                )
369            }
370            QasmParseError::UnsupportedVersion(version) => {
371                write!(f, "Unsupported OpenQASM version {version}; expected 2.0")
372            }
373            QasmParseError::DuplicateDeclaration(s) => {
374                write!(f, "Duplicate declaration: {}", s)
375            }
376            QasmParseError::IncludeCycle(stack) => {
377                write!(f, "Include cycle detected: ")?;
378                for (index, path) in stack.iter().enumerate() {
379                    if index > 0 {
380                        write!(f, " -> ")?;
381                    }
382                    write!(f, "{}", path.display())?;
383                }
384                Ok(())
385            }
386            QasmParseError::UnsupportedOpaqueGate(s) => {
387                write!(f, "Opaque gate '{}' cannot be invoked", s)
388            }
389        }
390    }
391}
392
393impl std::error::Error for QasmParseError {
394    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
395        match self {
396            QasmParseError::IoError(error) => Some(error),
397            _ => None,
398        }
399    }
400}
401
402impl From<CircuitError> for QasmParseError {
403    fn from(value: CircuitError) -> Self {
404        Self::ConversionError(value.to_string())
405    }
406}
407
408/// Default maximum recursion depth for gate expansion
409const DEFAULT_MAX_RECURSION_DEPTH: usize = 100;
410
411/// Converts OpenQASM AST to internal Circuit representation.
412///
413/// This struct performs multi-phase conversion:
414/// 1. **Discovery**: Collects register declarations and gate definitions
415/// 2. **Compilation**: Compiles custom gates into CircuitGate
416/// 3. **Generation**: Converts statements to circuit operations
417struct AstToCircuit {
418    /// Quantum register name -> size mapping
419    qregs: HashMap<String, i64>,
420    /// Declaration order for deterministic qubit indexing
421    qreg_order: Vec<String>,
422    /// Classical register name -> size mapping
423    cregs: HashMap<String, i64>,
424    /// Declaration order for deterministic classical-variable allocation.
425    creg_order: Vec<String>,
426    /// Classical register name -> circuit-local BitVec variable.
427    creg_vars: HashMap<String, ClassicalVar>,
428    /// Custom gate definitions (name -> definition)
429    custom_gates: HashMap<String, CustomGateDef>,
430    /// User declarations across the OpenQASM namespace.
431    declarations: HashMap<String, DeclarationKind>,
432    /// Base path for resolving relative include paths
433    base_path: Option<PathBuf>,
434    /// Optional path of the top-level file being loaded.
435    root_path: Option<PathBuf>,
436    /// Parsed include cache (path -> AST) to avoid re-parsing
437    file_cache: HashMap<PathBuf, OpenQASMProgram>,
438    /// Current gate expansion depth (for recursion limiting)
439    recursion_depth: usize,
440    /// Maximum allowed recursion depth (default: 100)
441    max_recursion_depth: usize,
442    /// Gates currently being compiled (for cycle detection)
443    compiling_gates: HashSet<String>,
444    /// File system abstraction
445    resolver: Box<dyn QasmSourceResolver>,
446    qelib1_included: bool,
447}
448
449#[derive(Clone, Copy, Debug, Eq, PartialEq)]
450enum DeclarationKind {
451    QReg,
452    CReg,
453    Gate,
454    Opaque,
455}
456
457/// A custom gate definition that stores either AST or compiled CircuitGate
458#[derive(Clone, Debug)]
459struct CustomGateDef {
460    #[allow(dead_code)]
461    name: String,
462    params: Vec<String>,
463    qubits: Vec<String>,
464    /// The AST body compiled after discovery
465    body: Vec<Statement>,
466    /// The compiled circuit gate - None until compiled, None for opaque gates
467    circuit_gate: Option<CircuitGate>,
468    /// Whether this is an opaque gate
469    is_opaque: bool,
470}
471
472impl AstToCircuit {
473    #[cfg(test)]
474    fn new(base_path: Option<PathBuf>, resolver: Box<dyn QasmSourceResolver>) -> Self {
475        Self::new_with_root(base_path, None, resolver)
476    }
477
478    fn new_with_root(
479        base_path: Option<PathBuf>,
480        root_path: Option<PathBuf>,
481        resolver: Box<dyn QasmSourceResolver>,
482    ) -> Self {
483        let mut converter = Self {
484            qregs: HashMap::new(),
485            qreg_order: Vec::new(),
486            cregs: HashMap::new(),
487            creg_order: Vec::new(),
488            creg_vars: HashMap::new(),
489            custom_gates: HashMap::new(),
490            declarations: HashMap::new(),
491            base_path,
492            root_path,
493            file_cache: HashMap::new(),
494            recursion_depth: 0,
495            max_recursion_depth: DEFAULT_MAX_RECURSION_DEPTH,
496            compiling_gates: HashSet::new(),
497            resolver,
498            qelib1_included: false,
499        };
500        converter
501            .register_qelib1_include()
502            .expect("built-in qelib1 gate construction should not fail");
503        converter
504    }
505
506    fn convert(&mut self, program: &OpenQASMProgram) -> Result<Circuit, QasmParseError> {
507        // Phase 1: Discovery
508        // Recursively traverse includes to find all qregs and gate definitions
509        let current_dir = self.base_path.clone();
510        let mut include_stack = self.initial_include_stack();
511        self.discovery_pass_with_context(program, current_dir.as_deref(), &mut include_stack)?;
512
513        // Phase 1.5: Compile all custom gates
514        // This must happen after all gate definitions are discovered
515        self.compile_all_gates()?;
516
517        // Calculate total qubits
518        let total_qubits = self.qregs.values().try_fold(0usize, |total, size| {
519            total.checked_add(*size as usize).ok_or_else(|| {
520                QasmParseError::ConversionError(
521                    "Total quantum register size exceeds platform limits".to_string(),
522                )
523            })
524        })?;
525        let mut circuit = Circuit::new(total_qubits);
526
527        for name in &self.creg_order {
528            let width = self.cregs[name] as u32;
529            let ty = ClassicalType::bit_vec(width).ok_or_else(|| {
530                QasmParseError::ConversionError(format!(
531                    "Classical register '{name}' must have non-zero width"
532                ))
533            })?;
534            let var = circuit.var(ty);
535            let zero =
536                ClassicalExpr::pack_bits((0..width).map(|_| ClassicalExpr::bit_literal(false)))
537                    .map_err(|e| QasmParseError::ConversionError(e.to_string()))?;
538            circuit
539                .store(var, zero)
540                .map_err(|e| QasmParseError::ConversionError(e.to_string()))?;
541            self.creg_vars.insert(name.clone(), var);
542        }
543
544        // Create the register start map: reg_name -> global_start_index
545        let mut reg_start_map: HashMap<String, usize> = HashMap::new();
546        let mut global_idx = 0;
547
548        // Use declaration order for qubit mapping
549        for name in &self.qreg_order {
550            reg_start_map.insert(name.clone(), global_idx);
551            let size = self.qregs[name];
552            global_idx += size as usize;
553        }
554
555        // Phase 2: Generation
556        // Process operations
557        let mut include_stack = self.initial_include_stack();
558        self.generation_pass(
559            program,
560            &mut circuit,
561            &reg_start_map,
562            current_dir.as_deref(),
563            &mut include_stack,
564        )?;
565
566        Ok(circuit)
567    }
568
569    fn initial_include_stack(&self) -> Vec<PathBuf> {
570        self.root_path.iter().cloned().collect()
571    }
572
573    /// Compile all custom gate definitions
574    fn compile_all_gates(&mut self) -> Result<(), QasmParseError> {
575        // Get list of gate names to compile
576        let gate_names: Vec<String> = self.custom_gates.keys().cloned().collect();
577
578        for name in gate_names {
579            self.compile_gate_if_needed(&name)?;
580        }
581
582        Ok(())
583    }
584
585    /// Compile a single gate if not already compiled
586    fn compile_gate_if_needed(
587        &mut self,
588        name: &str,
589    ) -> Result<Option<CircuitGate>, QasmParseError> {
590        // Check recursion depth
591        if self.recursion_depth >= self.max_recursion_depth {
592            return Err(QasmParseError::RecursionLimitExceeded(format!(
593                "Gate expansion depth exceeded limit of {} (compiling {})",
594                self.max_recursion_depth, name
595            )));
596        }
597
598        // Check for circular dependency: if the gate is already being compiled, we have a cycle
599        if self.compiling_gates.contains(name) {
600            return Err(QasmParseError::CircularGateDependency {
601                gate: name.to_string(),
602                dependency: name.to_string(),
603            });
604        }
605
606        // Check if already compiled
607        if let Some(def) = self.custom_gates.get(name) {
608            if def.is_opaque {
609                return Ok(None);
610            }
611            if def.circuit_gate.is_some() {
612                return Ok(def.circuit_gate.clone());
613            }
614        } else {
615            return Err(QasmParseError::UndefinedGate(name.to_string()));
616        }
617
618        // Clone needed data to avoid borrow issues
619        let (params, qubits, body) = if let Some(def) = self.custom_gates.get(name) {
620            (def.params.clone(), def.qubits.clone(), def.body.clone())
621        } else {
622            return Err(QasmParseError::UndefinedGate(name.to_string()));
623        };
624
625        // Mark this gate as being compiled (for cycle detection)
626        self.compiling_gates.insert(name.to_string());
627
628        // Increment recursion depth
629        self.recursion_depth += 1;
630
631        let result = (|| {
632            // Ensure dependencies are compiled
633            for stmt in &body {
634                if let Statement::CustomGate(dep_name, _, _) = stmt
635                    && self.custom_gates.contains_key(dep_name)
636                {
637                    // Check if this dependency creates a cycle
638                    if self.compiling_gates.contains(dep_name) {
639                        return Err(QasmParseError::CircularGateDependency {
640                            gate: name.to_string(),
641                            dependency: dep_name.to_string(),
642                        });
643                    }
644                    self.compile_gate_if_needed(dep_name)?;
645                }
646            }
647
648            self.build_circuit_gate(name, &params, &qubits, &body)
649        })();
650
651        // Restore compilation state before propagating any error.
652        self.recursion_depth -= 1;
653        self.compiling_gates.remove(name);
654
655        let circuit_gate = result?;
656
657        // Store the compiled result
658        if let Some(def) = self.custom_gates.get_mut(name) {
659            def.circuit_gate = Some(circuit_gate.clone());
660        }
661
662        Ok(Some(circuit_gate))
663    }
664
665    fn register_qelib1_include(&mut self) -> Result<(), QasmParseError> {
666        if self.qelib1_included {
667            return Ok(());
668        }
669
670        debug_assert!(
671            QELIB1_DIRECT_GATES
672                .iter()
673                .chain(QELIB1_CUSTOM_GATES.iter())
674                .all(|name| QELIB1_RESERVED_GATE_NAMES.contains(name))
675        );
676
677        for decl in [
678            Self::qelib1_ch_gate()?,
679            Self::qelib1_cu1_gate()?,
680            Self::qelib1_cu3_gate()?,
681        ] {
682            self.custom_gates.insert(decl.name.clone(), decl);
683        }
684        self.qelib1_included = true;
685        Ok(())
686    }
687
688    fn qelib1_ch_gate() -> Result<CustomGateDef, QasmParseError> {
689        let a = Qubit::new(0);
690        let b = Qubit::new(1);
691        let mut circuit = Circuit::new(2);
692
693        circuit.h(b)?;
694        circuit.sdg(b)?;
695        circuit.cx(a, b)?;
696        circuit.h(b)?;
697        circuit.t(b)?;
698        circuit.cx(a, b)?;
699        circuit.t(b)?;
700        circuit.h(b)?;
701        circuit.s(b)?;
702        circuit.x(b)?;
703        circuit.s(a)?;
704
705        Self::qelib1_gate_def("ch", &[], &["a", "b"], circuit)
706    }
707
708    fn qelib1_cu1_gate() -> Result<CustomGateDef, QasmParseError> {
709        let a = Qubit::new(0);
710        let b = Qubit::new(1);
711        let lambda = Parameter::symbol("lambda");
712        let mut circuit = Circuit::new(2);
713        circuit.add_parameter(lambda.clone());
714
715        circuit.phase(a, lambda.clone() / 2.0)?;
716        circuit.cx(a, b)?;
717        circuit.phase(b, (Parameter::from(0.0) - lambda.clone()) / 2.0)?;
718        circuit.cx(a, b)?;
719        circuit.phase(b, lambda / 2.0)?;
720
721        Self::qelib1_gate_def("cu1", &["lambda"], &["a", "b"], circuit)
722    }
723
724    fn qelib1_cu3_gate() -> Result<CustomGateDef, QasmParseError> {
725        let c = Qubit::new(0);
726        let t = Qubit::new(1);
727        let theta = Parameter::symbol("theta");
728        let phi = Parameter::symbol("phi");
729        let lambda = Parameter::symbol("lambda");
730        let mut circuit = Circuit::new(2);
731        circuit.add_parameter(theta.clone());
732        circuit.add_parameter(phi.clone());
733        circuit.add_parameter(lambda.clone());
734
735        circuit.phase(t, (lambda.clone() - phi.clone()) / 2.0)?;
736        circuit.cx(c, t)?;
737        circuit.u(
738            t,
739            (Parameter::from(0.0) - theta.clone()) / 2.0,
740            0.0,
741            (Parameter::from(0.0) - (phi.clone() + lambda.clone())) / 2.0,
742        )?;
743        circuit.cx(c, t)?;
744        circuit.u(t, theta / 2.0, phi, 0.0)?;
745
746        Self::qelib1_gate_def("cu3", &["theta", "phi", "lambda"], &["c", "t"], circuit)
747    }
748
749    fn qelib1_gate_def(
750        name: &str,
751        params: &[&str],
752        qubits: &[&str],
753        circuit: Circuit,
754    ) -> Result<CustomGateDef, QasmParseError> {
755        let circuit_gate = CircuitGate::with_signature(
756            name,
757            FrozenCircuit::new(circuit),
758            params.iter().map(|param| (*param).to_string()),
759        )
760        .map_err(|e| QasmParseError::ConversionError(e.to_string()))?;
761        Ok(CustomGateDef {
762            name: name.to_string(),
763            params: params.iter().map(|param| (*param).to_string()).collect(),
764            qubits: qubits.iter().map(|qubit| (*qubit).to_string()).collect(),
765            body: Vec::new(),
766            circuit_gate: Some(circuit_gate),
767            is_opaque: false,
768        })
769    }
770
771    fn declare_name(&mut self, name: &str, kind: DeclarationKind) -> Result<(), QasmParseError> {
772        if let Some(existing) = self.declarations.get(name) {
773            return Err(QasmParseError::DuplicateDeclaration(format!(
774                "'{name}' declared as {existing:?} and {kind:?}"
775            )));
776        }
777        self.declarations.insert(name.to_string(), kind);
778        Ok(())
779    }
780
781    fn ensure_gate_name_allowed(&self, name: &str) -> Result<(), QasmParseError> {
782        if QELIB1_RESERVED_GATE_NAMES.contains(&name) {
783            return Err(QasmParseError::ReservedGateName(name.to_string()));
784        }
785        Ok(())
786    }
787
788    fn validate_gate_formals(params: &[String], qubits: &[String]) -> Result<(), QasmParseError> {
789        let mut seen_params = HashSet::new();
790        for param in params {
791            if !seen_params.insert(param) {
792                return Err(QasmParseError::InvalidArgument(format!(
793                    "Duplicate gate parameter formal '{param}'"
794                )));
795            }
796        }
797
798        let mut seen_qubits = HashSet::new();
799        for qubit in qubits {
800            if !seen_qubits.insert(qubit) {
801                return Err(QasmParseError::InvalidArgument(format!(
802                    "Duplicate gate qubit formal '{qubit}'"
803                )));
804            }
805            if seen_params.contains(qubit) {
806                return Err(QasmParseError::InvalidArgument(format!(
807                    "Gate formal '{qubit}' is both a parameter and a qubit"
808                )));
809            }
810        }
811
812        Ok(())
813    }
814
815    fn resolve_include_path(&self, current_dir: Option<&Path>, filename: &str) -> PathBuf {
816        let path = if let Some(base) = current_dir.or(self.base_path.as_deref()) {
817            base.join(filename)
818        } else {
819            PathBuf::from(filename)
820        };
821        normalize_path(path)
822    }
823
824    fn include_cycle_error(stack: &[PathBuf], target: &Path) -> QasmParseError {
825        let mut cycle = stack.to_vec();
826        cycle.push(target.to_path_buf());
827        QasmParseError::IncludeCycle(cycle)
828    }
829
830    fn parse_include(
831        &mut self,
832        target_path: &Path,
833        filename: &str,
834    ) -> Result<OpenQASMProgram, QasmParseError> {
835        if let Some(program) = self.file_cache.get(target_path) {
836            return Ok(program.clone());
837        }
838
839        let content = self.resolver.resolve_source(target_path).map_err(|e| {
840            QasmParseError::IoError(io::Error::new(
841                e.kind(),
842                format!("Include {}: {}", filename, e),
843            ))
844        })?;
845        let parser = parser::ProgramBodyParser::new();
846        let included_program = parser
847            .parse(&content)
848            .map_err(|e| QasmParseError::ParseError(format!("In {}: {:?}", filename, e)))?;
849
850        self.file_cache
851            .insert(target_path.to_path_buf(), included_program.clone());
852        Ok(included_program)
853    }
854
855    #[cfg(test)]
856    fn discovery_pass(&mut self, program: &OpenQASMProgram) -> Result<(), QasmParseError> {
857        let current_dir = self.base_path.clone();
858        let mut include_stack = self.initial_include_stack();
859        self.discovery_pass_with_context(program, current_dir.as_deref(), &mut include_stack)
860    }
861
862    fn discovery_pass_with_context(
863        &mut self,
864        program: &OpenQASMProgram,
865        current_dir: Option<&Path>,
866        include_stack: &mut Vec<PathBuf>,
867    ) -> Result<(), QasmParseError> {
868        for stmt in &program.statements {
869            match stmt {
870                Statement::QReg(name, size) => {
871                    if *size <= 0 {
872                        return Err(QasmParseError::InvalidArgument(format!(
873                            "Quantum register '{name}' must have positive width, got {size}"
874                        )));
875                    }
876                    self.declare_name(name, DeclarationKind::QReg)?;
877                    self.qregs.insert(name.clone(), *size);
878                    self.qreg_order.push(name.clone());
879                }
880                Statement::CReg(name, size) => {
881                    if *size <= 0 {
882                        return Err(QasmParseError::InvalidArgument(format!(
883                            "Classical register '{name}' must have positive width, got {size}"
884                        )));
885                    }
886                    self.declare_name(name, DeclarationKind::CReg)?;
887                    self.cregs.insert(name.clone(), *size);
888                    self.creg_order.push(name.clone());
889                }
890                Statement::GateDecl(data) => {
891                    self.ensure_gate_name_allowed(&data.name)?;
892                    self.declare_name(&data.name, DeclarationKind::Gate)?;
893                    Self::validate_gate_formals(&data.params, &data.qubits)?;
894
895                    // Store AST for compilation after discovery
896                    let decl = CustomGateDef {
897                        name: data.name.clone(),
898                        params: data.params.clone(),
899                        qubits: data.qubits.clone(),
900                        body: data.body.clone(),
901                        circuit_gate: None,
902                        is_opaque: false,
903                    };
904                    self.custom_gates.insert(data.name.clone(), decl);
905                }
906                Statement::Include(filename) => {
907                    if filename == "qelib1.inc" {
908                        self.register_qelib1_include()?;
909                        continue;
910                    }
911
912                    let target_path = self.resolve_include_path(current_dir, filename);
913                    if include_stack.contains(&target_path) {
914                        return Err(Self::include_cycle_error(include_stack, &target_path));
915                    }
916
917                    let should_discover = !self.file_cache.contains_key(&target_path);
918                    let included_program = self.parse_include(&target_path, filename)?;
919                    if should_discover {
920                        include_stack.push(target_path.clone());
921                        let next_dir = target_path.parent().map(Path::to_path_buf);
922                        self.discovery_pass_with_context(
923                            &included_program,
924                            next_dir.as_deref(),
925                            include_stack,
926                        )?;
927                        include_stack.pop();
928                    }
929                }
930                Statement::Opaque(name, params, qubits) => {
931                    self.ensure_gate_name_allowed(name)?;
932                    self.declare_name(name, DeclarationKind::Opaque)?;
933                    Self::validate_gate_formals(params, qubits)?;
934
935                    // Opaque gates have no body - they cannot be expanded
936                    let decl = CustomGateDef {
937                        name: name.clone(),
938                        params: params.clone(),
939                        qubits: qubits.clone(),
940                        body: vec![],
941                        circuit_gate: None,
942                        is_opaque: true,
943                    };
944                    self.custom_gates.insert(name.clone(), decl);
945                }
946                _ => {}
947            }
948        }
949        Ok(())
950    }
951
952    /// Build a CircuitGate from a gate declaration
953    fn build_circuit_gate(
954        &self,
955        name: &str,
956        params: &[String],
957        qubits: &[String],
958        body: &[Statement],
959    ) -> Result<CircuitGate, QasmParseError> {
960        // Create a circuit with qubits named after the gate's formal parameters
961        let num_qubits = qubits.len();
962        let mut gate_circuit = Circuit::new(num_qubits);
963
964        // Map formal qubit names to actual qubit indices
965        let mut qubit_map: HashMap<String, Qubit> = HashMap::new();
966        for (i, qubit_name) in qubits.iter().enumerate() {
967            qubit_map.insert(qubit_name.clone(), Qubit::new(i as u32));
968        }
969
970        // Build parameter map for symbol resolution
971        // In the gate body, parameter symbols should be bound to circuit parameters
972        let mut param_map: HashMap<String, Parameter> = HashMap::new();
973        for param_name in params.iter() {
974            // Create a parameter symbol for this gate parameter
975            let param = Parameter::symbol(param_name);
976            param_map.insert(param_name.clone(), param);
977        }
978
979        // Process each statement in the gate body
980        for stmt in body {
981            self.build_gate_body_statement(stmt, &mut gate_circuit, &qubit_map, &param_map)?;
982        }
983
984        // Convert to CircuitGate
985        let frozen = FrozenCircuit::new(gate_circuit);
986        CircuitGate::with_signature(name, frozen, params.iter().cloned())
987            .map_err(|e| QasmParseError::ConversionError(e.to_string()))
988    }
989
990    /// Build a single statement in a gate body
991    fn build_gate_body_statement(
992        &self,
993        stmt: &Statement,
994        circuit: &mut Circuit,
995        qubit_map: &HashMap<String, Qubit>,
996        param_map: &HashMap<String, Parameter>,
997    ) -> Result<(), QasmParseError> {
998        match stmt {
999            Statement::CustomGate(name, args, qargs) => {
1000                // Resolve qubits
1001                let mut resolved_qubits = Vec::new();
1002                for arg in qargs {
1003                    match arg {
1004                        Argument::Id(qname) => {
1005                            if let Some(&q) = qubit_map.get(qname) {
1006                                resolved_qubits.push(q);
1007                            } else {
1008                                return Err(QasmParseError::UndefinedQubit(qname.clone()));
1009                            }
1010                        }
1011                        Argument::IndexedId(_, _) => {
1012                            return Err(QasmParseError::InvalidArgument(
1013                                "Indexed arguments not supported in gate body".to_string(),
1014                            ));
1015                        }
1016                    }
1017                }
1018
1019                // Resolve parameters - convert expressions to Parameters
1020                let mut resolved_params: SmallVec<[ParameterValue; 3]> = smallvec![];
1021                for expr in args {
1022                    let param = Self::expr_to_parameter(expr, param_map)?;
1023                    resolved_params.push(ParameterValue::from(param));
1024                }
1025
1026                // Try to find the gate definition
1027                if let Some(gate_def) = self.custom_gates.get(name) {
1028                    if let Some(ref cg) = gate_def.circuit_gate {
1029                        // Add the CircuitGate directly
1030                        circuit
1031                            .append(
1032                                Instruction::CircuitGate(Box::new(cg.clone())),
1033                                resolved_qubits,
1034                                resolved_params,
1035                                None,
1036                            )
1037                            .map_err(|e| QasmParseError::ConversionError(e.to_string()))?;
1038                    } else {
1039                        // Opaque gate - cannot be added to circuit body
1040                        return Err(QasmParseError::UndefinedGate(format!(
1041                            "Opaque gate {} cannot be used in gate body",
1042                            name
1043                        )));
1044                    }
1045                } else {
1046                    // Try standard gate
1047                    self.append_standard_gate(circuit, name, &resolved_params, &resolved_qubits)?;
1048                }
1049            }
1050            Statement::Barrier(args) => {
1051                let mut resolved_qubits = Vec::new();
1052                for arg in args {
1053                    match arg {
1054                        Argument::Id(qname) => {
1055                            if let Some(&q) = qubit_map.get(qname) {
1056                                resolved_qubits.push(q);
1057                            } else {
1058                                return Err(QasmParseError::UndefinedQubit(qname.clone()));
1059                            }
1060                        }
1061                        Argument::IndexedId(_, _) => {
1062                            return Err(QasmParseError::InvalidArgument(
1063                                "Indexed barrier arguments are not supported in gate body"
1064                                    .to_string(),
1065                            ));
1066                        }
1067                    }
1068                }
1069                circuit
1070                    .append(
1071                        Instruction::Directive(Directive::Barrier),
1072                        resolved_qubits,
1073                        std::iter::empty::<ParameterValue>(),
1074                        None,
1075                    )
1076                    .map_err(|e| QasmParseError::ConversionError(e.to_string()))?;
1077            }
1078            _ => {
1079                return Err(QasmParseError::ConversionError(format!(
1080                    "Unsupported statement type in gate body: {:?}",
1081                    stmt
1082                )));
1083            }
1084        }
1085        Ok(())
1086    }
1087
1088    /// Convert an expression to a Parameter
1089    fn expr_to_parameter(
1090        expr: &Expression,
1091        param_map: &HashMap<String, Parameter>,
1092    ) -> Result<Parameter, QasmParseError> {
1093        match expr {
1094            Expression::Real(v) => Ok(Parameter::from(*v)),
1095            Expression::Integer(v) => Ok(Parameter::from(*v as f64)),
1096            Expression::Pi => Ok(Parameter::pi()),
1097            Expression::Id(name) => {
1098                if let Some(param) = param_map.get(name) {
1099                    Ok(param.clone())
1100                } else {
1101                    // Try to evaluate as a constant or return error
1102                    Err(QasmParseError::EvaluationError(format!(
1103                        "Unknown parameter: {}",
1104                        name
1105                    )))
1106                }
1107            }
1108            Expression::BinaryOp(left, op, right) => {
1109                let l = Self::expr_to_parameter(left, param_map)?;
1110                let r = Self::expr_to_parameter(right, param_map)?;
1111                Ok(match op {
1112                    OpCode::Add => l + r,
1113                    OpCode::Sub => l - r,
1114                    OpCode::Mul => l * r,
1115                    OpCode::Div => {
1116                        // Check for division by zero in constant case
1117                        if let Ok(val) = r.evaluate(&None)
1118                            && val == 0.0
1119                        {
1120                            return Err(QasmParseError::EvaluationError(
1121                                "Division by zero".to_string(),
1122                            ));
1123                        }
1124                        l / r
1125                    }
1126                    OpCode::Pow => l.pow(r),
1127                })
1128            }
1129            Expression::UnaryOp(op, expr) => {
1130                let v = Self::expr_to_parameter(expr, param_map)?;
1131                Ok(match op {
1132                    UnaryOpCode::Sin => v.sin(),
1133                    UnaryOpCode::Cos => v.cos(),
1134                    UnaryOpCode::Tan => v.tan(),
1135                    UnaryOpCode::Exp => v.exp(),
1136                    UnaryOpCode::Ln => v.ln(),
1137                    UnaryOpCode::Sqrt => v.sqrt(),
1138                    UnaryOpCode::Asin => v.asin(),
1139                    UnaryOpCode::Acos => v.acos(),
1140                    UnaryOpCode::Atan => v.atan(),
1141                    UnaryOpCode::Neg => Parameter::from(0.0) - v,
1142                })
1143            }
1144        }
1145    }
1146
1147    /// Build a standard gate instruction with validated parameters.
1148    ///
1149    /// This centralized function handles all standard gate name matching, qubit/parameter count
1150    /// validation, and special parameter transformations (like U2).
1151    ///
1152    /// Returns the Instruction and the validated ParameterValues to use.
1153    fn build_standard_gate_instruction(
1154        name: &str,
1155        params: &[ParameterValue],
1156        qubits: &[Qubit],
1157    ) -> Result<(Instruction, Vec<ParameterValue>), QasmParseError> {
1158        // Helper closure for checking qubit and parameter counts
1159        let check_counts = |req_q: usize, req_p: usize| -> Result<(), QasmParseError> {
1160            if qubits.len() != req_q {
1161                return Err(QasmParseError::MismatchedQubitCount {
1162                    expected: req_q,
1163                    actual: qubits.len(),
1164                });
1165            }
1166            if params.len() != req_p {
1167                return Err(QasmParseError::MismatchedParameterCount {
1168                    expected: req_p,
1169                    actual: params.len(),
1170                });
1171            }
1172            Ok(())
1173        };
1174
1175        match name {
1176            "u2" | "U2" => {
1177                check_counts(1, 2)?;
1178                // U2(phi, lambda) = U(pi/2, phi, lambda)
1179                let pi_2 = ParameterValue::Param(Parameter::pi() / 2.0);
1180                let new_params = vec![pi_2, params[0].clone(), params[1].clone()];
1181                Ok((Instruction::Standard(StandardGate::U), new_params))
1182            }
1183
1184            "rx" | "RX" => {
1185                check_counts(1, 1)?;
1186                Ok((Instruction::Standard(StandardGate::RX), params.to_vec()))
1187            }
1188            "ry" | "RY" => {
1189                check_counts(1, 1)?;
1190                Ok((Instruction::Standard(StandardGate::RY), params.to_vec()))
1191            }
1192            "rz" | "RZ" => {
1193                check_counts(1, 1)?;
1194                Ok((Instruction::Standard(StandardGate::RZ), params.to_vec()))
1195            }
1196            "p" | "P" => {
1197                check_counts(1, 1)?;
1198                Ok((Instruction::Standard(StandardGate::Phase), params.to_vec()))
1199            }
1200            "u1" | "U1" => {
1201                check_counts(1, 1)?;
1202                Ok((Instruction::Standard(StandardGate::Phase), params.to_vec()))
1203            }
1204
1205            // --- Standard Parametrized Gates (1 qubit, 3 params) ---
1206            "u3" | "U3" | "u" | "U" => {
1207                check_counts(1, 3)?;
1208                Ok((Instruction::Standard(StandardGate::U), params.to_vec()))
1209            }
1210
1211            "rxx" | "RXX" => {
1212                check_counts(2, 1)?;
1213                Ok((Instruction::Standard(StandardGate::RXX), params.to_vec()))
1214            }
1215            "ryy" | "RYY" => {
1216                check_counts(2, 1)?;
1217                Ok((Instruction::Standard(StandardGate::RYY), params.to_vec()))
1218            }
1219            "rzz" | "RZZ" => {
1220                check_counts(2, 1)?;
1221                Ok((Instruction::Standard(StandardGate::RZZ), params.to_vec()))
1222            }
1223            "fsim" | "FSIM" => {
1224                check_counts(2, 2)?;
1225                Ok((Instruction::Standard(StandardGate::FSIM), params.to_vec()))
1226            }
1227
1228            // --- Controlled rotation gates (2 qubits, 1 param) ---
1229            "crx" | "CRX" => {
1230                check_counts(2, 1)?;
1231                Ok((Instruction::Standard(StandardGate::CRX), params.to_vec()))
1232            }
1233            "cry" | "CRY" => {
1234                check_counts(2, 1)?;
1235                Ok((Instruction::Standard(StandardGate::CRY), params.to_vec()))
1236            }
1237            "crz" | "CRZ" => {
1238                check_counts(2, 1)?;
1239                Ok((Instruction::Standard(StandardGate::CRZ), params.to_vec()))
1240            }
1241
1242            "h" | "H" => {
1243                check_counts(1, 0)?;
1244                Ok((Instruction::Standard(StandardGate::H), vec![]))
1245            }
1246            "x" | "X" => {
1247                check_counts(1, 0)?;
1248                Ok((Instruction::Standard(StandardGate::X), vec![]))
1249            }
1250            "y" | "Y" => {
1251                check_counts(1, 0)?;
1252                Ok((Instruction::Standard(StandardGate::Y), vec![]))
1253            }
1254            "z" | "Z" => {
1255                check_counts(1, 0)?;
1256                Ok((Instruction::Standard(StandardGate::Z), vec![]))
1257            }
1258            "s" | "S" => {
1259                check_counts(1, 0)?;
1260                Ok((Instruction::Standard(StandardGate::S), vec![]))
1261            }
1262            "sdg" | "SDG" => {
1263                check_counts(1, 0)?;
1264                Ok((Instruction::Standard(StandardGate::SDG), vec![]))
1265            }
1266            "t" | "T" => {
1267                check_counts(1, 0)?;
1268                Ok((Instruction::Standard(StandardGate::T), vec![]))
1269            }
1270            "tdg" | "TDG" => {
1271                check_counts(1, 0)?;
1272                Ok((Instruction::Standard(StandardGate::TDG), vec![]))
1273            }
1274            "sx" | "SX" => {
1275                check_counts(1, 0)?;
1276                Ok((Instruction::Standard(StandardGate::X2P), vec![]))
1277            }
1278            "sxdg" | "SXDG" => {
1279                check_counts(1, 0)?;
1280                Ok((Instruction::Standard(StandardGate::X2M), vec![]))
1281            }
1282            "id" | "ID" | "i" | "I" => {
1283                check_counts(1, 0)?;
1284                Ok((Instruction::Standard(StandardGate::I), vec![]))
1285            }
1286            "cx" | "CX" => {
1287                check_counts(2, 0)?;
1288                Ok((Instruction::Standard(StandardGate::CX), vec![]))
1289            }
1290            "cy" | "CY" => {
1291                check_counts(2, 0)?;
1292                Ok((Instruction::Standard(StandardGate::CY), vec![]))
1293            }
1294            "cz" | "CZ" => {
1295                check_counts(2, 0)?;
1296                Ok((Instruction::Standard(StandardGate::CZ), vec![]))
1297            }
1298            "swap" | "SWAP" => {
1299                check_counts(2, 0)?;
1300                Ok((Instruction::Standard(StandardGate::SWAP), vec![]))
1301            }
1302            "ccx" | "CCX" | "toffoli" | "TOFFOLI" => {
1303                check_counts(3, 0)?;
1304                Ok((Instruction::Standard(StandardGate::CCX), vec![]))
1305            }
1306
1307            _ => Err(QasmParseError::UndefinedGate(name.to_string())),
1308        }
1309    }
1310
1311    /// Append a standard gate to the circuit (used in gate declarations)
1312    fn append_standard_gate(
1313        &self,
1314        circuit: &mut Circuit,
1315        name: &str,
1316        params: &[ParameterValue],
1317        qubits: &[Qubit],
1318    ) -> Result<(), QasmParseError> {
1319        let (instruction, valid_params) =
1320            Self::build_standard_gate_instruction(name, params, qubits)?;
1321        circuit
1322            .append(instruction, qubits.to_vec(), valid_params, None)
1323            .map_err(|e| QasmParseError::ConversionError(e.to_string()))?;
1324        Ok(())
1325    }
1326
1327    fn generation_pass(
1328        &mut self,
1329        program: &OpenQASMProgram,
1330        circuit: &mut Circuit,
1331        reg_start_map: &HashMap<String, usize>,
1332        current_dir: Option<&Path>,
1333        include_stack: &mut Vec<PathBuf>,
1334    ) -> Result<(), QasmParseError> {
1335        for stmt in &program.statements {
1336            match stmt {
1337                Statement::Include(filename) => {
1338                    if filename == "qelib1.inc" {
1339                        continue;
1340                    }
1341
1342                    let target_path = self.resolve_include_path(current_dir, filename);
1343                    if include_stack.contains(&target_path) {
1344                        return Err(Self::include_cycle_error(include_stack, &target_path));
1345                    }
1346
1347                    // Retrieve from cache instead of re-parsing
1348                    // Clone the program to avoid borrow issues
1349                    let included_program = self.file_cache.get(&target_path).cloned();
1350                    if let Some(included) = included_program {
1351                        include_stack.push(target_path.clone());
1352                        let next_dir = target_path.parent().map(Path::to_path_buf);
1353                        self.generation_pass(
1354                            &included,
1355                            circuit,
1356                            reg_start_map,
1357                            next_dir.as_deref(),
1358                            include_stack,
1359                        )?;
1360                        include_stack.pop();
1361                    } else {
1362                        // Should not happen if discovery pass worked correctly
1363                        return Err(QasmParseError::ConversionError(format!(
1364                            "Included file not found in cache: {:?}",
1365                            target_path
1366                        )));
1367                    }
1368                }
1369                Statement::Barrier(args) => {
1370                    let qubits = self.resolve_global_args(args, reg_start_map)?;
1371                    circuit
1372                        .barrier(qubits)
1373                        .map_err(|e| QasmParseError::ConversionError(e.to_string()))?;
1374                }
1375                Statement::Reset(arg) => {
1376                    // Reset can be applied to single qubit or entire register
1377                    let qubits = self.resolve_global_args_single_or_register(arg, reg_start_map)?;
1378                    for qubit in qubits {
1379                        circuit
1380                            .reset(qubit)
1381                            .map_err(|e| QasmParseError::ConversionError(e.to_string()))?;
1382                    }
1383                }
1384                Statement::Measure(qarg, carg) => {
1385                    self.append_measurement(circuit, qarg, carg, reg_start_map)?;
1386                }
1387                Statement::CustomGate(name, params, args) => {
1388                    self.append_gate_invocation(circuit, name, params, args, reg_start_map)?;
1389                }
1390                Statement::If(creg, value, stmt) => {
1391                    let condition = self.build_condition(creg, *value)?;
1392                    circuit
1393                        .if_(condition, |body| {
1394                            self.append_conditional_statement(stmt, body, reg_start_map)
1395                                .map_err(|e| CircuitError::InvalidOperation(e.to_string()))
1396                        })
1397                        .map_err(|e| QasmParseError::ConversionError(e.to_string()))?;
1398                }
1399                _ => {} // Declarations already handled
1400            }
1401        }
1402        Ok(())
1403    }
1404
1405    fn append_gate_invocation(
1406        &self,
1407        circuit: &mut Circuit,
1408        name: &str,
1409        params: &[Expression],
1410        args: &[Argument],
1411        reg_start_map: &HashMap<String, usize>,
1412    ) -> Result<(), QasmParseError> {
1413        let param_values = Self::resolve_invocation_params(params)?;
1414        let arg_groups = args
1415            .iter()
1416            .map(|arg| self.resolve_global_args_single_or_register(arg, reg_start_map))
1417            .collect::<Result<Vec<_>, _>>()?;
1418
1419        let width = arg_groups.iter().map(Vec::len).max().ok_or_else(|| {
1420            QasmParseError::InvalidArgument(format!("Gate '{name}' has no arguments"))
1421        })?;
1422
1423        for group in &arg_groups {
1424            if group.len() != 1 && group.len() != width {
1425                return Err(QasmParseError::InvalidArgument(format!(
1426                    "Cannot broadcast gate '{name}': argument widths must be 1 or {width}, got {}",
1427                    group.len()
1428                )));
1429            }
1430        }
1431
1432        for index in 0..width {
1433            let qubits = arg_groups
1434                .iter()
1435                .map(|group| group[if group.len() == 1 { 0 } else { index }])
1436                .collect::<Vec<_>>();
1437            self.append_single_gate_invocation(circuit, name, param_values.clone(), qubits)?;
1438        }
1439
1440        Ok(())
1441    }
1442
1443    fn resolve_invocation_params(
1444        params: &[Expression],
1445    ) -> Result<SmallVec<[ParameterValue; 3]>, QasmParseError> {
1446        let mut param_values: SmallVec<[ParameterValue; 3]> = smallvec![];
1447        let empty_param_map: HashMap<String, Parameter> = HashMap::new();
1448        for expr in params {
1449            let param = Self::expr_to_parameter(expr, &empty_param_map)?;
1450            param_values.push(ParameterValue::from(param));
1451        }
1452        Ok(param_values)
1453    }
1454
1455    fn append_single_gate_invocation(
1456        &self,
1457        circuit: &mut Circuit,
1458        name: &str,
1459        params: SmallVec<[ParameterValue; 3]>,
1460        qubits: Vec<Qubit>,
1461    ) -> Result<(), QasmParseError> {
1462        if let Some(gate_def) = self.custom_gates.get(name) {
1463            if gate_def.is_opaque {
1464                return Err(QasmParseError::UnsupportedOpaqueGate(name.to_string()));
1465            }
1466            if let Some(ref cg) = gate_def.circuit_gate {
1467                circuit
1468                    .append(
1469                        Instruction::CircuitGate(Box::new(cg.clone())),
1470                        qubits,
1471                        params,
1472                        None,
1473                    )
1474                    .map_err(|e| QasmParseError::ConversionError(e.to_string()))?;
1475                return Ok(());
1476            }
1477            return Err(QasmParseError::UndefinedGate(name.to_string()));
1478        }
1479
1480        self.append_standard_gate_to_circuit(circuit, name, &params, &qubits)
1481    }
1482
1483    fn append_measurement(
1484        &self,
1485        circuit: &mut Circuit,
1486        qarg: &Argument,
1487        carg: &Argument,
1488        reg_start_map: &HashMap<String, usize>,
1489    ) -> Result<(), QasmParseError> {
1490        let qubits = self.resolve_global_args_single_or_register(qarg, reg_start_map)?;
1491        let creg_indices = self.resolve_creg_indices(carg)?;
1492
1493        if qubits.len() != creg_indices.len() {
1494            return Err(QasmParseError::InvalidArgument(format!(
1495                "Measure qubit count ({}) does not match classical register count ({})",
1496                qubits.len(),
1497                creg_indices.len()
1498            )));
1499        }
1500
1501        let creg_name = match carg {
1502            Argument::Id(name) | Argument::IndexedId(name, _) => name,
1503        };
1504        let target = *self
1505            .creg_vars
1506            .get(creg_name)
1507            .ok_or_else(|| QasmParseError::UndefinedRegister(creg_name.clone()))?;
1508
1509        if matches!(carg, Argument::Id(_)) {
1510            let result = circuit.measure_bits(qubits)?;
1511            circuit.store(target, result.expr())?;
1512        } else {
1513            let result = circuit.measure(qubits[0])?;
1514            let width = target.ty().width();
1515            let measured_index = creg_indices[0] as u32;
1516            let mut bits = Vec::with_capacity(width as usize);
1517            for index in 0..width {
1518                let bit = if index == measured_index {
1519                    result.expr()
1520                } else {
1521                    ClassicalExpr::extract_bit(target.expr(), index)
1522                        .map_err(|e| QasmParseError::ConversionError(e.to_string()))?
1523                };
1524                bits.push(bit);
1525            }
1526            let packed = ClassicalExpr::pack_bits(bits)
1527                .map_err(|e| QasmParseError::ConversionError(e.to_string()))?;
1528            circuit.store(target, packed)?;
1529        }
1530        Ok(())
1531    }
1532
1533    fn append_conditional_statement(
1534        &mut self,
1535        stmt: &Statement,
1536        circuit: &mut Circuit,
1537        reg_start_map: &HashMap<String, usize>,
1538    ) -> Result<(), QasmParseError> {
1539        match stmt {
1540            Statement::CustomGate(name, params, args) => {
1541                self.append_gate_invocation(circuit, name, params, args, reg_start_map)?;
1542            }
1543            Statement::Reset(arg) => {
1544                let qubits = self.resolve_global_args_single_or_register(arg, reg_start_map)?;
1545                for qubit in qubits {
1546                    circuit.reset(qubit)?;
1547                }
1548            }
1549            Statement::Measure(qarg, carg) => {
1550                self.append_measurement(circuit, qarg, carg, reg_start_map)?;
1551            }
1552            Statement::Barrier(_) => {
1553                return Err(QasmParseError::ConversionError(
1554                    "Barrier in an OpenQASM 2.0 conditional body is not supported".to_string(),
1555                ));
1556            }
1557            _ => {
1558                return Err(QasmParseError::ConversionError(format!(
1559                    "Unsupported statement type in if-body: {:?}",
1560                    stmt
1561                )));
1562            }
1563        }
1564        Ok(())
1565    }
1566
1567    fn resolve_global_args(
1568        &self,
1569        args: &[Argument],
1570        reg_start_map: &HashMap<String, usize>,
1571    ) -> Result<Vec<Qubit>, QasmParseError> {
1572        let mut qubits = Vec::new();
1573        for arg in args {
1574            match arg {
1575                Argument::Id(name) => {
1576                    // Expand whole register
1577                    if let Some(size) = self.qregs.get(name) {
1578                        if let Some(&start_idx) = reg_start_map.get(name) {
1579                            for i in 0..*size {
1580                                qubits.push(Qubit::new((start_idx as i64 + i) as u32));
1581                            }
1582                        } else {
1583                            return Err(QasmParseError::UndefinedRegister(name.clone()));
1584                        }
1585                    } else {
1586                        return Err(QasmParseError::UndefinedRegister(name.clone()));
1587                    }
1588                }
1589                Argument::IndexedId(name, idx) => {
1590                    if let Some(&start_idx) = reg_start_map.get(name) {
1591                        // Validate index
1592                        if let Some(size) = self.qregs.get(name) {
1593                            if *idx < 0 || *idx >= *size {
1594                                return Err(QasmParseError::UndefinedQubit(format!(
1595                                    "{}[{}] out of bounds",
1596                                    name, idx
1597                                )));
1598                            }
1599                            qubits.push(Qubit::new((start_idx as i64 + *idx) as u32));
1600                        } else {
1601                            return Err(QasmParseError::UndefinedRegister(name.clone()));
1602                        }
1603                    } else {
1604                        return Err(QasmParseError::UndefinedQubit(format!("{}[{}]", name, idx)));
1605                    }
1606                }
1607            }
1608        }
1609        Ok(qubits)
1610    }
1611
1612    /// Resolve argument to either single qubit or expanded register
1613    fn resolve_global_args_single_or_register(
1614        &self,
1615        arg: &Argument,
1616        reg_start_map: &HashMap<String, usize>,
1617    ) -> Result<Vec<Qubit>, QasmParseError> {
1618        let mut qubits = Vec::new();
1619        match arg {
1620            Argument::Id(name) => {
1621                // Expand entire register
1622                if let Some(size) = self.qregs.get(name) {
1623                    if let Some(&start_idx) = reg_start_map.get(name) {
1624                        for i in 0..*size {
1625                            qubits.push(Qubit::new((start_idx as i64 + i) as u32));
1626                        }
1627                    } else {
1628                        return Err(QasmParseError::UndefinedRegister(name.clone()));
1629                    }
1630                } else {
1631                    return Err(QasmParseError::UndefinedRegister(name.clone()));
1632                }
1633            }
1634            Argument::IndexedId(name, idx) => {
1635                // Single indexed qubit
1636                if let Some(&start_idx) = reg_start_map.get(name) {
1637                    if let Some(size) = self.qregs.get(name) {
1638                        if *idx < 0 || *idx >= *size {
1639                            return Err(QasmParseError::UndefinedQubit(format!(
1640                                "{}[{}]",
1641                                name, idx
1642                            )));
1643                        }
1644                        qubits.push(Qubit::new((start_idx as i64 + *idx) as u32));
1645                    } else {
1646                        return Err(QasmParseError::UndefinedRegister(name.clone()));
1647                    }
1648                } else {
1649                    return Err(QasmParseError::UndefinedQubit(format!("{}[{}]", name, idx)));
1650                }
1651            }
1652        }
1653        Ok(qubits)
1654    }
1655
1656    /// Resolve classical register argument to indices
1657    fn resolve_creg_indices(&self, arg: &Argument) -> Result<Vec<usize>, QasmParseError> {
1658        let mut indices = Vec::new();
1659        match arg {
1660            Argument::Id(name) => {
1661                // Entire classical register
1662                if let Some(size) = self.cregs.get(name) {
1663                    for i in 0..*size {
1664                        indices.push(i as usize);
1665                    }
1666                } else {
1667                    return Err(QasmParseError::UndefinedRegister(format!(
1668                        "Classical register '{}' not defined",
1669                        name
1670                    )));
1671                }
1672            }
1673            Argument::IndexedId(name, idx) => {
1674                let size = self.cregs.get(name).ok_or_else(|| {
1675                    QasmParseError::UndefinedRegister(format!(
1676                        "Classical register '{}' not defined",
1677                        name
1678                    ))
1679                })?;
1680                if *idx < 0 || *idx >= *size {
1681                    return Err(QasmParseError::InvalidArgument(format!(
1682                        "Classical register index {name}[{idx}] is out of bounds for width {size}"
1683                    )));
1684                }
1685                indices.push(*idx as usize);
1686            }
1687        }
1688        Ok(indices)
1689    }
1690
1691    fn build_condition(&self, name: &str, value: i64) -> Result<ClassicalExpr, QasmParseError> {
1692        let var = *self
1693            .creg_vars
1694            .get(name)
1695            .ok_or_else(|| QasmParseError::UndefinedRegister(name.to_string()))?;
1696        let width = var.ty().width();
1697
1698        if value < 0 {
1699            return Err(QasmParseError::InvalidArgument(format!(
1700                "Classical condition value must be non-negative, got {value}"
1701            )));
1702        }
1703
1704        let value = value as u128;
1705        if width < 128 && value >= (1u128 << width) {
1706            return Err(QasmParseError::InvalidArgument(format!(
1707                "Condition value {value} does not fit classical register '{name}' width {width}"
1708            )));
1709        }
1710        let lhs = ClassicalExpr::bit_vec_to_uint(var.expr())
1711            .map_err(|e| QasmParseError::ConversionError(e.to_string()))?;
1712        let rhs = ClassicalExpr::uint_literal(width, value)
1713            .map_err(|e| QasmParseError::ConversionError(e.to_string()))?;
1714
1715        ClassicalExpr::eq(lhs, rhs).map_err(|e| QasmParseError::ConversionError(e.to_string()))
1716    }
1717
1718    /// Append a standard gate to the circuit using ParameterValues (for top-level circuit)
1719    fn append_standard_gate_to_circuit(
1720        &self,
1721        circuit: &mut Circuit,
1722        name: &str,
1723        params: &[ParameterValue],
1724        qubits: &[Qubit],
1725    ) -> Result<(), QasmParseError> {
1726        let (instruction, valid_params) =
1727            Self::build_standard_gate_instruction(name, params, qubits)?;
1728        circuit
1729            .append(instruction, qubits.to_vec(), valid_params, None)
1730            .map_err(|e| QasmParseError::ConversionError(e.to_string()))?;
1731        Ok(())
1732    }
1733}
1734
1735fn normalize_path(path: impl AsRef<Path>) -> PathBuf {
1736    let mut normalized = PathBuf::new();
1737    for component in path.as_ref().components() {
1738        match component {
1739            Component::CurDir => {}
1740            Component::ParentDir => {
1741                normalized.pop();
1742            }
1743            Component::Normal(part) => normalized.push(part),
1744            Component::RootDir => normalized.push(component.as_os_str()),
1745            Component::Prefix(prefix) => normalized.push(prefix.as_os_str()),
1746        }
1747    }
1748    normalized
1749}
1750
1751#[cfg(test)]
1752#[path = "./load_test.rs"]
1753mod load_test;