diff --git a/Cargo.lock b/Cargo.lock index 2327b6f..1533890 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,7 +19,7 @@ checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" [[package]] name = "openqasm-parser" -version = "0.1.0" +version = "0.2.0" dependencies = [ "regex", ] diff --git a/Cargo.toml b/Cargo.toml index f4db79c..5a9b228 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "openqasm-parser" -version = "0.1.0" +version = "0.2.0" edition = "2024" [dependencies] diff --git a/default.nix b/default.nix new file mode 100644 index 0000000..011822f --- /dev/null +++ b/default.nix @@ -0,0 +1,36 @@ +{ pkgs, localLib }: +let + openqasm-parser = pkgs.rustPlatform.buildRustPackage { + pname = "openqasm-parser"; + version = "0.2.0"; + src = localLib.filters.cleanSourceWithFiles { + src = ./.; + files = [ + "Cargo.toml" + "Cargo.lock" + "src/" + ]; + }; + + cargoLock.lockFile = ./Cargo.lock; + }; +in +{ + packages = { + inherit openqasm-parser; + }; + + devShells = { + openqasm-parser = pkgs.mkShell { + inputsFrom = [ + openqasm-parser + ]; + + buildInputs = with pkgs; [ + rust-analyzer + rustfmt + clippy + ]; + }; + }; +} diff --git a/flake.nix b/flake.nix index 2014f99..3940725 100644 --- a/flake.nix +++ b/flake.nix @@ -14,36 +14,22 @@ flake-utils.lib.eachDefaultSystem (system: let pkgs = nixpkgs.legacyPackages.${system}; + localLib = import ./localLib { inherit pkgs; }; + project = import ./. { inherit pkgs localLib; }; in { - packages = { - openqasm-parser = pkgs.rustPlatform.buildRustPackage { - pname = "openqasm-parser"; - version = "0.1.0"; - src = self; - - cargoLock.lockFile = ./Cargo.lock; + packages = + project.packages // + { + default = project.packages.openqasm-parser; }; - default = self.packages.${system}.openqasm-parser; - }; - - devShells = { - openqasm-parser = pkgs.mkShell { - inputsFrom = [ - self.packages.${system}.openqasm-parser - ]; - - buildInputs = with pkgs; [ - rust-analyzer - rustfmt - clippy - ]; + devShells = + project.devShells // + { + default = project.devShells.openqasm-parser; }; - default = self.devShells.${system}.openqasm-parser; - }; - formatter = pkgs.nixpkgs-fmt; } ); diff --git a/localLib/default.nix b/localLib/default.nix new file mode 100644 index 0000000..06a118b --- /dev/null +++ b/localLib/default.nix @@ -0,0 +1,4 @@ +{ pkgs }: +{ + filters = import ./filters.nix { inherit pkgs; }; +} diff --git a/localLib/filters.nix b/localLib/filters.nix new file mode 100644 index 0000000..5293e6d --- /dev/null +++ b/localLib/filters.nix @@ -0,0 +1,28 @@ +{ pkgs }: +let + testFiles = files: relPath: + let + filesHead = builtins.head files; + isDirectory = (builtins.substring ((builtins.stringLength filesHead) - 1) 1 filesHead) == "/"; + current = + if isDirectory + then builtins.substring 0 ((builtins.stringLength filesHead) - 1) filesHead + else filesHead; + in + files != [ ] && ( + relPath == current || + (isDirectory && builtins.match "${current}/.*" relPath != null) || + testFiles (builtins.tail files) relPath + ); +in +{ + cleanSourceWithFiles = { src, files }: + pkgs.lib.cleanSourceWith { + inherit src; + filter = path: type: + let + relPath = builtins.replaceStrings [ (toString src + "/") ] [ "" ] (toString path); + in + testFiles files relPath; + }; +} diff --git a/src/ast/factory.rs b/src/ast/factory.rs index 1105438..d23e5fa 100644 --- a/src/ast/factory.rs +++ b/src/ast/factory.rs @@ -8,6 +8,7 @@ pub fn make_reg_dec(loc: Location, name: String, ty: node::RegTy, size: u32) -> name: name, ty: ty, size: size, + dec: None, }) } @@ -15,6 +16,7 @@ pub fn make_simple_reg(loc: Location, name: String) -> node::Reg { node::Reg::SimpleReg { loc: loc, name: name, + dec: None, } } @@ -23,6 +25,7 @@ pub fn make_subscript_reg(loc: Location, name: String, index: u32) -> node::Reg loc: loc, name: name, index: index, + dec: None, } } @@ -37,6 +40,7 @@ pub fn make_gate_stmt( gate: gate, pars: pars, args: args, + dec: None, } } diff --git a/src/ast/node.rs b/src/ast/node.rs index 2be7d28..bd87b10 100644 --- a/src/ast/node.rs +++ b/src/ast/node.rs @@ -1,3 +1,8 @@ +use std::rc::Rc; + +use crate::bind::gate; +use crate::bind::reg; + use crate::utils::location::Location; pub enum RegTy { @@ -14,12 +19,22 @@ impl ToString for RegTy { } } +impl Clone for RegTy { + fn clone(&self) -> Self { + match self { + Self::QReg => Self::QReg, + Self::CReg => Self::CReg, + } + } +} + pub enum Dec { RegDec { loc: Location, name: String, ty: RegTy, size: u32, + dec: Option>, }, } @@ -31,6 +46,7 @@ impl Dec { name: _, ty: _, size: _, + dec: _, } => &loc, } } @@ -40,22 +56,29 @@ pub enum Reg { SimpleReg { loc: Location, name: String, + dec: Option>, }, SubscriptReg { loc: Location, name: String, index: u32, + dec: Option>, }, } impl Reg { pub fn get_loc(&self) -> &Location { match self { - Reg::SimpleReg { loc, name: _ } => &loc, + Reg::SimpleReg { + loc, + name: _, + dec: _, + } => &loc, Reg::SubscriptReg { loc, name: _, index: _, + dec: _, } => &loc, } } @@ -68,6 +91,7 @@ pub enum Stmt { gate: String, pars: Vec, args: Vec, + dec: Option>, }, MeasureStmt { loc: Location, @@ -89,6 +113,7 @@ impl Stmt { gate: _, pars: _, args: _, + dec: _, } => &loc, Stmt::MeasureStmt { loc, diff --git a/src/ast/pretty_printer.rs b/src/ast/pretty_printer.rs index 7d76b4a..91df615 100644 --- a/src/ast/pretty_printer.rs +++ b/src/ast/pretty_printer.rs @@ -13,18 +13,48 @@ impl Visitor for PrettyPrinter { ref name, ref ty, size, - } => println!("{} {}[{}];", ty.to_string(), name, size), + ref dec, + } => { + if let Some(dec) = dec { + println!( + "{} {} /* {} */ [{}];", + ty.to_string(), + name, + dec.to_string(), + size + ) + } else { + println!("{} {}[{}];", ty.to_string(), name, size) + } + } } } fn visit_reg(&mut self, e: &node::Reg) { match e { - &node::Reg::SimpleReg { loc: _, ref name } => print!("{}", name), + &node::Reg::SimpleReg { + loc: _, + ref name, + ref dec, + } => { + if let Some(dec) = dec { + print!("{} /* {} */", name, dec.to_string()) + } else { + print!("{}", name) + } + } &node::Reg::SubscriptReg { loc: _, ref name, index, - } => print!("{}[{}]", name, index), + ref dec, + } => { + if let Some(dec) = dec { + print!("{} /* {} */ [{}]", name, dec.to_string(), index) + } else { + print!("{}[{}]", name, index) + } + } } } @@ -36,8 +66,12 @@ impl Visitor for PrettyPrinter { ref gate, ref pars, ref args, + ref dec, } => { print!("{} ", gate); + if let Some(dec) = dec { + print!("/* {} */ ", dec.to_string()); + } if !pars.is_empty() { print!("("); self.visit_exp(&pars[0]); diff --git a/src/bind/binder.rs b/src/bind/binder.rs new file mode 100644 index 0000000..0b4ae33 --- /dev/null +++ b/src/bind/binder.rs @@ -0,0 +1,124 @@ +use std::collections::HashMap; +use std::io::Error; +use std::rc::Rc; +use std::vec::Vec; + +use crate::ast::node; +use crate::ast::node::RegTy; +use crate::ast::visitor::MutVisitor; + +use crate::bind::gate::GateDec; +use crate::bind::par::ParDec; +use crate::bind::reg::RegDec; + +pub struct Binder { + gates: HashMap>, + regs: HashMap>, + error: Option, +} + +impl MutVisitor for Binder { + fn visit_dec(&mut self, e: &mut node::Dec) { + match e { + node::Dec::RegDec { + loc: _, + name, + ty, + size, + dec, + } => { + if self.regs.contains_key(name) { + self.error = Some(Error::other(format!("Redefined register '{}'", name))); + return; + } + let it = Rc::new(RegDec::new(name.clone(), ty.clone(), *size)); + dec.replace(Rc::clone(&it)); + self.regs.insert(name.clone(), it); + } + } + } + + fn visit_reg(&mut self, e: &mut node::Reg) { + match e { + node::Reg::SimpleReg { loc: _, name, dec } => match self.regs.get(name) { + Some(it) => drop(dec.replace(Rc::clone(it))), + _ => self.error = Some(Error::other(format!("Undeclared register '{}'", name))), + }, + node::Reg::SubscriptReg { + loc: _, + name, + index: _, + dec, + } => match self.regs.get(name) { + Some(it) => drop(dec.replace(Rc::clone(it))), + _ => self.error = Some(Error::other(format!("Undeclared register '{}'", name))), + }, + } + } + + fn visit_stmt(&mut self, e: &mut node::Stmt) { + match e { + node::Stmt::DecStmt(dec) => self.visit_dec(dec), + node::Stmt::GateStmt { + loc: _, + gate, + pars: _, + args, + dec, + } => { + match self.gates.get(gate) { + Some(it) => drop(dec.replace(Rc::clone(it))), + _ => self.error = Some(Error::other(format!("Undeclared gate '{}'", gate))), + }; + args.iter_mut().for_each(|arg| self.visit_reg(arg)); + } + node::Stmt::MeasureStmt { loc: _, src, dst } => { + self.visit_reg(src); + self.visit_reg(dst); + } + node::Stmt::ResetStmt { loc: _, reg } => self.visit_reg(reg), + } + } + + fn visit_exp(&mut self, _: &mut node::Exp) {} +} + +impl Binder { + pub fn new() -> Binder { + let ugate = Rc::new(GateDec::new( + String::from("U"), + vec![ + Rc::new(ParDec::new(String::from("theta"))), + Rc::new(ParDec::new(String::from("phi"))), + Rc::new(ParDec::new(String::from("lambda"))), + ], + vec![Rc::new(RegDec::new(String::from("qubit"), RegTy::QReg, 1))], + )); + let cxgate = Rc::new(GateDec::new( + String::from("CX"), + vec![], + vec![ + Rc::new(RegDec::new(String::from("control"), RegTy::QReg, 1)), + Rc::new(RegDec::new(String::from("target"), RegTy::QReg, 1)), + ], + )); + + let mut gates = HashMap::new(); + gates.insert(ugate.get_name().clone(), ugate); + gates.insert(cxgate.get_name().clone(), cxgate); + + Binder { + gates: gates, + regs: HashMap::new(), + error: None, + } + } + + pub fn bind(&mut self, program: &mut Vec) { + program.iter_mut().for_each(|x| self.visit_stmt(x)); + } + + pub fn get_error(&self) -> &Option { + &self.error + } +} diff --git a/src/bind/gate.rs b/src/bind/gate.rs new file mode 100644 index 0000000..e8b4c54 --- /dev/null +++ b/src/bind/gate.rs @@ -0,0 +1,45 @@ +use std::rc::Rc; +use std::vec::Vec; + +use crate::bind::par::ParDec; +use crate::bind::reg::RegDec; + +pub struct GateDec { + name: String, + pars: Vec>, + args: Vec>, +} + +impl ToString for GateDec { + fn to_string(&self) -> String { + format!( + "{:p} ({},[{}],[{}])", + self, + self.name, + self.pars + .iter() + .map(|x| x.to_string()) + .collect::>() + .join(","), + self.args + .iter() + .map(|x| x.to_string()) + .collect::>() + .join(",") + ) + } +} + +impl GateDec { + pub fn new(name: String, pars: Vec>, args: Vec>) -> GateDec { + GateDec { + name: name, + pars: pars, + args: args, + } + } + + pub fn get_name(&self) -> &String { + &self.name + } +} diff --git a/src/bind/mod.rs b/src/bind/mod.rs new file mode 100644 index 0000000..c6b29a0 --- /dev/null +++ b/src/bind/mod.rs @@ -0,0 +1,4 @@ +pub mod binder; +pub mod gate; +pub mod par; +pub mod reg; diff --git a/src/bind/par.rs b/src/bind/par.rs new file mode 100644 index 0000000..3589fca --- /dev/null +++ b/src/bind/par.rs @@ -0,0 +1,19 @@ +pub struct ParDec { + name: String, +} + +impl ToString for ParDec { + fn to_string(&self) -> String { + format!("{:p} ({})", self, self.name) + } +} + +impl ParDec { + pub fn new(name: String) -> ParDec { + ParDec { name: name } + } + + pub fn get_name(&self) -> &String { + &self.name + } +} diff --git a/src/bind/reg.rs b/src/bind/reg.rs new file mode 100644 index 0000000..f5f883e --- /dev/null +++ b/src/bind/reg.rs @@ -0,0 +1,33 @@ +use crate::ast::node::RegTy; + +pub struct RegDec { + name: String, + ty: RegTy, + size: u32, +} + +impl ToString for RegDec { + fn to_string(&self) -> String { + format!( + "{:p} ({},{},{})", + self, + self.ty.to_string(), + self.name, + self.size + ) + } +} + +impl RegDec { + pub fn new(name: String, ty: RegTy, size: u32) -> RegDec { + RegDec { + name: name, + ty: ty, + size: size, + } + } + + pub fn get_name(&self) -> &String { + &self.name + } +} diff --git a/src/main.rs b/src/main.rs index 0f40af8..f7d59f5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,19 +1,32 @@ pub mod ast; +pub mod bind; pub mod parse; pub mod utils; use crate::ast::pretty_printer::PrettyPrinter; +use crate::bind::binder::Binder; + use crate::parse::lexer::Lexer; use crate::parse::parser::Parser; fn main() { let lexer = Lexer::new(String::from(""), std::io::stdin()); let mut parser = Parser::new(lexer); - let program = parser.parse_input(); - program.iter().for_each(|it| PrettyPrinter.pretty_print(it)); + let mut binder = Binder::new(); + + if let Some(program) = &mut parser.parse_input() { + binder.bind(program); + + PrettyPrinter.pretty_print(program); + } + parser .get_error() .iter() .for_each(|it| println!("{}", it.to_string())); + binder + .get_error() + .iter() + .for_each(|it| println!("{}", it.to_string())); } diff --git a/src/parse/lexer.rs b/src/parse/lexer.rs index 9f25efc..6cb5449 100644 --- a/src/parse/lexer.rs +++ b/src/parse/lexer.rs @@ -61,6 +61,36 @@ impl Lexer { } } + fn handle_line_comment(&mut self) { + loop { + match self.next_char() { + Some('\n') | None => break, + _ => self.reset_char(), + } + } + self.reset_char(); + } + + fn handle_block_comment(&mut self) { + loop { + match self.next_char() { + Some('*') => { + self.reset_char(); + match self.next_char() { + Some('/') => break, + _ => continue, + } + } + None => { + self.error = Some(Error::other("Unterminated comment")); + return; + } + _ => self.reset_char(), + } + } + self.reset_char(); + } + fn process_identifier(&mut self) { let mut location = self.location.clone(); let mut id = String::from(""); @@ -196,7 +226,20 @@ impl Lexer { None => None, }, '*' => Some(TokenTy::Mul), - '/' => Some(TokenTy::Div), + '/' => match self.next_char() { + Some('/') => { + self.reset_char(); + self.handle_line_comment(); + return self.process(); + } + Some('*') => { + self.reset_char(); + self.handle_block_comment(); + return self.process(); + } + Some(_) => Some(TokenTy::Div), + None => None, + }, '^' => Some(TokenTy::Pow), _ => { self.error = Some(Error::other(format!("Invalid operator '{}'", c)));