Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

interpolating variables into a string

24 views
Skip to first unread message

luser droog

unread,
Apr 5, 2023, 12:08:31 AM4/5/23
to
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>

luser droog

unread,
Apr 10, 2023, 1:42:44 AM4/10/23
to
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>

0 new messages