Una Chiave per il Desktop Publishing
Corso di PostScript
- Marco A. Calamari
Seconda Puntata
La struttura del linguaggio
Come promesso, in questa seconda parte illustreremo il
funzionamento del programma realizzato nella prima puntata. Prima
di ciò, è però necessario fare parentesi (non breve) per
introdurre le caratteristiche principali e la struttura del linguaggio
PostScript; questo permetterà a chi ha familiarità con altri
linguaggi simili di utilizzare le conoscenze che possiede, e
sarà una fatica necessaria ma redditizia, per chi non ha molta
esperienza di programmazione.
Il linguaggio PostScript possiede le seguenti principali caratteristiche:
I programmi PostScript
Un programma PostScript è una sequenza di caratteri ASCII
stampabili, e può contenere, oltre ai caratteri di controllo
elencati la volta scorsa, i caratteri alfabetici maiuscoli e
minuscoli, le cifre e i seguenti caratteri speciali
/ (slash), % (percento), ( (parentesi tonda aperta), ) (parentesi tonda chiusa), [ (parentesi quadra aperta) ] (parentesi quadra chiusa) { (parentesi graffa aperta) } (parentesi graffa chiusa) < (parentesi angolata aperta) > (parentesi angolata chiusa) \ (backslash)
Questi ultimi sono caratteri riservati, nel senso che non
possono essere usati per formare nomi; tutti gli altri caratteri speciali
stampabili sono in realtà utilizzabili nei nomi, ma, per rendere
il programma più leggibile (e in PostScript è vitale scrivere
programmi leggibili !) vi consigliamo di limitare l'utilizzo di
questi caratteri ai soli - (meno) e _
(sottolineatura). Sempre per migliorare la leggibilità, anticipiamo
qui una serie di convenzioni universalmente utilizzate in
PostScript:
- I nomi devono essere formati solo da caratteri alfabetici e numerici, e, dato che l'interprete considera diverse le lettere maiuscole e minuscole, particolare cura dovrà essere posta nell'evitare di scrivere cose come Nome, NOME e nome, che sono considerate tre oggetti diversi.
I nomi di variabili devono sempre iniziare con una lettera maiuscola, e quelli di procedure con lettera minuscola.
I nomi composti di più parole saranno formati scrivendo
maiuscole le lettere iniziali di ogni parola oltre la prima; ad esempio
nomeMoltoLungoDiProcedura
NomeMoltoLungoDiVariabile.
In PostScript non è esatto parlare di programma nel senso
classico del termine, poiché in questo linguaggio non vi è una separazione
netta fra programma e dati; è più semplice pensare che, quando sottoponiamo
un file di comandi all'interprete PostScript, esso lo consumi
carattere per carattere, producendo oggetti e azioni all'interno
della memoria della LaserWriter. Quando l'interprete PostScript
riceve i caratteri, li immagazzina in un buffer; all'arrivo del
primo carattere separatore (spazio, return, ecc.) lo scarta,
considera terminata la stringa e la interpreta, eseguendo il
comando PostScript corrispondente, od operando su di essa
mediante le regole predefinite e trasformandola in un oggetto PostScript.
Questa interpretazione immediata implica che non esiste nessuna precedenza
fra gli operatori del PostScript, a differenza di quello che
accade nella maggior parte degli altri linguaggi.
Gli oggetti PostScript
I tipi di dati del PostScript, che d'ora in poi indicheremo
con il nome di oggetti, sono 13, e possono essere raggruppati come
segue:
Oggetti numerici
- numeri interi
- numeri reali
- valori logici
Oggetti nominali
- nomi
- operatori
- file
Oggetti speciali
- mark
- null
- fontID
- save
Oggetti composti
- vettori
- stringhe di caratteri
- dizionari
Tutti gli oggetti PostScript possiedono alcune caratteristiche comuni, che sono tipo, valore ed attributi. Normalmente, per programmare in PostScript, è necessario considerare solo il tipo ed il valore di un oggetto, essendo gli attributi principalmente di interesse dell'interprete PostScript; solo in casi molto particolari, che di rado si presentano, può essere necessario intervenire sugli attributi con gli opportuni operatori.
La suddivisione più importante fra i tipi di oggetti è quella fra oggetti semplici e composti. La maggior parte degli oggetti PostScript sono oggetti semplici; questo significa che tutte le loro caratteristiche (tipo,valore ed eventuali attributi) sono strettamente legate assieme, e non possono essere modificate singolarmente. Ciò è necessario, ad esempio, per evitare che l'oggetto numerico intero 12 possa avere il valore di 13. L'unico metodo disponibile per cambiare le caratteristiche di un oggetto semplice è quello di copiarlo in un nuovo oggetto che abbia le caratteristiche desiderate.
Gli oggetti di tipo vettore, stringa e dizionario
sono invece oggetti composti; ciò significa che questi tipi di
oggetti possiedono una struttura interna formata da componenti, i
quali sono visibili, accessibili e modificabili singolarmente. Maggiori
dettagli su questi oggetti saranno forniti quando ne introdurremo
l'uso.
Gli oggetti numerici in PostScript possono essere interi o reali. I numeri interi possono variare fra limiti superiori ed inferiori che dipendono dall'implementazione; i numeri reali sono rappresentati in virgola mobile, ed hanno quindi una precisione che dipende dall'implementazione. Nessuna di queste caratteristiche variabili è di solito significativa nella normale programmazione in PostScript, posto che si evitino situazioni in cui l'errore di arrotondamento è decisivo. Un caso è quello in cui in un salto condizionato si confronta un intero con un reale; si tratta di situazioni da evitare indipendentemente dal linguaggio di programmazione utilizzato.
Malgrado non si tratti propriamente di numeri, considereremo i valori
logici come appartenenti a questa categoria di oggetti. Un
valore logico può avere solo due possibili valori, vero o falso; benché
siano equivalenti ai valori 1 e 0, in PostScript
essi hanno rappresentazioni interne particolari, quindi 0
non è uguale a falso ed 1 non è uguale a vero,
come avviene di solito nel Basic. Per questa ragione il
PostScript fornisce due operatori appositi, true e false,
che restituiscono i relativi valori e devono essere utilizzati
per i confronti con valori logici.
Gli oggetti nominali sono simboli indivisibili, definiti da una stringa di caratteri.
Un oggetto di tipo nome, non è la stringa di caratteri che lo identifica; i caratteri che formano questa stringa non sono infatti elementi del nome, ma devono essere presi nel loro insieme solo come il suo identificatore. Gli oggetti di tipo nome (che nel seguito indicheremo semplicemente come nome) non hanno un valore nel senso in cui lo possiedono in altri linguaggi di programmazione; essi sono invece associati con dei valori mediante l'uso del meccanismo dei dizionari. Per il momento accontentiamoci di questa definizione; ritorneremo su questo argomento più avanti.
Un oggetto di tipo operatore è un comando primitivo del PostScript; ogni volta che un operatore viene incontrato, l'interprete PostScript esegue l'azione corrispondente. Possiamo per ora pensare che un operatore ed il suo nome coincidano, in attesa di chiarire anche questo punto quando esamineremo in dettaglio il meccanismo dei dizionari.
Un oggetto di tipo file è una sequenza di
caratteri che può essere letta o scritta, e viene utilizzato per
trasferire informazioni fra il PostScript e l'ambiente esterno.
L'interprete PostScript possiede due oggetti di tipo file
predefiniti; lo standard input e lo standard output.
Il primo è la normale sorgente da cui l'interprete riceve i
programmi, i comandi ed i dati, il secondo è la normale destinazione
dei messaggi di status e di errore. Si noti che lo standard
output non ha nulla a che fare con l'output finale che si vuole
ottenere, cioè la pagina stampata; infatti i messaggi
dell'interprete PostScript (ad esempio lo %%[Status: idle]%%
che si ottiene battendo CONTROL-T) non finiscono sulla pagina
stampata ma ritornano al terminale da cui sono stati battuti. Nel
caso di un collegamento seriale con la LaserWriter possiamo
quindi dire che lo standard input è la tastiera del computer
e lo standard output è il relativo video.
Gli oggetti speciali sono particolari tipi di oggetti che il PostScript usa internamente per i suoi reconditi scopi; una minima conoscenza di essi è comunque necessaria per completare il panorama (molto sintetico) degli oggetti PostScript.
Il primo oggetto di questo tipo è il null. Come il suo nome suggerisce esso non contiene niente; viene usato ad esempio per occupare le posizioni di oggetti compositi quando vengono creati ma non sono ancora stati inizializzati. Un oggetto null può essere generato usando l'omonimo operatore null.
Un altro tipo di oggetto speciale è il mark. Viene largamente usato durante la programmazione per poter ritrovare particolari posizioni sugli stack quando vengono usati gli operatori di manipolazione degli stack (un attimo di pazienza, ne parleremo fra poco).
Il tipo di oggetto save viene utilizzato in momenti particolari dall'interprete PostScript per memorizzare e ripristinare il suo stato grafico; ne parleremo in esteso quando introdurremo i relativi operatori.
Il tipo fontID viene invece usato durante la
creazione dei font di carattere; si tratta di una operazione
abbastanza rara, perché i font vengono di solito acquisiti già
pronti, ed al massimo modificati. La creazione di un font sarà
forse oggetto di una intera puntata di questo corso.
Gli oggetti compositi sono, come abbiamo già ricordato, quelli dotati di struttura interna.
Il tipo stringa è formato da una sequenza di caratteri ASCII, inclusi quelli speciali e quelli non stampabili; i singoli caratteri vengono immagazzinati come byte, i quali possono essere interpretati anche come interi da 0 a 255. È possibile eseguire sulle stringhe le classiche operazioni che tutti i linguaggi consentono, ed accedere ai singoli caratteri per mezzo del loro indice. Una stringa viene definita racchiudendone i caratteri fra parentesi tonde (le parentesi tonde sono i primi caratteri speciali del PostScript che utilizziamo); si consideri però che gli spazi bianchi eventualmente presenti fanno parte integrante della stringa, quindi le stringhe
sono tutte diverse tra loro.
Il tipo vettore è formato da un insieme arbitrario di altri oggetti PostScript, inclusi altri oggetti vettore. Benché i vettori del PostScript possano avere elementi di tipo diverso, essi sono limitati ad una dimensione; nel caso che sia necessario utilizzare matrici, esse possono essere realizzate con qualunque numero di dimensioni semplicemente creando vettori di vettori .... di vettori. L'indice di un vettore PostScript parte da 0, quindi un vettore di n elementi ha il primo con indice 0 e l'ultimo con indice n-1. Un vettore viene definito racchiudendone gli elementi fra parentesi quadre; quindi un vettore di tre elementi che abbia come primo elemento la stringa pippo, come secondo il numero intero 2 e come terzo il valore di pi greco si ottiene scrivendo
Il tipo di oggetto dizionario verrà trattato in
dettaglio più avanti.
Gli stack del PostScript
Quelli di voi che conoscono il linguaggio Forth si saranno già accorti delle notevoli somiglianze che esso presenta col PostScript. Come quest'ultimo infatti il Forth obbliga ad utilizzare gli stack per manipolare i numeri, e permette di definire nuovi comandi utilizzando quelli preesistenti. La struttura interna dell'interprete PostScript ed il suo modo di funzionamento rivelano però significative differenze rispetto al Forth; infatti uno stack del Forth può contenere solo un tipo di dati (numeri interi o numeri reali).
Ma che cos'è uno stack? Uno stack non è niente altro che una
pila di oggetti (eventualmente vuota), in cui questi possono essere
messi o tolti solo partendo dalla cima, proprio come se si
trattasse di una pila di libri. Nel PostScript esistono quattro
distinti stack:
Tutti questi stack sono indipendenti l'uno dall'altro; le operazioni che avvengono su uno di essi non hanno nessuna influenza diretta sugli altri. Ogni stack ha un modo di accesso particolare, ma nelle linee generali, il funzionamento è identico
Lo stack di esecuzione è sotto l'esclusivo controllo dell'interprete PostScript, e può essere solo interrogato ma non alterato; il suo utilizzo è riservato a programmi molto sofisticati, e per i nostri scopi attuali ci basta sapere che si tratta dello stack su cui l'interprete pone gli oggetti che devono essere eseguiti, come le procedure.
Lo stack degli stati grafici contiene esclusivamente oggetti di tipo save; ne parleremo diffusamente quando introdurremo il concetto di stato grafico.
Lo stack dei dizionari contiene esclusivamente oggetti di tipo dizionario; anche di questo stack parleremo quando introdurremo l'uso dei dizionari.
Lo stack degli operandi, è di gran lunga il più importante, infatti la maggior parte degli operatori PostScript prelevano da qui i dati e vi depositano gli eventuali risultati; d'ora in poi ci riferiremo a lui chiamandolo semplicemente stack, mentre specificheremo sempre per esteso i nomi degli altri.
Lo stack degli operandi è, come gli altri, uno stack
di tipo LIFO (Last In First Out
- ultimo entrato primo uscito); esaminiamo il funzionamento con
un semplice programma che calcola la somma di due numeri e la
stampa sullo standard output.
Collegatevi via seriale alla LaserWriter, resettatela con
CONTROL-D, e, senza entrare in modalità executive,
battete la seguente riga di caratteri
12 37 add == flush
senza battere RETURN dopo l'ultimo carattere. Battete ora uno
spazio bianco; la LaserWriter risponderà
49
In figura 1 possiamo
osservare cosa avviene nello stack degli operandi durante
l'esecuzione di questo brevissimo programma (si suppone che
all'inizio lo stack sia vuoto) :
Gli operatori del PostScript
Gli operatori primitivi del PostScript sono, nella release 38.0 oltre 240; per fortuna molti di essi sono di uso raro e si può utilmente lavorare in PostScript conoscendone solo qualche decina. Non lamentatevi mai comunque del numero eccessivo di operatori; buona parte della potenza del PostScript risiede proprio nella presenza di questi operatori estremamente specializzati e potenti, che se ne stanno buoni da una parte in attesa che ne abbiate bisogno.
Gli operatori del PostScript possono essere classificati in
cinque gruppi principali:
Questa ultima classe è ovviamente device dependent; gli
esempi che faremo durante il corso riguarderanno, salvo avviso contrario,
quelli implementati sulla LaserWriter.
Il programma della prima parte
Commentiamo adesso il programma che è stato eseguito nella prima parte di questo corso; ci limiteremo però solo ad accennare al funzionamento degli operatori che incontreremo, riservandoci di definirli completamente in seguito. Il listato era
Commentiamolo adesso linea per linea.
Si conclude così questa seconda parte del corso, che vi dovrebbe aver messo in grado di compiere le prime prove utilizzando gli operatori fin qui illustrati; nella prossima parte apprenderemo l'uso di un set di base degli operatori PostScript e ci impadroniremo dell'utilizzo di procedure e dizionari.
Copyright © 1985: Marco A. Calamari