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}