diff --git a/compiler/erg_common/build.rs b/compiler/erg_common/build.rs index 38e0df91..c4f10cb8 100644 --- a/compiler/erg_common/build.rs +++ b/compiler/erg_common/build.rs @@ -1,5 +1,4 @@ use std::process::Command; -// use std::io::Write; mod datetime; diff --git a/compiler/erg_common/combinations.rs b/compiler/erg_common/combinations.rs deleted file mode 100644 index 1194ba2f..00000000 --- a/compiler/erg_common/combinations.rs +++ /dev/null @@ -1,220 +0,0 @@ -// Copied and modified from https://github.com/rust-itertools/itertools/blob/master/src/combinations.rs -// and https://github.com/rust-itertools/itertools/blob/master/src/impl_macros.rs -// MIT license | Apache 2.0 license -// License files are placed at the root. - -use std::fmt; -use std::iter::FusedIterator; - -use super::lazy_buffer::LazyBuffer; - -pub struct TotalCombinations { - combinations: Combinations, - len: usize, -} - -/// ``` -/// let it = total_combinations(0..2); -/// itertools::assert_equal(it, vec![ -/// vec![0], -/// vec![1], -/// vec![2], -/// vec![0, 1], -/// vec![0, 2], -/// vec![1, 2], -/// vec![0, 1, 2], -/// ]); -/// -pub fn total_combinations(iter: I) -> TotalCombinations -where - I: Iterator + ExactSizeIterator, - I::Item: Clone, -{ - TotalCombinations { - len: iter.len(), - combinations: combinations(iter, 1), - } -} - -impl Iterator for TotalCombinations -where - I: Iterator, - I::Item: Clone, -{ - type Item = Vec; - fn next(&mut self) -> Option { - if let Some(i) = self.combinations.next() { - Some(i) - } else { - self.combinations.reset(self.combinations.k() + 1); - self.len -= 1; - if self.len == 0 { - return None; - } - self.combinations.next() - } - } -} - -impl FusedIterator for TotalCombinations -where - I: Iterator, - I::Item: Clone, -{ -} - -macro_rules! debug_fmt_fields { - ($tyname:ident, $($($field:tt/*TODO ideally we would accept ident or tuple element here*/).+),*) => { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { - f.debug_struct(stringify!($tyname)) - $( - .field(stringify!($($field).+), &self.$($field).+) - )* - .finish() - } - } -} - -macro_rules! clone_fields { - ($($field:ident),*) => { - fn clone(&self) -> Self { - Self { - $($field: self.$field.clone(),)* - } - } - } -} - -/// An iterator to iterate through all the `k`-length combinations in an iterator. -/// -/// See [`.combinations()`](crate::Itertools::combinations) for more information. -#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] -pub struct Combinations { - indices: Vec, - pool: LazyBuffer, - first: bool, -} - -impl Clone for Combinations -where - I: Clone + Iterator, - I::Item: Clone, -{ - clone_fields!(indices, pool, first); -} - -impl fmt::Debug for Combinations -where - I: Iterator + fmt::Debug, - I::Item: fmt::Debug, -{ - debug_fmt_fields!(Combinations, indices, pool, first); -} - -/// Create a new `Combinations` from a clonable iterator. -pub fn combinations(iter: I, k: usize) -> Combinations -where - I: Iterator, -{ - let mut pool = LazyBuffer::new(iter); - pool.prefill(k); - - Combinations { - indices: (0..k).collect(), - pool, - first: true, - } -} - -impl Combinations { - /// Returns the length of a combination produced by this iterator. - #[inline] - pub fn k(&self) -> usize { - self.indices.len() - } - - /// Returns the (current) length of the pool from which combination elements are - /// selected. This value can change between invocations of [`next`](Combinations::next). - #[inline] - pub fn n(&self) -> usize { - self.pool.len() - } - - /// Returns a reference to the source iterator. - #[inline] - pub fn src(&self) -> &I { - &self.pool.it - } - - /// Resets this `Combinations` back to an initial state for combinations of length - /// `k` over the same pool data source. If `k` is larger than the current length - /// of the data pool an attempt is made to prefill the pool so that it holds `k` - /// elements. - pub fn reset(&mut self, k: usize) { - self.first = true; - - if k < self.indices.len() { - self.indices.truncate(k); - for i in 0..k { - self.indices[i] = i; - } - } else { - for i in 0..self.indices.len() { - self.indices[i] = i; - } - self.indices.extend(self.indices.len()..k); - self.pool.prefill(k); - } - } -} - -impl Iterator for Combinations -where - I: Iterator, - I::Item: Clone, -{ - type Item = Vec; - fn next(&mut self) -> Option { - if self.first { - if self.k() > self.n() { - return None; - } - self.first = false; - } else if self.indices.is_empty() { - return None; - } else { - // Scan from the end, looking for an index to increment - let mut i: usize = self.indices.len() - 1; - - // Check if we need to consume more from the iterator - if self.indices[i] == self.pool.len() - 1 { - self.pool.get_next(); // may change pool size - } - - while self.indices[i] == i + self.pool.len() - self.indices.len() { - if i > 0 { - i -= 1; - } else { - // Reached the last combination - return None; - } - } - - // Increment index, and reset the ones to its right - self.indices[i] += 1; - for j in i + 1..self.indices.len() { - self.indices[j] = self.indices[j - 1] + 1; - } - } - - // Create result vector based on the indices - Some(self.indices.iter().map(|i| self.pool[*i].clone()).collect()) - } -} - -impl FusedIterator for Combinations -where - I: Iterator, - I::Item: Clone, -{ -} diff --git a/compiler/erg_common/config.rs b/compiler/erg_common/config.rs index 0aa4562a..cdd23adb 100644 --- a/compiler/erg_common/config.rs +++ b/compiler/erg_common/config.rs @@ -2,12 +2,12 @@ //! //! コマンドオプション(パーサー)を定義する use std::env; -use std::env::consts::{ARCH, OS}; +// use std::env::consts::{ARCH, OS}; use std::fs::File; use std::io::{BufRead, BufReader, Read, stdin}; use std::process; -use crate::lazy::Lazy; +// use crate::lazy::Lazy; use crate::stdin; use crate::Str; use crate::{power_assert, read_file}; @@ -15,9 +15,6 @@ use crate::{power_assert, read_file}; pub const SEMVER: &str = env!("CARGO_PKG_VERSION"); pub const GIT_HASH_SHORT: &str = env!("GIT_HASH_SHORT"); pub const BUILD_DATE: &str = env!("BUILD_DATE"); -/// TODO: タグを含める -pub const BUILD_INFO: Lazy = - Lazy::new(|| format!("(tags/?:{GIT_HASH_SHORT}, {BUILD_DATE}) on {ARCH}/{OS}")); /// 入力はファイルからだけとは限らないので /// Inputで操作を一本化する diff --git a/compiler/erg_common/lazy.rs b/compiler/erg_common/lazy.rs deleted file mode 100644 index 26c213c1..00000000 --- a/compiler/erg_common/lazy.rs +++ /dev/null @@ -1,374 +0,0 @@ -// Copied and modified from https://github.com/rust-lang/rust/blob/master/library/core/src/lazy.rs -// and https://github.com/rust-lang/rust/blob/master/library/core/src/mem/maybe_uninit.rs -// MIT license | Apache 2.0 license -// License files are placed at the root. - -//! Lazy values and one-time initialization of static data. - -use std::cell::{Cell, UnsafeCell}; -use std::fmt; -use std::mem; -use std::ops::Deref; - -/// A cell which can be written to only once. -/// -/// Unlike `RefCell`, a `OnceCell` only provides shared `&T` references to its value. -/// Unlike `Cell`, a `OnceCell` doesn't require copying or replacing the value to access it. -/// -/// # Examples -/// -/// ``` -/// #![feature(once_cell)] -/// -/// use std::lazy::OnceCell; -/// -/// let cell = OnceCell::new(); -/// assert!(cell.get().is_none()); -/// -/// let value: &String = cell.get_or_init(|| { -/// "Hello, World!".to_string() -/// }); -/// assert_eq!(value, "Hello, World!"); -/// assert!(cell.get().is_some()); -/// ``` -pub struct OnceCell { - // Invariant: written to at most once. - inner: UnsafeCell>, -} - -impl Default for OnceCell { - fn default() -> Self { - Self::new() - } -} - -impl fmt::Debug for OnceCell { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.get() { - Some(v) => f.debug_tuple("OnceCell").field(v).finish(), - None => f.write_str("OnceCell(Uninit)"), - } - } -} - -impl Clone for OnceCell { - fn clone(&self) -> OnceCell { - let res = OnceCell::new(); - if let Some(value) = self.get() { - match res.set(value.clone()) { - Ok(()) => (), - Err(_) => unreachable!(), - } - } - res - } -} - -impl PartialEq for OnceCell { - fn eq(&self, other: &Self) -> bool { - self.get() == other.get() - } -} - -impl Eq for OnceCell {} - -impl From for OnceCell { - fn from(value: T) -> Self { - OnceCell { - inner: UnsafeCell::new(Some(value)), - } - } -} - -impl OnceCell { - /// Creates a new empty cell. - pub const fn new() -> OnceCell { - OnceCell { - inner: UnsafeCell::new(None), - } - } - - /// Gets the reference to the underlying value. - /// - /// Returns `None` if the cell is empty. - pub fn get(&self) -> Option<&T> { - // SAFETY: Safe due to `inner`'s invariant - unsafe { &*self.inner.get() }.as_ref() - } - - /// Gets the mutable reference to the underlying value. - /// - /// Returns `None` if the cell is empty. - pub fn get_mut(&mut self) -> Option<&mut T> { - // SAFETY: Safe because we have unique access - unsafe { &mut *self.inner.get() }.as_mut() - } - - /// Sets the contents of the cell to `value`. - /// - /// # Errors - /// - /// This method returns `Ok(())` if the cell was empty and `Err(value)` if - /// it was full. - /// - /// # Examples - /// - /// ``` - /// #![feature(once_cell)] - /// - /// use std::lazy::OnceCell; - /// - /// let cell = OnceCell::new(); - /// assert!(cell.get().is_none()); - /// - /// assert_eq!(cell.set(92), Ok(())); - /// assert_eq!(cell.set(62), Err(62)); - /// - /// assert!(cell.get().is_some()); - /// ``` - pub fn set(&self, value: T) -> Result<(), T> { - // SAFETY: Safe because we cannot have overlapping mutable borrows - let slot = unsafe { &*self.inner.get() }; - if slot.is_some() { - return Err(value); - } - - // SAFETY: This is the only place where we set the slot, no races - // due to reentrancy/concurrency are possible, and we've - // checked that slot is currently `None`, so this write - // maintains the `inner`'s invariant. - let slot = unsafe { &mut *self.inner.get() }; - *slot = Some(value); - Ok(()) - } - - /// Gets the contents of the cell, initializing it with `f` - /// if the cell was empty. - /// - /// # Panics - /// - /// If `f` panics, the panic is propagated to the caller, and the cell - /// remains uninitialized. - /// - /// It is an error to reentrantly initialize the cell from `f`. Doing - /// so results in a panic. - /// - /// # Examples - /// - /// ``` - /// #![feature(once_cell)] - /// - /// use std::lazy::OnceCell; - /// - /// let cell = OnceCell::new(); - /// let value = cell.get_or_init(|| 92); - /// assert_eq!(value, &92); - /// let value = cell.get_or_init(|| unreachable!()); - /// assert_eq!(value, &92); - /// ``` - pub fn get_or_init(&self, f: F) -> &T - where - F: FnOnce() -> T, - { - match self.get_or_try_init(|| Ok::(f())) { - Ok(val) => val, - Err(()) => unreachable!(), - } - } - - /// Gets the contents of the cell, initializing it with `f` if - /// the cell was empty. If the cell was empty and `f` failed, an - /// error is returned. - /// - /// # Panics - /// - /// If `f` panics, the panic is propagated to the caller, and the cell - /// remains uninitialized. - /// - /// It is an error to reentrantly initialize the cell from `f`. Doing - /// so results in a panic. - /// - /// # Examples - /// - /// ``` - /// #![feature(once_cell)] - /// - /// use std::lazy::OnceCell; - /// - /// let cell = OnceCell::new(); - /// assert_eq!(cell.get_or_try_init(|| Err(())), Err(())); - /// assert!(cell.get().is_none()); - /// let value = cell.get_or_try_init(|| -> Result { - /// Ok(92) - /// }); - /// assert_eq!(value, Ok(&92)); - /// assert_eq!(cell.get(), Some(&92)) - /// ``` - pub fn get_or_try_init(&self, f: F) -> Result<&T, E> - where - F: FnOnce() -> Result, - { - if let Some(val) = self.get() { - return Ok(val); - } - let val = f()?; - // Note that *some* forms of reentrant initialization might lead to - // UB (see `reentrant_init` test). I believe that just removing this - // `assert`, while keeping `set/get` would be sound, but it seems - // better to panic, rather than to silently use an old value. - assert!(self.set(val).is_ok(), "reentrant init"); - Ok(self.get().unwrap()) - } - - /// Consumes the cell, returning the wrapped value. - /// - /// Returns `None` if the cell was empty. - /// - /// # Examples - /// - /// ``` - /// #![feature(once_cell)] - /// - /// use std::lazy::OnceCell; - /// - /// let cell: OnceCell = OnceCell::new(); - /// assert_eq!(cell.into_inner(), None); - /// - /// let cell = OnceCell::new(); - /// cell.set("hello".to_string()).unwrap(); - /// assert_eq!(cell.into_inner(), Some("hello".to_string())); - /// ``` - pub fn into_inner(self) -> Option { - // Because `into_inner` takes `self` by value, the compiler statically verifies - // that it is not currently borrowed. So it is safe to move out `Option`. - self.inner.into_inner() - } - - /// Takes the value out of this `OnceCell`, moving it back to an uninitialized state. - /// - /// Has no effect and returns `None` if the `OnceCell` hasn't been initialized. - /// - /// Safety is guaranteed by requiring a mutable reference. - /// - /// # Examples - /// - /// ``` - /// #![feature(once_cell)] - /// - /// use std::lazy::OnceCell; - /// - /// let mut cell: OnceCell = OnceCell::new(); - /// assert_eq!(cell.take(), None); - /// - /// let mut cell = OnceCell::new(); - /// cell.set("hello".to_string()).unwrap(); - /// assert_eq!(cell.take(), Some("hello".to_string())); - /// assert_eq!(cell.get(), None); - /// ``` - pub fn take(&mut self) -> Option { - mem::take(self).into_inner() - } -} - -/// A value which is initialized on the first access. -/// -/// # Examples -/// -/// ``` -/// #![feature(once_cell)] -/// -/// use std::lazy::Lazy; -/// -/// let lazy: Lazy = Lazy::new(|| { -/// println!("initializing"); -/// 92 -/// }); -/// println!("ready"); -/// println!("{}", *lazy); -/// println!("{}", *lazy); -/// -/// // Prints: -/// // ready -/// // initializing -/// // 92 -/// // 92 -/// ``` -pub struct Lazy T> { - cell: OnceCell, - init: Cell>, -} - -impl fmt::Debug for Lazy { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Lazy") - .field("cell", &self.cell) - .field("init", &"..") - .finish() - } -} - -impl Lazy { - /// Creates a new lazy value with the given initializing function. - /// - /// # Examples - /// - /// ``` - /// #![feature(once_cell)] - /// - /// # fn main() { - /// use std::lazy::Lazy; - /// - /// let hello = "Hello, World!".to_string(); - /// - /// let lazy = Lazy::new(|| hello.to_uppercase()); - /// - /// assert_eq!(&*lazy, "HELLO, WORLD!"); - /// # } - /// ``` - pub const fn new(init: F) -> Lazy { - Lazy { - cell: OnceCell::new(), - init: Cell::new(Some(init)), - } - } -} - -impl T> Lazy { - /// Forces the evaluation of this lazy value and returns a reference to - /// the result. - /// - /// This is equivalent to the `Deref` impl, but is explicit. - /// - /// # Examples - /// - /// ``` - /// #![feature(once_cell)] - /// - /// use std::lazy::Lazy; - /// - /// let lazy = Lazy::new(|| 92); - /// - /// assert_eq!(Lazy::force(&lazy), &92); - /// assert_eq!(&*lazy, &92); - /// ``` - pub fn force(this: &Lazy) -> &T { - this.cell.get_or_init(|| match this.init.take() { - Some(f) => f(), - None => panic!("`Lazy` instance has previously been poisoned"), - }) - } -} - -impl T> Deref for Lazy { - type Target = T; - fn deref(&self) -> &T { - Lazy::force(self) - } -} - -impl Default for Lazy { - /// Creates a new lazy value using `Default` as the initializing function. - fn default() -> Lazy { - Lazy::new(T::default) - } -} diff --git a/compiler/erg_common/lazy_buffer.rs b/compiler/erg_common/lazy_buffer.rs deleted file mode 100644 index 280b2244..00000000 --- a/compiler/erg_common/lazy_buffer.rs +++ /dev/null @@ -1,74 +0,0 @@ -// Copied and modified from https://github.com/rust-itertools/itertools/blob/master/src/lazy_buffer.rs -// MIT license | Apache 2.0 license -// License files are placed at the root. - -use std::ops::Index; - -#[derive(Debug, Clone)] -pub struct LazyBuffer { - pub it: I, - done: bool, - buffer: Vec, -} - -impl LazyBuffer -where - I: Iterator, -{ - pub fn new(it: I) -> LazyBuffer { - LazyBuffer { - it, - done: false, - buffer: Vec::new(), - } - } - - pub fn len(&self) -> usize { - self.buffer.len() - } - - pub fn is_empty(&self) -> bool { - self.buffer.is_empty() - } - - pub fn get_next(&mut self) -> bool { - if self.done { - return false; - } - let next_item = self.it.next(); - match next_item { - Some(x) => { - self.buffer.push(x); - true - } - None => { - self.done = true; - false - } - } - } - - pub fn prefill(&mut self, len: usize) { - let buffer_len = self.buffer.len(); - - if !self.done && len > buffer_len { - let delta = len - buffer_len; - - self.buffer.extend(self.it.by_ref().take(delta)); - self.done = self.buffer.len() < len; - } - } -} - -impl Index for LazyBuffer -where - I: Iterator, - I::Item: Sized, - Vec: Index, -{ - type Output = as Index>::Output; - - fn index(&self, _index: J) -> &Self::Output { - self.buffer.index(_index) - } -} diff --git a/compiler/erg_common/lib.rs b/compiler/erg_common/lib.rs index e55692ff..2c079faa 100644 --- a/compiler/erg_common/lib.rs +++ b/compiler/erg_common/lib.rs @@ -4,15 +4,12 @@ use std::fmt; pub mod cache; pub mod codeobj; pub mod color; -pub mod combinations; pub mod config; pub mod datetime; pub mod deserialize; pub mod dict; pub mod error; pub mod fxhash; -pub mod lazy; -pub mod lazy_buffer; pub mod levenshtein; pub mod macros; pub mod opcode; diff --git a/compiler/erg_common/traits.rs b/compiler/erg_common/traits.rs index 37798e09..2ce1b957 100644 --- a/compiler/erg_common/traits.rs +++ b/compiler/erg_common/traits.rs @@ -1,6 +1,7 @@ //! defines common traits used in the compiler. //! //! コンパイラ等で汎用的に使われるトレイトを定義する +use std::env::consts::{ARCH, OS}; use std::io::{stdout, BufWriter, Write}; use std::mem; use std::process; @@ -8,7 +9,7 @@ use std::slice::{Iter, IterMut}; use std::vec::IntoIter; use crate::color::{GREEN, RESET}; -use crate::config::{ErgConfig, Input}; +use crate::config::{ErgConfig, Input, SEMVER, GIT_HASH_SHORT, BUILD_DATE}; use crate::error::{ErrorDisplay, Location, MultiErrorDisplay}; use crate::ty::Type; use crate::Str; @@ -308,9 +309,12 @@ fn expect_block(src: &str) -> bool { pub trait Runnable: Sized { type Err: ErrorDisplay; type Errs: MultiErrorDisplay; + const NAME: &'static str; fn new(cfg: ErgConfig) -> Self; fn input(&self) -> &Input; - fn start_message(&self) -> String; + fn start_message(&self) -> String { + format!("{} {SEMVER} tags/?:{GIT_HASH_SHORT}, {BUILD_DATE}) on {ARCH}/{OS}\n", Self::NAME) + } fn finish(&mut self); // called when the :exit command is received. fn clear(&mut self); fn eval(&mut self, src: Str) -> Result; diff --git a/compiler/erg_compiler/compile.rs b/compiler/erg_compiler/compile.rs index a59a8405..beee924b 100644 --- a/compiler/erg_compiler/compile.rs +++ b/compiler/erg_compiler/compile.rs @@ -5,7 +5,7 @@ use std::path::Path; use erg_common::codeobj::{CodeObj, CodeObjFlags}; use erg_common::color::{GREEN, RESET}; -use erg_common::config::{ErgConfig, Input, BUILD_INFO, SEMVER}; +use erg_common::config::{ErgConfig, Input}; use erg_common::error::MultiErrorDisplay; use erg_common::log; use erg_common::traits::{Runnable, Stream}; @@ -104,6 +104,7 @@ pub struct Compiler { impl Runnable for Compiler { type Err = CompileError; type Errs = CompileErrors; + const NAME: &'static str = "Erg compiler"; fn new(cfg: ErgConfig) -> Self { Self { @@ -118,11 +119,6 @@ impl Runnable for Compiler { &self.cfg.input } - #[inline] - fn start_message(&self) -> String { - format!("Erg compiler {} {}\n", SEMVER, &*BUILD_INFO) - } - #[inline] fn finish(&mut self) {} diff --git a/compiler/erg_parser/lex.rs b/compiler/erg_parser/lex.rs index b2e3f612..456a2b68 100644 --- a/compiler/erg_parser/lex.rs +++ b/compiler/erg_parser/lex.rs @@ -20,6 +20,7 @@ pub struct LexerRunner { impl Runnable for LexerRunner { type Err = LexerRunnerError; type Errs = LexerRunnerErrors; + const NAME: &'static str = "Erg lexer"; #[inline] fn new(cfg: ErgConfig) -> Self { @@ -31,11 +32,6 @@ impl Runnable for LexerRunner { &self.cfg.input } - #[inline] - fn start_message(&self) -> String { - "Erg lexer\n".to_string() - } - #[inline] fn finish(&mut self) {} diff --git a/compiler/erg_parser/parse.rs b/compiler/erg_parser/parse.rs index 94e62560..621e8207 100644 --- a/compiler/erg_parser/parse.rs +++ b/compiler/erg_parser/parse.rs @@ -7,7 +7,7 @@ use std::mem; use erg_common::color::{GREEN, RED, RESET}; use erg_common::config::ErgConfig; -use erg_common::config::{Input, BUILD_INFO, SEMVER}; +use erg_common::config::{Input}; use erg_common::error::Location; use erg_common::set::Set; use erg_common::traits::Runnable; @@ -272,6 +272,7 @@ pub struct ParserRunner { impl Runnable for ParserRunner { type Err = ParserRunnerError; type Errs = ParserRunnerErrors; + const NAME: &'static str = "Erg parser"; #[inline] fn new(cfg: ErgConfig) -> Self { @@ -283,11 +284,6 @@ impl Runnable for ParserRunner { &self.cfg.input } - #[inline] - fn start_message(&self) -> String { - format!("Erg parser {} {}\n", SEMVER, &*BUILD_INFO) - } - #[inline] fn finish(&mut self) {} diff --git a/src/dummy.rs b/src/dummy.rs index 3541bd3c..570e8034 100644 --- a/src/dummy.rs +++ b/src/dummy.rs @@ -4,7 +4,7 @@ use std::net::TcpStream; use std::thread::sleep; use std::time::Duration; -use erg_common::config::{ErgConfig, Input, BUILD_INFO, SEMVER}; +use erg_common::config::{ErgConfig, Input}; use erg_common::python_util::{exec_py, exec_pyc}; use erg_common::str::Str; use erg_common::traits::Runnable; @@ -23,6 +23,7 @@ pub struct DummyVM { impl Runnable for DummyVM { type Err = CompileError; type Errs = CompileErrors; + const NAME: &'static str = "Erg interpreter"; fn new(cfg: ErgConfig) -> Self { let stream = if cfg.input.is_repl() { @@ -59,11 +60,6 @@ impl Runnable for DummyVM { &self.cfg.input } - #[inline] - fn start_message(&self) -> String { - format!("Erg interpreter {} {}\n", SEMVER, &*BUILD_INFO) - } - fn finish(&mut self) { if let Some(stream) = &mut self.stream { stream.write_all("exit".as_bytes()).unwrap();