用于记录Racket评估步骤和中间值的宏?

时间:2016-09-28 02:47:34

标签: macros scheme racket evaluation expression-evaluation

作为学习Racket宏系统的练习,我一直在实施基于C++ catch framework的单元测试框架。该框架的一个特点是,如果我写这样的支票:

CHECK(x == y); // (check x y)

当违反检查时,错误消息将打印出x和y的值,即使使用的宏是完全通用的,不像其他测试框架要求你使用像CHECK_EQUALS,CHECK_GREATER等宏。这是可能的通过涉及表达式模板和运算符重载的一些hackery。

在我看来,在Racket中你应该能够做得更好。在C ++版本中,宏不能看到子表达式内部,所以如果你写了类似的东西:

CHECK(f(x, g(y)) == z); // (check (= (f x (g y)) z))

当违反检查时,您只能找到等号的左侧和右侧的值,而不是x,y或g(y)的值。在球拍中,我希望应该可以递归到子表达式并打印一个显示评估每个步骤的树。

问题是我不知道最好的方法是:

  • 我对语法分析非常熟悉,但这似乎超出了它的能力。
  • 我读到自定义#%app几乎看起来像我想要的,但是如果例如f是一个宏,我不想打印出扩展中表达式的每个评估,只是评估用户调用检查宏时可见的表达式。还不确定我是否可以在不定义语言的情况下使用它。
  • 我可以使用syntax-parameterize来劫持基本运算符的含义,但这对g(y)等函数调用没有帮助。
  • 我可以使用syntax-> datum并手动遍历AST,自己在子表达式上调用eval。这看起来很棘手。
  • 跟踪库几乎看起来像我想要的那样,但是你必须先给它一个功能列表,它似乎不能让你控制输出的位置(我只想要如果检查失败则打印任何内容,如果成功则打印,所以我需要在执行过程中将中间值保存到一边。)

实现这一目标的最佳或至少是惯用的方法是什么?

2 个答案:

答案 0 :(得分:2)

这是让你入门的东西。

<input type="button" />

输出:

<form id="form" method="post" action="xxxxx.php">

答案 1 :(得分:2)

打印所有内容的替代方法是为应显示的内容添加标记。这是一个简单的草图:

#lang racket

(require racket/stxparam)

(define-syntax-parameter ?
  (λ(stx) (raise-syntax-error '? "can only be used in a `test' context")))

(define-syntax-rule (test expr)
  (let ([log '()])
    (define (log! stuff) (set! log (cons stuff log)))
    (syntax-parameterize ([? (syntax-rules ()
                               [(_ E) (let ([r E]) (log! `(E => ,r)) r)])])
      (unless expr
        (printf "Test failure: ~s\n" 'expr)
        (for ([l (in-list (reverse log))])
          (for-each display
                    `("  " ,@(add-between (map ~s l) " ") "\n")))))))

(define x 11)
(define y 22)
(test (equal? (? (* (? x) 2)) (? y)))
(test (equal? (? (* (? x) 3)) (? y)))

导致此输出:

Test failure: (equal? (? (* (? x) 3)) (? y))
  x => 11
  (* (? x) 3) => 33
  y => 22