Comando Linux lex
Em sistemas operacionais do tipo Unix, o comando lex gera programas para executar tarefas lexicais .
Descrição
O utilitário lex gera programas C para serem usados no processamento lexical da entrada de caracteres e que pode ser usado como uma interface para o yacc . Os programas C são gerados a partir do código fonte lex e estão em conformidade com o padrão ISO C. Normalmente, o utilitário lex grava o programa que gera no arquivo lex.yy.c. O estado desse arquivo não é especificado se o lex sair com um status de saída diferente de zero. Consulte DESCRIÇÃO ESTENDIDA para obter uma descrição completa do idioma de entrada lex .
Sintaxe
lex [-cntv] [-e | -w] [-V -Q [y | n]] [ arquivo ] ...
Opções
-c | Indique ação no idioma C (esse é o padrão). |
-e | Gera um programa que pode manipular caracteres EUC (não pode ser usado com a opção -w ). yytext [] é do tipo char não assinado [] . |
-n | Suprime o resumo das estatísticas geralmente escritas com a opção -v . Se nenhum tamanho de tabela for especificado no código-fonte lex e a opção -v não for especificada, então -n estará implícito. |
-t | Escreva o programa resultante na saída padrão em vez de lex.yy.c. |
-v | Escreva um resumo das estatísticas lex no erro padrão. Se os tamanhos de tabela forem especificados no código-fonte lex e se a opção -n não for especificada, a opção -v poderá estar ativada. |
-W | Gere um programa que possa manipular caracteres EUC (não pode ser usado com a opção -e ). Diferentemente da opção -e , o yytext [] é do tipo wchar_t [] . |
-V | Imprima as informações da versão com erro padrão. |
-Q [ y | n ] | Imprima as informações da versão no arquivo de saída lex.yy.c usando -Qy . A opção -Qn não imprime informações da versão e é o padrão. |
Arquivo | O nome do caminho de um arquivo de entrada. Se mais de um arquivo for especificado, todos os arquivos serão concatenados para produzir um único programa lex. Se nenhum operando de arquivo for especificado, ou se um operando de arquivo for – , a entrada padrão será usada. |
Sintaxe
Cada arquivo de entrada contém o código-fonte lex , que é uma tabela de expressões regulares com ações correspondentes na forma de fragmentos do programa C.
Quando o lex.yy.c é compilado e vinculado à biblioteca lex (usando o operando -ll com c89 ou cc ), o programa resultante lê a entrada de caracteres da entrada padrão e a particiona em strings que correspondem às expressões especificadas.
Quando uma expressão é correspondida, essas ações ocorrem:
- A sequência de entrada correspondente foi deixada em yytext como uma sequência terminada em nulo; yytext é uma matriz de caracteres externa ou um ponteiro para uma cadeia de caracteres. O tipo pode ser selecionado explicitamente usando as declarações % array ou % apontador , mas o padrão é % array .
- O número inteiro externo yyleng é definido como o comprimento da sequência correspondente.
- O fragmento de programa correspondente da expressão, ou ação, é executado.
Durante a correspondência de padrões, lex pesquisa no conjunto de padrões a única correspondência mais longa possível. Entre as regras que correspondem ao mesmo número de caracteres, a regra dada primeiro será escolhida.
O formato geral da fonte lex é:
Definições %% Rules %% Sub-rotinas do usuário
O primeiro %% é necessário para marcar o início das regras (expressões regulares e ações); o segundo %% será necessário apenas se as sub-rotinas do usuário seguirem.
Qualquer linha na seção ” Definições no lex ” que comece com um caractere em branco será assumida como um fragmento do programa C e será copiada para a área de definição externa do arquivo lex.yy.c. Da mesma forma, qualquer coisa na seção “Definições no lex” incluída entre as linhas delimitadoras contendo apenas % { e %} também será copiada inalterada para a área de definição externa do arquivo lex.yy.c.
Qualquer entrada desse tipo (começando com um caractere em branco ou dentro das linhas delimitadoras % { e %} ) que apareça no início da seção ” Rules ” antes que qualquer regra seja especificada será gravada em lex.yy.c após as declarações de variáveis para o função yylex e antes da primeira linha de código no yylex . Portanto, as variáveis de usuário locais para o yylex podem ser declaradas aqui, bem como o código do aplicativo para ser executado após a entrada no yylex .
A ação executada pelo lex ao encontrar qualquer entrada que comece com um caractere em branco ou dentro das linhas delimitadoras % { e %} que aparecem na seção “Regras”, mas após uma ou mais regras é indefinida. A presença de tal entrada pode resultar em uma definição incorreta da função yylex .
Definições no lex
As definições em lex aparecem antes do primeiro delimitador %% . Qualquer linha nesta seção que não contenha entre % { e %} linhas e que não comece com um caractere em branco é assumida para definir uma sequência de substituição de lex . O formato dessas linhas é:
nome substituto
Se um nome não atender aos requisitos para identificadores no padrão ISO C, o resultado será indefinido. O substituto da string substituirá a string { name } quando for usada em uma regra. A cadeia de nomes é reconhecida nesse contexto apenas quando os colchetes são fornecidos e quando não aparece em uma expressão entre colchetes ou entre aspas duplas.
Nesta seção, qualquer linha iniciada com um caractere % (sinal de porcentagem) e seguida por uma palavra alfanumérica iniciada por s ou S define um conjunto de condições de início. Qualquer linha que comece com % seguida por uma palavra que comece com x ou X define um conjunto de condições iniciais exclusivas. Quando o scanner gerado estiver no estado % s , os padrões sem o estado especificado também estarão ativos; no estado % x , esses padrões não estarão ativos. O restante da linha, após a primeira palavra, é considerado um ou mais nomes de condições de início separadas por caracteres em branco. Os nomes das condições de início são construídos da mesma maneira que os nomes de definição. As condições de início podem ser usadas para restringir a correspondência de expressões regulares a um ou mais estados, conforme descrito em Expressões regulares no lex .
As implementações aceitam uma das duas declarações mutuamente exclusivas a seguir na seção Definições no lex:
% array | Declare o tipo de texto yy como uma matriz de caracteres terminada em nulo . |
% ponteiro | Declare o tipo de texto yy como um ponteiro para uma cadeia de caracteres terminada em nulo. |
Nota: Ao usar a opção de ponteiro% , você também não pode usar a função yyless para alterar o texto yy .
% array é o padrão. Se % array for especificado (ou nem % array nem % ponteiro forem especificados), a maneira correta de fazer uma referência externa ao yyext é com uma declaração no formato:
texto de char externo []
Se % ponteiro for especificado, a referência externa correta será no formato:
extern char * yytext;
O lex aceitará declarações para definir certos tamanhos internos de tabela. As declarações são mostradas na tabela a seguir.
declaração | descrição | padrão |
---|---|---|
% pn | Número de posições | 2500 |
% nn | Número de estados | 500 |
%a | Número de transições | 2000 |
% pt | Número de nós da árvore de análise | 1000 |
% kn | Número de classes de caracteres compactados | 10000 |
%em | Tamanho da matriz de saída | 3000 |
Os programas gerados pelo lex precisam da opção -e ou -w para manipular a entrada que contém caracteres EUC de conjuntos de códigos suplementares. Se nenhuma dessas opções for especificada, o texto yy será do tipo char [] e o programa gerado poderá manipular apenas caracteres ASCII .
Quando a opção -e é usada, o texto yy é do tipo unsigned char [] e yyleng fornece o número total de bytes na cadeia correspondente. Com essa opção, as macros input () , unput ( c ) e output ( c ) devem executar uma E / S baseada em bytes da mesma maneira que no lex ASCII comum. Duas outras variáveis estão disponíveis com a opção -e , yywtext e yywleng , que se comportam da mesma forma que yytext e yyleng na opção -w .
Quando a opção -w é usada, o texto yy é do tipo wchar_t [] e yyleng fornece o número total de caracteres na sequência correspondente. Se você fornecer macros de entrada () , de saída (c) ou de saída (c) com esta opção, elas deverão retornar ou aceitar caracteres EUC na forma de caracteres largos (wchar_t) . Isso permite uma interface diferente entre o seu programa e os lex internals, para agilizar alguns programas.
Regras em lex
As regras nos arquivos de origem lex são uma tabela na qual a coluna da esquerda contém expressões regulares e a coluna da direita contém ações (fragmentos do programa C) a serem executadas quando as expressões são reconhecidas.
Ação ERE Ação ERE ...
A parte da expressão regular estendida (ERE) de uma linha será separada da ação por um ou mais caracteres em branco. Uma expressão regular contendo caracteres em branco é reconhecida sob uma das seguintes condições:
- A expressão inteira aparece entre aspas duplas.
- Os caracteres em branco aparecem entre aspas duplas ou colchetes.
- Cada caractere em branco é precedido por um caractere de barra invertida.
Sub-rotinas de usuário em lex
Qualquer coisa na seção de sub-rotinas do usuário será copiada para lex.yy.c após o yylex .
Expressões regulares em lex
O utilitário lex suporta o conjunto de expressões regulares estendidas (EREs), com as seguintes exceções:
… | Qualquer cadeia de caracteres entre aspas duplas representará os caracteres dentro das aspas duplas como eles mesmos, exceto que as escapes de barra invertida (que aparecem na tabela a seguir) são reconhecidas. Qualquer sequência de escape de barra invertida é finalizada pela cotação de fechamento. Por exemplo, “\ 01” “1” representa uma única seqüência de caracteres: o valor octal 1 seguido pelo caractere 1 . |
<estado> r , r | A expressão regular r será correspondida apenas quando o programa estiver em uma das condições de início indicadas por estado , estado1 e assim por diante. Para mais informações, consulte Ações no lex . Como uma exceção às convenções tipográficas do restante deste documento, neste caso, < state > não representa uma meta-variável, mas os caracteres literais entre colchetes angulares que cercam um símbolo. A condição inicial é reconhecida como tal apenas no início de uma expressão regular. |
r / x | A expressão regular r será correspondida apenas se for seguida por uma ocorrência da expressão regular x . O token retornado em yytext corresponderá apenas a r . Se a parte à direita de r corresponder ao início de x , o resultado não será especificado. A expressão r não pode incluir contexto posterior adicional ou o operador $ (match-end-of-line); x não pode incluir o operador ^ (coincidir com o início da linha), nem o contexto final, nem o operador $ . Ou seja, apenas uma ocorrência do contexto à direita é permitida em uma expressão regular lex , e o operador ^ pode ser usado apenas no início dessa expressão. Uma restrição adicional é que o operador de contexto à direita / (barra) não pode ser agrupado entre parênteses. |
{ name } | Quando name é um dos símbolos de substituição da seção Definições, a cadeia de caracteres, incluindo os colchetes, será substituída pelo valor de substituição. O valor substituto será tratado na expressão regular estendida como se estivesse entre parênteses. Nenhuma substituição ocorrerá se { name } ocorrer dentro de uma expressão entre colchetes ou entre aspas duplas. |
Dentro de um ERE, um caractere de barra invertida ( \\ , \ a , \ b , \ f , \ n , \ r , \ t , \ v ) é considerado para iniciar uma sequência de escape. Além disso, as seqüências de escape na tabela a seguir serão reconhecidas.
Um caractere literal de nova linha não pode ocorrer dentro de um ERE; a sequência de escape \ n pode ser usada para representar um caractere de nova linha. Um caractere de nova linha não pode ser correspondido por um operador de período.
Sequências de escape em lex
fuga | descrição | significado |
---|---|---|
\ digits | Um caractere de barra invertida seguido pela sequência mais longa de um, dois ou três caracteres de dígito octal ( 01234567 ). Se todos os dígitos forem 0 (ou seja, a representação do caractere NUL), o comportamento será indefinido. | O caractere cuja codificação é representada pelo número inteiro octal de um, dois ou três dígitos. Caracteres de vários bytes requerem várias seqüências de escape concatenadas desse tipo, incluindo o \ inicial de cada byte. |
\ x dígitos | Um caractere de barra invertida seguido pela sequência mais longa de caracteres hexadecimais de dígitos ( 01234567abcdefABCDEF ). Se todos os dígitos forem 0 (ou seja, uma representação do caractere NUL), o comportamento será indefinido. | O caractere cuja codificação é representada pelo número inteiro hexadecimal. |
\ c | Um caractere de barra invertida seguido por qualquer caractere não descrito nesta tabela. ( \\ , \ a , \ b , \ f , \ en , \ r , \ t , \ v ). | O caractere c , inalterado. |
A ordem de precedência dada às expressões regulares estendidas para lex é mostrada na tabela a seguir, de alta para baixa.
Nota: A entrada de caracteres de escape não significa que esses são operadores, mas são incluídos na tabela para mostrar seus relacionamentos com os verdadeiros operadores. A condição inicial, o contexto final e as notações de ancoragem foram omitidos da tabela devido às restrições de posicionamento descritas nesta seção; eles podem aparecer apenas no início ou no final de um ERE.
precedência | tipo | sintaxe |
---|---|---|
1 | símbolos de colchetes relacionados | [= =] [::] [. .] |
2 | caracteres escapados | \ |
3 | expressão entre parênteses | [] |
4 | citando | “ … “ |
5 | agrupamento | () |
6 | definição | { name } |
7 | duplicação de RE de um caractere | * +? |
8 | concatenação | |
9 | expressão de intervalo | { m , n } |
10 | alternação |
Os operadores de ancoragem do ERE ( ^ e $ ) não aparecem na tabela. Com expressões regulares lex , esses operadores são restritos em seu uso: o operador ^ só pode ser usado no início de uma expressão regular inteira e o operador $ apenas no final. Os operadores se aplicam a toda a expressão regular. Assim, por exemplo, o padrão (^ abc) | (def $) é indefinido; em vez disso, pode ser escrita como duas regras separadas, uma com a expressão regular ^ abc e outra com def $ , que compartilham uma ação comum por meio do especial | ação (veja abaixo). Se o padrão fosse escrito ^ abc | def $ , ele corresponderia a abc ou def em uma linha por si só.
Diferentemente das regras gerais do ERE, a ancoragem incorporada não é permitida pela maioria das implementações históricas de lex . Um exemplo de ancoragem incorporada seria que padrões como (^) foo ($) correspondam a foo quando existir como uma palavra completa. Essa funcionalidade pode ser obtida usando os recursos lex existentes:
^ foo / [\ n] | "foo" / [\ n] / * encontrou foo como uma palavra separada * /
Observe também que $ é uma forma de contexto à direita (é equivalente a / \ n e, como tal, não pode ser usado com expressões regulares que contêm outra instância do operador (consulte a discussão anterior sobre o contexto à direita).
As expressões regulares adicionais operador de contexto à direita / (barra) podem ser usadas como um caractere comum se apresentadas entre aspas duplas, “/” ; precedido por uma barra invertida, \ / ; ou dentro de uma expressão entre colchetes, [/] . Os operadores de condição inicial < e > são especiais apenas em uma condição inicial no início de uma expressão regular; em outras partes da expressão regular, eles são tratados como caracteres comuns.
Os exemplos a seguir esclarecem as diferenças entre expressões regulares lex e expressões regulares que aparecem em outras partes deste documento. Para expressões regulares do formato r / x , a sequência correspondente r é sempre retornada; confusão pode surgir quando o início de x corresponde à parte à direita de r . Por exemplo, dada a expressão regular a * b / cc e a entrada aaabcc , o yytext conteria a sequência aaab nessa correspondência. Mas, dada a expressão regular x * / xy e a entrada xxxy , o token xxx , não xx , é retornado por algumas implementações porque xxx corresponde a x * .
Na regra ab * / bc , o b * no final de r estenderá a correspondência de r até o início do contexto final, portanto, o resultado não é especificado. Se essa regra fosse ab / bc , no entanto, a regra corresponderá ao texto ab quando for seguida pelo texto bc . Nesse último caso, a correspondência de r não pode se estender até o início de x , portanto, o resultado é especificado.
Ações em lex
A ação a ser tomada quando um ERE é correspondido pode ser um fragmento do programa C ou as ações especiais descritas abaixo; o fragmento do programa pode conter uma ou mais instruções C e também pode incluir ações especiais. A instrução C vazia ; é uma ação válida; qualquer sequência na entrada lex.yy.c que corresponda à parte do padrão dessa regra é efetivamente ignorada ou ignorada. No entanto, a ausência de uma ação não é válida e a ação que o lex realiza em tal condição é indefinida.
A especificação de uma ação, incluindo instruções C e ações especiais, pode se estender por várias linhas se estiver entre chaves:
ERE {declaração do programa declaração do programa}
A ação padrão quando uma sequência na entrada de um programa lex.yy.c não corresponde a nenhuma expressão é copiar a sequência na saída. Como o comportamento padrão de um programa gerado pelo lex é ler a entrada e copiá-la para a saída, um programa de origem lex mínimo que tenha apenas %% gera um programa C que copia a entrada para a saída inalterada.
Quatro ações especiais estão disponíveis:
| | A ação | significa que a ação para a próxima regra é a ação para esta regra. Ao contrário das outras três ações, | não pode ser colocado entre chaves ou ser terminado em ponto e vírgula. Ele deve ser especificado sozinho, sem outras ações. |
ECO; | Grava o conteúdo da sequência yytext na saída. |
REJEITAR; | Normalmente, apenas uma única expressão é correspondida por uma determinada sequência na entrada. REJECT significa “continuar para a próxima expressão que corresponde à entrada atual” e faz com que qualquer regra seja a segunda opção após a regra atual ser executada para a mesma entrada. Assim, várias regras podem ser correspondidas e executadas para uma sequência de entrada ou sequência de entrada sobreposta. Por exemplo, dadas as expressões regulares xyz e xy e a entrada xyz , geralmente apenas a expressão regular xyz corresponderia. A próxima tentativa de partida começaria após z . Se a última ação na regra xyz for REJECT , essa regra e a regra xy serão executadas. A ação REJECT pode ser implementada de tal maneira que o fluxo de controle não continue depois dela, como se fosse equivalente a ir para outra parte do yylex . O uso de REJECT pode resultar em scanners um pouco maiores e mais lentos. |
INÍCIO | A ação ” BEGIN newstate ; ” alterna o estado (condição inicial) para newstate . Se a cadeia newstate não tiver sido declarada anteriormente como uma condição inicial na seção Definições no lex , os resultados não serão especificados. O estado inicial é indicado pelo dígito 0 ou pelo token INITIAL . |
As funções ou macros descritas abaixo são acessíveis ao código do usuário incluído na entrada lex . Não é especificado se eles aparecem na saída do código C do lex ou são acessíveis apenas através do operando -ll para c89 ou cc (a biblioteca lex ).
int yylex (nulo) | Executa análise lexical na entrada; essa é a função principal gerada pelo utilitário lex . A função retorna zero quando o final da entrada é atingido; caso contrário, ele retornará valores diferentes de zero (tokens) determinados pelas ações selecionadas. |
int yymore (vazio) | Quando chamada, indica que quando a próxima sequência de entrada for reconhecida, ela deverá ser anexada ao valor atual do texto yy em vez de substituí-lo; o valor em yyleng é ajustado em conformidade. |
sem estilo (int n ) | Mantém n caracteres iniciais em yytext , terminados em NUL, e trata os caracteres restantes como se eles não tivessem sido lidos; o valor em yyleng é ajustado em conformidade. |
entrada int (nula) | Retorna o próximo caractere da entrada ou zero no final do arquivo. Ele obtém entrada do ponteiro de fluxo yyin , embora possivelmente por meio de um buffer intermediário. Assim, uma vez iniciada a varredura, o efeito de alterar o valor de yyin é indefinido. A leitura de caracteres é removida do fluxo de entrada do scanner sem nenhum processamento pelo scanner. |
int unput (int c ) | Retorna o caractere c para a entrada; yytext e yyleng são indefinidos até que a próxima expressão seja correspondida. O resultado do uso da entrada para mais caracteres do que os inseridos não é especificado. |
As funções a seguir aparecem apenas na biblioteca lex acessível através do operando -ll ; portanto, eles podem ser redefinidos por um aplicativo portátil:
int yywrap (nulo) | Chamado por yylex no final do arquivo; o yywrap padrão sempre retornará 1 . Se o aplicativo exigir que o yylex continue processando com outra fonte de entrada, o aplicativo poderá incluir uma função yywrap , que associa outro arquivo à variável externa FILE * yyin e retornará um valor zero. |
int main (int argc, char * argv []) | Chama o yylex para executar a análise lexical e sai. O código do usuário pode conter main para executar operações específicas do aplicativo, chamando yylex conforme aplicável. |
A razão para dividir essas funções em duas listas é que apenas essas funções no libl.a podem ser redefinidas com segurança por um aplicativo portátil.
Exceto pela entrada , unput e main , todos os nomes externos e estáticos gerados pelo lex começam com o prefixo yy ou YY .
Uso
Os aplicativos portáteis são avisados de que, na seção Regras na lex , um ERE sem uma ação não é aceitável, mas não precisa ser detectado como errado pela lex . Isso pode resultar em erros de compilação ou tempo de execução.
O objetivo da entrada é retirar caracteres do fluxo de entrada e descartá-los no que diz respeito à análise lexical. Um uso comum é descartar o corpo de um comentário assim que o início de um comentário for reconhecido.
O utilitário lex não é totalmente internacionalizado no tratamento de expressões regulares no código-fonte lex ou no analisador lexical gerado. Parece desejável que o analisador lexical interprete as expressões regulares fornecidas na fonte lex de acordo com o ambiente especificado quando o analisador lexical é executado, mas isso não é possível com a tecnologia lex atual. Além disso, a própria natureza dos analisadores lexicais produzidos pela lex deve estar intimamente ligada aos requisitos lexicais do idioma de entrada que está sendo descrito, que, de qualquer maneira, será frequentemente específico do local. Por exemplo, escrever um analisador usado para texto em francês não será útil automaticamente para o processamento de outros idiomas.
Exemplos
A seguir, é apresentado um exemplo de um programa lex que implementa um scanner rudimentar para uma sintaxe semelhante a Pascal :
% { / * precisa disso para a chamada para atof () abaixo * / #include <math.h> / * precisa disso para printf (), fopen () e stdin abaixo * / #include <stdio.h> %} DÍGITO [0-9] ID [az] [a-z0-9] * %% {DÍGITO} + { printf ("Um número inteiro:% s (% d) \ n", yytext, atoi (texto yy)); } {DÍGITO} + "." {DÍGITO} * { printf ("Um ponto flutuante:% s (% g) \ n", texto yy, atof (texto yy)); } if | then | begin | end | procedure | function { printf ("Uma palavra-chave:% s \ n", texto do yy); } {ID} printf ("Um identificador:% s \ n", texto do yy); "+" | "-" | "*" | "/" printf ("Um operador:% s \ n", yytext); "{" [^} \ n] * "}" / * coma comentários de uma linha * / [\ t \ n] + / * consome espaço em branco * / . printf ("Caractere não reconhecido:% s \ n", yytext); %% int main (int argc, char * argv []) { ++ argv, --argc; / * pule o nome do programa * / if (argc> 0) yyin = fopen (argv [0], "r"); outro yyin = stdin; yylex (); }
Comandos relacionados
yacc – “Mais um compilador-compilador.”