A primeira coisa a se fazer em uma especificação léxica e saber quais os tokens que deverão ser reconhecidos pelo analisador.
Como neste exemplo vão ser precisos números, operadores e parênteses, já é possível ter uma idéia de quais tokens serão precisos:
Antes de especificar de fato so tokens, é preciso notar que a especificação léxica é dividida em duas partes: Definições Regulares e Definição dos Tokens.
Os tokens são definidos na segunda parte. Nas definições regulares são definidas expressões auxiliáres, para serem utilizadas na definição dos tokens.
Pare este exemplo será feira apenas a seguinte definição:
D : [0-9]
Esta definição diz que D (digito) é qualquer letra entre 0 e 9.
Os tokens para este exemplo são definidos da seguinte forma:
"+"
"-"
"*"
"/"
"("
")"
NUMERO : {D}+
: [\s\t\n\r]*
Primeiro são definidos os operadores. Uma grupo de caracteres entre aspas define um tokens cuja representação é a de string entre aspas.
Em seguida é definido NUMERO. Para este token é fornecida uma expressão regular para representá-lo. Nesta expressão é utilizada a definição regular anteriormente definida. Um NUMERO é um D (digito) repetido uma ou mais vezes. Para utilizar uma definição deve-se colocá-la entre { e }.
Por fim é descrita uma expressão sem um token associado. Isto indica ao analisador que ele deve ignorar esta expressão sempre que encontrá-la. Neste caso devem ser ignorados espaço em branco (\s), tabulação (\t) e quebra de linha (\n e \r).
Esta tabela ilustra as possíbilidades de expressões regulares. Quaisquer combinações entre estes padrões é possível. Espaços em branco são ignorados (exceto entre " e ").
a | reconhece a |
ab | reconhece a seguido de b |
a|b | reconhece a ou b |
[abc] | recohece a, b ou c |
[^abc] | reconhece qualquer caractere, exceto a, b e c |
[a-z] | reconhece a, b, c, ... ou z |
a* | reconhece zero ou mais a's |
a+ | reconhece um ou mais a's |
a? | reconhece um a ou nenhum a. |
(a|b)* | reconhece qualquer número de a's ou b's |
. | reconhece qualquer caractere, exceto quebra de linha |
\123 | reconhace o caractere ASCII 123 (decimal) |
Os operadores posfixos (*, + e ?) tem prioridade máxima. Em seguida está a concatenação e por fim a união ( | ). Parenteses podem ser utulisador para agrupar símbolos e driblar prioridades.
Os caracteres " \ | * + ? ( ) [ ] { } . ^ -
possuem significado especial. Para utilizá-los como caracteres normais
deve-se precedê-los por \, ou colocá-los entre " e ".
Qualquer sequancia de caractateres entre " e " é tratada como
caracteres ordinários.
\+ | reconhece + |
"+*" | reconhece + seguido de * |
"a""b" | reconhece a, seguido de ", seguido de b |
\" | reconhece " |
Existem ainda os caracteres não imprimíveis, representados por sequancias de escape
\n | Line Feed |
\r | Carriage Return |
\s | Espaço |
\t | Tabulação |
\b | Backspace |
\e | Esc |
\XXX | O caractere ASCII XXX (XXX é um número decimal) |
Pode-se definir ainda um tokens como sendo um caso particular de um outro token. Por exemplo:
ID : [a-z
A-Z][a-z A-Z 0-9]* //letra seguida de zero
ou mais letras ou dígito
BEGIN = ID : "begin"
END = ID :
"end"
WHILE = ID : "while"
Assim define-se que BEGIN, END e WHILE são casos especiais de ID. Sempre que o analisador encontrar um ID ele procura na lista de casos especiais para ver se este ID não é um BEGIN ou um WHILE.