Skip to content

Commit 181ae63

Browse files
committed
feat: implement bus declaration parsing
1 parent 2f2149f commit 181ae63

File tree

8 files changed

+151
-5
lines changed

8 files changed

+151
-5
lines changed

parser/src/ast/declarations.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ use super::*;
3535
pub enum Declaration {
3636
/// Import one or more items from the specified AirScript module to the current module
3737
Import(Span<Import>),
38+
/// A Bus section declaration
39+
Buses(Span<Vec<Bus>>),
3840
/// A constant value declaration
3941
Constant(Constant),
4042
/// An evaluator function definition
@@ -76,6 +78,39 @@ pub enum Declaration {
7678
IntegrityConstraints(Span<Vec<Statement>>),
7779
}
7880

81+
/// Represents a bus declaration in an AirScript module.
82+
#[derive(Debug, Clone, Spanned)]
83+
pub struct Bus {
84+
#[span]
85+
pub span: SourceSpan,
86+
pub name: Identifier,
87+
pub bus_type: BusType,
88+
}
89+
impl Bus {
90+
/// Creates a new bus declaration
91+
pub const fn new(span: SourceSpan, name: Identifier, bus_type: BusType) -> Self {
92+
Self {
93+
span,
94+
name,
95+
bus_type,
96+
}
97+
}
98+
}
99+
#[derive(Debug, Clone, PartialEq, Eq)]
100+
pub enum BusType {
101+
/// A multiset bus
102+
Unit,
103+
/// A logup bus
104+
Mult,
105+
}
106+
107+
impl Eq for Bus {}
108+
impl PartialEq for Bus {
109+
fn eq(&self, other: &Self) -> bool {
110+
self.name == other.name && self.bus_type == other.bus_type
111+
}
112+
}
113+
79114
/// Stores a constant's name and value. There are three types of constants:
80115
///
81116
/// * Scalar: 123

parser/src/ast/expression.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -90,28 +90,31 @@ impl From<ResolvableIdentifier> for Identifier {
9090

9191
/// Represents an identifier qualified with its namespace.
9292
///
93-
/// Identifiers in AirScript are separated into two namespaces: one for functions,
94-
/// and one for bindings. This is because functions cannot be bound, and bindings
95-
/// cannot be called, so we can always disambiguate identifiers based on its usage.
93+
/// Identifiers in AirScript are separated into three namespaces: one for functions,
94+
/// one for buses and one for bindings. This is because functions cannot be bound,
95+
/// added to / remove from, buses cannot be bound or called, and bindings
96+
/// cannot be called added to / remove from.
97+
/// So we can always disambiguate identifiers based on its usage.
9698
///
9799
/// It is still probably best practice to avoid having name conflicts between functions
98100
/// and bindings, but that is a matter of style rather than one of necessity.
99101
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Spanned)]
100102
pub enum NamespacedIdentifier {
101103
Function(#[span] Identifier),
102104
Binding(#[span] Identifier),
105+
Bus(#[span] Identifier),
103106
}
104107
impl NamespacedIdentifier {
105108
pub fn id(&self) -> Identifier {
106109
match self {
107-
Self::Function(ident) | Self::Binding(ident) => *ident,
110+
Self::Function(ident) | Self::Binding(ident) | Self::Bus(ident) => *ident,
108111
}
109112
}
110113
}
111114
impl AsRef<Identifier> for NamespacedIdentifier {
112115
fn as_ref(&self) -> &Identifier {
113116
match self {
114-
Self::Function(ref ident) | Self::Binding(ref ident) => ident,
117+
Self::Function(ref ident) | Self::Binding(ref ident) | Self::Bus(ref ident) => ident,
115118
}
116119
}
117120
}

parser/src/ast/module.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ pub struct Module {
5959
pub public_inputs: BTreeMap<Identifier, PublicInput>,
6060
pub random_values: Option<RandomValues>,
6161
pub trace_columns: Vec<TraceSegment>,
62+
pub buses: BTreeMap<Identifier, Bus>,
6263
pub boundary_constraints: Option<Span<Vec<Statement>>>,
6364
pub integrity_constraints: Option<Span<Vec<Statement>>>,
6465
}
@@ -81,6 +82,7 @@ impl Module {
8182
constants: Default::default(),
8283
evaluators: Default::default(),
8384
functions: Default::default(),
85+
buses: Default::default(),
8486
periodic_columns: Default::default(),
8587
public_inputs: Default::default(),
8688
random_values: None,
@@ -152,6 +154,11 @@ impl Module {
152154
Declaration::IntegrityConstraints(statements) => {
153155
module.declare_integrity_constraints(diagnostics, statements)?;
154156
}
157+
Declaration::Buses(mut buses) => {
158+
for bus in buses.drain(..) {
159+
module.declare_bus(diagnostics, &mut names, bus)?;
160+
}
161+
}
155162
}
156163
}
157164

@@ -416,6 +423,22 @@ impl Module {
416423
Ok(())
417424
}
418425

426+
fn declare_bus(
427+
&mut self,
428+
diagnostics: &DiagnosticsHandler,
429+
names: &mut HashSet<NamespacedIdentifier>,
430+
bus: Bus,
431+
) -> Result<(), SemanticAnalysisError> {
432+
if let Some(prev) = names.replace(NamespacedIdentifier::Bus(bus.name)) {
433+
conflicting_declaration(diagnostics, "bus", prev.span(), bus.name.span());
434+
return Err(SemanticAnalysisError::NameConflict(bus.name.span()));
435+
}
436+
437+
self.buses.insert(bus.name, bus);
438+
439+
Ok(())
440+
}
441+
419442
fn declare_periodic_column(
420443
&mut self,
421444
diagnostics: &DiagnosticsHandler,

parser/src/lexer/mod.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,15 @@ pub enum Token {
116116
/// Keyword to declare the function section in the AIR constraints module.
117117
Fn,
118118

119+
// BUSES KEYWORDS
120+
// --------------------------------------------------------------------------------------------
121+
/// Marks the beginning of buses section in the constraints file.
122+
Buses,
123+
/// Used to represent a multiset bus declaration.
124+
Unit,
125+
/// Used to represent a logup bus declaration.
126+
Mult,
127+
119128
// BOUNDARY CONSTRAINT KEYWORDS
120129
// --------------------------------------------------------------------------------------------
121130
/// Marks the beginning of boundary constraints section in the constraints file.
@@ -187,6 +196,9 @@ impl Token {
187196
"ev" => Self::Ev,
188197
"fn" => Self::Fn,
189198
"felt" => Self::Felt,
199+
"buses" => Self::Buses,
200+
"unit" => Self::Unit,
201+
"mult" => Self::Mult,
190202
"boundary_constraints" => Self::BoundaryConstraints,
191203
"integrity_constraints" => Self::IntegrityConstraints,
192204
"first" => Self::First,
@@ -260,6 +272,9 @@ impl fmt::Display for Token {
260272
Self::Ev => write!(f, "ev"),
261273
Self::Fn => write!(f, "fn"),
262274
Self::Felt => write!(f, "felt"),
275+
Self::Buses => write!(f, "buses"),
276+
Self::Unit => write!(f, "unit"),
277+
Self::Mult => write!(f, "mult"),
263278
Self::BoundaryConstraints => write!(f, "boundary_constraints"),
264279
Self::First => write!(f, "first"),
265280
Self::Last => write!(f, "last"),

parser/src/parser/grammar.lalrpop

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ Declaration: Declaration = {
7676
RandomValues => Declaration::RandomValues(<>),
7777
EvaluatorFunction => Declaration::EvaluatorFunction(<>),
7878
Function => Declaration::Function(<>),
79+
Buses => Declaration::Buses(<>),
7980
<l:@L> <trace:Trace> <r:@R> => Declaration::Trace(Span::new(span!(l, r), trace)),
8081
<PublicInputs> => Declaration::PublicInputs(<>),
8182
<BoundaryConstraints> => Declaration::BoundaryConstraints(<>),
@@ -180,6 +181,26 @@ PeriodicColumn: PeriodicColumn = {
180181
=> PeriodicColumn::new(span!(l, r), name, values),
181182
}
182183

184+
185+
// BUSES
186+
// ================================================================================================
187+
188+
// Buses are not required, and there is no limit to the number that can be provided.
189+
Buses: Span<Vec<Bus>> = {
190+
<l:@L> "buses" "{" <bus:Bus*> "}" <r:@R>
191+
=> Span::new(span!(l, r), bus)
192+
}
193+
194+
Bus: Bus = {
195+
<l:@L> <bus_type: BusType> <name: Identifier> "," <r:@R>
196+
=> Bus::new(span!(l, r), name, bus_type),
197+
}
198+
199+
BusType: BusType = {
200+
"unit" => BusType::Unit,
201+
"mult" => BusType::Mult,
202+
}
203+
183204
// RANDOM VALUES
184205
// ================================================================================================
185206

@@ -646,6 +667,9 @@ extern {
646667
"public_inputs" => Token::PublicInputs,
647668
"periodic_columns" => Token::PeriodicColumns,
648669
"random_values" => Token::RandomValues,
670+
"buses" => Token::Buses,
671+
"unit" => Token::Unit,
672+
"mult" => Token::Mult,
649673
"boundary_constraints" => Token::BoundaryConstraints,
650674
"first" => Token::First,
651675
"last" => Token::Last,

parser/src/parser/tests/buses.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
use miden_diagnostics::SourceSpan;
2+
3+
use crate::ast::*;
4+
5+
use super::ParseTest;
6+
7+
#[test]
8+
fn buses() {
9+
let source = "
10+
mod test
11+
12+
buses {
13+
unit p,
14+
mult q,
15+
}";
16+
17+
let mut expected = Module::new(ModuleType::Library, SourceSpan::UNKNOWN, ident!(test));
18+
expected.buses.insert(
19+
ident!(p),
20+
Bus::new(SourceSpan::UNKNOWN, ident!(p), BusType::Unit),
21+
);
22+
expected.buses.insert(
23+
ident!(q),
24+
Bus::new(SourceSpan::UNKNOWN, ident!(q), BusType::Mult),
25+
);
26+
ParseTest::new().expect_module_ast(source, expected);
27+
}
28+
29+
#[test]
30+
fn empty_buses() {
31+
let source = "
32+
mod test
33+
34+
buses{}";
35+
36+
let expected = Module::new(ModuleType::Library, SourceSpan::UNKNOWN, ident!(test));
37+
ParseTest::new().expect_module_ast(source, expected);
38+
}

parser/src/parser/tests/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -627,6 +627,7 @@ macro_rules! import {
627627

628628
mod arithmetic_ops;
629629
mod boundary_constraints;
630+
mod buses;
630631
mod calls;
631632
mod constant_propagation;
632633
mod constants;

parser/src/sema/semantic_analysis.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1045,6 +1045,13 @@ impl<'a> VisitMut<SemanticAnalysisError> for SemanticAnalysis<'a> {
10451045
)
10461046
.emit();
10471047
}
1048+
NamespacedIdentifier::Bus(_) => {
1049+
self.diagnostics
1050+
.diagnostic(Severity::Error)
1051+
.with_message("reference to undefined bus")
1052+
.with_primary_label(namespaced_id.span(), "this bus is not defined")
1053+
.emit();
1054+
}
10481055
}
10491056

10501057
ControlFlow::Continue(())

0 commit comments

Comments
 (0)