Come ho costruito un linguaggio di programmazione Il (difficile) cammino verso il successo

Building a programming language The difficult path to success

Onestamente, difficile è un eufemismo.

Il mio articolo precedente sul mio linguaggio di programmazione ha delineato la sua sintassi e ha fornito un’idea generale di come l’ho costruito.

Ma ho deciso di scriverne un altro dedicato al mio viaggio verso il risultato finale, perché se devo essere onesto, il viaggio ha avuto sicuramente più momenti negativi che positivi e le sfide che ho dovuto affrontare erano davvero intimidatorie. Spero che questo articolo serva anche da motivazione per chiunque intraprenda un progetto simile e si trovi di fronte a difficoltà simili, per non arrendersi ma sapere che qualcun altro si è trovato nella stessa situazione e alla fine ha raggiunto il suo obiettivo.

Ma perché dovresti interessarti?

Dopotutto, tutti voi provenite da background diversi, che sia la scienza dei dati o semplicemente il buon vecchio Python e Java.

Ebbene, anche se non sei interessato a costruire il tuo linguaggio di programmazione, molte delle soft skills che ho migliorato durante questa esperienza saranno certamente relazionabili (e spero motivanti) per tutti voi, specialmente se siete interessati alla programmazione.

Quante volte scrivi un programma in cui ogni riga sembra corretta e invece ti viene restituito un errore criptico che sembra quasi impossibile da debuggare, a quel punto inizi a perdere la motivazione e pensi di arrenderti.

Foto di Tim Gouw su Unsplash

Ebbene, ho affrontato questi problemi troppo comuni troppe volte durante la costruzione del linguaggio e il modo in cui alla fine li ho superati e perseverato spero comunicano un messaggio memorabile mentre procedi nell’articolo!

Iniziare

L’idea originale di questo è venuta quando ero in ottava elementare (o nona elementare, non ricordo bene), ma all’epoca capivo appena le condizioni e i loop iterativi, quindi l’ho dimenticata.

Ma anni dopo, la stessa idea è riemersa nella mia mente, solo che non avevo idea di come iniziare nemmeno con essa.

Foto di Towfiqu barbhuiya su Unsplash

Quindi, come tutte le altre cose che non sapevo fare, ho cercato su Google

“Come creare il tuo linguaggio di programmazione”.

E beh, i risultati erano piuttosto promettenti. Ho passato molto tempo a leggerli riga per riga e ho colto l’essenza:

Ci sono due tipi di linguaggi: interpretati e compilati. I linguaggi interpretati tendono ad essere più semplici da costruire, ma sono più lenti. D’altra parte, i linguaggi compilati convertono il tuo codice in codice macchina e poi lo eseguono (i linguaggi compilati sono generalmente più veloci).

Il processo generale per costruire un linguaggio è:

  • Definire lo scopo e la sintassi del linguaggio: Questo è stato abbastanza facile per me (sapevo di voler costruire un linguaggio legato alla matematica) e per risparmiare lavoro e rendere il linguaggio il più ad alto livello possibile, ho deciso di mantenere solo alcune funzioni (essenziali e utili) e due variabili, una funzione e un numero in virgola mobile.
  • Costruire un lexer e un parser: Un lexer è il primo componente di qualsiasi linguaggio e colloca ogni parola/frase del tuo codice in diverse categorie (come parole chiave, operatori, commenti, ecc.). Ma di per sé, questi lexeme sono privi di significato e per dargli un senso, è necessario costruire un parser. Il parser garantisce che i lexeme seguano la sintassi del linguaggio e li colloca in un AST (albero di sintassi astratta).
  • Eseguire il codice: Ogni nodo nell’AST rappresenta uno dei token raccolti dal nostro lexer originale e questo include anche i nomi delle funzioni. Pertanto, eseguire il nostro AST ci consente di eseguire il codice scritto riga per riga; questa è la base generale per un linguaggio di programmazione interpretato.

Ma un compilatore richiede alcuni passaggi extra; è necessario utilizzare librerie popolari come LLVM e libgccjit, che convertono il codice in un file eseguibile compilato.

Compilato o interpretato?

Da un lato, potrei creare un linguaggio interpretato, che è piuttosto impegnativo e richiede di dedicare una grande quantità di tempo e sforzo. D’altra parte, potrei creare un linguaggio compilato, che è ancora più impegnativo e richiede di dedicare ancora più tempo e sforzo.

Ma l’idea di costruire il mio stesso linguaggio compilato sembrava troppo allettante e sono caduto nella trappola.

Consiglio: non farlo.

Sebbene ci siano risorse disponibili per la costruzione di un compilatore usando LLVM (consiglio vivamente di guardare il video di 100 secondi su LLVM, divertente e anche educativo), mi sono presto perso nella loro documentazione a causa della sua complessità.

Azzarderei a dire che probabilmente potresti imparare le basi di Java più velocemente che imparare le basi della costruzione di un compilatore con LLVM.

Foto di Emile Perron su Unsplash

Uno spreco di tempo colossale (o no)?

Purtroppo per me, ho passato oltre due mesi cercando di cavarmela prima con LLVM e poi con libgccjit. Quest’ultimo si è rivelato più promettente e ho persino scritto del codice per il compilatore, tuttavia mi sono poi trovato di fronte a una serie di problemi legati alla configurazione del pacchetto libgccjit; sono arrivato persino a scaricare l’intero codice sorgente da GitHub e a metterlo nella stessa directory del mio codice del compilatore. Ma ancora, sembrava che niente funzionasse. Ho scorso innumerevoli discussioni su Reddit e StackOverflow in proposito, ma senza successo.

Quando StackOverflow non può aiutarti, sai che qualcosa non va.

Sul punto di arrendersi

A questo punto, ero davvero arrivato a un nuovo punto basso, perché non avevo fatto un briciolo di progresso da tanto tempo e cominciavo a svalutare tutto il lavoro che avevo fatto, come la costruzione di un lexer e un parser da zero, senza gli strumenti popolari lex/yacc e flex/Bison. A questo punto, mi sono chiesto “Dovrei buttarlo tutto nel cestino (virtuale)?”. Ero tentato di dire di sì, ma non mi arrendo così facilmente, soprattutto quando si tratta di progetti legati alla programmazione.

E così sono uscito a fare una passeggiata, ho fatto un po’ di chiarezza nella mia testa e sono tornato alla lavagna, questa volta con un obiettivo diverso: costruire un linguaggio interpretato.

Finalmente completandolo.

Una volta deciso di costruire un interprete, ho preso anche la difficile decisione di abbandonare il linguaggio C e tornare a Python. In circostanze normali, è meglio utilizzare linguaggi di livello più basso per costruire linguaggi interpretati, poiché la loro velocità compensa l’interprete lento.

Ma ero in una situazione disperata, e quelle richiedono misure disperate.

Dopo essere tornato in India, ho trascorso la settimana successiva in uno stato di insonnia e ho letteralmente lavorato 24 ore al giorno (circa 21 se non si contano colazione, pranzo e cena – sono un mangiatore lento).

E ciò che è successo dopo è qualcosa che mi è capitato per la prima volta e probabilmente non mi capiterà mai più: il mio codice ha funzionato al primo tentativo! Nessun debug, nessuna confusione.

Quando ho eseguito il mio codice sorgente, ogni singolo componente: dal lexer, al parser e all’AST, ha funzionato correttamente ed ha eseguito correttamente ogni riga di codice come doveva.

Foto di the blowup su Unsplash

Riflessione e una nota finale

La citazione sopra può sembrare banale, ma ciò non ne sminuisce la validità; ho affrontato molto più fallimenti che successi mentre costruivo AdvAnalysis, ma alla fine sono riuscito a venire fuori.

Anche se potresti imparare cose da tenere presente quando costruisci il tuo linguaggio di programmazione (come compilato vs interpretato, il processo da seguire), il messaggio principale che ho avuto (e spero tu abbia avuto anche tu) è che non importa quanto scoraggiante possa essere incontrare costantemente un errore criptico nel tuo programma, alla fine funziona sempre come ti aspetti, se solo hai la capacità di essere paziente e tornare al punto di partenza e seguire un approccio diverso.

Ad essere onesti, come appassionato di programmazione come te, probabilmente già lo sapevi. 🙂