Come faccio a semplificare un compilatore / interprete?

1

Recentemente ho scritto un interprete per operazioni su matrici sparse (una "calcolatrice a matrice sparsa") in lex / yacc. Il linguaggio è ancora molto semplice e non include nemmeno strutture di controllo o subroutine parametrizzate, ma è già presente a diverse migliaia di righe di codice, e questo non include le classi di matrici. In particolare, il file yacc ha una lunghezza di circa duemila righe. Per questo motivo trovo molto difficile lavorare su. È normale o esiste un modo per semplificare le cose?

Se vuoi rivedere il mio codice, lo puoi trovare su: link

    
posta user18850 09.05.2014 - 19:47
fonte

2 risposte

7

Poche migliaia di righe per un parser + interprete che effettivamente fa qualcosa di interessante non è inusuale. Ho esaminato il repository SVN e in particolare il tuo grammatica principale e notato varie cose:

  • Esistono varie classi di utilità che avrebbero più senso in una libreria di algoritmi generali. Un'implementazione quicksort, davvero? IIRC, la libreria standard C ++ contiene già tale funzionalità.

    In generale, il codice sembra essere molto simile a C (a parte l'uso delle classi) e potrebbe trarre beneficio dalla costruzione di astrazioni utili usando la vasta gamma di funzionalità del linguaggio C ++. Vedo un sacco di riutilizzo del codice tramite copia e incolla.

  • Non sembra che tu costruisca un AST, ma l'analisi e la valutazione avvengono contemporaneamente. Dovresti disgiungere l'analisi dalla convalida semantica dall'interpretazione. Una volta che la tua grammatica yacc contiene solo una grammatica e non un interprete a metà, dovrebbe diventare molto più gestibile.

    Separare l'interprete dal parser ha anche il vantaggio che è più semplice implementare un flusso di controllo complesso come loop, condizionali o chiamate di funzione.

  • La tua grammatica include anche messaggi di aiuto come stringhe letterali. Calcola tali messaggi in un file di risorse, piuttosto che codificare tutto.

Questa era solo una rapida recensione di design. Se sei interessato a una critica approfondita del tuo codice, pubblica un estratto del tuo codice in Revisione codice .

    
risposta data 09.05.2014 - 20:45
fonte
2

L'analisi è intrinsecamente complessa. È difficile da fare correttamente e anche più difficile da fare elegantemente. Un sacco di codice non è necessariamente un segno di un problema. Tuttavia, ci sono molte cose che puoi fare per renderti più facile.

  • Utilizza le funzioni invece di inserire tutto nel tuo file yacc. Volete che un file yacc sia solo la specifica della vostra grammatica, mettendo la maggior quantità possibile di implementazione in altri file. Ciò ti consente di eseguire il debug della grammatica più facilmente e ti aiuta anche a riconoscere più facilmente la ridondanza.
  • Spendi un lotto di tempo per ottenere la grammatica prima di lavorare sull'implementazione. Non ho guardato il tuo file in profondità, e il mio yacc è un po 'arrugginito, ma sembra che ci sia molta ripetizione che potrebbe essere eliminata con una grammatica migliore. Aggiungere extra non-terminali in determinati posti può farti risparmiare un sacco di lavoro. In particolare, invece di elencare tutte le possibili combinazioni di espressioni vettoriali, scalari e matriciali separatamente, prova a combinarle in un non terminale chiamato value o qualcosa, combina i tuoi operatori in un non terminale chiamato op o qualcosa ( o gruppo in base alla precedenza degli operatori) e scrivere regole come value op value .
  • Metti molta parsimonia nel parser, in cui dovresti concentrarti principalmente sulla struttura e implementare la semantica in altri file. Prova a generare l'output del tuo parser solo AST . I parser semplici, come quelli che trovi nei tutorial, possono saltare questo passaggio, ma troverai che per linguaggi moderatamente complessi hai davvero bisogno di quel livello aggiuntivo.
risposta data 09.05.2014 - 20:52
fonte

Leggi altre domande sui tag