SDK Multiplataforma en C logo

SDK Multiplataforma en C

Analizador léxico

❮ Anterior
Siguiente ❯

El analizador léxico nos ayudará a identificar y etiquetar los diferentes campos que forman un texto.


Funciones

LexScn*lexscn_create (void)
voidlexscn_destroy (...)
voidlexscn_spaces (...)
voidlexscn_newlines (...)
voidlexscn_escapes (...)
voidlexscn_comments (...)
voidlexscn_start (...)
voidlexscn_jump_bom (...)
lextoken_tlexscn_token (...)
uint32_tlexscn_row (...)
uint32_tlexscn_col (...)
const char_t*lexscn_lexeme (...)
const char_t*lexscn_string (...)
voidlexscn_jump (...)
uint32_tlexscn_read_u32 (...)
real32_tlexscn_read_r32 (...)
real64_tlexscn_read_r64 (...)

El análisis léxico permite fragmentar un bloque de texto en elementos atómicos denominados tokens y es el primer paso a la hora de implementar un compilador, interprete o traductor (Figura 1).

  • Utiliza lexscn_create para crear el escáner.
  • Utiliza lexscn_start para indicar el stream origen del texto.
  • Utiliza lexscn_token para obtener el siguiente token del stream.
  • Representación de los diferentes tokens que forman una sentencia.
    Figura 1: El analizador léxico fragmenta un texto en tokens.

Si bien, los propios Stream proporcionan funciones para leer caracteres, líneas o palabras, estos son insuficientes si nuestro cometido es procesar texto estructurado como el código fuente de un lenguaje de programación u otros formatos que sigan ciertas reglas gramaticales. LexScn es un sencillo analizador léxico que reconoce los tokens propios del lenguaje C, muy comunes en infinidad de gramáticas y formatos de archivo. Está implementado como una máquina de estados finitos y permite cierta configuración activando diferentes opciones. En (Listado 1) vemos el código necesario para leer uno a uno todos los tokens de un archivo .c. El resultado de procesar el archivo (Listado 2) lo tenemos en (Listado 3).

Listado 1: Lectura de los tokens de un archivo en C.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
LexScn *lex = lexscn_create();
Stream *stm = stm_from_file("source.c", NULL);
lextoken_t token;

lexscn_start(lex, stm);
while ((token = lexscn_token(lex)) != ekTEOF)
{
    switch (token) {
    case ekTIDENT:
        ...
    }
}
Listado 2: Archivo source.c.
1
2
3
4
5
6
7
void func(int a)
{
    int i;
    char *str = "Hello";

    i = 5 + 2.5;
}
Listado 3: Análisis léxico de source.c.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Token           Lexeme
-----           ------
ekTIDENT        "void"
ekTIDENT        "func"
ekTOPENPAR      "("
ekTIDENT        "int"
ekTIDENT        "a"
ekTCLOSPAR      ")"
ekTOPENCURL     "{"
ekTIDENT        "int"
ekTIDENT        "i"
ekTSCOLON       ";"
ekTIDENT        "char"
ekTASTERK       "*"
ekTIDENT        "str"
ekTEQUALS       "="
ekTSTRING       "Hello"
ekTSCOLON       ";"
ekTIDENT        "i"
ekTEQUALS       "="
ekTINTEGER      "5"
ekTPLUS         "+"
ekTREAL         "2.5"
ekTSCOLON       ";"

1.1. Identificadores

Un identificador es una "palabra" alfanumérica que debe comenzar por una letra o '_' y contiene números, letras o '_'. Se utiliza para nombrar variables, funciones, palabras reservadas, etc. No permiten espacios ni símbolos. (Listado 4) (Figura 2).

Listado 4: Identificadores correctos e incorrectos.
1
2
OK: while cos _reSult a56B _06_t aG h9 _12AcVb
NO: 045 ?er "_5G _tg(
Autómata finito que reconoce un identificador en C.
Figura 2: Autómata finito que reconoce un identificador.

Se pueden reservar ciertos identificadores para que actúen como palabras clave del lenguaje. Por ejemplo for, while o if son palabras clave de C y no podrán ser utilizadas para el nombrado de variables o funciones. Al ser de propósito general, este escáner no reconoce ningún tipo de palabra reservada, sino hay que etiquetarla expresamente tras leer el token (Listado 5).

Listado 5: Reconociendo la palabra clave while.
1
2
3
4
5
6
while ((token = lexscn_token(lex)) != ekTEOF)
{
    if (token == ekTIDENT && str_equ_c(lexscn_lexeme(lex, NULL), "while"))
        token = ekTRESERVED;
    ...
}

1.2. Cadenas

Una cadena de texto es una serie de caracteres Unicode puestos entre comillas (") (Figura 3). LexScn reconoce las secuencias de escape de C para representar códigos no imprimibles o caracteres no disponibles en el teclado (Listado 6).

  • Utiliza lexscn_escapes para hacer efectivas las secuencias de escape al leer cadenas.
  • Listado 6: Secuencias de escape aceptadas en ekTSTRING.
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    
    \a      07  Alert (Beep, Bell) (added in C89)
    \b      08  Backspace
    \f      0C  Formfeed Page Break
    \n      0A  Newline (Line Feed)
    \r      0D  Carriage Return
    \t      09  Horizontal Tab
    \v      0B  Vertical Tab
    \\      5C  Backslash
    \'      27  Single quotation mark
    \"      22  Double quotation mark
    \?      3F  Question mark (used to avoid trigraphs)
    \nnn        Octal number
    \xhh        Hexadecimal number
    \Uhhhhhhhh  Unicode code point
    \uhhhh      Unicode code point
    
    Autómata finito que reconoce una cadena de texto.
    Figura 3: Autómata finito que reconoce una cadena de texto.

1.3. Números

En el caso de tokens numéricos la cosa se complica un poco debido a las diferentes bases numéricas y a la representación exponencial de números reales (Figura 4). La resumimos brevemente, aunque es común a muchos lenguajes de programación (C incluido).

  • Si el número empieza por 0 será considerado octal (base 8), por lo tanto, las cifras siguientes están limitadas a 0-7, pe: 043, 001, 0777.
  • Si el número empieza por 0x se considerará hexadecimal (base 16) con cifras 0-9 a-f A-F, pe: 0x4F, 0XAA5, 0x01EAC.
  • En el momento que aparezca un punto decimal '.' se considerará número real. El punto inicial es válido, pe: .56.
  • Un número entero o real permite notación exponencial con el carácter 'e' ('E'), pe: 12.4e2, .56e3, 1e4.
  • Autómata finito que reconoce números en diferentes bases.
    Figura 4: Autómata finito que reconoce números.

1.4. Símbolos

Los símbolos son tokens de un solo carácter que representan casi la totalidad de signos de puntuación US-ASCII y que suelen utilizarse como operadores, separadores o limitadores dentro de las gramáticas (Listado 7) (Figura 5).

Listado 7: Símbolos reconocidos por LexScn como tokens.
1
< > , . ; : ( ) [ ] { } + - * = $ % # & ' " ^ ! ? | / \ @ 
Autómata finito que reconoce los símbolos más, menos, asterisco e igual.
Figura 5: Autómata finito que reconoce algunos símbolos.

lexscn_create ()

Crea un analizador léxico.

LexScn*
lexscn_create(void);

Retorna

El analizador léxico recién creado.


lexscn_destroy ()

Destruye un analizador léxico.

void
lexscn_destroy(LexScn **lex);
lex

Analizador léxico. Será puesto a NULL tras la destrucción.


lexscn_spaces ()

Opción de espacios en blanco.

void
lexscn_spaces(LexScn *lex,
              const bool_t activate);
lex

Analizador léxico.

activate

TRUE el analizador devolverá el token ekTSPACE al encontrar secuencias de espacios en blanco. FALSE ignorará los espacios en blanco. Por defecto FALSE.


lexscn_newlines ()

Opción de nueva línea.

void
lexscn_newlines(LexScn *lex,
                const bool_t activate);
lex

Analizador léxico.

activate

TRUE el analizador devolverá el token ekTEOL al encontrar carácteres nueva línea. FALSE ignorará los saltos de línea. Por defecto FALSE.

Observaciones

Solo tiene efecto si lexscn_spaces es FALSE.


lexscn_escapes ()

Opción de secuencias de escape.

void
lexscn_escapes(LexScn *lex,
               const bool_t activate);
lex

Analizador léxico.

activate

TRUE se procesarán las secuencias de escape al leer tokens ekTSTRING. Por ejemplo, la secuencia "\n" se transformará en el carácter 0x0A (10). FALSE ignorará las secuencias de escape, leyendo las cadenas de texto de forma literal. Por defecto FALSE.


lexscn_comments ()

Opción de comentarios.

void
lexscn_comments(LexScn *lex,
                const bool_t activate);
lex

Analizador léxico.

activate

TRUE el analizador devolverá un token ekTMLCOM cada vez que encuentre comentarios de C /* Comment */ y ekTSLCOM para comentarios C++ // Comment. FALSE ignorará los comentarios. Por defecto FALSE.


lexscn_start ()

Inicia el analizador. Pone a cero el contador de líneas y columnas.

void
lexscn_start(LexScn *lex,
             Stream *stm);
lex

Analizador léxico.

stm

Stream de texto de lectura con el origen de datos.


lexscn_jump_bom ()

Salta la posible secuencia Byte Order Mark "" que se encuentra al comienzo de algunos archivos/streams UTF8.

void
lexscn_jump_bom(LexScn *lex);
lex

Analizador léxico.

Observaciones

Esta función no tendrá efecto si no existe dicha secuencia al inicio del stream. El BOM es habitual en streams provenientes de algunos servidores Web.


lexscn_token ()

Obtiene el siguiente token a partir del Stream de texto asignado en lexscn_start.

lextoken_t
lexscn_token(LexScn *lex);
lex

Analizador léxico.

Retorna

El tipo de token obtenido.


lexscn_row ()

Obtiene el número de fila del último token leído.

uint32_t
lexscn_row(const LexScn *lex);
lex

Analizador léxico.

Retorna

El número de fila.


lexscn_col ()

Obtiene el número de columna del primer carácter del último token leído.

uint32_t
lexscn_col(const LexScn *lex);
lex

Analizador léxico.

Retorna

El número de columna.


lexscn_lexeme ()

Obtiene el lexema del último token leído. El lexema es la cadena de texto asociada al token.

const char_t*
lexscn_lexeme(const LexScn *lex,
              uint32_t *size);
lex

Analizador léxico.

size

Tamaño en bytes del lexema, sin contar el carácter nulo '\0'. Es equivalente a hacer un str_len_c del lexema.

Retorna

El lexema. Está almacenado en un buffer temporal y se perderá al leer el siguiente token. Si lo necesitas, haz una copia con str_c.


lexscn_string ()

Obtiene un texto descriptivo a partir de un tipo de token. Es útil para tareas de depuración.

const char_t*
lexscn_string(const lextoken_t token);
1
2
const char_t *desc = lexscn_string(ekTEOL);
// desc = "newline";
token

Token.

Retorna

Texto descriptivo.


lexscn_jump ()

Salta el siguiente token del stream. Si el token no corresponde al indicado, se marcará el stream como corrupto.

void
lexscn_jump(LexScn *lex,
            const lextoken_t token);
1
2
3
4
5
6
void lexscn_jump(LexScn *lex, const lextoken_t token)
{
    lextoken_t tok = lexscn_token(lex);
    if (tok != token)
        stm_corrupt(lex->stm);
}
lex

Analizador léxico.

token

Token esperado.


lexscn_read_u32 ()

Lee el siguiente token y lo transforma a uint32_t. Si el token no es numérico, se marcará el stream como corrupto.

uint32_t
lexscn_read_u32(LexScn *lex);
lex

Analizador léxico.

Retorna

El valor leído.


lexscn_read_r32 ()

Lee el siguiente token y lo transforma a real32_t. Si el token no es numérico, se marcará el stream como corrupto.

real32_t
lexscn_read_r32(LexScn *lex);
lex

Analizador léxico.

Retorna

El valor leído.


lexscn_read_r64 ()

Lee el siguiente token y lo transforma a real64_t. Si el token no es numérico, se marcará el stream como corrupto.

real64_t
lexscn_read_r64(LexScn *lex);
lex

Analizador léxico.

Retorna

El valor leído.

❮ Anterior
Siguiente ❯