Analizar expresiones
Construya un árbol sintáctico
Analizar expresiones es una lección gratuita de C Academy en CoddyKit. Esta es la lección 2 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de C Academy, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de C Academy incluye 4 lecciones en total.
De los tokens al árbol
El análisis sintáctico convierte un flujo plano de tokens en un árbol de sintaxis abstracta (AST) estructurado. El árbol codifica la precedencia y la agrupación que los tokens sin procesar solo sugieren.
Para 3 + 4 * 2, el AST anida la multiplicación bajo la suma, por lo que el resultado es 11, no 14.
Estructura de un nodo AST
Cada nodo es una hoja numérica o una operación binaria con dos hijos. Una estructura etiquetada con una unión mantiene reducido el uso de memoria.
El carácter del operador distingue entre +, -, * y / durante la ejecución.
typedef struct Node {
enum { N_NUM, N_BINOP } kind;
union {
int value; /* N_NUM */
struct { /* N_BINOP */
char op;
struct Node *left, *right;
} bin;
};
} Node;Asignación de nodos
Dos constructores pequeños asignan nodos en el heap. Construir el árbol de abajo arriba significa crear primero las hojas y después envolverlas en nodos de operador.
En un intérprete de producción, registraría estas asignaciones para liberarlas más adelante.
#include <stdlib.h>
static Node *num(int v) {
Node *n = malloc(sizeof *n);
n->kind = N_NUM; n->value = v;
return n;
}
static Node *binop(char op, Node *l, Node *r) {
Node *n = malloc(sizeof *n);
n->kind = N_BINOP;
n->bin.op = op; n->bin.left = l; n->bin.right = r;
return n;
}La gramática
Usamos una gramática clásica basada en precedencia. expr gestiona + y -, term gestiona * y /, y factor gestiona los números y los paréntesis.
Como las reglas de mayor precedencia están más profundamente anidadas, la multiplicación se vincula automáticamente con más fuerza que la suma.
/* Grammar (EBNF):
expr = term { ('+' | '-') term } ;
term = factor { ('*' | '/') factor } ;
factor = NUMBER | '(' expr ')' ; */Coincidencia de tokens
Un auxiliar expect consume un token del tipo requerido o aborta. Es el contrato del parser con el lexer.
Reutilizamos los auxiliares de anticipación cur y bump de la lección sobre el tokenizer.
#include <stdio.h>
#include <stdlib.h>
static void expect(TokKind k) {
if (cur().kind != k) {
fprintf(stderr, "parse error: unexpected token\n");
exit(1);
}
bump();
}Análisis de un factor
Un factor es el átomo de la gramática: un número literal o una subexpresión entre paréntesis. Los paréntesis vuelven a llamar a parse_expr.
Esta recursión es la que da su nombre a los parsers de descenso recursivo.
static Node *parse_expr(void);
static Node *parse_factor(void) {
if (cur().kind == TOK_NUM) {
int v = cur().value; bump();
return num(v);
}
expect(TOK_LPAREN);
Node *e = parse_expr();
expect(TOK_RPAREN);
return e;
}Análisis de un término
Un término analiza un factor y después repite el proceso mientras encuentra * o /, incorporando cada operador a un nodo binop asociativo por la izquierda.
La asociatividad por la izquierda significa que 8 / 4 / 2 se analiza como (8 / 4) / 2 = 1.
static Node *parse_term(void) {
Node *left = parse_factor();
while (cur().kind == TOK_STAR || cur().kind == TOK_SLASH) {
char op = (cur().kind == TOK_STAR) ? '*' : '/';
bump();
left = binop(op, left, parse_factor());
}
return left;
}Análisis de una expresión
La regla superior refleja parse_term, pero gestiona + y -. Cada nivel llama a la regla de precedencia inmediatamente superior, por lo que el árbol queda anidado correctamente.
Esta estructura de tres funciones es el núcleo del parser.
static Node *parse_expr(void) {
Node *left = parse_term();
while (cur().kind == TOK_PLUS || cur().kind == TOK_MINUS) {
char op = (cur().kind == TOK_PLUS) ? '+' : '-';
bump();
left = binop(op, left, parse_term());
}
return left;
}Inspección del árbol
Este programa analiza una expresión y la vuelve a imprimir con todos los paréntesis, mostrando cómo se resolvió la precedencia.
El formateador recorre recursivamente la misma estructura de nodos que construyó el parser.
#include <stdio.h>
typedef struct Node {
int is_num; int value;
char op; struct Node *l, *r;
} Node;
static Node *N(int v){ Node*n=calloc(1,sizeof*n); n->is_num=1; n->value=v; return n; }
static Node *B(char o,Node*a,Node*b){ Node*n=calloc(1,sizeof*n); n->op=o; n->l=a; n->r=b; return n; }
static void show(Node *n){
if (n->is_num){ printf("%d", n->value); return; }
printf("("); show(n->l); printf(" %c ", n->op); show(n->r); printf(")");
}
int main(void){
/* 3 + 4 * 2 -> (3 + (4 * 2)) */
Node *ast = B('+', N(3), B('*', N(4), N(2)));
show(ast); printf("\n");
return 0;
}Cómo evitar la recursión por la izquierda
Una gramática ingenua como expr = expr '+' term haría que parse_expr se llamara a sí misma indefinidamente. El descenso recursivo no puede gestionar la recursión por la izquierda directa.
Reescribir la regla como un bucle while sobre { '+' term } evita por completo la recursión infinita.
¿Por qué un AST?
El AST separa la sintaxis de la ejecución. El mismo árbol se puede evaluar, optimizar o compilar a bytecode sin volver a analizarlo.
A continuación recorreremos este árbol para calcular su valor.
Comprobación rápida
Considere cómo las capas de la gramática imponen la precedencia.
Repaso
Ha escrito un parser de descenso recursivo: estructuras de nodos del AST, constructores y las funciones expr/term/factor que codifican la precedencia y la asociatividad por la izquierda.
El árbol resultante está listo para evaluarse.
Preguntas frecuentes
¿La lección «Analizar expresiones» es gratis?
Sí — el texto completo de «Analizar expresiones» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de C Academy, actualiza a CoddyKit PRO. El curso de C Academy incluye 4 lecciones en total.
¿Qué aprenderé en «Analizar expresiones»?
Construya un árbol sintáctico Practicas C Academy con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.
¿Necesito experiencia previa para empezar C Academy?
No se requiere experiencia previa. C Academy en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 2 de 4.
¿Cuánto tiempo toma la lección «Analizar expresiones»?
La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.
¿Puedo escribir y ejecutar código en esta lección de C Academy?
Sí. Cada lección de C Academy incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.
Todas las lecciones de este curso
- Tokenizar la entrada
- Analizar expresiones
- Evaluar el árbol
- Añadir variables