Skip to content

Commit f40dade

Browse files
committed
Update CLAUDE.md
1 parent 64bf31e commit f40dade

1 file changed

Lines changed: 95 additions & 30 deletions

File tree

CLAUDE.md

Lines changed: 95 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,11 @@ Performance, execution cost, and interpreter compatibility are critical.
4343

4444
### Commands
4545

46-
- `make -j $(nproc)` - Build all distribution targets (resource, packaged_resource, monolithic, monolithic_package)
47-
- `make -j $(nproc) test` - Run all tests; should be ran before declaring a code change complete
48-
- `make clean` - Clean
49-
- `make build/standalone/<encoder>.ps` - Build standalone encoder
46+
- `make -j $(nproc)` - Build all distribution targets (resource, packaged_resource, monolithic, monolithic_package)
47+
- `make -j $(nproc) test` - Run all tests; should be ran before declaring a code change complete
48+
- `make clean` - Clean
49+
- `make build/standalone/<encoder>.ps` - Build standalone encoder
50+
- `make build/standalone_package/<encoder>.ps` - Build packaged standalone encoder
5051

5152
Note: On the default MacOS execution environment for AI it's `sysctl -n hw.ncpu` instead of `$(nproc)`.
5253

@@ -100,24 +101,28 @@ that affect the behaviour of BWIPP:
100101

101102
Core library:
102103

103-
- `src/*.ps.src` - PostScript resource source files
104-
- `src/uk.co.terryburton.bwipp.upr` - Resource name to path mapping for all resources; required by Distiller; build system uses the order to determine resource order in monolithic and standalone outputs
105-
- `tests/ps_tests/*.ps.test` - PostScript test files
104+
- `src/*.ps.src` - PostScript resource source files
105+
- `src/uk.co.terryburton.bwipp.upr` – PostScript resource index mapping resource names to file paths; required by Distiller and used by the build system to enforce resource order in monolithic and standalone outputs
106+
- `tests/ps_tests/*.ps.test` - PostScript test files
106107

107108
Build scripts:
108109

109-
- `build/make_resource.pl` - Resource builder (invokes GhostScript)
110-
- `build/make_deps.pl` - Dependency rule generator (creates .d files)
111-
- `build/make_monolithic.pl` - Monolithic resource assembler
112-
- `build/make_standalone.pl` - Standalone file assembler (supports multiple encoders)
110+
- `build/make_resource.pl` - Resource builder (invokes GhostScript)
111+
- `build/make_deps.pl` - Dependency rule generator (creates .d files)
112+
- `build/make_monolithic.pl` - Monolithic resource assembler
113+
- `build/make_standalone.pl` - Standalone file assembler (supports multiple encoders)
113114

114-
Build outputs:
115+
Automatic build outputs:
115116

116-
- `build/resource/` - `make resource`: Unpackaged resources
117-
- `build/packaged_resource/` - `make packaged_resource`: Packaged resources
118-
- `build/monolithic/barcode.ps` - `make monolithic`: All encoders combined
119-
- `build/monolithic_package/barcode.ps` - `make monolithic_package`: Packaged monolithic
120-
- `build/standalone/<encoder>.ps` - Standalone encoder (manual, only required deps built)
117+
- `build/resource/` - `make resource`: Unpackaged resources
118+
- `build/packaged_resource/` - `make packaged_resource`: Packaged resources
119+
- `build/monolithic/barcode.ps` - `make monolithic`: All encoders combined
120+
- `build/monolithic_package/barcode.ps` - `make monolithic_package`: Packaged monolithic
121+
122+
On-demand build outputs:
123+
124+
- `build/standalone/<encoder>.ps` - Standalone encoder containing all required resource dependencies
125+
- `build/standalone_package/<encoder>.ps` - As above, but packaged
121126

122127

123128
## Code structure
@@ -325,17 +330,22 @@ bind def
325330
```
326331

327332

328-
### Resource calling pattern
333+
### Resource calling pattern and error handling
334+
335+
Upon encountering an error, a resource shall clean up **only** its own stack
336+
entries and then call the `raiseerror` procedure. This halts execution and
337+
results in either a custom error handler or a user-provided `stopped` context
338+
being run.
329339

330-
Resources should call one another with a clean stack, otherwise the stack may
331-
contain junk upon `raiseerror` from the called resource. (`raiseerror` only
332-
attempts to clean up the stack use by own resource.)
340+
To avoid orphaning stack entries when a resource does not run to completion — such
341+
as when another resource invokes `raiseerror` — resources must execute other
342+
resources only with a clean stack.
333343

334344
Any example is "wrapper encoders" that delegate to another encoder with
335345
modified options:
336346

337347
```postscript
338-
% Good: /args only pushed after inner encoder succeeds
348+
% Good: /args is only pushed after inner encoder succeeds
339349
barcode options //innerencoder exec /args exch def
340350
```
341351

@@ -375,8 +385,8 @@ GS1 AI syntax is first processed by `gs1process.ps.src` before regular parsing
375385

376386
### Error Handling (raiseerror.ps.src)
377387

378-
- Errors are raised by popping stack items added by current resource, pushing a user-friendly info string and error name, then calling raiseerror
379-
- Uses standard PostScript `stop` mechanism for custom error handlers
388+
- Errors are raised by popping stack items added by current resource, pushing a user-friendly info string and error name, then calling `raiseerror`
389+
- Uses standard PostScript `stop` mechanism to invoke custom error handlers or a `stopped` context
380390
- Error names typically follow pattern: `/bwipp.<resource><ErrorType>`, e.g. `/bwipp.code39badCharacter`
381391

382392

@@ -425,34 +435,88 @@ Encoders create a common dictionary structure expected by their renderer:
425435
- Basic stack ops (pop, index, roll) have similar cost to dict ops
426436
- Consecutive stack operations have small marginal cost after initial op
427437

438+
428439
### Optimization Patterns
429440

430441
- Use `//name` immediate lookup for static data (avoids runtime structure allocation cost)
431442
- Mark static arrays/dicts `readonly` (prevents accidental modification)
432443
- Defer expensive computation to lazy init (first-run cost, cached thereafter)
433444
- Prefer stack manipulation over creating intermediate dictionaries
434445

446+
**Conditional Assignment Pattern**
447+
448+
Use an inline condition when performing simple conditional assignments:
449+
450+
```postscript
451+
% Bad: Verbose
452+
condition {
453+
/a 2 def
454+
} {
455+
/a 5 def
456+
} ifelse
457+
458+
% Good: Concise
459+
/a condition { 2 } { 5 } ifelse def
460+
```
461+
462+
463+
**"Switch" blocks (short circuit)**
464+
465+
Long `ifelse` chains should be avoided by using a "common exit" pattern,
466+
clearly denoted by a comment on the first line:
467+
468+
```postscript
469+
% Bad: Hard to modify
470+
condition1 {
471+
/c1
472+
} {
473+
condition2 {
474+
/c2
475+
} ... {
476+
/default
477+
} ifelse ... } ifelse
478+
479+
% Good: Clarity
480+
1 { % Common exit
481+
condition1 { /c1 exit } if
482+
condition2 { /c2 exit } if
483+
...
484+
/default
485+
} repeat
486+
/result exch def
487+
```
488+
489+
**Loops with conditional exit**
490+
491+
Loops should be commented as such in the first line:
492+
493+
{ % loop
494+
condition { exit } if
495+
...
496+
} loop
497+
498+
435499
### Anti-patterns
436500

437501
- Creating variables (dictionary entries) in hot loops
438502
- Defining static data in the main procedure (hoist to define time, then use `//name`)
439503
- Computing derived data on every invocation (use latevars)
440504

441-
### Hot Loop Stack Pattern
505+
**Hot Loop Stack Pattern**
442506

443507
In high computational complexity loops, avoid `/idx exch def`. Keep loop index
444508
on stack, reference with `index` and finally consume with `roll` (rather than
445509
`pop`):
446510

447511
```postscript
448512
% Bad: Creates dictionary entry each iteration
449-
0 1 k { % E.g. k from outer loop
513+
0 1 k { % E.g. k from an outer loop
450514
/idx exch def
451515
% Stuff using "idx" variable...
452516
arr k idx sub 1 sub get
453517
} for
454518
455-
% Good: Loop index consumed by roll
519+
% Good: Iterator referenced by "index" and finally consumed by "roll"
456520
0 1 k 1 sub { % idx on stack
457521
% Stuff using "N index" to access idx on the stack...
458522
% Finally, roll moves idx to top where we consume it:
@@ -463,6 +527,7 @@ on stack, reference with `index` and finally consume with `roll` (rather than
463527
The RSEC loops in qrcode, datamatrix, pdf417 demonstrate advanced uses of this
464528
pattern, including stack-based access to variables outside of the inner loop.
465529

530+
466531
### Profiling
467532

468533
Simple profiling of the overall runtime for some encoder:
@@ -750,9 +815,9 @@ Some PLRM terminology is a source of confusion. As a result of the following com
750815
/b a def
751816
```
752817

753-
- a is referred to as the "object" (within the currentdict)
754-
- The --array-- created by "]" is referred to as "the storage for the object in VM" (either global or local VM depending on globalstatus)
755-
- b is also an "object" that refers to the same VM storage as a
818+
- `a` is referred to as the "object" (within the currentdict)
819+
- The `--array--` created by `]` is referred to as "the storage for the object in VM" (either global or local VM depending on the allocation mode indicated by `globalstatus`)
820+
- `b` is also an "object" that refers to the same VM storage as `a`
756821

757822
The terminology differs from many languages where the array itself would be referred to as an object and a and b would be referred to as names or references.
758823

0 commit comments

Comments
 (0)