El encargo era pequeño y sonaba aburrido: dentro de un backoffice de tarifas, la gente de negocio quería escribir sus propias condiciones de descuento sin pedir un despliegue cada vez. Algo tipo total > 500 && pais == ‘PT’ || cupon in [‘BF24’, ‘BF25’]. Nada de eval y nada de aceptar JavaScript de usuario: un mini-lenguaje propio, con su tokenizador, su parser y su evaluador.
Empecé por lo tonto, que es por donde hay que empezar: el tokenizador. Números, cadenas, identificadores con punto (cliente.pais), operadores de dos caracteres, posiciones para los mensajes de error. Eso un modelo lo escribe entero y bien, porque es trabajo repetitivo con reglas claras. Lo pedí, lo leí, cambié tres cosas y seguí.
El parser fue lo fácil; la precedencia, no
La primera versión fue un descenso recursivo con una función por nivel: parseOr, parseAnd, parseComparison. Funciona, pero cada operador nuevo son dos funciones más y un rediseño. Le pedí reescribirlo como parser de Pratt, con una tabla de precedencias, y ahí la conversación se puso interesante: la tabla que salió colocaba el not unario por debajo del &&.
Traducido: !activo && premium se parseaba como !(activo && premium).
Un descuento que se aplica al revés no revienta. Se cobra.
Ningún test feliz lo pilla. Lo pillé porque en vez de pedir más código le pedí otra cosa: que me imprimiera el AST de una lista de expresiones y lo comparara con lo que yo esperaba. Ahí es donde una IA que tienes al lado, sin contar peticiones, cambia cómo trabajas:
- Genera veinte casos límite y sus árboles esperados, y tú discutes los que no cuadran.
- Le enseñas el árbol malo y le pides que explique por qué su tabla lo produce. Suele confesar sola.
- Reescribes la tabla, vuelves a pasar los veinte casos, sigues.
El resto de la tarde fue lo previsible: cortocircuito de verdad (que && no evalúe la derecha), decidir qué pasa cuando un campo viene null — decisión de negocio, no del parser — y errores con la columna exacta para que el de tarifas vea dónde le falta el paréntesis.
Por qué nada de esto salió de la máquina
Aquí no había datos personales de nadie. Había algo que se protege menos y duele parecido: la lógica comercial. Los ejemplos que le pasaba a la IA no eran genéricos. Eran los umbrales reales por país, nombres de campañas sin lanzar y la forma exacta en que la empresa segmenta a sus clientes. Un prompt con veinte de esas expresiones es la política de precios del trimestre en texto plano.
Con el modelo corriendo dentro del navegador, sobre la GPU del propio equipo, esa pregunta —¿esto se puede pegar o no?— simplemente no aparece. No hay petición que salga, no hay cuenta, no hay registro en el historial de nadie. Y como efecto lateral tonto pero real: funciona en un tren sin cobertura, que es donde acabé cerrando la tabla de precedencias.
El lenguaje tiene ocho operadores y cabe en un fichero. Ni es impresionante ni falta que hace. Es exactamente el tipo de utilidad pequeña y fiddly donde tener la IA al lado ahorra la tarde entera, siempre que la conversación se quede entre tú y tu editor.
Abre Elffuss Code y escribe el tuyo: el modelo corre entero en tu navegador, sobre tu GPU, y ni el código ni el prompt suben a ningún sitio.
Abrir Code →