Skip to content

Commit

Permalink
feat: parse binary logic
Browse files Browse the repository at this point in the history
  • Loading branch information
nilslice committed Mar 14, 2022
1 parent e52bea9 commit 7a97298
Show file tree
Hide file tree
Showing 9 changed files with 229 additions and 14 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "quil-rs"
description = "Rust tooling for Quil (Quantum Instruction Language)"
version ="0.8.7"
version = "0.8.7"
edition = "2018"
license = "Apache-2.0"
repository = "https://github.com/rigetti/quil-rust"
Expand Down
67 changes: 67 additions & 0 deletions src/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,62 @@ impl fmt::Display for ArithmeticOperator {
}
}

#[derive(Clone, Debug, PartialEq)]
pub enum LogicalOperand {
LiteralInteger(i64),
MemoryReference(MemoryReference),
}

impl fmt::Display for LogicalOperand {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match &self {
LogicalOperand::LiteralInteger(value) => write!(f, "{}", value),
LogicalOperand::MemoryReference(value) => write!(f, "{}", value),
}
}
}

#[derive(Clone, Debug, PartialEq)]
pub enum LogicalOperator {
Binary(BinaryLogic),
Unary(UnaryLogic),
}

#[derive(Clone, Debug, PartialEq)]
pub enum BinaryLogic {
And,
Or,
Ior,
Xor,
}

#[derive(Clone, Debug, PartialEq)]
pub enum UnaryLogic {
Neg,
Not,
True,
False,
}

impl fmt::Display for LogicalOperator {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match &self {
LogicalOperator::Binary(logic) => match logic {
BinaryLogic::And => write!(f, "AND"),
BinaryLogic::Or => write!(f, "OR"),
BinaryLogic::Ior => write!(f, "IOR"),
BinaryLogic::Xor => write!(f, "XOR"),
},
LogicalOperator::Unary(logic) => match logic {
UnaryLogic::Neg => write!(f, "NEG"),
UnaryLogic::Not => write!(f, "NOT"),
UnaryLogic::True => write!(f, "TRUE"),
UnaryLogic::False => write!(f, "FALSE"),
},
}
}
}

#[derive(Clone, Debug, PartialEq)]
pub enum AttributeValue {
String(String),
Expand Down Expand Up @@ -364,6 +420,12 @@ pub struct Arithmetic {
pub source: ArithmeticOperand,
}

#[derive(Clone, Debug, PartialEq)]
pub struct Logic {
pub operator: LogicalOperator,
pub operands: (MemoryReference, LogicalOperand),
}

#[derive(Clone, Debug, PartialEq)]
pub struct Label(pub String);

Expand Down Expand Up @@ -435,6 +497,7 @@ pub enum Instruction {
SwapPhases(SwapPhases),
WaveformDefinition(WaveformDefinition),
Arithmetic(Arithmetic),
Logic(Logic),
Halt,
Label(Label),
Move(Move),
Expand Down Expand Up @@ -481,6 +544,7 @@ impl From<&Instruction> for InstructionRole {
| Instruction::ShiftPhase(_)
| Instruction::SwapPhases(_) => InstructionRole::RFControl,
Instruction::Arithmetic(_)
| Instruction::Logic(_)
| Instruction::Move(_)
| Instruction::Exchange(_)
| Instruction::Load(_)
Expand Down Expand Up @@ -801,6 +865,9 @@ impl fmt::Display for Instruction {
write!(f, "JUMP-WHEN @{} {}", target, condition)
}
Instruction::Label(Label(label)) => write!(f, "LABEL @{}", label),
Instruction::Logic(Logic { operator, operands }) => {
write!(f, "{} {} {}", operator, operands.0, operands.1)
}
}
}
}
Expand Down
23 changes: 21 additions & 2 deletions src/parser/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@ use nom::{
use crate::instruction::{
Arithmetic, ArithmeticOperand, ArithmeticOperator, Calibration, Capture, CircuitDefinition,
Declaration, Delay, Exchange, Fence, FrameDefinition, Instruction, Jump, JumpUnless, JumpWhen,
Label, Load, Measurement, Move, Pragma, Pulse, RawCapture, Reset, SetFrequency, SetPhase,
SetScale, ShiftFrequency, ShiftPhase, Store, Waveform, WaveformDefinition,
Label, Load, Logic, LogicalOperator, Measurement, Move, Pragma, Pulse, RawCapture, Reset,
SetFrequency, SetPhase, SetScale, ShiftFrequency, ShiftPhase, Store, Waveform,
WaveformDefinition,
};
use crate::parser::common::parse_variable_qubit;
use crate::parser::instruction::parse_block;
Expand Down Expand Up @@ -57,6 +58,24 @@ pub fn parse_arithmetic(
))
}

/// Parse an arithmetic instruction of the form `destination source`.
/// Called using the arithmetic operator itself (such as `ADD`) which should be previously parsed.
pub fn parse_logical_binary(
operator: LogicalOperator,
input: ParserInput,
) -> ParserResult<Instruction> {
let (input, left) = common::parse_memory_reference(input)?;
let (input, right) = common::parse_binary_logic_operand(input)?;

Ok((
input,
Instruction::Logic(Logic {
operator,
operands: (left, right),
}),
))
}

/// Parse the contents of a `DECLARE` instruction.
pub fn parse_declare<'a>(input: ParserInput<'a>) -> ParserResult<'a, Instruction> {
let (input, name) = token!(Identifier(v))(input)?;
Expand Down
24 changes: 22 additions & 2 deletions src/parser/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ use crate::{
expected_token,
expression::Expression,
instruction::{
ArithmeticOperand, AttributeValue, FrameIdentifier, GateModifier, MemoryReference, Qubit,
ScalarType, Vector, WaveformInvocation,
ArithmeticOperand, AttributeValue, FrameIdentifier, GateModifier, LogicalOperand,
MemoryReference, Qubit, ScalarType, Vector, WaveformInvocation,
},
parser::lexer::Operator,
token,
Expand Down Expand Up @@ -71,6 +71,26 @@ pub fn parse_arithmetic_operand<'a>(input: ParserInput<'a>) -> ParserResult<'a,
))(input)
}

/// Parse the operand of a binary logic instruction, which may be a literal integer or memory reference.
pub fn parse_binary_logic_operand<'a>(input: ParserInput<'a>) -> ParserResult<'a, LogicalOperand> {
alt((
map(
tuple((opt(token!(Operator(o))), token!(Integer(v)))),
|(op, v)| {
let sign = match op {
None => 1,
Some(Operator::Minus) => -1,
_ => panic!("Implement this error"), // TODO
};
LogicalOperand::LiteralInteger(sign * (v as i64))
},
),
map(parse_memory_reference, |f| {
LogicalOperand::MemoryReference(f)
}),
))(input)
}

/// Parse a single attribute key-value pair of a frame. The value may be either a frame or an expression.
pub fn parse_frame_attribute<'a>(
input: ParserInput<'a>,
Expand Down
102 changes: 96 additions & 6 deletions src/parser/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use nom::{
};

use crate::{
instruction::{ArithmeticOperator, Instruction},
instruction::{ArithmeticOperator, BinaryLogic, Instruction, LogicalOperator},
token,
};

Expand All @@ -43,7 +43,10 @@ pub fn parse_instruction(input: ParserInput) -> ParserResult<Instruction> {
Some((Token::Command(command), remainder)) => {
match command {
Command::Add => command::parse_arithmetic(ArithmeticOperator::Add, remainder),
// Command::And => {}
Command::And => command::parse_logical_binary(
LogicalOperator::Binary(BinaryLogic::And),
remainder,
),
Command::Capture => command::parse_capture(remainder, true),
// Command::Convert => {}
Command::Declare => command::parse_declare(remainder),
Expand All @@ -61,7 +64,10 @@ pub fn parse_instruction(input: ParserInput) -> ParserResult<Instruction> {
// Command::GT => {}
Command::Halt => Ok((remainder, Instruction::Halt)),
// Command::Include => {}
// Command::Ior => {}
Command::Ior => command::parse_logical_binary(
LogicalOperator::Binary(BinaryLogic::Ior),
remainder,
),
Command::Jump => command::parse_jump(remainder),
Command::JumpUnless => command::parse_jump_unless(remainder),
Command::JumpWhen => command::parse_jump_when(remainder),
Expand All @@ -76,6 +82,10 @@ pub fn parse_instruction(input: ParserInput) -> ParserResult<Instruction> {
// Command::Neg => {}
// Command::Nop => {}
// Command::Not => {}
Command::Or => command::parse_logical_binary(
LogicalOperator::Binary(BinaryLogic::Or),
remainder,
),
Command::Pragma => command::parse_pragma(remainder),
Command::Pulse => command::parse_pulse(remainder, true),
Command::RawCapture => command::parse_raw_capture(remainder, true),
Expand All @@ -88,7 +98,10 @@ pub fn parse_instruction(input: ParserInput) -> ParserResult<Instruction> {
Command::Store => command::parse_store(remainder),
Command::Sub => command::parse_arithmetic(ArithmeticOperator::Subtract, remainder),
// Command::Wait => {}
// Command::Xor => {}
Command::Xor => command::parse_logical_binary(
LogicalOperator::Binary(BinaryLogic::Xor),
remainder,
),
_ => Err(nom::Err::Failure(Error {
input: &input[..1],
error: ErrorKind::UnsupportedInstruction,
Expand Down Expand Up @@ -149,8 +162,8 @@ mod tests {
use std::collections::HashMap;

use crate::instruction::{
Label, Reset, SetFrequency, SetPhase, SetScale, ShiftFrequency, ShiftPhase, Waveform,
WaveformDefinition,
BinaryLogic, Label, Logic, LogicalOperand, LogicalOperator, Reset, SetFrequency, SetPhase,
SetScale, ShiftFrequency, ShiftPhase, Waveform, WaveformDefinition,
};
use crate::parser::lexer::lex;
use crate::{
Expand Down Expand Up @@ -242,6 +255,83 @@ mod tests {
]
);

make_test!(
logic,
parse_instructions,
"AND ro 1\nOR ro ro[1]\nIOR ro[1] ro[2]\nXOR ro[1] 0\nAND ro[1] ro[2]",
vec![
Instruction::Logic(Logic {
operator: LogicalOperator::Binary(BinaryLogic::And),
operands: (
MemoryReference {
name: "ro".to_owned(),
index: 0
},
LogicalOperand::LiteralInteger(1)
)
}),
Instruction::Logic(Logic {
operator: LogicalOperator::Binary(BinaryLogic::Or),
operands: (
MemoryReference {
name: "ro".to_owned(),
index: 0
},
LogicalOperand::MemoryReference(MemoryReference {
name: "ro".to_owned(),
index: 1
})
)
}),
Instruction::Logic(Logic {
operator: LogicalOperator::Binary(BinaryLogic::Ior),
operands: (
MemoryReference {
name: "ro".to_owned(),
index: 1
},
LogicalOperand::MemoryReference(MemoryReference {
name: "ro".to_owned(),
index: 2
})
)
}),
Instruction::Logic(Logic {
operator: LogicalOperator::Binary(BinaryLogic::Xor),
operands: (
MemoryReference {
name: "ro".to_owned(),
index: 1
},
LogicalOperand::LiteralInteger(0)
)
}),
Instruction::Logic(Logic {
operator: LogicalOperator::Binary(BinaryLogic::And),
operands: (
MemoryReference {
name: "ro".to_owned(),
index: 1
},
LogicalOperand::MemoryReference(MemoryReference {
name: "ro".to_owned(),
index: 2
})
)
}),
]
);

#[test]
fn test_binary_logic_error() {
["AND ro", "XOR 1 1", "IOR 1", "OR 1 ro"]
.iter()
.for_each(|input| {
let tokens = lex(input).unwrap();
assert!(parse_instructions(&tokens).is_err())
})
}

make_test!(
capture_instructions,
parse_instructions,
Expand Down
2 changes: 2 additions & 0 deletions src/parser/lexer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ pub enum Command {
Neg,
Nop,
Not,
Or,
Pragma,
Pulse,
RawCapture,
Expand Down Expand Up @@ -196,6 +197,7 @@ fn recognize_command_or_identifier(identifier: String) -> Token {
"DEFGATE" => Token::Command(DefGate),
"ADD" => Token::Command(Add),
"AND" => Token::Command(And),
"OR" => Token::Command(Or),
"CONVERT" => Token::Command(Convert),
"DIV" => Token::Command(Div),
"EQ" => Token::Command(Eq),
Expand Down
1 change: 1 addition & 0 deletions src/program/graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,7 @@ impl ScheduledProgram {
let instruction_index = Some(index);
match instruction {
Instruction::Arithmetic(_)
| Instruction::Logic(_)
| Instruction::Capture(_)
| Instruction::Delay(_)
| Instruction::Fence(_)
Expand Down
21 changes: 18 additions & 3 deletions src/program/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ use std::collections::HashSet;
use crate::expression::Expression;
use crate::instruction::{
Arithmetic, ArithmeticOperand, Capture, CircuitDefinition, Delay, Exchange, Gate,
GateDefinition, Instruction, Jump, JumpUnless, JumpWhen, Label, Load,
MeasureCalibrationDefinition, Measurement, MemoryReference, Move, Pulse, RawCapture, SetPhase,
SetScale, ShiftPhase, Store, Vector, WaveformInvocation,
GateDefinition, Instruction, Jump, JumpUnless, JumpWhen, Label, Load, Logic, LogicalOperand,
LogicalOperator, MeasureCalibrationDefinition, Measurement, MemoryReference, Move, Pulse,
RawCapture, SetPhase, SetScale, ShiftPhase, Store, Vector, WaveformInvocation,
};

#[derive(Clone, Debug, Hash, PartialEq)]
Expand Down Expand Up @@ -90,6 +90,21 @@ impl Instruction {
/// Return all memory accesses by the instruction - in expressions, captures, and memory manipulation
pub fn get_memory_accesses(&self) -> MemoryAccesses {
match self {
Instruction::Logic(Logic { operands, operator }) => match operator {
LogicalOperator::Binary(_) => {
let mut memories = HashSet::new();
memories.insert(operands.0.name.clone());
if let LogicalOperand::MemoryReference(mem) = &operands.1 {
memories.insert(mem.name.clone());
}

MemoryAccesses {
reads: memories,
..Default::default()
}
}
LogicalOperator::Unary(_) => todo!("implement memory access for unary logic"),
},
Instruction::Arithmetic(Arithmetic {
destination,
source,
Expand Down
Loading

0 comments on commit 7a97298

Please sign in to comment.