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

Qiskit / qiskit / 12129097356

02 Dec 2024 10:28PM UTC coverage: 88.944% (+0.007%) from 88.937%
12129097356

Pull #13300

github

web-flow
Merge 90e517022 into cc30af587
Pull Request #13300: Improve qiskit.circuit.QuantumCircuit page

95 of 97 new or added lines in 1 file covered. (97.94%)

8 existing lines in 2 files now uncovered.

79386 of 89254 relevant lines covered (88.94%)

358803.18 hits per line

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

97.62
/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::{PyObject, PyResult, Python};
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)]
68
        pub struct $id(usize);
69

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

76
        impl pyo3::IntoPy<PyObject> for $id {
77
            fn into_py(self, py: Python<'_>) -> PyObject {
8,316✔
78
                self.0.into_py(py)
8,316✔
79
            }
8,316✔
80
        }
81
    };
82

83
    ($id:ident, true) => {
84
        newtype_id!($id, false);
85

86
        impl std::ops::Add<usize> for $id {
87
            type Output = Self;
88

89
            fn add(self, rhs: usize) -> Self {
5,808✔
90
                Self::new(self.0 + rhs)
5,808✔
91
            }
5,808✔
92
        }
93
    };
94
}
95

96
newtype_id!(GateId, false);
97
newtype_id!(CregId, false);
98
newtype_id!(ParamId, false);
99
newtype_id!(QubitId, true);
100
newtype_id!(ClbitId, true);
101

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

125
impl GlobalSymbol {
126
    pub fn describe(&self) -> &'static str {
42✔
127
        match self {
42✔
128
            Self::Qreg { .. } => "a quantum register",
10✔
129
            Self::Creg { .. } => "a classical register",
12✔
130
            Self::Gate { .. } => "a gate",
14✔
131
            Self::Classical { .. } => "a custom classical function",
6✔
132
        }
133
    }
42✔
134
}
135

136
/// Information about a gate that permits a new definition in the OQ3 file only in the case that
137
/// the number of parameters and qubits matches.  This is used when the user specifies custom gates
138
/// that are not
139
struct OverridableGate {
140
    num_params: usize,
141
    num_qubits: usize,
142
    index: GateId,
143
}
144

145
impl From<OverridableGate> for GlobalSymbol {
146
    fn from(value: OverridableGate) -> Self {
3,608✔
147
        Self::Gate {
3,608✔
148
            num_params: value.num_params,
3,608✔
149
            num_qubits: value.num_qubits,
3,608✔
150
            index: value.index,
3,608✔
151
        }
3,608✔
152
    }
3,608✔
153
}
154

155
/// A symbol in the scope of a single gate definition.  This only includes the symbols that are
156
/// specifically gate-scoped.  The rest are part of [GlobalSymbol].
157
pub enum GateSymbol {
158
    Qubit { index: QubitId },
159
    Parameter { index: ParamId },
160
}
161

162
impl GateSymbol {
163
    pub fn describe(&self) -> &'static str {
6✔
164
        match self {
6✔
165
            Self::Qubit { .. } => "a qubit",
2✔
166
            Self::Parameter { .. } => "a parameter",
4✔
167
        }
168
    }
6✔
169
}
170

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

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

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

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

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

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

351
    /// Get the next token available in the stack of token streams, popping and removing any
352
    /// complete streams, except the base case.  Will only return `None` once all streams are
353
    /// exhausted.
354
    fn next_token(&mut self) -> PyResult<Option<Token>> {
49,668✔
355
        let mut pointer = self.tokens.len() - 1;
49,668✔
356
        while pointer > 0 {
49,670✔
357
            let out = self.tokens[pointer].next(&mut self.context)?;
140✔
358
            if out.is_some() {
140✔
359
                return Ok(out);
138✔
360
            }
2✔
361
            self.tokens.pop();
2✔
362
            pointer -= 1;
2✔
363
        }
364
        self.tokens[0].next(&mut self.context)
49,530✔
365
    }
49,668✔
366

367
    /// Peek the next token in the stack of token streams.  This does not remove any complete
368
    /// streams yet.  Will only return `None` once all streams are exhausted.
369
    fn peek_token(&mut self) -> PyResult<Option<&Token>> {
31,372✔
370
        let mut pointer = self.tokens.len() - 1;
31,372✔
371
        while pointer > 0 && self.tokens[pointer].peek(&mut self.context)?.is_none() {
31,390✔
372
            pointer -= 1;
18✔
373
        }
18✔
374
        self.tokens[pointer].peek(&mut self.context)
31,372✔
375
    }
31,372✔
376

377
    /// Get the filename associated with the currently active token stream.
378
    fn current_filename(&self) -> &std::ffi::OsStr {
608✔
379
        &self.tokens[self.tokens.len() - 1].filename
608✔
380
    }
608✔
381

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

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

425
    /// Take the next token from the stream, if it is of the correct type.  Returns `None` and
426
    /// leaves the next token in the underlying iterator if it does not match.
427
    fn accept(&mut self, expected: TokenType) -> PyResult<Option<Token>> {
20,420✔
428
        let peeked = self.peek_token()?;
20,420✔
429
        if peeked.is_some() && peeked.unwrap().ttype == expected {
20,420✔
430
            self.next_token()
13,876✔
431
        } else {
432
            Ok(None)
6,544✔
433
        }
434
    }
20,420✔
435

436
    /// True if the next token in the stream matches the given type, and false if it doesn't.
437
    fn next_is(&mut self, expected: TokenType) -> PyResult<bool> {
1,922✔
438
        let peeked = self.peek_token()?;
1,922✔
439
        Ok(peeked.is_some() && peeked.unwrap().ttype == expected)
1,918✔
440
    }
1,922✔
441

442
    /// If in `strict` mode, and we have a trailing comma, emit a suitable error message.
443
    fn check_trailing_comma(&self, comma: Option<&Token>) -> PyResult<()> {
4,018✔
444
        match (self.strict, comma) {
4,018✔
445
            (true, Some(token)) => Err(QASM2ParseError::new_err(message_generic(
14✔
446
                Some(&Position::new(
14✔
447
                    self.current_filename(),
14✔
448
                    token.line,
14✔
449
                    token.col,
14✔
450
                )),
14✔
451
                "[strict] trailing commas in parameter and qubit lists are forbidden",
14✔
452
            ))),
14✔
453
            _ => Ok(()),
4,004✔
454
        }
455
    }
4,018✔
456

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1693
        if let Some(symbol) = self.overridable_gates.remove(&name) {
10,728✔
1694
            if num_params != symbol.num_params || num_qubits != symbol.num_qubits {
3,654✔
1695
                return mismatched_definitions(self, name, symbol);
20✔
1696
            }
3,634✔
1697
            match self.symbols.get(&name) {
3,634✔
1698
                None => {
1699
                    // The gate wasn't a built-in, so we need to move the symbol in, but we don't
1700
                    // need to increment the number of gates because it's already got a gate ID
1701
                    // assigned.
1702
                    self.symbols.insert(name, symbol.into());
3,608✔
1703
                    Ok(false)
3,608✔
1704
                }
1705
                Some(GlobalSymbol::Gate { .. }) => {
1706
                    // The gate was built-in and we can ignore the new definition (it's the same).
1707
                    Ok(false)
26✔
1708
                }
1709
                _ => already_defined(self, name),
×
1710
            }
1711
        } else if self.symbols.contains_key(&name) {
7,074✔
1712
            already_defined(self, name)
16✔
1713
        } else {
1714
            self.symbols.insert(
7,058✔
1715
                name,
7,058✔
1716
                GlobalSymbol::Gate {
7,058✔
1717
                    num_params,
7,058✔
1718
                    num_qubits,
7,058✔
1719
                    index: GateId::new(self.num_gates),
7,058✔
1720
                },
7,058✔
1721
            );
7,058✔
1722
            self.num_gates += 1;
7,058✔
1723
            Ok(true)
7,058✔
1724
        }
1725
    }
10,728✔
1726

1727
    /// Parse the next OpenQASM 2 statement in the program into a series of bytecode instructions.
1728
    ///
1729
    /// This is the principal public worker function of the parser.  One call to this function
1730
    /// parses a single OpenQASM 2 statement, which may expand to several bytecode instructions if
1731
    /// there is any broadcasting to resolve, or if the statement is a gate definition.  A call to
1732
    /// this function that returns `Some` will always have pushed at least one instruction to the
1733
    /// bytecode stream (the number is included).  A return of `None` signals the end of the
1734
    /// iterator.
1735
    pub fn parse_next(
6,662✔
1736
        &mut self,
6,662✔
1737
        bc: &mut Vec<Option<InternalBytecode>>,
6,662✔
1738
    ) -> PyResult<Option<usize>> {
6,662✔
1739
        if self.strict && self.allow_version {
6,662✔
1740
            match self.peek_token()?.map(|tok| tok.ttype) {
46✔
1741
                Some(TokenType::OpenQASM) => self.parse_version(),
42✔
1742
                Some(_) => {
1743
                    let token = self.next_token()?.unwrap();
2✔
1744
                    Err(QASM2ParseError::new_err(message_generic(
2✔
1745
                        Some(&Position::new(
2✔
1746
                            self.current_filename(),
2✔
1747
                            token.line,
2✔
1748
                            token.col,
2✔
1749
                        )),
2✔
1750
                        "[strict] the first statement must be 'OPENQASM 2.0;'",
2✔
1751
                    )))
2✔
1752
                }
1753
                None => Err(QASM2ParseError::new_err(message_generic(
2✔
1754
                    None,
2✔
1755
                    "[strict] saw an empty token stream, but needed a version statement",
2✔
1756
                ))),
2✔
1757
            }?;
4✔
1758
            self.allow_version = false;
42✔
1759
        }
6,616✔
1760
        let allow_version = self.allow_version;
6,658✔
1761
        self.allow_version = false;
6,658✔
1762
        while let Some(ttype) = self.peek_token()?.map(|tok| tok.ttype) {
7,312✔
1763
            let emitted = match ttype {
6,770✔
1764
                TokenType::Id => self.parse_gate_application(bc, None, false)?,
2,118✔
1765
                TokenType::Creg => self.parse_creg(bc)?,
1,078✔
1766
                TokenType::Qreg => self.parse_qreg(bc)?,
1,302✔
1767
                TokenType::Include => self.parse_include(bc)?,
380✔
1768
                TokenType::Measure => self.parse_measure(bc, None)?,
404✔
1769
                TokenType::Reset => self.parse_reset(bc, None)?,
32✔
1770
                TokenType::Barrier => self.parse_barrier(bc, None)?,
174✔
1771
                TokenType::If => self.parse_conditional(bc)?,
270✔
1772
                TokenType::Opaque => self.parse_opaque_definition(bc)?,
108✔
1773
                TokenType::Gate => self.parse_gate_definition(bc)?,
302✔
1774
                TokenType::OpenQASM => {
1775
                    if allow_version {
580✔
1776
                        self.parse_version()?
578✔
1777
                    } else {
1778
                        let token = self.next_token()?.unwrap();
2✔
1779
                        return Err(QASM2ParseError::new_err(message_generic(
2✔
1780
                            Some(&Position::new(
2✔
1781
                                self.current_filename(),
2✔
1782
                                token.line,
2✔
1783
                                token.col,
2✔
1784
                            )),
2✔
1785
                            "only the first statement may be a version declaration",
2✔
1786
                        )));
2✔
1787
                    }
1788
                }
1789
                TokenType::Semicolon => {
1790
                    let token = self.next_token()?.unwrap();
18✔
1791
                    if self.strict {
18✔
1792
                        return Err(QASM2ParseError::new_err(message_generic(
4✔
1793
                            Some(&Position::new(
4✔
1794
                                self.current_filename(),
4✔
1795
                                token.line,
4✔
1796
                                token.col,
4✔
1797
                            )),
4✔
1798
                            "[strict] empty statements and/or extra semicolons are forbidden",
4✔
1799
                        )));
4✔
1800
                    } else {
1801
                        0
14✔
1802
                    }
1803
                }
1804
                _ => {
1805
                    let token = self.next_token()?.unwrap();
4✔
1806
                    return Err(QASM2ParseError::new_err(message_generic(
4✔
1807
                        Some(&Position::new(
4✔
1808
                            self.current_filename(),
4✔
1809
                            token.line,
4✔
1810
                            token.col,
4✔
1811
                        )),
4✔
1812
                        &format!(
4✔
1813
                            "needed a start-of-statement token, but instead got {}",
4✔
1814
                            token.text(&self.context)
4✔
1815
                        ),
4✔
1816
                    )));
4✔
1817
                }
1818
            };
1819
            if emitted > 0 {
6,052✔
1820
                return Ok(Some(emitted));
5,398✔
1821
            }
654✔
1822
        }
1823
        Ok(None)
518✔
1824
    }
6,662✔
1825
}
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

© 2024 Coveralls, Inc