The big one is probably that tagrenderers can output blocks such as tables. We also slurp sections, so that subsections become a part of the above sections children. The FSI evaluator adds about 1000% of runtime for testing, so I added a NullEvaluator. I also added default constructors for many of the base elements. The parsers are not yet done, but you can do very rudimentary tables. The AST has gotten a large update, and it is pretty much complete. |
||
|---|---|---|
| src | ||
| Fibble.slnx | ||
| Readme.md | ||
Fibble - make your own markup language in f#
This is not quite, but also not completely unlike, scribble, the documentation language of the Racket project. Scribble reads your text document as code and when you run it it produces a document. Fibble does not do that, but it instead reads the document and runs commands formatted like so: @command[argument, arguments]{text}.
As an example, if you are too lazy to write your own table formatting, you can just pipe it to pandoc and let pandoc's markdown parser de the works like so:
Here is som text, @bold{some bold text}, @link[www.example.com]{some linked text}, and here is a table:
@pandoc_markdown{
who where with what
--- ----- ---------
Dan home a carrot
Mil library an axe
Tony office a hamburger
}
This gets parsed and executed and turned into a tree. This tree is fed to whatever writer you want. A markdownwriter, a HTMLwriter and so on.
code
You can also write code in your document:
---
title: a cluedo mystery
author: Boaty McBoatface
note: metadata is written using yaml headers and can be accessed by all commands.
---
@"""
let suspects = ["Linda"; "Mark"; "Dan";"Tony";"Mil"]
let murder_victim = "Sad Penguin"
let murder_weapon = "christmas tree"
let mutable current = 0
let mutable suspect = suspects[current]
let next_suspect () =
current <- current+1
suspect <- suspects.[current]
let tell_the_rest_to_go_home () =
List.skip current+1 suspects
|> List.iter (printfn "%s, go home\n\n")
"""
@section{@value[title]}
There are many suspects. The first one is @(suspect).
@bold{interrogator:} So @(suspect): your hair is full of spruce needles. You are going to jail
@bold{@(suspect):} I don't care as long as @(victim) burns in hell!
@(tell_the_rest_to_go_home())
A command is defined as a function that takes three arguments. metadata is a Map<string, string>. arguments is (string * string) list, and children is InlineNode list. There are also helpers to make sure that you can define a function that only takes positional arguments and helpers to let you extract values from the arguments if you chose to not use positional arguments or mix positional and non-positional arguments.
Commands are defined in the prelude, which is just a mapping of "command", function. If you want to produce HTML, and want to have a @bold{text here} command, you would write it like this: (fun _ _ children -> Element("b", [], children)) and then make sure that your htmlwriter converts that element("b" ...) to the correct html tag.
Why?
Because it is fun and I always end up having to work around limitations of markdown and asciidoc. This lets me tailor every document to my needs.
Todo
error reporting. add some more writers, add a command to parse tables natively, one where you manually write @table{ @row{...} @row{...}} and one where I can parse my own table format into a proper table.