PostScript, un lenguaje utilizado tradicionalmente para describir documentos impresos, opera bajo un modelo de ejecución basado en pilas donde los programas se procesan como una secuencia de tokens delimitados por espacios en blanco. Al mapear estas mecánicas a tipos de JavaScript, un desarrollador puede construir un intérprete 'de juguete' funcional que maneje aritmética básica, manipulación de arreglos y definiciones de procedimientos.
El modelo de ejecución basado en pilas
El núcleo de la lógica de PostScript se basa en dos estructuras principales: una pila de operandos y una pila de diccionarios. En este modelo, los operadores extraen valores de la pila de operandos, realizan un cálculo y vuelven a insertar el resultado en la pila. Los nombres, como `/double`, se insertan en la pila como literales, mientras que otros nombres se buscan en el diccionario para encontrar los procedimientos asociados.
Por ejemplo, el comando `3 double` primero inserta el número 3. El intérprete luego busca 'double' en la pila de diccionarios, encuentra su procedimiento asociado (por ejemplo, `{2 mul}`) y lo ejecuta. Este procedimiento extrae el 3, lo multiplica por 2 y vuelve a insertar el resultado, 6, en la pila.
“PostScript está diseñado principalmente para describir documentos impresos: tiene un rico vocabulario de características para crear y manipular gráficos rasterizados y vectoriales.”
— Let's Write a PostScript Interpreter
Mapeando PostScript a JavaScript
Para construir un intérprete simplificado, los tipos de datos de PostScript deben traducirse a equivalentes de JavaScript. Los números y booleanos se mapean directamente a los tipos `Number` y `Boolean` de JavaScript, mientras que los arreglos y diccionarios de PostScript se representan como objetos `Array` y `Map` de JavaScript. Un concepto único de PostScript, el `mark`, se implementa como un objeto singleton para rastrear las posiciones de la pila para la construcción de arreglos.
El proceso de análisis utiliza una expresión regular para dividir un programa en tokens, filtrando los comentarios de línea (que comienzan con `%`). El intérprete luego itera a través de estos tokens. Si un token es un nombre sin el prefijo `/`, el sistema recupera su procedimiento del diccionario; si es un procedimiento, el intérprete ejecuta la secuencia de tokens contenidos dentro de las llaves.

Esta lista de código ilustra la traducción de los tipos de PostScript a objetos de JavaScript y la ejecución de procedimientos.
Implementación y limitaciones
La maquinaria del intérprete está contenida en gran medida en definiciones de operadores primitivos. Estos incluyen aritmética estándar (add, sub, mul, div), manipulaciones de pila (dup, exch, roll) y flujo de control (ifelse, for, forall). Al instalar estas primitivas en la pila de diccionarios durante la inicialización, el intérprete puede ejecutar expresiones anidadas complejas como `[11 22 33] {10 mul} forall`.

Esta imagen proporciona un ejemplo concreto de la maquinaria del intérprete y cómo maneja la secuencia de tokens.
Esta implementación sirve como un ejercicio de 'escala humana' para comprender las mecánicas del lenguaje en lugar de ser una herramienta lista para producción. Omite intencionalmente características complejas como el manejo de cadenas y el manejo de errores por simplicidad. Un motor de PostScript a gran escala, como Ghostscript, involucra una complejidad adicional significativa con respecto al renderizado de gráficos de alta fidelidad y la gestión de fuentes que está fuera del alcance de un intérprete básico.
Qué dicen los lectores de Hacker News
- Experiencia: Un comentarista sugirió que construir un pequeño intérprete es un valioso ejercicio educativo para comprender cómo funcionan internamente los lenguajes de programación. comentario ↗
- Escéptico: Otro lector argumentó que el lenguaje base es relativamente simple y que la verdadera complejidad de una implementación de PostScript reside en el renderizado de gráficos y fuentes. comentario ↗
Resumen de opiniones del hilo de Hacker News; no son hechos verificados. Leer el debate completo ↗