seam

Symbolic-Expressions As Markup.
git clone git://git.knutsen.co/seam
Log | Files | Refs | README | LICENSE

commit d712e4897a92db63d5737ae8500fc2e10100d390
parent 2748bee7863eec92d6fad7df5462766a3cde3560
Author: Demonstrandum <samuel@knutsen.co>
Date:   Sat,  7 Dec 2024 22:35:24 +0000

Added %toml support.

Diffstat:
MCargo.lock | 97++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Mcrates/seam/Cargo.toml | 1+
Mcrates/seam/src/parse/expander.rs | 145++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
Mcrates/seam/src/parse/parser.rs | 6+++---
Mcrates/seam/src/parse/tokens.rs | 4++--
5 files changed, 235 insertions(+), 18 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock @@ -117,6 +117,12 @@ dependencies = [ ] [[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] name = "formatx" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -138,12 +144,18 @@ dependencies = [ ] [[package]] +name = "hashbrown" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" + +[[package]] name = "hashlink" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" dependencies = [ - "hashbrown", + "hashbrown 0.14.5", ] [[package]] @@ -170,6 +182,16 @@ dependencies = [ ] [[package]] +name = "indexmap" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" +dependencies = [ + "equivalent", + "hashbrown 0.15.2", +] + +[[package]] name = "js-sys" version = "0.3.72" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -285,6 +307,7 @@ dependencies = [ "markdown", "regex", "seam_argparse_proc_macro", + "toml", "unicode-width", "yaml-rust2", ] @@ -299,6 +322,35 @@ dependencies = [ ] [[package]] +name = "serde" +version = "1.0.215" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.215" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_spanned" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +dependencies = [ + "serde", +] + +[[package]] name = "shlex" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -316,6 +368,40 @@ dependencies = [ ] [[package]] +name = "toml" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] name = "unicode-id" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -534,6 +620,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] +name = "winnow" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" +dependencies = [ + "memchr", +] + +[[package]] name = "yaml-rust2" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/crates/seam/Cargo.toml b/crates/seam/Cargo.toml @@ -32,3 +32,4 @@ regex = "1.10.5" glob = "0.3.1" markdown = "1.0.0-alpha.21" yaml-rust2 = "0.9.0" +toml = "0.8.19" diff --git a/crates/seam/src/parse/expander.rs b/crates/seam/src/parse/expander.rs @@ -697,7 +697,7 @@ impl<'a> Expander<'a> { extract_frontmatter()?; Ok(Box::new([])) }, - Only::Content => to_html(), + Only::Content => to_html(), Only::Both => { // Ignore any errors if no frontmatter exists. let _ = extract_frontmatter(); @@ -1438,15 +1438,6 @@ fn expand_yaml<'a>(context: &Expander<'a>, text: &str, sep: &str, site: &Site<'a impl<'a, 'b> yaml::parser::EventReceiver for EventSink<'a, 'b> { fn on_event(&mut self, event: yaml::Event) { - /* - eprintln!("---"); - eprintln!("event: {:?}", event); - eprintln!("mode: {:?}", self.mode); - eprintln!("defn: {:?}", self.defining); - eprintln!("prefix: {:?}", self.prefix); - eprintln!("nodes: [{}]", self.nodes.iter().map(|node| node.to_string()).collect::<Vec<String>>().join("; ")); - eprintln!("parent: {:?}", self.parent); - */ let the_dreaded_rparen = crate::parse::tokens::Token::new( crate::parse::tokens::Kind::RParen, ")", "", self.site.clone() @@ -1606,7 +1597,7 @@ fn expand_yaml<'a>(context: &Expander<'a>, text: &str, sep: &str, site: &Site<'a yaml::parser::Parser::new_from_str(text) .load(&mut sink, false) .map_err(|err| ExpansionError( - format!("Failed to parse yaml: {}", err), + format!("Failed to parse YAML: {}", err), site.to_owned() ))?; @@ -1615,5 +1606,135 @@ fn expand_yaml<'a>(context: &Expander<'a>, text: &str, sep: &str, site: &Site<'a /// See [`expand_yaml`], but for the TOML configuration language instead. fn expand_toml<'a>(context: &Expander<'a>, text: &str, sep: &str, site: &Site<'a>) -> Result<ParseTree<'a>, ExpansionError<'a>> { - Ok(Box::new([])) + use toml; + + let table: toml::Table = text.parse() + .map_err(|err| ExpansionError( + format!("Failed to parse TOML: {}", err), + site.to_owned() + ))?; + + struct Traverser<'a, 'b> { + context: &'b Expander<'a>, + site: Site<'a>, + sep: String, + prefix: Vec<String>, + the_dreaded_rparen: crate::parse::tokens::Token<'a>, + } + + fn whitespace(leading: bool) -> String { + if leading { String::new() } else { String::from(" ") } + } + + impl<'a, 'b> Traverser<'a, 'b> { + fn qualified(&self, name: &str) -> String { + if self.prefix.is_empty() { + name.to_owned() + } else { + let prefix = self.prefix.join(&self.sep); + format!("{}{}{}", prefix, self.sep, name) + } + } + + fn define(&self, name: String, node: ParseNode<'a>) { + let qualified = self.qualified(&name); + self.context.insert_variable(qualified, Rc::new(Macro { + name, + params: Box::new([]), + body: Box::new([node]), + })); + } + + fn traverse_value(&mut self, value: toml::Value, leading: bool) -> ParseNode<'a> { + match value { + toml::Value::Array(array) => self.traverse_array(array, leading), + toml::Value::Table(table) => self.traverse_table(table, leading), + toml::Value::String(string) => ParseNode::String(Node { + value: string, + site: self.site, + leading_whitespace: whitespace(leading), + }), + toml::Value::Integer(int) => ParseNode::Number(Node { + value: format!("{}", int), + site: self.site, + leading_whitespace: whitespace(leading), + }), + toml::Value::Float(float) => ParseNode::Number(Node { + value: format!("{}", float), + site: self.site, + leading_whitespace: whitespace(leading), + }), + toml::Value::Datetime(date) => ParseNode::Number(Node { + value: format!("{}", date), + site: self.site, + leading_whitespace: whitespace(leading), + }), + toml::Value::Boolean(boolean) => ParseNode::Number(Node { + value: format!("{}", boolean), + site: self.site, + leading_whitespace: whitespace(leading), + }), + } + } + + fn traverse_array(&mut self, array: Vec<toml::Value>, leading: bool) -> ParseNode<'a> { + let mut first = true; + let mut expanded = vec![]; + let mut i = 0; + for value in array { + let name = format!("{}", i); + self.prefix.push(name.clone()); + let node = self.traverse_value(value, first); + self.prefix.pop(); + self.define(name, node.clone()); + expanded.push(node); + first = false; + i += 1; + } + ParseNode::List { + nodes: expanded.into_boxed_slice(), + site: self.site, + end_token: self.the_dreaded_rparen, + leading_whitespace: whitespace(leading) + } + } + + fn traverse_table(&mut self, table: toml::Table, leading: bool) -> ParseNode<'a> { + let mut nodes = vec![]; + let mut first = true; + for (keyword, value) in table { + self.prefix.push(keyword.clone()); + let node = self.traverse_value(value, false); + self.prefix.pop(); + self.define(keyword.clone(), node.clone()); + nodes.push(ParseNode::Attribute { + keyword, + node: Box::new(node), + site: self.site, + leading_whitespace: whitespace(first), + }); + first = false; + } + let nodes = nodes.into_boxed_slice(); + ParseNode::List { + nodes, + site: self.site, + end_token: self.the_dreaded_rparen, + leading_whitespace: whitespace(leading) + } + } + } + + Ok(Box::new([ + Traverser { + context, + site: *site, + prefix: Vec::new(), + sep: sep.to_owned(), + the_dreaded_rparen: crate::parse::tokens::Token::new( + crate::parse::tokens::Kind::RParen, + ")", "", *site + ), + }.traverse_table(table, true) + ])) } diff --git a/crates/seam/src/parse/parser.rs b/crates/seam/src/parse/parser.rs @@ -223,9 +223,9 @@ impl<'a> ParseNode<'a> { Self::Symbol(node) | Self::Number(node) | Self::String(node) - | Self::Raw(node) => node.site.clone(), - Self::List { site, .. } => site.clone(), - Self::Attribute { site, .. } => site.clone(), + | Self::Raw(node) => node.site, + Self::List { site, .. } => *site, + Self::Attribute { site, .. } => *site, } } diff --git a/crates/seam/src/parse/tokens.rs b/crates/seam/src/parse/tokens.rs @@ -5,7 +5,7 @@ use unicode_width::UnicodeWidthStr; /// Including references to the source-code and path, line number, bytes offsets /// within the file, including from start of line, and the number of /// bytes it occupies in the source. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Copy)] pub struct Site<'a> { pub source: &'a str, pub source_code: &'a str, @@ -128,7 +128,7 @@ pub enum Kind { Keyword, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Copy)] pub struct Token<'a> { pub kind: Kind, pub value: &'a str,