For this project, you will write a Racket interpreter in Java. Your interpreter will support a larger subset of Racket than our in-class version (Mini-Racket), but you will not implement every feature of the entire language. Since I need a name for this language you're writing an interpreter for, I'll call it JRacket.
This project is structured similarly to our in-class Mini-Racket interpreter, but since we're in Java, we're using an OOP style. In other words, instead of having a single eval() function, we have a RacketExpression class with an eval() method that other expression classes will override.
The types of expressions you must support are:
>>> 4
==> 4
>>> #t
==> #t
>>> '(1 2 3)
Evaluating: (quote (1 2 3))
==> (1 2 3)
>>> 'x
Evaluating: (quote x)
==> x
The basic interpreter is flexible enough that if you make a parenthetical or syntactical
mistake, it will detect it and not kick you out of the interpreter:
>>> '(3 4)) cs360.ParsingException: Too many closing parens in '(3 4)). Already parsed [', [3, 4]] >>> #tf cs360.ParsingException: Cannot parse boolean value: #tf
The major difference between this interpreter and Mini-Racket's interpreter is that we have multiple eval() methods, since each Expression is a class. The first thing you should do is take a look at the eval() methods in RacketInteger and RacketBoolean and notice how they work (they're very simple). For instance, RacketInteger's eval() method reflects how in Mini-Racket we tested if an expression was a number and if so, we just returned the expression itself (because numbers, when evaluated, return themselves).
I suggest you add things in a slightly different order than we did in class:
Now, go to RacketSymbol and edit the eval() method to call lookupVariableValue() just like Mini-Racket does when it sees a symbol.
Next, you now need to edit RacketList's eval() method to support expressions of the type (define x 3). Notice how RacketList's eval() dispatches to evalDefine(), evalLambda(), etc, just like Mini-Racket. Take a look at evalQuote() first. It's already written for you, but it will guide you in writing the other evalXYZ() methods. Write evalDefine(). This method should call defineVariable().
You now should be able to do the following:
>>> (define x 3)
Evaluating: (define x 3)
==> done
>>> x
Evaluating: x
==> 3
>>> (define blah '(1 2 3))
Evaluating: (define blah (quote (1 2 3)))
Evaluating: (quote (1 2 3))
==> done
>>> blah
Evaluating: blah
==> (1 2 3)
>>> (define y blah)
Evaluating: (define y blah)
Evaluating: blah
==> done
>>> y
Evaluating: y
==> (1 2 3)
You now should be able to do the following:
>>> (+ 3 3)
Evaluating: (+ 3 3)
Evaluating: +
==> 6
>>> (define z 42)
Evaluating: (define z 42)
==> done
>>> (define q 5)
Evaluating: (define q 5)
==> done
>>> (* (- 2 q) z)
Evaluating: (* (- 2 q) z)
Evaluating: *
Evaluating: (- 2 q)
Evaluating: -
Evaluating: q
Evaluating: z
==> -126
>>> (cons 1 '())
Evaluating: (cons 1 (quote ()))
Evaluating: cons
Evaluating: (quote ())
==> (1)
>>> (define L (cons 1 '(2 3)))
Evaluating: (define L (cons 1 (quote (2 3))))
Evaluating: (cons 1 (quote (2 3)))
Evaluating: cons
Evaluating: (quote (2 3))
==> done
>>> L
Evaluating: L
==> (1 2 3)
>>> l
Evaluating: l
cs360.InterpreterException: Cannot find variable l
>>> (= 3 4)
Evaluating: (= 3 4)
Evaluating: =
==> #f
>>> (cons (car L) L)
Evaluating: (cons (car L) L)
Evaluating: cons
Evaluating: (car L)
Evaluating: car
Evaluating: L
Evaluating: L
==> (1 1 2 3)
Important: Unlike in Mini-Racket, JRacket does not need separate tests for
each primitive function (e.g., add?, subtract?, multiply?, etc). Our interpreter
knows whether or not a function is a primitive because every object knows
what class it belongs to. Therefore, as long as inside evalCall() you call
apply() on the appropriate object, it will get dispatched correctly.
You now should be able to do this:
>>> (if (= 3 4) 1 2)
Evaluating: (if (= 3 4) 1 2)
Evaluating: (= 3 4)
Evaluating: =
==> 2
>>> (if (equal? '(1 2) '(1 2)) (cons 'a '(b)) 'kablooie)
Evaluating: (if (equal? (quote (1 2)) (quote (1 2))) (cons (quote a) (quote (b))) (quote kablooie))
Evaluating: (equal? (quote (1 2)) (quote (1 2)))
Evaluating: equal?
Evaluating: (quote (1 2))
Evaluating: (quote (1 2))
Evaluating: (cons (quote a) (quote (b)))
Evaluating: cons
Evaluating: (quote a)
Evaluating: (quote (b))
==> (a b)
>>> (lambda (x y) (+ x y))
Evaluating: (lambda (x y) (+ x y))
==> #[function:anonymous]
>>> (lambda () 'p)
Evaluating: (lambda () (quote p))
==> #[function:anonymous]
>>> (lambda (lst) (cons 1 lst))
Evaluating: (lambda (lst) (cons 1 lst))
==> #[function:anonymous]
You're done! You can now write anything:
>>> (define add1 (lambda (x) (+ x 1)))
Evaluating: (define add1 (lambda (x) (+ x 1)))
Evaluating: (lambda (x) (+ x 1))
==> done
>>> (add1 5)
Evaluating: (add1 5)
Evaluating: add1
Applying: [Func:add1 [x] ...
Evaluating: (+ x 1)
Evaluating: +
Evaluating: x
==> 6
>>> (define make-adder (lambda (x) (lambda (y) (+ x y))))
Evaluating: (define make-adder (lambda (x) (lambda (y) (+ x y))))
Evaluating: (lambda (x) (lambda (y) (+ x y)))
==> done
>>> (define add2 (make-adder 2))
Evaluating: (define add2 (make-adder 2))
Evaluating: (make-adder 2)
Evaluating: make-adder
Applying: [Func:make-adder [x] ...
Evaluating: (lambda (y) (+ x y))
==> done
>>> (add2 19)
Evaluating: (add2 19)
Evaluating: add2
Applying: [Func:add2 [y] ...
Evaluating: (+ x y)
Evaluating: +
Evaluating: x
Evaluating: y
==> 21
>>> (define fact (lambda (n) (if (= n 0) 1 (* n (fact (- n 1))))))
Evaluating: (define fact (lambda (n) (if (= n 0) 1 (* n (fact (- n 1))))))
Evaluating: (lambda (n) (if (= n 0) 1 (* n (fact (- n 1)))))
==> done
>>> (fact 3)
Evaluating: (fact 3)
Evaluating: fact
Applying: [Func:fact [n] ...
Evaluating: (if (= n 0) 1 (* n (fact (- n 1))))
Evaluating: (= n 0)
Evaluating: =
Evaluating: n
Evaluating: (* n (fact (- n 1)))
Evaluating: *
Evaluating: n
Evaluating: (fact (- n 1))
Evaluating: fact
Evaluating: (- n 1)
Evaluating: -
Evaluating: n
Applying: [Func:fact [n] ...
Evaluating: (if (= n 0) 1 (* n (fact (- n 1))))
Evaluating: (= n 0)
Evaluating: =
Evaluating: n
Evaluating: (* n (fact (- n 1)))
Evaluating: *
Evaluating: n
Evaluating: (fact (- n 1))
Evaluating: fact
Evaluating: (- n 1)
Evaluating: -
Evaluating: n
Applying: [Func:fact [n] ...
Evaluating: (if (= n 0) 1 (* n (fact (- n 1))))
Evaluating: (= n 0)
Evaluating: =
Evaluating: n
Evaluating: (* n (fact (- n 1)))
Evaluating: *
Evaluating: n
Evaluating: (fact (- n 1))
Evaluating: fact
Evaluating: (- n 1)
Evaluating: -
Evaluating: n
Applying: [Func:fact [n] ...
Evaluating: (if (= n 0) 1 (* n (fact (- n 1))))
Evaluating: (= n 0)
Evaluating: =
Evaluating: n
==> 6