microsoft/qdk

Public

mirrored fromhttps://github.com/microsoft/qdkAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
billti/bloch

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

compiler/qsc/src/bin/qsi.rs

324lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4allocator::assign_global!();
5
6use clap::{crate_version, Parser};
7use miette::{Context, IntoDiagnostic, Report, Result};
8use num_bigint::BigUint;
9use num_complex::Complex64;
10use qsc::{
11 hir::PackageId,
12 interpret::{self, InterpretResult, Interpreter},
13 packages::BuildableProgram,
14 PackageStore,
15};
16use qsc_data_structures::{language_features::LanguageFeatures, target::TargetCapabilityFlags};
17use qsc_eval::{
18 output::{self, Receiver},
19 state::format_state_id,
20 val::Value,
21};
22use qsc_frontend::compile::{SourceContents, SourceMap, SourceName};
23use qsc_passes::PackageType;
24use qsc_project::{FileSystem, StdFs};
25use std::{
26 fs,
27 io::{self, prelude::BufRead, Write},
28 path::{Path, PathBuf},
29 process::ExitCode,
30 string::String,
31 sync::Arc,
32};
33
34#[derive(Debug, Parser)]
35#[command(name = "qsi", version = concat!(crate_version!(), " (", env!("QSHARP_GIT_HASH"), ")"))]
36#[command(author, about, next_line_help = true)]
37struct Cli {
38 /// Use the given file on startup as initial session input.
39 #[arg(long = "use")]
40 sources: Vec<PathBuf>,
41
42 /// Execute the given Q# expression on startup.
43 #[arg(long)]
44 entry: Option<String>,
45
46 /// Disable automatic inclusion of the standard library.
47 #[arg(long)]
48 nostdlib: bool,
49
50 /// Exit after loading the files or running the given file(s)/entry on the command line.
51 #[arg(long)]
52 exec: bool,
53
54 /// Path to a Q# manifest for a project
55 #[arg(short, long)]
56 qsharp_json: Option<PathBuf>,
57
58 /// Language features to compile with
59 #[arg(short, long)]
60 features: Vec<String>,
61
62 /// Compile the given files and interactive snippets in debug mode.
63 #[arg(long)]
64 debug: bool,
65}
66
67struct TerminalReceiver;
68
69impl Receiver for TerminalReceiver {
70 fn state(
71 &mut self,
72 states: Vec<(BigUint, Complex64)>,
73 qubit_count: usize,
74 ) -> Result<(), output::Error> {
75 println!("DumpMachine:");
76 for (qubit, amplitude) in states {
77 let id = format_state_id(&qubit, qubit_count);
78 println!("{id}: [{}, {}]", amplitude.re, amplitude.im);
79 }
80
81 Ok(())
82 }
83
84 fn matrix(&mut self, matrix: Vec<Vec<Complex64>>) -> std::result::Result<(), output::Error> {
85 println!("Matrix:");
86 for row in matrix {
87 let row = row.iter().map(|elem| format!("[{}, {}]", elem.re, elem.im));
88 println!("{}", row.collect::<Vec<_>>().join(", "));
89 }
90
91 Ok(())
92 }
93
94 fn message(&mut self, msg: &str) -> Result<(), output::Error> {
95 println!("{msg}");
96 Ok(())
97 }
98}
99
100#[allow(clippy::too_many_lines)]
101fn main() -> miette::Result<ExitCode> {
102 let cli = Cli::parse();
103 let mut features = LanguageFeatures::from_iter(cli.features);
104
105 let (store, dependencies, source_map) = if let Some(qsharp_json) = cli.qsharp_json {
106 if let Some(dir) = qsharp_json.parent() {
107 match load_project(dir, &mut features) {
108 Ok(items) => items,
109 Err(code) => return Ok(code),
110 }
111 } else {
112 eprintln!("{} must have a parent directory", qsharp_json.display());
113 return Ok(ExitCode::FAILURE);
114 }
115 } else {
116 let sources = cli
117 .sources
118 .iter()
119 .map(read_source)
120 .collect::<miette::Result<Vec<_>>>()?;
121
122 let mut store = PackageStore::new(qsc::compile::core());
123 let dependencies = if cli.nostdlib {
124 vec![]
125 } else {
126 let std_id = store.insert(qsc::compile::std(&store, TargetCapabilityFlags::all()));
127 vec![(std_id, None)]
128 };
129 (
130 store,
131 dependencies,
132 SourceMap::new(sources, cli.entry.clone().map(std::convert::Into::into)),
133 )
134 };
135
136 if cli.exec {
137 let mut interpreter = match (if cli.debug {
138 Interpreter::new_with_debug
139 } else {
140 Interpreter::new
141 })(
142 source_map,
143 PackageType::Exe,
144 TargetCapabilityFlags::all(),
145 features,
146 store,
147 &dependencies,
148 ) {
149 Ok(interpreter) => interpreter,
150 Err(errors) => {
151 for error in errors {
152 eprintln!("error: {:?}", Report::new(error));
153 }
154 return Ok(ExitCode::FAILURE);
155 }
156 };
157 return Ok(print_exec_result(
158 interpreter.eval_entry(&mut TerminalReceiver),
159 ));
160 }
161
162 let mut interpreter = match (if cli.debug {
163 Interpreter::new_with_debug
164 } else {
165 Interpreter::new
166 })(
167 source_map,
168 PackageType::Lib,
169 TargetCapabilityFlags::all(),
170 features,
171 store,
172 &dependencies,
173 ) {
174 Ok(interpreter) => interpreter,
175 Err(errors) => {
176 for error in errors {
177 eprintln!("error: {:?}", Report::new(error));
178 }
179 return Ok(ExitCode::FAILURE);
180 }
181 };
182
183 if let Some(entry) = cli.entry {
184 print_interpret_result(interpreter.eval_fragments(&mut TerminalReceiver, &entry));
185 }
186
187 repl(&mut interpreter, &mut TerminalReceiver).into_diagnostic()?;
188
189 Ok(ExitCode::SUCCESS)
190}
191
192fn repl(interpreter: &mut Interpreter, receiver: &mut impl Receiver) -> io::Result<()> {
193 print_prompt(false);
194
195 let mut lines = io::BufReader::new(io::stdin()).lines();
196 while let Some(line) = lines.next() {
197 let mut line = line?;
198
199 while line.ends_with('\\') {
200 print_prompt(true);
201 if let Some(continuation) = lines.next() {
202 line.pop(); // Remove backslash.
203 line.push_str(&continuation?);
204 } else {
205 println!();
206 return Ok(());
207 }
208 }
209
210 if !line.trim().is_empty() {
211 print_interpret_result(interpreter.eval_fragments(receiver, &line));
212 }
213
214 print_prompt(false);
215 }
216
217 println!();
218 Ok(())
219}
220
221fn read_source(path: impl AsRef<Path>) -> miette::Result<(SourceName, SourceContents)> {
222 let path = path.as_ref();
223 let contents = fs::read_to_string(path)
224 .into_diagnostic()
225 .with_context(|| format!("could not read source file `{}`", path.display()))?;
226
227 Ok((path.to_string_lossy().into(), contents.into()))
228}
229
230fn print_prompt(continuation: bool) {
231 if continuation {
232 print!(" > ");
233 } else {
234 print!("qsi$ ");
235 }
236
237 io::stdout().flush().expect("standard out should flush");
238}
239
240fn print_interpret_result(result: InterpretResult) {
241 match result {
242 Ok(Value::Tuple(items)) if items.is_empty() => {}
243 Ok(value) => println!("{value}"),
244 Err(errors) => {
245 for error in errors {
246 if let Some(stack_trace) = error.stack_trace() {
247 eprintln!("{stack_trace}");
248 }
249 let report = Report::new(error);
250 eprintln!("error: {report:?}");
251 }
252 }
253 }
254}
255
256fn print_exec_result(result: Result<Value, Vec<interpret::Error>>) -> ExitCode {
257 match result {
258 Ok(value) => {
259 println!("{value}");
260 ExitCode::SUCCESS
261 }
262 Err(errors) => {
263 for error in errors {
264 if let Some(stack_trace) = error.stack_trace() {
265 eprintln!("{stack_trace}");
266 }
267 let report = Report::new(error);
268 eprintln!("error: {report:?}");
269 }
270 ExitCode::FAILURE
271 }
272 }
273}
274
275/// Loads a project from the given directory and returns the package store, the list of
276/// dependencies, and the source map.
277/// Pre-populates the package store with all of the compiled dependencies.
278#[allow(clippy::type_complexity)]
279fn load_project(
280 dir: impl AsRef<Path>,
281 features: &mut LanguageFeatures,
282) -> Result<(PackageStore, Vec<(PackageId, Option<Arc<str>>)>, SourceMap), ExitCode> {
283 let fs = StdFs;
284 let project = match fs.load_project(dir.as_ref(), None) {
285 Ok(project) => project,
286 Err(errs) => {
287 for e in errs {
288 eprintln!("{e:?}");
289 }
290 return Err(ExitCode::FAILURE);
291 }
292 };
293
294 if !project.errors.is_empty() {
295 for e in project.errors {
296 eprintln!("{e:?}");
297 }
298 return Err(ExitCode::FAILURE);
299 }
300
301 // This builds all the dependencies
302 let buildable_program =
303 BuildableProgram::new(TargetCapabilityFlags::all(), project.package_graph_sources);
304
305 if !buildable_program.dependency_errors.is_empty() {
306 for e in buildable_program.dependency_errors {
307 eprintln!("{e:?}");
308 }
309 return Err(ExitCode::FAILURE);
310 }
311
312 let BuildableProgram {
313 store,
314 user_code,
315 user_code_dependencies,
316 ..
317 } = buildable_program;
318
319 let source_map = qsc::SourceMap::new(user_code.sources, None);
320
321 features.merge(LanguageFeatures::from_iter(user_code.language_features));
322
323 Ok((store, user_code_dependencies, source_map))
324}
325