1 #! A lambda expression manipulator, by Matthew Willis
2 USING: lazy-lists strings arrays hashtables
3 sequences namespaces words parser kernel ;
8 TUPLE: lambda-node self expr name ;
9 TUPLE: apply-node func arg ;
10 TUPLE: var-node name ; #! var is either a var, name, or pointer to a lambda-node
11 TUPLE: beta-node expr lambdas ; #! a namespace node
12 TUPLE: alien-node word ;
14 M: lambda-node equal? 2drop f ;
17 M: lambda-node bind-var ( binding lambda -- )
18 lambda-node-expr bind-var ;
20 M: apply-node bind-var ( binding apply -- )
21 [ apply-node-func bind-var ] 2keep apply-node-arg bind-var ;
23 M: var-node bind-var ( binding var-node -- )
24 2dup var-node-name swap lambda-node-name =
25 [ set-var-node-name ] [ 2drop ] if ;
27 M: alien-node bind-var ( binding alien -- ) 2drop ;
29 C: lambda-node ( expr var lambda -- lambda )
30 swapd [ set-lambda-node-name ] keep
31 [ set-lambda-node-expr ] 2keep
32 dup [ set-lambda-node-self ] keep
33 [ swap bind-var ] keep ;
36 #! push the beta further down the syntax tree
37 #! this is how lambda achieves lazy beta reduction and efficient cloning.
38 #! everything outside of the beta must have been cloned.
39 M: lambda-node beta-push ( beta lambda -- lambda )
40 clone dup lambda-node-expr pick set-beta-node-expr
41 [ set-lambda-node-expr ] keep ;
43 M: apply-node beta-push ( beta apply -- apply )
44 #! push the beta into each branch, cloning the beta
46 pick apply-node-func swap [ set-beta-node-expr ] keep swap
47 rot apply-node-arg swap [ set-beta-node-expr ] keep
50 M: var-node beta-push ( beta var -- expr )
51 #! substitute the variable with the appropriate entry from the
53 tuck var-node-name swap beta-node-lambdas hash dup
56 M: beta-node beta-push ( beta inner-beta -- beta )
57 #! combines the namespaces of two betas
58 dup beta-node-lambdas rot beta-node-lambdas hash-union
59 swap [ set-beta-node-lambdas ] keep ;
61 M: alien-node beta-push ( beta alien -- alien ) nip ;
63 : beta-reduce ( apply -- beta )
64 #! construct a beta-node which carries the namespace of the lambda
65 dup apply-node-arg swap apply-node-func dup lambda-node-expr -rot
66 lambda-node-self H{ } clone [ set-hash ] keep <beta-node> ;
69 : left-reduce ( apply -- apply/f )
70 #! we are at an application node -- evaluate the function
71 dup apply-node-func evaluate dup
72 [ swap [ set-apply-node-func ] keep ]
75 : alien-reduce ( apply -- node/f )
76 #! we have come to an alien application, which requires us to
77 #! fully normalize the argument before proceeding
78 dup apply-node-arg evaluate dup
79 [ swap [ set-apply-node-arg ] keep ]
80 [ #! right side is normalized, we are ready to do the alien application
81 drop dup apply-node-arg swap apply-node-func
82 alien-node-word "lambda" lookup execute
87 #! beta-reduction, beta-pushing, and name replacing.
88 : normalize ( expr -- expr )
89 dup evaluate [ nip normalize ] when* ;
91 M: lambda-node evaluate ( lambda -- node/f ) drop f ;
93 M: apply-node evaluate ( apply -- node )
94 dup apply-node-func lambda-node?
97 dup apply-node-func alien-node?
102 M: var-node evaluate ( var -- node/f )
103 var-node-name lambda-names get hash ;
105 M: beta-node evaluate ( beta -- node/f )
106 dup beta-node-expr beta-push ;
108 M: alien-node evaluate ( alien -- node/f ) drop f ;
111 M: lambda-node expr>string ( lambda-node -- string )
113 dup "(" , lambda-node-name , ". " ,
114 lambda-node-expr expr>string , ")" ,
117 M: apply-node expr>string ( apply-node -- string )
119 dup "(" , apply-node-func expr>string , " " ,
120 apply-node-arg expr>string , ")" ,
123 M: var-node expr>string ( variable-node -- string )
124 var-node-name dup string? [ lambda-node-name ] unless ;
126 M: alien-node expr>string ( alien-node -- string )
127 [ "[" , alien-node-word , "]" , ] { } make concat ;
129 M: beta-node expr>string ( beta -- string )
130 [ "beta<" , beta-node-expr expr>string , ">" , ] { } make concat ;