Skip to main content

bloq_graph/parser/
ast.rs

1//! Abstract syntax tree for `.blog` source text.
2//!
3//! These types mirror the surface syntax before lowering into a
4//! [`BlockGraph`](crate::BlockGraph): references are still unresolved (block ids
5//! or position literals) and every node carries a source [`Span`] for
6//! diagnostics.
7
8use crate::{Basis, BlockKind, CubeHeight, Direction, UDirection, WalkingBoundaryKind};
9use bloq_utils::PauliBasis;
10use glam::IVec3;
11
12use crate::action::BinaryOp;
13
14/// Byte-offset span in source text.
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub struct Span {
17    /// Inclusive starting byte offset.
18    pub start: u32,
19    /// Exclusive ending byte offset.
20    pub end: u32,
21}
22
23impl Span {
24    /// Converts a `usize` byte range, saturating offsets above [`u32::MAX`].
25    pub fn from_range(range: std::ops::Range<usize>) -> Self {
26        // Saturate rather than silently wrap on the (practically impossible)
27        // >4 GiB source file, so a span never points at a bogus offset.
28        Self {
29            start: u32::try_from(range.start).unwrap_or(u32::MAX),
30            end: u32::try_from(range.end).unwrap_or(u32::MAX),
31        }
32    }
33}
34
35/// Value with source location.
36#[derive(Debug, Clone, PartialEq, Eq)]
37pub struct Spanned<T> {
38    /// Parsed value.
39    pub node: T,
40    /// Source range containing the value.
41    pub span: Span,
42}
43
44impl<T> Spanned<T> {
45    /// Wraps a parsed value with its source span.
46    pub fn new(node: T, span: Span) -> Self {
47        Self { node, span }
48    }
49}
50
51/// Standalone BLOG source grouped by statement kind.
52#[derive(Debug, Clone, PartialEq, Eq)]
53pub struct SourceFile {
54    /// Declared BLOG major and minor version.
55    pub version: Spanned<(u32, u32)>,
56    /// Graph-geometry statements in source order.
57    pub data_stmts: Vec<Spanned<DataStmt>>,
58    /// Classical-action statements in source order.
59    pub action_stmts: Vec<Spanned<ActionStmt>>,
60}
61
62/// A BLOG file containing named module definitions.
63#[derive(Debug, Clone, PartialEq, Eq)]
64pub struct ModularSourceFile {
65    /// Declared BLOG major and minor version.
66    pub version: Spanned<(u32, u32)>,
67    /// Imported module sources.
68    pub imports: Vec<Spanned<ImportDef>>,
69    /// Module definitions in source order.
70    pub modules: Vec<Spanned<ModuleDef>>,
71}
72
73/// `import "path" as name`
74#[derive(Debug, Clone, PartialEq, Eq)]
75pub struct ImportDef {
76    /// Imported source path.
77    pub path: Spanned<String>,
78    /// Local name assigned to the imported definition.
79    pub alias: Spanned<String>,
80}
81
82/// `module name { ... }`
83#[derive(Debug, Clone, PartialEq, Eq)]
84pub struct ModuleDef {
85    /// Module definition name.
86    pub name: Spanned<String>,
87    /// Declared quantum and classical interface.
88    pub interface_stmts: Vec<Spanned<InterfaceStmt>>,
89    /// Child module instances.
90    pub instance_stmts: Vec<Spanned<InstanceDef>>,
91    /// Parent-local graph geometry.
92    pub data_stmts: Vec<Spanned<DataStmt>>,
93    /// Parent-local classical actions.
94    pub action_stmts: Vec<Spanned<ActionStmt>>,
95    /// Connections between blocks, instances, and interface ports.
96    pub connect_stmts: Vec<Spanned<ConnectStmt>>,
97}
98
99/// A module interface statement.
100#[derive(Debug, Clone, PartialEq, Eq)]
101pub enum InterfaceStmt {
102    /// Quantum input or output port.
103    Quantum(QuantumPortDef),
104    /// Named classical bit input.
105    BitInput(Spanned<String>),
106    /// Named classical bit output expression.
107    BitOutput(BitOutputDef),
108}
109
110/// `in|out name: resource-type = block-id`
111#[derive(Debug, Clone, PartialEq, Eq)]
112pub struct QuantumPortDef {
113    /// Whether the port is an input or output.
114    pub direction: Spanned<PortDirectionAst>,
115    /// Public interface port name.
116    pub name: Spanned<String>,
117    /// Parent-local block ID implementing the port.
118    pub block_id: Spanned<u32>,
119    /// Declared logical resource type.
120    pub resource_type: Spanned<String>,
121}
122
123/// Direction of one declared quantum module port.
124#[derive(Debug, Clone, Copy, PartialEq, Eq)]
125pub enum PortDirectionAst {
126    /// Quantum input port.
127    Input,
128    /// Quantum output port.
129    Output,
130}
131
132/// `out name = expr`
133#[derive(Debug, Clone, PartialEq, Eq)]
134pub struct BitOutputDef {
135    /// Public classical output name.
136    pub name: Spanned<String>,
137    /// Boolean expression exported through the output.
138    pub expr: Spanned<Expr>,
139}
140
141/// `name: definition @ [x, y, z] [rotate X|Y|Z degrees]`
142#[derive(Debug, Clone, PartialEq, Eq)]
143pub struct InstanceDef {
144    /// Parent-local instance name.
145    pub name: Spanned<String>,
146    /// Referenced module definition name.
147    pub definition: Spanned<String>,
148    /// Translation applied to the instance.
149    pub translation: Spanned<IVec3>,
150    /// Optional axis and angle applied after translation.
151    pub rotation: Option<Spanned<(UDirection, i32)>>,
152}
153
154/// A module connection statement.
155#[derive(Debug, Clone, PartialEq, Eq)]
156pub enum ConnectStmt {
157    /// Binds a block endpoint to a named module or interface endpoint.
158    Bind {
159        /// Whether the connection changes basis by Hadamard.
160        hadamard: bool,
161        /// Connection source.
162        source: Spanned<ConnectEndpointAst>,
163        /// Connection target.
164        target: Spanned<ConnectEndpointAst>,
165    },
166    /// Connects a named quantum output to a named quantum input.
167    Pipe {
168        /// Whether the connection changes basis by Hadamard.
169        hadamard: bool,
170        /// Qualified output endpoint name.
171        output: Spanned<String>,
172        /// Qualified input endpoint name.
173        input: Spanned<String>,
174    },
175}
176
177/// A parent-local block ID or named connection endpoint.
178#[derive(Debug, Clone, PartialEq, Eq)]
179pub enum ConnectEndpointAst {
180    /// Parent-local block ID.
181    Block(u32),
182    /// Named module or interface endpoint.
183    Name(String),
184}
185
186/// A graph statement.
187#[derive(Debug, Clone, PartialEq, Eq)]
188pub enum DataStmt {
189    /// Block declaration.
190    Block(BlockDef),
191    /// Pipe declaration.
192    Pipe(PipeDef),
193    /// Explicit conditional branch region.
194    Branch(BranchRegionDef),
195}
196
197/// An action statement.
198#[derive(Debug, Clone, PartialEq, Eq)]
199pub enum ActionStmt {
200    /// Named measurement record.
201    Measure(MeasureDef),
202    /// Named Boolean binding.
203    Let(LetDef),
204    /// Post-selection condition.
205    DiscardIf(Spanned<Expr>),
206    /// Selective or branch resolution.
207    Resolve(ResolveDef),
208    /// Conditional or unconditional Pauli feedback.
209    Feedback(FeedbackDef),
210}
211
212/// A block or pipe inside one explicit branch arm.
213#[derive(Debug, Clone, PartialEq, Eq)]
214pub enum BranchArmStmt {
215    /// Block owned by the arm.
216    Block(BlockDef),
217    /// Pipe owned by the arm.
218    Pipe(PipeDef),
219}
220
221/// `branch <name> { false { ... } true { ... } }`
222#[derive(Debug, Clone, PartialEq, Eq)]
223pub struct BranchRegionDef {
224    /// Public branch name.
225    pub name: Spanned<String>,
226    /// Statements selected when the condition is false.
227    pub on_false: Vec<Spanned<BranchArmStmt>>,
228    /// Statements selected when the condition is true.
229    pub on_true: Vec<Spanned<BranchArmStmt>>,
230}
231
232/// Unresolved reference — block ID or position literal.
233#[derive(Debug, Clone, PartialEq, Eq)]
234pub enum Ref {
235    /// Explicit lattice position.
236    Pos(IVec3),
237    /// Source-local block ID.
238    Id(u32),
239}
240
241/// A block definition: `<id>: <kind> <pos> [...]`.
242///
243/// `end` is present for multi-cell blocks (walking, patch rotation). `height` is
244/// `Some` only when the source wrote a `height=` modifier — the distinction is
245/// load-bearing: an explicit height propagates across the cube's spatial
246/// component and conflicts with a different explicit height, whereas an absent
247/// one silently defaults to `height=d` and never conflicts.
248#[derive(Debug, Clone, PartialEq, Eq)]
249pub struct BlockDef {
250    /// Source-local block ID.
251    pub id: Spanned<u32>,
252    /// Parsed block kind before endpoint-dependent lowering.
253    pub kind: Spanned<BlockKindAst>,
254    /// Whether a selective block's written basis order is reversed.
255    pub selective_order_reversed: bool,
256    /// Block anchor position.
257    pub pos: Spanned<IVec3>,
258    /// Explicit symbolic cube height, if written.
259    pub height: Option<Spanned<CubeHeight>>,
260    /// Explicit port display color, if written.
261    pub color: Option<Spanned<[u8; 3]>>,
262    /// Explicit port role, if written.
263    pub role: Option<Spanned<crate::PortRole>>,
264    /// Second endpoint for walking or patch-rotation blocks.
265    pub end: Option<Spanned<IVec3>>,
266    /// Optional user tag.
267    pub tag: Option<Spanned<String>>,
268}
269
270/// A parsed block kind, before the two-endpoint kinds are resolved with their
271/// movement vectors.
272#[derive(Debug, Clone, Copy, PartialEq, Eq)]
273pub enum BlockKindAst {
274    /// A kind that is fully determined without endpoint geometry.
275    Concrete(BlockKind),
276    /// A walking block's boundary orientation; movement comes from the endpoints.
277    WalkingBoundary(WalkingBoundaryKind),
278    /// A patch-rotation block's basis; movement comes from the endpoints.
279    PatchRotationBasis(Basis),
280}
281
282/// A pipe definition connecting a source to either an explicit destination or a
283/// direction.
284#[derive(Debug, Clone, PartialEq, Eq)]
285pub struct PipeDef {
286    /// Whether the pipe changes basis by Hadamard.
287    pub hadamard: bool,
288    /// Source endpoint reference.
289    pub src: Spanned<Ref>,
290    /// Destination reference or direction.
291    pub dst: Spanned<PipeDst>,
292    /// Optional user tag.
293    pub tag: Option<Spanned<String>>,
294}
295
296/// A pipe destination: an explicit endpoint reference or a step direction.
297#[derive(Debug, Clone, PartialEq, Eq)]
298pub enum PipeDst {
299    /// Explicit endpoint reference.
300    Ref(Ref),
301    /// Unit step from the source endpoint.
302    Dir(crate::Direction),
303}
304
305/// `<name> = measure <ref>` or `<name> = measure <ref> -> <dir>`
306#[derive(Debug, Clone, PartialEq, Eq)]
307pub struct MeasureDef {
308    /// Unresolved node or edge being measured.
309    pub target: Spanned<MeasureTargetAst>,
310    /// Measurement record name.
311    pub name: Spanned<String>,
312}
313
314/// Unresolved measure target — either a node reference or an edge
315/// (node reference + direction).
316#[derive(Debug, Clone, PartialEq, Eq)]
317pub enum MeasureTargetAst {
318    /// Node measurement.
319    Node(Ref),
320    /// Edge measurement identified by an endpoint and direction.
321    Edge(Ref, crate::Direction),
322}
323
324/// `<name> = <expr>`
325#[derive(Debug, Clone, PartialEq, Eq)]
326pub struct LetDef {
327    /// Name being defined.
328    pub name: Spanned<String>,
329    /// Boolean value assigned to the name.
330    pub expr: Spanned<Expr>,
331}
332
333/// A boolean expression over measurement-outcome variables, with spanned
334/// sub-expressions.
335#[derive(Debug, Clone, PartialEq, Eq)]
336pub enum Expr {
337    /// Named Boolean variable.
338    Var(String),
339    /// Logical negation.
340    Not(Box<Spanned<Expr>>),
341    /// Binary Boolean operation with ordered operands.
342    Binary(BinaryOp, Box<Spanned<Expr>>, Box<Spanned<Expr>>),
343}
344
345/// A selective-block reference or a named branch region.
346#[derive(Debug, Clone, PartialEq, Eq)]
347pub enum ResolveTargetDef {
348    /// Selective block reference.
349    Ref(Ref),
350    /// Named structural branch region.
351    Branch(String),
352}
353
354/// `resolve <ref-or-branch-name> if <expr>`
355#[derive(Debug, Clone, PartialEq, Eq)]
356pub struct ResolveDef {
357    /// Selective block or branch being resolved.
358    pub target: Spanned<ResolveTargetDef>,
359    /// Boolean selector expression.
360    pub condition: Spanned<Expr>,
361}
362
363/// `feedback <targets> [if <expr>]`
364#[derive(Debug, Clone, PartialEq, Eq)]
365pub struct FeedbackDef {
366    /// Pauli correction targets.
367    pub targets: Vec<FeedbackTargetDef>,
368    /// Optional Boolean feedback guard.
369    pub condition: Option<Spanned<Expr>>,
370}
371
372/// A single Pauli correction site within a [`FeedbackDef`].
373#[derive(Debug, Clone, PartialEq, Eq)]
374pub struct FeedbackTargetDef {
375    /// Pauli basis of the correction.
376    pub pauli: Spanned<PauliBasis>,
377    /// Block receiving the correction.
378    pub target: Spanned<Ref>,
379    /// Optional endpoint wire selecting the target Pauli frame.
380    pub direction: Option<Spanned<Direction>>,
381}