wip: try to get a simplified parser going

This commit is contained in:
🪞👃🪞 2025-01-04 08:49:38 +01:00
parent fc82d6ff9b
commit 600d0b3aca
17 changed files with 676 additions and 133 deletions

92
edn/Cargo.lock generated Normal file
View file

@ -0,0 +1,92 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "autocfg"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
[[package]]
name = "clojure-reader"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9edf141eea627c101a97509266bc9f6ba8cd408618f5e2ac4a0cb6b64b1d4ea8"
dependencies = [
"ordered-float",
]
[[package]]
name = "const_panic"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53857514f72ee4a2b583de67401e3ff63a5472ca4acf289d09a9ea7636dfec17"
[[package]]
name = "konst"
version = "0.3.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4381b9b00c55f251f2ebe9473aef7c117e96828def1a7cb3bd3f0f903c6894e9"
dependencies = [
"const_panic",
"konst_kernel",
"konst_proc_macros",
"typewit",
]
[[package]]
name = "konst_kernel"
version = "0.3.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4b1eb7788f3824c629b1116a7a9060d6e898c358ebff59070093d51103dcc3c"
dependencies = [
"typewit",
]
[[package]]
name = "konst_proc_macros"
version = "0.3.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00af7901ba50898c9e545c24d5c580c96a982298134e8037d8978b6594782c07"
[[package]]
name = "num-traits"
version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [
"autocfg",
]
[[package]]
name = "ordered-float"
version = "4.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7bb71e1b3fa6ca1c61f383464aaf2bb0e2f8e772a1f01d486832464de363b951"
dependencies = [
"num-traits",
]
[[package]]
name = "tek_edn"
version = "0.1.0"
dependencies = [
"clojure-reader",
"konst",
]
[[package]]
name = "typewit"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb77c29baba9e4d3a6182d51fa75e3215c7fd1dab8f4ea9d107c716878e55fc0"
dependencies = [
"typewit_proc_macros",
]
[[package]]
name = "typewit_proc_macros"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e36a83ea2b3c704935a01b4642946aadd445cea40b10935e3f8bd8052b8193d6"

8
edn/Cargo.toml Normal file
View file

@ -0,0 +1,8 @@
[package]
name = "tek_edn"
edition = "2021"
version = "0.1.0"
[dependencies]
clojure-reader = "0.3.0"
konst = "0.3.16"

118
edn/src/edn_layout.rs Normal file
View file

@ -0,0 +1,118 @@
use crate::*;
#[cfg(test)] #[test] fn test_edn_layout () -> Result<(), ParseError> {
let source = include_str!("example.edn");
let layout = Item::read_all(source)?;
panic!("{layout:?}");
let content = EdnLayout::from(&layout);
Ok(())
}
impl<'a> From<&'a [Item]> for EdnLayout<'a> {
fn from (items: &'a [Item]) -> Self {
Self(items)
}
}
pub struct EdnLayout<'a, E>(&'a [Item]);
impl<'a, E> EdnLayout<'a, E> {
fn get_content (&self) -> impl Content<E> {
}
}
//pub struct EdnContent<'a, T>(T, &'a [Item]);
#[macro_export] macro_rules! edn_ns {
($name:literal = $Host:ident<$E:ident: $Engine:path> |$args:ident| { $(
($fn:literal
$Struct:ident
$(<$($G:ident$(: $Generic:ty)?),+>)?
$(::$Variant:ident)?
($($arg:expr),*))
)* }) => {
//pub trait $Host<$E: Engine> {
//fn read_one <'e> (edn: &[Edn<'e>]) -> impl Content<$E> {
//if let Some(Edn::Symbol(name)) = edn.get(0) {
//match *name {
//$(
//$fn => $Struct$(::$Variant)?($($arg),+),
//)*
//_ => {}
//}
//} else {
//panic!("invalid edn")
//}
//}
//}
};
}
edn_ns! {
"when" = When<A>(args[0].into(), args[1].into()),
"either" = Either<A, B>(args[0].into(), args[1].into(), args[2].into()),
"map" = Map<A, B, I, F, G>(args[0].into(), args[1].into()),
"fill" = edn_ns! {
"x" = Fixed<T>::X(args[0].into(), args[1].into()),
"y" = Fixed<T>::Y(args[0].into(), args[1].into()),
"xy" = Fixed<T>::XY(args[0].into(), args[1].into(), args[2].into()),
},
"fixed" = edn_ns! {
"x" = Fixed<T>::X(args[0].into(), args[1].into()),
"y" = Fixed<T>::Y(args[0].into(), args[1].into()),
"xy" = Fixed<T>::XY(args[0].into(), args[1].into(), args[2].into()),
},
"shrink" = edn_ns! {
"x" = Shrink<T>::X(args[0].into(), args[1].into()),
"y" = Shrink<T>::Y(args[0].into(), args[1].into()),
"xy" = Shrink<T>::XY(args[0].into(), args[1].into(), args[2].into()),
},
"expand" = edn_ns! {
"x" = Expand<T>::X(args[0].into(), args[1].into()),
"y" = Expand<T>::Y(args[0].into(), args[1].into()),
"xy" = Expand<T>::XY(args[0].into(), args[1].into(), args[2].into()),
},
"min" = edn_ns! {
"x" = Min<T>::X(args[0].into(), args[1].into()),
"y" = Min<T>::Y(args[0].into(), args[1].into()),
"xy" = Min<T>::XY(args[0].into(), args[1].into(), args[2].into()),
},
"max" = edn_ns! {
"x" = Max<T>::X(args[0].into(), args[1].into()),
"y" = Max<T>::Y(args[0].into(), args[1].into()),
"xy" = Max<T>::XY(args[0].into(), args[1].into(), args[2].into()),
},
"push" = edn_ns! {
"x" = Push<T>::X(args[0].into(), args[1].into()),
"y" = Push<T>::Y(args[0].into(), args[1].into()),
"xy" = Push<T>::XY(args[0].into(), args[1].into(), args[2].into()),
},
"pull" = edn_ns! {
"x" = Pull<T>::X(args[0].into(), args[1].into()),
"y" = Pull<T>::Y(args[0].into(), args[1].into()),
"xy" = Pull<T>::XY(args[0].into(), args[1].into(), args[2].into()),
},
"margin" = edn_ns! {
"x" = Margin<T>::X(args[0].into(), args[1].into()),
"y" = Margin<T>::Y(args[0].into(), args[1].into()),
"xy" = Margin<T>::XY(args[0].into(), args[1].into(), args[2].into()),
},
"padding" = edn_ns! {
"x" = Padding<T>::X(args[0].into(), args[1].into()),
"y" = Padding<T>::Y(args[0].into(), args[1].into()),
"xy" = Padding<T>::XY(args[0].into(), args[1].into(), args[2].into()),
},
}

296
edn/src/edn_lib.rs Normal file
View file

@ -0,0 +1,296 @@
use std::sync::{Arc, RwLock};
use std::collections::BTreeMap;
pub use clojure_reader::edn::Edn;
#[cfg(test)] #[test] fn test_edn () -> Result<(), ParseError> {
use Item::*;
assert_eq!(Item::read_all("")?,
vec![]);
assert_eq!(Item::read_all(" ")?,
vec![]);
assert_eq!(Item::read_all("1234")?,
vec![Num(1234)]);
assert_eq!(Item::read_all("1234 5 67")?,
vec![Num(1234), Num(5), Num(67)]);
assert_eq!(Item::read_all("foo/bar")?,
vec![Key("foo/bar".into())]);
assert_eq!(Item::read_all(":symbol")?,
vec![Sym(":symbol".into())]);
assert_eq!(Item::read_all(" foo/bar :baz 456")?,
vec![Key("foo/bar".into()), Sym(":baz".into()), Num(456)]);
assert_eq!(Item::read_all(" (foo/bar :baz 456) ")?,
vec![Exp(vec![Key("foo/bar".into()), Sym(":baz".into()), Num(456)])]);
Ok(())
}
fn number (digits: &str) -> usize {
let mut value = 0;
for c in digits.chars() {
value = 10 * value + digit(c);
}
value
}
const fn digit (c: char) -> usize {
match c { '0' => 0, '1' => 1, '2' => 2, '3' => 3, '4' => 4,
'5' => 5, '6' => 6, '7' => 7, '8' => 8, '9' => 9, _ => unreachable!() }
}
#[derive(Debug)]
pub enum ParseError {
Empty,
Unexpected(char),
Incomplete
}
#[derive(Debug, Clone, Default, PartialEq)]
pub enum Item {
#[default] Nil,
Num(usize),
Sym(String),
Key(String),
Exp(Vec<Item>),
}
impl Item {
pub fn read_all <'a> (mut source: &'a str) -> Result<Vec<Self>, ParseError> {
let mut items = vec![];
loop {
if source.len() == 0 {
break
}
let (remaining, token) = Token::chomp(source)?;
match Item::read(token)? { Item::Nil => {}, item => items.push(item) };
source = remaining
}
Ok(items)
}
pub fn read <'a> (token: Token<'a>) -> Result<Self, ParseError> {
use Token::*;
Ok(match token {
Nil => Item::Nil,
Num(chars, index, length) =>
Self::Num(number(&chars[index..index+length])),
Sym(chars, index, length) =>
Self::Sym(chars[index..index+length].to_string()),
Key(chars, index, length) =>
Self::Key(chars[index..index+length].to_string()),
Exp(chars, index, length, 0) =>
Self::Exp(Self::read_all(&chars[index+1..(index+length).saturating_sub(1)])?),
_ => panic!("unclosed delimiter")
})
}
}
#[derive(Debug, Copy, Clone, Default, PartialEq)]
pub enum Token<'a> {
#[default] Nil,
Num(&'a str, usize, usize),
Sym(&'a str, usize, usize),
Key(&'a str, usize, usize),
Exp(&'a str, usize, usize, usize),
}
impl<'a> Token<'a> {
fn chomp (source: &'a str) -> Result<(&'a str, Self), ParseError> {
use Token::*;
let mut state = Self::default();
for (index, c) in source.char_indices() {
state = match state {
// must begin expression
Nil => match c {
' '|'\n'|'\r'|'\t' => Nil,
'(' => Exp(source, index, 1, 1),
':' => Sym(source, index, 1),
'0'..='9' => Num(source, index, 1),
'a'..='z' => Key(source, index, 1),
_ => return Err(ParseError::Unexpected(c))
},
Num(_, _, 0) => unreachable!(),
Sym(_, _, 0) => unreachable!(),
Key(_, _, 0) => unreachable!(),
Num(source, index, length) => match c {
'0'..='9' => Num(source, index, length + 1),
' '|'\n'|'\r'|'\t' => return Ok((&source[index+length..], Num(source, index, length))),
_ => return Err(ParseError::Unexpected(c))
},
Sym(source, index, length) => match c {
'a'..='z'|'0'..='9'|'-' => Sym(source, index, length + 1),
' '|'\n'|'\r'|'\t' => return Ok((&source[index+length..], Sym(source, index, length))),
_ => return Err(ParseError::Unexpected(c))
},
Key(source, index, length) => match c {
'a'..='z'|'0'..='9'|'-'|'/' => Key(source, index, length + 1),
' '|'\n'|'\r'|'\t' => return Ok((&source[index+length..], Key(source, index, length))),
_ => return Err(ParseError::Unexpected(c))
},
Exp(source, index, length, 0) => match c {
' '|'\n'|'\r'|'\t' => return Ok((&source[index+length..], Exp(source, index, length, 0))),
_ => return Err(ParseError::Unexpected(c))
},
Exp(source, index, length, depth) => match c {
')' => Exp(source, index, length + 1, depth - 1),
'(' => Exp(source, index, length + 1, depth + 1),
_ => Exp(source, index, length + 1, depth)
},
}
}
Ok(("", state))
}
}
//#[derive(Debug, Copy, Clone, Default, PartialEq)]
//pub struct Items<'a>(&'a [Item<'a>]);
//impl<'a> Items<'a> {
//fn iter (&'a self) -> ItemsIterator<'a> {
//ItemsIterator(0, self.0)
//}
//}
//pub struct ItemsIterator<'a>(usize, &'a [Item<'a>]);
//impl<'a> Iterator for ItemsIterator<'a> {
//type Item = &'a Item<'a>;
//fn next (&mut self) -> Option<Self::Item> {
//let item = self.1.get(self.0);
//self.0 += 1;
//item
//}
//}
/*
nice but doesn't work without compile time slice concat
(which i guess could be implemeted using an unsafe linked list?)
never done that one before im ny life, might try
use konst::slice_concat;
const fn read <'a> (
chars: impl Iterator<Item = char>
) -> Result<Range<'a>, ParseError> {
use Range::*;
let mut state = Range::Nil;
let mut tokens: &[Range<'a>] = &[];
while let Some(c) = chars.next() {
state = match state {
// must begin expression
Nil => match c {
' ' => Nil,
'(' => Exp(&[]),
':' => Sym(&[]),
'1'..'9' => Num(digit(c)),
'a'..'z' => Key(&[&[c]]),
_ => return Err(ParseError::Unexpected(c))
},
Num(b) => match c {
' ' => return Ok(Num(digit(c))),
'1'..'9' => Num(b*10+digit(c)),
_ => return Err(ParseError::Unexpected(c))
}
Sym([]) => match c {
'a'..'z' => Sym(&[c]),
_ => return Err(ParseError::Unexpected(c))
},
Sym([b @ ..]) => match c {
' ' => return Ok(Sym(&b)),
'a'..'z' | '0'..'9' | '-' => Sym(&[..b, c]),
_ => return Err(ParseError::Unexpected(c))
}
Key([[b @ ..]]) => match c {
' ' => return Ok(Key(&[&b])),
'/' => Key(&[&b, &[]]),
'a'..'z' | '0'..'9' | '-' => Key(&[&[..b, c], &[]]),
_ => return Err(ParseError::Unexpected(c))
}
Key([s @ .., []]) => match c {
'a'..'z' => Key(&[..s, &[c]]),
_ => return Err(ParseError::Unexpected(c))
}
Key([s @ .., [b @ ..]]) => match c {
'/' => Key([..s, &b, &[]]),
'a'..'z' | '0'..'9' | '-' => Key(&[..s, &[..b, c]]),
_ => return Err(ParseError::Unexpected(c))
}
// expression must begin with key or symbol
Exp([]) => match c {
' ' => Exp(&[]),
')' => return Err(ParseError::Empty),
':' => Exp(&[Sym(&[':'])]),
c => Exp(&[Key(&[&[c]])]),
},
// expression can't begin with number
Exp([Num(num)]) => return Err(ParseError::Unexpected(c)),
// symbol begins with : and lowercase a-z
Exp([Sym([':'])]) => match c {
'a'..'z' => Exp(&[Sym(&[':', c])]),
_ => return Err(ParseError::Unexpected(c)),
},
// any other char is part of symbol until space or )
Exp([Sym([':', b @ ..])]) => match c {
')' => { tokens = &[..tokens, Exp(&[Sym(&[":", ..b])])]; Nil },
' ' => Exp(&[Sym(&[':', ..b]), Nil]),
c => Exp(&[Sym(&[':', ..b, c])]),
},
// key begins with lowercase a-z
Exp([Key([])]) => match c {
'a'..'z' => Exp([Key([[c]])]),
_ => return Err(ParseError::Unexpected(c)),
},
// any other char is part of key until slash space or )
Exp([Key([[b @ ..]])]) => match c {
'/' => Exp(&[Key(&[[..b], []])]),
' ' => Exp(&[Key(&[[..b]]), Nil]),
')' => { tokens = &[..tokens, Exp(&[Sym(&[":", ..b])])]; Nil },
c => Exp(&[Key(&[[..b, c]])])
}
// slash adds new section to key
Exp([Key([b @ .., []])]) => match c {
'/' => Exp(&[Key(&[[..b], []])]),
' ' => Exp(&[Key(&[[..b]]), Nil]),
')' => { tokens = &[..tokens, Exp(&[Sym(&[":", ..b])])]; Nil },
c => Exp(&[Key(&[[..b, c]])])
}
}
}
Ok(state)
}
*/
/// EDN parsing helper.
#[macro_export] macro_rules! edn {
($edn:ident { $($pat:pat => $expr:expr),* $(,)? }) => {
match $edn { $($pat => $expr),* }
};
($edn:ident in $args:ident { $($pat:pat => $expr:expr),* $(,)? }) => {
for $edn in $args {
edn!($edn { $($pat => $expr),* })
}
};
}
pub trait FromEdn<C>: Sized {
const ID: &'static str;
fn from_edn (context: C, expr: &[Edn<'_>]) ->
std::result::Result<Self, Box<dyn std::error::Error>>;
}
/// Implements the [FromEdn] trait.
#[macro_export] macro_rules! from_edn {
($id:expr => |$context:tt:$Context:ty, $args:ident| -> $T:ty $body:block) => {
impl FromEdn<$Context> for $T {
const ID: &'static str = $id;
fn from_edn <'e> ($context: $Context, $args: &[Edn<'e>]) -> Usually<Self> {
$body
}
}
}
}

12
edn/src/example.edn Normal file
View file

@ -0,0 +1,12 @@
(sized
(bsp/s (fill/x (fixed/y 2 (lay
(align/w :input-meter-l)
(align/e :input-meter-r)
(align/x :transport))))
(bsp/n (row :clip-play :clip-next :clip-edit :edit-stat)
(bsp/n (max/y :sample-h (fill/xy :sample-view))
(bsp/n (align/w (fixed/y 1 :sample-stat))
(bsp/n (fixed/x :pool-w :pool-view)
(fill/xy (bsp/e
(fixed/x :samples-w (push/y :samples-y :samples-view))
:midi-view))))))))

2
edn/src/lib.rs Normal file
View file

@ -0,0 +1,2 @@
mod edn_lib; pub use self::edn_lib::*;
mod edn_layout; pub use self::edn_layout::*;