#! /usr/bin/env racket #lang racket (require "H0.rkt") (define lang "H") #| A read-eval-print loop ("repl") for our 380 language. To make this runnable from a (*nix-y) command-line, `chmod u+x E-repl.rkt`. Conceptually, the repl is just: (define (repl _) (repl (print-expr (eval (read-expr))))) ; loop forever (tail recursively) (repl #f) ; start it up But then we add minor bells and whistles (print a prompt, exit gracefully on EOF, print opening phrases), so it's a bit longer. @author ibarland@radford.edu @version 2023-Nov-15 @license CC0 |# (require "scanner.rkt") (define in (create-scanner)) ; reads from stdin (define (read-expr [scnr in]) (parse! scnr)) (define (print-expr e) (displayln (expr->string e))) (define examples '("scale 3 to serve 4" "sample 1; add 1 to taste or use 2 instead" "shake 99 bake" #;"if 3 not 4 enough, add 2" #;"substitute x with 5 in skim x off 2" #;"chop 8.1 into -3" #;"recipe using x: add x to 17" #;"use leftover 5 in recipe using x: add x to 17" )) (define adjectives '("Tasty" "Cookin'" "(Half)Baked" "for-the-refined-palate" "home-cooked")) (define welcome-message (format "*** Welcome to ~a, the ~a Language ***" lang (list-ref adjectives (random (length adjectives))))) (printf "\n~a\n~a~a\n" (make-string 80 #\*) (make-string (floor (/ (- 80 (string-length welcome-message)) 2)) #\space) welcome-message) (printf "See https://www.radford.edu/itec380/2024spring-ibarland/Homeworks/Project/~a.rkt\n\n" lang) (unless (empty? examples) (define plural? (cons? (rest examples))) (printf "~a example ~a program~a:\n" (if plural? "Some" "An") lang (if plural? "s" "")) (map (curry printf " ~a\n") examples) ; like `(map displayln examples)`, but adds some indentation for each line. Also, `for-each` instead of `map`. (printf "\n")) ;(printf "Note: You must include spaces around '-' and '+' operators (but not '/' or '*')\n (else `read` gets them confused with number-literals).\n\n") (let repl {[_ #f]} (printf "~a> " lang) (unless (eof-object? (peek in)) (repl (print-expr (eval (read-expr)))))) ; Btw, the above expression uses: ; Three things that are only useful ( there's state-change/mutation: ; - implicit sequencing, in the body ; - the one-armed-if, "unless". [It returns void otherwise] ; - the void function (here, `displayln`) ; - note that "the void value" is an actual value in racket (Cf. python's "None"); ; that's why we have repl take an ignored argument `_`, whose initial value is #f: ; when we loop, that argument is needed to accept the void-value passed to `repl`. ; ; and one thing that is super-handy in general racket: ; - named-let: after the word `let` we provide a name, followed by k local-vars; ; this creates a function with that name taking k parameters. ; Named-let is a handy way to make a function *and* call it ; ("passing" it the initializers for those k local vars, `#f` in this case). ; - okay, a function with default-args is also handy, which named-let is *kinda* doing for us here. ; But you can write: ; `(define (foo a [b 6]) (+ a b))`, and now ; `(foo 3 4)` returns 7 ; and `(foo 5)` (no args passed) returns 11. (printf "\nThank you for visiting ~a.\n" lang)