Введение

Диалект ALGOL 60 для системного программирования

SAIL, язык искусственного интеллекта Стэнфорда, был разработан Дэном Суинхартом и Бобом Спроулом из лаборатории искусственного интеллекта Стэнфорда. Изначально это был крупный язык, подобный ALGOL 60, для ЭВМ PDP 10 и DECSYSTEM 20. Язык объединил более ранний компилятор GOGOL для PDP 6/10, по сути, версию ALGOL, работающую только с целыми числами, с ассоциативной памятью из языка LEAP. Первый выпуск состоялся в ноябре 1969 года, и разработка продолжалась вплоть до 1980-х годов, включая коммерческую версию MAINSAIL. Главной особенностью SAIL является символьная система данных, основанная на ассоциативной памяти, разработанной Джерри Фельдманом и Полом Ровнером на основе LEAP. Элементы могут храниться в виде неупорядоченных множеств или ассоциаций (троек). Другие особенности включают процессы, процедурные переменные, события и прерывания, контексты, отслеживание (backtracking) и сбор мусора. Он также поддерживает блочные макросы, сопрограммы и новые типы данных, предназначенные для построения деревьев поиска и списков ассоциаций.

История

Компилятор GOGOL был первоначально написан Биллом МакКиманом на PDP 1. По сути, это была версия ALGOL 60, работающая только с целыми числами, с рядом дополнений, обеспечивающих прямой доступ к памяти и другому оборудованию, что позволяло использовать его как язык системного программирования. Массивы были сведены к одному измерению, возможность динамического выделения памяти была исключена, но были добавлены некоторые дополнительные функции для работы со строками. Значительно обновлённая версия, GOGOL II, была написана Джоном Саутером в рамках переноса базовой операционной системы с ODIN на THOR. Когда лаборатория искусственного интеллекта Стэнфорда получила PDP 6, Саутер, Петтит и (в основном) Дэн Суинхарт написали GOGOL III для новой машины. Суинхарт, объединившись с Робертом Спроуллом, объединил синтаксис GOGOL с дополнениями из современных версий языка LEAP, чтобы создать первую версию SAIL в ноябре 1969 года. Основной особенностью LEAP было использование ассоциативной памяти, более известной сегодня как карта или словарь. В LEAP можно было установить значение поля в типе, используя тройку, где первым элементом было имя переменной, вторым – имя поля, а третьим – значение. Дальнейшие улучшения внесли Рассел Тейлор, Джим Лоу и Хана Самет, добавив процессы, переменные-процедуры, прерывания, контекст, процедуры сопоставления, новую макросистему и другие функции. Затем разработка перешла к Тейлору, Джону Райзеру и Роберту Смиту, которые добавили отладчик, системную инструкцию печати, записи и выполнили преобразование из собственной SUAI Стэнфорда в TENEX. Позже он был портирован на TOPS 10 от DEC, а оригинальная версия TENEX работала без изменений под TOPS 20.

Процедура заявлений

Процедуры были реализованы аналогично языку программирования C, с типом возвращаемого значения, если он есть, перед именем, например, Обратите внимание на необычное использование точки с запятой здесь, в то время как в Паскале сразу следовал блок, обычно . Для повышения производительности в SAIL были добавлены два квалификатора процедур: и . Первый сообщал компилятору, что процедура может вызывать себя, и, следовательно, её локальные переменные должны записываться в стек, а не только информация о возврате из подпрограммы. Второй делал обратное, требуя, чтобы процедура вообще не имела локальных переменных, не позволяя выходить из функции и не позволяя обращаться к переменным внешней процедуры. Эти директивы могли избежать необходимости заполнения полного регистра активации, тем самым повышая производительность. Это также имело побочный эффект: переменные, объявленные внутри процедуры, не помеченной как RECURSIVE, не сбрасывались между вызовами, действуя аналогично в C. SAIL также включал квалификатор , используемый для вставки прямых объявлений, обычно когда две процедуры вызывают друг друга. работал как в C, выходя из процедуры и возвращаясь к вызывающей стороне, а также, опционально, возвращая значение, если процедура его использует. Параметры, передаваемые процедурам, могли передаваться по значению или по ссылке, последнее позволяло передавать значения обратно.

Основные типы данных и операторы

Основными типами переменных в SAIL являются целые числа, числа с плавающей точкой, логические значения и строки. Преобразование типов выполнялось автоматически, поэтому значение 5 преобразовывалось в число с двойной точностью, так как это требуется функции SQRT, а затем результат приводился к целому числу. Любой из этих типов можно было преобразовать в массив, добавив соответствующий квалификатор и указав границы массива в квадратных скобках, например, SAIL поддерживал одномерные и двумерные массивы. Для присваивания использовалась левая стрелка, или символ подчеркивания на платформах, не поддерживающих кодировку Stanford ASCII. Язык включал в себя ряд стандартных функций, таких как квадратный корень, все распространенные математические операторы, и в целом был похож на большинство производных ALGOL для обычного программирования. Строками манипулировали с помощью срезов массивов, при этом `S[i:j]` возвращала подстроку, содержащую символы с индексами от i до j, а `S[i:]` возвращала подстроку, начинающуюся с индекса i и длиной j символов. Ключевое слово `(inity)` обозначало конец строки, поэтому можно было использовать `S[i:]` для получения подстроки, начиная с индекса i и до конца строки. Функции и операторы для работы со строками включали оператор равенства для проверки, равны ли две строки, амперсанд для конкатенации, `S[0:j]` и оператор, удаляющий первый символ из строки. Сравнить строки иным способом, чем с помощью оператора равенства, было невозможно, операторы, подобные `<` или `>`, были определены только для чисел.

Записи и указатели

Концепция записей как типа данных была введена лишь недавно, когда разрабатывался SAIL. Поэтому эта особенность демонстрирует признаки "добавления" к синтаксису языка. Например, структура записи определялась с помощью оператора: Этот оператор работал аналогично оператору в Паскале, определяя шаблон для записи. Для создания записи использовался оператор , который возвращал указатели. Указатели были типизированными и могли быть типизированы для нескольких типов, например, определяет rp, указатель либо на запись о человеке, либо на запись об университете. Указатели также могли быть объявлены для указания на Доступ к данным в записи был также своеобразным; для вывода имени файла человека, например, синтаксис был .

Сканер струн

В дополнение к базовой функциональности строк, SAIL включал в себя систему сканирования строк как часть базового языка. Она работала с переменными строк, в то время как в остальном аналогичная функция использовалась для сканирования строк, читаемых из файла. Обе использовали систему, известную как "таблица разрывов", которая состояла из набора символов, обозначающих места для остановки чтения, например, символы новой строки, различные пробельные символы и знаки препинания. Эти таблицы хранились в специальных структурах, и система допускала только 54 из них, число, которое не объясняется в документации. Для создания новой таблицы сначала вызывалась функция, которая возвращала следующий свободный слот в таблице, или "номер таблицы". Затем следовал вызов функции, которая принимала номер таблицы, строку с символами разрыва, другую строку с "исключаемыми символами", которые просто игнорировались при чтении (как будто их не было в строке), и, наконец, "режимы" – флаги, указывающие, как система должна работать. После установки программа могла многократно вызывать функции или и получать полные строки. Это включало в себя передаваемый по ссылке параметр, обычно brkchar, который содержал символ, вызвавший разрыв, позволяя, например, проверять на конец файла. Система концептуально похожа на функциональность `strtok` в C, которая является частью stdlib, а не частью самого языка, как в SAIL.

Вход/выход

Система ввода/вывода SAIL была основана на идее пронумерованных "каналов", устроенной несколько похоже на записи сканера. Чтобы открыть файл, сначала вызывали функцию для получения номера свободного канала, а затем настраивали этот канал с помощью различных параметров, описывающих файл и режимы его работы. Функция служила для закрытия файла. После открытия файл можно было читать, соблюдая вышеупомянутые правила сканирования, вызывая функцию и проверяя достижение конца файла. Файлы не имели имен при открытии; вместо этого, можно было направить канал на существующий файл, создать новый файл, связанный с каналом, или изменить имя существующего файла. Существующий файл можно было открыть для записи с помощью .

Во время операций ввода/вывода использовалось множество специальных обработчиков и переменных. Например, функция была жестко привязана к пользовательскому терминалу и всегда открыта, принимая ввод с терминала и возвращая символ прерывания в системную переменную . Функция обычно осуществляла вывод на тот же терминальный канал, но могла быть перенаправлена на любой другой открытый канал.

Директивы о компиляторе

В качестве языка системного программирования, производительность была важна, и для этого SAIL включал в себя механизм, который использовал замену строк аналогично макросам C. Отличие заключалось в том, что ограничители вокруг подстановки должны были быть определены, например, одним из распространенных способов использования этих макросов было определение символьных констант, таких как , поскольку они не входили в базовый язык. Другой способ – переопределить оператор в более короткий . Система также включала в себя систему условной компиляции с использованием операторов, в отличие от директив препроцессора, как в C. компилировал блоки между соответствующими и или. Условие в IFCR должно было быть известно во время компиляции, поэтому, как и в C, обычно представляло собой d-значение.

Данные LEAP

Основным отличием SAIL от других языков, произошедших от ALGOL, было включение ассоциативного хранилища из языка LEAP. Эта система позволяла размещать данные в структурах, подобных записям, и затем сохранять, извлекать и осуществлять поиск по ним. В этом отношении она была схожа с возможностями обработки данных в COBOL. В основе хранилища лежала ассоциация или тройка, позволяющая связывать значение данных с именованным полем в записи. Например, можно было создать запись типа с "Том" и установить поле на "Гарри". Это привело бы к тройке вида (Отец, Том, Гарри). Связанные библиотеки могли бы найти все записи с "Гарри" в качестве , возможно, вернув "Том" и "Алису".

Применение

В SAIL было запрограммировано несколько интересных программных систем, включая ранние версии FTP и TeX, систему форматирования документов под названием PUB и BRIGHT – проект клинической базы данных, финансируемый Национальными институтами здоровья. В 1978 году для PDP 10 существовало полдюжины различных операционных систем: ITS (MIT), WAITS (Stanford), TOPS 10 (DEC), CMU TOPS 10 (Carnegie Mellon), TENEX (BBN), Tymcom X (Tymshare) и TOPS 20 (DEC, основанная на TENEX). SAIL был портирован из WAITS в ITS, чтобы исследователи MIT могли использовать программное обеспечение, разработанное в Стэнфордском университете. Как правило, каждый такой перенос требовал переписывания кода ввода-вывода для каждого приложения. В конце 1970-х годов была разработана машинно-независимая версия SAIL под названием MAINSAIL, которая использовалась для разработки многих инструментов автоматизированного проектирования (eCAD) в 1980-х годах. MAINSAIL легко переносился на новые процессоры и операционные системы и оставался в ограниченном использовании по состоянию на 2005 год.