Parserizzazione e
compilazione in VB
Enrico Granata  atgrana@tin.it

[Seconda parte]

Stavolta tenteremo di fare in VB qualcosa di un po' più complesso di DAO, ovvero parserizzare e compilare dati, quante volte avete usato OCX non adatti al vostro caso specifico per tale uso?
Questo poichè scrivere un Parser in VB è un uso del linguaggio raro, visto che tutti i compilatori e gli IDE sono scritti in C++ o anche C

Operazioni di I/O

Iniziamo col chiarire che parserizzare e compilare non sono attività separate dallÆI/O dei dati, ma dipendenti dal successo di queste

Poiché

Dim Stringa$

Open "codicesorgente.c" For Input As 1

Do Until Eof(1) : Line Input#1, Stringa$ : Compile(Stringa$): Loop
Close 1

Public Sub Compile(Source$)

Dim Src$

Do Until Tokens(Source$)=0

Src$=GetToken(Tokens(Source$))

If IsKeyWord(Src$) Then ....

If IsIdentifier(Src$) Then...

Loop

End Sub

Questo codice non è che un semplice esempio di I/O e parserizzazione

Esaminiamola

Public Sub Compile(Source$)

Dim Src$

1->Do Until Tokens(Source$)=0

2->Src$=GetToken(Tokens(Source$))

3->If IsKeyWord(Src$) Then ....

4->If IsIdentifier(Src$) Then...

Loop

End Sub

  1. Tokens(Str$) dovrebbe riportare il numero di Token ancora non letti
  2. GetToken(Int%) dovrebbe ritornare il Intesimo Token
  3. IsKeyWord(Str$) ritorna True se il Token è una parola chiave
  4. IsIdentifier(Str$) ritorna True se il Token è un identificatore

Primi elementi di parserizzazione: I token

CosÆè un token?
Esso è un elemento di una linea di codice sorgente, non dipendente da altri adiacenti per la formazione del suo significato generale, ma che può avere bisogno di altri elementi adiacenti per la formazione del suo significato specifico

Ovvero?

Dim X As Integer

Dim

X

As

Integer

sono i 4 token

Ciascuno di essi è un token poichè il suo significato generale ci è chiaro a priori

Dim->dichiarazione di variabili

X->identificatore

As->parola chiave di tipo

Integer->tipo di dati

Insieme i quattro token formano una espressione

Una espressione è un insieme di token che abbiano un significato nella sintassi S del linguaggio L

Ovvero che E appartenga allÆinsieme S di L

Cioè

Dim KX 2E+456 As If IsLong()

EÆ un insieme di 6 token, ma non è una espressione

Poichè la sintassi S di Vb stabilisce che

Dim id As tipo

E la sintassi di id è tale che

IsId=True if id like [0-Z] and mid(id,1,1) like [A-Z]

Ovvero può contenere sia numeri che caratteri, ma deve iniziare con un carattere

Questo ci introduce ad un primo uso delle regole sui token e sulle espressioni

Ma quale token?....

Ricordate che in base allÆelemento da parserizzazare P la sintassi S usata può usare 2 o più identificativi I

Ovvero alla ricerca di XYZ in un file di testo esistono 2 tipi di token

Il secondo tipo di token potrebbe anche essere detto "ogni altra sequenza", poichè un token è una sequenza di caratteri

Tornando al nostro file osserviamo che

P=file.txt

S=XYZ, ognialtracosa

I=parolachiave, idestranei

Ovvero per P la sintassi S in collaborazione con gli identificativi I definisce come

parolachiave-XYZ

idestranei-ognialtracosa

Tale sintassi è molto facile, ma ovviamente ne esistono altre molto più difficili, quale quella di VB(ma essa è molto semplice rispetto a quella di C++)

AllÆinterno di una generica sintassi S si potrebbe osservare lÆesistenza di 2 tipi di espressioni

LÆespressione totale è tale che essa contiene almeno 1 token per ogni tipo di token consentito dalla sintassi usata per la sua generazione

LÆespressione nulla invece è quella che contiene 1 solo token, di solito INVIO (che è un token poichè ha un significato generale(di fine della linea) ed uno specifico(uguale a quello generale))

EÆ abbastanza facile capire che esistono poi le espressioni normali, la maggioranza che contengono dei token, di uno o più tipi

Ricordate se conoscete C++/C che lÆespressione nulla non è lÆistruzione nulla

Poichè

;

la prima linea è lÆespressione nulla(1 token di newline), la seconda è lÆistruzione nulla(2 token, uno di punteggiatura è uno di newline)

Tornando a Vb, possiamo dire che una vera e propria espressione nulla esiste, ma non è applicabile in tutti i contesti, e neanche Vb dispone di una vera istruzione nulla, almeno in tutti i contesti, poichè

If X=True Then ...

dopo il Then non è possibile omettere codice, ma noi possiamo crearne una

Tip per VB5

Per creare lÆistruzione nulla aprite un nuovo form

Scrivete tale codice

Sub IstruzioneNulla()
End Sub

salvatelo come Template, ed ecco il vostro form

If X=True Then IstruzioneNulla

Dopo tanta teoria...mi sarò pure meritato un file da parserizzare?

In effetti, si, sono stato molto teorico allÆinizio dellÆarticolo, ma adesso passiamo a noi, diciamo di avere un generico file F di nome "miofile.ml"(ml sta per miolinguaggio)

Dobbiamo parserizzarlo per ottenere miofile.bas, la sintassi S è la seguente

Sintassi S per il file F

Sintassi per il token T

Tipo di Token di T e descrizione di esso

Token VBT per VB

Set x=y

Parola chiave con 2 parametri, un identificatore e un numero

Let x=y

Message x,y,z

Parola chiave con 3 parametri, 2 stringhe ed un numero

MsgBox x,z,y

BailOut

Parola chiave senza parametri

End

TempBailOut

Parola chiave senza parametri

Stop

Print x

Parola chiave con 1 parametro, una stringa

Print x

SymbolNew x,y

Parola chiave con 2 parametri, due identificatori

Dim x as y

TellMeWhat x,y

Parola chiave con 2 parametri, una stringa ed un identificatore

y=InputBox(x)

La sintassi è Case Sensitive

Questa è la sintassi S di L(Linguaggio) applicata ad F(file)

Inizialmente notiamo che esistono i seguenti tipi di token

  1. Identificatore
  2. Parola chiave
  3. Numero
  4. Stringa

Un identificatore è un nome di variabile

Una parola chiave è un token che non può essere usato come identificatore

Un numero è un qualsiasi token che può essere usato in ambito matematico

Una stringa è una sequenza di caratteri delimitata da " e "

Adesso possiamo iniziare a scrivere il codice

Saltiamo gli ovvi preliminari di I/O che già vi dovrebbero essere noti per passare al dunque

Siamo sicuri che non sia una stringa?

Il modo migliore per parserizzare?
Quello context-depending, ovvero per trovare XYZ nel nostro esempio precedente conviene una semplice Instr(), e se dobbiamo trovarla come parola singola su una linea un Len() sarà adatto al nostro scopo, ma per compilare Linux non abbiamo scelta, dovremo districarci tra decine di tipi di token ed adattamenti di S(Sintassi) al fine di ottenere qualcosa che sia accettabile(non dico veloce)

e che non sbagli a compilare

if (a==5) {

A=4;

}

assegnando ad a il valore 4, per cui ripassiamo i due tipi di sintassi

Ovvero, VB e Delphi sono linguaggi per cui

If

if

iF

sono equivalenti, in C++ e Java invece

class

Class

claSS

clasS

sono diversi, solo il primo class è una parola chiave, gli altri sono identificatori validi

Tornando alla nostra sintassi S iniziamo a scrivere il codice C

Un po' di stinghe, qualche parola chiave ed il .EXE è fatto

Prima cosa ci servono il file di Input ed il file di Output, i loro handle saranno 1 e 2

Poi ci serve una stringa per memorizzare i dati

Le fasi della parserizzazione di un token

  1. Individuazione del tipo di token
  • Verifica dellÆesistenza di una espressione e aggiunta del token ad essa
  • Verifica della correttezza sintattica dellÆespressione
  • Compilazione dellÆespressione se essa è valida e completa
  • Fase 1.GetTokenType()

    La funzione GetTokenType(Token$) stabilisce il tipo di token, chiamando una funzione per ogni tipo di token, la cui convenzione di chiamata è Istipotoken(per esempio IsIdentifier)

    Public Function GetTokenType%(Token$)

    If IsKeyWord(Token$) Then

    GetTokenType%=TKN_KEYWORD

    Exit Function

    ElseIf IsIdentifier(Token$) Then

    GetTokenType%=TKN_IDENTIFIER

    Exit Function

    ElseIf IsNumber(Token$) Then

    GetTokenType%=TKN_NUMBER

    Exit Function

    ElseIf IsString(Token$) Then

    GetTokenType%=TKN_STRING

    Exit Function

    Else

    GetTokenType%=TKN_NONE

    Exit Function

    End If

    End Function

    Le costanti TKN_* sono numeri interi così definiti

    Const TKN_KEYWORD=1

    Const TKN_IDENTIFIER=2

    Const TKN_NUMBER=3

    Const TKN_STRING=4

    Const TKN_NONE=-1

    Questo per chiarezza, poichè TKN_STRING è più chiaro di 4

    Il caso TKN_NONE è possibile?

    Si:

    ADGB

    non è alcun token del linguaggio, ne è stato definito prima da SymbolNew

    Adesso, le singole funzioni di verifica sono abbastanza facili, tranne IsIdentifier()

    Comunque iniziamo

    Public Function IsString(Token$) As Boolean

    IsString=(Left(Token$,1)="æ" And Right(Token$,1)="æ")

    End Function

    Public Function IsKeyWord(Token$) As Boolean

    Select Case Token$

    Case "SymbolNew","TellMeWhat","Set","BailOut","TempBailOut","Message","Print"

    IsKeyWord=True

    Case Else

    IsKeyWord=False

    End Select

    End Function

    Public Function IsNumber(Token$) As Boolean

    IsNumber=IsNumeric(Token$)

    End Function

    La prossima volta scopriremo come scrivere IsIdentifier()

    Ciao a tutti!!!

    Stavolta tenteremo di fare in VB qualcosa di un po' più complesso di DAO, ovvero parserizzare e compilare dati, quante volte avete usato OCX non adatti al vostro caso specifico per tale uso?
    Questo poichè scrivere un Parser in VB è un uso del linguaggio raro, visto che tutti i compilatori e gli IDE sono scritti in C++ o anche C

    Operazioni di I/O

    Iniziamo col chiarire che parserizzare e compilare non sono attività separate dallÆI/O dei dati, ma dipendenti dal successo di queste

    Poiché

    Dim Stringa$

    Open "codicesorgente.c" For Input As 1

    Do Until Eof(1) : Line Input#1, Stringa$ : Compile(Stringa$): Loop
    Close 1

    Public Sub Compile(Source$)

    Dim Src$

    Do Until Tokens(Source$)=0

    Src$=GetToken(Tokens(Source$))

    If IsKeyWord(Src$) Then ....

    If IsIdentifier(Src$) Then...

    Loop

    End Sub

    Questo codice non è che un semplice esempio di I/O e parserizzazione

    Esaminiamola

    Public Sub Compile(Source$)

    Dim Src$

    1->Do Until Tokens(Source$)=0

    2->Src$=GetToken(Tokens(Source$))

    3->If IsKeyWord(Src$) Then ....

    4->If IsIdentifier(Src$) Then...

    Loop

    End Sub

    1. Tokens(Str$) dovrebbe riportare il numero di Token ancora non letti
    2. GetToken(Int%) dovrebbe ritornare il Intesimo Token
    3. IsKeyWord(Str$) ritorna True se il Token è una parola chiave
    4. IsIdentifier(Str$) ritorna True se il Token è un identificatore

    Primi elementi di parserizzazione: I token

    CosÆè un token?
    Esso è un elemento di una linea di codice sorgente, non dipendente da altri adiacenti per la formazione del suo significato generale, ma che può avere bisogno di altri elementi adiacenti per la formazione del suo significato specifico

    Ovvero?

    Dim X As Integer

    Dim

    X

    As

    Integer

    sono i 4 token

    Ciascuno di essi è un token poichè il suo significato generale ci è chiaro a priori

    Dim->dichiarazione di variabili

    X->identificatore

    As->parola chiave di tipo

    Integer->tipo di dati

    Insieme i quattro token formano una espressione

    Una espressione è un insieme di token che abbiano un significato nella sintassi S del linguaggio L

    Ovvero che E appartenga allÆinsieme S di L

    Cioè

    Dim KX 2E+456 As If IsLong()

    EÆ un insieme di 6 token, ma non è una espressione

    Poichè la sintassi S di Vb stabilisce che

    Dim id As tipo

    E la sintassi di id è tale che

    IsId=True if id like [0-Z] and mid(id,1,1) like [A-Z]

    Ovvero può contenere sia numeri che caratteri, ma deve iniziare con un carattere

    Questo ci introduce ad un primo uso delle regole sui token e sulle espressioni

    Ma quale token?....

    Ricordate che in base allÆelemento da parserizzazare P la sintassi S usata può usare 2 o più identificativi I

    Ovvero alla ricerca di XYZ in un file di testo esistono 2 tipi di token

    Il secondo tipo di token potrebbe anche essere detto "ogni altra sequenza", poichè un token è una sequenza di caratteri

    Tornando al nostro file osserviamo che

    P=file.txt

    S=XYZ, ognialtracosa

    I=parolachiave, idestranei

    Ovvero per P la sintassi S in collaborazione con gli identificativi I definisce come

    parolachiave-XYZ

    idestranei-ognialtracosa

    Tale sintassi è molto facile, ma ovviamente ne esistono altre molto più difficili, quale quella di VB(ma essa è molto semplice rispetto a quella di C++)

    AllÆinterno di una generica sintassi S si potrebbe osservare lÆesistenza di 2 tipi di espressioni

    LÆespressione totale è tale che essa contiene almeno 1 token per ogni tipo di token consentito dalla sintassi usata per la sua generazione

    LÆespressione nulla invece è quella che contiene 1 solo token, di solito INVIO (che è un token poichè ha un significato generale(di fine della linea) ed uno specifico(uguale a quello generale))

    EÆ abbastanza facile capire che esistono poi le espressioni normali, la maggioranza che contengono dei token, di uno o più tipi

    Ricordate se conoscete C++/C che lÆespressione nulla non è lÆistruzione nulla

    Poichè

    ;

    la prima linea è lÆespressione nulla(1 token di newline), la seconda è lÆistruzione nulla(2 token, uno di punteggiatura è uno di newline)

    Tornando a Vb, possiamo dire che una vera e propria espressione nulla esiste, ma non è applicabile in tutti i contesti, e neanche Vb dispone di una vera istruzione nulla, almeno in tutti i contesti, poichè

    If X=True Then ...

    dopo il Then non è possibile omettere codice, ma noi possiamo crearne una

    Tip per VB5

    Per creare lÆistruzione nulla aprite un nuovo form

    Scrivete tale codice

    Sub IstruzioneNulla()
    End Sub

    salvatelo come Template, ed ecco il vostro form

    If X=True Then IstruzioneNulla

    Dopo tanta teoria...mi sarò pure meritato un file da parserizzare?

    In effetti, si, sono stato molto teorico allÆinizio dellÆarticolo, ma adesso passiamo a noi, diciamo di avere un generico file F di nome "miofile.ml"(ml sta per miolinguaggio)

    Dobbiamo parserizzarlo per ottenere miofile.bas, la sintassi S è la seguente

    Sintassi S per il file F

    Sintassi per il token T

    Tipo di Token di T e descrizione di esso

    Token VBT per VB

    Set x=y

    Parola chiave con 2 parametri, un identificatore e un numero

    Let x=y

    Message x,y,z

    Parola chiave con 3 parametri, 2 stringhe ed un numero

    MsgBox x,z,y

    BailOut

    Parola chiave senza parametri

    End

    TempBailOut

    Parola chiave senza parametri

    Stop

    Print x

    Parola chiave con 1 parametro, una stringa

    Print x

    SymbolNew x,y

    Parola chiave con 2 parametri, due identificatori

    Dim x as y

    TellMeWhat x,y

    Parola chiave con 2 parametri, una stringa ed un identificatore

    y=InputBox(x)

    La sintassi è Case Sensitive

    Questa è la sintassi S di L(Linguaggio) applicata ad F(file)

    Inizialmente notiamo che esistono i seguenti tipi di token

    1. Identificatore
    2. Parola chiave
    3. Numero
    4. Stringa

    Un identificatore è un nome di variabile

    Una parola chiave è un token che non può essere usato come identificatore

    Un numero è un qualsiasi token che può essere usato in ambito matematico

    Una stringa è una sequenza di caratteri delimitata da " e "

    Adesso possiamo iniziare a scrivere il codice

    Saltiamo gli ovvi preliminari di I/O che già vi dovrebbero essere noti per passare al dunque

    Siamo sicuri che non sia una stringa?

    Il modo migliore per parserizzare?
    Quello context-depending, ovvero per trovare XYZ nel nostro esempio precedente conviene una semplice Instr(), e se dobbiamo trovarla come parola singola su una linea un Len() sarà adatto al nostro scopo, ma per compilare Linux non abbiamo scelta, dovremo districarci tra decine di tipi di token ed adattamenti di S(Sintassi) al fine di ottenere qualcosa che sia accettabile(non dico veloce)

    e che non sbagli a compilare

    if (a==5) {

    A=4;

    }

    assegnando ad a il valore 4, per cui ripassiamo i due tipi di sintassi

    Ovvero, VB e Delphi sono linguaggi per cui

    If

    if

    iF

    sono equivalenti, in C++ e Java invece

    class

    Class

    claSS

    clasS

    sono diversi, solo il primo class è una parola chiave, gli altri sono identificatori validi

    Tornando alla nostra sintassi S iniziamo a scrivere il codice C

    Un po' di stinghe, qualche parola chiave ed il .EXE è fatto

    Prima cosa ci servono il file di Input ed il file di Output, i loro handle saranno 1 e 2

    Poi ci serve una stringa per memorizzare i dati

    Le fasi della parserizzazione di un token

    1. Individuazione del tipo di token
  • Verifica dellÆesistenza di una espressione e aggiunta del token ad essa
  • Verifica della correttezza sintattica dellÆespressione
  • Compilazione dellÆespressione se essa è valida e completa
  • Fase 1.GetTokenType()

    La funzione GetTokenType(Token$) stabilisce il tipo di token, chiamando una funzione per ogni tipo di token, la cui convenzione di chiamata è Istipotoken(per esempio IsIdentifier)

    Public Function GetTokenType%(Token$)

    If IsKeyWord(Token$) Then

    GetTokenType%=TKN_KEYWORD

    Exit Function

    ElseIf IsIdentifier(Token$) Then

    GetTokenType%=TKN_IDENTIFIER

    Exit Function

    ElseIf IsNumber(Token$) Then

    GetTokenType%=TKN_NUMBER

    Exit Function

    ElseIf IsString(Token$) Then

    GetTokenType%=TKN_STRING

    Exit Function

    Else

    GetTokenType%=TKN_NONE

    Exit Function

    End If

    End Function

    Le costanti TKN_* sono numeri interi così definiti

    Const TKN_KEYWORD=1

    Const TKN_IDENTIFIER=2

    Const TKN_NUMBER=3

    Const TKN_STRING=4

    Const TKN_NONE=-1

    Questo per chiarezza, poichè TKN_STRING è più chiaro di 4

    Il caso TKN_NONE è possibile?

    Si:

    ADGB

    non è alcun token del linguaggio, ne è stato definito prima da SymbolNew

    Adesso, le singole funzioni di verifica sono abbastanza facili, tranne IsIdentifier()

    Comunque iniziamo

    Public Function IsString(Token$) As Boolean

    IsString=(Left(Token$,1)="æ" And Right(Token$,1)="æ")

    End Function

    Public Function IsKeyWord(Token$) As Boolean

    Select Case Token$

    Case "SymbolNew","TellMeWhat","Set","BailOut","TempBailOut","Message","Print"

    IsKeyWord=True

    Case Else

    IsKeyWord=False

    End Select

    End Function

    Public Function IsNumber(Token$) As Boolean

    IsNumber=IsNumeric(Token$)

    End Function

    La prossima volta scopriremo come scrivere IsIdentifier()

    Ciao a tutti!!!