Hello people,

I made a patch for shakespeare-sass which makes it support splices like the
other shakespeare languages. I'd like to explain why and how because it's not
easy to understand from my still ugly-and-undocumented code.

First, how do these splices work in shakespeare in general?

Answer: Shakespeare has its own small simple parser of Haskell expressions.
Then, each shakespeare language has its own parser, and these parses reuse that
Haskell expression parser to parse the splices. There is additionally a simple
"text with splices" module in shakespeare which allows to embed splices in
general plain text, and it uses that expression parser too.

Now suppose you want to have SASS with splices supported in shakespeare!
Obviously there is a problem: The libsass based SASS parser that can be found
in Hackage, nicely wrapped in the 'hsass' package, is already written, in the C
language! You can't embed the Haskell expression parser into it. So what do you

Well, you have 2 options:

1. Write a SASS parser in Haskell for the shakespeare package
2. Run the 'hsass' parser on SASS sources that contain splices and then process
   the splices

The 'shakespeare-sass' and 'yesod-sass' packages do neither, they simply don't
support Haskell splices at all.

I chose the 2nd option. My first attempt was like this:

1. Compile SASS into CSS using the 'hsass' package
2. Treat the CSS as a Lucius source file and process splices

This failed because the SASS compiler apparently doesn't like shakespeare
splices! If you write "#{avatar}" in your SASS source, the compiled CSS
contains just "avatar". The compiler just strips some characters for reasons
unknown to me. So, what do we do?

I had an idea!

Before running the SASS compiler, I'll run a preprocessor that encodes those
special characters in the splices using just alphanumeric ASCII characters, so
that the SASS compiler doesn't touch them, and after compilcation I'll run a
postprocessor that decodes the splices back to the original form! And *then*
treat the result as a Lucius source.

Ikomi tried this today and it seems to work!

The patch that makes this possible is quite ugly and dirty, I'd like to explain
because it's important for the long term, the code should probably be cleaned
and made safe and elegant:

- Shakespeare 2.0.13 exposes 2 internal modules sue to a GHC bug. My code uses
  one of these modules. A future version is likely to stop exposing them or to
  break its API, a quick solution is to copy functions from those modules into
  my code.
- You may say "wait but copying is ugly", I agree but guess what, there's a lot
  more copying I already had to do. I needed functions from shakespreare that
  it doesn't export. And also shakespeare uses parsec for parsing, and parsec
  doesn't have the 'match' function like attoparsec does, which means I
  couldn't reuse the parsers as much as I wanted to, and needed to write more
  code and copy some from shakespeare too.
- There are many ways to encode strings into clean alphanumeric
  representations. I didn't care much about optimization, I just picked the
  first simple idea I had: Hex. The splices are encoded by converting the
  String to ByteString to the hex representation of the bytes. I considerd base
  64 too, but it has a few symbols (I didn't check if the SASS compiler touches
  them) so I just went for something that doesn't.

Final part: What are my thoughts about making this patch sane and elegant and
future proof?

Well, in shakespeare all the separate language parsers reuse the internals, and
the internals are a mess. The best way to add a new shakespeare language is
probably to include it in the shakespeare package itself. If its maintainer
likes the idea, I can write a patch, and shakespeare will start supporting SASS.

Either way, since I wrote the shakespeare-sass patch, I can keep maintaining it
and making sure it keeps working. I can turn it into a patch to shakespeare
itself like I said, but to make such a patch really clean and elegant I'd need
to modify the internal modules and it would be hard to constantly rebase it on
new shakespeare versions. Due to the shakespeare internals being ugly, really
the best option I see is to get the the code into shakespeare, or perhaps to
clean and stabilize its internals. Maybe start by making shakespeare uses
megaparsec instead of parsec, for example, and make the parsers look less

Design team: Just enjoy the patch and explore :) Ikomi knows how to use it

Dev team: Indeed I need to document my code, either way this email explains the
idea and any further questions and stability and security concerns are very
welcome :)

-- fr33

Attachment: pgpoIlvnSzNHX.pgp
Description: OpenPGP digital signature

Dev mailing list

Reply via email to