#lang racket ;;; Change this line, depending on which file you want to run test-cases on. ;;; The file *must* export: ;;; parse-string : string -> expr ;;; expr->string : expr -> string ;;; eval : expr -> value ;;; tests : (a list of test cases -- see hw06-O0.rkt for format.) ;;; (require "O4.rkt") ; <--- change this line as needed. (require "scanner.rkt") ; Needed for create-scanner; we use it here to compare two strings of N. ; Like check-expect, except that we can use this inside map. ; (define (my-check-expect actual expected test-info) (cond [(and (string? actual) (string? expected)) ; Comparing two strings of O0; ignore whitespace, only look at list of tokens. (my-check-expect (string->sexpr actual) (string->sexpr expected) test-info)] [(and (not (equal? actual expected)) (not (and (number? actual) (number? expected) (< (abs (- actual expected)) TOLERANCE)))) (begin (display (format "!!! Failed test ~v:\nGot ~v,\nexpected ~v.\n" test-info actual expected)) false)] [else (begin (display ".") true)])) (define TOLERANCE 0.0001) ;;;; Some test-functions for manual inspecting the output: ;; (Call these, to see exactly what a failed test case is doing.) ; get-test : (-> int string) ; Return the i'th test case (the raw, unparsed string) ; (define (get-test i) (first (list-ref tests i))) ; test-parse : (-> int void) ; A manual helper function: ; Extract the i'th test string, and parse it. ; Print the result. ; (define (test-parse i) (printf "~v => ~v" (get-test i) (parse-string (get-test i)))) ; test-eval : (-> int void) ; A manual helper function: ; Extract the i'th test string, parse it, and eval it. ; Print the result. ; (define (test-eval i) (printf "~v => ~v" (get-test i) (eval (parse-string (get-test i))))) ; test-toString : (-> int string) ; A manual helper function: ; Extract the i'th test string, parse it, and then ; convert the internal representation into a string. ; (define (test-expr->string i) (expr->string (parse-string (get-test i)))) ; Check that the internal representation ; (if present in 'tests') is as expected: ; (define (test-parse-all) (map (lambda (t) (or (< (length t) 3) (my-check-expect (parse-string (first t)) (third t) "internal representation"))) tests)) ; Check that s = (expr->string (parse-string s)). ; More precisely, because of differences in parens (and pretty-printing) in expr->string, ; we'll check that (parse-string s) = (parse-string (expr->string (parse-string s))) ; (define (test-expr->string-all) (map (lambda (t) (my-check-expect (expr->string (parse-string (first t))) (first t) "expr->string should be inverse of parse-string:")) tests)) ; ; Actually, we won't get *exactly* the same string back: ; Check that the internal representation (if present in 'tests') is as expected: ; Check eval: ; (define (test-eval-all) (map (lambda (t) (my-check-expect (eval (parse-string (first t))) (second t) (format "eval ~a" (first t)))) tests)) (define (test-all) (list (test-parse-all) (test-expr->string-all) (test-eval-all))) ; Given a string, return a list of tokens (of O0). ; (define (string->sexpr str) ; (Should probably re-name this `string->tokens`) ; Don't use scheme's built-in `read`, because our string might contain semicolons etc. (scanner->tokens! (create-scanner str))) ; Given a scanner, return a list of all its tokens. ; Consumes the scanner, in the process. ; (define (scanner->tokens! scnr) (if (eof-object? (peek scnr)) empty (cons (pop! scnr) (scanner->tokens! scnr)))) ; We RELY on left-to-right eval here: ; the pop happens before the recursive call. (test-all)