pragmaticTheo
pragmaticTheo

Reputation: 21

Evaluating cons expressions using "eval" and "map" (Racket interpreter written in Racket)

I've been tasked with writing a Racket interpreter using the Racket language itself for one of my classes in a series of 5 labs, and the first one concerns writing two basic functions: lookup, and evaluate. The lookup function is given a list that serves as our environment, containing a table of symbols, and a single symbol. As you'll surely notice, for the purposes of this first assignment, the table is very basic. I've already written and tested this function and all is well there.

The evaluate function has me in a rut, however. It does correctly evaluate the primitive procedures I've given it: '(+ 3 4 (- 5 2) 3) does indeed evaluate to 13, for example. It also works for singular cons expression, such as '(cons 1 null) = '(1) or '(cons 2 4) = '(2 . 5). But it throws an error whenever I attempt to evaluate a compound statement of multiple cons expressions. The one particular case I've been examining is '(cons 1 (cons 2 null)). After much debugging, I discovered that the problem is as follows:

  1. Map evaluates the final expression, '(2 null) correctly as '(2). But then...
  2. The Map waiting on the above mentioned Map procedure to finish, somehow ends up evaluating to '(1 (2)). I expected it to evaluate to '(1 '(2)), which would have worked, but it doesn't, and I don't fully understands why. But ultimately, as a result of this...
  3. The procedure cons is consed with the list returned by Map, yielding '(cons 1 (2)).
  4. The program attempts to eval this string, but yells at me instead because it doesn't recognize (2) as a list, instead expecting it to be a procedure.
#lang racket
(provide lookup)
(provide evaluate)

(define-namespace-anchor mySpace)
(define ns (namespace-anchor->namespace mySpace))

;The list which serves as the environment for our lookup function.
(define testEnvironment(list
                        (cons 'x 10)
                        (cons '+ +)
                        (cons '- -)
                        (cons '* *)
                        (cons 'y 20)
                        (cons 'cons cons)
                        (cons 'nil '())))
;Assumes that there is at least one element in the environment given for consideration.
(define lookup
  (lambda (symbol environment)
    (if(symbol? symbol)
       (let recursiveLookup((symbol symbol) (environment environment))
         (if(equal? symbol (car (car environment)))
            (cdr (car environment))
            (if(null? (cdr environment))
               (error "This symbol does not exist in the provided environment!")
               (recursiveLookup symbol (cdr environment)))))                 
       (error "The provided argument is not a symbol."))))

(define evaluate
  (lambda (expression environment)
    (if(not (pair? expression))
       (cond         
         ;If the expression is a number, then return that number.
         ((number? expression) expression)
         ;If the expression is null, or contains a symbol equivalent to null, return null
         ((null? expression) null)
         ((equal? null (lookup expression environment)) 'null)
         ;Otherwise, the expression must be some kind of symbol, so look up 
         ; the value of that symbol.
         (else (lookup expression environment)))
       ;If the expression is a list, then use map to evaluate the remaining elements
       ;in the expression. Then pair the first element (which is a procedure)
       ;to the resulting list. Finally, apply the procedure to the resulting function
       ;using evaluate to obtain the final value.
       (eval (cons (lookup (car expression) environment) 
                   (map (lambda (listElement) 
                          (evaluate listElement environment)) 
                        (cdr expression)))
             ns))))

It's very important to me to better understand what's happening here and how to fix it because if I can't manage this much, I can't imagine the remainder of writing this Racket interpreter will be easy with a shaky foundation. I have spent about 15 hours trying to figure this out in numerous ways but I've hit a wall... I'm afraid I'm over-thinking this problem, but even so it's immensely frustrating because the procedures seem to work flawlessly in any case that doesn't involve multiple cons expressions. It has made me a very sad and ignorant feeling panda.

I hope I phrased my question well enough for you all, and I extend my everlasting gratitude to anyone who takes the time to assist me here! Thank you all for existing because I'm much too timid to approach anyone in the real world for help :3

Upvotes: 2

Views: 766

Answers (1)

Sylwester
Sylwester

Reputation: 48745

You should not use eval. Use apply:

(let ((exprs (map (lambda (listElement) (evaluate listElement environment)) expression))
  (apply (car exprs) (cdr exprs)))

eval evaluates all arguments. For numbers they evaluate to themselves, but with cons it tries to evaluate the list with number and it is not valid Scheme code. Your arguments are already evaluated so apply does the same as eval except it uses the passed arguments as is. Later you probably need to make your own version of this to support user defined procedures.

I think it's good to evaluate the operand and not assume its a symbol. Eg. this should work :

((car (cons + '())) 4 5)
; ==> 9

Upvotes: 0

Related Questions