Symbolic Expressions As Markup.
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.
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.
- XML (
--xml; including: SVG, MathML) - HTML (
--html; SGML) - CSS (
--css) - JSON (
--json) - JavaScript (
--js; written as s-expressions) - SExp (
--sexp; S-expression, basically a macro expansion utility) - Plain Text (
--text; renders escaped strings to text)
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 seamEither way, you'll need the Rust (nightly) compiler and along
with it, comes cargo.
You may use it by passing in a file and piping from STDOUT.
seam test.sex --html > test.htmltest.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.htmlYou 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)To run a file only for what its macros do, such as writing files with
%write, use seam run, which reports each action like make does:
seam run build.sex
#stdout:
# write public/index.html
# ...- 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. Sohello world (earth) (%do (p letter "\x61")) "\x61"turns in to (in HTML mode)hello world <earth></earth> <p>hi a</p> anormally, but in literate (HTML) mode turns intohello 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--literateoption;%includeand%embedshould 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
%getto work with slicing(%get (1 3) (a b c d e))becomesb c d; negative indices(%get -1 (a b c))becomesc. - Shell-style
${var:...}string manipulation, written%{var:...}. -
%while,%take,%drop,%spliton 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. -
%path/...macros:cwd,this,here,join,split,stem,with-extension,normalise,canonical,absolute,relative,exists,is-fileandis-directory. -
seam run file.sex, running a file for its side effects (%write,%exec), reported on STDOUT likemake. - Math operators:
+,-,*,/,mod,pow,exp,sqrt,ln,log2,log10,hypot(%logalready logs), along with comparisons,%if,%and,%or,%notand%range. - User
(%error msg)macro for aborting compilation. - List reverse macro
(%reverse (...)). - Literal/atomic conversion macros:
(%symbol lit),(%number lit),(%string lit),(%raw lit). - Sorting macro
(%sort (...))which sorts alphanumerically on literals. Allow providing a:keyto sort "by field": e.g. sort by title name(%sort :key (%lambda ((:title _ &&_)) %title) %posts) - 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). - Pattern-matching
(%match expr (pat1 ...) (pat2 ...))macro. Pattern matching is already implemented for%defineinternally. - The trailing keyword-matching operator.
&&restmatches excess keyword. Extracting a value from a map(:a 1 :b 2 :c 3)is done with:(%match %h ((:b default &&_) %b)). -
%getmacro:(%get b (:a 1 :b 2))becomes2;(%get 0 (a b c))becomesa. -
(%yaml "..."),(%toml "...")and(%json "...")converts whichever config-lang definition into a seam%define-definition. -
(%do ...)which just expands to the...; the identity function. - Catch expansion errors:
(%try (%do code-to-try) :error the-error (%do caught-error %the-error)). - Implement
(%strip ...)which evaluates to the...without any of the leading whitespace. - Implement splat operation:
(%splat (a b c))becomesa b c. -
(%define x %body)evaluates%bodyeagerly (at definition), while(%define (y) %body)only evaluates%bodyper call-site(%y). - Namespace macro
(%namespace ns (%include "file.sex"))will prefix all definitions in its body withns/, 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. - Command line
-Iinclude directory. - First argument in a macro invocation should have its whitespace stripped.
-
(%os/env ENV_VAR)environment variable macro. - Lazy evaluation for user macros (like in
ifdef):~paramparameters are expanded where they are used. Code can also be kept as data with(%quote ...), and expanded with(%eval ...). -
(%apply name x y z)macro which is equivalent to(%name x y z). -
(%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)becomesy x -
(%string ...),(%join ...),(%map ...),(%filter ...)macros. - Quasiquotation,
(%quasi (a (%unquote %x) (%splice %xs))), and(%read "..."). - Inspecting and building nodes:
%type,%list?(etc.),%attribute,%dict. - List functions taking lambdas:
%map,%filter,%fold,%find,%any,%all,%zip,%enumerate,%flatten,%group-by,(%sort :by ...). - Walking trees:
(%walk f tree)and(%select h2 tree). - Tools for writing macros:
%gensym,%definedand%macroexpand. -
(%concat ...)which is just(%join "" ...), or joins lists. - Lazy lists, internally: ranges, slices, reversals and concatenations are made as needed, and
%fornever makes a whole list. - Add options to
%globfor sorting by type, date(s), name, etc. -
(%format "{}")macro with Rust'sformatsyntax. e.g.(%format "Hello {}, age {age:0>2}" "Sam" :age 9) - 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 reference to the scope they were defined in.
-
(%with (:name value ...) ...)dynamically overrides top-level definitions within its body, e.g. for passing page metadata through to a shared layout macro. -
(%embed "/path")macro, like%include, but just returns the file contents as a string. - Variadic arguments via
&restsyntax. - Type-checking facilities for user macros.
-
%listmacro which expands from(%list %a %b %c)to( %a %b %c )but without calling%aas a macro with%band%cas argument. -
%for-loop macro, iterating over%lists. -
%globwhich returns a list of files/directories matching a glob. -
%markdownrenders Markdown given to it as%rawhtml-string. - Add keyword macro arguments.
- Caching or checking time-stamps as to not regenerate unmodified source files:
(%write path :from (sources...) ...), and unchanged files are not rewritten. - HTML object
style="..."object should handle s-expressions well, (e.g.(p :style (:color red :border none) Hello World)) - JSON output (
--json,%formatter/json), the reverse of%json. - JavaScript output (
--js,%formatter/js), written as s-expressions:(const f (=> (x) (* x x))). - Add more supported formats (
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). Done with%exec:(%define (chez ~&code) (%exec scheme --quiet :stdin (%formatter/sexp %code))).