通过在球拍中设计一个解释器来重写这个脚本



原始脚本如下:

#lang racket
(for ([i (in-range 3)])
    (for ([j (in-range 9)])
      (display "X"))
     (display "n"))
(for ([i (in-range 6)])
  (for ([j (in-range 3)])
    (display " "))
  (for ([j (in-range 3)])
    (display "X"))
  (for ([j (in-range 3)])
    (display " "))
  (display "n"))
(for ([i (in-range 3)])
    (for ([j (in-range 9)])
      (display "X"))
     (display "n"))

输出为:

XXXXXXXXX
XXXXXXXXX
XXXXXXXXX
   XXX   
   XXX   
   XXX   
   XXX   
   XXX   
   XXX   
XXXXXXXXX
XXXXXXXXX
XXXXXXXXX

我想知道我是否可以使用这样的DSL重写它:

(define a
  "3 9 X
6 3 b 3 X 3 b
3 9 X")

然后:

(interpret a)

来绘制这张图。

有人知道什么是最好的方法吗?

要解决此类问题,请首先描述一种数据类型,它可以捕获DSL中所需的操作,而不是集中于表面语法。一旦你掌握了数据类型,你应该会更容易地处理这个问题。

乍一看,我们可以用您的语言设计3种基本形式:

  1. 字符串
  2. 重复
  3. 排序

我们可以用原始字符串和结构来表示这个不相交的类。让我们把这个类作为一个整体称为"pexpr",表示"可打印的expr"。代码中:

;; An pexpr is one of the following:
;;   * a primitive string,
;;   * a seq, or
;;   * a repeat
(struct seq (bodies) #:transparent)    ;; bodies is a list of pexpr
(struct repeat (n body) #:transparent) ;; n is a number, body is a pexpr

将一些辅助函数作为缩写可能会有所帮助,因为"seq"one_answers"repeat"本身就有点冗长。

;; For convenience, we define some abbreviations s and r for seq and repeat,
;; respectively.
(define (s . bodies)
  (seq bodies))
(define (r n . bodies)
  (repeat n (seq bodies)))

您的示例"I"字符串可以这样写:

(define an-example
  (s
   (r 3 (r 9 "X") "n")
   (r 6 (r 3 " ") (r 3 "X") "n")
   (r 3 (r 9 "X") "n")))

请注意,这种编码具有换行符的显式表示,仅从表面语法来看,换行符是隐式的。然后,解析器的工作就是在表面语法中提取行并将其转换为pexpr,但这应该不会太难。希望如此。:)

无论如何,explore函数变成了一个为如下模板填充细节的问题:

(define (interpret pexpr)
  (match pexpr
    [(? string?)
     ...]
    [(struct seq (bodies))
     ...]
    [(struct repeat (n body))
     ...]))

其中的"…"s应该很容易填写。

如何设计程序和程序设计语言:应用和解释中描述了这种解决这类问题的方法。我建议你看看它们:它们是好东西。

当然,这看起来是可行的。这主要是一个解析问题。我会这样把它打碎的。每个输入行指定一个输出行块。在Racket中找到一个很好的表达方式。举一些例子,确保它涵盖了你希望它涵盖的内容。接下来,我可能会编写渲染其中一个块的函数。大多数情况下,我会先做一次,这样我就可以对看到产出感到满意。然后,我将编写一个函数,该函数获取这些块规范的列表,并将它们全部输出。然后,我将编写一个函数来解析一行输入。看起来您可以使用空白分割这些行(例如,使用"regexp split"),并且然后使用ad-hoc解析器处理这些列表。这是我认为我最有可能出错的部分,在编码之前我会写一堆测试用例。最后,您需要一个函数,该函数在每一行输入上调用这个解析器,然后将生成的块规范发送给显示函数。

最新更新