Nome corretto per un parser di discesa ricorsivo che utilizza loop per gestire la ricorsione a sinistra?


8

Questa grammatica è ricorsiva:

Expression  ::= AdditionExpression

AdditionExpression  ::=
    MultiplicationExpression
        | AdditionExpression '+' MultiplicationExpression
        | AdditionExpression '-' MultiplicationExpression

MultiplicationExpression    ::=
    Term
        | MultiplicationExpression '*' Term
        | MultiplicationExpression '/' Term

Term    ::=
    Number
        | '(' AdditionExpression ')'

Number  ::=
    [+-]?[0-9]+(\.[0-9]+)?

Quindi, in teoria, la discesa ricorsiva non funzionerà. Ma sfruttando le proprietà della grammatica secondo cui ciascuna regola ricorsiva a sinistra corrisponde a un livello di precedenza specifico e che lo sguardo di un singolo token è sufficiente per scegliere la produzione corretta, le regole ricorsive a sinistra possono essere analizzate individualmente con mentre loop.

Ad esempio, per analizzare il terminale non AdditionExpression, questo pseudocodice è sufficiente:

function parse_addition_expression() {
    num = parse_multiplication_expression()
    while (has_token()) {
        get_token()
        if (current_token == PLUS)
            num += parse_multiplication_expression()
        else if (current_token == MINUS)
            num -= parse_multiplication_expression()
        else {
            unget_token()
            return num
        }
    }
    return num
}

Qual è il nome corretto per questo tipo di parser? Questo articolo informativo si riferisce solo ad esso come alla "soluzione classica": https://www.engr.mun.ca/~theo/Misc/exp_parsing.htm

Ci deve essere un nome proprio per questo tipo di parser.


Per me non è una specie di parser, è solo l'applicazione della rimozione della ricorsione sinistra combinata con un parser di discesa ricorsivo. Vedi questa domanda per una tecnica per rimuovere la ricorsione sinistra.
— AProgrammer

Penso che potresti avere ragione. Assomiglia a un equivalente di runtime dell'algoritmo di rimozione della ricorsione sinistra.
— user71015

1
Non utilizzare la casella "rispondi" per pubblicare commenti o altre osservazioni. Se crei un account , manterrai l'accesso e sarai in grado di accettare la risposta che ti ha aiutato di più. Se hai inserito un'email e hai perso l'accesso, puoi recuperare l'accesso . Se non hai inserito un indirizzo email e non hai accesso al browser / ai cookie che hai usato per pubblicare la domanda, probabilmente sei sfortunato. Nessun altro può accettare la risposta per te, nemmeno i moderatori.
— DW

Risposte:


11

È solo un parser LL (1) implementato con discesa ricorsiva.

Inizia con:

AdditionExpression  ::=
    MultiplicationExpression
        | AdditionExpression '+' MultiplicationExpression
        | AdditionExpression '-' MultiplicationExpression

applica la rimozione della ricorsione a sinistra per ottenere una grammatica LL (1):

AdditionExpression  ::= 
    MultiplicationExpression AdditionExpressionTail

AdditionExpressionTail ::=
        | '+' MultiplicationExpression AdditionExpressionTail
        | '-' MultiplicationExpression AdditionExpressionTail

scrivere le funzioni corrispondenti:

function parse_AdditionExpression() {
    parse_MultiplicationExpression()
    parse_AdditionExpressionTail()
}

function parse_AdditionExpressionTail() {
    if (has_token()) {
        get_token()
        if (current_token == PLUS) {
            parse_MultiplicationExpression()
            parse_AdditionExpressionTail()
        } else if (current_token == MINUS) {
            parse_MultiplicationExpression()
            parse_AdditionExpressionTail()
        } else {
            unget_token()
        }
    }
}

rimuovere la ricorsione della coda:

function parse_AdditionExpressionTail() {
    while (has_token()) {
        get_token()
        if (current_token == PLUS)
            parse_MultiplicationExpression()
        else if (current_token == MINUS)
            parse_MultiplicationExpression()
        else {
            unget_token()
            return
        }
    }
}

in linea:

function parse_AdditionExpression() {
    parse_MultiplicationExpression()
    while (has_token()) {
        get_token()
        if (current_token == PLUS)
            parse_MultiplicationExpression()
        else if (current_token == MINUS)
            parse_MultiplicationExpression()
        else {
            unget_token()
            return
        }
    }
}

e devi solo aggiungere l'elaborazione semantica per ottenere la tua funzione.


6

Vuoi dare un'occhiata a LL (K) analisi . L'articolo di Wikipedia è per lo più inutile, ma è fondamentalmente una discesa ricorsivaK simboli lookahead.

C'è anche LL (*) che consente lookahead illimitato.

Vedi qui per una panoramica completa di quanto sia potente questa classe di parser.


1
Non vedo come questo sia correlato. Il codice non utilizza più di un simbolo di look-ahead.
— AProgrammer

@AProgrammer Quindi è un parser LL (1) o strettamente correlato.
— Raffaello

È un parser LL (1). Ho ampliato il mio commento in una risposta.
— Programmatore

2
@AProgrammer Non vedo come fosse necessaria una seconda risposta. LL (1) è LL (k) per k = 1 (non è così ovvio?). Ma bene.
— Raffaello
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.