Bitácora de Código

Parsear una ley no es parsear texto

18 AGO 2026

parserarquitecturaakoma ntosoxmllegislaciónpython

Diseño técnico de un pipeline para transformar Markdown legislativo en una representación estructurada y versionable: lexer contextual, Abstract Law Tree, proyección a Akoma Ntoso e instrucciones de consolidación.

Parsear una ley no es parsear texto

El reto de procesar legislación no consiste en extraer palabras de un PDF o presentar Markdown en una interfaz. Consiste en reconocer que el documento tiene una gramática, una jerarquía y una historia de cambios.

El pipeline que surge de esa idea separa responsabilidades. Si quieres el recorrido de cómo se llegó a este diseño —en vez de solo el resultado— las dos primeras partes cuentan la historia.

Parte 1: el origen

Los proyectos "inútiles" nunca son completamente inútiles

Un curso de compiladores y un pequeño lenguaje educativo en español parecían no tener relación con el derecho. Años después, dejaron el modelo mental necesario para empezar a interpretar legislación.

Parte 2: la investigación

Encontrar el nombre del problema

El parser ya podía construir un Abstract Law Tree. El reto real apareció después: reformas parciales, identidad, temporalidad y consolidación. La investigación cambió cuando el problema encontró su vocabulario.

Markdown legislativo
    ↓
Lexer legislativo
    ↓
Tokens
    ↓
Parser
    ↓
ALT
    ↓
Proyección
    ↓
Akoma Ntoso XML
    ↓
Operaciones de consolidación

La separación importa. Cada etapa transforma una incertidumbre distinta en una estructura más explícita.

El input no es texto libre

Considera este documento reducido:

# TÍTULO PRIMERO

## CAPÍTULO I

Artículo 1. Texto del artículo.

I. Primera fracción.

II. Segunda fracción.

a) Primer inciso.

Para un lector humano, la jerarquía es inmediata. Para una máquina, los mismos caracteres pueden ser un encabezado, un artículo, una fracción o una oración ordinaria. El primer problema es clasificar los fragmentos sin confundir su forma con su significado.

Un lexer legislativo es contextual

En un lenguaje de programación, muchos tokens pueden reconocerse con símbolos pequeños:

if (x == 10)

En legislación, los delimitadores suelen ser expresiones, líneas completas o incluso la combinación de varias líneas:

Artículo 254.- Para efectos de...
TÍTULO PRIMERO
DISPOSICIONES GENERALES
I. ...
a) ...

Por eso un lexer legislativo se parece menos a un tokenizador de caracteres y más a un parser ligero. Puede emitir tokens como:

TITLE("PRIMERO")
CHAPTER("I")
ARTICLE("1")
PARAGRAPH(...)
POINT("I")
POINT("II")
INDENT("a")

Pero para hacerlo necesita considerar numeración, mayúsculas, patrones del texto, posición, formato, la línea anterior y, en algunos casos, el contexto ya reconocido. Un I. aislado no significa necesariamente lo mismo que un I. que aparece después de un artículo.

Del stream de tokens al ALT

El parser toma cada token y decide su posición en el árbol. No se limita a anexarlo al último nodo: evalúa el tipo actual, el tipo anterior, el nivel jerárquico, la indentación, los ancestros y las reglas particulares de la legislación procesada.

El resultado puede ser:

Ley
└── Título Primero
    └── Capítulo I
        └── Artículo 1
            ├── Párrafo 1
            ├── Fracción I
            └── Fracción II
                └── Inciso a)

Ese Abstract Law Tree funciona muy bien cuando el input es una versión completa:

Markdown completo → ALT completo

Además tiene una responsabilidad limpia: representar correctamente lo que el parser entendió, sin obligarlo a conocer la persistencia, la temporalidad o el estándar final.

El caso que rompe la simetría: las reformas

Una reforma rara vez reproduce todo el artículo, capítulo o ley. Puede contener marcadores conceptuales como “...”, es decir: esto existe, pero no se publica aquí porque no cambió.

Artículo 3. ...

I, II y III. ...

IV. Nuevo texto.

El ALT de la ley base y el ALT de una reforma no son dos estados completos para comparar con un diff convencional. La reforma es una descripción parcial de cambios, con contexto implícito.

Intentar resolver solo con grafos lleva a tareas costosas y frágiles: traversal, búsqueda de subgrafos, equivalencia parcial, matching estructural y resolución de referencias incompletas. Además, si ALT fuera el modelo canónico, habría que construir desde cero un serializador, motor de consultas, IDs, diff, patch, versionado, referencias, modelo temporal y tooling.

El parser deja de ser el proyecto. El proyecto pasa a ser un ecosistema entero alrededor de una estructura propietaria.

ALT como representación intermedia

La salida no es desechar el ALT ni generar Akoma Ntoso directamente desde el lexer. Eso desperdiciaría una abstracción útil y obligaría a mezclar reconocimiento sintáctico con detalles de interoperabilidad.

La arquitectura queda así:

legislación
    ↓
parser
    ↓
ALT (IR del dominio)
    ↓
proyección AKN
    ↓
documento XML y operaciones posteriores

Es el mismo patrón que aparece en compiladores:

source code → AST / IR → representación objetivo

El ALT es una IR: conserva una visión conveniente para el parser. Akoma Ntoso se convierte en la representación estructurada sobre la que pueden apoyarse validación XML, XPath, XSLT, schemas, metadata y referencias estándar.

Identidad estable antes que búsqueda por recorrido

Una operación legislativa necesita señalar con precisión su objetivo. “El inciso b de la fracción III del artículo 254” no debería requerir recorrer todo el árbol cada vez.

Una identidad estructurada permite expresar ese destino de forma directa:

art_254__frac_III__inc_b

No es solo un detalle de nombres. Los identificadores estables convierten un problema de navegación en una operación direccionable:

buscar nodo: art_254__frac_III__inc_b

Con ellos, una reforma puede descomponerse en cambios atómicos:

replace(target, content)
insert_after(target, content)
delete(target)
renumber(scope)

Consolidar es aplicar operaciones

El cambio conceptual decisivo es dejar de pensar en:

árbol completo vs. árbol parcial

y pensar en:

estado base
    +
operaciones de modificación
    =
nuevo estado

Una consolidación se parece más a aplicar un patch que a calcular un diff entre dos documentos completos. Akoma Ntoso no entrega una aplicación terminada, pero ofrece un modelo de dominio y vocabulario para representar documentos y sus modificaciones sin diseñar todos los conceptos desde cero.

Ese matiz importa:

sin librería ≠ sin conocimiento previo
implementar desde cero ≠ diseñar desde cero

El resultado no es un parser “mágico” ni una tarea completamente automatizada. Hay casos ambiguos —por ejemplo, reformas que derogan un párrafo introductorio antes de una lista de fracciones— que requieren revisión humana. Pero la arquitectura reduce la ambigüedad a decisiones concretas y auditables.

Parsear una ley, entonces, no es parsear texto. Es construir una representación que conserve estructura, identidad y tiempo; lo bastante expresiva para que una modificación parcial pueda convertirse en una nueva versión consolidada.

Y, de forma un poco irónica, todo parte de una serie sobre Codexivo que se quedó esperando su artículo sobre parsers. El experimento sigue en su repositorio; el parser legislativo es, en cierto sentido, esa conversación retomada años después.