Racket 编程

4 部分 · 语言设计

实战:设计一门 DSL

实战:设计一门 DSL

综合运用宏和语言创建技术,设计并实现一门领域特定语言。

目标:构建一个 HTML 生成 DSL

我们将创建一门简洁的 DSL,让 HTML 生成变得直观:

;; 我们想要的效果
(html
 (head (title "My Page"))
 (body
  (h1 "Hello")
  (p "This is a " (strong "cool") " DSL")
  (ul (li "Item 1") (li "Item 2"))))

第一步:核心宏

#lang racket

(define (html-escape s)
  (let* ([s (string-replace s "&" "&")]
         [s (string-replace s "<" "&lt;")]
         [s (string-replace s ">" "&gt;")])
    s))

(define (render-node node)
  (match node
    [(? string?) (html-escape node)]
    [(list tag children ...)
     (define inner (string-join (map render-node children) ""))
     (format "<~a>~a</~a>" tag inner tag)]
    [_ (format "~a" node)]))

(define-syntax html-dsl
  (syntax-rules ()
    [(_ body ...)
     (render-node '(body ...))]))

第二步:添加属性支持

(define (render-node node)
  (match node
    [(? string?) (html-escape node)]
    [(list '*attrs* attrs body ...)
     (define attr-str
       (string-join
        (for/list ([(k v) (in-hash attrs)])
          (format " ~a=\"~a\"" k (html-escape v)))
        ""))
     (define inner (string-join (map render-node body) ""))
     ...]
    ...))

第三步:整合为 #lang

;; html-dsl/lang/reader.rkt
;; 让用户可以直接用 #lang html-dsl

扩展方向

  • 添加 CSS 生成
  • 支持模板继承
  • 添加条件渲染
  • 编译期验证 HTML 结构

小结

通过这个实战项目,你体验了从宏到 #lang 的完整语言设计流程。DSL 让特定领域的问题表达更加自然。