microsoft/qdk

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
cesarzc/hw-provider-package

Branches

Tags

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

Clone

HTTPS

Download ZIP

compiler/qsc_codegen/src/qsharp.rs

830lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4#[cfg(test)]
5mod spec_decls;
6
7#[cfg(test)]
8mod tests;
9
10#[cfg(test)]
11mod test_utils;
12
13use std::io::Write;
14use std::vec;
15
16use qsc_ast::ast::{
17 self, Attr, BinOp, Block, CallableBody, CallableDecl, CallableKind, Expr, ExprKind, Functor,
18 FunctorExpr, FunctorExprKind, Ident, Idents, ImportOrExportItem, Item, ItemKind, Lit,
19 Mutability, Pat, PatKind, Path, Pauli, QubitInit, QubitInitKind, QubitSource, SetOp, SpecBody,
20 SpecDecl, SpecGen, Stmt, StmtKind, StringComponent, TernOp, TopLevelNode, Ty, TyDef, TyDefKind,
21 TyKind, UnOp,
22};
23use qsc_ast::ast::{Namespace, Package};
24use qsc_ast::visit::Visitor;
25use qsc_formatter::formatter::format_str;
26use qsc_frontend::compile::PackageStore;
27
28fn write<W: Write>(output: W, packages: &[&Package]) {
29 let mut gen = QSharpGen::new(output);
30 for package in packages {
31 gen.visit_package(package);
32 }
33}
34
35pub fn write_store<W: Write>(output: W, store: &PackageStore) {
36 let mut gen = QSharpGen::new(output);
37 for (_, unit) in store {
38 gen.visit_package(&unit.ast.package);
39 }
40}
41
42#[must_use]
43pub fn write_store_string(store: &PackageStore) -> Vec<String> {
44 let mut package_strings: Vec<_> = vec![];
45 for (_, unit) in store {
46 package_strings.push(write_package_string(&unit.ast.package));
47 }
48 package_strings
49}
50
51#[must_use]
52pub fn write_package_string(package: &Package) -> String {
53 let mut output = Vec::new();
54 write(&mut output, &[package]);
55 let s = match std::str::from_utf8(&output) {
56 Ok(v) => v.to_owned(),
57 Err(e) => format!("Invalid UTF-8 sequence: {e}"),
58 };
59
60 output.clear();
61 format_str(&s)
62}
63
64struct QSharpGen<W: Write> {
65 pub(crate) output: W,
66}
67
68impl<W> QSharpGen<W>
69where
70 W: Write,
71{
72 pub fn new(output: W) -> Self {
73 Self { output }
74 }
75
76 pub fn write(&mut self, args: &str) {
77 write!(&mut self.output, "{args}").expect("write failed");
78 }
79
80 pub fn writeln(&mut self, args: &str) {
81 self.write(args);
82 self.write("\n");
83 }
84
85 /// special case for tuple with one element
86 /// otherwise we are changing the semantics of the program
87 fn ensure_trailing_comma_for_arity_one_tuples<T>(&mut self, most: &[T]) {
88 if most.is_empty() {
89 self.write(",");
90 }
91 }
92}
93
94impl<W: Write> Visitor<'_> for QSharpGen<W> {
95 fn visit_package(&mut self, package: &'_ Package) {
96 package.nodes.iter().for_each(|n| match n {
97 TopLevelNode::Namespace(ns) => {
98 self.visit_namespace(ns);
99 }
100 TopLevelNode::Stmt(stmt) => self.visit_stmt(stmt),
101 });
102 package.entry.iter().for_each(|e| self.visit_expr(e));
103 }
104
105 fn visit_namespace(&mut self, namespace: &'_ Namespace) {
106 self.write("namespace ");
107 self.visit_idents(&namespace.name);
108 self.writeln("{");
109 namespace.items.iter().for_each(|i| {
110 self.visit_item(i);
111 });
112 self.write("}");
113 }
114
115 fn visit_item(&mut self, item: &'_ Item) {
116 item.attrs.iter().for_each(|a| self.visit_attr(a));
117 match &*item.kind {
118 ItemKind::Err => {
119 unreachable!()
120 }
121 ItemKind::Callable(decl) => self.visit_callable_decl(decl),
122 ItemKind::Open(ns, alias) => {
123 self.write("open ");
124 self.visit_idents(ns);
125 if let Some(alias) = alias {
126 self.write(" as ");
127 self.visit_ident(alias);
128 }
129 self.writeln(";");
130 }
131 ItemKind::Ty(ident, def) => {
132 self.write("newtype ");
133 self.visit_ident(ident);
134 self.write(" = ");
135 self.visit_ty_def(def);
136 self.writeln(";");
137 }
138 ItemKind::Struct(decl) => self.visit_struct_decl(decl),
139 ItemKind::ImportOrExport(decl) => {
140 if decl.is_export() {
141 self.write("export ");
142 } else {
143 self.write("import ");
144 }
145
146 for (
147 ix,
148 ImportOrExportItem {
149 ref path,
150 ref is_glob,
151 ..
152 },
153 ) in decl.items.iter().enumerate()
154 {
155 let is_last = ix == decl.items.len() - 1;
156 self.visit_path(path);
157 if *is_glob {
158 self.write(".*");
159 }
160 if !is_last {
161 self.write(", ");
162 };
163 }
164 self.write(";");
165 }
166 }
167 }
168
169 fn visit_attr(&mut self, attr: &'_ Attr) {
170 self.write("@");
171 self.visit_ident(&attr.name);
172 self.visit_expr(&attr.arg);
173 self.writeln("");
174 }
175
176 fn visit_ty_def(&mut self, def: &'_ TyDef) {
177 match &*def.kind {
178 TyDefKind::Field(name, ty) => {
179 if let Some(n) = name {
180 self.visit_ident(n);
181 self.write(": ");
182 }
183 self.visit_ty(ty);
184 }
185 TyDefKind::Paren(def) => self.visit_ty_def(def),
186 TyDefKind::Tuple(defs) => {
187 self.write("(");
188 if let Some((last, most)) = defs.split_last() {
189 for i in most {
190 self.visit_ty_def(i);
191 self.write(", ");
192 }
193 self.visit_ty_def(last);
194 self.ensure_trailing_comma_for_arity_one_tuples(most);
195 }
196 self.write(")");
197 }
198 TyDefKind::Err => {}
199 }
200 }
201
202 fn visit_callable_decl(&mut self, decl: &'_ CallableDecl) {
203 match decl.kind {
204 CallableKind::Function => self.write("function "),
205 CallableKind::Operation => self.write("operation "),
206 }
207 self.visit_ident(&decl.name);
208 if !decl.generics.is_empty() {
209 self.write("<");
210 if let Some((last, most)) = decl.generics.split_last() {
211 for i in most {
212 self.visit_ident(i);
213 self.write(", ");
214 }
215 self.visit_ident(last);
216 }
217
218 self.write(">");
219 }
220
221 self.visit_pat(&decl.input);
222 self.write(" : ");
223 self.visit_ty(&decl.output);
224 if let Some(functors) = decl.functors.as_deref() {
225 self.write(" is ");
226 self.visit_functor_expr(functors);
227 }
228
229 match &*decl.body {
230 CallableBody::Block(block) => {
231 self.visit_block(block);
232 }
233 CallableBody::Specs(specs) => {
234 self.writeln("{");
235 specs.iter().for_each(|s| self.visit_spec_decl(s));
236 self.writeln("}");
237 }
238 }
239 }
240
241 fn visit_struct_decl(&mut self, decl: &'_ ast::StructDecl) {
242 self.write("struct ");
243 self.visit_ident(&decl.name);
244 self.writeln(" {");
245 if let Some((last, most)) = decl.fields.split_last() {
246 for i in most {
247 self.visit_field_def(i);
248 self.write(", ");
249 }
250 self.visit_field_def(last);
251 }
252 self.writeln("}");
253 }
254
255 fn visit_field_def(&mut self, def: &'_ ast::FieldDef) {
256 self.visit_ident(&def.name);
257 self.write(" : ");
258 self.visit_ty(&def.ty);
259 }
260
261 fn visit_spec_decl(&mut self, decl: &'_ SpecDecl) {
262 match decl.spec {
263 ast::Spec::Body => self.write("body "),
264 ast::Spec::Adj => self.write("adjoint "),
265 ast::Spec::Ctl => self.write("controlled "),
266 ast::Spec::CtlAdj => self.write("controlled adjoint "),
267 }
268 match &decl.body {
269 SpecBody::Gen(spec) => match spec {
270 SpecGen::Auto => self.writeln("auto;"),
271 SpecGen::Distribute => self.writeln("distribute;"),
272 SpecGen::Intrinsic => self.writeln("intrinsic;"),
273 SpecGen::Invert => self.writeln("invert;"),
274 SpecGen::Slf => self.writeln("self;"),
275 },
276 SpecBody::Impl(pat, block) => {
277 self.visit_pat(pat);
278 self.visit_block(block);
279 }
280 }
281 }
282
283 fn visit_functor_expr(&mut self, expr: &'_ FunctorExpr) {
284 match &*expr.kind {
285 FunctorExprKind::BinOp(op, lhs, rhs) => {
286 self.visit_functor_expr(lhs);
287 match op {
288 SetOp::Union => self.write(" + "),
289 SetOp::Intersect => self.write(" * "),
290 }
291 self.visit_functor_expr(rhs);
292 }
293 FunctorExprKind::Lit(functor) => match functor {
294 Functor::Adj => self.write("Adj"),
295 Functor::Ctl => self.write("Ctl"),
296 },
297 FunctorExprKind::Paren(expr) => {
298 self.write("(");
299 self.visit_functor_expr(expr);
300 self.write(")");
301 }
302 }
303 }
304
305 fn visit_ty(&mut self, ty: &'_ Ty) {
306 match &*ty.kind {
307 TyKind::Array(item) => {
308 self.visit_ty(item);
309 self.write("[]");
310 }
311 TyKind::Arrow(kind, lhs, rhs, functors) => {
312 self.visit_ty(lhs);
313 match kind {
314 CallableKind::Function => self.write(" -> "),
315 CallableKind::Operation => self.write(" => "),
316 }
317 self.visit_ty(rhs);
318 if let Some(functors) = functors.as_deref() {
319 self.write(" is ");
320 self.visit_functor_expr(functors);
321 }
322 }
323 TyKind::Hole => self.write("_"),
324 TyKind::Paren(ty) => {
325 self.write("(");
326 self.visit_ty(ty);
327 self.write(")");
328 }
329 TyKind::Path(path) => self.visit_path(path),
330 TyKind::Param(name) => self.visit_ident(name),
331 TyKind::Tuple(tys) => {
332 if tys.is_empty() {
333 self.write("()");
334 } else {
335 self.write("(");
336 if let Some((last, most)) = tys.split_last() {
337 for t in most {
338 self.visit_ty(t);
339 self.write(", ");
340 }
341 self.visit_ty(last);
342 self.ensure_trailing_comma_for_arity_one_tuples(most);
343 }
344 self.write(")");
345 }
346 }
347 TyKind::Err => unreachable!(),
348 }
349 }
350
351 fn visit_block(&mut self, block: &'_ Block) {
352 self.writeln(" {");
353 block.stmts.iter().for_each(|s| {
354 self.visit_stmt(s);
355 });
356 self.writeln("}");
357 }
358
359 fn visit_stmt(&mut self, stmt: &'_ Stmt) {
360 match &*stmt.kind {
361 StmtKind::Empty | StmtKind::Err => {}
362 StmtKind::Semi(expr) => {
363 self.visit_expr(expr);
364 self.writeln(";");
365 }
366 StmtKind::Expr(expr) => {
367 self.visit_expr(expr);
368 }
369 StmtKind::Item(item) => self.visit_item(item),
370 StmtKind::Local(mutability, pat, value) => {
371 match mutability {
372 Mutability::Mutable => self.write("mutable "),
373 Mutability::Immutable => self.write("let "),
374 }
375 self.visit_pat(pat);
376 self.write(" = ");
377 self.visit_expr(value);
378 self.writeln(";");
379 }
380 StmtKind::Qubit(source, pat, init, block) => {
381 match source {
382 QubitSource::Dirty => self.write("borrow "),
383 QubitSource::Fresh => self.write("use "),
384 }
385 self.visit_pat(pat);
386 self.write(" = ");
387 self.visit_qubit_init(init);
388 if let Some(b) = block {
389 self.visit_block(b);
390 } else {
391 self.writeln(";");
392 }
393 }
394 }
395 }
396
397 #[allow(clippy::too_many_lines)]
398 fn visit_expr(&mut self, expr: &'_ Expr) {
399 match &*expr.kind {
400 ExprKind::Array(exprs) => {
401 self.write("[");
402 if let Some((last, most)) = exprs.split_last() {
403 for e in most {
404 self.visit_expr(e);
405 self.write(", ");
406 }
407 self.visit_expr(last);
408 }
409 self.write("]");
410 }
411 ExprKind::ArrayRepeat(item, size) => {
412 self.write("[");
413 self.visit_expr(item);
414 self.write(", size = ");
415 self.visit_expr(size);
416 self.write("]");
417 }
418 ExprKind::Assign(lhs, rhs) => {
419 self.write("set ");
420 self.visit_expr(lhs);
421 self.write(" = ");
422 self.visit_expr(rhs);
423 }
424 ExprKind::AssignOp(op, lhs, rhs) => {
425 self.write("set ");
426 self.visit_expr(lhs);
427 self.write(" ");
428 let op_str = binop_as_str(op);
429 self.write(op_str);
430 self.write("= ");
431 self.visit_expr(rhs);
432 }
433 ExprKind::BinOp(op, lhs, rhs) => {
434 self.visit_expr(lhs);
435 self.write(" ");
436 let op_str = binop_as_str(op);
437 self.write(op_str);
438 self.write(" ");
439 self.visit_expr(rhs);
440 }
441 ExprKind::AssignUpdate(record, index, value) => {
442 self.write("set ");
443 self.visit_expr(record);
444 self.write(" w/= ");
445 self.visit_expr(index);
446 self.write(" <- ");
447 self.visit_expr(value);
448 }
449 ExprKind::Block(block) => self.visit_block(block),
450 ExprKind::Call(callee, arg) => {
451 self.visit_expr(callee);
452 self.visit_expr(arg);
453 }
454 ExprKind::Conjugate(within, apply) => {
455 self.write("within");
456 self.visit_block(within);
457 self.write("apply");
458 self.visit_block(apply);
459 }
460 ExprKind::Fail(msg) => {
461 self.write("fail ");
462 self.visit_expr(msg);
463 }
464 ExprKind::Field(record, name) => {
465 self.visit_expr(record);
466 self.write(".");
467 self.visit_ident(name);
468 }
469 ExprKind::For(pat, iter, block) => {
470 self.write("for ");
471 self.visit_pat(pat);
472 self.write(" in ");
473 self.visit_expr(iter);
474 self.write(" ");
475 self.visit_block(block);
476 }
477 ExprKind::If(cond, body, otherwise) => {
478 self.write("if ");
479 self.visit_expr(cond);
480 self.write(" ");
481 self.visit_block(body);
482 if let Some(expr) = otherwise {
483 if matches!(*expr.kind, ExprKind::If(..)) {
484 // visiting expr as if writes 'if' to make 'elif'
485 self.write(" el");
486 } else {
487 self.write(" else ");
488 }
489 self.visit_expr(expr);
490 }
491 }
492 ExprKind::Index(array, index) => {
493 self.visit_expr(array);
494 self.write("[");
495 self.visit_expr(index);
496 self.write("]");
497 }
498 ExprKind::Interpolate(components) => {
499 self.write("$\"");
500 for component in components.as_ref() {
501 match component {
502 StringComponent::Expr(expr) => {
503 self.write("{");
504 self.visit_expr(expr.as_ref());
505 self.write("}");
506 }
507 StringComponent::Lit(lit) => {
508 self.write(lit);
509 }
510 }
511 }
512 self.write("\"");
513 }
514 ExprKind::Lambda(kind, pat, expr) => {
515 self.visit_pat(pat);
516 match kind {
517 CallableKind::Function => self.write(" -> "),
518 CallableKind::Operation => self.write(" => "),
519 }
520 self.visit_expr(expr);
521 }
522 ExprKind::Paren(expr) => {
523 self.write("(");
524 self.visit_expr(expr);
525 self.write(")");
526 }
527 ExprKind::Return(expr) => {
528 self.write("return ");
529 self.visit_expr(expr);
530 }
531 ExprKind::Struct(name, copy, assigns) => {
532 self.write("new ");
533 self.visit_path(name);
534 self.writeln(" {");
535 if let Some(copy) = copy {
536 self.write("...");
537 self.visit_expr(copy);
538 if !assigns.is_empty() {
539 self.writeln(",");
540 }
541 }
542 if let Some((last, most)) = assigns.split_last() {
543 for assign in most {
544 self.visit_field_assign(assign);
545 self.writeln(",");
546 }
547 self.visit_field_assign(last);
548 self.writeln("");
549 }
550 self.writeln("}");
551 }
552 ExprKind::UnOp(op, expr) => {
553 let op_str = unop_as_str(op);
554 if op == &UnOp::Unwrap {
555 self.visit_expr(expr);
556 self.write(op_str);
557 } else {
558 self.write(op_str);
559 self.visit_expr(expr);
560 }
561 }
562 ExprKind::Path(path) => self.visit_path(path),
563 ExprKind::Range(start, step, end) => {
564 // A range: `start..step..end`, `start..end`, `start...`, `...end`, or `...`.
565 match (start, step, end) {
566 (None, None, None) => {
567 self.write("...");
568 }
569 (None, None, Some(end)) => {
570 self.write("...");
571 self.visit_expr(end);
572 }
573 (None, Some(step), None) => {
574 self.write("...");
575 self.visit_expr(step);
576 self.write("...");
577 }
578 (None, Some(step), Some(end)) => {
579 self.write("...");
580 self.visit_expr(step);
581 self.write("..");
582 self.visit_expr(end);
583 }
584 (Some(start), None, None) => {
585 self.visit_expr(start);
586 self.write("...");
587 }
588 (Some(start), None, Some(end)) => {
589 self.visit_expr(start);
590 self.write("..");
591 self.visit_expr(end);
592 }
593 (Some(start), Some(step), None) => {
594 self.visit_expr(start);
595 self.write("..");
596 self.visit_expr(step);
597 self.write("...");
598 }
599 (Some(start), Some(step), Some(end)) => {
600 self.visit_expr(start);
601 self.write("..");
602 self.visit_expr(step);
603 self.write("..");
604 self.visit_expr(end);
605 }
606 }
607 }
608 ExprKind::Repeat(body, until, fixup) => {
609 self.write("repeat ");
610 self.visit_block(body);
611 self.write("until ");
612 self.visit_expr(until);
613 if let Some(fixup) = fixup {
614 self.write(" fixup ");
615 self.visit_block(fixup);
616 }
617 }
618 ExprKind::TernOp(op, e1, e2, e3) => {
619 match op {
620 TernOp::Cond => {
621 // Conditional: `a ? b | c`.
622 self.visit_expr(e1);
623 self.write(" ? ");
624 self.visit_expr(e2);
625 self.write(" | ");
626 self.visit_expr(e3);
627 }
628 TernOp::Update => {
629 // Aggregate update: `a w/ b <- c`.
630 self.visit_expr(e1);
631 self.write(" w/ ");
632 self.visit_expr(e2);
633 self.write(" <- ");
634 self.visit_expr(e3);
635 }
636 }
637 }
638 ExprKind::Tuple(exprs) => {
639 self.write("(");
640 if let Some((last, most)) = exprs.split_last() {
641 for e in most {
642 self.visit_expr(e);
643 self.write(", ");
644 }
645 self.visit_expr(last);
646 self.ensure_trailing_comma_for_arity_one_tuples(most);
647 }
648 self.write(")");
649 }
650 ExprKind::While(cond, block) => {
651 self.write("while ");
652 self.visit_expr(cond);
653 self.visit_block(block);
654 }
655 ExprKind::Lit(lit) => match lit.as_ref() {
656 Lit::BigInt(value) => {
657 self.write(value.to_string().as_str());
658 self.write("L");
659 }
660 Lit::Bool(value) => {
661 if *value {
662 self.write("true");
663 } else {
664 self.write("false");
665 }
666 }
667 Lit::Double(value) => {
668 let num_str = if value.fract() == 0.0 {
669 format!("{value}.")
670 } else {
671 format!("{value}")
672 };
673 self.write(&num_str);
674 }
675 Lit::Int(value) => self.write(&value.to_string()),
676 Lit::Pauli(value) => match value {
677 Pauli::I => self.write("PauliI"),
678 Pauli::X => self.write("PauliX"),
679 Pauli::Y => self.write("PauliY"),
680 Pauli::Z => self.write("PauliZ"),
681 },
682 Lit::Result(value) => match value {
683 ast::Result::One => self.write("One"),
684 ast::Result::Zero => self.write("Zero"),
685 },
686 Lit::String(value) => {
687 self.write("\"");
688 self.write(value.as_ref());
689 self.write("\"");
690 }
691 },
692 ExprKind::Hole => {
693 self.write("_");
694 }
695 ExprKind::Err => {
696 unreachable!();
697 }
698 }
699 }
700
701 fn visit_field_assign(&mut self, assign: &'_ ast::FieldAssign) {
702 self.visit_ident(&assign.field);
703 self.write(" = ");
704 self.visit_expr(&assign.value);
705 }
706
707 fn visit_pat(&mut self, pat: &'_ Pat) {
708 match &*pat.kind {
709 PatKind::Bind(name, ty) => {
710 self.visit_ident(name);
711
712 if let Some(t) = ty {
713 self.write(": ");
714 self.visit_ty(t);
715 }
716 }
717 PatKind::Discard(ty) => {
718 self.write("_");
719 if let Some(t) = ty {
720 self.write(": ");
721 self.visit_ty(t);
722 }
723 }
724 PatKind::Elided => {
725 self.write("...");
726 }
727 PatKind::Paren(pat) => {
728 self.write("(");
729 self.visit_pat(pat);
730 self.write(")");
731 }
732 PatKind::Tuple(pats) => {
733 self.write("(");
734 if let Some((last, most)) = pats.split_last() {
735 for pat in most {
736 self.visit_pat(pat);
737 self.write(", ");
738 }
739 self.visit_pat(last);
740 self.ensure_trailing_comma_for_arity_one_tuples(most);
741 }
742 self.write(")");
743 }
744 PatKind::Err => {
745 unreachable!();
746 }
747 }
748 }
749
750 fn visit_qubit_init(&mut self, init: &'_ QubitInit) {
751 match &*init.kind {
752 QubitInitKind::Array(len) => {
753 self.write("Qubit[");
754 self.visit_expr(len);
755 self.write("]");
756 }
757 QubitInitKind::Paren(init) => self.visit_qubit_init(init),
758 QubitInitKind::Single => {
759 self.write("Qubit()");
760 }
761 QubitInitKind::Tuple(inits) => {
762 self.write("(");
763 if let Some((last, most)) = inits.split_last() {
764 for init in most {
765 self.visit_qubit_init(init);
766 self.write(", ");
767 }
768 self.visit_qubit_init(last);
769 self.ensure_trailing_comma_for_arity_one_tuples(most);
770 }
771 self.write(")");
772 }
773 QubitInitKind::Err => unreachable!(),
774 }
775 }
776
777 fn visit_path(&mut self, path: &'_ Path) {
778 if let Some(parts) = &path.segments {
779 self.visit_idents(parts);
780 self.write(".");
781 }
782 self.visit_ident(&path.name);
783 }
784
785 fn visit_ident(&mut self, id: &'_ Ident) {
786 self.write(&id.name);
787 }
788
789 fn visit_idents(&mut self, idents: &'_ Idents) {
790 self.write(&idents.name());
791 }
792}
793
794fn binop_as_str(op: &BinOp) -> &str {
795 match op {
796 BinOp::Add => "+",
797 BinOp::AndB => "&&&",
798 BinOp::AndL => "and",
799 BinOp::Div => "/",
800 BinOp::Eq => "==",
801 BinOp::Exp => "^",
802 BinOp::Gt => ">",
803 BinOp::Gte => ">=",
804 BinOp::Lt => "<",
805 BinOp::Lte => "<=",
806 BinOp::Mod => "%",
807 BinOp::Mul => "*",
808 BinOp::Neq => "!=",
809 BinOp::OrB => "|||",
810 BinOp::OrL => "or",
811 BinOp::Shl => "<<<",
812 BinOp::Shr => ">>>",
813 BinOp::Sub => "-",
814 BinOp::XorB => "^^^",
815 }
816}
817
818fn unop_as_str(op: &UnOp) -> &str {
819 match op {
820 UnOp::Functor(functor) => match functor {
821 Functor::Adj => "Adjoint ",
822 Functor::Ctl => "Controlled ",
823 },
824 UnOp::Neg => "-",
825 UnOp::NotB => "~~~",
826 UnOp::NotL => "not ",
827 UnOp::Pos => "+",
828 UnOp::Unwrap => "!",
829 }
830}
831