microsoft/qdk

Public

mirrored from https://github.com/microsoft/qdkAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v1.8.0

Branches

Tags

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

Clone

HTTPS

Download ZIP

compiler/qsc/src/bin/qsi.rs

314lines · 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 message(&mut self, msg: &str) -> Result<(), output::Error> {
85 println!("{msg}");
86 Ok(())
87 }
88}
89
90#[allow(clippy::too_many_lines)]
91fn main() -> miette::Result<ExitCode> {
92 let cli = Cli::parse();
93 let mut features = LanguageFeatures::from_iter(cli.features);
94
95 let (store, dependencies, source_map) = if let Some(qsharp_json) = cli.qsharp_json {
96 if let Some(dir) = qsharp_json.parent() {
97 match load_project(dir, &mut features) {
98 Ok(items) => items,
99 Err(code) => return Ok(code),
100 }
101 } else {
102 eprintln!("{} must have a parent directory", qsharp_json.display());
103 return Ok(ExitCode::FAILURE);
104 }
105 } else {
106 let sources = cli
107 .sources
108 .iter()
109 .map(read_source)
110 .collect::<miette::Result<Vec<_>>>()?;
111
112 let mut store = PackageStore::new(qsc::compile::core());
113 let dependencies = if cli.nostdlib {
114 vec![]
115 } else {
116 let std_id = store.insert(qsc::compile::std(&store, TargetCapabilityFlags::all()));
117 vec![(std_id, None)]
118 };
119 (
120 store,
121 dependencies,
122 SourceMap::new(sources, cli.entry.clone().map(std::convert::Into::into)),
123 )
124 };
125
126 if cli.exec {
127 let mut interpreter = match (if cli.debug {
128 Interpreter::new_with_debug
129 } else {
130 Interpreter::new
131 })(
132 source_map,
133 PackageType::Exe,
134 TargetCapabilityFlags::all(),
135 features,
136 store,
137 &dependencies,
138 ) {
139 Ok(interpreter) => interpreter,
140 Err(errors) => {
141 for error in errors {
142 eprintln!("error: {:?}", Report::new(error));
143 }
144 return Ok(ExitCode::FAILURE);
145 }
146 };
147 return Ok(print_exec_result(
148 interpreter.eval_entry(&mut TerminalReceiver),
149 ));
150 }
151
152 let mut interpreter = match (if cli.debug {
153 Interpreter::new_with_debug
154 } else {
155 Interpreter::new
156 })(
157 source_map,
158 PackageType::Lib,
159 TargetCapabilityFlags::all(),
160 features,
161 store,
162 &dependencies,
163 ) {
164 Ok(interpreter) => interpreter,
165 Err(errors) => {
166 for error in errors {
167 eprintln!("error: {:?}", Report::new(error));
168 }
169 return Ok(ExitCode::FAILURE);
170 }
171 };
172
173 if let Some(entry) = cli.entry {
174 print_interpret_result(interpreter.eval_fragments(&mut TerminalReceiver, &entry));
175 }
176
177 repl(&mut interpreter, &mut TerminalReceiver).into_diagnostic()?;
178
179 Ok(ExitCode::SUCCESS)
180}
181
182fn repl(interpreter: &mut Interpreter, receiver: &mut impl Receiver) -> io::Result<()> {
183 print_prompt(false);
184
185 let mut lines = io::BufReader::new(io::stdin()).lines();
186 while let Some(line) = lines.next() {
187 let mut line = line?;
188
189 while line.ends_with('\\') {
190 print_prompt(true);
191 if let Some(continuation) = lines.next() {
192 line.pop(); // Remove backslash.
193 line.push_str(&continuation?);
194 } else {
195 println!();
196 return Ok(());
197 }
198 }
199
200 if !line.trim().is_empty() {
201 print_interpret_result(interpreter.eval_fragments(receiver, &line));
202 }
203
204 print_prompt(false);
205 }
206
207 println!();
208 Ok(())
209}
210
211fn read_source(path: impl AsRef<Path>) -> miette::Result<(SourceName, SourceContents)> {
212 let path = path.as_ref();
213 let contents = fs::read_to_string(path)
214 .into_diagnostic()
215 .with_context(|| format!("could not read source file `{}`", path.display()))?;
216
217 Ok((path.to_string_lossy().into(), contents.into()))
218}
219
220fn print_prompt(continuation: bool) {
221 if continuation {
222 print!(" > ");
223 } else {
224 print!("qsi$ ");
225 }
226
227 io::stdout().flush().expect("standard out should flush");
228}
229
230fn print_interpret_result(result: InterpretResult) {
231 match result {
232 Ok(Value::Tuple(items)) if items.is_empty() => {}
233 Ok(value) => println!("{value}"),
234 Err(errors) => {
235 for error in errors {
236 if let Some(stack_trace) = error.stack_trace() {
237 eprintln!("{stack_trace}");
238 }
239 let report = Report::new(error);
240 eprintln!("error: {report:?}");
241 }
242 }
243 }
244}
245
246fn print_exec_result(result: Result<Value, Vec<interpret::Error>>) -> ExitCode {
247 match result {
248 Ok(value) => {
249 println!("{value}");
250 ExitCode::SUCCESS
251 }
252 Err(errors) => {
253 for error in errors {
254 if let Some(stack_trace) = error.stack_trace() {
255 eprintln!("{stack_trace}");
256 }
257 let report = Report::new(error);
258 eprintln!("error: {report:?}");
259 }
260 ExitCode::FAILURE
261 }
262 }
263}
264
265/// Loads a project from the given directory and returns the package store, the list of
266/// dependencies, and the source map.
267/// Pre-populates the package store with all of the compiled dependencies.
268#[allow(clippy::type_complexity)]
269fn load_project(
270 dir: impl AsRef<Path>,
271 features: &mut LanguageFeatures,
272) -> Result<(PackageStore, Vec<(PackageId, Option<Arc<str>>)>, SourceMap), ExitCode> {
273 let fs = StdFs;
274 let project = match fs.load_project(dir.as_ref(), None) {
275 Ok(project) => project,
276 Err(errs) => {
277 for e in errs {
278 eprintln!("{e:?}");
279 }
280 return Err(ExitCode::FAILURE);
281 }
282 };
283
284 if !project.errors.is_empty() {
285 for e in project.errors {
286 eprintln!("{e:?}");
287 }
288 return Err(ExitCode::FAILURE);
289 }
290
291 // This builds all the dependencies
292 let buildable_program =
293 BuildableProgram::new(TargetCapabilityFlags::all(), project.package_graph_sources);
294
295 if !buildable_program.dependency_errors.is_empty() {
296 for e in buildable_program.dependency_errors {
297 eprintln!("{e:?}");
298 }
299 return Err(ExitCode::FAILURE);
300 }
301
302 let BuildableProgram {
303 store,
304 user_code,
305 user_code_dependencies,
306 ..
307 } = buildable_program;
308
309 let source_map = qsc::SourceMap::new(user_code.sources, None);
310
311 features.merge(LanguageFeatures::from_iter(user_code.language_features));
312
313 Ok((store, user_code_dependencies, source_map))
314}
315