mirror of
https://codeberg.org/unspeaker/tengri.git
synced 2025-12-06 03:36:42 +01:00
wip: mrr
This commit is contained in:
parent
6c3a0964ec
commit
7271081fc9
10 changed files with 119 additions and 296 deletions
200
]
200
]
|
|
@ -1,200 +0,0 @@
|
|||
use crate::*;
|
||||
/// A collection of input bind.
|
||||
///
|
||||
/// Each contained layer defines a mapping from input event to command invocation
|
||||
/// over a given state. Furthermore, each layer may have an associated cond,
|
||||
/// so that only certain layers are active at a given time depending on state.
|
||||
#[derive(Debug, Default)] pub struct InputMap<I, T: Dsl>(std::collections::BTreeMap<I, T>);
|
||||
impl<I, T: Dsl> InputMap<I, T> {
|
||||
/// Create input layer collection from path to text file.
|
||||
pub fn from_path <P: Debug + AsRef<Path>> (path: P) -> Usually<Self> {
|
||||
if !exists(path.as_ref())? {
|
||||
return Err(format!("(e5) not found: {path:?}").into())
|
||||
}
|
||||
Self::from_source(read_and_leak(path)?)
|
||||
}
|
||||
/// Create input layer collection from string.
|
||||
pub fn from_source <S: AsRef<str>> (source: S) -> Usually<Self> {
|
||||
Self::from_dsl(CstIter::from(source.as_ref()))
|
||||
}
|
||||
/// Create input layer collection from DSL.
|
||||
pub fn from_dsl (dsl: impl Dsl) -> Usually<Self> {
|
||||
use DslVal::*;
|
||||
let mut input_map: Self = Self(Default::default());
|
||||
while let Exp(_, mut exp) = dsl.val() {
|
||||
match exp.nth(0).map(|x|x.val()) {
|
||||
Some(Str(path)) => {
|
||||
let path = PathBuf::from(path.as_ref());
|
||||
let module = InputMap::<I, T>::from_path(&path)?;
|
||||
for (key, val) in module.0.into_iter() {
|
||||
}
|
||||
},
|
||||
Some(Exp(_, expr)) if let Some(Sym(sym)) = expr.nth(0) => {
|
||||
//input_map.unconditional.push(expr);
|
||||
todo!("binding");
|
||||
},
|
||||
Some(Exp(_, expr)) if expr.nth(0).map(|x|x.val()) == Some(Key("if")) => {
|
||||
todo!("conditional binding");
|
||||
},
|
||||
_ => return Result::Err("invalid token in keymap".into()),
|
||||
}
|
||||
}
|
||||
Ok(input_map)
|
||||
}
|
||||
/// Evaluate the active layers for a given state,
|
||||
/// returning the command to be executed, if any.
|
||||
pub fn handle <S, O> (&self, state: &mut S, input: I) -> Perhaps<O> where
|
||||
S: DslInto<bool> + DslInto<O>,
|
||||
O: Command<S>
|
||||
{
|
||||
todo!();
|
||||
//let layers = self.0.as_slice();
|
||||
//for InputLayer { cond, bind } in layers.iter() {
|
||||
//let mut matches = true;
|
||||
//if let Some(cond) = cond {
|
||||
//matches = state.dsl_into(cond, ||format!("input: no cond").into())?;
|
||||
//}
|
||||
//if matches
|
||||
//&& let Some(exp) = bind.val().exp_head()
|
||||
//&& input.dsl_into(exp, ||format!("InputMap: input.eval(binding) failed").into())?
|
||||
//&& let Some(command) = state.try_dsl_into(exp)? {
|
||||
//return Ok(Some(command))
|
||||
//}
|
||||
//}
|
||||
Ok(None)
|
||||
}
|
||||
/*
|
||||
/// Create an input map with a single non-condal layer.
|
||||
/// (Use [Default::default] to get an empty map.)
|
||||
pub fn new (layer: DslVal<T::Str, T::Exp>) -> Self {
|
||||
Self::default().layer(layer)
|
||||
}
|
||||
/// Add layer, return `Self`.
|
||||
pub fn layer (mut self, layer: DslVal<T::Str, T::Exp>) -> Self {
|
||||
self.add_layer(layer); self
|
||||
}
|
||||
/// Add condal layer, return `Self`.
|
||||
pub fn layer_if (mut self, cond: DslVal<T::Str, T::Exp>, layer: DslVal<T::Str, T::Exp>) -> Self {
|
||||
self.add_layer_if(Some(cond), layer); self
|
||||
}
|
||||
/// Add layer, return `&mut Self`.
|
||||
pub fn add_layer (&mut self, layer: DslVal<T::Str, T::Exp>) -> &mut Self {
|
||||
self.add_layer_if(None, layer.into()); self
|
||||
}
|
||||
/// Add condal layer, return `&mut Self`.
|
||||
pub fn add_layer_if (&mut self, cond: Option<DslVal<T::Str, T::Exp>>, bind: DslVal<T::Str, T::Exp>) -> &mut Self {
|
||||
self.0.push(InputLayer { cond, bind });
|
||||
self
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
|
||||
//let mut keys = iter.unwrap();
|
||||
//let mut map = InputMap::default();
|
||||
//while let Some(token) = keys.next() {
|
||||
//if let Value::Exp(_, mut exp) = token.value {
|
||||
//let next = exp.next();
|
||||
//if let Some(Token { value: Value::Key(sym), .. }) = next {
|
||||
//match sym {
|
||||
//"layer" => {
|
||||
//if let Some(Token { value: Value::Str(path), .. }) = exp.peek() {
|
||||
//let path = base.as_ref().parent().unwrap().join(unquote(path));
|
||||
//if !std::fs::exists(&path)? {
|
||||
//return Err(format!("(e5) not found: {path:?}").into())
|
||||
//}
|
||||
//map.add_layer(read_and_leak(path)?.into());
|
||||
//print!("layer:\n path: {:?}...", exp.0.0.trim());
|
||||
//println!("ok");
|
||||
//} else {
|
||||
//return Err(format!("(e4) unexpected non-string {next:?}").into())
|
||||
//}
|
||||
//},
|
||||
|
||||
//"layer-if" => {
|
||||
//let mut cond = None;
|
||||
|
||||
//if let Some(Token { value: Value::Sym(sym), .. }) = exp.next() {
|
||||
//cond = Some(leak(sym));
|
||||
//} else {
|
||||
//return Err(format!("(e4) unexpected non-symbol {next:?}").into())
|
||||
//};
|
||||
|
||||
//if let Some(Token { value: Value::Str(path), .. }) = exp.peek() {
|
||||
//let path = base.as_ref().parent().unwrap().join(unquote(path));
|
||||
//if !std::fs::exists(&path)? {
|
||||
//return Err(format!("(e5) not found: {path:?}").into())
|
||||
//}
|
||||
//print!("layer-if:\n cond: {}\n path: {path:?}...",
|
||||
//cond.unwrap_or_default());
|
||||
//let keys = read_and_leak(path)?.into();
|
||||
//let cond = cond.unwrap();
|
||||
//println!("ok");
|
||||
//map.add_layer_if(
|
||||
//Box::new(move |state: &App|Take::take_or_fail(
|
||||
//state, exp, ||"missing input layer conditional"
|
||||
//)), keys
|
||||
//);
|
||||
//} else {
|
||||
//return Err(format!("(e4) unexpected non-symbol {next:?}").into())
|
||||
//}
|
||||
//},
|
||||
|
||||
//_ => return Err(format!("(e3) unexpected symbol {sym:?}").into())
|
||||
//}
|
||||
//} else {
|
||||
//return Err(format!("(e2) unexpected exp {:?}", next.map(|x|x.value)).into())
|
||||
//}
|
||||
//} else {
|
||||
//return Err(format!("(e1) unexpected token {token:?}").into())
|
||||
//}
|
||||
//}
|
||||
//Ok(map)
|
||||
//}
|
||||
//{
|
||||
//}
|
||||
//fn from (source: &'s str) -> Self {
|
||||
//// this should be for single layer:
|
||||
//use DslVal::*;
|
||||
//let mut layers = vec![];
|
||||
//let mut source = CstIter::from(source);
|
||||
//while let Some(Exp(_, mut iter)) = source.next().map(|x|x.value) {
|
||||
//let mut iter = iter.clone();
|
||||
//layers.push(match iter.next().map(|x|x.value) {
|
||||
//Some(Sym(sym)) if sym.starts_with("@") => InputLayer {
|
||||
//cond: None,
|
||||
//bind: vec![[
|
||||
//dsl_val(source.nth(1).unwrap()),
|
||||
//dsl_val(source.nth(2).unwrap()),
|
||||
//]],
|
||||
//},
|
||||
//Some(Str(layer)) => InputLayer {
|
||||
//cond: None,
|
||||
//bind: dsl_val(source.nth(1).unwrap()),
|
||||
//},
|
||||
//Some(Key("if")) => InputLayer {
|
||||
//cond: Some(dsl_val(source.nth(1).unwrap())),
|
||||
//bind: dsl_val(source.nth(2).unwrap()),
|
||||
//},
|
||||
//_ => panic!("invalid token in keymap"),
|
||||
//})
|
||||
//}
|
||||
//Self(layers)
|
||||
//}
|
||||
//}
|
||||
|
||||
fn unquote (x: &str) -> &str {
|
||||
let mut chars = x.chars();
|
||||
chars.next();
|
||||
//chars.next_back();
|
||||
chars.as_str()
|
||||
}
|
||||
|
||||
fn read_and_leak (path: impl AsRef<Path>) -> Usually<&'static str> {
|
||||
Ok(leak(String::from_utf8(std::fs::read(path.as_ref())?)?))
|
||||
}
|
||||
|
||||
fn leak (x: impl AsRef<str>) -> &'static str {
|
||||
Box::leak(x.as_ref().into())
|
||||
}
|
||||
115
dsl/src/cst.rs
115
dsl/src/cst.rs
|
|
@ -5,10 +5,10 @@ use crate::*;
|
|||
|
||||
/// CST stores strings as source references and expressions as [CstIter] instances.
|
||||
#[derive(Debug, Clone, Default, PartialEq)]
|
||||
pub struct Cst<'src>(pub CstIter<'src>);
|
||||
impl<'src> Dsl for Cst<'src> {
|
||||
type Str = &'src str;
|
||||
type Exp = CstIter<'src>;
|
||||
pub struct Cst<'s>(pub CstIter<'s>);
|
||||
impl<'s> Dsl for Cst<'s> {
|
||||
type Str = &'s str;
|
||||
type Exp = CstIter<'s>;
|
||||
fn nth (&self, index: usize) -> Option<DslVal<Self::Str, Self::Exp>> {
|
||||
self.0.nth(index)
|
||||
}
|
||||
|
|
@ -16,49 +16,49 @@ impl<'src> Dsl for Cst<'src> {
|
|||
|
||||
/// Parsed substring with range and value.
|
||||
#[derive(Debug, Copy, Clone, Default, PartialEq)]
|
||||
pub struct CstVal<'src> {
|
||||
pub struct CstVal<'s> {
|
||||
/// Meaning of token.
|
||||
pub value: DslVal<&'src str, CstIter<'src>>,
|
||||
pub value: DslVal<&'s str, CstIter<'s>>,
|
||||
/// Reference to source text.
|
||||
pub source: &'src str,
|
||||
pub source: &'s str,
|
||||
/// Index of 1st character of token.
|
||||
pub start: usize,
|
||||
/// Length of token.
|
||||
pub length: usize,
|
||||
}
|
||||
impl<'src> Dsl for CstVal<'src> {
|
||||
type Str = &'src str;
|
||||
type Exp = CstIter<'src>;
|
||||
impl<'s> Dsl for CstVal<'s> {
|
||||
type Str = &'s str;
|
||||
type Exp = CstIter<'s>;
|
||||
fn nth (&self, index: usize) -> Option<DslVal<Self::Str, Self::Exp>> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'src> CstVal<'src> {
|
||||
impl<'s> CstVal<'s> {
|
||||
pub const fn new (
|
||||
source: &'src str,
|
||||
source: &'s str,
|
||||
start: usize,
|
||||
length: usize,
|
||||
value: DslVal<&'src str, CstIter<'src>>
|
||||
value: DslVal<&'s str, CstIter<'s>>
|
||||
) -> Self {
|
||||
Self { source, start, length, value }
|
||||
}
|
||||
pub const fn end (&self) -> usize {
|
||||
self.start.saturating_add(self.length)
|
||||
}
|
||||
pub const fn slice (&'src self) -> &'src str {
|
||||
pub const fn slice (&'s self) -> &'s str {
|
||||
self.slice_source(self.source)
|
||||
}
|
||||
pub const fn slice_source <'range> (&'src self, source: &'range str) -> &'range str {
|
||||
pub const fn slice_source <'range> (&'s self, source: &'range str) -> &'range str {
|
||||
str_range(source, self.start, self.end())
|
||||
}
|
||||
pub const fn slice_source_exp <'range> (&'src self, source: &'range str) -> &'range str {
|
||||
pub const fn slice_source_exp <'range> (&'s self, source: &'range str) -> &'range str {
|
||||
str_range(source, self.start.saturating_add(1), self.end())
|
||||
}
|
||||
pub const fn with_value (self, value: DslVal<&'src str, CstIter<'src>>) -> Self {
|
||||
pub const fn with_value (self, value: DslVal<&'s str, CstIter<'s>>) -> Self {
|
||||
Self { value, ..self }
|
||||
}
|
||||
pub const fn value (&self) -> DslVal<&'src str, CstIter<'src>> {
|
||||
pub const fn value (&self) -> DslVal<&'s str, CstIter<'s>> {
|
||||
self.value
|
||||
}
|
||||
pub const fn error (self, error: DslErr) -> Self {
|
||||
|
|
@ -121,46 +121,37 @@ impl<'src> CstVal<'src> {
|
|||
/// [Cst::next] returns just the [Cst] and mutates `self`,
|
||||
/// instead of returning an updated version of the struct as [CstConstIter::next] does.
|
||||
#[derive(Copy, Clone, Debug, Default, PartialEq)]
|
||||
pub struct CstIter<'src>(pub CstConstIter<'src>);
|
||||
impl<'src> Dsl for CstIter<'src> {
|
||||
type Str = &'src str;
|
||||
pub struct CstIter<'s>(pub CstConstIter<'s>);
|
||||
impl<'s> Dsl for CstIter<'s> {
|
||||
type Str = &'s str;
|
||||
type Exp = Self;
|
||||
fn nth (&self, index: usize) -> Option<DslVal<Self::Str, Self::Exp>> {
|
||||
use DslVal::*;
|
||||
self.0.nth(index).map(|x|match x {
|
||||
Nil => Nil,
|
||||
Err(e) => Err(e),
|
||||
Num(u) => Num(u),
|
||||
Sym(s) => Sym(s),
|
||||
Key(s) => Sym(s),
|
||||
Str(s) => Sym(s),
|
||||
Exp(d, x) => DslVal::Exp(d, CstIter(x)),
|
||||
})
|
||||
self.0.nth(index).map(|x|dsl_val(x))
|
||||
}
|
||||
}
|
||||
impl<'src> CstIter<'src> {
|
||||
pub const fn new (source: &'src str) -> Self {
|
||||
impl<'s> CstIter<'s> {
|
||||
pub const fn new (source: &'s str) -> Self {
|
||||
Self(CstConstIter::new(source))
|
||||
}
|
||||
pub const fn peek (&self) -> Option<CstVal<'src>> {
|
||||
pub const fn peek (&self) -> Option<CstVal<'s>> {
|
||||
self.0.peek()
|
||||
}
|
||||
}
|
||||
impl<'src> Iterator for CstIter<'src> {
|
||||
type Item = CstVal<'src>;
|
||||
fn next (&mut self) -> Option<CstVal<'src>> {
|
||||
impl<'s> Iterator for CstIter<'s> {
|
||||
type Item = CstVal<'s>;
|
||||
fn next (&mut self) -> Option<CstVal<'s>> {
|
||||
self.0.next().map(|(item, rest)|{
|
||||
self.0 = rest;
|
||||
item
|
||||
})
|
||||
}
|
||||
}
|
||||
impl<'src> Into<Vec<CstVal<'src>>> for CstIter<'src> {
|
||||
fn into (self) -> Vec<CstVal<'src>> {
|
||||
impl<'s> Into<Vec<CstVal<'s>>> for CstIter<'s> {
|
||||
fn into (self) -> Vec<CstVal<'s>> {
|
||||
self.collect()
|
||||
}
|
||||
}
|
||||
impl<'src> Into<Vec<Ast>> for CstIter<'src> {
|
||||
impl<'s> Into<Vec<Ast>> for CstIter<'s> {
|
||||
fn into (self) -> Vec<Ast> {
|
||||
self.map(Into::into).collect()
|
||||
}
|
||||
|
|
@ -172,9 +163,9 @@ impl<'src> Into<Vec<Ast>> for CstIter<'src> {
|
|||
/// * the source text remaining
|
||||
/// * [ ] TODO: maybe [CstConstIter::next] should wrap the remaining source in `Self` ?
|
||||
#[derive(Copy, Clone, Debug, Default, PartialEq)]
|
||||
pub struct CstConstIter<'src>(pub &'src str);
|
||||
impl<'src> Dsl for CstConstIter<'src> {
|
||||
type Str = &'src str;
|
||||
pub struct CstConstIter<'s>(pub &'s str);
|
||||
impl<'s> Dsl for CstConstIter<'s> {
|
||||
type Str = &'s str;
|
||||
type Exp = Self;
|
||||
fn nth (&self, mut index: usize) -> Option<DslVal<Self::Str, Self::Exp>> {
|
||||
use DslVal::*;
|
||||
|
|
@ -182,37 +173,39 @@ impl<'src> Dsl for CstConstIter<'src> {
|
|||
for i in 0..index {
|
||||
iter = iter.next()?.1
|
||||
}
|
||||
iter.next().map(|(x, _)|match x.value {
|
||||
Nil => Nil,
|
||||
Err(e) => Err(e),
|
||||
Num(u) => Num(u),
|
||||
Sym(s) => Sym(s),
|
||||
Key(s) => Sym(s),
|
||||
Str(s) => Sym(s),
|
||||
Exp(d, x) => DslVal::Exp(d, x.0),
|
||||
})
|
||||
iter.next().map(|(x, _)|dsl_val(x.value))
|
||||
}
|
||||
}
|
||||
impl<'src> CstConstIter<'src> {
|
||||
pub const fn new (source: &'src str) -> Self {
|
||||
impl<'s> CstConstIter<'s> {
|
||||
pub const fn new (source: &'s str) -> Self {
|
||||
Self(source)
|
||||
}
|
||||
pub const fn chomp (&self, index: usize) -> Self {
|
||||
Self(split_at(self.0, index).1)
|
||||
}
|
||||
pub const fn next (mut self) -> Option<(CstVal<'src>, Self)> {
|
||||
pub const fn next (mut self) -> Option<(CstVal<'s>, Self)> {
|
||||
Self::next_mut(&mut self)
|
||||
}
|
||||
pub const fn peek (&self) -> Option<CstVal<'src>> {
|
||||
pub const fn peek (&self) -> Option<CstVal<'s>> {
|
||||
peek_src(self.0)
|
||||
}
|
||||
pub const fn next_mut (&mut self) -> Option<(CstVal<'src>, Self)> {
|
||||
pub const fn next_mut (&mut self) -> Option<(CstVal<'s>, Self)> {
|
||||
match self.peek() {
|
||||
Some(token) => Some((token, self.chomp(token.end()))),
|
||||
None => None
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<'s> From<CstConstIter<'s>> for CstIter<'s> {
|
||||
fn from (iter: CstConstIter<'s>) -> Self {
|
||||
Self(iter)
|
||||
}
|
||||
}
|
||||
impl<'s> From<CstIter<'s>> for CstConstIter<'s> {
|
||||
fn from (iter: CstIter<'s>) -> Self {
|
||||
iter.0
|
||||
}
|
||||
}
|
||||
|
||||
/// Implement the const iterator pattern.
|
||||
macro_rules! const_iter {
|
||||
|
|
@ -229,8 +222,8 @@ macro_rules! const_iter {
|
|||
}
|
||||
}
|
||||
|
||||
const_iter!(<'src>|self: CstConstIter<'src>|
|
||||
=> CstVal<'src>
|
||||
const_iter!(<'s>|self: CstConstIter<'s>|
|
||||
=> CstVal<'s>
|
||||
=> self.next_mut().map(|(result, _)|result));
|
||||
|
||||
/// Static iteration helper used by [cst].
|
||||
|
|
@ -244,9 +237,9 @@ macro_rules! iterate {
|
|||
}
|
||||
}
|
||||
|
||||
pub const fn peek_src <'src> (source: &'src str) -> Option<CstVal<'src>> {
|
||||
pub const fn peek_src <'s> (source: &'s str) -> Option<CstVal<'s>> {
|
||||
use DslVal::*;
|
||||
let mut token: CstVal<'src> = CstVal::new(source, 0, 0, Nil);
|
||||
let mut token: CstVal<'s> = CstVal::new(source, 0, 0, Nil);
|
||||
iterate!(char_indices(source) => (start, c) => token = match token.value() {
|
||||
Err(_) => return Some(token),
|
||||
Nil => match c {
|
||||
|
|
|
|||
|
|
@ -42,7 +42,20 @@ pub enum DslVal<Str, Exp> {
|
|||
Exp(usize, Exp),
|
||||
}
|
||||
|
||||
pub trait Dsl {
|
||||
pub fn dsl_val <A: Into<X>, B: Into<Y>, X, Y> (val: DslVal<A, B>) -> DslVal<X, Y> {
|
||||
use DslVal::*;
|
||||
match val {
|
||||
Nil => Nil,
|
||||
Err(e) => Err(e),
|
||||
Num(u) => Num(u),
|
||||
Sym(s) => Sym(s.into()),
|
||||
Key(s) => Key(s.into()),
|
||||
Str(s) => Str(s.into()),
|
||||
Exp(d, x) => Exp(d, x.into()),
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Dsl: Debug {
|
||||
type Str: PartialEq + Clone + Default + Debug + AsRef<str>;
|
||||
type Exp: PartialEq + Clone + Default + Debug + Dsl;
|
||||
fn nth (&self, index: usize) -> Option<DslVal<Self::Str, Self::Exp>>;
|
||||
|
|
@ -61,7 +74,7 @@ impl<
|
|||
fn val (&self) -> DslVal<Str, Exp> {
|
||||
self.clone()
|
||||
}
|
||||
fn nth (&self, index: usize) -> Option<DslVal<Str, Exp>> {
|
||||
fn nth (&self, _index: usize) -> Option<DslVal<Str, Exp>> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
|
@ -163,16 +176,3 @@ from_str!(Ast|source|Self::from(CstIter::from(source)));
|
|||
from_str!(Cst<'s>|source|Self(CstIter(CstConstIter(source))));
|
||||
from_str!(CstIter<'s>|source|Self(CstConstIter(source)));
|
||||
from_str!(CstConstIter<'s>|source|Self::new(source));
|
||||
|
||||
pub fn dsl_val <A: Into<X>, B: Into<Y>, X, Y> (val: DslVal<A, B>) -> DslVal<X, Y> {
|
||||
use DslVal::*;
|
||||
match val {
|
||||
Nil => Nil,
|
||||
Err(e) => Err(e),
|
||||
Num(u) => Num(u),
|
||||
Sym(s) => Sym(s.into()),
|
||||
Key(s) => Key(s.into()),
|
||||
Str(s) => Str(s.into()),
|
||||
Exp(d, x) => Exp(d, x.into()),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,8 +5,6 @@ pub(crate) use ::tengri_core::*;
|
|||
pub(crate) use std::fmt::Debug;
|
||||
pub(crate) use std::sync::Arc;
|
||||
pub(crate) use std::collections::VecDeque;
|
||||
pub(crate) use std::path::Path;
|
||||
pub(crate) use std::fs::exists;
|
||||
pub(crate) use konst::iter::{ConstIntoIter, IsIteratorKind};
|
||||
pub(crate) use konst::string::{split_at, str_range, char_indices};
|
||||
pub(crate) use thiserror::Error;
|
||||
|
|
|
|||
5
editor/Cargo.toml
Normal file
5
editor/Cargo.toml
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
[package]
|
||||
name = "tengri_editor"
|
||||
description = "Embeddable editor for Tengri DSL."
|
||||
version = { workspace = true }
|
||||
edition = { workspace = true }
|
||||
|
|
@ -1,11 +1,22 @@
|
|||
use crate::*;
|
||||
/// A collection of input bind.
|
||||
/// A collection of input bindings.
|
||||
///
|
||||
/// Each contained layer defines a mapping from input event to command invocation
|
||||
/// over a given state. Furthermore, each layer may have an associated cond,
|
||||
/// so that only certain layers are active at a given time depending on state.
|
||||
#[derive(Debug, Default)] pub struct InputMap<I, T: Dsl>(std::collections::BTreeMap<I, T>);
|
||||
impl<I, T: Dsl> InputMap<I, T> {
|
||||
#[derive(Debug, Default)] pub struct InputMap<I, T: Dsl>(
|
||||
/// Map of input event (key combination) to
|
||||
/// all command expressions bound to it by
|
||||
/// all loaded input layers.
|
||||
pub BTreeMap<I, Vec<InputBinding<T>>>
|
||||
);
|
||||
#[derive(Debug, Default)] pub struct InputBinding<T: Dsl> {
|
||||
condition: Option<T>,
|
||||
command: T,
|
||||
description: Option<Arc<str>>,
|
||||
source: Option<Arc<PathBuf>>,
|
||||
}
|
||||
impl<I: Debug + Ord, T: Dsl> InputMap<I, T> {
|
||||
/// Create input layer collection from path to text file.
|
||||
pub fn from_path <P: Debug + AsRef<Path>> (path: P) -> Usually<Self> {
|
||||
if !exists(path.as_ref())? {
|
||||
|
|
@ -18,29 +29,38 @@ impl<I, T: Dsl> InputMap<I, T> {
|
|||
Self::from_dsl(CstIter::from(source.as_ref()))
|
||||
}
|
||||
/// Create input layer collection from DSL.
|
||||
pub fn from_dsl (dsl: impl Dsl) -> Usually<Self> {
|
||||
pub fn from_dsl <D: Dsl> (dsl: D) -> Usually<Self> {
|
||||
use DslVal::*;
|
||||
let mut input_map: Self = Self(Default::default());
|
||||
while let Exp(_, mut exp) = dsl.val() {
|
||||
let mut input_map: BTreeMap<I, Vec<InputBinding<T>>> = Default::default();
|
||||
let mut index = 0;
|
||||
while let Some(Exp(_, mut exp)) = dsl.nth(index) {
|
||||
let val = exp.nth(0).map(|x|x.val());
|
||||
match val {
|
||||
Some(Str(path)) => {
|
||||
let path = PathBuf::from(path.as_ref());
|
||||
let module = InputMap::<I, T>::from_path(&path)?;
|
||||
for (key, val) in module.0.into_iter() {
|
||||
todo!("import {exp:?} {key:?} {val:?} {path:?}");
|
||||
if !input_map.contains_key(&key) {
|
||||
input_map.insert(key, vec![]);
|
||||
}
|
||||
}
|
||||
},
|
||||
Some(Exp(_, expr)) if let Some(Sym(sym)) = expr.nth(0) => {
|
||||
//input_map.unconditional.push(expr);
|
||||
todo!("binding");
|
||||
Some(Sym(sym)) => {
|
||||
//let key: I = sym.into();
|
||||
//if !input_map.contains_key(&key) {
|
||||
//input_map.insert(key, vec![]);
|
||||
//}
|
||||
todo!("binding {exp:?} {sym:?}");
|
||||
},
|
||||
Some(Exp(_, expr)) if let Some(Key(key)) = expr.nth(0) && key.as_ref() == "if" => {
|
||||
todo!("conditional binding");
|
||||
Some(Key(key)) if key.as_ref() == "if" => {
|
||||
todo!("conditional binding {exp:?} {key:?}");
|
||||
},
|
||||
_ => return Result::Err(format!("invalid token in keymap: {val:?}").into()),
|
||||
}
|
||||
index += 1;
|
||||
}
|
||||
Ok(input_map)
|
||||
Ok(Self(input_map))
|
||||
}
|
||||
/// Evaluate the active layers for a given state,
|
||||
/// returning the command to be executed, if any.
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@
|
|||
#![feature(if_let_guard)]
|
||||
|
||||
pub(crate) use std::fmt::Debug;
|
||||
pub(crate) use std::collections::BTreeMap;
|
||||
pub(crate) use std::sync::Arc;
|
||||
pub(crate) use std::collections::{BTreeMap, HashMap};
|
||||
pub(crate) use std::path::{Path, PathBuf};
|
||||
pub(crate) use std::fs::exists;
|
||||
pub(crate) use tengri_core::*;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,15 @@
|
|||
use crate::*;
|
||||
use crate::{dsl::*, input::*, tui::TuiIn};
|
||||
use crossterm::event::{Event, KeyEvent, KeyCode, KeyModifiers, KeyEventKind, KeyEventState};
|
||||
use std::cmp::Ordering;
|
||||
|
||||
#[test] fn test_subcommand () -> Usually<()> {
|
||||
struct Test { keys: InputMap<(), Ast> }
|
||||
#[derive(Debug)] struct Event(crossterm::event::Event);
|
||||
impl Eq for Event {}
|
||||
impl PartialEq for Event { fn eq (&self, other: &Self) -> bool { todo!() } }
|
||||
impl Ord for Event { fn cmp (&self, other: &Self) -> Ordering { todo!() } }
|
||||
impl PartialOrd for Event { fn partial_cmp (&self, other: &Self) -> Option<Ordering> { None } }
|
||||
struct Test { keys: InputMap<Event, Ast> }
|
||||
|
||||
handle!(TuiIn: |self: Test, input|Ok(None));/*if let Some(command) = self.keys.command(self, input) {
|
||||
Ok(Some(true))
|
||||
|
|
|
|||
|
|
@ -8,13 +8,13 @@ pub struct TuiIn(
|
|||
/// Exit flag
|
||||
pub Arc<AtomicBool>,
|
||||
/// Input event
|
||||
pub Event,
|
||||
pub crossterm::event::Event,
|
||||
);
|
||||
|
||||
impl Input for TuiIn {
|
||||
type Event = Event;
|
||||
type Event = crossterm::event::Event;
|
||||
type Handled = bool;
|
||||
fn event (&self) -> &Event { &self.1 }
|
||||
fn event (&self) -> &crossterm::event::Event { &self.1 }
|
||||
fn is_done (&self) -> bool { self.0.fetch_and(true, Relaxed) }
|
||||
fn done (&self) { self.0.store(true, Relaxed); }
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue