Sviluppare i propri linguaggi - Perché e come

di il
13 risposte

Sviluppare i propri linguaggi - Perché e come

Sviluppare i propri linguaggi - Perché e come


Ciao a tutti, cari colleghi programmatori.

Approfitto di questo primo post per presentarmi brevemente: mi chiamo Davide e, dal 1984 per lavoro sviluppo software. All'inizio degli anni '90 ci fu il boom dei CASE, Computer Aided Software Engineering, e così da allora mi occupo quasi esclusivamente dello sviluppo di software che scrivono, o aiutano a scrivere, software.

Ma veniamo al topic di questo post: sviluppare i propri linguaggi, cioè realizzare linguaggi di programmazione personalizzati.
Spesso nell'immaginario collettivo, quando si parla di linguaggi di programmazione custom, ci si immagina di farsi il proprio linguaggio ed utilizzarlo al posto di Java, del C o del BASIC. Sì, può essere divertente come esperimento, ma commercialmente inutile: è come se un tassista si autocostruisse l'automobile invece di usarne una già in commercio.

I linguaggi custom sono definiti e sono alla base di un modo più evoluto di sviluppare software.
Quando noi scriviamo un software, traduciamo delle specifiche che parlano di argomenti del cliente (ad esempio, fatture, fornitori e leggi fiscali), area definita business domain, in concetti inerenti il linguaggio di programmazione che stiamo usando (ad esempio, tabelle SQL e metodi Java), area definita application domain.

I programmatori sviluppano delle tecniche mentali ripetitive, tramite le quali un certo tipo di concetto nel business domain scatena una serie di azioni nell'application domain (ad esempio realizzare una tabella, scrivere dei metodi, ecc.).
Appare subito evidente che:
1) la parte implementativa può essere molto diversa a partire dallo stesso business model: il codice per un applicazione GUI Java con DBMS SQL sarà ben diverso dallo stesso programma scritto in C operante in command-line e con dati su file ASCII, pur essendo identiche le regole e i dati da trattare;
2) ogni modifica nel business domain provoca una serie di cambiamenti a cascata nell'implementazione;
3) ogni cambio di application domain, cioè ad esempio l'introduzione di nuove tecnologie dovute a progresso tecnico o ragioni prestazionali, spesso richiede la rianalisi del business domain (cioè, tradotto in soldoni, la riscrittura ex-novo).

Il "sogno" dei linguaggi domain-specific è molto semplice:
1) si inventa un linguaggio avente i termini e i costrutti necessari a spiegare tutto e solo quanto competa il business model del nostro particolare caso;
2) si realizza un compilatore a partire da quello realizzi l'applicazione, magari passando per la generazione di codice in linguaggi standard.

La tecnica può essere applicata a "matrioska": non solo un linguaggio custom per l'intera applicazione, ma linguaggi custom per problematiche implementative intermedie. Se ci pensiamo, esistono già in commercio "linguaggi custom" dedicati a problematiche particolari. Ad esempio, il linguaggio SQL parla in maniera astratta di dati rappresentati in forma tabellare e query: certo non è un "linguaggio di programmazione" come lo intendiamo di solito ma è adatto ad una serie di casi particolari. Oppure MathLab, che parla la lingua dei matematici.

Se pensiamo ai vantaggi di questa tecnica, sono molto appetibili:
- se cambiano le specifiche, devo solo aggiornare la loro definizione e l'applicazione viene rigenerata;
- se cambio l'implementazione, devo solo aggiornare il compilatore e l'applicazione viene rigenerata;
- posso generare la stessa applicazione con implementazioni diverse: ad esempio, una web e una per smartphone;
- posso usare la specifica iniziale per generare dei test automatici dell'applicazione;
- posso generare della documentazione utente sempre automaticamente aggiornata;
- se c'è un errore nel generatore, l'errore viene ripetuto dappertutto e subito individuato: in generale, la qualità è altissima;
- posso partire con una generazione elementare e potenziarla in seguito in base alle esigenze;
- le specifiche presenti nel business-model sono sempre quelle effettive, dato che esso è il sorgente: in quanti casi, nelle applicazioni reali, siamo pronti a mettere la mano sul fuoco che, magari dopo 10 anni, l'applicazione attuale sia perfettamente allineata al suo documento Word di specifica (ammesso che ce ne sia uno)?

Perché, allora, ad oltre un quarto di secolo dalla nascita del concetto di "CASE", stiamo ancora scrivendo in C, in Java e in .NET?

La risposta risiede ovviamente negli enormi ostacoli tecnici che si frappongono alla realizzazione di un linguaggio custom.

Innanzitutto il primo grosso problema è che linguaggio realizzare, cioè sintassi e semantica. Noi siamo abituati ad utilizzare linguaggi già stabilizzati, in cui è provato dal tempo e dall'esperienza che sono adatti ad isolare il dominio di competenza. Infatti un linguaggio deve essere intuitivo, consentire di esprimere concetti senza essere troppo limitante e, allo stesso tempo, senza essere macchinoso.

Ma il vero problema è che scrivere un compilatore per un DSL è materia per specialisti. Infatti le problematiche relative al parsing sintattico, gestione degli errori e generazione degli output sono notevoli e poco conosciute.
Oltretutto, i compilatori per DSL hanno problematiche ancora diverse da quelle dei compilatori per linguaggi generalisti. Ad esempio, un compilatore C parte da una sintassi stabile e standardizzata e il grosso del lavoro è nell'ottimizzazione per le varie CPU. Un linguaggio DSL invece, ha una sintassi più semplice ma molto variabile, perché essa finisce con l'evolversi continuamente insieme allo sviluppo dell'applicazione.
Nessuno lavora definendo al primo colpo la sintassi perfetta e facendo il resto in cascata: man mano che ci si lavora, si capisce cosa funziona bene e cosa meno bene del nostro linguaggio custom e si aggiusta il tiro.

Dai primi anni '90, con l'azienda per cui lavoro abbiamo realizzato numerosi DSL con relativi compilatori provando tutte le tecniche di sviluppo: dal C con lex e yacc, al C++, al perl, passando per TCL e altri linguaggi. Ci siamo resi conto che i linguaggi e gli strumenti tradizionali ponevano alcuni problemi seri e così abbiamo cominciato a pensare ad un linguaggio custom... per realizzare linguaggi custom!
L'obiettivo era quello di consentire a tutti di svilupparsi facilmente linguaggi custom DSL fornendo uno strumento ed una metodologia per farli.

Nel 1999 è nata la prima generazione di questo progetto, che ha risolto molti problemi lasciandone altri ancora aperti. Nel 2008 è iniziato sviluppo della seconda generazione che è sfociata in un prodotto chiamato FormalCapture, che la mia azienda ha reso gratuitamente scaricabile ed utilizzabile per fini non commerciali.

Con FormalCapture vengono affrontati numerosi problemi relativi allo sviluppo di DSL:

1) sintassi - Definire e sopratutto modificare e manutenere un analizzatore sintattico con un linguaggio tradizionale è un lavoraccio. Però anche generatori specifici come lex e yacc non scherzano. Uno dei problemi più grossi e sottovalutati è che si definiscono sintassi pensando di ottenere un risultato mentre invece il loro comportamento effettivo è inatteso e incomprensibilie ai meno esperti. FormalCapture ha un sistema grafico guidato di definizione delle sintassi. La sintassi ha delle (piccole) limitazioni nelle possibili definizioni affinché le sintassi risultanti siano sempre ovvie e prevedibili.

2) trasformazioni - Il punto veramente critico nella realizzazione dei compilatori sono le fasi di trasformazione dei dati dall'input al risultato finale. Queste operazioni hanno a che fare con strutture ad albero che devono essere analizzate e linkate. Il problema con queste strutture è che quando uno implementa il codice per un nodo, la situazione evolutiva presunta dei nodi circostanti a volte non è quella reale: cioè ci si aspetta che il nodo di fianco abbia un dato mentre invece in certe circostanze non è ancora venuto il suo turno di calcolarlo. O magari si scopre a cose fatte che queste necessità a volte si configurano in un loop e sono di fatto irrisolvibili.
FormalCapture introduce nel suo linguaggio ad oggetti FCL un nuovo concetto denominato "programmazione a fasi". Grazie ad esso le operazioni che possono creare risultati inattesi sono impedite dal direttamente compilatore FCL: lo stato degli altri nodi è dichiarato e certo al momento della compilazione.

3) gestione degli errori - Uno dei fatti più sottovalutati è che il grosso del lavoro di un compilatore serio è segnalare gli errori. Nessuno vorrebbe un compilatore che dica solo "errore" e buonanotte. Un buon compilatore segnala l'errore, spiega in maniera comprensibile in cosa consiste e indica dove trovare nel sorgente il punto in cui è avvenuto. FormalCapture gestisce automaticamente gli errori sintattici e fornisce un tracciamento automatico delle stringhe all'interno del programma. Quindi, quando scriveremo "Errore: il termine 'pippo' non è stato definito", la parola "pippo" sarà automaticamente iperlinkata al punto in cui l'utente ha scritto "pippo" nel suo sorgente.

4) sintassi grafica - Le sintassi non devono essere necessariamente testuali: a volte dice più un diagramma che cento righe di testo. Per questo FormalCapture consente la definizione di sintassi formali grafiche, composte da simboli programmabili, linee di connessione e campi di testo formali.

5) separazione codice generante e generato - Nei sorgenti dei compilatori ci sono le istruzioni che generano sottoforma di testo il codice in uscita. Il problema è che entrambi i linguaggi fanno largo uso di keyword, simboli, caratteri di escape e indentazioni che si mischiano in maniera illeggibile: non si capisce più qual'è il sorgente del generatore e quello del codice generato.
FormalCapture fornisce una modalità speciale di inserimento del codice generato, che appare colorato diversamente e non soggetto ad escaping, così che possa essere inserito in chiaro, compreso il corretto indenting.

Ad oggi siamo ormai perfettamente consapevoli della validità del modello operativo implementato in FormalCapture: come collaudo abbiamo realizzato tutti i progetti già fatti in passato impiegando qualche ora e, in certi casi anche pochi minuti, quando i loro predecessori avevano richiesto settimane se non mesi di lavoro.
Ora stiamo introducendo FormalCapture presso diverse comunità ristrette di programmatori per avere il feedback di utenti che non abbiano già una preparazione specifica in merito alla creazione di compilatori per linguaggi DSL, cioè il tipo di utenti a cui FormalCapture è destinato.

Pensiamo anche che possa essere anche un'opportunità di crescita per chi è nel settore, perché lavorare per DSL è talmente vantaggioso che è solo questione di tempo prima che diventi un tipo di esperienza molto ambito.

Pertanto vi invito, se vi va, a scaricare gratuitamente FormalCapture, provarlo con i suoi esempi e farci avere il vostro feedback qui su <i>iprogrammatori.it</i>.

Sperando di non aver abusato del forum e della vostra attenzione con questo lungo post, rimango in attesa dei vostri riscontri e a disposizione per qualunque tipo di domanda.

Saluti
Davide

13 Risposte

  • Re: Sviluppare i propri linguaggi - Perché e come

    Attenzione, mi hanno segnalato che sul sito il link di download era interrotto causa aggiornamento versione online.
    Adesso è stato rimesso a posto a FormalCapture_1.0.17484.msi.

    Mi scuso per l'inconviente e saluti
    Davide
  • Re: Sviluppare i propri linguaggi - Perché e come

    Chiedo scusa per il crossposting: abbiamo selezionato alcune audience e stiamo cercando di coinvolgere la comunità. Anche se il mercato italiano per i nostri prodotti è totalmente inesistente, abbiamo pensato di offrire questa opportunità per primo alla nostra nazione.
    Presto troverete post simili anche su forum internazionali.
    Se l'argomento e la nostra esperienza a disposizione del forum IProgrammatori.it non è gradita o non interessante, possiamo cancellare subito il messaggio e cessare ogni collaborazione.

    Grazie e saluti
    Davide
  • Re: Sviluppare i propri linguaggi - Perché e come

    Scanso alla fuffa da marketting!

    Che grammatiche supporta?

    In che cosa si differenzia da LEX/YACC e AntLR?

    Visto che scrivere una grammatica NON AMBIGUA e' tutt'altro che facile, e non certamente alla portata del programmatore medio, come fa il sistema a risolvere tali situazioni?

    Risposte tecniche ma comprensibili, per la verificabilita'!
  • Re: Sviluppare i propri linguaggi - Perché e come

    migliorabile ha scritto:


    Scanso alla fuffa da marketting!

    Che grammatiche supporta?

    In che cosa si differenzia da LEX/YACC e AntLR?

    Visto che scrivere una grammatica NON AMBIGUA e' tutt'altro che facile, e non certamente alla portata del programmatore medio, come fa il sistema a risolvere tali situazioni?

    Risposte tecniche ma comprensibili, per la verificabilita'!
    Ciao migliorabile.

    FormalCapture supporta una grammatica semplificata con delle regole molto semplici che impediscono l'ambiguità.
    Riassumo le regole in maniera diciamo "informale":
    - i terminali possono essere identificatori, numeri (supporta automaticamente float, interi,hex, ecc.) stringhe quotate ("..."), parole chiave (anche contenti spazi) e free-text, che sono un testo formattato i cui limiti sono gestiti dall'editor interno
    - i non terminali sono la ripetizione (0-infinito) e la choice (a OR b OR c con l'opzione "empty");
    - in un "OR" non ci possono essere più entry con lo stesso "front-end": cioè il primo token deve identificare univocamente la scelta. Ad esempio ("pippo" a | "pippo" b) non è consentito perché l'entry non è univoca;
    - la "coda" degli elementi non deve avere ambiguità: ad esempio [a ("pippo" | empty) "pippo") non è ammesso perché dopo "a", incontrando un "pippo" non è immediatamente noto se si tratti del primo o del secondo "pippo".

    Queste semplici regole, anche se apparentemente restrittive, rendono la grammatica molto prevedibile; inoltre abbiamo verificato che non costituiscono un limite ai linguaggi custom, dato che con piccole modifiche possono essere adeguati. Negli esempi abbiamo anche implementato un compilatore BASIC con VCPU come proof-of-concept.

    L'unico punto critico sono gli operatori: infatti in quasi tutti i linguaggi serve poter costruire operatori con i diritti di precedenza.
    In FormalCapture c'è un costrutto apposta: si indica la regola dell'operando (in genere un "or" tra identificatori, parentesi, numeri, ecc.) e le keyword degli operatori e la loro precedenza.
    Gli operatori possono essere binari infissi LR (come il + della matematica), binari infissi RL (come l'operatore = del C, che ha precedenza da destra a sinistra), unari prefissi (come il ! del C) o unari postfissi (es. x++).

    Spero di aver risposto almeno in parte alle domande.

    A disposizione per altri chiarimenti.
    Ciao
    Davide
  • Re: Sviluppare i propri linguaggi - Perché e come

    Ottimo!

    Ma non e' troppo limitativo?
    Basta veramente poco per ritrovarsi con grammatiche che richiedono piu' di un token di lookhaed.

    Non sono domande banali: proprio in questi giorni mi e' capitato di dover realizzare dei convertitori di formato di scene 3D, ed ho utilizzato PLY (Lex/Yacc per Python, che per quel che dovevo fare andava piu' che bene), e di rognette con produzioni ambigue ne ho trovate diverse.

    E per risovere, ovviamente, ho dovuto riorganizzare le produzioni.

    Banale considerazione: ma perche' non fare un frontend per Lex/Yacc?
  • Re: Sviluppare i propri linguaggi - Perché e come

    migliorabile ha scritto:


    Ottimo!

    Ma non e' troppo limitativo?
    Basta veramente poco per ritrovarsi con grammatiche che richiedono piu' di un token di lookhaed.

    Non sono domande banali: proprio in questi giorni mi e' capitato di dover realizzare dei convertitori di formato di scene 3D, ed ho utilizzato PLY (Lex/Yacc per Python, che per quel che dovevo fare andava piu' che bene), e di rognette con produzioni ambigue ne ho trovate diverse.

    E per risovere, ovviamente, ho dovuto riorganizzare le produzioni.

    Banale considerazione: ma perche' non fare un frontend per Lex/Yacc?
    Ciao migliorabile.

    Innanzitutto considera che questa è la soluzione a cui siamo giunti dopo vent'anni lavorando dapprima con lex/yacc (flex e bison per la precisione), poi scrivendo parser a mano e infine con la prina generazione di un prodotto tutto nostro che usa una grammatica con sintassi di input BNF qualunque.
    Non dimentichiamo lo scopo della missione: noi non abbiamo un linguaggio X e dobbiamo parsarlo. Siamo noi che inventiamo un linguaggio e possiamo anche un po' adattarlo alle limitazioni del parser.
    La quasi totalità dei linguaggi custom che abbiamo incontrato alla fine erano molto semplici o potevano essere resi tali con piccole modifiche.
    Così abbiamo studiato questa generazione di parser: era fondamentale che qualunque cosa si scrivesse, se fosse stata accettata dal programma, non facesse piombare l'operatore nell'incubo grammaticale in cui puntualmente si cadeva con gli altri strumenti.
    Non dovevano più esserci giornate spese a leggere il dump di un parse per capire perché in quel frangente aveva preso Roma per Toma, specialmente se l'utente non è un esperto in materia.

    Il nostro parser non può leggere il C (se vuoi ti spiego anche perché) ma è abbastanza potente per parsare il BASIC o il Pascal, per citare due linguaggi esistenti.

    Perché non integrarsi con lex/yacc? A parte le considerazioni sulla grammatica viste sopra, il parsing è solo la punta dell'iceberg. Perché il sistema sia efficace, tutta l'informazione letta dal parser deve essere incanalata in un processo di trasformazione integrato. Ad ogni stringa letta deve corrispondere un dato nel sistema che possa essere processato e trasformato e qui è ancora un'altra storia...

    Ciao
    Davide
  • Re: Sviluppare i propri linguaggi - Perché e come

    Grazie, per la spegazione.
    Mi e' chiara anche la questione delle limitazioni sui linguaggi parsabili.
  • Re: Sviluppare i propri linguaggi - Perché e come

    migliorabile ha scritto:


    Grazie, per la spegazione.
    Mi e' chiara anche la questione delle limitazioni sui linguaggi parsabili.
    Ciao migliorabile.

    Uno dei problemi di noi softwareisti è che siamo spesso mossi dall'esigenza di fare vedere che siamo bravi, per cui realizziamo questo:


    Quando per l'utente sarebbe più che sufficiente questo:


    La vera difficoltà del progettista non è fare qualcosa di tecnicamente strabiliante, ma fornire un modello opearativo che consenta di fare tutto il necessario con il minimo di opzioni, complicazioni e possibilità di errori possibile.
    La prima volta che usai un Mac negli anni '80 mi misi a ridere perché tutto quello che facevo con i batch del DOS o con la shell UNIX non si poteva fare; se volevo rinominare 100 files, dovevo cliccarli uno per uno e rinominarli a mano.
    Solo molti anni dopo mi sono reso conto che quei comandi così ridicolmente elementari erano tali solo per una ristretta elìte di smanettoni ma chiari e comprensibili per un'enorme moltitudine di non addetti ai lavori. Tant'è che oggi il Mac ha la shell CLI, ma il 99% degli utenti non lo sa e non ne ha mai sentito l'esigenza.

    Ciaoo
    Davide

    Ciaoo
    Davide
  • Re: Sviluppare i propri linguaggi - Perché e come

    Scusa, ma il testo postato e' markettttting di basso livello, forse adatto all'ufficio acquisti (ma neanche), non certamente al Computer Scientist, o se vuoi, al professionista che si occupa di sviluppo software:

    errata la terminologia. Solo una persona che non si occupa di informatica utilizzerebbe il termine softwarista

    errato il confronto tra Mac e Dos (prima versione): e' come confrontare una Panda con una ruspa!

    se vuoi e' errato anche l'esempio dei due volanti: compito del professionista e' realizzare il primo e configurarlo in modo che l'utente finale veda il secondo.
    E la bravura sta' prioprio in questo: avere un sistema configurabile ed adattabile a tutte le necessita' dell'utente finale, e di diversi utenti finali, con sforzo minimo.
    Oltre al fatto che il professionista e' in grado di prevedere anche le successive necessita' dell'utente, che sono ovvia conseguenza del lavoro svolto.

    Questo per dire: state attenti a chi avete come interlocutore, perche' questo ha impatto su come dovreste presentate il vostro lavoro.


    Questa mi era piaciuta di piu', comunque:

    http://adcorsi.com/analisidisegnowp/wp-content/uploads/2013/08/tireswing-altalena.gif

    Vabbe'!
  • Re: Sviluppare i propri linguaggi - Perché e come

    Provo a riformulare il concetto, magari mi spiego meglio.

    Lex e yacc leggono tutto. Però ci si incasina con le produzioni e si perde tempo a capire cosa non va e a metterle a posto. L'hai detto tu e l'hanno riscontrato tutti quelli che in vita loro hanno usato lex e yacc.
    Con la nostra grammatica si legge non tutto ma tanto. Però le produzioni non si incasinano mai per via dei limiti progettuali a loro imposte.
    Se il "tanto" di cui sopra è sufficiente a fare tutti i lavori per cui il prodotto è stato concepito, allora la seconda grammatica è vincente sulla prima anche se meno potente. Se invece non è sufficiente, allora il prodotto è inutile.
    E' più facile realizzare uno strumento che faccia tutto con i rischi annessi e connessi che uno con delle limitazioni che riducano i rischi senza impedirne la funzionalità completa per l'ambito in cui è destinato.
    Affermare che bisogna offrire la massima potenza possibile sempre e comunque, secondo me, è un errore.

    P.S.: non so cosa ci trovi di male nella parola "softwarista", ci si è sempre definiti così in contrapposizione agli "hardwaristi", che erano quelli che facevano le schede su cui giravano i nostri software.

    Ciao
  • Re: Sviluppare i propri linguaggi - Perché e come

    Softwaristi e hardwaristi ? Termini da responsabile commerciale o da addetto ad un PLC, non da informatici ... ... confermo ...
  • Re: Sviluppare i propri linguaggi - Perché e come

    oregon ha scritto:


    Softwaristi e hardwaristi ? Termini da responsabile commerciale o da addetto ad un PLC, non da informatici ... ... confermo ...
    Quindi riassumendo, l'auspicato confronto tecnico, dopo il prevedibile lex e yacc, si è ridotto ad una mera ironia sui termini utilizzati?
    Un modello grammaticale nuovo che supporta anche le sintassi grafiche oltre che testuali, un modello ad oggetti che protegge al momento della compilazione (e non a runtime) gli attributi delle singole istanze in base al loro stato evolutivo (e non gli attributi tra classi, come i canonici private & co.), un sistema di editing che consente di mantenere nello stesso sorgente codice generante e codice generato...
    Nulla: il massimo che l'informatico riesce a fare è confermarmi che "softwarista" è un termine desueto. Grazie di avermi aggiornato, ne terrò conto.

    Ciao
    Davide
  • Re: Sviluppare i propri linguaggi - Perché e come

    A mio parere , sviluppare un proprio linguaggio puo' essere interessante
    Personalmente sto sviluppando un compilatore - interprete e credo che sarà più interprete che compilatore .Cmq bisogna implementare diversi design pattern , progettare bene il framework con classi astratte , interfacce ect (in java) , scrivere gli oggetti scanner , parser , token ect
    Non e' una cosa da poco .Io sto implementando una specie di pascal , in realtà ST che è un linguaggio per plc e un altro pseudolinguaggio che usero' per Arduino da scheda embedded UDOO.
    In sostanza tramite l'interprete accedo alle funzioni che ho stabilito io , di itnerfaccia all'hw, e poi da queste un wrapping alle API ....
    E' un bel impegno ma se riesco credo di raggiugnere potenzialità non indifferenti .Si possono creare sub set di linguaggio gia esistenti o crearne di nuovi , mirati per particolari applicazioni
    ciao
    Walter
Devi accedere o registrarti per scrivere nel forum
13 risposte