Sto scrivendo un parser per un linguaggio di markup che ho creato (scrivendo in Python, ma non è molto rilevante per questa domanda - infatti se questa sembra una cattiva idea, mi piacerebbe un suggerimento per un percorso migliore) .
Sto leggendo qui i parser: http://www.ferg.org/parsing/index.html e sto lavorando alla scrittura del lexer che, se ho capito bene, dovrebbe dividere il contenuto in token. Quello che ho difficoltà a capire è quali tipi di token dovrei usare o come crearli. Ad esempio, i tipi di token nell'esempio a cui sono collegato sono:
- CORDA
- IDENTIFIER
- NUMERO
- WHITESPACE
- COMMENTO
- EOF
- Molti simboli come {e (contano come il proprio tipo di token
Il problema che sto riscontrando è che i tipi di token più generali mi sembrano un po 'arbitrari. Ad esempio, perché STRING ha il proprio tipo di token separato rispetto a IDENTIFICATORE. Una stringa può essere rappresentata come STRING_START + (IDENTIFIER | WHITESPACE) + STRING_START.
Questo potrebbe anche avere a che fare con le difficoltà della mia lingua. Ad esempio, le dichiarazioni delle variabili sono scritte come {var-name var value}
e distribuite con {var-name}
. Sembra '{'
e '}'
dovrebbe essere i propri token, ma sono nome_var e VAR_VALUE tipi di token ammissibili, o sarebbero questi entrambi cadono sotto IDENTIFIER? Inoltre, VAR_VALUE può effettivamente contenere spazi bianchi. Lo spazio bianco dopo var-name
viene utilizzato per indicare l'inizio del valore nella dichiarazione. Qualsiasi altro spazio bianco fa parte del valore. Questo spazio bianco diventa il suo token? Lo spazio bianco ha solo quel significato in questo contesto. Inoltre, {
potrebbe non essere l'inizio di una dichiarazione variabile .. dipende dal contesto (c'è di nuovo quella parola!). {:
avvia una dichiarazione di nome e{
può anche essere usato come parte di un certo valore.
Il mio linguaggio è simile a Python in quanto i blocchi sono creati con rientro. Stavo leggendo come Python utilizza il lexer per creare token INDENT e DEDENT (che servono più o meno come cosa {
e }
farebbero in molte altre lingue). Python afferma di essere privo di contesto, il che significa per me che almeno il lexer non dovrebbe preoccuparsi di dove si trova nello stream durante la creazione di token. Come fa il lexer di Python a sapere che sta costruendo un token INDENT di una lunghezza specifica senza conoscere i caratteri precedenti (ad esempio che la riga precedente era una nuova riga, quindi iniziare a creare gli spazi per INDENT)? Lo chiedo perché devo sapere anche questo.
La mia ultima domanda è la più stupida: perché è persino necessario un lexer? Mi sembra che il parser possa andare carattere per carattere e capire dove si trova e cosa si aspetta. Il lexer aggiunge il vantaggio della semplicità?