Skip to content

sadScript 💔

SadScript es un lenguaje de programación temático donde cada palabra clave duele. Está diseñado para sentirse triste, cansado y existencial por fuera, pero seguir siendo formalmente compilable por dentro.

oscura_mente.sad
dolor edad = 20;
vacio promedio = 3.14;
esperanza seguir_vivo = vivo;
recuerdo mensaje = "nadie me entiende";

si_duele (edad > 18) {
confesar("ya soy adulto... qué triste");
}

“SadScript debe sentirse triste, cansado y existencial por fuera, pero seguir siendo formalmente compilable por dentro.”

El lenguaje une dos mundos:

  • El tema: vocabulario emo en primera persona — toda palabra reservada evoca angustia, melancolía o drama existencial.
  • La técnica: un analizador léxico (Flex) + parser (Bison) con precedencia de operadores, tipos explícitos, colecciones reales y reporte de errores preciso.

Es un proyecto ideal para:

  • Aprender cómo funcionan lexers y parsers en compiladores
  • Explorar diseño de lenguajes con un toque de humor negro
  • Usar como base para un lenguaje completo con intérprete o transpilador

SadScript es actualmente un validador de sintaxis. El analizador léxico y el parser están completos y funcionales. No hay todavía un intérprete, generación de código, ni análisis semántico (type checking, AST, etc.).

FaseEstado
Léxico (Flex)✅ Completo
Sintáctico (Bison)✅ Completo
Semántico❌ Pendiente
Intérprete / VM❌ Pendiente
Generación de código❌ Pendiente

  • Flex (analizador léxico)
  • Bison (generador de parser)
  • GCC o Clang (compilador C)

Instalación en Linux (Debian/Ubuntu):

Terminal window
sudo apt install flex bison build-essential

Instalación en macOS:

Terminal window
brew install flex bison
Terminal window
git clone https://github.com/Aserturik/sadScript.git
cd sadScript
./build.sh

Esto genera:

  1. a_sintactico.tab.c / .h — parser generado por Bison
  2. lex.yy.c — lexer generado por Flex
  3. sadScript — binario ejecutable
Terminal window
./sadScript tests/01_happy.sad

Salida exitosa:

Análisis léxico y sintáctico completado sin errores.
Terminal window
./run_tests.sh

SadScriptSignificadoEjemplo
dolorEntero (int)dolor edad = 20;
vacioFlotante (float)vacio promedio = 3.14;
esperanzaBooleano (bool)esperanza ok = vivo;
recuerdoCadena (string)recuerdo msg = "hola";
cicatrizVector / arreglo 1Dcicatriz nums = [1,2,3];
traumaMatriz / arreglo 2Dtrauma mat = [[1,2],[3,4]];
Tipo de literalEjemploToken interno
Booleano trueesperanza x = vivoTRUE
Booleano falseesperanza x = muertoFALSE
NulonuloNULO
Cadenarecuerdo x = "texto"LITERAL_CADENA
Caracter'a'LITERAL_CARACTER
Enterodolor x = 42LITERAL_ENTERO
Flotantevacio x = 3.14LITERAL_DECIMAL
Notación científicavacio x = 1.5e10 (también 42e10)LITERAL_DECIMAL
dolor edad = 20;
vacio promedio = 3.14;
esperanza seguir_vivo = vivo;
recuerdo msg = "hola mundo";
cicatriz nums = [1, 2, 3, 4];
trauma mat = [[1.2, 3.4], [5.6, 7.8]];

Las variables se pueden declarar sin inicializar:

dolor edad;
recuerdo nombre;
edad = 21;
edad += 1;
edad -= 1;
edad *= 2;
edad /= 2;
edad %= 3;
OperadorOperación
+Suma
-Resta
*Multiplicación
/División
%Módulo
**Potencia
++Incremento
--Decremento
OperadorSignificado
==Igual a
!=Diferente de
<>Diferente de (alternativo)
<Menor que
<=Menor o igual que
>Mayor que
>=Mayor o igual que
SímboloPalabraSignificado
&&yAND
||oOR
!noNOT

Puedes usar ambas formas:

si_duele (edad > 18 && vivo) { ... }
si_duele (edad > 18 y vivo) { ... }
OperadorToken internoUso típico
->FLECHAPunteros / acceso
=>FLECHA_DOBLELambdas / mapeo
#NUMERALDirectivas / metadatos
@ARROBADecoradores / anotaciones

SadScript incluye operadores de agregación sobre colecciones:

sum([1, 2, 3]) // → 6
prod([2, 3, 4]) // → 24
max([1, 5, 2]) // → 5
min([1, 5, 2]) // → 1
prom([1, 2, 3]) // → 2.0

De menor a mayor precedencia:

PrecedenciaOperadoresAsociatividad
1 (más baja)= += -= *= /= %=Derecha
2|| oIzquierda
3&& yIzquierda
4== != <>Izquierda
5> < >= <=Izquierda
6+ -Izquierda
7* / %Izquierda
8**Derecha
9 (más alta)! no - (unario)Derecha
si_duele (edad > 18) {
confesar("adulto");
} si_no_duele_pero (edad > 12) {
confesar("adolescente");
} si_no_duele {
confesar("niño");
}
mientras_duela (vivo) {
confesar("todavía respiro");
cortarme; // break
}
para_que_duela (dolor i = 0; i < 10; i = i + 1) {
confesar("duele");
}
aunque_no_quiera {
confesar("una vez");
} mientras_duela (condicion);
switch.exe
segun_mi_animo (edad) 
    caso 18:
        confesar("ya casi");
        cortarme;
    caso 21:
        confesar("legalmente adulto");
        cortarme;
    por_defecto_mio:
        confesar("sin rumbo");
cortarme; // break
seguir_fingiendo; // continue
ritual llorar() : vacio {
confesar("hola mundo");
regresar_a_llorar 0.0;
}
ritual gritar(dolor intensidad) {
confesar("AHH");
}
ritual sumar(dolor a, dolor b) : dolor {
regresar_a_llorar a + b;
}
llorar();
gritar(intensidad: 10); // argumento nombrado
sumar(a: 5, b: 3); // múltiples argumentos nombrados
cicatriz numeros = [1, 2, 3, 4];
numeros[0] = 10;
confesar(numeros[2]); // imprime 3
trauma sombras = [[1.2, 3.4], [5.6, 7.8]];
confesar(sombras[0][1]); // imprime 3.4
intentar_sentir {
confesar("intentando...");
} romperse(error) {
confesar(error);
}
colapsar("algo salió mal");
afirmar(edad > 0);
confesar("mensaje");
confesar(edad);
confesar(3.14);
// Comentario de línea
/* Comentario
multilínea */

// Declaraciones base con tipos emo
dolor edad = 20;
vacio promedio = 3.14;
esperanza seguir_vivo = vivo;
recuerdo mensaje = "nadie me entiende";
// Colecciones
cicatriz numeros = [1, 2, 3, 4];
trauma sombras = [[1.2, 3.4], [5.6, 7.8]];
// Condicional
si_duele (edad > 18) {
confesar("ya soy adulto");
}
// Ciclo while con break
mientras_duela (seguir_vivo) {
confesar("todavia respiro");
cortarme;
}
// Función con retorno
ritual llorar() : vacio {
confesar("hola mundo");
regresar_a_llorar 0.0;
}
dolor edad = 15+3-0;
segun_mi_animo (edad) {
caso 18:
confesar("ya casi");
por_defecto_mio:
confesar("sin rumbo");
}
dolor edad = 20;
confesar("léxico roto");
~ // ← error: '~' no está en el alfabeto del lenguaje

Salida:

Error léxico en línea 4, columna 3: token no reconocido '~'
dolor edad = 20;
si_duel (edad > 18) { // ← error: debería ser "si_duele"
confesar("typo en keyword");
}

Salida:

Error sintáctico en línea 4, columna 18: syntax error, unexpected LLAVE_IZQ, expecting PUNTO_COMA cerca de '{'
dolor edad = 21;
si_duele (edad > 18) {
confesar("parser roto");
// falta la llave de cierre

Salida:

Error sintáctico en línea 4, columna 4: syntax error, unexpected end of file
dolor edad = 20
confesar("falta punto y coma"); // ← error: falta ; en línea anterior

SadScript sigue el modelo clásico de frontend de compilador en dos fases:

Código fuente (.sad)
│
▼
┌─────────────────┐
│ Flex Lexer │ a_lexico.l
│ (análisis │ → produce tokens
│ léxico) │
└────────┬─────────┘
│ tokens
▼
┌─────────────────┐
│ Bison Parser │ a_sintactico.y
│ (análisis │ → valida gramática
│ sintáctico) │ → llama yyerror() si falla
└────────┬─────────┘
│
▼
✅ Éxito (exit 0)
❌ Error (exit 1 + mensaje)
  • ~195 líneas de definiciones Flex
  • Reconoce ~78 tokens: palabras reservadas, operadores, literales, identificadores, delimitadores
  • Soporta caracteres con acentos del español (áéíóúüñÁÉÍÓÚÜÑ)
  • Comentarios multilínea mediante estados Flex (COMENTARIO_MULTI)
  • Reporte de errores léxicos con línea y columna exacta
  • Los identificadores soportan caracteres Unicode del español
  • 356 líneas de gramática Bison
  • Precedencia de operadores declarativa con %left, %right, %nonassoc
  • %define parse.error verbose para mensajes de error descriptivos
  • yyerror() imprime: Error sintáctico en línea X, columna Y: <mensaje> cerca de '<token>'
  • Errores léxicos → se reportan a stdout
  • Errores sintácticos → se reportan a stderr
  • Ambos incluyen número de línea y columna
  • main() devuelve exit 0 en éxito, exit 1 en cualquier error
Terminal window
bison -d a_sintactico.y # genera parser + header
flex a_lexico.l # genera lexer
cc lex.yy.c a_sintactico.tab.c -o sadScript -lfl -lm # compila

ArchivoEsperadoDescripción
tests/01_happy.sad✅ exit 0Programa válido completo
tests/02_typo_keyword_fail.sad❌ exit 1Keyword mal escrita
tests/03_missing_semicolon_fail.sad❌ exit 1Punto y coma faltante
tests/04_default_switch.sad✅ exit 0Switch con default
tests/05_lexer_fail.sad❌ exit 1Caracter inválido ~
tests/06_parser_fail.sad❌ exit 1Bloque sin cerrar
Terminal window
./run_tests.sh

El script:

  1. Compila el proyecto con build.sh
  2. Ejecuta cada .sad verificando código de salida
  3. Verifica mensajes de salida esperados (stdout/stderr)
  4. Reporta PASS/FAIL con resumen

La tabla completa de mapeo entre el vocabulario emo visible y los tokens internos del parser:

SadScriptToken internoSignificado
dolorENTEROTipo entero
vacioFLOTANTETipo flotante
esperanzaBOOLEANOTipo booleano
recuerdoCADENATipo cadena
cicatrizVECTORTipo vector
traumaMATRIZTipo matriz
si_dueleIFCondicional if
si_no_dueleELSECondicional else
si_no_duele_peroELSEIFCondicional else if
segun_mi_animoSWITCHSwitch
casoCASECase
por_defecto_mioDEFAULTDefault
mientras_duelaWHILEBucle while
para_que_duelaFORBucle for
aunque_no_quieraDOBucle do-while
cortarmeBREAKBreak
seguir_fingiendoCONTINUEContinue
ritualFUNCTIONDefinición de función
regresar_a_llorarRETURNReturn
confesarPRINTSalida por consola
intentar_sentirTRYTry
romperseCATCHCatch
colapsarTHROWThrow
afirmarASSERTAssert
vivoTRUEBooleano verdadero
muertoFALSEBooleano falso
nuloNULONull
sumOP_SUMA_REDUCCIONSuma de colección
prodOP_PROD_REDUCCIONProducto de colección
maxOP_MAX_REDUCCIONMáximo de colección
minOP_MIN_REDUCCIONMínimo de colección
promOP_PROM_REDUCCIONPromedio de colección

Estas son las áreas donde SadScript puede crecer:

  1. Árbol de sintaxis abstracta (AST) — construir un AST en las acciones semánticas del parser
  2. Intérprete — evaluar el AST y ejecutar programas
  3. Type checker — validar tipos en tiempo de compilación
  4. Transpilador — generar código JavaScript, Python o C
  5. Standard library — funciones incorporadas (IO, matemáticas, strings)
  6. Sistema de módulos — imports y exports entre archivos .sad
  7. Editor support — resaltado de sintaxis para VS Code, Vim, etc.
  8. Playground web — intérprete en el navegador

sadScript/
├── a_lexico.l # Lexer (Flex)
├── a_sintactico.y # Parser (Bison)
├── a_sintactico.tab.c # Parser generado (no editar)
├── a_sintactico.tab.h # Tokens generados (no editar)
├── lex.yy.c # Lexer generado (no editar)
├── sadScript # Binario compilado
├── build.sh # Script de compilación
├── run_tests.sh # Suite de tests automatizada
├── requirements.md # Especificación del lenguaje
└── tests/
├── 01_happy.sad
├── 02_typo_keyword_fail.sad
├── 03_missing_semicolon_fail.sad
├── 04_default_switch.sad
├── 05_lexer_fail.sad
└── 06_parser_fail.sad

MIT — haz con esto lo que quieras, pero si duele, ya sabes por qué.


¿Te duele algo y querés contribuir? Bienvenido. Este proyecto acepta:

  • Pull requests con mejoras al lexer/parser
  • Nuevos tests (especialmente casos negativos)
  • Ideas para la semántica del lenguaje
  • Implementación de intérprete o transpilador
  • Temática: mientras más depresión existencial, mejor

“Duele, pero al menos compila.”