slex
====

2026-10
  * d2datdinc: una TABLE de SCEPTRE se guarda como el bloque entero de
    texto (formato pares) y din la escribe asi en el .din, debajo de
    #formato pares: los numeros los lee tabular2 (2.0-11 en adelante),
    venga la tabla como cadena o como archivo. Siru::Din y Siru::Rerun
    usan el texto.

  * d2datdinc: una lectura que falla (un include que no esta, un control de
    rama) ya no termina el proceso: leer() devuelve NULL con estado 1. Los
    valores JSON se pueden liberar por arena (dato/json.h). varios arma
    tambien el .din y lo prueba desde hilos.

  * d2datdinc: las piezas de un valor de ELEMENTS (separadas por & con sus
    V: en un .ds, por las comas de afuera en un .d) las lee una gramatica de
    Bison (piezas.y, nueva), y la marca D: de un parametro, parametros.y. Un
    & o una coma entre comillas o parentesis ya no parte el valor; con
    D : DX (con blancos) el valor se reescribe igual que con D:DX.

  * d2datdinc: la lista del valor de RUN_CONTROLS de un .ds (control.y),
    EXTERNAL (declaraciones.y), el valor de una ELEMENT DEFINITION (edef.y,
    nueva) y las comas que separan sentencias en una linea (comas.y, nueva)
    se leen con Bison. Lo raro se avisa: corchetes de mas en una lista, un
    nombre de EXTERNAL que empieza con un digito, una marca IC: en medio de un
    campo, un parentesis o una comilla que no cierra. En ELEMENT DEFINITION
    la coma de adentro de los parentesis ya no separa campos.

  * din: los valores que se traducen a ExprTk los lee una gramatica de Bison
    (din/valor.y), en lugar de recorrer el texto a mano. Un valor que no es
    una expresion se protesta con la rama y desde donde no se entiende; la
    multiplicacion sin * pasa, como en ExprTk. and, or, not y xor pasan
    tambien en las ecuaciones de un modelo. Los cuerpos de FUNCTIONS tambien:
    uno que no es una expresion se protesta; el texto entre comillas de un
    cuerpo ya no se toca al expandirlo en linea; 2TIME es 2 por TIME.

  * d2datdinc: las expresiones de los valores las lee una gramatica de Bison
    (d2datdinc/expresion.y). Las reescrituras de SCEPTRE (EQUATION N, TABLE N
    (X), las fuentes J) se hacen sobre los valores y no sobre la sentencia
    entera; EXPRESSION N es la etiqueta XN; en un .d una tabla definida
    despues de usarla se traduce. Lo que no se entiende (una multiplicacion
    sin *) se avisa.

  * d2datdinc: las cotas, los conjuntos DC, la cabecera de MODEL, las
    instancias y ELEMENT DESCRIPTION de un .d las lee una gramatica de Bison
    (d2datdinc/sceptre.y). CHANGE ya no se lleva las opciones que le siguen
    (SUPPRESS, PRINT), que ahora se avisan y se saltean.

  * d2datdinc: la cabeza de ELEMENTS de un .d (NOMBRE, n1-n2 =) la lee una
    gramatica de Bison (d2datdinc/rama.y). Un nodo con blancos o un nombre
    con _ se avisan y la linea se ignora.

  * d2datdinc: la forma de RUN CONTROLS (PALABRAS [= VALOR]) la lee una
    gramatica de Bison (d2datdinc/control.y). En un .ds, lo raro --un valor
    que no es una lista, una clave de varias palabras, algo despues de la
    lista-- se avisa y se ignora (antes quedaba la clave vacia).

  * d2datdinc: OUTPUTS de un .d lo lee una gramatica de Bison
    (d2datdinc/salidas.y). Lo raro (VC1 VC2, IR1+IR2) se avisa y la linea se
    ignora; antes iba como variable.

  * d2datdinc: DEFINED PARAMETERS y VARIABLES las lee una gramatica de Bison
    (d2datdinc/parametros.y). Un nombre con _ ahora vale entero (Siru lo
    cortaba en el _); lo raro --algo antes del nombre, un nombre con blancos,
    un parametro repetido, un valor vacio-- se avisa y se ignora.

  * d2datdinc: CONSTANTS y FUNCTIONS las lee una gramatica de Bison
    (d2datdinc/declaraciones.y). Lo bien escrito da lo mismo; lo raro --algo
    antes del nombre o despues del numero, un nombre de funcion con _, una
    constante o funcion repetida, y otros-- se avisa y la sentencia se
    ignora (antes se tomaba una parte).

  * d2datdinc: EQUATIONS y CONTROL EQUATIONS las lee una gramatica de Bison
    (d2datdinc/ecuacion.y). Lo bien escrito da lo mismo; lo raro --texto
    despues de las marcas, una marca repetida, TIPO: sin valor, V: vacio,
    un blanco antes de una coma, el vector de una LINEAL que no se entiende,
    y otros-- se avisa y la sentencia se ignora (antes salia una ecuacion
    rota).

  * d2datdinc: la seccion DATA la lee una gramatica de Bison (d2datdinc/data.y,
    un parser puro con un lexer de slex). Es la primera seccion que pasa a
    Bison. Lo bien escrito da lo mismo; lo raro --un indice vacio o con
    signo, un numero hexadecimal, una comilla sin cerrar, flechas de mas, un
    camino que pisa una lista con una clave-- ahora se avisa y la sentencia
    se ignora sin tocar el dato (antes se aceptaba, o rompia el programa).
    Para armar slex hace falta bison (3.6 o posterior).

  * d2datdinc: lo que lleva la lectura de un circuito --el dato que se arma
    y por donde va el lexer-- deja de estar en variables del programa y pasa
    a ser de cada lectura. El programa hace lo mismo y da lo mismo; lo que
    cambia es que se pueden leer varios circuitos en un proceso, uno despues
    de otro o a la vez desde hilos distintos, que antes daba el segundo
    mezclado con el primero. Prueba nueva en el make check: d2datdinc/varios.
    Todavia no es una biblioteca: hay errores que terminan el proceso y los
    valores JSON no se liberan (d2datdinc/README.md, 11).

  * Los ejemplos 01 a 04 y 06 a 08 piden los tokens con slex_next.

  * d2datdinc: las claves del paso en el dato pasan de SIMUL2_* a SIRU_*
    (SIRU_PASO, SIRU_PASO_MIN, SIRU_PASO_MAX, SIRU_ADAPT, SIRU_ABSOLUTO,
    SIRU_RELATIVO, SIRU_ORDEN_ERROR, SIRU_COMPRESION_CUENTA,
    SIRU_COMPRESION_CRITERIO), y los controles propios de un .d, de
    "SIMUL2 ..." a "SIRU ..." (SIRU ADAPTIVE STEP = 0). Es porque Simul2
    ahora se llama siru, y su directorio, siru/. Los nombres de antes se
    siguen aceptando al leer un .d, un .ds o un .dat, y con -v se avisa. Un
    .dat escrito por esta version lleva las claves nuevas: quien lo lea
    tiene que conocerlas.

  * slex por contexto. El estado del lexer --reglas, acciones, flujos,
    fuentes, tokens, nivel de trazas, errores-- pasa de variables globales a
    un struct slex_ctx que crea y libera quien lo usa: varios lexers
    independientes en un proceso, uno adentro de la accion de otro, y
    contextos distintos desde hilos del sistema distintos. API nueva en
    slex.h (slex_ctx_new, slex_ctx_register, slex_ctx_ini_file,
    slex_ctx_ini_string, slex_next, slex_ctx_free...), descrita en
    doc/API-CONTEXTO.md. Un error ya no termina el proceso: se anota en el
    contexto y la funcion lo devuelve. slex_ctx_free libera todo.

  * La API de siempre no cambia: ini_slex, get_token, las tablas globales y
    vectorize_usr_func siguen, sobre el mismo nucleo, como un contexto mas.
    Un programa escrito para ella compila sin tocarlo y da la misma salida;
    esta comprobado con test_api, d2datdinc/probar, los ejemplos, d2datdinc
    sobre los 1205 circuitos del arbol y las 1087 pruebas de siru.
    vectorize_usr_func deja de ser obligatoria.

  * d2datdinc, cir2ds y los ejemplos usan un contexto propio.

  * La memoria: cada intento de cada regexp pedia un bloque a PCRE2 y una
    copia del buffer, y no los devolvia. d2datdinc usaba 275 MB para un
    circuito de 15 KB; ahora, 5. Pasar los 1205 circuitos lleva 15 s en
    lugar de 53.

  * Errores arreglados: la marca <nombre> de un def reconocia tokens
    mientras se leia la configuracion y, segun donde cortara la lectura, se
    comia el principio de la regla siguiente; una definicion que se nombra
    a si misma dejaba a la biblioteca girando para siempre; un segundo
    ini_slex terminaba en violacion de segmento; la funcion de una regla
    recibia use en 0;
    end_while con max_vez 0 daba siempre error; dos restos de depuracion
    leian el response de cualquier programa como el struct de los ejemplos
    07 y 08. La lista completa quedo en reentrante/DIFERENCIAS.md.

  * tests/: test_ctx (99 pruebas de la API por contexto), test_hilos (ocho
    hilos del sistema, cada uno con su contexto) y test_azar (texto, reglas
    y llamadas al azar) entran al make check; tests/bison es el piloto de
    slex con Bison: dos gramaticas en un programa, parsers puros, yylex
    sobre slex_next.

  Lo que cambia y lo que no, con el detalle: reentrante/DIFERENCIAS.md. Como
  se comprobo: reentrante/README.md.

2026-09
  Slex pasa a ser la base de los lexers de los programas de este arbol, y
  Simulparser --el parser en Perl-- se retira. La decision se apoya en algo
  medido, no en una intencion: d2datdinc lleva un .d, un .ds o un .ds2
  al dato de Simul y da el mismo JSON que Simulparser sobre el corpus
  entero, 191 iguales y 0 distintos. Los 55 que quedan afuera son los que
  al de Perl tampoco le salen, por includes que en el corpus no existen.

  * Empaquetado: libslex0 con la biblioteca compartida y libslex-dev con
    los encabezados, el enlace de desarrollo, el .a y slex.pc. Se instala
    con  sudo ../instalar slex .

  * libslex declara sus dependencias. Antes dejaba sin resolver los
    simbolos de PCRE2 y de mem_util, y cada programa que la usaba tenia
    que saber eso y enlazarlas por su cuenta --los Makefile de los
    ejemplos lo hacen a mano-- con rutas fijas a /usr/local/lib. Ahora
    libslex.so.0 tiene los NEEDED y slex.pc las pide con Requires, asi que
    PKG_CHECK_MODULES([SLEX],[slex]) alcanza.

  * El README decia de si mismo que "no funciona mucho mas alla de los
    ejemplos dados". Eso quedo viejo: ahora el punto 13 cuenta el estado
    con los numeros y como comprobarlos. ANALISIS.md, que era un
    relevamiento de cuando slex era un experimento, lo aclara al principio.

  * Ejemplo 09 nuevo: tokeniza los 246 .d y .ds del corpus
    Simul/varios/dods, todos. El 10, que le agregaba los estados y las
    acciones que producen el .dat, dejo de ser un ejemplo: es el programa
    d2datdinc, con su propio paquete.

  * Paquete d2datdinc, el tercero de este arbol: el programa que lleva un .d,
    un .ds o un .ds2 al dato JSON de Simul. Instala la tabla de regexps en
    /usr/share/slex/d2datdinc.slex y la biblioteca de elementos de SCEPTRE en
    /usr/share/slex/biblioteca, que antes se buscaban en el arbol de fuentes
    y en el home del autor. No agrega dependencias: enlaza libslex y la
    libdato de este mismo arbol.

  * "make check" de verdad, que antes no existia: no habia ningun TESTS en
    ningun Makefile.am y las pruebas eran un script con el prefijo de
    instalacion cableado. Ahora tests/ trae test_api (38 comprobaciones de
    la API) y badini (config invalida), compilados contra la biblioteca de
    este arbol y no contra una instalada, y el paquete Debian los corre al
    construirse: si una prueba falla, el .deb no se arma.

  * Las pruebas de d2datdinc no se salen de slex. Antes buscaban archivos
    en Simulparser/pruebas, o sea afuera del paquete, y por eso no podian
    correr en el build del .deb. Estan copiadas en d2datdinc/pruebas: 12
    pruebas, todas bien, sin necesitar Perl. Y contra-dods, nuevo, es la
    regresion del corpus completo contra los .dat congelados: 191 iguales,
    0 distintos, tambien sin Perl. La comparacion cruzada contra
    Simulparser, contra-ds2dat, sigue aparte, que es donde corresponde.

  * Directorio dato/: la parte de d2datdinc que no sabe nada del formato
    que lee --el arbol JSON, las utilidades de texto y rutas, y el
    evaluador de numeros y listas de Perl-- salio a su propio lugar, para
    que el proximo lector que se escriba con slex no empiece de cero. Son
    320 lineas de codigo; las otras 1439 de d2datdinc/dato.c son el
    lenguaje .d/.ds, que no tiene nada de generalizable.

  * El .din de un circuito, en C y desde el arbol. slex/din traduce el dato
    de Simul a un sistema del modulo dinamico de Calcula. Es la traduccion de
    Simul2/lib/Simul2/DesdeDat.pm, que sigue en Perl y es el oraculo: los 18
    circuitos del corpus que las dos implementaciones traducen dan los 36
    .din iguales byte a byte y los 2 .json de configuracion tambien. La
    diferencia que importa no es el lenguaje sino la entrada: el de Perl parte
    de un archivo .dat y esto parte del arbol en memoria, asi que no hay
    archivo intermedio. Las reglas de traduccion estan en din/README.md.

  * El alcance crecio de 18 a 155 de los 184 datos del corpus, resolviendo una
    categoria por vez y comprobando en cada paso que los 36 .din de los 18 que
    el Perl tambien traduce siguieran iguales byte a byte. El tipo de rama ya
    no se enumera --lo que importa es su modelo en el dato, con cuantos
    parametros y cuantas ecuaciones trae, asi que JB, ESIN, MER, MQER y MHER
    entraron sin nombrarlas--; el valor de una rama puede ser el nombre de un
    parametro, que entonces se comparte entre las ramas que lo nombren, o una
    expresion que se traduce simbolo por simbolo, o varias partes separadas
    por comas cuando el modelo pide varios parametros; las tablas del .d
    llegan al .din con sus filas adentro, y T1(punto) y XTABLE(tabla, punto)
    salen como tabla(...), la funcion que provee Calcula; los datos que no
    describen circuitos dan un .din con solo la carga comentada; y los 30
    archivos que numeran sus nodos desde 1 eligen masa, anotada con
    # @masa N .

  * Un detalle del que conviene acordarse: la derivada de una rama es estado
    de integracion solo cuando su modelo tiene una sola expresion. Con dos, la
    segunda da la derivada en lugar de relacionarla --ESIN es una fuente y su
    derivada es dato-- y entonces no hay nada que integrar. Ademas de ser lo
    fisico, es lo que hace cerrar los grados de libertad.

  * Tres reglas de nombre demasiado estrechas impedian que una tabla del .d
    llegara al .din, y estaban en tres lugares: la regla d_tabla de la tabla
    de expresiones regulares no admitia un nombre numerico, asi que TABLE 1 no
    se reconocia; d2datdinc se negaba a traducir esa referencia suponiendo que
    era de un MODEL de SCEPTRE, lo que en a02.d es falso porque TABLE 1 esta
    en la seccion FUNCTIONS de la corrida principal; y la validacion de din,
    copiada del Perl, exigia que el nombre empezara con letra.

  * Dos defectos del Perl, arreglados en Simul2 para que la comparacion sea
    exacta: encode_json dejaba las claves de los comentarios en el orden del
    hash, asi que el mismo .dat daba un .din distinto en cada corrida
    (comprobado con PERL_HASH_SEED: cuatro semillas, cuatro ordenes); y el
    print sin capa escribia el byte interno, de modo que una o acentuada
    salia como un 0xF3 que no es UTF-8 valido.

  * La seccion FUNCTIONS del .d ya no se pierde. El parser aceptaba
    argumentos de un solo caracter, asi que QC(AA,BB) = ... se descartaba con
    un aviso que solo se ve con -v, y con ella las 97 ramas de c11.d que la
    usan. Y ahora la lista de argumentos se guarda en el dato, que el
    postparser de Siru tiraba: sin ella no se puede volver a escribir la
    funcion. Las funciones van a la seccion funciones del .din.

  * dato/json.c: j_leer y j_leer_archivo, el lector que le faltaba al modulo
    --verificado con ida y vuelta sobre los 191 .dat del corpus, 191 iguales
    y 0 distintos-- y j_escribir_bonito, que reproduce el pretty de JSON::PP.

  * PARSERS.md, nuevo: por que slex y no Simulparser para armar un parser,
    por que no tiene sentido vincularlo a Perl con SWIG, y el plan de lo que
    falta --el programa d2datdinc y el escritor de .din-- con lo que cada paso
    deja verificable.

4.0
