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) ==> xThe 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