quantrs2_circuit/qasm/
validator.rs

1//! Validator for `OpenQASM` 3.0 programs
2
3use super::ast::{
4    BinaryOp, ClassicalRef, Condition, Declaration, Expression, Literal, Measurement, QasmGate,
5    QasmProgram, QasmStatement, QubitRef, UnaryOp,
6};
7use std::collections::{HashMap, HashSet};
8use thiserror::Error;
9
10/// Validation error types
11#[derive(Debug, Error)]
12pub enum ValidationError {
13    #[error("Undefined register: {0}")]
14    UndefinedRegister(String),
15
16    #[error("Undefined gate: {0}")]
17    UndefinedGate(String),
18
19    #[error("Undefined variable: {0}")]
20    UndefinedVariable(String),
21
22    #[error("Type mismatch: expected {expected}, found {found}")]
23    TypeMismatch { expected: String, found: String },
24
25    #[error(
26        "Index out of bounds: register {register} has size {size}, but index {index} was used"
27    )]
28    IndexOutOfBounds {
29        register: String,
30        size: usize,
31        index: usize,
32    },
33
34    #[error("Parameter count mismatch: gate {gate} expects {expected} parameters, but {found} were provided")]
35    ParameterCountMismatch {
36        gate: String,
37        expected: usize,
38        found: usize,
39    },
40
41    #[error(
42        "Qubit count mismatch: gate {gate} expects {expected} qubits, but {found} were provided"
43    )]
44    QubitCountMismatch {
45        gate: String,
46        expected: usize,
47        found: usize,
48    },
49
50    #[error("Invalid slice: start index {start} is greater than end index {end}")]
51    InvalidSlice { start: usize, end: usize },
52
53    #[error("Duplicate declaration: {0}")]
54    DuplicateDeclaration(String),
55
56    #[error("Invalid control: {0}")]
57    InvalidControl(String),
58
59    #[error("Semantic error: {0}")]
60    SemanticError(String),
61}
62
63/// Symbol information in the validator
64#[derive(Debug, Clone)]
65enum Symbol {
66    QuantumRegister {
67        size: usize,
68    },
69    ClassicalRegister {
70        size: usize,
71    },
72    Gate {
73        params: Vec<String>,
74        qubits: Vec<String>,
75    },
76    Variable {
77        typ: ValueType,
78    },
79    Constant {
80        typ: ValueType,
81    },
82}
83
84/// Value types in QASM
85#[derive(Debug, Clone, PartialEq)]
86enum ValueType {
87    Bool,
88    Int,
89    Float,
90    Angle,
91    Duration,
92    Qubit,
93    Bit,
94    String,
95}
96
97/// QASM validator
98pub struct QasmValidator {
99    /// Symbol table
100    symbols: HashMap<String, Symbol>,
101    /// Standard gates (name -> (`param_count`, `qubit_count`))
102    standard_gates: HashMap<String, (usize, usize)>,
103    /// Current scope for nested blocks
104    scope_stack: Vec<HashMap<String, Symbol>>,
105}
106
107impl Default for QasmValidator {
108    fn default() -> Self {
109        Self::new()
110    }
111}
112
113impl QasmValidator {
114    /// Create a new validator
115    #[must_use]
116    pub fn new() -> Self {
117        let mut standard_gates = HashMap::new();
118
119        // Single-qubit gates
120        standard_gates.insert("id".to_string(), (0, 1));
121        standard_gates.insert("x".to_string(), (0, 1));
122        standard_gates.insert("y".to_string(), (0, 1));
123        standard_gates.insert("z".to_string(), (0, 1));
124        standard_gates.insert("h".to_string(), (0, 1));
125        standard_gates.insert("s".to_string(), (0, 1));
126        standard_gates.insert("sdg".to_string(), (0, 1));
127        standard_gates.insert("t".to_string(), (0, 1));
128        standard_gates.insert("tdg".to_string(), (0, 1));
129        standard_gates.insert("sx".to_string(), (0, 1));
130        standard_gates.insert("sxdg".to_string(), (0, 1));
131
132        // Parametric single-qubit gates
133        standard_gates.insert("rx".to_string(), (1, 1));
134        standard_gates.insert("ry".to_string(), (1, 1));
135        standard_gates.insert("rz".to_string(), (1, 1));
136        standard_gates.insert("p".to_string(), (1, 1));
137        standard_gates.insert("u1".to_string(), (1, 1));
138        standard_gates.insert("u2".to_string(), (2, 1));
139        standard_gates.insert("u3".to_string(), (3, 1));
140        standard_gates.insert("u".to_string(), (3, 1));
141
142        // Two-qubit gates
143        standard_gates.insert("cx".to_string(), (0, 2));
144        standard_gates.insert("cy".to_string(), (0, 2));
145        standard_gates.insert("cz".to_string(), (0, 2));
146        standard_gates.insert("ch".to_string(), (0, 2));
147        standard_gates.insert("swap".to_string(), (0, 2));
148        standard_gates.insert("iswap".to_string(), (0, 2));
149        standard_gates.insert("ecr".to_string(), (0, 2));
150        standard_gates.insert("dcx".to_string(), (0, 2));
151
152        // Parametric two-qubit gates
153        standard_gates.insert("crx".to_string(), (1, 2));
154        standard_gates.insert("cry".to_string(), (1, 2));
155        standard_gates.insert("crz".to_string(), (1, 2));
156        standard_gates.insert("cp".to_string(), (1, 2));
157        standard_gates.insert("cu1".to_string(), (1, 2));
158        standard_gates.insert("rxx".to_string(), (1, 2));
159        standard_gates.insert("ryy".to_string(), (1, 2));
160        standard_gates.insert("rzz".to_string(), (1, 2));
161        standard_gates.insert("rzx".to_string(), (1, 2));
162
163        // Three-qubit gates
164        standard_gates.insert("ccx".to_string(), (0, 3));
165        standard_gates.insert("cswap".to_string(), (0, 3));
166
167        Self {
168            symbols: HashMap::new(),
169            standard_gates,
170            scope_stack: vec![],
171        }
172    }
173
174    /// Validate a QASM program
175    pub fn validate(&mut self, program: &QasmProgram) -> Result<(), ValidationError> {
176        // Clear previous state
177        self.symbols.clear();
178        self.scope_stack.clear();
179
180        // Add built-in constants
181        self.symbols.insert(
182            "pi".to_string(),
183            Symbol::Constant {
184                typ: ValueType::Float,
185            },
186        );
187        self.symbols.insert(
188            "e".to_string(),
189            Symbol::Constant {
190                typ: ValueType::Float,
191            },
192        );
193        self.symbols.insert(
194            "tau".to_string(),
195            Symbol::Constant {
196                typ: ValueType::Float,
197            },
198        );
199
200        // Validate declarations
201        for decl in &program.declarations {
202            self.validate_declaration(decl)?;
203        }
204
205        // Validate statements
206        for stmt in &program.statements {
207            self.validate_statement(stmt)?;
208        }
209
210        Ok(())
211    }
212
213    /// Validate a declaration
214    fn validate_declaration(&mut self, decl: &Declaration) -> Result<(), ValidationError> {
215        match decl {
216            Declaration::QuantumRegister(reg) => {
217                if self.symbols.contains_key(&reg.name) {
218                    return Err(ValidationError::DuplicateDeclaration(reg.name.clone()));
219                }
220
221                if reg.size == 0 {
222                    return Err(ValidationError::SemanticError(
223                        "Register size must be greater than 0".to_string(),
224                    ));
225                }
226
227                self.symbols
228                    .insert(reg.name.clone(), Symbol::QuantumRegister { size: reg.size });
229            }
230            Declaration::ClassicalRegister(reg) => {
231                if self.symbols.contains_key(&reg.name) {
232                    return Err(ValidationError::DuplicateDeclaration(reg.name.clone()));
233                }
234
235                if reg.size == 0 {
236                    return Err(ValidationError::SemanticError(
237                        "Register size must be greater than 0".to_string(),
238                    ));
239                }
240
241                self.symbols.insert(
242                    reg.name.clone(),
243                    Symbol::ClassicalRegister { size: reg.size },
244                );
245            }
246            Declaration::GateDefinition(def) => {
247                if self.symbols.contains_key(&def.name) {
248                    return Err(ValidationError::DuplicateDeclaration(def.name.clone()));
249                }
250
251                // Create new scope for gate body
252                self.push_scope();
253
254                // Add parameters to scope
255                for param in &def.params {
256                    self.add_to_current_scope(
257                        param.clone(),
258                        Symbol::Variable {
259                            typ: ValueType::Angle,
260                        },
261                    );
262                }
263
264                // Add qubit arguments to scope
265                for qubit in &def.qubits {
266                    self.add_to_current_scope(
267                        qubit.clone(),
268                        Symbol::Variable {
269                            typ: ValueType::Qubit,
270                        },
271                    );
272                }
273
274                // Validate gate body
275                for stmt in &def.body {
276                    self.validate_statement(stmt)?;
277                }
278
279                // Pop scope
280                self.pop_scope();
281
282                // Add gate to symbols
283                self.symbols.insert(
284                    def.name.clone(),
285                    Symbol::Gate {
286                        params: def.params.clone(),
287                        qubits: def.qubits.clone(),
288                    },
289                );
290            }
291            Declaration::Constant(name, expr) => {
292                if self.symbols.contains_key(name) {
293                    return Err(ValidationError::DuplicateDeclaration(name.clone()));
294                }
295
296                let typ = self.validate_expression(expr)?;
297
298                self.symbols.insert(name.clone(), Symbol::Constant { typ });
299            }
300        }
301
302        Ok(())
303    }
304
305    /// Validate a statement
306    fn validate_statement(&mut self, stmt: &QasmStatement) -> Result<(), ValidationError> {
307        match stmt {
308            QasmStatement::Gate(gate) => self.validate_gate(gate),
309            QasmStatement::Measure(meas) => self.validate_measure(meas),
310            QasmStatement::Reset(qubits) => {
311                for qubit in qubits {
312                    self.validate_qubit_ref(qubit)?;
313                }
314                Ok(())
315            }
316            QasmStatement::Barrier(qubits) => {
317                for qubit in qubits {
318                    self.validate_qubit_ref(qubit)?;
319                }
320                Ok(())
321            }
322            QasmStatement::Assignment(var, expr) => {
323                let typ = self.validate_expression(expr)?;
324
325                // Check if variable exists
326                if let Some(symbol) = self.lookup_symbol(var) {
327                    match symbol {
328                        Symbol::Variable { typ: var_typ } => {
329                            if !self.types_compatible(var_typ, &typ) {
330                                return Err(ValidationError::TypeMismatch {
331                                    expected: format!("{var_typ:?}"),
332                                    found: format!("{typ:?}"),
333                                });
334                            }
335                        }
336                        _ => {
337                            return Err(ValidationError::SemanticError(format!(
338                                "{var} is not a variable"
339                            )))
340                        }
341                    }
342                } else {
343                    // Create new variable
344                    self.add_to_current_scope(var.clone(), Symbol::Variable { typ });
345                }
346
347                Ok(())
348            }
349            QasmStatement::If(cond, stmt) => {
350                self.validate_condition(cond)?;
351                self.validate_statement(stmt)
352            }
353            QasmStatement::For(for_loop) => {
354                self.push_scope();
355
356                // Add loop variable
357                self.add_to_current_scope(
358                    for_loop.variable.clone(),
359                    Symbol::Variable {
360                        typ: ValueType::Int,
361                    },
362                );
363
364                // Validate range
365                let start_typ = self.validate_expression(&for_loop.start)?;
366                let end_typ = self.validate_expression(&for_loop.end)?;
367
368                if start_typ != ValueType::Int || end_typ != ValueType::Int {
369                    return Err(ValidationError::TypeMismatch {
370                        expected: "int".to_string(),
371                        found: "non-int".to_string(),
372                    });
373                }
374
375                if let Some(step) = &for_loop.step {
376                    let step_typ = self.validate_expression(step)?;
377                    if step_typ != ValueType::Int {
378                        return Err(ValidationError::TypeMismatch {
379                            expected: "int".to_string(),
380                            found: format!("{step_typ:?}"),
381                        });
382                    }
383                }
384
385                // Validate body
386                for stmt in &for_loop.body {
387                    self.validate_statement(stmt)?;
388                }
389
390                self.pop_scope();
391                Ok(())
392            }
393            QasmStatement::While(cond, body) => {
394                self.validate_condition(cond)?;
395
396                self.push_scope();
397                for stmt in body {
398                    self.validate_statement(stmt)?;
399                }
400                self.pop_scope();
401
402                Ok(())
403            }
404            QasmStatement::Call(name, args) => {
405                // For now, just check that arguments are valid expressions
406                for arg in args {
407                    self.validate_expression(arg)?;
408                }
409                Ok(())
410            }
411            QasmStatement::Delay(duration, qubits) => {
412                let dur_typ = self.validate_expression(duration)?;
413                if dur_typ != ValueType::Duration && dur_typ != ValueType::Float {
414                    return Err(ValidationError::TypeMismatch {
415                        expected: "duration".to_string(),
416                        found: format!("{dur_typ:?}"),
417                    });
418                }
419
420                for qubit in qubits {
421                    self.validate_qubit_ref(qubit)?;
422                }
423
424                Ok(())
425            }
426        }
427    }
428
429    /// Validate a gate application
430    fn validate_gate(&self, gate: &QasmGate) -> Result<(), ValidationError> {
431        // Check if gate exists
432        let (expected_params, expected_qubits) =
433            if let Some(&(p, q)) = self.standard_gates.get(&gate.name) {
434                (p, q)
435            } else if let Some(symbol) = self.symbols.get(&gate.name) {
436                match symbol {
437                    Symbol::Gate { params, qubits } => (params.len(), qubits.len()),
438                    _ => return Err(ValidationError::UndefinedGate(gate.name.clone())),
439                }
440            } else {
441                return Err(ValidationError::UndefinedGate(gate.name.clone()));
442            };
443
444        // Check parameter count
445        if gate.params.len() != expected_params {
446            return Err(ValidationError::ParameterCountMismatch {
447                gate: gate.name.clone(),
448                expected: expected_params,
449                found: gate.params.len(),
450            });
451        }
452
453        // Validate parameters
454        for param in &gate.params {
455            let typ = self.validate_expression(param)?;
456            if typ != ValueType::Float && typ != ValueType::Angle && typ != ValueType::Int {
457                return Err(ValidationError::TypeMismatch {
458                    expected: "numeric".to_string(),
459                    found: format!("{typ:?}"),
460                });
461            }
462        }
463
464        // Check qubit count (accounting for control modifier)
465        let actual_qubits = gate.qubits.len();
466        let required_qubits = expected_qubits + gate.control.unwrap_or(0);
467
468        if actual_qubits != required_qubits {
469            return Err(ValidationError::QubitCountMismatch {
470                gate: gate.name.clone(),
471                expected: required_qubits,
472                found: actual_qubits,
473            });
474        }
475
476        // Validate qubits
477        for qubit in &gate.qubits {
478            self.validate_qubit_ref(qubit)?;
479        }
480
481        Ok(())
482    }
483
484    /// Validate a measurement
485    fn validate_measure(&self, meas: &Measurement) -> Result<(), ValidationError> {
486        if meas.qubits.len() != meas.targets.len() {
487            return Err(ValidationError::SemanticError(
488                "Measurement must have equal number of qubits and classical bits".to_string(),
489            ));
490        }
491
492        for qubit in &meas.qubits {
493            self.validate_qubit_ref(qubit)?;
494        }
495
496        for target in &meas.targets {
497            self.validate_classical_ref(target)?;
498        }
499
500        Ok(())
501    }
502
503    /// Validate a qubit reference
504    fn validate_qubit_ref(&self, qubit_ref: &QubitRef) -> Result<(), ValidationError> {
505        match qubit_ref {
506            QubitRef::Single { register, index } => {
507                match self.lookup_symbol(register) {
508                    Some(Symbol::QuantumRegister { size }) => {
509                        if *index >= *size {
510                            return Err(ValidationError::IndexOutOfBounds {
511                                register: register.clone(),
512                                size: *size,
513                                index: *index,
514                            });
515                        }
516                    }
517                    Some(Symbol::Variable {
518                        typ: ValueType::Qubit,
519                    }) => {
520                        // Single qubit variable
521                    }
522                    _ => return Err(ValidationError::UndefinedRegister(register.clone())),
523                }
524            }
525            QubitRef::Slice {
526                register,
527                start,
528                end,
529            } => match self.lookup_symbol(register) {
530                Some(Symbol::QuantumRegister { size }) => {
531                    if *start >= *size || *end > *size {
532                        return Err(ValidationError::IndexOutOfBounds {
533                            register: register.clone(),
534                            size: *size,
535                            index: (*start).max(*end),
536                        });
537                    }
538                    if *start >= *end {
539                        return Err(ValidationError::InvalidSlice {
540                            start: *start,
541                            end: *end,
542                        });
543                    }
544                }
545                _ => return Err(ValidationError::UndefinedRegister(register.clone())),
546            },
547            QubitRef::Register(name) => match self.lookup_symbol(name) {
548                Some(Symbol::QuantumRegister { .. }) => {}
549                Some(Symbol::Variable {
550                    typ: ValueType::Qubit,
551                }) => {}
552                _ => return Err(ValidationError::UndefinedRegister(name.clone())),
553            },
554        }
555
556        Ok(())
557    }
558
559    /// Validate a classical reference
560    fn validate_classical_ref(&self, classical_ref: &ClassicalRef) -> Result<(), ValidationError> {
561        match classical_ref {
562            ClassicalRef::Single { register, index } => {
563                match self.lookup_symbol(register) {
564                    Some(Symbol::ClassicalRegister { size }) => {
565                        if *index >= *size {
566                            return Err(ValidationError::IndexOutOfBounds {
567                                register: register.clone(),
568                                size: *size,
569                                index: *index,
570                            });
571                        }
572                    }
573                    Some(Symbol::Variable {
574                        typ: ValueType::Bit,
575                    }) => {
576                        // Single bit variable
577                    }
578                    _ => return Err(ValidationError::UndefinedRegister(register.clone())),
579                }
580            }
581            ClassicalRef::Slice {
582                register,
583                start,
584                end,
585            } => match self.lookup_symbol(register) {
586                Some(Symbol::ClassicalRegister { size }) => {
587                    if *start >= *size || *end > *size {
588                        return Err(ValidationError::IndexOutOfBounds {
589                            register: register.clone(),
590                            size: *size,
591                            index: (*start).max(*end),
592                        });
593                    }
594                    if *start >= *end {
595                        return Err(ValidationError::InvalidSlice {
596                            start: *start,
597                            end: *end,
598                        });
599                    }
600                }
601                _ => return Err(ValidationError::UndefinedRegister(register.clone())),
602            },
603            ClassicalRef::Register(name) => match self.lookup_symbol(name) {
604                Some(Symbol::ClassicalRegister { .. }) => {}
605                Some(Symbol::Variable {
606                    typ: ValueType::Bit,
607                }) => {}
608                _ => return Err(ValidationError::UndefinedRegister(name.clone())),
609            },
610        }
611
612        Ok(())
613    }
614
615    /// Validate an expression and return its type
616    fn validate_expression(&self, expr: &Expression) -> Result<ValueType, ValidationError> {
617        match expr {
618            Expression::Literal(lit) => Ok(match lit {
619                Literal::Integer(_) => ValueType::Int,
620                Literal::Float(_) | Literal::Pi | Literal::Euler | Literal::Tau => ValueType::Float,
621                Literal::Bool(_) => ValueType::Bool,
622                Literal::String(_) => ValueType::String,
623            }),
624            Expression::Variable(name) => match self.lookup_symbol(name) {
625                Some(Symbol::Variable { typ }) => Ok(typ.clone()),
626                Some(Symbol::Constant { typ }) => Ok(typ.clone()),
627                _ => Err(ValidationError::UndefinedVariable(name.clone())),
628            },
629            Expression::Binary(op, left, right) => {
630                let left_typ = self.validate_expression(left)?;
631                let right_typ = self.validate_expression(right)?;
632
633                // Type checking for binary operations
634                match op {
635                    BinaryOp::Add | BinaryOp::Sub | BinaryOp::Mul | BinaryOp::Div => {
636                        if (left_typ == ValueType::Int || left_typ == ValueType::Float)
637                            && (right_typ == ValueType::Int || right_typ == ValueType::Float)
638                        {
639                            Ok(ValueType::Float)
640                        } else {
641                            Err(ValidationError::TypeMismatch {
642                                expected: "numeric".to_string(),
643                                found: format!("{left_typ:?} and {right_typ:?}"),
644                            })
645                        }
646                    }
647                    BinaryOp::Mod
648                    | BinaryOp::BitAnd
649                    | BinaryOp::BitOr
650                    | BinaryOp::BitXor
651                    | BinaryOp::Shl
652                    | BinaryOp::Shr => {
653                        if left_typ == ValueType::Int && right_typ == ValueType::Int {
654                            Ok(ValueType::Int)
655                        } else {
656                            Err(ValidationError::TypeMismatch {
657                                expected: "int".to_string(),
658                                found: format!("{left_typ:?} and {right_typ:?}"),
659                            })
660                        }
661                    }
662                    BinaryOp::And | BinaryOp::Or | BinaryOp::Xor => {
663                        if left_typ == ValueType::Bool && right_typ == ValueType::Bool {
664                            Ok(ValueType::Bool)
665                        } else {
666                            Err(ValidationError::TypeMismatch {
667                                expected: "bool".to_string(),
668                                found: format!("{left_typ:?} and {right_typ:?}"),
669                            })
670                        }
671                    }
672                    BinaryOp::Eq
673                    | BinaryOp::Ne
674                    | BinaryOp::Lt
675                    | BinaryOp::Le
676                    | BinaryOp::Gt
677                    | BinaryOp::Ge => Ok(ValueType::Bool),
678                    BinaryOp::Pow => {
679                        if (left_typ == ValueType::Int || left_typ == ValueType::Float)
680                            && (right_typ == ValueType::Int || right_typ == ValueType::Float)
681                        {
682                            Ok(ValueType::Float)
683                        } else {
684                            Err(ValidationError::TypeMismatch {
685                                expected: "numeric".to_string(),
686                                found: format!("{left_typ:?} and {right_typ:?}"),
687                            })
688                        }
689                    }
690                }
691            }
692            Expression::Unary(op, expr) => {
693                let typ = self.validate_expression(expr)?;
694
695                match op {
696                    UnaryOp::Neg => {
697                        if typ == ValueType::Int || typ == ValueType::Float {
698                            Ok(typ)
699                        } else {
700                            Err(ValidationError::TypeMismatch {
701                                expected: "numeric".to_string(),
702                                found: format!("{typ:?}"),
703                            })
704                        }
705                    }
706                    UnaryOp::Not => {
707                        if typ == ValueType::Bool {
708                            Ok(ValueType::Bool)
709                        } else {
710                            Err(ValidationError::TypeMismatch {
711                                expected: "bool".to_string(),
712                                found: format!("{typ:?}"),
713                            })
714                        }
715                    }
716                    UnaryOp::BitNot => {
717                        if typ == ValueType::Int {
718                            Ok(ValueType::Int)
719                        } else {
720                            Err(ValidationError::TypeMismatch {
721                                expected: "int".to_string(),
722                                found: format!("{typ:?}"),
723                            })
724                        }
725                    }
726                    UnaryOp::Sin
727                    | UnaryOp::Cos
728                    | UnaryOp::Tan
729                    | UnaryOp::Asin
730                    | UnaryOp::Acos
731                    | UnaryOp::Atan
732                    | UnaryOp::Exp
733                    | UnaryOp::Ln
734                    | UnaryOp::Sqrt => {
735                        if typ == ValueType::Int || typ == ValueType::Float {
736                            Ok(ValueType::Float)
737                        } else {
738                            Err(ValidationError::TypeMismatch {
739                                expected: "numeric".to_string(),
740                                found: format!("{typ:?}"),
741                            })
742                        }
743                    }
744                }
745            }
746            Expression::Function(name, args) => {
747                // Validate arguments
748                for arg in args {
749                    self.validate_expression(arg)?;
750                }
751
752                // For now, assume functions return float
753                Ok(ValueType::Float)
754            }
755            Expression::Index(name, index) => {
756                // Validate index
757                let idx_typ = self.validate_expression(index)?;
758                if idx_typ != ValueType::Int {
759                    return Err(ValidationError::TypeMismatch {
760                        expected: "int".to_string(),
761                        found: format!("{idx_typ:?}"),
762                    });
763                }
764
765                // For now, assume indexing returns same type
766                match self.lookup_symbol(name) {
767                    Some(Symbol::Variable { typ }) => Ok(typ.clone()),
768                    _ => Err(ValidationError::UndefinedVariable(name.clone())),
769                }
770            }
771        }
772    }
773
774    /// Validate a condition
775    fn validate_condition(&self, cond: &Condition) -> Result<(), ValidationError> {
776        let left_typ = self.validate_expression(&cond.left)?;
777        let right_typ = self.validate_expression(&cond.right)?;
778
779        // For comparisons, types should be compatible
780        if !self.types_compatible(&left_typ, &right_typ) {
781            return Err(ValidationError::TypeMismatch {
782                expected: format!("{left_typ:?}"),
783                found: format!("{right_typ:?}"),
784            });
785        }
786
787        Ok(())
788    }
789
790    /// Check if two types are compatible
791    fn types_compatible(&self, typ1: &ValueType, typ2: &ValueType) -> bool {
792        match (typ1, typ2) {
793            (ValueType::Int, ValueType::Float) | (ValueType::Float, ValueType::Int) => true,
794            (ValueType::Angle, ValueType::Float) | (ValueType::Float, ValueType::Angle) => true,
795            (ValueType::Duration, ValueType::Float) | (ValueType::Float, ValueType::Duration) => {
796                true
797            }
798            (t1, t2) => t1 == t2,
799        }
800    }
801
802    /// Push a new scope
803    fn push_scope(&mut self) {
804        self.scope_stack.push(HashMap::new());
805    }
806
807    /// Pop the current scope
808    fn pop_scope(&mut self) {
809        self.scope_stack.pop();
810    }
811
812    /// Add symbol to current scope
813    fn add_to_current_scope(&mut self, name: String, symbol: Symbol) {
814        if let Some(scope) = self.scope_stack.last_mut() {
815            scope.insert(name, symbol);
816        } else {
817            self.symbols.insert(name, symbol);
818        }
819    }
820
821    /// Look up a symbol in all scopes
822    fn lookup_symbol(&self, name: &str) -> Option<&Symbol> {
823        // Check scopes from innermost to outermost
824        for scope in self.scope_stack.iter().rev() {
825            if let Some(symbol) = scope.get(name) {
826                return Some(symbol);
827            }
828        }
829
830        // Check global symbols
831        self.symbols.get(name)
832    }
833}
834
835/// Validate a QASM 3.0 program
836pub fn validate_qasm3(program: &QasmProgram) -> Result<(), ValidationError> {
837    let mut validator = QasmValidator::new();
838    validator.validate(program)
839}
840
841#[cfg(test)]
842mod tests {
843    use super::*;
844    use crate::qasm::parser::parse_qasm3;
845
846    #[test]
847    fn test_validate_simple_circuit() {
848        let input = r"
849OPENQASM 3.0;
850
851qubit[2] q;
852bit[2] c;
853
854h q[0];
855cx q[0], q[1];
856measure q -> c;
857";
858
859        let program = parse_qasm3(input).expect("parse_qasm3 should succeed for valid circuit");
860        let result = validate_qasm3(&program);
861        assert!(result.is_ok());
862    }
863
864    #[test]
865    fn test_validate_undefined_register() {
866        let input = r"
867OPENQASM 3.0;
868
869qubit[2] q;
870
871h q[0];
872cx q[0], r[1];  // r is undefined
873";
874
875        let program =
876            parse_qasm3(input).expect("parse_qasm3 should succeed for undefined register test");
877        let result = validate_qasm3(&program);
878        assert!(matches!(result, Err(ValidationError::UndefinedRegister(_))));
879    }
880
881    #[test]
882    fn test_validate_index_out_of_bounds() {
883        let input = r"
884OPENQASM 3.0;
885
886qubit[2] q;
887
888h q[5];  // Index 5 is out of bounds
889";
890
891        let program =
892            parse_qasm3(input).expect("parse_qasm3 should succeed for out of bounds test");
893        let result = validate_qasm3(&program);
894        assert!(matches!(
895            result,
896            Err(ValidationError::IndexOutOfBounds { .. })
897        ));
898    }
899
900    #[test]
901    fn test_validate_gate_parameters() {
902        let input = r"
903OPENQASM 3.0;
904
905qubit q;
906
907rx(pi/2) q;  // Correct
908rx q;        // Missing parameter
909";
910
911        let program =
912            parse_qasm3(input).expect("parse_qasm3 should succeed for gate parameter test");
913        let result = validate_qasm3(&program);
914        assert!(matches!(
915            result,
916            Err(ValidationError::ParameterCountMismatch { .. })
917        ));
918    }
919}