next up previous contents
Next: Manipulação das sentenças reconhecidas Up: Geradores de analisadores sintáticos Previous: Geradores de analisadores sintáticos   Sumário

Especificação da gramática

O arquivo de entrada para yacc, que por convenção recebe a extensão .y, é estruturado em três seções. Como na definição de arquivos lex, essas três seções -- definições, regras da gramática e código do usuário -- são separadas pelos símbolos %%.

A especificação das regras da gramática utiliza uma notação próxima de BNF (Seção 3.1.5). Cada produção é expressa na forma

   simb  :   exp  ;
onde simb é um símbolo não terminal e exp é a sua expansão em termos de outros símbolos da gramática. A expansão pode conter símbolos terminais e não-terminais, que por convenção são representados usando letras maiúsculas e minúsculas, respectivamente.

Pelas características de gramáticas livres de contexto, a expansão pode ser recursiva, isto é, conter o próprio símbolo que está sendo definido, como em

   expr : expr '+' expr ;
Porém, pelo menos uma expansão para esse símbolo deve ser não-recursiva:
   expr : IDENT ;
Em caso de definição recursiva, pelas características do analisador gerado (LR(1)) recomenda-se optar quando possível pela recursão à esquerda.

Produções para um mesmo símbolo podem ser agrupadas usando o símbolo '|',

  expr  :  expr + expr
        |  IDENT
        ;

Expansões para a string vazia podem ser definidas; por convenção e para tornar mais clara a definição, essa expansão é destacada na forma de um comentário C:

  retv  :  /* empty */
        |  expr
        ;

O símbolo sentencial da gramática pode ser estabelecido na seção de definições através da declaração start, como em

  %start  expr
Na ausência dessa declaração, o símbolo não-terminal cuja expansão é definida na primeira produção da seção de regras da gramática é assumido ser o símbolo sentencial.

Outros tipos de declaração que podem estar presentes na primeira seção são declarações em C, colocadas entre os símbolos %{ e %}, e a definição dos nomes de tipos de tokens, os quais serão usados posteriormente nas expansões das produções.

Tokens que são representados por um único caráter, como '+' ou ';', não precisam ser declarados e podem ser usados dessa forma (como constantes do tipo caráter em C) nas expansões; os demais tokens precisam ser explicitamente declarados. Para tanto, a declaração token pode ser utilizada, como em

  %token IDENT

Alternativamente, tokens para operadores podem ser definidos com uma especificação de associatividade usando, ao invés de token, as declarações left, right ou nonassoc. Uma declaração

  %left OP
determina que uma expressão A OP B OP C será interpretada como (A OP B) OP C, enquanto que se a declaração tivesse sido
  %right OP
a interpretação seria A OP (B OP C). A declaração
  %nonassoc OP
determinaria que a expressão A OP B OP C estaria incorreta, pois o operador não é associativo.

A precedência dos operadores também é definida através dessas declarações. Operadores definidos através da mesma linha de declaração, como

  %left OP1 OP2
têm a mesma precedência. Para aqueles definidos em linhas distintas, as últimas declarações têm maior precedência.

O símbolo terminal error é pré-definido, podendo ser utilizado como a última expansão de um símbolo caso a aplicação deseje determinar um curso de ação específico em uma situação de não-reconhecimento de uma sentença a partir das expansões previamente definidas para o símbolo.


next up previous contents
Next: Manipulação das sentenças reconhecidas Up: Geradores de analisadores sintáticos Previous: Geradores de analisadores sintáticos   Sumário
Ivan L. M. Ricarte 2003-02-14