This is my exploration into making a markup language, mostly for blog articles
Some vague properties that I'd like the language to eventually have:
- Uniform — syntax and semantics comprise a few simple rules
- Fractal — it's easy to create bigger, more complex layouts from smaller building blocks; and especially to reuse a single definition in multiple places
- Narrow in scope — this is not your tool of choice if you want to typeset a beautiful book on bleeding edge mathematics
-
Mathematical functions (parentheses, fractions, indices etc.)
- proof of concept, works for simple stuff
- fix layout bug with using a fraction as a subscript/superscript
- generally make sub, sup & frac play better together
-
Better code editor, probably something with "custom elements" and CodeMirror 6
-
User-provided functions
- First-order functions
- Passing functions by argument
- Capturing closures
-
Possibly export the "reduced" article as an intermediate data structure
This is needed for alternative frontends (e.g. buildling a static HTML site)
- Making a 'table of contents'
- Generally figure out how to 'wire' stuff inside an article
-
Calling an external resource (like HTML)
-
Some basic data structures
- lists
- maps
The project is in its early infancy, but it seems to kinda work.
- Copy the sample markup from
./examples/sample.markup
in the root of this repository - Go to https://decorator-factory.github.io/elm-markup-language-demo
- Paste the sample markup into the code editor
- Scroll down to see the rendered page
A program, or rather an "article" consists of a single expression. An expression is either:
- a text literal, like
"Hello, world!"
- a name, like
emdash
. A name should match/[%a-z][-]/
- a function call, consisting of a function name followed by 0 or more arguments, like:
(para "a particularly " (bold "bold statement") ".")
)
Additionally, you can use this shorthand for interleaving:
(para
| I am about to make a particularly $(bold "bold") statement. A $(italic "very" (bold "bold")) one.
| Last time this "statement" was made everyone in the room got teleported to $(italic "Rome").
)
The above means the same as:
(para
"I am about to make a particularly " (bold "bold") "statement. A " (italic "very" (bold "bold")) "one. "
"Last time this \"statement\" was made everyone in the room got teleported to " (italic "Rome") ". "
)
"Calling" a string with zero arguments produces that string exactly. That's a conscious decision to make stuff like this possible:
(para
| I'm gonna pop some tags
| Only got $(dollar)20 in my pocket
)
string
--- a chunk of Unicoe text.block
--- a rectangular element. (Similar to "block elements" in HTML/CSS)inline
--- an inline element, something that can "flow" as part of a paragraph. A string is considered an inline element as well. (Similar to "inline elements" in HTML/CSS)function
--- something you can call.
I will be using the term vis
(short for "visual element") to mean "inline
or block
".
A definition like (aside string vis)
means that aside
is a function
accepting two arguments: a string and a visual element.
A definition like (link string ...inline)
means that link
is a function
accepting at least one argument, where the first argument is a string and the others are inline elements.
The string "—"
The string "$"
A top-level node for your article. Will arrange the items in a column.
Currently this is the same as
col
Structures elements in a vertical column
Structures elements in a horizontal row
A bulleted list, something like:
- foo
- bar
- baz
A paragraph. This is a "bridge" between the world of inline elements and block elements.
Inline monospace text, like Foo Bar
.
A block of code lines. Will arrange each element as its own line.
Be careful with interpolating anything into a code-block
with |
and $
, because
a line like | Foo $(emdash) the $(bold "best") baz in the biz
actually consists of 5 expressions,
which will render as 5 lines.
You may want to use the cc
function in this case.
Currently the same as `italic
A note displayed to the side. See `examples/sample.markup for an example.
Define a place you can jump to using a link
.
(anchor "chapter-1"
(col
(para ...)
(para ...)
(code-block ...)
(para ...)))
A clickable link that teleports you to an anchor
with the same "tag".
(col
(bold "Table of contents")
(bulleted-list
(link "chapter-1" "1. Foo")
(link "chapter-2" "2. Bar")
(link "chapter-3" "3. Baz")))
Everything inside a comment is ignored. It has to be syntactically valid, though.
(comment
| TODO: It would be nice if we had a 'cool' function which would be
| like `col` but like... cooler
(cool
(bold "A very cool table of contents")
(bulleted-list
(link "chapter-1" "1. Foo")
(link "chapter-2" "2. Bar")
(link "chapter-3" "3. Baz"))))
(row
"Quack"
(image
"https://upload.wikimedia.org/wikipedia/commons/5/51/Mandarin.duck.arp.jpg"))
Concatenate inline elements into one element.
(col
"Shopping list:"
(bullet-list
(cc "Apples")
(cc (italic "Fresh") "bananas")
(cc "Cherries")
(cc "Dates (do " (bold "not") " buy the brand " (italic "Foo"))))
Define a name to refer to a particular value.
Example:
(article
(def product-name
"AmazingWare")
(def product-with-version
(cc product-name "v1.0"))
(para
| Thanks for using $product-with-version!
| If you are stuck, please contact the $product-name Support Team.
))
Output:
Thanks for using AmazingWare v1.0! If you are stuck, please contact the AmazingWare Support Team.
Create a function. A function is essentially a "sub-document" or an "expression" with some "placeholders" missing. Those placeholders are called arguments.
Functions are useful when:
-
You want to extract the commonl repeated bits from your document, to make the source code more concise and to not have to change the same thing across many places.
-
You want to configure the behaviour of another function (see
twice
andsarcasm
for examples)
Example:
(article
(def section
(fun (%title)
(row
(col "")
(col (bold %title))
(col ""))))
(def twice
(fun (%func %arg)
(%func (%func %arg))))
(def sarcasm
(fun (%word)
(italic
(twice
(fun (%s) (cc "\"" %s "\""))
%word))))
(section "1. Foo")
(para "Lorem ipsum...")
(section "2. Foo")
(para "Dolor sit amet...")
(para
| What an $(sarcasm "interesting") article.
| I'm sure everyone $(sarcasm "enjoyed") reading it.
))
Note that Closures are not implemented yet. We're working on that.
A shorthand for def
and fun
. This is what you should use instead
of the code in the fun
example:
(article
(defun (section %title)
(row
(col "")
(col (bold %title))
(col "")))
(defun (twice %func %arg)
(%func (%func %arg)))
(defun (sarcasm %word)
(italic
(twice
(fun (%s) (cc "\"" %s "\""))
%word)))
(section "1. Foo")
(para "Lorem ipsum...")
(section "2. Foo")
(para "Dolor sit amet...")
(para
| What an $(sarcasm "interesting") article.
| I'm sure everyone $(sarcasm "enjoyed") reading it.
))
Vertical fraction, like (frac "22" "7")
or (frac "x + 1" "x")
A version of frac
that doesn't scale down the numerator and the denominator.
Superscript, like (sup "x" "2")
Subscript, like (cc (sub "item" "i") ".." (sub "item" "j"))
Wrap expressions in parentheses. For example, these are the same:
(cc (gr "4" "+" (sup "x" "2")) "-" "y" )
(cc (cc "(" "4" "+" (sup "x" "2") ")" ) "-" "y" )
Why is this needed? Because:
- the language will help you balance your parentheses. No more searching for a forgotten
\right)
.- in the future, I might copy the LaTeX feature of making parentheses slightly smaller every time you nest deeper.
TODO: make it possible to do this in a user-defined function. This is a useful construct for all kinds of brackets.