#lang racket ; http://www.radford.edu/itec380/2014fall-ibarland/Homeworks/Project/ ; Ian Barland, 2008.Nov.27, updates through 2014.Nov.04 ; We'll run this program in language-level 'Racket', *not* 'Advanced Student'. ; (This is because we are exporting our functions ; as a module, so the test-cases can use them.) ; This next line exports all functions ; (in Java terms: a package where everything is public). ; Alternately, you could `provide` just the specific names you wanted public. ; (provide (all-defined-out)) (require "scanner.rkt") ; scanner.rkt must be in the same dir as this file. (require test-engine/scheme-tests) ;;;;;;;;;;; language P0 ;;;;;;;;;;;;;; ; An Expr is one of: ; - a number ; - (make-paren-expr [Expr]) ; - (make-bin-expr [Expr] [Bin-op] [Expr]) ; - (make-whenz-expr [Expr] [Expr] [Expr]) ; ; A Bin-op is a string in the list BIN_OP_TOKENS: ; (define BIN_OP_TOKENS (list "+++" "---" "***")) (define (bin-op? any-val) (member any-val BIN_OP_TOKENS)) (define-struct bin-expr (left op right) #:transparent) (define-struct paren-expr (e) #:transparent) (define-struct whenz-expr (cond then else) #:transparent) ; ; The keyword 'transparent' makes structs with 'public' fields; ; in particular check-expect can inspect these structs. #| Examples of the data: 8 (written as "8") (make-paren-expr 8) (written as "<8>") (make-bin-expr 8 "+++" 4) (written as "(: 8 +++ 4 :)") (make-bin-expr (make-bin-expr 8 "+++" 4) "***" 5) (written as "(: (: 8 +++ 4 :) *** 5 :)" |# ; parse!: ( scanner -> expr) ; Return (our internal, parse-tree representation of) the first P0 expr ; at the front of the scanner. ; Side effect: Consumes tokens from scnr corresponding exactly ; to the returned expr. ; ; "recursive descent parsing" ; (define (parse! scnr) (cond ; The following cond-branches correspond directly to the grammar. [(number? (peek scnr)) (pop! scnr)] [(string=? (peek scnr) "whenz") (let* {[open-whenz (pop! scnr)] ; Pop off the "whenz" we just peeked at. Ignore it. [subexpr1 (parse! scnr)] [the-then-keyword (pop! scnr)] ; pop off the "then"; ignore it. [subexpr2 (parse! scnr)] [the-else-keyword (pop! scnr)] ; pop off the "otherwise"; ignore it. [subexpr3 (parse! scnr)] [the-bang (pop! scnr)] ; pop off the "!"; ignore it. } (make-whenz-expr subexpr1 subexpr2 subexpr3))] [(string=? (peek scnr) "<") (let* {[open-paren (pop! scnr)] [the-inside-expr (parse! scnr)] [close-paren (pop! scnr)] } (make-paren-expr the-inside-expr))] [(string=? (peek scnr) "(") (let* {[open-paren (pop! scnr)] ; Pop off the "(" we just peeked at. Ignore it. [open-colon (pop! scnr)] ; Pop off the ":" that must follow. Ignore it. [subexpr1 (parse! scnr)] ; Read an *entire* expr, even if deeply nested! [operator (pop! scnr)] [subexpr2 (parse! scnr)] [closing-colon (pop! scnr)] [closing-paren (pop! scnr)] } (when (not (string=? closing-paren ")")) (error 'parse! "Expected ')', got: ~v." closing-paren)) (when (not (bin-op? operator)) (error 'parse! "Expected one of ~v, got ~v." BIN_OP_TOKENS operator)) (make-bin-expr subexpr1 operator subexpr2))] [else (error 'parse! "Unrecognized expr: ~v." (peek scnr))])) ; string->expr (-> string expr) ; Convert the given string representing exactly *one* single Expr ; into our internal format (structs). ; (define (string->expr str) (parse! (create-scanner str))) ; expr->string : (-> expr string) (a.k.a. toString) ; Return a human-readable version of our internal representaiton of Exprs. ; (define (expr->string e) (cond [(number? e) (format "~v" e)] [(paren-expr? e) (string-append "<" (expr->string (paren-expr-e e)) ">" )] [(bin-expr? e) (string-append "(:" (expr->string (bin-expr-left e)) " " (bin-expr-op e) " " (expr->string (bin-expr-right e)) ":)" )] [(whenz-expr? e) (string-append "whenz " (expr->string (whenz-expr-cond e)) " then " (expr->string (whenz-expr-then e)) " otherwise " (expr->string (whenz-expr-else e)) "!" )] [else (error 'expr->string "unknown internal format?!: ~v" e)] )) ; eval : (-> expr val) ; Evaluate the given expr. ; (define (eval e) (cond [(number? e) e] [(paren-expr? e) (eval (paren-expr-e e))] [(bin-expr? e) (eval-bin-expr e)] ; defer to a helper [(whenz-expr? e) (if (= (eval (whenz-expr-cond e)) 0) (eval (whenz-expr-then e)) (eval (whenz-expr-else e)))] [else (error 'eval "unknown internal format?!: ~v" e)])) ; eval-bin-expr : (-> bin-expr val) ; (define (eval-bin-expr e) (let* {[left-value (eval (bin-expr-left e))] [right-value (eval (bin-expr-right e))] } (cond [(string=? (bin-expr-op e) "+++") (+ left-value right-value)] [(string=? (bin-expr-op e) "***") (* left-value right-value)] [(string=? (bin-expr-op e) "---") (- left-value right-value)] [else (error 'eval-bin-expr "Syntax error: unknown binary operator '~a' in ~a." (bin-expr-op e) (expr->string e))])))