Dovrei usare un generatore di parser o devo rotolare il mio codice lexer e parser personalizzato?


Risposte:


78

Ci sono davvero tre opzioni, tutte e tre preferibili in diverse situazioni.

Opzione 1: generatori di parser o "devi analizzare un po 'di lingua e vuoi solo farlo funzionare, dannazione"

Supponiamo che ti venga chiesto di creare un parser per alcuni formati di dati antichi ADESSO. O hai bisogno che il tuo parser sia veloce. Oppure hai bisogno che il tuo parser sia facilmente gestibile.

In questi casi, probabilmente è meglio usare un generatore di parser. Non devi armeggiare con i dettagli, non devi avere un sacco di codice complicato per funzionare correttamente, devi solo scrivere la grammatica a cui l'input aderirà, scrivere un po 'di codice di gestione e presto: parser istantaneo.

I vantaggi sono evidenti:

  • È (di solito) abbastanza facile scrivere una specifica, in particolare se il formato di input non è troppo strano (l'opzione 2 sarebbe meglio se lo fosse).
  • Si finisce con un lavoro molto facilmente gestibile che è facilmente comprensibile: una definizione grammaticale di solito scorre molto più naturale del codice.
  • I parser generati da buoni generatori di parser sono in genere molto più veloci del codice scritto a mano. Il codice scritto a mano può essere più veloce, ma solo se conosci le tue cose - questo è il motivo per cui i compilatori più utilizzati usano un parser scritto a mano ricorsivo-discesa.

C'è una cosa che devi fare attenzione con i generatori di parser: a volte puoi rifiutare le tue grammatiche. Per una panoramica dei diversi tipi di parser e come possono morderti, potresti iniziare qui . Qui puoi trovare una panoramica di molte implementazioni e dei tipi di grammatiche che accettano.

Opzione 2: parser scritti a mano o "vuoi creare il tuo parser personale e ti interessa essere di facile utilizzo"

I generatori di parser sono belli, ma non sono molto user friendly (l'utente finale, non tu). In genere non è possibile fornire buoni messaggi di errore, né è possibile fornire il ripristino degli errori. Forse la tua lingua è molto strana e i parser rifiutano la tua grammatica o hai bisogno di un controllo maggiore di quello che ti dà il generatore.

In questi casi, usare un parser di discesa ricorsiva scritto a mano è probabilmente il migliore. Mentre farlo correttamente può essere complicato, hai il controllo completo sul tuo parser in modo da poter fare tutti i tipi di cose carine che non puoi fare con i generatori di parser, come i messaggi di errore e persino il recupero degli errori (prova a rimuovere tutti i punti e virgola da un file C # : il compilatore C # si lamenterà, ma rileverà comunque la maggior parte degli altri errori indipendentemente dalla presenza di punti e virgola).

Anche i parser scritti a mano di solito funzionano meglio di quelli generati, supponendo che la qualità del parser sia abbastanza alta. D'altra parte, se non riesci a scrivere un buon parser - di solito a causa di (una combinazione di) mancanza di esperienza, conoscenza o progettazione - allora le prestazioni sono generalmente più lente. Per i lexer è vero il contrario: i lexer generati generalmente usano ricerche di tabelle, rendendole più veloci di (la maggior parte) di quelle scritte a mano.

Per quanto riguarda l'educazione, scrivere il proprio parser ti insegnerà più che usare un generatore. Devi scrivere codice sempre più complicato dopo tutto, inoltre devi capire esattamente come analizzare una lingua. D'altra parte, se vuoi imparare come creare la tua lingua (quindi, acquisire esperienza nella progettazione della lingua), è preferibile l'opzione 1 o l'opzione 3: se stai sviluppando una lingua, probabilmente cambierà molto, e le opzioni 1 e 3 ti offrono un momento più facile.

Opzione 3: generatori di parser scritti a mano, o "stai cercando di imparare molto da questo progetto e non ti dispiacerebbe finire con un pezzo di codice che puoi riutilizzare molto"

Questo è il percorso che sto percorrendo attualmente: scrivi il tuo generatore di parser. Sebbene altamente non banale, farlo probabilmente ti insegnerà di più.

Per darti un'idea di cosa significhi fare un progetto come questo, ti parlerò dei miei progressi.

Il generatore di lexer

Ho creato prima il mio generatore di lexer. Di solito progetto software a partire da come verrà usato il codice, quindi ho pensato a come volevo poter usare il mio codice e ho scritto questo pezzo di codice (è in C #):

Lexer<CalculatorToken> calculatorLexer = new Lexer<CalculatorToken>(
    new List<StringTokenPair>()
    { // This is just like a lex specification:
      //                    regex   token
        new StringTokenPair("\\+",  CalculatorToken.Plus),
        new StringTokenPair("\\*",  CalculatorToken.Times),
        new StringTokenPair("(",    CalculatorToken.LeftParenthesis),
        new StringTokenPair(")",    CalculatorToken.RightParenthesis),
        new StringTokenPair("\\d+", CalculatorToken.Number),
    });

foreach (CalculatorToken token in
             calculatorLexer.GetLexer(new StringReader("15+4*10")))
{ // This will iterate over all tokens in the string.
    Console.WriteLine(token.Value);
}

// Prints:
// 15
// +
// 4
// *
// 10

Le coppie stringa-token di input vengono convertite in una struttura ricorsiva corrispondente che descrive le espressioni regolari che rappresentano usando le idee di una pila aritmetica. Questo viene quindi convertito in un NFA (automa finito non deterministico), che a sua volta viene convertito in un DFA (automa finito deterministico). È quindi possibile abbinare le stringhe al DFA.

In questo modo, hai una buona idea di come funzionano esattamente i lexer. Inoltre, se lo fai nel modo giusto, i risultati del tuo generatore lexer possono essere all'incirca veloci quanto le implementazioni professionali. Inoltre, non si perde alcuna espressività rispetto all'opzione 2 e non c'è molta espressività rispetto all'opzione 1.

Ho implementato il mio generatore di lexer in poco più di 1600 righe di codice. Questo codice fa funzionare quanto sopra, ma genera comunque il lexer al volo ogni volta che avvii il programma: ad un certo punto aggiungerò il codice per scriverlo sul disco.

Se vuoi sapere come scrivere il tuo lexer, questo è un buon punto di partenza.

Il generatore di parser

Quindi scrivi il tuo generatore di parser. Mi riferisco di nuovo qui per una panoramica dei diversi tipi di parser - come regola generale, più possono analizzare, più sono lenti.

La velocità non è un problema per me, ho scelto di implementare un parser Earley. Le implementazioni avanzate di un parser Earley hanno dimostrato di essere circa due volte più lente di altri tipi di parser.

In cambio di quel colpo di velocità, hai la possibilità di analizzare qualsiasi tipo di grammatica, anche ambigua. Ciò significa che non devi mai preoccuparti se il tuo parser ha una ricorsione a sinistra o se è un conflitto di riduzione del turno. Puoi anche definire le grammatiche più facilmente usando grammatiche ambigue se non importa quale albero di analisi è il risultato, ad esempio che non importa se analizzi 1 + 2 + 3 come (1 + 2) +3 o come 1 + (2 + 3).

Ecco come può apparire un pezzo di codice usando il mio generatore di parser:

Lexer<CalculatorToken> calculatorLexer = new Lexer<CalculatorToken>(
    new List<StringTokenPair>()
    {
        new StringTokenPair("\\+",  CalculatorToken.Plus),
        new StringTokenPair("\\*",  CalculatorToken.Times),
        new StringTokenPair("(",    CalculatorToken.LeftParenthesis),
        new StringTokenPair(")",    CalculatorToken.RightParenthesis),
        new StringTokenPair("\\d+", CalculatorToken.Number),
    });

Grammar<IntWrapper, CalculatorToken> calculator
    = new Grammar<IntWrapper, CalculatorToken>(calculatorLexer);

// Declaring the nonterminals.
INonTerminal<IntWrapper> expr = calculator.AddNonTerminal<IntWrapper>();
INonTerminal<IntWrapper> term = calculator.AddNonTerminal<IntWrapper>();
INonTerminal<IntWrapper> factor = calculator.AddNonTerminal<IntWrapper>();

// expr will be our head nonterminal.
calculator.SetAsMainNonTerminal(expr);

// expr: term | expr Plus term;
calculator.AddProduction(expr, term.GetDefault());
calculator.AddProduction(expr,
                         expr.GetDefault(),
                         CalculatorToken.Plus.GetDefault(),
                         term.AddCode(
                         (x, r) => { x.Result.Value += r.Value; return x; }
                         ));

// term: factor | term Times factor;
calculator.AddProduction(term, factor.GetDefault());
calculator.AddProduction(term,
                         term.GetDefault(),
                         CalculatorToken.Times.GetDefault(),
                         factor.AddCode
                         (
                         (x, r) => { x.Result.Value *= r.Value; return x; }
                         ));

// factor: LeftParenthesis expr RightParenthesis
//         | Number;
calculator.AddProduction(factor,
                         CalculatorToken.LeftParenthesis.GetDefault(),
                         expr.GetDefault(),
                         CalculatorToken.RightParenthesis.GetDefault());
calculator.AddProduction(factor,
                         CalculatorToken.Number.AddCode
                         (
                         (x, s) => { x.Result = new IntWrapper(int.Parse(s));
                                     return x; }
                         ));

IntWrapper result = calculator.Parse("15+4*10");
// result == 55

(Nota che IntWrapper è semplicemente un Int32, tranne per il fatto che C # richiede che sia una classe, quindi ho dovuto introdurre una classe wrapper)

Spero che tu veda che il codice sopra è molto potente: qualsiasi grammatica che puoi inventare può essere analizzata. È possibile aggiungere nella grammatica bit arbitrari di codice in grado di eseguire molte attività. Se riesci a far funzionare tutto questo, puoi riutilizzare il codice risultante per svolgere molte attività molto facilmente: immagina di costruire un interprete da riga di comando usando questo pezzo di codice.


3
Penso che tu sottovaluti la quantità di lavoro necessaria per creare un parser e un lexer ad alte prestazioni.

Ho già finito di costruire il mio generatore di lexer ed ero abbastanza lontano dalla costruzione del mio generatore di parser quando ho deciso di implementare un algoritmo diverso. Non mi ci è voluto molto per far funzionare tutto, ma poi non ho mirato a "alte prestazioni", solo "buone prestazioni" e "grandi prestazioni asintotiche" - Unicode è una cagna per ottenere buoni tempi di esecuzione per e l'utilizzo di C # impone già un sovraccarico di prestazioni.
Alex ten Brink,

Risposta molto bella. Concordo con la tua opzione n. 3 per tutti i motivi indicati in precedenza. Ma posso aggiungere che se, come nel mio caso, sei anche molto serio nel progettare una lingua, forse dovresti anche usare generatori di parser mentre provi a crearne uno tuo. In questo modo puoi ottenere un vantaggio sui problemi della lingua ed essere in grado di vedere la tua lingua in azione più velocemente
Lefteris,

1
C'è una quarta opzione: combinatori parser.
YuriAlbuquerque,

@AlextenBrink Ti capita di avere un account github per caso? Voglio davvero mettere le mani su quel lexer / parser. Cosa impressionante che hai fatto.
Behrooz,

22

Se non hai mai scritto mai un parser, ti consiglio di farlo. È divertente e impari come funzionano le cose e impari ad apprezzare lo sforzo che i generatori di parser e lexer ti salvano dal fare la prossima volta che hai bisogno di un parser.

Vorrei anche suggerire di provare a leggere http://compilers.iecc.com/crenshaw/ in quanto ha un atteggiamento molto concreto verso come farlo.


2
Buon suggerimento e un link molto utile.
Maniero,

14

Il vantaggio di scrivere il proprio parser di discesa ricorsivo è che è possibile generare messaggi di errore di alta qualità sugli errori di sintassi. Utilizzando i generatori di parser, è possibile effettuare produzioni di errori e aggiungere messaggi di errore personalizzati in determinati punti, ma i generatori di parser semplicemente non corrispondono alla potenza di avere il controllo completo sull'analisi.

Un altro vantaggio di scrivere il tuo è che è più facile analizzare una rappresentazione più semplice che non ha una corrispondenza uno a uno con la tua grammatica.

Se la tua grammatica è fissa e i messaggi di errore sono importanti, prendi in considerazione l'idea di crearne uno tuo, o almeno di utilizzare un generatore di parser che ti dia i messaggi di errore di cui hai bisogno. Se la tua grammatica è in continua evoluzione, dovresti invece considerare l'utilizzo di generatori di parser.

Bjarne Stroustrup parla di come ha usato YACC per la prima implementazione di C ++ (vedi The Design and Evolution of C ++ ). In quel primo caso, avrebbe voluto invece scrivere il suo parser di discesa ricorsivo!


Sono a malapena convinto che i primi esperimenti dovrebbero essere con un generatore di parser. Mi hai dato alcuni vantaggi per passare a una soluzione personalizzata. Non sto ancora decidendo nulla, ma è una risposta utile per aiutarmi.
Maniero,

++ Questa risposta è esattamente ciò che direi. Ho costruito numerose lingue e quasi sempre ho usato la discesa ricorsiva. Vorrei solo aggiungere che ci sono stati momenti in cui il linguaggio di cui avevo bisogno era stato creato semplicemente sovrapponendo alcune macro sopra C o C ++ (o Lisp).
Mike Dunlavey,

Si dice che JavaCC abbia i migliori messaggi di errore. Inoltre, noti l'errore JavaScript e i messaggi di avviso su V8 ​​e Firefox, penso che non abbiano usato alcun generatore di parser.
Ming-Tang,

2
@SHiNKiROU: In effetti, probabilmente non è un caso che JavaCC utilizzi anche l'analisi della discesa ricorsiva.
Macneil,

10

Opzione 3: nessuno dei due (ruota il tuo generatore di parser)

Solo perché c'è un motivo per non usare ANTLR , bisonte , Coco / R , Grammatica , JavaCC , Lemon , Parboiled , SableCC , Quex , ecc. , Ciò non significa che dovresti immediatamente rotolare il tuo parser + lexer.

Identifica perché tutti questi strumenti non sono abbastanza buoni - perché non ti consentono di raggiungere il tuo obiettivo?

A meno che tu non sia sicuro che le stranezze nella grammatica che stai affrontando siano uniche, non dovresti semplicemente creare un singolo parser personalizzato + lexer per questo. Invece, crea uno strumento che creerà ciò che desideri, ma può anche essere utilizzato per soddisfare le esigenze future, quindi rilascialo come software libero per impedire ad altre persone di avere lo stesso problema.


1
Concordo prima con i generatori di parser e poi con una soluzione personalizzata, ma quali specifici (dis) vantaggi? Questo è quasi un consiglio generale.
Maniero,

1
È un consiglio generale, ma poi hai posto una domanda generale. : P Lo estenderò con alcuni pensieri più specifici su pro e contro domani.
Peter Boughton,

1
Penso che tu sottovaluti la quantità di lavoro necessaria per creare un parser e un lexer personalizzati. Soprattutto riutilizzabile.

8

Il rolling del tuo parser ti costringe a pensare direttamente alla complessità della tua lingua. Se la lingua è difficile da analizzare, probabilmente sarà difficile da capire.

All'inizio c'era molto interesse nei generatori di parser, motivati ​​da una sintassi linguistica altamente complicata (alcuni direbbero "torturati"). JOVIAL fu un esempio particolarmente negativo: richiese due simboli, in un momento in cui tutto il resto richiedeva al massimo un simbolo. Ciò ha reso la generazione del parser per un compilatore JOVIAL più difficile del previsto (poiché la divisione General Dynamics / Fort Worth ha imparato a fatica quando hanno procurato i compilatori JOVIAL per il programma F-16).

Oggi la discesa ricorsiva è universalmente il metodo preferito, perché è più facile per gli autori di compilatori. I compilatori di discendenza ricorsiva premiano fortemente la progettazione di un linguaggio semplice e pulito, in quanto è molto più facile scrivere un parser a discesa ricorsiva per un linguaggio semplice e pulito che per un linguaggio contorto e disordinato.

Infine: hai preso in considerazione l'idea di incorporare la tua lingua in LISP e lasciare che un interprete LISP faccia il lavoro pesante per te? AutoCAD lo ha fatto e ha scoperto che ha reso la loro vita molto più semplice. Ci sono alcuni interpreti LISP leggeri là fuori, alcuni incorporabili.


È un argomento interessante lanciare una soluzione personalizzata.
Maniero,

1
Molto bella. Aggiungerò semplicemente come punto di informazione che Fortran ha richiesto un look quasi arbitrario (l'intera linea) per analizzare le cose, prima del JOVIAL. Ma all'epoca non avevano altra idea di come fare (o implementare) una lingua.
Macneil,

Camminare è il miglior mezzo di trasporto in quanto ti dà il tempo di pensare se vale davvero la pena andare dove stai andando. È anche salutare.
babou,

6

Ho scritto un parser per un'applicazione commerciale una volta e ho usato yacc . Esisteva un prototipo in competizione in cui uno sviluppatore scriveva tutto a mano in C ++ e funzionava circa cinque volte più lentamente.

Per quanto riguarda il lexer per questo parser, l'ho scritto interamente a mano. Ci sono voluti - scusate, era quasi 10 anni fa, quindi non mi ricordo con precisione - circa 1000 linee in C .

Il motivo per cui ho scritto a mano il lexer è stata la grammatica di input del parser. Era un requisito, qualcosa che la mia implementazione del parser doveva rispettare, al contrario di qualcosa che avevo progettato. (Ovviamente l'avrei progettato diversamente. E meglio!) La grammatica era fortemente dipendente dal contesto e persino il lessico dipendeva dalla semantica in alcuni punti. Ad esempio un punto e virgola potrebbe far parte di un token in un posto, ma un separatore in un posto diverso - basato su un'interpretazione semantica di alcuni elementi che sono stati analizzati in precedenza. Quindi, ho "seppellito" tali dipendenze semantiche nel lexer scritto a mano e questo mi ha lasciato con un BNF abbastanza semplice che era facile da implementare in Yacc.

AGGIUNTO in risposta a Macneil : yacc fornisce un'astrazione molto potente che consente al programmatore di pensare in termini di terminali, non terminali, produzioni e cose del genere. Inoltre, durante l'implementazione della yylex()funzione, mi ha aiutato a concentrarmi sulla restituzione del token corrente e non preoccuparmi di ciò che era prima o dopo. Il programmatore C ++ ha lavorato a livello di personaggio, senza il beneficio di tale astrazione e ha finito per creare un algoritmo più complicato e meno efficiente. Abbiamo concluso che la velocità più lenta non aveva nulla a che fare con il C ++ stesso o le librerie. Abbiamo misurato la velocità di analisi pura con i file caricati in memoria; se avessimo un problema di buffering dei file, yacc non sarebbe il nostro strumento preferito per risolverlo.

VUOI ANCHE AGGIUNGERE : questa non è una ricetta per scrivere parser in generale, solo un esempio di come ha funzionato in una situazione particolare.


Sono curioso di sapere a mano l'implementazione di C ++ cinque volte più lenta: forse è stato uno scarso buffering dei file? Può fare una grande differenza.
Macneil,

@Macneil: ho intenzione di pubblicare un'aggiunta alla mia risposta; il commento è troppo lungo.
Azheglov,

1
++ Buona esperienza. Non darei troppo peso alle prestazioni. È facile per i programmi altrimenti buoni essere rallentati da qualcosa di sciocco e inutile. Ho scritto abbastanza parser a discesa ricorsiva per sapere cosa non fare, quindi dubito che ci sia qualcosa di molto più veloce. Dopotutto, i personaggi devono essere letti. Sospetto che i parser che scappano dalle tabelle saranno un po 'più lenti, ma probabilmente non abbastanza da notare.
Mike Dunlavey,

3

Dipende interamente da ciò che devi analizzare. Riesci a tirare il tuo più velocemente di quanto potresti colpire la curva di apprendimento di un lexer? Le cose da analizzare sono abbastanza statiche da non pentirti della decisione in seguito? Trovi le implementazioni esistenti troppo complesse? In tal caso, divertiti a farlo da solo, ma solo se non stai evitando una curva di apprendimento.

Ultimamente mi è piaciuto molto il parser al limone , che è probabilmente il più semplice e facile che io abbia mai usato. Per rendere le cose facili da mantenere, le uso solo per la maggior parte delle esigenze. SQLite lo utilizza e alcuni altri progetti importanti.

Ma non mi interessa affatto i lexer, al di là di loro non mi ostacolano quando ne ho bisogno (uno, quindi, il limone). Potresti esserlo, e in tal caso, perché non crearne uno? Ho la sensazione che tornerai a usarne uno esistente, ma gratta il prurito se devi :)


3
+1 per "Riesci a tirare il tuo più velocemente di quanto potresti colpire la curva di apprendimento di un lexer?"
Bobah,

Sì, buon punto.
Maniero,

3

Dipende dal tuo obiettivo.

Stai cercando di imparare come funzionano i parser / compilatori? Quindi scrivi il tuo da zero. Questo è l'unico modo in cui impareresti davvero ad apprezzare tutti i dettagli di ciò che stanno facendo. Ne ho scritto uno negli ultimi due mesi, ed è stata un'esperienza interessante e preziosa, in particolare i momenti "ah, ecco perché la lingua X fa questo ..." momenti.

Hai bisogno di mettere insieme qualcosa rapidamente per un'applicazione entro una scadenza? Quindi forse usa uno strumento parser.

Hai bisogno di qualcosa su cui vorresti ampliare nei prossimi 10, 20, forse anche 30 anni? Scrivi il tuo e prenditi il ​​tuo tempo. Ne varrà la pena.


È il mio primo lavoro sui compilatori, sto imparando / sperimentando ed è il mio intento di mantenerlo per molto tempo.
Maniero,

3

Hai preso in considerazione l' approccio al workbench del linguaggio Martin Fowlers ? Citando l'articolo

Il cambiamento più evidente che un workbench linguistico apporta all'equazione è la facilità di creazione di DSL esterni. Non è più necessario scrivere un parser. Devi definire una sintassi astratta, ma in realtà è un passaggio di modellazione dei dati piuttosto semplice. Inoltre, il tuo DSL ottiene un IDE potente, anche se devi dedicare un po 'di tempo a definire quell'editor. Il generatore è ancora qualcosa che devi fare, e il mio senso è che non è molto più facile di quanto non sia mai stato. Ma poi costruire un generatore per un DSL buono e semplice è una delle parti più facili dell'esercizio.

Leggendolo, direi che i giorni in cui hai scritto il tuo parser sono finiti ed è meglio usare una delle librerie disponibili. Dopo aver acquisito padronanza della libreria, tutti i DSL creati in futuro trarranno vantaggio da tale conoscenza. Inoltre, gli altri non devono imparare il tuo approccio all'analisi.

Modifica per coprire il commento (e la domanda rivista)

Vantaggi del rotolamento personale

  1. Avrai il parser e otterrai tutta quella bella esperienza di pensiero attraverso una complessa serie di problemi
  2. Potresti inventare qualcosa di speciale a cui nessun altro ha pensato (improbabile ma sembri un tipo intelligente)
  3. Ti terrà occupato con un problema interessante

Quindi, in breve, dovresti farlo da solo quando vuoi davvero scavare in profondità nelle viscere di un problema seriamente difficile che ti senti fortemente motivato a padroneggiare.

Vantaggi dell'utilizzo della libreria di qualcun altro

  1. Eviterai di reinventare la ruota (accetti un problema comune nella programmazione)
  2. Puoi concentrarti sul risultato finale (il tuo nuovo linguaggio brillante) e non preoccuparti troppo di come viene analizzato, ecc
  3. Vedrai la tua lingua in azione molto più velocemente (ma la tua ricompensa sarà inferiore perché non era tutto te)

Pertanto, se si desidera un risultato finale rapido, utilizzare la libreria di qualcun altro.

Nel complesso, ciò si riduce alla scelta di quanto si desidera possedere il problema, e quindi la soluzione. Se lo vuoi tutto, fai il tuo.


È un'ottima alternativa al pensiero.
Maniero,

1
@bigown Modificato per rispondere meglio alla tua domanda
Gary Rowe,

2

Il grande vantaggio di scrivere il tuo è che saprai come scrivere il tuo. Il grande vantaggio dell'uso di uno strumento come yacc è che saprai come utilizzare lo strumento. Sono un fan delle cime degli alberi per l'esplorazione iniziale.


Non particolarmente utile. Avresti anche potuto dire: "Il vantaggio di imparare a guidare è che puoi guidare. Il vantaggio di imparare ad andare in bicicletta è che puoi andare in bicicletta ”.
Zearin,

1

Perché non fork un generatore di parser open source e renderlo tuo? Se non usi generatori di parser, il tuo codice sarà molto difficile da mantenere, se hai apportato grandi cambiamenti alla sintassi della tua lingua.

Nei miei parser, ho usato espressioni regolari (intendo, stile Perl) per tokenizzare e usare alcune funzioni di convenienza per aumentare la leggibilità del codice. Tuttavia, un codice generato dal parser può essere più veloce creando tabelle di stato e long switch- cases, che possono aumentare le dimensioni del codice sorgente a meno che tu non lo .gitignorefaccia.

Ecco due esempi dei miei parser scritti su misura:

https://github.com/SHiNKiROU/DesignScript - un dialetto BASIC, perché ero troppo pigro per scrivere lookahead in notazione array, ho sacrificato la qualità del messaggio di errore https://github.com/SHiNKiROU/ExprParser - Un calcolatore di formule. Nota gli strani trucchi di metaprogrammazione


0

"Dovrei usare questa collaudata" ruota "o reinventarla?"


1
Cos'è questa "ruota" di cui parli? ;-)
Jason Whitehorn,

IMO questa non è una buona opinione su questa domanda. Questo è solo un consiglio generale non adatto a casi specifici. Comincio a sospettare che la proposta area51.stackexchange.com/proposals/7848 sia stata chiusa prematuramente.
Maniero,

2
Se la ruota non fosse mai stata reinventata, non viaggeremmo a 100 km orari + su base giornaliera - a meno che tu non abbia intenzione di suggerire che grossi blocchi di roccia che ruotano su assi di legno sono migliori delle molte molte varianti di pneumatici moderni utilizzati in così tanti veicoli?
Peter Boughton,

Questa è un'opinione valida ed è l'intuizione giusta. Sto pensando che questa risposta potrebbe essere più utile se tu potessi elencare vantaggi o svantaggi specifici, perché questo genere di cose dipende interamente dalle circostanze.
Macneil,

@Peter: Una cosa è reinventare qualcosa (implica farlo in modo completamente diverso) ma perfezionare una soluzione esistente per soddisfare requisiti aggiuntivi è meglio. Sono tutto per "migliorare", ma tornare al tavolo da disegno per un problema già risolto sembra sbagliato.
JBR Wilkinson,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.