2006-12-30
Factor Pattern Matching Additions
I've made a couple of additions to the pattern matching library I wrote for Factor. The idea came from a suggestion from Slava in #concatenative.
The main addition is a 'match-replace' word. This matches an object against a pattern and constructs a new object given a second pattern. The second pattern can use pattern matching variables from the first pattern to control the object construction. Here are some examples:
USE: match
MATCH-VARS: ?a ?b ?c ;
{ 1 2 } { ?a ?b } { ?b ?a } match-replace .
! => { 2 1 }
TUPLE: foo one two ;
{ 1 2 { 3 4 } } { ?a _ { _ ?b } } T{ foo f ?a ?b } match-replace .
! => T{ foo f 1 4 }
1 2 <foo> T{ foo f ?a ?b } T{ foo f ?b ?a } match-replace .
! => T{ foo f 2 1 }
This is built on top of a 'replace-patterns' word which replaces pattern matching variables with the relevant symbol values in the accessable namespaces:
{ 1 2 } { ?a ?b } match [
{ ?b ?a } replace-patterns
] bind .
! => { 2 1 }
These words have been added to the 'libs/match' library and should appear in the darcs version of Factor soon. They are in my repository if you want them now.
Just for fun I created a 'shuffle' word that lets you do stack shuffling via pattern matching. The implementation of the word is complex as it manipulates Factor's datastack directly:
: shuffle ( pattern1 pattern2 -- )
dupd >vector >r >vector >r length >r
datastack clone r> over length swap - 2dup
tail r> r> match-replace
>r head r> append set-datastack ;
As it uses 'datastack' and 'set-datastack' this word cannot be compiled. I haven't added it to the library for this reason and it encourages complex stack effects which should be avoided but it's pretty neat that Factor allows this sort of thing to be written.
It takes two patterns on the stack. The first is how the stack looks now, the second is how it should look after the shuffling. Here's some examples of how standard Factor stack shuffling words look using this method:
! drop
1 2 3 { _ } { } shuffle .s clear
! => 1
! 2
! nip
1 2 3 { _ ?b } { ?b } shuffle .s clear
! => 1
! 3
! pick
1 2 3 { ?a ?b ?c } { ?a ?b ?c ?a } shuffle .s clear
! => 1
! 2
! 3
! 1
! 2dup
1 2 3 { ?a ?b } { ?a ?b ?a ?b } shuffle .s clear
! => 1
! 2
! 3
! 2
! 3