A Quantum Engineering Focused Linux Distro From Scratch — Day 14

Marcus Edwards
3 min readMar 26, 2023

The last time I was working on the WASM to QMASM transpiler that will be a part of our linux distribution, I was thinking of ways to simplify the syntax for declaring PhysicalExpressions like an 8 bit multiplier.

AbstractExpression::Mul{ ty: Type::I8 } => {
let mut expr: Vec<PhysicalExpression> = vec![
PhysicalExpression::Mul {
operand_one: Box::new(operands[5]),
operand_two: Box::new(operands[11])
},
PhysicalExpression::Add {
operand_one: Box::new(PhysicalExpression::Not {
operand: Box::new(PhysicalExpression::Mul {
operand_one: Box::new(operands[4]),
operand_two: Box::new(operands[11])
})
}),
operand_two: Box::new(PhysicalExpression::Not {
operand: Box::new(PhysicalExpression::Mul {
operand_one: Box::new(operands[5]),
operand_two: Box::new(operands[10])
})
})
},

...

PhysicalExpression::Mul {
operand_one: Box::new(operands[0]),
operand_two: Box::new(operands[6])
}
];

...

Thanks to a The Rust Hobbyist Lounge, a discord server, I was reminded that declarative macros would be a great way to approach this.

Declarative macros use matching expressions, like regular expressions, to match Rust code. So, writing a macro is a lot like writing a match expression. The matched code can then be expanded to whatever (longer) code block the macro specifies, at compile time. This isn’t limited to replacing Rust expressions by other Rust expressions. You can use loops, conditionals and any other Rust syntax. You can also match expressions and sub-expressions and transform them by assigning them variable names in the macro.

Just like in PHP, macro variables are identified by their starting with a dollar sign $. Here’s an example of a simple macro that takes num![x] to for any value x. PhysicalExpression::Num { val: x } for any Rust expression x.

macro_rules! num {
($x:expr) => {
{
PhysicalExpression::Num {
val: $x
}
}
};
}

You can also match multiple expressions, or any repeated patterns. I only needed to match pairs of inputs to help simplify my code today.

macro_rules! not {
($x:expr) => {
{
PhysicalExpression::Not {
operand: Box::new(operands[$x])
}
}
};
}

macro_rules! mul {
($x:expr, $y:expr) => {
{
PhysicalExpression::Mul {
operand_one: Box::new(operands[$x]),
operand_two: Box::new(operands[$y])
}
}
};
}

macro_rules! add {
($x:expr, $y:expr) => {
{
PhysicalExpression::Add {
operand_one: Box::new(operands[$x]),
operand_two: Box::new(operands[$y])
}
}
};
}

With these macros in place, what was an over 200 line block of code was shortened to this dense format.

let mut expr: Vec<PhysicalExpression> = vec![
mul![5, 11],
add![not![mul![4, 11]], not![mul![5, 10]]],
add![add![not![mul![3, 11]], mul![4, 10]], not![mul![5, 9]]],
add![add![not![mul![2, 11]], mul![3, 10]], add![mul![4, 9], not![mul![5, 8]]]],
add![add![add![not![mul![1, 11]], mul![2, 10]], add![mul![4, 8], not![mul![5, 7]]]], add![num![1], mul![3, 9]]],
add![add![add![not![mul![0, 11]], mul![1, 10]], add![mul![2, 9], not![mul![5, 6]]]], add![mul![4, 7], mul![3, 8]]],
add![add![add![mul![0, 10], mul![1, 9]], add![mul![2, 8], mul![3, 7]]], mul![4, 6]],
add![add![add![mul![0, 10], mul![1, 9]], add![mul![2, 8], mul![3, 7]]], mul![4, 6]],
add![add![mul![0, 9], mul![1, 8]], add![mul![2, 7], mul![3, 8]]],
add![add![mul![0, 8], mul![1, 7]], mul![2, 6]],
add![mul![0, 7], mul![1, 6]],
mul![0, 6]
];

As a next step I plan to simplify the expression here further by writing macros for adding more than two bits at a time.

To learn more about Rust macros including procedural macros and derive macros, give this a read.

Cheers.

--

--