Hacker News new | ask | show | jobs
by chriswarbo 922 days ago
> I wonder whether there's something similar that executes code fences in markdown?

I wrote a script to do this called panpipe: https://github.com/warbo/panpipe

It uses Pandoc, so it supports many formats (I've personally used it with Markdown code fences and with HTML <code> elements). The idea is pretty simple and powerful: if a code block has an attribute/annotation like `pipe="foo"` then that value is executed as a shell command, and the contents of the code block gets piped through it and replaced by the stdout.

This supports running basically all programming languages, by giving their interpreter as the value of `pipe`. I use this for my web site ( chriswarbo.net ) to execute code in PHP, Haskell, Racket, Bash, Coq, etc. It can also run things indirectly, e.g. if you want to compile and run C you could do something like this:

  Here is my C program:

  ```{pipe="tee myfile.c"}
  int main() etc etc.
  ```

  Here is its output:

  ```{pipe="sh"}
  gcc -o myfile myfile.c 1>&2 || exit 1
  ./myfile
  ```
A more scalable alternative, for documents which will run many such snippets, is to write a helper script to use as our command, e.g.

  ```{pipe="cat > runC && chmod +x runC"}
  #!/usr/bin/env bash
  set -e
  cat > myfile.c
  gcc -o myfile myfile.c 1>&2
  ./myfile
  ```

  Here is the output of my C program:

  ```{pipe="./runC"}
  int main() etc. etc.
  ```
I've also written a companion script called panhandle: https://github.com/warbo/panhandle

Panhandle allows the contents of a code block (annotated with class `unwrap`) to be spliced into its containing document. This is useless on its own (since we could just write that part of the document directly, rather than putting it inside a code block); but it allows us to generate markup using panpipe, and insert it into the document. For example, the following is a HTML page containing a <p> element and a <code> element; panpipe can execute the contents of the <code> element, which generates HTML for an <img> element, but that HTML is stuck as the text of a <code> element. Panhandle lets us "unwrap" it, to become a real <img> element in the document:

  <p>Here is a checkerboard:</p>

  <code class="unwrap" pipe="sh | pandoc -f html -t json">
   printf '&lt;img src="data:image/png;base64,'
   {
    echo "P1 101 100"
    for N in $(seq 10100); do echo $(( N % 2 )); done
   } > checker.pbm
   convert checker.pbm checker.png
   base64 -w0 < checker.png
   printf '" /&gt;'
  </code>
Note that panhandle needs to parse the content of a code block in order to unwrap it; to keep things simple and unambiguous it only supports Pandoc JSON format (a serialisation of Pandoc's internal AST). Since the above shell code generates HTML, we pipe it through `pandoc -f html -t json` to get the required JSON format.

This whole approach is described at http://www.chriswarbo.net/projects/activecode

1 comments

Oh wow that looks really neat. I'm using Pandoc a lot anyways, so this might fit nicely into my workload. Thanks for sharing!
Pandoc even poses codeblock execution as an exercise in their custom-filter documentation: https://pandoc.org/filters.html The python package panflute is also really nice if you don't want to play around with haskell or the AST JSON directly.