whalesong/test-compiler.rkt
2011-03-08 18:22:18 -05:00

452 lines
14 KiB
Racket

#lang racket
(require "simulator.rkt"
"simulator-structs.rkt"
"compile.rkt"
"parse.rkt")
(define (run-compiler code)
(compile (parse code) 'val 'next))
;; Test out the compiler, using the simulator.
(define-syntax (test stx)
(syntax-case stx ()
[(_ code exp options ...)
(with-syntax ([stx stx])
(syntax/loc #'stx
(begin
(printf "Running ~s ...\n" 'code)
(let*-values([(a-machine num-steps)
(run (new-machine (run-compiler 'code)) options ...)]
[(actual) (machine-val a-machine)])
(unless (equal? actual exp)
(raise-syntax-error #f (format "Expected ~s, got ~s" exp actual)
#'stx))
(unless (= (machine-stack-size a-machine) 1)
(raise-syntax-error #f (format "Stack is not back to the prefix as expected!")
#'stx))
(unless (null? (machine-control a-machine))
(raise-syntax-error #f (format "Control is not empty as expected!")
#'stx))
(printf "ok. ~s steps.\n\n" num-steps)))))]))
;; test, and expect an error
(define-syntax (test/exn stx)
(syntax-case stx ()
[(_ code options ...)
(with-syntax ([stx stx])
(syntax/loc #'stx
(begin
(printf "Running/exn ~s ...\n" 'code)
(let/ec return
(with-handlers ([exn:fail? (lambda (exn)
(printf "ok\n\n")
(return))])
(run (new-machine (run-compiler 'code)) options ...))
(raise-syntax-error #f (format "Expected an exception")
#'stx)))))]))
;; run: machine -> (machine number)
;; Run the machine to completion.
(define (run m
#:debug? (debug? false)
#:stack-limit (stack-limit false)
#:control-limit (control-limit false))
(let loop ([m m]
[steps 0])
(when debug?
(when (can-step? m)
(printf "|env|=~s, |control|=~s, instruction=~s\n"
(length (machine-env m))
(length (machine-control m))
(current-instruction m))))
(when stack-limit
(when (> (machine-stack-size m) stack-limit)
(error 'run "Stack overflow")))
(when control-limit
(when (> (machine-control-size m) control-limit)
(error 'run "Control overflow")))
(cond
[(can-step? m)
(loop (step m) (add1 steps))]
[else
(values m steps)])))
;; Atomic expressions
(test 42 42)
(test "hello world" "hello world")
(test #t true)
(test #f false)
;; quoted
(test '(+ 3 4)
'(+ 3 4))
;; Simple definitions
(test (begin (define x 42)
(+ x x))
84)
(test (begin (define x 6)
(define y 7)
(define z 8)
(* x y z))
(* 6 7 8))
;; Simple branching
(test (if #t 'ok 'not-ok)
'ok)
(test (if #f 'not-ok 'ok)
'ok)
;; Sequencing
(test (begin 1
2
3)
3)
(test (begin 1)
1)
(test (+ (* 3 4) 5)
17)
;; Square
(test (begin (define (f x)
(* x x))
(f 3))
9)
;; Other simple expressions
(test (+ 137 349)
486)
(test (/ 10 5)
2)
;; composition of square
(test (begin (define (f x)
(* x x))
(f (f 3)))
81)
(test (begin (define pi 3.14159)
(define radius 10)
(* pi (* radius radius)))
314.159)
(test (begin (define pi 3.14159)
(define radius 10)
(define circumference (* 2 pi radius))
circumference)
62.8318)
;; Slightly crazy expression
(test (begin (define (f x)
(* x x))
(define (g x)
(* x x x))
(- (g (f (+ (g 3)
(f 3))))
1))
2176782335)
;; Simple application
(test ((lambda (x) x) 42)
42)
(test ((lambda (x)
(begin (* x x))) 42)
1764)
(test ((lambda (x y z) x) 3 4 5)
3)
(test ((lambda (x y z) y) 3 4 5)
4)
(test ((lambda (x y z) z) 3 4 5)
5)
;; And this should fail because it's not a lambda
(test/exn (not-a-procedure 5))
;; We should see an error here, since the arity is wrong
(test/exn ((lambda (x y z) x) 3))
(test/exn ((lambda (x y z) z) 3))
(test/exn ((lambda (x y z) x) 3 4 5 6))
; factorial
(test (begin (define (f x)
(if (= x 0)
1
(* x (f (sub1 x)))))
(f 0))
1)
(test (begin (define (f x)
(if (= x 0)
1
(* x (f (sub1 x)))))
(f 1))
1)
(test (begin (define (f x)
(if (= x 0)
1
(* x (f (sub1 x)))))
(f 2))
2)
(test (begin (define (f x)
(if (= x 0)
1
(* x (f (sub1 x)))))
(f 100))
93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000
)
;; Tail calling behavior: watch that the stack never grows beyond 8.
(test (begin (define (f x acc)
(if (= x 0)
acc
(f (sub1 x) (* x acc))))
(f 1000 1))
(letrec ([f (lambda (x)
(if (= x 0)
1
(* x (f (sub1 x)))))])
(f 1000))
#:stack-limit 8)
;; And from experimental testing, anything below 7 will break.
(test/exn (begin (define (f x acc)
(if (= x 0)
acc
(f (sub1 x) (* x acc))))
(f 1000 1))
(letrec ([f (lambda (x)
(if (= x 0)
1
(* x (f (sub1 x)))))])
(f 1000))
#:stack-limit 7)
;; tak test
(test (begin (define (tak x y z)
(if (>= y x)
z
(tak (tak (- x 1) y z)
(tak (- y 1) z x)
(tak (- z 1) x y))))
(tak 18 12 6))
7)
;; deriv
(test (begin (define (deriv-aux a) (list '/ (deriv a) a))
(define (map f l)
(if (null? l)
l
(cons (f (car l))
(map f (cdr l)))))
(define (deriv a)
(if (not (pair? a))
(if (eq? a 'x) 1 0)
(if (eq? (car a) '+)
(cons '+ (map deriv (cdr a)))
(if (eq? (car a) '-)
(cons '- (map deriv
(cdr a)))
(if (eq? (car a) '*)
(list '*
a
(cons '+ (map deriv-aux (cdr a))))
(if (eq? (car a) '/)
(list '-
(list '/
(deriv (cadr a))
(caddr a))
(list '/
(cadr a)
(list '*
(caddr a)
(caddr a)
(deriv (caddr a)))))
'error))))))
(deriv '(+ (* 3 x x) (* a x x) (* b x) 5)))
'(+ (* (* 3 x x) (+ (/ 0 3) (/ 1 x) (/ 1 x)))
(* (* a x x) (+ (/ 0 a) (/ 1 x) (/ 1 x)))
(* (* b x) (+ (/ 0 b) (/ 1 x)))
0))
;; Foldl
(test (begin (define (foldl f acc lst)
(if (null? lst)
acc
(foldl f (f (car lst) acc) (cdr lst))))
(foldl (lambda (x acc)
(* x acc))
1
'(1 2 3 4 5 6 7 8 9 10)))
(* 1 2 3 4 5 6 7 8 9 10))
;; iterating, with some crazy expressions
(test (begin (define (iterate f x n)
(if (= n 0)
x
(iterate f (f x) (sub1 n))))
(list (iterate (lambda (x) (* x x)) 20 2)
(iterate add1 1 1000)
(iterate (lambda (x)
(iterate (lambda (y)
(+ x y))
x
x))
1
3)))
(list 160000 1001 42))
;; Trying out closures
(test (begin
(define delta 1)
(define (diff f)
(lambda (x)
(/ (- (f (+ x delta))
(f x))
delta)))
(define 2x (diff (lambda (x) (* x x))))
(define two (diff 2x))
(list (2x 1000)
(two 2011)))
(list 2001 2))
(test (begin (define (square x)
(* x x))
(square (square 3)))
81)
(test (begin (define (square x)
(* x x))
(define (sum-of-squares x y)
(+ (square x) (square y)))
(sum-of-squares 3 4))
25)
(test (begin (define (sqrt-iter guess x)
(if (good-enough? guess x)
guess
(sqrt-iter (improve guess x)
x)))
(define (improve guess x)
(average guess (/ x guess)))
(define (square x)
(* x x))
(define (average x y)
(/ (+ x y) 2))
(define (good-enough? guess x)
(< (abs (- (square guess) x)) 0.001))
(define (sqrt x)
(sqrt-iter 1.0 x))
(list (sqrt 9) (sqrt 23942) (sqrt 31337)))
'(3.00009155413138 154.73202642085838 177.02259745919164))
;; fibonacci
(test (begin (define (fib n)
(if (= n 0) 0
(if (= n 1) 1
(+ (fib (- n 1))
(fib (- n 2))))))
(fib 10))
55)
;; Fibonacci, iterative. This should be computable while using at most 10 spots.
(test (begin
(define (fib n)
(fib-iter 1 0 n))
(define (fib-iter a b count)
(if (= count 0)
b
(fib-iter (+ a b) a (- count 1))))
(fib 10000))
33644764876431783266621612005107543310302148460680063906564769974680081442166662368155595513633734025582065332680836159373734790483865268263040892463056431887354544369559827491606602099884183933864652731300088830269235673613135117579297437854413752130520504347701602264758318906527890855154366159582987279682987510631200575428783453215515103870818298969791613127856265033195487140214287532698187962046936097879900350962302291026368131493195275630227837628441540360584402572114334961180023091208287046088923962328835461505776583271252546093591128203925285393434620904245248929403901706233888991085841065183173360437470737908552631764325733993712871937587746897479926305837065742830161637408969178426378624212835258112820516370298089332099905707920064367426202389783111470054074998459250360633560933883831923386783056136435351892133279732908133732642652633989763922723407882928177953580570993691049175470808931841056146322338217465637321248226383092103297701648054726243842374862411453093812206564914032751086643394517512161526545361333111314042436854805106765843493523836959653428071768775328348234345557366719731392746273629108210679280784718035329131176778924659089938635459327894523777674406192240337638674004021330343297496902028328145933418826817683893072003634795623117103101291953169794607632737589253530772552375943788434504067715555779056450443016640119462580972216729758615026968443146952034614932291105970676243268515992834709891284706740862008587135016260312071903172086094081298321581077282076353186624611278245537208532365305775956430072517744315051539600905168603220349163222640885248852433158051534849622434848299380905070483482449327453732624567755879089187190803662058009594743150052402532709746995318770724376825907419939632265984147498193609285223945039707165443156421328157688908058783183404917434556270520223564846495196112460268313970975069382648706613264507665074611512677522748621598642530711298441182622661057163515069260029861704945425047491378115154139941550671256271197133252763631939606902895650288268608362241082050562430701794976171121233066073310059947366875
#:stack-limit 10
#:control-limit 1)
;; Exponentiation
(test (begin (define (expt b n)
(if (= n 0)
1
(* b (expt b (- n 1)))))
(expt 2 30))
(expt 2 30))
(test (begin
(define (expt b n)
(expt-iter b n 1))
(define (expt-iter b counter product)
(if (= counter 0)
product
(expt-iter b
(- counter 1)
(* b product))))
(expt 2 30))
(expt 2 30))
(test (begin
(define (fast-expt b n)
(cond ((= n 0) 1)
((even? n) (square (fast-expt b (/ n 2))))
(else (* b (fast-expt b (- n 1))))))
(define (square x) (* x x))
(define (expt b n)
(fast-expt b n))
(define (even? n)
(= (remainder n 2) 0))
(list (expt 2 30)
(expt 2 23984000)))
(list (expt 2 30)
(expt 2 23984000)))
;(simulate (compile (parse '42) 'val 'next))
;(compile (parse '(+ 3 4)) 'val 'next)