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 exprNa 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 OPdetermina 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 OPa interpretação seria A OP (B OP C). A declaração
%nonassoc OPdeterminaria 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 OP2tê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.