seam

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

README.md (6718B)


SEAM

Symbolic Expressions As Markup.

Why

Because all markup is terrible, especially XML/SGML and derivatives.

But mainly, for easier static markup code generation, such as by macros and code includes and such.

Try it out

This may be used as a Rust library, such as from within a server, generating HTML (or any other supported markup) before it is served to the client. Personally, I just use the seam binary to statically generate my personal websites through a Makefile.

Read the USAGE.md file for code examples and documentation.

Current Formats

Installation

You may clone the repo, then build and install by

git clone git://git.knutsen.co/seam
cd seam
cargo build --release
cargo install --path .

Or install it from crates.io

cargo install seam

Either way, you’ll need the Rust (nightly) compiler and along with it, comes cargo.

Using The Binary

You may use it by passing in a file and piping from STDOUT.

seam test.sex --html > test.html

test.sex contains your symbolic-expressions, which is used to generate HTML, saved in test.html.

Likewise, you may read from STDIN

seam --html < example.sex > example.html
# ... same as
cat example.sex | seam --html > example.html

You may also use here-strings or here-docs, if your shell supports it.

seam --html <<< "(p Hello World)"
#stdout:
#   <!DOCTYPE html>
#   <html>
#   <head></head>
#   <body>
#   <p>Hello World</p>
#   </body>
#   </html>
seam --html --nodocument <<< "(p Hello World)"
#stdout:
#   <p>Hello World</p>
seam --xml <<< '(para Today is a day in (%date "%B, year %Y").)'
#stdout:
#   <?xml version="1.0" encoding="UTF-8" ?>
#   <para>Today is a day in November, year 2020.</para>
seam --sexp <<< '(hello (%define subject world) %subject)'
#stdout:
#   (hello world)

Checklist

# SEAM

> **S**ymbolic **E**xpressions **A**s **M**arkup.

## Why

Because all markup is terrible, especially XML/SGML and derivatives.

But mainly, for easier static markup code generation, such as by
macros and code includes and such.

## Try it out

This may be used as a Rust library, such as from within a server,
generating HTML (or any other supported markup) before it is served to the
client.  Personally, I just use the `seam` binary to statically
generate my personal websites through a Makefile.

Read the [USAGE.md](USAGE.md) file for code examples and documentation.

### Current Formats

 - XML (`--xml`; including: SVG, MathML)
 - HTML (`--html`; SGML)
 - CSS (`--css`)
 - SExp (`--sexp`; S-expression, basically a macro expansion utility)
 - Plain Text (`--text`; renders escaped strings to text)

### Installation

You may clone the repo, then build and install by
```sh
git clone git://git.knutsen.co/seam
cd seam
cargo build --release
cargo install --path .
```

Or install it from [crates.io](https://crates.io/crates/seam)
```sh
cargo install seam
```

Either way, you'll need the Rust (nightly) compiler and along
with it, comes `cargo`.

### Using The Binary

You may use it by passing in a file and piping from STDOUT.
```sh
seam test.sex --html > test.html
```

`test.sex` contains your symbolic-expressions, which is used to generate
HTML, saved in `test.html`.

Likewise, you may read from STDIN
```sh
seam --html < example.sex > example.html
# ... same as
cat example.sex | seam --html > example.html
```
You may also use here-strings or here-docs, if your shell
supports it.
```sh
seam --html <<< "(p Hello World)"
#stdout:
#   <!DOCTYPE html>
#   <html>
#   <head></head>
#   <body>
#   <p>Hello World</p>
#   </body>
#   </html>
```
```sh
seam --html --nodocument <<< "(p Hello World)"
#stdout:
#   <p>Hello World</p>
```
```sh
seam --xml <<< '(para Today is a day in (%date "%B, year %Y").)'
#stdout:
#   <?xml version="1.0" encoding="UTF-8" ?>
#   <para>Today is a day in November, year 2020.</para>
```
```sh
seam --sexp <<< '(hello (%define subject world) %subject)'
#stdout:
#   (hello world)
```

## Checklist
 - [ ] 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)`.
 - [x] Sorting macro `(%sort (...))` which sorts alphanumerically on literals.
       Allow providing a `:key` to sort "by field": e.g. sort by title name `(%sort :key (%lambda ((:title _ &&_)) %title) %posts)`
 - [x] Extend the strftime-style `(%date)` to be able to read UNIX numeric timestamps and display relative to timezones.
       Add complementary strptime-style utility `(%timestamp)` to convert date-strings to timestamps (relative to a timezone).
 - [x] Pattern-matching `(%match expr (pat1 ...) (pat2 ...))` macro.
       Pattern matching is already implemented for `%define` internally.
 - [x] The trailing keyword-matching operator. `&&rest` matches excess keyword.
       Extracting a value from a map `(:a 1 :b 2 :c 3)` is done with:
       `(%match %h ((:b default &&_) %b))`.
 - [x] `%get` macro: `(%get b (:a 1 :b 2))` becomes `2`; `(%get 0 (a b c))` becomes `a`.
 - [x] `(%yaml "...")`, `(%toml "...")` and `(%json "...")` converts
       whichever config-lang definition into a seam `%define`-definition.
 - [x] `(%do ...)` which just expands to the `...`; the identity function.
 - [ ] Catch expansion errors: `(%try :catch index-error (%do code-to-try) :error the-error (%do caught-error %the-error))`.
 - [x] Implement `(%strip ...)` which evaluates to the `...` without any of the leading whitespace.
 - [x] Implement *splat* operation: `(%splat (a b c))` becomes `a b c`.
 - [x] `(%define x %body)` evaluates `%body` eagerly (at definition),
       while `(%define (y) %body)` only evaluates `%body` per call-site `(%y)`.
 - [x] Namespace macro `(%namespace ns (%include "file.sex"))` will prefix all definitions in its body with `ns/`, e.g. `%ns/defn`.
       Allows for a customizable separator, e.g. `(%namespace ns :separator "-" ...)` will allow for writing `%ns-defn`.
       Otherwise, the macro leaves the content produced by the body completely unchanged.
 - [x] Command line `-I` include directory.
 - [x] First argument in a macro invocation should have its whitespace stripped.
 - [x] `(%os/env ENV_VAR)` environment variable macro.
 - [ ] Lazy evaluation for *user* macros (like in `ifdef`) with use of new `(%eval ...)` macro.
 - [x] `(%apply name x y z)` macro which is equivalent to `(%name x y z)`.
 - [x] `(%lambda (x y) ...)` macro which just evaluates to an secret symbol, e.g. `__lambda0`.
       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.
 - [ ] `(%formatter/text ...)` can take any seam (sexp) source code, for which it just embeds the expanded code (plain-text formatter).
 - [ ] `(%formatter/html ...)` etc. which call the respective available formatters.
 - [ ] Implement lexical scope by letting macros store a copy of the scope they were defined in (or a reference?).
 - [x] `(%embed "/path")` macro, like `%include`, but just returns the file contents as a string.
 - [x] Variadic arguments via `&rest` syntax.
 - [ ] Type-checking facilities for user macros.
 - [x] `%list` macro which expands from `(%list %a %b %c)` to `( %a %b %c )` but *without* calling `%a` as a macro with `%b` and `%c` as argument.
 - [x] `%for`-loop macro, iterating over `%list`s.
 - [x] `%glob` which returns a list of files/directories matching a glob.
 - [x] `%markdown` renders Markdown given to it as `%raw` html-string.
 - [x] Add keyword macro arguments.
 - [ ] Caching or checking time-stamps as to not regenerate unmodified source files.
 - [ ] HTML object `style="..."` object should handle s-expressions well, (e.g. `(p :style (:color red :border none) Hello World)`)
 - [ ] Add more supported formats (`JSON`, `JS`, `TOML`, &c.).
 - [ ] Allow for arbitrary embedding of code with their REPLs, that can be run by
   a LISP interpreter (or any other language), for example.  (e.g. `(%chez (+ 1 2))` executes
   `(+ 1 2)` with Chez-Scheme LISP, and places the result in the source (i.e. `3`).