第 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 "<" "<")] [s (string-replace s ">" ">")]) 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 让特定领域的问题表达更加自然。