begin working on command implementation for REPL

repl
Avril 3 years ago
parent e8d215a65f
commit b7d6bb0095
Signed by: flanchan
GPG Key ID: 284488987C31F630

@ -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<Iterator>`.
#[derive(Debug, Clone)]
pub struct OptionIterator<I>(Option<I>);
impl<I> OptionIterator<I>
{
/// Consume into the inner `Option`.
#[inline] pub fn into_inner(self) -> Option<I>
{
self.0
}
/// Does this `OptionIterator` have a value?
pub fn is_some(&self) -> bool
{
self.0.is_some()
}
}
impl<I: Iterator> From<Option<I>> for OptionIterator<I>
{
fn from(from: Option<I>) -> Self
{
Self(from)
}
}
impl<I: Iterator> Iterator for OptionIterator<I>
{
type Item = I::Item;
fn next(&mut self) -> Option<Self::Item>
{
self.0.map(|x| x.next()).flatten()
}
fn size_hint(&self) -> (usize, Option<usize>) {
match self.0 {
Some(i) => i.size_hint(),
_ => (0, Some(0)),
}
}
}
impl<I: Iterator> std::iter::FusedIterator for OptionIterator<I>
where I: std::iter::FusedIterator{}
impl<I: Iterator> ExactSizeIterator for OptionIterator<I>
where I: ExactSizeIterator{}
impl<I: Iterator> DoubleEndedIterator for OptionIterator<I>
where I: DoubleEndedIterator
{
fn next_back(&mut self) -> Option<Self::Item> {
self.0.map(|x| x.next_back()).flatten()
}
}
/// Create a duration with time suffix `h`, `m`, `s`, `ms` or `ns`.
///
/// # Combination

@ -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<Option<PathBuf>>
}
/// 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.

@ -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<usize, HashMap<String, Value>>,
/// 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<Item = &'_ Value> + '_
{
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<Item = &'_ Value> + '_
{
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<Value>
{
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<Value>),
Command(Box<dyn Command>),
}
pub trait Command: fmt::Debug
{
fn execute(self: Box<Self>, cx: &mut Context<'_>, params: Vec<Value>) -> 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<String>,
}
impl FromStr for IR
{
type Err = CommandParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
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)
}
}
Loading…
Cancel
Save