constr/lerox: remove

This commit is contained in:
John 2023-10-26 12:23:26 -05:00
parent 5c4c8bcb80
commit d1b5c48aac
5 changed files with 1 additions and 349 deletions

View File

@ -1,5 +1,5 @@
[workspace] [workspace]
members = ["libconlang", "lerox", "constr"] members = ["libconlang"]
resolver = "2" resolver = "2"
[workspace.package] [workspace.package]

View File

@ -1,10 +0,0 @@
[package]
name = "constr"
version.workspace = true
authors.workspace = true
edition.workspace = true
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

View File

@ -1,255 +0,0 @@
//! [String] tools for Conlang
//#![warn(clippy::all)]
#![feature(decl_macro, const_trait_impl)]
impl<T: Iterator> ConstrTools for T {}
pub trait ConstrTools {
/// Unescapes string escape sequences
fn unescape(self) -> UnescapeString<Self>
where Self: Iterator<Item = char> + Sized {
UnescapeString::new(self)
}
/// Parse an integer
fn parse_int<O>(self) -> ParseInt<Self, O>
where Self: Iterator<Item = char> + Sized {
ParseInt::new(self)
}
}
pub use unescape_string::UnescapeString;
pub mod unescape_string {
//! TODO: Write the module-level documentation
pub struct UnescapeString<I: Iterator<Item = char>> {
inner: I,
}
impl<I: Iterator<Item = char>> Iterator for UnescapeString<I> {
type Item = I::Item;
fn next(&mut self) -> Option<Self::Item> {
self.unescape()
}
}
impl<I: Iterator<Item = char>> UnescapeString<I> {
pub fn new(inner: I) -> Self {
Self { inner }
}
/// Consumes an escape sequence. See the [module level documentation](self).
pub fn unescape(&mut self) -> Option<char> {
match self.inner.next()? {
'\\' => (),
other => return Some(other),
}
Some(match self.inner.next()? {
'a' => '\x07',
'b' => '\x08',
'f' => '\x0c',
'n' => '\n',
'r' => '\r',
't' => '\t',
'x' => self.hex_digits::<2>()?,
'u' => self.unicode()?,
'0' => '\0',
byte => byte,
})
}
fn unicode(&mut self) -> Option<char> {
let mut out = 0;
let Some('{') = self.inner.next() else {
return None;
};
for c in self.inner.by_ref() {
match c {
'}' => return char::from_u32(out),
_ => out = (out << 4) + super::base::<16>(c)? as u32,
}
}
None
}
fn hex_digits<const DIGITS: u32>(&mut self) -> Option<char> {
let mut out = 0;
for _ in 0..DIGITS {
out = (out << 4) + self.hex_digit()? as u32;
}
char::from_u32(out)
}
fn hex_digit(&mut self) -> Option<u8> {
super::base::<16>(self.inner.next()?)
}
}
}
pub use parse_int::ParseInt;
pub mod parse_int {
use std::marker::PhantomData;
pub struct ParseInt<I: Iterator<Item = char>, O> {
inner: I,
_data: PhantomData<O>,
}
impl<I: Iterator<Item = char>, O> ParseInt<I, O> {
pub fn new(inner: I) -> Self {
Self { inner, _data: Default::default() }
}
fn digit<const B: u8>(&mut self) -> Option<u8> {
let next = loop {
match self.inner.next()? {
'_' => continue,
c => break c,
}
};
super::base::<B>(next)
}
}
parse_int_impl!(u8, i8, u16, i16, u32, i32, u64, i64, u128, i128);
macro parse_int_impl($($T:ty),*$(,)?) {$(
impl<I: Iterator<Item = char>> ParseInt<I, $T> {
fn digits<const B: u8>(&mut self, init: Option<u8>) -> Option<$T> {
let mut out = match init {
Some(digit) => digit,
None => self.digit::<B>()?,
} as $T;
while let Some(digit) = self.digit::<B>() {
out = out.checked_mul(B as $T)?.checked_add(digit as $T)?
}
Some(out)
}
fn base(&mut self) -> Option<$T> {
match self.inner.next()? {
'b' => self.digits::<2>(None),
'd' => self.digits::<10>(None),
'o' => self.digits::<8>(None),
'x' => self.digits::<16>(None),
c => self.digits::<10>(Some(super::base::<10>(c)?)),
}
}
}
impl<I: Iterator<Item = char>> Iterator for ParseInt<I, $T> {
type Item = $T;
fn next(&mut self) -> Option<Self::Item> {
match self.digit::<10>()? {
0 => self.base(),
c if (0..=9).contains(&c) => self.digits::<10>(Some(c)),
_ => None,
}
}
}
)*}
}
/// Converts a single char [0-9A-Za-z] to their [base B](base::<B>) equivalent.
///
/// # May Panic
/// Panics in debug mode when B > 36
pub const fn base<const B: u8>(c: char) -> Option<u8> {
// TODO: Wait for a way to limit const generics at compile time
debug_assert!(B <= 36);
// Can't use Ord::min in const context yet :(
// This function also relies on wrapping arithmetic
macro wrap ($c:ident - $b:literal $(+ $ten:literal)? $(< $B:ident.min($min:literal))?) {
$c.wrapping_sub($b)$(.wrapping_add($ten))? $(< if $B < $min {$B} else {$min})?
}
let c = c as u8;
match c {
c if wrap!(c - b'0' < B.min(10)) => Some(wrap!(c - b'0')),
_ if B <= 10 => None, // cuts base<1..=10> to 4 instructions on x86 :^)
c if wrap!(c - b'A' + 10 < B.min(36)) => Some(wrap!(c - b'A' + 10)),
c if wrap!(c - b'a' + 10 < B.min(36)) => Some(wrap!(c - b'a' + 10)),
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::*;
mod unescape_string {
use super::*;
test_unescape! {
empty = ["" => ""];
n_newline = ["\\n" => "\n", "This is a\\ntest" => "This is a\ntest"];
a_bell = ["\\a" => "\x07", "Ring the \\abell" => "Ring the \x07bell"];
b_backspace = ["\\b" => "\x08"];
f_feed = ["\\f" => "\x0c"];
r_return = ["\\r" => "\r"];
t_tab = ["\\t" => "\t"];
_0_nul = ["\\0" => "\0"];
x_hex = [
"\\x41\\x41\\x41\\x41" => "AAAA",
"\x00" => "\0",
"\\x7f" => "\x7f",
"\\x80" => "\u{80}",
"\\xD0" => "\u{D0}",
];
u_unicode = [
"\\u{41}" => "A",
"\\u{1f988}" => "🦈",
];
}
macro test_unescape ($($f:ident = [$($test:expr => $expect:expr),*$(,)?];)*) {$(
#[test] fn $f () {
$(assert_eq!($test.chars().unescape().collect::<String>(), dbg!($expect));)*
}
)*}
}
mod parse_int {
use super::*;
#[test]
#[should_panic]
fn base_37_panics() {
base::<37>('a');
}
test_parse! {
parse_u8: u8 = [
"0xc5" => Some(0xc5),
"0xc_____________________5" => Some(0xc5),
"0x7d" => Some(0x7d),
"0b10" => Some(0b10),
"0o10" => Some(0o10),
"0x10" => Some(0x10),
"0d10" => Some(10),
"10" => Some(10),
];
parse_u16: u16 = [
"0xc5c5" => Some(0xc5c5),
"0x1234" => Some(0x1234),
"0x5678" => Some(0x5678),
"0x9abc" => Some(0x9abc),
"0xdef0" => Some(0xdef0),
"0xg" => None,
"0b10" => Some(0b10),
"0o10" => Some(0o10),
"0x10" => Some(0x10),
"0d10" => Some(10),
"10" => Some(10),
];
parse_u32: u32 = [
"0xc5c5c5c5" => Some(0xc5c5c5c5),
"0xc5_c5_c5_c5" => Some(0xc5c5c5c5),
"1_234_567____" => Some(1234567),
"4294967295" => Some(4294967295),
"4294967296" => None,
"🦈" => None,
];
parse_u64: u64 = [
"0xffffffffffffffff" => Some(0xffffffffffffffff),
"0x10000000000000000" => None,
"0xc5c5c5c5c5c5c5c5" => Some(0xc5c5c5c5c5c5c5c5),
"0x123456789abcdef0" => Some(1311768467463790320),
"0x123456789abcdefg" => Some(81985529216486895),
"0d1234567890" => Some(1234567890),
"0o12345670" => Some(2739128),
"0b10" => Some(2),
];
parse_u128: u128 = [
"0x10000000000000000" => Some(0x10000000000000000),
"0xc5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5" => Some(0xc5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5),
"0o77777777777777777777777777777777" => Some(0o77777777777777777777777777777777),
];
}
macro test_parse ($($f:ident : $T:ty = [$($test:expr => $expect:expr),*$(,)?];)*) {$(
#[test] fn $f () {
type Test = $T;
$(assert_eq!(($test.chars().parse_int() as ParseInt<_, Test>).next(), dbg!($expect));)*
}
)*}
}
}

View File

@ -1,10 +0,0 @@
[package]
name = "lerox"
authors.workspace = true
version.workspace = true
edition.workspace = true
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

View File

@ -1,73 +0,0 @@
//! Lerox is a stupid, simple combinator library, part of the Conlang project
/// The core trait of Lerox, [Combinator], generically implements a handful of simple
/// combinators for your type based on two required functions:
/// - [`is_alright(&self) -> bool`](Combinator::is_alright) performs some unspecified *evaluation*
/// on `self`, and returns true if `self` is valid for your problem domain.
/// - [`into_alright(self) -> Self`](Combinator::into_alright) performs some unspecified
/// *transformation* of `self` into an alright version of `self` (that is, one which satisfies
/// [`self.is_alright()`](Combinator::is_alright) == `true`)
pub trait Combinator: Sized {
/// Evaluates the [Combinator], and returns whether it is alright.
///
/// This is the basis for [Combinator]'s [and*](Combinator::and())/[or*](Combinator::or())
/// behaviors, and is like a generic form of [Option::is_some()] or [Result::is_ok()]
fn is_alright(&self) -> bool;
/// Does something to make this [Combinator] [alright](Combinator::is_alright())
fn into_alright(self) -> Self;
/// If self is alright, runs f(self)
fn and(self, f: impl Fn(Self) -> Self) -> Self {
match self.is_alright() {
true => f(self),
false => self,
}
}
/// If self is not alright, runs f(self) as if it were alright
fn or(self, f: impl FnOnce(Self) -> Self) -> Self {
match self.is_alright() {
true => self,
false => f(self.into_alright()),
}
}
/// Repeats the function f on self until it would fail
fn and_any(self, f: impl Fn(Self) -> Self) -> Self {
self.and(|mut this| {
while this.is_alright() {
this = this.and(&f)
}
this.into_alright()
})
}
/// Repeats the function f on self at least once, then until it fails.
fn and_many(self, f: impl Fn(Self) -> Self) -> Self {
self.and(&f).and_any(f)
}
/// Returns the result of running f on self, or if it fails, the original self
fn and_maybe(self, f: impl Fn(Self) -> Self) -> Self
where Self: Clone {
self.and_either(f, |g| g)
}
/// Returns the result of running f on self, or if it fails, runs g on self
fn and_either(self, f: impl Fn(Self) -> Self, g: impl Fn(Self) -> Self) -> Self
where Self: Clone {
self.clone().and(f).or(|_| g(self))
}
/// Returns the result of the first f that succeeds, or self
fn and_one_of(mut self, f: &[&dyn Fn(Self) -> Self]) -> Self {
for &f in f {
self = self.into_alright().and(f);
if self.is_alright() {
break;
}
}
self
}
}