microsoft/qdk

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
5360d424b9fc901476a87a7f4db2e6d8b922972b

Branches

Tags

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

Clone

HTTPS

Download ZIP

compiler/qsc_codegen/src/qsharp.rs

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