Newsgroup: it.comp.lang.delphi
Titolo: Sempre array di funzioni (o sistema di scripting)

Ciao a tutti!
Ho fatto un array di funzioni tutte uguali, richiamabili con l'indice
dell'array e tutto funziona bene.
Sarebbe carino pero' poter richiamare funzioni che hanno parametri
diversificati. Come si puo' fare?
In pratica vorrei fare un micro sistemino di scripting da file di testo.
Se io faccio un script.txt cosi':
cerchio (10,10,5);
messaggio ("ciao");
Come potrei "compilarlo/interpretarlo" per essere eseguito da funzioni
definite nel programma?
Beh, le funzioni potrebbero spiazzare l'indice di un array (cerchio=1,
messaggio=2) che associa i comandi del mio script a funzioni reali. E qui
pero' c'e' il problema che la definizione delle funzioni e' diversa per
ognuna di esse.
I parametri potrei convertirli tutti in sequenze di byte e poi
interpretarli...
Mi serve una mano :-*
Faccio prima a prendere qualcosa di gia' fatto, forse :-P
A questo proposito, qual'e' secondo voi il sistema di scripting ad
esecuzione piu' veloce per Delphi? Lua? Innerfuse? Qualcos'altro che non
conosco? Innerfuse l'ho appena scaricato, sembra bello, anche se e' troppo
per quello che serve a me.
A presto, Chris

 In pratica vorrei fare un micro sistemino di scripting da file di testo.
Io per un programmino che stavo facendo e non ho portato a termine, avevo
fatto così (è la prima cosa che mi
è venuta in mente :) ):
    TComandi = (
                cNone,
                cATR,
                cGetUA,
                cGetEntities,
                cGetOS,
                cGetProv,
                cDataReq,
                cSendDRCmd,
                cGetProvRec,
                cGetProvPKey,
                cModStatoPIN,
                ...bla...bla...
               );
    PTDettaglioComandi  = ^TDettaglioComandi;
    TDettaglioComandi = record
        fCmd        : TComandi;
        fDescr      : string;
        fQTemplate  : string;
        fATemplate  : string;
        fSequenza   : string;
        fGetParams  : boolean;  // Segnala che esiste un elenco di
parametri.
        fCheckINS   : boolean;  // Indica che è necessario controllare la
corrispondenza del campo INS con la risposta ottenuta.
        fCheckSW    : boolean;  // Indica che occorre confrontare la SW
ricevuta con quella attesa.
        fDLength    : integer;  // Lunghezza stringa di Dati nella risposta.
        fDLengthPar : boolean;  // Recupera la DLength dai Parametri. Per
convenzione, la lunghezza è sempre l'ultimo parametro.
        fALength    : integer;  // Lunghezza attesa della risposta.
        fALenFixed  : boolean;  // Indica che la lunghezza della risposta
attesa è fissa e non va calcolata al momento della costruzione del comando.
        fALenPar    : boolean;  // Recupera la DLength dai Parametri. Per
convenzione, la lunghezza è sempre il penultimo parametro.
        fSWAtteso   : TSWMeaning;
        fCheckFlag  : boolean;  // "True" segnala la presenza di un Flag da
utilizzare in fase di interpretazione della risposta.
        fFlag       : word;
    end;
    cElencoSequenze     : array [TComandi,TCampiSequenza] of string = (
                                                            ('','Comando
Nullo','',''),
('$RTS$$1:50$$0:X$','Richiesta ATR','',''),
('C10E000008','Recupera UA','',''),
('C116000006','Recupera Entità Supportate','',''),
('C10A00005F','Recupera SO Card','',''),
('C112'+cIDParam+'0019','Informazioni Provider','',''),
                                                            ...bla...bla...
                                        );
    cElencoComandi  : array [TComandi] of TDettaglioComandi = (
                                                                (fCmd:
cNone; fGetParams: bNo; fCheckINS: bNo; fCheckSW: bNo; fDLength: cUnkLen;
fDLengthPar: bNo; fALength: cUnkLen; fALenFixed: bNo; fALenPar: bNo;
fSWAtteso: swNull; fCheckFlag: bNo),
                                                                (fCmd: cAtr;
fGetParams: bNo; fCheckINS: bNo; fCheckSW: bSi; fDLength: $10; fDLengthPar:
bNo; fALength: $10; fALenFixed: bNo; fALenPar: bNo; fSWAtteso: sw9000;
fCheckFlag: bNo),
                                                                (fCmd:
cGetUA; fGetParams: bNo; fCheckINS: bSi; fCheckSW: bSi; fDLength: $08;
fDLengthPar: bNo; fALength: $08; fALenFixed: bNo; fALenPar: bNo; fSWAtteso:
sw9000; fCheckFlag: bNo),
                                                                (fCmd:
cGetEntities; fGetParams: bNo; fCheckINS: bSi; fCheckSW: bSi; fDLength: $06;
fDLengthPar: bNo; fALength: $06; fALenFixed: bNo; fALenPar: bNo; fSWAtteso:
sw9000; fCheckFlag: bNo),
                                                                (fCmd:
cGetOS; fGetParams: bNo; fCheckINS: bSi; fCheckSW: bSi; fDLength: $5F;
fDLengthPar: bNo; fALength: $5F;  fALenFixed: bNo; fALenPar: bNo; fSWAtteso:
sw9000; fCheckFlag: bNo),
                                                                (fCmd:
cGetProv; fGetParams: bSi; fCheckINS: bSi; fCheckSW: bSi; fDLength: $19;
fDLengthPar: bNo; fALength: $18; fALenFixed: bNo; fALenPar: bNo; fSWAtteso:
sw9000; fCheckFlag: bNo),
...bla...bla...
                                                              );
Allora:
TComandi è un "contenitore" dell'elenco dei comandi riconosciuti dal
componente, inoltre esso viene
utilizzato dal componente anche per "affari suoi" :)
TDettaglioComandi, come è facile intuire, contiene la descrizione di ogni
comando (parametri, risposte attese ecc..)
cElencoSequenze contiene la descrizione ed il template del comando.
cElencoComandi è un array di TDettaglioComandi.
Uno script era composto come segue:
    // - Una riga di codice in formato script deve rispettare il seguente
formato:
    // <Numero Riga:><Istruzione>[;Status Word;][// Commento]
    // Se il primo carattere di <Istruzione> è:
    //  $ allora l'istruzione è del tipo $C13200000D
    //  # allora è un'istruzione del tipo #IF, #GOTO
    // not [$,#] allora è una chiamata ad uno dei comandi predefiniti. (es:
cGetOS)
trovando ad esempio:
01:GetOs;// Recupera la versione del sistema operativo
02:GetProv(0A);// Recupera le informazioni sul provide $0A
L'oggetto che interpretava lo script si comportava in questo modo:
1) Decomponeva ogni riga nelle sue componenti secondo il modello specificato
sopra.
2) il comando "GetOs" (ad esempio) veniva recuperato dalla struttura
cElencoComandi
mediante le informazioni RTTI per mezzo della funzione Delphi
"GetEnumValue":
Es:
    supponendo che:
var
    comando    : string;
    indice        : integer;
    info            : TDettaglioComandi;
begin
    comando ad un certo punto vale --> comando:='GetOs';
    indice:=GetEnumValue(TypeInfo(TComandi),format('c%s',[comando]));
    info:=cElencoComandi[indice];
end;
Nella variabile "info" avrò tutto quello che mi occorre per decifrare il
comando recuperato
dallo script.
Nel mio caso, i parametri erano tutti di tipo numerico (a lunghezza
variabile).
Il template da inviare come comando, assieme alla posizione (ed
eventualmente al tipo di parametro)
viene recuperato per mezzo di cElencoSequenze.
Certo, il "linguaggio script" così implementato è piuttosto semplice e,
forse, poco flessibile. Senza
contare che utilizzava pure i "Goto" :)
Se avessi voluto portare a termine il "progetto", era prevista anche la
possibilità di estendere il linguaggio
per mezzo di dll e template di comandi esterni all'applicazione principale.
Spero di non averti creato troppa confusione.
Cordiali saluti,
Carmine Moleti