Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.postscript > #3345 > unrolled thread
| Started by | M Joshua Ryan <luser.droog@gmail.com> |
|---|---|
| First post | 2019-03-16 17:00 -0500 |
| Last post | 2019-03-20 13:20 -0700 |
| Articles | 3 — 2 participants |
Back to article view | Back to comp.lang.postscript
Parsing yet again all over again M Joshua Ryan <luser.droog@gmail.com> - 2019-03-16 17:00 -0500
Re: Parsing yet again all over again luser droog <luser.droog@gmail.com> - 2019-03-19 16:29 -0700
Re: Parsing yet again all over again luser droog <luser.droog@gmail.com> - 2019-03-20 13:20 -0700
| From | M Joshua Ryan <luser.droog@gmail.com> |
|---|---|
| Date | 2019-03-16 17:00 -0500 |
| Subject | Parsing yet again all over again |
| Message-ID | <vriutvg2qy23.fsf@gmail.com> |
I was having difficulty actually building larger tools with my
combinators, so I dug into some more papers, several by Sierstraa and
learned about the importance of lazy evaluation as a tool. And
particularly, the importance of lazy lists as a data structure became
apparent.
I tried digging through Sierstra paper on implementing it in Java-- and
going nowhere fast-- but I finally got a better handle on it from Graham
Hutton's paper "Higher Order Functions for Parsing" which uses the
Miranda language. Miranda seems to have most of the good stuff that
Haskell has but a little easier to grasp, for me anyway.
So, starting over yet again the other profound realization is that my
weird problem of propagating the results from the callbacks is
completely resolved by imitating the example code more closely. In the
Miranda and Haskell parsers, the returned value is an array of
tuples. Each element of the array is a successful parse state which is a
tuple of a *witness* value and the remainder of the input, as a lazy
list. This witness value is the very "result of the callbacks" that I
needed. But they don't need to be orchestrated and managed through
callbacks and whatnot, but built in.
So I modelled the lazy lists with lots of length 2 arrays where element
0 is the *car* of the list and element 1 is *cdr*. My code calls the two
parts 'first' and 'second'. The cdr can be an executable procedure which
must be executed to yield or manifest more of the list.
Right away this makes it trivial to abstract over taking input from a
string vs from a file: they both yield a lazy list of characters. So
they're handled by 1 short function each. :-D
And I expect it will be relatively easy to abstract over lists of
characters or lists of tokens, so I can build 2 strata of lexer/parser
... and this may help when getting it to build a usable AST.
But for the current state of the code, there's no fancy input syntax
stuff. No dictionary syntax hijacking. Thanks to the 'satisfy' parser
constructor which takes a predicate function, stuff like 'range' and
'cclass' can be build *sub-atomically* instead of as molecules of
alternates.
Apologetically, there's probably a lot of bewildering jumps from low
abstraction levels to high and back down again. That's the nature of my
current coding style which teeters tenuously upon that very edge.
In the output from the 'word' parser (the two big blobs), you can
distinguish between the numbers that have been *consumed* by the parser
by the negative signs. The result of the 'many' combinator (the Kleene
* star) is every possible match from the longest to shortest.
My 'll' function which does '{load-if-literal-name}deep-map' is a
lot like a closure in the sense that it is a lambda with all the
free variables filled in.
$ cat pcomb/pc8.ps
(struct2.ps) run {
string-input {s} { [ s {first}stopped{ pop pop 0 0 rep ] }
{ [ s rest /string-input cvx ] cvx ] } ifelse } @func
file-input {f} { [ f read { [ f /file-input cvx ] cvx ] }
{ 0 0 rep ] } ifelse } @func
filename-input {fn} { (r) file file-input } @func
succeed {v} { { [ exch [ exch /v exch ] ] } ll } @func
fail { { pop [] } }
satisfy {pred}{ { dup first /pred exec { xs-x succeed }
{ fail } ifelse exec } ll } @func
literal {x} { { /x eq } ll satisfy } @func
range {a b} { { dup /a ge exch /b le and } ll satisfy } @func
cclass {cc} { { 1 string dup 0 4 3 roll put /cc exch search { pop pop pop true }
{ pop false } ifelse } ll satisfy } @func
using {p f} { { /p exec [ exch { dup 0 2 copy get /f exec put } forall ] } ll } @func
alt2 {p q} { { dup /p exec [ 3 1 roll spill counttomark -1 roll /q exec spill ] } ll } @func
seq2 {p q} { { /p exec dup length 0 gt { [ exch {
spill /q exec exch {
y 0 2 copy get % [] x [] 0 []_0
4 -1 roll exch cons put % [ [ x []_0 ] .. ]
} curry forall
} forall ] } if } ll } @func
? maybe maybe { [] succeed alt2 }
* many many { % p* = p p* | eps
{{{}exec}exec}{}deep-map dup 0 get 3 1 roll seq2 maybe % {{}e} {p then {{{}e}e}}
2 copy 0 exch put exch pop }
+ some some { dup many seq2 }
y { 1 index }
aa { array astore }
ps { (stack:)= pstack }
pc { ps clear }
second { 1 get }
spill { {} forall }
cons { 2 aa }
/@ { { dup xcheck { exec }{ exit } ifelse } loop }
next { dup second xcheck { dup 1 2 copy get @ put second }{ second } ifelse }
x-xs { dup first exch next }
xs-x { dup next exch first }
ll { {load-if-literal-name} deep-map }
deep-map { y type /arraytype ne { exec }{
y xcheck 3 1 roll [ 3 1 roll /deep-map cvx 2 array astore cvx forall ] exch {cvx} if } ifelse }
rep {x} { { [ /x { /x rep } ] } ll } @func
add1 {x} { x @ dup length 0 eq { [] }
{ [ exch dup first 1 add exch next /add1 cvx 2 aa cvx ] } ifelse } @func
take {x n} { n 0 eq { [] }{ [ x @ x-xs n 1 sub take ] } ifelse } @func
} pairs-begin <<
>> begin
/is_a (a) spill literal def
/is_b (b) spill literal def
/alpha (az) spill range def
/digit (09) spill range def
/alnum //alpha //digit alt2 def
/inv //alnum { neg } using def
/word //inv //inv seq2 //inv seq2 //inv seq2 //digit maybe seq2 def
/word //inv many def
{
1 rep pc
1 rep @ pc
1 rep 5 take pc
1 rep add1 5 take pc
(abcdefghjiklm) string-input dup 5 take pc
}exec%pop
(ab34efghjiklm) string-input
is_a
first second inv
first second inv
first second inv
first second inv
pc
(ab345efghjiklm) string-input word pc
{ currentfile file-input word pc } exec
abcdefghijklm
nopq
quit
$ cd pcomb; gsnd -DNOSAFER pc8.ps
GPL Ghostscript 9.22 (2017-10-04)
Copyright (C) 2017 Artifex Software, Inc. All rights reserved.
This software comes with NO WARRANTY: see the file PUBLIC for details.
stack:
{[ 1 {1 rep} ]}
stack:
[1 {1 rep}]
stack:
[1 [1 [1 [1 [1 []]]]]]
stack:
[2 [2 [2 [2 [2 []]]]]]
stack:
[97 [98 [99 [100 [101 []]]]]]
[97 [98 [99 [100 [101 [102 {(ghjiklm) string-input}]]]]]]
stack:
[[-101 [102 {(ghjiklm) string-input}]]]
stack:
[[[-97 [-98 [-51 [-52 [-53 [-101 [-102 [-103 [-104 [-106 [-105 [-107 [-108 [-109 []]]]]]]]]]]]]]] [0 {[ 0 {0 rep} ]}]] [[-97 [-98 [-51 [-52 [-53 [-101 [-102 [-103 [-104 [-106 [-105 [-107 [-108 []]]]]]]]]]]]]] [109 [0 {[ 0 {0 rep} ]}]]] [[-97 [-98 [-51 [-52 [-53 [-101 [-102 [-103 [-104 [-106 [-105 [-107 []]]]]]]]]]]]] [108 [109 [0 {[ 0 {0 rep} ]}]]]] [[-97 [-98 [-51 [-52 [-53 [-101 [-102 [-103 [-104 [-106 [-105 []]]]]]]]]]]] [107 [108 [109 [0 {[ 0 {0 rep} ]}]]]]] [[-97 [-98 [-51 [-52 [-53 [-101 [-102 [-103 [-104 [-106 []]]]]]]]]]] [105 [107 [108 [109 [0 {[ 0 {0 rep} ]}]]]]]] [[-97 [-98 [-51 [-52 [-53 [-101 [-102 [-103 [-104 []]]]]]]]]] [106 [105 [107 [108 [109 [0 {[ 0 {0 rep} ]}]]]]]]] [[-97 [-98 [-51 [-52 [-53 [-101 [-102 [-103 []]]]]]]]] [104 [106 [105 [107 [108 [109 [0 {[ 0 {0 rep} ]}]]]]]]]] [[-97 [-98 [-51 [-52 [-53 [-101 [-102 []]]]]]]] [103 [104 [106 [105 [107 [108 [109 [0 {[ 0 {0 rep} ]}]]]]]]]]] [[-97 [-98 [-51 [-52 [-53 [-101 []]]]]]] [102 [103 [104 [106 [105 [107 [108 [109 [0 {[ 0 {0 rep} ]}]]]]]]]]]] [[-97 [-98 [-51 [-52 [-53 []]]]]] [101 [102 [103 [104 [106 [105 [107 [108 [109 [0 {[ 0 {0 rep} ]}]]]]]]]]]]] [[-97 [-98 [-51 [-52 []]]]] [53 [101 [102 [103 [104 [106 [105 [107 [108 [109 [0 {[ 0 {0 rep} ]}]]]]]]]]]]]] [[-97 [-98 [-51 []]]] [52 [53 [101 [102 [103 [104 [106 [105 [107 [108 [109 [0 {[ 0 {0 rep} ]}]]]]]]]]]]]]] [[-97 [-98 []]] [51 [52 [53 [101 [102 [103 [104 [106 [105 [107 [108 [109 [0 {[ 0 {0 rep} ]}]]]]]]]]]]]]]] [[-97 []] [98 [51 [52 [53 [101 [102 [103 [104 [106 [105 [107 [108 [109 [0 {[ 0 {0 rep} ]}]]]]]]]]]]]]]]] [[] [97 [98 [51 [52 [53 [101 [102 [103 [104 [106 [105 [107 [108 [109 [0 {[ 0 {0 rep} ]}]]]]]]]]]]]]]]]]]
stack:
[[[-97 [-98 [-99 [-100 [-101 [-102 [-103 [-104 [-105 [-106 [-107 [-108 [-109 []]]]]]]]]]]]]] [10 {-file- file-input}]] [[-97 [-98 [-99 [-100 [-101 [-102 [-103 [-104 [-105 [-106 [-107 [-108 []]]]]]]]]]]]] [109 [10 {-file- file-input}]]] [[-97 [-98 [-99 [-100 [-101 [-102 [-103 [-104 [-105 [-106 [-107 []]]]]]]]]]]] [108 [109 [10 {-file- file-input}]]]] [[-97 [-98 [-99 [-100 [-101 [-102 [-103 [-104 [-105 [-106 []]]]]]]]]]] [107 [108 [109 [10 {-file- file-input}]]]]] [[-97 [-98 [-99 [-100 [-101 [-102 [-103 [-104 [-105 []]]]]]]]]] [106 [107 [108 [109 [10 {-file- file-input}]]]]]] [[-97 [-98 [-99 [-100 [-101 [-102 [-103 [-104 []]]]]]]]] [105 [106 [107 [108 [109 [10 {-file- file-input}]]]]]]] [[-97 [-98 [-99 [-100 [-101 [-102 [-103 []]]]]]]] [104 [105 [106 [107 [108 [109 [10 {-file- file-input}]]]]]]]] [[-97 [-98 [-99 [-100 [-101 [-102 []]]]]]] [103 [104 [105 [106 [107 [108 [109 [10 {-file- file-input}]]]]]]]]] [[-97 [-98 [-99 [-100 [-101 []]]]]] [102 [103 [104 [105 [106 [107 [108 [109 [10 {-file- file-input}]]]]]]]]]] [[-97 [-98 [-99 [-100 []]]]] [101 [102 [103 [104 [105 [106 [107 [108 [109 [10 {-file- file-input}]]]]]]]]]]] [[-97 [-98 [-99 []]]] [100 [101 [102 [103 [104 [105 [106 [107 [108 [109 [10 {-file- file-input}]]]]]]]]]]]] [[-97 [-98 []]] [99 [100 [101 [102 [103 [104 [105 [106 [107 [108 [109 [10 {-file- file-input}]]]]]]]]]]]]] [[-97 []] [98 [99 [100 [101 [102 [103 [104 [105 [106 [107 [108 [109 [10 {-file- file-input}]]]]]]]]]]]]]] [[] [97 [98 [99 [100 [101 [102 [103 [104 [105 [106 [107 [108 [109 [10 {-file- file-input}]]]]]]]]]]]]]]]]
Error: /undefined in nopq
Operand stack:
Execution stack:
%interp_exit .runexec2 --nostringval-- --nostringval-- --nostringval-- 2 %stopped_push --nostringval-- --nostringval-- --nostringval-- false 1 %stopped_push 2015 1 3 %oparray_pop 2014 1 3 %oparray_pop 1998 1 3 %oparray_pop 1884 1 3 %oparray_pop --nostringval-- %errorexec_pop .runexec2 --nostringval-- --nostringval-- --nostringval-- 2 %stopped_push --nostringval--
Dictionary stack:
--dict:985/1684(ro)(G)-- --dict:0/20(G)-- --dict:78/200(L)-- --dict:34/58(L)-- --dict:34/34(L)-- --dict:7/10(L)--
Current allocation mode is local
Current file position is 2988
GPL Ghostscript 9.22: Unrecoverable error, exit code 1
[toc] | [next] | [standalone]
| From | luser droog <luser.droog@gmail.com> |
|---|---|
| Date | 2019-03-19 16:29 -0700 |
| Message-ID | <e989e831-41c5-4ed7-a4c0-c3a96fe89608@googlegroups.com> |
| In reply to | #3345 |
On Saturday, March 16, 2019 at 5:01:00 PM UTC-5, luser droog wrote:
> filename-input {fn} { (r) file file-input } @func
Bug. Takes the filename off the stack to make a local variable 'fn',
but the variable is never used.
[toc] | [prev] | [next] | [standalone]
| From | luser droog <luser.droog@gmail.com> |
|---|---|
| Date | 2019-03-20 13:20 -0700 |
| Message-ID | <9a4ac9cd-f660-47bc-bdcd-1cb949ffc907@googlegroups.com> |
| In reply to | #3345 |
On Saturday, March 16, 2019 at 5:01:00 PM UTC-5, luser droog wrote:
> I was having difficulty actually building larger tools with my
> combinators, so I dug into some more papers, several by [Sierstra] and
> learned about the importance of lazy evaluation as a tool. And
> particularly, the importance of lazy lists as a data structure became
> apparent.
>
> I tried digging through Sierstra paper on implementing it in Java-- and
> going nowhere fast-- but I finally got a better handle on it from Graham
> Hutton's paper "Higher Order Functions for Parsing" which uses the
> Miranda language. Miranda seems to have most of the good stuff that
> Haskell has but a little easier to grasp, for me anyway.
>
> So, starting over yet again the other profound realization is that my
> weird problem of propagating the results from the callbacks is
> completely resolved by imitating the example code more closely. In the
> Miranda and Haskell parsers, the returned value is an array of
> tuples. Each element of the array is a successful parse state which is a
> tuple of a *witness* value and the remainder of the input, as a lazy
> list. This witness value is the very "result of the callbacks" that I
> needed. But they don't need to be orchestrated and managed through
> callbacks and whatnot, but built in.
>
> So I modelled the lazy lists with lots of length 2 arrays where element
> 0 is the *car* of the list and element 1 is *cdr*. My code calls the two
> parts 'first' and 'second'. The cdr can be an executable procedure which
> must be executed to yield or manifest more of the list.
>
> Right away this makes it trivial to abstract over taking input from a
> string vs from a file: they both yield a lazy list of characters. So
> they're handled by 1 short function each. :-D
>
> And I expect it will be relatively easy to abstract over lists of
> characters or lists of tokens, so I can build 2 strata of lexer/parser
> ... and this may help when getting it to build a usable AST.
>
> But for the current state of the code, there's no fancy input syntax
> stuff. No dictionary syntax hijacking. Thanks to the 'satisfy' parser
> constructor which takes a predicate function, stuff like 'range' and
> 'cclass' can be build *sub-atomically* instead of as molecules of
> alternates.
>
> Apologetically, there's probably a lot of bewildering jumps from low
> abstraction levels to high and back down again. That's the nature of my
> current coding style which teeters tenuously upon that very edge.
>
> In the output from the 'word' parser (the two big blobs), you can
> distinguish between the numbers that have been *consumed* by the parser
> by the negative signs. The result of the 'many' combinator (the Kleene
> * star) is every possible match from the longest to shortest.
>
> My 'll' function which does '{load-if-literal-name}deep-map' is a
> lot like a closure in the sense that it is a lambda with all the
> free variables filled in.
>
>
Some further elaboration and exercise (and debugging very stupid mistakes),
and I almost have the regular expression parser working. That is, it's
working but doesn't handle the full regex syntax just yet.
One notable discovery is that when I make a parser that has recursive
loop in it, I can mark it as 'executeonly' which allows me to call '=='
and 'pstack' on any parser and it will just print -array- when there's
a loop further down.
This new way of writing everything seems more concise and flexible,
but *weirder*. The 'into' combinator is very weird. It takes two
parsers and a variable name. It executes the left parser and sets
the result as the value associated with the passed variable name,
then executes the right parser. The right parser and any attached
code can access the left result and use it. But it requires extra
gyrations to make sure that left result gets returned if an optional
right part fails.
A little further googling and pdfing led me to Hutton and Meijer,
*Monadic Parser Combinators* and it seems to be offering exactly
what I want, at the expense of adding another layer of weird. sigh.
$ for i in struct2.ps pc8.ps pc8re.ps ; do echo '******' $i '*******' ; cat $i ; echo ; echo ; done
****** struct2.ps *******
%!
% struct2.ps An enhanced PostScript syntax for defining functions with named,
% type-checked arguments. Using @func within a block or other construct that uses
% 'pairs' accomplishes a sort of compile-time macro expansion of the shorthand function description.
<< /pairs-begin { pairs begin }
/pairs-def { pairs {def} forall } pop { pairs currentdict copy pop }
/pairs { << exch explode >> }
/explode { { @exec } forall }
/@exec { dup type /nametype eq 1 index xcheck and { exec-if-@ } if }
/exec-if-@ { dup dup length string cvs dup first (@) first eq { exec@ }{ pop } ifelse }
/first { 0 get } /exec@ { exch pop rest cvn cvx exec }
/rest { 1 1 index length 1 sub getinterval }
>> begin {
block { pairs-begin main end }
func { 1 index type /stringtype eq { typed-func }{ simple-func } ifelse }
simple-func { func-begin { end } compose }
typed-func { exch args-and-types reverse { make-type-name } map check-stack 3 1 roll
exch simple-func compose }
func-begin { exch reverse /args-begin load curry exch compose }
args-begin { dup length dict begin { exch def } forall }
args-and-types { /was_x false def [ exch { each-specifier } fortokens fix-last ] dup args exch types }
each-specifier { dup xcheck /is_x exch def is_x was_x and { null exch } if /was_x is_x def }
fix-last { counttomark 2 mod 1 eq { null } if }
check-stack { {pop} 4 index cvlit { cvx /stackunderflow signalerror } curry compose
/if cvx 2 array astore cvx {check-count} exch compose curry
3 index cvlit { cvx /typecheck signalerror } curry
/if cvx 2 array astore cvx {check-types} exch compose compose }
check-count { dup length count 2 sub gt }
check-types { dup length 1 add copy true exch { check-type and } forall exch pop not }
check-type { dup null eq { 3 -1 roll pop pop true }{ 3 -1 roll type eq } ifelse }
make-type-name { dup type /nametype eq { dup length 4 add string dup dup 4 2 roll cvs
2 copy 0 exch putinterval length (type) putinterval cvn } if }
args { [ exch 2 { 0 get } fortuple ] cvx }
types { [ exch 2 { 1 get } fortuple ] }
map { 1 index xcheck 3 1 roll [ 3 1 roll forall ] exch {cvx} if }
reduce { exch dup first exch rest 3 -1 roll forall }
rreduce { exch aload length 1 sub dup 3 add -1 roll repeat }
curry { [ 3 1 roll {} forall ] cvx } @pop
{ dup xcheck 3 1 roll
dup length 1 add array dup 0 5 -1 roll put dup 1 4 -1 roll putinterval
exch {cvx}if }
compose { 2 array astore cvx { {} forall } map } @pop
{ 1 index xcheck 3 1 roll
1 index length 1 index length add array dup 0 4 index putinterval
dup 4 -1 roll length 4 -1 roll putinterval
exch {cvx} if }
reverse {
dup xcheck exch
[ exch dup length 1 sub -1 0 { 2 copy get 3 1 roll pop } for pop ]
exch {cvx} if }
} pairs-def {
fortokens {src proc}{ { src token {exch /src exch store}{exit}ifelse proc } loop } @func
fortuple {a n p}{ 0 n /a load length 1 sub
{ /a exch /n getinterval /p exec } {load-if-literal-name} map end for
} @func-begin
load-if-literal-name { dup type /nametype eq 1 index xcheck not and { load } if }
} pairs-def
****** pc8.ps *******
(struct2.ps) run {
filename-input { (r) file file-input }
file-input {f} { [ f read { [ f /file-input cvx ] cvx }
{ -255 -255 rep } ifelse ] } @func
string-input {s} { [ s {first}stopped{ pop pop -255 -255 rep }
{ [ s rest /string-input cvx ] cvx } ifelse ] } @func
succeed {v} { { [ exch [ exch /v exch ] ] } ll } @func
fail { { pop [] } }
satisfy {pred}{ { dup first /pred exec { xs-x succeed }{ fail } ifelse exec } ll } @func
literal {x} { { /x eq } ll satisfy } @func
range {a b} { { dup /a ge exch /b le and } ll satisfy } @func
cclass {cc} { { charstring /cc exch search { pop pop pop true }
{ pop false } ifelse } ll satisfy } @func
return {p v} { { /p exec dup length 0 gt { xs-x pop /v exch cons } if } ll } @func
using {p f} { { /p exec [ exch { dup 0 2 copy get /f exec put } forall ] } ll } @func
alt2 {p q} { { dup /p exec [ 3 1 roll spill counttomark -1 roll /q exec spill ] } ll } @func
seq2 {p q} { { /p exec { x-xs /q exec exch {
exch x-xs 3 1 roll cons exch cons
} curry forall } map } ll } @func
into {p v q} { { /p exec { xs-x << exch /v exch >> begin /q exec aload pop end } map } ll } @func
then seq2 thenx { then {first} using }
xthen { then {second} using }
? maybe maybe { [] succeed alt2 }
+ some some { dup many seq2 }
* many many { % p* = p p* | eps
{{-777 exec}exec}{}deep-map dup 0 get 3 1 roll seq2 maybe % {{}e} {p then {{{}e}e}}
2 copy 0 exch put exch pop executeonly }
many- { many trim }
trim {p} { { /p exec dup length 0 gt { 0 1 getinterval } if } ll } @func
white { ( \t\n) anyof many }
nibble { white exch xthen }
dot { {0 ge} satisfy }
any { map {alt2} reduce }
anyof { {literal} any }
noneof { anyof none }
none {p} { { dup /p exec dup length 0 gt { pop fail }
{ pop dot } ifelse exec } ll } @func
y { 1 index } aa { array astore }
ps { (stack:)= pstack } pc { ps clear }
second { 1 get } spill { {} forall } cons { 2 aa }
/@ { { dup xcheck { exec }{ exit } ifelse } loop }
next { dup second xcheck { dup 1 2 copy get @ put } if second }
x-xs { dup first exch next }
xs-x { dup next exch first }
ll { {load-if-literal-name} deep-map }
deep-map { y type /arraytype ne { exec }{
y xcheck 3 1 roll [ 3 1 roll /deep-map cvx 2 array astore cvx forall ] exch {cvx} if } ifelse }
rep {x} { { [ /x { /x rep } ] } ll } @func
add1 {x} { x @ dup length 0 eq { [] }
{ [ exch dup first 1 add exch next /add1 cvx 2 aa cvx ] } ifelse } @func
take {x n} { n 0 eq { [] }{ [ x @ x-xs n 1 sub take ] } ifelse } @func
charstring { 1 string dup 0 4 3 roll put }
flatten { [ exch { dup type /arraytype eq {{}forall}if}dup 4 get 0 2 index put exec ] }
to-string { flatten dup length dup string exch 1 sub -1 0 { % A S i
3 copy 3 copy exch pop get put pop pop
} for exch pop }
} pairs-begin
****** pc8re.ps *******
(pc8.ps)run
<<
/lit { first literal }
/px {dup print cvx exec}
>> begin
/Dot (.) lit def
/Meta (*+?) anyof def
(*) first { many } def
(+) first { some } def
(?) first { maybe } def
/Character (*+?.|()) noneof def
/Expression { -777 exec } def
/Atom //Dot {pop dot}using
%(\() lit //Expression seq2 (\)) lit seq2 alt2
//Character {literal}using alt2 def
/Factor //Atom /atom
//Meta { /atom load exch load exec dup /atom exch store }using
maybe { dup xcheck{}{pop /atom load}ifelse }using into def
/Term //Factor /left-factor
//Factor { /left-factor load exch seq2 dup /left-factor exch store }using
many { dup xcheck{}{pop /left-factor load}ifelse }using into %{1 aa}using
def
//Expression 0
//Term %/left-term
(|) lit //Term then %{ (A)= /left-term load exch alt2 dup /left-term exch store }using
many %{ (T)= ps dup xcheck{}{pop /left-term load (L)= ps}ifelse }using
thenx %into
put
%/atom 0 def
%/left-factor 0 def
%/left-term 0 def
/regex { string-input //Expression exec dup length 0 gt {
first first trim {to-string}using
}{} ifelse } def
/r (a+b?) regex def
%r == %[[[97 []] [[97 []] [[98 []] [[46 63] []]]]] []]
(/r load == ) px
(aa) string-input r pc
(aaab) string-input r pc
(abab) string-input r pc
quit
$ gsnd -q -dNOSAFER pc8re.ps
/r load == {{{{{{dup first {97 eq} exec {xs-x succeed} {fail} ifelse exec} exec {x-xs -array- exec exch {exch x-xs 3 1 roll cons exch cons} curry forall} map} exec {x-xs {dup {dup first {98 eq} exec {xs-x succeed} {fail} ifelse exec} exec [ 3 1 roll spill counttomark -1 roll {[ exch [ exch [] exch ] ]} exec spill ]} exec exch {exch x-xs 3 1 roll cons exch cons} curry forall} map} exec {x-xs {dup {dup first {98 eq} exec {xs-x succeed} {fail} ifelse exec} exec [ 3 1 roll spill counttomark -1 roll {[ exch [ exch [] exch ] ]} exec spill ]} exec exch {exch x-xs 3 1 roll cons exch cons} curry forall} map} exec dup length 0 gt {0 1 getinterval} if} exec [ exch {dup 0 2 copy get {to-string} exec put} forall ]}
stack:
[[(aa) [-255 {[ -255 {-255 rep} ]}]]]
stack:
[[(aaab) [-255 {[ -255 {-255 rep} ]}]]]
stack:
[[(ab) [97 {(b) string-input}]]]
[toc] | [prev] | [standalone]
Back to top | Article view | comp.lang.postscript
csiph-web