commit 058b03c670ed178b1024902ac8d390067a7bacf8
parent 97b8cb9233805976bb2e91d36dd42b3799f3a160
Author: Demonstrandum <samuel@knutsen.co>
Date: Fri, 13 Dec 2024 23:25:12 +0000
fix: expand symbol macros recursively.
Diffstat:
4 files changed, 19 insertions(+), 4 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
@@ -337,7 +337,7 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
[[package]]
name = "seam"
-version = "0.4.2"
+version = "0.4.3"
dependencies = [
"chrono",
"colored",
diff --git a/README.md b/README.md
@@ -91,6 +91,22 @@ seam --sexp <<< '(hello (%define subject world) %subject)'
```
## Checklist
+ - [ ] Literate mode for parser+lexer, where string nodes are not escaped and parentheses are converted to symbols.
+ The only time strings are escaped and parentheses make lists is inside a `(%` and `)` pair, i.e. when calling a macro.
+ So `hello world (earth) (%do (p letter "\x61")) "\x61"` turns in to (in HTML mode)
+ `hello world <earth></earth> <p>hi a</p> a` normally, but in *literate* (HTML) mode turns into
+ `hello world (earth) <p>letter a</p> "\x61"`. Parentheses and quotes have been preserved.
+ Markdown source in `(%markdown ...)` should be parsed as literate files by default.
+ Provide command line `--literate` option; `%include` and `%embed` should also have options for enabling literate mode.
+ - [ ] Way to match on unknown keywords in attributes, examples when `(%define dict (:a 1 :b 2 :c 3))`:
+ - `(%for (kw val) in (%items %dict) (%log %kw = %val))`
+ - `(%for kw in (%keys %dict) (%log %kw = (%get %kw %dict)))`
+ - `(%for val in (%values %dict) (%log ___ = %val))`
+ - [ ] Extend `%get` to work with slicing `(%get (1 3) (a b c d e))` becomes `b c d`; negative indices `(%get -1 (a b c))` becomes `c`.
+ - [ ] Shell-style `${var:...}` string manipulation.
+ - [ ] `%while`, `%take`, `%drop`, `%split` on symbols in lists, `%intercalate`.
+ - [ ] `(%basename :suffix "txt" /path/file.txt)` (-> `file`), `(%dirname /path/file.txt)` (-> `/path`) and `(%extension /path/file.txt)` (-> `txt`), macros for paths.
+ - [ ] Math operators: `+`, `-`, `*`, `/`, `mod`, `pow`, `exp`, `sqrt`, `log`, `ln`, `hypot`.
- [ ] User `(%error msg)` macro for aborting compilation.
- [x] List reverse macro `(%reverse (...))`.
- [x] Literal/atomic conversion macros: `(%symbol lit)`, `(%number lit)`, `(%string lit)`, `(%raw lit)`.
@@ -124,7 +140,6 @@ seam --sexp <<< '(hello (%define subject world) %subject)'
used by applying `%apply`, e.g. `(%apply (%lambda (a b) b a) x y)` becomes `y x`
- [x] `(%string ...)`, `(%join ...)`, `(%map ...)`, `(%filter ...)` macros.
- [x] `(%concat ...)` which is just `(%join "" ...)`.
- - [ ] `(%basename)`, `(%dirname)` and `(%extension)` macros for paths.
- [x] Add options to `%glob` for sorting by type, date(s), name, etc.
- [x] `(%format "{}")` macro with Rust's `format` syntax. e.g. `(%format "Hello {}, age {age:0>2}" "Sam" :age 9)`
- [x] Add `(%raw ...)` macro which takes a string and leaves it unchanged in the final output.
diff --git a/crates/seam/Cargo.toml b/crates/seam/Cargo.toml
@@ -5,7 +5,7 @@ keywords = ["markup", "lisp", "macro", "symbolic-expression", "sexp"]
license-file = "../../LICENSE"
readme = "../../README.md"
homepage = "https://git.knutsen.co/seam"
-version = "0.4.2"
+version = "0.4.3"
authors = ["Demonstrandum <samuel@knutsen.co>"]
edition = "2021"
diff --git a/crates/seam/src/parse/expander.rs b/crates/seam/src/parse/expander.rs
@@ -1780,7 +1780,7 @@ impl<'a> Expander<'a> {
and cannot be used as a variable.", name),
&sym.site))
}
- let mut expanded = def.body.clone();
+ let mut expanded = self.expand_nodes(def.body.clone())?;
// Inherit the whitespace of the call-site.
if let Some(first) = expanded.first_mut() {
first.set_leading_whitespace(sym.leading_whitespace.to_owned());