#lang pl (define-type FLANG [Num Number] [Add FLANG FLANG] [Sub FLANG FLANG] [Mul FLANG FLANG] [Div FLANG FLANG] [Id Symbol] [With Symbol FLANG FLANG] [Fun Symbol FLANG] [Call FLANG FLANG]) ; Note first type (: parse-sexpr : Sexpr -> FLANG) ;; to convert s-expressions into FLANGs (define (parse-sexpr sexpr) (match sexpr [(number: n) (Num n)] [(symbol: name) (Id name)] [(cons 'with more) (match sexpr [(list 'with (list (symbol: name) named) body) (With name (parse-sexpr named) (parse-sexpr body))] [else (error 'parse-sexpr "bad `with' syntax in ~s" sexpr)])] [(cons 'fun more) (match sexpr [(list 'fun (list (symbol: name)) body) (Fun name (parse-sexpr body))] [else (error 'parse-sexpr "bad `fun' syntax in ~s" sexpr)])] [(list '+ lhs rhs) (Add (parse-sexpr lhs) (parse-sexpr rhs))] [(list '- lhs rhs) (Sub (parse-sexpr lhs) (parse-sexpr rhs))] [(list '* lhs rhs) (Mul (parse-sexpr lhs) (parse-sexpr rhs))] [(list '/ lhs rhs) (Div (parse-sexpr lhs) (parse-sexpr rhs))] [(list 'call fun arg) (Call (parse-sexpr fun) (parse-sexpr arg))] [else (error 'parse-sexpr "bad syntax in ~s" sexpr)])) (: parse : String -> FLANG ) (define (parse str) (parse-sexpr (string->sexpr str))) (define-type VAL [NumV Number] [FunV (VAL -> VAL)]) ;; Define a type for functional environments (define-type ENV = Symbol -> VAL) (: EmptyEnv : -> ENV) (define (EmptyEnv) (lambda (id) (error 'lookup "no binding for ~s" id))) (: Extend : Symbol VAL ENV -> ENV) (define (Extend id v rest-env) (lambda (name) (if (eq? name id) v (rest-env name)))) (: lookup : Symbol ENV -> VAL) (define (lookup name env) (env name)) (: arith-op : (Number Number -> Number) VAL VAL -> VAL) ;; gets a Racket numeric binary operator, ;; uses it within a NumV wrapper (define (arith-op op val1 val2) (: NumV->number : VAL -> Number) (define (NumV->number v) (cases v [(NumV n) n] [else (error 'arith-op "expects a number, got: ~s" v)])) (NumV (op (NumV->number val1) (NumV->number val2)))) (: eval : FLANG ENV -> VAL) ;; evaluates FLANG expressions by reducing them to values (define (eval expr env) (cases expr [(Num n) (NumV n)] [(Add l r) (arith-op + (eval l env) (eval r env))] [(Sub l r) (arith-op - (eval l env) (eval r env))] [(Mul l r) (arith-op * (eval l env) (eval r env))] [(Div l r) (arith-op / (eval l env) (eval r env))] [(With bound-id named-expr bound-body) (eval bound-body (Extend bound-id (eval named-expr env) env))] [(Id name) (lookup name env)] [(Fun bound-id bound-body) (FunV (lambda (arg-val) (eval bound-body (Extend bound-id arg-val env))))] [(Call fun-expr arg-expr) (let ([fval (eval fun-expr env)]) (cases fval [(FunV proc) (proc (eval arg-expr env))] [else (error 'eval "`call' expects a function, got: ~s" fval)]))])) (: run : String -> Number) ;; evaluate a FLANG program contained in a string (define (run str) (let ([result (eval (parse str) (EmptyEnv))]) (cases result [(NumV n) n] [else (error 'run "evaluation returned a non-number: ~s" result)]))) (test (run "{call {fun {x} {+ x 1}} 4}") => 5) (test (run "{with {add3 {fun {x} {+ x 3}}} {call add3 1}}") => 4) (test (run "{with {add3 {fun {x} {+ x 3}}} {with {add1 {fun {x} {+ x 1}}} {with {x 3} {call add1 {call add3 x}}}}}") => 7) (test (run "{with {identity {fun {x} x}} {with {foo {fun {x} {+ x 1}}} {call {call identity foo} 123}}}") => 124) (test (run "{with {x 3} {with {f {fun {y} {+ x y}}} {with {x 5} {call f 4}}}}") => 7) (test (run "{call {with {x 3} {fun {y} {+ x y}}} 4}") => 7) (test (run "{call {call {fun {x} {call x 1}} {fun {x} {fun {y} {+ x y}}}} 123}") => 124) (define foo (eval (parse "{fun {x} {+ x 1}}") (EmptyEnv))) (define bar (eval (parse "41") (EmptyEnv))) (: funcall : VAL VAL -> VAL) (define (funcall fun-val arg) (cases fun-val [(FunV f) (f arg)] [else (error 'funcall "oops")])) (funcall foo bar)