У вас вопросы?
У нас ответы:) SamZan.net

Система программирования PascalABCNET

Работа добавлена на сайт samzan.net:

Поможем написать учебную работу

Если у вас возникли сложности с курсовой, контрольной, дипломной, рефератом, отчетом по практике, научно-исследовательской и любой другой работой - мы готовы помочь.

Предоплата всего

от 25%

Подписываем

договор

Выберите тип работы:

Скидка 25% при заказе до 28.12.2024

СОДЕРЖАНИЕ

1. Введение

. Постановка задачи

. Структура компилятора PascalABC.NET

. Синтаксическое дерево

.1 Концепция визиторов

.2 Структура дерева и примеры узлов

.3 Редактор синтаксического дерева

. Синтаксический анализатор

.1 Интерфейс подключения

.2 Язык PascalABC.NET

.2.1 Процедуры с переменным числом параметров

.2.2 Операция new

.2.3 Упрощенный синтаксис записи модулей

.2.4 Определение методов внутри класса

.2.5 Перегрузка операций

.2.6 Шаблоны

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

.2.8 Операции += -=

.2.9 Методы в записях

.2.10 Объявление имен, совпадающих с ключевыми словами

.3 Генератор синтаксических анализаторов

.4 Генерация синтаксического анализатора языка PascalABC.NET

.5 Диагностика сообщений об ошибках

. Семантическое дерево

.1 Узел для представления операции is

.2 Узел для представления операции sizeof

. Семантический анализатор

.1 Таблица символов

.1.1 Дерево областей видимости

.1.2 Общий алгоритм поиска

.1.3 Иерархия областей видимости

.1.4 Алгоритмы поиска в областях видимости

.1.5 Алгоритмы поиска в таблице символов

.2 Генерация узлов семантического дерева

.2.1 Операции is, as

.2.2 Операции typeof, sizeof

.2.3 Операция new

.2.4 Типизированные и бинарные файлы

.2.4.1 Часть кода, реализованная на PascalABC.NET

.2.4.2 Генерация узла семантического дерева

.2.4.3 Проверки этапа компиляции

.2.4.4 Реализация процедуры read

.2.5 Изменяемые строки string

.3 Генерация сообщений об ошибках

. Генератор кода

.1 Перевод семантического дерева в IL код

.2 Перевод конструкций в IL код

.2.1 Операции is, as

.2.2 Операции typeof, sizeof

. Промежуточная форма хранения программы (PCU)

.1 Выбор промежуточной формы хранения программы

.2 Варианты использования PCU

.3 Схема компиляции с использованием PCU

.4 Сериализация и десериализация некоторых узлов семантического дерева

. Управляющий блок

.1 Алгоритм компиляции модулей

. Система локализации

. Консольная оболочка компилятора

. Модули визуальной оболочки

.1 Интерфейс подключения

.2 Модуль «Визуализатор синтаксического дерева»

.3 Модуль «Управление компилятором»

.4 Модуль «Контроль внутренних ошибок»

. Подключение задачника Programming Taskbook

.1 Модуль на языке PascalABC.NET

.2 Модуль визуальной оболочки

Заключение

Литература

ПРИЛОЖЕНИЕ 1. Грамматика языка PascalABC.NET

ПРИЛОЖЕНИЕ 2. Классы синтаксического дерева


1. Введение

Система программирования PascalABC.NET создается как система, ориентированная на начальное обучение. Ее прототип - компилятор Pascal ABC - основан на языке Object Pascal и успешно используется на факультете Математики Механики и Компьютерных Наук Южного федерального университета при обучении основам программирования. Однако Pascal ABC является интерпретатором, имеет низкую скорость выполнения программ и не генерирует исполнимые файлы. Таким образом, создание полноценного компилятора и системы программирования PascalABC.NET представляется актуальным.

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

· Во-первых, создать бесплатную среду, базирующуюся на наиболее используемом в России для обучения начальному программированию языке Паскаль.

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

· В-третьих, упростить и осовременить используемый язык Delphi Pascal. Язык PascalABC.NET проектируется с целью обеспечить преподавателям и учащимся возможность использования накопленных методик обучения на языке Паскаль, сочетая их с самыми современными возможностями языков программирования (шаблоны классов, перегрузка операций, пространства имен, делегаты, исключения, сборка мусора).

В качестве целевой платформы для реализации выбрана Microsoft .NET. Платформа Microsoft .NET создавалась с целью обеспечения возможности совместной, равноправной и единообразной работы всех .NET-языков программирования. Она содержит богатые библиотеки классов, поддерживается на уровне операционной системы, активно развивается. Кроме того, генерация .NET-кода значительно проще генерации машинного кода. Наконец, еще одним существенным плюсом является бесплатность компилятора языка C#, на котором написан компилятор PascalABC.NET.

При создании компилятора автор существенно опирался на классический труд по компиляторам - книгу Ахо А. "Компиляторы: принципы, технологии и инструменты" [1].

Работа состоит из 14 глав. В главах 1-3 описывается общая проблематика задачи и структура компилятора. Главы 4,5 посвящены синтаксическому анализатору (front-end). Главы 6,7,9 описывают внутреннее представление компилятора и центральную часть компилятора - семантический анализатор (middle-end). Глава 8 посвящена генерации кода (back-end). Глава 10 описывает блок, соединяющий вместе все части компилятора. В главах 11-14 описываются различные модули и вспомогательные системы компилятора.

По тематике проекта автором опубликованы 4 работы [2-5].


2. Постановка задачи

Разработать следующие части системы программирования PascalABC.NET:

· синтаксическое дерево программы;

· интерфейс подключения синтаксических анализаторов;

· синтаксический анализатор языка PascalABC.NET;

· таблица символов компилятора;

· алгоритм компиляции модулей;

· система локализации;

· консольная оболочка компилятора;

· интерфейс подключения модулей к визуальной оболочке;

· некоторые модули к визуальной оболочке.

Также автором ведется поддержка и развитие следующих частей проекта:

· семантическое дерево;

· семантический анализатор;

· генерация кода;

· промежуточная форма хранения программы (PCU).


3. Структура компилятора PascalABC.NET

На схеме приведена структура компилятора PascalABC.NET

Управляющий блок управляет процессом компиляции программы и является интерфейсом для подключения оболочек к компилятору. Он содержит также базовую иерархию ошибок, алгоритм компиляции модулей и алгоритмы чтения/записи внутреннего представления (PCU файлов).

Парсер (синтаксический анализатор) - программа, осуществляющая синтаксический анализ текста. При запросе на компиляцию файла контроллер парсеров решает, какой парсер необходимо использовать для компиляции этого файла. После компиляции парсер возвращает синтаксическое дерево программы.

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

Синтаксическое дерево представляет собой разобранную программу без учета семантики. Синтаксическое дерево - это набор классов (около 170). Само дерево не содержит процедур для его обработки. Обработка дерева происходит при его обходе с помощью визитора.

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

Конвертор семантического дерева в синтаксическое переводит семантическое дерево программы в синтаксическое. На этом блоке лежит задача семантического анализа программы.

Таблица символов используется лишь на этапе перевода синтаксического дерева в семантическое для быстрого поиска имен и хранения структуры областей видимости.

Доступ к .NET используется для поиска имен в сборках .NET.

Интерфейс таблицы символов используется для сокрытия платформы .NET от конвертора. Это сделано для того, чтобы конвертор дерева не зависел от целевой платформы.

Генератор кода для платформы .NET - это набор классов, который обеспечивает перевод семантического дерева в MSIL код.

Процесс компиляции происходит в несколько этапов:

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

2. Алгоритм компиляции модулей в нужном порядке либо переводит текст программы с помощью парсера в синтаксическое и далее в семантическое дерево, либо считывает семантическое дерево из PCU файла.

. Семантические деревья модулей сохраняются в PCU файлы

. Полученное семантическое дерево передается генератору кода, который переводит семантическое дерево в MSIL код.


4. Синтаксическое дерево

Синтаксическое дерево представляет собой разобранную программу без учета семантики. Внутреннее представление - это иерархия классов на языке C#.

Разделение на синтаксическое и семантическое деревья несколько нетрадиционно [2]. Обычно принято аннотировать семантическое дерево за несколько проходов. Основная мотивировка разделения на синтаксическое и семантическое деревья - принципиально разная структура этих деревьев, а также независимость конвертора синтаксического дерева в семантическое от используемого парсера. Следует отметить, что структура синтаксического дерева позволяет перевести программы на родственных языках (Pascal, Modula, Oberon) в синтаксические деревья близкой структуры, так что уже на этапе синтаксического дерева обеспечивается относительная независимость от языка.

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

На данный момент синтаксическое дерево состоит из 170 классов, описанных в приложении 2.

4.1 Концепция визиторов

Концепция визиторов (паттерн «посетитель» [6]) позволяет отделить алгоритмы обработки дерева от самой структуры данных, т.е. визитор является набором алгоритмов для обработки узлов дерева. Рассмотрим реализацию паттерна «посетитель» на примере узла if_node:

public class if_node : statement

{expression condition;statement then_body;statement else_body;override void visit(IVisitor visitor)

{.visit(this);

}

}

У узла if_node есть функция visit, которой на вход подается объект visitor, реализующий интерфейс IVisitor. В теле этой функции происходит вызов метода visit объекта visitor. Рассмотрим интерфейс IVisitor:

public interface IVisitor

{

...visit(if_node _if_node);

}

Таким образом, определяя класс, удовлетворяющий этому интерфейсу, мы сможем запрограммировать алгоритм обработки для каждого узла:

class visitor : IVisitor

{

...

public void visit(if_node _if_node)

{

//алгоритм обработки узла if_node(_if_node.condition, "condition");_node(_if_node.then_body, "then_body");_node(_if_node.else_body, "else_body");

}

...

}

Запустить визитор по дереву можно следующим образом:

visitor vs = new visitor();_node if_n = new if_node();_n.visit(vs);

4.2 Структура дерева и примеры узлов

syntax_tree_node - базовый класс синтаксического дерева программы

public class syntax_tree_node

{syntax_tree_node(SourceContext _source_context)

{_context = _source_context;

}SourceContext source_context;virtual void visit(IVisitor visitor)

{.visit(this);

}

}

Базовый класс содержит функцию visit, необходимую для обхода дерева посетителем, и объект класса SourceContext.

public class file_position

{int line_numint column_num

}class SourceContext

{file_position begin_positionfile_position end_positionoverride string ToString()

}

Класс SourceContext служит для хранения позиции данной конструкции в тесте программы.

compilation_unit - базовый класс для узлов program_module и unit_module.

public class compilation_unit : syntax_tree_node

{string file_nameList<compiler_directive> compiler_directivesoverride void visit(IVisitor visitor)

}

Хранит имя файла программы и список директив компилятора.

program_module - класс для представления основной программы.

public class program_module : compilation_unit

{program_name program_nameuses_list used_unitsusing_list using_namespacesblock program_blockoverride void visit(IVisitor visitor)

}

Хранит имя программы, список подключенных модулей, список подключенных пространств имен и блок программы.

unit_module - класс для представления модуля

public class unit_module : compilation_unit

{unit_name unit_nameinterface_node interface_partimplementation_node implementation_partstatement_list initialization_partstatement_list finalization_partoverride void visit(IVisitor visitor)

}

Хранит имя модуля, интерфейсную часть, часть реализаций, список операторов в секциях initialization и finalization.

for_node - класс для представления цикла for.

public class for_node : statement

{ident loop_variableexpression initial_valueexpression finish_valuestatement statementsfor_cycle_type cycle_typeexpression increment_valueoverride void visit(IVisitor visitor)

}

Хранит переменную цикла, начальное значение, конечное значение, список операторов, тип цикла (to или downto) и выражение, на которое надо увеличивать переменную цикла.

4.3 Редактор синтаксического дерева

Для добавления новых узлов и редактирования уже имеющихся используется программа nodes_generator, которая позволяет в визуальном режиме редактировать, добавлять, удалять узлы дерева. Эта программа генерирует код всех классов дерева, а также интерфейс визитора. Программа была разработана автором совместно с Водолазовым Н. и Ивановым С.



5. Синтаксический анализатор

Синтаксический анализатор (парсер) - программа, осуществляющая разбор текста программы в синтаксическое дерево.

Синтаксические анализаторы подключаются к компилятору динамически. Для реализации нового языка программирования достаточно реализовать парсер, который будет строить синтаксическое дерево.

5.1 Интерфейс подключения

Синтаксический анализатор, подключаемый к компилятору PascalABC .NET, должен быть унаследован от класса BaseParser, определенного в пространсве имен PascalABCCompiler.ParserTools и находящегося в сборке CompilerTools.dll:class BaseParser

{List<Error> Errors;bool CaseSensitive;string[] FilesExtensions;string Name;BaseParser(string name, bool case_sensitive);virtual compilation_unit BuildTree(FileName, string Text);virtual void Reset();override string ToString();

}

Здесь:- список для хранения ошибок, возникших при синтаксическом анализе;- свойство, определяющее чувствительность парсера к регистру символов;- массив расширений имен файлов, которые может обрабатывать парсер;- имя синтаксического анализатора;- функция, производящая анализ текста и возвращающая корнь синтаксического дерева; параметр Text - текст файла с именем FileName;- функция, которая вызывается компилятором в начале компиляции программы.

Компилятор взаимодействует с парсерами через класс Controller, определенный в пространстве имен PascalABCCompiler.Parsers и находящийся в сборке Compiler.dll:class Controller

{List<BaseParser> Parsers;BaseParser LastParser;Controller();SyntaxTree.compilation_unit Compile(FileName, string Text, List<Error> Errors);void Reset();

}

Здесь:- список парсеров, подключенных к компилятору;- последний использовавшийся парсер;- функция, которая производит выбор нужного парсера для данного файла и вызов функции BaseParser.BuildTree. Решение о выборе парсера пинимается взависимости от расширения файла;- функция, которая вызывается компилятором в начале компиляции программы.

В конструкторе класса Controller происходит поиск и подключение парсеров. Сборки Dll, содержащие парсеры, должны удовлетворять следующим требованиям:

· находиться в одной папке с компилятором;

· иметь имя Parser*.dll;

· содержать один или несколько парсеров: классов-потомков BaseParser;

· парсеры должны иметь имя *LanguageParser.

5.2 Язык PascalABC.NET

Язык PascalABC.NET базируется на языке Object Pascal (Delphi Pascal).

Рассмотрим особенности языка PascalABC.NET.

При объявлении класса можно пользоваться четырьмя модификаторами видимости:

public - видимо для всех;

internal - видимо для всех только в этой сборке, этот модификатор видимости используется по умолчанию;

protected - видимо только в классе, его потомках и в модуле в котором объявлен класс;

private - видимо только в классе и в модуле, в котором объявлен класс;

При объявлении методов можно пользоваться следующими модификаторами:

static - статический метод;

virtual - виртуальный метод;

override - переопределенный метод;

overload - перегруженная подпрограмма; для определения перегруженных подпрограмм можно не пользоваться этим модификатором, он оставлен для совместимости.

Рассмотрим некоторые особенности языка.

1. В языке все конструкторы имеют имя Create. Можно также объявить конструктор без имени, при этом ему будет назначено имя Create. Подробнее о способы вызова конструктора будут описаны в пункте 5.2.2.

2. В платформе .NET примитивные типы (integer,real) являются объектами, поэтому язык поддерживает вызовы методов у примитивных типов, например:

s := 10.ToString;

s := 3.14.ToString;

3. В секции uses можно подключать как модули, так и пространства имен .NET. Модуль вводит неявное пространство имен. Рассмотрим пример, как можно обращаться к именам в модулях и пространках имен .NET:

uses System,  // пространство имен.NET.IO, // пространство имен.NET;  // модуль на языке PascalABC.NET

var s1: StreamReader; // класс из System.IO: System.IO.StreamReader; // явное указание имени;   // процедура из модуля Unit11.p;  // явное обращение

end.

4. Определение внешних подпрограмм производится следующим образом:

function MessageBox(h: integer;

m,c: string; t: integer): integer;'User32.dll' name 'MessageBox';

5. Из языка Delphi Pascal убраны устаревшая концепция объектов object и ключевое слово object.

6. Также убраны ключевые слова message и низкоуровневые конструкции, такие как absolute.

. В языке принята структурная эквивалентность типов для процедурных переменных, поэтому типы можно описывать в заголовках методов и объявлениях переменных:

Type= class(Control): procedure(Sender: Component);;

5.2.1 Процедуры с переменным числом параметров

Процедуры с переменным числом параметров реализуются с помощью введения в язык нового ключевого слова params.

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

function Concat(params strs: array of string): string;: integer;: System.Text.StringBuilder;:= new System.Text.StringBuilder;i:=0 to strs.length-1 do.Append(strs[i]);:= sb.ToString;

end;

5.2.2 Операция new

Операция new является синонимом вызова конструктора Create и позволяет создать новый объект класса.

new_expr ::= ‘new’ named_type_reference [‘<’ template_args ‘>’]

[‘(’ expr_list ‘)’]

Пример::= new Point(10, 20);:= Point.Create(10, 20); //Эквивалентная запись

Предпочтительным синтаксисом вызова конструктора является операция new т.к. позволяет явно видеть места создания объектов в тексте программы. Вызов конструктора как процедуры в языке PascalABC.NET запрещен.

Также операция new позволяет инстанцировать шаблон:

a := new List<integer>;

Слово new не является ключевым словом, им можно пользоваться при объявлении идентификаторов.

5.2.3 Упрощенный синтаксис записи модулей

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

Обычный синтаксис записи модуля:

unit a;

//интерфейсная часть

//реализация.

Упрощенный синтаксис записи модуля:

unit a;

//реализация.

При упрощенном синтаксисе считается, что все сущности находятся в интерфейсной части модуля.

.2.4 Определение методов внутри класса

Методы можно определять как внутри, так и вне класса. В методах, определенных внутри класса, не может быть вложенных подпрограмм. Данная концепция была взята из языка программирования Pascal ABC.

type

Point = class

//метод, определенный внутри класса

procedure Show;

begin

//код;Hide;;

//метод, определенный вне класса

procedure Point.Hide;

begin

//код

end;

5.2.5 Перегрузка операций

Для реализации синтаксиса перегрузки операций потребовалось ввести в язык ключевое слово operator. Ключевое слово operator является единственным «сильным» ключевым словом в языке, т.е. его нельзя использовать в составных идентификаторах (составной идентификатор - это идентификатор вида ident1.ident2....identN). Операция реализуется в виде статического метода класса с соответствующим своей арности числом параметров и может быть вызвана как обычным способом, так и явно.

Следующий пример иллюстрирует перегрузку операций:

type= class: string;: integer;(Name: string; Height: integer);.Name := Name;.Height := Height;;operator<(left,right: Student): boolean; static;:= left.Height < right.Height;;ToString: string; override;:= string.Format('{0} ({1})', Name, Height);;;,s2: Student;:=new Student('Stepa Morkovkin',188);:=new Student('Petya Pomidorov',180);(s1,s2);('s1<s2');

//Обычный вызов операции

Writeln(s1<s2);

//Явный вызов операции(Student.operator<(s1,s2));

Readln;.


5.2.6 Шаблоны

В языке возможны два типа шаблонов:

· Управляемые(Generic) шаблоны - шаблоны, которые переводятся в шаблоны .NET. На данный момент семантика для таких шаблонов не реализована.

· Неуправляемые шаблоны - шаблоны в стиле C++; инстанцирование такого шаблона происходит непосредственно в семантическом дереве.

Определение шаблона производится следующим образом:

type= class<ARG1,ARG2,...>(BaseClass,Interface1,...) //секция, необходимая для управляемых шаблонов

ARG1: IArg1Interface1, ..., constructor(Param1,...);: IArg2Interface;

//объявление полей и методов

end;

ConcreteClass = TemplateClass<Class1,Class2,...>;

//инстанцирование шаблона:= new TemplateClass<Class1,Class2,...>(...);:= new ConcreteClass({параметры конструктора});.

Здесь ARG1, ARG2 - шаблонные аргументы. Секция where необходима для объявления управляемых шаблонов; в этой секции определяется, какие интерфейсы реализуют шаблонные аргументы, а также какие параметры имеют их конструкторы.

Инстанцировать шаблон класса с конкретными аргументами возможно только с помощью операции new. Такое решение было принято из-за того, что инстанцирование шаблона в выражении приводит к неоднозначной грамматике.

Пример программы содержащей шаблоны:

type

Pair = class<T, Q>

public: T;: Q;(First: T; Second: Q);.First := First;.Second := Second;;operator+(Left,Right: Pair<T,Q>): Pair<T,Q>; static;:= new Pair<T,Q>(Left.First + Right.First,.Second + Right.Second);;ToString: string; override;:= string.Format('[{0}; {1}]', First, Second);;;,b: Pair<integer, string>;:= new Pair<integer,string>(1, 'один ');:= new Pair<integer,string>(2, 'два');(a + b);;.


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

Директивы компилятора имеют следующий формат:

#directive_name param1 param2 ...

Компилятор может обрабатывать следующие директивы:

1. #apptype application_type

Здесь application_type может иметь значения:

· console - для приложения генерируется консольное окно (по умолчанию);

· windows - для приложения не генерируется консольное окно;

· dll - динамическая библиотека.

. #reference ’dllname’

Подключает сборку с именем dllname в проект.

Наличие директив компилятора позволило обойтись без файла проекта.

Приведем пример использования директив компилятора (простое оконное приложение):

#apptype windows

#reference 'System.Windows.Forms.Dll'System.Windows.Forms;.Run(new Form);

end.

5.2.8 Операции += -=

В язык введены операции += -=. Пример использования таких операций - процедурные переменные:

procedure p1;('p1');;p2;('p2');;p: procedure;+= p1;+= p2;;;.

В данном примере операция += используется для добавления процедуры к процедурной переменной (делегат в .NET).

5.2.9 Методы в записях

В записях имеется возможность определять процедуры и функции. Синтаксис объявления не отличается от методов в классах.

type= record,y: integer;ToString: String; override;:= string.Format('({0},{1})', x, y);

end;;

В данном примере у записи переопределяется метод ToString. Все методы в записи получают модификатор видимости Public.

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


5.2.10 Объявление имен, совпадающих с ключевыми словами

Ключевые слова могут выступать в качестве идентификаторов, если они находятся в составном идентификаторе, например var t: System.Type. Это необходимо, т.к. в платформе .NET классы могут иметь имена, совпадающие с ключевыми словами языка PascalABC.NET. Также в язык введен специальный символ ‘&’ который “гасит” ключевое слово и делает из него обычный идентификатор.

Пример программы:System;TReal: &Type;: System.Type;

&if: integer;

TInteger := typeof(integer);(TInteger);

TReal := typeof(real);(TReal);

&if := &if + 1;(&if);;

end.

5.3 Генератор синтаксических анализаторов

В качестве генератора синтаксических анализаторов была выбрана система GOLDParserBuilder (GPB) [7]. Это свободно распространяемая система, которая является LALR (LookAhead Left Recursive, рекурсия влево с просмотром вперед) генератором синтаксических анализаторов. GOLD переводится как - Grammar Oriented Language Developer.

GPB поддерживает множество языков программирования для написания парсера: ANSI C, C#, C++, Delphi, Java, Python, Visual Basic.

Рассмотрим схему работы GOLDParserBuilder:

Файл грамматики, имеющий расширение .grm, содержит:

· определение множеств символов;

· описание терминалов в виде регулярных выражений;

· описание правил языка в форме Бекуса-Наура.

CGT файл - результат компиляции файла грамматики (Compiled Grammar Tables).

Движок - совокупность классов, которые позволяют интерпретировать CGT файл.

Шаблон для построения правил - файл, имеющий расширение PGT и содержащий набор правил, по которым следует построить скелет для правил на конкретном языке программирования.

5.4 Генерация синтаксического анализатора языка PascalABC.NET

В системе GPB грамматика и действия при свертке правил находятся в разных файлах. Отметим, что в YACC парсерах правила и действия при свертке правил находятся в одном файле, при этом действия при свертке правил записываются справа от правила. Также в GPB отсутствуют удобные символы $$, $1, $2,... которые служат для обращения к аргументам правила. Автору было неудобно редактировать грамматику и действия, поэтому была предложена следующая схема: действия записывать справа от правил, как и в YACC, но заключать их в комментарии.

Комментарии в GOLDParserBuilder заключаются в !* *!, например !*комментарий*!. Однострочный комментарий начинается символом !

Для реализации указанной идеи был написан анализатор grmCommentCompiler (используя GOLDParserBuilder), который анализирует grm файл и записывает содержимое комментариев вместо заглушек правил в скелет парсера. Таким образом, схема компиляции парсера теперь выглядит так:


компилирует grm файл в CGT файл, не обращая внимания на комментарии. Далее с помощью GOLDParserBuilder для движка GPBEngine генерируется скелет парсера. После этого запускается grmCommentCompiler, который анализирует grm файл и записывает содержимое комментариев вместо заглушек правил в скелет парсера. При этом grmCommentCompiler подвергает содержимое комментариев обработке с помощью набора шаблонов, определенных в grm файле, если в комментарии встречены треугольные скобки. Также обрабатываются символы $$ - результат правила, $1, $2, ... - значения терминалов и нетерминалов слева направо. Такие символы приняты в YACC для обработки аргументов правила. При этом символы $$, $n заменяются на:

$n -> LRParser.GetReductionSyntaxNode(n-1)

$$= -> return

Терминальные шаблоны используются grmCommentCompiler для формирования кода правила обработки терминального символа. Рассмотрим пример терминального шаблона:

!*

[TERMINALTEMPLATE]

{

%NAME% _%NAME%=new %NAME%(%PARAMS%);

_%NAME%.source_context=parsertools.create_source_context(token);

%CODE%_%NAME%;

}

*!

Терминальные правила, использующие такой шаблон, будут выглядеть так:

tkHex  ='$'{Hex Digit}+

!*$$=parsertools.create_hex_const(this,$$);*!='#'{Number}+

!*$$=parsertools.create_sharp_char_const(this,$$);*!=''({String Char}|'''')*''

!*$$=parsertools.create_string_const(this,$$);*!  ='-'

!*<op_type_node(op_type.sub_op)>*!  ='+'

!*<op_type_node(op_type.plus_op)>*!  = '/'

!*<op_type_node(op_type.div_op)>*!

Нетерминальные шаблоны используются grmCommentCompiler для формирования кода правила обработки нетерминального символа. Рассмотрим пример нетерминального шаблона:

!*

[NONTERMINALTEMPLATE6]

//TemplateList for %NAME% (create)

{

%NAME% _%NAME%=new %NAME%();

_%NAME%.source_context=((%CODE%)$1).source_context;

_%NAME%.%PARAMS%.Add((%CODE%)$1);_%NAME%;

}

*!

!*

[NONTERMINALTEMPLATE7]

//TemplateList for %NAME% (add)

{

%NAME% _%NAME%=(%NAME%)$1;.create_source_context(_%NAME%,$$,$3);

_%NAME%.%PARAMS%.Add((%CODE%)$3);_%NAME%;

}

*!

Приведем пример использования нетерминальных шаблонов для создания списка:

<ident_list>

::= <identifier> !*6ident_list<idents>ident*!

| <ident_list> tkComma <identifier>

!*7ident_list<idents>ident*!

После генерации CGT файла и файла с правилами, запускается программа ResxMaker, которая упаковывает CGT файл (используется стандартный алгоритм GZIP), а затем генерирует файл ресурса. Текст программы ResxMaker ввиду ее простоты приведен ниже:

#define USEGZIPSystem;System.Resources;System.Reflection;System.Globalization;System.Threading;System.IO;System.IO.Compression;ResXMaker

{class Maker

{static void Make(string filename, string resname,string outputfilename)

{fs = null;br = null;w = null;

{infile = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read);[] data = new byte[infile.Length];count = infile.Read(data, 0, data.Length);.Close();ms = new MemoryStream();

#if USEGZIPcompressedStream = new GZipStream(ms, CompressionMode.Compress, true);.Write(data, 0, data.Length);.Close();

#elif !USEGZIP.Write(data, 0, data.Length);

#endif.Position = 0;= new BinaryReader(ms);[] arr = br.ReadBytes((int)br.BaseStream.Length);= new ResourceWriter(outputfilename);.AddResource(resname, arr);.Generate();.WriteLine("Input file size={0}byte", data.Length);.WriteLine("Output file size={0}byte", arr.Length);c = data.Length; c = c / 100; c = 100 - arr.Length / c;.WriteLine("Compression={0}%", Convert.ToInt32(c));

}(Exception e)

{.WriteLine(e);

}

{(fs != null)

{.Close();.Close();

}

}

}

}MainClass

{static void Main(string[] args)

{(args.Length < 3)

{.WriteLine("CGTResXMaker filename resname outputfilename");;

}.WriteLine("ResMaker: FILE->RESX");.Make(args[0],args[1],args[2]);

}

}

}

В итоге, командный файл для компиляции парсера выглядит так:

echo Compile: GRM to CGT...\goldbuilder_main.exe PascalABC.grm PascalABC.cgtCompile: CGT,PGT to TEMPLATE...\createskelprog_main.exe PascalABC.cgt.pgt PascalABC.tmplCompile: GRM,TEMPLATE to CS....exe PascalABC.grm PascalABC.tmpl PascalABC.csCompile: CGT to RES...\ResXMaker.exe PascalABC.CGT PascalABCLanguage PascalABCLang.resources

Здесь:

· PascalABC.grm - файл грамматики и правыми частями правил;

· PascalABC.pgt - шаблон для построения скелета;

· в каталоге gpbcmd должна находиться консольная версия GOLDParserBuilder.

Результат: парсер, который находится в файле PascalABC.cs, и файл ресурса со сжатой грамматикой, который находится в PascalABCLang.resources.

Отметим, что грамматика языка PascalABC.NET содержит 161 терминальных символа, 796 правил и описана в приложении 1.

5.5 Диагностика сообщений об ошибках

При синтаксическом анализе текста программы движок (набор классов, умеющий интерпретировать CGT-файлы) позволяет обработать ошибки двух типов:

1) Неожиданный символ [символ]

2) Ожидались [набор терминалов], а встречено [терминал]

В движке GPE(Gold Parser Engine) обработка таких ошибок производится следующим образом.

· После очередного вызова LRParser.Parse() мы смотрим что сейчас произошло в парсере:

ParseMessage.LexicalError: Произошла ошибка типа 1.SyntaxError: Произошла ошибка типа 2

· После того, как произошла ошибка, работу пасрера можно продолжить. Для этого необходимо удалить ошибочный токен с вершины стека: LRParser.PopInputToken().

Для обработки ошибок создана иерархия исключений.

error    ->Exception_error   ->error_operand_type  ->syntax_error_token  ->syntax_error_read_error  ->syntax_error_int    ->syntax_error_float    ->syntax_error_hex    ->syntax_error

Потомки класса syntax_error содержат SourceContext (контекст места, где произошла ошибка), что позволяет отладчику выделить место ошибки в тексте программы.

При возникновении ошибки создается объект нужного типа и помещается в список ошибок. Парсер работает, пока количество ошибок не превысит заданное число; если это число превышено, работа парсера прерывается. Для нормальной обработки ошибок этого явно недостаточно. Также неудобно то, что обычно при возникновении ошибок второго типа [набор терминалов] является очень большим - в парсере PascalABC.NET в среднем 25 терминалов.

Эта проблема решена следующим образом:

1) введены приоритеты для терминальных символов;

2) модифицирован движок парсера так, что в момент возникновения ошибки можно было выяснить ожидаемые нетерминальные символы;

) введены приоритеты для нетерминальных символов.

Ниже приведен код, который отвечает за назначения приоритетов для терминалов и нетерминалов.

public override int symbol_priority(Symbol symbol)

{(symbol.Index)(int)SymbolConstants.SYMBOL_TKEND:8;(int)SymbolConstants.SYMBOL_TKBEGIN:(int)SymbolConstants.SYMBOL_TKINTEGER:(int)SymbolConstants.SYMBOL_TKROUNDCLOSE:9;(int)SymbolConstants.SYMBOL_TKIDENTIFIER:10;(int)SymbolConstants.SYMBOL_TKASSIGN:25;(int)SymbolConstants.SYMBOL_TKCOLON:20;(int)SymbolConstants.SYMBOL_TKEQUAL:25;(int)SymbolConstants.SYMBOL_TKSEMICOLON:30;(int)SymbolConstants.SYMBOL_EXPR:100;(int)SymbolConstants.SYMBOL_STMT:110;

}(symbol.SymbolType == SymbolType.Terminal)1;0;

}

При возникновении ошибки второго типа из набора символов выбираются символы с наибольшим приоритетом.

Концепция ошибок, принятая в компиляторе PascalABC.NET, следующая: парсер старается разобрать текст программы до конца, несмотря на ошибки. В каждой ошибке запоминается, на каком узле синтаксического дерева она произошла. Далее конвертор синтаксического дерева в семантическое начинает компиляцию до первого встреченного им узла, в котором произошла синтаксическая ошибка. Если конвертор встретил ранее семантическую ошибку (например, повторное описание идентификатора), то его работа прерывается и выдается семантическая ошибка. Если же семантических ошибок раньше синтаксических не встречено, то выдается первая синтаксическая ошибка.


6. Семантическое дерево

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

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

6.1 Узел для представления операции is

Синтаксис: obj is type_name

Операция is позволяет определить, является ли объект obj потомком класса type_name, либо объектом класса type_name.

Интерфейс:

public interface IIsNode : IExpressionNode

{left

{;

}right

{;

}

}

Реализация:

public class is_node : expression_node, SemanticTree.IIsNode

{expression_node _left;type_node _right;is_node(expression_node left, type_node right, location loc)

: base(compiled_type_node.get_type_node(typeof(bool)), loc)

{

_left = left;

_right = right;

}expression_node left

{

{_left;

}

}type_node right

{

{_right;

}

}.IExpressionNode SemanticTree.IIsNode.left

{

{_left;

}

}.ITypeNode SemanticTree.IIsNode.right

{

{_right;

}

}override semantic_node_type semantic_node_type

{

{semantic_node_type.is_node;

}

}override void visit(SemanticTree.ISemanticVisitor visitor)

{.visit(this);

}

}

Как видно из кода, данный узел является потомком узла expression_node. При конструировании мы указываем, что данное выражение имеет тип bool.

6.2 Узел для представления операции sizeof

Синтаксис: sizeof(type_name)

Операция is позволяет определить размер типа type_name в байтах.

Интерфейс:

public interface ISizeOfOperator : IExpressionNode

{oftype

{;

}

}

Реализация:

public class sizeof_operator : expression_node, SemanticTree.ISizeOfOperator

{type_node _oftype;sizeof_operator(type_node oftype, location loc)

: base(compiled_type_node.get_type_node(typeof(int)), loc)

{

_oftype = oftype;

}type_node oftype

{

{_oftype;

}

}.ITypeNode SemanticTree.ISizeOfOperator.oftype

{

{_oftype;

}

}override semantic_node_type semantic_node_type

{

{semantic_node_type.sizeof_operator;

}

}override void visit(SemanticTree.ISemanticVisitor visitor)

{.visit(this);

}

}

Как видно из кода, данный узел также является потомком узла expression_node. При конструировании мы указываем, что данное выражение имеет тип int.


7. Семантический анализатор

Семантический анализатор - центральная часть компилятора. Семантический анализатор осуществляет перевод синтаксического дерева в семантическое. Важнейшей структурой, используемой в процессе перевода, является таблица символов. Основная часть семантического анализатора была разрботана Водолазовым Н.

7.1 Таблица символов

Таблица символов (ТС) - это таблица имен, встречающихся при компиляции. Вместе с каждым именем хранится его семантическая характеристика (тип, пространство имен, модификатор видимости и т.д.). Пространства имен (области видимости) хранятся в отдельной таблице, тесно связанной с таблицей символов.

Таблица символов используется лишь на этапе перевода синтаксического дерева в семантическое для быстрого поиска имен. Затем вся информация о переменных хранится непосредственно в семантическом дереве.

Таблица символов в данной реализации устроена как единая хеш-таблица для всех пространств имен. Вместе с каждым именем хранится список пространств имен, в которых данное имя встречается.


В качестве хеш-функции была использована:

int HashFunc(string s)

{n=0;(int i=0;i<s.Length;i++)=127*n+s[i]*7;Math.Abs(n % hash_size);

}

В качестве функции разрешения конфликтов была использована:

int GetHash(string s)

{hash=HashFunc(s);i=1;(hash_arr[hash]!=null)

{(hash_arr[hash].Name==s) return hash;=i*11;=Math.Abs((hash + i)%hash_size);

}hash;

}

Эффективность рассмотренной хеш-таблицы была показана в докладе «Реализация таблицы символов компилятора» [8] (докладчик Ткачук А.В., научный руководитель Михалкович С.С.) в 2004 на студенческой конференции «Неделя науки», в секции «Теоретическое и прикладное программирование». В докладе было обосновано, что хеш-таблица с такой хеш-функцией показывает результаты производительности, близкие к теоретическим [9].

7.1.1 Дерево областей видимости

Дерево областей видимости реализовано в виде динамического массива. На следующем примере показано, как дерево областей видимости можно упаковать в одномерный массив. Красными стрелками отмечен путь по дереву областей видимости из области с номером 5 в область с номером 0:

Области видимости объединены в иерархию классов. Далее в пункте 7.1.3 мы подробно рассмотрим все типы областей видимости и алгоритмы поиска в них.

7.1.2 Общий алгоритм поиска

Рассмотрим общий алгоритм поиска в таблице символов на примере.

Поиск в таблице символов (ТС) происходит следующим образом: допустим в ТС поступает запрос: «найти имя “a” в области видимости 5 и выше». В хеш-таблице находим имя “a”. Далее смотрим, есть ли в списке областей видимости область 5. Такой нет, и мы начинаем подниматься по таблице из области 5. Рассмотрим верхнюю область 4: ее в списке тоже нет. Поднимаемся выше - там находим область 1. Она есть в списке, и мы можем дать ответ «”a” найден в области видимости 1».


7.1.3 Иерархия областей видимости

Рассмотрим все классы областей видимости и их члены.

1. BaseScopeSymbolInfo Find(string name)

Базовый класс для всех областей видимости. Содержит виртуальную функцию Find.

. Scope->BaseScope

Scope TopScopeSymbolInfo Find(string name)FindOnlyInScope(string name)FindOnlyInType(string name)AddSymbol(string Name,SymbolInfo Inf)

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

. DotNETScope->Scope

Служит для объявления области видимости класса из сборки .NET

Разработчик блока «доступ к .NET» (см. структура компилятора PascalABC.NET) Бондарев И. порождает потомка этого класса, в котором переопределяет метод Find. Процессом добавления таких областей видимости занимается блок “Интерфейс TC”. Таким образом, таблица символов не занимается поиском в сборках, а лишь знает о том, что эта область - из .NET сборки. Тем самым удалось отделить собственно поиск имен в сборках от таблицы символов.

. UnitPartScope->Scope[] TopScopeArray

Предок для областей, которые являются частями модуля. Содержит массив областей видимости (строится по секции uses), которые по смыслу являются интерфейсными частями модулей либо сборками .NET.

. UnitInterfaceScope->UnitPartScope

Интерфейсная часть модуля.

. UnitImplementationScope->UnitPartScope

Часть модуля, содержащая реализации. Свойство TopScope в этом классе всегда должно иметь тип UnitInterfaceScope.

. ClassScope->ScopeBaseClassScope

Используется для задания области видимости класса. Хранит область видимости базового класса.

. ClassMethodScope->Scope

Scope MyClass

Используется для задания области видимости метода. Хранит область видимости класса, в котором он описан.

7.1.4 Алгоритмы поиска в областях видимости

Для поиска имен в таблице символов используется свой алгоритм для каждой области видимости. Все алгоритмы поиска возвращают односвязный список элементов типа SymbolInfo (информацию о символе). Опишем алгоритм поиска для каждой области видимости.

Простая область видимости

Ищем в этой области видимости затем в объемлющей области видимости (ТоpScope).

Интерфейсная часть модуля

Ищем в области видимости интерфейса, затем ищем в массиве областей видимости (TopScopeArray) справа налево (подключенные модули).

Часть модуля, содержащая реализации

Сначала ищем в этой области видимости, затем ищем в массиве областей видимости справа налево. Далее ищем в TopScope (интерфейсной части модуля).

Область видимости класса

Ищем в классе, далее в надклассах (BaseClassScope), затем в модуле, в котором описан класс (ТоpScope), далее в подключенных модулях (ТоpScope.TopScopeArray).

Область видимости метода

Ищем в методе, ищем в классе (MyClass), далее в надклассах, затем в модуле (ТоpScope), в котором описан метод, далее в подключенных модулях (ТоpScope.TopScopeArray).

Область видимости класса в сборке

Поиск осуществляется с помощью рефлексии. При этом происходит кеширование в специальные хеш-таблицы. В зависимости от типа найденного члена класса создается специальный SymbolInfo.

7.1.5 Алгоритмы поиска в таблице символов

Первый алгоритм служит для поиска имени только в заданной области видимости.

SymbolInfo FindOnlyInScope(Scope scope,string Name)

1. Если в хеш-таблице такого имени нет то выход;

2. Список=ХешТабица[ХешТабица.ВзятьХеш(Name)].СписокОбластей;

. Если Список.Найти(scope), то вернуть информацию;

. Если Scope типа UnitImplementationScope то

scope=scope.TopArea;

перейти к пункту 3;

Второй алгоритм предназначен для поиска всех подходящих имен. Параметр OnlyInType определяет, нужно ли искать это имя только в классе.

SymbolInfo FindAll(Scope scope,string Name,bool OnlyInType)

1. Если OnlyIntype и не(Scope типа ClassScope) то выход;

2. ТекущаяОблась=scope;

2.1 Если ТекущаяОблась типа DotNETScope то

Результат.Добавить(ТекущаяОблась,name);

Если Результат.ЕстьХоябыОдин то вернуть Результат;

.2 Если ТекущаяОблась типа UnitPartScope то

Если ТекущаяОблась типа UnitImplementation то

Результат.Добавить(

ПоискПоВсемМодулям(ТекущаяОблась,name));

ТекущаяОблась=ТекущаяОблась.TopScope;

Результат.Добавить(

ПоискПоВсемМодулям(ТекущаяОблась,name));

Если Результат.ЕстьХоябыОдин то вернуть Результат;

.3 Если ТекущаяОблась типа СlassScope то

Двигаться по всей иерархии классов вверх

Результат.Добавить(ТекущаяОблась,name);

Если Результат.ЕстьХоябыОдин или OnlyInType то

вернуть Результат;

.4 Результат.Добавить(ТекущаяОблась,name);

.5 Если Результат.ЕстьХоябыОдин то вернуть Результат;

.6 Если ТекущаяОблась типа СlassMethodScope то

Двигаться по всей иерархии классов вверх

Результат.Добавить(ТекущаяОблась,name);

Если Результат.ЕстьХоябыОдин то вернуть Результат;

.7 Если ТекущаяОблась.TopArea!=null то

ТекущаяОблась=ТекущаяОблась.TopArea;
перейти к пункту 2.1;

Иначе Выход;

Выбор подходящих имен из области видимости происходит в методе

Результат.Добавить() с помощью двух проверок.

Первая проверка реализуется с помощью функции IsNormal:

private bool IsNormal(SymbolInfo to,SymbolInfo add)

{(((to.symbol_kind==symbol_kind.sk_none)&&(add.symbol_kind==symbol_kind.sk_none))&&(to.scope==add.scope))

||

((to.symbol_kind==symbol_kind.sk_overload_function)&&(add.symbol_kind==symbol_kind.sk_overload_function)));

}

Эта функция проверяет, подходит ли add к символу to.

С каждым символом хранится информация

enum symbol_kind {sk_none, sk_overload_function};

т.е. символ может быть обычным либо перегруженной подпрограммой

Алгоритм:

1. Если (to - обычный символ) и (add - обычный символ) и (они в одной области видимости) то разрешить. Далее конвертор дерева, получив в ответ на запрос поиска такой список, поймет, что здесь надо выдать ошибку «повторно описанный идентификатор».

2. Если (to - перегруженная подпрограмма) и (add - перегруженная подпрограмма) то разрешить. Конвертор дерева должен сам выбрать подходящую подпрограмму, исходя из анализа параметров подпрограммы.

Вторая проверка реализуется с помощью функции IsVisible:

private bool IsVisible(SymbolInfo ident, Scope fromScope)

{(fromScope == null)true;(FindClassScope(ident.scope) == null)true;(ident.access_level)

{access_level.al_public:access_level.al_internal:true;access_level.al_protected:(ident.scope, fromScope)

||(ident.scope, fromScope);access_level.al_private:IsInOneModule(ident.scope, fromScope);

}true;

}

Эта функция проверяет, виден ли данный символ, исходя из данного контекста поиска символа. Параметр fromScope указывает, из какой области видимости осущесвляется поиск (контекст поиска).

Уровни видимости символа:_level.al_public - публичный, видим для всех;_level.al_internal - видим для всех, но после компиляции становися приватным;_level.al_protected - защищенный, видин только в модуле в котором определен, в классе в котором определен и в о всех потомках этого класса;_level.al_private - приватный, видин только в модуле в котором определен и в классе в котором определен.

Функция IsVisible реализуется с помощью нескольких вспомогательных функций:- определяет, находится ли область видимости IdentScope в одном классе с FromScope, либо в одном из базовых классах FromScope:

private bool IsInOneOrDerivedClass(Scope IdentScope, Scope FromScope)

{= FindClassScope(IdentScope);= FindClassScope(FromScope);(FromScope != null)

{(IdentScope.ScopeNum == FromScope.ScopeNum)true;= ((ClassScope)FromScope).BaseClassScope;

}false;

}

IsInOneModule - позволяет определить, находится ли Scope1 и Scope2 в одном модуле:

private bool IsInOneModule(Scope Scope1, Scope Scope2)

{= FindUnitInterfaceScope(Scope1);= FindUnitInterfaceScope(Scope2);

(Scope1 != null) && (Scope2 != null)

&& (Scope1.ScopeNum == Scope2.ScopeNum);

}

FindClassScope - находит область видимости класса, в которую вложена область видимости scope:

private Scope FindClassScope(Scope scope)

{(scope != null && !(scope is ClassScope))(scope is ClassMethodScope)= ((ClassMethodScope)scope).MyClass;= scope.TopScope;scope;

}

FindUnitInterfaceScope - находит интерфейсную область видимости модуля класса, в которую вложена область видимости scope:

private Scope FindUnitInterfaceScope(Scope scope)

{(scope!=null && !(scope is UnitInterfaceScope))= scope.TopScope;scope;

}

.2 Генерация узлов семантического дерева

Семантический анализатор состоит из визитора по синтаксическому дереву и нескольких вспомогательных блоков (таблица символов, таблица типов, контекст компиляции), котрые предоставляют необходимые алгоритмы для семантического анализа. Также семантический анализатор содержит реализацию всех интерфейсов семантического дерева. Семантический анализатор и большинство алгоритмов семантического анализа были разработаны Водолазовым Н. Далее будет описан процесс генерации некоторых частей семантического дерева, которые были сделаны автором.

7.2.1 Операции is, as

В синтаксическом дереве операции is и as определяются одним узлом. В узле содержится флаг, который определяет тип операции. Операции были реализованы в виде одного узла, т.к. имеют одинаковые аргументы и схожий семантический смысл.

Функция визитора семантического анализатора для этого узла выглядит следующим образом:void visit(SyntaxTree.typecast_node node)

{_node en = convert_strong(node.left);_node tp = convert_strong(node.right);(type_table.is_derived(en.type, tp) ||_table.is_derived(tp, en.type) || en.type==tp)

{(node.cast_op ==.SyntaxTree.op_typecast.is_op)

{_node isn = new is_node(en, tp,_location(node));_value(isn);

}

{(tp.is_value_type)new OperatorAsMustBeUsedWithAReferenceType(tp.name,_location(node.right));_node asn = new as_node(en, tp,_location(node));_value(asn);

};

}new ExpectedDerivedClasses(get_location(node));

}

Дадим словесное описание алгоритма:

Конвертируется выражение, стоящее слева от операции

Конвертируется тип, стоящий справа от операции

Если тип выражения и тип справа связаны в иерархию или одного типа то

{

Если это оператор is то

Конструируем семантический узел is

иначе

{

Если тип справа это не ссылочный тип то

ошибка «Оператор as применим только к

ссылочным типам»

Конструируем семантический узел as

}

}

иначе ошибка «У обьекта и типа нет общего предка»

7.2.2 Операции typeof, sizeof

Операция typeof служит для получения обьекта типа System.Type из типа.void visit(SyntaxTree.typeof_operator node)

{_value(new typeof_operator(_type(node.type_name,_location(node.type_name)),_location(node))

);

}

При конструировании семантического узла все необходимые проверки делает функция find_type.

Операция sizeof служит для определения размера типа в байтах.void visit(SyntaxTree.sizeof_operator node)

{_node tn = find_type(node.type_name,_location(node.type_name));(!tn.is_value_type)new OperatorSizeOfMustBeUsedWithAValueType(.name, get_location(name));_value(new sizeof_operator(tn, get_location(node)));

}

Данная операция не применима к ссылочным типам. В случае, если параметр - ссылочный, то происходит ошибка «Оператор sizeof не применим к ссылочным типам».

7.2.3 Операция new

Операция new является синонимом вызова конструктора Create и позволяет создать новый объект класса. При конструировании операции new происходит поиск подходящего конструктора и создание узла вызова этого конструктора.

public void visit(SyntaxTree.new_expr _new_expr)

{_node tn = ret.visit(_new_expr.name_ref);_list exprs = null;(_new_expr.params_list != null)= convert_expression_list(

_new_expr.params_list.expressions);= new expressions_list();_value(create_constructor_call(tn, exprs,_location(_new_expr.name_ref)));

}

private base_function_call create_constructor_call(type_node tn, expressions_list exprs, location loc)

{si = tn.find_in_type(.compiler_string_consts.default_constructor_name,.CurrentScope);(si == null)new ConstructorNotFound(loc);_node fn = convertion_data_and_alghoritms.select_function(exprs, si, loc);create_static_method_call_with_params(fn, loc, tn, false, exprs);

}

Вначале ищется тип, который необходимо сконструировать. Далее с помощью функции find_in_type ищутся все его конструкторы. Если ни одного конструктора не найдено, то происходит ошибка. Функция convertion_data_and_alghoritms.select_function выбирает конструктор с подходящими параметрами, если подходящая функция не будет найдена, то происходит ошибка. Далее с помощью функции create_static_method_call_with_params создается вызов этого конструктора.

7.2.4 Типизированные и бинарные файлы

Типизированный файл - файл с произвольным доступом, хранящий элементы одного типа. Для типизированного файла применимы следующие операции: чтение, запись, переход к записи с определенным номером. Также можно узнать, сколько записей находится в файле. В качестве элементов файла могут выступать записи, примитивные типы, строки фиксированной длины и массивы. В составляющей элемента файла не допустимы ссылочные типы (например, файл не может состоять из записей, содержащих поле типа string или array of integer).

На языке PascalABC.NET типизированные файлы описываются так же как и на языке Object Pascal:f: file of file_type;

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

На языке PasclaABC.NET типизированные файлы описываются так же как и на языке Object Pascal:f: file;

7.2.4.1 Часть кода, реализованная на PascalABC.NET

Большинство кода для реализации типизированных и бинарных файлов написано на самом языке PascalABC.NET и находится в системной библиотеке PABCSystem.pas:

Типизированный файл:

TypedFile = class: FileInfo;: FileStream;: BinaryReader;: BinaryWriter;: System.Type;: longint;(ElementType: System.Type);.ElementType := ElementType;:= RuntimeSizeOf(ElementType);;ToString: string; override;:= string.Format('file of {0}', ElementType);;;

Бинарный файл:

BinaryFile = record: FileInfo;: FileStream;: BinaryReader;: BinaryWriter;

end;

Функция, позволяющая получить из обычного массива массив System.Array:

function GetNullBasedArray(arr: object): System.Array;fi: FieldInfo;:= arr.GetType.GetField(InternalNullBasedArrayName);fi = nil then:= nilResult := System.Array(fi.GetValue(arr));

end;

Функция, которая позволяет вычислить размер типа:

function RunTimeSizeOf(t: System.Type): integer;: System.Type;: object;: array of FieldInfo;: System.Array;: integer;: FieldInfo;t.IsPrimitive thent = typeof(integer) then:= sizeof(integer)if t = typeof(real) then:= sizeof(real)if t = typeof(boolean) then:= sizeof(boolean)if t = typeof(char) then:= sizeof(char)if t = typeof(byte) then:= sizeof(byte)if t = typeof(shortint) then:= sizeof(shortint)if t = typeof(smallint) then:= sizeof(smallint)if t = typeof(word) then:= sizeof(word)if t = typeof(longword) then:= sizeof(longword)if t = typeof(longint) then:= sizeof(longint)if t = typeof(uint64) then:= sizeof(uint64)if t = typeof(single) then:= sizeof(single)if t.IsValueType then:= Activator.CreateInstance(t);:= t.GetFields;:= 0;i:=0 to fa.Length-1 do:= Result + RunTimeSizeOf(fa[i].FieldType):= t.GetField(InternalNullBasedArrayName);fi = nil thennew Exception('Bad Type in RunTimeSizeOf');:= Activator.CreateInstance(t);:= GetNullBasedArray(elem);:= NullBasedArray.GetType.GetElementType;:= RunTimeSizeOf(t1)*NullBasedArray.Length;

end;;

Приведем алгоритм работы последней функции:

Если это примитивный тип то

результат := sizeof(тип)

иначе

если это не ссылочный тип то

результат := просматриваем все поля вычисляя их размер

иначе

//это обычный массив

получаем массив индексируемый с нуля

результат := разимер элемента массива * количество элементов

Процедура чтения одного элемента из типизированного файла имеет вид:

function TypedFileReadT(f: TypedFile; t: System.Type): object;: System.Type;: object;: array of FieldInfo;: System.Array;: integer;t.IsPrimitive thent = typeof(integer) then:= f.br.ReadInt32if t = typeof(real) then:= f.br.ReadDoubleif t = typeof(boolean) then:= f.br.ReadBooleanif t = typeof(char) then:= f.br.ReadCharif t = typeof(byte) then:= f.br.ReadByteif t = typeof(shortint) then:= f.br.ReadSByteif t = typeof(smallint) then:= f.br.ReadInt16if t = typeof(word) then:= f.br.ReadUInt16if t = typeof(longword) then:= f.br.ReadUInt32if t = typeof(longint) then:= f.br.ReadInt64if t = typeof(uint64) then:= f.br.ReadUInt64if t = typeof(single) then:= f.br.ReadSingleif t.IsValueType then:= Activator.CreateInstance(t);:= t.GetFields;i:=0 to fa.Length-1 do[i].SetValue(elem,TypedFileReadT(f,fa[i].FieldType));:= elem;:= Activator.CreateInstance(t);:= GetNullBasedArray(elem);NullBasedArray<>nil then:= NullBasedArray.GetType.GetElementType;i:=0 to NullBasedArray.Length-1 do.SetValue(TypedFileReadT(f,t1),i);;:= elem;;;

Приведем алгоритм работы процедуры чтения:

Если это примитивный тип то

считать из файла элемент соответствующего типа

иначе

если это не ссылочный тип то

создать элемент этого типа

просматриваем все поля и считываем элементы

соответствующего типа

иначе

//это обычный массив

создать такой массив

получаем массив индексируемый с нуля

выясняем тип элементов массива и его длину

считываем необходимое количество элементов

соответствующего типа

Процедура записи одного элемента из типизированного файла имеет вид:

procedure Write(f: TypedFile; val: object);: System.Type;: array of FieldInfo;: integer;: System.Array;:=val.GetType;t.IsPrimitive thent = typeof(integer) then.bw.Write(integer(val))if t = typeof(real) then.bw.Write(real(val))if t = typeof(char) then.bw.Write(char(val))if t = typeof(boolean) then.bw.Write(boolean(val))if t = typeof(byte) then.bw.Write(byte(val))if t = typeof(shortint) then.bw.Write(shortint(val))if t = typeof(smallint) then.bw.Write(smallint(val))if t = typeof(word) then.bw.Write(word(val))if t = typeof(longword) then.bw.Write(longword(val))if t = typeof(longint) then.bw.Write(longint(val))if t = typeof(uint64) then.bw.Write(uint64(val))if t = typeof(single) then.bw.Write(single(val))if t.IsValueType then:= t.GetFields;i:=0 to fa.Length-1 do(f,fa[i].GetValue(val));:= GetNullBasedArray(val);NullBasedArray<>nil theni:=0 to NullBasedArray.Length-1 do(f,NullBasedArray.GetValue(i));

end;;

Алгоритм данной процедуры аналогичен алгоритму чтения из типизированного файла.

Функция, возвращающая размер файла:

function FileSize(f: TypedFile): longint;

beginf.fs.Length mod f.ElementSize <> 0 thennew Exception('Bad typed file size');:= f.fs.Length div f.ElementSize;

end;

Функция, возвращающая текущую позицию в файле:

function FilePos(f: TypedFile): longint;:= f.fs.Position div f.ElementSize;

end;

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

procedure Seek(f: TypedFile; n: integer);.fs.Position := n*f.ElementSize;

end;

Процедура, осуществляющая обрезание файла с текущей позиции:

function Eof(f: TypedFile): boolean;f.fs <> nil then:= f.fs.Position = f.fs.Lengthraise new Exception('File not opened');

end;

Как видно из кода, эти файлы реализованы на базе стандартных потоков System.IO.BinaryReader и System.IO.BinaryWriter. Обращение к компонентам элемента файла реализовано с помощью рефлексии. Процедура write полностью реализована на самом языке PascalABC.NET за исключением проверок параметров во время этапа компиляции.


7.2.4.2 Генерация узла семантического дерева

Процесс семантического анализа типизированых файлов происходит следующим образом: во время компиляции семантический анализатор находит уже переведенный в семантическое дерево тип TypedFile. В начале блока, где обявляется переменная этого типа, вставляются команды создания этого типа с параметром - типом элементов.

public void visit(SyntaxTree.file_type _file_type)

{

//Типизированый файл_node el_type =_strong(_file_type.file_of_type);(!CanUseThisTypeForTypedFiles(el_type))new InvalidElementTypeForTypedFile(el_type, get_location(_file_type.file_of_type));_value(context.create_typed_file_type(el_type, get_location(_file_type)));

}

Перевод бинарного файла происходит аналогично, за исключением того, что ему не нужно знать тип своих элементов.

7.2.4.3 Проверки этапа компиляции

На этапе компиляции необходимо делается проверка на допустимость элементов для типизированных файлов. Данная проверка раеализована с помощю рекурсивной функции CanUseThisTypeForTypedFiles:bool CanUseThisTypeForTypedFiles(type_node el_type)

{(el_type.is_value)

{

//проверка на пимитивный тип(SystemLibrary.CanUseThisTypeForTypedFiles(el_type))true;

//это запись(el_type is common_type_node)

{_type_node ctn = el_type as common_type_node;(class_field cf in ctn.fields)(!CanUseThisTypeForTypedFiles(cf.type))false;true;

}

//Это откомпилированная запись(el_type is compiled_type_node)

{_type_node ctn = el_type as_type_node;.Reflection.FieldInfo[] fields =.compiled_type.GetFields();(System.Reflection.FieldInfo fi in fields)(!fi.IsStatic)(!CanUseThisTypeForTypedFiles(_type_node.get_type_node(fi.FieldType)))false;true;

}

}(IsBoudedArray(el_type))

{

//это обычный массив_array_interface bai = (bounded_array_interface)el_type.get_internal_interface(internal_interface_kind.bounded_array_interface);CanUseThisTypeForTypedFiles(bai.element_type);

}false;

}

С помощью рекурсии обходятся все поля типа и проверяются на допустимость для типизированного файла. Необходимо отметить, что хотя в .NET массив представлятся ссылочным типом, в PascalABC.NET он считается размерным: при передаче как параметр либо присваивании массив копируется. Также массивы разрешены в качестве элементов типизированного файла.

Для процедур read и write также делаются следующие проверки:

· Если первый параметр - это типизированный файл, то сравнить все остальные параметры на совпадение их типа с типом элементов типизированного файла.

· Если тип хотя бы одного параметра не совпадает с типом элементов типизированного файла, то происходит ошибка времени компиляции.

7.2.4.4 Реализация процедуры read

Процедура read не может быть реализована на языке Pascal, т.к. имеет переменное число var-параметров. Решена эта проблема следующим образом: все вызовы процедуры Read, в которых первый параметр - это типизированный файл, заменяются вызовом специальной функции TypedFileRead, имеющей прототип:

function TypedFileRead(var f: TypedFile;: System.Type): object;

и описанной в модуле PABCSystem.pas, при этом вставляется также узел явного приведения типов.

Пример:

var, x2: integer;

f: file of integer;(f, x1, x2);

//заменяется на:= integer(TypedFileRead(f, typeof(integer)));:= integer(TypedFileRead(f, typeof(integer)));

//это эквивалентный код на PascalABC.NET дерева на которое

//происходит замена вызова процедуры Read.

Для бинарных файлов концепция аналогична, за исключением того, что специальная функция с именем BinaryFileRead имеет прототип

function BinaryFileRead(var f: BinaryFile;: System.Type): object;

7.2.5 Изменяемые строки string

В платформе .NET строки неизменяемые. Для обеспечения совместимости с Object Pascal было необходимо сделать строки изменяемыми. Было рассмотрено несколько вариантов решения этой проблемы:

· сделать строки на базе StringBuilder;

· сделать строки на базе char[].

Но такие варианты вели к несовместимости строк в компиляторе со стороками .NET. Поэтому было принято следующее решение:

var s: string;[1]:='x';

//заменяется на(s, 1, 'x');

//это эквивалентный код на PascalABC.NET дерева на которое

//происходит замена вызова s[1]:=’x’.

Процедура StringDefaultPropertySet находится в модуле PABCSystem.pas:

procedure StringDefaultPropertySet(var s: string;: integer; c: char);:= string.Concat(s.Substring(0, index),,.Substring(index + 1));;

Таким образом, образуется новая строка из трех подстрок. Такое решение обладает несомненным плюсом: совместимостью с Object Pascal и строками .NET, но и очевидным минусом: такое присваивание индекса имеет очень маленькую скорость.

7.3 Генерация сообщений об ошибках

Каждая семантическая ошибка является классом. Семантические ошибки заключены в иерархию:-> Error -> SemanticError -> CompilationError -> CompilationErrorWithLocation

При генерации исключения в параметры конструктора передаются все необходимые сведения для каждой конткретной ошибки. В текущей версии компилятора - 120 классов семантических ошибок.

Приведем пример реализации класса семантической ошибки для неправильного типа элементов типизированного файла:class InvalidElementTypeForTypedFile : CompilationErrorWithLocation

{type_node el_type;InvalidElementTypeForTypedFile(type_node el_type, location loc)

: base(loc)

{.el_type = el_type;

}override string ToString()

{string.Format(StringResources.Get(

"INVALID_ELEMENT_TYPE_FOR_TYPED_FILE"));

}

}

Для получения текстового варианта сообщения о ошибке необходимо воспользоваться методом ToString.

Для обеспечения локализации ошибки на другие языки используется система локализации и класс StringResources, о котором будет расказано в главе 11.

Классы ошибок, порожденные от CompilationErrorWithLocation, имеют свойство Location, удовлетворяющее интерфейсу ILocation. Данное свойство содержит информацию о месте начала и конца ошибки в тексте программы (строка, столбец), а также имя файла программы.


8. Генератор кода

В качестве целевой платформы была выбрана платформа Microsoft .NET, т.к. генерируемый под эту платформу код (MSIL)[10] имеет ряд существенных достоинств:

• кроссплатформенность;

• объектная ориентированность;

• высокая скорость выполнения;

• простота генерации кода в сравнении с генерацией машинного кода.

Генератор кода является визитором по семантическому дереву.

IL (MSIL) - это единый байт-код для платформы .NET. IL ориентирован на работу со стеком, т. е. все его команды помещают операнды в стек исполнения и извлекают операнды из стека. Поскольку IL не поддерживает команды работы с регистрами, отпадает необходимость в распределении регистров. Кроме того, команд IL гораздо меньше, чем машинных (около 200).

8.1 Перевод семантического дерева в IL код

Для перевода используется стандартная библиотека Reflection.Emit.

Перевод семантического дерева в IL-код происходит в несколько этапов:

1. Создается динамическая сборка.

2. Создается главный статический класc cо статическим методом Main.

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

. Переводятся все заголовки типов.

. Переводятся все заголовки методов.

. Пререводятся заголовки подпрограмм, глобальные переменные и константы.

. Переводятся тела подпрограмм и методов.

. Переводится тело основной программы.

. Закрываются типы (требование .NET).

8.2 Перевод конструкций в IL код

Переврод большенсва конструкций был осуществлен Бондаревым И.

Перевод конструкций в основном осуществяется в соответсвующих методах visit визитора по семантическому дереву. При переводе используется набор алгоритмов, находящихся в NETGenerator.NETGeneratorTools. Алгоритмы из этого класса являютсся стаическими методами - обертками над вызовами метода System.Reflection.Emit.ILGenerator.Emit.

Прмер такого алгоритма - PushLdc, который кладет на стек константу:static void PushLdc(ILGenerator il, Type elem_type, object value)

{(Type.GetTypeCode(elem_type))

{TypeCode.Boolean:TypeCode.Byte:.Emit(OpCodes.Ldc_I4_S, (byte)value);;TypeCode.SByte:.Emit(OpCodes.Ldc_I4_S, (sbyte)value);;TypeCode.Char:.Emit(OpCodes.Ldc_I4, (char)value);;TypeCode.Int16:.Emit(OpCodes.Ldc_I4, Convert.ToInt32(value));;TypeCode.UInt16:.Emit(OpCodes.Ldc_I4, (UInt16)value);;TypeCode.Int32:.Emit(OpCodes.Ldc_I4, (Int32)value);;TypeCode.UInt32:.Emit(OpCodes.Ldc_I4, (UInt32)value);;TypeCode.Int64:.Emit(OpCodes.Ldc_I8, (Int64)value);;TypeCode.UInt64:((UInt64)value > Int64.MaxValue)

{tmp =

(Int64)((UInt64)value - Int64.MaxValue - 1);.Emit(OpCodes.Ldc_I8, tmp);.Emit(OpCodes.Conv_U8);.Emit(OpCodes.Ldc_I8, Int64.MaxValue);.Emit(OpCodes.Conv_U8);.Emit(OpCodes.Add);.Emit(OpCodes.Ldc_I4_1);.Emit(OpCodes.Add);

}.Emit(OpCodes.Ldc_I8, Convert.ToInt64(value));;TypeCode.Single:.Emit(OpCodes.Ldc_R4, (Single)value);;TypeCode.Double:.Emit(OpCodes.Ldc_R8, (Double)value);;TypeCode.String:.Emit(OpCodes.Ldstr, (string)value);;:(elem_type.IsEnum).Emit(OpCodes.Ldc_I4, (Int32)value);new Exception("Немогу положить PushLdc для " +.GetType().ToString());;

}

}

Исходя из типа константы, выбирается необходимая команда, и константа кладется на стек. Данный метод будет работать для всех примитивных типов, а также для перечислимых типов.

8.2.1 Операции is, as

Логическая операция is позволяет определить, принадлежит ли обьект к некоторому классу или его потомкам.

Метод visit для узла is:override void visit(SemanticTree.IIsNode value)

{idexpr = is_dot_expr;_dot_expr = false;.left.visit(this);_dot_expr = idexpr;right = helper.GetTypeReference(value.right).tp;.Emit(OpCodes.Isinst, right);.Emit(OpCodes.Ldnull);.Emit(OpCodes.Cgt_Un);(is_dot_expr).CreateLocalAndLoad(il, typeof(bool));

}

Переменная is_dot_expr является членом класса ILGenerator и указывает на то, является ли вызов этой операции частью составного выражения. Перменная il является обьектом класса System.Reflection.Emit.ILGenerator. Вызов value.left.visit(this) запускает визитор для левого поддерева операции is. В результате этого на стек будет положен объект некоторого класса. Длаее получаем тип правого поддерева. После чего кладем команды

1. (OpCodes.Isinst, type) - проверка на то, является ли лежащий на стеке объект типом type либо его потомком. Если да, то на стек положится объект, приведенный к этому типу, иначе на стек кладется константа null.

2. OpCodes.Ldnull - кладет на стек константу null

. OpCodes.Cgt_Un - производит сравнение двух значений на вершине стека. Если первое значение больше второго, то на стек кладется константа 1, иначе кладется константа 0.

Все эти команды снимают используемые ими аргументы со стека.

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

Операция as необходима для приведения обекта к некоторому типу. Если приведение невозможно, то на стек положится контана null.

Метод visit для узла as имеет вид:override void visit(SemanticTree.IAsNode value)

{idexpr = is_dot_expr;_dot_expr = false;.left.visit(this);_dot_expr = idexpr;right = helper.GetTypeReference(value.right).tp;.Emit(OpCodes.Isinst, right);

}

Так как команда OpCodes.Isinst собственно и реализует функционал операции as, то данный код проще, чем для операции is: value.left.visit(this) запускает визитор для левого поддерева операции as. В результате этого на стек будет положен объект некоторого класса. Далее получаем тип правого поддерева. После чего кладем команду (OpCodes.Isinst, type).

.2.2 Операции typeof, sizeof

Операция typeof позволяет из типа получить объект типа System.Type.

Метод visit для операции typeof:override void visit(ITypeOfOperator value)

{.Emit(OpCodes.Ldtoken,.GetTypeReference(value.oftype).tp);.EmitCall(OpCodes.Call,(Type).GetMethod("GetTypeFromHandle"), null);

}

Первая команда кладет на стек токен типа, тип которого необходимо узнать. Вторая команда кладет на стек вызов метода System.Type.GetTypeFromHandle, который возвращает объект типа System.Type.

Операция sizeof позволяет узнать размер типа в байтах. В отличие до операции sizeof в С#, в PascalABC.NET эта операция позволяет определить размер не только примитивного типа но и любого размерного типа.

Метод visit для операции sizeof:override void visit(ISizeOfOperator value)

{tp=helper.GetTypeReference(value.oftype).tp;(tp.IsPrimitive)

{(TypeFactory.GetPrimitiveTypeSize(tp));;

}(tp.IsValueType)

{.PushTypeOf(il, tp);typ = typeof(.Runtime.InteropServices.Marshal);[] prms = new Type[1];[0] = typeof(Type);.EmitCall(OpCodes.Call,.GetMethod("SizeOf", prms), null);;

}

}

После получения типа мы выясняем:

1. Если это примитивный тип, то кладем на стек константу - размер этого типа. Это делается с помощю метода TypeFactory.GetPrimitiveTypeSize, котрый содержит хеш-таблицу с размерами примитивных типов.

2. Если это размерный тип, то кладем на стек вызов метода System.Runtime.InteropServices.Marshal.SizeOf, который позволяет определить размер value-типа.


9. Промежуточная форма хранения программы (PCU)

(Pascal Compiled Unit) - промежуточная форма хранения программы в компиляторе PascalABC.NET. PCU является сохраненным в бинарный формат семантическим деревом программы. Особенности PCU:

· семантическое дерево может быть воссановлено из PCU частями;

· каждый модуль хранится в отдельном PCU файле.

9.1 Выбор промежуточной формы хранения программы

В качестве промежуточной формы хранения программы можно было бы выбрать не семантическое дерево, а IL код. Это быстрее, но менее гибко: если использовать внутренне предсавление ввиде IL кода то невозможно проводить высокоуровневые преобразования программ т.к. для них необходимо дерево прграммы. Кроме того прийдется делать поправки в IL коде при сборке нескольих программых модулей. Следует отметить что Delphi.NET испльзует для хранения промежуточного представления именно IL код.

Были сделаны замеры скорости работы различных частей компилятора. Они приводятся на диаграмме ниже.


Из данной диаграммы видно, что генерация кода по семантическому дереву занимает лишь 8% от общего времени компиляции. Следовательно, использование в качестве промежуточного представления семантического дерева, а не IL кода, не приведет к значительному замедлению компиляции.

9.2 Варианты использования PCU

Один из главных вариантов испрользования PCU - ускорение компиляции. Тесты показали, что при использовании PCU повторная компиляция многомодульных программ ускоряется многократно.

Т.к. внутреннее представление - это семнантическое дерево, а на IL код, то с ним можно проводить ряд высокоуровневых перобразований:

• оптимизирующие преобразования программ;

• распараллеливание программ;

• генерация текста программы на другом языке;

• анализ качества кода;

• визуализация кода (генерация UML-диаграмм классов).

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

9.3 Схема компиляции с использованием PCU

Схема компиляции с использованием PCU выглядет следующим образом:

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


9.4 Сериализация и десериализация некоторых узлов семантического дерева

Основные методы сохранания и чтения PCU файлов были разработаны Бондаревым И. Далее будут описаны некоторые узлы семантического дерева, реализованные автором. Методы чтения/записи из/в PCU в этих узлах используют уже готовые методы чтения/записи различных конструкций.

Функции для записи узлов as, is, typeof, sizeof, array_const имеют вид:void VisitIsNode(is_node node)

{(node.left);(node.right);

}void VisitAsNode(as_node node)

{(node.left);(node.right);

}void VisitTypeOfOperator(typeof_operator node)

{(node.oftype);

}void VisitSizeOfOperator(sizeof_operator node)

{(node.oftype);

}void VisitArrayConst(array_const node)

{.Write(node.element_values.Count);(constant_node cn in node.element_values)(cn);

}

Функции для чтения узлов as, is, typeof, sizeof, array_const имеют вид:is_node CreateIsNode()

{new is_node(CreateExpression(), GetTypeReference(), null);

}as_node CreateAsNode()

{new as_node(CreateExpression(), GetTypeReference(), null);

}typeof_operator CreateTypeOfOperator()

{new typeof_operator(GetTypeReference(), null);

}sizeof_operator CreateSizeOfOperator()

{new sizeof_operator(GetTypeReference(), null);

}array_const CreateArrayConst()

{<constant_node> element_values = new List<constant_node>();count = br.ReadInt32();(int i = 0; i < count; i++)_values.Add((constant_node)CreateExpression());new array_const(element_values, null);

}


10. Управляющий блок

Управляющий блок управляет процессом компиляции программы и является интерфейсом для подключения оболочек к компилятору. Управляющий блок содержит базовую иерархию ошибок, алгоритм компиляции модулей и алгоритмы чтения/записи внутреннего представления (PCU файлов). Управляющий блок контролирует работу следующих блоков компилятора:

· контроллер синтаксических анализаторов;

· конвертор синтаксического дерева в семантическое;

· генератор кода.

Интерфейс управляющего блока имеет вид:

public class Compiler

{

public uint LinesCompiledCompilerInternalDebug InternalDebugCompilerState StateSupportedSourceFile[] SupportedSourceFilesCompilationUnitHashTable UnitTableCompilerOptions CompilerOptionsdelegate void ChangeCompilerStateEventDelegate(Compiler sender, CompilerState State, string FileName);event ChangeCompilerStateEventDelegate OnChangeCompilerState;List<Errors.Error> ErrorsListCompiler(SourceFilesProviderDelegate SourceFilesProvider)string Compile()void Reload()

}

Здесь параметр конструктора SourceFilesProvider - делегат предостовляющий компилятору механизм обращения к исходным кодам программы.delegate object SourceFilesProviderDelegate(string FileName, SourceFileOperation FileOperation);enum SourceFileOperation

{, GetLastWriteTime, Exists

}

Данный механизм необходим, т.к. визуальная оболочка может не сохранять изменения текста программы на диске. Для работы компилятора необходимо три операции с файлами:

· GetText - возвращает текст файла

· GetLastWriteTime - последнне вермя изменения

· Exists - существует ли файл

Реализация такого делегата для прямого доступа к файлам на диске имеет вид:object DefaultSourceFilesProvider(string FileName, SourceFileOperation FileOperation)

{(FileOperation)

{SourceFileOperation.GetText:(!File.Exists(FileName)) return null;tr = new StreamReader(FileName, System.Text.Encoding.GetEncoding(1251));Text = tr.ReadToEnd();.Close();Text;SourceFileOperation.Exists:File.Exists(FileName);SourceFileOperation.GetLastWriteTime:File.GetLastWriteTime(FileName);

}null;

}

Опишем остальные члены класса Compiler:- количесво откомпилированых строк;- свойство, предостовляющее доступ к различным настройкам компилятора. Данное свойсто будет описано в пункте 13.3;- состояние компилятора. Может принимать следующие занчения:

· Ready - компилятор закончил компиляцию;

· CompilationStarting - старт компиляции;

· Reloading - загрузка компилятора;

· BeginCompileFile - начало компиляции файла;

· CompileInterface - компиляция интерфейса модуля;

· CompileImplementation - компиляция части реализаций модуля;

· EndCompileFile - конец компиляции файла;

· ReadDLL - чтение сборки;

· ReadPCUFile - чтение PCU файла;

· SavePCUFile - сохранение PCU файла;

· CodeGeneration - генерация кода;

· CompilationFinished - компиляция окончена;

· PCUReadingError - произошла ошибка чтения PCU файла;

· PCUWritingError - произошла ошибка записи PCU файла.

Во время изменеия этого свойства срабатывет событие OnChangeCompilerState.- поддерживаемые типы исходных файлов. Данное свойство зависит от синтаксических анализаторов, подключенных к компилятору. Класс SupportedSourceFile содержит следующие поля:

· string[] Extensions - расширения файлов;

· string LanguageName - имя языка.- таблица откомпилированных модулей. Данное свойство будет описано в пункте 13.2.- опции компиляции:

· OutputFileType - тип выходного файла. Може принимать значения:

СlassLibrary, ConsoleApplicaton, WindowsApplication, PascalCompiledUnit;

· SourceFileName - имя главного файла программы;

· OutputFileName - имя выходного файла;

· SystemDirectory - директория? в которой находятся системные модули;

· SearchDirectory - директоря для поиска модулей;

· StandartModules - имена стандартых модулей, которые поумолчанию подключаются к каждому модулю;- список ошибок, произошедших во время компиляции.- функция, запускающая процесс компиляции. В случае успешной компиляции возвращает имя выходного файла, иначе возвращает null.- функция, осуществляющая перезагрузку всех частей компилятора. Позволяет переподключить синтаксические анализаторы.

10.1 Алгоритм компиляции модулей

Одним из самых сложных алгоритмов в компиляторе оказался алгоритм компиляции модулей. Нетривиальной является последовательность, в которой надо откомпилировать interface и implementation части модулей. Было принято следующее решение: управление порядком компиляции модулей будет осуществлять управляющий блок. Конвертор дерева при этом должен иметь две функции:nterface

При вызове этих функций секции uses должны быть откомпилированы, т.е. все интерфейсные части модулей из секции uses должны быть откомпилированы.

Идея алгоритма состоит в следующем:

1. сначала компилируются модули, которые не зависят от других модулей;

2. затем компилируются интерфейсные части тех модулей, для которых секции uses из части интерфейса откомпилированы (т.е. все интерфейсные части модулей из секции uses откомпилированы);

. затем компилируются implementation части модулей, в которых интерфейсная часть уже откомпилирована;

. далее шаг 2-3 повторяется, пока все модули не будут откомпилированы.

Рассмотрим пример:

Стрелки сверху - это uses из секции interface, например, на этой схеме модуль t3 в секции interface содержит uses t1.

Стрелка снизу указывает на uses из секции implementation, например, на этой схеме модуль t2 в секции implementation имеет uses t3.

Для такой связки модулей необходимо выполнить компиляцию в следующей последовательности:

Compiling t1.pas...t1.past2.pasInterface t2.past3.pasInterface t1.pasImplementation t1.pasInterface t3.pasImplementation t3.pasImplementation t2.pas

Посмотрим на порядок компиляции модулей указанным алгоритмом в случае более сложной завязки модулей:

Compiling t1.pas...t1.past2.past3.past4.pasInterface t4.pasInterface t3.past5.pasInterface t5.pasImplementation t5.pas

Compile Interface t2.pasInterface t1.pasImplementation t1.pasImplementation t4.pasImplementation t2.pasImplementation t3.pas

Далее приведен собственно сам рекурсивный алгоритм компиляции модулей.

*

* CompileUnit(ИмяФайла)

* 1.CompileUnit(new СписокМодулей,ИмяФайла)

* 2.Докомпилировать модули из СписокОтложенойКомпиляции;

*

* CompileUnit(СписокМодулей,ИмяФайла)

* 1.ТекущийМодуль=ТаблицаМодулей[ИмяФайла];

* Если (ТекущийМодуль!=0) то

*  Если (ТекущийМодуль.Состояние==BeginCompilation)

* СписокМодулей.Добавить(ТекущийМодуль);

* Выход;

* иначе перейти к пункту 5

*

* 2.Если ЭтоФайлDLL(ИмяФайла) то

* Если ((ТекущийМодуль=СчитатьDLL(ИмяФайла))!=0) то

* СписокМодулей.Добавить(ТекущийМодуль);

* ТаблицаМодулей.Добавить(ТекущийМодуль);

* Выход;

* иначе

* Ошибка("Не могу подключить сборку");

* Выход;

*

* 3.Если ЭтоФайлPCU(ИмяФайла) то

* Если ((ТекущийМодуль=СчитатьPCU(ИмяФайла))!=0) то

* СписокМодулей.Добавить(ТекущийМодуль);

* ТаблицаМодулей.Добавить(ТекущийМодуль);

* Выход;

* иначе

* иначе перейти к пункту 4;

*

* 4.ТекущийМодуль=новыйМодуль();

* ТекущийМодуль.СинтаксическоеДерево=

Парасеры.Парсить(ИмяФайла,ТекущийМодуль.СписокОшибок);

* Если (ТекущийМодуль.СинтаксическоеДерево==0) то

* Если (ТекущийМодуль.СписокОшибок.Количество==0) то

* Ошибка("Модуль не неайден");

* иначе

* Ошибка(ТекущийМодуль.СписокОшибок[0]);

* ТаблицаМодулей[ИмяФайла]=ТекущийМодуль;

* ТекущийМодуль.Состояние=BeginCompilation;

*

* 5.СинтаксическийСписокМодулей=

ТекущийМодуль.СинтаксическоеДерево.Interface.UsesList;

* Для(i=СинтаксическийСписокМодулей.Количество-1-ТекущийМодуль.КомпилированыеВInterface.Количество;i>=0;i--)

* ТекушийМодуль.ТекущийUsesМодуль=

СинтаксическийСписокМодулей[i].ИмяФайла;

* ИмяUsesФайла=СинтаксическийСписокМодулей[i].ИмяФайла;

* Если (ТаблицаМодулей[ИмяUsesФайла]!=0)

* Если (ТаблицаМодулей[ИмяUsesФайла].Состояние==BeginCompilation)

* Если (ТаблицаМодулей[ТаблицаМодулей[ИмяUsesФайла].

ТекущийUsesМодуль].Состояние=BeginCompilation)

* Ошибка("Циклическая связь модулей");

* CompileUnit(ТекущийМодуль.КомпилированыеВInterface,ИмяUsesФайла);

* Если (ТекушийМодуль.Состояние==Compiled) то

* СписокМодулей.Добавить(ТекушийМодуль);

* Выход;

*

* 6.ТекущийМодуль.СемантическоеДерево=

КонверторДерева.КонвертироватьInterfaceЧасть(

ТекущийМодуль.СинтаксическоеДерево,

ТекущийМодуль.КомпилированыеВInterface,

ТекущийМодуль.СписокОшибок);

* СписокМодулей.Добавить(ТекущийМодуль);

* СинтаксическийСписокМодулей=

ТекущийМодуль.СинтаксическоеДерево.Implementation.UsesList;

* Для(i=СинтаксическийСписокМодулей.Количество-1;i>=0;i--)

* Если (ТаблицаМодулей[СинтаксическийСписокМодулей[i].ИмяФайла].

Cостояние=BeginCompilation)

* СписокОтложенойКомпиляции.Добавить(

ТаблицаМодулей[СинтаксическийСписокМодулей[i].ИмяФайла]);

* иначе

* CompileUnit(ТекущийМодуль.КомпилированыеВImplementation,

СинтаксическийСписокМодулей[i].ИмяФайла);

* Если(ДобавлялиХотябыОдинВСписокОтложенойКомпиляции)

* СписокОтложенойКомпиляции.Добавить(ТекущийМодуль);

* выход;

* иначе

* КонверторДерева.КонвертироватьImplementationЧасть(

ТекущийМодуль.СинтаксическоеДерево,

ТекущийМодуль.СемантическоеДерево,

ТекущийМодуль.КомпилированыеВImplementation

ТекущийМодуль.СписокОшибок);

* ТекущийМодуль.Состояние=Compiled;

* СохранитьPCU(ТекущийМодуль);

*

*

*

* [краткая верcия алгоритма компиляции модулей]

*

* CompileUnit(ИмяФайла)

* 1.CompileUnit(new СписокМодулей,ИмяФайла)

* 2.Докомпилировать модули из СписокОтложенойКомпиляции;

*

* CompileUnit(СписокМодулей,ИмяФайла);

* 1.Если у этого модуля откомпилирован хотябы интерфейс то

* добавить его в СписокМодулей

* выход

* 2.Если это DLL то

* считать

* добавить его в СписокМодулей

* выход

* 3.Если это PCU то

* считать

* добавить его в СписокМодулей

* выход

* 4.создать новый компилируемыйМодуль

* РаспарситьТекст(ИмяФайла)

* Состояние компилируемогоМодуля установить на BeginCompilation

* 5.Для всех модулей из Interface части

компилируемогоМодуля справа налево

* Если мы уже начаинали компилировать этот модуль

* Если состояние модуля BeginCompilation

* Если состояние последнего

компилируемого им модуля BeginCompilation

* ошибка("Циклическая связь модулей")

* выход

* CompileUnit(Список из Interface части компилируемогоМодуля,модуль.имя)

* Если компилируемыйМодуль.Состояние Compiled то

* добавить его в СписокМодулей

* выход

* 6.Откомпилировать Interface часть компилируемогоМодуля

* Для всех модулей из Implementation части компилируемогоМодуля справа налево

* Если состояние очередного модуля BeginCompilation то

* добавить его в список отложеной компиляции;

* иначе

* CompileUnit(Список из Implementation части компилируемогоМодуля,модуль.имя)

* Если Добавляли Хотябы Один В Список Отложеной Компиляции то

* добавить компилируемыйМодуль в список отложеной компиляции

* выход

* Откомпилировать Implementation часть компилируемогоМодуля

* Состояние компилируемогоМодуля установить на Compiled

* добавить его в СписокМодулей

* Сохранить компилируемыйМодуль в виде PCU файла на диск

*

*

************************************************************


11. Система локализации

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

Идея локализации состоит в следующем: вместо обычных строк используются специальные строки-идентификаторы (ключи): например, «IDENT», далее мы ставим в соответвие таким идентификаторам текст на разных языках:

файл rus.dat:=идентификатор

файл eng.dat:=identifier

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

Система локализации реализована в виде статического класса StringResources в сборке Localisation.dll.class StringResources

{StringResources()static string Get(string keyName)static string ResDirectoryName

public static void SetTextForObject(object obj, string prefix)static void UpdateObjectsText()

}

Статический конструктор загружает в таблицу соответствия язык по умолчанию. Язык по умолчанию хранится в ресурсе. Файл ресурса создается программой, написаной на PascalABC.NET.- функция, возвращающая текст, соответвующий ключу KEY.- Задает директорию, из которой считываются языковые файлы. При изменении этого свойсва происходит считывание.- Функция, производящая замену всех строковых полей объекта obj, содержащих ключ с перфиксом prefix, на соответвующие значания. Если обьект содержит свойство, возвращающее массив объектов, то замена производится и в этих объектах. Это необходимо для реализации локализации для визуальных объектов таких как форма или кнопка. Данная возможнось используется в визуальной оболочке компилятора PascalABC.NET. Все поля, которые подвергались замене, запоминаются в специальной таблице соответствия ObjectHashtable. Функция реализована с помощю рефлексии.- производит замену всех значений полей объектов из таблицы ObjectHashtable на соответвующие значения. Эта функция необходима для реализации механизма смены языка «на лету».

Языковые файлы могут содержать строки следующего формата:=value - устанавливает ключу IDENT значение value

%PREFIX%=prefix - устанавливает префикс для всех последующих ключей

//coment - коментарий. Не учитывается=nLINES

...

устанавливает ключу IDENT многострочное значение line1 ... linen

Управление языками производит статический класс StringResourcesLanguage:class StringResourcesLanguage

{static List<string> AccessibleLanguagesstatic string ConfigDirectorystatic void LoadDefaultConfig()static string CurrentLanguageName

}

Здесь:- список доступных языков.- директория, в которой находятся папки с языковыми файлами. В каждой папке с языковыми файлами должен быть файл с именем “.LanguageName” в котором содержится имя данного языка. При изменении данного свойсва происходит сканирование подпапок и заполнение списка AccessibleLanguages.- устанавливает значение ConfigDirectory на «папка_сборки\lng».- задает текущий язык, после чего из соответвующей папки загружаются языковые файлы и вызывется StringResources.UpdateObjectsText().

Рассмотрим пример использования системы локализации для генерации сообщения об ошибках. Класс ошибки NameRedefinition (повторное определение имени) содерит функциюoverride string ToString()

{string.Format(.Get("NAME_REDEFINITION_{0}"), _name);

}

Как видно из кода, для формирования сообщения о ошибке используется ключ “NAME_REDEFINITION_{0}” который заменяется на значение с помощью вызова функции StringResources.Get, а затем в полученую строку подставляется параметр - имя повторно объявленого идентефикатора.

В русском языке ключ имеет следующее значение:_REDEFINITION_{0}=Повторно объявленный идентификатор {0}

в английском:_REDEFINITION_{0}=Name redefinition {0}


12. Консольная оболочка компилятора

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

Командная строка:source_file_name_file_name - имя исходного файла главной программы

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

?    - Помощьdir   - Переход в директорию dir_name  - Компилировать файл_mask  - Компилировать файлы по маске  - Перезапустить компилятор=0|1 - Перезапускать копилятор перед компиляцией [0]=0|1  - Перекомпилировать PCU файлы [0]=0|1  - Генерировать отладочную информацию (PDB файлы) [0]=output_directory - Задать выходную директорию  - Очистить экран=0|1 - Выводить все сообщения компилятора [0]=n  - Выбрать язык=0|1 - сканировать поддиректории [0]

[] - значение поумолчанию.


13. Модули визуальной оболочки

Модули визуальной оболочки (плагины) необходимы для расширения функциональности визуальной оболочки. Модулям доступно внутреннее представление компилятора (синтаксическое и семантическое дерево), с которым они могут делать различные преобразования, например визуалирацию.

13.1 Интерфейс подключения

Интерфейсы для плагинов находятся в сборке PluginsSupport.dll. Интерфейс, который должен реализовать плагин, имеет вид:interface IVisualPascalABCPlugin

{Name

{;

}Banner

{;

}GetGUI(List<IPluginGUIItem> MenuItems,<IPluginGUIItem> ToolBarItems);

}- возвращает имя плагина.- возвращает имя и версию плагина.- служит для получения визуального интерфейса плагина.- пункты в меню;- пункты на панели.

Интерфейс IPluginGUIItem служит для создания визуальных элементов плакина всраеваемых в оболочку(пунктов меню, кнопок на панели).interface IPluginGUIItem

{Text

{;

}Hint

{;

}.Drawing.Image Image

{;

}.Drawing.Color ImageTransparentColor

{;

}.Windows.Forms.Keys ShortcutKeys

{;

}ShortcutKeyDisplayString

{;

}Execute();

}- текст на кнопке;- всплывающая подсказка;- изображение;- фоновый цвет изображения;- горячие клавиши;- тест, отображающий сочетание горячих клавиш;- функция, которая выполняется в тот момент, когда нажали данную кнопку.

Интерфейс, который должна реализовывать оболочка, к которой подключается такой плагин:interface IVisualEnvironmentCompiler

{.Compiler Compiler

{;

}ExecuteSourceLocationAction(.SourceLocation SourceLocation,Action);ExecuteAction(VisualEnvironmentCompilerAction Action,obj);

}- компилятор, обьект класса PascalABCCompiler.Compiler.- выполняет манипуляции с курсором в исходном тексте программы. Action - действие которое необходимо выполнить с положением курсора SourceLocation.enum SourceLocationAction

{,,,

}- функция, которая позволяет выполнить некоторое действие оболочки. Возможные действия:enum VisualEnvironmentCompilerAction

{,,,,,

}- запустить пограмму;- откомпилировать программу;- перекомпилировать программу;- остановить программу;- открыть файл;- получить одну из стандартных директорий.

Также сборка содержит класс PluginGUIItem, реализующий интерфейс IPluginGUIItem:delegate void PluginGUIItemExecuteDelegate();class PluginGUIItem : IPluginGUIItem

{text;hint;image;imageTransparentColor;executeDelegate;.Windows.Forms.Keys shortcutKeys = System.Windows.Forms.Keys.None;shortcutKeyDisplayString = null;PluginGUIItem(string text, string hint, Image image, Color imageTransparentColor, PluginGUIItemExecuteDelegate executeDelegate)

{.text = text;.hint = hint;.image = image;.imageTransparentColor = imageTransparentColor;.executeDelegate = executeDelegate;

}PluginGUIItem(string text, string hint, Image image, Color imageTransparentColor, PluginGUIItemExecuteDelegate executeDelegate, System.Windows.Forms.Keys shortcutKeys, string shortcutKeyDisplayString)

{.text = text;.hint = hint;.image = image;.imageTransparentColor = imageTransparentColor;.executeDelegate = executeDelegate;.shortcutKeys = shortcutKeys;.shortcutKeyDisplayString = shortcutKeyDisplayString;

}string Text

{{ return text; }

}string Hint

{{ return hint; }

}Image Image

{{ return image; }

}Color ImageTransparentColor

{{ return imageTransparentColor; }

}void Execute()

{();

}System.Windows.Forms.Keys ShortcutKeys

{{ return shortcutKeys; }

}string ShortcutKeyDisplayString

{{ return shortcutKeyDisplayString; }

}

}

Это позволяет при реализации плагинов пользоваться уже готовым классом для визуальных компонентов, либо порождать от него наследника.


13.2 Модуль «Визуализатор синтаксического дерева»

Плагин «Визуализатор синтаксического дерева» служит для визуализации синтаксического дерева программы и позволяет осуществлять переход от узла дерева к тексту программы, из которого был получен данный узел. Данный плагин может быть использован при обучении для того чтобы показать, как тест программы преобразуется в синтаксическое дерево.

Ниже приводится скриншот данного плагина:

Для «перехвата» синтаксического дерева необходимо подключится к событию OnChangeCompilerState компилятора. Все структуры внутреннего предсавления, в том числе и синтаксическое дерево, будут существовать вплоть до момента CompilerState.Ready. Самый удобный момент захвата ссылки на синтаксическое дерево - это событие CompilerState.CompilationFinished, которое срабатывает сразу перед событием CompilerState.Ready..UnitTable - хеш таблица откомпилированных модулей. Каждый модуль из этой таблицы хранит свое синтаксическое и семантическое дерево. Обраться к синтаксическому дереву i-го модуля можно следующим образом: Compiler.UnitTable.Values[i].SyntaxTree. Далее все ссылки на синтаксические деревья запоминаются, и для просмотра любого дерева достаточно выбрать его из списка.

Для отображения дерева используется визитор по синтаксическому дереву. Приведем пример функций visit данного визитора:void visit(statement_list _statement_list)

{_node(_statement_list.left_logical_bracket, "left_logical_bracket");_collection(_statement_list.subnodes,"subnodes");_node(_statement_list.right_logical_bracket, "right_logical_bracket");

}void visit(assign _assign)

{_node(_assign.to,"to");_node(_assign.from,"from");

}

Функция prepare_node создает визуальный узел дерева и далее запускает для него тот же самый визитор.


13.3 Модуль «Управление компилятором»

Плагин «Управление компилятором» используется разработчиками для отладки компилятора. Данный модуль позволяет отключить различные блоки компилятора. Отключение различных частей компилятора происходит с помощью обращения к специальному полю Compiler.InternalDebug, которое имеет следующую структуру:class CompilerInternalDebug

{bool AddStandartUnits;bool CodeGeneration;bool PCUGenerate;bool SemanticAnalysis;bool SkipInternalErrorsIfSyntaxTreeIsCorrupt;bool SkipPCUErrors;bool DebugVersion { get; }

}

Изменение этих полей приводит к отключению соответствующих частей компилятора. Например, положив CodeGeneration=True, можно осуществить компиляцию без генерации кода.


Также данный модуль добавляет на панель визуальной оболочки кнопки:

· запустить ILDASM - запускает стандартный IL дизасемблер для последенего откомпилированного файла;

· запустить DBGCLR - запускает стандартный отладчик для последенего откомпилированного файла.

13.4 Модуль «Контроль внутренних ошибок»

Плагин «Контроль внутренних ошибок» перехватывает ошибки компилятора, принадлежащие к классу CompilerInternalError. Появление такой ошибки свидетельствует об ошибке в коде ядра компилятора. При возникновении такой ошибки плагин собирает все необходимые сведения: исходные коды модулей, которые участвовали в компиляции, PCU файлы, а также следующую информацию:

· время и дату;

· полную версию компилятора;

· версию платформы;

· версию ОС;

· количество процессоров;

· список состояний компилятора;

· полный текст ошибки.

Все эти данные сжимаются в zip-архив, и этот архив предлагается выслать разрабочикам компилятора.

Ошибка перхватывается при срабатывании события CompilerState.Ready путем анализа списка всех возникших во время компиляции ошибок, находящихся в Compiler.ErrorsList.

Скриншот произошедшей внутренней ошибки приведен ниже:



14. Подключение задачника Programming Taskbook

Электронный задачник Programming Taskbook предназначен для обучения программированию на языках Pascal, Visual Basic, C++, C#, Visual Basic .NET. Он содержит 1000 учебных заданий, охватывающих все основные разделы базового курса программирования: от скалярных типов и управляющих операторов до сложных структур данных и рекурсивных алгоритмов. Автор задачника - М. Э. Абрамян [11].

Задачник поставляется в виде неуправляемой динамической библиотеки (dll) со следующим интерфейсом:

procedure StartPT(options:integer);'PT4PABC.dll' name 'startpt';FreePT;'PT4PABC.dll' name 'freept';CheckPT(var res:integer):string;'PT4PABC.dll' name 'checkptf';

procedure RaisePT(s1,s2:string);'PT4PABC.dll' name 'raisept';

procedure Task(name:string);'PT4PABC.dll' name 'task';GetR(var param:real);'PT4PABC.dll' name 'getr';PutR(r:real);'PT4PABC.dll' name 'putr';GetN(var param:integer);'PT4PABC.dll' name 'getn';PutN(param:integer);'PT4PABC.dll' name 'putn';GetC(var param:char);'PT4PABC.dll' name 'getc';PutC(param:char);'PT4PABC.dll' name 'putc';GetS(param:StringBuilder);'PT4PABC.dll' name 'gets';PutS(param:string);'PT4PABC.dll' name 'puts';GetB(var param:integer);'PT4PABC.dll' name 'getb';PutB(param:integer);'PT4PABC.dll' name 'putb';GetP(var param:IntPtr);'PT4PABC.dll' name 'getp';PutP(var param:IntPtr);'PT4PABC.dll' name 'putvarp';DisposeP(sNode:IntPtr);'PT4PABC.dll' name 'disposep';

14.1 Модуль на языке PascalABC.NET

Подлючение задачника к системе программирования PascalABC.NET осуществляет модуль PT4.pas, который написан на языке PascalABC.NET. Данный модуль делает все необходимые обертки над вызовами процедур из dll, а также инициализирует и завершает работу задачника. Интерфейс этого модуля приведен далее:

type

InternalNode = record

Data:integer;

Next:IntPtr;:IntPtr;;Node = classNext:PT4NodePrev:PT4NodeData:integer;(aData:integer);(aData:integer; aNext:PT4Node);Destroy;;= PT4Node;Task(name:string);Write(params args:array of object);GetInteger:integer;GetReal:real;GetChar:char;GetString:string;GetBoolean:boolean;GetNode:PT4Node;PutInteger(val:integer);PutReal(val:real);PutChar(val:char);PutString(val:string);PutBoolean(val:boolean);PutNode(val:PT4Node);Read(var val:integer);Read(var val:real);Read(var val:char);Read(var val:string);Read(var val:boolean);Read(var val:PT4Node);

Пример прогаммы, реализующей решение задачи Text1 на PascalABC.NET, имеет вид:

uses PT4;f:text;:string;,k,i,j:integer;('Text1');(filename);read(n);read(k);(f,filename);(f);i:=1 to n do beginj:=1 to k do(f,'*');(f);;(f);

end.

Пример прогаммы реализующей решение задачи Dynamic2, имеет вид:

uses PT4;tek,prev:TNode;:integer;('Dynamic2');(tek);tek<>nil do begin:=n+1;(tek.Data);:=tek;:=tek.Next;;(n,prev);.

Ниже приводится скриншот выполненного задания:

14.2 Модуль визуальной оболочки

Модуль визуальной оболочки используется для добавления в оболочку трех кнопок:

· посмотреть задания;

· создать шаблон программы;

· посмотреть результаты.


Плагин использует следующие функции из dll задачника:

[DllImport("PT4\\PT4PABC.dll", CharSet = CharSet.Ansi, EntryPoint = "pt4demo")]extern int pt4demo(string startdir,ptdir, string dllname,dlloptions, int usedlltopics, int usedemodat,demodatname, string demodatdir,topic, ref int number);

[DllImport("PT4\\PT4PABC.dll", CharSet = CharSet.Ansi, EntryPoint = "pt4load")]extern int pt4load(string startdir,ptdir, string envlist, string dirlist,startenv, StringBuilder edittext, StringBuilder filename);

[DllImport("PT4\\PT4PABC.dll", CharSet = CharSet.Ansi, EntryPoint = "pt4results")]extern int pt4results(string startdir,ptdir, int showbrowsedir, int browsesubdirs,int fontsize, ref int showinffile, ref int showtxtfile);

[DllImport("PT4\\PT4PABC.dll", CharSet = CharSet.Ansi, EntryPoint = "pt4getdatinfo")]extern int pt4getdatinfo(string startdir,studentname, ref int fontsize,envir, StringBuilder ptdir);

[DllImport("PT4\\PT4PABC.dll", CharSet = CharSet.Ansi, EntryPoint = "pt4setdatinfo")]static extern int pt4setdatinfo(string startdir,fontsize, string envir);

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

public int Demo(StringBuilder topic, ref int number)

{pt4demo(PABCWorkDirectory, PT4Directory, "pt4", 0, 1, 2, "", "", topic, ref number);

}int Load(StringBuilder edittext, StringBuilder filename)

{pt4load(PABCWorkDirectory, PT4Directory, "PABC2", "", 1, edittext, filename);

}void Results()

{= new StringBuilder(50),= new StringBuilder(20),= new StringBuilder(100);fsize = 0, fontsize = 0,= 1, showtxt = 1,= pt4getdatinfo("", s1, ref fsize, s2, s3);(i == 0)= fsize;results(PABCWorkDirectory, PT4Directory, 0, 0, ref fontsize,showinf, ref showtxt);(i == 0 && fsize != fontsize)setdatinfo("", fontsize, "");

}

Функции, реализующие собственно эти три действия, выглядят так:

. Посмотреть задания:

public void ExecuteB_D()

{(topic, ref number);

}

2. Создать шаблон программы:

public void ExecuteB_L()

{filename = new StringBuilder(250);i = Load(edittext, filename);(i < 1 || filename.ToString() == "");.ExecuteAction(.OpenFile,.ToString());

}

3. Посмотреть результаты:

public void ExecuteB_R()

{();

}


Заключение

Работа над проектом PascalABC.NET началась в сентябре 2005 года. Одной из наиболее сложных задач было распараллеливание проекта на относительно независимые части, для того чтобы разработчики могли работать над проектом независимо. Проект был разделен на следующие независимые части:

· синтаксическое дерево;

· синтаксический анализатор;

· семантическое дерево;

· конвертор синтаксического дерева в семантическое дерево;

· генератор кода.

Центральной идеей, позволившей улучшить и ускорить разработку, стало разделение дерева программы на синтаксическое и семантическое деревья.

Настоящий текст содержит описание той работы, которая была проделана непосредственно автором: от синтаксического анализа до генерации кода. Наиболее важными и сложными частями компилятора, разработанными автором, являются:

· грамматика языка PascalABC.NET;

· синтаксическое дерево и синтаксический анализатор;

· таблица символов и таблица пространств имен, алгоритмы поиска имен в таблице символов;

· управляющий блок;

· алгоритм компиляции модулей, учитывающий наличие промежуточного представления PCU;

· реализация типизированных файлов;

· концепция реализации изменяемых строк;

· система локализации.

Итогом совместной работы ниже перечисленных студентов (5 человек) над проектом является система программирования PascalABC.NET, включающая язык PascalABC.NET и компилятор с этого языка и предназначенная для начального обучения современному программированию.

В проекте участвовали следующие разработчики:

Бондарев Иван, магистр 2 года мехмата РГУ. Генератор IL кода, PCU файлы. Работал над проектом до весны 2006 года.

Водолазов Николай, магистр 2 года мехмата ЮФУ. Семантическое дерево и семантический анализ, генерация кода.

Иванов Сергей, магистр 1 года мехмата ЮФУ. Работает в проекте, начиная с февраля 2007 года. Занимается введением в язык управляемых и неуправляемых шаблонов. С апреля 2007: семантическое дерево и семантический анализ, генерация кода.

Ткачук Александр, 5 курс мехмата ЮФУ. Синтаксическое дерево и синтаксический анализатор. Таблица символов. Управляющий блок. Плагины. С апреля 2007: Семантическое дерево и семантический анализ, генерация кода.

Харитонова Любовь, 4 курс мехмата ЮФУ. Занимается разработкой визуальной оболочки для компилятора. Работает в проекте с сентября 2006 года.

Исходный код проекта состоит из 220 файлов (4,6 мегабайт, 145000 строк).

По результатам работы опубликовано 4 статьи и в 2006-2007 гг. проведено 2 семинара: для преподавателей и для студентов, а также сделан доклад на семинаре Штейнберга Б.Я. Кроме того, в 2007 году Михалковичем С.С. и Ткачуком А.В. был создан сайт проекта [12].


Литература

1. Ахо А., Сети Р., Ульман Дж. Компиляторы: принципы, технологии и инструменты.: Пер. с англ. - М.: Издательский дом «Вильямс», 2001, 768 с.

2. Водолазов Н.Н., Михалкович С.С., Ткачук А.В. Архитектура компилятора PascalABC.NET. Труды IV Всероссийской научно-технической конференции «Технологии Microsoft в теории и практике программирования». 2007 г., с 181-182.

. Бондарев И.В., Водолазов Н.Н., Михалкович С.С., Ткачук А.В. Некоторые вопросы реализации компилятора языка программирования PascalABC.NET. Всероссийская школа-семинар «Математическое моделирование, биомеханика и информационные технологии в современном университете». 2006. Тезисы докладов. с.10-11.

. Водолазов Н.Н., Михалкович С.С., Ткачук А.В. Преимущества использования компилятора PascalABC.NET в учебном процессе. Всероссийская школа-семинар "Математическое моделирование, биомеханика и информационные технологии в современном университете". 2007. Тезисы докладов.

. Водолазов Н.Н., Михалкович С.С., Ткачук А.В. Опыт разработки учебного языка программирования для платформы .NET. «Современные информационные технологии в образовании: Южный Федеральный округ» Научно-методическая конференция 2007. Тезисы докладов.

. Э.Гамма, Р. Хелм, Р. Джонсон, Дж. Влиссидес. Приемы объектно-ориентированного проектирования (паттерны проектирования). Питер, 2001, 368 с.

7. http://www.devincook.com/goldparser/doc/index.htm. Documentation for the GOLD Parser Builder.

8. Ткачук А.В. Доклад «Реализация таблицы символов компилятора», науч. рук. Михалкович С.С.

http://pascalabc.net/downloads/doklad_nametable_konf2004.rar.

9. Роберт Седжвик. Фундаментальные алгоритмы на С++. Части 1-4. М., Диасофт, 2002. 496 с.

10. Common Language Infrastructure Standards. http://msdn2.microsoft.com/en-us/netframework/aa569283.aspx

11. Абрамян М.Э. Электронный задачник Programming Taskbook: опыт разработки и применения. II Международная научно-практическая конференция «Современные информационные технологии и ИТ-образование». Сборник трудов. Москва, 2006 г., с.194-199.

. Сайт проекта PаscalABC.NET - http://pascalabc.net

pascalabc компилятор модуль семантический


ПРИЛОЖЕНИЕ 1. Грамматика языка PascalABC.NET

"Start Symbol"   = <parse_goal>

{Ident Letter}  = {Letter} + [_]

{All ASCII}   = {#1..#256}+{Cyrillic}+{#8100..#8800}

{TextPart}   = {All ASCII} - {Control Codes}

{String Char}  = {TextPart} - ['']

{CommentPartSlashes} = {All ASCII} - {LF}

{CommentPart1}  = {All ASCII} - [}]

{CommentPart2}  = {All ASCII}

{Hex Digit}   = {Number} + [abcdefABCDEF]   = ({Whitespace}+ | (('//' {CommentPartSlashes}* {LF}) | ('{' {CommentPart1}* '}' )))   = ({Number}+'.'{Number}+) | (({Number}+'.')? {Number}+ [eE] [+-]? {Number}+)  = ('&')? {Ident Letter} ({Ident Letter}|{Number})*   = {Number}+   = '$'{Hex Digit}+  = '#'{Number}+  = ''({String Char}|'''')*''  = '#'{Ident Letter} ({Ident Letter}|{Number})*  = '@'   = ','   = ':'   = '^'   = '..'   = '.'  = '('  = ')'  = ';'  = '['  = ']'   = ':='  = '+='  = '-='  = '*='   = '/='

tkMinus    = '-'   = '+'

tkSlash    = '/'   = '*'   = '='   = '>'  = '>='   = '<'  = '<='   = '<>'

! Reserved !    = 'or'   = 'xor'   = 'and'   = 'div'   = 'mod'   = 'shl'   = 'shr'   = 'not'    = 'as'    = 'in'    = 'is'   = 'sizeof'   = 'typeof'   = 'where'   = 'array'   = 'begin'   = 'case'   = 'class'   = 'const'  = 'constructor'  = 'destructor'   = 'downto'    = 'do'   = 'else'   = 'end'   = 'except'   = 'file'  = 'finalization'   = 'finally'   = 'for'   = 'foreach'   = 'function'    = 'if'  = 'implementation'  = 'inherited'  = 'initialization'  = 'interface'  = 'procedure'   = 'operator'   = 'property'   = 'raise'   = 'record'   = 'repeat'   = 'set'   = 'try'   = 'type'   = 'then'    = 'to'   = 'until'   = 'uses'   = 'using'   = 'var'   = 'while'   = 'with'   = 'nil'   = 'goto'    = 'of'   = 'label'  = 'dispinterface'   = 'program'   = 'packed'   = 'inline'   = 'exports'  = 'resourcestring'  = 'threadvar'

! Non Reserved !    = 'at'    = 'on'    = 'bf'   = 'contains'   = 'library'   = 'out'   = 'package'   = 'requires'   = 'unit'   = 'shortint'   = 'smallint'  = 'integer'   = 'byte'   = 'longint'   = 'int64'   = 'word'   = 'boolean'   = 'char'   = 'widechar'   = 'longword'   = 'pchar'   = 'cardinal'   = 'real'   = 'single'   = 'double'   = 'extended'   = 'currency'   = 'comp'   = 'variant'  = 'olevariant'   = 'params'   = 'object'   = 'static'   = 'abstract'   = 'forward'   = 'overload'   = 'override'   = 'virtual'   = 'absolute'  = 'assembler'  = 'automated'   = 'default'   = 'external'   = 'index'   = 'name'   = 'private'  = 'protected'   = 'public'   = 'internal'   = 'read'  = 'reintroduce'   = 'resident'   = 'stored'   = 'write'   = 'readonly'   = 'writeonly'

! ======================================= Rules

<parse_goal>

::= <program_file>

| <unit_file>

<opt_head_compiler_directives>

::=

| <head_compiler_directives>

<head_compiler_directives>

::= <one_compiler_directive>

| <head_compiler_directives> <one_compiler_directive>

<one_compiler_directive>

::= tkDirectiveName tkIdentifier

| tkDirectiveName tkStringLiteral

<program_file>

::= <program_heading> <opt_head_compiler_directives> <main_uses_clause> <using_clause> <program_block> tkPoint

<program_heading>

::=

| tkProgram <program_name> <program_heading_2>

<program_heading_2>

::= tkSemiColon

| tkRoundOpen <program_param_list> tkRoundClose tkSemiColon

<program_name>

::= tkIdentifier

<program_param_list>

::= <program_param>

| <program_param_list> tkComma <program_param>

<program_param>

::= tkIdentifier

<program_block>

::= <program_decl_sect_list> <compound_stmt>

<program_decl_sect_list>

::= <impl_decl_sect_list>

<uses_clause>

::= <main_uses_clause>

<using_clause>

::=

| <using_list>

<using_list>

::= <using_one>

| <using_list> <using_one>

<using_one>

::=tkUsing <ident_or_keyword_pointseparator_list> tkSemiColon

<ident_or_keyword_pointseparator_list>

::= <identifier_or_keyword>

| <ident_or_keyword_pointseparator_list> tkPoint <identifier_or_keyword>

<main_uses_clause>

::=

| tkUses <main_used_units_list> tkSemiColon

<main_used_units_list>

::= <main_used_units_list> tkComma <main_used_unit_name>

| <main_used_unit_name>

<main_used_unit_name>

::= <ident_or_keyword_pointseparator_list>

| <ident_or_keyword_pointseparator_list> tkIn tkStringLiteral

<library_file>

::= <library_heading> <main_uses_clause> <library_block> tkPoint

<library_heading>

::= tkLibrary tkIdentifier tkSemiColon

<library_block>

::= <library_impl_decl_sect_list> <compound_stmt>

<library_impl_decl_sect_list>

::=

| <library_impl_decl_sect_list> <library_impl_decl_sect>

<library_impl_decl_sect>

::= <label_decl_sect>

| <const_decl_sect>

| <res_str_decl_sect>

| <type_decl_sect>

| <var_decl_sect>

| <proc_decl>

| <func_decl>

| <constructor_decl>

| <destructor_decl>

| <export_clause>

<export_clause>

::= tkExports <exports_list> tkSemiColon

<exports_list>

::= <exports_entry>

| <exports_list> tkComma <exports_entry>

<exports_entry>

::= <identifier> <exports_index> <exports_name> <exports_resident>

<exports_index>

::=

| tkIndex <integer_const>

<exports_name>

::=

| tkName <identifier>

| tkName <literal>

<exports_resident>

::=

| tkResident

<unit_file>

::= <unit_heading> <interface_part> <implementation_part> <initialization_part> tkPoint

| <unit_heading> <abc_interface_part> <initialization_part> tkPoint

<unit_heading>

::= tkUnit <unit_name> tkSemiColon <opt_head_compiler_directives>

<unit_name>

::= tkIdentifier

<interface_part>

::= tkInterface <uses_clause> <using_clause> <int_decl_sect_list>

<implementation_part>

::= tkImplementation <uses_clause> <using_clause> <impl_decl_sect_list>

<abc_interface_part>

::=<uses_clause> <using_clause> <impl_decl_sect_list>

<initialization_part>

::= tkEnd

| tkInitialization <stmt_list> tkEnd

| tkInitialization <stmt_list> tkFinalization <stmt_list> tkEnd

| tkBegin <stmt_list> tkEnd

<package_file>

::= tkPackage <package_name> tkSemiColon <requires_clause> <contains_clause> tkEnd tkPoint

<package_name>

::= <identifier>

<requires_clause>

::=

| tkRequires

| tkRequires <main_used_units_list> tkSemiColon

<contains_clause>

::=

| tkContains

| tkContains <main_used_units_list> tkSemiColon

<int_decl_sect_list>

::= <int_decl_sect_list1>

<int_decl_sect_list1>

::=

| <int_decl_sect_list1> <int_decl_sect>

<impl_decl_sect_list>

::=<impl_decl_sect_list1>

<impl_decl_sect_list1>

::=

| <impl_decl_sect_list1> <impl_decl_sect>

<abc_decl_sect_list>

::=<abc_decl_sect_list1>

<abc_decl_sect_list1>

::=

| <abc_decl_sect_list1> <abc_decl_sect>

<int_decl_sect>

::= <const_decl_sect>

| <res_str_decl_sect>

| <type_decl_sect>

| <var_decl_sect>

| <int_proc_heading>

| <int_func_heading>

<impl_decl_sect>

::= <label_decl_sect>

| <const_decl_sect>

| <res_str_decl_sect>

| <type_decl_sect>

| <var_decl_sect>

| <proc_decl>

| <func_decl>

| <constructor_decl>

| <destructor_decl>

<abc_decl_sect>

::= <label_decl_sect>

| <const_decl_sect>

| <res_str_decl_sect>

| <type_decl_sect>

| <var_decl_sect>

<int_proc_heading>

::= <proc_heading>

| <proc_heading> tkForward tkSemiColon

<int_func_heading>

::= <func_heading>

| <func_heading> tkForward tkSemiColon

<label_decl_sect>

::= tkLabel <label_list> tkSemiColon

<label_list>

::= <label_name>

| <label_list> tkComma <label_name>

<label_name>

::= tkInteger

| tkFloat

| <identifier>

<const_decl_sect>

::= tkConst <const_decl>

| <const_decl_sect> <const_decl>

<res_str_decl_sect>

::= tkResourceString <const_decl>

| <res_str_decl_sect> <const_decl>

<type_decl_sect>

::= tkType <type_decl>

| <type_decl_sect> <type_decl>

<var_decl_sect>

::= tkVar <var_decl>

| tkThreadvar <var_decl>

| <var_decl_sect> <var_decl>

<const_decl>

::= <only_const_decl> tkSemiColon

<only_const_decl>

::= <const_name> tkEqual <const_expr>

| <const_name> tkColon <type_ref> tkEqual <typed_const>

<const_name>

::= <identifier>

<const_expr>

::= <const_simple_expr>

| <const_simple_expr> <const_relop> <const_simple_expr>

<const_relop>

::= tkEqual

| tkNotEqual

| tkLower

| tkGreater

| tkLowerEqual

| tkGreaterEqual

| tkIn

<const_simple_expr>

::= <const_term>

| <const_simple_expr> <const_addop> <const_term>

<const_addop>

::= tkPlus

| tkMinus

| tkOr

| tkXor

<const_term>

::= <const_factor>

| <const_term> <const_mulop> <const_factor>

<const_mulop>

::= tkStar

| tkSlash

| tkDiv

| tkMod

| tkShl

| tkShr

| tkAnd

<const_factor>

::= <const_variable>

| <const_set>

| <unsigned_number>

| <literal>

| tkNil

| tkAddressOf <const_factor>

| tkRoundOpen <const_expr> tkRoundClose

| tkNot <const_factor>

| <sign> <const_factor>

| tkDeref <const_factor>

<const_set>

::= tkSquareOpen <const_elem_list> tkSquareClose

<sign>

::= tkPlus

| tkMinus

<const_variable>

::= <identifier>

| <const_variable> <const_variable_2>

<const_variable_2>

::= tkPoint <identifier_or_keyword>

| tkDeref

| tkRoundOpen <const_func_expr_list> tkRoundClose

<const_func_expr_list>

::= <const_expr>

| <const_func_expr_list> tkComma <const_expr>

<const_elem_list>

::= <const_elem_list1>

|

<const_elem_list1>

::= <const_elem>

| <const_elem_list1> tkComma <const_elem>

<const_elem>

::= <const_expr>

| <const_expr> tkDotDot <const_expr>

<unsigned_number>

::= tkInteger

| tkHex

| tkFloat

<typed_const>

::= <const_expr>

| <array_const>

| <record_const>

<array_const>

::= tkRoundOpen <typed_const_list> tkRoundClose

| tkRoundOpen <record_const> tkRoundClose

| tkRoundOpen <array_const> tkRoundClose

<typed_const_list>

::=

| <typed_const> tkComma <typed_const>

| <typed_const_list> tkComma <typed_const>

<record_const>

::= tkRoundOpen <const_field_list> tkRoundClose

<const_field_list>

::= <const_field_list_1>

| <const_field_list_1> tkSemiColon

<const_field_list_1>

::= <const_field>

| <const_field_list_1> tkSemiColon <const_field>

<const_field>

::= <const_field_name> tkColon <typed_const>

<const_field_name>

::= <identifier>

<type_decl>

::= <identifier> tkEqual <type_decl_type> tkSemiColon

<type_decl_type>

::= <type_ref>

| tkType <type_ref>

| <object_type>

<type_ref>

::= <simple_type>

| <string_type>

| <pointer_type>

| <structured_type>

| <procedural_type>

| <template_type>

<template_type>

::= <simple_type_identifier> <template_type_params>

<template_type_params>

::= tkLower <template_param_list> tkGreater

<template_param_list>

::= <template_param>

| <template_param_list> tkComma <template_param>

<template_param>

::= <simple_type_identifier>

| <template_type>

<simple_type>

::= <simple_type_identifier>

| <range_expr> tkDotDot <range_expr>

| tkRoundOpen <enumeration_id_list> tkRoundClose

<range_expr>

::= <range_term>

| <range_expr> <const_addop> <range_term>

<range_term>

::= <range_factor>

| <range_term> <const_mulop> <range_factor>

<range_factor>

::= <simple_type_identifier>

| <unsigned_number>

| <sign> <range_factor>

| <literal>

| <range_factor> tkRoundOpen <const_elem_list> tkRoundClose

| tkRoundOpen <const_expr> tkRoundClose

<range_methodname>

::= <identifier>

| <identifier> tkPoint <identifier_or_keyword>

<simple_type_identifier>

::= <identifier>

| <simple_type_identifier> tkPoint <identifier_or_keyword>

<enumeration_id_list>

::= <enumeration_id> tkComma <enumeration_id>

| <enumeration_id_list> tkComma <enumeration_id>

<enumeration_id>

::= <identifier>

<pointer_type>

::= tkDeref <fptype>

<structured_type>

::= <unpacked_structured_type>

| tkPacked <unpacked_structured_type>

<unpacked_structured_type>

::= <array_type>

| <new_record_type>

| <set_type>

| <file_type>

<array_type>

::= tkArray tkSquareOpen <simple_type_list> tkSquareClose tkOf <type_ref>

| tkArray tkOf <type_ref>

<simple_type_list>

::= <simple_type>

| <simple_type_list> tkComma <simple_type>

<record_type>

::= tkRecord <field_list> tkEnd

| tkRecord tkEnd

<field_list>

::= <fixed_part>

| <variant_part>

| <fixed_part_2> tkSemiColon <variant_part>

<fixed_part>

::= <fixed_part_2>

| <fixed_part_2> tkSemiColon

<fixed_part_2>

::= <record_section>

| <fixed_part_2> tkSemiColon <record_section>

<record_section>

::= <record_section_id_list> tkColon <type_ref>

<record_section_id_list>

::= <record_section_id>

| <record_section_id_list> tkComma <record_section_id>

<record_section_id>

::= <identifier>

<variant_part>

::= tkCase <tag_field> tkOf <variant_list>

<tag_field>

::= <tag_field_name>

| <tag_field_name> tkColon <tag_field_typename>

<tag_field_name>

::= <identifier>

<tag_field_typename>

::= <fptype>

<variant_list>

::= <variant_list_2>

| <variant_list_2> tkSemiColon

<variant_list_2>

::= <variant>

| <variant_list_2> tkSemiColon <variant>

<variant>

::= <case_tag_list> tkColon tkRoundOpen <variant_field_list> tkRoundClose

<variant_field_list>

::=

| <field_list>

<case_tag_list>

::= <const_expr_list>

<const_expr_list>

::= <const_expr>

| <const_expr_list> tkComma <const_expr>

<set_type>

::= tkSet tkOf <simple_type>

<file_type>

::= tkFile tkOf <type_ref>

| tkFile

<string_type>

::= tkIdentifier tkSquareOpen <const_expr> tkSquareClose

<procedural_type>

::= <procedural_type_kind>

<procedural_type_kind>

::= <procedural_type_decl>

| <procedural_type_decl> tkOf <identifier>

<procedural_type_decl>

::= tkProcedure <fp_list> <maybe_error>

| tkFunction <fp_list> tkColon <fptype>

<maybe_error>

::= tkColon <fptype>

|

<object_type>

::= <new_object_type>

<oot_privat_list>

::=

| tkPrivate <oot_component_list>

<oot_component_list>

::=

| <oot_field_list>

| <oot_field_list> <oot_method_list>

| <oot_method_list>

<oot_successor>

::= tkRoundOpen <oot_typeidentifier> tkRoundClose

<oot_typeidentifier>

::= <identifier>

<oot_field_list>

::= <oot_field>

| <oot_field_list> <oot_field>

<oot_field>

::= <oot_id_list> tkColon <type_ref> tkSemiColon

<oot_id_list>

::= <oot_field_identifier>

| <oot_id_list> tkComma <oot_field_identifier>

<oot_field_identifier>

::= <identifier>

<oot_method_list>

::= <oot_method>

| <oot_method_list> <oot_method>

<oot_method>

::= <oot_method_head>

<oot_method_head>

::= <proc_heading>

| <func_heading>

| <oot_constructor_head>

| <oot_destructor_head>

<oot_constructor_head>

::= tkConstructor <proc_name> <fp_list> <opt_meth_modificators>

<oot_destructor_head>

::= tkDestructor <proc_name> <fp_list> <opt_meth_modificators>

<new_object_type>

::= <not_class_reference_type>

| <not_object_type>

<not_class_reference_type>

::= tkClass tkOf <not_object_type_identifier>

<not_object_type_identifier>

::= <identifier>

<not_object_type>

::= <class_or_interface_keyword> <opt_template_arguments> <opt_base_classes> <opt_where_section> <opt_not_component_list_seq_end>

<new_record_type>

::= tkRecord <opt_base_classes> <record_component_list> tkEnd

<class_or_interface_keyword>

::= tkClass

| tkInterface

<opt_not_component_list_seq_end>

::=

| <not_component_list_seq> tkEnd

<opt_base_classes>

::=

| tkRoundOpen <base_classes_names_list> tkRoundClose

<base_classes_names_list>

::= <base_class_name>

| <base_classes_names_list> tkComma <base_class_name>

<base_class_name>

::= <simple_type_identifier>

| <template_type>

<opt_template_arguments>

::=

| tkLower <ident_list> tkGreater

<opt_where_section>

::=

| tkWhere <where_part_list>

<where_part_list>

::= <where_part>

| <where_part_list> <where_part>

<where_part>

::= <ident_list> tkColon <type_ref_list> <opt_constructor_secific> tkSemiColon

<type_ref_list>

::= <type_ref>

| <type_ref_list> tkComma <type_ref>

<opt_constructor_secific>

::=

| tkComma tkConstructor <opt_constructor_specific_params>

<opt_constructor_specific_params>

::=

| tkRoundOpen <opt_type_ref_list> tkRoundClose

<opt_type_ref_list>

::=

| <type_ref_list>

<record_component_list>

::= <not_component_list>

<not_component_list_seq>

::= <not_component_list>

| <not_component_list_seq> <ot_visibility_specifier> <not_component_list>

<ot_visibility_specifier>

::= tkInternal

| tkPublic

| tkProtected

| tkPrivate

<not_object_type_identifier_list>

::= <simple_type_identifier>

| <not_object_type_identifier_list> tkComma <simple_type_identifier>

<ident_list>

::= <identifier>

| <ident_list> tkComma <identifier>

<not_component_list>

::= <not_guid>

| <not_guid> <not_component_list_1> <opt_semicolon>

| <not_guid> <not_component_list_2>

| <not_guid> <not_component_list_1> tkSemiColon <not_component_list_2>

<opt_semicolon>

::=

| tkSemiColon

<not_guid>

::=

<not_component_list_1>

::= <filed_or_const_definition>

| <not_component_list_1> tkSemiColon <filed_or_const_definition>

<not_component_list_2>

::= <not_method_definition>

| <not_property_definition>

| <not_component_list_2> <not_method_definition>

| <not_component_list_2> <not_property_definition>

<filed_or_const_definition>

::= tkConst <only_const_decl>

| <not_field_definition>

<not_field_definition>

::= <not_field_identifier_list> tkColon <type_ref>

<not_field_identifier_list>

::= <not_field_identifier>

| <not_field_identifier_list> tkComma <not_field_identifier>

<not_field_identifier>

::= <identifier>

<not_method_definition>

::= <not_method_heading>

| <abc_method_decl>

<abc_method_decl>

::= <abc_proc_decl>

| <abc_func_decl>

| <abc_constructor_decl>

| <abc_destructor_decl>

<not_method_heading>

::= tkClass <proc_heading>

| tkClass <func_heading>

| <func_heading>

| <proc_heading>

| <not_constructor_heading>

| <not_destructor_heading>

<optional_qualified_identifier>

::= <qualified_identifier>

|

<not_constructor_heading>

::= tkConstructor <optional_qualified_identifier> <fp_list> <opt_meth_modificators>

<not_destructor_heading>

::= tkDestructor <optional_qualified_identifier> <fp_list> <opt_meth_modificators>

<qualified_identifier>

::= <identifier>

| <visibility_specifier>

| <qualified_identifier> tkPoint <identifier>

| <qualified_identifier> tkPoint <visibility_specifier>

<not_property_definition>

::= tkProperty <qualified_identifier> <not_property_interface> <not_property_specifiers> tkSemiColon <not_array_defaultproperty>

<not_array_defaultproperty>

::=

| tkDefault tkSemiColon

<not_property_interface>

::=

| <not_property_parameter_list> tkColon <fptype> <not_property_interface_index>

<not_property_interface_index>

::=

| tkIndex <expr>

<not_property_parameter_list>

::=

| tkSquareOpen <not_parameter_decl_list> tkSquareClose

<not_parameter_decl_list>

::= <not_parameter_decl>

| <not_parameter_decl_list> tkSemiColon <not_parameter_decl>

<not_parameter_decl>

::= <not_parameter_name_list> tkColon <fptype>

| tkConst <not_parameter_name_list> tkColon <fptype>

| tkVar <not_parameter_name_list> tkColon <fptype>

| tkOut <not_parameter_name_list> tkColon <fptype>

<not_parameter_name_list>

::= <ident_list>

<not_property_specifiers>

::=

| tkReadOnly <not_property_specifiers>

| tkWriteOnly <not_property_specifiers>

| tkDispid <const_expr> <not_property_specifiers>

| tkDefault <const_expr> <not_property_specifiers>

| tkNodefault <not_property_specifiers>

| tkStored <const_expr> <not_property_specifiers>

| tkRead <identifier> <not_property_specifiers>

| tkWrite <identifier> <not_property_specifiers>

<var_decl>

::= <var_decl_part> tkSemiColon

<var_decl_part>

::= <var_decl_part_normal>

| <var_name_list> tkColon <type_ref> tkEqual <var_init_value>

| <var_name_list> tkEqual <var_init_value>

<var_decl_part_normal>

::= <var_name_list> tkColon <type_ref>

<var_init_value>

::= <typed_const>

<var_name_list>

::= <var_name>

| <var_name_list> tkComma <var_name>

<var_name>

::= <identifier>

<declared_var_name>

::= <identifier>

<constructor_decl>

::= <not_constructor_heading> <not_constructor_block_decl>

<abc_constructor_decl>

::= <not_constructor_heading> <abc_block>

<destructor_decl>

::= <not_destructor_heading> <not_constructor_block_decl>

<abc_destructor_decl>

::= <not_destructor_heading> <abc_block>

<not_constructor_block_decl>

::= <block>

| <external_directr>

| <asm_block>

<proc_decl>

::= <proc_decl_noclass>

| tkClass <proc_decl_noclass>

<proc_decl_noclass>

::= <proc_heading> <proc_block>

<abc_proc_decl>

::= <abc_proc_decl_noclass>

| tkClass <abc_proc_decl_noclass>

<abc_proc_decl_noclass>

::= <proc_heading> <abc_proc_block>

<func_decl>

::= <func_decl_noclass>

| tkClass <func_decl_noclass>

<func_decl_noclass>

::= <func_heading> <func_block>

<abc_func_decl>

::= <abc_func_decl_noclass>

| tkClass <abc_func_decl_noclass>

<abc_func_decl_noclass>

::= <func_heading> <abc_proc_block>

<proc_heading>

::= tkProcedure <proc_name> <fp_list> <maybe_error> <opt_meth_modificators>

| tkProcedure <proc_name> tkEqual <qualified_identifier> tkSemiColon

<proc_name>

::= <func_name>

<func_name>

::= <func_meth_name_ident>

| <func_class_name_ident> tkPoint <func_meth_name_ident>

<func_meth_name_ident>

::= <identifier>

| <visibility_specifier>

| <operator_name_ident>

<func_class_name_ident>

::= <identifier>

| <visibility_specifier>

<func_heading>

::= tkFunction <func_name> <fp_list> tkColon <fptype> <opt_meth_modificators>

| tkFunction <func_name> <opt_meth_modificators>

| tkFunction <func_name> tkEqual <qualified_identifier> tkSemiColon

<proc_block>

::= <proc_block_decl>

<func_block>

::= <proc_block_decl>

<proc_block_decl>

::= <block>

| <external_directr>

| <asm_block>

| tkForward tkSemiColon

<abc_proc_block>

::= <abc_block>

| <external_directr>

<external_directr>

::= <abc_external_directr>

| <abc_external_directr> tkSemiColon

<external_directr_ident>

::= <identifier>

| <literal>

<abc_external_directr>

::= tkExternal <external_directr_ident> tkName <external_directr_ident>

<asm_block>

::= <impl_decl_sect_list> tkAsmBody tkSemiColon

<block>

::= <impl_decl_sect_list> <compound_stmt> tkSemiColon

<abc_block>

::= <abc_decl_sect_list> <compound_stmt> tkSemiColon

<fp_list>

::=

| tkRoundOpen <fp_sect_list> tkRoundClose

<fp_sect_list>

::=

| <fp_sect>

| <fp_sect_list> tkSemiColon <fp_sect>

<fp_sect>

::= <param_name_list> tkColon <fptype_new>

| <param_name_list>

| tkVar <param_name_list> tkColon <fptype_new>

| tkVar <param_name_list>

| tkOut <param_name_list> tkColon <fptype_new>

| tkOut <param_name_list>

| tkConst <param_name_list> tkColon <fptype_new>

| tkConst <param_name_list>

| tkParams <param_name_list> tkColon <fptype_new>

| tkParams <param_name_list>

| <param_name_list> tkColon <fptype> tkEqual <const_expr>

| tkVar <param_name_list> tkColon <fptype> tkEqual <const_expr>

| tkOut <param_name_list> tkColon <fptype> tkEqual <const_expr>

| tkConst <param_name_list> tkColon <fptype> tkEqual <const_expr>

<param_name_list>

::= <param_name>

| <param_name_list> tkComma <param_name>

<param_name>

::= <identifier>

<fptype>

::= <type_ref>

<fptype_new>

::= <type_ref>

| tkArray tkOf tkConst

<stmt>

::= <unlabelled_stmt>

| <label_name> tkColon <unlabelled_stmt>

<unlabelled_stmt>

::=

| <assignment>

| <proc_call>

| <goto_stmt>

| <compound_stmt>

| <if_stmt>

| <case_stmt>

| <repeat_stmt>

| <while_stmt>

| <for_stmt>

| <with_stmt>

| <asm_stmt>

| <inherited_message>

| <try_stmt>

| <raise_stmt>

| <foreach_stmt>

| <var_stmt>

<var_stmt>

::= tkVar <var_decl_part_in_stmt>

<var_decl_part_in_stmt>

::= <var_decl_part_normal>

| <var_name_list> tkColon <type_ref> tkEqual <expr>

| <var_name_list> tkAssign <expr>

<assignment>

::= <var_reference> <assign_operator> <expr>

<proc_call>

::= <var_reference>

<goto_stmt>

::= tkGoto <label_name>

<compound_stmt>

::= tkBegin <stmt_list> tkEnd

<stmt_list>

::= <stmt>

| <stmt_list> tkSemiColon <stmt>

<if_stmt>

::= tkIf <expr> <if_then_else_branch>

<if_then_else_branch>

::= tkThen <then_branch>

| tkThen <then_branch> tkElse <else_branch>

<then_branch>

::= <stmt>

<else_branch>

::= <stmt>

<case_stmt>

::= tkCase <expr> tkOf <case_list> <else_case> tkEnd

<case_list>

::= <case_item>

| <case_list> tkSemiColon <case_item>

<case_item>

::=

| <case_label_list> tkColon <stmt>

<case_label_list>

::= <case_label>

| <case_label_list> tkComma <case_label>

<case_label>

::= <const_elem>

<else_case>

::=

| tkElse <stmt_list>

<repeat_stmt>

::= tkRepeat <stmt_list> tkUntil <expr>

<while_stmt>

::= tkWhile <expr> tkDo <stmt>

<foreach_stmt>

::= tkForeach <identifier> tkColon <type_ref> tkIn <expr> tkDo <stmt>

<for_stmt>

::= tkFor <identifier> tkAssign <expr> <for_cycle_type> <expr> tkDo <stmt>

<for_cycle_type>

::= tkTo

| tkDownto

<with_stmt>

::= tkWith <expr_list> tkDo <stmt>

<inherited_message>

::= tkInherited

<try_stmt>

::= tkTry <stmt_list> <try_handler>

<try_handler>

::= tkFinally <stmt_list> tkEnd

| tkExcept <exception_block> tkEnd

<exception_block>

::= <exception_handler_list> <exception_block_else_branch>

| <exception_handler_list> tkSemiColon <exception_block_else_branch>

| <stmt_list>

<exception_handler_list>

::= <exception_handler>

| <exception_handler_list> tkSemiColon <exception_handler>

<exception_block_else_branch>

::=

| tkElse <stmt_list>

<exception_handler>

::= tkOn <exception_identifier> tkDo <stmt>

<exception_identifier>

::= <exception_class_type_identifier>

| <exception_variable> tkColon <exception_class_type_identifier>

<exception_class_type_identifier>

::= <simple_type_identifier>

<exception_variable>

::= <identifier>

<raise_stmt>

::= tkRaise

| tkRaise <expr>

| tkRaise <expr> tkAt <expr>

<asm_stmt>

::= tkAsmBody

<expr_list>

::= <expr>

| <expr_list> tkComma <expr>

<expr>

::= <relop_expr>

| <new_expr>

<sizeof_expr>

::= tkSizeOf tkRoundOpen <simple_type_identifier> tkRoundClose

<typeof_expr>

::= tkTypeOf tkRoundOpen <simple_type_identifier> tkRoundClose

<new_expr>

::= <identifier> <simple_type_identifier> <opt_template_type_params>

<opt_expr_list_with_bracket>

<opt_template_type_params>

::=

| <template_type_params>

<opt_expr_list_with_bracket>

::=

| tkRoundOpen <opt_expr_list> tkRoundClose

<relop_expr>

::= <simple_expr>

| <simple_expr> <relop> <relop_expr>

| <simple_expr> tkColon <simple_expr>

| <simple_expr> tkColon <simple_expr> tkColon <simple_expr>

<relop>

::= tkEqual

| tkNotEqual

| tkLower

| tkGreater

| tkLowerEqual

| tkGreaterEqual

| tkIn

<simple_expr>

::= <term>

| <simple_expr> <addop> <term>

<addop>

::= tkPlus

| tkMinus

| tkOr

| tkXor

<typecast_op>

::= tkAs

| tkIs

<term>

::= <factor>

| <term> <mulop> <factor>

| <term> <typecast_op> <simple_type_identifier>

<mulop>

::= tkStar

| tkSlash

| tkDiv

| tkMod

| tkShl

| tkShr

| tkAnd

<factor>

::= tkNil

| <literal_or_number>

| tkSquareOpen <elem_list> tkSquareClose

| tkNot <factor>

| <sign> <factor>

| tkDeref <factor>

| <var_reference>

<literal_or_number>

::= <literal>

| <unsigned_number>

<var_reference>

::= <var_address> <variable>

| <variable>

<var_address>

::= tkAddressOf

| <var_address> tkAddressOf

<variable>

::= <identifier>

| <operator_name_ident>

| tkInherited <identifier>

| tkRoundOpen <expr> tkRoundClose

| <sizeof_expr>

| <typeof_expr>

| tkRoundOpen tkRoundClose

| <literal_or_number> tkPoint <identifier_or_keyword>

| <variable> <var_specifiers>

<opt_expr_list>

::= <expr_list>

|

<var_specifiers>

::= tkSquareOpen <expr_list> tkSquareClose

| tkSquareOpen tkSquareClose

| tkRoundOpen <opt_expr_list> tkRoundClose

| tkPoint <identifier_keyword_operatorname>

| tkDeref

<template_type_back_varspecifiers>

::= tkRoundOpen <expr_list> tkRoundClose

| tkRoundOpen tkRoundClose

<elem_list>

::= <elem_list1>

|

<elem_list1>

::= <elem>

| <elem_list1> tkComma <elem>

<elem>

::= <expr>

| <expr> tkDotDot <expr>

<one_literal>

::= tkStringLiteral

| tkAsciiChar

<literal>

::=<literal_list>

<literal_list>

::= <one_literal>

| <literal_list> <one_literal>

<operator_name_ident>

::= tkOperator <overload_operator>

<opt_meth_modificators>

::= tkSemiColon

| tkSemiColon <meth_modificators> tkSemiColon

<meth_modificators>

::= <meth_modificator>

| <meth_modificators> tkSemiColon <meth_modificator>

<integer_const>

::= <sign> tkInteger

| tkInteger

| <sign> tkHex

| tkHex

| <identifier>

| <sign> <identifier>

<identifier>

::= tkIdentifier

| <real_type_name>

| <ord_type_name>

| <variant_type_name>

| <meth_modificator>

| <property_specifier_directives>

| <non_reserved>

| <other>

<identifier_or_keyword>

::= <identifier>

| <keyword>

| <reserved_keyword>

<identifier_keyword_operatorname>

::= <identifier>

| <keyword>

| <operator_name_ident>

<real_type_name>

::= tkReal

| tkSingle

| tkDouble

| tkExtended

| tkCurrency

| tkComp

<ord_type_name>

::= tkShortInt

| tkSmallInt

| tkOrdInteger

| tkByte

| tkLongInt

| tkInt64

| tkWord

| tkBoolean

| tkChar

| tkWideChar

| tkLongWord

| tkPChar

| tkCardinal

<variant_type_name>

::= tkVariant

| tkOleVariant

<meth_modificator>

::= tkAbstract

| tkOverload

| tkOverride

| tkVirtual

| tkStatic

<property_specifier_directives>

::= tkDefault

| tkRead

| tkWrite

| tkStored

| tkNodefault

| tkImplements

| tkWriteOnly

| tkReadOnly

| tkDispid

<non_reserved>

::= tkAt

| tkAbsolute

| tkOn

| tkName

| tkIndex

| tkMessage

| tkContains

| tkRequires

| tkForward

| tkOut

| tkObject

<visibility_specifier>

::= tkInternal

| tkPublic

| tkProtected

| tkPrivate

<other>

::= tkPackage

| tkUnit

| tkLibrary

| tkExternal

| tkBF

| tkParams

<keyword>

::= <visibility_specifier>

| tkOr

| tkTypeOf

| tkSizeOf

| tkWhere

| tkXor

| tkAnd

| tkDiv

| tkMod

| tkShl

| tkShr

| tkNot

| tkAs

| tkIn

| tkIs

| tkArray

| tkBegin

| tkCase

| tkClass

| tkConst

| tkConstructor

| tkDestructor

| tkDownto

| tkDo

| tkElse

| tkEnd

| tkExcept

| tkFile

| tkFinalization

| tkFinally

| tkFor

| tkFunction

| tkIf

| tkImplementation

| tkInherited

| tkInitialization

| tkInterface

| tkProcedure

| tkProperty

| tkRaise

| tkRecord

| tkRepeat

| tkSet

| tkTry

| tkType

| tkThen

| tkTo

| tkUntil

| tkUses

| tkUsing

| tkVar

| tkWhile

| tkWith

| tkNil

| tkGoto

| tkOf

| tkLabel

| tkProgram

<reserved_keyword>

::= tkOperator

<overload_operator>

::= tkMinus

| tkPlus

| tkSquareOpen tkSquareClose

| tkRoundOpen tkRoundClose

| tkSlash

| tkStar

| tkEqual

| tkGreater

| tkGreaterEqual

| tkLower

| tkLowerEqual

| tkNotEqual

| tkOr

| tkXor

| tkAnd

| tkDiv

| tkMod

| tkShl

| tkShr

| tkNot

| tkIn

| tkAddressOf

| tkDeref

| <assign_operator>

<assign_operator>

::= tkAssign

| tkPlusEqual

| tkMinusEqual

| tkMultEqual

| tkDivEqual


ПРИЛОЖЕНИЕ 2. Классы синтаксического дерева

Классы отображены в следующем формате:

имя_класса->базовый_класс

тип_поля1 поле1

тип_поля2 поле2

. . .

syntax_tree_node

SourceContext source_context_list->statementsubnodes>syntax_tree_node>statement_value tofrom_expr->addressed_valueleftright_type operation_type_expr->addressed_valuesubnode_type operation_type_node->addressed_value_const->const_nodeval_const->const_nodeval_const->const_nodeval>syntax_tree_node_body->syntax_tree_node_list subprogram_code_definitions subprogram_defs>addressed_value_funcnamename_const->const_nodeval_value->expression_definition->syntax_tree_node_type_reference->type_definitionunit_nametype_name_definitions->subprogram_definition_def_statementArrayList var_definitions_list->syntax_tree_nodeidents_def_statement->subprogram_definition_list vars_definition vars_typeinital_value_definition->type_definition_definitions->syntax_tree_node_definitionArrayList defs_tree->syntax_tree_node_unitArrayList compilation_units_name->syntax_tree_nodeprog_name_const->literalValue_list->expressionexpressions>addressed_value_funcname_value dereferencing_value_dereference->dereference>dereference_list indexes_node->statementloop_variableinitial_valuefinish_valuestatements_cycle_type cycle_typeincrement_value_node->statementstatementsexpr_node->statementexprstatements_node->statementconditionthen_bodyelse_body_type->type_definition_definition pointed_to>type_definitionleftright_types->type_definition_definitionArrayList indexers_type->type_definition_types indexers_definition elemets_types_definitions->subprogram_definition_list labels_attribute->ident_attribute attribute_type_parametres->syntax_tree_node_list idents_definition vars_type_kind param_kindinital_value_parametres->syntax_tree_node_parametresArrayList params_list_attributes_list->syntax_tree_node_attributeArrayList proc_attributes_header->subprogram_definition_parametres parametres_attributes_list proc_attributes_name nameof_objectclass_keyword_header->procedure_header_definition return_type_definition->subprogram_definition_header proc_header_block proc_body_declaration->subprogram_definitiontype_name_definition type_def_declarations->subprogram_definition_declarationArrayList types_decl_const_definition->const_definition_const_definition->const_definition_definition const_type_definition->syntax_tree_nodeconst_nameconst_value_definitions_list->subprogram_definition_definitionArrayList const_defs_name->syntax_tree_nodeidunit_name_unit->syntax_tree_nodeunit_name_unit_in->uses_unit_const in_file_list->syntax_tree_node_unitArrayList units_body->syntax_tree_node_list used_units_definitions program_definitions_list program_code_list using_list_unit->syntax_tree_nodefile_name_module->compilation_unit_name unit_name_node interface_part_node implementation_part_list initialization_part_list finalization_part_module->compilation_unit_name program_name_list used_unitsprogram_block_list using_namespaces_constant->long_const_address->addressed_value_funcname_value address_of_variant->statement_list conditionsexec_if_true_node->statementparam_variants conditionselse_statement_name->syntax_tree_nodeclass_namemeth_name_node->addressed_value_funcname_value left_value right_statement->statement_statement->statementlabel_statement->statementlabel_nameto_statement_statement->statementwhat_do_list do_with_call->dereference_list parametres_set_constant->expression_list values_const->expression_list elements_accessor_name->syntax_tree_nodeaccessor_name_accessor_name->syntax_tree_nodeaccessor_name_accessors->syntax_tree_node_accessor_name read_accessor_accessor_name write_accessor_property->subprogram_definitionproperty_name_definition property_typeindex_expression_accessors accessors_array_default array_default_parameter_list parameter_list_property->simple_property_parametres property_parametres_indexer_property_node is_default_members->syntax_tree_node_definitionArrayList members_modifer_node access_mod_modifer_node->syntax_tree_node_modifer access_level_body->syntax_tree_node_membersArrayList class_def_blocks_definition->type_definition_list class_parents_body body_indexer_property_node->syntax_tree_node_type_definition->type_definition_type tpunit_name_type_definition->type_definition_definition of_type_statement->statement_list statements_exception->syntax_tree_nodeexception_var_nameexception_type_namestat_exception_list->syntax_tree_node_exceptionArrayList on_exceptions_finally_statement->try_statement_list finally_statements_except_statement->try_statement_exception_list on_except_list else_statements_const_definition->statementnameval_const->expression_const_definitionArrayList rec_consts_type->type_definition_type_parts parts_definition base_type_type_definition->type_definition_list values_const->literalcconst_statement->statementexcep_char_const->literalchar_num_const_line->literalliterals_num_definition->type_definitionnum_of_symbols>syntax_tree_node_list vars_definition vars_type_list->syntax_tree_nodevars_type->syntax_tree_node_list case_exprs_type_parts parts_types->syntax_tree_node_typeArrayList vars_record_type->syntax_tree_nodevar_name_definition var_type_types vars_call->statement_value func_name_predefinition->type_declarationclass_name_const->const_node_type_definition->type_definition_definition elem_type>procedure_header>procedure_header_method_call->statementmethod_name_list exprs_node->addressed_value_value left_definition right_node->syntax_tree_node_definitions interface_definitions_list uses_modules_list using_namespaces_node->syntax_tree_node_list uses_modules_definitions implementation_definitions_list using_namespaces_expr->expressionleftright>proc_block_definitions defs_list program_code_block->syntax_tree_node_of_named_type_definition->type_definition_type_reference type_name_of_const_type_definition->type_definition>const_node_variants->syntax_tree_node_variantArrayList variants_expr->expressionleftright_def_list->syntax_tree_node_def_statementArrayList vars_type_parts->syntax_tree_node_def_list fixed_part_record_type variant_part_array_default->syntax_tree_node_interface_parameter_list parameter_list_definition property_typeindex_expression_parameter->syntax_tree_node_list names_definition type_parameter_list->syntax_tree_node_parameterArrayList parameters_ident->ident_expr->expressionexprformat1format2_part_list initialization_sect_list finalization_sect_info->syntax_tree_nodetext_stmt->statementexpraddress_type_node->token_info_type type_type->type_definition_definition file_of_type_type_ident->ident_type type_handler->syntax_tree_nodevariable_type_reference type_namestatements_ident->syntax_tree_nodevariable_type_reference type_name_handler_list->syntax_tree_node_handlerArrayList handlers_block->syntax_tree_node_list stmt_list_handler_list handlers_list else_stmt_list_handler->syntax_tree_node_handler_finally->try_handler_list stmt_list_handler_except->try_handler_block except_block_stmt->statement_list stmt_list_handler handler_message->statement_directive->proc_blockmodulenamename_space->syntax_tree_node_list name_space_parts_list->syntax_tree_node_spaceArrayList namespaces_import_module->uses_unitnew_name_module->compilation_unitfirst_namesecond_name_list import_list_definitions definitions_list module_code_ident_with_export_marker->ident_export_marker marker_exit_stmt->statementtext_stmt->statementexpr_procedure_receiver->syntax_tree_node_kind param_kindreceiver_namereceiver_typename_procedure_header->function_header_procedure_receiver receiverfirst_namesecond_name_withstmt_guardstat->syntax_tree_node_value name_definition type_namestmt_withstmt_guardstat_list->syntax_tree_node_withstmt_guardstatArrayList guardstats_withstmt->statement_withstmt_guardstat_list quardstat_listelse_stmt_stmt->statementstmt_stmt->statementidentifier_definition type_namein_whatstmt_name->method_name_type_node operator_type_value_funcname->addressed_value_type_reference_list->syntax_syntax_tree_node<named_type_reference> types_param_list->dereference<type_definition> params_list_type_reference->named_type_reference_type_reference name_param_list params_list_const->const_nodeval_const->const_nodeval_expr->addressed_value_type_reference name_ref_list params_list_definition_list->syntax_syntax_tree_node<type_definition> defs_definition->syntax_syntax_tree_node_list names_definition_list types_definition_list constructor_specific_params_definition_list->syntax_syntax_tree_node<where_definition> defs_operator->expression_type_reference type_name_operator->expression_type_reference type_name_directive->syntax_syntax_tree_nodeNameDirective_name_ident->ident_type operator_type_statement->statement_def_statement var_def




1. Основы работы с редакторами MS Word, MS Excel и Visual Basic
2. Обеспечение качества услуг в ОсОО
3. Право Напрям підготовки спеціальність 6
4. Арифметика на службе защиты
5. штатная структура отряда ПВП представлен следующими функциональными подразделениями- Сортировочноэ
6. Номинация «Юные журналисты»
7. научнометодическим центром по непрерывному медицинскому и фармацевтическому образованию МЗ РФ 1993 ~ перече
8. ... [7703944] фондовый рынок [7703945] рынок ссудного капитала [7703946] валютный рынок [7703947] рынок то
9. Аудит бухгалтерской отчетности
10. из статора 1 предназначенного для создания магнитного поля; 2 из подвижной части или ротора 2 в котором пр
11. Тема- Microsoft Excel. Сортировка и фильтрация данных Цель- Научиться осуществлять сортировку и фильтрацию данных
12. Коньюктура рынка и маркетинговая логистика
13. YYY за период с 1 января по 31 декабря 20XX г
14. УТВЕРЖДАЮ Начальник Управления Алтайского края по физической культуре и спорту УТ
15. Русская кухня
16. восточно Сибирская государственная академия образования
17. БЕЛОРУССКИЙ ГОСУДАРСТВЕННЫЙ ПЕДАГОГИЧЕСКИЙ УНИВЕРСИТЕТ ИМЕНИ МАКСИМА ТАНКА ФАКУЛЬТЕТ ЕСТЕСТВОЗНАНИЯ
18. МОДУЛЬНИЙ КОНТРОЛЬ 2 Семестр осінній Дисципліна Основи програмування і алгоритмічні мовиrdquo; К
19. Соціологічні методи дослідження у педагогіці
20. Совершенствование конкурентоспособности промышленного предприятия