Рус Eng Cn Перевести страницу на:  
Please select your language to translate the article


You can just close the window to don't translate
Библиотека
ваш профиль

Вернуться к содержанию

Программные системы и вычислительные методы
Правильная ссылка на статью:

Разработка интерпретатора LISP

Барабаш Константин Алексеевич

студент кафедры компьютерных систем Казанского национального исследовательского технического университета им. А.Н.Туполева-КАИ

420015, Россия, республика Татарстан, г. Казань, ул. Большая Красная, 55

Barabash Konstantin Alekseevich

Student, Computer Systems Department, Kazan National Research Technical University named after A.N.Tupolev-KAI

420015, Russia, Republic of Tatarstan, Kazan, Bolshaya Krasnaya str., 55

kostyandriy@mail.ru
Другие публикации этого автора
 

 
Мангушева Алина Раисовна

кандидат физико-математических наук

доцент кафедры интеллектуальных систем и управления информационными ресурсами Казанского национального исследовательского технологического университета

420015, Россия, республика Татарстан, г. Казань, ул. Карла Маркса, 68

Mangusheva Alina Raisovna

PhD in Physics and Mathematics

Associate Professor, Department of Intelligent Systems and Information Resource Management, Kazan National Research Technological University

420015, Russia, Republic of Tatarstan, Kazan, Karl Marx str., 68

alinamr@mail.ru
Обухова Маргарита Юрьевна

старший преподаватель кафедры компьютерного моделирования и техносферной безопасности Казанского инновационного университета

420111, Россия, республика Татарстан, г. Казань, ул. Московская, 42

Obukhova Margarita Yur'evna

Senior Lecturer, Department of Computer Modeling and Technosphere Safety, Kazan Innovative University

420111, Russia, Republic of Tatarstan, Kazan, Moskovskaya str., 42

obuhova@ieml.ru
Григорян Карен Альбертович

кандидат экономических наук

магистрант кафедры интеллектуальных систем и управления информационными ресурсами Казанского национального исследовательского технологического университета

420015, Россия, республика Татарстан, г. Казань, ул. Карла Маркса, 68

Grigoryan Karen Al'bertovich

PhD in Economics

Master's student, Department of Intelligent Systems and Information Resource Management, Kazan National Research Technological University

420015, Russia, Republic of Tatarstan, Kazan, Karl Marx str., 68

karigri@yandex.ru

DOI:

10.7256/2454-0714.2022.4.39289

EDN:

ZAAPXY

Дата направления статьи в редакцию:

30-11-2022


Дата публикации:

30-12-2022


Аннотация: В статье освещаются аспекты разработки интерпретатора LISP. Несмотря на то, что язык LISP это не самый популярный в настоящие дни (в индексе TIOBE за ноябрь 2022 года данный язык стоит на 30-м месте), проделанная авторами работа является актуальной. Многие популярные на сегодня идеи и программные технологии были впервые разработаны с помощью LISP-машин. Разработанный интерпретатор позволяет программисту избегать определения элементов программы (функции, классы и др.) без необходимости. Также результат разработки позволяет запустить любую сущность LISP, которая возвращает осмысленный результат. Современные интерпретаторы LISP не имеют возможности перегрузки функций из-за чего пользователям приходится заучивать огромное количество имен функций, действия которых однотипны. Это значительно усложняет процесс обучения, так как приходится искать в документации названия примитивных функций. Из-за этого большая часть потенциальных пользователей бросает обучение, возвращаясь к современным языкам программирования, так и не познав возможности языка LISP. Статья раскрывает создание интерпретатора LISP, способного по простоте взаимодействия с объектами конкурировать с современными языками программирования. Также в статье предлагается подход, обеспечивающий механизм сборки мусора посредством подсчета ссылок на объекты.


Ключевые слова:

common lisp, lateral lisp, lisp программирование, языки программирования, язык C, сборщик мусора, разработка интерпретатора, интерпретатор lisp, объектно-ориентированное программирование, функциональное программирование

Abstract: The article highlights aspects of the development of the LISP interpreter. Despite the fact that LISP is not the most popular language these days (in the TIOBE index for November 2022, this language is in 30th place), the work done by the authors is relevant. Many popular ideas and software technologies today were first developed using LISP machines. The developed interpreter allows the programmer to avoid defining program elements (functions, classes, etc.) unnecessarily. Also, the development result allows you to run any LISP entity that returns a meaningful result. Modern LISP interpreters do not have the ability to overload functions, which is why users have to memorize a huge number of function names whose actions are of the same type. This greatly complicates the learning process, since you have to look for the names of primitive functions in the documentation. Because of this, most of the potential users drop out of training, returning to modern programming languages, without knowing the possibilities of the LISP language. The article reveals the creation of a LISP interpreter capable of competing with modern programming languages in terms of ease of interaction with objects. The article also suggests an approach that provides a garbage collection mechanism by counting references to objects.


Keywords:

common lisp, lateral lisp, lisp programming, programming languages, C language, garbage collector, interpreter development, lisp interpreter, object-oriented programming, functional programming

Архитектура интерпретатора

Разработанный интерпретатор LISP имеет три основные подпрограммы: read, eval, print. Процедура read:

● Отвечает за считывание входных данных и их обработку – разложение на имена атомов, знаки препинания; введение новых обычных атомов в таблицу атомов и новых числовых атомов в таблицу чисел; создание S-выражения, представляющего входные данные. (Термин «атом» является историческим; этот термин использовал разработчик LISP Джон Маккарти.) [1, 2]

● Возвращает типизированный указатель на созданное S-выражение. При этом, если входное S-выражение не является атомом, в области списка будет создана соответствующая структура списка.

Типизированный указатель, созданный в качестве вывода read, затем передается в качестве входных данных подпрограмме eval. Процедура eval реализует функцию EVAL из LISP. eval интерпретирует вычисление S-выражения, на которое указывает его ввод, рекурсивно вызывая себя для вычисления подвыражений.

Конечным результатом является S-выражение, которое при необходимости создается в области списка, и в качестве вывода возвращается типизированный указатель на это результирующее S-выражение. Если входное S-выражение недопустимо, eval выводит сообщение об ошибке и переходит к точке, где вызывается read, чтобы получить дополнительные входные данные.

Если исходный ввод поддается оценке, вывод типизированного указателя из eval направляется в качестве ввода для записи. Процедура print собирает имена и знаки препинания, необходимые для формирования текстовой строки, представляющей заданное S-выражение, и печатает эту строку на терминале (см. рисунок 1) [3, 4].

Рисунок 1. REPL

Таким образом, интерпретатор LISP имеет следующую форму.

1. Инициализация

2. o = read()

3. r = eval(o)

4. print(r)

5. перейти к шагу 2

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

Все типы данных представляются одной структурой l_object. Это сделано для возможности передавать любые объекты как аргументы для функций. Структура объекта выглядит следующим образом:

Листинг 1. Структура объекта

Поле class определяет класс объекта, сам класс, имеет тип данных класса, а его имя располагается в поле raw.s. Поле vars является таблицей символов и используется для хранения собственных переменных объекта. В случае пользовательской функции внутри vars создаются две переменные: args типа c_list и body типа c_list. Для встроенной функции или специальной формы в vars создается только args типа c_list, а ссылка на функцию указывается в raw.fn. Таблицу символов в свою очередь представляет следующая структура l_var, которая является обычным связным списком объектов, дополнительно содержащую поле name:

Листинг 2. Структура l_var

Инициализация

До начала работы интерпретатора все классы, функции и специальные формы должны быть проинициализированы. Инициализация классов происходит вызовом соответствующего метода init. В то время как стандартные функции и специальные формы должны быть добавлены в env отдельно.

Листинг 3. Структура l_var

Создание объектов

Класс создается с помощью функции new_class, которая возвращает объект класса. Функция принимает имя класса и таблицу символов класса. Для более удобной работы созданы доп функции, возвращающие необходимые объекты: list, raw_fn, fn, raw_spec, spec, object и т. д.

Листинг 4. Структура класса c_int

Если функция написана на языке C, то создать такой объект можно при помощи raw_fn. Первым аргументом передается список аргументов получаемых функцией, вторым передается адрес на нее.

Листинг 5. Описание встроенного метода plus для класса c_int

Если в функцию придет больше аргументов чем в списке аргументов, то все остальные будут автоматически записаны в переменную под именем “&rest” хранящую лист оставшихся переменных.

Чтение S-выражений

Данная процедура сканирует входную строку посредством чтения следующего символа, и возвращает:

● NULL если символ равен “)”;

● результат выполнения parse_list если символ равен “(”;

● результат выполнения parse_quote если символ равен “””;

● результат выполнения parse_string если символ равен “‘“;

● результат выполнения функции parse_token.

В любых иных случаях read создает S-выражение [5], соответствующее входной строке, и возвращает в качестве результата указатель на нее.

Листинг 6. Функция чтения S-выражений

Исполнение

Для исполнения объект, полученный после этапа чтения, передается в функцию eval.

Листинг 7. Функция eval

Листинг 8. Функция exec

Функция eval работает следующим образом:

● Если eval получает лист, то тогда, согласно правилам, вызывается eval сначала для первого элемента списка.

Если результатом будет специальная форма, то eval не вызывается для остальных элементов, а в форму передаются все последующие значения листа.

● Если eval получает символ, то в окружении по имени ищется объект и возвращается. Если объект не найден, то создается переменная с именем символа и с объектом типа “c_undefined”.

Во всех остальных случаях возвращается сам объект.

Сборка мусора

Так как S-выражения поступают в интерпретатор LISP для оценки, то различные структуры в области списка создаются либо на основе ввода, либо на основе оценки. Большинство этих структур списка используются единожды. Такие структуры называются мусорными объектами. А в случае узлов области списка мусорными узлами являются те, которые недоступны.

Нам нужно найти такие бесполезные узлы списка и поместить их в список свободного пространства области списка, чтобы их можно было использовать повторно; в противном случае у нас закончится память области списка. Каждый раз, когда при пересчете числа ссылок на объект получаем нуль, то данный объект удаляется из памяти.

Листинг 9. Функция new_object

Для сбора мусора используется метод подсчета ссылок. Он заключается в том, что каждый объект подсчитывает количество указателей на него. Когда есть указатель на себя, значение счетчика увеличивается на 1; когда указатель на себя удаляется, значение счетчика уменьшается на 1, если значение счетчика уменьшается до 0, что указывает на отсутствие указателя на объект, то его можно безопасно уничтожить. Это можно представить в виде следующего рисунка:

Рисунок 2. Иллюстрация работы счетчика сборщика мусора

Преимущество алгоритма подсчета ссылок заключается в том, что накладные расходы на управление памятью распределяются по всему периоду работы приложения. Также преимущество связно с тем, что когда значение счетчика ссылок объекта становится равным 0, системе не нужно обращаться к блокам, расположенным на других страницах в куче.

Примеры работы отдельных частей интерпретатора

Для демонстрации работы интерпретатора приведем несколько примеров. В листингах будут указаны результаты работы функции через комментарии вида “;; =>”, после которого будет указано возвращаемое значение.

Приведем пример функции факториала:

Листинг 10. Функция вычисления факториала

Пример, демонстрирующий работу в стиле объектно-ориентированного программирования (ООП). Опишем класс “Человек”. “Человек” имеет метод взросления, который прибавляет к его годам 1, также имеет метод изменения уровня счастья:

Листинг 11. Пример ООП

По сравнению с Common lisp, данная реализация языка имеет более интуитивный синтаксис. В листинге 11 код, написанный на LISP, противопоставляется коду на языке Common lisp (листинг 12). Как видим, новому пользователю не придется запоминать большое количество функций выполняющих схожие действия, что в совокупности с динамической типизацией добавляет гибкости.

Листинг 12. Сравнение. Код на данной реализации LISP

Листинг 13. Сравнение. Код Common lisp

Также можно переопределить функции для использования со своим классом, например для смешивания цветов (листинг 14). Это сделает LISP более удобным для конечного пользователя. Если бы данная реализация LISP не позволяла переопределять функции, то это было бы проблемой, ведь стандарта в названиях нет и они могут отличаться от библиотеки к библиотеке. Например, функция сложения цветов могла бы называться следующими именами: mixcolor, add-color, color-add, color+ и т.д.

Листинг 14. Класс color

Следующий пример демонстрирует работу макросов LISP. Здесь (листинг 15) имеется макрос n+, он принимает один аргумент n и объявляет глобальную функцию, которая принимает один аргумент x и прибавляет к нему n.

Листинг 15. Макрос n+

Апробация разработанного интерпретатора

В качестве демонстрации работы интерпретатора представим реализацию игры “Жизнь”. Опишем суть данной игры.

Место действия – плоскость, размеченная на клетки, которая может быть замкнутой, ограниченной или бесконечной. У каждой клетки есть восемь соседей, окружающих её, и она может быть в двух состояниях: клетка либо “жива”, либо "мертва”. Живые клетки, которые стоят в начале игры, называются первым поколением. Каждое последующее поколение строится на основе предыдущего по правилам:

● для “мертвой” клетки: если живых соседей не меньше трех, то клетка становится “живой”;

● для “живой” клетки: если живых соседей два или три, то клетка продолжает жить, а в иных случаях умирает.

Игра прекращается, когда на поле не остается ни одной живой клетки. Несмотря на простоту правил, в игре может возникать огромное разнообразие форм.

Определим вспомогательные функции, которые реализуют некоторые существующие функции LISP. Функция map вызывает переданную ей функцию для всех элементов листа. Nth возвращает n-ный элемент с начала листа, в то время как nth-tail возвращает n-ный элемент с конца.

Листинг 16. Реализация дополнительных функций

Далее описываются функции для работы с полем. Get возвращает символ, находящийся по координатам x и y на поле. Alive возвращает булево значение состояния клетки. Print печатает поле. Count возвращает количество живых клеток, находящихся вокруг данной клетки. Next в зависимости от текущего состояния поля возвращает значение клетки на следующий ход.

Листинг 17. Вспомогательные функции

Функция запуска представляет собой циклический перебор по всем клеткам и выявление их состояния на следующий ход.

Листинг 18. Функция запуска

Определим стартовое поле в виде колонии (листинг 19). Данная конструкция называется планером, так как способна перемещаться бесконечно далеко. В данном случае, так как границы мира ограничены, планер остановится в углу, где приобретет стабильную форму в виде квадрата.

Листинг 19. Колония 1

Рисунок 3. Эволюция колонии 1

Существуют формы колоний, которые способны изменяться бесконечно. Самым простым примером такой формы является прямая линия, состоящая из трех элементов в ряд.

Листинг 20. Колония 2

Результатом жизни такой колонии является циклическая смена вертикального положения на горизонтальное и наоборот.

Рисунок 4. Эволюция колонии 2

Заключение

По результатам проделанной работы создан интерпретатор языка LISP на языке C, упрощающий написание программ посредством более простого синтаксиса с возможностью перегрузки функций. На примере реализованного игрового приложения “Жизнь” язык разработанного интерпретатора продемонстрировал более упрощённый стиль написания программы. Также в работе дан посыл для развития языка LISP посредством включения языковых конструкций, обеспечивающих объектно-ориентированные подходы программирования.

Таким образом, коллектив авторов создал фундамент для дальнейшего развития языка LISP и рассматривает предметом дальнейших работ по применению и модернизации интерпретатора в целях создания программного обеспечения в таких предметных областях, как нейронные сети в промышленной индустрии [6], параллельное программирование [7], защита информации [8, 9].

Библиография
1. T. Ono and H. Yamazaki, "Design and implementation of the distributed Lisp," Proceedings of IEEE Singapore International Conference on Networks and International Conference on Information Engineering '95, 1995, pp. 195-198, doi: 10.1109/SICON.1995.526045.
2. J. M. Farrow and S. Sevinc, "Modelling tools for a common LISP object system environment," [1991] Proceedings. The Second Annual Conference on AI, Simulation and Planning in High Autonomy Systems, 1991, pp. 219-224, doi: 10.1109/AIHAS.1991.138474.
3. M. A. Fukase and T. Nakamura, "Semiparallel execution of compiled Lisp programs," [1991] Proceedings The Fifteenth Annual International Computer Software & Applications Conference, 1991, pp. 719-724, doi: 10.1109/CMPSAC.1991.170266.
4. S. Asadianfam and H. Kolivand, "Verification of Airport Control Using Lisp Functional Language," 2020 13th International Conference on Developments in eSystems Engineering (DeSE), 2020, pp. 245-250, doi: 10.1109/DeSE51703.2020.9450787.
5. M. Kaufmann and J. S. Moore, "An industrial strength theorem prover for a logic based on Common Lisp," in IEEE Transactions on Software Engineering, vol. 23, no. 4, pp. 203-213, April 1997, doi: 10.1109/32.588534.
6. Gibadullin, R.F., Lekomtsev, D.V. & Perukhin, M.Y. Analysis of Industrial Network Parameters Using Neural Network Processing. Sci. Tech. Inf. Proc. 48, 446–451 (2021). https://doi.org/10.3103/S0147688221060046.
7. Гибадуллин Р.Ф. Потокобезопасные вызовы элементов управления в обогащенных клиентских приложениях // Программные системы и вычислительные методы. – 2022. – № 4. – С. 1-19. DOI: 10.7256/2454-0714.2022.4.39029 EDN: IAXOMA URL: https://nbpublish.com/library_read_article.php?id=39029.
8. Гибадуллин Р.Ф. Организация защищенной передачи данных в сенсорной сети на базе микроконтроллеров AVR // Кибернетика и программирование. – 2018. – № 6. – С. 80-86. DOI: 10.25136/2306-4196.2018.6.24048 URL: https://nbpublish.com/library_read_article.php?id=24048.
9. Гибадуллин Р. Ф. Развитие единообразного формализма защиты точечных, линейных и площадных объектов картографии //Вестник Казанского государственного технического университета им. АН Туполева. – 2010. – №. 2. – С. 101-105.
References
1. T. Ono and H. Yamazaki, "Design and implementation of the distributed Lisp," Proceedings of IEEE Singapore International Conference on Networks and International Conference on Information Engineering '95, 1995, pp. 195-198, doi: 10.1109/SICON.1995.526045.
2. J. M. Farrow and S. Sevinc, "Modelling tools for a common LISP object system environment," [1991] Proceedings. The Second Annual Conference on AI, Simulation and Planning in High Autonomy Systems, 1991, pp. 219-224, doi: 10.1109/AIHAS.1991.138474.
3. M. A. Fukase and T. Nakamura, "Semiparallel execution of compiled Lisp programs," [1991] Proceedings The Fifteenth Annual International Computer Software & Applications Conference, 1991, pp. 719-724, doi: 10.1109/CMPSAC.1991.170266.
4. S. Asadianfam and H. Kolivand, "Verification of Airport Control Using Lisp Functional Language," 2020 13th International Conference on Developments in eSystems Engineering (DeSE), 2020, pp. 245-250, doi: 10.1109/DeSE51703.2020.9450787.
5. M. Kaufmann and J. S. Moore, "An industrial strength theorem prover for a logic based on Common Lisp," in IEEE Transactions on Software Engineering, vol. 23, no. 4, pp. 203-213, April 1997, doi: 10.1109/32.588534.
6. Gibadullin, R.F., Lekomtsev, D.V. & Perukhin, M.Y. Analysis of Industrial Network Parameters Using Neural Network Processing. Sci. Tech. Inf. Proc. 48, 446–451 (2021). https://doi.org/10.3103/S0147688221060046.
7. Gibadullin R.F. Flow-safe calls of controls in enriched client applications // Software Systems and Computational Methods. – 2022. – № 4. – pp. 1-19. DOI: 10.7256/2454-0714.2022.4.39029 EDN: IAXOMA URL: https://nbpublish.com/library_read_article.php?id=39029.
8. Gibadullin R.F. Organization of secure data transmission in sensor network based on AVR microcontrollers // Cybernetics and Programming. – 2018. – № 6. – pp. 80-86. DOI: 10.25136/2306-4196.2018.6.24048 URL: https://nbpublish.com/library_read_article.php?id=24048.
9. Gibadullin R. F. F. Development of Uniform Formalism of Protection of Point, Linear and Area Objects in Cartography // Bulletin of Kazan State Technical University named after A.N. Tupolev. – 2010. – №. 2. – pp. 101-105.

Результаты процедуры рецензирования статьи

В связи с политикой двойного слепого рецензирования личность рецензента не раскрывается.
Со списком рецензентов издательства можно ознакомиться здесь.

Предметом исследования в рецензируемом материале выступает разработка интерпретатора на LISP — семействе языков программирования для обработки списков.
Методология исследования базируется на изучении литературы по теме исследования, описании структуры разработанного интерпретатора, визуальном представлении отдельных процедур и фрагментов программного кода.
К сожалению, актуальность работы автором не обоснована надлежащим образом, поэтому не понятно на решение какой научной проблемы ориентировано исследование.
Научная новизна рецензируемого исследования, по мнению рецензента, состоит в разработке интерпретатора, упрощающего написание программ посредством более простого синтаксиса с возможностью перегрузки функций.
В статье структурно выделены следующие разделы: Архитектура интерпретатора, Описание типов данных, Инициализация, Создание объектов, Чтение S-выражений, Исполнение, Сборка мусора, Примеры работы отдельных частей интерпретатора, Апробация разработанного интерпретатора, Заключение, Библиография.
Автор демонстрирует функционирование разработанного интерпретатора на примере реализации игры “Жизнь”, приводит описание реализации дополнительных и вспомогательных функций, функций запуска, планера, говорит о циклической смене вертикального положения на горизонтальное и наоборот в описываемых формах колоний.
Библиографический список включает 9 источников – публикации зарубежных и отечественных ученых по теме статьи, на которые в тексте имеются адресные ссылки, подтверждающие наличие апелляции к оппонентам.
Следует высказать ряд замечаний по представленному материалу. Во-первых, в наименовании статьи присутствует аббревиатура на иностранном языке, которая может быть известна далеко не всем потенциальным читателям журнала. Во-вторых, в тексте статьи не отражены четкие формулировки цели и задач исследования, решаемых задач, предмета и объекта исследования, а также и полученных результатов, их новизны и практической значимости. В-третьих, в статье отсутствует введение, без которого читателю будет не ясна актуальность исследования, степень разработанности проблемы, методологический аппарат, использованный автором в процессе исследования. Без этого проект публикации не имеет очертаний, присущих научному исследованию и не может быть опубликован без соответствующей доработки. В-четвертых, в статье допущены отступления от общепринятых правил оформления иллюстраций – кроме четырех рисунков приведены 20 фрагментов листинга программного кода, которые по сути также являются рисунками, но размещение 24 иллюстраций в журнальной статье общепринятого объема приводят к явным диспропорциям между объемом текстовой и иллюстративной частей материала. В-пятых, рисунок 4 «Эволюция колонии 2» не отображен в информационной системе журнала – его невозможно просмотреть и оценить.
Статья соответствует направлению журнала «Программные системы и вычислительные методы», может вызвать интерес у читателей после доработки в соответствии с высказанными замечаниями, однако, рецензируемый материал, по мнению рецензента, нуждается в надлежащем структурировании и доработке.