operators interpreter vm-implementation forth

operators - ¿Cuáles son los primeros operadores Forth?



interpreter vm-implementation (7)

¿Qué implementación de Forth está utilizando que no proporciona esta información en la documentación? Dada la naturaleza de Forth, podría depender de la implementación. Hay un conjunto de palabras estándar en el diccionario, pero no importa si llegaron allí por ensamblador / C / lo que sea o por Forth, ya que Forth es, por definición, un lenguaje autoextensible.

Estoy interesado en implementar un sistema Forth, solo para que pueda obtener algo de experiencia en la construcción de una máquina virtual y un tiempo de ejecución simples.

Cuando se inicia en Forth, uno normalmente aprende sobre la pila y sus operadores (DROP, DUP, SWAP, etc.) primero, por lo que es natural pensar en estos como uno de los operadores primitivos. Pero no lo son. Cada uno de ellos se puede dividir en operadores que manipulan directamente la memoria y los punteros de pila. Más tarde, uno aprende acerca de store (!) Y fetch (@) que se puede utilizar para implementar DUP, SWAP, etc. (¡ja!).

Entonces, ¿cuáles son los operadores primitivos? ¿Cuáles deben implementarse directamente en el entorno de tiempo de ejecución a partir del cual se pueden construir todos los demás? No estoy interesado en alto rendimiento; Quiero algo de lo que yo (y otros) podamos aprender. La optimización del operador puede venir más tarde.

(Sí, soy consciente de que puedo comenzar con una máquina de Turing e ir desde allí. Eso es un poco extremo).

Editar: Lo que pretendo es similar a arrancar un sistema operativo o un nuevo compilador. ¿Qué necesito implementar, como mínimo, para poder construir el resto del sistema a partir de esos bloques primitivos? No implementaré esto en hardware desnudo; como ejercicio educativo, escribiría mi VM mínima.


Hace mucho tiempo, tuve un libro llamado "Threaded Interpretive Languages", publicado creo por Byte, que discutía cómo implementar un lenguaje Forth (no creo que lo hayan llamado Forth) en el ensamble Z80.

Puede que no tengas un Z80 a mano o que quieras uno, pero el libro puede ser instructivo.


Todavía no estoy convencido de que la pregunta esté bien formada. Por ejemplo, las instrucciones de Plinth se pueden reducir; después de todo, * y / pueden implementarse en términos de + y - , pero luego ''+'' puede implementarse en términos de una función sucesora (ver los axiomas de Peano .) Lo que lo ubica en el vecindario de una máquina de Turing. ¿Cómo sabes dónde parar?



Esta publicación en comp.lang.forth enumera algunos "Forth mínimos".

http://groups.google.com/group/comp.lang.forth/msg/10872cb68edcb526

¿Por qué sé esto? Mi hermano, Mikael, escribió # 3 y también escribió un artículo sobre hacer un "Forth mínimo" (en sueco, sin embargo). Si mal no recuerdo, quería obtener un conjunto mínimo de operadores que pudieran construirse en silicio.


Este hilo cubre tu pregunta exacta. Aquí hay una implementación de sopa a nueces con documentación completa.

Escribí una subrutina con Forth dirigida a 68K cuando estaba en la universidad. Definí el entorno de tiempo de ejecución y el formato del diccionario, luego escribí un código C que arrancaba una aplicación de Macintosh que cargaba un diccionario predeterminado, llenaba algunos vectores de E / S y ejecutaba el código. Luego tomé el libro de Leo Brodie Starting Forth y comencé a implementar el diccionario básico en lenguaje ensamblador 68K. Empecé con palabras aritméticas / lógicas, luego controlé estructuras y luego palabras de definición / manipulación de palabras. Tengo entendido que, como mínimo, necesitas @,!, +, -, * y /. El resto se puede implementar en términos de eso, pero eso es como tratar de escribir una biblioteca de gráficos completa basada en SetPixel y GetPixel : funcionará, pero ¿por qué?

Disfruté el proceso ya que había algunos acertijos realmente interesantes, como obtener DOES> exactamente correcto (y una vez que tuve una implementación sólida de DOES> , estaba creando cierres que se convirtieron en pequeñas cantidades de código).


Al contrario de lo que dices, generalmente DROP SWAP, etc. se consideran operaciones básicas de Forth. La razón es que si los implementa usando operaciones de memoria como sugiere, el sistema general se vuelve más, no menos complicado. Tampoco hay una distinción clara en Forth entre lo básico y lo que no. En los años 80, la búsqueda de un diccionario sería básica y se codificaría en ensamblador para velocidad, mientras que un Linux hospedado moderno puede permitirse codificarlo en el llamado nivel alto. También los Forthers tienden a recodificar rutinariamente palabras de ensamblador en alto nivel y palabras de alto nivel en ensamblador. Soy el autor de ciforth y yourforth. Es posible definir <= como "> no" como lo hice en ciforth. Pero en el futuro decidí que tener todas <==== = rutinas de ensamblador pequeñas similares, de aspecto uniforme, era efectivamente más simple. Esa es una decisión, y una cuestión de gusto, ciertamente no es una cuestión de principios.

En el contexto, interpreto la pregunta como: "¿Cuál es el tamaño razonable para que el número de operaciones primitivas llegue a un Forth razonablemente avanzado con una velocidad razonable?" Claramente, no le interesan los trucos ingeniosos para deshacerse de una palabra de ensamblador a expensas de una tremenda sobrecarga que se encuentra en algunos de los temas que tratan este tema.

Ahora puedes ver un número de Forth pequeños como jones en adelante y concluir que, en su mayoría, uno llega a entre 50 y 100 primitivos. Esos Forth se definen en ensamblador. Si desea definir sus primitivas en c, python o Java, la situación nuevamente es diferente. Ahora, por ejemplo, para la búsqueda del diccionario anterior, puede elegir entre c y Forth. Consideraciones que no tienen nada que ver con el diseño del lenguaje entran en juego. Puede ser un c-programador prolífico, o puede insistir en codificarlo en Forth porque es un proyecto de aprendizaje.