2016-09-28 2 views
0

ラケットマクロシステムを学ぶ練習として、私はC++ catch frameworkに基づいて単体テストフレームワークを実装しています。ラケットに評価ステップと中間値を記録するマクロ?

チェックは、xとyの値を出力しますエラーメッセージを違反している
CHECK(x == y); // (check x y) 

は、使用するマクロは完全に汎用的であるにもかかわらず:そのフレームワークの特徴の一つは、私はこのようなチェックを書く場合ということですこれは、CHECK_EQUALS、CHECK_GREATERなどのマクロを使用する必要がある他のテストフレームワークとは異なります。これは、式テンプレートと演算子のオーバーロードに関連するいくつかのハッカーによって可能です。

ラケットでは、より良い仕事をすることができるはずです。

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

チェックに違反した場合、あなただけ等号の左側と右側の値を見つける、:あなたのような何かを書く場合のように、C++のバージョンではマクロは、内部の部分式を見ることができませんx、y、またはg(y)の値ではありません。ラケットでは、サブ式に再帰し、評価の各ステップを示すツリーを印刷することができるはずです。

問題は、私はこれを行うための最善の方法は何であるか全くわから持っていないです:

  • 私は、構文解析とかなり馴染み得ているが、これはその能力を超えているようです。
  • #%appのカスタマイズについては、ほとんど私が望んでいるように見えますが、たとえばfがマクロの場合は、展開中の式のすべての評価をプリントアウトしたくはありません。ユーザーがチェックマクロを呼び出したときに表示された式。また、言語を定義せずに使用できるかどうかはわかりません。
  • 私はsyntax-parameterizeを使って基本演算子の意味を乗り越えることができますが、g(y)のような関数呼び出しには役立ちません。
  • 私はsyntax-> datumを使用して、手作業でASTを歩いて、自分自身の部分式にevalを呼び出すことができました。これは難しいようです。
  • トレースライブラリは、私が望むものとほとんど同じですが、関数のリストを前もって指定しなければなりません。また、出力の場所を制御することはできません。チェックが失敗した場合は何か、成功した場合は実行しないので、実行が進むにつれて中間の値をサイドに保存する必要があります)。

これを実装するには、どのような方法が最適か、少なくとも慣用的な方法でしょうか。

+0

理由を説明する心を閉じるために投票します誰でもここではラフスケッチシンプルですか?質問はかなり具体的ですが、私はそれがどのように "広範な" –

答えて

2

ここにあなたを始めるための何かがあります。

#lang racket 

(require (for-syntax syntax/parse racket/list)) 

(begin-for-syntax 
    (define (expression->subexpressions stx) 
    (define expansion (local-expand stx 'expression '())) 
    (syntax-parse expansion 
     #:datum-literals (#%app quote) 
     [x:id  (list #'x)] 
     [b:boolean (list #'b)] 
     [n:number (list #'n)] 
     ; insert other atoms here 
     [(quote literal) (list #'literal)] 
     [(#%app e ...) 
     (cons stx 
      (append-map expression->subexpressions (syntax->list #'(e ...))))] 
     ; other forms in fully expanded syntax goes here 
     [else 
     (raise-syntax-error 'expression->subexpressions 
          "implement this construct" 
          stx)]))) 

(define-syntax (echo-and-eval stx) 
    (syntax-parse stx 
    [(_ expr) 
    #'(begin 
     (display "] ") (displayln (syntax->datum #'expr)) 
     (displayln expr))])) 

(define-syntax (echo-and-eval-subexpressions stx) 
    (syntax-parse stx 
    [(_ expr) 
    (define subs (expression->subexpressions #'expr)) 
    (with-syntax ([(sub ...) subs]) 
     #'(begin 
      ; sub expressions 
      (echo-and-eval sub) 
      ... 
      ; original expression 
      (echo-and-eval expr)))])) 


(echo-and-eval-subexpressions (+ 1 2 (* 4 5))) 

出力:

] (+ 1 2 (* 4 5)) 
23 
] + 
#<procedure:+> 
] 1 
1 
] 2 
2 
] (#%app * '4 '5) 
20 
] * 
#<procedure:*> 
] 4 
4 
] 5 
5 
] (+ 1 2 (* 4 5)) 
23 
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 
関連する問題