Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.postscript > #3345 > unrolled thread

Parsing yet again all over again

Started byM Joshua Ryan <luser.droog@gmail.com>
First post2019-03-16 17:00 -0500
Last post2019-03-20 13:20 -0700
Articles 3 — 2 participants

Back to article view | Back to comp.lang.postscript


Contents

  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

#3345 — Parsing yet again all over again

FromM Joshua Ryan <luser.droog@gmail.com>
Date2019-03-16 17:00 -0500
SubjectParsing 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]


#3355

Fromluser droog <luser.droog@gmail.com>
Date2019-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]


#3356

Fromluser droog <luser.droog@gmail.com>
Date2019-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