如何在Clojure中实现类似继承的语法解析器代码复用能力
你可以通过Clojure的动态var绑定特性实现完全和Scala方案同等的代码复用率,仅需要修改新增必要的规则即可生成新方言的解析器,不需要复制全量语法代码,也不需要实现继承体系。
实现思路
Clojure的var默认是静态绑定的,给需要扩展修改的语法规则添加^:dynamic元标记后,就可以通过binding块在运行时覆盖该规则的定义,所有依赖该规则的上层语法会自动使用覆盖后的版本,和Scala子类重写父类方法的效果完全一致。
改造步骤
- 给基础版本中需要被扩展的规则添加
^:dynamic元标记,其他静态规则无需改动:
(def EQU (token (literal "="))) (def COMMA (token (literal ","))) (def LPAR (token (literal "("))) (def RPAR (token (literal ")"))) (def LBKT (token (literal "["))) (def RBKT (token (literal "]"))) (def IN (token (literal "X"))) (def OUT (token (literal "Y"))) (def IF (token (literal "if"))) (def ELSE (token (literal "else"))) (def END (token (literal "end"))) (def COLON (token (literal ":"))) (def ident (token (regex #"[_a-zA-Z$][_a-zA-Z0-9'$]*"))) (def number (token (regex #"[-+]?[0-9]+"))) (def variable ident) (def index number) (def input (do* IN LBKT index RBKT)) (def output (do* OUT LBKT index RBKT)) ;; 给需要扩展的核心规则加动态标记 (def ^:dynamic expr (choice input output variable)) (def actual-args (sep-by expr COMMA)) (def funcall (do* ident LPAR actual-args RPAR)) (def reference (either output variable)) (def assign (do* reference EQU funcall)) (def ^:dynamic statement assign) (def command (do* (optional statement) (many eol))) (def ^:dynamic block (many command)) (def program (many command))
- 实现内联函数方言:仅覆盖
expr规则即可,其他规则完全复用基础版本
(defn inline-parser [] (binding [expr (choice input output variable funcall)] (fn [input-str] (program input-str))))
使用时直接调用((inline-parser) 输入代码)即可解析带内联函数的方言。
- 实现if/else语法糖方言:仅新增if规则,覆盖
statement规则即可
;; 新增if语句规则 (def if-sttmt (do* IF expr COLON block (optional (do* ELSE COLON block)) END)) (defn if-parser [] (binding [statement (choice assign if-sttmt)] (fn [input-str] (program input-str))))
- 还可以任意组合多个扩展特性,不需要额外的类层次设计:
;; 同时支持内联函数和if/else的方言 (defn inline+if-parser [] (binding [expr (choice input output variable funcall) statement (choice assign if-sttmt)] (fn [input-str] (program input-str))))
其他可选方案
如果你不想使用动态绑定,也可以把所有语法规则封装到map中,扩展时用merge覆盖变动的规则项,所有规则的引用都从map中读取即可,该方案为纯数据操作,没有动态作用域的隐式依赖。
内容的提问来源于stack exchange,提问作者Jeff
相关产品推荐
相关产品推荐

