• interpolating variables into a string

    From luser droog@21:1/5 to All on Tue Apr 4 21:08:29 2023
    A different approach to the sprintf chimera. Here the % is followed
    by a name which is loaded, converted, and spliced into the
    format string at that point. The name is read out of the string using
    the `token` operator so it can be predicted to consume exactly
    one whitespace character following the name unless the name is
    delimited by a character from PostScript's delimiter set, ie. []()<>/%

    This feels like a better direction, despite/because of not consuming
    from the stack where you need a mark on the stack and juggling or
    an array of arguments and extra syntax to call it.

    /interpolate { % format-string interpolate result-string
    [ exch
    (%) { % post match
    exch token not {exit} if exch % match tok rem
    3 1 roll % rem match tok
    [ exch load convert % rem match [ tok*
    } on-matches
    join-to-mark
    } def

    /on-matches { % string seek proc post match proc post' match [ tok*
    1 dict begin /proc exch def
    ({search not {exit} if % post match pre
    3 1 roll % pre post match
    //proc exec % pre rem match [ tok*
    counttomark dup 3 add exch roll pop % pre tok* rem match
    }) cvx exec end loop
    } def

    /join-to-mark {
    ]
    0 1 index { length add } forall
    1 index 0 get type /arraytype eq {array}{string} ifelse % src dest
    exch % dest src
    0 exch % dest 0 src
    { 3 copy putinterval length add } forall
    pop
    } def

    /convert {
    256 string cvs
    } def

    /a 4 def
    /b 12 def

    (interp%a olate%b) interpolate
    pstack


    $ gsnd interpolate.ps
    GPL Ghostscript 9.55.0 (2021-09-27)
    Copyright (C) 2021 Artifex Software, Inc. All rights reserved.
    This software is supplied under the GNU AGPLv3 and comes with NO WARRANTY:
    see the file COPYING for details.
    (interp4olate12)
    GS<1>

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From luser droog@21:1/5 to luser droog on Sun Apr 9 22:42:42 2023
    On Tuesday, April 4, 2023 at 11:08:31 PM UTC-5, luser droog wrote:
    A different approach to the sprintf chimera. Here the % is followed
    by a name which is loaded, converted, and spliced into the
    format string at that point. The name is read out of the string using
    the `token` operator so it can be predicted to consume exactly
    one whitespace character following the name unless the name is
    delimited by a character from PostScript's delimiter set, ie. []()<>/%

    This feels like a better direction, despite/because of not consuming
    from the stack where you need a mark on the stack and juggling or
    an array of arguments and extra syntax to call it.


    A little sup'ed up, incorporating the ideas elaborated in the sprintf
    thread. `on-matches` isn't the greatest, as far as reusable control
    structures are concerned. It was designed in close promiscuity with
    the function that needed it. But I also came up with a different way
    to write it that doesn't abuse the scanner and still seems readable.

    Most of the tricks using `counttomark` have been factored out into
    named procedures after everything was working. There does seem
    to be some value in isolating and naming them.



    /interpolate { % format-string interpolate result-string
    [ exch
    (%) { % post match
    exch token not {exit} if % match rem tok
    exch 3 1 roll % rem match tok
    [ exch load convert % rem match [ tok*
    } on-matches
    join-to-mark
    } def

    /on-matches-v1 { % string seek proc post match proc post' match [ tok*
    1 dict begin /proc exch def
    ({ % string seek
    search not {exit} if 3 1 roll % pre post match
    //proc exec % pre rem match [ tok*
    2 put-below % pre tok* rem match
    }) cvx exec end loop
    } def

    /on-matches { % string seek proc post match proc post' match [ tok*
    [ exch
    { search not {exit} if 3 1 roll } exch % in loop: % pre post match
    % -- call proc -- % % pre rem match [ tok*
    { 2 put-below } % % pre tok* rem match
    join-to-mark loop
    } def

    /join-to-mark { % [ <obj1> .. <objN> join-to-mark <obj1..objN>
    counttomark dup 1 add copy % [ <obj1> .. <objN> n <obj1> .. <objN> n
    0 exch { % ... <obj1> .. <objn> 0
    exch length add
    } repeat % [ <obj1> .. <objN> n length
    first-after-mark type /stringtype eq
    {string}{array} ifelse % [ <obj1> .. <objN> n dest
    first-after-mark xcheck {cvx} if
    exch 0 exch { % [ <obj1> .. <objN> dest pos
    snag-first % [ <obj2> .. <objN> dest pos <obj1>
    3 copy putinterval % [ <obj2> .. <objN> dest' pos <obj1>
    length add % [ <obj2> .. <objN> dest' pos'
    } repeat % [ dest length
    pop exch pop % dest
    } def

    /put-below { % obj1 .. objN [ obj'1 .. obj'M N put-below obj'1 .. obj'M obj1 .. objN
    counttomark % ...(n) [ ...(m) n m+1
    exch 1 index add exch 1 sub % ...(n) [ ...(m) n+m+1 m
    roll pop % ...(m) ...(n)
    } def

    /first-after-mark {
    counttomark 1 sub index
    } def

    /snag-first {
    counttomark -1 roll
    } def

    /curry {
    dup length 1 add array 1 index xcheck {cvx} if % obj arr dest
    dup 0 5 -1 roll put % arr dest
    dup 1 4 -1 roll putinterval
    } def

    /base (10) cvx def
    /convert <<
    /default { 256 string cvs }
    /stringtype { }
    /nametype { dup xcheck not { (/) exch } if
    dup length string cvs }
    /booleantype { {(true)}{(false)} ifelse }
    /marktype { pop (MARK) }
    /nulltype { pop (-) }
    /savetype { pop (-save-) }
    /filetype { pop (-file-) }
    /fonttype { pop (-font-) }
    /gstatetype { pop (-gstate-) }
    /integertype { base 10 eq { 256 string cvs }{
    /base load cvlit exch (#) exch base 256 string cvrs
    } ifelse }
    /arraytype { dup xcheck {
    dup length 0 eq { pop ({}) }{
    ({) exch { convert ( ) }forall pop (})
    } ifelse
    }{
    dup length 0 eq { pop ([]) }{
    ([) exch { convert ( ) }forall pop (])
    } ifelse
    } ifelse }
    /dicttype {
    dup length 0 eq { pop (<<>>) }{
    (<<) exch { % key val
    exch [ exch convert % val [ (key)*
    1 put-below % key* val
    ( ) exch
    convert ( )
    } forall pop (>>)
    } ifelse
    }
    {
    1 index type 2 copy known not { pop /default } if get exec
    } curry def


    /a 4 def
    /b 12 def
    /c {a b} def
    /d [a b] def
    /e <</a a /b b>> def

    (format %a %b %c %d %e) interpolate
    pstack

    $ gsnd interpolate.ps
    GPL Ghostscript 9.55.0 (2021-09-27)
    Copyright (C) 2021 Artifex Software, Inc. All rights reserved.
    This software is supplied under the GNU AGPLv3 and comes with NO WARRANTY:
    see the file COPYING for details.
    (format 4 12 {a b} [4 12] <</a 4 /b 12>>)
    GS<1>

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)