STXT Tutorial

STXT es un formato basado en indentación, jerárquico y pensado para personas. No tiene caracteres de escape, y sus documentos pueden entenderse como texto común.

Un documento STXT en diez líneas

# Primer ejemplo de un documento, con todos los elementos
Book (com.acme.book):
	Title: Arquitectura de software moderna
	Authors:
		Author: María Pérez
		Author: Juan García
	Publisher: ACME Editorial
	Summary >>
		Este libro ofrece una visión práctica de patrones y buenas prácticas
		para diseñar sistemas distribuidos y escalables.

La indentación define la estructura

Un documento STXT es un árbol ordenado de nodos anidados. Cada nodo debe tener un nombre, y dependiendo del tipo de nodo puede tener un valor o un texto asociado.

En este ejemplo tenemos un nodo principal Book (más adelante hablaremos de com.acme.book). Este nodo tiene los siguientes hijos: Title, Authors, Publisher y Summary. A su vez, Authors tiene 2 hijos Author.

Con este ejemplo ya se puede ver que la indentación define la estructura:

Book
├── Title
├── Authors
	├── Author
	├── Author
├── Publisher
├── Summary

Nodos inline : y nodos block >>

Existen dos tipos de nodo, y en el ejemplo ya los hemos visto:

  • Nodos inline: Book, Title, Authors, Author y Publisher
  • Nodos block: Summary

Los nodos inline pueden tener un valor asociado, por ejemplo, el primer Author tiene como valor María Pérez, y Publisher tiene como valor ACME Editorial. Además, los nodos inline pueden tener como hijos a otros nodos.

Los nodos block no tienen un valor asociado, solo pueden tener un bloque de líneas de texto, justo a partir de la línea siguiente. Las líneas de texto siempre deben tener una indentación mayor que la de la definición del bloque. Lo más importante a recordar es que el texto del bloque no se interpreta, es texto literal.

Así, el texto de Summary es:

Este libro ofrece una visión práctica de patrones y buenas prácticas
para diseñar sistemas distribuidos y escalables.

Comentarios

La primera línea del ejemplo es un comentario. Los comentarios son líneas que empiezan por # (los blancos de indentación no cuentan).

Namespaces

Los nodos pueden pertenecer a un namespace. Un namespace solamente es un agrupador. En nuestro ejemplo, Book pertenece al namespace com.acme.book. Esto lo hace distinto a Book del namespace com.demo.

Para definir un namespace es suficiente con ponerlo entre paréntesis justo después de la definición del nombre: Book (com.acme.book).

Una característica importante es que si un nodo no define un namespace, entonces hereda el de su padre. Es decir, sin redefinición, los hijos heredan el namespace del padre. Así, en el ejemplo, Title también pertenece al namespace com.acme.book, y lo mismo con Authors, Author, Publisher y Summary.

Esquemas y validación

STXT permite validar directamente los documentos, con esquemas escritos en el propio lenguaje. Esta validación contempla qué estructura es válida, el número de nodos hijos y cómo deben ser los valores de los nodos.

Con esto es suficiente para que un namespace se convierta en un vocabulario para ese ámbito concreto.

Los esquemas pueden definirse también como plantillas. La del ejemplo sería la siguiente:

Template (@stxt.template): com.acme.book
	Description >>
		Book: Plantilla para fichas de libros editoriales
	Structure >>
		Book:
			Title: (1)
			Authors: (1)
				Author: (+)
			ISBN: (?)
			Publisher: (?)
			Published: (?) DATE
			Summary: (?) TEXT
			Chapter: (*)
				Content: (?) TEXT

¿Probamos el Playground?

En este punto ya hemos visto lo básico para usar STXT. Se puede seguir profundizando en los conceptos que ya hemos aprendido, o probar directamente en el Playground.

Los ejemplos de este tutorial y del portal pueden probarse en el playground pulsando en el icono de "Abrir en el Playground".

El playground también empieza con un minitutorial, que se puede mirar por encima, o se puede experimentar directamente con otros documentos.

Nodos en profundidad

Nodos con namespace y sin namespace

Los nodos pueden definir un namespace. En caso de no definirlo y no heredarlo del padre, se dice que es un nodo sin namespace. Como los namespaces se heredan, si un nodo tiene un namespace, sus nodos hijos también lo tendrán, ya sea heredado o redefinido. El primer ejemplo es un nodo con namespace, por lo que se hereda en todos sus nodos hijos:

Book (com.acme.book)
├── Title (com.acme.book)
├── Authors (com.acme.book)
	├── Author (com.acme.book)
	├── Author (com.acme.book)
├── Publisher (com.acme.book)
├── Summary (com.acme.book)

Este es el mismo ejemplo sin namespace:

Book:
	Title: Arquitectura de software moderna
	Authors:
		Author: María Pérez
		Author: Juan García
	Publisher: ACME Editorial
	Summary >>
		Este libro ofrece una visión práctica de patrones y buenas prácticas
		para diseñar sistemas distribuidos y escalables.
Book
├── Title
├── Authors
	├── Author
	├── Author
├── Publisher
├── Summary

Al no tener namespace no podemos asociarlo a ningún esquema, por lo que solo puede validarse la estructura, no el contenido.

Jerarquía y niveles

La indentación define la jerarquía

Vamos a mostrar dos ejemplos, en los que la jerarquía cambia según el nivel:

# Ejemplo 1
Nodo 1:
	Nodo 2:
	Nodo 3:
		Nodo 4:
			Nodo 5:

# Ejemplo 2
Nodo 1:
	Nodo 2:
	Nodo 3:
	Nodo 4:
		Nodo 5:

La jerarquía es diferente, y se asocia directamente con la indentación:

Ejemplo 1
=========
Nodo 1
├── Nodo 2
├── Nodo 3
	├── Nodo 4
		├── Nodo 5

Ejemplo 2
=========
Nodo 1
├── Nodo 2
├── Nodo 3
├── Nodo 4
	├── Nodo 5

Un nivel se especifica con 1 tab o 4 espacios.

Reglas principales:

  • Un tabulador es un nivel.
  • Cuatro espacios son un nivel. Deben ser múltiplos de cuatro. No se permite otro número de espacios.
  • No se mezclan tabs y espacios en una misma línea. O solo tabuladores, o solo espacios.
  • Los niveles deben ser consecutivos. Del nivel 1 no se puede saltar al 3.

Nodos inline

Los nodos inline son todos los nodos con la forma Nombre nodo:. Es decir, usan como separador :. Estos nodos permiten tener hijos y son los que definen la estructura principal de los documentos. Ejemplo:

Nodo 1: valor 1, nodo principal
	Nodo 2: valor 2, hijo de nodo 1
	Nodo 3: valor 3, hijo de nodo 1
		Nodo 4: valor 4 # Hijo de 3: ¡Esto forma parte del valor!
	Nodo 5: hijo de nodo 1

En este ejemplo se puede ver directamente la estructura:

Nodo 1
├── Nodo 2
├── Nodo 3
	├── Nodo 4
├── Nodo 5

Y también los valores.

Nombre nodo Valor del nodo
Nodo 1 valor 1, nodo principal
Nodo 2 valor 2, hijo de nodo 1
Nodo 3 valor 3, hijo de nodo 1
Nodo 4 valor 4 # Hijo de 3: ¡Esto forma parte del valor!
Nodo 5 hijo de nodo 1

Mención especial merece el valor de Nodo 4. El valor es todo lo que hay después del primer :. A partir de ahí no hay ninguna interpretación, todo es texto, y no son necesarios caracteres de escape. Por este motivo puede haber caracteres :, # y otros que están reservados para el nombre del nodo. Otro ejemplo:

Rutas:
	Ruta: C:\Users\ana
	Ruta: C:\Windows\STXT\@stxt.template\
Rutas
├── Ruta
├── Ruta
Nombre nodo Valor del nodo
Ruta C:\Users\ana
Ruta C:\Windows\STXT\@stxt.template\

Nodos block

Los nodos block son todos los nodos con la forma Nombre nodo >>. Es decir, usan como separador >>, y no llevan nada más en esa línea. Estos nodos no tienen valor ni hijos: solo tienen las líneas de texto que hay debajo, con más indentación. Ejemplo:

Summary >>
	Esta línea está en el nivel del bloque.
		Esta otra va más indentada, y esa indentación forma parte del texto.
	# Esto no es un comentario: es texto.
	Title: esto tampoco es un nodo.

El texto de Summary es:

Esta línea está en el nivel del bloque.
	Esta otra va más indentada, y esa indentación forma parte del texto.
# Esto no es un comentario: es texto.
Title: esto tampoco es un nodo.

El texto empieza un nivel por debajo del nodo >>. Esa indentación es lo único que se quita: la indentación adicional forma parte del texto. Dentro del bloque no hay ninguna interpretación: :, # y >> son texto, y no son necesarios caracteres de escape. Por este motivo un bloque sirve para cualquier contenido: un párrafo, un fragmento de código, un trozo de Markdown o un texto en otro formato.

Un bloque termina en la primera línea no vacía con indentación menor o igual que la del nodo >>.

Esa línea puede ser un nodo o un comentario. Las líneas vacías no terminan el bloque:

  • Las que están antes de más texto forman parte del bloque, como líneas vacías.
  • Las que quedan al final se descartan: separan visualmente el documento, pero no son contenido.

Nombres de los nodos

Un nombre admite letras, dígitos y marcas combinantes de cualquier alfabeto (latino, griego, cirílico, árabe, devanagari, CJK...), además de los separadores -, _ y espacio. No admite ningún otro signo: ni :, ni paréntesis, ni #. Debe contener al menos una letra o un dígito.

Para decidir si dos nodos son el mismo se compara su nombre canónico: el nombre en minúsculas, con cada secuencia de separadores reducida a un solo - y sin guiones en los extremos. Los acentos y el alfabeto se conservan. En este ejemplo, el valor de cada nodo es su nombre canónico:

Un nombré con äcento: un-nombré-con-äcento
UN NOMBRÉ CON ÄCENTO: un-nombré-con-äcento
TAMaÑo número 2__ y 3: tamaño-número-2-y-3
Пример 1: пример-1
Nombre 日本語: nombre-日本語

Título, título y TÍTULO- son el mismo nodo.
Peña y Pena son nodos distintos, como lo son dos palabras distintas.

Así se puede escribir un documento en cualquier idioma sin que el lenguaje altere las palabras ni unifique dos nombres que para quien lee son distintos. La única excepción son los namespaces, que se limitan a ASCII.

Listas

STXT no tiene una sintaxis especial para listas. Los hijos de un nodo son una secuencia ordenada, y un mismo nombre puede repetirse. Ya lo hemos visto con Authors:

Authors:
	Author: María Pérez
	Author: Juan García
	Author: Ana López

Y una secuencia de elementos distintos se escribe en orden:

Book: Arquitectura de software moderna
	Chapter: Introducción
	Chapter: Comunicación entre servicios
	Appendix: Glosario
	Chapter: Despliegue

El orden de aparición se conserva. Cuántas veces puede aparecer cada hijo lo dice la validación, que veremos más adelante.

Trim de valores y texto

  • En un nodo inline se quitan los blancos del principio y del final del valor. Es decir, trim() por la izquierda y la derecha.
  • En un nodo block se quitan los blancos del final de cada línea y las líneas finales vacías. Se conservan los blancos del principio del bloque. Es decir, trim() por la derecha de las líneas y eliminación de las líneas vacías finales (no las iniciales).
Nombre: Joan
Nombre:      Joan

En los dos casos el valor de Nombre es Joan.

Recomendaciones de estilo

Son recomendaciones y no reglas:

  • Un solo espacio entre el nombre y el namespace.
  • : va justo después del nombre, o del namespace si lo hay.
  • Un solo espacio entre : y el valor.
  • Un espacio antes de >>.
  • No más de un espacio seguido en los nombres.
  • Un mismo estilo de indentación en todo el documento: o tabuladores, o espacios.
Nombre con valor: El valor
Nombre sin valor:
Nombre con namespace (el.namespace):
Nodo de texto >>

Múltiples nodos en un documento

Los documentos pueden tener varios nodos raíz, independientes entre sí. Esto permite usar STXT directamente en ficheros grandes, sin tener que definir otro tipo de ficheros. Una aplicación podrían ser los ficheros de logs, leídos en streaming:

Log: Inicio creación de usuario
	Level: INFO
	Request ID: 3e45bad6-3a82-4959-844e-9eefd4c418a3
	Message >>
		Creando el usuario:
		- Nombre:
		- Edad: 19
Log: Error crear usuario
	Level: ERROR
	Request ID: 3e45bad6-3a82-4959-844e-9eefd4c418a3
	Message: Cannot invoke "String.length()" because "nombre" is null
	Stacktrace >>
		Exception in thread "main" java.lang.NullPointerException: Cannot invoke "String.length()" because "nombre" is null
			at com.ejemplo.servicio.UsuarioService.validarNombre(UsuarioService.java:42)
			at com.ejemplo.servicio.UsuarioService.crearUsuario(UsuarioService.java:27)
			at com.ejemplo.controlador.UsuarioController.registrar(UsuarioController.java:58)
			at com.ejemplo.App.main(App.java:15)

Comentarios en profundidad

Ya hemos visto que un comentario es una línea que empieza por #, sin contar los blancos de indentación. Los comentarios se descartan: no forman parte del árbol ni de los datos.

# Ficha de un libro
Book:
	# El título va primero
	Title: Arquitectura de software moderna
	ISBN: 978-84-123456-7-8

Los comentarios son de línea completa.

No hay comentarios a final de línea: en Title: Mi libro # nota, el # y lo que le sigue son parte del valor, como hemos visto con Nodo 4.

Indentación de los comentarios

La indentación de un comentario se valida como la de un nodo: las mismas reglas de tabuladores y espacios y, como máximo, un nivel más que el último nodo. Pero un comentario no cambia la jerarquía.

Comentarios y final de un nodo block

Dentro de un nodo block no hay comentarios: un # ahí es texto. Una línea # con indentación menor o igual que la del nodo >> sí es un comentario, y termina el bloque:

Book:
	Summary >>
		Texto del resumen.
		# Esto es texto del bloque.
	# Esto es un comentario, y termina el bloque.
	Title: Arquitectura de software moderna

Estilo de los comentarios

Se recomienda poner cada comentario en el mismo nivel que el nodo siguiente, que es el nodo que describe.

Namespaces en profundidad

Ya hemos visto que un nodo puede pertenecer a un namespace, que se escribe entre paréntesis tras el nombre del nodo. El namespace dice a qué vocabulario pertenecen los nodos, y es lo que permite validarlos. Este es el documento del principio, ahora completo:

Book (com.acme.book):
	Title: Arquitectura de software moderna
	Authors:
		Author: María Pérez
		Author: Juan García
	ISBN: 978-84-123456-7-8
	Publisher: ACME Editorial
	Published: 2025-10-01
	Summary >>
		Este libro ofrece una visión práctica de patrones y buenas prácticas
		para diseñar sistemas distribuidos y escalables.
	Chapter: Introducción a la arquitectura
		Content >>
			En este capítulo presentamos conceptos básicos:
			monolitos, microservicios y criterios de diseño.
	Chapter: Comunicación entre servicios
		Content >>
			Se describen protocolos, mensajería y patrones de integración.

Las reglas para escribir un namespace son pocas:

  • Solo ASCII [a-z0-9] y puntos, con al menos dos partes (a.b).
  • Las mayúsculas se pasan a minúsculas: COM.ACME.BOOK es el mismo namespace.
  • La convención es usar un dominio propio al revés, como en Java, para que dos organizaciones no choquen.
  • Los namespaces que empiezan por @ son especiales: @stxt.* está reservado al propio lenguaje. Más adelante veremos dos: @stxt.template y @stxt.schema. Un namespace propio nunca empieza por @.

Un documento con un namespace sin definición es un documento correcto, pero no se puede validar su contenido.

Herencia de namespaces

En el ejemplo solo Book declara el namespace com.acme.book. Con eso es suficiente, porque el namespace se hereda: Title, Authors, Author, Chapter, Content... todos los descendientes de Book pertenecen a com.acme.book sin escribirlo.

Una consecuencia de esto es que, una vez que un nodo define un namespace, sus hijos también tendrán namespace, ya que no hay una forma establecida de eliminarlo.

Múltiples namespaces

Un hijo puede declarar otro namespace, y entonces sus descendientes heredan el nuevo:

Order (com.example.orders): 1234
	Customer (com.example.customers): Ana López
		Email: ana@example.com
	Total: 120

Order y Total pertenecen a com.example.orders. Customer y Email pertenecen a com.example.customers. Al final del tutorial veremos cómo se valida un documento con varios namespaces.

Validación de documentos

@stxt.template

Una plantilla describe qué forma deben tener los documentos de un namespace: qué nodos existen, cuántas veces aparece cada uno y de qué tipo son sus valores. Es un documento STXT más, con namespace @stxt.template, y su bloque Structure >> tiene la misma forma que los documentos que describe:

Template (@stxt.template): com.acme.book
	Description >>
		Book: Plantilla para fichas de libros editoriales
	Structure >>
		Book:
			Title: (1)
			Authors: (1)
				Author: (+)
			ISBN: (?)
			Publisher: (?)
			Published: (?) DATE
			Summary: (?) TEXT
			Chapter: (*)
				Content: (?) TEXT

Se lee casi como el documento del libro. Lo que hay entre paréntesis es la cardinalidad (cuántas veces puede aparecer ese nodo dentro de su padre) y lo que va después, si hay algo, el tipo del valor:

  • Title: (1): exactamente uno. Authors, igual.
  • Author: (+): uno o más.
  • ISBN: (?): cero o uno. Publisher, igual.
  • Published: (?) DATE: opcional y, si aparece, una fecha AAAA-MM-DD.
  • Summary: (?) TEXT: opcional, y su valor es texto (inline o block).
  • Chapter: (*) con Content: (?) TEXT dentro: cualquier número de capítulos, cada uno con su texto opcional.

Con esta plantilla validan los dos documentos del libro que hemos visto: el del principio y el completo.

Book no admite ningún hijo que no esté en la plantilla.

Un nodo Pages dentro de Book es un error, y no un dato extra que se ignora.

Cardinalidades

Forma Significado
(1) Exactamente uno.
(?) Cero o uno.
(*) Cualquier número.
(+) Uno o más.
(n) Exactamente n.
(n+) n o más.
(n-) Hasta n.
(min,max) Entre min y max.

Tipos

El tipo va tras la cardinalidad; si no se indica, es INLINE. Los más habituales:

Tipo Forma del valor Qué valida
INLINE inline Texto simple. Admite hijos. Es el tipo por defecto.
GROUP sin valor Solo estructura: el nodo agrupa, no lleva valor.
TEXT inline o >> Texto, sin interpretación.
MARKDOWN inline o >> Texto que quien lo consuma debe tratar como Markdown.
NUMBER inline Un número.
DATE inline Fecha AAAA-MM-DD, con calendario.
ENUM inline Uno de los valores de una lista: ENUM [a, b, c].

Solo INLINE y GROUP admiten hijos.

@stxt.schema

Un esquema (@stxt.schema) describe lo mismo que una plantilla, pero nodo a nodo: cada Node con su tipo y la lista de sus Child, con cardinalidades Min/Max. Toda plantilla se compila a un esquema equivalente, y validar con uno u otro da el mismo resultado.

Schema (@stxt.schema): com.acme.book
	Node: Book
		Children:
			Child: Title
				Min: 1
				Max: 1
			Child: Authors
				Min: 1
				Max: 1
			Child: ISBN
				Max: 1
			Child: Publisher
				Max: 1
			Child: Published
				Max: 1
			Child: Summary
				Max: 1
			Child: Chapter
	Node: Authors
		Children:
			Child: Author
				Min: 1
	Node: Chapter
		Children:
			Child: Content
				Max: 1
	Node: Title
	Node: Author
	Node: ISBN
	Node: Publisher
	Node: Published
		Type: DATE
	Node: Summary
		Type: TEXT
	Node: Content
		Type: TEXT

La plantilla es más corta y se parece al documento. El esquema es más largo y más explícito. Para un namespace solo puede haber una definición activa, plantilla o esquema.

El documento final

Un documento completo que valida tanto con la plantilla como con el esquema:

Book (com.acme.book):
	Title: Arquitectura de software moderna
	Authors:
		Author: María Pérez
		Author: Juan García
	ISBN: 978-84-123456-7-8
	Published: 2025-10-01
	Summary: Una introducción práctica a la arquitectura de sistemas modernos.
	Chapter: Introducción
		Content >>
			Conceptos básicos y objetivos del libro.

Summary va aquí como nodo inline, y en los ejemplos anteriores iba como nodo block: TEXT admite las dos formas. Publisher no aparece, y no hace falta: era (?).

Cómo se hace en un proyecto real (la plantilla en un directorio .stxt/, el documento al lado y stxt validate desde el terminal, o los errores en el editor) lo enseña El entorno de trabajo.

Varios namespaces en un mismo documento

Ya hemos visto que cualquier nodo puede declarar su propio namespace, y que sus descendientes lo heredan. Cada namespace se valida contra su propia definición, así que un vocabulario se define una vez y se incorpora desde otros. Por ejemplo, unas reseñas, que podrían acompañar igual a cualquier otro producto.

En la plantilla que incorpora el vocabulario ajeno, el nodo externo se declara con su namespace y su cardinalidad. Su forma no se describe ahí, sino en la plantilla de su propio namespace. El conjunto completo (el documento y las dos plantillas) cabe en un solo fichero:

Book (com.acme.book):
	Title: Arquitectura de software moderna
	Authors:
		Author: María Pérez
		Author: Juan García
	ISBN: 978-84-123456-7-8
	Published: 2025-10-01
	Review (com.acme.reviews):
		Reviewer: Ana López
		Score: 9
		Comment >>
			Una guía clara y bien estructurada, con ejemplos
			fáciles de seguir.
	Review (com.acme.reviews):
		Reviewer: Luis Martín
		Score: 8

Template (@stxt.template): com.acme.book
	Structure >>
		Book:
			Title: (1)
			Authors: (1)
				Author: (+)
			ISBN: (?)
			Published: (?) DATE
			Review (com.acme.reviews): (*)

Template (@stxt.template): com.acme.reviews
	Structure >>
		Review:
			Reviewer: (1)
			Score: (1) NUMBER
			Comment: (?) TEXT

La plantilla del libro, aquí reducida, añade una sola línea nueva, Review (com.acme.reviews): (*): un libro admite cualquier número de reseñas, y qué es una reseña lo dice la plantilla de com.acme.reviews. Las reseñas se validan contra su plantilla, y el resto del libro contra la suya.

Un documento STXT puede tener varios nodos raíz, y este tiene tres: el libro y las dos plantillas. Que quepan en un solo fichero es una posibilidad y no una obligación: lo habitual es que cada plantilla esté en su fichero, y que documento y definiciones se encuentren por el namespace.

Dónde seguir