calyx_frontend/ast.rs
1//! Abstract Syntax Tree for Calyx
2use super::parser;
3use crate::{Attributes, PortDef, Primitive};
4use atty::Stream;
5use calyx_utils::{CalyxResult, Error, GPosIdx, Id};
6use std::{num::NonZeroU64, path::PathBuf};
7
8/// Corresponds to an individual Calyx file.
9#[derive(Debug)]
10pub struct NamespaceDef {
11 /// Path to extern files.
12 pub imports: Vec<String>,
13 /// List of component definitions.
14 pub components: Vec<ComponentDef>,
15 /// Extern statements and any primitive declarations in them.
16 pub externs: Vec<(Option<String>, Vec<Primitive>)>,
17 /// Optional opaque metadata
18 pub metadata: Option<String>,
19}
20
21impl NamespaceDef {
22 /// Construct a namespace from a file or the input stream.
23 /// If no file is provided, the input stream must be a TTY.
24 pub fn construct(file: &Option<PathBuf>) -> CalyxResult<Self> {
25 match file {
26 Some(file) => parser::CalyxParser::parse_file(file),
27 None => {
28 if atty::isnt(Stream::Stdin) {
29 parser::CalyxParser::parse(std::io::stdin())
30 } else {
31 Err(Error::invalid_file(
32 "No file provided and terminal not a TTY".to_string(),
33 ))
34 }
35 }
36 }
37 }
38
39 /// Construct a namespace from a definition using a string.
40 pub fn construct_from_str(inp: &str) -> CalyxResult<Self> {
41 parser::CalyxParser::parse(inp.as_bytes())
42 }
43}
44
45/// AST statement for defining components.
46#[derive(Debug)]
47pub struct ComponentDef {
48 /// Name of the component.
49 pub name: Id,
50 /// Defines input and output ports along with their attributes.
51 pub signature: Vec<PortDef<u64>>,
52 /// List of instantiated sub-components
53 pub cells: Vec<Cell>,
54 /// List of groups
55 pub groups: Vec<Group>,
56 /// List of StaticGroups
57 pub static_groups: Vec<StaticGroup>,
58 /// List of continuous assignments
59 pub continuous_assignments: Vec<Wire>,
60 /// Single control statement for this component.
61 pub control: Control,
62 /// Attributes attached to this component
63 pub attributes: Attributes,
64 /// True iff this is a combinational component
65 pub is_comb: bool,
66 /// (Optional) latency of component, if it is static
67 pub latency: Option<NonZeroU64>,
68}
69
70impl ComponentDef {
71 pub fn new<S>(
72 name: S,
73 is_comb: bool,
74 latency: Option<NonZeroU64>,
75 signature: Vec<PortDef<u64>>,
76 ) -> Self
77 where
78 S: Into<Id>,
79 {
80 Self {
81 name: name.into(),
82 signature,
83 cells: Vec::new(),
84 groups: Vec::new(),
85 static_groups: Vec::new(),
86 continuous_assignments: Vec::new(),
87 control: Control::empty(),
88 attributes: Attributes::default(),
89 is_comb,
90 latency,
91 }
92 }
93}
94
95/// Statement that refers to a port on a subcomponent.
96/// This is distinct from a `Portdef` which defines a port.
97#[derive(Debug)]
98pub enum Port {
99 /// Refers to the port named `port` on the subcomponent
100 /// `component`.
101 Comp { component: Id, port: Id },
102
103 /// Refers to the port named `port` on the component
104 /// currently being defined.
105 This { port: Id },
106
107 /// `group[name]` parses into `Hole { group, name }`
108 /// and is a hole named `name` on group `group`
109 Hole { group: Id, name: Id },
110}
111
112// ===================================
113// AST for wire guard expressions
114// ===================================
115
116#[derive(Debug)]
117pub enum NumType {
118 Decimal,
119 Binary,
120 Octal,
121 Hex,
122}
123
124/// Custom bitwidth numbers
125#[derive(Debug)]
126pub struct BitNum {
127 pub width: u64,
128 pub num_type: NumType,
129 pub val: u64,
130 pub span: GPosIdx,
131}
132
133/// Atomic operations used in guard conditions and RHS of the
134/// guarded assignments.
135#[derive(Debug)]
136pub enum Atom {
137 /// Accessing a particular port on a component.
138 Port(Port),
139 /// A constant.
140 Num(BitNum),
141}
142
143/// The AST for GuardExprs
144#[derive(Debug)]
145pub enum GuardExpr {
146 // Logical operations
147 And(Box<GuardExpr>, Box<GuardExpr>),
148 Or(Box<GuardExpr>, Box<GuardExpr>),
149 Not(Box<GuardExpr>),
150 CompOp(CompGuard),
151 Atom(Atom),
152}
153
154/// Guard Comparison Type
155pub type CompGuard = (GuardComp, Atom, Atom);
156
157/// The AST for StaticGuardExprs
158#[derive(Debug)]
159pub enum StaticGuardExpr {
160 And(Box<StaticGuardExpr>, Box<StaticGuardExpr>),
161 Or(Box<StaticGuardExpr>, Box<StaticGuardExpr>),
162 Not(Box<StaticGuardExpr>),
163 CompOp(CompGuard),
164 Atom(Atom),
165 StaticInfo((u64, u64)),
166}
167
168/// Possible comparison operators for guards.
169#[derive(Debug)]
170pub enum GuardComp {
171 Eq,
172 Neq,
173 Gt,
174 Lt,
175 Geq,
176 Leq,
177}
178
179/// Guards `expr` using the optional guard condition `guard`.
180#[derive(Debug)]
181pub struct Guard {
182 pub guard: Option<GuardExpr>,
183 pub expr: Atom,
184}
185
186/// Guards `expr` using the optional guard condition `guard`.
187#[derive(Debug)]
188pub struct StaticGuard {
189 pub guard: Option<StaticGuardExpr>,
190 pub expr: Atom,
191}
192
193// ===================================
194// Data definitions for Structure
195// ===================================
196
197/// Prototype of the cell definition
198#[derive(Debug)]
199pub struct Proto {
200 /// Name of the primitive.
201 pub name: Id,
202 /// Parameter binding for primitives
203 pub params: Vec<u64>,
204}
205
206/// The Cell AST nodes.
207#[derive(Debug)]
208pub struct Cell {
209 /// Name of the cell.
210 pub name: Id,
211 /// Name of the prototype this cell was built from.
212 pub prototype: Proto,
213 /// Attributes attached to this cell definition
214 pub attributes: Attributes,
215 /// Whether this cell is external
216 pub reference: bool,
217}
218
219/// Methods for constructing the structure AST nodes.
220impl Cell {
221 /// Constructs a primitive cell instantiation.
222 pub fn from(
223 name: Id,
224 proto: Id,
225 params: Vec<u64>,
226 attributes: Attributes,
227 reference: bool,
228 ) -> Cell {
229 Cell {
230 name,
231 prototype: Proto {
232 name: proto,
233 params,
234 },
235 attributes,
236 reference,
237 }
238 }
239}
240
241#[derive(Debug)]
242pub struct Group {
243 pub name: Id,
244 pub wires: Vec<Wire>,
245 pub attributes: Attributes,
246 pub is_comb: bool,
247}
248
249#[derive(Debug)]
250pub struct StaticGroup {
251 pub name: Id,
252 pub wires: Vec<StaticWire>,
253 pub attributes: Attributes,
254 pub latency: NonZeroU64,
255}
256
257/// Data for the `->` structure statement.
258#[derive(Debug)]
259pub struct Wire {
260 /// Source of the wire.
261 pub src: Guard,
262
263 /// Guarded destinations of the wire.
264 pub dest: Port,
265
266 /// Attributes for this assignment
267 pub attributes: Attributes,
268}
269
270/// Data for the `->` structure statement.
271#[derive(Debug)]
272pub struct StaticWire {
273 /// Source of the wire.
274 pub src: StaticGuard,
275
276 /// Guarded destinations of the wire.
277 pub dest: Port,
278
279 /// Attributes for this assignment
280 pub attributes: Attributes,
281}
282
283/// Control AST nodes.
284/// Since enables and static enables are indistinguishable to the AST, there
285/// is single Control Enum for both Static and Dynamic Control
286#[derive(Debug)]
287#[allow(clippy::large_enum_variant)]
288pub enum Control {
289 /// Represents sequential composition of control statements.
290 Seq {
291 /// List of `Control` statements to run in sequence.
292 stmts: Vec<Control>,
293 /// Attributes
294 attributes: Attributes,
295 },
296 /// Represents parallel composition of control statements.
297 Par {
298 /// List of `Control` statements to run in sequence.
299 stmts: Vec<Control>,
300 /// Attributes
301 attributes: Attributes,
302 },
303 /// Standard imperative if statement
304 If {
305 /// Port that connects the conditional check.
306 port: Port,
307
308 /// Modules that need to be enabled to send signal on `port`.
309 cond: Option<Id>,
310
311 /// Control for the true branch.
312 tbranch: Box<Control>,
313
314 /// Control for the true branch.
315 fbranch: Box<Control>,
316
317 /// Attributes
318 attributes: Attributes,
319 },
320 /// Standard imperative while statement
321 While {
322 /// Port that connects the conditional check.
323 port: Port,
324
325 /// Modules that need to be enabled to send signal on `port`.
326 cond: Option<Id>,
327
328 /// Control for the loop body.
329 body: Box<Control>,
330
331 /// Attributes
332 attributes: Attributes,
333 },
334 /// Static Repeat (essentially a bounded while loop w/o a condition)
335 Repeat {
336 /// Control for the true branch.
337 num_repeats: u64,
338
339 /// Control for the true branch.
340 body: Box<Control>,
341
342 /// Attributes
343 attributes: Attributes,
344 },
345 /// Runs the control for a list of subcomponents.
346 Enable {
347 /// Group to be enabled
348 comp: Id,
349 /// Attributes
350 attributes: Attributes,
351 },
352 /// Invoke component with input/output assignments.
353 Invoke {
354 /// Name of the component to be invoked.
355 comp: Id,
356 /// Input assignments
357 inputs: Vec<(Id, Atom)>,
358 /// Output assignments
359 outputs: Vec<(Id, Atom)>,
360 /// Attributes
361 attributes: Attributes,
362 /// Combinational group that may execute with this invoke.
363 comb_group: Option<Id>,
364 /// External cells that may execute with this invoke.
365 ref_cells: Vec<(Id, Id)>,
366 },
367 /// Invoke component with input/output assignments.
368 StaticInvoke {
369 /// Name of the component to be invoked.
370 comp: Id,
371 /// Input assignments
372 inputs: Vec<(Id, Atom)>,
373 /// Output assignments
374 outputs: Vec<(Id, Atom)>,
375 /// Attributes
376 attributes: Attributes,
377 /// External cells that may execute with this invoke.
378 ref_cells: Vec<(Id, Id)>,
379 /// Combinational group that may execute with this invoke.
380 comb_group: Option<Id>,
381 /// (optional) latency. Latency can be inferred if not given.
382 latency: Option<NonZeroU64>,
383 },
384 /// Control statement that does nothing.
385 Empty {
386 /// Attributes
387 attributes: Attributes,
388 },
389 /// Represents sequential composition of static control statements.
390 StaticSeq {
391 /// List of `Control` statements to run in sequence.
392 /// If not all of these stmts are static, we should error out
393 stmts: Vec<Control>,
394 /// Attributes
395 attributes: Attributes,
396 /// Optional latency for the seq
397 latency: Option<NonZeroU64>,
398 },
399 /// Represents parallel composition of static control statements.
400 StaticPar {
401 /// List of `Control` statements to run in sequence.
402 /// If not all of these stmts are static, we should error out
403 stmts: Vec<Control>,
404 /// Attributes
405 attributes: Attributes,
406 /// Optional latency for the par
407 latency: Option<NonZeroU64>,
408 },
409 /// Static if statement.
410 StaticIf {
411 /// Port that connects the conditional check.
412 port: Port,
413
414 /// Control for the true branch.
415 tbranch: Box<Control>,
416
417 /// Control for the true branch.
418 fbranch: Box<Control>,
419
420 /// Attributes
421 attributes: Attributes,
422
423 /// Optional latency; should be the longer of the two branches
424 latency: Option<NonZeroU64>,
425 },
426 /// Static Repeat (essentially a bounded while loop w/o a condition)
427 StaticRepeat {
428 /// Control for the true branch.
429 num_repeats: u64,
430
431 /// Control for the true branch.
432 body: Box<Control>,
433
434 /// Attributes
435 attributes: Attributes,
436 },
437}
438
439impl Control {
440 pub fn empty() -> Control {
441 Control::Empty {
442 attributes: Attributes::default(),
443 }
444 }
445
446 pub fn get_attributes(&self) -> &Attributes {
447 match self {
448 Control::Seq { attributes, .. } => attributes,
449 Control::Par { attributes, .. } => attributes,
450 Control::If { attributes, .. } => attributes,
451 Control::While { attributes, .. } => attributes,
452 Control::Repeat { attributes, .. } => attributes,
453 Control::Enable { attributes, .. } => attributes,
454 Control::Invoke { attributes, .. } => attributes,
455 Control::Empty { attributes, .. } => attributes,
456 Control::StaticSeq { attributes, .. } => attributes,
457 Control::StaticPar { attributes, .. } => attributes,
458 Control::StaticIf { attributes, .. } => attributes,
459 Control::StaticRepeat { attributes, .. } => attributes,
460 Control::StaticInvoke { attributes, .. } => attributes,
461 }
462 }
463}