This commit is contained in:
2026-03-03 22:51:55 -05:00
parent 560dec5783
commit 2dcf5ba10f
+88 -24
View File
@@ -10,6 +10,7 @@
| { bind {{ <id> <TOY> } ... } <TOY> ... } | { bind {{ <id> <TOY> } ... } <TOY> ... }
| { bindrec {{ <id> <TOY> } ... } <TOY> ... } | { bindrec {{ <id> <TOY> } ... } <TOY> ... }
| { fun { <id> ... } <TOY> ... } | { fun { <id> ... } <TOY> ... }
| { rfun { <id> ... } <TOY> ... }
| { <TOY> <TOY> ... } | { <TOY> <TOY> ... }
| { if <TOY> <TOY> <TOY> } | { if <TOY> <TOY> <TOY> }
| { set! <id> <TOY> } | { set! <id> <TOY> }
@@ -22,6 +23,7 @@
[Bind (Listof Symbol) (Listof TOY) (Listof TOY)] [Bind (Listof Symbol) (Listof TOY) (Listof TOY)]
[Bindrec (Listof Symbol) (Listof TOY) (Listof TOY)] [Bindrec (Listof Symbol) (Listof TOY) (Listof TOY)]
[Fun (Listof Symbol) (Listof TOY)] [Fun (Listof Symbol) (Listof TOY)]
[Rfun (Listof Symbol) (Listof TOY)]
[Call TOY (Listof TOY)] [Call TOY (Listof TOY)]
[If TOY TOY TOY] [If TOY TOY TOY]
[Set Symbol TOY]) [Set Symbol TOY])
@@ -49,23 +51,25 @@
(match sexpr (match sexpr
[(list b (list (list (symbol: names) (sexpr: nameds)) ...) body ...) [(list b (list (list (symbol: names) (sexpr: nameds)) ...) body ...)
(if (not (null? body)) (if (not (null? body))
(if (unique-list? names) (if (unique-list? names)
((if (symbol=? b 'bind) Bind Bindrec) ((if (symbol=? b 'bind) Bind Bindrec)
names names
(map parse-sexpr nameds) (map parse-sexpr nameds)
(last (map parse-sexpr body))) (map parse-sexpr body))
(error 'parse-sexpr "duplicate `~s' names: ~s" binder names)) (error 'parse-sexpr "duplicate `~s' names: ~s" binder names))
(error 'parse-sexpr "bind expression missing body: ~s" sexpr))] (error 'parse-sexpr "bind expression missing body: ~s" sexpr))]
[else (error 'parse-sexpr "bad `~s' syntax in ~s" binder sexpr)])] [else (error 'parse-sexpr "bad `~s' syntax in ~s" binder sexpr)])]
; Function definition. ; Reference function definition.
[(cons 'fun more) [(cons (and funer (or 'fun 'rfun)) more)
(match sexpr (match sexpr
[(list 'fun (list (symbol: names) ...) body) [(list f (list (symbol: parameters) ...) body ...)
(if (unique-list? names) (if (not (null? body))
(Fun names (parse-sexpr body)) (if (unique-list? parameters)
(error 'parse-sexpr "duplicate `fun' names: ~s" names))] ((if (symbol=? f 'fun) Fun Rfun) parameters (map parse-sexpr body))
[else (error 'parse-sexpr "bad `fun' syntax in ~s" sexpr)])] (error 'parse-sexpr "bad parameters defined for function in ~s" sexpr))
(error 'parse-sexpr "disemobodied function in ~s" sexpr))]
[else (error 'parse-sexpr "bad function syntax in ~s" sexpr)])]
; Conditional. ; Conditional.
[(cons 'if more) [(cons 'if more)
@@ -107,7 +111,8 @@
(define-type VAL (define-type VAL
[RktV Any] [RktV Any]
[FunV (Listof Symbol) TOY ENV] ; Parameters, body expressions, whether it's an Rfun, environment.
[FunV (Listof Symbol) (Listof TOY) Boolean ENV]
[PrimV ((Listof VAL) -> VAL)] [PrimV ((Listof VAL) -> VAL)]
[VoidV]) [VoidV])
@@ -133,8 +138,6 @@
names vals) names vals)
newenv) newenv)
(: lookup : Symbol ENV -> (Boxof VAL)) (: lookup : Symbol ENV -> (Boxof VAL))
;; lookup a symbol in an environment, frame by frame, ;; lookup a symbol in an environment, frame by frame,
;; return its value or throw an error if it isn't bound ;; return its value or throw an error if it isn't bound
@@ -164,7 +167,7 @@
(define (racket-func->prim-val racket-func) (define (racket-func->prim-val racket-func)
(define list-func (make-untyped-list-function racket-func)) (define list-func (make-untyped-list-function racket-func))
(box (PrimV (lambda (args) (box (PrimV (lambda (args)
(RktV (list-func (map unwrap-rktv args))))))) (RktV (list-func (map unwrap-rktv args)))))))
;; The global environment has a few primitives: ;; The global environment has a few primitives:
(: global-environment : ENV) (: global-environment : ENV)
@@ -195,18 +198,20 @@
[(Num n) (RktV n)] [(Num n) (RktV n)]
[(Id name) (unbox (lookup name env))] [(Id name) (unbox (lookup name env))]
[(Bind names exprs bound-body) [(Bind names exprs bound-body)
(eval bound-body (extend names (map eval* exprs) env))] (eval-body bound-body (extend names (map eval* exprs) env))]
[(Bindrec names exprs bound-body) [(Bindrec names exprs bound-body)
(eval bound-body (extend-rec names exprs env))] (eval-body bound-body (extend-rec names exprs env))]
[(Fun names bound-body) [(Fun names bound-body)
(FunV names bound-body env)] (FunV names bound-body #f env)]
[(Rfun names bound-body)
(FunV names bound-body #t env)]
[(Call fun-expr arg-exprs) [(Call fun-expr arg-exprs)
(let ([fval (eval* fun-expr)] (let ([fval (eval* fun-expr)]
[arg-vals (map eval* arg-exprs)]) [arg-vals (map eval* arg-exprs)])
(cases fval (cases fval
[(PrimV proc) (proc arg-vals)] [(PrimV proc) (proc arg-vals)]
[(FunV names body fun-env) [(FunV names body ref? fun-env)
(eval body (extend names arg-vals fun-env))] (if ref? (eval-rfun names arg-exprs body fun-env) (eval-body body (extend names arg-vals fun-env)))]
[else (error 'eval "function call with a non-function: ~s" [else (error 'eval "function call with a non-function: ~s"
fval)]))] fval)]))]
[(If cond-expr then-expr else-expr) [(If cond-expr then-expr else-expr)
@@ -219,6 +224,25 @@
(set-box! (lookup thing env) (eval* as)) ; We are not a lazy language. (set-box! (lookup thing env) (eval* as)) ; We are not a lazy language.
(VoidV)])) (VoidV)]))
(: eval-rfun : (Listof Symbol) (Listof TOY) (Listof TOY) ENV -> VAL)
(define (eval-rfun params args body env)
(eval-body body (raw-extend params (get-boxes args env) env)))
(: get-boxes : (Listof TOY) ENV -> (Listof (Boxof VAL)))
(define (get-boxes ids env)
(: boxes (Listof (Boxof VAL)))
(define boxes '())
(for-each (lambda ([id : TOY])
(cases id
[(Id name) (set! boxes (cons (lookup name env) boxes))]
[else (error 'get-boxes "non-identifier")])) ids)
boxes)
(: eval-body : (Listof TOY) ENV -> VAL)
; Evaluates a body of expressions, returning the result of the last.
(define (eval-body exprs env)
(list-ref (map (lambda ([expr : TOY]) (eval expr env)) exprs) (sub1 (length exprs))))
(: run : String -> Any) (: run : String -> Any)
;; evaluate a TOY program contained in a string ;; evaluate a TOY program contained in a string
(define (run str) (define (run str)
@@ -230,6 +254,46 @@
;;; ---------------------------------------------------------------- ;;; ----------------------------------------------------------------
;;; Tests ;;; Tests
(test (run "
{bind
{
{swap!
{rfun {x y}
{bind {{tmp x}}
{set! x y}
{set! y tmp}
}
}
}
{a 1}
{b 2}
}
{swap! a b}
{+ a {* 10 b}}
}
") => 12)
(test (run "{bind {{make-counter
{fun {}
{bind {{c 0}}
{fun {}
{set! c {+ 1 c}}
c}}}}}
{bind {{c1 {make-counter}}
{c2 {make-counter}}}
{* {c1} {c1} {c2} {c1}}}}")
=> 6)
(test (run "{bindrec {{foo {fun {}
{set! foo {fun {} 2}}
1}}}
{+ {foo} {* 10 {foo}}}}")
=> 21)
(test (run "{bindrec {{fact {fun {n} (test (run "{bindrec {{fact {fun {n}
{if {= 0 n} {if {= 0 n}
@@ -237,7 +301,7 @@
{* n {fact {- n 1}}}}}}} {* n {fact {- n 1}}}}}}}
{fact 5}}") {fact 5}}")
=> 120) => 120)
(test (run " (test (run "
{bind {{x 10}} {bind {{x 10}}