Hi, all. I am working on an embedded DSL for CSS in Racket. I want to get feedback from the community regarding the project’s goals and my choice of tools for implementing it.
LANGUAGE I want to use Racket to generate CSS for me. I am not interested in a “#lang” language, but in a DSL that lives embedded in the code. In particular, I want “quote” and “unquote”. The best I can think of is something like: (define main-background-color #'red) (define the-css #`([body #:background-color #,main-background-color])) (css-expr->string the-css) ;; => "body{background-color:red;}" Note the explicit use of syntax objects. They allow for arbitrary “quote” and “unquote” and preserve source location information, which improves the quality of error messages. This is the best solution I could find, but I recognize it is unusual—I cannot name a single other example of embedded DSL in Racket that operates with syntax objects directly. Can you think of something better? COMPILER FRONT END I want good error messages, so I am using “syntax-parse”. I defined the concrete syntax tree in terms of syntax classes—each production rule on the context-free grammar for the concrete syntax tree is a “syntax-parse” syntax class. For example, the following defines a “stylesheet” as a list of “rules”: (define-syntax-class stylesheet (pattern (rule:rule ...))) The abstract syntax tree are typed structs, because I want to benefit from the correctness guarantees of Typed Racket. For example, the following is a struct for “stylesheets”: (struct stylesheet ([rules : (Listof rule)]) #:transparent) Then, the parser uses “syntax-parse” with the syntax classes to generate the typed structs. For example, the following is a parser rule for “stylesheets”: (define (parse/stylesheet expression) (syntax-parse expression [stylesheet:stylesheet (extended:stylesheet (map parse/rule (syntax->list #'(stylesheet.rule ...))))])) With this approach, I get a combination of good parsing error messages and typed structures to work on the compiler back end. But there is no “syntax-parse” in Typed Racket, so the parser module must be untyped. The interaction between untyped (compiler front end) and typed (compiler back end) modules leads to performance penalties. Overall, the whole architecture feels awkward, I feel like I am fighting the tools. Can you think of a better solution? COMPILER BACK END I use the typed structures in a module that concatenates strings to form CSS. This is the part I am happiest about, but I accept suggestions of more principled approaches than carefully constructed calls to “string-append” and “string-join”. OPINION My inspiration were the DSLs from http://www.cliki.net/CSS. I could not find a Racket equivalent. The following is an example of what the language looks like, recreating the CSS from http://bettermotherfuckingwebsite.com/: ([body {#:margin (40px auto) #:max-width 650px #:line-height 1.6 #:font-size 18px #:color |#444| #:padding (0 10px)}] [h1 h2 h3 {#:line-height 1.2}]) ;; => body{margin:40px auto;… Furthermore, I implemented some ideas that I borrowed from other CSS preprocessors such as SASS/Less. For example, the language supports nested declarations and attributes, appropriately unnesting them when generating the CSS output. What I like about embedding this DSL in Racket is that some of the preprocessors’ features are free. For example, CSS variables and mixins are just “unquote” in Racket. I am using this DSL to build my website in Pollen, and so far I am happy with the results. I am envisioning more principled approach to CSS than concatenating strings, which other (excellent) projects do. For example, see Typography for Lawyers at https://github.com/mbutterick/pollen-tfl/blob/1267be5bd002d4d0fe09aaef89b4089147128841/styles.css.pp. Is this something that interests you too? How do you think I can make this project better and more useful to the community? * * * Thank you very much for your attention and for the feedback. -- You received this message because you are subscribed to the Google Groups "Racket Users" group. To unsubscribe from this group and stop receiving emails from it, send an email to racket-users+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.