IF ITALIA - Il sito Italiano sull'Interactive Fiction
VII. GRAMMATICA E PARSING
VII.a. Definizione della grammatica
Ogni comando valido del giocatore deve essere specificato. Più precisamente ogni uso di un particolare verbo deve essere dettagliato per intero nel codice sorgente.Le definizioni della grammatica devono essere inserite sempre all'inizio del codice, prima di ogni oggetto o codice da eseguire. In pratica se diversi file addizionali di grammatica devono essere inclusi, o una nuova grammatica deve essere definita esplicitamente nel codice sorgente, deve essere fatto prima dell'inclusione dei file che contengono del codice da eseguire, o prima della definizione di ogni routine, oggetto, ecc.
La sintassi da usare è:
[x]verb "<verbo1>" [, "<verbo2>", "<verbo3>",...]Bene, che vuol dire? Ecco alcuni esempi tratti dal file di grammatica della libreria VERBLIB.G:
* <specificazione di sintassi 1> <RoutineVerbo1>
* <specificazione di sintassi 2> <RoutineVerbo2>
...verb "get"Ogni intestazione 'verb' o 'xverb' comincia con una nuova definizione di un verbo. Un 'xverb' è un qualificatore speciale che indica che l'interprete non deve chiamare la routine Main dopo aver completato con successo l'azione. Un 'xverb' viene di solito usato con i verbi non di azione, o di utilità come il salvataggio, il ripristino, l'uscita dal gioco, o il ricominciare da capo.
* DoVague
* "up"/"out"/"off" DoExit
* "outof"/"offof"/"off" object DoExit
* "in"/"on" object DoEnter
* multinotheld "from"/"off" parent DoGet
* multinotheld "offof"/"outof" parent DoGet
* multinotheld DoGetverb "take"
* DoVague
* "off" multiheld DoTakeOff
* multiheld "off" DoTakeOff
* multinotheld DoGet
* multinotheld "from"/"off" parent DoGet
* multinotheld "offof"/"outof" parent DoGetxverb "save"
* DoSave
* "game" DoSaveverb "read", "peruse"
* DoVague
* readable DoReadverb "unlock"
* DoVague
* lockable DoUnLock
* lockable "with" held DoUnLockSuccessivamente nell'intestazione sono indicati uno o più nomi del verbo. Ogni nome specificato userà la grammatica seguente allo stesso modo.
Questo è il motivo per il quale "get" e "take" negli esempi precedenti sono definiti separatamente, invece diverb "get", "take"In questo modo i comandiget upetake off hatsono consentiti, mentretake upeget off hatnon hanno senso.Ogni riga che comincia con l'asterisco ('*') è un uso valido del verbo che si sta defininendo. (Ogni input del giocatore deve cominciare con un verbo. Le eccezioni, dove un comando è diretto ad un oggetto come in
Ned, get the ballverranno dettagliate in seguito).Fino a due oggetti ed un qualunque numero di parole del dizionario possono essere usate per creare una riga di sintassi. Gli oggetti devono essere separati ad almeno una parola del dizionario.
Specificazioni di oggetto valide sono:
object qualunque oggetto visibile (l'oggetto diretto) xobject l'oggetto indiretto attribute qualunque oggetto visibile che ha l'attributo <attribute> parent qualunque xobject che è il padre dell'oggetto held qualunque oggetto in possesso del giocatore notheld un oggetto non in possesso del giocatore multi oggetti visibili multipli multiheld oggetti multipli in possesso del giocatore multinotheld oggetti multipli non posseduti dal giocatore number un numero intero positivo word qualunque parola del dizionario string una stringa tra virgolette (NomeRoutine) un nome di routine, tra parentesi (nomeogg) un nome di oggetto, tra parentesi (Se viene specificato un numero nella sintassi della grammatica, questo viene passato alla routine verbo nell'oggetto global. Se viene specificata una stringa, questa viene passata nella variabile dell'interprete parse$, che può essere trasformata in un array di stringhe usando la funzione 'string').
Le parole del dizionario che possono essere usate in maniera intercambiabile vanno separate da una barra ('/').
Due o più parole del dizionario digitate in sequenza devono essere specificate separatamente. Cioè se la riga in input è:
take hat out of suitcasela riga di sintassi* object "out" "of" containercorrisponderà, mentre* object "out of" containernon verrà mai riconosciuta, visto che l'interprete tratta in automatico "out" e "of" come due parole separate; il parser non troverà mai una corrispondenza per "out of".Per quel che riguarda l'indicazione degli oggetti nella riga di sintassi: Una volta che l'oggetto diretto è stato trovato, l'oggetto rimanente sulla riga di input verrà memorizzato come xobject. In pratica nell'esempio precendente, un oggetto valido sulla riga di input con l'attributo container verrà considerato l'oggetto indiretto dalla routine verbo.
NOTA: Un punto importante da ricordare quando si mischiano parole del dizionario ed oggetti in una riga di sintassi è che, a meno che sia stato indirizzato diversamente, il parser può confondere una combinazione parola-oggetto con un nome di oggetto non valido. Considerate questo:
verb "pick"Questa definizione avrà come risultato qualcosa tipo
* object DoGet
* "up" object DoGet>pick up box(partendo dal presupposto che "up" sia stato definito da qualche altra parte come parte di un altro nome di oggetto, come in OBJLIB.H), visto che il processore processa la sintassi
You haven't seen any "up box", nor are you likely to in
the near future even if such a thing exists.* objecte determina che un nome di oggetto non valido viene usato; non arriva mai a* "up" objectLa definizione corretta del verbo deve essere ordinata comeverb "pick"così che "pick <oggetto>" e "pick up <oggetto>" sono comandi validi.
* "up" object DoGet
* object DoGetPer definire una nuova condizione grammaticale che abbia la precedenza rispetto ad una già esistente -- come in VERBLIB.G -- basta definire la nuova condizione prima (ad esempio prima di includere VERBLIB.G).
NOTA: Come regola generale, a meno che si abbia la necessità di anticipare il normale processo della grammatica della libreria, la nuova grammatica va inserita dopo l'inclusione dei file della libreria. (La ragione di questo è che la grammatica della libreria è stata messa punto per gestire situazioni come quelle esposte più sopra).
Un oggetto singolo può essere specificato come l'unico oggetto valido per una particolare sintassi:
verb "sfrega"che produrrà un errore "You can't do that with..." per ogni oggetto diverso dall'oggetto lampada_magica.
* (lampada_magica) DoSfregaLampadaMagicaUsare un nome di routine per specificare un oggetto è leggermente più complicato: l'interpete chiama la routine indicata passandole l'oggetto specificato nell'input come argomento; se la routine restituisce true allora l'oggetto è valido, altrimenti un errore di interpretazione deve essere stampato dalla routine. Se due nomi di routine vengono usati in una determinata sintassi, come in
* (PrimaRoutine) "con" (SecondaRoutine)allora PrimaRoutine viene usata per la validazione di object e SecondaRoutine per la validazione di xobject.VII.b. Il Parser
Subito dopo che una riga in input è stata ricevuta, l'interprete chiama il parser, ed il primo passo consiste nell'identificare tutte le parole non valide, cioè le parole che non sono nella tabella del dizionario.NOTA: Nella riga di input è consentito inserire una parola o frase non compresa nel dizionario a patto che venga racchiusa tra virgolette (""). Se il comando viene interpretato correttamente, questa stringa viene passata a parse$. Non è possibile avere più di una parola o frase non compresa nel dizionario (anche se le frasi addizionali vengono racchiuse tra virgolette).
Il passo successivo consiste nel suddividere la riga in parole singole. Le parole sono separate da spazi e simboli di punteggiatura (inclusi "!" e "?") che vengono rimossi. Tutti i caratteri della riga di input vengono convertiti in minuscolo.
Successivamente si tratta di processare i tre tipi di parole speciali che possono essere definiti nel codice sorgente.
I REMOVAL [rimuovibili] sono i più semplici. Sono semplicemente parole che vengono automaticamente rimosse dalla riga in input, e sono fondamentalmente limitati a parole come "a" e "the", che renderebbero l'esame della grammatica più complicato e difficile.
La sintassi per la definizione di un removal è:
removal "<parola1>"[, "<parola2>", "<parola3>",...]come inremoval "a", "an", "the"Il PUNCTUATION [punteggiatura] è simile al removal, solo che specifica la rimozione di caratteri singoli invece di intere parole:punctuation "<carattere1>[<carattere2>...]"come inpunctuation "$%"I SYNONYM [sinonimi] sono più complicati. Sono parole che non verranno mai trovate nella riga di input interpretata; sono sostituiti dalla parola specificata per la quale sono sinonimi.synonym "<sinonimo>" for "<parola>"come insynonym "myself" for "me"L'esempio precedente sostituisce tutte le ricorrenze di "myself" nella riga di input con "me". L'uso dei sinonimi non deve essere molto esteso, visto che esiste la possibilità, particolarmente nel caso dei nomi di oggetti e degli aggettivi, di specificare come sinonimi parole che vengono già trattate come distinte.I COMPOUND [compositi] sono l'ultimo tipo di parole speciali, specificati con:
compound "<parola1>", "<parola2>"come incompound "out", "of"così che la riga di inputget hat out of suitcasevenga interpretata comeget hat outof suitcaseA seconda della progettazione delle tabelle di grammatica per alcune sintassi, l'uso dei compound può rendere la definizione della grammatica più semplice, così che usando il compound precedente,verb "get"è possibile, e preferibile a
* multinotheld "outof"/"offof"/"from" parentverb "get"Quando il parser ha terminato di processare la riga di input, il risultato è un array definito (da Hugo Engine) in maniera speciale chiamato word, il cui numero di elementi validi è contenuto nella variabile globale words.
* multinotheld "out"/"off" "of" parent
* multinotheld "from" parentPerciò in
get the hat from the tableil parser -- usando i removal definiti in HUGOLIB.H -- produce i seguenti risultati:word[1] = "get"NOTA: Le righe di comandi multipli sono consentite, purché ogni comando venga separato da un punto (".").
word[2] = "hat"
word[3] = "from"
word[4] = "table"words = 4
get hat. go n. go e.diventaword[1] = "get"(Controllate la routine Parse in HUGOLIB.H per un esempio di come
word[2] = "hat"
word[3] = ""
word[4] = "go"
word[5] = "n"
word[6] = ""
word[7] = "go"
word[8] = "e"
word[9] = ""words = 9
get hat then go nviene tradotto in:word[1] = "get"È consentito un massimo di trentadue parole. Il punto viene in ogni caso convertito nella voce di dizionario null ("", indirizzo = 0), che segnala all'interprete che l'analisi del comando corrente termina qui.
word[2] = "hat"
word[3] = ""
word[4] = "go"
word[5] = "n")NOTA: le routine di parsing e della grammatica riconoscono anche diverse parole di sistema, ognuna nel formato "~parola". Queste sono:
~and riferendosi a: oggetti multipli specifici ~all riferendosi a: oggetti multipli in generale ~any riferendosi a: uno qualunque in una lista di oggetti ~except riferendosi a: un oggetto da escludere ~oops per correggere un errore nella riga di input precedente Per consentire ad una riga di input di accedere ad una di queste parole di sistema, bisogna definire un sinonimo come in
synonym "and" for "~and"La libreria definisce diversi di questi sinonimi.
©2000 Simone Zanella e ©2000 IF Italia. E' vietata la riproduzione.