From 297f9b30df2fc55d3f90155a7fca5308c4260324 Mon Sep 17 00:00:00 2001 From: unspeaker Date: Fri, 17 Jan 2025 22:26:49 +0100 Subject: [PATCH] wip: more const parsing --- edn/Cargo.toml | 2 +- edn/src/token.rs | 175 +++++++++++++++++++++++++------------------- input/src/keymap.rs | 76 +++++++++---------- input/src/lib.rs | 2 +- 4 files changed, 141 insertions(+), 114 deletions(-) diff --git a/edn/Cargo.toml b/edn/Cargo.toml index 9a2fa766..11626bea 100644 --- a/edn/Cargo.toml +++ b/edn/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" [dependencies] clojure-reader = "0.3.0" -konst = "0.3.16" +konst = { version = "0.3.16", features = [ "rust_1_83" ] } itertools = "0.14.0" [features] diff --git a/edn/src/token.rs b/edn/src/token.rs index 1aa78645..975b234d 100644 --- a/edn/src/token.rs +++ b/edn/src/token.rs @@ -1,16 +1,8 @@ use crate::*; +use konst::iter::{ConstIntoIter, IsIteratorKind}; use konst::string::{split_at, str_range, char_indices}; use self::ParseError::*; use self::TokenKind::*; -macro_rules! iterate { - ($expr:expr => $arg: pat => $body:expr) => { - let mut iter = $expr; - while let Some(($arg, next)) = iter.next() { - $body; - iter = next; - } - } -} #[derive(Debug)] pub enum ParseError { Unimplemented, Empty, Incomplete, Unexpected(char), Code(u8), } impl std::fmt::Display for ParseError { fn fmt (&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { @@ -24,6 +16,88 @@ impl std::fmt::Display for ParseError { } } impl std::error::Error for ParseError {} +/// Iterator helper macro because I can't find the canonical one in [konst] docs. +macro_rules! iterate { + ($expr:expr => $arg: pat => $body:expr) => { + let mut iter = $expr; + while let Some(($arg, next)) = iter.next() { + $body; + iter = next; + } + } +} +pub struct TokenIterator<'a>(&'a str); +impl<'a> ConstIntoIter for TokenIterator<'a> { + type Kind = IsIteratorKind; + type Item = Token<'a>; + type IntoIter = Self; +} +impl<'a> TokenIterator<'a> { + pub const fn new (source: &'a str) -> Self { Self(source) } + pub const fn split (self, index: usize) -> Self { Self(split_at(self.0, index).1) } + pub const fn next (self) -> Option<(Result, ParseError>, Self)> { + let src = self.0; + let mut token: Token<'a> = Token::new(src, Nil, 0, 0, 0); + iterate!(char_indices(src) => (index, c) => token = match token.kind() { + Nil => match c { + '(' => Token::new(src, Exp, index, 1, 1), + ':'|'@' => Token::new(src, Sym, index, 1, 0), + '0'..='9' => Token::new(src, Num, index, 1, 0), + '/'|'a'..='z' => Token::new(src, Key, index, 1, 0), + ' '|'\n'|'\r'|'\t' => token.grow(), + _ => return Some((Err(Unexpected(c)), self.split(token.end()))) + }, + Num => match c { + '0'..='9' => token.grow(), + ' '|'\n'|'\r'|'\t' => return Some((Ok(token), self.split(token.end()))), + _ => return Some((Err(Unexpected(c)), self.split(token.end()))) + }, + Sym => match c { + 'a'..='z'|'0'..='9'|'-' => token.grow(), + ' '|'\n'|'\r'|'\t' => return Some((Ok(token), self.split(token.end()))), + _ => return Some((Err(Unexpected(c)), self.split(token.end()))) + }, + Key => match c { + 'a'..='z'|'0'..='9'|'-'|'/' => token.grow(), + ' '|'\n'|'\r'|'\t' => return Some((Ok(token), self.split(token.end()))), + _ => return Some((Err(Unexpected(c)), self.split(token.end()))) + }, + Exp => match token.depth { + 0 => return Some((Ok(token), Self(split_at(src, token.end()).1))), + _ => match c { + ')' => match token.grow_out() { + Ok(token) => token, + Err(e) => return Some((Err(e), self.split(token.end()))) + }, + '(' => token.grow_in(), + _ => token.grow(), + } + }, + }); + match token.kind() { Nil => None, _ => Some((Err(ParseError::Incomplete), self.split(token.end()))) } + } +} +pub struct AtomIterator<'a>(TokenIterator<'a>); +impl<'a> ConstIntoIter for AtomIterator<'a> { + type Kind = IsIteratorKind; + type Item = Atom<&'a str>; + type IntoIter = Self; +} +impl<'a> AtomIterator<'a> { + pub const fn new (tokens: TokenIterator<'a>) -> Self { Self(tokens) } + pub const fn next (mut self) -> Option<(Result, ParseError>, Self)> { + match self.0.next() { + None => None, + Some((result, next)) => match result { + Err(e) => Some((Err(e), Self(next))), + Ok(token) => match token.to_ref_atom() { + Err(e) => Some((Err(e), Self(next))), + Ok(atom) => Some((Ok(atom), Self(next))), + } + } + } + } +} #[derive(Debug, Copy, Clone, Default, PartialEq)] pub enum TokenKind { #[default] Nil, Num, Sym, Key, Exp } #[derive(Debug, Copy, Clone, Default, PartialEq)] @@ -55,74 +129,10 @@ impl<'a> Token<'a> { d => Ok(Self { length: self.length + 1, depth: d - 1, ..self }) } } - pub const fn chomp_first (source: &'a str) -> Result { - match Self::chomp(source) { - Ok((token, _)) => match token.kind() { Nil => Err(Empty), _ => Ok(token) }, - Err(e) => Err(e), - } - } - pub const fn chomp (src: &'a str) -> Result<(Self, &'a str), ParseError> { - let mut token: Token<'a> = Token::new(src, Nil, 0, 0, 0); - iterate!(char_indices(src) => (index, c) => token = match token.kind() { - Nil => match c { - '(' => Self::new(src, Exp, index, 1, 1), - ':'|'@' => Self::new(src, Sym, index, 1, 0), - '0'..='9' => Self::new(src, Num, index, 1, 0), - '/'|'a'..='z' => Self::new(src, Key, index, 1, 0), - ' '|'\n'|'\r'|'\t' => token.grow(), - _ => return Err(Unexpected(c)) - }, - Num => match c { - '0'..='9' => token.grow(), - ' '|'\n'|'\r'|'\t' => return Ok((token, split_at(src, token.end()).1)), - _ => return Err(Unexpected(c)) - }, - Sym => match c { - 'a'..='z'|'0'..='9'|'-' => token.grow(), - ' '|'\n'|'\r'|'\t' => return Ok((token, split_at(src, token.end()).1)), - _ => return Err(Unexpected(c)), - }, - Key => match c { - 'a'..='z'|'0'..='9'|'-'|'/' => token.grow(), - ' '|'\n'|'\r'|'\t' => return Ok((token, split_at(src, token.end()).1)), - _ => return Err(Unexpected(c)) - }, - Exp => match token.depth { - 0 => match c { - ' '|'\n'|'\r'|'\t' => return Ok((token, split_at(src, token.end()).1)), - _ => return Err(Unexpected(c)) - }, - _ => match c { - ')' => match token.grow_out() { - Ok(token) => token, - Err(e) => return Err(e) - }, - '(' => token.grow_in(), - _ => token.grow(), - } - }, - }); - Err(Empty) - } - pub const fn number (digits: &str) -> Result { - let mut value = 0; - iterate!(char_indices(digits) => (_, c) => match Self::digit(c) { - Ok(digit) => value = 10 * value + digit, - Err(e) => return Err(e) - }); - Ok(value) - } - pub const fn digit (c: char) -> Result { - Ok(match c { - '0' => 0, '1' => 1, '2' => 2, '3' => 3, '4' => 4, - '5' => 5, '6' => 6, '7' => 7, '8' => 8, '9' => 9, - _ => return Err(Unexpected(c)) - }) - } pub const fn to_ref_atom (&'a self) -> Result, ParseError> { Ok(match self.kind { Nil => return Err(ParseError::Empty), - Num => match Self::number(self.slice()) { + Num => match to_number(self.slice()) { Ok(n) => Atom::Num(n), Err(e) => return Err(e) }, @@ -134,7 +144,7 @@ impl<'a> Token<'a> { pub fn to_arc_atom (&self) -> Result>, ParseError> { Ok(match self.kind { Nil => return Err(ParseError::Empty), - Num => match Self::number(self.slice()) { + Num => match to_number(self.slice()) { Ok(n) => Atom::Num(n), Err(e) => return Err(e) }, @@ -144,6 +154,21 @@ impl<'a> Token<'a> { }) } } +const fn to_number (digits: &str) -> Result { + let mut value = 0; + iterate!(char_indices(digits) => (_, c) => match to_digit(c) { + Ok(digit) => value = 10 * value + digit, + Err(e) => return Err(e) + }); + Ok(value) +} +const fn to_digit (c: char) -> Result { + Ok(match c { + '0' => 0, '1' => 1, '2' => 2, '3' => 3, '4' => 4, + '5' => 5, '6' => 6, '7' => 7, '8' => 8, '9' => 9, + _ => return Err(Unexpected(c)) + }) +} #[derive(Clone, PartialEq)] pub enum Atom { Num(usize), Sym(T), Key(T), Exp(Vec>) } impl<'a, T: 'a> Atom { pub fn transform U + Clone> (&'a self, f: F) -> Atom { diff --git a/input/src/keymap.rs b/input/src/keymap.rs index e56a3f1f..41e19be8 100644 --- a/input/src/keymap.rs +++ b/input/src/keymap.rs @@ -1,13 +1,49 @@ use crate::*; -use std::sync::Arc; - +pub struct KeyMap<'a, T>(AtomIterator<'a>); +impl<'a, T> KeyMap<'a, T> { + pub const fn new (source: &'a str) -> Self { + Self(AtomIterator::new(TokenIterator::new(source))) + } + /// Try to find a binding matching the currently pressed key + pub fn command (&self, state: &S, input: &impl EdnInput) -> Option + where + C: Command + EdnCommand + { + use Atom::*; + let mut command: Option = None; + match self { + Self::TokenIterator(t) => todo!(), + Self::TokenSlice(t) => todo!(), + Self::TokenVec(t) => todo!(), + //Self::AtomIterator(t) => todo!(), + Self::AtomSlice(t) => todo!(), + Self::Atom(t) => todo!(), + } + for item in self.0.iter() { + if let Exp(e) = item { + match e.as_slice() { + [Sym(key), c, args @ ..] if input.matches_edn(key) => { + command = C::from_edn(state, c, args); + if command.is_some() { + break + } + }, + _ => {} + } + } else { + panic!("invalid config") + } + } + command + } +} +/// [Input] state that can be matched against an [Atom]. pub trait EdnInput: Input { fn matches_edn (&self, token: &str) -> bool; fn get_event (_: &Atom>) -> Option { None } } - /// Turns an EDN item sequence into a command enum variant. pub trait EdnCommand: Command { fn from_edn <'a> ( @@ -16,7 +52,6 @@ pub trait EdnCommand: Command { tail: &'a [Atom>] ) -> Option; } - /** Implement `EdnCommand` for given `State` and `Command` */ #[macro_export] macro_rules! edn_command { ($Command:ty : |$state:ident:<$T:ident: $Trait:path>| { $(( @@ -112,39 +147,6 @@ pub trait EdnCommand: Command { let $arg: $type = EdnProvide::<$type>::get_or_fail($state, $arg); }; } - -pub struct KeyMap(Vec>>); -impl KeyMap { - /// Construct keymap from source text or fail - pub fn new (keymap: &str) -> Usually { - Ok(Self(Atom::read_all(keymap)?)) - } - /// Try to find a binding matching the currently pressed key - pub fn command (&self, state: &T, input: &impl EdnInput) -> Option - where - C: Command + EdnCommand - { - use Atom::*; - let mut command: Option = None; - for item in self.0.iter() { - if let Exp(e) = item { - match e.as_slice() { - [Sym(key), c, args @ ..] if input.matches_edn(key) => { - command = C::from_edn(state, c, args); - if command.is_some() { - break - } - }, - _ => {} - } - } else { - panic!("invalid config") - } - } - command - } -} - #[cfg(test)] #[test] fn test_edn_keymap () -> Usually<()> { let keymap = KeyMap::new("")?; Ok(()) diff --git a/input/src/lib.rs b/input/src/lib.rs index edba6774..119f04fd 100644 --- a/input/src/lib.rs +++ b/input/src/lib.rs @@ -3,7 +3,7 @@ mod input; pub use self::input::*; mod command; pub use self::command::*; mod keymap; pub use self::keymap::*; //mod event_map; pub use self::event_map::*; -pub(crate) use ::tek_edn::Atom; +pub(crate) use ::tek_edn::*; /// Standard error trait. pub(crate) use std::error::Error; /// Standard result type.