diff --git a/Cargo.toml b/Cargo.toml index b1e2586a..2551f3dc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" diff --git a/src/instruction.rs b/src/instruction.rs index 24678b64..fdd7c461 100644 --- a/src/instruction.rs +++ b/src/instruction.rs @@ -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), @@ -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); @@ -435,6 +497,7 @@ pub enum Instruction { SwapPhases(SwapPhases), WaveformDefinition(WaveformDefinition), Arithmetic(Arithmetic), + Logic(Logic), Halt, Label(Label), Move(Move), @@ -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(_) @@ -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) + } } } } diff --git a/src/parser/command.rs b/src/parser/command.rs index d21ff204..63f49810 100644 --- a/src/parser/command.rs +++ b/src/parser/command.rs @@ -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; @@ -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 { + 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)?; diff --git a/src/parser/common.rs b/src/parser/common.rs index 2ddffcf6..77b41f04 100644 --- a/src/parser/common.rs +++ b/src/parser/common.rs @@ -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, @@ -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>, diff --git a/src/parser/instruction.rs b/src/parser/instruction.rs index e94752a0..41e5de86 100644 --- a/src/parser/instruction.rs +++ b/src/parser/instruction.rs @@ -20,7 +20,7 @@ use nom::{ }; use crate::{ - instruction::{ArithmeticOperator, Instruction}, + instruction::{ArithmeticOperator, BinaryLogic, Instruction, LogicalOperator}, token, }; @@ -43,7 +43,10 @@ pub fn parse_instruction(input: ParserInput) -> ParserResult { 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), @@ -61,7 +64,10 @@ pub fn parse_instruction(input: ParserInput) -> ParserResult { // 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), @@ -76,6 +82,10 @@ pub fn parse_instruction(input: ParserInput) -> ParserResult { // 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), @@ -88,7 +98,10 @@ pub fn parse_instruction(input: ParserInput) -> ParserResult { 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, @@ -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::{ @@ -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, diff --git a/src/parser/lexer.rs b/src/parser/lexer.rs index 4db79e2a..61ad2f05 100644 --- a/src/parser/lexer.rs +++ b/src/parser/lexer.rs @@ -96,6 +96,7 @@ pub enum Command { Neg, Nop, Not, + Or, Pragma, Pulse, RawCapture, @@ -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), diff --git a/src/program/graph.rs b/src/program/graph.rs index b8eef721..3f85454c 100644 --- a/src/program/graph.rs +++ b/src/program/graph.rs @@ -511,6 +511,7 @@ impl ScheduledProgram { let instruction_index = Some(index); match instruction { Instruction::Arithmetic(_) + | Instruction::Logic(_) | Instruction::Capture(_) | Instruction::Delay(_) | Instruction::Fence(_) diff --git a/src/program/memory.rs b/src/program/memory.rs index f71a31bf..8a583ff7 100644 --- a/src/program/memory.rs +++ b/src/program/memory.rs @@ -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)] @@ -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, diff --git a/src/program/mod.rs b/src/program/mod.rs index 5afa3462..830ead1c 100644 --- a/src/program/mod.rs +++ b/src/program/mod.rs @@ -173,6 +173,7 @@ impl Program { | Instruction::Pragma(_) | Instruction::WaveformDefinition(_) | Instruction::Arithmetic(_) + | Instruction::Logic(_) | Instruction::Halt | Instruction::Label(_) | Instruction::Move(_)