qvnt_qasm/lib.rs
1#![allow(unused)]
2//! # QASM
3//!
4//! This library is a parser for the IBM OpenQASM 2.0 language.
5//!
6//! It is seperated into 3 parts:
7//! 1. Processing - Removing comments and resolving include statements.
8//! 2. Lexing - Splitting up the processed source file into a list of tokens (`Vec<Token>`)
9//! 3. Parsing - Turning the list of tokens into a list of AST nodes.
10//!
11//! There is methods provided for each.
12//!
13//! ## Processing
14//!
15//! Processing is done with the [processing](fn.process.html) function.
16//! It requires 2 arguments, the input string, and the `Path` of the directory it
17//! is in. This path is used to locate the include files.
18//! It is used like so:
19//!
20//! ```no_run
21//! extern crate qasm;
22//! use std::env;
23//!
24//! let source = r#"
25//! OPENQASM 2.0;
26//! // Here is a comment
27//! include "sample.inc";
28//!
29//! qreg a[3];
30//! // And so on
31//! "#;
32//!
33//! let cwd = env::current_dir().unwrap();
34//! qasm::process(source, &cwd);
35//! /* Will Return:
36//! *
37//! * ```
38//! * OPENQASM 2.0;
39//! *
40//! * (CONTENTS OF SAMPLE.INC)
41//! *
42//! * qreg a[3];
43//! * ```
44//! */
45//!
46//! ```
47//!
48//! ## Lexing
49//!
50//! Lexing is done with the [lex](fn.lex.html) function.
51//! It takes a source string (which must not have any comments or include statements)
52//! and returns a Vector of [Token](enum.Token.html)s.
53//!
54//! It is used like so:
55//!
56//! ```rust
57//! extern crate qasm;
58//!
59//! let source = r#"
60//! OPENQASM 2.0;
61//! qreg a[3];
62//! CX a[0], a[1];
63//! "#;
64//!
65//! let tokens = qasm::lex(source);
66//! println!("{:?}", tokens);
67//! // [OpenQASM, Real(2.0), Semicolon,
68//! // QReg, Id("a"), LSParen, NNInteger(3), RSParen, Semicolon,
69//! // Id("CX"), Id("a"), LSParen, NNInteger(0), RSParen, Comma, Id("a"), LSParen, NNInteger(1), RSParen, Semicolon]
70//! ```
71//!
72//! for a full list of tokens that can be returned, please see the [Token](enum.Token.html) enum.
73//!
74//! ## Parsing
75//! Parsing is done with the [parse](fn.parse.html) function. It accepts a vector of [Token](enum.Tokem.html)s
76//! and returns a vector of [AstNode](enum.AstNode.html)s or an [Error](enum.Error.html) as a result
77//!
78//!
79//! It is used like so:
80//! ```rust
81//! extern crate qasm;
82//! use qasm::Token;
83//!
84//! let mut tokens = vec![
85//! Token::OpenQASM,
86//! Token::Real(2.0),
87//! Token::Semicolon,
88//! Token::QReg,
89//! Token::Id("a".to_string()),
90//! Token::LSParen,
91//! Token::NNInteger(3),
92//! Token::RSParen,
93//! Token::Semicolon,
94//! Token::Id("CX".to_string()),
95//! Token::Id("a".to_string()),
96//! Token::LSParen,
97//! Token::NNInteger(0),
98//! Token::RSParen,
99//! Token::Comma,
100//! Token::Id("a".to_string()),
101//! Token::LSParen,
102//! Token::NNInteger(1),
103//! Token::RSParen,
104//! Token::Semicolon,
105//! ];
106//! let ast = qasm::parse(&mut tokens);
107//!
108//! // Ok([QReg("a", 3), ApplyGate("CX", [Qubit("a", 0), Qubit("a", 1)], [])])
109//! ```
110//!
111//! ## Combining Functions
112//! The functions can be combined to process, lex and parse a source string.
113//! Here is an example that reads a file 'test.qasm', processes it and then prints the AST.
114//!
115//! ### test.qasm
116//!
117//! ```qasm
118//! OPENQASM 2.0;
119//!
120//! // Clifford gate: Hadamard
121//! gate h a { u2(0,pi) a; }
122//!
123//! qreg q[2];
124//! creg c[1];
125//!
126//! h q[0];
127//! CX q[0], q[1];
128//!
129//! measure q[1] -> c[1];
130//! ```
131//!
132//! ### main.rs
133//!
134//! ```no_run
135//! extern crate qvnt_qasm as qasm;
136//!
137//! use std::env;
138//! use std::fs::File;
139//! use std::io::prelude::*;
140//! use qasm::{process, lex, parse};
141//!
142//! fn main() {
143//! let cwd = vec![env::current_dir().unwrap()];
144//! let mut source = String::new();
145//!
146//! let mut f = File::open("test.qasm").expect("cannot find source file 'test.qasm'");
147//! f.read_to_string(&mut source).expect("couldn't read file 'test.qasm'");
148//!
149//! let processed_source = process(&source, cwd).unwrap();
150//! let mut tokens = lex(&processed_source);
151//! let ast = parse(&mut tokens);
152//!
153//! println!("{:?}", ast);
154//! }
155//! ```
156//!
157//! ### Output
158//!
159//! ```rust,ignore
160//! Ok([
161//! Gate("h", ["a"], [], [ApplyGate("u2", [Register("a")], [" 0 ", " pi "])]),
162//! QReg("q", 2),
163//! CReg("c", 1),
164//! ApplyGate("h", [Qubit("q", 0)], []),
165//! ApplyGate("CX", [Qubit("q", 0), Qubit("q", 1)], []),
166//! Measure(Qubit("q", 1), Qubit("c", 1))
167//! ])
168//! ```
169extern crate regex;
170
171mod ast;
172mod error;
173mod lexer;
174mod parser;
175mod pre_proc;
176mod token;
177
178use regex::Regex;
179use std::fs::File;
180use std::io::prelude::*;
181use std::path::Path;
182
183pub use ast::Argument;
184pub use ast::AstNode;
185pub use error::Error;
186pub use token::Token;
187
188pub(crate) type Span = std::ops::Range<usize>;
189
190/// Remove comments from an input string and resolves include statements.
191///
192/// This function has 2 arguments, the input string, and the path that the file is in.
193/// The path is used to resolve the include statements.
194///
195/// This function returns a String that has no comments and no include statements.
196/// The include statements will have been replaced by the text of the include file.
197///
198/// This function will panic when the included file doesn't exist or when it couldn't be read.
199///
200/// ## Example
201/// ```no_run
202/// extern crate qasm;
203/// use std::env;
204///
205/// let source = r#"
206/// OPENQASM 2.0;
207/// // Here is a comment
208/// include "sample.inc";
209///
210/// qreg a[3];
211/// // And so on
212/// "#;
213///
214/// let cwd = env::current_dir().unwrap();
215/// qasm::process(source, &cwd);
216/// /* Will Return:
217/// *
218/// * ```
219/// * OPENQASM 2.0;
220/// *
221/// * (CONTENTS OF SAMPLE.INC)
222/// *
223/// * qreg a[3];
224/// * ```
225/// */
226/// ```
227pub fn process<'t, Cwds>(input: &'t str, cwds: Cwds) -> Result<String, &'t str>
228where
229 Cwds: IntoIterator,
230 Cwds::IntoIter: Clone,
231 Cwds::Item: AsRef<Path>,
232{
233 let include_regex = Regex::new(r#"(?P<i>include\s*"(?P<s>.*)";)"#).unwrap(); // Regex for include statments
234 let mut span = (0, 0);
235 let mut source = String::new();
236 let cwds = cwds.into_iter();
237 for cap in include_regex.captures_iter(&*input) {
238 let m = cap.name("s").unwrap();
239 let filename = m.as_str();
240 let mut cwd_iter = cwds.clone();
241 let content = loop {
242 let cwd = cwd_iter.next().ok_or_else(|| filename)?;
243 let path = cwd.as_ref().join(&filename);
244 let mut f = match File::open(path) {
245 Ok(f) => f,
246 Err(_) => continue,
247 };
248
249 let mut buf = String::new();
250 f.read_to_string(&mut buf).unwrap();
251 break buf;
252 };
253
254 let m = cap.name("i").unwrap();
255 let prev = span.1;
256 span = (m.start(), m.end());
257
258 source += &input[prev..span.0];
259 source += &content;
260 }
261 source = source + &input[span.1..];
262
263 let comment_regex = Regex::new(r"//.*").unwrap();
264 let source = comment_regex.replace_all(&*source, "").to_string(); // Removed All Comments
265
266 Ok(source)
267}
268
269pub fn pre_process<'t>(input: &'t str) -> pre_proc::ProcStr<'t> {
270 use pre_proc::ProcStr;
271 ProcStr::from(input).combine(ProcStr::includes).combine(ProcStr::comments)
272}
273
274/// Take a source string with no includes or comments and returns the tokens
275///
276/// The source string can be processed with the [process](fn.process.html) function.
277/// The tokens are all varients of [Token](enum.Token.html). An illegal token will be returned
278/// for any unrechognised tokens.
279///
280/// ## Examples
281///
282/// ```rust
283/// extern crate qasm;
284///
285/// let source = r#"
286/// OPENQASM 2.0;
287/// qreg a[3];
288/// CX a[0], a[1];
289/// "#;
290///
291/// let tokens = qasm::lex(source);
292/// println!("{:?}", tokens);
293/// // [OpenQASM, Real(2.0), Semicolon,
294/// // QReg, Id("a"), LSParen, NNInteger(3), RSParen, Semicolon,
295/// // Id("CX"), Id("a"), LSParen, NNInteger(0), RSParen, Comma, Id("a"), LSParen, NNInteger(1), RSParen, Semicolon]
296/// ```
297pub fn lex<'t, P>(input: P) -> token::TokenTree<'t, impl Clone + Iterator<Item = Token<'t>>>
298where
299 pre_proc::ProcStr<'t>: From<P>,
300{
301 let proc_input = pre_proc::ProcStr::from(input);
302 let input = proc_input.input;
303 let tree = proc_input
304 .flat_map(move |span| lexer::Lexer::new_spanned(input, span))
305 .peekable();
306 token::TokenTree { input, tree }
307}
308
309/// Changes a vector of tokens into an AST.
310///
311/// Parsing is done with the [parse](fn.parse.html) function. It accepts a vector of [Token](enum.Tokem.html)s
312/// and returns a vector of [AstNode](enum.AstNode.html)s or an [Error](enum.Error.html) as a result
313///
314/// ## Example
315///
316/// ```rust
317/// extern crate qasm;
318///
319/// let mut tokens = vec![
320/// qasm::Token::OpenQASM,
321/// qasm::Token::Real(2.0),
322/// qasm::Token::Semicolon,
323/// qasm::Token::QReg,
324/// qasm::Token::Id("a".to_string()),
325/// qasm::Token::LSParen,
326/// qasm::Token::NNInteger(3),
327/// qasm::Token::RSParen,
328/// qasm::Token::Semicolon,
329/// qasm::Token::Id("CX".to_string()),
330/// qasm::Token::Id("a".to_string()),
331/// qasm::Token::LSParen,
332/// qasm::Token::NNInteger(0),
333/// qasm::Token::RSParen,
334/// qasm::Token::Comma,
335/// qasm::Token::Id("a".to_string()),
336/// qasm::Token::LSParen,
337/// qasm::Token::NNInteger(1),
338/// qasm::Token::RSParen,
339/// qasm::Token::Semicolon,
340/// ];
341/// let ast = qasm::parse(&mut tokens);
342///
343/// // Ok([QReg("a", 3), ApplyGate("CX", [Qubit("a", 0), Qubit("a", 1)], [])])
344/// ```
345pub fn parse<'t, I>(mut tokens: token::TokenTree<'t, I>) -> Result<Vec<AstNode<'t>>, Error>
346where
347 I: Iterator<Item = token::Token<'t>>,
348{
349 parser::parse(&mut tokens)
350}