microsoft/qdk

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
7b5efb8550a7234f02dded061311bbdff3ccfff8

Branches

Tags

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

Clone

HTTPS

Download ZIP

compiler/qsc_codegen/src/qsharp.rs

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