;; The first three lines of this file were inserted by DrRacket. They record metadata ;; about the language level of this file in a form that our tools can easily process. #reader(lib "htdp-advanced-reader.ss" "lang")((modname expr-test-P0) (read-case-sensitive #t) (teachpacks ()) (htdp-settings #(#t constructor repeating-decimal #t #t none #f ()))) (require "P0.rkt") (require "scanner.rkt") (require rackunit) ; use `check-equal?`, not `check-expect`. ; ; We use `check-equal?` because it can be passed as a function (e.g. to `map`). ; The one advantage of `check-expect` we lose, is that all check-expects are ; delayed until the end of the file (hence, you can have a `check-expect` ; *before* the function it tests -- not so, with `check-equal?`.) ;;;;;;;;;;;;;;;;;;; TEST CASES: P0 ;;;;;;;;;;;;;;;; ; Some expressions to test in a non-automated way: (define e0 "43") (define e1 "<43>") (define e2 "(:4 +++ 3:)") (define e3 "<<<(4 +++ 3)>>>") (define e4 "(:<43> +++ ( : 42 *** 3:):)") (check-equal? (string->expr "39") 39) (check-equal? (eval (string->expr e4)) 169) ;;; three types of test we want to make, for many different exprs: (check-equal? (string->expr "(:4 +++ 3:)") (make-bin-expr 4 "+++" 3)) (check-equal? (eval (string->expr "(:4 +++ 3:)")) 7) (check-equal? (expr->string (string->expr "(:4 +++ 3:)")) "(:4 +++ 3:)") ;; If you wanted to test/verify that exceptions really do get thrown ;; for particular inputs, you can use `check-exn`. ;; (This requires advanced-student, since the exception-throwing-code ;; needs to be wrapped in a "thunk" -- a lambda of 0 arguments.) ;; (check-exn #rx"parse!: Unrecognized expr" (λ() (string->expr "<>"))) ;; ;; You don't need to do this -- I'm just including it to show ;; what things a unit-testing library can/should check for. ;; ;; Btw, I should call `(regexp (regexp-quote ...the-err-message...))` ;; or at least quote the "." in my #rx above. ;; Althougy regexp-quote isn't in the student languages, ;; you can `(require "student-extras.rkt")` to get it. (define tests ; Each entry in the list is either ; [str val] (where val is the result of interpreting the string str), or ; [str val expr] (as above, but expr is the internal (struct) representation). `{["7" 7 7] ["(:3 +++ 4:)" 7 ,(make-bin-expr 3 "+++" 4)] ["(:3 *** 4:)" 12 ,(make-bin-expr 3 "***" 4)] ["<(:(:3 +++ 4:) +++(: 3 *** 4 :) :)>" 19] ["whenz 0 then 1 otherwise 2!" 1 ,(make-whenz-expr 0 1 2)] ["whenz 1 then 1 otherwise 2!" 2 ,(make-whenz-expr 1 1 2)] ["whenz (:3 +++ -3:) then 1 otherwise 2!" 1 ,(make-whenz-expr (make-bin-expr 3 "+++" -3) 1 2)] ["whenz (:whenz whenz 0 then 1 otherwise 2! then 3 otherwise 4 ! +++ -3:) then 1 otherwise 2!" 2 ,(make-whenz-expr (make-bin-expr (make-whenz-expr (make-whenz-expr 0 1 2) 3 4) "+++" -3) 1 2)] #| Further tests, for P1: ["(:3.0 %%% 4.0:)" 3] ["(:(: 5.0 +++ 6.0 :) %%% 3.0:)" 2] ["(:8.1 %%% 3.0:)" 2.1] ["(:8.0 %%% 3.1:)" 1.8] ["(:-8.1 %%% 3.0:)" 0.9] ["(:-8.0 %%% 3.1:)" 1.3] ["(:8.1 %%% -3:)" -0.9] ["(:8.0 %%% -3.1:)" -1.3] ["(:-8.1 %%% -3.0:)" -2.1] ["(:-8.0 %%% -3.1:)" -1.8] ["(:8.0 %%% 2.0:)" 0] ["(:-8.0 %%% 2.0:)" 0] ["(:8.0 %%% -2.0:)" 0] ["(:-8.0 %%% -2.0:)" 0] ["(:8.0 %%% 3.0:)" 2] ["(:-8.0 %%% 3.0:)" 1] ["(:8.0 %%% -3.0:)" -1] ["(:-8.0 %%% -3.0:)" -2] |# }) ; For info on backquote, see documentations and/or: ; http://www.radford.edu/itec380/2014fall-ibarland/Lectures/backquote.html ; Given a string, return a list of tokens (of P). ; (define (string->P-tokens str) ; Don't use scheme's built-in `read`, because our string might contain semicolons etc. (let loop {[scnr (create-scanner str)]} ; See "named let" -- advanced-student. (if (eof-object? (peek scnr)) empty (cons (pop! scnr) (loop scnr))))) ; N.B. We RELY on left-to-right eval here: ; the pop happens before looping back. ; Test the internal representations: (for-each (λ (t) (check-equal? (string->expr (first t)) (third t))) (filter (λ(t) (>= (length t) 3)) tests)) ; Test that expr->string and string->expr are inverses: (for-each (λ (t) (check-equal? (string->P-tokens (expr->string (string->expr (first t)))) (string->P-tokens (first t)))) tests) ; Now, test `eval`: (for-each (λ (t) (check-equal? (eval (string->expr (first t))) (second t))) tests)