Search code examples
functional-programmingschemeracketinterpretermit-scheme

Scheme/Racket interpreter for math expressions


I am trying to make a math-expression interpreter for a project, but I have difficulty in parsing unusual (unary) expressions like '(- -1), '(- (- -1)), '(* 10), '(/ 10) and other similar expressions.

(define (math-eval expr)
  (define (helper op expr)
    (cond [(and (or (eqv? + op) (eqv? - op)) (null? expr)) 0]
          [(and (or (eqv? * op) (eqv? / op)) (null? expr)) 1]
          [(eqv? '+ (car expr)) (helper + (cdr expr))]
          [(eqv? '- (car expr)) (helper - (cdr expr))]
          [(eqv? '* (car expr)) (helper * (cdr expr))]
          [(eqv? '/ (car expr)) (helper / (cdr expr))]
          [(atom? (car expr))
           (op (car expr) (helper op (cdr expr)))]
          [else (+ (helper + (car expr)) (helper + (cdr expr)))]))
  (helper + expr))

This function accepts list and I think that the problem is in the atom? option, but I am not sure how to fix it. I would be very thankful if you could help me or give me directions how to solve my problem.


Solution

  • Try to build math-eval recursively:

    • If expr is number, return that number.
    • If expr is symbol of function, return that function.
    • If expr is list, evaluate each element of list and then use apply to call function (first element of list, car) on numbers (rest of elements, cdr).

    Here is helper function, which returns function for given symbol:

    (define (fetch-function expr)
      (second
       (assoc expr (list (list '+ +)
                         (list '- -)
                         (list '* *)
                         (list '/ /)))))
    

    Examples:

    > (fetch-function '+)
    #<procedure:+>
    > (fetch-function '-)
    #<procedure:->
    

    And here is recursive math-eval, working as described:

    (define (math-eval expr)
      (cond ((number? expr) expr)
            ((symbol? expr) (fetch-function expr))
            (else (let ((op (car expr))
                        (args (cdr expr)))
                    (apply (math-eval op)
                           (map math-eval args))))))
    

    Examples:

    > (math-eval '(- -1))
    1
    > (math-eval '(- (- -1)))
    -1
    > (math-eval '(* 10))
    10
    > (math-eval '(/ 10))
    1/10