2009-07-23 7 views
8

Buongiorno!Come posso costruire una grammatica pulita, simile a Python in ANTLR?

Come è possibile creare una semplice grammatica ANTLR che gestisca le espressioni su più righe senza la necessità né di punto e virgola né di barre inverse?

sto cercando di scrivere un semplice DSL per le espressioni:

# sh style comments 
ThisValue = 1 
ThatValue = ThisValue * 2 
ThisOtherValue = (1 + 2 + ThisValue * ThatValue) 
YetAnotherValue = MAX(ThisOtherValue, ThatValue) 

Nel complesso, voglio la mia domanda per fornire lo script con alcuni valori denominati iniziali ed estrarre il risultato finale. Tuttavia, mi sto bloccando sulla sintassi. Mi piacerebbe supportare più espressioni di linea come la seguente:

# Note: no backslashes required to continue expression, as we're in brackets 
# Note: no semicolon required at end of expression, either 
ThisValueWithAReallyLongName = (ThisOtherValueWithASimilarlyLongName 
           +AnotherValueWithAGratuitouslyLongName) 

Ho iniziato con una grammatica ANTLR in questo modo:

exprlist 
    : (assignment_statement | empty_line)* EOF! 
    ; 
assignment_statement 
    : assignment NL!? 
    ; 
empty_line 
    : NL; 
assignment 
    : ID '=' expr 
    ; 

// ... and so on 

Sembra semplice, ma io sono già nei guai con la a capo:

warning(200): StackOverflowQuestion.g:11:20: Decision can match input such as "NL" using multiple alternatives: 1, 2 
As a result, alternative(s) 2 were disabled for that input 

Graficamente, in org.antlr.works.IDE:

Decision Can Match NL Using Multiple Alternatives http://img.skitch.com/20090723-ghpss46833si9f9ebk48x28b82.png

ho preso a calci la grammatica in giro, ma finisco sempre con violazioni di comportamento previsto:

  • Un ritorno a capo non è richiesto alla fine del file
  • righe vuote sono accettabili
  • Tutto in una riga da un cancelletto in avanti viene scartato come commento
  • Le assegnazioni terminano con la fine della riga, non il punto e virgola
  • Le espressioni possono estendersi su più righe se racchiuse tra parentesi quadre ets

Sono in grado di trovare grammatiche ANTLR di esempio con molte di queste caratteristiche. Trovo che quando li abbasso per limitare la loro espressività a ciò di cui ho bisogno, finisco per rompere qualcosa. Gli altri sono troppo semplici e li rompo mentre aggiungo espressività.

Quale angolo devo prendere con questa grammatica? Puoi indicare esempi che non sono linguaggi completi o completi di Turing?

risposta

6

avrei lasciate che il vostro tokenizer fare il lavoro pesante, piuttosto che mescolare le regole di nuova riga nella tua grammatica:

  • Count parentesi tonde, quadre e graffe, e non generano gettoni NL mentre ci sono gruppi non chiusi . Ciò ti darà continuazioni di linea gratis senza che la tua grammatica sia più saggia.

  • Genera sempre un token NL alla fine del file anche se l'ultima riga termina con un carattere '\n', quindi non devi preoccuparti di un caso speciale di un'istruzione senza NL. Dichiarazioni sempre terminano con una NL.

Il secondo punto sarebbe consentono di semplificare la grammatica a qualcosa di simile:

exprlist 
    : (assignment_statement | empty_line)* EOF! 
    ; 
assignment_statement 
    : assignment NL 
    ; 
empty_line 
    : NL 
    ; 
assignment 
    : ID '=' expr 
    ; 
+0

Ora ho bisogno di capire come ottenere il tokenizer per fare questo sollevamento pesi. Tornando alla documentazione, immagino. :) –

+0

John, mi sfugge ancora. Qual è la sintassi della grammatica ANTLR in modo che il tokenizzatore inserisca NL prima di EOF? –

+0

+1 Per finire sempre con una nuova linea, rende le cose molto più pulite. Grazie. – Craz

0

ne dici di questo?

exprlist 
    : (expr)? (NL+ expr)* NL!? EOF! 
    ; 
expr 
    : assignment | ... 
    ; 
assignment 
    : ID '=' expr 
    ; 
0

presumo si è scelto di fare NL facoltativa, perché l'ultima istruzione nel codice di ingresso non deve terminare con un ritorno a capo.

Mentre ha molto senso, si sta rendendo la vita molto più difficile per il parser. I token Separator (come NL) dovrebbero essere apprezzati, in quanto disambiguano e riducono la possibilità di conflitti.

Nel vostro caso, il parser non sa se deve analizzare "assignment NL" o "assignment empty_line". Ci sono molti modi per risolverlo, ma la maggior parte di essi è solo un sussidio di fascia per una scelta progettuale imprudente.

La mia raccomandazione è un trucco innocente: rendere NL obbligatorio e aggiungere sempre NL alla fine del flusso di input!

Può sembrare un po 'sgradevole, ma in realtà ti farà risparmiare un sacco di mal di testa in futuro.