SAIL , el lenguaje de inteligencia artificial de Stanford , fue desarrollado por Dan Swinehart y Bob Sproull del Laboratorio de IA de Stanford . Originalmente era un gran lenguaje similar a ALGOL 60 para PDP-10 y DECSYSTEM-20 . El lenguaje combinaba el compilador GOGOL del lenguaje PDP-6 /-10 anterior, esencialmente una versión de ALGOL solo para números enteros , con el almacén asociativo del lenguaje LEAP . El primer lanzamiento fue en noviembre de 1969 y continuó desarrollándose hasta la década de 1980, incluido un derivado comercial, MAINSAIL .
La característica principal de SAIL es un sistema de datos simbólicos basado en un almacén asociativo basado en LEAP de Jerry Feldman y Paul Rovner. Los elementos se pueden almacenar como conjuntos desordenados o como asociaciones (triples). Otras características incluyen procesos, variables de procedimiento, eventos e interrupciones, contextos, retroceso y recolección de basura de registros . También tiene macros estructuradas en bloques, una función de corrutinas y algunos tipos de datos nuevos destinados a construir árboles de búsqueda y listas de asociaciones.
El compilador GOGOL fue escrito originalmente por Bill McKeeman en el PDP-1 . Era esencialmente una versión de ALGOL-60 que solo admitía números enteros , con una serie de añadidos para proporcionar acceso directo a la memoria y a otro hardware para permitir su uso como lenguaje de programación de sistemas . Redujo las matrices a una única dimensión, eliminó cualquier capacidad de realizar asignaciones de memoria dinámicas, pero añadió alguna funcionalidad de cadena adicional. Una versión muy actualizada por John Sauter, GOGOL II, fue escrita como parte de un puerto del sistema operativo subyacente de ODIN a THOR. Cuando el Laboratorio de IA de Stanford recibió su PDP-6 , Sauter, Pettit y (principalmente) Dan Swinehart escribieron GOGOL III para la nueva máquina. [1]
Swinehart, junto con Robert Sproull, fusionó la sintaxis de GOGOL con añadidos de las versiones contemporáneas del lenguaje LEAP para producir la primera versión de SAIL en noviembre de 1969. La característica principal de LEAP como lenguaje era su uso de almacenamiento asociativo, más comúnmente conocido hoy como Mapa o Diccionario. En LEAP, uno podía establecer el valor de un campo en un tipo usando un triple, siendo la primera entrada el nombre de la variable, la segunda el nombre del campo y la tercera el valor. [2]
Russell Taylor, Jim Low y Hana Samet añadieron mejoras adicionales, añadiendo procesos, variables de procedimiento, interrupciones, contexto, procedimientos de coincidencia, un nuevo sistema de macros y otras características. El desarrollo pasó entonces a manos de Taylor, John Reiser y Robert Smith, que añadieron un depurador, una sentencia de impresión a nivel de sistema, registros y realizaron la conversión del propio SUAI de Standord a TENEX . Más tarde se trasladó también a TOPS-10 de DEC, mientras que la versión original de TENEX funcionaba sin modificaciones con TOPS-20 . [2]
Al igual que muchos sistemas ALGOL, y el posterior Pascal , la estructura básica de SAIL se basa en el bloque , que se denota por el código entre las palabras clave BEGIN
y END
. Dentro de un bloque hay una estructura adicional, con las declaraciones de variables locales en la parte superior, si las hay, y el código, o declaraciones , a continuación. A diferencia de la mayoría de los dialectos, SAIL permitía colocar una cadena después de BEGIN
, como BEGIN "program"
, y luego terminar el bloque con END "program"
. El compilador usaría estos, si se ingresaban, para verificar el corchete correcto. [3] SAIL no incluía el equivalente de un PROGRAM
bloque como en Pascal, ni un main
como en C, la ejecución comenzaba con la primera línea de código en el bloque más externo. [4]
Las declaraciones estándar incluían IF...THEN...ELSE
, [5] FOR...STEP...UNTIL...DO
, [6] WHILE...DO
para los bucles con las pruebas más altas, WHILE...UNTIL
para los bucles con las pruebas más bajas y GOTO
que usaban una etiqueta. [7] El CASE
era similar a switch
en C, pero normalmente usaba una sintaxis algo diferente, como CASE i OF ("Zero","One","Two");
, que devuelve la cadena apropiada en función del valor de i. [5] Si uno quería probar valores explícitos en el CASE, los valores tenían que estar entre corchetes:
CASO I DE INICIO [ 0 ] 10 ; [ 4 ] 25 ; [ 6 ][ 7 ] 50 FIN ;
Este código ignorará valores como 1 a 3 y solo devolverá un valor para los valores enumerados. Tenga en cuenta que el último elemento no puede tener un punto y coma a continuación. [8]
DONE
Salía de un bloque, normalmente se usaba en bucles y CONTINUE
regresaba al principio del bloque. Un bucle infinito se implementaba normalmente con WHILE TRUE DO...
. [9]
Los procedimientos se implementaron de una manera similar al lenguaje de programación C , con el tipo de retorno, si lo hubiera, delante del nombre, por ejemplo, STRING PROCEDURE toUpper(STRING originalStr);BEGIN...
. Observe el uso poco común del punto y coma aquí, mientras que Pascal seguiría inmediatamente con un bloque, normalmente un BEGIN
. [10]
Para mejorar el rendimiento, SAIL agregó dos calificadores de procedimiento SIMPLE
y RECURSIVE
. RECURSIVE
le dijo al compilador que el procedimiento podría llamarse a sí mismo y, por lo tanto, sus variables locales tenían que escribirse en la pila, no solo la información de retorno de la subrutina. SIMPLE
hizo lo opuesto, exigiendo que el procedimiento no tuviera variables locales en absoluto, no permitiendo GOTO
salir de la función y no podía hacer referencia a las variables del procedimiento envolvente. Estas directivas podrían evitar el requisito de completar un registro de activación completo , mejorando así el rendimiento. [11] Esto también tuvo el efecto secundario de significar que las variables declaradas dentro de un procedimiento que no estaba marcado como RECURSIVO no se restablecerían entre llamadas, [11] actuando de manera similar a static
.
SAIL también incluía el FORWARD
calificador, usado para insertar declaraciones hacia adelante , típicamente cuando dos procedimientos se llaman entre sí. [10] RETURN
funcionaba como en C, saliendo del procedimiento y volviendo al llamador, así como también devolviendo opcionalmente un valor si el procedimiento usa uno. [12] Los parámetros pasados a los procedimientos podrían ser por VALUE
o REFERENCE
, el último permitiendo que los valores sean devueltos. [13]
Los tipos de variables básicos en SAIL son números enteros , reales (punto flotante), booleanos y cadenas . [14] Las conversiones de tipos eran automáticas, por lo que INTEGER i;i←SQRT(5);
convertían el valor 5 en un doble, ya que eso es lo que requiere SQRT, y luego convertían el resultado en un entero. [3] Cualquiera de estos tipos se puede convertir en una matriz agregando el ARRAY
calificador y colocando los límites de la matriz entre corchetes, por ejemplo, REAL ARRAY weeks[1:52]);
. SAIL admitía matrices unidimensionales y bidimensionales. [15]
El lenguaje usaba la flecha izquierda para la asignación, ←
o el guión bajo en plataformas que no tenían Stanford ASCII . [16] Incluía una serie de funciones estándar como raíz cuadrada , todos los operadores matemáticos comunes y, por lo demás, era similar a la mayoría de los derivados de ALGOL para la programación normal. [17]
Las cadenas se manipulaban utilizando el corte de matrices , con aStr[i TO j]
la devolución de la subcadena con caracteres de i a j, o aStr[i FOR j]
que devolvía la subcadena comenzando en i y continuando por j caracteres. [18] La INF
palabra clave (inity) representaba el final de la cadena, por lo que se podía aStr[i TO INF]
devolver todo desde i en adelante. [3] Se incluyeron funciones y operadores de cadena EQU
para probar si dos cadenas eran iguales, [5] el ampersand para la concatenación, LENGTH
y LOP
que elimina el primer carácter de la cadena. [18] No había forma de comparar cadenas que no fueran EQU
, operadores como <
se definieron solo para números. [4]
El concepto de registros como un tipo de datos se había introducido recientemente cuando se estaba escribiendo SAIL. Por lo tanto, esta característica muestra signos de estar "agregada" a la sintaxis del lenguaje. Por ejemplo, se definía una estructura de registro utilizando la RECORD!CLASS
declaración: RECORD!CLASS person (STRING name, address; INTEGER accountnum; REAL balance)
. Esta declaración funcionaba de manera similar a la RECORD
declaración en Pascal, definiendo la plantilla para el registro. Para crear un registro, se utilizaba la NEW!RECORD
declaración, que devolvía un RECORD!POINTER
. Los punteros se tipificaban y podían tipificarse en más de un tipo, por ejemplo, RECORD POINTER (person,university) rp;
define rp, un puntero a un registro de persona o universidad. [19] Los punteros también podían declararse para apuntar a ANY!CLASS
. [20] El acceso a los datos en un registro era igualmente idiosincrásico; para imprimir el archivo de nombre de una persona, por ejemplo, la sintaxis era PRINT(person:name[rp]);
. [20]
Además de la funcionalidad básica de cadenas, SAIL incluía un sistema de escaneo de cadenas como parte del lenguaje básico. El sistema de escaneo de cadenas SCAN
trabajaba con variables de cadena, mientras que el sistema de escaneo de cadenas, que por lo demás era similar INPUT
, se utilizaba para escanear cadenas que se leían desde un archivo. Ambos utilizaban un sistema conocido como "tabla de interrupción", que consistía en un conjunto de caracteres que representaban lugares donde detener la lectura; por ejemplo, saltos de línea, varios espacios en blanco y puntuación. Estas tablas se almacenaban en estructuras especiales y el sistema solo permitía 54 de ellas, un número que no se explica en la documentación. [21]
Para construir una nueva tabla, primero se llamaba a GETBREAK
una función que devolvía el siguiente espacio libre en la tabla, o "número de tabla". A esto le seguía un SETBREAK
, que tomaba el número de tabla, una cadena con los caracteres de ruptura, otra cadena de "caracteres de omisión" que simplemente se ignoraban durante la lectura (como si no estuvieran en la cadena) y finalmente los "modos", indicadores que indicaban cómo debería funcionar el sistema. Una vez configurado, el programa podía llamar repetidamente SCAN
a o INPUT
y devolver cadenas completas. [22] Esto incluía un parámetro de referencia, normalmente brkchar, que contenía el carácter que causaba la ruptura, lo que permitía probar, por ejemplo, los caracteres de fin de archivo. El sistema es conceptualmente similar a la strtok
funcionalidad de C, que es parte de stdlib [23] en lugar de ser parte del lenguaje en sí mismo como en SAIL.
El sistema de entrada/salida de SAIL se basaba en la idea de "canales" numerados de una manera similar a las entradas del escáner. Para abrir un archivo, primero se llamaba GETCHAN
para que devolviera un valor de un canal libre y luego OPEN
se lo editaba con varios parámetros para describir el archivo y los modos de operación. RELEASE
era equivalente a cerrar. Una vez abierto, el archivo podía leerse, sujeto a las reglas de escaneo mencionadas anteriormente, llamando INPUT
y buscando el final del archivo. Los archivos no tenían nombres como parte de OPEN, en cambio, LOOKUP
podían usarse para apuntar un canal a un archivo dado, ENTER
hacer que un nuevo archivo se asociara con un canal y RENAME
permitir que se cambiara el nombre de un archivo existente. [24] Se puede abrir un archivo existente para escribir usando GETCHAN... OPEN... LOOKUP... ENTER
. [25]
Existían numerosos controladores y variables especiales que se utilizaban durante la entrada y salida. Por ejemplo, la INCHWL
función era una INPUT conectada directamente al terminal de usuario y siempre abierta, y devolvía su carácter de interrupción en la variable del sistema !SKIP!
. [26] La PRINT
función normalmente tenía salida al mismo canal del terminal, pero también podía dirigirse a cualquier otro canal abierto. [27]
Como lenguaje de programación de sistemas, el rendimiento era importante y para ayudar con esto, SAIL incluyó un DEFINE
que usaba el reemplazo de cadenas de una manera similar a #define
las macros de C. [28] Una diferencia era que los delimitadores alrededor de la sustitución tenían que estar definidos, por ejemplo REQUIRE "[][]" DELIMITERS;DEFINE maxSize=[100];
. Un uso común de estas macros era definir constantes de caracteres como CRLF
, ya que estas no eran parte del lenguaje básico. [28] Otro era redefinir la COMMENT
declaración a la . más corta !
. [29]
El sistema también incluía un sistema de compilación condicional que utilizaba sentencias, en lugar de directivas de preprocesador como las que se encuentran en C. IFCR
compilaría los bloques entre los THENC
y ELSEC
o ENDC
. La condición en el IFCR debe conocerse en el momento de la compilación, por lo que, como en C, normalmente era un DEFINE
valor d. [30]
La principal diferencia entre SAIL y otros lenguajes derivados de ALGOL fue la inclusión del almacén asociativo del lenguaje LEAP. Este sistema proporcionaba un sistema que permitía colocar datos en estructuras similares a registros y luego guardarlos, recuperarlos y buscarlos. En este sentido, era similar a las características de manejo de datos de COBOL . La base del almacén era la asociación o triple , que permitía asociar un valor de datos con una ranura con nombre en un registro. Por ejemplo, uno podría crear un registro del tipo Family_Member
con Name
"Tom" y establecer el Father
campo en "Harry". Esto da como resultado un triple de la forma (Father, Tom, Harry). Las bibliotecas asociadas podrían entonces encontrar todos los Family_Member
s con "Harry" como Father
, tal vez devolviendo "Tom" y "Alice". [31]
El siguiente código, que se encuentra en el Tutorial, convierte una cadena de entrada a mayúsculas. [10]
PROCEDIMIENTO DE CADENA upper ( CADENA rawstring ) ; BEGIN " upper " CADENA tmp ; INTEGER char ; tmp ← NULL ; WHILE LENGTH ( rawstring ) DO BEGIN char ← LOP ( rawstring ) ; COMENTARIO LOP devuelve el primer carácter y mueve el puntero más allá de él tmp ← tmp & ( IF " a " LEQ char LEQ " z " THEN char - '40 ELSE char); END; RETURN(tmp); END "upper";
Se codificaron varios sistemas de software interesantes en SAIL, incluidas algunas versiones tempranas de FTP y TeX , un sistema de formato de documentos llamado PUB, [32] y BRIGHT, un proyecto de base de datos clínica patrocinado por los Institutos Nacionales de Salud . [33] [34] [35] [36] [37] [38] [39] [40] [41]
En 1978, había media docena de sistemas operativos diferentes para el PDP-10: ITS (MIT), WAITS (Stanford), TOPS-10 (DEC), CMU TOPS-10 (Carnegie Mellon), TENEX ( BBN ), Tymcom-X (Tymshare) y TOPS-20 (DEC, basado en TENEX).
SAIL fue trasladado de WAITS a ITS para que los investigadores del MIT pudieran utilizar el software desarrollado en la Universidad de Stanford . Cada traslado requería normalmente la reescritura del código de E/S en cada aplicación.
A fines de la década de 1970 se desarrolló una versión independiente de la máquina de SAIL llamada MAINSAIL y se utilizó para desarrollar muchas herramientas de diseño eCAD durante la década de 1980. MAINSAIL era fácilmente portable a nuevos procesadores y sistemas operativos, y todavía tenía un uso limitado en 2005 [actualizar].
El operador de subrayado en las asignaciones del código fuente de SAIL se imprimía como una flecha hacia la izquierda en la variante Stanford de ASCII, pero los sitios PDP-10 en otros lugares lo veían simplemente como un subrayado simple. Sin embargo, su uso como operador de asignación significaba que no podía usarse como una letra extendida para hacer que los nombres compuestos fueran más legibles, como es común ahora en muchos otros lenguajes de programación. La flecha hacia la izquierda en la variante Stanford de ASCII no era el único carácter inusual.