Skip to main content

qrab/
parser.rs

1use std::collections::HashMap;
2use std::ops::Range;
3
4use logos::Logos;
5use miette::Diagnostic as MietteDiagnostic;
6use thiserror::Error;
7
8use crate::ast::{
9    BackendEscapes, BraceSide, Circuit, Control, EscapeBlock, Group, Layout, MeasurementShape,
10    NoteSide, Operation, OperationKind, Orientation, ParityBasis, Shape, Span, Style, Wire,
11    WireKind,
12};
13
14/// A source-located lexer, parser, or semantic diagnostic.
15///
16/// Diagnostics are produced by [`parse`](crate::parse) and are not constructed
17/// by downstream code.
18#[derive(Debug, Clone, PartialEq, Eq, MietteDiagnostic, Error)]
19#[error("{message}")]
20#[non_exhaustive]
21pub struct Diagnostic {
22    /// Human-readable explanation of the error.
23    pub message: String,
24    /// Byte range and derived source location where the error was detected.
25    pub span: Span,
26    /// Actionable advice, when the error has a direct remedy.
27    #[help]
28    pub help: Option<String>,
29    #[label]
30    label: Option<miette::SourceSpan>,
31    #[related]
32    related: Box<[Diagnostic]>,
33}
34
35impl Diagnostic {
36    fn new(message: impl Into<String>, span: Span) -> Self {
37        Self {
38            message: message.into(),
39            span,
40            help: None,
41            label: Some(span.into()),
42            related: Box::default(),
43        }
44    }
45
46    fn with_help(message: impl Into<String>, help: impl Into<String>, span: Span) -> Self {
47        Self {
48            message: message.into(),
49            span,
50            help: Some(help.into()),
51            label: Some(span.into()),
52            related: Box::default(),
53        }
54    }
55
56    fn multiple(mut diagnostics: Vec<Self>) -> Self {
57        debug_assert!(!diagnostics.is_empty());
58        if diagnostics.len() == 1 {
59            return diagnostics.pop().expect("one diagnostic exists");
60        }
61        Self {
62            message: format!("{} errors found", diagnostics.len()),
63            span: diagnostics[0].span,
64            help: None,
65            label: None,
66            related: diagnostics.into_boxed_slice(),
67        }
68    }
69
70    /// Returns independently located errors collected after parser recovery.
71    pub fn related(&self) -> &[Self] {
72        &self.related
73    }
74}
75
76#[derive(Debug, Clone, PartialEq, Eq)]
77enum TokenKind {
78    Identifier(String),
79    String(String),
80    Number(String),
81    LeftBrace,
82    RightBrace,
83    LeftParen,
84    RightParen,
85    LeftBracket,
86    RightBracket,
87    Colon,
88    Equal,
89    Comma,
90    Bang,
91    Arrow,
92    DotDot,
93    Newline,
94    End,
95}
96
97#[derive(Debug, Clone, PartialEq, Eq)]
98struct Token {
99    kind: TokenKind,
100    span: Span,
101}
102
103#[derive(Logos, Debug, Clone, Copy, PartialEq, Eq)]
104#[logos(skip r"[ \t\r]+")]
105#[logos(skip(r"//[^\n]*", allow_greedy = true))]
106enum Lexeme {
107    #[regex(r"[A-Za-z_][A-Za-z0-9_]*")]
108    Identifier,
109    #[regex(r#""([^"\\\n]|\\.)*""#)]
110    String,
111    #[regex(r"[0-9]+(\.[0-9]+)?")]
112    Number,
113    #[token("{")]
114    LeftBrace,
115    #[token("}")]
116    RightBrace,
117    #[token("(")]
118    LeftParen,
119    #[token(")")]
120    RightParen,
121    #[token("[")]
122    LeftBracket,
123    #[token("]")]
124    RightBracket,
125    #[token(":")]
126    Colon,
127    #[token("=")]
128    Equal,
129    #[token(",")]
130    Comma,
131    #[token("!")]
132    Bang,
133    #[token("->")]
134    Arrow,
135    #[token("..")]
136    DotDot,
137    #[token("\n")]
138    #[token(";")]
139    Newline,
140}
141
142/// Parses one expanded `.qrab` source string into a checked circuit.
143///
144/// Use [`crate::load_source`] first when the source contains imports.
145pub fn parse(source: &str) -> Result<Circuit, Diagnostic> {
146    Parser::new(lex(source)?).parse_circuit()
147}
148
149fn lex(source: &str) -> Result<Vec<Token>, Diagnostic> {
150    let line_starts = std::iter::once(0)
151        .chain(source.match_indices('\n').map(|(offset, _)| offset + 1))
152        .collect::<Vec<_>>();
153    let mut lexer = Lexeme::lexer(source);
154    let mut tokens = Vec::new();
155    while let Some(lexeme) = lexer.next() {
156        let range = lexer.span();
157        let span = source_span(source, &line_starts, range.clone());
158        let lexeme = lexeme.map_err(|()| lex_error(source, &line_starts, range.clone()))?;
159        let kind = match lexeme {
160            Lexeme::Identifier => TokenKind::Identifier(lexer.slice().into()),
161            Lexeme::String => {
162                TokenKind::String(decode_string(source, &line_starts, range.clone())?)
163            }
164            Lexeme::Number => TokenKind::Number(lexer.slice().into()),
165            Lexeme::LeftBrace => TokenKind::LeftBrace,
166            Lexeme::RightBrace => TokenKind::RightBrace,
167            Lexeme::LeftParen => TokenKind::LeftParen,
168            Lexeme::RightParen => TokenKind::RightParen,
169            Lexeme::LeftBracket => TokenKind::LeftBracket,
170            Lexeme::RightBracket => TokenKind::RightBracket,
171            Lexeme::Colon => TokenKind::Colon,
172            Lexeme::Equal => TokenKind::Equal,
173            Lexeme::Comma => TokenKind::Comma,
174            Lexeme::Bang => TokenKind::Bang,
175            Lexeme::Arrow => TokenKind::Arrow,
176            Lexeme::DotDot => TokenKind::DotDot,
177            Lexeme::Newline => TokenKind::Newline,
178        };
179        tokens.push(Token { kind, span });
180    }
181    tokens.push(Token {
182        kind: TokenKind::End,
183        span: source_span(source, &line_starts, source.len()..source.len()),
184    });
185    Ok(tokens)
186}
187
188fn source_span(source: &str, line_starts: &[usize], range: Range<usize>) -> Span {
189    let line_index = line_starts
190        .partition_point(|start| *start <= range.start)
191        .saturating_sub(1);
192    Span {
193        offset: range.start,
194        length: range.len(),
195        line: line_index + 1,
196        column: source[line_starts[line_index]..range.start].chars().count() + 1,
197    }
198}
199
200fn decode_string(
201    source: &str,
202    line_starts: &[usize],
203    range: Range<usize>,
204) -> Result<String, Diagnostic> {
205    let mut value = String::new();
206    let mut characters = source[range.start + 1..range.end - 1].char_indices();
207    while let Some((_, character)) = characters.next() {
208        if character != '\\' {
209            value.push(character);
210            continue;
211        }
212        let (offset, escaped) = characters
213            .next()
214            .expect("the string token ends after a complete escape");
215        match escaped {
216            'n' => value.push('\n'),
217            'r' => value.push('\r'),
218            't' => value.push('\t'),
219            '"' | '\\' => value.push(escaped),
220            '$' => value.push_str(r"\$"),
221            _ => {
222                let start = range.start + 1 + offset;
223                return Err(Diagnostic::new(
224                    format!("unknown string escape `\\{escaped}`"),
225                    source_span(source, line_starts, start..start + escaped.len_utf8()),
226                ));
227            }
228        }
229    }
230    Ok(value)
231}
232
233fn lex_error(source: &str, line_starts: &[usize], range: Range<usize>) -> Diagnostic {
234    let character = source[range.clone()]
235        .chars()
236        .next()
237        .expect("logos errors cover at least one character");
238    if character != '"' {
239        return Diagnostic::new(
240            format!("unexpected character `{character}`"),
241            source_span(source, line_starts, range),
242        );
243    }
244
245    let opening = source_span(source, line_starts, range.clone());
246    let mut characters = source[range.end..].char_indices();
247    while let Some((offset, character)) = characters.next() {
248        if character == '\n' {
249            let start = range.end + offset;
250            return Diagnostic::new(
251                "strings cannot cross lines",
252                source_span(source, line_starts, start..start + 1),
253            );
254        }
255        if character == '\\' {
256            match characters.next() {
257                Some((offset, '\n')) => {
258                    let start = range.end + offset;
259                    return Diagnostic::new(
260                        "strings cannot cross lines",
261                        source_span(source, line_starts, start..start + 1),
262                    );
263                }
264                Some(_) => {}
265                None => return Diagnostic::new("unterminated string escape", opening),
266            }
267        }
268    }
269    Diagnostic::new("unterminated string", opening)
270}
271
272fn is_reserved_statement(name: &str) -> bool {
273    matches!(
274        name,
275        "circuit"
276            | "import"
277            | "fn"
278            | "let"
279            | "style"
280            | "layout"
281            | "backend"
282            | "qubit"
283            | "bit"
284            | "hidden"
285            | "ellipsis"
286            | "autowires"
287            | "h"
288            | "x"
289            | "y"
290            | "z"
291            | "s"
292            | "t"
293            | "gate"
294            | "phase"
295            | "measure"
296            | "swap"
297            | "barrier"
298            | "set"
299            | "start"
300            | "end"
301            | "label"
302            | "equals"
303            | "bundle"
304            | "permute"
305            | "space"
306            | "touch"
307            | "repeat"
308            | "reverse"
309            | "parallel"
310            | "overlay"
311            | "labels"
312            | "brace"
313            | "note"
314            | "above"
315            | "below"
316            | "cut"
317            | "mark"
318            | "group"
319            | "if"
320            | "on"
321            | "as"
322            | "to"
323            | "from"
324            | "here"
325            | "with"
326            | "using"
327            | "braced"
328            | "latex"
329            | "typst"
330            | "preamble"
331            | "before"
332            | "after"
333    )
334}
335
336fn is_style_property(name: &str) -> bool {
337    matches!(
338        name,
339        "stroke" | "fill" | "link" | "width" | "height" | "size" | "shape" | "dash" | "opacity"
340    )
341}
342
343struct Parser {
344    tokens: Vec<Token>,
345    position: usize,
346    wires: Vec<Wire>,
347    wire_indices: HashMap<String, usize>,
348    operations: Vec<Operation>,
349    layout: Layout,
350    functions: HashMap<String, Function>,
351    values: HashMap<String, String>,
352    styles: HashMap<String, Style>,
353    in_function: bool,
354    operation_block_depth: usize,
355    marks: HashMap<String, usize>,
356    groups: Vec<Group>,
357    escapes: BackendEscapes,
358    auto_wires: bool,
359    next_overlay: usize,
360}
361
362#[derive(Clone, Copy)]
363struct Checkpoint {
364    position: usize,
365    wires: usize,
366    operations: usize,
367    groups: usize,
368    operation_block_depth: usize,
369    next_overlay: usize,
370}
371
372#[derive(Clone)]
373struct Function {
374    parameters: Vec<String>,
375    body: Vec<Operation>,
376}
377
378impl Parser {
379    fn new(tokens: Vec<Token>) -> Self {
380        Self {
381            tokens,
382            position: 0,
383            wires: Vec::new(),
384            wire_indices: HashMap::new(),
385            operations: Vec::new(),
386            layout: Layout::default(),
387            functions: HashMap::new(),
388            values: HashMap::new(),
389            styles: HashMap::new(),
390            in_function: false,
391            operation_block_depth: 0,
392            marks: HashMap::new(),
393            groups: Vec::new(),
394            escapes: BackendEscapes::default(),
395            auto_wires: false,
396            next_overlay: 0,
397        }
398    }
399
400    fn parse_circuit(mut self) -> Result<Circuit, Diagnostic> {
401        self.skip_newlines();
402        while self.at_keyword("fn") || self.at_keyword("let") || self.at_keyword("style") {
403            if self.at_keyword("fn") {
404                self.parse_function_definition()?;
405            } else if self.at_keyword("let") {
406                self.parse_value_definition()?;
407            } else {
408                self.parse_style_definition()?;
409            }
410            self.skip_newlines();
411        }
412        self.expect_keyword("circuit")?;
413        let name = self.take_identifier("circuit name")?;
414        self.expect(TokenKind::LeftBrace, "`{`")?;
415        self.skip_newlines();
416
417        let mut diagnostics = Vec::new();
418        while !self.at(&TokenKind::RightBrace) && !self.at(&TokenKind::End) {
419            let checkpoint = self.checkpoint();
420            if let Err(diagnostic) = self.parse_statement() {
421                self.restore(checkpoint);
422                self.recover_statement();
423                diagnostics.push(diagnostic);
424            }
425            self.skip_newlines();
426        }
427        if self.at(&TokenKind::End) {
428            diagnostics.push(self.error("expected `}` to close the circuit"));
429        } else {
430            self.advance();
431            self.skip_newlines();
432            if let Err(diagnostic) = self.expect(TokenKind::End, "end of file") {
433                diagnostics.push(diagnostic);
434            }
435        }
436        if !diagnostics.is_empty() {
437            return Err(Diagnostic::multiple(diagnostics));
438        }
439
440        if self.wires.is_empty() {
441            return Err(Diagnostic::new(
442                "a circuit needs at least one wire",
443                Span {
444                    offset: 0,
445                    length: 0,
446                    line: 1,
447                    column: 1,
448                },
449            ));
450        }
451        self.resolve_active_defaults()?;
452
453        Ok(Circuit {
454            name,
455            layout: self.layout,
456            wires: self.wires,
457            operations: self.operations,
458            groups: self.groups,
459            escapes: self.escapes,
460        })
461    }
462
463    fn checkpoint(&self) -> Checkpoint {
464        Checkpoint {
465            position: self.position,
466            wires: self.wires.len(),
467            operations: self.operations.len(),
468            groups: self.groups.len(),
469            operation_block_depth: self.operation_block_depth,
470            next_overlay: self.next_overlay,
471        }
472    }
473
474    fn restore(&mut self, checkpoint: Checkpoint) {
475        self.position = checkpoint.position;
476        self.wires.truncate(checkpoint.wires);
477        self.wire_indices.retain(|_, wire| *wire < checkpoint.wires);
478        self.operations.truncate(checkpoint.operations);
479        self.groups.truncate(checkpoint.groups);
480        self.operation_block_depth = checkpoint.operation_block_depth;
481        self.next_overlay = checkpoint.next_overlay;
482    }
483
484    fn resolve_active_defaults(&mut self) -> Result<(), Diagnostic> {
485        let wire_count = self.wires.len();
486        let mut active = (0..wire_count)
487            .map(|wire| {
488                !self
489                    .operations
490                    .iter()
491                    .find_map(|operation| {
492                        if let OperationKind::Endpoint { wires, start, .. } = &operation.kind
493                            && (wires.is_empty() || wires.contains(&wire))
494                        {
495                            Some(*start)
496                        } else {
497                            None
498                        }
499                    })
500                    .unwrap_or(false)
501            })
502            .collect::<Vec<_>>();
503
504        for operation in &mut self.operations {
505            let active_wires = || {
506                active
507                    .iter()
508                    .enumerate()
509                    .filter_map(|(wire, is_active)| is_active.then_some(wire))
510                    .collect::<Vec<_>>()
511            };
512            let inactive_wires = || {
513                active
514                    .iter()
515                    .enumerate()
516                    .filter_map(|(wire, is_active)| (!is_active).then_some(wire))
517                    .collect::<Vec<_>>()
518            };
519
520            let defaulted_empty = match &mut operation.kind {
521                OperationKind::Endpoint { wires, start, .. } if wires.is_empty() => {
522                    *wires = if *start {
523                        inactive_wires()
524                    } else {
525                        active_wires()
526                    };
527                    wires.is_empty()
528                }
529                OperationKind::Barrier { wires }
530                | OperationKind::Label { wires, .. }
531                | OperationKind::Phantom { wires }
532                | OperationKind::Touch { wires }
533                | OperationKind::WireLabels { wires, .. }
534                | OperationKind::Brace { wires, .. }
535                | OperationKind::Note { wires, .. }
536                | OperationKind::Cut { wires, .. }
537                    if wires.is_empty() =>
538                {
539                    *wires = active_wires();
540                    wires.is_empty()
541                }
542                _ => false,
543            };
544            if defaulted_empty {
545                return Err(Diagnostic::new(
546                    "the targetless statement has no applicable active wires",
547                    operation.span,
548                ));
549            }
550            if let OperationKind::WireLabels { wires, labels } = &operation.kind
551                && labels.len() != 1
552                && labels.len() != wires.len()
553            {
554                return Err(Diagnostic::new(
555                    format!(
556                        "labels needs one label or one per selected wire ({}), but got {}",
557                        wires.len(),
558                        labels.len()
559                    ),
560                    operation.span,
561                ));
562            }
563            if let OperationKind::Endpoint { wires, start, .. } = &operation.kind {
564                for wire in wires {
565                    active[*wire] = *start;
566                }
567            }
568        }
569        Ok(())
570    }
571
572    fn parse_value_definition(&mut self) -> Result<(), Diagnostic> {
573        let span = self.current().span;
574        self.expect_keyword("let")?;
575        let name = self.take_identifier("value name")?;
576        if is_reserved_statement(&name)
577            || self.values.contains_key(&name)
578            || self.styles.contains_key(&name)
579            || self.functions.contains_key(&name)
580        {
581            return Err(Diagnostic::new(
582                format!("definition name `{name}` is reserved or already used"),
583                span,
584            ));
585        }
586        self.expect(TokenKind::Equal, "`=`")?;
587        let value = self.take_label("string or earlier value")?;
588        self.expect_statement_end()?;
589        self.values.insert(name, value);
590        Ok(())
591    }
592
593    fn parse_style_definition(&mut self) -> Result<(), Diagnostic> {
594        let span = self.current().span;
595        self.expect_keyword("style")?;
596        let name = self.take_identifier("style name")?;
597        if is_reserved_statement(&name)
598            || is_style_property(&name)
599            || self.values.contains_key(&name)
600            || self.styles.contains_key(&name)
601            || self.functions.contains_key(&name)
602        {
603            return Err(Diagnostic::new(
604                format!("definition name `{name}` is reserved or already used"),
605                span,
606            ));
607        }
608        self.expect(TokenKind::LeftBrace, "`{`")?;
609        let mut style = Style::default();
610        self.skip_newlines();
611        while !self.at(&TokenKind::RightBrace) {
612            self.parse_style_property(&mut style)?;
613            self.expect_statement_end()?;
614            self.skip_newlines();
615        }
616        self.advance();
617        self.expect_statement_end()?;
618        self.styles.insert(name, style);
619        Ok(())
620    }
621
622    fn parse_function_definition(&mut self) -> Result<(), Diagnostic> {
623        let span = self.current().span;
624        self.expect_keyword("fn")?;
625        let name = self.take_identifier("function name")?;
626        if is_reserved_statement(&name) {
627            return Err(Diagnostic::new(
628                format!("function name `{name}` is reserved"),
629                span,
630            ));
631        }
632        if self.functions.contains_key(&name)
633            || self.values.contains_key(&name)
634            || self.styles.contains_key(&name)
635        {
636            return Err(Diagnostic::new(
637                format!("function `{name}` is already defined"),
638                span,
639            ));
640        }
641        self.expect(TokenKind::LeftParen, "`(`")?;
642        let mut parameters = Vec::new();
643        if !self.at(&TokenKind::RightParen) {
644            loop {
645                let parameter_span = self.current().span;
646                let parameter = self.take_identifier("parameter name")?;
647                if parameters.contains(&parameter) {
648                    return Err(Diagnostic::new(
649                        format!("parameter `{parameter}` is repeated"),
650                        parameter_span,
651                    ));
652                }
653                parameters.push(parameter);
654                if !self.consume(&TokenKind::Comma) {
655                    break;
656                }
657            }
658        }
659        self.expect(TokenKind::RightParen, "`)`")?;
660        self.expect(TokenKind::LeftBrace, "`{`")?;
661
662        let circuit_wires = std::mem::take(&mut self.wires);
663        let circuit_indices = std::mem::take(&mut self.wire_indices);
664        let circuit_operations = std::mem::take(&mut self.operations);
665        self.wires = parameters
666            .iter()
667            .map(|parameter| Wire {
668                name: parameter.clone(),
669                kind: WireKind::Quantum,
670                ellipsis: false,
671                input: None,
672                output: None,
673                style: Style::default(),
674            })
675            .collect();
676        self.wire_indices = parameters
677            .iter()
678            .enumerate()
679            .map(|(index, parameter)| (parameter.clone(), index))
680            .collect();
681        self.in_function = true;
682        self.skip_newlines();
683        while !self.at(&TokenKind::RightBrace) {
684            if self.at(&TokenKind::End) {
685                return Err(self.error("expected `}` to close the function"));
686            }
687            self.parse_statement()?;
688            self.skip_newlines();
689        }
690        self.advance();
691        let body = std::mem::take(&mut self.operations);
692        self.wires = circuit_wires;
693        self.wire_indices = circuit_indices;
694        self.operations = circuit_operations;
695        self.in_function = false;
696        self.functions.insert(name, Function { parameters, body });
697        self.expect_statement_end()
698    }
699
700    fn parse_statement(&mut self) -> Result<(), Diagnostic> {
701        let span = self.current().span;
702        let keyword = self.take_identifier("statement")?;
703        if (self.in_function || self.operation_block_depth > 0)
704            && matches!(
705                keyword.as_str(),
706                "layout" | "backend" | "qubit" | "bit" | "hidden" | "autowires" | "mark" | "group"
707            )
708        {
709            return Err(Diagnostic::new(
710                "operation blocks may contain operations and function calls, not declarations",
711                span,
712            ));
713        }
714        match keyword.as_str() {
715            "layout" => self.parse_layout(),
716            "backend" => self.parse_backend(),
717            "qubit" => self.parse_wire_declaration(WireKind::Quantum, false),
718            "bit" => self.parse_wire_declaration(WireKind::Classical, false),
719            "hidden" => self.parse_wire_declaration(WireKind::Hidden, false),
720            "ellipsis" => self.parse_wire_declaration(WireKind::Hidden, true),
721            "autowires" => self.parse_autowires(),
722            "h" | "x" | "y" | "z" | "s" | "t" => {
723                self.parse_builtin_gate(keyword.to_ascii_uppercase(), span)
724            }
725            "gate" => self.parse_named_gate(span),
726            "phase" => self.parse_phase_gate(span),
727            "measure" => self.parse_measure(span),
728            "swap" => self.parse_swap(span),
729            "barrier" => self.parse_barrier(span),
730            "set" => self.parse_wire_change(span),
731            "start" => self.parse_endpoint(span, true),
732            "end" => self.parse_endpoint(span, false),
733            "label" => self.parse_label(span),
734            "equals" => self.parse_equals(span),
735            "bundle" => self.parse_bundle(span),
736            "permute" => self.parse_permute(span),
737            "space" => self.parse_phantom(span),
738            "touch" => self.parse_touch(span),
739            "repeat" => self.parse_repeat(span),
740            "reverse" => self.parse_reverse(span),
741            "parallel" => self.parse_parallel(span),
742            "overlay" => self.parse_overlay(span),
743            "labels" => self.parse_wire_labels(span),
744            "brace" => self.parse_brace(span),
745            "note" => self.parse_note(span),
746            "cut" => self.parse_cut(span),
747            "mark" => self.parse_mark(span),
748            "group" => self.parse_group(span),
749            name if self.functions.contains_key(name) => self.parse_function_call(name, span),
750            _ => Err(Diagnostic::new(
751                format!("unknown statement `{keyword}`"),
752                span,
753            )),
754        }
755    }
756
757    fn parse_function_call(&mut self, name: &str, span: Span) -> Result<(), Diagnostic> {
758        self.expect(TokenKind::LeftParen, "`(` after the function name")?;
759        let mut arguments = Vec::new();
760        if !self.at(&TokenKind::RightParen) {
761            loop {
762                arguments.extend(self.parse_wire_selection(true)?);
763                if !self.consume(&TokenKind::Comma) {
764                    break;
765                }
766            }
767        }
768        self.expect(TokenKind::RightParen, "`)`")?;
769        self.expect_statement_end()?;
770        let function = self
771            .functions
772            .get(name)
773            .cloned()
774            .expect("function dispatch checked the name");
775        if arguments.len() != function.parameters.len() {
776            return Err(Diagnostic::new(
777                format!(
778                    "function `{name}` expects {} wire argument(s), but got {}",
779                    function.parameters.len(),
780                    arguments.len()
781                ),
782                span,
783            ));
784        }
785        self.ensure_unique(&arguments, span, "function argument")?;
786        let start = self.operations.len();
787        self.operations
788            .extend(function.body.into_iter().map(|operation| Operation {
789                kind: operation.kind.remap_wires(&arguments),
790                span,
791                style: operation.style,
792                overlay: operation.overlay,
793            }));
794        self.freshen_overlays(start);
795        Ok(())
796    }
797
798    fn parse_repeat(&mut self, span: Span) -> Result<(), Diagnostic> {
799        let count = self.take_number("repeat count")?;
800        let body = self.parse_operation_block("repeat")?;
801        let added = body
802            .len()
803            .checked_mul(count)
804            .ok_or_else(|| Diagnostic::new("repeat block is too large", span))?;
805        self.operations
806            .try_reserve(added)
807            .map_err(|_| Diagnostic::new("repeat block is too large", span))?;
808        for _ in 0..count {
809            let start = self.operations.len();
810            self.operations.extend(body.iter().cloned());
811            self.freshen_overlays(start);
812        }
813        Ok(())
814    }
815
816    fn parse_reverse(&mut self, span: Span) -> Result<(), Diagnostic> {
817        let mut body = if self.consume_keyword("from") {
818            let start_span = self.current().span;
819            let start_name = self.take_identifier("start mark")?;
820            let start = self.marks.get(&start_name).copied().ok_or_else(|| {
821                Diagnostic::new(format!("unknown mark `{start_name}`"), start_span)
822            })?;
823            self.expect_keyword("to")?;
824            let end = if self.consume_keyword("here") {
825                self.operations.len()
826            } else {
827                let end_span = self.current().span;
828                let end_name = self.take_identifier("end mark or `here`")?;
829                self.marks.get(&end_name).copied().ok_or_else(|| {
830                    Diagnostic::new(format!("unknown mark `{end_name}`"), end_span)
831                })?
832            };
833            self.expect_statement_end()?;
834            if start >= end {
835                return Err(Diagnostic::new(
836                    "a reversed range must contain at least one operation",
837                    span,
838                ));
839            }
840            self.operations[start..end].to_vec()
841        } else {
842            self.parse_operation_block("reverse")?
843        };
844        body.reverse();
845        let start = self.operations.len();
846        self.operations.extend(body);
847        self.freshen_overlays(start);
848        Ok(())
849    }
850
851    fn parse_parallel(&mut self, span: Span) -> Result<(), Diagnostic> {
852        let body = self.parse_operation_block("parallel")?;
853        if body.is_empty() {
854            return Ok(());
855        }
856        let touch = || Operation {
857            kind: OperationKind::Touch { wires: Vec::new() },
858            span,
859            style: Style::default(),
860            overlay: None,
861        };
862        self.operations.push(touch());
863        self.operations.extend(body);
864        self.operations.push(touch());
865        Ok(())
866    }
867
868    fn parse_overlay(&mut self, span: Span) -> Result<(), Diagnostic> {
869        let mut body = self.parse_operation_block("overlay")?;
870        if body.is_empty() {
871            return Ok(());
872        }
873        if let Some(operation) = body.iter().find(|operation| {
874            matches!(
875                operation.kind,
876                OperationKind::Endpoint { .. } | OperationKind::Permute { .. }
877            )
878        }) {
879            return Err(Diagnostic::new(
880                "overlay blocks cannot contain lifecycle changes or permutations",
881                operation.span,
882            ));
883        }
884        let mut occupied = vec![false; self.wires.len()];
885        for operation in &body {
886            if matches!(operation.kind, OperationKind::Note { .. }) {
887                continue;
888            }
889            for wire in operation
890                .kind
891                .occupied_wires(self.wires.len())
892                .iter()
893                .copied()
894            {
895                if std::mem::replace(&mut occupied[wire], true) {
896                    return Err(Diagnostic::with_help(
897                        format!(
898                            "overlay operations cannot share wire `{}`",
899                            self.wires[wire].name
900                        ),
901                        "use `parallel` to serialize operations on the same wire",
902                        span,
903                    ));
904                }
905            }
906        }
907        let overlay = self.next_overlay;
908        self.next_overlay += 1;
909        for operation in &mut body {
910            operation.overlay = Some(overlay);
911        }
912        self.operations.extend(body);
913        Ok(())
914    }
915
916    fn freshen_overlays(&mut self, start: usize) {
917        let mut overlays = HashMap::new();
918        for operation in &mut self.operations[start..] {
919            let Some(old) = operation.overlay else {
920                continue;
921            };
922            let next_overlay = &mut self.next_overlay;
923            operation.overlay = Some(*overlays.entry(old).or_insert_with(|| {
924                let overlay = *next_overlay;
925                *next_overlay += 1;
926                overlay
927            }));
928        }
929    }
930
931    fn parse_operation_block(&mut self, name: &str) -> Result<Vec<Operation>, Diagnostic> {
932        self.expect(TokenKind::LeftBrace, "`{`")?;
933        let start = self.operations.len();
934        // The caller either succeeds or restores the whole statement from a checkpoint.
935        self.operation_block_depth += 1;
936        self.skip_newlines();
937        while !self.at(&TokenKind::RightBrace) {
938            if self.at(&TokenKind::End) {
939                return Err(self.error(format!("expected `}}` to close the {name} block")));
940            }
941            self.parse_statement()?;
942            self.skip_newlines();
943        }
944        self.advance();
945        self.operation_block_depth -= 1;
946        let body = self.operations.split_off(start);
947        self.expect_statement_end()?;
948        Ok(body)
949    }
950
951    fn recover_statement(&mut self) {
952        let mut braces = 0_usize;
953        loop {
954            match self.current().kind {
955                TokenKind::End => break,
956                TokenKind::LeftBrace => {
957                    braces += 1;
958                    self.advance();
959                }
960                TokenKind::RightBrace if braces == 0 => break,
961                TokenKind::RightBrace => {
962                    braces -= 1;
963                    self.advance();
964                    if braces == 0 {
965                        break;
966                    }
967                }
968                TokenKind::Newline if braces == 0 => {
969                    self.advance();
970                    break;
971                }
972                _ => self.advance(),
973            }
974        }
975    }
976
977    fn parse_layout(&mut self) -> Result<(), Diagnostic> {
978        let mut layout = self.layout.clone();
979        self.expect(TokenKind::LeftBrace, "`{`")?;
980        self.skip_newlines();
981        while !self.at(&TokenKind::RightBrace) {
982            let span = self.current().span;
983            let property = self.take_identifier("layout property")?;
984            self.expect(TokenKind::Colon, "`:`")?;
985            match property.as_str() {
986                "orientation" => {
987                    let value = self.take_identifier("`horizontal` or `vertical`")?;
988                    layout.orientation = match value.as_str() {
989                        "horizontal" => Orientation::Horizontal,
990                        "vertical" => Orientation::Vertical,
991                        _ => {
992                            return Err(Diagnostic::new(
993                                "orientation must be `horizontal` or `vertical`",
994                                span,
995                            ));
996                        }
997                    };
998                }
999                "scale" => {
1000                    layout.scale = self.take_positive_scalar("layout scale")?;
1001                }
1002                "column_gap" => {
1003                    layout.column_gap = self.take_positive_scalar("column gap")?;
1004                }
1005                "wire_gap" => {
1006                    layout.wire_gap = self.take_positive_scalar("wire gap")?;
1007                }
1008                "gate_size" => {
1009                    layout.gate_size = self.take_positive_scalar("gate size")?;
1010                }
1011                "corner_radius" => {
1012                    layout.corner_radius = self.take_scalar("corner radius")?;
1013                    if layout.corner_radius < 0.0 {
1014                        return Err(Diagnostic::new("corner radius cannot be negative", span));
1015                    }
1016                }
1017                "comment_width" => {
1018                    layout.comment_width = self.take_positive_scalar("comment width")?;
1019                }
1020                "background" => layout.background = self.take_color("background color")?,
1021                _ => {
1022                    return Err(Diagnostic::new(
1023                        format!("unknown layout property `{property}`"),
1024                        span,
1025                    ));
1026                }
1027            }
1028            self.expect_statement_end()?;
1029            self.skip_newlines();
1030        }
1031        self.advance();
1032        self.expect_statement_end()?;
1033        self.layout = layout;
1034        Ok(())
1035    }
1036
1037    fn parse_backend(&mut self) -> Result<(), Diagnostic> {
1038        let target_span = self.current().span;
1039        let target = self.take_identifier("`latex` or `typst`")?;
1040        if !matches!(target.as_str(), "latex" | "typst") {
1041            return Err(Diagnostic::new(
1042                "backend target must be `latex` or `typst`",
1043                target_span,
1044            ));
1045        }
1046        self.expect(TokenKind::LeftBrace, "`{`")?;
1047        let mut block = EscapeBlock::default();
1048        self.skip_newlines();
1049        while !self.at(&TokenKind::RightBrace) {
1050            let section_span = self.current().span;
1051            let section = self.take_identifier("`preamble`, `before`, or `after`")?;
1052            self.expect(TokenKind::Colon, "`:`")?;
1053            let code = self.take_string("backend code string")?;
1054            match section.as_str() {
1055                "preamble" => block.preamble.push(code),
1056                "before" => block.before.push(code),
1057                "after" => block.after.push(code),
1058                _ => {
1059                    return Err(Diagnostic::new(
1060                        "backend section must be `preamble`, `before`, or `after`",
1061                        section_span,
1062                    ));
1063                }
1064            }
1065            self.expect_statement_end()?;
1066            self.skip_newlines();
1067        }
1068        self.advance();
1069        self.expect_statement_end()?;
1070
1071        let destination = if target == "latex" {
1072            &mut self.escapes.latex
1073        } else {
1074            &mut self.escapes.typst
1075        };
1076        destination.preamble.extend(block.preamble);
1077        destination.before.extend(block.before);
1078        destination.after.extend(block.after);
1079        Ok(())
1080    }
1081
1082    fn parse_wire_declaration(&mut self, kind: WireKind, ellipsis: bool) -> Result<(), Diagnostic> {
1083        let span = self.current().span;
1084        let base = self.take_identifier("wire name")?;
1085        let count = if self.consume(&TokenKind::LeftBracket) {
1086            let count = self.take_number("wire count")?;
1087            self.expect(TokenKind::RightBracket, "`]`")?;
1088            if count == 0 {
1089                return Err(Diagnostic::new("wire arrays cannot be empty", span));
1090            }
1091            Some(count)
1092        } else {
1093            None
1094        };
1095        let input = if self.consume(&TokenKind::Colon) {
1096            Some(self.take_string("input label")?)
1097        } else {
1098            None
1099        };
1100        let output = if self.consume(&TokenKind::Arrow) {
1101            Some(self.take_string("output label")?)
1102        } else {
1103            None
1104        };
1105        let style = self.parse_style()?;
1106        self.expect_statement_end()?;
1107
1108        if ellipsis && count.is_some() {
1109            return Err(Diagnostic::new(
1110                "an ellipsis declaration names one visual gap",
1111                span,
1112            ));
1113        }
1114
1115        let names = count.map_or_else(
1116            || vec![base.clone()],
1117            |length| {
1118                (0..length)
1119                    .map(|index| format!("{base}[{index}]"))
1120                    .collect()
1121            },
1122        );
1123        for name in names {
1124            if self.wire_indices.contains_key(&name) {
1125                return Err(Diagnostic::new(
1126                    format!("wire `{name}` is already declared"),
1127                    span,
1128                ));
1129            }
1130            let index = self.wires.len();
1131            self.wire_indices.insert(name.clone(), index);
1132            self.wires.push(Wire {
1133                name,
1134                kind,
1135                ellipsis,
1136                input: input.clone().or_else(|| ellipsis.then(|| "...".into())),
1137                output: output.clone().or_else(|| ellipsis.then(|| "...".into())),
1138                style: style.clone(),
1139            });
1140        }
1141        Ok(())
1142    }
1143
1144    fn parse_autowires(&mut self) -> Result<(), Diagnostic> {
1145        self.expect_statement_end()?;
1146        self.auto_wires = true;
1147        Ok(())
1148    }
1149
1150    fn parse_builtin_gate(&mut self, label: String, span: Span) -> Result<(), Diagnostic> {
1151        let target = self.parse_wire_reference()?;
1152        let controls = self.parse_controls()?;
1153        let style = self.parse_style()?;
1154        self.expect_statement_end()?;
1155        self.push_gate(label, vec![target], controls, style, span)
1156    }
1157
1158    fn parse_named_gate(&mut self, span: Span) -> Result<(), Diagnostic> {
1159        let label = self.take_label("gate label")?;
1160        self.expect_keyword("on")?;
1161        let targets = self.parse_wire_list()?;
1162        let controls = self.parse_controls()?;
1163        let style = self.parse_style()?;
1164        self.expect_statement_end()?;
1165        self.push_gate(label, targets, controls, style, span)
1166    }
1167
1168    fn parse_phase_gate(&mut self, span: Span) -> Result<(), Diagnostic> {
1169        let phase = self.take_label("phase label")?;
1170        self.expect_keyword("on")?;
1171        let target = self.parse_wire_reference()?;
1172        let controls = self.parse_controls()?;
1173        let mut style = self.parse_style()?;
1174        // The circle is what marks a phase gate, exactly as in qpic, so the
1175        // label is drawn as written. Wrapping it in `P(...)` put three extra
1176        // characters inside a shape whose diameter has to span its own label.
1177        style.shape.get_or_insert(Shape::Circle);
1178        self.expect_statement_end()?;
1179        self.push_gate(phase, vec![target], controls, style, span)
1180    }
1181
1182    fn parse_measure(&mut self, span: Span) -> Result<(), Diagnostic> {
1183        let targets = self.parse_wire_list()?;
1184        let label = if self.consume_keyword("as") {
1185            Some(self.take_label("measurement label")?)
1186        } else {
1187            None
1188        };
1189        let explicit_shape = self.consume_keyword("using");
1190        let shape = if explicit_shape {
1191            let shape_span = self.current().span;
1192            match self.take_identifier("`d` or `tag`")?.as_str() {
1193                "d" => MeasurementShape::D,
1194                "tag" => MeasurementShape::Tag,
1195                _ => {
1196                    return Err(Diagnostic::new(
1197                        "measurement shape must be `d` or `tag`",
1198                        shape_span,
1199                    ));
1200                }
1201            }
1202        } else {
1203            MeasurementShape::D
1204        };
1205        let style = self.parse_style()?;
1206        self.expect_statement_end()?;
1207        if label.is_none() && explicit_shape {
1208            return Err(Diagnostic::new(
1209                "a shaped measurement needs a label introduced by `as`",
1210                span,
1211            ));
1212        }
1213        if style.shape.is_some() {
1214            return Err(Diagnostic::with_help(
1215                "the generic `shape` style does not apply to measurements",
1216                "use `using d` or `using tag` after the measurement target",
1217                span,
1218            ));
1219        }
1220        self.ensure_unique(&targets, span, "measurement target")?;
1221        self.operations.push(Operation {
1222            kind: OperationKind::Measure {
1223                targets,
1224                label,
1225                shape,
1226            },
1227            span,
1228            style,
1229            overlay: None,
1230        });
1231        Ok(())
1232    }
1233
1234    fn parse_swap(&mut self, span: Span) -> Result<(), Diagnostic> {
1235        let left = self.parse_wire_reference()?;
1236        self.expect(TokenKind::Comma, "`,`")?;
1237        let right = self.parse_wire_reference()?;
1238        let style = self.parse_style()?;
1239        self.expect_statement_end()?;
1240        if left == right {
1241            return Err(Diagnostic::new(
1242                "a wire cannot be swapped with itself",
1243                span,
1244            ));
1245        }
1246        self.operations.push(Operation {
1247            kind: OperationKind::Swap { left, right },
1248            span,
1249            style,
1250            overlay: None,
1251        });
1252        Ok(())
1253    }
1254
1255    fn parse_barrier(&mut self, span: Span) -> Result<(), Diagnostic> {
1256        let wires = if self.at_statement_end() || self.at_keyword("with") {
1257            Vec::new()
1258        } else {
1259            self.parse_wire_list()?
1260        };
1261        let style = self.parse_style()?;
1262        self.expect_statement_end()?;
1263        self.ensure_unique(&wires, span, "barrier wire")?;
1264        self.operations.push(Operation {
1265            kind: OperationKind::Barrier { wires },
1266            span,
1267            style,
1268            overlay: None,
1269        });
1270        Ok(())
1271    }
1272
1273    fn parse_wire_change(&mut self, span: Span) -> Result<(), Diagnostic> {
1274        let wires = self.parse_wire_list()?;
1275        self.expect_keyword("to")?;
1276        let kind = self.parse_wire_kind()?;
1277        let label = if self.consume_keyword("as") {
1278            Some(self.take_label("known wire value")?)
1279        } else {
1280            None
1281        };
1282        let style = self.parse_style()?;
1283        self.expect_statement_end()?;
1284        if label.is_some() && kind == WireKind::Classical {
1285            return Err(Diagnostic::new(
1286                "known-value markers only apply to `quantum` or `hidden` transitions",
1287                span,
1288            ));
1289        }
1290        self.ensure_unique(&wires, span, "wire")?;
1291        self.operations.push(Operation {
1292            kind: OperationKind::WireChange { wires, kind, label },
1293            span,
1294            style,
1295            overlay: None,
1296        });
1297        Ok(())
1298    }
1299
1300    fn parse_endpoint(&mut self, span: Span, start: bool) -> Result<(), Diagnostic> {
1301        let wires = if self.at_statement_end() || self.at_keyword("as") || self.at_keyword("with") {
1302            Vec::new()
1303        } else {
1304            self.parse_wire_list()?
1305        };
1306        let label = if self.consume_keyword("as") {
1307            Some(self.take_label("endpoint label")?)
1308        } else {
1309            None
1310        };
1311        let style = self.parse_style()?;
1312        self.expect_statement_end()?;
1313        self.ensure_unique(&wires, span, "endpoint wire")?;
1314        self.operations.push(Operation {
1315            kind: OperationKind::Endpoint {
1316                wires,
1317                start,
1318                label,
1319            },
1320            span,
1321            style,
1322            overlay: None,
1323        });
1324        Ok(())
1325    }
1326
1327    fn parse_label(&mut self, span: Span) -> Result<(), Diagnostic> {
1328        let label = self.take_label("label text")?;
1329        let wires = if self.consume_keyword("on") {
1330            self.parse_wire_list()?
1331        } else {
1332            Vec::new()
1333        };
1334        let style = self.parse_style()?;
1335        self.expect_statement_end()?;
1336        self.ensure_unique(&wires, span, "label wire")?;
1337        self.operations.push(Operation {
1338            kind: OperationKind::Label {
1339                wires,
1340                label,
1341                brace: None,
1342            },
1343            span,
1344            style,
1345            overlay: None,
1346        });
1347        Ok(())
1348    }
1349
1350    fn parse_equals(&mut self, span: Span) -> Result<(), Diagnostic> {
1351        let label = if self.at_statement_end()
1352            || self.at_keyword("on")
1353            || self.at_keyword("braced")
1354            || self.at_keyword("with")
1355        {
1356            "=".into()
1357        } else {
1358            self.take_label("equals label")?
1359        };
1360        let wires = if self.consume_keyword("on") {
1361            self.parse_wire_list()?
1362        } else {
1363            Vec::new()
1364        };
1365        let brace = if self.consume_keyword("braced") {
1366            let brace_span = self.current().span;
1367            Some(
1368                match self.take_identifier("`left`, `right`, or `both`")?.as_str() {
1369                    "left" => BraceSide::Left,
1370                    "right" => BraceSide::Right,
1371                    "both" => BraceSide::Both,
1372                    _ => {
1373                        return Err(Diagnostic::new(
1374                            "brace side must be `left`, `right`, or `both`",
1375                            brace_span,
1376                        ));
1377                    }
1378                },
1379            )
1380        } else {
1381            None
1382        };
1383        let style = self.parse_style()?;
1384        self.expect_statement_end()?;
1385        self.ensure_unique(&wires, span, "equals wire")?;
1386        self.operations.push(Operation {
1387            kind: OperationKind::Label {
1388                wires,
1389                label,
1390                brace,
1391            },
1392            span,
1393            style,
1394            overlay: None,
1395        });
1396        Ok(())
1397    }
1398
1399    fn parse_wire_labels(&mut self, span: Span) -> Result<(), Diagnostic> {
1400        let mut labels = vec![self.take_label("wire label")?];
1401        while self.consume(&TokenKind::Comma) {
1402            labels.push(self.take_label("wire label")?);
1403        }
1404        let wires = if self.consume_keyword("on") {
1405            self.parse_wire_list()?
1406        } else {
1407            Vec::new()
1408        };
1409        let style = self.parse_style()?;
1410        self.expect_statement_end()?;
1411        self.ensure_unique(&wires, span, "label wire")?;
1412        self.operations.push(Operation {
1413            kind: OperationKind::WireLabels { wires, labels },
1414            span,
1415            style,
1416            overlay: None,
1417        });
1418        Ok(())
1419    }
1420
1421    fn parse_brace(&mut self, span: Span) -> Result<(), Diagnostic> {
1422        let side_span = self.current().span;
1423        let side = match self.take_identifier("`left`, `right`, or `both`")?.as_str() {
1424            "left" => BraceSide::Left,
1425            "right" => BraceSide::Right,
1426            "both" => BraceSide::Both,
1427            _ => {
1428                return Err(Diagnostic::new(
1429                    "brace side must be `left`, `right`, or `both`",
1430                    side_span,
1431                ));
1432            }
1433        };
1434        let label = self.take_label("brace label")?;
1435        let wires = if self.consume_keyword("on") {
1436            self.parse_wire_list()?
1437        } else {
1438            Vec::new()
1439        };
1440        let style = self.parse_style()?;
1441        self.expect_statement_end()?;
1442        self.ensure_unique(&wires, span, "brace wire")?;
1443        self.operations.push(Operation {
1444            kind: OperationKind::Brace { wires, label, side },
1445            span,
1446            style,
1447            overlay: None,
1448        });
1449        Ok(())
1450    }
1451
1452    fn parse_note(&mut self, span: Span) -> Result<(), Diagnostic> {
1453        let side = if self.consume_keyword("below") {
1454            NoteSide::Below
1455        } else {
1456            self.consume_keyword("above");
1457            NoteSide::Above
1458        };
1459        let text = self.take_label("note text")?;
1460        let wires = if self.consume_keyword("on") {
1461            self.parse_wire_list()?
1462        } else {
1463            Vec::new()
1464        };
1465        let style = self.parse_style()?;
1466        self.expect_statement_end()?;
1467        self.ensure_unique(&wires, span, "note wire")?;
1468        self.operations.push(Operation {
1469            kind: OperationKind::Note { wires, text, side },
1470            span,
1471            style,
1472            overlay: None,
1473        });
1474        Ok(())
1475    }
1476
1477    fn parse_cut(&mut self, span: Span) -> Result<(), Diagnostic> {
1478        let wires = if self.consume_keyword("on") {
1479            self.parse_wire_list()?
1480        } else {
1481            Vec::new()
1482        };
1483        let label = if self.consume_keyword("as") {
1484            Some(self.take_label("cut label")?)
1485        } else {
1486            None
1487        };
1488        let style = self.parse_style()?;
1489        self.expect_statement_end()?;
1490        self.ensure_unique(&wires, span, "cut wire")?;
1491        self.operations.push(Operation {
1492            kind: OperationKind::Cut { wires, label },
1493            span,
1494            style,
1495            overlay: None,
1496        });
1497        Ok(())
1498    }
1499
1500    fn parse_mark(&mut self, span: Span) -> Result<(), Diagnostic> {
1501        let name = self.take_identifier("mark name")?;
1502        self.expect_statement_end()?;
1503        if self.marks.contains_key(&name) {
1504            return Err(Diagnostic::new(
1505                format!("mark `{name}` is already defined"),
1506                span,
1507            ));
1508        }
1509        self.marks.insert(name, self.operations.len());
1510        Ok(())
1511    }
1512
1513    fn parse_group(&mut self, span: Span) -> Result<(), Diagnostic> {
1514        let label = self.take_label("group label")?;
1515        self.expect_keyword("from")?;
1516        let start_span = self.current().span;
1517        let start_name = self.take_identifier("start mark")?;
1518        let start =
1519            self.marks.get(&start_name).copied().ok_or_else(|| {
1520                Diagnostic::new(format!("unknown mark `{start_name}`"), start_span)
1521            })?;
1522        self.expect_keyword("to")?;
1523        let end = if self.consume_keyword("here") {
1524            self.operations.len()
1525        } else {
1526            let end_span = self.current().span;
1527            let end_name = self.take_identifier("end mark or `here`")?;
1528            self.marks
1529                .get(&end_name)
1530                .copied()
1531                .ok_or_else(|| Diagnostic::new(format!("unknown mark `{end_name}`"), end_span))?
1532        };
1533        let wires = if self.consume_keyword("on") {
1534            self.parse_wire_list()?
1535        } else {
1536            Vec::new()
1537        };
1538        let style = self.parse_style()?;
1539        self.expect_statement_end()?;
1540        self.ensure_unique(&wires, span, "group wire")?;
1541        if start >= end {
1542            return Err(Diagnostic::new(
1543                "a group must contain at least one operation after its start mark",
1544                span,
1545            ));
1546        }
1547        self.groups.push(Group {
1548            label,
1549            wires,
1550            start,
1551            end,
1552            style,
1553        });
1554        Ok(())
1555    }
1556
1557    fn parse_bundle(&mut self, span: Span) -> Result<(), Diagnostic> {
1558        let label = self.take_label("bundle label")?;
1559        self.expect_keyword("on")?;
1560        let wire = self.parse_wire_reference()?;
1561        let style = self.parse_style()?;
1562        self.expect_statement_end()?;
1563        self.operations.push(Operation {
1564            kind: OperationKind::Bundle { wire, label },
1565            span,
1566            style,
1567            overlay: None,
1568        });
1569        Ok(())
1570    }
1571
1572    fn parse_permute(&mut self, span: Span) -> Result<(), Diagnostic> {
1573        let wires = self.parse_wire_list()?;
1574        let style = self.parse_style()?;
1575        self.expect_statement_end()?;
1576        if wires.len() < 2 {
1577            return Err(Diagnostic::new("permute needs at least two wires", span));
1578        }
1579        self.ensure_unique(&wires, span, "permutation wire")?;
1580        self.operations.push(Operation {
1581            kind: OperationKind::Permute { wires },
1582            span,
1583            style,
1584            overlay: None,
1585        });
1586        Ok(())
1587    }
1588
1589    fn parse_phantom(&mut self, span: Span) -> Result<(), Diagnostic> {
1590        let wires = self.parse_optional_wires()?;
1591        let style = self.parse_style()?;
1592        self.expect_statement_end()?;
1593        self.ensure_unique(&wires, span, "space wire")?;
1594        self.operations.push(Operation {
1595            kind: OperationKind::Phantom { wires },
1596            span,
1597            style,
1598            overlay: None,
1599        });
1600        Ok(())
1601    }
1602
1603    fn parse_touch(&mut self, span: Span) -> Result<(), Diagnostic> {
1604        let wires = self.parse_optional_wires()?;
1605        let style = self.parse_style()?;
1606        self.expect_statement_end()?;
1607        self.ensure_unique(&wires, span, "touch wire")?;
1608        self.operations.push(Operation {
1609            kind: OperationKind::Touch { wires },
1610            span,
1611            style,
1612            overlay: None,
1613        });
1614        Ok(())
1615    }
1616
1617    fn parse_optional_wires(&mut self) -> Result<Vec<usize>, Diagnostic> {
1618        if self.at_statement_end() || self.at_keyword("with") {
1619            Ok(Vec::new())
1620        } else {
1621            self.parse_wire_list()
1622        }
1623    }
1624
1625    fn parse_wire_kind(&mut self) -> Result<WireKind, Diagnostic> {
1626        let span = self.current().span;
1627        let kind = self.take_identifier("wire type")?;
1628        match kind.as_str() {
1629            "quantum" | "qubit" => Ok(WireKind::Quantum),
1630            "classical" | "bit" => Ok(WireKind::Classical),
1631            "hidden" | "off" => Ok(WireKind::Hidden),
1632            _ => Err(Diagnostic::new(
1633                "wire type must be `quantum`, `classical`, or `hidden`",
1634                span,
1635            )),
1636        }
1637    }
1638
1639    fn push_gate(
1640        &mut self,
1641        label: String,
1642        targets: Vec<usize>,
1643        controls: Vec<Control>,
1644        style: Style,
1645        span: Span,
1646    ) -> Result<(), Diagnostic> {
1647        self.ensure_unique(&targets, span, "gate target")?;
1648        let control_wires = controls
1649            .iter()
1650            .map(|control| control.wire)
1651            .collect::<Vec<_>>();
1652        self.ensure_unique(&control_wires, span, "gate control")?;
1653        if let Some(wire) = targets.iter().find(|target| control_wires.contains(target)) {
1654            return Err(Diagnostic::new(
1655                format!(
1656                    "wire `{}` cannot be both a target and a control",
1657                    self.wires[*wire].name
1658                ),
1659                span,
1660            ));
1661        }
1662        self.operations.push(Operation {
1663            kind: OperationKind::Gate {
1664                label,
1665                targets,
1666                controls,
1667            },
1668            span,
1669            style,
1670            overlay: None,
1671        });
1672        Ok(())
1673    }
1674
1675    fn parse_controls(&mut self) -> Result<Vec<Control>, Diagnostic> {
1676        if !self.consume_keyword("if") {
1677            return Ok(Vec::new());
1678        }
1679        let mut controls = Vec::new();
1680        loop {
1681            let parity = match (&self.current().kind, self.tokens.get(self.position + 1)) {
1682                (
1683                    TokenKind::Identifier(name),
1684                    Some(Token {
1685                        kind: TokenKind::LeftParen,
1686                        ..
1687                    }),
1688                ) => match name.as_str() {
1689                    "parity_x" => Some(ParityBasis::X),
1690                    "parity_y" => Some(ParityBasis::Y),
1691                    "parity" | "parity_z" => Some(ParityBasis::Z),
1692                    _ => None,
1693                },
1694                _ => None,
1695            };
1696            let (wires, positive) = if parity.is_some() {
1697                self.advance();
1698                self.expect(TokenKind::LeftParen, "`(` after the parity control")?;
1699                let wires = self.parse_wire_list()?;
1700                self.expect(TokenKind::RightParen, "`)`")?;
1701                (wires, true)
1702            } else {
1703                let positive = !self.consume(&TokenKind::Bang);
1704                (self.parse_wire_selection(true)?, positive)
1705            };
1706            controls.extend(wires.into_iter().map(|wire| Control {
1707                wire,
1708                positive,
1709                parity,
1710            }));
1711            if !self.consume(&TokenKind::Comma) {
1712                break;
1713            }
1714        }
1715        Ok(controls)
1716    }
1717
1718    fn parse_style(&mut self) -> Result<Style, Diagnostic> {
1719        if !self.consume_keyword("with") {
1720            return Ok(Style::default());
1721        }
1722
1723        let mut style = if let TokenKind::Identifier(name) = &self.current().kind
1724            && let Some(style) = self.styles.get(name).cloned()
1725        {
1726            self.advance();
1727            if !self.consume(&TokenKind::Comma) {
1728                return Ok(style);
1729            }
1730            style
1731        } else {
1732            Style::default()
1733        };
1734        loop {
1735            self.parse_style_property(&mut style)?;
1736            if !self.consume(&TokenKind::Comma) {
1737                break;
1738            }
1739        }
1740        Ok(style)
1741    }
1742
1743    fn parse_style_property(&mut self, style: &mut Style) -> Result<(), Diagnostic> {
1744        let span = self.current().span;
1745        let property = self.take_identifier("style property")?;
1746        self.expect(TokenKind::Colon, "`:`")?;
1747        match property.as_str() {
1748            "stroke" => style.stroke = Some(self.take_color("stroke color")?),
1749            "fill" => style.fill = Some(self.take_color("fill color")?),
1750            "link" => style.link = Some(self.take_link()?),
1751            "width" => style.width = Some(self.take_positive_scalar("gate width")?),
1752            "height" => style.height = Some(self.take_positive_scalar("gate height")?),
1753            "size" => {
1754                let size = self.take_positive_scalar("gate size")?;
1755                style.width = Some(size);
1756                style.height = Some(size);
1757            }
1758            "shape" => {
1759                let value = self.take_identifier("shape")?;
1760                style.shape = Some(match value.as_str() {
1761                    "box" => Shape::Box,
1762                    "circle" => Shape::Circle,
1763                    "ellipse" => Shape::Ellipse,
1764                    "none" => Shape::None,
1765                    _ => {
1766                        return Err(Diagnostic::new(
1767                            "shape must be `box`, `circle`, `ellipse`, or `none`",
1768                            span,
1769                        ));
1770                    }
1771                });
1772            }
1773            "dash" => {
1774                let value = self.take_identifier("`dashed` or `solid`")?;
1775                style.dashed = match value.as_str() {
1776                    "dashed" | "true" => true,
1777                    "solid" | "false" => false,
1778                    _ => return Err(Diagnostic::new("dash must be `dashed` or `solid`", span)),
1779                };
1780            }
1781            "opacity" => {
1782                let opacity = self.take_scalar("opacity")?;
1783                if !(0.0..=1.0).contains(&opacity) {
1784                    return Err(Diagnostic::new("opacity must be between 0 and 1", span));
1785                }
1786                style.opacity = Some(opacity);
1787            }
1788            _ => {
1789                return Err(Diagnostic::new(
1790                    format!("unknown style property `{property}`"),
1791                    span,
1792                ));
1793            }
1794        }
1795        Ok(())
1796    }
1797
1798    fn parse_wire_list(&mut self) -> Result<Vec<usize>, Diagnostic> {
1799        let mut wires = self.parse_wire_selection(true)?;
1800        while self.consume(&TokenKind::Comma) {
1801            wires.extend(self.parse_wire_selection(true)?);
1802        }
1803        Ok(wires)
1804    }
1805
1806    fn parse_wire_reference(&mut self) -> Result<usize, Diagnostic> {
1807        // ponytail: reuse the checked range parser; split out a scalar path only if this
1808        // one-element allocation becomes measurable.
1809        Ok(self
1810            .parse_wire_selection(false)?
1811            .into_iter()
1812            .next()
1813            .expect("a wire selection is not empty"))
1814    }
1815
1816    fn parse_wire_selection(&mut self, allow_range: bool) -> Result<Vec<usize>, Diagnostic> {
1817        let span = self.current().span;
1818        let base = self.take_identifier("wire name")?;
1819        if self.consume(&TokenKind::LeftBracket) {
1820            let start = self.take_number("wire index")?;
1821            if self.consume(&TokenKind::DotDot) {
1822                if !allow_range {
1823                    return Err(Diagnostic::new(
1824                        "a wire range is not valid where one wire is required",
1825                        span,
1826                    ));
1827                }
1828                let end = self.take_number("wire range end")?;
1829                self.expect(TokenKind::RightBracket, "`]`")?;
1830                if start >= end {
1831                    return Err(Diagnostic::new(
1832                        "a wire range must have an end greater than its start",
1833                        span,
1834                    ));
1835                }
1836                return (start..end)
1837                    .map(|index| self.resolve_wire(&format!("{base}[{index}]"), span))
1838                    .collect();
1839            }
1840            self.expect(TokenKind::RightBracket, "`]`")?;
1841            return self
1842                .resolve_wire(&format!("{base}[{start}]"), span)
1843                .map(|wire| vec![wire]);
1844        }
1845        self.resolve_wire(&base, span).map(|wire| vec![wire])
1846    }
1847
1848    fn resolve_wire(&mut self, name: &str, span: Span) -> Result<usize, Diagnostic> {
1849        if let Some(wire) = self.wire_indices.get(name) {
1850            return Ok(*wire);
1851        }
1852        if !self.auto_wires {
1853            return Err(Diagnostic::with_help(
1854                format!("unknown wire `{name}`"),
1855                "declare it before use or enable `autowires`",
1856                span,
1857            ));
1858        }
1859        let wire = self.wires.len();
1860        self.wire_indices.insert(name.into(), wire);
1861        self.wires.push(Wire {
1862            name: name.into(),
1863            kind: WireKind::Quantum,
1864            ellipsis: false,
1865            input: Some(name.into()),
1866            output: None,
1867            style: Style::default(),
1868        });
1869        Ok(wire)
1870    }
1871
1872    fn ensure_unique(
1873        &self,
1874        wires: &[usize],
1875        span: Span,
1876        description: &str,
1877    ) -> Result<(), Diagnostic> {
1878        let mut sorted = wires.to_vec();
1879        sorted.sort_unstable();
1880        if let Some(pair) = sorted.windows(2).find(|pair| pair[0] == pair[1]) {
1881            return Err(Diagnostic::new(
1882                format!("{} `{}` is repeated", description, self.wires[pair[0]].name),
1883                span,
1884            ));
1885        }
1886        Ok(())
1887    }
1888
1889    fn take_label(&mut self, expected: &str) -> Result<String, Diagnostic> {
1890        match self.current().kind.clone() {
1891            TokenKind::String(value) => {
1892                self.advance();
1893                Ok(value)
1894            }
1895            TokenKind::Identifier(name) => {
1896                let value = self.values.get(&name).cloned().unwrap_or(name);
1897                self.advance();
1898                Ok(value)
1899            }
1900            _ => Err(self.error(format!("expected {expected}"))),
1901        }
1902    }
1903
1904    fn take_color(&mut self, expected: &str) -> Result<String, Diagnostic> {
1905        let span = self.current().span;
1906        let color = self.take_label(expected)?;
1907        if color.len() == 7
1908            && color.starts_with('#')
1909            && color[1..]
1910                .chars()
1911                .all(|character| character.is_ascii_hexdigit())
1912        {
1913            return Ok(color.to_ascii_uppercase());
1914        }
1915        if [
1916            "black", "white", "gray", "red", "green", "blue", "teal", "purple", "orange", "yellow",
1917            "olive", "lime",
1918        ]
1919        .contains(&color.as_str())
1920        {
1921            Ok(color)
1922        } else {
1923            Err(Diagnostic::new(
1924                format!(
1925                    "{expected} `{color}` is not a portable named color or six-digit hex color"
1926                ),
1927                span,
1928            ))
1929        }
1930    }
1931
1932    fn take_link(&mut self) -> Result<String, Diagnostic> {
1933        let span = self.current().span;
1934        let link = self.take_string("link URL")?;
1935        let valid_scheme = ["https://", "http://", "mailto:"]
1936            .iter()
1937            .any(|scheme| link.starts_with(scheme));
1938        let safe_characters = link.chars().all(|character| {
1939            character.is_ascii_alphanumeric() || ":/?#[]@!$&'()*+,-._~=%".contains(character)
1940        });
1941        if valid_scheme && safe_characters {
1942            Ok(link)
1943        } else {
1944            Err(Diagnostic::new(
1945                "link must be a safe absolute http, https, or mailto URL",
1946                span,
1947            ))
1948        }
1949    }
1950
1951    fn take_identifier(&mut self, expected: &str) -> Result<String, Diagnostic> {
1952        if let TokenKind::Identifier(value) = &self.current().kind {
1953            let value = value.clone();
1954            self.advance();
1955            Ok(value)
1956        } else {
1957            Err(self.error(format!("expected {expected}")))
1958        }
1959    }
1960
1961    fn take_string(&mut self, expected: &str) -> Result<String, Diagnostic> {
1962        if let TokenKind::String(value) = &self.current().kind {
1963            let value = value.clone();
1964            self.advance();
1965            Ok(value)
1966        } else {
1967            Err(self.error(format!("expected {expected} string")))
1968        }
1969    }
1970
1971    fn take_number(&mut self, expected: &str) -> Result<usize, Diagnostic> {
1972        if let TokenKind::Number(value) = &self.current().kind {
1973            let value = value.parse().map_err(|_| {
1974                self.error(format!("expected {expected} as a non-negative integer"))
1975            })?;
1976            self.advance();
1977            Ok(value)
1978        } else {
1979            Err(self.error(format!("expected {expected}")))
1980        }
1981    }
1982
1983    fn take_scalar(&mut self, expected: &str) -> Result<f32, Diagnostic> {
1984        if let TokenKind::Number(value) = &self.current().kind {
1985            let value = value
1986                .parse()
1987                .map_err(|_| self.error(format!("invalid {expected}")))?;
1988            if !f32::is_finite(value) {
1989                return Err(self.error(format!("invalid {expected}")));
1990            }
1991            self.advance();
1992            Ok(value)
1993        } else {
1994            Err(self.error(format!("expected {expected}")))
1995        }
1996    }
1997
1998    fn take_positive_scalar(&mut self, expected: &str) -> Result<f32, Diagnostic> {
1999        let span = self.current().span;
2000        let value = self.take_scalar(expected)?;
2001        if value > 0.0 {
2002            Ok(value)
2003        } else {
2004            Err(Diagnostic::new(
2005                format!("{expected} must be greater than zero"),
2006                span,
2007            ))
2008        }
2009    }
2010
2011    fn expect_keyword(&mut self, keyword: &str) -> Result<(), Diagnostic> {
2012        if self.consume_keyword(keyword) {
2013            Ok(())
2014        } else {
2015            Err(self.error(format!("expected `{keyword}`")))
2016        }
2017    }
2018
2019    fn consume_keyword(&mut self, keyword: &str) -> bool {
2020        let matches = self.at_keyword(keyword);
2021        if matches {
2022            self.advance();
2023        }
2024        matches
2025    }
2026
2027    fn at_keyword(&self, keyword: &str) -> bool {
2028        matches!(&self.current().kind, TokenKind::Identifier(value) if value == keyword)
2029    }
2030
2031    fn expect(&mut self, kind: TokenKind, expected: &str) -> Result<(), Diagnostic> {
2032        if self.consume(&kind) {
2033            Ok(())
2034        } else {
2035            Err(self.error(format!("expected {expected}")))
2036        }
2037    }
2038
2039    fn consume(&mut self, kind: &TokenKind) -> bool {
2040        if self.at(kind) {
2041            self.advance();
2042            true
2043        } else {
2044            false
2045        }
2046    }
2047
2048    fn expect_statement_end(&mut self) -> Result<(), Diagnostic> {
2049        if self.at_statement_end() {
2050            Ok(())
2051        } else {
2052            Err(self.error("expected a newline or `;` after the statement"))
2053        }
2054    }
2055
2056    fn at_statement_end(&self) -> bool {
2057        self.at(&TokenKind::Newline) || self.at(&TokenKind::RightBrace) || self.at(&TokenKind::End)
2058    }
2059
2060    fn skip_newlines(&mut self) {
2061        while self.consume(&TokenKind::Newline) {}
2062    }
2063
2064    fn at(&self, kind: &TokenKind) -> bool {
2065        self.current().kind == *kind
2066    }
2067
2068    fn advance(&mut self) {
2069        // The lexer always appends End, and every parse loop checks it before advancing.
2070        if self.position + 1 < self.tokens.len() {
2071            self.position += 1;
2072        }
2073    }
2074
2075    fn current(&self) -> &Token {
2076        &self.tokens[self.position]
2077    }
2078
2079    fn error(&self, message: impl Into<String>) -> Diagnostic {
2080        Diagnostic::new(message, self.current().span)
2081    }
2082}
2083
2084#[cfg(test)]
2085mod tests {
2086    use super::*;
2087
2088    #[test]
2089    fn lexer_tracks_byte_ranges_and_derived_locations() {
2090        let tokens = lex("\tqubit q\n").expect("valid tokens");
2091        let span = tokens[0].span;
2092        assert_eq!(
2093            (span.offset, span.length, span.line, span.column),
2094            (1, 5, 1, 2)
2095        );
2096
2097        let error = lex("circuit 💥").expect_err("non-ASCII punctuation must fail");
2098        assert_eq!(
2099            (
2100                error.span.offset,
2101                error.span.length,
2102                error.span.line,
2103                error.span.column
2104            ),
2105            (8, 4, 1, 9)
2106        );
2107
2108        let error = lex("circuit\u{c}bad").expect_err("form feed must fail");
2109        assert_eq!(error.message, "unexpected character `\u{c}`");
2110    }
2111
2112    #[test]
2113    fn parses_a_readable_bell_circuit() {
2114        let circuit = parse(
2115            r#"
2116                circuit bell {
2117                  qubit q[2]: "|0>" -> "bell"
2118
2119                  h q[0]
2120                  x q[1] if q[0]
2121                  measure q[0], q[1]
2122                }
2123            "#,
2124        )
2125        .expect("valid circuit");
2126
2127        assert_eq!(circuit.name, "bell");
2128        assert_eq!(circuit.wires.len(), 2);
2129        assert_eq!(circuit.operations.len(), 3);
2130        assert!(matches!(
2131            circuit.operations[1].kind,
2132            OperationKind::Gate {
2133                ref label,
2134                ref targets,
2135                ref controls,
2136            } if label == "X"
2137                && targets == &[1]
2138                && controls == &[Control { wire: 0, positive: true, parity: None }]
2139        ));
2140    }
2141
2142    #[test]
2143    fn parses_xyz_parity_controls() {
2144        let circuit = parse(
2145            r#"
2146                circuit parity_controls {
2147                  qubit top
2148                  qubit middle
2149                  qubit bottom
2150                  s top if middle, bottom
2151                  s top if parity(middle, bottom)
2152                  s top if parity_x(middle, bottom)
2153                  s top if parity_y(middle, bottom)
2154                  s top if parity_z(middle, bottom)
2155                }
2156            "#,
2157        )
2158        .expect("valid parity controls");
2159
2160        let controls = circuit
2161            .operations
2162            .iter()
2163            .map(|operation| match &operation.kind {
2164                OperationKind::Gate { controls, .. } => (controls.len(), controls[0].parity),
2165                _ => unreachable!("fixture contains only gates"),
2166            })
2167            .collect::<Vec<_>>();
2168        assert_eq!(
2169            controls,
2170            [
2171                (2, None),
2172                (2, Some(ParityBasis::Z)),
2173                (2, Some(ParityBasis::X)),
2174                (2, Some(ParityBasis::Y)),
2175                (2, Some(ParityBasis::Z)),
2176            ]
2177        );
2178    }
2179
2180    #[test]
2181    fn reports_unknown_wires_at_the_source_location() {
2182        let error = parse("circuit bad {\n  qubit q\n  h missing\n}\n")
2183            .expect_err("unknown wire should fail");
2184
2185        assert_eq!(
2186            (
2187                error.span.offset,
2188                error.span.length,
2189                error.span.line,
2190                error.span.column
2191            ),
2192            (28, 7, 3, 5)
2193        );
2194        assert!(error.message.contains("unknown wire `missing`"));
2195        assert_eq!(
2196            error.help.as_deref(),
2197            Some("declare it before use or enable `autowires`")
2198        );
2199    }
2200
2201    #[test]
2202    fn recovers_at_statement_boundaries_to_report_multiple_errors() {
2203        let error = parse("circuit bad {\n  qubit q\n  h missing\n  x absent\n}\n")
2204            .expect_err("both unknown wires should fail");
2205
2206        assert_eq!(error.message, "2 errors found");
2207        assert_eq!(error.related().len(), 2);
2208        assert!(error.related()[0].message.contains("`missing`"));
2209        assert!(error.related()[1].message.contains("`absent`"));
2210    }
2211
2212    #[test]
2213    fn recovery_checkpoints_scale_to_generated_circuits() {
2214        let mut source = String::from("circuit generated {\nqubit q\n");
2215        for _ in 0..5_000 {
2216            source.push_str("h q\n");
2217        }
2218        source.push_str("}\n");
2219
2220        let started = std::time::Instant::now();
2221        let circuit = parse(&source).expect("large generated circuit should parse");
2222        assert_eq!(circuit.operations.len(), 5_000);
2223        assert!(
2224            started.elapsed() < std::time::Duration::from_secs(1),
2225            "5,000 statements took {:?}",
2226            started.elapsed()
2227        );
2228    }
2229
2230    #[test]
2231    fn autowires_declares_quantum_wires_on_first_use() {
2232        let circuit = parse(
2233            r#"
2234                circuit sketch {
2235                  autowires
2236                  h control
2237                  x target if control
2238                  gate "U" on work[0..2]
2239                }
2240            "#,
2241        )
2242        .expect("valid automatic wires");
2243
2244        assert_eq!(
2245            circuit
2246                .wires
2247                .iter()
2248                .map(|wire| wire.name.as_str())
2249                .collect::<Vec<_>>(),
2250            ["control", "target", "work[0]", "work[1]"]
2251        );
2252        assert!(
2253            circuit
2254                .wires
2255                .iter()
2256                .all(|wire| wire.kind == WireKind::Quantum && wire.input.is_some())
2257        );
2258    }
2259
2260    #[test]
2261    fn parses_structured_layout_and_portable_styles() {
2262        let circuit = parse(
2263            r##"
2264                circuit styled {
2265                  layout {
2266                    orientation: vertical
2267                    scale: 1.25
2268                    gate_size: 24
2269                    corner_radius: 2
2270                    comment_width: 96
2271                    background: "#f7f8fc"
2272                  }
2273                  qubit q[2] with stroke: "#336699"
2274                  h q[0] with fill: yellow, shape: circle, size: 20, link: "https://example.com/gate?id=H"
2275                }
2276            "##,
2277        )
2278        .expect("valid styled circuit");
2279
2280        assert_eq!(circuit.layout.orientation, Orientation::Vertical);
2281        assert_eq!(circuit.layout.scale, 1.25);
2282        assert_eq!(circuit.layout.gate_size, 24.0);
2283        assert_eq!(circuit.layout.corner_radius, 2.0);
2284        assert_eq!(circuit.layout.comment_width, 96.0);
2285        assert_eq!(circuit.layout.background, "#F7F8FC");
2286        assert_eq!(circuit.wires[0].style.stroke.as_deref(), Some("#336699"));
2287        assert_eq!(circuit.operations[0].style.shape, Some(Shape::Circle));
2288        assert_eq!(circuit.operations[0].style.width, Some(20.0));
2289        assert_eq!(
2290            circuit.operations[0].style.link.as_deref(),
2291            Some("https://example.com/gate?id=H")
2292        );
2293
2294        let error = parse("circuit bad { qubit q with stroke: \"#12ZZ99\" }")
2295            .expect_err("invalid hex color should fail");
2296        assert!(error.message.contains("six-digit hex color"));
2297        assert!(
2298            parse("circuit bad { qubit q; h q with link: \"javascript:alert(1)\" }")
2299                .expect_err("unsafe link should fail")
2300                .message
2301                .contains("safe absolute")
2302        );
2303    }
2304
2305    #[test]
2306    fn isolates_explicit_backend_escape_blocks() {
2307        let circuit = parse(
2308            r##"
2309                circuit hooks {
2310                  backend latex {
2311                    preamble: "\\newcommand{\\hook}{ok}"
2312                    before: "\\node {ok};"
2313                  }
2314                  backend typst {
2315                    after: "#v(1pt)"
2316                  }
2317                  qubit q
2318                }
2319            "##,
2320        )
2321        .expect("valid backend hooks");
2322
2323        assert_eq!(circuit.escapes.latex.preamble, ["\\newcommand{\\hook}{ok}"]);
2324        assert_eq!(circuit.escapes.latex.before, ["\\node {ok};"]);
2325        assert_eq!(circuit.escapes.typst.after, ["#v(1pt)"]);
2326    }
2327
2328    #[test]
2329    fn parses_wire_lifecycle_and_placement_statements() {
2330        let circuit = parse(
2331            r#"
2332                circuit lifecycle {
2333                  qubit q[3]
2334                  start q[2] as "aux"
2335                  bundle "3" on q[0]
2336                  label "middle" on q[0], q[2]
2337                  permute q[2], q[0], q[1]
2338                  set q[0] to classical
2339                  space q[1] with width: 12
2340                  touch q[0], q[2]
2341                  end q[2]
2342                }
2343            "#,
2344        )
2345        .expect("valid lifecycle circuit");
2346
2347        assert_eq!(circuit.operations.len(), 8);
2348        assert!(matches!(
2349            circuit.operations[0].kind,
2350            OperationKind::Endpoint {
2351                start: true,
2352                ref label,
2353                ..
2354            } if label.as_deref() == Some("aux")
2355        ));
2356        assert!(matches!(
2357            circuit.operations[3].kind,
2358            OperationKind::Permute { ref wires } if wires == &[2, 0, 1]
2359        ));
2360        assert!(matches!(
2361            circuit.operations[4].kind,
2362            OperationKind::WireChange {
2363                kind: WireKind::Classical,
2364                ..
2365            }
2366        ));
2367        assert!(matches!(
2368            circuit.operations[6].kind,
2369            OperationKind::Touch { ref wires } if wires == &[0, 2]
2370        ));
2371    }
2372
2373    #[test]
2374    fn targetless_statements_use_only_active_wires() {
2375        let circuit = parse(
2376            r#"
2377                circuit active_defaults {
2378                  qubit q[3]
2379                  touch
2380                  start q[2]
2381                  end q[0]
2382                  label "active"
2383                  end q[2]
2384                  barrier
2385                }
2386            "#,
2387        )
2388        .expect("valid lifecycle defaults");
2389
2390        assert!(matches!(
2391            circuit.operations[0].kind,
2392            OperationKind::Touch { ref wires } if wires == &[0, 1]
2393        ));
2394        assert!(matches!(
2395            circuit.operations[3].kind,
2396            OperationKind::Label { ref wires, .. } if wires == &[1, 2]
2397        ));
2398        assert!(matches!(
2399            circuit.operations[5].kind,
2400            OperationKind::Barrier { ref wires } if wires == &[1]
2401        ));
2402    }
2403
2404    #[test]
2405    fn targetless_statements_require_an_active_wire() {
2406        let error = parse("circuit inactive { qubit q[2]; end q[0]; end q[1]; barrier }")
2407            .expect_err("a targetless statement cannot select ended wires");
2408
2409        assert!(error.message.contains("no applicable active wires"));
2410    }
2411
2412    #[test]
2413    fn wire_label_arity_is_checked_after_autowires_finish() {
2414        let error = parse("circuit labels { autowires; h a; h b; labels \"p\", \"q\"; h c }")
2415            .expect_err("late automatic wires must be included in label arity");
2416
2417        assert!(error.message.contains("selected wire (3)"));
2418    }
2419
2420    #[test]
2421    fn rejects_non_finite_scalars() {
2422        let error = parse(
2423            "circuit huge { layout { scale: 99999999999999999999999999999999999999999999999999 }; qubit q }",
2424        )
2425        .expect_err("an overflowing scalar must not render as infinity");
2426
2427        assert!(error.message.contains("invalid layout scale"));
2428    }
2429
2430    #[test]
2431    fn parses_named_measurement_shapes() {
2432        let circuit = parse(
2433            r#"
2434                circuit measurements {
2435                  qubit q[3]
2436                  measure q[0]
2437                  measure q[1] as "Z"
2438                  measure q[2] as "X" using tag
2439                }
2440            "#,
2441        )
2442        .expect("valid measurement shapes");
2443
2444        assert!(matches!(
2445            circuit.operations[1].kind,
2446            OperationKind::Measure {
2447                shape: MeasurementShape::D,
2448                ..
2449            }
2450        ));
2451        assert!(matches!(
2452            circuit.operations[2].kind,
2453            OperationKind::Measure {
2454                shape: MeasurementShape::Tag,
2455                ..
2456            }
2457        ));
2458        assert!(
2459            parse("circuit bad { qubit q; measure q using tag }")
2460                .expect_err("tag needs a label")
2461                .message
2462                .contains("needs a label")
2463        );
2464    }
2465
2466    #[test]
2467    fn parses_known_value_wire_transitions() {
2468        let circuit = parse(
2469            r#"
2470                circuit values {
2471                  qubit q
2472                  set q to hidden as "0"
2473                  set q to quantum as "1"
2474                }
2475            "#,
2476        )
2477        .expect("valid known-value transitions");
2478
2479        assert!(matches!(
2480            circuit.operations[0].kind,
2481            OperationKind::WireChange {
2482                kind: WireKind::Hidden,
2483                label: Some(ref label),
2484                ..
2485            } if label == "0"
2486        ));
2487        assert!(
2488            parse("circuit bad { bit c; set c to classical as \"0\" }")
2489                .expect_err("classical transition cannot carry a value marker")
2490                .message
2491                .contains("quantum` or `hidden")
2492        );
2493    }
2494
2495    #[test]
2496    fn parses_an_ellipsis_wire_as_a_visual_gap() {
2497        let circuit = parse(
2498            "circuit gap { qubit first; ellipsis omitted; qubit last; label \"...\" on omitted }",
2499        )
2500        .expect("valid ellipsis wire");
2501
2502        assert_eq!(circuit.wires.len(), 3);
2503        assert!(circuit.wires[1].ellipsis);
2504        assert_eq!(circuit.wires[1].kind, WireKind::Hidden);
2505        assert_eq!(circuit.wires[1].input.as_deref(), Some("..."));
2506        assert_eq!(circuit.wires[1].output.as_deref(), Some("..."));
2507        assert!(
2508            parse("circuit bad { ellipsis gap[2] }")
2509                .expect_err("ellipsis arrays are ambiguous")
2510                .message
2511                .contains("one visual gap")
2512        );
2513    }
2514
2515    #[test]
2516    fn parses_and_lowers_typed_function_calls() {
2517        let circuit = parse(
2518            r#"
2519                fn entangle(control, target) {
2520                  h control
2521                  x target if control
2522                  gate "control" on target
2523                }
2524
2525                fn ghz(a, b, c) {
2526                  entangle(a, b)
2527                  x c if b
2528                  barrier
2529                }
2530
2531                circuit functions {
2532                  qubit q[3]
2533                  ghz(q[2], q[0], q[1])
2534                }
2535            "#,
2536        )
2537        .expect("valid functions");
2538
2539        assert_eq!(circuit.operations.len(), 5);
2540        assert!(matches!(
2541            circuit.operations[1].kind,
2542            OperationKind::Gate {
2543                ref label,
2544                ref targets,
2545                ref controls,
2546            } if label == "X"
2547                && targets == &[0]
2548                && controls == &[Control { wire: 2, positive: true, parity: None }]
2549        ));
2550        assert!(matches!(
2551            circuit.operations[2].kind,
2552            OperationKind::Gate { ref label, .. } if label == "control"
2553        ));
2554        assert!(matches!(
2555            circuit.operations[4].kind,
2556            OperationKind::Barrier { ref wires } if wires == &[2, 0, 1]
2557        ));
2558    }
2559
2560    #[test]
2561    fn resolves_typed_values_and_named_styles() {
2562        let circuit = parse(
2563            r##"
2564                let operator = "U"
2565                let accent = "#ddeeff"
2566
2567                style highlighted {
2568                  fill: accent
2569                  stroke: blue
2570                  shape: circle
2571                }
2572
2573                circuit declarations {
2574                  qubit q
2575                  gate operator on q with highlighted, opacity: 0.5
2576                }
2577            "##,
2578        )
2579        .expect("valid typed declarations");
2580
2581        assert!(matches!(
2582            circuit.operations[0].kind,
2583            OperationKind::Gate { ref label, .. } if label == "U"
2584        ));
2585        assert_eq!(circuit.operations[0].style.fill.as_deref(), Some("#DDEEFF"));
2586        assert_eq!(circuit.operations[0].style.shape, Some(Shape::Circle));
2587        assert_eq!(circuit.operations[0].style.opacity, Some(0.5));
2588        assert!(
2589            parse("let h = \"bad\"; circuit bad { qubit q }")
2590                .expect_err("reserved definition name should fail")
2591                .message
2592                .contains("reserved or already used")
2593        );
2594    }
2595
2596    #[test]
2597    fn reports_function_arity_at_the_call() {
2598        let error =
2599            parse("fn one(a) { h a }\n\ncircuit bad {\n  qubit q[2]\n  one(q[0], q[1])\n}\n")
2600                .expect_err("wrong arity should fail");
2601
2602        assert_eq!((error.span.line, error.span.column), (5, 3));
2603        assert!(
2604            error
2605                .message
2606                .contains("expects 1 wire argument(s), but got 2")
2607        );
2608    }
2609
2610    #[test]
2611    fn expands_ranges_and_structured_operation_blocks() {
2612        let circuit = parse(
2613            r#"
2614                fn pair(a, b) {
2615                  parallel {
2616                    h a
2617                    h b
2618                  }
2619                }
2620
2621                circuit structured {
2622                  qubit q[4]
2623                  pair(q[1..3])
2624                  repeat 2 {
2625                    measure q[0..2]
2626                  }
2627                }
2628            "#,
2629        )
2630        .expect("valid structured blocks");
2631
2632        assert_eq!(circuit.operations.len(), 6);
2633        assert!(matches!(
2634            circuit.operations[0].kind,
2635            OperationKind::Touch { ref wires } if wires == &[1, 2]
2636        ));
2637        assert!(matches!(
2638            circuit.operations[4].kind,
2639            OperationKind::Measure { ref targets, .. } if targets == &[0, 1]
2640        ));
2641        assert!(matches!(
2642            circuit.operations[5].kind,
2643            OperationKind::Measure { ref targets, .. } if targets == &[0, 1]
2644        ));
2645    }
2646
2647    #[test]
2648    fn reverses_blocks_and_marked_ranges() {
2649        let circuit = parse(
2650            r#"
2651                circuit reverse_order {
2652                  qubit q[3]
2653                  mark forward
2654                  reverse {
2655                    h q[0]
2656                    measure q[1]
2657                    z q[2]
2658                  }
2659                  mark reversed
2660                  reverse from forward to reversed
2661                }
2662            "#,
2663        )
2664        .expect("valid reverse block");
2665
2666        let kinds = circuit
2667            .operations
2668            .iter()
2669            .map(|operation| match &operation.kind {
2670                OperationKind::Gate { label, .. } => label.as_str(),
2671                OperationKind::Measure { .. } => "M",
2672                _ => panic!("unexpected reverse fixture operation"),
2673            })
2674            .collect::<Vec<_>>();
2675        assert_eq!(kinds, ["Z", "M", "H", "H", "M", "Z"]);
2676    }
2677
2678    #[test]
2679    fn rejects_a_range_where_one_wire_is_required() {
2680        let error = parse("circuit bad {\n  qubit q[2]\n  h q[0..2]\n}\n")
2681            .expect_err("range target should fail");
2682
2683        assert!(error.message.contains("where one wire is required"));
2684    }
2685
2686    #[test]
2687    fn parses_labels_braces_notes_and_cuts() {
2688        let circuit = parse(
2689            r#"
2690                circuit annotations {
2691                  qubit q[3]
2692                  labels "a", "b", "c" on q[0..3]
2693                  brace both "register" on q[0..3]
2694                  note below "decode" on q[1]
2695                  cut on q[0..2] as "stage" with stroke: blue
2696                }
2697            "#,
2698        )
2699        .expect("valid annotations");
2700
2701        assert!(matches!(
2702            circuit.operations[0].kind,
2703            OperationKind::WireLabels { ref wires, ref labels }
2704                if wires == &[0, 1, 2] && labels == &["a", "b", "c"]
2705        ));
2706        assert!(matches!(
2707            circuit.operations[1].kind,
2708            OperationKind::Brace {
2709                side: BraceSide::Both,
2710                ..
2711            }
2712        ));
2713        assert!(matches!(
2714            circuit.operations[2].kind,
2715            OperationKind::Note {
2716                side: NoteSide::Below,
2717                ..
2718            }
2719        ));
2720        assert!(matches!(
2721            circuit.operations[3].kind,
2722            OperationKind::Cut { ref wires, ref label }
2723                if wires == &[0, 1] && label.as_deref() == Some("stage")
2724        ));
2725    }
2726
2727    #[test]
2728    fn parses_centered_equals_with_optional_braces() {
2729        let circuit = parse(
2730            r#"
2731                circuit equals {
2732                  qubit q[2]
2733                  equals on q[0..2]
2734                  equals "stage" on q[0..2] braced both with stroke: blue
2735                }
2736            "#,
2737        )
2738        .expect("valid equals statements");
2739
2740        assert!(matches!(
2741            circuit.operations[0].kind,
2742            OperationKind::Label {
2743                ref label,
2744                brace: None,
2745                ..
2746            } if label == "="
2747        ));
2748        assert!(matches!(
2749            circuit.operations[1].kind,
2750            OperationKind::Label {
2751                ref label,
2752                brace: Some(BraceSide::Both),
2753                ..
2754            } if label == "stage"
2755        ));
2756    }
2757
2758    #[test]
2759    fn resolves_marked_group_ranges() {
2760        let circuit = parse(
2761            r#"
2762                circuit regions {
2763                  qubit q[2]
2764                  mark start
2765                  h q[0]
2766                  mark middle
2767                  x q[1]
2768                  group "first" from start to middle on q[0]
2769                  group "all" from start to here with fill: yellow, opacity: 0.2
2770                }
2771            "#,
2772        )
2773        .expect("valid groups");
2774
2775        assert_eq!(circuit.groups.len(), 2);
2776        assert_eq!((circuit.groups[0].start, circuit.groups[0].end), (0, 1));
2777        assert_eq!((circuit.groups[1].start, circuit.groups[1].end), (0, 2));
2778        assert_eq!(circuit.groups[0].wires, [0]);
2779        assert_eq!(circuit.groups[1].style.opacity, Some(0.2));
2780    }
2781}