#lang racket ;; File: evaluator.rkt ;; Author: You! ;; A starting point for the unilang evaluator (require "unicalc-db.rkt") (require "unicalc.rkt") (require "parser.rkt") (require htdp/testing) ;; for check-expect (require racket/string) ;; for string-join (provide read-eval-print) ;;;;;;;;;;;;;;;;;;; ;; Configuration ;; ;;;;;;;;;;;;;;;;;;; ;; How to prompt the user for input (define prompt-string "input > ") ;; The input that makes the read-eval-print loop stop. (define quit-string "quit") ;; How to label the output of the evaluator (define result-string "result: ") ;; The main read-eval-print loop (define (read-eval-print) (newline) (display prompt-string) (let* ( (expression (read-line)) ) (if (equal? expression quit-string) '() ;; exit ;; Parse and do the input (let* ([parsed (parse expression)]) (interpret-unicalc parsed) ;; Repeat (read-eval-print))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Helper Functions for Displaying Results ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; function: UQL->string ;; Input: a UQL ;; Output: a reasonably "nice" representation of the UQL (define (UQL->string UQL) (let* ([part1 (real->decimal-string (get-quant UQL) 5)] [part2 (if (> (get-error UQL) 0) (string-append "~" (real->decimal-string (get-error UQL) 5)) "")] [part3 " "] [part4 (units->string (get-num UQL))] [part5 (if (null? (get-den UQL)) "" (string-append " / " (units->string (get-den UQL))))]) (string-append part1 part2 part3 part4 part5))) ;; function: units->string ;; Input: a list of symbols ;; Output: a string containing the names of the symbols, ;; separated by spaces. ;; Helper function for UQL->string (define (units->string L) (string-join (map symbol->string L) " ")) ;;;;;;;;;;;;;;;;;;;;;;;;;; ;; The Main Interpreter ;; ;;;;;;;;;;;;;;;;;;;;;;;;;; ;; function: interpret-unicalc ;; Input: a *top-level* abstract syntax tree ;; Output: unspecified ;; Executes the effect of the given unicalc input, ;; either extending the database with a new definition, ;; or evaluating and printing an expression. (define (interpret-unicalc ast) (cond [(null? ast) '()] [(equal? (first ast) 'def) (let* ([symbol (second ast)] [expr (third ast)]) ;; Definition: change the global database of units ;; (but print nothing) (extend-db symbol (eval-expr expr)))] [(equal? (first ast) 'eval) (let* ([expr (second ast)]) ;; Expression evaluation: evaluate it and ;; display the result (display result-string) (display (UQL->string (eval-expr expr))))] [else (error "Invalid abstract syntax tree")])) ;; Function: eval-expr ;; Input: an expression AST (corresponding to L or below in the grammar) ;; Output: a UQL, the result of that expression ;; Evaluates unicalc expressions. (define (eval-expr ast) ;; Recursively traverse the input tree, with different ;; cases depending on the symbol at the root. (cond [(null? ast) (error "Empty input!")] [(equal? (first ast) 'uql) (make-UQL (second ast) (third ast) (fourth ast) (fifth ast))] ;; FIX ME! ADD MORE CASES! [else (error "Unrecognized expression")]))