|
|
|||||||||||||||||||||||||||||
|
Макет интерпретатора скриптового языка на основе LispИсточник: delphikingdom Владимир Баскаков
Автор: Владсилав Баскаков, Королевство Delphi Краткое описание проекта. Представленная библиотека реализует интерпретатор подмножества языка Lisp. Библиотека является экспериментальной, она не претендует ни на полноту реализации, ни на высокое быстродействие, ни на тщательное тестирование. Показателем ее относительной работоспособности является успешная интерпретация нескольких простых программ:
Состав библиотеки. Библиотека состоит из модулей uNodes, uEval, uPars, ulMathLib, ulFormsLib, ulOleLib. В модуле uNodes описаны основные классы, поддерживающие работу интерпретатора, в том числе:
В модуле uEval вводится порожденный от TLDict класс TLEval, включающий в себя функции - члены, реализующие основные ключевые слова интерпретатора:
Объект класса TLEval при создании создает и загружает дополнительные функции из объектов - библиотек (экземпляров классов TLMathLib, TLFormsLib, TLOleLib, описанных в модулях ULMathLib, ULFormsLib, ULOleLib соответственно). TLMathLib содержит реализацию стандартных функций + - * / и нестандартных +! и -! - арифметических макросов (исполняются быстрее соответствующих функций, но несовместимы с aply) TLFormsLib содержит реализацию стандартных конструкций if, while, let*, do* и нестандартных oWith, oCall, oGet, oSet, предназначенных для упрощения работы со свойствами и методами IDispatch. TLOleLib содержит реализация нестандартной функции OleObject- скриптового аналога CreateOleObject. В модуле uPars описан унаследованый от TLEval класс TLParser. Он и предназначен для трансляции входной строки - программы во внутреннее представление - узел-список, экземпляр TLConsNode, и обратного преобразования узла - результата исполнения программы в итоговую строку. Принцип работы интерпретатора.Программа передается на исполнение через метод TLParser.Run(Prg: String): String. При исполнении этого метода интерпретатор преобразует входную строку ко внутреннму представлению в виде списка (function TLParser.Compile(s: String): TLNode), сформированный узел-программа возвращает значение (function TLNode.Eval: TLNode), которое конвертируется в строку функцией TLNode.PutToStream(s: TStream) , сохраняющей текстовое представление узла в потоке. Функция Compile состоит из двух частей. Первая часть - procedure TLParser.Parse(s: String); разбирает входную строку на слова и размещает их в списке TLParser.tokens: TstringList. В процессе разбора отсекаются комментарии. Эта часть реализована как конечный автомат, состояние автомата фиксируется в поле onChar:TcharProc, содержащем текущую функцию обработки очередного символа строки. Всего таких фукций 4: procedure TLParser.pBlank(c:Char); procedure TLParser.pComment(c:Char); procedure TLParser.pString(c:Char). procedure TLParser.pToken(c:Char); Они соответствуют 4 состояниям автомата - неизвестное состояние/пробельные символы; комментарий, начинающийся с символа ";"; строковая константа, начинающаяся с символа "двойной кавычки"; другое слово (атом, число, спец.символы). Вторая часть функции Compile - рекурсивная функция function TLParser.CompileNext:TLNode, последовательно перебирающая элементы полученного списка строк и формирующая итоговый объект - узел. Метод function TLNode.Eval: TLNode - виртуальный, он перегружен для узлов разных типов. Так, для узлов - строк, чисел, вариантов, встроенных функций и макросов (TLNumber, TLString, TLVariant, TLKFun) он возвращает сам узел; для узлов-именованных атомов (TLAtom) - возвращает верхушку стека значений; для списков (TLCons)- рассматривает голову списка как функцию или макрос, и применяет ее к оставшейся части списка, рассматриваемой как аргументы. Функция обработки аргументов function TLNode.ProcessArgs(args: TLNode): TLNode при необходимости вычисляет значения аргументов и передает их список перегружаемой функции function TLNode.Apply(args: TLNode): TLNode. Реализация метода Apply во встроенной функции (TLKFun) обрабатывает аргументы непосредственно; пользовательские функции представлены списками (TLCons), реализация Apply в списках предполагает связывание аргументов с формальными параметрами (TLNode.AddValue) и исполнением тела функции. При тестировании библиотеки выяснилось, что исполнение рекурсивных функций по обработке списков с большой глубиной рекурсии, требует постоянного создания и освобождения объектов, что существенно увеличивает время интерпретации. Для ускорения работы освобождаемые объекты не уничтожаются, а помещаются в списки неиспользованных (TLDict: ConsHeap: TLCons, NumHeap: TLNumber; StrHeap:TLString; VarHeap:TLVariant;) перегружаемой функцией TLNode.Util, и при необходимости извлекаются оттуда (TLDict.NewNumber, TLDict.NewNumber, TLDict.NewVariant, TLDict.Cons). ЗаключениеВ результате разработки была получена библиотека - интерпретатор языка, поддерживающего базовые модели функционального программирования. Простота синтаксиса языка Lisp позволяет программисту отвечься от сложностей синтаксического анализа и получить работающий прототип программы в очень короткие сроки, однако непривычный синтаксис Lisp'а затрудняет его использование в промышленных проектах. С другой стороны, логическая стройность концепций функционального программирования делает Lisp удобным и интересным полигоном для начинающего программиста. Представленная библиотека разрабатывалась в течении долгого времени как логическая игрушка, прошу не судить меня за отсутствие полноценных комментариев или описания библиотеки! В рамках этой разработки остались нерешенными вопросы построения функциональных замыканий и основ объектно - ориентированного языка, это - тема для будущих размышлений. Для полноценного решения указанных вопросов нужно вычислительную модель библиотеки привести в соответствие с идеологией Lisp, обеспечив связывание аргументов и формальных параметров функции через единый ассоциативный список. Кроме того, в статье не описан механизм обращения к методам и свойствам объектов OLE - автоматизации. Основа для функции доступа к свойствам OLE объектов (TLVariant.GetProp и TLVariant.SetProp) была найдена в стандартном модуле ComObj; идея вызова метода OLE - объекта взята из модуля OLE2AUTO широко известной библиотеки RX. Главным источником информации по основам функционального программирования была для меня книга 'Введение в теорию программирования. Функциональный подход', являющаяся частью учебного курса "Интернет университета информационных технологий", она размещена на сайте проекта http://www.intuit.ru/department/se/tppfunc/. Программа компилировалась под Delphi 6 и не использует сторонних библиотек. Приложение: Программа "Ферзи"
Ссылки по теме
|
|