Counts the number of elements on the operand stack from the top down to (but not including) the topmost mark object.
1. Description
The counttomark operator counts how many elements are on the operand stack between the top and the nearest mark object (searching from top to bottom). The mark itself is not counted, and the stack is left unchanged except for the count value being pushed.
This operator is essential for implementing variable-argument procedures where the number of arguments is not known in advance. It’s commonly used with mark to process all elements in a marked segment.
This is a Level 1 operator, available in all PostScript implementations.
2. Syntax
mark obj1 ... objn counttomark mark obj1 ... objn n
2.1. Stack Effect
| Position | Content |
|---|---|
Top |
|
… |
|
Bottom (of segment) |
|
| Position | Content |
|---|---|
Top |
|
Top-1 |
|
… |
|
Bottom (of segment) |
|
3. Parameters
Requires at least one mark object on the stack. The operator counts all objects above the topmost mark.
obj1 … objn-
Any objects (of any type other than marks) between the top of the stack and the nearest mark.
mark-
A mark object (type
marktype) that delimits the counting boundary.
4. Return Values
Returns a non-negative integer representing the number of objects between the top of the stack and the nearest mark (exclusive of the mark itself).
5. Examples
5.1. Basic Usage
% Count elements after mark
mark 1 2 3 counttomark
% Stack: mark 1 2 3 3
% Empty marked segment
mark counttomark
% Stack: mark 0
% Multiple elements
1 mark 2 3 counttomark
% Stack: 1 mark 2 3 2
5.2. Variable Argument Functions
% Sum all arguments after mark
/sumAll { % mark n1 n2 ... nk -> sum
0 % Initialize sum
counttomark { % Loop count times
exch add
} repeat
exch pop % Remove mark
} def
% Usage
mark 1 2 3 4 5 sumAll % Stack: 15
mark 10 20 sumAll % Stack: 30
6. Advanced Examples
6.1. Collecting Elements Conditionally
% Collect only positive numbers
/collectPositive { % mark n1 n2 ... nk -> array
counttomark % Get count
/temp exch array def
0 % Index counter
{
counttomark 0 eq { exit } if
dup 0 gt {
temp 3 1 roll put
1 add
} {
pop
} ifelse
} loop
temp 0 3 -1 roll getinterval
exch pop % Remove mark
} def
mark -5 3 -2 7 1 -8 4 collectPositive
% Stack: [3 7 1 4]
6.2. Building Nested Structures
% Create nested array from multiple marks
/nestedArray { % mark1 ... markn elem... -> nested_array
counttomark array astore
{
% Check if contains mark
dup 0 get type /marktype eq {
% Process nested level
1 1 index length 1 sub getinterval
nestedArray
} if
} forall
} def
6.3. Safe Argument Processing
% Process exactly n arguments after mark
/processN { % mark obj1 ... objk n proc -> result
exch % mark obj1 ... objk proc n
counttomark % mark obj1 ... objk proc n count
1 index ne {
pop pop
(Error: argument count mismatch) print
cleartomark
} {
pop % Remove n
counttomark {
2 copy exec
} repeat
pop % Remove proc
exch pop % Remove mark
} ifelse
} def
6.4. Variable-Length Dictionary Builder
[source,postscript>
% Build dictionary from key-value pairs
/makeDict { % mark /key1 val1 ... /keyn valn -> dict
counttomark 2 idiv % Count key-value pairs
dup dict begin
{
def
} repeat
currentdict end
exch pop % Remove mark
} def
mark
/name (PostScript)
/level 3
/year 1999
makeDict
7. Edge Cases and Common Pitfalls
If no mark exists on the stack, counttomark causes an unmatchedmark error.
|
7.1. No Mark on Stack
% BAD: No mark to count to
clear
1 2 3
counttomark % ERROR: unmatchedmark
% GOOD: Always ensure mark exists
mark 1 2 3
counttomark % OK, returns 3
7.2. Only Counts to First Mark
% CAUTION: Only counts to nearest mark
mark 1 2 mark 3 4 5
counttomark
% Stack: mark 1 2 mark 3 4 5 3
% Only counted 3 4 5 (after second mark)
cleartomark % Remove inner segment
counttomark
% Stack: mark 1 2 2
% Now counts 1 2 (after first mark)
7.3. Count Adds to Stack
% Remember: counttomark adds count to stack
mark 1 2 3
counttomark % Stack: mark 1 2 3 3
count % Stack: mark 1 2 3 3 5
% Stack now has 5 elements (including mark and count)
Use counttomark immediately before the operation that needs the count. Don’t store the count for later use if the stack might change.
|
8. Related Commands
-
mark- Push a mark object onto stack -
cleartomark- Remove elements down to and including mark -
count- Count total stack depth -
]- Create array from marked elements -
pop- Remove single element
9. PostScript Level
Available in: PostScript Level 1 and higher
This is a fundamental operator available in all PostScript implementations.
10. Error Conditions
unmatchedmark-
No mark object is found on the operand stack when searching from top to bottom.
clear 1 2 3 counttomark % ERROR: unmatchedmark stackoverflow-
The stack is at maximum capacity and cannot accommodate the count value. This is extremely rare in practice.
% (Only possible if stack nearly full)
11. Performance Considerations
The counttomark operator has O(n) time complexity where n is the number of elements between the top of the stack and the mark. The operator must scan the stack to find the mark and count elements.
For very deep marks, this can be slightly expensive, but in practice marked segments are usually small and performance is not a concern.
12. Best Practices
-
Use with mark: Always ensure a
markexists before callingcounttomark -
Immediate use: Use the count value immediately; don’t store it for later
-
Variable arguments: Ideal for implementing procedures with variable-length argument lists
-
Document expectations: Clearly document when procedures expect marked arguments
-
Verify count: For robust code, verify the count matches expectations before processing
12.1. Variable Argument Pattern
% Standard pattern for variable arguments
/varArgProc { % mark arg1 ... argn -> result
% Get count
counttomark
% Process that many arguments
{
% Process each argument
% ... operation ...
} repeat
% Clean up mark
exch pop
} def
12.2. Defensive Counting
% Verify argument count
/strictArgProc { % mark arg1 ... argn expected -> result
counttomark
2 copy ne {
(Error: expected ) print dup =
(got ) print =
cleartomark
} {
pop % Remove expected
% Process arguments
% ...
exch pop % Remove mark
} ifelse
} def
12.3. Safe Mark Handling
% Check for mark before counting
/safeCountToMark { % ... -> ... n (or 0 if no mark)
false % Found flag
count 1 sub 0 1 3 -1 roll {
index type /marktype eq {
% Found mark, count elements
pop
0 exch 0 1 3 -1 roll {
pop 1 add
} for
true exit
} if
} for
not {
0 % No mark found, return 0
} if
} def
13. See Also
-
Operators Overview - Understanding PostScript operators
-
Stack Operations Guide - Stack manipulation tutorial
-
Procedures - Implementing variable-argument functions
-
Stack Manipulation - All stack operators