Herranz

Sesión 10: Representando HTML

En este laboratorio vamos a definir diferentes tipos en C para poder representar HTML usando struct y union.

HTML

HTML es el lenguaje de marcado con el que se crean las páginas Web. Veamos un ejemplo de fichero HTML, un hello world (puedes copiarlo, salvarlo en un fichero con extensión .html y abrirlo con un navegador Web):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<html>
  <head>
    <meta charset="utf-8"></meta>
    <title>Página Hello world</title>
  </head>
  <body>
    <p style="text-align: center; background-color: #f0f8ff; color: #2c3e50; padding: 15px; border-radius: 8px; font-family: Arial, sans-serif; font-size: 16px;">
      Hello world
    </p>
  </body>
</html>

Vamos a diseccionar lo que ves, de forma un tanto informal de momento:

Tu tarea en este laboratorio es crear un módulo en C para trabajar con HTML y para empezar tendrás que representar la estructura anterior.

Structs, Union y punteros

Atributos

1
 struct { char *nombre; char *valor; } atr;

ini_atributo

1
2
3
4
5
6
struct { char *nombre; char *valor; } ini_atributo(char *n, char *v) {
  struct { char *nombre; char *valor; } a;
  a.nombre = n;
  a.valor = v;
  return a
}
1
2
3
struct { char *nombre; char *valor; } atr;

atr = ini_atributo("class", "bg-primary text-primary");
1
 extern void ini_atributo(struct { char *nombre; char *valor; } *a, char *n, char *v);

Etiquetas

1
 struct { char *nombre; char *valor; }
1
 struct atr_s { char *nombre, char *valor; };
1
 struct atr_s a;
1
 struct { char *nombre, char *valor; } a;

Typedef

1
2
3
typedef unsigned int natural_t;
typedef long unsigned int size_t;
typedef char *string_t;

Elementos

Union y Enum

1
 union { int temp; double pres; } medida;
1
 enum { ELEMENTO, TEXTO } tipo_de_contenido;

Un módulo HTML

El header

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/*
 * Dado un nombre y un valor devuelve un nuevo atributo
 * NOTA: deberás copiar los strings usando funciones como strndup
 */
atributo_t *crear_atributo(char *nombre, char *valor);

/*
 * Dada una lista de atributos y otra de contenidos devuelve un nuevo elemento
 */
elemento_t *crear_elemento(atributo_t *atributos, size_t n_atributos,
                           contenido_t *contenidos, size_t n_contenidos);

/*
 * Devuelve un contenido que es un elemento.
 */
contenido_t *crear_contenido_elem(elemento_t *elemento);

/*
 * Devuelve un contenido que es texto.
 * NOTA: deberás copiar el string usando funciones como strndup
 */
contenido_t *crear_contenido_texto(char *texto);

/*
 * Deja una representación bonita del HTML representado por un elemento
 * en un buffer limitado a buffer_size caracteres.
 */
void sprint_elemento(elemento_t *elemento, char *buffer, size_t buffer_size);

La implementación vacía de html.c

1
2
3
4
5
6
7
8
atributo_t *crear_atributo(char *nombre, char *valor) {
  /*
   * Nombramos los parámetros para engañar al compilador
   * para que crea que los usamos ;)
   */
  nombre; valor;
  return NULL;
}

El test

La implementación

FAQ

Algunas preguntas surgidas en clase:

1
2
atributo_t a;
a = {"class", "bg-primary text-primary"};
1
2
  atributo_t a;
  a = {"class", "bg-primary text-onprimary"};
1
2
  atributo_t a;
  a = (atributo_t){"class", "bg-primary text-onprimary"};
1
2
3
4
struct atr_s {char * n; char *v; };

struct atr_s a;
struct atr_s *p;

Vamos a exponer MATEMÁTICAMENTE una sintaxis que habla de los tipos de las expresiones:

1
2
3
4
5
6
7
8
9
a : struct atr_s (el tipo de a es struct atr_s)
p : struct atr_s *
a.n : char *
*p : struct atr_s
*p.n : char *
(*p).n : char *
p.n : ERROR DE TIPOS
*(p.n) : ERROR DE TIPOS
*p.n == *(p.n)

Por suerte C nos regala una bonita sintaxis para acceder al campo de un struct apuntado por p:

1
                    (*p).n == p->n

“Last words”

A estas alturas ya te habrás dado cuenta de que las definiciones de los tipos son “recursivas”, para definir el tipo elemento_t necesitas contenido_t y viceversa. Para resolver este problema de tipos no definidos que te habrás encontrado C te permite anticipar simplemente los nombres de structs, unions y enums. Veamos la definición completa de todos los tipos:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
/*
 * Se pueden anticipar structs con nombre (en este caso ni
 * siquiera haría falta por que con el typedef bastaría)
 */
struct atr_s;
struct ele_s;
struct con_s;

/*
 * Se pueden usar structs con nombre para declarar nombres de tipos
 */
typedef struct atr_s atributo_t;
typedef struct ele_s elemento_t;
typedef struct con_s contenido_t;

/*
 * Y ahora ya se pueden definir los structs incluso "recursivamente"
 * (¡elemento_t hace uso de contenido_t y viceversa!)
 */
struct atr_s {
  char *nombre;
  char *valor;
};

/* Importante: en C siempre necesitamos la longitud de los arrays */
struct ele_s {
  char *tag;
  size_t n_atributos;
  atributo_t *atributos;
  size_t n_contenidos;
  contenido_t *contenidos;
};

/* Podríamos definir un tipo para este union pero no es necesario */
union elemento_o_texto_u {
  char *texto;
  elemento_t *elemento;
};

/*
 * Podríamos haber definido el enum con una etiqueta tipo_contenido_e
 * y luego el typedef usando enum tipo_contenido_e
 */
typedef enum { TEXTO, ELEM } tipo_contenido_t;

struct con_s {
  tipo_contenido_t tipo;
  union elemento_o_texto_u dato;
};