1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212
//! The REPL API. //! //! The REPL uses a state machine to control what methods can be applied to it. mod any_state; mod data; mod eval; mod print; mod read; use crate::{ cmds::CommandResult, code::{ModsMap, StaticFile, StaticFiles}, input::InputResult, linking::{self, LinkingConfiguration}, output::{self, Output}, }; use cmdtree::*; use colored::*; use crossbeam_channel::Receiver; use kserd::Kserd; use std::{ borrow::Cow, collections::VecDeque, fmt, fs, io, marker::PhantomData, path::{Path, PathBuf}, }; /// The repl structure. Stored as a state machine. /// See the [module level documentation] for more information. /// /// A repl has different available methods depending on its state. pub struct Repl<S, Data> { /// The inner repl configuration data. pub data: ReplData<Data>, state: S, /// A persistent flag for the prompt to change for more input. more: bool, data_mrker: PhantomData<Data>, } /// The inner configuration data of the repl. pub struct ReplData<Data> { /// The REPL commands as a `cmdtree::Commander`. pub cmdtree: Commander<CommandResult<Data>>, /// The modules map of relative paths. pub(crate) mods_map: ModsMap, /// The current editing and executing mod. pub(crate) current_mod: PathBuf, /// The colour of the prompt region. ie `papyrus`. pub prompt_colour: Color, /// The colour of the out component. ie `[out0]`. pub out_colour: Color, /// The directory for which compilation is done within. /// Defaults to `$HOME/.papyrus/`. compilation_dir: PathBuf, /// The external crate linking configuration, linking: LinkingConfiguration, /// Flag for editing a statement, item, or crate. /// /// If a value is set when an evaluation starts, the input buffer /// will be used to overwrite the element at the given index (if it exists). /// Compilation and evaluation could both fail, but the change _will not be reverted_. /// /// If the index is outside the array bounds then there will be no change. Evaluation /// phase will still run. /// /// [`read()`]: Repl::read pub editing: Option<EditingIndex>, /// The rust source code as a string which is being edited. /// /// This is helpful if an alteration has been requested and you want to /// show the old source code. It is recommended to `.take()` the value /// to avoid repeating the contents. pub editing_src: Option<String>, /// Store of static files written to disk and to be included in REPL cycle. static_files: StaticFiles, /// Stored loaded libraries of the papyrus mem code. loadedlibs: VecDeque<Box<libloading::Library>>, /// Limit the number of loaded libraries that are kept in memory and not dropped. /// /// There exists a use pattern which can create segmentation faults if code defined in the /// library is called once the library goes out of scope and is dropped. Detailed in /// [#44](https://github.com/kurtlawrence/papyrus/issues/44). /// /// Libraries are returned on a successful execution and stored in a vector. Once the vector /// reaches the size limit, the _oldest_ library is removed and dropped, freeing resources. /// /// The default is to keep the size limit at zero, thus ensuring no libraries are kept in /// memory. This is recommended unless issues are arising from esoteric use cases. pub loaded_libs_size_limit: usize, } /// Repl read state. #[derive(Debug)] pub struct Read { output: Output<output::Read>, } /// Repl evaluate state. #[derive(Debug)] pub struct Evaluate { output: Output<output::Write>, result: InputResult, } /// Repl evaluating state. This can be constructed via a `eval_async` call. pub struct Evaluating<D> { jh: Receiver<EvalResult<D>>, } /// Repl print state. #[derive(Debug)] pub struct Print { output: Output<output::Write>, data: EvalOutput, } /// Was the eval something that produces data?? #[derive(Debug)] enum EvalOutput { /// If there is data, then it should be prefixed with `[out#]`. Data(Kserd<'static>), Print(Cow<'static, str>), } /// Represents an evaluating result. Signal should be checked and handled. pub struct EvalResult<D> { /// The repl, in print ready state. pub repl: Repl<Print, D>, /// The signal, if any. pub signal: Signal, } /// Return signals from evaluating. /// Sometimes there are extra signals that result from evaluating, /// such as the signal to exit the repl. These signals are enumerated here. #[derive(Debug, PartialEq)] pub enum Signal { /// No signal was sent. None, /// A signal to exit the repl has been sent. Exit, /// Signal to run the evaluation loop again with the inner /// value as the line input. /// /// This is usually signaled when [`EditReplace`] is instigated. /// Re-evaulation is signalled rather than handled as the input /// may be not enough to complete a full repl cycle. /// /// [`EditReplace`]: super::cmds::CommandResult ReEvaluate(String), } /// Result of [`read`]ing the current input buffer. /// /// [`read`]: Repl::read pub enum ReadResult<D> { /// The repl is still in a read state. Read(Repl<Read, D>), /// The repl is in an eval state. Eval(Repl<Evaluate, D>), } /// The index of the statement group, item, or crate being edited. #[derive(Copy, Clone, Debug)] pub struct EditingIndex { /// Type being edited. pub editing: Editing, /// Index. pub index: usize, } /// Type being edited #[derive(Copy, Clone, Debug)] pub enum Editing { /// A statement group, corresponding to the `out#`. Stmt, /// An item, such as fn or struct. Item, /// A crate. Crate, } /// `$HOME/.papyrus` fn default_compile_dir() -> PathBuf { dirs::home_dir().unwrap_or_default().join(".papyrus/") } #[test] fn test_default_compile_dir() { let dir = default_compile_dir(); println!("{}", dir.display()); assert!(dir.ends_with(".papyrus/")); if cfg!(windows) { assert!(dir.starts_with("C:\\Users\\")); } else if cfg!(target_os = "macos") { assert!(dir.starts_with("/Users/")); } else { assert!(dir.starts_with("/home/")); } }