From b7d6bb0095d0eaa910991006c409229dfef4cdae Mon Sep 17 00:00:00 2001 From: Avril Date: Sat, 6 Mar 2021 04:37:30 +0000 Subject: [PATCH] begin working on command implementation for REPL --- src/ext.rs | 59 +++++++++++++++ src/info/repl.rs | 5 ++ src/info/repl/command.rs | 155 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 219 insertions(+) create mode 100644 src/info/repl/command.rs diff --git a/src/ext.rs b/src/ext.rs index d6be313..90ab111 100644 --- a/src/ext.rs +++ b/src/ext.rs @@ -18,6 +18,8 @@ pub mod prelude pub use super::StreamGateExt as _; pub use super::StreamLagExt as _; pub use super::INodeExt as _; + + pub use super::OptionIterator; pub use super::MaybeVec; } @@ -203,6 +205,63 @@ where S: Stream } } +/// An iterator that can be constructed from an `Option`. +#[derive(Debug, Clone)] +pub struct OptionIterator(Option); + +impl OptionIterator +{ + /// Consume into the inner `Option`. + #[inline] pub fn into_inner(self) -> Option + { + self.0 + } + + /// Does this `OptionIterator` have a value? + pub fn is_some(&self) -> bool + { + self.0.is_some() + } +} + +impl From> for OptionIterator +{ + fn from(from: Option) -> Self + { + Self(from) + } +} + + +impl Iterator for OptionIterator +{ + type Item = I::Item; + fn next(&mut self) -> Option + { + self.0.map(|x| x.next()).flatten() + } + + fn size_hint(&self) -> (usize, Option) { + match self.0 { + Some(i) => i.size_hint(), + _ => (0, Some(0)), + } + } +} + +impl std::iter::FusedIterator for OptionIterator +where I: std::iter::FusedIterator{} +impl ExactSizeIterator for OptionIterator +where I: ExactSizeIterator{} +impl DoubleEndedIterator for OptionIterator +where I: DoubleEndedIterator +{ + fn next_back(&mut self) -> Option { + self.0.map(|x| x.next_back()).flatten() + } +} + + /// Create a duration with time suffix `h`, `m`, `s`, `ms` or `ns`. /// /// # Combination diff --git a/src/info/repl.rs b/src/info/repl.rs index d72070b..727741a 100644 --- a/src/info/repl.rs +++ b/src/info/repl.rs @@ -7,6 +7,8 @@ use std::io; use rustyline::error::ReadlineError; use rustyline::Editor; +mod command; + /// Default history file name /// /// # Path lookup @@ -36,6 +38,9 @@ pub fn calculate_history_path() -> io::Result> } /// Inspect the graph with commands +/// +/// # Note +/// This function synchronously blocks the current thread. pub fn inspect(cfg: &Config, graph: &HierarchicalINodeGraph) -> Result<(), ReplExitError> { let mut repl = Editor::<()>::new(); //TODO: Change `()` to our completer, when we have a decent idea of how they'll work. diff --git a/src/info/repl/command.rs b/src/info/repl/command.rs new file mode 100644 index 0000000..af065b6 --- /dev/null +++ b/src/info/repl/command.rs @@ -0,0 +1,155 @@ +//! Repl commands +use super::*; +use std::str::FromStr; +use std::collections::{BTreeMap, HashMap}; + +#[derive(Debug)] +pub struct Lexenv +{ + /// Maps symbol name to value in generations. + kvstack: BTreeMap>, + /// Current generation of the satck + current_generation: usize, +} +impl Lexenv +{ + /// The generation number of the current level. + pub fn depth(&self) -> usize + { + self.current_generation + } + /// Create a new empty lexenv + pub fn new() -> Self + { + Self { + kvstack: BTreeMap::new(), + current_generation: 0, + } + } + /// All valid symbols at this level. + /// + /// # Ordering + /// Each symbol's level will appear in the order from level 0 to the current level, however the order of intra-level symbols is undefined. + pub fn symbols(&self) -> impl Iterator + '_ + { + self.kvstack.range(0..=self.current_generation).flat_map(|(_, v)| v.values()) + } + + /// All valid symbols **in** this level. + pub fn symbols_local(&self) -> impl Iterator + '_ + { + OptionIterator::from(self.kvstack.get(&self.current_generation).map(|x| x.values())) + } + + /// Remove the current level, but leave its memory allocated for further use. + pub fn pop(&mut self) + { + self.kvstack.entry(self.current_generation).or_insert_with(|| HashMap::new()).clear(); + if self.current_generation > 0 { + self.current_generation-=1; + } + } + + /// Remove a symbol from the **current** level. + pub fn remove(&mut self, name: &str) -> Option + { + self.kvstack.entry(self.current_generation).or_insert_with(|| HashMap::new()).remove(name) + } + + /// Insert a new value mapping into the current level. + pub fn insert(&mut self, name: String, value: Value) + { + self.kvstack.entry(self.current_generation).or_insert_with(|| HashMap::new()).insert(name, value); + } + + /// Look up a symbol in this or any of the above levels. + pub fn lookup(&self, name: &str) -> Option<&Value> + { + for (_, lvmap) in self.kvstack.range(0..=self.current_generation).rev() + { + let m = lvmap.get(name); + if m.is_some() { + return m; + } + } + None + } + + /// Look up a symbol in this level. + pub fn lookup_local(&self, name: &str) -> Option<&Value> + { + self.kvstack.get(&self.current_generation).map(|map| map.get(name)).flatten() + } + + /// Create a new, empty level. + pub fn push(&mut self) + { + self.current_generation+=1; + } + + /// Remove the current level, deallocating any memory it was using. + pub fn pop_clear(&mut self) + { + if self.current_generation > 0 { + self.kvstack.remove(&self.current_generation); + self.current_generation -=1; + } else { + self.kvstack.entry(0).or_insert_with(|| HashMap::new()).clear(); + } + } +} + +#[derive(Debug)] +pub struct Context<'a> +{ + /// Environment containing variable name mappings. + env: &'a mut Lexenv, +} + +#[derive(Debug)] +pub enum Value +{ + String(String), + Symbol(String), + List(Vec), + + Command(Box), +} + +pub trait Command: fmt::Debug +{ + fn execute(self: Box, cx: &mut Context<'_>, params: Vec) -> eyre::Result<()>; +} + +/// Command structurally parsed. +/// +/// Can be converted into `Command` with the `TryInto` trait. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct IR +{ + op: String, + params: Vec, +} + +impl FromStr for IR +{ + type Err = CommandParseError; + + fn from_str(s: &str) -> Result { + todo!() + } +} + +/// Error when parsing a command into `IR`. +#[derive(Debug)] +pub struct CommandParseError(String); + +impl error::Error for CommandParseError{} + +impl fmt::Display for CommandParseError +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { + write!(f, "failed to parse command from {:?}", self.0) + } +}