Refactored Lambda, implemented reverse name lookups

matthew.willis 2006-08-09 05:57:56 +00:00
parent 835ed00a14
commit 3bf43d3c61
4 changed files with 226 additions and 148 deletions

View File

@ -1,159 +1,47 @@
#! An interpreter for lambda expressions, by Matthew Willis
#! The grammar in BNF is:
#! <expr> ::= <id>
#! <expr> ::= <name>
#! <expr> ::= (<id> . <expr>)
#! <expr> ::= (<expr> <expr>)
#! <line> ::= <expr>
#! <line> ::= <name> : <expr>
REQUIRES: parser-combinators ;
USING: parser-combinators lazy-lists io strings
hashtables sequences prettyprint namespaces kernel ;
REQUIRES: lazy-lists ;
USING: lazy-lists io strings hashtables sequences kernel ;
IN: lambda
: <letter>
#! parses an uppercase or lowercase letter
[ letter? ] satisfy [ ch>string ] <@ ;
: <LETTER>
#! parses an uppercase or lowercase letter
[ LETTER? ] satisfy [ ch>string ] <@ ;
: <number>
#! parses a number
[ digit? ] satisfy [ ch>string ] <@ ;
: <alphanumeric>
#! parses an alphanumeral
<letter> <number> <|> ;
: <ALPHANUMERIC>
#! parses an alphanumeral
<LETTER> <number> <|> ;
TUPLE: lambda-node expr temp-label ;
TUPLE: apply-node func arg ;
TUPLE: variable-node var ;
GENERIC: variable-eq?
M: string variable-eq? ( var string -- bool ) = ;
M: lambda-node variable-eq? ( var lambda-node-pointer -- bool ) eq? ;
GENERIC: substitute
M: lambda-node substitute ( expr var lambda-node -- )
[ lambda-node-expr substitute ] keep [ set-lambda-node-expr ] keep ;
M: apply-node substitute ( expr var apply-node -- )
[ [ apply-node-func substitute ] keep set-apply-node-func ] 3keep
[ apply-node-arg substitute ] keep [ set-apply-node-arg ] keep ;
M: variable-node substitute ( expr var variable-node -- )
#! ( variable-node == var ) ? expr | variable-node
#! this could use multiple dispatch!
[ variable-node-var variable-eq? ] keep swap ( expr variable-node cond )
[ swap ] unless drop ;
: beta-reduce ( expr lambda-node -- reduced-expr )
#! "pass" expr to the lambda-node, returning a reduced expression
dup lambda-node-expr substitute ;
GENERIC: reduce
#! TODO: eta reduction
M: lambda-node reduce ( lambda-node -- reduced-lambda-node )
[ [ lambda-node-expr reduce ] keep set-lambda-node-expr ] keep ;
M: apply-node reduce ( apply-node -- reduced-apply-node )
#! beta-reduction
[ [ apply-node-func reduce ] keep set-apply-node-func ] keep
[ [ apply-node-arg reduce ] keep set-apply-node-arg ] keep
[ apply-node-func dup lambda-node? ] keep swap
[ apply-node-arg swap beta-reduce reduce ] [ nip ] if ;
M: variable-node reduce ( -- ) ;
GENERIC: expr>string
M: lambda-node expr>string ( available-vars lambda-node -- string )
[ uncons swap ] swap slip [ set-lambda-node-temp-label ] 2keep
[ swap ] swap slip lambda-node-expr expr>string swap
[ "(" , , ". " , , ")" , ] { } make concat ;
M: apply-node expr>string ( available-vars apply-node -- string )
[ apply-node-arg expr>string ] 2keep apply-node-func expr>string
[ "(" , , " " , , ")" , ] { } make concat ;
M: variable-node expr>string ( available-vars variable-node -- string )
nip variable-node-var dup string? [ lambda-node-temp-label ] unless ;
GENERIC: replace-names
M: lambda-node replace-names ( names-hash l-node -- node )
[ lambda-node-expr replace-names ] keep [ set-lambda-node-expr ] keep ;
M: apply-node replace-names ( names-hash l-node -- node )
[
[ apply-node-func replace-names ] keep set-apply-node-func
] 2keep [ apply-node-arg replace-names ] keep [ set-apply-node-arg ] keep ;
M: variable-node replace-names ( names-hash variable-node -- node )
[ variable-node-var swap hash ] keep over not [ nip ] [ drop ] if ;
C: lambda-node ( var expr implicit-empty-lambda-node -- lambda-node )
#! store the expr, replacing every occurence of var with
#! a pointer to this lambda-node
[ <variable-node> -rot substitute ] keep [ set-lambda-node-expr ] keep ;
: <id>
#! parses an identifier (string for now)
#! TODO: do we need to enter it into a symbol table?
<letter> <alphanumeric> <*> <:&> [ concat <variable-node> ] <@ ;
: <name>
#! parses a name, which is used in replacement
<ALPHANUMERIC> <+> [ concat ] <@ ;
DEFER: <expr>
: <lambda>
#! parses (<id>.<expr>), the "lambda" expression
#! all occurences of <id> are replaced with a pointer to this
#! lambda expression.
"(" token <id> sp &> "." token sp <&
<expr> sp <&> ")" token sp <&
[ [ first variable-node-var ] keep second <lambda-node> ] <@ ;
: <apply>
#! parses (<expr> <expr>), the function application
"(" token <expr> sp &> <expr> sp <&> ")" token sp <&
[ [ first ] keep second <apply-node> ] <@ ;
: <expr>
[ <id> call ] [ <lambda> call ] [ <apply> call ] <|> <|>
<name> [ <variable-node> ] <@ <|> ;
: <line>
":" token <name> &> <expr> sp <&> "OK" succeed <expr> <&>
<|> ;
: lambda-parse
#! debug word to parse this <expr> and print the result
<line> some call ;
#! every expression has a canonical representation of this form
: bound-variables-list ( -- lazy-list ) 65 lfrom [ ch>string ] lmap ;
: lambda-print ( name expr -- )
bound-variables-list swap expr>string ":" swap append append print flush ;
TUPLE: linterp names reps ;
: (lint>string) ( linterp expr -- linterp )
bound-variables-list swap expr>string over dupd linterp-reps hash
", " join ":" append swap append "=> " swap append ;
: update-names ( names-hash name expr -- names-hash )
swap rot [ set-hash ] keep ;
#! Interpreter: listen-reduce-print loop
: lint ( names-hash -- new-names-hash )
readln [ "." = ] keep swap [ drop ] [
lambda-parse [ first ] keep second pick swap replace-names reduce
[ lambda-print ] 2keep update-names lint
] if ;
C: linterp ( names-hash )
#! take a names hash, and generate the reverse lookup hash from it.
#! TODO: make this really ugly code cleaner
2dup set-linterp-names swap H{ } clone [ swap hash>alist
[ [ first ] keep second bound-variables-list swap expr>string rot
[ hash ] 2keep rot dup not [ drop rot { } swap add -rot ]
[ >r rot r> swap add -rot ] if set-hash ] each-with ] keep
swap [ set-linterp-reps ] keep ;
: lint-read ( -- input )
readln [ "." = ] keep swap ;
: lint-eval ( linterp input -- linterp name expr )
lambda-parse [ first ] keep second pick linterp-names swap replace-names
evaluate ;
: lint>string ( linterp name expr -- linterp )
rot linterp-names -rot [ update-names ] keep [ <linterp> ] dip
(lint>string) ;
: lint-print ( linterp name expr -- linterp )
lint>string print flush ;
: lint-boot ( -- initial-names )
H{ } clone ;
H{ } clone <linterp> ;
: (lint) ( linterp -- linterp )
lint-read [ drop ] [ lint-eval lint-print lint ] if ;
: lint ( -- linterp )
lint-boot (lint) ;

View File

@ -1,4 +1,6 @@
PROVIDE: lambda {
"nodes.factor"
"parser.factor"
"lambda.factor"
} {
"test/lambda.factor"

120
contrib/lambda/nodes.factor Normal file
View File

@ -0,0 +1,120 @@
#! A lambda expression manipulator, by Matthew Willis
REQUIRES: lazy-lists ;
USING: lazy-lists strings arrays hashtables
sequences namespaces words kernel ;
IN: kernel
: dip swap slip ; inline
IN: lambda
TUPLE: lambda-node expr temp-label ;
TUPLE: apply-node func arg ;
TUPLE: variable-node var ;
DEFER: substitute
C: lambda-node ( var expr implicit-empty-lambda-node -- lambda-node )
#! store the expr, replacing every occurence of var with
#! a pointer to this lambda-node
[ <variable-node> -rot substitute ] keep
[ set-lambda-node-expr ] keep ;
GENERIC: (post-order)
#! Traverses the tree while executing a word in post-order
M: lambda-node (post-order) ( data-array word lambda-node -- node )
[ [ lambda-node-expr (post-order) ] keep set-lambda-node-expr ] 3keep
swap execute ;
M: apply-node (post-order) ( data-array word apply-node -- node )
[ [ apply-node-func (post-order) ] keep set-apply-node-func ] 3keep
[ [ apply-node-arg (post-order) ] keep set-apply-node-arg ] 3keep
swap execute ;
M: variable-node (post-order) ( data-array word variable-node -- node )
swap execute ;
: post-order ( node data-array word -- node )
#! the public face of post-order.
rot (post-order) ;
GENERIC: (clone-node)
#! (clone-node) uses both pre and post orders.
#! We could factor out (pre-post-order) and have both clone
#! and the existing post-order invoke that
M: lambda-node (clone-node) ( lambda-node -- node )
dup clone
[ lambda-node-expr (clone-node) ] keep [ set-lambda-node-expr ] keep
[ dup <variable-node> -rot lambda-node-expr substitute ] keep
[ set-lambda-node-expr ] keep ;
M: apply-node (clone-node) ( apply-node -- node )
clone
[ apply-node-func (clone-node) ] keep [ set-apply-node-func ] keep
[ apply-node-arg (clone-node) ] keep [ set-apply-node-arg ] keep ;
M: variable-node (clone-node) ( variable-node -- node )
clone ;
GENERIC: variable-eq?
M: string variable-eq? ( var string -- bool ) = ;
M: lambda-node variable-eq? ( var lambda-node-pointer -- bool ) eq? ;
GENERIC: (substitute)
M: lambda-node (substitute) ( data-array lambda-node -- ) nip ;
M: apply-node (substitute) ( data-array apply-node -- ) nip ;
M: variable-node (substitute) ( data-array variable-node -- )
#! ( variable-node == var ) ? expr | variable-node
#! this could use multiple dispatch!
[ [ first ] keep second ] dip ( expr var variable-node -- )
[ variable-node-var variable-eq? ] keep swap ( expr variable-node cond )
[ swap ] unless drop ;
: substitute ( expr var node -- node )
-rot 2array \ (substitute) post-order ;
: beta-reduce ( expr lambda-node -- expr )
#! "pass" expr to the lambda-node, returning a reduced expression
(clone-node) dup lambda-node-expr substitute ;
GENERIC: (evaluate)
DEFER: evaluate
#! TODO: eta reduction
M: lambda-node (evaluate) ( data-array lambda-node -- node ) nip ;
M: apply-node (evaluate) ( data-array apply-node -- node )
#! beta-reduction
nip [ apply-node-func dup lambda-node? ] keep swap
[ apply-node-arg swap beta-reduce evaluate ] [ nip ] if ;
M: variable-node (evaluate) ( data-array variable-node -- node ) nip ;
: evaluate ( node -- node )
{ } \ (evaluate) post-order ;
GENERIC: (replace-names)
M: lambda-node (replace-names) ( names-hash l-node -- node ) nip ;
M: apply-node (replace-names) ( names-hash l-node -- node ) nip ;
M: variable-node (replace-names) ( names-hash variable-node -- node )
[ variable-node-var swap hash ] keep over not
[ nip ] [ drop (clone-node) ] if ;
: replace-names ( names-hash node -- node )
swap \ (replace-names) post-order ;
GENERIC: expr>string
M: lambda-node expr>string ( available-vars lambda-node -- string )
[ uncons swap ] dip [ set-lambda-node-temp-label ] 2keep
[ swap ] dip lambda-node-expr expr>string swap
[ "(" , , ". " , , ")" , ] { } make concat ;
M: apply-node expr>string ( available-vars apply-node -- string )
[ apply-node-arg expr>string ] 2keep apply-node-func expr>string
[ "(" , , " " , , ")" , ] { } make concat ;
M: variable-node expr>string ( available-vars variable-node -- string )
nip variable-node-var dup string? [ lambda-node-temp-label ] unless ;

View File

@ -0,0 +1,68 @@
#! A parser for lambda expressions, by Matthew Willis
#! The grammar in BNF is:
#! <expr> ::= <id>
#! <expr> ::= <name>
#! <expr> ::= (<id> . <expr>)
#! <expr> ::= (<expr> <expr>)
#! <line> ::= <expr>
#! <line> ::= <name> : <expr>
REQUIRES: parser-combinators ;
USING: parser-combinators strings sequences kernel ;
IN: lambda
: <letter>
#! parses an uppercase or lowercase letter
[ letter? ] satisfy [ ch>string ] <@ ;
: <LETTER>
#! parses an uppercase or lowercase letter
[ LETTER? ] satisfy [ ch>string ] <@ ;
: <number>
#! parses a number
[ digit? ] satisfy [ ch>string ] <@ ;
: <alphanumeric>
#! parses an alphanumeral
<letter> <number> <|> ;
: <ALPHANUMERIC>
#! parses an alphanumeral
<LETTER> <number> <|> ;
: <id>
#! parses an identifier (string for now)
#! TODO: do we need to enter it into a symbol table?
<letter> <alphanumeric> <*> <&:> [ concat <variable-node> ] <@ ;
: <name>
#! parses a name, which is used in replacement
<ALPHANUMERIC> <+> [ concat ] <@ ;
DEFER: <expr>
: <lambda>
#! parses (<id>.<expr>), the "lambda" expression
#! all occurences of <id> are replaced with a pointer to this
#! lambda expression.
"(" token <id> sp &> "." token sp <&
<expr> sp <&> ")" token sp <&
[ [ first variable-node-var ] keep second <lambda-node> ] <@ ;
: <apply>
#! parses (<expr> <expr>), the function application
"(" token <expr> sp &> <expr> sp <&> ")" token sp <&
[ [ first ] keep second <apply-node> ] <@ ;
: <expr>
[ <id> call ] [ <lambda> call ] [ <apply> call ] <|> <|>
<name> [ <variable-node> ] <@ <|> ;
: <line>
":" token <name> &> <expr> sp <&> "OK" succeed <expr> <&>
<|> ;
: lambda-parse
#! debug word to parse this <expr> and print the result
<line> some call ;