【发布时间】:2022-01-05 05:38:34
【问题描述】:
我最近开始研究 Clojure,所以我承认我没有太多 我也没有读过所有关于它的书。尽管如此,我还是很难 是时候弄清楚如何扩展用 Clojure 编码的系统的行为了。
更具体地说,出于教育目的,前段时间我实现了 Scala 中的解析器,用于一系列小型语言——NAND-CIRC 语言 家庭,类似于 Boaz Barak 在“Introduction to Theoretical Computer Science” 中的定义。有一个 纯版本的语言,然后有连续的语法糖 可以添加(内联函数、用户定义函数、if/else 和 for)。
在 Scala 中——使用解析器组合库——我可以定义基本的 语法作为一个类,如下图(语义动作省略 简洁)。
import scala.util.parsing.combinator._
class NandCircParsers extends RegexParsers {
def program: Parser[Any] = block
def block: Parser[Any] = rep(command)
def command: Parser[Any] = opt(statement) ~ rep(eol)
def statement: Parser[Any] = assign
def assign: Parser[Any] = reference ~ "=" ~ funcall
def reference: Parser[Any] = outputPort | variable
def funcall: Parser[Any] = identifier ~ "(" ~ actualArgs ~ ")"
def actualArgs: Parser[Any] = repsep(expression, ",")
def expression: Parser[Any] = inputPort | reference
def variable: Parser[Any] = identifier
def inputPort: Parser[Any] = "X" ~ "[" ~ index ~ "]"
def outputPort: Parser[Any] = "Y" ~ "[" ~ index ~ "]"
def identifier: Parser[Any] = """[_a-zA-Z$][_a-zA-Z0-9'$]*""".r
def index: Parser[Any] = number
def number: Parser[Any] = """[-+]?[0-9]+""".r
}
然后我可以创建一个 NandCircParsers 类的实例并将其用于
处理语言的 vanilla 版本。
但是NAND-CIRD语言的另一种方言允许使用内联
职能。为此,expression 的生产规则必须更改。在斯卡拉
这是一个创建子类并覆盖相关方法的问题。
class NandCircInlineParsers extends NandCircParsers {
override def expression: Parser[Any] = inputPort | reference | funcall
}
然后我可以实例化 NandCircInlineParsers 类并使用新的
方言 无需重写所有语法,即解析器层次结构。
对于带有 if/else 语法糖的方言(仅)情况类似。
class NandCircIfParsers extends NandCircParsers {
override def statement: Parser[Any] = assign | ifSttmt
def ifSttmt: Parser[Any] =
"if" ~ expression ~ ":" ~ block ~ opt("else" ~ ":" ~ block) ~ "end"
}
我只需要覆盖改变的语法(方法)的产生规则 并添加新方言,我就有了新方言的解析器。
但这是在 Scala 中。现在我正在尝试使用 Clojure 实现等效的结果。
我已经实现了一个解析器组合器库,它符合以下内容
是为parsec.el Emacs Lisp parser combinator
library 完成的,根据我的需要,它是
工作。
vanilla 版本的 NAND-CIRC 语言的解析器变为 像这样的。
(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 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 expr (choice input output variable))
(def actual-args (sep-by expr COMMA))
(def funcall (do* ident LPAR actual-args RPAR))
(def formal-args (sep-by var COMMA))
(def reference (either output variable))
(def assign (do* reference EQU funcall))
(def command (do* (optional assign) (many eol)))
(def statement command)
(def program (many command))
抛开可读性问题不谈,我的问题是:我怎样才能达到相同水平的 我在 Scala 中使用 Clojure 的代码重用?我怎样才能把这个设计模块化 足以让我只能更改或添加必要的规则来获得新的方言 的语言?现在,我能想到的一切——不涉及 用继承实现一个对象系统——就是复制整个 语法。
Clojure 是否有任何资源可以促进这一点?
谢谢。
【问题讨论】:
标签: scala parsing inheritance clojure polymorphism