[Risolto][C] Notazione polacca

Linguaggi di programmazione: php, perl, python, C, bash e tutti gli altri.
gila75
Imperturbabile Insigne
Imperturbabile Insigne
Messaggi: 2739
Iscrizione: mercoledì 16 gennaio 2013, 17:28
Desktop: ubuntu-2d
Distribuzione: Ubuntu 12.04.2 LTS i686
Località: Airuno(Lecco)

[Risolto][C] Notazione polacca

Messaggio da gila75 »

Ciao a tutti, sto implementando il calcolo in notazione polacca inversa:
5 8 * 4 9 - /=-8
Apparentemente sembra facile : ad ogni numero nella stringa fai un push nello stack, ad ogni operatore calcoli e fai un pop.
i problemi sono sulla lettura della stringa:
1) devo riconoscere i numeri delimitati dagli spazi
2) gli spazi possono essere uno o tanti, il programma deve essere "immune"
3) non sono ammesse scritture come:5a 8 * 4 9 - /
4) operatori doppi come ++ se non staccati da spazio non sono ammessi, nemmeno:5+ 8 * 4 9 -
5) bisogna riconoscere se il numero degli operatori è corretto 5 3 + + (sbagliato e devo stampare buffer underflow).

Mi domando se per gestire tutte queste cose non sia necessario studiarsi le espressioni regolari. Che tra l'altro non so nemmeno cosa
siano, suppongo facciano ricerche molto più evolute.
Io ho scritto un programma , è un prototipo, ripeto, pensavo fosse molto più semplice...invece no.
Mi piacerebbe sapere se ci sono modi per non incappare in molteplici if per le varie ricerche nella stringa.
Per ora uso solo numeri interi, quando invece dovrei usare i float.
Ma cosi facendo ho un controllo in più
5.3 4.9 + va bene
ma deve essere in grado di capire che il punto spaiato è un errore:
5.3 4.9 . + errore (un'idea ce l'avrei).
Il programma è questo. Alcune cose non mi piacciono e vanno cambiate:

Codice: Seleziona tutto

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#define UNDER_STACK 1
#define SIZE_STACK  200

int  check           (char *token,int *push,int stak[], int *number);
int  push_stack      (int *number,int *push,int stack[]);
int  operand         (char *token,int *push,int stack[],int *number);
void somma           (int *push,int stack[]);
void sottrazione     (int *push,int stack[]);
void moltiplicazione (int *push,int stack[]);
void divisione       (int *push,int stack[]);


//---------------------------------------------------------------------
// test per vedere se il token è formato
// da soli numeri
//---------------------------------------------------------------------
int check( char *token,int *push,int stack[],int *number)
{
    char *p=token;

    while (*p!='\0')
    {
        if (isdigit(*p)==0 )
            return -1;
        p++;
    }  
       *number=atoi(token); // traformo la stringa in intero
        return 1;
}
       
//---------------------------------------------------------------------
// inserisco il numero nello stack
//---------------------------------------------------------------------
int push_stack(int *number,int *push,int stack[])
{
    
    if (*push<SIZE_STACK) //superfluo se non usi input 
    {                     //interattivi come fgets
        ++(*push);
        stack[(*push)]=(*number);
        return 1;
    }
    return -1;
}       
//---------------------------------------------------------------------
// controllo a quale operazione corrisponde
// il token e intraprendo operazione
// corrispondente
//---------------------------------------------------------------------
  int operand     ( char *token,int *push,int stack[],int *number)
{
    static char add[]="+";
    static char sot[]="-";
    static char mol[]="*";
    static char div[]="/";
    char *p=token;

    if ((strcmp (p,add)==0))
    {
        somma(push,stack);
        return 1;
    }

    if ((strcmp (p,sot)==0))
    {
        sottrazione(push,stack);
        return 1;
    }

    if ((strcmp (p,mol)==0))
    {
        moltiplicazione(push,stack);
        return 1;
    }

    if ((strcmp (p,div)==0))
    {
        divisione(push,stack);
        return 1;
    }

    return 0;
}
//---------------------------------------------------------------------
//      funzione somma
//---------------------------------------------------------------------
void somma(int *push,int stack[])
{
    if (*push>=UNDER_STACK)
    {
        stack[(*push-1)]=stack[(*push-1)]+stack[(*push)];
       --(*push);
    }

    else
    {
        puts("buffer underflow");
        exit(0);
    }

}
//---------------------------------------------------------------------
//      funzione sottrazione
//---------------------------------------------------------------------
void sottrazione (int *push,int stack[])
{
    if (*push>=UNDER_STACK)
    {
        stack[(*push-1)]=stack[(*push-1)]-stack[(*push)];
       --(*push);
    }

    else
    {
        puts("buffer underflow");
        exit(0);
    }
}

//---------------------------------------------------------------------
//      funzione moltiplicazione
//---------------------------------------------------------------------

void moltiplicazione (int *push,int stack[])
{
    if (*push>=UNDER_STACK)
    {
        stack[(*push-1)]=stack[(*push-1)]*stack[(*push)];
       --(*push);
    }

    else
    {
        puts("buffer underflow");
        exit(0);
    }
}
//---------------------------------------------------------------------
//      funzione divisione
//---------------------------------------------------------------------

void divisione (int *push,int stack[])
{
    if (*push>=UNDER_STACK)
    {
        stack[(*push-1)]=stack[(*push-1)]/stack[(*push)];
       --(*push);
    }

    else
    {
        puts("buffer underflow");
        exit(0);
    }
}

//---------------------------------------------------------------------




int main(void)
{
    char str[512]="5 8 * 4 9 - /";
    const char s[2] = " ";
    int stack[SIZE_STACK];
    char *token;
    int push=-1;
    int number=0;
   
    token = strtok(str, s);
    while( token != NULL ) 
    {
        if ( (check(token,&push,stack,&number))==1)// se test numero ok
        {  
           
            if ((push_stack(&number,&push,stack))!=1)// inserisci nello stack
            {
                puts("buffer overflow");
                exit (0);
            }        
        }
           
            else  // altrimenti potrebbe essere un operatore
            {
             
                if (operand(token,&push, stack,&number)!=1)
                {
                    puts ("scrittura errata\n");
                    return 0;
                }
            }  
        
        token = strtok(NULL, s);
        
    }
    if (push==0)
        printf("\nrisultato %d\n",stack[0]);
    else
        puts ("espressione malformata");
    
    return 0;
}
 

input:

Codice: Seleziona tutto

5 8 * 4 9 - /
output:

Codice: Seleziona tutto

risultato -8
input

Codice: Seleziona tutto

5a 10 +
output

Codice: Seleziona tutto

scrittura errata
input

Codice: Seleziona tutto

5 10 + + +
output:

Codice: Seleziona tutto

buffer underflow
ecc..
Non so se ho considerato tutto e non garantisco sia esente da bugs.
Più che altro mi domando come fare confronti nella stringa in modo elegante...cosa scrivereste voi.
Sarebbe tutto più semplice con scanf e man mano controllare e mettere nello stack, ma io voglio farlo su una stringa
Grazie a tutti e nel caso non ci si senta buon anno :)
Ultima modifica di gila75 il mercoledì 20 gennaio 2016, 6:48, modificato 2 volte in totale.
Avatar utente
vbextreme
Entusiasta Emergente
Entusiasta Emergente
Messaggi: 1214
Iscrizione: domenica 12 gennaio 2014, 14:06
Desktop: lxde
Distribuzione: xubuntu 14.10

Re: [C] Notazione polacca

Messaggio da vbextreme »

per il lexer devi fare una cosa del genere:

Codice: Seleziona tutto


#define skipspace(S) ({ while(*S && *S == ' ' && *S == '\t') ++S; })
....
gets(s,1234,f);

while(*s)
{
    skipspace(s);
    if ( !*s ) break;
    switch (*s)
    {
         case '+': 
             fai qualcosa;
         break;

         case '-': 
             fai qualcosa;
         break;

         case '0' ... '9': 
             è un numero
             usare strtol() perchè accetta un puntatore per la fine del numero, quindi diventa una cosa del genere:
             char* e=NULL;
             int num = strtol(s,&e,10);
             s = e; //salto i caratteri che ho gia analizzato
         break;
         
         default: errore carattere non riconosciuto break;
    }
}
Easy framework per il linguaggio C.
vbextreme hack your life
gila75
Imperturbabile Insigne
Imperturbabile Insigne
Messaggi: 2739
Iscrizione: mercoledì 16 gennaio 2013, 17:28
Desktop: ubuntu-2d
Distribuzione: Ubuntu 12.04.2 LTS i686
Località: Airuno(Lecco)

Re: [C] Notazione polacca

Messaggio da gila75 »

per il lexer devi fare una cosa del genere:
Scusa l'ignoranza Vb ma cosa s'intende per lexer?
Io ho optato per strtok() perchè mettendo come token lo spazio mi prende i vari blocchi e li analizzo.
In effetti avevo pensato anche io a strtol().. insomma sono solo alla prima bozza.
L'hai compilato e testato per caso?
Intanto grazie
sbam
Scoppiettante Seguace
Scoppiettante Seguace
Messaggi: 305
Iscrizione: giovedì 11 dicembre 2014, 20:31

Re: [C] Notazione polacca

Messaggio da sbam »

Non vi e' nessuna possibilita' che tra i numeri si trovi un operatore, giusto?
gila75
Imperturbabile Insigne
Imperturbabile Insigne
Messaggi: 2739
Iscrizione: mercoledì 16 gennaio 2013, 17:28
Desktop: ubuntu-2d
Distribuzione: Ubuntu 12.04.2 LTS i686
Località: Airuno(Lecco)

Re: [C] Notazione polacca

Messaggio da gila75 »

Nuova versione con float e potenza (non sono sicuro che si comporti nel modo esatto però).
Ho aggiunto un cast finale in modo tale da stampare 5 e non 5.0000 nel caso il risultato sia un intero

Codice: Seleziona tutto

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <math.h>

#define UNDER_STACK 1
#define SIZE_STACK  200

int  check           (char *token,int *push,float stack[], float *number);
int  push_stack      (float *number,int *push,float stack[]);
int  operand         (char *token,int *push,float stack[],float *number);
void _pow            (int *push,float stack[]);
void somma           (int *push,float stack[]);
void sottrazione     (int *push,float stack[]);
void moltiplicazione (int *push,float stack[]);
void divisione       (int *push,float stack[]);
void stampa          (float stack[],int *push);

//---------------------------------------------------------------------
// se push==0 lo stack è ok
// quindi stampo
// se risultato è intero, faccio un cast
// per avere 5 e non 5.000
//---------------------------------------------------------------------


void stampa          (float stack[],int *push)
{
    if (*push==0)
    {
        if( (stack[0]-(int)stack[0] )==0)
            printf("%d\n",(int) stack[0] );
        else
            printf ("%f\n", stack[0]);  
    }  
    else
        puts ("espressione malformata");
    
}
    
//---------------------------------------------------------------------
// test per vedere se il token è formato
// da soli numeri
//---------------------------------------------------------------------
int check( char *token,int *push,float stack[],float *number)
{
    char *p=token;

    while (*p!='\0')
    {
        if (isdigit(*p)==0 && *p!='.' )
            return -1;
        
        p++;
    }  
       *number=atof(token); // traformo la stringa in float
        return 1;
}
       
//---------------------------------------------------------------------
// inserisco il numero nello stack
//---------------------------------------------------------------------
int push_stack(float *number,int *push,float stack[])
{
    
    if (*push<SIZE_STACK) //superfluo se non usi input 
    {                     //interattivi come fgets
        ++(*push);
        stack[(*push)]=(*number);
        return 1;
    }
    return -1;
}       
//---------------------------------------------------------------------
// controllo a quale operazione corrisponde
// il token e intraprendo operazione
// corrispondente
//---------------------------------------------------------------------
  int operand     ( char *token,int *push,float stack[],float *number)
{
    static char add[]="+";
    static char sot[]="-";
    static char mol[]="*";
    static char div[]="/";
    static char pot[]="^";
    char *p=token;

    if ((strcmp (p,add)==0))
    {
        somma(push,stack);
        return 1;
    }

    if ((strcmp (p,sot)==0))
    {
        sottrazione(push,stack);
        return 1;
    }

    if ((strcmp (p,mol)==0))
    {
        moltiplicazione(push,stack);
        return 1;
    }

    if ((strcmp (p,div)==0))
    {
        divisione(push,stack);
        return 1;
    }

     if ((strcmp (p,pot)==0))
    {
        _pow(push,stack);
        return 1;
    }
    return 0;
}

//---------------------------------------------------------------------
//      funzione potenza
//---------------------------------------------------------------------
void _pow (int *push,float stack[])
{
    if (*push>=UNDER_STACK)
    {
        stack[(*push-1)]= pow( stack[(*push)], stack[(*push-1)] );
       --(*push);
    }

    else
    {
        puts("buffer underflow");
        exit(0);
    }

}



//---------------------------------------------------------------------
//      funzione somma
//---------------------------------------------------------------------
void somma(int *push,float stack[])
{
    if (*push>=UNDER_STACK)
    {
        stack[(*push-1)]=stack[(*push-1)]+stack[(*push)];
       --(*push);
    }

    else
    {
        puts("buffer underflow");
        exit(0);
    }

}
//---------------------------------------------------------------------
//      funzione sottrazione
//---------------------------------------------------------------------
void sottrazione (int *push,float stack[])
{
    if (*push>=UNDER_STACK)
    {
        stack[(*push-1)]=stack[(*push-1)]-stack[(*push)];
       --(*push);
    }

    else
    {
        puts("buffer underflow");
        exit(0);
    }
}

//---------------------------------------------------------------------
//      funzione moltiplicazione
//---------------------------------------------------------------------

void moltiplicazione (int *push,float stack[])
{
    if (*push>=UNDER_STACK)
    {
        stack[(*push-1)]=stack[(*push-1)]*stack[(*push)];
       --(*push);
    }

    else
    {
        puts("buffer underflow");
        exit(0);
    }
}
//---------------------------------------------------------------------
//      funzione divisione
//---------------------------------------------------------------------

void divisione (int *push,float stack[])
{
    if (*push>=UNDER_STACK)
    {
        stack[(*push-1)]=stack[(*push-1)]/stack[(*push)];
       --(*push);
    }

    else
    {
        puts("buffer underflow");
        exit(0);
    }
}

//---------------------------------------------------------------------




int main(void)
{
    char str[512]="0.3  0.3  +";
    const char s[2] = " ";
    float stack[SIZE_STACK];
    char *token;
    int push=-1;
    float number=0;
    printf ("risultato di %s =",str); //strtok è distruttivo quindi stampo prima
   
    token = strtok(str, s);
    while( token != NULL ) 
    {
        if ( (check(token,&push,stack,&number))==1)// se test numero ok
        {  
           
            if ((push_stack(&number,&push,stack))!=1)// inserisci nello stack
            {
                puts("buffer overflow");
                exit (0);
            }        
        }
           
            else  // altrimenti potrebbe essere un operatore
            {
             
                if (operand(token,&push, stack,&number)!=1)
                {
                    puts ("scrittura errata\n");
                    return 0;
                }
            }  
        
        token = strtok(NULL, s);
        
    }
    stampa(stack,&push);

    
    return 0;
}
  

Non vi e' nessuna possibilita' che tra i numeri si trovi un operatore, giusto?
in che senso ?

questo è lecito:
5 8 * 4 9 - /
Ma a dire il vero nel dettaglio non la conosco nemmeno io la notazione polacca :D
gila75
Imperturbabile Insigne
Imperturbabile Insigne
Messaggi: 2739
Iscrizione: mercoledì 16 gennaio 2013, 17:28
Desktop: ubuntu-2d
Distribuzione: Ubuntu 12.04.2 LTS i686
Località: Airuno(Lecco)

Re: [C] Notazione polacca

Messaggio da gila75 »

Adesso sto lavorando per eliminare la scrittura
3..2 2.5 +
cioè il doppio punto all'interno del token trovato.
Userei un flag...
più tardi posto
Avatar utente
SuperStep
Entusiasta Emergente
Entusiasta Emergente
Messaggi: 2037
Iscrizione: lunedì 19 dicembre 2011, 16:26
Desktop: Unity
Distribuzione: Ubuntu 16.04 LTS x86_64
Sesso: Maschile
Località: Somma Vesuviana (NA)

Re: [C] Notazione polacca

Messaggio da SuperStep »

nessun flag, controlla il successivo (o precedente).

[EDIT] adesso che ci penso, forse è meglio il flag... in un caso del tipo: 2.3.5 dove non sono adiacenti, la mia logica non funziona; la tua si.
ubuntu 16.04 LTS 64-bit - Memoria: 31,3 Gib - Processore: Intel Core i7-5960X CPU @ 3.00 GHz × 16 - Grafica: AMD Radeon HD 7800 Series - Disco: SSD 256 GB x 4 (RAID 01)
gila75
Imperturbabile Insigne
Imperturbabile Insigne
Messaggi: 2739
Iscrizione: mercoledì 16 gennaio 2013, 17:28
Desktop: ubuntu-2d
Distribuzione: Ubuntu 12.04.2 LTS i686
Località: Airuno(Lecco)

Re: [C] Notazione polacca

Messaggio da gila75 »

[EDIT] adesso che ci penso, forse è meglio il flag... in un caso del tipo: 2.3.5 dove non sono adiacenti, la mia logica non funziona; la tua si.
infatti
Elenco i casi non consentiti:
5a 3 +
5 3 ++
5..2 3 +
5 3
+ 5 3
e altri che ora non mi vengono in mente...
Insomma ci sono molti casi da gestire
edit versione che dovrebbe gestire anche i numeri negativi...che faticaccia districarsi con i vari casi che si danno noia a vicenda :muro:

Codice: Seleziona tutto

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <math.h>

#define UNDER_STACK 1
#define SIZE_STACK  200

int  check           (char *token,int *push,float stack[], float *number);
int  push_stack      (float *number,int *push,float stack[]);
int  operand         (char *token,int *push,float stack[],float *number);
void _pow            (int *push,float stack[]);
void somma           (int *push,float stack[]);
void sottrazione     (int *push,float stack[]);
void moltiplicazione (int *push,float stack[]);
void divisione       (int *push,float stack[]);
void stampa          (float stack[],int *push);

//---------------------------------------------------------------------
// se push==0 lo stack è ok
// quindi stampo
// se risultato è intero, faccio un cast
// per avere 5 e non 5.000
//---------------------------------------------------------------------


void stampa          (float stack[],int *push)
{
    if (*push==0)
    {
        if( (stack[0]-(int)stack[0] )==0)
            printf("%d\n",(int) stack[0] );
        else
            printf ("%f\n", stack[0]);  
    }  
    else
        puts ("espressione malformata");
    
}
    
//---------------------------------------------------------------------
// test per vedere se il token è formato
// da soli numeri
//---------------------------------------------------------------------
int check( char *token,int *push,float stack[],float *number)
{
    char *p=token;
    int flag=0; // doppi punti nel token
    int flag_2=0; // per numeri negativi
    
    while (*p!='\0')
    {
        if (isdigit(*p)==0 && ( (*p!='.') || (flag>0) ) )
        {   
            if (p[0]=='-' && p[1]!=' ' && (p[1])!='\0')
                flag_2=1;
            else return -1;
        }
            if (*p=='.')
                flag++;
            else
                flag=0;
          
        
        p++;
    }  
       *number=atof(token); // traformo la stringa in intero
        if (flag_2==1)
        {
            
            flag_2=0;
        }
        return 1;
}
       
//---------------------------------------------------------------------
// inserisco il numero nello stack
//---------------------------------------------------------------------
int push_stack(float *number,int *push,float stack[])
{
    
    if (*push<SIZE_STACK) //superfluo se non usi input 
    {                     //interattivi come fgets
        ++(*push);
        stack[(*push)]=(*number);
        return 1;
    }
    return -1;
}       
//---------------------------------------------------------------------
// controllo a quale operazione corrisponde
// il token e intraprendo operazione
// corrispondente
//---------------------------------------------------------------------
  int operand     ( char *token,int *push,float stack[],float *number)
{
    static char add[]="+";
    static char sot[]="-";
    static char mol[]="*";
    static char div[]="/";
    static char pot[]="^";
    char *p=token;

    if ((strcmp (p,add)==0))
    {
        somma(push,stack);
        return 1;
    }

    if ((strcmp (p,sot)==0))
    {
        sottrazione(push,stack);
        return 1;
    }

    if ((strcmp (p,mol)==0))
    {
        moltiplicazione(push,stack);
        return 1;
    }

    if ((strcmp (p,div)==0))
    {
        divisione(push,stack);
        return 1;
    }

     if ((strcmp (p,pot)==0))
    {
        _pow(push,stack);
        return 1;
    }
    return 0;
}

//---------------------------------------------------------------------
//      funzione potenza
//---------------------------------------------------------------------
void _pow (int *push,float stack[])
{
    if (*push>=UNDER_STACK)
    {
        stack[(*push-1)]= pow( stack[(*push)], stack[(*push-1)] );
       --(*push);
    }

    else
    {
        puts("buffer underflow");
        exit(0);
    }

}



//---------------------------------------------------------------------
//      funzione somma
//---------------------------------------------------------------------
void somma(int *push,float stack[])
{
    if (*push>=UNDER_STACK)
    {
        stack[(*push-1)]=stack[(*push-1)]+stack[(*push)];
       --(*push);
    }

    else
    {
        puts("buffer underflow");
        exit(0);
    }

}
//---------------------------------------------------------------------
//      funzione sottrazione
//---------------------------------------------------------------------
void sottrazione (int *push,float stack[])
{
    if (*push>=UNDER_STACK)
    {
        stack[(*push-1)]=stack[(*push-1)]-stack[(*push)];
       --(*push);
    }

    else
    {
        puts("buffer underflow");
        exit(0);
    }
}

//---------------------------------------------------------------------
//      funzione moltiplicazione
//---------------------------------------------------------------------

void moltiplicazione (int *push,float stack[])
{
    if (*push>=UNDER_STACK)
    {
        stack[(*push-1)]=stack[(*push-1)]*stack[(*push)];
       --(*push);
    }

    else
    {
        puts("buffer underflow");
        exit(0);
    }
}
//---------------------------------------------------------------------
//      funzione divisione
//---------------------------------------------------------------------

void divisione (int *push,float stack[])
{
    if (*push>=UNDER_STACK)
    {
        stack[(*push-1)]=stack[(*push-1)]/stack[(*push)];
       --(*push);
    }

    else
    {
        puts("buffer underflow");
        exit(0);
    }
}

//---------------------------------------------------------------------




int main(void)
{
    char str[512]="-2.5 -3.5 -  ";
    const char s[2] = " ";
    float stack[SIZE_STACK];
    char *token;
    int push=-1;
    float number=0;
    printf ("risultato di %s =",str);
   
    token = strtok(str, s);
    while( token != NULL ) 
    {
        if ( (check(token,&push,stack,&number))==1)// se test numero ok
        {  
           
            if ((push_stack(&number,&push,stack))!=1)// inserisci nello stack
            {
                puts("buffer overflow");
                exit (0);
            }        
        }
           
            else  // altrimenti potrebbe essere un operatore
            {
             
                if (operand(token,&push, stack,&number)!=1)
                {
                    puts ("scrittura errata\n");
                    return 0;
                }
            }  
        
        token = strtok(NULL, s);
        
    }
    stampa(stack,&push);

    
    return 0;
}
 

sbam
Scoppiettante Seguace
Scoppiettante Seguace
Messaggi: 305
Iscrizione: giovedì 11 dicembre 2014, 20:31

Re: [C] Notazione polacca

Messaggio da sbam »

gila75 [url=http://forum.ubuntu-it.org/viewtopic.php?p=4838085#p4838085][img]http://forum.ubuntu-it.org/images/icons/icona-cita.gif[/img][/url] ha scritto:...
Non vi e' nessuna possibilita' che tra i numeri si trovi un operatore, giusto?
in che senso ?

questo è lecito:
5 8 * 4 9 - /
Ma a dire il vero nel dettaglio non la conosco nemmeno io la notazione polacca :D
Ah, io pensavo non potesse essere cosi', comunque buon anno :D
gila75
Imperturbabile Insigne
Imperturbabile Insigne
Messaggi: 2739
Iscrizione: mercoledì 16 gennaio 2013, 17:28
Desktop: ubuntu-2d
Distribuzione: Ubuntu 12.04.2 LTS i686
Località: Airuno(Lecco)

Re: [C] Notazione polacca

Messaggio da gila75 »

Buon anno anche a te e a tutto il forum :)
Avatar utente
SuperStep
Entusiasta Emergente
Entusiasta Emergente
Messaggi: 2037
Iscrizione: lunedì 19 dicembre 2011, 16:26
Desktop: Unity
Distribuzione: Ubuntu 16.04 LTS x86_64
Sesso: Maschile
Località: Somma Vesuviana (NA)

Re: [C] Notazione polacca

Messaggio da SuperStep »

Appena mi avanzano 3/4 ore ti buttò le basi per un parser fatto bene
ubuntu 16.04 LTS 64-bit - Memoria: 31,3 Gib - Processore: Intel Core i7-5960X CPU @ 3.00 GHz × 16 - Grafica: AMD Radeon HD 7800 Series - Disco: SSD 256 GB x 4 (RAID 01)
gila75
Imperturbabile Insigne
Imperturbabile Insigne
Messaggi: 2739
Iscrizione: mercoledì 16 gennaio 2013, 17:28
Desktop: ubuntu-2d
Distribuzione: Ubuntu 12.04.2 LTS i686
Località: Airuno(Lecco)

Re: [C] Notazione polacca

Messaggio da gila75 »

Se ti va, più che volentieri :)
é che qui i casi sono davvero tanti come ho detto. Cerco di rielencarli

*gli spazi anche se tanti tra i token devono essere considerati uno solo
* i doppi operatori non sono ammessi: 5 ++ 10 +
* gli operatori che vanno in negativo dello stack non sono ammessi : 5 3 ++
*i punti doppi non sono ammessi 5..3 3.2 + , ma il punto singolo si ma solo nel toke,
al di fuori no : 5.3 3.5 . +
*gli operatori nel token non sono ammessi :5+ 10 +
*le lettere o i caratteri non numeri esclusi gli operatori e il punto non sono ammessi : 5a 3.5 +
* il meno è ammesso come operatore e come numero negativo, ma solo prima del numero:
-5 3 + ammesso
--5 3 + non ammesso
5- 3 + non ammesso
quindi bisogna istruire: se leggi il meno nel token, solo se è in prima posizione ok è un numero negativo.
Ma poi potrebbe leggere il meno come operatore che a sua volta è il primo del token, ma è un operatore, non un segno...
quindi gestire la cosa in modo diverso.
L'ultima versione sembra funzionare con i numeri negativi e i float. Va compilata co -lm per via dell'header math.
Non ho ancora testato a fondo i vari bugs, visto che le cobinazioni sono molteplici.
Se ti\vi va provate a compilare e testare.
Per ora non ho inserito l'input da terminale con fgets() scrivo la formula e compilo...semmai quello è il meno
Penso che quando le ricerche sono soggette a molte variabili (cerca il - ma se dopo c'è... e invece prima... ma se isolato fai altro)
siano un po' inevitabili gli if...a meno che sei davvero bravo :D
Ho pronta la versione definitiva, mi piacerebbe la provaste per eventuali bugs, la sistemo e dopo la posto
Avatar utente
vbextreme
Entusiasta Emergente
Entusiasta Emergente
Messaggi: 1214
Iscrizione: domenica 12 gennaio 2014, 14:06
Desktop: lxde
Distribuzione: xubuntu 14.10

Re: [C] Notazione polacca

Messaggio da vbextreme »

Come già detto serve un lexer per analizzare la stringa e creare i token.
Poi serve un parser che analizzi ciò che ha estratto il lexer.
Infine serve la vera calcolatrice.

Scritto in un'ora e velocemente (quindi con probabili bug) si ha

Codice: Seleziona tutto

#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdio_ext.h>
#include <time.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <unistd.h>
#include <errno.h>
#include <stdbool.h>

#define _UNUSED_ __attribute__((unused))

#define NEW(T) (T*)malloc(sizeof(T))

#define TKDEL "+-*/! \t\n"
#define skipspace(S) do{ while(*S && (*S == ' ' || *S == '\t' || *S == '\n') ) ++S; }while(0)


typedef enum { TK_NP, TK_DEC, TK_DBL, TK_HEX, TK_OP, TK_STR, TK_ERR, TK_COUNT } token_t;

struct lex
{
	token_t type;
	char* s;
	char* e;
	struct lex* next;
};

void lex_free(struct lex* lx)
{
	struct lex* n;
	while ( lx )
	{
		n = lx->next;
		free(lx);
		lx = n;
	}
}

void lex_add(struct lex* f, struct lex* n)
{
	if ( !f ) return;
	for(; f->next; f = f->next);
	f->next = n;
	n->next = NULL;
}

int lex_gettoken(char** d, char** e, char* s)
{
	skipspace(s);
	if ( !*s ) return -1;
	
	*d = s;
	*e = strpbrk(s, TKDEL);
	if ( *e == *d ) ++(*e);
	return 0;
}

struct lex* lex_tokenize(char* l)
{
	char* s = NULL;
	char* e = NULL;
	
	struct lex* f = NULL;
	
	s = l;
	while( !lex_gettoken(&s,&e,s) )
	{	
		struct lex* lx = NEW(struct lex);
		lx->s = s;
		lx->e = e;
		lx->type = TK_NP;
		lx->next = NULL;
		
		if ( !f )
			f = lx;
		else
			lex_add(f, lx);
			
		s = e;
	}
	
	return f;
}

int lex_isDEC(char* s, char* e)
{
	for(; s < e; ++s )
		if ( *s < '0' || *s > '9' ) return 0;
	return 1;
}

int lex_isDBL(char* s, char* e)
{
	int p = 0;
	for(; s < e; ++s )
	{
		if ( (*s < '0' || *s > '9') )
		{
			if ( p ) return 0;
			if ( *s == '.' ) 
				p = 1;
			else
				return 0;
		}
	}
	return 1;
}

int lex_isHEX(char* s, char* e)
{
	if ( *s++ != '0' ) return 0;
	if ( *s++ != 'x' ) return 0;
	for(; s < e; ++s )
		if ( (*s < '0' || *s > '9') && ( tolower(*s) < 'a' || tolower(*s) > 'f' ) ) return 0;
	return 1;
}

int lex_isOP(char* s, char* e _UNUSED_)
{
	switch ( *s )
	{
		case '+': case '-': case '*': case '/': case '!':
		return 1;
		
		default: return 0;
	}
	return 0;
}

int lex_isSTR(char* s, char* e)
{
	for(; s < e; ++s )
		if ( tolower(*s) < 'a' || tolower(*s) > 'z' ) return 0;
	return 1;	
}


int lex_tokentype(struct lex* lx)
{
	for(; lx; lx = lx->next )
	{
		if ( lx->type != TK_NP ) continue;
		
		if ( lex_isDEC(lx->s,lx->e) )
			lx->type = TK_DEC;
		else if ( lex_isDBL(lx->s,lx->e) )
			lx->type = TK_DBL;
		else if ( lex_isHEX(lx->s,lx->e) )
			lx->type = TK_HEX;
		else if ( lex_isOP(lx->s,lx->e) )
			lx->type = TK_OP;
		else if ( lex_isSTR(lx->s,lx->e) )
			lx->type = TK_STR;
		else
			lx->type = TK_ERR;
	}
	
	return 0;
}

void _err(char* d, char* s, char* e) 
{ 
	if ( s )
	{
		char* rn = s;
		while ( *rn ) 
		{
			if (*rn == '\n' ) *rn = '\0';
			++rn;
		}
	
		if ( e-s )
			fprintf(stderr,"error: '%s'\non '%.*s'\n", d,e-s,s); 
		else
			fprintf(stderr,"error: '%s'\non '%c'\n", d,*s); 
	}
	else
	{
		fprintf(stderr,"error: '%s'\n", d); 
	}
}

struct lex* lex_analyze(char* l)
{
	struct lex* lx = lex_tokenize(l);
	if ( !lx ) 
	{
		_err("no token in line",l, l+strlen(l));
		return NULL;
	}
		
	if ( lex_tokentype(lx) ) 
	{
		_err("on tokenized",l, l+strlen(l));
		return NULL;
    }
		
	return lx;
}

struct icalc
{
	double n;
	int op;
};

unsigned int parse(struct lex* lx, struct icalc* clc, unsigned int imax, unsigned int stksz)
{
	unsigned int i = 0;
	unsigned int ops = 0;
	
	for(; lx; lx = lx->next)
	{
		switch ( lx->type )
		{
			case TK_NP:
				_err("undefined symbol",lx->s,lx->e);
			return 0;
			
			case TK_DEC:
				++ops;
				clc[i].n = (double)strtol(lx->s, NULL, 10);
				clc[i].op = 0;
			break;
			
			case TK_DBL:
				++ops;
				clc[i].n = strtod(lx->s, NULL);
				clc[i].op = 0;
			break;
			
			case TK_HEX:
				++ops;
				clc[i].n = (double)strtol(lx->s+2, NULL, 16);
				clc[i].op = 0;
			break;
			
			case TK_OP:
				clc[i].n = 0.0;
				clc[i].op = *lx->s;
				if ( ops == 0 )
				{
					_err("no numbers for operator",lx->s,lx->e);
					return 0;
				}
				
				if ( clc[i].op != '!')
				{
					if ( ops < 2 )
					{
						_err("no numbers for operator",lx->s,lx->e);
						return 0;
					}
					--ops;
				}
			break;
			
			case TK_STR:
				_err("undefined operator string",lx->s,lx->e);
			return 0;
			
			default: case TK_ERR:
				_err("undefined",lx->s,lx->e);
			return 0;
		}
		++i;
		if ( i >= imax )
		{
			_err("parse calculator full",lx->s,lx->e);
			return 0;
		}
		
		if ( ops >= stksz )
		{
			_err("stack overflow",lx->s,lx->e);
			return 0;
		}
	}
	
	if ( ops != 1 ) 
	{
		_err("multi data on stack",NULL,NULL);
		return 0;
	}
	
	return i;
}


char* tkn(token_t t)
{
	static char* n[] = { "undefined","decimal","double","hexadecimal","operator","string","error","outoftype"};
	
	if ( t < 0 || t >= TK_COUNT )
		return n[TK_COUNT];
	
	return n[t];
}

#define STKSZ 100

double calculator(struct icalc* clc, unsigned int n)
{
	double stk[STKSZ];
	unsigned int ik = 0;
	
	unsigned int i;
	double a,b;
	
	for ( i = 0; i < n; ++i)
	{
		if ( 0 == clc[i].op )
		{
			stk[ik++] = clc[i].n;
			continue;
		}
		
		switch ( clc[i].op )
		{
			case '+':
		
				a = stk[--ik];
				b = stk[--ik];
				a += b;
				stk[ik++] = a;
			break;
			
			case '-':
				a = stk[--ik];
				b = stk[--ik];
				a -= b;
				stk[ik++] = a;
			break;
			
			case '*':
				a = stk[--ik];
				b = stk[--ik];
				a *= b;
				stk[ik++] = a;
			break;
			
			case '/':
				a = stk[--ik];
				b = stk[--ik];
				a -= b;
				stk[ik++] = a;
			break;
			
			case '!':
				a = stk[--ik];
				a = !a;
				stk[ik++] = a;
			break;
		}	
	}
	
	return stk[--ik];
}

int main() 
{
    char line[100];
    
    while ( 1 )
    {
		fputs("calcolatrice$ ",stdout);
		fflush(stdout);
		if ( !fgets(line,100,stdin) ) break;
		if ( line[0] == '\n' ) break;
		
		struct lex* lx = lex_analyze(line);
		struct lex* dbg;
		
		puts("lexer:");
		for ( dbg = lx; dbg; dbg = dbg->next )
		{
			if ( dbg->e - dbg->s )
				printf("<'%.*s'::%s>",dbg->e-dbg->s,dbg->s,tkn(dbg->type));
		}
		putchar('\n');
		putchar('\n');
		
		puts("parser:");
		struct icalc clc[200];
		unsigned n = parse(lx,clc,200,STKSZ);
		if ( n == 0 )
		{
			lex_free(lx);
			continue;
		}
		puts("ok\n");
		
		printf("result:%f\n",calculator(clc,n));
		
		lex_free(lx);
    }
    
    return 0;
}

Easy framework per il linguaggio C.
vbextreme hack your life
Avatar utente
Claudio_F
Entusiasta Emergente
Entusiasta Emergente
Messaggi: 1463
Iscrizione: lunedì 28 maggio 2012, 18:49
Desktop: Mate/Gnome
Distribuzione: Ubu22.04

Re: [C] Notazione polacca

Messaggio da Claudio_F »

gila75 ha scritto:Penso che quando le ricerche sono soggette a molte variabili (cerca il - ma se dopo c'è... e invece prima... ma se isolato fai altro) siano un po' inevitabili gli if
Non è tanto importante focalizzarsi e considerare tutti i possibili casi errati (sempre molto maggiori dei casi corretti e qualcosa sfugge sempre), quanto piuttosto stabilire una grammatica iniziale non ambigua (magari descrivendola con una BNF... i flowchart delle grammatiche) e accettare in modo draconiano solo una stringa compatibile al 100% con quella grammatica, *tutti* i casi "errati" (quantunque essi siano) vengono automaticamente rilevati.

Ora... come scriviamo in modo non ambiguo la seconda RPN contenente più e meno unari?

Codice: Seleziona tutto

10 20 30 40 + - +
10 +20 -30 -40 + - +
gila75
Imperturbabile Insigne
Imperturbabile Insigne
Messaggi: 2739
Iscrizione: mercoledì 16 gennaio 2013, 17:28
Desktop: ubuntu-2d
Distribuzione: Ubuntu 12.04.2 LTS i686
Località: Airuno(Lecco)

Re: [C] Notazione polacca

Messaggio da gila75 »

Scritto in un'ora e velocemente (quindi con probabili bug) si ha
Bhè in un'ora già la dice lunga sul tuo curriculm :D
Comunque per esempio
5 8 * 4 9 - /
deve dare -8
ma a te da:

Codice: Seleziona tutto

calcolatrice$ 5 8 * 4 9 - /
lexer:
<'5'::decimal><'8'::decimal><'*'::operator><'4'::decimal><'9'::decimal><'-'::operator><'/'::operator>

parser:
ok

result:-35.000000
deve dare -8
inoltre non mi sembra che gestisci i numeri negativi.
Per il resto mi sembra vada tutto bene...ora lo guardo.
AZZ vb un'ora!!! Mannaggia a te :D

EDIT:
Ora... come scriviamo in modo non ambiguo la seconda RPN contenente più e meno unari?
Codice: Seleziona tutto
10 20 30 40 + - +
10 +20 -30 -40 + - +
non ho capito cosa intendi
Qui posto la mia versione "definitiva"

Codice: Seleziona tutto

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <math.h>

#define UNDER_STACK 1
#define SIZE_STACK  200

int  check           (char *token,int *push,float stack[], float *number);
int  push_stack      (float *number,int *push,float stack[]);
int  operand         (char *token,int *push,float stack[],float *number);
void _pow            (int *push,float stack[]);
void somma           (int *push,float stack[]);
void sottrazione     (int *push,float stack[]);
void moltiplicazione (int *push,float stack[]);
void divisione       (int *push,float stack[]);
void stampa          (float stack[],int *push);
void input_string    (char str[]);

//---------------------------------------------------------------------
//      input stringa
//---------------------------------------------------------------------
void input_string    (char str[])
{
    int len_str;
    printf("input esperssione: ");
    if ( (fgets(str,SIZE_STACK,stdin) ) !=NULL)
    {
        
        len_str=strlen(str);
        str[len_str-1]='\0';
    }
    else
    {
        puts("ritornato NULL, esco");
        exit(0);
    }
}
        
//---------------------------------------------------------------------
// se push==0 lo stack è ok
// quindi stampo
// se risultato è intero, faccio un cast
// per avere 5 e non 5.000
//---------------------------------------------------------------------

void stampa          (float stack[],int *push)
{
    if (*push==0)
    {
        if( (stack[0]-(int)stack[0] )==0)
            printf("%d\n",(int) stack[0] );
        else
            printf ("%f\n", stack[0]);  
    }  
    else
        puts ("espressione malformata");
    
}
    
//---------------------------------------------------------------------
// test per vedere se il token è formato
// da soli numeri
//---------------------------------------------------------------------
int check( char *token,int *push,float stack[],float *number)
{
    char *p=token;
    unsigned char flag=0; // doppi punti nel token
    unsigned char flag_2=0; // per numeri negativi
    
    while (*p!='\0')
    {
        if (isdigit(*p)==0 && ( (*p!='.') || (flag>0) ) )
        {   
            if (p[0]=='-' && p[1]!=' ' && (p[1])!='\0' && p[1]!='-')
                flag_2=1;
            else return -1;
        }
            if (*p=='.')
                flag++;
            else; // qui è da corregere forse
               // flag=0;
          
        
        p++;
    }  
       *number=atof(token); // traformo la stringa in float
        if (flag_2==1)
        {
            
            flag_2=0;
        }
        return 1;
}
       
//---------------------------------------------------------------------
// inserisco il numero nello stack
//---------------------------------------------------------------------
int push_stack(float *number,int *push,float stack[])
{
    
    if (*push<SIZE_STACK) //superfluo se non usi input 
    {                     //interattivi come fgets
        ++(*push);
        stack[(*push)]=(*number);
        return 1;
    }
    return -1;
}       
//---------------------------------------------------------------------
// controllo a quale operazione corrisponde
// il token e intraprendo operazione
// corrispondente
//---------------------------------------------------------------------
  int operand     ( char *token,int *push,float stack[],float *number)
{
    static char add[]="+";
    static char sot[]="-";
    static char mol[]="*";
    static char div[]="/";
    static char pot[]="^";
    char *p=token;

    if ((strcmp (p,add)==0))
    {
        somma(push,stack);
        return 1;
    }

    if ((strcmp (p,sot)==0))
    {
        sottrazione(push,stack);
        return 1;
    }

    if ((strcmp (p,mol)==0))
    {
        moltiplicazione(push,stack);
        return 1;
    }

    if ((strcmp (p,div)==0))
    {
        divisione(push,stack);
        return 1;
    }

     if ((strcmp (p,pot)==0))
    {
        _pow(push,stack);
        return 1;
    }
    return 0;
}

//---------------------------------------------------------------------
//      funzione potenza
//---------------------------------------------------------------------
void _pow (int *push,float stack[])
{
    if (*push>=UNDER_STACK)
    {
        stack[(*push-1)]= pow( stack[(*push)], stack[(*push-1)] );
       --(*push);
    }

    else
    {
        puts("buffer underflow");
        exit(0);
    }

}



//---------------------------------------------------------------------
//      funzione somma
//---------------------------------------------------------------------
void somma(int *push,float stack[])
{
    if (*push>=UNDER_STACK)
    {
        stack[(*push-1)]=stack[(*push-1)]+stack[(*push)];
       --(*push);
    }

    else
    {
        puts("buffer underflow");
        exit(0);
    }

}
//---------------------------------------------------------------------
//      funzione sottrazione
//---------------------------------------------------------------------
void sottrazione (int *push,float stack[])
{
    if (*push>=UNDER_STACK)
    {
        stack[(*push-1)]=stack[(*push-1)]-stack[(*push)];
       --(*push);
    }

    else
    {
        puts("buffer underflow");
        exit(0);
    }
}

//---------------------------------------------------------------------
//      funzione moltiplicazione
//---------------------------------------------------------------------

void moltiplicazione (int *push,float stack[])
{
    if (*push>=UNDER_STACK)
    {
        stack[(*push-1)]=stack[(*push-1)]*stack[(*push)];
       --(*push);
    }

    else
    {
        puts("buffer underflow");
        exit(0);
    }
}
//---------------------------------------------------------------------
//      funzione divisione
//---------------------------------------------------------------------

void divisione (int *push,float stack[])
{
    if (*push>=UNDER_STACK)
    {
        stack[(*push-1)]=stack[(*push-1)]/stack[(*push)];
       --(*push);
    }

    else
    {
        puts("buffer underflow");
        exit(0);
    }
}

//---------------------------------------------------------------------




int main(void)
{
    char str[SIZE_STACK];
    const char s[2] = " ";
    float stack[SIZE_STACK]={0.0f};
    char *token;
    int push=-1;
    float number=0;
    input_string(str);
    printf ("risultato di %s =",str);
   
    token = strtok(str, s);
    while( token != NULL ) 
    {
        if ( (check(token,&push,stack,&number))==1)// se test numero ok
        {  
           
            if ((push_stack(&number,&push,stack))!=1)// inserisci nello stack
            {
                puts("buffer overflow");
                exit (0);
            }        
        }
           
            else  // altrimenti potrebbe essere un operatore
            {
             
                if (operand(token,&push, stack,&number)!=1)
                {
                    puts ("scrittura errata\n");
                    return 0;
                }
            }  
        
        token = strtok(NULL, s);
        
    }
    stampa(stack,&push);

    
    return 0;
}
  



Cerco di gestire tutti i casi con numeri negativi e floats e evitare cose tipo : --5 3 + ecc...
insomma provate e nel caso mi comunicate i bugs
Avatar utente
vbextreme
Entusiasta Emergente
Entusiasta Emergente
Messaggi: 1214
Iscrizione: domenica 12 gennaio 2014, 14:06
Desktop: lxde
Distribuzione: xubuntu 14.10

Re: [C] Notazione polacca

Messaggio da vbextreme »

urca! mi sono scordato i numeri negativi!
per il risultato sbagliato penso sia dovuto ad una errata gestione dello stack, forse basta invertire il prelevamento dei nuneri
modifica in calculator tutto questo

Codice: Seleziona tutto

a = stk[--ik];
  b = stk[--ik];
così

Codice: Seleziona tutto

b = stk[--ik];
a = stk[--ik];
Easy framework per il linguaggio C.
vbextreme hack your life
Avatar utente
vbextreme
Entusiasta Emergente
Entusiasta Emergente
Messaggi: 1214
Iscrizione: domenica 12 gennaio 2014, 14:06
Desktop: lxde
Distribuzione: xubuntu 14.10

Re: [C] Notazione polacca

Messaggio da vbextreme »

effettivamente c'erano 2/3 bug il piu bello era la divisione che invece faceva la sottrazione, in piu stack invertito.
ora dovrebbe andare anche con i negativi.

Codice: Seleziona tutto

#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdio_ext.h>
#include <time.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <unistd.h>
#include <errno.h>
#include <stdbool.h>

#define _UNUSED_ __attribute__((unused))

#define NEW(T) (T*)malloc(sizeof(T))

#define TKDEL "+-*/! \t\n"
#define skipspace(S) do{ while(*S && (*S == ' ' || *S == '\t' || *S == '\n') ) ++S; }while(0)


typedef enum { TK_NP, TK_DEC, TK_DBL, TK_HEX, TK_OP, TK_STR, TK_ERR, TK_COUNT } token_t;

struct lex
{
	token_t type;
	char* s;
	char* e;
	struct lex* next;
};

void lex_free(struct lex* lx)
{
	struct lex* n;
	while ( lx )
	{
		n = lx->next;
		free(lx);
		lx = n;
	}
}

void lex_add(struct lex* f, struct lex* n)
{
	if ( !f ) return;
	for(; f->next; f = f->next);
	f->next = n;
	n->next = NULL;
}

int lex_gettoken(char** d, char** e, char* s)
{
	skipspace(s);
	if ( !*s ) return -1;
	
	*d = s;
	*e = strpbrk(++s, TKDEL);
    
	if ( *e == *d ) ++(*e);
	return 0;
}

struct lex* lex_tokenize(char* l)
{
	char* s = NULL;
	char* e = NULL;
	
	struct lex* f = NULL;
	
	s = l;
	while( !lex_gettoken(&s,&e,s) )
	{	
		struct lex* lx = NEW(struct lex);
		lx->s = s;
		lx->e = e;
		lx->type = TK_NP;
		lx->next = NULL;
		
		if ( !f )
			f = lx;
		else
			lex_add(f, lx);
			
		s = e;
	}
	
	return f;
}

int lex_isDEC(char* s, char* e)
{
    if (*s == '-' ) ++s;
    if ( *s < '0' || *s > '9' ) return 0;
        
	for(; s < e; ++s )
		if ( *s < '0' || *s > '9' ) return 0;
	return 1;
}

int lex_isDBL(char* s, char* e)
{
	int p = 0;
	for(; s < e; ++s )
	{
		if ( (*s < '0' || *s > '9') )
		{
			if ( p ) return 0;
			if ( *s == '.' ) 
				p = 1;
			else
				return 0;
		}
	}
	return 1;
}

int lex_isHEX(char* s, char* e)
{
	if ( *s++ != '0' ) return 0;
	if ( *s++ != 'x' ) return 0;
	for(; s < e; ++s )
		if ( (*s < '0' || *s > '9') && ( tolower(*s) < 'a' || tolower(*s) > 'f' ) ) return 0;
	return 1;
}

int lex_isOP(char* s, char* e _UNUSED_)
{
	switch ( *s )
	{
		case '+': case '-': case '*': case '/': case '!':
		return 1;
		
		default: return 0;
	}
	return 0;
}

int lex_isSTR(char* s, char* e)
{
	for(; s < e; ++s )
		if ( tolower(*s) < 'a' || tolower(*s) > 'z' ) return 0;
	return 1;	
}


int lex_tokentype(struct lex* lx)
{
	for(; lx; lx = lx->next )
	{
		if ( lx->type != TK_NP ) continue;
		
		if ( lex_isDEC(lx->s,lx->e) )
			lx->type = TK_DEC;
		else if ( lex_isDBL(lx->s,lx->e) )
			lx->type = TK_DBL;
		else if ( lex_isHEX(lx->s,lx->e) )
			lx->type = TK_HEX;
		else if ( lex_isOP(lx->s,lx->e) )
			lx->type = TK_OP;
		else if ( lex_isSTR(lx->s,lx->e) )
			lx->type = TK_STR;
		else
			lx->type = TK_ERR;
	}
	
	return 0;
}

void _err(char* d, char* s, char* e) 
{ 
	if ( s )
	{
		char* rn = s;
		while ( *rn ) 
		{
			if (*rn == '\n' ) *rn = '\0';
			++rn;
		}
	
		if ( e-s )
			fprintf(stderr,"error: '%s'\non '%.*s'\n", d,e-s,s); 
		else
			fprintf(stderr,"error: '%s'\non '%c'\n", d,*s); 
	}
	else
	{
		fprintf(stderr,"error: '%s'\n", d); 
	}
}

struct lex* lex_analyze(char* l)
{
	struct lex* lx = lex_tokenize(l);
	if ( !lx ) 
	{
		_err("no token in line",l, l+strlen(l));
		return NULL;
	}
		
	if ( lex_tokentype(lx) ) 
	{
		_err("on tokenized",l, l+strlen(l));
		return NULL;
    }
		
	return lx;
}

struct icalc
{
	double n;
	int op;
};

unsigned int parse(struct lex* lx, struct icalc* clc, unsigned int imax, unsigned int stksz)
{
	unsigned int i = 0;
	unsigned int ops = 0;
	
	for(; lx; lx = lx->next)
	{
		switch ( lx->type )
		{
			case TK_NP:
				_err("undefined symbol",lx->s,lx->e);
			return 0;
			
			case TK_DEC:
				++ops;
				clc[i].n = (double)strtol(lx->s, NULL, 10);
				clc[i].op = 0;
			break;
			
			case TK_DBL:
				++ops;
				clc[i].n = strtod(lx->s, NULL);
				clc[i].op = 0;
			break;
			
			case TK_HEX:
				++ops;
				clc[i].n = (double)strtol(lx->s+2, NULL, 16);
				clc[i].op = 0;
			break;
			
			case TK_OP:
				clc[i].n = 0.0;
				clc[i].op = *lx->s;
				if ( ops == 0 )
				{
					_err("no numbers for operator",lx->s,lx->e);
					return 0;
				}
				
				if ( clc[i].op != '!')
				{
					if ( ops < 2 )
					{
						_err("no numbers for operator",lx->s,lx->e);
						return 0;
					}
					--ops;
				}
			break;
			
			case TK_STR:
				_err("undefined operator string",lx->s,lx->e);
			return 0;
			
			default: case TK_ERR:
				_err("undefined",lx->s,lx->e);
			return 0;
		}
		++i;
		if ( i >= imax )
		{
			_err("parse calculator full",lx->s,lx->e);
			return 0;
		}
		
		if ( ops >= stksz )
		{
			_err("stack overflow",lx->s,lx->e);
			return 0;
		}
	}
	
	if ( ops != 1 ) 
	{
		_err("multi data on stack",NULL,NULL);
		return 0;
	}
	
	return i;
}


char* tkn(token_t t)
{
	static char* n[] = { "undefined","decimal","double","hexadecimal","operator","string","error","outoftype"};
	
	if ( t < 0 || t >= TK_COUNT )
		return n[TK_COUNT];
	
	return n[t];
}

#define STKSZ 100

double calculator(struct icalc* clc, unsigned int n)
{
	double stk[STKSZ];
	unsigned int ik = 0;
	
	unsigned int i;
	double a,b;
	
	for ( i = 0; i < n; ++i)
	{
		if ( 0 == clc[i].op )
		{
			stk[ik++] = clc[i].n;
			continue;
		}
		
		switch ( clc[i].op )
		{
			case '+':
                b = stk[--ik];
				a = stk[--ik];
				a += b;
				stk[ik++] = a;
			break;
			
			case '-':
				b = stk[--ik];
				a = stk[--ik];
				a -= b;
				stk[ik++] = a;
			break;
			
			case '*':
				a = stk[--ik];
				b = stk[--ik];
				a *= b;
				stk[ik++] = a;
			break;
			
			case '/':
				b = stk[--ik];
				a = stk[--ik];
				a /= b;
				stk[ik++] = a;
			break;
			
			case '!':
				a = stk[--ik];
				a = !a;
				stk[ik++] = a;
			break;
		}	
	}
	
	return stk[--ik];
}

int main() 
{
    char line[100];
    
    while ( 1 )
    {
		fputs("calcolatrice$ ",stdout);
		fflush(stdout);
		if ( !fgets(line,100,stdin) ) break;
		if ( line[0] == '\n' ) break;
		
		struct lex* lx = lex_analyze(line);
		struct lex* dbg;
		
		puts("lexer:");
		for ( dbg = lx; dbg; dbg = dbg->next )
		{
			if ( dbg->e - dbg->s )
				printf("<'%.*s'::%s>",dbg->e-dbg->s,dbg->s,tkn(dbg->type));
		}
		putchar('\n');
		putchar('\n');
		
		puts("parser:");
		struct icalc clc[200];
		unsigned n = parse(lx,clc,200,STKSZ);
		if ( n == 0 )
		{
			lex_free(lx);
			continue;
		}
		puts("ok\n");
		
		printf("result:%f\n\n",calculator(clc,n));
		
		lex_free(lx);
    }
    
    return 0;
}

Easy framework per il linguaggio C.
vbextreme hack your life
Avatar utente
M_A_W_ 1968
Scoppiettante Seguace
Scoppiettante Seguace
Messaggi: 856
Iscrizione: venerdì 15 febbraio 2013, 3:57
Desktop: KDE
Distribuzione: SuSE
Sesso: Maschile
Località: Un luogo geometrico
Contatti:

Re: [C] Notazione polacca

Messaggio da M_A_W_ 1968 »

Aurugi a tutti, ragassuoli.

Come prima cosa è obbligatorio ricordare che la RPN è un'altra invenzione del geniale (e impronunciabile) filosofo e logico polacco Jan Łukasiewicz, padre della logica a 3 (e più) valori, peraltro fondamentale nell'implementazione dei circuiti logici, non meno dei lavori del più noto Claude Shannon.

La seconda nota è che procedendo per casi c'è da farsi del male, la varietà degli errori possibili è enorme. Qui occorre un automa a stati, oppure si usa un vero parser. Come proponimento per il nuovo anno, i più temerari tra voi dovrebbero sfruttare l'occasione per familiarizzare con yacc e bison. Ci sono anche appositi esempi che circolano da varie ere geologiche. :D

Buon divertimento!
Sì, un blog ce l'ho perfino io: gli è che mi manca il tempo...

"...in una società che sembra sempre più spaventata dai problemi troppo articolati e che rigetta come un corpo estraneo ogni elemento di complessità, sapremo ancora come utilizzare il parere degli esperti?"
gila75
Imperturbabile Insigne
Imperturbabile Insigne
Messaggi: 2739
Iscrizione: mercoledì 16 gennaio 2013, 17:28
Desktop: ubuntu-2d
Distribuzione: Ubuntu 12.04.2 LTS i686
Località: Airuno(Lecco)

Re: [C] Notazione polacca

Messaggio da gila75 »

Sembra essere ok. Bello poi la notazione esadecimale se non ho visto male.
Me lo voglio studiare.
Invece tu che ne pensi del mio metodo...se hai voglia s'intende
Aurugi a tutti, ragassuoli.
Anche a te MAW.
Dall'alto della mia ignoranza pensavo che i parser non potessero gestire situazioni infinite.
Come detto prima: se - prima del numero ok, se isolato operatore, se doppio non valido ecc.
Io ho cercato di gestire le situazioni, per ora sembra funzionare bene, ma il bug è sempre in agguato.
Non so se ciò che linki è alla mia portata, ma proverò a guardare.
Senza demolirmi in battuta come inizio 2016 (improbabile) magari prova a vedere se può essere almeno lontanamente accettabili ciò che ho postato :)
Avatar utente
vbextreme
Entusiasta Emergente
Entusiasta Emergente
Messaggi: 1214
Iscrizione: domenica 12 gennaio 2014, 14:06
Desktop: lxde
Distribuzione: xubuntu 14.10

Re: [C] Notazione polacca

Messaggio da vbextreme »

@gila, oltre a poter inserire i numeri in formato esadecimale, riconoscibili da 0x anteposto prima del numero stesso, il lexer riconosce anche le stringhe, grazie a questo se un domani si vorrà aggiungere "sqr" il lexer sarà già impostato e bisognerà solo dire al parser come reagire alle stringhe.
Come vedi un approccio lexer/parser è molto piu scalabile e permette una gestione maggiore degli errori.
Il tuo codice è veramente molto bello, ma cercare di farlo funzionare potrebbe essere sempre una sfida contro i bug, ecco perchè ho preferito suddividere i compiti:
.1 leggo la stringa
.2 lexer: analizzo la stringa e suddivido in token assegnandoli un valore.
.3 parser: analizzo i token controllo che rispettino le regole prefisse inserisco in una struttura i calcoli da fare
.4 calcolatore: scorro la lista eseguendo i calcoli dati.
.5 visualizzo l'unico elemento rimasto in stack
.6 go to .1
@maw ha scritto: Come proponimento per il nuovo anno, i più temerari tra voi dovrebbero sfruttare l'occasione per familiarizzare con yacc e bison.
un compilatore è proprio quello che vorrei fare quest'anno, ho gia iniziato a leggermi qualcosa sui lexer e parser. Ma detta francamente ho capito solo la teoria e infatti riesco ad applicarla, mentre devo ancora capire da che lato è girato lo yacc e il bison!
Easy framework per il linguaggio C.
vbextreme hack your life
Scrivi risposta

Ritorna a “Programmazione”

Chi c’è in linea

Visualizzano questa sezione: 0 utenti iscritti e 4 ospiti