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


Groups > comp.lang.postscript > #3842

formatted string output

Newsgroups comp.lang.postscript
Date 2023-05-24 09:19 -0700
Message-ID <eaea87c8-b7b9-4d5a-86ff-e38fe0850d92n@googlegroups.com> (permalink)
Subject formatted string output
From luser droog <luser.droog@gmail.com>

Show all headers | View raw


With a little tweak, the string interpolation function becomes a
pretty close analog of sprintf. The function 'format' takes a 
format string and yields an output string. A percent sign in
the format indicates the start of an interpolated value. The next
token following the percent sign is scanned from the format 
string (consuming exactly one terminating whitespace character 
unless the token is delimited by one of the PostScript 
delimiters []()<>/%). If the token is a /nametype object, it is loaded
and converted into a string and spliced into the output at that 
point. If the token is an /integertype object, it is used to index 
up the stack past the location of the format string in the original
stack picture as it looked when 'format' was called. So %1 refers
to the first thing on the stack just below the format string. But only
the format string is popped by the format function and these other
objects referenced by stack position are not removed.

12 (%1) format  % 12 (12)

/a 4 def
(%a) format  % (4)
(%a ) format  % (4)   %% if there's a space, it's gon' get eaten
(%a  ) format  % (4 )

So this gives you the power of a sprintf to access a variable number
of arguments -- without needing to create an array to hold them or to
bracket the arguments with a bare mark. It might be useful to write
a variant that pops extra objects up to the highest integer reference.

(There's also a few versions of an 'rforall' loop that goes backwards
through a string or array because it happened in the same file.)


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

/fetch {
    dup type /nametype eq {
	load
    }{
	dup type /integertype eq {
	    index-past-mark
        }{
	    unrecognized-type-in-format-string
        } ifelse
    } ifelse
} def

% string seek proc
% where:        post match  proc  post' match [ tok*
/on-matches-v1 {
    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

% string seek proc
% where:         post match  proc  post' match [ tok*
/on-matches {
    [ 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

/curry { % obj {...}  curry  {obj ...}
    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                     % dest
} 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> len=0
	exch length add         % ... <obj1> .. <objn-1> len+=#objn
    } 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 {               % [ <obji> .. <objN> dest pos
	snag-first              % [ <obji+1> .. <objN> dest pos <obji>
	3 copy putinterval      % [ <obji+1> .. <objN> dest' pos <obji>
	length add              % [ <obji+1> .. <objN> dest' pos+=#obji
    } 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

/index-past-mark { % 3 2 1 [ ...(n) 2 
    counttomark    % 3 2 1 [ ...(n) 2 n+1
    1 sub          % 3 2 1 [ ...(n) 2 n
    add            % 3 2 1 [ ...(n) 2+n
    index
} 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 }
  /packedarraytype 1 index
  /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 switch
} curry def

/switch {
    2 copy known not { pop /default } if get exec
} def

/rforall-v1 { % array/string proc  rforall
    1 dict begin /proc exch def /src exch def
    /src load length 1 sub -1 0 ({
        //src exch get
        //proc exec
    }) cvx exec end for
} def

/rforall-v2 {
    1 index length 1 sub -1 0
    [ 6 4 roll 
      exch {exch get} curry exch
    join-to-mark for
} def

/rforall-v3 { % src proc  rforall
    1 index length 1 sub -1 0 5 3 roll  % $ -1 0 src proc
    {exch get} exch join curry for   % $ -1 0 { //src exch get proc... } for
} def

/rforall { % src proc  rforall  -
    dup length 3 add array cvx  % src proc loop
    dup 0 4 index put  % src proc {src }
    dup 1 { exch get } putinterval  % src proc {src exch get }
    dup 3 4 -1 roll putinterval % src {src exch get proc...}
    exch length 1 sub -1 0  4 3 roll  % $ -1 0 {src exch get proc...}
    for
} def

/join {
    [ 3 1 roll join-to-mark
} def
    
/args {
    {exch def} rforall
} def
    
%{a b c d e}{=} rforall

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

(x) 42 (format %a  %b  %c  %d  %e %1 %2) format
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>>42x)
42
(x)
GS<3>

Back to comp.lang.postscript | Previous | Next | Find similar


Thread

formatted string output luser droog <luser.droog@gmail.com> - 2023-05-24 09:19 -0700

csiph-web