• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Qiskit / qiskit / 13540158629

26 Feb 2025 09:01AM UTC coverage: 87.854% (-0.7%) from 88.599%
13540158629

Pull #12814

github

web-flow
Merge f02a7b9b8 into 7169f6db0
Pull Request #12814: Light Cone Transpiler Pass

79 of 81 new or added lines in 2 files covered. (97.53%)

2576 existing lines in 133 files now uncovered.

77200 of 87873 relevant lines covered (87.85%)

339322.89 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.15
/crates/qasm2/src/parse.rs
1
// This code is part of Qiskit.
2
//
3
// (C) Copyright IBM 2023
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
//! The core of the parsing algorithm.  This module contains the core logic for the
14
//! recursive-descent parser, which handles all statements of OpenQASM 2.  In places where we have
15
//! to evaluate a mathematical expression on parameters, we instead swap to a short-lived
16
//! operator-precedence parser.
17

18
use hashbrown::{HashMap, HashSet};
19
use num_bigint::BigUint;
20
use pyo3::prelude::*;
21

22
use crate::bytecode::InternalBytecode;
23
use crate::error::{
24
    message_bad_eof, message_generic, message_incorrect_requirement, Position, QASM2ParseError,
25
};
26
use crate::expr::{Expr, ExprParser};
27
use crate::lex::{Token, TokenContext, TokenStream, TokenType, Version};
28
use crate::{CustomClassical, CustomInstruction};
29

30
/// The number of gates that are built in to the OpenQASM 2 language.  This is U and CX.
31
const N_BUILTIN_GATES: usize = 2;
32
/// The "qelib1.inc" special include.  The elements of the tuple are the gate name, the number of
33
/// parameters it takes, and the number of qubits it acts on.
34
const QELIB1: [(&str, usize, usize); 23] = [
35
    ("u3", 3, 1),
36
    ("u2", 2, 1),
37
    ("u1", 1, 1),
38
    ("cx", 0, 2),
39
    ("id", 0, 1),
40
    ("x", 0, 1),
41
    ("y", 0, 1),
42
    ("z", 0, 1),
43
    ("h", 0, 1),
44
    ("s", 0, 1),
45
    ("sdg", 0, 1),
46
    ("t", 0, 1),
47
    ("tdg", 0, 1),
48
    ("rx", 1, 1),
49
    ("ry", 1, 1),
50
    ("rz", 1, 1),
51
    ("cz", 0, 2),
52
    ("cy", 0, 2),
53
    ("ch", 0, 2),
54
    ("ccx", 0, 3),
55
    ("crz", 1, 2),
56
    ("cu1", 1, 2),
57
    ("cu3", 3, 2),
58
];
59

60
const BUILTIN_CLASSICAL: [&str; 6] = ["cos", "exp", "ln", "sin", "sqrt", "tan"];
61

62
/// Define a simple newtype that just has a single non-public `usize` field, has a `new`
63
/// constructor, and implements `Copy` and `IntoPy`.  The first argument is the name of the type,
64
/// the second is whether to also define addition to make offsetting the newtype easier.
65
macro_rules! newtype_id {
66
    ($id:ident, false) => {
67
        #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, IntoPyObject, IntoPyObjectRef)]
68
        pub struct $id(usize);
69

70
        impl $id {
71
            pub fn new(value: usize) -> Self {
24,174✔
72
                Self(value)
24,174✔
73
            }
24,174✔
74
        }
75
    };
76

77
    ($id:ident, true) => {
78
        newtype_id!($id, false);
79

80
        impl std::ops::Add<usize> for $id {
81
            type Output = Self;
82

83
            fn add(self, rhs: usize) -> Self {
5,460✔
84
                Self::new(self.0 + rhs)
5,460✔
85
            }
5,460✔
86
        }
87
    };
88
}
89

90
newtype_id!(GateId, false);
91
newtype_id!(CregId, false);
92
newtype_id!(ParamId, false);
93
newtype_id!(QubitId, true);
94
newtype_id!(ClbitId, true);
95

96
/// A symbol in the global symbol table.  Parameters and individual qubits can't be in the global
97
/// symbol table, as there is no way for them to be defined.
98
pub enum GlobalSymbol {
99
    Qreg {
100
        size: usize,
101
        start: QubitId,
102
    },
103
    Creg {
104
        size: usize,
105
        start: ClbitId,
106
        index: CregId,
107
    },
108
    Gate {
109
        num_params: usize,
110
        num_qubits: usize,
111
        index: GateId,
112
    },
113
    Classical {
114
        callable: PyObject,
115
        num_params: usize,
116
    },
117
}
118

119
impl GlobalSymbol {
120
    pub fn describe(&self) -> &'static str {
42✔
121
        match self {
42✔
122
            Self::Qreg { .. } => "a quantum register",
10✔
123
            Self::Creg { .. } => "a classical register",
12✔
124
            Self::Gate { .. } => "a gate",
14✔
125
            Self::Classical { .. } => "a custom classical function",
6✔
126
        }
127
    }
42✔
128
}
129

130
/// Information about a gate that permits a new definition in the OQ3 file only in the case that
131
/// the number of parameters and qubits matches.  This is used when the user specifies custom gates
132
/// that are not
133
struct OverridableGate {
134
    num_params: usize,
135
    num_qubits: usize,
136
    index: GateId,
137
}
138

139
impl From<OverridableGate> for GlobalSymbol {
140
    fn from(value: OverridableGate) -> Self {
2,688✔
141
        Self::Gate {
2,688✔
142
            num_params: value.num_params,
2,688✔
143
            num_qubits: value.num_qubits,
2,688✔
144
            index: value.index,
2,688✔
145
        }
2,688✔
146
    }
2,688✔
147
}
148

149
/// A symbol in the scope of a single gate definition.  This only includes the symbols that are
150
/// specifically gate-scoped.  The rest are part of [GlobalSymbol].
151
pub enum GateSymbol {
152
    Qubit { index: QubitId },
153
    Parameter { index: ParamId },
154
}
155

156
impl GateSymbol {
157
    pub fn describe(&self) -> &'static str {
6✔
158
        match self {
6✔
159
            Self::Qubit { .. } => "a qubit",
2✔
160
            Self::Parameter { .. } => "a parameter",
4✔
161
        }
162
    }
6✔
163
}
164

165
/// An operand for an instruction.  This can be both quantum or classical.  Classical operands only
166
/// occur in the `measure` operation.  `Single` variants are what we mostly expect to see; these
167
/// happen in gate definitions (always), and in regular applications when registers are indexed.
168
/// The `Range` operand only occurs when a register is used as an operation target.
169
enum Operand<T> {
170
    Single(T),
171
    Range(usize, T),
172
}
173

174
/// The available types for the arrays of parameters in a gate call.  The `Constant` variant is for
175
/// general applications, whereas the more general `Expression` variant is for gate bodies, where
176
/// there might be mathematics occurring on symbolic parameters.  We have the special case for the
177
/// far more common `Constant` form; in this case we immediately unwrap the result of the
178
/// `ExprParser`, and we won't have to make a new vector with the conversion later.
179
enum GateParameters {
180
    Constant(Vec<f64>),
181
    Expression(Vec<Expr>),
182
}
183

184
/// An equality condition from an `if` statement.  These can condition gate applications, measures
185
/// and resets, although in practice they're basically only ever used on gates.
186
#[derive(Clone)]
187
struct Condition {
188
    creg: CregId,
189
    value: BigUint,
190
}
191

192
/// Find the first match for the partial [filename] in the directories along [path].  Returns
193
/// `None` if the cannot be found.
194
fn find_include_path(
20✔
195
    filename: &std::path::Path,
20✔
196
    path: &[std::path::PathBuf],
20✔
197
) -> Option<std::path::PathBuf> {
20✔
198
    for directory in path.iter() {
24✔
199
        let mut absolute_path = directory.clone();
24✔
200
        absolute_path.push(filename);
24✔
201
        if absolute_path.is_file() {
24✔
202
            return Some(absolute_path);
18✔
203
        }
6✔
204
    }
205
    None
2✔
206
}
20✔
207

208
/// The state of the parser (but not its output).  This struct is opaque to the rest of the
209
/// program; only its associated functions ever need to modify its internals.  The counts of
210
/// qubits, clbits, classical registers and gates are necessary to efficiently assign index keys to
211
/// new symbols as they arise.  We don't need to track quantum registers like this because no part
212
/// of the output instruction set requires a reference to a quantum register, since we resolve any
213
/// broadcast gate applications from within Rust.
214
pub struct State {
215
    tokens: Vec<TokenStream>,
216
    /// The context object that owns all the text strings that back the tokens seen so far.  This
217
    /// needs to be given as a read-only reference to the [Token] methods that extract information
218
    /// based on the text they came from.
219
    context: TokenContext,
220
    include_path: Vec<std::path::PathBuf>,
221
    /// Mapping of name to global-scoped symbols.
222
    symbols: HashMap<String, GlobalSymbol>,
223
    /// Mapping of name to gate-scoped symbols.  This object only logically lasts for the duration
224
    /// of the parsing of one gate definition, but we can save allocations by re-using the same
225
    /// object between calls.
226
    gate_symbols: HashMap<String, GateSymbol>,
227
    /// Gate names that can accept a definition, even if they are already in the symbol table as
228
    /// gates. For example, if a user defines a custom gate as a `builtin`, we don't error out if
229
    /// we see a compatible definition later.  Regardless of whether the symbol was already in the
230
    /// symbol table or not, any gate-based definition of this symbol must match the signature we
231
    /// expect for it.
232
    overridable_gates: HashMap<String, OverridableGate>,
233
    num_qubits: usize,
234
    num_clbits: usize,
235
    num_cregs: usize,
236
    num_gates: usize,
237
    /// Whether a version statement is allowed in this position.
238
    allow_version: bool,
239
    /// Whether we're in strict mode or (the default) more permissive parse.
240
    strict: bool,
241
}
242

243
impl State {
244
    /// Create and initialise a state for the parser.
245
    pub fn new(
1,192✔
246
        tokens: TokenStream,
1,192✔
247
        include_path: Vec<std::path::PathBuf>,
1,192✔
248
        custom_instructions: &[CustomInstruction],
1,192✔
249
        custom_classical: &[CustomClassical],
1,192✔
250
        strict: bool,
1,192✔
251
    ) -> PyResult<Self> {
1,192✔
252
        let mut state = State {
1,192✔
253
            tokens: vec![tokens],
1,192✔
254
            context: TokenContext::new(),
1,192✔
255
            include_path,
1,192✔
256
            // For Qiskit-created circuits, all files will have the builtin gates and `qelib1.inc`,
1,192✔
257
            // so we allocate with that in mind.  There may well be overlap between libraries and
1,192✔
258
            // custom instructions, but this is small-potatoes allocation and we'd rather not have
1,192✔
259
            // to reallocate.
1,192✔
260
            symbols: HashMap::with_capacity(
1,192✔
261
                N_BUILTIN_GATES + QELIB1.len() + custom_instructions.len() + custom_classical.len(),
1,192✔
262
            ),
1,192✔
263
            gate_symbols: HashMap::new(),
1,192✔
264
            overridable_gates: HashMap::new(),
1,192✔
265
            num_qubits: 0,
1,192✔
266
            num_clbits: 0,
1,192✔
267
            num_cregs: 0,
1,192✔
268
            num_gates: 0,
1,192✔
269
            allow_version: true,
1,192✔
270
            strict,
1,192✔
271
        };
1,192✔
272
        for inst in custom_instructions {
6,942✔
273
            if state.symbols.contains_key(&inst.name)
5,752✔
274
                || state.overridable_gates.contains_key(&inst.name)
5,752✔
275
            {
276
                return Err(QASM2ParseError::new_err(message_generic(
2✔
277
                    None,
2✔
278
                    &format!("duplicate custom instruction '{}'", inst.name),
2✔
279
                )));
2✔
280
            }
5,750✔
281
            state.overridable_gates.insert(
5,750✔
282
                inst.name.clone(),
5,750✔
283
                OverridableGate {
5,750✔
284
                    num_params: inst.num_params,
5,750✔
285
                    num_qubits: inst.num_qubits,
5,750✔
286
                    index: GateId::new(state.num_gates),
5,750✔
287
                },
5,750✔
288
            );
5,750✔
289
            if inst.builtin {
5,750✔
290
                state.symbols.insert(
2,546✔
291
                    inst.name.clone(),
2,546✔
292
                    GlobalSymbol::Gate {
2,546✔
293
                        num_params: inst.num_params,
2,546✔
294
                        num_qubits: inst.num_qubits,
2,546✔
295
                        index: GateId::new(state.num_gates),
2,546✔
296
                    },
2,546✔
297
                );
2,546✔
298
            }
3,204✔
299
            state.num_gates += 1;
5,750✔
300
        }
301
        state.define_gate(None, "U".to_owned(), 3, 1)?;
1,190✔
302
        state.define_gate(None, "CX".to_owned(), 0, 2)?;
1,188✔
303
        for classical in custom_classical {
1,580✔
304
            if BUILTIN_CLASSICAL.contains(&&*classical.name) {
412✔
305
                return Err(QASM2ParseError::new_err(message_generic(
12✔
306
                    None,
12✔
307
                    &format!(
12✔
308
                        "cannot override builtin classical function '{}'",
12✔
309
                        &classical.name
12✔
310
                    ),
12✔
311
                )));
12✔
312
            }
400✔
313
            match state.symbols.insert(
400✔
314
                classical.name.clone(),
400✔
315
                GlobalSymbol::Classical {
400✔
316
                    num_params: classical.num_params,
400✔
317
                    callable: classical.callable.clone(),
400✔
318
                },
400✔
319
            ) {
400✔
320
                Some(GlobalSymbol::Gate { .. }) => {
321
                    let message = match classical.name.as_str() {
4✔
322
                        "U" | "CX" => format!(
4✔
323
                            "custom classical instructions cannot shadow built-in gates, but got '{}'",
2✔
324
                            &classical.name,
2✔
325
                        ),
2✔
326
                        _ => format!(
2✔
327
                            "custom classical instruction '{}' has a naming clash with a custom gate",
2✔
328
                            &classical.name,
2✔
329
                        ),
2✔
330
                    };
331
                    return Err(QASM2ParseError::new_err(message_generic(None, &message)));
4✔
332
                }
333
                Some(GlobalSymbol::Classical { .. }) => {
334
                    return Err(QASM2ParseError::new_err(message_generic(
2✔
335
                        None,
2✔
336
                        &format!("duplicate custom classical function '{}'", &classical.name,),
2✔
337
                    )));
2✔
338
                }
339
                _ => (),
394✔
340
            }
341
        }
342
        Ok(state)
1,168✔
343
    }
1,192✔
344

345
    /// Get the next token available in the stack of token streams, popping and removing any
346
    /// complete streams, except the base case.  Will only return `None` once all streams are
347
    /// exhausted.
348
    fn next_token(&mut self) -> PyResult<Option<Token>> {
45,886✔
349
        let mut pointer = self.tokens.len() - 1;
45,886✔
350
        while pointer > 0 {
45,888✔
351
            let out = self.tokens[pointer].next(&mut self.context)?;
140✔
352
            if out.is_some() {
140✔
353
                return Ok(out);
138✔
354
            }
2✔
355
            self.tokens.pop();
2✔
356
            pointer -= 1;
2✔
357
        }
358
        self.tokens[0].next(&mut self.context)
45,748✔
359
    }
45,886✔
360

361
    /// Peek the next token in the stack of token streams.  This does not remove any complete
362
    /// streams yet.  Will only return `None` once all streams are exhausted.
363
    fn peek_token(&mut self) -> PyResult<Option<&Token>> {
29,718✔
364
        let mut pointer = self.tokens.len() - 1;
29,718✔
365
        while pointer > 0 && self.tokens[pointer].peek(&mut self.context)?.is_none() {
29,736✔
366
            pointer -= 1;
18✔
367
        }
18✔
368
        self.tokens[pointer].peek(&mut self.context)
29,718✔
369
    }
29,718✔
370

371
    /// Get the filename associated with the currently active token stream.
372
    fn current_filename(&self) -> &std::ffi::OsStr {
550✔
373
        &self.tokens[self.tokens.len() - 1].filename
550✔
374
    }
550✔
375

376
    /// Take a token from the stream that is known to be present and correct, generally because it
377
    /// has already been peeked.  Panics if the token type is not correct.
378
    fn expect_known(&mut self, expected: TokenType) -> Token {
6,862✔
379
        let out = self.next_token().unwrap().unwrap();
6,862✔
380
        if out.ttype != expected {
6,862✔
381
            panic!(
×
382
                "expected '{}' but got '{}'",
×
383
                expected.describe(),
×
384
                out.ttype.describe()
×
385
            )
×
386
        }
6,862✔
387
        out
6,862✔
388
    }
6,862✔
389

390
    /// Take the next token from the stream, expecting that it is of a particular type because it
391
    /// is required to be in order for the input program to be valid OpenQASM 2.  This returns the
392
    /// token if successful, and a suitable error message if the token type is incorrect, or the
393
    /// end of the file is reached.
394
    fn expect(&mut self, expected: TokenType, required: &str, cause: &Token) -> PyResult<Token> {
25,606✔
395
        let token = match self.next_token()? {
25,606✔
396
            None => {
397
                return Err(QASM2ParseError::new_err(message_bad_eof(
114✔
398
                    Some(&Position::new(
114✔
399
                        self.current_filename(),
114✔
400
                        cause.line,
114✔
401
                        cause.col,
114✔
402
                    )),
114✔
403
                    required,
114✔
404
                )))
114✔
405
            }
406
            Some(token) => token,
25,474✔
407
        };
25,474✔
408
        if token.ttype == expected {
25,474✔
409
            Ok(token)
25,366✔
410
        } else {
411
            Err(QASM2ParseError::new_err(message_incorrect_requirement(
108✔
412
                required,
108✔
413
                &token,
108✔
414
                self.current_filename(),
108✔
415
            )))
108✔
416
        }
417
    }
25,606✔
418

419
    /// Take the next token from the stream, if it is of the correct type.  Returns `None` and
420
    /// leaves the next token in the underlying iterator if it does not match.
421
    fn accept(&mut self, expected: TokenType) -> PyResult<Option<Token>> {
19,584✔
422
        let peeked = self.peek_token()?;
19,584✔
423
        if peeked.is_some() && peeked.unwrap().ttype == expected {
19,584✔
424
            self.next_token()
13,372✔
425
        } else {
426
            Ok(None)
6,212✔
427
        }
428
    }
19,584✔
429

430
    /// True if the next token in the stream matches the given type, and false if it doesn't.
431
    fn next_is(&mut self, expected: TokenType) -> PyResult<bool> {
1,918✔
432
        let peeked = self.peek_token()?;
1,918✔
433
        Ok(peeked.is_some() && peeked.unwrap().ttype == expected)
1,914✔
434
    }
1,918✔
435

436
    /// If in `strict` mode, and we have a trailing comma, emit a suitable error message.
437
    fn check_trailing_comma(&self, comma: Option<&Token>) -> PyResult<()> {
3,868✔
438
        match (self.strict, comma) {
3,868✔
439
            (true, Some(token)) => Err(QASM2ParseError::new_err(message_generic(
14✔
440
                Some(&Position::new(
14✔
441
                    self.current_filename(),
14✔
442
                    token.line,
14✔
443
                    token.col,
14✔
444
                )),
14✔
445
                "[strict] trailing commas in parameter and qubit lists are forbidden",
14✔
446
            ))),
14✔
447
            _ => Ok(()),
3,854✔
448
        }
449
    }
3,868✔
450

451
    /// Take a complete quantum argument from the token stream, if the next token is an identifier.
452
    /// This includes resolving any following subscript operation.  Returns an error variant if the
453
    /// next token _is_ an identifier, but the symbol represents something other than a quantum
454
    /// register, or isn't defined.  This can also be an error if the subscript is opened, but
455
    /// cannot be completely resolved due to a typing error or other invalid parse.  `Ok(None)` is
456
    /// returned if the next token in the stream does not match a possible quantum argument.
457
    fn accept_qarg(&mut self) -> PyResult<Option<Operand<QubitId>>> {
4,138✔
458
        let (name, name_token) = match self.accept(TokenType::Id)? {
4,138✔
459
            None => return Ok(None),
22✔
460
            Some(token) => (token.id(&self.context), token),
4,116✔
461
        };
462
        let (register_size, register_start) = match self.symbols.get(&name) {
4,116✔
463
            Some(GlobalSymbol::Qreg { size, start }) => (*size, *start),
4,094✔
464
            Some(symbol) => {
18✔
465
                return Err(QASM2ParseError::new_err(message_generic(
18✔
466
                    Some(&Position::new(
18✔
467
                        self.current_filename(),
18✔
468
                        name_token.line,
18✔
469
                        name_token.col,
18✔
470
                    )),
18✔
471
                    &format!(
18✔
472
                        "'{}' is {}, not a quantum register",
18✔
473
                        name,
18✔
474
                        symbol.describe()
18✔
475
                    ),
18✔
476
                )))
18✔
477
            }
478
            None => {
479
                return Err(QASM2ParseError::new_err(message_generic(
4✔
480
                    Some(&Position::new(
4✔
481
                        self.current_filename(),
4✔
482
                        name_token.line,
4✔
483
                        name_token.col,
4✔
484
                    )),
4✔
485
                    &format!("'{}' is not defined in this scope", name),
4✔
486
                )))
4✔
487
            }
488
        };
489
        self.complete_operand(&name, register_size, register_start)
4,094✔
490
            .map(Some)
4,094✔
491
    }
4,138✔
492

493
    /// Take a complete quantum argument from the stream, if it matches.  This is for use within
494
    /// gates, and so the only valid type of quantum argument is a single qubit.
495
    fn accept_qarg_gate(&mut self) -> PyResult<Option<Operand<QubitId>>> {
410✔
496
        let (name, name_token) = match self.accept(TokenType::Id)? {
410✔
497
            None => return Ok(None),
×
498
            Some(token) => (token.id(&self.context), token),
410✔
499
        };
410✔
500
        match self.gate_symbols.get(&name) {
410✔
501
            Some(GateSymbol::Qubit { index }) => Ok(Some(Operand::Single(*index))),
398✔
502
            Some(GateSymbol::Parameter { .. }) => Err(QASM2ParseError::new_err(message_generic(
2✔
503
                Some(&Position::new(
2✔
504
                    self.current_filename(),
2✔
505
                    name_token.line,
2✔
506
                    name_token.col,
2✔
507
                )),
2✔
508
                &format!("'{}' is a parameter, not a qubit", name),
2✔
509
            ))),
2✔
510
            None => {
511
                if let Some(symbol) = self.symbols.get(&name) {
10✔
512
                    Err(QASM2ParseError::new_err(message_generic(
8✔
513
                        Some(&Position::new(
8✔
514
                            self.current_filename(),
8✔
515
                            name_token.line,
8✔
516
                            name_token.col,
8✔
517
                        )),
8✔
518
                        &format!("'{}' is {}, not a qubit", name, symbol.describe()),
8✔
519
                    )))
8✔
520
                } else {
521
                    Err(QASM2ParseError::new_err(message_generic(
2✔
522
                        Some(&Position::new(
2✔
523
                            self.current_filename(),
2✔
524
                            name_token.line,
2✔
525
                            name_token.col,
2✔
526
                        )),
2✔
527
                        &format!("'{}' is not defined in this scope", name),
2✔
528
                    )))
2✔
529
                }
530
            }
531
        }
532
    }
410✔
533

534
    /// Take a complete quantum argument from the token stream, returning an error message if one
535
    /// is not present.
536
    fn require_qarg(&mut self, instruction: &Token) -> PyResult<Operand<QubitId>> {
422✔
537
        match self.peek_token()?.map(|tok| tok.ttype) {
422✔
538
            Some(TokenType::Id) => self.accept_qarg().map(Option::unwrap),
408✔
539
            Some(_) => {
540
                let token = self.next_token()?;
10✔
541
                Err(QASM2ParseError::new_err(message_incorrect_requirement(
10✔
542
                    "a quantum argument",
10✔
543
                    &token.unwrap(),
10✔
544
                    self.current_filename(),
10✔
545
                )))
10✔
546
            }
547
            None => Err(QASM2ParseError::new_err(message_bad_eof(
4✔
548
                Some(&Position::new(
4✔
549
                    self.current_filename(),
4✔
550
                    instruction.line,
4✔
551
                    instruction.col,
4✔
552
                )),
4✔
553
                "a quantum argument",
4✔
554
            ))),
4✔
555
        }
556
    }
422✔
557

558
    /// Take a complete classical argument from the token stream, if the next token is an
559
    /// identifier.  This includes resolving any following subscript operation.  Returns an error
560
    /// variant if the next token _is_ an identifier, but the symbol represents something other
561
    /// than a classical register, or isn't defined.  This can also be an error if the subscript is
562
    /// opened, but cannot be completely resolved due to a typing error or other invalid parse.
563
    /// `Ok(None)` is returned if the next token in the stream does not match a possible classical
564
    /// argument.
565
    fn accept_carg(&mut self) -> PyResult<Option<Operand<ClbitId>>> {
346✔
566
        let (name, name_token) = match self.accept(TokenType::Id)? {
346✔
567
            None => return Ok(None),
×
568
            Some(token) => (token.id(&self.context), token),
346✔
569
        };
570
        let (register_size, register_start) = match self.symbols.get(&name) {
346✔
571
            Some(GlobalSymbol::Creg { size, start, .. }) => (*size, *start),
338✔
572
            Some(symbol) => {
6✔
573
                return Err(QASM2ParseError::new_err(message_generic(
6✔
574
                    Some(&Position::new(
6✔
575
                        self.current_filename(),
6✔
576
                        name_token.line,
6✔
577
                        name_token.col,
6✔
578
                    )),
6✔
579
                    &format!(
6✔
580
                        "'{}' is {}, not a classical register",
6✔
581
                        name,
6✔
582
                        symbol.describe()
6✔
583
                    ),
6✔
584
                )))
6✔
585
            }
586
            None => {
587
                return Err(QASM2ParseError::new_err(message_generic(
2✔
588
                    Some(&Position::new(
2✔
589
                        self.current_filename(),
2✔
590
                        name_token.line,
2✔
591
                        name_token.col,
2✔
592
                    )),
2✔
593
                    &format!("'{}' is not defined in this scope", name),
2✔
594
                )))
2✔
595
            }
596
        };
597
        self.complete_operand(&name, register_size, register_start)
338✔
598
            .map(Some)
338✔
599
    }
346✔
600

601
    /// Take a complete classical argument from the token stream, returning an error message if one
602
    /// is not present.
603
    fn require_carg(&mut self, instruction: &Token) -> PyResult<Operand<ClbitId>> {
352✔
604
        match self.peek_token()?.map(|tok| tok.ttype) {
352✔
605
            Some(TokenType::Id) => self.accept_carg().map(Option::unwrap),
346✔
606
            Some(_) => {
607
                let token = self.next_token()?;
4✔
608
                Err(QASM2ParseError::new_err(message_incorrect_requirement(
4✔
609
                    "a classical argument",
4✔
610
                    &token.unwrap(),
4✔
611
                    self.current_filename(),
4✔
612
                )))
4✔
613
            }
614
            None => Err(QASM2ParseError::new_err(message_bad_eof(
2✔
615
                Some(&Position::new(
2✔
616
                    self.current_filename(),
2✔
617
                    instruction.line,
2✔
618
                    instruction.col,
2✔
619
                )),
2✔
620
                "a classical argument",
2✔
621
            ))),
2✔
622
        }
623
    }
352✔
624

625
    /// Evaluate a possible subscript on a register into a final [Operand], consuming the tokens
626
    /// (if present) from the stream.  Can return error variants if the subscript cannot be
627
    /// completed or if there is a parse error while reading the subscript.
628
    fn complete_operand<T>(
4,432✔
629
        &mut self,
4,432✔
630
        name: &str,
4,432✔
631
        register_size: usize,
4,432✔
632
        register_start: T,
4,432✔
633
    ) -> PyResult<Operand<T>>
4,432✔
634
    where
4,432✔
635
        T: std::ops::Add<usize, Output = T>,
4,432✔
636
    {
4,432✔
637
        let lbracket_token = match self.accept(TokenType::LBracket)? {
4,432✔
638
            Some(token) => token,
3,804✔
639
            None => return Ok(Operand::Range(register_size, register_start)),
628✔
640
        };
641
        let index_token = self.expect(TokenType::Integer, "an integer index", &lbracket_token)?;
3,804✔
642
        let index = index_token.int(&self.context);
3,786✔
643
        self.expect(TokenType::RBracket, "a closing bracket", &lbracket_token)?;
3,786✔
644
        if index < register_size {
3,776✔
645
            Ok(Operand::Single(register_start + index))
3,770✔
646
        } else {
647
            Err(QASM2ParseError::new_err(message_generic(
6✔
648
                Some(&Position::new(
6✔
649
                    self.current_filename(),
6✔
650
                    index_token.line,
6✔
651
                    index_token.col,
6✔
652
                )),
6✔
653
                &format!(
6✔
654
                    "index {} is out-of-range for register '{}' of size {}",
6✔
655
                    index, name, register_size
6✔
656
                ),
6✔
657
            )))
6✔
658
        }
659
    }
4,432✔
660

661
    /// Parse an `OPENQASM <version>;` statement completely.  This function does not need to take
662
    /// the bytecode stream because the version information has no actionable effects for Qiskit
663
    /// to care about.  We simply error if the version supplied by the file is not the version of
664
    /// OpenQASM that we are able to support.  This assumes that the `OPENQASM` token is still in
665
    /// the stream.
666
    fn parse_version(&mut self) -> PyResult<usize> {
524✔
667
        let openqasm_token = self.expect_known(TokenType::OpenQASM);
524✔
668
        let version_token = self.expect(TokenType::Version, "version number", &openqasm_token)?;
524✔
669
        match version_token.version(&self.context) {
510✔
670
            Version {
671
                major: 2,
672
                minor: Some(0) | None,
673
            } => Ok(()),
506✔
674
            _ => Err(QASM2ParseError::new_err(message_generic(
4✔
675
                Some(&Position::new(
4✔
676
                    self.current_filename(),
4✔
677
                    version_token.line,
4✔
678
                    version_token.col,
4✔
679
                )),
4✔
680
                &format!(
4✔
681
                    "can only handle OpenQASM 2.0, but given {}",
4✔
682
                    version_token.text(&self.context),
4✔
683
                ),
4✔
684
            ))),
4✔
685
        }?;
4✔
686
        self.expect(TokenType::Semicolon, ";", &openqasm_token)?;
506✔
687
        Ok(0)
500✔
688
    }
524✔
689

690
    /// Parse a complete gate definition (including the body of the definition).  This assumes that
691
    /// the `gate` token is still in the scheme.  This function will likely result in many
692
    /// instructions being pushed onto the bytecode stream; one for the start and end of the gate
693
    /// definition, and then one instruction each for the gate applications in the body.
694
    fn parse_gate_definition(&mut self, bc: &mut Vec<Option<InternalBytecode>>) -> PyResult<usize> {
286✔
695
        let gate_token = self.expect_known(TokenType::Gate);
286✔
696
        let name_token = self.expect(TokenType::Id, "an identifier", &gate_token)?;
286✔
697
        let name = name_token.id(&self.context);
284✔
698
        // Parse the gate parameters (if any) into the symbol take.
284✔
699
        let mut num_params = 0usize;
284✔
700
        if let Some(lparen_token) = self.accept(TokenType::LParen)? {
284✔
701
            let mut comma = None;
152✔
702
            while let Some(param_token) = self.accept(TokenType::Id)? {
270✔
703
                let param_name = param_token.id(&self.context);
256✔
704
                if let Some(symbol) = self.gate_symbols.insert(
256✔
705
                    param_name.to_owned(),
256✔
706
                    GateSymbol::Parameter {
256✔
707
                        index: ParamId::new(num_params),
256✔
708
                    },
256✔
709
                ) {
256✔
710
                    return Err(QASM2ParseError::new_err(message_generic(
2✔
711
                        Some(&Position::new(
2✔
712
                            self.current_filename(),
2✔
713
                            param_token.line,
2✔
714
                            param_token.col,
2✔
715
                        )),
2✔
716
                        &format!(
2✔
717
                            "'{}' is already defined as {}",
2✔
718
                            param_name,
2✔
719
                            symbol.describe()
2✔
720
                        ),
2✔
721
                    )));
2✔
722
                }
254✔
723
                num_params += 1;
254✔
724
                comma = self.accept(TokenType::Comma)?;
254✔
725
                if comma.is_none() {
254✔
726
                    break;
136✔
727
                }
118✔
728
            }
729
            self.check_trailing_comma(comma.as_ref())?;
150✔
730
            self.expect(TokenType::RParen, "a closing parenthesis", &lparen_token)?;
148✔
731
        }
132✔
732
        // Parse the quantum parameters into the symbol table.
733
        let mut num_qubits = 0usize;
266✔
734
        let mut comma = None;
266✔
735
        while let Some(qubit_token) = self.accept(TokenType::Id)? {
390✔
736
            let qubit_name = qubit_token.id(&self.context).to_owned();
374✔
737
            if let Some(symbol) = self.gate_symbols.insert(
374✔
738
                qubit_name.to_owned(),
374✔
739
                GateSymbol::Qubit {
374✔
740
                    index: QubitId::new(num_qubits),
374✔
741
                },
374✔
742
            ) {
374✔
743
                return Err(QASM2ParseError::new_err(message_generic(
4✔
744
                    Some(&Position::new(
4✔
745
                        self.current_filename(),
4✔
746
                        qubit_token.line,
4✔
747
                        qubit_token.col,
4✔
748
                    )),
4✔
749
                    &format!(
4✔
750
                        "'{}' is already defined as {}",
4✔
751
                        qubit_name,
4✔
752
                        symbol.describe()
4✔
753
                    ),
4✔
754
                )));
4✔
755
            }
370✔
756
            num_qubits += 1;
370✔
757
            comma = self.accept(TokenType::Comma)?;
370✔
758
            if comma.is_none() {
370✔
759
                break;
246✔
760
            }
124✔
761
        }
762
        self.check_trailing_comma(comma.as_ref())?;
262✔
763
        if num_qubits == 0 {
260✔
764
            let eof = self.peek_token()?.is_none();
8✔
765
            let position = Position::new(self.current_filename(), gate_token.line, gate_token.col);
8✔
766
            return if eof {
8✔
767
                Err(QASM2ParseError::new_err(message_bad_eof(
4✔
768
                    Some(&position),
4✔
769
                    "a qubit identifier",
4✔
770
                )))
4✔
771
            } else {
772
                Err(QASM2ParseError::new_err(message_generic(
4✔
773
                    Some(&position),
4✔
774
                    "gates must act on at least one qubit",
4✔
775
                )))
4✔
776
            };
777
        }
252✔
778
        let lbrace_token = self.expect(TokenType::LBrace, "a gate body", &gate_token)?;
252✔
779
        bc.push(Some(InternalBytecode::DeclareGate {
242✔
780
            name: name.clone(),
242✔
781
            num_qubits,
242✔
782
        }));
242✔
783
        // The actual body of the gate.  Most of this is devolved to [Self::parse_gate_application]
242✔
784
        // to do the right thing.
242✔
785
        let mut statements = 0usize;
242✔
786
        loop {
787
            match self.peek_token()?.map(|tok| tok.ttype) {
508✔
788
                Some(TokenType::Id) => statements += self.parse_gate_application(bc, None, true)?,
290✔
789
                Some(TokenType::Barrier) => {
790
                    statements += self.parse_barrier(bc, Some(num_qubits))?
12✔
791
                }
792
                Some(TokenType::RBrace) => {
793
                    self.expect_known(TokenType::RBrace);
196✔
794
                    break;
196✔
795
                }
796
                Some(_) => {
797
                    let token = self.next_token()?.unwrap();
8✔
798
                    return Err(QASM2ParseError::new_err(message_generic(
8✔
799
                        Some(&Position::new(
8✔
800
                            self.current_filename(),
8✔
801
                            token.line,
8✔
802
                            token.col,
8✔
803
                        )),
8✔
804
                        &format!(
8✔
805
                            "only gate applications are valid within a 'gate' body, but saw {}",
8✔
806
                            token.text(&self.context)
8✔
807
                        ),
8✔
808
                    )));
8✔
809
                }
810
                None => {
811
                    return Err(QASM2ParseError::new_err(message_bad_eof(
2✔
812
                        Some(&Position::new(
2✔
813
                            self.current_filename(),
2✔
814
                            lbrace_token.line,
2✔
815
                            lbrace_token.col,
2✔
816
                        )),
2✔
817
                        "a closing brace '}' of the gate body",
2✔
818
                    )))
2✔
819
                }
820
            }
821
        }
822
        bc.push(Some(InternalBytecode::EndDeclareGate {}));
196✔
823
        self.gate_symbols.clear();
196✔
824
        let num_bytecode = statements + 2;
196✔
825
        if self.define_gate(Some(&gate_token), name, num_params, num_qubits)? {
196✔
826
            Ok(num_bytecode)
162✔
827
        } else {
828
            // The gate was built-in, so we don't actually need to emit the bytecode.  This is
829
            // uncommon, so it doesn't matter too much that we throw away allocation work we did -
830
            // it still helps that we verified that the gate body was valid OpenQASM 2.
831
            bc.truncate(bc.len() - num_bytecode);
10✔
832
            Ok(0)
10✔
833
        }
834
    }
286✔
835

836
    /// Parse an `opaque` statement.  This assumes that the `opaque` token is still in the token
837
    /// stream we are reading from.
838
    fn parse_opaque_definition(
96✔
839
        &mut self,
96✔
840
        bc: &mut Vec<Option<InternalBytecode>>,
96✔
841
    ) -> PyResult<usize> {
96✔
842
        let opaque_token = self.expect_known(TokenType::Opaque);
96✔
843
        let name = self
96✔
844
            .expect(TokenType::Id, "an identifier", &opaque_token)?
96✔
845
            .text(&self.context)
90✔
846
            .to_owned();
90✔
847
        let mut num_params = 0usize;
90✔
848
        if let Some(lparen_token) = self.accept(TokenType::LParen)? {
90✔
849
            let mut comma = None;
76✔
850
            while self.accept(TokenType::Id)?.is_some() {
122✔
851
                num_params += 1;
110✔
852
                comma = self.accept(TokenType::Comma)?;
110✔
853
                if comma.is_none() {
110✔
854
                    break;
64✔
855
                }
46✔
856
            }
857
            self.check_trailing_comma(comma.as_ref())?;
76✔
858
            self.expect(TokenType::RParen, "closing parenthesis", &lparen_token)?;
74✔
859
        }
14✔
860
        let mut num_qubits = 0usize;
74✔
861
        let mut comma = None;
74✔
862
        while self.accept(TokenType::Id)?.is_some() {
104✔
863
            num_qubits += 1;
82✔
864
            comma = self.accept(TokenType::Comma)?;
82✔
865
            if comma.is_none() {
82✔
866
                break;
52✔
867
            }
30✔
868
        }
869
        self.check_trailing_comma(comma.as_ref())?;
74✔
870
        self.expect(TokenType::Semicolon, ";", &opaque_token)?;
72✔
871
        if num_qubits == 0 {
48✔
872
            return Err(QASM2ParseError::new_err(message_generic(
4✔
873
                Some(&Position::new(
4✔
874
                    self.current_filename(),
4✔
875
                    opaque_token.line,
4✔
876
                    opaque_token.col,
4✔
877
                )),
4✔
878
                "gates must act on at least one qubit",
4✔
879
            )));
4✔
880
        }
44✔
881
        bc.push(Some(InternalBytecode::DeclareOpaque {
44✔
882
            name: name.clone(),
44✔
883
            num_qubits,
44✔
884
        }));
44✔
885
        self.define_gate(Some(&opaque_token), name, num_params, num_qubits)?;
44✔
886
        Ok(1)
36✔
887
    }
96✔
888

889
    /// Parse a gate application into the bytecode stream.  This resolves any broadcast over
890
    /// registers into a series of bytecode instructions, rather than leaving Qiskit to do it,
891
    /// which would involve much slower Python execution.  This assumes that the identifier token
892
    /// is still in the token stream.
893
    fn parse_gate_application(
2,510✔
894
        &mut self,
2,510✔
895
        bc: &mut Vec<Option<InternalBytecode>>,
2,510✔
896
        condition: Option<Condition>,
2,510✔
897
        in_gate: bool,
2,510✔
898
    ) -> PyResult<usize> {
2,510✔
899
        let name_token = self.expect_known(TokenType::Id);
2,510✔
900
        let name = name_token.id(&self.context);
2,510✔
901
        let (index, num_params, num_qubits) = match self.symbols.get(&name) {
2,510✔
902
            Some(GlobalSymbol::Gate {
903
                num_params,
2,496✔
904
                num_qubits,
2,496✔
905
                index,
2,496✔
906
            }) => Ok((*index, *num_params, *num_qubits)),
2,496✔
907
            Some(symbol) => Err(QASM2ParseError::new_err(message_generic(
6✔
908
                Some(&Position::new(
6✔
909
                    self.current_filename(),
6✔
910
                    name_token.line,
6✔
911
                    name_token.col,
6✔
912
                )),
6✔
913
                &format!("'{}' is {}, not a gate", name, symbol.describe()),
6✔
914
            ))),
6✔
915
            None => {
916
                let pos = Position::new(self.current_filename(), name_token.line, name_token.col);
8✔
917
                let message = if self.overridable_gates.contains_key(&name) {
8✔
918
                    format!(
2✔
919
                        "cannot use non-builtin custom instruction '{}' before definition",
2✔
920
                        name,
2✔
921
                    )
2✔
922
                } else {
923
                    format!("'{}' is not defined in this scope", name)
6✔
924
                };
925
                Err(QASM2ParseError::new_err(message_generic(
8✔
926
                    Some(&pos),
8✔
927
                    &message,
8✔
928
                )))
8✔
929
            }
930
        }?;
14✔
931
        let parameters = self.expect_gate_parameters(&name_token, num_params, in_gate)?;
2,496✔
932
        let mut qargs = Vec::<Operand<QubitId>>::with_capacity(num_qubits);
2,386✔
933
        let mut comma = None;
2,386✔
934
        if in_gate {
2,386✔
935
            while let Some(qarg) = self.accept_qarg_gate()? {
392✔
936
                qargs.push(qarg);
380✔
937
                comma = self.accept(TokenType::Comma)?;
380✔
938
                if comma.is_none() {
380✔
939
                    break;
256✔
940
                }
124✔
941
            }
942
        } else {
943
            while let Some(qarg) = self.accept_qarg()? {
3,448✔
944
                qargs.push(qarg);
3,414✔
945
                comma = self.accept(TokenType::Comma)?;
3,414✔
946
                if comma.is_none() {
3,414✔
947
                    break;
2,084✔
948
                }
1,330✔
949
            }
950
        }
951
        self.check_trailing_comma(comma.as_ref())?;
2,354✔
952
        if qargs.len() != num_qubits {
2,352✔
953
            return match self.peek_token()?.map(|tok| tok.ttype) {
34✔
954
                Some(TokenType::Semicolon) => Err(QASM2ParseError::new_err(message_generic(
955
                    Some(&Position::new(
16✔
956
                        self.current_filename(),
16✔
957
                        name_token.line,
16✔
958
                        name_token.col,
16✔
959
                    )),
16✔
960
                    &format!(
16✔
961
                        "'{}' takes {} quantum argument{}, but got {}",
16✔
962
                        name,
16✔
963
                        num_qubits,
16✔
964
                        if num_qubits == 1 { "" } else { "s" },
16✔
965
                        qargs.len()
16✔
966
                    ),
967
                ))),
968
                Some(_) => Err(QASM2ParseError::new_err(message_incorrect_requirement(
8✔
969
                    "the end of the argument list",
8✔
970
                    &name_token,
8✔
971
                    self.current_filename(),
8✔
972
                ))),
8✔
973
                None => Err(QASM2ParseError::new_err(message_bad_eof(
10✔
974
                    Some(&Position::new(
10✔
975
                        self.current_filename(),
10✔
976
                        name_token.line,
10✔
977
                        name_token.col,
10✔
978
                    )),
10✔
979
                    "the end of the argument list",
10✔
980
                ))),
10✔
981
            };
982
        }
2,318✔
983
        self.expect(TokenType::Semicolon, "';'", &name_token)?;
2,318✔
984
        self.emit_gate_application(bc, &name_token, index, parameters, &qargs, condition)
2,314✔
985
    }
2,510✔
986

987
    /// Parse the parameters (if any) from a gate application.
988
    fn expect_gate_parameters(
2,496✔
989
        &mut self,
2,496✔
990
        name_token: &Token,
2,496✔
991
        num_params: usize,
2,496✔
992
        in_gate: bool,
2,496✔
993
    ) -> PyResult<GateParameters> {
2,496✔
994
        let lparen_token = match self.accept(TokenType::LParen)? {
2,496✔
995
            Some(lparen_token) => lparen_token,
892✔
996
            None => {
997
                return Ok(if in_gate {
1,604✔
998
                    GateParameters::Expression(vec![])
162✔
999
                } else {
1000
                    GateParameters::Constant(vec![])
1,442✔
1001
                });
1002
            }
1003
        };
1004
        let mut seen_params = 0usize;
892✔
1005
        let mut comma = None;
892✔
1006
        // This code duplication is to avoid duplication of allocation when parsing the far more
1007
        // common case of expecting constant parameters for a gate application in the body of the
1008
        // OQ2 file.
1009
        let parameters = if in_gate {
892✔
1010
            let mut parameters = Vec::<Expr>::with_capacity(num_params);
126✔
1011
            while !self.next_is(TokenType::RParen)? {
260✔
1012
                let mut expr_parser = ExprParser {
260✔
1013
                    tokens: &mut self.tokens,
260✔
1014
                    context: &mut self.context,
260✔
1015
                    gate_symbols: &self.gate_symbols,
260✔
1016
                    global_symbols: &self.symbols,
260✔
1017
                    strict: self.strict,
260✔
1018
                };
260✔
1019
                parameters.push(expr_parser.parse_expression(&lparen_token)?);
260✔
1020
                seen_params += 1;
240✔
1021
                comma = self.accept(TokenType::Comma)?;
240✔
1022
                if comma.is_none() {
240✔
1023
                    break;
106✔
1024
                }
134✔
1025
            }
1026
            self.expect(TokenType::RParen, "')'", &lparen_token)?;
106✔
1027
            GateParameters::Expression(parameters)
106✔
1028
        } else {
1029
            let mut parameters = Vec::<f64>::with_capacity(num_params);
766✔
1030
            while !self.next_is(TokenType::RParen)? {
1,476✔
1031
                let mut expr_parser = ExprParser {
1,438✔
1032
                    tokens: &mut self.tokens,
1,438✔
1033
                    context: &mut self.context,
1,438✔
1034
                    gate_symbols: &self.gate_symbols,
1,438✔
1035
                    global_symbols: &self.symbols,
1,438✔
1036
                    strict: self.strict,
1,438✔
1037
                };
1,438✔
1038
                match expr_parser.parse_expression(&lparen_token)? {
1,438✔
1039
                    Expr::Constant(value) => parameters.push(value),
1,366✔
1040
                    _ => {
1041
                        return Err(QASM2ParseError::new_err(message_generic(
×
1042
                            Some(&Position::new(
×
1043
                                self.current_filename(),
×
1044
                                lparen_token.line,
×
1045
                                lparen_token.col,
×
1046
                            )),
×
1047
                            "non-constant expression in program body",
×
1048
                        )))
×
1049
                    }
1050
                }
1051
                seen_params += 1;
1,366✔
1052
                comma = self.accept(TokenType::Comma)?;
1,366✔
1053
                if comma.is_none() {
1,366✔
1054
                    break;
656✔
1055
                }
710✔
1056
            }
1057
            self.expect(TokenType::RParen, "')'", &lparen_token)?;
690✔
1058
            GateParameters::Constant(parameters)
690✔
1059
        };
1060
        self.check_trailing_comma(comma.as_ref())?;
796✔
1061
        if seen_params != num_params {
794✔
1062
            return Err(QASM2ParseError::new_err(message_generic(
1063
                Some(&Position::new(
12✔
1064
                    self.current_filename(),
12✔
1065
                    name_token.line,
12✔
1066
                    name_token.col,
12✔
1067
                )),
12✔
1068
                &format!(
12✔
1069
                    "'{}' takes {} parameter{}, but got {}",
12✔
1070
                    &name_token.text(&self.context),
12✔
1071
                    num_params,
12✔
1072
                    if num_params == 1 { "" } else { "s" },
12✔
1073
                    seen_params
1074
                ),
1075
            )));
1076
        }
782✔
1077
        Ok(parameters)
782✔
1078
    }
2,496✔
1079

1080
    /// Emit the bytecode for the application of a gate.  This involves resolving any broadcasts
1081
    /// in the operands of the gate (i.e. if one or more of them is a register).
1082
    fn emit_gate_application(
2,314✔
1083
        &self,
2,314✔
1084
        bc: &mut Vec<Option<InternalBytecode>>,
2,314✔
1085
        instruction: &Token,
2,314✔
1086
        gate_id: GateId,
2,314✔
1087
        parameters: GateParameters,
2,314✔
1088
        qargs: &[Operand<QubitId>],
2,314✔
1089
        condition: Option<Condition>,
2,314✔
1090
    ) -> PyResult<usize> {
2,314✔
1091
        // Fast path for most common gate patterns that don't need broadcasting.
1092
        if let Some(qubits) = match qargs {
1,860✔
1093
            [Operand::Single(index)] => Some(vec![*index]),
1,190✔
1094
            [Operand::Single(left), Operand::Single(right)] => {
674✔
1095
                if *left == *right {
674✔
1096
                    return Err(QASM2ParseError::new_err(message_generic(
4✔
1097
                        Some(&Position::new(
4✔
1098
                            self.current_filename(),
4✔
1099
                            instruction.line,
4✔
1100
                            instruction.col,
4✔
1101
                        )),
4✔
1102
                        "duplicate qubits in gate application",
4✔
1103
                    )));
4✔
1104
                }
670✔
1105
                Some(vec![*left, *right])
670✔
1106
            }
1107
            [] => Some(vec![]),
324✔
1108
            _ => None,
450✔
1109
        } {
1110
            return match parameters {
1,860✔
1111
                GateParameters::Constant(parameters) => {
1,616✔
1112
                    self.emit_single_global_gate(bc, gate_id, parameters, qubits, condition)
1,616✔
1113
                }
1114
                GateParameters::Expression(parameters) => {
244✔
1115
                    self.emit_single_gate_gate(bc, gate_id, parameters, qubits)
244✔
1116
                }
1117
            };
1118
        };
450✔
1119
        // If we're here we either have to broadcast or it's a 3+q gate - either way, we're not as
450✔
1120
        // worried about performance.
450✔
1121
        let mut qubits = HashSet::<QubitId>::with_capacity(qargs.len());
450✔
1122
        let mut broadcast_length = 0usize;
450✔
1123
        for qarg in qargs {
1,576✔
1124
            match qarg {
1,182✔
1125
                Operand::Single(index) => {
874✔
1126
                    if !qubits.insert(*index) {
874✔
1127
                        return Err(QASM2ParseError::new_err(message_generic(
12✔
1128
                            Some(&Position::new(
12✔
1129
                                self.current_filename(),
12✔
1130
                                instruction.line,
12✔
1131
                                instruction.col,
12✔
1132
                            )),
12✔
1133
                            "duplicate qubits in gate application",
12✔
1134
                        )));
12✔
1135
                    }
862✔
1136
                }
1137
                Operand::Range(size, start) => {
308✔
1138
                    if broadcast_length != 0 && broadcast_length != *size {
308✔
1139
                        return Err(QASM2ParseError::new_err(message_generic(
36✔
1140
                            Some(&Position::new(
36✔
1141
                                self.current_filename(),
36✔
1142
                                instruction.line,
36✔
1143
                                instruction.col,
36✔
1144
                            )),
36✔
1145
                            "cannot resolve broadcast in gate application",
36✔
1146
                        )));
36✔
1147
                    }
272✔
1148
                    for offset in 0..*size {
558✔
1149
                        if !qubits.insert(*start + offset) {
558✔
1150
                            return Err(QASM2ParseError::new_err(message_generic(
8✔
1151
                                Some(&Position::new(
8✔
1152
                                    self.current_filename(),
8✔
1153
                                    instruction.line,
8✔
1154
                                    instruction.col,
8✔
1155
                                )),
8✔
1156
                                "duplicate qubits in gate application",
8✔
1157
                            )));
8✔
1158
                        }
550✔
1159
                    }
1160
                    broadcast_length = *size;
264✔
1161
                }
1162
            }
1163
        }
1164
        if broadcast_length == 0 {
394✔
1165
            let qubits = qargs
294✔
1166
                .iter()
294✔
1167
                .filter_map(|qarg| {
886✔
1168
                    if let Operand::Single(index) = qarg {
886✔
1169
                        Some(*index)
818✔
1170
                    } else {
1171
                        None
68✔
1172
                    }
1173
                })
886✔
1174
                .collect::<Vec<_>>();
294✔
1175
            if qubits.len() < qargs.len() {
294✔
1176
                // We're broadcasting against at least one empty register.
1177
                return Ok(0);
44✔
1178
            }
250✔
1179
            return match parameters {
250✔
1180
                GateParameters::Constant(parameters) => {
240✔
1181
                    self.emit_single_global_gate(bc, gate_id, parameters, qubits, condition)
240✔
1182
                }
1183
                GateParameters::Expression(parameters) => {
10✔
1184
                    self.emit_single_gate_gate(bc, gate_id, parameters, qubits)
10✔
1185
                }
1186
            };
1187
        }
100✔
1188
        for i in 0..broadcast_length {
290✔
1189
            let qubits = qargs
290✔
1190
                .iter()
290✔
1191
                .map(|qarg| match qarg {
450✔
1192
                    Operand::Single(index) => *index,
48✔
1193
                    Operand::Range(_, start) => *start + i,
402✔
1194
                })
450✔
1195
                .collect::<Vec<_>>();
290✔
1196
            match parameters {
290✔
1197
                GateParameters::Constant(ref parameters) => {
290✔
1198
                    self.emit_single_global_gate(
290✔
1199
                        bc,
290✔
1200
                        gate_id,
290✔
1201
                        parameters.clone(),
290✔
1202
                        qubits,
290✔
1203
                        condition.clone(),
290✔
1204
                    )?;
290✔
1205
                }
1206
                // Gates used in gate-body definitions can't ever broadcast, because their only
1207
                // operands are single qubits.
1208
                _ => unreachable!(),
×
1209
            }
1210
        }
1211
        Ok(broadcast_length)
100✔
1212
    }
2,314✔
1213

1214
    /// Emit the bytecode for a single gate application in the global scope.  This could
1215
    /// potentially have a classical condition.
1216
    fn emit_single_global_gate(
2,146✔
1217
        &self,
2,146✔
1218
        bc: &mut Vec<Option<InternalBytecode>>,
2,146✔
1219
        gate_id: GateId,
2,146✔
1220
        arguments: Vec<f64>,
2,146✔
1221
        qubits: Vec<QubitId>,
2,146✔
1222
        condition: Option<Condition>,
2,146✔
1223
    ) -> PyResult<usize> {
2,146✔
1224
        if let Some(condition) = condition {
2,146✔
1225
            bc.push(Some(InternalBytecode::ConditionedGate {
114✔
1226
                id: gate_id,
114✔
1227
                arguments,
114✔
1228
                qubits,
114✔
1229
                creg: condition.creg,
114✔
1230
                value: condition.value,
114✔
1231
            }));
114✔
1232
        } else {
2,032✔
1233
            bc.push(Some(InternalBytecode::Gate {
2,032✔
1234
                id: gate_id,
2,032✔
1235
                arguments,
2,032✔
1236
                qubits,
2,032✔
1237
            }));
2,032✔
1238
        }
2,032✔
1239
        Ok(1)
2,146✔
1240
    }
2,146✔
1241

1242
    /// Emit the bytecode for a single gate application in a gate-definition body.  These are not
1243
    /// allowed to be conditioned, because otherwise the containing `gate` would not be unitary.
1244
    fn emit_single_gate_gate(
254✔
1245
        &self,
254✔
1246
        bc: &mut Vec<Option<InternalBytecode>>,
254✔
1247
        gate_id: GateId,
254✔
1248
        arguments: Vec<Expr>,
254✔
1249
        qubits: Vec<QubitId>,
254✔
1250
    ) -> PyResult<usize> {
254✔
1251
        bc.push(Some(InternalBytecode::GateInBody {
254✔
1252
            id: gate_id,
254✔
1253
            arguments,
254✔
1254
            qubits,
254✔
1255
        }));
254✔
1256
        Ok(1)
254✔
1257
    }
254✔
1258

1259
    /// Parse a complete conditional statement, including the operation that follows the condition
1260
    /// (though this work is delegated to the requisite other grammar rule).  This assumes that the
1261
    /// `if` token is still on the token stream.
1262
    fn parse_conditional(&mut self, bc: &mut Vec<Option<InternalBytecode>>) -> PyResult<usize> {
220✔
1263
        let if_token = self.expect_known(TokenType::If);
220✔
1264
        let lparen_token = self.expect(TokenType::LParen, "'('", &if_token)?;
220✔
1265
        let name_token = self.expect(TokenType::Id, "classical register", &if_token)?;
216✔
1266
        self.expect(TokenType::Equals, "'=='", &if_token)?;
210✔
1267
        let value = self
204✔
1268
            .expect(TokenType::Integer, "an integer", &if_token)?
204✔
1269
            .bigint(&self.context);
202✔
1270
        self.expect(TokenType::RParen, "')'", &lparen_token)?;
202✔
1271
        let name = name_token.id(&self.context);
200✔
1272
        let creg = match self.symbols.get(&name) {
200✔
1273
            Some(GlobalSymbol::Creg { index, .. }) => Ok(*index),
194✔
1274
            Some(symbol) => Err(QASM2ParseError::new_err(message_generic(
4✔
1275
                Some(&Position::new(
4✔
1276
                    self.current_filename(),
4✔
1277
                    name_token.line,
4✔
1278
                    name_token.col,
4✔
1279
                )),
4✔
1280
                &format!(
4✔
1281
                    "'{}' is {}, not a classical register",
4✔
1282
                    name,
4✔
1283
                    symbol.describe()
4✔
1284
                ),
4✔
1285
            ))),
4✔
1286
            None => Err(QASM2ParseError::new_err(message_generic(
2✔
1287
                Some(&Position::new(
2✔
1288
                    self.current_filename(),
2✔
1289
                    name_token.line,
2✔
1290
                    name_token.col,
2✔
1291
                )),
2✔
1292
                &format!("'{}' is not defined in this scope", name),
2✔
1293
            ))),
2✔
1294
        }?;
6✔
1295
        let condition = Some(Condition { creg, value });
194✔
1296
        match self.peek_token()?.map(|tok| tok.ttype) {
194✔
1297
            Some(TokenType::Id) => self.parse_gate_application(bc, condition, false),
160✔
1298
            Some(TokenType::Measure) => self.parse_measure(bc, condition),
20✔
1299
            Some(TokenType::Reset) => self.parse_reset(bc, condition),
10✔
1300
            Some(_) => {
1301
                let token = self.next_token()?;
2✔
1302
                Err(QASM2ParseError::new_err(message_incorrect_requirement(
2✔
1303
                    "a gate application, measurement or reset",
2✔
1304
                    &token.unwrap(),
2✔
1305
                    self.current_filename(),
2✔
1306
                )))
2✔
1307
            }
1308
            None => Err(QASM2ParseError::new_err(message_bad_eof(
2✔
1309
                Some(&Position::new(
2✔
1310
                    self.current_filename(),
2✔
1311
                    if_token.line,
2✔
1312
                    if_token.col,
2✔
1313
                )),
2✔
1314
                "a gate, measurement or reset to condition",
2✔
1315
            ))),
2✔
1316
        }
1317
    }
220✔
1318

1319
    /// Parse a barrier statement.  This assumes that the `barrier` token is still in the token
1320
    /// stream.
1321
    fn parse_barrier(
182✔
1322
        &mut self,
182✔
1323
        bc: &mut Vec<Option<InternalBytecode>>,
182✔
1324
        num_gate_qubits: Option<usize>,
182✔
1325
    ) -> PyResult<usize> {
182✔
1326
        let barrier_token = self.expect_known(TokenType::Barrier);
182✔
1327
        let qubits = if !self.next_is(TokenType::Semicolon)? {
182✔
1328
            let mut qubits = Vec::new();
162✔
1329
            let mut used = HashSet::<QubitId>::new();
162✔
1330
            let mut comma = None;
162✔
1331
            while let Some(qarg) = if num_gate_qubits.is_some() {
300✔
1332
                self.accept_qarg_gate()?
18✔
1333
            } else {
1334
                self.accept_qarg()?
282✔
1335
            } {
1336
                match qarg {
286✔
1337
                    Operand::Single(index) => {
180✔
1338
                        if used.insert(index) {
180✔
1339
                            qubits.push(index)
174✔
1340
                        }
6✔
1341
                    }
1342
                    Operand::Range(size, start) => qubits.extend((0..size).filter_map(|offset| {
298✔
1343
                        let index = start + offset;
298✔
1344
                        if used.insert(index) {
298✔
1345
                            Some(index)
284✔
1346
                        } else {
1347
                            None
14✔
1348
                        }
1349
                    })),
298✔
1350
                }
1351
                comma = self.accept(TokenType::Comma)?;
286✔
1352
                if comma.is_none() {
286✔
1353
                    break;
148✔
1354
                }
138✔
1355
            }
1356
            self.check_trailing_comma(comma.as_ref())?;
156✔
1357
            qubits
154✔
1358
        } else if self.strict {
20✔
1359
            return Err(QASM2ParseError::new_err(message_generic(
2✔
1360
                Some(&Position::new(
2✔
1361
                    self.current_filename(),
2✔
1362
                    barrier_token.line,
2✔
1363
                    barrier_token.col,
2✔
1364
                )),
2✔
1365
                "[strict] barrier statements must have at least one argument",
2✔
1366
            )));
2✔
1367
        } else if let Some(num_gate_qubits) = num_gate_qubits {
18✔
1368
            (0..num_gate_qubits).map(QubitId::new).collect::<Vec<_>>()
2✔
1369
        } else {
1370
            (0..self.num_qubits).map(QubitId::new).collect::<Vec<_>>()
16✔
1371
        };
1372
        self.expect(TokenType::Semicolon, "';'", &barrier_token)?;
172✔
1373
        if qubits.is_empty() {
158✔
1374
            Ok(0)
4✔
1375
        } else {
1376
            // The qubits are empty iff the only operands are zero-sized registers.  If there's no
1377
            // quantum arguments at all, then `qubits` will contain everything.
1378
            bc.push(Some(InternalBytecode::Barrier { qubits }));
154✔
1379
            Ok(1)
154✔
1380
        }
1381
    }
182✔
1382

1383
    /// Parse a measurement operation into bytecode.  This resolves any broadcast in the
1384
    /// measurement statement into a series of bytecode instructions, so we do more of the work in
1385
    /// Rust space than in Python space.
1386
    fn parse_measure(
376✔
1387
        &mut self,
376✔
1388
        bc: &mut Vec<Option<InternalBytecode>>,
376✔
1389
        condition: Option<Condition>,
376✔
1390
    ) -> PyResult<usize> {
376✔
1391
        let measure_token = self.expect_known(TokenType::Measure);
376✔
1392
        let qarg = self.require_qarg(&measure_token)?;
376✔
1393
        self.expect(TokenType::Arrow, "'->'", &measure_token)?;
360✔
1394
        let carg = self.require_carg(&measure_token)?;
352✔
1395
        self.expect(TokenType::Semicolon, "';'", &measure_token)?;
332✔
1396
        if let Some(Condition { creg, value }) = condition {
326✔
1397
            match (qarg, carg) {
20✔
1398
                (Operand::Single(qubit), Operand::Single(clbit)) => {
4✔
1399
                    bc.push(Some(InternalBytecode::ConditionedMeasure {
4✔
1400
                        qubit,
4✔
1401
                        clbit,
4✔
1402
                        creg,
4✔
1403
                        value,
4✔
1404
                    }));
4✔
1405
                    Ok(1)
4✔
1406
                }
1407
                (Operand::Range(q_size, q_start), Operand::Range(c_size, c_start))
6✔
1408
                    if q_size == c_size =>
8✔
1409
                {
6✔
1410
                    bc.extend((0..q_size).map(|i| {
8✔
1411
                        Some(InternalBytecode::ConditionedMeasure {
8✔
1412
                            qubit: q_start + i,
8✔
1413
                            clbit: c_start + i,
8✔
1414
                            creg,
8✔
1415
                            value: value.clone(),
8✔
1416
                        })
8✔
1417
                    }));
8✔
1418
                    Ok(q_size)
6✔
1419
                }
1420
                _ => Err(QASM2ParseError::new_err(message_generic(
10✔
1421
                    Some(&Position::new(
10✔
1422
                        self.current_filename(),
10✔
1423
                        measure_token.line,
10✔
1424
                        measure_token.col,
10✔
1425
                    )),
10✔
1426
                    "cannot resolve broadcast in measurement",
10✔
1427
                ))),
10✔
1428
            }
1429
        } else {
1430
            match (qarg, carg) {
306✔
1431
                (Operand::Single(qubit), Operand::Single(clbit)) => {
224✔
1432
                    bc.push(Some(InternalBytecode::Measure { qubit, clbit }));
224✔
1433
                    Ok(1)
224✔
1434
                }
1435
                (Operand::Range(q_size, q_start), Operand::Range(c_size, c_start))
72✔
1436
                    if q_size == c_size =>
74✔
1437
                {
72✔
1438
                    bc.extend((0..q_size).map(|i| {
202✔
1439
                        Some(InternalBytecode::Measure {
202✔
1440
                            qubit: q_start + i,
202✔
1441
                            clbit: c_start + i,
202✔
1442
                        })
202✔
1443
                    }));
202✔
1444
                    Ok(q_size)
72✔
1445
                }
1446
                _ => Err(QASM2ParseError::new_err(message_generic(
10✔
1447
                    Some(&Position::new(
10✔
1448
                        self.current_filename(),
10✔
1449
                        measure_token.line,
10✔
1450
                        measure_token.col,
10✔
1451
                    )),
10✔
1452
                    "cannot resolve broadcast in measurement",
10✔
1453
                ))),
10✔
1454
            }
1455
        }
1456
    }
376✔
1457

1458
    /// Parse a single reset statement.  This resolves any broadcast in the statement, i.e. if the
1459
    /// target is a register rather than a single qubit.  This assumes that the `reset` token is
1460
    /// still in the token stream.
1461
    fn parse_reset(
46✔
1462
        &mut self,
46✔
1463
        bc: &mut Vec<Option<InternalBytecode>>,
46✔
1464
        condition: Option<Condition>,
46✔
1465
    ) -> PyResult<usize> {
46✔
1466
        let reset_token = self.expect_known(TokenType::Reset);
46✔
1467
        let qarg = self.require_qarg(&reset_token)?;
46✔
1468
        self.expect(TokenType::Semicolon, "';'", &reset_token)?;
24✔
1469
        if let Some(Condition { creg, value }) = condition {
16✔
1470
            match qarg {
10✔
1471
                Operand::Single(qubit) => {
4✔
1472
                    bc.push(Some(InternalBytecode::ConditionedReset {
4✔
1473
                        qubit,
4✔
1474
                        creg,
4✔
1475
                        value,
4✔
1476
                    }));
4✔
1477
                    Ok(1)
4✔
1478
                }
1479
                Operand::Range(size, start) => {
6✔
1480
                    bc.extend((0..size).map(|offset| {
8✔
1481
                        Some(InternalBytecode::ConditionedReset {
8✔
1482
                            qubit: start + offset,
8✔
1483
                            creg,
8✔
1484
                            value: value.clone(),
8✔
1485
                        })
8✔
1486
                    }));
8✔
1487
                    Ok(size)
6✔
1488
                }
1489
            }
1490
        } else {
1491
            match qarg {
6✔
1492
                Operand::Single(qubit) => {
2✔
1493
                    bc.push(Some(InternalBytecode::Reset { qubit }));
2✔
1494
                    Ok(0)
2✔
1495
                }
1496
                Operand::Range(size, start) => {
4✔
1497
                    bc.extend((0..size).map(|offset| {
4✔
1498
                        Some(InternalBytecode::Reset {
4✔
1499
                            qubit: start + offset,
4✔
1500
                        })
4✔
1501
                    }));
4✔
1502
                    Ok(size)
4✔
1503
                }
1504
            }
1505
        }
1506
    }
46✔
1507

1508
    /// Parse a declaration of a classical register, emitting the relevant bytecode and adding the
1509
    /// definition to the relevant parts of the internal symbol tables in the parser state.  This
1510
    /// assumes that the `creg` token is still in the token stream.
1511
    fn parse_creg(&mut self, bc: &mut Vec<Option<InternalBytecode>>) -> PyResult<usize> {
888✔
1512
        let creg_token = self.expect_known(TokenType::Creg);
888✔
1513
        let name_token = self.expect(TokenType::Id, "a valid identifier", &creg_token)?;
888✔
1514
        let name = name_token.id(&self.context);
884✔
1515
        let lbracket_token = self.expect(TokenType::LBracket, "'['", &creg_token)?;
884✔
1516
        let size = self
880✔
1517
            .expect(TokenType::Integer, "an integer", &lbracket_token)?
880✔
1518
            .int(&self.context);
876✔
1519
        self.expect(TokenType::RBracket, "']'", &lbracket_token)?;
876✔
1520
        self.expect(TokenType::Semicolon, "';'", &creg_token)?;
874✔
1521
        let symbol = GlobalSymbol::Creg {
868✔
1522
            size,
868✔
1523
            start: ClbitId::new(self.num_clbits),
868✔
1524
            index: CregId::new(self.num_cregs),
868✔
1525
        };
868✔
1526
        if self.symbols.insert(name.clone(), symbol).is_none() {
868✔
1527
            self.num_clbits += size;
858✔
1528
            self.num_cregs += 1;
858✔
1529
            bc.push(Some(InternalBytecode::DeclareCreg { name, size }));
858✔
1530
            Ok(1)
858✔
1531
        } else {
1532
            Err(QASM2ParseError::new_err(message_generic(
10✔
1533
                Some(&Position::new(
10✔
1534
                    self.current_filename(),
10✔
1535
                    name_token.line,
10✔
1536
                    name_token.col,
10✔
1537
                )),
10✔
1538
                &format!("'{}' is already defined", name_token.id(&self.context)),
10✔
1539
            )))
10✔
1540
        }
1541
    }
888✔
1542

1543
    /// Parse a declaration of a quantum register, emitting the relevant bytecode and adding the
1544
    /// definition to the relevant parts of the internal symbol tables in the parser state.  This
1545
    /// assumes that the `qreg` token is still in the token stream.
1546
    fn parse_qreg(&mut self, bc: &mut Vec<Option<InternalBytecode>>) -> PyResult<usize> {
1,198✔
1547
        let qreg_token = self.expect_known(TokenType::Qreg);
1,198✔
1548
        let name_token = self.expect(TokenType::Id, "a valid identifier", &qreg_token)?;
1,198✔
1549
        let name = name_token.id(&self.context);
1,192✔
1550
        let lbracket_token = self.expect(TokenType::LBracket, "'['", &qreg_token)?;
1,192✔
1551
        let size = self
1,186✔
1552
            .expect(TokenType::Integer, "an integer", &lbracket_token)?
1,186✔
1553
            .int(&self.context);
1,182✔
1554
        self.expect(TokenType::RBracket, "']'", &lbracket_token)?;
1,182✔
1555
        self.expect(TokenType::Semicolon, "';'", &qreg_token)?;
1,178✔
1556
        let symbol = GlobalSymbol::Qreg {
1,174✔
1557
            size,
1,174✔
1558
            start: QubitId::new(self.num_qubits),
1,174✔
1559
        };
1,174✔
1560
        if self.symbols.insert(name.clone(), symbol).is_none() {
1,174✔
1561
            self.num_qubits += size;
1,164✔
1562
            bc.push(Some(InternalBytecode::DeclareQreg { name, size }));
1,164✔
1563
            Ok(1)
1,164✔
1564
        } else {
1565
            Err(QASM2ParseError::new_err(message_generic(
10✔
1566
                Some(&Position::new(
10✔
1567
                    self.current_filename(),
10✔
1568
                    name_token.line,
10✔
1569
                    name_token.col,
10✔
1570
                )),
10✔
1571
                &format!("'{}' is already defined", name_token.id(&self.context)),
10✔
1572
            )))
10✔
1573
        }
1574
    }
1,198✔
1575

1576
    /// Parse an include statement.  This currently only actually handles includes of `qelib1.inc`,
1577
    /// which aren't actually parsed; the parser has a built-in version of the file that it simply
1578
    /// updates its state with (and the Python side of the parser does the same) rather than
1579
    /// re-parsing the same file every time.  This assumes that the `include` token is still in the
1580
    /// token stream.
1581
    fn parse_include(&mut self, bc: &mut Vec<Option<InternalBytecode>>) -> PyResult<usize> {
340✔
1582
        let include_token = self.expect_known(TokenType::Include);
340✔
1583
        let filename_token =
326✔
1584
            self.expect(TokenType::Filename, "a filename string", &include_token)?;
340✔
1585
        self.expect(TokenType::Semicolon, "';'", &include_token)?;
326✔
1586
        let filename = filename_token.filename(&self.context);
322✔
1587
        if filename == "qelib1.inc" {
322✔
1588
            self.symbols.reserve(QELIB1.len());
302✔
1589
            let mut indices = Vec::with_capacity(QELIB1.len());
302✔
1590
            for (i, (name, num_params, num_qubits)) in QELIB1.iter().enumerate() {
6,946✔
1591
                if self.define_gate(
6,946✔
1592
                    Some(&include_token),
6,946✔
1593
                    name.to_string(),
6,946✔
1594
                    *num_params,
6,946✔
1595
                    *num_qubits,
6,946✔
1596
                )? {
6,946✔
1597
                    indices.push(i);
4,272✔
1598
                }
4,272✔
1599
            }
1600
            bc.push(Some(InternalBytecode::SpecialInclude { indices }));
302✔
1601
            Ok(1)
302✔
1602
        } else {
1603
            let base_filename = std::path::PathBuf::from(&filename);
20✔
1604
            let absolute_filename = find_include_path(&base_filename, &self.include_path)
20✔
1605
                .ok_or_else(|| {
20✔
1606
                    QASM2ParseError::new_err(message_generic(
2✔
1607
                        Some(&Position::new(
2✔
1608
                            self.current_filename(),
2✔
1609
                            filename_token.line,
2✔
1610
                            filename_token.col,
2✔
1611
                        )),
2✔
1612
                        &format!(
2✔
1613
                            "unable to find '{}' in the include search path",
2✔
1614
                            base_filename.display()
2✔
1615
                        ),
2✔
1616
                    ))
2✔
1617
                })?;
20✔
1618
            let new_stream =
18✔
1619
                TokenStream::from_path(absolute_filename, self.strict).map_err(|err| {
18✔
1620
                    QASM2ParseError::new_err(message_generic(
×
1621
                        Some(&Position::new(
×
1622
                            self.current_filename(),
×
1623
                            filename_token.line,
×
1624
                            filename_token.col,
×
1625
                        )),
×
1626
                        &format!("unable to open file '{}' for reading: {}", &filename, err),
×
1627
                    ))
×
1628
                })?;
18✔
1629
            self.tokens.push(new_stream);
18✔
1630
            self.allow_version = true;
18✔
1631
            Ok(0)
18✔
1632
        }
1633
    }
340✔
1634

1635
    /// Update the parser state with the definition of a particular gate.  This does not emit any
1636
    /// bytecode because not all gate definitions need something passing to Python.  For example,
1637
    /// the Python parser initializes its state including the built-in gates `U` and `CX`, and
1638
    /// handles the `qelib1.inc` include specially as well.
1639
    ///
1640
    /// Returns whether the gate needs to be defined in Python space (`true`) or if it was some sort
1641
    /// of built-in that doesn't need the definition (`false`).
1642
    fn define_gate(
9,564✔
1643
        &mut self,
9,564✔
1644
        owner: Option<&Token>,
9,564✔
1645
        name: String,
9,564✔
1646
        num_params: usize,
9,564✔
1647
        num_qubits: usize,
9,564✔
1648
    ) -> PyResult<bool> {
9,564✔
1649
        let already_defined = |state: &Self, name: String| {
9,564✔
1650
            let pos = owner.map(|tok| Position::new(state.current_filename(), tok.line, tok.col));
16✔
1651
            Err(QASM2ParseError::new_err(message_generic(
16✔
1652
                pos.as_ref(),
16✔
1653
                &format!("'{}' is already defined", name),
16✔
1654
            )))
16✔
1655
        };
16✔
1656
        let mismatched_definitions = |state: &Self, name: String, previous: OverridableGate| {
9,564✔
1657
            let plural = |count: usize, singular: &str| {
80✔
1658
                let mut out = format!("{} {}", count, singular);
80✔
1659
                if count != 1 {
80✔
1660
                    out.push('s');
26✔
1661
                }
54✔
1662
                out
80✔
1663
            };
80✔
1664
            let from_custom = format!(
20✔
1665
                "{} and {}",
20✔
1666
                plural(previous.num_params, "parameter"),
20✔
1667
                plural(previous.num_qubits, "qubit")
20✔
1668
            );
20✔
1669
            let from_program = format!(
20✔
1670
                "{} and {}",
20✔
1671
                plural(num_params, "parameter"),
20✔
1672
                plural(num_qubits, "qubit")
20✔
1673
            );
20✔
1674
            let pos = owner.map(|tok| Position::new(state.current_filename(), tok.line, tok.col));
20✔
1675
            Err(QASM2ParseError::new_err(message_generic(
20✔
1676
                pos.as_ref(),
20✔
1677
                &format!(
20✔
1678
                    concat!(
20✔
1679
                        "custom instruction '{}' is mismatched with its definition: ",
20✔
1680
                        "OpenQASM program has {}, custom has {}",
20✔
1681
                    ),
20✔
1682
                    name, from_program, from_custom
20✔
1683
                ),
20✔
1684
            )))
20✔
1685
        };
20✔
1686

1687
        if let Some(symbol) = self.overridable_gates.remove(&name) {
9,564✔
1688
            if num_params != symbol.num_params || num_qubits != symbol.num_qubits {
2,734✔
1689
                return mismatched_definitions(self, name, symbol);
20✔
1690
            }
2,714✔
1691
            match self.symbols.get(&name) {
2,714✔
1692
                None => {
1693
                    // The gate wasn't a built-in, so we need to move the symbol in, but we don't
1694
                    // need to increment the number of gates because it's already got a gate ID
1695
                    // assigned.
1696
                    self.symbols.insert(name, symbol.into());
2,688✔
1697
                    Ok(false)
2,688✔
1698
                }
1699
                Some(GlobalSymbol::Gate { .. }) => {
1700
                    // The gate was built-in and we can ignore the new definition (it's the same).
1701
                    Ok(false)
26✔
1702
                }
1703
                _ => already_defined(self, name),
×
1704
            }
1705
        } else if self.symbols.contains_key(&name) {
6,830✔
1706
            already_defined(self, name)
16✔
1707
        } else {
1708
            self.symbols.insert(
6,814✔
1709
                name,
6,814✔
1710
                GlobalSymbol::Gate {
6,814✔
1711
                    num_params,
6,814✔
1712
                    num_qubits,
6,814✔
1713
                    index: GateId::new(self.num_gates),
6,814✔
1714
                },
6,814✔
1715
            );
6,814✔
1716
            self.num_gates += 1;
6,814✔
1717
            Ok(true)
6,814✔
1718
        }
1719
    }
9,564✔
1720

1721
    /// Parse the next OpenQASM 2 statement in the program into a series of bytecode instructions.
1722
    ///
1723
    /// This is the principal public worker function of the parser.  One call to this function
1724
    /// parses a single OpenQASM 2 statement, which may expand to several bytecode instructions if
1725
    /// there is any broadcasting to resolve, or if the statement is a gate definition.  A call to
1726
    /// this function that returns `Some` will always have pushed at least one instruction to the
1727
    /// bytecode stream (the number is included).  A return of `None` signals the end of the
1728
    /// iterator.
1729
    pub fn parse_next(
6,098✔
1730
        &mut self,
6,098✔
1731
        bc: &mut Vec<Option<InternalBytecode>>,
6,098✔
1732
    ) -> PyResult<Option<usize>> {
6,098✔
1733
        if self.strict && self.allow_version {
6,098✔
1734
            match self.peek_token()?.map(|tok| tok.ttype) {
46✔
1735
                Some(TokenType::OpenQASM) => self.parse_version(),
42✔
1736
                Some(_) => {
1737
                    let token = self.next_token()?.unwrap();
2✔
1738
                    Err(QASM2ParseError::new_err(message_generic(
2✔
1739
                        Some(&Position::new(
2✔
1740
                            self.current_filename(),
2✔
1741
                            token.line,
2✔
1742
                            token.col,
2✔
1743
                        )),
2✔
1744
                        "[strict] the first statement must be 'OPENQASM 2.0;'",
2✔
1745
                    )))
2✔
1746
                }
1747
                None => Err(QASM2ParseError::new_err(message_generic(
2✔
1748
                    None,
2✔
1749
                    "[strict] saw an empty token stream, but needed a version statement",
2✔
1750
                ))),
2✔
1751
            }?;
4✔
1752
            self.allow_version = false;
42✔
1753
        }
6,052✔
1754
        let allow_version = self.allow_version;
6,094✔
1755
        self.allow_version = false;
6,094✔
1756
        while let Some(ttype) = self.peek_token()?.map(|tok| tok.ttype) {
6,652✔
1757
            let emitted = match ttype {
6,152✔
1758
                TokenType::Id => self.parse_gate_application(bc, None, false)?,
2,060✔
1759
                TokenType::Creg => self.parse_creg(bc)?,
888✔
1760
                TokenType::Qreg => self.parse_qreg(bc)?,
1,198✔
1761
                TokenType::Include => self.parse_include(bc)?,
340✔
1762
                TokenType::Measure => self.parse_measure(bc, None)?,
356✔
1763
                TokenType::Reset => self.parse_reset(bc, None)?,
36✔
1764
                TokenType::Barrier => self.parse_barrier(bc, None)?,
170✔
1765
                TokenType::If => self.parse_conditional(bc)?,
220✔
1766
                TokenType::Opaque => self.parse_opaque_definition(bc)?,
96✔
1767
                TokenType::Gate => self.parse_gate_definition(bc)?,
286✔
1768
                TokenType::OpenQASM => {
1769
                    if allow_version {
484✔
1770
                        self.parse_version()?
482✔
1771
                    } else {
1772
                        let token = self.next_token()?.unwrap();
2✔
1773
                        return Err(QASM2ParseError::new_err(message_generic(
2✔
1774
                            Some(&Position::new(
2✔
1775
                                self.current_filename(),
2✔
1776
                                token.line,
2✔
1777
                                token.col,
2✔
1778
                            )),
2✔
1779
                            "only the first statement may be a version declaration",
2✔
1780
                        )));
2✔
1781
                    }
1782
                }
1783
                TokenType::Semicolon => {
1784
                    let token = self.next_token()?.unwrap();
18✔
1785
                    if self.strict {
18✔
1786
                        return Err(QASM2ParseError::new_err(message_generic(
4✔
1787
                            Some(&Position::new(
4✔
1788
                                self.current_filename(),
4✔
1789
                                token.line,
4✔
1790
                                token.col,
4✔
1791
                            )),
4✔
1792
                            "[strict] empty statements and/or extra semicolons are forbidden",
4✔
1793
                        )));
4✔
1794
                    } else {
1795
                        0
14✔
1796
                    }
1797
                }
1798
                _ => {
UNCOV
1799
                    let token = self.next_token()?.unwrap();
×
UNCOV
1800
                    return Err(QASM2ParseError::new_err(message_generic(
×
UNCOV
1801
                        Some(&Position::new(
×
UNCOV
1802
                            self.current_filename(),
×
UNCOV
1803
                            token.line,
×
UNCOV
1804
                            token.col,
×
UNCOV
1805
                        )),
×
UNCOV
1806
                        &format!(
×
UNCOV
1807
                            "needed a start-of-statement token, but instead got {}",
×
UNCOV
1808
                            token.text(&self.context)
×
UNCOV
1809
                        ),
×
UNCOV
1810
                    )));
×
1811
                }
1812
            };
1813
            if emitted > 0 {
5,490✔
1814
                return Ok(Some(emitted));
4,932✔
1815
            }
558✔
1816
        }
1817
        Ok(None)
476✔
1818
    }
6,098✔
1819
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc